#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 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. |
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 }
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 }
const int STANDARD_STUN_PORT = 3478 [static] |