/*** vector_insert************************************************************* * nice thing about file descriptors is that we are assured by POSIX * that they are monotonically increasing. */ int vector_insert(VECTOR *v, int key, PPTP_CALL * call) { int i; assert(v != NULL && call != NULL); assert(!vector_contains(v, key)); #ifdef VECTOR_DEBUG assert(v->key_max < key); #endif if (!(v->size < v->alloc)) { void *tmp = realloc(v->item, sizeof(*(v->item)) * 2 * v->alloc); if (tmp != NULL) { v->alloc *= 2; v->item = tmp; } else return FALSE; /* failed to alloc memory. */ } assert(v->size < v->alloc); /* for safety, we make this work in the general case; * but this is optimized for adding call to the end of the vector. */ for(i = v->size - 1; i >= 0; i--) if (v->item[i].key < key) break; /* insert after item i */ memmove(&v->item[i + 2], &v->item[i + 1], (v->size - i - 1) * sizeof(*(v->item))); v->item[i + 1].key = key; v->item[i + 1].call = call; v->size++; #ifdef VECTOR_DEBUG if (v->key_max < key) /* ie, always. */ v->key_max = key; #endif return TRUE; }
/*** 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); }
/* Call callback */ void call_callback(PPTP_CONN *conn, PPTP_CALL *call, enum call_state state) { struct local_callinfo *lci; struct local_conninfo *conninfo; u_int16_t call_id[2]; switch(state) { case CALL_OPEN_DONE: /* okey dokey. This means that the call_id and peer_call_id are now * valid, so lets send them on to our friends who requested this call. */ pptp_debug("About to get the call closure stuff"); lci = pptp_call_closure_get(conn, call); assert(lci != NULL); pptp_call_get_ids(conn, call, &call_id[0], &call_id[1]); pptp_debug("writing out the call_ids"); if (lci) write(lci->unix_sock, (char *) &call_id, sizeof(call_id)); /* Our duty to the fatherland is now complete. */ break; case CALL_OPEN_FAIL: case CALL_CLOSE_RQST: case CALL_CLOSE_DONE: /* don't need to do anything here, except make sure tables are sync'ed */ conninfo = pptp_conn_closure_get(conn); lci = pptp_call_closure_get(conn, call); // assert(lci != NULL && conninfo != NULL); if (lci && conninfo && vector_contains(conninfo->call_list, lci->unix_sock)) { vector_remove(conninfo->call_list, lci->unix_sock); if (lci->unix_sock >= 0) FD_CLR(lci->unix_sock, conninfo->call_set); // syslog(LOG_NOTICE, "Closing connection"); kill(lci->pid[0], SIGTERM); } if (lci) { pptp_debug("close %d", lci->unix_sock); close(lci->unix_sock); lci->unix_sock = -1; #if 0 memset(lci, 0, sizeof(*lci)); free(lci); pptp_call_closure_put(conn, call, NULL); #endif } break; default: logmsg("Unhandled call callback state [%d].", (int) state); break; } }
/*** 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. */ }
/* Call callback */ void call_callback(PPTP_CONN *conn, PPTP_CALL *call, enum call_state state) { struct local_callinfo *lci; struct local_conninfo *conninfo; u_int16_t call_id[2]; switch(state) { case CALL_OPEN_DONE: /* okey dokey. This means that the call_id and peer_call_id are * now valid, so lets send them on to our friends who requested * this call. */ lci = pptp_call_closure_get(conn, call); assert(lci != NULL); pptp_call_get_ids(conn, call, &call_id[0], &call_id[1]); write(lci->unix_sock, &call_id, sizeof(call_id)); /* Our duty to the fatherland is now complete. */ break; case CALL_OPEN_FAIL: case CALL_CLOSE_RQST: case CALL_CLOSE_DONE: /* don't need to do anything here, except make sure tables * are sync'ed */ log("Closing connection (call state)"); conninfo = pptp_conn_closure_get(conn); lci = pptp_call_closure_get(conn, call); assert(lci != NULL && conninfo != NULL); if (vector_contains(conninfo->call_list, lci->unix_sock)) { vector_remove(conninfo->call_list, lci->unix_sock); close(lci->unix_sock); FD_CLR(lci->unix_sock, conninfo->call_set); if(lci->pid[0] > 1) kill(lci->pid[0], SIGTERM); if(lci->pid[1] > 1) kill(lci->pid[1], SIGTERM); } break; default: log("Unhandled call callback state [%d].", (int) state); break; } }
/*** 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 [email protected] - * * 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; }
void main(void) { int i, j, retval; VECTOR *v = vector_create(); assert(v != NULL); assert(vector_size(v)==0); for (i=0; i<=MAX; i++) { assert(!vector_contains(v, i)); assert(!vector_remove(v, i)); assert(!vector_search(v, i, (PPTP_CALL **)&j)); retval = vector_scan(v, i, MAX*2, &j); assert(retval); assert(j==i); } for (i=1; i<=MAX; i++) { retval = vector_insert(v, i, (PPTP_CALL *)i); assert(retval); assert(vector_size(v)==i); } for (i=1; i<MAX; i++) { retval = vector_search(v, i, (PPTP_CALL **)&j); assert(retval); assert(j==i); retval = vector_contains(v, i); assert(retval); } assert(vector_size(v)==MAX); retval = vector_contains(v, MAX+1); assert(!retval); retval = vector_search(v, MAX+1, (PPTP_CALL **)&j); assert(!retval); retval = vector_scan(v, 0, MAX, &j); assert(retval); assert(j==0); retval = vector_scan(v, 1, MAX, &j); assert(!retval); retval = vector_scan(v, 1, MAX+1, &j); assert(retval); assert(j==MAX+1); retval = vector_scan(v, 1, MAX+MAX, &j); assert(retval); assert(j==MAX+1); for (i=0; i<(MAX*10); i++) { int k = (random() % MAX) + 1; assert(vector_contains(v, k)); assert(!vector_scan(v, 1, k, &j)); assert(!vector_scan(v, k, MAX, &j)); retval = vector_remove(v, k); assert(retval); assert(vector_size(v)==MAX-1); assert(!vector_contains(v, k)); assert(!vector_search(v, k, (PPTP_CALL **) &j)); retval = vector_scan(v, 1, MAX, &j); assert(retval); assert(j==k); retval = vector_insert(v, k, (PPTP_CALL *) k); assert(retval); assert(vector_size(v)==MAX); assert(vector_contains(v, k)); assert(!vector_scan(v, 1, MAX, &j)); retval = vector_search(v, k, (PPTP_CALL **) &j); assert(retval); assert(j==k); } for (i=1; i<=MAX; i++) { assert(vector_size(v)==MAX-(i-1)); vector_remove(v, i); assert(vector_size(v)==MAX-i); assert(!vector_contains(v, i)); retval = vector_search(v, i, (PPTP_CALL **) &j); assert(!retval); retval = vector_scan(v, 1, MAX, &j); assert(retval); assert(j==1); } assert(vector_size(v)==0); vector_destroy(v); }