Sat Apr 26 2014 22:03:19

Asterisk developer's documentation


stun.c File Reference

STUN Support. More...

#include "asterisk.h"
#include "asterisk/_private.h"
#include "asterisk/stun.h"
#include "asterisk/cli.h"
#include "asterisk/utils.h"
#include "asterisk/channel.h"
Include dependency graph for stun.c:

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

Defines

#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_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
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.
void ast_stun_init (void)
 Initialize the STUN system in Asterisk.
int ast_stun_request (int s, struct sockaddr_in *dst, const char *username, struct sockaddr_in *answer)
 Generic STUN request.
static char * handle_cli_stun_set_debug (struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
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 int stundebug

Detailed Description

STUN Support.

Author:
Mark Spencer <markster@digium.com>
Note:
STUN is defined in RFC 3489.

Definition in file stun.c.


Define Documentation

#define STUN_BINDERR   0x0111

Definition at line 105 of file stun.c.

Referenced by ast_stun_request(), and stun_msg2str().

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

Definition at line 103 of file stun.c.

Referenced by ast_stun_handle_packet(), ast_stun_request(), and stun_msg2str().

#define STUN_BINDRESP   0x0101

Definition at line 104 of file stun.c.

Referenced by ast_stun_handle_packet(), ast_stun_request(), and stun_msg2str().

#define STUN_CHANGE_REQUEST   0x0003

Definition at line 115 of file stun.c.

Referenced by stun_attr2str().

#define STUN_CHANGED_ADDRESS   0x0005

Definition at line 117 of file stun.c.

Referenced by stun_attr2str().

#define STUN_ERROR_CODE   0x0009

Definition at line 121 of file stun.c.

Referenced by stun_attr2str().

#define STUN_MAPPED_ADDRESS   0x0001

Basic attribute types in stun messages. Messages can also contain custom attributes (codes above 0x7fff)

Definition at line 113 of file stun.c.

Referenced by ast_stun_handle_packet(), stun_attr2str(), and stun_get_mapped().

#define STUN_MESSAGE_INTEGRITY   0x0008

Definition at line 120 of file stun.c.

Referenced by stun_attr2str().

#define STUN_PASSWORD   0x0007

Definition at line 119 of file stun.c.

Referenced by stun_attr2str(), and stun_process_attr().

#define STUN_REFLECTED_FROM   0x000b

Definition at line 123 of file stun.c.

Referenced by stun_attr2str().

#define STUN_RESPONSE_ADDRESS   0x0002

Definition at line 114 of file stun.c.

Referenced by stun_attr2str().

#define STUN_SECERR   0x0112

Definition at line 108 of file stun.c.

Referenced by stun_msg2str().

#define STUN_SECREQ   0x0002

Definition at line 106 of file stun.c.

Referenced by stun_msg2str().

#define STUN_SECRESP   0x0102

Definition at line 107 of file stun.c.

Referenced by stun_msg2str().

#define STUN_SOURCE_ADDRESS   0x0004

Definition at line 116 of file stun.c.

Referenced by stun_attr2str().

#define STUN_UNKNOWN_ATTRIBUTES   0x000a

Definition at line 122 of file stun.c.

Referenced by stun_attr2str().

#define STUN_USERNAME   0x0006

Definition at line 118 of file stun.c.

Referenced by ast_stun_handle_packet(), ast_stun_request(), stun_attr2str(), and stun_process_attr().


Function Documentation

static void append_attr_address ( struct stun_attr **  attr,
int  attrval,
struct sockaddr_in *  sin,
int *  len,
int *  left 
) [static]

append an address to an STUN message

Definition at line 216 of file stun.c.

References stun_addr::addr, stun_addr::family, stun_addr::port, and stun_addr::unused.

Referenced by ast_stun_handle_packet().

{
   int size = sizeof(**attr) + 8;
   struct stun_addr *addr;
   if (*left > size) {
      (*attr)->attr = htons(attrval);
      (*attr)->len = htons(8);
      addr = (struct stun_addr *)((*attr)->value);
      addr->unused = 0;
      addr->family = 0x01;
      addr->port = sin->sin_port;
      addr->addr = sin->sin_addr.s_addr;
      (*attr) = (struct stun_attr *)((*attr)->value + 8);
      *len += size;
      *left -= size;
   }
}
static void append_attr_string ( struct stun_attr **  attr,
int  attrval,
const char *  s,
int *  len,
int *  left 
) [static]

append a string to an STUN message

Definition at line 202 of file stun.c.

Referenced by ast_stun_handle_packet(), and ast_stun_request().

{
   int size = sizeof(**attr) + strlen(s);
   if (*left > size) {
      (*attr)->attr = htons(attrval);
      (*attr)->len = htons(strlen(s));
      memcpy((*attr)->value, s, strlen(s));
      (*attr) = (struct stun_attr *)((*attr)->value + strlen(s));
      *len += size;
      *left -= size;
   }
}
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 264 of file stun.c.

References append_attr_address(), append_attr_string(), ast_debug, AST_STUN_ACCEPT, AST_STUN_IGNORE, ast_stun_request(), ast_verbose(), stun_attr::attr, stun_header::id, stun_header::ies, stun_attr::len, stun_header::msglen, stun_header::msgtype, stun_attr2str(), STUN_BINDREQ, STUN_BINDRESP, STUN_MAPPED_ADDRESS, stun_msg2str(), stun_process_attr(), stun_send(), STUN_USERNAME, and stun_state::username.

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

{
   struct stun_header *hdr = (struct stun_header *)data;
   struct stun_attr *attr;
   struct stun_state st;
   int ret = AST_STUN_IGNORE;
   int x;

   /* On entry, 'len' is the length of the udp payload. After the
    * initial checks it becomes the size of unprocessed options,
    * while 'data' is advanced accordingly.
    */
   if (len < sizeof(struct stun_header)) {
      ast_debug(1, "Runt STUN packet (only %d, wanting at least %d)\n", (int) len, (int) sizeof(struct stun_header));
      return -1;
   }
   len -= sizeof(struct stun_header);
   data += sizeof(struct stun_header);
   x = ntohs(hdr->msglen); /* len as advertised in the message */
   if (stundebug)
      ast_verbose("STUN Packet, msg %s (%04x), length: %d\n", stun_msg2str(ntohs(hdr->msgtype)), ntohs(hdr->msgtype), x);
   if (x > len) {
      ast_debug(1, "Scrambled STUN packet length (got %d, expecting %d)\n", x, (int)len);
   } else
      len = x;
   memset(&st, 0, sizeof(st));
   while (len) {
      if (len < sizeof(struct stun_attr)) {
         ast_debug(1, "Runt Attribute (got %d, expecting %d)\n", (int)len, (int) sizeof(struct stun_attr));
         break;
      }
      attr = (struct stun_attr *)data;
      /* compute total attribute length */
      x = ntohs(attr->len) + sizeof(struct stun_attr);
      if (x > len) {
         ast_debug(1, "Inconsistent Attribute (length %d exceeds remaining msg len %d)\n", x, (int)len);
         break;
      }
      if (stun_cb)
         stun_cb(attr, arg);
      if (stun_process_attr(&st, attr)) {
         ast_debug(1, "Failed to handle attribute %s (%04x)\n", stun_attr2str(ntohs(attr->attr)), ntohs(attr->attr));
         break;
      }
      /* Clear attribute id: in case previous entry was a string,
       * this will act as the terminator for the string.
       */
      attr->attr = 0;
      data += x;
      len -= x;
   }
   /* Null terminate any string.
    * XXX NOTE, we write past the size of the buffer passed by the
    * caller, so this is potentially dangerous. The only thing that
    * saves us is that usually we read the incoming message in a
    * much larger buffer in the struct ast_rtp
    */
   *data = '\0';

   /* Now prepare to generate a reply, which at the moment is done
    * only for properly formed (len == 0) STUN_BINDREQ messages.
    */
   if (len == 0) {
      unsigned char respdata[1024];
      struct stun_header *resp = (struct stun_header *)respdata;
      int resplen = 0;  /* len excluding header */
      int respleft = sizeof(respdata) - sizeof(struct stun_header);
      char combined[33];

      resp->id = hdr->id;
      resp->msgtype = 0;
      resp->msglen = 0;
      attr = (struct stun_attr *)resp->ies;
      switch (ntohs(hdr->msgtype)) {
      case STUN_BINDREQ:
         if (stundebug)
            ast_verbose("STUN Bind Request, username: %s\n",
                   st.username ? st.username : "<none>");
         if (st.username) {
            append_attr_string(&attr, STUN_USERNAME, st.username, &resplen, &respleft);
            snprintf(combined, sizeof(combined), "%16s%16s", st.username + 16, st.username);
         }

         append_attr_address(&attr, STUN_MAPPED_ADDRESS, src, &resplen, &respleft);
         resp->msglen = htons(resplen);
         resp->msgtype = htons(STUN_BINDRESP);
         stun_send(s, src, resp);
         ast_stun_request(s, src, combined, NULL);
         ret = AST_STUN_ACCEPT;
         break;
      default:
         if (stundebug)
            ast_verbose("Dunno what to do with STUN message %04x (%s)\n", ntohs(hdr->msgtype), stun_msg2str(ntohs(hdr->msgtype)));
      }
   }
   return ret;
}
void ast_stun_init ( void  )

Initialize the STUN system in Asterisk.

Provided by stun.c

Definition at line 513 of file stun.c.

References ast_cli_register_multiple(), ast_register_atexit(), and stun_shutdown().

Referenced by main().

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 378 of file stun.c.

References append_attr_string(), ast_debug, ast_poll, ast_stun_handle_packet(), stun_attr::attr, errno, stun_header::id, stun_header::ies, stun_header::msglen, stun_header::msgtype, STUN_BINDERR, STUN_BINDREQ, STUN_BINDRESP, stun_get_mapped(), stun_id_cmp(), stun_req_id(), stun_send(), and STUN_USERNAME.

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

{
   struct stun_header *req;
   struct stun_header *rsp;
   unsigned char req_buf[1024];
   unsigned char rsp_buf[1024];
   int reqlen, reqleft;
   struct stun_attr *attr;
   int res = -1;
   int retry;

   if (answer) {
      /* Always clear answer in case the request fails. */
      memset(answer, 0, sizeof(struct sockaddr_in));
   }

   /* Create STUN bind request */
   req = (struct stun_header *) req_buf;
   stun_req_id(req);
   reqlen = 0;
   reqleft = sizeof(req_buf) - sizeof(struct stun_header);
   req->msgtype = 0;
   req->msglen = 0;
   attr = (struct stun_attr *) req->ies;
   if (username) {
      append_attr_string(&attr, STUN_USERNAME, username, &reqlen, &reqleft);
   }
   req->msglen = htons(reqlen);
   req->msgtype = htons(STUN_BINDREQ);

   for (retry = 0; retry++ < 3;) {  /* XXX make retries configurable */
      /* send request, possibly wait for reply */
      struct sockaddr_in src;
      socklen_t srclen;

      /* Send STUN message. */
      res = stun_send(s, dst, req);
      if (res < 0) {
         ast_debug(1, "stun_send try %d failed: %s\n", retry, strerror(errno));
         break;
      }
      if (!answer) {
         /* Successful send since we don't care about any response. */
         res = 0;
         break;
      }

try_again:
      /* Wait for response. */
      {
         struct pollfd pfds = { .fd = s, .events = POLLIN };

         res = ast_poll(&pfds, 1, 3000);
         if (res < 0) {
            /* Error */
            continue;
         }
         if (!res) {
            /* No response, timeout */
            res = 1;
            continue;
         }
      }

      /* Read STUN response. */
      memset(&src, 0, sizeof(src));
      srclen = sizeof(src);
      /* XXX pass sizeof(rsp_buf) - 1 in the size, because stun_handle_packet might
       * write past the end of the buffer.
       */
      res = recvfrom(s, rsp_buf, sizeof(rsp_buf) - 1,
         0, (struct sockaddr *) &src, &srclen);
      if (res < 0) {
         ast_debug(1, "recvfrom try %d failed: %s\n", retry, strerror(errno));
         break;
      }

      /* Process the STUN response. */
      rsp = (struct stun_header *) rsp_buf;
      if (ast_stun_handle_packet(s, &src, rsp_buf, res, stun_get_mapped, answer)
         || (rsp->msgtype != htons(STUN_BINDRESP)
            && rsp->msgtype != htons(STUN_BINDERR))
         || stun_id_cmp(&req->id, &rsp->id)) {
         /* Bad STUN packet, not right type, or transaction ID did not match. */
         memset(answer, 0, sizeof(struct sockaddr_in));

         /* Was not a resonse to our request. */
         goto try_again;
      }
      /* Success.  answer contains the external address if available. */
      res = 0;
      break;
   }
   return res;
}
static char* handle_cli_stun_set_debug ( struct ast_cli_entry e,
int  cmd,
struct ast_cli_args a 
) [static]

Definition at line 475 of file stun.c.

References ast_cli_args::argc, ast_cli_entry::args, ast_cli_args::argv, ast_cli(), CLI_GENERATE, CLI_INIT, CLI_SHOWUSAGE, CLI_SUCCESS, ast_cli_entry::command, ast_cli_args::fd, and ast_cli_entry::usage.

{
   switch (cmd) {
   case CLI_INIT:
      e->command = "stun set debug {on|off}";
      e->usage =
         "Usage: stun set debug {on|off}\n"
         "       Enable/Disable STUN (Simple Traversal of UDP through NATs)\n"
         "       debugging\n";
      return NULL;
   case CLI_GENERATE:
      return NULL;
   }

   if (a->argc != e->args)
      return CLI_SHOWUSAGE;

   if (!strncasecmp(a->argv[e->args-1], "on", 2))
      stundebug = 1;
   else if (!strncasecmp(a->argv[e->args-1], "off", 3))
      stundebug = 0;
   else
      return CLI_SHOWUSAGE;

   ast_cli(a->fd, "STUN Debugging %s\n", stundebug ? "Enabled" : "Disabled");
   return CLI_SUCCESS;
}
static const char* stun_attr2str ( int  msg) [static]

helper function to print attribute names

Definition at line 146 of file stun.c.

References STUN_CHANGE_REQUEST, STUN_CHANGED_ADDRESS, STUN_ERROR_CODE, STUN_MAPPED_ADDRESS, STUN_MESSAGE_INTEGRITY, STUN_PASSWORD, STUN_REFLECTED_FROM, STUN_RESPONSE_ADDRESS, STUN_SOURCE_ADDRESS, STUN_UNKNOWN_ATTRIBUTES, and STUN_USERNAME.

Referenced by ast_stun_handle_packet(), and stun_process_attr().

{
   switch (msg) {
   case STUN_MAPPED_ADDRESS:
      return "Mapped Address";
   case STUN_RESPONSE_ADDRESS:
      return "Response Address";
   case STUN_CHANGE_REQUEST:
      return "Change Request";
   case STUN_SOURCE_ADDRESS:
      return "Source Address";
   case STUN_CHANGED_ADDRESS:
      return "Changed Address";
   case STUN_USERNAME:
      return "Username";
   case STUN_PASSWORD:
      return "Password";
   case STUN_MESSAGE_INTEGRITY:
      return "Message Integrity";
   case STUN_ERROR_CODE:
      return "Error Code";
   case STUN_UNKNOWN_ATTRIBUTES:
      return "Unknown Attributes";
   case STUN_REFLECTED_FROM:
      return "Reflected From";
   }
   return "Non-RFC3489 Attribute";
}
static int stun_get_mapped ( struct stun_attr attr,
void *  arg 
) [static]

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.

Definition at line 366 of file stun.c.

References stun_addr::addr, stun_attr::attr, stun_attr::len, stun_addr::port, and STUN_MAPPED_ADDRESS.

Referenced by ast_stun_request().

{
   struct stun_addr *addr = (struct stun_addr *)(attr + 1);
   struct sockaddr_in *sa = (struct sockaddr_in *)arg;

   if (ntohs(attr->attr) != STUN_MAPPED_ADDRESS || ntohs(attr->len) != 8)
      return 1;   /* not us. */
   sa->sin_port = addr->port;
   sa->sin_addr.s_addr = addr->addr;
   return 0;
}
static int stun_id_cmp ( stun_trans_id left,
stun_trans_id right 
) [static]

Definition at line 251 of file stun.c.

Referenced by ast_stun_request().

{
   return memcmp(left, right, sizeof(*left));
}
static const char* stun_msg2str ( int  msg) [static]

helper function to print message names

Definition at line 126 of file stun.c.

References STUN_BINDERR, STUN_BINDREQ, STUN_BINDRESP, STUN_SECERR, STUN_SECREQ, and STUN_SECRESP.

Referenced by ast_stun_handle_packet().

{
   switch (msg) {
   case STUN_BINDREQ:
      return "Binding Request";
   case STUN_BINDRESP:
      return "Binding Response";
   case STUN_BINDERR:
      return "Binding Error Response";
   case STUN_SECREQ:
      return "Shared Secret Request";
   case STUN_SECRESP:
      return "Shared Secret Response";
   case STUN_SECERR:
      return "Shared Secret Error Response";
   }
   return "Non-RFC3489 Message";
}
static int stun_process_attr ( struct stun_state state,
struct stun_attr attr 
) [static]

Definition at line 181 of file stun.c.

References ast_verbose(), stun_attr::attr, stun_attr::len, stun_state::password, stun_attr2str(), STUN_PASSWORD, STUN_USERNAME, stun_state::username, and stun_attr::value.

Referenced by ast_stun_handle_packet().

{
   if (stundebug)
      ast_verbose("Found STUN Attribute %s (%04x), length %d\n",
             stun_attr2str(ntohs(attr->attr)), ntohs(attr->attr), ntohs(attr->len));
   switch (ntohs(attr->attr)) {
   case STUN_USERNAME:
      state->username = (const char *) (attr->value);
      break;
   case STUN_PASSWORD:
      state->password = (const char *) (attr->value);
      break;
   default:
      if (stundebug)
         ast_verbose("Ignoring STUN attribute %s (%04x), length %d\n",
                stun_attr2str(ntohs(attr->attr)), ntohs(attr->attr), ntohs(attr->len));
   }
   return 0;
}
static void stun_req_id ( struct stun_header req) [static]

helper function to generate a random request id

Definition at line 257 of file stun.c.

References ast_random(), stun_trans_id::id, and stun_header::id.

Referenced by ast_stun_request().

{
   int x;
   for (x = 0; x < 4; x++)
      req->id.id[x] = ast_random();
}
static int stun_send ( int  s,
struct sockaddr_in *  dst,
struct stun_header resp 
) [static]

wrapper to send an STUN message

Definition at line 235 of file stun.c.

References stun_header::msglen.

Referenced by ast_stun_handle_packet(), and ast_stun_request().

{
   return sendto(s, resp, ntohs(resp->msglen) + sizeof(*resp), 0,
            (struct sockaddr *)dst, sizeof(*dst));
}
static void stun_shutdown ( void  ) [static]

Definition at line 507 of file stun.c.

References ast_cli_unregister_multiple().

Referenced by ast_stun_init().


Variable Documentation

struct ast_cli_entry cli_stun[] [static]
Initial value:
 {
   AST_CLI_DEFINE(handle_cli_stun_set_debug, "Enable/Disable STUN debugging"),
}

Definition at line 503 of file stun.c.

int stundebug [static]

Are we debugging stun?

Definition at line 43 of file stun.c.