Q: “How can I capture UDP messages sent from multiple IP addresses and rebroadcast them to another set of multiple IP addresses?”
—Juan Osorno
A: There are several reasons why someone might want to forward User Datagram Protocol (UDP) packets in this fashion. Good examples of projects that might use this functionality include a network intrusion analyzer, a central information gateway for distributed applications, or even an Internet game server.
To accomplish Juan’s task, we’ll turn to our good friend the Microsoft Winsock Control.
UDP is a connectionless protocol, so there are some things to keep in mind when dealing with it in an application. Unlike Transmission Control Protocol (TCP), there is no explicit connection between the sending and receiving computers, so there’s no guarantee of delivery. This property makes UDP less consumptive of resources than TCP, but at the same time, more prone to problems caused by lost packets or confusion in the arrival order of packets.
For this article, I created a sample application called UDPRebroadcaster that illustrates one way of answering Juan’s question. The app listens on a specified number of ports for incoming UDP packets from a list of originating addresses and forwards the data in these packets to another list of destination addresses. The source code is divided into two sidebars:
- · Listing A contains the code for the application’s main form, frmRebroadcast, shown in Figure A.
- · Listing B contains modAddressListIO, helper code that loads the list of ports the application should monitor and the two lists of IP addresses.
Figure A |
![]() |
UDPRebroadcaster’s main form |
A guided tour
When the application starts, we first need to build our list of originating and destination IP addresses and the list of ports UDPRebroadcaster will monitor. The ReadIncomingAddressList and ReadOutgoingAddressList functions shown in Listing B retrieve the address lists, while ReadPortList retrieves the list of monitored ports. Currently, all three of these functions expect these lists to be in plain text files in the application’s directory, one item per line. Breaking this functionality away from the main form of the application makes it easier to change the storage method of the IP addresses and ports if needed.
The application actually employs two Winsock controls, sckListener, which listens for incoming UDP packets, and sckSender, which forwards received packets to the addresses in our outgoing address list. Why use two Winsock controls? There’s a quirk in the implementation of the Winsock control: When an incoming UDP message is received, the control’s RemoteHostIP and RemoteHostPort properties are updated to reflect the information read from the incoming packet. Since we are forwarding received packets to multiple recipients, there is a potential to either lose an incoming packet’s originating IP address or inadvertently forward a packet to the wrong address if we were to use just one control for both listening and sending. Using two dedicated controls sidesteps this problem.
Because we might want to be able to monitor multiple ports for incoming UDP packets, sckListener is actually a control array—the “base” instance of the control is on the form with an index of zero. The event handler for the Start Listening button (cmdStart) creates any additional instances of sckListener that are needed and binds each one to a requested port from the m_intPortNumbers array. The controls in the sckListener control array then begin listening for incoming UDP messages on their assigned ports. The following code fragment illustrates how this is done:
‘Open requested ports
For i = 0 To UBound(m_intPortNumbers)
If i <> 0 Then
‘Load an additional sckListener
‘to handle the additional port
Load sckListener(i)
End If
sckListener(i).Bind m_intPortNumbers(i)
Next i
As I mentioned above, UDP is a connectionless protocol, so there are no ConnectionRequest events to handle in code before data arrives. Instead, packets come in more or less unannounced through sckListener’s DataArrival event, where we extract the data from them in the form of a byte array. DataArrival will queue multiple packets and hold them for you to process if a second or third one arrives before you have processed the first. Let’s look at one important fragment of code from sckListener’s DataArrival event:
Private Sub sckListener_DataArrival(Index _
As Integer, ByVal bytesTotal As Long)
‘get the data in binary format
‘to preserve it
ReDim bytData(bytesTotal) As Byte
sckListener(Index).GetData bytData(),_
vbByte + vbArray, bytesTotal
‘Call BroadCastPacket to forward _
‘the message
BroadcastPacket bytData(), _
sckListener(Index).RemoteHostIP
End Sub
Here, we discover another quirk of the Winsock control. When dealing with UDP messages, the RemoteHostIP property is not set to the message’s originating IP address until after the GetData method is called. This presents us with a bit of a problem: We are only selectively responding to messages from the IP addresses identified in our originating list, but we don’t know what the originating address is until after we get the data from the packet. To work around this limitation and to give us a little cleaner code, I put the logic responsible for determining if a packet should be forwarded inside the BroadcastPacket sub.
When called, BroadcastPacket checks the originating address of the packet against our incoming address list, which is stored in the collection m_colIncomingAddresses. If the originating address is found in our incoming list, then BroadcastPacket uses sckSender to forward the packet to the addresses in our outgoing list, which are stored in the string array m_strOutgoingAddresses. If the originating address is not in our incoming list, we “eat” the packet by simply not forwarding it. Here’s the code fragment that does the important work of forwarding the UDP packets:
For i = 0 To UBound(m_strOutgoingAddresses)
Me.lbxMessages.AddItem (vbTab & “Forwarding” _
& ” to ” & m_strOutgoingAddresses(i))
With sckSender
.RemoteHost = m_strOutgoingAddresses(i)
.RemotePort = Me.txtSendPort.Text
.SendData bytData()
End With
Next i
Again, because UDP is connectionless, sckSender’s SendProgress and SendComplete events will never fire. Likewise, we don’t have to open or close any connections before we send to a different address. We therefore don’t have confirmation that the packet was successfully received at its destination, and indeed, we can’t really know if it was or not unless the destination IP happens to belong to another computer in the room with us.
Extending the solution
UDPRebroadcaster immediately forwards packets as they come in. If you’re dealing with a lot of packets arriving soon after one another, you may find it more efficient to hold them and forward groups of them at regular intervals. To do this, you’ll need some kind of storage structure to hold the data from the incoming packets until you’re ready to forward them. When you download the code for UDPRebroadcaster, you’ll also get a simple packet-queuing class, cSimPcktQ, that will do this for you—consider it a bonus.
That’ll about do it for this installment of Visual Basic Q&A. Until next time, may all your UDP messages arrive in the correct order.