diff --git a/src/assuan-socket-connect.c b/src/assuan-socket-connect.c index 182f9b3..b2f3c76 100644 --- a/src/assuan-socket-connect.c +++ b/src/assuan-socket-connect.c @@ -1,378 +1,378 @@ /* assuan-socket-connect.c - Assuan socket based client * Copyright (C) 2002, 2003, 2004, 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+ */ #include #include #include #include #include #include #ifdef HAVE_STDINT_H # include #endif #ifdef HAVE_UNISTD_H # include #endif #ifdef HAVE_SYS_TYPES_H # include #endif #ifdef HAVE_W32_SYSTEM # ifdef HAVE_WINSOCK2_H # include # endif # include #else # include # include # include # include #endif #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 #ifndef INADDR_NONE #define INADDR_NONE ((unsigned long)(-1)) #endif /*INADDR_NONE*/ #ifndef SUN_LEN # define SUN_LEN(ptr) ((size_t) (((struct sockaddr_un *) 0)->sun_path) \ + strlen ((ptr)->sun_path)) #endif #undef WITH_IPV6 #if defined (AF_INET6) && defined(PF_INET) \ && defined (INET6_ADDRSTRLEN) && defined(HAVE_INET_PTON) # define WITH_IPV6 1 #endif /* Returns true if STR represents a valid port number in decimal * notation and no garbage is following. */ static int parse_portno (const char *str, uint16_t *r_port) { unsigned int value; for (value=0; *str && (*str >= '0' && *str <= '9'); str++) { value = value * 10 + (*str - '0'); if (value > 65535) return 0; } if (*str || !value) return 0; *r_port = value; return 1; } static gpg_error_t _assuan_connect_finalize (assuan_context_t ctx, assuan_fd_t fd, unsigned int flags) { gpg_error_t err; ctx->engine.release = _assuan_client_release; ctx->engine.readfnc = _assuan_simple_read; ctx->engine.writefnc = _assuan_simple_write; ctx->engine.sendfd = NULL; ctx->engine.receivefd = NULL; ctx->finish_handler = _assuan_client_finish; ctx->inbound.fd = fd; ctx->outbound.fd = fd; ctx->max_accepts = -1; #ifdef HAVE_W32_SYSTEM ctx->engine.sendfd = w32_fdpass_send; #else if (flags & ASSUAN_SOCKET_CONNECT_FDPASSING) _assuan_init_uds_io (ctx); #endif /* initial handshake */ { assuan_response_t response; int off; err = _assuan_read_from_server (ctx, &response, &off, 0); if (err) TRACE1 (ctx, ASSUAN_LOG_SYSIO, "assuan_socket_connect", ctx, "can't connect to server: %s\n", gpg_strerror (err)); else if (response == ASSUAN_RESPONSE_OK) { #if defined(HAVE_W32_SYSTEM) const char *line = ctx->inbound.line + off; int process_id = -1; /* Parse the message: OK ..., process %i */ - line = strchr (line, ','); + line = strrchr (line, ','); if (line) { line = strchr (line + 1, ' '); if (line) { line = strchr (line + 1, ' '); if (line) process_id = atoi (line + 1); } } if (process_id != -1) ctx->process_id = process_id; #else ; #endif } else { char *sname = _assuan_encode_c_string (ctx, ctx->inbound.line); if (sname) { TRACE1 (ctx, ASSUAN_LOG_SYSIO, "assuan_socket_connect", ctx, "can't connect to server: %s", sname); _assuan_free (ctx, sname); } err = _assuan_error (ctx, GPG_ERR_ASS_CONNECT_FAILED); } } return err; } /* Attach an existing connected file descriptor FD to an allocated handle CTX * and initialize the connection. */ gpg_error_t assuan_socket_connect_fd (assuan_context_t ctx, assuan_fd_t fd, unsigned int flags) { gpg_error_t err; if (!ctx) return GPG_ERR_INV_ARG; if (fd == ASSUAN_INVALID_FD) return GPG_ERR_INV_ARG; ctx->flags.is_socket = 1; err = _assuan_connect_finalize (ctx, fd, flags); if (err) _assuan_reset (ctx); return err; } /* Make a connection to the Unix domain socket NAME and return a new * Assuan context in CTX. SERVER_PID is currently not used but may * become handy in the future. Defined flag bits are: * * ASSUAN_SOCKET_CONNECT_FDPASSING * sendmsg and recvmsg are used. * * NAME must either start with a slash and optional with a drive * prefix ("c:") or use one of these URL schemata: * * file:// * * This is the same as the default just with an explicit schemata. * * assuan://: * assuan://[]: * * Connect using TCP to PORT of the server with the numerical * IPADDR. Note that '[' and ']' are literal characters. */ gpg_error_t assuan_socket_connect (assuan_context_t ctx, const char *name, pid_t server_pid, unsigned int flags) { gpg_error_t err = 0; assuan_fd_t fd; #ifdef WITH_IPV6 struct sockaddr_in6 srvr_addr_in6; #endif struct sockaddr_un srvr_addr_un; struct sockaddr_in srvr_addr_in; struct sockaddr *srvr_addr = NULL; uint16_t port = 0; size_t len = 0; const char *s; int af = AF_LOCAL; int pf = PF_LOCAL; TRACE2 (ctx, ASSUAN_LOG_CTX, "assuan_socket_connect", ctx, "name=%s, flags=0x%x", name ? name : "(null)", flags); if (!ctx || !name) return _assuan_error (ctx, GPG_ERR_ASS_INV_VALUE); if (!strncmp (name, "file://", 7) && name[7]) name += 7; else if (!strncmp (name, "assuan://", 9) && name[9]) { name += 9; af = AF_INET; pf = PF_INET; } else /* Default. */ { /* We require that the name starts with a slash if no URL schemata is used. To make things easier we allow an optional drive prefix. */ s = name; if (*s && s[1] == ':') s += 2; if (*s != DIRSEP_C && *s != '/') return _assuan_error (ctx, GPG_ERR_ASS_INV_VALUE); } if (af == AF_LOCAL) { int redirected; if (_assuan_sock_set_sockaddr_un (name, (struct sockaddr *)&srvr_addr_un, &redirected)) return _assuan_error (ctx, gpg_err_code_from_syserror ()); len = SUN_LEN (&srvr_addr_un); srvr_addr = (struct sockaddr *)&srvr_addr_un; } else { char *addrstr, *p; #ifdef HAVE_INET_PTON void *addrbuf = NULL; #endif addrstr = _assuan_malloc (ctx, strlen (name) + 1); if (!addrstr) return _assuan_error (ctx, gpg_err_code_from_syserror ()); if (*name == '[') { strcpy (addrstr, name+1); p = strchr (addrstr, ']'); if (!p || p[1] != ':' || !parse_portno (p+2, &port)) err = _assuan_error (ctx, GPG_ERR_BAD_URI); else { *p = 0; #ifdef WITH_IPV6 af = AF_INET6; pf = PF_INET6; memset (&srvr_addr_in6, 0, sizeof srvr_addr_in6); srvr_addr_in6.sin6_family = af; srvr_addr_in6.sin6_port = htons (port); #ifdef HAVE_INET_PTON addrbuf = &srvr_addr_in6.sin6_addr; #endif srvr_addr = (struct sockaddr *)&srvr_addr_in6; len = sizeof srvr_addr_in6; #else err = _assuan_error (ctx, GPG_ERR_EAFNOSUPPORT); #endif } } else { strcpy (addrstr, name); p = strchr (addrstr, ':'); if (!p || !parse_portno (p+1, &port)) err = _assuan_error (ctx, GPG_ERR_BAD_URI); else { *p = 0; memset (&srvr_addr_in, 0, sizeof srvr_addr_in); srvr_addr_in.sin_family = af; srvr_addr_in.sin_port = htons (port); #ifdef HAVE_INET_PTON addrbuf = &srvr_addr_in.sin_addr; #endif srvr_addr = (struct sockaddr *)&srvr_addr_in; len = sizeof srvr_addr_in; } } if (!err) { #ifdef HAVE_INET_PTON switch (inet_pton (af, addrstr, addrbuf)) { case 1: break; case 0: err = _assuan_error (ctx, GPG_ERR_BAD_URI); break; default: err = _assuan_error (ctx, gpg_err_code_from_syserror ()); } #else /*!HAVE_INET_PTON*/ /* We need to use the old function. If we are here v6 support isn't enabled anyway and thus we can do fine without. Note that Windows as a compatible inet_pton function named inetPton, but only since Vista. */ srvr_addr_in.sin_addr.s_addr = inet_addr (addrstr); if (srvr_addr_in.sin_addr.s_addr == INADDR_NONE) err = _assuan_error (ctx, GPG_ERR_BAD_URI); #endif /*!HAVE_INET_PTON*/ } _assuan_free (ctx, addrstr); if (err) return err; } ctx->flags.is_socket = 1; fd = _assuan_sock_new (ctx, pf, SOCK_STREAM, 0); if (fd == ASSUAN_INVALID_FD) { err = _assuan_error (ctx, gpg_err_code_from_syserror ()); TRACE1 (ctx, ASSUAN_LOG_SYSIO, "assuan_socket_connect", ctx, "can't create socket: %s", strerror (errno)); return err; } if (_assuan_sock_connect (ctx, fd, srvr_addr, len) == -1) { TRACE2 (ctx, ASSUAN_LOG_SYSIO, "assuan_socket_connect", ctx, "can't connect to `%s': %s\n", name, strerror (errno)); _assuan_close (ctx, fd); return _assuan_error (ctx, GPG_ERR_ASS_CONNECT_FAILED); } err = _assuan_connect_finalize (ctx, fd, flags); if (err) _assuan_reset (ctx); return err; } diff --git a/tests/fdpassing.c b/tests/fdpassing.c index 438c8c5..eef447f 100644 --- a/tests/fdpassing.c +++ b/tests/fdpassing.c @@ -1,465 +1,466 @@ /* fdpassing - Check the file descriptor passing. Copyright (C) 2006, 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 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 #if HAVE_W32_SYSTEM # define WIN32_LEAN_AND_MEAN # include # include # include #include #else # include # include #endif #include #include #include "../src/assuan.h" #include "common.h" /* S E R V E R */ static gpg_error_t cmd_echo (assuan_context_t ctx, char *line) { int fd; int c; FILE *fp; int nbytes; #ifdef HAVE_W32_SYSTEM HANDLE file_handle; #endif log_info ("got ECHO command (%s)\n", line); #if HAVE_W32_SYSTEM file_handle = assuan_get_input_fd (ctx); if (file_handle == ASSUAN_INVALID_FD) return gpg_error (GPG_ERR_ASS_NO_INPUT); fd = _open_osfhandle ((intptr_t)file_handle, _O_RDONLY); if (fd < 0) return gpg_error (GPG_ERR_ASS_NO_INPUT); #else fd = (int)assuan_get_input_fd (ctx); if (fd == -1) return gpg_error (GPG_ERR_ASS_NO_INPUT); #endif fp = fdopen (fd, "r"); if (!fp) { log_error ("fdopen failed on input fd: %s\n", strerror (errno)); return gpg_error (GPG_ERR_ASS_GENERAL); } nbytes = 0; while ( (c=getc (fp)) != -1) { putc (c, stderr); nbytes++; } fflush (stderr); log_info ("done printing %d bytes to stderr\n", nbytes); fclose (fp); return 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); } table[] = { { "ECHO", cmd_echo }, { "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 server_common (assuan_context_t ctx) { int rc; rc = register_commands (ctx); if (rc) log_fatal ("register_commands failed: %s\n", gpg_strerror(rc)); 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 void server_pipe (void) { int rc; assuan_context_t ctx; log_info ("server started\n"); rc = assuan_new (&ctx); if (rc) log_fatal ("assuan_new failed: %s\n", gpg_strerror (rc)); rc = assuan_init_pipe_server (ctx, NULL); if (rc) log_fatal ("assuan_init_pipe_server failed: %s\n", gpg_strerror (rc)); server_common (ctx); } static assuan_sock_nonce_t socket_nonce; static void server_socket (const char *socketname) { int rc; assuan_context_t ctx; assuan_fd_t fd; struct sockaddr_un unaddr_struct; struct sockaddr *addr; socklen_t len; log_info ("server on socket started\n"); fd = assuan_sock_new (AF_UNIX, SOCK_STREAM, 0); if (fd == ASSUAN_INVALID_FD) log_fatal ("assuan_sock_new failed\n"); addr = (struct sockaddr *)&unaddr_struct; rc = assuan_sock_set_sockaddr_un (socketname, addr, NULL); if (rc) { assuan_sock_close (fd); log_fatal ("assuan_sock_set_sockaddr_un failed: %s\n", gpg_strerror (rc)); } len = offsetof (struct sockaddr_un, sun_path) + strlen (unaddr_struct.sun_path); rc = assuan_sock_bind (fd, addr, len); if (rc) { assuan_sock_close (fd); log_fatal ("assuan_sock_bind failed: %s\n", gpg_strerror (rc)); } rc = assuan_sock_get_nonce (addr, len, &socket_nonce); if (rc) { assuan_sock_close (fd); log_fatal ("assuan_sock_get_nonce failed: %s\n", gpg_strerror (rc)); } rc = listen (HANDLE2SOCKET (fd), 5); if (rc < 0) { assuan_sock_close (fd); log_fatal ("listen failed: %s\n", gpg_strerror (gpg_error_from_syserror ())); } rc = assuan_new (&ctx); if (rc) log_fatal ("assuan_new failed: %s\n", gpg_strerror (rc)); rc = assuan_init_socket_server (ctx, fd, ASSUAN_SOCKET_SERVER_FDPASSING); if (rc) log_fatal ("assuan_init_socket_server failed: %s\n", gpg_strerror (rc)); assuan_set_sock_nonce (ctx, &socket_nonce); + assuan_set_hello_line (ctx, "Hello, this is a socket server."); server_common (ctx); } /* C L I E N T */ /* Client main. If true is returned, a disconnect has not been done. */ static int client (assuan_context_t ctx, const char *fname) { int rc; FILE *fp; int i; #if HAVE_W32_SYSTEM HANDLE file_handle; #endif log_info ("client started. Servers's pid is %ld\n", (long)assuan_get_pid (ctx)); for (i=0; i < 6; i++) { fp = fopen (fname, "r"); if (!fp) { log_error ("failed to open `%s': %s\n", fname, strerror (errno)); return -1; } #ifdef HAVE_W32_SYSTEM file_handle = (HANDLE)_get_osfhandle (fileno (fp)); rc = assuan_sendfd (ctx, file_handle); #else rc = assuan_sendfd (ctx, (assuan_fd_t)fileno (fp)); #endif if (rc) { fclose (fp); log_error ("assuan_sendfd failed: %s\n", gpg_strerror (rc)); return -1; } fclose (fp); rc = assuan_transact (ctx, "INPUT FD", NULL, NULL, NULL, NULL, NULL, NULL); if (rc) { log_error ("sending INPUT FD failed: %s\n", gpg_strerror (rc)); return -1; } rc = assuan_transact (ctx, "ECHO", NULL, NULL, NULL, NULL, NULL, NULL); if (rc) { log_error ("sending ECHO failed: %s\n", gpg_strerror (rc)); return -1; } } /* Give us some time to check with lsof that all descriptors are closed. */ /* sleep (10); */ assuan_release (ctx); return 0; } /* M A I N */ int main (int argc, char **argv) { const char *program_name = NULL; int last_argc = -1; assuan_context_t ctx; gpg_error_t err; int is_server = 0; int with_exec = 0; const char *socketname = NULL; if (argc) { program_name = *argv; log_set_prefix (*argv); argc--; argv++; } while (argc && last_argc != argc ) { last_argc = argc; if (!strcmp (*argv, "--help")) { puts ( "usage: ./fdpassing [options]\n" "\n" "Options:\n" " --verbose Show what is going on\n" " --with-exec Exec the child. Default is just a fork on POSIX machine\n" " --socketname Specify the socket path\n" ); 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")) { is_server = 1; argc--; argv++; } else if (!strcmp (*argv, "--with-exec")) { with_exec = 1; argc--; argv++; } else if (!strcmp (*argv, "--socketname")) { argc--; argv++; if (argc) { socketname = *argv++; argc--; } } } if (socketname) assuan_sock_init (); else { #ifdef HAVE_W32_SYSTEM with_exec = 1; #else ; #endif } assuan_set_assuan_log_prefix (log_prefix); if (is_server) { if (socketname) server_socket (socketname); else server_pipe (); log_info ("server finished\n"); } else { char *fname; err = assuan_new (&ctx); if (err) log_fatal ("assuan_new failed: %s\n", gpg_strerror (err)); if (socketname) { err = assuan_socket_connect (ctx, socketname, 0, ASSUAN_SOCKET_CONNECT_FDPASSING); if (err) { log_error ("assuan_socket_connect failed: %s\n", gpg_strerror (err)); assuan_release (ctx); errorcount++; goto done; } } else { assuan_fd_t no_close_fds[2]; const char *arglist[10]; const char *loc; no_close_fds[0] = verbose? assuan_fd_from_posix_fd (2): (assuan_fd_t)-1; no_close_fds[1] = (assuan_fd_t)-1; if (with_exec) { arglist[0] = program_name; arglist[1] = "--server"; arglist[2] = verbose? "--verbose":NULL; arglist[3] = NULL; } err = assuan_pipe_connect (ctx, with_exec? program_name : NULL, with_exec ? arglist : &loc, no_close_fds, NULL, NULL, 1); if (err) { log_error ("assuan_pipe_connect failed: %s\n", gpg_strerror (err)); assuan_release (ctx); errorcount++; } else if (!with_exec && loc[0] == 's') { server_pipe (); assuan_release (ctx); log_info ("server finished\n"); goto done; } } fname = prepend_srcdir ("motd"); if (client (ctx, fname)) { log_info ("waiting for server to terminate...\n"); assuan_release (ctx); } log_info ("client finished\n"); xfree (fname); } done: return errorcount ? 1 : 0; }