1
/* assuan-domain-connect.c - Assuan unix domain socket based client
2
* Copyright (C) 2002, 2003 Free Software Foundation, Inc.
4
* This file is part of Assuan.
6
* Assuan is free software; you can redistribute it and/or modify it
7
* under the terms of the GNU Lesser General Public License as
8
* published by the Free Software Foundation; either version 2.1 of
9
* the License, or (at your option) any later version.
11
* Assuan is distributed in the hope that it will be useful, but
12
* WITHOUT ANY WARRANTY; without even the implied warranty of
13
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14
* Lesser General Public License for more details.
16
* You should have received a copy of the GNU Lesser General Public
17
* License along with this program; if not, write to the Free Software
18
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA
29
#include <sys/types.h>
30
#ifndef HAVE_W32_SYSTEM
31
#include <sys/socket.h>
44
#include "assuan-defs.h"
48
# define PF_LOCAL PF_UNIX
50
# define PF_LOCAL AF_UNIX
53
# define AF_LOCAL AF_UNIX
59
do_deinit (assuan_context_t ctx)
61
if (ctx->inbound.fd != -1)
62
_assuan_close (ctx->inbound.fd);
64
ctx->outbound.fd = -1;
66
if (ctx->domainbuffer)
68
assert (ctx->domainbufferallocated);
69
free (ctx->domainbuffer);
76
assert (ctx->pendingfdscount > 0);
77
for (i = 0; i < ctx->pendingfdscount; i ++)
78
_assuan_close (ctx->pendingfds[i]);
80
free (ctx->pendingfds);
83
unlink (ctx->myaddr.sun_path);
87
/* Read from the socket server. */
89
domain_reader (assuan_context_t ctx, void *buf, size_t buflen)
91
int len = ctx->domainbuffersize;
93
#ifndef HAVE_W32_SYSTEM
96
/* No data is buffered. */
100
struct sockaddr_un sender;
108
memset (&msg, 0, sizeof (msg));
112
msg.msg_name = &sender;
113
msg.msg_namelen = sizeof (struct sockaddr_un);
114
msg.msg_iov = &iovec;
116
iovec.iov_base = ctx->domainbuffer;
117
iovec.iov_len = ctx->domainbufferallocated;
118
msg.msg_control = &cmsg;
119
msg.msg_controllen = sizeof cmsg;
121
/* Peek first: if the buffer we have is too small then it
122
will be truncated. */
123
len = recvmsg (ctx->inbound.fd, &msg, MSG_PEEK);
126
printf ("domain_reader: %m\n");
130
if (strcmp (ctx->serveraddr.sun_path,
131
((struct sockaddr_un *) msg.msg_name)->sun_path) != 0)
133
/* XXX: Arg. Not from whom we expected! What do we
134
want to do? Should we just ignore it? Either way,
135
we still need to consume the message. */
139
if (msg.msg_flags & MSG_TRUNC)
140
/* Enlarge the buffer and try again. */
142
int size = ctx->domainbufferallocated;
154
free (ctx->domainbuffer);
155
ctx->domainbuffer = tmp;
156
ctx->domainbufferallocated = size;
159
/* We have enough space! */
163
/* Now we have to actually consume it (remember, we only
165
msg.msg_name = &sender;
166
msg.msg_namelen = sizeof (struct sockaddr_un);
167
msg.msg_iov = &iovec;
169
iovec.iov_base = ctx->domainbuffer;
170
iovec.iov_len = ctx->domainbufferallocated;
171
msg.msg_control = &cmsg;
172
msg.msg_controllen = sizeof cmsg;
174
if (strcmp (ctx->serveraddr.sun_path,
175
((struct sockaddr_un *) msg.msg_name)->sun_path) != 0)
177
/* XXX: Arg. Not from whom we expected! What do we want to
178
do? Should we just ignore it? We shall do the latter
180
_assuan_log_printf ("not setup to receive messages from `%s'\n",
181
((struct sockaddr_un *) msg.msg_name)->sun_path);
185
len = recvmsg (ctx->inbound.fd, &msg, 0);
188
_assuan_log_printf ("domain_reader: %s\n", strerror (errno));
192
ctx->domainbuffersize = len;
193
ctx->domainbufferoffset = 0;
195
if (sizeof (cmsg) == msg.msg_controllen)
196
/* We received a file descriptor. */
200
tmp = realloc (ctx->pendingfds,
201
sizeof (int) * (ctx->pendingfdscount + 1));
204
_assuan_log_printf ("domain_reader: %s\n", strerror (errno));
208
ctx->pendingfds = tmp;
209
ctx->pendingfds[ctx->pendingfdscount++]
210
= *(int *) CMSG_DATA (&cmsg.hdr);
212
_assuan_log_printf ("received file descriptor %d from peer\n",
213
ctx->pendingfds[ctx->pendingfdscount - 1]);
220
len = recvfrom (ctx->inbound.fd, buf, buflen, 0, NULL, NULL);
223
/* Return some data to the user. */
226
/* We have more than the user requested. */
229
memcpy (buf, ctx->domainbuffer + ctx->domainbufferoffset, len);
230
ctx->domainbuffersize -= len;
231
assert (ctx->domainbuffersize >= 0);
232
ctx->domainbufferoffset += len;
233
assert (ctx->domainbufferoffset <= ctx->domainbufferallocated);
238
/* Write to the domain server. */
240
domain_writer (assuan_context_t ctx, const void *buf, size_t buflen)
242
#ifndef HAVE_W32_SYSTEM
247
memset (&msg, 0, sizeof (msg));
249
msg.msg_name = &ctx->serveraddr;
250
msg.msg_namelen = offsetof (struct sockaddr_un, sun_path)
251
+ strlen (ctx->serveraddr.sun_path) + 1;
254
msg.msg_iov = &iovec;
255
iovec.iov_base = (void *) buf;
256
iovec.iov_len = buflen;
258
msg.msg_controllen = 0;
260
len = sendmsg (ctx->outbound.fd, &msg, 0);
262
_assuan_log_printf ("domain_writer: %s\n", strerror (errno));
266
len = sendto (ctx->outbound.fd, buf, buflen, 0,
267
(struct sockaddr *)&ctx->serveraddr,
268
sizeof (struct sockaddr_in));
273
static assuan_error_t
274
domain_sendfd (assuan_context_t ctx, int fd)
276
#ifndef HAVE_W32_SYSTEM
286
memset (&msg, 0, sizeof (msg));
288
msg.msg_name = &ctx->serveraddr;
289
msg.msg_namelen = offsetof (struct sockaddr_un, sun_path)
290
+ strlen (ctx->serveraddr.sun_path) + 1;
295
cmsg.hdr.cmsg_level = SOL_SOCKET;
296
cmsg.hdr.cmsg_type = SCM_RIGHTS;
297
cmsg.hdr.cmsg_len = sizeof (cmsg);
299
msg.msg_control = &cmsg;
300
msg.msg_controllen = sizeof (cmsg);
302
*(int *) CMSG_DATA (&cmsg.hdr) = fd;
304
len = sendmsg (ctx->outbound.fd, &msg, 0);
307
_assuan_log_printf ("domain_sendfd: %s\n", strerror (errno));
308
return ASSUAN_General_Error;
317
static assuan_error_t
318
domain_receivefd (assuan_context_t ctx, int *fd)
320
#ifndef HAVE_W32_SYSTEM
321
if (ctx->pendingfds == 0)
323
_assuan_log_printf ("no pending file descriptors!\n");
324
return ASSUAN_General_Error;
327
*fd = ctx->pendingfds[0];
328
if (-- ctx->pendingfdscount == 0)
330
free (ctx->pendingfds);
336
memmove (ctx->pendingfds, ctx->pendingfds + 1,
337
ctx->pendingfdscount * sizeof (int));
338
ctx->pendingfds = realloc (ctx->pendingfds,
339
ctx->pendingfdscount * sizeof (int));
347
/* Make a connection to the Unix domain socket NAME and return a new
348
Assuan context in CTX. SERVER_PID is currently not used but may
349
become handy in the future. */
351
_assuan_domain_init (assuan_context_t *r_ctx, int rendezvousfd, pid_t peer)
353
static struct assuan_io io = { domain_reader, domain_writer,
354
domain_sendfd, domain_receivefd };
357
assuan_context_t ctx;
363
return ASSUAN_Invalid_Value;
366
err = _assuan_new_context (&ctx);
370
/* Save it in case we need it later. */
373
/* Override the default (NOP) handlers. */
374
ctx->deinit_handler = do_deinit;
376
/* Setup the socket. */
378
fd = _assuan_sock_new (PF_LOCAL, SOCK_DGRAM, 0);
381
_assuan_log_printf ("can't create socket: %s\n", strerror (errno));
382
_assuan_release_context (ctx);
383
return ASSUAN_General_Error;
386
ctx->inbound.fd = fd;
387
ctx->outbound.fd = fd;
389
/* And the io buffers. */
392
ctx->domainbuffer = 0;
393
ctx->domainbufferoffset = 0;
394
ctx->domainbuffersize = 0;
395
ctx->domainbufferallocated = 0;
397
ctx->pendingfdscount = 0;
399
/* Get usable name and bind to it. */
401
for (tries = 0; tries < TMP_MAX; tries ++)
406
/* XXX: L_tmpnam must be shorter than sizeof (sun_path)! */
407
assert (L_tmpnam < sizeof (ctx->myaddr.sun_path));
409
/* XXX: W32 tmpnam is broken */
413
_assuan_log_printf ("cannot determine an appropriate temporary file "
414
"name. DoS in progress?\n");
415
_assuan_release_context (ctx);
417
return ASSUAN_General_Error;
420
memset (&ctx->myaddr, 0, sizeof ctx->myaddr);
421
ctx->myaddr.sun_family = AF_LOCAL;
422
len = strlen (buf) + 1;
423
memcpy (ctx->myaddr.sun_path, buf, len);
424
len += offsetof (struct sockaddr_un, sun_path);
426
err = _assuan_sock_bind (fd, (struct sockaddr *) &ctx->myaddr, len);
433
_assuan_log_printf ("can't bind to `%s': %s\n", ctx->myaddr.sun_path,
435
_assuan_release_context (ctx);
437
return ASSUAN_Connect_Failed;
440
/* Rendezvous with our peer. */
445
fp = fdopen (rendezvousfd, "w+");
448
_assuan_log_printf ("can't open rendezvous port: %s\n", strerror (errno));
449
return ASSUAN_Connect_Failed;
452
/* Send our address. */
453
fprintf (fp, "%s\n", ctx->myaddr.sun_path);
456
/* And receive our peer's. */
457
memset (&ctx->serveraddr, 0, sizeof ctx->serveraddr);
458
for (p = ctx->serveraddr.sun_path;
459
p < (ctx->serveraddr.sun_path
460
+ sizeof ctx->serveraddr.sun_path - 1);
470
ctx->serveraddr.sun_family = AF_LOCAL;
478
assuan_domain_connect (assuan_context_t * r_ctx, int rendezvousfd, pid_t peer)
483
aerr = _assuan_domain_init (r_ctx, rendezvousfd, peer);
487
/* Initial handshake. */
488
aerr = _assuan_read_from_server (*r_ctx, &okay, &off);
490
_assuan_log_printf ("can't connect to server: %s\n",
491
assuan_strerror (aerr));
494
_assuan_log_printf ("can't connect to server: `");
495
_assuan_log_sanitized_string ((*r_ctx)->inbound.line);
496
fprintf (assuan_get_assuan_log_stream (), "'\n");
497
aerr = ASSUAN_Connect_Failed;
501
assuan_disconnect (*r_ctx);