mirror of
https://github.com/asterisk/asterisk.git
synced 2025-10-13 00:04:53 +00:00
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:
@@ -211,7 +211,22 @@ void ast_rtp_setdtmfcompensate(struct ast_rtp *rtp, int compensate);
|
|||||||
/*! \brief Enable STUN capability */
|
/*! \brief Enable STUN capability */
|
||||||
void ast_rtp_setstun(struct ast_rtp *rtp, int stun_enable);
|
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);
|
void ast_rtp_stun_request(struct ast_rtp *rtp, struct sockaddr_in *suggestion, const char *username);
|
||||||
|
|
||||||
/*! \brief The RTP bridge.
|
/*! \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);
|
return sizeof(struct ast_rtp);
|
||||||
}
|
}
|
||||||
|
|
||||||
/*! \brief send a STUN BIND request to the given destination.
|
/*! \brief callback type to be invoked on stun responses. */
|
||||||
* Optionally, add a username if specified.
|
typedef int (stun_cb_f)(struct stun_attr *attr, void *arg);
|
||||||
*/
|
|
||||||
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 handle an incoming STUN message.
|
/*! \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.
|
* try to extract a bit of information, and possibly reply.
|
||||||
* At the moment this only processes BIND requests, and returns
|
* At the moment this only processes BIND requests, and returns
|
||||||
* the externally visible address of the request.
|
* 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_header *hdr = (struct stun_header *)data;
|
||||||
struct stun_attr *attr;
|
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);
|
ast_debug(1, "Inconsistent Attribute (length %d exceeds remaining msg len %d)\n", x, (int)len);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
if (stun_cb)
|
||||||
|
stun_cb(attr, arg);
|
||||||
if (stun_process_attr(&st, attr)) {
|
if (stun_process_attr(&st, attr)) {
|
||||||
ast_debug(1, "Failed to handle attribute %s (%04x)\n", stun_attr2str(ntohs(attr->attr)), ntohs(attr->attr));
|
ast_debug(1, "Failed to handle attribute %s (%04x)\n", stun_attr2str(ntohs(attr->attr)), ntohs(attr->attr));
|
||||||
break;
|
break;
|
||||||
@@ -571,6 +554,107 @@ static int stun_handle_packet(int s, struct sockaddr_in *src, unsigned char *dat
|
|||||||
return ret;
|
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 */
|
/*! \brief List of current sessions */
|
||||||
static AST_RWLIST_HEAD_STATIC(protos, ast_rtp_protocol);
|
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
|
* answers to requests, and it returns STUN_ACCEPT
|
||||||
* if the request is valid.
|
* 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)) {
|
(!rtp->them.sin_port && !rtp->them.sin_addr.s_addr)) {
|
||||||
memcpy(&rtp->them, &sin, sizeof(rtp->them));
|
memcpy(&rtp->them, &sin, sizeof(rtp->them));
|
||||||
}
|
}
|
||||||
|
Reference in New Issue
Block a user