#if HAVE_CONFIG_H #include #endif #include #include #include #include "pptpd.h" int handle_control_packet(struct control_struct * ctrl, struct pptp_header * packet); char * getctrlname(int nr); unsigned char pptpd_hostname[MAX_HOSTNAME_SIZE] = PPTP_HOSTNAME; unsigned char pptpd_vendor[MAX_VENDOR_SIZE] = PPTP_VENDOR; struct control_struct * init_control_struct(struct control_struct ** ctrl_chain) { struct control_struct * ctrl; ctrl = malloc(sizeof(struct control_struct)); if (!ctrl) return NULL; memset((void *)ctrl, 0, sizeof(*ctrl)); ctrl->ctrl_fd = -1; ctrl->gre_fd = -1; ctrl->gre_dtv.tv_sec=1; ctrl->gre_dtv.tv_usec=0; gre_set_timeout(ctrl); ctrl->ctrl_rcv_offset = CTRL_RCV_LEN0; ctrl->last_pong_time = time(NULL); ctrl->control_chain = ctrl_chain; ctrl->next = *ctrl_chain; *ctrl_chain = ctrl; return ctrl; } void finish_control_struct(struct control_struct * ctrl) { struct control_struct ** pp; struct ppp_struct *ppp; if (!ctrl) return; for (pp = ctrl->control_chain; *pp; pp=&((*pp)->next)) { if (*pp == ctrl) { *pp = ctrl->next; break; } if ((*pp)->next == NULL) break; } if (ctrl->ctrl_fd>=0) close(ctrl->ctrl_fd); if (ctrl->gre_fd>=0) close(ctrl->gre_fd); if (ctrl->ctrl_rcv_buf!=NULL) free(ctrl->ctrl_rcv_buf); while( ctrl->ppp!=NULL ) { ppp=ctrl->ppp; ctrl->ppp=ctrl->ppp->next; release_ppp(ppp); } free(ctrl); } char * getctrlname(int nr) { static char buf[64]; switch (nr) { case START_CTRL_CONN_RQST: return "START_CTRL_CONN_RQST"; case START_CTRL_CONN_RPLY: return "START_CTRL_CONN_RPLY"; case STOP_CTRL_CONN_RQST: return "STOP_CTRL_CONN_RQST"; case STOP_CTRL_CONN_RPLY: return "STOP_CTRL_CONN_RPLY"; case ECHO_RQST: return "ECHO_RQST"; case ECHO_RPLY: return "ECHO_RPLY"; case OUT_CALL_RQST: return "OUT_CALL_RQST"; case OUT_CALL_RPLY: return "OUT_CALL_RPLY"; case IN_CALL_RQST: return "IN_CALL_RQST"; case IN_CALL_RPLY: return "IN_CALL_RPLY"; case IN_CALL_CONN: return "IN_CALL_CONN"; case CALL_CLR_RQST: return "CALL_CLR_RQST"; case CALL_DISCONN_NTFY: return "CALL_DISCONN_NTFY"; case WAN_ERR_NTFY: return "WAN_ERR_NTFY"; case SET_LINK_INFO: return "SET_LINK_INFO"; } sprintf(buf, "UNKNOWN(%d)", nr); return buf; } int read_control_packet(struct control_struct * ctrl) { int i; switch(ctrl->ctrl_rcv_offset) { case CTRL_RCV_LEN0: i = read(ctrl->ctrl_fd, ((void *)&(ctrl->ctrl_rcv_len)), 2); if (i<=0) { LOG("control connection died"); return -1; } if (i == 1) ctrl->ctrl_rcv_offset = CTRL_RCV_LEN1; else ctrl->ctrl_rcv_offset = CTRL_RCV_START; return 0; case CTRL_RCV_LEN1: i = read(ctrl->ctrl_fd, ((void *)&(ctrl->ctrl_rcv_len))+1, 1); if (i<=0) { LOG("control connection died"); return -1; } ctrl->ctrl_rcv_offset = CTRL_RCV_START; return 0; case CTRL_RCV_START: ctrl->ctrl_rcv_len = ntohs(ctrl->ctrl_rcv_len); if (ctrl->ctrl_rcv_len > PPTP_MAX_CTRL_PCKT_SIZE) { LOG("packet too long"); return -1; } ctrl->ctrl_rcv_buf = malloc(ctrl->ctrl_rcv_len); if (!ctrl) { LOG("not enough memory"); return -1; } *(u_int16_t *)ctrl->ctrl_rcv_buf = htons(ctrl->ctrl_rcv_len); ctrl->ctrl_rcv_offset = 2; /* fallthrough */ default: i = read(ctrl->ctrl_fd, ctrl->ctrl_rcv_buf + ctrl->ctrl_rcv_offset, ctrl->ctrl_rcv_len - ctrl->ctrl_rcv_offset); if (i<=0) { LOG("read error in the middle of packet"); return -1; } if ((ctrl->ctrl_rcv_offset -= i) > 0) return 0; i = handle_control_packet(ctrl, (struct pptp_header *)(ctrl->ctrl_rcv_buf)); ctrl->ctrl_rcv_offset = CTRL_RCV_LEN0; free(ctrl->ctrl_rcv_buf); return i; } } int handle_control_packet(struct control_struct * ctrl, struct pptp_header * packet) { LOG("<- %s", getctrlname(ntohs(packet->ctrl_type))); switch(ntohs(packet->ctrl_type)) { case START_CTRL_CONN_RQST: return handle_start_ctrl_conn_rqst(ctrl,(struct pptp_start_ctrl_conn_rqst *)packet); case START_CTRL_CONN_RPLY: return handle_start_ctrl_conn_rply(ctrl,(struct pptp_start_ctrl_conn_rply *)packet); case STOP_CTRL_CONN_RQST: return handle_stop_ctrl_conn_rqst(ctrl,(struct pptp_stop_ctrl_conn_rqst *)packet); case STOP_CTRL_CONN_RPLY: return handle_stop_ctrl_conn_rply(ctrl,(struct pptp_stop_ctrl_conn_rply *)packet); case ECHO_RQST: return handle_echo_rqst(ctrl,(struct pptp_echo_rqst *)packet); case ECHO_RPLY: return handle_echo_rply(ctrl,(struct pptp_echo_rply *)packet); case OUT_CALL_RQST: return handle_out_call_rqst(ctrl,(struct pptp_out_call_rqst *)packet); case OUT_CALL_RPLY: return handle_out_call_rply(ctrl,(struct pptp_out_call_rply *)packet); case IN_CALL_RQST: return handle_in_call_rqst(ctrl,(struct pptp_in_call_rqst *)packet); case IN_CALL_RPLY: return handle_in_call_rply(ctrl,(struct pptp_in_call_rply *)packet); case IN_CALL_CONN: return handle_in_call_conn(ctrl,(struct pptp_in_call_connect *)packet); case CALL_CLR_RQST: return handle_call_clr_rqst(ctrl,(struct pptp_call_clr_rqst *)packet); case CALL_DISCONN_NTFY: return handle_call_disconn_ntfy(ctrl,(struct pptp_call_disconn_ntfy *)packet); case WAN_ERR_NTFY: return handle_wan_err_ntfy(ctrl,(struct pptp_wan_err_ntfy *)packet); case SET_LINK_INFO: return handle_set_link_info(ctrl,(struct pptp_set_link_info *)packet); default: LOG("unknown packet type: %d", ntohs(packet->ctrl_type)); } return -1; } int send_packet(struct control_struct * ctrl, void * packet) { LOG("-> %s", getctrlname(ntohs(((struct pptp_header *)packet)->ctrl_type))); return write(ctrl->ctrl_fd, packet, ntohs(((struct pptp_header *)packet)->length)); } void * prepare_call_close_packet(struct ppp_struct * p, int result_code) { struct pptp_call_clr_rqst * rply_pns; struct pptp_call_disconn_ntfy * rply_pac; if (p->pptp_type == PPTP_PNS) { ALLOC_PACKET(rply_pns, CALL_CLR_RQST); if (!rply_pns) return NULL; rply_pns->call_id=p->call_id; rply_pns->reserved1 = 0; return rply_pns; } ALLOC_PACKET(rply_pac, CALL_DISCONN_NTFY); if (!rply_pac) return NULL; rply_pac->call_id=p->call_id; rply_pac->result_code = result_code; rply_pac->error_code = 0; rply_pac->cause_code = 0; rply_pac->reserved1 = 0; memset(rply_pac->call_stats, 0, sizeof(rply_pac->call_stats)); return rply_pac; } int handle_start_ctrl_conn_rqst(struct control_struct * ctrl, struct pptp_start_ctrl_conn_rqst * packet) { struct pptp_start_ctrl_conn_rply * rply; ALLOC_PACKET(rply, START_CTRL_CONN_RPLY); if (!rply) { LOG("not enough memory"); return -1; } rply->version = htons(PPTP_VERSION); rply->result_code = CONNECTED; rply->error_code = NO_ERROR; rply->framing_cap = htons(OUR_FRAMING); rply->bearer_cap = htons(OUR_BEARER); rply->max_channels = htons(MAX_CHANNELS); rply->firmware_rev = htons(PPTP_FIRMWARE_VERSION); memcpy(rply->hostname, pptpd_hostname, MAX_HOSTNAME_SIZE); memcpy(rply->vendor, pptpd_vendor, MAX_VENDOR_SIZE); send_packet(ctrl, rply); free(rply); return 0; } int handle_start_ctrl_conn_rply(struct control_struct * ctrl, struct pptp_start_ctrl_conn_rply * packet) { /* XXX: estabilish GRE connection? */ return 0; } int handle_stop_ctrl_conn_rqst(struct control_struct * ctrl, struct pptp_stop_ctrl_conn_rqst * packet) { struct pptp_stop_ctrl_conn_rply * rply; ALLOC_PACKET(rply, STOP_CTRL_CONN_RPLY); LOG("stopping due to control connection close request"); if (!rply) return -1; rply->result_code = CLOSED; rply->error_code = NO_ERROR; rply->reserved1 = htons(RESERVED); send_packet(ctrl, rply); free(rply); return -1; } int handle_stop_ctrl_conn_rply(struct control_struct * ctrl, struct pptp_stop_ctrl_conn_rply * packet) { LOG("stopping: remote end confirmed connection close request"); return -1; } int handle_echo_rqst(struct control_struct * ctrl, struct pptp_echo_rqst * packet) { struct pptp_echo_rply * rply; ALLOC_PACKET(rply, ECHO_RPLY); if (!rply) { LOG("not enough memory"); return -1; } rply->identifier = packet->identifier; rply->result_code = CONNECTED; rply->error_code = NO_ERROR; rply->reserved1 = htons(RESERVED); send_packet(ctrl, rply); free(rply); return 0; } int handle_echo_rply(struct control_struct * ctrl, struct pptp_echo_rply * packet) { ctrl->last_pong_time = time(NULL); return 0; } int handle_out_call_rqst(struct control_struct * ctrl, struct pptp_out_call_rqst * packet) { /* TODO */ return 0; } int handle_out_call_rply(struct control_struct * ctrl, struct pptp_out_call_rply * packet) { return 0; } int handle_in_call_rqst(struct control_struct * ctrl, struct pptp_in_call_rqst * packet) { struct pptp_in_call_rply * rply; struct ppp_struct * ppp; int i; char tmpbuf[1024], * p; ALLOC_PACKET(rply, IN_CALL_RPLY); if (!rply) { LOG("not enough memory"); return -1; } ppp = new_ppp(ctrl); if (!ppp) { rply->result_code = DO_NOT_ACCEPT_IN; LOG("not enough memory to answer call"); } else { rply->result_code = CONNECT; rply->call_id = ppp->call_id; ppp->peers_call_id = packet->call_id; ppp->pptp_type = PPTP_PNS; i = ntohs(packet->dialing_len); if (i>sizeof(packet->dialing_num)) i = sizeof(packet->dialing_num); strcpy(tmpbuf, "answering in-call request, dialed from ["); p = tmpbuf + strlen(tmpbuf); memcpy(p, packet->dialing_num, i); p+=i; strcpy(p, "] to ["); p += strlen(p); i = ntohs(packet->dialed_len); if (i>sizeof(packet->dialed_num)) i = sizeof(packet->dialed_num); memcpy(p, packet->dialed_num, i); p+=i; strcpy(p, "]"); LOG("%s", tmpbuf); gre_init(ctrl); } rply->peers_call_id = packet->call_id; rply->error_code = NO_ERROR; rply->pckt_recv_size = hton16(PCKT_RECV_WINDOW_SIZE); rply->pckt_delay = htons(PCKT_PROCESS_DELAY); send_packet(ctrl, rply); free(rply); return 0; } int handle_in_call_rply(struct control_struct * ctrl, struct pptp_in_call_rply * packet) { /* non-important TODO */ return 0; } int handle_in_call_conn(struct control_struct * ctrl, struct pptp_in_call_connect * packet) { /* FIXME: use the parameters from the packet */ LOG("connected at %u bps, %ssynchronous framing", htonl(packet->speed), (htonl(packet->framing_type) == 1) ? "a" : ""); return 0; } int handle_call_clr_rqst(struct control_struct * ctrl, struct pptp_call_clr_rqst * packet) { /* non-important TODO */ return 0; } int handle_call_disconn_ntfy(struct control_struct * ctrl, struct pptp_call_disconn_ntfy * packet) { char * reason; struct ppp_struct ** pp = &(ctrl->ppp); unlink_and_release_ppp(&pp, packet->call_id); switch(packet->result_code) { case LOST_CARRIER: reason="lost carrier"; break; case GENERAL_ERROR: reason="general error"; break; case ADMIN_SHUTDOWN: reason="admin shutdown"; break; case CALL_CLEAR_REQUEST: reason="our request"; break; default: reason="(unknown reason)"; } LOG("call disconnect reason: %s", reason); return 0; } int handle_wan_err_ntfy(struct control_struct * ctrl, struct pptp_wan_err_ntfy * packet) { return 0; } int handle_set_link_info(struct control_struct * ctrl, struct pptp_set_link_info * packet) { return 0; }