diff options
Diffstat (limited to 'usr.sbin/httpd')
36 files changed, 2163 insertions, 726 deletions
diff --git a/usr.sbin/httpd/Makefile.bsd-wrapper b/usr.sbin/httpd/Makefile.bsd-wrapper index e8e9de2bb8f..585383dc2d3 100644 --- a/usr.sbin/httpd/Makefile.bsd-wrapper +++ b/usr.sbin/httpd/Makefile.bsd-wrapper @@ -1,5 +1,5 @@ # Build wrapper for Apache -# $OpenBSD: Makefile.bsd-wrapper,v 1.64 2008/01/08 10:08:31 henning Exp $ +# $OpenBSD: Makefile.bsd-wrapper,v 1.65 2008/05/09 08:06:27 mbalmer Exp $ # Our lndir is hacked; specify a full path to avoid potential conflicts # with the one installed with X11. @@ -93,7 +93,8 @@ DSO_MODULE_ARGS= \ --enable-module=usertrack \ --enable-shared=usertrack \ --enable-module=vhost_alias \ - --enable-shared=vhost_alias + --enable-shared=vhost_alias \ + --enable-rule=INET6 INSTALL_MODULES= @@ -252,6 +253,7 @@ MANUALFILES= \ manual/handler.html \ manual/index.html \ manual/invoking.html \ + manual/ipv6.html \ manual/keepalive.html \ manual/location.html \ manual/logs.html \ @@ -739,6 +741,14 @@ cleanman: @echo NOMAN is set .endif +man: + @-for i in ${MANUALFILES}; do \ + j=`dirname $$i`; \ + echo "Installing ${DESTDIR}${HTTPD_HTDOCSDIR}/$$i"; \ + ${INSTALL} ${INSTALL_COPY} -g ${BINGRP} -m 444 \ + htdocs/$$i ${DESTDIR}${HTTPD_HTDOCSDIR}/$$j/; \ + done + distribution: @-for i in ${MODCONFDIR}; do \ echo "Installing ${DESTDIR}${HTTPD_SYSCONFDIR}/$$i"; \ diff --git a/usr.sbin/httpd/README.v6 b/usr.sbin/httpd/README.v6 new file mode 100644 index 00000000000..5160c617bb1 --- /dev/null +++ b/usr.sbin/httpd/README.v6 @@ -0,0 +1,127 @@ +$OpenBSD: README.v6,v 1.1 2008/05/09 08:06:27 mbalmer Exp $ +$Id: README.v6,v 1.1 2008/05/09 08:06:27 mbalmer Exp $ + +IPv6 support for the OpenBSD httpd(8) + +To support IPv6 the apache module API/ABI had to be changed, to avoid +IPv4-dependent structure member variables (like use of u_long to hold +an IPv4 address, or whatever). Keep this in mind when writing new +modules or adding modules to the ports collection. + +Basically you can write IPv6 address where IPv4 address fits. + +extra command-line argument: + -4 Assume IPv4 address on ambiguous directives (default) + -6 Assume IPv6 address on ambiguous directives + + The above two can be used, for example, to disambiguate + "BindAddress *". + +base commands: + Listen + Listen is expanded to take one or two arguments. + Listen port + Listen address:port + Listen address port + This is to let you specify "Listen :: 80", since "Listen :::80" + won't work. + +mod_access: + deny from + allow from + "deny from" and "allow from" supports IPv6 addresses, under the + following forms: + {deny,allow} from v6addr + {deny,allow} from v6addr/v6mask + {deny,allow} from v6addr/prefixlen + Also, wildcard ("*") and string hostname matches IPv6 hosts as well. + +mod_proxy: + ProxyRequests on + http/ftp proxying for both IPv4 and IPv6 is possible. + Access control functions (NoProxy) are not updated yet. + + NOTE: for security reasons, we recommend you to filter out + outsider's access to your proxy, by directives like below: + <Directory proxy:*> + order deny,allow + deny from all + allow from 10.0.0.0/8 + allow from 3ffe:9999:8888:7777::/64 + </Directory> + +virtual host: + If you would like to this feature, you must describe 'Listen' + part on configuration file explicitly. like below: + Listen :: 80 + Listen 0.0.0.0 80 + + NameVirtualHost + NameVirtualHost is expanded to take one more two arguments. + NameVirtualHost address + NameVirtualHost address:port + NameVirtualHost address port + This is to let you specify IPv6 address into address part. + + Note that, if a colon is found in the specified address string, + the code will to resolve the address in the following way: + 1. try to resolve as address:port (most of IPv6 address fails) + 2. if (1) is failed, try to resolve as address only + If there's ambiguity, i.e. 3ffe:0501::1:2, the address may not be + parsed as you expect (3ffe:0501::1 with port 2, or 3ffe:0501::1:2 + with default port). To get the right effect you are encouraged + to specify it without ambiguity. In IPv6 case "address port" + (specify address and port separated by a space) is the safest way. + + <VirtualHost host:port [host:port ...]> + If you would like to specify IPv6 numeric address in host part, + use bracketed format like below: + <VirtualHost [::1]:80> + Note: Now we DO NOT handle old non-bracketed format, + <VirtualHost 0:0:0:0:0:0:0:1:80> + so configuration file must be updated. + Note: The following is bad example to specify host ::1 port 80. + This will treated as host ::1:80. + <VirtualHost ::1:80> + +logresolve (src/support) + error statistics in nameserver cache code is omitted. + +mod_unique_id + Originally mod_unique_id used IPv4 address as a seed for UNIQUE_ID, + and took IPv4 address registered onto DNS for the hostname (UNIX + hostname taken by gethostname(3)). Therefore, this does not work + for IPv6-only hosts as they do not have IPv4 address for them. + + Now, UNIQUE_ID can be generated using IPv6 address. IPv6 address can + be used as the seed for UNIQUE_ID. + Because of this, UNIQUE_ID will be longer than normal apache. This + may cause problem with some of the CGI scripts. + The preference of the addresses is based on the order returned + by getaddrinfo(). If your getaddrinfo() returns IPv4 address, IPv4 + adderss will be used as a seed. + Note that some of IPv6 addresses are "scoped"; If you happened to use + link-local or site-local address as a seed, the UNIQUE_ID may not be + worldwide unique. + + If longer UNIQUE_ID causes a problem, define SHORT_UNIQUE_ID in + mod_unique_id.c. In this case, length of UNIQUE_ID will be kept the + same. However, for IPv6 addresses mod_unique_id.c will use the last + 32bit (not the whole 128bit) as the seed. Therefore, there can be + collision in UNIQUE_ID. + + The behavior should be improved in the near future; we welcome your + inputs. + +configuration file + We do not support IPv4 mapped addresses (IPv6 address format like + ::ffff:10.1.1.1) in configuration file. + +Credit: + +This file is derived from the README.v6 file that accompanied the +original patchkit for Apache 1.3.9 from the KAME project. It was +written by Jun-ichiro itojun Hagino. + + http://www.kame.net/ + mailto:core@kame.net diff --git a/usr.sbin/httpd/conf/httpd.conf-dist b/usr.sbin/httpd/conf/httpd.conf-dist index 84da5f10142..5e159777b3b 100644 --- a/usr.sbin/httpd/conf/httpd.conf-dist +++ b/usr.sbin/httpd/conf/httpd.conf-dist @@ -185,6 +185,11 @@ MaxSTACKPerChild 0 #Listen 3000 #Listen 12.34.56.78:80 +# Listen can take two arguments. +# (this is an extension for supporting IPv6 addresses) +#Listen :: 80 +#Listen 0.0.0.0 80 + # # BindAddress: You can support virtual hosts with this option. This directive # is used to tell the server which IP address to listen to. It can either @@ -945,7 +950,7 @@ ServerSignature On # # Use name-based virtual hosting. # -#NameVirtualHost *:80 +#NameVirtualHost 0.0.0.0:80 # # VirtualHost example: @@ -953,7 +958,7 @@ ServerSignature On # The first VirtualHost section is used for requests without a known # server name. # -#<VirtualHost *:80> +#<VirtualHost 0.0.0.0:80> # ServerAdmin webmaster@dummy-host.example.com # DocumentRoot /www/docs/dummy-host.example.com # ServerName dummy-host.example.com diff --git a/usr.sbin/httpd/htdocs/manual/index.html b/usr.sbin/httpd/htdocs/manual/index.html index e19b0ebe715..0c4ada5943d 100644 --- a/usr.sbin/httpd/htdocs/manual/index.html +++ b/usr.sbin/httpd/htdocs/manual/index.html @@ -141,7 +141,10 @@ <td align="center" bgcolor="#e9e9e9"> <strong>Platform Specific Notes</strong> </td> </tr> - + <tr> + <td><a href="ipv6.html">Support for IPv6</a> + </td> + </tr> </table> </td> diff --git a/usr.sbin/httpd/htdocs/manual/ipv6.html b/usr.sbin/httpd/htdocs/manual/ipv6.html new file mode 100644 index 00000000000..cb2e46e5c55 --- /dev/null +++ b/usr.sbin/httpd/htdocs/manual/ipv6.html @@ -0,0 +1,228 @@ +<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" + "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> + +<html xmlns="http://www.w3.org/1999/xhtml"> + <head> + <meta name="generator" content="HTML Tidy, see www.w3.org" /> + + <title>IPv6 Support for the OpenBSD Apache HTTP Server</title> + </head> + <!-- Background white, links blue (unvisited), navy (visited), red (active) --> + + <body bgcolor="#FFFFFF" text="#000000" link="#0000FF" + vlink="#000080" alink="#FF0000"> + <div align="CENTER"> + <img src="images/sub.gif" alt="[APACHE DOCUMENTATION]" /> + + <h3>Apache HTTP Server</h3> + </div> + + + + <h1 align="center">IPv6 Support for the OpenBSD Apache HTTP Server</h1> + + <p>This document explains how OpenBSD Apache makes use of IPv6.</p> + + +<p> +To support IPv6 the apache module API/ABI had to be changed, to avoid +IPv4-dependent structure member variables (like use of u_long to hold +an IPv4 address, or whatever). Keep this in mind when writing new +modules or adding modules to the ports collection. +</p> + +Basically you can write IPv6 address where IPv4 address fits. + +<h2>extra command-line argument</h2> + +<dl> +<dt>-4</dt><dd>Assume IPv4 address on ambiguous directives (default)</dd> +<dt>-6</dt><dd>Assume IPv6 address on ambiguous directives</dd> +</dl> + +<p> +The above two can be used, for example, to disambiguate "BindAddress *". +</p> + +<h2>base commands</h2> +<h3>Listen</h3> +<p> +Listen is expanded to take one or two arguments. +</p> +<pre> + Listen port + Listen address:port + Listen address port +</pre> +<p> +This is to let you specify "Listen :: 80", since "Listen :::80" +won't work. +</p> +<p> +If you want httpd to listen on port 80 of all IPv4 and IPv6 addresses +simultaneously, you would specify this using the following commands in +your main server configuration: +<pre> + Listen 0.0.0.0 80 + Listen :: 80 +</pre> + + +<h2>mod_access</h2> + +deny from<br> +allow from + +<p> +"deny from" and "allow from" supports IPv6 addresses, under the +following forms: +</p> + +<pre> + {deny,allow} from v6addr + {deny,allow} from v6addr/v6mask + {deny,allow} from v6addr/prefixlen +</pre> + +<p> +Also, wildcard ("*") and string hostname matches IPv6 hosts as well. +</p> + +<h2>mod_proxy</h2> + +ProxyRequests on<br> + +<p> +http/ftp proxying for both IPv4 and IPv6 is possible. +Access control functions (NoProxy) are not updated yet. +</p> +<p> +NOTE: for security reasons, we recommend you to filter out +outsider's access to your proxy, by directives like below: +</p> +<pre> + <Directory proxy:*> + order deny,allow + deny from all + allow from 10.0.0.0/8 + allow from 3ffe:9999:8888:7777::/64 + </Directory> +</pre> + +<h2>virtual host</h2> +<p> +If you would like to this feature, you must describe 'Listen' +part on configuration file explicitly. like below: +</p> +<pre> + Listen :: 80 + Listen 0.0.0.0 80 +</pre> + +NameVirtualHost<br> +<p> +NameVirtualHost is expanded to take one or two arguments. +</p> +<pre> + NameVirtualHost address + NameVirtualHost address:port + NameVirtualHost address port +</pre> +<p> +This is to let you specify IPv6 address into address part. +</p> +<p> +Note that, if a colon is found in the specified address string, +the code will try to resolve the address in the following way: +<ol> + <li>try to resolve as address:port (most of IPv6 address fails) + <li>if (1) is failed, try to resolve as address only +</ol> +</p> +<p> +If there's ambiguity, i.e. 3ffe:0501::1:2, the address may not be +parsed as you expect (3ffe:0501::1 with port 2, or 3ffe:0501::1:2 +with default port). To get the right effect you are encouraged +to specify it without ambiguity. In IPv6 case "address port" +(specify address and port separated by a space) is the safest way. +</p> + +<pre> +<VirtualHost host:port [host:port ...]><br> +</pre> +<p> +If you would like to specify IPv6 numeric address in host part, +use bracketed format like below: +<p> +<pre> + <VirtualHost [::1]:80> +</pre> +<p> +Note: Now we DO NOT handle old non-bracketed format, +</p> +<pre> + <VirtualHost 0:0:0:0:0:0:0:1:80> +</pre> +<p> +so configuration file must be updated. +</p> +<p> +Note: The following is bad example to specify host ::1 port 80. +This will treated as host ::1:80. +</p> +<pre> + <VirtualHost ::1:80> +</pre> + +<h2>logresolve (src/support)</h2> +<p> +error statistics in nameserver cache code is omitted. +</p> + +<h2>mod_unique_id</h2> +<p> +Originally mod_unique_id used IPv4 address as a seed for UNIQUE_ID, +and took IPv4 address registered onto DNS for the hostname (UNIX +hostname taken by gethostname(3)). Therefore, this does not work +for IPv6-only hosts as they do not have IPv4 address for them. +</p> +<p> +Now, UNIQUE_ID can be generated using IPv6 address. IPv6 address can +be used as the seed for UNIQUE_ID. +Because of this, UNIQUE_ID will be longer than normal apache. This +may cause problem with some of the CGI scripts. +The preference of the addresses is based on the order returned +by getaddrinfo(). If your getaddrinfo() returns IPv4 address, IPv4 +adderss will be used as a seed. +</p> +<p> +Note that some of IPv6 addresses are "scoped"; If you happened to use +link-local or site-local address as a seed, the UNIQUE_ID may not be +worldwide unique. +</p> +<p> +If longer UNIQUE_ID causes a problem, define SHORT_UNIQUE_ID in +mod_unique_id.c. In this case, length of UNIQUE_ID will be kept the +same. However, for IPv6 addresses mod_unique_id.c will use the last +32bit (not the whole 128bit) as the seed. Therefore, there can be +collision in UNIQUE_ID. +</p> +<p> +The behavior should be improved in the near future; we welcome your +inputs. +</p> + +<h2>configuration file</h2> +<p> +We do not support IPv4 mapped addresses (IPv6 address format like +::ffff:10.1.1.1) in configuration file. +</p> + + <hr /> + + <h3 align="CENTER">Apache HTTP Server</h3> + <a href="./"><img src="images/index.gif" alt="Index" /></a> + + </body> +</html> + diff --git a/usr.sbin/httpd/htdocs/manual/misc/rewriteguide.html b/usr.sbin/httpd/htdocs/manual/misc/rewriteguide.html index 195daefd044..bd62b24d778 100644 --- a/usr.sbin/httpd/htdocs/manual/misc/rewriteguide.html +++ b/usr.sbin/httpd/htdocs/manual/misc/rewriteguide.html @@ -2103,7 +2103,7 @@ perl.apache.org OK <tr> <td> <pre> -<VirtualHost *:8008> +<VirtualHost 0.0.0.0:8008> ... RewriteEngine On # Either use the (plaintext) allow list from goodsites.txt diff --git a/usr.sbin/httpd/htdocs/manual/mod/core.html b/usr.sbin/httpd/htdocs/manual/mod/core.html index 18ed5be92f9..55829a0e982 100644 --- a/usr.sbin/httpd/htdocs/manual/mod/core.html +++ b/usr.sbin/httpd/htdocs/manual/mod/core.html @@ -2867,7 +2867,8 @@ Syntax OK <blockquote> <code>NameVirtualHost 111.22.33.44:8080</code> </blockquote> - In Apache 1.3.13 and greater you can specify a <code>*</code> + In OpenBSD Apache you can specify a <code>0.0.0.0</code>(IPv4) + or <code>::</code>(IPv6) for the <em>addr</em>. This creates a wildcard NameVirtualHost which will match connections to any address that isn't configured with a more specific NameVirtualHost directive or <a diff --git a/usr.sbin/httpd/httpd.8 b/usr.sbin/httpd/httpd.8 index ad5e80ab516..6f97df90143 100644 --- a/usr.sbin/httpd/httpd.8 +++ b/usr.sbin/httpd/httpd.8 @@ -1,4 +1,4 @@ -.\" $OpenBSD: httpd.8,v 1.27 2007/05/31 19:20:24 jmc Exp $ +.\" $OpenBSD: httpd.8,v 1.28 2008/05/09 08:06:27 mbalmer Exp $ .\" Copyright (c) 1995-1997 David Robinson. All rights reserved. .\" Copyright (c) 1997-1999 The Apache Group. All rights reserved. .\" Copyright (c) 1998-1999 Bob Beck. All rights reserved. @@ -50,7 +50,7 @@ .\" Supercomputing Applications, University of Illinois, Urbana-Champaign. .\" For more information on the Apache Group and the Apache HTTP server .\" project, please see <http://www.apache.org/>. -.Dd $Mdocdate: May 31 2007 $ +.Dd $Mdocdate: May 9 2008 $ .Dt HTTPD 8 .Os .Sh NAME @@ -59,7 +59,7 @@ .Sh SYNOPSIS .Nm httpd .Bk -words -.Op Fl FhLlSTtuVvX +.Op Fl 46FhLlSTtuVvX .Op Fl C Ar directive .Op Fl c Ar directive .Op Fl D Ar parameter @@ -159,6 +159,12 @@ by default with .Pp The options are as follows: .Bl -tag -width Ds +.It Fl 4 +Assume IPv4 address on ambiguous directives (default) +.It Fl 6 +Assume IPv6 address on ambiguous directives +.Pp +The above two can be used, for example, to disambiguate "BindAddress *". .It Fl C Ar directive Process the configuration .Ar directive diff --git a/usr.sbin/httpd/src/Configuration.tmpl b/usr.sbin/httpd/src/Configuration.tmpl index 9912cb46d94..f471d8d7545 100644 --- a/usr.sbin/httpd/src/Configuration.tmpl +++ b/usr.sbin/httpd/src/Configuration.tmpl @@ -235,6 +235,9 @@ Rule SHARED_CHAIN=default # implementation and uses the Win32 native calls. Should be faster # and more reliable for high-load systems. # +# INET6: +# IPv6 support. +# Rule SOCKS4=no Rule SOCKS5=no @@ -243,6 +246,7 @@ Rule IRIXN32=yes Rule PARANOID=no Rule EXPAT=default Rule CYGWIN_WINSOCK=no +Rule INET6=yes # DEV_RANDOM: # Note: this rule is only used when compiling mod_auth_digest. diff --git a/usr.sbin/httpd/src/Configure b/usr.sbin/httpd/src/Configure index 0cbd290df05..4447623f70a 100644 --- a/usr.sbin/httpd/src/Configure +++ b/usr.sbin/httpd/src/Configure @@ -1,5 +1,5 @@ #!/bin/sh -# $OpenBSD: Configure,v 1.27 2007/02/14 20:11:09 millert Exp $ +# $OpenBSD: Configure,v 1.28 2008/05/09 08:06:27 mbalmer Exp $ ## ==================================================================== ## The Apache Software License, Version 1.1 ## @@ -240,6 +240,7 @@ RULE_EXPAT=`${SHELL} helpers/CutRule EXPAT $file` RULE_CYGWIN_WINSOCK=`${SHELL} helpers/CutRule CYGWIN_WINSOCK $file` RULE_SHARED_CORE=`${SHELL} helpers/CutRule SHARED_CORE $file` RULE_SHARED_CHAIN=`${SHELL} helpers/CutRule SHARED_CHAIN $file` +RULE_INET6=`${SHELL} helpers/CutRule INET6 $file` #################################################################### ## Rule SHARED_CORE implies required DSO support @@ -1670,7 +1671,7 @@ case "$PLAT" in # Test for the presence of the "union semun": if TCADDINCL='#include <sys/types.h> #include <sys/ipc.h> -#include <sys/sem.h>' ./helpers/TestCompile sizeof "union semun"; then +#include <sys/sem.h>' ${SHELL} helpers/TestCompile sizeof "union semun"; then : Okay, union semun is defined else CFLAGS="$CFLAGS -DNEED_UNION_SEMUN" @@ -1726,6 +1727,62 @@ if [ "x$RULE_SOCKS5" = "xyes" ]; then esac fi +# INET6 support. +if [ "$RULE_INET6" = "yes" ]; then + echo " + enabling INET6 support" + CFLAGS="$CFLAGS -DINET6" + CFLAGS="$CFLAGS -Dss_family=__ss_family -Dss_len=__ss_len" + IPV6_STACKTYPE=KAME +fi + +echo '#include <sys/types.h>' >testfunc.c +echo '#include <sys/socket.h>' >>testfunc.c +echo 'int testfunc(){ struct sockaddr sa; int i = sa.sa_len; };' >>testfunc.c +rm -f testfunc.o +eval "${MAKE-make} -f Makefile.config testfunc.o >/dev/null 2>/dev/null" +if [ -f testfunc.o ]; then + echo " + you have sa_len in struct sockaddr." + CFLAGS="$CFLAGS -DHAVE_SOCKADDR_LEN" +else + echo " + you don't have sa_len in struct sockaddr." +fi +rm -f testfunc.c testfunc.o + +echo '#include <sys/types.h>' >testfunc.c +echo '#include <sys/socket.h>' >>testfunc.c +echo 'struct sockaddr_storage sockaddr_storage;' >>testfunc.c +rm -f testfunc.o +eval "${MAKE-make} -f Makefile.config testfunc.o >/dev/null 2>/dev/null" +if [ -f testfunc.o ]; then + echo " + assuming you have struct sockaddr_storage" +else + CFLAGS="$CFLAGS -DNEED_SOCKADDR_STORAGE" + echo " + you need struct sockaddr_storage" +fi +rm -f testfunc.c testfunc.o + +echo '#include <sys/types.h>' >testfunc.c +echo '#include <sys/socket.h>' >>testfunc.c +echo 'int testfunc(){ socklen_t t; }' >>testfunc.c +rm -f testfunc.o +eval "${MAKE-make} -f Makefile.config testfunc.o >/dev/null 2>/dev/null" +if [ ! -f testfunc.o ]; then + CFLAGS="$CFLAGS -Dsocklen_t=int" +fi +rm -f testfunc.c testfunc.o + +echo '#include <sys/types.h>' >testfunc.c +echo '#include <sys/socket.h>' >>testfunc.c +echo 'struct sockaddr_in sin;' >>testfunc.c +echo 'int main(){ int i = sin.sin_len; }' >>testfunc.c +rm -f testfunc.o +eval "${MAKE-make} -f Makefile.config testfunc.o >/dev/null 2>/dev/null" +if [ -f testfunc.o ]; then + CFLAGS="$CFLAGS -DSIN_LEN" +fi +rm -f testfunc.c testfunc.o + + #################################################################### ## Find out what modules we want and try and configure things for them ## Module lines can look like this: diff --git a/usr.sbin/httpd/src/include/ap.h b/usr.sbin/httpd/src/include/ap.h index 4a3b554221e..dbe41bfe83d 100644 --- a/usr.sbin/httpd/src/include/ap.h +++ b/usr.sbin/httpd/src/include/ap.h @@ -1,4 +1,4 @@ -/* $OpenBSD: ap.h,v 1.13 2005/03/28 23:26:51 niallo Exp $ */ +/* $OpenBSD: ap.h,v 1.14 2008/05/09 08:06:27 mbalmer Exp $ */ /* ==================================================================== * The Apache Software License, Version 1.1 @@ -88,8 +88,9 @@ API_EXPORT(long) ap_strtol(const char *nptr, char **endptr, int base); /* ap_vformatter() is a generic printf-style formatting routine * with some extensions. The extensions are: * - * %pA takes a struct in_addr *, and prints it as a.b.c.d - * %pI takes a struct sockaddr_in * and prints it as a.b.c.d:port + * %pA takes a struct in_addr *, and prints it as a.b.c.d + * %pI takes a struct sockaddr * and prints it as a.b.c.d:port, or + * ipv6-numeric-addr:port * %pp takes a void * and outputs it in hex * * The %p hacks are to force gcc's printf warning code to skip diff --git a/usr.sbin/httpd/src/include/ap_config.h b/usr.sbin/httpd/src/include/ap_config.h index b9313af6062..bd489a78357 100644 --- a/usr.sbin/httpd/src/include/ap_config.h +++ b/usr.sbin/httpd/src/include/ap_config.h @@ -1,4 +1,4 @@ -/* $OpenBSD: ap_config.h,v 1.21 2006/04/04 11:39:28 henning Exp $ */ +/* $OpenBSD: ap_config.h,v 1.22 2008/05/09 08:06:27 mbalmer Exp $ */ /* ==================================================================== * The Apache Software License, Version 1.1 @@ -332,6 +332,19 @@ Sigfunc *signal(int signo, Sigfunc * func); #define ap_wait_t int #endif +#ifndef INET6_ADDRSTRLEN +#define INET6_ADDRSTRLEN 46 +#endif +#ifndef INET_ADDRSTRLEN +#define INET_ADDRSTRLEN 16 +#endif +#ifndef NI_MAXHOST +#define NI_MAXHOST 1025 +#endif +#ifndef NI_MAXSERV +#define NI_MAXSERV 32 +#endif + #ifdef __cplusplus } #endif diff --git a/usr.sbin/httpd/src/include/ap_config_auto.h b/usr.sbin/httpd/src/include/ap_config_auto.h index 34b9b0e8e69..8c460078960 100644 --- a/usr.sbin/httpd/src/include/ap_config_auto.h +++ b/usr.sbin/httpd/src/include/ap_config_auto.h @@ -66,6 +66,26 @@ #define AP_OFF_T_IS_QUAD 1 #endif +/* build flag: -DINET6 */ +#ifndef INET6 +#define INET6 1 +#endif + +/* build flag: -Dss_family=__ss_family */ +#ifndef ss_family +#define ss_family __ss_family +#endif + +/* build flag: -Dss_len=__ss_len */ +#ifndef ss_len +#define ss_len __ss_len +#endif + +/* build flag: -DHAVE_SOCKADDR_LEN */ +#ifndef HAVE_SOCKADDR_LEN +#define HAVE_SOCKADDR_LEN 1 +#endif + /* build flag: -DMOD_SSL=208116 */ #ifndef MOD_SSL #define MOD_SSL 208116 diff --git a/usr.sbin/httpd/src/include/http_conf_globals.h b/usr.sbin/httpd/src/include/http_conf_globals.h index 2559a0d15aa..14ff3b2afcd 100644 --- a/usr.sbin/httpd/src/include/http_conf_globals.h +++ b/usr.sbin/httpd/src/include/http_conf_globals.h @@ -1,4 +1,4 @@ -/* $OpenBSD: http_conf_globals.h,v 1.16 2006/02/22 15:07:12 henning Exp $ */ +/* $OpenBSD: http_conf_globals.h,v 1.17 2008/05/09 08:06:28 mbalmer Exp $ */ /* ==================================================================== * The Apache Software License, Version 1.1 @@ -83,7 +83,8 @@ extern API_VAR_EXPORT int ap_max_rss_per_child; extern API_VAR_EXPORT int ap_max_stack_per_child; extern API_VAR_EXPORT int ap_threads_per_child; extern API_VAR_EXPORT int ap_excess_requests_per_child; -extern API_VAR_EXPORT struct in_addr ap_bind_address; +extern API_VAR_EXPORT struct sockaddr_storage ap_bind_address; +extern API_VAR_EXPORT int ap_default_family; extern listen_rec *ap_listeners; extern API_VAR_EXPORT int ap_daemons_to_start; extern API_VAR_EXPORT int ap_daemons_min_free; diff --git a/usr.sbin/httpd/src/include/http_vhost.h b/usr.sbin/httpd/src/include/http_vhost.h index 0677e51654f..1ff99faee40 100644 --- a/usr.sbin/httpd/src/include/http_vhost.h +++ b/usr.sbin/httpd/src/include/http_vhost.h @@ -1,4 +1,4 @@ -/* $OpenBSD: http_vhost.h,v 1.7 2005/03/28 23:26:51 niallo Exp $ */ +/* $OpenBSD: http_vhost.h,v 1.8 2008/05/09 08:06:28 mbalmer Exp $ */ /* ==================================================================== * The Apache Software License, Version 1.1 @@ -77,7 +77,7 @@ API_EXPORT(const char *) ap_parse_vhost_addrs(pool *p, const char *hostname, /* handle NameVirtualHost directive */ API_EXPORT_NONSTD(const char *) ap_set_name_virtual_host (cmd_parms *cmd, - void *dummy, char *arg); + void *dummy, char *h, char *p); /* given an ip address only, give our best guess as to what vhost it is */ API_EXPORT(void) ap_update_vhost_given_ip(conn_rec *conn); diff --git a/usr.sbin/httpd/src/include/httpd.h b/usr.sbin/httpd/src/include/httpd.h index 79ee6ef0493..957d1995cf5 100644 --- a/usr.sbin/httpd/src/include/httpd.h +++ b/usr.sbin/httpd/src/include/httpd.h @@ -1,4 +1,4 @@ -/* $OpenBSD: httpd.h,v 1.27 2006/02/22 15:07:12 henning Exp $ */ +/* $OpenBSD: httpd.h,v 1.28 2008/05/09 08:06:28 mbalmer Exp $ */ /* ==================================================================== * The Apache Software License, Version 1.1 @@ -66,6 +66,13 @@ extern "C" { #endif /* + * Define APACHE6 so that additional modules depending on Apache can + * tell if this a pacthed apache-1.3.*. With this definition apache6 + * is working together with e.g. the ap-perl module in NetBSD. + */ +#define APACHE6 1 + +/* * httpd.h: header for simple (ha! not anymore) http daemon */ @@ -847,8 +854,8 @@ struct conn_rec { /* Who is the client? */ - struct sockaddr_in local_addr; /* local address */ - struct sockaddr_in remote_addr; /* remote address */ + struct sockaddr_storage local_addr; /* local address */ + struct sockaddr_storage remote_addr; /* remote address */ char *remote_ip; /* Client's IP address */ char *remote_host; /* Client's DNS name, if known. * NULL if DNS hasn't been checked, @@ -888,9 +895,9 @@ struct conn_rec { typedef struct server_addr_rec server_addr_rec; struct server_addr_rec { server_addr_rec *next; - struct in_addr host_addr; /* The bound address, for this server */ + struct sockaddr_storage host_addr; /* The bound address, for this server */ unsigned short host_port; /* The bound port, for this server */ - char *virthost; /* The name given in <VirtualHost> */ + char *virthost; /* The name given in <VirtualHost> */ }; struct server_rec { @@ -942,7 +949,7 @@ struct server_rec { array_header *names; /* Normal names for ServerAlias servers */ array_header *wild_names;/* Wildcarded names for ServerAlias servers */ - uid_t server_uid; /* effective user id when calling exec wrapper */ + uid_t server_uid; /* effective user id when calling exec wrapper */ gid_t server_gid; /* effective group id when calling exec wrapper */ int limit_req_line; /* limit on size of the HTTP request line */ @@ -950,20 +957,18 @@ struct server_rec { int limit_req_fields; /* limit on number of request header fields */ ap_ctx *ctx; - }; +}; - /* These are more like real hosts than virtual hosts */ - struct listen_rec { +/* These are more like real hosts than virtual hosts */ +struct listen_rec { listen_rec *next; - struct sockaddr_in local_addr; /* local IP address and port */ + struct sockaddr_storage local_addr; /* local IP address and port */ int fd; int used; /* Only used during restart */ /* more stuff here, like which protocol is bound to the port */ }; -/* Prototypes for utilities... util.c. - */ - +/* Prototypes for utilities... util.c. */ extern void ap_util_init(void); /* Time */ @@ -1098,7 +1103,7 @@ API_EXPORT(char *) ap_os_canonical_filename(pool *p, const char *file); API_EXPORT(char *) ap_get_local_host(pool *); -API_EXPORT(unsigned long) ap_get_virthost_addr(char *hostname, +API_EXPORT(struct sockaddr *) ap_get_virthost_addr(char *hostname, unsigned short *port); extern API_VAR_EXPORT time_t ap_restart_time; diff --git a/usr.sbin/httpd/src/main/http_config.c b/usr.sbin/httpd/src/main/http_config.c index 9b3e53b5e62..dfd17b48ae3 100644 --- a/usr.sbin/httpd/src/main/http_config.c +++ b/usr.sbin/httpd/src/main/http_config.c @@ -1,4 +1,4 @@ -/* $OpenBSD: http_config.c,v 1.18 2007/11/19 14:59:10 robert Exp $ */ +/* $OpenBSD: http_config.c,v 1.19 2008/05/09 08:06:28 mbalmer Exp $ */ /* ==================================================================== * The Apache Software License, Version 1.1 @@ -1556,7 +1556,6 @@ static void init_config_globals(pool *p) ap_max_nofile_per_child = DEFAULT_MAX_NOFILE_PER_CHILD; ap_max_rss_per_child = DEFAULT_MAX_RSS_PER_CHILD; ap_max_stack_per_child = DEFAULT_MAX_STACK_PER_CHILD; - ap_bind_address.s_addr = htonl(INADDR_ANY); ap_listeners = NULL; ap_listenbacklog = DEFAULT_LISTENBACKLOG; ap_extended_status = 0; @@ -1589,7 +1588,13 @@ static server_rec *init_server_config(pool *p) s->next = NULL; s->addrs = ap_pcalloc(p, sizeof(server_addr_rec)); /* NOT virtual host; don't match any real network interface */ - s->addrs->host_addr.s_addr = htonl(INADDR_ANY); + memset(&s->addrs->host_addr, 0, sizeof(s->addrs->host_addr)); +#if 0 + s->addrs->host_addr.ss_family = ap_default_family; /* XXX: needed?, XXX: PF_xxx can be different from AF_xxx */ +#endif +#ifdef HAVE_SOCKADDR_LEN + s->addrs->host_addr.ss_len = sizeof(s->addrs->host_addr); /* XXX: needed ? */ +#endif s->addrs->host_port = 0; /* matches any port */ s->addrs->virthost = ""; /* must be non-NULL */ s->names = s->wild_names = NULL; @@ -1606,21 +1611,33 @@ static server_rec *init_server_config(pool *p) static void default_listeners(pool *p, server_rec *s) { listen_rec *new; + struct addrinfo hints, *res0, *res; + int gai; + char servbuf[NI_MAXSERV]; if (ap_listeners != NULL) { return; } + ap_snprintf(servbuf, sizeof(servbuf), "%d", s->port ? s->port : DEFAULT_HTTP_PORT); + memset (&hints, 0, sizeof(hints)); + hints.ai_family = ap_default_family; + hints.ai_socktype = SOCK_STREAM; + hints.ai_flags = AI_PASSIVE; + gai = getaddrinfo(NULL, servbuf, &hints, &res0); + if (gai){ + fprintf(stderr, "default_listeners(): getaddrinfo(PASSIVE) for family %u: %s\n", + ap_default_family, gai_strerror(gai)); + exit (1); + } /* allocate a default listener */ new = ap_pcalloc(p, sizeof(listen_rec)); - new->local_addr.sin_family = AF_INET; - new->local_addr.sin_addr = ap_bind_address; - /* Buck ugly cast to get around terniary op bug in some (MS) compilers */ - new->local_addr.sin_port = htons((unsigned short)(s->port ? s->port - : DEFAULT_HTTP_PORT)); + memcpy(&new->local_addr, res0->ai_addr, res0->ai_addrlen); new->fd = -1; new->used = 0; new->next = NULL; ap_listeners = new; + + freeaddrinfo(res0); } diff --git a/usr.sbin/httpd/src/main/http_core.c b/usr.sbin/httpd/src/main/http_core.c index 7248c212b75..d5c543d0c39 100644 --- a/usr.sbin/httpd/src/main/http_core.c +++ b/usr.sbin/httpd/src/main/http_core.c @@ -1,4 +1,4 @@ -/* $OpenBSD: http_core.c,v 1.22 2007/08/24 11:31:29 mbalmer Exp $ */ +/* $OpenBSD: http_core.c,v 1.23 2008/05/09 08:06:28 mbalmer Exp $ */ /* ==================================================================== * The Apache Software License, Version 1.1 @@ -536,7 +536,7 @@ API_EXPORT(const char *) ap_auth_nonce(request_rec *r) * file if you care. So the adhoc value should do. */ return ap_psprintf(r->pool,"%pp%pp%pp%pp%pp", - (void *)&((r->connection->local_addr).sin_addr ), + (void *)&(r->connection->local_host), (void *)ap_user_name, (void *)ap_listeners, (void *)ap_server_argv0, @@ -617,7 +617,9 @@ API_EXPORT(char *) ap_response_code_string(request_rec *r, int error_index) */ static ap_inline void do_double_reverse (conn_rec *conn) { - struct hostent *hptr; + struct addrinfo hints, *res, *res0; + char hostbuf1[128], hostbuf2[128]; /* INET6_ADDRSTRLEN(=46) is enough */ + int ok = 0; if (conn->double_reverse) { /* already done */ @@ -629,30 +631,49 @@ static ap_inline void do_double_reverse (conn_rec *conn) conn->remote_host = ""; /* prevent another lookup */ return; } - hptr = gethostbyname(conn->remote_host); - if (hptr) { - char **haddr; - - for (haddr = hptr->h_addr_list; *haddr; haddr++) { - if (((struct in_addr *)(*haddr))->s_addr - == conn->remote_addr.sin_addr.s_addr) { - conn->double_reverse = 1; - return; - } + memset(&hints, 0, sizeof(hints)); + hints.ai_family = PF_UNSPEC; + hints.ai_socktype = SOCK_STREAM; + if (getaddrinfo(conn->remote_host, NULL, &hints, &res0)) { + conn->double_reverse = -1; + return; + } + for (res = res0; res; res = res->ai_next) { + if (res->ai_addr->sa_family != conn->remote_addr.ss_family || + !(res->ai_family == AF_INET + || res->ai_family == AF_INET6 + ) + ) + continue; +#ifndef HAVE_SOCKADDR_LEN + if (res->ai_addrlen != SA_LEN((struct sockaddr *)&conn->remote_addr)) +#else + if (res->ai_addr->sa_len != conn->remote_addr.ss_len) +#endif + continue; + if (getnameinfo(res->ai_addr, res->ai_addrlen, + hostbuf1, sizeof(hostbuf1), NULL, 0, + NI_NUMERICHOST)) + continue; + if (getnameinfo(((struct sockaddr *)&conn->remote_addr), res->ai_addrlen, + hostbuf2, sizeof(hostbuf2), NULL, 0, + NI_NUMERICHOST)) + continue; + if (strcmp(hostbuf1, hostbuf2) == 0){ + ok = 1; + break; } } - conn->double_reverse = -1; - /* invalidate possible reverse-resolved hostname if forward lookup fails */ - conn->remote_host = ""; + conn->double_reverse = ok ? 1 : -1; + freeaddrinfo(res0); } API_EXPORT(const char *) ap_get_remote_host(conn_rec *conn, void *dir_config, int type) { - struct in_addr *iaddr; - struct hostent *hptr; int hostname_lookups; int old_stat = SERVER_DEAD; /* we shouldn't ever be in this state */ + char hostnamebuf[MAXHOSTNAMELEN]; /* If we haven't checked the host name, and we want to */ if (dir_config) { @@ -674,10 +695,14 @@ API_EXPORT(const char *) ap_get_remote_host(conn_rec *conn, void *dir_config, || hostname_lookups != HOSTNAME_LOOKUP_OFF)) { old_stat = ap_update_child_status(conn->child_num, SERVER_BUSY_DNS, (request_rec*)NULL); - iaddr = &(conn->remote_addr.sin_addr); - hptr = gethostbyaddr((char *)iaddr, sizeof(struct in_addr), AF_INET); - if (hptr != NULL) { - conn->remote_host = ap_pstrdup(conn->pool, (void *)hptr->h_name); + if (!getnameinfo((struct sockaddr *)&conn->remote_addr, +#ifndef SIN6_LEN + SA_LEN((struct sockaddr *)&conn->remote_addr), +#else + conn->remote_addr.ss_len, +#endif + hostnamebuf, sizeof(hostnamebuf), NULL, 0, 0)) { + conn->remote_host = ap_pstrdup(conn->pool, (void *)hostnamebuf); ap_str_tolower(conn->remote_host); if (hostname_lookups == HOSTNAME_LOOKUP_DOUBLE) { @@ -755,6 +780,7 @@ API_EXPORT(const char *) ap_get_server_name(request_rec *r) { conn_rec *conn = r->connection; core_dir_config *d; + char hbuf[MAXHOSTNAMELEN]; d = (core_dir_config *)ap_get_module_config(r->per_dir_config, &core_module); @@ -764,23 +790,22 @@ API_EXPORT(const char *) ap_get_server_name(request_rec *r) } if (d->use_canonical_name == USE_CANONICAL_NAME_DNS) { if (conn->local_host == NULL) { - struct in_addr *iaddr; - struct hostent *hptr; int old_stat; old_stat = ap_update_child_status(conn->child_num, SERVER_BUSY_DNS, r); - iaddr = &(conn->local_addr.sin_addr); - hptr = gethostbyaddr((char *)iaddr, sizeof(struct in_addr), - AF_INET); - if (hptr != NULL) { - conn->local_host = ap_pstrdup(conn->pool, - (void *)hptr->h_name); - ap_str_tolower(conn->local_host); - } - else { - conn->local_host = ap_pstrdup(conn->pool, - r->server->server_hostname); + if (getnameinfo((struct sockaddr *)&conn->local_addr, +#ifndef SIN6_LEN + SA_LEN((struct sockaddr *)&conn->local_addr), +#else + conn->local_addr.ss_len, +#endif + hbuf, sizeof(hbuf), NULL, 0, 0) == 0) { + conn->local_host = ap_pstrdup(conn->pool, hbuf); + } else { + conn->local_host = ap_pstrdup(conn->pool, + r->server->server_hostname); } + ap_str_tolower(conn->local_host); (void) ap_update_child_status(conn->child_num, old_stat, r); } return conn->local_host; @@ -792,29 +817,20 @@ API_EXPORT(const char *) ap_get_server_name(request_rec *r) API_EXPORT(unsigned) ap_get_server_port(const request_rec *r) { unsigned port; - unsigned cport = ntohs(r->connection->local_addr.sin_port); core_dir_config *d = (core_dir_config *)ap_get_module_config(r->per_dir_config, &core_module); + + port = r->server->port ? r->server->port : ap_default_port(r); if (d->use_canonical_name == USE_CANONICAL_NAME_OFF - || d->use_canonical_name == USE_CANONICAL_NAME_DNS) { - - /* With UseCanonicalName Off Apache will form self-referential - * URLs using the hostname and port supplied by the client if - * any are supplied (otherwise it will use the canonical name). - */ - port = r->parsed_uri.port_str ? r->parsed_uri.port : - cport ? cport : - r->server->port ? r->server->port : - ap_default_port(r); - } else { /* d->use_canonical_name == USE_CANONICAL_NAME_ON */ - port = r->server->port ? r->server->port : - cport ? cport : - ap_default_port(r); + || d->use_canonical_name == USE_CANONICAL_NAME_DNS) { + return r->hostname + ? ntohs(((struct sockaddr_in *)&r->connection->local_addr)->sin_port) + : port; } - - /* default */ - return port; + return r->hostname + ? ntohs(((struct sockaddr_in *)&r->connection->local_addr)->sin_port) + : port; } API_EXPORT(char *) ap_construct_url(pool *p, const char *uri, @@ -2380,12 +2396,25 @@ static const char *set_limit_nofile(cmd_parms *cmd, core_dir_config *conf, static const char *set_bind_address(cmd_parms *cmd, void *dummy, char *arg) { + struct addrinfo hints, *res; + struct sockaddr *sa; + size_t sa_len; + int error; const char *err = ap_check_cmd_context(cmd, GLOBAL_ONLY); if (err != NULL) { return err; } - ap_bind_address.s_addr = ap_get_virthost_addr(arg, NULL); + if (strcmp(arg, "*") == 0) + arg = NULL; + + sa = ap_get_virthost_addr(arg, NULL); +#ifdef HAVE_SOCKADDR_LEN + sa_len = sa->sa_len; +#else + sa_len = SA_LEN(sa); +#endif + memcpy(&ap_bind_address, &sa, sa_len); return NULL; } @@ -2402,48 +2431,78 @@ static const char *set_acceptfilter(cmd_parms *cmd, void *dummy, int flag) return NULL; } -static const char *set_listener(cmd_parms *cmd, void *dummy, char *ips) +static const char *set_listener(cmd_parms *cmd, void *dummy, char *h, char *p) { listen_rec *new; - char *ports, *endptr; - long port; + char *host, *port, *endptr; + struct addrinfo hints, *res; + int error; const char *err = ap_check_cmd_context(cmd, GLOBAL_ONLY); if (err != NULL) { return err; } - ports = strchr(ips, ':'); - if (ports != NULL) { - if (ports == ips) { + host = port = NULL; + if (!p) { + port = strrchr(h, ':'); + if (port != NULL) { + if (port == h) { return "Missing IP address"; } - else if (ports[1] == '\0') { + else if (port[1] == '\0') { return "Address must end in :<port-number>"; } - *(ports++) = '\0'; - } - else { - ports = ips; - } + *(port++) = '\0'; + if (*h) + host = h; + } else { + host = NULL; + port = h; + } + } else { + host = h; + port = p; + } + + /* strip [] for ipv6 before calling getaddrinfo */ + if (host && host[0] == '[') { + if (strlen(host) < 2 || host[strlen(host) - 1] != ']') + return "Malformed IPv6 Address in :<host>"; + host[strlen(host) - 1] = 0; + host++; + } + + if (host && strcmp(host, "*") == 0) + host = NULL; + + new = ap_pcalloc(cmd->pool, sizeof(listen_rec)); + + memset(&hints, 0, sizeof(hints)); + hints.ai_family = host ? PF_UNSPEC : ap_default_family; + hints.ai_flags = AI_PASSIVE; + hints.ai_socktype = SOCK_STREAM; + error = getaddrinfo(host, port, &hints, &res); + if (error || !res) { + fprintf(stderr, "could not resolve "); + if (host) + fprintf(stderr, "host \"%s\" ", host); + if (port) + fprintf(stderr, "port \"%s\" ", port); + fprintf(stderr, "--- %s\n", gai_strerror(error)); + exit(1); + } + if (res->ai_next) { + if (host) + fprintf(stderr, "host \"%s\" ", host); + if (port) + fprintf(stderr, "port \"%s\" ", port); + fprintf(stderr, "resolved to multiple addresses, ambiguous.\n"); + exit(1); + } + + memcpy(&new->local_addr, res->ai_addr, res->ai_addrlen); - new=ap_pcalloc(cmd->pool, sizeof(listen_rec)); - new->local_addr.sin_family = AF_INET; - if (ports == ips) { /* no address */ - new->local_addr.sin_addr.s_addr = htonl(INADDR_ANY); - } - else { - new->local_addr.sin_addr.s_addr = ap_get_virthost_addr(ips, NULL); - } - errno = 0; /* clear errno before calling strtol */ - port = ap_strtol(ports, &endptr, 10); - if (errno /* some sort of error */ - || (endptr && *endptr) /* make sure no trailing characters */ - || port < 1 || port > 65535) /* underflow/overflow */ - { - return "Missing, invalid, or non-numeric port"; - } - new->local_addr.sin_port = htons((unsigned short)port); new->fd = -1; new->used = 0; new->next = ap_listeners; @@ -3198,7 +3257,7 @@ static const command_rec core_cmds[] = { OR_ALL, TAKE12, "soft/hard limits for max number of files per process" }, { "BindAddress", set_bind_address, NULL, RSRC_CONF, TAKE1, "'*', a numeric IP address, or the name of a host with a unique IP address"}, -{ "Listen", set_listener, NULL, RSRC_CONF, TAKE1, +{ "Listen", set_listener, NULL, RSRC_CONF, TAKE12, "A port number or a numeric IP address and a port number"}, { "SendBufferSize", set_send_buffer_size, NULL, RSRC_CONF, TAKE1, "Send buffer size in bytes"}, @@ -3226,7 +3285,7 @@ static const command_rec core_cmds[] = { "Name of the config file to be included" }, { "LogLevel", set_loglevel, NULL, RSRC_CONF, TAKE1, "Level of verbosity in error logging" }, -{ "NameVirtualHost", ap_set_name_virtual_host, NULL, RSRC_CONF, TAKE1, +{ "NameVirtualHost", ap_set_name_virtual_host, NULL, RSRC_CONF, TAKE12, "A numeric IP address:port, or the name of a host" }, { "CGICommandArgs", set_cgi_command_args, NULL, OR_OPTIONS, FLAG, "Allow or Disallow CGI requests to pass args on the command line" }, diff --git a/usr.sbin/httpd/src/main/http_main.c b/usr.sbin/httpd/src/main/http_main.c index e336256bc4d..ace88475e8d 100644 --- a/usr.sbin/httpd/src/main/http_main.c +++ b/usr.sbin/httpd/src/main/http_main.c @@ -1,4 +1,4 @@ -/* $OpenBSD: http_main.c,v 1.49 2007/08/09 10:44:54 martynas Exp $ */ +/* $OpenBSD: http_main.c,v 1.50 2008/05/09 08:06:28 mbalmer Exp $ */ /* ==================================================================== * The Apache Software License, Version 1.1 @@ -168,7 +168,8 @@ API_VAR_EXPORT char *ap_pid_fname=NULL; API_VAR_EXPORT char *ap_scoreboard_fname=NULL; API_VAR_EXPORT char *ap_lock_fname=NULL; API_VAR_EXPORT char *ap_server_argv0=NULL; -API_VAR_EXPORT struct in_addr ap_bind_address={0}; +API_VAR_EXPORT int ap_default_family = PF_INET; +API_VAR_EXPORT struct sockaddr_storage ap_bind_address; API_VAR_EXPORT int ap_daemons_to_start=0; API_VAR_EXPORT int ap_daemons_min_free=0; API_VAR_EXPORT int ap_daemons_max_free=0; @@ -1807,11 +1808,13 @@ static int init_suexec(void) static conn_rec *new_connection(pool *p, server_rec *server, BUFF *inout, - const struct sockaddr_in *remaddr, - const struct sockaddr_in *saddr, + const struct sockaddr *remaddr, + const struct sockaddr *saddr, int child_num) { conn_rec *conn = (conn_rec *) ap_pcalloc(p, sizeof(conn_rec)); + char hostnamebuf[MAXHOSTNAMELEN]; + size_t addr_len; /* Got a connection structure, so initialize what fields we can * (the rest are zeroed out by pcalloc). @@ -1820,17 +1823,29 @@ static conn_rec *new_connection(pool *p, server_rec *server, BUFF *inout, conn->child_num = child_num; conn->pool = p; - conn->local_addr = *saddr; - conn->local_ip = ap_pstrdup(conn->pool, - inet_ntoa(conn->local_addr.sin_addr)); +#ifndef SIN6_LEN + addr_len = SA_LEN(saddr); +#else + addr_len = saddr->sa_len; +#endif + memcpy(&conn->local_addr, saddr, addr_len); + getnameinfo((struct sockaddr *)&conn->local_addr, addr_len, + hostnamebuf, sizeof(hostnamebuf), NULL, 0, NI_NUMERICHOST); + conn->local_ip = ap_pstrdup(conn->pool, hostnamebuf); conn->server = server; /* just a guess for now */ ap_update_vhost_given_ip(conn); conn->base_server = conn->server; conn->client = inout; - conn->remote_addr = *remaddr; - conn->remote_ip = ap_pstrdup(conn->pool, - inet_ntoa(conn->remote_addr.sin_addr)); +#ifndef SIN6_LEN + addr_len = SA_LEN(remaddr); +#else + addr_len = remaddr->sa_len; +#endif + memcpy(&conn->remote_addr, remaddr, addr_len); + getnameinfo((struct sockaddr *)&conn->remote_addr, addr_len, + hostnamebuf, sizeof(hostnamebuf), NULL, 0, NI_NUMERICHOST); + conn->remote_ip = ap_pstrdup(conn->pool, hostnamebuf); conn->ctx = ap_ctx_new(conn->pool); /* @@ -1876,21 +1891,42 @@ static void sock_disable_nagle(int s, struct sockaddr_in *sin_client) } } -static int make_sock(pool *p, const struct sockaddr_in *server) +static int make_sock(pool *p, const struct sockaddr *server) { int s; int one = 1; - char addr[512]; - - if (server->sin_addr.s_addr != htonl(INADDR_ANY)) - ap_snprintf(addr, sizeof(addr), "address %s port %d", - inet_ntoa(server->sin_addr), ntohs(server->sin_port)); - else - ap_snprintf(addr, sizeof(addr), "port %d", ntohs(server->sin_port)); + char addr[INET6_ADDRSTRLEN + 128]; + char a0[INET6_ADDRSTRLEN]; + char p0[NI_MAXSERV]; + + switch(server->sa_family){ + case AF_INET: + case AF_INET6: + break; + default: + ap_log_error(APLOG_MARK, APLOG_CRIT, server_conf, + "make_sock: unsupported address family %u", + server->sa_family); + ap_unblock_alarms(); + exit(1); + } + + getnameinfo(server, +#ifndef SIN6_LEN + SA_LEN(server), +#else + server->sa_len, +#endif + a0, sizeof(a0), p0, sizeof(p0), NI_NUMERICHOST | NI_NUMERICSERV); + ap_snprintf(addr, sizeof(addr), "address %s port %s", a0, p0); +#ifdef MPE + if (atoi(p0) < 1024) + privport++; +#endif /* note that because we're about to slack we don't use psocket */ ap_block_alarms(); - if ((s = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP)) == -1) { + if ((s = socket(server->sa_family, SOCK_STREAM, IPPROTO_TCP)) == -1) { ap_log_error(APLOG_MARK, APLOG_CRIT, server_conf, "make_sock: failed to get a socket for %s", addr); @@ -1951,8 +1987,12 @@ static int make_sock(pool *p, const struct sockaddr_in *server) } } - - if (bind(s, (struct sockaddr *) server, sizeof(struct sockaddr_in)) == -1) { +#ifndef SIN6_LEN + if (bind(s, server, SA_LEN(server)) == -1) +#else + if (bind(s, server, server->sa_len) == -1) +#endif + { ap_log_error(APLOG_MARK, APLOG_CRIT, server_conf, "make_sock: could not bind to %s", addr); @@ -2065,15 +2105,17 @@ static void setup_listeners(pool *p) for (;;) { fd = find_listener(lr); if (fd < 0) { - fd = make_sock(p, &lr->local_addr); + fd = make_sock(p, (struct sockaddr *)&lr->local_addr); } else { ap_note_cleanups_for_socket_ex(p, fd, 1); } /* if we get here, (fd >= 0) && (fd < FD_SETSIZE) */ - FD_SET(fd, &listenfds); - if (fd > listenmaxfd) - listenmaxfd = fd; + if (fd >= 0) { + FD_SET(fd, &listenfds); + if (fd > listenmaxfd) + listenmaxfd = fd; + } lr->fd = fd; if (lr->next == NULL) break; @@ -2231,8 +2273,8 @@ API_EXPORT(void) ap_child_terminate(request_rec *r) static void child_main(int child_num_arg) { NET_SIZE_T clen; - struct sockaddr sa_server; - struct sockaddr sa_client; + struct sockaddr_storage sa_server; + struct sockaddr_storage sa_client; listen_rec *lr; struct rlimit rlp; @@ -2422,7 +2464,7 @@ static void child_main(int child_num_arg) usr1_just_die = 0; for (;;) { clen = sizeof(sa_client); - csd = ap_accept(sd, &sa_client, &clen); + csd = ap_accept(sd, (struct sockaddr *)&sa_client, &clen); if (csd >= 0 || errno != EINTR) break; if (deferred_die) { @@ -2526,7 +2568,7 @@ static void child_main(int child_num_arg) */ clen = sizeof(sa_server); - if (getsockname(csd, &sa_server, &clen) < 0) { + if (getsockname(csd, (struct sockaddr *)&sa_server, &clen) < 0) { ap_log_error(APLOG_MARK, APLOG_DEBUG, server_conf, "getsockname, client %pA probably dropped the " "connection", @@ -2545,8 +2587,8 @@ static void child_main(int child_num_arg) ap_bpushfd(conn_io, csd, dupped_csd); current_conn = new_connection(ptrans, server_conf, conn_io, - (struct sockaddr_in *) &sa_client, - (struct sockaddr_in *) &sa_server, + (struct sockaddr *)&sa_client, + (struct sockaddr *)&sa_server, my_child_num); /* @@ -3206,7 +3248,7 @@ int REALMAIN(int argc, char *argv[]) ap_setup_prelinked_modules(); while ((c = getopt(argc, argv, - "D:C:c:xXd:Ff:vVlLR:StThu" + "D:C:c:xXd:Ff:vVlLR:StThu46" #ifdef DEBUG_SIGSTOP "Z:" #endif @@ -3272,6 +3314,13 @@ int REALMAIN(int argc, char *argv[]) break; case 'h': usage(argv[0]); + break; + case '4': + ap_default_family = PF_INET; + break; + case '6': + ap_default_family = PF_INET6; + break; case 'u': ap_server_chroot = 0; break; @@ -3307,9 +3356,10 @@ int REALMAIN(int argc, char *argv[]) else { conn_rec *conn; request_rec *r; - struct sockaddr sa_server, sa_client; BUFF *cio; + struct sockaddr_storage sa_server, sa_client; NET_SIZE_T l; + char servbuf[NI_MAXSERV]; ap_set_version(); /* Yes this is called twice. */ @@ -3338,25 +3388,32 @@ int REALMAIN(int argc, char *argv[]) sock_out = fileno(stdout); l = sizeof(sa_client); - if ((getpeername(sock_in, &sa_client, &l)) < 0) { + if ((getpeername(sock_in, (struct sockaddr *)&sa_client, &l)) < 0) { /* get peername will fail if the input isn't a socket */ perror("getpeername"); memset(&sa_client, '\0', sizeof(sa_client)); } l = sizeof(sa_server); - if (getsockname(sock_in, &sa_server, &l) < 0) { + if (getsockname(sock_in, (struct sockaddr *)&sa_server, &l) < 0) { perror("getsockname"); fprintf(stderr, "Error getting local address\n"); exit(1); } - server_conf->port = ntohs(((struct sockaddr_in *) &sa_server)->sin_port); + if (getnameinfo(((struct sockaddr *)&sa_server), l, + NULL, 0, servbuf, sizeof(servbuf), + NI_NUMERICSERV)){ + fprintf(stderr, "getnameinfo(): family=%d\n", sa_server.ss_family); + exit(1); + } + servbuf[sizeof(servbuf)-1] = '\0'; + server_conf->port = atoi(servbuf); cio = ap_bcreate(ptrans, B_RDWR | B_SOCKET); cio->fd = sock_out; cio->fd_in = sock_in; conn = new_connection(ptrans, server_conf, cio, - (struct sockaddr_in *) &sa_client, - (struct sockaddr_in *) &sa_server, -1); + (struct sockaddr *)&sa_client, + (struct sockaddr *)&sa_server, -1); while ((r = ap_read_request(conn)) != NULL) { diff --git a/usr.sbin/httpd/src/main/http_vhost.c b/usr.sbin/httpd/src/main/http_vhost.c index 3d9df203ac2..c659dbb1a7c 100644 --- a/usr.sbin/httpd/src/main/http_vhost.c +++ b/usr.sbin/httpd/src/main/http_vhost.c @@ -165,78 +165,108 @@ API_EXPORT(void) ap_init_vhost_config(pool *p) * *paddr is the variable used to keep track of **paddr between calls * port is the default port to assume */ -static const char *get_addresses(pool *p, char *w, server_addr_rec ***paddr, - unsigned port) +static const char *get_addresses(pool *p, char *w, char *pstr, + server_addr_rec ***paddr, unsigned port) { - struct hostent *hep; - unsigned long my_addr; + struct addrinfo hints, *res, *res0; server_addr_rec *sar; - char *t; - int i, is_an_ip_addr; + char *t = NULL, *u = NULL, *v = NULL; + char *hoststr = NULL, *portstr = NULL; + char portpool[10]; + int error; + char servbuf[NI_MAXSERV]; - if (*w == 0) + if (w == 0 || *w == 0) return NULL; - t = strchr(w, ':'); - if (t) { - if (strcmp(t + 1, "*") == 0) { - port = 0; + portstr = portpool; + ap_snprintf(portpool, sizeof(portpool), "%u", port); + if (!pstr) { + v = w; + u = NULL; + if (*w == '['){ + u = strrchr(w, ']'); + if (u) { /* [host]:port or [host] */ + w++; + *u = '\0'; + v = u + 1; + } } - else if ((i = atoi(t + 1))) { - port = i; + /* w uv , w=v , w=v */ + /* u!=0: [host]:port , u==0: [host:port , host */ + t = strchr(v, ':'); + if (t != NULL && strchr(t+1, ':') == NULL) { + /* [host]:port-w/o-colons, host-without-colons:port-w/o-colons */ + *t = '\0'; + portstr = t + 1; } else { - return ":port must be numeric"; + portstr = "0"; } - *t = 0; + } else { + portstr = pstr; } - is_an_ip_addr = 0; - if (strcmp(w, "*") == 0) { - my_addr = htonl(INADDR_ANY); - is_an_ip_addr = 1; + memset(&hints, 0, sizeof(hints)); + hints.ai_socktype = SOCK_STREAM; + if (strcmp(w, "*") == 0 || strlen(w) == 0) { + hoststr = NULL; + hints.ai_family = PF_UNSPEC; + hints.ai_flags = AI_PASSIVE; } - else if (strcasecmp(w, "_default_") == 0 - || strcmp(w, "255.255.255.255") == 0) { - my_addr = DEFAULT_VHOST_ADDR; - is_an_ip_addr = 1; + else if (strcasecmp(w, "_default4_") == 0 || + ((ap_default_family == PF_INET + || ap_default_family == PF_UNSPEC + ) && strcasecmp(w, "_default_") == 0)){ + hoststr = "255.255.255.255"; + hints.ai_family = PF_INET; } - else if ((my_addr = ap_inet_addr(w)) != INADDR_NONE) { - is_an_ip_addr = 1; + else if (strcasecmp(w, "_default6_") == 0 || + ((ap_default_family == PF_INET6 + || ap_default_family == PF_UNSPEC + ) && strcasecmp(w, "_default_") == 0)){ + hoststr = "ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff"; + hints.ai_family = PF_INET6; } - if (is_an_ip_addr) { - sar = ap_pcalloc(p, sizeof(server_addr_rec)); - **paddr = sar; - *paddr = &sar->next; - sar->host_addr.s_addr = my_addr; - sar->host_port = port; - sar->virthost = ap_pstrdup(p, w); - if (t != NULL) - *t = ':'; - return NULL; + else{ + hoststr = w; + hints.ai_family = PF_UNSPEC; } - hep = gethostbyname(w); - - if ((!hep) || (hep->h_addrtype != AF_INET || !hep->h_addr_list[0])) { + error = getaddrinfo(hoststr, portstr, &hints, &res0); + if (error || !res0) { ap_log_error(APLOG_MARK, APLOG_NOERRNO|APLOG_ERR, NULL, - "Cannot resolve host name %s --- ignoring!", w); - if (t != NULL) - *t = ':'; + "Cannot resolve host %s port %s --- ignoring!", hoststr, portstr); + if (t != NULL) *t = ':'; + if (u != NULL) *u = ']'; return NULL; } - - for (i = 0; hep->h_addr_list[i]; ++i) { + for (res=res0; res; res=res->ai_next) { + switch (res->ai_addr->sa_family) { + case AF_INET: + case AF_INET6: + break; + default: + ap_log_error(APLOG_MARK, APLOG_NOERRNO|APLOG_ERR, NULL, + "Unsupported address family %u, for host %s port %s --- ignoring!", + res->ai_addr->sa_family, hoststr, portstr); + continue; + } sar = ap_pcalloc(p, sizeof(server_addr_rec)); **paddr = sar; *paddr = &sar->next; - sar->host_addr = *(struct in_addr *) hep->h_addr_list[i]; - sar->host_port = port; + memcpy(&sar->host_addr, res->ai_addr, res->ai_addrlen); + if (getnameinfo(res->ai_addr, res->ai_addrlen, NULL, 0, servbuf, + sizeof(servbuf), NI_NUMERICSERV) == 0) + sar->host_port = atoi(servbuf); + else + sar->host_port = 0; sar->virthost = ap_pstrdup(p, w); } - if (t != NULL) - *t = ':'; + freeaddrinfo(res0); + if (t != NULL) *t = ':'; + if (u != NULL) *u = ']'; return NULL; } @@ -250,7 +280,8 @@ API_EXPORT(const char *) ap_parse_vhost_addrs(pool *p, const char *hostname, ser /* start the list of addreses */ addrs = &s->addrs; while (hostname[0]) { - err = get_addresses(p, ap_getword_conf(p, &hostname), &addrs, s->port); + err = get_addresses(p, ap_getword_conf(p, &hostname), NULL, + &addrs, s->port); if (err) { *addrs = NULL; return err; @@ -268,10 +299,11 @@ API_EXPORT(const char *) ap_parse_vhost_addrs(pool *p, const char *hostname, ser } -API_EXPORT_NONSTD(const char *) ap_set_name_virtual_host (cmd_parms *cmd, void *dummy, char *arg) +API_EXPORT_NONSTD(const char *) ap_set_name_virtual_host (cmd_parms *cmd, void *dummy, char *h, + char *p) { /* use whatever port the main server has at this point */ - return get_addresses(cmd->pool, arg, &name_vhost_list_tail, + return get_addresses(cmd->pool, h, p, &name_vhost_list_tail, cmd->server->port); } @@ -345,6 +377,17 @@ static ap_inline unsigned hash_inaddr(unsigned key) return ((key >> 8) ^ key) % IPHASH_TABLE_SIZE; } +static unsigned hash_addr(struct sockaddr *sa) +{ + switch (sa->sa_family) { + case AF_INET: + return hash_inaddr(((struct sockaddr_in *)sa)->sin_addr.s_addr); + case AF_INET6: + return hash_inaddr(((struct sockaddr_in6 *)sa)->sin6_addr.s6_addr[12]); + default: + return hash_inaddr(sa->sa_family); + } +} static ipaddr_chain *new_ipaddr_chain(pool *p, @@ -372,25 +415,52 @@ static name_chain *new_name_chain(pool *p, server_rec *s, server_addr_rec *sar) return new; } - -static ap_inline ipaddr_chain *find_ipaddr(struct in_addr *server_ip, - unsigned port) +static ap_inline ipaddr_chain *find_ipaddr(struct sockaddr *sa) { unsigned bucket; ipaddr_chain *trav; - unsigned addr; + char a[NI_MAXHOST], b[NI_MAXHOST]; /* scan the hash table for an exact match first */ - addr = server_ip->s_addr; - bucket = hash_inaddr(addr); + bucket = hash_addr(sa); for (trav = iphash_table[bucket]; trav; trav = trav->next) { server_addr_rec *sar = trav->sar; - if ((sar->host_addr.s_addr == addr) - && (sar->host_port == 0 || sar->host_port == port - || port == 0)) { - return trav; + if (sar->host_addr.ss_family != sa->sa_family) + continue; + switch (sa->sa_family) { + case AF_INET: + { + struct sockaddr_in *sin1, *sin2; + sin1 = (struct sockaddr_in *)&sar->host_addr; + sin2 = (struct sockaddr_in *)sa; + if (sin1->sin_port == 0 || sin2->sin_port == 0 + || sin1->sin_port == sin2->sin_port) { + if (memcmp(&sin1->sin_addr, &sin2->sin_addr, + sizeof(sin1->sin_addr)) == 0) { + return trav; + } + } + break; + } + case AF_INET6: + { + struct sockaddr_in6 *sin1, *sin2; + sin1 = (struct sockaddr_in6 *)&sar->host_addr; + sin2 = (struct sockaddr_in6 *)sa; + if (sin1->sin6_port == 0 || sin2->sin6_port == 0 + || sin1->sin6_port == sin2->sin6_port) { + if (memcmp(&sin1->sin6_addr, &sin2->sin6_addr, + sizeof(sin1->sin6_addr)) == 0) { + return trav; + } + } + break; + } + default: /*unsupported*/ + break; } } + return NULL; } @@ -416,21 +486,7 @@ static void dump_a_vhost(FILE *f, ipaddr_chain *ic) int len; char buf[MAX_STRING_LEN]; - if (ic->sar->host_addr.s_addr == DEFAULT_VHOST_ADDR) { - len = ap_snprintf(buf, sizeof(buf), "_default_:%u", - ic->sar->host_port); - } - else if (ic->sar->host_addr.s_addr == INADDR_ANY) { - len = ap_snprintf(buf, sizeof(buf), "*:%u", - ic->sar->host_port); - } - else { - len = ap_snprintf(buf, sizeof(buf), "%pA:%u", - &ic->sar->host_addr, ic->sar->host_port); - } - if (ic->sar->host_port == 0) { - buf[len-1] = '*'; - } + len = ap_snprintf(buf, sizeof(buf), "%pI", &ic->sar->host_addr); if (ic->names == NULL) { if (ic->server == NULL) fprintf(f, "%-22s WARNING: No <VirtualHost> defined for this NameVirtualHost!\n", buf); @@ -558,10 +614,35 @@ API_EXPORT(void) ap_fini_vhost_config(pool *p, server_rec *main_s) * occured in the config file, we'll copy it in that order. */ for (sar = name_vhost_list; sar; sar = sar->next) { - unsigned bucket = hash_inaddr(sar->host_addr.s_addr); + unsigned bucket = hash_addr((struct sockaddr *)&sar->host_addr); ipaddr_chain *ic = new_ipaddr_chain(p, NULL, sar); + int wildcard; + + wildcard = 0; + switch (sar->host_addr.ss_family) { + case AF_INET: + { + struct sockaddr_in *sin; + sin = (struct sockaddr_in *)&sar->host_addr; + if (sin->sin_addr.s_addr == INADDR_ANY) + wildcard++; + break; + } + case AF_INET6: + { + struct sockaddr_in6 *sin6; + sin6 = (struct sockaddr_in6 *)&sar->host_addr; + if (*(uint32_t *)&sin6->sin6_addr.s6_addr[0] == 0 + && *(uint32_t *)&sin6->sin6_addr.s6_addr[4] == 0 + && *(uint32_t *)&sin6->sin6_addr.s6_addr[8] == 0 + && *(uint32_t *)&sin6->sin6_addr.s6_addr[12] == 0) { + wildcard++; + } + break; + } + } - if (sar->host_addr.s_addr != INADDR_ANY) { + if (!wildcard) { *iphash_table_tail[bucket] = ic; iphash_table_tail[bucket] = &ic->next; } @@ -588,12 +669,43 @@ API_EXPORT(void) ap_fini_vhost_config(pool *p, server_rec *main_s) has_default_vhost_addr = 0; for (sar = s->addrs; sar; sar = sar->next) { ipaddr_chain *ic; + int wildcard; + + wildcard = 0; + switch (sar->host_addr.ss_family) { + case AF_INET: + { + struct sockaddr_in *sin; + sin = (struct sockaddr_in *)&sar->host_addr; + if (sin->sin_addr.s_addr == DEFAULT_VHOST_ADDR) + wildcard++; + else if (sin->sin_addr.s_addr == INADDR_ANY) + wildcard++; + break; + } + case AF_INET6: + { + struct sockaddr_in6 *sin6; + sin6 = (struct sockaddr_in6 *)&sar->host_addr; + if (*(uint32_t *)&sin6->sin6_addr.s6_addr[0] == ~0 + && *(uint32_t *)&sin6->sin6_addr.s6_addr[4] == ~0 + && *(uint32_t *)&sin6->sin6_addr.s6_addr[8] == ~0 + && *(uint32_t *)&sin6->sin6_addr.s6_addr[12] == ~0) { + wildcard++; + } + break; + } + } + + if (wildcard) { + /* add it to default bucket for each appropriate sar + * since we need to do a port test + */ + ipaddr_chain *other; - if (sar->host_addr.s_addr == DEFAULT_VHOST_ADDR - || sar->host_addr.s_addr == INADDR_ANY) { - ic = find_default_server(sar->host_port); - if (!ic || !add_name_vhost_config(p, main_s, s, sar, ic)) { - if (ic && ic->sar->host_port != 0) { + other = find_default_server(sar->host_port); + if (!other || !add_name_vhost_config(p, main_s, s, sar, other)) { + if (other && other->sar->host_port != 0) { ap_log_error(APLOG_MARK, APLOG_NOERRNO|APLOG_WARNING, main_s, "_default_ VirtualHost overlap on port %u," " the first has precedence", sar->host_port); @@ -606,10 +718,11 @@ API_EXPORT(void) ap_fini_vhost_config(pool *p, server_rec *main_s) } else { /* see if it matches something we've already got */ - ic = find_ipaddr(&sar->host_addr, sar->host_port); + ic = find_ipaddr((struct sockaddr *)&sar->host_addr); if (!ic) { - unsigned bucket = hash_inaddr(sar->host_addr.s_addr); + unsigned bucket = + hash_addr((struct sockaddr *)&sar->host_addr); ic = new_ipaddr_chain(p, s, sar); ic->next = *iphash_table_tail[bucket]; @@ -646,19 +759,33 @@ API_EXPORT(void) ap_fini_vhost_config(pool *p, server_rec *main_s) } else { struct hostent *h; + char hostnamebuf[MAXHOSTNAMELEN]; - if ((h = gethostbyaddr((char *) &(s->addrs->host_addr), - sizeof(struct in_addr), AF_INET))) { - s->server_hostname = ap_pstrdup(p, (char *) h->h_name); + if (!getnameinfo((struct sockaddr *)&s->addrs->host_addr, +#ifndef SIN6_LEN + SA_LEN((struct sockaddr *)&s->addrs->host_addr), +#else + s->addrs->host_addr.ss_len, +#endif + hostnamebuf, sizeof(hostnamebuf), + NULL, 0, 0)) { + s->server_hostname = ap_pstrdup(p, hostnamebuf); } else { /* again, what can we do? They didn't specify a ServerName, and their DNS isn't working. -djg */ + getnameinfo((struct sockaddr *)&s->addrs->host_addr, +#ifndef SIN6_LEN + SA_LEN((struct sockaddr *)&s->addrs->host_addr), +#else + s->addrs->host_addr.ss_len, +#endif + hostnamebuf, sizeof(hostnamebuf), + NULL, 0, NI_NUMERICHOST); ap_log_error(APLOG_MARK, APLOG_NOERRNO|APLOG_ERR, main_s, "Failed to resolve server name " "for %s (check DNS) -- or specify an explicit " - "ServerName", - inet_ntoa(s->addrs->host_addr)); + "ServerName", hostnamebuf); s->server_hostname = ap_pstrdup(p, "bogus_host_without_reverse_dns"); } @@ -705,35 +832,58 @@ static void fix_hostname(request_rec *r) char *host = ap_palloc(r->pool, strlen(r->hostname) + 1); const char *src; char *dst; + const char *u = NULL, *v = NULL; /* check and copy the host part */ - src = r->hostname; + u = src = r->hostname; dst = host; - while (*src) { - if (*src == '.') { - *dst++ = *src++; - if (*src == '.') + if (*u == '[') { /* IPv6 numeral address in brackets */ + v = strchr(u, ']'); + if (v == NULL) { + /* missing closing bracket */ + goto bad; + } + if (v == (u + 1)) { + /* bad empty address */ + goto bad; + } + for (src = u+1; src < v; src++) /* copy IPv6 adress */ + *dst = *src; + v++; + if (*v == ':') { + v++; + while (*v) { /* check if portnum is correct */ + if (!ap_isdigit(*v++)) + goto bad; + } + } + } else { + while (*src) { + if (*src == '.') { + *dst++ = *src++; + if (*src == '.') + goto bad; + else + continue; + } + if (*src == '/' || *src == '\\') { goto bad; - else - continue; - } - if (*src == '/' || *src == '\\') { - goto bad; - } - if (*src == ':') { - /* check the port part */ - while (*++src) { - if (!ap_isdigit(*src)) { - goto bad; - } - } - if (src[-1] == ':') - goto bad; - else - break; + } + if (*src == ':') { + /* sheck the port part */ + while (*++src) { + if (!ap_isdigit(*src)) { + goto bad; + } + } + if (src[-1] == ':') + goto bad; + else + break; + } + *dst++ = *src++; } - *dst++ = *src++; } /* strip trailing gubbins */ if (dst > host && dst[-1] == '.') { @@ -748,7 +898,7 @@ static void fix_hostname(request_rec *r) bad: r->status = HTTP_BAD_REQUEST; ap_log_rerror(APLOG_MARK, APLOG_NOERRNO|APLOG_ERR, r, - "Client sent malformed Host header"); + "Client sent malformed Host header <<%s>>",u); return; } @@ -851,11 +1001,23 @@ static void check_hostalias(request_rec *r) * names we'll match have ports associated with them */ const char *host = r->hostname; - unsigned port = ntohs(r->connection->local_addr.sin_port); + unsigned port; server_rec *s; server_rec *last_s; name_chain *src; + switch (r->connection->local_addr.ss_family) { + case AF_INET: + port = ntohs(((struct sockaddr_in *) + &r->connection->local_addr)->sin_port); + break; + case AF_INET6: + port = ntohs(((struct sockaddr_in6 *) + &r->connection->local_addr)->sin6_port); + break; + default: + port = 0; /*XXX*/ + } last_s = NULL; /* Recall that the name_chain is a list of server_addr_recs, some of @@ -910,7 +1072,20 @@ static void check_serverpath(request_rec *r) server_rec *s; server_rec *last_s; name_chain *src; - unsigned port = ntohs(r->connection->local_addr.sin_port); + unsigned port; + + switch (r->connection->local_addr.ss_family) { + case AF_INET: + port = ntohs(((struct sockaddr_in *) + &r->connection->local_addr)->sin_port); + break; + case AF_INET6: + port = ntohs(((struct sockaddr_in6 *) + &r->connection->local_addr)->sin6_port); + break; + default: + port = 0; /*XXX*/ + } /* * This is in conjunction with the ServerPath code in http_core, so we @@ -970,10 +1145,22 @@ API_EXPORT(void) ap_update_vhost_from_headers(request_rec *r) API_EXPORT(void) ap_update_vhost_given_ip(conn_rec *conn) { ipaddr_chain *trav; - unsigned port = ntohs(conn->local_addr.sin_port); + char portbuf[NI_MAXSERV]; + unsigned port; + + if (getnameinfo((struct sockaddr *)&conn->local_addr, +#ifndef SIN6_LEN + SA_LEN((struct sockaddr *)&conn->local_addr), +#else + conn->local_addr.ss_len, +#endif + NULL, 0, portbuf, sizeof(portbuf), NI_NUMERICSERV) != 0) { + goto fail; + } + port = atoi(portbuf); /* scan the hash table for an exact match first */ - trav = find_ipaddr(&conn->local_addr.sin_addr, port); + trav = find_ipaddr((struct sockaddr *)&conn->local_addr); if (trav) { /* save the name_chain for later in case this is a name-vhost */ conn->vhost_lookup_data = trav->names; @@ -991,6 +1178,7 @@ API_EXPORT(void) ap_update_vhost_given_ip(conn_rec *conn) return; } +fail: /* otherwise we're stuck with just the main server * and no name-based vhosts */ diff --git a/usr.sbin/httpd/src/main/rfc1413.c b/usr.sbin/httpd/src/main/rfc1413.c index 7321b0d2b1c..b543e298d58 100644 --- a/usr.sbin/httpd/src/main/rfc1413.c +++ b/usr.sbin/httpd/src/main/rfc1413.c @@ -110,12 +110,13 @@ static void ident_timeout(int sig) /* bind_connect - bind both ends of a socket */ /* Ambarish fix this. Very broken */ -static int get_rfc1413(int sock, const struct sockaddr_in *our_sin, - const struct sockaddr_in *rmt_sin, +static int get_rfc1413(int sock, const struct sockaddr *our_sin, + const struct sockaddr *rmt_sin, char user[RFC1413_USERLEN+1], server_rec *srv) { - struct sockaddr_in rmt_query_sin, our_query_sin; - unsigned int rmt_port, our_port; + struct sockaddr_storage rmt_query_sin, our_query_sin; + unsigned int o_rmt_port, o_our_port; /* original port pair */ + unsigned int rmt_port, our_port; /* replied port pair */ int i; char *cp; char buffer[RFC1413_MAXDATA + 1]; @@ -130,13 +131,38 @@ static int get_rfc1413(int sock, const struct sockaddr_in *our_sin, * addresses from the query socket. */ - our_query_sin = *our_sin; - our_query_sin.sin_port = htons(ANY_PORT); - rmt_query_sin = *rmt_sin; - rmt_query_sin.sin_port = htons(RFC1413_PORT); +#ifndef SIN6_LEN + memcpy(&our_query_sin, our_sin, SA_LEN(our_sin)); + memcpy(&rmt_query_sin, rmt_sin, SA_LEN(rmt_sin)); +#else + memcpy(&our_query_sin, our_sin, our_sin->sa_len); + memcpy(&rmt_query_sin, rmt_sin, rmt_sin->sa_len); +#endif + switch (our_sin->sa_family) { + case AF_INET: + ((struct sockaddr_in *)&our_query_sin)->sin_port = htons(ANY_PORT); + o_our_port = ntohs(((struct sockaddr_in *)our_sin)->sin_port); + ((struct sockaddr_in *)&rmt_query_sin)->sin_port = htons(RFC1413_PORT); + o_rmt_port = ntohs(((struct sockaddr_in *)rmt_sin)->sin_port); + break; + case AF_INET6: + ((struct sockaddr_in6 *)&our_query_sin)->sin6_port = htons(ANY_PORT); + o_our_port = ntohs(((struct sockaddr_in6 *)our_sin)->sin6_port); + ((struct sockaddr_in6 *)&rmt_query_sin)->sin6_port = htons(RFC1413_PORT); + o_rmt_port = ntohs(((struct sockaddr_in6 *)rmt_sin)->sin6_port); + break; + default: + /* unsupported AF */ + return -1; + } if (bind(sock, (struct sockaddr *) &our_query_sin, - sizeof(struct sockaddr_in)) < 0) { +#ifndef SIN6_LEN + SA_LEN((struct sockaddr *) &our_query_sin) +#else + our_query_sin.ss_len +#endif + ) < 0) { ap_log_error(APLOG_MARK, APLOG_CRIT, srv, "bind: rfc1413: Error binding to local port"); return -1; @@ -147,12 +173,18 @@ static int get_rfc1413(int sock, const struct sockaddr_in *our_sin, * the service */ if (connect(sock, (struct sockaddr *) &rmt_query_sin, - sizeof(struct sockaddr_in)) < 0) - return -1; +#ifndef SIN6_LEN + SA_LEN((struct sockaddr *) &rmt_query_sin) +#else + rmt_query_sin.ss_len +#endif + ) < 0) { + return -1; + } /* send the data */ - buflen = ap_snprintf(buffer, sizeof(buffer), "%u,%u\r\n", ntohs(rmt_sin->sin_port), - ntohs(our_sin->sin_port)); + buflen = ap_snprintf(buffer, sizeof(buffer), "%u,%u\r\n", o_rmt_port, + o_our_port); /* send query to server. Handle short write. */ i = 0; @@ -197,9 +229,9 @@ static int get_rfc1413(int sock, const struct sockaddr_in *our_sin, /* RFC1413_USERLEN = 512 */ if (sscanf(buffer, "%u , %u : USERID :%*[^:]:%512s", &rmt_port, &our_port, - user) != 3 || ntohs(rmt_sin->sin_port) != rmt_port - || ntohs(our_sin->sin_port) != our_port) + user) != 3 || o_rmt_port != rmt_port || o_our_port != our_port) { return -1; + } /* * Strip trailing carriage return. It is part of the @@ -221,7 +253,7 @@ API_EXPORT(char *) ap_rfc1413(conn_rec *conn, server_rec *srv) result = FROM_UNKNOWN; - sock = ap_psocket_ex(conn->pool, AF_INET, SOCK_STREAM, IPPROTO_TCP, 1); + sock = ap_psocket_ex(conn->pool, conn->remote_addr.ss_family, SOCK_STREAM, IPPROTO_TCP, 1); if (sock < 0) { ap_log_error(APLOG_MARK, APLOG_CRIT, srv, "socket: rfc1413: error creating socket"); @@ -234,8 +266,10 @@ API_EXPORT(char *) ap_rfc1413(conn_rec *conn, server_rec *srv) if (ap_setjmp(timebuf) == 0) { ap_set_callback_and_alarm(ident_timeout, ap_rfc1413_timeout); - if (get_rfc1413(sock, &conn->local_addr, &conn->remote_addr, user, srv) >= 0) + if (get_rfc1413(sock, (struct sockaddr *)&conn->local_addr, + (struct sockaddr *)&conn->remote_addr, user, srv) >= 0) { result = user; + } } ap_set_callback_and_alarm(NULL, 0); ap_pclosesocket(conn->pool, sock); diff --git a/usr.sbin/httpd/src/main/util.c b/usr.sbin/httpd/src/main/util.c index 556416fa4dc..a473eee9f74 100644 --- a/usr.sbin/httpd/src/main/util.c +++ b/usr.sbin/httpd/src/main/util.c @@ -1854,52 +1854,87 @@ API_EXPORT(gid_t) ap_gname2id(const char *name) * Parses a host of the form <address>[:port] * :port is permitted if 'port' is not NULL */ -API_EXPORT(unsigned long) ap_get_virthost_addr(char *w, unsigned short *ports) -{ - struct hostent *hep; - unsigned long my_addr; - char *p; - - p = strchr(w, ':'); +API_EXPORT(struct sockaddr *) ap_get_virthost_addr(char *w, unsigned short *ports) +{ + static struct sockaddr_storage ss; + struct addrinfo hints, *res; + char *p, *r; + char *host; + char *port = "0"; + int error; + char servbuf[NI_MAXSERV]; + + if (w == NULL) + w = "*"; + p = r = NULL; + if (*w == '['){ + if (r = strrchr(w+1, ']')){ + *r = '\0'; + p = r + 1; + switch(*p){ + case ':': + p++; + /* nobreak; */ + case '\0': + w++; + break; + default: + p = NULL; + } + } + } + else{ + p = strchr(w, ':'); + if (p != NULL && strchr(p+1, ':') != NULL) + p = NULL; + } if (ports != NULL) { - *ports = 0; - if (p != NULL && strcmp(p + 1, "*") != 0) - *ports = atoi(p + 1); + if (p != NULL && *p && strcmp(p + 1, "*") != 0) + port = p + 1; } + memset(&hints, 0, sizeof(hints)); + hints.ai_socktype = SOCK_STREAM; if (p != NULL) *p = '\0'; if (strcmp(w, "*") == 0) { - if (p != NULL) - *p = ':'; - return htonl(INADDR_ANY); - } - - my_addr = ap_inet_addr((char *)w); - if (my_addr != INADDR_NONE) { - if (p != NULL) - *p = ':'; - return my_addr; + host = NULL; + hints.ai_flags = AI_PASSIVE; + hints.ai_family = ap_default_family; + } else { + host = w; + hints.ai_family = PF_UNSPEC; } - hep = gethostbyname(w); + error = getaddrinfo(host, port, &hints, &res); - if ((!hep) || (hep->h_addrtype != AF_INET || !hep->h_addr_list[0])) { - fprintf(stderr, "Cannot resolve host name %s --- exiting!\n", w); + if (error || !res) { + fprintf(stderr, "ap_get_vitrhost_addr(): getaddrinfo(%s):%s --- exiting!\n", w, gai_strerror(error)); exit(1); } - if (hep->h_addr_list[1]) { - fprintf(stderr, "Host %s has multiple addresses ---\n", w); + if (res->ai_next) { + fprintf(stderr, "ap_get_vitrhost_addr(): Host %s has multiple addresses ---\n", w); fprintf(stderr, "you must choose one explicitly for use as\n"); fprintf(stderr, "a virtual host. Exiting!!!\n"); exit(1); } + if (r != NULL) + *r = ']'; if (p != NULL) *p = ':'; - return ((struct in_addr *) (hep->h_addr))->s_addr; + memcpy(&ss, res->ai_addr, res->ai_addrlen); + if (getnameinfo(res->ai_addr, res->ai_addrlen, + NULL, 0, servbuf, sizeof(servbuf), + NI_NUMERICSERV)){ + fprintf(stderr, "ap_get_virthost_addr(): getnameinfo() failed --- Exiting!!!\n"); + exit(1); + } + if (ports) *ports = atoi(servbuf); + freeaddrinfo(res); + return (struct sockaddr *)&ss; } @@ -1927,7 +1962,8 @@ API_EXPORT(char *) ap_get_local_host(pool *a) #endif char str[MAXHOSTNAMELEN]; char *server_hostname = NULL; - struct hostent *p; + struct addrinfo hints, *res; + int error; if (gethostname(str, sizeof(str) - 1) != 0) { ap_log_error(APLOG_MARK, APLOG_WARNING, NULL, @@ -1936,23 +1972,25 @@ API_EXPORT(char *) ap_get_local_host(pool *a) } else { - str[sizeof(str) - 1] = '\0'; - if ((!(p = gethostbyname(str))) - || (!(server_hostname = find_fqdn(a, p)))) { - if (p == NULL || p->h_addr_list == NULL) - server_hostname=NULL; - else { - /* Recovery - return the default servername by IP: */ - if (p->h_addr_list[0]) { - ap_snprintf(str, sizeof(str), "%pA", p->h_addr_list[0]); - server_hostname = ap_pstrdup(a, str); - /* We will drop through to report the IP-named server */ - } - } - } + str[sizeof(str) - 1] = '\0'; + memset(&hints, 0, sizeof(hints)); + hints.ai_family = PF_UNSPEC; + hints.ai_flags = AI_CANONNAME; + res = NULL; + error = getaddrinfo(str, NULL, &hints, &res); + if (error == 0 && res) + { + /* Since we found a fqdn, rturn it with no logged message. */ + server_hostname = ap_pstrdup(a, res->ai_canonname); + freeaddrinfo(res); + return server_hostname; + } else - /* Since we found a fqdn, return it with no logged message. */ - return server_hostname; + { + /* Recovery - return the default server by IP: */ + server_hostname = ap_pstrdup(a, str); + /* We will drop through to report the IP-named server */ + } } /* If we don't have an fqdn or IP, fall back to the loopback addr */ @@ -1964,6 +2002,8 @@ API_EXPORT(char *) ap_get_local_host(pool *a) "domain name, using %s for ServerName", ap_server_argv0, server_hostname); + if (res) + freeaddrinfo(res); return server_hostname; } diff --git a/usr.sbin/httpd/src/main/util_script.c b/usr.sbin/httpd/src/main/util_script.c index 7029c1f6e73..de946c0ec92 100644 --- a/usr.sbin/httpd/src/main/util_script.c +++ b/usr.sbin/httpd/src/main/util_script.c @@ -196,6 +196,7 @@ API_EXPORT(void) ap_add_common_vars(request_rec *r) array_header *hdrs_arr = ap_table_elts(r->headers_in); table_entry *hdrs = (table_entry *) hdrs_arr->elts; int i; + char servbuf[NI_MAXSERV]; /* use a temporary table which we'll overlap onto * r->subprocess_env later @@ -258,8 +259,16 @@ API_EXPORT(void) ap_add_common_vars(request_rec *r) ap_table_addn(e, "SERVER_ADMIN", s->server_admin); /* Apache */ ap_table_addn(e, "SCRIPT_FILENAME", r->filename); /* Apache */ - ap_table_addn(e, "REMOTE_PORT", - ap_psprintf(r->pool, "%d", ntohs(c->remote_addr.sin_port))); + servbuf[0] = '\0'; + if (!getnameinfo((struct sockaddr *)&c->remote_addr, +#ifndef HAVE_SOCKADDR_LEN + SA_LEN((struct sockaddr *)&c->remote_addr), +#else + c->remote_addr.ss_len, +#endif + NULL, 0, servbuf, sizeof(servbuf), NI_NUMERICSERV)){ + ap_table_addn(e, "REMOTE_PORT", ap_pstrdup(r->pool, servbuf)); + } if (c->user) { ap_table_addn(e, "REMOTE_USER", c->user); diff --git a/usr.sbin/httpd/src/main/util_uri.c b/usr.sbin/httpd/src/main/util_uri.c index 6daf5fcdb65..18554a0f685 100644 --- a/usr.sbin/httpd/src/main/util_uri.c +++ b/usr.sbin/httpd/src/main/util_uri.c @@ -410,45 +410,50 @@ API_EXPORT(int) ap_parse_uri_components(pool *p, const char *uri, * &hostinfo[-1] < &hostinfo[0] ... and this loop is valid C. */ do { - --s; + --s; } while (s >= hostinfo && *s != '@'); if (s < hostinfo) { - /* again we want the common case to be fall through */ - deal_with_host: - /* We expect hostinfo to point to the first character of - * the hostname. If there's a port it is the first colon. - */ - s = memchr(hostinfo, ':', uri - hostinfo); - if (s == NULL) { - /* we expect the common case to have no port */ - uptr->hostname = ap_pstrndup(p, hostinfo, uri - hostinfo); - goto deal_with_path; - } - uptr->hostname = ap_pstrndup(p, hostinfo, s - hostinfo); - ++s; - uptr->port_str = ap_pstrndup(p, s, uri - s); - if (uri != s) { - port = ap_strtol(uptr->port_str, &endstr, 10); - uptr->port = port; - if (*endstr == '\0') { - goto deal_with_path; - } - /* Invalid characters after ':' found */ - return HTTP_BAD_REQUEST; - } - uptr->port = ap_default_port_for_scheme(uptr->scheme); - goto deal_with_path; + /* again we want the common case to be fall through */ +deal_with_host: + /* We expect hostinfo to point to the first character of + * the hostname. If there's a port it is the first colon. + */ + if (*hostinfo == '[') { + s = memchr(hostinfo+1, ']', uri - hostinfo - 1); + if (s) + s = strchr(s, ':'); + } else + s = memchr(hostinfo, ':', uri - hostinfo); + if (s == NULL) { + /* we expect the common case to have no port */ + uptr->hostname = ap_pstrndup(p, hostinfo, uri - hostinfo); + goto deal_with_path; + } + uptr->hostname = ap_pstrndup(p, hostinfo, s - hostinfo); + ++s; + uptr->port_str = ap_pstrndup(p, s, uri - s); + if (uri != s) { + port = ap_strtol(uptr->port_str, &endstr, 10); + uptr->port = port; + if (*endstr == '\0') { + goto deal_with_path; + } + /* Invalid characters after ':' found */ + return HTTP_BAD_REQUEST; + } + uptr->port = ap_default_port_for_scheme(uptr->scheme); + goto deal_with_path; } /* first colon delimits username:password */ s1 = memchr(hostinfo, ':', s - hostinfo); if (s1) { - uptr->user = ap_pstrndup(p, hostinfo, s1 - hostinfo); - ++s1; - uptr->password = ap_pstrndup(p, s1, s - s1); + uptr->user = ap_pstrndup(p, hostinfo, s1 - hostinfo); + ++s1; + uptr->password = ap_pstrndup(p, s1, s - s1); } else { - uptr->user = ap_pstrndup(p, hostinfo, s - hostinfo); + uptr->user = ap_pstrndup(p, hostinfo, s - hostinfo); } hostinfo = s + 1; goto deal_with_host; @@ -475,7 +480,12 @@ API_EXPORT(int) ap_parse_hostinfo_components(pool *p, const char *hostinfo, /* We expect hostinfo to point to the first character of * the hostname. There must be a port, separated by a colon */ - s = strchr(hostinfo, ':'); + if (*hostinfo == '[') { + s = strchr(hostinfo+1, ']'); + if (s) + s = strchr(s, ':'); + } else + s = strchr(hostinfo, ':'); if (s == NULL) { return HTTP_BAD_REQUEST; } diff --git a/usr.sbin/httpd/src/modules/proxy/mod_proxy.c b/usr.sbin/httpd/src/modules/proxy/mod_proxy.c index 5206151041d..42f070ccb15 100644 --- a/usr.sbin/httpd/src/modules/proxy/mod_proxy.c +++ b/usr.sbin/httpd/src/modules/proxy/mod_proxy.c @@ -565,11 +565,31 @@ static const char * struct proxy_remote *new; char *p, *q; int port; + char *bl = NULL, *br = NULL; p = strchr(r, ':'); if (p == NULL || p[1] != '/' || p[2] != '/' || p[3] == '\0') - return "ProxyRemote: Bad syntax for a remote proxy server"; - q = strchr(p + 3, ':'); + return "ProxyRemote: Bad syntax for a remote proxy server"; + bl = p + 3; + if (*bl == '['){ + br = strrchr(bl+1, ']'); + if (br){ + bl++; + *br = '\0'; + if (*(br+1) == ':'){ /* [host]:xx */ + q = br+1; + } + else if (*(br+1) == '\0'){ /* [host] */ + q = NULL; + } + else + q = strrchr(br, ':'); /* XXX */ + } + else + q = strrchr(bl, ':'); /* XXX */ + } + else + q = strrchr(bl, ':'); if (q != NULL) { if (sscanf(q + 1, "%u", &port) != 1 || port > 65535) return "ProxyRemote: Bad syntax for a remote proxy server (bad port number)"; @@ -580,7 +600,7 @@ static const char * *p = '\0'; if (strchr(f, ':') == NULL) ap_str_tolower(f); /* lowercase scheme */ - ap_str_tolower(p + 3); /* lowercase hostname */ + ap_str_tolower(bl); /* lowercase hostname */ if (port == -1) { int i; @@ -593,7 +613,7 @@ static const char * new = ap_push_array(conf->proxies); new->scheme = f; new->protocol = r; - new->hostname = p + 3; + new->hostname = bl; new->port = port; return NULL; } diff --git a/usr.sbin/httpd/src/modules/proxy/mod_proxy.h b/usr.sbin/httpd/src/modules/proxy/mod_proxy.h index 78e9b17c919..adacd729449 100644 --- a/usr.sbin/httpd/src/modules/proxy/mod_proxy.h +++ b/usr.sbin/httpd/src/modules/proxy/mod_proxy.h @@ -312,7 +312,7 @@ int ap_proxy_is_ipaddr(struct dirconn_entry *This, pool *p); int ap_proxy_is_domainname(struct dirconn_entry *This, pool *p); int ap_proxy_is_hostname(struct dirconn_entry *This, pool *p); int ap_proxy_is_word(struct dirconn_entry *This, pool *p); -int ap_proxy_doconnect(int sock, struct sockaddr_in *addr, request_rec *r); +int ap_proxy_doconnect(int sock, struct sockaddr *addr, request_rec *r); int ap_proxy_garbage_init(server_rec *, pool *); /* This function is called by ap_table_do() for all header lines */ int ap_proxy_send_hdr_line(void *p, const char *key, const char *value); diff --git a/usr.sbin/httpd/src/modules/proxy/proxy_connect.c b/usr.sbin/httpd/src/modules/proxy/proxy_connect.c index 6e802651bbf..42b39a33bfc 100644 --- a/usr.sbin/httpd/src/modules/proxy/proxy_connect.c +++ b/usr.sbin/httpd/src/modules/proxy/proxy_connect.c @@ -109,14 +109,15 @@ int ap_proxy_connect_handler(request_rec *r, cache_req *c, char *url, const char *proxyhost, int proxyport) { struct sockaddr_in server; - struct in_addr destaddr; - struct hostent server_hp; - const char *host, *err; + struct addrinfo hints, *res, *res0; + const char *hoststr; + const char *portstr = NULL; char *p; int port, sock; char buffer[HUGE_STRING_LEN]; - int nbytes, i, j; + int nbytes, i; fd_set fds; + int error; void *sconf = r->server->module_config; proxy_server_conf *conf = @@ -124,27 +125,60 @@ int ap_proxy_connect_handler(request_rec *r, cache_req *c, char *url, struct noproxy_entry *npent = (struct noproxy_entry *) conf->noproxies->elts; memset(&server, '\0', sizeof(server)); +#ifdef HAVE_SOCKADDR_LEN + server.sin_len = sizeof(server); +#endif server.sin_family = AF_INET; /* Break the URL into host:port pairs */ - host = url; + hoststr = url; p = strchr(url, ':'); - if (p == NULL) - port = DEFAULT_HTTPS_PORT; - else { - port = atoi(p + 1); - *p = '\0'; + if (p == NULL) { + char pbuf[32]; + ap_snprintf(pbuf, sizeof(pbuf), "%d", DEFAULT_HTTPS_PORT); + portstr = pbuf; + } else { + portstr = p + 1; + *p = '\0'; + } + port = atoi(portstr); + + memset(&hints, 0, sizeof(hints)); + hints.ai_family = PF_UNSPEC; + hints.ai_socktype = SOCK_STREAM; + hints.ai_protocol = IPPROTO_TCP; + error = getaddrinfo(hoststr, portstr, &hints, &res0); + if (error && proxyhost == NULL) { + return ap_proxyerror(r, HTTP_INTERNAL_SERVER_ERROR, + gai_strerror(error)); /* give up */ } /* check if ProxyBlock directive on this host */ - destaddr.s_addr = ap_inet_addr(host); for (i = 0; i < conf->noproxies->nelts; i++) { - if ((npent[i].name != NULL && strstr(host, npent[i].name) != NULL) - || destaddr.s_addr == npent[i].addr.s_addr - || npent[i].name[0] == '*') - return ap_proxyerror(r, HTTP_FORBIDDEN, - "Connect to remote machine blocked"); + int fail; + struct sockaddr_in *sin; + + fail = 0; + if (npent[i].name != NULL && strstr(hoststr, npent[i].name)) + fail++; + if (npent[i].name != NULL && strcmp(npent[i].name, "*") == 0) + fail++; + for (res = res0; res; res = res->ai_next) { + switch (res->ai_family) { + case AF_INET: + sin = (struct sockaddr_in *)res->ai_addr; + if (sin->sin_addr.s_addr == npent[i].addr.s_addr) + fail++; + break; + } + } + if (fail) { + if (res0 != NULL) + freeaddrinfo(res0); + return ap_proxyerror(r, HTTP_FORBIDDEN, + "Connect to remote machine blocked"); + } } /* Check if it is an allowed port */ @@ -155,54 +189,60 @@ int ap_proxy_connect_handler(request_rec *r, cache_req *c, char *url, case DEFAULT_SNEWS_PORT: break; default: + if (res0 != NULL) + freeaddrinfo(res0); return HTTP_FORBIDDEN; } } - else if (!allowed_port(conf, port)) + else if(!allowed_port(conf, port)) { + if (res0 != NULL) + freeaddrinfo(res0); return HTTP_FORBIDDEN; + } if (proxyhost) { + char pbuf[10]; + + if (res0 != NULL) + freeaddrinfo(res0); + ap_snprintf(pbuf, sizeof(pbuf), "%d", proxyport); + memset(&hints, 0, sizeof(hints)); + hints.ai_family = PF_UNSPEC; + hints.ai_socktype = SOCK_STREAM; + hints.ai_protocol = IPPROTO_TCP; + error = getaddrinfo(proxyhost, pbuf, &hints, &res0); + if (error) + return HTTP_INTERNAL_SERVER_ERROR; /* XXX */ + ap_log_error(APLOG_MARK, APLOG_DEBUG | APLOG_NOERRNO, r->server, - "CONNECT to remote proxy %s on port %d", proxyhost, proxyport); + "CONNECT to remote proxy %s on port %d", proxyhost, proxyport); } else { ap_log_error(APLOG_MARK, APLOG_DEBUG | APLOG_NOERRNO, r->server, - "CONNECT to %s on port %d", host, port); + "CONNECT to %s on port %d", hoststr, port); } - /* Nasty cast to work around broken terniary expressions on MSVC */ - server.sin_port = htons((unsigned short)(proxyport ? proxyport : port)); - err = ap_proxy_host2addr(proxyhost ? proxyhost : host, &server_hp); + sock = i = -1; + for (res = res0; res; res = res->ai_next) { + sock = ap_psocket(r->pool, res->ai_family, res->ai_socktype, res->ai_protocol); + if (sock == -1) + continue; - if (err != NULL) - return ap_proxyerror(r, - proxyhost ? HTTP_BAD_GATEWAY : HTTP_INTERNAL_SERVER_ERROR, err); + if (sock >= FD_SETSIZE) { + ap_log_error(APLOG_MARK, APLOG_NOERRNO | APLOG_WARNING, NULL, + "proxy_connect_handler: filedescriptor (%u) " + "larger than FD_SETSIZE (%u) " + "found, you probably need to rebuild Apache with a " + "larger FD_SETSIZE", sock, FD_SETSIZE); + ap_pclosesocket(r->pool, sock); + return HTTP_INTERNAL_SERVER_ERROR; + } - sock = ap_psocket_ex(r->pool, PF_INET, SOCK_STREAM, IPPROTO_TCP, 1); - if (sock == -1) { - ap_log_rerror(APLOG_MARK, APLOG_ERR, r, "proxy: error creating socket"); - return HTTP_INTERNAL_SERVER_ERROR; - } - - if (sock >= FD_SETSIZE) { - ap_log_error(APLOG_MARK, APLOG_NOERRNO | APLOG_WARNING, NULL, - "proxy_connect_handler: filedescriptor (%u) " - "larger than FD_SETSIZE (%u) " - "found, you probably need to rebuild Apache with a " - "larger FD_SETSIZE", sock, FD_SETSIZE); - ap_pclosesocket(r->pool, sock); - return HTTP_INTERNAL_SERVER_ERROR; - } - - j = 0; - while (server_hp.h_addr_list[j] != NULL) { - memcpy(&server.sin_addr, server_hp.h_addr_list[j], - sizeof(struct in_addr)); - i = ap_proxy_doconnect(sock, &server, r); - if (i == 0) - break; - j++; + i = ap_proxy_doconnect(sock, res->ai_addr, r); + if (i == 0) + break; } + freeaddrinfo(res0); if (i == -1) { ap_pclosesocket(r->pool, sock); return ap_proxyerror(r, HTTP_INTERNAL_SERVER_ERROR, ap_pstrcat(r->pool, diff --git a/usr.sbin/httpd/src/modules/proxy/proxy_ftp.c b/usr.sbin/httpd/src/modules/proxy/proxy_ftp.c index cbd976ac131..9c5d6b4ca01 100644 --- a/usr.sbin/httpd/src/modules/proxy/proxy_ftp.c +++ b/usr.sbin/httpd/src/modules/proxy/proxy_ftp.c @@ -556,8 +556,10 @@ int ap_proxy_ftp_handler(request_rec *r, cache_req *c, char *url) const char *err; int destport, i, j, len, rc, nocache = 0; int csd = 0, sock = -1, dsock = -1; - struct sockaddr_in server; - struct hostent server_hp; + struct sockaddr_storage server; + struct addrinfo hints, *res, *res0; + char portbuf[10]; + int error; struct in_addr destaddr; table *resp_hdrs; BUFF *ctrl = NULL; @@ -580,10 +582,17 @@ int ap_proxy_ftp_handler(request_rec *r, cache_req *c, char *url) unsigned int presult, h0, h1, h2, h3, p0, p1; unsigned int paddr; unsigned short pport; - struct sockaddr_in data_addr; + struct sockaddr_storage data_addr; + struct sockaddr_in *sin; int pasvmode = 0; char pasv[64]; - char *pstr; + char *pstr, *host, *port; + +/* stuff for LPSV/EPSV */ + unsigned int paf, holen, ho[16], polen, po[2]; + struct sockaddr_in6 *sin6; + int lpsvmode = 0; + char *cmd; /* stuff for responses */ char resp[MAX_STRING_LEN]; @@ -596,6 +605,17 @@ int ap_proxy_ftp_handler(request_rec *r, cache_req *c, char *url) /* We break the URL into host, port, path-search */ + host = r->parsed_uri.hostname; + port = (r->parsed_uri.port != 0) + ? r->parsed_uri.port + : ap_default_port_for_request(r); + path = ap_pstrdup(p, r->parsed_uri.path); + if (path == NULL) + path = ""; + else + while (*path == '/') + ++path; + urlptr = strstr(url, "://"); if (urlptr == NULL) return HTTP_BAD_REQUEST; @@ -678,20 +698,22 @@ int ap_proxy_ftp_handler(request_rec *r, cache_req *c, char *url) if (parms != NULL) *(parms++) = '\0'; - memset(&server, 0, sizeof(struct sockaddr_in)); - server.sin_family = AF_INET; - server.sin_port = htons((unsigned short)destport); - err = ap_proxy_host2addr(desthost, &server_hp); - if (err != NULL) - return ap_proxyerror(r, HTTP_INTERNAL_SERVER_ERROR, err); - - sock = ap_psocket_ex(p, PF_INET, SOCK_STREAM, IPPROTO_TCP, 1); - if (sock == -1) { - ap_log_rerror(APLOG_MARK, APLOG_ERR, r, - "proxy: error creating socket"); - return HTTP_INTERNAL_SERVER_ERROR; + ap_snprintf(portbuf, sizeof(portbuf), "%d", port); + memset(&hints, 0, sizeof(hints)); + hints.ai_family = PF_UNSPEC; + hints.ai_socktype = SOCK_STREAM; + error = getaddrinfo(host, portbuf, &hints, &res0); + if (error) { + return ap_proxyerror(r, HTTP_INTERNAL_SERVER_ERROR, + gai_strerror(error)); } + i = -1; + for (res = res0; res; res = res->ai_next) { + dsock = ap_psocket(p, server.ss_family, SOCK_STREAM, res->ai_protocol); + if (sock == -1) + continue; + if (conf->recv_buffer_size > 0 && setsockopt(sock, SOL_SOCKET, SO_RCVBUF, (const char *)&conf->recv_buffer_size, sizeof(int)) @@ -705,18 +727,18 @@ int ap_proxy_ftp_handler(request_rec *r, cache_req *c, char *url) ap_log_rerror(APLOG_MARK, APLOG_ERR, r, "proxy: error setting reuseaddr option: setsockopt(SO_REUSEADDR)"); ap_pclosesocket(p, sock); + freeaddrinfo(res0); return HTTP_INTERNAL_SERVER_ERROR; } - j = 0; - while (server_hp.h_addr_list[j] != NULL) { - memcpy(&server.sin_addr, server_hp.h_addr_list[j], - sizeof(struct in_addr)); - i = ap_proxy_doconnect(sock, &server, r); - if (i == 0) + i = ap_proxy_doconnect(sock, res->ai_addr, r); + if (i == 0){ + memcpy(&server, res->ai_addr, res->ai_addrlen); break; - j++; + } + ap_pclosesocket(p, sock); } + freeaddrinfo(res0); if (i == -1) { return ftp_cleanup_and_return(r, ctrl, data, sock, dsock, ap_proxyerror(r, HTTP_BAD_GATEWAY, ap_pstrcat(r->pool, @@ -941,11 +963,21 @@ int ap_proxy_ftp_handler(request_rec *r, cache_req *c, char *url) } } - ap_bputs("PASV" CRLF, ctrl); +lpsvagain: + if (server.ss_family == AF_INET) + cmd = "PASV"; + else if (lpsvmode) + cmd = "LPSV"; + else + cmd = "EPSV"; + ap_bputs(cmd, ctrl); + ap_bputs(CRLF, ctrl); ap_bflush(ctrl); - ap_log_error(APLOG_MARK, APLOG_DEBUG | APLOG_NOERRNO, r->server, "FTP: PASV command issued"); + ap_log_error(APLOG_MARK, APLOG_DEBUG | APLOG_NOERRNO, r->server, "FTP: passive command issued"); /* possible results: 227, 421, 500, 501, 502, 530 */ /* 227 Entering Passive Mode (h1,h2,h3,h4,p1,p2). */ + /* 228 Entering Long Passive Mode (...). */ + /* 229 Entering Extended Passive Mode (...). */ /* 421 Service not available, closing control connection. */ /* 500 Syntax error, command unrecognized. */ /* 501 Syntax error in parameters or arguments. */ @@ -956,7 +988,7 @@ int ap_proxy_ftp_handler(request_rec *r, cache_req *c, char *url) if (i == -1 || i == 421) { return ftp_cleanup_and_return(r, ctrl, data, sock, dsock, ap_proxyerror(r, HTTP_INTERNAL_SERVER_ERROR, - "proxy: PASV: control connection is toast")); + "proxy: passive: control connection is toast")); } else { pasv[i - 1] = '\0'; @@ -984,10 +1016,14 @@ int ap_proxy_ftp_handler(request_rec *r, cache_req *c, char *url) pport = (p1 << 8) + p0; ap_log_error(APLOG_MARK, APLOG_DEBUG | APLOG_NOERRNO, r->server, "FTP: contacting host %d.%d.%d.%d:%d", h3, h2, h1, h0, pport); - data_addr.sin_family = AF_INET; - data_addr.sin_addr.s_addr = htonl(paddr); - data_addr.sin_port = htons(pport); - i = ap_proxy_doconnect(dsock, &data_addr, r); + sin = (struct sockaddr_in *)&data_addr; + sin->sin_family = AF_INET; +#ifdef SIN6_LEN + sin->sin_len = sizeof(*sin); +#endif + sin->sin_addr.s_addr = htonl(paddr); + sin->sin_port = htons(pport); + i = ap_proxy_doconnect(dsock, (struct sockaddr *)&data_addr, r); if (i == -1) { return ftp_cleanup_and_return(r, ctrl, data, sock, dsock, @@ -997,6 +1033,64 @@ int ap_proxy_ftp_handler(request_rec *r, cache_req *c, char *url) strerror(errno), NULL))); } pasvmode = 1; + } else if (presult == 228 && pstr != NULL + && sscanf(pstr, +"%u,%u,%u,%u,%u,%u,%u,%u,%u,%u,%u,%u,%u,%u,%u,%u,%u,%u,%u,%u,%u", + &paf, &holen, &ho[0], &ho[1], &ho[2], &ho[3], + &ho[4], &ho[5], &ho[6], &ho[7], &ho[8], &ho[9], &ho[10], &ho[11], + &ho[12], &ho[13], &ho[14], &ho[15], &polen, &po[0], &po[1]) == 21 + && paf == 6 && holen == 16 && polen == 2) { + int i; + sin6 = (struct sockaddr_in6 *)&data_addr; + sin6->sin6_family = AF_INET6; +#ifdef SIN6_LEN + sin6->sin6_len = sizeof(*sin6); +#endif + for (i = 0; i < 16; i++) + sin6->sin6_addr.s6_addr[i] = ho[i] & 0xff; + sin6->sin6_port = htons(((po[0] & 0xff) << 8) | (po[1] & 0xff)); + i = ap_proxy_doconnect(dsock, (struct sockaddr *)&data_addr, r); + + if (i == -1) { + ap_kill_timeout(r); + return ap_proxyerror(r, HTTP_BAD_GATEWAY, + ap_pstrcat(r->pool, + "Could not connect to remote machine: ", + strerror(errno), NULL)); + } + pasvmode = 1; + } else if (presult == 229 && pstr != NULL + && pstr[0] == pstr[1] && pstr[0] == pstr[2] + && pstr[0] == pstr[strlen(pstr) - 1]) { + /* expect "|||port|" */ +#ifndef SIN6_LEN + memcpy(&data_addr, &server, SA_LEN((struct sockaddr *)&server)); +#else + memcpy(&data_addr, &server, server.ss_len); +#endif + switch (data_addr.ss_family) { + case AF_INET: + sin = (struct sockaddr_in *)&data_addr; + sin->sin_port = htons(atoi(pstr + 3)); + break; + case AF_INET6: + sin6 = (struct sockaddr_in6 *)&data_addr; + sin6->sin6_port = htons(atoi(pstr + 3)); + break; + } + i = ap_proxy_doconnect(dsock, (struct sockaddr *)&data_addr, r); + + if (i == -1) { + ap_kill_timeout(r); + return ap_proxyerror(r, HTTP_BAD_GATEWAY, + ap_pstrcat(r->pool, + "Could not connect to remote machine: ", + strerror(errno), NULL)); + } + pasvmode = 1; + } else if (!lpsvmode && strcmp(cmd, "EPSV") == 0) { + lpsvmode = 1; + goto lpsvagain; } else { ap_pclosesocket(p, dsock); /* and try the regular way */ @@ -1005,14 +1099,14 @@ int ap_proxy_ftp_handler(request_rec *r, cache_req *c, char *url) } if (!pasvmode) { /* set up data connection */ - clen = sizeof(struct sockaddr_in); + clen = sizeof(server); if (getsockname(sock, (struct sockaddr *)&server, &clen) < 0) { return ftp_cleanup_and_return(r, ctrl, data, sock, dsock, ap_proxyerror(r, HTTP_INTERNAL_SERVER_ERROR, "proxy: error getting socket address")); } - dsock = ap_psocket_ex(p, PF_INET, SOCK_STREAM, IPPROTO_TCP, 1); + dsock = ap_psocket_ex(p, server.ss_family, SOCK_STREAM, IPPROTO_TCP, 1); if (dsock == -1) { return ftp_cleanup_and_return(r, ctrl, data, sock, dsock, ap_proxyerror(r, HTTP_INTERNAL_SERVER_ERROR, @@ -1026,13 +1120,28 @@ int ap_proxy_ftp_handler(request_rec *r, cache_req *c, char *url) "proxy: error setting reuseaddr option")); } - if (bind(dsock, (struct sockaddr *)&server, - sizeof(struct sockaddr_in)) == -1) { +#ifndef SIN6_LEN + if (bind(dsock, (struct sockaddr *) &server, SA_LEN((struct sockaddr *)&server)) == -1) +#else + if (bind(dsock, (struct sockaddr *) &server, server.ss_len) == -1) +#endif + { + char hostnamebuf[MAXHOSTNAMELEN], portnamebuf[MAXHOSTNAMELEN]; + + getnameinfo((struct sockaddr *)&server, +#ifndef SIN6_LEN + SA_LEN((struct sockaddr *)&server), +#else + server.ss_len, +#endif + hostnamebuf, sizeof(hostnamebuf), + portnamebuf, sizeof(portnamebuf), + NI_NUMERICHOST | NI_NUMERICSERV); return ftp_cleanup_and_return(r, ctrl, data, sock, dsock, - ap_proxyerror(r, HTTP_INTERNAL_SERVER_ERROR, - ap_psprintf(p, "proxy: error binding to ftp data socket %s:%d", - inet_ntoa(server.sin_addr), server.sin_port))); + ap_proxyerror(r, HTTP_INTERNAL_SERVER_ERROR, + ap_psprintf(p, "proxy: error binding to ftp data socket %s:%s", + hostnamebuf, portnamebuf))); } listen(dsock, 2); /* only need a short queue */ } @@ -1279,7 +1388,7 @@ int ap_proxy_ftp_handler(request_rec *r, cache_req *c, char *url) if (!pasvmode) { /* wait for connection */ ap_hard_timeout("proxy ftp data connect", r); - clen = sizeof(struct sockaddr_in); + clen = sizeof(server); do csd = accept(dsock, (struct sockaddr *)&server, &clen); while (csd == -1 && errno == EINTR); diff --git a/usr.sbin/httpd/src/modules/proxy/proxy_http.c b/usr.sbin/httpd/src/modules/proxy/proxy_http.c index 46630aafe32..6f0b96b5fe5 100644 --- a/usr.sbin/httpd/src/modules/proxy/proxy_http.c +++ b/usr.sbin/httpd/src/modules/proxy/proxy_http.c @@ -151,23 +151,21 @@ int ap_proxy_http_handler(request_rec *r, cache_req *c, char *url, { const char *strp; char *strp2; - const char *err, *desthost, *hostname; + const char *err, *desthost; int i, j, sock,/* len,*/ backasswards; table *req_hdrs, *resp_hdrs; array_header *reqhdrs_arr; table_entry *reqhdrs_elts; - struct sockaddr_in server; - struct in_addr destaddr; - struct hostent server_hp; BUFF *f; char buffer[HUGE_STRING_LEN]; char portstr[32]; pool *p = r->pool; - int destport = 0; - int chunked = 0; + int chunked = 0, destport = 0; char *destportstr = NULL; const char *urlptr = NULL; const char *datestr, *urlstr; + struct addrinfo hints, *res, *res0; + int error; int result, major, minor; const char *content_length; char *peer; @@ -182,20 +180,19 @@ int ap_proxy_http_handler(request_rec *r, cache_req *c, char *url, if (conf->cache.root == NULL) nocache = 1; - memset(&server, '\0', sizeof(server)); - server.sin_family = AF_INET; - /* We break the URL into host, port, path-search */ urlptr = strstr(url, "://"); if (urlptr == NULL) return HTTP_BAD_REQUEST; - urlptr += 3; destport = DEFAULT_HTTP_PORT; + urlptr += 3; ap_hook_use("ap::mod_proxy::http::handler::set_destport", AP_HOOK_SIG2(int,ptr), AP_HOOK_TOPMOST, &destport, r); + ap_snprintf(portstr, sizeof(portstr), "%d", DEFAULT_HTTP_PORT); + destportstr = portstr; strp = strchr(urlptr, '/'); if (strp == NULL) { desthost = ap_pstrdup(p, urlptr); @@ -208,39 +205,88 @@ int ap_proxy_http_handler(request_rec *r, cache_req *c, char *url, urlptr = strp; desthost = q; } + if (*desthost == '['){ + char *u = strrchr(desthost+1, ']'); + if (u){ + desthost++; + *u = '\0'; + if (*(u+1) == ':'){ /* [host]:xx */ + strp2 = u+1; + } + else if (*(u+1) == '\0'){ /* [host] */ + strp2 = NULL; + } + else + return HTTP_BAD_REQUEST; + } + else + return HTTP_BAD_REQUEST; + } + else + strp2 = strrchr(desthost, ':'); - strp2 = strchr(desthost, ':'); if (strp2 != NULL) { *(strp2++) = '\0'; - if (ap_isdigit(*strp2)) { - destport = atoi(strp2); + if (ap_isdigit(*strp2)) destportstr = strp2; - } } - - /* check if ProxyBlock directive on this host */ - destaddr.s_addr = ap_inet_addr(desthost); - for (i = 0; i < conf->noproxies->nelts; i++) { - if (destaddr.s_addr == npent[i].addr.s_addr || - (npent[i].name != NULL && - (npent[i].name[0] == '*' || strstr(desthost, npent[i].name) != NULL))) - return ap_proxyerror(r, HTTP_FORBIDDEN, - "Connect to remote machine blocked"); + memset(&hints, 0, sizeof(hints)); + hints.ai_family = PF_UNSPEC; + hints.ai_socktype = SOCK_STREAM; + hints.ai_protocol = IPPROTO_TCP; + error = getaddrinfo(desthost, destportstr, &hints, &res0); + if (error && proxyhost == NULL) { + return ap_proxyerror(r, HTTP_INTERNAL_SERVER_ERROR, + gai_strerror(error)); /* give up */ + } + + /* check if ProxyBlock directive on this host */ + for (i = 0; i < conf->noproxies->nelts; i++) { + int fail; + struct sockaddr_in *sin; + + fail = 0; + if (npent[i].name != NULL && strstr(desthost, npent[i].name)) + fail++; + if (npent[i].name != NULL && strcmp(npent[i].name, "*") == 0) + fail++; + for (res = res0; res; res = res->ai_next) { + switch (res->ai_family) { + case AF_INET: + sin = (struct sockaddr_in *)res->ai_addr; + if (sin->sin_addr.s_addr == npent[i].addr.s_addr) + fail++; + break; + + } + } + if (fail) { + if (res0 != NULL) + freeaddrinfo(res0); + return ap_proxyerror(r, HTTP_FORBIDDEN, + "Connect to remote machine blocked"); + } } - if (proxyhost != NULL) { - server.sin_port = htons((unsigned short)proxyport); - err = ap_proxy_host2addr(proxyhost, &server_hp); - if (err != NULL) - return DECLINED; /* try another */ - peer = ap_psprintf(p, "%s:%u", proxyhost, proxyport); + char pbuf[10]; + + if (res0 != NULL) + freeaddrinfo(res0); + + ap_snprintf(pbuf, sizeof(pbuf), "%d", proxyport); + memset(&hints, 0, sizeof(hints)); + hints.ai_family = PF_UNSPEC; + hints.ai_socktype = SOCK_STREAM; + hints.ai_protocol = IPPROTO_TCP; + error = getaddrinfo(proxyhost, pbuf, &hints, &res0); + if (error) + return DECLINED; /* try another */ } - else { - server.sin_port = htons((unsigned short)destport); - err = ap_proxy_host2addr(desthost, &server_hp); - if (err != NULL) - return ap_proxyerror(r, HTTP_INTERNAL_SERVER_ERROR, err); - peer = ap_psprintf(p, "%s:%u", desthost, destport); + + + /* check if ProxyBlock directive on this host */ + for (i = 0; i < conf->noproxies->nelts; i++) { + peer = ap_psprintf(p, "%s:%s", desthost, destportstr); } @@ -248,31 +294,29 @@ int ap_proxy_http_handler(request_rec *r, cache_req *c, char *url, * we have worked out who exactly we are going to connect to, now make * that connection... */ - sock = ap_psocket_ex(p, PF_INET, SOCK_STREAM, IPPROTO_TCP, 1); - if (sock == -1) { - ap_log_rerror(APLOG_MARK, APLOG_ERR, r, - "proxy: error creating socket"); - return HTTP_INTERNAL_SERVER_ERROR; - } - - if (conf->recv_buffer_size) { - if (setsockopt(sock, SOL_SOCKET, SO_RCVBUF, - (const char *)&conf->recv_buffer_size, sizeof(int)) - == -1) { - ap_log_rerror(APLOG_MARK, APLOG_ERR, r, - "setsockopt(SO_RCVBUF): Failed to set ProxyReceiveBufferSize, using default"); - } + sock = i = -1; + for (res = res0; res; res = res->ai_next) { + sock = ap_psocket(p, res->ai_family, res->ai_socktype, + res->ai_protocol); + if (sock < 0) + continue; + + if (conf->recv_buffer_size) { + if (setsockopt(sock, SOL_SOCKET, SO_RCVBUF, + (const char *)&conf->recv_buffer_size, sizeof(int)) + == -1) { + ap_log_rerror(APLOG_MARK, APLOG_ERR, r, + "setsockopt(SO_RCVBUF): Failed to set ProxyReceiveBufferSize, using default"); + } + } + + i = ap_proxy_doconnect(sock, res->ai_addr, r); + if (i == 0) + break; + ap_pclosesocket(p, sock); } + freeaddrinfo(res0); - j = 0; - while (server_hp.h_addr_list[j] != NULL) { - memcpy(&server.sin_addr, server_hp.h_addr_list[j], - sizeof(struct in_addr)); - i = ap_proxy_doconnect(sock, &server, r); - if (i == 0) - break; - j++; - } if (i == -1) { if (proxyhost != NULL) return DECLINED; /* try again another way */ @@ -313,39 +357,17 @@ int ap_proxy_http_handler(request_rec *r, cache_req *c, char *url, ap_hard_timeout("proxy send", r); ap_bvputs(f, r->method, " ", proxyhost ? url : urlptr, " HTTP/1.1" CRLF, NULL); - - if (conf->preserve_host) { - hostname = ap_table_get(r->headers_in, "Host"); - if (!hostname) { - hostname = r->server->server_hostname; - ap_log_rerror(APLOG_MARK, APLOG_WARNING, r, - "proxy: No host line on incoming request " - "and preserve host set forcing hostname to " - "be %s for uri %s", hostname, r->uri); - } - strp2 = strchr(hostname, ':'); - if (strp2 != NULL) { - *(strp2++) = '\0'; - if (ap_isdigit(*strp2)) { - destport = atoi(strp2); - destportstr = strp2; - } - } - } - else - hostname = desthost; - { int rc = DECLINED; ap_hook_use("ap::mod_proxy::http::handler::write_host_header", - AP_HOOK_SIG6(int,ptr,ptr,ptr,int,ptr), + AP_HOOK_SIG6(ptr,ptr,ptr,ptr,ptr,ptr), AP_HOOK_DECLINE(DECLINED), - &rc, r, f, hostname, destport, destportstr); + &rc, r, f, desthost, destportstr, destportstr); if (rc == DECLINED) { - if (destportstr != NULL && destport != DEFAULT_HTTP_PORT) - ap_bvputs(f, "Host: ", hostname, ":", destportstr, CRLF, NULL); + if (destportstr != NULL) + ap_bvputs(f, "Host: ", desthost, ":", destportstr, CRLF, NULL); else - ap_bvputs(f, "Host: ", hostname, CRLF, NULL); + ap_bvputs(f, "Host: ", desthost, CRLF, NULL); } } @@ -592,21 +614,31 @@ int ap_proxy_http_handler(request_rec *r, cache_req *c, char *url, if ((urlstr = ap_table_get(resp_hdrs, "Content-Location")) != NULL) ap_table_set(resp_hdrs, "Content-Location", proxy_location_reverse_map(r, urlstr)); - /* check if NoCache directive on this host */ +/* check if NoCache directive on this host */ + { + struct sockaddr_in *sin; + struct sockaddr_in6 *sin6; + if (nocache == 0) { - for (i = 0; i < conf->nocaches->nelts; i++) { - if (destaddr.s_addr == ncent[i].addr.s_addr || - (ncent[i].name != NULL && - (ncent[i].name[0] == '*' || - strstr(desthost, ncent[i].name) != NULL))) { - nocache = 1; - break; - } - } + for (i = 0; i < conf->nocaches->nelts; i++) { + if (ncent[i].name != NULL && + (ncent[i].name[0] == '*' || + strstr(desthost, ncent[i].name) != NULL)) { + nocache = 1; + break; + } + switch (res->ai_addr->sa_family) { + case AF_INET: + sin = (struct sockaddr_in *)res->ai_addr; + if (sin->sin_addr.s_addr == ncent[i].addr.s_addr) { + nocache = 1; + break; + } + } + } - /* - * update the cache file, possibly even fulfilling the request if it - * turns out a conditional allowed us to serve the object from the + /* update the cache file, possibly even fulfilling the request if + * it turns out a conditional allowed us to serve the object from the * cache... */ i = ap_proxy_cache_update(c, resp_hdrs, !backasswards, nocache); @@ -618,6 +650,7 @@ int ap_proxy_http_handler(request_rec *r, cache_req *c, char *url, /* write status line and headers to the cache file */ ap_proxy_write_headers(c, ap_pstrcat(p, "HTTP/1.1 ", r->status_line, NULL), resp_hdrs); } + } /* Setup the headers for our client from upstreams response-headers */ ap_proxy_table_replace(r->headers_out, resp_hdrs); diff --git a/usr.sbin/httpd/src/modules/proxy/proxy_util.c b/usr.sbin/httpd/src/modules/proxy/proxy_util.c index 08a53579c9e..d06f26246b3 100644 --- a/usr.sbin/httpd/src/modules/proxy/proxy_util.c +++ b/usr.sbin/httpd/src/modules/proxy/proxy_util.c @@ -206,6 +206,7 @@ char * int i; char *strp, *host, *url = *urlp; char *user = NULL, *password = NULL; + char *t = NULL, *u = NULL, *v = NULL; if (url[0] != '/' || url[1] != '/') return "Malformed URL"; @@ -244,35 +245,57 @@ char * *passwordp = password; } - strp = strrchr(host, ':'); - if (strp != NULL) { - *(strp++) = '\0'; - - for (i = 0; strp[i] != '\0'; i++) - if (!ap_isdigit(strp[i])) - break; - - /* if (i == 0) the no port was given; keep default */ - if (strp[i] != '\0') { - return "Bad port number in URL"; - } + v = host; + if (*host == '['){ + u = strrchr(host, ']'); + if (u){ + host++; + *u = '\0'; + v = u + 1; + } + } + t = strrchr(v, ':'); + if (t){ + *t = '\0'; + strp = t + 1; + } + if (strp){ + for (i=0; strp[i] != '\0'; i++) + if (!ap_isdigit(strp[i])) + break; + + /* if (i == 0) the no port was given; keep default */ + if (strp[i] != '\0') { + return "Bad port number in URL"; + } else if (i > 0) { - *port = atoi(strp); - if (*port > 65535) - return "Port number in URL > 65535"; - } + *port = atoi(strp); + if (*port > 65535) + return "Port number in URL > 65535"; + } } ap_str_tolower(host); /* DNS names are case-insensitive */ if (*host == '\0') return "Missing host in URL"; /* check hostname syntax */ for (i = 0; host[i] != '\0'; i++) - if (!ap_isdigit(host[i]) && host[i] != '.') - break; + if (!ap_isxdigit(host[i]) && host[i] != '.' && host[i] != ':') + break; /* must be an IP address */ - if (host[i] == '\0' && (ap_inet_addr(host) == -1 || inet_network(host) == -1)) - { - return "Bad IP address in URL"; + if (host[i] == '\0') { + struct addrinfo hints, *res0; + int gai; + memset(&hints, 0, sizeof(hints)); + hints.ai_family = PF_UNSPEC; + hints.ai_flags = AI_NUMERICHOST; + if (gai = getaddrinfo(host, NULL, &hints, &res0)) { +#if 0 + return gai_strerror(gai); +#else + return "Bad IP address in URL"; +#endif + } + freeaddrinfo(res0); } /* if (strchr(host,'.') == NULL && domain != NULL) @@ -1248,18 +1271,41 @@ static int proxy_match_word(struct dirconn_entry *This, request_rec *r) return host != NULL && strstr(host, This->name) != NULL; } -int ap_proxy_doconnect(int sock, struct sockaddr_in *addr, request_rec *r) +int ap_proxy_doconnect(int sock, struct sockaddr *addr, request_rec *r) { int i; + int salen; + char hbuf[NI_MAXHOST], pbuf[NI_MAXSERV]; +#ifdef NI_WITHSCOPEID + const int niflags = NI_NUMERICHOST | NI_NUMERICSERV | NI_WITHSCOPEID; +#else + const int niflags = NI_NUMERICHOST | NI_NUMERICSERV; +#endif ap_hard_timeout("proxy connect", r); +#ifdef HAVE_SOCKADDR_LEN + salen = addr->sa_len; +#else + switch (addr->sa_family) { + case AF_INET6: + salen = sizeof(struct sockaddr_in6); + break; + default: + salen = sizeof(struct sockaddr_in); + break; + } +#endif do { - i = connect(sock, (struct sockaddr *)addr, sizeof(struct sockaddr_in)); + i = connect(sock, addr, salen); } while (i == -1 && errno == EINTR); if (i == -1) { + if (getnameinfo(addr, salen, hbuf, sizeof(hbuf), pbuf, sizeof(pbuf), + niflags) != 0) { + strcpy(hbuf, "?"); + strcpy(pbuf, "?"); + } ap_log_rerror(APLOG_MARK, APLOG_ERR, r, - "proxy connect to %s port %d failed", - inet_ntoa(addr->sin_addr), ntohs(addr->sin_port)); + "proxy connect to %s port %s failed", hbuf, pbuf); } ap_kill_timeout(r); diff --git a/usr.sbin/httpd/src/modules/ssl/Makefile.tmpl b/usr.sbin/httpd/src/modules/ssl/Makefile.tmpl index 92aa0439c05..b0dd6a2203f 100644 --- a/usr.sbin/httpd/src/modules/ssl/Makefile.tmpl +++ b/usr.sbin/httpd/src/modules/ssl/Makefile.tmpl @@ -510,6 +510,18 @@ ssl_util.o ssl_util.lo: ssl_util.c mod_ssl.h $(INCDIR)/ap_config.h \ $(INCDIR)/http_log.h $(INCDIR)/scoreboard.h \ $(INCDIR)/util_md5.h $(INCDIR)/ap_md5.h \ $(INCDIR)/fnmatch.h ssl_expr.h ssl_util_ssl.h ssl_util_table.h +ssl_util_sdbm.o ssl_util_sdbm.lo: ssl_util_sdbm.c mod_ssl.h $(INCDIR)/ap_config.h \ + $(INCDIR)/ap_mmn.h $(INCDIR)/ap_config_auto.h \ + $(OSDIR)/os.h $(OSDIR)/os-inline.c $(INCDIR)/ap_ctype.h \ + $(INCDIR)/httpd.h $(INCDIR)/ap_mm.h $(INCDIR)/ap_alloc.h \ + $(INCDIR)/ap_hook.h $(INCDIR)/ap_ctx.h $(INCDIR)/buff.h \ + $(INCDIR)/ap.h $(INCDIR)/util_uri.h \ + $(INCDIR)/http_config.h $(INCDIR)/http_conf_globals.h \ + $(INCDIR)/http_protocol.h $(INCDIR)/http_request.h \ + $(INCDIR)/http_main.h $(INCDIR)/http_core.h \ + $(INCDIR)/http_log.h $(INCDIR)/scoreboard.h \ + $(INCDIR)/util_md5.h $(INCDIR)/ap_md5.h \ + $(INCDIR)/fnmatch.h ssl_expr.h ssl_util_ssl.h ssl_util_table.h ssl_util_ssl.o ssl_util_ssl.lo: ssl_util_ssl.c mod_ssl.h $(INCDIR)/ap_config.h \ $(INCDIR)/ap_mmn.h $(INCDIR)/ap_config_auto.h \ $(OSDIR)/os.h $(OSDIR)/os-inline.c $(INCDIR)/ap_ctype.h \ diff --git a/usr.sbin/httpd/src/modules/ssl/ssl_engine_ext.c b/usr.sbin/httpd/src/modules/ssl/ssl_engine_ext.c index 61c63c765a8..944ec338619 100644 --- a/usr.sbin/httpd/src/modules/ssl/ssl_engine_ext.c +++ b/usr.sbin/httpd/src/modules/ssl/ssl_engine_ext.c @@ -218,7 +218,7 @@ static int ssl_ext_mp_handler(request_rec *, void *, char *, char *, int, char static int ssl_ext_mp_set_destport(request_rec *); static char *ssl_ext_mp_new_connection(request_rec *, BUFF *, char *); static void ssl_ext_mp_close_connection(void *); -static int ssl_ext_mp_write_host_header(request_rec *, BUFF *, char *, int, char *); +static int ssl_ext_mp_write_host_header(request_rec *, BUFF *, char *, char *, char *); #ifdef SSL_EXPERIMENTAL_PROXY static void ssl_ext_mp_init(server_rec *, pool *); static int ssl_ext_mp_verify_cb(int, X509_STORE_CTX *); @@ -559,7 +559,7 @@ static void ssl_ext_mp_close_connection(void *_fb) } static int ssl_ext_mp_write_host_header( - request_rec *r, BUFF *fb, char *host, int port, char *portstr) + request_rec *r, BUFF *fb, char *host, char *port, char *portstr) { if (ap_ctx_get(r->ctx, "ssl::proxy::enabled") == PFALSE) return DECLINED; diff --git a/usr.sbin/httpd/src/modules/standard/mod_access.c b/usr.sbin/httpd/src/modules/standard/mod_access.c index 68dec7aa3d9..68f752648ee 100644 --- a/usr.sbin/httpd/src/modules/standard/mod_access.c +++ b/usr.sbin/httpd/src/modules/standard/mod_access.c @@ -74,7 +74,8 @@ enum allowdeny_type { T_ALL, T_IP, T_HOST, - T_FAIL + T_FAIL, + T_IP6, }; typedef struct { @@ -85,6 +86,10 @@ typedef struct { struct in_addr net; struct in_addr mask; } ip; + struct { + struct in6_addr net6; + struct in6_addr mask6; + } ip6; } x; enum allowdeny_type type; } allowdeny; @@ -167,93 +172,222 @@ static const char *allow_cmd(cmd_parms *cmd, void *dv, char *from, char *where) } else if ((s = strchr(where, '/'))) { - struct in_addr mask; + struct addrinfo hints, *resnet, *resmask; + struct sockaddr_storage net, mask; + int error; + char *p; + int justdigits; - a->type = T_IP; + a->type = T_FAIL; /*just in case*/ /* trample on where, we won't be using it any more */ *s++ = '\0'; - if (!is_ip(where) - || (a->x.ip.net.s_addr = ap_inet_addr(where)) == INADDR_NONE) { + justdigits = 0; + for (p = s; *p; p++) { + if (!isdigit(*p)) + break; + } + if (!*p) + justdigits++; + + memset(&hints, 0, sizeof(hints)); + hints.ai_family = PF_UNSPEC; + hints.ai_socktype = SOCK_STREAM; /*dummy*/ +#ifdef AI_NUMERICHOST + hints.ai_flags = AI_NUMERICHOST; /*don't resolve*/ +#endif + resnet = NULL; + error = getaddrinfo(where, NULL, &hints, &resnet); + if (error || !resnet) { + if (resnet) + freeaddrinfo(resnet); a->type = T_FAIL; return "syntax error in network portion of network/netmask"; } - - /* is_ip just tests if it matches [\d.]+ */ - if (!is_ip(s)) { + if (resnet->ai_next) { + freeaddrinfo(resnet); a->type = T_FAIL; - return "syntax error in mask portion of network/netmask"; + return "network/netmask resolved to multiple addresses"; } - /* is it in /a.b.c.d form? */ - if (strchr(s, '.')) { - mask.s_addr = ap_inet_addr(s); - if (mask.s_addr == INADDR_NONE) { - a->type = T_FAIL; - return "syntax error in mask portion of network/netmask"; - } + memcpy(&net, resnet->ai_addr, resnet->ai_addrlen); + freeaddrinfo(resnet); + + switch (net.ss_family) { + case AF_INET: + a->type = T_IP; + a->x.ip.net.s_addr = ((struct sockaddr_in *)&net)->sin_addr.s_addr; + break; + case AF_INET6: + a->type = T_IP6; + memcpy(&a->x.ip6.net6, &((struct sockaddr_in6 *)&net)->sin6_addr, + sizeof(a->x.ip6.net6)); + break; + default: + a->type = T_FAIL; + return "unknown address family for network"; } - else { - int i; - /* assume it's in /nnn form */ - i = atoi(s); - if (i > 32 || i <= 0) { + if (!justdigits) { + memset(&hints, 0, sizeof(hints)); + hints.ai_family = PF_UNSPEC; + hints.ai_socktype = SOCK_STREAM; /*dummy*/ +#ifdef AI_NUMERICHOST + hints.ai_flags = AI_NUMERICHOST; /*don't resolve*/ +#endif + resmask = NULL; + error = getaddrinfo(s, NULL, &hints, &resmask); + if (error || !resmask) { + if (resmask) + freeaddrinfo(resmask); a->type = T_FAIL; - return "invalid mask in network/netmask"; + return "syntax error in mask portion of network/netmask"; } - mask.s_addr = 0xFFFFFFFFUL << (32 - i); - mask.s_addr = htonl(mask.s_addr); - } - a->x.ip.mask = mask; - a->x.ip.net.s_addr = (a->x.ip.net.s_addr & mask.s_addr); /* pjr - This fixes PR 4770 */ - } - else if (ap_isdigit(*where) && is_ip(where)) { - /* legacy syntax for ip addrs: a.b.c. ==> a.b.c.0/24 for example */ - int shift; - char *t; - int octet; - - a->type = T_IP; - /* parse components */ - s = where; - a->x.ip.net.s_addr = 0; - a->x.ip.mask.s_addr = 0; - shift = 24; - while (*s) { - t = s; - if (!ap_isdigit(*t)) { + if (resmask->ai_next) { + freeaddrinfo(resmask); a->type = T_FAIL; - return "invalid ip address"; - } - while (ap_isdigit(*t)) { - ++t; + return "network/netmask resolved to multiple addresses"; } - if (*t == '.') { - *t++ = 0; - } - else if (*t) { + memcpy(&mask, resmask->ai_addr, resmask->ai_addrlen); + freeaddrinfo(resmask); + + if (net.ss_family != mask.ss_family) { a->type = T_FAIL; - return "invalid ip address"; + return "network/netmask resolved to different address family"; } - if (shift < 0) { - a->type = T_FAIL; - return "invalid ip address, only 4 octets allowed"; + + switch (a->type) { + case T_IP: + a->x.ip.mask.s_addr = + ((struct sockaddr_in *)&mask)->sin_addr.s_addr; + break; + case T_IP6: + memcpy(&a->x.ip6.mask6, + &((struct sockaddr_in6 *)&mask)->sin6_addr, + sizeof(a->x.ip6.mask6)); + break; } - octet = atoi(s); - if (octet < 0 || octet > 255) { - a->type = T_FAIL; - return "each octet must be between 0 and 255 inclusive"; + } else { + int mask; + mask = atoi(s); + switch (a->type) { + case T_IP: + if (mask < 0 || 32 < mask) { + a->type = T_FAIL; + return "netmask out of range"; + } + a->x.ip.mask.s_addr = htonl(0xFFFFFFFFUL << (32 - mask)); + break; + case T_IP6: + { + int i; + if (mask < 0 || 128 < mask) { + a->type = T_FAIL; + return "netmask out of range"; + } + for (i = 0; i < mask / 8; i++) { + a->x.ip6.mask6.s6_addr[i] = 0xff; + } + if (mask % 8) + a->x.ip6.mask6.s6_addr[i] = 0xff << (8 - (mask % 8)); + break; + } } - a->x.ip.net.s_addr |= (unsigned int)octet << shift; - a->x.ip.mask.s_addr |= 0xFFUL << shift; - s = t; - shift -= 8; } - a->x.ip.net.s_addr = htonl(a->x.ip.net.s_addr); - a->x.ip.mask.s_addr = htonl(a->x.ip.mask.s_addr); } else { - a->type = T_HOST; + struct addrinfo hints, *res; + struct sockaddr_storage ss; + int error; + + a->type = T_FAIL; /*just in case*/ + + /* First, try using the old apache code to match */ + /* legacy syntax for ip addrs: a.b.c. ==> a.b.c.0/24 for example */ + if (ap_isdigit(*where) && is_ip(where)) { + int shift; + char *t; + int octet; + + a->type = T_IP; + /* parse components */ + s = where; + a->x.ip.net.s_addr = 0; + a->x.ip.mask.s_addr = 0; + shift = 24; + while (*s) { + t = s; + if (!ap_isdigit(*t)) { + a->type = T_FAIL; + return "invalid ip address"; + } + while (ap_isdigit(*t)) { + ++t; + } + if (*t == '.') { + *t++ = 0; + } + else if (*t) { + a->type = T_FAIL; + return "invalid ip address"; + } + if (shift < 0) { + return "invalid ip address, only 4 octets allowed"; + } + octet = atoi(s); + if (octet < 0 || octet > 255) { + a->type = T_FAIL; + return "each octet must be between 0 and 255 inclusive"; + } + a->x.ip.net.s_addr |= octet << shift; + a->x.ip.mask.s_addr |= 0xFFUL << shift; + s = t; + shift -= 8; + } + a->x.ip.net.s_addr = ntohl(a->x.ip.net.s_addr); + a->x.ip.mask.s_addr = ntohl(a->x.ip.mask.s_addr); + + return NULL; + } + + /* IPv4/v6 numeric address */ + memset(&hints, 0, sizeof(hints)); + hints.ai_family = PF_UNSPEC; + hints.ai_socktype = SOCK_STREAM; /*dummy*/ +#ifdef AI_NUMERICHOST + hints.ai_flags = AI_NUMERICHOST; /*don't resolve*/ +#endif + res = NULL; + error = getaddrinfo(where, NULL, &hints, &res); + if (error || !res) { + if (res) + freeaddrinfo(res); + a->type = T_HOST; + return NULL; + } + if (res->ai_next) { + freeaddrinfo(res); + a->type = T_FAIL; + return "network/netmask resolved to multiple addresses"; + } + memcpy(&ss, res->ai_addr, res->ai_addrlen); + freeaddrinfo(res); + + switch (ss.ss_family) { + case AF_INET: + a->type = T_IP; + a->x.ip.net.s_addr = ((struct sockaddr_in *)&ss)->sin_addr.s_addr; + memset(&a->x.ip.mask, 0xff, sizeof(a->x.ip.mask)); + break; + case AF_INET6: + a->type = T_IP6; + memcpy(&a->x.ip6.net6, &((struct sockaddr_in6 *)&ss)->sin6_addr, + sizeof(a->x.ip6.net6)); + memset(&a->x.ip6.mask6, 0xff, sizeof(a->x.ip6.mask6)); + break; + default: + a->type = T_FAIL; + return "unknown address family for network"; + } } return NULL; @@ -318,12 +452,59 @@ static int find_allowdeny(request_rec *r, array_header *a, int method) return 1; case T_IP: - if (ap[i].x.ip.net.s_addr != INADDR_NONE - && (r->connection->remote_addr.sin_addr.s_addr - & ap[i].x.ip.mask.s_addr) == ap[i].x.ip.net.s_addr) { - return 1; + if (ap[i].x.ip.net.s_addr == INADDR_NONE) + break; + switch (r->connection->remote_addr.ss_family) { + case AF_INET: + if ((((struct sockaddr_in *)&r->connection->remote_addr)->sin_addr.s_addr + & ap[i].x.ip.mask.s_addr) == ap[i].x.ip.net.s_addr) { + return 1; + } + break; + case AF_INET6: + if (!IN6_IS_ADDR_V4MAPPED(&((struct sockaddr_in6 *)&r->connection->remote_addr)->sin6_addr)) /*XXX*/ + break; + if ((*(uint32_t *)&((struct sockaddr_in6 *)&r->connection->remote_addr)->sin6_addr.s6_addr[12] + & ap[i].x.ip.mask.s_addr) == ap[i].x.ip.net.s_addr) { + return 1; + } + break; + } + break; + + case T_IP6: + { + struct in6_addr masked; + int j; + if (IN6_IS_ADDR_UNSPECIFIED(&ap[i].x.ip6.net6)) + break; + switch (r->connection->remote_addr.ss_family) { + case AF_INET: + if (!IN6_IS_ADDR_V4MAPPED(&ap[i].x.ip6.net6)) /*XXX*/ + break; + memset(&masked, 0, sizeof(masked)); + masked.s6_addr[10] = masked.s6_addr[11] = 0xff; + memcpy(&masked.s6_addr[12], + &((struct sockaddr_in *)&r->connection->remote_addr)->sin_addr.s_addr, + sizeof(struct sockaddr_in)); + for (j = 0; j < sizeof(struct in6_addr); j++) + masked.s6_addr[j] &= ap[i].x.ip6.mask6.s6_addr[j]; + if (memcmp(&masked, &ap[i].x.ip6.net6, sizeof(masked)) == 0) + return 1; + break; + case AF_INET6: + memset(&masked, 0, sizeof(masked)); + memcpy(&masked, + &((struct sockaddr_in6 *)&r->connection->remote_addr)->sin6_addr, + sizeof(masked)); + for (j = 0; j < sizeof(struct in6_addr); j++) + masked.s6_addr[j] &= ap[i].x.ip6.mask6.s6_addr[j]; + if (memcmp(&masked, &ap[i].x.ip6.net6, sizeof(masked)) == 0) + return 1; + break; } break; + } case T_HOST: if (!gothost) { diff --git a/usr.sbin/httpd/src/modules/standard/mod_unique_id.c b/usr.sbin/httpd/src/modules/standard/mod_unique_id.c index 36319a0a47a..b13723a4f2a 100644 --- a/usr.sbin/httpd/src/modules/standard/mod_unique_id.c +++ b/usr.sbin/httpd/src/modules/standard/mod_unique_id.c @@ -70,7 +70,14 @@ typedef struct { unsigned int stamp; - unsigned int in_addr; + union { + uint32_t in; +#ifdef SHORT_UNIQUE_ID + uint32_t in6; +#else + struct in6_addr in6; +#endif + } addr; unsigned int pid; unsigned short counter; } unique_id_rec; @@ -139,7 +146,7 @@ typedef struct { * this shouldn't be a problem till year 2106. */ -static unsigned global_in_addr; +static struct sockaddr_storage global_addr; /* Even when not MULTITHREAD, this will return a single structure, since @@ -168,7 +175,8 @@ static void unique_id_global_init(server_rec *s, pool *p) #define MAXHOSTNAMELEN 256 #endif char str[MAXHOSTNAMELEN + 1]; - struct hostent *hent; + struct addrinfo hints, *res, *res0; + int error; struct timeval tv; unique_id_rec *cur_unique_id = get_cur_unique_id(1); @@ -177,8 +185,8 @@ static void unique_id_global_init(server_rec *s, pool *p) */ unique_id_rec_offset[0] = XtOffsetOf(unique_id_rec, stamp); unique_id_rec_size[0] = sizeof(cur_unique_id->stamp); - unique_id_rec_offset[1] = XtOffsetOf(unique_id_rec, in_addr); - unique_id_rec_size[1] = sizeof(cur_unique_id->in_addr); + unique_id_rec_offset[1] = XtOffsetOf(unique_id_rec, addr); + unique_id_rec_size[1] = sizeof(cur_unique_id->addr); unique_id_rec_offset[2] = XtOffsetOf(unique_id_rec, pid); unique_id_rec_size[2] = sizeof(cur_unique_id->pid); unique_id_rec_offset[3] = XtOffsetOf(unique_id_rec, counter); @@ -204,17 +212,42 @@ static void unique_id_global_init(server_rec *s, pool *p) } str[sizeof(str) - 1] = '\0'; - if ((hent = gethostbyname(str)) == NULL) { + memset(&hints, 0, sizeof(hints)); + hints.ai_family = PF_UNSPEC; + error = getaddrinfo(str, NULL, &hints, &res0); + if (error) { ap_log_error(APLOG_MARK, APLOG_NOERRNO|APLOG_ALERT, s, - "mod_unique_id: unable to gethostbyname(\"%s\")", str); + "mod_unique_id: getaddrinfo failed for \"%s\" (%s)", str, + gai_strerror(error)); exit(1); } - global_in_addr = ((struct in_addr *) hent->h_addr_list[0])->s_addr; + error = 1; + for (res = res0; res; res = res->ai_next) { + switch (res->ai_family) { + case AF_INET: + case AF_INET6: + memcpy(&global_addr, res->ai_addr, res->ai_addrlen); + error = 0; + break; + } + } + freeaddrinfo(res0); + if (error) { + ap_log_error(APLOG_MARK, APLOG_NOERRNO|APLOG_ALERT, s, + "mod_unique_id: no known AF found for \"%s\"", str); + exit(1); + } + getnameinfo((struct sockaddr *)&global_addr, +#ifndef SIN6_LEN + SA_LEN((struct sockaddr *)&global_addr), +#else + global_addr.ss_len, +#endif + str, sizeof(str), NULL, 0, NI_NUMERICHOST); ap_log_error(APLOG_MARK, APLOG_NOERRNO|APLOG_INFO, s, - "mod_unique_id: using ip addr %s", - inet_ntoa(*(struct in_addr *) hent->h_addr_list[0])); + "mod_unique_id: using ip addr %s", str); /* * If the server is pummelled with restart requests we could possibly end @@ -265,7 +298,22 @@ static void unique_id_child_init(server_rec *s, pool *p) "oh no! pids are greater than 32-bits! I'm broken!"); } - cur_unique_id->in_addr = global_in_addr; + memset(&cur_unique_id->addr, 0, sizeof(cur_unique_id->addr)); + switch (global_addr.ss_family) { + case AF_INET: + cur_unique_id->addr.in = + ((struct sockaddr_in *)&global_addr)->sin_addr.s_addr; + break; + case AF_INET6: +#ifdef SHORT_UNIQUE_ID + cur_unique_id->addr.in6 = + ((struct sockaddr_in6 *)&global_addr)->sin6_addr.s6_addr32[3]; +#else + cur_unique_id->addr.in6 = + ((struct sockaddr_in6 *)&global_addr)->sin6_addr; +#endif + break; + } /* * If we use 0 as the initial counter we have a little less protection diff --git a/usr.sbin/httpd/src/modules/standard/mod_usertrack.c b/usr.sbin/httpd/src/modules/standard/mod_usertrack.c index 56f9f556502..37abaf4f314 100644 --- a/usr.sbin/httpd/src/modules/standard/mod_usertrack.c +++ b/usr.sbin/httpd/src/modules/standard/mod_usertrack.c @@ -147,13 +147,17 @@ static char * make_cookie_id(char * buffer, int bufsize, request_rec *r, { struct timeval tv; struct timezone tz = {0, 0}; + char hbuf[NI_MAXHOST]; cookie_dir_rec *dcfg; long reqtime = (long) r->request_time; long clocktime; - unsigned long ipaddr = ntohl(r->connection->remote_addr.sin_addr.s_addr); + getnameinfo((struct sockaddr *)&r->connection->remote_addr, + r->connection->remote_addr.ss_len, + hbuf, sizeof(hbuf), NULL, 0, NI_NUMERICHOST); + const char *rname = ap_get_remote_host(r->connection, r->per_dir_config, REMOTE_NAME); dcfg = ap_get_module_config(r->per_dir_config, &usertrack_module); @@ -167,8 +171,8 @@ static char * make_cookie_id(char * buffer, int bufsize, request_rec *r, clocktime = (long) (tv.tv_usec / 1000); if (cformat == CF_COMPACT) - ap_snprintf(buffer, bufsize, "%s%lx%x%lx%lx", - dcfg->prefix_string, ipaddr, (int) getpid(), + ap_snprintf(buffer, bufsize, "%s%s%x%lx%lx", + dcfg->prefix_string, hbuf, (int) getpid(), reqtime, clocktime); else ap_snprintf(buffer, bufsize, "%s%s.%d%ld%ld", diff --git a/usr.sbin/httpd/src/support/logresolve.c b/usr.sbin/httpd/src/support/logresolve.c index df940d75d65..662145b6d1d 100644 --- a/usr.sbin/httpd/src/support/logresolve.c +++ b/usr.sbin/httpd/src/support/logresolve.c @@ -46,7 +46,7 @@ #include <arpa/inet.h> -static void cgethost(struct in_addr ipnum, char *string, int check); +static void cgethost(struct sockaddr *sa, char *string, int check); static int getline(char *s, int n); static void stats(FILE *output); static void usage(void); @@ -71,7 +71,7 @@ static void usage(void); */ struct nsrec { - struct in_addr ipnum; + struct sockaddr_storage addr; char *hostname; int noname; struct nsrec *next; @@ -102,17 +102,44 @@ static int errors[MAX_ERR + 3]; * IP numbers with their IP number as hostname, setting noname flag */ -static void cgethost (struct in_addr ipnum, char *string, int check) +static void cgethost (struct sockaddr *sa, char *string, int check) { + uint32_t hashval; + struct sockaddr_in *sin; + struct sockaddr_in6 *sin6; struct nsrec **current, *new; - struct hostent *hostdata; char *name; + char hostnamebuf[MAXHOSTNAMELEN]; + + switch (sa->sa_family) { + case AF_INET: + hashval = ((struct sockaddr_in *)sa)->sin_addr.s_addr; + break; + case AF_INET6: + hashval = *(uint32_t *)&((struct sockaddr_in6 *)sa)->sin6_addr.s6_addr[12]; + break; + default: + hashval = 0; + break; + } + + current = &nscache[((hashval + (hashval >> 8) + + (hashval >> 16) + (hashval >> 24)) % BUCKETS)]; - current = &nscache[((ipnum.s_addr + (ipnum.s_addr >> 8) + - (ipnum.s_addr >> 16) + (ipnum.s_addr >> 24)) % BUCKETS)]; + while (*current) { +#ifndef SIN6_LEN + if (SA_LEN(sa) == SA_LEN((struct sockaddr *)&(*current)->addr) + && memcmp(sa, &(*current)->addr, SA_LEN(sa)) == 0) +#else + if (sa->sa_len == (*current)->addr.ss_len + && memcmp(sa, &(*current)->addr, sa->sa_len) == 0) +#endif + { + break; + } - while (*current != NULL && ipnum.s_addr != (*current)->ipnum.s_addr) current = &(*current)->next; + } if (*current == NULL) { cachesize++; @@ -125,45 +152,55 @@ static void cgethost (struct in_addr ipnum, char *string, int check) *current = new; new->next = NULL; - new->ipnum = ipnum; - - hostdata = gethostbyaddr((const char *) &ipnum, sizeof(struct in_addr), - AF_INET); - if (hostdata == NULL) { - if (h_errno > MAX_ERR) - errors[UNKNOWN_ERR]++; - else - errors[h_errno]++; - new->noname = h_errno; - name = strdup(inet_ntoa(ipnum)); - } - else { - new->noname = 0; - name = strdup(hostdata->h_name); - if (check) { - if (name == NULL) { - perror("strdup"); - fprintf(stderr, "Insufficient memory\n"); - exit(1); - } - hostdata = gethostbyname(name); - if (hostdata != NULL) { - char **hptr; - - for (hptr = hostdata->h_addr_list; *hptr != NULL; hptr++) - if (((struct in_addr *) (*hptr))->s_addr == ipnum.s_addr) - break; - if (*hptr == NULL) - hostdata = NULL; - } - if (hostdata == NULL) { - fprintf(stderr, "Bad host: %s != %s\n", name, - inet_ntoa(ipnum)); - new->noname = NO_REVERSE; - free(name); - name = strdup(inet_ntoa(ipnum)); - errors[NO_REVERSE]++; +#ifndef SIN6_LEN + memcpy(&new->addr, sa, SA_LEN(sa)); +#else + memcpy(&new->addr, sa, sa->sa_len); +#endif + + new->noname = getnameinfo(sa, +#ifndef SIN6_LEN + SA_LEN(sa), +#else + sa->sa_len, +#endif + hostnamebuf, sizeof(hostnamebuf), NULL, 0, 0); + name = strdup(hostnamebuf); + if (check) { + struct addrinfo hints, *res; + int error; + memset(&hints, 0, sizeof(hints)); + hints.ai_family = PF_UNSPEC; + error = getaddrinfo(hostnamebuf, NULL, &hints, &res); + if (!error) { + while (res) { +#ifndef SIN6_LEN + if (SA_LEN(sa) == res->ai_addrlen + && memcmp(sa, res->ai_addr, SA_LEN(sa)) == 0) +#else + if (sa->sa_len == res->ai_addrlen + && memcmp(sa, res->ai_addr, sa->sa_len) == 0) +#endif + { + break; + } + res = res->ai_next; } + if (!res) + error++; + } + if (error) { + getnameinfo(sa, +#ifndef SIN6_LEN + SA_LEN(sa), +#else + sa->sa_len, +#endif + hostnamebuf, sizeof(hostnamebuf), NULL, 0, NI_NUMERICHOST); + fprintf(stderr, "Bad host: %s != %s\n", name, hostnamebuf); + new->noname = NO_REVERSE; + free(name); + name = strdup(hostnamebuf); } } new->hostname = name; @@ -191,6 +228,7 @@ static void stats (FILE *output) char *ipstring; struct nsrec *current; char *errstring[MAX_ERR + 3]; + char hostnamebuf[MAXHOSTNAMELEN]; for (i = 0; i < MAX_ERR + 3; i++) errstring[i] = "Unknown error"; @@ -222,7 +260,14 @@ static void stats (FILE *output) for (i = 0; i < BUCKETS; i++) for (current = nscache[i]; current != NULL; current = current->next) { - ipstring = inet_ntoa(current->ipnum); + getnameinfo((struct sockaddr *)¤t->addr, +#ifndef SIN6_LEN + SA_LEN((struct sockaddr *)¤t->addr), +#else + current->addr.ss_len, +#endif + hostnamebuf, sizeof(hostnamebuf), NULL, 0, NI_NUMERICHOST); + ipstring = hostnamebuf; if (current->noname == 0) fprintf(output, " %3d %15s - %s\n", i, ipstring, current->hostname); @@ -259,9 +304,10 @@ static void usage(void) int main (int argc, char *argv[]) { - struct in_addr ipnum; char *bar, hoststring[MAXDNAME + 1], line[MAXLINE], *statfile; int i, check; + struct addrinfo hints, *res; + int error; int ch; check = 0; @@ -301,8 +347,10 @@ int main (int argc, char *argv[]) bar = strchr(line, ' '); if (bar != NULL) *bar = '\0'; - ipnum.s_addr = inet_addr(line); - if (ipnum.s_addr == 0xffffffffu) { + memset(&hints, 0, sizeof(hints)); + hints.ai_family = PF_UNSPEC; + error = getaddrinfo(line, NULL, &hints, &res); + if (error) { if (bar != NULL) *bar = ' '; puts(line); @@ -312,11 +360,12 @@ int main (int argc, char *argv[]) resolves++; - cgethost(ipnum, hoststring, check); + cgethost(res->ai_addr, hoststring, check); if (bar != NULL) printf("%s %s\n", hoststring, bar + 1); else puts(hoststring); + freeaddrinfo(res); } if (statfile != NULL) { |