mirror of
https://github.com/SoftEtherVPN/SoftEtherVPN.git
synced 2025-07-15 20:25:00 +03:00
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.
This commit is contained in:
271
src/Cedar/Proto.c
Normal file
271
src/Cedar/Proto.c
Normal file
@ -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;
|
||||
}
|
Reference in New Issue
Block a user