Mon Aug 31 12:30:46 2015

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

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 264 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_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_rtp_read(), and ast_stun_request().

00265 {
00266    struct stun_header *hdr = (struct stun_header *)data;
00267    struct stun_attr *attr;
00268    struct stun_state st;
00269    int ret = AST_STUN_IGNORE;
00270    int x;
00271 
00272    /* On entry, 'len' is the length of the udp payload. After the
00273     * initial checks it becomes the size of unprocessed options,
00274     * while 'data' is advanced accordingly.
00275     */
00276    if (len < sizeof(struct stun_header)) {
00277       ast_debug(1, "Runt STUN packet (only %d, wanting at least %d)\n", (int) len, (int) sizeof(struct stun_header));
00278       return -1;
00279    }
00280    len -= sizeof(struct stun_header);
00281    data += sizeof(struct stun_header);
00282    x = ntohs(hdr->msglen); /* len as advertised in the message */
00283    if (stundebug)
00284       ast_verbose("STUN Packet, msg %s (%04x), length: %d\n", stun_msg2str(ntohs(hdr->msgtype)), (unsigned)ntohs(hdr->msgtype), x);
00285    if (x > len) {
00286       ast_debug(1, "Scrambled STUN packet length (got %d, expecting %d)\n", x, (int)len);
00287    } else
00288       len = x;
00289    memset(&st, 0, sizeof(st));
00290    while (len) {
00291       if (len < sizeof(struct stun_attr)) {
00292          ast_debug(1, "Runt Attribute (got %d, expecting %d)\n", (int)len, (int) sizeof(struct stun_attr));
00293          break;
00294       }
00295       attr = (struct stun_attr *)data;
00296       /* compute total attribute length */
00297       x = ntohs(attr->len) + sizeof(struct stun_attr);
00298       if (x > len) {
00299          ast_debug(1, "Inconsistent Attribute (length %d exceeds remaining msg len %d)\n", x, (int)len);
00300          break;
00301       }
00302       if (stun_cb)
00303          stun_cb(attr, arg);
00304       if (stun_process_attr(&st, attr)) {
00305          ast_debug(1, "Failed to handle attribute %s (%04x)\n", stun_attr2str(ntohs(attr->attr)), (unsigned)ntohs(attr->attr));
00306          break;
00307       }
00308       /* Clear attribute id: in case previous entry was a string,
00309        * this will act as the terminator for the string.
00310        */
00311       attr->attr = 0;
00312       data += x;
00313       len -= x;
00314    }
00315    /* Null terminate any string.
00316     * XXX NOTE, we write past the size of the buffer passed by the
00317     * caller, so this is potentially dangerous. The only thing that
00318     * saves us is that usually we read the incoming message in a
00319     * much larger buffer in the struct ast_rtp
00320     */
00321    *data = '\0';
00322 
00323    /* Now prepare to generate a reply, which at the moment is done
00324     * only for properly formed (len == 0) STUN_BINDREQ messages.
00325     */
00326    if (len == 0) {
00327       unsigned char respdata[1024];
00328       struct stun_header *resp = (struct stun_header *)respdata;
00329       int resplen = 0;  /* len excluding header */
00330       int respleft = sizeof(respdata) - sizeof(struct stun_header);
00331 
00332       resp->id = hdr->id;
00333       resp->msgtype = 0;
00334       resp->msglen = 0;
00335       attr = (struct stun_attr *)resp->ies;
00336       switch (ntohs(hdr->msgtype)) {
00337       case STUN_BINDREQ:
00338          if (stundebug)
00339             ast_verbose("STUN Bind Request, username: %s\n",
00340                    st.username ? st.username : "<none>");
00341          if (st.username)
00342             append_attr_string(&attr, STUN_USERNAME, st.username, &resplen, &respleft);
00343          append_attr_address(&attr, STUN_MAPPED_ADDRESS, src, &resplen, &respleft);
00344          resp->msglen = htons(resplen);
00345          resp->msgtype = htons(STUN_BINDRESP);
00346          stun_send(s, src, resp);
00347          ret = AST_STUN_ACCEPT;
00348          break;
00349       default:
00350          if (stundebug)
00351             ast_verbose("Dunno what to do with STUN message %04x (%s)\n", (unsigned)ntohs(hdr->msgtype), stun_msg2str(ntohs(hdr->msgtype)));
00352       }
00353    }
00354    return ret;
00355 }

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 373 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(), gtalk_update_externip(), and stun_monitor_request().

00375 {
00376    struct stun_header *req;
00377    struct stun_header *rsp;
00378    unsigned char req_buf[1024];
00379    unsigned char rsp_buf[1024];
00380    int reqlen, reqleft;
00381    struct stun_attr *attr;
00382    int res = -1;
00383    int retry;
00384 
00385    if (answer) {
00386       /* Always clear answer in case the request fails. */
00387       memset(answer, 0, sizeof(struct sockaddr_in));
00388    }
00389 
00390    /* Create STUN bind request */
00391    req = (struct stun_header *) req_buf;
00392    stun_req_id(req);
00393    reqlen = 0;
00394    reqleft = sizeof(req_buf) - sizeof(struct stun_header);
00395    req->msgtype = 0;
00396    req->msglen = 0;
00397    attr = (struct stun_attr *) req->ies;
00398    if (username) {
00399       append_attr_string(&attr, STUN_USERNAME, username, &reqlen, &reqleft);
00400    }
00401    req->msglen = htons(reqlen);
00402    req->msgtype = htons(STUN_BINDREQ);
00403 
00404    for (retry = 0; retry++ < 3;) {  /* XXX make retries configurable */
00405       /* send request, possibly wait for reply */
00406       struct sockaddr_in src;
00407       socklen_t srclen;
00408 
00409       /* Send STUN message. */
00410       res = stun_send(s, dst, req);
00411       if (res < 0) {
00412          ast_debug(1, "stun_send try %d failed: %s\n", retry, strerror(errno));
00413          break;
00414       }
00415       if (!answer) {
00416          /* Successful send since we don't care about any response. */
00417          res = 0;
00418          break;
00419       }
00420 
00421 try_again:
00422       /* Wait for response. */
00423       {
00424          struct pollfd pfds = { .fd = s, .events = POLLIN };
00425 
00426          res = ast_poll(&pfds, 1, 3000);
00427          if (res < 0) {
00428             /* Error */
00429             continue;
00430          }
00431          if (!res) {
00432             /* No response, timeout */
00433             res = 1;
00434             continue;
00435          }
00436       }
00437 
00438       /* Read STUN response. */
00439       memset(&src, 0, sizeof(src));
00440       srclen = sizeof(src);
00441       /* XXX pass sizeof(rsp_buf) - 1 in the size, because stun_handle_packet might
00442        * write past the end of the buffer.
00443        */
00444       res = recvfrom(s, rsp_buf, sizeof(rsp_buf) - 1,
00445          0, (struct sockaddr *) &src, &srclen);
00446       if (res < 0) {
00447          ast_debug(1, "recvfrom try %d failed: %s\n", retry, strerror(errno));
00448          break;
00449       }
00450 
00451       /* Process the STUN response. */
00452       rsp = (struct stun_header *) rsp_buf;
00453       if (ast_stun_handle_packet(s, &src, rsp_buf, res, stun_get_mapped, answer)
00454          || (rsp->msgtype != htons(STUN_BINDRESP)
00455             && rsp->msgtype != htons(STUN_BINDERR))
00456          || stun_id_cmp(&req->id, &rsp->id)) {
00457          /* Bad STUN packet, not right type, or transaction ID did not match. */
00458          memset(answer, 0, sizeof(struct sockaddr_in));
00459 
00460          /* Was not a resonse to our request. */
00461          goto try_again;
00462       }
00463       /* Success.  answer contains the external address if available. */
00464       res = 0;
00465       break;
00466    }
00467    return res;
00468 }


Variable Documentation

const int STANDARD_STUN_PORT = 3478 [static]

Definition at line 35 of file stun.h.

Referenced by gtalk_load_config(), and setup_stunaddr().


Generated on 31 Aug 2015 for Asterisk - The Open Source Telephony Project by  doxygen 1.6.1