[svn-commits] blanchet: branch blanchet/v6 r58478 - /team/blanchet/v6/main/netsock.c

svn-commits at lists.digium.com svn-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 svn-commits mailing list