diff --git a/configure.in b/configure.in index 179f944..68d4d31 100644 --- a/configure.in +++ b/configure.in @@ -1215,7 +1215,7 @@ AC_FUNC_SETPGRP AC_TYPE_SIGNAL dnl uncommenting does not work for swa. suse linux dnl AC_FUNC_STAT -AC_CHECK_FUNCS([access atexit getcwd gethostbyaddr gethostbyaddr_r gethostbyname gethostbyname_r gettimeofday inet_ntoa localtime_r memchr memmove memset putenv random regcomp select setlocale snprintf socket strchr strdup strerror strftime strlcat strlcpy strptime strstr strtoul timegm tzset]) +AC_CHECK_FUNCS([access atexit getaddrinfo getcwd gethostbyaddr gethostbyaddr_r gethostbyname gethostbyname_r getnameinfo gettimeofday inet_ntoa localtime_r memchr memmove memset putenv random regcomp select setlocale snprintf socket strchr strdup strerror strftime strlcat strlcpy strptime strstr strtoul timegm tzset]) dnl ================================================================= diff --git a/jbsockets.c b/jbsockets.c index 91832f8..5a087ef 100644 --- a/jbsockets.c +++ b/jbsockets.c @@ -345,19 +345,50 @@ const char jbsockets_h_rcs[] = JBSOCKETS_H_VERSION; *********************************************************************/ jb_socket connect_to(const char *host, int portnum, struct client_state *csp) { +#ifdef HAVE_GETADDRINFO + struct addrinfo hints, *result, *rp; + char service[6]; + int retval; +#else struct sockaddr_in inaddr; - jb_socket fd; int addr; +#endif /* def HAVE_GETADDRINFO */ + jb_socket fd; fd_set wfds; struct timeval tv[1]; #if !defined(_WIN32) && !defined(__BEOS__) && !defined(AMIGA) int flags; #endif /* !defined(_WIN32) && !defined(__BEOS__) && !defined(AMIGA) */ + int connect_failed; #ifdef FEATURE_ACL struct access_control_addr dst[1]; #endif /* def FEATURE_ACL */ +#ifdef HAVE_GETADDRINFO + retval = snprintf(service, 6, "%d", portnum); + if (-1 == retval || 6 <= retval) { + log_error(LOG_LEVEL_ERROR, + "Port number (%d) ASCII decimal representation doesn't fit into 6 bytes", + portnum); + csp->http->host_ip_addr_str = strdup("unknown"); + return(JB_INVALID_SOCKET); + } + + memset((char *)&hints, 0, sizeof hints); + hints.ai_family = AF_UNSPEC; + hints.ai_socktype = SOCK_STREAM; + hints.ai_flags = AI_ADDRCONFIG | AI_NUMERICSERV; /* avoid service look-up */ + if ((retval = getaddrinfo(host, service, &hints, &result))) + { + log_error(LOG_LEVEL_INFO, + "Can not resolve %s: %s", host, gai_strerror(retval)); + csp->http->host_ip_addr_str = strdup("unknown"); + return(JB_INVALID_SOCKET); + } + + for (rp = result; rp != NULL; rp = rp->ai_next) { +#else memset((char *)&inaddr, 0, sizeof inaddr); if ((addr = resolve_hostname_to_ip(host)) == INADDR_NONE) @@ -367,6 +398,7 @@ jb_socket connect_to(const char *host, int portnum, struct client_state *csp) } #ifdef FEATURE_ACL + /* FIXME: Implement ACL with sockaddr_storage */ dst->addr = ntohl((unsigned long) addr); dst->port = portnum; @@ -380,11 +412,37 @@ jb_socket connect_to(const char *host, int portnum, struct client_state *csp) return(JB_INVALID_SOCKET); } #endif /* def FEATURE_ACL */ - +#endif /* def HAVE_GETADDRINFO */ + +#ifdef HAVE_GETNAMEINFO + csp->http->host_ip_addr_str = malloc(NI_MAXHOST); + retval = getnameinfo(rp->ai_addr, rp->ai_addrlen, + csp->http->host_ip_addr_str, NI_MAXHOST, NULL, 0, NI_NUMERICHOST); + if (!csp->http->host_ip_addr_str || retval) { + log_error(LOG_LEVEL_ERROR, "Can not save csp->http->host_ip_addr_str: %s", + (csp->http->host_ip_addr_str) ? gai_strerror(retval) : + "Insufficient memory"); + free(csp->http->host_ip_addr_str); + csp->http->host_ip_addr_str = NULL; + continue; + } +#else inaddr.sin_addr.s_addr = addr; inaddr.sin_family = AF_INET; csp->http->host_ip_addr_str = strdup(inet_ntoa(inaddr.sin_addr)); +#endif /* def HAVE_GETNAMERINFO */ +#ifdef HAVE_GETADDRINFO +#ifdef _WIN32 + if ((fd = socket(rp->ai_family, rp->ai_socktype, rp->ai_protocol)) == + JB_INVALID_SOCKET) +#else + if ((fd = socket(rp->ai_family, rp->ai_socktype, rp->ai_protocol)) < 0) +#endif + { + continue; + } +#else #ifndef _WIN32 if (sizeof(inaddr.sin_port) == sizeof(short)) #endif /* ndef _WIN32 */ @@ -406,6 +464,7 @@ jb_socket connect_to(const char *host, int portnum, struct client_state *csp) { return(JB_INVALID_SOCKET); } +#endif /* HAVE_GETADDRINFO */ #ifdef TCP_NODELAY { /* turn off TCP coalescence */ @@ -422,7 +481,12 @@ jb_socket connect_to(const char *host, int portnum, struct client_state *csp) } #endif /* !defined(_WIN32) && !defined(__BEOS__) && !defined(AMIGA) && !defined(__OS2__) */ + connect_failed = 0; +#ifdef HAVE_GETADDRINFO + while (connect(fd, rp->ai_addr, rp->ai_addrlen) == JB_INVALID_SOCKET) +#else while (connect(fd, (struct sockaddr *) & inaddr, sizeof inaddr) == JB_INVALID_SOCKET) +#endif /* HAVE_GETADDRINFO */ { #ifdef _WIN32 if (errno == WSAEINPROGRESS) @@ -442,9 +506,17 @@ jb_socket connect_to(const char *host, int portnum, struct client_state *csp) #endif /* __OS2__ */ { close_socket(fd); - return(JB_INVALID_SOCKET); + connect_failed = 1; + break; } } + if (connect_failed) { +#ifdef HAVE_GETADDRINFO + continue; +#else + return(JB_INVALID_SOCKET); +#endif + } #if !defined(_WIN32) && !defined(__BEOS__) && !defined(AMIGA) && !defined(__OS2__) if (flags != -1) @@ -465,8 +537,31 @@ jb_socket connect_to(const char *host, int portnum, struct client_state *csp) if (select((int)fd + 1, NULL, &wfds, NULL, tv) <= 0) { close_socket(fd); +#ifdef HAVE_GETADDRINFO + continue; +#else return(JB_INVALID_SOCKET); +#endif + } + +#ifdef HAVE_GETADDRINFO + break; /* for */ } + + freeaddrinfo(result); + if (!rp) { + log_error(LOG_LEVEL_INFO, "Could not connect to TCP/[%s]:%s", host, service); + return(JB_INVALID_SOCKET); + } + /* XXX: Current connection verification (EINPROGRESS && select() for + * writing) is not sufficient. E.g. on my Linux-2.6.27 with glibc-2.6 + * select returns socket ready for writing, however subsequential write(2) + * fails with ENOCONNECT. Read Linux connect(2) man page about non-blocking + * sockets. + * Thus we can not log here the socket is connected. */ + /*log_error(LOG_LEVEL_INFO, "Connected to TCP/[%s]:%s", host, service);*/ +#endif + return(fd); } @@ -625,7 +720,16 @@ void close_socket(jb_socket fd) *********************************************************************/ int bind_port(const char *hostnam, int portnum, jb_socket *pfd) { +#ifdef HAVE_GETADDRINFO + struct addrinfo hints; + struct addrinfo *result, *rp; + /* TODO: portnum shuld be string to allow symbolic service names in + * configuration and to avoid following int2string */ + char servnam[6]; + int retval; +#else struct sockaddr_in inaddr; +#endif /* def HAVE_GETADDRINFO */ jb_socket fd; #ifndef _WIN32 int one = 1; @@ -633,6 +737,30 @@ int bind_port(const char *hostnam, int portnum, jb_socket *pfd) *pfd = JB_INVALID_SOCKET; +#ifdef HAVE_GETADDRINFO + retval = snprintf(servnam, 6, "%d", portnum); + if (-1 == retval || 6 <= retval) { + log_error(LOG_LEVEL_ERROR, + "Port number (%d) ASCII decimal representation doesn't fit into 6 bytes", + portnum); + return -1; + } + + memset(&hints, 0, sizeof(struct addrinfo)); + hints.ai_family=AF_UNSPEC; + hints.ai_socktype=SOCK_STREAM; + hints.ai_flags=AI_PASSIVE|AI_ADDRCONFIG; + hints.ai_protocol=0; /* Realy any stream protocol or TCP only */ + hints.ai_canonname=NULL; + hints.ai_addr=NULL; + hints.ai_next=NULL; + + if ((retval = getaddrinfo(hostnam, servnam, &hints, &result))) { + log_error(LOG_LEVEL_ERROR, + "Can not resolve %s: %s", hostnam, gai_strerror(retval)); + return -2; + } +#else memset((char *)&inaddr, '\0', sizeof inaddr); inaddr.sin_family = AF_INET; @@ -655,8 +783,14 @@ int bind_port(const char *hostnam, int portnum, jb_socket *pfd) inaddr.sin_port = htonl((unsigned long) portnum); } #endif /* ndef _WIN32 */ +#endif /* def HAVE_GETADDRINFO */ +#ifdef HAVE_GETADDRINFO + for (rp = result; rp != NULL; rp = rp->ai_next) { + fd = socket(rp->ai_family, rp->ai_socktype, rp->ai_protocol); +#else fd = socket(AF_INET, SOCK_STREAM, 0); +#endif /* def HAVE_GETADDRINFO */ #ifdef _WIN32 if (fd == JB_INVALID_SOCKET) @@ -664,7 +798,11 @@ int bind_port(const char *hostnam, int portnum, jb_socket *pfd) if (fd < 0) #endif { +#ifdef HAVE_GETADDRINFO + continue; +#else return(-1); +#endif } #ifndef _WIN32 @@ -683,7 +821,11 @@ int bind_port(const char *hostnam, int portnum, jb_socket *pfd) setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, (char *)&one, sizeof(one)); #endif /* ndef _WIN32 */ +#ifdef HAVE_GETADDRINFO + if (bind(fd, rp->ai_addr, rp->ai_addrlen) < 0) +#else if (bind(fd, (struct sockaddr *)&inaddr, sizeof(inaddr)) < 0) +#endif { #ifdef _WIN32 errno = WSAGetLastError(); @@ -692,15 +834,35 @@ int bind_port(const char *hostnam, int portnum, jb_socket *pfd) if (errno == EADDRINUSE) #endif { +#ifdef HAVE_GETADDRINFO + freeaddrinfo(result); +#endif close_socket(fd); return(-3); } else { close_socket(fd); +#ifndef HAVE_GETADDRINFO return(-1); } } +#else + } + } + else + /* bind() succeeded, escape from for-loop */ + /* TODO: Support multiple listening sockets (e.g. localhost resolves to + * AF_INET and AF_INET6, but only fist address is used */ + break; + } + + freeaddrinfo(result); + if (rp == NULL) { + /* All bind()s failed */ + return(-1); + } +#endif /* ndef HAVE_GETADDRINFO */ while (listen(fd, MAX_LISTEN_BACKLOG) == -1) { @@ -740,14 +902,20 @@ int bind_port(const char *hostnam, int portnum, jb_socket *pfd) *********************************************************************/ void get_host_information(jb_socket afd, char **ip_address, char **hostname) { +#ifdef HAVE_GETNAMEINFO + struct sockaddr_storage server; + int retval; +#else struct sockaddr_in server; struct hostent *host = NULL; +#endif /* HAVE_GETNAMEINFO */ #if defined(_WIN32) || defined(__OS2__) || defined(__APPLE_CC__) || defined(AMIGA) /* according to accept_connection() this fixes a warning. */ - int s_length; + int s_length, s_length_provided; #else - socklen_t s_length; + socklen_t s_length, s_length_provided; #endif +#ifndef HAVE_GETNAMEINFO #if defined(HAVE_GETHOSTBYADDR_R_8_ARGS) || defined(HAVE_GETHOSTBYADDR_R_7_ARGS) || defined(HAVE_GETHOSTBYADDR_R_5_ARGS) struct hostent result; #if defined(HAVE_GETHOSTBYADDR_R_5_ARGS) @@ -757,7 +925,8 @@ void get_host_information(jb_socket afd, char **ip_address, char **hostname) int thd_err; #endif /* def HAVE_GETHOSTBYADDR_R_5_ARGS */ #endif /* def HAVE_GETHOSTBYADDR_R_(8|7|5)_ARGS */ - s_length = sizeof(server); +#endif /* ifndef HAVE_GETNAMEINFO */ + s_length = s_length_provided = sizeof(server); if (NULL != hostname) { @@ -767,8 +936,23 @@ void get_host_information(jb_socket afd, char **ip_address, char **hostname) if (!getsockname(afd, (struct sockaddr *) &server, &s_length)) { + if (s_length > s_length_provided) { + log_error(LOG_LEVEL_ERROR, "getsockname() truncated server address"); + return; + } +#ifdef HAVE_GETNAMEINFO + *ip_address = malloc(NI_MAXHOST); + if ((retval = getnameinfo((struct sockaddr *) &server, s_length, + *ip_address, NI_MAXHOST, NULL, 0, NI_NUMERICHOST))) { + log_error(LOG_LEVEL_ERROR, "Unable to print my own IP address: %s", + gai_strerror(retval)); + free(*ip_address); + *ip_address = NULL; + return; + } +#else *ip_address = strdup(inet_ntoa(server.sin_addr)); - +#endif /* HAVE_GETNAMEINFO */ if (NULL == hostname) { /* @@ -777,6 +961,17 @@ void get_host_information(jb_socket afd, char **ip_address, char **hostname) */ return; } + +#ifdef HAVE_GETNAMEINFO + *hostname = malloc(NI_MAXHOST); + if ((retval = getnameinfo((struct sockaddr *) &server, s_length, + *hostname, NI_MAXHOST, NULL, 0, NI_NAMEREQD))) { + log_error(LOG_LEVEL_ERROR, "Unable to resolve my own IP address: %s", + gai_strerror(retval)); + free(*hostname); + *hostname = NULL; + } +#else #if defined(HAVE_GETHOSTBYADDR_R_8_ARGS) gethostbyaddr_r((const char *)&server.sin_addr, sizeof(server.sin_addr), AF_INET, @@ -814,6 +1009,7 @@ void get_host_information(jb_socket afd, char **ip_address, char **hostname) { *hostname = strdup(host->h_name); } +#endif /* else def HAVE_GETNAMEINFO */ } return; @@ -838,7 +1034,14 @@ void get_host_information(jb_socket afd, char **ip_address, char **hostname) *********************************************************************/ int accept_connection(struct client_state * csp, jb_socket fd) { +#ifdef HAVE_GETNAMEINFO + /* client is stored directly into csp->tcp_addr + struct sockaddr_storage client; */ +#define client (csp->tcp_addr) + int retval; +#else struct sockaddr_in client; +#endif jb_socket afd; #if defined(_WIN32) || defined(__OS2__) || defined(__APPLE_CC__) || defined(AMIGA) /* Wierdness - fix a warning. */ @@ -867,8 +1070,21 @@ int accept_connection(struct client_state * csp, jb_socket fd) #endif csp->cfd = afd; +#ifdef HAVE_GETNAMEINFO + csp->ip_addr_str = malloc(NI_MAXHOST); + retval = getnameinfo((struct sockaddr *) &client, c_length, + csp->ip_addr_str, NI_MAXHOST, NULL, 0, NI_NUMERICHOST); + if (!csp->ip_addr_str || retval) { + log_error(LOG_LEVEL_ERROR, "Can not save csp->ip_addr_str: %s", + (csp->ip_addr_str) ? gai_strerror(retval) : "Insuffcient memory"); + free(csp->ip_addr_str); + csp->ip_addr_str = NULL; + } +#undef client +#else csp->ip_addr_str = strdup(inet_ntoa(client.sin_addr)); csp->ip_addr_long = ntohl(client.sin_addr.s_addr); +#endif /* def HAVE_GETNAMEINFO */ return 1; @@ -999,4 +1215,6 @@ unsigned long resolve_hostname_to_ip(const char *host) Local Variables: tab-width: 3 end: + + vim:softtabstop=3 shiftwidth=3 */ diff --git a/jcc.c b/jcc.c index 0fb7cfd..2800869 100644 --- a/jcc.c +++ b/jcc.c @@ -2320,7 +2320,7 @@ static void chat(struct client_state *csp) if (fwd->forward_host) { - log_error(LOG_LEVEL_CONNECT, "via %s:%d to: %s", + log_error(LOG_LEVEL_CONNECT, "via [%s]:%d to: %s", fwd->forward_host, fwd->forward_port, http->hostport); } else @@ -3899,4 +3899,6 @@ static void listen_loop(void) Local Variables: tab-width: 3 end: + + vim:softtabstop=3 shiftwidth=3 */ diff --git a/loadcfg.c b/loadcfg.c index 69cce44..508afff 100644 --- a/loadcfg.c +++ b/loadcfg.c @@ -1133,7 +1133,18 @@ struct configuration_spec * load_config(void) { cur_fwd->forward_host = strdup(p); - if (NULL != (p = strchr(cur_fwd->forward_host, ':'))) + if (*cur_fwd->forward_host == '[' && + NULL != (p = strchr(cur_fwd->forward_host, ']'))) + { + *p++ = '\0'; + memmove(cur_fwd->forward_host, cur_fwd->forward_host + 1, + (size_t) (p - cur_fwd->forward_host)); + if (*p == ':') + { + cur_fwd->forward_port = atoi(++p); + } + } + else if (NULL != (p = strchr(cur_fwd->forward_host, ':'))) { *p++ = '\0'; cur_fwd->forward_port = atoi(p); @@ -1735,18 +1746,20 @@ struct configuration_spec * load_config(void) if ( NULL != config->haddr ) { - if (NULL != (p = strchr(config->haddr, ':'))) + if (*config->haddr == '[' && NULL != (p = strchr(config->haddr, ']')) && + p[1] == ':' && 0 < (config->hport = atoi(p + 2))) { - *p++ = '\0'; - if (*p) - { - config->hport = atoi(p); - } + *p='\0'; + memmove((void *) config->haddr, config->haddr + 1, + (size_t) (p - config->haddr)); } - - if (config->hport <= 0) + else if (NULL != (p = strchr(config->haddr, ':')) && + 0 < (config->hport = atoi(p + 1))) + { + *p = '\0'; + } + else { - *--p = ':'; log_error(LOG_LEVEL_FATAL, "invalid bind port spec %s", config->haddr); /* Never get here - LOG_LEVEL_FATAL causes program exit */ } @@ -1922,4 +1935,6 @@ static void savearg(char *command, char *argument, struct configuration_spec * c Local Variables: tab-width: 3 end: + + vim:softtabstop=3 shiftwidth=3 */ diff --git a/project.h b/project.h index 4dbe236..6ddb273 100644 --- a/project.h +++ b/project.h @@ -657,6 +657,12 @@ /* Needed for pcre choice */ #include "config.h" +#ifdef HAVE_GETADDRINFO +/* Need for struct sockaddr_storage */ +#include +#endif + + /* * Include appropriate regular expression libraries. * Note that pcrs and pcre (native) are needed for cgi @@ -1349,9 +1355,15 @@ struct client_state /** Client PC's IP address, as reported by the accept() function. As a string. */ char *ip_addr_str; +#ifdef HAVE_GETADDRINFO + /** Client PC's TCP address, as reported by the accept() function. + As a sockaddr. */ + struct sockaddr_storage tcp_addr; +#else /** Client PC's IP address, as reported by the accept() function. As a number. */ long ip_addr_long; +#endif /* def HAVE_GETADDRINFO */ /** The URL that was requested */ struct http_request http[1]; @@ -1804,4 +1816,6 @@ struct configuration_spec Local Variables: tab-width: 3 end: + + vim:softtabstop=3 shiftwidth=3 */ diff --git a/urlmatch.c b/urlmatch.c index 3ad1111..04f232f 100644 --- a/urlmatch.c +++ b/urlmatch.c @@ -533,8 +533,35 @@ jb_err parse_http_url(const char *url, struct http_request *http, int require_pr host = buf; } + /* Move after hostname before port number */ + if (*host == '[') + { + /* Numeric IPv6 address delimited by brackets */ + host++; + port = strchr(host, ']'); + + if (port == NULL) { + /* Missing closing bracket */ + freez(buf); + return JB_ERR_PARSE; + } + + *port++='\0'; + + if (*port != '\0' && *port != ':') + { + /* Garbage after closing bracket */ + freez(buf); + return JB_ERR_PARSE; + } + } + else + { + /* Plain non-escaped hostname */ + port = strchr(host, ':'); + } + /* check if url contains port */ - port = strchr(host, ':'); if (port != NULL) { /* Contains port */ @@ -1437,4 +1464,6 @@ int match_portlist(const char *portlist, int port) Local Variables: tab-width: 3 end: + + vim:softtabstop=3 shiftwidth=3 */