Wed Jan 8 2020 09:49:51

Asterisk developer's documentation


stun.c
Go to the documentation of this file.
1 /*
2  * Asterisk -- An open source telephony toolkit.
3  *
4  * Copyright (C) 1999 - 2008, Digium, Inc.
5  *
6  * Mark Spencer <markster@digium.com>
7  *
8  * See http://www.asterisk.org for more information about
9  * the Asterisk project. Please do not directly contact
10  * any of the maintainers of this project for assistance;
11  * the project provides a web site, mailing lists and IRC
12  * channels for your use.
13  *
14  * This program is free software, distributed under the terms of
15  * the GNU General Public License Version 2. See the LICENSE file
16  * at the top of the source tree.
17  */
18 
19 /*!
20  * \file
21  *
22  * \brief STUN Support
23  *
24  * \author Mark Spencer <markster@digium.com>
25  *
26  * \note STUN is defined in RFC 3489.
27  */
28 
29 /*** MODULEINFO
30  <support_level>core</support_level>
31  ***/
32 
33 #include "asterisk.h"
34 
35 ASTERISK_FILE_VERSION(__FILE__, "$Revision: 413586 $")
36 
37 #include "asterisk/_private.h"
38 #include "asterisk/stun.h"
39 #include "asterisk/cli.h"
40 #include "asterisk/utils.h"
41 #include "asterisk/channel.h"
42 
43 static int stundebug; /*!< Are we debugging stun? */
44 
45 /*!
46  * \brief STUN support code
47  *
48  * This code provides some support for doing STUN transactions.
49  * Eventually it should be moved elsewhere as other protocols
50  * than RTP can benefit from it - e.g. SIP.
51  * STUN is described in RFC3489 and it is based on the exchange
52  * of UDP packets between a client and one or more servers to
53  * determine the externally visible address (and port) of the client
54  * once it has gone through the NAT boxes that connect it to the
55  * outside.
56  * The simplest request packet is just the header defined in
57  * struct stun_header, and from the response we may just look at
58  * one attribute, STUN_MAPPED_ADDRESS, that we find in the response.
59  * By doing more transactions with different server addresses we
60  * may determine more about the behaviour of the NAT boxes, of
61  * course - the details are in the RFC.
62  *
63  * All STUN packets start with a simple header made of a type,
64  * length (excluding the header) and a 16-byte random transaction id.
65  * Following the header we may have zero or more attributes, each
66  * structured as a type, length and a value (whose format depends
67  * on the type, but often contains addresses).
68  * Of course all fields are in network format.
69  */
70 
71 typedef struct { unsigned int id[4]; } __attribute__((packed)) stun_trans_id;
72 
73 struct stun_header {
74  unsigned short msgtype;
75  unsigned short msglen;
76  stun_trans_id id;
77  unsigned char ies[0];
78 } __attribute__((packed));
79 
80 struct stun_attr {
81  unsigned short attr;
82  unsigned short len;
83  unsigned char value[0];
84 } __attribute__((packed));
85 
86 /*
87  * The format normally used for addresses carried by STUN messages.
88  */
89 struct stun_addr {
90  unsigned char unused;
91  unsigned char family;
92  unsigned short port;
93  unsigned int addr;
94 } __attribute__((packed));
95 
96 /*! \brief STUN message types
97  * 'BIND' refers to transactions used to determine the externally
98  * visible addresses. 'SEC' refers to transactions used to establish
99  * a session key for subsequent requests.
100  * 'SEC' functionality is not supported here.
101  */
102 
103 #define STUN_BINDREQ 0x0001
104 #define STUN_BINDRESP 0x0101
105 #define STUN_BINDERR 0x0111
106 #define STUN_SECREQ 0x0002
107 #define STUN_SECRESP 0x0102
108 #define STUN_SECERR 0x0112
109 
110 /*! \brief Basic attribute types in stun messages.
111  * Messages can also contain custom attributes (codes above 0x7fff)
112  */
113 #define STUN_MAPPED_ADDRESS 0x0001
114 #define STUN_RESPONSE_ADDRESS 0x0002
115 #define STUN_CHANGE_REQUEST 0x0003
116 #define STUN_SOURCE_ADDRESS 0x0004
117 #define STUN_CHANGED_ADDRESS 0x0005
118 #define STUN_USERNAME 0x0006
119 #define STUN_PASSWORD 0x0007
120 #define STUN_MESSAGE_INTEGRITY 0x0008
121 #define STUN_ERROR_CODE 0x0009
122 #define STUN_UNKNOWN_ATTRIBUTES 0x000a
123 #define STUN_REFLECTED_FROM 0x000b
124 
125 /*! \brief helper function to print message names */
126 static const char *stun_msg2str(int msg)
127 {
128  switch (msg) {
129  case STUN_BINDREQ:
130  return "Binding Request";
131  case STUN_BINDRESP:
132  return "Binding Response";
133  case STUN_BINDERR:
134  return "Binding Error Response";
135  case STUN_SECREQ:
136  return "Shared Secret Request";
137  case STUN_SECRESP:
138  return "Shared Secret Response";
139  case STUN_SECERR:
140  return "Shared Secret Error Response";
141  }
142  return "Non-RFC3489 Message";
143 }
144 
145 /*! \brief helper function to print attribute names */
146 static const char *stun_attr2str(int msg)
147 {
148  switch (msg) {
149  case STUN_MAPPED_ADDRESS:
150  return "Mapped Address";
152  return "Response Address";
153  case STUN_CHANGE_REQUEST:
154  return "Change Request";
155  case STUN_SOURCE_ADDRESS:
156  return "Source Address";
158  return "Changed Address";
159  case STUN_USERNAME:
160  return "Username";
161  case STUN_PASSWORD:
162  return "Password";
164  return "Message Integrity";
165  case STUN_ERROR_CODE:
166  return "Error Code";
168  return "Unknown Attributes";
169  case STUN_REFLECTED_FROM:
170  return "Reflected From";
171  }
172  return "Non-RFC3489 Attribute";
173 }
174 
175 /*! \brief here we store credentials extracted from a message */
176 struct stun_state {
177  const char *username;
178  const char *password;
179 };
180 
181 static int stun_process_attr(struct stun_state *state, struct stun_attr *attr)
182 {
183  if (stundebug)
184  ast_verbose("Found STUN Attribute %s (%04x), length %d\n",
185  stun_attr2str(ntohs(attr->attr)), (unsigned)ntohs(attr->attr), ntohs(attr->len));
186  switch (ntohs(attr->attr)) {
187  case STUN_USERNAME:
188  state->username = (const char *) (attr->value);
189  break;
190  case STUN_PASSWORD:
191  state->password = (const char *) (attr->value);
192  break;
193  default:
194  if (stundebug)
195  ast_verbose("Ignoring STUN attribute %s (%04x), length %d\n",
196  stun_attr2str(ntohs(attr->attr)), (unsigned)ntohs(attr->attr), ntohs(attr->len));
197  }
198  return 0;
199 }
200 
201 /*! \brief append a string to an STUN message */
202 static void append_attr_string(struct stun_attr **attr, int attrval, const char *s, int *len, int *left)
203 {
204  int size = sizeof(**attr) + strlen(s);
205  if (*left > size) {
206  (*attr)->attr = htons(attrval);
207  (*attr)->len = htons(strlen(s));
208  memcpy((*attr)->value, s, strlen(s));
209  (*attr) = (struct stun_attr *)((*attr)->value + strlen(s));
210  *len += size;
211  *left -= size;
212  }
213 }
214 
215 /*! \brief append an address to an STUN message */
216 static void append_attr_address(struct stun_attr **attr, int attrval, struct sockaddr_in *sin, int *len, int *left)
217 {
218  int size = sizeof(**attr) + 8;
219  struct stun_addr *addr;
220  if (*left > size) {
221  (*attr)->attr = htons(attrval);
222  (*attr)->len = htons(8);
223  addr = (struct stun_addr *)((*attr)->value);
224  addr->unused = 0;
225  addr->family = 0x01;
226  addr->port = sin->sin_port;
227  addr->addr = sin->sin_addr.s_addr;
228  (*attr) = (struct stun_attr *)((*attr)->value + 8);
229  *len += size;
230  *left -= size;
231  }
232 }
233 
234 /*! \brief wrapper to send an STUN message */
235 static int stun_send(int s, struct sockaddr_in *dst, struct stun_header *resp)
236 {
237  return sendto(s, resp, ntohs(resp->msglen) + sizeof(*resp), 0,
238  (struct sockaddr *)dst, sizeof(*dst));
239 }
240 
241 /*!
242  * \internal
243  * \brief Compare the STUN tranaction IDs.
244  *
245  * \param left Transaction ID.
246  * \param right Transaction ID.
247  *
248  * \retval 0 if match.
249  * \retval non-zero if not match.
250  */
251 static int stun_id_cmp(stun_trans_id *left, stun_trans_id *right)
252 {
253  return memcmp(left, right, sizeof(*left));
254 }
255 
256 /*! \brief helper function to generate a random request id */
257 static void stun_req_id(struct stun_header *req)
258 {
259  int x;
260  for (x = 0; x < 4; x++)
261  req->id.id[x] = ast_random();
262 }
263 
264 int ast_stun_handle_packet(int s, struct sockaddr_in *src, unsigned char *data, size_t len, stun_cb_f *stun_cb, void *arg)
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 }
356 
357 /*! \brief Extract the STUN_MAPPED_ADDRESS from the stun response.
358  * This is used as a callback for stun_handle_response
359  * when called from ast_stun_request.
360  */
361 static int stun_get_mapped(struct stun_attr *attr, void *arg)
362 {
363  struct stun_addr *addr = (struct stun_addr *)(attr + 1);
364  struct sockaddr_in *sa = (struct sockaddr_in *)arg;
365 
366  if (ntohs(attr->attr) != STUN_MAPPED_ADDRESS || ntohs(attr->len) != 8)
367  return 1; /* not us. */
368  sa->sin_port = addr->port;
369  sa->sin_addr.s_addr = addr->addr;
370  return 0;
371 }
372 
373 int ast_stun_request(int s, struct sockaddr_in *dst,
374  const char *username, struct sockaddr_in *answer)
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 }
469 
470 static char *handle_cli_stun_set_debug(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
471 {
472  switch (cmd) {
473  case CLI_INIT:
474  e->command = "stun set debug {on|off}";
475  e->usage =
476  "Usage: stun set debug {on|off}\n"
477  " Enable/Disable STUN (Simple Traversal of UDP through NATs)\n"
478  " debugging\n";
479  return NULL;
480  case CLI_GENERATE:
481  return NULL;
482  }
483 
484  if (a->argc != e->args)
485  return CLI_SHOWUSAGE;
486 
487  if (!strncasecmp(a->argv[e->args-1], "on", 2))
488  stundebug = 1;
489  else if (!strncasecmp(a->argv[e->args-1], "off", 3))
490  stundebug = 0;
491  else
492  return CLI_SHOWUSAGE;
493 
494  ast_cli(a->fd, "STUN Debugging %s\n", stundebug ? "Enabled" : "Disabled");
495  return CLI_SUCCESS;
496 }
497 
498 static struct ast_cli_entry cli_stun[] = {
499  AST_CLI_DEFINE(handle_cli_stun_set_debug, "Enable/Disable STUN debugging"),
500 };
501 
502 static void stun_shutdown(void)
503 {
504  ast_cli_unregister_multiple(cli_stun, sizeof(cli_stun) / sizeof(struct ast_cli_entry));
505 }
506 
507 /*! \brief Initialize the STUN system in Asterisk */
508 void ast_stun_init(void)
509 {
510  ast_cli_register_multiple(cli_stun, sizeof(cli_stun) / sizeof(struct ast_cli_entry));
512 }
STUN support code.
Definition: stun.c:71
unsigned char unused
Definition: stun.c:90
#define AST_CLI_DEFINE(fn, txt,...)
Definition: cli.h:191
#define STUN_PASSWORD
Definition: stun.c:119
Asterisk main include file. File version handling, generic pbx functions.
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
static void stun_shutdown(void)
Definition: stun.c:502
int ast_cli_unregister_multiple(struct ast_cli_entry *e, int len)
Unregister multiple commands.
Definition: cli.c:2177
#define STUN_MAPPED_ADDRESS
Basic attribute types in stun messages. Messages can also contain custom attributes (codes above 0x7f...
Definition: stun.c:113
descriptor for a cli entry.
Definition: cli.h:165
const int argc
Definition: cli.h:154
#define STUN_CHANGE_REQUEST
Definition: stun.c:115
switch(yytype)
Definition: ast_expr2.c:1510
Definition: stun.c:80
void ast_verbose(const char *fmt,...)
Definition: logger.c:1568
const char * username
Definition: stun.c:177
Definition: cli.h:146
#define STUN_MESSAGE_INTEGRITY
Definition: stun.c:120
void ast_cli(int fd, const char *fmt,...)
Definition: cli.c:105
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
static struct ast_cli_entry cli_stun[]
Definition: stun.c:498
Utility functions.
int args
This gets set in ast_cli_register()
Definition: cli.h:179
#define STUN_USERNAME
Definition: stun.c:118
int( stun_cb_f)(struct stun_attr *attr, void *arg)
callback type to be invoked on stun responses.
Definition: stun.h:69
unsigned int id[4]
Definition: stun.c:71
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
General Asterisk PBX channel definitions.
static void stun_req_id(struct stun_header *req)
helper function to generate a random request id
Definition: stun.c:257
unsigned int addr
Definition: stun.c:93
const int fd
Definition: cli.h:153
int ast_stun_request(int s, struct sockaddr_in *dst, const char *username, struct sockaddr_in *answer)
Generic STUN request.
Definition: stun.c:373
#define ast_poll(a, b, c)
Definition: poll-compat.h:88
Definition: stun.c:89
int ast_register_atexit(void(*func)(void))
Register a function to be executed before Asterisk exits.
Definition: asterisk.c:998
long int ast_random(void)
Definition: utils.c:1640
STUN support.
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
#define STUN_CHANGED_ADDRESS
Definition: stun.c:117
const char *const * argv
Definition: cli.h:155
unsigned char family
Definition: stun.c:91
void ast_stun_init(void)
Initialize the STUN system in Asterisk.
Definition: stun.c:508
const char * password
Definition: stun.c:178
stun_trans_id id
Definition: stun.c:76
#define CLI_SHOWUSAGE
Definition: cli.h:44
#define STUN_REFLECTED_FROM
Definition: stun.c:123
#define STUN_RESPONSE_ADDRESS
Definition: stun.c:114
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
static int stun_id_cmp(stun_trans_id *left, stun_trans_id *right)
Definition: stun.c:251
static char * handle_cli_stun_set_debug(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
Definition: stun.c:470
int errno
here we store credentials extracted from a message
Definition: stun.c:176
char * command
Definition: cli.h:180
static int stundebug
Definition: stun.c:43
#define STUN_SECERR
Definition: stun.c:108
if(yyss+yystacksize-1<=yyssp)
Definition: ast_expr2.c:1874
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
const char * usage
Definition: cli.h:171
unsigned short attr
Definition: stun.c:81
#define CLI_SUCCESS
Definition: cli.h:43
Standard Command Line Interface.
int ast_cli_register_multiple(struct ast_cli_entry *e, int len)
Register multiple commands.
Definition: cli.c:2167
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
#define STUN_ERROR_CODE
Definition: stun.c:121
#define STUN_BINDERR
Definition: stun.c:105
#define STUN_SECRESP
Definition: stun.c:107
unsigned short port
Definition: stun.c:92
unsigned char value[0]
Definition: stun.c:83
static int stun_send(int s, struct sockaddr_in *dst, struct stun_header *resp)
wrapper to send an STUN message
Definition: stun.c:235
#define STUN_SOURCE_ADDRESS
Definition: stun.c:116
#define STUN_UNKNOWN_ATTRIBUTES
Definition: stun.c:122
#define ASTERISK_FILE_VERSION(file, version)
Register/unregister a source code file with the core.
Definition: asterisk.h:180
#define STUN_SECREQ
Definition: stun.c:106
static const char * stun_attr2str(int msg)
helper function to print attribute names
Definition: stun.c:146