📄 pptp_ctrl.c
字号:
/* pptp_ctrl.c ... handle PPTP control connection. * C. Scott Ananian <cananian@alumni.princeton.edu> * * $Id: pptp_ctrl.c,v 1.31 2005/03/31 07:42:39 quozl Exp $ */#include <errno.h>#include <sys/time.h>#include <sys/types.h>#include <sys/socket.h>#include <netinet/in.h>#include <unistd.h>#include <stdlib.h>#include <assert.h>#include <signal.h>#include <string.h>#include <ctype.h>#include <fcntl.h>#include "pptp_msg.h"#include "pptp_ctrl.h"#include "pptp_options.h"#include "vector.h"#include "util.h"#include "pptp_quirks.h"/* BECAUSE OF SIGNAL LIMITATIONS, EACH PROCESS CAN ONLY MANAGE ONE * CONNECTION. SO THIS 'PPTP_CONN' STRUCTURE IS A BIT MISLEADING. * WE'LL KEEP CONNECTION-SPECIFIC INFORMATION IN THERE ANYWAY (AS * OPPOSED TO USING GLOBAL VARIABLES), BUT BEWARE THAT THE ENTIRE * UNIX SIGNAL-HANDLING SEMANTICS WOULD HAVE TO CHANGE (OR THE * TIME-OUT CODE DRASTICALLY REWRITTEN) BEFORE YOU COULD DO A * PPTP_CONN_OPEN MORE THAN ONCE PER PROCESS AND GET AWAY WITH IT. *//* This structure contains connection-specific information that the * signal handler needs to see. Thus, it needs to be in a global * variable. If you end up using pthreads or something (why not * just processes?), this would have to be placed in a thread-specific * data area, using pthread_get|set_specific, etc., so I've * conveniently encapsulated it for you. * [linux threads will have to support thread-specific signals * before this would work at all, which, as of this writing * (linux-threads v0.6, linux kernel 2.1.72), it does not.] *//* Globals *//* control the number of times echo packets will be logged */static int nlogecho = 10;static struct thread_specific { struct sigaction old_sigaction; /* evil signals */ PPTP_CONN * conn;} global;#define INITIAL_BUFSIZE 512 /* initial i/o buffer size. */struct PPTP_CONN { int inet_sock; /* Connection States */ enum { CONN_IDLE, CONN_WAIT_CTL_REPLY, CONN_WAIT_STOP_REPLY, CONN_ESTABLISHED } conn_state; /* on startup: CONN_IDLE */ /* Keep-alive states */ enum { KA_NONE, KA_OUTSTANDING } ka_state; /* on startup: KA_NONE */ /* Keep-alive ID; monotonically increasing (watch wrap-around!) */ u_int32_t ka_id; /* on startup: 1 */ /* Other properties. */ u_int16_t version; u_int16_t firmware_rev; u_int8_t hostname[64], vendor[64]; /* XXX these are only PNS properties, currently XXX */ /* Call assignment information. */ u_int16_t call_serial_number; VECTOR *call; void * closure; pptp_conn_cb callback; /******* IO buffers ******/ char * read_buffer, *write_buffer; size_t read_alloc, write_alloc; size_t read_size, write_size;};struct PPTP_CALL { /* Call properties */ enum { PPTP_CALL_PAC, PPTP_CALL_PNS } call_type; union { enum pptp_pac_state { PAC_IDLE, PAC_WAIT_REPLY, PAC_ESTABLISHED, PAC_WAIT_CS_ANS } pac; enum pptp_pns_state { PNS_IDLE, PNS_WAIT_REPLY, PNS_ESTABLISHED, PNS_WAIT_DISCONNECT } pns; } state; u_int16_t call_id, peer_call_id; u_int16_t sernum; u_int32_t speed; /* For user data: */ pptp_call_cb callback; void * closure;};/* PPTP error codes: ----------------------------------------------*//* (General Error Codes) */static const struct { const char *name, *desc;} pptp_general_errors[] = {#define PPTP_GENERAL_ERROR_NONE 0 { "(None)", "No general error" },#define PPTP_GENERAL_ERROR_NOT_CONNECTED 1 { "(Not-Connected)", "No control connection exists yet for this " "PAC-PNS pair" },#define PPTP_GENERAL_ERROR_BAD_FORMAT 2 { "(Bad-Format)", "Length is wrong or Magic Cookie value is incorrect" },#define PPTP_GENERAL_ERROR_BAD_VALUE 3 { "(Bad-Value)", "One of the field values was out of range or " "reserved field was non-zero" },#define PPTP_GENERAL_ERROR_NO_RESOURCE 4 { "(No-Resource)", "Insufficient resources to handle this command now" },#define PPTP_GENERAL_ERROR_BAD_CALLID 5 { "(Bad-Call ID)", "The Call ID is invalid in this context" },#define PPTP_GENERAL_ERROR_PAC_ERROR 6 { "(PAC-Error)", "A generic vendor-specific error occured in the PAC" }};#define MAX_GENERAL_ERROR ( sizeof(pptp_general_errors) / \ sizeof(pptp_general_errors[0]) - 1)/* Outgoing Call Reply Result Codes */static const char *pptp_out_call_reply_result[] = {/* 0 */ "Unknown Result Code",/* 1 */ "Connected",/* 2 */ "General Error",/* 3 */ "No Carrier Detected",/* 4 */ "Busy Signal",/* 5 */ "No Dial Tone",/* 6 */ "Time Out",/* 7 */ "Not Accepted, Call is administratively prohibited" };#define MAX_OUT_CALL_REPLY_RESULT 7/* Call Disconnect Notify Result Codes */static const char *pptp_call_disc_ntfy[] = {/* 0 */ "Unknown Result Code",/* 1 */ "Lost Carrier",/* 2 */ "General Error",/* 3 */ "Administrative Shutdown",/* 4 */ "(your) Request" };#define MAX_CALL_DISC_NTFY 4/* Call Disconnect Notify Result Codes */static const char *pptp_start_ctrl_conn_rply[] = {/* 0 */ "Unknown Result Code",/* 1 */ "Successful Channel Establishment",/* 2 */ "General Error",/* 3 */ "Command Channel Already Exists",/* 4 */ "Requester is not Authorized" };#define MAX_START_CTRL_CONN_REPLY 4/* timing options */int idle_wait = PPTP_TIMEOUT;int max_echo_wait = PPTP_TIMEOUT;/* Local prototypes */static void pptp_reset_timer(void);static void pptp_handle_timer();/* Write/read as much as we can without blocking. */int pptp_write_some(PPTP_CONN * conn);int pptp_read_some(PPTP_CONN * conn);/* Make valid packets from read_buffer */int pptp_make_packet(PPTP_CONN * conn, void **buf, size_t *size);/* Add packet to write_buffer */int pptp_send_ctrl_packet(PPTP_CONN * conn, void * buffer, size_t size);/* Dispatch packets (general) */int pptp_dispatch_packet(PPTP_CONN * conn, void * buffer, size_t size);/* Dispatch packets (control messages) */int ctrlp_disp(PPTP_CONN * conn, void * buffer, size_t size);/* Set link info, for pptp servers that need it. this is a noop, unless the user specified a quirk and there's a set_link hook defined in the quirks table for that quirk */void pptp_set_link(PPTP_CONN * conn, int peer_call_id);/*** log error information in control packets *********************************/static void ctrlp_error( int result, int error, int cause, const char *result_text[], int max_result){ if( cause >= 0) log("Result code is %d '%s'. Error code is %d, Cause code is %d", result, result_text[result <= max_result ? result : 0], error, cause ); else log("Reply result code is %d '%s'. Error code is %d", result, result_text[result <= max_result ? result : 0], error); if ((error > 0) && (error <= MAX_GENERAL_ERROR)){ if( result != PPTP_RESULT_GENERAL_ERROR ) log("Result code is something else then \"general error\", " "so the following error is probably bogus."); log("Error is '%s', Error message: '%s'", pptp_general_errors[error].name, pptp_general_errors[error].desc); }}static const char *ctrl_msg_types[] = { "invalid control message type",/* (Control Connection Management) */ "Start-Control-Connection-Request", /* 1 */ "Start-Control-Connection-Reply", /* 2 */ "Stop-Control-Connection-Request", /* 3 */ "Stop-Control-Connection-Reply", /* 4 */ "Echo-Request", /* 5 */ "Echo-Reply", /* 6 *//* (Call Management) */ "Outgoing-Call-Request", /* 7 */ "Outgoing-Call-Reply", /* 8 */ "Incoming-Call-Request", /* 9 */ "Incoming-Call-Reply", /* 10 */ "Incoming-Call-Connected", /* 11 */ "Call-Clear-Request", /* 12 */ "Call-Disconnect-Notify", /* 13 *//* (Error Reporting) */ "WAN-Error-Notify", /* 14 *//* (PPP Session Control) */ "Set-Link-Info" /* 15 */};#define MAX_CTRLMSG_TYPE 15 /*** report a sent packet ****************************************************/static void ctrlp_rep( void * buffer, int size, int isbuff){ struct pptp_header *packet = buffer; unsigned int type; if(size < sizeof(struct pptp_header)) return; type = ntoh16(packet->ctrl_type); /* FIXME: do not report sending echo requests as long as they are * sent in a signal handler. This may dead lock as the syslog call * is not reentrant */ if( type == PPTP_ECHO_RQST ) return; /* don't keep reporting sending of echo's */ if( (type == PPTP_ECHO_RQST || type == PPTP_ECHO_RPLY) && nlogecho <= 0 ) return; log("%s control packet type is %d '%s'\n",isbuff ? "Buffered" : "Sent", type, ctrl_msg_types[type <= MAX_CTRLMSG_TYPE ? type : 0]);} /* Open new pptp_connection. Returns NULL on failure. */PPTP_CONN * pptp_conn_open(int inet_sock, int isclient, pptp_conn_cb callback){ PPTP_CONN *conn; /* Allocate structure */ if ((conn = malloc(sizeof(*conn))) == NULL) return NULL; if ((conn->call = vector_create()) == NULL) { free(conn); return NULL; } /* Initialize */ conn->inet_sock = inet_sock; conn->conn_state = CONN_IDLE; conn->ka_state = KA_NONE; conn->ka_id = 1; conn->call_serial_number = 0; conn->callback = callback; /* Create I/O buffers */ conn->read_size = conn->write_size = 0; conn->read_alloc = conn->write_alloc = INITIAL_BUFSIZE; conn->read_buffer = malloc(sizeof(*(conn->read_buffer)) * conn->read_alloc); conn->write_buffer = malloc(sizeof(*(conn->write_buffer)) * conn->write_alloc); if (conn->read_buffer == NULL || conn->write_buffer == NULL) { if (conn->read_buffer != NULL) free(conn->read_buffer); if (conn->write_buffer != NULL) free(conn->write_buffer); vector_destroy(conn->call); free(conn); return NULL; } /* Make this socket non-blocking. */ fcntl(conn->inet_sock, F_SETFL, O_NONBLOCK); /* Request connection from server, if this is a client */ if (isclient) { struct pptp_start_ctrl_conn packet = { PPTP_HEADER_CTRL(PPTP_START_CTRL_CONN_RQST), hton16(PPTP_VERSION), 0, 0, hton32(PPTP_FRAME_CAP), hton32(PPTP_BEARER_CAP), hton16(PPTP_MAX_CHANNELS), hton16(PPTP_FIRMWARE_VERSION), PPTP_HOSTNAME, PPTP_VENDOR }; /* fix this packet, if necessary */ int idx, rc; idx = get_quirk_index(); if (idx != -1 && pptp_fixups[idx].start_ctrl_conn) { if ((rc = pptp_fixups[idx].start_ctrl_conn(&packet))) warn("calling the start_ctrl_conn hook failed (%d)", rc); } if (pptp_send_ctrl_packet(conn, &packet, sizeof(packet))) conn->conn_state = CONN_WAIT_CTL_REPLY; else return NULL; /* could not send initial start request. */ } /* Set up interval/keep-alive timer */ /* First, register handler for SIGALRM */ sigpipe_create(); sigpipe_assign(SIGALRM); global.conn = conn; /* Reset event timer */ pptp_reset_timer(); /* all done. */ return conn;}int pptp_conn_established(PPTP_CONN *conn) { return (conn->conn_state == CONN_ESTABLISHED);}/* This currently *only* works for client call requests. * We need to do something else to allocate calls for incoming requests. */PPTP_CALL * pptp_call_open(PPTP_CONN * conn, pptp_call_cb callback, char *phonenr){ PPTP_CALL * call; int i; int idx, rc; /* Send off the call request */ struct pptp_out_call_rqst packet = { PPTP_HEADER_CTRL(PPTP_OUT_CALL_RQST), 0,0, /*call_id, sernum */ hton32(PPTP_BPS_MIN), hton32(PPTP_BPS_MAX), hton32(PPTP_BEARER_CAP), hton32(PPTP_FRAME_CAP), hton16(PPTP_WINDOW), 0, 0, 0, {0}, {0} }; assert(conn && conn->call); assert(conn->conn_state == CONN_ESTABLISHED); /* Assign call id */ if (!vector_scan(conn->call, 0, PPTP_MAX_CHANNELS - 1, &i)) /* no more calls available! */ return NULL; /* allocate structure. */ if ((call = malloc(sizeof(*call))) == NULL) return NULL; /* Initialize call structure */ call->call_type = PPTP_CALL_PNS; call->state.pns = PNS_IDLE; call->call_id = (u_int16_t) i; call->sernum = conn->call_serial_number++; call->callback = callback; call->closure = NULL; packet.call_id = htons(call->call_id); packet.call_sernum = htons(call->sernum); /* if we have a quirk, build a new packet to fit it */ idx = get_quirk_index(); if (idx != -1 && pptp_fixups[idx].out_call_rqst_hook) { if ((rc = pptp_fixups[idx].out_call_rqst_hook(&packet))) warn("calling the out_call_rqst hook failed (%d)", rc); }
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -