network connection relay.

BlitzMax Forums/BlitzMax Programming/network connection relay.

Paul "Taiphoz"(Posted 2006) [#1]
Hello guys.

One of my collegue's is working on a small open source firewall, its not released yet he wants it full functional first, then it will probably end up in the opensource repository.

Anyway, He has asked me to test his firewall, for fear of having some one else rip it appart all tests are being done locally, and a small secure local network of 100 computers.

Now. While I have tested some basic stuff already he has asked me to code and test its IDS detection system, he seems really proud of it.

What I need to code is a relay of sorts. An application we can run on one of the network systems, that just takes anything that it receives on a port, and then echo it to a target system, and then anything the target does in reply, should then be echoed back to the main or source computer.

Here is an example.

Source(ME) sends a Ping to the relay
The relay sends the ping to the dest
the dest accepts the ping and send the reply packets.
the relay gets the reply packets and send them back to the source.

Its goto deal with all connections as raw data, we cant lock it down to only work with Ping.

So we can test it with the likes of telnet, ftp etc.. and have it just simply relay them all in the same way.

If your wondering why were not using any premade software for this, the network he uses belongs to his work, and even tho his boss is allowing him to test and code his firewall on their network(which they plan to use when its done) they will not allow me to use anything that they can trust or see the source code for and compile on their own.

Anyway. Before I start, and having not coded anything with the BlitzMax sockets or network stuff, I was wondering if anyone here has any exmaples. or links I could have a look at.


AlexO(Posted 2006) [#2]

If your wondering why were not using any premade software for this, the network he uses belongs to his work, and even tho his boss is allowing him to test and code his firewall on their network(which they plan to use when its done) they will not allow me to use anything that they can trust or see the source code for and compile on their own.


this makes no sense... but anyway,

are you looking for a particular protocol? Because while UDP and TCP share some functions (some don't even work for UDP even though docs say they should) they take a bit of a different approach to setting up and manipulating.

what you are asking for is pretty simple. While I'm not going to code it for you I can show you a networking example a friend and I did in Bmax to get a better understanding of UDP/TCP protocols in bmax. I'll go through it and comment it and post in a bit. It deals with both UDP and TCP so it might be considered a bit large for an example, but I consider it a good starting point.


AlexO(Posted 2006) [#3]
K here is the networking example for both UDP and TCP. Its a bit lengthy, but unfortunately networking isn't a two-line thing. Some of the comments might be 'duh' for you or others, but hopefully will help those looking to get their feet wet with networking in Bmax.

notes:
- UDP server doesn't detect disconnects from clients. this takes way more logic/code to implement compared to TCP and is beyond the scope of the example.
- UDP and TCP don't mix. so don't try to connect to a UDP server with a TCP client.

this example can:
- create either a TCP or UDP server
- host multiple clients per server
- echo integers back and forth from client to server back to client.

SuperStrict
Rem
BlitzMax TCP / UDP networking example
Authors: Martin Ahrens , Alex Okafor
webpage: http://www.enddream.com
End Rem

Rem
operation:
1. select server, client, or quit
2a. if server: select port to start on
2b. if client: select server IP and port to connect to.
3. press escape to quit/disconnect from server. 
End Rem





' get the host name of this computer
Extern "win32"
	Function GetComputerNameA(lpbuffer:Byte Ptr,nSize:Int Ptr)
EndExtern


Local Size:Int = 32								' maximum length of cstring
Local Buffer:Byte Ptr = MemAlloc(Size)		' allocate memory to hold cstring
GetComputerNameA(buffer,Varptr(size))		' ask for name
Local name$ = String.FromCString(buffer)	' convert cstring to string
MemFree buffer 								' release cstring

Print "Connected on " + name$ + " (" + DottedIP(HostIp(name$)) + ")"
'Print "computer name contains " + size + " characters"
'------------------------------------





Rem
this is what the server keeps a hold of when someone connects
to the server
End Rem
Type Client
	Field intIP:Int ' integer IP
	Field port:Int
	Field socket:TSocket
	Field stream:TStream
	Field tcp:Int	' is this client a TCP or UDP client. false = UDP.
	
	Rem
	create a TCP client
	with the needed streams
	and field data
	End Rem
	Function CreateTCP:Client(sock:TSocket)
		Local newClient:Client = New Client
		newClient.socket = sock
		newClient.stream = CreateSocketStream(sock , True)
		newClient.tcp = True
		newClient.intIP = SocketRemoteIP(sock)
		newClient.port = SocketRemotePort(sock)
		
		If newClient.stream = Null Then
			Print "cannot stream new client socket, abandoning client"
			Return Null
		EndIf
			
		Return newClient
	End Function
	
	Rem
	create UDP client
	UDP clients don't
	actual personal 'streams' like TCP
	End Rem
	Function createUDP:Client(intip:Int , port:Int)
		Local nClient:Client = New Client
		nClient.intip = intip
		nClient.port = port
		nClient.tcp = False
		Return nClient	
	End Function
	
	Rem
	returns true / false if a TCP socket is still connected
	used for server to detect if a client has disconnected.
	End Rem
	Method isConnected:Int()
		Return SocketConnected(socket)
	End Method
	
	Rem
	wrapper method for disconnecting a client's streams
	End Rem
	Method disconnect()
		CloseStream(stream)
		
	End Method
	
	' getter methods to 
	' determine which
	' type of clien this is
	Method isTCP:Int()
		Return tcp
	End Method
	
	Method isUDP:Int()
		Return Not(tcp)
	End Method
End Type







Local TorU:String = ""	' holds whether user chose to do a TCP or UDP network test
Local tuChoice:Int = 0 '1=T 2=U 3=Q		where T = TCP; U = UDP; Q = Quit

'-----INPUT LOOP-----
' gather some input to 
' setup the correct
' network connections
While tuChoice = 0
	TorU:String = Lower$(Trim$(Input$("TCP (T) or UDP (U) or Quit (Q): ") ) )
	If TorU = "t" Then
		tuChoice = 1
	ElseIf TorU = "u"
		tuChoice = 2
	ElseIf TorU = "q"
		tuChoice = 3
	Else
		'tuChoice = 0
	EndIf
Wend

If tuChoice = 3 Then End	' if user chose to quit then end the program.
'-------------------

'--------------------

Local SorC:String = ""	' holds if user wants to be a server or client
Local scChoice:Int = 0 '1=S 2=C 3=Q		where S = Server; C = Client; Q = Quit

'-----INPUT LOOP-----
' gather more input
' to setup correct
' server or client streams
While scChoice = 0
	SorC:String = Lower$(Trim$(Input$("Server (S) or Client (C) or Quit (Q): ") ) )
	If SorC = "s" Then
		scChoice = 1
	ElseIf SorC = "c"
		scChoice = 2
	ElseIf SorC = "q"
		scChoice = 3
	Else
		'scChoice = 0
	EndIf
Wend

'--------------------
' run the demo app.
Select scChoice
Case 1
	' SERVER
	BeServer(tuChoice)
Case 2
	' CLIENT
	BeClient(tuChoice)
Case 3
	End
End Select

'---------------------


' runs a simple echo server.
' this is both TCP and UDP
Function BeServer(tuChoice:Int)
	
	Local sockTypeName:String = ""	' just a string to keep track of which type of server this is
	If tuChoice = 1 Then
		sockTypeName = "TCP"
	ElseIf tuChoice = 2 Then
		sockTypeName = "UDP"
	EndIf
	
	Local localPort:Int = -1
	
	' ask user which port they want to start the server on.
	While ( (localPort < 0) Or (localPort > 65535) )
		localPort:Int = Int(Trim$(Input$("Port (1-65535, 0 to quit) : ") ) ) 
	Wend
	
	If localPort = 0 Then End
	'-------------------
	
	' create the sockets
	Local servSocket:TSocket = Null
	If tuChoice = 1 Then 'tcp
		servSocket:TSocket = CreateTCPSocket:TSocket()
	Else 'udp
		servSocket:TSocket = CreateUDPSocket:TSocket()
	EndIf
	If servSocket = Null Then
		Print "Cannot create " + sockTypeName + " server socket... "
		End
	Else
		Print "Created server " + sockTypeName + " socket"
	EndIf
	
	' user must bind the socket to a port once it is created successfully
	Local servSocketBound:Int = BindSocket(servSocket , localPort)
	If Not(servSocketBound) Then
		Print "Cannot bind " + sockTypeName + " server socket to local port... " + localPort
		CloseSocket(servSocket)
		Print sockTypeName + " socket closed"
		End
	Else
		Print  sockTypeName + " server socket bound To Local port " + localPort
	EndIf
	
	
	Rem
	wrap the socket around the stream
	End Rem
	Local servSocketStream:TSocketStream = CreateSocketStream:TSocketStream(servSocket , True)
	If servSocketStream = Null Then
		CloseSocket(servSocket)
		Print "Cannot stream " + sockTypeName + " server socket"
		End
	Else
		Print "Streaming " + sockTypeName + " server socket"
	EndIf
	
	Rem
	for TCP you must listen on the socket
	for UDP this is not required.
	End Rem
	If tuChoice = 1 Then
		If Not(SocketListen(servSocket)) Then
			Print "Cannot Listen on " + sockTypeName + " server socket"
		Else
			Print "Listening on " + sockTypeName + " server socket"
		EndIf
	Else
		Print "In UDP, skipping listen"
	EndIf
	
	
	'----------INIT------------
	Local clients:TList = CreateList()	' a list to hold all client connections.
	'--------MAIN LOOP---------
	Print "Server GUI starting..."
	Graphics 800 , 600 , 0 , 60
	Local ft:TTimer = CreateTimer:TTimer(100) '10 hertz timer to trigger events
	Repeat
		WaitEvent()
		
		Rem
		listen for new connections
		either in TCP or UDP.
		notice UDP requires a bit
		more setting up and checking to keep track of new clients.
		End Rem
		If tuChoice = 1 Then 'tcp
			Local newClientSocket:TSocket = SocketAccept(servSocket)
			If newClientSocket <> Null Then
				Print "Heard a connect from " + DottedIP(SocketRemoteIP(newClientSocket))
				ListAddLast(clients, Client.CreateTCP(newClientSocket))
			EndIf
		Else 'udp
			Rem
			in UDP the bytes array is nothing more than a byte ptr for an int.
			since all we do is echo integers back and forth. The
			recvFrom_() function just needs a byte ptr to be able to store the
			information it recieves somewhere. 
			we instantly send back the integer through sendto_()
			
			we check to see if the we've never seen this client before
			by checking this client's ip and port against our list. if its 
			new, we add it to the list.
			End Rem
			Local sra:Int = SocketReadAvail(servSocket)
			If sra > 0 Then
				Local bytes:Int[1]	' this variable will recieve the incoming 'raw' data.
				Local rip:Int = 0	' remote ip
				Local rport:Int = 0	' remote port
				'http://www.blitzmax.com/Community/posts.php?topic=57586#640994 
				recvfrom_(servSocket._socket , bytes , 4 , 0 , rip , rport)
			
				sendto_(servSocket._socket , bytes , 4 , 0 , rip , rport)
				
				Print CurrentTime() + " (" + DottedIP(rip) + ":" + rport + ") got: " + bytes[0] + ", replied"
				If Not(clientExists(rip , rport , clients) ) Then
					clients.addLast(Client.createUDP(rip , rport) )
				End If
			EndIf
		EndIf
		
		
		Rem
		this checks to see if
		any of the TCP clients have sent
		anything to us. 
		Also if the TCP client has disconnected
		we handle it here.
		End Rem
		For Local eClient:Client = EachIn clients
			If eClient.isTCP() Then
				If eClient.isConnected() Then
					
					Local sra:Int = 1
					While sra > 0
						sra:Int = SocketReadAvail(eClient.socket)
						
						If sra >= 4 Then 'can read an int (4 bytes)
							' read from their stream
							Local integer:Int = ReadInt(eClient.stream)
							WriteInt(eClient.stream , integer) ' send it back
							Print CurrentTime() + " (" + DottedIP(eClient.intIP) + ":" + eClient.port + ") got: " + integer + ", replied"
							
						EndIf
					Wend
					
				Else
					Print "Removed a client (DC)"
					clients.remove(eClient)
					eClient.disconnect()
				End If
			ElseIf eClient.isUDP() Then
			
			End If
			
		Next
		
		' just draw number of clients connected.
		Cls
		DrawText("Clients: " + CountList(clients), 0,0)
		Flip(False)
	Until KeyHit(Key_Escape)
	'--------------------------
	
	' exiting the function
	' close the server socket.
	CloseSocket(servSocket)
	Print sockTypeName + " socket closed"
	
End Function

'---------------------
Rem
tuChoice is tcp or udp. 1 = tcp
this handles being a client 
either TCP or UDP
End Rem
Function BeClient(tuChoice:Int)
	
	' gather more input to connect to a server
	Local servIP:String = Trim(Input("Server IP: ") )
	If servIP = "" Then End
	Local servPort:Int = Int(Trim(Input("Server Port 1-65535, 0 to quit: " ) ) )	
	If servPort <= 0 Or servPort > 65535 Then End
	
	Local cliSocket:TSocket = Null
	Local socketTypeName:String
	
	' create sockets
	If tuChoice = 1 Then 'tcp
		cliSocket = CreateTCPSocket:TSocket()
		socketTypeName = "TCP"
	Else 'udp
		cliSocket = CreateUDPSocket:TSocket()
		socketTypeName = "UDP"
	End If
	
	If cliSocket= Null Then
		Print "Could not create " + socketTypeName + " client socket "
		End
	Else
		Print "Created " + socketTypeName + " client socket"
	End If
	
	
	Local intIP:Int = getIntIP(servIP)	' get the integer IP of the server
	Local connected:Int = ConnectSocket(cliSocket, intIP , servPort)	' connect to the server
	
	If Not(connected) Then
		Print "Connection failed on " + socketTypeName + " socket to server"
		CloseSocket(cliSocket)
		End
	Else
		Print "Connected to server on " + socketTypeName + " socket"
	End If
	
	' create a stream for easier reading/writing
	Local netStream:TSocketStream = CreateSocketStream(cliSocket, True)
	If netStream = Null Then
		Print "Cannot create stream of type " + socketTypeName + " on socket"
		CloseSocket(cliSocket)
		End
	Else
		Print "Streaming " + socketTypeName + " client socket"
	End If
	
	'get the in/out streams from the netstream
	Local netOut:TStream = WriteStream:TStream(netStream)
	Local netIn:TStream = ReadStream:TStream(netStream)
	
	Print "Client GUI starting..."
	Graphics 100 , 100 , 0 , 60
	Local ft:TTimer = CreateTimer:TTimer(100) '10 hertz timer to trigger events
	Local currentInt:Int = 0
	Local currentIntSent:Int = MilliSecs()
	
	Repeat
		WaitEvent()
		
		Rem
		everytime user clicks on graphics window
		send a random int.
		for automation purposes send a random int
		at random intervals.
		End Rem
		If MouseHit(1) Or Rand(60)=1 Then
			currentInt:Int = Rand(1000)
			currentIntSent:Int = MilliSecs()
			WriteInt(netOut , currentInt)
			Print "Send int " + currentInt
		EndIf
		Local sra:Int = SocketReadAvail(cliSocket)
		If sra >= 4 Then	' recieved an int into the stream [ints = 4 bytes]
			Local integer:Int = ReadInt(netIn)
			Print "Recv: " + integer
			If integer = currentInt Then
				Local timeTaken:Int = MilliSecs() - currentIntSent
				Print "PING: " + timeTaken
			EndIf
		EndIf
		
		'bleh :P
		Cls
			DrawText("Something",0,0)
		Flip(False)
	Until KeyHit(key_ESCAPE)
	
	CloseSocket(cliSocket)
End Function

'---utility functions--

Rem
takes a normal dotted IP string
and returns the integer value of that IP
since IP's are used in integer format
when communicating , this is needed
for initial connections and sending 
of data!
End Rem
Function getIntIP:Int(ip:String) 
	Local firstDot:Int = Instr(ip,".",1)
	Local secDot:Int = Instr(ip,".",firstDot+1)
	Local thirdDot:Int = Instr(ip,".",secDot+1)
	
	
	Local first:Int = Int(Mid(ip,1,firstDot))
	Local sec:Int = Int(Mid(ip,firstDot+1,secDot-firstDot))
	Local third:Int = Int(Mid(ip , secDot + 1 , thirdDot - secDot))
	Local fourth:Int = Int(Mid(ip , thirdDot+1))
	Print first + "." + sec + "." + third + "." + fourth
	Return dottedToIntIP(first , sec , third , fourth)
	
End Function

Rem
a helper function for getIntIP() to translate a 
dotted IP to integer format.
End Rem
Function dottedToIntIP:Int(oc1:Int , oc2:Int , oc3:Int , oc4:Int) 'take 4 octets, return bitwise concat int
	Return ((oc1 Shl 24) | (oc2 Shl 16) | (oc3 Shl 8) | (oc4))
End Function


Rem
used in UDP server to see if the integer it 
recieved came from a client that it already knows about or
is a new connection.
End Rem
Function clientExists:Int(ip:Int , port:Int, list:Tlist)
	For Local c:Client = EachIn list
		If c.intip = ip And c.port = port Then
			Return True
		End If
	Next
	Return False
End Function


I went through each of the relevant functions and commented them with what they do and any other notes I deemed might make things clearer.

Might think this is alot to just echo back integers, but this is a decent setup to get the clients/servers doing what you want. Its a crude framework, but by editing the sends on either end you *could* mold this to do other simple tasks.

Hope this helps.


Paul "Taiphoz"(Posted 2006) [#4]
Thanks for the example. should help a lot. alos thanks for the comments.


@Alex O. - Exactly what was it about that, that you didnt understand.

The guy is programming a firewall which his boss/work, is allowing him to test and code on their work network, their wetwork admin will only allow stuff to be run that he can read the source of , and stuff made by people he trusts..

AKA me and the firewall programmer.


AlexO(Posted 2006) [#5]
glad I could help :)


The guy is programming a firewall which his boss/work, is allowing him to test and code on their work network, their wetwork admin will only allow stuff to be run that he can read the source of , and stuff made by people he trusts..



ah that makes more sense now.


they will not allow me to use anything that they can trust or see the source code for and compile on their own.


can = can't. it was late I didn't pick up on the mistake :o