diff --git a/src/Cedar/Proto.c b/src/Cedar/Proto.c index 482aa85a..d4299da0 100644 --- a/src/Cedar/Proto.c +++ b/src/Cedar/Proto.c @@ -19,6 +19,102 @@ int ProtoImplCompare(void *p1, void *p2) return false; } +int ProtoSessionCompare(void *p1, void *p2) +{ + int ret; + PROTO_SESSION *session_1, *session_2; + + if (p1 == NULL || p2 == NULL) + { + return 0; + } + + session_1 = *(PROTO_SESSION **)p1; + session_2 = *(PROTO_SESSION **)p2; + + // The source port must match + ret = COMPARE_RET(session_1->SrcPort, session_2->SrcPort); + if (ret != 0) + { + return ret; + } + + // The destination port must match + ret = COMPARE_RET(session_1->DstPort, session_2->DstPort); + if (ret != 0) + { + return ret; + } + + // The source IP address must match + ret = CmpIpAddr(&session_1->SrcIp, &session_2->SrcIp); + if (ret != 0) + { + return ret; + } + + // The destination IP address must match + return CmpIpAddr(&session_1->DstIp, &session_2->DstIp); +} + +UINT ProtoSessionHash(void *p) +{ + IP *ip; + UINT ret = 0; + PROTO_SESSION *session = p; + + if (session == NULL) + { + return 0; + } + + ip = &session->SrcIp; + if (IsIP6(ip)) + { + UINT i; + for (i = 0; i < sizeof(ip->ipv6_addr); ++i) + { + ret += ip->ipv6_addr[i]; + } + + ret += ip->ipv6_scope_id; + } + else + { + UINT i; + for (i = 0; i < sizeof(ip->addr); ++i) + { + ret += ip->addr[i]; + } + } + + ret += session->SrcPort; + + ip = &session->DstIp; + if (IsIP6(ip)) + { + UINT i; + for (i = 0; i < sizeof(ip->ipv6_addr); ++i) + { + ret += ip->ipv6_addr[i]; + } + + ret += ip->ipv6_scope_id; + } + else + { + UINT i; + for (i = 0; i < sizeof(ip->addr); ++i) + { + ret += ip->addr[i]; + } + } + + ret += session->DstPort; + + return ret; +} + PROTO *ProtoNew(CEDAR *cedar) { PROTO *proto; @@ -31,22 +127,36 @@ PROTO *ProtoNew(CEDAR *cedar) proto = Malloc(sizeof(PROTO)); proto->Cedar = cedar; proto->Impls = NewList(ProtoImplCompare); + proto->Sessions = NewHashList(ProtoSessionHash, ProtoSessionCompare, 0, true); AddRef(cedar->ref); // OpenVPN ProtoImplAdd(proto, OvsGetProtoImpl()); + proto->UdpListener = NewUdpListener(ProtoHandleDatagrams, proto, &cedar->Server->ListenIP); + return proto; } void ProtoDelete(PROTO *proto) { + UINT i = 0; + if (proto == NULL) { return; } + StopUdpListener(proto->UdpListener); + + for (i = 0; i < HASH_LIST_NUM(proto->Sessions); ++i) + { + ProtoDeleteSession(LIST_DATA(proto->Sessions->AllList, i)); + } + + FreeUdpListener(proto->UdpListener); + ReleaseHashList(proto->Sessions); ReleaseList(proto->Impls); ReleaseCedar(proto->Cedar); Free(proto); @@ -65,32 +175,138 @@ bool ProtoImplAdd(PROTO *proto, PROTO_IMPL *impl) { return true; } -PROTO_IMPL *ProtoImplDetect(PROTO *proto, SOCK *sock) +PROTO_IMPL *ProtoImplDetect(PROTO *proto, const PROTO_MODE mode, const UCHAR *data, const UINT size) { - UCHAR buf[PROTO_CHECK_BUFFER_SIZE]; UINT i; - if (proto == NULL || sock == NULL) + if (proto == NULL || data == NULL || size == 0) { return NULL; } - if (Peek(sock, buf, sizeof(buf)) == 0) - { - return false; - } - for (i = 0; i < LIST_NUM(proto->Impls); ++i) { PROTO_IMPL *impl = LIST_DATA(proto->Impls, i); - if (impl->IsPacketForMe(buf, sizeof(buf))) + if (impl->IsPacketForMe(mode, data, size) == false) { - Debug("ProtoImplDetect(): %s detected\n", impl->Name()); - return impl; + continue; + } + + if (StrCmp(impl->Name(), "OpenVPN") == 0 && proto->Cedar->Server->DisableOpenVPNServer) + { + Debug("ProtoImplDetect(): OpenVPN detected, but it's disabled\n"); + continue; + } + + Debug("ProtoImplDetect(): %s detected\n", impl->Name()); + return impl; + } + + Debug("ProtoImplDetect(): unrecognized protocol\n"); + return NULL; +} + +PROTO_SESSION *ProtoNewSession(PROTO *proto, PROTO_IMPL *impl, const IP *src_ip, const USHORT src_port, const IP *dst_ip, const USHORT dst_port) +{ + PROTO_SESSION *session; + + if (impl == NULL || src_ip == NULL || src_port == 0 || dst_ip == NULL || dst_port == 0) + { + return NULL; + } + + session = ZeroMalloc(sizeof(PROTO_SESSION)); + + session->SockEvent = NewSockEvent(); + session->InterruptManager = NewInterruptManager(); + + if (impl->Init != NULL && impl->Init(&session->Param, proto->Cedar, session->InterruptManager, session->SockEvent) == false) + { + Debug("ProtoNewSession(): failed to initialize %s\n", impl->Name()); + + ReleaseSockEvent(session->SockEvent); + FreeInterruptManager(session->InterruptManager); + Free(session); + + return NULL; + } + + session->Proto = proto; + session->Impl = impl; + + CopyIP(&session->SrcIp, src_ip); + session->SrcPort = src_port; + CopyIP(&session->DstIp, dst_ip); + session->DstPort = dst_port; + + session->DatagramsIn = NewListFast(NULL); + session->DatagramsOut = NewListFast(NULL); + + session->Lock = NewLock(); + session->Thread = NewThread(ProtoSessionThread, session); + + return session; +} + +void ProtoDeleteSession(PROTO_SESSION *session) +{ + if (session == NULL) + { + return; + } + + session->Halt = true; + SetSockEvent(session->SockEvent); + + WaitThread(session->Thread, INFINITE); + ReleaseThread(session->Thread); + + session->Impl->Free(session->Param); + + ReleaseSockEvent(session->SockEvent); + FreeInterruptManager(session->InterruptManager); + + ReleaseList(session->DatagramsIn); + ReleaseList(session->DatagramsOut); + + DeleteLock(session->Lock); + + Free(session); +} + +bool ProtoSetListenIP(PROTO *proto, const IP *ip) +{ + if (proto == NULL || ip == NULL) + { + return false; + } + + Copy(&proto->UdpListener->ListenIP, ip, sizeof(proto->UdpListener->ListenIP)); + + return true; +} + +bool ProtoSetUdpPorts(PROTO *proto, const LIST *ports) +{ + UINT i = 0; + + if (proto == NULL || ports == NULL) + { + return false; + } + + DeleteAllPortFromUdpListener(proto->UdpListener); + + for (i = 0; i < LIST_NUM(ports); ++i) + { + UINT port = *((UINT *)LIST_DATA(ports, i)); + if (port >= 1 && port <= 65535) + { + AddPortToUdpListener(proto->UdpListener, port); } } - return NULL; + return true; } bool ProtoHandleConnection(PROTO *proto, SOCK *sock) @@ -104,29 +320,23 @@ bool ProtoHandleConnection(PROTO *proto, SOCK *sock) INTERRUPT_MANAGER *im; SOCK_EVENT *se; - const UINT64 giveup = Tick64() + (UINT64)OPENVPN_NEW_SESSION_DEADLINE_TIMEOUT; - if (proto == NULL || sock == NULL) { return false; } - impl = ProtoImplDetect(proto, sock); - if (impl == NULL) { - Debug("ProtoHandleConnection(): unrecognized protocol\n"); - return false; - } + UCHAR tmp[PROTO_CHECK_BUFFER_SIZE]; + if (Peek(sock, tmp, sizeof(tmp)) == 0) + { + return false; + } - if (StrCmp(impl->Name(), "OpenVPN") == 0 && proto->Cedar->Server->DisableOpenVPNServer == true) - { - Debug("ProtoHandleConnection(): OpenVPN detected, but it's disabled\n"); - return false; - } - - if ((impl->SupportedModes() & PROTO_MODE_TCP) == false) - { - return false; + impl = ProtoImplDetect(proto, PROTO_MODE_TCP, tmp, sizeof(tmp)); + if (impl == NULL) + { + return false; + } } im = NewInterruptManager(); @@ -208,23 +418,6 @@ bool ProtoHandleConnection(PROTO *proto, SOCK *sock) 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 @@ -248,3 +441,120 @@ bool ProtoHandleConnection(PROTO *proto, SOCK *sock) return true; } + +void ProtoHandleDatagrams(UDPLISTENER *listener, LIST *datagrams) +{ + UINT i; + HASH_LIST *sessions; + PROTO *proto = listener->Param; + + if (proto == NULL || listener == NULL || datagrams == NULL) + { + return; + } + + sessions = proto->Sessions; + + for (i = 0; i < LIST_NUM(datagrams); ++i) + { + UDPPACKET *datagram = LIST_DATA(datagrams, i); + PROTO_SESSION *session, tmp; + + CopyIP(&tmp.SrcIp, &datagram->SrcIP); + tmp.SrcPort = datagram->SrcPort; + CopyIP(&tmp.DstIp, &datagram->DstIP); + tmp.DstPort = datagram->DestPort; + + session = SearchHash(sessions, &tmp); + if (session == NULL) + { + tmp.Impl = ProtoImplDetect(proto, PROTO_MODE_UDP, datagram->Data, datagram->Size); + if (tmp.Impl == NULL) + { + continue; + } + + session = ProtoNewSession(proto, tmp.Impl, &tmp.SrcIp, tmp.SrcPort, &tmp.DstIp, tmp.DstPort); + if (session == NULL) + { + continue; + } + + AddHash(proto->Sessions, session); + } + + if (session->Halt) + { + DeleteHash(sessions, session); + ProtoDeleteSession(session); + continue; + } + + Lock(session->Lock); + { + void *data = Clone(datagram->Data, datagram->Size); + UDPPACKET *packet = NewUdpPacket(&datagram->SrcIP, datagram->SrcPort, &datagram->DstIP, datagram->DestPort, data, datagram->Size); + Add(session->DatagramsIn, packet); + } + Unlock(session->Lock); + } + + for (i = 0; i < LIST_NUM(sessions->AllList); ++i) + { + PROTO_SESSION *session = LIST_DATA(sessions->AllList, i); + if (LIST_NUM(session->DatagramsIn) > 0) + { + SetSockEvent(session->SockEvent); + } + } +} + +void ProtoSessionThread(THREAD *thread, void *param) +{ + PROTO_SESSION *session = param; + + if (thread == NULL || session == NULL) + { + return; + } + + while (session->Halt == false) + { + bool ok; + UINT interval; + void *param = session->Param; + PROTO_IMPL *impl = session->Impl; + LIST *received = session->DatagramsIn; + LIST *to_send = session->DatagramsOut; + + Lock(session->Lock); + { + UINT i; + + ok = impl->ProcessDatagrams(param, received, to_send); + + UdpListenerSendPackets(session->Proto->UdpListener, to_send); + + for (i = 0; i < LIST_NUM(received); ++i) + { + FreeUdpPacket(LIST_DATA(received, i)); + } + + DeleteAll(received); + DeleteAll(to_send); + } + Unlock(session->Lock); + + if (ok == false) + { + Debug("ProtoSessionThread(): breaking main loop\n"); + session->Halt = true; + break; + } + + // Wait until the next event occurs + interval = GetNextIntervalForInterrupt(session->InterruptManager); + interval = MIN(interval, UDPLISTENER_WAIT_INTERVAL); + WaitSockEvent(session->SockEvent, interval); + } +} diff --git a/src/Cedar/Proto.h b/src/Cedar/Proto.h index 26b3c1ee..b9b946f2 100644 --- a/src/Cedar/Proto.h +++ b/src/Cedar/Proto.h @@ -7,13 +7,19 @@ #define PROTO_TCP_BUFFER_SIZE (128 * 1024) -#define PROTO_MODE_TCP 1 -#define PROTO_MODE_UDP 2 +typedef enum PROTO_MODE +{ + PROTO_MODE_UNKNOWN = 0, + PROTO_MODE_TCP = 1, + PROTO_MODE_UDP = 2 +} PROTO_MODE; typedef struct PROTO { CEDAR *Cedar; LIST *Impls; + HASH_LIST *Sessions; + UDPLISTENER *UdpListener; } PROTO; typedef struct PROTO_IMPL @@ -21,22 +27,49 @@ 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); + bool (*IsPacketForMe)(const PROTO_MODE mode, const UCHAR *data, const UINT size); + bool (*ProcessData)(void *param, TCP_RAW_DATA *in, FIFO *out); + bool (*ProcessDatagrams)(void *param, LIST *in, LIST *out); void (*BufferLimit)(void *param, const bool reached); - bool (*IsOk)(void *param); - UINT (*EstablishedSessions)(void *param); } PROTO_IMPL; +typedef struct PROTO_SESSION +{ + void *Param; + PROTO *Proto; + PROTO_IMPL *Impl; + IP SrcIp; + USHORT SrcPort; + IP DstIp; + USHORT DstPort; + LIST *DatagramsIn; + LIST *DatagramsOut; + SOCK_EVENT *SockEvent; + INTERRUPT_MANAGER *InterruptManager; + THREAD *Thread; + LOCK *Lock; + volatile bool Halt; +} PROTO_SESSION; + int ProtoImplCompare(void *p1, void *p2); +int ProtoSessionCompare(void *p1, void *p2); + +UINT ProtoSessionHash(void *p); PROTO *ProtoNew(CEDAR *cedar); void ProtoDelete(PROTO *proto); bool ProtoImplAdd(PROTO *proto, PROTO_IMPL *impl); -PROTO_IMPL *ProtoImplDetect(PROTO *proto, SOCK *sock); +PROTO_IMPL *ProtoImplDetect(PROTO *proto, const PROTO_MODE mode, const UCHAR *data, const UINT size); + +PROTO_SESSION *ProtoNewSession(PROTO *proto, PROTO_IMPL *impl, const IP *src_ip, const USHORT src_port, const IP *dst_ip, const USHORT dst_port); +void ProtoDeleteSession(PROTO_SESSION *session); + +bool ProtoSetListenIP(PROTO *proto, const IP *ip); +bool ProtoSetUdpPorts(PROTO *proto, const LIST *ports); bool ProtoHandleConnection(PROTO *proto, SOCK *sock); +void ProtoHandleDatagrams(UDPLISTENER *listener, LIST *datagrams); +void ProtoSessionThread(THREAD *thread, void *param); #endif diff --git a/src/Cedar/Proto_OpenVPN.c b/src/Cedar/Proto_OpenVPN.c index f61c3de2..c6acc5fa 100644 --- a/src/Cedar/Proto_OpenVPN.c +++ b/src/Cedar/Proto_OpenVPN.c @@ -21,12 +21,10 @@ PROTO_IMPL *OvsGetProtoImpl() OvsInit, OvsFree, OvsName, - OvsSupportedModes, OvsIsPacketForMe, OvsProcessData, + OvsProcessDatagrams, OvsBufferLimit, - OvsIsOk, - OvsEstablishedSessions }; return &impl; @@ -55,48 +53,54 @@ 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) +bool OvsIsPacketForMe(const PROTO_MODE mode, const UCHAR *data, const UINT size) { - if (buf == NULL || size != 2) + if (mode == PROTO_MODE_TCP) { - return false; - } + if (data == NULL || size < 2) + { + return false; + } - if (buf[0] == 0x00 && buf[1] == 0x0E) + if (data[0] == 0x00 && data[1] == 0x0E) + { + return true; + } + } + else if (mode == PROTO_MODE_UDP) { + OPENVPN_PACKET *packet = OvsParsePacket(data, size); + if (packet == NULL) + { + return false; + } + + OvsFreePacket(packet); return true; } return false; } -bool OvsProcessData(void *param, TCP_RAW_DATA *received_data, FIFO *data_to_send) +bool OvsProcessData(void *param, TCP_RAW_DATA *in, FIFO *out) { bool ret = true; UINT i; - OPENVPN_SERVER *server; + OPENVPN_SERVER *server = param; UCHAR buf[OPENVPN_TCP_MAX_PACKET_SIZE]; - if (param == NULL || received_data == NULL || data_to_send == NULL) + if (server == NULL || in == NULL || out == NULL) { return false; } - server = param; - // Separate to a list of datagrams by interpreting the data received from the TCP socket while (true) { UDPPACKET *packet; USHORT payload_size, packet_size; - FIFO *fifo = received_data->Data; + FIFO *fifo = in->Data; const UINT fifo_size = FifoSize(fifo); if (fifo_size < sizeof(USHORT)) @@ -130,13 +134,12 @@ bool OvsProcessData(void *param, TCP_RAW_DATA *received_data, FIFO *data_to_send } // Insert packet into the list - packet = NewUdpPacket(&received_data->SrcIP, received_data->SrcPort, &received_data->DstIP, received_data->DstPort, Clone(buf + sizeof(USHORT), payload_size), payload_size); - packet->Type = OPENVPN_PROTOCOL_TCP; + packet = NewUdpPacket(&in->SrcIP, in->SrcPort, &in->DstIP, in->DstPort, Clone(buf + sizeof(USHORT), payload_size), payload_size); Add(server->RecvPacketList, packet); } // Process the list of received datagrams - OvsRecvPacket(server, server->RecvPacketList); + OvsRecvPacket(server, server->RecvPacketList, OPENVPN_PROTOCOL_TCP); // Release the received packet list for (i = 0; i < LIST_NUM(server->RecvPacketList); ++i) @@ -155,10 +158,10 @@ bool OvsProcessData(void *param, TCP_RAW_DATA *received_data, FIFO *data_to_send // Store the size in the TCP send queue first USHORT us = Endian16((USHORT)p->Size); - WriteFifo(data_to_send, &us, sizeof(USHORT)); + WriteFifo(out, &us, sizeof(USHORT)); // Write the data body - WriteFifo(data_to_send, p->Data, p->Size); + WriteFifo(out, p->Data, p->Size); // Packet release FreeUdpPacket(p); @@ -166,9 +169,66 @@ bool OvsProcessData(void *param, TCP_RAW_DATA *received_data, FIFO *data_to_send DeleteAll(server->SendPacketList); + if (server->Giveup <= server->Now) + { + UINT i; + for (i = 0; i < LIST_NUM(server->SessionList); ++i) + { + OPENVPN_SESSION *se = LIST_DATA(server->SessionList, i); + + if (se->Established) + { + return ret && server->DisconnectCount < 1; + } + } + + return false; + } + return ret; } +bool OvsProcessDatagrams(void *param, LIST *in, LIST *out) +{ + UINT i; + LIST *to_send; + OPENVPN_SERVER *server = param; + + if (server == NULL || in == NULL || out == NULL) + { + return false; + } + + OvsRecvPacket(server, in, OPENVPN_PROTOCOL_UDP); + + to_send = server->SendPacketList; + + for (i = 0; i < LIST_NUM(to_send); ++i) + { + Add(out, LIST_DATA(to_send, i)); + } + + DeleteAll(server->SendPacketList); + + if (server->Giveup <= server->Now) + { + UINT i; + for (i = 0; i < LIST_NUM(server->SessionList); ++i) + { + OPENVPN_SESSION *se = LIST_DATA(server->SessionList, i); + + if (se->Established) + { + return server->DisconnectCount < 1; + } + } + + return false; + } + + return true; +} + void OvsBufferLimit(void *param, const bool reached) { if (param == NULL) @@ -179,46 +239,6 @@ void OvsBufferLimit(void *param, const bool reached) ((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, ...) { @@ -529,7 +549,7 @@ final: } // Process the received packet -void OvsProceccRecvPacket(OPENVPN_SERVER *s, UDPPACKET *p) +void OvsProceccRecvPacket(OPENVPN_SERVER *s, UDPPACKET *p, UINT protocol) { OPENVPN_CHANNEL *c; OPENVPN_SESSION *se; @@ -541,7 +561,7 @@ void OvsProceccRecvPacket(OPENVPN_SERVER *s, UDPPACKET *p) } // Search for the session - se = OvsFindOrCreateSession(s, &p->DstIP, p->DestPort, &p->SrcIP, p->SrcPort, p->Type); + se = OvsFindOrCreateSession(s, &p->DstIP, p->DestPort, &p->SrcIP, p->SrcPort, protocol); if (se == NULL) { return; @@ -771,8 +791,13 @@ void OvsProcessRecvControlPacket(OPENVPN_SERVER *s, OPENVPN_SESSION *se, OPENVPN // Create an SSL pipe Lock(s->Cedar->lock); { - bool cert_verify = true; - c->SslPipe = NewSslPipeEx(true, s->Cedar->ServerX, s->Cedar->ServerK, s->Dh, cert_verify, &c->ClientCert); + if (s->Dh->Size != s->Cedar->DhParamBits) + { + DhFree(s->Dh); + s->Dh = DhNewFromBits(s->Cedar->DhParamBits); + } + + c->SslPipe = NewSslPipeEx(true, s->Cedar->ServerX, s->Cedar->ServerK, s->Dh, true, &c->ClientCert); } Unlock(s->Cedar->lock); @@ -2187,7 +2212,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) +void OvsRecvPacket(OPENVPN_SERVER *s, LIST *recv_packet_list, UINT protocol) { UINT i, j; LIST *delete_session_list = NULL; @@ -2219,7 +2244,7 @@ void OvsRecvPacket(OPENVPN_SERVER *s, LIST *recv_packet_list) { UDPPACKET *p = LIST_DATA(recv_packet_list, i); - OvsProceccRecvPacket(s, p); + OvsProceccRecvPacket(s, p, protocol); } // Treat for all sessions and all channels @@ -2924,12 +2949,13 @@ OPENVPN_SERVER *NewOpenVpnServer(CEDAR *cedar, INTERRUPT_MANAGER *interrupt, SOC s->SendPacketList = NewListFast(NULL); s->Now = Tick64(); + s->Giveup = s->Now + OPENVPN_NEW_SESSION_DEADLINE_TIMEOUT; s->NextSessionId = 1; OvsLog(s, NULL, NULL, "LO_START"); - s->Dh = DhNewFromBits(DH_PARAM_BITS_DEFAULT); + s->Dh = DhNewFromBits(cedar->DhParamBits); return s; } @@ -2977,123 +3003,3 @@ void FreeOpenVpnServer(OPENVPN_SERVER *s) Free(s); } - -// UDP reception procedure -void OpenVpnServerUdpListenerProc(UDPLISTENER *u, LIST *packet_list) -{ - OPENVPN_SERVER_UDP *us; - // Validate arguments - if (u == NULL || packet_list == NULL) - { - return; - } - - us = (OPENVPN_SERVER_UDP *)u->Param; - - if (us->OpenVpnServer != NULL) - { - { - u->PollMyIpAndPort = false; - - ClearStr(us->Cedar->OpenVPNPublicPorts, sizeof(us->Cedar->OpenVPNPublicPorts)); - } - - OvsRecvPacket(us->OpenVpnServer, packet_list); - - UdpListenerSendPackets(u, us->OpenVpnServer->SendPacketList); - DeleteAll(us->OpenVpnServer->SendPacketList); - } -} - -// Create an OpenVPN server (UDP mode) -OPENVPN_SERVER_UDP *NewOpenVpnServerUdp(CEDAR *cedar) -{ - OPENVPN_SERVER_UDP *u; - // Validate arguments - if (cedar == NULL) - { - return NULL; - } - - u = ZeroMalloc(sizeof(OPENVPN_SERVER_UDP)); - - u->Cedar = cedar; - - AddRef(u->Cedar->ref); - - // Create a UDP listener - 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); - - return u; -} - -void OpenVpnServerUdpSetDhParam(OPENVPN_SERVER_UDP *u, DH_CTX *dh) -{ - // Validate arguments - if (u == NULL) { - return; - } - - if (u->OpenVpnServer->Dh) - { - DhFree(u->OpenVpnServer->Dh); - } - - u->OpenVpnServer->Dh = dh; -} - -// Apply the port list to the OpenVPN server -void OvsApplyUdpPortList(OPENVPN_SERVER_UDP *u, char *port_list, IP *listen_ip) -{ - LIST *o; - UINT i; - // Validate arguments - if (u == NULL) - { - return; - } - - DeleteAllPortFromUdpListener(u->UdpListener); - - if (u->UdpListener != NULL && listen_ip != NULL) - { - Copy(&u->UdpListener->ListenIP, listen_ip, sizeof(IP)); - } - - o = StrToIntList(port_list, true); - - for (i = 0;i < LIST_NUM(o);i++) - { - UINT port = *((UINT *)LIST_DATA(o, i)); - - if (port >= 1 && port <= 65535) - { - AddPortToUdpListener(u->UdpListener, port); - } - } - - ReleaseIntList(o); -} - -// Release the OpenVPN server (UDP mode) -void FreeOpenVpnServerUdp(OPENVPN_SERVER_UDP *u) -{ - // Validate arguments - if (u == NULL) - { - return; - } - - // Stop the UDP listener - FreeUdpListener(u->UdpListener); - - // Release the OpenVPN server - FreeOpenVpnServer(u->OpenVpnServer); - - ReleaseCedar(u->Cedar); - - Free(u); -} diff --git a/src/Cedar/Proto_OpenVPN.h b/src/Cedar/Proto_OpenVPN.h index 8fae45f3..39324b84 100644 --- a/src/Cedar/Proto_OpenVPN.h +++ b/src/Cedar/Proto_OpenVPN.h @@ -194,6 +194,7 @@ struct OPENVPN_SERVER LIST *SendPacketList; // Transmission packet list LIST *SessionList; // Session list UINT64 Now; // Current time + UINT64 Giveup; // Session establishment deadline SOCK_EVENT *SockEvent; // Socket event UCHAR TmpBuf[OPENVPN_TMP_BUFFER_SIZE]; // Temporary buffer UINT DisconnectCount; // The number of session lost that have occurred so far @@ -203,15 +204,6 @@ struct OPENVPN_SERVER UINT SessionEstablishedCount; // Number of session establishment }; -// OpenVPN server (UDP mode) -struct OPENVPN_SERVER_UDP -{ - CEDAR *Cedar; - UDPLISTENER *UdpListener; // UDP listener - OPENVPN_SERVER *OpenVpnServer; // OpenVPN server - UINT64 VgsNextGetPublicPortsTick; -}; - // 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" @@ -220,22 +212,17 @@ 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); +bool OvsIsPacketForMe(const PROTO_MODE mode, const UCHAR *data, const UINT size); +bool OvsProcessData(void *param, TCP_RAW_DATA *in, FIFO *out); +bool OvsProcessDatagrams(void *param, LIST *in, LIST *out); 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); -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); -void OvsProceccRecvPacket(OPENVPN_SERVER *s, UDPPACKET *p); +void OvsRecvPacket(OPENVPN_SERVER *s, LIST *recv_packet_list, UINT protocol); +void OvsProceccRecvPacket(OPENVPN_SERVER *s, UDPPACKET *p, UINT protocol); 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); @@ -279,6 +266,4 @@ UINT OvsCalcTcpMss(OPENVPN_SERVER *s, OPENVPN_SESSION *se, OPENVPN_CHANNEL *c); CIPHER *OvsGetCipher(char *name); MD *OvsGetMd(char *name); -void OpenVpnServerUdpSetDhParam(OPENVPN_SERVER_UDP *u, DH_CTX *dh); - #endif // PROTO_OPENVPN_H diff --git a/src/Cedar/Server.c b/src/Cedar/Server.c index 8f43404a..417a4480 100644 --- a/src/Cedar/Server.c +++ b/src/Cedar/Server.c @@ -33,6 +33,8 @@ void SiSetOpenVPNAndSSTPConfig(SERVER *s, OPENVPN_SSTP_CONFIG *c) Lock(s->OpenVpnSstpConfigLock); { + LIST *ports; + // Save the settings if (s->Cedar->Bridge || s->ServerType != SERVER_TYPE_STANDALONE) { @@ -45,24 +47,14 @@ void SiSetOpenVPNAndSSTPConfig(SERVER *s, OPENVPN_SSTP_CONFIG *c) s->DisableOpenVPNServer = !c->EnableOpenVPN; } - NormalizeIntListStr(s->OpenVpnServerUdpPorts, sizeof(s->OpenVpnServerUdpPorts), - c->OpenVPNPortList, true, ", "); + // TODO: Now that we have a unified protocol interface (Proto), the setting's name should be changed. + NormalizeIntListStr(s->OpenVpnServerUdpPorts, sizeof(s->OpenVpnServerUdpPorts), c->OpenVPNPortList, true, ", "); + ports = StrToIntList(s->OpenVpnServerUdpPorts, true); + ProtoSetUdpPorts(s->Proto, ports); + ReleaseIntList(ports); s->Cedar->OpenVPNObfuscation = c->OpenVPNObfuscation; StrCpy(s->Cedar->OpenVPNObfuscationMask, sizeof(s->Cedar->OpenVPNObfuscationMask), c->OpenVPNObfuscationMask); - - // Apply the OpenVPN configuration - if (s->OpenVpnServerUdp != NULL) - { - if (s->DisableOpenVPNServer) - { - OvsApplyUdpPortList(s->OpenVpnServerUdp, "", NULL); - } - else - { - OvsApplyUdpPortList(s->OpenVpnServerUdp, s->OpenVpnServerUdpPorts, &s->ListenIP); - } - } } Unlock(s->OpenVpnSstpConfigLock); } @@ -2629,8 +2621,6 @@ void SiInitConfiguration(SERVER *s) s->Proto = ProtoNew(s->Cedar); // IPsec server s->IPsecServer = NewIPsecServer(s->Cedar); - // OpenVPN server (UDP) - s->OpenVpnServerUdp = NewOpenVpnServerUdp(s->Cedar); } SLog(s->Cedar, "LS_LOAD_CONFIG_1"); @@ -5660,7 +5650,9 @@ void SiLoadServerCfg(SERVER *s, FOLDER *f) } s->DontBackupConfig = CfgGetBool(f, "DontBackupConfig"); + CfgGetIp(f, "ListenIP", &s->ListenIP); + ProtoSetListenIP(s->Proto, &s->ListenIP); if (CfgIsItem(f, "BackupConfigOnlyWhenModified")) { @@ -6036,10 +6028,6 @@ void SiLoadServerCfg(SERVER *s, FOLDER *f) } SetDhParam(DhNewFromBits(c->DhParamBits)); - if (s->OpenVpnServerUdp) - { - OpenVpnServerUdpSetDhParam(s->OpenVpnServerUdp, DhNewFromBits(c->DhParamBits)); - } } Unlock(c->lock); @@ -6549,14 +6537,6 @@ void SiFreeConfiguration(SERVER *s) s->IPsecServer = NULL; } - // Terminate the OpenVPN server - if (s->OpenVpnServerUdp != NULL) - { - FreeOpenVpnServerUdp(s->OpenVpnServerUdp); - s->OpenVpnServerUdp = NULL; - } - - // Terminate the DDNS client if (s->DDnsClient != NULL) { diff --git a/src/Cedar/Server.h b/src/Cedar/Server.h index 75225ecf..cd26634d 100644 --- a/src/Cedar/Server.h +++ b/src/Cedar/Server.h @@ -244,7 +244,6 @@ struct SERVER PROTO *Proto; // Protocols handler IPSEC_SERVER *IPsecServer; // IPsec server function - OPENVPN_SERVER_UDP *OpenVpnServerUdp; // OpenVPN server function char OpenVpnServerUdpPorts[MAX_SIZE]; // UDP port list string DDNS_CLIENT *DDnsClient; // DDNS client feature LOCK *OpenVpnSstpConfigLock; // Lock OpenVPN and SSTP configuration diff --git a/src/Mayaqua/Network.c b/src/Mayaqua/Network.c index 717d0f9d..14c1f325 100644 --- a/src/Mayaqua/Network.c +++ b/src/Mayaqua/Network.c @@ -19329,6 +19329,19 @@ UDPLISTENER *NewUdpListenerEx(UDPLISTENER_RECV_PROC *recv_proc, void *param, IP return u; } +// Stop the UDP listener +void StopUdpListener(UDPLISTENER *u) +{ + if (u == NULL) + { + return; + } + + u->Halt = true; + SetSockEvent(u->Event); + WaitThread(u->Thread, INFINITE); +} + // Release the UDP listener void FreeUdpListener(UDPLISTENER *u) { @@ -19339,10 +19352,8 @@ void FreeUdpListener(UDPLISTENER *u) return; } - u->Halt = true; - SetSockEvent(u->Event); + StopUdpListener(u); - WaitThread(u->Thread, INFINITE); ReleaseThread(u->Thread); ReleaseSockEvent(u->Event); diff --git a/src/Mayaqua/Network.h b/src/Mayaqua/Network.h index 1744ff70..b99f179a 100644 --- a/src/Mayaqua/Network.h +++ b/src/Mayaqua/Network.h @@ -1357,6 +1357,7 @@ 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 StopUdpListener(UDPLISTENER *u); void FreeUdpListener(UDPLISTENER *u); void AddPortToUdpListener(UDPLISTENER *u, UINT port); void DeletePortFromUdpListener(UDPLISTENER *u, UINT port);