#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 103 of file stun.c.
Referenced by ast_stun_handle_packet(), ast_stun_request(), and stun_msg2str().
#define STUN_BINDRESP 0x0101 |
Definition at line 104 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 113 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 118 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 216 of file stun.c.
References stun_addr::addr.
Referenced by ast_stun_handle_packet().
00217 { 00218 int size = sizeof(**attr) + 8; 00219 struct stun_addr *addr; 00220 if (*left > size) { 00221 (*attr)->attr = htons(attrval); 00222 (*attr)->len = htons(8); 00223 addr = (struct stun_addr *)((*attr)->value); 00224 addr->unused = 0; 00225 addr->family = 0x01; 00226 addr->port = sin->sin_port; 00227 addr->addr = sin->sin_addr.s_addr; 00228 (*attr) = (struct stun_attr *)((*attr)->value + 8); 00229 *len += size; 00230 *left -= size; 00231 } 00232 }
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 202 of file stun.c.
Referenced by ast_stun_handle_packet(), and ast_stun_request().
00203 { 00204 int size = sizeof(**attr) + strlen(s); 00205 if (*left > size) { 00206 (*attr)->attr = htons(attrval); 00207 (*attr)->len = htons(strlen(s)); 00208 memcpy((*attr)->value, s, strlen(s)); 00209 (*attr) = (struct stun_attr *)((*attr)->value + strlen(s)); 00210 *len += size; 00211 *left -= size; 00212 } 00213 }
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 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_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().
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)), 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)), 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", ntohs(hdr->msgtype), stun_msg2str(ntohs(hdr->msgtype))); 00352 } 00353 } 00354 return ret; 00355 }
void ast_stun_init | ( | void | ) |
Initialize the STUN system in Asterisk.
Provided by stun.c
Definition at line 503 of file stun.c.
References ast_cli_register_multiple(), and cli_stun.
Referenced by main().
00504 { 00505 ast_cli_register_multiple(cli_stun, sizeof(cli_stun) / sizeof(struct ast_cli_entry)); 00506 }
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 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::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 }
static char* handle_cli_stun_set_debug | ( | struct ast_cli_entry * | e, | |
int | cmd, | |||
struct ast_cli_args * | a | |||
) | [static] |
Definition at line 470 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.
00471 { 00472 switch (cmd) { 00473 case CLI_INIT: 00474 e->command = "stun set debug {on|off}"; 00475 e->usage = 00476 "Usage: stun set debug {on|off}\n" 00477 " Enable/Disable STUN (Simple Traversal of UDP through NATs)\n" 00478 " debugging\n"; 00479 return NULL; 00480 case CLI_GENERATE: 00481 return NULL; 00482 } 00483 00484 if (a->argc != e->args) 00485 return CLI_SHOWUSAGE; 00486 00487 if (!strncasecmp(a->argv[e->args-1], "on", 2)) 00488 stundebug = 1; 00489 else if (!strncasecmp(a->argv[e->args-1], "off", 3)) 00490 stundebug = 0; 00491 else 00492 return CLI_SHOWUSAGE; 00493 00494 ast_cli(a->fd, "STUN Debugging %s\n", stundebug ? "Enabled" : "Disabled"); 00495 return CLI_SUCCESS; 00496 }
static const char* stun_attr2str | ( | int | msg | ) | [static] |
helper function to print attribute names
Definition at line 146 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().
00147 { 00148 switch (msg) { 00149 case STUN_MAPPED_ADDRESS: 00150 return "Mapped Address"; 00151 case STUN_RESPONSE_ADDRESS: 00152 return "Response Address"; 00153 case STUN_CHANGE_REQUEST: 00154 return "Change Request"; 00155 case STUN_SOURCE_ADDRESS: 00156 return "Source Address"; 00157 case STUN_CHANGED_ADDRESS: 00158 return "Changed Address"; 00159 case STUN_USERNAME: 00160 return "Username"; 00161 case STUN_PASSWORD: 00162 return "Password"; 00163 case STUN_MESSAGE_INTEGRITY: 00164 return "Message Integrity"; 00165 case STUN_ERROR_CODE: 00166 return "Error Code"; 00167 case STUN_UNKNOWN_ATTRIBUTES: 00168 return "Unknown Attributes"; 00169 case STUN_REFLECTED_FROM: 00170 return "Reflected From"; 00171 } 00172 return "Non-RFC3489 Attribute"; 00173 }
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 361 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().
00362 { 00363 struct stun_addr *addr = (struct stun_addr *)(attr + 1); 00364 struct sockaddr_in *sa = (struct sockaddr_in *)arg; 00365 00366 if (ntohs(attr->attr) != STUN_MAPPED_ADDRESS || ntohs(attr->len) != 8) 00367 return 1; /* not us. */ 00368 sa->sin_port = addr->port; 00369 sa->sin_addr.s_addr = addr->addr; 00370 return 0; 00371 }
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 126 of file stun.c.
References STUN_BINDERR, STUN_BINDREQ, STUN_BINDRESP, STUN_SECERR, STUN_SECREQ, and STUN_SECRESP.
Referenced by ast_stun_handle_packet().
00127 { 00128 switch (msg) { 00129 case STUN_BINDREQ: 00130 return "Binding Request"; 00131 case STUN_BINDRESP: 00132 return "Binding Response"; 00133 case STUN_BINDERR: 00134 return "Binding Error Response"; 00135 case STUN_SECREQ: 00136 return "Shared Secret Request"; 00137 case STUN_SECRESP: 00138 return "Shared Secret Response"; 00139 case STUN_SECERR: 00140 return "Shared Secret Error Response"; 00141 } 00142 return "Non-RFC3489 Message"; 00143 }
static int stun_process_attr | ( | struct stun_state * | state, | |
struct stun_attr * | attr | |||
) | [static] |
Definition at line 181 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().
00182 { 00183 if (stundebug) 00184 ast_verbose("Found STUN Attribute %s (%04x), length %d\n", 00185 stun_attr2str(ntohs(attr->attr)), ntohs(attr->attr), ntohs(attr->len)); 00186 switch (ntohs(attr->attr)) { 00187 case STUN_USERNAME: 00188 state->username = (const char *) (attr->value); 00189 break; 00190 case STUN_PASSWORD: 00191 state->password = (const char *) (attr->value); 00192 break; 00193 default: 00194 if (stundebug) 00195 ast_verbose("Ignoring STUN attribute %s (%04x), length %d\n", 00196 stun_attr2str(ntohs(attr->attr)), ntohs(attr->attr), ntohs(attr->len)); 00197 } 00198 return 0; 00199 }
static void stun_req_id | ( | struct stun_header * | req | ) | [static] |
helper function to generate a random request id
Definition at line 257 of file stun.c.
References ast_random(), stun_trans_id::id, and stun_header::id.
Referenced by ast_stun_request().
00258 { 00259 int x; 00260 for (x = 0; x < 4; x++) 00261 req->id.id[x] = ast_random(); 00262 }
static int stun_send | ( | int | s, | |
struct sockaddr_in * | dst, | |||
struct stun_header * | resp | |||
) | [static] |
wrapper to send an STUN message
Definition at line 235 of file stun.c.
References stun_header::msglen.
Referenced by ast_stun_handle_packet(), and ast_stun_request().
00236 { 00237 return sendto(s, resp, ntohs(resp->msglen) + sizeof(*resp), 0, 00238 (struct sockaddr *)dst, sizeof(*dst)); 00239 }
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 498 of file stun.c.
Referenced by ast_stun_init().