STUN Support. More...
#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 |
| #define STUN_BINDERR 0x0111 |
Definition at line 101 of file stun.c.
Referenced by ast_stun_request(), and stun_msg2str().
| #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 |
Definition at line 111 of file stun.c.
Referenced by stun_attr2str().
| #define STUN_CHANGED_ADDRESS 0x0005 |
Definition at line 113 of file stun.c.
Referenced by stun_attr2str().
| #define STUN_ERROR_CODE 0x0009 |
Definition at line 117 of file stun.c.
Referenced by stun_attr2str().
| #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 |
Definition at line 116 of file stun.c.
Referenced by stun_attr2str().
| #define STUN_PASSWORD 0x0007 |
Definition at line 115 of file stun.c.
Referenced by stun_attr2str(), and stun_process_attr().
| #define STUN_REFLECTED_FROM 0x000b |
Definition at line 119 of file stun.c.
Referenced by stun_attr2str().
| #define STUN_RESPONSE_ADDRESS 0x0002 |
Definition at line 110 of file stun.c.
Referenced by stun_attr2str().
| #define STUN_SECERR 0x0112 |
Definition at line 104 of file stun.c.
Referenced by stun_msg2str().
| #define STUN_SECREQ 0x0002 |
Definition at line 102 of file stun.c.
Referenced by stun_msg2str().
| #define STUN_SECRESP 0x0102 |
Definition at line 103 of file stun.c.
Referenced by stun_msg2str().
| #define STUN_SOURCE_ADDRESS 0x0004 |
Definition at line 112 of file stun.c.
Referenced by stun_attr2str().
| #define STUN_UNKNOWN_ATTRIBUTES 0x000a |
Definition at line 118 of file stun.c.
Referenced by stun_attr2str().
| #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, stun_addr::family, stun_addr::port, and stun_addr::unused.
Referenced by ast_stun_handle_packet().
{
int size = sizeof(**attr) + 8;
struct stun_addr *addr;
if (*left > size) {
(*attr)->attr = htons(attrval);
(*attr)->len = htons(8);
addr = (struct stun_addr *)((*attr)->value);
addr->unused = 0;
addr->family = 0x01;
addr->port = sin->sin_port;
addr->addr = sin->sin_addr.s_addr;
(*attr) = (struct stun_attr *)((*attr)->value + 8);
*len += size;
*left -= size;
}
}
| 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().
| 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 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_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().
{
struct stun_header *hdr = (struct stun_header *)data;
struct stun_attr *attr;
struct stun_state st;
int ret = AST_STUN_IGNORE;
int x;
/* On entry, 'len' is the length of the udp payload. After the
* initial checks it becomes the size of unprocessed options,
* while 'data' is advanced accordingly.
*/
if (len < sizeof(struct stun_header)) {
ast_debug(1, "Runt STUN packet (only %d, wanting at least %d)\n", (int) len, (int) sizeof(struct stun_header));
return -1;
}
len -= sizeof(struct stun_header);
data += sizeof(struct stun_header);
x = ntohs(hdr->msglen); /* len as advertised in the message */
if (stundebug)
ast_verbose("STUN Packet, msg %s (%04x), length: %d\n", stun_msg2str(ntohs(hdr->msgtype)), ntohs(hdr->msgtype), x);
if (x > len) {
ast_debug(1, "Scrambled STUN packet length (got %d, expecting %d)\n", x, (int)len);
} else
len = x;
memset(&st, 0, sizeof(st));
while (len) {
if (len < sizeof(struct stun_attr)) {
ast_debug(1, "Runt Attribute (got %d, expecting %d)\n", (int)len, (int) sizeof(struct stun_attr));
break;
}
attr = (struct stun_attr *)data;
/* compute total attribute length */
x = ntohs(attr->len) + sizeof(struct stun_attr);
if (x > len) {
ast_debug(1, "Inconsistent Attribute (length %d exceeds remaining msg len %d)\n", x, (int)len);
break;
}
if (stun_cb)
stun_cb(attr, arg);
if (stun_process_attr(&st, attr)) {
ast_debug(1, "Failed to handle attribute %s (%04x)\n", stun_attr2str(ntohs(attr->attr)), ntohs(attr->attr));
break;
}
/* Clear attribute id: in case previous entry was a string,
* this will act as the terminator for the string.
*/
attr->attr = 0;
data += x;
len -= x;
}
/* Null terminate any string.
* XXX NOTE, we write past the size of the buffer passed by the
* caller, so this is potentially dangerous. The only thing that
* saves us is that usually we read the incoming message in a
* much larger buffer in the struct ast_rtp
*/
*data = '\0';
/* Now prepare to generate a reply, which at the moment is done
* only for properly formed (len == 0) STUN_BINDREQ messages.
*/
if (len == 0) {
unsigned char respdata[1024];
struct stun_header *resp = (struct stun_header *)respdata;
int resplen = 0; /* len excluding header */
int respleft = sizeof(respdata) - sizeof(struct stun_header);
resp->id = hdr->id;
resp->msgtype = 0;
resp->msglen = 0;
attr = (struct stun_attr *)resp->ies;
switch (ntohs(hdr->msgtype)) {
case STUN_BINDREQ:
if (stundebug)
ast_verbose("STUN Bind Request, username: %s\n",
st.username ? st.username : "<none>");
if (st.username)
append_attr_string(&attr, STUN_USERNAME, st.username, &resplen, &respleft);
append_attr_address(&attr, STUN_MAPPED_ADDRESS, src, &resplen, &respleft);
resp->msglen = htons(resplen);
resp->msgtype = htons(STUN_BINDRESP);
stun_send(s, src, resp);
ret = AST_STUN_ACCEPT;
break;
default:
if (stundebug)
ast_verbose("Dunno what to do with STUN message %04x (%s)\n", ntohs(hdr->msgtype), stun_msg2str(ntohs(hdr->msgtype)));
}
}
return ret;
}
| 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().
Referenced by main().
{
ast_cli_register_multiple(cli_stun, sizeof(cli_stun) / sizeof(struct ast_cli_entry));
}
| 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 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::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().
{
struct stun_header *req;
struct stun_header *rsp;
unsigned char req_buf[1024];
unsigned char rsp_buf[1024];
int reqlen, reqleft;
struct stun_attr *attr;
int res = -1;
int retry;
if (answer) {
/* Always clear answer in case the request fails. */
memset(answer, 0, sizeof(struct sockaddr_in));
}
/* Create STUN bind request */
req = (struct stun_header *) req_buf;
stun_req_id(req);
reqlen = 0;
reqleft = sizeof(req_buf) - sizeof(struct stun_header);
req->msgtype = 0;
req->msglen = 0;
attr = (struct stun_attr *) req->ies;
if (username) {
append_attr_string(&attr, STUN_USERNAME, username, &reqlen, &reqleft);
}
req->msglen = htons(reqlen);
req->msgtype = htons(STUN_BINDREQ);
for (retry = 0; retry++ < 3;) { /* XXX make retries configurable */
/* send request, possibly wait for reply */
struct sockaddr_in src;
socklen_t srclen;
/* Send STUN message. */
res = stun_send(s, dst, req);
if (res < 0) {
ast_debug(1, "stun_send try %d failed: %s\n", retry, strerror(errno));
break;
}
if (!answer) {
/* Successful send since we don't care about any response. */
res = 0;
break;
}
try_again:
/* Wait for response. */
{
struct pollfd pfds = { .fd = s, .events = POLLIN };
res = ast_poll(&pfds, 1, 3000);
if (res < 0) {
/* Error */
continue;
}
if (!res) {
/* No response, timeout */
res = 1;
continue;
}
}
/* Read STUN response. */
memset(&src, 0, sizeof(src));
srclen = sizeof(src);
/* XXX pass sizeof(rsp_buf) - 1 in the size, because stun_handle_packet might
* write past the end of the buffer.
*/
res = recvfrom(s, rsp_buf, sizeof(rsp_buf) - 1,
0, (struct sockaddr *) &src, &srclen);
if (res < 0) {
ast_debug(1, "recvfrom try %d failed: %s\n", retry, strerror(errno));
break;
}
/* Process the STUN response. */
rsp = (struct stun_header *) rsp_buf;
if (ast_stun_handle_packet(s, &src, rsp_buf, res, stun_get_mapped, answer)
|| (rsp->msgtype != htons(STUN_BINDRESP)
&& rsp->msgtype != htons(STUN_BINDERR))
|| stun_id_cmp(&req->id, &rsp->id)) {
/* Bad STUN packet, not right type, or transaction ID did not match. */
memset(answer, 0, sizeof(struct sockaddr_in));
/* Was not a resonse to our request. */
goto try_again;
}
/* Success. answer contains the external address if available. */
res = 0;
break;
}
return res;
}
| 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.
{
switch (cmd) {
case CLI_INIT:
e->command = "stun set debug {on|off}";
e->usage =
"Usage: stun set debug {on|off}\n"
" Enable/Disable STUN (Simple Traversal of UDP through NATs)\n"
" debugging\n";
return NULL;
case CLI_GENERATE:
return NULL;
}
if (a->argc != e->args)
return CLI_SHOWUSAGE;
if (!strncasecmp(a->argv[e->args-1], "on", 2))
stundebug = 1;
else if (!strncasecmp(a->argv[e->args-1], "off", 3))
stundebug = 0;
else
return CLI_SHOWUSAGE;
ast_cli(a->fd, "STUN Debugging %s\n", stundebug ? "Enabled" : "Disabled");
return CLI_SUCCESS;
}
| 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().
{
switch (msg) {
case STUN_MAPPED_ADDRESS:
return "Mapped Address";
case STUN_RESPONSE_ADDRESS:
return "Response Address";
case STUN_CHANGE_REQUEST:
return "Change Request";
case STUN_SOURCE_ADDRESS:
return "Source Address";
case STUN_CHANGED_ADDRESS:
return "Changed Address";
case STUN_USERNAME:
return "Username";
case STUN_PASSWORD:
return "Password";
case STUN_MESSAGE_INTEGRITY:
return "Message Integrity";
case STUN_ERROR_CODE:
return "Error Code";
case STUN_UNKNOWN_ATTRIBUTES:
return "Unknown Attributes";
case STUN_REFLECTED_FROM:
return "Reflected From";
}
return "Non-RFC3489 Attribute";
}
| 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().
| static int stun_id_cmp | ( | stun_trans_id * | left, |
| stun_trans_id * | right | ||
| ) | [static] |
Definition at line 247 of file stun.c.
Referenced by ast_stun_request().
{
return memcmp(left, right, sizeof(*left));
}
| 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().
{
switch (msg) {
case STUN_BINDREQ:
return "Binding Request";
case STUN_BINDRESP:
return "Binding Response";
case STUN_BINDERR:
return "Binding Error Response";
case STUN_SECREQ:
return "Shared Secret Request";
case STUN_SECRESP:
return "Shared Secret Response";
case STUN_SECERR:
return "Shared Secret Error Response";
}
return "Non-RFC3489 Message";
}
| 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, stun_state::password, stun_attr2str(), STUN_PASSWORD, STUN_USERNAME, stun_state::username, and stun_attr::value.
Referenced by ast_stun_handle_packet().
{
if (stundebug)
ast_verbose("Found STUN Attribute %s (%04x), length %d\n",
stun_attr2str(ntohs(attr->attr)), ntohs(attr->attr), ntohs(attr->len));
switch (ntohs(attr->attr)) {
case STUN_USERNAME:
state->username = (const char *) (attr->value);
break;
case STUN_PASSWORD:
state->password = (const char *) (attr->value);
break;
default:
if (stundebug)
ast_verbose("Ignoring STUN attribute %s (%04x), length %d\n",
stun_attr2str(ntohs(attr->attr)), ntohs(attr->attr), ntohs(attr->len));
}
return 0;
}
| 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().
{
int x;
for (x = 0; x < 4; x++)
req->id.id[x] = ast_random();
}
| 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().
{
return sendto(s, resp, ntohs(resp->msglen) + sizeof(*resp), 0,
(struct sockaddr *)dst, sizeof(*dst));
}
struct ast_cli_entry cli_stun[] [static] |
{
AST_CLI_DEFINE(handle_cli_stun_set_debug, "Enable/Disable STUN debugging"),
}