summaryrefslogtreecommitdiff
path: root/pppd_plugin/src/pptp_ctrl.c
diff options
context:
space:
mode:
Diffstat (limited to 'pppd_plugin/src/pptp_ctrl.c')
-rw-r--r--pppd_plugin/src/pptp_ctrl.c1077
1 files changed, 0 insertions, 1077 deletions
diff --git a/pppd_plugin/src/pptp_ctrl.c b/pppd_plugin/src/pptp_ctrl.c
deleted file mode 100644
index 1bab3f9a..00000000
--- a/pppd_plugin/src/pptp_ctrl.c
+++ /dev/null
@@ -1,1077 +0,0 @@
-/* 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, int call_id,pptp_call_cb callback,
- char *phonenr,int window)
-{
- PPTP_CALL * call;
- 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(window), 0, 0, 0, {0}, {0}
- };
- assert(conn && conn->call);
- assert(conn->conn_state == CONN_ESTABLISHED);
- /* Assign call id */
- if (!call_id && !vector_scan(conn->call, 0, PPTP_MAX_CHANNELS - 1, &call_id))
- /* 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) call_id;
- 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);
- }
- /* fill in the phone number if it was specified */
- if (phonenr) {
- strncpy(packet.phone_num, phonenr, sizeof(packet.phone_num));
- packet.phone_len = strlen(phonenr);
- if( packet.phone_len > sizeof(packet.phone_num))
- packet.phone_len = sizeof(packet.phone_num);
- packet.phone_len = hton16 (packet.phone_len);
- }
- if (pptp_send_ctrl_packet(conn, &packet, sizeof(packet))) {
- pptp_reset_timer();
- call->state.pns = PNS_WAIT_REPLY;
- /* and add it to the call vector */
- vector_insert(conn->call, call_id, call);
- return call;
- } else { /* oops, unsuccessful. Deallocate. */
- free(call);
- return NULL;
- }
-}
-
-/*** pptp_call_close **********************************************************/
-void pptp_call_close(PPTP_CONN * conn, PPTP_CALL * call)
-{
- struct pptp_call_clear_rqst rqst = {
- PPTP_HEADER_CTRL(PPTP_CALL_CLEAR_RQST), 0, 0
- };
- assert(conn && conn->call); assert(call);
- assert(vector_contains(conn->call, call->call_id));
- /* haven't thought about PAC yet */
- assert(call->call_type == PPTP_CALL_PNS);
- assert(call->state.pns != PNS_IDLE);
- rqst.call_id = hton16(call->call_id);
- /* don't check state against WAIT_DISCONNECT... allow multiple disconnect
- * requests to be made.
- */
- pptp_send_ctrl_packet(conn, &rqst, sizeof(rqst));
- pptp_reset_timer();
- call->state.pns = PNS_WAIT_DISCONNECT;
- /* call structure will be freed when we have confirmation of disconnect. */
-}
-
-/*** hard close ***************************************************************/
-void pptp_call_destroy(PPTP_CONN *conn, PPTP_CALL *call)
-{
- assert(conn && conn->call); assert(call);
- assert(vector_contains(conn->call, call->call_id));
- /* notify */
- if (call->callback != NULL) call->callback(conn, call, CALL_CLOSE_DONE);
- /* deallocate */
- vector_remove(conn->call, call->call_id);
- free(call);
-}
-
-/*** this is a soft close *****************************************************/
-void pptp_conn_close(PPTP_CONN * conn, u_int8_t close_reason)
-{
- struct pptp_stop_ctrl_conn rqst = {
- PPTP_HEADER_CTRL(PPTP_STOP_CTRL_CONN_RQST),
- hton8(close_reason), 0, 0
- };
- int i;
- assert(conn && conn->call);
- /* avoid repeated close attempts */
- if (conn->conn_state == CONN_IDLE || conn->conn_state == CONN_WAIT_STOP_REPLY)
- return;
- /* close open calls, if any */
- for (i = 0; i < vector_size(conn->call); i++)
- pptp_call_close(conn, vector_get_Nth(conn->call, i));
- /* now close connection */
- log("Closing PPTP connection");
- pptp_send_ctrl_packet(conn, &rqst, sizeof(rqst));
- pptp_reset_timer(); /* wait 60 seconds for reply */
- conn->conn_state = CONN_WAIT_STOP_REPLY;
- return;
-}
-
-/*** this is a hard close *****************************************************/
-void pptp_conn_destroy(PPTP_CONN * conn)
-{
- int i;
- assert(conn != NULL); assert(conn->call != NULL);
- /* destroy all open calls */
- for (i = 0; i < vector_size(conn->call); i++)
- pptp_call_destroy(conn, vector_get_Nth(conn->call, i));
- /* notify */
- if (conn->callback != NULL) conn->callback(conn, CONN_CLOSE_DONE);
- sigpipe_close();
- close(conn->inet_sock);
- /* deallocate */
- vector_destroy(conn->call);
- free(conn);
-}
-
-/*** Deal with messages, in a non-blocking manner
- * Add file descriptors used by pptp to fd_set.
- */
-void pptp_fd_set(PPTP_CONN * conn, fd_set * read_set, fd_set * write_set,
- int * max_fd)
-{
- assert(conn && conn->call);
- /* Add fd to write_set if there are outstanding writes. */
- if (conn->write_size > 0)
- FD_SET(conn->inet_sock, write_set);
- /* Always add fd to read_set. (always want something to read) */
- FD_SET(conn->inet_sock, read_set);
- if (*max_fd < conn->inet_sock) *max_fd = conn->inet_sock;
- /* Add signal pipe file descriptor to set */
- int sig_fd = sigpipe_fd();
- FD_SET(sig_fd, read_set);
- if (*max_fd < sig_fd) *max_fd = sig_fd;
-}
-
-/*** handle any pptp file descriptors set in fd_set, and clear them ***********/
-int pptp_dispatch(PPTP_CONN * conn, fd_set * read_set, fd_set * write_set)
-{
- int r = 0;
- assert(conn && conn->call);
- /* Check for signals */
- if (FD_ISSET(sigpipe_fd(), read_set)) {
- if (sigpipe_read() == SIGALRM) pptp_handle_timer();
- FD_CLR(sigpipe_fd(), read_set);
- }
- /* Check write_set could be set. */
- if (FD_ISSET(conn->inet_sock, write_set)) {
- FD_CLR(conn->inet_sock, write_set);
- if (conn->write_size > 0)
- r = pptp_write_some(conn);/* write as much as we can without blocking */
- }
- /* Check read_set */
- if (r >= 0 && FD_ISSET(conn->inet_sock, read_set)) {
- void *buffer; size_t size;
- FD_CLR(conn->inet_sock, read_set);
- r = pptp_read_some(conn); /* read as much as we can without blocking */
- if (r < 0)
- return r;
- /* make packets of the buffer, while we can. */
- while (r >= 0 && pptp_make_packet(conn, &buffer, &size)) {
- r = pptp_dispatch_packet(conn, buffer, size);
- free(buffer);
- }
- }
- /* That's all, folks. Simple, eh? */
- return r;
-}
-
-/*** Non-blocking write *******************************************************/
-int pptp_write_some(PPTP_CONN * conn) {
- ssize_t retval;
- assert(conn && conn->call);
- retval = write(conn->inet_sock, conn->write_buffer, conn->write_size);
- if (retval < 0) { /* error. */
- if (errno == EAGAIN || errno == EINTR) {
- return 0;
- } else { /* a real error */
- log("write error: %s", strerror(errno));
- return -1;
- }
- }
- assert(retval <= conn->write_size);
- conn->write_size -= retval;
- memmove(conn->write_buffer, conn->write_buffer + retval, conn->write_size);
- ctrlp_rep(conn->write_buffer, retval, 0);
- return 0;
-}
-
-/*** Non-blocking read ********************************************************/
-int pptp_read_some(PPTP_CONN * conn)
-{
- ssize_t retval;
- assert(conn && conn->call);
- if (conn->read_size == conn->read_alloc) { /* need to alloc more memory */
- char *new_buffer = realloc(conn->read_buffer,
- sizeof(*(conn->read_buffer)) * conn->read_alloc * 2);
- if (new_buffer == NULL) {
- log("Out of memory"); return -1;
- }
- conn->read_alloc *= 2;
- conn->read_buffer = new_buffer;
- }
- retval = read(conn->inet_sock, conn->read_buffer + conn->read_size,
- conn->read_alloc - conn->read_size);
- if (retval == 0) {
- log("read returned zero, peer has closed");
- return -1;
- }
- if (retval < 0) {
- if (errno == EINTR || errno == EAGAIN)
- return 0;
- else { /* a real error */
- log("read error: %s", strerror(errno));
- return -1;
- }
- }
- conn->read_size += retval;
- assert(conn->read_size <= conn->read_alloc);
- return 0;
-}
-
-/*** Packet formation *********************************************************/
-int pptp_make_packet(PPTP_CONN * conn, void **buf, size_t *size)
-{
- struct pptp_header *header;
- size_t bad_bytes = 0;
- assert(conn && conn->call); assert(buf != NULL); assert(size != NULL);
- /* Give up unless there are at least sizeof(pptp_header) bytes */
- while ((conn->read_size-bad_bytes) >= sizeof(struct pptp_header)) {
- /* Throw out bytes until we have a valid header. */
- header = (struct pptp_header *) (conn->read_buffer + bad_bytes);
- if (ntoh32(header->magic) != PPTP_MAGIC) goto throwitout;
- if (ntoh16(header->reserved0) != 0)
- log("reserved0 field is not zero! (0x%x) Cisco feature? \n",
- ntoh16(header->reserved0));
- if (ntoh16(header->length) < sizeof(struct pptp_header)) goto throwitout;
- if (ntoh16(header->length) > PPTP_CTRL_SIZE_MAX) goto throwitout;
- /* well. I guess it's good. Let's see if we've got it all. */
- if (ntoh16(header->length) > (conn->read_size-bad_bytes))
- /* nope. Let's wait until we've got it, then. */
- goto flushbadbytes;
- /* One last check: */
- if ((ntoh16(header->pptp_type) == PPTP_MESSAGE_CONTROL) &&
- (ntoh16(header->length) !=
- PPTP_CTRL_SIZE(ntoh16(header->ctrl_type))))
- goto throwitout;
- /* well, I guess we've got it. */
- *size = ntoh16(header->length);
- *buf = malloc(*size);
- if (*buf == NULL) { log("Out of memory."); return 0; /* ack! */ }
- memcpy(*buf, conn->read_buffer + bad_bytes, *size);
- /* Delete this packet from the read_buffer. */
- conn->read_size -= (bad_bytes + *size);
- memmove(conn->read_buffer, conn->read_buffer + bad_bytes + *size,
- conn->read_size);
- if (bad_bytes > 0)
- log("%lu bad bytes thrown away.", (unsigned long) bad_bytes);
- return 1;
-throwitout:
- bad_bytes++;
- }
-flushbadbytes:
- /* no more packets. Let's get rid of those bad bytes */
- conn->read_size -= bad_bytes;
- memmove(conn->read_buffer, conn->read_buffer + bad_bytes, conn->read_size);
- if (bad_bytes > 0)
- log("%lu bad bytes thrown away.", (unsigned long) bad_bytes);
- return 0;
-}
-
-/*** pptp_send_ctrl_packet ****************************************************/
-int pptp_send_ctrl_packet(PPTP_CONN * conn, void * buffer, size_t size)
-{
- assert(conn && conn->call); assert(buffer);
- if( conn->write_size > 0) pptp_write_some( conn);
- if( conn->write_size == 0) {
- ssize_t retval;
- retval = write(conn->inet_sock, buffer, size);
- if (retval < 0) { /* error. */
- if (errno == EAGAIN || errno == EINTR) {
- /* ignore */;
- retval = 0;
- } else { /* a real error */
- log("write error: %s", strerror(errno));
- pptp_conn_destroy(conn); /* shut down fast. */
- return 0;
- }
- }
- ctrlp_rep( buffer, retval, 0);
- size -= retval;
- if( size <= 0) return 1;
- }
- /* Shove anything not written into the write buffer */
- if (conn->write_size + size > conn->write_alloc) { /* need more memory */
- char *new_buffer = realloc(conn->write_buffer,
- sizeof(*(conn->write_buffer)) * conn->write_alloc * 2);
- if (new_buffer == NULL) {
- log("Out of memory"); return 0;
- }
- conn->write_alloc *= 2;
- conn->write_buffer = new_buffer;
- }
- memcpy(conn->write_buffer + conn->write_size, buffer, size);
- conn->write_size += size;
- ctrlp_rep( buffer,size,1);
- return 1;
-}
-
-/*** Packet Dispatch **********************************************************/
-int pptp_dispatch_packet(PPTP_CONN * conn, void * buffer, size_t size)
-{
- int r = 0;
- struct pptp_header *header = (struct pptp_header *)buffer;
- assert(conn && conn->call); assert(buffer);
- assert(ntoh32(header->magic) == PPTP_MAGIC);
- assert(ntoh16(header->length) == size);
- switch (ntoh16(header->pptp_type)) {
- case PPTP_MESSAGE_CONTROL:
- r = ctrlp_disp(conn, buffer, size);
- break;
- case PPTP_MESSAGE_MANAGE:
- /* MANAGEMENT messages aren't even part of the spec right now. */
- log("PPTP management message received, but not understood.");
- break;
- default:
- log("Unknown PPTP control message type received: %u",
- (unsigned int) ntoh16(header->pptp_type));
- break;
- }
- return r;
-}
-
-/*** log echo request/replies *************************************************/
-static void logecho( int type)
-{
- /* hack to stop flooding the log files (the most interesting part is right
- * after the connection built-up) */
- if( nlogecho > 0) {
- log( "Echo Re%s received.", type == PPTP_ECHO_RQST ? "quest" :"ply");
- if( --nlogecho == 0)
- log("no more Echo Reply/Request packets will be reported.");
- }
-}
-
-/*** pptp_dispatch_ctrl_packet ************************************************/
-int ctrlp_disp(PPTP_CONN * conn, void * buffer, size_t size)
-{
- struct pptp_header *header = (struct pptp_header *)buffer;
- u_int8_t close_reason = PPTP_STOP_NONE;
- assert(conn && conn->call); assert(buffer);
- assert(ntoh32(header->magic) == PPTP_MAGIC);
- assert(ntoh16(header->length) == size);
- assert(ntoh16(header->pptp_type) == PPTP_MESSAGE_CONTROL);
- if (size < PPTP_CTRL_SIZE(ntoh16(header->ctrl_type))) {
- log("Invalid packet received [type: %d; length: %d].",
- (int) ntoh16(header->ctrl_type), (int) size);
- return 0;
- }
- switch (ntoh16(header->ctrl_type)) {
- /* ----------- STANDARD Start-Session MESSAGES ------------ */
- case PPTP_START_CTRL_CONN_RQST:
- {
- struct pptp_start_ctrl_conn *packet =
- (struct pptp_start_ctrl_conn *) buffer;
- struct pptp_start_ctrl_conn reply = {
- PPTP_HEADER_CTRL(PPTP_START_CTRL_CONN_RPLY),
- 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 };
- int idx, rc;
- log("Received Start Control Connection Request");
- /* fix this packet, if necessary */
- idx = get_quirk_index();
- if (idx != -1 && pptp_fixups[idx].start_ctrl_conn) {
- if ((rc = pptp_fixups[idx].start_ctrl_conn(&reply)))
- warn("calling the start_ctrl_conn hook failed (%d)", rc);
- }
- if (conn->conn_state == CONN_IDLE) {
- if (ntoh16(packet->version) < PPTP_VERSION) {
- /* Can't support this (earlier) PPTP_VERSION */
- reply.version = packet->version;
- /* protocol version not supported */
- reply.result_code = hton8(5);
- pptp_send_ctrl_packet(conn, &reply, sizeof(reply));
- pptp_reset_timer(); /* give sender a chance for a retry */
- } else { /* same or greater version */
- if (pptp_send_ctrl_packet(conn, &reply, sizeof(reply))) {
- conn->conn_state = CONN_ESTABLISHED;
- log("server connection ESTABLISHED.");
- pptp_reset_timer();
- }
- }
- }
- break;
- }
- case PPTP_START_CTRL_CONN_RPLY:
- {
- struct pptp_start_ctrl_conn *packet =
- (struct pptp_start_ctrl_conn *) buffer;
- log("Received Start Control Connection Reply");
- if (conn->conn_state == CONN_WAIT_CTL_REPLY) {
- /* XXX handle collision XXX [see rfc] */
- if (ntoh16(packet->version) != PPTP_VERSION) {
- if (conn->callback != NULL)
- conn->callback(conn, CONN_OPEN_FAIL);
- close_reason = PPTP_STOP_PROTOCOL;
- goto pptp_conn_close;
- }
- if (ntoh8(packet->result_code) != 1 &&
- /* J'ai change le if () afin que la connection ne se ferme
- * pas pour un "rien" :p adel@cybercable.fr -
- *
- * Don't close the connection if the result code is zero
- * (feature found in certain ADSL modems)
- */
- ntoh8(packet->result_code) != 0) {
- log("Negative reply received to our Start Control "
- "Connection Request");
- ctrlp_error(packet->result_code, packet->error_code,
- -1, pptp_start_ctrl_conn_rply,
- MAX_START_CTRL_CONN_REPLY);
- if (conn->callback != NULL)
- conn->callback(conn, CONN_OPEN_FAIL);
- close_reason = PPTP_STOP_PROTOCOL;
- goto pptp_conn_close;
- }
- conn->conn_state = CONN_ESTABLISHED;
- /* log session properties */
- conn->version = ntoh16(packet->version);
- conn->firmware_rev = ntoh16(packet->firmware_rev);
- memcpy(conn->hostname, packet->hostname, sizeof(conn->hostname));
- memcpy(conn->vendor, packet->vendor, sizeof(conn->vendor));
- pptp_reset_timer(); /* 60 seconds until keep-alive */
- log("Client connection established.");
- if (conn->callback != NULL)
- conn->callback(conn, CONN_OPEN_DONE);
- } /* else goto pptp_conn_close; */
- break;
- }
- /* ----------- STANDARD Stop-Session MESSAGES ------------ */
- case PPTP_STOP_CTRL_CONN_RQST:
- {
- /* conn_state should be CONN_ESTABLISHED, but it could be
- * something else */
- struct pptp_stop_ctrl_conn reply = {
- PPTP_HEADER_CTRL(PPTP_STOP_CTRL_CONN_RPLY),
- hton8(1), hton8(PPTP_GENERAL_ERROR_NONE), 0
- };
- log("Received Stop Control Connection Request.");
- if (conn->conn_state == CONN_IDLE) break;
- if (pptp_send_ctrl_packet(conn, &reply, sizeof(reply))) {
- if (conn->callback != NULL)
- conn->callback(conn, CONN_CLOSE_RQST);
- conn->conn_state = CONN_IDLE;
- return -1;
- }
- break;
- }
- case PPTP_STOP_CTRL_CONN_RPLY:
- {
- log("Received Stop Control Connection Reply.");
- /* conn_state should be CONN_WAIT_STOP_REPLY, but it
- * could be something else */
- if (conn->conn_state == CONN_IDLE) break;
- conn->conn_state = CONN_IDLE;
- return -1;
- }
- /* ----------- STANDARD Echo/Keepalive MESSAGES ------------ */
- case PPTP_ECHO_RPLY:
- {
- struct pptp_echo_rply *packet =
- (struct pptp_echo_rply *) buffer;
- logecho( PPTP_ECHO_RPLY);
- if ((conn->ka_state == KA_OUTSTANDING) &&
- (ntoh32(packet->identifier) == conn->ka_id)) {
- conn->ka_id++;
- conn->ka_state = KA_NONE;
- pptp_reset_timer();
- }
- break;
- }
- case PPTP_ECHO_RQST:
- {
- struct pptp_echo_rqst *packet =
- (struct pptp_echo_rqst *) buffer;
- struct pptp_echo_rply reply = {
- PPTP_HEADER_CTRL(PPTP_ECHO_RPLY),
- packet->identifier, /* skip hton32(ntoh32(id)) */
- hton8(1), hton8(PPTP_GENERAL_ERROR_NONE), 0
- };
- logecho( PPTP_ECHO_RQST);
- pptp_send_ctrl_packet(conn, &reply, sizeof(reply));
- pptp_reset_timer();
- break;
- }
- /* ----------- OUTGOING CALL MESSAGES ------------ */
- case PPTP_OUT_CALL_RQST:
- {
- struct pptp_out_call_rqst *packet =
- (struct pptp_out_call_rqst *)buffer;
- struct pptp_out_call_rply reply = {
- PPTP_HEADER_CTRL(PPTP_OUT_CALL_RPLY),
- 0 /* callid */, packet->call_id, 1, PPTP_GENERAL_ERROR_NONE, 0,
- hton32(PPTP_CONNECT_SPEED),
- hton16(PPTP_WINDOW), hton16(PPTP_DELAY), 0
- };
- log("Received Outgoing Call Request.");
- /* XXX PAC: eventually this should make an outgoing call. XXX */
- reply.result_code = hton8(7); /* outgoing calls verboten */
- pptp_send_ctrl_packet(conn, &reply, sizeof(reply));
- break;
- }
- case PPTP_OUT_CALL_RPLY:
- {
- struct pptp_out_call_rply *packet =
- (struct pptp_out_call_rply *)buffer;
- PPTP_CALL * call;
- u_int16_t callid = ntoh16(packet->call_id_peer);
- log("Received Outgoing Call Reply.");
- if (!vector_search(conn->call, (int) callid, &call)) {
- log("PPTP_OUT_CALL_RPLY received for non-existant call: "
- "peer call ID (us) %d call ID (them) %d.",
- callid, ntoh16(packet->call_id));
- break;
- }
- if (call->call_type != PPTP_CALL_PNS) {
- log("Ack! How did this call_type get here?"); /* XXX? */
- break;
- }
- if (call->state.pns != PNS_WAIT_REPLY) {
- warn("Unexpected(?) Outgoing Call Reply will be ignored.");
- break;
- }
- /* check for errors */
- if (packet->result_code != 1) {
- /* An error. Log it verbosely. */
- log("Our outgoing call request [callid %d] has not been "
- "accepted.", (int) callid);
- ctrlp_error(packet->result_code, packet->error_code,
- packet->cause_code, pptp_out_call_reply_result,
- MAX_OUT_CALL_REPLY_RESULT);
- call->state.pns = PNS_IDLE;
- if (call->callback != NULL)
- call->callback(conn, call, CALL_OPEN_FAIL);
- pptp_call_destroy(conn, call);
- } else {
- /* connection established */
- call->state.pns = PNS_ESTABLISHED;
- call->peer_call_id = ntoh16(packet->call_id);
- call->speed = ntoh32(packet->speed);
- pptp_reset_timer();
- /* call pptp_set_link. unless the user specified a quirk
- and this quirk has a set_link hook, this is a noop */
- pptp_set_link(conn, call->peer_call_id);
- if (call->callback != NULL)
- call->callback(conn, call, CALL_OPEN_DONE);
- log("Outgoing call established (call ID %u, peer's "
- "call ID %u).\n", call->call_id, call->peer_call_id);
- }
- break;
- }
- /* ----------- INCOMING CALL MESSAGES ------------ */
- /* XXX write me XXX */
- /* ----------- CALL CONTROL MESSAGES ------------ */
- case PPTP_CALL_CLEAR_RQST:
- {
- struct pptp_call_clear_rqst *packet =
- (struct pptp_call_clear_rqst *)buffer;
- struct pptp_call_clear_ntfy reply = {
- PPTP_HEADER_CTRL(PPTP_CALL_CLEAR_NTFY), packet->call_id,
- 1, PPTP_GENERAL_ERROR_NONE, 0, 0, {0}
- };
- log("Received Call Clear Request.");
- if (vector_contains(conn->call, ntoh16(packet->call_id))) {
- PPTP_CALL * call;
- vector_search(conn->call, ntoh16(packet->call_id), &call);
- if (call->callback != NULL)
- call->callback(conn, call, CALL_CLOSE_RQST);
- pptp_send_ctrl_packet(conn, &reply, sizeof(reply));
- pptp_call_destroy(conn, call);
- log("Call closed (RQST) (call id %d)", (int) call->call_id);
- }
- break;
- }
- case PPTP_CALL_CLEAR_NTFY:
- {
- struct pptp_call_clear_ntfy *packet =
- (struct pptp_call_clear_ntfy *)buffer;
- log("Call disconnect notification received (call id %d)",
- ntoh16(packet->call_id));
- if (vector_contains(conn->call, ntoh16(packet->call_id))) {
- PPTP_CALL * call;
- ctrlp_error(packet->result_code, packet->error_code,
- packet->cause_code, pptp_call_disc_ntfy,
- MAX_CALL_DISC_NTFY);
- vector_search(conn->call, ntoh16(packet->call_id), &call);
- pptp_call_destroy(conn, call);
- }
- /* XXX we could log call stats here XXX */
- /* XXX not all servers send this XXX */
- break;
- }
- case PPTP_SET_LINK_INFO:
- {
- /* I HAVE NO CLUE WHAT TO DO IF send_accm IS NOT 0! */
- /* this is really dealt with in the HDLC deencapsulation, anyway. */
- struct pptp_set_link_info *packet =
- (struct pptp_set_link_info *)buffer;
- /* log it. */
- log("PPTP_SET_LINK_INFO received from peer_callid %u",
- (unsigned int) ntoh16(packet->call_id_peer));
- log(" send_accm is %08lX, recv_accm is %08lX",
- (unsigned long) ntoh32(packet->send_accm),
- (unsigned long) ntoh32(packet->recv_accm));
- if (!(ntoh32(packet->send_accm) == 0 &&
- ntoh32(packet->recv_accm) == 0))
- warn("Non-zero Async Control Character Maps are not supported!");
- break;
- }
- default:
- log("Unrecognized Packet %d received.",
- (int) ntoh16(((struct pptp_header *)buffer)->ctrl_type));
- /* goto pptp_conn_close; */
- break;
- }
- return 0;
-pptp_conn_close:
- warn("pptp_conn_close(%d)", (int) close_reason);
- pptp_conn_close(conn, close_reason);
- return 0;
-}
-
-/*** pptp_set_link **************************************************************/
-void pptp_set_link(PPTP_CONN* conn, int peer_call_id)
-{
- int idx, rc;
- /* if we need to send a set_link packet because of buggy
- hardware or pptp server, do it now */
- if ((idx = get_quirk_index()) != -1 && pptp_fixups[idx].set_link_hook) {
- struct pptp_set_link_info packet;
- if ((rc = pptp_fixups[idx].set_link_hook(&packet, peer_call_id)))
- warn("calling the set_link hook failed (%d)", rc);
- if (pptp_send_ctrl_packet(conn, &packet, sizeof(packet))) {
- pptp_reset_timer();
- }
- }
-}
-
-/*** Get info from call structure *********************************************/
-/* NOTE: The peer_call_id is undefined until we get a server response. */
-void pptp_call_get_ids(PPTP_CONN * conn, PPTP_CALL * call,
- u_int16_t * call_id, u_int16_t * peer_call_id)
-{
- assert(conn != NULL); assert(call != NULL);
- *call_id = call->call_id;
- *peer_call_id = call->peer_call_id;
-}
-
-/*** pptp_call_closure_put ****************************************************/
-void pptp_call_closure_put(PPTP_CONN * conn, PPTP_CALL * call, void *cl)
-{
- assert(conn != NULL); assert(call != NULL);
- call->closure = cl;
-}
-
-/*** pptp_call_closure_get ****************************************************/
-void * pptp_call_closure_get(PPTP_CONN * conn, PPTP_CALL * call)
-{
- assert(conn != NULL); assert(call != NULL);
- return call->closure;
-}
-
-/*** pptp_conn_closure_put ****************************************************/
-void pptp_conn_closure_put(PPTP_CONN * conn, void *cl)
-{
- assert(conn != NULL);
- conn->closure = cl;
-}
-
-/*** pptp_conn_closure_get ****************************************************/
-void * pptp_conn_closure_get(PPTP_CONN * conn)
-{
- assert(conn != NULL);
- return conn->closure;
-}
-
-/*** Reset keep-alive timer ***************************************************/
-static void pptp_reset_timer(void)
-{
- const struct itimerval tv = { { 0, 0 }, /* stop on time-out */
- { idle_wait, 0 } };
- if (idle_wait) setitimer(ITIMER_REAL, &tv, NULL);
-}
-
-
-/*** Handle keep-alive timer **************************************************/
-static void pptp_handle_timer()
-{
- int i;
- /* "Keep Alives and Timers, 1": check connection state */
- if (global.conn->conn_state != CONN_ESTABLISHED) {
- if (global.conn->conn_state == CONN_WAIT_STOP_REPLY)
- /* hard close. */
- pptp_conn_destroy(global.conn);
- else /* soft close */
- pptp_conn_close(global.conn, PPTP_STOP_NONE);
- }
- /* "Keep Alives and Timers, 2": check echo status */
- if (global.conn->ka_state == KA_OUTSTANDING) {
- /* no response to keep-alive */
- log ("closing control connection due to missing echo reply");
- pptp_conn_close(global.conn, PPTP_STOP_NONE);
- } else { /* ka_state == NONE */ /* send keep-alive */
- struct pptp_echo_rqst rqst = {
- PPTP_HEADER_CTRL(PPTP_ECHO_RQST), hton32(global.conn->ka_id) };
- pptp_send_ctrl_packet(global.conn, &rqst, sizeof(rqst));
- global.conn->ka_state = KA_OUTSTANDING;
- }
- /* check incoming/outgoing call states for !IDLE && !ESTABLISHED */
- for (i = 0; i < vector_size(global.conn->call); i++) {
- PPTP_CALL * call = vector_get_Nth(global.conn->call, i);
- if (call->call_type == PPTP_CALL_PNS) {
- if (call->state.pns == PNS_WAIT_REPLY) {
- /* send close request */
- pptp_call_close(global.conn, call);
- assert(call->state.pns == PNS_WAIT_DISCONNECT);
- } else if (call->state.pns == PNS_WAIT_DISCONNECT) {
- /* hard-close the call */
- pptp_call_destroy(global.conn, call);
- }
- } else if (call->call_type == PPTP_CALL_PAC) {
- if (call->state.pac == PAC_WAIT_REPLY) {
- /* XXX FIXME -- drop the PAC connection XXX */
- } else if (call->state.pac == PAC_WAIT_CS_ANS) {
- /* XXX FIXME -- drop the PAC connection XXX */
- }
- }
- }
- pptp_reset_timer();
-}