Wed Jan 8 2020 09:50:21

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. More...
 

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

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.

37  {
38  AST_STUN_IGNORE = 0,
40 };

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_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, switch(), and stun_state::username.

Referenced by ast_rtp_read(), and ast_stun_request().

265 {
266  struct stun_header *hdr = (struct stun_header *)data;
267  struct stun_attr *attr;
268  struct stun_state st;
269  int ret = AST_STUN_IGNORE;
270  int x;
271 
272  /* On entry, 'len' is the length of the udp payload. After the
273  * initial checks it becomes the size of unprocessed options,
274  * while 'data' is advanced accordingly.
275  */
276  if (len < sizeof(struct stun_header)) {
277  ast_debug(1, "Runt STUN packet (only %d, wanting at least %d)\n", (int) len, (int) sizeof(struct stun_header));
278  return -1;
279  }
280  len -= sizeof(struct stun_header);
281  data += sizeof(struct stun_header);
282  x = ntohs(hdr->msglen); /* len as advertised in the message */
283  if (stundebug)
284  ast_verbose("STUN Packet, msg %s (%04x), length: %d\n", stun_msg2str(ntohs(hdr->msgtype)), (unsigned)ntohs(hdr->msgtype), x);
285  if (x > len) {
286  ast_debug(1, "Scrambled STUN packet length (got %d, expecting %d)\n", x, (int)len);
287  } else
288  len = x;
289  memset(&st, 0, sizeof(st));
290  while (len) {
291  if (len < sizeof(struct stun_attr)) {
292  ast_debug(1, "Runt Attribute (got %d, expecting %d)\n", (int)len, (int) sizeof(struct stun_attr));
293  break;
294  }
295  attr = (struct stun_attr *)data;
296  /* compute total attribute length */
297  x = ntohs(attr->len) + sizeof(struct stun_attr);
298  if (x > len) {
299  ast_debug(1, "Inconsistent Attribute (length %d exceeds remaining msg len %d)\n", x, (int)len);
300  break;
301  }
302  if (stun_cb)
303  stun_cb(attr, arg);
304  if (stun_process_attr(&st, attr)) {
305  ast_debug(1, "Failed to handle attribute %s (%04x)\n", stun_attr2str(ntohs(attr->attr)), (unsigned)ntohs(attr->attr));
306  break;
307  }
308  /* Clear attribute id: in case previous entry was a string,
309  * this will act as the terminator for the string.
310  */
311  attr->attr = 0;
312  data += x;
313  len -= x;
314  }
315  /* Null terminate any string.
316  * XXX NOTE, we write past the size of the buffer passed by the
317  * caller, so this is potentially dangerous. The only thing that
318  * saves us is that usually we read the incoming message in a
319  * much larger buffer in the struct ast_rtp
320  */
321  *data = '\0';
322 
323  /* Now prepare to generate a reply, which at the moment is done
324  * only for properly formed (len == 0) STUN_BINDREQ messages.
325  */
326  if (len == 0) {
327  unsigned char respdata[1024];
328  struct stun_header *resp = (struct stun_header *)respdata;
329  int resplen = 0; /* len excluding header */
330  int respleft = sizeof(respdata) - sizeof(struct stun_header);
331 
332  resp->id = hdr->id;
333  resp->msgtype = 0;
334  resp->msglen = 0;
335  attr = (struct stun_attr *)resp->ies;
336  switch (ntohs(hdr->msgtype)) {
337  case STUN_BINDREQ:
338  if (stundebug)
339  ast_verbose("STUN Bind Request, username: %s\n",
340  st.username ? st.username : "<none>");
341  if (st.username)
342  append_attr_string(&attr, STUN_USERNAME, st.username, &resplen, &respleft);
343  append_attr_address(&attr, STUN_MAPPED_ADDRESS, src, &resplen, &respleft);
344  resp->msglen = htons(resplen);
345  resp->msgtype = htons(STUN_BINDRESP);
346  stun_send(s, src, resp);
347  ret = AST_STUN_ACCEPT;
348  break;
349  default:
350  if (stundebug)
351  ast_verbose("Dunno what to do with STUN message %04x (%s)\n", (unsigned)ntohs(hdr->msgtype), stun_msg2str(ntohs(hdr->msgtype)));
352  }
353  }
354  return ret;
355 }
unsigned short msglen
Definition: stun.c:75
static const char * stun_msg2str(int msg)
helper function to print message names
Definition: stun.c:126
#define STUN_BINDRESP
Definition: stun.c:104
unsigned short msgtype
Definition: stun.c:74
#define STUN_MAPPED_ADDRESS
Basic attribute types in stun messages. Messages can also contain custom attributes (codes above 0x7f...
Definition: stun.c:113
switch(yytype)
Definition: ast_expr2.c:1510
Definition: stun.c:80
void ast_verbose(const char *fmt,...)
Definition: logger.c:1568
#define STUN_USERNAME
Definition: stun.c:118
#define ast_debug(level,...)
Log a DEBUG message.
Definition: logger.h:236
static void append_attr_address(struct stun_attr **attr, int attrval, struct sockaddr_in *sin, int *len, int *left)
append an address to an STUN message
Definition: stun.c:216
stun_trans_id id
Definition: stun.c:76
static int len(struct ast_channel *chan, const char *cmd, char *data, char *buf, size_t buflen)
#define STUN_BINDREQ
STUN message types &#39;BIND&#39; refers to transactions used to determine the externally visible addresses...
Definition: stun.c:103
here we store credentials extracted from a message
Definition: stun.c:176
static int stundebug
Definition: stun.c:43
static int stun_process_attr(struct stun_state *state, struct stun_attr *attr)
Definition: stun.c:181
unsigned char ies[0]
Definition: stun.c:77
unsigned short attr
Definition: stun.c:81
static void append_attr_string(struct stun_attr **attr, int attrval, const char *s, int *len, int *left)
append a string to an STUN message
Definition: stun.c:202
unsigned short len
Definition: stun.c:82
static int stun_send(int s, struct sockaddr_in *dst, struct stun_header *resp)
wrapper to send an STUN message
Definition: stun.c:235
static const char * stun_attr2str(int msg)
helper function to print attribute names
Definition: stun.c:146
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 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, if(), 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().

375 {
376  struct stun_header *req;
377  struct stun_header *rsp;
378  unsigned char req_buf[1024];
379  unsigned char rsp_buf[1024];
380  int reqlen, reqleft;
381  struct stun_attr *attr;
382  int res = -1;
383  int retry;
384 
385  if (answer) {
386  /* Always clear answer in case the request fails. */
387  memset(answer, 0, sizeof(struct sockaddr_in));
388  }
389 
390  /* Create STUN bind request */
391  req = (struct stun_header *) req_buf;
392  stun_req_id(req);
393  reqlen = 0;
394  reqleft = sizeof(req_buf) - sizeof(struct stun_header);
395  req->msgtype = 0;
396  req->msglen = 0;
397  attr = (struct stun_attr *) req->ies;
398  if (username) {
399  append_attr_string(&attr, STUN_USERNAME, username, &reqlen, &reqleft);
400  }
401  req->msglen = htons(reqlen);
402  req->msgtype = htons(STUN_BINDREQ);
403 
404  for (retry = 0; retry++ < 3;) { /* XXX make retries configurable */
405  /* send request, possibly wait for reply */
406  struct sockaddr_in src;
407  socklen_t srclen;
408 
409  /* Send STUN message. */
410  res = stun_send(s, dst, req);
411  if (res < 0) {
412  ast_debug(1, "stun_send try %d failed: %s\n", retry, strerror(errno));
413  break;
414  }
415  if (!answer) {
416  /* Successful send since we don't care about any response. */
417  res = 0;
418  break;
419  }
420 
421 try_again:
422  /* Wait for response. */
423  {
424  struct pollfd pfds = { .fd = s, .events = POLLIN };
425 
426  res = ast_poll(&pfds, 1, 3000);
427  if (res < 0) {
428  /* Error */
429  continue;
430  }
431  if (!res) {
432  /* No response, timeout */
433  res = 1;
434  continue;
435  }
436  }
437 
438  /* Read STUN response. */
439  memset(&src, 0, sizeof(src));
440  srclen = sizeof(src);
441  /* XXX pass sizeof(rsp_buf) - 1 in the size, because stun_handle_packet might
442  * write past the end of the buffer.
443  */
444  res = recvfrom(s, rsp_buf, sizeof(rsp_buf) - 1,
445  0, (struct sockaddr *) &src, &srclen);
446  if (res < 0) {
447  ast_debug(1, "recvfrom try %d failed: %s\n", retry, strerror(errno));
448  break;
449  }
450 
451  /* Process the STUN response. */
452  rsp = (struct stun_header *) rsp_buf;
453  if (ast_stun_handle_packet(s, &src, rsp_buf, res, stun_get_mapped, answer)
454  || (rsp->msgtype != htons(STUN_BINDRESP)
455  && rsp->msgtype != htons(STUN_BINDERR))
456  || stun_id_cmp(&req->id, &rsp->id)) {
457  /* Bad STUN packet, not right type, or transaction ID did not match. */
458  memset(answer, 0, sizeof(struct sockaddr_in));
459 
460  /* Was not a resonse to our request. */
461  goto try_again;
462  }
463  /* Success. answer contains the external address if available. */
464  res = 0;
465  break;
466  }
467  return res;
468 }
unsigned short msglen
Definition: stun.c:75
#define STUN_BINDRESP
Definition: stun.c:104
unsigned short msgtype
Definition: stun.c:74
Definition: stun.c:80
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.
Definition: stun.c:264
#define STUN_USERNAME
Definition: stun.c:118
static int stun_get_mapped(struct stun_attr *attr, void *arg)
Extract the STUN_MAPPED_ADDRESS from the stun response. This is used as a callback for stun_handle_re...
Definition: stun.c:361
#define ast_debug(level,...)
Log a DEBUG message.
Definition: logger.h:236
static void stun_req_id(struct stun_header *req)
helper function to generate a random request id
Definition: stun.c:257
#define ast_poll(a, b, c)
Definition: poll-compat.h:88
stun_trans_id id
Definition: stun.c:76
#define STUN_BINDREQ
STUN message types &#39;BIND&#39; refers to transactions used to determine the externally visible addresses...
Definition: stun.c:103
static int stun_id_cmp(stun_trans_id *left, stun_trans_id *right)
Definition: stun.c:251
int errno
if(yyss+yystacksize-1<=yyssp)
Definition: ast_expr2.c:1874
unsigned char ies[0]
Definition: stun.c:77
unsigned short attr
Definition: stun.c:81
static void append_attr_string(struct stun_attr **attr, int attrval, const char *s, int *len, int *left)
append a string to an STUN message
Definition: stun.c:202
#define STUN_BINDERR
Definition: stun.c:105
static int stun_send(int s, struct sockaddr_in *dst, struct stun_header *resp)
wrapper to send an STUN message
Definition: stun.c:235

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().