Sat Mar 10 01:54:27 2012

Asterisk developer's documentation


stun.c

Go to the documentation of this file.
00001 /*
00002  * Asterisk -- An open source telephony toolkit.
00003  *
00004  * Copyright (C) 1999 - 2008, Digium, Inc.
00005  *
00006  * Mark Spencer <markster@digium.com>
00007  *
00008  * See http://www.asterisk.org for more information about
00009  * the Asterisk project. Please do not directly contact
00010  * any of the maintainers of this project for assistance;
00011  * the project provides a web site, mailing lists and IRC
00012  * channels for your use.
00013  *
00014  * This program is free software, distributed under the terms of
00015  * the GNU General Public License Version 2. See the LICENSE file
00016  * at the top of the source tree.
00017  */
00018 
00019 /*!
00020  * \file
00021  *
00022  * \brief STUN Support
00023  *
00024  * \author Mark Spencer <markster@digium.com>
00025  *
00026  * \note STUN is defined in RFC 3489.
00027  */
00028 
00029 #include "asterisk.h"
00030 
00031 ASTERISK_FILE_VERSION(__FILE__, "$Revision: 346700 $")
00032 
00033 #include "asterisk/_private.h"
00034 #include "asterisk/stun.h"
00035 #include "asterisk/cli.h"
00036 #include "asterisk/utils.h"
00037 #include "asterisk/channel.h"
00038 
00039 static int stundebug;         /*!< Are we debugging stun? */
00040 
00041 /*!
00042  * \brief STUN support code
00043  *
00044  * This code provides some support for doing STUN transactions.
00045  * Eventually it should be moved elsewhere as other protocols
00046  * than RTP can benefit from it - e.g. SIP.
00047  * STUN is described in RFC3489 and it is based on the exchange
00048  * of UDP packets between a client and one or more servers to
00049  * determine the externally visible address (and port) of the client
00050  * once it has gone through the NAT boxes that connect it to the
00051  * outside.
00052  * The simplest request packet is just the header defined in
00053  * struct stun_header, and from the response we may just look at
00054  * one attribute, STUN_MAPPED_ADDRESS, that we find in the response.
00055  * By doing more transactions with different server addresses we
00056  * may determine more about the behaviour of the NAT boxes, of
00057  * course - the details are in the RFC.
00058  *
00059  * All STUN packets start with a simple header made of a type,
00060  * length (excluding the header) and a 16-byte random transaction id.
00061  * Following the header we may have zero or more attributes, each
00062  * structured as a type, length and a value (whose format depends
00063  * on the type, but often contains addresses).
00064  * Of course all fields are in network format.
00065  */
00066 
00067 typedef struct { unsigned int id[4]; } __attribute__((packed)) stun_trans_id;
00068 
00069 struct stun_header {
00070    unsigned short msgtype;
00071    unsigned short msglen;
00072    stun_trans_id  id;
00073    unsigned char ies[0];
00074 } __attribute__((packed));
00075 
00076 struct stun_attr {
00077    unsigned short attr;
00078    unsigned short len;
00079    unsigned char value[0];
00080 } __attribute__((packed));
00081 
00082 /*
00083  * The format normally used for addresses carried by STUN messages.
00084  */
00085 struct stun_addr {
00086    unsigned char unused;
00087    unsigned char family;
00088    unsigned short port;
00089    unsigned int addr;
00090 } __attribute__((packed));
00091 
00092 /*! \brief STUN message types
00093  * 'BIND' refers to transactions used to determine the externally
00094  * visible addresses. 'SEC' refers to transactions used to establish
00095  * a session key for subsequent requests.
00096  * 'SEC' functionality is not supported here.
00097  */
00098 
00099 #define STUN_BINDREQ 0x0001
00100 #define STUN_BINDRESP   0x0101
00101 #define STUN_BINDERR 0x0111
00102 #define STUN_SECREQ  0x0002
00103 #define STUN_SECRESP 0x0102
00104 #define STUN_SECERR  0x0112
00105 
00106 /*! \brief Basic attribute types in stun messages.
00107  * Messages can also contain custom attributes (codes above 0x7fff)
00108  */
00109 #define STUN_MAPPED_ADDRESS   0x0001
00110 #define STUN_RESPONSE_ADDRESS 0x0002
00111 #define STUN_CHANGE_REQUEST   0x0003
00112 #define STUN_SOURCE_ADDRESS   0x0004
00113 #define STUN_CHANGED_ADDRESS  0x0005
00114 #define STUN_USERNAME      0x0006
00115 #define STUN_PASSWORD      0x0007
00116 #define STUN_MESSAGE_INTEGRITY   0x0008
00117 #define STUN_ERROR_CODE    0x0009
00118 #define STUN_UNKNOWN_ATTRIBUTES  0x000a
00119 #define STUN_REFLECTED_FROM   0x000b
00120 
00121 /*! \brief helper function to print message names */
00122 static const char *stun_msg2str(int msg)
00123 {
00124    switch (msg) {
00125    case STUN_BINDREQ:
00126       return "Binding Request";
00127    case STUN_BINDRESP:
00128       return "Binding Response";
00129    case STUN_BINDERR:
00130       return "Binding Error Response";
00131    case STUN_SECREQ:
00132       return "Shared Secret Request";
00133    case STUN_SECRESP:
00134       return "Shared Secret Response";
00135    case STUN_SECERR:
00136       return "Shared Secret Error Response";
00137    }
00138    return "Non-RFC3489 Message";
00139 }
00140 
00141 /*! \brief helper function to print attribute names */
00142 static const char *stun_attr2str(int msg)
00143 {
00144    switch (msg) {
00145    case STUN_MAPPED_ADDRESS:
00146       return "Mapped Address";
00147    case STUN_RESPONSE_ADDRESS:
00148       return "Response Address";
00149    case STUN_CHANGE_REQUEST:
00150       return "Change Request";
00151    case STUN_SOURCE_ADDRESS:
00152       return "Source Address";
00153    case STUN_CHANGED_ADDRESS:
00154       return "Changed Address";
00155    case STUN_USERNAME:
00156       return "Username";
00157    case STUN_PASSWORD:
00158       return "Password";
00159    case STUN_MESSAGE_INTEGRITY:
00160       return "Message Integrity";
00161    case STUN_ERROR_CODE:
00162       return "Error Code";
00163    case STUN_UNKNOWN_ATTRIBUTES:
00164       return "Unknown Attributes";
00165    case STUN_REFLECTED_FROM:
00166       return "Reflected From";
00167    }
00168    return "Non-RFC3489 Attribute";
00169 }
00170 
00171 /*! \brief here we store credentials extracted from a message */
00172 struct stun_state {
00173    const char *username;
00174    const char *password;
00175 };
00176 
00177 static int stun_process_attr(struct stun_state *state, struct stun_attr *attr)
00178 {
00179    if (stundebug)
00180       ast_verbose("Found STUN Attribute %s (%04x), length %d\n",
00181              stun_attr2str(ntohs(attr->attr)), ntohs(attr->attr), ntohs(attr->len));
00182    switch (ntohs(attr->attr)) {
00183    case STUN_USERNAME:
00184       state->username = (const char *) (attr->value);
00185       break;
00186    case STUN_PASSWORD:
00187       state->password = (const char *) (attr->value);
00188       break;
00189    default:
00190       if (stundebug)
00191          ast_verbose("Ignoring STUN attribute %s (%04x), length %d\n",
00192                 stun_attr2str(ntohs(attr->attr)), ntohs(attr->attr), ntohs(attr->len));
00193    }
00194    return 0;
00195 }
00196 
00197 /*! \brief append a string to an STUN message */
00198 static void append_attr_string(struct stun_attr **attr, int attrval, const char *s, int *len, int *left)
00199 {
00200    int size = sizeof(**attr) + strlen(s);
00201    if (*left > size) {
00202       (*attr)->attr = htons(attrval);
00203       (*attr)->len = htons(strlen(s));
00204       memcpy((*attr)->value, s, strlen(s));
00205       (*attr) = (struct stun_attr *)((*attr)->value + strlen(s));
00206       *len += size;
00207       *left -= size;
00208    }
00209 }
00210 
00211 /*! \brief append an address to an STUN message */
00212 static void append_attr_address(struct stun_attr **attr, int attrval, struct sockaddr_in *sin, int *len, int *left)
00213 {
00214    int size = sizeof(**attr) + 8;
00215    struct stun_addr *addr;
00216    if (*left > size) {
00217       (*attr)->attr = htons(attrval);
00218       (*attr)->len = htons(8);
00219       addr = (struct stun_addr *)((*attr)->value);
00220       addr->unused = 0;
00221       addr->family = 0x01;
00222       addr->port = sin->sin_port;
00223       addr->addr = sin->sin_addr.s_addr;
00224       (*attr) = (struct stun_attr *)((*attr)->value + 8);
00225       *len += size;
00226       *left -= size;
00227    }
00228 }
00229 
00230 /*! \brief wrapper to send an STUN message */
00231 static int stun_send(int s, struct sockaddr_in *dst, struct stun_header *resp)
00232 {
00233    return sendto(s, resp, ntohs(resp->msglen) + sizeof(*resp), 0,
00234             (struct sockaddr *)dst, sizeof(*dst));
00235 }
00236 
00237 /*!
00238  * \internal
00239  * \brief Compare the STUN tranaction IDs.
00240  *
00241  * \param left Transaction ID.
00242  * \param right Transaction ID.
00243  *
00244  * \retval 0 if match.
00245  * \retval non-zero if not match.
00246  */
00247 static int stun_id_cmp(stun_trans_id *left, stun_trans_id *right)
00248 {
00249    return memcmp(left, right, sizeof(*left));
00250 }
00251 
00252 /*! \brief helper function to generate a random request id */
00253 static void stun_req_id(struct stun_header *req)
00254 {
00255    int x;
00256    for (x = 0; x < 4; x++)
00257       req->id.id[x] = ast_random();
00258 }
00259 
00260 int ast_stun_handle_packet(int s, struct sockaddr_in *src, unsigned char *data, size_t len, stun_cb_f *stun_cb, void *arg)
00261 {
00262    struct stun_header *hdr = (struct stun_header *)data;
00263    struct stun_attr *attr;
00264    struct stun_state st;
00265    int ret = AST_STUN_IGNORE;
00266    int x;
00267 
00268    /* On entry, 'len' is the length of the udp payload. After the
00269     * initial checks it becomes the size of unprocessed options,
00270     * while 'data' is advanced accordingly.
00271     */
00272    if (len < sizeof(struct stun_header)) {
00273       ast_debug(1, "Runt STUN packet (only %d, wanting at least %d)\n", (int) len, (int) sizeof(struct stun_header));
00274       return -1;
00275    }
00276    len -= sizeof(struct stun_header);
00277    data += sizeof(struct stun_header);
00278    x = ntohs(hdr->msglen); /* len as advertised in the message */
00279    if (stundebug)
00280       ast_verbose("STUN Packet, msg %s (%04x), length: %d\n", stun_msg2str(ntohs(hdr->msgtype)), ntohs(hdr->msgtype), x);
00281    if (x > len) {
00282       ast_debug(1, "Scrambled STUN packet length (got %d, expecting %d)\n", x, (int)len);
00283    } else
00284       len = x;
00285    memset(&st, 0, sizeof(st));
00286    while (len) {
00287       if (len < sizeof(struct stun_attr)) {
00288          ast_debug(1, "Runt Attribute (got %d, expecting %d)\n", (int)len, (int) sizeof(struct stun_attr));
00289          break;
00290       }
00291       attr = (struct stun_attr *)data;
00292       /* compute total attribute length */
00293       x = ntohs(attr->len) + sizeof(struct stun_attr);
00294       if (x > len) {
00295          ast_debug(1, "Inconsistent Attribute (length %d exceeds remaining msg len %d)\n", x, (int)len);
00296          break;
00297       }
00298       if (stun_cb)
00299          stun_cb(attr, arg);
00300       if (stun_process_attr(&st, attr)) {
00301          ast_debug(1, "Failed to handle attribute %s (%04x)\n", stun_attr2str(ntohs(attr->attr)), ntohs(attr->attr));
00302          break;
00303       }
00304       /* Clear attribute id: in case previous entry was a string,
00305        * this will act as the terminator for the string.
00306        */
00307       attr->attr = 0;
00308       data += x;
00309       len -= x;
00310    }
00311    /* Null terminate any string.
00312     * XXX NOTE, we write past the size of the buffer passed by the
00313     * caller, so this is potentially dangerous. The only thing that
00314     * saves us is that usually we read the incoming message in a
00315     * much larger buffer in the struct ast_rtp
00316     */
00317    *data = '\0';
00318 
00319    /* Now prepare to generate a reply, which at the moment is done
00320     * only for properly formed (len == 0) STUN_BINDREQ messages.
00321     */
00322    if (len == 0) {
00323       unsigned char respdata[1024];
00324       struct stun_header *resp = (struct stun_header *)respdata;
00325       int resplen = 0;  /* len excluding header */
00326       int respleft = sizeof(respdata) - sizeof(struct stun_header);
00327 
00328       resp->id = hdr->id;
00329       resp->msgtype = 0;
00330       resp->msglen = 0;
00331       attr = (struct stun_attr *)resp->ies;
00332       switch (ntohs(hdr->msgtype)) {
00333       case STUN_BINDREQ:
00334          if (stundebug)
00335             ast_verbose("STUN Bind Request, username: %s\n",
00336                    st.username ? st.username : "<none>");
00337          if (st.username)
00338             append_attr_string(&attr, STUN_USERNAME, st.username, &resplen, &respleft);
00339          append_attr_address(&attr, STUN_MAPPED_ADDRESS, src, &resplen, &respleft);
00340          resp->msglen = htons(resplen);
00341          resp->msgtype = htons(STUN_BINDRESP);
00342          stun_send(s, src, resp);
00343          ret = AST_STUN_ACCEPT;
00344          break;
00345       default:
00346          if (stundebug)
00347             ast_verbose("Dunno what to do with STUN message %04x (%s)\n", ntohs(hdr->msgtype), stun_msg2str(ntohs(hdr->msgtype)));
00348       }
00349    }
00350    return ret;
00351 }
00352 
00353 /*! \brief Extract the STUN_MAPPED_ADDRESS from the stun response.
00354  * This is used as a callback for stun_handle_response
00355  * when called from ast_stun_request.
00356  */
00357 static int stun_get_mapped(struct stun_attr *attr, void *arg)
00358 {
00359    struct stun_addr *addr = (struct stun_addr *)(attr + 1);
00360    struct sockaddr_in *sa = (struct sockaddr_in *)arg;
00361 
00362    if (ntohs(attr->attr) != STUN_MAPPED_ADDRESS || ntohs(attr->len) != 8)
00363       return 1;   /* not us. */
00364    sa->sin_port = addr->port;
00365    sa->sin_addr.s_addr = addr->addr;
00366    return 0;
00367 }
00368 
00369 int ast_stun_request(int s, struct sockaddr_in *dst,
00370    const char *username, struct sockaddr_in *answer)
00371 {
00372    struct stun_header *req;
00373    struct stun_header *rsp;
00374    unsigned char req_buf[1024];
00375    unsigned char rsp_buf[1024];
00376    int reqlen, reqleft;
00377    struct stun_attr *attr;
00378    int res = -1;
00379    int retry;
00380 
00381    if (answer) {
00382       /* Always clear answer in case the request fails. */
00383       memset(answer, 0, sizeof(struct sockaddr_in));
00384    }
00385 
00386    /* Create STUN bind request */
00387    req = (struct stun_header *) req_buf;
00388    stun_req_id(req);
00389    reqlen = 0;
00390    reqleft = sizeof(req_buf) - sizeof(struct stun_header);
00391    req->msgtype = 0;
00392    req->msglen = 0;
00393    attr = (struct stun_attr *) req->ies;
00394    if (username) {
00395       append_attr_string(&attr, STUN_USERNAME, username, &reqlen, &reqleft);
00396    }
00397    req->msglen = htons(reqlen);
00398    req->msgtype = htons(STUN_BINDREQ);
00399 
00400    for (retry = 0; retry++ < 3;) {  /* XXX make retries configurable */
00401       /* send request, possibly wait for reply */
00402       struct sockaddr_in src;
00403       socklen_t srclen;
00404 
00405       /* Send STUN message. */
00406       res = stun_send(s, dst, req);
00407       if (res < 0) {
00408          ast_debug(1, "stun_send try %d failed: %s\n", retry, strerror(errno));
00409          break;
00410       }
00411       if (!answer) {
00412          /* Successful send since we don't care about any response. */
00413          res = 0;
00414          break;
00415       }
00416 
00417 try_again:
00418       /* Wait for response. */
00419       {
00420          struct pollfd pfds = { .fd = s, .events = POLLIN };
00421 
00422          res = ast_poll(&pfds, 1, 3000);
00423          if (res < 0) {
00424             /* Error */
00425             continue;
00426          }
00427          if (!res) {
00428             /* No response, timeout */
00429             res = 1;
00430             continue;
00431          }
00432       }
00433 
00434       /* Read STUN response. */
00435       memset(&src, 0, sizeof(src));
00436       srclen = sizeof(src);
00437       /* XXX pass sizeof(rsp_buf) - 1 in the size, because stun_handle_packet might
00438        * write past the end of the buffer.
00439        */
00440       res = recvfrom(s, rsp_buf, sizeof(rsp_buf) - 1,
00441          0, (struct sockaddr *) &src, &srclen);
00442       if (res < 0) {
00443          ast_debug(1, "recvfrom try %d failed: %s\n", retry, strerror(errno));
00444          break;
00445       }
00446 
00447       /* Process the STUN response. */
00448       rsp = (struct stun_header *) rsp_buf;
00449       if (ast_stun_handle_packet(s, &src, rsp_buf, res, stun_get_mapped, answer)
00450          || (rsp->msgtype != htons(STUN_BINDRESP)
00451             && rsp->msgtype != htons(STUN_BINDERR))
00452          || stun_id_cmp(&req->id, &rsp->id)) {
00453          /* Bad STUN packet, not right type, or transaction ID did not match. */
00454          memset(answer, 0, sizeof(struct sockaddr_in));
00455 
00456          /* Was not a resonse to our request. */
00457          goto try_again;
00458       }
00459       /* Success.  answer contains the external address if available. */
00460       res = 0;
00461       break;
00462    }
00463    return res;
00464 }
00465 
00466 static char *handle_cli_stun_set_debug(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
00467 {
00468    switch (cmd) {
00469    case CLI_INIT:
00470       e->command = "stun set debug {on|off}";
00471       e->usage =
00472          "Usage: stun set debug {on|off}\n"
00473          "       Enable/Disable STUN (Simple Traversal of UDP through NATs)\n"
00474          "       debugging\n";
00475       return NULL;
00476    case CLI_GENERATE:
00477       return NULL;
00478    }
00479 
00480    if (a->argc != e->args)
00481       return CLI_SHOWUSAGE;
00482 
00483    if (!strncasecmp(a->argv[e->args-1], "on", 2))
00484       stundebug = 1;
00485    else if (!strncasecmp(a->argv[e->args-1], "off", 3))
00486       stundebug = 0;
00487    else
00488       return CLI_SHOWUSAGE;
00489 
00490    ast_cli(a->fd, "STUN Debugging %s\n", stundebug ? "Enabled" : "Disabled");
00491    return CLI_SUCCESS;
00492 }
00493 
00494 static struct ast_cli_entry cli_stun[] = {
00495    AST_CLI_DEFINE(handle_cli_stun_set_debug, "Enable/Disable STUN debugging"),
00496 };
00497 
00498 /*! \brief Initialize the STUN system in Asterisk */
00499 void ast_stun_init(void)
00500 {
00501    ast_cli_register_multiple(cli_stun, sizeof(cli_stun) / sizeof(struct ast_cli_entry));
00502 }

Generated on Sat Mar 10 01:54:27 2012 for Asterisk - The Open Source Telephony Project by  doxygen 1.4.7