Reasonable answer why STUN in Java is so huge is also acceptable.
It's a reasonable question. 99% of what STUN is just a simple echo/response protocol for a client to self-discover the IP and port mapping as a result of NAT between it and the public internet. Having built a STUN library in C++, I have some insight.
Let's think about what is required of a STUN library:
A message writer that generates the STUN messages with an attribute field schema that not only allows for fields to appear in any order, it also allows for custom attributes to be added as well.
A message parser that can read such messages back and convert a data
structure reasonable for code to use. It needs to do this securely and avoid unhandled exceptions.
Socket networking code to send/receive such messages. And STUN servers are technically required to listen on 2 IPs and 2 ports, so that makes the networking code for the server a bit more complex.
If we just care about binding requests and binding responses, we'd be
done. But the STUN RFCs also define a set of NAT classification tests. So additional state machine logic is needed to make any such library complete.
And if the STUN library is going to go all the way with the security options afforded by the protocol, it would need some amount of crypto code for hashing and signing of messages
So combining all this into a library that anyone can use for all the different purposes of STUN including mapped address discovery, NAT classification, and ICE negotiation, it starts to get big quick.
You could easily just roll some socket code that hardcodes the bytes of a binding request and then some hacked up parsing to parse the response. That might meet your own needs, but a well established open source library would never be written this way.
JSTUN is a good start. I've shared some interop and bug fixing code with the original author. He doesn't actively maintain it, but it's a good implementation of RFC 3489. I even hacked it up once to run on Android.
To generate a STUN binding request in JSTUN.
MessageHeader sendMH = new MessageHeader(MessageHeader.MessageHeaderType.BindingRequest);
sendMH.generateTransactionID();
// add an empty ChangeRequest attribute. Not required by the standard, but JSTUN server requires it
ChangeRequest changeRequest = new ChangeRequest();
sendMH.addMessageAttribute(changeRequest);
byte[] data = sendMH.getBytes();
// not shown - sending the message
Then to parse the response back:
byte [] receivedData = new byte[500];
// not shown - socket code that receives the messages into receivedData
receiveMH.parseAttributes(receivedData);
MappedAddress ma = (MappedAddress) receiveMH.getMessageAttribute(MessageAttribute.MessageAttributeType.MappedAddress);
Then combine the above with some socket code. The best example of combining the above with socket code can be found in the DiscoveryTest.java source file. You really just need the code in the test1()
method of this class.