From f3fe3fc7be5a8f3ab944a72f51dbc828d2ad743d Mon Sep 17 00:00:00 2001 From: Adam Dickmeiss Date: Fri, 5 Apr 2013 13:52:35 +0200 Subject: [PATCH] Use getaddrinfo for HTTP listener Allows Pazpar2 to listen on IPV6 addresses if host+port is given for listen element. By default (no host), it still listens on IPV4 only. --- src/getaddrinfo.c | 3 - src/http.c | 162 ++++++++++++++++++++++++++++++----------------------- 2 files changed, 93 insertions(+), 72 deletions(-) diff --git a/src/getaddrinfo.c b/src/getaddrinfo.c index 24ce534..ed8ff70 100644 --- a/src/getaddrinfo.c +++ b/src/getaddrinfo.c @@ -40,9 +40,6 @@ Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA #if HAVE_NETDB_H #include #endif -#if HAVE_NETINET_IN_H -#include -#endif #include #include diff --git a/src/http.c b/src/http.c index 6580381..74fdf66 100644 --- a/src/http.c +++ b/src/http.c @@ -27,7 +27,8 @@ Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA #include #ifdef WIN32 -#include +#include +#include typedef int socklen_t; #endif @@ -54,14 +55,6 @@ typedef int socklen_t; #include #include -#if HAVE_NETINET_IN_H -#include -#endif - -#if HAVE_ARPA_INET_H -#include -#endif - #include #include #include @@ -97,6 +90,12 @@ static void http_channel_destroy(IOCHAN i); static http_server_t http_server_create(void); static void http_server_incref(http_server_t hs); +#ifdef WIN32 +#define CLOSESOCKET(x) closesocket(x) +#else +#define CLOSESOCKET(x) close(x) +#endif + struct http_server { YAZ_MUTEX mutex; @@ -622,6 +621,7 @@ static struct http_buf *http_serialize_response(struct http_channel *c, struct http_header *h; wrbuf_rewind(c->wrbuf); + wrbuf_printf(c->wrbuf, "HTTP/%s %s %s\r\n", c->version, r->code, r->msg); for (h = r->headers; h; h = h->next) wrbuf_printf(c->wrbuf, "%s: %s\r\n", h->name, h->value); @@ -1047,11 +1047,7 @@ static void proxy_io(IOCHAN pi, int event) yaz_log(YLOG_WARN, "Proxy read came up short"); // Close channel and alert client HTTP channel that we're gone http_buf_destroy(hc->http_server, htbuf); -#ifdef WIN32 - closesocket(iochan_getfd(pi)); -#else - close(iochan_getfd(pi)); -#endif + CLOSESOCKET(iochan_getfd(pi)); iochan_destroy(pi); pc->iochan = 0; } @@ -1121,11 +1117,7 @@ static void http_channel_destroy(IOCHAN i) { if (s->proxy->iochan) { -#ifdef WIN32 - closesocket(iochan_getfd(s->proxy->iochan)); -#else - close(iochan_getfd(s->proxy->iochan)); -#endif + CLOSESOCKET(iochan_getfd(s->proxy->iochan)); iochan_destroy(s->proxy->iochan); } http_buf_destroy_queue(s->http_server, s->proxy->oqueue); @@ -1140,11 +1132,8 @@ static void http_channel_destroy(IOCHAN i) http_server_destroy(http_server); -#ifdef WIN32 - closesocket(iochan_getfd(i)); -#else - close(iochan_getfd(i)); -#endif + CLOSESOCKET(iochan_getfd(i)); + iochan_destroy(i); nmem_destroy(s->nmem); wrbuf_destroy(s->wrbuf); @@ -1173,6 +1162,7 @@ static struct http_channel *http_channel_create(http_server_t hs, r->keep_alive = 0; r->request = 0; r->response = 0; + strcpy(r->version, "1.0"); if (!addr) { yaz_log(YLOG_WARN, "Invalid HTTP forward address"); @@ -1187,7 +1177,8 @@ static struct http_channel *http_channel_create(http_server_t hs, /* Accept a new command connection */ static void http_accept(IOCHAN i, int event) { - struct sockaddr_in addr; + char host[256]; + struct sockaddr addr; int fd = iochan_getfd(i); socklen_t len; int s; @@ -1196,18 +1187,25 @@ static void http_accept(IOCHAN i, int event) struct conf_server *server = iochan_getdata(i); len = sizeof addr; - if ((s = accept(fd, (struct sockaddr *) &addr, &len)) < 0) + if ((s = accept(fd, &addr, &len)) < 0) { yaz_log(YLOG_WARN|YLOG_ERRNO, "accept"); return; } + if (getnameinfo(&addr, len, host, sizeof(host)-1, 0, 0, NI_NUMERICHOST)) + { + yaz_log(YLOG_WARN|YLOG_ERRNO, "getnameinfo"); + CLOSESOCKET(s); + return; + } enable_nonblock(s); yaz_log(YLOG_DEBUG, "New command connection"); - c = iochan_create(s, http_io, EVENT_INPUT | EVENT_EXCEPT, "http_session_socket"); + c = iochan_create(s, http_io, EVENT_INPUT | EVENT_EXCEPT, + "http_session_socket"); - ch = http_channel_create(server->http_server, inet_ntoa(addr.sin_addr), - server); + + ch = http_channel_create(server->http_server, host, server); ch->iochan = c; iochan_setdata(c, ch); iochan_add(server->iochan_man, c); @@ -1219,75 +1217,101 @@ int http_init(const char *addr, struct conf_server *server, { IOCHAN c; int l; - struct protoent *p; - struct sockaddr_in myaddr; int one = 1; const char *pp; - short port; FILE *record_file = 0; + struct addrinfo hints, *ai = 0; + int error; + int ipv6_only = -1; yaz_log(YLOG_LOG, "HTTP listener %s", addr); + hints.ai_flags = 0; + hints.ai_family = AF_UNSPEC; + hints.ai_socktype = SOCK_STREAM; + hints.ai_protocol = 0; + hints.ai_addrlen = 0; + hints.ai_addr = NULL; + hints.ai_canonname = NULL; + hints.ai_next = NULL; - if (record_fname) - { - record_file = fopen(record_fname, "wb"); - if (!record_file) - { - yaz_log(YLOG_FATAL|YLOG_ERRNO, "fopen %s", record_fname); - return 1; - } - } - - memset(&myaddr, 0, sizeof myaddr); - myaddr.sin_family = AF_INET; pp = strchr(addr, ':'); if (pp) { WRBUF w = wrbuf_alloc(); - struct hostent *he; - wrbuf_write(w, addr, pp - addr); - wrbuf_puts(w, ""); - - he = gethostbyname(wrbuf_cstr(w)); - wrbuf_destroy(w); - if (!he) - { - yaz_log(YLOG_FATAL, "Unable to resolve '%s'", addr); - return 1; + if (!strcmp(wrbuf_cstr(w), "@")) + { /* IPV4 + IPV6 .. same as YAZ */ + ipv6_only = 0; + hints.ai_flags = AI_PASSIVE; + hints.ai_family = AF_INET6; + error = getaddrinfo(0, pp + 1, &hints, &ai); } - memcpy(&myaddr.sin_addr.s_addr, he->h_addr_list[0], he->h_length); - port = atoi(pp + 1); + else + error = getaddrinfo(wrbuf_cstr(w), pp + 1, &hints, &ai); + wrbuf_destroy(w); } else { - port = atoi(addr); - myaddr.sin_addr.s_addr = INADDR_ANY; + /* default for no host given is IPV4 */ + hints.ai_flags = AI_PASSIVE; + hints.ai_family = AF_INET; + error = getaddrinfo(0, addr, &hints, &ai); } - - myaddr.sin_port = htons(port); - - if (!(p = getprotobyname("tcp"))) { + if (error) + { + yaz_log(YLOG_FATAL, "Failed to resolve %s: %s", addr, + gai_strerror(error)); return 1; } - if ((l = socket(PF_INET, SOCK_STREAM, p->p_proto)) < 0) + l = socket(ai->ai_family, ai->ai_socktype, ai->ai_protocol); + if (l < 0) + { yaz_log(YLOG_FATAL|YLOG_ERRNO, "socket"); - if (setsockopt(l, SOL_SOCKET, SO_REUSEADDR, (char*) - &one, sizeof(one)) < 0) + freeaddrinfo(ai); return 1; - - if (bind(l, (struct sockaddr *) &myaddr, sizeof myaddr) < 0) + } + if (ipv6_only >= 0 && + setsockopt(l, IPPROTO_IPV6, IPV6_V6ONLY, &ipv6_only, sizeof(ipv6_only))) { - yaz_log(YLOG_FATAL|YLOG_ERRNO, "bind"); + yaz_log(YLOG_FATAL|YLOG_ERRNO, "setsockopt IPV6_V6ONLY %s %d", addr, + ipv6_only); + freeaddrinfo(ai); + CLOSESOCKET(l); return 1; } + if (setsockopt(l, SOL_SOCKET, SO_REUSEADDR, &one, sizeof(one))) + { + yaz_log(YLOG_FATAL|YLOG_ERRNO, "setsockopt SO_REUSEADDR %s", addr); + freeaddrinfo(ai); + CLOSESOCKET(l); + return 1; + } + if (bind(l, ai->ai_addr, ai->ai_addrlen) < 0) + { + yaz_log(YLOG_FATAL|YLOG_ERRNO, "bind %s", addr); + freeaddrinfo(ai); + CLOSESOCKET(l); + return 1; + } + freeaddrinfo(ai); if (listen(l, SOMAXCONN) < 0) { - yaz_log(YLOG_FATAL|YLOG_ERRNO, "listen"); + yaz_log(YLOG_FATAL|YLOG_ERRNO, "listen %s", addr); + CLOSESOCKET(l); return 1; } + if (record_fname) + { + record_file = fopen(record_fname, "wb"); + if (!record_file) + { + yaz_log(YLOG_FATAL|YLOG_ERRNO, "fopen %s", record_fname); + CLOSESOCKET(l); + return 1; + } + } server->http_server = http_server_create(); server->http_server->record_file = record_file; -- 1.7.10.4