Asterisk - The Open Source Telephony Project  21.4.1
Data Structures | Macros | Functions | Variables
stun.c File Reference

STUN Support. More...

#include "asterisk.h"
#include "asterisk/logger.h"
#include "asterisk/logger_category.h"
#include "asterisk/_private.h"
#include "asterisk/stun.h"
#include "asterisk/cli.h"
#include "asterisk/utils.h"
#include "asterisk/channel.h"

Go to the source code of this file.

Data Structures

struct  stun_addr
 
struct  stun_attr
 
struct  stun_header
 
struct  stun_state
 here we store credentials extracted from a message More...
 
struct  stun_trans_id
 STUN support code. More...
 

Macros

#define STUN_BINDERR   0x0111
 
#define STUN_BINDREQ   0x0001
 STUN message types 'BIND' refers to transactions used to determine the externally visible addresses. 'SEC' refers to transactions used to establish a session key for subsequent requests. 'SEC' functionality is not supported here.
 
#define STUN_BINDRESP   0x0101
 
#define STUN_CHANGE_REQUEST   0x0003
 
#define STUN_CHANGED_ADDRESS   0x0005
 
#define STUN_ERROR_CODE   0x0009
 
#define STUN_MAPPED_ADDRESS   0x0001
 Basic attribute types in stun messages. Messages can also contain custom attributes (codes above 0x7fff)
 
#define STUN_MAX_RETRIES   3
 
#define STUN_MESSAGE_INTEGRITY   0x0008
 
#define STUN_PASSWORD   0x0007
 
#define STUN_REFLECTED_FROM   0x000b
 
#define STUN_RESPONSE_ADDRESS   0x0002
 
#define STUN_SECERR   0x0112
 
#define STUN_SECREQ   0x0002
 
#define STUN_SECRESP   0x0102
 
#define STUN_SOURCE_ADDRESS   0x0004
 
#define STUN_UNKNOWN_ATTRIBUTES   0x000a
 
#define STUN_USERNAME   0x0006
 

Functions

static void append_attr_address (struct stun_attr **attr, int attrval, struct sockaddr_in *sin, int *len, int *left)
 append an address to an STUN message
 
static void append_attr_string (struct stun_attr **attr, int attrval, const char *s, int *len, int *left)
 append a string to an STUN message
 
uintmax_t ast_debug_category_stun_id (void)
 
uintmax_t ast_debug_category_stun_packet_id (void)
 
int ast_stun_handle_packet (int s, struct sockaddr_in *src, unsigned char *data, size_t len, stun_cb_f *stun_cb, void *arg)
 handle an incoming STUN message. More...
 
void ast_stun_init (void)
 Initialize the STUN system in Asterisk. More...
 
int ast_stun_request (int s, struct sockaddr_in *dst, const char *username, struct sockaddr_in *answer)
 Generic STUN request. More...
 
static char * handle_cli_stun_set_debug (struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
 
static void handle_stun_timeout (int retry, struct sockaddr_in *dst)
 
static const char * stun_attr2str (int msg)
 helper function to print attribute names
 
static int stun_get_mapped (struct stun_attr *attr, void *arg)
 Extract the STUN_MAPPED_ADDRESS from the stun response. This is used as a callback for stun_handle_response when called from ast_stun_request.
 
static int stun_id_cmp (stun_trans_id *left, stun_trans_id *right)
 
static const char * stun_msg2str (int msg)
 helper function to print message names
 
static int stun_process_attr (struct stun_state *state, struct stun_attr *attr)
 
static void stun_req_id (struct stun_header *req)
 helper function to generate a random request id
 
static int stun_send (int s, struct sockaddr_in *dst, struct stun_header *resp)
 wrapper to send an STUN message
 
static void stun_shutdown (void)
 

Variables

static struct ast_cli_entry cli_stun []
 
static uintmax_t debug_category_stun_id
 
static uintmax_t debug_category_stun_packet_id
 

Detailed Description

STUN Support.

Author
Mark Spencer marks.nosp@m.ter@.nosp@m.digiu.nosp@m.m.co.nosp@m.m
Note
STUN is defined in RFC 3489.

Definition in file stun.c.

Function Documentation

int ast_stun_handle_packet ( int  s,
struct sockaddr_in *  src,
unsigned char *  data,
size_t  len,
stun_cb_f stun_cb,
void *  arg 
)

handle an incoming STUN message.

Parameters
sSocket to send any response to.
srcAddress where packet came from.
dataSTUN packet buffer to process.
lenLength of packet
stun_cbIf not NULL, callback for each STUN attribute.
argArg to pass to callback.

Do some basic sanity checks on packet size and content, try to extract a bit of information, and possibly reply. At the moment this only processes BIND requests, and returns the externally visible address of the request. If a callback is specified, invoke it with the attribute.

Return values
AST_STUN_ACCEPTif responed to a STUN request
AST_STUN_IGNORE
-1on error

Definition at line 293 of file stun.c.

References append_attr_address(), append_attr_string(), ast_debug_stun, ast_stun_request(), stun_attr2str(), STUN_BINDREQ, STUN_MAPPED_ADDRESS, stun_msg2str(), and stun_send().

Referenced by ast_rtcp_read(), ast_rtp_read(), and ast_stun_request().

294 {
295  struct stun_header *hdr = (struct stun_header *)data;
296  struct stun_attr *attr;
297  struct stun_state st;
298  int ret = AST_STUN_IGNORE;
299  int x;
300 
301  /* On entry, 'len' is the length of the udp payload. After the
302  * initial checks it becomes the size of unprocessed options,
303  * while 'data' is advanced accordingly.
304  */
305  if (len < sizeof(struct stun_header)) {
306  ast_debug_stun(1, "Runt STUN packet (only %d, wanting at least %d)\n", (int) len, (int) sizeof(struct stun_header));
307  return -1;
308  }
309  len -= sizeof(struct stun_header);
310  data += sizeof(struct stun_header);
311  x = ntohs(hdr->msglen); /* len as advertised in the message */
312  if (ast_debug_stun_packet_is_allowed)
313  ast_verbose("STUN Packet, msg %s (%04x), length: %d\n", stun_msg2str(ntohs(hdr->msgtype)), (unsigned)ntohs(hdr->msgtype), x);
314  if (x > len) {
315  ast_debug_stun(1, "Scrambled STUN packet length (got %d, expecting %d)\n", x, (int)len);
316  } else
317  len = x;
318  memset(&st, 0, sizeof(st));
319  while (len) {
320  if (len < sizeof(struct stun_attr)) {
321  ast_debug_stun(1, "Runt Attribute (got %d, expecting %d)\n", (int)len, (int) sizeof(struct stun_attr));
322  break;
323  }
324  attr = (struct stun_attr *)data;
325  /* compute total attribute length */
326  x = ntohs(attr->len) + sizeof(struct stun_attr);
327  if (x > len) {
328  ast_debug_stun(1, "Inconsistent Attribute (length %d exceeds remaining msg len %d)\n", x, (int)len);
329  break;
330  }
331  if (stun_cb)
332  stun_cb(attr, arg);
333  if (stun_process_attr(&st, attr)) {
334  ast_debug_stun(1, "Failed to handle attribute %s (%04x)\n", stun_attr2str(ntohs(attr->attr)), (unsigned)ntohs(attr->attr));
335  break;
336  }
337  /* Clear attribute id: in case previous entry was a string,
338  * this will act as the terminator for the string.
339  */
340  attr->attr = 0;
341  data += x;
342  len -= x;
343  }
344  /* Null terminate any string.
345  * XXX NOTE, we write past the size of the buffer passed by the
346  * caller, so this is potentially dangerous. The only thing that
347  * saves us is that usually we read the incoming message in a
348  * much larger buffer in the struct ast_rtp
349  */
350  *data = '\0';
351 
352  /* Now prepare to generate a reply, which at the moment is done
353  * only for properly formed (len == 0) STUN_BINDREQ messages.
354  */
355  if (len == 0) {
356  unsigned char respdata[1024];
357  struct stun_header *resp = (struct stun_header *)respdata;
358  int resplen = 0; /* len excluding header */
359  int respleft = sizeof(respdata) - sizeof(struct stun_header);
360  char combined[33];
361 
362  resp->id = hdr->id;
363  resp->msgtype = 0;
364  resp->msglen = 0;
365  attr = (struct stun_attr *)resp->ies;
366  switch (ntohs(hdr->msgtype)) {
367  case STUN_BINDREQ:
368  if (ast_debug_stun_packet_is_allowed)
369  ast_verbose("STUN Bind Request, username: %s\n",
370  st.username ? st.username : "<none>");
371  if (st.username) {
372  append_attr_string(&attr, STUN_USERNAME, st.username, &resplen, &respleft);
373  /*
374  * For Google Voice, the stun username is made up of the local
375  * and remote usernames, each being fixed at 16 bytes. We have
376  * to swap the two at this point.
377  */
378  snprintf(combined, 17, "%16s", st.username + 16);
379  snprintf(combined + 16, 17, "%16s", st.username);
380  } else {
381  combined[0] = '\0';
382  }
383 
384  append_attr_address(&attr, STUN_MAPPED_ADDRESS, src, &resplen, &respleft);
385  resp->msglen = htons(resplen);
386  resp->msgtype = htons(STUN_BINDRESP);
387  stun_send(s, src, resp);
388  ast_stun_request(s, src, combined, NULL);
389  ret = AST_STUN_ACCEPT;
390  break;
391  default:
392  if (ast_debug_stun_packet_is_allowed)
393  ast_verbose("Dunno what to do with STUN message %04x (%s)\n", (unsigned)ntohs(hdr->msgtype), stun_msg2str(ntohs(hdr->msgtype)));
394  }
395  }
396  return ret;
397 }
static const char * stun_msg2str(int msg)
helper function to print message names
Definition: stun.c:126
#define STUN_MAPPED_ADDRESS
Basic attribute types in stun messages. Messages can also contain custom attributes (codes above 0x7f...
Definition: stun.c:111
Definition: stun.c:78
int ast_stun_request(int s, struct sockaddr_in *dst, const char *username, struct sockaddr_in *answer)
Generic STUN request.
Definition: stun.c:415
static void append_attr_address(struct stun_attr **attr, int attrval, struct sockaddr_in *sin, int *len, int *left)
append an address to an STUN message
Definition: stun.c:222
#define ast_debug_stun(sublevel,...)
Log debug level STUN information.
Definition: stun.h:54
#define STUN_BINDREQ
STUN message types 'BIND' refers to transactions used to determine the externally visible addresses...
Definition: stun.c:101
here we store credentials extracted from a message
Definition: stun.c:176
static void append_attr_string(struct stun_attr **attr, int attrval, const char *s, int *len, int *left)
append a string to an STUN message
Definition: stun.c:205
static int stun_send(int s, struct sockaddr_in *dst, struct stun_header *resp)
wrapper to send an STUN message
Definition: stun.c:264
static const char * stun_attr2str(int msg)
helper function to print attribute names
Definition: stun.c:146
void ast_stun_init ( void  )

Initialize the STUN system in Asterisk.

Provided by stun.c

Definition at line 576 of file stun.c.

References ast_cli_register_multiple, and ast_register_atexit().

577 {
578  ast_cli_register_multiple(cli_stun, sizeof(cli_stun) / sizeof(struct ast_cli_entry));
579  debug_category_stun_id = ast_debug_category_register(AST_LOG_CATEGORY_STUN);
580  debug_category_stun_packet_id = ast_debug_category_register(AST_LOG_CATEGORY_STUN_PACKET);
581 
582  /*
583  * Normnally a core module should call ast_register_cleanup
584  * which doesn't run if any module fails to unload. This
585  * prevents resources being pulled out from under a running
586  * module and ppossibly causing a segfault. In this case however,
587  * the only thing we're cleaning up is the cli command and
588  * the registers of the debug categories.
589  */
590  ast_register_atexit(stun_shutdown);
591 
592 }
descriptor for a cli entry.
Definition: cli.h:171
#define ast_cli_register_multiple(e, len)
Register multiple commands.
Definition: cli.h:265
int ast_register_atexit(void(*func)(void))
Register a function to be executed before Asterisk exits.
Definition: clicompat.c:13
int ast_stun_request ( int  s,
struct sockaddr_in *  dst,
const char *  username,
struct sockaddr_in *  answer 
)

Generic STUN request.

Parameters
sThe socket used to send the request.
dstIf non null, the address of the STUN server. Only needed if the socket is not bound or connected.
usernameIf non null, add the username in the request.
answerIf non null, the function waits for a response and puts here the externally visible address.

Send a generic STUN request to the server specified, possibly waiting for a reply and filling the answer parameter with the externally visible address. Note that in this case the request will be blocking.

Note
The interface may change slightly in the future.
Return values
0on success.
<0on error.
>0on timeout.

Definition at line 415 of file stun.c.

References append_attr_string(), ast_debug_stun, ast_remaining_ms(), ast_stun_handle_packet(), ast_tvnow(), STUN_BINDREQ, stun_get_mapped(), stun_req_id(), and stun_send().

Referenced by ast_rtp_stun_request(), ast_stun_handle_packet(), rtp_add_candidates_to_ice(), and stun_monitor_request().

417 {
418  struct stun_header *req;
419  struct stun_header *rsp;
420  unsigned char req_buf[1024];
421  unsigned char rsp_buf[1024];
422  int reqlen, reqleft;
423  struct stun_attr *attr;
424  int res = -1;
425  int retry;
426 
427  if (answer) {
428  /* Always clear answer in case the request fails. */
429  memset(answer, 0, sizeof(struct sockaddr_in));
430  }
431 
432  /* Create STUN bind request */
433  req = (struct stun_header *) req_buf;
434  stun_req_id(req);
435  reqlen = 0;
436  reqleft = sizeof(req_buf) - sizeof(struct stun_header);
437  attr = (struct stun_attr *) req->ies;
438  if (username) {
439  append_attr_string(&attr, STUN_USERNAME, username, &reqlen, &reqleft);
440  }
441  req->msglen = htons(reqlen);
442  req->msgtype = htons(STUN_BINDREQ);
443 
444  for (retry = 0; retry++ < STUN_MAX_RETRIES;) { /* XXX make retries configurable */
445  /* send request, possibly wait for reply */
446  struct sockaddr_in src;
447  socklen_t srclen;
448  struct timeval start;
449 
450  /* Send STUN message. */
451  res = stun_send(s, dst, req);
452  if (res < 0) {
453  ast_debug_stun(1, "stun_send try %d failed: %s\n", retry, strerror(errno));
454  break;
455  }
456  if (!answer) {
457  /* Successful send since we don't care about any response. */
458  res = 0;
459  break;
460  }
461 
462  start = ast_tvnow();
463 try_again:
464  /* Wait for response. */
465  {
466  struct pollfd pfds = { .fd = s, .events = POLLIN };
467  int ms;
468 
469  ms = ast_remaining_ms(start, 3000);
470  if (ms <= 0) {
471  /* No response, timeout */
472  handle_stun_timeout(retry, dst);
473  res = 1;
474  continue;
475  }
476  res = ast_poll(&pfds, 1, ms);
477  if (res < 0) {
478  /* Error */
479  continue;
480  }
481  if (!res) {
482  /* No response, timeout */
483  handle_stun_timeout(retry, dst);
484  res = 1;
485  continue;
486  }
487  }
488 
489  /* Read STUN response. */
490  memset(&src, 0, sizeof(src));
491  srclen = sizeof(src);
492  /* XXX pass sizeof(rsp_buf) - 1 in the size, because stun_handle_packet might
493  * write past the end of the buffer.
494  */
495  res = recvfrom(s, rsp_buf, sizeof(rsp_buf) - 1,
496  0, (struct sockaddr *) &src, &srclen);
497  if (res < 0) {
498  ast_debug_stun(1, "recvfrom try %d failed: %s\n", retry, strerror(errno));
499  break;
500  }
501 
502  /* Process the STUN response. */
503  rsp = (struct stun_header *) rsp_buf;
504  if (ast_stun_handle_packet(s, &src, rsp_buf, res, stun_get_mapped, answer)
505  || (rsp->msgtype != htons(STUN_BINDRESP)
506  && rsp->msgtype != htons(STUN_BINDERR))
507  || stun_id_cmp(&req->id, &rsp->id)) {
508  /* Bad STUN packet, not right type, or transaction ID did not match. */
509  memset(answer, 0, sizeof(struct sockaddr_in));
510 
511  /* Was not a resonse to our request. */
512  goto try_again;
513  }
514  /* Success. answer contains the external address if available. */
515  res = 0;
516  break;
517  }
518  return res;
519 }
int ast_stun_handle_packet(int s, struct sockaddr_in *src, unsigned char *data, size_t len, stun_cb_f *stun_cb, void *arg)
handle an incoming STUN message.
Definition: stun.c:293
Definition: stun.c:78
struct timeval ast_tvnow(void)
Returns current timeval. Meant to replace calls to gettimeofday().
Definition: time.h:159
static int stun_get_mapped(struct stun_attr *attr, void *arg)
Extract the STUN_MAPPED_ADDRESS from the stun response. This is used as a callback for stun_handle_re...
Definition: stun.c:403
static void stun_req_id(struct stun_header *req)
helper function to generate a random request id
Definition: stun.c:286
#define ast_debug_stun(sublevel,...)
Log debug level STUN information.
Definition: stun.h:54
int ast_remaining_ms(struct timeval start, int max_ms)
Calculate remaining milliseconds given a starting timestamp and upper bound.
Definition: utils.c:2281
#define STUN_BINDREQ
STUN message types 'BIND' refers to transactions used to determine the externally visible addresses...
Definition: stun.c:101
static void append_attr_string(struct stun_attr **attr, int attrval, const char *s, int *len, int *left)
append a string to an STUN message
Definition: stun.c:205
static int stun_send(int s, struct sockaddr_in *dst, struct stun_header *resp)
wrapper to send an STUN message
Definition: stun.c:264

Variable Documentation

struct ast_cli_entry cli_stun[]
static
Initial value:
= {
{ .handler = handle_cli_stun_set_debug , .summary = "Enable/Disable STUN debugging" ,},
}

Definition at line 549 of file stun.c.