X-Git-Url: http://git.tuebingen.mpg.de/?p=paraslash.git;a=blobdiff_plain;f=net.c;h=c11f67c4bac3f7f316daa317ae71ed8ebe6f10a9;hp=caf84c90f0680538140bc77b840befb2ec8cd9c8;hb=69a294cd641c623db61f46ee86901845789a1c7b;hpb=de24aa05c1376811be9f7f3c9a3f8c1fea14f13e diff --git a/net.c b/net.c index caf84c90..c11f67c4 100644 --- a/net.c +++ b/net.c @@ -1,5 +1,5 @@ /* - * Copyright (C) 2005-2013 Andre Noll + * Copyright (C) 2005-2014 Andre Noll * * Licensed under the GPL v2. For licencing details see COPYING. */ @@ -12,6 +12,11 @@ */ #define _GNU_SOURCE +#include +#include +#include +#include +#include #include /* At least NetBSD needs these. */ @@ -328,7 +333,14 @@ static void flowopt_setopts(int sockfd, struct flowopts *fo) } } -static void flowopt_cleanup(struct flowopts *fo) +/** + * Deallocate all resources of a flowopts structure. + * + * \param fo A pointer as returned from flowopt_new(). + * + * It's OK to pass \p NULL here in which case the function does nothing. + */ +void flowopt_cleanup(struct flowopts *fo) { struct pre_conn_opt *cur, *next; @@ -344,137 +356,148 @@ static void flowopt_cleanup(struct flowopts *fo) } /** - * Resolve IPv4/IPv6 address and create a ready-to-use active or passive socket. + * Resolve an IPv4/IPv6 address. * * \param l4type The layer-4 type (\p IPPROTO_xxx). - * \param passive Whether this is a passive (1) or active (0) socket. + * \param passive Whether \p AI_PASSIVE should be included as hint. * \param host Remote or local hostname or IPv/6 address string. - * \param port_number Decimal port number. - * \param fo Socket options to be set before making the connection. + * \param port_number Used to set the port in each returned address structure. + * \param result addrinfo structures are returned here. * - * This creates a ready-made IPv4/v6 socket structure after looking up the - * necessary parameters. The interpretation of \a host depends on the value of - * \a passive: - * - on a passive socket host is interpreted as an interface IPv4/6 address - * (can be left NULL); - * - on an active socket, \a host is the peer DNS name or IPv4/6 address - * to connect to; - * - \a port_number is in either case the numeric port number (not service - * string). - * - * Furthermore, bind(2) is called on passive sockets, and connect(2) on active - * sockets. The algorithm tries all possible address combinations until it - * succeeds. If \a fo is supplied, options are set and cleanup is performed. - * - * \return This function returns 1 on success and \a -E_ADDRESS_LOOKUP when no - * matching connection could be set up (with details in the error log). - * - * \sa ipv6(7), getaddrinfo(3), bind(2), connect(2). - */ -int makesock(unsigned l4type, bool passive, - const char *host, uint16_t port_number, - struct flowopts *fo) -{ - struct addrinfo *local = NULL, *src = NULL, *remote = NULL, - *dst = NULL, hints; - unsigned int l3type = AF_UNSPEC; - int rc, on = 1, sockfd = -1, - socktype = sock_type(l4type); + * The interpretation of \a host depends on the value of \a passive. On a + * passive socket host is interpreted as an interface IPv4/6 address (can be + * left NULL). On an active socket, \a host is the peer DNS name or IPv4/6 + * address to connect to. + * + * \return Standard. + * + * \sa getaddrinfo(3). + */ +int lookup_address(unsigned l4type, bool passive, const char *host, + int port_number, struct addrinfo **result) +{ + int ret; char port[6]; /* port number has at most 5 digits */ + struct addrinfo *addr = NULL, hints; - sprintf(port, "%u", port_number); + *result = NULL; + sprintf(port, "%u", port_number & 0xffff); /* Set up address hint structure */ memset(&hints, 0, sizeof(hints)); - hints.ai_family = l3type; - hints.ai_socktype = socktype; - /* + hints.ai_family = AF_UNSPEC; + hints.ai_socktype = sock_type(l4type); + /* * getaddrinfo does not support SOCK_DCCP, so for the sake of lookup * (and only then) pretend to be UDP. */ if (l4type == IPPROTO_DCCP) hints.ai_socktype = SOCK_DGRAM; - /* only use addresses available on the host */ hints.ai_flags = AI_ADDRCONFIG; - if (l3type == AF_INET6) - /* use v4-mapped-v6 if no v6 addresses found */ - hints.ai_flags |= AI_V4MAPPED | AI_ALL; - if (passive && host == NULL) hints.ai_flags |= AI_PASSIVE; - /* Obtain local/remote address information */ - if ((rc = getaddrinfo(host, port, &hints, passive ? &local : &remote))) { - PARA_ERROR_LOG("can not resolve %s address %s#%s: %s.\n", - layer4_name(l4type), - host? host : (passive? "[loopback]" : "[localhost]"), - port, gai_strerror(rc)); - rc = -E_ADDRESS_LOOKUP; - goto out; + ret = getaddrinfo(host, port, &hints, &addr); + if (ret != 0) { + PARA_ERROR_LOG("can not resolve %s address %s#%s: %s\n", + layer4_name(l4type), + host? host : (passive? "[loopback]" : "[localhost]"), + port, gai_strerror(ret)); + return -E_ADDRESS_LOOKUP; } + *result = addr; + return 1; +} - /* Iterate over all src/dst combination, exhausting dst first */ - for (src = local, dst = remote; src != NULL || dst != NULL; /* no op */ ) { - if (src && dst && src->ai_family == AF_INET - && dst->ai_family == AF_INET6) - goto get_next_dst; /* v4 -> v6 is not possible */ - - sockfd = socket(src ? src->ai_family : dst->ai_family, - socktype, l4type); - if (sockfd < 0) - goto get_next_dst; +/** + * Create an active or passive socket. + * + * \param l4type \p IPPROTO_TCP, \p IPPROTO_UDP, or \p IPPROTO_DCCP. + * \param passive Whether to call bind(2) or connect(2). + * \param ai Address information as obtained from \ref lookup_address(). + * \param fo Socket options to be set before making the connection. + * + * bind(2) is called on passive sockets, and connect(2) on active sockets. The + * algorithm tries all possible address combinations until it succeeds. If \a + * fo is supplied, options are set but cleanup must be performed in the caller. + * + * \return File descriptor on success, \p E_MAKESOCK on errors. + * + * \sa \ref lookup_address(), \ref makesock(), ip(7), ipv6(7), bind(2), + * connect(2). + */ +int makesock_addrinfo(unsigned l4type, bool passive, struct addrinfo *ai, + struct flowopts *fo) +{ + int ret = -E_MAKESOCK, on = 1; + for (; ai; ai = ai->ai_next) { + int fd; + ret = socket(ai->ai_family, sock_type(l4type), l4type); + if (ret < 0) + continue; + fd = ret; + flowopt_setopts(fd, fo); + if (!passive) { + if (connect(fd, ai->ai_addr, ai->ai_addrlen) == 0) + return fd; + close(fd); + continue; + } /* * Reuse the address on passive sockets to avoid failure on * restart (protocols using listen()) and when creating * multiple listener instances (UDP multicast). */ - if (passive && setsockopt(sockfd, SOL_SOCKET, SO_REUSEADDR, - &on, sizeof(on)) == -1) { - rc = errno; - close(sockfd); - PARA_ERROR_LOG("can not set SO_REUSEADDR: %s\n", - strerror(rc)); - rc = -ERRNO_TO_PARA_ERROR(rc); - break; - } - flowopt_setopts(sockfd, fo); - - if (src) { - if (bind(sockfd, src->ai_addr, src->ai_addrlen) < 0) { - close(sockfd); - goto get_next_src; - } - if (!dst) /* bind-only completed successfully */ - break; + if (setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &on, + sizeof(on)) == -1) { + close(fd); + continue; } - - if (dst && connect(sockfd, dst->ai_addr, dst->ai_addrlen) == 0) - break; /* connection completed successfully */ - close(sockfd); -get_next_dst: - if (dst && (dst = dst->ai_next)) + if (bind(fd, ai->ai_addr, ai->ai_addrlen) < 0) { + close(fd); continue; -get_next_src: - if (src && (src = src->ai_next)) /* restart inner loop */ - dst = remote; + } + return fd; } -out: - if (local) - freeaddrinfo(local); - if (remote) - freeaddrinfo(remote); - flowopt_cleanup(fo); - - if (src == NULL && dst == NULL) { - if (rc >= 0) - rc = -E_MAKESOCK; - PARA_ERROR_LOG("can not create %s socket %s#%s.\n", - layer4_name(l4type), host? host : (passive? - "[loopback]" : "[localhost]"), port); - return rc; + return -E_MAKESOCK; +} + +/** + * Resolve IPv4/IPv6 address and create a ready-to-use active or passive socket. + * + * \param l4type The layer-4 type (\p IPPROTO_xxx). + * \param passive Whether this is a passive or active socket. + * \param host Passed to \ref lookup_address(). + * \param port_number Passed to \ref lookup_address(). + * \param fo Passed to \ref makesock_addrinfo(). + * + * This creates a ready-made IPv4/v6 socket structure after looking up the + * necessary parameters. The function first calls \ref lookup_address() and + * passes the address information to makesock_addrinfo() to create and + * initialize the socket. + * + * \return The newly created file descriptor on success, a negative error code + * on failure. + * + * \sa \ref lookup_address(), \ref makesock_addrinfo(). + */ +int makesock(unsigned l4type, bool passive, const char *host, uint16_t port_number, + struct flowopts *fo) +{ + struct addrinfo *ai; + int ret = lookup_address(l4type, passive, host, port_number, &ai); + + if (ret >= 0) + ret = makesock_addrinfo(l4type, passive, ai, fo); + if (ai) + freeaddrinfo(ai); + if (ret < 0) { + PARA_ERROR_LOG("can not create %s socket %s#%d.\n", + layer4_name(l4type), host? host : (passive? + "[loopback]" : "[localhost]"), port_number); } - return sockfd; + return ret; } /** @@ -687,6 +710,32 @@ void extract_v4_addr(const struct sockaddr_storage *ss, struct in_addr *ia) *ia = ((struct sockaddr_in *)sa)->sin_addr; } +/** + * Compare the address part of IPv4/6 addresses. + * + * \param sa1 First address. + * \param sa2 Second address. + * + * \return True iff the IP address of \a sa1 and \a sa2 match. + */ +bool sockaddr_equal(const struct sockaddr *sa1, const struct sockaddr *sa2) +{ + if (!sa1 || !sa2) + return false; + if (sa1->sa_family != sa2->sa_family) + return false; + if (sa1->sa_family == AF_INET) { + struct sockaddr_in *a1 = (typeof(a1))sa1, + *a2 = (typeof (a2))sa2; + return a1->sin_addr.s_addr == a2->sin_addr.s_addr; + } else if (sa1->sa_family == AF_INET6) { + struct sockaddr_in6 *a1 = (typeof(a1))sa1, + *a2 = (typeof (a2))sa2; + return !memcmp(a1, a2, sizeof(*a1)); + } else + return false; +} + /** * Receive data from a file descriptor. *