[asterisk-commits] blanchet: branch blanchet/v6 r58478 -
/team/blanchet/v6/main/netsock.c
asterisk-commits at lists.digium.com
asterisk-commits at lists.digium.com
Thu Mar 8 14:20:32 MST 2007
Author: blanchet
Date: Thu Mar 8 15:20:31 2007
New Revision: 58478
URL: http://svn.digium.com/view/asterisk?view=rev&rev=58478
Log:
First cut of IP version independent netsock routines
Supports both IPv4 and IPv6.
no changes to the current ast_netsock routines were done.
new routines names start with ast_vinetsock. (vi stands for version independent)
Modified:
team/blanchet/v6/main/netsock.c
Modified: team/blanchet/v6/main/netsock.c
URL: http://svn.digium.com/view/asterisk/team/blanchet/v6/main/netsock.c?view=diff&rev=58478&r1=58477&r2=58478
==============================================================================
--- team/blanchet/v6/main/netsock.c (original)
+++ team/blanchet/v6/main/netsock.c Thu Mar 8 15:20:31 2007
@@ -19,10 +19,19 @@
/*! \file
*
- * \brief Network socket handling
+/*! \file
+ * \brief Network socket handling.
*
* \author Kevin P. Fleming <kpfleming at digium.com>
* \author Mark Spencer <markster at digium.com>
+ *
+ * IPv6/version independent version:
+ * The ast_vinetwork* functions, vi stands * for 'version independant'
+ * support both IPv4 and IPv6. Storage of sockaddr_*
+ * is in sockaddr_storage but, as per RFC3493 routines, pointers to
+ * sockaddr struct are use for public API.
+ * \author Marc Blanchet <marc.blanchet at viagenie.ca>
+ * \author Frederick Lefebvre <frederick.lefebvre at viagenie.ca>
*/
#include "asterisk.h"
@@ -61,7 +70,1201 @@
#include "asterisk/utils.h"
#include "asterisk/lock.h"
#include "asterisk/srv.h"
-
+#include "asterisk/strings.h"
+
+/*! \brief Destroy and remove from memory a vinetsock
+ * \param vinetsock pointer to the vinetsock object we are destroying
+ * \return void
+ * \note internal utility function. users should use ast_vinetsock_free
+ */
+static void ast_vinetsock_destroy(struct ast_vinetsock *vinetsock)
+{
+ if (!vinetsock) return;
+
+ if (vinetsock->ioc && vinetsock->ioref)
+ ast_io_remove(vinetsock->ioc, vinetsock->ioref);
+
+ if (vinetsock->sockfd > 0)
+ close(vinetsock->sockfd);
+ ast_free(vinetsock);
+}
+
+/*! \brief Destroy, remove from memory and unref in the list a vinetsock
+ * \param ns pointer to the vinetsock object we are freeing
+ * \return void
+ * \note should be used in conjonction with ast_vinetsock_new
+ */
+void ast_vinetsock_free(struct ast_vinetsock* ns)
+{
+ if (!ns) return;
+ ASTOBJ_UNREF(ns, ast_vinetsock_destroy);
+}
+
+/*! \brief Allocates memory for the ast_vinetsock_list object
+ * \param void
+ * \return pointer to the new allocated ast_vinetsock_list object
+ */
+struct ast_vinetsock_list *ast_vinetsock_list_alloc(void)
+{
+ return ast_calloc(1, sizeof(struct ast_vinetsock_list));
+}
+
+/*! \brief Initialize a ast_vinetsock_list object
+ * \param list pointer to the list we are initializing.
+ * \return void
+ * \note the list object should exist in memory
+ */
+void ast_vinetsock_init(struct ast_vinetsock_list *list)
+{
+ if (!list) return;
+
+ memset(list, 0, sizeof(*list));
+ ASTOBJ_CONTAINER_INIT(list);
+}
+
+/*! \brief Destroy all objects in the list and the list itself
+ * \param list pointer to the list we are destroying
+ * \return void
+ * \note the list object itself is not memory freed.
+ */
+void ast_vinetsock_release(struct ast_vinetsock_list *list)
+{
+ if (!list) return;
+
+ ASTOBJ_CONTAINER_DESTROYALL(list, ast_vinetsock_destroy);
+ ASTOBJ_CONTAINER_DESTROY(list);
+}
+
+/*! \brief Add a ast_vinetsock object to an ast_vinetsock_list
+ * \param list pointer to the list in which we add the new vinetsock object
+ * \param ns pointer to the ast_vinetsock object to be added to the list
+ * \return void
+ */
+void ast_vinetsock_list_add(struct ast_vinetsock_list *list, struct ast_vinetsock *ns)
+{
+ if (!list || !ns) return;
+ ASTOBJ_CONTAINER_LINK(list, ns);
+}
+
+/*! \brief Iterate over the ast_vinetsock_list and set the port in each sockaddr
+ * to the port passed in parameter, unless the port of a sockaddr was not zero.
+ * \param list pointer to the list we are modifying
+ * \param port number of the transport port
+ * \return void
+ */
+void ast_vinetsock_list_set_defport(struct ast_vinetsock_list *list,
+ int portno)
+{
+ int port_ori = 0;
+
+ if (!list) return;
+
+ ASTOBJ_CONTAINER_TRAVERSE(list, 1, {
+ ASTOBJ_RDLOCK(iterator);
+ port_ori = ast_vinetsock_sa_getport((struct sockaddr*)&iterator->bindaddr,
+ iterator->bindaddrlen);
+ ASTOBJ_UNLOCK(iterator);
+ if (0 == port_ori) {
+ ASTOBJ_WRLOCK(iterator);
+ ast_vinetsock_sa_setport((struct sockaddr*)&iterator->bindaddr,
+ portno);
+ ASTOBJ_UNLOCK(iterator);
+ }
+ });
+}
+
+/*! \brief Iterate over the ast_vinetsock_list and bind to each ast_vinetsock
+ * \param list pointer to the list of vinetsock we are binding to
+ * \param ioc pointer to an io_context
+ * \param tos type of service to be set on the socket
+ * \param callback callback function
+ * \param data
+ * \param reuseaddr
+ * \param module_name name of the module calling that function. for logging purposes.
+ * \return void
+ */
+void ast_vinetsock_list_bind(struct ast_vinetsock_list *list,
+ struct io_context *ioc,
+ int tos, ast_io_cb callback, void *data,
+ int reuseaddr, char *module_name)
+{
+ if (!list) return;
+
+ ASTOBJ_CONTAINER_TRAVERSE(list, 1, {
+ ASTOBJ_WRLOCK(iterator);
+ ast_vinetsock_bind(iterator, ioc, tos,
+ callback, data, reuseaddr, module_name);
+ ASTOBJ_UNLOCK(iterator);
+
+ });
+}
+
+/*! \brief Iterate over the ast_vinetsock_list and return the sockfd
+ * for the first vinetsock struct that matches the address family
+ * \param list pointer to the list we do a lookup
+ * \param af address family to be matched
+ * \return socket file descriptor; -1 if error.
+ */
+int ast_vinetsock_list_getsockfd(struct ast_vinetsock_list *list, int af)
+{
+ int sockfd = 0;
+
+ if (!list) return -1;
+
+ ASTOBJ_CONTAINER_TRAVERSE(list, !sockfd, {
+ ASTOBJ_RDLOCK(iterator);
+ if (iterator->sockfd && iterator->bindaddrlen &&
+ iterator->bindaddr.ss_family == af)
+ sockfd = iterator->sockfd;
+ ASTOBJ_UNLOCK(iterator);
+ });
+
+ return sockfd;
+}
+
+/*! \brief Return the best ast_vinetsock according to the received parameters.
+ * \param list pointer to the list we do a lookup
+ * \param dst sockaddr used for matching
+ * \param dstlen length of the sockaddr.
+ * \param af Address family of the desired netsock (AF_INET or AF_INET6)
+ * \return pointer to the ast_vinetsock object found. NULL if not found
+ * \note currently only compares the address family of the sockaddr struct
+ * or the af parameter if sockaddr is NULL
+ */
+struct ast_vinetsock*
+ast_vinetsock_find_best(struct ast_vinetsock_list *list, struct sockaddr* dst,
+ socklen_t dstlen, int af)
+{
+ int afi=af;
+
+ if (!list) return NULL;
+
+ if (dst)
+ afi = dst->sa_family;
+
+ struct ast_vinetsock *ns = NULL;
+ ASTOBJ_CONTAINER_TRAVERSE(list, !ns, {
+ ASTOBJ_RDLOCK(iterator);
+ if (iterator->bindaddrlen &&
+ iterator->bindaddr.ss_family == afi)
+ ns = iterator;
+ ASTOBJ_UNLOCK(iterator);
+ });
+ ASTOBJ_REF(ns);
+ return ns;
+}
+
+/*! \brief Return 'true' if the list is empty
+ * \param list pointer to the list to be tested
+ * \return 1 if list is empty, 0 otherwise
+ * \note should be done in include/asterisk/astobj.h instead
+ */
+int ast_vinetsock_list_isempty(struct ast_vinetsock_list *list)
+{
+ if (!list) return 1;
+ return (list->head == NULL);
+}
+
+/*! \brief Find a ast_vinetsock in 'list', based on the struct
+ * sockaddr* or from a socket file descriptor
+ * \param list pointer to the list we do a lookup
+ * \param sa pointer to sockaddr we are trying to find in the list. set to zero if not used.
+ * \param salen length of the sockaddr. set to zero if not used.
+ * \param sockfd socket file descriptor. set to zero if not used.
+ * \return pointer to sockaddr found or NULL if not found
+ * \note if the sockaddr is used for comparison (sockfd is not used),
+ * the port of the sockaddr is also used in the comparison.
+ * i.e. a sockaddr in the list which has the same address
+ * but different port than the passing sockaddr will return NULL.
+ */
+struct ast_vinetsock *ast_vinetsock_find(struct ast_vinetsock_list *list,
+ struct sockaddr *sa, socklen_t salen,
+ int sockfd)
+{
+ struct ast_vinetsock *sock = NULL;
+ int cmp = 0;
+
+ if (!list) return NULL;
+
+ if (sa && salen) {
+ ASTOBJ_CONTAINER_TRAVERSE(list, !sock, {
+ ASTOBJ_RDLOCK(iterator);
+ cmp = ast_vinetsock_sacmp((struct sockaddr*)&iterator->bindaddr,
+ iterator->bindaddrlen, sa, salen, 0);
+ if (0 == cmp) {
+ sock = iterator;
+ ASTOBJ_REF(sock);
+ }
+ ASTOBJ_UNLOCK(iterator);
+ });
+ } else if (sockfd > 0) {
+ ASTOBJ_CONTAINER_TRAVERSE(list, !sock, {
+ ASTOBJ_RDLOCK(iterator);
+ if (iterator->sockfd > 0 && iterator->sockfd == sockfd) {
+ sock = iterator;
+ ASTOBJ_REF(sock);
+ }
+ ASTOBJ_UNLOCK(iterator);
+ });
+ }
+ return sock;
+}
+
+/*! \brief Set the TOS(IPv4) or TCLASS(IPv6) on a socket
+ * \param sockfd Socket file descriptor
+ * \param af Address family of the socket
+ * \param tos Type of Service to be set on the socket
+ * \return setsockopt(3) rc: 0 on success or -1 otherwise
+ * \note per RFC2460 section 7, currently TOS bits are same as TCLASS bits
+ * \note if OS does not support IPV6_TCLASS define, then do not set
+ * the traffic class, which does not harm in any way.
+ */
+int ast_vinetsock_sockfd_settos(int sockfd, int af, int tos)
+{
+ int result = -1;
+
+ switch(af) {
+ case AF_INET:
+ if ((result = setsockopt(sockfd, IPPROTO_IP, IP_TOS, &tos, sizeof(tos))))
+ ast_log(LOG_WARNING, "Unable to set TOS to %d\n", tos);
+ break;
+ case AF_INET6:
+#if HAVE_IPV6_TCLASS
+ if ((result = setsockopt(sockfd, IPPROTO_IPV6, IPV6_TCLASS, &tos, sizeof(tos))))
+ ast_log(LOG_WARNING, "Unable to set TCLASS to %d\n", tos);
+#endif
+ break;
+ }
+ return result;
+}
+
+/*! \brief Create and bind a socket to the bindaddr and register a callback
+ * \param list pointer to the list in which the new socket will be attached
+ * \param ioc io context on which the new socket is attached
+ * \param bindaddr pointer to addrinfo object specifying the addr to bind to.
+ * \param tos Type of service to be set to the socket
+ * \param callback Callback function
+ * \param data
+ * \return the vinetsock object pointer or NULL if error
+ */
+struct ast_vinetsock *ast_vinetsock_bindaddr(struct ast_vinetsock_list *list,
+ struct io_context *ioc,
+ struct addrinfo *bindaddr, int tos,
+ ast_io_cb callback, void *data)
+{
+ int netsocket = -1;
+ int *ioref;
+ char iabuf[NI_MAXHOST];
+ char sbuf[NI_MAXSERV];
+
+ struct ast_vinetsock *ns = NULL;
+
+ if (!list || !bindaddr) return NULL;
+
+ netsocket = socket(bindaddr->ai_family, bindaddr->ai_socktype, bindaddr->ai_protocol);
+
+ if (netsocket < 0) {
+ getnameinfo(bindaddr->ai_addr, bindaddr->ai_addrlen, iabuf,
+ sizeof(iabuf), sbuf, sizeof(sbuf),
+ NI_NUMERICHOST|NI_NUMERICSERV);
+ ast_log(LOG_ERROR, "Unable to create network socket to %s on port %s: %s\n", iabuf, sbuf, strerror(errno));
+ return NULL;
+ }
+ if (bind(netsocket,bindaddr->ai_addr, bindaddr->ai_addrlen)) {
+ getnameinfo(bindaddr->ai_addr, bindaddr->ai_addrlen, iabuf,
+ sizeof(iabuf), sbuf, sizeof(sbuf),
+ NI_NUMERICHOST|NI_NUMERICSERV);
+ ast_log(LOG_ERROR, "Unable to bind to %s port %s: %s\n",
+ iabuf, sbuf, strerror(errno));
+ close(netsocket);
+ return NULL;
+ }
+ if (option_verbose > 1)
+ ast_verbose(VERBOSE_PREFIX_2 "Using TOS bits %d\n", tos);
+
+ if (ast_vinetsock_sockfd_settos(netsocket, bindaddr->ai_family, tos)) {
+ getnameinfo(bindaddr->ai_addr, bindaddr->ai_addrlen, iabuf,
+ sizeof(iabuf), sbuf, sizeof(sbuf),
+ NI_NUMERICHOST|NI_NUMERICSERV);
+ ast_log(LOG_WARNING, "Unable to set TOS/TCLASS to %d on %s port %s\n", tos, iabuf, sbuf);
+ }
+ if (!(ns = ast_calloc(1, sizeof(struct ast_vinetsock)))) {
+ close(netsocket);
+ return NULL;
+ }
+
+ /* Establish I/O callback for socket read */
+ if (!(ioref = ast_io_add(ioc, netsocket, callback, AST_IO_IN, ns))) {
+ ast_free(ns);
+ close(netsocket);
+ return NULL;
+ }
+ ASTOBJ_INIT(ns);
+ ns->ioref = ioref;
+ ns->ioc = ioc;
+ ns->sockfd = netsocket;
+ ns->data = data;
+
+ memcpy(&ns->bindaddr, bindaddr->ai_addr, sizeof(ns->bindaddr));
+ ns->bindaddrlen = bindaddr->ai_addrlen;
+ ASTOBJ_CONTAINER_LINK(list, ns);
+
+ return ns;
+}
+
+/*! \brief Create and bind a socket to the host/port number and register a callback
+ * \param list pointer to the list in which the new socket will be attached
+ * \param ioc io context on which the new socket is attached
+ * \param bindinfo pointer to host string onto which we bind
+ * \param port port number
+ * \param tos Type of service to be set to the socket
+ * \param callback Callback function
+ * \param data
+ * \return the vinetsock object pointer or NULL if error
+ */
+struct ast_vinetsock *ast_vinetsock_bind_portn(struct ast_vinetsock_list *list,
+ struct io_context *ioc,
+ const char *bindinfo, int port, int tos,
+ ast_io_cb callback, void *data)
+{
+ char portno[NI_MAXSERV];
+
+ if (!list) return NULL;
+
+ if (!ast_vinetsock_isvalid_port(port))
+ return NULL;
+
+ snprintf(portno, NI_MAXSERV, "%d", port);
+ return ast_vinetsock_bind_portstr(list, ioc, bindinfo, portno, tos, callback, data);
+}
+
+/*! \brief Create and bind a socket to the host/port number string and register a callback
+ * \param list pointer to the list in which the new socket will be attached
+ * \param ioc io context on which the new socket is attached
+ * \param bindinfo pointer to host string onto which we bind
+ * \param port port number
+ * \param tos Type of service to be set to the socket
+ * \param callback Callback function
+ * \param data
+ * \return the vinetsock object pointer or NULL if error
+ */
+struct ast_vinetsock *ast_vinetsock_bind_portstr(struct ast_vinetsock_list *list,
+ struct io_context *ioc,
+ const char *bindinfo, char *port, int tos,
+ ast_io_cb callback, void *data)
+{
+ struct addrinfo hints;
+ struct addrinfo *res_addrinfo = NULL;
+ struct ast_vinetsock *res_vinetsock = NULL;
+ int res = 0;
+
+ if (!list) return NULL;
+
+ memset(&hints, 0, sizeof(hints));
+ hints.ai_family = PF_UNSPEC;
+ hints.ai_socktype = SOCK_DGRAM;
+ hints.ai_flags = AI_PASSIVE;
+
+ if ((res = getaddrinfo(bindinfo, port, &hints, &res_addrinfo))) {
+ ast_log(LOG_ERROR, "getaddrinfo with address %s and port %s : %s \n", bindinfo, port, gai_strerror(res));
+ return NULL;
+ }
+
+ res_vinetsock = ast_vinetsock_bindaddr(list, ioc, res_addrinfo, tos, callback, data);
+
+ freeaddrinfo(res_addrinfo);
+ return res_vinetsock;
+}
+
+/*! \brief Create and initialize a vinetsock struct from a host and port string
+ * \param iabuf Address string
+ * \param sbuf Port number string
+ * \return Pointer to an allocated ast_vinetsock struct
+ */
+struct ast_vinetsock *ast_vinetsock_new(const char* iabuf, const char *sbuf)
+{
+ struct addrinfo hints;
+ struct addrinfo *res_addrinfo = NULL;
+ int res = 0;
+ struct ast_vinetsock *ns = 0;
+
+ memset(&hints, 0, sizeof(hints));
+ hints.ai_family = PF_UNSPEC;
+ hints.ai_socktype = SOCK_DGRAM;
+ hints.ai_flags = AI_PASSIVE;
+
+ if ((res = getaddrinfo(iabuf, sbuf, &hints, &res_addrinfo))) {
+ ast_log(LOG_ERROR, "getaddrinfo with address %s and port %s : %s \n", iabuf, sbuf, gai_strerror(res));
+ return NULL;
+ }
+
+ if (!(ns = ast_calloc(1, sizeof(struct ast_vinetsock)))) {
+ freeaddrinfo(res_addrinfo);
+ return NULL;
+ }
+ memcpy(&ns->bindaddr, res_addrinfo->ai_addr, res_addrinfo->ai_addrlen);
+ ns->bindaddrlen = res_addrinfo->ai_addrlen;
+ ns->sock_protocol = res_addrinfo->ai_protocol;
+ ns->sock_type = res_addrinfo->ai_socktype;
+ ns->sock_family = res_addrinfo->ai_family;
+ freeaddrinfo(res_addrinfo);
+ ASTOBJ_INIT(ns);
+
+ return ns;
+}
+
+/*! \brief Bind a vinetsock and register its callback
+ * \param ns pointer to vinetsock to be bound
+ * \param ioc pointer to io_context
+ * \param callback callback function pointer
+ * \param reuseaddr If true, then reuseaddr option will be set on the socket
+ * \param module_name Name of the calling module for logging.
+ * \return 0 if success; -1 if error
+ */
+int ast_vinetsock_bind(struct ast_vinetsock *ns, struct io_context *ioc,
+ int tos, ast_io_cb callback, void *data, int reuseaddr,
+ const char* module_name)
+{
+ int result = -1;
+ char iabuf[NI_MAXHOST];
+ char sbuf[NI_MAXSERV];
+ char hostport[NI_MAXHOST];
+
+ if (!ns) return -1;
+
+ ns->sockfd = socket(ns->sock_family, ns->sock_type, ns->sock_protocol);
+
+ if (ns->sockfd < 0) {
+ getnameinfo((struct sockaddr*)&ns->bindaddr, ns->bindaddrlen,
+ iabuf, sizeof(iabuf), sbuf, sizeof(sbuf),
+ NI_NUMERICHOST|NI_NUMERICSERV);
+ ast_log(LOG_ERROR, "Unable to create network socket to %s port %s: %s\n", iabuf, sbuf, strerror(errno));
+ return result;
+ }
+ if (reuseaddr) {
+ const int reuseFlag = 1;
+ setsockopt(ns->sockfd, SOL_SOCKET, SO_REUSEADDR,
+ (const char*)&reuseFlag,
+ sizeof reuseFlag);
+ }
+
+ if (bind(ns->sockfd ,(struct sockaddr*)&ns->bindaddr,
+ ns->bindaddrlen)) {
+ getnameinfo((struct sockaddr*)&ns->bindaddr, ns->bindaddrlen,
+ iabuf, sizeof(iabuf), sbuf, sizeof(sbuf),
+ NI_NUMERICHOST|NI_NUMERICSERV);
+ ast_log(LOG_ERROR, "Unable to bind to %s port %s: %s\n", iabuf, sbuf,
+ strerror(errno));
+ close(ns->sockfd);
+ ns->sockfd = -1;
+ return result;
+ }
+ if (option_verbose > 1) {
+ ast_vinetsock_get_hostport(ns, hostport, NI_MAXHOST);
+ ast_verbose(VERBOSE_PREFIX_2 "%s, Listening on %s\n", module_name,hostport);
+ ast_verbose(VERBOSE_PREFIX_3 "Using TOS bits %d\n", tos);
+ }
+
+ if (ast_vinetsock_sockfd_settos(ns->sockfd, ns->bindaddr.ss_family, tos)) {
+ getnameinfo((struct sockaddr*)&ns->bindaddr, ns->bindaddrlen,
+ iabuf, sizeof(iabuf), sbuf, sizeof(sbuf),
+ NI_NUMERICHOST|NI_NUMERICSERV);
+ ast_log(LOG_WARNING, "Unable to set TOS/TCLASS to %d on %s port %s\n", tos, iabuf, sbuf);
+ }
+
+ /* Establish I/O callback for socket read */
+ if (!(ns->ioref = ast_io_add(ioc, ns->sockfd, callback, AST_IO_IN, ns))) {
+ close(ns->sockfd);
+ ns->sockfd = -1;
+ return result;
+ }
+
+ ns->ioc = ioc;
+ ns->data = data;
+ return 0;
+}
+
+/*! \brief Decrement the reference count on a ast_vinetsock struct
+ * \param ns Pointer to the ast_vinetsock struct to operate on.
+ * \return void
+ */
+void ast_vinetsock_unref(struct ast_vinetsock *ns)
+{
+ ASTOBJ_UNREF(ns, ast_vinetsock_destroy);
+}
+
+/*! \brief Create a hostport string from a vinetsock struct
+ * \param ns Netsock structure
+ * \param hostport Pointer to an allocated buffer to put the result in
+ * \return 0 on success, >0 otherwise
+ */
+int ast_vinetsock_get_hostport(const struct ast_vinetsock *ns, char *hostport, size_t hplen)
+{
+ if (!ns || !hplen)
+ return -1;
+
+ return ast_vinetsock_sa_get_hostport((struct sockaddr*)&ns->bindaddr,
+ ns->bindaddrlen, hostport, hplen);
+}
+
+/*! \brief Create a host (as per BNF host definition in
+ * RFC3986) string from a sockaddr struct
+ * \param sa Pointer to sockaddr structure
+ * \param salen Length of sa
+ * \param host Pointer to an allocated buffer to put the result in
+ * \param hostlen Length of host buffer
+ * \return 0 on success, >0 otherwise
+ */
+int ast_vinetsock_sa_get_host(const struct sockaddr* sa, socklen_t salen,
+ char* host, size_t hostlen)
+{
+ char iabuf[NI_MAXHOST];
+
+ if (!sa || !host)
+ return 1;
+
+ if (getnameinfo(sa, salen, iabuf, sizeof(iabuf), NULL, 0,
+ NI_NUMERICHOST))
+ return 1;
+
+ switch(sa->sa_family) {
+ case(AF_INET):
+ snprintf(host, hostlen, "%s", iabuf);
+ return 0;
+ break;
+ case(AF_INET6):
+ snprintf(host, hostlen, "[%s]", iabuf);
+ return 0;
+ break;
+ }
+ return 0;
+}
+
+/*! \brief Create a hostport (as per BNF authority definition in
+ * RFC3986) string from a sockaddr struct
+ * \param ns Netsock structure
+ * \param hostport Pointer to an allocated buffer to put the result in
+ * \return 0 on success, >0 otherwise
+ */
+int ast_vinetsock_sa_get_hostport(const struct sockaddr *sa, socklen_t salen,
+ char *hostport, size_t hplen)
+{
+ int result = 1;
+ char host[NI_MAXHOST];
+ char sbuf[NI_MAXSERV];
+
+ if (!sa || !salen)
+ return result;
+
+ if(ast_vinetsock_sa_get_host(sa, salen, host, sizeof(host)))
+ goto end;
+
+ if (getnameinfo(sa, salen, NULL, 0, sbuf, sizeof(sbuf),
+ NI_NUMERICSERV))
+ goto end;
+
+ if (strlen(sbuf) && ast_vinetsock_str2port(sbuf))
+ snprintf(hostport, hplen, "%s:%s", host, sbuf);
+ else
+ snprintf(hostport, hplen, "%s", host);
+ result = 0;
+
+ end:
+ return(result);
+}
+
+/*! \brief Verifies that an integer is a valid port number
+ * \param portno long integer
+ * \return >0 if portno is a valid port number, 0 if it is not
+ */
+int ast_vinetsock_isvalid_port(long int portno)
+{
+ return (portno >= 0 && portno <= USHRT_MAX);
+}
+
+/*! \brief Verifies that a string is a valid port number
+ * \param port String to convert to port
+ * \param end Pointer to return the first char after the port
+ * \note If end is null, the function will return 0 if there are any char
+ * after the port.
+ * \return integer port number if portno is a valid port number, 0 if it is not
+ */
+int ast_vinetsock_str2port(const char *port)
+{
+ long int portno = 0;
+ char *end;
+
+ portno = strtol(port, &end, 10);
+ if (end[0] != '\0' || !ast_vinetsock_isvalid_port(portno))
+ return 0;
+ else
+ return portno;
+}
+
+/*! \brief Get an string representation of the address in a ast_vinetsock
+ * \param ns ast_vinetsock from which to extract the address
+ * \param iabuf String through which the result is returned
+ * \param iabuflen Length of the iabuf string
+ * \return 0 on success, !0 on failure
+ */
+int ast_vinetsock_getaddr(const struct ast_vinetsock* ns,
+ char* iabuf, size_t iabuflen)
+{
+ if (!ns || ! iabuf)
+ return -1;
+
+ return ast_vinetsock_sa_getaddr((struct sockaddr*)&ns->bindaddr,
+ ns->bindaddrlen, iabuf, iabuflen);
+}
+
+/*! \brief Get a string representation of the address in a sockaddr
+ * \param sa Sockaddr from which to extract the address
+ * \param salen Length of the sockaddr structure
+ * \param iabuf String through which the result is returned
+ * \param iabuflen Length of the iabuf string
+ * \return 0 on success, !0 on failure
+ */
+int ast_vinetsock_sa_getaddr(const struct sockaddr* sa, socklen_t salen,
+ char* iabuf, size_t iabuflen)
+{
+ if (!sa || ! iabuf || !salen || !iabuflen)
+ return -1;
+
+ memset(iabuf, 0, iabuflen);
+
+ return getnameinfo(sa, salen, iabuf, iabuflen, NULL, 0,
+ NI_NUMERICHOST|NI_NUMERICSERV);
+}
+
+/*! \brief Create a sockaddr from an address and port strings.
+ * If getaddrinfo returns multiple addrinfo, only the first one
+ * with a matching address family will be used.
+ * \param addr Address string
+ * \param port Port string
+ * \param sa Pointer to sockaddr struct to put the result in
+ * \param salen Pointer to the length of the sockaddr struct
+ * \param af Address family of the sockaddr to be returned
+ * \return 0 on success, <0 otherwise
+ */
+int ast_vinetsock_sa_fromstr(const char* addr, const char* port, struct sockaddr* sa,
+ socklen_t *salen, int af, int numerichost)
+{
+ struct addrinfo hints, *res = NULL;
+
+ if (!addr || !sa)
+ return -1;
+
+ memset(&hints, 0, sizeof(hints));
+ if (numerichost)
+ hints.ai_flags = AI_NUMERICHOST;
+ else
+ hints.ai_family = af;
+ hints.ai_socktype = SOCK_DGRAM;
+ if(getaddrinfo(addr, port, &hints, &res))
+ return -1;
+
+ memcpy(sa, res->ai_addr, res->ai_addrlen);
+ *salen = res->ai_addrlen;
+
+ freeaddrinfo(res);
+ return 0;
+}
+
+/*! \brief Get the port in host order from a ast_vinetsock struct
+ * \param sa Pointer to ast_vinetsock struct
+ * \return Host ordered port number or -1 on failure
+ */
+int ast_vinetsock_getport(const struct ast_vinetsock *ns)
+{
+ if (!ns)
+ return -1;
+
+ return ast_vinetsock_sa_getport((struct sockaddr*)&ns->bindaddr,
+ ns->bindaddrlen);
+}
+
+/*! \brief Get the port in host order from a sockaddr struct
+ * \param sa Pointer to sockaddr struct
+ * \param salen Length of sa
+ * \return Host ordered port number or -1 on failure
+ */
+int ast_vinetsock_sa_getport(const struct sockaddr* sa, socklen_t salen)
+{
+ char port[NI_MAXSERV];
+
+ if (!sa || salen < sizeof(struct sockaddr_in))
+ return -1;
+
+ if (getnameinfo(sa, salen, NULL, 0, port, sizeof(port), NI_NUMERICSERV))
+ return -1;
+
+ return ast_vinetsock_str2port(port);
+}
+
+/*! \brief Build a hostport string (as per the BNF in RFC 3986) from a host
+ * and port string
+ * \param hbuf input host string
+ * \param sbuf input port string
+ * \param hp output hostport string
+ * \param hplen output hostport string length
+ * \return 0 if success; -1 otherwise
+ * \note
+ */
+int ast_vinetsock_hp2str(char *hbuf, char *sbuf, char *hp, size_t hplen)
+{
+ char *p1 = 0;
+ size_t sbuflen = strlen(sbuf);
+
+ if (!hplen)
+ return -1;
+
+ /* We need to figure if hbuf contains a hostname, an
+ * IPv4 address or an IPv6 address:
+ * 2 ':' => IPv6 address; else IPv4 address or hostname.
+ */
+ if ((p1 = strchr(hbuf, ':')) && (p1 = strchr(hbuf, ':'))) {
+ /* 2 or more ':'... it is an IPv6 address */
+ snprintf(hp, hplen, "[%s]%s%s", hbuf,
+ sbuflen ? ":" : "", sbuflen ? sbuf : "");
+ } else {
+ snprintf(hp, hplen, "%s%s%s", hbuf,
+ sbuflen ? ":" : "", sbuflen ? sbuf : "");
+ }
+
+ return 0;
+}
+
+/*! \brief get port and addr from hostport string
+ * \param hbuf output host string. should be memory allocated (NI_MAXHOST)
+ * \param hbuflen length of output host string. should be NI_MAXHOST
+ * \param sbuf output port string. should be memory allocated (NI_MAXSERV)
+ * \param sbuflen length of output port string. should be NI_MAXSERV
+ * \param hostport string URI or host:port combination to be read
+ * \param numeric If set, only a numeric host will be valid.
+ * \param end Pointer to the first character after the address or port
+ * \note supported formats for string: 1.2.3.4, 1.2.3.4:1234, [2001:db8::1],
+ * [2001:db8::1]:1234, 1.2.3.4;extensions, etc. IPv6 addresses
+ * must always be between brackets
+ */
+int ast_vinetsock_str2hp(char *hbuf, size_t hbuflen, char *sbuf, size_t sbuflen,
+ const char *hostport, int numeric, const char** end)
+{
+ char *p1, *head, *p2;
+ size_t len = 0;
+ struct addrinfo hints, *res;
+ char *string = ast_strdupa(hostport);
+
+ head = string;
+
+ if (!string || !hbuf || !hbuflen || !sbuf || !sbuflen)
+ return -1;
+
+ memset(hbuf, 0, hbuflen);
+ memset(sbuf, 0, sbuflen);
+
+ if ('[' == string[0]) {
+ /* IPv6 address between brackets */
+ if (!(p1 = strchr(++string, ']'))) {
+ /* Unclosed bracket */
+ if (option_verbose > 1)
+ ast_log(LOG_WARNING,"missing ] of %s\n", hostport);
+ return -1;
+ }
+ len = p1++ - string;
+ /* [2001:db8::1]:1234;extensions\0
+ * |
+ * p1
+ */
+ } else {
+ /* It must be an IPv4 address or IPv6 addr or hostname */
+ if ((p1 = strchr(string, ':'))) {
+ /* either an ipv4add or hostname with port number of v6 address) */
+ len = p1 - string;
+ /* 1.2.3.4:1234;extensions\0
+ * |
+ * p1
+ */
+ p2 = p1 + 1;
+ if ((p2 = strchr(p2, ':'))) {
+ /* two ':' it must be an ipv6 address (without [], so no port number) */
+ len = strlen(string);
+ p1 = string + len;
+ /* 3ffe:b00::1\0
+ * |
+ * p1
+ */
+ }
+ } else {
+ /* no port number, not an ipv6 address */
+ if ((p1 = strchr(string, ';'))) {
+ /* 1.2.3.4;extensions\0
+ * |
+ * p1
+ */
+ len = p1 - string;
+ }
+ len = strlen(string);
+ p1 = string + len;
+ /* 1.2.3.4\0
+ * |
+ * p1
+ */
+ }
+ }
+ if(len >= hbuflen) {
+ /* Length exceeded */
+ if (option_verbose > 1)
+ ast_log(LOG_WARNING, "len '%d' is larger or equal to hbuflen '%d' in %s\n", (int)len, (int)hbuflen, hostport);
+ return -1;
+ }
+ ast_copy_string(hbuf, string, len + 1);
+ if (':' == p1[0] && sbuf && sbuflen) {
+ memset(sbuf, 0, sbuflen);
+ /* port delimiter */
+ string = ++p1;
+ int portno = strtol(string, &p1, 10);
+ /* 1.2.3.4:1234;extensions\0
+ * |
+ * p1
+ */
+ if((p1[0] != '\0' && p1[0] != ';') ||
+ !ast_vinetsock_isvalid_port(portno)) {
+ /* Invalid characters after the port number
+ * or invalid port number */
+ if (option_verbose > 1)
+ ast_log(LOG_WARNING, "invalid characters '%s' after port no '%d' in %s\n", p1, portno, hostport);
+ return -1;
+ }
+ if (p1 - string >= sbuflen) {
+ /* Length exceeded */
+ if (option_verbose > 1)
+ ast_log(LOG_WARNING, "string '%d' >= sbuflen '%d' in %s\n", p1-string, (int)sbuflen, hostport);
+ return -1;
+ }
+ ast_copy_string(sbuf, string, p1 - string + 1);
+ }
+ if (end)
+ *end = hostport + (p1 - head);
+
+ if (numeric) {
+ /* Validate the host string is a valid address with getaddrinfo
+ * We use port '80' and socktype STREAM to make sure a single
+ * addrinfo struct is returned. This will speed-up
+ * the call to freeaddrinfo later.
+ */
+ int error;
+ memset(&hints, 0, sizeof(hints));
+ hints.ai_flags = AI_NUMERICHOST;
+ hints.ai_socktype = SOCK_STREAM;
+ if ((error = getaddrinfo(hbuf, "80", &hints, &res))) {
+ /* Invalid numeric address */
+ if (option_verbose > 1)
+ ast_log(LOG_WARNING, "getaddrinfo on %s: %s\n", hostport, gai_strerror(error));
+ return -1;
+ }
+ freeaddrinfo(res);
+ }
+ return 0;
+}
+
+/*! \brief Returns the first sockaddr to which a connection can be established
+ * \param host String containing the host name
+ * \param port String containing the port number
+ * \param stype Socket type (SOCK_DGRAM or SOCK_STREAM)
+ * \param sa Sockaddr pointer to store the result in
+ * \param salen Length of the sockaddr struct
+ * \return 0 on success, >0 otherwise
+ * \note only works for TCP, not UDP. Uses getaddrinfo with the hostname
+ * and port and iterate through the received list of candidate sockaddr
+ * and try to connect. first established is returned.
+ */
+int ast_vinetsock_safromhost(char* host, char* port, int stype,
+ struct sockaddr* sa, socklen_t *salen)
+{
+ int error = 0;
+ struct addrinfo hints, *res = NULL, *res0 = NULL;
+ int sockfd = -1;
+
+ if (!host || !port)
+ return 1;
+
+ memset(&hints, 0, sizeof(hints));
+ hints.ai_socktype = stype;
+ hints.ai_family = AF_UNSPEC;
+ error = getaddrinfo(host, port, &hints, &res0);
+ if (error) {
+ ast_log(LOG_WARNING, "No such host %s, port %s: %s\n",
+ host, port, gai_strerror(error));
+ return 1;
+ }
+ for (res = res0; res; res = res->ai_next) {
+ sockfd = socket(res->ai_family, res->ai_socktype,
+ res->ai_protocol);
+ if (sockfd < 0)
+ continue;
+ if (connect(sockfd, res->ai_addr, res->ai_addrlen) < 0) {
+ close(sockfd);
+ sockfd = -1;
+ continue;
+ }
+
+ /* If we reach this point, we found an address */
+ break;
+ }
+
+ if (sockfd < 0) {
+ ast_log(LOG_WARNING, "Could not resolve sucessfully %s, port %s\n", host, port);
+ freeaddrinfo(res0);
+ return 1;
+ }
+ memcpy(sa, res->ai_addr, res->ai_addrlen);
+ *salen = res->ai_addrlen;
+ freeaddrinfo(res0);
+
+ return 0;
+}
+
+/*! \brief Select the most appropriate source address to
+ * destination received in parameter
+ * \param dst Pointer to sockaddr containing the peer's address
+ * \param dstlen length of sa_dst
+ * \param src Pointer to sockaddr that will contain the
+ * source address
+ * \param srclen length of sa_src
+ * \param socktype Socket type(SOCK_DGRAM or SOCK_STREAM)
+ * \return 0 on success or <0 otherwise
+ */
+int ast_vinetsock_sa_getsrc(struct sockaddr* dst, socklen_t dstlen,
+ struct sockaddr* src, socklen_t *srclen,
+ int socktype)
+{
+ int sockfd = -1;
+ char iabuf[NI_MAXHOST];
+ char sbuf[NI_MAXSERV];
+
+ if (!dst || !src)
+ return -1;
+
+ sockfd = socket(dst->sa_family, socktype, 0);
+ if (sockfd < 0) {
+ getnameinfo(dst, dstlen, iabuf, sizeof(iabuf),
+ sbuf, sizeof(sbuf), NI_NUMERICHOST|NI_NUMERICSERV);
+ ast_log(LOG_WARNING, "Unable to create network socket to %s port %s: %s\n", iabuf, sbuf, strerror(errno));
+ return -1;
+ }
+ if (connect(sockfd, dst, dstlen)) {
+ getnameinfo(dst, dstlen, iabuf, sizeof(iabuf),
+ sbuf, sizeof(sbuf), NI_NUMERICHOST|NI_NUMERICSERV);
+ ast_log(LOG_WARNING, "Unable to connect to %s port %s: %s\n", iabuf, sbuf, strerror(errno));
+ close(sockfd);
+ return -1;
+ }
+ *srclen = sizeof(struct sockaddr_storage);
+ if (getsockname(sockfd, src, srclen)) {
+ getnameinfo(src, (int)srclen, iabuf, sizeof(iabuf),
+ sbuf, sizeof(sbuf), NI_NUMERICHOST|NI_NUMERICSERV);
+ ast_log(LOG_WARNING, "Cannot get socket name %s port %s: %s\n", iabuf, sbuf, strerror(errno));
+ close(sockfd);
+ return -1;
+ }
+ close(sockfd);
+ return 0;
+}
+
+/*! \brief Get the default source address from a sockaddr
+ * \param dst Pointer to sockaddr containing the peer's address
+ * \param dstlen length of dst sockaddr
+ * \param src Pointer to sockaddr that will contain the
+ * source address
+ * \param srclen length of src sockaddr
+ * \return 0 on success or <0 otherwise
+ */
+int ast_vinetsock_sa_getsrc_default(struct sockaddr* dst, socklen_t dstlen,
+ struct sockaddr* src, socklen_t *srclen,
+ int portno)
+{
+ struct addrinfo hints, *res = NULL;
+ char port[NI_MAXSERV];
+
+ if (!dst || !src)
+ return -1;
+
+ snprintf(port, sizeof(port), "%d", portno);
+
+ memset(&hints, 0, sizeof(hints));
+ hints.ai_flags = AI_PASSIVE;
+ hints.ai_family = dst->sa_family;
+ hints.ai_socktype = SOCK_DGRAM;
+ if (getaddrinfo(NULL, port, &hints, &res)) {
+ return -1;
+ }
+
+ memcpy(src, res->ai_addr, res->ai_addrlen);
+ *srclen = res->ai_addrlen;
+ freeaddrinfo(res);
+ return 0;
+}
+
+/*! \brief Set the port number in a sockaddr struct
+ * \param sa Pointer to sockaddr struct
+ * \param portno Port number in host order
+ * \return 0 if success, >0 otherwise
+ */
+int ast_vinetsock_sa_setport(const struct sockaddr *sa, const int portno)
+{
+
+ if (!sa || !ast_vinetsock_isvalid_port(portno))
+ return -1;
+
+ switch(sa->sa_family) {
+ case AF_INET:
+ ((struct sockaddr_in*)sa)->sin_port = htons(portno);
+ return 0;
+ break;
+ case AF_INET6:
+ ((struct sockaddr_in6*)sa)->sin6_port = htons(portno);
+ return 0;
+ break;
+ }
+ return 0;
+}
+
+/*! \brief Return true if sockaddr contains an IPv4 address
+ * \param sa pointer to sockaddr to be tested
+ * \return 1 if sockaddr is IPv4 or 0 otherwise
+ */
+int ast_vinetsock_sa_is_ipv4(const struct sockaddr* sa)
+{
+ if (sa == NULL) return 0;
+ return (AF_INET == sa->sa_family);
+}
+
+/*! \brief Return true if sockaddr contains an IPv6 address
+ * \param sa pointer to sockaddr to be tested
+ * \return 1 if sockaddr is IPv6 or 0 otherwise
+ */
+int ast_vinetsock_sa_is_ipv6(const struct sockaddr* sa)
+{
+ if (sa == NULL) return 0;
+ return (AF_INET6 == sa->sa_family);
+}
+
+/*! \brief Return true if sockaddr contains a wildcard address
+ * \param sa pointer to sockaddr to be tested
+ * \return 1 if sockaddr is wildcard or 0 otherwise
+ * \note port number is ignored
+ */
+int ast_vinetsock_sa_is_wildcard(const struct sockaddr* sa)
+{
+ if (sa == NULL) return 0;
+ switch(sa->sa_family) {
+ case AF_INET:
+ if (((struct sockaddr_in*)sa)->sin_addr.s_addr == INADDR_ANY)
+ return 1;
+ break;
+ case AF_INET6:
+ return IN6_IS_ADDR_UNSPECIFIED(&((struct sockaddr_in6*)sa)->sin6_addr);
+ break;
+ }
+ return 0;
+}
+
+/*! \brief Return true if ast_vinetsock contains an IPv4 address
+ * \param ns pointer to vinetsock object to be tested
+ * \return 1 if vinetsock is IPv4 or 0 otherwise
+ */
+int ast_vinetsock_is_ipv4(const struct ast_vinetsock* ns)
+{
+ if (ns == NULL) return 0;
+ return ast_vinetsock_sa_is_ipv4((struct sockaddr*)&ns->bindaddr);
+}
+
+/*! \brief Return true if ast_vinetsock contains an IPv6 address
+ * \param ns pointer to vinetsock object to be tested
+ * \return 1 if vinetsock is IPv6 or 0 otherwise
+ */
+int ast_vinetsock_is_ipv6(const struct ast_vinetsock* ns)
+{
+ if (ns == NULL) return 0;
+ return ast_vinetsock_sa_is_ipv6((struct sockaddr*)&ns->bindaddr);
+}
+
+/*! \brief Return true if ast_vinetsock contains a wildcard address
+ * \param ns pointer to vinetsock to be tested
+ * \return 1 if sockaddr is wildcard or 0 otherwise
+ */
+int ast_vinetsock_is_wildcard(const struct ast_vinetsock* ns)
+{
+ if (ns == NULL) return 0;
+ return ast_vinetsock_sa_is_wildcard((struct sockaddr*)&ns->bindaddr);
+}
+
+
+/*! \briefCompares the source address and port of two sockaddr_storage
+ * or only the address
+ * \param ignore_port If true, ignore the ports in the comparison
+ * \return 0 if sockaddr are equals, <0 or >0 otherwise
+ */
+int ast_vinetsock_sacmp(const struct sockaddr *sa1,
+ socklen_t sa1len,
+ const struct sockaddr *sa2,
+ socklen_t sa2len,
+ int ignore_port)
+{
+ int result = 1;
+ char hbuf1[NI_MAXHOST];
+ char hbuf2[NI_MAXHOST];
+ char sbuf1[NI_MAXSERV];
+ char sbuf2[NI_MAXSERV];
+
+ if (NULL == sa1 || NULL == sa2)
+ return 1;
+
+ /* Convert the addresses to string and compare the strings
+ * to make sure to take the scope into account
+ */
+ if (getnameinfo(sa1, sa1len, hbuf1, sizeof(hbuf1), sbuf1, sizeof(sbuf1),
+ NI_NUMERICHOST|NI_NUMERICSERV))
+ return 1;
+ if (getnameinfo(sa2, sa2len, hbuf2, sizeof(hbuf2), sbuf2, sizeof(sbuf2),
+ NI_NUMERICHOST|NI_NUMERICSERV))
+ return 1;
+
+ result = strncmp(hbuf1, hbuf2, sizeof(hbuf1));
+ if (0 == result && !ignore_port)
+ result = strncmp(sbuf1, sbuf2, sizeof(sbuf1));
+ return result;
+}
+
+/*! \briefCompares the ports of two sockaddr
+ * \param sa1 pointer to the first sockaddr to compare
+ * \param sa1len length of the first sockaddr
+ * \param sa2 pointer to the second sockaddr to compare
+ * \param sa2len length of the second sockaddr
+ * \return 0 if ports are equals, <0 or >0 otherwise
+ */
+int ast_vinetsock_sacmp_port(const struct sockaddr *sa1,
+ socklen_t sa1len,
+ const struct sockaddr *sa2,
+ socklen_t sa2len)
+{
+ char sbuf1[NI_MAXSERV];
+ char sbuf2[NI_MAXSERV];
+
+ if (NULL == sa1 || NULL == sa2)
+ return 1;
+
+ if (getnameinfo(sa1, sa1len, NULL, 0, sbuf1, sizeof(sbuf1),
+ NI_NUMERICSERV))
+ return 1;
+ if (getnameinfo(sa2, sa2len, NULL, 0, sbuf2, sizeof(sbuf2),
+ NI_NUMERICSERV))
+ return 1;
+
+ return strncmp(sbuf1, sbuf2, sizeof(sbuf1));
+}
+
+/* below is original netsock code which is IPv4-only */
struct ast_netsock {
ASTOBJ_COMPONENTS(struct ast_netsock);
struct sockaddr_in bindaddr;
More information about the asterisk-commits
mailing list