diff --git a/src/Cedar/Proto.c b/src/Cedar/Proto.c index 84379e3a..23ad8c0b 100644 --- a/src/Cedar/Proto.c +++ b/src/Cedar/Proto.c @@ -133,6 +133,8 @@ PROTO *ProtoNew(CEDAR *cedar) // OpenVPN ProtoImplAdd(proto, OvsGetProtoImpl()); + // SSTP + ProtoImplAdd(proto, SstpGetProtoImpl()); proto->UdpListener = NewUdpListener(ProtoHandleDatagrams, proto, &cedar->Server->ListenIP); diff --git a/src/Cedar/Proto_SSTP.c b/src/Cedar/Proto_SSTP.c index 63c2666c..001bab7d 100644 --- a/src/Cedar/Proto_SSTP.c +++ b/src/Cedar/Proto_SSTP.c @@ -16,6 +16,171 @@ bool GetNoSstp() return g_no_sstp; } +PROTO_IMPL *SstpGetProtoImpl() +{ + static PROTO_IMPL impl = + { + SstpInit, + SstpFree, + SstpName, + NULL, + SstpProcessData, + NULL + }; + + return &impl; +} + +bool SstpInit(void **param, struct CEDAR *cedar, INTERRUPT_MANAGER *im, SOCK_EVENT *se, const char *cipher, const char *hostname) +{ + if (param == NULL || cedar == NULL || im == NULL || se == NULL) + { + return false; + } + + Debug("SstpInit(): cipher: %s, hostname: %s\n", cipher, hostname); + + *param = NewSstpServer(cedar, im, se, cipher, hostname); + + return true; +} + +void SstpFree(void *param) +{ + FreeSstpServer(param); +} + +char *SstpName() +{ + return "SSTP"; +} + +bool SstpProcessData(void *param, TCP_RAW_DATA *in, FIFO *out) +{ + FIFO *recv_fifo; + bool disconnected = false; + SSTP_SERVER *server = param; + + if (server == NULL || in == NULL || out == NULL) + { + return false; + } + + if (server->Status == SSTP_SERVER_STATUS_NOT_INITIALIZED) + { + HTTP_HEADER *header; + char *header_str, date_str[MAX_SIZE]; + + GetHttpDateStr(date_str, sizeof(date_str), SystemTime64()); + + header = NewHttpHeader("HTTP/1.1", "200", "OK"); + AddHttpValue(header, NewHttpValue("Content-Length", "18446744073709551615")); + AddHttpValue(header, NewHttpValue("Server", "Microsoft-HTTPAPI/2.0")); + AddHttpValue(header, NewHttpValue("Date", date_str)); + + header_str = HttpHeaderToStr(header); + + FreeHttpHeader(header); + + if (header_str == NULL) + { + return false; + } + + WriteFifo(out, header_str, StrLen(header_str)); + + Free(header_str); + + Copy(&server->ClientIp, &in->SrcIP, sizeof(server->ClientIp)); + server->ClientPort = in->SrcPort; + Copy(&server->ServerIp, &in->DstIP, sizeof(server->ServerIp)); + server->ServerPort = in->DstPort; + + server->Status = SSTP_SERVER_STATUS_REQUEST_PENGING; + + return true; + } + + recv_fifo = in->Data; + + while (recv_fifo->size >= 4) + { + UCHAR *first4; + bool ok = false; + UINT read_size = 0; + + // Read 4 bytes from the beginning of the received queue. + first4 = ((UCHAR *)recv_fifo->p) + recv_fifo->pos; + if (first4[0] == SSTP_VERSION_1) + { + const USHORT len = READ_USHORT(first4 + 2) & 0xFFF; + if (len >= 4) + { + ok = true; + + if (recv_fifo->size >= len) + { + UCHAR *data; + BLOCK *b; + + read_size = len; + data = Malloc(read_size); + + ReadFifo(recv_fifo, data, read_size); + + b = NewBlock(data, read_size, 0); + + InsertQueue(server->RecvQueue, b); + } + } + } + + if (read_size == 0) + { + break; + } + + if (ok == false) + { + // Bad packet received, trigger disconnection. + disconnected = true; + break; + } + } + + // Process the timer interrupt + SstpProcessInterrupt(server); + + if (server->Disconnected) + { + disconnected = true; + } + + while (true) + { + BLOCK *b = GetNext(server->SendQueue); + if (b == NULL) + { + break; + } + + // Discard the data block if the transmission queue's size is greater than ~2.5 MB. + if (b->PriorityQoS || (FifoSize(out) <= MAX_BUFFERING_PACKET_SIZE)) + { + WriteFifo(out, b->Buf, b->Size); + } + + FreeBlock(b); + } + + if (disconnected) + { + return false; + } + + return true; +} + // Process the SSTP control packet reception void SstpProcessControlPacket(SSTP_SERVER *s, SSTP_PACKET *p) { @@ -829,39 +994,28 @@ void SstpFreePacket(SSTP_PACKET *p) } // Create a SSTP server -SSTP_SERVER *NewSstpServer(CEDAR *cedar, IP *client_ip, UINT client_port, IP *server_ip, - UINT server_port, SOCK_EVENT *se, - char *client_host_name, char *crypt_name) +SSTP_SERVER *NewSstpServer(CEDAR *cedar, INTERRUPT_MANAGER *im, SOCK_EVENT *se, const char *cipher, const char *hostname) { SSTP_SERVER *s = ZeroMalloc(sizeof(SSTP_SERVER)); - s->LastRecvTick = Tick64(); + s->Status = SSTP_SERVER_STATUS_NOT_INITIALIZED; - StrCpy(s->ClientHostName, sizeof(s->ClientHostName), client_host_name); - StrCpy(s->ClientCipherName, sizeof(s->ClientCipherName), crypt_name); + s->Now = Tick64(); + s->LastRecvTick = s->Now; s->Cedar = cedar; - AddRef(s->Cedar->ref); + s->Interrupt = im; + s->SockEvent = se; + + StrCpy(s->ClientHostName, sizeof(s->ClientHostName), hostname); + StrCpy(s->ClientCipherName, sizeof(s->ClientCipherName), cipher); NewTubePair(&s->TubeSend, &s->TubeRecv, 0); SetTubeSockEvent(s->TubeSend, se); - s->Now = Tick64(); - - Copy(&s->ClientIp, client_ip, sizeof(IP)); - s->ClientPort = client_port; - Copy(&s->ServerIp, server_ip, sizeof(IP)); - s->ServerPort = server_port; - - s->SockEvent = se; - - AddRef(s->SockEvent->ref); - s->RecvQueue = NewQueueFast(); s->SendQueue = NewQueueFast(); - s->Interrupt = NewInterruptManager(); - return s; } @@ -907,242 +1061,8 @@ void FreeSstpServer(SSTP_SERVER *s) ReleaseQueue(s->RecvQueue); ReleaseQueue(s->SendQueue); - ReleaseSockEvent(s->SockEvent); - - FreeInterruptManager(s->Interrupt); - - ReleaseCedar(s->Cedar); - ReleaseTube(s->TubeSend); ReleaseTube(s->TubeRecv); Free(s); } - -// Handle the communication of SSTP protocol -bool ProcessSstpHttps(CEDAR *cedar, SOCK *s, SOCK_EVENT *se) -{ - UINT tmp_size = 65536; - UCHAR *tmp_buf; - FIFO *recv_fifo; - FIFO *send_fifo; - SSTP_SERVER *sstp; - bool ret = false; - // Validate arguments - if (cedar == NULL || s == NULL || se == NULL) - { - return false; - } - - tmp_buf = Malloc(tmp_size); - recv_fifo = NewFifo(); - send_fifo = NewFifo(); - - sstp = NewSstpServer(cedar, &s->RemoteIP, s->RemotePort, &s->LocalIP, s->LocalPort, se, - s->RemoteHostname, s->CipherName); - - while (true) - { - UINT r; - bool is_disconnected = false; - bool state_changed = false; - - // Receive data over SSL - while (true) - { - r = Recv(s, tmp_buf, tmp_size, true); - if (r == 0) - { - // SSL is disconnected - is_disconnected = true; - break; - } - else if (r == SOCK_LATER) - { - // Data is not received any more - break; - } - else - { - // Queue the received data - WriteFifo(recv_fifo, tmp_buf, r); - state_changed = true; - } - } - - while (recv_fifo->size >= 4) - { - UCHAR *first4; - UINT read_size = 0; - bool ok = false; - // Read 4 bytes from the beginning of the receive queue - first4 = ((UCHAR *)recv_fifo->p) + recv_fifo->pos; - if (first4[0] == SSTP_VERSION_1) - { - USHORT len = READ_USHORT(first4 + 2) & 0xFFF; - if (len >= 4) - { - ok = true; - - if (recv_fifo->size >= len) - { - UCHAR *data; - BLOCK *b; - - read_size = len; - data = Malloc(read_size); - - ReadFifo(recv_fifo, data, read_size); - - b = NewBlock(data, read_size, 0); - - InsertQueue(sstp->RecvQueue, b); - } - } - } - - if (read_size == 0) - { - break; - } - - if (ok == false) - { - // Disconnect the connection since a bad packet received - is_disconnected = true; - break; - } - } - - // Process the timer interrupt - SstpProcessInterrupt(sstp); - - if (sstp->Disconnected) - { - is_disconnected = true; - } - - // Put the transmission data that SSTP module has generated into the transmission queue - while (true) - { - BLOCK *b = GetNext(sstp->SendQueue); - - if (b == NULL) - { - break; - } - - // When transmit a data packet, If there are packets of more than about - // 2.5 MB in the transmission queue of the TCP, discard without transmission - if (b->PriorityQoS || (send_fifo->size <= MAX_BUFFERING_PACKET_SIZE)) - { - WriteFifo(send_fifo, b->Buf, b->Size); - } - - FreeBlock(b); - } - - // Data is transmitted over SSL - while (send_fifo->size != 0) - { - r = Send(s, ((UCHAR *)send_fifo->p) + send_fifo->pos, send_fifo->size, true); - if (r == 0) - { - // SSL is disconnected - is_disconnected = true; - break; - } - else if (r == SOCK_LATER) - { - // Can not send any more - break; - } - else - { - // Advance the transmission queue by the amount of the transmitted - ReadFifo(send_fifo, NULL, r); - state_changed = true; - } - } - - if (is_disconnected) - { - // Disconnected - break; - } - - // Wait for the next state change - if (state_changed == false) - { - UINT select_time = SELECT_TIME; - UINT r = GetNextIntervalForInterrupt(sstp->Interrupt); - WaitSockEvent(se, MIN(r, select_time)); - } - } - - if (sstp != NULL && sstp->EstablishedCount >= 1) - { - ret = true; - } - - FreeSstpServer(sstp); - - ReleaseFifo(recv_fifo); - ReleaseFifo(send_fifo); - Free(tmp_buf); - - YieldCpu(); - Disconnect(s); - - return ret; -} - -// Accept the SSTP connection -bool AcceptSstp(CONNECTION *c) -{ - SOCK *s; - HTTP_HEADER *h; - char date_str[MAX_SIZE]; - bool ret; - bool ret2 = false; - SOCK_EVENT *se; - // Validate arguments - if (c == NULL) - { - return false; - } - - s = c->FirstSock; - - GetHttpDateStr(date_str, sizeof(date_str), SystemTime64()); - - // Return a response - h = NewHttpHeader("HTTP/1.1", "200", "OK"); - AddHttpValue(h, NewHttpValue("Content-Length", "18446744073709551615")); - AddHttpValue(h, NewHttpValue("Server", "Microsoft-HTTPAPI/2.0")); - AddHttpValue(h, NewHttpValue("Date", date_str)); - - ret = PostHttp(s, h, NULL, 0); - - FreeHttpHeader(h); - - if (ret) - { - SetTimeout(s, INFINITE); - - se = NewSockEvent(); - - JoinSockToSockEvent(s, se); - - Debug("ProcessSstpHttps Start.\n"); - ret2 = ProcessSstpHttps(c->Cedar, s, se); - Debug("ProcessSstpHttps End.\n"); - - ReleaseSockEvent(se); - } - - Disconnect(s); - - return ret2; -} - diff --git a/src/Cedar/Proto_SSTP.h b/src/Cedar/Proto_SSTP.h index 8ef8e424..ccec28c7 100644 --- a/src/Cedar/Proto_SSTP.h +++ b/src/Cedar/Proto_SSTP.h @@ -61,6 +61,7 @@ #define SSTP_SERVER_STATUS_REQUEST_PENGING 0 // Connection incomplete #define SSTP_SERVER_STATUS_CONNECTED_PENDING 1 // Connection completed. Authentication incomplete #define SSTP_SERVER_STATUS_ESTABLISHED 2 // Connection completed. Communication available +#define SSTP_SERVER_STATUS_NOT_INITIALIZED INFINITE // Connection not accepted yet. // Length of Nonce #define SSTP_NONCE_SIZE 32 // 256 bits @@ -121,12 +122,13 @@ struct SSTP_SERVER //// Function prototype -bool AcceptSstp(CONNECTION *c); -bool ProcessSstpHttps(CEDAR *cedar, SOCK *s, SOCK_EVENT *se); +PROTO_IMPL *SstpGetProtoImpl(); +bool SstpInit(void **param, struct CEDAR *cedar, INTERRUPT_MANAGER *im, SOCK_EVENT *se, const char *cipher, const char *hostname); +void SstpFree(void *param); +char *SstpName(); +bool SstpProcessData(void *param, TCP_RAW_DATA *in, FIFO *out); -SSTP_SERVER *NewSstpServer(CEDAR *cedar, IP *client_ip, UINT client_port, IP *server_ip, - UINT server_port, SOCK_EVENT *se, - char *client_host_name, char *crypt_name); +SSTP_SERVER *NewSstpServer(CEDAR *cedar, INTERRUPT_MANAGER *im, SOCK_EVENT *se, const char *cipher, const char *hostname); void FreeSstpServer(SSTP_SERVER *s); void SstpProcessInterrupt(SSTP_SERVER *s); SSTP_PACKET *SstpParsePacket(UCHAR *data, UINT size); diff --git a/src/Cedar/Protocol.c b/src/Cedar/Protocol.c index 4dd2bd62..a34515cb 100644 --- a/src/Cedar/Protocol.c +++ b/src/Cedar/Protocol.c @@ -5891,7 +5891,7 @@ bool ServerDownloadSignature(CONNECTION *c, char **error_detail_str) // Accept the SSTP connection c->Type = CONNECTION_TYPE_OTHER; - sstp_ret = AcceptSstp(c); + sstp_ret = ProtoHandleConnection(server->Proto, s, "SSTP"); c->Err = ERR_DISCONNECTED; FreeHttpHeader(h);