-
Notifications
You must be signed in to change notification settings - Fork 20
How to use the Python library
STUN-based client and servers are much easier to implement when you have a message encoder/decoder. This library has been written aiming to provide you a simple STUN message encoder/decoder.
This documentation describes the Python library.
- If your code is in C, then reference to the C documentation.
- If your code is in C++, then reference to the C++ documentation.
In short, stunmsg
is also a Python library for encoding and decoding STUN
messages. The Python API provides the same support available on the
C and C++ libraries, but it has been adapted to ease the Python programming.
stunmsg
depends on the following libraries:
-
_stunmsg_c.so
library (generated bysetup.py
);
The usage is straightforward:
from stunmsg import StunMsg
def send_stun_request( ... ):
# Create the STUN message object
msg = StunMsg(StunMsg.BINDING_REQUEST, tsx_id)
# Appends a SOFTWARE attribute
msg.appendattr(StunMsg.ATTR_SOFTWARE, software_name)
# Appends a PRIORITY attribute
msg.appendattr(StunMsg.ATTR_PRIORITY, 0x6e0001ff)
# Appends a ICE-CONTROLLED attribute
msg.appendattr(StunMsg.ATTR_ICE_CONTROLLED, 0x932ff9b151263b36L)
# Appends a USERNAME attribute
msg.appendattr(StunMsg.ATTR_USERNAME, username)
# Appends a MESSAGE-INTEGRITY attribute
msg.appendattr(StunMsg.ATTR_MESSAGE_INTEGRITY, key)
# Appends a FINGERPRINT attribute as last attribute
msg.appendattr(StunMsg.ATTR_FINGERPRINT)
# Now, send the message
send(msg.data)
The decoding part is simple, but it will depend on the socket type you're working on.
For datagram sockets you usually allocate a max permitted memory block size, and simply begin parsing it. In this case, you can do the following:
from stunmsg import StunMsg
def receive_message(dgram_sock, ... ):
# Receive STUN messages up to 2k
data, addr = dgram_sock.recvfrom(2*1024)
# Create the StunMsg object
msg = StunMsg(data=data)
# Check if this is a valid STUN message
if not msg.verify():
raise Exception('Not a STUN packet')
# Read the message type
if msg.type == StunMsg.BINDING_REQUEST:
# do something
pass
elif msg.type == StunMsg.BINDING_RESPONSE:
# do something
pass
# ...
else:
send_unknown_method(msg)
return
# Iterate over the message attributes
for attr_type, attr_value in msg.iterattrs():
if attr_type == StunMsg.ATTR_SOFTWARE:
print "SOFTWARE: ", attr_value
elif attr_type == StunMsg.ATTR_USERNAME:
print "USERNAME: ", attr_value
elif attr_type == StunMsg.ATTR_XOR_MAPPED_ADDRESS:
addr, port = attr_value
print "XOR-MAPPED-ADDRESS: ", addr, port
elif attr_type == StunMsg.ATTR_MESSAGE_INTEGRITY:
# Attribute value is a function to validate the incoming
# message that receives the key needed to check the message
check_function = attr_value
print "MESSAGE-INTEGRITY: ", check_function(key)
elif attr_type == StunMsg.ATTR_FINGERPRINT:
# Attribute value is a boolean flag indicating whether the
# fingerprint validation has passed
check_result = attr_value
print "FINGERPRINT: ", check_result
# etc...
First receive the STUN message header (20 bytes), then the rest of the message:
from stunmsg import StunMsg
def receive_message(stream_sock ...):
# Receive the STUN message header first
stun_header = sock_stream.recv(20)
# Now, get the whole message size
msg_len = StunMsg.getlength(stun_header)
# And receive the remaining message bytes
if msg_len > 20:
stun_attrs = sock_stream.recv(msg_len - 20)
msg = StunMsg(buf=stun_header+stun_attrs)
else:
# If message length is 20 bytes, it's a header-only message
msg = StunMsg(buf=stun_header)
# Handle the message ...