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
This commit is contained in:
Luigi Rizzo 2007-07-13 16:22:09 +00:00
parent 4d915d13c7
commit e950538bdd
2 changed files with 125 additions and 26 deletions

View File

@ -211,7 +211,22 @@ void ast_rtp_setdtmfcompensate(struct ast_rtp *rtp, int compensate);
/*! \brief Enable STUN capability */
void ast_rtp_setstun(struct ast_rtp *rtp, int stun_enable);
/*! \brief Send STUN request (??) */
/*! \brief Generic STUN request
* send a generic stun request to the server specified.
* \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.
* The interface it may change in the future.
*/
int ast_stun_request(int s, struct sockaddr_in *dst,
const char *username, struct sockaddr_in *answer);
/*! \brief Send STUN request for an RTP socket
* Deprecated, this is just a wrapper for ast_rtp_stun_request()
*/
void ast_rtp_stun_request(struct ast_rtp *rtp, struct sockaddr_in *suggestion, const char *username);
/*! \brief The RTP bridge.

View File

@ -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));
}