From 7d58e6bf60a65af1f00b43155406a8ceb497df51 Mon Sep 17 00:00:00 2001 From: Davide Beatrici Date: Fri, 26 Jul 2019 08:36:54 +0200 Subject: [PATCH 1/2] Add interface for easy protocol implementation This commit adds a protocol interface to the server, its purpose is to manage TCP connections and the various third-party protocols. More specifically, ProtoHandleConnection() takes care of exchanging the packets between the local and remote endpoint; the protocol implementation only has to parse them and act accordingly. The interface knows which protocol is the connection for by calling IsPacketForMe(), a function implemented for each protocol. --- src/Cedar/Cedar.c | 6 + src/Cedar/Cedar.h | 4 +- src/Cedar/Cedar.vcproj | 8 ++ src/Cedar/Connection.c | 79 ++---------- src/Cedar/Proto.c | 271 +++++++++++++++++++++++++++++++++++++++++ src/Cedar/Proto.h | 44 +++++++ src/Cedar/Protocol.c | 2 +- src/Mayaqua/MayaType.h | 1 + src/Mayaqua/Network.c | 34 ++++++ src/Mayaqua/Network.h | 12 ++ 10 files changed, 392 insertions(+), 69 deletions(-) create mode 100644 src/Cedar/Proto.c create mode 100644 src/Cedar/Proto.h diff --git a/src/Cedar/Cedar.c b/src/Cedar/Cedar.c index c4a6f56a..b8aa5254 100644 --- a/src/Cedar/Cedar.c +++ b/src/Cedar/Cedar.c @@ -1606,6 +1606,9 @@ void InitCedar() // Initialize protocol module InitProtocol(); + + // Initialize third-party protocol interface + ProtoInit(); } // Free Cedar communication module @@ -1616,6 +1619,9 @@ void FreeCedar() return; } + // Free third-party protocol interface + ProtoFree(); + // Free protocol module FreeProtocol(); } diff --git a/src/Cedar/Cedar.h b/src/Cedar/Cedar.h index f9b6b320..50bd61ae 100644 --- a/src/Cedar/Cedar.h +++ b/src/Cedar/Cedar.h @@ -305,8 +305,7 @@ #define CONNECTION_TYPE_ADMIN_RPC 5 // RPC for Management #define CONNECTION_TYPE_ENUM_HUB 6 // HUB enumeration #define CONNECTION_TYPE_PASSWORD 7 // Password change -#define CONNECTION_TYPE_SSTP 8 // SSTP -#define CONNECTION_TYPE_OPENVPN 9 // OpenVPN +#define CONNECTION_TYPE_OTHER 0xffffffff // E.g. Third-party protocol // Protocol #define CONNECTION_TCP 0 // TCP protocol @@ -1031,6 +1030,7 @@ typedef struct CEDAR // Layer-2/Layer-3 converter #include // Third party protocols +#include #include #include #include diff --git a/src/Cedar/Cedar.vcproj b/src/Cedar/Cedar.vcproj index 07d866f8..9faa83fc 100644 --- a/src/Cedar/Cedar.vcproj +++ b/src/Cedar/Cedar.vcproj @@ -650,6 +650,10 @@ RelativePath=".\NullLan.c" > + + @@ -1280,6 +1284,10 @@ RelativePath=".\NullLan.h" > + + diff --git a/src/Cedar/Connection.c b/src/Cedar/Connection.c index 71e2d0bb..ae8ddde4 100644 --- a/src/Cedar/Connection.c +++ b/src/Cedar/Connection.c @@ -2901,21 +2901,8 @@ void ConnectionAccept(CONNECTION *c) X *x; K *k; char tmp[128]; - UCHAR openssl_check_buf[2]; - char *error_details = NULL; - SERVER *server; - UCHAR *peek_buf = NULL; - UINT peek_buf_size = 1500; - char sni[256] = {0}; - bool native1 = false; - bool native2 = false; - bool native3 = false; - bool no_native = false; - UINT peek_size = 0; UINT initial_timeout = CONNECTING_TIMEOUT; - bool no_peek_log = false; UCHAR ctoken_hash[SHA1_SIZE]; - bool no_write_ctoken_log = false; // Validate arguments if (c == NULL) @@ -2925,13 +2912,7 @@ void ConnectionAccept(CONNECTION *c) Zero(ctoken_hash, sizeof(ctoken_hash)); - peek_buf = ZeroMalloc(peek_buf_size); - - Debug("ConnectionAccept()\n"); - - server = c->Cedar->Server; - - // get a socket + // Get a socket s = c->FirstSock; AddRef(s->ref); @@ -2945,37 +2926,18 @@ void ConnectionAccept(CONNECTION *c) initial_timeout += GetMachineRand() % (CONNECTING_TIMEOUT / 2); SetTimeout(s, initial_timeout); - - // Peek whether OpenSSL packet - if (s->IsReverseAcceptedSocket == false) + // Handle third-party protocols + if (s->IsReverseAcceptedSocket == false && s->Type == SOCK_TCP) { - if (s->Type == SOCK_TCP && (c->Cedar != NULL && c->Cedar->Server != NULL && c->Cedar->Server->DisableOpenVPNServer == false)) + if (c->Cedar != NULL && c->Cedar->Server != NULL) { - if (Peek(s, openssl_check_buf, sizeof(openssl_check_buf)) == sizeof(openssl_check_buf)) + c->Type = CONNECTION_TYPE_OTHER; + + if (ProtoHandleConnection(c->Cedar, s) == true) { - if (OvsCheckTcpRecvBufIfOpenVPNProtocol(openssl_check_buf, sizeof(openssl_check_buf))) - { - // Detect OpenSSL packet - Debug("Detect OpenSSL on TCP!\n"); - - no_native = true; - - if (OvsGetNoOpenVpnTcp() == false) - { - // Do OpenSSL processing - c->Type = CONNECTION_TYPE_OPENVPN; - if (OvsPerformTcpServer(c->Cedar, s) == false) - { - error_details = "OpenVPN_TCP_Aborted"; - } - } - - goto ERROR; - } + goto FINAL; } } - - } // Specify the encryption algorithm @@ -2992,22 +2954,18 @@ void ConnectionAccept(CONNECTION *c) Unlock(c->Cedar->lock); // Start the SSL communication - Debug("StartSSL()\n"); Copy(&s->SslAcceptSettings, &c->Cedar->SslAcceptSettings, sizeof(SSL_ACCEPT_SETTINGS)); if (StartSSL(s, x, k) == false) { // Failed AddNoSsl(c->Cedar, &s->RemoteIP); - Debug("Failed to StartSSL.\n"); + Debug("ConnectionAccept(): StartSSL() failed\n"); FreeX(x); FreeK(k); - error_details = "StartSSL"; - - goto ERROR; + goto FINAL; } - FreeX(x); FreeK(k); @@ -3019,29 +2977,18 @@ void ConnectionAccept(CONNECTION *c) if (ServerAccept(c) == false) { // Failed - Debug("ServerAccept Failed. Err = %u\n", c->Err); - goto ERROR; + Debug("ConnectionAccept(): ServerAccept() failed with error %u\n", c->Err); } +FINAL: if (c->flag1 == false) { Debug("%s %u c->flag1 == false\n", __FILE__, __LINE__); Disconnect(s); } + DelConnection(c->Cedar, c); ReleaseSock(s); - - Free(peek_buf); - return; - -ERROR: - Debug("ConnectionAccept() Error.\n"); - - - Disconnect(s); - DelConnection(c->Cedar, c); - ReleaseSock(s); - Free(peek_buf); } // Stop the threads putting additional connection of all that are currently running diff --git a/src/Cedar/Proto.c b/src/Cedar/Proto.c new file mode 100644 index 00000000..084632b6 --- /dev/null +++ b/src/Cedar/Proto.c @@ -0,0 +1,271 @@ +#include "CedarPch.h" + +#include "Proto_OpenVPN.h" + +static LIST *protocols = NULL; + +int ProtoCompare(void *p1, void *p2) +{ + PROTO *proto_1, *proto_2; + + if (p1 == NULL || p2 == NULL) + { + return 0; + } + + proto_1 = (PROTO *)p1; + proto_2 = (PROTO *)p2; + + if (StrCmp(proto_1->impl->Name(), proto_2->impl->Name()) == 0) + { + return true; + } + + return false; +} + +void ProtoInit() +{ + if (protocols != NULL) + { + ProtoFree(); + } + + protocols = NewList(ProtoCompare); + + // OpenVPN + ProtoAdd(OvsGetProtoImpl()); +} + +void ProtoFree() +{ + UINT i; + PROTO_IMPL *impl; + + for (i = 0; i < ProtoNum(); ++i) + { + PROTO *proto = ProtoGet(i); + impl = proto->impl; + Free(proto); + } + + ReleaseList(protocols); + protocols = NULL; +} + +bool ProtoAdd(PROTO_IMPL *impl) +{ + PROTO *proto; + + if (protocols == NULL || impl == NULL) + { + return false; + } + + proto = Malloc(sizeof(PROTO)); + proto->impl = impl; + + Add(protocols, proto); + + Debug("ProtoAdd(): added %s\n", proto->impl->Name()); + + return true; +} + +UINT ProtoNum() +{ + return LIST_NUM(protocols); +} + +PROTO *ProtoGet(const UINT index) +{ + return LIST_DATA(protocols, index); +} + +PROTO *ProtoDetect(SOCK *sock) +{ + UCHAR buf[PROTO_CHECK_BUFFER_SIZE]; + UINT i; + + if (sock == NULL) + { + return NULL; + } + + if (Peek(sock, buf, sizeof(buf)) == 0) + { + return false; + } + + for (i = 0; i < ProtoNum(); ++i) + { + PROTO *p = ProtoGet(i); + if (p->impl->IsPacketForMe(buf, sizeof(buf))) + { + Debug("ProtoDetect(): %s detected\n", p->impl->Name()); + return p; + } + } + + return NULL; +} + +bool ProtoHandleConnection(CEDAR *cedar, SOCK *sock) +{ + void *impl_data; + const PROTO_IMPL *impl; + const PROTO *proto; + + UCHAR *buf; + TCP_RAW_DATA *recv_raw_data; + FIFO *send_fifo; + INTERRUPT_MANAGER *im; + SOCK_EVENT *se; + + const UINT64 giveup = Tick64() + (UINT64)OPENVPN_NEW_SESSION_DEADLINE_TIMEOUT; + + if (cedar == NULL || sock == NULL) + { + return false; + } + + proto = ProtoDetect(sock); + + if (proto == NULL) + { + Debug("ProtoHandleConnection(): unrecognized protocol\n"); + return false; + } + + impl = proto->impl; + + if (StrCmp(impl->Name(), "OpenVPN") == 0 && cedar->Server->DisableOpenVPNServer == true) + { + Debug("ProtoHandleConnection(): OpenVPN detected, but it's disabled\n"); + return false; + } + + if ((impl->SupportedModes() & PROTO_MODE_TCP) == false) + { + return false; + } + + im = NewInterruptManager(); + se = NewSockEvent(); + + if (impl->Init != NULL && impl->Init(&impl_data, cedar, im, se) == false) + { + Debug("ProtoHandleConnection(): failed to initialize %s\n", impl->Name()); + FreeInterruptManager(im); + ReleaseSockEvent(se); + return false; + } + + SetTimeout(sock, TIMEOUT_INFINITE); + JoinSockToSockEvent(sock, se); + + recv_raw_data = NewTcpRawData(&sock->RemoteIP, sock->RemotePort, &sock->LocalIP, sock->LocalPort); + send_fifo = NewFifoFast(); + + buf = Malloc(PROTO_TCP_BUFFER_SIZE); + + Debug("ProtoHandleConnection(): entering main loop\n"); + + // Receive data from the TCP socket + while (true) + { + UINT next_interval; + bool stop = false; + + while (true) + { + const UINT ret = Recv(sock, buf, PROTO_TCP_BUFFER_SIZE, false); + + if (ret == SOCK_LATER) + { + // No more data to read + break; + } + else if (ret == 0) + { + // Disconnected + stop = true; + break; + } + else + { + // Write the received data into the FIFO + WriteFifo(recv_raw_data->Data, buf, ret); + } + } + + if (impl->ProcessData(impl_data, recv_raw_data, send_fifo) == false) + { + stop = true; + } + + // Send data to the TCP socket + while (FifoSize(send_fifo) >= 1) + { + const UINT ret = Send(sock, FifoPtr(send_fifo), FifoSize(send_fifo), false); + + if (ret == SOCK_LATER) + { + // Can not write anymore + break; + } + else if (ret == 0) + { + // Disconnected + stop = true; + break; + } + else + { + // Remove data that has been sent from the FIFO + ReadFifo(send_fifo, NULL, ret); + } + } + + impl->BufferLimit(impl_data, FifoSize(send_fifo) > MAX_BUFFERING_PACKET_SIZE); + + if (impl->IsOk(impl_data) == false) + { + if (impl->EstablishedSessions(impl_data) == 0) + { + if (Tick64() >= giveup) + { + Debug("ProtoHandleConnection(): I waited too much for the session to start, I give up!\n"); + stop = true; + } + } + else + { + Debug("ProtoHandleConnection(): implementation not OK, stopping the server\n"); + stop = true; + } + } + + if (stop) + { + // Error or disconnection occurs + Debug("ProtoHandleConnection(): breaking main loop\n"); + break; + } + + // Wait until the next event occurs + next_interval = GetNextIntervalForInterrupt(im); + next_interval = MIN(next_interval, UDPLISTENER_WAIT_INTERVAL); + WaitSockEvent(se, next_interval); + } + + impl->Free(impl_data); + + FreeInterruptManager(im); + ReleaseSockEvent(se); + FreeTcpRawData(recv_raw_data); + ReleaseFifo(send_fifo); + Free(buf); + + return true; +} diff --git a/src/Cedar/Proto.h b/src/Cedar/Proto.h new file mode 100644 index 00000000..e2a89a25 --- /dev/null +++ b/src/Cedar/Proto.h @@ -0,0 +1,44 @@ +#ifndef PROTO_H +#define PROTO_H + +// OpenVPN sends 2 bytes, thus this is the buffer size. +// If another protocol requires more bytes to be detected, the buffer size must be increased. +#define PROTO_CHECK_BUFFER_SIZE 2 + +#define PROTO_TCP_BUFFER_SIZE (128 * 1024) + +#define PROTO_MODE_TCP 1 +#define PROTO_MODE_UDP 2 + +typedef struct PROTO_IMPL +{ + bool (*Init)(void **param, CEDAR *cedar, INTERRUPT_MANAGER *im, SOCK_EVENT *se); + void (*Free)(void *param); + char *(*Name)(); + UINT (*SupportedModes)(); + bool (*IsPacketForMe)(const UCHAR *data, const UINT size); + bool (*ProcessData)(void *param, TCP_RAW_DATA *received_data, FIFO *data_to_send); + void (*BufferLimit)(void *param, const bool reached); + bool (*IsOk)(void *param); + UINT (*EstablishedSessions)(void *param); +} PROTO_IMPL; + +typedef struct PROTO +{ + PROTO_IMPL *impl; +} PROTO; + +int ProtoCompare(void *p1, void *p2); + +void ProtoInit(); +void ProtoFree(); + +bool ProtoAdd(PROTO_IMPL *impl); + +UINT ProtoNum(); +PROTO *ProtoGet(const UINT index); +PROTO *ProtoDetect(SOCK *sock); + +bool ProtoHandleConnection(CEDAR *cedar, SOCK *sock); + +#endif diff --git a/src/Cedar/Protocol.c b/src/Cedar/Protocol.c index e54dc353..36db2d0a 100644 --- a/src/Cedar/Protocol.c +++ b/src/Cedar/Protocol.c @@ -6412,7 +6412,7 @@ bool ServerDownloadSignature(CONNECTION *c, char **error_detail_str) { bool sstp_ret; // Accept the SSTP connection - c->Type = CONNECTION_TYPE_SSTP; + c->Type = CONNECTION_TYPE_OTHER; sstp_ret = AcceptSstp(c); diff --git a/src/Mayaqua/MayaType.h b/src/Mayaqua/MayaType.h index ce3f9492..b00174be 100644 --- a/src/Mayaqua/MayaType.h +++ b/src/Mayaqua/MayaType.h @@ -421,6 +421,7 @@ typedef struct TUBEPAIR_DATA TUBEPAIR_DATA; typedef struct UDPLISTENER UDPLISTENER; typedef struct UDPLISTENER_SOCK UDPLISTENER_SOCK; typedef struct UDPPACKET UDPPACKET; +typedef struct TCP_RAW_DATA TCP_RAW_DATA; typedef struct INTERRUPT_MANAGER INTERRUPT_MANAGER; typedef struct TUBE_FLUSH_LIST TUBE_FLUSH_LIST; typedef struct ICMP_RESULT ICMP_RESULT; diff --git a/src/Mayaqua/Network.c b/src/Mayaqua/Network.c index 38422823..eee6d40c 100644 --- a/src/Mayaqua/Network.c +++ b/src/Mayaqua/Network.c @@ -18980,6 +18980,40 @@ UDPLISTENER_SOCK *DetermineUdpSocketForSending(UDPLISTENER *u, UDPPACKET *p) return NULL; } +void FreeTcpRawData(TCP_RAW_DATA *trd) +{ + // Validate arguments + if (trd == NULL) + { + return; + } + + ReleaseFifo(trd->Data); + Free(trd); +} + +TCP_RAW_DATA *NewTcpRawData(IP *src_ip, UINT src_port, IP *dst_ip, UINT dst_port) +{ + TCP_RAW_DATA *trd; + // Validate arguments + if (dst_ip == NULL || dst_port == 0) + { + return NULL; + } + + trd = ZeroMalloc(sizeof(TCP_RAW_DATA)); + + Copy(&trd->SrcIP, src_ip, sizeof(IP)); + trd->SrcPort = src_port; + + Copy(&trd->DstIP, dst_ip, sizeof(IP)); + trd->DstPort = dst_port; + + trd->Data = NewFifoFast(); + + return trd; +} + // Release of the UDP packet void FreeUdpPacket(UDPPACKET *p) { diff --git a/src/Mayaqua/Network.h b/src/Mayaqua/Network.h index a9a980a6..b44c8f69 100644 --- a/src/Mayaqua/Network.h +++ b/src/Mayaqua/Network.h @@ -456,6 +456,16 @@ struct TUBEPAIR_DATA SOCK_EVENT *SockEvent1, *SockEvent2; // SockEvent }; +// TCP raw data +struct TCP_RAW_DATA +{ + IP SrcIP; // Source IP address + IP DstIP; // Destination IP address + UINT SrcPort; // Source port + UINT DstPort; // Destination port + FIFO *Data; // Data body +}; + // UDP listener socket entry struct UDPLISTENER_SOCK { @@ -1411,6 +1421,8 @@ void AddPortToUdpListener(UDPLISTENER *u, UINT port); void DeletePortFromUdpListener(UDPLISTENER *u, UINT port); void DeleteAllPortFromUdpListener(UDPLISTENER *u); void UdpListenerSendPackets(UDPLISTENER *u, LIST *packet_list); +TCP_RAW_DATA *NewTcpRawData(IP *src_ip, UINT src_port, IP *dst_ip, UINT dst_port); +void FreeTcpRawData(TCP_RAW_DATA *trd); UDPPACKET *NewUdpPacket(IP *src_ip, UINT src_port, IP *dst_ip, UINT dst_port, void *data, UINT size); void FreeUdpPacket(UDPPACKET *p); UDPLISTENER_SOCK *DetermineUdpSocketForSending(UDPLISTENER *u, UDPPACKET *p); From 9f19efb7af660859fbb6a134bc4c6a7e55b0071c Mon Sep 17 00:00:00 2001 From: Davide Beatrici Date: Fri, 26 Jul 2019 07:58:22 +0200 Subject: [PATCH 2/2] OpenVPN: use new protocol interface --- src/Cedar/Proto_OpenVPN.c | 532 ++++++++++++++++---------------------- src/Cedar/Proto_OpenVPN.h | 26 +- src/Mayaqua/Network.c | 8 + src/Mayaqua/Network.h | 2 + 4 files changed, 246 insertions(+), 322 deletions(-) diff --git a/src/Cedar/Proto_OpenVPN.c b/src/Cedar/Proto_OpenVPN.c index d23de964..cd4e0aa3 100644 --- a/src/Cedar/Proto_OpenVPN.c +++ b/src/Cedar/Proto_OpenVPN.c @@ -7,10 +7,6 @@ #include "CedarPch.h" - -static bool g_no_openvpn_tcp = false; -static bool g_no_openvpn_udp = false; - // Ping signature of the OpenVPN protocol static UCHAR ping_signature[] = { @@ -18,18 +14,213 @@ static UCHAR ping_signature[] = 0x07, 0xed, 0x2d, 0x0a, 0x98, 0x1f, 0xc7, 0x48 }; -// Get the OpenVPN over TCP disabling flag -bool OvsGetNoOpenVpnTcp() +PROTO_IMPL *OvsGetProtoImpl() { - return g_no_openvpn_tcp; + static PROTO_IMPL impl = + { + OvsInit, + OvsFree, + OvsName, + OvsSupportedModes, + OvsIsPacketForMe, + OvsProcessData, + OvsBufferLimit, + OvsIsOk, + OvsEstablishedSessions + }; + + return &impl; } -// Get the OpenVPN over UDP disabling flag -bool OvsGetNoOpenVpnUdp() +bool OvsInit(void **param, CEDAR *cedar, INTERRUPT_MANAGER *im, SOCK_EVENT *se) { - return g_no_openvpn_udp; + if (param == NULL || cedar == NULL || im == NULL || se == NULL) + { + return false; + } + + *param = NewOpenVpnServer(cedar, im, se); + + return true; } +void OvsFree(void *param) +{ + FreeOpenVpnServer(param); +} + +// Return the protocol name +char *OvsName() +{ + return "OpenVPN"; +} + +// Return the supported modes (TCP & UDP) +UINT OvsSupportedModes() +{ + return PROTO_MODE_TCP | PROTO_MODE_UDP; +} + +// Check whether it's an OpenVPN packet +bool OvsIsPacketForMe(const UCHAR *buf, const UINT size) +{ + if (buf == NULL || size != 2) + { + return false; + } + + if (buf[0] == 0x00 && buf[1] == 0x0E) + { + return true; + } + + return false; +} + +bool OvsProcessData(void *param, TCP_RAW_DATA *received_data, FIFO *data_to_send) +{ + bool ret = true; + UINT i; + OPENVPN_SERVER *server; + UCHAR buf[OPENVPN_TCP_MAX_PACKET_SIZE]; + + if (param == NULL || received_data == NULL || data_to_send == NULL) + { + return false; + } + + server = param; + + // Separate to a list of datagrams by interpreting the data received from the TCP socket + while (true) + { + UDPPACKET *packet; + UCHAR *packet_ptr; + UINT packet_size, total_packet_size; + FIFO *recv_fifo = received_data->Data; + const UINT data_size = FifoSize(recv_fifo); + + if (data_size < sizeof(USHORT)) + { + // Corrupt data + break; + } + + packet_size = READ_USHORT(FifoPtr(recv_fifo)); + + if (packet_size == 0 || packet_size > sizeof(buf)) + { + // Invalid packet size + ret = false; + break; + } + + total_packet_size = packet_size + sizeof(USHORT); + + if (data_size < total_packet_size) + { + // Corrupt data + break; + } + + if (ReadFifo(recv_fifo, buf, total_packet_size) != total_packet_size) + { + // Mismatch + ret = false; + break; + } + + // Read one packet and put it in the list + packet_ptr = buf + sizeof(USHORT); + + packet = NewUdpPacket(&received_data->SrcIP, received_data->SrcPort, &received_data->DstIP, received_data->DstPort, Clone(packet_ptr, packet_size), packet_size); + packet->Type = OPENVPN_PROTOCOL_TCP; + Add(server->RecvPacketList, packet); + } + + // Process the list of received datagrams + OvsRecvPacket(server, server->RecvPacketList); + + // Release the received packet list + for (i = 0; i < LIST_NUM(server->RecvPacketList); ++i) + { + UDPPACKET *p = LIST_DATA(server->RecvPacketList, i); + FreeUdpPacket(p); + } + + DeleteAll(server->RecvPacketList); + + // Store in the queue by getting a list of the datagrams to be transmitted from the OpenVPN server + for (i = 0; i < LIST_NUM(server->SendPacketList); ++i) + { + UDPPACKET *p = LIST_DATA(server->SendPacketList, i); + + // Store the size in the TCP send queue first + USHORT us = Endian16((USHORT)p->Size); + + WriteFifo(data_to_send, &us, sizeof(USHORT)); + + // Write the data body + WriteFifo(data_to_send, p->Data, p->Size); + + // Packet release + FreeUdpPacket(p); + } + + DeleteAll(server->SendPacketList); + + return ret; +} + +void OvsBufferLimit(void *param, const bool reached) +{ + if (param == NULL) + { + return; + } + + ((OPENVPN_SERVER *)param)->SupressSendPacket = reached; +} + +bool OvsIsOk(void *param) +{ + OPENVPN_SERVER *s; + + if (param == NULL) + { + return false; + } + + s = param; + + return (s->DisconnectCount < 1) && (s->SessionEstablishedCount > 0); +} + +UINT OvsEstablishedSessions(void *param) +{ + LIST *sessions; + UINT i; + UINT established_sessions = 0; + + if (param == NULL) + { + return 0; + } + + sessions = ((OPENVPN_SERVER *)param)->SessionList; + + for (i = 0;i < LIST_NUM(sessions);i++) + { + OPENVPN_SESSION *se = LIST_DATA(sessions, i); + + if (se->Established) + { + ++established_sessions; + } + } + + return established_sessions; +} // Write the OpenVPN log void OvsLog(OPENVPN_SERVER *s, OPENVPN_SESSION *se, OPENVPN_CHANNEL *c, char *name, ...) @@ -341,7 +532,7 @@ final: } // Process the received packet -void OvsProceccRecvPacket(OPENVPN_SERVER *s, UDPPACKET *p, UINT protocol) +void OvsProceccRecvPacket(OPENVPN_SERVER *s, UDPPACKET *p) { OPENVPN_CHANNEL *c; OPENVPN_SESSION *se; @@ -353,7 +544,7 @@ void OvsProceccRecvPacket(OPENVPN_SERVER *s, UDPPACKET *p, UINT protocol) } // Search for the session - se = OvsFindOrCreateSession(s, &p->DstIP, p->DestPort, &p->SrcIP, p->SrcPort, protocol); + se = OvsFindOrCreateSession(s, &p->DstIP, p->DestPort, &p->SrcIP, p->SrcPort, p->Type); if (se == NULL) { return; @@ -1989,7 +2180,7 @@ OPENVPN_SESSION *OvsSearchSession(OPENVPN_SERVER *s, IP *server_ip, UINT server_ } // Receive packets in the OpenVPN server -void OvsRecvPacket(OPENVPN_SERVER *s, LIST *recv_packet_list, UINT protocol) +void OvsRecvPacket(OPENVPN_SERVER *s, LIST *recv_packet_list) { UINT i, j; LIST *delete_session_list = NULL; @@ -2021,7 +2212,7 @@ void OvsRecvPacket(OPENVPN_SERVER *s, LIST *recv_packet_list, UINT protocol) { UDPPACKET *p = LIST_DATA(recv_packet_list, i); - OvsProceccRecvPacket(s, p, protocol); + OvsProceccRecvPacket(s, p); } // Treat for all sessions and all channels @@ -2702,24 +2893,17 @@ OPENVPN_SERVER *NewOpenVpnServer(CEDAR *cedar, INTERRUPT_MANAGER *interrupt, SOC s = ZeroMalloc(sizeof(OPENVPN_SERVER)); s->Cedar = cedar; - - AddRef(s->Cedar->ref); - s->Interrupt = interrupt; + s->SockEvent = sock_event; s->SessionList = NewList(OvsCompareSessionList); + s->RecvPacketList = NewListFast(NULL); s->SendPacketList = NewListFast(NULL); s->Now = Tick64(); s->NextSessionId = 1; - if (sock_event != NULL) - { - s->SockEvent = sock_event; - AddRef(s->SockEvent->ref); - } - OvsLog(s, NULL, NULL, "LO_START"); s->Dh = DhNewFromBits(DH_PARAM_BITS_DEFAULT); @@ -2739,33 +2923,33 @@ void FreeOpenVpnServer(OPENVPN_SERVER *s) OvsLog(s, NULL, NULL, "LO_STOP"); - // Release the session list - for (i = 0;i < LIST_NUM(s->SessionList);i++) + // Release the sessions list + for (i = 0; i < LIST_NUM(s->SessionList); ++i) { OPENVPN_SESSION *se = LIST_DATA(s->SessionList, i); - OvsFreeSession(se); } ReleaseList(s->SessionList); - // Release the packet which is attempting to send - for (i = 0;i < LIST_NUM(s->SendPacketList);i++) + // Release the incoming packets list + for (i = 0; i < LIST_NUM(s->RecvPacketList); ++i) + { + UDPPACKET *p = LIST_DATA(s->RecvPacketList, i); + FreeUdpPacket(p); + } + + ReleaseList(s->RecvPacketList); + + // Release the outgoing packets list + for (i = 0; i < LIST_NUM(s->SendPacketList); ++i) { UDPPACKET *p = LIST_DATA(s->SendPacketList, i); - FreeUdpPacket(p); } ReleaseList(s->SendPacketList); - ReleaseCedar(s->Cedar); - - if (s->SockEvent != NULL) - { - ReleaseSockEvent(s->SockEvent); - } - DhFree(s->Dh); Free(s); @@ -2783,12 +2967,6 @@ void OpenVpnServerUdpListenerProc(UDPLISTENER *u, LIST *packet_list) us = (OPENVPN_SERVER_UDP *)u->Param; - if (OvsGetNoOpenVpnUdp()) - { - // OpenVPN over UDP is disabled - return; - } - if (us->OpenVpnServer != NULL) { { @@ -2797,7 +2975,7 @@ void OpenVpnServerUdpListenerProc(UDPLISTENER *u, LIST *packet_list) ClearStr(us->Cedar->OpenVPNPublicPorts, sizeof(us->Cedar->OpenVPNPublicPorts)); } - OvsRecvPacket(us->OpenVpnServer, packet_list, OPENVPN_PROTOCOL_UDP); + OvsRecvPacket(us->OpenVpnServer, packet_list); UdpListenerSendPackets(u, us->OpenVpnServer->SendPacketList); DeleteAll(us->OpenVpnServer->SendPacketList); @@ -2821,7 +2999,7 @@ OPENVPN_SERVER_UDP *NewOpenVpnServerUdp(CEDAR *cedar) AddRef(u->Cedar->ref); // Create a UDP listener - u->UdpListener = NewUdpListener(OpenVpnServerUdpListenerProc, u, &cedar->Server->ListenIP); + u->UdpListener = NewUdpListenerEx(OpenVpnServerUdpListenerProc, u, &cedar->Server->ListenIP, OPENVPN_PROTOCOL_UDP); // Create an OpenVPN server u->OpenVpnServer = NewOpenVpnServer(cedar, u->UdpListener->Interrupts, u->UdpListener->Event); @@ -2896,269 +3074,3 @@ void FreeOpenVpnServerUdp(OPENVPN_SERVER_UDP *u) Free(u); } - -// Check whether it's OpenSSL protocol by looking the first receive buffer of the TCP -bool OvsCheckTcpRecvBufIfOpenVPNProtocol(UCHAR *buf, UINT size) -{ - if (buf == NULL || size != 2) - { - return false; - } - - if (buf[0] == 0x00 && buf[1] == 0x0E) - { - return true; - } - - return false; -} - -// Run the OpenVPN server in TCP mode -bool OvsPerformTcpServer(CEDAR *cedar, SOCK *sock) -{ - OPENVPN_SERVER *s; - INTERRUPT_MANAGER *im; - SOCK_EVENT *se; - FIFO *tcp_recv_fifo; - FIFO *tcp_send_fifo; - UINT buf_size = (128 * 1024); - UCHAR *buf; - UINT64 giveup_time = Tick64() + (UINT64)OPENVPN_NEW_SESSION_DEADLINE_TIMEOUT; - LIST *ovs_recv_packet; - UINT i; - bool ret = false; - // Validate arguments - if (cedar == NULL || sock == NULL) - { - return false; - } - - // Initialize - buf = Malloc(buf_size); - im = NewInterruptManager(); - se = NewSockEvent(); - SetTimeout(sock, TIMEOUT_INFINITE); - JoinSockToSockEvent(sock, se); - - tcp_recv_fifo = NewFifoFast(); - tcp_send_fifo = NewFifoFast(); - - ovs_recv_packet = NewListFast(NULL); - - // Create an OpenVPN server - s = NewOpenVpnServer(cedar, im, se); - - // Main loop - Debug("Entering OpenVPN TCP Server Main Loop.\n"); - while (true) - { - UINT next_interval; - bool disconnected = false; - UINT64 now = Tick64(); - - // Receive data from a TCP socket - while (true) - { - UINT r = Recv(sock, buf, buf_size, false); - if (r == SOCK_LATER) - { - // Can not read any more - break; - } - else if (r == 0) - { - // Disconnected - disconnected = true; - break; - } - else - { - // Read - WriteFifo(tcp_recv_fifo, buf, r); - } - } - - // Separate to a list of datagrams by interpreting the data received from the TCP socket - while (true) - { - UINT r = FifoSize(tcp_recv_fifo); - if (r >= sizeof(USHORT)) - { - void *ptr = FifoPtr(tcp_recv_fifo); - USHORT packet_size = READ_USHORT(ptr); - if (packet_size != 0 && packet_size <= OPENVPN_TCP_MAX_PACKET_SIZE) - { - UINT total_len = (UINT)packet_size + sizeof(USHORT); - if (r >= total_len) - { - if (ReadFifo(tcp_recv_fifo, buf, total_len) != total_len) - { - // Mismatch - disconnected = true; - break; - } - else - { - // Read one packet - UINT payload_len = packet_size; - UCHAR *payload_ptr = buf + sizeof(USHORT); - - // Pass the packet to the OpenVPN server - Add(ovs_recv_packet, NewUdpPacket(&sock->RemoteIP, sock->RemotePort, - &sock->LocalIP, sock->LocalPort, - Clone(payload_ptr, payload_len), payload_len)); - } - } - else - { - // Non-arrival - break; - } - } - else - { - // Invalid packet size - disconnected = true; - break; - } - } - else - { - // Non-arrival - break; - } - } - - // Pass a list of received datagrams to the OpenVPN server - OvsRecvPacket(s, ovs_recv_packet, OPENVPN_PROTOCOL_TCP); - - // Release the received packet list - for (i = 0;i < LIST_NUM(ovs_recv_packet);i++) - { - UDPPACKET *p = LIST_DATA(ovs_recv_packet, i); - - FreeUdpPacket(p); - } - - DeleteAll(ovs_recv_packet); - - // Store in the queue by getting a list of the datagrams to be transmitted from the OpenVPN server - for (i = 0;i < LIST_NUM(s->SendPacketList);i++) - { - UDPPACKET *p = LIST_DATA(s->SendPacketList, i); - // Store the size to the TCP send queue first - USHORT us = (USHORT)p->Size; - //Debug(" *** TCP SEND %u\n", us); - us = Endian16(us); - WriteFifo(tcp_send_fifo, &us, sizeof(USHORT)); - - // Write the data body - WriteFifo(tcp_send_fifo, p->Data, p->Size); - - // Packet release - FreeUdpPacket(p); - } - DeleteAll(s->SendPacketList); - - // Send data to the TCP socket - while (FifoSize(tcp_send_fifo) >= 1) - { - UINT r = Send(sock, FifoPtr(tcp_send_fifo), FifoSize(tcp_send_fifo), false); - - if (r == SOCK_LATER) - { - // Can not write any more - break; - } - else if (r == 0) - { - // Disconnected - disconnected = true; - break; - } - else - { - // Wrote out - ReadFifo(tcp_send_fifo, NULL, r); - } - } - - if (FifoSize(tcp_send_fifo) > MAX_BUFFERING_PACKET_SIZE) - { - s->SupressSendPacket = true; - } - else - { - s->SupressSendPacket = false; - } - - if (s->DisconnectCount >= 1) - { - // Session disconnection has occurred on OpenVPN server-side - disconnected = true; - } - - if (giveup_time <= now) - { - UINT i; - UINT num_established_sessions = 0; - for (i = 0;i < LIST_NUM(s->SessionList);i++) - { - OPENVPN_SESSION *se = LIST_DATA(s->SessionList, i); - - if (se->Established) - { - num_established_sessions++; - } - } - - if (num_established_sessions == 0) - { - // If the number of sessions is 0 even if wait a certain period of time after the start of server, abort - disconnected = true; - } - } - - if (disconnected) - { - // Error or disconnect occurs - Debug("Breaking OpenVPN TCP Server Main Loop.\n"); - break; - } - - // Wait until the next event occurs - next_interval = GetNextIntervalForInterrupt(im); - next_interval = MIN(next_interval, UDPLISTENER_WAIT_INTERVAL); - WaitSockEvent(se, next_interval); - } - - if (s != NULL && s->SessionEstablishedCount != 0) - { - ret = true; - } - - // Release the OpenVPN server - FreeOpenVpnServer(s); - - // Release object - FreeInterruptManager(im); - ReleaseSockEvent(se); - ReleaseFifo(tcp_recv_fifo); - ReleaseFifo(tcp_send_fifo); - Free(buf); - - // Release the received packet list - for (i = 0;i < LIST_NUM(ovs_recv_packet);i++) - { - UDPPACKET *p = LIST_DATA(ovs_recv_packet, i); - - FreeUdpPacket(p); - } - - ReleaseList(ovs_recv_packet); - - return ret; -} - - - diff --git a/src/Cedar/Proto_OpenVPN.h b/src/Cedar/Proto_OpenVPN.h index 210137ed..02ba2247 100644 --- a/src/Cedar/Proto_OpenVPN.h +++ b/src/Cedar/Proto_OpenVPN.h @@ -188,6 +188,7 @@ struct OPENVPN_SERVER { CEDAR *Cedar; INTERRUPT_MANAGER *Interrupt; // Interrupt manager + LIST *RecvPacketList; // Received packets list LIST *SendPacketList; // Transmission packet list LIST *SessionList; // Session list UINT64 Now; // Current time @@ -212,8 +213,18 @@ struct OPENVPN_SERVER_UDP // OpenVPN Default Client Option String #define OVPN_DEF_CLIENT_OPTION_STRING "dev-type tun,link-mtu 1500,tun-mtu 1500,cipher AES-128-CBC,auth SHA1,keysize 128,key-method 2,tls-client" - //// Function prototype +PROTO_IMPL *OvsGetProtoImpl(); +bool OvsInit(void **param, CEDAR *cedar, INTERRUPT_MANAGER *im, SOCK_EVENT *se); +void OvsFree(void *param); +char *OvsName(); +UINT OvsSupportedModes(); +bool OvsIsPacketForMe(const UCHAR *buf, const UINT size); +bool OvsProcessData(void *param, TCP_RAW_DATA *received_data, FIFO *data_to_send); +void OvsBufferLimit(void *param, const bool reached); +bool OvsIsOk(void *param); +UINT OvsEstablishedSessions(void *param); + OPENVPN_SERVER_UDP *NewOpenVpnServerUdp(CEDAR *cedar); void FreeOpenVpnServerUdp(OPENVPN_SERVER_UDP *u); void OpenVpnServerUdpListenerProc(UDPLISTENER *u, LIST *packet_list); @@ -221,8 +232,8 @@ void OvsApplyUdpPortList(OPENVPN_SERVER_UDP *u, char *port_list, IP *listen_ip); OPENVPN_SERVER *NewOpenVpnServer(CEDAR *cedar, INTERRUPT_MANAGER *interrupt, SOCK_EVENT *sock_event); void FreeOpenVpnServer(OPENVPN_SERVER *s); -void OvsRecvPacket(OPENVPN_SERVER *s, LIST *recv_packet_list, UINT protocol); -void OvsProceccRecvPacket(OPENVPN_SERVER *s, UDPPACKET *p, UINT protocol); +void OvsRecvPacket(OPENVPN_SERVER *s, LIST *recv_packet_list); +void OvsProceccRecvPacket(OPENVPN_SERVER *s, UDPPACKET *p); int OvsCompareSessionList(void *p1, void *p2); OPENVPN_SESSION *OvsSearchSession(OPENVPN_SERVER *s, IP *server_ip, UINT server_port, IP *client_ip, UINT client_port, UINT protocol); OPENVPN_SESSION *OvsNewSession(OPENVPN_SERVER *s, IP *server_ip, UINT server_port, IP *client_ip, UINT client_port, UINT protocol); @@ -264,16 +275,7 @@ UINT OvsCalcTcpMss(OPENVPN_SERVER *s, OPENVPN_SESSION *se, OPENVPN_CHANNEL *c); CIPHER *OvsGetCipher(char *name); MD *OvsGetMd(char *name); -bool OvsCheckTcpRecvBufIfOpenVPNProtocol(UCHAR *buf, UINT size); - -bool OvsPerformTcpServer(CEDAR *cedar, SOCK *sock); - -void OvsSetReplyForVgsPollEnable(bool b); - -bool OvsGetNoOpenVpnTcp(); void OpenVpnServerUdpSetDhParam(OPENVPN_SERVER_UDP *u, DH_CTX *dh); - - #endif // PROTO_OPENVPN_H diff --git a/src/Mayaqua/Network.c b/src/Mayaqua/Network.c index eee6d40c..d9567c8f 100644 --- a/src/Mayaqua/Network.c +++ b/src/Mayaqua/Network.c @@ -18792,6 +18792,8 @@ LABEL_FATAL_ERROR: p->SrcPort = p->DestPort = MAKE_SPECIAL_PORT(50); } + p->Type = u->PacketType; + Add(recv_list, p); } @@ -19084,6 +19086,11 @@ void UdpListenerSendPackets(UDPLISTENER *u, LIST *packet_list) // Creating a UDP listener UDPLISTENER *NewUdpListener(UDPLISTENER_RECV_PROC *recv_proc, void *param, IP *listen_ip) +{ + return NewUdpListenerEx(recv_proc, param, listen_ip, INFINITE); +} + +UDPLISTENER *NewUdpListenerEx(UDPLISTENER_RECV_PROC *recv_proc, void *param, IP *listen_ip, UINT packet_type) { UDPLISTENER *u; // Validate arguments @@ -19095,6 +19102,7 @@ UDPLISTENER *NewUdpListener(UDPLISTENER_RECV_PROC *recv_proc, void *param, IP *l u = ZeroMalloc(sizeof(UDPLISTENER)); u->Param = param; + u->PacketType = packet_type; u->PortList = NewList(NULL); u->Event = NewSockEvent(); diff --git a/src/Mayaqua/Network.h b/src/Mayaqua/Network.h index b44c8f69..54fe2c76 100644 --- a/src/Mayaqua/Network.h +++ b/src/Mayaqua/Network.h @@ -506,6 +506,7 @@ struct UDPLISTENER UINT64 LastCheckTick; // Time which the socket list was checked last UDPLISTENER_RECV_PROC *RecvProc; // Receive procedure LIST *SendPacketList; // Transmission packet list + UINT PacketType; // The type to set when creating an UDPPACKET void *Param; // Parameters INTERRUPT_MANAGER *Interrupts; // Interrupt manager bool HostIPAddressListChanged; // IP address list of the host has changed @@ -1415,6 +1416,7 @@ int CmpIpAddressList(void *p1, void *p2); UINT64 GetHostIPAddressListHash(); UDPLISTENER *NewUdpListener(UDPLISTENER_RECV_PROC *recv_proc, void *param, IP *listen_ip); +UDPLISTENER *NewUdpListenerEx(UDPLISTENER_RECV_PROC *recv_proc, void *param, IP *listen_ip, UINT packet_type); void UdpListenerThread(THREAD *thread, void *param); void FreeUdpListener(UDPLISTENER *u); void AddPortToUdpListener(UDPLISTENER *u, UINT port);