summaryrefslogtreecommitdiff
path: root/accel-pptpd/pptp.c
diff options
context:
space:
mode:
Diffstat (limited to 'accel-pptpd/pptp.c')
-rw-r--r--accel-pptpd/pptp.c399
1 files changed, 399 insertions, 0 deletions
diff --git a/accel-pptpd/pptp.c b/accel-pptpd/pptp.c
new file mode 100644
index 0000000..6d24460
--- /dev/null
+++ b/accel-pptpd/pptp.c
@@ -0,0 +1,399 @@
+/*
+* C Implementation: ctrl
+*
+* Description:
+*
+*
+* Author: <xeb@mail.ru>, (C) 2009
+*
+* Copyright: See COPYING file that comes with this distribution
+*
+*/
+
+#include <unistd.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <stdarg.h>
+#include <errno.h>
+#include <string.h>
+#include <arpa/inet.h>
+#include <netinet/in.h>
+
+#include "if_pppox.h"
+
+#include "list.h"
+#include "pptp_prot.h"
+#include "triton/triton.h"
+#include "pptpd.h"
+#include "log.h"
+#include "ppp.h"
+
+
+#define TIMEOUT 10000
+
+#define STATE_IDLE 0
+#define STATE_ESTB 1
+#define STATE_PPP 2
+#define STATE_FIN 10
+
+struct pptp_conn_t
+{
+ struct triton_md_handler_t hnd;
+ int state;
+
+ u_int8_t *in_buf;
+ int in_size;
+ u_int8_t *out_buf;
+ int out_size;
+ int out_pos;
+
+ struct ppp_t ppp;
+};
+
+static void pptp_read(struct triton_md_handler_t *h);
+static void pptp_write(struct triton_md_handler_t *h);
+static void pptp_timeout(struct triton_md_handler_t *h);
+static void ppp_started(struct ppp_t *);
+static void ppp_finished(struct ppp_t *);
+
+static void ctrl_read(struct triton_md_handler_t *h)
+{
+ struct pptp_conn_t *conn;
+
+ int fd;
+ int n=read(h->fd,&fd,sizeof(fd));
+ if (n!=4)
+ {
+ log_error("too short message from controlling thread\n");
+ return;
+ }
+
+ conn=malloc(sizeof(*conn));
+ memset(conn,0,sizeof(*conn));
+ conn->in_buf=malloc(PPTP_CTRL_SIZE_MAX);
+ conn->out_buf=malloc(PPTP_CTRL_SIZE_MAX);
+ conn->hnd.fd=fd;
+ conn->hnd.twait=TIMEOUT;
+ conn->hnd.read=pptp_read;
+ conn->hnd.write=pptp_write;
+ conn->hnd.timeout=pptp_timeout;
+
+ triton_md_register_handler(&conn->hnd);
+ triton_md_enable_handler(&conn->hnd,MD_MODE_READ);
+}
+
+int ctrl_init(struct ctrl_thread_t*ctrl)
+{
+ struct triton_md_handler_t *h=malloc(sizeof(*h));
+ memset(h,0,sizeof(*h));
+ h->fd=ctrl->pipe_fd[0];
+ h->twait=-1;
+ h->read=ctrl_read;
+ triton_md_register_handler(h);
+ triton_md_enable_handler(h,MD_MODE_READ);
+
+ return 0;
+}
+
+static void disconnect(struct pptp_conn_t *conn)
+{
+ close(conn->hnd.fd);
+ triton_md_unregister_handler(&conn->hnd);
+ free(conn);
+}
+
+static int post_msg(struct pptp_conn_t *conn,void *buf,int size)
+{
+ int n;
+ if (conn->out_size)
+ {
+ log_debug("post_msg: buffer is not empty\n");
+ return -1;
+ }
+
+ n=write(conn->hnd.fd,buf,size);
+ if (n<0)
+ {
+ if (errno==EINTR) n=0;
+ else
+ {
+ log_debug("post_msg: failed to write socket %i\n",errno);
+ return -1;
+ }
+ }
+
+ if (n<size)
+ {
+ memcpy(conn->out_buf,buf+n,size-n);
+ triton_md_enable_handler(&conn->hnd,MD_MODE_WRITE);
+ }
+
+ return 0;
+}
+
+static int send_pptp_stop_ctrl_conn_rqst(struct pptp_conn_t *conn,int reason,int err_code)
+{
+ struct pptp_stop_ctrl_conn msg={
+ .header=PPTP_HEADER_CTRL(PPTP_STOP_CTRL_CONN_RQST),
+ .reason_result=hton8(reason),
+ .error_code=hton8(err_code),
+ };
+
+ return post_msg(conn,&msg,sizeof(msg));
+}
+
+static int send_pptp_stop_ctrl_conn_rply(struct pptp_conn_t *conn,int reason,int err_code)
+{
+ struct pptp_stop_ctrl_conn msg={
+ .header=PPTP_HEADER_CTRL(PPTP_STOP_CTRL_CONN_RPLY),
+ .reason_result=hton8(reason),
+ .error_code=hton8(err_code),
+ };
+
+ return post_msg(conn,&msg,sizeof(msg));
+}
+static int pptp_stop_ctrl_conn_rqst(struct pptp_conn_t *conn)
+{
+ struct pptp_stop_ctrl_conn *msg=(struct pptp_stop_ctrl_conn *)conn->in_buf;
+ log_info("PPTP_STOP_CTRL_CONN_RQST reason=%i error_code=%i\n",msg->reason_result,msg->error_code);
+
+ if (conn->state==STATE_PPP)
+ ppp_terminate(&conn->ppp);
+
+ conn->state=STATE_FIN;
+ conn->hnd.twait=1000;
+
+ return send_pptp_stop_ctrl_conn_rply(conn,PPTP_CONN_STOP_OK,0);
+}
+
+static int send_pptp_start_ctrl_conn_rply(struct pptp_conn_t *conn,int res_code,int err_code)
+{
+ struct pptp_start_ctrl_conn msg={
+ .header=PPTP_HEADER_CTRL(PPTP_START_CTRL_CONN_RPLY),
+ .version=htons(PPTP_VERSION),
+ .result_code=res_code,
+ .error_code=err_code,
+ .framing_cap=htonl(PPTP_FRAME_SYNC),
+ .bearer_cap=htonl(0),
+ .max_channels=htons(1),
+ .firmware_rev=htons(PPTP_FIRMWARE_VERSION),
+ };
+
+ memset(msg.hostname,0,sizeof(msg.hostname));
+ strcpy((char*)msg.hostname,PPTP_HOSTNAME);
+
+ memset(msg.vendor,0,sizeof(msg.vendor));
+ strcpy((char*)msg.vendor,PPTP_VENDOR);
+
+ return post_msg(conn,&msg,sizeof(msg));
+}
+static int pptp_start_ctrl_conn_rqst(struct pptp_conn_t *conn)
+{
+ struct pptp_start_ctrl_conn *msg=(struct pptp_start_ctrl_conn *)conn->in_buf;
+
+ if (conn->state!=STATE_IDLE)
+ {
+ log_info("unexpected PPTP_START_CTRL_CONN_RQST\n");
+ if (send_pptp_start_ctrl_conn_rply(conn,PPTP_CONN_RES_EXISTS,0))
+ return -1;
+ return 0;
+ }
+
+ if (msg->version!=htons(PPTP_VERSION))
+ {
+ log_info("PPTP version mismatch: expecting %x, received %s\n",PPTP_VERSION,msg->version);
+ if (send_pptp_start_ctrl_conn_rply(conn,PPTP_CONN_RES_PROTOCOL,0))
+ return -1;
+ return 0;
+ }
+ if (!(ntohl(msg->framing_cap)&PPTP_FRAME_SYNC))
+ {
+ log_info("connection does not supports sync mode\n");
+ if (send_pptp_start_ctrl_conn_rply(conn,PPTP_CONN_RES_GE,0))
+ return -1;
+ return 0;
+ }
+ if (send_pptp_start_ctrl_conn_rply(conn,PPTP_CONN_RES_SUCCESS,0))
+ return -1;
+
+ conn->state=STATE_ESTB;
+
+ return 0;
+}
+
+static int send_pptp_out_call_rply(struct pptp_conn_t *conn,struct pptp_out_call_rqst *rqst,int call_id,int res_code,int err_code)
+{
+ struct pptp_out_call_rply msg={
+ .header=PPTP_HEADER_CTRL(PPTP_OUT_CALL_RPLY),
+ .call_id=htons(call_id),
+ .call_id_peer=rqst->call_id,
+ .result_code=res_code,
+ .error_code=err_code,
+ .cause_code=0,
+ .speed=rqst->bps_max,
+ .recv_size=rqst->recv_size,
+ .delay=0,
+ .channel=0,
+ };
+
+ return post_msg(conn,&msg,sizeof(msg));
+}
+
+static int pptp_out_call_rqst(struct pptp_conn_t *conn)
+{
+ struct pptp_out_call_rqst *msg=(struct pptp_out_call_rqst *)conn->in_buf;
+ struct sockaddr_pppox src_addr,dst_addr;
+ struct sockaddr_in addr;
+ socklen_t addrlen;
+ int pptp_sock;
+
+ if (conn->state!=STATE_ESTB)
+ {
+ log_info("unexpected PPTP_OUT_CALL_RQST\n");
+ if (send_pptp_out_call_rply(conn,msg,0,PPTP_CALL_RES_GE,PPTP_GE_NOCONN))
+ return -1;
+ return 0;
+ }
+
+ src_addr.sa_family=AF_PPPOX;
+ src_addr.sa_protocol=PX_PROTO_PPTP;
+ src_addr.sa_addr.pptp.call_id=0;
+ addrlen=sizeof(addr); getsockname(conn->hnd.fd,(struct sockaddr*)&addr,&addrlen);
+ src_addr.sa_addr.pptp.sin_addr=addr.sin_addr;
+
+ dst_addr.sa_family=AF_PPPOX;
+ dst_addr.sa_protocol=PX_PROTO_PPTP;
+ dst_addr.sa_addr.pptp.call_id=htons(msg->call_id);
+ addrlen=sizeof(addr); getpeername(conn->hnd.fd,(struct sockaddr*)&addr,&addrlen);
+ dst_addr.sa_addr.pptp.sin_addr=addr.sin_addr;
+
+ pptp_sock=socket(AF_PPPOX,SOCK_STREAM,PX_PROTO_PPTP);
+ if (pptp_sock<0)
+ {
+ log_error("failed to create PPTP socket (%s)\n",strerror(errno));
+ return -1;
+ }
+ if (bind(pptp_sock,(struct sockaddr*)&src_addr,sizeof(src_addr)))
+ {
+ log_error("failed to bind PPTP socket (%s)\n",strerror(errno));
+ close(pptp_sock);
+ return -1;
+ }
+ addrlen=sizeof(src_addr);
+ getsockname(pptp_sock,(struct sockaddr*)&src_addr,&addrlen);
+
+ if (connect(pptp_sock,(struct sockaddr*)&dst_addr,sizeof(dst_addr)))
+ {
+ log_error("failed to connect PPTP socket (%s)\n",strerror(errno));
+ close(pptp_sock);
+ return -1;
+ }
+
+ if (send_pptp_out_call_rply(conn,msg,src_addr.sa_addr.pptp.call_id,PPTP_CALL_RES_OK,0))
+ return -1;
+
+ conn->ppp.fd=pptp_sock;
+ conn->ppp.chan_name=strdup(inet_ntoa(dst_addr.sa_addr.pptp.sin_addr));
+ conn->ppp.events.started=ppp_started;
+ conn->ppp.events.finished=ppp_finished;
+ if (establish_ppp(&conn->ppp))
+ {
+ close(pptp_sock);
+ send_pptp_stop_ctrl_conn_rqst(conn,0,0);
+ conn->state=STATE_FIN;
+ conn->hnd.twait=1000;
+ }else conn->state=STATE_PPP;
+
+ return 0;
+}
+
+static int process_packet(struct pptp_conn_t *conn)
+{
+ struct pptp_header *hdr=(struct pptp_header *)conn->in_buf;
+ switch(ntohs(hdr->ctrl_type))
+ {
+ case PPTP_START_CTRL_CONN_RQST:
+ return pptp_start_ctrl_conn_rqst(conn);
+ case PPTP_STOP_CTRL_CONN_RQST:
+ return pptp_stop_ctrl_conn_rqst(conn);
+ case PPTP_OUT_CALL_RQST:
+ return pptp_out_call_rqst(conn);
+ }
+ return 0;
+}
+
+static void pptp_read(struct triton_md_handler_t *h)
+{
+ struct pptp_conn_t *conn=container_of(h,typeof(*conn),hnd);
+ struct pptp_header *hdr=(struct pptp_header *)conn->in_buf;
+ int n;
+
+ n=read(h->fd,conn->in_buf,PPTP_CTRL_SIZE_MAX-conn->in_size);
+ if (n<=0)
+ {
+ if (errno==EAGAIN) return;
+ disconnect(conn);
+ return;
+ }
+ conn->in_size+=n;
+ if (conn->in_size>=sizeof(*hdr))
+ {
+ if (hdr->magic!=htonl(PPTP_MAGIC)) goto drop;
+ if (ntohs(hdr->length)>=PPTP_CTRL_SIZE_MAX) goto drop;
+ if (ntohs(hdr->length)>conn->in_size) goto drop;
+ if (ntohs(hdr->length)==conn->in_size)
+ {
+ if (ntohs(hdr->length)!=PPTP_CTRL_SIZE(ntohs(hdr->ctrl_type))) goto drop;
+ if (process_packet(conn)) goto drop;
+ conn->in_size=0;
+ }
+ }
+ h->twait=TIMEOUT;
+ return;
+drop:
+ disconnect(conn);
+ return;
+}
+static void pptp_write(struct triton_md_handler_t *h)
+{
+ struct pptp_conn_t *conn=container_of(h,typeof(*conn),hnd);
+ int n=write(h->fd,conn->out_buf+conn->out_pos,conn->out_size-conn->out_pos);
+
+ if (n<0)
+ {
+ if (errno==EINTR) n=0;
+ else
+ {
+ log_debug("post_msg: failed to write socket %i\n",errno);
+ disconnect(conn);
+ return;
+ }
+ }
+
+ conn->out_pos+=n;
+ if (conn->out_pos==conn->out_size)
+ {
+ conn->out_pos=0;
+ conn->out_size=0;
+ triton_md_disable_handler(h,MD_MODE_WRITE);
+ }
+ h->twait=TIMEOUT;
+}
+static void pptp_timeout(struct triton_md_handler_t *h)
+{
+}
+
+static void ppp_started(struct ppp_t *ppp)
+{
+ log_msg("ppp_started\n");
+}
+static void ppp_finished(struct ppp_t *ppp)
+{
+ struct pptp_conn_t *conn=container_of(ppp,typeof(*conn),ppp);
+
+ log_msg("ppp_finished\n");
+ close(conn->ppp.fd);
+ send_pptp_stop_ctrl_conn_rqst(conn,0,0);
+ conn->state=STATE_FIN;
+ conn->hnd.twait=1000;
+}