diff options
author | Luigi Rizzo <rizzo@icir.org> | 2007-07-13 16:22:09 +0000 |
---|---|---|
committer | Luigi Rizzo <rizzo@icir.org> | 2007-07-13 16:22:09 +0000 |
commit | e950538bdd1ebc5086c0f5422e7867794fdfb926 (patch) | |
tree | dae29c664c9f07f1ce2e0daf9798b30270777fd4 /main | |
parent | 4d915d13c703334e34ecdbeb7024ec29a53a67a3 (diff) |
Small improvement to the STUN support so it can be used by
sockets other than RTP ones.
The main change is a new API function in main/rtp.c (see there
for a description)
int ast_stun_request(int s, struct sockaddr_in *dst,
const char *username, struct sockaddr_in *answer)
which can be used to send an STUN request on a socket, and
optionally wait for a reply and store the STUN_MAPPED_ADDRESS
into the 'answer' argument (obviously, the version that
waits for a reply is blocking, but this is no different
from DNS resolutions).
Internally there are minor modifications to let stun_handle_packet()
be somewhat configurable on how to parse the body of responses.
At the moment i am not committing any change to the clients,
but adding STUN client support is extremely simple, e.g. chan_sip.c
could do something like this:
+ add a variable to store the stun server address;
static struct sockaddr_in stunaddr = { 0, }; /*!< stun server address */
+ add code to parse a config file of the form "stunaddr=my.stun.server.org:3478"
(not shown for brevity);
+ right after binding the main sip socket, talk to the stun server to
determine the externally visible address
if (stunaddr.sin_addr.s_addr != 0)
ast_stun_request(sipsock, &stunaddr, NULL, &externip);
so now 'externip' is set with the externally visible address.
so it is really trivial.
Similarly ast_stun_request could be called when creating the RTP
socket (possibly adding a struct sockaddr_in field in the struct
ast_rtp to store the externalip).
git-svn-id: https://origsvn.digium.com/svn/asterisk/trunk@75034 65c4cc65-6c06-0410-ace0-fbb531ad65f3
Diffstat (limited to 'main')
-rw-r--r-- | main/rtp.c | 134 |
1 files changed, 109 insertions, 25 deletions
diff --git a/main/rtp.c b/main/rtp.c index 0bac1cef3..9f0c05808 100644 --- a/main/rtp.c +++ b/main/rtp.c @@ -449,29 +449,8 @@ size_t ast_rtp_alloc_size(void) return sizeof(struct ast_rtp); } -/*! \brief send a STUN BIND request to the given destination. - * Optionally, add a username if specified. - */ -void ast_rtp_stun_request(struct ast_rtp *rtp, struct sockaddr_in *suggestion, const char *username) -{ - struct stun_header *req; - unsigned char reqdata[1024]; - int reqlen, reqleft; - struct stun_attr *attr; - - req = (struct stun_header *)reqdata; - stun_req_id(req); - reqlen = 0; - reqleft = sizeof(reqdata) - 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); - stun_send(rtp->s, suggestion, req); -} +/*! \brief callback type to be invoked on stun responses. */ +typedef int (stun_cb_f)(struct stun_attr *attr, void *arg); /*! \brief handle an incoming STUN message. * @@ -479,8 +458,10 @@ void ast_rtp_stun_request(struct ast_rtp *rtp, struct sockaddr_in *suggestion, c * 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. */ -static int stun_handle_packet(int s, struct sockaddr_in *src, unsigned char *data, size_t len) +static int stun_handle_packet(int s, struct sockaddr_in *src, + unsigned char *data, size_t len, stun_cb_f *stun_cb, void *arg) { struct stun_header *hdr = (struct stun_header *)data; struct stun_attr *attr; @@ -518,6 +499,8 @@ static int stun_handle_packet(int s, struct sockaddr_in *src, unsigned char *dat 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; @@ -571,6 +554,107 @@ static int stun_handle_packet(int s, struct sockaddr_in *src, unsigned char *dat return ret; } +/*! \brief 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_get_mapped(struct stun_attr *attr, void *arg) +{ + struct stun_addr *addr = (struct stun_addr *)(attr + 1); + struct sockaddr_in *sa = (struct sockaddr_in *)arg; + + if (ntohs(attr->attr) != STUN_MAPPED_ADDRESS || ntohs(attr->len) != 8) + return 1; /* not us. */ + sa->sin_port = addr->port; + sa->sin_addr.s_addr = addr->addr; + return 0; +} + +/*! \brief Generic STUN request + * Send a generic stun request to the server specified, + * possibly waiting for a reply and filling the 'reply' field with + * the externally visible address. Note that in this case the request + * will be blocking. + * (Note, the interface may change slightly in the future). + * + * \param s the socket used to send the request + * \param dst the address of the STUN server + * \param username if non null, add the username in the request + * \param answer if non null, the function waits for a response and + * puts here the externally visible address. + * \return 0 on success, other values on error. + */ +int ast_stun_request(int s, struct sockaddr_in *dst, + const char *username, struct sockaddr_in *answer) +{ + struct stun_header *req; + unsigned char reqdata[1024]; + int reqlen, reqleft; + struct stun_attr *attr; + int res = 0; + int retry; + + req = (struct stun_header *)reqdata; + stun_req_id(req); + reqlen = 0; + reqleft = sizeof(reqdata) - 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; retry++) { /* XXX make retries configurable */ + /* send request, possibly wait for reply */ + unsigned char reply_buf[1024]; + fd_set rfds; + struct timeval to = { 3, 0 }; /* timeout, make it configurable */ + struct sockaddr_in src; + int srclen; + + res = stun_send(s, dst, req); + if (res < 0) { + ast_log(LOG_WARNING, "ast_stun_request send #%d failed error %d, retry\n", + retry, res); + continue; + } + if (answer == NULL) + break; + FD_ZERO(&rfds); + FD_SET(s, &rfds); + res = ast_select(s + 1, &rfds, NULL, NULL, &to); + if (res <= 0) /* timeout or error */ + continue; + bzero(&src, sizeof(src)); + srclen = sizeof(src); + /* XXX pass -1 in the size, because stun_handle_packet might + * write past the end of the buffer. + */ + res = recvfrom(s, reply_buf, sizeof(reply_buf) - 1, + 0, (struct sockaddr *)&src, &srclen); + if (res < 0) { + ast_log(LOG_WARNING, "ast_stun_request recvfrom #%d failed error %d, retry\n", + retry, res); + continue; + } + bzero(answer, sizeof(struct sockaddr_in)); + stun_handle_packet(s, &src, reply_buf, res, + stun_get_mapped, answer); + res = 0; /* signal regular exit */ + break; + } + return res; +} + +/*! \brief send a STUN BIND request to the given destination. + * Optionally, add a username if specified. + */ +void ast_rtp_stun_request(struct ast_rtp *rtp, struct sockaddr_in *suggestion, const char *username) +{ + ast_stun_request(rtp->s, suggestion, username, NULL); +} + /*! \brief List of current sessions */ static AST_RWLIST_HEAD_STATIC(protos, ast_rtp_protocol); @@ -1338,7 +1422,7 @@ struct ast_frame *ast_rtp_read(struct ast_rtp *rtp) * answers to requests, and it returns STUN_ACCEPT * if the request is valid. */ - if ((stun_handle_packet(rtp->s, &sin, rtp->rawdata + AST_FRIENDLY_OFFSET, res) == STUN_ACCEPT) && + if ((stun_handle_packet(rtp->s, &sin, rtp->rawdata + AST_FRIENDLY_OFFSET, res, NULL, NULL) == STUN_ACCEPT) && (!rtp->them.sin_port && !rtp->them.sin_addr.s_addr)) { memcpy(&rtp->them, &sin, sizeof(rtp->them)); } |