Line data Source code
1 : /* assuan-uds.c - Assuan unix domain socket utilities
2 : Copyright (C) 2006, 2009 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, see <http://www.gnu.org/licenses/>.
18 : */
19 :
20 : #ifdef HAVE_CONFIG_H
21 : #include <config.h>
22 : #endif
23 :
24 : #include <stdlib.h>
25 : #include <stddef.h>
26 : #include <stdio.h>
27 : #include <errno.h>
28 : #ifdef HAVE_SYS_TYPES_H
29 : # include <sys/types.h>
30 : #endif
31 : #ifndef HAVE_W32_SYSTEM
32 : # include <sys/socket.h>
33 : # include <sys/un.h>
34 : #else
35 : # ifdef HAVE_WINSOCK2_H
36 : # include <winsock2.h>
37 : # endif
38 : # include <windows.h>
39 : #endif
40 : #if HAVE_SYS_UIO_H
41 : #include <sys/uio.h>
42 : #endif
43 : #ifdef HAVE_UNISTD_H
44 : # include <unistd.h>
45 : #endif
46 : #ifdef HAVE_FCNTL_H
47 : #include <fcntl.h>
48 : #endif
49 : #include <string.h>
50 : #include <assert.h>
51 :
52 : #include "assuan-defs.h"
53 : #include "debug.h"
54 :
55 : #ifdef USE_DESCRIPTOR_PASSING
56 : /* Provide replacement for missing CMSG maccros. We assume that
57 : size_t matches the alignment requirement. NOTE: This is not true
58 : on Mac OS X, so be extra careful to define _DARWIN_C_SOURCE to get
59 : those definitions instead of using these. */
60 : #define MY_ALIGN(n) ((((n))+ sizeof(size_t)-1) & (size_t)~(sizeof(size_t)-1))
61 : #ifndef CMSG_SPACE
62 : #define CMSG_SPACE(n) (MY_ALIGN(sizeof(struct cmsghdr)) + MY_ALIGN((n)))
63 : #endif
64 : #ifndef CMSG_LEN
65 : #define CMSG_LEN(n) (MY_ALIGN(sizeof(struct cmsghdr)) + (n))
66 : #endif
67 : #ifndef CMSG_FIRSTHDR
68 : #define CMSG_FIRSTHDR(mhdr) \
69 : ((size_t)(mhdr)->msg_controllen >= sizeof (struct cmsghdr) \
70 : ? (struct cmsghdr*) (mhdr)->msg_control : (struct cmsghdr*)NULL)
71 : #endif
72 : #ifndef CMSG_DATA
73 : #define CMSG_DATA(cmsg) ((unsigned char*)((struct cmsghdr*)(cmsg)+1))
74 : #endif
75 : #endif /*USE_DESCRIPTOR_PASSING*/
76 :
77 :
78 : /* Read from a unix domain socket using sendmsg. */
79 : static ssize_t
80 32 : uds_reader (assuan_context_t ctx, void *buf, size_t buflen)
81 : {
82 : #ifndef HAVE_W32_SYSTEM
83 32 : int len = 0;
84 : /* This loop should be OK. As FDs are followed by data, the
85 : readable status of the socket does not change and no new
86 : select/event-loop round is necessary. */
87 96 : while (!len) /* No data is buffered. */
88 : {
89 : struct msghdr msg;
90 : struct iovec iovec;
91 : #ifdef USE_DESCRIPTOR_PASSING
92 : union {
93 : struct cmsghdr cm;
94 : char control[CMSG_SPACE(sizeof (int))];
95 : } control_u;
96 : struct cmsghdr *cmptr;
97 : #endif /*USE_DESCRIPTOR_PASSING*/
98 :
99 32 : memset (&msg, 0, sizeof (msg));
100 :
101 32 : msg.msg_name = NULL;
102 32 : msg.msg_namelen = 0;
103 32 : msg.msg_iov = &iovec;
104 32 : msg.msg_iovlen = 1;
105 32 : iovec.iov_base = buf;
106 32 : iovec.iov_len = buflen;
107 : #ifdef USE_DESCRIPTOR_PASSING
108 32 : msg.msg_control = control_u.control;
109 32 : msg.msg_controllen = sizeof (control_u.control);
110 : #endif
111 :
112 32 : len = _assuan_recvmsg (ctx, ctx->inbound.fd, &msg, 0);
113 32 : if (len < 0)
114 0 : return -1;
115 32 : if (len == 0)
116 0 : return 0;
117 :
118 : #ifdef USE_DESCRIPTOR_PASSING
119 32 : cmptr = CMSG_FIRSTHDR (&msg);
120 32 : if (cmptr && cmptr->cmsg_len == CMSG_LEN (sizeof(int)))
121 : {
122 6 : if (cmptr->cmsg_level != SOL_SOCKET
123 6 : || cmptr->cmsg_type != SCM_RIGHTS)
124 0 : TRACE0 (ctx, ASSUAN_LOG_SYSIO, "uds_reader", ctx,
125 : "unexpected ancillary data received");
126 : else
127 : {
128 : int fd;
129 :
130 6 : memcpy (&fd, CMSG_DATA (cmptr), sizeof (fd));
131 :
132 6 : if (ctx->uds.pendingfdscount >= DIM (ctx->uds.pendingfds))
133 : {
134 0 : TRACE1 (ctx, ASSUAN_LOG_SYSIO, "uds_reader", ctx,
135 : "too many descriptors pending - "
136 : "closing received descriptor %d", fd);
137 0 : _assuan_close (ctx, fd);
138 : }
139 : else
140 6 : ctx->uds.pendingfds[ctx->uds.pendingfdscount++] = fd;
141 : }
142 : }
143 : #endif /*USE_DESCRIPTOR_PASSING*/
144 : }
145 :
146 32 : return len;
147 : #else /*HAVE_W32_SYSTEM*/
148 : int res = recvfrom (HANDLE2SOCKET(ctx->inbound.fd), buf, buflen, 0, NULL, NULL);
149 : if (res < 0)
150 : gpg_err_set_errno (_assuan_sock_wsa2errno (WSAGetLastError ()));
151 : return res;
152 : #endif /*HAVE_W32_SYSTEM*/
153 : }
154 :
155 :
156 : /* Write to the domain server. */
157 : static ssize_t
158 53 : uds_writer (assuan_context_t ctx, const void *buf, size_t buflen)
159 : {
160 : #ifndef HAVE_W32_SYSTEM
161 : struct msghdr msg;
162 : struct iovec iovec;
163 : ssize_t len;
164 :
165 53 : memset (&msg, 0, sizeof (msg));
166 :
167 53 : msg.msg_name = NULL;
168 53 : msg.msg_namelen = 0;
169 53 : msg.msg_iovlen = 1;
170 53 : msg.msg_iov = &iovec;
171 53 : iovec.iov_base = (void*)buf;
172 53 : iovec.iov_len = buflen;
173 :
174 53 : len = _assuan_sendmsg (ctx, ctx->outbound.fd, &msg, 0);
175 :
176 53 : return len;
177 : #else /*HAVE_W32_SYSTEM*/
178 : int res = sendto (HANDLE2SOCKET(ctx->outbound.fd), buf, buflen, 0,
179 : (struct sockaddr *)&ctx->serveraddr,
180 : sizeof (struct sockaddr_in));
181 : if (res < 0)
182 : gpg_err_set_errno ( _assuan_sock_wsa2errno (WSAGetLastError ()));
183 : return res;
184 : #endif /*HAVE_W32_SYSTEM*/
185 : }
186 :
187 :
188 : static gpg_error_t
189 6 : uds_sendfd (assuan_context_t ctx, assuan_fd_t fd)
190 : {
191 : #ifdef USE_DESCRIPTOR_PASSING
192 : struct msghdr msg;
193 : struct iovec iovec;
194 : union {
195 : struct cmsghdr cm;
196 : char control[CMSG_SPACE(sizeof (int))];
197 : } control_u;
198 : struct cmsghdr *cmptr;
199 : int len;
200 : char buffer[80];
201 :
202 : /* We need to send some real data so that a read won't return 0
203 : which will be taken as an EOF. It also helps with debugging. */
204 6 : snprintf (buffer, sizeof(buffer)-1, "# descriptor %d is in flight\n", fd);
205 6 : buffer[sizeof(buffer)-1] = 0;
206 :
207 6 : memset (&msg, 0, sizeof (msg));
208 :
209 6 : msg.msg_name = NULL;
210 6 : msg.msg_namelen = 0;
211 6 : msg.msg_iovlen = 1;
212 6 : msg.msg_iov = &iovec;
213 6 : iovec.iov_base = buffer;
214 6 : iovec.iov_len = strlen (buffer);
215 :
216 6 : msg.msg_control = control_u.control;
217 6 : msg.msg_controllen = sizeof (control_u.control);
218 6 : cmptr = CMSG_FIRSTHDR (&msg);
219 6 : cmptr->cmsg_len = CMSG_LEN(sizeof(int));
220 6 : cmptr->cmsg_level = SOL_SOCKET;
221 6 : cmptr->cmsg_type = SCM_RIGHTS;
222 :
223 6 : memcpy (CMSG_DATA (cmptr), &fd, sizeof (fd));
224 :
225 6 : len = _assuan_sendmsg (ctx, ctx->outbound.fd, &msg, 0);
226 6 : if (len < 0)
227 : {
228 0 : int saved_errno = errno;
229 0 : TRACE1 (ctx, ASSUAN_LOG_SYSIO, "uds_sendfd", ctx,
230 : "uds_sendfd: %s", strerror (errno));
231 0 : errno = saved_errno;
232 0 : return _assuan_error (ctx, gpg_err_code_from_syserror ());
233 : }
234 : else
235 6 : return 0;
236 : #else
237 : return _assuan_error (ctx, GPG_ERR_NOT_IMPLEMENTED);
238 : #endif
239 : }
240 :
241 :
242 : static gpg_error_t
243 6 : uds_receivefd (assuan_context_t ctx, assuan_fd_t *fd)
244 : {
245 : #ifdef USE_DESCRIPTOR_PASSING
246 : int i;
247 :
248 6 : if (!ctx->uds.pendingfdscount)
249 : {
250 0 : TRACE0 (ctx, ASSUAN_LOG_SYSIO, "uds_receivefd", ctx,
251 : "no pending file descriptors");
252 0 : return _assuan_error (ctx, GPG_ERR_ASS_GENERAL);
253 : }
254 6 : assert (ctx->uds.pendingfdscount <= DIM(ctx->uds.pendingfds));
255 :
256 6 : *fd = ctx->uds.pendingfds[0];
257 6 : for (i=1; i < ctx->uds.pendingfdscount; i++)
258 0 : ctx->uds.pendingfds[i-1] = ctx->uds.pendingfds[i];
259 6 : ctx->uds.pendingfdscount--;
260 :
261 6 : return 0;
262 : #else
263 : return _assuan_error (ctx, GPG_ERR_NOT_IMPLEMENTED);
264 : #endif
265 : }
266 :
267 :
268 : /* Close all pending fds. */
269 : void
270 6 : _assuan_uds_close_fds (assuan_context_t ctx)
271 : {
272 : int i;
273 :
274 6 : for (i = 0; i < ctx->uds.pendingfdscount; i++)
275 0 : _assuan_close (ctx, ctx->uds.pendingfds[i]);
276 6 : ctx->uds.pendingfdscount = 0;
277 6 : }
278 :
279 : /* Deinitialize the unix domain socket I/O functions. */
280 : void
281 6 : _assuan_uds_deinit (assuan_context_t ctx)
282 : {
283 6 : _assuan_uds_close_fds (ctx);
284 6 : }
285 :
286 :
287 : /* Helper function to initialize a context for domain I/O. */
288 : void
289 2 : _assuan_init_uds_io (assuan_context_t ctx)
290 : {
291 2 : ctx->engine.readfnc = uds_reader;
292 2 : ctx->engine.writefnc = uds_writer;
293 2 : ctx->engine.sendfd = uds_sendfd;
294 2 : ctx->engine.receivefd = uds_receivefd;
295 :
296 2 : ctx->uds.pendingfdscount = 0;
297 2 : }
298 :
|