mirror of
https://github.com/SoftEtherVPN/SoftEtherVPN.git
synced 2024-11-23 01:49:53 +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:
parent
3f9b610c80
commit
7d58e6bf60
@ -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();
|
||||
}
|
||||
|
@ -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 <Cedar/IPC.h>
|
||||
// Third party protocols
|
||||
#include <Cedar/Proto.h>
|
||||
#include <Cedar/Proto_IPsec.h>
|
||||
#include <Cedar/Proto_EtherIP.h>
|
||||
#include <Cedar/Proto_IkePacket.h>
|
||||
|
@ -650,6 +650,10 @@
|
||||
RelativePath=".\NullLan.c"
|
||||
>
|
||||
</File>
|
||||
<File
|
||||
RelativePath=".\Proto.c"
|
||||
>
|
||||
</File>
|
||||
<File
|
||||
RelativePath=".\Proto_EtherIP.c"
|
||||
>
|
||||
@ -1280,6 +1284,10 @@
|
||||
RelativePath=".\NullLan.h"
|
||||
>
|
||||
</File>
|
||||
<File
|
||||
RelativePath=".\Proto.h"
|
||||
>
|
||||
</File>
|
||||
<File
|
||||
RelativePath=".\Proto_EtherIP.h"
|
||||
>
|
||||
|
@ -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
|
||||
|
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;
|
||||
}
|
44
src/Cedar/Proto.h
Normal file
44
src/Cedar/Proto.h
Normal file
@ -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
|
@ -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);
|
||||
|
||||
|
@ -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;
|
||||
|
@ -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)
|
||||
{
|
||||
|
@ -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);
|
||||
|
Loading…
Reference in New Issue
Block a user