mirror of
https://github.com/SoftEtherVPN/SoftEtherVPN.git
synced 2025-04-03 18:00:08 +03:00
In addition to saving 4 bytes for each instantiation, this change makes IP-related operations faster and clearer. https://tools.ietf.org/html/rfc3493.html#section-3.7
763 lines
16 KiB
C
763 lines
16 KiB
C
#include "Proto.h"
|
|
|
|
#include "Cedar.h"
|
|
#include "Logging.h"
|
|
#include "Proto_OpenVPN.h"
|
|
#include "Proto_SSTP.h"
|
|
#include "Proto_WireGuard.h"
|
|
#include "Server.h"
|
|
|
|
#include "Mayaqua/Internat.h"
|
|
#include "Mayaqua/Kernel.h"
|
|
#include "Mayaqua/Memory.h"
|
|
#include "Mayaqua/Object.h"
|
|
#include "Mayaqua/Str.h"
|
|
#include "Mayaqua/Table.h"
|
|
|
|
void ProtoLog(const PROTO *proto, const PROTO_SESSION *session, const char *name, ...)
|
|
{
|
|
wchar_t message[MAX_SIZE * 2];
|
|
|
|
if (proto == NULL)
|
|
{
|
|
return;
|
|
}
|
|
|
|
if (session != NULL)
|
|
{
|
|
wchar_t *proto_name;
|
|
UINT current_len;
|
|
va_list args;
|
|
|
|
proto_name = CopyStrToUni(session->Impl->Name());
|
|
UniFormat(message, sizeof(message), _UU("LP_PREFIX_SESSION"), proto_name, &session->SrcIp, session->SrcPort, &session->DstIp, session->DstPort, L"UDP");
|
|
Free(proto_name);
|
|
|
|
current_len = UniStrLen(message);
|
|
|
|
va_start(args, name);
|
|
UniFormatArgs(message + current_len, sizeof(message) - current_len, _UU(name), args);
|
|
va_end(args);
|
|
}
|
|
else
|
|
{
|
|
va_list args;
|
|
|
|
UniStrCpy(message, sizeof(message), _UU("LP_PREFIX_SESSION"));
|
|
UniStrCat(message, sizeof(message), _UU(name));
|
|
|
|
va_start(args, name);
|
|
UniFormatArgs(message, sizeof(message), message, args);
|
|
va_end(args);
|
|
}
|
|
|
|
WriteServerLog(proto->Cedar, message);
|
|
}
|
|
|
|
int ProtoOptionCompare(void *p1, void *p2)
|
|
{
|
|
PROTO_OPTION *option_1, *option_2;
|
|
|
|
if (p1 == NULL || p2 == NULL)
|
|
{
|
|
return (p1 == NULL && p2 == NULL ? 0 : (p1 == NULL ? -1 : 1));
|
|
}
|
|
|
|
option_1 = *(PROTO_OPTION **)p1;
|
|
option_2 = *(PROTO_OPTION **)p2;
|
|
|
|
return StrCmpi(option_1->Name, option_2->Name);
|
|
}
|
|
|
|
int ProtoContainerCompare(void *p1, void *p2)
|
|
{
|
|
PROTO_CONTAINER *container_1, *container_2;
|
|
|
|
if (p1 == NULL || p2 == NULL)
|
|
{
|
|
return (p1 == NULL && p2 == NULL ? 0 : (p1 == NULL ? -1 : 1));
|
|
}
|
|
|
|
container_1 = *(PROTO_CONTAINER **)p1;
|
|
container_2 = *(PROTO_CONTAINER **)p2;
|
|
|
|
return StrCmpi(container_1->Name, container_2->Name);
|
|
}
|
|
|
|
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;
|
|
for (BYTE i = 0; i < sizeof(ip->address); ++i)
|
|
{
|
|
ret += ip->address[i];
|
|
}
|
|
|
|
ret += ip->ipv6_scope_id;
|
|
ret += session->SrcPort;
|
|
|
|
ip = &session->DstIp;
|
|
for (BYTE i = 0; i < sizeof(ip->address); ++i)
|
|
{
|
|
ret += ip->address[i];
|
|
}
|
|
|
|
ret += ip->ipv6_scope_id;
|
|
ret += session->DstPort;
|
|
|
|
return ret;
|
|
}
|
|
|
|
bool ProtoEnabled(const PROTO *proto, const char *name)
|
|
{
|
|
PROTO_OPTION *option, tmp_o;
|
|
PROTO_CONTAINER *container, tmp_c;
|
|
|
|
if (proto == NULL || name == NULL)
|
|
{
|
|
return false;
|
|
}
|
|
|
|
tmp_c.Name = name;
|
|
|
|
container = Search(proto->Containers, &tmp_c);
|
|
if (container == NULL)
|
|
{
|
|
return false;
|
|
}
|
|
|
|
tmp_o.Name = PROTO_OPTION_TOGGLE_NAME;
|
|
|
|
option = Search(container->Options, &tmp_o);
|
|
if (option == NULL || option->Type != PROTO_OPTION_BOOL)
|
|
{
|
|
return false;
|
|
}
|
|
|
|
return option->Bool;
|
|
}
|
|
|
|
PROTO *ProtoNew(CEDAR *cedar)
|
|
{
|
|
PROTO *proto;
|
|
|
|
if (cedar == NULL)
|
|
{
|
|
return NULL;
|
|
}
|
|
|
|
proto = Malloc(sizeof(PROTO));
|
|
proto->Cedar = cedar;
|
|
proto->Containers = NewList(ProtoContainerCompare);
|
|
proto->Sessions = NewHashList(ProtoSessionHash, ProtoSessionCompare, 0, true);
|
|
|
|
AddRef(cedar->ref);
|
|
|
|
// WireGuard
|
|
Add(proto->Containers, ProtoContainerNew(WgsGetProtoImpl()));
|
|
// OpenVPN
|
|
Add(proto->Containers, ProtoContainerNew(OvsGetProtoImpl()));
|
|
// SSTP
|
|
Add(proto->Containers, ProtoContainerNew(SstpGetProtoImpl()));
|
|
|
|
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)
|
|
{
|
|
ProtoSessionDelete(LIST_DATA(proto->Sessions->AllList, i));
|
|
}
|
|
ReleaseHashList(proto->Sessions);
|
|
|
|
for (i = 0; i < LIST_NUM(proto->Containers); ++i)
|
|
{
|
|
ProtoContainerDelete(LIST_DATA(proto->Containers, i));
|
|
}
|
|
ReleaseList(proto->Containers);
|
|
|
|
FreeUdpListener(proto->UdpListener);
|
|
ReleaseCedar(proto->Cedar);
|
|
Free(proto);
|
|
}
|
|
|
|
PROTO_CONTAINER *ProtoContainerNew(const PROTO_IMPL *impl)
|
|
{
|
|
UINT i;
|
|
PROTO_OPTION *option;
|
|
PROTO_CONTAINER *container;
|
|
const PROTO_OPTION *impl_options;
|
|
|
|
if (impl == NULL)
|
|
{
|
|
return false;
|
|
}
|
|
|
|
container = Malloc(sizeof(PROTO_CONTAINER));
|
|
container->Name = impl->Name();
|
|
container->Options = NewList(ProtoOptionCompare);
|
|
container->Impl = impl;
|
|
|
|
option = ZeroMalloc(sizeof(PROTO_OPTION));
|
|
option->Name = PROTO_OPTION_TOGGLE_NAME;
|
|
option->Type = PROTO_OPTION_BOOL;
|
|
option->Bool = true;
|
|
|
|
Add(container->Options, option);
|
|
|
|
impl_options = impl->Options();
|
|
|
|
for (i = 0; impl_options[i].Name != NULL; ++i)
|
|
{
|
|
const PROTO_OPTION *impl_option = &impl_options[i];
|
|
|
|
option = ZeroMalloc(sizeof(PROTO_OPTION));
|
|
option->Name = impl_option->Name;
|
|
option->Type = impl_option->Type;
|
|
|
|
switch (impl_option->Type)
|
|
{
|
|
case PROTO_OPTION_BOOL:
|
|
option->Bool = impl_option->Bool;
|
|
break;
|
|
case PROTO_OPTION_STRING:
|
|
option->String = impl_option->String != NULL ? CopyStr(impl_option->String) : impl->OptionStringValue(option->Name);
|
|
break;
|
|
default:
|
|
Debug("ProtoContainerNew(): unhandled option type %u!\n", impl_option->Type);
|
|
Free(option);
|
|
continue;
|
|
}
|
|
|
|
Add(container->Options, option);
|
|
}
|
|
|
|
Debug("ProtoContainerNew(): %s\n", container->Name);
|
|
|
|
return container;
|
|
}
|
|
|
|
void ProtoContainerDelete(PROTO_CONTAINER *container)
|
|
{
|
|
UINT i;
|
|
LIST *options;
|
|
|
|
if (container == NULL)
|
|
{
|
|
return;
|
|
}
|
|
|
|
options = container->Options;
|
|
|
|
for (i = 0; i < LIST_NUM(options); ++i)
|
|
{
|
|
PROTO_OPTION *option = LIST_DATA(options, i);
|
|
if (option->Type == PROTO_OPTION_STRING)
|
|
{
|
|
Free(option->String);
|
|
}
|
|
|
|
Free(option);
|
|
}
|
|
|
|
ReleaseList(options);
|
|
Free(container);
|
|
}
|
|
|
|
const PROTO_CONTAINER *ProtoDetect(const PROTO *proto, const PROTO_MODE mode, const UCHAR *data, const UINT size)
|
|
{
|
|
UINT i;
|
|
|
|
if (proto == NULL || data == NULL || size == 0)
|
|
{
|
|
return NULL;
|
|
}
|
|
|
|
for (i = 0; i < LIST_NUM(proto->Containers); ++i)
|
|
{
|
|
const PROTO_CONTAINER *container = LIST_DATA(proto->Containers, i);
|
|
const PROTO_IMPL *impl = container->Impl;
|
|
|
|
if (ProtoEnabled(proto, container->Name) == false)
|
|
{
|
|
Debug("ProtoDetect(): skipping %s because it's disabled\n", container->Name);
|
|
continue;
|
|
}
|
|
|
|
if (impl->IsPacketForMe != NULL && impl->IsPacketForMe(mode, data, size))
|
|
{
|
|
Debug("ProtoDetect(): %s detected\n", container->Name);
|
|
return container;
|
|
}
|
|
}
|
|
|
|
Debug("ProtoDetect(): unrecognized protocol\n");
|
|
return NULL;
|
|
}
|
|
|
|
PROTO_SESSION *ProtoSessionNew(const PROTO *proto, const PROTO_CONTAINER *container, const IP *src_ip, const USHORT src_port, const IP *dst_ip, const USHORT dst_port)
|
|
{
|
|
LIST *options;
|
|
PROTO_SESSION *session;
|
|
const PROTO_IMPL *impl;
|
|
|
|
if (container == NULL || src_ip == NULL || src_port == 0 || dst_ip == NULL || dst_port == 0)
|
|
{
|
|
return NULL;
|
|
}
|
|
|
|
options = container->Options;
|
|
impl = container->Impl;
|
|
|
|
session = ZeroMalloc(sizeof(PROTO_SESSION));
|
|
session->SockEvent = NewSockEvent();
|
|
session->InterruptManager = NewInterruptManager();
|
|
|
|
LockList(options);
|
|
|
|
if (impl->Init != NULL && impl->Init(&session->Param, container->Options, proto->Cedar, session->InterruptManager, session->SockEvent, NULL, NULL) == false)
|
|
{
|
|
Debug("ProtoNewSession(): failed to initialize %s\n", container->Name);
|
|
|
|
UnlockList(options);
|
|
ReleaseSockEvent(session->SockEvent);
|
|
FreeInterruptManager(session->InterruptManager);
|
|
Free(session);
|
|
|
|
return NULL;
|
|
}
|
|
|
|
UnlockList(options);
|
|
|
|
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);
|
|
|
|
ProtoLog(proto, session, "LP_SESSION_CREATED");
|
|
|
|
return session;
|
|
}
|
|
|
|
void ProtoSessionDelete(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);
|
|
|
|
ProtoLog(session->Proto, session, "LP_SESSION_DELETED");
|
|
|
|
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 true;
|
|
}
|
|
|
|
bool ProtoHandleConnection(PROTO *proto, SOCK *sock, const char *protocol)
|
|
{
|
|
const PROTO_IMPL *impl;
|
|
void *impl_data = NULL;
|
|
|
|
UCHAR *buf;
|
|
TCP_RAW_DATA *recv_raw_data;
|
|
FIFO *send_fifo;
|
|
INTERRUPT_MANAGER *im;
|
|
SOCK_EVENT *se;
|
|
|
|
if (proto == NULL || sock == NULL)
|
|
{
|
|
return false;
|
|
}
|
|
|
|
{
|
|
const PROTO_CONTAINER *container = NULL;
|
|
wchar_t *proto_name;
|
|
LIST *options;
|
|
|
|
if (protocol != NULL)
|
|
{
|
|
UINT i;
|
|
for (i = 0; i < LIST_NUM(proto->Containers); ++i)
|
|
{
|
|
const PROTO_CONTAINER *tmp = LIST_DATA(proto->Containers, i);
|
|
if (StrCmp(tmp->Name, protocol) == 0)
|
|
{
|
|
container = tmp;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
UCHAR tmp[PROTO_CHECK_BUFFER_SIZE];
|
|
|
|
if (Peek(sock, tmp, sizeof(tmp)) == 0)
|
|
{
|
|
return false;
|
|
}
|
|
|
|
container = ProtoDetect(proto, PROTO_MODE_TCP, tmp, sizeof(tmp));
|
|
}
|
|
|
|
if (container == NULL)
|
|
{
|
|
return false;
|
|
}
|
|
|
|
options = container->Options;
|
|
impl = container->Impl;
|
|
|
|
im = NewInterruptManager();
|
|
se = NewSockEvent();
|
|
|
|
LockList(options);
|
|
|
|
if (impl->Init != NULL && impl->Init(&impl_data, options, proto->Cedar, im, se, sock->CipherName, sock->RemoteHostname) == false)
|
|
{
|
|
Debug("ProtoHandleConnection(): failed to initialize %s\n", container->Name);
|
|
|
|
UnlockList(options);
|
|
FreeInterruptManager(im);
|
|
ReleaseSockEvent(se);
|
|
|
|
return false;
|
|
}
|
|
|
|
UnlockList(options);
|
|
|
|
proto_name = CopyStrToUni(container->Name);
|
|
ProtoLog(proto, NULL, "LP_SESSION_CREATED", proto_name, &sock->RemoteIP, sock->RemotePort, &sock->LocalIP, sock->LocalPort, L"TCP");
|
|
Free(proto_name);
|
|
}
|
|
|
|
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, sock->SecureMode);
|
|
|
|
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), sock->SecureMode);
|
|
|
|
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);
|
|
}
|
|
}
|
|
|
|
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);
|
|
|
|
{
|
|
wchar_t *proto_name = CopyStrToUni(impl->Name());
|
|
ProtoLog(proto, NULL, "LP_SESSION_DELETED", proto_name, &sock->RemoteIP, sock->RemotePort, &sock->LocalIP, sock->LocalPort, L"TCP");
|
|
Free(proto_name);
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
void ProtoHandleDatagrams(UDPLISTENER *listener, LIST *datagrams)
|
|
{
|
|
UINT i;
|
|
PROTO *proto;
|
|
HASH_LIST *sessions;
|
|
|
|
if (listener == NULL || datagrams == NULL)
|
|
{
|
|
return;
|
|
}
|
|
|
|
proto = listener->Param;
|
|
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)
|
|
{
|
|
const PROTO_CONTAINER *container = ProtoDetect(proto, PROTO_MODE_UDP, datagram->Data, datagram->Size);
|
|
if (container == NULL)
|
|
{
|
|
continue;
|
|
}
|
|
|
|
session = ProtoSessionNew(proto, container, &tmp.SrcIp, tmp.SrcPort, &tmp.DstIp, tmp.DstPort);
|
|
if (session == NULL)
|
|
{
|
|
continue;
|
|
}
|
|
|
|
AddHash(proto->Sessions, session);
|
|
}
|
|
|
|
Lock(session->Lock);
|
|
{
|
|
if (session->Halt == false)
|
|
{
|
|
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 (session->Halt)
|
|
{
|
|
DeleteHash(sessions, session);
|
|
ProtoSessionDelete(session);
|
|
continue;
|
|
}
|
|
|
|
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)
|
|
{
|
|
UINT interval;
|
|
void *param = session->Param;
|
|
const PROTO_IMPL *impl = session->Impl;
|
|
LIST *received = session->DatagramsIn;
|
|
LIST *to_send = session->DatagramsOut;
|
|
|
|
Lock(session->Lock);
|
|
{
|
|
UINT i;
|
|
|
|
session->Halt = impl->ProcessDatagrams(param, received, to_send) == false;
|
|
|
|
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 (session->Halt)
|
|
{
|
|
Debug("ProtoSessionThread(): breaking main loop\n");
|
|
break;
|
|
}
|
|
|
|
// Wait until the next event occurs
|
|
interval = GetNextIntervalForInterrupt(session->InterruptManager);
|
|
interval = MIN(interval, UDPLISTENER_WAIT_INTERVAL);
|
|
WaitSockEvent(session->SockEvent, interval);
|
|
}
|
|
}
|