diff --git a/src/assuan-defs.h b/src/assuan-defs.h
index e55e7c7..faf9aae 100644
--- a/src/assuan-defs.h
+++ b/src/assuan-defs.h
@@ -1,438 +1,476 @@
/* 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. */
+
+ /*
+ * Process reference (PID on POSIX, Process Handle on Windows).
+ * Internal use, only valid for client with pipe.
+ */
+ assuan_pid_t server_proc;
+
+ /*
+ * NOTE: There are two different references for the process:
+ *
+ * (1) Process ID which is valid on a system.
+ * (2) Process handle which is private to the process that get it.
+ *
+ * POSIX system only has (1).
+ * Windows system has both of (1) and (2).
+ */
+
#if defined(HAVE_W32_SYSTEM)
- int process_id; /* process ID of the server for FD passing. */
+ /*
+ * The process ID of the peer.
+ *
+ * client with pipe: Used internally for FD passing.
+ * client with socket: Used internally for FD passing.
+ *
+ * server with pipe: Not valid.
+ * server with socket: Valid for Cygwin Unix domain socket emulation.
+ *
+ */
+ int process_id;
+#else
+ /*
+ * The pid of the peer.
+ *
+ * client with pipe: Not valid.
+ * client with socket: Not valid.
+ *
+ * server with pipe: Valid (by env _assuan_pipe_connect_pid).
+ * server with socket: Valid on a system with SO_PEERCRED/etc.
+ *
+ */
+ pid_t pid;
#endif
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,
+int _assuan_spawn (assuan_context_t ctx, assuan_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);
+assuan_pid_t _assuan_waitpid (assuan_context_t ctx, assuan_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);
gpg_error_t w32_fdpass_send (assuan_context_t ctx, assuan_fd_t fd);
gpg_error_t w32_fdpass_recv (assuan_context_t ctx, assuan_fd_t *fd);
#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);
/* 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-pipe-connect.c b/src/assuan-pipe-connect.c
index 82a0aee..c116d1d 100644
--- a/src/assuan-pipe-connect.c
+++ b/src/assuan-pipe-connect.c
@@ -1,453 +1,453 @@
/* assuan-pipe-connect.c - Establish a pipe connection (client)
* Copyright (C) 2001, 2002, 2003, 2005, 2006, 2007, 2009, 2010,
* 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
/* On Windows systems signal.h is not needed and even not supported on
WindowsCE. */
#ifndef HAVE_DOSISH_SYSTEM
# include
#endif
#ifdef HAVE_UNISTD_H
# include
#endif
#include
#ifdef HAVE_FCNTL_H
#include
#endif
#ifdef HAVE_SYS_TYPES_H
# include
#endif
#ifndef HAVE_W32_SYSTEM
# include
#else
# ifdef HAVE_WINSOCK2_H
# include
# endif
# 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
/* This should be called to make sure that SIGPIPE gets ignored. */
static void
fix_signals (void)
{
#ifndef HAVE_DOSISH_SYSTEM /* No SIGPIPE for these systems. */
static int fixed_signals;
if (!fixed_signals)
{
struct sigaction act;
sigaction (SIGPIPE, NULL, &act);
if (act.sa_handler == SIG_DFL)
{
act.sa_handler = SIG_IGN;
sigemptyset (&act.sa_mask);
act.sa_flags = 0;
sigaction (SIGPIPE, &act, NULL);
}
fixed_signals = 1;
/* FIXME: This is not MT safe */
}
#endif /*HAVE_DOSISH_SYSTEM*/
}
/* Helper for pipe_connect. */
static gpg_error_t
initial_handshake (assuan_context_t ctx)
{
assuan_response_t response;
int off;
gpg_error_t err;
err = _assuan_read_from_server (ctx, &response, &off, 0);
if (err)
TRACE1 (ctx, ASSUAN_LOG_SYSIO, "initial_handshake", ctx,
"can't connect server: %s", 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, ',');
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
{
TRACE1 (ctx, ASSUAN_LOG_SYSIO, "initial_handshake", ctx,
"can't connect server: `%s'", ctx->inbound.line);
err = _assuan_error (ctx, GPG_ERR_ASS_CONNECT_FAILED);
}
return err;
}
struct at_pipe_fork
{
void (*user_atfork) (void *opaque, int reserved);
void *user_atforkvalue;
pid_t parent_pid;
};
static void
at_pipe_fork_cb (void *opaque, int reserved)
{
struct at_pipe_fork *atp = opaque;
if (atp->user_atfork)
atp->user_atfork (atp->user_atforkvalue, reserved);
#ifndef HAVE_W32_SYSTEM
{
char mypidstr[50];
/* We store our parents pid in the environment so that the execed
assuan server is able to read the actual pid of the client.
The server can't use getppid because it might have been double
forked before the assuan server has been initialized. */
sprintf (mypidstr, "%lu", (unsigned long) atp->parent_pid);
setenv ("_assuan_pipe_connect_pid", mypidstr, 1);
/* Make sure that we never pass a connection fd variable when
using a simple pipe. */
unsetenv ("_assuan_connection_fd");
}
#endif
}
static gpg_error_t
pipe_connect (assuan_context_t ctx,
const char *name, const char **argv,
assuan_fd_t *fd_child_list,
void (*atfork) (void *opaque, int reserved),
void *atforkvalue, unsigned int flags)
{
gpg_error_t rc;
assuan_fd_t rp[2];
assuan_fd_t wp[2];
- pid_t pid;
+ assuan_pid_t pid;
int res;
struct at_pipe_fork atp;
unsigned int spawn_flags;
atp.user_atfork = atfork;
atp.user_atforkvalue = atforkvalue;
atp.parent_pid = getpid ();
if (!ctx || !name || !argv || !argv[0])
return _assuan_error (ctx, GPG_ERR_ASS_INV_VALUE);
if (! ctx->flags.no_fixsignals)
fix_signals ();
if (_assuan_pipe (ctx, rp, 1) < 0)
return _assuan_error (ctx, gpg_err_code_from_syserror ());
if (_assuan_pipe (ctx, wp, 0) < 0)
{
_assuan_close (ctx, rp[0]);
_assuan_close_inheritable (ctx, rp[1]);
return _assuan_error (ctx, gpg_err_code_from_syserror ());
}
spawn_flags = 0;
if (flags & ASSUAN_PIPE_CONNECT_DETACHED)
spawn_flags |= ASSUAN_SPAWN_DETACHED;
/* FIXME: Use atfork handler that closes child fds on Unix. */
res = _assuan_spawn (ctx, &pid, name, argv, wp[0], rp[1],
fd_child_list, at_pipe_fork_cb, &atp, spawn_flags);
if (res < 0)
{
rc = gpg_err_code_from_syserror ();
_assuan_close (ctx, rp[0]);
_assuan_close_inheritable (ctx, rp[1]);
_assuan_close_inheritable (ctx, wp[0]);
_assuan_close (ctx, wp[1]);
return _assuan_error (ctx, rc);
}
/* Close the stdin/stdout child fds in the parent. */
_assuan_close_inheritable (ctx, rp[1]);
_assuan_close_inheritable (ctx, wp[0]);
ctx->engine.release = _assuan_client_release;
ctx->engine.readfnc = _assuan_simple_read;
ctx->engine.writefnc = _assuan_simple_write;
#ifdef HAVE_W32_SYSTEM
ctx->engine.sendfd = w32_fdpass_send;
#else
ctx->engine.sendfd = NULL;
#endif
ctx->engine.receivefd = NULL;
ctx->finish_handler = _assuan_client_finish;
ctx->max_accepts = 1;
ctx->accept_handler = NULL;
ctx->inbound.fd = rp[0]; /* Our inbound is read end of read pipe. */
ctx->outbound.fd = wp[1]; /* Our outbound is write end of write pipe. */
- ctx->pid = pid;
+ ctx->server_proc = pid;
rc = initial_handshake (ctx);
if (rc)
_assuan_reset (ctx);
return rc;
}
/* FIXME: For socketpair_connect, use spawn function and add atfork
handler to do the right thing. Instead of stdin and stdout, we
extend the fd_child_list by fds[1]. */
#ifndef HAVE_W32_SYSTEM
struct at_socketpair_fork
{
assuan_fd_t peer_fd;
void (*user_atfork) (void *opaque, int reserved);
void *user_atforkvalue;
pid_t parent_pid;
};
static void
at_socketpair_fork_cb (void *opaque, int reserved)
{
struct at_socketpair_fork *atp = opaque;
if (atp->user_atfork)
atp->user_atfork (atp->user_atforkvalue, reserved);
#ifndef HAVE_W32_SYSTEM
{
char mypidstr[50];
/* We store our parents pid in the environment so that the execed
assuan server is able to read the actual pid of the client.
The server can't use getppid because it might have been double
forked before the assuan server has been initialized. */
sprintf (mypidstr, "%lu", (unsigned long) atp->parent_pid);
setenv ("_assuan_pipe_connect_pid", mypidstr, 1);
/* Now set the environment variable used to convey the
connection's file descriptor. */
sprintf (mypidstr, "%d", atp->peer_fd);
if (setenv ("_assuan_connection_fd", mypidstr, 1))
_exit (4);
}
#endif
}
/* This function is similar to pipe_connect but uses a socketpair and
sets the I/O up to use sendmsg/recvmsg. */
static gpg_error_t
socketpair_connect (assuan_context_t ctx,
const char *name, const char **argv,
assuan_fd_t *fd_child_list,
void (*atfork) (void *opaque, int reserved),
void *atforkvalue)
{
gpg_error_t err;
int idx;
int fds[2];
char mypidstr[50];
pid_t pid;
int *child_fds = NULL;
int child_fds_cnt = 0;
struct at_socketpair_fork atp;
int rc;
TRACE_BEG3 (ctx, ASSUAN_LOG_CTX, "socketpair_connect", ctx,
"name=%s,atfork=%p,atforkvalue=%p", name ? name : "(null)",
atfork, atforkvalue);
atp.user_atfork = atfork;
atp.user_atforkvalue = atforkvalue;
atp.parent_pid = getpid ();
if (!ctx
|| (name && (!argv || !argv[0]))
|| (!name && !argv))
return _assuan_error (ctx, GPG_ERR_ASS_INV_VALUE);
if (! ctx->flags.no_fixsignals)
fix_signals ();
sprintf (mypidstr, "%lu", (unsigned long)getpid ());
if (fd_child_list)
while (fd_child_list[child_fds_cnt] != ASSUAN_INVALID_FD)
child_fds_cnt++;
child_fds = _assuan_malloc (ctx, (child_fds_cnt + 2) * sizeof (int));
if (! child_fds)
return TRACE_ERR (gpg_err_code_from_syserror ());
child_fds[1] = ASSUAN_INVALID_FD;
if (fd_child_list)
memcpy (&child_fds[1], fd_child_list, (child_fds_cnt + 1) * sizeof (int));
if (_assuan_socketpair (ctx, AF_LOCAL, SOCK_STREAM, 0, fds))
{
TRACE_LOG1 ("socketpair failed: %s", strerror (errno));
_assuan_free (ctx, child_fds);
return TRACE_ERR (GPG_ERR_ASS_GENERAL);
}
atp.peer_fd = fds[1];
child_fds[0] = fds[1];
rc = _assuan_spawn (ctx, &pid, name, argv, ASSUAN_INVALID_FD,
ASSUAN_INVALID_FD, child_fds, at_socketpair_fork_cb,
&atp, 0);
if (rc < 0)
{
err = gpg_err_code_from_syserror ();
_assuan_close (ctx, fds[0]);
_assuan_close (ctx, fds[1]);
_assuan_free (ctx, child_fds);
return TRACE_ERR (err);
}
/* For W32, the user needs to know the server-local names of the
inherited handles. Return them here. Note that the translation
of the peer socketpair fd (fd_child_list[0]) must be done by the
wrapper program based on the environment variable
_assuan_connection_fd. */
if (fd_child_list)
{
for (idx = 0; fd_child_list[idx] != -1; idx++)
/* We add 1 to skip over the socketpair end. */
fd_child_list[idx] = child_fds[idx + 1];
}
_assuan_free (ctx, child_fds);
/* If this is the server child process, exit early. */
if (! name && (*argv)[0] == 's')
{
_assuan_close (ctx, fds[0]);
return 0;
}
_assuan_close (ctx, fds[1]);
ctx->engine.release = _assuan_client_release;
ctx->finish_handler = _assuan_client_finish;
ctx->max_accepts = 1;
ctx->inbound.fd = fds[0];
ctx->outbound.fd = fds[0];
_assuan_init_uds_io (ctx);
err = initial_handshake (ctx);
if (err)
_assuan_reset (ctx);
return err;
}
#endif /*!HAVE_W32_SYSTEM*/
/* Connect to a server over a full-duplex socket (i.e. created by
socketpair), creating the assuan context and returning it in CTX.
The server filename is NAME, the argument vector in ARGV.
FD_CHILD_LIST is a -1 terminated list of file descriptors not to
close in the child. ATFORK is called in the child right after the
fork; ATFORKVALUE is passed as the first argument and 0 is passed
as the second argument. The ATFORK function should only act if the
second value is 0.
FLAGS is a bit vector and controls how the function acts:
Bit 0: If cleared a simple pipe based server is expected and the
function behaves similar to `assuan_pipe_connect'.
If set a server based on full-duplex pipes is expected. Such
pipes are usually created using the `socketpair' function.
It also enables features only available with such servers.
Bit 7: If set and there is a need to start the server it will be
started as a background process. This flag is useful under
W32 systems, so that no new console is created and pops up a
console window when starting the server
If NAME is NULL, no exec is done but the same process is continued.
However all file descriptors are closed and some special
environment variables are set. To let the caller detect whether the
child or the parent continues, the child returns "client" or
"server" in *ARGV (but it is sufficient to check only the first
character). This feature is only available on POSIX platforms. */
gpg_error_t
assuan_pipe_connect (assuan_context_t ctx,
const char *name, const char *argv[],
assuan_fd_t *fd_child_list,
void (*atfork) (void *opaque, int reserved),
void *atforkvalue, unsigned int flags)
{
TRACE2 (ctx, ASSUAN_LOG_CTX, "assuan_pipe_connect", ctx,
"name=%s, flags=0x%x", name ? name : "(null)", flags);
#ifndef HAVE_W32_SYSTEM
if (flags & ASSUAN_PIPE_CONNECT_FDPASSING)
return socketpair_connect (ctx, name, argv, fd_child_list,
atfork, atforkvalue);
else
#endif
return pipe_connect (ctx, name, argv, fd_child_list, atfork, atforkvalue,
flags);
}
diff --git a/src/assuan-pipe-server.c b/src/assuan-pipe-server.c
index 5c960a6..4858ac5 100644
--- a/src/assuan-pipe-server.c
+++ b/src/assuan-pipe-server.c
@@ -1,147 +1,151 @@
/* assuan-pipe-server.c - Assuan server working over a pipe
* Copyright (C) 2001, 2002, 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
# include
#endif
#ifdef HAVE_SYS_STAT_H
# include
#endif
#ifdef HAVE_UNISTD_H
# include
#endif
#ifdef HAVE_W32_SYSTEM
# ifdef HAVE_WINSOCK2_H
# include
# endif
# include
#ifdef HAVE_FCNTL_H
# include
#endif
#endif
#include "assuan-defs.h"
#include "debug.h"
/* Returns true if atoi(S) denotes a valid socket. */
#ifndef HAVE_W32_SYSTEM
static int
is_valid_socket (const char *s)
{
struct stat buf;
if ( fstat (atoi (s), &buf ) )
return 0;
return S_ISSOCK (buf.st_mode);
}
#endif /*!HAVE_W32_SYSTEM*/
/* This actually is a int file descriptor (and not assuan_fd_t) as
_get_osfhandle is called on W32 systems. */
gpg_error_t
assuan_init_pipe_server (assuan_context_t ctx, assuan_fd_t filedes[2])
{
+#if !defined(HAVE_W32_SYSTEM)
const char *s;
unsigned long ul;
+#endif
gpg_error_t rc;
assuan_fd_t infd = ASSUAN_INVALID_FD;
assuan_fd_t outfd = ASSUAN_INVALID_FD;
int is_usd = 0;
TRACE_BEG (ctx, ASSUAN_LOG_CTX, "assuan_init_pipe_server", ctx);
if (filedes)
{
TRACE_LOG2 ("fd[0]=0x%x, fd[1]=0x%x", filedes[0], filedes[1]);
}
rc = _assuan_register_std_commands (ctx);
if (rc)
return TRACE_ERR (rc);
#ifdef HAVE_W32_SYSTEM
if (filedes)
{
infd = filedes[0];
outfd = filedes[1];
}
else
{
infd = assuan_fd_from_posix_fd (0);
outfd = assuan_fd_from_posix_fd (1);
}
#else
s = getenv ("_assuan_connection_fd");
if (s && *s && is_valid_socket (s))
{
/* Well, we are called with an bi-directional file descriptor.
Prepare for using sendmsg/recvmsg. In this case we ignore
the passed file descriptors. */
infd = atoi (s);
outfd = atoi (s);
is_usd = 1;
}
else if (filedes && filedes[0] != ASSUAN_INVALID_FD
&& filedes[1] != ASSUAN_INVALID_FD )
{
/* Standard pipe server. */
infd = filedes[0];
outfd = filedes[1];
}
else
{
rc = _assuan_error (ctx, GPG_ERR_ASS_SERVER_START);
return TRACE_ERR (rc);
}
#endif
ctx->flags.is_server = 1;
ctx->engine.release = _assuan_server_release;
ctx->engine.readfnc = _assuan_simple_read;
ctx->engine.writefnc = _assuan_simple_write;
ctx->engine.sendfd = NULL;
#ifdef HAVE_W32_SYSTEM
ctx->engine.receivefd = w32_fdpass_recv;
#else
ctx->engine.receivefd = NULL;
#endif
ctx->max_accepts = 1;
+#if !defined(HAVE_W32_SYSTEM)
s = getenv ("_assuan_pipe_connect_pid");
if (s && (ul=strtoul (s, NULL, 10)) && ul)
ctx->pid = (pid_t)ul;
else
ctx->pid = (pid_t)-1;
+#endif
ctx->accept_handler = NULL;
ctx->finish_handler = _assuan_server_finish;
ctx->inbound.fd = infd;
ctx->outbound.fd = outfd;
if (is_usd)
_assuan_init_uds_io (ctx);
return TRACE_SUC();
}
diff --git a/src/assuan-socket-server.c b/src/assuan-socket-server.c
index f5fe038..a260221 100644
--- a/src/assuan-socket-server.c
+++ b/src/assuan-socket-server.c
@@ -1,263 +1,265 @@
/* assuan-socket-server.c - Assuan socket based server
* Copyright (C) 2002, 2007, 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
#ifdef HAVE_UNISTD_H
# include
#endif
#ifdef HAVE_SYS_TYPES_H
# include
#endif
#ifdef HAVE_W32_SYSTEM
# ifdef HAVE_WINSOCK2_H
# include
# endif
# include
# if HAVE_SYS_SOCKET_H
# include
# elif HAVE_WS2TCPIP_H
# include
# endif
#else
# include
# include
#endif
#ifdef HAVE_SYS_UCRED_H
#include
#endif
#ifdef HAVE_UCRED_H
#include
#endif
#include "debug.h"
#include "assuan-defs.h"
static gpg_error_t
accept_connection_bottom (assuan_context_t ctx)
{
assuan_fd_t fd = ctx->connected_fd;
TRACE (ctx, ASSUAN_LOG_SYSIO, "accept_connection_bottom", ctx);
ctx->peercred_valid = 0;
#ifdef SO_PEERCRED
{
#ifdef HAVE_STRUCT_SOCKPEERCRED_PID
struct sockpeercred cr; /* OpenBSD */
#else
struct ucred cr; /* GNU/Linux */
#endif
socklen_t cl = sizeof cr;
if (!getsockopt (fd, SOL_SOCKET, SO_PEERCRED, &cr, &cl))
{
ctx->peercred_valid = 1;
ctx->peercred.pid = cr.pid;
ctx->peercred.uid = cr.uid;
ctx->peercred.gid = cr.gid;
}
}
#elif defined (LOCAL_PEERPID)
{ /* macOS */
socklen_t len = sizeof (pid_t);
if (!getsockopt (fd, SOL_LOCAL, LOCAL_PEERPID, &ctx->peercred.pid, &len))
{
ctx->peercred_valid = 1;
#if defined (LOCAL_PEERCRED)
{
struct xucred cr;
len = sizeof (struct xucred);
if (!getsockopt (fd, SOL_LOCAL, LOCAL_PEERCRED, &cr, &len))
{
ctx->peercred.uid = cr.cr_uid;
ctx->peercred.gid = cr.cr_gid;
}
}
#endif
}
}
#elif defined (LOCAL_PEEREID)
{ /* NetBSD */
struct unpcbid unp;
socklen_t unpl = sizeof unp;
if (getsockopt (fd, 0, LOCAL_PEEREID, &unp, &unpl) != -1)
{
ctx->peercred_valid = 1;
ctx->peercred.pid = unp.unp_pid;
ctx->peercred.uid = unp.unp_euid;
ctx->peercred.gid = unp.unp_egid;
}
}
#elif defined (HAVE_GETPEERUCRED)
{ /* Solaris */
ucred_t *ucred = NULL;
if (getpeerucred (fd, &ucred) != -1)
{
ctx->peercred_valid = 1;
ctx->peercred.pid = ucred_getpid (ucred);
ctx->peercred.uid = ucred_geteuid (ucred);
ctx->peercred.gid = ucred_getegid (ucred);
ucred_free (ucred);
}
}
#elif defined(HAVE_GETPEEREID)
{ /* FreeBSD */
if (getpeereid (fd, &ctx->peercred.uid, &ctx->peercred.gid) != -1)
{
ctx->peercred_valid = 1;
ctx->peercred.pid = ASSUAN_INVALID_PID;
}
}
#endif
+#if !defined(HAVE_W32_SYSTEM)
/* This overrides any already set PID if the function returns
a valid one. */
if (ctx->peercred_valid && ctx->peercred.pid != ASSUAN_INVALID_PID)
ctx->pid = ctx->peercred.pid;
+#endif
ctx->inbound.fd = fd;
ctx->inbound.eof = 0;
ctx->inbound.linelen = 0;
ctx->inbound.attic.linelen = 0;
ctx->inbound.attic.pending = 0;
ctx->outbound.fd = fd;
ctx->outbound.data.linelen = 0;
ctx->outbound.data.error = 0;
ctx->flags.confidential = 0;
return 0;
}
static gpg_error_t
accept_connection (assuan_context_t ctx)
{
assuan_fd_t fd;
struct sockaddr_un clnt_addr;
socklen_t len = sizeof clnt_addr;
TRACE1 (ctx, ASSUAN_LOG_SYSIO, "accept_connection", ctx,
"listen_fd=0x%x", ctx->listen_fd);
fd = SOCKET2HANDLE(accept (HANDLE2SOCKET(ctx->listen_fd),
(struct sockaddr*)&clnt_addr, &len ));
if (fd == ASSUAN_INVALID_FD)
{
return _assuan_error (ctx, gpg_err_code_from_syserror ());
}
TRACE1 (ctx, ASSUAN_LOG_SYSIO, "accept_connection", ctx,
"fd->0x%x", fd);
if (_assuan_sock_check_nonce (ctx, fd, &ctx->listen_nonce))
{
_assuan_close (ctx, fd);
return _assuan_error (ctx, GPG_ERR_ASS_ACCEPT_FAILED);
}
ctx->connected_fd = fd;
return accept_connection_bottom (ctx);
}
/*
Flag bits: 0 - use sendmsg/recvmsg to allow descriptor passing
1 - FD has already been accepted.
*/
gpg_error_t
assuan_init_socket_server (assuan_context_t ctx, assuan_fd_t fd,
unsigned int flags)
{
gpg_error_t rc;
TRACE_BEG2 (ctx, ASSUAN_LOG_CTX, "assuan_init_socket_server", ctx,
"fd=0x%x, flags=0x%x", fd, flags);
ctx->flags.is_socket = 1;
rc = _assuan_register_std_commands (ctx);
if (rc)
return TRACE_ERR (rc);
ctx->engine.release = _assuan_server_release;
ctx->engine.readfnc = _assuan_simple_read;
ctx->engine.writefnc = _assuan_simple_write;
ctx->engine.sendfd = NULL;
ctx->engine.receivefd = NULL;
ctx->flags.is_server = 1;
if (flags & ASSUAN_SOCKET_SERVER_ACCEPTED)
/* We want a second accept to indicate EOF. */
ctx->max_accepts = 1;
else
ctx->max_accepts = -1;
ctx->input_fd = ASSUAN_INVALID_FD;
ctx->output_fd = ASSUAN_INVALID_FD;
ctx->inbound.fd = ASSUAN_INVALID_FD;
ctx->outbound.fd = ASSUAN_INVALID_FD;
if (flags & ASSUAN_SOCKET_SERVER_ACCEPTED)
{
ctx->listen_fd = ASSUAN_INVALID_FD;
ctx->connected_fd = fd;
}
else
{
ctx->listen_fd = fd;
ctx->connected_fd = ASSUAN_INVALID_FD;
}
ctx->accept_handler = ((flags & ASSUAN_SOCKET_SERVER_ACCEPTED)
? accept_connection_bottom
: accept_connection);
ctx->finish_handler = _assuan_server_finish;
#ifdef HAVE_W32_SYSTEM
ctx->engine.receivefd = w32_fdpass_recv;
#else
if (flags & ASSUAN_SOCKET_SERVER_FDPASSING)
_assuan_init_uds_io (ctx);
#endif
rc = _assuan_register_std_commands (ctx);
if (rc)
_assuan_reset (ctx);
return TRACE_ERR (rc);
}
/* Save a copy of NONCE in context CTX. This should be used to
register the server's nonce with an context established by
assuan_init_socket_server. */
void
assuan_set_sock_nonce (assuan_context_t ctx, assuan_sock_nonce_t *nonce)
{
if (ctx && nonce)
ctx->listen_nonce = *nonce;
}
diff --git a/src/assuan-socket.c b/src/assuan-socket.c
index f209457..cbd19fd 100644
--- a/src/assuan-socket.c
+++ b/src/assuan-socket.c
@@ -1,1580 +1,1582 @@
/* 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
# include
#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"
*
* %u 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 timeout)
{
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 (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 < 0)
return ret;
else 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;
if (timeout != 0)
{
if (timeout == -1)
{
tv.tv_sec = 0;
tv.tv_usec = 0;
}
else
{
/* TIMEOUT is in milisecond */
tv.tv_sec = timeout / 1000;
tv.tv_usec = (timeout % 1000) * 1000;
}
ret = select (HANDLE2SOCKET (sock)+1, &fds, NULL, NULL, &tv);
if (ret < 0)
return ret;
if (!ret)
{
gpg_err_set_errno (ETIMEDOUT);
return -1;
}
}
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 server 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, 0);
}
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, 0);
}
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.
TIMEOUT specifies connection timeout in miliseconds (0 means
default timeout, -1 means immediate timeout). 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 timeout,
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,
timeout))
{
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;
+
+ memcpy (&ctx->process_id, buffer, 4);
/* 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 (void)
{
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 (void)
{
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 timeout, const char *credentials,
unsigned int flags)
{
return _assuan_sock_connect_byname (sock_ctx,
host, port, timeout, 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/assuan.c b/src/assuan.c
index 58498f7..550f057 100644
--- a/src/assuan.c
+++ b/src/assuan.c
@@ -1,319 +1,326 @@
/* assuan.c - Global interface (not specific to context).
* Copyright (C) 2009 Free Software Foundation, Inc.
* Copyright (C) 2001, 2002, 2012, 2013 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 "assuan-defs.h"
#include "debug.h"
#define digitp(a) ((a) >= '0' && (a) <= '9')
/* Global default state. */
/* Functions called before and after blocking syscalls. */
static void (*pre_syscall_func) (void);
static void (*post_syscall_func) (void);
/* Variable to see if functions above are initialized. */
static int _assuan_syscall_func_initialized;
/* The default error source gor generated error codes. */
static gpg_err_source_t _assuan_default_err_source = GPG_ERR_SOURCE_USER_1;
/* The default memory management functions. */
static struct assuan_malloc_hooks _assuan_default_malloc_hooks =
{ malloc, realloc, free };
/* The default logging handler. */
static assuan_log_cb_t _assuan_default_log_cb = _assuan_log_handler;
static void *_assuan_default_log_cb_data = NULL;
/* Set the default gpg error source. */
void
assuan_set_gpg_err_source (gpg_err_source_t errsource)
{
_assuan_default_err_source = errsource;
}
/* Get the default gpg error source. */
gpg_err_source_t
assuan_get_gpg_err_source (void)
{
return _assuan_default_err_source;
}
/* Set the default malloc hooks. */
void
assuan_set_malloc_hooks (assuan_malloc_hooks_t malloc_hooks)
{
_assuan_default_malloc_hooks = *malloc_hooks;
}
/* Get the default malloc hooks. */
assuan_malloc_hooks_t
assuan_get_malloc_hooks (void)
{
return &_assuan_default_malloc_hooks;
}
/* Set the default log callback handler. */
void
assuan_set_log_cb (assuan_log_cb_t log_cb, void *log_cb_data)
{
_assuan_default_log_cb = log_cb;
_assuan_default_log_cb_data = log_cb_data;
_assuan_init_log_envvars ();
}
/* Get the default log callback handler. */
void
assuan_get_log_cb (assuan_log_cb_t *log_cb, void **log_cb_data)
{
*log_cb = _assuan_default_log_cb;
*log_cb_data = _assuan_default_log_cb_data;
}
void
assuan_set_system_hooks (assuan_system_hooks_t system_hooks)
{
_assuan_system_hooks_copy (&_assuan_system_hooks, system_hooks);
}
/* Used before blocking system calls. */
void
_assuan_pre_syscall (void)
{
again:
if (pre_syscall_func)
pre_syscall_func ();
else if (!_assuan_syscall_func_initialized)
{
gpgrt_get_syscall_clamp (&pre_syscall_func, &post_syscall_func);
_assuan_syscall_func_initialized = 1;
goto again;
}
}
/* Used after blocking system calls. */
void
_assuan_post_syscall (void)
{
if (post_syscall_func)
post_syscall_func ();
}
/* Create a new Assuan context. The initial parameters are all needed
in the creation of the context. */
gpg_error_t
assuan_new_ext (assuan_context_t *r_ctx, gpg_err_source_t err_source,
assuan_malloc_hooks_t malloc_hooks, assuan_log_cb_t log_cb,
void *log_cb_data)
{
struct assuan_context_s wctx;
assuan_context_t ctx;
/* Set up a working context so we can use standard functions. */
memset (&wctx, 0, sizeof (wctx));
wctx.err_source = err_source;
wctx.malloc_hooks = *malloc_hooks;
wctx.log_cb = log_cb;
wctx.log_cb_data = log_cb_data;
/* Need a new block for the trace macros to work. */
{
TRACE_BEG8 (&wctx, ASSUAN_LOG_CTX, "assuan_new_ext", r_ctx,
"err_source = %i (%s), malloc_hooks = %p (%p, %p, %p), "
"log_cb = %p, log_cb_data = %p", err_source,
gpg_strsource (err_source), malloc_hooks, malloc_hooks->malloc,
malloc_hooks->realloc, malloc_hooks->free, log_cb, log_cb_data);
*r_ctx = NULL;
ctx = _assuan_malloc (&wctx, sizeof (*ctx));
if (!ctx)
return TRACE_ERR (gpg_err_code_from_syserror ());
memcpy (ctx, &wctx, sizeof (*ctx));
ctx->system = _assuan_system_hooks;
/* FIXME: Delegate to subsystems/engines, as the FDs are not our
responsibility (we don't deallocate them, for example). */
ctx->input_fd = ASSUAN_INVALID_FD;
ctx->output_fd = ASSUAN_INVALID_FD;
ctx->inbound.fd = ASSUAN_INVALID_FD;
ctx->outbound.fd = ASSUAN_INVALID_FD;
ctx->listen_fd = ASSUAN_INVALID_FD;
+#if defined(HAVE_W32_SYSTEM)
+ ctx->process_id = -1;
+#else
+ ctx->pid = ASSUAN_INVALID_PID;
+#endif
+ ctx->server_proc = ASSUAN_INVALID_PID;
+
*r_ctx = ctx;
return TRACE_SUC1 ("ctx=%p", ctx);
}
}
/* Create a new context with default arguments. */
gpg_error_t
assuan_new (assuan_context_t *r_ctx)
{
return assuan_new_ext (r_ctx, _assuan_default_err_source,
&_assuan_default_malloc_hooks,
_assuan_default_log_cb,
_assuan_default_log_cb_data);
}
/* Release all resources associated with an engine operation. */
void
_assuan_reset (assuan_context_t ctx)
{
if (ctx->engine.release)
{
(*ctx->engine.release) (ctx);
ctx->engine.release = NULL;
}
/* FIXME: Clean standard commands */
}
/* Release all resources associated with the given context. */
void
assuan_release (assuan_context_t ctx)
{
if (! ctx)
return;
TRACE (ctx, ASSUAN_LOG_CTX, "assuan_release", ctx);
_assuan_reset (ctx);
/* None of the members that are our responsibility requires
deallocation. To avoid sensitive data in the line buffers we
wipe them out, though. Note that we can't wipe the entire
context because it also has a pointer to the actual free(). */
wipememory (&ctx->inbound, sizeof ctx->inbound);
wipememory (&ctx->outbound, sizeof ctx->outbound);
_assuan_free (ctx, ctx);
}
/*
Version number stuff.
*/
static const char*
parse_version_number (const char *s, int *number)
{
int val = 0;
if (*s == '0' && digitp (s[1]))
return NULL; /* Leading zeros are not allowed. */
for (; digitp (*s); s++)
{
val *= 10;
val += *s - '0';
}
*number = val;
return val < 0 ? NULL : s;
}
static const char *
parse_version_string (const char *s, int *major, int *minor, int *micro)
{
s = parse_version_number (s, major);
if (!s || *s != '.')
return NULL;
s++;
s = parse_version_number (s, minor);
if (!s || *s != '.')
return NULL;
s++;
s = parse_version_number (s, micro);
if (!s)
return NULL;
return s; /* Patchlevel. */
}
static const char *
compare_versions (const char *my_version, const char *req_version)
{
int my_major, my_minor, my_micro;
int rq_major, rq_minor, rq_micro;
const char *my_plvl, *rq_plvl;
if (!req_version)
return my_version;
if (!my_version)
return NULL;
my_plvl = parse_version_string (my_version, &my_major, &my_minor, &my_micro);
if (!my_plvl)
return NULL; /* Very strange: our own version is bogus. */
rq_plvl = parse_version_string(req_version,
&rq_major, &rq_minor, &rq_micro);
if (!rq_plvl)
return NULL; /* Requested version string is invalid. */
if (my_major > rq_major
|| (my_major == rq_major && my_minor > rq_minor)
|| (my_major == rq_major && my_minor == rq_minor
&& my_micro > rq_micro)
|| (my_major == rq_major && my_minor == rq_minor
&& my_micro == rq_micro))
{
return my_version;
}
return NULL;
}
/*
* Check that the the version of the library is at minimum REQ_VERSION
* and return the actual version string; return NULL if the condition
* is not met. If NULL is passed to this function, no check is done
* and the version string is simply returned.
*/
const char *
assuan_check_version (const char *req_version)
{
if (req_version && req_version[0] == 1 && req_version[1] == 1)
return _assuan_sysutils_blurb ();
return compare_versions (PACKAGE_VERSION, req_version);
}
diff --git a/src/assuan.h.in b/src/assuan.h.in
index 2f89f10..10917c9 100644
--- a/src/assuan.h.in
+++ b/src/assuan.h.in
@@ -1,613 +1,614 @@
/* assuan.h - Definitions for the Assuan IPC library -*- c -*-
* Copyright (C) 2001-2013 Free Software Foundation, Inc.
* Copyright (C) 2001-2021 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-or-later
*
* @configure_input@
*/
/* Compile time configuration:
*
* #define _ASSUAN_NO_SOCKET_WRAPPER
*
* Do not include the definitions for the socket wrapper feature.
*/
#ifndef ASSUAN_H
#define ASSUAN_H
#include
#include
#include
#include
#ifndef _ASSUAN_NO_SOCKET_WRAPPER
@include:includes@
#endif /*!_ASSUAN_NO_SOCKET_WRAPPER*/
@include:types@
#include
#ifdef __cplusplus
extern "C"
{
#if 0
}
#endif
#endif
/* The version of this header should match the one of the library. Do
* not use this symbol in your application; use assuan_check_version
* instead. */
#define ASSUAN_VERSION @version@
/* The version number of this header. It may be used to handle minor
* API incompatibilities. */
#define ASSUAN_VERSION_NUMBER @version-number@
/* Check for compiler features. */
#if __GNUC__
#define _ASSUAN_GCC_VERSION (__GNUC__ * 10000 \
+ __GNUC_MINOR__ * 100 \
+ __GNUC_PATCHLEVEL__)
#if _ASSUAN_GCC_VERSION > 30100
#define _ASSUAN_DEPRECATED __attribute__ ((__deprecated__))
#endif
#endif
#ifndef _ASSUAN_DEPRECATED
#define _ASSUAN_DEPRECATED
#endif
#define ASSUAN_LINELENGTH 1002 /* 1000 + [CR,]LF */
struct assuan_context_s;
typedef struct assuan_context_s *assuan_context_t;
@include:fd-t@
assuan_fd_t assuan_fdopen (int fd);
@include:sock-nonce@
/*
* Global interface.
*/
struct assuan_malloc_hooks
{
void *(*malloc) (size_t cnt);
void *(*realloc) (void *ptr, size_t cnt);
void (*free) (void *ptr);
};
typedef struct assuan_malloc_hooks *assuan_malloc_hooks_t;
/* Categories for log messages. */
#define ASSUAN_LOG_INIT 1
#define ASSUAN_LOG_CTX 2
#define ASSUAN_LOG_ENGINE 3
#define ASSUAN_LOG_DATA 4
#define ASSUAN_LOG_SYSIO 5
#define ASSUAN_LOG_CONTROL 8
/* If MSG is NULL, return true/false depending on if this category is
* logged. This is used to probe before expensive log message
* generation (buffer dumps). */
typedef int (*assuan_log_cb_t) (assuan_context_t ctx, void *hook,
unsigned int cat, const char *msg);
/* Return or check the version number. */
const char *assuan_check_version (const char *req_version);
/* Set the default gpg error source. */
void assuan_set_gpg_err_source (gpg_err_source_t errsource);
/* Get the default gpg error source. */
gpg_err_source_t assuan_get_gpg_err_source (void);
/* Set the default malloc hooks. */
void assuan_set_malloc_hooks (assuan_malloc_hooks_t malloc_hooks);
/* Get the default malloc hooks. */
assuan_malloc_hooks_t assuan_get_malloc_hooks (void);
/* Set the default log callback handler. */
void assuan_set_log_cb (assuan_log_cb_t log_cb, void *log_cb_data);
/* Get the default log callback handler. */
void assuan_get_log_cb (assuan_log_cb_t *log_cb, void **log_cb_data);
/* Create a new Assuan context. The initial parameters are all needed
* in the creation of the context. */
gpg_error_t assuan_new_ext (assuan_context_t *ctx, gpg_err_source_t errsource,
assuan_malloc_hooks_t malloc_hooks,
assuan_log_cb_t log_cb, void *log_cb_data);
/* Create a new context with default arguments. */
gpg_error_t assuan_new (assuan_context_t *ctx);
/* Release all resources associated with the given context. */
void assuan_release (assuan_context_t ctx);
/* 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);
/* Set user-data in a context. */
void assuan_set_pointer (assuan_context_t ctx, void *pointer);
/* Get user-data in a context. */
void *assuan_get_pointer (assuan_context_t ctx);
/* Definitions of flags for assuan_set_flag(). */
typedef unsigned int assuan_flag_t;
/* When using a pipe server, by default Assuan will wait for the
* forked process to die in assuan_release. In certain cases this
* is not desirable. By setting this flag, the waitpid will be
* skipped and the caller is responsible to cleanup a forked
* process. */
#define ASSUAN_NO_WAITPID 1
/* This flag indicates whether Assuan logging is in confidential mode.
You can use assuan_{begin,end}_condidential to change the mode. */
#define ASSUAN_CONFIDENTIAL 2
/* This flag suppresses fix up of signal handlers for pipes. */
#define ASSUAN_NO_FIXSIGNALS 3
/* This flag changes assuan_transact to return comment lines via the
* status callback. The default is to skip comment lines. */
#define ASSUAN_CONVEY_COMMENTS 4
/* This flag disables logging for one context. */
#define ASSUAN_NO_LOGGING 5
/* This flag forces a connection close. */
#define ASSUAN_FORCE_CLOSE 6
/* For context CTX, set the flag FLAG to VALUE. Values for flags
* are usually 1 or 0 but certain flags might allow for other values;
* see the description of the type assuan_flag_t for details. */
void assuan_set_flag (assuan_context_t ctx, assuan_flag_t flag, int value);
/* Return the VALUE of FLAG in context CTX. */
int assuan_get_flag (assuan_context_t ctx, assuan_flag_t flag);
/* Same as assuan_set_flag (ctx, ASSUAN_CONFIDENTIAL, 1). */
void assuan_begin_confidential (assuan_context_t ctx);
/* Same as assuan_set_flag (ctx, ASSUAN_CONFIDENTIAL, 0). */
void assuan_end_confidential (assuan_context_t ctx);
/* Direction values for assuan_set_io_monitor. */
#define ASSUAN_IO_FROM_PEER 0
#define ASSUAN_IO_TO_PEER 1
/* Return flags of I/O monitor. */
#define ASSUAN_IO_MONITOR_NOLOG 1
#define ASSUAN_IO_MONITOR_IGNORE 2
/* The IO monitor gets to see all I/O on the context, and can return
* ASSUAN_IO_MONITOR_* bits to control actions on it. */
typedef unsigned int (*assuan_io_monitor_t) (assuan_context_t ctx, void *hook,
int inout, const char *line,
size_t linelen);
/* Set the IO monitor function. */
void assuan_set_io_monitor (assuan_context_t ctx,
assuan_io_monitor_t io_monitor, void *hook_data);
/* The system hooks. See assuan_set_system_hooks et al. */
#define ASSUAN_SYSTEM_HOOKS_VERSION 2
#define ASSUAN_SPAWN_DETACHED 128
struct assuan_system_hooks
{
/* Always set to ASSUAN_SYTEM_HOOKS_VERSION. */
int version;
/* Sleep for the given number of microseconds. */
void (*usleep) (assuan_context_t ctx, unsigned int usec);
/* Create a pipe with an inheritable end. */
int (*pipe) (assuan_context_t ctx, assuan_fd_t fd[2], int inherit_idx);
/* Close the given file descriptor, created with _assuan_pipe or one
of the socket functions. */
int (*close) (assuan_context_t ctx, assuan_fd_t fd);
ssize_t (*read) (assuan_context_t ctx, assuan_fd_t fd, void *buffer,
size_t size);
ssize_t (*write) (assuan_context_t ctx, assuan_fd_t fd,
const void *buffer, size_t size);
int (*recvmsg) (assuan_context_t ctx, assuan_fd_t fd, assuan_msghdr_t msg,
int flags);
int (*sendmsg) (assuan_context_t ctx, assuan_fd_t fd,
const assuan_msghdr_t msg, int flags);
/* If NAME is NULL, don't exec, just fork. FD_CHILD_LIST is
modified to reflect the value of the FD in the peer process (on
Windows). */
- int (*spawn) (assuan_context_t ctx, pid_t *r_pid, const char *name,
+ int (*spawn) (assuan_context_t ctx, assuan_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);
/* If action is 0, like waitpid. If action is 1, just release the PID? */
- pid_t (*waitpid) (assuan_context_t ctx, pid_t pid,
- int action, int *status, int options);
+ assuan_pid_t (*waitpid) (assuan_context_t ctx, assuan_pid_t pid,
+ int action, int *status, int options);
int (*socketpair) (assuan_context_t ctx, int _namespace, int style,
int protocol, assuan_fd_t filedes[2]);
assuan_fd_t (*socket) (assuan_context_t ctx, int _namespace,
int style, int protocol);
int (*connect) (assuan_context_t ctx, assuan_fd_t sock,
struct sockaddr *addr, socklen_t length);
};
typedef struct assuan_system_hooks *assuan_system_hooks_t;
/*
* Configuration of the default log handler.
*/
/* Set the prefix to be used at the start of a line emitted by assuan
* on the log stream. The default is the empty string. Note, that
* this function is not thread-safe and should in general be used
* right at startup. */
void assuan_set_assuan_log_prefix (const char *text);
/* Return a prefix to be used at the start of a line emitted by assuan
* on the log stream. The default implementation returns the empty
* string, i.e. "". */
const char *assuan_get_assuan_log_prefix (void);
/* Global default log stream. */
void assuan_set_assuan_log_stream (FILE *fp);
/* Set the per context log stream for the default log handler. */
void assuan_set_log_stream (assuan_context_t ctx, FILE *fp);
/* The type for assuan command handlers. */
typedef gpg_error_t (*assuan_handler_t) (assuan_context_t, char *);
/*-- assuan-handler.c --*/
gpg_error_t assuan_register_command (assuan_context_t ctx,
const char *cmd_string,
assuan_handler_t handler,
const char *help_string);
gpg_error_t assuan_register_pre_cmd_notify (assuan_context_t ctx,
gpg_error_t (*fnc)(assuan_context_t,
const char *cmd));
gpg_error_t assuan_register_post_cmd_notify (assuan_context_t ctx,
void (*fnc)(assuan_context_t,
gpg_error_t));
gpg_error_t assuan_register_bye_notify (assuan_context_t ctx,
assuan_handler_t handler);
gpg_error_t assuan_register_reset_notify (assuan_context_t ctx,
assuan_handler_t handler);
gpg_error_t assuan_register_cancel_notify (assuan_context_t ctx,
assuan_handler_t handler);
gpg_error_t assuan_register_input_notify (assuan_context_t ctx,
assuan_handler_t handler);
gpg_error_t assuan_register_output_notify (assuan_context_t ctx,
assuan_handler_t handler);
gpg_error_t assuan_register_option_handler (assuan_context_t ctx,
gpg_error_t (*fnc)(assuan_context_t,
const char*,
const char*));
gpg_error_t assuan_process (assuan_context_t ctx);
gpg_error_t assuan_process_next (assuan_context_t ctx, int *done);
gpg_error_t assuan_process_done (assuan_context_t ctx, gpg_error_t rc);
int assuan_get_active_fds (assuan_context_t ctx, int what,
assuan_fd_t *fdarray, int fdarraysize);
const char *assuan_get_command_name (assuan_context_t ctx);
FILE *assuan_get_data_fp (assuan_context_t ctx);
gpg_error_t assuan_set_okay_line (assuan_context_t ctx, const char *line);
gpg_error_t assuan_write_status (assuan_context_t ctx,
const char *keyword, const char *text);
/* Negotiate a file descriptor. If LINE contains "FD=N", returns N
* assuming a local file descriptor. If LINE contains "FD" reads a
* file descriptor via CTX and stores it in *RDF (the CTX must be
* capable of passing file descriptors). Under Windows the returned
* FD is a libc-type one. */
gpg_error_t assuan_command_parse_fd (assuan_context_t ctx, char *line,
assuan_fd_t *rfd);
/*-- assuan-listen.c --*/
gpg_error_t assuan_set_hello_line (assuan_context_t ctx, const char *line);
gpg_error_t assuan_accept (assuan_context_t ctx);
assuan_fd_t assuan_get_input_fd (assuan_context_t ctx);
assuan_fd_t assuan_get_output_fd (assuan_context_t ctx);
gpg_error_t assuan_close_input_fd (assuan_context_t ctx);
gpg_error_t assuan_close_output_fd (assuan_context_t ctx);
/*-- assuan-pipe-server.c --*/
gpg_error_t assuan_init_pipe_server (assuan_context_t ctx,
assuan_fd_t filedes[2]);
/*-- assuan-socket-server.c --*/
#define ASSUAN_SOCKET_SERVER_FDPASSING 1
#define ASSUAN_SOCKET_SERVER_ACCEPTED 2
gpg_error_t assuan_init_socket_server (assuan_context_t ctx,
assuan_fd_t listen_fd,
unsigned int flags);
void assuan_set_sock_nonce (assuan_context_t ctx, assuan_sock_nonce_t *nonce);
/*-- assuan-pipe-connect.c --*/
#define ASSUAN_PIPE_CONNECT_FDPASSING 1
#define ASSUAN_PIPE_CONNECT_DETACHED 128
gpg_error_t assuan_pipe_connect (assuan_context_t ctx,
const char *name,
const char *argv[],
assuan_fd_t *fd_child_list,
void (*atfork) (void *, int),
void *atforkvalue,
unsigned int flags);
/*-- assuan-socket-connect.c --*/
#define ASSUAN_SOCKET_CONNECT_FDPASSING 1
gpg_error_t assuan_socket_connect (assuan_context_t ctx, const char *name,
pid_t server_pid, unsigned int flags);
/*-- assuan-socket-connect.c --*/
gpg_error_t assuan_socket_connect_fd (assuan_context_t ctx, assuan_fd_t fd,
unsigned int flags);
/*-- context.c --*/
pid_t assuan_get_pid (assuan_context_t ctx);
struct _assuan_peercred
{
#ifdef _WIN32
/* Empty struct not allowed on some compilers, so, put this (not valid). */
pid_t pid;
#else
pid_t pid;
uid_t uid;
gid_t gid;
#endif
};
typedef struct _assuan_peercred *assuan_peercred_t;
gpg_error_t assuan_get_peercred (assuan_context_t ctx,
assuan_peercred_t *peercred);
/*
* Client interface.
*/
/* Client response codes. */
#define ASSUAN_RESPONSE_ERROR 0
#define ASSUAN_RESPONSE_OK 1
#define ASSUAN_RESPONSE_DATA 2
#define ASSUAN_RESPONSE_INQUIRE 3
#define ASSUAN_RESPONSE_STATUS 4
#define ASSUAN_RESPONSE_END 5
#define ASSUAN_RESPONSE_COMMENT 6
typedef int assuan_response_t;
/* This already de-escapes data lines. */
gpg_error_t assuan_client_read_response (assuan_context_t ctx,
char **line, int *linelen);
gpg_error_t assuan_client_parse_response (assuan_context_t ctx,
char *line, int linelen,
assuan_response_t *response,
int *off);
/*-- assuan-client.c --*/
gpg_error_t
assuan_transact (assuan_context_t ctx,
const char *command,
gpg_error_t (*data_cb)(void *, const void *, size_t),
void *data_cb_arg,
gpg_error_t (*inquire_cb)(void*, const char *),
void *inquire_cb_arg,
gpg_error_t (*status_cb)(void*, const char *),
void *status_cb_arg);
/*-- assuan-inquire.c --*/
gpg_error_t assuan_inquire (assuan_context_t ctx, const char *keyword,
unsigned char **r_buffer, size_t *r_length,
size_t maxlen);
gpg_error_t assuan_inquire_ext (assuan_context_t ctx, const char *keyword,
size_t maxlen,
gpg_error_t (*cb) (void *cb_data,
gpg_error_t rc,
unsigned char *buf,
size_t buf_len),
void *cb_data);
/*-- assuan-buffer.c --*/
gpg_error_t assuan_read_line (assuan_context_t ctx,
char **line, size_t *linelen);
int assuan_pending_line (assuan_context_t ctx);
gpg_error_t assuan_write_line (assuan_context_t ctx, const char *line);
gpg_error_t assuan_send_data (assuan_context_t ctx,
const void *buffer, size_t length);
/* The file descriptor must be pending before assuan_receivefd is
* called. This means that assuan_sendfd should be called *before* the
* trigger is sent (normally via assuan_write_line ("INPUT FD")). */
gpg_error_t assuan_sendfd (assuan_context_t ctx, assuan_fd_t fd);
gpg_error_t assuan_receivefd (assuan_context_t ctx, assuan_fd_t *fd);
/*-- assuan-util.c --*/
gpg_error_t assuan_set_error (assuan_context_t ctx, gpg_error_t err,
const char *text);
/*-- assuan-socket.c --*/
/* This flag is used with assuan_sock_connect_byname to
* connect via SOCKS. */
#define ASSUAN_SOCK_SOCKS 1
/* This flag is used with assuan_sock_connect_byname to force a
connection via Tor even if the socket subsystem has not been
swicthed into Tor mode. This flags overrides ASSUAN_SOCK_SOCKS. */
#define ASSUAN_SOCK_TOR 2
/* These are socket wrapper functions to support an emulation of Unix
* domain sockets on Windows. */
gpg_error_t assuan_sock_init (void);
void assuan_sock_deinit (void);
int assuan_sock_close (assuan_fd_t fd);
assuan_fd_t assuan_sock_new (int domain, int type, int proto);
int assuan_sock_set_flag (assuan_fd_t sockfd, const char *name, int value);
int assuan_sock_get_flag (assuan_fd_t sockfd, const char *name, int *r_value);
assuan_fd_t assuan_sock_accept (assuan_fd_t sockfd,
struct sockaddr *addr, socklen_t *p_addrlen);
int assuan_sock_connect (assuan_fd_t sockfd,
struct sockaddr *addr, int addrlen);
assuan_fd_t assuan_sock_connect_byname (const char *host, unsigned short port,
int timeout,
const char *credentials,
unsigned int flags);
int assuan_sock_bind (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 (struct sockaddr *addr, int addrlen,
assuan_sock_nonce_t *nonce);
int assuan_sock_check_nonce (assuan_fd_t fd, assuan_sock_nonce_t *nonce);
void assuan_sock_set_system_hooks (assuan_system_hooks_t system_hooks);
/* Set the default system callbacks. This is irreversible. */
void assuan_set_system_hooks (assuan_system_hooks_t system_hooks);
/* Set the per context system callbacks. This is irreversible. */
void assuan_ctx_set_system_hooks (assuan_context_t ctx,
assuan_system_hooks_t system_hooks);
/* Change the system hooks for the socket interface.
* This is not thread-safe. */
void assuan_sock_set_system_hooks (assuan_system_hooks_t 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_spawn (assuan_context_t ctx, pid_t *r_pid, const char *name,
+int __assuan_spawn (assuan_context_t ctx, assuan_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 __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);
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,
const assuan_msghdr_t msg, int flags);
-pid_t __assuan_waitpid (assuan_context_t ctx, pid_t pid,
- int nowait, int *status, int options);
+assuan_pid_t __assuan_waitpid (assuan_context_t ctx, assuan_pid_t pid,
+ int nowait, int *status, int options);
#ifdef ASSUAN_REALLY_REQUIRE_OLD_WAY_OF_SYSTEM_NPTH
/* Standard system hooks for nPth. */
#define ASSUAN_SYSTEM_NPTH_IMPL \
static void _assuan_npth_usleep (assuan_context_t ctx, unsigned int usec) \
{ npth_unprotect(); \
__assuan_usleep (ctx, usec); \
npth_protect(); } \
static ssize_t _assuan_npth_read (assuan_context_t ctx, assuan_fd_t fd, \
void *buffer, size_t size) \
{ ssize_t res; (void) ctx; npth_unprotect(); \
res = __assuan_read (ctx, fd, buffer, size); \
npth_protect(); return res; } \
static ssize_t _assuan_npth_write (assuan_context_t ctx, assuan_fd_t fd, \
const void *buffer, size_t size) \
{ ssize_t res; (void) ctx; npth_unprotect(); \
res = __assuan_write (ctx, fd, buffer, size); \
npth_protect(); return res; } \
static int _assuan_npth_recvmsg (assuan_context_t ctx, assuan_fd_t fd, \
assuan_msghdr_t msg, int flags) \
{ int res; (void) ctx; npth_unprotect(); \
res = __assuan_recvmsg (ctx, fd, msg, flags); \
npth_protect(); return res; } \
static int _assuan_npth_sendmsg (assuan_context_t ctx, assuan_fd_t fd, \
const assuan_msghdr_t msg, int flags) \
{ int res; (void) ctx; npth_unprotect(); \
res = __assuan_sendmsg (ctx, fd, msg, flags); \
npth_protect(); return res; } \
- static pid_t _assuan_npth_waitpid (assuan_context_t ctx, pid_t pid, \
- int nowait, int *status, int options) \
- { pid_t res; (void) ctx; npth_unprotect(); \
+ static assuan_pid_t _assuan_npth_waitpid (assuan_context_t ctx, \
+ assuan_pid_t pid, int nowait, \
+ int *status, int options) \
+ { assuan_pid_t res; (void) ctx; npth_unprotect(); \
res = __assuan_waitpid (ctx, pid, nowait, status, options); \
npth_protect(); return res; } \
static int _assuan_npth_connect (assuan_context_t ctx, assuan_fd_t sock, \
struct sockaddr *addr, socklen_t len)\
{ int res; npth_unprotect(); \
res = __assuan_connect (ctx, sock, addr, len); \
npth_protect(); return res; } \
static int _assuan_npth_close (assuan_context_t ctx, assuan_fd_t fd) \
{ int res; npth_unprotect(); \
res = __assuan_close (ctx, fd); \
npth_protect(); return res; } \
\
struct assuan_system_hooks _assuan_system_npth = \
{ ASSUAN_SYSTEM_HOOKS_VERSION, _assuan_npth_usleep, __assuan_pipe, \
_assuan_npth_close, _assuan_npth_read, _assuan_npth_write, \
_assuan_npth_recvmsg, _assuan_npth_sendmsg, \
__assuan_spawn, _assuan_npth_waitpid, __assuan_socketpair, \
__assuan_socket, _assuan_npth_connect }
extern struct assuan_system_hooks _assuan_system_npth;
#define ASSUAN_SYSTEM_NPTH &_assuan_system_npth
#else
#define ASSUAN_SYSTEM_NPTH_IMPL /**/
#define ASSUAN_SYSTEM_NPTH NULL
#endif
#ifdef __cplusplus
}
#endif
#endif /* ASSUAN_H */
diff --git a/src/client.c b/src/client.c
index dcb2a1a..9235584 100644
--- a/src/client.c
+++ b/src/client.c
@@ -1,349 +1,349 @@
/* client.c - Functions common to all clients.
* 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 "assuan-defs.h"
#include "debug.h"
#define xtoi_1(p) (*(p) <= '9'? (*(p)- '0'): \
*(p) <= 'F'? (*(p)-'A'+10):(*(p)-'a'+10))
#define xtoi_2(p) ((xtoi_1(p) * 16) + xtoi_1((p)+1))
void
_assuan_client_finish (assuan_context_t ctx)
{
if (ctx->inbound.fd != ASSUAN_INVALID_FD)
{
_assuan_close (ctx, ctx->inbound.fd);
if (ctx->inbound.fd == ctx->outbound.fd)
ctx->outbound.fd = ASSUAN_INVALID_FD;
ctx->inbound.fd = ASSUAN_INVALID_FD;
}
if (ctx->outbound.fd != ASSUAN_INVALID_FD)
{
_assuan_close (ctx, ctx->outbound.fd);
ctx->outbound.fd = ASSUAN_INVALID_FD;
}
- if (ctx->pid != ASSUAN_INVALID_PID && ctx->pid)
+ if (ctx->server_proc != ASSUAN_INVALID_PID)
{
if (!ctx->flags.is_socket)
- _assuan_waitpid (ctx, ctx->pid, ctx->flags.no_waitpid, NULL, 0);
- ctx->pid = ASSUAN_INVALID_PID;
+ _assuan_waitpid (ctx, ctx->server_proc, ctx->flags.no_waitpid, NULL, 0);
+ ctx->server_proc = ASSUAN_INVALID_PID;
}
_assuan_uds_deinit (ctx);
}
/* Disconnect and release the context CTX. */
void
_assuan_client_release (assuan_context_t ctx)
{
assuan_write_line (ctx, "BYE");
_assuan_client_finish (ctx);
}
/* This function also does deescaping for data lines. */
gpg_error_t
assuan_client_read_response (assuan_context_t ctx,
char **line_r, int *linelen_r)
{
gpg_error_t rc;
char *line = NULL;
int linelen = 0;
*line_r = NULL;
*linelen_r = 0;
do
{
do
{
rc = _assuan_read_line (ctx);
}
while (_assuan_error_is_eagain (ctx, rc));
if (rc)
return rc;
line = ctx->inbound.line;
linelen = ctx->inbound.linelen;
}
while (!linelen);
/* For data lines, we deescape immediately. The user will never
have to worry about it. */
if (linelen >= 1 && line[0] == 'D' && line[1] == ' ')
{
char *s, *d;
for (s=d=line; linelen; linelen--)
{
if (*s == '%' && linelen > 2)
{ /* handle escaping */
s++;
*d++ = xtoi_2 (s);
s += 2;
linelen -= 2;
}
else
*d++ = *s++;
}
*d = 0; /* add a hidden string terminator */
linelen = d - line;
ctx->inbound.linelen = linelen;
}
*line_r = line;
*linelen_r = linelen;
return 0;
}
gpg_error_t
assuan_client_parse_response (assuan_context_t ctx, char *line, int linelen,
assuan_response_t *response, int *off)
{
*response = ASSUAN_RESPONSE_ERROR;
*off = 0;
if (linelen >= 1
&& line[0] == 'D' && line[1] == ' ')
{
*response = ASSUAN_RESPONSE_DATA; /* data line */
*off = 2;
}
else if (linelen >= 1
&& line[0] == 'S'
&& (line[1] == '\0' || line[1] == ' '))
{
*response = ASSUAN_RESPONSE_STATUS;
*off = 1;
while (line[*off] == ' ')
++*off;
}
else if (linelen >= 2
&& line[0] == 'O' && line[1] == 'K'
&& (line[2] == '\0' || line[2] == ' '))
{
*response = ASSUAN_RESPONSE_OK;
*off = 2;
while (line[*off] == ' ')
++*off;
}
else if (linelen >= 3
&& line[0] == 'E' && line[1] == 'R' && line[2] == 'R'
&& (line[3] == '\0' || line[3] == ' '))
{
*response = ASSUAN_RESPONSE_ERROR;
*off = 3;
while (line[*off] == ' ')
++*off;
}
else if (linelen >= 7
&& line[0] == 'I' && line[1] == 'N' && line[2] == 'Q'
&& line[3] == 'U' && line[4] == 'I' && line[5] == 'R'
&& line[6] == 'E'
&& (line[7] == '\0' || line[7] == ' '))
{
*response = ASSUAN_RESPONSE_INQUIRE;
*off = 7;
while (line[*off] == ' ')
++*off;
}
else if (linelen >= 3
&& line[0] == 'E' && line[1] == 'N' && line[2] == 'D'
&& (line[3] == '\0' || line[3] == ' '))
{
*response = ASSUAN_RESPONSE_END;
*off = 3;
}
else if (linelen >= 1 && line[0] == '#')
{
*response = ASSUAN_RESPONSE_COMMENT;
*off = 1;
}
else
return _assuan_error (ctx, GPG_ERR_ASS_INV_RESPONSE);
return 0;
}
gpg_error_t
_assuan_read_from_server (assuan_context_t ctx, assuan_response_t *response,
int *off, int convey_comments)
{
gpg_error_t rc;
char *line;
int linelen;
do
{
*response = ASSUAN_RESPONSE_ERROR;
*off = 0;
rc = assuan_client_read_response (ctx, &line, &linelen);
if (!rc)
rc = assuan_client_parse_response (ctx, line, linelen, response, off);
}
while (!rc && *response == ASSUAN_RESPONSE_COMMENT && !convey_comments);
return rc;
}
/**
* assuan_transact:
* @ctx: The Assuan context
* @command: Command line to be send to the server
* @data_cb: Callback function for data lines
* @data_cb_arg: first argument passed to @data_cb
* @inquire_cb: Callback function for a inquire response
* @inquire_cb_arg: first argument passed to @inquire_cb
* @status_cb: Callback function for a status response
* @status_cb_arg: first argument passed to @status_cb
*
* FIXME: Write documentation
*
* Return value: 0 on success or an error code. The error code may be
* the one one returned by the server via error lines or from the
* callback functions. Take care: If a callback returns an error
* this function returns immediately with this error.
**/
gpg_error_t
assuan_transact (assuan_context_t ctx,
const char *command,
gpg_error_t (*data_cb)(void *, const void *, size_t),
void *data_cb_arg,
gpg_error_t (*inquire_cb)(void*, const char *),
void *inquire_cb_arg,
gpg_error_t (*status_cb)(void*, const char *),
void *status_cb_arg)
{
gpg_error_t rc;
assuan_response_t response;
int off;
char *line;
int linelen;
rc = assuan_write_line (ctx, command);
if (rc)
return rc;
if (*command == '#' || !*command)
return 0; /* Don't expect a response for a comment line. */
again:
rc = _assuan_read_from_server (ctx, &response, &off,
ctx->flags.convey_comments);
if (rc)
return rc; /* error reading from server */
line = ctx->inbound.line + off;
linelen = ctx->inbound.linelen - off;
if (response == ASSUAN_RESPONSE_ERROR)
rc = atoi (line);
else if (response == ASSUAN_RESPONSE_DATA)
{
if (!data_cb)
rc = _assuan_error (ctx, GPG_ERR_ASS_NO_DATA_CB);
else
{
rc = data_cb (data_cb_arg, line, linelen);
if (ctx->flags.confidential)
wipememory (ctx->inbound.line, LINELENGTH);
if (!rc)
goto again;
}
}
else if (response == ASSUAN_RESPONSE_INQUIRE)
{
if (!inquire_cb)
{
assuan_write_line (ctx, "END"); /* get out of inquire mode */
_assuan_read_from_server (ctx, &response, &off, 0); /* dummy read */
rc = _assuan_error (ctx, GPG_ERR_ASS_NO_INQUIRE_CB);
}
else
{
ctx->flags.confidential_inquiry = 0;
ctx->flags.in_inq_cb = 1;
rc = inquire_cb (inquire_cb_arg, line);
if (!rc)
rc = assuan_send_data (ctx, NULL, 0); /* flush and send END */
else
{ /* Flush and send CAN. */
/* Note that in this error case we don't want to return
an error code from sending the cancel. The dummy
read is to remove the response from the server which
we are not interested in. */
assuan_send_data (ctx, NULL, 1);
_assuan_read_from_server (ctx, &response, &off, 0);
}
if (ctx->flags.confidential_inquiry)
wipememory (ctx->outbound.data.line, LINELENGTH);
ctx->flags.confidential_inquiry = 0;
ctx->flags.in_inq_cb = 0;
if (!rc)
goto again;
}
}
else if (response == ASSUAN_RESPONSE_STATUS)
{
if (status_cb)
rc = status_cb (status_cb_arg, line);
if (!rc)
goto again;
}
else if (response == ASSUAN_RESPONSE_COMMENT && ctx->flags.convey_comments)
{
line -= off; /* Send line with the comment marker. */
if (status_cb)
rc = status_cb (status_cb_arg, line);
if (!rc)
goto again;
}
else if (response == ASSUAN_RESPONSE_END)
{
if (!data_cb)
rc = _assuan_error (ctx, GPG_ERR_ASS_NO_DATA_CB);
else
{
rc = data_cb (data_cb_arg, NULL, 0);
if (!rc)
goto again;
}
}
return rc;
}
diff --git a/src/context.c b/src/context.c
index 947df76..e41b60b 100644
--- a/src/context.c
+++ b/src/context.c
@@ -1,232 +1,252 @@
/* context.c - Context specific interface.
* 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 "assuan-defs.h"
#include "debug.h"
/* Set user-data in a context. */
void
assuan_set_pointer (assuan_context_t ctx, void *user_pointer)
{
TRACE1 (ctx, ASSUAN_LOG_CTX, "assuan_set_pointer", ctx,
"user_pointer=%p", user_pointer);
if (ctx)
ctx->user_pointer = user_pointer;
}
/* Get user-data in a context. */
void *
assuan_get_pointer (assuan_context_t ctx)
{
#if 0
/* This is called often. */
TRACE1 (ctx, ASSUAN_LOG_CTX, "assuan_get_pointer", ctx,
"ctx->user_pointer=%p", ctx ? ctx->user_pointer : NULL);
#endif
if (! ctx)
return NULL;
return ctx->user_pointer;
}
/* For context CTX, set the flag FLAG to VALUE. Values for flags
are usually 1 or 0 but certain flags might allow for other values;
see the description of the type assuan_flag_t for details. */
void
assuan_set_flag (assuan_context_t ctx, assuan_flag_t flag, int value)
{
TRACE2 (ctx, ASSUAN_LOG_CTX, "assuan_set_flag", ctx,
"flag=%i,value=%i", flag, value);
if (!ctx)
return;
switch (flag)
{
case ASSUAN_NO_WAITPID:
ctx->flags.no_waitpid = value;
break;
case ASSUAN_CONFIDENTIAL:
ctx->flags.confidential = value;
if (ctx->flags.in_inq_cb && value)
ctx->flags.confidential_inquiry = value;
break;
case ASSUAN_NO_FIXSIGNALS:
ctx->flags.no_fixsignals = value;
break;
case ASSUAN_CONVEY_COMMENTS:
ctx->flags.convey_comments = value;
break;
case ASSUAN_NO_LOGGING:
ctx->flags.no_logging = value;
break;
case ASSUAN_FORCE_CLOSE:
ctx->flags.force_close = 1;
break;
}
}
/* Return the VALUE of FLAG in context CTX. */
int
assuan_get_flag (assuan_context_t ctx, assuan_flag_t flag)
{
int res = 0;
TRACE_BEG1 (ctx, ASSUAN_LOG_CTX, "assuan_get_flag", ctx,
"flag=%i", flag);
if (! ctx)
return 0;
switch (flag)
{
case ASSUAN_NO_WAITPID:
res = ctx->flags.no_waitpid;
break;
case ASSUAN_CONFIDENTIAL:
res = ctx->flags.confidential;
break;
case ASSUAN_NO_FIXSIGNALS:
res = ctx->flags.no_fixsignals;
break;
case ASSUAN_CONVEY_COMMENTS:
res = ctx->flags.convey_comments;
break;
case ASSUAN_NO_LOGGING:
res = ctx->flags.no_logging;
break;
case ASSUAN_FORCE_CLOSE:
res = ctx->flags.force_close;
break;
}
return TRACE_SUC1 ("flag_value=%i", res);
}
/* Same as assuan_set_flag (ctx, ASSUAN_CONFIDENTIAL, 1). */
void
assuan_begin_confidential (assuan_context_t ctx)
{
assuan_set_flag (ctx, ASSUAN_CONFIDENTIAL, 1);
}
/* Same as assuan_set_flag (ctx, ASSUAN_CONFIDENTIAL, 0). */
void
assuan_end_confidential (assuan_context_t ctx)
{
assuan_set_flag (ctx, ASSUAN_CONFIDENTIAL, 0);
}
/* Set the system callbacks. */
void
assuan_ctx_set_system_hooks (assuan_context_t ctx,
assuan_system_hooks_t system_hooks)
{
TRACE2 (ctx, ASSUAN_LOG_CTX, "assuan_set_system_hooks", ctx,
"system_hooks=%p (version %i)", system_hooks,
system_hooks->version);
_assuan_system_hooks_copy (&ctx->system, system_hooks);
}
/* Set the IO monitor function. */
void assuan_set_io_monitor (assuan_context_t ctx,
assuan_io_monitor_t io_monitor, void *hook_data)
{
TRACE2 (ctx, ASSUAN_LOG_CTX, "assuan_set_io_monitor", ctx,
"io_monitor=%p,hook_data=%p", io_monitor, hook_data);
if (! ctx)
return;
ctx->io_monitor = io_monitor;
ctx->io_monitor_data = hook_data;
}
/* Store the error in the context so that the error sending function
can take out a descriptive text. Inside the assuan code, use the
macro set_error instead of this function. */
gpg_error_t
assuan_set_error (assuan_context_t ctx, gpg_error_t err, const char *text)
{
TRACE4 (ctx, ASSUAN_LOG_CTX, "assuan_set_error", ctx,
"err=%i (%s,%s),text=%s", err, gpg_strsource (err),
gpg_strerror (err), text?text:"(none)");
ctx->err_no = err;
ctx->err_str = text;
return err;
}
/* Return the PID of the peer or ASSUAN_INVALID_PID if not known.
This function works in some situations where assuan_get_peercred
fails. */
pid_t
assuan_get_pid (assuan_context_t ctx)
{
+#if defined(HAVE_W32_SYSTEM)
+ TRACE1 (ctx, ASSUAN_LOG_CTX, "assuan_get_pid", ctx,
+ "pid=%i", ctx ? ctx->process_id : -1);
+#else
TRACE1 (ctx, ASSUAN_LOG_CTX, "assuan_get_pid", ctx,
"pid=%i", ctx ? ctx->pid : -1);
+#endif
- return (ctx && ctx->pid) ? ctx->pid : ASSUAN_INVALID_PID;
+ if (!ctx)
+ return ASSUAN_INVALID_PID;
+
+ if (ctx->flags.is_server)
+#if defined(HAVE_W32_SYSTEM)
+ return ctx->process_id;
+#else
+ return ctx->pid;
+#endif
+ else
+ /*
+ * This use case of getting internal process reference by the
+ * application should be fixed. It's here, only for backward
+ * compatibility.
+ */
+ return ctx->server_proc;
}
/* Return user credentials. For getting the pid of the peer the
assuan_get_pid is usually better suited. */
gpg_error_t
assuan_get_peercred (assuan_context_t ctx, assuan_peercred_t *peercred)
{
TRACE (ctx, ASSUAN_LOG_CTX, "assuan_get_peercred", ctx);
if (!ctx)
return _assuan_error (ctx, GPG_ERR_ASS_INV_VALUE);
if (!ctx->peercred_valid)
return _assuan_error (ctx, GPG_ERR_ASS_GENERAL);
*peercred = &ctx->peercred;
return 0;
}
diff --git a/src/posix-types.inc.h b/src/posix-types.inc.h
index e3810b3..03f7007 100644
--- a/src/posix-types.inc.h
+++ b/src/posix-types.inc.h
@@ -1,24 +1,25 @@
## posix-types.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 struct msghdr *assuan_msghdr_t;
+typedef pid_t assuan_pid_t;
##EOF##
diff --git a/src/server.c b/src/server.c
index 5e96798..2ffbdbc 100644
--- a/src/server.c
+++ b/src/server.c
@@ -1,69 +1,71 @@
/* server.c - Interfaces for all assuan servers.
* 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 "assuan-defs.h"
#include "debug.h"
/* Disconnect and release the context CTX. */
void
_assuan_server_finish (assuan_context_t ctx)
{
if (ctx->inbound.fd != ASSUAN_INVALID_FD)
{
_assuan_close (ctx, ctx->inbound.fd);
if (ctx->inbound.fd == ctx->outbound.fd)
ctx->outbound.fd = ASSUAN_INVALID_FD;
ctx->inbound.fd = ASSUAN_INVALID_FD;
}
if (ctx->outbound.fd != ASSUAN_INVALID_FD)
{
_assuan_close (ctx, ctx->outbound.fd);
ctx->outbound.fd = ASSUAN_INVALID_FD;
}
- if (ctx->pid != ASSUAN_INVALID_PID && ctx->pid)
- {
- ctx->pid = ASSUAN_INVALID_PID;
- }
+
+#if defined(HAVE_W32_SYSTEM)
+ ctx->process_id = -1;
+#else
+ ctx->pid = ASSUAN_INVALID_PID;
+#endif
_assuan_uds_deinit (ctx);
_assuan_inquire_release (ctx);
}
void
_assuan_server_release (assuan_context_t ctx)
{
_assuan_server_finish (ctx);
_assuan_free (ctx, ctx->hello_line);
ctx->hello_line = NULL;
_assuan_free (ctx, ctx->okay_line);
ctx->okay_line = NULL;
_assuan_free (ctx, ctx->cmdtbl);
ctx->cmdtbl = NULL;
}
diff --git a/src/system-posix.c b/src/system-posix.c
index 0f7732a..5f3a7f2 100644
--- a/src/system-posix.c
+++ b/src/system-posix.c
@@ -1,451 +1,451 @@
/* system-posix.c - System support functions.
* Copyright (C) 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+
*/
#ifdef HAVE_CONFIG_H
#include
#endif
#include
#include
#ifdef HAVE_STDINT_H
# include
#endif
/* Solaris 8 needs sys/types.h before time.h. */
#include
#include
#include
#include
#ifdef HAVE_GETRLIMIT
# include
# include
#endif /*HAVE_GETRLIMIT*/
#if __linux__
# include
#endif /*__linux__ */
#include "assuan-defs.h"
#include "debug.h"
assuan_fd_t
assuan_fdopen (int fd)
{
return dup (fd);
}
/* Sleep for the given number of microseconds. Default
implementation. */
void
__assuan_usleep (assuan_context_t ctx, unsigned int usec)
{
if (! usec)
return;
#ifdef HAVE_NANOSLEEP
{
struct timespec req;
struct timespec rem;
req.tv_sec = usec / 1000000;
req.tv_nsec = (usec % 1000000) * 1000;
while (nanosleep (&req, &rem) < 0 && errno == EINTR)
req = rem;
}
#else
{
struct timeval tv;
tv.tv_sec = usec / 1000000;
tv.tv_usec = usec % 1000000;
select (0, NULL, NULL, NULL, &tv);
}
#endif
}
/* Create a pipe with one inheritable end. Easy for Posix. */
int
__assuan_pipe (assuan_context_t ctx, assuan_fd_t fd[2], int inherit_idx)
{
return pipe (fd);
}
/* Close the given file descriptor, created with _assuan_pipe or one
of the socket functions. Easy for Posix. */
int
__assuan_close (assuan_context_t ctx, assuan_fd_t fd)
{
return close (fd);
}
ssize_t
__assuan_read (assuan_context_t ctx, assuan_fd_t fd, void *buffer, size_t size)
{
return read (fd, buffer, size);
}
ssize_t
__assuan_write (assuan_context_t ctx, assuan_fd_t fd, const void *buffer,
size_t size)
{
return write (fd, buffer, size);
}
int
__assuan_recvmsg (assuan_context_t ctx, assuan_fd_t fd, assuan_msghdr_t msg,
int flags)
{
int ret;
do
ret = recvmsg (fd, msg, flags);
while (ret == -1 && errno == EINTR);
return ret;
}
int
__assuan_sendmsg (assuan_context_t ctx, assuan_fd_t fd, assuan_msghdr_t msg,
int flags)
{
int ret;
do
ret = sendmsg (fd, msg, flags);
while (ret == -1 && errno == EINTR);
return ret;
}
static int
writen (int fd, const char *buffer, size_t length)
{
while (length)
{
int nwritten = write (fd, buffer, length);
if (nwritten < 0)
{
if (errno == EINTR)
continue;
return -1; /* write error */
}
length -= nwritten;
buffer += nwritten;
}
return 0; /* okay */
}
/* Return the maximum number of currently allowed open file
* descriptors. */
static int
get_max_fds (void)
{
int max_fds = -1;
#ifdef HAVE_GETRLIMIT
struct rlimit rl;
/* Under Linux we can figure out the highest used file descriptor by
* reading /proc/PID/fd. This is in the common cases much faster
* than for example doing 4096 close calls where almost all of them
* will fail. We use the same code in GnuPG and measured this: On a
* system with a limit of 4096 files and only 8 files open with the
* highest number being 10, we speedup close_all_fds from 125ms to
* 0.4ms including the readdir.
*
* Another option would be to close the file descriptors as returned
* from reading that directory - however then we need to snapshot
* that list before starting to close them. */
#ifdef __linux__
{
DIR *dir = NULL;
struct dirent *dir_entry;
const char *s;
int x;
dir = opendir ("/proc/self/fd");
if (dir)
{
while ((dir_entry = readdir (dir)))
{
s = dir_entry->d_name;
if ( *s < '0' || *s > '9')
continue;
x = atoi (s);
if (x > max_fds)
max_fds = x;
}
closedir (dir);
}
if (max_fds != -1)
return max_fds + 1;
}
#endif /* __linux__ */
# ifdef RLIMIT_NOFILE
if (!getrlimit (RLIMIT_NOFILE, &rl))
max_fds = rl.rlim_max;
# endif
# ifdef RLIMIT_OFILE
if (max_fds == -1 && !getrlimit (RLIMIT_OFILE, &rl))
max_fds = rl.rlim_max;
# endif
#endif /*HAVE_GETRLIMIT*/
#ifdef _SC_OPEN_MAX
if (max_fds == -1)
{
long int scres = sysconf (_SC_OPEN_MAX);
if (scres >= 0)
max_fds = scres;
}
#endif
#ifdef _POSIX_OPEN_MAX
if (max_fds == -1)
max_fds = _POSIX_OPEN_MAX;
#endif
#ifdef OPEN_MAX
if (max_fds == -1)
max_fds = OPEN_MAX;
#endif
if (max_fds == -1)
max_fds = 256; /* Arbitrary limit. */
/* AIX returns INT32_MAX instead of a proper value. We assume that
this is always an error and use a more reasonable limit. */
#ifdef INT32_MAX
if (max_fds == INT32_MAX)
max_fds = 256;
#endif
return max_fds;
}
int
-__assuan_spawn (assuan_context_t ctx, pid_t *r_pid, const char *name,
+__assuan_spawn (assuan_context_t ctx, assuan_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 pid;
pid = fork ();
if (pid < 0)
return -1;
if (pid == 0)
{
/* Child process (server side). */
int i;
int n;
char errbuf[512];
int *fdp;
int fdnul;
if (atfork)
atfork (atforkvalue, 0);
fdnul = open ("/dev/null", O_WRONLY);
if (fdnul == -1)
{
TRACE1 (ctx, ASSUAN_LOG_SYSIO, "__assuan_spawn", ctx,
"can't open `/dev/null': %s", strerror (errno));
_exit (4);
}
/* Dup handles to stdin/stdout. */
if (fd_out != STDOUT_FILENO)
{
if (dup2 (fd_out == ASSUAN_INVALID_FD ? fdnul : fd_out,
STDOUT_FILENO) == -1)
{
TRACE1 (ctx, ASSUAN_LOG_SYSIO, "__assuan_spawn", ctx,
"dup2 failed in child: %s", strerror (errno));
_exit (4);
}
}
if (fd_in != STDIN_FILENO)
{
if (dup2 (fd_in == ASSUAN_INVALID_FD ? fdnul : fd_in,
STDIN_FILENO) == -1)
{
TRACE1 (ctx, ASSUAN_LOG_SYSIO, "__assuan_spawn", ctx,
"dup2 failed in child: %s", strerror (errno));
_exit (4);
}
}
/* Dup stderr to /dev/null unless it is in the list of FDs to be
passed to the child. */
fdp = fd_child_list;
if (fdp)
{
for (; *fdp != -1 && *fdp != STDERR_FILENO; fdp++)
;
}
if (!fdp || *fdp == -1)
{
if (dup2 (fdnul, STDERR_FILENO) == -1)
{
TRACE1 (ctx, ASSUAN_LOG_SYSIO, "pipe_connect_unix", ctx,
"dup2(dev/null, 2) failed: %s", strerror (errno));
_exit (4);
}
}
close (fdnul);
/* Close all files which will not be duped and are not in the
fd_child_list. */
n = get_max_fds ();
for (i = 0; i < n; i++)
{
if (i == STDIN_FILENO || i == STDOUT_FILENO || i == STDERR_FILENO)
continue;
fdp = fd_child_list;
if (fdp)
{
while (*fdp != -1 && *fdp != i)
fdp++;
}
if (!(fdp && *fdp != -1))
close (i);
}
gpg_err_set_errno (0);
if (! name)
{
/* No name and no args given, thus we don't do an exec
but continue the forked process. */
*argv = "server";
/* FIXME: Cleanup. */
return 0;
}
execv (name, (char *const *) argv);
/* oops - use the pipe to tell the parent about it */
snprintf (errbuf, sizeof(errbuf)-1,
"ERR %d can't exec `%s': %.50s\n",
_assuan_error (ctx, GPG_ERR_ASS_SERVER_START),
name, strerror (errno));
errbuf[sizeof(errbuf)-1] = 0;
writen (1, errbuf, strlen (errbuf));
_exit (4);
}
if (! name)
*argv = "client";
*r_pid = pid;
return 0;
}
/* 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 nowait,
+assuan_pid_t
+__assuan_waitpid (assuan_context_t ctx, assuan_pid_t pid, int nowait,
int *status, int options)
{
/* We can't just release the PID, a waitpid is mandatory. But
NOWAIT in POSIX systems just means the caller already did the
waitpid for this child. */
if (! nowait)
return waitpid (pid, NULL, 0);
return 0;
}
int
__assuan_socketpair (assuan_context_t ctx, int namespace, int style,
int protocol, assuan_fd_t filedes[2])
{
return socketpair (namespace, style, protocol, filedes);
}
int
__assuan_socket (assuan_context_t ctx, int namespace, int style, int protocol)
{
return socket (namespace, style, protocol);
}
int
__assuan_connect (assuan_context_t ctx, int sock, struct sockaddr *addr,
socklen_t length)
{
return connect (sock, addr, length);
}
/* The default system hooks for assuan contexts. */
struct assuan_system_hooks _assuan_system_hooks =
{
0,
__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-w32.c b/src/system-w32.c
index 28d3564..415cd63 100644
--- a/src/system-w32.c
+++ b/src/system-w32.c
@@ -1,657 +1,657 @@
/* system-w32.c - System support functions for Windows.
* Copyright (C) 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+
*/
#ifdef HAVE_CONFIG_H
#include
#endif
#include
#include
#include
#include
#include "assuan-defs.h"
#include "debug.h"
assuan_fd_t
assuan_fdopen (int fd)
{
assuan_fd_t ifd = (assuan_fd_t) _get_osfhandle (fd);
assuan_fd_t ofd;
if (! DuplicateHandle(GetCurrentProcess(), ifd,
GetCurrentProcess(), &ofd, 0,
TRUE, DUPLICATE_SAME_ACCESS))
{
gpg_err_set_errno (EIO);
return ASSUAN_INVALID_FD;
}
return ofd;
}
/* Sleep for the given number of microseconds. Default
implementation. */
void
__assuan_usleep (assuan_context_t ctx, unsigned int usec)
{
if (!usec)
return;
Sleep (usec / 1000);
}
/* Three simple wrappers, only used because thes function are named in
the def file. */
HANDLE
_assuan_w32ce_prepare_pipe (int *r_rvid, int write_end)
{
(void)r_rvid;
(void)write_end;
return INVALID_HANDLE_VALUE;
}
HANDLE
_assuan_w32ce_finish_pipe (int rvid, int write_end)
{
(void)rvid;
(void)write_end;
return INVALID_HANDLE_VALUE;
}
DWORD
_assuan_w32ce_create_pipe (HANDLE *read_hd, HANDLE *write_hd,
LPSECURITY_ATTRIBUTES sec_attr, DWORD size)
{
return CreatePipe (read_hd, write_hd, sec_attr, size);
}
/* Create a pipe with one inheritable end. Default implementation. */
int
__assuan_pipe (assuan_context_t ctx, assuan_fd_t fd[2], int inherit_idx)
{
HANDLE rh;
HANDLE wh;
HANDLE th;
SECURITY_ATTRIBUTES sec_attr;
memset (&sec_attr, 0, sizeof (sec_attr));
sec_attr.nLength = sizeof (sec_attr);
sec_attr.bInheritHandle = FALSE;
if (!CreatePipe (&rh, &wh, &sec_attr, 0))
{
TRACE1 (ctx, ASSUAN_LOG_SYSIO, "__assuan_pipe", ctx,
"CreatePipe failed: %s", _assuan_w32_strerror (ctx, -1));
gpg_err_set_errno (EIO);
return -1;
}
if (! DuplicateHandle (GetCurrentProcess(), (inherit_idx == 0) ? rh : wh,
GetCurrentProcess(), &th, 0,
TRUE, DUPLICATE_SAME_ACCESS ))
{
TRACE1 (ctx, ASSUAN_LOG_SYSIO, "__assuan_pipe", ctx,
"DuplicateHandle failed: %s", _assuan_w32_strerror (ctx, -1));
CloseHandle (rh);
CloseHandle (wh);
gpg_err_set_errno (EIO);
return -1;
}
if (inherit_idx == 0)
{
CloseHandle (rh);
rh = th;
}
else
{
CloseHandle (wh);
wh = th;
}
fd[0] = rh;
fd[1] = wh;
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;
if (ctx->flags.is_socket)
{
rc = closesocket (HANDLE2SOCKET(fd));
if (rc)
gpg_err_set_errno ( _assuan_sock_wsa2errno (WSAGetLastError ()) );
}
else
{
rc = CloseHandle (fd);
if (rc)
/* FIXME. */
gpg_err_set_errno (EIO);
}
return rc;
}
/* Get a file HANDLE for other end to send, from MY_HANDLE. */
static gpg_error_t
get_file_handle (assuan_fd_t my_handle, int process_id, HANDLE *r_handle)
{
HANDLE prochandle, newhandle;
prochandle = OpenProcess (PROCESS_DUP_HANDLE, FALSE, process_id);
if (!prochandle)
return gpg_error (GPG_ERR_ASS_PARAMETER);/*FIXME: error*/
if (!DuplicateHandle (GetCurrentProcess (), my_handle, prochandle, &newhandle,
0, TRUE, DUPLICATE_SAME_ACCESS))
{
CloseHandle (prochandle);
return gpg_error (GPG_ERR_ASS_PARAMETER);/*FIXME: error*/
}
CloseHandle (prochandle);
*r_handle = newhandle;
return 0;
}
/* Send an FD (which means Windows HANDLE) to the peer. */
gpg_error_t
w32_fdpass_send (assuan_context_t ctx, assuan_fd_t fd)
{
char fdpass_msg[256];
int res;
HANDLE file_handle;
gpg_error_t err;
err = get_file_handle (fd, ctx->process_id, &file_handle);
if (err)
return err;
res = snprintf (fdpass_msg, sizeof (fdpass_msg), "SENDFD %p", file_handle);
if (res < 0)
{
CloseHandle (file_handle);
return gpg_error (GPG_ERR_ASS_PARAMETER);/*FIXME: error*/
}
err = assuan_transact (ctx, fdpass_msg, NULL, NULL, NULL, NULL, NULL, NULL);
return err;
}
/* Receive a HANDLE from the peer and turn it into an FD. */
gpg_error_t
w32_fdpass_recv (assuan_context_t ctx, assuan_fd_t *fd)
{
int i;
if (!ctx->uds.pendingfdscount)
{
TRACE0 (ctx, ASSUAN_LOG_SYSIO, "w32_receivefd", ctx,
"no pending file descriptors");
return _assuan_error (ctx, GPG_ERR_ASS_GENERAL);
}
*fd = ctx->uds.pendingfds[0];
for (i=1; i < ctx->uds.pendingfdscount; i++)
ctx->uds.pendingfds[i-1] = ctx->uds.pendingfds[i];
ctx->uds.pendingfdscount--;
TRACE1 (ctx, ASSUAN_LOG_SYSIO, "w32_fdpass_recv", ctx,
"received fd: %p", ctx->uds.pendingfds[0]);
return 0;
}
ssize_t
__assuan_read (assuan_context_t ctx, assuan_fd_t fd, void *buffer, size_t size)
{
int res;
int ec = 0;
if (ctx->flags.is_socket)
{
int tries = 3;
again:
ec = 0;
res = recv (HANDLE2SOCKET (fd), buffer, size, 0);
if (res == -1)
ec = WSAGetLastError ();
if (ec == WSAEWOULDBLOCK && tries--)
{
/* EAGAIN: Use select to wait for resources and try again.
We do this 3 times and then give up. The higher level
layer then needs to take care of EAGAIN. No need to
specify a timeout - the socket is not expected to be in
blocking mode. */
fd_set fds;
FD_ZERO (&fds);
FD_SET (HANDLE2SOCKET (fd), &fds);
select (0, &fds, NULL, NULL, NULL);
goto again;
}
}
else
{
DWORD nread = 0;
if (!ReadFile (fd, buffer, size, &nread, NULL))
{
res = -1;
ec = GetLastError ();
}
else
res = nread;
}
if (res == -1)
{
switch (ec)
{
case WSAENOTSOCK:
gpg_err_set_errno (EBADF);
break;
case WSAEWOULDBLOCK:
gpg_err_set_errno (EAGAIN);
break;
case WSAECONNRESET: /* Due to the use of recv. */
case ERROR_BROKEN_PIPE:
gpg_err_set_errno (EPIPE);
break;
default:
gpg_err_set_errno (EIO);
break;
}
}
return res;
}
ssize_t
__assuan_write (assuan_context_t ctx, assuan_fd_t fd, const void *buffer,
size_t size)
{
int res;
int ec = 0;
if (ctx->flags.is_socket)
{
int tries = 3;
again:
ec = 0;
res = send (HANDLE2SOCKET (fd), buffer, size, 0);
if (res == -1)
ec = WSAGetLastError ();
if (ec == WSAEWOULDBLOCK && tries--)
{
/* EAGAIN: Use select to wait for resources and try again.
We do this 3 times and then give up. The higher level
layer then needs to take care of EAGAIN. No need to
specify a timeout - the socket is not expected to be in
blocking mode. */
fd_set fds;
FD_ZERO (&fds);
FD_SET (HANDLE2SOCKET (fd), &fds);
select (0, NULL, &fds, NULL, NULL);
goto again;
}
}
else
{
DWORD nwrite;
if (!WriteFile (fd, buffer, size, &nwrite, NULL))
{
res = -1;
ec = GetLastError ();
}
else
res = (int)nwrite;
}
if (res == -1)
{
switch (ec)
{
case WSAENOTSOCK:
gpg_err_set_errno (EBADF);
break;
case WSAEWOULDBLOCK:
gpg_err_set_errno (EAGAIN);
break;
case ERROR_BROKEN_PIPE:
case ERROR_NO_DATA:
gpg_err_set_errno (EPIPE);
break;
default:
gpg_err_set_errno (EIO);
break;
}
}
return 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,
char **cmdline)
{
int i, n;
const char *s;
char *buf, *p;
*cmdline = NULL;
n = 0;
for (i=0; (s = argv[i]); i++)
{
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;
for (i = 0; argv[i]; i++)
{
if (i)
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,
+__assuan_spawn (assuan_context_t ctx, assuan_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)
{
SECURITY_ATTRIBUTES sec_attr;
PROCESS_INFORMATION pi =
{
NULL, /* Returns process handle. */
0, /* Returns primary thread handle. */
0, /* Returns pid. */
0 /* Returns tid. */
};
STARTUPINFOW si;
assuan_fd_t fd;
assuan_fd_t *fdp;
char *cmdline;
wchar_t *wcmdline = NULL;
wchar_t *wname = NULL;
HANDLE nullfd = INVALID_HANDLE_VALUE;
int rc;
/* fixme: Actually we should set the "_assuan_pipe_connect_pid" env
variable. However this requires us to write a full environment
handler, because the strings are expected in sorted order. The
suggestion given in the MS Reference Library, to save the old
value, change it, create process and restore it, is not thread
safe. */
/* Build the command line. */
if (build_w32_commandline (ctx, argv, &cmdline))
return -1;
/* Start the process. */
memset (&sec_attr, 0, sizeof sec_attr);
sec_attr.nLength = sizeof sec_attr;
sec_attr.bInheritHandle = FALSE;
memset (&si, 0, sizeof si);
si.cb = sizeof (si);
si.dwFlags = STARTF_USESTDHANDLES;
/* FIXME: Dup to nul if ASSUAN_INVALID_FD. */
si.hStdInput = fd_in;
si.hStdOutput = fd_out;
/* Dup stderr to /dev/null unless it is in the list of FDs to be
passed to the child. */
fd = assuan_fd_from_posix_fd (fileno (stderr));
fdp = fd_child_list;
if (fdp)
{
for (; *fdp != ASSUAN_INVALID_FD && *fdp != fd; fdp++)
;
}
if (!fdp || *fdp == ASSUAN_INVALID_FD)
{
nullfd = CreateFileW (L"nul", GENERIC_WRITE,
FILE_SHARE_READ | FILE_SHARE_WRITE,
NULL, OPEN_EXISTING, 0, NULL);
if (nullfd == INVALID_HANDLE_VALUE)
{
TRACE1 (ctx, ASSUAN_LOG_SYSIO, "__assuan_spawn", ctx,
"can't open `nul': %s", _assuan_w32_strerror (ctx, -1));
_assuan_free (ctx, cmdline);
gpg_err_set_errno (EIO);
return -1;
}
si.hStdError = nullfd;
}
else
si.hStdError = fd;
/* Note: We inherit all handles flagged as inheritable. This seems
to be a security flaw but there seems to be no way of selecting
handles to inherit. A fix for this would be to use a helper
process like we have in gpgme.
Take care: CreateProcessW may modify wpgmname */
/* _assuan_log_printf ("CreateProcess, path=`%s' cmdline=`%s'\n", */
/* name, cmdline); */
if (name && !(wname = _assuan_utf8_to_wchar (name)))
rc = 0;
else if (!(wcmdline = _assuan_utf8_to_wchar (cmdline)))
rc = 0;
else
rc = CreateProcessW (wname, /* Program to start. */
wcmdline, /* Command line arguments. */
&sec_attr, /* Process security attributes. */
&sec_attr, /* Thread security attributes. */
TRUE, /* Inherit handles. */
(CREATE_DEFAULT_ERROR_MODE
| ((flags & 128)? DETACHED_PROCESS : 0)
| GetPriorityClass (GetCurrentProcess ())
| CREATE_SUSPENDED), /* Creation flags. */
NULL, /* Environment. */
NULL, /* Use current drive/directory. */
&si, /* Startup information. */
&pi /* Returns process information. */
);
if (!rc)
{
TRACE1 (ctx, ASSUAN_LOG_SYSIO, "pipe_connect_w32", ctx,
"CreateProcess failed%s: %s", _assuan_w32_strerror (ctx, -1));
free (wname);
free (wcmdline);
_assuan_free (ctx, cmdline);
if (nullfd != INVALID_HANDLE_VALUE)
CloseHandle (nullfd);
gpg_err_set_errno (EIO);
return -1;
}
free (wname);
free (wcmdline);
_assuan_free (ctx, cmdline);
if (nullfd != INVALID_HANDLE_VALUE)
CloseHandle (nullfd);
ResumeThread (pi.hThread);
CloseHandle (pi.hThread);
/* _assuan_log_printf ("CreateProcess ready: hProcess=%p hThread=%p" */
/* " dwProcessID=%d dwThreadId=%d\n", */
/* pi.hProcess, pi.hThread, */
/* (int) pi.dwProcessId, (int) pi.dwThreadId); */
- *r_pid = (pid_t) pi.hProcess;
+ *r_pid = (assuan_pid_t) pi.hProcess;
/* No need to modify peer process, as we don't change the handle
names. However this also means we are not safe, as we inherit
too many handles. Should use approach similar to gpgme and glib
using a helper process. */
return 0;
}
/* 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 nowait,
+assuan_pid_t
+__assuan_waitpid (assuan_context_t ctx, assuan_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;
}
assuan_fd_t
__assuan_socket (assuan_context_t ctx, int domain, int type, int proto)
{
assuan_fd_t res;
res = SOCKET2HANDLE (socket (domain, type, proto));
if (res == SOCKET2HANDLE (INVALID_SOCKET))
gpg_err_set_errno (_assuan_sock_wsa2errno (WSAGetLastError ()));
return res;
}
int
__assuan_connect (assuan_context_t ctx, assuan_fd_t sock,
struct sockaddr *addr, socklen_t length)
{
int res;
res = connect (HANDLE2SOCKET (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 =
{
0,
__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 6638ece..9807d9d 100644
--- a/src/system.c
+++ b/src/system.c
@@ -1,539 +1,539 @@
/* 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)
{
if (src == NULL)
return;
/* 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);
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;
}
}
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
int 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
{
int 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
int 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
{
int 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,
+_assuan_spawn (assuan_context_t ctx, assuan_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,
+assuan_pid_t
+_assuan_waitpid (assuan_context_t ctx, assuan_pid_t pid, int action,
int *status, int options)
{
#if DEBUG_SYSIO
- pid_t res;
+ assuan_pid_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
{
- pid_t res;
+ assuan_pid_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/w32-types.inc.h b/src/w32-types.inc.h
index 552ffd0..b60cd2c 100644
--- a/src/w32-types.inc.h
+++ b/src/w32-types.inc.h
@@ -1,35 +1,41 @@
## w32-types.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_msghdr_t;
#ifdef _MSC_VER
# ifdef _WIN64
typedef long long ssize_t;
typedef long long pid_t;
#else
typedef long ssize_t;
typedef int pid_t;
# endif
#endif
+#ifdef _WIN64
+ typedef unsigned long long assuan_pid_t;
+#else
+ typedef unsigned long assuan_pid_t;
+#endif
+
##EOF##