c725392800cd79ccace21c41e69f1e4b1160d41a
[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
48 /* Read from a unix domain socket using sendmsg. 
49
50    FIXME: We don't need the buffering. It is a leftover from the time
51    when we used datagrams. */
52 static ssize_t
53 uds_reader (assuan_context_t ctx, void *buf, size_t buflen)
54 {
55   int len = ctx->uds.buffersize;
56
57 #ifndef HAVE_W32_SYSTEM
58
59   if (!ctx->uds.bufferallocated)
60     {
61       ctx->uds.buffer = xtrymalloc (2048);
62       if (!ctx->uds.buffer)
63         return _assuan_error (ASSUAN_Out_Of_Core);
64       ctx->uds.bufferallocated = 2048;
65     }
66
67   while (!len)  /* No data is buffered.  */
68     {
69       struct msghdr msg;
70       struct iovec iovec;
71       union {
72         struct cmsghdr cm;
73         char control[CMSG_SPACE(sizeof (int))];
74       } control_u;
75       struct cmsghdr *cmptr;
76
77       memset (&msg, 0, sizeof (msg));
78
79       msg.msg_name = NULL;
80       msg.msg_namelen = 0;
81       msg.msg_iov = &iovec;
82       msg.msg_iovlen = 1;
83       iovec.iov_base = ctx->uds.buffer;
84       iovec.iov_len = ctx->uds.bufferallocated;
85       msg.msg_control = control_u.control;
86       msg.msg_controllen = sizeof (control_u.control);
87
88       len = _assuan_simple_recvmsg (ctx, &msg);
89       if (len < 0)
90         return -1;
91
92       ctx->uds.buffersize = len;
93       ctx->uds.bufferoffset = 0;
94
95       cmptr = CMSG_FIRSTHDR (&msg);
96       if (cmptr && cmptr->cmsg_len == CMSG_LEN (sizeof(int)))
97         {
98           if (cmptr->cmsg_level != SOL_SOCKET
99               || cmptr->cmsg_type != SCM_RIGHTS)
100             _assuan_log_printf ("unexpected ancillary data received\n");
101           else
102             {
103               int fd = *((int*)CMSG_DATA (cmptr));
104
105               if (ctx->uds.pendingfdscount >= DIM (ctx->uds.pendingfds))
106                 {
107                   _assuan_log_printf ("too many descriptors pending - "
108                                       "closing received descriptor %d\n", fd);
109                   _assuan_close (fd);
110                 }
111               else
112                 ctx->uds.pendingfds[ctx->uds.pendingfdscount++] = fd;
113             }
114         }
115     }
116 #else /*HAVE_W32_SYSTEM*/
117   len = recvfrom (ctx->inbound.fd, buf, buflen, 0, NULL, NULL);
118 #endif /*HAVE_W32_SYSTEM*/
119
120   /* Return some data to the user.  */
121
122   if (len > buflen) /* We have more than the user requested.  */
123     len = buflen;
124
125   memcpy (buf, ctx->uds.buffer + ctx->uds.bufferoffset, len);
126   ctx->uds.buffersize -= len;
127   assert (ctx->uds.buffersize >= 0);
128   ctx->uds.bufferoffset += len;
129   assert (ctx->uds.bufferoffset <= ctx->uds.bufferallocated);
130
131   return len;
132 }
133
134
135 /* Write to the domain server.  */
136 static ssize_t
137 uds_writer (assuan_context_t ctx, const void *buf, size_t buflen)
138 {
139 #ifndef HAVE_W32_SYSTEM
140   struct msghdr msg;
141   struct iovec iovec;
142   ssize_t len;
143
144   memset (&msg, 0, sizeof (msg));
145
146   msg.msg_name = NULL;
147   msg.msg_namelen = 0;
148   msg.msg_iovlen = 1;
149   msg.msg_iov = &iovec;
150   iovec.iov_base = (void*)buf;
151   iovec.iov_len = buflen;
152   msg.msg_control = 0;
153   msg.msg_controllen = 0;
154
155   len = _assuan_simple_sendmsg (ctx, &msg);
156 #else /*HAVE_W32_SYSTEM*/
157   int len;
158   
159   len = sendto (ctx->outbound.fd, buf, buflen, 0,
160                 (struct sockaddr *)&ctx->serveraddr,
161                 sizeof (struct sockaddr_in));
162 #endif /*HAVE_W32_SYSTEM*/
163   return len;
164 }
165
166
167 static assuan_error_t
168 uds_sendfd (assuan_context_t ctx, int fd)
169 {
170 #ifndef HAVE_W32_SYSTEM
171   struct msghdr msg;
172   struct iovec iovec;
173   union {
174     struct cmsghdr cm;
175     char control[CMSG_SPACE(sizeof (int))];
176   } control_u;
177   struct cmsghdr *cmptr;
178   int len;
179   char buffer[80];
180
181   /* We need to send some real data so that a read won't return 0
182      which will be taken as an EOF.  It also helps with debugging. */ 
183   snprintf (buffer, sizeof(buffer)-1, "# descriptor %d is in flight\n", fd);
184   buffer[sizeof(buffer)-1] = 0;
185
186   memset (&msg, 0, sizeof (msg));
187
188   msg.msg_name = NULL;
189   msg.msg_namelen = 0;
190   msg.msg_iovlen = 1;
191   msg.msg_iov = &iovec;
192   iovec.iov_base = buffer;
193   iovec.iov_len = strlen (buffer);
194
195   msg.msg_control = control_u.control;
196   msg.msg_controllen = sizeof (control_u.control);
197   cmptr = CMSG_FIRSTHDR (&msg);
198   cmptr->cmsg_len = CMSG_LEN(sizeof(int));
199   cmptr->cmsg_level = SOL_SOCKET;
200   cmptr->cmsg_type = SCM_RIGHTS;
201   *((int*)CMSG_DATA (cmptr)) = fd;
202
203   len = _assuan_simple_sendmsg (ctx, &msg);
204   if (len < 0)
205     {
206       _assuan_log_printf ("uds_sendfd: %s\n", strerror (errno));
207       return _assuan_error (ASSUAN_Write_Error);
208     }
209   else
210     return 0;
211 #else
212   return _assuan_error (ASSUAN_Not_Implemented);
213 #endif
214 }
215
216
217 static assuan_error_t
218 uds_receivefd (assuan_context_t ctx, int *fd)
219 {
220 #ifndef HAVE_W32_SYSTEM
221   int i;
222
223   if (!ctx->uds.pendingfdscount)
224     {
225       _assuan_log_printf ("no pending file descriptors!\n");
226       return _assuan_error (ASSUAN_General_Error);
227     }
228   assert (ctx->uds.pendingfdscount <= DIM(ctx->uds.pendingfds));
229
230   *fd = ctx->uds.pendingfds[0];
231   for (i=1; i < ctx->uds.pendingfdscount; i++)
232     ctx->uds.pendingfds[i-1] = ctx->uds.pendingfds[i];
233   ctx->uds.pendingfdscount--;
234
235   return 0;
236 #else
237   return _assuan_error (ASSUAN_Not_Implemented);
238 #endif
239 }
240
241
242 /* Close all pending fds. */
243 void
244 _assuan_uds_close_fds (assuan_context_t ctx)
245 {
246   int i;
247
248   for (i = 0; i < ctx->uds.pendingfdscount; i++)
249     _assuan_close (ctx->uds.pendingfds[i]);
250   ctx->uds.pendingfdscount = 0;
251 }
252
253 /* Deinitialize the unix domain socket I/O functions.  */
254 void
255 _assuan_uds_deinit (assuan_context_t ctx)
256 {
257   /* First call the finish_handler which should close descriptors etc. */
258   ctx->finish_handler (ctx);
259
260   if (ctx->uds.buffer)
261     {
262       assert (ctx->uds.bufferallocated);
263       ctx->uds.bufferallocated = 0;
264       xfree (ctx->uds.buffer);
265     }
266
267   _assuan_uds_close_fds (ctx);
268 }
269
270
271 /* Helper function to initialize a context for domain I/O. */
272 void
273 _assuan_init_uds_io (assuan_context_t ctx)
274 {
275   static struct assuan_io io = { uds_reader, uds_writer,
276                                  uds_sendfd, uds_receivefd };
277
278   ctx->io = &io;
279   ctx->uds.buffer = 0;
280   ctx->uds.bufferoffset = 0;
281   ctx->uds.buffersize = 0;
282   ctx->uds.bufferallocated = 0;
283   ctx->uds.pendingfdscount = 0;
284 }
285