summaryrefslogtreecommitdiffhomepage
path: root/src/sock.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/sock.c')
-rw-r--r--src/sock.c138
1 files changed, 100 insertions, 38 deletions
diff --git a/src/sock.c b/src/sock.c
index eef606a..7aa43e2 100644
--- a/src/sock.c
+++ b/src/sock.c
@@ -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);
}