summaryrefslogtreecommitdiff
path: root/accel-pppd/ctrl/sstp/sstp.c
diff options
context:
space:
mode:
Diffstat (limited to 'accel-pppd/ctrl/sstp/sstp.c')
-rw-r--r--accel-pppd/ctrl/sstp/sstp.c2183
1 files changed, 2183 insertions, 0 deletions
diff --git a/accel-pppd/ctrl/sstp/sstp.c b/accel-pppd/ctrl/sstp/sstp.c
new file mode 100644
index 0000000..0c7bd9b
--- /dev/null
+++ b/accel-pppd/ctrl/sstp/sstp.c
@@ -0,0 +1,2183 @@
+#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 <pty.h>
+#include <arpa/inet.h>
+#include <netinet/in.h>
+#include <netinet/tcp.h>
+#include <sys/socket.h>
+#include <sys/ioctl.h>
+#include "linux_ppp.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
+
+#define PPP_SYNC 0 /* buggy yet */
+#define PPP_BUF_SIZE 8192
+#define PPP_F_ESCAPE 1
+#define PPP_F_TOSS 2
+
+#ifndef SHA_DIGEST_LENGTH
+#define SHA_DIGEST_LENGTH 20
+#endif
+#ifndef SHA256_DIGEST_LENGTH
+#define SHA256_DIGEST_LENGTH 32
+#endif
+
+enum {
+ STATE_INIT = 0,
+ STATE_STARTING,
+ STATE_STARTED,
+ STATE_FINISHED,
+};
+
+struct hash_t {
+ unsigned int len;
+ union {
+ uint8_t hash[0];
+ uint8_t sha1[SHA_DIGEST_LENGTH];
+ uint8_t sha256[SHA256_DIGEST_LENGTH];
+ };
+};
+
+struct buffer_t {
+ struct list_head entry;
+ size_t len;
+ unsigned char *head;
+ unsigned char *tail;
+ unsigned char *end;
+ unsigned char data[0];
+};
+
+struct sstp_stream_t {
+ union {
+ int fd;
+#ifdef CRYPTO_OPENSSL
+ SSL *ssl;
+#endif
+ };
+ ssize_t (*read)(struct sstp_stream_t *stream, void *buf, size_t count);
+ ssize_t (*write)(struct sstp_stream_t *stream, const void *buf, size_t count);
+ int (*close)(struct sstp_stream_t *stream);
+ void (*free)(struct sstp_stream_t *stream);
+};
+
+struct sstp_conn_t {
+ struct triton_context_t ctx;
+ struct triton_md_handler_t hnd, ppp_hnd;
+
+ struct triton_timer_t timeout_timer;
+ struct triton_timer_t hello_timer;
+
+ struct sstp_stream_t *stream;
+ int (*handler)(struct sstp_conn_t *conn, struct buffer_t *buf);
+
+ int sstp_state;
+ int nak_sent;
+ int hello_sent;
+ int hello_interval;
+
+// int bypass_auth:1;
+// char *http_cookie;
+// uint8_t auth_key[32];
+ uint8_t *nonce;
+
+ struct buffer_t *in;
+ struct list_head out_queue;
+
+ int ppp_state;
+ int ppp_flags;
+ struct buffer_t *ppp_in;
+ struct list_head ppp_queue;
+
+ struct ppp_t ppp;
+ struct ap_ctrl ctrl;
+};
+
+static struct sstp_serv_t {
+ struct triton_context_t ctx;
+ struct triton_md_handler_t hnd;
+
+#ifdef CRYPTO_OPENSSL
+ SSL_CTX *ssl_ctx;
+#endif
+} serv;
+
+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 = 1452;
+static const char *conf_ip_pool;
+static const char *conf_ifname;
+
+static int conf_hash_protocol = CERT_HASH_PROTOCOL_SHA1 | CERT_HASH_PROTOCOL_SHA256;
+static struct hash_t conf_hash_sha1 = { .len = 0 };
+static struct hash_t conf_hash_sha256 = { .len = 0 };
+//static int conf_bypass_auth = 0;
+static const char *conf_hostname = NULL;
+
+static mempool_t conn_pool;
+
+static int sstp_write(struct triton_md_handler_t *h);
+static inline void sstp_queue(struct sstp_conn_t *conn, struct buffer_t *buf);
+static int sstp_send(struct sstp_conn_t *conn, struct buffer_t *buf);
+static int sstp_abort(struct sstp_conn_t *conn, int disconnect);
+static void sstp_disconnect(struct sstp_conn_t *conn);
+static int sstp_handler(struct sstp_conn_t *conn, struct buffer_t *buf);
+
+/*
+ * FCS lookup table as calculated by genfcstab.
+ */
+static const uint16_t fcstab[256] = {
+ 0x0000, 0x1189, 0x2312, 0x329b, 0x4624, 0x57ad, 0x6536, 0x74bf,
+ 0x8c48, 0x9dc1, 0xaf5a, 0xbed3, 0xca6c, 0xdbe5, 0xe97e, 0xf8f7,
+ 0x1081, 0x0108, 0x3393, 0x221a, 0x56a5, 0x472c, 0x75b7, 0x643e,
+ 0x9cc9, 0x8d40, 0xbfdb, 0xae52, 0xdaed, 0xcb64, 0xf9ff, 0xe876,
+ 0x2102, 0x308b, 0x0210, 0x1399, 0x6726, 0x76af, 0x4434, 0x55bd,
+ 0xad4a, 0xbcc3, 0x8e58, 0x9fd1, 0xeb6e, 0xfae7, 0xc87c, 0xd9f5,
+ 0x3183, 0x200a, 0x1291, 0x0318, 0x77a7, 0x662e, 0x54b5, 0x453c,
+ 0xbdcb, 0xac42, 0x9ed9, 0x8f50, 0xfbef, 0xea66, 0xd8fd, 0xc974,
+ 0x4204, 0x538d, 0x6116, 0x709f, 0x0420, 0x15a9, 0x2732, 0x36bb,
+ 0xce4c, 0xdfc5, 0xed5e, 0xfcd7, 0x8868, 0x99e1, 0xab7a, 0xbaf3,
+ 0x5285, 0x430c, 0x7197, 0x601e, 0x14a1, 0x0528, 0x37b3, 0x263a,
+ 0xdecd, 0xcf44, 0xfddf, 0xec56, 0x98e9, 0x8960, 0xbbfb, 0xaa72,
+ 0x6306, 0x728f, 0x4014, 0x519d, 0x2522, 0x34ab, 0x0630, 0x17b9,
+ 0xef4e, 0xfec7, 0xcc5c, 0xddd5, 0xa96a, 0xb8e3, 0x8a78, 0x9bf1,
+ 0x7387, 0x620e, 0x5095, 0x411c, 0x35a3, 0x242a, 0x16b1, 0x0738,
+ 0xffcf, 0xee46, 0xdcdd, 0xcd54, 0xb9eb, 0xa862, 0x9af9, 0x8b70,
+ 0x8408, 0x9581, 0xa71a, 0xb693, 0xc22c, 0xd3a5, 0xe13e, 0xf0b7,
+ 0x0840, 0x19c9, 0x2b52, 0x3adb, 0x4e64, 0x5fed, 0x6d76, 0x7cff,
+ 0x9489, 0x8500, 0xb79b, 0xa612, 0xd2ad, 0xc324, 0xf1bf, 0xe036,
+ 0x18c1, 0x0948, 0x3bd3, 0x2a5a, 0x5ee5, 0x4f6c, 0x7df7, 0x6c7e,
+ 0xa50a, 0xb483, 0x8618, 0x9791, 0xe32e, 0xf2a7, 0xc03c, 0xd1b5,
+ 0x2942, 0x38cb, 0x0a50, 0x1bd9, 0x6f66, 0x7eef, 0x4c74, 0x5dfd,
+ 0xb58b, 0xa402, 0x9699, 0x8710, 0xf3af, 0xe226, 0xd0bd, 0xc134,
+ 0x39c3, 0x284a, 0x1ad1, 0x0b58, 0x7fe7, 0x6e6e, 0x5cf5, 0x4d7c,
+ 0xc60c, 0xd785, 0xe51e, 0xf497, 0x8028, 0x91a1, 0xa33a, 0xb2b3,
+ 0x4a44, 0x5bcd, 0x6956, 0x78df, 0x0c60, 0x1de9, 0x2f72, 0x3efb,
+ 0xd68d, 0xc704, 0xf59f, 0xe416, 0x90a9, 0x8120, 0xb3bb, 0xa232,
+ 0x5ac5, 0x4b4c, 0x79d7, 0x685e, 0x1ce1, 0x0d68, 0x3ff3, 0x2e7a,
+ 0xe70e, 0xf687, 0xc41c, 0xd595, 0xa12a, 0xb0a3, 0x8238, 0x93b1,
+ 0x6b46, 0x7acf, 0x4854, 0x59dd, 0x2d62, 0x3ceb, 0x0e70, 0x1ff9,
+ 0xf78f, 0xe606, 0xd49d, 0xc514, 0xb1ab, 0xa022, 0x92b9, 0x8330,
+ 0x7bc7, 0x6a4e, 0x58d5, 0x495c, 0x3de3, 0x2c6a, 0x1ef1, 0x0f78
+};
+
+/* buffer */
+
+static inline void *buf_put(struct buffer_t *buf, int len)
+{
+ void *tmp = buf->tail;
+ buf->tail += len;
+ buf->len += len;
+ return tmp;
+}
+
+static inline void *buf_put_data(struct buffer_t *buf, const void *data, int len)
+{
+ void *tmp = buf_put(buf, len);
+ memcpy(tmp, data, len);
+ return tmp;
+}
+
+static inline void *buf_put_zero(struct buffer_t *buf, int len)
+{
+ void *tmp = buf_put(buf, len);
+ memset(tmp, 0, len);
+ return tmp;
+}
+
+static inline void *buf_push(struct buffer_t *buf, int len)
+{
+ buf->head -= len;
+ buf->len += len;
+ return buf->head;
+}
+
+static inline void *buf_pull(struct buffer_t *buf, int len)
+{
+ buf->head += len;
+ buf->len -= len;
+ return buf->head;
+}
+
+static inline int buf_headroom(const struct buffer_t *buf)
+{
+ return buf->head - buf->data;
+}
+
+static inline int buf_tailroom(const struct buffer_t *buf)
+{
+ return buf->end - buf->tail;
+}
+
+static inline void buf_reserve(struct buffer_t *buf, int len)
+{
+ buf->head += len;
+ buf->tail += len;
+}
+
+static inline void buf_set_length(struct buffer_t *buf, int len)
+{
+ buf->tail = buf->head + len;
+ buf->len = len;
+}
+
+static inline int buf_expand_tail(struct buffer_t *buf, int tailroom)
+{
+ if (buf->len == 0)
+ buf->head = buf->tail = buf->data;
+ else if (buf_tailroom(buf) < tailroom) {
+ buf->head = memmove(buf->data, buf->head, buf->len);
+ buf->tail = buf->head + buf->len;
+ }
+ return (buf_tailroom(buf) >= tailroom);
+}
+
+static struct buffer_t *alloc_buf(size_t size)
+{
+ struct buffer_t *buf = _malloc(sizeof(*buf) + size);
+
+ if (!buf)
+ return NULL;
+
+ buf->head = buf->data;
+ buf->end = buf->data + size;
+ buf_set_length(buf, 0);
+ return buf;
+}
+
+static struct buffer_t *alloc_buf_printf(const char* format, ...)
+{
+ struct buffer_t *buf;
+ va_list ap;
+ int len;
+
+ va_start(ap, format);
+ len = vsnprintf(NULL, 0, format, ap);
+ va_end(ap);
+ if (len < 0)
+ return NULL;
+
+ buf = alloc_buf(len + 1);
+ if (buf) {
+ va_start(ap, format);
+ vsnprintf(buf_put(buf, len), len + 1, format, ap);
+ va_end(ap);
+ }
+ return buf;
+}
+
+static void free_buf(struct buffer_t *buf)
+{
+ _free(buf);
+}
+
+/* socket stream */
+
+static ssize_t stream_read(struct sstp_stream_t *stream, void *buf, size_t count)
+{
+ return read(stream->fd, buf, count);
+}
+
+static ssize_t stream_write(struct sstp_stream_t *stream, const void *buf, size_t count)
+{
+ return write(stream->fd, buf, count);
+}
+
+static int stream_close(struct sstp_stream_t *stream)
+{
+ return close(stream->fd);
+}
+
+static void stream_free(struct sstp_stream_t *stream)
+{
+ _free(stream);
+}
+
+static struct sstp_stream_t *stream_init(int fd)
+{
+ struct sstp_stream_t *stream = _malloc(sizeof(*stream));
+
+ if (!stream)
+ return NULL;
+
+ stream->fd = fd;
+ stream->read = stream_read;
+ stream->write = stream_write;
+ stream->close = stream_close;
+ stream->free = stream_free;
+
+ return stream;
+}
+
+/* ssl stream */
+
+#ifdef CRYPTO_OPENSSL
+#include <pthread.h>
+
+static pthread_mutex_t *lock_cs;
+
+static unsigned long pthreads_thread_id(void)
+{
+ return (unsigned long)pthread_self();
+}
+
+static void pthreads_locking_callback(int mode, int type, const char *file, int line)
+{
+ if (mode & CRYPTO_LOCK)
+ pthread_mutex_lock(&lock_cs[type]);
+ else
+ pthread_mutex_unlock(&lock_cs[type]);
+}
+
+static void CRYPTO_thread_setup(void)
+{
+ int i;
+
+ lock_cs = _malloc(CRYPTO_num_locks() * sizeof(*lock_cs));
+ if (!lock_cs)
+ return;
+
+ for (i = 0; i < CRYPTO_num_locks(); i++)
+ pthread_mutex_init(&lock_cs[i], NULL);
+
+ CRYPTO_set_id_callback(pthreads_thread_id);
+ CRYPTO_set_locking_callback(pthreads_locking_callback);
+}
+
+static void CRYPTO_thread_cleanup(void)
+{
+ int i;
+
+ if (!lock_cs)
+ return;
+
+ CRYPTO_set_id_callback(NULL);
+ CRYPTO_set_locking_callback(NULL);
+
+ for (i = 0; i < CRYPTO_num_locks(); i++)
+ pthread_mutex_destroy(&lock_cs[i]);
+
+ _free(lock_cs);
+}
+
+static ssize_t ssl_stream_read(struct sstp_stream_t *stream, void *buf, size_t count)
+{
+ int ret, err;
+
+ ERR_clear_error();
+ ret = SSL_read(stream->ssl, buf, count);
+ if (ret > 0)
+ return ret;
+
+ err = SSL_get_error(stream->ssl, ret);
+ switch (err) {
+ case SSL_ERROR_WANT_WRITE:
+ case SSL_ERROR_WANT_READ:
+ errno = EAGAIN;
+ /* fall through */
+ case SSL_ERROR_ZERO_RETURN:
+ case SSL_ERROR_SYSCALL:
+ return ret;
+ default:
+ errno = EIO;
+ return ret;
+ }
+}
+
+static ssize_t ssl_stream_write(struct sstp_stream_t *stream, const void *buf, size_t count)
+{
+ int ret, err;
+
+ ERR_clear_error();
+ ret = SSL_write(stream->ssl, buf, count);
+ if (ret > 0)
+ return ret;
+
+ err = SSL_get_error(stream->ssl, ret);
+ switch (err) {
+ case SSL_ERROR_WANT_WRITE:
+ case SSL_ERROR_WANT_READ:
+ errno = EAGAIN;
+ /* fall through */
+ case SSL_ERROR_ZERO_RETURN:
+ case SSL_ERROR_SYSCALL:
+ return ret;
+ default:
+ errno = EIO;
+ return ret;
+ }
+}
+
+static int ssl_stream_close(struct sstp_stream_t *stream)
+{
+ SSL_shutdown(stream->ssl);
+ return close(SSL_get_fd(stream->ssl));
+}
+
+static void ssl_stream_free(struct sstp_stream_t *stream)
+{
+ if (stream && stream->ssl)
+ SSL_free(stream->ssl);
+ _free(stream);
+}
+
+static struct sstp_stream_t *ssl_stream_init(int fd, SSL_CTX *ssl_ctx)
+{
+ struct sstp_stream_t *stream = _malloc(sizeof(*stream));
+
+ if (!stream)
+ return NULL;
+
+ stream->ssl = SSL_new(ssl_ctx);
+ if (!stream->ssl)
+ goto error;
+
+ SSL_set_verify(stream->ssl, SSL_VERIFY_NONE, NULL);
+ SSL_set_accept_state(stream->ssl);
+ SSL_set_fd(stream->ssl, fd);
+
+ stream->read = ssl_stream_read;
+ stream->write = ssl_stream_write;
+ stream->close = ssl_stream_close;
+ stream->free = ssl_stream_free;
+
+ return stream;
+
+error:
+ ssl_stream_free(stream);
+ return NULL;
+}
+#endif
+
+/* http */
+
+static char *http_getline(struct buffer_t *buf, char *line, size_t size)
+{
+ char *src, *dst, *ptr;
+ int len;
+
+ if (buf->len == 0 || size == 0)
+ return NULL;
+
+ src = (void *)buf->head;
+ ptr = memchr(src, '\n', buf->len);
+ if (ptr) {
+ len = ptr - src;
+ buf_pull(buf, len + 1);
+ if (len > 0 && src[len - 1] == '\r')
+ len--;
+ } else {
+ len = buf->len;
+ buf_pull(buf, len);
+ }
+
+ dst = line;
+ while (len-- > 0 && size-- > 1)
+ *dst++ = *src++;
+ if (size > 0)
+ *dst = '\0';
+
+ return line;
+}
+
+static char *http_getvalue(char *line, const char *name, int len)
+{
+ int sep;
+
+ if (len < 0)
+ len = strlen(name);
+
+ if (strncasecmp(line, name, len) != 0)
+ return NULL;
+
+ line += len;
+ for (sep = 0; *line; line++) {
+ if (!sep && *line == ':')
+ sep = 1;
+ else if (*line != ' ' && *line != '\t')
+ break;
+ }
+
+ return sep ? line : NULL;
+}
+
+static int http_send_response(struct sstp_conn_t *conn, char *proto, char *status, char *headers)
+{
+ char datetime[sizeof("aaa, dd bbb yyyy HH:MM:SS GMT")];
+ struct buffer_t *buf;
+ time_t now = time(NULL);
+
+ if (conf_verbose)
+ log_ppp_info2("send [HTTP <%s %s>]\n", proto, status);
+
+ strftime(datetime, sizeof(datetime), "%a, %d %b %Y %H:%M:%S GMT", gmtime(&now));
+ buf = alloc_buf_printf(
+ "%s %s\r\n"
+ /* "Server: %s\r\n" */
+ "Date: %s\r\n"
+ "%s"
+ "\r\n", proto, status, /* "accel-ppp",*/ datetime, headers ? : "");
+ if (!buf) {
+ log_error("sstp: no memory\n");
+ return -1;
+ }
+
+ return sstp_send(conn, buf);
+}
+
+static int http_recv_request(struct sstp_conn_t *conn, uint8_t *data, int len)
+{
+ char linebuf[1024], protobuf[sizeof("HTTP/1.x")];
+ char *line, *method, *request, *proto, *host;
+ struct buffer_t buf;
+ int ret = -1;
+
+ buf.head = data;
+ buf.end = data + len;
+ buf_set_length(&buf, len);
+
+ line = http_getline(&buf, linebuf, sizeof(linebuf));
+ if (!line)
+ return -1;
+ if (conf_verbose)
+ log_ppp_info2("recv [HTTP <%s>]\n", line);
+
+ method = strsep(&line, " ");
+ request = strsep(&line, " ");
+ proto = strsep(&line, " ");
+ host = NULL;
+
+ if (!method || !request || !proto) {
+ http_send_response(conn, "HTTP/1.1", "400 Bad Request", NULL);
+ goto error;
+ }
+ if (strncasecmp(proto, "HTTP/1", sizeof("HTTP/1") - 1) != 0) {
+ http_send_response(conn, "HTTP/1.1", "505 HTTP Version Not Supported", NULL);
+ goto error;
+ }
+ if (strcasecmp(method, SSTP_HTTP_METHOD) != 0) {
+ http_send_response(conn, proto, "501 Not Implemented", NULL);
+ goto error;
+ }
+ if (strcasecmp(request, SSTP_HTTP_URI) != 0) {
+ http_send_response(conn, proto, "404 Not Found", NULL);
+ goto error;
+ }
+
+ snprintf(protobuf, sizeof(protobuf), "%s", proto);
+ proto = protobuf;
+
+ while ((line = http_getline(&buf, linebuf, sizeof(linebuf))) != NULL) {
+ if (*line == '\0')
+ break;
+ if (conf_verbose)
+ log_ppp_info2("recv [HTTP <%s>]\n", line);
+
+ if (!host && conf_hostname) {
+ host = http_getvalue(line, "Host", sizeof("Host") - 1);
+ if (host)
+ host = _strdup(host);
+ }
+ }
+
+ if (conf_hostname && strcasecmp(host ? : "", conf_hostname) != 0) {
+ http_send_response(conn, proto, "434 Requested host unavailable", NULL);
+ goto error;
+ }
+
+ if (http_send_response(conn, proto, "200 OK",
+ "Content-Length: 18446744073709551615\r\n")) {
+ goto error;
+ }
+ ret = 0;
+
+error:
+ _free(host);
+ return ret;
+}
+
+static int http_handler(struct sstp_conn_t *conn, struct buffer_t *buf)
+{
+ static const char *table[] = { "\r\n\r\n", "\n\n", NULL };
+ const char **pptr;
+ char *ptr, *end;
+ int n;
+
+ if (conn->sstp_state != STATE_SERVER_CALL_DISCONNECTED)
+ return -1;
+
+ end = NULL;
+ for (pptr = table; *pptr; pptr++) {
+ ptr = memmem(buf->head, buf->len, *pptr, strlen(*pptr));
+ if (ptr && (!end || ptr < end))
+ end = ptr + strlen(*pptr);
+ }
+ if (!end) {
+ if (buf_tailroom(buf) > 0)
+ return 0;
+ log_ppp_error("recv [HTTP too long header]\n");
+ return -1;
+ } else
+ n = end - (char *)buf->head;
+
+ if (http_recv_request(conn, buf->head, n) < 0)
+ return -1;
+ buf_pull(buf, n);
+
+ conn->sstp_state = STATE_SERVER_CONNECT_REQUEST_PENDING;
+ conn->handler = sstp_handler;
+ return 0;
+}
+
+/* ppp */
+
+static int ppp_allocate_pty(int *master, int *slave, int flags)
+{
+ struct termios tios;
+ int value, mfd, sfd;
+
+ if (openpty(&mfd, &sfd, NULL, &tios, NULL) < 0) {
+ log_ppp_error("sstp: allocate pty: %s\n", strerror(errno));
+ return -1;
+ }
+
+ if (flags & O_CLOEXEC) {
+ fcntl(mfd, F_SETFD, fcntl(mfd, F_GETFD) | FD_CLOEXEC);
+ fcntl(sfd, F_SETFD, fcntl(sfd, F_GETFD) | FD_CLOEXEC);
+ flags &= ~O_CLOEXEC;
+ }
+
+ tios.c_cflag &= ~(CSIZE | CSTOPB | PARENB);
+ tios.c_cflag |= CS8 | CREAD | CLOCAL;
+ tios.c_iflag = IGNBRK | IGNPAR;
+ tios.c_oflag = 0;
+ tios.c_lflag = 0;
+ tios.c_cc[VMIN] = 1;
+ tios.c_cc[VTIME] = 0;
+ if (tcsetattr(sfd, TCSAFLUSH, &tios) < 0) {
+ log_ppp_warn("sstp: ppp: set pty attributes: %s\n", strerror(errno));
+ goto error;
+ }
+
+#if PPP_SYNC
+ value = N_SYNC_PPP;
+#else
+ value = N_PPP;
+#endif
+ if (ioctl(sfd, TIOCSETD, &value) < 0) {
+ log_ppp_error("sstp: ppp: set pty line discipline: %s\n", strerror(errno));
+ goto error;
+ }
+
+#if PPP_SYNC
+ value = N_HDLC;
+ if (ioctl(mfd, TIOCSETD, &value) < 0) {
+ log_ppp_error("sstp: ppp: set pty line discipline: %s\n", strerror(errno));
+ goto error;
+ }
+#endif
+
+ if ((value = fcntl(mfd, F_GETFL)) < 0 || fcntl(mfd, F_SETFL, value | flags) < 0 ||
+ (value = fcntl(sfd, F_GETFL)) < 0 || fcntl(sfd, F_SETFL, value | flags) < 0) {
+ log_ppp_error("sstp: ppp: set pty status flags: %s\n", strerror(errno));
+ goto error;
+ }
+
+ *master = mfd;
+ *slave = sfd;
+ return 0;
+
+error:
+ close(mfd);
+ close(sfd);
+ return -1;
+}
+
+static void ppp_started(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);
+
+ log_ppp_debug("sstp: ppp: started\n");
+
+ switch (conn->ppp_state) {
+ case STATE_STARTING:
+ conn->ppp_state = STATE_STARTED;
+ break;
+ }
+}
+
+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);
+
+ log_ppp_debug("sstp: ppp: finished\n");
+
+ switch (conn->ppp_state) {
+ case STATE_STARTING:
+ case STATE_STARTED:
+ conn->ppp_state = STATE_FINISHED;
+ sstp_abort(conn, 1);
+ break;
+ }
+}
+
+static int ppp_read(struct triton_md_handler_t *h)
+{
+ struct sstp_conn_t *conn = container_of(h, typeof(*conn), ppp_hnd);
+ struct buffer_t *buf;
+ struct sstp_hdr *hdr;
+ uint8_t pppbuf[PPP_BUF_SIZE], *src;
+ int i, n;
+#if !PPP_SYNC
+ uint8_t byte;
+
+ buf = conn->ppp_in;
+#endif
+ while (1) {
+ n = read(h->fd, pppbuf, sizeof(pppbuf));
+ if (n < 0) {
+ if (errno == EINTR)
+ continue;
+ if (errno == EAGAIN)
+ break;
+ log_ppp_error("sstp: ppp: read: %s\n", strerror(errno));
+ goto drop;
+ } else if (n == 0) {
+ if (conf_verbose)
+ log_ppp_info2("sstp: ppp: disconnect from tty\n");
+ goto drop;
+ }
+
+ switch (conn->sstp_state) {
+ case STATE_SERVER_CALL_CONNECTED_PENDING:
+ case STATE_SERVER_CALL_CONNECTED:
+ break;
+ default:
+ continue;
+ }
+
+ src = pppbuf;
+#if PPP_SYNC
+ while (n > 0) {
+ if (src[0] == PPP_ALLSTATIONS)
+ i = conn->ppp.mtu + 4 - (src[2] & 1);
+ else
+ i = conn->ppp.mtu + 2 - (src[0] & 1);
+ if (i > n)
+ i = n;
+
+ buf = alloc_buf(i + sizeof(*hdr));
+ if (!buf) {
+ log_ppp_error("sstp: ppp: no memory\n");
+ goto drop;
+ }
+ hdr = buf_put(buf, sizeof(*hdr));
+ buf_put_data(buf, src, i);
+ INIT_SSTP_DATA_HDR(hdr, buf->len);
+ sstp_queue(conn, buf);
+
+ n -= i;
+ src += i;
+ }
+#else
+ if (!buf) {
+ alloc:
+ conn->ppp_in = buf = alloc_buf(SSTP_MAX_PACKET_SIZE + PPP_FCSLEN);
+ if (!buf) {
+ log_ppp_error("sstp: ppp: no memory\n");
+ goto drop;
+ }
+ buf_reserve(buf, sizeof(*hdr));
+ }
+
+ while (n > 0) {
+ if ((conn->ppp_flags & PPP_F_ESCAPE) && *src == PPP_ESCAPE)
+ i = 1;
+ else {
+ for (i = 0; i < n &&
+ src[i] != PPP_ESCAPE && src[i] != PPP_FLAG; i++);
+ }
+ if (i > 0 && (conn->ppp_flags & PPP_F_TOSS) == 0) {
+ if (i <= buf_tailroom(buf)) {
+ char *p = buf_put_data(buf, src, i);
+ if (conn->ppp_flags & PPP_F_ESCAPE) {
+ *p ^= PPP_TRANS;
+ conn->ppp_flags &= ~PPP_F_ESCAPE;
+ }
+ } else
+ conn->ppp_flags |= PPP_F_TOSS;
+ }
+
+ byte = src[i++];
+ src += i;
+ n -= i;
+
+ switch (byte) {
+ case PPP_FLAG:
+ if (buf->len <= PPP_FCSLEN || conn->ppp_flags) {
+ buf_set_length(buf, 0);
+ conn->ppp_flags = 0;
+ break;
+ }
+ buf_put(buf, -PPP_FCSLEN);
+ hdr = buf_push(buf, sizeof(*hdr));
+ INIT_SSTP_DATA_HDR(hdr, buf->len);
+ sstp_queue(conn, buf);
+ goto alloc;
+ case PPP_ESCAPE:
+ conn->ppp_flags |= PPP_F_ESCAPE;
+ break;
+ }
+ }
+#endif
+ }
+ return sstp_write(&conn->hnd);
+
+drop:
+ sstp_disconnect(conn);
+ return 1;
+}
+
+static int ppp_write(struct triton_md_handler_t *h)
+{
+ struct sstp_conn_t *conn = container_of(h, typeof(*conn), ppp_hnd);
+ struct buffer_t *buf;
+ int n;
+
+ while (!list_empty(&conn->ppp_queue)) {
+ buf = list_first_entry(&conn->ppp_queue, typeof(*buf), entry);
+
+ if (buf_headroom(buf) > 0)
+ triton_md_disable_handler(h, MD_MODE_WRITE);
+
+ while (buf->len) {
+ n = write(conn->ppp_hnd.fd, buf->head, buf->len);
+ if (n < 0) {
+ if (errno == EINTR)
+ continue;
+ if (errno == EAGAIN)
+ break;
+ if (conf_verbose && errno != EPIPE)
+ log_ppp_info2("sstp: ppp: write: %s\n", strerror(errno));
+ goto drop;
+ } else if (n == 0)
+ break;
+ buf_pull(buf, n);
+ }
+
+ if (buf->len) {
+ triton_md_enable_handler(h, MD_MODE_WRITE);
+ break;
+ }
+
+ list_del(&buf->entry);
+ free_buf(buf);
+ }
+ return 0;
+
+drop:
+ triton_context_call(&conn->ctx, (triton_event_func)sstp_disconnect, conn);
+ return 1;
+}
+
+static inline void ppp_queue(struct sstp_conn_t *conn, struct buffer_t *buf)
+{
+ list_add_tail(&buf->entry, &conn->ppp_queue);
+}
+
+static int ppp_send(struct sstp_conn_t *conn, struct buffer_t *buf)
+{
+ ppp_queue(conn, buf);
+ return ppp_write(&conn->ppp_hnd) ? -1 : 0;
+}
+
+/* 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 int sstp_send_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;
+ struct buffer_t *buf = alloc_buf(sizeof(*msg));
+
+ if (conf_verbose)
+ log_ppp_info2("send [SSTP SSTP_MSG_CALL_CONNECT_ACK]\n");
+
+ if (!buf) {
+ log_error("sstp: no memory\n");
+ return -1;
+ }
+
+ msg = buf_put_zero(buf, sizeof(*msg));
+
+ 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;
+ if (conn->nonce)
+ memcpy(msg->attr.nonce, conn->nonce, SSTP_NONCE_SIZE);
+
+ return sstp_send(conn, buf);
+}
+
+static int sstp_send_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;
+ struct buffer_t *buf = alloc_buf(sizeof(*msg));
+
+ if (conf_verbose)
+ log_ppp_info2("send [SSTP SSTP_MSG_CALL_CONNECT_NAK]\n");
+
+ if (!buf) {
+ log_error("sstp: no memory\n");
+ return -1;
+ }
+
+ msg = buf_put_zero(buf, sizeof(*msg));
+
+ 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, buf);
+}
+
+static int sstp_send_msg_call_abort(struct sstp_conn_t *conn)
+{
+ struct {
+ struct sstp_ctrl_hdr hdr;
+ struct sstp_attrib_status_info attr;
+ } __attribute__((packed)) *msg;
+ struct buffer_t *buf = alloc_buf(sizeof(*msg));
+
+ if (conf_verbose)
+ log_ppp_info2("send [SSTP SSTP_MSG_CALL_ABORT]\n");
+
+ if (!buf) {
+ log_error("sstp: no memory\n");
+ return -1;
+ }
+
+ msg = buf_put_zero(buf, sizeof(*msg));
+
+ 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);
+
+ return sstp_send(conn, buf);
+}
+
+static int sstp_send_msg_call_disconnect(struct sstp_conn_t *conn)
+{
+ struct {
+ struct sstp_ctrl_hdr hdr;
+ struct sstp_attrib_status_info attr;
+ } __attribute__((packed)) *msg;
+ struct buffer_t *buf = alloc_buf(sizeof(*msg));
+
+ if (conf_verbose)
+ log_ppp_info2("send [SSTP SSTP_MSG_CALL_DISCONNECT]\n");
+
+ if (!buf) {
+ log_error("sstp: no memory\n");
+ return -1;
+ }
+
+ msg = buf_put_zero(buf, sizeof(*msg));
+
+ 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);
+
+ return sstp_send(conn, buf);
+}
+
+static int sstp_send_msg_call_disconnect_ack(struct sstp_conn_t *conn)
+{
+ struct {
+ struct sstp_ctrl_hdr hdr;
+ } __attribute__((packed)) *msg;
+ struct buffer_t *buf = alloc_buf(sizeof(*msg));
+
+ if (conf_verbose)
+ log_ppp_info2("send [SSTP SSTP_MSG_CALL_DISCONNECT_ACK]\n");
+
+ if (!buf) {
+ log_error("sstp: no memory\n");
+ return -1;
+ }
+
+ msg = buf_put_zero(buf, sizeof(*msg));
+
+ INIT_SSTP_CTRL_HDR(&msg->hdr, SSTP_MSG_CALL_DISCONNECT_ACK, 0, sizeof(*msg));
+
+ return sstp_send(conn, buf);
+}
+
+static int sstp_send_msg_echo_request(struct sstp_conn_t *conn)
+{
+ struct {
+ struct sstp_ctrl_hdr hdr;
+ } __attribute__((packed)) *msg;
+ struct buffer_t *buf = alloc_buf(sizeof(*msg));
+
+ if (conf_verbose)
+ log_ppp_info2("send [SSTP SSTP_MSG_ECHO_REQUEST]\n");
+
+ if (!buf) {
+ log_error("sstp: no memory\n");
+ return -1;
+ }
+
+ msg = buf_put_zero(buf, sizeof(*msg));
+
+ INIT_SSTP_CTRL_HDR(&msg->hdr, SSTP_MSG_ECHO_REQUEST, 0, sizeof(*msg));
+
+ return sstp_send(conn, buf);
+}
+
+static int sstp_send_msg_echo_response(struct sstp_conn_t *conn)
+{
+ struct {
+ struct sstp_ctrl_hdr hdr;
+ } __attribute__((packed)) *msg;
+ struct buffer_t *buf = alloc_buf(sizeof(*msg));
+
+ if (conf_verbose)
+ log_ppp_info2("send [SSTP SSTP_MSG_ECHO_RESPONSE]\n");
+
+ if (!buf) {
+ log_error("sstp: no memory\n");
+ return -1;
+ }
+
+ msg = buf_put_zero(buf, sizeof(*msg));
+
+ INIT_SSTP_CTRL_HDR(&msg->hdr, SSTP_MSG_ECHO_RESPONSE, 0, sizeof(*msg));
+
+ return sstp_send(conn, buf);
+}
+
+static int sstp_recv_msg_call_connect_request(struct sstp_conn_t *conn, struct sstp_ctrl_hdr *hdr)
+{
+ struct {
+ struct sstp_ctrl_hdr hdr;
+ struct sstp_attrib_encapsulated_protocol attr;
+ } __attribute__((packed)) *msg = (void *)hdr;
+ 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:
+ return sstp_abort(conn, 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)) {
+ return sstp_abort(conn, 0);
+ }
+ if (ntohs(msg->attr.protocol_id) != SSTP_ENCAPSULATED_PROTOCOL_PPP) {
+ if (conn->nak_sent++ == 3) {
+ log_ppp_error("sstp: nak limit reached\n");
+ return sstp_abort(conn, 0);
+ }
+ return sstp_send_msg_call_connect_nak(conn);
+ }
+
+ if (ppp_allocate_pty(&master, &slave, O_CLOEXEC | O_NONBLOCK) < 0)
+ return -1;
+
+ conn->ppp_hnd.fd = master;
+ conn->ppp_hnd.read = ppp_read;
+ conn->ppp_hnd.write = ppp_write;
+
+ 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 (conn->nonce)
+ read(urandom_fd, conn->nonce, SSTP_NONCE_SIZE);
+ if (sstp_send_msg_call_connect_ack(conn))
+ goto error;
+
+ conn->sstp_state = STATE_SERVER_CALL_CONNECTED_PENDING;
+ conn->ppp_state = STATE_STARTING;
+
+ conn->ppp.fd = slave;
+ if (establish_ppp(&conn->ppp)) {
+ conn->ppp_state = STATE_FINISHED;
+ goto error;
+ }
+
+ if (conn->timeout_timer.tpd)
+ triton_timer_del(&conn->timeout_timer);
+
+ return 0;
+
+error:
+ if (conn->ppp_hnd.tpd)
+ triton_md_unregister_handler(&conn->ppp_hnd, 1);
+ close(slave);
+ return -1;
+}
+
+static int sstp_recv_msg_call_connected(struct sstp_conn_t *conn, struct sstp_ctrl_hdr *hdr)
+{
+ struct {
+ struct sstp_ctrl_hdr hdr;
+ struct sstp_attrib_crypto_binding attr;
+ } __attribute__((packed)) *msg = (void *)hdr;
+
+ 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:
+ sstp_abort(conn, 0);
+ return 0;
+ }
+
+ if (ntohs(msg->hdr.length) < sizeof(*msg) ||
+ ntohs(msg->hdr.num_attributes) < 1 ||
+ msg->attr.hdr.attribute_id != SSTP_ATTRIB_CRYPTO_BINDING ||
+ ntohs(msg->attr.hdr.length) < sizeof(msg->attr)) {
+ return sstp_abort(conn, 0);
+ }
+
+ if (conn->nonce && memcmp(msg->attr.nonce, conn->nonce, SSTP_NONCE_SIZE) != 0)
+ return sstp_abort(conn, 0);
+
+ switch (msg->attr.hash_protocol_bitmask & conf_hash_protocol) {
+ case CERT_HASH_PROTOCOL_SHA1:
+ if (conf_hash_sha1.len == SHA_DIGEST_LENGTH &&
+ memcmp(msg->attr.cert_hash, conf_hash_sha1.hash, SHA_DIGEST_LENGTH) != 0)
+ return sstp_abort(conn, 0);
+ break;
+ case CERT_HASH_PROTOCOL_SHA256:
+ if (conf_hash_sha256.len == SHA256_DIGEST_LENGTH &&
+ memcmp(msg->attr.cert_hash, conf_hash_sha256.hash, SHA256_DIGEST_LENGTH) != 0)
+ return sstp_abort(conn, 0);
+ break;
+ default:
+ return sstp_abort(conn, 0);
+ }
+
+ conn->sstp_state = STATE_SERVER_CALL_CONNECTED;
+
+ _free(conn->nonce);
+ conn->nonce = NULL;
+
+ if (conn->hello_interval) {
+ conn->hello_timer.period = conn->hello_interval * 1000;
+ triton_timer_add(&conn->ctx, &conn->hello_timer, 0);
+ }
+
+ return 0;
+}
+
+static int sstp_recv_msg_call_abort(struct sstp_conn_t *conn)
+{
+ int ret = 0;
+
+ if (conf_verbose)
+ log_ppp_info2("recv [SSTP SSTP_MSG_CALL_ABORT]\n");
+
+ switch (conn->sstp_state) {
+ case STATE_CALL_ABORT_PENDING:
+ break;
+ case STATE_CALL_ABORT_TIMEOUT_PENDING:
+ case STATE_CALL_DISCONNECT_TIMEOUT_PENDING:
+ return 0;
+ default:
+ conn->sstp_state = STATE_CALL_ABORT_IN_PROGRESS_2;
+ ret = sstp_send_msg_call_abort(conn);
+ break;
+ }
+
+ conn->timeout_timer.period = SSTP_ABORT_TIMEOUT_2 * 1000;
+ if (conn->timeout_timer.tpd)
+ triton_timer_mod(&conn->timeout_timer, 0);
+ else
+ triton_timer_add(&conn->ctx, &conn->timeout_timer, 0);
+
+ conn->sstp_state = STATE_CALL_ABORT_TIMEOUT_PENDING;
+
+ return ret;
+}
+
+static int sstp_recv_msg_call_disconnect(struct sstp_conn_t *conn)
+{
+ int ret;
+
+ 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:
+ return 0;
+ case STATE_CALL_DISCONNECT_ACK_PENDING:
+ if (conn->timeout_timer.tpd)
+ triton_timer_del(&conn->timeout_timer);
+ break;
+ }
+
+ conn->sstp_state = STATE_CALL_DISCONNECT_IN_PROGRESS_2;
+
+ ret = sstp_send_msg_call_disconnect_ack(conn);
+
+ conn->timeout_timer.period = SSTP_DISCONNECT_TIMEOUT_2 * 1000;
+ if (conn->timeout_timer.tpd)
+ triton_timer_mod(&conn->timeout_timer, 0);
+ else
+ triton_timer_add(&conn->ctx, &conn->timeout_timer, 0);
+
+ conn->sstp_state = STATE_CALL_DISCONNECT_TIMEOUT_PENDING;
+
+ return ret;
+}
+
+static int sstp_recv_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:
+ break;
+ case STATE_CALL_ABORT_PENDING:
+ case STATE_CALL_ABORT_TIMEOUT_PENDING:
+ case STATE_CALL_DISCONNECT_TIMEOUT_PENDING:
+ return 0;
+ default:
+ return sstp_abort(conn, 0);
+ }
+
+ conn->sstp_state = STATE_SERVER_CALL_DISCONNECTED;
+ triton_context_call(&conn->ctx, (triton_event_func)sstp_disconnect, conn);
+
+ return 0;
+}
+
+static int sstp_recv_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:
+ break;
+ 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;
+ default:
+ return sstp_abort(conn, 0);
+ }
+
+ return sstp_send_msg_echo_response(conn);
+}
+
+static int sstp_recv_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:
+ break;
+ 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;
+ default:
+ return sstp_abort(conn, 0);
+ }
+
+ conn->hello_sent = 0;
+ return 0;
+}
+
+static int sstp_recv_data_packet(struct sstp_conn_t *conn, struct sstp_hdr *hdr)
+{
+ struct buffer_t *buf;
+ int size;
+#if !PPP_SYNC
+ uint8_t *src, *dst, byte;
+ uint16_t fcs;
+ int n;
+#endif
+
+ switch (conn->sstp_state) {
+ case STATE_SERVER_CALL_CONNECTED_PENDING:
+ case STATE_SERVER_CALL_CONNECTED:
+ break;
+ default:
+ return 0;
+ }
+
+ size = ntohs(hdr->length) - sizeof(*hdr);
+#if PPP_SYNC
+ buf = alloc_buf(size);
+ if (!buf) {
+ log_error("sstp: no memory\n");
+ return -1;
+ }
+
+ buf_put_data(buf, hdr->data, size);
+#else
+ buf = alloc_buf(size*2 + 2 + PPP_FCSLEN);
+ if (!buf) {
+ log_error("sstp: no memory\n");
+ return -1;
+ }
+
+ src = hdr->data;
+ dst = buf->tail;
+ fcs = PPP_INITFCS;
+
+ *dst++ = PPP_FLAG;
+ for (n = size + PPP_FCSLEN; n > 0; n--) {
+ if (n > PPP_FCSLEN) {
+ byte = *src++;
+ fcs = (fcs >> 8) ^ fcstab[(fcs ^ byte) & 0xff];
+ } else if (n == PPP_FCSLEN) {
+ fcs ^= PPP_INITFCS;
+ byte = fcs & 0xff;
+ } else if (n == PPP_FCSLEN - 1)
+ byte = fcs >> 8;
+ if (byte < 0x20 || byte == PPP_FLAG || byte == PPP_ESCAPE) {
+ *dst++ = PPP_ESCAPE;
+ *dst++ = byte ^ PPP_TRANS;
+ } else
+ *dst++ = byte;
+ }
+ *dst++ = PPP_FLAG;
+
+ buf_put(buf, dst - buf->tail);
+#endif
+
+ return ppp_send(conn, buf);
+}
+
+static int sstp_recv_packet(struct sstp_conn_t *conn, struct sstp_hdr *hdr)
+{
+ struct sstp_ctrl_hdr *msg = (struct sstp_ctrl_hdr *)hdr;
+
+ switch (hdr->reserved) {
+ case SSTP_DATA_PACKET:
+ return sstp_recv_data_packet(conn, hdr);
+ case SSTP_CTRL_PACKET:
+ if (ntohs(hdr->length) >= sizeof(*msg))
+ break;
+ log_ppp_error("recv [SSTP too short message]\n");
+ return -1;
+ default:
+ log_ppp_warn("recv [SSTP unknown packet type %02x]\n", hdr->reserved);
+ return 0;
+ }
+
+ if (conn->hello_timer.tpd) {
+ conn->hello_timer.period = conn->hello_interval * 1000;
+ triton_timer_mod(&conn->hello_timer, 0);
+ }
+
+ switch (ntohs(msg->message_type)) {
+ case SSTP_MSG_CALL_CONNECT_REQUEST:
+ return sstp_recv_msg_call_connect_request(conn, msg);
+ case SSTP_MSG_CALL_CONNECT_ACK:
+ case SSTP_MSG_CALL_CONNECT_NAK:
+ return sstp_abort(conn, 0);
+ case SSTP_MSG_CALL_CONNECTED:
+ return sstp_recv_msg_call_connected(conn, msg);
+ case SSTP_MSG_CALL_ABORT:
+ return sstp_recv_msg_call_abort(conn);
+ case SSTP_MSG_CALL_DISCONNECT:
+ return sstp_recv_msg_call_disconnect(conn);
+ case SSTP_MSG_CALL_DISCONNECT_ACK:
+ return sstp_recv_msg_call_disconnect_ack(conn);
+ case SSTP_MSG_ECHO_REQUEST:
+ return sstp_recv_msg_echo_request(conn);
+ case SSTP_MSG_ECHO_RESPONSE:
+ return sstp_recv_msg_echo_response(conn);
+ default:
+ log_ppp_warn("recv [SSTP unknown message type %04x]\n", ntohs(msg->message_type));
+ return 0;
+ }
+}
+
+static int sstp_handler(struct sstp_conn_t *conn, struct buffer_t *buf)
+{
+ struct sstp_hdr *hdr;
+ int n;
+
+ while (buf->len >= sizeof(*hdr)) {
+ hdr = (struct sstp_hdr *)buf->head;
+ if (hdr->version != SSTP_VERSION) {
+ log_ppp_error("recv [SSTP invalid version]\n");
+ return -1;
+ }
+
+ n = ntohs(hdr->length);
+ if (n > SSTP_MAX_PACKET_SIZE) {
+ log_ppp_error("recv [SSTP too long packet]\n");
+ return -1;
+ } else if (n > buf->len)
+ break;
+
+ if (sstp_recv_packet(conn, hdr) < 0)
+ return -1;
+ buf_pull(buf, n);
+ };
+
+ return 0;
+}
+
+static int sstp_read(struct triton_md_handler_t *h)
+{
+ struct sstp_conn_t *conn = container_of(h, typeof(*conn), hnd);
+ struct buffer_t *buf = conn->in;
+ int n;
+
+ while ((n = buf_tailroom(buf)) > 0) {
+ n = conn->stream->read(conn->stream, buf->tail, n);
+ if (n < 0) {
+ if (errno == EINTR)
+ continue;
+ if (errno == EAGAIN)
+ return 0;
+ log_ppp_error("sstp: read: %s\n", strerror(errno));
+ goto drop;
+ } else if (n == 0) {
+ if (conf_verbose)
+ log_ppp_info2("sstp: disconnect by peer\n");
+ goto drop;
+ }
+ buf_put(buf, n);
+
+ n = conn->handler(conn, buf);
+ if (n < 0)
+ goto drop;
+
+ buf_expand_tail(buf, SSTP_MAX_PACKET_SIZE);
+ }
+ return 0;
+
+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 buffer_t *buf;
+ int n;
+
+ while (!list_empty(&conn->out_queue)) {
+ buf = list_first_entry(&conn->out_queue, typeof(*buf), entry);
+ if (buf_headroom(buf) > 0)
+ triton_md_disable_handler(h, MD_MODE_WRITE);
+
+ while (buf->len) {
+ n = conn->stream->write(conn->stream, buf->head, buf->len);
+ if (n < 0) {
+ if (errno == EINTR)
+ continue;
+ if (errno == EAGAIN)
+ break;
+ if (conf_verbose && errno != EPIPE)
+ log_ppp_info2("sstp: write: %s\n", strerror(errno));
+ goto drop;
+ } else if (n == 0)
+ break;
+ buf_pull(buf, n);
+ }
+
+ if (buf->len) {
+ triton_md_enable_handler(h, MD_MODE_WRITE);
+ break;
+ }
+
+ list_del(&buf->entry);
+ free_buf(buf);
+ }
+ return 0;
+
+drop:
+ triton_context_call(&conn->ctx, (triton_event_func)sstp_disconnect, conn);
+ return 1;
+}
+
+static inline void sstp_queue(struct sstp_conn_t *conn, struct buffer_t *buf)
+{
+ list_add_tail(&buf->entry, &conn->out_queue);
+}
+
+static int sstp_send(struct sstp_conn_t *conn, struct buffer_t *buf)
+{
+ sstp_queue(conn, buf);
+ return sstp_write(&conn->hnd) ? -1 : 0;
+}
+
+static void sstp_msg_echo(struct triton_timer_t *t)
+{
+ struct sstp_conn_t *conn = container_of(t, typeof(*conn), hello_timer);
+ struct ppp_idle idle;
+
+ switch (conn->sstp_state) {
+ case STATE_SERVER_CALL_CONNECTED:
+ if (ioctl(conn->ppp.unit_fd, PPPIOCGIDLE, &idle) >= 0 &&
+ idle.recv_idle < conn->hello_interval) {
+ t->period = (conn->hello_interval - idle.recv_idle) * 1000;
+ triton_timer_mod(t, 0);
+ break;
+ }
+ if (conn->hello_sent++) {
+ log_ppp_warn("sstp: no echo reply\n");
+ sstp_abort(conn, 0);
+ } else
+ sstp_send_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);
+
+ triton_timer_del(t);
+
+ 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:
+ triton_context_call(&conn->ctx, (triton_event_func)sstp_disconnect, conn);
+ break;
+ default:
+ sstp_abort(conn, 0);
+ break;
+ }
+}
+
+static void sstp_close(struct triton_context_t *ctx)
+{
+ struct sstp_conn_t *conn = container_of(ctx, typeof(*conn), ctx);
+
+ switch (conn->ppp_state) {
+ case STATE_STARTING:
+ case STATE_STARTED:
+ conn->ppp_state = STATE_FINISHED;
+ ap_session_terminate(&conn->ppp.ses, TERM_ADMIN_RESET, 1);
+ sstp_abort(conn, 1);
+ break;
+ default:
+ sstp_abort(conn, 0);
+ break;
+ }
+}
+
+static int sstp_abort(struct sstp_conn_t *conn, int disconnect)
+{
+ static const struct {
+ int send_state;
+ int exit_state;
+ int timeout;
+ } modes[2] = {
+ { STATE_CALL_ABORT_IN_PROGRESS_1, STATE_CALL_ABORT_PENDING, SSTP_ABORT_TIMEOUT_1 },
+ { STATE_CALL_DISCONNECT_IN_PROGRESS_1, STATE_CALL_DISCONNECT_ACK_PENDING, SSTP_DISCONNECT_TIMEOUT_1 }
+ };
+ int ret, idx = !!disconnect;
+
+ conn->sstp_state = modes[idx].send_state;
+ ret = idx ? sstp_send_msg_call_disconnect(conn) : sstp_send_msg_call_abort(conn);
+
+ conn->timeout_timer.period = modes[idx].timeout * 1000;
+ if (conn->timeout_timer.tpd)
+ triton_timer_mod(&conn->timeout_timer, 0);
+ else
+ triton_timer_add(&conn->ctx, &conn->timeout_timer, 0);
+
+ conn->sstp_state = modes[idx].exit_state;
+
+ return ret;
+}
+
+static void sstp_disconnect(struct sstp_conn_t *conn)
+{
+ struct buffer_t *buf;
+
+ log_ppp_debug("disconnecting\n");
+
+ if (conn->timeout_timer.tpd)
+ triton_timer_del(&conn->timeout_timer);
+ if (conn->hello_timer.tpd)
+ triton_timer_del(&conn->hello_timer);
+
+ if (conn->hnd.tpd) {
+ triton_md_unregister_handler(&conn->hnd, 0);
+ conn->stream->close(conn->stream);
+ }
+ if (conn->ppp_hnd.tpd)
+ triton_md_unregister_handler(&conn->ppp_hnd, 1);
+
+ switch (conn->ppp_state) {
+ case STATE_STARTING:
+ case STATE_STARTED:
+ conn->ppp_state = STATE_FINISHED;
+ ap_session_terminate(&conn->ppp.ses, TERM_LOST_CARRIER, 1);
+ }
+// triton_event_fire(EV_CTRL_FINISHED, &conn->ppp.ses);
+
+ triton_context_unregister(&conn->ctx);
+
+ _free(conn->nonce);
+
+ if (conn->stream)
+ conn->stream->free(conn->stream);
+ free_buf(conn->in);
+ free_buf(conn->ppp_in);
+
+ list_splice_init(&conn->ppp_queue, &conn->out_queue);
+ while (!list_empty(&conn->out_queue)) {
+ buf = list_first_entry(&conn->out_queue, typeof(*buf), entry);
+ list_del(&buf->entry);
+ free_buf(buf);
+ }
+
+ _free(conn->ctrl.calling_station_id);
+ _free(conn->ctrl.called_station_id);
+
+ mempool_free(conn);
+
+ log_info2("sstp: disconnected\n");
+}
+
+static void sstp_start(struct sstp_conn_t *conn)
+{
+ log_debug("sstp: start\n");
+
+#ifdef CRYPTO_OPENSSL
+ if (serv.ssl_ctx)
+ conn->stream = ssl_stream_init(conn->hnd.fd, serv.ssl_ctx);
+ else
+#endif
+ conn->stream = stream_init(conn->hnd.fd);
+ if (!conn->stream) {
+ log_error("sstp: stream open error: %s\n", strerror(errno));
+ goto error;
+ }
+
+ triton_md_register_handler(&conn->ctx, &conn->hnd);
+ triton_md_enable_handler(&conn->hnd, MD_MODE_READ);
+
+ log_info2("sstp: started\n");
+// triton_event_fire(EV_CTRL_STARTING, &conn->ppp.ses);
+
+ return;
+
+error:
+ sstp_disconnect(conn);
+}
+
+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:%d\n", inet_ntoa(addr.sin_addr), ntohs(addr.sin_port));
+
+ 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 = 65536;
+ if (setsockopt(sock, SOL_SOCKET, SO_SNDBUF, &value, sizeof(value)) < 0) {
+ log_error("sstp: failed to set send buffer: %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_interval = conf_hello_interval;
+
+ conn->sstp_state = STATE_SERVER_CALL_DISCONNECTED;
+ conn->ppp_state = STATE_INIT;
+ conn->handler = http_handler;
+
+ //conn->bypass_auth = conf_bypass_auth;
+ //conn->http_cookie = NULL:
+ //conn->auth_key...
+ conn->nonce = _malloc(SSTP_NONCE_SIZE);
+
+ conn->in = alloc_buf(SSTP_MAX_PACKET_SIZE*2);
+ INIT_LIST_HEAD(&conn->out_queue);
+ INIT_LIST_HEAD(&conn->ppp_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(sizeof("255.255.255.255:65535"));
+ conn->ctrl.called_station_id = _malloc(sizeof("255.255.255.255"));
+ sprintf(conn->ctrl.calling_station_id, "%s:%d",
+ inet_ntoa(addr.sin_addr), ntohs(addr.sin_port));
+ 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);
+ if (conf_ifname)
+ conn->ppp.ses.ifname_rename = _strdup(conf_ifname);
+
+ triton_context_register(&conn->ctx, &conn->ppp.ses);
+ triton_context_call(&conn->ctx, (triton_event_func)sstp_start, conn);
+ triton_context_wakeup(&conn->ctx);
+
+ triton_timer_add(&conn->ctx, &conn->timeout_timer, 0);
+ }
+
+ return 0;
+}
+
+static void sstp_serv_close(struct triton_context_t *ctx)
+{
+ struct sstp_serv_t *serv = container_of(ctx, typeof(*serv), ctx);
+
+ triton_md_unregister_handler(&serv->hnd, 1);
+ triton_context_unregister(ctx);
+
+#ifdef CRYPTO_OPENSSL
+ if (serv->ssl_ctx)
+ SSL_CTX_free(serv->ssl_ctx);
+ serv->ssl_ctx = NULL;
+
+ CRYPTO_thread_cleanup();
+#endif
+}
+
+static int strhas(const char *s1, const char *s2, int delim)
+{
+ char *ptr;
+ int n = strlen(s2);
+
+ while ((ptr = strchr(s1, delim))) {
+ if (ptr - s1 == n && memcmp(s1, s2, n) == 0)
+ return 0;
+ s1 = ++ptr;
+ }
+ return strcmp(s1, s2);
+}
+
+static int hex2bin(const char *src, uint8_t *dst, size_t size)
+{
+ char buf[3], *err;
+ int n;
+
+ memset(buf, 0, sizeof(buf));
+ for (n = 0; n < size && src[0] && src[1]; n++) {
+ buf[0] = *src++;
+ buf[1] = *src++;
+ dst[n] = strtoul(buf, &err, 16);
+ if (err == buf || *err)
+ break;
+ if (*src == ':')
+ src++;
+ }
+ return n;
+}
+
+#ifdef CRYPTO_OPENSSL
+#ifdef SSL_CTRL_SET_TLSEXT_HOSTNAME
+static int ssl_servername(SSL *ssl, int *al, void *arg)
+{
+ const char *servername;
+
+ if (!conf_hostname)
+ return SSL_TLSEXT_ERR_OK;
+
+ servername = SSL_get_servername(ssl, TLSEXT_NAMETYPE_host_name);
+ if (conf_verbose) {
+ log_ppp_info2("sstp: recv [SSL <%s%s>]\n",
+ servername ? "SNI " : "no SNI", servername ? : "");
+ }
+
+ if (strcasecmp(servername ? : "", conf_hostname) != 0)
+ return SSL_TLSEXT_ERR_ALERT_FATAL;
+
+ return SSL_TLSEXT_ERR_OK;
+}
+#endif
+
+static void ssl_load_config(struct sstp_serv_t *serv, const char *servername)
+{
+ SSL_CTX *old_ctx, *ssl_ctx = NULL;
+ X509 *cert = NULL;
+ BIO *in = NULL;
+ char *opt;
+
+ opt = conf_get_opt("sstp", "ssl-pemfile");
+ if (opt) {
+ in = BIO_new(BIO_s_file());
+ if (!in) {
+ log_error("sstp: SSL certificate error: %s\n", ERR_error_string(ERR_get_error(), NULL));
+ goto error;
+ }
+
+ if (BIO_read_filename(in, opt) <= 0) {
+ log_error("sstp: SSL certificate error: %s\n", ERR_error_string(ERR_get_error(), NULL));
+ goto error;
+ }
+
+ cert = PEM_read_bio_X509(in, NULL, NULL, NULL);
+ if (!cert) {
+ log_error("sstp: SSL certificate error: %s\n", ERR_error_string(ERR_get_error(), NULL));
+ goto error;
+ }
+ }
+
+ opt = conf_get_opt("sstp", "ssl");
+ if (atoi(opt) > 0) {
+ ssl_ctx = SSL_CTX_new(SSLv23_server_method());
+ if (!ssl_ctx) {
+ log_error("sstp: SSL_CTX error: %s\n", ERR_error_string(ERR_get_error(), NULL));
+ goto error;
+ }
+
+ SSL_CTX_set_options(ssl_ctx,
+#ifdef SSL_OP_DONT_INSERT_EMPTY_FRAGMENTS
+ SSL_OP_DONT_INSERT_EMPTY_FRAGMENTS |
+#endif
+ SSL_OP_NO_SSLv2 |
+ SSL_OP_NO_SSLv3 |
+ SSL_OP_NO_COMPRESSION);
+ SSL_CTX_set_mode(ssl_ctx,
+ SSL_MODE_ENABLE_PARTIAL_WRITE |
+ SSL_MODE_ACCEPT_MOVING_WRITE_BUFFER);
+ SSL_CTX_set_read_ahead(ssl_ctx, 1);
+
+ opt = conf_get_opt("sstp", "ssl-ciphers");
+ if (opt && SSL_CTX_set_cipher_list(ssl_ctx, opt) != 1) {
+ log_error("sstp: SSL cipher list error: %s\n", ERR_error_string(ERR_get_error(), NULL));
+ goto error;
+ }
+
+ opt = conf_get_opt("sstp", "ssl-prefer-server-ciphers");
+ if (opt && atoi(opt))
+ SSL_CTX_set_options(ssl_ctx, SSL_OP_CIPHER_SERVER_PREFERENCE);
+
+ if (cert && SSL_CTX_use_certificate(ssl_ctx, cert) != 1) {
+ log_error("sstp: SSL certificate error: %s\n", ERR_error_string(ERR_get_error(), NULL));
+ goto error;
+ }
+
+ opt = conf_get_opt("sstp", "ssl-keyfile") ? : conf_get_opt("sstp", "ssl-pemfile");
+ if ((opt && SSL_CTX_use_PrivateKey_file(ssl_ctx, opt, SSL_FILETYPE_PEM) != 1) ||
+ SSL_CTX_check_private_key(ssl_ctx) != 1) {
+ log_error("sstp: SSL private key error: %s\n", ERR_error_string(ERR_get_error(), NULL));
+ goto error;
+ }
+
+ opt = conf_get_opt("sstp", "ssl-ca-file");
+ if (opt && SSL_CTX_load_verify_locations(ssl_ctx, opt, NULL) != 1) {
+ log_error("sstp: SSL ca file error: %s\n", ERR_error_string(ERR_get_error(), NULL));
+ goto error;
+ }
+
+#ifdef SSL_CTRL_SET_TLSEXT_HOSTNAME
+ if (servername && SSL_CTX_set_tlsext_servername_callback(ssl_ctx, ssl_servername) != 1)
+ log_warn("sstp: SSL server name check error: %s\n", ERR_error_string(ERR_get_error(), NULL));
+#endif
+ }
+
+ if (cert) {
+ if (conf_hash_protocol & CERT_HASH_PROTOCOL_SHA1)
+ X509_digest(cert, EVP_sha1(), conf_hash_sha1.hash, &conf_hash_sha1.len);
+ if (conf_hash_protocol & CERT_HASH_PROTOCOL_SHA256)
+ X509_digest(cert, EVP_sha256(), conf_hash_sha256.hash, &conf_hash_sha256.len);
+ }
+
+ old_ctx = serv->ssl_ctx;
+ serv->ssl_ctx = ssl_ctx;
+ ssl_ctx = old_ctx;
+
+error:
+ if (ssl_ctx)
+ SSL_CTX_free(ssl_ctx);
+ if (cert)
+ X509_free(cert);
+ if (in)
+ BIO_free(in);
+}
+#endif
+
+static void load_config(void)
+{
+ char *opt;
+
+ opt = conf_get_opt("sstp", "verbose");
+ if (opt && atoi(opt) >= 0)
+ conf_verbose = atoi(opt) > 0;
+
+ conf_hostname = conf_get_opt("sstp", "host-name");
+
+ opt = conf_get_opt("sstp", "cert-hash-proto");
+ if (opt) {
+ conf_hash_protocol = 0;
+ if (strhas(opt, "sha1", ',') == 0)
+ conf_hash_protocol |= CERT_HASH_PROTOCOL_SHA1;
+ if (strhas(opt, "sha256", ',') == 0)
+ conf_hash_protocol |= CERT_HASH_PROTOCOL_SHA256;
+ }
+
+#ifdef CRYPTO_OPENSSL
+ ssl_load_config(&serv, conf_hostname);
+ opt = serv.ssl_ctx ? "enabled" : "disabled";
+#else
+ opt = "not available";
+#endif
+ if (conf_verbose)
+ log_info2("sstp: SSL support %s\n", opt);
+
+ opt = conf_get_opt("sstp", "cert-hash-sha1");
+ if (opt) {
+ conf_hash_sha1.len = hex2bin(opt,
+ conf_hash_sha1.hash, sizeof(conf_hash_sha1.hash));
+ }
+
+ opt = conf_get_opt("sstp", "cert-hash-sha256");
+ if (opt) {
+ conf_hash_sha256.len = hex2bin(opt,
+ conf_hash_sha256.hash, sizeof(conf_hash_sha256.hash));
+ }
+
+ 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", "ppp-max-mtu");
+ if (opt && atoi(opt) > 0)
+ conf_ppp_max_mtu = atoi(opt);
+
+ conf_ip_pool = conf_get_opt("sstp", "ip-pool");
+ conf_ifname = conf_get_opt("sstp", "ifname");
+
+ 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;
+ }
+}
+
+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;
+
+ 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;
+ }
+
+#ifdef CRYPTO_OPENSSL
+ CRYPTO_thread_setup();
+ SSL_load_error_strings();
+ SSL_library_init();
+#endif
+
+ conn_pool = mempool_create(sizeof(struct sstp_conn_t));
+
+ 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);
+
+ triton_event_register_handler(EV_CONFIG_RELOAD, (triton_event_func)load_config);
+}
+
+DEFINE_INIT(20, sstp_init);