diff --git a/Makefile.am b/Makefile.am index 11e4696..0da00ea 100644 --- a/Makefile.am +++ b/Makefile.am @@ -1,76 +1,76 @@ # Assuan top level Makefile # Copyright (C) 2001, 2002, 2003, 2007, 2011 Free Software Foundation, Inc. # # This file is part of Assuan. # # Assuan is free software; you can redistribute it and/or modify it # under the terms of the GNU Lesser General Public License as # published by the Free Software Foundation; either version 2.1 of # the License, or (at your option) any later version. # # Assuan 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 # Lesser General Public License for more details. # # You should have received a copy of the GNU Lesser General Public # License along with this program; if not, see . # SPDX-License-Identifier: LGPL-2.1+ ## Process this file with automake to produce Makefile.in ACLOCAL_AMFLAGS = -I m4 DISTCHECK_CONFIGURE_FLAGS = --enable-doc # (A suitable gitlog-to-changelog script can be found in GnuPG master.) GITLOG_TO_CHANGELOG=gitlog-to-changelog EXTRA_DIST = autogen.sh autogen.rc README.GIT VERSION \ ChangeLog-2011 doc/ChangeLog-2011 src/ChangeLog-2011 \ - tests/ChangeLog-2011 contrib/ChangeLog-2011 \ + tests/ChangeLog-2011 \ build-aux/git-log-footer build-aux/git-log-fix if BUILD_DOC doc = doc else doc = endif SUBDIRS = m4 src $(doc) tests dist-hook: gen-ChangeLog distcheck-hook: set -e; ( \ pref="#+macro: $$(echo $(PACKAGE_NAME)|tr '-' '_')_" ;\ reldate="$$(date -u +%Y-%m-%d)" ;\ echo "$${pref}ver $(PACKAGE_VERSION)" ;\ echo "$${pref}date $${reldate}" ;\ list='$(DIST_ARCHIVES)'; for i in $$list; do \ case "$$i" in *.tar.bz2) \ echo "$${pref}size $$(wc -c <$$i|awk '{print int($$1/1024)}')k" ;\ echo "$${pref}sha1 $$(sha1sum <$$i|cut -d' ' -f1)" ;\ echo "$${pref}sha2 $$(sha256sum <$$i|cut -d' ' -f1)" ;;\ esac;\ done ) | tee $(distdir).swdb gen_start_date = 2011-12-01T00:00:00 .PHONY: gen-ChangeLog gen-ChangeLog: set -e; \ if test -d $(top_srcdir)/.git; then \ (cd $(top_srcdir) && \ $(GITLOG_TO_CHANGELOG) --append-dot --tear-off \ --amend=build-aux/git-log-fix --tear-off \ --since=$(gen_start_date) ) > $(distdir)/cl-t; \ cat $(top_srcdir)/build-aux/git-log-footer >> $(distdir)/cl-t;\ rm -f $(distdir)/ChangeLog; \ mv $(distdir)/cl-t $(distdir)/ChangeLog; \ fi stowinstall: $(MAKE) $(AM_MAKEFLAGS) install prefix=/usr/local/stow/libassuan diff --git a/NEWS b/NEWS index 0ed6e87..dc04e8f 100644 --- a/NEWS +++ b/NEWS @@ -1,586 +1,588 @@ Noteworthy changes in version 2.5.6 (unreleased) [C8/A8/R_] ------------------------------------------------ * Use of ASSUAN_SYSTEM_NPTH is deprecated. Instead, please use the gpgrt_set_syscall_clamp function from gpgrt library. + * No support for WindowsCE, any more. + Noteworthy changes in version 2.5.5 (2021-03-22) [C8/A8/R5] ------------------------------------------------ * Allows starting servers with Unicode names on Windows. [#4398] * Fix a crash in the logging code. [0dd8ffbd32] * Upgrade autoconf stuff. Release-info: https://dev.gnupg.org/T5354 Noteworthy changes in version 2.5.4 (2020-10-23) [C8/A8/R4] ------------------------------------------------ * Support Unicode socket names on Windows. * Fix some minor build annoyances. Release-info: https://dev.gnupg.org/T5112 Noteworthy changes in version 2.5.3 (2019-02-11) [C8/A8/R3] ------------------------------------------------ * Add a timeout for writing to a SOCKS5 proxy. This helps if another service is running on the standard tor socket (e.g. Windows 10). [#3381] * Add workaround for a problem with LD_LIBRARY_PATH on newer systems. [#4298] Release-info: https://dev.gnupg.org/T4361 Noteworthy changes in version 2.5.2 (2018-12-13) [C8/A8/R2] ------------------------------------------------ * Better credential support for BSDs. * Fix some compiler warnings. * Update the build system. Noteworthy changes in version 2.5.1 (2017-12-07) [C8/A8/R1] ------------------------------------------------ * Fix c+p error in the previous usleep fix. Noteworthy changes in version 2.5.0 (2017-12-07) [C8/A8/R0] ------------------------------------------------ * New function to change the system hooks for the socket interface. [#3378] * Fix the use of the internal usleep in the nPth implementation. * Interface changes relative to the 2.4.0 release: ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ assuan_sock_set_system_hooks NEW. Noteworthy changes in version 2.4.5 (2017-12-01) [C7/A7/R5] ------------------------------------------------ * Avoid a blocking close on Windows with nPth. [#3378] Noteworthy changes in version 2.4.4 (2017-11-16) [C7/A7/R4] ------------------------------------------------ * New configure option --disable-doc. * Fix the use of nanosleep. * Portability fixes for OpenBSD and macOS. [#2910] * Portability fix for systems not defining INADDR_LOOPBACK. [#2447] * Improve descriptor passing test to fix a Cygwin build problem. [#3384] Noteworthy changes in version 2.4.3 (2016-07-14) [C7/A7/R3] ------------------------------------------------ * Allow socket redirection with assuan_socket_connect. * Speedup spawning programs on Linux * Fix minor memory leaks * Portability fixes for Solaris and AIX. Noteworthy changes in version 2.4.2 (2015-12-02) [C7/A7/R2] ------------------------------------------------ * The nPth version of the connect system hook does now wrap the call with npth_unprotec/npth_protect to avoid blocking during a connect. * Add feature to assuan_sock_connect_byname to test for SOCKS5 availability. Noteworthy changes in version 2.4.1 (2015-11-23) [C7/A7/R1] ------------------------------------------------ * In Tor mode fallback to port 9150 if 9050 is not listening. * Allow building with older mingw-w64 versions. Noteworthy changes in version 2.4.0 (2015-11-03) [C7/A7/R0] ------------------------------------------------ * New flags "socks" and "tor-mode" for assuan_sock_{set,get}_flag. * New function assuan_sock_connect_byname. * Require at least libgpg-error 1.17. * Interface changes relative to the 2.3.0 release: ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ assuan_sock_connect_byname NEW. ASSUAN_SOCK_TOR NEW. ASSUAN_SOCK_SOCKS NEW. assuan_sock_set_flag EXTENDED. assuan_sock_get_flag EXTENDED. Noteworthy changes in version 2.3.0 (2015-08-28) [C6/A6/R0] ------------------------------------------------ * Now wipes out the memory of the context structure before freeing. The context may have stored sensitive data in its line buffers. * Fixed a problem with the data length limit in assuan_inquire. * Returns GPG_ERR_SOURCE_ASSUAN with errors from functions w/o a context. * Two new functions to tweak the behaviour of the socket wrappers. * Experimental code to support Cygwin's local sockets. * By default build without a build timestamp. * Interface changes relative to the 2.2.1 release: ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ assuan_sock_set_flag NEW. assuan_sock_get_flag NEW. Noteworthy changes in version 2.2.1 (2015-05-12) [C5/A5/R1] ------------------------------------------------ * Documentation updates. * Fixed building for Windows with newer versions of Mingw. Noteworthy changes in version 2.2.0 (2014-12-11) [C5/A5/R0] ------------------------------------------------ * Added support for socket redirection. * Interface changes relative to the 2.1.3 release: ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ assuan_sock_set_sockaddr_un NEW. Noteworthy changes in version 2.1.3 (2014-11-07) [C4/A4/R3] ------------------------------------------------ * Performance fix for Windows. Noteworthy changes in version 2.1.2 (2014-08-17) [C4/A4/R2] ------------------------------------------------ * Fixed portability bugs for Solaris and AIX. * Added support for ppc64le. Noteworthy changes in version 2.1.1 (2013-06-24) [C4/A4/R1] ------------------------------------------------ * Limited support for 64 bit Windows. This is sufficient for use by GpgEX. Noteworthy changes in version 2.1.0 (2013-02-22) ------------------------------------------------ * Support for the nPth library. * Add assuan_check_version and two version macros. * Interface changes relative to the 2.0.3 release: ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ ASSUAN_SYSTEM_NPTH_IMPL NEW macro. ASSUAN_SYSTEM_NPTH NEW macro. __assuan_read NEW (private). __assuan_write NEW (private). __assuan_recvmsg NEW (private). __assuan_sendmsg NEW (private). __assuan_waitpid NEW (private). ASSUAN_VERSION NEW macro. ASSUAN_VERSION_NUMBER NEW macro. assuan_check_version NEW. ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Noteworthy changes in version 2.0.3 (2011-12-20) ------------------------------------------------ * Make assuan_get_pid work correctly for pipe server. * Interface changes relative to the 2.0.2 release: ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ ASSUAN_FORCE_CLOSE NEW. ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Noteworthy changes in version 2.0.2 (2011-06-16) ------------------------------------------------ * A new flag may now be used to convey comments via assuan_transact. * A new flag value may now be used to disable logging. * The gpgcedev.c driver now provides a log device. * It is now possible to overwrite socket and connect functions in struct assuan_system_hooks. * Interface changes relative to the 2.0.1 release: ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ ASSUAN_CONVEY_COMMENTS NEW. ASSUAN_NO_LOGGING NEW. assuan_system_hooks_t CHANGED: Added socket and connect members. ASSUAN_SYSTEM_HOOKS_VERSION CHANGED: Bumped to 2. assuan_register_pre_cmd_notify NEW. ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Noteworthy changes in version 2.0.1 (2010-08-09) ------------------------------------------------ * Support for WindowsCE. * Input and output notification handler can now really access the parsed fd as stated in the manual. * Cleaned up the logging. * Interface changes relative to the 2.0.0 release: ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ assuan_free NEW. _assuan_w32ce_create_pipe NEW. ASSUAN_LOG_CONTROL NEW. ASSUAN_NO_LOGGING NEW. ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Noteworthy changes in version 2.0.0 (2010-01-08) ------------------------------------------------ * Now using libtool and builds a DSO. * Lots of interface cleanups. See below for details of the most important changes. Here is a quick note on how to upgrade: For each invocation of the connect or server functions, allocate a context with assuan_new and use that. Instead of assuan_disconnect or assuan_deinit_server, call assuan_release. Use assuan_set_gpg_err_source instead of assuan_set_assuan_err_source. If you use assuan_pipe_connect with NAME of NULL, you have to provide a non-NULL ARGV argument and check that against "server" or "client" to determine which end you got after fork(). If you use the assuan sock interface, you must call assuan_sock_init after setting global context defaults. Add a NULL as the last arg to assuan_register_command. * Pth support has changed. This now follows the same style as libgcrypt by setting system hook callbacks. * Interface changes relative to the 1.0.5 release: ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ _ASSUAN_ONLY_GPG_ERRORS REMOVED assuan_set_assuan_err_source REMOVED: Use assuan_set_gpg_err_source. assuan_set_gpg_err_source NEW assuan_get_gpg_err_source NEW assuan_strerror REMOVED ASSUAN_* Error values removed. assuan_error_t REMOVED AssuanError REMOVED assuan_init_connected_socket_server REMOVED assuan_pipe_connect2 REMOVED AssuanCommand REMOVED assuan_flag_t CHANGED: From enum to unsigned int. ASSUAN_CONTENT REMOVED assuan_disconnect REMOVED: Use assuan_release. assuan_deinit_server REMOVED: Use assuan_release. assuan_get_malloc_hooks NEW assuan_set_log_cb NEW assuan_get_log_cb NEW assuan_new_ext NEW assuan_new NEW assuan_release NEW assuan_init_socket_server CHANGED: Take ctx arg instead of pointer to ctx. CHANGED: As assuan_init_socket_server_ext was. assuan_init_socket_server_ext REMOVED assuan_socket_connect CHANGED: Take ctx arg instead of pointer to ctx. CHANGED: Is what assuan_socket_connect_ext was. assuan_socket_connect_ext REMOVED assuan_pipe_connect CHANGED: Take ctx arg instead of pointer to ctx. If NAME is NULL, ARGV will contain fork result. CHANGED: Is now what assuan_pipe_connect_ext was. CHANGED: Child fds are now assuan_fd_t. assuan_pipe_connect_ext REMOVED assuan_init_pipe_server CHANGED: Take ctx arg instead of pointer to ctx. CHANGED: Swallows fds (are closed at end). CHANGED: Take assuan_fd_t. assuan_fdopen NEW assuan_set_io_hooks REMOVED: Use assuan_system_hooks interface. assuan_io_hooks_t REMOVED: Use assuan_system_hooks interface. assuan_io_monitor_t CHANGED: Add a hook data argument. assuan_get_command_name NEW assuan_msghdr_t NEW ASSUAN_INVALID_PID NEW ASSUAN_NO_FIXSIGNALS NEW ASSUAN_SYSTEM_HOOKS_VERSION NEW assuan_system_hooks_t NEW assuan_set_system_hooks NEW assuan_ctx_set_system_hooks NEW ASSUAN_SYSTEM_PTH_IMPL NEW ASSUAN_SYSTEM_PTH_DECL NEW ASSUAN_SYSTEM_PTH NEW assuan_sock_init NEW assuan_sock_deinit NEW assuan_handler_t NEW assuan_register_command CHANGED: Add arg HELP_STRING. assuan_register_bye_notify CHANGED: Handler gets line and returns err now. assuan_register_reset_notify CHANGED: Handler gets line and returns err now. assuan_register_cancel_notify CHANGED: Handler gets line and returns err now. assuan_register_input_notify CHANGED: Handler returns error now. assuan_register_output_notify CHANGED: Handler returns error now. assuan_process_next CHANGED: New DONE argument instead EOF return. ASSUAN_PIPE_CONNECT_FDPASSING NEW ASSUAN_PIPE_CONNECT_DETACHED NEW ASSUAN_SOCKET_SERVER_FDPASSING NEW ASSUAN_SOCKET_SERVER_ACCEPTED NEW ASSUAN_SOCKET_CONNECT_FDPASSING NEW assuan_peercred_t NEW assuan_get_peercred CHANGED: Return assuan_peercred_t. assuan_client_read_response NEW assuan_client_parse_response NEW assuan_fd_from_posix_fd NEW ASSUAN_SPAWN_DETACHED NEW ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Noteworthy changes in version 1.0.5 (2008-05-25) ------------------------------------------------ * Minor fixes. * Build library for GNU/Linux as PIC. Noteworthy changes in version 1.0.4 (2007-12-12) ------------------------------------------------ * New socket wrapper functions to support Unix domain sockets under Windows. * New hook feature to enhance the internal I/O functions. Noteworthy changes in version 1.0.3 (2007-08-24) ------------------------------------------------ * New type assuan_fd_t and constant ASSUAN_INVALID_FD for better W32 support. It does not change the semantics under Unix. Under W32 all file descriptors are now guaranteed to be system handles. * New functions assuan_process_done and assuan_inquire_ext to support external event loops. * Changed the license of the library code back to LGPLv2.1 to support a bunch of GPLv2(only) software which does not allow the use of LGPLv3. Note that this is only a temporary change and authors of GPLv2(only) software are asked to switch to GPLv3 or to add an exception which allow the use of LPGLv3 software. Noteworthy changes in version 1.0.2 (2007-07-05) ------------------------------------------------ * Changed license to LGPLv3. * New flag ASSUAN_CONFIDENTIAL to return the state of confidential logging. * Better support for W32. * Assorted bug fixed and code cleanups. Noteworthy changes in version 1.0.1 (2006-11-22) ------------------------------------------------ * New function: assuan_set_io_monitor. * New function: assuan_register_post_cmd_notify. * Fixed a memory leak. Noteworthy changes in version 1.0.0 (2006-10-31) ------------------------------------------------ * Finished the manual. Noteworthy changes in version 0.9.3 (2006-10-10) ------------------------------------------------ * Portability fixes. * Pth is not anymore linked by means of weak symbol tricks. It is now required to link to the pth version of libassuan. New autoconf macros are provided to to check for this. The pth version is only build if Pth is available. * configure does now check that descripotor passing is available. A way to check at runtime for this is also provided Noteworthy changes in version 0.9.2 (2006-10-04) ------------------------------------------------ * A fix to make CANCEL work again in Pinentries. Noteworthy changes in version 0.9.1 (2006-10-04) ------------------------------------------------ * Minor bug fixes * Portability fixes. Noteworthy changes in version 0.9.0 (2006-09-14) ------------------------------------------------ * Internal cleanups to make inclusion of the code into libraries easier. * Made clear that the software is under the LGPL. * New function assuan_set_assuan_err_source. All gpg-error enabled software should call this right at startup to switch libassuan into the gpg-error style mode. All error codes are then returned as gpg-error style codes (GPG_ERR_ASS_* as well as others). If the new macro _ASSUAN_ONLY_GPG_ERRORS is defned all old definitions are excluded from assuan.h. * Logging of hex strings is now limited to 16 bytes. To enable printing of the full data, a new environment variable ASSUAN_FULL_LOGGING may be set to any value. * Removed the assuan_domain_* functions. * New functions assuan_pipe_connect_ext and assuan_socket_connect_ext to allow connections on a socketpair and to pass descriptors. * New function assuan_get_peercred. Noteworthy changes in version 0.6.10 (2005-06-20) ------------------------------------------------- * New functions assuan_get_flag and assuan_set_flag. * Add flag ASSUAN_NO_WAITPID. Noteworthy changes in version 0.6.9 (2004-12-22) ------------------------------------------------ * Ported to W32 based systems. Noteworthy changes in version 0.6.8 (2004-11-25) ------------------------------------------------ * assuan_write_status does now return an error code. * Fixes for C89 compatibility and some first takes on a W32 port. Noteworthy changes in version 0.6.7 (2004-09-27) ------------------------------------------------ * Minor build fixes. Noteworthy changes in version 0.6.6 (2004-06-08) ------------------------------------------------ * assuan_set_hello_line may now take a multi line argument where the first lines are send as comment lines and the last one as a OK line. Noteworthy changes in version 0.6.5 (2004-04-29) ------------------------------------------------ * Pass the client's pid to a pipe server. Noteworthy changes in version 0.6.4 (2004-02-20) ------------------------------------------------ * Will now also build on systems not providing funopen or fopencookie. * Some smaller build fixes. Noteworthy changes in version 0.6.3 (2004-01-29) ------------------------------------------------ * Fixed a data corruption bug in assuan_get_data_fp. * New function assuan_pipe_connect2. Noteworthy changes in version 0.6.2 (2003-12-18) ------------------------------------------------ * New function assuan_set_assuan_log_prefix to store a log prefix to be used when no context is available. The existing function assuan_get_assuan_log_context is not anymore declared as user overridable. * Documentation cleanups. Noteworthy changes in version 0.6.1 (2003-11-17) ------------------------------------------------ * Fixed a bug in assuan_inquire which led to an Invalid_Value error for large inquiry parameters. * Fixed a bug in the client socket connect code. Noteworthy changes in version 0.6.0 (2003-08-06) ------------------------------------------------ * Initial release as a standalone library. Copyright 2003, 2004, 2006, 2007, 2011, 2013 Free Software Foundation, Inc. This file is free software; as a special exception the author gives unlimited permission to copy and/or distribute it, with or without modifications, as long as this notice is preserved. This file is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY, to the extent permitted by law; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. diff --git a/configure.ac b/configure.ac index 29c66dd..b5c2cc7 100644 --- a/configure.ac +++ b/configure.ac @@ -1,473 +1,456 @@ # configure.ac - for libassuan # Copyright (C) 2001, 2002, 2003, 2006, 2007, 2009, # 2011, 2012, 2013 Free Software Foundation, Inc. # Copyright (C) 2013, 2014, 2015 g10 Code GmbH # # This file is part of Assuan. # # Assuan is free software; you can redistribute it and/or modify it # under the terms of the GNU Lesser General Public License as # published by the Free Software Foundation; either version 2.1 of # the License, or (at your option) any later version. # # Assuan 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 # Lesser General Public License for more details. # # You should have received a copy of the GNU Lesser General Public # License along with this program; if not, see . # SPDX-License-Identifier: LGPL-2.1+ # Process this file with autoconf to produce a configure script. AC_PREREQ([2.69]) min_automake_version="1.14" # To build a release you need to create a tag with the version number # (git tag -s libassuan-n.m.k) and run "./autogen.sh --force". Please # bump the version number immediately after the release and do another # commit and push so that the git magic is able to work. See below # for the LT versions. m4_define([mym4_package],[libassuan]) m4_define([mym4_major], [2]) m4_define([mym4_minor], [5]) m4_define([mym4_micro], [6]) # To start a new development series, i.e a new major or minor number # you need to mark an arbitrary commit before the first beta release # with an annotated tag. For example a 2.1 branch starts off with # the tag "foo-2.1-base". This is used as the base for counting # beta numbers before the first release of a series. # Below is m4 magic to extract and compute the git revision number, # the decimalized short revision number, a beta version string and a # flag indicating a development version (mym4_isbeta). Note that the # m4 processing is done by autoconf and not during the configure run. m4_define([mym4_verslist], m4_split(m4_esyscmd([./autogen.sh --find-version] \ mym4_package mym4_major mym4_minor mym4_micro),[:])) m4_define([mym4_isbeta], m4_argn(2, mym4_verslist)) m4_define([mym4_version], m4_argn(4, mym4_verslist)) m4_define([mym4_revision], m4_argn(7, mym4_verslist)) m4_define([mym4_revision_dec], m4_argn(8, mym4_verslist)) m4_esyscmd([echo ]mym4_version[>VERSION]) AC_INIT([mym4_package],[mym4_version],[https://bugs.gnupg.org]) # LT Version numbers, remember to change them just *before* a release. # (Code changed: REVISION++) # (Interfaces added/removed/changed: CURRENT++, REVISION=0) # (Interfaces added: AGE++) # (Interfaces removed/changed: AGE=0) # LIBASSUAN_LT_CURRENT=8 LIBASSUAN_LT_AGE=8 LIBASSUAN_LT_REVISION=5 # If the API is changed in an incompatible way: increment the next counter. LIBASSUAN_CONFIG_API_VERSION=2 ############################################## AC_SUBST(LIBASSUAN_LT_CURRENT) AC_SUBST(LIBASSUAN_LT_AGE) AC_SUBST(LIBASSUAN_LT_REVISION) AC_CONFIG_AUX_DIR([build-aux]) AM_INIT_AUTOMAKE([serial-tests dist-bzip2 no-dist-gzip]) AM_MAINTAINER_MODE AC_CONFIG_SRCDIR(src/assuan.h.in) AC_CONFIG_MACRO_DIR(m4) AC_CONFIG_HEADERS([config.h]) AC_CANONICAL_HOST AM_SILENT_RULES AB_INIT AC_USE_SYSTEM_EXTENSIONS # Taken from mpfr-4.0.1, then modified for LDADD_FOR_TESTS_KLUDGE dnl Under Linux, make sure that the old dtags are used if LD_LIBRARY_PATH dnl is defined. The issue is that with the new dtags, LD_LIBRARY_PATH has dnl the precedence over the run path, so that if a compatible MPFR library dnl is installed in some directory from $LD_LIBRARY_PATH, then the tested dnl MPFR library will be this library instead of the MPFR library from the dnl build tree. Other OS with the same issue might be added later. dnl dnl References: dnl https://bugs.debian.org/cgi-bin/bugreport.cgi?bug=859732 dnl http://lists.gnu.org/archive/html/libtool/2017-05/msg00000.html dnl dnl We need to check whether --disable-new-dtags is supported as alternate dnl linkers may be used (e.g., with tcc: CC=tcc LD=tcc). dnl case $host in *-*-linux*) if test -n "$LD_LIBRARY_PATH"; then saved_LDFLAGS="$LDFLAGS" LDADD_FOR_TESTS_KLUDGE="-Wl,--disable-new-dtags" LDFLAGS="$LDFLAGS $LDADD_FOR_TESTS_KLUDGE" AC_MSG_CHECKING(whether --disable-new-dtags is supported by the linker) AC_LINK_IFELSE([AC_LANG_SOURCE([[ int main (void) { return 0; } ]])], [AC_MSG_RESULT(yes (use it since LD_LIBRARY_PATH is set))], [AC_MSG_RESULT(no) LDADD_FOR_TESTS_KLUDGE="" ]) LDFLAGS="$saved_LDFLAGS" fi ;; esac AC_SUBST([LDADD_FOR_TESTS_KLUDGE]) VERSION_NUMBER=m4_esyscmd(printf "0x%02x%02x%02x" mym4_major \ mym4_minor mym4_micro) AC_SUBST(VERSION_NUMBER) # Don't default to build static libs. LT_PREREQ([2.2.6]) LT_INIT([win32-dll disable-static]) LT_LANG([Windows Resource]) # For now we hardcode the use of version scripts. It would be better # to write a test for this or even implement this within libtool. have_ld_version_script=no check_descriptor_passing=yes case "${host}" in *-*-cygwin*) check_descriptor_passing=no ;; *-*-linux*) have_ld_version_script=yes ;; *-*-gnu*) have_ld_version_script=yes ;; *-apple-darwin*) AC_DEFINE(_XOPEN_SOURCE, 500, Activate POSIX interface on MacOS X) AC_DEFINE(_DARWIN_C_SOURCE, 1, Expose all libc features (__DARWIN_C_FULL)) ;; esac AM_CONDITIONAL(HAVE_LD_VERSION_SCRIPT, test "$have_ld_version_script" = "yes") AH_TOP([ #ifndef _ASSUAN_CONFIG_H_INCLUDED #define _ASSUAN_CONFIG_H_INCLUDED -/* Enable gpg-error's strerror macro under W32CE. */ -#define GPG_ERR_ENABLE_ERRNO_MACROS 1 - /* Provide the es_ macro for estream. */ #define GPGRT_ENABLE_ES_MACROS 1 ]) AH_BOTTOM([ #endif /*_ASSUAN_CONFIG_H_INCLUDED*/ ]) # Checks for programs. missing_dir=`cd $ac_aux_dir && pwd` AM_MISSING_PROG(ACLOCAL, aclocal, $missing_dir) AM_MISSING_PROG(AUTOCONF, autoconf, $missing_dir) AM_MISSING_PROG(AUTOMAKE, automake, $missing_dir) AM_MISSING_PROG(AUTOHEADER, autoheader, $missing_dir) AM_MISSING_PROG(MAKEINFO, makeinfo, $missing_dir) AC_PROG_AWK AC_PROG_CC AC_PROG_CPP AM_PROG_CC_C_O if test "x$ac_cv_prog_cc_c89" = "xno" ; then AC_MSG_ERROR([[No C-89 compiler found]]) fi AC_PROG_INSTALL AC_PROG_LN_S AC_PROG_MAKE_SET #AC_ARG_PROGRAM # We need to compile and run a program on the build machine. AX_CC_FOR_BUILD if test "$GCC" = yes; then CFLAGS="$CFLAGS -Wall -Wcast-align -Wshadow -Wstrict-prototypes" AC_MSG_CHECKING([if gcc supports -Wpointer-arith]) _gcc_cflags_save=$CFLAGS CFLAGS="-Wpointer-arith" AC_COMPILE_IFELSE([AC_LANG_PROGRAM([])],_gcc_wopt=yes,_gcc_wopt=no) AC_MSG_RESULT($_gcc_wopt) CFLAGS=$_gcc_cflags_save; if test x"$_gcc_wopt" = xyes ; then CFLAGS="$CFLAGS -Wpointer-arith" fi fi # # Options depending on the host OS. # have_dosish_system=no have_w32_system=no have_w64_system=no -have_w32ce_system=no case "${host}" in *-linux*) if test "$GCC" = yes; then CFLAGS="$CFLAGS -fPIC -DPIC" fi ;; x86_64-*mingw32*) have_dosish_system=yes have_w32_system=yes have_w64_system=yes ;; - *-mingw32ce*) - have_dosish_system=yes - have_w32_system=yes - have_w32ce_system=yes - ;; *-mingw32*) have_dosish_system=yes have_w32_system=yes ;; *-solaris*) AC_DEFINE(_XOPEN_SOURCE, 500, Activate extensions on Solaris) AC_DEFINE(_XOPEN_SOURCE_EXTENDED, 1, Activate extensions on Solaris) AC_DEFINE(__EXTENSIONS__, 1, Activate extensions on Solaris) ;; esac if test "$have_dosish_system" = yes; then AC_DEFINE(HAVE_DOSISH_SYSTEM,1, [Defined if we run on some of the PCDOS like systems (DOS, Windoze. OS/2) with special properties like no file modes]) fi if test "$have_w32_system" = yes; then AC_DEFINE(HAVE_W32_SYSTEM,1,[Defined if we run on a W32 API based system]) if test "$have_w64_system" = yes; then AC_DEFINE(HAVE_W64_SYSTEM,1, [Defined if we run on a 64 bit W32 API based system]) fi - if test "$have_w32ce_system" = yes; then - AC_DEFINE(HAVE_W32CE_SYSTEM,1,[Defined if we run on WindowsCE]) - fi AC_CHECK_HEADERS([winsock2.h]) fi AM_CONDITIONAL(HAVE_W32_SYSTEM, test "$have_w32_system" = yes) -AM_CONDITIONAL(HAVE_W32CE_SYSTEM, test "$have_w32ce_system" = yes) AM_CONDITIONAL(HAVE_W64_SYSTEM, test "$have_w64_system" = yes) # # Provide information about the build. # BUILD_REVISION="mym4_revision" AC_SUBST(BUILD_REVISION) AC_DEFINE_UNQUOTED(BUILD_REVISION, "$BUILD_REVISION", [GIT commit id revision used to build this package]) changequote(,)dnl BUILD_FILEVERSION=`echo "$VERSION" | sed 's/\([0-9.]*\).*/\1./;s/\./,/g'` changequote([,])dnl BUILD_FILEVERSION="${BUILD_FILEVERSION}mym4_revision_dec" AC_SUBST(BUILD_FILEVERSION) AC_ARG_ENABLE([build-timestamp], AS_HELP_STRING([--enable-build-timestamp], [set an explicit build timestamp for reproducibility. (default is the current time in ISO-8601 format)]), [if test "$enableval" = "yes"; then BUILD_TIMESTAMP=`date -u +%Y-%m-%dT%H:%M+0000 2>/dev/null || date` else BUILD_TIMESTAMP="$enableval" fi], [BUILD_TIMESTAMP=""]) AC_SUBST(BUILD_TIMESTAMP) AC_DEFINE_UNQUOTED(BUILD_TIMESTAMP, "$BUILD_TIMESTAMP", [The time this package was configured for a build]) # # Check for network libraries. They are needed for tests. # AC_CHECK_FUNC(setsockopt, , AC_CHECK_LIB(socket, setsockopt, [NETLIBS="-lsocket $NETLIBS"])) AC_SUBST(NETLIBS) if test "$have_w32_system" = yes; then - if test "$have_w32ce_system" = yes; then - NETLIBS="-lws2 $NETLIBS" - else - # FIXME: Check why we need to use ws2_32 and document that. - NETLIBS="-lws2_32 $NETLIBS" - fi + # FIXME: Check why we need to use ws2_32 and document that. + NETLIBS="-lws2_32 $NETLIBS" fi # # Provide info for src/libassuan-config.in # LIBASSUAN_CONFIG_LIBS="-lassuan" LIBASSUAN_CONFIG_CFLAGS="" LIBASSUAN_CONFIG_HOST="$host" AC_SUBST(LIBASSUAN_CONFIG_LIBS) AC_SUBST(LIBASSUAN_CONFIG_CFLAGS) AC_SUBST(LIBASSUAN_CONFIG_HOST) AC_SUBST(LIBASSUAN_CONFIG_API_VERSION) # # Checks for header files. # AC_CHECK_HEADERS([locale.h sys/uio.h stdint.h inttypes.h \ sys/types.h sys/stat.h unistd.h sys/time.h fcntl.h \ sys/select.h ucred.h sys/ucred.h]) AC_TYPE_UINTPTR_T AC_TYPE_UINT16_T # # Checks for typedefs, structures, and compiler characteristics. # AC_C_CONST AC_C_INLINE AC_TYPE_SIZE_T gl_TYPE_SOCKLEN_T if test $check_descriptor_passing != yes; then use_descriptor_passing=no else AC_CHECK_MEMBER(struct cmsghdr.cmsg_len, [use_descriptor_passing=yes], [use_descriptor_passing=no AC_MSG_WARN([ *** *** Data structure for sending ancillary data missing. *** Descriptor passing won't work. ***])],[ #include #include #include #include #include #include #if HAVE_SYS_UIO_H #include #endif #include ]) fi if test "$use_descriptor_passing" = "yes"; then AC_DEFINE(USE_DESCRIPTOR_PASSING, 1, [Defined if descriptor passing is supported]) fi AM_CONDITIONAL(USE_DESCRIPTOR_PASSING, test "$use_descriptor_passing" = "yes") # Checking for libgpg-error. AM_PATH_GPG_ERROR(1.17,, AC_MSG_ERROR([libgpg-error was not found])) AM_CONDITIONAL(USE_GPGRT_CONFIG, [test -n "$GPGRT_CONFIG" \ -a "$ac_cv_path_GPG_ERROR_CONFIG" = no]) # # Checks for library functions. # AC_CHECK_FUNCS([flockfile funlockfile inet_pton stat getaddrinfo \ getrlimit ]) # If we didn't find inet_pton, it might be in -lsocket (which might # require -lnsl) if test X"$ac_cv_func_inet_pton" != X"yes" ; then AC_SEARCH_LIBS([inet_pton],[socket],[],[],[-lnsl]) if test X"$ac_cv_search_inet_pton" != X"no" ; then AC_DEFINE([HAVE_INET_PTON],1,[Define to 1 if you have `inet_pton'.]) fi fi # On some systems (e.g. Solaris) nanosleep requires linking to librl. # Given that we use nanosleep only as an optimization over a select # based wait function we want it only if it is available in libc. _save_libs="$LIBS" AC_SEARCH_LIBS([nanosleep], [], [AC_DEFINE(HAVE_NANOSLEEP,1, [Define to 1 if you have the `nanosleep' function in libc.])]) LIBS="$_save_libs" # Check for funopen AC_CHECK_FUNCS(funopen) if test $ac_cv_func_funopen != yes; then # No funopen but we can implement that in terms of fopencookie. AC_CHECK_FUNCS(fopencookie) if test $ac_cv_func_fopencookie = yes; then AC_LIBOBJ([funopen]) else AC_MSG_WARN([ *** *** No implementation of fopencookie or funopen available. *** The assuan_get_data_fp function won't work; see the *** manual for details. GnuPG does not require this feature. ***]) fi fi AC_REPLACE_FUNCS(isascii) AC_REPLACE_FUNCS(memrchr) AC_REPLACE_FUNCS(stpcpy) AC_CHECK_HEADERS(unistd.h) AC_REPLACE_FUNCS(setenv) # # Check for the getsockopt SO_PEERCRED, etc. # AC_CHECK_MEMBER(struct sockpeercred.pid, [AC_DEFINE(HAVE_STRUCT_SOCKPEERCRED_PID, 1, Define if struct sockpeercred contains the pid member.)], [], [#include #include ]) # (Open)Solaris AC_CHECK_FUNCS([getpeerucred]) # FreeBSD AC_CHECK_FUNCS([getpeereid]) # # Extra features # build_doc=yes AC_ARG_ENABLE([doc], AS_HELP_STRING([--disable-doc], [do not build the documentation]), build_doc=$enableval, build_doc=yes) AM_CONDITIONAL([BUILD_DOC], [test "x$build_doc" != xno]) # # Create the config files. # AC_CONFIG_FILES([Makefile]) AC_CONFIG_FILES([m4/Makefile]) AC_CONFIG_FILES([src/Makefile]) AC_CONFIG_FILES([doc/Makefile]) AC_CONFIG_FILES([tests/Makefile]) AC_CONFIG_FILES([src/libassuan-config], [chmod +x src/libassuan-config]) AC_CONFIG_FILES([src/versioninfo.rc]) AC_CONFIG_FILES([src/libassuan.pc]) AC_OUTPUT echo " Libassuan v${VERSION} has been configured as follows: Revision: mym4_revision (mym4_revision_dec) Platform: $host " diff --git a/contrib/ChangeLog-2011 b/contrib/ChangeLog-2011 deleted file mode 100644 index 65306e3..0000000 --- a/contrib/ChangeLog-2011 +++ /dev/null @@ -1,29 +0,0 @@ -2011-12-01 Werner Koch - - NB: ChangeLog files are no longer manually maintained. Starting - on December 1st, 2011 we put change information only in the GIT - commit log, and generate a top-level ChangeLog file from logs at - "make dist". See doc/HACKING for details. - -2010-11-15 Marcus Brinkmann - - * conf-w32ce-msc/stdint.h: New file. - * conf-w32ce-msc/build.mk (conf_sources): Add stdint.h. - (copy-static-source): Revert last change. - -2010-11-15 Werner Koch - - * conf-w32ce-msc/config.h (strdup, strcasecmp): Add macros. - - * conf-w32ce-msc/build.mk (copy-static-source): Create stdint.h. - (all): Add ws2.lib - (clean): New. - -2010-11-02 Werner Koch - - * conf-w32ce-msc/build.mk: Change directory layout. - -2010-11-01 Werner Koch - - * conf-w32ce-msc/build.mk: New. - * conf-w32ce-msc/config.h: New. diff --git a/contrib/conf-w32ce-msc/build.mk b/contrib/conf-w32ce-msc/build.mk deleted file mode 100755 index 70b9634..0000000 --- a/contrib/conf-w32ce-msc/build.mk +++ /dev/null @@ -1,169 +0,0 @@ -# build.mk - Makefile to build libgpg-error using Visual-C -# Copyright 2010 g10 Code GmbH -# -# This file is free software; as a special exception the author gives -# unlimited permission to copy and/or distribute it, with or without -# modifications, as long as this notice is preserved. -# -# This file is distributed in the hope that it will be useful, but -# WITHOUT ANY WARRANTY, to the extent permitted by law; without even the -# implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. - -# This is a helper make script to build libgpg-error for WindowsCE -# using the Microsoft Visual C compiler. - -# The target build directory where we run the Visual C compiler/ This -# needs to be an absolute directory name. Further we expect this -# structure of the tree: -# -# TARGET/src - Source directories: One directory for each project -# /bin - Installed DLLs -# /lib - Installed import libs. -# /include - Instaled header files. - -targetdir = /home/smb/xppro-gnu -targetsrc = $(targetdir)/src - -# Install directories (relative) -bindir = ../../../bin -libdir = ../../../lib -incdir = ../../../include - -help: - @echo "Run " - @echo " make -f ../contrib/conf-w32ce-msc/build.mk copy-source" - @echo "on the POSIX system and then" - @echo " nmake -f build.mk all" - @echo " nmake -f build.mk install" - @echo "on the Windows system" - -ce_defines = -DWINCE -D_WIN32_WCE=0x502 -DUNDER_CE \ - -DWIN32_PLATFORM_PSPC -D_UNICODE -DUNICODE \ - -D_CONSOLE -DARM -D_ARM_ -#-D_DEBUG -DDEBUG - -# See libgpg-error's build-mk for a list of compiler options. -CFLAGS = -nologo -W3 -fp:fast -Os $(ce_defines) \ - -DHAVE_CONFIG_H -DDLL_EXPORT -D_CRT_SECURE_NO_WARNINGS \ - -I. -I$(incdir) -I$(incdir)/gpg-extra - -LDFLAGS = - -# Standard source files -sources = \ - assuan.c \ - context.c \ - system.c \ - debug.c \ - conversion.c \ - sysutils.c \ - client.c \ - server.c \ - assuan-error.c \ - assuan-buffer.c \ - assuan-handler.c \ - assuan-inquire.c \ - assuan-listen.c \ - assuan-pipe-server.c \ - assuan-socket-server.c \ - assuan-pipe-connect.c \ - assuan-socket-connect.c \ - assuan-uds.c \ - assuan-logging.c \ - assuan-socket.c \ - system-w32ce.c \ - assuan-io.c \ - putc_unlocked.c \ - memrchr.c \ - stpcpy.c \ - setenv.c \ - vasprintf.c \ - assuan-defs.h \ - debug.h \ - libassuan.def - -# The object files we need to create from sources. -objs = \ - assuan.obj \ - context.obj \ - system.obj \ - debug.obj \ - conversion.obj \ - sysutils.obj \ - client.obj \ - server.obj \ - assuan-error.obj \ - assuan-buffer.obj \ - assuan-handler.obj \ - assuan-inquire.obj \ - assuan-listen.obj \ - assuan-pipe-server.obj \ - assuan-socket-server.obj \ - assuan-pipe-connect.obj \ - assuan-socket-connect.obj \ - assuan-uds.obj \ - assuan-logging.obj \ - assuan-socket.obj \ - system-w32ce.obj \ - assuan-io.obj \ - putc_unlocked.obj \ - memrchr.obj \ - stpcpy.obj \ - setenv.obj \ - vasprintf.obj - - -# Sources files in this directory inclduing this Makefile -conf_sources = \ - build.mk \ - config.h \ - stdint.h - -# Source files built by running the standard build system. -built_sources = \ - assuan.h - - -copy-static-source: - @if [ ! -f ./assuan-defs.h ]; then \ - echo "Please cd to the src/ directory first"; \ - exit 1; \ - fi - cp -t $(targetsrc)/libassuan/src $(sources); - cd ../contrib/conf-w32ce-msc ; \ - cp -t $(targetsrc)/libassuan/src $(conf_sources) - - -copy-built-source: - @if [ ! -f ./assuan.h ]; then \ - echo "Please build using ./autogen.sh --build-w32ce first"; \ - exit 1; \ - fi - cp -t $(targetsrc)/libassuan/src $(built_sources) - -copy-source: copy-static-source copy-built-source - - -.c.obj: - $(CC) $(CFLAGS) -c $< - -all: $(sources) $(conf_sources) $(built_sources) $(objs) - link /DLL /IMPLIB:libassuan-0-msc.lib \ - /OUT:libassuan-0-msc.dll \ - /DEF:libassuan.def /NOLOGO /MANIFEST:NO \ - /NODEFAULTLIB:"oldnames.lib" /DYNAMICBASE:NO \ - $(objs) $(libdir)/libgpg-error-0-msc.lib \ - coredll.lib corelibc.lib ole32.lib oleaut32.lib uuid.lib \ - commctrl.lib ws2.lib /subsystem:windowsce,5.02 - -# Note that we don't need to create the install directories because -# libgpg-error must have been build and installed prior to this -# package. -install: all - copy /y libassuan-0-msc.dll $(bindir:/=\) - copy /y libassuan-0-msc.lib $(libdir:/=\) - copy /y assuan.h $(incdir:/=\) - -clean: - del *.obj libassuan-0-msc.lib libassuan-0-msc.dll libassuan-0-msc.exp - diff --git a/contrib/conf-w32ce-msc/config.h b/contrib/conf-w32ce-msc/config.h deleted file mode 100644 index c247377..0000000 --- a/contrib/conf-w32ce-msc/config.h +++ /dev/null @@ -1,188 +0,0 @@ -/* config.h for building with Visual-C for WindowsCE. - * Copyright 2010 g10 Code GmbH - * - * This file is free software; as a special exception the author gives - * unlimited permission to copy and/or distribute it, with or without - * modifications, as long as this notice is preserved. - * - * This file is distributed in the hope that it will be useful, but - * WITHOUT ANY WARRANTY, to the extent permitted by law; without even the - * implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. - */ - -/* This file was originally created by running - * ./autogen.sh --build-w32ce - * on svn revision 389 (libassuan 2.0.2-svn389) and then adjusted to work - * with Visual-C. - */ - -#ifndef _ASSUAN_CONFIG_H_INCLUDED -#define _ASSUAN_CONFIG_H_INCLUDED - -/* Define to the version of this package. */ -#define PACKAGE_VERSION "2.0.2-svn389-msc1" - -/* Define to the full name and version of this package. */ -#define PACKAGE_STRING "libassuan " PACKAGE_VERSION - -/* Name of this package */ -#define PACKAGE "libassuan" - -/* Bug report address */ -#define PACKAGE_BUGREPORT "bug-libassuan@gnupg.org" - -/* Define to the full name of this package. */ -#define PACKAGE_NAME "libassuan" - - -/* Define to the one symbol short name of this package. */ -#define PACKAGE_TARNAME "libassuan" - -/* Define to the home page for this package. */ -#define PACKAGE_URL "" - -/* Version of this package */ -#define VERSION PACKAGE_VERSION - - -/* Enable gpg-error's strerror macro under W32CE. */ -#define GPG_ERR_ENABLE_ERRNO_MACROS 1 - - -/* Define to 1 if you have the declaration of `sys_siglist', and to 0 if you - don't. */ -#define HAVE_DECL_SYS_SIGLIST 0 - -/* Define to 1 if you have the header file. */ -/* #undef HAVE_DLFCN_H */ - -/* Defined if we run on some of the PCDOS like systems (DOS, Windoze. OS/2) - with special properties like no file modes */ -#define HAVE_DOSISH_SYSTEM 1 - -/* Define to 1 if you have the `flockfile' function. */ -/* #undef HAVE_FLOCKFILE */ - -/* Define to 1 if you have the `fopencookie' function. */ -/* #undef HAVE_FOPENCOOKIE */ - -/* Define to 1 if you have the `funlockfile' function. */ -/* #undef HAVE_FUNLOCKFILE */ - -/* Define to 1 if you have the `funopen' function. */ -/* #undef HAVE_FUNOPEN */ - -/* Define to 1 if you have the `inet_pton' function. */ -/* #undef HAVE_INET_PTON */ - -/* Define to 1 if you have the header file. */ -/* #undef HAVE_INTTYPES_H */ - -/* Define to 1 if you have the `isascii' function. */ -#define HAVE_ISASCII 1 - -/* Define to 1 if you have the header file. */ -/* #undef HAVE_LOCALE_H */ - -/* Define to 1 if you have the header file. */ -#define HAVE_MEMORY_H 1 - -/* Define to 1 if you have the `memrchr' function. */ -/* #undef HAVE_MEMRCHR */ - -/* Define to 1 if you have the `nanosleep' function in libc. */ -/* #undef HAVE_NANOSLEEP */ - -/* Define to 1 if you have the `putc_unlocked' function. */ -/* #undef HAVE_PUTC_UNLOCKED */ - -/* Define to 1 if you have the `setenv' function. */ -/* #undef HAVE_SETENV */ - -/* Defined if SO_PEERCRED is supported (Linux specific) */ -/* #undef HAVE_SO_PEERCRED */ - -/* Define to 1 if you have the header file. */ -#define HAVE_STDINT_H 1 - -/* Define to 1 if you have the header file. */ -#define HAVE_STDLIB_H 1 - -/* Define to 1 if you have the `stpcpy' function. */ -/* #undef HAVE_STPCPY */ - -/* Define to 1 if you have the header file. */ -/* #undef HAVE_STRINGS_H */ - -/* Define to 1 if you have the header file. */ -#define HAVE_STRING_H 1 - -/* Define to 1 if you have the header file. */ -/* #undef HAVE_SYS_SOCKET_H */ - -/* Define to 1 if you have the header file. */ -/* #undef HAVE_SYS_STAT_H */ - -/* Define to 1 if you have the header file. */ -/* #undef HAVE_SYS_TYPES_H */ - -/* Define to 1 if you have the header file. */ -/* #undef HAVE_SYS_UIO_H */ - -/* Define to 1 if the system has the type `uintptr_t'. */ -#define HAVE_UINTPTR_T 1 - -/* Define to 1 if you have the header file. */ -/* #undef HAVE_UNISTD_H */ - -/* Define to 1 if you have the `vasprintf' function. */ -/* #undef HAVE_VASPRINTF */ - -/* Defined if we run on WindowsCE */ -#define HAVE_W32CE_SYSTEM 1 - -/* Defined if we run on a W32 API based system */ -#define HAVE_W32_SYSTEM 1 - -/* Define to 1 if you have the header file. */ -#define HAVE_WINSOCK2_H 1 - -/* Define to 1 if you have the header file. */ -#define HAVE_WS2TCPIP_H 1 - -/* Define to 1 if your C compiler doesn't accept -c and -o together. */ -/* #undef NO_MINUS_C_MINUS_O */ - -/* Define as the return type of signal handlers (`int' or `void'). */ -#define RETSIGTYPE void - -/* Define to 1 if you have the ANSI C header files. */ -#define STDC_HEADERS 1 - -/* Defined if descriptor passing is supported */ -/* #undef USE_DESCRIPTOR_PASSING */ - -/* Enable extensions on AIX 3, Interix. */ -#ifndef _ALL_SOURCE -# define _ALL_SOURCE 1 -#endif -/* Enable GNU extensions on systems that have them. */ -#ifndef _GNU_SOURCE -# define _GNU_SOURCE 1 -#endif -/* Enable threading extensions on Solaris. */ -#ifndef _POSIX_PTHREAD_SEMANTICS -# define _POSIX_PTHREAD_SEMANTICS 1 -#endif - - -/* snprintf is not part of oldnames.lib thus we redefine it here. */ -#define snprintf _snprintf - -/* We also need to define these functions. */ -#define strdup _strdup -#define strcasecmp _stricmp - - -#endif /*_ASSUAN_CONFIG_H_INCLUDED*/ - diff --git a/contrib/conf-w32ce-msc/stdint.h b/contrib/conf-w32ce-msc/stdint.h deleted file mode 100755 index 0a821b7..0000000 --- a/contrib/conf-w32ce-msc/stdint.h +++ /dev/null @@ -1,9 +0,0 @@ -typedef unsigned long long uint64_t; -typedef long long int64_t; -typedef unsigned int uint32_t; -typedef int int32_t; -typedef unsigned short uint16_t; -typedef short int16_t; -typedef unsigned int uintptr_t; -typedef int intptr_t; - diff --git a/src/Makefile.am b/src/Makefile.am index 321aea3..f3a3aac 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -1,160 +1,139 @@ # Assuan Makefile # Copyright (C) 2001, 2002, 2003, 2009, 2010 Free Software Foundation, Inc. # # This file is part of Assuan. # # Assuan is free software; you can redistribute it and/or modify it # under the terms of the GNU Lesser General Public License as # published by the Free Software Foundation; either version 2.1 of # the License, or (at your option) any later version. # # Assuan 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 # Lesser General Public License for more details. # # You should have received a copy of the GNU Lesser General Public # License along with this program; if not, see . # # SPDX-License-Identifier: LGPL-2.1+ ## Process this file with automake to produce Makefile.in pkgconfigdir = $(libdir)/pkgconfig pkgconfig_DATA = libassuan.pc EXTRA_DIST = libassuan-config.in libassuan.m4 libassuan.vers \ - versioninfo.rc.in libassuan.def mkheader.c gpgcedev.def \ + versioninfo.rc.in libassuan.def mkheader.c \ libassuan.pc.in AM_CPPFLAGS = -I.. if USE_GPGRT_CONFIG noinst_SCRIPTS = libassuan-config else bin_SCRIPTS = libassuan-config endif m4datadir = $(datadir)/aclocal m4data_DATA = libassuan.m4 lib_LTLIBRARIES = libassuan.la -if HAVE_W32CE_SYSTEM -lib_LTLIBRARIES += libgpgcedev.la -bin_PROGRAMS = gpgcemgr -endif nodist_include_HEADERS = assuan.h if HAVE_LD_VERSION_SCRIPT libassuan_version_script_cmd = -Wl,--version-script=$(srcdir)/libassuan.vers else libassuan_version_script_cmd = endif CLEANFILES = mkheader assuan.h BUILT_SOURCES = assuan.h parts_of_assuan_h = \ - posix-includes.inc.h w32-includes.inc.h \ + posix-includes.inc.h \ posix-types.inc.h w32-types.inc.h \ - posix-fd-t.inc.h w32-fd-t.inc.h w32ce-fd-t.inc.h \ - posix-sock-nonce.inc.h w32-sock-nonce.inc.h \ - w32ce-add.h + posix-fd-t.inc.h w32-fd-t.inc.h \ + posix-sock-nonce.inc.h w32-sock-nonce.inc.h common_sources = \ assuan.h.in $(parts_of_assuan_h) \ assuan-defs.h \ assuan.c context.c system.c \ debug.c debug.h conversion.c sysutils.c \ client.c server.c \ assuan-error.c \ assuan-buffer.c \ assuan-handler.c \ assuan-inquire.c \ assuan-listen.c \ assuan-pipe-server.c \ assuan-socket-server.c \ assuan-pipe-connect.c \ assuan-socket-connect.c \ assuan-uds.c \ assuan-logging.c \ assuan-socket.c if HAVE_W32_SYSTEM -if HAVE_W32CE_SYSTEM -common_sources += system-w32ce.c -else common_sources += system-w32.c -endif else common_sources += system-posix.c endif if HAVE_W32_SYSTEM LTRCCOMPILE = $(LIBTOOL) --mode=compile $(RC) \ `echo $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) | \ sed -e 's/-I/--include-dir /g;s/-D/--define /g'` SUFFIXES: .rc .lo .rc.lo: $(LTRCCOMPILE) -i $< -o $@ libassuan_res = versioninfo.lo libassuan_res_ldflag = -Wl,.libs/versioninfo.o no_undefined = -no-undefined export_symbols = -export-symbols $(srcdir)/libassuan.def extra_ltoptions = -XCClinker -static-libgcc install-def-file: $(INSTALL) $(srcdir)/libassuan.def $(DESTDIR)$(libdir)/libassuan.def uninstall-def-file: -rm $(DESTDIR)$(libdir)/libassuan.def libassuan_deps = $(libassuan_res) libassuan.def else libassuan_res = libassuan_res_ldflag = no_undefined = export_symbols = extra_ltoptions = install-def-file: uninstall-def-file: libassuan_deps = endif libassuan_la_SOURCES = $(common_sources) assuan-io.c nodist_libassuan_la_SOURCES = assuan.h libassuan_la_CPPFLAGS = $(AM_CPPFLAGS) @GPG_ERROR_CFLAGS@ libassuan_la_LDFLAGS = $(libassuan_res_ldflag) $(no_undefined) \ $(extra_ltoptions) \ $(export_symbols) $(libassuan_version_script_cmd) -version-info \ @LIBASSUAN_LT_CURRENT@:@LIBASSUAN_LT_REVISION@:@LIBASSUAN_LT_AGE@ libassuan_la_DEPENDENCIES = @LTLIBOBJS@ \ $(srcdir)/libassuan.vers $(libassuan_deps) libassuan_la_LIBADD = @LTLIBOBJS@ @NETLIBS@ @GPG_ERROR_LIBS@ -if HAVE_W32CE_SYSTEM -libgpgcedev_la_SOURCES = gpgcedev.c -libgpgcedev_la_CPPFLAGS = $(AM_CPPFLAGS) -libgpgcedev_la_LDFLAGS = $(no_undefined) -export-symbols $(srcdir)/gpgcedev.def -libgpgcedev_la_DEPENDENCIES = gpgcedev.def -gpgcemgr_SOURCES = gpgcemgr.c -gpgcemgr_CPPFLAGS = $(AM_CPPFLAGS) -install-exec-hook: - mv -f $(DESTDIR)$(bindir)/libgpgcedev-0.dll \ - $(DESTDIR)$(bindir)/gpgcedev.dll -endif - mkheader$(EXEEXT_FOR_BUILD): mkheader.c Makefile $(CC_FOR_BUILD) $(CFLAGS_FOR_BUILD) $(CPPFLAGS_FOR_BUILD) \ $(LDFLAGS_FOR_BUILD) -I. -I$(srcdir) -o $@ $(srcdir)/mkheader.c assuan.h: assuan.h.in mkheader$(EXEEXT_FOR_BUILD) $(parts_of_assuan_h) ./mkheader$(EXEEXT_FOR_BUILD) $(host_os) $(srcdir)/assuan.h.in \ $(PACKAGE_VERSION) $(VERSION_NUMBER) >$@ diff --git a/src/assuan-buffer.c b/src/assuan-buffer.c index 7cb3032..895eb93 100644 --- a/src/assuan-buffer.c +++ b/src/assuan-buffer.c @@ -1,552 +1,550 @@ /* assuan-buffer.c - read and send data * Copyright (C) 2001-2004, 2006, 2009, 2010 Free Software Foundation, Inc. * * This file is part of Assuan. * * Assuan is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as * published by the Free Software Foundation; either version 2.1 of * the License, or (at your option) any later version. * * Assuan 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 * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this program; if not, see . * * SPDX-License-Identifier: LGPL-2.1+ */ #include #include #include #include #include #ifdef HAVE_UNISTD_H # include #endif #include #ifdef HAVE_W32_SYSTEM -#ifndef HAVE_W32CE_SYSTEM # include #endif -#endif #include "assuan-defs.h" /* Extended version of write(2) to guarantee that all bytes are written. Returns 0 on success or -1 and ERRNO on failure. NOTE: This function does not return the number of bytes written, so any error must be treated as fatal for this connection as the state of the receiver is unknown. This works best if blocking is allowed (so EAGAIN cannot occur). */ static int writen (assuan_context_t ctx, const char *buffer, size_t length) { while (length) { ssize_t nwritten = ctx->engine.writefnc (ctx, buffer, length); if (nwritten < 0) { if (errno == EINTR) continue; return -1; /* write error */ } length -= nwritten; buffer += nwritten; } return 0; /* okay */ } /* Read an entire line. Returns 0 on success or -1 and ERRNO on failure. EOF is indictated by setting the integer at address R_EOF. Note: BUF, R_NREAD and R_EOF contain a valid result even if an error is returned. */ static int readline (assuan_context_t ctx, char *buf, size_t buflen, int *r_nread, int *r_eof) { size_t nleft = buflen; char *p; *r_eof = 0; *r_nread = 0; while (nleft > 0) { ssize_t n = ctx->engine.readfnc (ctx, buf, nleft); if (n < 0) { if (errno == EINTR) continue; #ifdef HAVE_W32_SYSTEM if (errno == EPIPE) { /* Under Windows we get EPIPE (actually ECONNRESET) after termination of the client. Assume an EOF. */ *r_eof = 1; break; /* allow incomplete lines */ } #endif /*HAVE_W32_SYSTEM*/ return -1; /* read error */ } else if (!n) { *r_eof = 1; break; /* allow incomplete lines */ } p = buf; nleft -= n; buf += n; *r_nread += n; p = memrchr (p, '\n', n); if (p) break; /* at least one full line available - that's enough for now */ } return 0; } /* Read a line with buffering of partial lines. Function returns an Assuan error. */ gpg_error_t _assuan_read_line (assuan_context_t ctx) { gpg_error_t rc = 0; char *line = ctx->inbound.line; int nread, atticlen; char *endp = 0; if (ctx->inbound.eof) return _assuan_error (ctx, GPG_ERR_EOF); atticlen = ctx->inbound.attic.linelen; if (atticlen) { memcpy (line, ctx->inbound.attic.line, atticlen); ctx->inbound.attic.linelen = 0; endp = memchr (line, '\n', atticlen); if (endp) { /* Found another line in the attic. */ nread = atticlen; atticlen = 0; } else { /* There is pending data but not a full line. */ assert (atticlen < LINELENGTH); rc = readline (ctx, line + atticlen, LINELENGTH - atticlen, &nread, &ctx->inbound.eof); } } else /* No pending data. */ rc = readline (ctx, line, LINELENGTH, &nread, &ctx->inbound.eof); if (rc) { int saved_errno = errno; char buf[100]; snprintf (buf, sizeof buf, "error: %s", strerror (errno)); _assuan_log_control_channel (ctx, 0, buf, NULL, 0, NULL, 0); if (saved_errno == EAGAIN) { /* We have to save a partial line. Due to readline's behaviour, we know that this is not a complete line yet (no newline). So we don't set PENDING to true. */ memcpy (ctx->inbound.attic.line, line, atticlen + nread); ctx->inbound.attic.pending = 0; ctx->inbound.attic.linelen = atticlen + nread; } gpg_err_set_errno (saved_errno); return _assuan_error (ctx, gpg_err_code_from_syserror ()); } if (!nread) { assert (ctx->inbound.eof); _assuan_log_control_channel (ctx, 0, "eof", NULL, 0, NULL, 0); return _assuan_error (ctx, GPG_ERR_EOF); } ctx->inbound.attic.pending = 0; nread += atticlen; if (! endp) endp = memchr (line, '\n', nread); if (endp) { unsigned monitor_result; int n = endp - line + 1; if (n < nread) /* LINE contains more than one line. We copy it to the attic now as handlers are allowed to modify the passed buffer. */ { int len = nread - n; memcpy (ctx->inbound.attic.line, endp + 1, len); ctx->inbound.attic.pending = memrchr (endp + 1, '\n', len) ? 1 : 0; ctx->inbound.attic.linelen = len; } if (endp != line && endp[-1] == '\r') endp --; *endp = 0; ctx->inbound.linelen = endp - line; monitor_result = 0; if (ctx->io_monitor) monitor_result = ctx->io_monitor (ctx, ctx->io_monitor_data, 0, ctx->inbound.line, ctx->inbound.linelen); if (monitor_result & ASSUAN_IO_MONITOR_IGNORE) ctx->inbound.linelen = 0; if ( !(monitor_result & ASSUAN_IO_MONITOR_NOLOG)) _assuan_log_control_channel (ctx, 0, NULL, ctx->inbound.line, ctx->inbound.linelen, NULL, 0); return 0; } else { _assuan_log_control_channel (ctx, 0, "invalid line", NULL, 0, NULL, 0); *line = 0; ctx->inbound.linelen = 0; return _assuan_error (ctx, ctx->inbound.eof ? GPG_ERR_ASS_INCOMPLETE_LINE : GPG_ERR_ASS_LINE_TOO_LONG); } } /* Read the next line from the client or server and return a pointer in *LINE to a buffer holding the line. LINELEN is the length of *LINE. The buffer is valid until the next read operation on it. The caller may modify the buffer. The buffer is invalid (i.e. must not be used) if an error is returned. Returns 0 on success or an assuan error code. See also: assuan_pending_line(). */ gpg_error_t assuan_read_line (assuan_context_t ctx, char **line, size_t *linelen) { gpg_error_t err; if (!ctx) return _assuan_error (ctx, GPG_ERR_ASS_INV_VALUE); do { err = _assuan_read_line (ctx); } while (_assuan_error_is_eagain (ctx, err)); *line = ctx->inbound.line; *linelen = ctx->inbound.linelen; return err; } /* Return true if a full line is buffered (i.e. an entire line may be read without any I/O). */ int assuan_pending_line (assuan_context_t ctx) { return ctx && ctx->inbound.attic.pending; } gpg_error_t _assuan_write_line (assuan_context_t ctx, const char *prefix, const char *line, size_t len) { gpg_error_t rc = 0; size_t prefixlen = prefix? strlen (prefix):0; unsigned int monitor_result; /* Make sure that the line is short enough. */ if (len + prefixlen + 2 > ASSUAN_LINELENGTH) { _assuan_log_control_channel (ctx, 1, "supplied line too long - truncated", NULL, 0, NULL, 0); if (prefixlen > 5) prefixlen = 5; if (len > ASSUAN_LINELENGTH - prefixlen - 2) len = ASSUAN_LINELENGTH - prefixlen - 2 - 1; } monitor_result = 0; if (ctx->io_monitor) monitor_result = ctx->io_monitor (ctx, ctx->io_monitor_data, 1, line, len); /* Fixme: we should do some kind of line buffering. */ if (!(monitor_result & ASSUAN_IO_MONITOR_NOLOG)) _assuan_log_control_channel (ctx, 1, NULL, prefixlen? prefix:NULL, prefixlen, line, len); if (prefixlen && !(monitor_result & ASSUAN_IO_MONITOR_IGNORE)) { rc = writen (ctx, prefix, prefixlen); if (rc) rc = _assuan_error (ctx, gpg_err_code_from_syserror ()); } if (!rc && !(monitor_result & ASSUAN_IO_MONITOR_IGNORE)) { rc = writen (ctx, line, len); if (rc) rc = _assuan_error (ctx, gpg_err_code_from_syserror ()); if (!rc) { rc = writen (ctx, "\n", 1); if (rc) rc = _assuan_error (ctx, gpg_err_code_from_syserror ()); } } return rc; } gpg_error_t assuan_write_line (assuan_context_t ctx, const char *line) { size_t len; const char *str; if (! ctx) return _assuan_error (ctx, GPG_ERR_ASS_INV_VALUE); /* Make sure that we never take a LF from the user - this might violate the protocol. */ str = strchr (line, '\n'); len = str ? (str - line) : strlen (line); if (str) _assuan_log_control_channel (ctx, 1, "supplied line with LF - truncated", NULL, 0, NULL, 0); return _assuan_write_line (ctx, NULL, line, len); } /* Write out the data in buffer as datalines with line wrapping and percent escaping. This function is used for GNU's custom streams. */ int _assuan_cookie_write_data (void *cookie, const char *buffer, size_t orig_size) { assuan_context_t ctx = cookie; size_t size = orig_size; char *line; size_t linelen; if (ctx->outbound.data.error) return 0; line = ctx->outbound.data.line; linelen = ctx->outbound.data.linelen; line += linelen; while (size) { unsigned int monitor_result; /* Insert data line header. */ if (!linelen) { *line++ = 'D'; *line++ = ' '; linelen += 2; } /* Copy data, keep space for the CRLF and to escape one character. */ while (size && linelen < LINELENGTH-2-2) { if (*buffer == '%' || *buffer == '\r' || *buffer == '\n') { sprintf (line, "%%%02X", *(unsigned char*)buffer); line += 3; linelen += 3; buffer++; } else { *line++ = *buffer++; linelen++; } size--; } monitor_result = 0; if (ctx->io_monitor) monitor_result = ctx->io_monitor (ctx, ctx->io_monitor_data, 1, ctx->outbound.data.line, linelen); if (linelen >= LINELENGTH-2-2) { if (!(monitor_result & ASSUAN_IO_MONITOR_NOLOG)) _assuan_log_control_channel (ctx, 1, NULL, ctx->outbound.data.line, linelen, NULL, 0); *line++ = '\n'; linelen++; if ( !(monitor_result & ASSUAN_IO_MONITOR_IGNORE) && writen (ctx, ctx->outbound.data.line, linelen)) { ctx->outbound.data.error = gpg_err_code_from_syserror (); return 0; } line = ctx->outbound.data.line; linelen = 0; } } ctx->outbound.data.linelen = linelen; return (int) orig_size; } /* Write out any buffered data This function is used for GNU's custom streams */ int _assuan_cookie_write_flush (void *cookie) { assuan_context_t ctx = cookie; char *line; size_t linelen; unsigned int monitor_result; if (ctx->outbound.data.error) return 0; line = ctx->outbound.data.line; linelen = ctx->outbound.data.linelen; line += linelen; monitor_result = 0; if (ctx->io_monitor) monitor_result = ctx->io_monitor (ctx, ctx->io_monitor_data, 1, ctx->outbound.data.line, linelen); if (linelen) { if (!(monitor_result & ASSUAN_IO_MONITOR_NOLOG)) _assuan_log_control_channel (ctx, 1, NULL, ctx->outbound.data.line, linelen, NULL, 0); *line++ = '\n'; linelen++; if (! (monitor_result & ASSUAN_IO_MONITOR_IGNORE) && writen (ctx, ctx->outbound.data.line, linelen)) { ctx->outbound.data.error = gpg_err_code_from_syserror (); return 0; } ctx->outbound.data.linelen = 0; } return 0; } /** * assuan_send_data: * @ctx: An assuan context * @buffer: Data to send or NULL to flush * @length: length of the data to send/ * * This function may be used by the server or the client to send data * lines. The data will be escaped as required by the Assuan protocol * and may get buffered until a line is full. To force sending the * data out @buffer may be passed as NULL (in which case @length must * also be 0); however when used by a client this flush operation does * also send the terminating "END" command to terminate the response on * a INQUIRE response. However, when assuan_transact() is used, this * function takes care of sending END itself. * * If BUFFER is NULL and LENGTH is 1 and we are a client, a "CAN" is * send instead of an "END". * * Return value: 0 on success or an error code **/ gpg_error_t assuan_send_data (assuan_context_t ctx, const void *buffer, size_t length) { if (!ctx) return _assuan_error (ctx, GPG_ERR_ASS_INV_VALUE); if (!buffer && length > 1) return _assuan_error (ctx, GPG_ERR_ASS_INV_VALUE); if (!buffer) { /* flush what we have */ _assuan_cookie_write_flush (ctx); if (ctx->flags.confidential) wipememory (ctx->outbound.data.line, LINELENGTH); if (ctx->outbound.data.error) return ctx->outbound.data.error; if (!ctx->flags.is_server) return assuan_write_line (ctx, length == 1? "CAN":"END"); } else { _assuan_cookie_write_data (ctx, buffer, length); if (ctx->outbound.data.error) return ctx->outbound.data.error; } return 0; } gpg_error_t assuan_sendfd (assuan_context_t ctx, assuan_fd_t fd) { /* It is explicitly allowed to use (NULL, -1) as a runtime test to check whether descriptor passing is available. */ if (!ctx && fd == ASSUAN_INVALID_FD) #ifdef USE_DESCRIPTOR_PASSING return 0; #else return _assuan_error (ctx, GPG_ERR_NOT_IMPLEMENTED); #endif if (!ctx) return _assuan_error (ctx, GPG_ERR_ASS_INV_VALUE); if (! ctx->engine.sendfd) return set_error (ctx, GPG_ERR_NOT_IMPLEMENTED, "server does not support sending and receiving " "of file descriptors"); return ctx->engine.sendfd (ctx, fd); } gpg_error_t assuan_receivefd (assuan_context_t ctx, assuan_fd_t *fd) { if (!ctx) return _assuan_error (ctx, GPG_ERR_ASS_INV_VALUE); if (! ctx->engine.receivefd) return set_error (ctx, GPG_ERR_NOT_IMPLEMENTED, "server does not support sending and receiving " "of file descriptors"); return ctx->engine.receivefd (ctx, fd); } diff --git a/src/assuan-defs.h b/src/assuan-defs.h index 37a50af..47ff4ef 100644 --- a/src/assuan-defs.h +++ b/src/assuan-defs.h @@ -1,442 +1,433 @@ /* assuan-defs.h - Internal definitions to Assuan * Copyright (C) 2001, 2002, 2004, 2005, 2007, 2008, * 2009, 2010 Free Software Foundation, Inc. * * This file is part of Assuan. * * Assuan is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as * published by the Free Software Foundation; either version 2.1 of * the License, or (at your option) any later version. * * Assuan 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 * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this program; if not, see . * SPDX-License-Identifier: LGPL-2.1+ */ #ifndef ASSUAN_DEFS_H #define ASSUAN_DEFS_H #ifdef HAVE_SYS_TYPES_H # include #endif #ifndef HAVE_W32_SYSTEM # include # include #else # ifdef HAVE_WINSOCK2_H # /* Avoid inclusion of winsock.h via windows.h. */ # include # endif # include #endif #ifdef HAVE_UNISTD_H # include #endif #include "assuan.h" #if __GNUC__ > 2 # define ASSUAN_GCC_A_PURE __attribute__ ((__pure__)) #else # define ASSUAN_GCC_A_PURE #endif #ifndef HAVE_W32_SYSTEM #define DIRSEP_C '/' #else #define DIRSEP_C '\\' #endif #define LINELENGTH ASSUAN_LINELENGTH struct cmdtbl_s { const char *name; assuan_handler_t handler; const char *helpstr; }; /* The context we use with most functions. */ struct assuan_context_s { /* Members managed by the generic routines in assuan.c. */ /* The error source for errors generated from this context. */ gpg_err_source_t err_source; #ifdef HAVE_W32_SYSTEM /* The per-context w32 error string. */ char w32_strerror[256]; #endif /* The allocation hooks. */ struct assuan_malloc_hooks malloc_hooks; /* Logging callback handler. */ assuan_log_cb_t log_cb; void *log_cb_data; void *user_pointer; /* Context specific flags (cf. assuan_flag_t). */ struct { unsigned int no_waitpid : 1; unsigned int confidential : 1; unsigned int no_fixsignals : 1; unsigned int convey_comments : 1; unsigned int no_logging : 1; unsigned int force_close : 1; /* From here, we have internal flags, not defined by assuan_flag_t. */ unsigned int is_socket : 1; unsigned int is_server : 1; /* Set if this is context belongs to a server */ unsigned int in_inquire : 1; /* Server: inside assuan_inquire */ unsigned int in_process_next : 1; unsigned int process_complete : 1; unsigned int in_command : 1; unsigned int in_inq_cb : 1; /* Client: inquire callback is active */ unsigned int confidential_inquiry : 1; /* Client: inquiry is confidential */ } flags; /* If set, this is called right before logging an I/O line. */ assuan_io_monitor_t io_monitor; void *io_monitor_data; /* Callback handlers replacing system I/O functions. */ struct assuan_system_hooks system; int peercred_valid; /* Whether this structure has valid information. */ struct _assuan_peercred peercred; /* Now come the members specific to subsystems or engines. FIXME: This is not developed yet. See below for the legacy members. */ struct { void (*release) (assuan_context_t ctx); /* Routine to read from input_fd. Sets errno on failure. */ ssize_t (*readfnc) (assuan_context_t, void *, size_t); /* Routine to write to output_fd. Sets errno on failure. */ ssize_t (*writefnc) (assuan_context_t, const void *, size_t); /* Send a file descriptor. */ gpg_error_t (*sendfd) (assuan_context_t, assuan_fd_t); /* Receive a file descriptor. */ gpg_error_t (*receivefd) (assuan_context_t, assuan_fd_t *); } engine; /* Engine specific or other subsystem members. */ /* assuan-logging.c. Does not require deallocation from us. */ FILE *log_fp; /* assuan-util.c */ gpg_error_t err_no; const char *err_str; /* The following members are used by assuan_inquire_ext. */ gpg_error_t (*inquire_cb) (void *cb_data, gpg_error_t rc, unsigned char *buf, size_t len); void *inquire_cb_data; void *inquire_membuf; char *hello_line; char *okay_line; /* See assuan_set_okay_line() */ struct { assuan_fd_t fd; int eof; char line[LINELENGTH]; int linelen; /* w/o CR, LF - might not be the same as strlen(line) due to embedded nuls. However a nul is always written at this pos. */ struct { char line[LINELENGTH]; int linelen ; int pending; /* i.e. at least one line is available in the attic */ } attic; } inbound; struct { assuan_fd_t fd; struct { FILE *fp; char line[LINELENGTH]; int linelen; int error; } data; } outbound; int max_accepts; /* If we can not handle more than one connection, set this to 1, otherwise to -1. */ pid_t pid; /* The pid of the peer. */ assuan_fd_t listen_fd; /* The fd we are listening on (used by socket servers) */ assuan_sock_nonce_t listen_nonce; /* Used with LISTEN_FD. */ assuan_fd_t connected_fd; /* helper */ /* Used for Unix domain sockets. */ struct sockaddr_un myaddr; struct sockaddr_un serveraddr; /* Structure used for unix domain sockets. */ struct { assuan_fd_t pendingfds[5]; /* Array to save received descriptors. */ int pendingfdscount; /* Number of received descriptors. */ } uds; gpg_error_t (*accept_handler)(assuan_context_t); void (*finish_handler)(assuan_context_t); struct cmdtbl_s *cmdtbl; size_t cmdtbl_used; /* used entries */ size_t cmdtbl_size; /* allocated size of table */ /* The name of the command currently processed by a command handler. This is a pointer into CMDTBL. NULL if not in a command handler. */ const char *current_cmd_name; assuan_handler_t bye_notify_fnc; assuan_handler_t reset_notify_fnc; assuan_handler_t cancel_notify_fnc; gpg_error_t (*option_handler_fnc)(assuan_context_t,const char*, const char*); assuan_handler_t input_notify_fnc; assuan_handler_t output_notify_fnc; /* This function is called right before a command handler is called. */ gpg_error_t (*pre_cmd_notify_fnc)(assuan_context_t, const char *cmd); /* This function is called right after a command has been processed. It may be used to command related cleanup. */ void (*post_cmd_notify_fnc)(assuan_context_t, gpg_error_t); assuan_fd_t input_fd; /* Set by the INPUT command. */ assuan_fd_t output_fd; /* Set by the OUTPUT command. */ }; /* Generate an error code specific to a context. */ static GPG_ERR_INLINE gpg_error_t _assuan_error (assuan_context_t ctx, gpg_err_code_t errcode) { return gpg_err_make (ctx?ctx->err_source: GPG_ERR_SOURCE_ASSUAN, errcode); } /* Release all resources associated with an engine operation. */ void _assuan_reset (assuan_context_t ctx); /* Default log handler. */ int _assuan_log_handler (assuan_context_t ctx, void *hook, unsigned int cat, const char *msg); /* Manage memory specific to a context. */ void *_assuan_malloc (assuan_context_t ctx, size_t cnt); void *_assuan_realloc (assuan_context_t ctx, void *ptr, size_t cnt); void *_assuan_calloc (assuan_context_t ctx, size_t cnt, size_t elsize); void _assuan_free (assuan_context_t ctx, void *ptr); /* System hooks. */ void _assuan_usleep (assuan_context_t ctx, unsigned int usec); int _assuan_pipe (assuan_context_t ctx, assuan_fd_t fd[2], int inherit_idx); int _assuan_close (assuan_context_t ctx, assuan_fd_t fd); int _assuan_close_inheritable (assuan_context_t ctx, assuan_fd_t fd); ssize_t _assuan_read (assuan_context_t ctx, assuan_fd_t fd, void *buffer, size_t size); ssize_t _assuan_write (assuan_context_t ctx, assuan_fd_t fd, const void *buffer, size_t size); int _assuan_recvmsg (assuan_context_t ctx, assuan_fd_t fd, assuan_msghdr_t msg, int flags); int _assuan_sendmsg (assuan_context_t ctx, assuan_fd_t fd, assuan_msghdr_t msg, int flags); int _assuan_spawn (assuan_context_t ctx, pid_t *r_pid, const char *name, const char *argv[], assuan_fd_t fd_in, assuan_fd_t fd_out, assuan_fd_t *fd_child_list, void (*atfork) (void *opaque, int reserved), void *atforkvalue, unsigned int flags); pid_t _assuan_waitpid (assuan_context_t ctx, pid_t pid, int nowait, int *status, int options); int _assuan_socketpair (assuan_context_t ctx, int namespace, int style, int protocol, assuan_fd_t filedes[2]); assuan_fd_t _assuan_socket (assuan_context_t ctx, int namespace, int style, int protocol); int _assuan_connect (assuan_context_t ctx, assuan_fd_t sock, struct sockaddr *addr, socklen_t length); extern struct assuan_system_hooks _assuan_system_hooks; /* Copy the system hooks struct, paying attention to version differences. SRC is usually from the user, DST MUST be from the library. */ void _assuan_system_hooks_copy (assuan_system_hooks_t dst, assuan_system_hooks_t src); /*-- assuan-pipe-server.c --*/ void _assuan_release_context (assuan_context_t ctx); /*-- assuan-uds.c --*/ void _assuan_uds_close_fds (assuan_context_t ctx); void _assuan_uds_deinit (assuan_context_t ctx); void _assuan_init_uds_io (assuan_context_t ctx); /*-- assuan-handler.c --*/ gpg_error_t _assuan_register_std_commands (assuan_context_t ctx); /*-- assuan-buffer.c --*/ gpg_error_t _assuan_read_line (assuan_context_t ctx); int _assuan_cookie_write_data (void *cookie, const char *buffer, size_t size); int _assuan_cookie_write_flush (void *cookie); gpg_error_t _assuan_write_line (assuan_context_t ctx, const char *prefix, const char *line, size_t len); /*-- client.c --*/ gpg_error_t _assuan_read_from_server (assuan_context_t ctx, assuan_response_t *okay, int *off, int convey_comments); /*-- assuan-error.c --*/ /*-- assuan-inquire.c --*/ gpg_error_t _assuan_inquire_ext_cb (assuan_context_t ctx); void _assuan_inquire_release (assuan_context_t ctx); /* Check if ERR means EAGAIN. */ int _assuan_error_is_eagain (assuan_context_t ctx, gpg_error_t err); #define set_error(c,e,t) \ assuan_set_error ((c), _assuan_error (c,e), (t)) #ifdef HAVE_W32_SYSTEM char *_assuan_w32_strerror (assuan_context_t ctx, int ec); #endif /*HAVE_W32_SYSTEM*/ /*-- assuan-logging.c --*/ void _assuan_init_log_envvars (void); void _assuan_log_control_channel (assuan_context_t ctx, int outbound, const char *string, const void *buffer1, size_t length1, const void *buffer2, size_t length2); /*-- assuan-io.c --*/ ssize_t _assuan_simple_read (assuan_context_t ctx, void *buffer, size_t size); ssize_t _assuan_simple_write (assuan_context_t ctx, const void *buffer, size_t size); /*-- assuan-socket.c --*/ assuan_fd_t _assuan_sock_new (assuan_context_t ctx, int domain, int type, int proto); int _assuan_sock_connect (assuan_context_t ctx, assuan_fd_t sockfd, struct sockaddr *addr, int addrlen); int _assuan_sock_bind (assuan_context_t ctx, assuan_fd_t sockfd, struct sockaddr *addr, int addrlen); int _assuan_sock_set_sockaddr_un (const char *fname, struct sockaddr *addr, int *r_redirected); int _assuan_sock_get_nonce (assuan_context_t ctx, struct sockaddr *addr, int addrlen, assuan_sock_nonce_t *nonce); int _assuan_sock_check_nonce (assuan_context_t ctx, assuan_fd_t fd, assuan_sock_nonce_t *nonce); #ifdef HAVE_W32_SYSTEM wchar_t *_assuan_utf8_to_wchar (const char *string); int _assuan_sock_wsa2errno (int err); #endif #ifdef HAVE_FOPENCOOKIE /* We have to implement funopen in terms of glibc's fopencookie. */ FILE *_assuan_funopen(void *cookie, cookie_read_function_t *readfn, cookie_write_function_t *writefn, cookie_seek_function_t *seekfn, cookie_close_function_t *closefn); #define funopen(a,r,w,s,c) _assuan_funopen ((a), (r), (w), (s), (c)) #endif /*HAVE_FOPENCOOKIE*/ /*-- sysutils.c --*/ const char *_assuan_sysutils_blurb (void); -#ifdef HAVE_W32CE_SYSTEM - -#define getpid() GetCurrentProcessId () -char *_assuan_getenv (const char *name); -#define getenv(a) _assuan_getenv ((a)) - -#endif /*HAVE_W32CE_SYSTEM*/ - - /* Prototypes for replacement functions. */ #ifndef HAVE_MEMRCHR void *memrchr (const void *block, int c, size_t size); #endif #ifndef HAVE_STPCPY char *stpcpy (char *dest, const char *src); #endif #ifndef HAVE_SETENV #define setenv _assuan_setenv #define unsetenv _assuan_unsetenv #define clearenv _assuan_clearenv int setenv (const char *name, const char *value, int replace); #endif #ifndef HAVE_PUTC_UNLOCKED int putc_unlocked (int c, FILE *stream); #endif #define DIM(v) (sizeof(v)/sizeof((v)[0])) /* To avoid that a compiler optimizes memset calls away, these macros can be used. */ #define wipememory2(_ptr,_set,_len) do { \ volatile char *_vptr=(volatile char *)(_ptr); \ size_t _vlen=(_len); \ while(_vlen) { *_vptr=(_set); _vptr++; _vlen--; } \ } while(0) #define wipememory(_ptr,_len) wipememory2(_ptr,0,_len) #if HAVE_W64_SYSTEM # define SOCKET2HANDLE(s) ((void *)(s)) # define HANDLE2SOCKET(h) ((uintptr_t)(h)) #elif HAVE_W32_SYSTEM # define SOCKET2HANDLE(s) ((void *)(s)) # define HANDLE2SOCKET(h) ((unsigned int)(h)) #else # define SOCKET2HANDLE(s) (s) # define HANDLE2SOCKET(h) (h) #endif void _assuan_client_finish (assuan_context_t ctx); void _assuan_client_release (assuan_context_t ctx); void _assuan_server_finish (assuan_context_t ctx); void _assuan_server_release (assuan_context_t ctx); /* Encode the C formatted string SRC and return the malloc'ed result. */ char *_assuan_encode_c_string (assuan_context_t ctx, const char *src); void _assuan_pre_syscall (void); void _assuan_post_syscall (void); #endif /*ASSUAN_DEFS_H*/ diff --git a/src/assuan-error.c b/src/assuan-error.c index 8799203..57fb740 100644 --- a/src/assuan-error.c +++ b/src/assuan-error.c @@ -1,68 +1,63 @@ /* assuan-error.c * Copyright (C) 2009 Free Software Foundation, Inc. * * This file is part of Assuan. * * Assuan is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as * published by the Free Software Foundation; either version 2.1 of * the License, or (at your option) any later version. * * Assuan 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 * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this program; if not, see . * SPDX-License-Identifier: LGPL-2.1+ */ #ifdef HAVE_CONFIG_H #include #endif #include #include #include #undef _ASSUAN_IN_LIBASSUAN /* undef to get all error codes. */ #include "assuan.h" #include "assuan-defs.h" /* A small helper function to treat EAGAIN transparently to the caller. */ int _assuan_error_is_eagain (assuan_context_t ctx, gpg_error_t err) { if (gpg_err_code (err) == GPG_ERR_EAGAIN) { /* Avoid spinning by sleeping for one tenth of a second. */ _assuan_usleep (ctx, 100000); return 1; } else return 0; } #ifdef HAVE_W32_SYSTEM char * _assuan_w32_strerror (assuan_context_t ctx, int ec) { if (ec == -1) ec = (int)GetLastError (); -#ifdef HAVE_W32CE_SYSTEM - snprintf (ctx->w32_strerror, sizeof (ctx->w32_strerror) - 1, - "ec=%d", (int)GetLastError ()); -#else FormatMessage (FORMAT_MESSAGE_FROM_SYSTEM, NULL, ec, MAKELANGID (LANG_NEUTRAL, SUBLANG_DEFAULT), ctx->w32_strerror, sizeof (ctx->w32_strerror) - 1, NULL); -#endif return ctx->w32_strerror; } #endif diff --git a/src/assuan-handler.c b/src/assuan-handler.c index a572b62..126eccb 100644 --- a/src/assuan-handler.c +++ b/src/assuan-handler.c @@ -1,1069 +1,1047 @@ /* assuan-handler.c - dispatch commands * Copyright (C) 2001, 2002, 2003, 2007, 2009, * 2011 Free Software Foundation, Inc. * * This file is part of Assuan. * * Assuan is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as * published by the Free Software Foundation; either version 2.1 of * the License, or (at your option) any later version. * * Assuan 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 * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this program; if not, see . * SPDX-License-Identifier: LGPL-2.1+ */ #ifdef HAVE_CONFIG_H #include #endif #include #include #include #include #include "assuan-defs.h" #include "debug.h" #define spacep(p) (*(p) == ' ' || *(p) == '\t') #define digitp(a) ((a) >= '0' && (a) <= '9') static int my_strcasecmp (const char *a, const char *b); #define PROCESS_DONE(ctx, rc) \ ((ctx)->flags.in_process_next ? assuan_process_done ((ctx), (rc)) : (rc)) static gpg_error_t dummy_handler (assuan_context_t ctx, char *line) { return PROCESS_DONE (ctx, set_error (ctx, GPG_ERR_ASSUAN_SERVER_FAULT, "no handler registered")); } static const char std_help_nop[] = "NOP\n" "\n" "No operation. Returns OK without any action."; static gpg_error_t std_handler_nop (assuan_context_t ctx, char *line) { return PROCESS_DONE (ctx, 0); /* okay */ } static const char std_help_cancel[] = "CANCEL\n" "\n" "Run the server's cancel handler if one has been registered."; static gpg_error_t std_handler_cancel (assuan_context_t ctx, char *line) { if (ctx->cancel_notify_fnc) /* Return value ignored. */ ctx->cancel_notify_fnc (ctx, line); return PROCESS_DONE (ctx, set_error (ctx, GPG_ERR_NOT_IMPLEMENTED, NULL)); } static const char std_help_option[] = "OPTION [ [=] ]\n" "\n" "Set option to configure server operation. Leading and\n" "trailing spaces around and are allowed but should be\n" "ignored. For compatibility reasons, may be prefixed with two\n" "dashes. The use of the equal sign is optional but suggested if\n" " is given."; static gpg_error_t std_handler_option (assuan_context_t ctx, char *line) { char *key, *value, *p; for (key=line; spacep (key); key++) ; if (!*key) return PROCESS_DONE (ctx, set_error (ctx, GPG_ERR_ASS_SYNTAX, "argument required")); if (*key == '=') return PROCESS_DONE (ctx, set_error (ctx, GPG_ERR_ASS_SYNTAX, "no option name given")); for (value=key; *value && !spacep (value) && *value != '='; value++) ; if (*value) { if (spacep (value)) *value++ = 0; /* terminate key */ for (; spacep (value); value++) ; if (*value == '=') { *value++ = 0; /* terminate key */ for (; spacep (value); value++) ; if (!*value) return PROCESS_DONE (ctx, set_error (ctx, GPG_ERR_ASS_SYNTAX, "option argument expected")); } if (*value) { for (p = value + strlen(value) - 1; p > value && spacep (p); p--) ; if (p > value) *++p = 0; /* strip trailing spaces */ } } if (*key == '-' && key[1] == '-' && key[2]) key += 2; /* the double dashes are optional */ if (*key == '-') return PROCESS_DONE (ctx, set_error (ctx, GPG_ERR_ASS_SYNTAX, "option should not begin with one dash")); if (ctx->option_handler_fnc) return PROCESS_DONE (ctx, ctx->option_handler_fnc (ctx, key, value)); return PROCESS_DONE (ctx, 0); } static const char std_help_bye[] = "BYE\n" "\n" "Close the connection. The server will reply with OK."; static gpg_error_t std_handler_bye (assuan_context_t ctx, char *line) { if (ctx->bye_notify_fnc) /* Return value ignored. */ ctx->bye_notify_fnc (ctx, line); assuan_close_input_fd (ctx); assuan_close_output_fd (ctx); /* pretty simple :-) */ ctx->flags.process_complete = 1; return PROCESS_DONE (ctx, 0); } static const char std_help_auth[] = "AUTH\n" "\n" "Reserved for future extensions."; static gpg_error_t std_handler_auth (assuan_context_t ctx, char *line) { return PROCESS_DONE (ctx, set_error (ctx, GPG_ERR_NOT_IMPLEMENTED, NULL)); } static const char std_help_reset[] = "RESET\n" "\n" "Reset the connection but not any existing authentication. The server\n" "should release all resources associated with the connection."; static gpg_error_t std_handler_reset (assuan_context_t ctx, char *line) { gpg_error_t err = 0; if (ctx->reset_notify_fnc) err = ctx->reset_notify_fnc (ctx, line); if (! err) { assuan_close_input_fd (ctx); assuan_close_output_fd (ctx); _assuan_uds_close_fds (ctx); } return PROCESS_DONE (ctx, err); } static const char std_help_help[] = "HELP []\n" "\n" "Lists all commands that the server understands as comment lines on\n" "the status channel. If is given, list detailed help for\n" "that command."; static gpg_error_t std_handler_help (assuan_context_t ctx, char *line) { unsigned int i; char buf[ASSUAN_LINELENGTH]; const char *helpstr; size_t n; n = strcspn (line, " \t\n"); if (!n) { /* Print all commands. If a help string is available and that starts with the command name, print the first line of the help string. */ for (i = 0; i < ctx->cmdtbl_used; i++) { n = strlen (ctx->cmdtbl[i].name); helpstr = ctx->cmdtbl[i].helpstr; if (helpstr && !strncmp (ctx->cmdtbl[i].name, helpstr, n) && (!helpstr[n] || helpstr[n] == '\n' || helpstr[n] == ' ') && (n = strcspn (helpstr, "\n")) ) snprintf (buf, sizeof (buf), "# %.*s", (int)n, helpstr); else snprintf (buf, sizeof (buf), "# %s", ctx->cmdtbl[i].name); buf[ASSUAN_LINELENGTH - 1] = '\0'; assuan_write_line (ctx, buf); } } else { /* Print the help for the given command. */ int c = line[n]; line[n] = 0; for (i=0; ctx->cmdtbl[i].name; i++) if (!my_strcasecmp (line, ctx->cmdtbl[i].name)) break; line[n] = c; if (!ctx->cmdtbl[i].name) return PROCESS_DONE (ctx, set_error (ctx,GPG_ERR_UNKNOWN_COMMAND,NULL)); helpstr = ctx->cmdtbl[i].helpstr; if (!helpstr) return PROCESS_DONE (ctx, set_error (ctx, GPG_ERR_NOT_FOUND, NULL)); do { n = strcspn (helpstr, "\n"); snprintf (buf, sizeof (buf), "# %.*s", (int)n, helpstr); helpstr += n; if (*helpstr == '\n') helpstr++; buf[ASSUAN_LINELENGTH - 1] = '\0'; assuan_write_line (ctx, buf); } while (*helpstr); } return PROCESS_DONE (ctx, 0); } static const char std_help_end[] = "END\n" "\n" "Used by a client to mark the end of raw data."; static gpg_error_t std_handler_end (assuan_context_t ctx, char *line) { return PROCESS_DONE (ctx, set_error (ctx, GPG_ERR_NOT_IMPLEMENTED, NULL)); } gpg_error_t assuan_command_parse_fd (assuan_context_t ctx, char *line, assuan_fd_t *rfd) { char *endp; if ((strncmp (line, "FD", 2) && strncmp (line, "fd", 2)) || (line[2] != '=' && line[2] != '\0' && !spacep(&line[2]))) return set_error (ctx, GPG_ERR_ASS_SYNTAX, "FD[=] expected"); line += 2; if (*line == '=') { line ++; if (!digitp (*line)) return set_error (ctx, GPG_ERR_ASS_SYNTAX, "number required"); #if HAVE_W64_SYSTEM *rfd = (void*)strtoull (line, &endp, 10); #elif HAVE_W32_SYSTEM *rfd = (void*)strtoul (line, &endp, 10); #else *rfd = strtoul (line, &endp, 10); #endif /* Remove that argument so that a notify handler won't see it. */ memset (line, ' ', endp? (endp-line):strlen(line)); if (*rfd == ctx->inbound.fd) return set_error (ctx, GPG_ERR_ASS_PARAMETER, "fd same as inbound fd"); if (*rfd == ctx->outbound.fd) return set_error (ctx, GPG_ERR_ASS_PARAMETER, "fd same as outbound fd"); return 0; } else /* Our peer has sent the file descriptor. */ return assuan_receivefd (ctx, rfd); } static const char std_help_input[] = "INPUT FD[=]\n" "\n" "Used by a client to pass an input file descriptor to the server.\n" "The server opens as a local file descriptor. Without , the\n" "server opens the file descriptor just sent by the client using\n" "assuan_sendfd."; static gpg_error_t std_handler_input (assuan_context_t ctx, char *line) { gpg_error_t rc; assuan_fd_t fd, oldfd; rc = assuan_command_parse_fd (ctx, line, &fd); if (rc) return PROCESS_DONE (ctx, rc); -#ifdef HAVE_W32CE_SYSTEM - oldfd = fd; - fd = _assuan_w32ce_finish_pipe ((int)fd, 0); - if (fd == INVALID_HANDLE_VALUE) - return PROCESS_DONE (ctx, set_error (ctx, GPG_ERR_ASS_PARAMETER, - "rvid conversion failed")); - TRACE2 (ctx, ASSUAN_LOG_SYSIO, "std_handler_input", ctx, - "turned RVID 0x%x into handle 0x%x", oldfd, fd); -#endif - if (ctx->input_notify_fnc) { oldfd = ctx->input_fd; ctx->input_fd = fd; rc = ctx->input_notify_fnc (ctx, line); if (rc) ctx->input_fd = oldfd; } else if (!rc) ctx->input_fd = fd; return PROCESS_DONE (ctx, rc); } static const char std_help_output[] = "OUTPUT FD[=]\n" "\n" "Used by a client to pass an output file descriptor to the server.\n" "The server opens as a local file descriptor. Without , the\n" "server opens the file descriptor just sent by the client using\n" "assuan_sendfd."; static gpg_error_t std_handler_output (assuan_context_t ctx, char *line) { gpg_error_t rc; assuan_fd_t fd, oldfd; rc = assuan_command_parse_fd (ctx, line, &fd); if (rc) return PROCESS_DONE (ctx, rc); -#ifdef HAVE_W32CE_SYSTEM - oldfd = fd; - fd = _assuan_w32ce_finish_pipe ((int)fd, 1); - if (fd == INVALID_HANDLE_VALUE) - return PROCESS_DONE (ctx, set_error (ctx, gpg_err_code_from_syserror (), - "rvid conversion failed")); - TRACE2 (ctx, ASSUAN_LOG_SYSIO, "std_handler_output", ctx, - "turned RVID 0x%x into handle 0x%x", oldfd, fd); -#endif - if (ctx->output_notify_fnc) { oldfd = ctx->output_fd; ctx->output_fd = fd; rc = ctx->output_notify_fnc (ctx, line); if (rc) ctx->output_fd = oldfd; } else if (!rc) ctx->output_fd = fd; return PROCESS_DONE (ctx, rc); } /* This is a table with the standard commands and handler for them. The table is used to initialize a new context and associate strings with default handlers */ static struct { const char *name; gpg_error_t (*handler)(assuan_context_t, char *line); const char *help; int always; /* always initialize this command */ } std_cmd_table[] = { { "NOP", std_handler_nop, std_help_nop, 1 }, { "CANCEL", std_handler_cancel, std_help_cancel, 1 }, { "OPTION", std_handler_option, std_help_option, 1 }, { "BYE", std_handler_bye, std_help_bye, 1 }, { "AUTH", std_handler_auth, std_help_auth, 1 }, { "RESET", std_handler_reset, std_help_reset, 1 }, { "END", std_handler_end, std_help_end, 1 }, { "HELP", std_handler_help, std_help_help, 1 }, { "INPUT", std_handler_input, std_help_input, 0 }, { "OUTPUT", std_handler_output, std_help_output, 0 }, { } }; /** * assuan_register_command: * @ctx: the server context * @cmd_name: A string with the command name * @handler: The handler function to be called or NULL to use a default * handler. * HELPSTRING * * Register a handler to be used for a given command. Note that * several default handlers are already registered with a new context. * This function however allows to override them. * * Return value: 0 on success or an error code **/ gpg_error_t assuan_register_command (assuan_context_t ctx, const char *cmd_name, assuan_handler_t handler, const char *help_string) { int i, cmd_index = -1; const char *s; if (cmd_name && !*cmd_name) cmd_name = NULL; if (!cmd_name) return _assuan_error (ctx, GPG_ERR_ASS_INV_VALUE); if (!handler) { /* find a default handler. */ for (i=0; (s=std_cmd_table[i].name) && strcmp (cmd_name, s); i++) ; if (!s) { /* Try again but case insensitive. */ for (i=0; (s=std_cmd_table[i].name) && my_strcasecmp (cmd_name, s); i++) ; } if (s) handler = std_cmd_table[i].handler; if (!handler) handler = dummy_handler; /* Last resort is the dummy handler. */ } if (!ctx->cmdtbl) { ctx->cmdtbl_size = 50; ctx->cmdtbl = _assuan_calloc (ctx, ctx->cmdtbl_size, sizeof *ctx->cmdtbl); if (!ctx->cmdtbl) return _assuan_error (ctx, gpg_err_code_from_syserror ()); ctx->cmdtbl_used = 0; } else if (ctx->cmdtbl_used >= ctx->cmdtbl_size) { struct cmdtbl_s *x; x = _assuan_realloc (ctx, ctx->cmdtbl, (ctx->cmdtbl_size+10) * sizeof *x); if (!x) return _assuan_error (ctx, gpg_err_code_from_syserror ()); ctx->cmdtbl = x; ctx->cmdtbl_size += 50; } for (i=0; icmdtbl_used; i++) { if (!my_strcasecmp (cmd_name, ctx->cmdtbl[i].name)) { cmd_index = i; break; } } if (cmd_index == -1) cmd_index = ctx->cmdtbl_used++; ctx->cmdtbl[cmd_index].name = cmd_name; ctx->cmdtbl[cmd_index].handler = handler; ctx->cmdtbl[cmd_index].helpstr = help_string; return 0; } /* Return the name of the command currently processed by a handler. The string returned is valid until the next call to an assuan function on the same context. Returns NULL if no handler is executed or the command is not known. */ const char * assuan_get_command_name (assuan_context_t ctx) { return ctx? ctx->current_cmd_name : NULL; } gpg_error_t assuan_register_pre_cmd_notify (assuan_context_t ctx, gpg_error_t (*fnc)(assuan_context_t, const char *cmd)) { if (!ctx) return _assuan_error (ctx, GPG_ERR_ASS_INV_VALUE); ctx->pre_cmd_notify_fnc = fnc; return 0; } gpg_error_t assuan_register_post_cmd_notify (assuan_context_t ctx, void (*fnc)(assuan_context_t, gpg_error_t)) { if (!ctx) return _assuan_error (ctx, GPG_ERR_ASS_INV_VALUE); ctx->post_cmd_notify_fnc = fnc; return 0; } gpg_error_t assuan_register_bye_notify (assuan_context_t ctx, assuan_handler_t fnc) { if (!ctx) return _assuan_error (ctx, GPG_ERR_ASS_INV_VALUE); ctx->bye_notify_fnc = fnc; return 0; } gpg_error_t assuan_register_reset_notify (assuan_context_t ctx, assuan_handler_t fnc) { if (!ctx) return _assuan_error (ctx, GPG_ERR_ASS_INV_VALUE); ctx->reset_notify_fnc = fnc; return 0; } gpg_error_t assuan_register_cancel_notify (assuan_context_t ctx, assuan_handler_t fnc) { if (!ctx) return _assuan_error (ctx, GPG_ERR_ASS_INV_VALUE); ctx->cancel_notify_fnc = fnc; return 0; } gpg_error_t assuan_register_option_handler (assuan_context_t ctx, gpg_error_t (*fnc)(assuan_context_t, const char*, const char*)) { if (!ctx) return _assuan_error (ctx, GPG_ERR_ASS_INV_VALUE); ctx->option_handler_fnc = fnc; return 0; } gpg_error_t assuan_register_input_notify (assuan_context_t ctx, assuan_handler_t fnc) { if (!ctx) return _assuan_error (ctx, GPG_ERR_ASS_INV_VALUE); ctx->input_notify_fnc = fnc; return 0; } gpg_error_t assuan_register_output_notify (assuan_context_t ctx, assuan_handler_t fnc) { if (!ctx) return _assuan_error (ctx, GPG_ERR_ASS_INV_VALUE); ctx->output_notify_fnc = fnc; return 0; } /* Helper to register the standards commands */ gpg_error_t _assuan_register_std_commands (assuan_context_t ctx) { gpg_error_t rc; int i; for (i = 0; std_cmd_table[i].name; i++) { if (std_cmd_table[i].always) { rc = assuan_register_command (ctx, std_cmd_table[i].name, NULL, NULL); if (rc) return rc; } } return 0; } /* Process the special data lines. The "D " has already been removed from the line. As all handlers this function may modify the line. */ static gpg_error_t handle_data_line (assuan_context_t ctx, char *line, int linelen) { return set_error (ctx, GPG_ERR_NOT_IMPLEMENTED, NULL); } /* like ascii_strcasecmp but assume that B is already uppercase */ static int my_strcasecmp (const char *a, const char *b) { if (a == b) return 0; for (; *a && *b; a++, b++) { if (((*a >= 'a' && *a <= 'z')? (*a&~0x20):*a) != *b) break; } return *a == *b? 0 : (((*a >= 'a' && *a <= 'z')? (*a&~0x20):*a) - *b); } /* Parse the line, break out the command, find it in the command table, remove leading and white spaces from the arguments, call the handler with the argument line and return the error. */ static gpg_error_t dispatch_command (assuan_context_t ctx, char *line, int linelen) { gpg_error_t err; char *p; const char *s; int shift, i; /* Note that as this function is invoked by assuan_process_next as well, we need to hide non-critical errors with PROCESS_DONE. */ if (*line == 'D' && line[1] == ' ') /* divert to special handler */ /* FIXME: Depending on the final implementation of handle_data_line, this may be wrong here. For example, if a user callback is invoked, and that callback is responsible for calling assuan_process_done, then this is wrong. */ return PROCESS_DONE (ctx, handle_data_line (ctx, line+2, linelen-2)); for (p=line; *p && *p != ' ' && *p != '\t'; p++) ; if (p==line) return PROCESS_DONE (ctx, set_error (ctx, GPG_ERR_ASS_SYNTAX, "leading white-space")); if (*p) { /* Skip over leading WS after the keyword */ *p++ = 0; while ( *p == ' ' || *p == '\t') p++; } shift = p - line; for (i=0; (s=ctx->cmdtbl[i].name); i++) { if (!strcmp (line, s)) break; } if (!s) { /* and try case insensitive */ for (i=0; (s=ctx->cmdtbl[i].name); i++) { if (!my_strcasecmp (line, s)) break; } } if (!s) return PROCESS_DONE (ctx, set_error (ctx, GPG_ERR_ASS_UNKNOWN_CMD, NULL)); line += shift; /* linelen -= shift; -- not needed. */ if (ctx->pre_cmd_notify_fnc) { err = ctx->pre_cmd_notify_fnc(ctx, ctx->cmdtbl[i].name); if (err) return PROCESS_DONE(ctx, err); } /* fprintf (stderr, "DBG-assuan: processing %s `%s'\n", s, line); */ ctx->current_cmd_name = ctx->cmdtbl[i].name; err = ctx->cmdtbl[i].handler (ctx, line); ctx->current_cmd_name = NULL; return err; } /* Call this to acknowledge the current command. */ gpg_error_t assuan_process_done (assuan_context_t ctx, gpg_error_t rc) { if (!ctx->flags.in_command) return _assuan_error (ctx, GPG_ERR_ASS_GENERAL); if (ctx->flags.force_close) ctx->flags.process_complete = 1; ctx->flags.in_command = 0; /* Check for data write errors. */ if (ctx->outbound.data.fp) { /* Flush the data lines. */ fclose (ctx->outbound.data.fp); ctx->outbound.data.fp = NULL; if (!rc && ctx->outbound.data.error) rc = ctx->outbound.data.error; } else { /* Flush any data send without using the data FP. */ assuan_send_data (ctx, NULL, 0); if (!rc && ctx->outbound.data.error) rc = ctx->outbound.data.error; } /* Error handling. */ if (!rc) { if (ctx->flags.process_complete) { /* No error checking because the peer may have already disconnect. */ assuan_write_line (ctx, "OK closing connection"); ctx->finish_handler (ctx); } else rc = assuan_write_line (ctx, ctx->okay_line ? ctx->okay_line : "OK"); } else { char errline[300]; const char *text = ctx->err_no == rc ? ctx->err_str : NULL; char ebuf[50]; if (ctx->flags.force_close) text = "[closing connection]"; gpg_strerror_r (rc, ebuf, sizeof (ebuf)); snprintf (errline, sizeof errline, "ERR %d %.50s <%.30s>%s%.100s", rc, ebuf, gpg_strsource (rc), text? " - ":"", text?text:""); rc = assuan_write_line (ctx, errline); if (ctx->flags.force_close) ctx->finish_handler (ctx); } if (ctx->post_cmd_notify_fnc) ctx->post_cmd_notify_fnc (ctx, rc); ctx->flags.confidential = 0; if (ctx->okay_line) { _assuan_free (ctx, ctx->okay_line); ctx->okay_line = NULL; } return rc; } static gpg_error_t process_next (assuan_context_t ctx) { gpg_error_t rc; /* What the next thing to do is depends on the current state. However, we will always first read the next line. The client is required to write full lines without blocking long after starting a partial line. */ rc = _assuan_read_line (ctx); if (_assuan_error_is_eagain (ctx, rc)) return 0; if (gpg_err_code (rc) == GPG_ERR_EOF) { ctx->flags.process_complete = 1; return 0; } if (rc) return rc; if (*ctx->inbound.line == '#' || !ctx->inbound.linelen) /* Comment lines are ignored. */ return 0; /* Now we have a line that really means something. It could be one of the following things: First, if we are not in a command already, it is the next command to dispatch. Second, if we are in a command, it can only be the response to an INQUIRE reply. */ if (!ctx->flags.in_command) { ctx->flags.in_command = 1; ctx->outbound.data.error = 0; ctx->outbound.data.linelen = 0; /* Dispatch command and return reply. */ ctx->flags.in_process_next = 1; rc = dispatch_command (ctx, ctx->inbound.line, ctx->inbound.linelen); ctx->flags.in_process_next = 0; } else if (ctx->flags.in_inquire) { /* FIXME: Pick up the continuation. */ rc = _assuan_inquire_ext_cb (ctx); } else { /* Should not happen. The client is sending data while we are in a command and not waiting for an inquire. We log an error and discard it. */ TRACE0 (ctx, ASSUAN_LOG_DATA, "process_next", ctx, "unexpected client data"); rc = 0; } return rc; } /* This function should be invoked when the assuan connected FD is ready for reading. If the equivalent to EWOULDBLOCK is returned (this should be done by the command handler), assuan_process_next should be invoked the next time the connected FD is readable. Eventually, the caller will finish by invoking assuan_process_done. DONE is set to 1 if the connection has ended. */ gpg_error_t assuan_process_next (assuan_context_t ctx, int *done) { gpg_error_t rc; if (done) *done = 0; ctx->flags.process_complete = 0; do { rc = process_next (ctx); } while (!rc && !ctx->flags.process_complete && assuan_pending_line (ctx)); if (done) *done = !!ctx->flags.process_complete; return rc; } static gpg_error_t process_request (assuan_context_t ctx) { gpg_error_t rc; if (ctx->flags.in_inquire) return _assuan_error (ctx, GPG_ERR_ASS_NESTED_COMMANDS); do { rc = _assuan_read_line (ctx); } while (_assuan_error_is_eagain (ctx, rc)); if (gpg_err_code (rc) == GPG_ERR_EOF) { ctx->flags.process_complete = 1; return 0; } if (rc) return rc; if (*ctx->inbound.line == '#' || !ctx->inbound.linelen) return 0; /* comment line - ignore */ ctx->flags.in_command = 1; ctx->outbound.data.error = 0; ctx->outbound.data.linelen = 0; /* dispatch command and return reply */ rc = dispatch_command (ctx, ctx->inbound.line, ctx->inbound.linelen); return assuan_process_done (ctx, rc); } /** * assuan_process: * @ctx: assuan context * * This function is used to handle the assuan protocol after a * connection has been established using assuan_accept(). This is the * main protocol handler. * * Return value: 0 on success or an error code if the assuan operation * failed. Note, that no error is returned for operational errors. **/ gpg_error_t assuan_process (assuan_context_t ctx) { gpg_error_t rc; ctx->flags.process_complete = 0; do { rc = process_request (ctx); } while (!rc && !ctx->flags.process_complete); return rc; } /** * assuan_get_active_fds: * @ctx: Assuan context * @what: 0 for read fds, 1 for write fds * @fdarray: Caller supplied array to store the FDs * @fdarraysize: size of that array * * Return all active filedescriptors for the given context. This * function can be used to select on the fds and call * assuan_process_next() if there is an active one. The first fd in * the array is the one used for the command connection. * * Note, that write FDs are not yet supported. * * Return value: number of FDs active and put into @fdarray or -1 on * error which is most likely a too small fdarray. **/ int assuan_get_active_fds (assuan_context_t ctx, int what, assuan_fd_t *fdarray, int fdarraysize) { int n = 0; if (!ctx || fdarraysize < 2 || what < 0 || what > 1) return -1; if (!what) { if (ctx->inbound.fd != ASSUAN_INVALID_FD) fdarray[n++] = ctx->inbound.fd; } else { if (ctx->outbound.fd != ASSUAN_INVALID_FD) fdarray[n++] = ctx->outbound.fd; if (ctx->outbound.data.fp) -#if defined(HAVE_W32CE_SYSTEM) - fdarray[n++] = (void*)fileno (ctx->outbound.data.fp); -#elif defined(HAVE_W32_SYSTEM) +#if defined(HAVE_W32_SYSTEM) fdarray[n++] = (void*)_get_osfhandle (fileno (ctx->outbound.data.fp)); #else fdarray[n++] = fileno (ctx->outbound.data.fp); #endif } return n; } /* Two simple wrappers to make the expected function types match. */ #ifdef HAVE_FUNOPEN static int fun1_cookie_write (void *cookie, const char *buffer, int orig_size) { return _assuan_cookie_write_data (cookie, buffer, orig_size); } #endif /*HAVE_FUNOPEN*/ #ifdef HAVE_FOPENCOOKIE static ssize_t fun2_cookie_write (void *cookie, const char *buffer, size_t orig_size) { return _assuan_cookie_write_data (cookie, buffer, orig_size); } #endif /*HAVE_FOPENCOOKIE*/ /* Return a FP to be used for data output. The FILE pointer is valid until the end of a handler. So a close is not needed. Assuan does all the buffering needed to insert the status line as well as the required line wappping and quoting for data lines. We use GNU's custom streams here. There should be an alternative implementaion for systems w/o a glibc, a simple implementation could use a child process */ FILE * assuan_get_data_fp (assuan_context_t ctx) { #if defined (HAVE_FOPENCOOKIE) || defined (HAVE_FUNOPEN) if (ctx->outbound.data.fp) return ctx->outbound.data.fp; #ifdef HAVE_FUNOPEN ctx->outbound.data.fp = funopen (ctx, 0, fun1_cookie_write, 0, _assuan_cookie_write_flush); #else ctx->outbound.data.fp = funopen (ctx, 0, fun2_cookie_write, 0, _assuan_cookie_write_flush); #endif ctx->outbound.data.error = 0; return ctx->outbound.data.fp; #else gpg_err_set_errno (ENOSYS); return NULL; #endif } /* Set the text used for the next OK response. This string is automatically reset to NULL after the next command. */ gpg_error_t assuan_set_okay_line (assuan_context_t ctx, const char *line) { if (!ctx) return _assuan_error (ctx, GPG_ERR_ASS_INV_VALUE); if (!line) { _assuan_free (ctx, ctx->okay_line); ctx->okay_line = NULL; } else { /* FIXME: we need to use gcry_is_secure() to test whether we should allocate the entire line in secure memory */ char *buf = _assuan_malloc (ctx, 3 + strlen(line) + 1); if (!buf) return _assuan_error (ctx, gpg_err_code_from_syserror ()); strcpy (buf, "OK "); strcpy (buf+3, line); _assuan_free (ctx, ctx->okay_line); ctx->okay_line = buf; } return 0; } gpg_error_t assuan_write_status (assuan_context_t ctx, const char *keyword, const char *text) { char buffer[256]; char *helpbuf; size_t n; gpg_error_t ae; if ( !ctx || !keyword) return _assuan_error (ctx, GPG_ERR_ASS_INV_VALUE); if (!text) text = ""; n = 2 + strlen (keyword) + 1 + strlen (text) + 1; if (n < sizeof (buffer)) { strcpy (buffer, "S "); strcat (buffer, keyword); if (*text) { strcat (buffer, " "); strcat (buffer, text); } ae = assuan_write_line (ctx, buffer); } else if ( (helpbuf = _assuan_malloc (ctx, n)) ) { strcpy (helpbuf, "S "); strcat (helpbuf, keyword); if (*text) { strcat (helpbuf, " "); strcat (helpbuf, text); } ae = assuan_write_line (ctx, helpbuf); _assuan_free (ctx, helpbuf); } else ae = 0; return ae; } diff --git a/src/assuan-socket.c b/src/assuan-socket.c index 0588dc2..bb5ccfd 100644 --- a/src/assuan-socket.c +++ b/src/assuan-socket.c @@ -1,1557 +1,1555 @@ /* assuan-socket.c - Socket wrapper * Copyright (C) 2004, 2005, 2009 Free Software Foundation, Inc. * Copyright (C) 2001-2015 g10 Code GmbH * * This file is part of Assuan. * * Assuan is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as * published by the Free Software Foundation; either version 2.1 of * the License, or (at your option) any later version. * * Assuan 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 * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this program; if not, see . * SPDX-License-Identifier: LGPL-2.1+ */ #ifdef HAVE_CONFIG_H #include #endif #include #include #ifdef HAVE_W32_SYSTEM # define WIN32_LEAN_AND_MEAN # include # include -#ifndef HAVE_W32CE_SYSTEM # include -#endif #else # include # include # include # include #endif #include #ifdef HAVE_SYS_STAT_H # include #endif #ifdef HAVE_FCNTL_H #include #endif #include #include "assuan-defs.h" #include "debug.h" /* Hacks for Slowaris. */ #ifndef PF_LOCAL # ifdef PF_UNIX # define PF_LOCAL PF_UNIX # else # define PF_LOCAL AF_UNIX # endif #endif #ifndef AF_LOCAL # define AF_LOCAL AF_UNIX #endif #ifdef HAVE_W32_SYSTEM #ifndef S_IRUSR # define S_IRUSR 0 # define S_IWUSR 0 #endif #ifndef S_IRGRP # define S_IRGRP 0 # define S_IWGRP 0 #endif #ifndef ENOTSUP #define ENOTSUP 129 #endif #ifndef EPROTO #define EPROTO 134 #endif #ifndef EPROTONOSUPPORT #define EPROTONOSUPPORT 135 #endif #ifndef ENETDOWN #define ENETDOWN 116 #endif #ifndef ENETUNREACH #define ENETUNREACH 118 #endif #ifndef EHOSTUNREACH #define EHOSTUNREACH 110 #endif #ifndef ECONNREFUSED #define ECONNREFUSED 107 #endif #ifndef ETIMEDOUT #define ETIMEDOUT 138 #endif #endif #ifndef ENAMETOOLONG # define ENAMETOOLONG EINVAL #endif #ifndef SUN_LEN # define SUN_LEN(ptr) ((size_t) (((struct sockaddr_un *) 0)->sun_path) \ + strlen ((ptr)->sun_path)) #endif #ifndef INADDR_LOOPBACK # define INADDR_LOOPBACK ((in_addr_t) 0x7f000001) /* 127.0.0.1. */ #endif /* The standard SOCKS and TOR port. */ #define SOCKS_PORT 1080 #define TOR_PORT 9050 #define TOR_PORT2 9150 /* The Tor browser is listening there. */ /* In the future, we can allow access to sock_ctx, if that context's hook functions need to be overridden. There can only be one global assuan_sock_* user (one library or one application) with this convenience interface, if non-standard hook functions are needed. */ static assuan_context_t sock_ctx; /* This global flag can be set using assuan_sock_set_flag to enable TOR or SOCKS mode for all sockets. It may not be reset. The value is the port to be used. */ static unsigned short tor_mode; #ifdef HAVE_W32_SYSTEM /* A table of active Cygwin connections. This is only used for listening socket which should be only a few. We do not enter sockets after a connect into this table. */ static assuan_fd_t cygwin_fdtable[16]; /* A critical section to guard access to the table of Cygwin connections. */ static CRITICAL_SECTION cygwin_fdtable_cs; /* Return true if SOCKFD is listed as Cygwin socket. */ static int is_cygwin_fd (assuan_fd_t sockfd) { int ret = 0; int i; EnterCriticalSection (&cygwin_fdtable_cs); for (i=0; i < DIM(cygwin_fdtable); i++) { if (cygwin_fdtable[i] == sockfd) { ret = 1; break; } } LeaveCriticalSection (&cygwin_fdtable_cs); return ret; } /* Insert SOCKFD into the table of Cygwin sockets. Return 0 on success or -1 on error. */ static int insert_cygwin_fd (assuan_fd_t sockfd) { int ret = 0; int mark = -1; int i; EnterCriticalSection (&cygwin_fdtable_cs); for (i=0; i < DIM(cygwin_fdtable); i++) { if (cygwin_fdtable[i] == sockfd) goto leave; /* Already in table. */ else if (cygwin_fdtable[i] == ASSUAN_INVALID_FD) mark = i; } if (mark == -1) { gpg_err_set_errno (EMFILE); ret = -1; } else cygwin_fdtable[mark] = sockfd; leave: LeaveCriticalSection (&cygwin_fdtable_cs); return ret; } /* Delete SOCKFD from the table of Cygwin sockets. */ static void delete_cygwin_fd (assuan_fd_t sockfd) { int i; EnterCriticalSection (&cygwin_fdtable_cs); for (i=0; i < DIM(cygwin_fdtable); i++) { if (cygwin_fdtable[i] == sockfd) { cygwin_fdtable[i] = ASSUAN_INVALID_FD; break; } } LeaveCriticalSection (&cygwin_fdtable_cs); return; } wchar_t * _assuan_utf8_to_wchar (const char *string) { int n; size_t nbytes; wchar_t *result; if (!string) return NULL; n = MultiByteToWideChar (CP_UTF8, 0, string, -1, NULL, 0); if (n < 0) return NULL; nbytes = (size_t)(n+1) * sizeof(*result); if (nbytes / sizeof(*result) != (n+1)) { SetLastError (ERROR_INVALID_PARAMETER); return NULL; } result = malloc (nbytes); if (!result) return NULL; n = MultiByteToWideChar (CP_UTF8, 0, string, -1, result, n); if (n < 0) { n = GetLastError (); free (result); result = NULL; SetLastError (n); } return result; } static HANDLE MyCreateFile (LPCSTR lpFileName, DWORD dwDesiredAccess, DWORD dwSharedMode, LPSECURITY_ATTRIBUTES lpSecurityAttributes, DWORD dwCreationDisposition, DWORD dwFlagsAndAttributes, HANDLE hTemplateFile) { wchar_t *filename; HANDLE result; int err; filename = _assuan_utf8_to_wchar (lpFileName); if (!filename) return INVALID_HANDLE_VALUE; result = CreateFileW (filename, dwDesiredAccess, dwSharedMode, lpSecurityAttributes, dwCreationDisposition, dwFlagsAndAttributes, hTemplateFile); err = GetLastError (); free (filename); SetLastError (err); return result; } static int MyDeleteFile (LPCSTR lpFileName) { wchar_t *filename; int result, err; filename = _assuan_utf8_to_wchar (lpFileName); if (!filename) return 0; result = DeleteFileW (filename); err = GetLastError (); free (filename); SetLastError (err); return result; } int _assuan_sock_wsa2errno (int err) { switch (err) { case WSAENOTSOCK: return EINVAL; case WSAEWOULDBLOCK: return EAGAIN; case ERROR_BROKEN_PIPE: return EPIPE; case WSANOTINITIALISED: return ENOSYS; case WSAECONNREFUSED: return ECONNREFUSED; default: return EIO; } } /* W32: Fill BUFFER with LENGTH bytes of random. Returns -1 on failure, 0 on success. Sets errno on failure. */ static int get_nonce (char *buffer, size_t nbytes) { HCRYPTPROV prov; int ret = -1; if (!CryptAcquireContext (&prov, NULL, NULL, PROV_RSA_FULL, (CRYPT_VERIFYCONTEXT|CRYPT_SILENT)) ) gpg_err_set_errno (ENODEV); else { if (!CryptGenRandom (prov, nbytes, (unsigned char *) buffer)) gpg_err_set_errno (ENODEV); else ret = 0; CryptReleaseContext (prov, 0); } return ret; } /* W32: The buffer for NONCE needs to be at least 16 bytes. Returns 0 on success and sets errno on failure. If FNAME has a Cygwin socket descriptor True is stored at CYGWIN. */ static int read_port_and_nonce (const char *fname, unsigned short *port, char *nonce, int *cygwin) { estream_t fp; char buffer[50], *p; size_t nread; int aval; *cygwin = 0; fp = gpgrt_fopen (fname, "rb"); if (!fp) return -1; nread = gpgrt_fread (buffer, 1, sizeof buffer - 1, fp); gpgrt_fclose (fp); if (!nread) { gpg_err_set_errno (ENOENT); return -1; } buffer[nread] = 0; if (!strncmp (buffer, "!", 10)) { /* This is the Cygwin compatible socket emulation. The format * of the file is: * * "!%u %c %08x-%08x-%08x-%08x\x00" * * %d for port number, %c for kind of socket (s for STREAM), and * we have 16-byte random bytes for nonce. We only support * stream mode. */ unsigned int u0; int narr[4]; if (sscanf (buffer+10, "%u s %08x-%08x-%08x-%08x", &u0, narr+0, narr+1, narr+2, narr+3) != 5 || u0 < 1 || u0 > 65535) { gpg_err_set_errno (EINVAL); return -1; } *port = u0; memcpy (nonce, narr, 16); *cygwin = 1; } else { /* This is our own socket emulation. */ aval = atoi (buffer); if (aval < 1 || aval > 65535) { gpg_err_set_errno (EINVAL); return -1; } *port = (unsigned int)aval; for (p=buffer; nread && *p != '\n'; p++, nread--) ; if (*p != '\n' || nread != 17) { gpg_err_set_errno (EINVAL); return -1; } p++; nread--; memcpy (nonce, p, 16); } return 0; } #endif /*HAVE_W32_SYSTEM*/ #ifndef HAVE_W32_SYSTEM /* Find a redirected socket name for fname and return a malloced setup filled sockaddr. If this does not work out NULL is returned and ERRNO is set. If the file seems to be a redirect True is stored at R_REDIRECT. Note that this function uses the standard malloc and not the assuan wrapped one. The format of the file is: %Assuan% socket=NAME where NAME is the actual socket to use. No white spaces are allowed, both lines must be terminated by a single LF, extra lines are not allowed. Environment variables are interpreted in NAME if given in "${VAR} notation; no escape characters are defined, if "${" shall be used verbatim, you need to use an environment variable with that content. The use of an absolute NAME is strongly suggested. The length of the file is limited to 511 bytes which is more than sufficient for that common value of 107 for sun_path. */ static struct sockaddr_un * eval_redirection (const char *fname, int *r_redirect) { FILE *fp; char buffer[512], *name; size_t n; struct sockaddr_un *addr; char *p, *pend; const char *s; *r_redirect = 0; fp = fopen (fname, "rb"); if (!fp) return NULL; n = fread (buffer, 1, sizeof buffer - 1, fp); fclose (fp); if (!n) { gpg_err_set_errno (ENOENT); return NULL; } buffer[n] = 0; /* Check that it is a redirection file. We also check that the first byte of the name is not a LF because that would lead to an zero length name. */ if (n < 17 || buffer[n-1] != '\n' || memcmp (buffer, "%Assuan%\nsocket=", 16) || buffer[16] == '\n') { gpg_err_set_errno (EINVAL); return NULL; } buffer[n-1] = 0; name = buffer + 16; *r_redirect = 1; addr = calloc (1, sizeof *addr); if (!addr) return NULL; addr->sun_family = AF_LOCAL; n = 0; for (p=name; *p; p++) { if (*p == '$' && p[1] == '{') { p += 2; pend = strchr (p, '}'); if (!pend) { free (addr); gpg_err_set_errno (EINVAL); return NULL; } *pend = 0; if (*p && (s = getenv (p))) { for (; *s; s++) { if (n < sizeof addr->sun_path - 1) addr->sun_path[n++] = *s; else { free (addr); gpg_err_set_errno (ENAMETOOLONG); return NULL; } } } p = pend; } else if (*p == '\n') break; /* Be nice and stop at the first LF. */ else if (n < sizeof addr->sun_path - 1) addr->sun_path[n++] = *p; else { free (addr); gpg_err_set_errno (ENAMETOOLONG); return NULL; } } return addr; } #endif /*!HAVE_W32_SYSTEM*/ /* Return a new socket. Note that under W32 we consider a socket the same as an System Handle; all functions using such a handle know about this dual use and act accordingly. */ assuan_fd_t _assuan_sock_new (assuan_context_t ctx, int domain, int type, int proto) { #ifdef HAVE_W32_SYSTEM assuan_fd_t res; if (domain == AF_UNIX || domain == AF_LOCAL) domain = AF_INET; res = _assuan_socket (ctx, domain, type, proto); return res; #else return _assuan_socket (ctx, domain, type, proto); #endif } int _assuan_sock_set_flag (assuan_context_t ctx, assuan_fd_t sockfd, const char *name, int value) { (void)ctx; if (!strcmp (name, "cygwin")) { #ifdef HAVE_W32_SYSTEM if (!value) delete_cygwin_fd (sockfd); else if (insert_cygwin_fd (sockfd)) return -1; #else /* Setting the Cygwin flag on non-Windows is ignored. */ #endif } else if (!strcmp (name, "tor-mode") || !strcmp (name, "socks")) { /* If SOCKFD is ASSUAN_INVALID_FD this controls global flag to switch AF_INET and AF_INET6 into TOR mode by using a SOCKS5 proxy on localhost:9050. It may only be switched on and this needs to be done before any new threads are started. Once TOR mode has been enabled, TOR mode can be disabled for a specific socket by using SOCKFD with a VALUE of 0. */ if (sockfd == ASSUAN_INVALID_FD) { if (tor_mode && !value) { gpg_err_set_errno (EPERM); return -1; /* Clearing the global flag is not allowed. */ } else if (value) { if (*name == 's') tor_mode = SOCKS_PORT; else tor_mode = TOR_PORT; } } else if (tor_mode && sockfd != ASSUAN_INVALID_FD) { /* Fixme: Disable/enable tormode for the given context. */ } else { gpg_err_set_errno (EINVAL); return -1; } } else { gpg_err_set_errno (EINVAL); return -1; } return 0; } int _assuan_sock_get_flag (assuan_context_t ctx, assuan_fd_t sockfd, const char *name, int *r_value) { (void)ctx; if (!strcmp (name, "cygwin")) { #ifdef HAVE_W32_SYSTEM *r_value = is_cygwin_fd (sockfd); #else *r_value = 0; #endif } else if (!strcmp (name, "tor-mode")) { /* FIXME: Find tor-mode for the given socket. */ *r_value = tor_mode == TOR_PORT; } else if (!strcmp (name, "socks")) { *r_value = tor_mode == SOCKS_PORT; } else { gpg_err_set_errno (EINVAL); return -1; } return 0; } /* Read NBYTES from SOCKFD into BUFFER. Return 0 on success. Handle EAGAIN and EINTR. */ static int do_readn (assuan_context_t ctx, assuan_fd_t sockfd, void *buffer, size_t nbytes) { char *p = buffer; ssize_t n; while (nbytes) { n = _assuan_read (ctx, sockfd, p, nbytes); if (n < 0 && errno == EINTR) ; else if (n < 0 && errno == EAGAIN) _assuan_usleep (ctx, 100000); /* 100ms */ else if (n < 0) return -1; else if (!n) { gpg_err_set_errno (EIO); return -1; } else { p += n; nbytes -= n; } } return 0; } /* Write NBYTES from BUFFER to SOCKFD. Return 0 on success; on error return -1 and set ERRNO. */ static int do_writen (assuan_context_t ctx, assuan_fd_t sockfd, const void *buffer, size_t nbytes) { int ret; ret = _assuan_write (ctx, sockfd, buffer, nbytes); if (ret >= 0 && ret != nbytes) { gpg_err_set_errno (EIO); ret = -1; } else if (ret >= 0) ret = 0; return ret; } #define TIMEOUT_NOT_WAITING_SOCKS5_FOREVER 1 /* in second(s) */ /* Connect using the SOCKS5 protocol. */ static int socks5_connect (assuan_context_t ctx, assuan_fd_t sock, unsigned short socksport, const char *credentials, const char *hostname, unsigned short hostport, struct sockaddr *addr, socklen_t length) { int ret; /* struct sockaddr_in6 proxyaddr_in6; */ struct sockaddr_in proxyaddr_in; struct sockaddr *proxyaddr; size_t proxyaddrlen; union { struct sockaddr *addr; struct sockaddr_in *addr_in; struct sockaddr_in6 *addr_in6; } addru; unsigned char buffer[22+512]; /* The extra 512 gives enough space for username/password or the hostname. */ size_t buflen, hostnamelen; int method; fd_set fds; struct timeval tv = { TIMEOUT_NOT_WAITING_SOCKS5_FOREVER, 0 }; addru.addr = addr; FD_ZERO (&fds); FD_SET (HANDLE2SOCKET (sock), &fds); /* memset (&proxyaddr_in6, 0, sizeof proxyaddr_in6); */ memset (&proxyaddr_in, 0, sizeof proxyaddr_in); /* Either HOSTNAME or ADDR may be given. */ if (hostname && addr) { gpg_err_set_errno (EINVAL); return -1; } /* If a hostname is given it must fit into our buffer and it must be less than 256 so that its length can be encoded in one byte. */ hostnamelen = hostname? strlen (hostname) : 0; if (hostnamelen > 255) { gpg_err_set_errno (ENAMETOOLONG); return -1; } /* Connect to local host. */ /* Fixme: First try to use IPv6 but note that _assuan_sock_connect_byname created the socket with AF_INET. */ proxyaddr_in.sin_family = AF_INET; proxyaddr_in.sin_port = htons (socksport); proxyaddr_in.sin_addr.s_addr = htonl (INADDR_LOOPBACK); proxyaddr = (struct sockaddr *)&proxyaddr_in; proxyaddrlen = sizeof proxyaddr_in; ret = _assuan_connect (ctx, sock, proxyaddr, proxyaddrlen); if (ret && socksport == TOR_PORT && errno == ECONNREFUSED) { /* Standard Tor port failed - try the Tor browser port. */ proxyaddr_in.sin_port = htons (TOR_PORT2); ret = _assuan_connect (ctx, sock, proxyaddr, proxyaddrlen); } /* If we get an EINPROGRESS here the caller is trying to do a * non-blocking connect (e.g. for custom time out handling) which * fails here. The easiest fix would be to allow the client to tell * us the timeout value and we do the timeout handling later on in the * Socks protocol. */ if (ret) return ret; buffer[0] = 5; /* RFC-1928 VER field. */ buffer[1] = 1; /* NMETHODS */ if (credentials) method = 2; /* Method: username/password authentication. */ else method = 0; /* Method: No authentication required. */ buffer[2] = method; /* Negotiate method. */ ret = do_writen (ctx, sock, buffer, 3); if (ret) return ret; /* There may be a different service at the port, which doesn't respond. Not to be bothred by such a service. */ /* FIXME: Since the process may block on select, it should be npth_select to release thread scheduling if nPth is enabled. Ideally, select is better to be in the system hooks. However, it is considered OK to use select directly; Normal use case is three steps: detect SOCKS5 service before nPth use, configure nPth system hooks, and then use socks5_connect. For the first call, select indeed blocks, but it's only single thread. For succeeding calls, this select should soon return successfully. */ ret = select (HANDLE2SOCKET (sock)+1, &fds, NULL, NULL, &tv); if (!ret) { gpg_err_set_errno (ETIMEDOUT); return -1; } ret = do_readn (ctx, sock, buffer, 2); if (ret) return ret; if (buffer[0] != 5 || buffer[1] != method ) { /* Socks server returned wrong version or does not support our requested method. */ gpg_err_set_errno (ENOTSUP); /* Fixme: Is there a better errno? */ return -1; } if (credentials) { const char *password; int ulen, plen; password = strchr (credentials, ':'); if (!password) { gpg_err_set_errno (EINVAL); /* No password given. */ return -1; } ulen = password - credentials; password++; plen = strlen (password); if (!ulen || ulen > 255 || !plen || plen > 255) { gpg_err_set_errno (EINVAL); return -1; } buffer[0] = 1; /* VER of the sub-negotiation. */ buffer[1] = ulen; buflen = 2; memcpy (buffer+buflen, credentials, ulen); buflen += ulen; buffer[buflen++] = plen; memcpy (buffer+buflen, password, plen); buflen += plen; ret = do_writen (ctx, sock, buffer, buflen); wipememory (buffer, buflen); if (ret) return ret; ret = do_readn (ctx, sock, buffer, 2); if (ret) return ret; if (buffer[0] != 1) { /* SOCKS server returned wrong version. */ gpg_err_set_errno (EPROTONOSUPPORT); return -1; } if (buffer[1]) { /* SOCKS server denied access. */ gpg_err_set_errno (EACCES); return -1; } } if (hostname && !*hostname && !hostport) { /* Empty hostname given. Stop right here to allow the caller to do the actual proxy request. */ return 0; } /* Send request details (rfc-1928, 4). */ buffer[0] = 5; /* VER */ buffer[1] = 1; /* CMD = CONNECT */ buffer[2] = 0; /* RSV */ if (hostname) { buffer[3] = 3; /* ATYP = DOMAINNAME */ buflen = 4; buffer[buflen++] = hostnamelen; memcpy (buffer+buflen, hostname, hostnamelen); buflen += hostnamelen; buffer[buflen++] = (hostport >> 8); /* DST.PORT */ buffer[buflen++] = hostport; } else if (addr->sa_family == AF_INET6) { buffer[3] = 4; /* ATYP = IPv6 */ memcpy (buffer+ 4, &addru.addr_in6->sin6_addr.s6_addr, 16); /* DST.ADDR */ memcpy (buffer+20, &addru.addr_in6->sin6_port, 2); /* DST.PORT */ buflen = 22; } else { buffer[3] = 1; /* ATYP = IPv4 */ memcpy (buffer+4, &addru.addr_in->sin_addr.s_addr, 4); /* DST.ADDR */ memcpy (buffer+8, &addru.addr_in->sin_port, 2); /* DST.PORT */ buflen = 10; } ret = do_writen (ctx, sock, buffer, buflen); if (ret) return ret; ret = do_readn (ctx, sock, buffer, 10 /* Length for IPv4 */); if (ret) return ret; if (buffer[0] != 5 || buffer[2] != 0 ) { /* Socks server returned wrong version or the reserved field is not zero. */ gpg_err_set_errno (EPROTONOSUPPORT); return -1; } if (buffer[1]) { switch (buffer[1]) { case 0x01: /* General SOCKS server failure. */ gpg_err_set_errno (ENETDOWN); break; case 0x02: /* Connection not allowed by ruleset. */ gpg_err_set_errno (EACCES); break; case 0x03: /* Network unreachable */ gpg_err_set_errno (ENETUNREACH); break; case 0x04: /* Host unreachable */ gpg_err_set_errno (EHOSTUNREACH); break; case 0x05: /* Connection refused */ gpg_err_set_errno (ECONNREFUSED); break; case 0x06: /* TTL expired */ gpg_err_set_errno (ETIMEDOUT); break; case 0x08: /* Address type not supported */ gpg_err_set_errno (EPROTONOSUPPORT); break; case 0x07: /* Command not supported */ default: gpg_err_set_errno (ENOTSUP); /* Fixme: Is there a better errno? */ } return -1; } if (buffer[3] == 4) { /* ATYP indicates a v6 address. We need to read the remaining 12 bytes. */ ret = do_readn (ctx, sock, buffer+10, 12); if (ret) return ret; } /* FIXME: We have not way to store the actual address used by the server. */ return 0; } /* Return true if SOCKS shall be used. This is the case if tor_mode is enabled and the desired address is not the loopback address. */ static int use_socks (struct sockaddr *addr) { union { struct sockaddr *addr; struct sockaddr_in *addr_in; struct sockaddr_in6 *addr_in6; } addru; addru.addr = addr; if (!tor_mode) return 0; else if (addr->sa_family == AF_INET6) { const unsigned char *s; int i; s = (unsigned char *)&addru.addr_in6->sin6_addr.s6_addr; if (s[15] != 1) return 1; /* Last octet is not 1 - not the loopback address. */ for (i=0; i < 15; i++, s++) if (*s) return 1; /* Non-zero octet found - not the loopback address. */ return 0; /* This is the loopback address. */ } else if (addr->sa_family == AF_INET) { if (*(unsigned char*)&addru.addr_in->sin_addr.s_addr == 127) return 0; /* Loopback (127.0.0.0/8) */ return 1; } else return 0; } static assuan_fd_t _assuan_sock_accept (assuan_context_t ctx, assuan_fd_t sockfd, struct sockaddr *addr, socklen_t *p_addrlen) { (void)ctx; #ifdef HAVE_W32_SYSTEM assuan_fd_t res; res = SOCKET2HANDLE (accept (HANDLE2SOCKET (sockfd), addr, p_addrlen)); if (res == SOCKET2HANDLE (INVALID_SOCKET)) gpg_err_set_errno (_assuan_sock_wsa2errno (WSAGetLastError ())); return res; #else return accept (sockfd, addr, p_addrlen); #endif } int _assuan_sock_connect (assuan_context_t ctx, assuan_fd_t sockfd, struct sockaddr *addr, int addrlen) { #ifdef HAVE_W32_SYSTEM if (addr->sa_family == AF_LOCAL || addr->sa_family == AF_UNIX) { struct sockaddr_in myaddr; struct sockaddr_un *unaddr; unsigned short port; char nonce[16]; int cygwin; int ret; unaddr = (struct sockaddr_un *)addr; if (read_port_and_nonce (unaddr->sun_path, &port, nonce, &cygwin)) return -1; myaddr.sin_family = AF_INET; myaddr.sin_port = htons (port); myaddr.sin_addr.s_addr = htonl (INADDR_LOOPBACK); /* Set return values. */ unaddr->sun_family = myaddr.sin_family; unaddr->sun_port = myaddr.sin_port; unaddr->sun_addr.s_addr = myaddr.sin_addr.s_addr; ret = _assuan_connect (ctx, sockfd, (struct sockaddr *)&myaddr, sizeof myaddr); if (!ret) { /* Send the nonce. */ ret = do_writen (ctx, sockfd, nonce, 16); if (!ret && cygwin) { char buffer[16]; /* The client sends the nonce back - not useful. We do a dummy read. */ ret = do_readn (ctx, sockfd, buffer, 16); if (!ret) { /* Send our credentials. */ int n = getpid (); memcpy (buffer, &n, 4); memset (buffer+4, 0, 4); /* uid = gid = 0 */ ret = do_writen (ctx, sockfd, buffer, 8); if (!ret) { /* Receive credentials. We don't need them. */ ret = do_readn (ctx, sockfd, buffer, 8); } } } } return ret; } else if (use_socks (addr)) { return socks5_connect (ctx, sockfd, tor_mode, NULL, NULL, 0, addr, addrlen); } else { return _assuan_connect (ctx, sockfd, addr, addrlen); } #else # if HAVE_STAT if (addr->sa_family == AF_LOCAL || addr->sa_family == AF_UNIX) { struct sockaddr_un *unaddr; struct stat statbuf; int redirect, res; unaddr = (struct sockaddr_un *)addr; if (!stat (unaddr->sun_path, &statbuf) && !S_ISSOCK (statbuf.st_mode) && S_ISREG (statbuf.st_mode)) { /* The given socket file is not a socket but a regular file. We use the content of that file to redirect to another socket file. This can be used to use sockets on file systems which do not support sockets or if for example a home directory is shared by several machines. */ unaddr = eval_redirection (unaddr->sun_path, &redirect); if (unaddr) { res = _assuan_connect (ctx, sockfd, (struct sockaddr *)unaddr, SUN_LEN (unaddr)); free (unaddr); return res; } if (redirect) return -1; /* Continue using the standard connect. */ } } # endif /*HAVE_STAT*/ if (use_socks (addr)) { return socks5_connect (ctx, sockfd, tor_mode, NULL, NULL, 0, addr, addrlen); } else { return _assuan_connect (ctx, sockfd, addr, addrlen); } #endif } /* Connect to HOST specified as host name on PORT. The current implementation requires that either the flags ASSUAN_SOCK_SOCKS or ASSUAN_SOCK_TOR are given in FLAGS. On success a new socket is returned; on error ASSUAN_INVALID_FD is returned and ERRNO set. If CREDENTIALS is not NULL, it is a string used for password based authentication. Username and password are separated by a colon. RESERVED must be 0. By passing HOST and PORT as 0 the function can be used to check for proxy availability: If the proxy is available a socket will be returned which the caller should then close. */ assuan_fd_t _assuan_sock_connect_byname (assuan_context_t ctx, const char *host, unsigned short port, int reserved, const char *credentials, unsigned int flags) { assuan_fd_t fd; unsigned short socksport; if ((flags & ASSUAN_SOCK_TOR)) socksport = TOR_PORT; else if ((flags & ASSUAN_SOCK_SOCKS)) socksport = SOCKS_PORT; else { gpg_err_set_errno (ENOTSUP); return ASSUAN_INVALID_FD; } if (host && !*host) { /* Error out early on an empty host name. See below. */ gpg_err_set_errno (EINVAL); return ASSUAN_INVALID_FD; } fd = _assuan_sock_new (ctx, AF_INET, SOCK_STREAM, 0); if (fd == ASSUAN_INVALID_FD) return fd; /* For HOST being NULL we pass an empty string which indicates to socks5_connect to stop midway during the proxy negotiation. Note that we can't pass NULL directly as this indicates IP address mode to the called function. */ if (socks5_connect (ctx, fd, socksport, credentials, host? host:"", port, NULL, 0)) { int save_errno = errno; assuan_sock_close (fd); gpg_err_set_errno (save_errno); return ASSUAN_INVALID_FD; } return fd; } int _assuan_sock_bind (assuan_context_t ctx, assuan_fd_t sockfd, struct sockaddr *addr, int addrlen) { #ifdef HAVE_W32_SYSTEM if (addr->sa_family == AF_LOCAL || addr->sa_family == AF_UNIX) { struct sockaddr_in myaddr; struct sockaddr_un *unaddr; HANDLE filehd; int len = sizeof myaddr; int rc; union { char data[16]; int aint[4]; } nonce; char tmpbuf[50+16]; DWORD nwritten; if (get_nonce (nonce.data, 16)) return -1; unaddr = (struct sockaddr_un *)addr; myaddr.sin_port = 0; myaddr.sin_family = AF_INET; myaddr.sin_addr.s_addr = htonl (INADDR_LOOPBACK); filehd = MyCreateFile (unaddr->sun_path, GENERIC_WRITE, FILE_SHARE_READ, NULL, CREATE_NEW, FILE_ATTRIBUTE_NORMAL, NULL); if (filehd == INVALID_HANDLE_VALUE) { if (GetLastError () == ERROR_FILE_EXISTS) gpg_err_set_errno (EADDRINUSE); return -1; } rc = bind (HANDLE2SOCKET (sockfd), (struct sockaddr *)&myaddr, len); if (!rc) rc = getsockname (HANDLE2SOCKET (sockfd), (struct sockaddr *)&myaddr, &len); if (rc) { int save_e = errno; CloseHandle (filehd); MyDeleteFile (unaddr->sun_path); gpg_err_set_errno (save_e); return rc; } if (is_cygwin_fd (sockfd)) { snprintf (tmpbuf, sizeof tmpbuf, "!%d s %08x-%08x-%08x-%08x", ntohs (myaddr.sin_port), nonce.aint[0], nonce.aint[1], nonce.aint[2], nonce.aint[3]); len = strlen (tmpbuf) + 1; } else { snprintf (tmpbuf, sizeof tmpbuf-16, "%d\n", ntohs (myaddr.sin_port)); len = strlen (tmpbuf); memcpy (tmpbuf+len, nonce.data,16); len += 16; } if (!WriteFile (filehd, tmpbuf, len, &nwritten, NULL)) { CloseHandle (filehd); MyDeleteFile (unaddr->sun_path); gpg_err_set_errno (EIO); return -1; } CloseHandle (filehd); return 0; } else { int res = bind (HANDLE2SOCKET(sockfd), addr, addrlen); if (res < 0) gpg_err_set_errno ( _assuan_sock_wsa2errno (WSAGetLastError ())); return res; } #else return bind (sockfd, addr, addrlen); #endif } /* Setup the ADDR structure for a Unix domain socket with the socket name FNAME. If this is a redirected socket and R_REDIRECTED is not NULL, it will be setup for the real socket. Returns 0 on success and stores 1 at R_REDIRECTED if it is a redirected socket. On error -1 is returned and ERRNO will be set. */ int _assuan_sock_set_sockaddr_un (const char *fname, struct sockaddr *addr, int *r_redirected) { struct sockaddr_un *unaddr = (struct sockaddr_un *)addr; #if !defined(HAVE_W32_SYSTEM) && defined(HAVE_STAT) struct stat statbuf; #endif if (r_redirected) *r_redirected = 0; #if !defined(HAVE_W32_SYSTEM) && defined(HAVE_STAT) if (r_redirected && !stat (fname, &statbuf) && !S_ISSOCK (statbuf.st_mode) && S_ISREG (statbuf.st_mode)) { /* The given socket file is not a socket but a regular file. We use the content of that file to redirect to another socket file. This can be used to use sockets on file systems which do not support sockets or if for example a home directory is shared by several machines. */ struct sockaddr_un *unaddr_new; int redirect; unaddr_new = eval_redirection (fname, &redirect); if (unaddr_new) { memcpy (unaddr, unaddr_new, sizeof *unaddr); free (unaddr_new); *r_redirected = 1; return 0; } if (redirect) { *r_redirected = 1; return -1; /* Error. */ } /* Fallback to standard setup. */ } #endif /*!HAVE_W32_SYSTEM && HAVE_STAT*/ if (strlen (fname)+1 >= sizeof unaddr->sun_path) { gpg_err_set_errno (ENAMETOOLONG); return -1; } memset (unaddr, 0, sizeof *unaddr); unaddr->sun_family = AF_LOCAL; strncpy (unaddr->sun_path, fname, sizeof unaddr->sun_path - 1); unaddr->sun_path[sizeof unaddr->sun_path - 1] = 0; return 0; } int _assuan_sock_get_nonce (assuan_context_t ctx, struct sockaddr *addr, int addrlen, assuan_sock_nonce_t *nonce) { #ifdef HAVE_W32_SYSTEM if (addr->sa_family == AF_LOCAL || addr->sa_family == AF_UNIX) { struct sockaddr_un *unaddr; unsigned short port; int dummy; if (sizeof nonce->nonce != 16) { gpg_err_set_errno (EINVAL); return -1; } nonce->length = 16; unaddr = (struct sockaddr_un *)addr; if (read_port_and_nonce (unaddr->sun_path, &port, nonce->nonce, &dummy)) return -1; } else { nonce->length = 42; /* Arbitrary value to detect unitialized nonce. */ nonce->nonce[0] = 42; } #else (void)addr; (void)addrlen; nonce->length = 0; #endif return 0; } int _assuan_sock_check_nonce (assuan_context_t ctx, assuan_fd_t fd, assuan_sock_nonce_t *nonce) { #ifdef HAVE_W32_SYSTEM char buffer[16]; int n; if (sizeof nonce->nonce != 16) { gpg_err_set_errno (EINVAL); return -1; } if (nonce->length == 42 && nonce->nonce[0] == 42) return 0; /* Not a Unix domain socket. */ if (nonce->length != 16) { gpg_err_set_errno (EINVAL); return -1; } if (do_readn (ctx, fd, buffer, 16)) return -1; if (memcmp (buffer, nonce->nonce, 16)) { gpg_err_set_errno (EACCES); return -1; } if (is_cygwin_fd (fd)) { /* Send the nonce back to the client. */ if (do_writen (ctx, fd, buffer, 16)) return -1; /* Read the credentials. Cygwin uses the struct ucred { pid_t pid; uid_t uid; gid_t gid; }; with pid_t being an int (4 bytes) and uid_t and gid_t being shorts (2 bytes). Thus we need to read 8 bytes. However we we ignore the values because they are not kernel controlled. */ if (do_readn (ctx, fd, buffer, 8)) return -1; /* Send our credentials: We use the uid and gid we received but our own pid. */ n = getpid (); memcpy (buffer, &n, 4); if (do_writen (ctx, fd, buffer, 8)) return -1; } #else (void)fd; (void)nonce; #endif return 0; } /* Public API. */ gpg_error_t assuan_sock_init () { gpg_error_t err; #ifdef HAVE_W32_SYSTEM WSADATA wsadat; #endif if (sock_ctx != NULL) return 0; #ifdef HAVE_W32_SYSTEM InitializeCriticalSection (&cygwin_fdtable_cs); #endif err = assuan_new (&sock_ctx); sock_ctx->flags.is_socket = 1; #ifdef HAVE_W32_SYSTEM if (! err) WSAStartup (0x202, &wsadat); #endif return err; } void assuan_sock_deinit () { if (sock_ctx == NULL) return; #ifdef HAVE_W32_SYSTEM WSACleanup (); #endif assuan_release (sock_ctx); sock_ctx = NULL; #ifdef HAVE_W32_SYSTEM DeleteCriticalSection (&cygwin_fdtable_cs); #endif } int assuan_sock_close (assuan_fd_t fd) { #ifdef HAVE_W32_SYSTEM if (fd != ASSUAN_INVALID_FD) delete_cygwin_fd (fd); #endif return _assuan_close (sock_ctx, fd); } assuan_fd_t assuan_sock_new (int domain, int type, int proto) { return _assuan_sock_new (sock_ctx, domain, type, proto); } int assuan_sock_set_flag (assuan_fd_t sockfd, const char *name, int value) { return _assuan_sock_set_flag (sock_ctx, sockfd, name, value); } int assuan_sock_get_flag (assuan_fd_t sockfd, const char *name, int *r_value) { return _assuan_sock_get_flag (sock_ctx, sockfd, name, r_value); } assuan_fd_t assuan_sock_accept (assuan_fd_t sockfd, struct sockaddr *addr, socklen_t *p_addrlen) { return _assuan_sock_accept (sock_ctx, sockfd, addr, p_addrlen); } int assuan_sock_connect (assuan_fd_t sockfd, struct sockaddr *addr, int addrlen) { return _assuan_sock_connect (sock_ctx, sockfd, addr, addrlen); } assuan_fd_t assuan_sock_connect_byname (const char *host, unsigned short port, int reserved, const char *credentials, unsigned int flags) { return _assuan_sock_connect_byname (sock_ctx, host, port, reserved, credentials, flags); } int assuan_sock_bind (assuan_fd_t sockfd, struct sockaddr *addr, int addrlen) { return _assuan_sock_bind (sock_ctx, sockfd, addr, addrlen); } int assuan_sock_set_sockaddr_un (const char *fname, struct sockaddr *addr, int *r_redirected) { return _assuan_sock_set_sockaddr_un (fname, addr, r_redirected); } int assuan_sock_get_nonce (struct sockaddr *addr, int addrlen, assuan_sock_nonce_t *nonce) { return _assuan_sock_get_nonce (sock_ctx, addr, addrlen, nonce); } int assuan_sock_check_nonce (assuan_fd_t fd, assuan_sock_nonce_t *nonce) { return _assuan_sock_check_nonce (sock_ctx, fd, nonce); } void assuan_sock_set_system_hooks (assuan_system_hooks_t system_hooks) { if (sock_ctx) _assuan_system_hooks_copy (&sock_ctx->system, system_hooks); } diff --git a/src/gpgcedev.c b/src/gpgcedev.c deleted file mode 100644 index c841ec2..0000000 --- a/src/gpgcedev.c +++ /dev/null @@ -1,1640 +0,0 @@ -/* gpgcedrv.c - WindowsCE device driver to implement pipe and syslog. - * Copyright (C) 2010 Free Software Foundation, Inc. - * - * This file is part of Assuan. - * - * Assuan is free software; you can redistribute it and/or modify it - * under the terms of the GNU Lesser General Public License as - * published by the Free Software Foundation; either version 3 of - * the License, or (at your option) any later version. - * - * Assuan 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 - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this program; if not, see . - * SPDX-License-Identifier: LGPL-3.0+ - */ - -#include -#include -#include -#include -#include -#include - -/* FIXME Cancel not handled. */ - -#define DBGFILENAME "\\gpgcedev.dbg" -#define LOGFILENAME L"\\gpgcedev.log" -#define GPGCEDEV_KEY_NAME L"Drivers\\GnuPG_Device" -#define GPGCEDEV_KEY_NAME2 L"Drivers\\GnuPG_Log" - - -/* Missing IOCTLs in the current mingw32ce. */ -#ifndef IOCTL_PSL_NOTIFY -# define FILE_DEVICE_PSL 259 -# define IOCTL_PSL_NOTIFY \ - CTL_CODE (259, 255, METHOD_NEITHER, FILE_ANY_ACCESS) -#endif /*IOCTL_PSL_NOTIFY*/ - - -/* The IOCTL to return the rendezvous id of the handle. - - The required outbuf parameter is the address of a variable to store - the rendezvous ID, which is a LONG value. */ -#define GPGCEDEV_IOCTL_GET_RVID \ - CTL_CODE (FILE_DEVICE_STREAMS, 2048, METHOD_BUFFERED, FILE_ANY_ACCESS) - -/* The IOCTL used to create the pipe. - - The caller sends this IOCTL to the read or the write handle. The - required inbuf parameter is address of a variable holding the - rendezvous id of the pipe's other end. There is one possible - problem with the code: If a pipe is kept in non-rendezvous state - until after the rendezvous ids overflow, it is possible that the - wrong end will be used. However this is not a realistic scenario. */ -#define GPGCEDEV_IOCTL_MAKE_PIPE \ - CTL_CODE (FILE_DEVICE_STREAMS, 2049, METHOD_BUFFERED, FILE_ANY_ACCESS) - -/* The IOCTL used to unblock a blocking thread. - - The caller sends this IOCTL to the read or the write handle. No - parameter is required. The effect is that a reader or writer - blocked on the same handle is unblocked (and will return - ERROR_BUSY). Note that the operation can be repeated, if so - desired. The state of the pipe itself will not be affected in any - way. */ -#define GPGCEDEV_IOCTL_UNBLOCK \ - CTL_CODE (FILE_DEVICE_STREAMS, 2050, METHOD_BUFFERED, FILE_ANY_ACCESS) - -/* The IOCTL to assign a rendezvous id to a process. - - The required inbuf parameters are the rendezvous ID to assign and - the process ID of the process receiving the RVID. The handle on - which this is called is not really used at all! */ -#define GPGCEDEV_IOCTL_ASSIGN_RVID \ - CTL_CODE (FILE_DEVICE_STREAMS, 2051, METHOD_BUFFERED, FILE_ANY_ACCESS) - - -/* An object to describe a pipe. */ -struct pipeimpl_s -{ - CRITICAL_SECTION critsect; /* Lock for all members. */ - - int refcnt; - char *buffer; - size_t buffer_size; - size_t buffer_len; /* The valid length of the bufer. */ - size_t buffer_pos; /* The actual read position. */ - -#define PIPE_FLAG_NO_READER 1 -#define PIPE_FLAG_NO_WRITER 2 -#define PIPE_FLAG_UNBLOCK_READER 4 -#define PIPE_FLAG_UNBLOCK_WRITER 8 -#define PIPE_FLAG_HALT_MONITOR 16 - int flags; - - HANDLE space_available; /* Set if space is available. */ - HANDLE data_available; /* Set if data is available. */ - - /* For the monitor thread started by ASSIGN_RVID. */ - HANDLE monitor_proc; - int monitor_access; - LONG monitor_rvid; -}; -typedef struct pipeimpl_s *pipeimpl_t; - - -/* An object to describe a logging context. We can't write directly - to the log stream because we want to do line buffering and thus we - need to store data until we see LF. */ -struct logimpl_s; -typedef struct logimpl_s *logimpl_t; -struct logimpl_s -{ - unsigned long logid; /* An identifier for a log source. */ - unsigned long dsec; /* Tenth of a second since system start. */ - char *line; /* Malloced line buffer. */ - size_t linesize; /* Allocated size of LINE. */ - size_t linelen; /* Used length of the line. */ - int truncated; /* Indicates a truncated log line. */ -}; - - - -/* An object to store information pertaining to an open-context. */ -struct opnctx_s; -typedef struct opnctx_s *opnctx_t; -struct opnctx_s -{ - int inuse; /* True if this object has valid data. */ - int is_log; /* True if this describes a log device. */ - LONG rvid; /* The unique rendezvous identifier. */ - DWORD access_code;/* Value from OpenFile. */ - DWORD share_mode; /* Value from OpenFile. */ - - /* The state shared by all pipe users. Only used if IS_LOG is false. */ - pipeimpl_t pipeimpl; - - /* The state used to implement a log stream. Only used if IS_LOG is true. */ - logimpl_t logimpl; -}; - -/* A malloced table of open-context and the number of allocated slots. */ -static opnctx_t opnctx_table; -static size_t opnctx_table_size; -/* The macros make sure that 0 is never a valid openctx_arg. */ -#define OPNCTX_TO_IDX(ctx_arg) (((ctx_arg) - opnctx_table) + 1) -#define OPNCTX_FROM_IDX(idx) (&opnctx_table[(idx) - 1]) -#define OPNCTX_VALID_IDX_P(idx) ((idx) > 0 && (idx) <= opnctx_table_size) - -typedef struct monitor_s *monitor_t; -struct monitor_s -{ - int inuse; /* True if this object has valid data. */ - pipeimpl_t pipeimpl; -}; -static monitor_t monitor_table; -static size_t monitor_table_size; - -/* A criticial section object used to protect the OPNCTX_TABLE and - MONITOR_TABLE. */ -static CRITICAL_SECTION opnctx_table_cs; - - - -/* A global object to control the logging. */ -struct { - CRITICAL_SECTION lock; /* Lock for this structure. */ - HANDLE filehd; /* Handle of the log output file. */ - int references; /* Number of objects references this one. */ -} logcontrol; - - -/* We don't need a device context for the pipe thus we use the address - of the critical section object for it. */ -#define PIPECTX_VALUE ((DWORD)(&opnctx_table_cs)) - -/* The device context for the log device is the address of our - control structure. */ -#define LOGCTX_VALUE ((DWORD)(&logcontrol)) - - -/* True if we have enabled debugging. */ -static int enable_debug; - -/* True if logging has been enabled. */ -static int enable_logging; - - - -static void -log_debug (const char *fmt, ...) -{ - if (enable_debug) - { - va_list arg_ptr; - FILE *fp; - - fp = fopen (DBGFILENAME, "a+"); - if (!fp) - return; - va_start (arg_ptr, fmt); - vfprintf (fp, fmt, arg_ptr); - va_end (arg_ptr); - fclose (fp); - } -} - - -/* Return a new rendezvous id. We will never return an RVID of 0. */ -static LONG -create_rendezvous_id (void) -{ - static LONG rendezvous_id; - LONG rvid; - - while (!(rvid = InterlockedIncrement (&rendezvous_id))) - ; - return rvid; -} - -/* Return a new log id. These log ids are used to identify log lines - send to the same device; ie. for each CreateFile("GPG2:") a new log - id is assigned. We will ever return a log id of 0. */ -static LONG -create_log_id (void) -{ - static LONG log_id; - LONG lid; - - while (!(lid = InterlockedIncrement (&log_id))) - ; - return lid; -} - - - -pipeimpl_t -pipeimpl_new (void) -{ - pipeimpl_t pimpl; - - pimpl = malloc (sizeof (*pimpl)); - if (!pimpl) - return NULL; - - InitializeCriticalSection (&pimpl->critsect); - pimpl->refcnt = 1; - pimpl->buffer_size = 512; - pimpl->buffer = malloc (pimpl->buffer_size); - if (!pimpl->buffer) - { - DeleteCriticalSection (&pimpl->critsect); - free (pimpl); - return NULL; - } - pimpl->buffer_len = 0; - pimpl->buffer_pos = 0; - pimpl->flags = 0; - pimpl->space_available = CreateEvent (NULL, FALSE, FALSE, NULL); - if (!pimpl->space_available) - { - free (pimpl->buffer); - DeleteCriticalSection (&pimpl->critsect); - free (pimpl); - return NULL; - } - pimpl->data_available = CreateEvent (NULL, FALSE, FALSE, NULL); - if (!pimpl->data_available) - { - CloseHandle (pimpl->space_available); - free (pimpl->buffer); - DeleteCriticalSection (&pimpl->critsect); - free (pimpl); - return NULL; - } - pimpl->monitor_proc = INVALID_HANDLE_VALUE; - pimpl->monitor_access = 0; - pimpl->monitor_rvid = 0; - return pimpl; -} - - -/* PIMPL must be locked. It is unlocked at exit. */ -void -pipeimpl_unref (pipeimpl_t pimpl) -{ - int release = 0; - - if (!pimpl) - return; - - log_debug ("pipeimpl_unref (%p): dereference\n", pimpl); - - if (--pimpl->refcnt == 0) - release = 1; - LeaveCriticalSection (&pimpl->critsect); - - if (! release) - return; - - log_debug ("pipeimpl_unref (%p): release\n", pimpl); - - DeleteCriticalSection (&pimpl->critsect); - if (pimpl->buffer) - { - free (pimpl->buffer); - pimpl->buffer = NULL; - pimpl->buffer_size = 0; - } - if (pimpl->space_available != INVALID_HANDLE_VALUE) - { - CloseHandle (pimpl->space_available); - pimpl->space_available = INVALID_HANDLE_VALUE; - } - if (pimpl->data_available != INVALID_HANDLE_VALUE) - { - CloseHandle (pimpl->data_available); - pimpl->data_available = INVALID_HANDLE_VALUE; - } -} - - - -/* Allocate a new log structure. */ -logimpl_t -logimpl_new (void) -{ - logimpl_t limpl; - - limpl = calloc (1, sizeof *limpl); - if (!limpl) - return NULL; - limpl->logid = create_log_id (); - limpl->linesize = 256; - limpl->line = malloc (limpl->linesize); - if (!limpl->line) - { - free (limpl); - return NULL; - } - - return limpl; -} - - -/* There is no need to lock LIMPL, thus is a dummy function. */ -void -logimpl_unref (logimpl_t limpl) -{ - (void)limpl; -} - - -/* Flush a pending log line. */ -static void -logimpl_flush (logimpl_t limpl) -{ - if (!limpl->linelen || !enable_logging) - return; - - EnterCriticalSection (&logcontrol.lock); - if (logcontrol.filehd == INVALID_HANDLE_VALUE) - logcontrol.filehd = CreateFile (LOGFILENAME, GENERIC_WRITE, - FILE_SHARE_READ, - NULL, OPEN_ALWAYS, - FILE_ATTRIBUTE_NORMAL, NULL); - if (!logcontrol.filehd) - log_debug ("can't open log file: rc=%d\n", (int)GetLastError ()); - else - { - char buf[50]; - DWORD nwritten; - - snprintf (buf, sizeof buf, - "%06lu/%lu//", limpl->dsec % 1000000, limpl->logid); - if (!WriteFile (logcontrol.filehd, buf, strlen (buf), &nwritten, NULL)) - log_debug ("error writing log file: rc=%d\n", (int)GetLastError ()); - else if (!WriteFile (logcontrol.filehd, - limpl->line, limpl->linelen, &nwritten, NULL)) - log_debug ("error writing log file: rc=%d\n", (int)GetLastError ()); - - snprintf (buf, sizeof buf, "%s\n", limpl->truncated? "[...]":""); - if (!WriteFile (logcontrol.filehd, buf, strlen (buf), &nwritten, NULL)) - log_debug ("error writing log file: rc=%d\n", (int)GetLastError ()); - } - - LeaveCriticalSection (&logcontrol.lock); - limpl->linelen = 0; - limpl->truncated = 0; -} - - -/* Return a new opnctx handle and mark it as used. Returns NULL and - sets LastError on memory failure etc. opnctx_table_cs must be - locked on entry and is locked on exit. Note that the returned - pointer is only valid as long as opnctx_table_cs stays locked, as - it is not stable under table reallocation. */ -static opnctx_t -allocate_opnctx (int is_log) -{ - opnctx_t opnctx = NULL; - int idx; - - for (idx = 0; idx < opnctx_table_size; idx++) - if (! opnctx_table[idx].inuse) - break; - if (idx == opnctx_table_size) - { - /* We need to increase the size of the table. The approach we - take is straightforward to minimize the risk of bugs. */ - opnctx_t newtbl; - size_t newsize = opnctx_table_size + 64; - - newtbl = calloc (newsize, sizeof *newtbl); - if (!newtbl) - goto leave; - memcpy (newtbl, opnctx_table, opnctx_table_size * sizeof (*newtbl)); - free (opnctx_table); - opnctx_table = newtbl; - idx = opnctx_table_size; - opnctx_table_size = newsize; - } - opnctx = &opnctx_table[idx]; - opnctx->inuse = 1; - opnctx->is_log = is_log; - opnctx->rvid = 0; - opnctx->access_code = 0; - opnctx->share_mode = 0; - opnctx->pipeimpl = 0; - opnctx->logimpl = 0; - - leave: - return opnctx; -} - - -/* Verify context CTX, returns NULL if not valid and the pointer to - the context if valid. opnctx_table_cs must be locked on entry and - is locked on exit. Note that the returned pointer is only valid as - long as opnctx_table_cs remains locked. */ -opnctx_t -verify_opnctx (DWORD ctx_arg) -{ - opnctx_t ctx; - - if (! OPNCTX_VALID_IDX_P (ctx_arg)) - { - SetLastError (ERROR_INVALID_HANDLE); - return NULL; - } - ctx = OPNCTX_FROM_IDX (ctx_arg); - - if (! ctx->inuse) - { - SetLastError (ERROR_INVALID_HANDLE); - return NULL; - } - return ctx; -} - - -/* Return a new monitor handle and mark it as used. Returns NULL and - sets LastError on memory failure etc. opnctx_table_cs must be - locked on entry and is locked on exit. Note that the returned - pointer is only valid as long as opnctx_table_cs stays locked, as - it is not stable under table reallocation. */ -static monitor_t -allocate_monitor (void) -{ - monitor_t monitor = NULL; - int idx; - - for (idx = 0; idx < monitor_table_size; idx++) - if (! monitor_table[idx].inuse) - break; - if (idx == monitor_table_size) - { - /* We need to increase the size of the table. The approach we - take is straightforward to minimize the risk of bugs. */ - monitor_t newtbl; - size_t newsize = monitor_table_size + 16; - - newtbl = calloc (newsize, sizeof *newtbl); - if (!newtbl) - goto leave; - memcpy (newtbl, monitor_table, monitor_table_size * sizeof (*newtbl)); - free (monitor_table); - monitor_table = newtbl; - idx = monitor_table_size; - monitor_table_size = newsize; - } - monitor = &monitor_table[idx]; - monitor->inuse = 1; - monitor->pipeimpl = 0; - - leave: - return monitor; -} - - -static pipeimpl_t -assert_pipeimpl (opnctx_t ctx) -{ - DWORD ctx_arg = OPNCTX_TO_IDX (ctx); - - if (ctx->is_log) - { - log_debug (" assert_pipeimpl (ctx=%i): " - "error: not valid for a log device\n", ctx_arg); - return NULL; - } - if (! ctx->pipeimpl) - { - ctx->pipeimpl = pipeimpl_new (); - if (! ctx->pipeimpl) - { - log_debug (" assert_pipeimpl (ctx=%i): error: can't create pipe\n", - ctx_arg); - return NULL; - } - log_debug (" assert_pipeimpl (ctx=%i): created pipe 0x%p\n", - ctx_arg, ctx->pipeimpl); - } - return ctx->pipeimpl; -} - - -static logimpl_t -assert_logimpl (opnctx_t ctx) -{ - DWORD ctx_arg = OPNCTX_TO_IDX (ctx); - - if (!ctx->is_log) - { - log_debug (" assert_logimpl (ctx=%i): " - "error: not valid for a pipe device\n", ctx_arg); - return NULL; - } - if (!ctx->logimpl) - { - ctx->logimpl = logimpl_new (); - if (!ctx->logimpl) - { - log_debug (" assert_logimpl (ctx=%i): error: can't create log\n", - ctx_arg); - return NULL; - } - log_debug (" assert_logimpl (ctx=%i): created log 0x%p\n", - ctx_arg, ctx->logimpl); - } - return ctx->logimpl; -} - - -/* Verify access CODE for context CTX_ARG, returning a reference to - the locked pipe or the locked log implementation. opnctx_table_cs - must be unlocked on entry and is unlocked on exit. */ -int -access_opnctx (DWORD ctx_arg, DWORD code, pipeimpl_t *r_pipe, logimpl_t *r_log) -{ - opnctx_t ctx; - - *r_pipe = NULL; - *r_log = NULL; - - EnterCriticalSection (&opnctx_table_cs); - ctx = verify_opnctx (ctx_arg); - if (! ctx) - { - /* Error is set by verify_opnctx. */ - LeaveCriticalSection (&opnctx_table_cs); - return -1; - } - - if (! (ctx->access_code & code)) - { - SetLastError (ERROR_INVALID_ACCESS); - LeaveCriticalSection (&opnctx_table_cs); - return -1; - } - - if (ctx->is_log) - { - logimpl_t limpl; - - limpl = assert_logimpl (ctx); - if (!limpl) - { - LeaveCriticalSection (&opnctx_table_cs); - return -1; - } - *r_log = limpl; - } - else - { - pipeimpl_t pimpl; - - pimpl = assert_pipeimpl (ctx); - if (! pimpl) - { - LeaveCriticalSection (&opnctx_table_cs); - return -1; - } - EnterCriticalSection (&pimpl->critsect); - pimpl->refcnt++; - *r_pipe = pimpl; - } - - LeaveCriticalSection (&opnctx_table_cs); - return 0; -} - - - -static char * -wchar_to_utf8 (const wchar_t *string) -{ - int n; - size_t length = wcslen (string); - char *result; - - n = WideCharToMultiByte (CP_UTF8, 0, string, length, NULL, 0, NULL, NULL); - if (n < 0 || (n+1) <= 0) - abort (); - - result = malloc (n+1); - if (!result) - abort (); - n = WideCharToMultiByte (CP_ACP, 0, string, length, result, n, NULL, NULL); - if (n < 0) - abort (); - - result[n] = 0; - return result; -} - - -/* Initialize the device and return a device specific context. */ -DWORD -GPG_Init (LPCTSTR active_key, DWORD bus_context) -{ - static int firsttimedone; - HKEY handle; - wchar_t buffer[25]; - DWORD buflen; - DWORD result; - - (void)bus_context; - - EnterCriticalSection (&logcontrol.lock); - if (!firsttimedone) - { - firsttimedone++; - if (!RegOpenKeyEx (HKEY_LOCAL_MACHINE, GPGCEDEV_KEY_NAME, - 0, KEY_READ, &handle)) - { - buflen = sizeof buffer; - if (!RegQueryValueEx (handle, L"debugDriver", 0, NULL, - (PBYTE)buffer, &buflen) - && wcstol (buffer, NULL, 10) > 0) - enable_debug = 1; - RegCloseKey (handle); - } - if (!RegOpenKeyEx (HKEY_LOCAL_MACHINE, GPGCEDEV_KEY_NAME2, - 0, KEY_READ, &handle)) - { - buflen = sizeof buffer; - if (!RegQueryValueEx (handle, L"enableLog", 0, NULL, - (PBYTE)buffer, &buflen) - && wcstol (buffer, NULL, 10) > 0) - enable_logging = 1; - RegCloseKey (handle); - } - } - LeaveCriticalSection (&logcontrol.lock); - - if (enable_debug) - { - char *tmpbuf; - tmpbuf = wchar_to_utf8 (active_key); - log_debug ("GPG_Init (%s)\n", tmpbuf); - free (tmpbuf); - } - - if (RegOpenKeyEx (HKEY_LOCAL_MACHINE, active_key, 0, KEY_READ, &handle)) - { - log_debug ("GPG_Init: error reading registry: rc=%d\n", - (int)GetLastError ()); - return 0; - } - - buflen = sizeof buffer; - if (RegQueryValueEx (handle, L"Name", 0, NULL, (PBYTE)buffer, &buflen)) - { - log_debug ("GPG_Init: error reading registry value 'Name': rc=%d\n", - (int)GetLastError ()); - result = 0; - } - else if (!wcscmp (buffer, L"GPG1:")) - { - /* This is the pipe device: We don't need any global data. - However, we need to return something. */ - log_debug ("GPG_Init: created pipe device (devctx=%p)\n", PIPECTX_VALUE); - result = PIPECTX_VALUE; - } - else if (!wcscmp (buffer, L"GPG2:")) - { - /* This is the log device. Clear the object and return something. */ - logcontrol.filehd = INVALID_HANDLE_VALUE; - log_debug ("GPG_Init: created log device (devctx=%p)\n", 0); - result = LOGCTX_VALUE; - } - else - { - if (enable_debug) - { - char *tmpbuf; - tmpbuf = wchar_to_utf8 (buffer); - log_debug ("GPG_Init: device '%s' is not supported\n", tmpbuf); - free (tmpbuf); - } - SetLastError (ERROR_DEV_NOT_EXIST); - result = 0; - } - - RegCloseKey (handle); - return result; -} - - - -/* Deinitialize this device driver. */ -BOOL -GPG_Deinit (DWORD devctx) -{ - log_debug ("GPG_Deinit (devctx=0x%p)\n", (void*)devctx); - if (devctx == PIPECTX_VALUE) - { - /* No need to release resources. */ - } - else if (devctx == LOGCTX_VALUE) - { - EnterCriticalSection (&logcontrol.lock); - if (logcontrol.filehd != INVALID_HANDLE_VALUE) - { - CloseHandle (logcontrol.filehd); - logcontrol.filehd = INVALID_HANDLE_VALUE; - } - LeaveCriticalSection (&logcontrol.lock); - } - else - { - SetLastError (ERROR_INVALID_PARAMETER); - return FALSE; /* Error. */ - } - - return TRUE; /* Success. */ -} - - - -/* Create a new open context. This function is called due to a - CreateFile from the application. */ -DWORD -GPG_Open (DWORD devctx, DWORD access_code, DWORD share_mode) -{ - opnctx_t opnctx; - DWORD ctx_arg = 0; - int is_log; - - log_debug ("GPG_Open (devctx=%p)\n", (void*)devctx); - if (devctx == PIPECTX_VALUE) - is_log = 0; - else if (devctx == LOGCTX_VALUE) - is_log = 1; - else - { - log_debug ("GPG_Open (devctx=%p): error: wrong devctx (expected 0x%p)\n", - (void*) devctx); - SetLastError (ERROR_INVALID_PARAMETER); - return 0; /* Error. */ - } - - EnterCriticalSection (&opnctx_table_cs); - opnctx = allocate_opnctx (is_log); - if (!opnctx) - { - log_debug ("GPG_Open (devctx=%p): error: could not allocate context\n", - (void*) devctx); - goto leave; - } - - opnctx->access_code = access_code; - opnctx->share_mode = share_mode; - - ctx_arg = OPNCTX_TO_IDX (opnctx); - - log_debug ("GPG_Open (devctx=%p, is_log=%d): success: created context %i\n", - (void*) devctx, is_log, ctx_arg); - if (is_log) - { - EnterCriticalSection (&logcontrol.lock); - logcontrol.references++; - LeaveCriticalSection (&logcontrol.lock); - } - - leave: - LeaveCriticalSection (&opnctx_table_cs); - return ctx_arg; -} - - - -BOOL -GPG_Close (DWORD opnctx_arg) -{ - opnctx_t opnctx; - BOOL result = FALSE; - - log_debug ("GPG_Close (ctx=%i)\n", opnctx_arg); - - EnterCriticalSection (&opnctx_table_cs); - opnctx = verify_opnctx (opnctx_arg); - if (!opnctx) - { - log_debug ("GPG_Close (ctx=%i): could not find context\n", opnctx_arg); - goto leave; - } - - if (opnctx->pipeimpl) - { - pipeimpl_t pimpl = opnctx->pipeimpl; - EnterCriticalSection (&pimpl->critsect); - /* This needs to be adjusted if there can be multiple - reader/writers. */ - if (opnctx->access_code & GENERIC_READ) - { - pimpl->flags |= PIPE_FLAG_NO_READER; - SetEvent (pimpl->space_available); - } - else if (opnctx->access_code & GENERIC_WRITE) - { - pimpl->flags |= PIPE_FLAG_NO_WRITER; - SetEvent (pimpl->data_available); - } - pipeimpl_unref (pimpl); - opnctx->pipeimpl = 0; - } - if (opnctx->logimpl) - { - logimpl_t limpl = opnctx->logimpl; - - logimpl_flush (limpl); - logimpl_unref (limpl); - free (limpl->line); - free (limpl); - opnctx->logimpl = 0; - EnterCriticalSection (&logcontrol.lock); - logcontrol.references--; - if (!logcontrol.references && logcontrol.filehd) - { - CloseHandle (logcontrol.filehd); - logcontrol.filehd = INVALID_HANDLE_VALUE; - } - LeaveCriticalSection (&logcontrol.lock); - } - opnctx->access_code = 0; - opnctx->share_mode = 0; - opnctx->rvid = 0; - opnctx->inuse = 0; - result = TRUE; - log_debug ("GPG_Close (ctx=%i): success\n", opnctx_arg); - - leave: - LeaveCriticalSection (&opnctx_table_cs); - return result; -} - - - -DWORD -GPG_Read (DWORD opnctx_arg, void *buffer, DWORD count) -{ - pipeimpl_t pimpl; - logimpl_t limpl; - const char *src; - char *dst; - int result = -1; - - log_debug ("GPG_Read (ctx=%i, buffer=0x%p, count=%d)\n", - opnctx_arg, buffer, count); - - if (access_opnctx (opnctx_arg, GENERIC_READ, &pimpl, &limpl)) - { - log_debug ("GPG_Read (ctx=%i): error: could not access context\n", - opnctx_arg); - return -1; - } - - if (limpl) - { - /* Reading from a log stream does not make sense. Return EOF. */ - result = 0; - goto leave; - } - - retry: - if (pimpl->buffer_pos == pimpl->buffer_len) - { - HANDLE data_available = pimpl->data_available; - - /* Check for end of file. */ - if (pimpl->flags & PIPE_FLAG_NO_WRITER) - { - log_debug ("GPG_Read (ctx=%i): success: EOF\n", opnctx_arg); - result = 0; - goto leave; - } - - /* Check for request to unblock once. */ - if (pimpl->flags & PIPE_FLAG_UNBLOCK_READER) - { - log_debug ("GPG_Read (ctx=%i): success: EBUSY (due to unblock)\n", - opnctx_arg); - pimpl->flags &= ~PIPE_FLAG_UNBLOCK_READER; - SetLastError (ERROR_BUSY); - result = -1; - goto leave; - } - - LeaveCriticalSection (&pimpl->critsect); - log_debug ("GPG_Read (ctx=%i): waiting: data_available\n", opnctx_arg); - WaitForSingleObject (data_available, INFINITE); - log_debug ("GPG_Read (ctx=%i): resuming: data_available\n", opnctx_arg); - EnterCriticalSection (&pimpl->critsect); - goto retry; - } - - dst = buffer; - src = pimpl->buffer + pimpl->buffer_pos; - while (count > 0 && pimpl->buffer_pos < pimpl->buffer_len) - { - *dst++ = *src++; - count--; - pimpl->buffer_pos++; - } - result = (dst - (char*)buffer); - if (pimpl->buffer_pos == pimpl->buffer_len) - pimpl->buffer_pos = pimpl->buffer_len = 0; - - /* Now there should be some space available. Signal the write end. - Even if COUNT was passed as NULL and no space is available, - signaling must be done. */ - if (!SetEvent (pimpl->space_available)) - log_debug ("GPG_Read (ctx=%i): warning: SetEvent(space_available) " - "failed: rc=%d\n", opnctx_arg, (int)GetLastError ()); - - log_debug ("GPG_Read (ctx=%i): success: result=%d\n", opnctx_arg, result); - - leave: - pipeimpl_unref (pimpl); - logimpl_unref (limpl); - return result; -} - - - -DWORD -GPG_Write (DWORD opnctx_arg, const void *buffer, DWORD count) -{ - pipeimpl_t pimpl; - logimpl_t limpl; - int result = -1; - const char *src; - char *dst; - size_t nwritten = 0; - - log_debug ("GPG_Write (ctx=%i, buffer=0x%p, count=%d)\n", opnctx_arg, - buffer, count); - - if (access_opnctx (opnctx_arg, GENERIC_WRITE, &pimpl, &limpl)) - { - log_debug ("GPG_Write (ctx=%i): error: could not access context\n", - opnctx_arg); - return -1; - } - - if (!count) - { - log_debug ("GPG_Write (ctx=%i): success\n", opnctx_arg); - result = 0; - goto leave; - } - - retry: - if (limpl) - { - /* Store it in our buffer up to a LF and print complete lines. */ - result = count; - if (!limpl->linelen) - limpl->dsec = GetTickCount () / 100; - dst = limpl->line + limpl->linelen; - for (src = buffer; count; count--, src++) - { - if (*src == '\n') - { - logimpl_flush (limpl); - dst = limpl->line + limpl->linelen; - } - else if (limpl->linelen >= limpl->linesize) - limpl->truncated = 1; - else - { - *dst++ = *src; - limpl->linelen++; - } - } - } - else /* pimpl */ - { - /* Check for broken pipe. */ - if (pimpl->flags & PIPE_FLAG_NO_READER) - { - log_debug ("GPG_Write (ctx=%i): error: broken pipe\n", opnctx_arg); - SetLastError (ERROR_BROKEN_PIPE); - goto leave; - } - - /* Check for request to unblock once. */ - if (pimpl->flags & PIPE_FLAG_UNBLOCK_WRITER) - { - log_debug ("GPG_Write (ctx=%i): success: EBUSY (due to unblock)\n", - opnctx_arg); - pimpl->flags &= ~PIPE_FLAG_UNBLOCK_WRITER; - SetLastError (ERROR_BUSY); - result = -1; - goto leave; - } - - /* Write to our buffer. */ - if (pimpl->buffer_len == pimpl->buffer_size) - { - /* Buffer is full. */ - HANDLE space_available = pimpl->space_available; - LeaveCriticalSection (&pimpl->critsect); - log_debug ("GPG_Write (ctx=%i): waiting: space_available\n", - opnctx_arg); - WaitForSingleObject (space_available, INFINITE); - log_debug ("GPG_Write (ctx=%i): resuming: space_available\n", - opnctx_arg); - EnterCriticalSection (&pimpl->critsect); - goto retry; - } - - src = buffer; - dst = pimpl->buffer + pimpl->buffer_len; - while (count > 0 && pimpl->buffer_len < pimpl->buffer_size) - { - *dst++ = *src++; - count--; - pimpl->buffer_len++; - nwritten++; - } - result = nwritten; - - if (!SetEvent (pimpl->data_available)) - log_debug ("GPG_Write (ctx=%i): warning: SetEvent(data_available) " - "failed: rc=%d\n", opnctx_arg, (int)GetLastError ()); - } - - log_debug ("GPG_Write (ctx=%i): success: result=%d\n", opnctx_arg, result); - - leave: - pipeimpl_unref (pimpl); - logimpl_unref (limpl); - return result; -} - - - -DWORD -GPG_Seek (DWORD opnctx_arg, long amount, WORD type) -{ - SetLastError (ERROR_SEEK_ON_DEVICE); - return -1; /* Error. */ -} - - - -/* opnctx_table_s is locked on entering and on exit. */ -static BOOL -make_pipe (opnctx_t ctx, LONG rvid) -{ - DWORD ctx_arg = OPNCTX_TO_IDX (ctx); - opnctx_t peerctx = NULL; - int idx; - pipeimpl_t pimpl; - - log_debug (" make_pipe (ctx=%i, rvid=%08lx)\n", ctx_arg, rvid); - - if (ctx->pipeimpl) - { - log_debug (" make_pipe (ctx=%i): error: already assigned\n", ctx_arg); - SetLastError (ERROR_ALREADY_ASSIGNED); - return FALSE; - } - - /* GnuPG and other programs don't use the safe ASSIGN_RVID call - because they guarantee that the context exists during the whole - time the child process runs. GPGME is more asynchronous and - relies on ASSIGN_RVID monitors. So, first check for open - contexts, then check for monitors. */ - - for (idx = 0; idx < opnctx_table_size; idx++) - if (opnctx_table[idx].inuse && opnctx_table[idx].rvid == rvid) - { - peerctx = &opnctx_table[idx]; - break; - } - if (peerctx) - { - /* This is the GnuPG etc case, where the context is still open. - It may also cover the GPGME case if GPGME is still using its - own end of the pipe at the time of this call. */ - if (peerctx == ctx) - { - log_debug (" make_pipe (ctx=%i): error: target and source identical\n", - ctx_arg); - SetLastError (ERROR_INVALID_TARGET_HANDLE); - return FALSE; - } - - if ((ctx->access_code & GENERIC_READ)) - { - /* Check that the peer is a write end. */ - if (!(peerctx->access_code & GENERIC_WRITE)) - { - log_debug (" make_pipe (ctx=%i): error: peer is not writer\n", - ctx_arg); - SetLastError (ERROR_INVALID_ACCESS); - return FALSE; - } - } - else if ((ctx->access_code & GENERIC_WRITE)) - { - /* Check that the peer is a read end. */ - if (!(peerctx->access_code & GENERIC_READ)) - { - log_debug (" make_pipe (ctx=%i): error: peer is not reader\n", - ctx_arg); - SetLastError (ERROR_INVALID_ACCESS); - return FALSE; - } - } - else - { - log_debug (" make_pipe (ctx=%i): error: invalid access\n", ctx_arg); - SetLastError (ERROR_INVALID_ACCESS); - return FALSE; - } - - /* Make sure the peer has a pipe implementation to be shared. If - not yet, create one. */ - pimpl = assert_pipeimpl (peerctx); - if (! pimpl) - return FALSE; - } - else - { - /* This is the case where ASSIGN_RVID was called to create a - monitor, and the pipe is already closed at the parent side. - For example GPGME verify detached plain text, where GPG calls - MAKE_PIPE very late. */ - - for (idx = 0; idx < monitor_table_size; idx++) - if (monitor_table[idx].inuse - && monitor_table[idx].pipeimpl->monitor_rvid == rvid) - { - pimpl = monitor_table[idx].pipeimpl; - break; - } - if (idx == monitor_table_size) - { - log_debug (" make_pipe (ctx=%i): error: not found\n", ctx_arg); - SetLastError (ERROR_NOT_FOUND); - return FALSE; - } - - if (ctx->access_code & GENERIC_READ) - { - /* Check that the peer is a write end. */ - if (!(pimpl->monitor_access & GENERIC_READ)) - { - log_debug (" make_pipe (ctx=%i): error: monitor is not reader\n", - ctx_arg); - SetLastError (ERROR_INVALID_ACCESS); - return FALSE; - } - } - else if ((ctx->access_code & GENERIC_WRITE)) - { - /* Check that the peer is a read end. */ - if (!(pimpl->monitor_access & GENERIC_WRITE)) - { - log_debug (" make_pipe (ctx=%i): error: monitor is not writer\n", - ctx_arg); - SetLastError (ERROR_INVALID_ACCESS); - return FALSE; - } - } - else - { - log_debug (" make_pipe (ctx=%i): error: invalid access\n", ctx_arg); - SetLastError (ERROR_INVALID_ACCESS); - return FALSE; - } - } - - EnterCriticalSection (&pimpl->critsect); - pimpl->refcnt++; - if (pimpl->monitor_proc != INVALID_HANDLE_VALUE) - { - /* If there is a monitor to the pipe, then it's now about time - to ask it to go away. */ - log_debug (" make_pipe (ctx=%i): waking up monitor for pipe 0x%p\n", - ctx_arg, pimpl); - pimpl->flags |= PIPE_FLAG_HALT_MONITOR; - if (pimpl->monitor_access & GENERIC_READ) - SetEvent (pimpl->data_available); - else - SetEvent (pimpl->space_available); - } - LeaveCriticalSection (&pimpl->critsect); - - ctx->pipeimpl = pimpl; - - if (peerctx) - { - log_debug (" make_pipe (ctx=%i): success: combined with peer ctx=%i " - "(pipe 0x%p)\n", ctx_arg, OPNCTX_TO_IDX (peerctx), pimpl); - } - else - { - log_debug (" make_pipe (ctx=%i): success: combined with " - "pipe 0x%p\n", ctx_arg, OPNCTX_TO_IDX (peerctx), pimpl); - } - - return TRUE; -} - - -/* opnctx_table_s is locked on entering and on exit. */ -static BOOL -unblock_call (opnctx_t ctx) -{ - /* If there is no pipe object, no thread can be blocked. */ - if (!ctx->pipeimpl) - return TRUE; - - EnterCriticalSection (&ctx->pipeimpl->critsect); - if (ctx->access_code & GENERIC_READ) - { - ctx->pipeimpl->flags |= PIPE_FLAG_UNBLOCK_READER; - SetEvent (ctx->pipeimpl->data_available); - } - else if (ctx->access_code & GENERIC_WRITE) - { - ctx->pipeimpl->flags |= PIPE_FLAG_UNBLOCK_WRITER; - SetEvent (ctx->pipeimpl->space_available); - } - LeaveCriticalSection (&ctx->pipeimpl->critsect); - - return TRUE; -} - - -static DWORD CALLBACK -monitor_main (void *arg) -{ - pipeimpl_t pimpl = (pipeimpl_t) arg; - HANDLE handles[2]; - int idx; - - log_debug ("starting monitor (pimpl=0x%p)\n", pimpl); - - EnterCriticalSection (&pimpl->critsect); - /* Putting proc first in the array is convenient, as this is a hard - break-out condition (and thus takes precedence in WFMO). The - reader/writer event is a soft condition, which also requires a - flag to be set. */ - handles[0] = pimpl->monitor_proc; - if (pimpl->monitor_access & GENERIC_READ) - handles[1] = pimpl->data_available; - else - handles[1] = pimpl->space_available; - - retry: - /* First check if the peer has not gone away. If it has, we are done. */ - if (pimpl->flags & PIPE_FLAG_HALT_MONITOR) - { - log_debug ("monitor (pimpl=0x%p): done: monitored process taking over\n", - pimpl); - } - else - { - DWORD res; - - LeaveCriticalSection (&pimpl->critsect); - log_debug ("monitor (pimpl=0x%p): waiting\n", pimpl); - res = WaitForMultipleObjects (2, handles, FALSE, INFINITE); - log_debug ("monitor (pimpl=0x%p): resuming\n", pimpl); - EnterCriticalSection (&pimpl->critsect); - - if (res == WAIT_FAILED) - { - log_debug ("monitor (pimpl=0x%p): WFMO failed: %i\n", - pimpl, GetLastError ()); - } - else if (res == WAIT_OBJECT_0) - { - log_debug ("monitor (pimpl=0x%p): done: monitored process died\n", - pimpl); - } - else if (res == WAIT_OBJECT_0 + 1) - goto retry; - else - { - log_debug ("monitor (pimpl=0x%p): unexpected result of WFMO: %i\n", - pimpl, res); - } - } - - log_debug ("ending monitor (pimpl=0x%p)\n", pimpl); - - /* Remove the monitor from the monitor table. */ - LeaveCriticalSection (&pimpl->critsect); - EnterCriticalSection (&opnctx_table_cs); - for (idx = 0; idx < monitor_table_size; idx++) - if (monitor_table[idx].inuse - && monitor_table[idx].pipeimpl == pimpl) - { - monitor_table[idx].pipeimpl = NULL; - monitor_table[idx].inuse = 0; - break; - } - if (idx == monitor_table_size) - log_debug ("can not find monitor in table (pimpl=0x%p)\n", pimpl); - LeaveCriticalSection (&opnctx_table_cs); - EnterCriticalSection (&pimpl->critsect); - - /* Now do the rest of the cleanup. */ - CloseHandle (pimpl->monitor_proc); - pimpl->monitor_proc = INVALID_HANDLE_VALUE; - pimpl->monitor_access = 0; - pimpl->monitor_rvid = 0; - pimpl->flags &= ~PIPE_FLAG_HALT_MONITOR; - pipeimpl_unref (pimpl); - - return 0; -} - - -/* opnctx_table_s is locked on entering and on exit. */ -static BOOL -assign_rvid (opnctx_t ctx, DWORD rvid, DWORD pid) -{ - DWORD ctx_arg = OPNCTX_TO_IDX (ctx); - int idx; - opnctx_t peerctx; - HANDLE monitor_hnd; - HANDLE proc; - pipeimpl_t pimpl; - monitor_t monitor; - - log_debug (" assign_rvid (ctx=%i, rvid=%08lx, pid=%08lx)\n", - ctx_arg, rvid, pid); - - for (idx = 0; idx < opnctx_table_size; idx++) - if (opnctx_table[idx].inuse && opnctx_table[idx].rvid == rvid) - { - peerctx = &opnctx_table[idx]; - break; - } - if (! peerctx) - { - log_debug (" assign_rvid (ctx=%i): error: not found\n", ctx_arg); - SetLastError (ERROR_NOT_FOUND); - return FALSE; - } - - if (peerctx->pipeimpl - && peerctx->pipeimpl->monitor_proc != INVALID_HANDLE_VALUE) - { - log_debug (" assign_rvid (ctx=%i): error: rvid already assigned\n", - ctx_arg); - SetLastError (ERROR_ALREADY_ASSIGNED); - return FALSE; - } - - proc = OpenProcess (0, FALSE, pid); - if (proc == NULL) - { - log_debug (" assign_rvid (ctx=%i): error: process not found\n", - ctx_arg); - return FALSE; - } - - /* Acquire a reference to the pipe. We don't want accesss to be - checked. */ - pimpl = assert_pipeimpl (peerctx); - if (! pimpl) - { - CloseHandle (proc); - return FALSE; - } - - monitor = allocate_monitor (); - if (!monitor) - { - log_debug (" assign_rvid (ctx=%i): error: could not allocate monitor\n", - ctx_arg); - CloseHandle (proc); - return FALSE; - } - monitor->pipeimpl = pimpl; - - EnterCriticalSection (&pimpl->critsect); - pimpl->refcnt++; - - /* The monitor shadows the peer, so it takes its access. Our access - is the opposite of that of the peer. */ - pimpl->monitor_proc = proc; - if (peerctx->access_code == GENERIC_READ) - pimpl->monitor_access = GENERIC_WRITE; - else - pimpl->monitor_access = GENERIC_READ; - pimpl->monitor_rvid = rvid; - - monitor_hnd = CreateThread (NULL, 0, monitor_main, pimpl, 0, NULL); - if (monitor_hnd == INVALID_HANDLE_VALUE) - { - pimpl->monitor_access = 0; - pimpl->monitor_proc = INVALID_HANDLE_VALUE; - LeaveCriticalSection (&pimpl->critsect); - - monitor->pipeimpl = NULL; - monitor->inuse = 0; - - CloseHandle (proc); - log_debug (" assign_rvid (ctx=%i): error: can not create monitor\n", - ctx_arg); - return FALSE; - } - CloseHandle (monitor_hnd); - - /* Consume the pimpl reference. */ - LeaveCriticalSection (&pimpl->critsect); - - return TRUE; -} - - -BOOL -GPG_IOControl (DWORD opnctx_arg, DWORD code, void *inbuf, DWORD inbuflen, - void *outbuf, DWORD outbuflen, DWORD *actualoutlen) -{ - opnctx_t opnctx; - BOOL result = FALSE; - LONG rvid; - LONG pid; - - log_debug ("GPG_IOControl (ctx=%i, %08x)\n", opnctx_arg, code); - - EnterCriticalSection (&opnctx_table_cs); - opnctx = verify_opnctx (opnctx_arg); - if (!opnctx) - { - log_debug ("GPG_IOControl (ctx=%i): error: could not access context\n", - opnctx_arg); - goto leave; - } - if (opnctx->is_log) - { - log_debug ("GPG_IOControl (ctx=%i): error: invalid code %u" - " for log device\n", opnctx_arg, (unsigned int)code); - SetLastError (ERROR_INVALID_PARAMETER); - goto leave; - } - - switch (code) - { - case GPGCEDEV_IOCTL_GET_RVID: - log_debug ("GPG_IOControl (ctx=%i): code: GET_RVID\n", opnctx_arg); - if (inbuf || inbuflen || !outbuf || outbuflen < sizeof (LONG)) - { - log_debug ("GPG_IOControl (ctx=%i): error: invalid parameter\n", - opnctx_arg); - SetLastError (ERROR_INVALID_PARAMETER); - goto leave; - } - - if (! opnctx->rvid) - opnctx->rvid = create_rendezvous_id (); - log_debug ("GPG_IOControl (ctx=%i): returning rvid: %08lx\n", - opnctx_arg, opnctx->rvid); - - memcpy (outbuf, &opnctx->rvid, sizeof (LONG)); - if (actualoutlen) - *actualoutlen = sizeof (LONG); - result = TRUE; - break; - - case GPGCEDEV_IOCTL_MAKE_PIPE: - log_debug ("GPG_IOControl (ctx=%i): code: MAKE_PIPE\n", opnctx_arg); - if (!inbuf || inbuflen < sizeof (LONG) - || outbuf || outbuflen || actualoutlen) - { - log_debug ("GPG_IOControl (ctx=%i): error: invalid parameter\n", - opnctx_arg); - SetLastError (ERROR_INVALID_PARAMETER); - goto leave; - } - memcpy (&rvid, inbuf, sizeof (LONG)); - log_debug ("GPG_IOControl (ctx=%i): make pipe for rvid: %08lx\n", - opnctx_arg, rvid); - if (make_pipe (opnctx, rvid)) - result = TRUE; - break; - - case GPGCEDEV_IOCTL_UNBLOCK: - log_debug ("GPG_IOControl (ctx=%i): code: UNBLOCK\n", opnctx_arg); - if (inbuf || inbuflen || outbuf || outbuflen || actualoutlen) - { - log_debug ("GPG_IOControl (ctx=%i): error: invalid parameter\n", - opnctx_arg); - SetLastError (ERROR_INVALID_PARAMETER); - goto leave; - } - - if (unblock_call (opnctx)) - result = TRUE; - break; - - case GPGCEDEV_IOCTL_ASSIGN_RVID: - log_debug ("GPG_IOControl (ctx=%i): code: ASSIGN_RVID\n", opnctx_arg); - if (!inbuf || inbuflen < 2 * sizeof (DWORD) - || outbuf || outbuflen || actualoutlen) - { - log_debug ("GPG_IOControl (ctx=%i): error: invalid parameter\n", - opnctx_arg); - SetLastError (ERROR_INVALID_PARAMETER); - goto leave; - } - memcpy (&rvid, inbuf, sizeof (DWORD)); - memcpy (&pid, ((char *) inbuf) + sizeof (DWORD), sizeof (DWORD)); - log_debug ("GPG_IOControl (ctx=%i): assign rvid %08lx to pid %08lx\n", - opnctx_arg, rvid, pid); - if (assign_rvid (opnctx, rvid, pid)) - result = TRUE; - break; - - case IOCTL_PSL_NOTIFY: - /* This notification is received if the application's main - thread exits and the application has other threads running - and the application has open handles for this device. What - we do is to unblock them all simialr to an explicit unblock - call. */ - log_debug ("GPG_IOControl (ctx=%i): code: NOTIFY\n", opnctx_arg); - - if (unblock_call (opnctx)) - result = TRUE; - break; - - default: - log_debug ("GPG_IOControl (ctx=%i): code: (unknown)\n", opnctx_arg); - SetLastError (ERROR_INVALID_PARAMETER); - break; - } - - log_debug ("GPG_IOControl (ctx=%i): success: result=%d\n", opnctx_arg, - result); - - leave: - LeaveCriticalSection (&opnctx_table_cs); - return result; -} - - - -void -GPG_PowerUp (DWORD devctx) -{ - log_debug ("GPG_PowerUp (devctx=%i)\n", devctx); -} - - - -void -GPG_PowerDown (DWORD devctx) -{ - log_debug ("GPG_PowerDown (devctx=%i)\n", devctx); -} - - - - - -/* Entry point called by the DLL loader. */ -int WINAPI -DllMain (HINSTANCE hinst, DWORD reason, LPVOID reserved) -{ - (void)reserved; - - switch (reason) - { - case DLL_PROCESS_ATTACH: - InitializeCriticalSection (&opnctx_table_cs); - InitializeCriticalSection (&logcontrol.lock); - break; - - case DLL_THREAD_ATTACH: - break; - - case DLL_THREAD_DETACH: - break; - - case DLL_PROCESS_DETACH: - DeleteCriticalSection (&opnctx_table_cs); - break; - - default: - break; - } - - return TRUE; -} diff --git a/src/gpgcedev.def b/src/gpgcedev.def deleted file mode 100644 index bc52d3a..0000000 --- a/src/gpgcedev.def +++ /dev/null @@ -1,34 +0,0 @@ -; gpgcedev.def - List of symbols to export for gpgcedev. -; Copyright (C) 2010 Free Software Foundation, Inc. -; -; This file is part of Assuan. -; -; Assuan is free software; you can redistribute it and/or modify it -; under the terms of the GNU Lesser General Public License as -; published by the Free Software Foundation; either version 3 of -; the License, or (at your option) any later version. -; -; Assuan 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 -; Lesser General Public License for more details. -; -; You should have received a copy of the GNU Lesser General Public -; License along with this program; if not, see . -; SPDX-License-Identifier: LGPL-3.0+ - - -EXPORTS - GPG_Init - GPG_Deinit - GPG_Open - GPG_Close - GPG_Read - GPG_Write - GPG_Seek - GPG_IOControl - GPG_PowerUp - GPG_PowerDown - -; END - diff --git a/src/gpgcemgr.c b/src/gpgcemgr.c deleted file mode 100644 index 5b4f56e..0000000 --- a/src/gpgcemgr.c +++ /dev/null @@ -1,608 +0,0 @@ -/* gpgcempr.c - Manager for GPG CE devices - * Copyright (C) 2010 Free Software Foundation, Inc. - * - * This file is part of Assuan. - * - * Assuan is free software; you can redistribute it and/or modify it - * under the terms of the GNU Lesser General Public License as - * published by the Free Software Foundation; either version 3 of - * the License, or (at your option) any later version. - * - * Assuan 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 - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this program; if not, see . - * SPDX-License-Identifier: LGPL-3.0+ - */ - -#define _WIN32_WCE 0x0500 - -#include -#include - -#define PGM "gpgcemgr" - -#define GPGCEDEV_KEY_NAME L"Drivers\\GnuPG_Device" -#define GPGCEDEV_KEY_NAME2 L"Drivers\\GnuPG_Log" -#define GPGCEDEV_DLL_NAME L"gpgcedev.dll" -#define GPGCEDEV_PREFIX L"GPG" - - -static char * -wchar_to_utf8 (const wchar_t *string) -{ - int n; - size_t length = wcslen (string); - char *result; - - n = WideCharToMultiByte (CP_UTF8, 0, string, length, NULL, 0, NULL, NULL); - if (n < 0 || (n+1) <= 0) - abort (); - - result = malloc (n+1); - if (!result) - abort (); - n = WideCharToMultiByte (CP_ACP, 0, string, length, result, n, NULL, NULL); - if (n < 0) - abort (); - - result[n] = 0; - return result; -} - - -static wchar_t * -utf8_to_wchar (const char *string) -{ - int n; - size_t nbytes; - wchar_t *result; - - if (!string) - abort (); - - n = MultiByteToWideChar (CP_UTF8, 0, string, -1, NULL, 0); - if (n < 0) - abort (); - nbytes = (size_t)(n+1) * sizeof(*result); - if (nbytes / sizeof(*result) != (n+1)) - abort (); - result = malloc (nbytes); - if (!result) - abort (); - - n = MultiByteToWideChar (CP_UTF8, 0, string, -1, result, n); - if (n < 0) - abort (); - return result; -} - - -static int -install (void) -{ - HKEY handle; - DWORD disp, dw; - int rc; - - if ((rc=RegCreateKeyEx (HKEY_LOCAL_MACHINE, GPGCEDEV_KEY_NAME, 0, NULL, 0, - KEY_WRITE, NULL, &handle, &disp))) - { - fprintf (stderr, PGM": error creating registry key 1: rc=%d\n", rc); - return 1; - } - - RegSetValueEx (handle, L"dll", 0, REG_SZ, - (void*)GPGCEDEV_DLL_NAME, sizeof (GPGCEDEV_DLL_NAME)); - RegSetValueEx (handle, L"prefix", 0, REG_SZ, - (void*)GPGCEDEV_PREFIX, sizeof (GPGCEDEV_PREFIX)); - - dw = 1; - RegSetValueEx (handle, L"Index", 0, REG_DWORD, (void*)&dw, sizeof dw); - - RegCloseKey (handle); - - fprintf (stderr, PGM": registry key 1 created\n"); - - if ((rc=RegCreateKeyEx (HKEY_LOCAL_MACHINE, GPGCEDEV_KEY_NAME2, 0, NULL, 0, - KEY_WRITE, NULL, &handle, &disp))) - { - fprintf (stderr, PGM": error creating registry key 2: rc=%d\n", rc); - return 1; - } - - RegSetValueEx (handle, L"dll", 0, REG_SZ, - (void*)GPGCEDEV_DLL_NAME, sizeof (GPGCEDEV_DLL_NAME)); - RegSetValueEx (handle, L"prefix", 0, REG_SZ, - (void*)GPGCEDEV_PREFIX, sizeof (GPGCEDEV_PREFIX)); - - dw = 2; - RegSetValueEx (handle, L"Index", 0, REG_DWORD, (void*)&dw, sizeof dw); - - RegCloseKey (handle); - - fprintf (stderr, PGM": registry key 2 created\n"); - - - return 0; -} - - -static int -deinstall (wchar_t *name) -{ - int result = 0; - HANDLE shd; - DEVMGR_DEVICE_INFORMATION dinfo; - - memset (&dinfo, 0, sizeof dinfo); - dinfo.dwSize = sizeof dinfo; - shd = FindFirstDevice (DeviceSearchByLegacyName, name, &dinfo); - if (shd == INVALID_HANDLE_VALUE) - { - if (GetLastError () == 18) - fprintf (stderr, PGM": device not found\n"); - else - { - fprintf (stderr, PGM": FindFirstDevice failed: rc=%d\n", - (int)GetLastError ()); - result = 1; - } - } - else - { - fprintf (stderr, PGM": ActivateDevice handle is %p\n", dinfo.hDevice); - if (dinfo.hDevice && dinfo.hDevice != INVALID_HANDLE_VALUE) - { - if (!DeactivateDevice (dinfo.hDevice)) - { - fprintf (stderr, PGM": DeactivateDevice failed: rc=%d\n", - (int)GetLastError ()); - result = 1; - } - else - fprintf (stderr, PGM": DeactivateDevice succeeded\n"); - } - FindClose (shd); - } - - return result; -} - - -static int -enable_debug (int yes) -{ - HKEY handle; - DWORD disp; - int rc; - - if ((rc=RegCreateKeyEx (HKEY_LOCAL_MACHINE, GPGCEDEV_KEY_NAME, 0, NULL, 0, - KEY_WRITE, NULL, &handle, &disp))) - { - fprintf (stderr, PGM": error creating debug registry key: rc=%d\n", rc); - return 1; - } - - RegSetValueEx (handle, L"debugDriver", 0, REG_SZ, - (void*)(yes? L"1":L"0"), sizeof L"0"); - RegCloseKey (handle); - return 0; -} - - -static int -enable_log (int yes) -{ - HKEY handle; - DWORD disp; - int rc; - - if ((rc=RegCreateKeyEx (HKEY_LOCAL_MACHINE, GPGCEDEV_KEY_NAME2, 0, NULL, 0, - KEY_WRITE, NULL, &handle, &disp))) - { - fprintf (stderr, PGM": error creating debug registry key: rc=%d\n", rc); - return 1; - } - - RegSetValueEx (handle, L"enableLog", 0, REG_SZ, - (void*)(yes? L"1":L"0"), sizeof L"0"); - RegCloseKey (handle); - return 0; -} - - - -/* Kudos to Scott Seligman for his work - on the reverse engineering. */ -struct htc_sensor_s -{ - SHORT tilt_x; // From -1000 to 1000 (about), 0 is flat - SHORT tilt_y; // From -1000 to 1000 (about), 0 is flat - SHORT tilt_z; // From -1000 to 1000 (about) - DWORD angle_x; // 0 .. 359 - DWORD angle_y; // From 0 to 359 - DWORD orientation; // 0.. 5? - DWORD unknown; // Handle? -}; -typedef struct htc_sensor_s *htc_sensor_t; - -static HANDLE (WINAPI *HTCSensorOpen) (DWORD); -static void (WINAPI *HTCSensorClose) (HANDLE); -static DWORD (WINAPI *HTCSensorGetDataOutput) (HANDLE, htc_sensor_t); - -static int -load_sensor_api (void) -{ - static HMODULE dll_hd; - - if (dll_hd) - return 0; - - dll_hd = LoadLibrary (L"HTCSensorSDK.dll"); - if (!dll_hd) - { - fprintf (stderr, PGM": error loading sensor DLL: rc=%d\n", - (int)GetLastError ()); - return 1; - } - - HTCSensorOpen = (void*)GetProcAddress (dll_hd, L"HTCSensorOpen"); - if (HTCSensorOpen) - HTCSensorClose = (void*)GetProcAddress (dll_hd, L"HTCSensorClose"); - if (HTCSensorClose) - HTCSensorGetDataOutput = (void*) - GetProcAddress (dll_hd, L"HTCSensorGetDataOutput"); - if (!HTCSensorGetDataOutput) - { - fprintf (stderr, PGM": error loading function from sensor DLL: rc=%d\n", - (int)GetLastError ()); - CloseHandle (dll_hd); - return 1; - } - return 0; -} - - -static int -gravity (void) -{ - int rc; - HANDLE sensor; - struct htc_sensor_s lastdata; - struct htc_sensor_s data; - - rc = load_sensor_api (); - if (rc) - return rc; - - sensor = HTCSensorOpen (1 /* tilt sensor */); - if (!sensor || sensor == INVALID_HANDLE_VALUE) - { - fprintf (stderr, PGM": error opening gravity sensor: rc=%d\n", - (int)GetLastError ()); - HTCSensorClose (sensor); - return 1; - } - - memset (&lastdata, 0, sizeof lastdata); - while (HTCSensorGetDataOutput (sensor, &data)) - { - if (lastdata.tilt_x/10 != data.tilt_x/10 - || lastdata.tilt_y/10 != data.tilt_y/10 - || lastdata.tilt_z/10 != data.tilt_z/10 - || lastdata.angle_x/5 != data.angle_x/5 - || lastdata.angle_y/5 != data.angle_y/5 - || lastdata.orientation != data.orientation) - { - lastdata = data; - printf ("tilt: x=%-5d y=%-5d z=%-5d " - "angle: x=%-3d y=%-3d " - "ori: %d\n", - (int)data.tilt_x, (int)data.tilt_y, (int)data.tilt_z, - (int)data.angle_x, (int)data.angle_y, - (int)data.orientation); - } - Sleep (200); - } - fprintf (stderr, PGM": reading sensor data failed: rc=%d\n", - (int)GetLastError ()); - HTCSensorClose (sensor); - return 0; -} - - - -/* No GPD1 device on the HTC Touch Pro 2. */ -# if 0 -static int -gps_raw (void) -{ - HANDLE hd; - char buffer[1000]; - unsigned long nread; - int count; - - hd = CreateFile (L"GPD1:", GENERIC_READ, FILE_SHARE_READ, - NULL, OPEN_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL); - if (hd == INVALID_HANDLE_VALUE) - { - fprintf (stderr, PGM": can't open `GPD1': rc=%d\n", - (int)GetLastError ()); - return 1; - } - fprintf (stderr, PGM": GPS device successfully opened\n"); - - for (count=0; count < 100; count++) - { - if (!ReadFile (hd, buffer, sizeof buffer-1, &nread, NULL)) - { - fprintf (stderr, PGM": error reading `GPD1': rc=%d\n", - (int)GetLastError ()); - CloseHandle (hd); - return 1; - } - buffer[nread-1] = 0; - fputs (buffer, stdout); - } - - CloseHandle (hd); - return 0; -} -#endif - -/* Untested samples for CE6. */ -#if 0 -static int -gps (void) -{ - HANDLE hd; - GPS_POSITION pos; - - hd = GPSOpenDevice (NULL, NULL, NULL, 0); - if (hd == INVALID_HANDLE_VALUE) - { - fprintf (stderr, PGM": GPSOpenDevice failed: rc=%d\n", - (int)GetLastError ()); - return 1; - } - fprintf (stderr, PGM": GPS device successfully opened\n"); - - if (GPSGetPosition (hd, &pos, 2000, 0)) - { - fprintf (stderr, PGM": GPSGetPosition failed: rc=%d\n", - (int)GetLastError ()); - GPSCloseDevice (hd); - return 1; - } - - - GPSCloseDevice (hd); - return 0; -} -#endif - - -static void -set_show_registry (const wchar_t *key, const wchar_t *name, const char *value) -{ - HKEY handle; - DWORD disp, nbytes, n1, type; - int rc; - - if ((rc=RegCreateKeyEx (HKEY_LOCAL_MACHINE, key, 0, NULL, 0, - KEY_WRITE, NULL, &handle, &disp))) - { - fprintf (stderr, PGM": error creating registry key: rc=%d\n", rc); - return; - } - - if (value && !stricmp (value, "none")) - { - if ((rc=RegDeleteValue (handle, name))) - fprintf (stderr, PGM": error deleting registry value: rc=%d\n", rc); - } - else if (value) - { - wchar_t *tmp = utf8_to_wchar (value); - if ((rc=RegSetValueEx (handle, name, 0, REG_SZ, - (void*)tmp, wcslen (tmp)*sizeof(wchar_t)))) - fprintf (stderr, PGM": error setting registry value: rc=%d\n", rc); - free (tmp); - } - else - { - nbytes = 2; - if ((rc=RegQueryValueEx (handle, name, 0, NULL, NULL, &nbytes))) - { - if (rc == ERROR_FILE_NOT_FOUND) - fprintf (stderr, PGM": registry value not set\n"); - else - fprintf (stderr, PGM": error reading registry value: rc=%d\n", rc); - } - else - { - char *result = malloc ((n1=nbytes+2)); - if (!result) - fprintf (stderr, PGM": malloc failed: rc=%d\n", - (int)GetLastError ()); - else if ((rc=RegQueryValueEx (handle, name, 0, &type, - (void*)result, &n1))) - { - fprintf (stderr, PGM": error reading registry value (2): " - "rc=%d\n", rc); - free (result); - } - else - { - result[nbytes] = 0; /* Make sure it is a string. */ - result[nbytes+1] = 0; - if (type == REG_SZ) - { - wchar_t *tmp = (void*)result; - result = wchar_to_utf8 (tmp); - free (tmp); - printf ("%s\n", result); - } - else - fprintf (stderr, PGM": registry value is not a string\n"); - free (result); - } - } - } - - RegCloseKey (handle); -} - - - -int -main (int argc, char **argv) -{ - int result = 0; - - if (argc > 1 && !strcmp (argv[1], "--register")) - result = install (); - else if (argc > 1 && !strcmp (argv[1], "--deactivate")) - { - if (deinstall (L"GPG1:")) - result = 1; - if (deinstall (L"GPG2:")) - result = 1; - } - else if (argc > 1 && !strcmp (argv[1], "--activate")) - { - HANDLE hd; - - /* This is mainly for testing. The activation is usually done - right before the device is opened. */ - if (ActivateDevice (GPGCEDEV_KEY_NAME, 0) == INVALID_HANDLE_VALUE) - { - fprintf (stderr, PGM": ActivateDevice 1 failed: rc=%d\n", - (int)GetLastError ()); - result = 1; - } - else if (ActivateDevice (GPGCEDEV_KEY_NAME2, 0) == INVALID_HANDLE_VALUE) - { - fprintf (stderr, PGM": ActivateDevice 2 failed: rc=%d\n", - (int)GetLastError ()); - result = 1; - } - else - { - fprintf (stderr, PGM": devices activated\n"); - hd = CreateFile (L"GPG1:", GENERIC_WRITE, - FILE_SHARE_READ | FILE_SHARE_WRITE, - NULL, OPEN_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL); - if (hd == INVALID_HANDLE_VALUE) - { - fprintf (stderr, PGM": opening `GPG1:' failed: rc=%d\n", - (int)GetLastError ()); - result = 1; - } - else - { - fprintf (stderr, PGM": device `GPG1:' seems to work\n"); - CloseHandle (hd); - } - - hd = CreateFile (L"GPG2:", GENERIC_WRITE, - FILE_SHARE_READ | FILE_SHARE_WRITE, - NULL, OPEN_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL); - if (hd == INVALID_HANDLE_VALUE) - { - fprintf (stderr, PGM": opening `GPG2:' failed: rc=%d\n", - (int)GetLastError ()); - result = 1; - } - else - { - fprintf (stderr, PGM": device `GPG2:' seems to work\n"); - CloseHandle (hd); - } - - } - } - else if (argc > 1 && !strcmp (argv[1], "--gravity")) - result = gravity (); - /* else if (argc > 1 && !strcmp (argv[1], "--gps")) */ - /* result = gps (); */ - else if (argc > 1 && !strcmp (argv[1], "--log")) - { - HANDLE hd; - - hd = CreateFile (L"GPG2:", GENERIC_WRITE, - FILE_SHARE_READ | FILE_SHARE_WRITE, - NULL, OPEN_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL); - if (hd == INVALID_HANDLE_VALUE) - { - fprintf (stderr, PGM": opening `GPG2:' failed: rc=%d\n", - (int)GetLastError ()); - result = 1; - } - else - { - char marktwain[] = "I have never let my schooling interfere" - " with my education.\n"; - DWORD nwritten; - int i; - - for (i=0; i < 3; i++) - { - if (!WriteFile (hd, marktwain, strlen (marktwain), - &nwritten, NULL)) - { - fprintf (stderr, PGM": writing `GPG2:' failed: rc=%d\n", - (int)GetLastError ()); - result = 1; - } - Sleep (200); - } - CloseHandle (hd); - } - } - else if (argc > 1 && !strcmp (argv[1], "--enable-debug")) - result = enable_debug (1); - else if (argc > 1 && !strcmp (argv[1], "--disable-debug")) - result = enable_debug (0); - else if (argc > 1 && !strcmp (argv[1], "--enable-log")) - result = enable_log (1); - else if (argc > 1 && !strcmp (argv[1], "--disable-log")) - result = enable_log (0); - else if (argc > 1 && !strcmp (argv[1], "--gpgme-log")) - set_show_registry (L"Software\\GNU\\gpgme", L"debug", - argc > 2? argv[2] : NULL); - else if (argc > 1 && !strcmp (argv[1], "--gnupg-log")) - set_show_registry (L"Software\\GNU\\GnuPG", L"DefaultLogFile", - argc > 2? argv[2] : NULL); - else - { - fprintf (stderr, - "usage: " PGM " COMMAND\n" - "Commands are:\n" - " --register Register the GPGCEDEV device\n" - " --deactivate Deactivate the GPGCEDEV device\n" - " --activate Activate the GPGCEDEV devive\n" - " --enable-debug Enable debugging of GPGCEDEV device\n" - " --disable-debug Disable debugging of GPGCEDEV device\n" - " --gravity Show output of the gravity sensor\n" - " --enable-log Enable logging via \"GPG2:\"\n" - " --disable-log Disable logging via \"GPG2:\"\n" - " --log Write a test string to \"GPG2:\"\n" - " --gpgme-log [ARG] Show or set GPGME log output\n" - " --gnupg-log [ARG] Show or set GnuPG default log file\n" - " (No ARG shows, \"none\" disables)\n" - ); - result = 1; - } - - fflush (stdout); - fflush (stderr); - Sleep (1000); - return result; -} - - diff --git a/src/setenv.c b/src/setenv.c index 3410b30..487de4e 100644 --- a/src/setenv.c +++ b/src/setenv.c @@ -1,358 +1,354 @@ /* Copyright (C) 1992,1995-2001,2004 Free Software Foundation, Inc. * This file is part of the GNU C Library. * * The GNU C Library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * The GNU C 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 * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with the GNU C Library; if not, * see . * SPDX-License-Identifier: LGPL-2.1+ */ #if HAVE_CONFIG_H # include #endif -#ifndef HAVE_W32CE_SYSTEM - #define setenv _assuan_setenv #define unsetenv _assuan_unsetenv #define clearenv _assuan_clearenv #define __builtin_expect(cond,val) (cond) #include #if !_LIBC # if !defined errno && !defined HAVE_ERRNO_DECL extern int errno; # endif # define __set_errno(ev) ((errno) = (ev)) #endif #if _LIBC || HAVE_STDLIB_H # include #endif #if _LIBC || HAVE_STRING_H # include #endif #if _LIBC || HAVE_UNISTD_H # include #endif #if !_LIBC # define __environ environ # ifndef HAVE_ENVIRON_DECL extern char **environ; # endif #endif #if _LIBC /* This lock protects against simultaneous modifications of `environ'. */ # include __libc_lock_define_initialized (static, envlock) # define LOCK __libc_lock_lock (envlock) # define UNLOCK __libc_lock_unlock (envlock) #else # define LOCK # define UNLOCK #endif /* In the GNU C library we must keep the namespace clean. */ #ifdef _LIBC # define setenv __setenv # define unsetenv __unsetenv # define clearenv __clearenv # define tfind __tfind # define tsearch __tsearch #endif /* In the GNU C library implementation we try to be more clever and allow arbitrarily many changes of the environment given that the used values are from a small set. Outside glibc this will eat up all memory after a while. */ #if defined _LIBC || (defined HAVE_SEARCH_H && defined HAVE_TSEARCH \ && defined __GNUC__) # define USE_TSEARCH 1 # include /* This is a pointer to the root of the search tree with the known values. */ static void *known_values; # define KNOWN_VALUE(Str) \ ({ \ void *value = tfind (Str, &known_values, (__compar_fn_t) strcmp); \ value != NULL ? *(char **) value : NULL; \ }) # define STORE_VALUE(Str) \ tsearch (Str, &known_values, (__compar_fn_t) strcmp) #else # undef USE_TSEARCH # define KNOWN_VALUE(Str) NULL # define STORE_VALUE(Str) do { } while (0) #endif /* If this variable is not a null pointer we allocated the current environment. */ static char **last_environ; /* This function is used by `setenv' and `putenv'. The difference between the two functions is that for the former must create a new string which is then placed in the environment, while the argument of `putenv' must be used directly. This is all complicated by the fact that we try to reuse values once generated for a `setenv' call since we can never free the strings. */ static int __add_to_environ (const char *name, const char *value, const char *combined, int replace) { register char **ep; register size_t size; const size_t namelen = strlen (name); const size_t vallen = value != NULL ? strlen (value) + 1 : 0; LOCK; /* We have to get the pointer now that we have the lock and not earlier since another thread might have created a new environment. */ ep = __environ; size = 0; if (ep != NULL) { for (; *ep != NULL; ++ep) if (!strncmp (*ep, name, namelen) && (*ep)[namelen] == '=') break; else ++size; } if (ep == NULL || __builtin_expect (*ep == NULL, 1)) { char **new_environ; /* We allocated this space; we can extend it. */ new_environ = (char **) realloc (last_environ, (size + 2) * sizeof (char *)); if (new_environ == NULL) { UNLOCK; return -1; } /* If the whole entry is given add it. */ if (combined != NULL) /* We must not add the string to the search tree since it belongs to the user. */ new_environ[size] = (char *) combined; else { /* See whether the value is already known. */ #ifdef USE_TSEARCH # ifdef __GNUC__ char new_value[namelen + 1 + vallen]; # else char *new_value = (char *) alloca (namelen + 1 + vallen); # endif # ifdef _LIBC __mempcpy (__mempcpy (__mempcpy (new_value, name, namelen), "=", 1), value, vallen); # else memcpy (new_value, name, namelen); new_value[namelen] = '='; memcpy (&new_value[namelen + 1], value, vallen); # endif new_environ[size] = KNOWN_VALUE (new_value); if (__builtin_expect (new_environ[size] == NULL, 1)) #endif { new_environ[size] = (char *) malloc (namelen + 1 + vallen); if (__builtin_expect (new_environ[size] == NULL, 0)) { __set_errno (ENOMEM); UNLOCK; return -1; } #ifdef USE_TSEARCH memcpy (new_environ[size], new_value, namelen + 1 + vallen); #else memcpy (new_environ[size], name, namelen); new_environ[size][namelen] = '='; memcpy (&new_environ[size][namelen + 1], value, vallen); #endif /* And save the value now. We cannot do this when we remove the string since then we cannot decide whether it is a user string or not. */ STORE_VALUE (new_environ[size]); } } if (__environ != last_environ) memcpy ((char *) new_environ, (char *) __environ, size * sizeof (char *)); new_environ[size + 1] = NULL; last_environ = __environ = new_environ; } else if (replace) { char *np; /* Use the user string if given. */ if (combined != NULL) np = (char *) combined; else { #ifdef USE_TSEARCH # ifdef __GNUC__ char new_value[namelen + 1 + vallen]; # else char *new_value = (char *) alloca (namelen + 1 + vallen); # endif # ifdef _LIBC __mempcpy (__mempcpy (__mempcpy (new_value, name, namelen), "=", 1), value, vallen); # else memcpy (new_value, name, namelen); new_value[namelen] = '='; memcpy (&new_value[namelen + 1], value, vallen); # endif np = KNOWN_VALUE (new_value); if (__builtin_expect (np == NULL, 1)) #endif { np = malloc (namelen + 1 + vallen); if (__builtin_expect (np == NULL, 0)) { UNLOCK; return -1; } #ifdef USE_TSEARCH memcpy (np, new_value, namelen + 1 + vallen); #else memcpy (np, name, namelen); np[namelen] = '='; memcpy (&np[namelen + 1], value, vallen); #endif /* And remember the value. */ STORE_VALUE (np); } } *ep = np; } UNLOCK; return 0; } int setenv (const char *name, const char *value, int replace) { if (name == NULL || *name == '\0' || strchr (name, '=') != NULL) { __set_errno (EINVAL); return -1; } return __add_to_environ (name, value, NULL, replace); } int unsetenv (const char *name) { size_t len; char **ep; if (name == NULL || *name == '\0' || strchr (name, '=') != NULL) { __set_errno (EINVAL); return -1; } len = strlen (name); LOCK; ep = __environ; while (*ep != NULL) if (!strncmp (*ep, name, len) && (*ep)[len] == '=') { /* Found it. Remove this pointer by moving later ones back. */ char **dp = ep; do dp[0] = dp[1]; while (*dp++); /* Continue the loop in case NAME appears again. */ } else ++ep; UNLOCK; return 0; } /* The `clearenv' was planned to be added to POSIX.1 but probably never made it. Nevertheless the POSIX.9 standard (POSIX bindings for Fortran 77) requires this function. */ int clearenv (void) { LOCK; if (__environ == last_environ && __environ != NULL) { /* We allocated this environment so we can free it. */ free (__environ); last_environ = NULL; } /* Clear the environment pointer removes the whole environment. */ __environ = NULL; UNLOCK; return 0; } #ifdef _LIBC libc_freeres_fn (free_mem) { /* Remove all traces. */ clearenv (); /* Now remove the search tree. */ __tdestroy (known_values, free); known_values = NULL; } # undef setenv # undef unsetenv # undef clearenv weak_alias (__setenv, setenv) weak_alias (__unsetenv, unsetenv) weak_alias (__clearenv, clearenv) #endif - -#endif /*!HAVE_W32CE_SYSTEM*/ diff --git a/src/system-w32ce.c b/src/system-w32ce.c deleted file mode 100644 index da5dcf2..0000000 --- a/src/system-w32ce.c +++ /dev/null @@ -1,706 +0,0 @@ -/* system-w32ce.c - System support functions for WindowsCE. - * Copyright (C) 2010 Free Software Foundation, Inc. - * - * This file is part of Assuan. - * - * Assuan is free software; you can redistribute it and/or modify it - * under the terms of the GNU Lesser General Public License as - * published by the Free Software Foundation; either version 2.1 of - * the License, or (at your option) any later version. - * - * Assuan 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 - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this program; if not, see . - * SPDX-License-Identifier: LGPL-2.1+ - */ - - -#ifdef HAVE_CONFIG_H -#include -#endif - -#include -#include -#include -# ifdef HAVE_WINSOCK2_H -# include -# endif -#include -#include -#include - -#include "assuan-defs.h" -#include "debug.h" - - -#define GPGCEDEV_IOCTL_GET_RVID \ - CTL_CODE (FILE_DEVICE_STREAMS, 2048, METHOD_BUFFERED, FILE_ANY_ACCESS) -#define GPGCEDEV_IOCTL_MAKE_PIPE \ - CTL_CODE (FILE_DEVICE_STREAMS, 2049, METHOD_BUFFERED, FILE_ANY_ACCESS) - - - - -static wchar_t * -utf8_to_wchar (const char *string) -{ - int n; - size_t nbytes; - wchar_t *result; - - if (!string) - return NULL; - - n = MultiByteToWideChar (CP_UTF8, 0, string, -1, NULL, 0); - if (n < 0) - { - gpg_err_set_errno (EINVAL); - return NULL; - } - - nbytes = (size_t)(n+1) * sizeof(*result); - if (nbytes / sizeof(*result) != (n+1)) - { - gpg_err_set_errno (ENOMEM); - return NULL; - } - result = malloc (nbytes); - if (!result) - return NULL; - - n = MultiByteToWideChar (CP_UTF8, 0, string, -1, result, n); - if (n < 0) - { - free (result); - gpg_err_set_errno (EINVAL); - result = NULL; - } - return result; -} - -/* Convenience function. */ -static void -free_wchar (wchar_t *string) -{ - if (string) - free (string); -} - - - -assuan_fd_t -assuan_fdopen (int fd) -{ - return (assuan_fd_t)fd; -} - - - -/* Sleep for the given number of microseconds. Default - implementation. */ -void -__assuan_usleep (assuan_context_t ctx, unsigned int usec) -{ - if (usec) - Sleep (usec / 1000); -} - - - -/* Prepare a pipe. Returns a handle which is, depending on WRITE_END, - will either act the read or as the write end of the pipe. The - other value returned is a rendezvous id used to complete the pipe - creation with _assuan_w32ce_finish_pipe. The rendezvous id may be - passed to another process and that process may finish the pipe - creation. This creates the interprocess pipe. The rendezvous id - is not a handle but a plain number; there is no release function - and care should be taken not to pass it to a function expecting a - handle. */ -HANDLE -_assuan_w32ce_prepare_pipe (int *r_rvid, int write_end) -{ - HANDLE hd; - LONG rvid; - - ActivateDevice (L"Drivers\\GnuPG_Device", 0); - - /* Note: Using "\\$device\\GPG1" should be identical to "GPG1:". - However this returns an invalid parameter error without having - called GPG_Init in the driver. The docs mention something about - RegisterAFXEx but that API is not documented. */ - hd = CreateFile (L"GPG1:", write_end? GENERIC_WRITE : GENERIC_READ, - FILE_SHARE_READ | FILE_SHARE_WRITE, - NULL, OPEN_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL); - if (hd != INVALID_HANDLE_VALUE) - { - if (!DeviceIoControl (hd, GPGCEDEV_IOCTL_GET_RVID, - NULL, 0, &rvid, sizeof rvid, NULL, NULL)) - { - DWORD lastrc = GetLastError (); - CloseHandle (hd); - hd = INVALID_HANDLE_VALUE; - SetLastError (lastrc); - } - else - *r_rvid = rvid; - } - - return hd; -} - - -/* Create a pipe. WRITE_END shall have the opposite value of the one - pssed to _assuan_w32ce_prepare_pipe; see there for more - details. */ -HANDLE -_assuan_w32ce_finish_pipe (int rvid, int write_end) -{ - HANDLE hd; - - if (!rvid) - { - SetLastError (ERROR_INVALID_HANDLE); - return INVALID_HANDLE_VALUE; - } - - hd = CreateFile (L"GPG1:", write_end? GENERIC_WRITE : GENERIC_READ, - FILE_SHARE_READ | FILE_SHARE_WRITE, - NULL, OPEN_ALWAYS, FILE_ATTRIBUTE_NORMAL,NULL); - if (hd != INVALID_HANDLE_VALUE) - { - if (!DeviceIoControl (hd, GPGCEDEV_IOCTL_MAKE_PIPE, - &rvid, sizeof rvid, NULL, 0, NULL, NULL)) - { - DWORD lastrc = GetLastError (); - CloseHandle (hd); - hd = INVALID_HANDLE_VALUE; - SetLastError (lastrc); - } - } - - return hd; -} - - -/* WindowsCE does not provide a pipe feature. However we need - something like a pipe to convey data between processes and in some - cases within a process. This replacement is not only used by - libassuan but exported and thus usable by gnupg and gpgme as well. */ -DWORD -_assuan_w32ce_create_pipe (HANDLE *read_hd, HANDLE *write_hd, - LPSECURITY_ATTRIBUTES sec_attr, DWORD size) -{ - HANDLE hd[2] = {INVALID_HANDLE_VALUE, INVALID_HANDLE_VALUE}; - int rvid; - int rc = 0; - - hd[0] = _assuan_w32ce_prepare_pipe (&rvid, 0); - if (hd[0] != INVALID_HANDLE_VALUE) - { - hd[1] = _assuan_w32ce_finish_pipe (rvid, 1); - if (hd[1] != INVALID_HANDLE_VALUE) - rc = 1; - else - { - DWORD lastrc = GetLastError (); - CloseHandle (hd[0]); - hd[0] = INVALID_HANDLE_VALUE; - SetLastError (lastrc); - } - } - - *read_hd = hd[0]; - *write_hd = hd[1]; - return rc; -} - - -/* Create a pipe with one inheritable end. Default implementation. - If INHERIT_IDX is 0, the read end of the pipe is made inheritable; - with INHERIT_IDX is 1 the write end will be inheritable. The - question now is how we create an inheritable pipe end under windows - CE were handles are process local objects? The trick we employ is - to defer the actual creation to the other end: We create an - incomplete pipe and pass a rendezvous id to the other end - (process). The other end now uses the rendezvous id to lookup the - pipe in our device driver, creates a new handle and uses that one - to finally establish the pipe. */ -int -__assuan_pipe (assuan_context_t ctx, assuan_fd_t fd[2], int inherit_idx) -{ - HANDLE hd; - int rvid; - - hd = _assuan_w32ce_prepare_pipe (&rvid, !inherit_idx); - if (hd == INVALID_HANDLE_VALUE) - { - TRACE1 (ctx, ASSUAN_LOG_SYSIO, "__assuan_pipe", ctx, - "CreatePipe failed: %s", _assuan_w32_strerror (ctx, -1)); - gpg_err_set_errno (EIO); - return -1; - } - - if (inherit_idx) - { - fd[0] = hd; - fd[1] = (void*)rvid; - } - else - { - fd[0] = (void*)rvid; - fd[1] = hd; - } - return 0; -} - - - -/* Close the given file descriptor, created with _assuan_pipe or one - of the socket functions. Default implementation. */ -int -__assuan_close (assuan_context_t ctx, assuan_fd_t fd) -{ - int rc = closesocket (HANDLE2SOCKET(fd)); - int err = WSAGetLastError (); - - /* Note that gpg_err_set_errno on Windows CE overwrites - WSAGetLastError() (via SetLastError()). */ - if (rc) - gpg_err_set_errno (_assuan_sock_wsa2errno (err)); - if (rc && err == WSAENOTSOCK) - { - rc = CloseHandle (fd); - if (rc) - /* FIXME. */ - gpg_err_set_errno (EIO); - } - return rc; -} - - - -ssize_t -__assuan_read (assuan_context_t ctx, assuan_fd_t fd, void *buffer, size_t size) -{ - /* Due to the peculiarities of the W32 API we can't use read for a - network socket and thus we try to use recv first and fallback to - read if recv detects that it is not a network socket. */ - int res; - - TRACE_BEG3 (ctx, ASSUAN_LOG_SYSIO, "__assuan_read", ctx, - "fd=0x%x, buffer=%p, size=%i", fd, buffer, size); - -#ifdef HAVE_W32CE_SYSTEM - /* This is a bit of a hack to support stdin over ssh. Note that - fread buffers fully while getchar is line buffered. Weird, but - that's the way it is. ASSUAN_STDIN and ASSUAN_STDOUT are - special handle values that shouldn't occur in the wild. */ - if (fd == ASSUAN_STDIN) - { - int i = 0; - int chr; - while (i < size) - { - chr = getchar(); - if (chr == EOF) - break; - ((char*)buffer)[i++] = (char) chr; - if (chr == '\n') - break; - } - return TRACE_SYSRES (i); - } -#endif - - res = recv (HANDLE2SOCKET (fd), buffer, size, 0); - if (res == -1) - { - TRACE_LOG1 ("recv failed: rc=%d", (int)WSAGetLastError ()); - switch (WSAGetLastError ()) - { - case WSAENOTSOCK: - { - DWORD nread = 0; - - res = ReadFile (fd, buffer, size, &nread, NULL); - if (! res) - { - TRACE_LOG1 ("ReadFile failed: rc=%d", (int)GetLastError ()); - switch (GetLastError ()) - { - case ERROR_BROKEN_PIPE: - gpg_err_set_errno (EPIPE); - break; - - case ERROR_PIPE_NOT_CONNECTED: - case ERROR_BUSY: - gpg_err_set_errno (EAGAIN); - break; - - default: - gpg_err_set_errno (EIO); - } - res = -1; - } - else - res = (int) nread; - } - break; - - case WSAEWOULDBLOCK: - gpg_err_set_errno (EAGAIN); - break; - - case ERROR_BROKEN_PIPE: - gpg_err_set_errno (EPIPE); - break; - - default: - gpg_err_set_errno (EIO); - break; - } - } - return TRACE_SYSRES (res); -} - - - -ssize_t -__assuan_write (assuan_context_t ctx, assuan_fd_t fd, const void *buffer, - size_t size) -{ - /* Due to the peculiarities of the W32 API we can't use write for a - network socket and thus we try to use send first and fallback to - write if send detects that it is not a network socket. */ - int res; - - TRACE_BEG3 (ctx, ASSUAN_LOG_SYSIO, "__assuan_write", ctx, - "fd=0x%x, buffer=%p, size=%i", fd, buffer, size); - -#ifdef HAVE_W32CE_SYSTEM - /* This is a bit of a hack to support stdout over ssh. Note that - fread buffers fully while getchar is line buffered. Weird, but - that's the way it is. ASSUAN_STDIN and ASSUAN_STDOUT are - special handle values that shouldn't occur in the wild. */ - if (fd == ASSUAN_STDOUT) - { - res = fwrite (buffer, 1, size, stdout); - return TRACE_SYSRES (res); - } -#endif - - res = send ((int)fd, buffer, size, 0); - if (res == -1 && WSAGetLastError () == WSAENOTSOCK) - { - DWORD nwrite; - - TRACE_LOG ("send call failed - trying WriteFile"); - res = WriteFile (fd, buffer, size, &nwrite, NULL); - if (! res) - { - TRACE_LOG1 ("WriteFile failed: rc=%d", (int)GetLastError ()); - switch (GetLastError ()) - { - case ERROR_BROKEN_PIPE: - case ERROR_NO_DATA: - gpg_err_set_errno (EPIPE); - break; - - case ERROR_PIPE_NOT_CONNECTED: - case ERROR_BUSY: - gpg_err_set_errno (EAGAIN); - break; - - default: - gpg_err_set_errno (EIO); - break; - } - res = -1; - } - else - res = (int) nwrite; - } - else if (res == -1) - TRACE_LOG1 ("send call failed: rc=%d", (int)GetLastError ()); - return TRACE_SYSRES (res); -} - - - -int -__assuan_recvmsg (assuan_context_t ctx, assuan_fd_t fd, assuan_msghdr_t msg, - int flags) -{ - gpg_err_set_errno (ENOSYS); - return -1; -} - - - - -int -__assuan_sendmsg (assuan_context_t ctx, assuan_fd_t fd, assuan_msghdr_t msg, - int flags) -{ - gpg_err_set_errno (ENOSYS); - return -1; -} - - - - -/* Build a command line for use with W32's CreateProcess. On success - CMDLINE gets the address of a newly allocated string. */ -static int -build_w32_commandline (assuan_context_t ctx, const char * const *argv, - assuan_fd_t fd0, assuan_fd_t fd1, assuan_fd_t fd2, - int fd2_isnull, - char **cmdline) -{ - int i, n; - const char *s; - char *buf, *p; - char fdbuf[3*30]; - - p = fdbuf; - *p = 0; - if (fd0 != ASSUAN_INVALID_FD) - { - snprintf (p, 25, "-&S0=%d ", (int)fd0); - p += strlen (p); - } - if (fd1 != ASSUAN_INVALID_FD) - { - snprintf (p, 25, "-&S1=%d ", (int)fd1); - p += strlen (p); - } - if (fd2 != ASSUAN_INVALID_FD) - { - if (fd2_isnull) - strcpy (p, "-&S2=null "); - else - snprintf (p, 25, "-&S2=%d ", (int)fd2); - p += strlen (p); - } - - *cmdline = NULL; - n = strlen (fdbuf); - for (i=0; (s = argv[i]); i++) - { - if (!i) - continue; /* Ignore argv[0]. */ - n += strlen (s) + 1 + 2; /* (1 space, 2 quoting) */ - for (; *s; s++) - if (*s == '\"') - n++; /* Need to double inner quotes. */ - } - n++; - - buf = p = _assuan_malloc (ctx, n); - if (! buf) - return -1; - - p = stpcpy (p, fdbuf); - for (i = 0; argv[i]; i++) - { - if (!i) - continue; /* Ignore argv[0]. */ - if (i > 1) - p = stpcpy (p, " "); - - if (! *argv[i]) /* Empty string. */ - p = stpcpy (p, "\"\""); - else if (strpbrk (argv[i], " \t\n\v\f\"")) - { - p = stpcpy (p, "\""); - for (s = argv[i]; *s; s++) - { - *p++ = *s; - if (*s == '\"') - *p++ = *s; - } - *p++ = '\"'; - *p = 0; - } - else - p = stpcpy (p, argv[i]); - } - - *cmdline = buf; - return 0; -} - - -int -__assuan_spawn (assuan_context_t ctx, pid_t *r_pid, const char *name, - const char **argv, - assuan_fd_t fd_in, assuan_fd_t fd_out, - assuan_fd_t *fd_child_list, - void (*atfork) (void *opaque, int reserved), - void *atforkvalue, unsigned int flags) -{ - PROCESS_INFORMATION pi = - { - NULL, /* Returns process handle. */ - 0, /* Returns primary thread handle. */ - 0, /* Returns pid. */ - 0 /* Returns tid. */ - }; - assuan_fd_t fd; - assuan_fd_t *fdp; - assuan_fd_t fd_err; - int fd_err_isnull = 0; - char *cmdline; - - /* Dup stderr to /dev/null unless it is in the list of FDs to be - passed to the child. Well we don't actually open nul because - that is not available on Windows, but use our hack for it. - Because an RVID of 0 is an invalid value and HANDLES will never - have this value either, we test for this as well. */ - - /* FIXME: As long as we can't decide whether a handle is a real - handler or an rendezvous id we can't do anything with the - FD_CHILD_LIST. We can't do much with stderr either, thus we - better don't pass stderr to the child at all. If we would do so - and it is not a rendezvous id the client would run into - problems. */ - fd = assuan_fd_from_posix_fd (fileno (stderr)); - fdp = fd_child_list; - if (fdp) - { - for (; *fdp != ASSUAN_INVALID_FD && *fdp != 0 && *fdp != fd; fdp++) - ; - } - if (!fdp || *fdp == ASSUAN_INVALID_FD) - fd_err_isnull = 1; - fd_err = ASSUAN_INVALID_FD; - - if (build_w32_commandline (ctx, argv, fd_in, fd_out, fd_err, fd_err_isnull, - &cmdline)) - { - return -1; - } - - TRACE2 (ctx, ASSUAN_LOG_SYSIO, "__assuan_spawn", ctx, - "path=`%s' cmdline=`%s'", name, cmdline); - - { - wchar_t *wcmdline, *wname; - - wcmdline = utf8_to_wchar (cmdline); - _assuan_free (ctx, cmdline); - if (!wcmdline) - return -1; - - wname = utf8_to_wchar (name); - if (!wname) - { - free_wchar (wcmdline); - return -1; - } - - if (!CreateProcess (wname, /* Program to start. */ - wcmdline, /* Command line arguments. */ - NULL, /* (not supported) */ - NULL, /* (not supported) */ - FALSE, /* (not supported) */ - (CREATE_SUSPENDED), /* Creation flags. */ - NULL, /* (not supported) */ - NULL, /* (not supported) */ - NULL, /* (not supported) */ - &pi /* Returns process information.*/ - )) - { - TRACE1 (ctx, ASSUAN_LOG_SYSIO, "__assuan_spawn", ctx, - "CreateProcess failed: %s", _assuan_w32_strerror (ctx, -1)); - free_wchar (wname); - free_wchar (wcmdline); - gpg_err_set_errno (EIO); - return -1; - } - free_wchar (wname); - free_wchar (wcmdline); - } - - ResumeThread (pi.hThread); - - TRACE4 (ctx, ASSUAN_LOG_SYSIO, "__assuan_spawn", ctx, - "CreateProcess ready: hProcess=%p hThread=%p" - " dwProcessID=%d dwThreadId=%d\n", - pi.hProcess, pi.hThread, (int) pi.dwProcessId, (int) pi.dwThreadId); - - CloseHandle (pi.hThread); - - *r_pid = (pid_t) pi.hProcess; - return 0; -} - - - - -pid_t -__assuan_waitpid (assuan_context_t ctx, pid_t pid, int nowait, - int *status, int options) -{ - CloseHandle ((HANDLE) pid); - return 0; -} - - - -int -__assuan_socketpair (assuan_context_t ctx, int namespace, int style, - int protocol, assuan_fd_t filedes[2]) -{ - gpg_err_set_errno (ENOSYS); - return -1; -} - - -int -__assuan_socket (assuan_context_t ctx, int namespace, int style, int protocol) -{ - int res; - - res = socket (namespace, style, protocol); - if (res == -1) - gpg_err_set_errno (_assuan_sock_wsa2errno (WSAGetLastError ())); - return res; -} - - -int -__assuan_connect (assuan_context_t ctx, int sock, struct sockaddr *addr, - socklen_t length) -{ - int res; - - res = connect (sock, addr, length); - if (res < 0) - gpg_err_set_errno (_assuan_sock_wsa2errno (WSAGetLastError ())); - return res; -} - - - -/* The default system hooks for assuan contexts. */ -struct assuan_system_hooks _assuan_system_hooks = - { - ASSUAN_SYSTEM_HOOKS_VERSION, - __assuan_usleep, - __assuan_pipe, - __assuan_close, - __assuan_read, - __assuan_write, - __assuan_recvmsg, - __assuan_sendmsg, - __assuan_spawn, - __assuan_waitpid, - __assuan_socketpair, - __assuan_socket, - __assuan_connect - }; diff --git a/src/system.c b/src/system.c index fa13987..e5104bb 100644 --- a/src/system.c +++ b/src/system.c @@ -1,540 +1,536 @@ /* system.c - System support functions. * Copyright (C) 2009 Free Software Foundation, Inc. * * This file is part of Assuan. * * Assuan is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as * published by the Free Software Foundation; either version 2.1 of * the License, or (at your option) any later version. * * Assuan 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 * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this program; if not, see . * SPDX-License-Identifier: LGPL-2.1+ */ #ifdef HAVE_CONFIG_H #include #endif #include #include #ifdef HAVE_SYS_TYPES_H /* Solaris 8 needs sys/types.h before time.h. */ # include #endif #include #ifdef HAVE_FCNTL_H #include #endif #include "assuan-defs.h" #include "debug.h" #define DEBUG_SYSIO 0 /* Manage memory specific to a context. */ void * _assuan_malloc (assuan_context_t ctx, size_t cnt) { return ctx->malloc_hooks.malloc (cnt); } void * _assuan_realloc (assuan_context_t ctx, void *ptr, size_t cnt) { return ctx->malloc_hooks.realloc (ptr, cnt); } void * _assuan_calloc (assuan_context_t ctx, size_t cnt, size_t elsize) { void *ptr; size_t nbytes; nbytes = cnt * elsize; /* Check for overflow. */ if (elsize && nbytes / elsize != cnt) { gpg_err_set_errno (ENOMEM); return NULL; } ptr = ctx->malloc_hooks.malloc (nbytes); if (ptr) memset (ptr, 0, nbytes); return ptr; } void _assuan_free (assuan_context_t ctx, void *ptr) { if (ptr) ctx->malloc_hooks.free (ptr); } /* Release the memory at PTR using the allocation handler of the context CTX. This is a convenience function. */ void assuan_free (assuan_context_t ctx, void *ptr) { _assuan_free (ctx, ptr); } /* Copy the system hooks struct, paying attention to version differences. SRC is usually from the user, DST MUST be from the library. */ void _assuan_system_hooks_copy (assuan_system_hooks_t dst, assuan_system_hooks_t src) { /* Reset the defaults. */ if (dst != &_assuan_system_hooks) memcpy (dst, &_assuan_system_hooks, sizeof (*dst)); dst->version = ASSUAN_SYSTEM_HOOKS_VERSION; if (src->version >= 1) { dst->usleep = src->usleep; dst->pipe = src->pipe; dst->close = src->close; dst->read = src->read; dst->write = src->write; dst->sendmsg = src->sendmsg; dst->recvmsg = src->recvmsg; dst->spawn = src->spawn; dst->waitpid = src->waitpid; dst->socketpair = src->socketpair; } if (src->version >= 2) { dst->socket = src->socket; dst->connect = src->connect; } if (src->version > 2) /* FIXME. Application uses newer version of the library. What to do? */ ; } /* Sleep for the given number of microseconds. */ void _assuan_usleep (assuan_context_t ctx, unsigned int usec) { TRACE1 (ctx, ASSUAN_LOG_SYSIO, "_assuan_usleep", ctx, "usec=%u", usec); if (ctx->system.version) (ctx->system.usleep) (ctx, usec); else { _assuan_pre_syscall (); __assuan_usleep (ctx, usec); _assuan_post_syscall (); } } /* Create a pipe with one inheritable end. */ int _assuan_pipe (assuan_context_t ctx, assuan_fd_t fd[2], int inherit_idx) { int err; TRACE_BEG2 (ctx, ASSUAN_LOG_SYSIO, "_assuan_pipe", ctx, "inherit_idx=%i (Assuan uses it for %s)", inherit_idx, inherit_idx ? "reading" : "writing"); if (ctx->system.version) err = (ctx->system.pipe) (ctx, fd, inherit_idx); else err = __assuan_pipe (ctx, fd, inherit_idx); if (err) return TRACE_SYSRES (err); return TRACE_SUC2 ("read=0x%x, write=0x%x", fd[0], fd[1]); } /* Close the given file descriptor, created with _assuan_pipe or one of the socket functions. */ int _assuan_close (assuan_context_t ctx, assuan_fd_t fd) { TRACE1 (ctx, ASSUAN_LOG_SYSIO, "_assuan_close", ctx, "fd=0x%x", fd); if (ctx->system.version) return (ctx->system.close) (ctx, fd); else { int res; _assuan_pre_syscall (); res = __assuan_close (ctx, fd); _assuan_post_syscall (); return res; } } /* Same as assuan_close but used for the inheritable end of a pipe. */ int _assuan_close_inheritable (assuan_context_t ctx, assuan_fd_t fd) { TRACE1 (ctx, ASSUAN_LOG_SYSIO, "_assuan_close_inheritable", ctx, "fd=0x%x", fd); -#ifdef HAVE_W32CE_SYSTEM - return 0; /* Nothing to do because it is a rendezvous id. */ -#else if (ctx->system.version) return (ctx->system.close) (ctx, fd); else { int res; _assuan_pre_syscall (); res = __assuan_close (ctx, fd); _assuan_post_syscall (); return res; } -#endif } ssize_t _assuan_read (assuan_context_t ctx, assuan_fd_t fd, void *buffer, size_t size) { #if DEBUG_SYSIO ssize_t res; TRACE_BEG3 (ctx, ASSUAN_LOG_SYSIO, "_assuan_read", ctx, "fd=0x%x, buffer=%p, size=%i", fd, buffer, size); if (ctx->system.version) res = (ctx->system.read) (ctx, fd, buffer, size); else { _assuan_pre_syscall (); res = __assuan_read (ctx, fd, buffer, size); _assuan_post_syscall (); } return TRACE_SYSRES (res); #else if (ctx->system.version) return (ctx->system.read) (ctx, fd, buffer, size); else { ssize_t res; _assuan_pre_syscall (); res = __assuan_read (ctx, fd, buffer, size); _assuan_post_syscall (); return res; } #endif } ssize_t _assuan_write (assuan_context_t ctx, assuan_fd_t fd, const void *buffer, size_t size) { #if DEBUG_SYSIO ssize_t res; TRACE_BEG3 (ctx, ASSUAN_LOG_SYSIO, "_assuan_write", ctx, "fd=0x%x, buffer=%p, size=%i", fd, buffer, size); if (ctx->system.version) res = (ctx->system.write) (ctx, fd, buffer, size); else { _assuan_pre_syscall (); res = __assuan_write (ctx, fd, buffer, size); _assuan_post_syscall (); } return TRACE_SYSRES (res); #else if (ctx->system.version) return (ctx->system.write) (ctx, fd, buffer, size); else { ssize_t res; _assuan_pre_syscall (); res = __assuan_write (ctx, fd, buffer, size); _assuan_post_syscall (); return res; } #endif } int _assuan_recvmsg (assuan_context_t ctx, assuan_fd_t fd, assuan_msghdr_t msg, int flags) { #if DEBUG_SYSIO ssize_t res; TRACE_BEG3 (ctx, ASSUAN_LOG_SYSIO, "_assuan_recvmsg", ctx, "fd=0x%x, msg=%p, flags=0x%x", fd, msg, flags); if (ctx->system.version) res = (ctx->system.recvmsg) (ctx, fd, msg, flags); else { _assuan_pre_syscall (); res = __assuan_recvmsg (ctx, fd, msg, flags); _assuan_post_syscall (); } if (res > 0) { struct cmsghdr *cmptr; TRACE_LOG2 ("msg->msg_iov[0] = { iov_base=%p, iov_len=%i }", msg->msg_iov[0].iov_base, msg->msg_iov[0].iov_len); TRACE_LOGBUF (msg->msg_iov[0].iov_base, res); cmptr = CMSG_FIRSTHDR (msg); if (cmptr) { void *data = CMSG_DATA (cmptr); TRACE_LOG5 ("cmsg_len=0x%x (0x%x data), cmsg_level=0x%x, " "cmsg_type=0x%x, first data int=0x%x", cmptr->cmsg_len, cmptr->cmsg_len - (((char *)data) - ((char *)cmptr)), cmptr->cmsg_level, cmptr->cmsg_type, *(int *)data); } } return TRACE_SYSRES (res); #else if (ctx->system.version) return (ctx->system.recvmsg) (ctx, fd, msg, flags); else { ssize_t res; _assuan_pre_syscall (); res = __assuan_recvmsg (ctx, fd, msg, flags); _assuan_post_syscall (); return res; } #endif } int _assuan_sendmsg (assuan_context_t ctx, assuan_fd_t fd, assuan_msghdr_t msg, int flags) { #if DEBUG_SYSIO ssize_t res; TRACE_BEG3 (ctx, ASSUAN_LOG_SYSIO, "_assuan_sendmsg", ctx, "fd=0x%x, msg=%p, flags=0x%x", fd, msg, flags); { struct cmsghdr *cmptr; TRACE_LOG2 ("msg->iov[0] = { iov_base=%p, iov_len=%i }", msg->msg_iov[0].iov_base, msg->msg_iov[0].iov_len); TRACE_LOGBUF (msg->msg_iov[0].iov_base, msg->msg_iov[0].iov_len); cmptr = CMSG_FIRSTHDR (msg); if (cmptr) { void *data = CMSG_DATA (cmptr); TRACE_LOG5 ("cmsg_len=0x%x (0x%x data), cmsg_level=0x%x, " "cmsg_type=0x%x, first data int=0x%x", cmptr->cmsg_len, cmptr->cmsg_len - (((char *)data) - ((char *)cmptr)), cmptr->cmsg_level, cmptr->cmsg_type, *(int *)data); } } if (ctx->system.version) res = (ctx->system.sendmsg) (ctx, fd, msg, flags); else { _assuan_pre_syscall (); res = __assuan_sendmsg (ctx, fd, msg, flags); _assuan_post_syscall (); } return TRACE_SYSRES (res); #else if (ctx->system.version) return (ctx->system.sendmsg) (ctx, fd, msg, flags); else { ssize_t res; _assuan_pre_syscall (); res = __assuan_sendmsg (ctx, fd, msg, flags); _assuan_post_syscall (); return res; } #endif } /* Create a new process from NAME and ARGV. Provide FD_IN and FD_OUT as stdin and stdout. Inherit the ASSUAN_INVALID_FD-terminated FD_CHILD_LIST as given (no remapping), which must be inheritable. On Unix, call ATFORK with ATFORKVALUE after fork and before exec. */ int _assuan_spawn (assuan_context_t ctx, pid_t *r_pid, const char *name, const char **argv, assuan_fd_t fd_in, assuan_fd_t fd_out, assuan_fd_t *fd_child_list, void (*atfork) (void *opaque, int reserved), void *atforkvalue, unsigned int flags) { int res; int i; TRACE_BEG6 (ctx, ASSUAN_LOG_CTX, "_assuan_spawn", ctx, "name=%s,fd_in=0x%x,fd_out=0x%x," "atfork=%p,atforkvalue=%p,flags=%i", name ? name : "(null)", fd_in, fd_out, atfork, atforkvalue, flags); if (name) { i = 0; while (argv[i]) { TRACE_LOG2 ("argv[%2i] = %s", i, argv[i]); i++; } } i = 0; if (fd_child_list) { while (fd_child_list[i] != ASSUAN_INVALID_FD) { TRACE_LOG2 ("fd_child_list[%2i] = 0x%x", i, fd_child_list[i]); i++; } } if (ctx->system.version) res = (ctx->system.spawn) (ctx, r_pid, name, argv, fd_in, fd_out, fd_child_list, atfork, atforkvalue, flags); else res = __assuan_spawn (ctx, r_pid, name, argv, fd_in, fd_out, fd_child_list, atfork, atforkvalue, flags); if (name) { TRACE_LOG1 ("pid = 0x%x", *r_pid); } else { TRACE_LOG2 ("pid = 0x%x (%s)", *r_pid, *argv); } return TRACE_SYSERR (res); } /* FIXME: Add some sort of waitpid function that covers GPGME and gpg-agent's use of assuan. */ pid_t _assuan_waitpid (assuan_context_t ctx, pid_t pid, int action, int *status, int options) { #if DEBUG_SYSIO ssize_t res; TRACE_BEG4 (ctx, ASSUAN_LOG_SYSIO, "_assuan_waitpid", ctx, "pid=%i, action=%i, status=%p, options=%i", pid, action, status, options); if (ctx->system.version) res = (ctx->system.waitpid) (ctx, pid, action, status, options); else { _assuan_pre_syscall (); res = __assuan_waitpid (ctx, pid, action, status, options); _assuan_post_syscall (); } return TRACE_SYSRES (res); #else if (ctx->system.version) return (ctx->system.waitpid) (ctx, pid, action, status, options); else { ssize_t res; _assuan_pre_syscall (); res = __assuan_waitpid (ctx, pid, action, status, options); _assuan_post_syscall (); return res; } #endif } int _assuan_socketpair (assuan_context_t ctx, int namespace, int style, int protocol, assuan_fd_t filedes[2]) { int res; TRACE_BEG4 (ctx, ASSUAN_LOG_SYSIO, "_assuan_socketpair", ctx, "namespace=%i,style=%i,protocol=%i,filedes=%p", namespace, style, protocol, filedes); if (ctx->system.version) res = (ctx->system.socketpair) (ctx, namespace, style, protocol, filedes); else res = __assuan_socketpair (ctx, namespace, style, protocol, filedes); if (res == 0) TRACE_LOG2 ("filedes = { 0x%x, 0x%x }", filedes[0], filedes[1]); return TRACE_SYSERR (res); } assuan_fd_t _assuan_socket (assuan_context_t ctx, int namespace, int style, int protocol) { assuan_fd_t res; TRACE_BEG3 (ctx, ASSUAN_LOG_SYSIO, "_assuan_socket", ctx, "namespace=%i,style=%i,protocol=%i", namespace, style, protocol); if (ctx->system.version) res = (ctx->system.socket) (ctx, namespace, style, protocol); else res = __assuan_socket (ctx, namespace, style, protocol); return TRACE_SYSRES (res); } int _assuan_connect (assuan_context_t ctx, assuan_fd_t sock, struct sockaddr *addr, socklen_t length) { int res; TRACE_BEG3 (ctx, ASSUAN_LOG_SYSIO, "_assuan_connect", ctx, "socket=%i,addr=%p,length=%i", sock, addr, length); if (ctx->system.version) res = (ctx->system.connect) (ctx, sock, addr, length); else { _assuan_pre_syscall (); res = __assuan_connect (ctx, sock, addr, length); _assuan_post_syscall (); } return TRACE_SYSRES (res); } diff --git a/src/sysutils.c b/src/sysutils.c index 6c09e47..3778668 100644 --- a/src/sysutils.c +++ b/src/sysutils.c @@ -1,140 +1,53 @@ /* sysutils.c - System utilities * Copyright (C) 2010 Free Software Foundation, Inc. * * This file is part of Assuan. * * Assuan is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as * published by the Free Software Foundation; either version 2.1 of * the License, or (at your option) any later version. * * Assuan 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 * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this program; if not, see . * SPDX-License-Identifier: LGPL-2.1+ */ #ifdef HAVE_CONFIG_H #include #endif #include #include #include #include #ifdef HAVE_W32_SYSTEM # ifdef HAVE_WINSOCK2_H # include # endif # include -# ifdef HAVE_W32CE_SYSTEM -# include -# endif /*HAVE_W32CE_SYSTEM*/ #endif /*HAVE_W32_SYSTEM*/ #include "assuan-defs.h" /* This is actually a dummy function to make sure that is module is not empty. Some compilers barf on empty modules. */ const char * _assuan_sysutils_blurb (void) { static const char blurb[] = "\n\n" "This is Libassuan " PACKAGE_VERSION " - The GnuPG IPC Library\n" "Copyright 2001-2013 Free Software Foundation, Inc.\n" "Copyright 2001-2020 g10 Code GmbH\n" "\n" "SPDX-License-Identifier: LGPL-2.1-or-later\n" "(" BUILD_REVISION " " BUILD_TIMESTAMP ")\n" "\n\n"; return blurb; } - - -/* Return a string from the Win32 Registry or NULL in case of error. - The returned string is allocated using a plain malloc and thus the - caller needs to call the standard free(). The string is looked up - under HKEY_LOCAL_MACHINE. */ -#ifdef HAVE_W32CE_SYSTEM -static char * -w32_read_registry (const wchar_t *dir, const wchar_t *name) -{ - HKEY handle; - DWORD n, nbytes; - wchar_t *buffer = NULL; - char *result = NULL; - - if (RegOpenKeyEx (HKEY_LOCAL_MACHINE, dir, 0, KEY_READ, &handle)) - return NULL; /* No need for a RegClose, so return immediately. */ - - nbytes = 1; - if (RegQueryValueEx (handle, name, 0, NULL, NULL, &nbytes)) - goto out; - buffer = malloc ((n=nbytes+2)); - if (!buffer) - goto out; - if (RegQueryValueEx (handle, name, 0, NULL, (PBYTE)buffer, &n)) - { - free (buffer); - buffer = NULL; - goto out; - } - - n = WideCharToMultiByte (CP_UTF8, 0, buffer, nbytes, NULL, 0, NULL, NULL); - if (n < 0 || (n+1) <= 0) - goto out; - result = malloc (n+1); - if (!result) - goto out; - n = WideCharToMultiByte (CP_UTF8, 0, buffer, nbytes, result, n, NULL, NULL); - if (n < 0) - { - free (result); - result = NULL; - goto out; - } - result[n] = 0; - - out: - free (buffer); - RegCloseKey (handle); - return result; -} -#endif /*HAVE_W32CE_SYSTEM*/ - - - -#ifdef HAVE_W32CE_SYSTEM -/* Replacement for getenv which takes care of the our use of getenv. - The code is not thread safe but we expect it to work in all cases - because it is called for the first time early enough. */ -char * -_assuan_getenv (const char *name) -{ - static int initialized; - static char *val_debug; - static char *val_full_logging; - - if (!initialized) - { - val_debug = w32_read_registry (L"\\Software\\GNU\\libassuan", - L"debug"); - val_full_logging = w32_read_registry (L"\\Software\\GNU\\libassuan", - L"full_logging"); - initialized = 1; - } - - - if (!strcmp (name, "ASSUAN_DEBUG")) - return val_debug; - else if (!strcmp (name, "ASSUAN_FULL_LOGGING")) - return val_full_logging; - else - return NULL; -} -#endif /*HAVE_W32CE_SYSTEM*/ diff --git a/src/w32ce-add.h b/src/w32ce-add.h deleted file mode 100644 index 1f7d7db..0000000 --- a/src/w32ce-add.h +++ /dev/null @@ -1,34 +0,0 @@ -## w32ce-add.h - Include fragment to build assuan.h. -## Copyright (C) 2010 Free Software Foundation, Inc. -## -## This file is part of Assuan. -## -## Assuan is free software; you can redistribute it and/or modify it -## under the terms of the GNU Lesser General Public License as -## published by the Free Software Foundation; either version 2.1 of -## the License, or (at your option) any later version. -## -## Assuan 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 -## Lesser General Public License for more details. -## -## You should have received a copy of the GNU Lesser General Public -## License along with this program; if not, see . -## SPDX-License-Identifier: LGPL-2.1+ -## -## -## This file is included by the mkheader tool. Lines starting with -## a double hash mark are not copied to the destination file. - -HANDLE _assuan_w32ce_prepare_pipe (int *r_rvid, int write_end); -HANDLE _assuan_w32ce_finish_pipe (int rvid, int write_end); -DWORD _assuan_w32ce_create_pipe (HANDLE *read_hd, HANDLE *write_hd, - LPSECURITY_ATTRIBUTES sec_attr, DWORD size); -#define CreatePipe(a,b,c,d) _assuan_w32ce_create_pipe ((a),(b),(c),(d)) - -/* Magic handle values. Let's hope those never occur legitimately as - handles or sockets. (Sockets are numbered sequentially from 0, - while handles seem aligned to wordsize. */ -#define ASSUAN_STDIN (void*)0x7ffffffd -#define ASSUAN_STDOUT (void*)0x7fffffff diff --git a/src/w32ce-fd-t.inc.h b/src/w32ce-fd-t.inc.h deleted file mode 100644 index 70c7997..0000000 --- a/src/w32ce-fd-t.inc.h +++ /dev/null @@ -1,33 +0,0 @@ -## w32ce-fd-t.inc.h - Include fragment to build assuan.h. -## Copyright (C) 2010 Free Software Foundation, Inc. -## -## This file is part of Assuan. -## -## Assuan is free software; you can redistribute it and/or modify it -## under the terms of the GNU Lesser General Public License as -## published by the Free Software Foundation; either version 2.1 of -## the License, or (at your option) any later version. -## -## Assuan 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 -## Lesser General Public License for more details. -## -## You should have received a copy of the GNU Lesser General Public -## License along with this program; if not, see . -## SPDX-License-Identifier: LGPL-2.1+ -## -## -## This file is included by the mkheader tool. Lines starting with -## a double hash mark are not copied to the destination file. - -typedef void *assuan_fd_t; -#define ASSUAN_INVALID_FD ((void*)(-1)) -#define ASSUAN_INVALID_PID ((pid_t) -1) -static GPG_ERR_INLINE assuan_fd_t -assuan_fd_from_posix_fd (int fd) -{ - return (assuan_fd_t)(fd); -} - -##EOF## diff --git a/tests/Makefile.am b/tests/Makefile.am index b547d37..f43c712 100644 --- a/tests/Makefile.am +++ b/tests/Makefile.am @@ -1,51 +1,47 @@ # Makefile for Assuan regression tests # Copyright (C) 2006 Free Software Foundation, Inc. # # This file is part of Assuan. # # Assuan is free software; you can redistribute it and/or modify it # under the terms of the GNU Lesser General Public License as # published by the Free Software Foundation; either version 3 of # the License, or (at your option) any later version. # # Assuan 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 # Lesser General Public License for more details. # # You should have received a copy of the GNU Lesser General Public # License along with this program; if not, see . ## Process this file with automake to produce Makefile.in TESTS_ENVIRONMENT = -EXTRA_DIST = motd ce-createpipe.c +EXTRA_DIST = motd BUILT_SOURCES = CLEANFILES = TESTS = version -if HAVE_W32CE_SYSTEM -w32cetools = ce-createpipe ce-server -endif - if HAVE_W32_SYSTEM testtools = else TESTS += pipeconnect testtools = socks5 endif if USE_DESCRIPTOR_PASSING TESTS += fdpassing endif AM_CFLAGS = $(GPG_ERROR_CFLAGS) AM_LDFLAGS = -no-install noinst_HEADERS = common.h noinst_PROGRAMS = $(TESTS) $(w32cetools) $(testtools) LDADD = ../src/libassuan.la $(GPG_ERROR_LIBS) \ @LDADD_FOR_TESTS_KLUDGE@ diff --git a/tests/ce-createpipe.c b/tests/ce-createpipe.c deleted file mode 100644 index b44784c..0000000 --- a/tests/ce-createpipe.c +++ /dev/null @@ -1,179 +0,0 @@ -/* ce-createpipe.c - Test the W32CE CreatePipe implementation. - Copyright (C) 2010 Free Software Foundation, Inc. - - This file is part of Assuan. - - Assuan is free software; you can redistribute it and/or modify it - under the terms of the GNU Lesser General Public License as - published by the Free Software Foundation; either version 3 of - the License, or (at your option) any later version. - - Assuan 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 - Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public - License along with this program; if not, see . - */ - -#ifdef HAVE_CONFIG_H -#include -#endif - -#include -#include -#include -#include -#include - -#define WIN32_LEAN_AND_MEAN -#include - -#include "common.h" - -#include "../src/assuan.h" /* We need the CreatePipe prototype. */ - - -static DWORD -reader_thread (void *arg) -{ - HANDLE hd = arg; - DWORD nread; - char buffer[16]; - - for (;;) - { - if (!ReadFile (hd, buffer, sizeof buffer, &nread, FALSE)) - { - log_error ("reader: ReadFile failed: rc=%d\n", (int)GetLastError ()); - break; - } - log_info ("reader: read %d bytes\n", (int)nread); - log_printhex ("got: ", buffer, nread); - } - - log_info ("reader: finished\n"); - CloseHandle (hd); - return 0; -} - - -static DWORD -writer_thread (void *arg) -{ - HANDLE hd = arg; - DWORD nwritten; - int i = 0; - int j; - char buffer[20]; - int count; - - for (count=0; count < 30; count++) - { - for (j=0; j < sizeof buffer; j++) - buffer[j] = i++; - - if (!WriteFile (hd, buffer, sizeof buffer, &nwritten, NULL)) - { - log_error ("writer: WriteFile failed: rc=%d\n", (int)GetLastError ()); - break; - } - if (nwritten != sizeof buffer) - log_info ("writer: wrote only %d bytes\n", (int)nwritten); - } - - log_info ("writer: finished\n"); - CloseHandle (hd); - return 0; -} - - - -static void -run_test (void) -{ - HANDLE hd[2]; - HANDLE threads[2]; - - if (!CreatePipe (&hd[0], &hd[1], NULL, 0)) - { - log_error ("CreatePipe failed: rc=%d\n", (int)GetLastError ()); - return; - } - log_info ("pipe created read=%p write=%p\n", hd[0], hd[1]); - - threads[0] = CreateThread (NULL, 0, reader_thread, hd[0], 0, NULL); - if (!threads[0]) - log_fatal ("error creating reader thread: rc=%d\n", (int)GetLastError ()); - else - log_info ("reader thread created\n"); - threads[1] = CreateThread (NULL, 0, writer_thread, hd[1], 0, NULL); - if (!threads[0]) - log_fatal ("error creating writer thread: rc=%d\n", (int)GetLastError ()); - else - log_info ("writer thread created\n"); - - switch (WaitForMultipleObjects (2, threads, FALSE, INFINITE)) - { - case WAIT_OBJECT_0: - log_info ("reader thread finished first\n"); - break; - case WAIT_OBJECT_0 + 1: - log_info ("writer thread finished first\n"); - break; - default: - log_error ("WFMO failed: rc=%d\n", (int)GetLastError ()); - break; - } - - - - CloseHandle (threads[0]); - CloseHandle (threads[1]); -} - - - -/* - M A I N - */ -int -main (int argc, char **argv) -{ - int last_argc = -1; - - if (argc) - { - log_set_prefix (*argv); - argc--; argv++; - } - while (argc && last_argc != argc ) - { - last_argc = argc; - if (!strcmp (*argv, "--help")) - { - printf ("usage: %s [options]\n" - "\n" - "Options:\n" - " --verbose Show what is going on\n", - log_get_prefix ()); - exit (0); - } - if (!strcmp (*argv, "--verbose")) - { - verbose = 1; - argc--; argv++; - } - else if (!strcmp (*argv, "--debug")) - { - verbose = debug = 1; - argc--; argv++; - } - } - - run_test (); - - return errorcount ? 1 : 0; -} - diff --git a/tests/ce-server.c b/tests/ce-server.c deleted file mode 100644 index 0565aee..0000000 --- a/tests/ce-server.c +++ /dev/null @@ -1,1418 +0,0 @@ -/* ce-server.c - An Assuan testbed for W32CE; server code - Copyright (C) 2010 Free Software Foundation, Inc. - - This file is part of Assuan. - - Assuan is free software; you can redistribute it and/or modify it - under the terms of the GNU Lesser General Public License as - published by the Free Software Foundation; either version 3 of - the License, or (at your option) any later version. - - Assuan 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 - Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public - License along with this program; if not, see . - */ - -#ifdef HAVE_CONFIG_H -#include -#endif - -#include -#include -#include -#include -#include -#include -#ifdef HAVE_W32_SYSTEM -# define WIN32_LEAN_AND_MEAN -# include -#else -# include -# include -# include -# include -# ifdef HAVE_SYS_SELECT_H -# include -# endif -#endif -#include - -#ifdef HAVE_W32CE_SYSTEM -# ifndef FILE_ATTRIBUTE_ROMSTATICREF -# define FILE_ATTRIBUTE_ROMSTATICREF FILE_ATTRIBUTE_OFFLINE -# endif -extern BOOL GetStdioPathW (int, wchar_t *, DWORD *); -extern BOOL SetStdioPathW (int, const wchar_t *); -#endif /*!HAVE_W32CE_SYSTEM*/ - -#include "../src/assuan.h" - -#include "common.h" - -/* The port we are using by default. */ -static short server_port = 15898; - -/* Flag set to indicate a shutdown. */ -static int shutdown_pending; - -/* An object to keep track of file descriptors. */ -struct fdinfo_s -{ - struct fdinfo_s *next; - assuan_fd_t fd; /* The descriptor. */ -}; -typedef struct fdinfo_s *fdinfo_t; - - -/* The local state of a connection. */ -struct state_s -{ - /* The current working directory - access using get_cwd(). */ - char *cwd; - - /* If valid, a socket in listening state created by the dataport - command. */ - assuan_fd_t dataport_listen_fd; - - /* If valid the socket accepted for the dataport. */ - assuan_fd_t dataport_accepted_fd; - - /* The error code from a dataport accept operation. */ - gpg_error_t dataport_accept_err; - - /* A list of all unused descriptors created by dataport commands. */ - fdinfo_t dataport_fds; - - /* The descriptor set by the DATAPORT command. */ - assuan_fd_t dataport_fd; -}; -typedef struct state_s *state_t; - - - -/* Local prototypes. */ -static gpg_error_t cmd_newdataport_cont (void *opaque, gpg_error_t err, - unsigned char *data, size_t datalen); - - - -/* A wrapper around read to make it work under Windows with HANDLES - and socket descriptors. Takes care of EINTR on POSIX. */ -static int -my_read (assuan_fd_t fd, void *buffer, size_t size) -{ - int res; - -#ifdef HAVE_W32_SYSTEM - res = recv (HANDLE2SOCKET (fd), buffer, size, 0); - if (res == -1) - { - switch (WSAGetLastError ()) - { - case WSAENOTSOCK: - { - DWORD nread = 0; - - res = ReadFile (fd, buffer, size, &nread, NULL); - if (!res) - { - switch (GetLastError ()) - { - case ERROR_BROKEN_PIPE: - gpg_err_set_errno (EPIPE); - break; - - default: - gpg_err_set_errno (EIO); - } - res = -1; - } - else - res = (int) nread; - } - break; - - case WSAEWOULDBLOCK: - gpg_err_set_errno (EAGAIN); - break; - - case ERROR_BROKEN_PIPE: - gpg_err_set_errno (EPIPE); - break; - - default: - gpg_err_set_errno (EIO); - break; - } - } - return res; -#else /*!HAVE_W32_SYSTEM*/ - do - res = read (fd, buffer, size); - while (res == -1 && errno == EINTR); - return res; -#endif /*!HAVE_W32_SYSTEM*/ -} - - -/* Extended version of write(2) to guarantee that all bytes are - written. Returns 0 on success or -1 and ERRNO on failure. NOTE: - This function does not return the number of bytes written, so any - error must be treated as fatal for this connection as the state of - the receiver is unknown. This works best if blocking is allowed - (so EAGAIN cannot occur). Under Windows this function handles - socket descriptors and system handles. */ -static int -my_writen (assuan_fd_t fd, const char *buffer, size_t length) -{ - while (length) - { - int nwritten; -#ifdef HAVE_W32_SYSTEM - nwritten = send (HANDLE2SOCKET (fd), buffer, length, 0); - if (nwritten == -1 && WSAGetLastError () == WSAENOTSOCK) - { - DWORD nwrite; - - nwritten = WriteFile (fd, buffer, length, &nwrite, NULL); - if (!nwritten) - { - switch (GetLastError ()) - { - case ERROR_BROKEN_PIPE: - case ERROR_NO_DATA: - gpg_err_set_errno (EPIPE); - break; - - default: - gpg_err_set_errno (EIO); - break; - } - nwritten= -1; - } - else - nwritten = (int)nwrite; - } -#else /*!HAVE_W32_SYSTEM*/ - nwritten = write (fd, buffer, length); -#endif /*!HAVE_W32_SYSTEM*/ - if (nwritten < 0) - { - if (errno == EINTR) - continue; - return -1; /* write error */ - } - length -= nwritten; - buffer += nwritten; - } - return 0; /* okay */ -} - - - -static state_t -new_state (void) -{ - state_t state = xcalloc (1, sizeof *state); - state->dataport_listen_fd = ASSUAN_INVALID_FD; - state->dataport_accepted_fd = ASSUAN_INVALID_FD; - state->dataport_fd = ASSUAN_INVALID_FD; - return state; -} - -static void -release_state (state_t state) -{ - fdinfo_t fi, fi2; - - if (!state) - return; - - xfree (state->cwd); - - if (state->dataport_fd != ASSUAN_INVALID_FD) - assuan_sock_close (state->dataport_fd); - if (state->dataport_listen_fd != ASSUAN_INVALID_FD) - assuan_sock_close (state->dataport_listen_fd); - if (state->dataport_accepted_fd != ASSUAN_INVALID_FD) - assuan_sock_close (state->dataport_accepted_fd); - - for (fi=state->dataport_fds; fi; fi = fi2) - { - fi2 = fi->next; - if (fi->fd != ASSUAN_INVALID_FD) - assuan_sock_close (fi->fd); - } - - xfree (state); -} - - -/* Helper to print a message while leaving a command and to - acknowledge the command. */ -static gpg_error_t -leave_cmd (assuan_context_t ctx, gpg_error_t err) -{ - if (err) - { - const char *name = assuan_get_command_name (ctx); - if (!name) - name = "?"; - if (gpg_err_source (err) == GPG_ERR_SOURCE_DEFAULT) - log_error ("command '%s' failed: %s\n", name, gpg_strerror (err)); - else - log_error ("command '%s' failed: %s <%s>\n", name, - gpg_strerror (err), gpg_strsource (err)); - } - return assuan_process_done (ctx, err); -} - - -#ifdef HAVE_W32CE_SYSTEM -static char * -wchar_to_utf8 (const wchar_t *string) -{ - int n; - size_t length = wcslen (string); - char *result; - - n = WideCharToMultiByte (CP_UTF8, 0, string, length, NULL, 0, NULL, NULL); - if (n < 0 || (n+1) <= 0) - log_fatal ("WideCharToMultiByte failed\n"); - - result = xmalloc (n+1); - n = WideCharToMultiByte (CP_ACP, 0, string, length, result, n, NULL, NULL); - if (n < 0) - log_fatal ("WideCharToMultiByte failed\n"); - - result[n] = 0; - return result; -} - -static wchar_t * -utf8_to_wchar (const char *string) -{ - int n; - size_t length = strlen (string); - wchar_t *result; - size_t nbytes; - - n = MultiByteToWideChar (CP_UTF8, 0, string, length, NULL, 0); - if (n < 0 || (n+1) <= 0) - log_fatal ("MultiByteToWideChar failed\n"); - - nbytes = (size_t)(n+1) * sizeof(*result); - if (nbytes / sizeof(*result) != (n+1)) - log_fatal ("utf8_to_wchar: integer overflow\n"); - result = xmalloc (nbytes); - n = MultiByteToWideChar (CP_UTF8, 0, string, length, result, n); - if (n < 0) - log_fatal ("MultiByteToWideChar failed\n"); - result[n] = 0; - - return result; -} -#endif /*HAVE_W32CE_SYSTEM*/ - -#ifndef HAVE_W32CE_SYSTEM -static char * -gnu_getcwd (void) -{ - size_t size = 100; - - while (1) - { - char *buffer = xmalloc (size); - if (getcwd (buffer, size) == buffer) - return buffer; - xfree (buffer); - if (errno != ERANGE) - return 0; - size *= 2; - } -} -#endif /*!HAVE_W32CE_SYSTEM*/ - - -/* Return the current working directory. The returned string is valid - as long as STATE->cwd is not changed. */ -static const char * -get_cwd (state_t state) -{ - if (!state->cwd) - { - /* No working directory yet. On WindowsCE make it the module - directory of this process. */ -#ifdef HAVE_W32_SYSTEM - char *p; -#endif -#ifdef HAVE_W32CE_SYSTEM - wchar_t buf[MAX_PATH+1]; - size_t n; - - n = GetModuleFileName (NULL, buf, MAX_PATH); - if (!n) - state->cwd = xstrdup ("/"); - else - { - buf[n] = 0; - state->cwd = wchar_to_utf8 (buf); - p = strrchr (state->cwd, '\\'); - if (p) - *p = 0; - } -#else - state->cwd = gnu_getcwd (); -#endif -#ifdef HAVE_W32_SYSTEM - for (p=state->cwd; *p; p++) - if (*p == '\\') - *p = '/'; -#endif /*HAVE_W32_SYSTEM*/ - } - - return state->cwd; -} - - - -static gpg_error_t -reset_notify (assuan_context_t ctx, char *line) -{ - state_t state = assuan_get_pointer (ctx); - fdinfo_t fi, fi2; - - /* Close all lingering dataport connections. */ - for (fi=state->dataport_fds; fi; fi = fi2) - { - fi2 = fi->next; - if (fi->fd != ASSUAN_INVALID_FD) - assuan_sock_close (fi->fd); - } - state->dataport_fds = NULL; - - return 0; -} - - -static gpg_error_t -input_notify (assuan_context_t ctx, char *line) -{ - state_t state = assuan_get_pointer (ctx); - assuan_fd_t fd = assuan_get_input_fd (ctx); - fdinfo_t fi; - - if (fd != ASSUAN_INVALID_FD) - { - /* The fd is now in use use - remove it from the unused list. */ - for (fi=state->dataport_fds; fi; fi = fi->next) - if (fi->fd == fd) - fi->fd = ASSUAN_INVALID_FD; - } - - return 0; -} - - -static gpg_error_t -output_notify (assuan_context_t ctx, char *line) -{ - state_t state = assuan_get_pointer (ctx); - assuan_fd_t fd = assuan_get_output_fd (ctx); - fdinfo_t fi; - - if (fd != ASSUAN_INVALID_FD) - { - /* The fd is now in use - remove it from the unused list. */ - for (fi=state->dataport_fds; fi; fi = fi->next) - if (fi->fd == fd) - fi->fd = ASSUAN_INVALID_FD; - } - - return 0; -} - - - -static const char hlp_echo[] = - "ECHO \n" - "\n" - "Print LINE as data lines.\n"; -static gpg_error_t -cmd_echo (assuan_context_t ctx, char *line) -{ - gpg_error_t err; - - err = assuan_send_data (ctx, line, strlen (line)); - - return leave_cmd (ctx, err); -} - - - -static const char hlp_cat[] = - "CAT []\n" - "\n" - "Copy the content of FILENAME to the descriptor set by the OUTPUT\n" - "command. If no OUTPUT command has been given, send the content\n" - "using data lines. Without FILENAME take the content from the\n" - "descriptor set by the INPUT command; if a DATAPORT has been set\n" - "this descriptor is used for I/O and the INOPUT/OUTPUT descriptors\n" - "are not touched."; -static gpg_error_t -cmd_cat (assuan_context_t ctx, char *line) -{ - state_t state = assuan_get_pointer (ctx); - gpg_error_t err = 0; - assuan_fd_t fd_in = ASSUAN_INVALID_FD; - assuan_fd_t fd_out = ASSUAN_INVALID_FD; - FILE *fp_in = NULL; - char buf[256]; - size_t nread; - int use_dataport = 0; - - if (*line) - { - fp_in = fopen (line, "rb"); - if (!fp_in) - err = gpg_error_from_syserror (); - else - fd_out = assuan_get_output_fd (ctx); - } - else if (state->dataport_fd != ASSUAN_INVALID_FD) - { - use_dataport = 1; - fd_in = state->dataport_fd; - fd_out = state->dataport_fd; - } - else if ((fd_in = assuan_get_input_fd (ctx)) != ASSUAN_INVALID_FD) - { - /* This FD is actually a socket descriptor. We can't fdopen it - because under Windows we ust use recv(2) instead of read(2). - Note that on POSIX systems there is no difference between - libc file descriptors and socket descriptors. */ - - fd_out = assuan_get_output_fd (ctx); - } - else - err = gpg_error (GPG_ERR_ASS_NO_INPUT); - if (err) - goto leave; - - do - { - if (fp_in) - { - nread = fread (buf, 1, sizeof buf, fp_in); - if (nread < sizeof buf) - { - if (ferror (fp_in)) - err = gpg_error_from_syserror (); - else if (feof (fp_in)) - err = gpg_error (GPG_ERR_EOF); - } - } - else - { - int n; - - nread = 0; - n = my_read (fd_in, buf, sizeof buf); - if (n < 0) - err = gpg_error_from_syserror (); - else if (!n) - err = gpg_error (GPG_ERR_EOF); - else - nread = n; - } - - - if (fd_out != ASSUAN_INVALID_FD) - { - if (nread && my_writen (fd_out, buf, nread)) - err = gpg_error_from_syserror (); - } - else if (nread) - err = assuan_send_data (ctx, buf, nread); - } - while (!err); - if (gpg_err_code (err) == GPG_ERR_EOF) - err = 0; - -leave: - if (fp_in) - fclose (fp_in); - if (use_dataport) - { - if (state->dataport_fd != ASSUAN_INVALID_FD) - { - assuan_sock_close (state->dataport_fd); - state->dataport_fd = ASSUAN_INVALID_FD; - } - } - else - { - assuan_close_input_fd (ctx); - assuan_close_output_fd (ctx); - } - return leave_cmd (ctx, err); -} - - -static const char hlp_pwd[] = - "PWD\n" - "\n" - "Print the curent working directory of this session.\n"; -static gpg_error_t -cmd_pwd (assuan_context_t ctx, char *line) -{ - state_t state = assuan_get_pointer (ctx); - gpg_error_t err; - const char *string; - - string = get_cwd (state); - err = assuan_send_data (ctx, string, strlen (string)); - - return leave_cmd (ctx, err); -} - - -static const char hlp_cd[] = - "CD [dir]\n" - "\n" - "Change the curretn directory of the session.\n"; -static gpg_error_t -cmd_cd (assuan_context_t ctx, char *line) -{ - state_t state = assuan_get_pointer (ctx); - gpg_error_t err = 0; - char *newdir, *p; - - for (p=line; *p; p++) - if (*p == '\\') - *p = '/'; - - if (!*line) - { - xfree (state->cwd); - state->cwd = NULL; - get_cwd (state); - } - else - { - if (*line == '/') - newdir = xstrdup (line); - else - newdir = xstrconcat (get_cwd (state), "/", line, NULL); - - while (strlen(newdir) > 1 && line[strlen(newdir)-1] == '/') - line[strlen(newdir)-1] = 0; - xfree (state->cwd); - state->cwd = newdir; - } - - return leave_cmd (ctx, err); -} - - - - - -#ifdef HAVE_W32CE_SYSTEM -static const char hlp_ls[] = - "LS []\n" - "\n" - "List the files described by PATTERN.\n"; -static gpg_error_t -cmd_ls (assuan_context_t ctx, char *line) -{ - state_t state = assuan_get_pointer (ctx); - gpg_error_t err; - WIN32_FIND_DATA fi; - char buf[500]; - HANDLE hd; - char *p, *fname; - wchar_t *wfname; - - if (!*line) - fname = xstrconcat (get_cwd (state), "/*", NULL); - else if (*line == '/' || *line == '\\') - fname = xstrdup (line); - else - fname = xstrconcat (get_cwd (state), "/", line, NULL); - for (p=fname; *p; p++) - if (*p == '/') - *p = '\\'; - assuan_write_status (ctx, "PATTERN", fname); - wfname = utf8_to_wchar (fname); - xfree (fname); - hd = FindFirstFile (wfname, &fi); - free (wfname); - if (hd == INVALID_HANDLE_VALUE) - { - log_info ("FindFirstFile returned %d\n", GetLastError ()); - err = gpg_error_from_syserror (); /* Works for W32CE. */ - goto leave; - } - - do - { - DWORD attr = fi.dwFileAttributes; - - fname = wchar_to_utf8 (fi.cFileName); - snprintf (buf, sizeof buf, - "%c%c%c%c%c%c%c%c%c%c%c%c%c %7lu%c %s\n", - (attr & FILE_ATTRIBUTE_DIRECTORY) - ? ((attr & FILE_ATTRIBUTE_DEVICE)? 'c':'d'):'-', - (attr & FILE_ATTRIBUTE_READONLY)? 'r':'-', - (attr & FILE_ATTRIBUTE_HIDDEN)? 'h':'-', - (attr & FILE_ATTRIBUTE_SYSTEM)? 's':'-', - (attr & FILE_ATTRIBUTE_ARCHIVE)? 'a':'-', - (attr & FILE_ATTRIBUTE_COMPRESSED)? 'c':'-', - (attr & FILE_ATTRIBUTE_ENCRYPTED)? 'e':'-', - (attr & FILE_ATTRIBUTE_INROM)? 'R':'-', - (attr & FILE_ATTRIBUTE_REPARSE_POINT)? 'P':'-', - (attr & FILE_ATTRIBUTE_ROMMODULE)? 'M':'-', - (attr & FILE_ATTRIBUTE_ROMSTATICREF)? 'R':'-', - (attr & FILE_ATTRIBUTE_SPARSE_FILE)? 'S':'-', - (attr & FILE_ATTRIBUTE_TEMPORARY)? 't':'-', - (unsigned long)fi.nFileSizeLow, - fi.nFileSizeHigh? 'X':' ', - fname); - free (fname); - err = assuan_send_data (ctx, buf, strlen (buf)); - if (!err) - err = assuan_send_data (ctx, NULL, 0); - } - while (!err && FindNextFile (hd, &fi)); - if (err) - ; - else if (GetLastError () == ERROR_NO_MORE_FILES) - err = 0; - else - { - log_info ("FindNextFile returned %d\n", GetLastError ()); - err = gpg_error_from_syserror (); - } - FindClose (hd); - - leave: - return leave_cmd (ctx, err); -} -#endif /*HAVE_W32CE_SYSTEM*/ - - -#ifdef HAVE_W32CE_SYSTEM -static const char hlp_run[] = - "RUN []\n" - "\n" - "Run the program in FILENAME with the arguments ARGS.\n" - "This creates a new process and waits for it to finish.\n" - "FIXME: The process' stdin is connected to the file set by the\n" - "INPUT command; stdout and stderr to the one set by OUTPUT.\n"; -static gpg_error_t -cmd_run (assuan_context_t ctx, char *line) -{ - /* state_t state = assuan_get_pointer (ctx); */ - gpg_error_t err; - BOOL w32ret; - PROCESS_INFORMATION pi = { NULL, 0, 0, 0 }; - char *p; - wchar_t *pgmname = NULL; - wchar_t *cmdline = NULL; - int code; - DWORD exc; - int idx; - struct { - HANDLE hd[2]; - int oldname_valid; - wchar_t oldname[MAX_PATH]; - } pipes[3]; - - for (idx=0; idx < 3; idx++) - { - pipes[idx].hd[0] = pipes[idx].hd[1] = INVALID_HANDLE_VALUE; - pipes[idx].oldname_valid = 0; - } - - p = strchr (line, ' '); - if (p) - { - *p = 0; - pgmname = utf8_to_wchar (line); - for (p++; *p && *p == ' '; p++) - ; - cmdline = utf8_to_wchar (p); - } - else - pgmname = utf8_to_wchar (line); - { - char *tmp1 = wchar_to_utf8 (pgmname); - char *tmp2 = wchar_to_utf8 (cmdline); - log_info ("CreateProcess, path=`%s' cmdline=`%s'\n", tmp1, tmp2); - xfree (tmp2); - xfree (tmp1); - } - - /* Redirect the standard handles. */ - /* Create pipes. */ - for (idx=0; idx < 3; idx++) - { - if (!_assuan_w32ce_create_pipe (&pipes[idx].hd[0], &pipes[idx].hd[1], - NULL, 0)) - { - err = gpg_error_from_syserror (); - log_error ("CreatePipe failed: %d\n", GetLastError ()); - pipes[idx].hd[0] = pipes[idx].hd[1] = INVALID_HANDLE_VALUE; - goto leave; - } - } - - /* Save currently assigned devices. */ - for (idx=0; idx < 3; idx++) - { - DWORD dwlen = MAX_PATH; - if (!GetStdioPathW (idx, pipes[idx].oldname, &dwlen)) - { - err = gpg_error_from_syserror (); - log_error ("GetStdioPath failed: %d\n", GetLastError ()); - goto leave; - } - pipes[idx].oldname_valid = 1; - } - - /* Connect the pipes. */ - { - if (!SetStdioPathW (1, L"\\mystdout.log")) - { - err = gpg_error_from_syserror (); - log_error ("SetStdioPathW(%d) failed: %d\n", idx, GetLastError ()); - goto leave; - } - if (!SetStdioPathW (2, L"\\mystderr.log")) - { - err = gpg_error_from_syserror (); - log_error ("SetStdioPathW(%d) failed: %d\n", idx, GetLastError ()); - goto leave; - } - } - - /* Create the process, restore the devices and check the error. */ - w32ret = CreateProcess (pgmname, /* Program to start. */ - cmdline, /* Command line arguments. */ - NULL, /* Process security. Not used. */ - NULL, /* Thread security. Not used. */ - FALSE, /* Inherit handles. Not used. */ - CREATE_SUSPENDED, /* Creation flags. */ - NULL, /* Environment. Not used. */ - NULL, /* Use current dir. Not used. */ - NULL, /* Startup information. Not used. */ - &pi /* Returns process information. */ - ); - for (idx=0; idx < 3; idx++) - { - if (pipes[idx].oldname_valid) - { - if (!SetStdioPathW (idx, pipes[idx].oldname)) - log_error ("SetStdioPath(%d) failed during restore: %d\n", - idx, GetLastError ()); - else - pipes[idx].oldname_valid = 0; - } - } - if (!w32ret) - { - /* Error checking after restore so that the messages are visible. */ - log_error ("CreateProcess failed: %d\n", GetLastError ()); - err = gpg_error_from_syserror (); - goto leave; - } - - log_info ("CreateProcess ready: hProcess=%p hThread=%p" - " dwProcessID=%d dwThreadId=%d\n", - pi.hProcess, pi.hThread, - (int) pi.dwProcessId, (int) pi.dwThreadId); - - ResumeThread (pi.hThread); - CloseHandle (pi.hThread); - - code = WaitForSingleObject (pi.hProcess, INFINITE); - switch (code) - { - case WAIT_FAILED: - err = gpg_error_from_syserror ();; - log_error ("waiting for process %d to terminate failed: %d\n", - (int)pi.dwProcessId, GetLastError ()); - break; - - case WAIT_OBJECT_0: - if (!GetExitCodeProcess (pi.hProcess, &exc)) - { - err = gpg_error_from_syserror ();; - log_error ("error getting exit code of process %d: %s\n", - (int)pi.dwProcessId, GetLastError () ); - } - else if (exc) - { - log_info ("error running process: exit status %d\n", (int)exc); - err = gpg_error (GPG_ERR_GENERAL); - } - else - { - err = 0; - } - break; - - default: - err = gpg_error_from_syserror ();; - log_error ("WaitForSingleObject returned unexpected " - "code %d for pid %d\n", code, (int)pi.dwProcessId); - break; - } - CloseHandle (pi.hProcess); - - leave: - for (idx=0; idx < 3; idx++) - { - if (pipes[idx].oldname_valid) - { - if (!SetStdioPathW (idx, pipes[idx].oldname)) - log_error ("SetStdioPath(%d) failed during restore: %d\n", - idx, GetLastError ()); - else - pipes[idx].oldname_valid = 0; - } - } - for (idx=0; idx < 3; idx++) - { - if (pipes[idx].hd[0] != INVALID_HANDLE_VALUE) - CloseHandle (pipes[idx].hd[0]); - if (pipes[idx].hd[1] != INVALID_HANDLE_VALUE) - CloseHandle (pipes[idx].hd[1]); - } - xfree (cmdline); - xfree (pgmname); - return leave_cmd (ctx, err); -} -#endif /*HAVE_W32CE_SYSTEM*/ - - - - - -static const char hlp_newdataport[] = - "NEWDATAPORT\n" - "\n" - "Create a new dataport. The server creates a listening socket and\n" - "issues the inquiry:\n" - " INQUIRE CONNECT-TO \n" - "The client is expected to connect to PORT of the server and confirm\n" - "this by sending just an \"END\". In turn the server sends:\n" - " S FDINFO \n" - "With N being the local descriptor for the accepted connection. This\n" - "descriptor may now be used with INPUT or OUTPUT commands."; -struct cmd_dataport_locals -{ - assuan_context_t ctx; - int passthru; - int port; -}; -static gpg_error_t -cmd_newdataport (assuan_context_t ctx, char *line) -{ - state_t state = assuan_get_pointer (ctx); - gpg_error_t err = 0; - struct sockaddr_in addr; - socklen_t addrlen; - struct cmd_dataport_locals *cont; - char inqline[100]; - - cont = xmalloc (sizeof *cont); - cont->ctx = ctx; - cont->passthru = 0; - cont->port = 0; - - if (state->dataport_listen_fd != ASSUAN_INVALID_FD) - { - log_error ("Oops, still listening on a dataport socket\n"); - state->dataport_listen_fd = ASSUAN_INVALID_FD; - } - if (state->dataport_accepted_fd != ASSUAN_INVALID_FD) - { - log_error ("Oops, still holding an accepted dataport socket\n"); - state->dataport_accepted_fd = ASSUAN_INVALID_FD; - } - state->dataport_accept_err = 0; - - state->dataport_listen_fd = assuan_sock_new (PF_INET, SOCK_STREAM, 0); - if (state->dataport_listen_fd == ASSUAN_INVALID_FD) - { - err = gpg_error_from_syserror (); - log_error ("socket() failed: %s\n", strerror (errno)); - goto leave; - } - - addr.sin_family = AF_INET; - addr.sin_port = 0; - addr.sin_addr.s_addr = htonl (INADDR_ANY); - if (assuan_sock_bind (state->dataport_listen_fd, - (struct sockaddr *)&addr, sizeof addr)) - { - err = gpg_error_from_syserror (); - log_error ("listen() failed: %s\n", strerror (errno)); - goto leave; - } - - if (listen (HANDLE2SOCKET (state->dataport_listen_fd), 1)) - { - err = gpg_error_from_syserror (); - log_error ("listen() failed: %s\n", strerror (errno)); - goto leave; - } - - addrlen = sizeof addr; - if (getsockname (HANDLE2SOCKET (state->dataport_listen_fd), - (struct sockaddr *)&addr, &addrlen)) - { - err = gpg_error_from_syserror (); - log_error ("getsockname() failed: %s\n", strerror (errno)); - goto leave; - } - cont->port = ntohs (addr.sin_port); - - if (verbose) - log_info ("server now also listening on port %d\n", cont->port); - snprintf (inqline, sizeof inqline, "CONNECT-TO %d", cont->port); - err = assuan_inquire_ext (ctx, inqline, 0, cmd_newdataport_cont, cont); - if (!err) - return 0; /* Transfer to continuation. */ - - leave: - cont->passthru = 1; - return cmd_newdataport_cont (cont, err, NULL, 0); -} - -/* Continuation used by cmd_newdataport. */ -static gpg_error_t -cmd_newdataport_cont (void *opaque, gpg_error_t err, - unsigned char *data, size_t datalen) -{ - struct cmd_dataport_locals *cont = opaque; - assuan_context_t ctx = cont->ctx; - state_t state = assuan_get_pointer (ctx); - char numbuf[35]; - fdinfo_t fi; - - if (cont->passthru || err) - goto leave; - - err = state->dataport_accept_err; - if (err) - goto leave; - if (state->dataport_listen_fd != ASSUAN_INVALID_FD - || state->dataport_accepted_fd == ASSUAN_INVALID_FD) - { - err = gpg_error (GPG_ERR_MISSING_ACTION); - goto leave; - } - - for (fi = state->dataport_fds; fi; fi = fi->next) - if (fi->fd == ASSUAN_INVALID_FD) - break; - if (!fi) - { - fi = xcalloc (1, sizeof *fi); - fi->next = state->dataport_fds; - state->dataport_fds = fi; - } - fi->fd = state->dataport_accepted_fd; - state->dataport_accepted_fd = ASSUAN_INVALID_FD; - - /* Note that under Windows the FD is the socket descriptor. Socket - descriptors are neither handles nor libc file descriptors. */ - snprintf (numbuf, sizeof numbuf, "%d", HANDLE2SOCKET (fi->fd)); - err = assuan_write_status (ctx, "FDINFO", numbuf); - - leave: - if (state->dataport_listen_fd != ASSUAN_INVALID_FD) - { - assuan_sock_close (state->dataport_listen_fd); - state->dataport_listen_fd = ASSUAN_INVALID_FD; - } - if (state->dataport_accepted_fd != ASSUAN_INVALID_FD) - { - assuan_sock_close (state->dataport_accepted_fd); - state->dataport_accepted_fd = ASSUAN_INVALID_FD; - } - xfree (cont); - return leave_cmd (ctx, err); -} - - - -static const char hlp_dataport[] = - "DATAPORT FD[=]\n" - "\n" - "Set the file descriptor to read and write data via port.\n" - "This is similar to the \"INPUT\" and \"OUTPUT\" commands\n" - "but useful for socketpairs."; -static gpg_error_t -cmd_dataport (assuan_context_t ctx, char *line) -{ - state_t state = assuan_get_pointer (ctx); - gpg_error_t err; - assuan_fd_t fd; - - if (state->dataport_fd != ASSUAN_INVALID_FD) - { - assuan_sock_close (state->dataport_fd); - state->dataport_fd = ASSUAN_INVALID_FD; - } - - err = assuan_command_parse_fd (ctx, line, &fd); - if (!err && fd != ASSUAN_INVALID_FD) - { - fdinfo_t fi; - - state->dataport_fd = fd; - - /* The fd is now in use use - remove it from the unused list. */ - for (fi=state->dataport_fds; fi; fi = fi->next) - if (fi->fd == fd) - fi->fd = ASSUAN_INVALID_FD; - } - - return leave_cmd (ctx, err); -} - - - -static const char hlp_getinfo[] = - "GETINFO \n" - "\n" - "Multipurpose function to return a variety of information.\n" - "Supported values for WHAT are:\n" - "\n" - " version - Return the version of the program.\n" - " pid - Return the process id of the server.\n" - " dataports - Return a list of usused dataports."; -static gpg_error_t -cmd_getinfo (assuan_context_t ctx, char *line) -{ - state_t state = assuan_get_pointer (ctx); - gpg_error_t err = 0; - char numbuf[50]; - - if (!strcmp (line, "version")) - { - const char *s = VERSION; - err = assuan_send_data (ctx, s, strlen (s)); - } - else if (!strcmp (line, "pid")) - { - snprintf (numbuf, sizeof numbuf, "%lu", (unsigned long)getpid ()); - err = assuan_send_data (ctx, numbuf, strlen (numbuf)); - } - else if (!strcmp (line, "dataports")) - { - fdinfo_t fi; - int any = 0; - - for (fi=state->dataport_fds; !err && fi; fi = fi->next) - { - if (fi->fd != ASSUAN_INVALID_FD) - { - snprintf (numbuf, sizeof numbuf, "%s%d", - any? " ":"", HANDLE2SOCKET (fi->fd)); - any = 1; - err = assuan_send_data (ctx, numbuf, strlen (numbuf)); - } - } - } - else - err = gpg_error (GPG_ERR_ASS_PARAMETER); - - return leave_cmd (ctx, err); -} - - - -static const char hlp_shutdown[] = - "SHUTDOWN\n" - "\n" - "Shutdown the server process\n"; -static gpg_error_t -cmd_shutdown (assuan_context_t ctx, char *line) -{ - (void)line; - shutdown_pending = 1; - return leave_cmd (ctx, 0);; -} - - -static gpg_error_t -register_commands (assuan_context_t ctx) -{ - static struct - { - const char *name; - gpg_error_t (*handler) (assuan_context_t, char *line); - const char * const help; - } table[] = - { -#ifdef HAVE_W32CE_SYSTEM - { "LS", cmd_ls, hlp_ls }, - { "RUN", cmd_run, hlp_run }, -#endif - { "PWD", cmd_pwd, hlp_pwd }, - { "CD", cmd_cd, hlp_cd }, - { "ECHO", cmd_echo, hlp_echo }, - { "CAT", cmd_cat, hlp_cat }, - { "NEWDATAPORT", cmd_newdataport, hlp_newdataport }, - { "DATAPORT", cmd_dataport, hlp_dataport }, - { "INPUT", NULL }, - { "OUTPUT", NULL }, - { "GETINFO", cmd_getinfo, hlp_getinfo }, - { "SHUTDOWN", cmd_shutdown, hlp_shutdown }, - { NULL, NULL } - }; - int i; - gpg_error_t rc; - - for (i=0; table[i].name; i++) - { - rc = assuan_register_command (ctx, table[i].name, - table[i].handler, table[i].help); - if (rc) - return rc; - } - return 0; -} - - - -static assuan_fd_t -get_connection_fd (assuan_context_t ctx) -{ - assuan_fd_t fds[5]; - - if (assuan_get_active_fds (ctx, 0, fds, DIM (fds)) < 1) - log_fatal ("assuan_get_active_fds failed\n"); - if (fds[0] == ASSUAN_INVALID_FD) - log_fatal ("assuan_get_active_fds returned invalid conenction fd\n"); - return fds[0]; -} - - -/* Startup the server. */ -static void -server (void) -{ - gpg_error_t err; - assuan_fd_t server_fd; - assuan_sock_nonce_t server_nonce; - int one = 1; - struct sockaddr_in name; - assuan_context_t ctx; - state_t state = NULL; - - err = assuan_new (&ctx); - if (err) - log_fatal ("assuan_new failed: %s\n", gpg_strerror (err)); - - server_fd = assuan_sock_new (PF_INET, SOCK_STREAM, 0); - if (server_fd == ASSUAN_INVALID_FD) - log_fatal ("socket() failed: %s\n", strerror (errno)); - - if (setsockopt (HANDLE2SOCKET (server_fd), - SOL_SOCKET, SO_REUSEADDR, (void*)&one, sizeof one)) - log_error ("setsockopt(SO_REUSEADDR) failed: %s\n", strerror (errno)); - - name.sin_family = AF_INET; - name.sin_port = htons (server_port); - name.sin_addr.s_addr = htonl (INADDR_ANY); - if (assuan_sock_bind (server_fd, (struct sockaddr *) &name, sizeof name)) - log_fatal ("bind() failed: %s\n", strerror (errno)); - if (assuan_sock_get_nonce ((struct sockaddr*)&name, sizeof name, - &server_nonce)) - log_fatal ("assuan_sock_get_nonce failed: %s\n", strerror (errno)); - - /* Register the nonce with the context so that assuan_accept knows - about it. We can't do that directly in assuan_sock_bind because - we want these socket wrappers to be context neutral and drop in - replacement for the standard socket functions. */ - assuan_set_sock_nonce (ctx, &server_nonce); - - if (listen (HANDLE2SOCKET (server_fd), 5)) - log_fatal ("listen() failed: %s\n", strerror (errno)); - - log_info ("server listening on port %hd\n", server_port); - - err = assuan_init_socket_server (ctx, server_fd, 0); - if (err) - log_fatal ("assuan_init_socket_server failed: %s\n", gpg_strerror (err)); - - err = register_commands (ctx); - if (err) - log_fatal ("register_commands failed: %s\n", gpg_strerror(err)); - - if (debug) - assuan_set_log_stream (ctx, stderr); - - assuan_register_reset_notify (ctx, reset_notify); - assuan_register_input_notify (ctx, input_notify); - assuan_register_output_notify (ctx, output_notify); - - - state = new_state (); - - assuan_set_pointer (ctx, state); - - while (!shutdown_pending) - { - int done; - - err = assuan_accept (ctx); - if (err) - { - if (gpg_err_code (err) == GPG_ERR_EOF || err == -1) - log_error ("assuan_accept failed: %s\n", gpg_strerror (err)); - break; - } - - log_info ("client connected. Client's pid is %ld\n", - (long)assuan_get_pid (ctx)); - do - { - /* We need to use select here so that we can accept - supplemental connections from the client as requested by - the DATAPORT command. */ - fd_set rfds; - int connfd, datafd, max_fd; - - connfd = HANDLE2SOCKET (get_connection_fd (ctx)); - FD_ZERO (&rfds); - FD_SET (connfd, &rfds); - max_fd = connfd; - - if (state->dataport_listen_fd != ASSUAN_INVALID_FD) - { - datafd = HANDLE2SOCKET (state->dataport_listen_fd); - FD_SET (datafd, &rfds); - if (datafd > max_fd) - max_fd = datafd; - } - else - datafd = -1; - - if (select (max_fd + 1, &rfds, NULL, NULL, NULL) > 0) - { - if (datafd != -1 && FD_ISSET (datafd, &rfds)) - { - struct sockaddr_in clnt_addr; - socklen_t len = sizeof clnt_addr; - int fd; - - fd = accept (datafd, (struct sockaddr*)&clnt_addr, &len); - if (fd == -1) - { - err = gpg_err_code_from_syserror (); - assuan_sock_close (state->dataport_listen_fd); - state->dataport_listen_fd = ASSUAN_INVALID_FD; - log_error ("accepting on dataport failed: %s\n", - gpg_strerror (err)); - state->dataport_accept_err = err; - err = 0; - } - else - { - /* No more need for the listening socket. */ - assuan_sock_close (state->dataport_listen_fd); - state->dataport_listen_fd = ASSUAN_INVALID_FD; - /* Record the accepted fd. */ - state->dataport_accept_err = 0; - state->dataport_accepted_fd = SOCKET2HANDLE (fd); - } - } - - if (FD_ISSET (connfd, &rfds)) - { - err = assuan_process_next (ctx, &done); - } - } - } - while (!err && !done && !shutdown_pending); - if (err) - log_error ("assuan_process failed: %s\n", gpg_strerror (err)); - } - - assuan_sock_close (server_fd); - assuan_release (ctx); - release_state (state); -} - - - - - -/* - - M A I N - -*/ -int -main (int argc, char **argv) -{ - gpg_error_t err; - int last_argc = -1; - - if (argc) - { - log_set_prefix (*argv); - argc--; argv++; - } - while (argc && last_argc != argc ) - { - last_argc = argc; - if (!strcmp (*argv, "--help")) - { - printf ( - "usage: %s [options]\n" - "\n" - "Options:\n" - " --verbose Show what is going on\n", - log_get_prefix ()); - exit (0); - } - if (!strcmp (*argv, "--verbose")) - { - verbose = 1; - argc--; argv++; - } - else if (!strcmp (*argv, "--debug")) - { - verbose = debug = 1; - argc--; argv++; - } - } - - assuan_set_assuan_log_prefix (log_prefix); - if (debug) - assuan_set_assuan_log_stream (stderr); - - err = assuan_sock_init (); - if (err) - log_fatal ("assuan_sock_init failed: %s\n", gpg_strerror (err)); - - log_info ("server starting...\n"); - server (); - log_info ("server finished\n"); - - assuan_sock_deinit (); - - return errorcount ? 1 : 0; -} diff --git a/tests/common.h b/tests/common.h index c5ce811..dc3c073 100644 --- a/tests/common.h +++ b/tests/common.h @@ -1,289 +1,284 @@ /* common.h - Common functions for the tests. * Copyright (C) 2006 Free Software Foundation, Inc. * * This file is part of Assuan. * * Assuan is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as * published by the Free Software Foundation; either version 3 of * the License, or (at your option) any later version. * * Assuan 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 * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this program; if not, see . */ #include #if __GNUC__ >= 4 # define MY_GCC_A_SENTINEL(a) __attribute__ ((sentinel(a))) #else # define MY_GCC_A_SENTINEL(a) #endif -#ifdef HAVE_W32CE_SYSTEM -#define getpid() GetCurrentProcessId () -#define getenv(a) (NULL) -#endif - #if HAVE_W32_SYSTEM #define SOCKET2HANDLE(s) ((void *)(s)) #define HANDLE2SOCKET(h) ((unsigned int)(h)) CRITICAL_SECTION _log_critsect; #define _log_enter() do { EnterCriticalSection (&_log_critsect); } while (0) #define _log_leave() do { LeaveCriticalSection (&_log_critsect); } while (0) #else #define SOCKET2HANDLE(s) (s) #define HANDLE2SOCKET(h) (h) #define _log_enter() do { } while (0) #define _log_leave() do { } while (0) #endif #define DIM(v) (sizeof(v)/sizeof((v)[0])) #define DIMof(type,member) DIM(((type *)0)->member) char *xstrconcat (const char *s1, ...) MY_GCC_A_SENTINEL(0); static const char *log_prefix; static int errorcount; static int verbose; static int debug; void * xmalloc (size_t n) { char *p = malloc (n); if (!p) { if (log_prefix) fprintf (stderr, "%s[%u]: ", log_prefix, (unsigned int)getpid ()); fprintf (stderr, "out of core\n"); exit (1); } return p; } void * xcalloc (size_t n, size_t m) { char *p = calloc (n, m); if (!p) { _log_enter (); if (log_prefix) fprintf (stderr, "%s[%u]: ", log_prefix, (unsigned int)getpid ()); fprintf (stderr, "out of core\n"); _log_leave (); exit (1); } return p; } void xfree (void *a) { if (a) free (a); } void * xstrdup (const char *string) { char *p = xmalloc (strlen (string) + 1); strcpy (p, string); return p; } void log_set_prefix (const char *s) { #ifdef HAVE_W32_SYSTEM InitializeCriticalSection (&_log_critsect); log_prefix = strrchr (s, '\\'); #else log_prefix = strrchr (s, '/'); #endif if (log_prefix) log_prefix++; else log_prefix = s; } const char * log_get_prefix (void) { return log_prefix? log_prefix:""; } void log_info (const char *format, ...) { va_list arg_ptr ; if (!verbose) return; va_start (arg_ptr, format) ; _log_enter (); if (log_prefix) fprintf (stderr, "%s[%u]: ", log_prefix, (unsigned int)getpid ()); vfprintf (stderr, format, arg_ptr ); _log_leave (); va_end (arg_ptr); } void log_error (const char *format, ...) { va_list arg_ptr ; va_start (arg_ptr, format) ; _log_enter (); if (log_prefix) fprintf (stderr, "%s[%u]: ", log_prefix, (unsigned int)getpid ()); vfprintf (stderr, format, arg_ptr ); _log_leave (); va_end (arg_ptr); errorcount++; } void log_fatal (const char *format, ...) { va_list arg_ptr ; va_start (arg_ptr, format) ; _log_enter (); if (log_prefix) fprintf (stderr, "%s[%u]: ", log_prefix, (unsigned int)getpid ()); vfprintf (stderr, format, arg_ptr ); _log_leave (); va_end (arg_ptr); exit (2); } void log_printhex (const char *text, const void *buffer, size_t length) { const unsigned char *s; _log_enter (); if (log_prefix) fprintf (stderr, "%s[%u]: ", log_prefix, (unsigned int)getpid ()); fputs (text, stderr); for (s=buffer; length; s++, length--) fprintf (stderr, "%02X", *s); putc ('\n', stderr); _log_leave (); } /* Prepend FNAME with the srcdir environment variable's value and return an allocated filename. */ char * prepend_srcdir (const char *fname) { static const char *srcdir; char *result; if (!srcdir && !(srcdir = getenv ("srcdir"))) srcdir = "."; result = xmalloc (strlen (srcdir) + 1 + strlen (fname) + 1); strcpy (result, srcdir); strcat (result, "/"); strcat (result, fname); return result; } #ifndef HAVE_STPCPY #undef __stpcpy #undef stpcpy #ifndef weak_alias # define __stpcpy stpcpy #endif char * __stpcpy (char *a,const char *b) { while (*b) *a++ = *b++; *a = 0; return (char*)a; } #ifdef libc_hidden_def libc_hidden_def (__stpcpy) #endif #ifdef weak_alias weak_alias (__stpcpy, stpcpy) #endif #ifdef libc_hidden_builtin_def libc_hidden_builtin_def (stpcpy) #endif #endif static char * do_strconcat (const char *s1, va_list arg_ptr) { const char *argv[48]; size_t argc; size_t needed; char *buffer, *p; argc = 0; argv[argc++] = s1; needed = strlen (s1); while (((argv[argc] = va_arg (arg_ptr, const char *)))) { needed += strlen (argv[argc]); if (argc >= DIM (argv)-1) { fprintf (stderr, "too many args in strconcat\n"); exit (1); } argc++; } needed++; buffer = xmalloc (needed); if (buffer) { for (p = buffer, argc=0; argv[argc]; argc++) p = stpcpy (p, argv[argc]); } return buffer; } /* Concatenate the string S1 with all the following strings up to a NULL. Returns a malloced buffer or dies on malloc error. */ char * xstrconcat (const char *s1, ...) { va_list arg_ptr; char *result; if (!s1) result = xstrdup (""); else { va_start (arg_ptr, s1); result = do_strconcat (s1, arg_ptr); va_end (arg_ptr); } return result; } diff --git a/tests/pipeconnect.c b/tests/pipeconnect.c index ddb4a06..b477af1 100644 --- a/tests/pipeconnect.c +++ b/tests/pipeconnect.c @@ -1,397 +1,347 @@ /* pipeconnect.c - Check the assuan_pipe_connect call. Copyright (C) 2006, 2009, 2010 Free Software Foundation, Inc. This file is part of Assuan. Assuan is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 3 of the License, or (at your option) any later version. Assuan 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 Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this program; if not, see . */ /* This tests creates a program which starts an assuan server and runs some simple tests on it. The other program is actually the same program but called with the option --server. */ #ifdef HAVE_CONFIG_H #include #endif #include #include #include #include #include #include "../src/assuan.h" #include "common.h" static assuan_fd_t my_stdin = ASSUAN_INVALID_FD; static assuan_fd_t my_stdout = ASSUAN_INVALID_FD; static assuan_fd_t my_stderr = ASSUAN_INVALID_FD; static gpg_error_t cmd_echo (assuan_context_t ctx, char *line) { log_info ("got ECHO command (%s)\n", line); assuan_send_data (ctx, line, strlen (line)); return 0; } static gpg_error_t cmd_cat (assuan_context_t ctx, char *line) { assuan_fd_t fd, fdout; int c; FILE *fp, *fpout; int nbytes; log_info ("got CAT command (%s)\n", line); fd = assuan_get_input_fd (ctx); if (fd == ASSUAN_INVALID_FD) return gpg_error (GPG_ERR_ASS_NO_INPUT); fdout = assuan_get_output_fd (ctx); if (fdout == ASSUAN_INVALID_FD) return gpg_error (GPG_ERR_ASS_NO_OUTPUT); fp = fdopen (fd, "r"); if (!fp) { log_error ("fdopen failed on input fd: %s\n", strerror (errno)); return gpg_error (GPG_ERR_ASS_GENERAL); } fpout = fdopen (fdout, "w"); if (!fpout) { log_error ("fdopen failed on output fd: %s\n", strerror (errno)); fclose (fp); return gpg_error (GPG_ERR_ASS_GENERAL); } nbytes = 0; while ( (c=getc (fp)) != -1) { putc (c, fpout); nbytes++; } log_info ("done printing %d bytes to output fd\n", nbytes); /* Fixme: This also closes the original fd. */ fclose (fp); fclose (fpout); return 0; } static gpg_error_t server_register_commands (assuan_context_t ctx) { static struct { const char *name; gpg_error_t (*handler) (assuan_context_t, char *line); } table[] = { { "ECHO", cmd_echo }, { "CAT", cmd_cat }, { "INPUT", NULL }, { "OUTPUT", NULL }, { NULL, NULL } }; int i; gpg_error_t rc; for (i=0; table[i].name; i++) { rc = assuan_register_command (ctx, table[i].name, table[i].handler, NULL); if (rc) return rc; } return 0; } static void run_server (int enable_debug) { int rc; assuan_context_t ctx; assuan_fd_t filedes[2]; filedes[0] = my_stdin; filedes[1] = my_stdout; rc = assuan_new (&ctx); if (rc) log_fatal ("assuan_new failed: %s\n", gpg_strerror (rc)); rc = assuan_init_pipe_server (ctx, filedes); if (rc) log_fatal ("assuan_init_pipe_server failed: %s\n", gpg_strerror (rc)); rc = server_register_commands (ctx); if (rc) log_fatal ("register_commands failed: %s\n", gpg_strerror(rc)); if (enable_debug) assuan_set_log_stream (ctx, stderr); for (;;) { rc = assuan_accept (ctx); if (rc) { if (rc != -1) log_error ("assuan_accept failed: %s\n", gpg_strerror (rc)); break; } log_info ("client connected. Client's pid is %ld\n", (long)assuan_get_pid (ctx)); rc = assuan_process (ctx); if (rc) log_error ("assuan_process failed: %s\n", gpg_strerror (rc)); } assuan_release (ctx); } static gpg_error_t data_cb (void *opaque, const void *buffer, size_t length) { (void)opaque; if (buffer) printf ("Received data `%.*s'\n", (int)length, (char*)buffer); return 0; } static void run_client (const char *servername) { gpg_error_t err; assuan_context_t ctx; assuan_fd_t no_close_fds[2]; const char *arglist[5]; no_close_fds[0] = fileno (stderr); no_close_fds[1] = ASSUAN_INVALID_FD; arglist[0] = servername; arglist[1] = "--server"; arglist[2] = debug? "--debug" : verbose? "--verbose":NULL; arglist[3] = NULL; err = assuan_new (&ctx); if (err) log_fatal ("assuan_new failed: %s\n", gpg_strerror (err)); err = assuan_pipe_connect (ctx, servername, arglist, no_close_fds, NULL, NULL, 0); if (err) { log_error ("assuan_pipe_connect failed: %s\n", gpg_strerror (err)); assuan_release (ctx); return; } log_info ("server started; pid is %ld\n", (long)assuan_get_pid (ctx)); err = assuan_transact (ctx, "ECHO Your lucky number is 3552664958674928. " "Watch for it everywhere.", data_cb, NULL, NULL, NULL, NULL, NULL); if (err) { log_error ("sending ECHO failed: %s\n", gpg_strerror (err)); return; } err = assuan_transact (ctx, "BYE", NULL, NULL, NULL, NULL, NULL, NULL); if (err) { log_error ("sending BYE failed: %s\n", gpg_strerror (err)); return; } assuan_release (ctx); return; } static void parse_std_file_handles (int *argcp, char ***argvp) { -#ifdef HAVE_W32CE_SYSTEM - int argc = *argcp; - char **argv = *argvp; - const char *s; - assuan_fd_t fd; - int i; - int fixup = 0; - - if (!argc) - return; - - for (argc--, argv++; argc; argc--, argv++) - { - s = *argv; - if (*s == '-' && s[1] == '&' && s[2] == 'S' - && (s[3] == '0' || s[3] == '1' || s[3] == '2') - && s[4] == '=' - && (strchr ("-01234567890", s[5]) || !strcmp (s+5, "null"))) - { - if (s[5] == 'n') - fd = ASSUAN_INVALID_FD; - else - fd = _assuan_w32ce_finish_pipe (atoi (s+5), s[3] != '0'); - switch (s[3] - '0') - { - case 0: my_stdin = fd; break; - case 1: my_stdout = fd; break; - case 2: my_stderr = fd; break; - } - - fixup++; - } - else - break; - } - - if (fixup) - { - argc = *argcp; - argc -= fixup; - *argcp = argc; - - argv = *argvp; - for (i=1; i < argc; i++) - argv[i] = argv[i + fixup]; - for (; i < argc + fixup; i++) - argv[i] = NULL; - } -#else (void)argcp; (void)argvp; my_stdin = 0; my_stdout = 1; my_stderr = 2; -#endif } /* M A I N */ int main (int argc, char **argv) { gpg_error_t err; const char *myname = "no-pgm"; int last_argc = -1; int server = 0; int silent_client = 0; int silent_server = 0; parse_std_file_handles (&argc, &argv); if (argc) { myname = *argv; log_set_prefix (*argv); argc--; argv++; } while (argc && last_argc != argc ) { last_argc = argc; if (!strcmp (*argv, "--help")) { printf ("usage: %s [options]\n" "\n" "Options:\n" " --verbose Show what is going on\n" " --server Run in server mode\n", log_get_prefix ()); exit (0); } if (!strcmp (*argv, "--verbose")) { verbose = 1; argc--; argv++; } else if (!strcmp (*argv, "--debug")) { verbose = debug = 1; argc--; argv++; } else if (!strcmp (*argv, "--server")) { server = 1; argc--; argv++; } else if (!strcmp (*argv, "--silent-server")) { silent_server = 1; argc--; argv++; } else if (!strcmp (*argv, "--silent-client")) { silent_client = 1; argc--; argv++; } else log_fatal ("invalid option `%s' (try --help)\n", *argv); } log_set_prefix (xstrconcat (log_get_prefix (), server? ".server":".client", NULL)); assuan_set_assuan_log_prefix (log_get_prefix ()); err = assuan_sock_init (); if (err) log_fatal ("socket init failed: %s\n", gpg_strerror (err)); if (server) { log_info ("server started\n"); if (debug && !silent_server) assuan_set_assuan_log_stream (stderr); run_server (debug && !silent_server); log_info ("server finished\n"); } else { log_info ("client started\n"); if (debug && !silent_client) assuan_set_assuan_log_stream (stderr); run_client (myname); log_info ("client finished\n"); } return errorcount ? 1 : 0; }