summaryrefslogtreecommitdiff
path: root/accel-pppd
diff options
context:
space:
mode:
authorVladislav Grishenko <themiron@mail.ru>2017-02-02 14:32:41 +0500
committerVladislav Grishenko <themiron@mail.ru>2017-12-30 22:45:41 +0500
commit10cb4c07ec476872210906a0e4eb09068903f324 (patch)
tree7c5727753dc57078c50f20c202c2abe9b8656a80 /accel-pppd
parentbff66de7a08a6c62d263efc4fd2ef151094c83b7 (diff)
downloadaccel-ppp-xebd-10cb4c07ec476872210906a0e4eb09068903f324.tar.gz
accel-ppp-xebd-10cb4c07ec476872210906a0e4eb09068903f324.zip
sstp: implement preliminar sstp protocol support
Diffstat (limited to 'accel-pppd')
-rw-r--r--accel-pppd/accel-ppp.conf11
-rw-r--r--accel-pppd/ctrl/CMakeLists.txt1
-rw-r--r--accel-pppd/ctrl/sstp/CMakeLists.txt5
-rw-r--r--accel-pppd/ctrl/sstp/sstp.c1483
-rw-r--r--accel-pppd/ctrl/sstp/sstp_prot.h187
5 files changed, 1687 insertions, 0 deletions
diff --git a/accel-pppd/accel-ppp.conf b/accel-pppd/accel-ppp.conf
index a942d2c..2a87f10 100644
--- a/accel-pppd/accel-ppp.conf
+++ b/accel-pppd/accel-ppp.conf
@@ -6,6 +6,7 @@ log_file
pptp
l2tp
+#sstp
#pppoe
#ipoe
@@ -107,6 +108,16 @@ verbose=1
#ip-pool=l2tp
#ifname=l2tp%d
+[sstp]
+verbose=1
+#ssl=1
+#ssl_ciphers=HIGH:!aNULL:!kRSA:!PSK:!SRP:!MD5:!RC4
+#ssl_ca_file=/etc/ssl/sstp-ca.crt
+#ssl_pemfile=/etc/ssl/sstp.pem
+#timeout=60
+#hello-interval=60
+#ip-pool=sstp
+
[ipoe]
verbose=1
username=ifname
diff --git a/accel-pppd/ctrl/CMakeLists.txt b/accel-pppd/ctrl/CMakeLists.txt
index 9b6a11d..b218625 100644
--- a/accel-pppd/ctrl/CMakeLists.txt
+++ b/accel-pppd/ctrl/CMakeLists.txt
@@ -1,4 +1,5 @@
ADD_SUBDIRECTORY(pptp)
ADD_SUBDIRECTORY(pppoe)
ADD_SUBDIRECTORY(l2tp)
+ADD_SUBDIRECTORY(sstp)
ADD_SUBDIRECTORY(ipoe)
diff --git a/accel-pppd/ctrl/sstp/CMakeLists.txt b/accel-pppd/ctrl/sstp/CMakeLists.txt
new file mode 100644
index 0000000..006a93b
--- /dev/null
+++ b/accel-pppd/ctrl/sstp/CMakeLists.txt
@@ -0,0 +1,5 @@
+INCLUDE_DIRECTORIES(${CMAKE_CURRENT_SOURCE_DIR})
+
+ADD_LIBRARY(sstp SHARED sstp.c)
+
+INSTALL(TARGETS sstp LIBRARY DESTINATION lib${LIB_SUFFIX}/accel-ppp)
diff --git a/accel-pppd/ctrl/sstp/sstp.c b/accel-pppd/ctrl/sstp/sstp.c
new file mode 100644
index 0000000..b78ecc6
--- /dev/null
+++ b/accel-pppd/ctrl/sstp/sstp.c
@@ -0,0 +1,1483 @@
+#include <unistd.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <stdarg.h>
+#include <errno.h>
+#include <string.h>
+#include <fcntl.h>
+#include <time.h>
+#include <termios.h>
+#include <arpa/inet.h>
+#include <netinet/in.h>
+#include <netinet/tcp.h>
+#include <sys/socket.h>
+#include <sys/ioctl.h>
+
+#ifdef CRYPTO_OPENSSL
+#include <openssl/ssl.h>
+#include <openssl/err.h>
+#endif
+
+#include "triton.h"
+#include "events.h"
+#include "list.h"
+#include "log.h"
+#include "ppp.h"
+#include "utils.h"
+#include "mempool.h"
+#include "iprange.h"
+#include "connlimit.h"
+#include "cli.h"
+
+#include "memdebug.h"
+
+#include "sstp_prot.h"
+
+#ifndef min
+#define min(x,y) ((x) < (y) ? (x) : (y))
+#endif
+
+struct sstp_serv_t {
+ struct triton_context_t ctx;
+ struct triton_md_handler_t hnd;
+
+ //uint8_t certificate_hash[32];
+};
+
+struct sstp_conn_t {
+ struct triton_context_t ctx;
+ struct triton_md_handler_t hnd;
+ struct triton_md_handler_t ppp_hnd;
+ struct triton_timer_t timeout_timer;
+ struct triton_timer_t hello_timer;
+
+#ifdef CRYPTO_OPENSSL
+ SSL_CTX *ssl_ctx;
+ SSL *ssl;
+#endif
+ int state;
+ int sstp_state;
+ int hello_sent;
+
+// int bypass_auth:1;
+// char *http_cookie;
+// uint8_t auth_key[32];
+
+ struct list_head send_queue;
+ void *ppp_buf;
+ uint8_t *in_buf;
+ int in_size;
+
+ struct ap_ctrl ctrl;
+ struct ppp_t ppp;
+};
+
+struct sstp_pack_t {
+ struct list_head entry;
+ void *data;
+ int size;
+};
+
+static int conf_timeout = SSTP_NEGOTIOATION_TIMEOUT;
+static int conf_hello_interval = SSTP_HELLO_TIMEOUT;
+static int conf_verbose = 0;
+static int conf_ppp_max_mtu = SSTP_MAX_PACKET_SIZE - 8;
+static int conf_hash_protocol = CERT_HASH_PROTOCOL_SHA256;
+//static int conf_bypass_auth = 0;
+static const char *conf_ip_pool;
+static int conf_ssl = 1;
+static char *conf_ssl_ciphers = "HIGH:!aNULL:!kRSA:!PSK:!SRP:!MD5:!RC4";
+static char *conf_ssl_ca_file = NULL;
+static char *conf_ssl_pemfile = NULL;
+
+static mempool_t conn_pool;
+static mempool_t pack_pool;
+static mempool_t data_pool;
+
+static unsigned int stat_starting;
+static unsigned int stat_active;
+
+static int sstp_msg_call_abort(struct sstp_conn_t *conn);
+static int sstp_msg_call_disconnect(struct sstp_conn_t *conn);
+static int sstp_send(struct sstp_conn_t *conn, void *data, int size);
+
+/* http */
+
+static char *http_getline(struct sstp_conn_t *conn, int *pos, char *buf, int size)
+{
+ unsigned char *src, *dst, c, pc;
+
+ size = min(size - 1, conn->in_size - *pos);
+ if (size <= 0)
+ return NULL;
+
+ src = conn->in_buf + *pos;
+ dst = (unsigned char *)buf;
+ for (pc = 0; size--; dst++) {
+ c = *dst = *src++;
+ if (c == '\0')
+ break;
+ if (c == '\n') {
+ if (pc == '\r')
+ dst--;
+ break;
+ }
+ pc = c;
+ }
+ *dst = '\0';
+
+ *pos = src - conn->in_buf;
+
+ return buf;
+}
+
+static int send_http_response(struct sstp_conn_t *conn, char *proto, char *status, char *headers)
+{
+ char timebuf[80], *msg = mempool_alloc(data_pool);
+ time_t now = time(NULL);
+
+ if (!msg) {
+ log_error("sstp: no memory\n");
+ return -1;
+ }
+
+ strftime(timebuf, sizeof(timebuf), "%a, %d %b %Y %H:%M:%S GMT", gmtime(&now));
+ snprintf(msg, SSTP_MAX_PACKET_SIZE,
+ "%s %s\r\n"
+ "%s"
+ "Server: Microsoft-HTTPAPI/2.0\r\n"
+ "Date: %s\r\n"
+ "\r\n", proto, status, headers, timebuf);
+
+ if (conf_verbose)
+ log_ppp_debug("send [sstp HTTP reply <%s %s>]\n", proto, status);
+
+ return sstp_send(conn, msg, strlen(msg));
+}
+
+static int http_request(struct sstp_conn_t *conn)
+{
+ char buf[1024];
+ char *line, *method, *request, *proto;
+ int pos = 0;
+
+ if (conn->sstp_state != STATE_SERVER_CALL_DISCONNECTED)
+ return -1;
+
+ line = http_getline(conn, &pos, buf, sizeof(buf));
+ if (line == NULL)
+ goto error;
+ if (conf_verbose)
+ log_ppp_debug("recv [sstp HTTP request <%s>]\n", line);
+
+ method = strsep(&line, " ");
+ request = strsep(&line, " ");
+ proto = strsep(&line, " ");
+
+ if (!method || !request || !proto) {
+ send_http_response(conn, "HTTP/1.1", "400 Bad Request", NULL);
+ goto error;
+ }
+ if (strncmp(proto, "HTTP/1", sizeof("HTTP/1") - 1) != 0) {
+ send_http_response(conn, "HTTP/1.1", "505 HTTP Version Not Supported", NULL);
+ goto error;
+ }
+ if (strcmp(method, SSTP_HTTP_METHOD) != 0) {
+ send_http_response(conn, proto, "405 Method Not Allowed", NULL);
+ goto error;
+ }
+ if (strcmp(request, SSTP_HTTP_URI) != 0) {
+ send_http_response(conn, proto, "404 Not Found", NULL);
+ goto error;
+ }
+
+ while ((line = http_getline(conn, &pos, buf, sizeof(buf))) != NULL) {
+ if (*line == '\0')
+ break;
+ if (conf_verbose)
+ log_ppp_debug("recv [sstp HTTP request <%s>]\n", line);
+ } while (*line);
+
+ if (send_http_response(conn, proto, "200 OK",
+ "Content-Length: 18446744073709551615\r\n")) {
+ goto error;
+ }
+ conn->sstp_state = STATE_SERVER_CONNECT_REQUEST_PENDING;
+
+ return pos;
+
+error:
+ return -1;
+}
+
+/* ppp */
+
+static int ppp_allocate_pty(int *master, int *slave, int flags)
+{
+ struct termios tios;
+ char pty_name[16];
+ int value, mfd, sfd = -1;
+
+ mfd = open("/dev/ptmx", O_RDWR | flags);
+ if (mfd < 0) {
+ log_ppp_error("sstp: can't open pty %s: %s\n", "/dev/ptmx", strerror(errno));
+ return -1;
+ }
+
+ if (ioctl(mfd, TIOCGPTN, &value) < 0) {
+ log_ppp_error("sstp: can't allocate slave pty: %s\n", strerror(errno));
+ goto error;
+ }
+ snprintf(pty_name, sizeof(pty_name), "/dev/pts/%d", value);
+
+ value = 0;
+ if (ioctl(mfd, TIOCSPTLCK, &value) < 0)
+ log_ppp_warn("sstp: can't unlock pty %s: %s\n", pty_name, strerror(errno));
+
+ sfd = open(pty_name, O_RDWR | O_NOCTTY | flags);
+ if (sfd < 0) {
+ log_ppp_error("sstp: can't open pty %s: %s\n", pty_name, strerror(errno));
+ goto error;
+ }
+
+ if (tcgetattr(sfd, &tios) == 0) {
+ tios.c_cflag &= ~(CSIZE | CSTOPB | PARENB);
+ tios.c_cflag |= CS8 | CREAD | CLOCAL;
+ tios.c_iflag = IGNPAR;
+ tios.c_oflag = 0;
+ tios.c_lflag = 0;
+ if (tcsetattr(sfd, TCSAFLUSH, &tios) < 0)
+ log_ppp_warn("sstp: can't set attributes on pty: %s\n", strerror(errno));
+ }
+
+ value = N_HDLC;
+ if (ioctl(mfd, TIOCSETD, &value) < 0) {
+ log_ppp_error("sstp: can't set N_HDLC line discipline: %s", strerror(errno));
+ goto error;
+ }
+
+ value = N_SYNC_PPP;
+ if (ioctl(sfd, TIOCSETD, &value) < 0) {
+ log_ppp_error("sstp: can't set N_SYNC_PPP line discipline: %s", strerror(errno));
+ goto error;
+ }
+
+ *master = mfd;
+ *slave = sfd;
+ return 0;
+
+error:
+ if (mfd >= 0)
+ close(mfd);
+ if (sfd >= 0)
+ close(sfd);
+ return -1;
+}
+
+static void ppp_started(struct ap_session *ses)
+{
+ log_ppp_debug("sstp: ppp started\n");
+}
+
+static void ppp_finished(struct ap_session *ses)
+{
+ struct ppp_t *ppp = container_of(ses, typeof(*ppp), ses);
+ struct sstp_conn_t *conn = container_of(ppp, typeof(*conn), ppp);
+
+ if (conn->state != STATE_CLOSE) {
+ log_ppp_debug("sstp: ppp finished\n");
+ __sync_sub_and_fetch(&stat_active, 1);
+ conn->state = STATE_CLOSE;
+ sstp_msg_call_abort(conn);
+ }
+}
+
+static int ppp_read(struct triton_md_handler_t *h)
+{
+ struct sstp_conn_t *conn = container_of(h, typeof(*conn), ppp_hnd);
+ struct sstp_hdr *hdr;
+ int n;
+
+ hdr = conn->ppp_buf ? : mempool_alloc(data_pool);
+ if (!hdr) {
+ log_error("sstp: no memory\n");
+ return -1;
+ }
+
+again:
+ n = read(h->fd, hdr->data, SSTP_MAX_PACKET_SIZE - sizeof(*hdr));
+
+//int err = errno;
+//log_ppp_info2("sstp: ppp.read = %d [%02x%02x...] errno %d %s\n", n,
+// (n > 0) ? hdr->data[0] : 0,
+// (n > 1) ? hdr->data[1] : 0,
+// (n < 0) ? errno : 0, (n < 0) ? strerror(errno) : "");
+//errno = err;
+
+ if (n < 0) {
+ if (errno == EINTR)
+ goto again;
+ if (errno == EAGAIN) {
+ conn->ppp_buf = hdr;
+ return 0;
+ }
+ if (errno != EPIPE && conf_verbose)
+ log_ppp_error("ppp error: %s\n", strerror(errno));
+ goto drop;
+ }
+ if (n == 0) {
+ if (conf_verbose)
+ log_ppp_info2("ppp error\n");
+ goto drop;
+ }
+
+ switch (conn->sstp_state) {
+ case STATE_SERVER_CALL_CONNECTED_PENDING:
+ case STATE_SERVER_CALL_CONNECTED:
+ break;
+ default:
+ goto drop;
+ }
+
+ n += sizeof(*hdr);
+ INIT_SSTP_DATA_HDR(hdr, n);
+
+ if (sstp_send(conn, hdr, n))
+ goto drop;
+
+ conn->ppp_buf = NULL;
+ return 0;
+
+drop:
+ conn->ppp_buf = hdr;
+ return 1;
+}
+
+/* sstp */
+
+static void sstp_ctx_switch(struct triton_context_t *ctx, void *arg)
+{
+ if (arg) {
+ struct ap_session *s = arg;
+ net = s->net;
+ } else
+ net = def_net;
+ log_switch(ctx, arg);
+}
+
+static void sstp_timer_set(struct triton_context_t *ctx, struct triton_timer_t *t, int timeout)
+{
+ t->period = timeout * 1000;
+
+ if (timeout == 0)
+ triton_timer_del(t);
+ else if (t->tpd)
+ triton_timer_mod(t, 0);
+ else
+ triton_timer_add(ctx, t, 0);
+}
+
+static void sstp_disconnect(struct sstp_conn_t *conn)
+{
+ struct sstp_pack_t *pack;
+
+ log_ppp_debug("sstp: disconnect\n");
+
+#ifdef CRYPTO_OPENSSL
+ if (conn->ssl)
+ SSL_free(conn->ssl);
+#endif
+ triton_md_unregister_handler(&conn->hnd, 1);
+
+ if (conn->timeout_timer.tpd)
+ triton_timer_del(&conn->timeout_timer);
+ if (conn->hello_timer.tpd)
+ triton_timer_del(&conn->hello_timer);
+
+ if (conn->state == STATE_PPP) {
+ __sync_sub_and_fetch(&stat_active, 1);
+ conn->state = STATE_CLOSE;
+ ap_session_terminate(&conn->ppp.ses, TERM_LOST_CARRIER, 1);
+ } else if (conn->state != STATE_CLOSE)
+ __sync_sub_and_fetch(&stat_starting, 1);
+
+ if (conn->ppp_hnd.tpd)
+ triton_md_unregister_handler(&conn->ppp_hnd, 1);
+
+ triton_event_fire(EV_CTRL_FINISHED, &conn->ppp.ses);
+
+ log_ppp_info1("disconnected\n");
+
+#ifdef CRYPTO_OPENSSL
+ if (conn->ssl_ctx)
+ SSL_CTX_free(conn->ssl_ctx);
+#endif
+ triton_context_unregister(&conn->ctx);
+
+ while (!list_empty(&conn->send_queue)) {
+ pack = list_first_entry(&conn->send_queue, typeof(*pack), entry);
+ list_del(&pack->entry);
+ mempool_free(pack->data);
+ mempool_free(pack);
+ }
+
+ if (conn->ppp_buf)
+ mempool_free(conn->ppp_buf);
+ _free(conn->in_buf);
+ _free(conn->ctrl.calling_station_id);
+ _free(conn->ctrl.called_station_id);
+ mempool_free(conn);
+}
+
+static int send_sstp_msg_call_connect_ack(struct sstp_conn_t *conn)
+{
+ struct {
+ struct sstp_ctrl_hdr hdr;
+ struct sstp_attrib_crypto_binding_request attr;
+ } __attribute__((packed)) *msg = mempool_alloc(data_pool);
+
+ if (conf_verbose)
+ log_ppp_info2("send [sstp SSTP_MSG_CALL_CONNECT_ACK]\n");
+
+ if (!msg) {
+ log_error("sstp: no memory\n");
+ return -1;
+ }
+
+ INIT_SSTP_CTRL_HDR(&msg->hdr, SSTP_MSG_CALL_CONNECT_ACK, 1, sizeof(*msg));
+ INIT_SSTP_ATTR_HDR(&msg->attr.hdr, SSTP_ATTRIB_CRYPTO_BINDING_REQ, sizeof(msg->attr));
+ msg->attr.hash_protocol_bitmask = conf_hash_protocol;
+ //read(urandom_fd, msg->attr.nonce, sizeof(msg->attr.nonce));
+ memset(msg->attr.nonce, 0, sizeof(msg->attr.nonce));
+
+ return sstp_send(conn, msg, sizeof(*msg));
+}
+
+static int send_sstp_msg_call_connect_nak(struct sstp_conn_t *conn)
+{
+ struct {
+ struct sstp_ctrl_hdr hdr;
+ struct sstp_attrib_status_info attr;
+ uint16_t attr_value;
+ } __attribute__((packed)) *msg = mempool_alloc(data_pool);
+
+ if (conf_verbose)
+ log_ppp_info2("send [sstp SSTP_MSG_CALL_CONNECT_NAK]\n");
+
+ if (!msg) {
+ log_error("sstp: no memory\n");
+ return -1;
+ }
+
+ INIT_SSTP_CTRL_HDR(&msg->hdr, SSTP_MSG_CALL_CONNECT_NAK, 1, sizeof(*msg));
+ INIT_SSTP_ATTR_HDR(&msg->attr.hdr, SSTP_ATTRIB_STATUS_INFO, sizeof(msg->attr));
+ msg->attr.attrib_id = SSTP_ATTRIB_ENCAPSULATED_PROTOCOL_ID;
+ msg->attr.status = htonl(ATTRIB_STATUS_VALUE_NOT_SUPPORTED);
+ msg->attr_value = 0;
+
+ return sstp_send(conn, msg, sizeof(*msg));
+}
+
+static int send_sstp_msg_call_abort(struct sstp_conn_t *conn)
+{
+ struct {
+ struct sstp_ctrl_hdr hdr;
+ struct sstp_attrib_status_info attr;
+ } __attribute__((packed)) *msg = mempool_alloc(data_pool);
+
+ if (conf_verbose)
+ log_ppp_info2("send [sstp SSTP_MSG_CALL_ABORT]\n");
+
+ if (!msg) {
+ log_error("sstp: no memory\n");
+ return -1;
+ }
+
+ INIT_SSTP_CTRL_HDR(&msg->hdr, SSTP_MSG_CALL_ABORT, 1, sizeof(*msg));
+ INIT_SSTP_ATTR_HDR(&msg->attr.hdr, SSTP_ATTRIB_STATUS_INFO, sizeof(msg->attr));
+ msg->attr.attrib_id = SSTP_ATTRIB_STATUS_INFO;
+ msg->attr.status = htonl(ATTRIB_STATUS_INVALID_FRAME_RECEIVED);
+
+ if (sstp_send(conn, msg, sizeof(*msg)))
+ return -1;
+
+ switch (conn->sstp_state) {
+ case STATE_CALL_ABORT_IN_PROGRESS_1:
+ sstp_timer_set(&conn->ctx, &conn->timeout_timer, SSTP_ABORT_TIMEOUT_1);
+ conn->sstp_state = STATE_CALL_ABORT_PENDING;
+ break;
+ case STATE_CALL_ABORT_IN_PROGRESS_2:
+ sstp_timer_set(&conn->ctx, &conn->timeout_timer, SSTP_ABORT_TIMEOUT_2);
+ conn->sstp_state = STATE_CALL_ABORT_TIMEOUT_PENDING;
+ break;
+ }
+
+ return 0;
+}
+
+static int send_sstp_msg_call_disconnect(struct sstp_conn_t *conn)
+{
+ struct {
+ struct sstp_ctrl_hdr hdr;
+ struct sstp_attrib_status_info attr;
+ } __attribute__((packed)) *msg = mempool_alloc(data_pool);
+
+ if (conf_verbose)
+ log_ppp_info2("send [sstp SSTP_MSG_CALL_DISCONNECT]\n");
+
+ if (!msg) {
+ log_error("sstp: no memory\n");
+ return -1;
+ }
+
+ INIT_SSTP_CTRL_HDR(&msg->hdr, SSTP_MSG_CALL_DISCONNECT, 1, sizeof(*msg));
+ INIT_SSTP_ATTR_HDR(&msg->attr.hdr, SSTP_ATTRIB_STATUS_INFO, sizeof(msg->attr));
+ msg->attr.attrib_id = SSTP_ATTRIB_NO_ERROR;
+ msg->attr.status = htonl(ATTRIB_STATUS_NO_ERROR);
+
+ if (sstp_send(conn, msg, sizeof(*msg)))
+ return -1;
+
+ switch (conn->sstp_state) {
+ case STATE_CALL_DISCONNECT_IN_PROGRESS_1:
+ sstp_timer_set(&conn->ctx, &conn->timeout_timer, SSTP_DISCONNECT_TIMEOUT_1);
+ conn->sstp_state = STATE_CALL_DISCONNECT_ACK_PENDING;
+ break;
+ case STATE_CALL_DISCONNECT_IN_PROGRESS_2:
+ sstp_timer_set(&conn->ctx, &conn->timeout_timer, SSTP_DISCONNECT_TIMEOUT_2);
+ conn->sstp_state = STATE_CALL_DISCONNECT_TIMEOUT_PENDING;
+ break;
+ default:
+ break;
+ }
+
+ return 0;
+}
+
+static int send_sstp_msg_call_disconnect_ack(struct sstp_conn_t *conn)
+{
+ struct {
+ struct sstp_ctrl_hdr hdr;
+ } __attribute__((packed)) *msg = mempool_alloc(data_pool);
+
+ if (conf_verbose)
+ log_ppp_info2("send [sstp SSTP_MSG_CALL_DISCONNECT_ACK]\n");
+
+ if (!msg) {
+ log_error("sstp: no memory\n");
+ return -1;
+ }
+
+ INIT_SSTP_CTRL_HDR(&msg->hdr, SSTP_MSG_CALL_DISCONNECT_ACK, 0, sizeof(*msg));
+
+ return sstp_send(conn, msg, sizeof(*msg));
+}
+
+static int send_sstp_msg_echo_request(struct sstp_conn_t *conn)
+{
+ struct {
+ struct sstp_ctrl_hdr hdr;
+ } __attribute__((packed)) *msg = mempool_alloc(data_pool);
+
+ if (conf_verbose)
+ log_ppp_info2("send [sstp SSTP_MSG_ECHO_REQUEST]\n");
+
+ if (!msg) {
+ log_error("sstp: no memory\n");
+ return -1;
+ }
+
+ INIT_SSTP_CTRL_HDR(&msg->hdr, SSTP_MSG_ECHO_REQUEST, 0, sizeof(*msg));
+
+ return sstp_send(conn, msg, sizeof(*msg));
+}
+
+static int send_sstp_msg_echo_response(struct sstp_conn_t *conn)
+{
+ struct {
+ struct sstp_ctrl_hdr hdr;
+ } __attribute__((packed)) *msg = mempool_alloc(data_pool);
+
+ if (conf_verbose)
+ log_ppp_info2("send [sstp SSTP_MSG_ECHO_RESPONSE]\n");
+
+ if (!msg) {
+ log_error("sstp: no memory\n");
+ return -1;
+ }
+
+ INIT_SSTP_CTRL_HDR(&msg->hdr, SSTP_MSG_ECHO_RESPONSE, 0, sizeof(*msg));
+
+ return sstp_send(conn, msg, sizeof(*msg));
+}
+
+static int sstp_msg_call_connect_request(struct sstp_conn_t *conn)
+{
+ struct {
+ struct sstp_ctrl_hdr hdr;
+ struct sstp_attrib_encapsulated_protocol attr;
+ } __attribute__((packed)) *msg = (void *)conn->in_buf;
+ int master, slave;
+
+ if (conf_verbose)
+ log_ppp_info2("recv [sstp SSTP_MSG_CALL_CONNECT_REQUEST]\n");
+
+ switch (conn->sstp_state) {
+ case STATE_CALL_ABORT_TIMEOUT_PENDING:
+ case STATE_CALL_ABORT_PENDING:
+ case STATE_CALL_DISCONNECT_ACK_PENDING:
+ case STATE_CALL_DISCONNECT_TIMEOUT_PENDING:
+ return 0;
+ case STATE_SERVER_CONNECT_REQUEST_PENDING:
+ break;
+ default:
+ conn->sstp_state = STATE_CALL_ABORT_IN_PROGRESS_1;
+ if (send_sstp_msg_call_abort(conn))
+ return -1;
+ return 0;
+ }
+
+ if (ntohs(msg->hdr.length) < sizeof(*msg) ||
+ ntohs(msg->hdr.num_attributes) < 1 ||
+ msg->attr.hdr.attribute_id != SSTP_ATTRIB_ENCAPSULATED_PROTOCOL_ID ||
+ ntohs(msg->attr.hdr.length) < sizeof(msg->attr)) {
+ conn->sstp_state = STATE_CALL_ABORT_IN_PROGRESS_1;
+ if (send_sstp_msg_call_abort(conn))
+ return -1;
+ return 0;
+ }
+ if (ntohs(msg->attr.protocol_id) != SSTP_ENCAPSULATED_PROTOCOL_PPP) {
+ if (send_sstp_msg_call_connect_nak(conn))
+ return -1;
+ return 0;
+ }
+
+ if (ppp_allocate_pty(&master, &slave, O_CLOEXEC | O_NONBLOCK) < 0)
+ return -1;
+
+ conn->ppp_hnd.fd = master;
+ conn->ppp_hnd.read = ppp_read;
+ triton_md_register_handler(&conn->ctx, &conn->ppp_hnd);
+ triton_md_enable_handler(&conn->ppp_hnd, MD_MODE_READ);
+
+ triton_event_fire(EV_CTRL_STARTED, &conn->ppp.ses);
+
+ if (send_sstp_msg_call_connect_ack(conn))
+ goto error;
+ conn->sstp_state = STATE_SERVER_CALL_CONNECTED_PENDING;
+
+ conn->ppp.fd = slave;
+ if (establish_ppp(&conn->ppp)) {
+ conn->state = STATE_FIN;
+ goto error;
+ }
+
+ __sync_sub_and_fetch(&stat_starting, 1);
+ __sync_add_and_fetch(&stat_active, 1);
+ conn->state = STATE_PPP;
+
+ if (conn->timeout_timer.tpd)
+ triton_timer_del(&conn->timeout_timer);
+
+ if (conn->hello_timer.tpd)
+ triton_timer_mod(&conn->hello_timer, 0);
+ else if (conn->hello_timer.period)
+ triton_timer_add(&conn->ctx, &conn->hello_timer, 0);
+
+ return 0;
+
+error:
+ if (conn->ppp_hnd.tpd)
+ triton_md_unregister_handler(&conn->ppp_hnd, 0);
+ close(master);
+ close(slave);
+ return -1;
+}
+
+static int sstp_msg_call_connected(struct sstp_conn_t *conn)
+{
+ if (conf_verbose)
+ log_ppp_info2("recv [sstp SSTP_MSG_CALL_CONNECTED]\n");
+
+ switch (conn->sstp_state) {
+ case STATE_CALL_ABORT_TIMEOUT_PENDING:
+ case STATE_CALL_ABORT_PENDING:
+ case STATE_CALL_DISCONNECT_ACK_PENDING:
+ case STATE_CALL_DISCONNECT_TIMEOUT_PENDING:
+ return 0;
+ case STATE_SERVER_CALL_CONNECTED_PENDING:
+ break;
+ default:
+ conn->sstp_state = STATE_CALL_ABORT_IN_PROGRESS_1;
+ if (send_sstp_msg_call_abort(conn))
+ return -1;
+ return 0;
+ }
+
+ conn->sstp_state = STATE_SERVER_CALL_CONNECTED;
+
+ if (conn->hello_timer.tpd)
+ triton_timer_mod(&conn->hello_timer, 0);
+ else if (conn->hello_timer.period)
+ triton_timer_add(&conn->ctx, &conn->hello_timer, 0);
+
+ return 0;
+}
+
+static int sstp_msg_call_abort(struct sstp_conn_t *conn)
+{
+ if (conf_verbose)
+ log_ppp_info2("recv [sstp SSTP_MSG_CALL_ABORT]\n");
+
+ switch (conn->sstp_state) {
+ case STATE_CALL_ABORT_PENDING:
+ sstp_timer_set(&conn->ctx, &conn->timeout_timer, SSTP_ABORT_TIMEOUT_2);
+ conn->sstp_state = STATE_CALL_ABORT_TIMEOUT_PENDING;
+ break;
+ case STATE_CALL_ABORT_TIMEOUT_PENDING:
+ case STATE_CALL_DISCONNECT_TIMEOUT_PENDING:
+ break;
+ default:
+ conn->sstp_state = STATE_CALL_ABORT_IN_PROGRESS_2;
+ if (send_sstp_msg_call_abort(conn))
+ return -1;
+ break;
+ }
+
+ return 0;
+}
+
+static int sstp_msg_call_disconnect(struct sstp_conn_t *conn)
+{
+ if (conf_verbose)
+ log_ppp_info2("recv [sstp SSTP_MSG_CALL_DISCONNECT]\n");
+
+ switch (conn->sstp_state) {
+ case STATE_CALL_ABORT_TIMEOUT_PENDING:
+ case STATE_CALL_ABORT_PENDING:
+ case STATE_CALL_DISCONNECT_TIMEOUT_PENDING:
+ break;
+ case STATE_CALL_DISCONNECT_ACK_PENDING:
+ sstp_timer_set(&conn->ctx, &conn->timeout_timer, 0);
+ /* fall through */
+ default:
+ conn->sstp_state = STATE_CALL_DISCONNECT_IN_PROGRESS_2;
+ if (send_sstp_msg_call_disconnect_ack(conn))
+ return -1;
+ break;
+ }
+
+ return 0;
+}
+
+static int sstp_msg_call_disconnect_ack(struct sstp_conn_t *conn)
+{
+ if (conf_verbose)
+ log_ppp_info2("recv [sstp SSTP_MSG_CALL_DISCONNECT_ACK]\n");
+
+ switch (conn->sstp_state) {
+ case STATE_CALL_DISCONNECT_ACK_PENDING:
+ sstp_disconnect(conn);
+ conn->sstp_state = STATE_SERVER_CALL_DISCONNECTED;
+ break;
+ case STATE_CALL_ABORT_PENDING:
+ case STATE_CALL_ABORT_TIMEOUT_PENDING:
+ case STATE_CALL_DISCONNECT_TIMEOUT_PENDING:
+ break;
+ default:
+ conn->sstp_state = STATE_CALL_ABORT_IN_PROGRESS_1;
+ if (send_sstp_msg_call_abort(conn))
+ return -1;
+ break;
+ }
+
+ return 0;
+}
+
+static int sstp_msg_echo_request(struct sstp_conn_t *conn)
+{
+ if (conf_verbose)
+ log_ppp_info2("recv [sstp SSTP_MSG_ECHO_REQUEST]\n");
+
+ switch (conn->sstp_state) {
+ case STATE_SERVER_CALL_CONNECTED:
+ conn->hello_sent = 0;
+ if (conn->hello_timer.tpd)
+ triton_timer_mod(&conn->hello_timer, 0);
+ if (send_sstp_msg_echo_response(conn))
+ return -1;
+ break;
+ case STATE_CALL_ABORT_TIMEOUT_PENDING:
+ case STATE_CALL_ABORT_PENDING:
+ case STATE_CALL_DISCONNECT_ACK_PENDING:
+ case STATE_CALL_DISCONNECT_TIMEOUT_PENDING:
+ break;
+ default:
+ conn->sstp_state = STATE_CALL_ABORT_IN_PROGRESS_1;
+ if (send_sstp_msg_call_abort(conn))
+ return -1;
+ break;
+ }
+
+ return 0;
+}
+
+static int sstp_msg_echo_response(struct sstp_conn_t *conn)
+{
+ if (conf_verbose)
+ log_ppp_info2("recv [sstp SSTP_MSG_ECHO_RESPONSE]\n");
+
+ switch (conn->sstp_state) {
+ case STATE_SERVER_CALL_CONNECTED:
+ conn->hello_sent = 0;
+ if (conn->hello_timer.tpd)
+ triton_timer_mod(&conn->hello_timer, 0);
+ break;
+ case STATE_CALL_ABORT_TIMEOUT_PENDING:
+ case STATE_CALL_ABORT_PENDING:
+ case STATE_CALL_DISCONNECT_ACK_PENDING:
+ case STATE_CALL_DISCONNECT_TIMEOUT_PENDING:
+ break;
+ default:
+ conn->sstp_state = STATE_CALL_ABORT_IN_PROGRESS_1;
+ if (send_sstp_msg_call_abort(conn))
+ return -1;
+ break;
+ }
+
+ return 0;
+}
+
+static int sstp_data_packet(struct sstp_conn_t *conn)
+{
+ struct sstp_hdr *hdr = (struct sstp_hdr *)conn->in_buf;
+ int n, pos, size;
+
+ switch (conn->sstp_state) {
+ case STATE_SERVER_CALL_CONNECTED_PENDING:
+ case STATE_SERVER_CALL_CONNECTED:
+ break;
+ default:
+ return 0;
+ }
+
+ pos = 0;
+ size = ntohs(hdr->length) - sizeof(*hdr);
+ while (pos < size) {
+ n = write(conn->ppp_hnd.fd, hdr->data + pos, size - pos);
+ if (n < 0) {
+ if (errno == EINTR)
+ continue;
+ if (errno == EAGAIN)
+ break;
+ else {
+ if (conf_verbose && errno != EPIPE)
+ log_ppp_info2("sstp: write ppp: %s\n", strerror(errno));
+ return -1;
+ }
+ }
+ pos += n;
+ }
+
+ return 0;
+}
+
+static int sstp_packet(struct sstp_conn_t *conn)
+{
+ struct sstp_ctrl_hdr *hdr = (struct sstp_ctrl_hdr *)conn->in_buf;
+
+ switch (hdr->reserved) {
+ case SSTP_DATA_PACKET:
+ return sstp_data_packet(conn);
+ case SSTP_CTRL_PACKET:
+ break;
+ default:
+ log_ppp_warn("recv [sstp Unknown packet type %02x]\n", hdr->reserved);
+ return -1;
+ }
+
+ switch (ntohs(hdr->message_type)) {
+ case SSTP_MSG_CALL_CONNECT_REQUEST:
+ return sstp_msg_call_connect_request(conn);
+ case SSTP_MSG_CALL_CONNECT_ACK:
+ case SSTP_MSG_CALL_CONNECT_NAK:
+ break;
+ case SSTP_MSG_CALL_CONNECTED:
+ return sstp_msg_call_connected(conn);
+ case SSTP_MSG_CALL_ABORT:
+ return sstp_msg_call_abort(conn);
+ case SSTP_MSG_CALL_DISCONNECT:
+ return sstp_msg_call_disconnect(conn);
+ case SSTP_MSG_CALL_DISCONNECT_ACK:
+ return sstp_msg_call_disconnect_ack(conn);
+ case SSTP_MSG_ECHO_REQUEST:
+ return sstp_msg_echo_request(conn);
+ case SSTP_MSG_ECHO_RESPONSE:
+ return sstp_msg_echo_response(conn);
+ default:
+ log_ppp_warn("recv [sstp Unknown message type %04x]\n", ntohs(hdr->message_type));
+ }
+
+ return 0;
+}
+
+static int sstp_read(struct triton_md_handler_t *h)
+{
+ struct sstp_conn_t *conn = container_of(h, typeof(*conn), hnd);
+ struct sstp_hdr *hdr = (void *)conn->in_buf;
+ int n, size;
+#ifdef CRYPTO_OPENSSL
+ int err, ssl_err;
+#endif
+
+ while (1) {
+#ifdef CRYPTO_OPENSSL
+ if (conn->ssl) {
+ ERR_clear_error();
+ n = SSL_read(conn->ssl, conn->in_buf + conn->in_size, SSTP_MAX_PACKET_SIZE - conn->in_size);
+//err = errno;
+//log_ppp_info2("sstp: sstp.read = %d/%d errno %d %s\n", n, SSTP_MAX_PACKET_SIZE - conn->in_size, (n < 0) ? errno : 0, (n < 0) ? strerror(errno) : "");
+//errno = err;
+ if (n < 0) {
+ err = errno;
+ ssl_err = SSL_get_error(conn->ssl, n);
+ switch (ssl_err) {
+ case SSL_ERROR_WANT_READ:
+ case SSL_ERROR_WANT_WRITE:
+ return 0;
+ case SSL_ERROR_ZERO_RETURN:
+ n = 0;
+ break;
+ case SSL_ERROR_SYSCALL:
+ if (err == EINTR)
+ continue;
+ if (err == EAGAIN)
+ return 0;
+ log_ppp_error("sstp: SSL read: %s\n", strerror(err));
+ goto drop;
+ default:
+ log_ppp_error("sstp: SSL read: %s\n", ERR_error_string(ssl_err, NULL));
+ goto drop;
+ }
+ }
+ } else
+#endif
+ {
+ n = read(h->fd, conn->in_buf + conn->in_size, SSTP_MAX_PACKET_SIZE - conn->in_size);
+//err = errno;
+//log_ppp_info2("sstp: sstp.read = %d/%d errno %d %s\n", n, SSTP_MAX_PACKET_SIZE - conn->in_size, (n < 0) ? errno : 0, (n < 0) ? strerror(errno) : "");
+//errno = err;
+ if (n < 0) {
+ if (errno == EINTR)
+ continue;
+ if (errno == EAGAIN)
+ return 0;
+ log_ppp_error("sstp: read: %s\n", strerror(errno));
+ goto drop;
+ }
+ }
+
+ if (n == 0) {
+ if (conf_verbose)
+ log_ppp_info2("sstp: disconnect by peer\n");
+ goto drop;
+ }
+
+ conn->in_size += n;
+
+ if (conn->sstp_state == STATE_SERVER_CALL_DISCONNECTED) {
+ size = http_request(conn);
+ if (size < 0)
+ goto drop;
+ conn->in_size -= size;
+ if (conn->in_size)
+ memmove(conn->in_buf, conn->in_buf + size, conn->in_size);
+ }
+
+ if (conn->in_size >= sizeof(*hdr)) {
+ if (hdr->version != SSTP_VERSION) {
+ log_ppp_error("sstp: invalid version %d\n", hdr->version);
+ goto drop;
+ }
+ size = ntohs(hdr->length);
+ if (size > SSTP_MAX_PACKET_SIZE) {
+ log_ppp_error("sstp: message is too long\n");
+ goto drop;
+ }
+ if (size <= conn->in_size) {
+ if (sstp_packet(conn))
+ goto drop;
+ conn->in_size -= size;
+ if (conn->in_size)
+ memmove(conn->in_buf, conn->in_buf + size, conn->in_size);
+ }
+ }
+ }
+drop:
+ sstp_disconnect(conn);
+ return 1;
+}
+
+static int sstp_write(struct triton_md_handler_t *h)
+{
+ struct sstp_conn_t *conn = container_of(h, typeof(*conn), hnd);
+ struct sstp_pack_t *pack;
+ int n;
+#ifdef CRYPTO_OPENSSL
+ int err, ssl_err;
+#endif
+
+ while (!list_empty(&conn->send_queue)) {
+ pack = list_first_entry(&conn->send_queue, typeof(*pack), entry);
+ again:
+#ifdef CRYPTO_OPENSSL
+ if (conn->ssl) {
+ ERR_clear_error();
+ n = SSL_write(conn->ssl, pack->data, pack->size);
+//err = errno;
+//log_ppp_info2("sstp: sstp.write = %d/%d errno %d %s\n", n, pack->size, (n < 0) ? errno : 0, (n < 0) ? strerror(errno) : "");
+//errno = err;
+ if (n < 0) {
+ err = errno;
+ ssl_err = SSL_get_error(conn->ssl, n);
+ switch (ssl_err) {
+ case SSL_ERROR_WANT_READ:
+ case SSL_ERROR_WANT_WRITE:
+ case SSL_ERROR_ZERO_RETURN:
+ n = 0;
+ break;
+ case SSL_ERROR_SYSCALL:
+ if (err == EINTR)
+ goto again;
+ if (err == EAGAIN)
+ n = 0;
+ else {
+ if (conf_verbose && err != EPIPE)
+ log_ppp_info2("sstp: write: %s\n", strerror(errno));
+ goto drop;
+ }
+ break;
+ default:
+ log_ppp_error("sstp: SSL write: %s\n", ERR_error_string(ssl_err, NULL));
+ goto drop;
+ }
+ }
+ } else
+#endif
+ {
+ n = write(h->fd, pack->data, pack->size);
+//err = errno;
+//log_ppp_info2("sstp: sstp.write = %d/%d errno %d %s\n", n, pack->size, (n < 0) ? errno : 0, (n < 0) ? strerror(errno) : "");
+//errno = err;
+ if (n < 0) {
+ if (errno == EINTR)
+ goto again;
+ if (errno == EAGAIN)
+ n = 0;
+ else {
+ if (conf_verbose && errno != EPIPE)
+ log_ppp_info2("sstp: write: %s\n", strerror(errno));
+ goto drop;
+ }
+ }
+ }
+
+ if (n == 0)
+ break;
+
+ list_del(&pack->entry);
+ mempool_free(pack->data);
+ mempool_free(pack);
+ }
+
+ if (list_empty(&conn->send_queue))
+ triton_md_disable_handler(h, MD_MODE_WRITE);
+
+ return 0;
+
+drop:
+ sstp_disconnect(conn);
+ return 1;
+}
+
+static int sstp_send(struct sstp_conn_t *conn, void *data, int size)
+{
+ struct sstp_pack_t *pack;
+ int queue_empty = list_empty(&conn->send_queue);
+
+ pack = mempool_alloc(pack_pool);
+ if (!pack) {
+ log_debug("sstp: packet: allocation fail\n");
+ return -1;
+ }
+
+ memset(pack, 0, sizeof(*pack));
+ pack->data = data;
+ pack->size = size;
+ list_add_tail(&pack->entry, &conn->send_queue);
+
+ if (queue_empty)
+ return sstp_write(&conn->hnd);
+
+ triton_md_enable_handler(&conn->hnd, MD_MODE_WRITE);
+
+ return 0;
+}
+
+static void sstp_msg_echo(struct triton_timer_t *t)
+{
+ struct sstp_conn_t *conn = container_of(t, typeof(*conn), hello_timer);
+
+ switch (conn->sstp_state) {
+ case STATE_SERVER_CALL_CONNECTED:
+ if (conn->hello_sent++) {
+ log_ppp_warn("sstp: no echo reply\n");
+ conn->sstp_state = STATE_CALL_ABORT_IN_PROGRESS_1;
+ send_sstp_msg_call_abort(conn);
+ } else
+ send_sstp_msg_echo_request(conn);
+ break;
+ }
+}
+
+static void sstp_timeout(struct triton_timer_t *t)
+{
+ struct sstp_conn_t *conn = container_of(t, typeof(*conn), timeout_timer);
+
+ switch (conn->sstp_state) {
+ case STATE_CALL_ABORT_TIMEOUT_PENDING:
+ case STATE_CALL_ABORT_PENDING:
+ case STATE_CALL_DISCONNECT_TIMEOUT_PENDING:
+ case STATE_CALL_DISCONNECT_ACK_PENDING:
+ sstp_disconnect(conn);
+ break;
+ default:
+ conn->sstp_state = STATE_CALL_ABORT_IN_PROGRESS_1;
+ send_sstp_msg_call_abort(conn);
+ break;
+ }
+}
+
+static void sstp_close(struct triton_context_t *ctx)
+{
+ struct sstp_conn_t *conn = container_of(ctx, typeof(*conn), ctx);
+
+ if (conn->state == STATE_PPP) {
+ __sync_sub_and_fetch(&stat_active, 1);
+ conn->state = STATE_CLOSE;
+ ap_session_terminate(&conn->ppp.ses, TERM_ADMIN_RESET, 1);
+ conn->sstp_state = STATE_CALL_DISCONNECT_IN_PROGRESS_1;
+ send_sstp_msg_call_disconnect(conn);
+ } else {
+ conn->sstp_state = STATE_CALL_ABORT_IN_PROGRESS_1;
+ send_sstp_msg_call_abort(conn);
+ }
+}
+
+static void sstp_starting(struct sstp_conn_t *conn)
+{
+ log_ppp_debug("sstp: starting\n");
+
+#ifdef CRYPTO_OPENSSL
+ if (conf_ssl) {
+ conn->ssl_ctx = SSL_CTX_new(SSLv23_server_method());
+ if (!conn->ssl_ctx) {
+ log_error("sstp: SSL_CTX error: %s\n", ERR_error_string(ERR_get_error(), NULL));
+ goto error;
+ }
+
+ SSL_CTX_set_options(conn->ssl_ctx, SSL_OP_NO_SSLv2 | SSL_OP_NO_SSLv3 | SSL_OP_NO_COMPRESSION);
+
+ if (conf_ssl_ciphers &&
+ SSL_CTX_set_cipher_list(conn->ssl_ctx, conf_ssl_ciphers) != 1) {
+ log_error("sstp: SSL cipher list error: %s\n", ERR_error_string(ERR_get_error(), NULL));
+ goto error;
+ }
+ if (conf_ssl_ca_file &&
+ SSL_CTX_load_verify_locations(conn->ssl_ctx, conf_ssl_ca_file, NULL) != 1) {
+ log_error("sstp: SSL ca file error: %s\n", ERR_error_string(ERR_get_error(), NULL));
+ return;
+ }
+ if (!conf_ssl_pemfile ||
+ SSL_CTX_use_certificate_file(conn->ssl_ctx, conf_ssl_pemfile, SSL_FILETYPE_PEM) != 1 ||
+ SSL_CTX_use_PrivateKey_file(conn->ssl_ctx, conf_ssl_pemfile, SSL_FILETYPE_PEM) != 1 ||
+ SSL_CTX_check_private_key(conn->ssl_ctx) != 1) {
+ log_error("sstp: SSL certificate error: %s\n", ERR_error_string(ERR_get_error(), NULL));
+ goto error;
+ }
+
+ SSL_CTX_set_default_read_ahead(conn->ssl_ctx, 1);
+ SSL_CTX_set_mode(conn->ssl_ctx, SSL_CTX_get_mode(conn->ssl_ctx) | SSL_MODE_ACCEPT_MOVING_WRITE_BUFFER);
+
+ conn->ssl = SSL_new(conn->ssl_ctx);
+ if (!conn->ssl) {
+ log_error("sstp: SSL error: %s\n", ERR_error_string(ERR_get_error(), NULL));
+ goto error;
+ }
+
+ SSL_set_accept_state(conn->ssl);
+ if (SSL_set_fd(conn->ssl, conn->hnd.fd) != 1) {
+ log_error("sstp: SSL bind error: %s\n", ERR_error_string(ERR_get_error(), NULL));
+ goto error;
+ }
+ }
+#endif
+
+ triton_md_enable_handler(&conn->hnd, MD_MODE_READ);
+ triton_timer_add(&conn->ctx, &conn->timeout_timer, 0);
+
+ return;
+
+#ifdef CRYPTO_OPENSSL
+error:
+ sstp_disconnect(conn);
+#endif
+}
+
+static int sstp_connect(struct triton_md_handler_t *h)
+{
+ struct sstp_conn_t *conn;
+ struct sockaddr_in addr;
+ socklen_t size = sizeof(addr);
+ int sock, value;
+
+ while(1) {
+ sock = accept(h->fd, (struct sockaddr *)&addr, &size);
+ if (sock < 0) {
+ if (errno == EAGAIN)
+ return 0;
+ log_error("sstp: accept failed: %s\n", strerror(errno));
+ continue;
+ }
+
+ if (ap_shutdown) {
+ close(sock);
+ continue;
+ }
+
+ if (triton_module_loaded("connlimit") && connlimit_check(cl_key_from_ipv4(addr.sin_addr.s_addr))) {
+ close(sock);
+ return 0;
+ }
+
+ log_info2("sstp: new connection from %s\n", inet_ntoa(addr.sin_addr));
+
+ if (iprange_client_check(addr.sin_addr.s_addr)) {
+ log_warn("sstp: IP is out of client-ip-range, droping connection...\n");
+ close(sock);
+ continue;
+ }
+
+ if (fcntl(sock, F_SETFL, O_NONBLOCK)) {
+ log_error("sstp: failed to set nonblocking mode: %s, closing connection...\n", strerror(errno));
+ close(sock);
+ continue;
+ }
+
+ value = 1;
+ if (setsockopt(sock, IPPROTO_TCP, TCP_NODELAY, &value, sizeof(value)) < 0) {
+ log_error("sstp: failed to disable nagle: %s, closing connection...\n", strerror(errno));
+ close(sock);
+ continue;
+ }
+
+ conn = mempool_alloc(conn_pool);
+ memset(conn, 0, sizeof(*conn));
+
+ conn->ctx.close = sstp_close;
+ conn->ctx.before_switch = sstp_ctx_switch;
+ conn->hnd.fd = sock;
+ conn->hnd.read = sstp_read;
+ conn->hnd.write = sstp_write;
+
+ conn->timeout_timer.expire = sstp_timeout;
+ conn->timeout_timer.period = conf_timeout * 1000;
+ conn->hello_timer.expire = sstp_msg_echo;
+ conn->hello_timer.period = conf_hello_interval * 1000;
+
+ //conn->bypass_auth = conf_bypass_auth;
+ //conn->http_cookie = NULL:
+ //conn->auth_key...
+
+ conn->in_buf = _malloc(SSTP_MAX_PACKET_SIZE);
+ INIT_LIST_HEAD(&conn->send_queue);
+
+ conn->ctrl.ctx = &conn->ctx;
+ conn->ctrl.started = ppp_started;
+ conn->ctrl.finished = ppp_finished;
+ conn->ctrl.terminate = ppp_terminate;
+ conn->ctrl.max_mtu = conf_ppp_max_mtu;
+ conn->ctrl.type = CTRL_TYPE_SSTP;
+ conn->ctrl.ppp = 1;
+ conn->ctrl.name = "sstp";
+ conn->ctrl.ifname = "";
+ conn->ctrl.mppe = MPPE_UNSET;
+ conn->ctrl.calling_station_id = _malloc(17);
+ conn->ctrl.called_station_id = _malloc(17);
+ u_inet_ntoa(addr.sin_addr.s_addr, conn->ctrl.calling_station_id);
+ getsockname(sock, &addr, &size);
+ u_inet_ntoa(addr.sin_addr.s_addr, conn->ctrl.called_station_id);
+
+ ppp_init(&conn->ppp);
+ conn->ppp.ses.ctrl = &conn->ctrl;
+ conn->ppp.ses.chan_name = conn->ctrl.calling_station_id;
+ if (conf_ip_pool)
+ conn->ppp.ses.ipv4_pool_name = _strdup(conf_ip_pool);
+
+ triton_context_register(&conn->ctx, &conn->ppp.ses);
+ triton_md_register_handler(&conn->ctx, &conn->hnd);
+ triton_context_wakeup(&conn->ctx);
+
+ triton_context_call(&conn->ctx, (void (*)(void*))sstp_starting, conn);
+
+ triton_event_fire(EV_CTRL_STARTING, &conn->ppp.ses);
+
+ __sync_add_and_fetch(&stat_starting, 1);
+ }
+ return 0;
+}
+
+static void sstp_serv_close(struct triton_context_t *ctx)
+{
+ struct sstp_serv_t *s = container_of(ctx, typeof(*s), ctx);
+
+ triton_md_unregister_handler(&s->hnd, 1);
+ triton_context_unregister(ctx);
+}
+
+static int show_stat_exec(const char *cmd, char * const *fields, int fields_cnt, void *client)
+{
+ cli_send(client, "sstp:\r\n");
+ cli_sendv(client," starting: %u\r\n", stat_starting);
+ cli_sendv(client," active: %u\r\n", stat_active);
+
+ return CLI_CMD_OK;
+}
+
+void __export sstp_get_stat(unsigned int **starting, unsigned int **active)
+{
+ *starting = &stat_starting;
+ *active = &stat_active;
+}
+
+static void load_config(void)
+{
+ char *opt;
+
+ opt = conf_get_opt("sstp", "ssl");
+ if (opt)
+ conf_ssl = atoi(opt);
+
+ conf_ssl_ciphers = conf_get_opt("sstp", "ssl_ciphers");
+ conf_ssl_ca_file = conf_get_opt("sstp", "ssl_ca_file");
+ conf_ssl_pemfile = conf_get_opt("sstp", "ssl_pemfile");
+
+ opt = conf_get_opt("sstp", "timeout");
+ if (opt && atoi(opt) > 0)
+ conf_timeout = atoi(opt);
+
+ opt = conf_get_opt("sstp", "hello-interval");
+ if (opt && atoi(opt) >= 0)
+ conf_hello_interval = atoi(opt);
+
+ opt = conf_get_opt("sstp", "verbose");
+ if (opt && atoi(opt) >= 0)
+ conf_verbose = atoi(opt) > 0;
+
+ opt = conf_get_opt("sstp", "ppp-max-mtu");
+ if (opt && atoi(opt) > 0)
+ conf_ppp_max_mtu = atoi(opt);
+
+ conf_ip_pool = conf_get_opt("sstp", "ip-pool");
+
+ switch (iprange_check_activation()) {
+ case IPRANGE_DISABLED:
+ log_warn("sstp: iprange module disabled, improper IP configuration of PPP interfaces may cause kernel soft lockup\n");
+ break;
+ case IPRANGE_NO_RANGE:
+ log_warn("sstp: no IP address range defined in section [%s], incoming sstp connections will be rejected\n",
+ IPRANGE_CONF_SECTION);
+ break;
+ default:
+ /* Makes compiler happy */
+ break;
+ }
+
+ //read(urandom_fd, &serv.certificate_hash, sizeof(serv.certificate_hash));
+}
+
+static struct sstp_serv_t serv = {
+ .hnd.read = sstp_connect,
+ .ctx.close = sstp_serv_close,
+ .ctx.before_switch = sstp_ctx_switch,
+};
+
+static void sstp_init(void)
+{
+ struct sockaddr_in addr;
+ char *opt;
+
+#ifdef CRYPTO_OPENSSL
+ SSL_load_error_strings();
+ SSL_library_init();
+#endif
+
+ serv.hnd.fd = socket(PF_INET, SOCK_STREAM, 0);
+ if (serv.hnd.fd < 0) {
+ log_emerg("sstp: failed to create server socket: %s\n", strerror(errno));
+ return;
+ }
+
+ fcntl(serv.hnd.fd, F_SETFD, fcntl(serv.hnd.fd, F_GETFD) | FD_CLOEXEC);
+
+ addr.sin_family = AF_INET;
+
+ opt = conf_get_opt("sstp", "bind");
+ if (opt)
+ addr.sin_addr.s_addr = inet_addr(opt);
+ else
+ addr.sin_addr.s_addr = htonl(INADDR_ANY);
+
+ opt = conf_get_opt("sstp", "port");
+ if (opt && atoi(opt) > 0)
+ addr.sin_port = htons(atoi(opt));
+ else
+ addr.sin_port = htons(SSTP_PORT);
+
+ setsockopt(serv.hnd.fd, SOL_SOCKET, SO_REUSEADDR, &serv.hnd.fd, 4);
+
+ if (bind(serv.hnd.fd, (struct sockaddr *) &addr, sizeof (addr)) < 0) {
+ log_emerg("sstp: failed to bind socket: %s\n", strerror(errno));
+ close(serv.hnd.fd);
+ return;
+ }
+
+ if (listen(serv.hnd.fd, 100) < 0) {
+ log_emerg("sstp: failed to listen socket: %s\n", strerror(errno));
+ close(serv.hnd.fd);
+ return;
+ }
+
+ if (fcntl(serv.hnd.fd, F_SETFL, O_NONBLOCK)) {
+ log_emerg("sstp: failed to set nonblocking mode: %s\n", strerror(errno));
+ close(serv.hnd.fd);
+ return;
+ }
+
+ conn_pool = mempool_create(sizeof(struct sstp_conn_t));
+ pack_pool = mempool_create(sizeof(struct sstp_pack_t));
+ data_pool = mempool_create(SSTP_MAX_PACKET_SIZE);
+
+ load_config();
+
+ triton_context_register(&serv.ctx, NULL);
+ triton_md_register_handler(&serv.ctx, &serv.hnd);
+ triton_md_enable_handler(&serv.hnd, MD_MODE_READ);
+ triton_context_wakeup(&serv.ctx);
+
+ cli_register_simple_cmd2(show_stat_exec, NULL, 2, "show", "stat");
+
+ triton_event_register_handler(EV_CONFIG_RELOAD, (triton_event_func)load_config);
+}
+
+DEFINE_INIT(20, sstp_init);
diff --git a/accel-pppd/ctrl/sstp/sstp_prot.h b/accel-pppd/ctrl/sstp/sstp_prot.h
new file mode 100644
index 0000000..54acaf4
--- /dev/null
+++ b/accel-pppd/ctrl/sstp/sstp_prot.h
@@ -0,0 +1,187 @@
+#ifndef SSTP_PROT_H
+#define SSTP_PROT_H
+
+#include <stdint.h>
+
+/* Constants */
+#define SSTP_PORT 443
+#define SSTP_HTTP_METHOD "SSTP_DUPLEX_POST"
+#define SSTP_HTTP_URI "/sra_{BA195980-CD49-458b-9E23-C84EE0ADCD75}/"
+#define SSTP_VERSION 0x10
+#define SSTP_MAX_PACKET_SIZE 4095
+#define SSTP_NONCE_SIZE 32
+#define SSTP_NEGOTIOATION_TIMEOUT 60
+#define SSTP_HELLO_TIMEOUT 60
+#define SSTP_ABORT_TIMEOUT_1 3
+#define SSTP_ABORT_TIMEOUT_2 1
+#define SSTP_DISCONNECT_TIMEOUT_1 5
+#define SSTP_DISCONNECT_TIMEOUT_2 1
+
+/* Packet Type */
+enum {
+ SSTP_DATA_PACKET = 0x00,
+ SSTP_CTRL_PACKET = 0x01,
+};
+
+/* Message Type */
+enum {
+ SSTP_MSG_CALL_CONNECT_REQUEST = 0x0001,
+ SSTP_MSG_CALL_CONNECT_ACK = 0x0002,
+ SSTP_MSG_CALL_CONNECT_NAK = 0x0003,
+ SSTP_MSG_CALL_CONNECTED = 0x0004,
+ SSTP_MSG_CALL_ABORT = 0x0005,
+ SSTP_MSG_CALL_DISCONNECT = 0x0006,
+ SSTP_MSG_CALL_DISCONNECT_ACK = 0x0007,
+ SSTP_MSG_ECHO_REQUEST = 0x0008,
+ SSTP_MSG_ECHO_RESPONSE = 0x0009,
+};
+
+/* Attribute ID */
+enum {
+ SSTP_ATTRIB_NO_ERROR = 0x00,
+ SSTP_ATTRIB_ENCAPSULATED_PROTOCOL_ID = 0x01,
+ SSTP_ATTRIB_STATUS_INFO = 0x02,
+ SSTP_ATTRIB_CRYPTO_BINDING = 0x03,
+ SSTP_ATTRIB_CRYPTO_BINDING_REQ = 0x04,
+};
+
+/* Protocol ID */
+enum {
+ SSTP_ENCAPSULATED_PROTOCOL_PPP = 0x0001,
+};
+
+/* Hash Protocol Bitmask */
+enum {
+ CERT_HASH_PROTOCOL_SHA1 = 0x01,
+ CERT_HASH_PROTOCOL_SHA256 = 0x02,
+};
+
+/* Status */
+enum {
+ ATTRIB_STATUS_NO_ERROR = 0x00000000,
+ ATTRIB_STATUS_DUPLICATE_ATTRIBUTE = 0x00000001,
+ ATTRIB_STATUS_UNRECOGNIZED_ATTRIBUTE = 0x00000002,
+ ATTRIB_STATUS_INVALID_ATTRIB_VALUE_LENGTH = 0x00000003,
+ ATTRIB_STATUS_VALUE_NOT_SUPPORTED = 0x00000004,
+ ATTRIB_STATUS_UNACCEPTED_FRAME_RECEIVED = 0x00000005,
+ ATTRIB_STATUS_RETRY_COUNT_EXCEEDED = 0x00000006,
+ ATTRIB_STATUS_INVALID_FRAME_RECEIVED = 0x00000007,
+ ATTRIB_STATUS_NEGOTIATION_TIMEOUT = 0x00000008,
+ ATTRIB_STATUS_ATTRIB_NOT_SUPPORTED_IN_MSG = 0x00000009,
+ ATTRIB_STATUS_REQUIRED_ATTRIBUTE_MISSING = 0x0000000a,
+ ATTRIB_STATUS_STATUS_INFO_NOT_SUPPORTED_IN_MSG = 0x0000000b,
+};
+
+/* State */
+#define STATE_INIT 0
+#define STATE_ESTB 1
+#define STATE_PPP 2
+#define STATE_FIN 3
+#define STATE_CLOSE 4
+enum {
+ STATE_SERVER_CALL_DISCONNECTED = 0,
+ STATE_SERVER_CONNECT_REQUEST_PENDING,
+ STATE_SERVER_CALL_CONNECTED_PENDING,
+ STATE_SERVER_CALL_CONNECTED,
+ STATE_CALL_ABORT_IN_PROGRESS_1,
+ STATE_CALL_ABORT_IN_PROGRESS_2,
+ STATE_CALL_ABORT_TIMEOUT_PENDING,
+ STATE_CALL_ABORT_PENDING,
+ STATE_CALL_DISCONNECT_IN_PROGRESS_1,
+ STATE_CALL_DISCONNECT_IN_PROGRESS_2,
+ STATE_CALL_DISCONNECT_TIMEOUT_PENDING,
+ STATE_CALL_DISCONNECT_ACK_PENDING,
+};
+
+/* Packets */
+struct sstp_hdr {
+ uint8_t version;
+ uint8_t reserved;
+ uint16_t length;
+ uint8_t data[0];
+} __attribute__((packed));
+
+struct sstp_ctrl_hdr {
+ uint8_t version;
+ uint8_t reserved;
+ uint16_t length;
+ uint16_t message_type;
+ uint16_t num_attributes;
+ uint8_t data[0];
+} __attribute__((packed));
+
+struct sstp_attr_hdr {
+ uint8_t reserved;
+ uint8_t attribute_id;
+ uint16_t length;
+ uint8_t data[0];
+} __attribute__((packed));
+
+struct sstp_attrib_encapsulated_protocol {
+ struct sstp_attr_hdr hdr;
+ uint16_t protocol_id;
+} __attribute__((packed));
+
+struct sstp_attrib_status_info {
+ struct sstp_attr_hdr hdr;
+ uint8_t reserved[3];
+ uint8_t attrib_id;
+ uint32_t status;
+ uint8_t attrib_value[0];
+} __attribute__((packed));
+
+struct sstp_attrib_crypto_binding {
+ struct sstp_attr_hdr hdr;
+ uint8_t reserved[3];
+ uint8_t hash_protocol_bitmask;
+ uint8_t nonce[SSTP_NONCE_SIZE];
+ uint8_t cert_hash[SSTP_NONCE_SIZE];
+ uint8_t compound_mac[SSTP_NONCE_SIZE];
+} __attribute__((packed));
+
+struct sstp_attrib_crypto_binding_request {
+ struct sstp_attr_hdr hdr;
+ uint8_t reserved[3];
+ uint8_t hash_protocol_bitmask;
+ uint8_t nonce[SSTP_NONCE_SIZE];
+} __attribute__((packed));
+
+#define SSTP_DATA_HDR(len) { \
+ .version = SSTP_VERSION, \
+ .reserved = SSTP_DATA_PACKET, \
+ .length = htons(len), \
+}
+
+#define SSTP_CTRL_HDR(type, len, num) { \
+ .version = SSTP_VERSION, \
+ .reserved = SSTP_CTRL_PACKET, \
+ .length = htons(len), \
+ .message_type = htons(type), \
+ .num_attributes = htons(num) \
+}
+
+#define SSTP_ATTR_HDR(id, len) { \
+ .attribute_id = id, \
+ .length = htons(len) \
+}
+
+#define INIT_SSTP_DATA_HDR(hdr, len) { \
+ (hdr)->version = SSTP_VERSION; \
+ (hdr)->reserved = SSTP_DATA_PACKET; \
+ (hdr)->length = htons(len); \
+}
+
+#define INIT_SSTP_CTRL_HDR(hdr, type, num, len) {\
+ (hdr)->version = SSTP_VERSION; \
+ (hdr)->reserved = SSTP_CTRL_PACKET; \
+ (hdr)->length = htons(len); \
+ (hdr)->message_type = htons(type); \
+ (hdr)->num_attributes = htons(num); \
+}
+
+#define INIT_SSTP_ATTR_HDR(hdr, id, len) { \
+ (hdr)->attribute_id = id; \
+ (hdr)->length = htons(len); \
+}
+
+#endif