From 8cc2aee8859731ee2fe280070423f79e7d009ca3 Mon Sep 17 00:00:00 2001 From: Vsevolod Stakhov Date: Fri, 31 May 2013 17:45:18 +0100 Subject: [PATCH] Use getaddrinfo when opening sockets. --- src/lua/lua_http.c | 4 +- src/map.c | 35 ++++++---- src/map.h | 2 +- src/util.c | 167 ++++++++++++++++++++++----------------------- src/util.h | 4 +- 5 files changed, 108 insertions(+), 104 deletions(-) diff --git a/src/lua/lua_http.c b/src/lua/lua_http.c index 73d916244..8fc5c3e2c 100644 --- a/src/lua/lua_http.c +++ b/src/lua/lua_http.c @@ -302,7 +302,7 @@ lua_http_dns_callback (struct rspamd_dns_reply *reply, gpointer arg) elt = reply->elements->data; memcpy (&ina, &elt->a.addr[0], sizeof (struct in_addr)); - ud->fd = make_tcp_socket (&ina, ud->port, FALSE, TRUE); + ud->fd = make_universal_stream_socket (inet_ntoa (ina), ud->port, TRUE, FALSE, FALSE); if (ud->fd == -1) { lua_http_push_error (450, ud); @@ -473,7 +473,7 @@ lua_http_make_request_common_new (lua_State *L, struct rspamd_async_session *ses return 1; } - ud->fd = make_tcp_socket (&ina, ud->port, FALSE, TRUE); + ud->fd = make_universal_stream_socket (inet_ntoa (ina), ud->port, TRUE, FALSE, FALSE); if (ud->fd == -1) { luaL_unref (L, LUA_REGISTRYINDEX, cbref); diff --git a/src/map.c b/src/map.c index 912604118..fde6a3de9 100644 --- a/src/map.c +++ b/src/map.c @@ -68,7 +68,7 @@ connect_http (struct rspamd_map *map, struct http_map_data *data, gboolean is_as { gint sock; - if ((sock = make_tcp_socket (&data->addr, data->port, FALSE, is_async)) == -1) { + if ((sock = make_tcp_socket (data->addr, FALSE, is_async)) == -1) { msg_info ("cannot connect to http server %s: %d, %s", data->host, errno, strerror (errno)); return -1; } @@ -1013,8 +1013,8 @@ add_map (struct config_file *cfg, const gchar *map_line, const gchar *descriptio struct file_map_data *fdata; struct http_map_data *hdata; gchar portbuf[6]; - gint i, s; - struct hostent *hent; + gint i, s, r; + struct addrinfo hints, *res; /* First of all detect protocol line */ if (!check_map_proto (map_line, (int *)&proto, &def)) { @@ -1075,6 +1075,7 @@ add_map (struct config_file *cfg, const gchar *map_line, const gchar *descriptio } else { /* Default http port */ + rspamd_snprintf (portbuf, sizeof (portbuf), "80"); hdata->port = 80; /* Now separate host from path */ if ((p = strchr (def, '/')) == NULL) { @@ -1088,19 +1089,25 @@ add_map (struct config_file *cfg, const gchar *map_line, const gchar *descriptio hdata->path = memory_pool_strdup (cfg->map_pool, p); hdata->rlen = 0; /* Now try to resolve */ - if (!inet_aton (hdata->host, &hdata->addr)) { - /* Resolve using dns */ - hent = gethostbyname (hdata->host); - if (hent == NULL) { - msg_info ("cannot resolve: %s", hdata->host); - return FALSE; - } - else { - memcpy (&hdata->addr, hent->h_addr, sizeof (struct in_addr)); - } + memset (&hints, 0, sizeof (hints)); + hints.ai_family = AF_UNSPEC; /* Allow IPv4 or IPv6 */ + hints.ai_socktype = SOCK_STREAM; /* Stream socket */ + hints.ai_flags = 0; + hints.ai_protocol = 0; /* Any protocol */ + hints.ai_canonname = NULL; + hints.ai_addr = NULL; + hints.ai_next = NULL; + + if ((r = getaddrinfo (hdata->host, portbuf, &hints, &res)) == 0) { + hdata->addr = res; + memory_pool_add_destructor (cfg->cfg_pool, (pool_destruct_func)freeaddrinfo, hdata->addr); + } + else { + msg_err ("address resolution for %s failed: %s", hdata->host, gai_strerror (r)); + return FALSE; } /* Now try to connect */ - if ((s = make_tcp_socket (&hdata->addr, hdata->port, FALSE, FALSE)) == -1) { + if ((s = make_tcp_socket (hdata->addr, FALSE, FALSE)) == -1) { msg_info ("cannot connect to http server %s: %d, %s", hdata->host, errno, strerror (errno)); return FALSE; } diff --git a/src/map.h b/src/map.h index 8623e6845..ec35f8486 100644 --- a/src/map.h +++ b/src/map.h @@ -37,7 +37,7 @@ struct file_map_data { * Data specific to HTTP maps */ struct http_map_data { - struct in_addr addr; + struct addrinfo *addr; guint16 port; gchar *path; gchar *host; diff --git a/src/util.c b/src/util.c index 9c4d8bf4c..8d7bf69ad 100644 --- a/src/util.c +++ b/src/util.c @@ -87,94 +87,88 @@ poll_sync_socket (gint fd, gint timeout, short events) } static gint -make_inet_socket (gint family, struct in_addr *addr, u_short port, gboolean is_server, gboolean async) +make_inet_socket (gint family, struct addrinfo *addr, gboolean is_server, gboolean async) { gint fd, r, optlen, on = 1, s_error; - gint serrno; - struct sockaddr_in sin; - - /* Create socket */ - fd = socket (AF_INET, family, 0); - if (fd == -1) { - msg_warn ("socket failed: %d, '%s'", errno, strerror (errno)); - return -1; - } - - if (make_socket_nonblocking (fd) < 0) { - goto out; - } - - /* Set close on exec */ - if (fcntl (fd, F_SETFD, FD_CLOEXEC) == -1) { - msg_warn ("fcntl failed: %d, '%s'", errno, strerror (errno)); - goto out; - } - - memset (&sin, 0, sizeof (sin)); - - /* Bind options */ - sin.sin_family = AF_INET; - sin.sin_port = htons (port); - sin.sin_addr.s_addr = addr->s_addr; + struct addrinfo *cur; + + cur = addr; + while (cur) { + /* Create socket */ + fd = socket (cur->ai_protocol, family, 0); + if (fd == -1) { + msg_warn ("socket failed: %d, '%s'", errno, strerror (errno)); + goto out; + } - if (is_server) { - setsockopt (fd, SOL_SOCKET, SO_REUSEADDR, (const void *)&on, sizeof (gint)); - r = bind (fd, (struct sockaddr *)&sin, sizeof (struct sockaddr_in)); - } - else { - r = connect (fd, (struct sockaddr *)&sin, sizeof (struct sockaddr_in)); - } + if (make_socket_nonblocking (fd) < 0) { + goto out; + } - if (r == -1) { - if (errno != EINPROGRESS) { - msg_warn ("bind/connect failed: %d, '%s'", errno, strerror (errno)); + /* Set close on exec */ + if (fcntl (fd, F_SETFD, FD_CLOEXEC) == -1) { + msg_warn ("fcntl failed: %d, '%s'", errno, strerror (errno)); goto out; } - if (!async) { - /* Try to poll */ - if (poll_sync_socket (fd, CONNECT_TIMEOUT * 1000, POLLOUT) <= 0) { - errno = ETIMEDOUT; - msg_warn ("bind/connect failed: timeout"); + + if (is_server) { + setsockopt (fd, SOL_SOCKET, SO_REUSEADDR, (const void *)&on, sizeof (gint)); + r = bind (fd, cur->ai_addr, cur->ai_addrlen); + } + else { + r = connect (fd, cur->ai_addr, cur->ai_addrlen); + } + + if (r == -1) { + if (errno != EINPROGRESS) { + msg_warn ("bind/connect failed: %d, '%s'", errno, strerror (errno)); goto out; } - else { - /* Make synced again */ - if (make_socket_blocking (fd) < 0) { + if (!async) { + /* Try to poll */ + if (poll_sync_socket (fd, CONNECT_TIMEOUT * 1000, POLLOUT) <= 0) { + errno = ETIMEDOUT; + msg_warn ("bind/connect failed: timeout"); goto out; } + else { + /* Make synced again */ + if (make_socket_blocking (fd) < 0) { + goto out; + } + } } } - } - else { - /* Still need to check SO_ERROR on socket */ - optlen = sizeof (s_error); - getsockopt (fd, SOL_SOCKET, SO_ERROR, (void *)&s_error, &optlen); - if (s_error) { - errno = s_error; - goto out; + else { + /* Still need to check SO_ERROR on socket */ + optlen = sizeof (s_error); + getsockopt (fd, SOL_SOCKET, SO_ERROR, (void *)&s_error, &optlen); + if (s_error) { + errno = s_error; + goto out; + } + } + break; +out: + if (fd != -1) { + close (fd); } + fd = -1; + cur = cur->ai_next; } - - return (fd); - - out: - serrno = errno; - close (fd); - errno = serrno; - return (-1); } gint -make_tcp_socket (struct in_addr *addr, u_short port, gboolean is_server, gboolean async) +make_tcp_socket (struct addrinfo *addr, gboolean is_server, gboolean async) { - return make_inet_socket (SOCK_STREAM, addr, port, is_server, async); + return make_inet_socket (SOCK_STREAM, addr, is_server, async); } gint -make_udp_socket (struct in_addr *addr, u_short port, gboolean is_server, gboolean async) +make_udp_socket (struct addrinfo *addr, gboolean is_server, gboolean async) { - return make_inet_socket (SOCK_DGRAM, addr, port, is_server, async); + return make_inet_socket (SOCK_DGRAM, addr, is_server, async); } gint @@ -304,9 +298,9 @@ make_universal_stream_socket (const gchar *credits, guint16 port, gboolean async { struct sockaddr_un un; struct stat st; - struct in_addr in; - struct hostent *he; - gint r; + struct addrinfo hints, *res; + gint r; + gchar portbuf[8]; if (*credits == '/') { r = stat (credits, &st); @@ -340,25 +334,28 @@ make_universal_stream_socket (const gchar *credits, guint16 port, gboolean async } else { /* TCP related part */ - if (inet_aton (credits, &in) == 0) { - /* Try to resolve */ - if (try_resolve) { - if ((he = gethostbyname (credits)) == NULL) { - errno = ENOENT; - return -1; - } - else { - memcpy (&in, he->h_addr, sizeof (struct in_addr)); - return make_tcp_socket (&in, port, is_server, async); - } - } - else { - errno = ENOENT; - return -1; - } + memset (&hints, 0, sizeof (hints)); + hints.ai_family = AF_UNSPEC; /* Allow IPv4 or IPv6 */ + hints.ai_socktype = SOCK_STREAM; /* Stream socket */ + hints.ai_flags = is_server ? AI_PASSIVE : 0; + hints.ai_protocol = 0; /* Any protocol */ + hints.ai_canonname = NULL; + hints.ai_addr = NULL; + hints.ai_next = NULL; + + if (!try_resolve) { + hints.ai_flags |= AI_NUMERICHOST | AI_NUMERICSERV; + } + + rspamd_snprintf (portbuf, sizeof (portbuf), "%d", (int)port); + if ((r = getaddrinfo (credits, portbuf, &hints, &res)) == 0) { + r = make_tcp_socket (res, is_server, async); + freeaddrinfo (res); + return r; } else { - return make_tcp_socket (&in, port, is_server, async); + msg_err ("address resolution for %s failed: %s", credits, gai_strerror (r)); + return FALSE; } } } diff --git a/src/util.h b/src/util.h index 6bec21f46..3dfa851bc 100644 --- a/src/util.h +++ b/src/util.h @@ -17,11 +17,11 @@ struct classifier_config; /* * Create socket and bind or connect it to specified address and port */ -gint make_tcp_socket (struct in_addr *, u_short, gboolean is_server, gboolean async); +gint make_tcp_socket (struct addrinfo *, gboolean is_server, gboolean async); /* * Create socket and bind or connect it to specified address and port */ -gint make_udp_socket (struct in_addr *, u_short, gboolean is_server, gboolean async); +gint make_udp_socket (struct addrinfo *, gboolean is_server, gboolean async); /* * Accept from socket */ -- 2.39.5