Mon Mar 19 11:30:55 2012

Asterisk developer's documentation


stun.h File Reference

STUN support. More...

#include "asterisk/network.h"

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

enum ast_stun_result

Enumerator:
AST_STUN_IGNORE 
AST_STUN_ACCEPT 

Definition at line 37 of file stun.h.

00037                      {
00038    AST_STUN_IGNORE = 0,
00039    AST_STUN_ACCEPT,
00040 };


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:
s Socket to send any response to.
src Address where packet came from.
data STUN packet buffer to process.
len Length of packet
stun_cb If not NULL, callback for each STUN attribute.
arg Arg 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_ACCEPT if responed to a STUN request
AST_STUN_IGNORE 
-1 on error

Definition at line 260 of file stun.c.

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

Referenced by ast_rtp_read(), and ast_stun_request().

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 }

int ast_stun_request ( int  s,
struct sockaddr_in *  dst,
const char *  username,
struct sockaddr_in *  answer 
)

Generic STUN request.

Parameters:
s The socket used to send the request.
dst If non null, the address of the STUN server. Only needed if the socket is not bound or connected.
username If non null, add the username in the request.
answer If 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:
0 on success.
<0 on error.
>0 on timeout.

Definition at line 369 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::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(), gtalk_update_externip(), and stun_monitor_request().

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 }


Variable Documentation

const int STANDARD_STUN_PORT = 3478 [static]

Definition at line 35 of file stun.h.

Referenced by setup_stunaddr().


Generated on Mon Mar 19 11:30:56 2012 for Asterisk - The Open Source Telephony Project by  doxygen 1.4.7