It's cleared from all stuff like custom protocol/encryption, and here is a simple proxy for 1 client and 1 connection with hardcoded remote server.
It can however be easily modified for multiple client connections -- this depends on number of CLIENT instances and their handling in loop (or multiple remote servers also).
The example is configured to be a telnet proxy (for simplicity), you can test it this way:
1. Running following code from debugger or compiled exe and allowing it go through firewall
2. Executing windows telnet app and making connection to localhost:23 or 127.0.0.1:23
Then telnet connection will go through proxy and be redirected to some telnet server I picked from google for tests ^^
Code: Select all
EnableExplicit
; Raw TCP proxy example
; Inspired by http://www.martinbroadhurst.com/source/tcpproxy.c.html
; 2016 (c) Lunasole
;{ Network stuff }
; this has to be 1500 maximum, if transfering over internet. else packets becoming fragmented
; but well, maybe fragmentation is not a problem for raw proxy. need to try
#TCP_SIZE = 1024 ; 4096 ; used to transfer TCP data
; a structure to represent IP adress as single long and array of 4 bytes
Structure IP4Adress
StructureUnion
IP.l
BYTE.a [4]
EndStructureUnion
EndStructure
; A structure used to represent both client and server instances
Structure NETPOINT
IP.IP4Adress ; the IPV4 adress info [client mode]
PORT.i ; local port [if server's client] or server port [if server]
HANDLE.i ; a handle representing connection of this object
LEvent.i ; last network event which occured on this object
State.l ; if 1, then connection was established and this structure should be valid
TimeStamp.i ; used to track last activity time for this connection [last time data received]
RetryStamp.i ; used on connection establish/reconnect
EndStructure
; ----------------------------------------------------------
; some common network routines
; ----------------------------------------------------------
; receives hostname/IP adress string and resolves it to IP4 adress [long], then converts to string
Procedure$ GetIPByHost(Host$="")
Protected Result.l = 0
If Host$=""
Host$=Hostname()
EndIf
Protected SelfLoop = OpenNetworkConnection(Host$,0 , #PB_Network_UDP)
If SelfLoop
Result=GetClientIP(SelfLoop)
CloseNetworkConnection(SelfLoop)
EndIf
ProcedureReturn IPString (Result)
EndProcedure
; connect to specified server using TCP
; RETURN: on success 1, *lpData will contain the connection data
Procedure net_start (*lpData.NETPOINT, Server.s, ServerPort)
Protected HANDLE.i = OpenNetworkConnection(Server, ServerPort , #PB_Network_TCP, 30000)
If HANDLE
*lpData\HANDLE = HANDLE
*lpData\State = 1
*lpData\TimeStamp = ElapsedMilliseconds()
*lpData\RetryStamp = 0
ProcedureReturn 1
EndIf
EndProcedure
; zeroes specified connection data [should be called by server after #PB_NetworkEvent_Disconnect]
Procedure net_cls (*lpData.NETPOINT)
ClearStructure(*lpData, NETPOINT)
EndProcedure
; close specified connection and zeroes related structure. includes net_cls() call
Procedure net_stop (*lpData.NETPOINT)
If *lpData\HANDLE And *lpData\State
CloseNetworkConnection(*lpData\HANDLE)
EndIf
net_cls (*lpData)
EndProcedure
; used by server to receive some client data
Procedure srv_getClient (HANDLE, *lpData.NETPOINT)
*lpData\HANDLE = HANDLE
*lpData\IP\IP = GetClientIP (HANDLE)
*lpData\PORT = GetClientPort(HANDLE)
*lpData\State = 1
*lpData\TimeStamp = ElapsedMilliseconds()
*lpData\RetryStamp = 0
EndProcedure
; used by server: starts new server using specified port
; RETURN: 1 on success; *lpData contains server data
Procedure srv_start (*lpData.NETPOINT, Port)
Protected HANDLE.i = CreateNetworkServer(#PB_Any, Port, #PB_Network_TCP)
If HANDLE
*lpData\HANDLE = HANDLE
*lpData\State = 1
*lpData\TimeStamp = ElapsedMilliseconds()
*lpData\RetryStamp = 0
ProcedureReturn 1
EndIf
EndProcedure
; used by server: stop specified server and clear related data structure. includes net_cls() call
Procedure srv_stop (*lpData.NETPOINT)
If *lpData\HANDLE
CloseNetworkServer(*lpData\HANDLE)
ClearStructure(*lpData, NETPOINT)
EndIf
EndProcedure
; ----------------------------------------------------------
; TCP proxy
; ----------------------------------------------------------
; Transfer all data from network buffer "from_id", to "to_id"
; Params are connection handles
; RETURN: size of transfered data on success
; -1 If nothing sent
; -2 if receiver buffer is full [?]
; -3 if from_id or to_id is null or their state = 0
Procedure DataTransfer(*source.NETPOINT, *target.NETPOINT)
Protected Dim buf.a (#TCP_SIZE)
Protected state, transfer, bytes_read, bytes_written
Protected from_id = *source\HANDLE
Protected to_id = *target\HANDLE
If *source\HANDLE And *target\HANDLE And *source\STATE And *target\STATE
bytes_read = ReceiveNetworkData (from_id, @buf(0), #TCP_SIZE)
While Not bytes_read <= 0
bytes_written = SendNetworkData (to_id, @buf(0), bytes_read)
If bytes_written = -1 ; nothing can be sent
state = -1 : Break
ElseIf bytes_written < bytes_read ; the receiver buffer is probably full
transfer + bytes_written
state = -2 : Break
ElseIf bytes_written = #TCP_SIZE ; it's all ok, continue
transfer + bytes_read
bytes_read = ReceiveNetworkData (from_id, @buf(0), #TCP_SIZE); continue
Else
transfer + bytes_read ; no more data to transfer
Break
EndIf
Wend
Else
state = -3 ; from_id or to_id is null
EndIf
If state
ProcedureReturn state
Else
ProcedureReturn transfer
EndIf
EndProcedure
;}
Global CLIENT.NETPOINT ; connection to CLIENT
Global THIS_SRV.NETPOINT ; this server instance
Global REMOTE.NETPOINT ; connection to remote host
#THIS_PORT = 23 ; this server port (client should connect to it through 127.0.0.1:port)
#REMOTE_HOST = "news.thundernews.com" ; remote host address (data from client will be transfered to it)
#REMOTE_PORT = 23 ; remote host port
; Misc
#NET_LIFETIME = 3 * 60 * 1000 ; connection timeout, if there is no activity on connection within it, connection will be closed
#NET_RETRYTIME = 1 * 60 * 1000 ; a timeout between remote host connection attempts
#NET_FAST = 1 ; the delay in loop to not load CPU for 100%
#ENABLE_GUI = 1 ; if 1, use console window for output, else use debug
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
If #ENABLE_GUI
OpenConsole("Proxy example by Lunasole")
EndIf
Procedure log_add (text$, color = 0)
If #ENABLE_GUI
If color
ConsoleColor(color, 0)
Else
ConsoleColor(7, 0)
EndIf
PrintN(text$)
Else
Debug text$
EndIf
EndProcedure
If InitNetwork()
log_add("START SERVER using port " + Str(#THIS_PORT) + ". Result: " + Str(srv_start(@THIS_SRV, #THIS_PORT)), 8)
Global Timer
Repeat
Timer = ElapsedMilliseconds()
; this server action
THIS_SRV\LEvent = NetworkServerEvent (THIS_SRV\HANDLE)
Select THIS_SRV\LEvent
Case #PB_NetworkEvent_Connect ; client connected
If Not CLIENT\State
srv_getClient (EventClient(), CLIENT) ; get some info about client
log_add ("CLIENT CONNECTED: " + Str (EventClient()) + " [" + IPString (CLIENT\IP\IP) + "]", 14)
Else
log_add ("CLIENT ALREADY CONNECTED. DROPPED ANOTHER ONE: " + Str(EventClient()) + " [" + IPString (GetClientIP(EventClient())) + "]", 14)
CloseNetworkConnection(EventClient())
EndIf
Case #PB_NetworkEvent_Data ; a data from client received
If EventClient() = CLIENT\HANDLE ; if it is our client
If REMOTE\State
log_add ("CLIENT >> REMOTE = " + Str(DataTransfer (CLIENT, REMOTE)), 2)
EndIf
CLIENT\TimeStamp = Timer ; update activity time
REMOTE\TimeStamp = Timer
EndIf
Case #PB_NetworkEvent_Disconnect ; client disconnected
If EventClient() = CLIENT\HANDLE ; if it is our client
log_add ( "CLIENT DISCONNECTED: " + Str (EventClient()) + " [" + IPString (CLIENT\IP\IP) + "]", 14)
net_cls (CLIENT)
net_stop(REMOTE) ; close connection with proxy also
Else
log_add ( "UNKNOWN DISCONNECTED: " + Str (EventClient()) + " [" + IPString (GetClientIP(EventClient())) + "]", 14)
EndIf
EndSelect
; attempt to connect/reconnect to remote proxy if CLIENT connected but proxy not
If CLIENT\State And Not REMOTE\State
If REMOTE\RetryStamp + #NET_RETRYTIME < Timer ; reconnect timeout
REMOTE\RetryStamp = Timer
If net_start(@REMOTE, GetIPByHost(#REMOTE_HOST), #REMOTE_PORT)
log_add ("CONNECTED TO REMOTE HOST [" + #REMOTE_HOST + "]", 11)
REMOTE\TimeStamp = Timer
Else
log_add ("! CANNOT CONNECT TO REMOTE HOST [" + #REMOTE_HOST + "]", 12)
EndIf
EndIf
EndIf
; remote proxy action
If REMOTE\State
REMOTE\LEvent = NetworkClientEvent(REMOTE\HANDLE)
If REMOTE\LEvent = #PB_NetworkEvent_Data ; incoming data from C&C, it uses MA protocol for everything
If CLIENT\State
; receive data from remote and send to client
log_add ("CLIENT << REMOTE = " + Str(DataTransfer(REMOTE, CLIENT)), 3)
CLIENT\TimeStamp = Timer ; update activity time
REMOTE\TimeStamp = Timer
EndIf
EndIf
EndIf
; general control
; check if all connections alive and drop if not
If REMOTE\State
If REMOTE\TimeStamp + #NET_LIFETIME < Timer
log_add ("Connection timed out: REMOTE HOST", 14)
net_stop(REMOTE)
EndIf
EndIf
If CLIENT\State
If CLIENT\TimeStamp + #NET_LIFETIME < Timer
log_add ("Connection timed out: CLIENT", 14)
net_stop(CLIENT)
EndIf
EndIf
; Delay(#NET_FAST)
ForEver
EndIf