Update to rev 231 of libassuan.
[gpgme.git] / assuan / assuan-uds.c
1 /* assuan-uds.c - Assuan unix domain socket utilities
2  * Copyright (C) 2006 Free Software Foundation, Inc.
3  *
4  * This file is part of Assuan.
5  *
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.
10  *
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.
15  *
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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301,
19  * USA. 
20  */
21
22 #ifdef HAVE_CONFIG_H
23 #include <config.h>
24 #endif
25
26 #include <stdlib.h>
27 #include <stddef.h>
28 #include <stdio.h>
29 #include <errno.h>
30 #include <sys/types.h>
31 #ifndef HAVE_W32_SYSTEM
32 #include <sys/socket.h>
33 #include <sys/un.h>
34 #else
35 #include <windows.h>
36 #endif
37 #if HAVE_SYS_UIO_H
38 #include <sys/uio.h>
39 #endif
40 #include <unistd.h>
41 #include <fcntl.h>
42 #include <string.h>
43 #include <assert.h>
44
45 #include "assuan-defs.h"
46
47 #ifdef USE_DESCRIPTOR_PASSING
48 /* Provide replacement for missing CMSG maccros.  We assume that
49    size_t matches the alignment requirement. */
50 #define MY_ALIGN(n) ((((n))+ sizeof(size_t)-1) & (size_t)~(sizeof(size_t)-1))
51 #ifndef CMSG_SPACE
52 #define CMSG_SPACE(n) (MY_ALIGN(sizeof(struct cmsghdr)) + MY_ALIGN((n)))
53 #endif 
54 #ifndef CMSG_LEN
55 #define CMSG_LEN(n) (MY_ALIGN(sizeof(struct cmsghdr)) + (n))
56 #endif 
57 #ifndef CMSG_FIRSTHDR
58 #define CMSG_FIRSTHDR(mhdr) \
59   ((size_t)(mhdr)->msg_controllen >= sizeof (struct cmsghdr)                  \
60    ? (struct cmsghdr*) (mhdr)->msg_control : (struct cmsghdr*)NULL)
61 #endif
62 #ifndef CMSG_DATA
63 #define CMSG_DATA(cmsg) ((unsigned char*)((struct cmsghdr*)(cmsg)+1))
64 #endif
65 #endif /*USE_DESCRIPTOR_PASSING*/
66
67
68 /* Read from a unix domain socket using sendmsg. 
69
70    FIXME: We don't need the buffering. It is a leftover from the time
71    when we used datagrams. */
72 static ssize_t
73 uds_reader (assuan_context_t ctx, void *buf, size_t buflen)
74 {
75   int len = ctx->uds.buffersize;
76
77 #ifndef HAVE_W32_SYSTEM
78   if (!ctx->uds.bufferallocated)
79     {
80       ctx->uds.buffer = xtrymalloc (2048);
81       if (!ctx->uds.buffer)
82         return _assuan_error (ASSUAN_Out_Of_Core);
83       ctx->uds.bufferallocated = 2048;
84     }
85
86   while (!len)  /* No data is buffered.  */
87     {
88       struct msghdr msg;
89       struct iovec iovec;
90 #ifdef USE_DESCRIPTOR_PASSING
91       union {
92         struct cmsghdr cm;
93         char control[CMSG_SPACE(sizeof (int))];
94       } control_u;
95       struct cmsghdr *cmptr;
96 #endif /*USE_DESCRIPTOR_PASSING*/
97
98       memset (&msg, 0, sizeof (msg));
99
100       msg.msg_name = NULL;
101       msg.msg_namelen = 0;
102       msg.msg_iov = &iovec;
103       msg.msg_iovlen = 1;
104       iovec.iov_base = ctx->uds.buffer;
105       iovec.iov_len = ctx->uds.bufferallocated;
106 #ifdef USE_DESCRIPTOR_PASSING
107       msg.msg_control = control_u.control;
108       msg.msg_controllen = sizeof (control_u.control);
109 #endif
110
111       len = _assuan_simple_recvmsg (ctx, &msg);
112       if (len < 0)
113         return -1;
114
115       ctx->uds.buffersize = len;
116       ctx->uds.bufferoffset = 0;
117
118 #ifdef USE_DESCRIPTOR_PASSING
119       cmptr = CMSG_FIRSTHDR (&msg);
120       if (cmptr && cmptr->cmsg_len == CMSG_LEN (sizeof(int)))
121         {
122           if (cmptr->cmsg_level != SOL_SOCKET
123               || cmptr->cmsg_type != SCM_RIGHTS)
124             _assuan_log_printf ("unexpected ancillary data received\n");
125           else
126             {
127               int fd = *((int*)CMSG_DATA (cmptr));
128
129               if (ctx->uds.pendingfdscount >= DIM (ctx->uds.pendingfds))
130                 {
131                   _assuan_log_printf ("too many descriptors pending - "
132                                       "closing received descriptor %d\n", fd);
133                   _assuan_close (fd);
134                 }
135               else
136                 ctx->uds.pendingfds[ctx->uds.pendingfdscount++] = fd;
137             }
138         }
139 #endif /*USE_DESCRIPTOR_PASSING*/
140     }
141
142 #else /*HAVE_W32_SYSTEM*/
143
144   len = recvfrom (ctx->inbound.fd, buf, buflen, 0, NULL, NULL);
145
146 #endif /*HAVE_W32_SYSTEM*/
147
148   /* Return some data to the user.  */
149
150   if (len > buflen) /* We have more than the user requested.  */
151     len = buflen;
152
153   memcpy (buf, ctx->uds.buffer + ctx->uds.bufferoffset, len);
154   ctx->uds.buffersize -= len;
155   assert (ctx->uds.buffersize >= 0);
156   ctx->uds.bufferoffset += len;
157   assert (ctx->uds.bufferoffset <= ctx->uds.bufferallocated);
158
159   return len;
160 }
161
162
163 /* Write to the domain server.  */
164 static ssize_t
165 uds_writer (assuan_context_t ctx, const void *buf, size_t buflen)
166 {
167 #ifndef HAVE_W32_SYSTEM
168   struct msghdr msg;
169   struct iovec iovec;
170   ssize_t len;
171
172   memset (&msg, 0, sizeof (msg));
173
174   msg.msg_name = NULL;
175   msg.msg_namelen = 0;
176   msg.msg_iovlen = 1;
177   msg.msg_iov = &iovec;
178   iovec.iov_base = (void*)buf;
179   iovec.iov_len = buflen;
180
181   len = _assuan_simple_sendmsg (ctx, &msg);
182 #else /*HAVE_W32_SYSTEM*/
183   int len;
184   
185   len = sendto (ctx->outbound.fd, buf, buflen, 0,
186                 (struct sockaddr *)&ctx->serveraddr,
187                 sizeof (struct sockaddr_in));
188 #endif /*HAVE_W32_SYSTEM*/
189   return len;
190 }
191
192
193 static assuan_error_t
194 uds_sendfd (assuan_context_t ctx, int fd)
195 {
196 #ifdef USE_DESCRIPTOR_PASSING
197   struct msghdr msg;
198   struct iovec iovec;
199   union {
200     struct cmsghdr cm;
201     char control[CMSG_SPACE(sizeof (int))];
202   } control_u;
203   struct cmsghdr *cmptr;
204   int len;
205   char buffer[80];
206
207   /* We need to send some real data so that a read won't return 0
208      which will be taken as an EOF.  It also helps with debugging. */ 
209   snprintf (buffer, sizeof(buffer)-1, "# descriptor %d is in flight\n", fd);
210   buffer[sizeof(buffer)-1] = 0;
211
212   memset (&msg, 0, sizeof (msg));
213
214   msg.msg_name = NULL;
215   msg.msg_namelen = 0;
216   msg.msg_iovlen = 1;
217   msg.msg_iov = &iovec;
218   iovec.iov_base = buffer;
219   iovec.iov_len = strlen (buffer);
220
221   msg.msg_control = control_u.control;
222   msg.msg_controllen = sizeof (control_u.control);
223   cmptr = CMSG_FIRSTHDR (&msg);
224   cmptr->cmsg_len = CMSG_LEN(sizeof(int));
225   cmptr->cmsg_level = SOL_SOCKET;
226   cmptr->cmsg_type = SCM_RIGHTS;
227   *((int*)CMSG_DATA (cmptr)) = fd;
228
229   len = _assuan_simple_sendmsg (ctx, &msg);
230   if (len < 0)
231     {
232       _assuan_log_printf ("uds_sendfd: %s\n", strerror (errno));
233       return _assuan_error (ASSUAN_Write_Error);
234     }
235   else
236     return 0;
237 #else
238   return _assuan_error (ASSUAN_Not_Implemented);
239 #endif
240 }
241
242
243 static assuan_error_t
244 uds_receivefd (assuan_context_t ctx, int *fd)
245 {
246 #ifdef USE_DESCRIPTOR_PASSING
247   int i;
248
249   if (!ctx->uds.pendingfdscount)
250     {
251       _assuan_log_printf ("no pending file descriptors!\n");
252       return _assuan_error (ASSUAN_General_Error);
253     }
254   assert (ctx->uds.pendingfdscount <= DIM(ctx->uds.pendingfds));
255
256   *fd = ctx->uds.pendingfds[0];
257   for (i=1; i < ctx->uds.pendingfdscount; i++)
258     ctx->uds.pendingfds[i-1] = ctx->uds.pendingfds[i];
259   ctx->uds.pendingfdscount--;
260
261   return 0;
262 #else
263   return _assuan_error (ASSUAN_Not_Implemented);
264 #endif
265 }
266
267
268 /* Close all pending fds. */
269 void
270 _assuan_uds_close_fds (assuan_context_t ctx)
271 {
272   int i;
273
274   for (i = 0; i < ctx->uds.pendingfdscount; i++)
275     _assuan_close (ctx->uds.pendingfds[i]);
276   ctx->uds.pendingfdscount = 0;
277 }
278
279 /* Deinitialize the unix domain socket I/O functions.  */
280 void
281 _assuan_uds_deinit (assuan_context_t ctx)
282 {
283   /* First call the finish_handler which should close descriptors etc. */
284   ctx->finish_handler (ctx);
285
286   if (ctx->uds.buffer)
287     {
288       assert (ctx->uds.bufferallocated);
289       ctx->uds.bufferallocated = 0;
290       xfree (ctx->uds.buffer);
291     }
292
293   _assuan_uds_close_fds (ctx);
294 }
295
296
297 /* Helper function to initialize a context for domain I/O. */
298 void
299 _assuan_init_uds_io (assuan_context_t ctx)
300 {
301   static struct assuan_io io = { uds_reader, uds_writer,
302                                  uds_sendfd, uds_receivefd };
303
304   ctx->io = &io;
305   ctx->uds.buffer = 0;
306   ctx->uds.bufferoffset = 0;
307   ctx->uds.buffersize = 0;
308   ctx->uds.bufferallocated = 0;
309   ctx->uds.pendingfdscount = 0;
310 }
311