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 |
STUN support.
STUN is defined in RFC 3489.
Definition in file stun.h.
enum ast_stun_result |
Definition at line 37 of file stun.h.
00037 { 00038 AST_STUN_IGNORE = 0, 00039 AST_STUN_ACCEPT, 00040 };
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. |
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.
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.
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.
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 }
const int STANDARD_STUN_PORT = 3478 [static] |
Definition at line 35 of file stun.h.
Referenced by gtalk_load_config(), and setup_stunaddr().