network connection relay.
BlitzMax Forums/BlitzMax Programming/network connection relay.
| ||
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. |
| ||
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. |
| ||
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. |
| ||
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. |
| ||
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 |