/*** 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;
}
示例#2
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;
}
示例#3
0
/* 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);
    }
    /* fill in the phone number if it was specified */
    if (phonenr) {
        strncpy((char *)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, i, call);
        return call;
    } else { /* oops, unsuccessful. Deallocate. */
        free(call);
        return NULL;
    }
}
示例#4
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();
        }
    }
}
/*** Handle keep-alive timer **************************************************/
static void pptp_handle_timer(int sig)
{
    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*/
        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;
        /* XXX FIXME: wake up ctrl thread -- or will the SIGALRM do that
         * automagically? XXX
         */
    }
    /* 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();
}
示例#6
0
/*** 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. */
}
示例#7
0
/*** 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;
}