diff options
author | jbj <devnull@localhost> | 2004-10-29 13:57:41 +0000 |
---|---|---|
committer | jbj <devnull@localhost> | 2004-10-29 13:57:41 +0000 |
commit | 69227ecd1bf64421da5ee99c14cf1613e1114787 (patch) | |
tree | ababad53ef07514d61ef30f018c84eee59589cd7 /neon | |
parent | e603ae4198c394996d56a34bc69c92a1b16dfddb (diff) | |
download | librpm-tizen-69227ecd1bf64421da5ee99c14cf1613e1114787.tar.gz librpm-tizen-69227ecd1bf64421da5ee99c14cf1613e1114787.tar.bz2 librpm-tizen-69227ecd1bf64421da5ee99c14cf1613e1114787.zip |
Update to neon trunk.
CVS patchset: 7523
CVS date: 2004/10/29 13:57:41
Diffstat (limited to 'neon')
93 files changed, 4700 insertions, 1494 deletions
diff --git a/neon/.release.sh b/neon/.release.sh index a278217a2..2a540ae5e 100755 --- a/neon/.release.sh +++ b/neon/.release.sh @@ -7,12 +7,6 @@ minor=`echo $1 | awk -F. '{print $2;}'` rel=`echo $1 | awk -F. '{print $3;}'` version=$1 -# check that release version matches build version! -grep ^NEON_VERSION_MAJOR=$major\$ macros/neon.m4 || exit 1 -grep ^NEON_VERSION_MINOR=$minor\$ macros/neon.m4 || exit 1 -grep ^NEON_VERSION_RELEASE=$rel\$ macros/neon.m4 || exit 1 -grep '^NEON_VERSION_TAG=$' macros/neon.m4 || exit 1 - for f in config.hw; do in=$f.in out=$f diff --git a/neon/.version b/neon/.version index c9731ff41..cb676de10 100644 --- a/neon/.version +++ b/neon/.version @@ -1 +1 @@ -0.24.7 +0.0.0-dev diff --git a/neon/AUTHORS b/neon/AUTHORS index f64fe1852..660afedc7 100644 --- a/neon/AUTHORS +++ b/neon/AUTHORS @@ -1 +1,6 @@ -Joe Orton <joe@orton.demon.co.uk> +neon is Copyright (C) 1999-2004 Joe Orton <joe@manyfish.co.uk> +Portions are: +Copyright (C) 1999-2000 Tommi Komulainen <Tommi.Komulainen@iki.fi> +Copyright (C) 1999-2000 Peter Boos <pedib@colorfullife.com> +Copyright (C) 1991, 1995, 1996, 1997 Free Software Foundation, Inc. +Copyright (C) 2004 Aleix Conchillo Flaque <aleix@member.fsf.org> @@ -2,6 +2,8 @@ Known problems/bugs in neon -*- text -*- --------------------------- +* look at escaping logic again w.r.t. ?, # characters? + * 2818 requires that a on rejection of the SSL server cert, a "bad certificate" message should be sent - this is not being done currently (and can probably only be done with OpenSSL by actually doing cert verification in the verify @@ -10,19 +12,32 @@ Known problems/bugs in neon -*- text -*- * ne_lock_discover does not handle multiple (shared) locks on a single resource. +* ne_lock_refresh does not update the passed-in lock structure. + * SSL session caching issues; only cache for clean shutdowns, and only cache on shutdown, since the SSL_SESSION may change during - an ne_session. + an ne_session? * what is passed as 'path' to req create hook: auth needs Request-URI; how does that interact with proxies? also they will be passed NULL for a CONNECT request, or "*" possibly as well. -* expect100 support is broken. - * It would be nice to fail with a friendly error message if a client cert is requested by the srever but one is not provided. Currently, returning -1 from the provide_client_cert function would allow that (as it forces the SSL handshake to fail), but that would prevent opportunistic use of client certificates, of the "SSLVerifyClient optional" variety. + +* D.J. Heap has a proxy which returns a 401 in response to a CONNECT; +relax the ne_auth rules to allow this since it's unambiguous. + +* Error handling from ne__pull_request_body/send_request_body is +buggy: socket errors are not distinguished from body provider errors; +the connection must be closed in ne_request after a body provider +error. + +* Check whether the following always return UTF-8-encoded strings: + - ne_ssl_clicert_name + - ne_ssl_cert_identity + diff --git a/neon/ChangeLog b/neon/ChangeLog index bb1a69104..c1e1bd05b 100644 --- a/neon/ChangeLog +++ b/neon/ChangeLog @@ -1,15 +1,59 @@ Mon May 17 21:25:44 2004 Joe Orton <joe@manyfish.co.uk> - * neon.mak: Fix handling of paths with spaces, and simplify (Jon - Foster <jon@jon-foster.co.uk>). + * neon.mak: Fix for handling of paths with spaces, and + simplify (Jon Foster <jon@jon-foster.co.uk>). Thu May 13 11:42:07 2004 Joe Orton <joe@manyfish.co.uk> * configure.in: Don't rely on echo -n in ne_version. -Fri Mar 26 12:07:04 2004 Joe Orton <joe@manyfish.co.uk> +Sun Feb 22 20:29:04 2004 Joe Orton <joe@manyfish.co.uk> - * Makefile.in (distclean): Clean more. + * configure.in: Move memleak.h include to end of config.h. + +Sun Feb 22 18:44:55 2004 Joe Orton <joe@manyfish.co.uk> + + * configure.in: Fix to run DAV tests when an XML parser is + configured. + +Sat Jan 24 17:50:52 2004 Joe Orton <joe@manyfish.co.uk> + + * configure.in: AC_DEFINE _GNU_SOURCE again so that it's used + during compiler checks. + +Sat Jan 24 17:33:54 2004 Joe Orton <joe@manyfish.co.uk> + + * configure.in: Cleanup; move all AC_SUBSTs together; use a single + AH_TOP for config.h.in header; require autoconf 2.58. + +Thu Jan 1 17:40:34 2004 Joe Orton <joe@manyfish.co.uk> + + * neon-config.in: Handle 'lfs' feature. + +Sat Nov 15 09:42:40 2003 Joe Orton <joe@manyfish.co.uk> + + * neon-config.in: Print help output on stderr for unknown arguments. + (usage): Update help output for new NE_FLAG_ substitutions. + +Sat Nov 15 09:24:49 2003 Joe Orton <joe@manyfish.co.uk> + + * configure.in: Update for use latest autoconf best-practice: + s/AC_HELP_STRING/AS_HELP_STRING. + +Fri Nov 14 11:28:24 2003 Joe Orton <joe@manyfish.co.uk> + + * configure.in, neon-config.in: Use new NE_FLAG substitutions + for feature detection. + +Fri Nov 14 09:08:10 2003 Joe Orton <joe@manyfish.co.uk> + + * configure.in: Add -export-symbols-regex to NEON_LINK_FLAGS to + prevent export of ne__ symbols where possible. + +Sun Oct 26 12:42:15 2003 Joe Orton <joe@manyfish.co.uk> + + * neon-config.in: Fix to exit with failure for an unrecognized + option. Sat Oct 25 10:37:59 2003 Joe Orton <joe@manyfish.co.uk> diff --git a/neon/INSTALL.win32 b/neon/INSTALL.win32 index 04e161189..4b10550e0 100644 --- a/neon/INSTALL.win32 +++ b/neon/INSTALL.win32 @@ -1,14 +1,140 @@ -
-To install neon on Windows you need expat-lite already installed on your system.
-It can be taken from the Apache distribution or downloaded from the expat website.
-Now simply point make to the expat sources:
-
- nmake /f neon.mak EXPAT_SRC=\path\to\expat-lite
-
-This should work with Microsoft VC++ 5 and 6.
-
-After compiling the Release subdirectory contains neons.lib, against which you can
-link your program. When you run your program make sure the XMLPARSE.DLL from
-expat is accessable, i.e. is in your PATH.
-
-
+Building neon on Windows uses a single Nmake neon.mak file. By +placing various parameters on nmake's command line, you can specify +exactly the features and behavior of the Neon libraries. The +parameters are additive, so to add more features, add the command line +options specified in the particular section below. + +All the builds described below should work with Microsoft VC++ 5 and +6. + +Build neon +__________ + +This is the most basic version of the Neon library you can build. It +does not require any third party libraries, but you do not get the +full capabilities of Neon. + +Compile Neon with no parameters + + nmake /f neon.mak + +After compiling the library, the directory contains libneon.lib, +against which you can link your program. + + +Build neon with WebDAV support +______________________________ + +To compile Neon with WebDAV support, Neon must compile and link +against a third-party XML parser, either expat, expat-lite, libxml or +libxml2. This Windows neon.mak file is designed to compile and link +against the pre-built Expat Windows libraries version 1.95.X or newer. +This library is available for download from + + http://sourceforge.net/projects/expat/ + +Download the latest expat_win32bin package named + + expat_win32bin_X_YY_Z.exe + +and install it on your system. It wants to install itself into +Q:\some\dir\Expat-X.Y.ZZ. Choose your installation location for expat +and then compile Neon with + + nmake /f neon.mak EXPAT_SRC=\path\to\Expat-X.YY.Z + +NOTE: When you run your program make sure the LIBEXPAT.DLL from expat +is accessible, i.e. is in your PATH. + +This should work with Microsoft VC++ 5 and 6. + + +Build neon with dynamically linked SSL support +______________________________________________ + +To build neon on Windows with SSL support you need OpenSSL already +installed on your system (I used OpenSSL 0.9.6g). It can be +downloaded from + + http://www.openssl.org/source/openssl-0.9.6g.tar.gz + +After compiling OpenSSL, now simply point make to the OpenSSL sources: + + nmake /f neon.mak OPENSSL_SRC=\path\to\openssl + +NOTE: The include files for OpenSSL reside in inc32/ directory +("../openssl-0.9.6g/inc32"). + +NOTE: Make sure that your program is linked against libeay32.lib and +ssleay32.lib (normally in "../openssl-0.9.6g/out32dll") and that +libeay32.dll and ssleay32.dll is accessible, i.e. is in your PATH. + + +Build neon with statically linked OpenSSL support +_________________________________________________ + +If you want to statically link against OpenSSL, then add the +OPENSSL_STATIC parameter. + + nmake /f neon.mak OPENSSL_SRC=\path\to\openssl OPENSSL_STATIC=yes + + +Build neon with statically linked Zlib support +______________________________________________ + +If you want to build Neon with the capability to decompress compressed +content, then you need to compile against the Zlib library. + +Currently, the Neon's neon.mak file expects to compile and link a self +compiled version of Zlib. You need Zlib 1.1.4 or greater. Zlib 1.1.3 +and older has a serious security issue. + +Here's how to compile Zlib. + + 1) Get one of the Zlib source file packages in Zip format from + http://www.gzip.org/zlib/ + 2) Unzip it. + 3) Get the package + http://www.gzip.org/zlib/contrib/zlib113-win32.zip + 4) Unzip it and copy the Makefile from this package to the Zlib + 1.1.4 or greater package. + 5) Run nmake in the Zlib 1.1.4 or greater directory. + +Now add the ZLIB_SRC parameter to Neon's neon.mak pointing to your +newly compiled zlib. + + nmake /f neon.mak ZLIB_SRC=\path\to\zlib + + +Build neon with dynamically linked Zlib support +_______________________________________________ + +To build Neon with dynamically linked Zlib support, use the +instructions for the statically linked Zlib support above and add the +ZLIB_DLL parameter + + nmake /f neon.mak ZLIB_SRC=\path\to\zlib ZLIB_DLL=yes + + +Build neon with IPv6 support +____________________________ + +To build neon with support for IPv6, use parameter ENABLE_IPV6. + + nmake /f neon.mak ENABLE_IPV6=yes + +This requires a copy of the Platform SDK which contains the IPv6 +headers and libraries. + +Build neon with debugging support +_________________________________ + +Set the DEBUG_BUILD parameter + + nmake /f neon.mak DEBUG_BUILD=yes + +It does not matter what value DEBUG_BUILD is set to, as long as it is +not set to "". + +After compiling the library, the directory contains libneonD.lib, +against which you can link your program. diff --git a/neon/Makefile.in b/neon/Makefile.in index ae8a535e3..d0acd6826 100644 --- a/neon/Makefile.in +++ b/neon/Makefile.in @@ -99,7 +99,7 @@ clean: cd test && $(MAKE) clean distclean: clean - rm -rf Makefile config.h config.status libtool config.log config.cache neon-config autom4te.cache neon.pc test/Makefile test/common/Makefile src/Makefile + rm -rf Makefile config.h neon.pc config.status src/Makefile libtool config.log config.cache neon-config autom4te*.cache test/Makefile again: clean @@ -1,3 +1,22 @@ +Changes in release 0.25.0: +* New feature detection interface, ne_has_feature(): + - replaces ne_supports_ssl(); NEON_SSL is no longer defined by neon-config. +* ne_set_request_body_fd takes offset and length arguments and returns void. +* ne_set_request_body_provider takes an off_t length argument. +* Support for non-ASCII hostnames following the IDNA spec, using GNU libidn: + - if enabled, ne_session_create can be passed a UTF-8 encoded hostname. +* ne_xml_failed() replaces ne_xml_valid(), with different return value logic. +* Support for >2Gb request/response bodies on 32-bit Unixes with LFS support: + - new ne_set_request_body_fd64() call for using an fd opened using O_LARGEFILE + - new ne_set_request_body_provider64() which takes an off64_t length argument +* ne_xml interface now rejects more invalid XML element names (e.g. "foo::bar"). +* Add ne_set_addrlist() interface to bypass normal DNS resolution. +* Use a per-request flag to enable "Expect: 100-continue" support: + - ne_set_request_expect100() replaces ne_set_expect100() +* Fix handling of multiple Authentication challenges per request. +* Win32: Fix timezone handling (Jiang Lei). +* Win32: Add IPv6 support using ENABLE_IPV6 flag (Kai Sommerfeld). + Changes in release 0.24.7: * Compression interface fixes: - fix issues handling content decoding and request retries from diff --git a/neon/README b/neon/README index a2a3478c8..d43f4714e 100644 --- a/neon/README +++ b/neon/README @@ -1,5 +1,14 @@ -neon is an HTTP and WebDAV client library. +neon is an HTTP and WebDAV client library, with a C language API. +Bindings for other languages may also be available, see the web site +for more details. + +Mailing list: neon@webdav.org || Web site: http://www.webdav.org/neon/ + +PLEASE NOTE: The neon API is subject to backwards-incompatible change +over minor releases (0.23.x -> 0.24.x) until the 1.0.0 release, but +maintains source and binary backwards compatibility through patch +releases (0.24.0 -> 0.24.7). Current features: @@ -9,17 +18,32 @@ Current features: - Persistent connection support (HTTP/1.1 and HTTP/1.0 aware) - Basic and digest authentication (RFC2617) (including auth-int, md5-sess) - Proxy support (including basic/digest authentication) + - SSL/TLS support using OpenSSL (including client certificate support) - Generic WebDAV 207 XML response handling mechanism - - XML parsing using expat or libxml parser + - XML parsing using expat or libxml (1.x or 2.x) parser - Easy generation of error messages from 207 error responses - Basic HTTP/1.1 methods: GET, PUT, HEAD, OPTIONS, conditional PUT - WebDAV resource manipulation: MOVE, COPY, DELETE, MKCOL. - WebDAV metadata support: set and remove properties (PROPPATCH), query any set of properties (PROPFIND). + - WebDAV locking support + - Autoconf macros supplied for easily embedding neon directly inside + an application source tree. Provides lower-level interfaces to directly implement new HTTP -methods, and higher-level interfaces so that you don't have to -worry about the lower-level stuff. +methods, and higher-level interfaces so that you don't have to worry +about the lower-level stuff. + +neon is licensed under the GNU Library GPL; see src/COPYING.LIB for +full details. The manual is licensed under the terms of the GNU FDL; +see doc/fdl.sgml or the generated documentation. The autoconf macros +in the "macros" directory are under a less restrictive license, see +each file for details. The test suite is licensed under the GNU GPL; +see test/COPYING for full details. -Joe Orton -<joe@orton.demon.co.uk> +neon is Copyright (C) 1999-2004 Joe Orton +Portions are: +Copyright (C) 1999-2000 Tommi Komulainen <Tommi.Komulainen@iki.fi> +Copyright (C) 1999-2000, Peter Boos <pedib@colorfullife.com> +Copyright (C) 1991, 1995, 1996, 1997 Free Software Foundation, Inc. +Copyright (C) 2004 Aleix Conchillo Flaque <aleix@member.fsf.org> diff --git a/neon/THANKS b/neon/THANKS index 404db9e19..281d27069 100644 --- a/neon/THANKS +++ b/neon/THANKS @@ -1,52 +1,14 @@ -In alphabetical order: +Thanks go to the following people for contributing to neon development +with code, patches, or good bug reports or suggestions. -Arun Garg <arung@pspl.co.in> -Blair Zajac <blair@orcaware.com> -Branko Èibej <brane@xbc.nu> -Daniel Berlin <dberlin@dberlin.org> -David Sloat <d.sloat@f5.com> -David Reid <dreid@jetnet.co.uk> -Dirk Bergstrom <dirk@juniper.net> -Gerald Richter <richter@ecos.de> -Greg Stein <gstein@lyra.org> -Gregor Bornemann <Gregor.Bornemann@germany.sun.com> -Jeff Johnson <jbj@redhat.com> -Jeremy Elson <jelson@circlemud.org> -Jim Whitehead <ejw@cse.ucsc.edu> -Johan Lindh <johan@linkdata.se> -Justin Erenkrantz <jerenkrantz@apache.org> -Kai Sommerfeld <kai.sommerfeld@germany.sun.com> -Keith Wannamaker <keith@wannamaker.org> -Lee Mallabone <lee0@callnetuk.com> -Magnus Sirwiö <sirwio@hotmail.com> -Markus Mueller <markus-m.mueller@ubs.com> -Max Bowsher <maxb@ukf.net> -Michael Sobolev <mss@despair.spb.ru> -Mike Rosellini <m@icopyright.com> -Mo DeJong <mdejong@cygnus.com> -Noriaki Takamiya <takamiya@po.ntts.co.jp> -Olof Oberg <mill@pedgr571.sn.umu.se> -Pawel Golaszewski <blues@ds.pg.gda.pl> -Peter Boos <PediB@colorfullife.com> -Peter Moulder <pjm@bofh.asn.au> -rado <dzusto@yahoo.com> -Risko Gergely <risko@risko.hu> -Rodney Dawes <dobey@ximian.com> -Sam TH <sam@uchicago.edu> -Sander Alberink <sander.alberink@cmg.nl> -Sander Striker <striker@apache.org> -Stefan Esser <s.esser@e-matters.de> -Shane Mayer <shanemayer42@yahoo.com> -Taisuke Yamada <tai@iij.ad.jp> -Teng Xu <txu@soe.ucsc.edu> -Tom Bednarz <tombednarz@hotmail.com> -Tom Lee <i_am_gnomey@hotmail.com> -Torsten Kalix <torsten.kalix@bredex.de> -Wilfredo Sánchez <wsanchez@mit.edu> +Arun Garg, Blair Zajac, Branko Èibej, Daniel Berlin, David Sloat, +David Reid, Dirk Bergstrom, Ulrich Drepper, Gerald Richter, Greg +Stein, Gregor Bornemann, Jeff Johnson, Jeremy Elson, Jim Whitehead, +Johan Lindh, Justin Erenkrantz, Kai Sommerfeld, Keith Wannamaker, Lee +Mallabone, Magnus Sirwiö, Markus Mueller, Max Bowsher, Michael +Sobolev, Mike Rosellini, Mo DeJong, Noriaki Takamiya, Olof Oberg, +Pawel Golaszewski, Peter Boos, Peter Moulder, rado, Risko Gergely, +Rodney Dawes, Sam TH, Sander Alberink, Sander Striker, Stefan Esser, +Shane Mayer, Taisuke Yamada, Teng Xu, Tom Bednarz, Tom Lee, Tommi +Komulainen, Torsten Kalix, Wilfredo Sánchez, Daniel Veillard, -Originators of stolen code: - -Tommi Komulainen <Tommi.Komulainen@iki.fi> -Daniel Veillard <Daniel.Veillard@w3.org> -Eric S Raymond <esr@snark.thyrsus.com> -Ulrich Drepper <drepper@gnu.org> @@ -32,7 +32,7 @@ For one-point-oh 62. Select which auth mechanisms are allowed, e.g. JUST SAY NO to basic might very well be useful to some apps. -63. Unconditionally turn off Nagle algorithm. +64. Add options to only enable SSLv2 support, etc. Longer term ----------- @@ -116,3 +116,5 @@ Longer term 57. Add function to map of status-code values to i18n-ized reason phrase. +65. Add ne_uri_copy function and use it in ne_lock_copy. (patch + sent to neon@webdav.org) diff --git a/neon/autogen.sh b/neon/autogen.sh index cc95493b8..471327a1b 100644 --- a/neon/autogen.sh +++ b/neon/autogen.sh @@ -11,12 +11,12 @@ if test ! -f .version; then echo -n 0.0.0-dev > doc/version.xml fi set -e +echo -n "libtoolize... " +${LIBTOOLIZE:-libtoolize} --copy --force >/dev/null echo -n "aclocal... " ${ACLOCAL:-aclocal} -I macros echo -n "autoheader... " ${AUTOHEADER:-autoheader} -echo -n "libtoolize... " -${LIBTOOLIZE:-libtoolize} --copy --force >/dev/null echo -n "autoconf... " ${AUTOCONF:-autoconf} -Wall echo okay. diff --git a/neon/config.hw.in b/neon/config.hw.in index 3d86b2768..7b4ab639f 100644 --- a/neon/config.hw.in +++ b/neon/config.hw.in @@ -1,6 +1,7 @@ /* Win32 config.h Copyright (C) 1999-2000, Peter Boos <pedib@colorfullife.com> + Copyright (C) 2002, 2004, Joe Orton <joe@manyfish.co.uk> This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public @@ -18,25 +19,31 @@ MA 02111-1307, USA */ -#ifdef _WIN32
-#define WIN32
-#endif
+#if defined(_WIN32) && !defined(WIN32) +#define WIN32 +#endif #ifdef WIN32 -
-#define NEON_VERSION "@VERSION@"
-#define NEON_VERSION_MAJOR (@MAJOR@)
-#define NEON_VERSION_MINOR (@MINOR@)
-
-
-#define HAVE_STRING_H -#define HAVE_EXPAT -#define HAVE_OLD_EXPAT -#define HAVE_MEMCPY +#define NEON_VERSION "@VERSION@" +#define NEON_VERSION_MAJOR (@MAJOR@) +#define NEON_VERSION_MINOR (@MINOR@) + +#define HAVE_ERRNO_H +#define HAVE_LIMITS_H #define HAVE_STDLIB_H #define HAVE_STRING_H -#define HAVE_LIMITS_H + +#define HAVE_MEMCPY +#define HAVE_SETSOCKOPT + +#ifndef NEON_NODAV +#define USE_DAV_LOCKS +#endif + +#define NE_FMT_SIZE_T "u" +#define NE_FMT_SSIZE_T "d" +#define NE_FMT_OFF_T "ld" /* Win32 uses a underscore, so we use a macro to eliminate that. */ #define snprintf _snprintf @@ -44,10 +51,10 @@ #define strcasecmp strcmpi #define strncasecmp strnicmp #define ssize_t int -#define inline __inline
-#define off_t _off_t
+#define inline __inline +#define off_t _off_t #include <io.h> #define read _read -
+ #endif diff --git a/neon/configure.in b/neon/configure.in index 0ba04ce81..1b2e0f623 100644 --- a/neon/configure.in +++ b/neon/configure.in @@ -1,6 +1,4 @@ -dnl Require autoconf 2.53 for an AC_C_BIGENDIAN which supports -dnl cross-compiling. -AC_PREREQ(2.53) +AC_PREREQ(2.58) dnl 2.58 required for AS_HELP_STRING dnl Extract the version (sans LF) from .version, created at release-time. m4_define(ne_version, [m4_translit(m4_include(.version), [ @@ -8,7 +6,7 @@ m4_define(ne_version, [m4_translit(m4_include(.version), [ AC_INIT(neon, ne_version, [neon@webdav.org]) -AC_COPYRIGHT([Copyright (c) 2000, 2001, 2002 Joe Orton +AC_COPYRIGHT([Copyright 2000-2004 Joe Orton This configure script may be copied, distributed and modified under the terms of the GNU Library General Public license; see COPYING for more details]) @@ -21,16 +19,22 @@ NEON_WITH_LIBS # libraries which are detected (e.g. OpenSSL) can still be found when # building using the --libs output of neon-config. user_LDFLAGS=$LDFLAGS -AC_SUBST(user_LDFLAGS) # By default, allow 'make install' to work. ALLOW_INSTALL=yes -AC_SUBST(ALLOW_INSTALL) -# Always defined -AC_DEFINE([_GNU_SOURCE], 1, [Unconditionally define _GNU_SOURCE]) -# Defined when neon is built as library -AC_DEFINE(NEON_IS_LIBRARY, 1, [Define when building neon as a library]) +AC_DEFINE([_GNU_SOURCE], 1, [Always defined to enable GNU extensions]) + +dnl Place straight into config.h.in: +AH_TOP([ +/* Only defined when neon source directory built as a standalone library */ +#define NEON_IS_LIBRARY 1]) + +AH_BOTTOM([ +/* Enable leak-tracking versions of ne_*alloc when NEON_MEMLEAK is enabled */ +#ifdef NEON_MEMLEAK +# include "memleak.h" +#endif]) AC_PROG_INSTALL @@ -43,11 +47,8 @@ AC_PROG_LIBTOOL AC_EXEEXT -top_builddir=`pwd` -AC_SUBST(top_builddir) - -AC_ARG_ENABLE(webdav, -AC_HELP_STRING([--disable-webdav], [disable WebDAV support])) +AC_ARG_ENABLE(webdav, + AS_HELP_STRING([--disable-webdav],[disable WebDAV support])) if test "$enable_webdav" = "no"; then NEON_WITHOUT_WEBDAV @@ -60,7 +61,6 @@ fi # The bundled macros also set this, which makes sure we recurse # into the 'src' directory. NEON_BUILD_BUNDLED=yes -AC_SUBST(NEON_BUILD_BUNDLED) # Define NEON_VERSION* and make the appropriate substitutions. NEON_VERSIONS @@ -68,6 +68,10 @@ NEON_VERSIONS # Pass the interface version on to libtool when linking libneon.la NEON_LINK_FLAGS="-version-info ${NEON_INTERFACE_VERSION}" +# Library-internal symbols are in the ne__ namespace: tell libtool +# to not export these from the built library if possible. +NEON_LINK_FLAGS="$NEON_LINK_FLAGS -export-symbols-regex '^ne_[[^_]]'" + # Checks to compile test suite NEON_TEST @@ -82,20 +86,11 @@ NEON_DEBUG # Leave till last to prevent CFLAGS affecting checks. NEON_WARNINGS -CFLAGS="$CFLAGS -I\${top_builddir}" - -dnl Substitute NEON_VERSION for neon-config too. -AC_SUBST(NEON_VERSION) +CPPFLAGS="$CPPFLAGS -I\${top_builddir}" AC_ARG_ENABLE(memleak, -AC_HELP_STRING([--enable-memleak], [for test builds only: enable memory leak checking])) - -dnl Have autoheader include the following template in config.h.in: -AH_VERBATIM([NEON_MEMLEAK], -[/* Enable memory leak detection. */ -#ifdef NEON_MEMLEAK -# include "memleak.h" -#endif]) + AS_HELP_STRING([--enable-memleak], + [for test builds only: enable memory leak checking])) if test "$enable_memleak" = "yes"; then CPPFLAGS="$CPPFLAGS -DNEON_MEMLEAK -I\$(top_srcdir)/src" @@ -106,7 +101,7 @@ fi # Enable tests for optional features TESTS="\$(BASIC_TESTS)" HELPERS="" -if test "$NEON_SUPPORTS_SSL" = "yes"; then +if test $NE_FLAG_SSL = yes; then # Only enable SSL tests if an openssl binary is found (needed to make # certs etc). AC_PATH_PROG(OPENSSL, openssl, notfound) @@ -117,19 +112,25 @@ if test "$NEON_SUPPORTS_SSL" = "yes"; then AC_MSG_WARN([no openssl binary in \$PATH: SSL tests disabled]) fi fi -if test "$NEON_SUPPORTS_DAV" = "yes"; then - TESTS="$TESTS \$(DAV_TESTS)" -fi -if test "$NEON_SUPPORTS_ZLIB" = "yes"; then +if test $NE_FLAG_ZLIB = yes; then TESTS="$TESTS \$(ZLIB_TESTS)" HELPERS="$HELPERS \$(ZLIB_HELPERS)" fi -AC_SUBST(HELPERS) -AC_SUBST(TESTS) +if test x$enable_webdav != xno; then + TESTS="$TESTS \$(DAV_TESTS)" +fi AC_CONFIG_FILES([neon-config], [chmod +x neon-config]) AC_CONFIG_FILES([Makefile src/Makefile test/Makefile neon.pc]) +AC_SUBST(NEON_VERSION) +AC_SUBST(NEON_BUILD_BUNDLED) +AC_SUBST(top_builddir) +AC_SUBST(user_LDFLAGS) +AC_SUBST(HELPERS) +AC_SUBST(TESTS) +AC_SUBST(ALLOW_INSTALL) + AC_OUTPUT # for VPATH builds: @@ -140,8 +141,8 @@ AC_MSG_NOTICE([Configured to build AC_PACKAGE_STRING: Install prefix: ${prefix} Compiler: ${CC} XML Parser: ${neon_xml_parser_message} - SSL library: ${neon_ssl_message} - zlib support: ${neon_zlib_message} + SSL library: ${ne_SSL_message} + zlib support: ${ne_ZLIB_message} Build libraries: Shared=${enable_shared}, Static=${enable_static} Now run 'make' to compile the neon library. diff --git a/neon/doc/biblio.xml b/neon/doc/biblio.xml index cda37a76c..43574b838 100644 --- a/neon/doc/biblio.xml +++ b/neon/doc/biblio.xml @@ -10,12 +10,11 @@ <pubdate>March 2001</pubdate> </biblioentry> -<biblioentry id="bib.xsltrec"> +<biblioentry id="bib.xmlnames"> <abbrev>REC-XML-names</abbrev> - <editor><firstname>James</firstname><surname>Clark</surname></editor> - <title><ulink url="http://www.w3.org/TR/xslt">XSL Transformations - (XSLT) Version 1.0</ulink></title> <publishername>W3C - Recommendation</publishername> <pubdate>16 November 1999</pubdate> + <corpauthor>World Wide Web Consortium</corpauthor> + <title><ulink url="http://www.w3.org/TR/REC-xml-names">Namespaces in XML</ulink></title> + <pubdate>January 1999</pubdate> </biblioentry> <biblioentry id="bib.rfc2616"> diff --git a/neon/doc/date.xml b/neon/doc/date.xml index 4a8caa9a9..1e8d8d669 100644 --- a/neon/doc/date.xml +++ b/neon/doc/date.xml @@ -1 +1 @@ - 5 July 2004
\ No newline at end of file +29 October 2004
\ No newline at end of file diff --git a/neon/doc/manual.xml b/neon/doc/manual.xml index 06e7bca22..f665abd71 100644 --- a/neon/doc/manual.xml +++ b/neon/doc/manual.xml @@ -69,6 +69,7 @@ <!ENTITY refresolve SYSTEM "ref/resolve.xml"> <!ENTITY refiaddr SYSTEM "ref/iaddr.xml"> <!ENTITY refclicert SYSTEM "ref/clicert.xml"> +<!ENTITY refxml SYSTEM "ref/xml.xml"> ]> @@ -170,6 +171,7 @@ ignoring the WebDAV support if desired.</para> &refstatus; <!-- ne_status --> &reftok; <!-- ne_token --> &refvers; <!-- ne_version_match --> + &refxml; <!-- ne_xml_parser --> <!-- REFEND --> <!-- ******************************************************************* --> diff --git a/neon/doc/ref/clicert.xml b/neon/doc/ref/clicert.xml index 49f95cfbb..67833aa77 100644 --- a/neon/doc/ref/clicert.xml +++ b/neon/doc/ref/clicert.xml @@ -62,9 +62,9 @@ <para>The <function>ne_ssl_clicert_read</function> function reads a <firstterm>client certificate</firstterm> from a PKCS#12-formatted file, and returns an - <type>ne_ssl_client_certificate</type> object. If the client + <type>ne_ssl_client_cert</type> object. If the client certificate is encrypted, it must be decrypted before it is used. - An <type>ne_ssl_client_certificate</type> object holds a client + An <type>ne_ssl_client_cert</type> object holds a client certificate and the associated private key, not just a certificate; the term "<glossterm>client certificate</glossterm>" will used to refer to this pair.</para> @@ -124,7 +124,7 @@ <para>The following code reads a client certificate and decrypts it if necessary, then loads it into an HTTP session.</para> - <programlisting>ne_ssl_client_certificate *ccert; + <programlisting>ne_ssl_client_cert *ccert; ccert = ne_ssl_clicert_read("/path/to/client.p12"); diff --git a/neon/doc/ref/iaddr.xml b/neon/doc/ref/iaddr.xml index cf64a13c1..a4fba59b7 100644 --- a/neon/doc/ref/iaddr.xml +++ b/neon/doc/ref/iaddr.xml @@ -9,6 +9,7 @@ <refname id="ne_iaddr_make">ne_iaddr_make</refname> <refname id="ne_iaddr_cmp">ne_iaddr_cmp</refname> <refname id="ne_iaddr_print">ne_iaddr_print</refname> + <refname id="ne_iaddr_typeof">ne_iaddr_typeof</refname> <refname id="ne_iaddr_free">ne_iaddr_free</refname> <refpurpose>functions to manipulate and compare network addresses</refpurpose> </refnamediv> @@ -32,8 +33,8 @@ typedef enum { <funcprototype> <funcdef>int <function>ne_iaddr_cmp</function></funcdef> - <paramdef>const ne_inet_addr *<parameter>i1</parameter></paramdef> - <paramdef>const ne_inet_addr *<parameter>i2</parameter></paramdef> + <paramdef>const ne_inet_addr *<parameter>ia1</parameter></paramdef> + <paramdef>const ne_inet_addr *<parameter>ia2</parameter></paramdef> </funcprototype> <funcprototype> @@ -44,8 +45,13 @@ typedef enum { </funcprototype> <funcprototype> + <funcdef>ne_iaddr_type <function>ne_iaddr_typeof</function></funcdef> + <paramdef>const ne_inet_addr *<parameter>ia</parameter></paramdef> + </funcprototype> + + <funcprototype> <funcdef>void <function>ne_iaddr_free</function></funcdef> - <paramdef>const ne_inet_addr *<parameter>addr</parameter></paramdef> + <paramdef>const ne_inet_addr *<parameter>ia</parameter></paramdef> </funcprototype> </funcsynopsis> @@ -76,6 +82,9 @@ typedef enum { buffer, for instance the string <literal>"127.0.0.1"</literal>.</para> + <para><function>ne_iaddr_type</function> returns the type of the + given network address.</para> + <para><function>ne_iaddr_free</function> releases the memory associated with a network address object.</para> diff --git a/neon/doc/ref/sslca.xml b/neon/doc/ref/sslca.xml new file mode 100644 index 000000000..c68739d7a --- /dev/null +++ b/neon/doc/ref/sslca.xml @@ -0,0 +1,81 @@ + <refentry id="refsslca"> + + <refmeta> + <refentrytitle>ne_ssl_load_ca</refentrytitle> + <manvolnum>3</manvolnum> + </refmeta> + + <refnamediv> + <refname id="ne_ssl_load_ca">ne_ssl_load_ca</refname> + <refname id="ne_ssl_load_default_ca">ne_ssl_load_default_ca</refname> + <refpurpose>load SSL Certificate Authorities</refpurpose> + </refnamediv> + + <refsynopsisdiv> + + <funcsynopsis> + + <funcsynopsisinfo>#include <ne_session.h></funcsynopsisinfo> + + <funcprototype> + <funcdef>int <function>ne_ssl_load_ca</function></funcdef> + <paramdef>ne_session *<parameter>session</parameter></paramdef> + <paramdef>const char *<parameter>filename</parameter></paramdef> + </funcprototype> + + <funcprototype> + <funcdef>int <function>ne_ssl_load_default_ca</function></funcdef> + <paramdef>ne_session *<parameter>session</parameter></paramdef> + </funcprototype> + + </funcsynopsis> + + </refsynopsisdiv> + + <refsect1> + <title>Description</title> + + <para>To indicate that a given CA certificate is trusted by the user, +the certificate can be loaded using the <function>ne_ssl_load_ca</function> +function. The <parameter>filename</parameter> parameter given must specify +the location of a PEM-encoded CA certificate.</para> + + <para>The SSL library in use by neon may include a default set +of CA certificates; calling the +<function>ne_ssl_load_default_ca</function> function will indicate +that these CAs are trusted by the user.</para> + + <para>If no CA certificates are loaded, or the server presents +a certificate which is invalid in some way, then the certificate must +be manually verified (see <xref linkend="ne_ssl_set_verify"/>), otherwise the +connection will fail.</para> + + </refsect1> + + <refsect1> + <title>Return value</title> + + <para>Both <function>ne_ssl_load_ca</function> and +<function>ne_ssl_load_default_ca</function> functions return +<literal>0</literal> on success, or non-zero on failure.</para> + + </refsect1> + + <refsect1> + <title>Examples</title> + + <para>Load the CA certificate stored in <filename>/path/to/cacert.pem</filename>:</para> + <programlisting>&egsess; + +if (ne_ssl_load_ca(sess, "/path/to/cacert.pem")) { + printf("Could not load CA cert: %s\n", ne_get_error(sess)); +}</programlisting> + </refsect1> + + <refsect1> + <title>See also</title> + + <para><xref linkend="ne_get_error"/>, <xref + linkend="ne_ssl_set_verify"/></para> </refsect1> + + </refentry> diff --git a/neon/doc/ref/xml.xml b/neon/doc/ref/xml.xml new file mode 100644 index 000000000..e61a4515c --- /dev/null +++ b/neon/doc/ref/xml.xml @@ -0,0 +1,56 @@ +<refentry id="refxml"> + + <refmeta> + <refentrytitle>ne_xml_create</refentrytitle> + <manvolnum>3</manvolnum> + </refmeta> + + <refnamediv> + <refname id="ne_xml_create">ne_xml_create</refname> + <refname id="ne_xml_destroy">ne_xml_destroy</refname> + <refpurpose>create and destroy an XML parser</refpurpose> + </refnamediv> + + <refsynopsisdiv> + + <funcsynopsis> + + <funcsynopsisinfo>#include <ne_xml.h></funcsynopsisinfo> + + <funcprototype> + <funcdef>ne_xml_parser *<function>ne_xml_create</function></funcdef> + <void/> + </funcprototype> + + <funcprototype> + <funcdef>void <function>ne_xml_destroy</function></funcdef> + <paramdef>ne_xml_parser *<parameter>parser</parameter></paramdef> + </funcprototype> + + </funcsynopsis> + + </refsynopsisdiv> + + <refsect1> + <title>Description</title> + + <para>The <function>ne_xml_create</function> function creates an + XML parser object, which can be used for parsing XML documents + using stacked SAX handlers.</para> + + </refsect1> + + <refsect1> + <title>Return value</title> + + <para><function>ne_xml_create</function> returns a pointer to an + XML parser object, and never &null;</para> </refsect1> + + <refsect1> + <title>See also</title> + + <para>XXX</para> + </refsect1> + +</refentry> + diff --git a/neon/doc/using.xml b/neon/doc/using.xml index efb53d199..e88c9efb4 100644 --- a/neon/doc/using.xml +++ b/neon/doc/using.xml @@ -73,11 +73,11 @@ </sect1> <sect1 id="compliance"> - <title>Protocol compliance</title> + <title>Standards compliance</title> - <para>&neon; is intended to be compliant with the IETF - protocol standards it implements, with a few exceptions where - real-world use has necessitated minor deviations. These + <para>&neon; is intended to be compliant with the IETF and W3C + standards which it implements, with a few exceptions due to + practical necessity or interoperability issues. These exceptions are documented in this section.</para> <sect2><title>RFC 2518, HTTP Extensions for Distributed Authoring—WebDAV</title> @@ -116,4 +116,25 @@ quotes—this is not known to cause any problems with other server implementations.</para></sect2> + <sect2> + <title>Namespaces in XML</title> + + <para>The &neon; XML parser interface will accept and parse + without error some XML documents which are well-formed + according to the XML specification but do not conform to the + "Namespaces in XML" specification <xref + linkend="bib.xmlnames"/>. Specifically: the restrictions on + the first character of the <literal>NCName</literal> rule are + not all implemented; &neon; will allow any + <literal>CombiningChar</literal>, <literal>Extender</literal> + and some characters from the <literal>Digit</literal> class in + this position.</para> </sect2> + + <!-- a few RFC2818/3280 issues: rules about when to cache + sessions in the face of unclean shutdown are strict, neon is + probably not compliant: document or fix. Likewise SSL + shutdown issues in general. Cert hostname checks allow + wildcard "*." syntax which is less than 2818 but more than + 3280 requires. --> + </sect1> diff --git a/neon/doc/version.xml b/neon/doc/version.xml index 1380da071..3b2e7a037 100644 --- a/neon/doc/version.xml +++ b/neon/doc/version.xml @@ -1 +1 @@ -0.24.7
\ No newline at end of file +0.0.0-dev
\ No newline at end of file diff --git a/neon/macros/ChangeLog b/neon/macros/ChangeLog index 24e0824de..ad265430b 100644 --- a/neon/macros/ChangeLog +++ b/neon/macros/ChangeLog @@ -1,13 +1,24 @@ +Sun Sep 12 18:38:13 2004 Joe Orton <joe@manyfish.co.uk> + + * neon.m4 (NEON_USE_EXTERNAL): Check for IDNA, LFS, SOCKS + support. + (NEON_SOCKS): Use common feature code for SOCKSv5 support. + +Fri Sep 10 20:52:54 2004 Joe Orton <joe@manyfish.co.uk> + + * neon.m4 (LIBNEON_SOURCE_CHECKS): Safer autoheader template for + declaring stpcpy as necessary for bundled neon builds. + (NEON_WARNINGS): Drop -Winline. + +Wed Aug 25 19:44:26 2004 Joe Orton <joe@manyfish.co.uk> + + * neon.m4 (LIBNEON_SOURCE_CHECKS): Check for poll. + Sat Jul 3 11:39:01 2004 Joe Orton <joe@manyfish.co.uk> * neon.m4 (LIBNEON_SOURCE_CHECKS): Pick up gethostbyname in -lsocket for QNX. -Wed May 19 08:36:44 2004 Joe Orton <joe@manyfish.co.uk> - - * neon.m4 (LIBNEON_SOURCE_CHECKS): Declare stpcpy on modern - "Linux-like" AIXes. - Fri Apr 16 11:43:10 2004 Joe Orton <joe@manyfish.co.uk> * neon-xml-parser.m4 (NEON_XML_PARSER): If built using libtool, @@ -21,6 +32,87 @@ Tue Apr 13 20:51:59 2004 Joe Orton <joe@manyfish.co.uk> * neon.m4 (NEON_GSSAPI): Check for presence of gssapi/gssapi_generic.h. +Wed Apr 7 13:16:33 2004 Joe Orton <joe@manyfish.co.uk> + + * neon.m4 (NE_LARGEFILE): Check for strtoq. + +Mon Mar 15 19:59:36 2004 Joe Orton <joe@manyfish.co.uk> + + * neon.m4 (LIBNEON_SOURCE_CHECKS): Be safer around getaddrinfo + blacklist for HP-UX and reference why it's needed. + +Sun Mar 7 11:15:44 2004 Joe Orton <joe@manyfish.co.uk> + + * neon.m4 (LIBNEON_SOURCE_CHECKS): Use NE_LARGEFILE in-place + rather than AC_REQUIRE'ing it. + (NE_LARGEFILE): Add NE_LFS to CPPFLAGS for use in bundled builds. + +Mon Feb 23 23:02:54 2004 Joe Orton <joe@manyfish.co.uk> + + * neon.m4 (NE_SNPRINTF): Define HAVE_TRIO if trio is used. + +Mon Feb 23 00:22:39 2004 Joe Orton <joe@manyfish.co.uk> + + * neon.m4 (LIBNEON_SOURCE_CHECKS): Give INCLUDES argument to + AC_CHECK_HEADERS; prevent warning from cpp test for netinet/in.h + on some platforms. + +Sun Feb 22 17:52:42 2004 Joe Orton <joe@manyfish.co.uk> + + * neon.m4 (NE_SEARCH_LIBS): Fix to run actions-if-found if + function is found without needing additional libraries. + (LIBNEON_SOURCE_CHECKS): Only check for gethostbyname if + getaddrinfo is not found. Disable getaddrinfo on HP-UX 11.[01]* + here rather than ne_socket.c. + +Sat Jan 24 17:49:50 2004 Joe Orton <joe@manyfish.co.uk> + + * neon.m4 (LIBNEON_SOURCE_CHECKS): Also check for __tm_gmtoff in + struct tm. + +Sat Jan 24 17:16:48 2004 Joe Orton <joe@manyfish.co.uk> + + * neon.m4: Remove -ansi-pedantic and -Wimplicit-prototypes for gcc + 3.4 compatibility (thanks to Olaf Hering). + +Sat Jan 3 14:11:14 2004 Joe Orton <joe@manyfish.co.uk> + + * neon-test.m4: Check for stdint.h. + +Sat Jan 3 13:17:21 2004 Joe Orton <joe@manyfish.co.uk> + + * neon.m4 (NE_LARGEFILE): Add NE_LFS to neon-config --cflags + output. + +Thu Jan 1 18:42:56 2004 Joe Orton <joe@manyfish.co.uk> + + * neon.m4 (NEON_FORMAT): Use C99 'll' rather than non-standard 'q' + length modifier. + +Thu Jan 1 17:36:39 2004 Joe Orton <joe@manyfish.co.uk> + + * neon.m4 (NE_LARGEFILE): New macro. + (LIBNEON_SOURCE_CHECKS): Call it. + +Sat Nov 15 09:25:43 2003 Joe Orton <joe@manyfish.co.uk> + + * neon.m4, neon-xml-parser.m4: Update for latest autoconf + best-practice: s/AC_HELP_STRING/AS_HELP_STRING, replace AC_TRY_RUN + with AC_RUN_IFELSE, AC_TRY_LINK_FUNC with AC_LINK_IFELSE, + AC_TRY_COMPILE with AC_COMPILE_IFELSE, remove AC_LANG_C and + AC_PROG_CC_STDC, + +Fri Nov 14 13:12:10 2003 Joe Orton <joe@manyfish.co.uk> + + * neon.m4 (NEON_LIBIDN): New macro. + (LIBNEON_SOURCE_CHECKS): Use NEON_LIBIDN. + +Fri Nov 14 11:28:58 2003 Joe Orton <joe@manyfish.co.uk> + + * neon.m4 (NE_ENABLE_SUPPORT, NE_DISABLE_SUPPORT): New macros. + Use throughout to flag support or lack of support for optional + features. + Thu Nov 13 20:25:28 2003 Joe Orton <joe@manyfish.co.uk> * neon.m4 (LIBNEON_SOURCE_CHECKS): Check for gethostbyname in @@ -293,6 +385,11 @@ Sun May 19 20:23:55 2002 Joe Orton <joe@manyfish.co.uk> * neon.m4 (NEON_WARNINGS): Remove with_warnings variable; simplify. +Wed May 19 08:36:44 2004 Joe Orton <joe@manyfish.co.uk> + + * neon.m4 (LIBNEON_SOURCE_CHECKS): Declare stpcpy on modern + "Linux-like" AIXes. + Sun May 19 09:35:08 2002 Joe Orton <joe@manyfish.co.uk> * neon.m4 (NE_FIND_AR): Fix $PATH handling on some Linux diff --git a/neon/macros/neon-test.m4 b/neon/macros/neon-test.m4 index 7a9fa23bc..aec38dd96 100644 --- a/neon/macros/neon-test.m4 +++ b/neon/macros/neon-test.m4 @@ -1,4 +1,4 @@ -# Copyright (C) 2001-2002 Joe Orton <joe@manyfish.co.uk> -*- autoconf -*- +# Copyright (C) 2001-2004 Joe Orton <joe@manyfish.co.uk> -*- autoconf -*- # # This file is free software; you may copy and/or distribute it with # or without modifications, as long as this notice is preserved. @@ -29,7 +29,7 @@ dnl CPPFLAGS which make "gcc -Werror" fail in NEON_FORMAT; suggest dnl this macro is used first. AC_BEFORE([$0], [NEON_XML_PARSER]) -AC_CHECK_HEADERS(sys/time.h) +AC_CHECK_HEADERS(sys/time.h stdint.h) AC_CHECK_FUNCS(pipe isatty usleep shutdown) diff --git a/neon/macros/neon-xml-parser.m4 b/neon/macros/neon-xml-parser.m4 index 38a804e16..7fb033b4b 100644 --- a/neon/macros/neon-xml-parser.m4 +++ b/neon/macros/neon-xml-parser.m4 @@ -86,14 +86,14 @@ AC_DEFUN([NEON_XML_PARSER], [ dnl Switches to force choice of library AC_ARG_WITH([libxml2], -AC_HELP_STRING([--with-libxml2], [force use of libxml 2.x])) +AS_HELP_STRING([--with-libxml2], [force use of libxml 2.x])) AC_ARG_WITH([expat], -AC_HELP_STRING([--with-expat], [force use of expat])) +AS_HELP_STRING([--with-expat], [force use of expat])) dnl Flag to force choice of included expat, if available. ifelse($#, 1, [ AC_ARG_WITH([included-expat], -AC_HELP_STRING([--with-included-expat], [use bundled expat sources]),, +AS_HELP_STRING([--with-included-expat], [use bundled expat sources]),, with_included_expat=no)], with_included_expat=no) diff --git a/neon/macros/neon.m4 b/neon/macros/neon.m4 index 0d7401476..6baef5157 100644 --- a/neon/macros/neon.m4 +++ b/neon/macros/neon.m4 @@ -1,4 +1,5 @@ # Copyright (C) 1998-2004 Joe Orton <joe@manyfish.co.uk> -*- autoconf -*- +# Copyright (C) 2004 Aleix Conchillo Flaque <aleix@member.fsf.org> # # This file is free software; you may copy and/or distribute it with # or without modifications, as long as this notice is preserved. @@ -88,7 +89,7 @@ AC_DEFUN([NEON_COMMON_BUNDLED],[ AC_PREREQ(2.50) AC_ARG_WITH(included-neon, -AC_HELP_STRING([--with-included-neon], [force use of included neon library]), +AS_HELP_STRING([--with-included-neon], [force use of included neon library]), [neon_force_included="$withval"], [neon_force_included="no"]) NEON_COMMON @@ -121,9 +122,9 @@ AC_DEFUN([NEON_VERSIONS], [ # Define the current versions. NEON_VERSION_MAJOR=0 -NEON_VERSION_MINOR=24 -NEON_VERSION_RELEASE=7 -NEON_VERSION_TAG= +NEON_VERSION_MINOR=25 +NEON_VERSION_RELEASE=0 +NEON_VERSION_TAG=-dev NEON_VERSION="${NEON_VERSION_MAJOR}.${NEON_VERSION_MINOR}.${NEON_VERSION_RELEASE}${NEON_VERSION_TAG}" @@ -173,15 +174,16 @@ else ne_libver=`$NEON_CONFIG --version | sed -e "s/neon //g"` # Check whether it's possible to link against neon AC_CACHE_CHECK([linking against neon], [ne_cv_lib_neon], - AC_TRY_LINK_FUNC([ne_version_match], - [ne_cv_lib_neon=yes], [ne_cv_lib_neon=no])) + [AC_LINK_IFELSE( + [AC_LANG_PROGRAM([[#include <ne_utils.h>]], [[ne_version_match(0, 0);]])], + [ne_cv_lib_neon=yes], [ne_cv_lib_neon=no])]) if test "$ne_cv_lib_neon" = "yes"; then # Now check whether the neon library version is satisfactory AC_CACHE_CHECK([neon library version], [ne_cv_lib_neonver], - AC_TRY_RUN([#include <ne_utils.h> -int main(int argc, char **argv) { -return ne_version_match($neon_require_major, $neon_require_minor); -}], ne_cv_lib_neonver=yes, ne_cv_lib_neonver=no)) + [AC_RUN_IFELSE( + [AC_LANG_PROGRAM([[#include <ne_utils.h>]], + [[return ne_version_match($neon_require_major, $neon_require_minor);]])], + [ne_cv_lib_neonver=yes], [ne_cv_lib_neonver=no], [ne_cv_lib_neonver=no])]) fi ne_goodver=$ne_cv_lib_neonver LIBS=$ne_save_LIBS @@ -195,17 +197,38 @@ else $2 fi]) -dnl NEON_CHECK_SUPPORT(feature, var) +dnl NEON_CHECK_SUPPORT(feature, var, name) AC_DEFUN([NEON_CHECK_SUPPORT], [ if $NEON_CONFIG --support $1 >/dev/null; then - neon_$1_message="supported by neon" - $2=yes + NE_ENABLE_SUPPORT($2, [$3 is supported by neon]) else - neon_$1_message="not supported by neon" - $2=no + NE_DISABLE_SUPPORT($2, [$3 is not supported by neon]) fi ]) +dnl enable support for feature $1 with define $2, message $2 +AC_DEFUN([NE_ENABLE_SUPPORT], [ +NE_FLAG_$1=yes +AC_SUBST(NE_FLAG_$1) +AC_DEFINE([NE_HAVE_]$1, 1, [Defined if $1 is supported]) +m4_if([$2], [], + [ne_$1_message="support enabled" + AC_MSG_NOTICE([$1 support is enabled])], + [ne_$1_message="$2" + AC_MSG_NOTICE([$2])]) +]) + +dnl Disable support for feature $1 with define $1, message $3 +AC_DEFUN([NE_DISABLE_SUPPORT], [ +NE_FLAG_$1=no +AC_SUBST(NE_FLAG_$1) +m4_if([$2], [], + [ne_$1_message="not supported" + AC_MSG_NOTICE([$1 support is not enabled])], + [ne_$1_message="$2" + AC_MSG_NOTICE([$2])]) +]) + AC_DEFUN([NEON_USE_EXTERNAL], [ # Configure to use an external neon, given a neon-config script # found at $NEON_CONFIG. @@ -215,8 +238,12 @@ NEON_CHECK_VERSION([ NEON_LIBS="$NEON_LIBS `$NEON_CONFIG --libs`" neon_library_message="library in ${neon_prefix} (`$NEON_CONFIG --version`)" neon_xml_parser_message="using whatever neon uses" - NEON_CHECK_SUPPORT([ssl], [NEON_SUPPORTS_SSL]) - NEON_CHECK_SUPPORT([zlib], [NEON_SUPPORTS_ZLIB]) + NEON_CHECK_SUPPORT([ssl], [SSL], [SSL]) + NEON_CHECK_SUPPORT([zlib], [ZLIB], [zlib]) + NEON_CHECK_SUPPORT([idna], [IDNA], [IDNA]) + NEON_CHECK_SUPPORT([ipv6], [IPV6], [IPv6]) + NEON_CHECK_SUPPORT([lfs], [LFS], [LFS]) + NEON_CHECK_SUPPORT([socks], [SOCKS], [SOCKSv5]) neon_got_library=yes ], [neon_got_library=no]) ]) @@ -311,21 +338,27 @@ dnl give an error and fail configure. AC_DEFUN([NE_SEARCH_LIBS], [ AC_CACHE_CHECK([for library containing $1], [ne_cv_libsfor_$1], [ -AC_TRY_LINK_FUNC($1, [ne_cv_libsfor_$1="none needed"], [ +AC_LINK_IFELSE( + [AC_LANG_PROGRAM([], [[$1();]])], + [ne_cv_libsfor_$1="none needed"], [ ne_sl_save_LIBS=$LIBS ne_cv_libsfor_$1="not found" for lib in $2; do LIBS="$ne_sl_save_LIBS -l$lib $NEON_LIBS" - AC_TRY_LINK_FUNC($1, [ne_cv_libsfor_$1="-l$lib"; break]) + AC_LINK_IFELSE([AC_LANG_PROGRAM([], [[$1();]])], + [ne_cv_libsfor_$1="-l$lib"; break]) m4_if($3, [], [], dnl If $3 is specified, then... [LIBS="$ne_sl_save_LIBS -l$lib $3 $NEON_LIBS" - AC_TRY_LINK_FUNC($1, [ne_cv_libsfor_$1="-l$lib $3"; break])]) + AC_LINK_IFELSE([AC_LANG_PROGRAM([], [[$1();]])], + [ne_cv_libsfor_$1="-l$lib $3"; break])]) done LIBS=$ne_sl_save_LIBS])]) if test "$ne_cv_libsfor_$1" = "not found"; then - m4_if($4, [], [AC_MSG_ERROR([could not find library containing $1])], [$4]) -elif test "$ne_cv_libsfor_$1" != "none needed"; then + m4_if([$4], [], [AC_MSG_ERROR([could not find library containing $1])], [$4]) +elif test "$ne_cv_libsfor_$1" = "none needed"; then + m4_if([$5], [], [:], [$5]) +else NEON_LIBS="$ne_cv_libsfor_$1 $NEON_LIBS" $5 fi]) @@ -333,23 +366,18 @@ fi]) dnl Check for presence and suitability of zlib library AC_DEFUN([NEON_ZLIB], [ -AC_ARG_WITH(zlib, AC_HELP_STRING([--without-zlib], [disable zlib support]), +AC_ARG_WITH(zlib, AS_HELP_STRING([--without-zlib], [disable zlib support]), ne_use_zlib=$withval, ne_use_zlib=yes) -NEON_SUPPORTS_ZLIB=no -AC_SUBST(NEON_SUPPORTS_ZLIB) - if test "$ne_use_zlib" = "yes"; then AC_CHECK_HEADER(zlib.h, [ AC_CHECK_LIB(z, inflate, [ NEON_LIBS="$NEON_LIBS -lz" - NEON_CFLAGS="$NEON_CFLAGS -DNEON_ZLIB" - NEON_SUPPORTS_ZLIB=yes - neon_zlib_message="found in -lz" - ], [neon_zlib_message="zlib not found"]) - ], [neon_zlib_message="zlib not found"]) + NE_ENABLE_SUPPORT(ZLIB, [zlib support enabled, using -lz]) + ], [NE_DISABLE_SUPPORT(ZLIB, [zlib library not found])]) + ], [NE_DISABLE_SUPPORT(ZLIB, [zlib header not found])]) else - neon_zlib_message="zlib disabled" + NE_DISABLE_SUPPORT(ZLIB, [zlib not enabled]) fi ]) @@ -372,8 +400,6 @@ AC_DEFUN([NEON_COMMON_CHECKS], [ # is used. AC_REQUIRE([AC_PROG_CC]) -AC_REQUIRE([AC_PROG_CC_STDC]) -AC_REQUIRE([AC_LANG_C]) AC_REQUIRE([AC_ISC_POSIX]) AC_REQUIRE([AC_C_INLINE]) AC_REQUIRE([AC_C_CONST]) @@ -403,8 +429,8 @@ if test "$GCC" = "yes"; then # See whether a simple test program will compile without errors. ne_save_CPPFLAGS=$CPPFLAGS CPPFLAGS="$CPPFLAGS -Wformat -Werror" - AC_TRY_COMPILE([#include <sys/types.h> - #include <stdio.h>], [int i = 42; printf("%d", i);], + AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[#include <sys/types.h> +#include <stdio.h>]], [[int i = 42; printf("%d", i);]])], [ne_cv_cc_werror=yes], [ne_cv_cc_werror=no]) CPPFLAGS=$ne_save_CPPFLAGS]) ne_fmt_trycompile=$ne_cv_cc_werror @@ -413,6 +439,39 @@ else fi ]) +dnl Check for LFS support +AC_DEFUN([NE_LARGEFILE], [ +dnl Need the size of off_t +AC_REQUIRE([NEON_COMMON_CHECKS]) + +if test -z "$ac_cv_sizeof_off_t"; then + NE_DISABLE_SUPPORT(LFS, [LFS support omitted: off_t size unknown!]) +elif test $ac_cv_sizeof_off_t != 4; then + NE_DISABLE_SUPPORT(LFS, [LFS support unnecessary, off_t is not 32-bit]) + AC_CHECK_FUNCS([strtoll strtoq], [break]) +elif test -z "$ac_cv_sizeof_long_long"; then + NE_DISABLE_SUPPORT(LFS, [LFS support omitted: long long size unknown]) +elif test $ac_cv_sizeof_long_long != 8; then + NE_DISABLE_SUPPORT(LFS, [LFS support omitted: long long not 64-bit]) +else + ne_save_CPPFLAGS=$CPPFLAGS + CPPFLAGS="$CPPFLAGS -D_LARGEFILE64_SOURCE" + AC_CHECK_TYPE(off64_t, [ + NEON_FORMAT(off64_t) + ne_lfsok=no + AC_CHECK_FUNCS([strtoll strtoq], [ne_lfsok=yes; break]) + AC_CHECK_FUNCS([lseek64 fstat64], [], [ne_lfsok=no; break]) + if test x$ne_lfsok = xyes; then + NE_ENABLE_SUPPORT(LFS, [LFS (large file) support enabled]) + NEON_CFLAGS="$NEON_CFLAGS -D_LARGEFILE64_SOURCE -DNE_LFS" + ne_save_CPPFLAGS="$CPPFLAGS -DNE_LFS" + else + NE_DISABLE_SUPPORT(LFS, + [LFS support omitted: 64-bit support functions not found]) + fi], [NE_DISABLE_SUPPORT(LFS, [LFS support omitted: off64_t type not found])]) + CPPFLAGS=$ne_save_CPPFLAGS +fi]) + dnl NEON_FORMAT(TYPE[, HEADERS[, [SPECIFIER]]) dnl dnl This macro finds out which modifier is needed to create a @@ -436,11 +495,11 @@ if test $ne_fmt_trycompile = yes; then oflags="$CPPFLAGS" # Consider format string mismatches as errors CPPFLAGS="$CPPFLAGS -Wformat -Werror" - dnl obscured for m4 quoting: "for str in d ld qd; do" - for str in ne_spec l]ne_spec[ q]ne_spec[; do - AC_TRY_COMPILE([#include <sys/types.h> + dnl obscured for m4 quoting: "for str in d ld lld; do" + for str in ne_spec l]ne_spec[ ll]ne_spec[; do + AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[#include <sys/types.h> $2 -#include <stdio.h>], [$1 i = 1; printf("%$str", i);], +#include <stdio.h>]], [[$1 i = 1; printf("%$str", i);]])], [ne_cv_fmt_$1=$str; break]) done CPPFLAGS=$oflags @@ -481,47 +540,74 @@ AC_REQUIRE([AC_C_BIGENDIAN]) dnl Is strerror_r present; if so, which variant AC_REQUIRE([AC_FUNC_STRERROR_R]) -AC_CHECK_HEADERS([strings.h sys/time.h limits.h sys/select.h arpa/inet.h \ - signal.h sys/socket.h netinet/in.h netinet/tcp.h netdb.h]) +AC_CHECK_HEADERS([sys/time.h limits.h sys/select.h arpa/inet.h \ + signal.h sys/socket.h netinet/in.h netinet/tcp.h netdb.h sys/poll.h],,, +[AC_INCLUDES_DEFAULT +/* netinet/tcp.h requires netinet/in.h on some platforms. */ +#ifdef HAVE_NETINET_IN_H +#include <netinet/in.h> +#endif]) AC_REQUIRE([NE_SNPRINTF]) +dnl Check for large file support +NE_LARGEFILE + AC_REPLACE_FUNCS(strcasecmp) -AC_CHECK_FUNCS(signal setvbuf setsockopt stpcpy) +AC_CHECK_FUNCS(signal setvbuf setsockopt stpcpy poll) + +if test "x${ac_cv_func_poll}${ac_cv_header_sys_poll_h}y" = "xyesyesy"; then + AC_DEFINE([NE_USE_POLL], 1, [Define if poll() should be used]) +fi if test "$ac_cv_func_stpcpy" = "yes"; then AC_CHECK_DECLS(stpcpy) fi # Modern AIXes with the "Linux-like" libc have an undeclared stpcpy -AH_BOTTOM([#if defined(HAVE_STPCPY) && !HAVE_DECL_STPCPY && !defined(stpcpy) +AH_BOTTOM([#if defined(HAVE_STPCPY) && defined(HAVE_DECL_STPCPY) && !HAVE_DECL_STPCPY && !defined(stpcpy) char *stpcpy(char *, const char *); #endif]) +# Enable getaddrinfo support if it, gai_strerror and inet_ntop are +# all available. Solaris etc hide things in -lsocket, use that too. + # Unixware 7 can only link gethostbyname with -lnsl -lsocket # Pick up -lsocket first, then the gethostbyname check will work. # QNX has gethostbyname in -lsocket. BeOS only has it in -lbind. -NE_SEARCH_LIBS(socket, socket inet) +# CygWin/Winsock2 has it in -lws2_32, allegedly. +NE_SEARCH_LIBS(socket, socket inet ws2_32) NE_SEARCH_LIBS(gethostbyname, socket nsl bind) -# Enable getaddrinfo() support only if all the necessary functions -# are found. -ne_enable_gai=yes -NE_CHECK_FUNCS(getaddrinfo gai_strerror inet_ntop,,[ne_enable_gai=no; break]) +NE_SEARCH_LIBS(getaddrinfo, nsl,, + [ne_enable_gai=no], + [# HP-UX boxes commonly get into a state where getaddrinfo is present + # but borked: http://marc.theaimsgroup.com/?l=apr-dev&m=107730955207120&w=2 + case x`uname -sr 2>/dev/null`y in + xHP-UX*11.[[01]]*y) + AC_MSG_NOTICE([getaddrinfo support disabled on HP-UX 11.0x/11.1x]) ;; + *) + ne_enable_gai=yes + NE_CHECK_FUNCS(gai_strerror inet_ntop,,[ne_enable_gai=no; break]) ;; + esac +]) + if test $ne_enable_gai = yes; then + NE_ENABLE_SUPPORT(IPV6, [IPv6 support is enabled]) AC_DEFINE(USE_GETADDRINFO, 1, [Define if getaddrinfo() should be used]) AC_CACHE_CHECK([for working AI_ADDRCONFIG], [ne_cv_gai_addrconfig], [ AC_RUN_IFELSE([AC_LANG_PROGRAM([#include <netdb.h>], [struct addrinfo hints = {0}, *result; hints.ai_flags = AI_ADDRCONFIG; if (getaddrinfo("localhost", NULL, &hints, &result) != 0) return 1;])], - ne_cv_gai_addrconfig=yes, ne_cv_gai_addrconfig=no)]) + ne_cv_gai_addrconfig=yes, ne_cv_gai_addrconfig=no, ne_cv_gai_addrconfig=no)]) if test $ne_cv_gai_addrconfig = yes; then AC_DEFINE(USE_GAI_ADDRCONFIG, 1, [Define if getaddrinfo supports AI_ADDRCONFIG]) fi else # Checks for non-getaddrinfo() based resolver interfaces. + NE_SEARCH_LIBS(gethostbyname, nsl bind) NE_SEARCH_LIBS(hstrerror, resolv,,[:]) NE_CHECK_FUNCS(hstrerror) # Older Unixes don't declare h_errno. @@ -529,16 +615,16 @@ else #include <netdb.h>]) fi -AC_CHECK_MEMBERS(struct tm.tm_gmtoff,, -AC_MSG_WARN([no timezone handling in date parsing on this platform]), -[#include <time.h>]) +AC_CHECK_MEMBERS([struct tm.tm_gmtoff, struct tm.__tm_gmtoff],,, + [#include <time.h>]) -ifdef([neon_no_zlib], [ - neon_zlib_message="zlib disabled" - NEON_SUPPORTS_ZLIB=no -], [ - NEON_ZLIB() -]) +if test $ac_cv_member_struct_tm_tm_gmtoff$ac_cv_member_struct_tm___tm_gmtoff = nono; then + AC_MSG_WARN([no timezone handling in date parsing on this platform]) +fi + +ifdef([neon_no_zlib], + [NE_DISABLE_SUPPORT(ZLIB, [zlib not supported])], + [NEON_ZLIB()]) # Conditionally enable ACL support AC_MSG_CHECKING([whether to enable ACL support in neon]) @@ -551,6 +637,7 @@ fi NEON_SSL() NEON_SOCKS() +NEON_LIBIDN() NEON_GSSAPI() AC_SUBST(NEON_CFLAGS) @@ -601,27 +688,17 @@ ne="$NEON_EXTRAOBJS" NEON_EXTRAOBJS= for o in $ne; do NEON_EXTRAOBJS="$NEON_EXTRAOBJS $o.$NEON_OBJEXT" -done - -AC_MSG_CHECKING(whether to enable WebDAV support in neon) +done -dnl Did they want DAV support? +# Was DAV support explicitly turned off? if test "x$neon_no_webdav" = "xyes"; then # No WebDAV support - AC_MSG_RESULT(no) NEONOBJS="$NEONOBJS \$(NEON_BASEOBJS)" - NEON_CFLAGS="$NEON_CFLAGS -DNEON_NODAV" - NEON_SUPPORTS_DAV=no - AC_DEFINE(NEON_NODAV, 1, [Enable if built without WebDAV support]) + NE_DISABLE_SUPPORT(DAV, [WebDAV support is not enabled]) else # WebDAV support - NEON_SUPPORTS_DAV=yes NEONOBJS="$NEONOBJS \$(NEON_DAVOBJS)" - # Turn on DAV locking please then. - AC_DEFINE(USE_DAV_LOCKS, 1, [Support WebDAV locking through the library]) - - AC_MSG_RESULT(yes) - + NE_ENABLE_SUPPORT(DAV, [WebDAV support is enabled]) fi AC_SUBST(NEON_TARGET) @@ -629,7 +706,6 @@ AC_SUBST(NEON_OBJEXT) AC_SUBST(NEONOBJS) AC_SUBST(NEON_EXTRAOBJS) AC_SUBST(NEON_LINK_FLAGS) -AC_SUBST(NEON_SUPPORTS_DAV) ]) @@ -677,16 +753,16 @@ AC_CHECK_FUNCS(snprintf vsnprintf,,[ AC_MSG_ERROR([trio installation problem? libtrio found but not trio.h])) AC_MSG_NOTICE(using trio printf replacement library) NEON_LIBS="$NEON_LIBS -ltrio -lm" - NEON_CFLAGS="$NEON_CFLAGS -DNEON_TRIO"], + AC_DEFINE(HAVE_TRIO, 1, [Use trio printf replacement library])], [AC_MSG_NOTICE([no vsnprintf/snprintf detected in C library]) AC_MSG_ERROR([Install the trio library from http://daniel.haxx.se/trio/])]) LIBS=$ne_save_LIBS break ])]) -dnl Usage: NE_CHECK_SSLVER(variable, version-string, version-hex) +dnl Usage: NE_CHECK_OPENSSLVER(variable, version-string, version-hex) dnl Define 'variable' to 'yes' if OpenSSL version is >= version-hex -AC_DEFUN([NE_CHECK_SSLVER], [ +AC_DEFUN([NE_CHECK_OPENSSLVER], [ AC_CACHE_CHECK([OpenSSL version is >= $2], $1, [ AC_EGREP_CPP(good, [#include <openssl/opensslv.h> #if OPENSSL_VERSION_NUMBER >= $3 @@ -719,17 +795,22 @@ else fi fi]) -dnl Check for OpenSSL +dnl Check for an SSL library (GNU TLS or OpenSSL) AC_DEFUN([NEON_SSL], [ -AC_ARG_WITH(ssl, [AC_HELP_STRING([--with-ssl], [enable OpenSSL support])]) +AC_ARG_WITH(ssl, + AS_HELP_STRING([--with-ssl=openssl|gnutls], + [enable SSL support (default OpenSSL)])) AC_ARG_WITH(egd, [[ --with-egd[=PATH] enable EGD support [using EGD socket at PATH]]]) case $with_ssl in -yes) - +/*) + AC_MSG_NOTICE([to use SSL libraries in non-standard locations, try --with-ssl --with-libs=$with_ssl]) + AC_MSG_ERROR([--with-ssl does not take a path argument]) + ;; +yes|openssl) NE_PKG_CONFIG(NE_SSL, openssl, [AC_MSG_NOTICE(using SSL library configuration from pkg-config) CPPFLAGS="$CPPFLAGS ${NE_SSL_CFLAGS}" @@ -742,17 +823,17 @@ yes) [AC_MSG_ERROR([OpenSSL headers not found, cannot enable SSL support])]) # Enable EGD support if using 0.9.7 or newer - NE_CHECK_SSLVER(ne_cv_lib_ssl097, 0.9.7, 0x00907000L) + NE_CHECK_OPENSSLVER(ne_cv_lib_ssl097, 0.9.7, 0x00907000L) if test "$ne_cv_lib_ssl097" = "yes"; then AC_MSG_NOTICE([OpenSSL >= 0.9.7; EGD support not needed in neon]) - neon_ssl_message="OpenSSL (0.9.7 or later)" + NE_ENABLE_SUPPORT(SSL, [SSL support enabled, using OpenSSL (0.9.7 or later)]) else # Fail if OpenSSL is older than 0.9.6 - NE_CHECK_SSLVER(ne_cv_lib_ssl096, 0.9.6, 0x00906000L) + NE_CHECK_OPENSSLVER(ne_cv_lib_ssl096, 0.9.6, 0x00906000L) if test "$ne_cv_lib_ssl096" != "yes"; then AC_MSG_ERROR([OpenSSL 0.9.6 or later is required]) fi - neon_ssl_message="OpenSSL (0.9.6 or later)" + NE_ENABLE_SUPPORT(SSL, [SSL support enabled, using OpenSSL (0.9.6 or later)]) case "$with_egd" in yes|no) ne_cv_lib_sslegd=$with_egd ;; @@ -774,22 +855,63 @@ yes) fi fi - NEON_SUPPORTS_SSL=yes - NEON_CFLAGS="$NEON_CFLAGS -DNEON_SSL" + AC_DEFINE([HAVE_OPENSSL], 1, [Define if OpenSSL support is enabled]) NEON_EXTRAOBJS="$NEON_EXTRAOBJS ne_openssl" ;; +gnutls) + AC_PATH_PROG(GNUTLS_CONFIG, libgnutls-config, no) + + if test "$GNUTLS_CONFIG" = "no"; then + AC_MSG_ERROR([could not find libgnutls-config in \$PATH]) + fi + + ne_gnutls_ver=`$GNUTLS_CONFIG --version` + case $ne_gnutls_ver in + 1.*) ;; + *) AC_MSG_ERROR([GNU TLS major version "$ne_gnutls_ver" not supported]) ;; + esac + + CPPFLAGS="$CPPFLAGS `$GNUTLS_CONFIG --cflags`" + + AC_CHECK_HEADER([gnutls/gnutls.h],, + [AC_MSG_ERROR([could not find gnutls/gnutls.h in include path])]) + + NE_ENABLE_SUPPORT(SSL, [SSL support enabled, using GnuTLS $ne_gnutls_ver]) + NEON_EXTRAOBJS="$NEON_EXTRAOBJS ne_gnutls" + NEON_LIBS="$NEON_LIBS `$GNUTLS_CONFIG --libs`" + AC_DEFINE([HAVE_GNUTLS], 1, [Define if GnuTLS support is enabled]) + ;; *) # Default to off; only create crypto-enabled binaries if requested. - neon_ssl_message="No SSL support" - NEON_SUPPORTS_SSL=no + NE_DISABLE_SUPPORT(SSL, [SSL support is not enabled]) NEON_EXTRAOBJS="$NEON_EXTRAOBJS ne_stubssl" ;; esac AC_SUBST(NEON_SUPPORTS_SSL) ]) +dnl Check for GNU libidn +AC_DEFUN([NEON_LIBIDN], [ +AC_ARG_WITH(libidn, AS_HELP_STRING(--without-libidn, disable IDNA support)) +if test "$with_libidn" != "no"; then + ne_use_idna=no + AC_CHECK_HEADER(idna.h, + [NE_SEARCH_LIBS(idna_to_ascii_8z,idn,,,[ne_use_idna=yes])]) + if test $ne_use_idna = yes; then + NE_ENABLE_SUPPORT(IDNA, [IDNA support enabled using GNU libidn]) + else + NE_DISABLE_SUPPORT(IDNA, + [IDNA support not enabled; GNU libidn >=0.2.0 required]) + fi +fi]) + dnl Check for Kerberos installation AC_DEFUN([NEON_GSSAPI], [ -AC_PATH_PROG([KRB5_CONFIG], krb5-config, none, $PATH:/usr/kerberos/bin) +AC_ARG_WITH(gssapi, AS_HELP_STRING(--without-gssapi, disable GSSAPI support)) +if test "$with_gssapi" != "no"; then + AC_PATH_PROG([KRB5_CONFIG], krb5-config, none, $PATH:/usr/kerberos/bin) +else + KRB5_CONFIG=none +fi if test "x$KRB5_CONFIG" != "xnone"; then ne_save_CPPFLAGS=$CPPFLAGS ne_save_LIBS=$NEON_LIBS @@ -803,7 +925,7 @@ if test "x$KRB5_CONFIG" != "xnone"; then AC_MSG_NOTICE([GSSAPI authentication support enabled]) AC_DEFINE(HAVE_GSSAPI, 1, [Define if GSSAPI support is enabled]) AC_CHECK_HEADERS(gssapi/gssapi_generic.h) - # MIT Kerberos lacks GSS_C_NT_HOSTBASED_SERVICE + # Older versions of MIT Kerberos lack GSS_C_NT_HOSTBASED_SERVICE AC_CHECK_DECL([GSS_C_NT_HOSTBASED_SERVICE],, [AC_DEFINE([GSS_C_NT_HOSTBASED_SERVICE], gss_nt_service_name, [Define if GSS_C_NT_HOSTBASED_SERVICE is not defined otherwise])], @@ -825,12 +947,12 @@ AC_DEFUN([NEON_WARNINGS],[ AC_REQUIRE([AC_PROG_CC]) dnl so that $GCC is set AC_ARG_ENABLE(warnings, -AC_HELP_STRING(--enable-warnings, [enable compiler warnings])) +AS_HELP_STRING(--enable-warnings, [enable compiler warnings])) if test "$enable_warnings" = "yes"; then case $GCC:`uname` in yes:*) - CFLAGS="$CFLAGS -Wall -ansi-pedantic -Wmissing-declarations -Winline -Wshadow -Wreturn-type -Wsign-compare -Wundef -Wpointer-arith -Wcast-align -Wbad-function-cast -Wimplicit-prototypes -Wformat-security" + CFLAGS="$CFLAGS -Wall -Wmissing-declarations -Wshadow -Wreturn-type -Wsign-compare -Wundef -Wpointer-arith -Wcast-align -Wbad-function-cast -Wformat-security" if test -z "$with_ssl" -o "$with_ssl" = "no"; then # OpenSSL headers fail strict prototypes checks CFLAGS="$CFLAGS -Wstrict-prototypes" @@ -852,7 +974,7 @@ dnl AC_DEFUN([NEON_DEBUG], [ AC_ARG_ENABLE(debug, -AC_HELP_STRING(--disable-debug,[disable runtime debugging messages])) +AS_HELP_STRING(--disable-debug,[disable runtime debugging messages])) # default is to enable debugging case $enable_debug in @@ -867,21 +989,21 @@ esac]) dnl Macro to optionally enable socks support AC_DEFUN([NEON_SOCKS], [ -AC_ARG_WITH([socks], AC_HELP_STRING([--with-socks],[use SOCKSv5 library])) +AC_ARG_WITH([socks], AS_HELP_STRING([--with-socks],[use SOCKSv5 library])) if test "$with_socks" = "yes"; then ne_save_LIBS=$LIBS AC_CHECK_HEADERS(socks.h, - [AC_CHECK_LIB(socks5, connect, - [AC_MSG_NOTICE([SOCKSv5 support enabled])], + [AC_CHECK_LIB(socks5, connect, [:], [AC_MSG_ERROR([could not find libsocks5 for SOCKS support])])], [AC_MSG_ERROR([could not find socks.h for SOCKS support])]) - CFLAGS="$CFLAGS -DNEON_SOCKS" + NE_ENABLE_SUPPORT(SOCKS, [SOCKSv5 support is enabled]) NEON_LIBS="$NEON_LIBS -lsocks5" LIBS=$ne_save_LIBS - +else + NE_DISABLE_SUPPORT(SOCKS, [SOCKSv5 support is not enabled]) fi]) AC_DEFUN([NEON_WITH_LIBS], [ diff --git a/neon/neon-config.in b/neon/neon-config.in index 27b74d1be..8db113cc5 100644 --- a/neon/neon-config.in +++ b/neon/neon-config.in @@ -1,6 +1,6 @@ #! /bin/sh # Originally from libxml, Copyright (C) Daniel Veillard -# Modifications for neon Copyright (C) 2000-2002 Joe Orton. +# Modifications for neon Copyright (C) 2000-2004 Joe Orton. prefix=@prefix@ exec_prefix=@exec_prefix@ @@ -21,7 +21,7 @@ Known values for OPTION are: --help display this help and exit --version output version information --support FEATURE exit with success if feature is supported - Known features: dav [@NEON_SUPPORTS_DAV@], ssl [@NEON_SUPPORTS_SSL@], zlib [@NEON_SUPPORTS_ZLIB@] + Known features: dav [@NE_FLAG_DAV@], ssl [@NE_FLAG_SSL@], zlib [@NE_FLAG_ZLIB@], idna [@NE_FLAG_IDNA@], ipv6 [@NE_FLAG_IPV6@], lfs [@NE_FLAG_LFS@] EOF @@ -87,16 +87,18 @@ while test $# -gt 0; do shift case "$1" in - ssl|SSL) support @NEON_SUPPORTS_SSL@ ;; - zlib|ZLIB) support @NEON_SUPPORTS_ZLIB@ ;; - dav|DAV) support @NEON_SUPPORTS_DAV@ ;; + ssl|SSL) support @NE_FLAG_SSL@ ;; + zlib|ZLIB) support @NE_FLAG_ZLIB@ ;; + ipv6|IPV6) support @NE_FLAG_IPV6@ ;; + dav|DAV) support @NE_FLAG_DAV@ ;; + idna|IDNA) support @NE_FLAG_IDNA@ ;; + lfs|LFS) support @NE_FLAG_LFS@ ;; *) support no ;; esac ;; *) - usage - exit 1 + usage 1 1>&2 ;; esac shift diff --git a/neon/neon.mak b/neon/neon.mak index de7f10a32..bd432f88d 100644 --- a/neon/neon.mak +++ b/neon/neon.mak @@ -63,6 +63,11 @@ ZLIB_LIBS = "$(ZLIB_SRC)\zlibdll.lib" !ENDIF !ENDIF +######## +# Support for IPv6 +!IF "$(ENABLE_IPV6)" == "yes" +IPV6_FLAGS = /D USE_GETADDRINFO +!ENDIF !IF "$(DEBUG_BUILD)" == "" INTDIR = Release @@ -78,7 +83,7 @@ TARGET = .\libneonD.lib WIN32_DEFS = /D WIN32_LEAN_AND_MEAN /D NOUSER /D NOGDI /D NONLS /D NOCRYPT CPP=cl.exe -CPP_PROJ = /c /nologo $(CFLAGS) $(WIN32_DEFS) $(EXPAT_FLAGS) $(OPENSSL_FLAGS) $(ZLIB_FLAGS) /D "HAVE_CONFIG_H" /Fo"$(INTDIR)\\" /Fd"$(INTDIR)\\" +CPP_PROJ = /c /nologo $(CFLAGS) $(WIN32_DEFS) $(EXPAT_FLAGS) $(OPENSSL_FLAGS) $(ZLIB_FLAGS) $(IPV6_FLAGS) /D "HAVE_CONFIG_H" /Fo"$(INTDIR)\\" /Fd"$(INTDIR)\\" LIB32=link.exe -lib LIB32_FLAGS=/nologo /out:"$(TARGET)" diff --git a/neon/src/ChangeLog b/neon/src/ChangeLog index 4b330c2f7..7859493c7 100644 --- a/neon/src/ChangeLog +++ b/neon/src/ChangeLog @@ -1,3 +1,94 @@ +Sun Sep 12 19:21:30 2004 Joe Orton <joe@manyfish.co.uk> + + * ne_locks.c (ne_lock_refresh): Fix to pass correct userdata to + callbacks, and do call lk_cdata. + +Sun Sep 12 18:53:15 2004 Joe Orton <joe@manyfish.co.uk> + + * Makefile.in (libneon.a): Remove the archive first, avoiding + strange problems when build $(OBJECTS) change. + +Sun Sep 12 18:40:50 2004 Joe Orton <joe@manyfish.co.uk> + + * ne_utils.h: Add NE_FEATURE_SOCKS. + + * ne_utils.c (ne_has_support): Add NE_FEATURE_SOCKS. + (version_string): Use NE_HAVE_SOCKS, add NE_HAVE_IDNA. + + * ne_socket.c (ne_sock_init): Use NE_HAVE_SOCKS. + +Sun Sep 12 17:29:54 2004 Joe Orton <joe@manyfish.co.uk> + + * ne_utils.c (version_string): Declare as array rather than + pointer; include "IPv6" component as necessary + +Sun Sep 12 15:51:38 2004 Joe Orton <joe@manyfish.co.uk> + + * ne_socket.c (ne_iaddr_typeof): New function. + +Sun Sep 12 12:00:10 2004 Joe Orton <joe@manyfish.co.uk> + + * ne_defs.h (ne_attribute_malloc): New macro. + + * ne_alloc.h: Use it to avoid warnings with older GCCs. + +Wed Aug 25 21:03:40 2004 Joe Orton <joe@manyfish.co.uk> + + * ne_string.h (split_string, split_string_c, pair_string, + split_string_free, pair_string_free): Remove obsolete interfaces. + +Wed Aug 25 21:01:03 2004 Joe Orton <joe@manyfish.co.uk> + + * ne_cookies.c, ne_cookies.h: Drop cookies support: used old spec + revision and wasn't very complete anyway. + +Wed Aug 25 20:40:26 2004 Joe Orton <joe@manyfish.co.uk> + + * ne_socket.c: Remove ne_read, ne_write macros and just use recv + and send; remove unused SOCK_ERR macro. + +Wed Aug 25 20:27:43 2004 Joe Orton <joe@manyfish.co.uk> + + * ne_xml.c (declare_nspaces): Drop rejection of names including a + colon to prevent breaking SVN deployments. + +Wed Aug 25 19:45:20 2004 Joe Orton <joe@manyfish.co.uk> + + * ne_socket.c (readable_raw): Use poll where available. + (ne_sock_connect): Fail if not using poll and fd returned by + socket() is greater than FD_SETSIZE. + +Wed Aug 25 18:40:28 2004 Joe Orton <joe@manyfish.co.uk> + + * ne_xml.h (ne_xml_parse): Clarify that a len=0 call is required + to signify end-of-document. + +Wed Aug 25 18:37:13 2004 Joe Orton <joe@manyfish.co.uk> + + * ne_request.c (resolve_first, resolve_next): New functions. + (lookup_host): Use them to allow user-forced addresses. + + * ne_session.c (ne_set_addrlist): New function. + + * ne_private.h (struct ne_session_s): Add addrlist, numaddrs, + curaddr fields. + +Wed Aug 25 18:25:31 2004 Joe Orton <joe@manyfish.co.uk> + + * ne_xml.c (struct ne_xml_parser_s): Add bom_pos field. + (ne_xml_parse): Skip over the UTF-8 Byte Order Mark since + the XML parsers do not support it yet. + +Wed Jul 7 16:07:44 2004 Joe Orton <joe@manyfish.co.uk> + + * ne_request.c (do_connect): Fix ne_conn_connected status call + (Shameek Basu). + +Mon Jul 5 18:40:35 2004 Joe Orton <joe@manyfish.co.uk> + + * ne_basic.c (ne_content_type_handler): Use us-ascii as default + charset for text/xml, as per RFC3280. + Mon Jul 5 10:56:19 2004 Joe Orton <joe@manyfish.co.uk> * ne_compress.c (struct ne_decompress_s): Add acceptor field. @@ -12,20 +103,16 @@ Mon Jul 5 10:52:40 2004 Joe Orton <joe@manyfish.co.uk> (process_footer): Call the reader callback with size=0 to indicate end-of-response for a good checksum match. -Mon Jul 5 10:42:14 2004 Joe Orton <joe@manyfish.co.uk> - - * ne_compress.c (gz_destroy, gz_pre_send): New functions. - (ne_decompress_reader): Register pre-send and destroy hooks, - to initialize the compression context before each request attempt. - (Justin Erenkrantz). - - * ne_private.h, ne_request.c (ne_kill_pre_send): New function. - Sat Jul 3 14:33:56 2004 Joe Orton <joe@manyfish.co.uk> * ne_auth.c (auth_challenge): Fix to set got_qop in challenge correctly (Hideaki Takahashi). +Mon May 17 15:03:54 2004 Joe Orton <joe@manyfish.co.uk> + + * ne_socket.h (ne_addr_resolve): Clarify that 'flags' must + be passed as zero for forwards-compat. + Sun May 2 21:14:14 2004 Joe Orton <joe@manyfish.co.uk> Fix buffer overflow in RFC1036 date parser, CVE CAN-2004-0389. @@ -35,17 +122,19 @@ Sun May 2 21:14:14 2004 Joe Orton <joe@manyfish.co.uk> (ne_rfc1123_parse, ne_rfc1036_parse, ne_asctime_parse): Make thread-safe; remove static buffers. -Thu Mar 11 23:38:01 2004 Joe Orton <joe@manyfish.co.uk> - - * ne_openssl.c (provide_client_cert): Avoid malloc(0) when server - sends no CA names in CertificateRequest. - (ne_ssl_cert_write): Be paranoid and clear the OpenSSL error stack - on write failures. - Sun May 2 16:59:39 2004 Joe Orton <joe@manyfish.co.uk> * ne_dates.c [RFC1123_TEST] (main): Remove embedded test cases. +Sun May 2 13:18:29 2004 Joe Orton <joe@manyfish.co.uk> + + * Makefile.in (LINK): Add -no-undefined. + +Fri Apr 16 22:53:59 2004 Joe Orton <joe@manyfish.co.uk> + + * ne_xml.c (declare_nspaces, expand_qname): Don't try to include + document context in error strings. + Fri Apr 16 11:44:34 2004 Joe Orton <joe@manyfish.co.uk> * Makefile.in (LIBS): Include NEON_LTLIBS. @@ -77,11 +166,83 @@ Thu Apr 8 13:40:03 2004 Joe Orton <joe@manyfish.co.uk> * ne_props.h: Don't use an anonymous enum for the proppatch operation type, as some C++ compilers don't like it. +Wed Apr 7 13:50:10 2004 Joe Orton <joe@manyfish.co.uk> + + * ne_request.c (add_fixed_headers): Don't both sending Keep-Alive + header if persistent connections are disabled. + +Wed Apr 7 13:47:46 2004 Joe Orton <joe@manyfish.co.uk> + + * ne_auth.c (auth_challenge): Allow Negotiate challenges from + a proxy. + +Wed Apr 7 13:36:55 2004 Joe Orton <joe@manyfish.co.uk> + + * ne_auth.c (clean_session): Remove redundant assignment of + GSS_C_NO_CONTEXT; gss_delete_sec_context already does this. + +Wed Apr 7 13:33:10 2004 Joe Orton <joe@manyfish.co.uk> + + * ne_auth.c (get_gss_name): Handle failure case internally. + (auth_register): Updated accordingly. + +Wed Apr 7 13:15:57 2004 Joe Orton <joe@manyfish.co.uk> + + * ne_request.c: Use strtoq to print off_t's where necessary. + +Wed Apr 7 11:14:24 2004 Joe Orton <joe@manyfish.co.uk> + + * ne_auth.c (get_gss_name): Take a hostname string. + (auth_register): Pass proxy or server hostname to get_gss_name as + appropriate. + +Wed Apr 7 11:09:50 2004 Joe Orton <joe@manyfish.co.uk> + + * ne_auth.c (continue_negotiate): If given no input token, and the + gssctx is not in the initial state, reset it. + +Mon Mar 29 17:06:49 2004 Joe Orton <joe@manyfish.co.uk> + + * ne_auth.c: Adjust to cope with GSSAPI continuation: + (struct auth_session_s): Store GSSAPI context, name and mechanism. + (get_gss_name): Take an ne_session. + (continue_negotiate): Renamed from gssapi_challenge; take input + token, handle GSS_S_CONTINUE_NEEDED return value. + (verify_digest_response): Renamed from verify_response. + (verify_negotiate_response): New function. + (auth_challenge): Cope with Negotiate responses which gratuitously + break the auth-param grammar. + (ah_post_send): Handle Negotiate responses. + (free_auth, clean_session): Free persisted GSSAPI objects. + (auth_register): Initialize GSSAPI objects. + +Sun Mar 28 03:03:17 2004 Joe Orton <joe@manyfish.co.uk> + + * ne_auth.c (get_gss_name): Don't leak token.value. + Sun Mar 28 02:59:58 2004 Joe Orton <joe@manyfish.co.uk> * ne_auth.c (get_cnonce): Only use RAND_pseudo_bytes() if the PRNG is seeded. +Sun Mar 28 02:47:20 2004 Joe Orton <joe@manyfish.co.uk> + + * ne_auth.c (gssapi_challenge, get_gss_name): Simplify. + +Sun Mar 28 02:35:48 2004 Joe Orton <joe@manyfish.co.uk> + + * ne_auth.c (request_gssapi, get_gss_name, auth_challenge): + Implement the Negotiate protocol rather than the obsoleted + GSS-Negotiate. + (make_gss_error): New function. + (gssapi_challenge): Use it for better error handling (set session + error string); fix memory leaks. Don't delegate credentials. + +Sat Mar 27 20:49:24 2004 Joe Orton <joe@manyfish.co.uk> + + * ne_auth.c (ah_post_send): Clear auth header collector buffers + after each request. + Fri Mar 26 12:16:15 2004 Joe Orton <joe@manyfish.co.uk> * ne_socket.c (init_ssl): Just initialize the SSL library; delay @@ -95,12 +256,72 @@ Fri Mar 26 12:01:38 2004 Joe Orton <joe@manyfish.co.uk> * ne_utils.c: Include zlib.h before ne_*.h to fix issues on platforms where zconf.h does "#define const". -Fri Mar 26 11:55:06 2004 Joe Orton <joe@manyfish.co.uk> +Thu Mar 11 23:38:01 2004 Joe Orton <joe@manyfish.co.uk> + + * ne_openssl.c (provide_client_cert): Avoid malloc(0) when server + sends no CA names in CertificateRequest. + (ne_ssl_cert_write): Be paranoid and clear the OpenSSL error stack + on write failures. + +Sun Mar 7 11:17:04 2004 Joe Orton <joe@manyfish.co.uk> + + * Makefile.in (CFLAGS): Don't use NEON_CFLAGS. + +Mon Feb 23 23:03:08 2004 Joe Orton <joe@manyfish.co.uk> + + * ne_string.c (ne_vsnprintf, ne_snprintf): New functions. + +Sun Feb 22 23:34:47 2004 Joe Orton <joe@manyfish.co.uk> + + * ne_private.h (struct ne_session_s): Remove expect100_works field. + + * ne_request.c (ne_set_request_expect100): New function. + (ne_begin_request): Remove req->use_expect100 manipulation. + (send_request): Handle enabling 100continue without a request + body. + + * ne_session.c (ne_set_expect100): Removed function. + +Sun Feb 22 20:17:04 2004 Joe Orton <joe@manyfish.co.uk> + + * ne_socket.c (error_ossl): Check for ERR_reason_error_string + returning NULL. + +Sun Feb 22 17:54:43 2004 Joe Orton <joe@manyfish.co.uk> + + * ne_socket.c: Don't disable getaddrinfo support here. - * ne_auth.c (get_gss_name, request_gssapi, gssapi_challenge, - auth_challenge): Implement the Negotiate auth scheme rather than - the obsolete GSS-Negotiate, and fix memory leaks. Only accept - Negotiate challenges over SSL. +Sun Feb 22 17:40:07 2004 Joe Orton <joe@manyfish.co.uk> + + * ne_utils.h (min): Remove definition to... + + * ne_uri.c (min): ...here. + +Sun Feb 22 17:31:35 2004 Joe Orton <joe@manyfish.co.uk> + + * ne_props.h: Give the 'type' enum a tag name. + +Sun Feb 22 17:27:28 2004 Joe Orton <joe@manyfish.co.uk> + + * ne_207.c (end_element): Strip whitespace from cdata. + +Sun Feb 22 16:27:58 2004 Joe Orton <joe@manyfish.co.uk> + + * ne_auth.c (struct auth_request): Make auth_hdr, auth_info_hdr + fields into ne_buffer *'s. + (ah_collect_header): New function. + (ah_create): Create ne_buffers for auth_{,info_}hdr; use + ah_collect_header rather than ne_duplicate_header to fix handling + of multiple auth challenge headers. + (ah_post_send): Adjust for char * -> ne_buffer *. + (tokenize): Recognize a challenge scheme which is terminated with + a comma (i.e. with no challange parameters). + (auth_challenge): Fix handling of unrecognized challenges. + (ah_destroy): Destroy ne_buffers. + +Sun Feb 22 15:04:46 2004 Joe Orton <joe@manyfish.co.uk> + + * ne_request.c (ne_set_request_body_provider64): New function. Sun Feb 15 13:37:03 2004 Joe Orton <joe@manyfish.co.uk> @@ -110,11 +331,112 @@ Sun Feb 15 13:37:03 2004 Joe Orton <joe@manyfish.co.uk> * ne_openssl.c (ne_ssl_readable_dname): Convert dname strings to UTF-8, or use "???". +Sat Feb 14 21:57:25 2004 Joe Orton <joe@manyfish.co.uk> + + * ne_xml.c (invalid_ncname_ch1): New macro. + (declare_nspaces): Use it, to reject some more invalid namespace + prefixes; also check for a colon anywhere in the NCName. + (expand_qname): Likewise for the element name. + +Mon Feb 9 21:38:03 2004 Joe Orton <joe@manyfish.co.uk> + + * ne_dates.c [WIN32] (GMTOFF): Use gmt_to_local_win32; + (gmt_to_local_win32): New function, from Jiang Lei. + +Mon Jan 26 14:38:05 2004 Joe Orton <joe@manyfish.co.uk> + + * ne_socket.c (ne_sock_connect_ssl): Check that OpenSSL version + matches between library at run-time and headers at compile-time. + +Sat Jan 24 17:49:27 2004 Joe Orton <joe@manyfish.co.uk> + + * ne_dates.c (HAVE_STRUCT_TM___TM_GMTOFF): Alternative GMTOFF() + macro. + Sat Jan 24 16:49:30 2004 Joe Orton <joe@manyfish.co.uk> * ne_auth.c (basic_challenge): Cast first parameter to ne_base64 to unsigned char * to fix warnings with some compilers. +Sat Jan 3 13:17:36 2004 Joe Orton <joe@manyfish.co.uk> + + * ne_request.h (ne_set_request_body_fd64): Define conditional on + NE_LFS. + + * ne_request.c (ne_set_request_body_fd64): Likewise. + +Thu Jan 1 18:01:45 2004 Joe Orton <joe@manyfish.co.uk> + + * ne_request.c: Use NE_HAVE_LFS not _LARGEFILE64_SOURCE in + conditional support for off64_t. + +Thu Jan 1 17:38:55 2004 Joe Orton <joe@manyfish.co.uk> + + * ne_request.h [_LARGEFILE64_SOURCE] (ne_set_request_body_fd64): + New function. + + * ne_request.c: Define ne_lseek, ne_off_t, ne_strtoff, + NE_OFFT_MAX, FMT_NE_OFF_T appropriately for _LARGEFILE64_SOURCE or + otherwise. + (struct ne_request_s): Use ne_off_t in place of off_t throughout. + (body_fd_send): Use ne_lseek; reset 'remain' after seeking. + (clength_hdr_handler): Use ne_off_t, ne_strtoff and NE_OFFT_MAX. + (set_body_length): Take an ne_off_t length parameter; use + FMT_NE_OFF_T to print it. + (ne_set_request_body_fd64): New function. + + * ne_utils.h (NE_FEATURE_LFS): New feature. + + * ne_utils.c (ne_has_support): Support NE_FEATURE_LFS. + +Mon Nov 24 20:13:14 2003 Joe Orton <joe@manyfish.co.uk> + + * ne_request.c (struct ne_response): Split handling for chunked vs + clength-delimited responses into a union. Use off_t for storing + whole-length-of-response values. + (read_response_block, ne_read_response_block): Update accordingly. + (ne_begin_request): Remove unnecessary variable assignments. + +Sun Nov 23 16:03:22 2003 Joe Orton <joe@manyfish.co.uk> + + * ne_request.h (ne_set_request_body_fd): Take offset and length + arguments, return void. + (ne_set_request_body_provider): Take off_t length argument. + + * ne_request.c (struct ne_request_s): Store current position + within buffer/file used as request body source. Store request + body lengths using off_t type. + (body_string_send): Adjust for renamed fields. + (body_fd_send): Seek to requested offset; don't read past + requested body length. + (set_body_length): Renamed from set_body_size. + + * ne_basic.c (ne_put): Determine file size here; adjust for new + ne_set_request_body_fd API. + +Sun Nov 23 15:05:12 2003 Joe Orton <joe@manyfish.co.uk> + + * ne_basic.c, ne_basic.h: Remove two-functions-in-one, + ne_put_if_unmodified. + +Fri Nov 14 14:05:32 2003 Joe Orton <joe@manyfish.co.uk> + + * ne_utils.c (ne_has_support): Add NE_FEATURE_IDNA. + +Fri Nov 14 13:11:49 2003 Joe Orton <joe@manyfish.co.uk> + + * ne_session.c (set_hostinfo): [NE_HAVE_LIBIDN]: Use string from + IDNA ToAscii operation on provided hostname if successful. + +Fri Nov 14 11:23:16 2003 Joe Orton <joe@manyfish.co.uk> + + All files: replace use of NEON_NODAV with NE_HAVE_DAV, NEON_SSL + with NE_HAVE_SSL, NEON_ZLIB with NE_HAVE_ZLIB. Use NE_HAVE_DAV + not USE_DAV_LOCKS. + + * ne_utils.c (ne_has_support): New feature detection interface, + replaces ne_supports_ssl. + Thu Nov 13 20:38:28 2003 Joe Orton <joe@manyfish.co.uk> * ne_request.c (ne_begin_request): Presume a 205 response has no @@ -126,6 +448,42 @@ Thu Nov 13 20:31:07 2003 Joe Orton <joe@manyfish.co.uk> request as a valid proxy auth challenge, to work around buggy proxies. +Tue Nov 11 21:13:18 2003 Joe Orton <joe@manyfish.co.uk> + + Place library-internal symbols in the "ne__" namespace. + + * ne_request.c (ne__pull_request_body): Renamed from + ne_pull_request_body; all callers updated. + + * ne_session.c (ne__negotiate_ssl): Renamed from + ne_negotiate_ssl; all callers updated. + +Tue Nov 11 21:08:54 2003 Joe Orton <joe@manyfish.co.uk> + + * ne_alloc.h: Mark all allocation functions as having 'malloc' + attribute for GCC. + +Tue Nov 11 20:36:12 2003 Joe Orton <joe@manyfish.co.uk> + + * ne_xml.h (ne_xml_failure): Replaces ne_xml_valid, + inverted and more useful return value. + + * ne_xml.c (struct ne_xml_parser_s): Replace 'valid' field with + 'failure', with inverted logic. + (start_element, end_element, char_data): Check failure flag + appropriately. Set failure flag to return value of callback. + Set failure flag to positive integer on a parse error. + (ne_xml_create): Don't initialize failure flag. + (ne_xml_parse): Check/set failure flag appropriately. + (sax_error): Only set an error string (and the error flag) + if failure is zero. + + * ne_207.c (ne_simple_request): Adjust to use ne_xml_failure. + + * ne_locks.c (ne_lock, ne_lock_refresh): Likewise. + + * ne_props.c (propfind): Likewise. + Wed Oct 22 22:19:19 2003 Joe Orton <joe@manyfish.co.uk> * ne_request.c (read_response_block): Treat an EOF without clean diff --git a/neon/src/Makefile.in b/neon/src/Makefile.in index a0fb73ae0..76beea257 100644 --- a/neon/src/Makefile.in +++ b/neon/src/Makefile.in @@ -14,7 +14,7 @@ libdir = @libdir@ # Build paths VPATH = @srcdir@ -top_builddir = .. +top_builddir = @top_builddir@ top_srcdir = @top_srcdir@ # Toolchain settings. @@ -25,7 +25,7 @@ LIBTOOL = @LIBTOOL@ # Flags CPPFLAGS = @DEFS@ @CPPFLAGS@ -CFLAGS = @CFLAGS@ @NEON_CFLAGS@ +CFLAGS = @CFLAGS@ LDFLAGS = @LDFLAGS@ NEON_LINK_FLAGS = @NEON_LINK_FLAGS@ # Note: don't substitute @LIBS@ in here; during a bundled @@ -33,15 +33,14 @@ NEON_LINK_FLAGS = @NEON_LINK_FLAGS@ LIBS = @NEON_LIBS@ @NEON_LTLIBS@ COMPILE = $(CC) $(CPPFLAGS) $(CFLAGS) -LINK = $(LIBTOOL) --quiet --mode=link $(CC) $(LDFLAGS) +LINK = $(LIBTOOL) --quiet --mode=link $(CC) -no-undefined $(LDFLAGS) NEON_BASEOBJS = ne_request.@NEON_OBJEXT@ ne_session.@NEON_OBJEXT@ \ ne_basic.@NEON_OBJEXT@ ne_string.@NEON_OBJEXT@ \ ne_uri.@NEON_OBJEXT@ ne_dates.@NEON_OBJEXT@ ne_alloc.@NEON_OBJEXT@ \ ne_md5.@NEON_OBJEXT@ ne_utils.@NEON_OBJEXT@ \ ne_socket.@NEON_OBJEXT@ ne_auth.@NEON_OBJEXT@ \ - ne_cookies.@NEON_OBJEXT@ ne_redirect.@NEON_OBJEXT@ \ - ne_compress.@NEON_OBJEXT@ + ne_redirect.@NEON_OBJEXT@ ne_compress.@NEON_OBJEXT@ NEON_DAVOBJS = $(NEON_BASEOBJS) \ ne_207.@NEON_OBJEXT@ ne_xml.@NEON_OBJEXT@ \ @@ -70,6 +69,7 @@ libneon.la: $(OBJECTS) $(LINK) -rpath $(libdir) $(NEON_LINK_FLAGS) -o $@ $(OBJECTS) $(LIBS) libneon.a: $(OBJECTS) + @rm -f $@ $(AR) cru $@ $(OBJECTS) $(RANLIB) $@ @@ -102,6 +102,9 @@ ne_session.@NEON_OBJEXT@: ne_session.c ne_session.h ne_alloc.h \ ne_openssl.@NEON_OBJEXT@: ne_openssl.c ne_session.h ne_ssl.h ne_privssl.h \ ne_private.h $(top_builddir)/config.h +ne_gnutls.@NEON_OBJEXT@: ne_gnutls.c ne_session.h ne_ssl.h ne_privssl.h \ + ne_private.h $(top_builddir)/config.h + ne_socket.@NEON_OBJEXT@: ne_socket.c ne_socket.h $(top_builddir)/config.h \ ne_privssl.h ne_string.h @@ -139,9 +142,8 @@ ne_locks.@NEON_OBJEXT@: ne_locks.c $(neonreq) ne_locks.h ne_207.h ne_xml.h ne_redirect.@NEON_OBJEXT@: ne_redirect.c $(neonreq) ne_redirect.h \ ne_uri.h ne_private.h -ne_cookies.@NEON_OBJEXT@: ne_cookies.c $(neonreq) ne_cookies.h ne_uri.h \ - ne_private.h - ne_compress.@NEON_OBJEXT@: ne_compress.c $(neonreq) ne_compress.h ne_acl.@NEON_OBJEXT@: ne_acl.c ne_acl.h $(neonreq) + +ne_stubssl.@NEON_OBJEXT@: ne_stubssl.c $(neonreq) diff --git a/neon/src/ne_207.c b/neon/src/ne_207.c index 039bc6b9d..ad3b272a6 100644 --- a/neon/src/ne_207.c +++ b/neon/src/ne_207.c @@ -142,8 +142,12 @@ static int start_element(void *userdata, int parent, state != ELM_href) return NE_XML_DECLINE; - if (state == ELM_propstat && p->start_propstat) + if (state == ELM_propstat && p->start_propstat) { p->propstat = p->start_propstat(p->userdata, p->response); + if (p->propstat == NULL) { + return NE_XML_ABORT; + } + } ne_buffer_clear(p->cdata); @@ -158,7 +162,7 @@ static int end_element(void *userdata, int state, const char *nspace, const char *name) { ne_207_parser *p = userdata; - const char *cdata = p->cdata->data; + const char *cdata = ne_shave(p->cdata->data, "\r\n\t "); switch (state) { case ELM_responsedescription: @@ -318,7 +322,7 @@ int ne_simple_request(ne_session *sess, ne_request *req) if (ret == NE_OK) { if (ne_get_status(req)->code == 207) { - if (!ne_xml_valid(p)) { + if (ne_xml_failed(p)) { /* The parse was invalid */ ne_set_error(sess, "%s", ne_xml_get_error(p)); ret = NE_ERROR; diff --git a/neon/src/ne_207.h b/neon/src/ne_207.h index 65fe471fc..1ce8b0642 100644 --- a/neon/src/ne_207.h +++ b/neon/src/ne_207.h @@ -1,6 +1,6 @@ /* WebDAV 207 multi-status response handling - Copyright (C) 1999-2003, Joe Orton <joe@manyfish.co.uk> + Copyright (C) 1999-2004, Joe Orton <joe@manyfish.co.uk> This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public @@ -54,7 +54,9 @@ typedef void ne_207_end_response(void *userdata, void *response, * the response in which this propstat is contains is passed as the * 'response' parameter. The return value of each 'start_propstat' is * passed as the 'propstat' parameter' to the corresponding - * 'end_propstat' callback. */ + * 'end_propstat' callback. If the start_propstat callback returns + * NULL, parsing is aborted (the XML parser error must be set by the + * callback). */ typedef void *ne_207_start_propstat(void *userdata, void *response); typedef void ne_207_end_propstat(void *userdata, void *propstat, const ne_status *status, diff --git a/neon/src/ne_acl.c b/neon/src/ne_acl.c index 888cb752e..a6047e0a2 100644 --- a/neon/src/ne_acl.c +++ b/neon/src/ne_acl.c @@ -41,6 +41,7 @@ #include "ne_string.h" #include "ne_acl.h" #include "ne_uri.h" +#include "ne_xml.h" /* for NE_XML_MEDIA_TYPE */ static ne_buffer *acl_body(ne_acl_entry *right, int count) { @@ -110,12 +111,12 @@ int ne_acl_set(ne_session *sess, const char *uri, ne_request *req = ne_request_create(sess, "ACL", uri); ne_buffer *body = acl_body(entries, numentries); -#ifdef USE_DAV_LOCKS +#ifdef NE_HAVE_DAV ne_lock_using_resource(req, uri, 0); #endif ne_set_request_body_buffer(req, body->data, ne_buffer_size(body)); - ne_add_request_header(req, "Content-Type", "text/xml"); + ne_add_request_header(req, "Content-Type", NE_XML_MEDIA_TYPE); ret = ne_request_dispatch(req); ne_buffer_destroy(body); diff --git a/neon/src/ne_alloc.h b/neon/src/ne_alloc.h index 1da4a3806..3a3e2ecd7 100644 --- a/neon/src/ne_alloc.h +++ b/neon/src/ne_alloc.h @@ -1,6 +1,6 @@ /* Replacement memory allocation handling etc. - Copyright (C) 1999-2002, Joe Orton <joe@manyfish.co.uk> + Copyright (C) 1999-2004, Joe Orton <joe@manyfish.co.uk> This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public @@ -41,11 +41,11 @@ void ne_oom_callback(void (*callback)(void)); * neon will abort(); calling an OOM callback beforehand if one is * registered. The C library will only ever return NULL if the * operating system does not use optimistic memory allocation. */ -void *ne_malloc(size_t size); -void *ne_calloc(size_t size); -void *ne_realloc(void *ptr, size_t s); -char *ne_strdup(const char *s); -char *ne_strndup(const char *s, size_t n); +void *ne_malloc(size_t size) ne_attribute_malloc; +void *ne_calloc(size_t size) ne_attribute_malloc; +void *ne_realloc(void *ptr, size_t s) ne_attribute_malloc; +char *ne_strdup(const char *s) ne_attribute_malloc; +char *ne_strndup(const char *s, size_t n) ne_attribute_malloc; #define ne_free free #endif diff --git a/neon/src/ne_auth.c b/neon/src/ne_auth.c index fd2cfb787..17c365ce7 100644 --- a/neon/src/ne_auth.c +++ b/neon/src/ne_auth.c @@ -50,7 +50,7 @@ #include <windows.h> /* for GetCurrentThreadId() etc */ #endif -#ifdef NEON_SSL +#ifdef HAVE_OPENSSL #include <openssl/rand.h> #endif @@ -161,8 +161,11 @@ typedef struct { /* This used for Basic auth */ char *basic; #ifdef HAVE_GSSAPI - /* This used for GSSAPI auth */ + /* for the GSSAPI/Negotiate scheme: */ char *gssapi_token; + gss_ctx_id_t gssctx; + gss_name_t gssname; + gss_OID gssmech; #endif /* These all used for Digest auth */ char *realm; @@ -202,7 +205,7 @@ struct auth_request { struct ne_md5_ctx response_body; /* Results of response-header callbacks */ - char *auth_hdr, *auth_info_hdr; + ne_buffer *auth_hdr, *auth_info_hdr; }; static void clean_session(auth_session *sess) @@ -214,6 +217,17 @@ static void clean_session(auth_session *sess) NE_FREE(sess->opaque); NE_FREE(sess->realm); #ifdef HAVE_GSSAPI + { + int major; + + if (sess->gssctx != GSS_C_NO_CONTEXT) + gss_delete_sec_context(&major, sess->gssctx, GSS_C_NO_BUFFER); + + if (sess->gssmech != GSS_C_NO_OID) { + gss_release_oid(&major, &sess->gssmech); + sess->gssmech = GSS_C_NO_OID; + } + } NE_FREE(sess->gssapi_token); #endif } @@ -227,7 +241,7 @@ static char *get_cnonce(void) ne_md5_init_ctx(&hash); -#ifdef NEON_SSL +#ifdef HAVE_OPENSSL if (RAND_status() == 1 && RAND_pseudo_bytes(data, sizeof data) >= 0) ne_md5_process_bytes(data, sizeof data, &hash); else { @@ -260,7 +274,7 @@ static char *get_cnonce(void) ne_md5_process_bytes(&pid, sizeof pid, &hash); } -#ifdef NEON_SSL +#ifdef HAVE_OPENSSL } #endif @@ -321,69 +335,162 @@ static char *request_basic(auth_session *sess) /* Add GSSAPI authentication credentials to a request */ static char *request_gssapi(auth_session *sess) { - return ne_concat("Negotiate ", sess->gssapi_token, "\r\n", NULL); + if (sess->gssapi_token) + return ne_concat("Negotiate ", sess->gssapi_token, "\r\n", NULL); + else + return NULL; } -static int get_gss_name(gss_name_t *server, auth_session *sess) +/* Create an GSSAPI name for server HOSTNAME; returns non-zero on + * error. */ +static void get_gss_name(gss_name_t *server, const char *hostname) { - unsigned int major_status, minor_status; + unsigned int major, minor; gss_buffer_desc token = GSS_C_EMPTY_BUFFER; - token.value = ne_concat("HTTP@", sess->sess->server.hostname, NULL); + token.value = ne_concat("HTTP@", hostname, NULL); token.length = strlen(token.value); - major_status = gss_import_name(&minor_status, &token, - GSS_C_NT_HOSTBASED_SERVICE, - server); - return GSS_ERROR(major_status) ? -1 : 0; + major = gss_import_name(&minor, &token, GSS_C_NT_HOSTBASED_SERVICE, + server); + ne_free(token.value); + + if (GSS_ERROR(major)) { + NE_DEBUG(NE_DBG_HTTPAUTH, "gssapi: gss_import_name failed.\n"); + *server = GSS_C_NO_NAME; + } } -/* Examine a GSSAPI auth challenge; returns 0 if a valid challenge, - * else non-zero. */ -static int -gssapi_challenge(auth_session *sess, struct auth_challenge *parms) +/* Append GSSAPI error(s) for STATUS of type TYPE to BUF; prepending + * ": " to each error if *FLAG is non-zero, setting *FLAG after an + * error has been appended. */ +static void make_gss_error(ne_buffer *buf, int *flag, + unsigned int status, int type) { - gss_ctx_id_t context; - gss_name_t server_name; - unsigned int major_status, minor_status; - gss_buffer_desc input_token = GSS_C_EMPTY_BUFFER; - gss_buffer_desc output_token = GSS_C_EMPTY_BUFFER; + int major, minor; + int context = 0; + + do { + gss_buffer_desc msg; + major = gss_display_status(&minor, status, type, + GSS_C_NO_OID, &context, &msg); + if (major == GSS_S_COMPLETE && msg.length) { + if ((*flag)++) ne_buffer_append(buf, ": ", 2); + ne_buffer_append(buf, msg.value, msg.length); + } + if (msg.length) gss_release_buffer(&minor, &msg); + } while (context); +} - clean_session(sess); +/* Continue a GSS-API Negotiate exchange, using input TOKEN if + * non-NULL. Returns non-zero on error. */ +static int continue_negotiate(auth_session *sess, const char *token) +{ + unsigned int major, minor; + gss_buffer_desc input = GSS_C_EMPTY_BUFFER; + gss_buffer_desc output = GSS_C_EMPTY_BUFFER; + unsigned char *bintoken = NULL; + int ret; + gss_OID mech = sess->gssmech; + + if (token) { + input.length = ne_unbase64(token, &bintoken); + if (input.length == 0) { + NE_DEBUG(NE_DBG_HTTPAUTH, "gssapi: Invalid input [%s].\n", + token); + return -1; + } + input.value = bintoken; + NE_DEBUG(NE_DBG_HTTPAUTH, "gssapi: Continuation token [%s]\n", token); + } + else if (sess->gssctx != GSS_C_NO_CONTEXT) { + NE_DEBUG(NE_DBG_HTTPAUTH, "gssapi: Reset incomplete context.\n"); + gss_delete_sec_context(&minor, &sess->gssctx, GSS_C_NO_BUFFER); + } - if (get_gss_name(&server_name, sess)) + major = gss_init_sec_context(&minor, GSS_C_NO_CREDENTIAL, &sess->gssctx, + sess->gssname, mech, + GSS_C_MUTUAL_FLAG, GSS_C_INDEFINITE, + GSS_C_NO_CHANNEL_BINDINGS, + &input, &sess->gssmech, &output, NULL, NULL); + + /* done with the input token. */ + if (bintoken) ne_free(bintoken); + + if (GSS_ERROR(major)) { + ne_buffer *err = ne_buffer_create(); + int flag = 0; + + make_gss_error(err, &flag, major, GSS_C_GSS_CODE); + make_gss_error(err, &flag, minor, GSS_C_MECH_CODE); + NE_DEBUG(NE_DBG_HTTPAUTH, "gssapi: Error: %s\n", err->data); + ne_set_error(sess->sess, _("GSSAPI authentication error (%s)"), + err->data); + ne_buffer_destroy(err); return -1; + } - major_status = gss_init_sec_context(&minor_status, - GSS_C_NO_CREDENTIAL, - &context, - server_name, - GSS_C_NO_OID, - 0, - GSS_C_INDEFINITE, - GSS_C_NO_CHANNEL_BINDINGS, - &input_token, - NULL, - &output_token, - NULL, - NULL); - gss_release_name(&minor_status, &server_name); - - if (GSS_ERROR(major_status)) { - NE_DEBUG(NE_DBG_HTTPAUTH, "gss_init_sec_context failed.\n"); + if (major == GSS_S_CONTINUE_NEEDED || major == GSS_S_COMPLETE) { + NE_DEBUG(NE_DBG_HTTPAUTH, "gssapi: init_sec_context OK. (major=%d)\n", + major); + ret = 0; + } + else { + NE_DEBUG(NE_DBG_HTTPAUTH, "gssapi: Init failure %d.\n", major); + ret = -1; + } + + if (major != GSS_S_CONTINUE_NEEDED) { + /* context no longer needed: destroy it */ + gss_delete_sec_context(&minor, &sess->gssctx, GSS_C_NO_BUFFER); + } + + if (output.length) { + sess->gssapi_token = ne_base64(output.value, output.length); + NE_DEBUG(NE_DBG_HTTPAUTH, "gssapi: Output token: [%s]\n", + sess->gssapi_token); + gss_release_buffer(&minor, &output); + } else { + NE_DEBUG(NE_DBG_HTTPAUTH, "gssapi: No output token.\n"); + } + + return ret; +} + +/* Process a Negotiate challange CHALL in session SESS; returns zero + * if challenge is accepted. */ +static int gssapi_challenge(auth_session *sess, struct auth_challenge *chall) +{ + int ret = continue_negotiate(sess, chall->opaque); + if (ret == 0) + sess->scheme = auth_scheme_gssapi; + return ret; +} + +/* Verify the header HDR in a Negotiate response. */ +static int verify_negotiate_response(auth_session *sess, char *hdr) +{ + char *sep, *ptr = strchr(hdr, ' '); + + if (strncmp(hdr, "Negotiate", ptr - hdr) != 0) { + NE_DEBUG(NE_DBG_HTTPAUTH, "gssapi: Not a Negotiate response!\n"); return -1; } - if (output_token.length == 0) - return -1; + ptr++; - sess->gssapi_token = ne_base64(output_token.value, output_token.length); - gss_release_buffer(&major_status, &output_token); + if (strlen(ptr) == 0) { + NE_DEBUG(NE_DBG_HTTPAUTH, "gssapi: No token in Negotiate response!\n"); + return 0; + } - NE_DEBUG(NE_DBG_HTTPAUTH, - "Base64 encoded GSSAPI challenge: %s.\n", sess->gssapi_token); - sess->scheme = auth_scheme_gssapi; - return 0; + if ((sep = strchr(ptr, ',')) != NULL) + *sep = '\0'; + if ((sep = strchr(ptr, ' ')) != NULL) + *sep = '\0'; + + NE_DEBUG(NE_DBG_HTTPAUTH, "gssapi: Negotiate response token [%s]\n", ptr); + return continue_negotiate(sess, ptr); } #endif @@ -532,7 +639,7 @@ static char *request_digest(auth_session *sess, struct auth_request *req) * where-ever it is coming from, and calculate the digest. */ NE_DEBUG(NE_DBG_HTTPAUTH, "Digesting request body...\n"); - ne_pull_request_body(req->request, digest_body, &body); + ne__pull_request_body(req->request, digest_body, &body); NE_DEBUG(NE_DBG_HTTPAUTH, "Digesting request body done.\n"); ne_md5_finish_ctx(&body, tmp_md5); @@ -637,7 +744,8 @@ static int tokenize(char **hdr, char **key, char **value, int ischall) *pnt = '\0'; *value = pnt + 1; state = AFTER_EQ; - } else if (*pnt == ' ' && ischall && *key != NULL) { + } else if ((*pnt == ' ' || *pnt == ',') + && ischall && *key != NULL) { *value = NULL; *pnt = '\0'; *hdr = pnt + 1; @@ -679,8 +787,8 @@ static int tokenize(char **hdr, char **key, char **value, int ischall) * 0 if it gives a valid authentication for the server * non-zero otherwise (don't believe the response in this case!). */ -static int verify_response(struct auth_request *req, auth_session *sess, - const char *value) +static int verify_digest_response(struct auth_request *req, auth_session *sess, + const char *value) { char *hdr, *pnt, *key, *val; auth_qop qop = auth_qop_none; @@ -842,36 +950,39 @@ static int auth_challenge(auth_session *sess, const char *value) while (!tokenize(&pnt, &key, &val, 1)) { if (val == NULL) { - /* We have a new challenge */ - NE_DEBUG(NE_DBG_HTTPAUTH, "New challenge for scheme [%s]\n", key); - chall = ne_calloc(sizeof *chall); - - chall->next = challenges; - challenges = chall; - /* Initialize the challenge parameters */ - /* Which auth-scheme is it (case-insensitive matching) */ + auth_scheme scheme; + if (strcasecmp(key, "basic") == 0) { - NE_DEBUG(NE_DBG_HTTPAUTH, "Basic scheme.\n"); - chall->scheme = auth_scheme_basic; + scheme = auth_scheme_basic; } else if (strcasecmp(key, "digest") == 0) { - NE_DEBUG(NE_DBG_HTTPAUTH, "Digest scheme.\n"); - chall->scheme = auth_scheme_digest; -#ifdef HAVE_GSSAPI + scheme = auth_scheme_digest; + } +#ifdef HAVE_GSSAPI + /* cope with a Negotiate parameter which doesn't match the + * auth-param due to the broken spec. */ + else if (chall && chall->scheme == auth_scheme_gssapi + && chall->opaque == NULL) { + chall->opaque = key; + continue; } else if (strcasecmp(key, "negotiate") == 0) { - NE_DEBUG(NE_DBG_HTTPAUTH, "GSSAPI scheme.\n"); - chall->scheme = auth_scheme_gssapi; + scheme = auth_scheme_gssapi; + } #endif - } else { - NE_DEBUG(NE_DBG_HTTPAUTH, "Unknown scheme.\n"); - ne_free(chall); - challenges = NULL; - break; + else { + NE_DEBUG(NE_DBG_HTTPAUTH, "Ignoring challenge '%s'.\n", key); + chall = NULL; + continue; } + + NE_DEBUG(NE_DBG_HTTPAUTH, "New '%s' challenge.\n", key); + chall = ne_calloc(sizeof *chall); + chall->scheme = scheme; + chall->next = challenges; + challenges = chall; continue; } else if (chall == NULL) { - /* If we haven't got an auth-scheme, and we're - * haven't yet found a challenge, skip this pair. - */ + /* Ignore pairs for an unknown challenge. */ + NE_DEBUG(NE_DBG_HTTPAUTH, "Ignored pair: %s = %s\n", key, val); continue; } @@ -924,15 +1035,17 @@ static int auth_challenge(auth_session *sess, const char *value) success = 0; #ifdef HAVE_GSSAPI - if (strcmp(ne_get_scheme(sess->sess), "https") == 0) { + /* Ignore Negotiate challenges from origin servers which don't + * come over SSL. */ + if (sess->spec == &ah_proxy_class || sess->context != AUTH_ANY) { NE_DEBUG(NE_DBG_HTTPAUTH, "Looking for GSSAPI.\n"); /* Try a GSSAPI challenge */ for (chall = challenges; chall != NULL; chall = chall->next) { if (chall->scheme == auth_scheme_gssapi) { - if (!gssapi_challenge(sess, chall)) { - success = 1; - break; - } + if (!gssapi_challenge(sess, chall)) { + success = 1; + break; + } } } } @@ -985,12 +1098,21 @@ static int auth_challenge(auth_session *sess, const char *value) } /* The body reader callback. */ -static void auth_body_reader(void *cookie, const char *block, size_t length) +static int auth_body_reader(void *cookie, const char *block, size_t length) { struct ne_md5_ctx *ctx = cookie; NE_DEBUG(NE_DBG_HTTPAUTH, "Digesting %" NE_FMT_SIZE_T " bytes of response body.\n", length); ne_md5_process_bytes(block, length, ctx); + return 0; +} + +/* Collect auth challenges into an ne_buffer */ +static void ah_collect_header(void *userdata, const char *value) +{ + ne_buffer *ar = userdata; + if (ne_buffer_size(ar)) ne_buffer_append(ar, ", ", 2); + ne_buffer_zappend(ar, value); } static void ah_create(ne_request *req, void *session, const char *method, @@ -1009,14 +1131,14 @@ static void ah_create(ne_request *req, void *session, const char *method, areq->method = method; areq->uri = uri; areq->request = req; + areq->auth_hdr = ne_buffer_create(); + areq->auth_info_hdr = ne_buffer_create(); ne_add_response_header_handler(req, sess->spec->resp_hdr, - ne_duplicate_header, &areq->auth_hdr); - + ah_collect_header, areq->auth_hdr); ne_add_response_header_handler(req, sess->spec->resp_info_hdr, - ne_duplicate_header, - &areq->auth_info_hdr); + ah_collect_header, areq->auth_info_hdr); sess->attempt = 0; @@ -1035,7 +1157,7 @@ static void ah_pre_send(ne_request *r, void *cookie, ne_buffer *request) } else { char *value; - NE_DEBUG(NE_DBG_HTTPAUTH, "Handling."); + NE_DEBUG(NE_DBG_HTTPAUTH, "Handling auth session.\n"); req->will_handle = 1; if (sess->qop == auth_qop_auth_int) { @@ -1082,22 +1204,44 @@ static int ah_post_send(ne_request *req, void *cookie, const ne_status *status) if (!areq) return NE_OK; +#ifdef HAVE_GSSAPI + /* whatever happens: forget the GSSAPI token cached thus far */ + if (sess->gssapi_token) { + ne_free(sess->gssapi_token); + sess->gssapi_token = NULL; + } +#endif + NE_DEBUG(NE_DBG_HTTPAUTH, "ah_post_send (#%d), code is %d (want %d), %s is %s\n", sess->attempt, status->code, sess->spec->status_code, - sess->spec->resp_hdr, SAFELY(areq->auth_hdr)); - if (areq->auth_info_hdr != NULL && - verify_response(areq, sess, areq->auth_info_hdr)) { - NE_DEBUG(NE_DBG_HTTPAUTH, "Response authentication invalid.\n"); - ne_set_error(sess->sess, "%s", _(sess->spec->fail_msg)); - ret = NE_ERROR; - } else if ((status->code == sess->spec->status_code || - (status->code == 401 && sess->context == AUTH_CONNECT)) && - areq->auth_hdr != NULL) { + sess->spec->resp_hdr, areq->auth_hdr->data); + if (ne_buffer_size(areq->auth_info_hdr) + && sess->scheme == auth_scheme_digest) { + if (verify_digest_response(areq, sess, areq->auth_info_hdr->data)) { + NE_DEBUG(NE_DBG_HTTPAUTH, "Response authentication invalid.\n"); + ne_set_error(sess->sess, "%s", _(sess->spec->fail_msg)); + ret = NE_ERROR; + } + } +#ifdef HAVE_GSSAPI + /* one must wonder... has Mr Brezak actually read RFC2617? */ + else if (sess->scheme == auth_scheme_gssapi + && (status->klass == 2 || status->klass == 3) + && ne_buffer_size(areq->auth_hdr)) { + if (verify_negotiate_response(sess, areq->auth_hdr->data)) { + NE_DEBUG(NE_DBG_HTTPAUTH, "gssapi: Mutual auth failed.\n"); + ret = NE_ERROR; + } + } +#endif /* HAVE_GSSAPI */ + else if ((status->code == sess->spec->status_code || + (status->code == 401 && sess->context == AUTH_CONNECT)) && + ne_buffer_size(areq->auth_hdr)) { /* note above: allow a 401 in response to a CONNECT request * from a proxy since some buggy proxies send that. */ - NE_DEBUG(NE_DBG_HTTPAUTH, "Got challenge with code %d.\n", status->code); - if (!auth_challenge(sess, areq->auth_hdr)) { + NE_DEBUG(NE_DBG_HTTPAUTH, "Got challenge (code %d).\n", status->code); + if (!auth_challenge(sess, areq->auth_hdr->data)) { ret = NE_RETRY; } else { clean_session(sess); @@ -1105,9 +1249,9 @@ static int ah_post_send(ne_request *req, void *cookie, const ne_status *status) } } - NE_FREE(areq->auth_info_hdr); - NE_FREE(areq->auth_hdr); - + ne_buffer_clear(areq->auth_hdr); + ne_buffer_clear(areq->auth_info_hdr); + return ret; } @@ -1115,13 +1259,25 @@ static void ah_destroy(ne_request *req, void *session) { auth_session *sess = session; struct auth_request *areq = ne_get_request_private(req, sess->spec->id); - if (areq) ne_free(areq); + + if (areq) { + ne_buffer_destroy(areq->auth_info_hdr); + ne_buffer_destroy(areq->auth_hdr); + ne_free(areq); + } } static void free_auth(void *cookie) { auth_session *sess = cookie; +#ifdef HAVE_GSSAPI + if (sess->gssname != GSS_C_NO_NAME) { + int major; + gss_release_name(&major, sess->gssname); + } +#endif + clean_session(sess); ne_free(sess); } @@ -1137,10 +1293,19 @@ static void auth_register(ne_session *sess, int isproxy, ahs->sess = sess; ahs->spec = ahc; - if (strcmp(ne_get_scheme(sess), "https") == 0) + if (strcmp(ne_get_scheme(sess), "https") == 0) { ahs->context = isproxy ? AUTH_CONNECT : AUTH_NOTCONNECT; - else +#ifdef HAVE_GSSAPI + { + get_gss_name(&ahs->gssname, (isproxy ? sess->proxy.hostname + : sess->server.hostname)); + ahs->gssctx = GSS_C_NO_CONTEXT; + ahs->gssmech = GSS_C_NO_OID; + } +#endif + } else { ahs->context = AUTH_ANY; + } /* Register hooks */ ne_hook_create_request(sess, ah_create, ahs); diff --git a/neon/src/ne_basic.c b/neon/src/ne_basic.c index d3b4eb3fa..003877895 100644 --- a/neon/src/ne_basic.c +++ b/neon/src/ne_basic.c @@ -1,6 +1,6 @@ /* Basic HTTP and WebDAV methods - Copyright (C) 1999-2003, Joe Orton <joe@manyfish.co.uk> + Copyright (C) 1999-2004, Joe Orton <joe@manyfish.co.uk> This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public @@ -22,6 +22,7 @@ #include "config.h" #include <sys/types.h> +#include <sys/stat.h> /* for struct stat */ #ifdef HAVE_STRING_H #include <string.h> @@ -41,13 +42,11 @@ #include "ne_basic.h" #include "ne_207.h" -#ifndef NEON_NODAV +#ifdef NE_HAVE_DAV #include "ne_uri.h" -#endif - -#ifdef USE_DAV_LOCKS #include "ne_locks.h" #endif + #include "ne_dates.h" #include "ne_i18n.h" @@ -82,73 +81,32 @@ int ne_getmodtime(ne_session *sess, const char *uri, time_t *modtime) /* PUT's from fd to URI */ int ne_put(ne_session *sess, const char *uri, int fd) { - ne_request *req = ne_request_create(sess, "PUT", uri); + ne_request *req; + struct stat st; int ret; - -#ifdef USE_DAV_LOCKS - ne_lock_using_resource(req, uri, 0); - ne_lock_using_parent(req, uri); -#endif - ne_set_request_body_fd(req, fd); - - ret = ne_request_dispatch(req); - - if (ret == NE_OK && ne_get_status(req)->klass != 2) - ret = NE_ERROR; - - ne_request_destroy(req); + if (fstat(fd, &st)) { + int errnum = errno; + char buf[200]; - return ret; -} - -/* Conditional HTTP put. - * PUTs from fd to uri, returning NE_FAILED if resource as URI has - * been modified more recently than 'since'. - */ -int -ne_put_if_unmodified(ne_session *sess, const char *uri, int fd, - time_t since) -{ - ne_request *req; - char *date; - int ret; - - if (ne_version_pre_http11(sess)) { - time_t modtime; - /* Server is not minimally HTTP/1.1 compliant. Do a HEAD to - * check the remote mod time. Of course, this makes the - * operation very non-atomic, but better than nothing. */ - ret = ne_getmodtime(sess, uri, &modtime); - if (ret != NE_OK) return ret; - if (modtime != since) - return NE_FAILED; + ne_set_error(sess, _("Could not determine file size: %s"), + ne_strerror(errnum, buf, sizeof buf)); + return NE_ERROR; } - + req = ne_request_create(sess, "PUT", uri); - date = ne_rfc1123_date(since); - /* Add in the conditionals */ - ne_add_request_header(req, "If-Unmodified-Since", date); - ne_free(date); - -#ifdef USE_DAV_LOCKS +#ifdef NE_HAVE_DAV ne_lock_using_resource(req, uri, 0); - /* FIXME: this will give 412 if the resource doesn't exist, since - * PUT may modify the parent... does that matter? */ + ne_lock_using_parent(req, uri); #endif - ne_set_request_body_fd(req, fd); - + ne_set_request_body_fd(req, fd, 0, st.st_size); + ret = ne_request_dispatch(req); - if (ret == NE_OK) { - if (ne_get_status(req)->code == 412) { - ret = NE_FAILED; - } else if (ne_get_status(req)->klass != 2) { - ret = NE_ERROR; - } - } + if (ret == NE_OK && ne_get_status(req)->klass != 2) + ret = NE_ERROR; ne_request_destroy(req); @@ -163,27 +121,32 @@ struct get_context { ne_content_range *range; }; -static void get_to_fd(void *userdata, const char *block, size_t length) +static int get_to_fd(void *userdata, const char *block, size_t length) { struct get_context *ctx = userdata; ssize_t ret; + if (ctx->error) { + return -1; + } + if (!ctx->error) { while (length > 0) { ret = write(ctx->fd, block, length); if (ret < 0) { char err[200]; - ctx->error = 1; ne_strerror(errno, err, sizeof err); ne_set_error(ctx->session, _("Could not write to file: %s"), err); - break; + return -1; } else { length -= ret; block += ret; } } } + + return 0; } static int accept_206(void *ud, ne_request *req, const ne_status *st) @@ -396,9 +359,14 @@ void ne_content_type_handler(void *userdata, const char *value) /* set subtype, losing any trailing whitespace */ ct->subtype = ne_shave(stype, " \t"); - /* 2616#3.7.1: subtypes of text/ default to charset ISO-8859-1. */ - if (ct->charset == NULL && strcasecmp(ct->type, "text") == 0) - ct->charset = "ISO-8859-1"; + if (ct->charset == NULL && strcasecmp(ct->type, "text") == 0) { + /* 3280§3.1: text/xml without charset implies us-ascii. */ + if (strcasecmp(ct->subtype, "xml") == 0) + ct->charset = "us-ascii"; + /* 2616§3.7.1: subtypes of text/ default to charset ISO-8859-1. */ + else + ct->charset = "ISO-8859-1"; + } } static void dav_hdr_handler(void *userdata, const char *value) @@ -445,7 +413,7 @@ int ne_options(ne_session *sess, const char *uri, return ret; } -#ifndef NEON_NODAV +#ifdef NE_HAVE_DAV void ne_add_depth_header(ne_request *req, int depth) { @@ -474,7 +442,7 @@ static int copy_or_move(ne_session *sess, int is_move, int overwrite, ne_add_depth_header(req, depth); } -#ifdef USE_DAV_LOCKS +#ifdef NE_HAVE_DAV if (is_move) { ne_lock_using_resource(req, src, NE_DEPTH_INFINITE); } @@ -509,7 +477,7 @@ int ne_delete(ne_session *sess, const char *uri) { ne_request *req = ne_request_create(sess, "DELETE", uri); -#ifdef USE_DAV_LOCKS +#ifdef NE_HAVE_DAV ne_lock_using_resource(req, uri, NE_DEPTH_INFINITE); ne_lock_using_parent(req, uri); #endif @@ -539,7 +507,7 @@ int ne_mkcol(ne_session *sess, const char *uri) req = ne_request_create(sess, "MKCOL", real_uri); -#ifdef USE_DAV_LOCKS +#ifdef NE_HAVE_DAV ne_lock_using_resource(req, real_uri, 0); ne_lock_using_parent(req, real_uri); #endif @@ -551,4 +519,4 @@ int ne_mkcol(ne_session *sess, const char *uri) return ret; } -#endif /* NEON_NODAV */ +#endif /* NE_HAVE_DAV */ diff --git a/neon/src/ne_basic.h b/neon/src/ne_basic.h index 2daf91960..f9b515754 100644 --- a/neon/src/ne_basic.h +++ b/neon/src/ne_basic.h @@ -1,6 +1,6 @@ /* HTTP/1.1 methods - Copyright (C) 1999-2001, Joe Orton <joe@light.plus.com> + Copyright (C) 1999-2002, Joe Orton <joe@manyfish.co.uk> This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public @@ -28,58 +28,54 @@ BEGIN_NEON_DECLS -/* PUT resource at uri, reading request body from f */ -int ne_put(ne_session *sess, const char *uri, int fd); +/* Perform a GET request on resource at 'path', writing the entity + * body which is returned to 'fd'. */ +int ne_get(ne_session *sess, const char *path, int fd); -/* GET resource at uri, writing response body into f */ -int ne_get(ne_session *sess, const char *uri, int fd); - -#ifndef NEON_NODAV - -/* Basic WebDAV methods: - * ne_copy: copy resoure from src to dest - * ne_move: move resource from src to dest - * -> if overwrite is non-zero, the destination resource - * will be overwritten if it exists. - * ne_delete: delete resource at uri - * ne_mkcol: create a collection at uri (uri MUST have a trailing slash). - */ -int ne_copy(ne_session *sess, int overwrite, const char *src, const char *dest); -int ne_move(ne_session *sess, int overwrite, const char *src, const char *dest); -int ne_delete(ne_session *sess, const char *uri); -int ne_mkcol(ne_session *sess, const char *uri); +/* Perform a PUT request on resource at 'path', reading the entity + * body to submit from 'fd'. */ +int ne_put(ne_session *sess, const char *path, int fd); #define NE_DEPTH_ZERO (0) #define NE_DEPTH_ONE (1) #define NE_DEPTH_INFINITE (2) -/* Adds a Depth: header to a request */ -void ne_add_depth_header(ne_request *req, int depth); +/* For ne_copy and ne_move: + * + * If a resource exists at "dest" and overwrite is zero, the operation + * will fail; if overwrite is non-zero, any existing resource will + * be over-written. + */ -#endif /* NEON_NODAV */ +/* Copy resource from 'src to 'dest' paths. If 'src' identifies a + * collection resource, depth may be NE_DEPTH_ZERO to request that the + * collection and its properties are to be copied, or + * NE_DEPTH_INFINITE to request that the collection and its contents + * are to be copied. */ +int ne_copy(ne_session *sess, int overwrite, int depth, + const char *src, const char *dest); -/* PUT resource at uri as above, only if it has not been modified - * since given modtime. If server is HTTP/1.1, uses If-Unmodified-Since - * header; guaranteed failure if resource is modified after 'modtime'. - * If server is HTTP/1.0, HEAD's the resource first to fetch current - * modtime; race condition if resource is modified between HEAD and PUT. - */ -int ne_put_if_unmodified(ne_session *sess, - const char *uri, int fd, time_t modtime); +/* Move resource from 'src' to dest 'path'. */ +int ne_move(ne_session *sess, int overwrite, + const char *src, const char *dest); -/* GET resource at uri, passing response body blocks to 'reader' */ -int ne_read_file(ne_session *sess, const char *uri, - ne_block_reader reader, void *userdata); +/* Delete resource at 'path'. */ +int ne_delete(ne_session *sess, const char *path); +/* Create a collection at 'path', which MUST have a trailing slash. */ +int ne_mkcol(ne_session *sess, const char *path); -/* Retrieve modification time of resource at uri, place in *modtime. - * (uses HEAD) */ -int ne_getmodtime(ne_session *sess, const char *uri, time_t *modtime); +/* Adds a Depth: header to a request */ +void ne_add_depth_header(ne_request *req, int depth); + +/* Retrieve modification time of resource at location 'path', place in + * *modtime. (uses HEAD) */ +int ne_getmodtime(ne_session *sess, const char *path, time_t *modtime); typedef struct { const char *type, *subtype; const char *charset; char *value; -} ne_content_type; +} ne_content_type; /* Sets (*ne_content_type)userdata appropriately. * Caller must free ->value after use */ @@ -87,21 +83,15 @@ void ne_content_type_handler(void *userdata, const char *value); /* Server capabilities: */ typedef struct { - unsigned int broken_expect100:1; /* True if the server is known to - * have broken Expect: - * 100-continue support; Apache - * 1.3.6 and earlier. */ - unsigned int dav_class1; /* True if Class 1 WebDAV server */ unsigned int dav_class2; /* True if Class 2 WebDAV server */ unsigned int dav_executable; /* True if supports the 'executable' * property a. la. mod_dav */ } ne_server_capabilities; -/* Determines server capabilities (using OPTIONS). - * Pass uri="*" to determine proxy server capabilities if using - * a proxy server. */ -int ne_options(ne_session *sess, const char *uri, +/* Determines server capabilities (using OPTIONS). Pass 'path' as "*" + * to determine proxy server capabilities if using a proxy server. */ +int ne_options(ne_session *sess, const char *path, ne_server_capabilities *caps); /* Defines a range of bytes, starting at 'start' and ending @@ -124,12 +114,12 @@ typedef struct { * range.start = resume_from; * range.end = range.start + 999; (= 1000 bytes) * fseek(myfile, resume_from, SEEK_SET); - * ne_get_range(sess, uri, &range, myfile); */ -int ne_get_range(ne_session *sess, const char *uri, + * ne_get_range(sess, path, &range, myfile); */ +int ne_get_range(ne_session *sess, const char *path, ne_content_range *range, int fd); /* Post using buffer as request-body: stream response into f */ -int ne_post(ne_session *sess, const char *uri, int fd, const char *buffer); +int ne_post(ne_session *sess, const char *path, int fd, const char *buffer); END_NEON_DECLS diff --git a/neon/src/ne_compress.c b/neon/src/ne_compress.c index f35dc9e96..9b09c1968 100644 --- a/neon/src/ne_compress.c +++ b/neon/src/ne_compress.c @@ -33,9 +33,7 @@ #include "ne_utils.h" #include "ne_i18n.h" -#include "ne_private.h" - -#ifdef NEON_ZLIB +#ifdef NE_HAVE_ZLIB #include <zlib.h> @@ -89,8 +87,7 @@ struct ne_decompress_s { NE_Z_POST_HEADER, /* waiting for the end of the NUL-terminated bits. */ NE_Z_INFLATING, /* inflating response bytes. */ NE_Z_AFTER_DATA, /* after data; reading CRC32 & ISIZE */ - NE_Z_FINISHED, /* stream is finished. */ - NE_Z_ERROR /* inflate bombed. */ + NE_Z_FINISHED /* stream is finished. */ } state; }; @@ -105,7 +102,7 @@ struct ne_decompress_s { * HDR_DONE: all done, bytes following are raw DEFLATE data. * HDR_EXTENDED: all done, expect a NUL-termianted string * before the DEFLATE data - * HDR_ERROR: invalid header, give up. + * HDR_ERROR: invalid header, give up (session error is set). */ static int parse_header(ne_decompress *ctx) { @@ -115,7 +112,6 @@ static int parse_header(ne_decompress *ctx) h->id1, h->id2, h->cmeth, h->flags); if (h->id1 != ID1 || h->id2 != ID2 || h->cmeth != 8) { - ctx->state = NE_Z_ERROR; ne_set_error(ctx->session, "Compressed stream invalid"); return HDR_ERROR; } @@ -131,7 +127,6 @@ static int parse_header(ne_decompress *ctx) ctx->state = NE_Z_POST_HEADER; return HDR_EXTENDED; } else if (h->flags != 0) { - ctx->state = NE_Z_ERROR; ne_set_error(ctx->session, "Compressed stream not supported"); return HDR_ERROR; } @@ -147,14 +142,14 @@ static int parse_header(ne_decompress *ctx) /* Process extra 'len' bytes of 'buf' which were received after the * DEFLATE data. */ -static void process_footer(ne_decompress *ctx, +static int process_footer(ne_decompress *ctx, const unsigned char *buf, size_t len) { if (len + ctx->footcount > 8) { ne_set_error(ctx->session, "Too many bytes (%" NE_FMT_SIZE_T ") in gzip footer", len); - ctx->state = NE_Z_ERROR; + return -1; } else { memcpy(ctx->footer + ctx->footcount, buf, len); ctx->footcount += len; @@ -170,10 +165,11 @@ static void process_footer(ne_decompress *ctx, "given %lu vs computed %lu\n", crc, ctx->checksum); ne_set_error(ctx->session, "Checksum invalid for compressed stream"); - ctx->state = NE_Z_ERROR; + return -1; } } } + return 0; } /* A zlib function failed with 'code'; set the session error string @@ -197,7 +193,7 @@ static void set_zlib_error(ne_decompress *ctx, const char *msg, int code) } /* Inflate response buffer 'buf' of length 'len'. */ -static void do_inflate(ne_decompress *ctx, const char *buf, size_t len) +static int do_inflate(ne_decompress *ctx, const char *buf, size_t len) { int ret; @@ -236,39 +232,56 @@ static void do_inflate(ne_decompress *ctx, const char *buf, size_t len) ctx->zstr.avail_in); /* process the footer. */ ctx->state = NE_Z_AFTER_DATA; - process_footer(ctx, ctx->zstr.next_in, ctx->zstr.avail_in); + return process_footer(ctx, ctx->zstr.next_in, ctx->zstr.avail_in); } else if (ret != Z_OK) { - ctx->state = NE_Z_ERROR; set_zlib_error(ctx, _("Could not inflate data"), ret); + return NE_ERROR; } + return 0; } /* Callback which is passed blocks of the response body. */ -static void gz_reader(void *ud, const char *buf, size_t len) +static int gz_reader(void *ud, const char *buf, size_t len) { ne_decompress *ctx = ud; const char *zbuf; size_t count; + if (len == 0) { + /* End of response: */ + switch (ctx->state) { + case NE_Z_BEFORE_DATA: + if (ctx->enchdr && strcasecmp(ctx->enchdr, "gzip") == 0) { + /* response was truncated: break out. */ + break; + } + /* else, fall through */ + case NE_Z_FINISHED: /* complete gzip response */ + case NE_Z_PASSTHROUGH: /* complete uncompressed response */ + return ctx->reader(ctx->userdata, buf, 0); + default: + /* invalid state: truncated response. */ + break; + } + /* else: truncated response, fail. */ + ne_set_error(ctx->session, "Compressed response was truncated"); + return NE_ERROR; + } + switch (ctx->state) { case NE_Z_PASSTHROUGH: /* move along there. */ - ctx->reader(ctx->userdata, buf, len); - return; - - case NE_Z_ERROR: - /* beyond hope. */ - break; + return ctx->reader(ctx->userdata, buf, len); case NE_Z_FINISHED: /* Could argue for tolerance, and ignoring trailing content; * but it could mean something more serious. */ if (len > 0) { - ctx->state = NE_Z_ERROR; ne_set_error(ctx->session, "Unexpected content received after compressed stream"); + return NE_ERROR; } - break; + break; case NE_Z_BEFORE_DATA: /* work out whether this is a compressed response or not. */ @@ -280,7 +293,7 @@ static void gz_reader(void *ud, const char *buf, size_t len) ret = inflateInit2(&ctx->zstr, -MAX_WBITS); if (ret != Z_OK) { set_zlib_error(ctx, _("Could not initialize zlib"), ret); - return; + return -1; } ctx->zstrinit = 1; @@ -290,8 +303,7 @@ static void gz_reader(void *ud, const char *buf, size_t len) * would require add_resp_body_rdr to have defined * ordering semantics etc etc */ ctx->state = NE_Z_PASSTHROUGH; - ctx->reader(ctx->userdata, buf, len); - return; + return ctx->reader(ctx->userdata, buf, len); } ctx->state = NE_Z_IN_HEADER; @@ -308,7 +320,7 @@ static void gz_reader(void *ud, const char *buf, size_t len) ctx->incount += count; /* have we got the full header yet? */ if (ctx->incount != 10) { - return; + return 0; } buf += count; @@ -317,14 +329,15 @@ static void gz_reader(void *ud, const char *buf, size_t len) switch (parse_header(ctx)) { case HDR_EXTENDED: if (len == 0) - return; + return 0; break; + case HDR_ERROR: + return NE_ERROR; case HDR_DONE: if (len > 0) { - do_inflate(ctx, buf, len); + return do_inflate(ctx, buf, len); } - default: - return; + break; } /* FALLTHROUGH */ @@ -334,7 +347,7 @@ static void gz_reader(void *ud, const char *buf, size_t len) zbuf = memchr(buf, '\0', len); if (zbuf == NULL) { /* not found it yet. */ - return; + return 0; } NE_DEBUG(NE_DBG_HTTP, @@ -346,26 +359,23 @@ static void gz_reader(void *ud, const char *buf, size_t len) ctx->state = NE_Z_INFLATING; if (len == 0) { /* end of string was at end of buffer. */ - return; + return 0; } /* FALLTHROUGH */ case NE_Z_INFLATING: - do_inflate(ctx, buf, len); - break; + return do_inflate(ctx, buf, len); case NE_Z_AFTER_DATA: - process_footer(ctx, (unsigned char *)buf, len); - break; + return process_footer(ctx, (unsigned char *)buf, len); } + return 0; } -int ne_decompress_destroy(ne_decompress *ctx) +void ne_decompress_destroy(ne_decompress *ctx) { - int ret; - if (ctx->zstrinit) /* inflateEnd only fails if it's passed NULL etc; ignore * return value. */ @@ -374,50 +384,7 @@ int ne_decompress_destroy(ne_decompress *ctx) if (ctx->enchdr) ne_free(ctx->enchdr); - switch (ctx->state) { - case NE_Z_BEFORE_DATA: - case NE_Z_PASSTHROUGH: - case NE_Z_FINISHED: - ret = NE_OK; - break; - case NE_Z_ERROR: - /* session error already set. */ - ret = NE_ERROR; - break; - default: - /* truncated response. */ - ne_set_error(ctx->session, "Compressed response was truncated"); - ret = NE_ERROR; - break; - } - ne_free(ctx); - return ret; -} - -/* Prepare for a compressed response */ -static void gz_pre_send(ne_request *r, void *ud, ne_buffer *req) -{ - ne_decompress *ctx = ud; - - NE_DEBUG(NE_DBG_HTTP, "compress: Initialization.\n"); - - /* (Re-)Initialize the context */ - ctx->state = NE_Z_BEFORE_DATA; - if (ctx->zstrinit) inflateEnd(&ctx->zstr); - ctx->zstrinit = 0; - ctx->incount = ctx->footcount = 0; - ctx->checksum = crc32(0L, Z_NULL, 0); - if (ctx->enchdr) { - ne_free(ctx->enchdr); - ctx->enchdr = NULL; - } -} - -/* Kill the pre-send hook */ -static void gz_destroy(ne_request *req, void *userdata) -{ - ne_kill_pre_send(ne_get_session(req), gz_pre_send, userdata); } /* Wrapper for user-passed acceptor function. */ @@ -439,18 +406,18 @@ ne_decompress *ne_decompress_reader(ne_request *req, ne_accept_response acpt, ne_add_response_body_reader(req, gz_acceptor, gz_reader, ctx); + ctx->state = NE_Z_BEFORE_DATA; ctx->reader = rdr; ctx->userdata = userdata; ctx->session = ne_get_session(req); + /* initialize the checksum. */ + ctx->checksum = crc32(0L, Z_NULL, 0); ctx->acceptor = acpt; - ne_hook_pre_send(ctx->session, gz_pre_send, ctx); - ne_hook_destroy_request(ctx->session, gz_destroy, ctx); - return ctx; } -#else /* !NEON_ZLIB */ +#else /* !NE_HAVE_ZLIB */ /* Pass-through interface present to provide ABI compatibility. */ @@ -467,4 +434,4 @@ int ne_decompress_destroy(ne_decompress *dc) return 0; } -#endif /* NEON_ZLIB */ +#endif /* NE_HAVE_ZLIB */ diff --git a/neon/src/ne_compress.h b/neon/src/ne_compress.h index 9fa6b73bb..7b19687d3 100644 --- a/neon/src/ne_compress.h +++ b/neon/src/ne_compress.h @@ -1,6 +1,6 @@ /* - Compressed HTPT request/response Handling - Copyright (C) 2001, Joe Orton <joe@manyfish.co.uk> + Compressed HTTP response handling + Copyright (C) 2001-2004, Joe Orton <joe@manyfish.co.uk> This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public @@ -33,12 +33,12 @@ typedef struct ne_decompress_s ne_decompress; * * Returns pointer to context object which must be passed to * ne_decompress_destroy after the request has been dispatched, to - * free any internal state. */ + * free any internal state. If an error occurs during decompression, + * the request will be aborted and session error string set. */ ne_decompress *ne_decompress_reader(ne_request *req, ne_accept_response accpt, ne_block_reader rdr, void *userdata); -/* Free's up internal state. Returns non-zero if errors occured during - * decompression: the session error string will have the error. */ -int ne_decompress_destroy(ne_decompress *ctx); +/* Destroys decompression state. */ +void ne_decompress_destroy(ne_decompress *ctx); #endif /* NE_COMPRESS_H */ diff --git a/neon/src/ne_dates.c b/neon/src/ne_dates.c index 27c35175d..1e4921acf 100644 --- a/neon/src/ne_dates.c +++ b/neon/src/ne_dates.c @@ -1,6 +1,7 @@ /* Date manipulation routines Copyright (C) 1999-2004, Joe Orton <joe@manyfish.co.uk> + Copyright (C) 2004 Jiang Lei <tristone@deluxe.ocn.ne.jp> This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public @@ -35,7 +36,7 @@ #include "ne_alloc.h" #include "ne_dates.h" -#include "ne_utils.h" +#include "ne_string.h" /* Generic date manipulation routines. */ @@ -61,11 +62,35 @@ static const char *short_months[12] = { #if defined(HAVE_STRUCT_TM_TM_GMTOFF) #define GMTOFF(t) ((t).tm_gmtoff) +#elif defined(HAVE_STRUCT_TM___TM_GMTOFF) +#define GMTOFF(t) ((t).__tm_gmtoff) +#elif defined(WIN32) +#define GMTOFF(t) (gmt_to_local_win32()) #else /* FIXME: work out the offset anyway. */ #define GMTOFF(t) (0) #endif +#ifdef WIN32 +time_t gmt_to_local_win32(void) +{ + TIME_ZONE_INFORMATION tzinfo; + DWORD dwStandardDaylight; + long bias; + + dwStandardDaylight = GetTimeZoneInformation(&tzinfo); + bias = tzinfo.Bias; + + if (dwStandardDaylight == TIME_ZONE_ID_STANDARD) + bias += tzinfo.StandardBias; + + if (dwStandardDaylight == TIME_ZONE_ID_DAYLIGHT) + bias += tzinfo.DaylightBias; + + return (- bias * 60); +} +#endif + /* Returns the time/date GMT, in RFC1123-type format: eg * Sun, 06 Nov 1994 08:49:37 GMT. */ char *ne_rfc1123_date(time_t anytime) { diff --git a/neon/src/ne_defs.h b/neon/src/ne_defs.h index 4dacbda1a..c2bbb220e 100644 --- a/neon/src/ne_defs.h +++ b/neon/src/ne_defs.h @@ -1,6 +1,6 @@ /* Standard definitions for neon headers - Copyright (C) 2003, Joe Orton <joe@manyfish.co.uk> + Copyright (C) 2003-2004, Joe Orton <joe@manyfish.co.uk> This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public @@ -35,7 +35,13 @@ #endif #ifdef __GNUC__ +#if __GNUC__ >= 3 +#define ne_attribute_malloc __attribute__((malloc)) +#else +#define ne_attribute_malloc +#endif #define ne_attribute(x) __attribute__(x) #else #define ne_attribute(x) +#define ne_attribute_malloc #endif diff --git a/neon/src/ne_gnutls.c b/neon/src/ne_gnutls.c new file mode 100644 index 000000000..b6f3b4366 --- /dev/null +++ b/neon/src/ne_gnutls.c @@ -0,0 +1,853 @@ +/* + neon SSL/TLS support using GNU TLS + Copyright (C) 2002-2004, Joe Orton <joe@manyfish.co.uk> + Copyright (C) 2004, Aleix Conchillo Flaque <aleix@member.fsf.org> + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public + License along with this library; if not, write to the Free + Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, + MA 02111-1307, USA + +*/ + +#include "config.h" + +#include <sys/mman.h> +#include <sys/stat.h> +#include <sys/types.h> + +#ifdef HAVE_STRING_H +#include <string.h> +#endif + +#include <stdio.h> +#include <unistd.h> +#include <stdlib.h> + +#include <gnutls/gnutls.h> +#include <gnutls/pkcs12.h> + +#include "ne_ssl.h" +#include "ne_string.h" +#include "ne_session.h" +#include "ne_i18n.h" + +#include "ne_private.h" +#include "ne_privssl.h" + +struct ne_ssl_dname_s { + int subject; /* non-zero if this is the subject DN object */ + gnutls_x509_crt cert; +}; + +struct ne_ssl_certificate_s { + ne_ssl_dname subj_dn, issuer_dn; + gnutls_x509_crt subject; + ne_ssl_certificate *issuer; + char *identity; +}; + +struct ne_ssl_client_cert_s { + gnutls_pkcs12 p12; + int decrypted; /* non-zero if successfully decrypted. */ + ne_ssl_certificate cert; + gnutls_x509_privkey pkey; + char *friendly_name; +}; + +/* Returns the highest used index in subject (or issuer) DN of + * certificate CERT for OID, or -1 if no RDNs are present in the DN + * using that OID. */ +static int oid_find_highest_index(gnutls_x509_crt cert, int subject, const char *oid) +{ + int ret, idx = -1; + + do { + size_t len = 0; + + if (subject) + ret = gnutls_x509_crt_get_dn_by_oid(cert, oid, ++idx, 0, + NULL, &len); + else + ret = gnutls_x509_crt_get_issuer_dn_by_oid(cert, oid, ++idx, 0, + NULL, &len); + } while (ret == GNUTLS_E_SHORT_MEMORY_BUFFER); + + return idx - 1; +} + +/* Appends the value of RDN with given oid from certitifcate x5 + * subject (if subject is non-zero), or issuer DN to buffer 'buf': */ +static void append_rdn(ne_buffer *buf, gnutls_x509_crt x5, int subject, const char *oid) +{ + int idx, top, ret; + char rdn[50]; + + top = oid_find_highest_index(x5, subject, oid); + + for (idx = top; idx >= 0; idx--) { + size_t rdnlen = sizeof rdn; + + if (subject) + ret = gnutls_x509_crt_get_dn_by_oid(x5, oid, idx, 0, rdn, &rdnlen); + else + ret = gnutls_x509_crt_get_issuer_dn_by_oid(x5, oid, idx, 0, rdn, &rdnlen); + + if (ret < 0) + return; + + if (buf->used > 1) { + ne_buffer_append(buf, ", ", 2); + } + + ne_buffer_append(buf, rdn, rdnlen); + } +} + + +char *ne_ssl_readable_dname(const ne_ssl_dname *name) +{ + ne_buffer *buf = ne_buffer_create(); +#if 0 + /* this code can be used once there is a released version of GnuTLS + * with fixed _get_dn_oid functions */ + int ret, idx = 0; + + do { + char oid[32] = {0}; + size_t oidlen = sizeof oid; + + ret = name->subject + ? gnutls_x509_crt_get_dn_oid(name->cert, idx, oid, &oidlen) + : gnutls_x509_crt_get_issuer_dn_oid(name->cert, idx, oid, &oidlen); + + if (ret == 0) { + append_rdn(buf, name->cert, name->subject, oid); + idx++; + } + } while (ret != GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE); +#else + +#define APPEND_RDN(x) append_rdn(buf, name->cert, name->subject, GNUTLS_OID_##x) + + APPEND_RDN(X520_ORGANIZATIONAL_UNIT_NAME); + APPEND_RDN(X520_ORGANIZATION_NAME); + APPEND_RDN(X520_LOCALITY_NAME); + APPEND_RDN(X520_STATE_OR_PROVINCE_NAME); + APPEND_RDN(X520_COUNTRY_NAME); + + if (buf->used == 1) APPEND_RDN(X520_COMMON_NAME); + if (buf->used == 1) APPEND_RDN(PKCS9_EMAIL); + +#undef APPEND_RDN +#endif + + return ne_buffer_finish(buf); +} + +int ne_ssl_dname_cmp(const ne_ssl_dname *dn1, const ne_ssl_dname *dn2) +{ +#warning incomplete + return 1; +} + +void ne_ssl_clicert_free(ne_ssl_client_cert *cc) +{ + if (cc->p12) + gnutls_pkcs12_deinit(cc->p12); + if (cc->decrypted) { + if (cc->cert.identity) ne_free(cc->cert.identity); + if (cc->pkey) gnutls_x509_privkey_deinit(cc->pkey); + if (cc->cert.subject) gnutls_x509_crt_deinit(cc->cert.subject); + } + if (cc->friendly_name) ne_free(cc->friendly_name); + ne_free(cc); +} + +void ne_ssl_cert_validity(const ne_ssl_certificate *cert, + char *from, char *until) +{ +#warning FIXME strftime not portable + if (from) { + time_t t = gnutls_x509_crt_get_activation_time(cert->subject); + strftime(from, NE_SSL_VDATELEN, "%b %d %H:%M:%S %Y %Z", localtime(&t)); + } + if (until) { + time_t t = gnutls_x509_crt_get_expiration_time(cert->subject); + strftime(until, NE_SSL_VDATELEN, "%b %d %H:%M:%S %Y %Z", localtime(&t)); + } +} + +/* Return non-zero if hostname from certificate (cn) matches hostname + * used for session (hostname). (Wildcard matching is no longer + * mandated by RFC3280, but certs are deployed which use wildcards) */ +static int match_hostname(char *cn, const char *hostname) +{ + const char *dot; + NE_DEBUG(NE_DBG_SSL, "Match %s on %s...\n", cn, hostname); + dot = strchr(hostname, '.'); + if (dot == NULL) { + char *pnt = strchr(cn, '.'); + /* hostname is not fully-qualified; unqualify the cn. */ + if (pnt != NULL) { + *pnt = '\0'; + } + } + else if (strncmp(cn, "*.", 2) == 0) { + hostname = dot + 1; + cn += 2; + } + return !strcasecmp(cn, hostname); +} + +/* Check certificate identity. Returns zero if identity matches; 1 if + * identity does not match, or <0 if the certificate had no identity. + * If 'identity' is non-NULL, store the malloc-allocated identity in + * *identity. If 'server' is non-NULL, it must be the network address + * of the server in use, and identity must be NULL. */ +static int check_identity(const char *hostname, gnutls_x509_crt cert, + char **identity) +{ + char name[255]; + unsigned int critical; + int ret, seq = 0; + int match = 0, found = 0; + size_t len; + + do { + len = sizeof name; + ret = gnutls_x509_crt_get_subject_alt_name(cert, seq, name, &len, + &critical); + switch (ret) { + case GNUTLS_SAN_DNSNAME: + if (identity && !found) *identity = ne_strdup(name); + match = match_hostname(name, hostname); + found = 1; + break; + default: + break; + } + seq++; + } while (!match && ret != GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE); + + /* Check against the commonName if no DNS alt. names were found, + * as per RFC3280. */ + if (!found) { + seq = oid_find_highest_index(cert, 1, GNUTLS_OID_X520_COMMON_NAME); + + if (seq >= 0) { + len = sizeof name; + name[0] = '\0'; + ret = gnutls_x509_crt_get_dn_by_oid(cert, GNUTLS_OID_X520_COMMON_NAME, + seq, 0, name, &len); + if (ret == 0) { + if (identity) *identity = ne_strdup(name); + match = match_hostname(name, hostname); + } + } else { + return -1; + } + } + + NE_DEBUG(NE_DBG_SSL, "Identity match: %s\n", match ? "good" : "bad"); + return match ? 0 : 1; +} + +/* Populate an ne_ssl_certificate structure from an X509 object. */ +static ne_ssl_certificate *populate_cert(ne_ssl_certificate *cert, + gnutls_x509_crt x5) +{ + cert->subj_dn.cert = x5; + cert->subj_dn.subject = 1; + cert->issuer_dn.cert = x5; + cert->issuer_dn.subject = 0; + cert->issuer = NULL; + cert->subject = x5; + cert->identity = NULL; + check_identity("", x5, &cert->identity); + return cert; +} + +/* Returns a copy certificate of certificate SRC. */ +static gnutls_x509_crt x509_crt_copy(gnutls_x509_crt src) +{ + int ret; + size_t size; + gnutls_datum tmp; + gnutls_x509_crt dest; + + if (gnutls_x509_crt_init(&dest) == 0) { + return NULL; + } + + if (gnutls_x509_crt_export(src, GNUTLS_X509_FMT_DER, NULL, &size) + != GNUTLS_E_SHORT_MEMORY_BUFFER) { + gnutls_x509_crt_deinit(dest); + return NULL; + } + + tmp.data = ne_malloc(size); + ret = gnutls_x509_crt_export(src, GNUTLS_X509_FMT_DER, tmp.data, &size); + if (ret == 0) { + tmp.size = size; + ret = gnutls_x509_crt_import(dest, &tmp, GNUTLS_X509_FMT_DER); + } + + if (ret) { + gnutls_x509_crt_deinit(dest); + dest = NULL; + } + + ne_free(tmp.data); + return dest; +} + +/* Duplicate a client certificate, which must be in the decrypted state. */ +static ne_ssl_client_cert *dup_client_cert(const ne_ssl_client_cert *cc) +{ + int ret; + ne_ssl_client_cert *newcc = ne_calloc(sizeof *newcc); + + newcc->decrypted = 1; + + ret = gnutls_x509_privkey_init(&newcc->pkey); + if (ret != 0) goto dup_error; + + ret = gnutls_x509_privkey_cpy(newcc->pkey, cc->pkey); + if (ret != 0) goto dup_error; + + newcc->cert.subject = x509_crt_copy(cc->cert.subject); + if (!newcc->cert.subject) goto dup_error; + + if (cc->friendly_name) newcc->friendly_name = ne_strdup(cc->friendly_name); + + populate_cert(&newcc->cert, newcc->cert.subject); + return newcc; + +dup_error: + if (newcc->pkey) gnutls_x509_privkey_deinit(newcc->pkey); + if (newcc->cert.subject) gnutls_x509_crt_deinit(newcc->cert.subject); + ne_free(newcc); + return NULL; +} + +void ne_ssl_set_clicert(ne_session *sess, const ne_ssl_client_cert *cc) +{ + sess->client_cert = dup_client_cert(cc); +} + +ne_ssl_context *ne_ssl_context_create(int flags) +{ + ne_ssl_context *ctx = ne_malloc(sizeof *ctx); + gnutls_certificate_allocate_credentials(&ctx->cred); + return ctx; +} + +int ne_ssl_context_keypair(ne_ssl_context *ctx, + const char *cert, const char *key) +{ + gnutls_certificate_set_x509_key_file(ctx->cred, cert, key, + GNUTLS_X509_FMT_PEM); + return 0; +} + +int ne_ssl_context_set_verify(ne_ssl_context *ctx, int required, + const char *ca_names, const char *verify_cas) +{ + if (verify_cas) { + gnutls_certificate_set_x509_trust_file(ctx->cred, verify_cas, + GNUTLS_X509_FMT_PEM); + } +#warning argh + return 0; +} + + +void ne_ssl_context_destroy(ne_ssl_context *ctx) +{ + gnutls_certificate_free_credentials(ctx->cred); + ne_free(ctx); +} + +/* Return the certificate chain sent by the peer, or NULL on error. */ +static ne_ssl_certificate *make_peers_chain(gnutls_session sock) +{ + ne_ssl_certificate *current = NULL, *top = NULL; + const gnutls_datum *certs; + unsigned int n, count; + + certs = gnutls_certificate_get_peers(sock, &count); + if (!certs) { + return NULL; + } + + for (n = 0; n < count; n++) { + ne_ssl_certificate *cert = ne_malloc(sizeof *cert); + gnutls_x509_crt x5; + + if (gnutls_x509_crt_init(&x5) || + gnutls_x509_crt_import(x5, &certs[n], GNUTLS_X509_FMT_DER)) { + /* leak! */ + return NULL; + } + + populate_cert(cert, x5); + + if (top == NULL) { + current = top = cert; + } else { + current->issuer = cert; + current = cert; + } + } + + return top; +} + +/* Verifies an SSL server certificate. */ +static int check_certificate(ne_session *sess, gnutls_session sock, + ne_ssl_certificate *chain) +{ + time_t before, after, now = time(NULL); + int ret, failures = 0; + + before = gnutls_x509_crt_get_activation_time(chain->subject); + after = gnutls_x509_crt_get_expiration_time(chain->subject); + + if (now < before) + failures |= NE_SSL_NOTYETVALID; + else if (now > after) + failures |= NE_SSL_EXPIRED; + + ret = check_identity(sess->server.hostname, chain->subject, NULL); + if (ret < 0) { + ne_set_error(sess, _("Server certificate was missing commonName " + "attribute in subject name")); + return NE_ERROR; + } else if (ret > 0) { + failures |= NE_SSL_IDMISMATCH; + } + + if (gnutls_certificate_verify_peers(sock)) { + failures |= NE_SSL_UNTRUSTED; + } + + NE_DEBUG(NE_DBG_SSL, "Failures = %d\n", failures); + + if (failures == 0) { + ret = NE_OK; + } else { +#warning TODO: set up error string + ret = NE_ERROR; + if (sess->ssl_verify_fn + && sess->ssl_verify_fn(sess->ssl_verify_ud, failures, chain) == 0) + ret = NE_OK; + } + + return ret; +} + +/* Negotiate an SSL connection. */ +int ne__negotiate_ssl(ne_request *req) +{ + ne_session *const sess = ne_get_session(req); + ne_ssl_context *const ctx = sess->ssl_context; + ne_ssl_certificate *chain; + gnutls_session sock; + + NE_DEBUG(NE_DBG_SSL, "Negotiating SSL connection.\n"); + + if (ne_sock_connect_ssl(sess->socket, ctx)) { + ne_set_error(sess, _("SSL negotiation failed: %s"), + ne_sock_error(sess->socket)); + return NE_ERROR; + } + + sock = ne__sock_sslsock(sess->socket); + + chain = make_peers_chain(sock); + if (chain == NULL) { + ne_set_error(sess, _("Server did not send certificate chain")); + return NE_ERROR; + } + + if (check_certificate(sess, sock, chain)) { + ne_ssl_cert_free(chain); + return NE_ERROR; + } + + return NE_OK; +} + +const ne_ssl_dname *ne_ssl_cert_issuer(const ne_ssl_certificate *cert) +{ + return &cert->issuer_dn; +} + +const ne_ssl_dname *ne_ssl_cert_subject(const ne_ssl_certificate *cert) +{ + return &cert->subj_dn; +} + +const ne_ssl_certificate *ne_ssl_cert_signedby(const ne_ssl_certificate *cert) +{ + return cert->issuer; +} + +const char *ne_ssl_cert_identity(const ne_ssl_certificate *cert) +{ + return cert->identity; +} + +void ne_ssl_context_trustcert(ne_ssl_context *ctx, const ne_ssl_certificate *cert) +{ + gnutls_x509_crt certs = cert->subject; + gnutls_certificate_set_x509_trust(ctx->cred, &certs, 1); +} + +void ne_ssl_trust_default_ca(ne_session *sess) +{ +#warning incomplete +} + +/* Read the contents of file FILENAME into *DATUM. */ +static int read_to_datum(const char *filename, gnutls_datum *datum) +{ + FILE *f = fopen(filename, "r"); + ne_buffer *buf; + char tmp[4192]; + size_t len; + + if (!f) { + return -1; + } + + buf = ne_buffer_ncreate(8192); + while ((len = fread(tmp, 1, sizeof tmp, f)) > 0) { + ne_buffer_append(buf, tmp, len); + } + + if (!feof(f)) { + ne_buffer_destroy(buf); + return -1; + } + + datum->size = ne_buffer_size(buf); + datum->data = ne_buffer_finish(buf); + return 0; +} + +/* Parses a PKCS#12 structure and loads the certificate, private key + * and friendly name if possible. Returns zero on success, non-zero + * on error. */ +static int pkcs12_parse(gnutls_pkcs12 p12, gnutls_x509_privkey *pkey, + gnutls_x509_crt *x5, char **friendly_name, + const char *password) +{ + gnutls_pkcs12_bag bag = NULL; + int i, j, ret = 0; + + for (i = 0; ret == 0; ++i) { + if (bag) gnutls_pkcs12_bag_deinit(bag); + + ret = gnutls_pkcs12_bag_init(&bag); + if (ret < 0) continue; + + ret = gnutls_pkcs12_get_bag(p12, i, bag); + if (ret < 0) continue; + + gnutls_pkcs12_bag_decrypt(bag, password == NULL ? "" : password); + + for (j = 0; ret == 0 && j < gnutls_pkcs12_bag_get_count(bag); ++j) { + gnutls_pkcs12_bag_type type; + gnutls_datum data; + + if (friendly_name && *friendly_name == NULL) { + char *name; + gnutls_pkcs12_bag_get_friendly_name(bag, j, &name); + if (name) { + if (name[0] == '.') name++; /* weird GnuTLS bug? */ + *friendly_name = ne_strdup(name); + } + } + + type = gnutls_pkcs12_bag_get_type(bag, j); + switch (type) { + case GNUTLS_BAG_PKCS8_KEY: + case GNUTLS_BAG_PKCS8_ENCRYPTED_KEY: + gnutls_x509_privkey_init(pkey); + + ret = gnutls_pkcs12_bag_get_data(bag, j, &data); + if (ret < 0) continue; + + ret = gnutls_x509_privkey_import_pkcs8(*pkey, &data, + GNUTLS_X509_FMT_DER, + password, + 0); + if (ret < 0) continue; + break; + case GNUTLS_BAG_CERTIFICATE: + gnutls_x509_crt_init(x5); + + ret = gnutls_pkcs12_bag_get_data(bag, j, &data); + if (ret < 0) continue; + + ret = gnutls_x509_crt_import(*x5, &data, GNUTLS_X509_FMT_DER); + if (ret < 0) continue; + + break; + default: + break; + } + } + } + + /* Make sure last bag is freed */ + if (bag) gnutls_pkcs12_bag_deinit(bag); + + /* Free in case of error */ + if (ret < 0 && ret != GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE) { + if (*x5) gnutls_x509_crt_deinit(*x5); + if (*pkey) gnutls_x509_privkey_deinit(*pkey); + if (friendly_name && *friendly_name) ne_free(*friendly_name); + } + + if (ret == GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE) ret = 0; + return ret; +} + +ne_ssl_client_cert *ne_ssl_clicert_read(const char *filename) +{ + int ret; + gnutls_datum data; + gnutls_pkcs12 p12; + ne_ssl_client_cert *cc; + char *friendly_name = NULL; + gnutls_x509_crt cert = NULL; + gnutls_x509_privkey pkey = NULL; + + if (read_to_datum(filename, &data)) + return NULL; + + if (gnutls_pkcs12_init(&p12) != 0) { + return NULL; + } + + ret = gnutls_pkcs12_import(p12, &data, GNUTLS_X509_FMT_DER, 0); + ne_free(data.data); + if (ret < 0) { + gnutls_pkcs12_deinit(p12); + return NULL; + } + + ret = pkcs12_parse(p12, &pkey, &cert, &friendly_name, NULL); + + if (gnutls_pkcs12_verify_mac(p12, "") == 0) { + cc = ne_calloc(sizeof *cc); + cc->pkey = pkey; + cc->decrypted = 1; + cc->friendly_name = friendly_name; + populate_cert(&cc->cert, cert); + gnutls_pkcs12_deinit(p12); + cc->p12 = NULL; + return cc; + } else { + if (ret == 0) { + cc = ne_calloc(sizeof *cc); + cc->friendly_name = friendly_name; + cc->p12 = p12; + return cc; + } else { + gnutls_pkcs12_deinit(p12); + return NULL; + } + } +} + +int ne_ssl_clicert_encrypted(const ne_ssl_client_cert *cc) +{ + return !cc->decrypted; +} + +int ne_ssl_clicert_decrypt(ne_ssl_client_cert *cc, const char *password) +{ + int ret; + gnutls_x509_crt cert = NULL; + gnutls_x509_privkey pkey = NULL; + + if (gnutls_pkcs12_verify_mac(cc->p12, password) != 0) { + return -1; + } + + ret = pkcs12_parse(cc->p12, &pkey, &cert, NULL, password); + if (ret < 0) + return ret; + + gnutls_pkcs12_deinit(cc->p12); + populate_cert(&cc->cert, cert); + cc->pkey = pkey; + cc->decrypted = 1; + cc->p12 = NULL; + return 0; +} + +const ne_ssl_certificate *ne_ssl_clicert_owner(const ne_ssl_client_cert *cc) +{ + return &cc->cert; +} + +const char *ne_ssl_clicert_name(ne_ssl_client_cert *ccert) +{ + return ccert->friendly_name; +} + +ne_ssl_certificate *ne_ssl_cert_read(const char *filename) +{ + int ret; + gnutls_datum data; + gnutls_x509_crt x5; + + if (read_to_datum(filename, &data)) + return NULL; + + if (gnutls_x509_crt_init(&x5) != 0) + return NULL; + + ret = gnutls_x509_crt_import(x5, &data, GNUTLS_X509_FMT_PEM); + ne_free(data.data); + if (ret < 0) { + gnutls_x509_crt_deinit(x5); + return NULL; + } + + return populate_cert(ne_calloc(sizeof(struct ne_ssl_certificate_s)), x5); +} + +int ne_ssl_cert_write(const ne_ssl_certificate *cert, const char *filename) +{ + unsigned char buffer[10*1024]; + int len = sizeof buffer; + + FILE *fp = fopen(filename, "w"); + + if (fp == NULL) return -1; + + if (gnutls_x509_crt_export(cert->subject, GNUTLS_X509_FMT_PEM, buffer, + &len) < 0) { + fclose(fp); + return -1; + } + + if (fwrite(buffer, len, 1, fp) != 1) { + fclose(fp); + return -1; + } + + if (fclose(fp) != 0) + return -1; + + return 0; +} + +void ne_ssl_cert_free(ne_ssl_certificate *cert) +{ + gnutls_x509_crt_deinit(cert->subject); + if (cert->identity) ne_free(cert->identity); + if (cert->issuer) ne_ssl_cert_free(cert->issuer); + ne_free(cert); +} + +int ne_ssl_cert_cmp(const ne_ssl_certificate *c1, const ne_ssl_certificate *c2) +{ + char digest1[NE_SSL_DIGESTLEN], digest2[NE_SSL_DIGESTLEN]; + + if (ne_ssl_cert_digest(c1, digest1) || ne_ssl_cert_digest(c2, digest2)) { + return -1; + } + + return strcmp(digest1, digest2); +} + +/* The certificate import/export format is the base64 encoding of the + * raw DER; PEM without the newlines and wrapping. */ + +ne_ssl_certificate *ne_ssl_cert_import(const char *data) +{ + int ret; + size_t len; + unsigned char *der; + gnutls_datum buffer = { NULL, 0 }; + gnutls_x509_crt x5; + + if (gnutls_x509_crt_init(&x5) != 0) + return NULL; + + /* decode the base64 to get the raw DER representation */ + len = ne_unbase64(data, &der); + if (len == 0) return NULL; + + buffer.data = der; + buffer.size = len; + + ret = gnutls_x509_crt_import(x5, &buffer, GNUTLS_X509_FMT_DER); + ne_free(der); + + if (ret < 0) { + gnutls_x509_crt_deinit(x5); + return NULL; + } + + return populate_cert(ne_calloc(sizeof(struct ne_ssl_certificate_s)), x5); +} + +char *ne_ssl_cert_export(const ne_ssl_certificate *cert) +{ + unsigned char *der; + size_t len = 0; + char *ret; + + /* find the length of the DER encoding. */ + if (gnutls_x509_crt_export(cert->subject, GNUTLS_X509_FMT_DER, NULL, &len) != + GNUTLS_E_SHORT_MEMORY_BUFFER) { + return NULL; + } + + der = ne_malloc(len); + if (gnutls_x509_crt_export(cert->subject, GNUTLS_X509_FMT_DER, der, &len)) { + ne_free(der); + return NULL; + } + + ret = ne_base64(der, len); + ne_free(der); + return ret; +} + +int ne_ssl_cert_digest(const ne_ssl_certificate *cert, char *digest) +{ + int j, len = 20; + char sha1[20], *p; + + if (gnutls_x509_crt_get_fingerprint(cert->subject, GNUTLS_DIG_SHA, + sha1, &len) < 0) + return -1; + + for (j = 0, p = digest; j < 20; j++) { + *p++ = NE_HEX2ASC((sha1[j] >> 4) & 0x0f); + *p++ = NE_HEX2ASC(sha1[j] & 0x0f); + *p++ = ':'; + } + + *--p = '\0'; + return 0; +} diff --git a/neon/src/ne_locks.c b/neon/src/ne_locks.c index 8863bd4f4..7fc25a4d0 100644 --- a/neon/src/ne_locks.c +++ b/neon/src/ne_locks.c @@ -725,7 +725,7 @@ int ne_lock(ne_session *sess, struct ne_lock *lock) ne_buffer_destroy(body); ne_buffer_destroy(ctx.cdata); - parse_failed = !ne_xml_valid(parser); + parse_failed = ne_xml_failed(parser); if (ret == NE_OK && ne_get_status(req)->klass == 2) { if (ctx.token == NULL) { @@ -780,9 +780,13 @@ int ne_lock_refresh(ne_session *sess, struct ne_lock *lock) ne_request *req = ne_request_create(sess, "LOCK", lock->uri.path); ne_xml_parser *parser = ne_xml_create(); int ret, parse_failed; + struct lock_ctx ctx; + + memset(&ctx, 0, sizeof ctx); + ctx.cdata = ne_buffer_create(); /* Handle the response and update *lock appropriately. */ - ne_xml_push_handler(parser, lk_startelm, NULL, lk_endelm, lock); + ne_xml_push_handler(parser, lk_startelm, lk_cdata, lk_endelm, &ctx); ne_add_response_body_reader(req, ne_accept_2xx, ne_xml_parse_v, parser); @@ -797,7 +801,7 @@ int ne_lock_refresh(ne_session *sess, struct ne_lock *lock) ret = ne_request_dispatch(req); - parse_failed = !ne_xml_valid(parser); + parse_failed = ne_xml_failed(parser); if (ret == NE_OK && ne_get_status(req)->klass == 2) { if (parse_failed) { @@ -812,6 +816,7 @@ int ne_lock_refresh(ne_session *sess, struct ne_lock *lock) ret = NE_ERROR; } + ne_buffer_destroy(ctx.cdata); ne_request_destroy(req); ne_xml_destroy(parser); diff --git a/neon/src/ne_openssl.c b/neon/src/ne_openssl.c index a19d3d4aa..99ee32811 100644 --- a/neon/src/ne_openssl.c +++ b/neon/src/ne_openssl.c @@ -218,10 +218,8 @@ static int match_hostname(char *cn, const char *hostname) /* Check certificate identity. Returns zero if identity matches; 1 if * identity does not match, or <0 if the certificate had no identity. * If 'identity' is non-NULL, store the malloc-allocated identity in - * *identity. If 'server' is non-NULL, it must be the network address - * of the server in use, and identity must be NULL. */ -static int check_identity(const char *hostname, X509 *cert, char **identity, - const ne_inet_addr *server) + * *identity. */ +static int check_identity(const char *hostname, X509 *cert, char **identity) { STACK_OF(GENERAL_NAME) *names; int match = 0, found = 0; @@ -241,7 +239,7 @@ static int check_identity(const char *hostname, X509 *cert, char **identity, match = match_hostname(name, hostname); ne_free(name); found = 1; - } else if (nm->type == GEN_IPADD && server) { + } else if (nm->type == GEN_IPADD) { /* compare IP address with server IP address. */ ne_inet_addr *ia; if (nm->d.ip->length == 4) @@ -252,7 +250,10 @@ static int check_identity(const char *hostname, X509 *cert, char **identity, ia = NULL; /* ne_iaddr_make returns NULL if address type is unsupported */ if (ia != NULL) { /* address type was supported. */ - match = ne_iaddr_cmp(server, ia) == 0; + char buf[128]; + + match = strcmp(hostname, + ne_iaddr_print(ia, buf, sizeof buf)) == 0; found = 1; ne_iaddr_free(ia); } else { @@ -295,7 +296,8 @@ static int check_identity(const char *hostname, X509 *cert, char **identity, ne_free(name); } - NE_DEBUG(NE_DBG_SSL, "Identity match: %s\n", match ? "good" : "bad"); + NE_DEBUG(NE_DBG_SSL, "Identity match for '%s': %s\n", hostname, + match ? "good" : "bad"); return match ? 0 : 1; } @@ -308,7 +310,7 @@ static ne_ssl_certificate *populate_cert(ne_ssl_certificate *cert, X509 *x5) cert->subject = x5; /* Retrieve the cert identity; pass a dummy hostname to match. */ cert->identity = NULL; - check_identity("", x5, &cert->identity, NULL); + check_identity("", x5, &cert->identity); return cert; } @@ -323,7 +325,7 @@ static ne_ssl_certificate *make_chain(STACK_OF(X509) *chain) for (n = 0; n < count; n++) { ne_ssl_certificate *cert = ne_malloc(sizeof *cert); populate_cert(cert, X509_dup(sk_X509_value(chain, n))); -#if NE_DEBUGGING +#ifdef NE_DEBUGGING if (ne_debug_mask & NE_DBG_SSL) { fprintf(ne_debug_stream, "Cert #%d:\n", n); X509_print_fp(ne_debug_stream, cert->subject); @@ -357,8 +359,7 @@ static int check_certificate(ne_session *sess, SSL *ssl, ne_ssl_certificate *cha /* Check certificate was issued to this server; pass network * address of server if a proxy is not in use. */ - ret = check_identity(sess->server.hostname, cert, NULL, - sess->use_proxy ? NULL : sess->server.current); + ret = check_identity(sess->server.hostname, cert, NULL); if (ret < 0) { ne_set_error(sess, _("Server certificate was missing commonName " "attribute in subject name")); @@ -481,18 +482,56 @@ void ne_ssl_set_clicert(ne_session *sess, const ne_ssl_client_cert *cc) sess->client_cert = dup_client_cert(cc); } -ne_ssl_context *ne_ssl_context_create(void) +ne_ssl_context *ne_ssl_context_create(int mode) { - ne_ssl_context *ctx = ne_malloc(sizeof *ctx); - ctx->ctx = SSL_CTX_new(SSLv23_client_method()); - ctx->sess = NULL; - /* set client cert callback. */ - SSL_CTX_set_client_cert_cb(ctx->ctx, provide_client_cert); - /* enable workarounds for buggy SSL server implementations */ - SSL_CTX_set_options(ctx->ctx, SSL_OP_ALL); + ne_ssl_context *ctx = ne_calloc(sizeof *ctx); + if (mode == NE_SSL_CTX_CLIENT) { + ctx->ctx = SSL_CTX_new(SSLv23_client_method()); + ctx->sess = NULL; + /* set client cert callback. */ + SSL_CTX_set_client_cert_cb(ctx->ctx, provide_client_cert); + /* enable workarounds for buggy SSL server implementations */ + SSL_CTX_set_options(ctx->ctx, SSL_OP_ALL); + } else if (mode == NE_SSL_CTX_SERVER) { + ctx->ctx = SSL_CTX_new(SSLv23_server_method()); + } else { + ctx->ctx = SSL_CTX_new(SSLv2_server_method()); + } return ctx; } +int ne_ssl_context_keypair(ne_ssl_context *ctx, const char *cert, + const char *key) +{ + int ret; + + ret = SSL_CTX_use_PrivateKey_file(ctx->ctx, key, SSL_FILETYPE_PEM); + if (ret == 1) { + ret = SSL_CTX_use_certificate_file(ctx->ctx, cert, SSL_FILETYPE_PEM); + } + + return ret == 1 ? 0 : -1; +} + +int ne_ssl_context_set_verify(ne_ssl_context *ctx, + int required, + const char *ca_names, + const char *verify_cas) +{ + if (required) { + SSL_CTX_set_verify(ctx->ctx, SSL_VERIFY_PEER | + SSL_VERIFY_FAIL_IF_NO_PEER_CERT, NULL); + } + if (ca_names) { + SSL_CTX_set_client_CA_list(ctx->ctx, + SSL_load_client_CA_file(ca_names)); + } + if (verify_cas) { + SSL_CTX_load_verify_locations(ctx->ctx, verify_cas, NULL); + } + return 0; +} + void ne_ssl_context_destroy(ne_ssl_context *ctx) { SSL_CTX_free(ctx->ctx); @@ -502,11 +541,11 @@ void ne_ssl_context_destroy(ne_ssl_context *ctx) } /* For internal use only. */ -int ne_negotiate_ssl(ne_request *req) +int ne__negotiate_ssl(ne_request *req) { ne_session *sess = ne_get_session(req); ne_ssl_context *ctx = sess->ssl_context; - ne_ssl_socket *sock; + SSL *ssl; STACK_OF(X509) *chain; int freechain = 0; /* non-zero if chain should be free'd. */ @@ -527,12 +566,12 @@ int ne_negotiate_ssl(ne_request *req) return NE_ERROR; } - sock = ne_sock_sslsock(sess->socket); + ssl = ne__sock_sslsock(sess->socket); - chain = SSL_get_peer_cert_chain(sock->ssl); + chain = SSL_get_peer_cert_chain(ssl); /* For an SSLv2 connection, the cert chain will always be NULL. */ if (chain == NULL) { - X509 *cert = SSL_get_peer_certificate(sock->ssl); + X509 *cert = SSL_get_peer_certificate(ssl); if (cert) { chain = sk_X509_new_null(); sk_X509_push(chain, cert); @@ -561,7 +600,7 @@ int ne_negotiate_ssl(ne_request *req) if (freechain) sk_X509_free(chain); /* no longer need the chain */ - if (check_certificate(sess, sock->ssl, cert)) { + if (check_certificate(sess, ssl, cert)) { NE_DEBUG(NE_DBG_SSL, "SSL certificate checks failed: %s\n", sess->error); ne_ssl_cert_free(cert); @@ -573,12 +612,12 @@ int ne_negotiate_ssl(ne_request *req) if (!ctx->sess) { /* store the session. */ - ctx->sess = SSL_get1_session(sock->ssl); + ctx->sess = SSL_get1_session(ssl); } if (sess->notify_cb) { sess->notify_cb(sess->notify_ud, ne_conn_secure, - SSL_get_version(sock->ssl)); + SSL_get_version(ssl)); } return NE_OK; @@ -604,7 +643,7 @@ const char *ne_ssl_cert_identity(const ne_ssl_certificate *cert) return cert->identity; } -void ne_ssl_ctx_trustcert(ne_ssl_context *ctx, const ne_ssl_certificate *cert) +void ne_ssl_context_trustcert(ne_ssl_context *ctx, const ne_ssl_certificate *cert) { X509_STORE *store = SSL_CTX_get_cert_store(ctx->ctx); diff --git a/neon/src/ne_private.h b/neon/src/ne_private.h index 49f9d6445..b5d952990 100644 --- a/neon/src/ne_private.h +++ b/neon/src/ne_private.h @@ -67,14 +67,16 @@ struct ne_session_s { char *scheme; struct host_info server, proxy; + /* application-provided address list */ + const ne_inet_addr **addrlist; + size_t numaddrs, curaddr; + /* Settings */ unsigned int use_proxy:1; /* do we have a proxy server? */ unsigned int no_persist:1; /* set to disable persistent connections */ unsigned int use_ssl:1; /* whether a secure connection is required */ unsigned int in_connect:1; /* doing a proxy CONNECT */ - int expect100_works; /* known state of 100-continue support */ - ne_progress progress_cb; void *progress_ud; @@ -88,7 +90,7 @@ struct ne_session_s { char *user_agent; /* full User-Agent: header field */ -#ifdef NEON_SSL +#ifdef NE_HAVE_SSL ne_ssl_client_cert *client_cert; ne_ssl_certificate *server_cert; ne_ssl_context *ssl_context; @@ -105,17 +107,15 @@ struct ne_session_s { char error[BUFSIZ]; }; +/* Pushes block of 'count' bytes at 'buf'. Returns non-zero on + * error. */ typedef int (*ne_push_fn)(void *userdata, const char *buf, size_t count); /* Pulls the request body for the given request, passing blocks to the - * given callback. - */ -int ne_pull_request_body(ne_request *req, ne_push_fn fn, void *ud); + * given callback. */ +int ne__pull_request_body(ne_request *req, ne_push_fn fn, void *ud); /* Do the SSL negotiation. */ -int ne_negotiate_ssl(ne_request *req); - -/* 0.24.x hack to fix ne_compress layer problems */ -void ne_kill_pre_send(ne_session *sess, ne_pre_send_fn fn, void *userdata); +int ne__negotiate_ssl(ne_request *req); #endif /* HTTP_PRIVATE_H */ diff --git a/neon/src/ne_privssl.h b/neon/src/ne_privssl.h index 1f637590e..197d6f1a3 100644 --- a/neon/src/ne_privssl.h +++ b/neon/src/ne_privssl.h @@ -1,6 +1,7 @@ /* SSL interface definitions internal to neon. - Copyright (C) 2003, Joe Orton <joe@manyfish.co.uk> + Copyright (C) 2003, 2004, Joe Orton <joe@manyfish.co.uk> + Copyright (C) 2004, Aleix Conchillo Flaque <aleix@member.fsf.org> This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public @@ -25,24 +26,38 @@ #ifndef NE_PRIVSSL_H #define NE_PRIVSSL_H -/* This is the private interface between ne_socket and ne_openssl. */ +/* This is the private interface between ne_socket, ne_gnutls and + * ne_openssl. */ -#ifdef NEON_SSL +#include "ne_ssl.h" +#include "ne_socket.h" -#include <openssl/ssl.h> +#ifdef HAVE_OPENSSL -#include "ne_ssl.h" +#include <openssl/ssl.h> -/* SSL context */ struct ne_ssl_context_s { SSL_CTX *ctx; SSL_SESSION *sess; }; -struct ne_ssl_socket_s { - SSL *ssl; +typedef SSL *ne_ssl_socket; + +#endif /* HAVE_OPENSSL */ + +#ifdef HAVE_GNUTLS + +#include <gnutls/gnutls.h> + +struct ne_ssl_context_s { + gnutls_certificate_credentials cred; + /* TODO: store session here too */ }; -#endif /* NEON_SSL */ +typedef gnutls_session ne_ssl_socket; + +#endif /* HAVE_GNUTLS */ + +ne_ssl_socket ne__sock_sslsock(ne_socket *sock); -#endif +#endif /* NE_PRIVSSL_H */ diff --git a/neon/src/ne_props.c b/neon/src/ne_props.c index 057085e55..fc18a67b3 100644 --- a/neon/src/ne_props.c +++ b/neon/src/ne_props.c @@ -33,6 +33,7 @@ #include "ne_props.h" #include "ne_basic.h" #include "ne_locks.h" +#include "ne_i18n.h" /* don't store flat props with a value > 10K */ #define MAX_FLATPROP_LEN (102400) @@ -83,11 +84,12 @@ struct propstat { /* Results set. */ struct ne_prop_result_set_s { struct propstat *pstats; - int numpstats; + int numpstats, counter; void *private; char *href; }; +#define MAX_PROP_COUNTER (1024) static int startelm(void *userdata, int state, const char *name, const char *nspace, @@ -141,7 +143,7 @@ static int propfind(ne_propfind_handler *handler, if (ret == NE_OK && ne_get_status(req)->klass != 2) { ret = NE_ERROR; - } else if (!ne_xml_valid(handler->parser)) { + } else if (ne_xml_failed(handler->parser)) { ne_set_error(handler->sess, "%s", ne_xml_get_error(handler->parser)); ret = NE_ERROR; } @@ -220,7 +222,7 @@ int ne_proppatch(ne_session *sess, const char *uri, ne_set_request_body_buffer(req, body->data, ne_buffer_size(body)); ne_add_request_header(req, "Content-Type", NE_XML_MEDIA_TYPE); -#ifdef USE_DAV_LOCKS +#ifdef NE_HAVE_DAV ne_lock_using_resource(req, uri, NE_DEPTH_ZERO); #endif @@ -360,9 +362,15 @@ static void *start_response(void *userdata, const char *href) static void *start_propstat(void *userdata, void *response) { ne_prop_result_set *set = response; - int n; + ne_propfind_handler *hdl = userdata; struct propstat *pstat; + int n; + if (++hdl->current->counter == MAX_PROP_COUNTER) { + ne_xml_set_error(hdl->parser, _("Response exceeds maximum property count")); + return NULL; + } + n = set->numpstats; set->pstats = ne_realloc(set->pstats, sizeof(struct propstat) * (n+1)); set->numpstats = n+1; @@ -396,6 +404,13 @@ static int startelm(void *userdata, int parent, return ELM_flatprop; } + /* Enforce maximum number of properties per resource to prevent a + * memory exhaustion attack by a hostile server. */ + if (++hdl->current->counter == MAX_PROP_COUNTER) { + ne_xml_set_error(hdl->parser, _("Response exceeds maximum property count")); + return NE_XML_ABORT; + } + /* Add a property to this propstat */ n = pstat->numprops; diff --git a/neon/src/ne_request.c b/neon/src/ne_request.c index 7233cf378..6049a7e64 100644 --- a/neon/src/ne_request.c +++ b/neon/src/ne_request.c @@ -26,10 +26,6 @@ #include "config.h" #include <sys/types.h> -#include <sys/stat.h> -#ifdef __EMX__ -#include <sys/select.h> -#endif #ifdef HAVE_LIMITS_H #include <limits.h> /* for UINT_MAX etc */ @@ -90,6 +86,33 @@ struct body_reader { struct body_reader *next; }; +#ifdef NE_LFS +#define ne_lseek lseek64 +typedef off64_t ne_off_t; +#define FMT_NE_OFF_T NE_FMT_OFF64_T +#if defined(LONG_LONG_MAX) && !defined(LLONG_MAX) +#define LLONG_MAX LONG_LONG_MAX +#endif +#define NE_OFFT_MAX LLONG_MAX +#ifdef HAVE_STRTOLL +#define ne_strtoff strtoll +#else +#define ne_strtoff strtoq +#endif +#else /* !NE_LFS */ +typedef off_t ne_off_t; +#define ne_lseek lseek +#define FMT_NE_OFF_T NE_FMT_OFF_T +#define NE_OFFT_MAX LONG_MAX +#if SIZEOF_OFF_T > SIZEOF_LONG && defined(HAVE_STRTOLL) +#define ne_strtoff strtoll +#elif SIZEOF_OFF_T > SIZEOF_LONG && defined(HAVE_STRTOQ) +#define ne_strtoff strtoq +#else +#define ne_strtoff strtol +#endif +#endif /* NE_LFS */ + struct ne_request_s { char *method, *uri; /* method and Request-URI */ @@ -99,16 +122,23 @@ struct ne_request_s { ne_provide_body body_cb; void *body_ud; - /* Comes from either an fd or a buffer. */ + /* Request body source: file or buffer (if not callback). */ union { - int fd; + struct { + int fd; + ne_off_t offset, length; + ne_off_t remain; /* remaining bytes to send. */ + } file; struct { + /* length bytes @ buffer = whole body. + * remain bytes @ pnt = remaining bytes to send */ const char *buffer, *pnt; - size_t left; + size_t length, remain; } buf; } body; - size_t body_size, body_progress; + ne_off_t body_length; /* length of request body */ + ne_off_t body_progress; /* number of bytes of body sent so far */ /* temporary store for response lines. */ char respbuf[BUFSIZ]; @@ -117,17 +147,25 @@ struct ne_request_s { /* The transfer encoding types */ struct ne_response { - int length; /* Response entity-body content-length */ - size_t left; /* Bytes left to read */ - size_t chunk_left; /* Bytes of chunk left to read */ - size_t total; /* total bytes read so far. */ - /* how the message length is detemined: */ enum { R_TILLEOF = 0, /* read till eof */ - R_NO_BODY, /* implicitly no body (HEAD, 204, 205, 304) */ + R_NO_BODY, /* implicitly no body (HEAD, 204, 304) */ R_CHUNKED, /* using chunked transfer-encoding */ R_CLENGTH /* using given content-length */ } mode; + union { + /* clen: used if mode == R_CLENGTH; total and bytes + * remaining to be read of response body. */ + struct { + ne_off_t total, remain; + } clen; + /* chunk: used if mode == R_CHUNKED; total and bytes + * remaining to be read of current chunk */ + struct { + size_t total, remain; + } chunk; + } body; + ne_off_t progress; /* number of bytes read of response */ } resp; /* List of callbacks which are passed response headers */ @@ -316,25 +354,6 @@ void ne_hook_destroy_session(ne_session *sess, ADD_HOOK(sess->destroy_sess_hooks, fn, userdata); } -/* 0.24.x hack to fix ne_compress layer problems */ -void ne_kill_pre_send(ne_session *sess, ne_pre_send_fn fn, void *userdata) -{ - struct hook **last, *hk; - - last = &sess->pre_send_hooks; - hk = *last; - - while (hk) { - if (hk->fn == (void_fn)fn && hk->userdata == userdata) { - *last = hk->next; - ne_free(hk); - return; - } - last = &hk->next; - hk = *last; - } -} - void ne_set_session_private(ne_session *sess, const char *id, void *userdata) { add_hook(&sess->private, id, NULL, userdata); @@ -350,16 +369,16 @@ static ssize_t body_string_send(void *userdata, char *buffer, size_t count) ne_request *req = userdata; if (count == 0) { - req->body.buf.left = req->body_size; + req->body.buf.remain = req->body.buf.length; req->body.buf.pnt = req->body.buf.buffer; } else { /* if body_left == 0 we fall through and return 0. */ - if (req->body.buf.left < count) - count = req->body.buf.left; + if (req->body.buf.remain < count) + count = req->body.buf.remain; memcpy(buffer, req->body.buf.pnt, count); req->body.buf.pnt += count; - req->body.buf.left -= count; + req->body.buf.remain -= count; } return count; @@ -370,16 +389,26 @@ static ssize_t body_fd_send(void *userdata, char *buffer, size_t count) ne_request *req = userdata; if (count) { - return read(req->body.fd, buffer, count); + if (req->body.file.remain == 0) + return 0; + if ((off_t)count > req->body.file.remain) + count = req->body.file.remain; + return read(req->body.file.fd, buffer, count); } else { - /* rewind since we may have to send it again */ - return lseek(req->body.fd, SEEK_SET, 0); + /* rewind for next send. */ + if (ne_lseek(req->body.file.fd, req->body.file.offset, SEEK_SET) + == req->body.file.offset) { + req->body.file.remain = req->body.file.length; + return 0; + } else { + return -1; + } } } /* Pulls the request body from the source and pushes it to the given * callback. Returns 0 on success, or NE_* code */ -int ne_pull_request_body(ne_request *req, ne_push_fn fn, void *ud) +int ne__pull_request_body(ne_request *req, ne_push_fn fn, void *ud) { int ret = 0; char buffer[BUFSIZ]; @@ -419,7 +448,7 @@ static int send_with_progress(void *userdata, const char *data, size_t n) if (ret == 0) { req->body_progress += n; req->session->progress_cb(req->session->progress_ud, - req->body_progress, req->body_size); + req->body_progress, req->body_length); } return ret; @@ -434,10 +463,10 @@ static int send_request_body(ne_request *req) if (req->session->progress_cb) { /* with progress callbacks. */ req->body_progress = 0; - ret = ne_pull_request_body(req, send_with_progress, req); + ret = ne__pull_request_body(req, send_with_progress, req); } else { /* without progress callbacks. */ - ret = ne_pull_request_body(req, (ne_push_fn)ne_sock_fullwrite, + ret = ne__pull_request_body(req, (ne_push_fn)ne_sock_fullwrite, req->session->socket); } @@ -457,7 +486,8 @@ static void add_fixed_headers(ne_request *req) * harder to get a persistent connection, except if using a proxy * as per 2068 sec 19.7.1. Always add TE: trailers since those * are understood. */ - if (!req->session->is_http11 && !req->session->use_proxy) { + if (!req->session->no_persist && !req->session->is_http11 + && !req->session->use_proxy) { ne_buffer_zappend(req->headers, "Keep-Alive: " EOL "Connection: TE, Keep-Alive" EOL @@ -487,7 +517,8 @@ static void te_hdr_handler(void *userdata, const char *value) { struct ne_response *resp = userdata; - resp->mode = R_CHUNKED; + resp->mode = R_CHUNKED; + resp->body.chunk.remain = 0; } /* Handler for the "Connection" response header */ @@ -504,10 +535,10 @@ static void connection_hdr_handler(void *userdata, const char *value) static void clength_hdr_handler(void *userdata, const char *value) { struct ne_response *resp = userdata; - size_t len = strtoul(value, NULL, 10); - if (len != ULONG_MAX && resp->mode == R_TILLEOF) { + ne_off_t len = ne_strtoff(value, NULL, 10); + if (len != NE_OFFT_MAX && len >= 0 && resp->mode == R_TILLEOF) { resp->mode = R_CLENGTH; - resp->length = len; + resp->body.clen.total = resp->body.clen.remain = len; } } @@ -560,47 +591,66 @@ ne_request *ne_request_create(ne_session *sess, return req; } -static void set_body_size(ne_request *req, size_t size) +/* Set the request body length to 'length' */ +static void set_body_length(ne_request *req, ne_off_t length) { - req->body_size = size; - ne_print_request_header(req, "Content-Length", "%" NE_FMT_SIZE_T, size); + req->body_length = length; + ne_print_request_header(req, "Content-Length", "%" FMT_NE_OFF_T, length); } void ne_set_request_body_buffer(ne_request *req, const char *buffer, size_t size) { req->body.buf.buffer = buffer; + req->body.buf.length = size; req->body_cb = body_string_send; req->body_ud = req; - set_body_size(req, size); + set_body_length(req, size); } -void ne_set_request_body_provider(ne_request *req, size_t bodysize, +void ne_set_request_body_provider(ne_request *req, off_t bodysize, ne_provide_body provider, void *ud) { req->body_cb = provider; req->body_ud = ud; - set_body_size(req, bodysize); + set_body_length(req, bodysize); } -int ne_set_request_body_fd(ne_request *req, int fd) +void ne_set_request_body_fd(ne_request *req, int fd, + off_t offset, off_t length) { - struct stat bodyst; - - /* Get file length */ - if (fstat(fd, &bodyst) < 0) { - char err[200]; - ne_strerror(errno, err, sizeof err); - ne_set_error(req->session, _("Could not determine file length: %s"), - err); - NE_DEBUG(NE_DBG_HTTP, "Stat failed: %s\n", err); - return -1; - } - req->body.fd = fd; + req->body.file.fd = fd; + req->body.file.offset = offset; + req->body.file.length = length; req->body_cb = body_fd_send; req->body_ud = req; - set_body_size(req, bodyst.st_size); - return 0; + set_body_length(req, length); +} + +#ifdef NE_LFS +void ne_set_request_body_fd64(ne_request *req, int fd, + off64_t offset, off64_t length) +{ + req->body.file.fd = fd; + req->body.file.offset = offset; + req->body.file.length = length; + req->body_cb = body_fd_send; + req->body_ud = req; + set_body_length(req, length); +} + +void ne_set_request_body_provider64(ne_request *req, off64_t bodysize, + ne_provide_body provider, void *ud) +{ + req->body_cb = provider; + req->body_ud = ud; + set_body_length(req, bodysize); +} +#endif + +void ne_set_request_expect100(ne_request *req, int flag) +{ + req->use_expect100 = flag; } void ne_add_request_header(ne_request *req, const char *name, @@ -707,34 +757,33 @@ void ne_request_destroy(ne_request *req) } -/* Reads a block of the response into buffer, which is of size buflen. - * Returns number of bytes read, 0 on end-of-response, or NE_* on error. - * TODO?: only make one actual read() call in here... - */ +/* Reads a block of the response into BUFFER, which is of size + * *BUFLEN. Returns zero on success or non-zero on error. On + * success, *BUFLEN is updated to be the number of bytes read into + * BUFFER (which will be 0 to indicate the end of the repsonse). On + * error, the connection is closed and the session error string is + * set. */ static int read_response_block(ne_request *req, struct ne_response *resp, char *buffer, size_t *buflen) { + ne_socket *const sock = req->session->socket; size_t willread; ssize_t readlen; - ne_socket *sock = req->session->socket; + switch (resp->mode) { case R_CHUNKED: - /* We are doing a chunked transfer-encoding. - * It goes: `SIZE CRLF CHUNK CRLF SIZE CRLF CHUNK CRLF ...' - * ended by a `CHUNK CRLF 0 CRLF', a 0-sized chunk. - * The slight complication is that we have to cope with - * partial reads of chunks. - * For this reason, resp.chunk_left contains the number of - * bytes left to read in the current chunk. - */ - if (resp->chunk_left == 0) { - unsigned long int chunk_len; + /* Chunked transfer-encoding: chunk syntax is "SIZE CRLF CHUNK + * CRLF SIZE CRLF CHUNK CRLF ..." followed by zero-length + * chunk: "CHUNK CRLF 0 CRLF". resp.chunk.remain contains the + * number of bytes left to read in the current chunk. */ + if (resp->body.chunk.remain == 0) { + unsigned long chunk_len; char *ptr; - /* We are at the start of a new chunk. */ - NE_DEBUG(NE_DBG_HTTP, "New chunk.\n"); + + /* The start of a new chunk. */ SOCK_ERR(req, ne_sock_readline(sock, buffer, *buflen), _("Could not read chunk size")); - NE_DEBUG(NE_DBG_HTTP, "[Chunk Size] < %s", buffer); + NE_DEBUG(NE_DBG_HTTP, "[chunk] < %s", buffer); chunk_len = strtoul(buffer, &ptr, 16); /* limit chunk size to <= UINT_MAX, so it will probably * fit in a size_t. */ @@ -743,18 +792,14 @@ static int read_response_block(ne_request *req, struct ne_response *resp, return aborted(req, _("Could not parse chunk size"), 0); } NE_DEBUG(NE_DBG_HTTP, "Got chunk size: %lu\n", chunk_len); - if (chunk_len == 0) { - /* Zero-size chunk == end of response. */ - NE_DEBUG(NE_DBG_HTTP, "Zero-size chunk.\n"); - *buflen = 0; - return NE_OK; - } - resp->chunk_left = chunk_len; + resp->body.chunk.remain = chunk_len; } - willread = resp->chunk_left; + willread = resp->body.chunk.remain > *buflen + ? *buflen : resp->body.chunk.remain; break; case R_CLENGTH: - willread = resp->left; + willread = resp->body.clen.remain > (off_t)*buflen + ? *buflen : (size_t)resp->body.clen.remain; break; case R_TILLEOF: willread = *buflen; @@ -764,8 +809,7 @@ static int read_response_block(ne_request *req, struct ne_response *resp, willread = 0; break; } - if (willread > *buflen) willread = *buflen; - else if (willread == 0) { + if (willread == 0) { *buflen = 0; return 0; } @@ -792,8 +836,8 @@ static int read_response_block(ne_request *req, struct ne_response *resp, "Read block (%" NE_FMT_SSIZE_T " bytes):\n[%.*s]\n", readlen, (int)readlen, buffer); if (resp->mode == R_CHUNKED) { - resp->chunk_left -= readlen; - if (resp->chunk_left == 0) { + resp->body.chunk.remain -= readlen; + if (resp->body.chunk.remain == 0) { char crlfbuf[2]; /* If we've read a whole chunk, read a CRLF */ readlen = ne_sock_fullread(sock, crlfbuf, 2); @@ -804,8 +848,9 @@ static int read_response_block(ne_request *req, struct ne_response *resp, return aborted(req, _("Chunk delimiter was invalid"), 0); } } else if (resp->mode == R_CLENGTH) { - resp->left -= readlen; + resp->body.clen.remain -= readlen; } + resp->progress += readlen; return NE_OK; } @@ -813,20 +858,21 @@ ssize_t ne_read_response_block(ne_request *req, char *buffer, size_t buflen) { struct body_reader *rdr; size_t readlen = buflen; + struct ne_response *const resp = &req->resp; - if (read_response_block(req, &req->resp, buffer, &readlen)) + if (read_response_block(req, resp, buffer, &readlen)) return -1; - req->resp.total += readlen; - if (req->session->progress_cb) { - req->session->progress_cb(req->session->progress_ud, req->resp.total, - (req->resp.mode==R_CLENGTH)?req->resp.length:-1); + req->session->progress_cb(req->session->progress_ud, resp->progress, + resp->mode==R_CLENGTH ? resp->body.clen.total:-1); } - /* TODO: call the readers when this fails too. */ for (rdr = req->body_readers; rdr!=NULL; rdr=rdr->next) { - if (rdr->use) rdr->handler(rdr->userdata, buffer, readlen); + if (rdr->use && rdr->handler(rdr->userdata, buffer, readlen) != 0) { + ne_close_connection(req->session); + return -1; + } } return readlen; @@ -864,10 +910,10 @@ static ne_buffer *build_request(ne_request *req) static void dump_request(const char *request) { - if ((NE_DBG_HTTPPLAIN&ne_debug_mask) == NE_DBG_HTTPPLAIN) { + if (ne_debug_mask & NE_DBG_HTTPPLAIN) { /* Display everything mode */ NE_DEBUG(NE_DBG_HTTP, "Sending request headers:\n%s", request); - } else { + } else if (ne_debug_mask & NE_DBG_HTTP) { /* Blank out the Authorization paramaters */ char *reqdebug = ne_strdup(request), *pnt = reqdebug; while ((pnt = strstr(pnt, "Authorization: ")) != NULL) { @@ -979,7 +1025,7 @@ static int send_request(ne_request *req, const ne_buffer *request) return RETRY_RET(retry, ret, aret); } - if (!req->use_expect100 && req->body_size > 0) { + if (!req->use_expect100 && req->body_length > 0) { /* Send request body, if not using 100-continue. */ ret = send_request_body(req); if (ret < 0) { @@ -999,7 +1045,8 @@ static int send_request(ne_request *req, const ne_buffer *request) /* Discard headers with the interim response. */ if ((ret = discard_headers(req)) != NE_OK) break; - if (req->use_expect100 && (status->code == 100) && !sentbody) { + if (req->use_expect100 && (status->code == 100) + && req->body_length > 0 && !sentbody) { /* Send the body after receiving the first 100 Continue */ if ((ret = send_request_body(req)) != NE_OK) break; sentbody = 1; @@ -1135,8 +1182,12 @@ static int read_response_headers(ne_request *req) return ret; } +/* Perform any necessary DNS lookup for the host given by *info; + * return NE_ code. */ static int lookup_host(ne_session *sess, struct host_info *info) { + if (sess->addrlist) return NE_OK; + NE_DEBUG(NE_DBG_HTTP, "Doing DNS lookup on %s...\n", info->hostname); if (sess->notify_cb) sess->notify_cb(sess->notify_ud, ne_conn_namelookup, info->hostname); @@ -1169,10 +1220,6 @@ int ne_begin_request(ne_request *req) req->resp.mode = R_TILLEOF; - /* FIXME: Determine whether to use the Expect: 100-continue header. */ - req->use_expect100 = (req->session->expect100_works > -1) && - (req->body_size > HTTP_EXPECT_MINSIZE) && req->session->is_http11; - /* Build the request string, and send it */ data = build_request(req); DEBUG_DUMP_REQUEST(data->data); @@ -1197,7 +1244,7 @@ int ne_begin_request(ne_request *req) /* Read the headers */ HTTP_ERR(read_response_headers(req)); -#ifdef NEON_SSL +#ifdef NE_HAVE_SSL /* Special case for CONNECT handling: the response has no body, * and the connection can persist. */ if (req->session->in_connect && st->klass == 2) { @@ -1206,9 +1253,9 @@ int ne_begin_request(ne_request *req) } #endif - /* HEAD requests and 204, 205, 304 responses have no response body, + /* HEAD requests and 204, 304 responses have no response body, * regardless of what headers are present. */ - if (req->method_is_head || st->code==204 || st->code==205 || st->code==304) + if (req->method_is_head || st->code == 204 || st->code == 304) req->resp.mode = R_NO_BODY; /* Prepare for reading the response entity-body. Call each of the @@ -1218,9 +1265,6 @@ int ne_begin_request(ne_request *req) rdr->use = rdr->accept_response(rdr->userdata, req, st); } - req->resp.left = req->resp.length; - req->resp.chunk_left = 0; - return NE_OK; } @@ -1292,7 +1336,7 @@ ne_session *ne_get_session(const ne_request *req) return req->session; } -#ifdef NEON_SSL +#ifdef NE_HAVE_SSL /* Create a CONNECT tunnel through the proxy server. * Returns HTTP_* */ static int proxy_tunnel(ne_session *sess) @@ -1324,13 +1368,37 @@ static int proxy_tunnel(ne_session *sess) } #endif +/* Return the first resolved address for the given host. */ +static const ne_inet_addr *resolve_first(ne_session *sess, + struct host_info *host) +{ + if (sess->addrlist) { + sess->curaddr = 0; + return sess->addrlist[0]; + } else { + return ne_addr_first(host->address); + } +} + +/* Return the next resolved address for the given host or NULL if + * there are no more addresses. */ +static const ne_inet_addr *resolve_next(ne_session *sess, + struct host_info *host) +{ + if (sess->addrlist) { + if (sess->curaddr++ < sess->numaddrs) + return sess->addrlist[sess->curaddr]; + else + return NULL; + } else { + return ne_addr_next(host->address); + } +} + /* Make new TCP connection to server at 'host' of type 'name'. Note * that once a connection to a particular network address has * succeeded, that address will be used first for the next attempt to * connect. */ -/* TODO: an alternate implementation could always cycle through the - * addresses: this could ease server load, but could hurt SSL session - * caching for SSL sessions, which would increase server load. */ static int do_connect(ne_request *req, struct host_info *host, const char *err) { ne_session *const sess = req->session; @@ -1342,7 +1410,7 @@ static int do_connect(ne_request *req, struct host_info *host, const char *err) } if (host->current == NULL) - host->current = ne_addr_first(host->address); + host->current = resolve_first(sess, host); do { notify_status(sess, ne_conn_connecting, host->hostport); @@ -1355,7 +1423,7 @@ static int do_connect(ne_request *req, struct host_info *host, const char *err) #endif ret = ne_sock_connect(sess->socket, host->current, host->port); } while (ret && /* try the next address... */ - (host->current = ne_addr_next(host->address)) != NULL); + (host->current = resolve_next(sess, host)) != NULL); if (ret) { ne_set_error(sess, "%s: %s", err, ne_sock_error(sess->socket)); @@ -1363,7 +1431,7 @@ static int do_connect(ne_request *req, struct host_info *host, const char *err) return NE_CONNECT; } - notify_status(sess, ne_conn_connected, sess->proxy.hostport); + notify_status(sess, ne_conn_connected, host->hostport); if (sess->rdtimeout) ne_sock_read_timeout(sess->socket, sess->rdtimeout); @@ -1389,7 +1457,7 @@ static int open_connection(ne_request *req) if (ret != NE_OK) return ret; -#ifdef NEON_SSL +#ifdef NE_HAVE_SSL /* Negotiate SSL layer if required. */ if (sess->use_ssl && !sess->in_connect) { /* CONNECT tunnel */ @@ -1397,7 +1465,7 @@ static int open_connection(ne_request *req) ret = proxy_tunnel(sess); if (ret == NE_OK) - ret = ne_negotiate_ssl(req); + ret = ne__negotiate_ssl(req); /* This is probably only really needed for ne_negotiate_ssl * failures as proxy_tunnel will fail via aborted(). */ diff --git a/neon/src/ne_request.h b/neon/src/ne_request.h index e59b59de7..b3dadbad9 100644 --- a/neon/src/ne_request.h +++ b/neon/src/ne_request.h @@ -1,6 +1,6 @@ /* HTTP Request Handling - Copyright (C) 1999-2002, Joe Orton <joe@manyfish.co.uk> + Copyright (C) 1999-2004, Joe Orton <joe@manyfish.co.uk> This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public @@ -53,17 +53,21 @@ typedef struct ne_request_s ne_request; ne_request *ne_request_create(ne_session *sess, const char *method, const char *path); -/* 'buffer' will be sent as the request body with given request. */ +/* The request body will be taken from 'size' bytes of 'buffer'. */ void ne_set_request_body_buffer(ne_request *req, const char *buffer, size_t size); -/* Send the contents of a file as the request body; 'fd' must be a - * file descriptor of an open, seekable file. Current file offset of - * fd is not retained (and ignored: the file is read from the the - * first byte). Returns: - * 0 on okay. - * non-zero if could not determine length of file. */ -int ne_set_request_body_fd(ne_request *req, int fd); +/* The request body will be taken from 'length' bytes read from the + * file descriptor 'fd', starting from file offset 'offset'. */ +void ne_set_request_body_fd(ne_request *req, int fd, + off_t offset, off_t length); + +#ifdef NE_LFS +/* Alternate version of ne_set_request_body_fd taking off64_t + * offset type for systems supporting _LARGEFILE64_SOURCE. */ +void ne_set_request_body_fd64(ne_request *req, int fd, + off64_t offset, off64_t length); +#endif /* "Pull"-based request body provider: a callback which is invoked to * provide blocks of request body on demand. @@ -79,13 +83,20 @@ int ne_set_request_body_fd(ne_request *req, int fd); typedef ssize_t (*ne_provide_body)(void *userdata, char *buffer, size_t buflen); -/* Install a callback which is invoked as needed to provide request - * body blocks. Total request body is 'size' bytes: the callback MUST - * ensure it returns in total exactly 'size' bytes each time the - * request body is provided. */ -void ne_set_request_body_provider(ne_request *req, size_t size, +/* Install a callback which is invoked as needed to provide the + * request body, a block at a time. The total size of the request + * body is 'length'; the callback must ensure that it returns no more + * than 'length' bytes in total. */ +void ne_set_request_body_provider(ne_request *req, off_t length, ne_provide_body provider, void *userdata); +#ifdef NE_LFS +/* Duplicate version of ne_set_request_body_provider, taking an off64_t + * offset. */ +void ne_set_request_body_provider64(ne_request *req, off64_t length, + ne_provide_body provider, void *userdata); +#endif + /* Handling response bodies... you provide TWO callbacks: * * 1) 'acceptance' callback: determines whether you want to handle the @@ -108,8 +119,12 @@ int ne_accept_2xx(void *userdata, ne_request *req, const ne_status *st); * userdata. */ int ne_accept_always(void *userdata, ne_request *req, const ne_status *st); -/* Callback for reading a block of data. */ -typedef void (*ne_block_reader)(void *userdata, const char *buf, size_t len); +/* Callback for reading a block of data. Returns zero on success, or + * -1 on error. If returning an error, the response will be aborted + * and the callback will not be invoked again. The request dispatch + * (or ne_read_response_block call) will fail with NE_ERROR; the + * session error string should have been set by the callback. */ +typedef int (*ne_block_reader)(void *userdata, const char *buf, size_t len); /* Add a response reader for the given request, with the given * acceptance function. userdata is passed as the first argument to @@ -213,6 +228,12 @@ int ne_end_request(ne_request *req); */ ssize_t ne_read_response_block(ne_request *req, char *buffer, size_t buflen); +/* Include the HTTP/1.1 header "Expect: 100-continue" in request 'req' + * if 'flag' is non-zero. Warning: 100-continue support is not + * implemented correctly in some HTTP/1.1 servers, enabling this + * feature may cause requests to hang or time out. */ +void ne_set_request_expect100(ne_request *req, int flag); + /**** Request hooks handling *****/ typedef void (*ne_free_hooks)(void *cookie); @@ -261,4 +282,3 @@ void *ne_get_request_private(ne_request *req, const char *id); END_NEON_DECLS #endif /* NE_REQUEST_H */ - diff --git a/neon/src/ne_session.c b/neon/src/ne_session.c index 04b777e31..027160005 100644 --- a/neon/src/ne_session.c +++ b/neon/src/ne_session.c @@ -1,6 +1,6 @@ /* HTTP session handling - Copyright (C) 1999-2003, Joe Orton <joe@manyfish.co.uk> + Copyright (C) 1999-2004, Joe Orton <joe@manyfish.co.uk> Portions are: Copyright (C) 1999-2000 Tommi Komulainen <Tommi.Komulainen@iki.fi> @@ -33,6 +33,10 @@ #include <errno.h> #endif +#ifdef NE_HAVE_IDNA +#include <idna.h> +#endif + #include "ne_session.h" #include "ne_alloc.h" #include "ne_utils.h" @@ -84,7 +88,7 @@ void ne_session_destroy(ne_session *sess) ne_close_connection(sess); } -#ifdef NEON_SSL +#ifdef NE_HAVE_SSL if (sess->ssl_context) ne_ssl_context_destroy(sess->ssl_context); @@ -118,6 +122,14 @@ static void set_hostport(struct host_info *host, unsigned int defaultport) static void set_hostinfo(struct host_info *info, const char *hostname, unsigned int port) { +#ifdef NE_HAVE_IDNA + char *ihost; + +#define FLAGS IDNA_USE_STD3_ASCII_RULES + if (idna_to_ascii_8z(hostname, &ihost, FLAGS) == IDNA_SUCCESS) + info->hostname = ihost; + else /* fall back to use provided hostname string */ +#endif info->hostname = ne_strdup(hostname); info->port = port; } @@ -139,16 +151,14 @@ ne_session *ne_session_create(const char *scheme, set_hostinfo(&sess->server, hostname, port); set_hostport(&sess->server, sess->use_ssl?443:80); -#ifdef NEON_SSL +#ifdef NE_HAVE_SSL if (sess->use_ssl) { - sess->ssl_context = ne_ssl_context_create(); + sess->ssl_context = ne_ssl_context_create(0); } #endif sess->scheme = ne_strdup(scheme); - /* Default expect-100 to OFF. */ - sess->expect100_works = -1; return sess; } @@ -160,6 +170,12 @@ void ne_session_proxy(ne_session *sess, const char *hostname, set_hostinfo(&sess->proxy, hostname, port); } +void ne_set_addrlist(ne_session *sess, const ne_inet_addr **addrs, size_t n) +{ + sess->addrlist = addrs; + sess->numaddrs = n; +} + void ne_set_error(ne_session *sess, const char *format, ...) { va_list params; @@ -184,15 +200,6 @@ void ne_set_status(ne_session *sess, sess->notify_ud = userdata; } -void ne_set_expect100(ne_session *sess, int use_expect100) -{ - if (use_expect100) { - sess->expect100_works = 1; - } else { - sess->expect100_works = -1; - } -} - void ne_set_persist(ne_session *sess, int persist) { sess->no_persist = !persist; @@ -268,10 +275,7 @@ void ne_ssl_provide_clicert(ne_session *sess, void ne_ssl_trust_cert(ne_session *sess, const ne_ssl_certificate *cert) { -#ifdef NEON_SSL - ne_ssl_ctx_trustcert(sess->ssl_context, cert); +#ifdef NE_HAVE_SSL + ne_ssl_context_trustcert(sess->ssl_context, cert); #endif } - - - diff --git a/neon/src/ne_session.h b/neon/src/ne_session.h index 69e8647eb..5e846357b 100644 --- a/neon/src/ne_session.h +++ b/neon/src/ne_session.h @@ -1,6 +1,6 @@ /* HTTP session handling - Copyright (C) 1999-2003, Joe Orton <joe@manyfish.co.uk> + Copyright (C) 1999-2004, Joe Orton <joe@manyfish.co.uk> This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public @@ -27,6 +27,7 @@ #include "ne_ssl.h" #include "ne_uri.h" /* for ne_uri */ #include "ne_defs.h" +#include "ne_socket.h" BEGIN_NEON_DECLS @@ -34,7 +35,9 @@ typedef struct ne_session_s ne_session; /* Create a session to the given server, using the given scheme. If * "https" is passed as the scheme, SSL will be used to connect to the - * server. */ + * server. If neon is built with support for IDNA, 'hostname' can be + * a UTF-8-encoded Unicode string; otherwise, it must be an ASCII + * string. */ ne_session *ne_session_create(const char *scheme, const char *hostname, unsigned int port); @@ -49,18 +52,13 @@ void ne_close_connection(ne_session *sess); void ne_session_proxy(ne_session *sess, const char *hostname, unsigned int port); -/* Set protocol options for session: - * expect100: Defaults to OFF - * persist: Defaults to ON - * - * expect100: When set, send the "Expect: 100-continue" request header - * with requests with bodies. - * - * persist: When set, use a persistent connection. (Generally, - * you don't want to turn this off.) - * */ -void ne_set_expect100(ne_session *sess, int use_expect100); -void ne_set_persist(ne_session *sess, int persist); +/* Disable use of persistent connection if 'flag' is non-zero, else + * enable (the default). */ +void ne_set_persist(ne_session *sess, int flag); + +/* Bypass the normal name resolution; force the use of specific set of + * addresses for this session, addrs[0]...addrs[n-1]. */ +void ne_set_addrlist(ne_session *sess, const ne_inet_addr **addrs, size_t n); /* Progress callback. */ typedef void (*ne_progress)(void *userdata, off_t progress, off_t total); diff --git a/neon/src/ne_socket.c b/neon/src/ne_socket.c index 1bcf257d1..224014109 100644 --- a/neon/src/ne_socket.c +++ b/neon/src/ne_socket.c @@ -1,7 +1,8 @@ /* - socket handling routines + Socket handling routines Copyright (C) 1998-2004, Joe Orton <joe@manyfish.co.uk>, Copyright (C) 1999-2000 Tommi Komulainen <Tommi.Komulainen@iki.fi> + Copyright (C) 2004 Aleix Conchillo Flaque <aleix@member.fsf.org> This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public @@ -29,10 +30,6 @@ #ifdef __hpux /* pick up hstrerror */ #define _XOPEN_SOURCE_EXTENDED 1 -/* don't use the broken getaddrinfo shipped in HP-UX 11.11 */ -#ifdef USE_GETADDRINFO -#undef USE_GETADDRINFO -#endif #endif #include <sys/types.h> @@ -40,13 +37,16 @@ #include <sys/time.h> #endif #include <sys/stat.h> -#ifdef HAVE_SYS_SELECT_H -#include <sys/select.h> -#endif #ifdef HAVE_SYS_SOCKET_H #include <sys/socket.h> #endif +#ifdef NE_USE_POLL +#include <sys/poll.h> +#elif defined(HAVE_SYS_SELECT_H) +#include <sys/select.h> +#endif + #ifdef HAVE_NETINET_IN_H #include <netinet/in.h> #endif @@ -63,9 +63,12 @@ #ifdef WIN32 #include <winsock2.h> #include <stddef.h> +#ifdef USE_GETADDRINFO +#include <ws2tcpip.h> +#endif #endif -#if defined(NEON_SSL) && defined(HAVE_LIMITS_H) +#if defined(HAVE_OPENSSL) && defined(HAVE_LIMITS_H) #include <limits.h> /* for INT_MAX */ #endif #ifdef HAVE_STRING_H @@ -91,18 +94,17 @@ #include <socks.h> #endif -#ifdef NEON_SSL +#ifdef HAVE_OPENSSL #include <openssl/ssl.h> #include <openssl/err.h> #include <openssl/pkcs12.h> /* for PKCS12_PBE_add */ #include <openssl/rand.h> - -#include "ne_privssl.h" +#include <openssl/opensslv.h> /* for OPENSSL_VERSION_NUMBER */ #endif -#include "ne_i18n.h" -#include "ne_utils.h" -#include "ne_string.h" +#ifdef HAVE_GNUTLS +#include <gnutls/gnutls.h> +#endif #define NE_INET_ADDR_DEFINED /* A slightly ugly hack: change the ne_inet_addr definition to be the @@ -112,35 +114,35 @@ * structure, or losing type-safety by using void *. */ #ifdef USE_GETADDRINFO typedef struct addrinfo ne_inet_addr; +#else +typedef struct in_addr ne_inet_addr; +#endif + +#ifdef NE_HAVE_SSL +#include "ne_privssl.h" /* MUST come after ne_inet_addr is defined */ +#endif /* To avoid doing AAAA queries unless absolutely necessary, either use * AI_ADDRCONFIG where available, or a run-time check for working IPv6 * support; the latter is only known to work on Linux. */ -#if !defined(USE_GAI_ADDRCONFIG) && defined(__linux__) +#if defined(USE_GETADDRINFO) && !defined(USE_GAI_ADDRCONFIG) && defined(__linux__) #define USE_CHECK_IPV6 #endif -#else -typedef struct in_addr ne_inet_addr; -#endif - +#include "ne_i18n.h" +#include "ne_utils.h" +#include "ne_string.h" #include "ne_socket.h" #include "ne_alloc.h" #if defined(__BEOS__) && !defined(BONE_VERSION) /* pre-BONE */ -#define ne_write(a,b,c) send(a,b,c,0) -#define ne_read(a,b,c) recv(a,b,c,0) #define ne_close(s) closesocket(s) #define ne_errno errno #elif defined(WIN32) -#define ne_write(a,b,c) send(a,b,c,0) -#define ne_read(a,b,c) recv(a,b,c,0) #define ne_close(s) closesocket(s) #define ne_errno WSAGetLastError() #else /* really Unix! */ -#define ne_write(a,b,c) write(a,b,c) -#define ne_read(a,b,c) read(a,b,c) #define ne_close(s) close(s) #define ne_errno errno #endif @@ -151,7 +153,13 @@ typedef struct in_addr ne_inet_addr; #define NE_ISCLOSED(e) ((e) == WSAESHUTDOWN || (e) == WSAENOTCONN) #define NE_ISINTR(e) (0) #else /* Unix */ +/* ECONNABORTED shouldn't really be returned by anything but accept() + * but apparently nobody told CygWin that... */ +#ifdef ECONNABORTED +#define NE_ISRESET(e) ((e) == ECONNRESET || (e) == ECONNABORTED) +#else #define NE_ISRESET(e) ((e) == ECONNRESET) +#endif #define NE_ISCLOSED(e) ((e) == EPIPE) #define NE_ISINTR(e) ((e) == EINTR) #endif @@ -165,8 +173,8 @@ struct iofns { /* Read up to 'len' bytes into 'buf' from socket. Return <0 on * error or EOF, or >0; number of bytes read. */ ssize_t (*read)(ne_socket *s, char *buf, size_t len); - /* Write exactly 'len' bytes from 'buf' to socket. Return zero on - * success, <0 on error. */ + /* Write up to 'len' bytes from 'buf' to socket. Return number of + * bytes written on success, or <0 on error. */ ssize_t (*write)(ne_socket *s, const char *buf, size_t len); /* Wait up to 'n' seconds for socket to become readable. Returns * 0 when readable, otherwise NE_SOCK_TIMEOUT or NE_SOCK_ERROR. */ @@ -179,7 +187,7 @@ struct ne_socket_s { void *progress_ud; int rdtimeout; /* read timeout. */ const struct iofns *ops; -#ifdef NEON_SSL +#ifdef NE_HAVE_SSL ne_ssl_socket ssl; #endif /* The read buffer: ->buffer stores byte which have been read; as @@ -223,16 +231,21 @@ static void print_error(int errnum, char *buffer, size_t buflen) #define set_strerror(s, e) ne_strerror((e), (s)->error, sizeof (s)->error) #endif -#ifdef NEON_SSL - -/* Initialize SSL library. */ +#if defined(HAVE_OPENSSL) static void init_ssl(void) { SSL_load_error_strings(); SSL_library_init(); PKCS12_PBE_add(); /* ### not sure why this is needed. */ } +#elif defined(HAVE_GNUTLS) +static void init_ssl(void) +{ + gnutls_global_init(); +} +#endif /* HAVE_OPENSSL */ +#ifdef HAVE_OPENSSL /* Seed the SSL PRNG, if necessary; returns non-zero on failure. */ static int seed_ssl_prng(void) { @@ -240,7 +253,7 @@ static int seed_ssl_prng(void) if (RAND_status() == 1) return 0; -#ifdef EGD_PATH +#if defined(EGD_PATH) NE_DEBUG(NE_DBG_SOCKET, "Seeding PRNG from " EGD_PATH "...\n"); if (RAND_egd(EGD_PATH) != -1) return 0; @@ -260,7 +273,7 @@ static int seed_ssl_prng(void) NE_DEBUG(NE_DBG_SOCKET, "No entropy source found; could not seed PRNG.\n"); return -1; } -#endif /* NEON_SSL */ +#endif /* HAVE_OPENSSL */ #ifdef USE_CHECK_IPV6 static int ipv6_disabled = 0; @@ -307,7 +320,7 @@ int ne_sock_init(void) #endif -#ifdef NEON_SOCKS +#ifdef NE_HAVE_SOCKS SOCKSinit("neon"); #endif @@ -319,7 +332,7 @@ int ne_sock_init(void) init_ipv6(); #endif -#ifdef NEON_SSL +#ifdef NE_HAVE_SSL init_ssl(); #endif @@ -332,6 +345,9 @@ void ne_sock_exit(void) #ifdef WIN32 WSACleanup(); #endif +#ifdef HAVE_GNUTLS + gnutls_global_deinit(); +#endif init_result = 0; } @@ -345,9 +361,6 @@ int ne_sock_block(ne_socket *sock, int n) /* Cast address object AD to type 'sockaddr_TY' */ #define SACAST(ty, ad) ((struct sockaddr_##ty *)(ad)) -#define SOCK_ERR(x) do { ssize_t _sock_err = (x); \ -if (_sock_err < 0) return _sock_err; } while(0) - ssize_t ne_sock_read(ne_socket *sock, char *buffer, size_t buflen) { ssize_t bytes; @@ -411,7 +424,20 @@ ssize_t ne_sock_peek(ne_socket *sock, char *buffer, size_t buflen) /* Await data on raw fd in socket. */ static int readable_raw(ne_socket *sock, int secs) { - int fdno = sock->fd, ret; + int ret; +#ifdef NE_USE_POLL + struct pollfd fds; + int timeout = secs > 0 ? secs * 1000 : -1; + + fds.fd = sock->fd; + fds.events = POLLIN; + fds.revents = 0; + + do { + ret = poll(&fds, 1, timeout); + } while (ret < 0 && NE_ISINTR(ne_errno)); +#else + int fdno = sock->fd; fd_set rdfds; struct timeval timeout, *tvp = (secs >= 0 ? &timeout : NULL); @@ -425,6 +451,8 @@ static int readable_raw(ne_socket *sock, int secs) } ret = select(fdno + 1, &rdfds, NULL, NULL, tvp); } while (ret < 0 && NE_ISINTR(ne_errno)); +#endif + if (ret < 0) { set_strerror(sock, ne_errno); return NE_SOCK_ERROR; @@ -440,7 +468,7 @@ static ssize_t read_raw(ne_socket *sock, char *buffer, size_t len) if (ret) return ret; do { - ret = ne_read(sock->fd, buffer, len); + ret = recv(sock->fd, buffer, len, 0); } while (ret == -1 && NE_ISINTR(ne_errno)); if (ret == 0) { @@ -460,46 +488,36 @@ static ssize_t read_raw(ne_socket *sock, char *buffer, size_t len) static ssize_t write_raw(ne_socket *sock, const char *data, size_t length) { - ssize_t wrote; + ssize_t ret; do { - wrote = ne_write(sock->fd, data, length); - if (wrote > 0) { - data += wrote; - length -= wrote; - } - } while ((wrote > 0 || NE_ISINTR(ne_errno)) && length > 0); + ret = send(sock->fd, data, length, 0); + } while (ret == -1 && NE_ISINTR(ne_errno)); - if (wrote < 0) { + if (ret < 0) { int errnum = ne_errno; set_strerror(sock, errnum); return MAP_ERR(errnum); } - - return 0; + return ret; } static const struct iofns iofns_raw = { read_raw, write_raw, readable_raw }; -#ifdef NEON_SSL +#ifdef HAVE_OPENSSL /* OpenSSL I/O function implementations. */ static int readable_ossl(ne_socket *sock, int secs) { - /* If there is buffered SSL data, then don't block on the socket. - * FIXME: make sure that SSL_read *really* won't block if - * SSL_pending returns non-zero. Possibly need to do - * SSL_read(ssl, buf, SSL_pending(ssl)) */ - - if (SSL_pending(sock->ssl.ssl)) + if (SSL_pending(sock->ssl)) return 0; - return readable_raw(sock, secs); } /* SSL error handling, according to SSL_get_error(3). */ static int error_ossl(ne_socket *sock, int sret) { - int err = SSL_get_error(sock->ssl.ssl, sret), ret = NE_SOCK_ERROR; + int err = SSL_get_error(sock->ssl, sret), ret = NE_SOCK_ERROR; + const char *str; switch (err) { case SSL_ERROR_ZERO_RETURN: @@ -520,13 +538,15 @@ static int error_ossl(ne_socket *sock, int sret) ret = MAP_ERR(err); } } else { - ne_snprintf(sock->error, sizeof sock->error, - _("SSL error: %s"), ERR_reason_error_string(err)); + str = ERR_reason_error_string(err); + ne_snprintf(sock->error, sizeof sock->error, _("SSL error: %s"), + str ? str : _("unknown error code")); } break; default: + str = ERR_reason_error_string(ERR_get_error()); ne_snprintf(sock->error, sizeof sock->error, _("SSL error: %s"), - ERR_reason_error_string(ERR_get_error())); + str ? str : _("unknown error code")); break; } return ret; @@ -543,7 +563,7 @@ static ssize_t read_ossl(ne_socket *sock, char *buffer, size_t len) ret = readable_ossl(sock, sock->rdtimeout); if (ret) return ret; - ret = SSL_read(sock->ssl.ssl, buffer, CAST2INT(len)); + ret = SSL_read(sock->ssl, buffer, CAST2INT(len)); if (ret <= 0) ret = error_ossl(sock, ret); @@ -553,26 +573,126 @@ static ssize_t read_ossl(ne_socket *sock, char *buffer, size_t len) static ssize_t write_ossl(ne_socket *sock, const char *data, size_t len) { int ret, ilen = CAST2INT(len); - ret = SSL_write(sock->ssl.ssl, data, ilen); + ret = SSL_write(sock->ssl, data, ilen); /* ssl.h says SSL_MODE_ENABLE_PARTIAL_WRITE must be enabled to * have SSL_write return < length... so, SSL_write should never * return < length. */ if (ret != ilen) return error_ossl(sock, ret); - return 0; + return ret; } -static const struct iofns iofns_ossl = { +static const struct iofns iofns_ssl = { read_ossl, write_ossl, readable_ossl }; -#endif /* NEON_SSL */ +#elif defined(HAVE_GNUTLS) + +/* Return zero if an alert value can be ignored. */ +static int check_alert(ne_socket *sock, ssize_t ret) +{ + const char *alert; + + if (ret == GNUTLS_E_WARNING_ALERT_RECEIVED) { + alert = gnutls_alert_get_name(gnutls_alert_get(sock->ssl)); + NE_DEBUG(NE_DBG_SOCKET, "TLS warning alert: %s\n", alert); + return 0; + } else if (ret == GNUTLS_E_FATAL_ALERT_RECEIVED) { + alert = gnutls_alert_get_name(gnutls_alert_get(sock->ssl)); + NE_DEBUG(NE_DBG_SOCKET, "TLS fatal alert: %s\n", alert); + return -1; + } + return ret; +} + +static int readable_gnutls(ne_socket *sock, int secs) +{ + if (gnutls_record_check_pending(sock->ssl)) { + return 0; + } + return readable_raw(sock, secs); +} + +static ssize_t error_gnutls(ne_socket *sock, ssize_t sret) +{ + ssize_t ret; + + switch (sret) { + case 0: + ret = NE_SOCK_CLOSED; + set_error(sock, _("Connection closed")); + break; + case GNUTLS_E_FATAL_ALERT_RECEIVED: + ret = NE_SOCK_RESET; + ne_snprintf(sock->error, sizeof sock->error, _("SSL error: %s"), + gnutls_alert_get_name(gnutls_alert_get(sock->ssl))); + break; + default: + ret = NE_SOCK_ERROR; + ne_snprintf(sock->error, sizeof sock->error, _("SSL error: %s"), + gnutls_strerror(sret)); + } + return ret; +} + +#define RETRY_GNUTLS(sock, ret) ((ret < 0) \ + && (ret == GNUTLS_E_INTERRUPTED || ret == GNUTLS_E_AGAIN \ + || check_alert(sock, ret) == 0)) + +static ssize_t read_gnutls(ne_socket *sock, char *buffer, size_t len) +{ + ssize_t ret; + + ret = readable_gnutls(sock, sock->rdtimeout); + if (ret) return ret; + + do { + ret = gnutls_record_recv(sock->ssl, buffer, len); + } while (RETRY_GNUTLS(sock, ret)); + + if (ret <= 0) + ret = error_gnutls(sock, ret); + + return ret; +} + +static ssize_t write_gnutls(ne_socket *sock, const char *data, size_t len) +{ + ssize_t ret; + + do { + ret = gnutls_record_send(sock->ssl, data, len); + } while (RETRY_GNUTLS(sock, ret)); + + if (ret < 0) + return error_gnutls(sock, ret); + + return ret; +} + +static const struct iofns iofns_ssl = { + read_gnutls, + write_gnutls, + readable_gnutls +}; + +#endif int ne_sock_fullwrite(ne_socket *sock, const char *data, size_t len) { - return sock->ops->write(sock, data, len); + ssize_t ret; + + do { + ret = sock->ops->write(sock, data, len); + if (ret > 0) { + data += ret; + len -= ret; + } + } while (ret > 0 && len > 0); + + return ret < 0 ? ret : 0; } ssize_t ne_sock_readline(ne_socket *sock, char *buf, size_t buflen) @@ -761,7 +881,7 @@ char *ne_addr_error(const ne_sock_addr *addr, char *buf, size_t bufsiz) char *ne_iaddr_print(const ne_inet_addr *ia, char *buf, size_t bufsiz) { -#ifdef USE_GETADDRINFO /* implies inet_ntop */ +#if defined(USE_GETADDRINFO) && defined(HAVE_INET_NTOP) const char *ret; #ifdef AF_INET6 if (ia->ai_family == AF_INET6) { @@ -776,7 +896,12 @@ char *ne_iaddr_print(const ne_inet_addr *ia, char *buf, size_t bufsiz) ret = NULL; if (ret == NULL) ne_strnzcpy(buf, "[IP address]", bufsiz); -#else +#elif defined(USE_GETADDRINFO) && defined(NI_NUMERICHOST) + /* use getnameinfo instead for Win32, which lacks inet_ntop: */ + if (getnameinfo(ia->ai_addr, ia->ai_addrlen, buf, bufsiz, NULL, 0, + NI_NUMERICHOST)) + ne_strnzcpy(buf, "[IP address]", bufsiz); +#else /* USE_GETADDRINFO */ ne_strnzcpy(buf, inet_ntoa(*ia), bufsiz); #endif return buf; @@ -854,6 +979,14 @@ int ne_sock_connect(ne_socket *sock, return -1; } +#if !defined(NE_USE_POLL) && !defined(WIN32) + if (fd > FD_SETSIZE) { + ne_close(fd); + set_error(sock, _("Socket descriptor number exceeds FD_SETSIZE")); + return NE_SOCK_ERROR; + } +#endif + #if defined(TCP_NODELAY) && defined(HAVE_SETSOCKOPT) && defined(IPPROTO_TCP) { /* Disable the Nagle algorithm; better to add write buffering * instead of doing this. */ @@ -862,7 +995,7 @@ int ne_sock_connect(ne_socket *sock, } #endif - if (raw_connect(fd, addr, ntohs(port))) { + if (raw_connect(fd, addr, htons(port))) { set_strerror(sock, ne_errno); ne_close(fd); return -1; @@ -908,6 +1041,15 @@ ne_inet_addr *ne_iaddr_make(ne_iaddr_type type, const unsigned char *raw) return ia; } +ne_iaddr_type ne_iaddr_typeof(const ne_inet_addr *ia) +{ +#ifdef USE_GETADDRINFO + return ia->ai_family == AF_INET6 ? ne_iaddr_ipv6 : ne_iaddr_ipv4; +#else + return ne_iaddr_ipv4; +#endif +} + int ne_iaddr_cmp(const ne_inet_addr *i1, const ne_inet_addr *i2) { #ifdef USE_GETADDRINFO @@ -959,25 +1101,59 @@ void ne_sock_read_timeout(ne_socket *sock, int timeout) sock->rdtimeout = timeout; } -#ifdef NEON_SSL +#ifdef NE_HAVE_SSL -void ne_sock_switch_ssl(ne_socket *sock, void *ssl) +int ne_sock_accept_ssl(ne_socket *sock, ne_ssl_context *ctx) { - sock->ssl.ssl = ssl; - sock->ops = &iofns_ossl; + int ret; + ne_ssl_socket ssl; + +#if defined(HAVE_OPENSSL) + ssl = SSL_new(ctx->ctx); + + SSL_set_fd(ssl, sock->fd); + + sock->ssl = ssl; + ret = SSL_accept(ssl); + if (ret != 1) { + return error_ossl(sock, ret); + } +#elif defined(HAVE_GNUTLS) + gnutls_init(&ssl, GNUTLS_SERVER); + gnutls_credentials_set(ssl, GNUTLS_CRD_CERTIFICATE, ctx->cred); + gnutls_set_default_priority(ssl); + + sock->ssl = ssl; + gnutls_transport_set_ptr(sock->ssl, (gnutls_transport_ptr) sock->fd); + ret = gnutls_handshake(ssl); + if (ret < 0) { + return error_gnutls(sock, ret); + } +#endif + sock->ops = &iofns_ssl; + return 0; } int ne_sock_connect_ssl(ne_socket *sock, ne_ssl_context *ctx) { - SSL *ssl; int ret; +#if defined(HAVE_OPENSSL) + SSL *ssl; + if (seed_ssl_prng()) { set_error(sock, _("SSL disabled due to lack of entropy")); return NE_SOCK_ERROR; } - sock->ssl.ssl = ssl = SSL_new(ctx->ctx); + /* If runtime library version differs from compile-time version + * number in major/minor/fix level, abort soon. */ + if ((SSLeay() ^ OPENSSL_VERSION_NUMBER) & 0xFFFFF000) { + set_error(sock, _("SSL disabled due to library version mismatch")); + return NE_SOCK_ERROR; + } + + sock->ssl = ssl = SSL_new(ctx->ctx); if (!ssl) { set_error(sock, _("Could not create SSL structure")); return NE_SOCK_ERROR; @@ -986,7 +1162,7 @@ int ne_sock_connect_ssl(ne_socket *sock, ne_ssl_context *ctx) SSL_set_app_data(ssl, ctx); SSL_set_mode(ssl, SSL_MODE_AUTO_RETRY); SSL_set_fd(ssl, sock->fd); - sock->ops = &iofns_ossl; + sock->ops = &iofns_ssl; if (ctx->sess) SSL_set_session(ssl, ctx->sess); @@ -995,16 +1171,30 @@ int ne_sock_connect_ssl(ne_socket *sock, ne_ssl_context *ctx) if (ret != 1) { error_ossl(sock, ret); SSL_free(ssl); - sock->ssl.ssl = NULL; + sock->ssl = NULL; return NE_SOCK_ERROR; } +#elif defined(HAVE_GNUTLS) + /* DH and RSA params are set in ne_ssl_context_create */ + gnutls_init(&sock->ssl, GNUTLS_CLIENT); + gnutls_set_default_priority(sock->ssl); + gnutls_credentials_set(sock->ssl, GNUTLS_CRD_CERTIFICATE, ctx->cred); + + gnutls_transport_set_ptr(sock->ssl, (gnutls_transport_ptr) sock->fd); + sock->ops = &iofns_ssl; + ret = gnutls_handshake(sock->ssl); + if (ret < 0) { + error_gnutls(sock, ret); + return NE_SOCK_ERROR; + } +#endif return 0; } -ne_ssl_socket *ne_sock_sslsock(ne_socket *sock) +ne_ssl_socket ne__sock_sslsock(ne_socket *sock) { - return &sock->ssl; + return sock->ssl; } #endif @@ -1018,12 +1208,21 @@ const char *ne_sock_error(const ne_socket *sock) int ne_sock_close(ne_socket *sock) { int ret; -#ifdef NEON_SSL - if (sock->ssl.ssl) { - SSL_shutdown(sock->ssl.ssl); - SSL_free(sock->ssl.ssl); + +#if defined(HAVE_OPENSSL) + if (sock->ssl) { + SSL_shutdown(sock->ssl); + SSL_free(sock->ssl); + } +#elif defined(HAVE_GNUTLS) + if (sock->ssl) { + do { + ret = gnutls_bye(sock->ssl, GNUTLS_SHUT_RDWR); + } while (ret < 0 + && (ret == GNUTLS_E_INTERRUPTED || ret == GNUTLS_E_AGAIN)); } #endif + if (sock->fd < 0) ret = 0; else diff --git a/neon/src/ne_socket.h b/neon/src/ne_socket.h index 4da0c0c37..2061b4dd3 100644 --- a/neon/src/ne_socket.h +++ b/neon/src/ne_socket.h @@ -1,6 +1,6 @@ /* socket handling interface - Copyright (C) 1999-2003, Joe Orton <joe@manyfish.co.uk> + Copyright (C) 1999-2004, Joe Orton <joe@manyfish.co.uk> This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public @@ -36,7 +36,7 @@ BEGIN_NEON_DECLS #define NE_SOCK_CLOSED (-3) /* Connection was reset (e.g. server crashed) */ #define NE_SOCK_RESET (-4) -/* Secure connection was subject to possible truncation attack. */ +/* Secure connection was closed without proper SSL shutdown. */ #define NE_SOCK_TRUNC (-5) /* ne_socket represents a TCP socket. */ @@ -57,7 +57,7 @@ int ne_sock_init(void); /* Shutdown any underlying libraries. */ void ne_sock_exit(void); -/* Resolve the given hostname. 'flags' are currently ignored. Hex +/* Resolve the given hostname. 'flags' must be zero. Hex * string IPv6 addresses (e.g. `::1') may be enclosed in brackets * (e.g. `[::1]'). */ ne_sock_addr *ne_addr_resolve(const char *hostname, int flags); @@ -102,6 +102,9 @@ ne_inet_addr *ne_iaddr_make(ne_iaddr_type type, const unsigned char *raw); * are not equal. */ int ne_iaddr_cmp(const ne_inet_addr *i1, const ne_inet_addr *i2); +/* Returns the type of the given network address. */ +ne_iaddr_type ne_iaddr_typeof(const ne_inet_addr *ia); + /* Prints the string representation of network address 'ia' into the * 'buffer', which is of size 'bufsiz'. Returns 'buffer'. */ char *ne_iaddr_print(const ne_inet_addr *ia, char *buffer, size_t bufsiz); @@ -177,16 +180,14 @@ void ne_sock_read_timeout(ne_socket *sock, int timeout); * none is known. */ int ne_service_lookup(const char *name); -/* Enable SSL with an already-negotiated SSL socket. */ -void ne_sock_switch_ssl(ne_socket *sock, void *ssl); +/* Negotiate an SSL connection on socket as an SSL server, using given + * SSL context. */ +int ne_sock_accept_ssl(ne_socket *sock, ne_ssl_context *ctx); -/* Perform an SSL negotiation on 'sock', using given context. */ +/* Negotiate an SSL connection on socket as an SSL client, using given + * SSL context. */ int ne_sock_connect_ssl(ne_socket *sock, ne_ssl_context *ctx); -/* Return SSL socket object in use for 'sock'. */ -typedef struct ne_ssl_socket_s ne_ssl_socket; -ne_ssl_socket *ne_sock_sslsock(ne_socket *sock); - END_NEON_DECLS #endif /* NE_SOCKET_H */ diff --git a/neon/src/ne_ssl.h b/neon/src/ne_ssl.h index bda65a33d..99df86ac3 100644 --- a/neon/src/ne_ssl.h +++ b/neon/src/ne_ssl.h @@ -133,14 +133,31 @@ const ne_ssl_certificate *ne_ssl_clicert_owner(const ne_ssl_client_cert *ccert); /* Deallocate memory associated with a client certificate. */ void ne_ssl_clicert_free(ne_ssl_client_cert *ccert); -/* An SSL context; only necessary when interfacing with ne_socket.h. */ + +/* SSL context object. The interfaces to manipulate an SSL context + * are only needed when interfacing directly with ne_socket.h. */ typedef struct ne_ssl_context_s ne_ssl_context; +/* Context creation modes: */ +#define NE_SSL_CTX_CLIENT (0) /* client context */ +#define NE_SSL_CTX_SERVER (1) /* default server context */ +#define NE_SSL_CTX_SERVERv2 (2) /* SSLv2-specific server context */ + /* Create an SSL context. */ -ne_ssl_context *ne_ssl_context_create(void); +ne_ssl_context *ne_ssl_context_create(int mode); + +/* Client mode: trust the given certificate 'cert' in context 'ctx'. */ +void ne_ssl_context_trustcert(ne_ssl_context *ctx, const ne_ssl_certificate *cert); + +/* Server mode: use given cert and key (filenames to PEM certificates). */ +int ne_ssl_context_keypair(ne_ssl_context *ctx, + const char *cert, const char *key); -/* Trust the given certificate 'cert' in context 'ctx'. */ -void ne_ssl_ctx_trustcert(ne_ssl_context *ctx, const ne_ssl_certificate *cert); +/* Server mode: set client cert verification options: required is non-zero if + * a client cert is required, if ca_names is non-NULL it is a filename containing + * a set of PEM certs from which CA names are sent in the ccert request. */ +int ne_ssl_context_set_verify(ne_ssl_context *ctx, int required, + const char *ca_names, const char *verify_cas); /* Destroy an SSL context. */ void ne_ssl_context_destroy(ne_ssl_context *ctx); diff --git a/neon/src/ne_string.c b/neon/src/ne_string.c index 3cddad901..31e3612b2 100644 --- a/neon/src/ne_string.c +++ b/neon/src/ne_string.c @@ -1,6 +1,6 @@ /* String utility functions - Copyright (C) 1999-2003, Joe Orton <joe@manyfish.co.uk> + Copyright (C) 1999-2004, Joe Orton <joe@manyfish.co.uk> This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public @@ -31,6 +31,8 @@ #include <unistd.h> #endif +#include <stdio.h> + #include <ctype.h> /* for isprint() etc in ne_strclean() */ #include "ne_alloc.h" @@ -101,172 +103,6 @@ char *ne_shave(char *str, const char *whitespace) return ret; } -/* TODO: deprecate all these and use ne_token() instead. */ - -char **split_string(const char *str, const char separator, - const char *quotes, const char *whitespace) -{ - return split_string_c(str, separator, quotes, whitespace, NULL); -} - -char **split_string_c(const char *str, const char separator, - const char *quotes, const char *whitespace, - int *give_count) -{ - char **comps; - const char *pnt, *quot = NULL, - *start, *end; /* The start of the current component */ - int count, /* The number of components */ - iswhite, /* is it whitespace */ - issep, /* is it the separator */ - curr, /* current component index */ - length, /* length of component */ - leading_wspace; /* in leading whitespace still? */ - - /* Inefficient, but easier - first off, count the number of - * components we have. */ - count = 1; - for (pnt = str; *pnt!='\0'; pnt++) { - if (quotes != NULL) { - quot = strchr(quotes, *pnt); - } - if (quot != NULL) { - /* We found a quote, so skip till the next quote */ - for (pnt++; (*pnt!=*quot) && (*pnt!='\0'); pnt++) - /* nullop */; - } else if (*pnt == separator) { - count++; - } - } - - if (give_count) { - /* Write the count */ - *give_count = count; - } - - /* Now, have got the number of components. - * Allocate the comps array. +1 for the NULL */ - comps = ne_malloc(sizeof(char *) * (count + 1)); - - comps[count] = NULL; - - quot = end = start = NULL; - curr = 0; - leading_wspace = 1; - - /* Now fill in the array */ - for (pnt = str; *pnt != '\0'; pnt++) { - /* What is the current character - quote, whitespace, separator? */ - if (quotes != NULL) { - quot = strchr(quotes, *pnt); - } - iswhite = (whitespace!=NULL) && - (strchr(whitespace, *pnt) != NULL); - issep = (*pnt == separator); - /* What to do? */ - if (leading_wspace) { - if (quot!=NULL) { - /* Quoted bit */ - start = pnt; - length = 1; - leading_wspace = 0; - } else if (issep) { - /* Zero-length component */ - comps[curr++] = ne_strdup(""); - } else if (!iswhite) { - start = end = pnt; - length = 1; - leading_wspace = 0; - } - } else { - if (quot!=NULL) { - /* Quoted bit */ - length++; - } else if (issep) { - /* End of component - enter it into the array */ - length = (end - start) + 1; - comps[curr] = ne_malloc(length+1); - memcpy(comps[curr], start, length); - comps[curr][length] = '\0'; - curr++; - leading_wspace = 1; - } else if (!iswhite) { - /* Not whitespace - update end marker */ - end = pnt; - } - } - if (quot != NULL) { - /* Skip to closing quote */ - for (pnt++; *pnt!=*quot && *pnt != '\0'; ++pnt) - /* nullop */; - /* Last non-wspace char is closing quote */ - end = pnt; - } - } - /* Handle final component */ - if (leading_wspace) { - comps[curr] = ne_strdup(""); - } else { - /* End of component - enter it into the array */ - length = (end - start) + 1; - comps[curr] = ne_malloc(length+1); - memcpy(comps[curr], start, length); - comps[curr][length] = '\0'; - } - return comps; -} - -char **pair_string(const char *str, const char compsep, const char kvsep, - const char *quotes, const char *whitespace) -{ - char **comps, **pairs, *split; - int count = 0, n, length; - comps = split_string_c(str, compsep, quotes, whitespace, &count); - /* Allocate space for 2* as many components as split_string returned, - * +2 for the NULLS. */ - pairs = ne_malloc((2*count+2) * sizeof(char *)); - if (pairs == NULL) { - return NULL; - } - for (n = 0; n < count; n++) { - /* Find the split */ - split = strchr(comps[n], kvsep); - if (split == NULL) { - /* No seperator found */ - length = strlen(comps[n]); - } else { - length = split-comps[n]; - } - /* Enter the key into the array */ - pairs[2*n] = comps[n]; - /* Null-terminate the key */ - pairs[2*n][length] = '\0'; - pairs[2*n+1] = split?(split + 1):NULL; - } - ne_free(comps); - pairs[2*count] = pairs[2*count+1] = NULL; - return pairs; -} - -void split_string_free(char **components) -{ - char **pnt = components; - while (*pnt != NULL) { - ne_free(*pnt); - pnt++; - } - ne_free(components); -} - -void pair_string_free(char **pairs) -{ - int n; - for (n = 0; pairs[n] != NULL; n+=2) { - ne_free(pairs[n]); - } - ne_free(pairs); -} - void ne_buffer_clear(ne_buffer *buf) { memset(buf->data, 0, buf->length); @@ -517,3 +353,31 @@ char *ne_strerror(int errnum, char *buf, size_t buflen) #endif return buf; } + + +/* Wrapper for ne_snprintf. */ +size_t ne_snprintf(char *str, size_t size, const char *fmt, ...) +{ + va_list ap; + va_start(ap, fmt); +#ifdef HAVE_TRIO + trio_vsnprintf(str, size, fmt, ap); +#else + vsnprintf(str, size, fmt, ap); +#endif + va_end(ap); + str[size-1] = '\0'; + return strlen(str); +} + +/* Wrapper for ne_vsnprintf. */ +size_t ne_vsnprintf(char *str, size_t size, const char *fmt, va_list ap) +{ +#ifdef HAVE_TRIO + trio_vsnprintf(str, size, fmt, ap); +#else + vsnprintf(str, size, fmt, ap); +#endif + str[size-1] = '\0'; + return strlen(str); +} diff --git a/neon/src/ne_string.h b/neon/src/ne_string.h index 81ede092e..9412c905b 100644 --- a/neon/src/ne_string.h +++ b/neon/src/ne_string.h @@ -1,6 +1,6 @@ /* String utility functions - Copyright (C) 1999-2002, Joe Orton <joe@manyfish.co.uk> + Copyright (C) 1999-2002, 2004, Joe Orton <joe@manyfish.co.uk> This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public @@ -57,17 +57,6 @@ char *ne_base64(const unsigned char *text, size_t len); * or zero on decode error (in which case *out is undefined). */ size_t ne_unbase64(const char *data, unsigned char **out); -/*** OBSOLETE INTERFACES ***/ -char **split_string(const char *str, const char seperator, - const char *quotes, const char *whitespace); -char **split_string_c(const char *str, const char seperator, - const char *quotes, const char *whitespace, int *count); -char **pair_string(const char *str, const char compsep, const char kvsep, - const char *quotes, const char *whitespace); -void split_string_free(char **components); -void pair_string_free(char **pairs); -/*** END OF OBSOLETE INTERFACES */ - /* String buffer handling. (Strings are zero-terminated still). A * string buffer ne_buffer * which grows dynamically with the * string. */ @@ -132,6 +121,15 @@ char *ne_concat(const char *str, ...); #define NE_ASC2HEX(x) (((x) <= '9') ? ((x) - '0') : (tolower((x)) + 10 - 'a')) #define NE_HEX2ASC(x) ((char) ((x) > 9 ? ((x) - 10 + 'a') : ((x) + '0'))) +/* Wrapper for snprintf: always NUL-terminates returned buffer, and + * returns strlen(str). */ +size_t ne_snprintf(char *str, size_t size, const char *fmt, ...) + ne_attribute((format(printf, 3, 4))); + +/* Wrapper for vsnprintf. */ +size_t ne_vsnprintf(char *str, size_t size, const char *fmt, va_list ap) + ne_attribute((format(printf, 3, 0))); + END_NEON_DECLS #endif /* NE_STRING_H */ diff --git a/neon/src/ne_stubssl.c b/neon/src/ne_stubssl.c index ff71e1e0f..8580b9713 100644 --- a/neon/src/ne_stubssl.c +++ b/neon/src/ne_stubssl.c @@ -1,6 +1,6 @@ /* Stubs for SSL support when no SSL library has been configured - Copyright (C) 2002-2003, Joe Orton <joe@manyfish.co.uk> + Copyright (C) 2002-2004, Joe Orton <joe@manyfish.co.uk> This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public @@ -82,14 +82,22 @@ void ne_ssl_clicert_free(ne_ssl_client_cert *ccert) {} void ne_ssl_trust_default_ca(ne_session *sess) {} -ne_ssl_context *ne_ssl_context_create(void) +ne_ssl_context *ne_ssl_context_create(int mode) { return NULL; } -void ne_ssl_ctx_trustcert(ne_ssl_context *ctx, const ne_ssl_certificate *cert) +void ne_ssl_context_trustcert(ne_ssl_context *ctx, const ne_ssl_certificate *cert) {} +int ne_ssl_context_set_verify(ne_ssl_context *ctx, + int required, + const char *ca_names, + const char *verify_cas) +{ + return -1; +} + void ne_ssl_context_destroy(ne_ssl_context *ctx) {} int ne_ssl_cert_digest(const ne_ssl_certificate *cert, char digest[60]) diff --git a/neon/src/ne_uri.c b/neon/src/ne_uri.c index 51107d1bc..0aab20d39 100644 --- a/neon/src/ne_uri.c +++ b/neon/src/ne_uri.c @@ -34,9 +34,10 @@ #include <stdlib.h> #endif +#include <stdio.h> + #include <ctype.h> -#include "ne_utils.h" /* for 'min' */ #include "ne_string.h" /* for ne_buffer */ #include "ne_alloc.h" #include "ne_uri.h" @@ -277,6 +278,11 @@ int ne_uri_cmp(const ne_uri *u1, const ne_uri *u2) #undef CMP #undef CASECMP +#ifndef WIN32 +#undef min +#define min(a,b) ((a)<(b)?(a):(b)) +#endif + /* TODO: implement properly */ int ne_path_compare(const char *a, const char *b) { diff --git a/neon/src/ne_utils.c b/neon/src/ne_utils.c index 76929b0d0..9b22f0950 100644 --- a/neon/src/ne_utils.c +++ b/neon/src/ne_utils.c @@ -30,10 +30,18 @@ #include <stdio.h> #include <ctype.h> /* isdigit() for ne_parse_statusline */ -#ifdef NEON_SSL +#ifdef NE_HAVE_ZLIB +#include <zlib.h> +#endif + +#ifdef HAVE_OPENSSL #include <openssl/opensslv.h> #endif +#ifdef HAVE_GNUTLS +#include <gnutls/gnutls.h> +#endif + /* libxml2: pick up the version string. */ #if defined(HAVE_LIBXML) #include <libxml/xmlversion.h> @@ -41,10 +49,6 @@ #include <expat.h> #endif -#ifdef NEON_ZLIB -#include <zlib.h> -#endif - #include "ne_utils.h" #include "ne_string.h" /* for ne_strdup */ #include "ne_dates.h" @@ -78,12 +82,15 @@ void ne_debug(int ch, const char *template, ...) #define NE_STRINGIFY(x) # x #define NE_EXPAT_VER(x,y,z) NE_STRINGIFY(x) "." NE_STRINGIFY(y) "." NE_STRINGIFY(z) -static const char *version_string = "neon " NEON_VERSION ": " +static const char version_string[] = "neon " NEON_VERSION ": " #ifdef NEON_IS_LIBRARY "Library build" #else "Bundled build" #endif +#ifdef NE_HAVE_IPV6 + ", IPv6" +#endif #ifdef HAVE_EXPAT ", Expat" /* expat >=1.95.2 exported the version */ @@ -95,19 +102,25 @@ static const char *version_string = "neon " NEON_VERSION ": " ", libxml " LIBXML_DOTTED_VERSION #endif /* HAVE_LIBXML */ #endif /* !HAVE_EXPAT */ -#if defined(NEON_ZLIB) && defined(ZLIB_VERSION) +#if defined(NE_HAVE_ZLIB) && defined(ZLIB_VERSION) ", zlib " ZLIB_VERSION -#endif /* NEON_ZLIB && ... */ -#ifdef NEON_SOCKS +#endif /* NE_HAVE_ZLIB && ... */ +#ifdef NE_HAVE_SOCKS ", SOCKSv5" #endif -#ifdef NEON_SSL +#ifdef NE_HAVE_IDNA + ", IDNA" +#endif +#ifdef HAVE_OPENSSL #ifdef OPENSSL_VERSION_TEXT ", " OPENSSL_VERSION_TEXT #else "OpenSSL (unknown version)" #endif /* OPENSSL_VERSION_TEXT */ -#endif +#endif /* HAVE_OPENSSL */ +#ifdef HAVE_GNUTLS + ", GNU TLS " LIBGNUTLS_VERSION +#endif /* HAVE_GNUTLS */ "." ; @@ -121,13 +134,31 @@ int ne_version_match(int major, int minor) return (NEON_VERSION_MAJOR != major) || (NEON_VERSION_MINOR < minor); } -int ne_supports_ssl(void) +int ne_has_support(int feature) { -#ifdef NEON_SSL - return 1; -#else - return 0; + switch (feature) { +#ifdef NE_HAVE_SSL + case NE_FEATURE_SSL: +#endif +#ifdef NE_HAVE_ZLIB + case NE_FEATURE_ZLIB: +#endif +#ifdef NE_HAVE_IPV6 + case NE_FEATURE_IPV6: +#endif +#ifdef NE_HAVE_IDNA + case NE_FEATURE_IDNA: +#endif +#ifdef NE_HAVE_SOCKS + case NE_FEATURE_SOCKS: +#endif +#ifdef NE_HAVE_LFS + case NE_FEATURE_LFS: #endif + return 1; + default: + return 0; + } } int ne_parse_statusline(const char *status_line, ne_status *st) diff --git a/neon/src/ne_utils.h b/neon/src/ne_utils.h index 87e659300..df07c2fa9 100644 --- a/neon/src/ne_utils.h +++ b/neon/src/ne_utils.h @@ -1,6 +1,6 @@ /* HTTP utility functions - Copyright (C) 1999-2002, Joe Orton <joe@manyfish.co.uk> + Copyright (C) 1999-2004, Joe Orton <joe@manyfish.co.uk> This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public @@ -46,22 +46,16 @@ const char *ne_version_string(void); * 'minor'. */ int ne_version_match(int major, int minor); -/* Returns non-zero if neon has support for SSL. */ -int ne_supports_ssl(void); +#define NE_FEATURE_SSL (1) /* SSL/TLS support */ +#define NE_FEATURE_ZLIB (2) /* zlib compression in compress interface */ +#define NE_FEATURE_IPV6 (3) /* IPv6 is supported in resolver */ +#define NE_FEATURE_IDNA (4) /* internationalized domain name support */ +#define NE_FEATURE_LFS (5) /* large file support */ +#define NE_FEATURE_SOCKS (6) /* SOCKSv5 support */ -/* Use replacement snprintf's if trio is being used. */ -#ifdef NEON_TRIO -#define ne_snprintf trio_snprintf -#define ne_vsnprintf trio_vsnprintf -#else -#define ne_snprintf snprintf -#define ne_vsnprintf vsnprintf -#endif - -#ifndef WIN32 -#undef min -#define min(a,b) ((a)<(b)?(a):(b)) -#endif +/* Returns non-zero if neon has support for given feature code + * NE_FEATURE_*. */ +int ne_has_support(int feature); /* CONSIDER: mutt has a nicer way of way of doing debugging output... maybe * switch to like that. */ diff --git a/neon/src/ne_xml.c b/neon/src/ne_xml.c index d405fa2ed..a0c67a91e 100644 --- a/neon/src/ne_xml.c +++ b/neon/src/ne_xml.c @@ -1,5 +1,5 @@ /* - Higher Level Interface to XML Parsers. + Wrapper interface to XML parser Copyright (C) 1999-2004, Joe Orton <joe@manyfish.co.uk> This library is free software; you can redistribute it and/or @@ -96,9 +96,11 @@ struct ne_xml_parser_s { struct element *current; /* current element in the branch */ struct handler *top_handlers; /* always points at the * handler on top of the stack. */ - int valid; /* non-zero whilst parse should continue */ + int failure; /* zero whilst parse should continue */ int prune; /* if non-zero, depth within a dead branch */ + int bom_pos; + #ifdef HAVE_EXPAT XML_Parser parser; char *encoding; @@ -193,6 +195,21 @@ const char *ne_xml_doc_encoding(const ne_xml_parser *p) #endif } +/* The first character of the REC-xml-names "NCName" rule excludes + * "Digit | '.' | '-' | '_' | CombiningChar | Extender"; the XML + * parser will not enforce this rule in a namespace declaration since + * it treats the entire attribute name as a REC-xml "Name" rule. It's + * too hard to check for all of CombiningChar | Digit | Extender here, + * but the valid_ncname_ch1 macro catches some of the rest. */ + +/* Return non-zero if 'ch' is an invalid start character for an NCName: */ +#define invalid_ncname_ch1(ch) ((ch) == '\0' || strchr("-.0123456789", (ch)) != NULL) + +/* Subversion repositories have been deployed which use property names + * marshalled as NCNames including a colon character; these should + * also be rejected but will be allowed for the time being. */ +#define invalid_ncname(xn) (invalid_ncname_ch1((xn)[0])) + /* Extract the namespace prefix declarations from 'atts'. */ static int declare_nspaces(ne_xml_parser *p, struct element *elm, const ne_xml_char **atts) @@ -200,13 +217,15 @@ static int declare_nspaces(ne_xml_parser *p, struct element *elm, int n; for (n = 0; atts && atts[n]; n += 2) { - if (strcasecmp(atts[n], "xmlns") == 0) { + if (strcmp(atts[n], "xmlns") == 0) { /* New default namespace */ elm->default_ns = ne_strdup(atts[n+1]); - } else if (strncasecmp(atts[n], "xmlns:", 6) == 0) { + } else if (strncmp(atts[n], "xmlns:", 6) == 0) { struct namespace *ns; - if (atts[n][6] == '\0' || atts[n+1][0] == '\0') { + /* Reject some invalid NCNames as namespace prefix, and an + * empty URI as the namespace URI */ + if (invalid_ncname(atts[n] + 6) || atts[n+1][0] == '\0') { ne_snprintf(p->error, ERR_SIZE, ("XML parse error at line %d: invalid namespace " "declaration"), ne_xml_currentline(p)); @@ -243,22 +262,20 @@ static int expand_qname(ne_xml_parser *p, struct element *elm, elm->name = ne_strdup(qname); elm->nspace = e->default_ns; + } else if (invalid_ncname(pfx + 1) || qname == pfx) { + ne_snprintf(p->error, ERR_SIZE, + _("XML parse error at line %d: invalid element name"), + ne_xml_currentline(p)); + return -1; } else { const char *uri = resolve_nspace(elm, qname, pfx-qname); if (uri) { - /* The name is everything after the ':' */ - if (pfx[1] == '\0') { - ne_snprintf(p->error, ERR_SIZE, - ("XML parse error at line %d: element name missing" - "after namespace prefix"), ne_xml_currentline(p)); - return -1; - } elm->name = ne_strdup(pfx+1); elm->nspace = uri; } else { ne_snprintf(p->error, ERR_SIZE, - ("XML parse error at line %d: undeclared namespace"), + ("XML parse error at line %d: undeclared namespace prefix"), ne_xml_currentline(p)); return -1; } @@ -275,7 +292,7 @@ static void start_element(void *userdata, const ne_xml_char *name, struct handler *hand; int state = NE_XML_DECLINE; - if (!p->valid) return; + if (p->failure) return; if (p->prune) { p->prune++; @@ -288,7 +305,7 @@ static void start_element(void *userdata, const ne_xml_char *name, p->current = elm; if (declare_nspaces(p, elm, atts) || expand_qname(p, elm, name)) { - p->valid = 0; + p->failure = 1; return; } @@ -308,8 +325,8 @@ static void start_element(void *userdata, const ne_xml_char *name, else if (state == NE_XML_DECLINE) /* prune this branch. */ p->prune++; - else /* state == NE_XML_ABORT */ - p->valid = 0; + else /* state < 0 => abort parse */ + p->failure = state; } /* Destroys an element structure. */ @@ -337,12 +354,11 @@ static void char_data(void *userdata, const ne_xml_char *data, int len) ne_xml_parser *p = userdata; struct element *elm = p->current; - if (!p->valid || p->prune) return; + if (p->failure || p->prune) return; - if (elm->handler->cdata_cb && - elm->handler->cdata_cb(elm->handler->userdata, elm->state, data, len)) { - NE_DEBUG(NE_DBG_XML, "Cdata callback failed.\n"); - p->valid = 0; + if (elm->handler->cdata_cb) { + p->failure = elm->handler->cdata_cb(elm->handler->userdata, elm->state, data, len); + NE_DEBUG(NE_DBG_XML, "Cdata callback returned %d.\n", p->failure); } } @@ -352,15 +368,17 @@ static void end_element(void *userdata, const ne_xml_char *name) ne_xml_parser *p = userdata; struct element *elm = p->current; - if (!p->valid) return; + if (p->failure) return; if (p->prune) { if (p->prune-- > 1) return; - } else if (elm->handler->endelm_cb && - elm->handler->endelm_cb(elm->handler->userdata, elm->state, - elm->nspace, elm->name)) { - NE_DEBUG(NE_DBG_XML, "XML: end-element for %d failed.\n", elm->state); - p->valid = 0; + } else if (elm->handler->endelm_cb) { + p->failure = elm->handler->endelm_cb(elm->handler->userdata, elm->state, + elm->nspace, elm->name); + if (p->failure) { + NE_DEBUG(NE_DBG_XML, "XML: end-element for %d failed with %d.\n", + elm->state, p->failure); + } } NE_DEBUG(NE_DBG_XMLPARSE, "XML: end-element (%d, {%s, %s})\n", @@ -397,8 +415,6 @@ static const char *resolve_nspace(const struct element *elm, ne_xml_parser *ne_xml_create(void) { ne_xml_parser *p = ne_calloc(sizeof *p); - /* Initialize other stuff */ - p->valid = 1; /* Placeholder for the root element */ p->current = p->root = ne_calloc(sizeof *p->root); p->root->default_ns = ""; @@ -448,24 +464,22 @@ void ne_xml_push_handler(ne_xml_parser *p, } } -void ne_xml_parse_v(void *userdata, const char *block, size_t len) +int ne_xml_parse_v(void *userdata, const char *block, size_t len) { ne_xml_parser *p = userdata; - /* FIXME: The two XML parsers break all our nice abstraction by - * choosing different char *'s. The swine. This cast will come - * back and bite us someday, no doubt. */ - ne_xml_parse(p, block, len); + return ne_xml_parse(p, (const ne_xml_char *)block, len); } -/* Parse the given block of input of length len */ -void ne_xml_parse(ne_xml_parser *p, const char *block, size_t len) +#define BOM_UTF8 "\xEF\xBB\xBF" /* UTF-8 BOM */ + +int ne_xml_parse(ne_xml_parser *p, const char *block, size_t len) { int ret, flag; /* duck out if it's broken */ - if (!p->valid) { + if (p->failure) { NE_DEBUG(NE_DBG_XML, "Not parsing %" NE_FMT_SIZE_T " bytes.\n", len); - return; + return p->failure; } if (len == 0) { flag = -1; @@ -476,34 +490,54 @@ void ne_xml_parse(ne_xml_parser *p, const char *block, size_t len) len); flag = 0; } - /* Note, don't write a parser error if !p->valid, since an error + + if (p->bom_pos < 3) { + while (len > 0 && p->bom_pos < 3 && + block[0] == BOM_UTF8[p->bom_pos]) { + block++; + len--; + p->bom_pos++; + } + if (len == 0) + return 0; + if (p->bom_pos == 0) { + p->bom_pos = 3; /* no BOM */ + } else if (p->bom_pos > 0 && p->bom_pos < 3) { + strcpy(p->error, _("Invalid Byte Order Mark")); + return p->failure = 1; + } + } + + /* Note, don't write a parser error if p->failure, since an error * will already have been written in that case. */ #ifdef HAVE_EXPAT ret = XML_Parse(p->parser, block, len, flag); NE_DEBUG(NE_DBG_XMLPARSE, "XML_Parse returned %d\n", ret); - if (ret == 0 && p->valid) { + if (ret == 0 && p->failure == 0) { ne_snprintf(p->error, ERR_SIZE, "XML parse error at line %d: %s", XML_GetCurrentLineNumber(p->parser), XML_ErrorString(XML_GetErrorCode(p->parser))); - p->valid = 0; + p->failure = 1; } #else ret = xmlParseChunk(p->parser, block, len, flag); NE_DEBUG(NE_DBG_XMLPARSE, "xmlParseChunk returned %d\n", ret); /* Parse errors are normally caught by the sax_error() callback, * which clears p->valid. */ - if (p->parser->errNo && p->valid) { + if (p->parser->errNo && p->failure == 0) { ne_snprintf(p->error, ERR_SIZE, "XML parse error at line %d.", ne_xml_currentline(p)); - p->valid = 0; + p->failure = 1; + NE_DEBUG(NE_DBG_XMLPARSE, "XML parse error: %s\n", p->error); } #endif + return p->failure; } -int ne_xml_valid(ne_xml_parser *p) +int ne_xml_failed(ne_xml_parser *p) { - return p->valid; + return p->failure; } void ne_xml_destroy(ne_xml_parser *p) @@ -553,11 +587,12 @@ static void sax_error(void *ctx, const char *msg, ...) ne_vsnprintf(buf, 1024, msg, ap); va_end(ap); - ne_snprintf(p->error, ERR_SIZE, - _("XML parse error at line %d: %s."), - p->parser->input->line, buf); - - p->valid = 0; + if (p->failure == 0) { + ne_snprintf(p->error, ERR_SIZE, + _("XML parse error at line %d: %s."), + p->parser->input->line, buf); + p->failure = 1; + } } #endif diff --git a/neon/src/ne_xml.h b/neon/src/ne_xml.h index 58cde9acb..50b350a8f 100644 --- a/neon/src/ne_xml.h +++ b/neon/src/ne_xml.h @@ -1,6 +1,6 @@ /* neon XML parser interface - Copyright (C) 1999-2003, Joe Orton <joe@manyfish.co.uk> + Copyright (C) 1999-2004, Joe Orton <joe@manyfish.co.uk> This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public @@ -86,31 +86,38 @@ void ne_xml_push_handler(ne_xml_parser *p, ne_xml_endelm_cb *endelm, void *userdata); -/* Returns non-zero if the parse was valid, zero if it failed (e.g., - * any of the callbacks failed, the XML was not well-formed, etc). - * Use ne_xml_get_error to retrieve the error message if it failed. */ -int ne_xml_valid(ne_xml_parser *p); +/* ne_xml_failed returns non-zero if there was an error during + * parsing, or zero if the parse completed successfully. The return + * value is equal to that of the last ne_xml_parse call for this + * parser. */ +int ne_xml_failed(ne_xml_parser *p); + +/* Set error string for parser: the string may be truncated. */ +void ne_xml_set_error(ne_xml_parser *p, const char *msg); + +/* Return the error string (never NULL). After ne_xml_failed returns + * >0, this will describe the parse error. Otherwise it will be a + * default error string. */ +const char *ne_xml_get_error(ne_xml_parser *p); /* Destroy the parser object. */ void ne_xml_destroy(ne_xml_parser *p); -/* Parse the given block of input of length len. Block does not need - * to be NUL-terminated. */ -void ne_xml_parse(ne_xml_parser *p, const char *block, size_t len); +/* Parse the given block of input of length len. Parser must be + * called with len=0 to signify the end of the document (for that + * case, the block argument is ignored). Returns zero on success, or + * non-zero on error: for an XML syntax error, a positive number is + * returned; if parsing is aborted by a caller-supplied callback, that + * callback's return value is returned. */ +int ne_xml_parse(ne_xml_parser *p, const char *block, size_t len); /* As above, casting (ne_xml_parser *)userdata internally. * (This function can be passed to ne_add_response_body_reader) */ -void ne_xml_parse_v(void *userdata, const char *block, size_t len); +int ne_xml_parse_v(void *userdata, const char *block, size_t len); /* Return current parse line for errors */ int ne_xml_currentline(ne_xml_parser *p); -/* Set error string for parser. */ -void ne_xml_set_error(ne_xml_parser *p, const char *msg); - -/* Return the error string for the parser and never NULL. */ -const char *ne_xml_get_error(ne_xml_parser *p); - /* From a start_element callback which was passed 'attrs' using given * parser, return attribute of given name and namespace. If nspace is * NULL, no namespace resolution is performed. */ diff --git a/neon/test/ChangeLog b/neon/test/ChangeLog index 080a87fcc..d484dedfa 100644 --- a/neon/test/ChangeLog +++ b/neon/test/ChangeLog @@ -1,6 +1,25 @@ +Wed Aug 25 21:05:28 2004 Joe Orton <joe@manyfish.co.uk> + + * cookies.c: Removed. + + * Makefile.in: Updated. + +Wed Aug 25 19:47:28 2004 Joe Orton <joe@manyfish.co.uk> + + * socket.c (do_connect, begin): Simplify do_connect use. + +Wed Aug 25 18:28:19 2004 Joe Orton <joe@manyfish.co.uk> + + * xml.c (matches, fail_parse): Test for UTF-8 BOM handling. + +Mon Jul 5 18:41:07 2004 Joe Orton <joe@manyfish.co.uk> + + * basic.c (content_type): Test for correct default charset for + text/xml content-type by RFC3280. + Mon Jul 5 10:59:17 2004 Joe Orton <joe@manyfish.co.uk> - Add regression tests for trio of ne_compress.c bugs: + Add XFAIL regression tests for trio of ne_compress.c bugs: * compress.c (reader): Validate that a size=0 call comes only after the expected response data, and use struct string. @@ -16,12 +35,6 @@ Sun Jul 4 21:55:00 2004 Joe Orton <joe@manyfish.co.uk> * sockets.c: All callers updated. -Tue May 18 21:00:07 2004 Joe Orton <joe@manyfish.co.uk> - - * ssl.c (fail_ssl_request): - * basic.c (do_range): Fix false negatives from gcc - -Wformat-security. - Sun May 2 21:16:45 2004 Joe Orton <joe@manyfish.co.uk> * util-tests.c (regress_dates): Add regression tests. @@ -31,6 +44,39 @@ Wed Apr 14 10:45:43 2004 Joe Orton <joe@manyfish.co.uk> * props.c (regress, patch_regress): Add regression tests for CAN-2004-0179 issues. +Thu Apr 8 13:57:04 2004 Joe Orton <joe@manyfish.co.uk> + + * largefile.c (read_large_response): Go faster: turn off debugging + during request dispatch. + +Wed Apr 7 13:39:37 2004 Joe Orton <joe@manyfish.co.uk> + + * auth.c (basic): Add some multi-scheme challenges. + +Wed Apr 7 13:14:16 2004 Joe Orton <joe@manyfish.co.uk> + + * request.c (s_progress): Use NE_FMT_OFF_T for printing off_t's + throughout. + +Sun Feb 22 23:38:05 2004 Joe Orton <joe@manyfish.co.uk> + + * request.c (expect_100_once, serve_100_once): Adjust for new + 100-continue interface. + (expect_100_nobody): New test. + +Sun Feb 22 20:39:15 2004 Joe Orton <joe@manyfish.co.uk> + + * cookies.c (parsing): Use ne_cookie_empty_cache. + +Sun Feb 22 17:28:41 2004 Joe Orton <joe@manyfish.co.uk> + + * props.c (pfind_simple): Test for whitespace handling. + +Sun Feb 22 16:31:52 2004 Joe Orton <joe@manyfish.co.uk> + + * auth.c (basic): Test handling of Basic challenge in presence of + multiple challenges. + Sun Feb 15 12:34:13 2004 Joe Orton <joe@manyfish.co.uk> * makekeys.sh, openssl.conf: Create new utf8subj.cert, @@ -39,10 +85,67 @@ Sun Feb 15 12:34:13 2004 Joe Orton <joe@manyfish.co.uk> * ssl.c (dname_readable): Test that ne_ssl_readable_dname always gives back UTF-8. +Sat Feb 14 21:59:17 2004 Joe Orton <joe@manyfish.co.uk> + + * xml.c (fail_parse): Add tests for invalid NCNames in namespace + prefix declaration and as element names. + +Sun Jan 25 15:21:56 2004 Joe Orton <joe@manyfish.co.uk> + + * largefile.c (serve_large_response, read_large_response): New + test. + +Sat Jan 24 18:10:14 2004 Joe Orton <joe@manyfish.co.uk> + + * Makefile.in: Fix test suite for 'make' implementatinos which + don't handle single-suffix inference rules. + +Sat Jan 3 14:10:14 2004 Joe Orton <joe@manyfish.co.uk> + + * largefile.c (send_high_offset): Renamed from send_large_file. + +Sat Jan 3 13:57:16 2004 Joe Orton <joe@manyfish.co.uk> + + * Makefile.in (BASIC_TESTS): Add cookies. + +Thu Jan 1 17:42:30 2004 Joe Orton <joe@manyfish.co.uk> + + * util-tests.c (support): Check for NE_FEATURE_LFS. + + * largefile.c: New file. + + * Makefile.in: Add lfs-check, largefile, largefile.lo targets. + +Sat Nov 15 08:04:22 2003 Joe Orton <joe@manyfish.co.uk> + + * request.c (idna_hostname, dup_header, serve_check_host): New + test. + +Fri Nov 14 14:06:57 2003 Joe Orton <joe@manyfish.co.uk> + + * util-tests.c (support): Test for NE_FEATURE_IDNA. + +Fri Nov 14 11:26:29 2003 Joe Orton <joe@manyfish.co.uk> + + * acl.c: Remove NEON_NODAV condition. + + * stubs.c: Use new NE_HAVE_* conditions. + + * util-tests.c (supports): Test new ne_has_support interface. + Thu Nov 13 20:33:44 2003 Joe Orton <joe@manyfish.co.uk> * request.c (no_body_205): New test. +Tue Nov 11 20:36:43 2003 Joe Orton <joe@manyfish.co.uk> + + Adjust for ne_xml_valid->ne_xml_failure API change. + + * xml.c (chardata, startelm_abort, endelm_abort, parse_match): + Check for propagation of negative failure codes. + (fail_parse): Check for positive failure code. + (attributes): Use ne_xml_failure. + Sat Oct 25 00:11:29 2003 Joe Orton <joe@manyfish.co.uk> * ssl.c (fail_truncated_eof): Remove test. diff --git a/neon/test/Makefile.in b/neon/test/Makefile.in index 9d89e425e..b49fa550e 100644 --- a/neon/test/Makefile.in +++ b/neon/test/Makefile.in @@ -19,10 +19,11 @@ CC = @CC@ OPENSSL = @OPENSSL@ HELPERS = @HELPERS@ -BASIC_TESTS = uri-tests util-tests string-tests session socket \ - request auth basic stubs redirect +BASIC_TESTS = uri-tests util-tests string-tests socket \ + session request auth basic stubs redirect ZLIB_TESTS = compress -ZLIB_HELPERS = file1.gz file2.gz trailing.gz badcsum.gz truncated.gz corrupt1.gz corrupt2.gz +ZLIB_HELPERS = file1.gz file2.gz trailing.gz badcsum.gz truncated.gz \ + corrupt1.gz corrupt2.gz empty.gz DAV_TESTS = xml acl props lock SSL_TESTS = socket-ssl ssl SSL_HELPERS = ca-stamp @@ -52,7 +53,7 @@ clean: rm -f $(TESTS) $(HELPERS) *.*o common/*.*o libtest.a *.log rm -rf ca .libs rm -f ca-stamp client.key *.csr ssigned.pem wrongcn.pem \ - server.cert client.cert client.p12 *.cert + server.cert client.cert client.p12 *.cert sparse.bin check: $(TESTS) $(HELPERS) @SRCDIR=$(srcdir) $(SHELL) $(srcdir)/run.sh $(TESTS) @@ -60,6 +61,9 @@ check: $(TESTS) $(HELPERS) grind: $(TESTS) $(HELPERS) @SRCDIR=$(srcdir) HARNESS="$(VALGRIND)" $(SHELL) $(srcdir)/run.sh $(TESTS) +lfs-check: largefile $(LFS_HELPERS) + @SRCDIR=$(srcdir) $(SHELL) $(srcdir)/run.sh largefile + NEWS = $(top_srcdir)/NEWS file1.gz: $(NEWS) @@ -87,6 +91,9 @@ corrupt1.gz: file1.gz corrupt2.gz: cat $(NEWS) > $@ +empty.gz: + touch $@ + # Dummy target to create the CA keys etc. makekeys stderr is redirected # since it changes for every invocation; not helpful for regression # testing. @@ -107,9 +114,6 @@ $(LIBTEST): $(LIBOBJS) .c.lo: $(COMPILE) -c $< -o $@ -.lo: - $(LINK) -o $@ $< $(LIBS) - # Recompile socket.c with SOCKET_SSL defined socket-ssl.lo: $(srcdir)/socket.c $(HDRS) $(COMPILE) -DSOCKET_SSL -c $(srcdir)/socket.c -o $@ @@ -120,6 +124,9 @@ socket-ssl: socket-ssl.lo $(LIBTEST) resolve: resolve.lo $(LIBNEON) $(LINK) -o $@ resolve.lo $(LIBNEON) +common/tests.lo: $(srcdir)/common/tests.c $(OBJDEPS) +common/child.lo: $(srcdir)/common/child.c $(OBJDEPS) +utils.lo: $(srcdir)/utils.c $(OBJDEPS) auth.lo: $(srcdir)/auth.c $(OBJDEPS) uri-tests.lo: $(srcdir)/uri-tests.c $(OBJDEPS) util-tests.lo: $(srcdir)/util-tests.c $(OBJDEPS) @@ -137,26 +144,46 @@ session.lo: $(srcdir)/session.c $(OBJDEPS) redirect.lo: $(srcdir)/redirect.c $(OBJDEPS) basic.lo: $(srcdir)/basic.c $(OBJDEPS) ssl.lo: $(srcdir)/ssl.c $(OBJDEPS) -cookies.lo: $(srcdir)/cookies.c $(OBJDEPS) +lock.lo: $(srcdir)/lock.c $(OBJDEPS) +largefile.lo: $(srcdir)/largefile.c $(OBJDEPS) auth: auth.lo $(DEPS) + $(LINK) -o $@ auth.lo $(DEPS) basic: basic.lo $(DEPS) + $(LINK) -o $@ basic.lo $(DEPS) uri-tests: uri-tests.lo $(DEPS) + $(LINK) -o $@ uri-tests.lo $(DEPS) util-tests: util-tests.lo $(DEPS) + $(LINK) -o $@ util-tests.lo $(DEPS) string-tests: string-tests.lo $(DEPS) + $(LINK) -o $@ string-tests.lo $(DEPS) socket: socket.lo $(DEPS) + $(LINK) -o $@ socket.lo $(DEPS) server: server.lo $(DEPS) + $(LINK) -o $@ server.lo $(DEPS) request: request.lo $(DEPS) + $(LINK) -o $@ request.lo $(DEPS) regress: regress.lo $(DEPS) + $(LINK) -o $@ regress.lo $(DEPS) compress: compress.lo $(DEPS) + $(LINK) -o $@ compress.lo $(DEPS) acl: acl.lo $(DEPS) + $(LINK) -o $@ acl.lo $(DEPS) utils: utils.lo $(DEPS) + $(LINK) -o $@ utils.lo $(DEPS) stubs: stubs.lo $(DEPS) + $(LINK) -o $@ stubs.lo $(DEPS) props: props.lo $(DEPS) + $(LINK) -o $@ props.lo $(DEPS) session: session.lo $(DEPS) + $(LINK) -o $@ session.lo $(DEPS) redirect: redirect.lo $(DEPS) -basic: basic.lo $(DEPS) + $(LINK) -o $@ redirect.lo $(DEPS) ssl: ssl.lo $(DEPS) + $(LINK) -o $@ ssl.lo $(DEPS) xml: xml.lo $(DEPS) + $(LINK) -o $@ xml.lo $(DEPS) lock: lock.lo $(DEPS) -cookies: cookies.lo $(DEPS) + $(LINK) -o $@ lock.lo $(DEPS) +largefile: largefile.lo $(DEPS) + $(LINK) -o $@ largefile.lo $(DEPS) diff --git a/neon/test/acl.c b/neon/test/acl.c index 14008b16a..2e72caa80 100644 --- a/neon/test/acl.c +++ b/neon/test/acl.c @@ -24,18 +24,6 @@ #include "child.h" #include "utils.h" -#ifdef NEON_NODAV - -static int skip(void) -{ - t_context("built without WebDAV support"); - return SKIP; -} - -ne_test tests[] = { T(skip), T(NULL) }; - -#else - /**** DUMMY TESTS: just makes sure the stuff doesn't dump core. */ static int test_acl(const char *uri, ne_acl_entry *es, int nume) @@ -111,5 +99,3 @@ ne_test tests[] = { T(deny_byprop), T(NULL) }; - -#endif /* NEON_NODAV */ diff --git a/neon/test/auth.c b/neon/test/auth.c index 30fe88eb7..7f7e99379 100644 --- a/neon/test/auth.c +++ b/neon/test/auth.c @@ -1,6 +1,6 @@ /* Authentication tests - Copyright (C) 2001-2003, Joe Orton <joe@manyfish.co.uk> + Copyright (C) 2001-2004, Joe Orton <joe@manyfish.co.uk> This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by @@ -40,11 +40,16 @@ static const char username[] = "Aladdin", password[] = "open sesame"; static int auth_failed; -static const char www_wally[] = "WWW-Authenticate: Basic realm=WallyWorld"; +#define BASIC_WALLY "Basic realm=WallyWorld" +#define CHAL_WALLY "WWW-Authenticate: " BASIC_WALLY static int auth_cb(void *userdata, const char *realm, int tries, char *un, char *pw) { + if (strcmp(realm, "WallyWorld")) { + NE_DEBUG(NE_DBG_HTTP, "Got wrong realm '%s'!\n", realm); + return -1; + } strcpy(un, username); strcpy(pw, password); return tries; @@ -87,6 +92,8 @@ static int send_response(ne_socket *sock, const char *hdr, int code, int eoc) * second doesn't. */ static int auth_serve(ne_socket *sock, void *userdata) { + char *hdr = userdata; + auth_failed = 1; /* Register globals for discard_request. */ @@ -94,7 +101,7 @@ static int auth_serve(ne_socket *sock, void *userdata) want_header = "Authorization"; discard_request(sock); - send_response(sock, www_wally, 401, 0); + send_response(sock, hdr, 401, 0); discard_request(sock); send_response(sock, NULL, auth_failed?500:200, 1); @@ -102,33 +109,71 @@ static int auth_serve(ne_socket *sock, void *userdata) return 0; } +/* Test that various Basic auth challenges are correctly handled. */ static int basic(void) { - ne_session *sess; - - CALL(make_session(&sess, auth_serve, NULL)); - ne_set_server_auth(sess, auth_cb, NULL); - - CALL(any_2xx_request(sess, "/norman")); + const char *hdrs[] = { + /* simplest case */ + CHAL_WALLY, + + /* several challenges, one header */ + "WWW-Authenticate: BarFooScheme, " BASIC_WALLY, + + /* several challenges, one header */ + CHAL_WALLY ", BarFooScheme realm=\"PenguinWorld\"", + + /* whitespace tests. */ + "WWW-Authenticate: Basic realm=WallyWorld ", + + /* nego test. */ + "WWW-Authenticate: Negotiate fish, Basic realm=WallyWorld", + + /* nego test. */ + "WWW-Authenticate: Negotiate fish, bar=boo, Basic realm=WallyWorld", + + /* multi-header case 1 */ + "WWW-Authenticate: BarFooScheme\r\n" + CHAL_WALLY, + + /* multi-header cases 1 */ + CHAL_WALLY "\r\n" + "WWW-Authenticate: BarFooScheme bar=\"foo\"", + + /* multi-header case 3 */ + "WWW-Authenticate: FooBarChall foo=\"bar\"\r\n" + CHAL_WALLY "\r\n" + "WWW-Authenticate: BarFooScheme bar=\"foo\"" + }; + size_t n; + + for (n = 0; n < sizeof(hdrs)/sizeof(hdrs[0]); n++) { + ne_session *sess; + + CALL(make_session(&sess, auth_serve, (void *)hdrs[n])); + ne_set_server_auth(sess, auth_cb, NULL); + + CALL(any_2xx_request(sess, "/norman")); + + ne_session_destroy(sess); + CALL(await_server()); + } - ne_session_destroy(sess); - CALL(await_server()); return OK; } static int retry_serve(ne_socket *sock, void *ud) { discard_request(sock); - send_response(sock, www_wally, 401, 0); + send_response(sock, CHAL_WALLY, 401, 0); discard_request(sock); - send_response(sock, www_wally, 401, 0); + send_response(sock, CHAL_WALLY, 401, 0); discard_request(sock); send_response(sock, NULL, 200, 0); discard_request(sock); - send_response(sock, www_wally, 401, 0); + send_response(sock, CHAL_WALLY, 401, 0); discard_request(sock); send_response(sock, NULL, 200, 0); @@ -140,19 +185,19 @@ static int retry_serve(ne_socket *sock, void *ud) send_response(sock, NULL, 200, 0); discard_request(sock); - send_response(sock, www_wally, 401, 0); + send_response(sock, CHAL_WALLY, 401, 0); discard_request(sock); send_response(sock, NULL, 200, 0); discard_request(sock); - send_response(sock, www_wally, 401, 0); + send_response(sock, CHAL_WALLY, 401, 0); discard_request(sock); - send_response(sock, www_wally, 401, 0); + send_response(sock, CHAL_WALLY, 401, 0); discard_request(sock); - send_response(sock, www_wally, 401, 0); + send_response(sock, CHAL_WALLY, 401, 0); discard_request(sock); send_response(sock, NULL, 200, 0); @@ -296,8 +341,8 @@ static int tunnel_regress(void) /* test digest auth 2617-style. */ -/* negotiation: within a single header, multiple headers. - * check digest has precedence */ +/* test that digest has precedence over Basic for multi-scheme + * challenges */ /* test auth-int, auth-int FAILURE. chunk trailers/non-trailer */ diff --git a/neon/test/basic.c b/neon/test/basic.c index 9ffe0e188..cabe2d248 100644 --- a/neon/test/basic.c +++ b/neon/test/basic.c @@ -46,8 +46,10 @@ static int content_type(void) { "foo/bar", "foo", "bar", NULL }, { "foo/bar ", "foo", "bar", NULL }, { "application/xml", "application", "xml", NULL }, - /* text/ subtypes default to charset ISO-8859-1. */ + /* text/ subtypes default to charset ISO-8859-1, per 2616. */ { "text/lemon", "text", "lemon", "ISO-8859-1" }, + /* text/xml defaults to charset us-ascii, per 3280 */ + { "text/xml", "text", "xml", "us-ascii" }, #undef TXU #define TXU "text", "xml", "utf-8" /* 2616 doesn't *say* that charset can be quoted, but bets are diff --git a/neon/test/ca1.pem b/neon/test/ca1.pem new file mode 100644 index 000000000..a52ebe44b --- /dev/null +++ b/neon/test/ca1.pem @@ -0,0 +1,15 @@ +-----BEGIN CERTIFICATE----- +MIICQTCCAeugAwIBAgIBADANBgkqhkiG9w0BAQQFADCBnzELMAkGA1UEBhMCR0Ix +FTATBgNVBAgTDExpbmNvbG5zaGlyZTEQMA4GA1UEBxMHTGluY29sbjERMA8GA1UE +ChMIQ0FzIEx0ZC4xGDAWBgNVBAsTD0ZpcnN0IFJhbmRvbSBDQTEaMBgGA1UEAxMR +Zmlyc3QuZXhhbXBsZS5jb20xHjAcBgkqhkiG9w0BCQEWD25lb25Ad2ViZGF2Lm9y +ZzAeFw0wNDEwMjkxMzEzNDdaFw0wNzA0MTcxMzEzNDdaMIGfMQswCQYDVQQGEwJH +QjEVMBMGA1UECBMMTGluY29sbnNoaXJlMRAwDgYDVQQHEwdMaW5jb2xuMREwDwYD +VQQKEwhDQXMgTHRkLjEYMBYGA1UECxMPRmlyc3QgUmFuZG9tIENBMRowGAYDVQQD +ExFmaXJzdC5leGFtcGxlLmNvbTEeMBwGCSqGSIb3DQEJARYPbmVvbkB3ZWJkYXYu +b3JnMFwwDQYJKoZIhvcNAQEBBQADSwAwSAJBAPNFTmxnz4JZA+8+SonD0qWgSBPY +WrNlH1FP+psm5EGZGmGJGvSDsk6HkyvstdopKF50UuEaJ263IorAhkmdGG0CAwEA +AaMQMA4wDAYDVR0TBAUwAwEB/zANBgkqhkiG9w0BAQQFAANBAOvtDu4Cs6nj5DG1 +NG5gqCkrVWqvjSd1jzsxOJBoN9gk/d87rAzNCvK4dTkFf9btdRZwv5OZFJzKLcU8 +f7qzMnM= +-----END CERTIFICATE----- diff --git a/neon/test/ca2.pem b/neon/test/ca2.pem new file mode 100644 index 000000000..d0708b993 --- /dev/null +++ b/neon/test/ca2.pem @@ -0,0 +1,15 @@ +-----BEGIN CERTIFICATE----- +MIICPzCCAemgAwIBAgIBADANBgkqhkiG9w0BAQQFADCBnjELMAkGA1UEBhMCR0Ix +ETAPBgNVBAgTCENvcm53YWxsMREwDwYDVQQHEwhGYWxtb3V0aDERMA8GA1UEChMI +Q0FzIEx0ZC4xGTAXBgNVBAsTEFNlY29uZCBSYW5kb20gQ0ExGzAZBgNVBAMTEnNl +Y29uZC5leGFtcGxlLmNvbTEeMBwGCSqGSIb3DQEJARYPbmVvbkB3ZWJkYXYub3Jn +MB4XDTA0MTAyOTEzMTM0N1oXDTA3MDQxNzEzMTM0N1owgZ4xCzAJBgNVBAYTAkdC +MREwDwYDVQQIEwhDb3Jud2FsbDERMA8GA1UEBxMIRmFsbW91dGgxETAPBgNVBAoT +CENBcyBMdGQuMRkwFwYDVQQLExBTZWNvbmQgUmFuZG9tIENBMRswGQYDVQQDExJz +ZWNvbmQuZXhhbXBsZS5jb20xHjAcBgkqhkiG9w0BCQEWD25lb25Ad2ViZGF2Lm9y +ZzBcMA0GCSqGSIb3DQEBAQUAA0sAMEgCQQDzRU5sZ8+CWQPvPkqJw9KloEgT2Fqz +ZR9RT/qbJuRBmRphiRr0g7JOh5Mr7LXaKShedFLhGidutyKKwIZJnRhtAgMBAAGj +EDAOMAwGA1UdEwQFMAMBAf8wDQYJKoZIhvcNAQEEBQADQQANIBQyploOs1JrmzZu +TATbjH6wsoEWdAUKs+UMuigRHskqK4j6PutoPWYFB+Ebxfh1miiaWS/KwFzWmPte +iNeK +-----END CERTIFICATE----- diff --git a/neon/test/ca3.pem b/neon/test/ca3.pem new file mode 100644 index 000000000..c445e497a --- /dev/null +++ b/neon/test/ca3.pem @@ -0,0 +1,14 @@ +-----BEGIN CERTIFICATE----- +MIICNzCCAeGgAwIBAgIBADANBgkqhkiG9w0BAQQFADCBmjELMAkGA1UEBhMCR0Ix +EDAOBgNVBAgTB1N1ZmZvbGsxEDAOBgNVBAcTB0lwc3dpY2gxETAPBgNVBAoTCENB +cyBMdGQuMRgwFgYDVQQLEw9UaGlyZCBSYW5kb20gQ0ExGjAYBgNVBAMTEXRoaXJk +LmV4YW1wbGUuY29tMR4wHAYJKoZIhvcNAQkBFg9uZW9uQHdlYmRhdi5vcmcwHhcN +MDQxMDI5MTMxMzQ3WhcNMDcwNDE3MTMxMzQ3WjCBmjELMAkGA1UEBhMCR0IxEDAO +BgNVBAgTB1N1ZmZvbGsxEDAOBgNVBAcTB0lwc3dpY2gxETAPBgNVBAoTCENBcyBM +dGQuMRgwFgYDVQQLEw9UaGlyZCBSYW5kb20gQ0ExGjAYBgNVBAMTEXRoaXJkLmV4 +YW1wbGUuY29tMR4wHAYJKoZIhvcNAQkBFg9uZW9uQHdlYmRhdi5vcmcwXDANBgkq +hkiG9w0BAQEFAANLADBIAkEA80VObGfPglkD7z5KicPSpaBIE9has2UfUU/6mybk +QZkaYYka9IOyToeTK+y12ikoXnRS4RonbrciisCGSZ0YbQIDAQABoxAwDjAMBgNV +HRMEBTADAQH/MA0GCSqGSIb3DQEBBAUAA0EAwL7CNh2/1USUCHz55X/5BmySC1UJ +mImoh0DD0LocIQJUjGsScm3M8HXarGxl6Vfkklt/k2TMAy6YCTv+1/dazw== +-----END CERTIFICATE----- diff --git a/neon/test/ca4.pem b/neon/test/ca4.pem new file mode 100644 index 000000000..d08509a2e --- /dev/null +++ b/neon/test/ca4.pem @@ -0,0 +1,14 @@ +-----BEGIN CERTIFICATE----- +MIICOzCCAeWgAwIBAgIBADANBgkqhkiG9w0BAQQFADCBnDELMAkGA1UEBhMCR0Ix +EDAOBgNVBAgTB05vcmZvbGsxEDAOBgNVBAcTB05vcndpY2gxETAPBgNVBAoTCENB +cyBMdGQuMRkwFwYDVQQLExBGb3VydGggUmFuZG9tIENBMRswGQYDVQQDExJmb3Vy +dGguZXhhbXBsZS5jb20xHjAcBgkqhkiG9w0BCQEWD25lb25Ad2ViZGF2Lm9yZzAe +Fw0wNDEwMjkxMzEzNDdaFw0wNzA0MTcxMzEzNDdaMIGcMQswCQYDVQQGEwJHQjEQ +MA4GA1UECBMHTm9yZm9sazEQMA4GA1UEBxMHTm9yd2ljaDERMA8GA1UEChMIQ0Fz +IEx0ZC4xGTAXBgNVBAsTEEZvdXJ0aCBSYW5kb20gQ0ExGzAZBgNVBAMTEmZvdXJ0 +aC5leGFtcGxlLmNvbTEeMBwGCSqGSIb3DQEJARYPbmVvbkB3ZWJkYXYub3JnMFww +DQYJKoZIhvcNAQEBBQADSwAwSAJBAPNFTmxnz4JZA+8+SonD0qWgSBPYWrNlH1FP ++psm5EGZGmGJGvSDsk6HkyvstdopKF50UuEaJ263IorAhkmdGG0CAwEAAaMQMA4w +DAYDVR0TBAUwAwEB/zANBgkqhkiG9w0BAQQFAANBAMxApCvWUMkO47bO/Cpy6ow2 +BTb8BQW+NeSXV3EIlu1IPVJ9e1pcfTtVP5z5bseeUQSMcRWwEbV3Aie+a5xsT9Y= +-----END CERTIFICATE----- diff --git a/neon/test/calist.pem b/neon/test/calist.pem new file mode 100644 index 000000000..2ea2e13dd --- /dev/null +++ b/neon/test/calist.pem @@ -0,0 +1,58 @@ +-----BEGIN CERTIFICATE----- +MIICQTCCAeugAwIBAgIBADANBgkqhkiG9w0BAQQFADCBnzELMAkGA1UEBhMCR0Ix +FTATBgNVBAgTDExpbmNvbG5zaGlyZTEQMA4GA1UEBxMHTGluY29sbjERMA8GA1UE +ChMIQ0FzIEx0ZC4xGDAWBgNVBAsTD0ZpcnN0IFJhbmRvbSBDQTEaMBgGA1UEAxMR +Zmlyc3QuZXhhbXBsZS5jb20xHjAcBgkqhkiG9w0BCQEWD25lb25Ad2ViZGF2Lm9y +ZzAeFw0wNDEwMjkxMzEzNDdaFw0wNzA0MTcxMzEzNDdaMIGfMQswCQYDVQQGEwJH +QjEVMBMGA1UECBMMTGluY29sbnNoaXJlMRAwDgYDVQQHEwdMaW5jb2xuMREwDwYD +VQQKEwhDQXMgTHRkLjEYMBYGA1UECxMPRmlyc3QgUmFuZG9tIENBMRowGAYDVQQD +ExFmaXJzdC5leGFtcGxlLmNvbTEeMBwGCSqGSIb3DQEJARYPbmVvbkB3ZWJkYXYu +b3JnMFwwDQYJKoZIhvcNAQEBBQADSwAwSAJBAPNFTmxnz4JZA+8+SonD0qWgSBPY +WrNlH1FP+psm5EGZGmGJGvSDsk6HkyvstdopKF50UuEaJ263IorAhkmdGG0CAwEA +AaMQMA4wDAYDVR0TBAUwAwEB/zANBgkqhkiG9w0BAQQFAANBAOvtDu4Cs6nj5DG1 +NG5gqCkrVWqvjSd1jzsxOJBoN9gk/d87rAzNCvK4dTkFf9btdRZwv5OZFJzKLcU8 +f7qzMnM= +-----END CERTIFICATE----- +-----BEGIN CERTIFICATE----- +MIICPzCCAemgAwIBAgIBADANBgkqhkiG9w0BAQQFADCBnjELMAkGA1UEBhMCR0Ix +ETAPBgNVBAgTCENvcm53YWxsMREwDwYDVQQHEwhGYWxtb3V0aDERMA8GA1UEChMI +Q0FzIEx0ZC4xGTAXBgNVBAsTEFNlY29uZCBSYW5kb20gQ0ExGzAZBgNVBAMTEnNl +Y29uZC5leGFtcGxlLmNvbTEeMBwGCSqGSIb3DQEJARYPbmVvbkB3ZWJkYXYub3Jn +MB4XDTA0MTAyOTEzMTM0N1oXDTA3MDQxNzEzMTM0N1owgZ4xCzAJBgNVBAYTAkdC +MREwDwYDVQQIEwhDb3Jud2FsbDERMA8GA1UEBxMIRmFsbW91dGgxETAPBgNVBAoT +CENBcyBMdGQuMRkwFwYDVQQLExBTZWNvbmQgUmFuZG9tIENBMRswGQYDVQQDExJz +ZWNvbmQuZXhhbXBsZS5jb20xHjAcBgkqhkiG9w0BCQEWD25lb25Ad2ViZGF2Lm9y +ZzBcMA0GCSqGSIb3DQEBAQUAA0sAMEgCQQDzRU5sZ8+CWQPvPkqJw9KloEgT2Fqz +ZR9RT/qbJuRBmRphiRr0g7JOh5Mr7LXaKShedFLhGidutyKKwIZJnRhtAgMBAAGj +EDAOMAwGA1UdEwQFMAMBAf8wDQYJKoZIhvcNAQEEBQADQQANIBQyploOs1JrmzZu +TATbjH6wsoEWdAUKs+UMuigRHskqK4j6PutoPWYFB+Ebxfh1miiaWS/KwFzWmPte +iNeK +-----END CERTIFICATE----- +-----BEGIN CERTIFICATE----- +MIICNzCCAeGgAwIBAgIBADANBgkqhkiG9w0BAQQFADCBmjELMAkGA1UEBhMCR0Ix +EDAOBgNVBAgTB1N1ZmZvbGsxEDAOBgNVBAcTB0lwc3dpY2gxETAPBgNVBAoTCENB +cyBMdGQuMRgwFgYDVQQLEw9UaGlyZCBSYW5kb20gQ0ExGjAYBgNVBAMTEXRoaXJk +LmV4YW1wbGUuY29tMR4wHAYJKoZIhvcNAQkBFg9uZW9uQHdlYmRhdi5vcmcwHhcN +MDQxMDI5MTMxMzQ3WhcNMDcwNDE3MTMxMzQ3WjCBmjELMAkGA1UEBhMCR0IxEDAO +BgNVBAgTB1N1ZmZvbGsxEDAOBgNVBAcTB0lwc3dpY2gxETAPBgNVBAoTCENBcyBM +dGQuMRgwFgYDVQQLEw9UaGlyZCBSYW5kb20gQ0ExGjAYBgNVBAMTEXRoaXJkLmV4 +YW1wbGUuY29tMR4wHAYJKoZIhvcNAQkBFg9uZW9uQHdlYmRhdi5vcmcwXDANBgkq +hkiG9w0BAQEFAANLADBIAkEA80VObGfPglkD7z5KicPSpaBIE9has2UfUU/6mybk +QZkaYYka9IOyToeTK+y12ikoXnRS4RonbrciisCGSZ0YbQIDAQABoxAwDjAMBgNV +HRMEBTADAQH/MA0GCSqGSIb3DQEBBAUAA0EAwL7CNh2/1USUCHz55X/5BmySC1UJ +mImoh0DD0LocIQJUjGsScm3M8HXarGxl6Vfkklt/k2TMAy6YCTv+1/dazw== +-----END CERTIFICATE----- +-----BEGIN CERTIFICATE----- +MIICOzCCAeWgAwIBAgIBADANBgkqhkiG9w0BAQQFADCBnDELMAkGA1UEBhMCR0Ix +EDAOBgNVBAgTB05vcmZvbGsxEDAOBgNVBAcTB05vcndpY2gxETAPBgNVBAoTCENB +cyBMdGQuMRkwFwYDVQQLExBGb3VydGggUmFuZG9tIENBMRswGQYDVQQDExJmb3Vy +dGguZXhhbXBsZS5jb20xHjAcBgkqhkiG9w0BCQEWD25lb25Ad2ViZGF2Lm9yZzAe +Fw0wNDEwMjkxMzEzNDdaFw0wNzA0MTcxMzEzNDdaMIGcMQswCQYDVQQGEwJHQjEQ +MA4GA1UECBMHTm9yZm9sazEQMA4GA1UEBxMHTm9yd2ljaDERMA8GA1UEChMIQ0Fz +IEx0ZC4xGTAXBgNVBAsTEEZvdXJ0aCBSYW5kb20gQ0ExGzAZBgNVBAMTEmZvdXJ0 +aC5leGFtcGxlLmNvbTEeMBwGCSqGSIb3DQEJARYPbmVvbkB3ZWJkYXYub3JnMFww +DQYJKoZIhvcNAQEBBQADSwAwSAJBAPNFTmxnz4JZA+8+SonD0qWgSBPYWrNlH1FP ++psm5EGZGmGJGvSDsk6HkyvstdopKF50UuEaJ263IorAhkmdGG0CAwEAAaMQMA4w +DAYDVR0TBAUwAwEB/zANBgkqhkiG9w0BAQQFAANBAMxApCvWUMkO47bO/Cpy6ow2 +BTb8BQW+NeSXV3EIlu1IPVJ9e1pcfTtVP5z5bseeUQSMcRWwEbV3Aie+a5xsT9Y= +-----END CERTIFICATE----- diff --git a/neon/test/chain.pem b/neon/test/chain.pem new file mode 100644 index 000000000..fbaf061ac --- /dev/null +++ b/neon/test/chain.pem @@ -0,0 +1,56 @@ +-----BEGIN CERTIFICATE----- +MIICNzCCAeGgAwIBAgIBADANBgkqhkiG9w0BAQQFADCBmjELMAkGA1UEBhMCVVMx +EzARBgNVBAgTCkNhbGlmb3JuaWExEDAOBgNVBAcTB09ha2xhbmQxEDAOBgNVBAoT +B05lb3NpZ24xFDASBgNVBAsTC1JhbmRvbSBEZXB0MRwwGgYDVQQDExNub3doZXJl +LmV4YW1wbGUuY29tMR4wHAYJKoZIhvcNAQkBFg9uZW9uQHdlYmRhdi5vcmcwHhcN +MDQxMDI5MTMxMzQ3WhcNMDcwNDE3MTMxMzQ3WjCBmjELMAkGA1UEBhMCVVMxEzAR +BgNVBAgTCkNhbGlmb3JuaWExEDAOBgNVBAcTB09ha2xhbmQxEDAOBgNVBAoTB05l +b3NpZ24xFDASBgNVBAsTC1JhbmRvbSBEZXB0MRwwGgYDVQQDExNub3doZXJlLmV4 +YW1wbGUuY29tMR4wHAYJKoZIhvcNAQkBFg9uZW9uQHdlYmRhdi5vcmcwXDANBgkq +hkiG9w0BAQEFAANLADBIAkEAxZZK/iqR8mWCs0u3A4Vz58fik0B2RpVLF8LFmXuu +XCDNR4bjCwcOJpd+zpHrxb7LKaUYIA9Y3g1i90qylbgJBwIDAQABoxAwDjAMBgNV +HRMEBTADAQH/MA0GCSqGSIb3DQEBBAUAA0EAEGXxwEqcoz3uXP6/7QqmMhxDhrce +xAGgVRXo546dK9XW3/E6HL7vM+euPnsF/Anhjv5PSRG5pLTy2hDE+r/QuQ== +-----END CERTIFICATE----- +Certificate: + Data: + Version: 3 (0x2) + Serial Number: 2 (0x2) + Signature Algorithm: md5WithRSAEncryption + Issuer: C=US, ST=California, L=Oakland, O=Neosign, OU=Random Dept, CN=nowhere.example.com/emailAddress=neon@webdav.org + Validity + Not Before: Oct 29 13:13:48 2004 GMT + Not After : Apr 17 13:13:48 2007 GMT + Subject: C=GB, ST=Cambridgeshire, L=Cambridge, O=Neon Hackers Ltd, OU=Neon QA Dept, CN=localhost/emailAddress=neon@webdav.org + Subject Public Key Info: + Public Key Algorithm: rsaEncryption + RSA Public Key: (512 bit) + Modulus (512 bit): + 00:f3:45:4e:6c:67:cf:82:59:03:ef:3e:4a:89:c3: + d2:a5:a0:48:13:d8:5a:b3:65:1f:51:4f:fa:9b:26: + e4:41:99:1a:61:89:1a:f4:83:b2:4e:87:93:2b:ec: + b5:da:29:28:5e:74:52:e1:1a:27:6e:b7:22:8a:c0: + 86:49:9d:18:6d + Exponent: 65537 (0x10001) + X509v3 extensions: + X509v3 Basic Constraints: + CA:FALSE + Signature Algorithm: md5WithRSAEncryption + 7e:43:47:e7:a6:40:a3:c0:08:ac:79:15:8b:f3:6f:50:5b:27: + a4:10:8d:61:b8:17:00:95:1e:bf:93:30:d6:19:99:b1:d6:a2: + ef:25:b4:c8:c0:06:0b:84:05:af:ae:75:f1:01:6a:83:b9:01: + 35:01:11:8f:8b:35:5b:dc:3b:09 +-----BEGIN CERTIFICATE----- +MIICOjCCAeSgAwIBAgIBAjANBgkqhkiG9w0BAQQFADCBmjELMAkGA1UEBhMCVVMx +EzARBgNVBAgTCkNhbGlmb3JuaWExEDAOBgNVBAcTB09ha2xhbmQxEDAOBgNVBAoT +B05lb3NpZ24xFDASBgNVBAsTC1JhbmRvbSBEZXB0MRwwGgYDVQQDExNub3doZXJl +LmV4YW1wbGUuY29tMR4wHAYJKoZIhvcNAQkBFg9uZW9uQHdlYmRhdi5vcmcwHhcN +MDQxMDI5MTMxMzQ4WhcNMDcwNDE3MTMxMzQ4WjCBoDELMAkGA1UEBhMCR0IxFzAV +BgNVBAgTDkNhbWJyaWRnZXNoaXJlMRIwEAYDVQQHEwlDYW1icmlkZ2UxGTAXBgNV +BAoTEE5lb24gSGFja2VycyBMdGQxFTATBgNVBAsTDE5lb24gUUEgRGVwdDESMBAG +A1UEAxMJbG9jYWxob3N0MR4wHAYJKoZIhvcNAQkBFg9uZW9uQHdlYmRhdi5vcmcw +XDANBgkqhkiG9w0BAQEFAANLADBIAkEA80VObGfPglkD7z5KicPSpaBIE9has2Uf +UU/6mybkQZkaYYka9IOyToeTK+y12ikoXnRS4RonbrciisCGSZ0YbQIDAQABow0w +CzAJBgNVHRMEAjAAMA0GCSqGSIb3DQEBBAUAA0EAfkNH56ZAo8AIrHkVi/NvUFsn +pBCNYbgXAJUev5Mw1hmZsdai7yW0yMAGC4QFr6518QFqg7kBNQERj4s1W9w7CQ== +-----END CERTIFICATE----- diff --git a/neon/test/common/ChangeLog b/neon/test/common/ChangeLog index 7e89c0854..f17d47dba 100644 --- a/neon/test/common/ChangeLog +++ b/neon/test/common/ChangeLog @@ -1,3 +1,11 @@ +Wed Aug 25 19:27:26 2004 Joe Orton <joe@manyfish.co.uk> + + * child.c (reset_socket): New function. + +Mon Jul 5 18:38:08 2004 Joe Orton <joe@manyfish.co.uk> + + * tests.c (main): Print 'xfail' for expected failures. + Tue Oct 7 21:19:56 2003 Joe Orton <joe@manyfish.co.uk> * child.c (close_socket): New function. diff --git a/neon/test/common/child.c b/neon/test/common/child.c index 5756882cc..ee1ea536f 100644 --- a/neon/test/common/child.c +++ b/neon/test/common/child.c @@ -1,6 +1,6 @@ /* Framework for testing with a server process - Copyright (C) 2001-2002, Joe Orton <joe@manyfish.co.uk> + Copyright (C) 2001-2004, Joe Orton <joe@manyfish.co.uk> This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by @@ -131,6 +131,17 @@ void minisleep(void) #endif } +int reset_socket(ne_socket *sock) +{ +#ifdef SO_LINGER + /* Stevens' magic trick to send an RST on close(). */ + struct linger l = {1, 0}; + return setsockopt(ne_sock_fd(sock), SOL_SOCKET, SO_LINGER, &l, sizeof l); +#else + return 1; +#endif +} + /* close 'sock', performing lingering close to avoid premature RST. */ static int close_socket(ne_socket *sock) { diff --git a/neon/test/common/child.h b/neon/test/common/child.h index 24121f1c3..39bdb1d68 100644 --- a/neon/test/common/child.h +++ b/neon/test/common/child.h @@ -1,6 +1,6 @@ /* Framework for testing with a server process - Copyright (C) 2001, Joe Orton <joe@manyfish.co.uk> + Copyright (C) 2001-2004, Joe Orton <joe@manyfish.co.uk> This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by @@ -21,12 +21,26 @@ #ifndef CHILD_H #define CHILD_H 1 +#include "config.h" + +#ifdef HAVE_STRING_H +#include <string.h> /* for strlen() */ +#endif + +#include "ne_socket.h" + /* Test which does DNS lookup on "localhost": this must be the first * named test. */ int lookup_localhost(void); +/* Test which looks up real local hostname. */ +int lookup_hostname(void); + +/* set to local hostname if lookup_hostname succeeds. */ +extern char *local_hostname; + /* Callback for spawn_server. */ -typedef int (*server_fn)(nsocket *sock, void *userdata); +typedef int (*server_fn)(ne_socket *sock, void *userdata); /* Spawns server child process: * - forks child process. @@ -38,10 +52,66 @@ typedef int (*server_fn)(nsocket *sock, void *userdata); */ int spawn_server(int port, server_fn fn, void *userdata); +/* Like spawn_server; if bind_local is non-zero, binds server to + * localhost, otherwise, binds server to real local hostname. (must + * have called lookup_localhost or lookup_hostname as approprate + * beforehand). */ +int spawn_server_addr(int bind_local, int port, server_fn fn, void *userdata); + +/* Like spawn server except will continue accepting connections and + * processing requests, up to 'n' times. If 'n' is reached, then the + * child process exits with a failure status. */ +int spawn_server_repeat(int port, server_fn fn, void *userdata, int n); + /* Blocks until child process exits, and gives return code of 'fn'. */ int await_server(void); /* Kills child process. */ int reap_server(void); +/* Returns non-zero if server process has already died. */ +int dead_server(void); + +/* If discard_request comes across a header called 'want_header', it + * will call got_header passing the header field value. */ +extern const char *want_header; +typedef void (*got_header_fn)(char *value); +extern got_header_fn got_header; + +/* Send string to child; ne_sock_fullwrite with debugging. */ +ssize_t server_send(ne_socket *sock, const char *data, size_t len); + +/* Utility macro: send given string down socket. */ +#define SEND_STRING(sock, str) server_send((sock), (str), strlen((str))) + +/* Tries to ensure that the socket will be closed using RST rather + * than FIN. */ +int reset_socket(ne_socket *sock); + +/* Utility function: discard request. Sets context on error. */ +int discard_request(ne_socket *sock); + +/* Utility function: discard request body. Sets context on error. */ +int discard_body(ne_socket *sock); + +struct serve_file_args { + const char *fname; + const char *headers; + int chunks; +}; + +/* Utility function: callback for spawn_server: pass pointer to + * serve_file_args as userdata, and args->fname is served as a 200 + * request. If args->headers is non-NULL, it must be a set of + * CRLF-terminated lines which is added in to the response headers. + * If args->chunks is non-zero, the file is delivered using chunks of + * that size. */ +int serve_file(ne_socket *sock, void *ud); + +/* set to value of C-L header by discard_request. */ +extern int clength; + +/* Sleep for a short time. */ +void minisleep(void); + #endif /* CHILD_H */ diff --git a/neon/test/common/tests.c b/neon/test/common/tests.c index 83f0c5271..ad1b8f55e 100644 --- a/neon/test/common/tests.c +++ b/neon/test/common/tests.c @@ -1,6 +1,6 @@ /* Stupidly simple test framework - Copyright (C) 2001-2002, Joe Orton <joe@manyfish.co.uk> + Copyright (C) 2001-2002, 2004, Joe Orton <joe@manyfish.co.uk> This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by @@ -41,6 +41,7 @@ #include <errno.h> #endif +#include "ne_string.h" #include "ne_utils.h" #include "ne_socket.h" @@ -195,7 +196,7 @@ int main(int argc, char *argv[]) printf("-> running `%s':\n", test_suite); for (n = 0; !aborted && tests[n].fn != NULL; n++) { - int result; + int result, is_xfail = 0; #ifdef NEON_MEMLEAK size_t allocated = ne_alloc_used; #endif @@ -233,8 +234,10 @@ int main(int argc, char *argv[]) if (result == OK) { t_context("test passed but expected failure"); result = FAIL; - } else if (result == FAIL) + } else if (result == FAIL) { result = OK; + is_xfail = 1; + } } /* align the result column if we've had warnings. */ @@ -244,7 +247,14 @@ int main(int argc, char *argv[]) switch (result) { case OK: - COL("32"); printf("pass"); NOCOL; + if (is_xfail) { + COL("32;07"); + printf("xfail"); + } else { + COL("32"); + printf("pass"); + } + NOCOL; if (warned) { printf(" (with %d warning%s)", warned, (warned > 1)?"s":""); } @@ -266,7 +276,7 @@ int main(int argc, char *argv[]) aborted = 1; /* fall-through */ case SKIP: - COL("44"); printf("SKIPPED"); NOCOL; + COL("44;37;01"); printf("SKIPPED"); NOCOL; if (have_context) { printf(" (%s)", test_context); } diff --git a/neon/test/compress.c b/neon/test/compress.c index cb90c23c3..7e23af166 100644 --- a/neon/test/compress.c +++ b/neon/test/compress.c @@ -47,7 +47,7 @@ static int init(void) #define EXTRA_DEBUG 0 /* disabled by default */ -static void reader(void *ud, const char *block, size_t len) +static int reader(void *ud, const char *block, size_t len) { struct string *b = ud; @@ -56,7 +56,7 @@ static void reader(void *ud, const char *block, size_t len) (int)len, block); #endif - if (failed == f_mismatch) return; + if (failed == f_mismatch) return -1; if (failed == f_partial && len == 0) { if (b->len != 0) { @@ -66,13 +66,14 @@ static void reader(void *ud, const char *block, size_t len) } else { failed = f_complete; } - return; + return 0; } if (len > b->len || memcmp(b->data, block, len) != 0) { NE_DEBUG(NE_DBG_HTTP, "reader: failed, got [[%.*s]] not [[%.*s]]\n", (int)len, block, (int)b->len, b->data); failed = f_mismatch; + return -1; } else { b->data += len; b->len -= len; @@ -81,6 +82,8 @@ static void reader(void *ud, const char *block, size_t len) (int)b->len); #endif } + + return 0; } static int file2buf(int fd, ne_buffer *buf) @@ -100,7 +103,7 @@ static int do_fetch(const char *realfn, const char *gzipfn, { ne_session *sess; ne_request *req; - int fd; + int fd, ret; ne_buffer *buf = ne_buffer_create(); struct serve_file_args sfargs; ne_decompress *dc; @@ -130,19 +133,34 @@ static int do_fetch(const char *realfn, const char *gzipfn, req = ne_request_create(sess, "GET", "/"); dc = ne_decompress_reader(req, ne_accept_2xx, reader, &body); - ONREQ(ne_request_dispatch(req)); +#ifdef NE_DEBUGGING + ne_debug_init(ne_debug_stream, ne_debug_mask & ~NE_DBG_HTTPBODY); +#endif + + ret = ne_request_dispatch(req); + +#ifdef NE_DEBUGGING + ne_debug_init(ne_debug_stream, ne_debug_mask | NE_DBG_HTTPBODY); +#endif ONN("file not served", ne_get_status(req)->code != 200); + + ONN("decompress succeeded", expect_fail && !ret); + ONV(!expect_fail && ret, ("decompress failed: %s", ne_get_error(sess))); - ONN("decompress succeeded", expect_fail && !ne_decompress_destroy(dc)); - ONV(!expect_fail && ne_decompress_destroy(dc), - ("decompress failed: %s", ne_get_error(sess))); + NE_DEBUG(NE_DBG_HTTP, "session error: %s\n", ne_get_error(sess)); ne_request_destroy(req); ne_session_destroy(sess); ne_buffer_destroy(buf); - - CALL(await_server()); + + if (expect_fail) { + /* if the decompress callback fails, the connection may + * be aborted and hence the server will abort. */ + reap_server(); + } else { + CALL(await_server()); + } if (!expect_fail) { ONN("inflated response truncated", failed == f_partial); @@ -211,6 +229,11 @@ static int fail_trailing(void) return do_fetch(newsfn, "trailing.gz", 0, 1); } +static int fail_trailing_1b(void) +{ + return do_fetch(newsfn, "trailing.gz", 1, 1); +} + static int fail_truncate(void) { return do_fetch(newsfn, "truncated.gz", 0, 1); @@ -231,6 +254,16 @@ static int fail_corrupt2(void) return do_fetch(newsfn, "corrupt2.gz", 0, 1); } +static int fail_empty(void) +{ + return do_fetch(newsfn, "empty.gz", 0, 1); +} + +static int notcomp_empty(void) +{ + return fetch("empty.gz", NULL, 0); +} + static int auth_cb(void *userdata, const char *realm, int tries, char *un, char *pw) { @@ -256,8 +289,7 @@ static int retry_compress_helper(ne_accept_response acceptor, ONREQ(ne_request_dispatch(req)); - ONV(ne_decompress_destroy(dc), - ("decompress failed: %s", ne_get_error(sess))); + ne_decompress_destroy(dc); ONN("got bad response body", failed != f_complete); @@ -342,17 +374,20 @@ ne_test tests[] = { T(simple), T(withname), T(fail_trailing), + T(fail_trailing_1b), T(fail_bad_csum), T(fail_truncate), T(fail_corrupt1), T(fail_corrupt2), + T(fail_empty), + T(notcomp_empty), T(chunked_1b), T(chunked_1b_wn), T(chunked_12b), T(chunked_20b), T(chunked_10b), T(chunked_10b_wn), - T(retry_notcompress), - T(retry_compress), + T_XFAIL(retry_notcompress), + T_XFAIL(retry_compress), T(NULL) }; diff --git a/neon/test/largefile.c b/neon/test/largefile.c new file mode 100644 index 000000000..c68b4fdaf --- /dev/null +++ b/neon/test/largefile.c @@ -0,0 +1,190 @@ +/* + Tests for LFS support in neon + Copyright (C) 2004, Joe Orton <joe@manyfish.co.uk> + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + +*/ + +#include "config.h" + +#include <sys/types.h> + +#include <unistd.h> +#include <fcntl.h> + +#ifdef HAVE_STDINT_H +#include <stdint.h> +#endif + +#include "ne_request.h" + +#include "child.h" +#include "utils.h" +#include "tests.h" + +#ifndef INT64_C +#define INT64_C(x) x ## LL +#endif + +static const char data[] = "Hello, world.\n"; +static off64_t point = INT64_C(2) << 32; +#define SPARSE "sparse.bin" + +/* make a sparse large file */ +static int make_sparse_file(void) +{ + int fd = open64(SPARSE, O_CREAT | O_TRUNC | O_WRONLY, 0644); + + ONN("could not create large file " SPARSE, fd < 0); + ONN("seek to point", lseek64(fd, point, SEEK_SET) != point); + ONN("could not write to file", + write(fd, data, strlen(data)) != (ssize_t)strlen(data)); + ONN("close failed", close(fd)); + + return OK; +} + +/* server function which checks that the request body sent was the + * same as the 'data' array. */ +static int serve_check_body(ne_socket *sock, void *userdata) +{ + CALL(discard_request(sock)); + + if (clength != (ssize_t)strlen(data)) { + CALL(discard_body(sock)); + SEND_STRING(sock, "HTTP/1.0 400 Bad Request Body Length\r\n" + "\r\n"); + } else { + char buf[20]; + + if (ne_sock_fullread(sock, buf, clength) == 0) { + SEND_STRING(sock, "HTTP/1.0 200 OK Then!\r\n\r\n"); + } + } + return 0; +} + +/* sends a small segment of the file from a high offset. */ +static int send_high_offset(void) +{ + int ret, fd = open64(SPARSE, O_RDONLY); + ne_session *sess; + ne_request *req; + + ONN("could not open sparse file", fd < 0); + + CALL(make_session(&sess, serve_check_body, NULL)); + + req = ne_request_create(sess, "PUT", "/sparse"); + ne_set_request_body_fd64(req, fd, point, strlen(data)); + ret = ne_request_dispatch(req); + CALL(await_server()); + ONV(ret != NE_OK || ne_get_status(req)->klass != 2, + ("request failed: %s", ne_get_error(sess))); + ne_request_destroy(req); + ne_session_destroy(sess); + close(fd); + return OK; +} + +#if 1 +#define RESPSIZE INT64_C(4295008256) +#define RESPSTR "4295008256" +#else +#define RESPSIZE INT64_C(2147491840) /* 2^31+8192 */ +#define RESPSTR "2147491840" +#endif + +/* Reads a request, sends a large response, reads a request, then + * sends a little response. */ +static int serve_large_response(ne_socket *sock, void *ud) +{ + int n = 0; + char empty[8192]; + + CALL(discard_request(sock)); + + SEND_STRING(sock, + "HTTP/1.1 200 OK\r\n" + "Content-Length: " RESPSTR "\r\n" + "Server: BigFileServerTM\r\n" "\r\n"); + + memset(empty, 0, sizeof empty); + + for (n = 0; n < RESPSIZE/sizeof(empty); n++) { + if (ne_sock_fullwrite(sock, empty, sizeof empty)) { + NE_DEBUG(NE_DBG_SOCKET, "fullwrite failed\n"); + return 1; + } + } + + NE_DEBUG(NE_DBG_SOCKET, "Wrote %d lots of %d\n", n, (int)sizeof empty); + + CALL(discard_request(sock)); + + SEND_STRING(sock, "HTTP/1.1 200 OK\r\n" + "Connection: close\r\n\r\n"); + + return 0; +} + +static int read_large_response(void) +{ + ne_session *sess; + ne_request *req; + off64_t count = 0; + int ret; + char buf[8192]; + + CALL(make_session(&sess, serve_large_response, NULL)); + + req = ne_request_create(sess, "GET", "/foo"); + +#ifdef NE_DEBUGGING + ne_debug_init(ne_debug_stream, ne_debug_mask & ~(NE_DBG_HTTPBODY|NE_DBG_HTTP)); +#endif + + ret = ne_begin_request(req); + if (ret == NE_OK) { + while ((ret = ne_read_response_block(req, buf, sizeof buf)) > 0) + count += ret; + if (ret == NE_OK) + ret = ne_end_request(req); + } + +#ifdef NE_DEBUGGING + ne_debug_init(ne_debug_stream, ne_debug_mask & (NE_DBG_HTTPBODY|NE_DBG_HTTP)); +#endif + + ONV(ret, ("request failed: %s", ne_get_error(sess))); + ONV(count != RESPSIZE, + ("response body was %" NE_FMT_OFF64_T " not %" NE_FMT_OFF64_T, + count, RESPSIZE)); + + ne_request_destroy(req); + + CALL(any_2xx_request(sess, "/bar")); + CALL(await_server()); + ne_session_destroy(sess); + return OK; +} + +ne_test tests[] = { + T(make_sparse_file), + T(send_high_offset), + T(read_large_response), + T(NULL), +}; diff --git a/neon/test/makekeys.sh b/neon/test/makekeys.sh index d9548b89f..eed5e6f96 100755 --- a/neon/test/makekeys.sh +++ b/neon/test/makekeys.sh @@ -8,7 +8,7 @@ CONF=${srcdir}/openssl.conf REQ="${OPENSSL} req -config ${CONF}" CA="${OPENSSL} ca -config ${CONF} -batch" # MKCERT makes a self-signed cert -MKCERT="${REQ} -x509 -new -days 9000" +MKCERT="${REQ} -x509 -new -days 900" REQDN=reqDN STRMASK=default @@ -70,9 +70,12 @@ ${REQ} -new -key ${srcdir}/server.key -out altname3.csr csr_fields "Fourth AltName Dept" localhost | \ ${REQ} -new -key ${srcdir}/server.key -out altname4.csr -csr_fields "Fifth Altname Dept" localhost | \ +csr_fields "Good ipAddress altname Dept" nowhere.example.com | \ ${REQ} -new -key ${srcdir}/server.key -out altname5.csr +csr_fields "Bad ipAddress altname Dept" nowhere.example.com | \ +${REQ} -new -key ${srcdir}/server.key -out altname6.csr + csr_fields "Self-Signed" | \ ${MKCERT} -key ${srcdir}/server.key -out ssigned.pem @@ -117,7 +120,7 @@ fqdn=`hostname -f 2>/dev/null` || true if [ "x${hostname}.${domain}" = "x${fqdn}" ]; then csr_fields "Wildcard Cert Dept" "*.${domain}" | \ ${REQ} -new -key ${srcdir}/server.key -out wildcard.csr - ${CA} -days 9000 -in wildcard.csr -out wildcard.cert + ${CA} -days 900 -in wildcard.csr -out wildcard.cert fi csr_fields "Neon Client Cert" ignored.example.com | \ @@ -138,13 +141,18 @@ echo GB | ${REQ} -new -key ${srcdir}/server.key -out missingcn.csr REQDN=reqDN.justEmail echo blah@example.com | ${REQ} -new -key ${srcdir}/server.key -out justmail.csr +# presume AVAs will come out in least->most specific order still... +REQDN=reqDN.twoOU +csr_fields "Second OU Dept +First OU Dept" | ${REQ} -new -key ${srcdir}/server.key -out twoou.csr + ### don't put ${REQ} invocations after here -for f in server client twocn caseless cnfirst missingcn justmail; do +for f in server client twocn caseless cnfirst missingcn justmail twoou; do ${CA} -days 900 -in ${f}.csr -out ${f}.cert done -for n in 1 2 3 4 5; do +for n in 1 2 3 4 5 6; do ${CA} -extensions altExt${n} -days 900 \ -in altname${n}.csr -out altname${n}.cert done diff --git a/neon/test/openssl.conf b/neon/test/openssl.conf index 827b44e22..390d34d56 100644 --- a/neon/test/openssl.conf +++ b/neon/test/openssl.conf @@ -52,6 +52,10 @@ subjectAltName = email:neon@webdav.org [altExt5] subjectAltName = IP:127.0.0.1 +# an AltName with a bad IP address +[altExt6] +subjectAltName = IP:1.2.3.4 + [reqDN] countryName = Country Name stateOrProvinceName = State or Province Name @@ -72,6 +76,16 @@ organizationalUnitName = Organizational Unit Name 1.commonName = Common Name emailAddress = Email Address +[reqDN.twoOU] +countryName = Country Name +stateOrProvinceName = State or Province Name +localityName = Locality Name +organizationName = Organization Name +0.organizationalUnitName = Organizational Unit Name +1.organizationalUnitName = Organizational Unit Name +commonName = Common Name (eg, your name or your server\'s hostname) +emailAddress = Email Address + [reqDN.CNfirst] commonName = Common Name diff --git a/neon/test/props.c b/neon/test/props.c index ef0f1e707..4636a5939 100644 --- a/neon/test/props.c +++ b/neon/test/props.c @@ -263,8 +263,8 @@ static int run_207_response(char *resp, const char *expected) CALL(await_server()); - ONV(!ne_xml_valid(p), - ("response body was invalid: %s", ne_xml_get_error(p))); + ONV(ne_xml_failed(p), + ("parse error in response body: %s", ne_xml_get_error(p))); ONV(strcmp(buf->data, expected), ("comparison failed.\n" @@ -521,7 +521,14 @@ static int pfind_simple(void) STAT_207("256 Second is OK")))), "results(/alpha,prop:[{DAV:,fishbone}='strike one':{234 First is OK}];)//" "results(/beta,prop:[{DAV:,fishbone}='strike two':{256 Second is OK}];)//", - 0, 0} + 0, 0}, + + /* whitespace handling. */ + { MULTI_207(RESP_207("\r\nhttp://localhost:7777/alpha ", + PSTAT_207(PROPS_207(APROP_207("alpha", "beta")) + "<D:status>\r\nHTTP/1.1 200 OK </D:status>"))), + "results(http://localhost:7777/alpha,prop:[{DAV:,alpha}='beta':{200 OK}];)//", + 0, 0} }; const ne_propname pset1[] = { { "DAV:", "fishbone", }, @@ -540,12 +547,51 @@ static int pfind_simple(void) return OK; } +static int unbounded_response(const char *header, const char *repeats) +{ + ne_session *sess; + struct infinite i = { header, repeats}; + int dbg; + + CALL(make_session(&sess, serve_infinite, &i)); + + dbg = ne_debug_mask; + + ONN("unbounded PROPFIND response did not fail", + ne_simple_propfind(sess, "/", 0, NULL, + dummy_results, NULL) != NE_ERROR); + + CALL(reap_server()); + ne_session_destroy(sess); + return OK; +} + +static int unbounded_propstats(void) +{ + return unbounded_response( + RESP207 "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n" + "<multistatus xmlns=\"DAV:\">" + "<response><href>/</href>", + "<propstat></propstat>"); +} + +static int unbounded_props(void) +{ + return unbounded_response( + RESP207 "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n" + "<multistatus xmlns=\"DAV:\">" + "<response><href>/</href><propstat>", + "<prop><jim>hello, world</jim></prop>"); +} + ne_test tests[] = { T(two_oh_seven), T(patch_simple), T(pfind_simple), T(regress), T(patch_regress), + T(unbounded_props), + T(unbounded_propstats), T(NULL) }; diff --git a/neon/test/request.c b/neon/test/request.c index 4df5c78d2..1dcf7c2f7 100644 --- a/neon/test/request.c +++ b/neon/test/request.c @@ -1,6 +1,6 @@ /* HTTP request handling tests - Copyright (C) 2001-2003, Joe Orton <joe@manyfish.co.uk> + Copyright (C) 2001-2004, Joe Orton <joe@manyfish.co.uk> This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by @@ -69,10 +69,11 @@ static int finish_request(void) #define TE_CHUNKED "Transfer-Encoding: chunked\r\n" /* takes response body chunks and appends them to a buffer. */ -static void collector(void *ud, const char *data, size_t len) +static int collector(void *ud, const char *data, size_t len) { ne_buffer *buf = ud; ne_buffer_append(buf, data, len); + return 0; } typedef ne_request *(*construct_request)(ne_session *sess, void *userdata); @@ -251,12 +252,6 @@ static int no_body_204(void) "Content-Length: 5\r\n\r\n"); } -static int no_body_205(void) -{ - return expect_no_body("GET", "HTTP/1.1 205 Reset Content\r\n" - "Content-Length: 5\r\n\r\n"); -} - static int no_body_HEAD(void) { return expect_no_body("HEAD", "HTTP/1.1 200 OK\r\n" @@ -830,17 +825,12 @@ static int skip_1xx_hdrs(void) #undef sess -/* server for expect_100_once: eats a dummy request, then serves a - * 100-continue request, and fails if the request body is sent - * twice. */ +/* server for expect_100_once: serves a 100-continue request, and + * fails if the request body is sent twice. */ static int serve_100_once(ne_socket *sock, void *ud) { struct s1xx_args args = {2, 0}; char ch; - /* dummy first request. */ - CALL(discard_request(sock)); - CALL(SEND_STRING(sock, RESP200 "Content-Length: 0\r\n\r\n")); - /* now the real 1xx request. */ CALL(serve_1xx(sock, &args)); CALL(discard_body(sock)); ONN("body was served twice", ne_sock_read(sock, &ch, 1) == 1); @@ -857,24 +847,35 @@ static int expect_100_once(void) char body[BUFSIZ]; CALL(make_session(&sess, serve_100_once, NULL)); - ne_set_expect100(sess, 1); - /* 100-continue is only used if the server is known to claim - * HTTP/1.1 compliance; make a dummy request on the socket first, - * to trigger that logic. */ - CALL(any_request(sess, "/foo")); - - /* now the real request. */ req = ne_request_create(sess, "GET", "/foo"); + ne_set_request_expect100(req, 1); memset(body, 'A', sizeof(body)); ne_set_request_body_buffer(req, body, sizeof(body)); - ONN("request failed", ne_request_dispatch(req)); + ONREQ(ne_request_dispatch(req)); ne_request_destroy(req); ne_session_destroy(sess); CALL(await_server()); return OK; } +/* regression test for enabling 100-continue without sending a body. */ +static int expect_100_nobody(void) +{ + ne_session *sess; + ne_request *req; + + CALL(make_session(&sess, serve_100_once, NULL)); + + req = ne_request_create(sess, "GET", "/foo"); + ne_set_request_expect100(req, 1); + ONREQ(ne_request_dispatch(req)); + ne_request_destroy(req); + ne_session_destroy(sess); + + return await_server(); +} + struct body { char *body; size_t size; @@ -971,19 +972,6 @@ static int send_bodies(void) return OK; } -static int serve_infinite_headers(ne_socket *sock, void *userdata) -{ - CALL(discard_request(sock)); - - SEND_STRING(sock, RESP200); - - for (;;) { - SEND_STRING(sock, "x-foo: bar\r\n"); - } - - return 0; -} - /* Utility function: run a request using the given server fn, and the * request should fail. If 'error' is non-NULL, it must be a substring * of the error string. */ @@ -1052,7 +1040,8 @@ static int fail_request(int with_body, server_fn fn, void *ud, int forever) static int unbounded_headers(void) { - return fail_request(0, serve_infinite_headers, NULL, 0); + struct infinite i = { RESP200, "x-foo: bar\r\n" }; + return fail_request(0, serve_infinite, &i, 0); } static int blank_response(void) @@ -1074,18 +1063,11 @@ static int not_http(void) return fail_request(0, serve_non_http, NULL, 0); } -static int serve_infinite_folds(ne_socket *sock, void *ud) -{ - SEND_STRING(sock, "HTTP/1.0 200 OK\r\nFoo: bar\r\n"); - for (;;) { - SEND_STRING(sock, " hello there.\r\n"); - } - return OK; -} - static int unbounded_folding(void) { - return fail_request(0, serve_infinite_folds, NULL, 0); + struct infinite i = { "HTTP/1.0 200 OK\r\nFoo: bar\r\n", + " hello there.\r\n" }; + return fail_request(0, serve_infinite, &i, 0); } static int serve_close(ne_socket *sock, void *ud) @@ -1166,11 +1148,13 @@ static enum { static off_t prog_last = -1, prog_total; +#define FOFF "%" NE_FMT_OFF_T + /* callback for send_progress. */ static void s_progress(void *userdata, off_t prog, off_t total) { NE_DEBUG(NE_DBG_HTTP, - "progress callback: %" NE_FMT_OFF_T "/%" NE_FMT_OFF_T ".\n", + "progress callback: " FOFF "/" FOFF ".\n", prog, total); switch (prog_state) { @@ -1179,19 +1163,19 @@ static void s_progress(void *userdata, off_t prog, off_t total) return; case prog_transfer: if (total != prog_total) { - t_context("total unexpected: %ld not %ld", total, prog_total); + t_context("total unexpected: " FOFF " not " FOFF "", total, prog_total); prog_state = prog_error; } else if (prog > total) { - t_context("first progress was invalid (%ld/%ld)", prog, total); + t_context("first progress was invalid (" FOFF "/" FOFF ")", prog, total); prog_state = prog_error; } else if (prog_last != -1 && prog_last > prog) { - t_context("progess went backwards: %ld to %ld", prog_last, prog); + t_context("progess went backwards: " FOFF " to " FOFF, prog_last, prog); prog_state = prog_error; } else if (prog_last == prog) { - t_context("no progress made! %ld to %ld", prog_last, prog); + t_context("no progress made! " FOFF " to " FOFF, prog_last, prog); prog_state = prog_error; } else if (prog == total) { @@ -1203,6 +1187,8 @@ static void s_progress(void *userdata, off_t prog, off_t total) prog_last = prog; } +#undef FOFF + static ssize_t provide_progress(void *userdata, char *buf, size_t bufsiz) { int *count = userdata; @@ -1580,13 +1566,105 @@ static int dup_method(void) return OK; } +#ifdef NE_HAVE_IDNA + +/* "ħêłłø.com" in UTF-8 and ACE form. */ +#define HELLO_DOT_COM "\xc4\xa7" "\xc3\xaa" "\xc5\x82" "\xc5\x82" "\xc3\xb8" ".com" +#define HELLO_IDNA_ACE "xn--bda2a5j8da.com" + +static char *host_hdr = NULL; + +static void dup_header(char *hdr) +{ + host_hdr = strdup(hdr); +} + +static int serve_check_host(ne_socket *sock, void *userdata) +{ + const char *host = userdata; + + want_header = "Host"; + got_header = dup_header; + + CALL(discard_request(sock)); + + if (host_hdr == NULL) { + CALL(SEND_STRING(sock, "HTTP/1.0 500 No Host header!?\r\n\r\n")); + } else if (strcmp(host_hdr, host) != 0) { + CALL(SEND_STRING(sock, "HTTP/1.0 500 Bad Host Header\r\n\r\n")); + } else { + CALL(SEND_STRING(sock, "HTTP/1.0 200 Good Host Header\r\n\r\n")); + } + + return OK; +} + +static int idna_hostname(void) +{ + ne_session *sess = ne_session_create("http", HELLO_DOT_COM, 80); + + ne_session_proxy(sess, "localhost", 7777); + + CALL(spawn_server(7777, serve_check_host, HELLO_IDNA_ACE)); + CALL(any_2xx_request(sess, "/idnafoo")); + CALL(await_server()); + + ne_session_destroy(sess); + return OK; +} +#else +static int idna_hostname(void) +{ + t_context("IDNA support not enabled"); + return SKIP; +} +#endif + +static int abortive_reader(void *userdata, const char *buf, size_t len) +{ + ne_session *sess = userdata; + if (len == 5 && strncmp(buf, "abcde", 5) == 0) { + ne_set_error(sess, "Reader callback failed"); + } else { + ne_set_error(sess, "Reader callback called with length %" NE_FMT_SIZE_T, + len); + } + return NE_ERROR; +} + +static int abort_reader(void) +{ + ne_session *sess; + ne_request *req; + int ret; + + CALL(make_session(&sess, single_serve_string, + RESP200 "Content-Length: 5\r\n\r\n" + "abcde" + "HTTP/1.1 200 OK\r\n" + "Content-Length: 0\r\n\r\n")); + + req = ne_request_create(sess, "GET", "/foo"); + ne_add_response_body_reader(req, ne_accept_2xx, abortive_reader, sess); + ret = ne_request_dispatch(req); + ONV(ret != NE_ERROR, ("request did not fail with NE_ERROR: %d", ret)); + ONV(strcmp(ne_get_error(sess), "Reader callback failed") != 0, + ("unexpected session error string: %s", ne_get_error(sess))); + ne_request_destroy(req); + /* test that the connection was closed. */ + ONN("connection not closed after aborted response", + any_2xx_request(sess, "/failmeplease") == OK); + ne_session_destroy(sess); + CALL(await_server()); + return OK; +} + ne_test tests[] = { T(lookup_localhost), T(single_get_clength), T(single_get_eof), T(single_get_chunked), T(no_body_204), - T(no_body_205), T(no_body_304), T(no_body_HEAD), T(no_body_empty_clength), @@ -1630,6 +1708,7 @@ ne_test tests[] = { T(skip_1xx_hdrs), T(send_bodies), T(expect_100_once), + T(expect_100_nobody), T(unbounded_headers), T(unbounded_folding), T(blank_response), @@ -1652,5 +1731,7 @@ ne_test tests[] = { T(dup_method), T(versions), T(hook_create_req), + T(idna_hostname), + T(abort_reader), T(NULL) }; diff --git a/neon/test/run.sh b/neon/test/run.sh index 046457123..f05c321be 100755 --- a/neon/test/run.sh +++ b/neon/test/run.sh @@ -1,12 +1,13 @@ #!/bin/sh -rm -f debug.log -rm -f child.log +rm -f debug.log child.log + +ulimit -c unlimited +ulimit -v 10240 # enable an safety-checking malloc in glibc which will abort() if # heap corruption is detected. MALLOC_CHECK_=2 - export MALLOC_CHECK_ for f in $*; do diff --git a/neon/test/socket.c b/neon/test/socket.c index 687ff1d03..47a86f884 100644 --- a/neon/test/socket.c +++ b/neon/test/socket.c @@ -43,14 +43,8 @@ #include "utils.h" #ifdef SOCKET_SSL -#include <openssl/err.h> -#include <openssl/ssl.h> - #include "ne_ssl.h" - - -SSL_CTX *server_ctx; -ne_ssl_context *client_ctx; +ne_ssl_context *server_ctx, *client_ctx; #endif static ne_sock_addr *localhost; @@ -82,25 +76,26 @@ static int multi_init(void) return OK; } -static ne_socket *do_connect(ne_sock_addr *addr, unsigned int port) +/* Create and connect *sock to address addr on given port. */ +static int do_connect(ne_socket **sock, ne_sock_addr *addr, unsigned int port) { - ne_socket *sock = ne_sock_create(); const ne_inet_addr *ia; - if (!sock) return NULL; + *sock = ne_sock_create(); + ONN("could not create socket", *sock == NULL); for (ia = ne_addr_first(addr); ia; ia = ne_addr_next(addr)) { - if (ne_sock_connect(sock, ia, port) == 0) - return sock; + if (ne_sock_connect(*sock, ia, port) == 0) + return OK; } - ne_sock_close(sock); - return NULL; + t_context("could not connect to server: %s", ne_sock_error(*sock)); + ne_sock_close(*sock); + return FAIL; } #ifdef SOCKET_SSL -/* FIXME: largely cut'n'pasted from ssl.c. */ static int init_ssl(void) { char *server_key; @@ -110,26 +105,23 @@ static int init_ssl(void) if (test_argc > 1) { server_key = ne_concat(test_argv[1], "/server.key", NULL); } else { - server_key = "server.key"; + server_key = ne_strdup("server.key"); } - ONN("sock_init failed.\n", ne_sock_init()); - server_ctx = SSL_CTX_new(SSLv23_server_method()); + ONN("sock_init failed", ne_sock_init()); + server_ctx = ne_ssl_context_create(1); ONN("SSL_CTX_new failed", server_ctx == NULL); - ONN("failed to load private key", - !SSL_CTX_use_PrivateKey_file(server_ctx, server_key, - SSL_FILETYPE_PEM)); - ONN("failed to load certificate", - !SSL_CTX_use_certificate_file(server_ctx, "server.cert", - SSL_FILETYPE_PEM)); - - client_ctx = ne_ssl_context_create(); + + ne_ssl_context_keypair(server_ctx, "server.cert", server_key); + + client_ctx = ne_ssl_context_create(0); ONN("SSL_CTX_new failed for client", client_ctx == NULL); cert = ne_ssl_cert_read("ca/cert.pem"); ONN("could not load ca/cert.pem", cert == NULL); - ne_ssl_ctx_trustcert(client_ctx, cert); + ne_ssl_context_trustcert(client_ctx, cert); + ne_free(server_key); return OK; } @@ -160,24 +152,13 @@ struct serve_pair { static int wrap_serve(ne_socket *sock, void *ud) { struct serve_pair *pair = ud; - int fd = ne_sock_fd(sock), ret; - SSL *ssl = SSL_new(server_ctx); - BIO *bio = BIO_new_socket(fd, BIO_NOCLOSE); - - ONN("SSL_new failed", ssl == NULL); - SSL_set_bio(ssl, bio, bio); - -#define ERROR_SSL_STRING (ERR_reason_error_string(ERR_get_error())) - - NE_DEBUG(NE_DBG_SOCKET, "Doing SSL accept:\n"); - ret = SSL_accept(ssl); - if (ret != 1) { - NE_DEBUG(NE_DBG_SOCKET, "SSL_accept failed: %s\n", ERROR_SSL_STRING); + + if (ne_sock_accept_ssl(sock, server_ctx)) { + NE_DEBUG(NE_DBG_SOCKET, "SSL_accept failed: %s\n", ne_sock_error(sock)); return 1; } NE_DEBUG(NE_DBG_SOCKET, "SSL accept okay.\n"); - ne_sock_switch_ssl(sock, ssl); return pair->fn(sock, pair->userdata); } @@ -187,8 +168,7 @@ static int begin(ne_socket **sock, server_fn fn, void *ud) pair.fn = fn; pair.userdata = ud; CALL(spawn_server(7777, wrap_serve, &pair)); - *sock = do_connect(localhost, 7777); - ONN("could not connect to localhost:7777", *sock == NULL); + CALL(do_connect(sock, localhost, 7777)); ONV(ne_sock_connect_ssl(*sock, client_ctx), ("SSL negotation failed: %s", ne_sock_error(*sock))); return OK; @@ -199,9 +179,7 @@ static int begin(ne_socket **sock, server_fn fn, void *ud) static int begin(ne_socket **sock, server_fn fn, void *ud) { CALL(spawn_server(7777, fn, ud)); - *sock = do_connect(localhost, 7777); - ONN("could not connect to localhost:7777", *sock == NULL); - return OK; + return do_connect(sock, localhost, 7777); } #endif @@ -568,7 +546,8 @@ static int line_closure(void) ret = ne_sock_readline(sock, buffer, BUFSIZ); ONV(ret != NE_SOCK_CLOSED, - ("readline got %" NE_FMT_SSIZE_T " not EOF", ret)); + ("readline got %" NE_FMT_SSIZE_T " not EOF: %s", ret, + ne_sock_error(sock))); return finish(sock, 0); } @@ -792,7 +771,8 @@ static int ssl_closure(void) ret = ne_sock_fullwrite(sock, "a", 1); } while (ret == 0); ONV(ret != NE_SOCK_RESET && ret != NE_SOCK_CLOSED, - ("write got %" NE_FMT_SSIZE_T " not reset or closure", ret)); + ("write got %" NE_FMT_SSIZE_T " not reset or closure: %s", ret, + ne_sock_error(sock))); return good_close(sock); } @@ -816,13 +796,20 @@ static int ssl_truncate(void) } #else -/* thanks to W Richard Stevens for the precise repro case for getting - * an RST on demand. */ + +/* use W Richard Stevens' SO_LINGER trick to elicit a TCP RST */ +static int serve_reset(ne_socket *sock, void *ud) +{ + reset_socket(sock); + exit(0); + return 0; +} + static int write_reset(void) { ne_socket *sock; int ret; - CALL(begin(&sock, serve_close, NULL)); + CALL(begin(&sock, serve_reset, NULL)); CALL(full_write(sock, "a", 1)); CALL(await_server()); ret = ne_sock_fullwrite(sock, "a", 1); @@ -830,7 +817,8 @@ static int write_reset(void) ne_sock_close(sock); return SKIP; } - ONV(ret != NE_SOCK_RESET, ("write got %d not reset", ret)); + ONV(ret != NE_SOCK_RESET, + ("write got %d not reset: %s", ret, ne_sock_error(sock))); return good_close(sock); } @@ -838,7 +826,7 @@ static int read_reset(void) { ne_socket *sock; ssize_t ret; - CALL(begin(&sock, serve_close, NULL)); + CALL(begin(&sock, serve_reset, NULL)); CALL(full_write(sock, "a", 1)); CALL(await_server()); ret = ne_sock_read(sock, buffer, 1); @@ -846,7 +834,9 @@ static int read_reset(void) ne_sock_close(sock); return SKIP; } - ONV(ret != NE_SOCK_RESET, ("read got %" NE_FMT_SSIZE_T " not reset", ret)); + ONV(ret != NE_SOCK_RESET, + ("read got %" NE_FMT_SSIZE_T " not reset: %s", ret, + ne_sock_error(sock))); return good_close(sock); } #endif diff --git a/neon/test/ssl.c b/neon/test/ssl.c index dba1d4fda..0f8683c8d 100644 --- a/neon/test/ssl.c +++ b/neon/test/ssl.c @@ -39,7 +39,7 @@ #include "child.h" #include "utils.h" -#ifndef NEON_SSL +#ifndef NE_HAVE_SSL /* this file shouldn't be built if SSL is not enabled. */ #error SSL not supported #endif @@ -725,10 +725,10 @@ static int get_failures(void *userdata, int fs, const ne_ssl_certificate *c) /* Helper function: run a request using the given self-signed server * certificate, and expect the request to fail with the given * verification failure flags. */ -static int fail_ssl_request(char *cert, char *cacert, +static int fail_ssl_request(char *cert, char *cacert, const char *host, const char *msg, int failures) { - ne_session *sess = DEFSESS; + ne_session *sess = ne_session_create("https", host, 7777); int gotf = 0, ret; ret = any_ssl_request(sess, fail_serve, cert, cacert, @@ -753,14 +753,14 @@ static int fail_ssl_request(char *cert, char *cacert, return OK; } -/* Note that the certs used for fail_* are all self-signed, so the +/* Note that the certs used for fail_* are mostly self-signed, so the * cert is passed as CA cert and server cert to fail_ssl_request. */ /* Check that a certificate with the incorrect commonName attribute is * flagged as such. */ static int fail_wrongCN(void) { - return fail_ssl_request("wrongcn.pem", "wrongcn.pem", + return fail_ssl_request("wrongcn.pem", "wrongcn.pem", "localhost", "certificate with incorrect CN was accepted", NE_SSL_IDMISMATCH); } @@ -769,8 +769,8 @@ static int fail_wrongCN(void) static int fail_expired(void) { char *c = ne_concat(srcdir, "/expired.pem", NULL); - CALL(fail_ssl_request(c, c, "expired certificate was accepted", - NE_SSL_EXPIRED)); + CALL(fail_ssl_request(c, c, "localhost", + "expired certificate was accepted", NE_SSL_EXPIRED)); ne_free(c); return OK; } @@ -778,7 +778,8 @@ static int fail_expired(void) static int fail_notvalid(void) { char *c = ne_concat(srcdir, "/notvalid.pem", NULL); - CALL(fail_ssl_request(c, c, "not yet valid certificate was accepted", + CALL(fail_ssl_request(c, c, "localhost", + "not yet valid certificate was accepted", NE_SSL_NOTYETVALID)); ne_free(c); return OK; @@ -788,14 +789,14 @@ static int fail_notvalid(void) * fail with UNTRUSTED. */ static int fail_untrusted_ca(void) { - return fail_ssl_request("server.cert", NULL, "untrusted CA.", - NE_SSL_UNTRUSTED); + return fail_ssl_request("server.cert", NULL, "localhost", + "untrusted CA.", NE_SSL_UNTRUSTED); } static int fail_self_signed(void) { - return fail_ssl_request("ssigned.pem", NULL, "self-signed cert", - NE_SSL_UNTRUSTED); + return fail_ssl_request("ssigned.pem", NULL, "localhost", + "self-signed cert", NE_SSL_UNTRUSTED); } /* Test for failure when a server cert is presented which has no @@ -815,6 +816,21 @@ static int fail_missing_CN(void) return OK; } +/* test for a bad ipAddress altname */ +static int fail_bad_ipaltname(void) +{ + return fail_ssl_request("altname6.cert", CA_CERT, "127.0.0.1", + "bad IP altname cert", NE_SSL_IDMISMATCH); +} + +/* test for a ipAddress which matched against the hostname as per neon + * 0.24 behaviour. */ +static int fail_host_ipaltname(void) +{ + return fail_ssl_request("altname5.cert", CA_CERT, "localhost", + "bad IP altname cert", NE_SSL_IDMISMATCH); +} + /* Test that the SSL session is cached across connections. */ static int session_cache(void) { @@ -957,6 +973,7 @@ static int ccert_unencrypted(void) args.require_cc = 1; ccert = ne_ssl_clicert_read("unclient.p12"); + ONN("could not load unclient.p12", ccert == NULL); ONN("unclient.p12 was encrypted", ne_ssl_clicert_encrypted(ccert)); ne_ssl_set_clicert(sess, ccert); @@ -1171,6 +1188,7 @@ static int cert_identities(void) static const struct { const char *fname, *identity; } certs[] = { + { "ssigned.pem", "localhost" }, { "twocn.cert", "localhost" }, { "altname1.cert", "localhost" }, { "altname2.cert", "nohost.example.com" }, @@ -1270,7 +1288,9 @@ static int dname_readable(void) { "justmail.cert", "blah@example.com", NULL }, { "t61subj.cert", I18N_DNAME, NULL }, { "bmpsubj.cert", I18N_DNAME, NULL }, - { "utf8subj.cert", I18N_DNAME, NULL } + { "utf8subj.cert", I18N_DNAME, NULL }, + { "twoou.cert", "First OU Dept, Second OU Dept, Neon Hackers Ltd, " + "Cambridge, Cambridgeshire, GB" } }; size_t n; @@ -1538,6 +1558,8 @@ ne_test tests[] = { T(fail_untrusted_ca), T(fail_self_signed), T(fail_missing_CN), + T(fail_host_ipaltname), + T(fail_bad_ipaltname), T(session_cache), diff --git a/neon/test/string-tests.c b/neon/test/string-tests.c index 3d0fac183..62e8afe00 100644 --- a/neon/test/string-tests.c +++ b/neon/test/string-tests.c @@ -1,6 +1,6 @@ /* String handling tests - Copyright (C) 2001-2003, Joe Orton <joe@manyfish.co.uk> + Copyright (C) 2001-2004, Joe Orton <joe@manyfish.co.uk> This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by @@ -410,7 +410,7 @@ static int base64(void) unsigned char bits[256]; size_t n; -#define B64B(x, l, y) CALL(b64_check(x, l, y)) +#define B64B(x, l, y) CALL(b64_check((unsigned char *)x, l, y)) #define B64(x, y) B64B(x, strlen(x), y) /* invent these with @@ -464,6 +464,39 @@ static int unbase64(void) return OK; } +static int printing(void) +{ + struct { + const char *in, *out; + size_t pass, ret; + } ts[] = { + { "alpha", "alpha", 10, 5 }, + { "alpha", "alph", 5, 4 }, + { "foobar", "", 1, 0 }, + { NULL, NULL, 0, 0} + }; + size_t n; + + for (n = 0; ts[n].in; n++) { + char buf[512]; + size_t ret; + + memset(buf, 'A', sizeof buf); + + ret = ne_snprintf(buf, ts[n].pass, "%s", ts[n].in); + + ONCMP(buf, ts[n].out); + ONV(ret != ts[n].ret, + ("got return value %" NE_FMT_SIZE_T " not %" NE_FMT_SIZE_T, + ret, ts[n].ret)); + + /* byte past the NUL must still be 'A' */ + ONN("buffer over-ran!", buf[ret + 1] != 'A'); + } + + return OK; +} + ne_test tests[] = { T(simple), T(buf_concat), @@ -487,6 +520,7 @@ ne_test tests[] = { T(cleaner), T(base64), T(unbase64), + T(printing), T(NULL) }; diff --git a/neon/test/stubs.c b/neon/test/stubs.c index 494f8f08e..1c599623b 100644 --- a/neon/test/stubs.c +++ b/neon/test/stubs.c @@ -42,11 +42,11 @@ #include "child.h" #include "utils.h" -#if defined(NEON_ZLIB) && defined(NEON_SSL) +#if defined(NE_HAVE_ZLIB) && defined(NE_HAVE_SSL) #define NO_TESTS 1 #endif -#ifndef NEON_ZLIB +#ifndef NE_HAVE_ZLIB static int sd_result = OK; static void sd_reader(void *ud, const char *block, size_t len) @@ -90,7 +90,7 @@ static int stub_decompress(void) } #endif -#ifndef NEON_SSL +#ifndef NE_HAVE_SSL static int stub_ssl(void) { ne_session *sess = ne_session_create("https", "localhost", 7777); @@ -156,10 +156,10 @@ static int null_test(void) { return OK; } #endif ne_test tests[] = { -#ifndef NEON_ZLIB +#ifndef NE_HAVE_ZLIB T(stub_decompress), #endif -#ifndef NEON_SSL +#ifndef NE_HAVE_SSL T(stub_ssl), #endif /* to prevent failure when SSL and zlib are supported. */ diff --git a/neon/test/util-tests.c b/neon/test/util-tests.c index 464f33877..0cee94b23 100644 --- a/neon/test/util-tests.c +++ b/neon/test/util-tests.c @@ -254,10 +254,30 @@ static int version_string(void) static int support(void) { -#ifdef NEON_SSL - ONN("SSL support not advertised", !ne_supports_ssl()); +#ifdef NE_HAVE_SSL + ONN("SSL support not advertised", !ne_has_support(NE_FEATURE_SSL)); #else - ONN("SSL support advertised", ne_supports_ssl()); + ONN("SSL support advertised", ne_has_support(NE_FEATURE_SSL)); +#endif +#ifdef NE_HAVE_ZLIB + ONN("zlib support not advertised", !ne_has_support(NE_FEATURE_ZLIB)); +#else + ONN("zlib support advertised", ne_has_support(NE_FEATURE_ZLIB)); +#endif +#ifdef NE_HAVE_IPV6 + ONN("IPv6 support not advertised", !ne_has_support(NE_FEATURE_IPV6)); +#else + ONN("IPv6 support advertised", ne_has_support(NE_FEATURE_IPV6)); +#endif +#ifdef NE_HAVE_IDNA + ONN("IDNA support not advertised", !ne_has_support(NE_FEATURE_IDNA)); +#else + ONN("IDNA support advertised", ne_has_support(NE_FEATURE_IDNA)); +#endif +#ifdef NE_HAVE_LFS + ONN("LFS support not advertised", !ne_has_support(NE_FEATURE_LFS)); +#else + ONN("LFS support advertised", ne_has_support(NE_FEATURE_LFS)); #endif return OK; } diff --git a/neon/test/utils.c b/neon/test/utils.c index c23860abb..0a070b3a0 100644 --- a/neon/test/utils.c +++ b/neon/test/utils.c @@ -85,7 +85,7 @@ int any_2xx_request(ne_session *sess, const char *uri) ne_request *req = ne_request_create(sess, "GET", uri); int ret = ne_request_dispatch(req); ONV(ret != NE_OK || ne_get_status(req)->klass != 2, - ("request failed: %s\n", ne_get_error(sess))); + ("request failed: %s", ne_get_error(sess))); ne_request_destroy(req); return ret; } @@ -100,7 +100,7 @@ int any_2xx_request_body(ne_session *sess, const char *uri) ret = ne_request_dispatch(req); ne_free(body); ONV(ret != NE_OK || ne_get_status(req)->klass != 2, - ("request failed: %s\n", ne_get_error(sess))); + ("request failed: %s", ne_get_error(sess))); ne_request_destroy(req); return ret; } @@ -132,3 +132,17 @@ int serve_sstring_slowly(ne_socket *sock, void *ud) return 0; } + +int serve_infinite(ne_socket *sock, void *ud) +{ + struct infinite *i = ud; + + CALL(discard_request(sock)); + + SEND_STRING(sock, i->header); + + while (server_send(sock, i->repeat, strlen(i->repeat)) == 0) + /* nullop */; + + return OK; +} diff --git a/neon/test/utils.h b/neon/test/utils.h index 2b466b6d9..ad31b2ef5 100644 --- a/neon/test/utils.h +++ b/neon/test/utils.h @@ -65,4 +65,12 @@ int serve_sstring(ne_socket *sock, void *ud); /* Serve a struct string slowly. */ int serve_sstring_slowly(ne_socket *sock, void *ud); +struct infinite { + const char *header, *repeat; +}; + +/* Pass a "struct infinite *" as userdata, this function sends + * ->header and then loops sending ->repeat forever. */ +int serve_infinite(ne_socket *sock, void *ud); + #endif /* UTILS_H */ diff --git a/neon/test/xml.c b/neon/test/xml.c index 9cea6c753..592ba8043 100644 --- a/neon/test/xml.c +++ b/neon/test/xml.c @@ -1,6 +1,6 @@ /* neon test suite - Copyright (C) 2002-2003, Joe Orton <joe@manyfish.co.uk> + Copyright (C) 2002-2004, Joe Orton <joe@manyfish.co.uk> This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by @@ -35,6 +35,8 @@ #include "child.h" #include "utils.h" +#define ABORT (-42) /* magic code for abort handlers */ + /* A set of SAX handlers which serialize SAX events back into a * pseudo-XML-like string. */ static int startelm(void *userdata, int state, @@ -60,7 +62,7 @@ static int chardata(void *userdata, int state, const char *cdata, size_t len) { ne_buffer *buf = userdata; ne_buffer_append(buf, cdata, len); - return !strncmp(cdata, "!ABORT!", len); + return strncmp(cdata, "!ABORT!", len) == 0 ? ABORT : NE_XML_DECLINE; } static int endelm(void *userdata, int state, @@ -139,7 +141,7 @@ static int startelm_abort(void *buf, int parent, { if (strcmp(name, "abort-start") == 0) { ne_buffer_zappend(buf, "ABORT"); - return NE_XML_ABORT; + return ABORT; } else return startelm(buf, parent, nspace, name, atts); } @@ -149,22 +151,25 @@ static int endelm_abort(void *buf, int state, { if (strcmp(name, "abort-end") == 0) { ne_buffer_zappend(buf, "ABORT"); - return -1; + return ABORT; } else return 0; } +/* Test mode for parse_match: */ enum match_type { - match_valid = 0, - match_invalid, - match_nohands, - match_encoding + match_valid = 0, /* test that the parse succeeds */ + match_invalid, /* test that the parse fails */ + match_nohands, /* test with no handlers registered */ + match_encoding, /* test whether the encoding is equal to the result string */ + match_chunked /* parse the document one byte at a time */ }; static int parse_match(const char *doc, const char *result, enum match_type t) { ne_xml_parser *p = ne_xml_create(); ne_buffer *buf = ne_buffer_create(); + int ret; if (t == match_invalid) ne_xml_push_handler(p, startelm_abort, chardata, endelm_abort, buf); @@ -174,13 +179,26 @@ static int parse_match(const char *doc, const char *result, enum match_type t) ne_xml_push_handler(p, startelm_xform, chardata, endelm_xform, buf); } - ne_xml_parse(p, doc, strlen(doc)); - ne_xml_parse(p, "", 0); - + if (t == match_chunked) { + do { + ret = ne_xml_parse(p, doc++, 1); + } while (ret == 0 && *doc); + } else { + ret = ne_xml_parse(p, doc, strlen(doc)); + } + + if (ret == 0) { + ne_xml_parse(p, "", 0); + } + + ONV(ret != ne_xml_failed(p), + ("ne_xml_failed gave %d not %d", ne_xml_failed(p), ret)); + if (t == match_invalid) - ONV(ne_xml_valid(p), ("parse did not fail: %s", buf->data)); + ONV(ret != ABORT, + ("parse got %d not abort failure: %s", ret, buf->data)); else - ONV(!ne_xml_valid(p), ("parse failed: %s", ne_xml_get_error(p))); + ONV(ret, ("parse failed: %s", ne_xml_get_error(p))); if (t == match_encoding) { const char *enc = ne_xml_doc_encoding(p); @@ -215,6 +233,11 @@ static int matches(void) { PFX "<hello>\r\n<wide> world</wide></hello>", "<{}hello>\n<{}wide> world</{}wide></{}hello>"}, + /* UTF-8 XML Byte Order Mark */ + { "\xEF\xBB\xBF" PFX "<hello/>", "<{}hello></{}hello>" }, + /* UTF-8 XML Byte Order Mark */ + { "\xEF\xBB\xBF" PFX "<hello/>", "<{}hello></{}hello>", match_chunked }, + /*** Tests for namespace handling. ***/ #define NSA "xmlns:foo='bar'" { PFX "<foo:widget " NSA "/>", @@ -329,7 +352,27 @@ static int fail_parse(void) /* malformed namespace declarations */ PFX "<foo xmlns:D=''/>", PFX "<foo xmlns:='fish'/>", + PFX "<foo xmlns:.bar='fish'/>", + PFX "<foo xmlns:-bar='fish'/>", + PFX "<foo xmlns:0bar='fish'/>", + PFX "<fee xmlns:8baz='bar'/>", + + /* element names which are not valid QNames. */ PFX "<foo: xmlns:foo='bar'/>", + PFX "<:fee/>", + PFX "<0fish/>", + PFX "<foo:0fish xmlns:foo='bar'/>", + PFX "<foo:9fish xmlns:foo='bar'/>", + PFX "<foo:-fish xmlns:foo='bar'/>", + PFX "<foo:.fish xmlns:foo='bar'/>", + +#if 0 /* currently disabled to allow SVN to work */ + PFX "<foo:bar:baz xmlns:foo='bar'/>", + PFX "<fee xmlns:baz:bar='bar'/>", + PFX "<fee xmlns::bar='bar'/>", + PFX "<foo::fish xmlns:foo='bar'/>", +#endif + #if 0 /* 2-byte encoding of '.': */ PFX "<foo>" "\x2F\xC0\xAE\x2E\x2F" "</foo>", @@ -341,9 +384,14 @@ static int fail_parse(void) PFX "<foo>" "\x2F\xF8\x80\x80\x80\xAE\x2E\x2F" "</foo>", /* 6-byte encoding of '.': */ PFX "<foo>" "\x2F\xFC\x80\x80\x80\x80\xAE\x2E\x2F" "</foo>", +#endif /* two-byte encoding of '<' must not be parsed as a '<': */ PFX "\xC0\xBC" "foo></foo>", -#endif + + /* Invalid UTF-8 XML Byte Order Marks */ + "\xEF\xBB" PFX "<hello/>", + "\xEF" PFX "<hello/>", + NULL }; int n; @@ -354,10 +402,13 @@ static int fail_parse(void) ne_xml_parse(p, docs[n], strlen(docs[n])); ne_xml_parse(p, "", 0); - ONV(ne_xml_valid(p), ("`%s' was valid", docs[n])); + ONV(ne_xml_failed(p) <= 0, + ("`%s' did not get positive parse error", docs[n])); err = ne_xml_get_error(p); - ONV(strstr(err, "parse error") == NULL, + NE_DEBUG(NE_DBG_HTTP, "Parse error for '%s': %s\n", docs[n], err); + ONV(strstr(err, "parse error") == NULL + && strstr(err, "Invalid Byte Order Mark") == NULL, ("bad error %s", err)); ne_xml_destroy(p); @@ -426,7 +477,7 @@ static int attributes(void) ne_xml_parse_v(p, doc, strlen(doc)); - ONV(!ne_xml_valid(p), ("parse error: %s", ne_xml_get_error(p))); + ONV(ne_xml_failed(p), ("parse error: %s", ne_xml_get_error(p))); ne_xml_destroy(p); return OK; |