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;
}