Sat Apr 26 2014 22:03:19

Asterisk developer's documentation


stun.h File Reference

STUN support. More...

#include "asterisk/network.h"
Include dependency graph for stun.h:
This graph shows which files directly or indirectly include this file:

Go to the source code of this file.

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

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

Variables

static const int STANDARD_STUN_PORT = 3478

Detailed Description

STUN support.

STUN is defined in RFC 3489.

Definition in file stun.h.


Typedef Documentation

typedef int( stun_cb_f)(struct stun_attr *attr, void *arg)

callback type to be invoked on stun responses.

Definition at line 69 of file stun.h.


Enumeration Type Documentation

Enumerator:
AST_STUN_IGNORE 
AST_STUN_ACCEPT 

Definition at line 37 of file stun.h.


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

Variable Documentation

const int STANDARD_STUN_PORT = 3478 [static]

Definition at line 35 of file stun.h.

Referenced by gtalk_load_config(), rtp_reload(), and setup_stunaddr().