From 1801847a36c5737b92ef25f96bb7e10135c0381a Mon Sep 17 00:00:00 2001 From: Vladislav Grishenko Date: Mon, 27 Nov 2017 02:17:12 +0500 Subject: sstp: make sstp great again. simplify ssl handlers, fix crashes, move to async ppp TODO: accounting/statistics, minimize syscall & memory usage --- accel-pppd/ctrl/sstp/CMakeLists.txt | 2 + accel-pppd/ctrl/sstp/sstp.c | 1692 ++++++++++++++++++++++------------- accel-pppd/ctrl/sstp/sstp_prot.h | 5 - 3 files changed, 1052 insertions(+), 647 deletions(-) diff --git a/accel-pppd/ctrl/sstp/CMakeLists.txt b/accel-pppd/ctrl/sstp/CMakeLists.txt index 006a93b..3cb1799 100644 --- a/accel-pppd/ctrl/sstp/CMakeLists.txt +++ b/accel-pppd/ctrl/sstp/CMakeLists.txt @@ -2,4 +2,6 @@ INCLUDE_DIRECTORIES(${CMAKE_CURRENT_SOURCE_DIR}) ADD_LIBRARY(sstp SHARED sstp.c) +TARGET_LINK_LIBRARIES(sstp util) + 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 index 5200943..1796488 100644 --- a/accel-pppd/ctrl/sstp/sstp.c +++ b/accel-pppd/ctrl/sstp/sstp.c @@ -7,11 +7,13 @@ #include #include #include +#include #include #include #include #include #include +#include "linux_ppp.h" #ifdef CRYPTO_OPENSSL #include @@ -37,52 +39,111 @@ #define min(x,y) ((x) < (y) ? (x) : (y)) #endif -struct sstp_serv_t { - struct triton_context_t ctx; - struct triton_md_handler_t hnd; +#define log_sstp(log_func, conn, fmt, ...) \ + do { \ + log_func("sstp (%s): " fmt, \ + conn->ctrl.calling_station_id, \ + ##__VA_ARGS__); \ + } while (0) +#define log_sstp_error(conn, fmt, ...) log_sstp(log_error, conn, fmt, ####__VA_ARGS__) +#define log_sstp_warn(conn, fmt, ...) log_sstp(log_warn, conn, fmt, ####__VA_ARGS__) +#define log_sstp_info1(conn, fmt, ...) log_sstp(log_info1, conn, fmt, ####__VA_ARGS__) +#define log_sstp_info2(conn, fmt, ...) log_sstp(log_info2, conn, fmt, ####__VA_ARGS__) +#define log_sstp_debug(conn, fmt, ...) log_sstp(log_debug, conn, fmt, ####__VA_ARGS__) +#define log_sstp_msg(conn, fmt, ...) log_sstp(log_msg, conn, fmt, ####__VA_ARGS__) + +#define log_sstp_ppp(log_func, conn, fmt, ...) \ + do { \ + log_func("sstp (%s): " fmt, \ + conn->ctrl.ifname[0] ? conn->ctrl.ifname : \ + conn->ctrl.calling_station_id, \ + ##__VA_ARGS__); \ + } while (0) +#define log_sstp_ppp_error(conn, fmt, ...) log_sstp_ppp(log_ppp_error, conn, fmt, ####__VA_ARGS__) +#define log_sstp_ppp_warn(conn, fmt, ...) log_sstp_ppp(log_ppp_warn, conn, fmt, ####__VA_ARGS__) +#define log_sstp_ppp_info1(conn, fmt, ...) log_sstp_ppp(log_ppp_info1, conn, fmt, ####__VA_ARGS__) +#define log_sstp_ppp_info2(conn, fmt, ...) log_sstp_ppp(log_ppp_info2, conn, fmt, ####__VA_ARGS__) +#define log_sstp_ppp_debug(conn, fmt, ...) log_sstp_ppp(log_ppp_debug, conn, fmt, ####__VA_ARGS__) +#define log_sstp_ppp_msg(conn, fmt, ...) log_sstp_ppp(log_ppp_msg, conn, fmt, ####__VA_ARGS__) + +#define PPP_SYNC 0 /* buggy yet */ +#define PPP_BUF_SIZE 8192 +#define PPP_F_ESCAPE 1 +#define PPP_F_TOSS 2 + +enum { + STATE_INIT = 0, + STATE_STARTING, + STATE_STARTED, + STATE_FINISHED, +}; + +struct buffer_t { + struct list_head entry; + size_t len; + unsigned char *head; + unsigned char *tail; + unsigned char *end; + unsigned char data[0]; +}; - //uint8_t certificate_hash[32]; +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; - struct triton_md_handler_t ppp_hnd; + struct triton_md_handler_t hnd, 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; + 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]; + struct buffer_t *in; + struct list_head out_queue; - struct list_head send_queue; - void *ppp_buf; - uint8_t *in_buf; - int in_size; + int ppp_state; + int ppp_flags; + struct buffer_t *ppp_in; + struct list_head ppp_queue; - struct ap_ctrl ctrl; struct ppp_t ppp; -}; + struct ap_ctrl ctrl; -struct sstp_pack_t { - struct list_head entry; - void *data; - int size; +#ifdef CRYPTO_OPENSSL + SSL_CTX *ssl_ctx; +#endif }; +static struct sstp_serv_t { + struct triton_context_t ctx; + struct triton_md_handler_t hnd; + + uint8_t certificate_hash[32]; +} 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 = SSTP_MAX_PACKET_SIZE - 8; +static int conf_ppp_max_mtu = 1456; static int conf_hash_protocol = CERT_HASH_PROTOCOL_SHA256; //static int conf_bypass_auth = 0; static const char *conf_ip_pool; @@ -93,15 +154,306 @@ 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_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_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 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 + +static pthread_mutex_t *lock_cs; + +static unsigned long pthreads_thread_id(void) +{ + return (unsigned long)pthread_self(); +} -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); +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); + err = SSL_get_error(stream->ssl, ret); + switch (err) { + case SSL_ERROR_NONE: + case SSL_ERROR_ZERO_RETURN: + return ret; + case SSL_ERROR_WANT_WRITE: + case SSL_ERROR_WANT_READ: + errno = EAGAIN; + /* fall through */ + case SSL_ERROR_SYSCALL: + default: + return -1; + } +} + +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); + err = SSL_get_error(stream->ssl, ret); + switch (err) { + case SSL_ERROR_NONE: + case SSL_ERROR_ZERO_RETURN: + return ret; + case SSL_ERROR_WANT_WRITE: + case SSL_ERROR_WANT_READ: + errno = EAGAIN; + /* fall through */ + case SSL_ERROR_SYSCALL: + default: + return -1; + } +} + +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_mode(stream->ssl, SSL_MODE_AUTO_RETRY); + 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 */ @@ -109,11 +461,11 @@ static char *http_getline(struct sstp_conn_t *conn, int *pos, char *buf, int siz { unsigned char *src, *dst, c, pc; - size = min(size - 1, conn->in_size - *pos); + size = min(size - 1, conn->in->len - *pos); if (size <= 0) return NULL; - src = conn->in_buf + *pos; + src = conn->in->head + *pos; dst = (unsigned char *)buf; for (pc = 0; size--; dst++) { c = *dst = *src++; @@ -128,36 +480,40 @@ static char *http_getline(struct sstp_conn_t *conn, int *pos, char *buf, int siz } *dst = '\0'; - *pos = src - conn->in_buf; + *pos = src - conn->in->head; return buf; } -static int send_http_response(struct sstp_conn_t *conn, char *proto, char *status, char *headers) +static int http_send_response(struct sstp_conn_t *conn, char *proto, char *status, char *headers) { - char timebuf[80], *msg = mempool_alloc(data_pool); + char datetime[sizeof("aaa, dd bbb yyyy HH:MM:SS GMT")], msg[1024]; + struct buffer_t *buf; time_t now = time(NULL); + int n; - 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, + strftime(datetime, sizeof(datetime), "%a, %d %b %Y %H:%M:%S GMT", gmtime(&now)); + n = snprintf(msg, sizeof(msg), "%s %s\r\n" - "%s" - "Server: Microsoft-HTTPAPI/2.0\r\n" + /* "Server: %s\r\n" */ "Date: %s\r\n" - "\r\n", proto, status, headers, timebuf); + "%s" + "\r\n", proto, status, /* "accel-ppp",*/ datetime, headers ? : ""); if (conf_verbose) - log_ppp_debug("send [sstp HTTP reply <%s %s>]\n", proto, status); + log_sstp_info2(conn, "send [HTTP <%s %s>]\n", proto, status); - return sstp_send(conn, msg, strlen(msg)); + buf = alloc_buf(n); + if (!buf) { + log_sstp_error(conn, "no memory\n"); + return -1; + } + buf_put_data(buf, msg, n); + + return sstp_send(conn, buf); } -static int http_request(struct sstp_conn_t *conn) +static int http_recv_request(struct sstp_conn_t *conn) { char buf[1024]; char *line, *method, *request, *proto; @@ -167,29 +523,29 @@ static int http_request(struct sstp_conn_t *conn) return -1; line = http_getline(conn, &pos, buf, sizeof(buf)); - if (line == NULL) + if (!line) goto error; if (conf_verbose) - log_ppp_debug("recv [sstp HTTP request <%s>]\n", line); + log_sstp_info2(conn, "recv [HTTP <%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); + http_send_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); + http_send_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); + http_send_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); + http_send_response(conn, proto, "404 Not Found", NULL); goto error; } @@ -197,13 +553,14 @@ static int http_request(struct sstp_conn_t *conn) if (*line == '\0') break; if (conf_verbose) - log_ppp_debug("recv [sstp HTTP request <%s>]\n", line); + log_sstp_info2(conn, "recv [HTTP <%s>]\n", line); } while (*line); - if (send_http_response(conn, proto, "200 OK", + if (http_send_response(conn, proto, "200 OK", "Content-Length: 18446744073709551615\r\n")) { goto error; } + conn->sstp_state = STATE_SERVER_CONNECT_REQUEST_PENDING; return pos; @@ -212,55 +569,70 @@ error: return -1; } +static int http_handler(struct sstp_conn_t *conn, struct buffer_t *buf) +{ + int n; + + n = http_recv_request(conn); + if (n < 0) + return -1; + buf_pull(buf, n); + + if (conn->sstp_state == STATE_SERVER_CONNECT_REQUEST_PENDING) + conn->handler = sstp_handler; + + return n; +} + /* ppp */ static int ppp_allocate_pty(int *master, int *slave, int flags) { struct termios tios; - char pty_name[16]; - int value, mfd, sfd = -1; + int value, mfd, sfd; - 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)); + if (openpty(&mfd, &sfd, NULL, &tios, NULL) < 0) { + log_ppp_error("sstp: allocate pty: %s\n", 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; + 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; } - 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)); + 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 (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)); +#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; } - 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)); +// value = N_HDLC; +// if (ioctl(mfd, TIOCSETD, &value) < 0) { +// log_ppp_error("sstp: ppp: set pty line discipline: %s\n", strerror(errno)); +// goto error; +// } + + 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; } @@ -269,16 +641,23 @@ static int ppp_allocate_pty(int *master, int *slave, int flags) return 0; error: - if (mfd >= 0) - close(mfd); - if (sfd >= 0) - close(sfd); + close(mfd); + close(sfd); return -1; } static void ppp_started(struct ap_session *ses) { - log_ppp_debug("sstp: ppp started\n"); + 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) @@ -286,75 +665,176 @@ 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); + 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]; int n; +#if !PPP_SYNC + uint8_t *src, byte; + int i; - hdr = conn->ppp_buf ? : mempool_alloc(data_pool); - if (!hdr) { - log_error("sstp: no memory\n"); - return -1; - } + 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; + } -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; + switch (conn->sstp_state) { + case STATE_SERVER_CALL_CONNECTED_PENDING: + case STATE_SERVER_CALL_CONNECTED: + break; + default: + continue; } - 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; +#if PPP_SYNC + buf = alloc_buf(n + sizeof(*hdr)); + if (!buf) { + log_ppp_error("sstp: ppp: no memory\n"); + goto drop; + } + hdr = buf_put(buf, sizeof(*hdr)); + buf_put_data(buf, pppbuf, n); + INIT_SSTP_DATA_HDR(hdr, buf->len); + sstp_queue(conn, buf); +#else + src = pppbuf; + 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; - n += sizeof(*hdr); - INIT_SSTP_DATA_HDR(hdr, n); + while (!list_empty(&conn->ppp_queue)) { + buf = list_first_entry(&conn->ppp_queue, typeof(*buf), entry); - if (sstp_send(conn, hdr, n)) - goto drop; + if (buf_headroom(buf) > 0) + triton_md_disable_handler(h, MD_MODE_WRITE); - conn->ppp_buf = NULL; + 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: - conn->ppp_buf = hdr; + 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) @@ -367,262 +847,182 @@ static void sstp_ctx_switch(struct triton_context_t *ctx, void *arg) 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) +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 = mempool_alloc(data_pool); + } __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"); + log_sstp_ppp_info2(conn, "send [SSTP_MSG_CALL_CONNECT_ACK]\n"); - if (!msg) { - log_error("sstp: no memory\n"); + if (!buf) { + log_sstp_error(conn, "no memory\n"); return -1; } + msg = buf_put(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; //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)); + return sstp_send(conn, buf); } -static int send_sstp_msg_call_connect_nak(struct sstp_conn_t *conn) +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 = mempool_alloc(data_pool); + } __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"); + log_sstp_ppp_info2(conn, "send [SSTP_MSG_CALL_CONNECT_NAK]\n"); - if (!msg) { - log_error("sstp: no memory\n"); + if (!buf) { + log_sstp_error(conn, "no memory\n"); return -1; } + msg = buf_put(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, msg, sizeof(*msg)); + return sstp_send(conn, buf); } -static int send_sstp_msg_call_abort(struct sstp_conn_t *conn) +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 = mempool_alloc(data_pool); + } __attribute__((packed)) *msg; + struct buffer_t *buf = alloc_buf(sizeof(*msg)); if (conf_verbose) - log_ppp_info2("send [sstp SSTP_MSG_CALL_ABORT]\n"); + log_sstp_ppp_info2(conn, "send [SSTP_MSG_CALL_ABORT]\n"); - if (!msg) { - log_error("sstp: no memory\n"); + if (!buf) { + log_sstp_error(conn, "no memory\n"); return -1; } + msg = buf_put(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); - 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; + return sstp_send(conn, buf); } -static int send_sstp_msg_call_disconnect(struct sstp_conn_t *conn) +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 = mempool_alloc(data_pool); + } __attribute__((packed)) *msg; + struct buffer_t *buf = alloc_buf(sizeof(*msg)); if (conf_verbose) - log_ppp_info2("send [sstp SSTP_MSG_CALL_DISCONNECT]\n"); + log_sstp_ppp_info2(conn, "send [SSTP_MSG_CALL_DISCONNECT]\n"); - if (!msg) { - log_error("sstp: no memory\n"); + if (!buf) { + log_sstp_error(conn, "no memory\n"); return -1; } + msg = buf_put(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); - 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; + return sstp_send(conn, buf); } -static int send_sstp_msg_call_disconnect_ack(struct sstp_conn_t *conn) +static int sstp_send_msg_call_disconnect_ack(struct sstp_conn_t *conn) { struct { struct sstp_ctrl_hdr hdr; - } __attribute__((packed)) *msg = mempool_alloc(data_pool); + } __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"); + log_sstp_ppp_info2(conn, "send [SSTP_MSG_CALL_DISCONNECT_ACK]\n"); - if (!msg) { - log_error("sstp: no memory\n"); + if (!buf) { + log_sstp_error(conn, "no memory\n"); return -1; } + msg = buf_put(buf, sizeof(*msg)); INIT_SSTP_CTRL_HDR(&msg->hdr, SSTP_MSG_CALL_DISCONNECT_ACK, 0, sizeof(*msg)); - return sstp_send(conn, msg, sizeof(*msg)); + return sstp_send(conn, buf); } -static int send_sstp_msg_echo_request(struct sstp_conn_t *conn) +static int sstp_send_msg_echo_request(struct sstp_conn_t *conn) { struct { struct sstp_ctrl_hdr hdr; - } __attribute__((packed)) *msg = mempool_alloc(data_pool); + } __attribute__((packed)) *msg; + struct buffer_t *buf = alloc_buf(sizeof(*msg)); if (conf_verbose) - log_ppp_info2("send [sstp SSTP_MSG_ECHO_REQUEST]\n"); + log_sstp_ppp_info2(conn, "send [SSTP_MSG_ECHO_REQUEST]\n"); - if (!msg) { - log_error("sstp: no memory\n"); + if (!buf) { + log_sstp_error(conn, "no memory\n"); return -1; } + msg = buf_put(buf, sizeof(*msg)); INIT_SSTP_CTRL_HDR(&msg->hdr, SSTP_MSG_ECHO_REQUEST, 0, sizeof(*msg)); - return sstp_send(conn, msg, sizeof(*msg)); + return sstp_send(conn, buf); } -static int send_sstp_msg_echo_response(struct sstp_conn_t *conn) +static int sstp_send_msg_echo_response(struct sstp_conn_t *conn) { struct { struct sstp_ctrl_hdr hdr; - } __attribute__((packed)) *msg = mempool_alloc(data_pool); + } __attribute__((packed)) *msg; + struct buffer_t *buf = alloc_buf(sizeof(*msg)); if (conf_verbose) - log_ppp_info2("send [sstp SSTP_MSG_ECHO_RESPONSE]\n"); + log_sstp_ppp_info2(conn, "send [SSTP_MSG_ECHO_RESPONSE]\n"); - if (!msg) { - log_error("sstp: no memory\n"); + if (!buf) { + log_sstp_error(conn, "no memory\n"); return -1; } + msg = buf_put(buf, sizeof(*msg)); INIT_SSTP_CTRL_HDR(&msg->hdr, SSTP_MSG_ECHO_RESPONSE, 0, sizeof(*msg)); - return sstp_send(conn, msg, sizeof(*msg)); + return sstp_send(conn, buf); } -static int sstp_msg_call_connect_request(struct sstp_conn_t *conn) +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 *)conn->in_buf; + } __attribute__((packed)) *msg = (void *)hdr; int master, slave; if (conf_verbose) - log_ppp_info2("recv [sstp SSTP_MSG_CALL_CONNECT_REQUEST]\n"); + log_sstp_ppp_info2(conn, "recv [SSTP_MSG_CALL_CONNECT_REQUEST]\n"); switch (conn->sstp_state) { case STATE_CALL_ABORT_TIMEOUT_PENDING: @@ -633,31 +1033,21 @@ static int sstp_msg_call_connect_request(struct sstp_conn_t *conn) 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; + 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)) { - conn->sstp_state = STATE_CALL_ABORT_IN_PROGRESS_1; - if (send_sstp_msg_call_abort(conn)) - return -1; - return 0; + return sstp_abort(conn, 0); } if (ntohs(msg->attr.protocol_id) != SSTP_ENCAPSULATED_PROTOCOL_PPP) { if (conn->nak_sent++ == 3) { - log_ppp_warn("sstp: nak limit reached\n"); - conn->sstp_state = STATE_CALL_ABORT_IN_PROGRESS_1; - if (send_sstp_msg_call_abort(conn)) - return -1; - } else - if (send_sstp_msg_call_connect_nak(conn)) - return -1; - return 0; + log_sstp_ppp_warn(conn, "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) @@ -665,47 +1055,41 @@ static int sstp_msg_call_connect_request(struct sstp_conn_t *conn) 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); +// triton_event_fire(EV_CTRL_STARTED, &conn->ppp.ses); - if (send_sstp_msg_call_connect_ack(conn)) + 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->state = STATE_FIN; + conn->ppp_state = STATE_FINISHED; 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); + triton_md_unregister_handler(&conn->ppp_hnd, 1); close(slave); return -1; } -static int sstp_msg_call_connected(struct sstp_conn_t *conn) +static int sstp_recv_msg_call_connected(struct sstp_conn_t *conn) { if (conf_verbose) - log_ppp_info2("recv [sstp SSTP_MSG_CALL_CONNECTED]\n"); + log_sstp_ppp_info2(conn, "recv [SSTP_MSG_CALL_CONNECTED]\n"); switch (conn->sstp_state) { case STATE_CALL_ABORT_TIMEOUT_PENDING: @@ -716,150 +1100,155 @@ static int sstp_msg_call_connected(struct sstp_conn_t *conn) 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; + sstp_abort(conn, 0); 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) + 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_msg_call_abort(struct sstp_conn_t *conn) +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"); + log_sstp_ppp_info2(conn, "recv [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; + return 0; default: conn->sstp_state = STATE_CALL_ABORT_IN_PROGRESS_2; - if (send_sstp_msg_call_abort(conn)) - return -1; + ret = sstp_send_msg_call_abort(conn); break; } - return 0; + 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_msg_call_disconnect(struct sstp_conn_t *conn) +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"); + log_sstp_ppp_info2(conn, "recv [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; + return 0; 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; + if (conn->timeout_timer.tpd) + triton_timer_del(&conn->timeout_timer); break; } - return 0; + 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_msg_call_disconnect_ack(struct sstp_conn_t *conn) +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"); + log_sstp_ppp_info2(conn, "recv [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; + return 0; default: - conn->sstp_state = STATE_CALL_ABORT_IN_PROGRESS_1; - if (send_sstp_msg_call_abort(conn)) - return -1; - break; + 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_msg_echo_request(struct sstp_conn_t *conn) +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"); + log_sstp_ppp_info2(conn, "recv [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; + return 0; default: - conn->sstp_state = STATE_CALL_ABORT_IN_PROGRESS_1; - if (send_sstp_msg_call_abort(conn)) - return -1; - break; + return sstp_abort(conn, 0); } - return 0; + return sstp_send_msg_echo_response(conn); } -static int sstp_msg_echo_response(struct sstp_conn_t *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"); + log_sstp_ppp_info2(conn, "recv [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; + return 0; default: - conn->sstp_state = STATE_CALL_ABORT_IN_PROGRESS_1; - if (send_sstp_msg_call_abort(conn)) - return -1; - break; + return sstp_abort(conn, 0); } + conn->hello_sent = 0; return 0; } -static int sstp_data_packet(struct sstp_conn_t *conn) +static int sstp_recv_data_packet(struct sstp_conn_t *conn, struct sstp_hdr *hdr) { - struct sstp_hdr *hdr = (struct sstp_hdr *)conn->in_buf; - int n, pos, size; + 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: @@ -869,62 +1258,119 @@ static int sstp_data_packet(struct sstp_conn_t *conn) 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; +#if PPP_SYNC + buf = alloc_buf(size); + if (!buf) { + log_sstp_error(conn, "no memory\n"); + return -1; } - return 0; + buf_put_data(buf, hdr->data, size); +#else + buf = alloc_buf(size*2 + 2 + PPP_FCSLEN); + if (!buf) { + log_sstp_error(conn, "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_packet(struct sstp_conn_t *conn) +static int sstp_recv_packet(struct sstp_conn_t *conn, struct sstp_hdr *hdr) { - struct sstp_ctrl_hdr *hdr = (struct sstp_ctrl_hdr *)conn->in_buf; + struct sstp_ctrl_hdr *msg = (struct sstp_ctrl_hdr *)hdr; switch (hdr->reserved) { case SSTP_DATA_PACKET: - return sstp_data_packet(conn); + return sstp_recv_data_packet(conn, hdr); case SSTP_CTRL_PACKET: - break; - default: - log_ppp_warn("recv [sstp Unknown packet type %02x]\n", hdr->reserved); + if (ntohs(hdr->length) >= sizeof(*msg)) + break; + log_sstp_ppp_error(conn, "recv [SSTP too short message]\n"); return -1; + default: + log_sstp_ppp_warn(conn, "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(hdr->message_type)) { + switch (ntohs(msg->message_type)) { case SSTP_MSG_CALL_CONNECT_REQUEST: - return sstp_msg_call_connect_request(conn); + return sstp_recv_msg_call_connect_request(conn, msg); case SSTP_MSG_CALL_CONNECT_ACK: case SSTP_MSG_CALL_CONNECT_NAK: - break; + return sstp_abort(conn, 0); case SSTP_MSG_CALL_CONNECTED: - return sstp_msg_call_connected(conn); + return sstp_recv_msg_call_connected(conn); case SSTP_MSG_CALL_ABORT: - return sstp_msg_call_abort(conn); + return sstp_recv_msg_call_abort(conn); case SSTP_MSG_CALL_DISCONNECT: - return sstp_msg_call_disconnect(conn); + return sstp_recv_msg_call_disconnect(conn); case SSTP_MSG_CALL_DISCONNECT_ACK: - return sstp_msg_call_disconnect_ack(conn); + return sstp_recv_msg_call_disconnect_ack(conn); case SSTP_MSG_ECHO_REQUEST: - return sstp_msg_echo_request(conn); + return sstp_recv_msg_echo_request(conn); case SSTP_MSG_ECHO_RESPONSE: - return sstp_msg_echo_response(conn); + return sstp_recv_msg_echo_response(conn); default: - log_ppp_warn("recv [sstp Unknown message type %04x]\n", ntohs(hdr->message_type)); + log_sstp_ppp_warn(conn, "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_sstp_ppp_error(conn, "recv [SSTP invalid version]\n"); + return -1; + } + + n = ntohs(hdr->length); + if (n > SSTP_MAX_PACKET_SIZE) { + log_sstp_ppp_error(conn, "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; } @@ -932,95 +1378,33 @@ static int sstp_packet(struct sstp_conn_t *conn) 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; - } - } + struct buffer_t *buf = conn->in; + int n; - if (n == 0) { + 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); - 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); - } + n = conn->handler(conn, buf); + if (n < 0) + goto drop; - 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); - } - } + buf_expand_tail(buf, SSTP_MAX_PACKET_SIZE); } + return 0; + drop: sstp_disconnect(conn); return 1; @@ -1029,121 +1413,73 @@ drop: 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; + struct buffer_t *buf; 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; + 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) - goto again; + continue; if (errno == EAGAIN) - n = 0; - else { - if (conf_verbose && errno != EPIPE) - log_ppp_info2("sstp: write: %s\n", strerror(errno)); - goto drop; - } - } + 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 (n == 0) + if (buf->len) { + triton_md_enable_handler(h, MD_MODE_WRITE); break; + } - list_del(&pack->entry); - mempool_free(pack->data); - mempool_free(pack); + list_del(&buf->entry); + free_buf(buf); } - - if (list_empty(&conn->send_queue)) - triton_md_disable_handler(h, MD_MODE_WRITE); - return 0; drop: - sstp_disconnect(conn); + triton_context_call(&conn->ctx, (triton_event_func)sstp_disconnect, conn); return 1; } -static int sstp_send(struct sstp_conn_t *conn, void *data, int size) +static inline void sstp_queue(struct sstp_conn_t *conn, struct buffer_t *buf) { - 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); + list_add_tail(&buf->entry, &conn->out_queue); +} - return 0; +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"); - conn->sstp_state = STATE_CALL_ABORT_IN_PROGRESS_1; - send_sstp_msg_call_abort(conn); + sstp_abort(conn, 0); } else - send_sstp_msg_echo_request(conn); + sstp_send_msg_echo_request(conn); break; } } @@ -1152,16 +1488,17 @@ 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: - sstp_disconnect(conn); + triton_context_call(&conn->ctx, (triton_event_func)sstp_disconnect, conn); break; default: - conn->sstp_state = STATE_CALL_ABORT_IN_PROGRESS_1; - send_sstp_msg_call_abort(conn); + sstp_abort(conn, 0); break; } } @@ -1170,27 +1507,104 @@ 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; + 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); - 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); + 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_sstp_ppp_debug(conn, "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); + + 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); + +#ifdef CRYPTO_OPENSSL + if (conn->ssl_ctx) + SSL_CTX_free(conn->ssl_ctx); +#endif + + mempool_free(conn); + + log_info2("sstp: disconnected\n"); } -static void sstp_starting(struct sstp_conn_t *conn) +static void sstp_start(struct sstp_conn_t *conn) { - log_ppp_debug("sstp: starting\n"); + log_sstp_debug(conn, "start\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)); + log_sstp_error(conn, "SSL_CTX error: %s\n", ERR_error_string(ERR_get_error(), NULL)); goto error; } @@ -1198,48 +1612,45 @@ static void sstp_starting(struct sstp_conn_t *conn) 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)); + log_sstp_error(conn, "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; + log_sstp_error(conn, "SSL ca file error: %s\n", ERR_error_string(ERR_get_error(), NULL)); + goto error; } 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)); + log_sstp_error(conn, "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_CTX_set_mode(conn->ssl_ctx, SSL_CTX_get_mode(conn->ssl_ctx) | + SSL_MODE_ACCEPT_MOVING_WRITE_BUFFER | SSL_MODE_ENABLE_PARTIAL_WRITE); - 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; - } - } + conn->stream = ssl_stream_init(conn->hnd.fd, conn->ssl_ctx); + } else #endif + conn->stream = stream_init(conn->hnd.fd); + if (!conn->stream) { + log_sstp_error(conn, "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); - triton_timer_add(&conn->ctx, &conn->timeout_timer, 0); + + log_sstp_info2(conn, "started\n"); +// triton_event_fire(EV_CTRL_STARTING, &conn->ppp.ses); return; -#ifdef CRYPTO_OPENSSL error: sstp_disconnect(conn); -#endif } static int sstp_connect(struct triton_md_handler_t *h) @@ -1249,7 +1660,7 @@ static int sstp_connect(struct triton_md_handler_t *h) socklen_t size = sizeof(addr); int sock, value; - while(1) { + while (1) { sock = accept(h->fd, (struct sockaddr *)&addr, &size); if (sock < 0) { if (errno == EAGAIN) @@ -1268,7 +1679,8 @@ static int sstp_connect(struct triton_md_handler_t *h) return 0; } - log_info2("sstp: new connection from %s\n", inet_ntoa(addr.sin_addr)); + + 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"); @@ -1282,6 +1694,13 @@ static int sstp_connect(struct triton_md_handler_t *h) 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)); @@ -1301,14 +1720,19 @@ static int sstp_connect(struct triton_md_handler_t *h) 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->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->in_buf = _malloc(SSTP_MAX_PACKET_SIZE); - INIT_LIST_HEAD(&conn->send_queue); + 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; @@ -1320,9 +1744,10 @@ static int sstp_connect(struct triton_md_handler_t *h) 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); + 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); @@ -1335,39 +1760,25 @@ static int sstp_connect(struct triton_md_handler_t *h) conn->ppp.ses.ifname_rename = _strdup(conf_ifname); triton_context_register(&conn->ctx, &conn->ppp.ses); - triton_md_register_handler(&conn->ctx, &conn->hnd); + triton_context_call(&conn->ctx, (triton_event_func)sstp_start, conn); 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); + 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 *s = container_of(ctx, typeof(*s), ctx); + struct sstp_serv_t *serv = container_of(ctx, typeof(*serv), ctx); - triton_md_unregister_handler(&s->hnd, 1); + triton_md_unregister_handler(&serv->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; +#ifdef CRYPTO_OPENSSL + CRYPTO_thread_cleanup(); +#endif } static void load_config(void) @@ -1428,11 +1839,6 @@ 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)); @@ -1475,9 +1881,13 @@ static void sstp_init(void) return; } +#ifdef CRYPTO_OPENSSL + CRYPTO_thread_setup(); + SSL_load_error_strings(); + SSL_library_init(); +#endif + 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(); @@ -1486,8 +1896,6 @@ static void sstp_init(void) 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); } diff --git a/accel-pppd/ctrl/sstp/sstp_prot.h b/accel-pppd/ctrl/sstp/sstp_prot.h index 54acaf4..0e5785f 100644 --- a/accel-pppd/ctrl/sstp/sstp_prot.h +++ b/accel-pppd/ctrl/sstp/sstp_prot.h @@ -73,11 +73,6 @@ enum { }; /* 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, -- cgit v1.2.3