Asterisk - The Open Source Telephony Project  21.4.1
Macros | Typedefs | Enumerations | Functions | Variables
stun.h File Reference

STUN support. More...

#include "asterisk/network.h"
#include "asterisk/logger_category.h"

Go to the source code of this file.

Macros

#define AST_DEBUG_CATEGORY_STUN   ast_debug_category_stun_id() /* STUN debug logging category id */
 
#define AST_DEBUG_CATEGORY_STUN_PACKET   ast_debug_category_stun_packet_id() /* STUN packet debug logging category id */
 
#define ast_debug_stun(sublevel, ...)   ast_debug_category(sublevel, AST_DEBUG_CATEGORY_STUN, __VA_ARGS__)
 Log debug level STUN information. More...
 
#define ast_debug_stun_packet_is_allowed   ast_debug_category_is_allowed(AST_LOG_CATEGORY_ENABLED, AST_DEBUG_CATEGORY_STUN_PACKET)
 
#define AST_LOG_CATEGORY_STUN   "stun"
 
#define AST_LOG_CATEGORY_STUN_PACKET   "stun_packet"
 

Typedefs

typedef int( stun_cb_f) (struct stun_attr *attr, void *arg)
 callback type to be invoked on stun responses.
 

Enumerations

enum  ast_stun_result { AST_STUN_IGNORE = 0, AST_STUN_ACCEPT }
 

Functions

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...
 
int ast_stun_request (int s, struct sockaddr_in *dst, const char *username, struct sockaddr_in *answer)
 Generic STUN request. More...
 

Variables

static const int STANDARD_STUN_PORT = 3478
 

Detailed Description

STUN support.

STUN is defined in RFC 3489.

Definition in file stun.h.

Macro Definition Documentation

#define ast_debug_stun (   sublevel,
  ... 
)    ast_debug_category(sublevel, AST_DEBUG_CATEGORY_STUN, __VA_ARGS__)

Log debug level STUN information.

Parameters
sublevelDebug output sublevel (>= 0)
...String format and any associated arguments

Definition at line 54 of file stun.h.

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

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
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