Wed Apr 6 11:29:48 2011

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: 284597 $")
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 /*! \brief helper function to generate a random request id */
00238 static void stun_req_id(struct stun_header *req)
00239 {
00240    int x;
00241    for (x = 0; x < 4; x++)
00242       req->id.id[x] = ast_random();
00243 }
00244 
00245 /*! \brief handle an incoming STUN message.
00246  *
00247  * Do some basic sanity checks on packet size and content,
00248  * try to extract a bit of information, and possibly reply.
00249  * At the moment this only processes BIND requests, and returns
00250  * the externally visible address of the request.
00251  * If a callback is specified, invoke it with the attribute.
00252  */
00253 int ast_stun_handle_packet(int s, struct sockaddr_in *src, unsigned char *data, size_t len, stun_cb_f *stun_cb, void *arg)
00254 {
00255    struct stun_header *hdr = (struct stun_header *)data;
00256    struct stun_attr *attr;
00257    struct stun_state st;
00258    int ret = AST_STUN_IGNORE;
00259    int x;
00260 
00261    /* On entry, 'len' is the length of the udp payload. After the
00262     * initial checks it becomes the size of unprocessed options,
00263     * while 'data' is advanced accordingly.
00264     */
00265    if (len < sizeof(struct stun_header)) {
00266       ast_debug(1, "Runt STUN packet (only %d, wanting at least %d)\n", (int) len, (int) sizeof(struct stun_header));
00267       return -1;
00268    }
00269    len -= sizeof(struct stun_header);
00270    data += sizeof(struct stun_header);
00271    x = ntohs(hdr->msglen); /* len as advertised in the message */
00272    if (stundebug)
00273       ast_verbose("STUN Packet, msg %s (%04x), length: %d\n", stun_msg2str(ntohs(hdr->msgtype)), ntohs(hdr->msgtype), x);
00274    if (x > len) {
00275       ast_debug(1, "Scrambled STUN packet length (got %d, expecting %d)\n", x, (int)len);
00276    } else
00277       len = x;
00278    memset(&st, 0, sizeof(st));
00279    while (len) {
00280       if (len < sizeof(struct stun_attr)) {
00281          ast_debug(1, "Runt Attribute (got %d, expecting %d)\n", (int)len, (int) sizeof(struct stun_attr));
00282          break;
00283       }
00284       attr = (struct stun_attr *)data;
00285       /* compute total attribute length */
00286       x = ntohs(attr->len) + sizeof(struct stun_attr);
00287       if (x > len) {
00288          ast_debug(1, "Inconsistent Attribute (length %d exceeds remaining msg len %d)\n", x, (int)len);
00289          break;
00290       }
00291       if (stun_cb)
00292          stun_cb(attr, arg);
00293       if (stun_process_attr(&st, attr)) {
00294          ast_debug(1, "Failed to handle attribute %s (%04x)\n", stun_attr2str(ntohs(attr->attr)), ntohs(attr->attr));
00295          break;
00296       }
00297       /* Clear attribute id: in case previous entry was a string,
00298        * this will act as the terminator for the string.
00299        */
00300       attr->attr = 0;
00301       data += x;
00302       len -= x;
00303    }
00304    /* Null terminate any string.
00305     * XXX NOTE, we write past the size of the buffer passed by the
00306     * caller, so this is potentially dangerous. The only thing that
00307     * saves us is that usually we read the incoming message in a
00308     * much larger buffer in the struct ast_rtp
00309     */
00310    *data = '\0';
00311 
00312    /* Now prepare to generate a reply, which at the moment is done
00313     * only for properly formed (len == 0) STUN_BINDREQ messages.
00314     */
00315    if (len == 0) {
00316       unsigned char respdata[1024];
00317       struct stun_header *resp = (struct stun_header *)respdata;
00318       int resplen = 0;  /* len excluding header */
00319       int respleft = sizeof(respdata) - sizeof(struct stun_header);
00320 
00321       resp->id = hdr->id;
00322       resp->msgtype = 0;
00323       resp->msglen = 0;
00324       attr = (struct stun_attr *)resp->ies;
00325       switch (ntohs(hdr->msgtype)) {
00326       case STUN_BINDREQ:
00327          if (stundebug)
00328             ast_verbose("STUN Bind Request, username: %s\n",
00329                    st.username ? st.username : "<none>");
00330          if (st.username)
00331             append_attr_string(&attr, STUN_USERNAME, st.username, &resplen, &respleft);
00332          append_attr_address(&attr, STUN_MAPPED_ADDRESS, src, &resplen, &respleft);
00333          resp->msglen = htons(resplen);
00334          resp->msgtype = htons(STUN_BINDRESP);
00335          stun_send(s, src, resp);
00336          ret = AST_STUN_ACCEPT;
00337          break;
00338       default:
00339          if (stundebug)
00340             ast_verbose("Dunno what to do with STUN message %04x (%s)\n", ntohs(hdr->msgtype), stun_msg2str(ntohs(hdr->msgtype)));
00341       }
00342    }
00343    return ret;
00344 }
00345 
00346 /*! \brief Extract the STUN_MAPPED_ADDRESS from the stun response.
00347  * This is used as a callback for stun_handle_response
00348  * when called from ast_stun_request.
00349  */
00350 static int stun_get_mapped(struct stun_attr *attr, void *arg)
00351 {
00352    struct stun_addr *addr = (struct stun_addr *)(attr + 1);
00353    struct sockaddr_in *sa = (struct sockaddr_in *)arg;
00354 
00355    if (ntohs(attr->attr) != STUN_MAPPED_ADDRESS || ntohs(attr->len) != 8)
00356       return 1;   /* not us. */
00357    sa->sin_port = addr->port;
00358    sa->sin_addr.s_addr = addr->addr;
00359    return 0;
00360 }
00361 
00362 /*! \brief Generic STUN request
00363  * Send a generic stun request to the server specified,
00364  * possibly waiting for a reply and filling the 'reply' field with
00365  * the externally visible address. Note that in this case the request
00366  * will be blocking.
00367  * (Note, the interface may change slightly in the future).
00368  *
00369  * \param s the socket used to send the request
00370  * \param dst the address of the STUN server
00371  * \param username if non null, add the username in the request
00372  * \param answer if non null, the function waits for a response and
00373  *    puts here the externally visible address.
00374  * \return 0 on success, other values on error.
00375  */
00376 int ast_stun_request(int s, struct sockaddr_in *dst,
00377    const char *username, struct sockaddr_in *answer)
00378 {
00379    struct stun_header *req;
00380    unsigned char reqdata[1024];
00381    int reqlen, reqleft;
00382    struct stun_attr *attr;
00383    int res = 0;
00384    int retry;
00385 
00386    req = (struct stun_header *)reqdata;
00387    stun_req_id(req);
00388    reqlen = 0;
00389    reqleft = sizeof(reqdata) - sizeof(struct stun_header);
00390    req->msgtype = 0;
00391    req->msglen = 0;
00392    attr = (struct stun_attr *)req->ies;
00393    if (username)
00394       append_attr_string(&attr, STUN_USERNAME, username, &reqlen, &reqleft);
00395    req->msglen = htons(reqlen);
00396    req->msgtype = htons(STUN_BINDREQ);
00397    for (retry = 0; retry < 3; retry++) {  /* XXX make retries configurable */
00398       /* send request, possibly wait for reply */
00399       unsigned char reply_buf[1024];
00400       struct pollfd pfds = { .fd = s, .events = POLLIN };
00401       struct sockaddr_in src;
00402       socklen_t srclen;
00403 
00404       res = stun_send(s, dst, req);
00405       if (res < 0) {
00406          ast_log(LOG_WARNING, "ast_stun_request send #%d failed error %d, retry\n",
00407             retry, res);
00408          continue;
00409       }
00410       if (answer == NULL)
00411          break;
00412       res = ast_poll(&pfds, 1, 3000);
00413       if (res <= 0)  /* timeout or error */
00414          continue;
00415       memset(&src, 0, sizeof(src));
00416       srclen = sizeof(src);
00417       /* XXX pass -1 in the size, because stun_handle_packet might
00418        * write past the end of the buffer.
00419        */
00420       res = recvfrom(s, reply_buf, sizeof(reply_buf) - 1,
00421          0, (struct sockaddr *)&src, &srclen);
00422       if (res < 0) {
00423          ast_log(LOG_WARNING, "ast_stun_request recvfrom #%d failed error %d, retry\n",
00424             retry, res);
00425          continue;
00426       }
00427       memset(answer, 0, sizeof(struct sockaddr_in));
00428       ast_stun_handle_packet(s, &src, reply_buf, res,
00429          stun_get_mapped, answer);
00430       res = 0; /* signal regular exit */
00431       break;
00432    }
00433    return res;
00434 }
00435 
00436 static char *handle_cli_stun_set_debug(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
00437 {
00438    switch (cmd) {
00439    case CLI_INIT:
00440       e->command = "stun set debug {on|off}";
00441       e->usage =
00442          "Usage: stun set debug {on|off}\n"
00443          "       Enable/Disable STUN (Simple Traversal of UDP through NATs)\n"
00444          "       debugging\n";
00445       return NULL;
00446    case CLI_GENERATE:
00447       return NULL;
00448    }
00449 
00450    if (a->argc != e->args)
00451       return CLI_SHOWUSAGE;
00452 
00453    if (!strncasecmp(a->argv[e->args-1], "on", 2))
00454       stundebug = 1;
00455    else if (!strncasecmp(a->argv[e->args-1], "off", 3))
00456       stundebug = 0;
00457    else
00458       return CLI_SHOWUSAGE;
00459 
00460    ast_cli(a->fd, "STUN Debugging %s\n", stundebug ? "Enabled" : "Disabled");
00461    return CLI_SUCCESS;
00462 }
00463 
00464 static struct ast_cli_entry cli_stun[] = {
00465    AST_CLI_DEFINE(handle_cli_stun_set_debug, "Enable/Disable STUN debugging"),
00466 };
00467 
00468 /*! \brief Initialize the STUN system in Asterisk */
00469 void ast_stun_init(void)
00470 {
00471    ast_cli_register_multiple(cli_stun, sizeof(cli_stun) / sizeof(struct ast_cli_entry));
00472 }

Generated on Wed Apr 6 11:29:48 2011 for Asterisk - The Open Source Telephony Project by  doxygen 1.4.7