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:
parent
4d915d13c7
commit
e950538bdd
|
@ -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.
|
||||
|
|
134
main/rtp.c
134
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));
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue