#include "asterisk.h"
#include "asterisk/_private.h"
#include "asterisk/stun.h"
#include "asterisk/cli.h"
#include "asterisk/utils.h"
#include "asterisk/channel.h"
Go to the source code of this file.
Data Structures | |
struct | stun_addr |
struct | stun_attr |
struct | stun_header |
struct | stun_state |
here we store credentials extracted from a message More... | |
struct | stun_trans_id |
STUN support code. More... | |
Defines | |
#define | STUN_BINDERR 0x0111 |
#define | STUN_BINDREQ 0x0001 |
STUN message types 'BIND' refers to transactions used to determine the externally visible addresses. 'SEC' refers to transactions used to establish a session key for subsequent requests. 'SEC' functionality is not supported here. | |
#define | STUN_BINDRESP 0x0101 |
#define | STUN_CHANGE_REQUEST 0x0003 |
#define | STUN_CHANGED_ADDRESS 0x0005 |
#define | STUN_ERROR_CODE 0x0009 |
#define | STUN_MAPPED_ADDRESS 0x0001 |
Basic attribute types in stun messages. Messages can also contain custom attributes (codes above 0x7fff). | |
#define | STUN_MESSAGE_INTEGRITY 0x0008 |
#define | STUN_PASSWORD 0x0007 |
#define | STUN_REFLECTED_FROM 0x000b |
#define | STUN_RESPONSE_ADDRESS 0x0002 |
#define | STUN_SECERR 0x0112 |
#define | STUN_SECREQ 0x0002 |
#define | STUN_SECRESP 0x0102 |
#define | STUN_SOURCE_ADDRESS 0x0004 |
#define | STUN_UNKNOWN_ATTRIBUTES 0x000a |
#define | STUN_USERNAME 0x0006 |
Functions | |
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 | |
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 | |
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. | |
void | ast_stun_init (void) |
Initialize the STUN system in Asterisk. | |
int | ast_stun_request (int s, struct sockaddr_in *dst, const char *username, struct sockaddr_in *answer) |
Generic STUN request. | |
static char * | handle_cli_stun_set_debug (struct ast_cli_entry *e, int cmd, struct ast_cli_args *a) |
static const char * | stun_attr2str (int msg) |
helper function to print attribute names | |
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_response when called from ast_stun_request. | |
static int | stun_id_cmp (stun_trans_id *left, stun_trans_id *right) |
static const char * | stun_msg2str (int msg) |
helper function to print message names | |
static int | stun_process_attr (struct stun_state *state, struct stun_attr *attr) |
static void | stun_req_id (struct stun_header *req) |
helper function to generate a random request id | |
static int | stun_send (int s, struct sockaddr_in *dst, struct stun_header *resp) |
wrapper to send an STUN message | |
Variables | |
static struct ast_cli_entry | cli_stun [] |
static int | stundebug |
Definition in file stun.c.
#define STUN_BINDERR 0x0111 |
#define STUN_BINDREQ 0x0001 |
STUN message types 'BIND' refers to transactions used to determine the externally visible addresses. 'SEC' refers to transactions used to establish a session key for subsequent requests. 'SEC' functionality is not supported here.
Definition at line 99 of file stun.c.
Referenced by ast_stun_handle_packet(), ast_stun_request(), and stun_msg2str().
#define STUN_BINDRESP 0x0101 |
Definition at line 100 of file stun.c.
Referenced by ast_stun_handle_packet(), ast_stun_request(), and stun_msg2str().
#define STUN_CHANGE_REQUEST 0x0003 |
#define STUN_CHANGED_ADDRESS 0x0005 |
#define STUN_ERROR_CODE 0x0009 |
#define STUN_MAPPED_ADDRESS 0x0001 |
Basic attribute types in stun messages. Messages can also contain custom attributes (codes above 0x7fff).
Definition at line 109 of file stun.c.
Referenced by ast_stun_handle_packet(), stun_attr2str(), and stun_get_mapped().
#define STUN_MESSAGE_INTEGRITY 0x0008 |
#define STUN_PASSWORD 0x0007 |
#define STUN_REFLECTED_FROM 0x000b |
#define STUN_RESPONSE_ADDRESS 0x0002 |
#define STUN_SECERR 0x0112 |
#define STUN_SECREQ 0x0002 |
#define STUN_SECRESP 0x0102 |
#define STUN_SOURCE_ADDRESS 0x0004 |
#define STUN_UNKNOWN_ATTRIBUTES 0x000a |
#define STUN_USERNAME 0x0006 |
Definition at line 114 of file stun.c.
Referenced by ast_stun_handle_packet(), ast_stun_request(), stun_attr2str(), and stun_process_attr().
static void append_attr_address | ( | struct stun_attr ** | attr, | |
int | attrval, | |||
struct sockaddr_in * | sin, | |||
int * | len, | |||
int * | left | |||
) | [static] |
append an address to an STUN message
Definition at line 212 of file stun.c.
References stun_addr::addr.
Referenced by ast_stun_handle_packet().
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 }
static void append_attr_string | ( | struct stun_attr ** | attr, | |
int | attrval, | |||
const char * | s, | |||
int * | len, | |||
int * | left | |||
) | [static] |
append a string to an STUN message
Definition at line 198 of file stun.c.
Referenced by ast_stun_handle_packet(), and ast_stun_request().
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 }
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.
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. |
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 }
void ast_stun_init | ( | void | ) |
Initialize the STUN system in Asterisk.
Provided by stun.c
Definition at line 499 of file stun.c.
References ast_cli_register_multiple(), and cli_stun.
Referenced by main().
00500 { 00501 ast_cli_register_multiple(cli_stun, sizeof(cli_stun) / sizeof(struct ast_cli_entry)); 00502 }
int ast_stun_request | ( | int | s, | |
struct sockaddr_in * | dst, | |||
const char * | username, | |||
struct sockaddr_in * | answer | |||
) |
Generic STUN request.
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. |
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 }
static char* handle_cli_stun_set_debug | ( | struct ast_cli_entry * | e, | |
int | cmd, | |||
struct ast_cli_args * | a | |||
) | [static] |
Definition at line 466 of file stun.c.
References ast_cli_args::argc, ast_cli_entry::args, ast_cli_args::argv, ast_cli(), CLI_GENERATE, CLI_INIT, CLI_SHOWUSAGE, CLI_SUCCESS, ast_cli_entry::command, ast_cli_args::fd, and ast_cli_entry::usage.
00467 { 00468 switch (cmd) { 00469 case CLI_INIT: 00470 e->command = "stun set debug {on|off}"; 00471 e->usage = 00472 "Usage: stun set debug {on|off}\n" 00473 " Enable/Disable STUN (Simple Traversal of UDP through NATs)\n" 00474 " debugging\n"; 00475 return NULL; 00476 case CLI_GENERATE: 00477 return NULL; 00478 } 00479 00480 if (a->argc != e->args) 00481 return CLI_SHOWUSAGE; 00482 00483 if (!strncasecmp(a->argv[e->args-1], "on", 2)) 00484 stundebug = 1; 00485 else if (!strncasecmp(a->argv[e->args-1], "off", 3)) 00486 stundebug = 0; 00487 else 00488 return CLI_SHOWUSAGE; 00489 00490 ast_cli(a->fd, "STUN Debugging %s\n", stundebug ? "Enabled" : "Disabled"); 00491 return CLI_SUCCESS; 00492 }
static const char* stun_attr2str | ( | int | msg | ) | [static] |
helper function to print attribute names
Definition at line 142 of file stun.c.
References STUN_CHANGE_REQUEST, STUN_CHANGED_ADDRESS, STUN_ERROR_CODE, STUN_MAPPED_ADDRESS, STUN_MESSAGE_INTEGRITY, STUN_PASSWORD, STUN_REFLECTED_FROM, STUN_RESPONSE_ADDRESS, STUN_SOURCE_ADDRESS, STUN_UNKNOWN_ATTRIBUTES, and STUN_USERNAME.
Referenced by ast_stun_handle_packet(), and stun_process_attr().
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 }
static int stun_get_mapped | ( | struct stun_attr * | attr, | |
void * | arg | |||
) | [static] |
Extract the STUN_MAPPED_ADDRESS from the stun response. This is used as a callback for stun_handle_response when called from ast_stun_request.
Definition at line 357 of file stun.c.
References stun_addr::addr, stun_attr::attr, stun_attr::len, stun_addr::port, and STUN_MAPPED_ADDRESS.
Referenced by ast_stun_request().
00358 { 00359 struct stun_addr *addr = (struct stun_addr *)(attr + 1); 00360 struct sockaddr_in *sa = (struct sockaddr_in *)arg; 00361 00362 if (ntohs(attr->attr) != STUN_MAPPED_ADDRESS || ntohs(attr->len) != 8) 00363 return 1; /* not us. */ 00364 sa->sin_port = addr->port; 00365 sa->sin_addr.s_addr = addr->addr; 00366 return 0; 00367 }
static int stun_id_cmp | ( | stun_trans_id * | left, | |
stun_trans_id * | right | |||
) | [static] |
static const char* stun_msg2str | ( | int | msg | ) | [static] |
helper function to print message names
Definition at line 122 of file stun.c.
References STUN_BINDERR, STUN_BINDREQ, STUN_BINDRESP, STUN_SECERR, STUN_SECREQ, and STUN_SECRESP.
Referenced by ast_stun_handle_packet().
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 }
static int stun_process_attr | ( | struct stun_state * | state, | |
struct stun_attr * | attr | |||
) | [static] |
Definition at line 177 of file stun.c.
References ast_verbose, stun_attr::attr, stun_attr::len, state, stun_attr2str(), STUN_PASSWORD, STUN_USERNAME, and stun_attr::value.
Referenced by ast_stun_handle_packet().
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 }
static void stun_req_id | ( | struct stun_header * | req | ) | [static] |
helper function to generate a random request id
Definition at line 253 of file stun.c.
References ast_random(), stun_trans_id::id, and stun_header::id.
Referenced by ast_stun_request().
00254 { 00255 int x; 00256 for (x = 0; x < 4; x++) 00257 req->id.id[x] = ast_random(); 00258 }
static int stun_send | ( | int | s, | |
struct sockaddr_in * | dst, | |||
struct stun_header * | resp | |||
) | [static] |
wrapper to send an STUN message
Definition at line 231 of file stun.c.
References stun_header::msglen.
Referenced by ast_stun_handle_packet(), and ast_stun_request().
00232 { 00233 return sendto(s, resp, ntohs(resp->msglen) + sizeof(*resp), 0, 00234 (struct sockaddr *)dst, sizeof(*dst)); 00235 }
struct ast_cli_entry cli_stun[] [static] |
Initial value:
{ { .handler = handle_cli_stun_set_debug , .summary = "Enable/Disable STUN debugging" ,__VA_ARGS__ }, }
Definition at line 494 of file stun.c.
Referenced by ast_stun_init().