diff options
Diffstat (limited to 'src/sock.c')
-rw-r--r-- | src/sock.c | 138 |
1 files changed, 100 insertions, 38 deletions
@@ -33,6 +33,19 @@ #include "sock.h" #include "text.h" #include "conf.h" +#include "loop.h" +#include "sblist.h" + +/* + * Return a human readable error for getaddrinfo() and getnameinfo(). + */ +static const char * get_gai_error (int n) +{ + if (n == EAI_SYSTEM) + return strerror (errno); + else + return gai_strerror (n); +} /* * Bind the given socket to the supplied address. The socket is @@ -43,6 +56,7 @@ static int bind_socket (int sockfd, const char *addr, int family) { struct addrinfo hints, *res, *ressave; + int n; assert (sockfd >= 0); assert (addr != NULL && strlen (addr) != 0); @@ -51,9 +65,13 @@ bind_socket (int sockfd, const char *addr, int family) hints.ai_family = family; hints.ai_socktype = SOCK_STREAM; - /* The local port it not important */ - if (getaddrinfo (addr, NULL, &hints, &res) != 0) + /* The local port is not important */ + n = getaddrinfo (addr, NULL, &hints, &res); + if (n != 0) { + log_message (LOG_INFO, + "bind_socket: getaddrinfo failed for %s: %s", addr, get_gai_error (n)); return -1; + } ressave = res; @@ -70,12 +88,32 @@ bind_socket (int sockfd, const char *addr, int family) return sockfd; } +/** + * Try binding the given socket to supplied addresses, stopping when one succeeds. + */ +static int +bind_socket_list (int sockfd, sblist *addresses, int family) +{ + size_t nb_addresses = sblist_getsize(addresses); + size_t i; + + for (i = 0; i < nb_addresses; i++) { + const char *address = *(const char **)sblist_get(addresses, i); + if (bind_socket(sockfd, address, family) >= 0) { + log_message(LOG_INFO, "Bound to %s", address); + return 0; + } + } + + return -1; +} + /* * Open a connection to a remote host. It's been re-written to use * the getaddrinfo() library function, which allows for a protocol * independent implementation (mostly for IPv4 and IPv6 addresses.) */ -int opensock (const char *host, int port, const char *bind_to) +int opensock (const char *host, int port, const char *bind_to, const char *bind_to_alt) { int sockfd, n; struct addrinfo hints, *res, *ressave; @@ -86,6 +124,10 @@ int opensock (const char *host, int port, const char *bind_to) log_message(LOG_INFO, "opensock: opening connection to %s:%d", host, port); + if (bind_to) { + log_message(LOG_INFO, + "opensock: bind to %s or %s", bind_to, bind_to_alt); + } memset (&hints, 0, sizeof (struct addrinfo)); hints.ai_family = AF_UNSPEC; @@ -96,7 +138,7 @@ int opensock (const char *host, int port, const char *bind_to) n = getaddrinfo (host, portstr, &hints, &res); if (n != 0) { log_message (LOG_ERR, - "opensock: Could not retrieve info for %s", host); + "opensock: Could not retrieve address info for %s:%d: %s", host, port, get_gai_error (n)); return -1; } @@ -113,20 +155,32 @@ int opensock (const char *host, int port, const char *bind_to) /* Bind to the specified address */ if (bind_to) { if (bind_socket (sockfd, bind_to, - res->ai_family) < 0) { + res->ai_family) < 0 && + (!bind_to_alt || + bind_socket (sockfd, bind_to_alt, + res->ai_family) < 0)) { close (sockfd); continue; /* can't bind, so try again */ } - } else if (config.bind_address) { - if (bind_socket (sockfd, config.bind_address, - res->ai_family) < 0) { + } else if (config->bind_addrs) { + if (bind_socket_list (sockfd, config->bind_addrs, + res->ai_family) < 0) { close (sockfd); continue; /* can't bind, so try again */ } } - if (connect (sockfd, res->ai_addr, res->ai_addrlen) == 0) + if (connect (sockfd, res->ai_addr, res->ai_addrlen) == 0) { + union sockaddr_union *p = (void*) res->ai_addr, u; + int af = res->ai_addr->sa_family; + unsigned dport = ntohs(af == AF_INET ? p->v4.sin_port : p->v6.sin6_port); + socklen_t slen = sizeof u; + if (dport == config->port) { + getsockname(sockfd, (void*)&u, &slen); + loop_records_add(&u); + } break; /* success */ + } close (sockfd); } while ((res = res->ai_next) != NULL); @@ -134,8 +188,9 @@ int opensock (const char *host, int port, const char *bind_to) freeaddrinfo (ressave); if (res == NULL) { log_message (LOG_ERR, - "opensock: Could not establish a connection to %s", - host); + "opensock: Could not establish a connection to %s:%d", + host, + port); return -1; } @@ -186,8 +241,7 @@ static int listen_on_one_socket(struct addrinfo *ad) ret = getnameinfo(ad->ai_addr, ad->ai_addrlen, numerichost, NI_MAXHOST, NULL, 0, flags); if (ret != 0) { - log_message(LOG_ERR, "error calling getnameinfo: %s", - gai_strerror(errno)); + log_message(LOG_ERR, "getnameinfo failed: %s", get_gai_error (ret)); return -1; } @@ -251,11 +305,12 @@ static int listen_on_one_socket(struct addrinfo *ad) * Upon success, the listen-fds are added to the listen_fds list * and 0 is returned. Upon error, -1 is returned. */ -int listen_sock (const char *addr, uint16_t port, vector_t listen_fds) +int listen_sock (const char *addr, uint16_t port, sblist* listen_fds) { struct addrinfo hints, *result, *rp; char portstr[6]; int ret = -1; + int n; assert (port > 0); assert (listen_fds != NULL); @@ -270,10 +325,13 @@ int listen_sock (const char *addr, uint16_t port, vector_t listen_fds) snprintf (portstr, sizeof (portstr), "%d", port); - if (getaddrinfo (addr, portstr, &hints, &result) != 0) { + n = getaddrinfo (addr, portstr, &hints, &result); + if (n != 0) { log_message (LOG_ERR, - "Unable to getaddrinfo() because of %s", - strerror (errno)); + "Unable to getaddrinfo() for %s:%d because of %s", + addr, + port, + get_gai_error (n)); return -1; } @@ -285,7 +343,7 @@ int listen_sock (const char *addr, uint16_t port, vector_t listen_fds) continue; } - vector_append (listen_fds, &listenfd, sizeof(int)); + sblist_add (listen_fds, &listenfd); /* success */ ret = 0; @@ -331,30 +389,34 @@ int getsock_ip (int fd, char *ipaddr) return 0; } -/* - * Return the peer's socket information. - */ -int getpeer_information (int fd, char *ipaddr, char *string_addr) +int getmapped_ipv4 (const char *ipv6mapped_conf, const char *ipaddr, char *ipaddr_mapped) { - struct sockaddr_storage sa; - socklen_t salen = sizeof sa; + struct in6_addr addr6; + struct in_addr addr; - assert (fd >= 0); - assert (ipaddr != NULL); - assert (string_addr != NULL); + memset(&addr6, 0, sizeof(addr6)); + if ( inet_pton(AF_INET6, ipaddr, &addr6) < 0 ) + return -1; - /* Set the strings to default values */ - ipaddr[0] = '\0'; - strlcpy (string_addr, "[unknown]", HOSTNAME_LENGTH); + memcpy(&addr, addr6.s6_addr + 12, sizeof(addr)); + return inet_ntop(AF_INET, &addr, ipaddr_mapped, IP_LENGTH) == NULL ? -1 : 0; +} - /* Look up the IP address */ - if (getpeername (fd, (struct sockaddr *) &sa, &salen) != 0) - return -1; +int getmapped_ipv6 (const char *ipv4mapped_conf, const char *ipaddr, char *ipaddr_mapped) +{ + memset(ipaddr_mapped, 0, IP_LENGTH); + strncpy(ipaddr_mapped, ipv4mapped_conf, IP_LENGTH-1); + strncat(ipaddr_mapped, ipaddr, IP_LENGTH-1-strlen(ipaddr_mapped)); + return 0; +} - if (get_ip_string ((struct sockaddr *) &sa, ipaddr, IP_LENGTH) == NULL) - return -1; - /* Get the full host name */ - return getnameinfo ((struct sockaddr *) &sa, salen, - string_addr, HOSTNAME_LENGTH, NULL, 0, 0); +/* + * Return the peer's socket information. + */ +void getpeer_information (union sockaddr_union* addr, char *ipaddr, size_t ipaddr_len) +{ + int af = addr->v4.sin_family; + void *ipdata = af == AF_INET ? (void*)&addr->v4.sin_addr : (void*)&addr->v6.sin6_addr; + inet_ntop(af, ipdata, ipaddr, ipaddr_len); } |