diff options
author | Marc Balmer <mbalmer@cvs.openbsd.org> | 2008-05-09 08:06:29 +0000 |
---|---|---|
committer | Marc Balmer <mbalmer@cvs.openbsd.org> | 2008-05-09 08:06:29 +0000 |
commit | b7b0df859db0329e6f3db4e1346a9fee12d65aee (patch) | |
tree | ae34c9647bd8defed39f43aaf337bf596fb048e0 /usr.sbin | |
parent | 239c3bdd554989379fb2e7b923658ec5dc632320 (diff) |
Add support for IPv6 while keeping the default at IPv4 to not break
existing installations. See the documentation for the IPv6 related
configuration.
This changes the module ABI since addresses are now struct addrinfo.
This has been tested by many people and run on production machines
for several months.
feedback many, ok todd
Diffstat (limited to 'usr.sbin')
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) { |