STUN is used in several different network implementations, one of which is VoIP. The most common use case for STUN is to discover clients' public IPs behind a NAT. It is also useful to punch a pinhole behind a NAT and let incoming UDP traffic come.
As a PHP coder, I needed to find a PHP library to handle the STUN easily. I found this class, I am just documenting it here for convenience.
PHP Code
class STUNClient{
private $socket;
public function __construct(){
//stun.l.google.com
$this->setServerAddr("66.102.1.127", 19302);
$this->createSocket();
}
public function setServerAddr($host, $port = 3478){
$this->serverIP = $host;
$this->serverPort = $port;
}
public function createSocket(){
$this->socket = socket_create(AF_INET, SOCK_DGRAM, getprotobyname("udp"));
socket_set_nonblock($this->socket);
}
public function getPublicIp(){
$msg = "\x00\x01\x00\x08\xc0\x0c\xee\x42\x7c\x20\x25\xa3\x3f\x0f\xa1\x7f\xfd\x7f\x00\x00\x00\x03\x00\x04\x00\x00\x00\x00";
$numberOfBytesSent = socket_sendto($this->socket, $msg, strlen($msg), 0, $this->serverIP, $this->serverPort);
$st = time();
while (time() - $st < 1){
socket_recvfrom($this->socket, $data, 32, 0, $remoteIP, $remotePort);
if (strlen($data) < 32){
continue;
}
break;
}
try{
$info = unpack("nport/C4s", substr($data, 26, 6));
$ip = sprintf("%u.%u.%u.%u", $info["s1"], $info["s2"], $info["s3"], $info["s4"]);
$port = $info['port'];
return [
'ip' => $ip,
'port' => $port
];
} catch (Exception $e){
return [
'ip' => "0.0.0.0",
'port' => "0"
];
}
}
}
Use this small code to test it:
$sc = new STUNClient;
$sc->setServerAddr('stun.freeswitch.org', 3478);
print_r( $sc->getPublicIp() );
(
[ip] => xxx.xxx.xxx.xxx
[port] => 40582
)
Because UDP is unreliable, if you get a 0.0.0.0 IP and 0 port answer, it means that communication didn't go well, you have to add some verification code to retry.
What about TCP?
Sadly TCP won't work. Not because you cannot open the NAT pinhole, but because of the way TCP works.
Why STUN can be used to punch a UDP Pinhole through a NAT?
To answer that you must understand how NAT works. Your local devices in your local network cannot connect directly to the servers, basically because they have a private IP address. Your router, which holds a routable public IP alters your connection package (layer 3) to forward your payload. When a packet comes back, the FW router keeps track in a table where to forward the incoming traffic. It keeps creating a table similar to this one.
Local IP | Local Port | External IP | External Port |
192.168.7.3 | 5060 | 5.23.1.9 | 5060 |
192.168.7.39 | 8593 | 5.23.1.9 | 8593 |
The following drawing shows the usual traffic flow.
- The client sends an STUN request to a public STUN server. The FW adds to its NAT table a new row (if it doesn't exist).
- The STUN server answers back.
- A third server gets that information (out-of-the-band) and sends UDP data to the IP and port that the STUN server reported, which is the same that the FW NAT table has.
The thing here is that most routers do not implement any kind of security in the NAT. Any arriving packet to an open pinhole will be blindly forwarded to the local client. This is one of the reasons why ghost calls happen.
Hopefully, this explanation clears any doubts.
Good luck!