diff --git a/acl.c b/acl.c index 85abe84f7bfaf059845a61a570696237ad580447..cf695474f090a4a83da979ea83f608fe2926979f 100755 --- a/acl.c +++ b/acl.c @@ -372,6 +372,22 @@ int ast_netsock_release(struct ast_netsock_list *list) return 0; } +struct ast_netsock *ast_netsock_find(struct ast_netsock_list *list, + struct sockaddr_in *sa) +{ + struct ast_netsock *sock = NULL; + + ASTOBJ_CONTAINER_TRAVERSE(list, !sock, { + ASTOBJ_RDLOCK(iterator); + if (!inaddrcmp(&iterator->bindaddr, sa)) + sock = iterator; + ASTOBJ_UNLOCK(iterator); + }); + + return sock; +} + + const struct sockaddr_in *ast_netsock_boundaddr(struct ast_netsock *ns) { return &(ns->bindaddr); diff --git a/channels/chan_iax2.c b/channels/chan_iax2.c index 4889d4efdccbef687db498273ee0f346409d9dc1..e01f02fbea8fee81aa6baa86f38ca6baf982f5b5 100755 --- a/channels/chan_iax2.c +++ b/channels/chan_iax2.c @@ -7827,6 +7827,92 @@ static int get_auth_methods(char *value) } +/*--- check_src_ip: Check if address can be used as packet source. + returns: + 0 address available + 1 address unavailable +-1 error +*/ +static int check_srcaddr(struct sockaddr *sa, socklen_t salen) +{ + int sd; + int res; + + sd = socket(AF_INET, SOCK_DGRAM, 0); + if (sd < 0) { + ast_log(LOG_ERROR, "Socket: %s\n", strerror(errno)); + return -1; + } + + res = bind(sd, sa, salen); + if (res < 0) { + ast_log(LOG_DEBUG, "Can't bind: %s\n", strerror(errno)); + close(sd); + return 1; + } + + close(sd); + return 0; +} + +/*--- peer_set_srcaddr: Parse the "sourceaddress" value, + lookup in netsock list and set peer's sockfd. Defaults to defaultsockfd if + not found. */ +static int peer_set_srcaddr(struct iax2_peer *peer, const char *srcaddr) +{ + struct sockaddr_in sin; + int nonlocal = 1; + int port = IAX_DEFAULT_PORTNO; + int sockfd = defaultsockfd; + char *tmp; + char *addr; + char *portstr; + + tmp = ast_strdupa(srcaddr); + if (!tmp) { + ast_log(LOG_WARNING, "Out of memory!\n"); + return -1; + } + + addr = strsep(&tmp, ":"); + portstr = tmp; + + if (portstr) { + port = atoi(portstr); + if (port < 1) + port = IAX_DEFAULT_PORTNO; + } + + if (!ast_get_ip(&sin, tmp)) { + struct ast_netsock *sock; + int res; + + sin.sin_port = 0; + res = check_srcaddr((struct sockaddr *) &sin, sizeof(sin)); + if (res == 0) { + /* ip address valid. */ + sin.sin_port = htons(port); + sock = ast_netsock_find(&netsock, &sin); + if (sock) { + sockfd = ast_netsock_sockfd(sock); + nonlocal = 0; + } + } + } + + peer->sockfd = sockfd; + + if (nonlocal) { + ast_log(LOG_WARNING, "Non-local or unbound address specified (%s) in sourceaddress for '%s', reverting to default\n", + srcaddr, peer->name); + return -1; + } else { + ast_log(LOG_DEBUG, "Using sourceaddress %s for '%s'\n", srcaddr, peer->name); + return 0; + } +} + + /*--- build_peer: Create peer structure based on configuration */ static struct iax2_peer *build_peer(const char *name, struct ast_variable *v, int temponly) { @@ -7943,6 +8029,8 @@ static struct iax2_peer *build_peer(const char *name, struct ast_variable *v, in free(peer); return NULL; } + } else if (!strcasecmp(v->name, "sourceaddress")) { + peer_set_srcaddr(peer, v->value); } else if (!strcasecmp(v->name, "permit") || !strcasecmp(v->name, "deny")) { peer->ha = ast_append_ha(v->name, v->value, peer->ha); diff --git a/include/asterisk/acl.h b/include/asterisk/acl.h index 100d45ad32054de481ac1e92c24519f4ca2455a2..3baa45e5cc24ef6c2a0270dcda9e549609ae50fd 100755 --- a/include/asterisk/acl.h +++ b/include/asterisk/acl.h @@ -49,6 +49,8 @@ extern struct ast_netsock *ast_netsock_bind(struct ast_netsock_list *list, struc extern struct ast_netsock *ast_netsock_bindaddr(struct ast_netsock_list *list, struct io_context *ioc, struct sockaddr_in *bindaddr, int tos, ast_io_cb callback, void *data); extern int ast_netsock_free(struct ast_netsock_list *list, struct ast_netsock *netsock); extern int ast_netsock_release(struct ast_netsock_list *list); +extern struct ast_netsock *ast_netsock_find(struct ast_netsock_list *list, + struct sockaddr_in *sa); extern int ast_netsock_sockfd(struct ast_netsock *ns); extern const struct sockaddr_in *ast_netsock_boundaddr(struct ast_netsock *ns); extern void *ast_netsock_data(struct ast_netsock *ns);