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 33 : uds_reader (assuan_context_t ctx, void *buf, size_t buflen)
81 : {
82 : #ifndef HAVE_W32_SYSTEM
83 33 : 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 99 : 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 33 : memset (&msg, 0, sizeof (msg));
100 :
101 33 : msg.msg_name = NULL;
102 33 : msg.msg_namelen = 0;
103 33 : msg.msg_iov = &iovec;
104 33 : msg.msg_iovlen = 1;
105 33 : iovec.iov_base = buf;
106 33 : iovec.iov_len = buflen;
107 : #ifdef USE_DESCRIPTOR_PASSING
108 33 : msg.msg_control = control_u.control;
109 33 : msg.msg_controllen = sizeof (control_u.control);
110 : #endif
111 :
112 33 : len = _assuan_recvmsg (ctx, ctx->inbound.fd, &msg, 0);
113 33 : if (len < 0)
114 0 : return -1;
115 33 : if (len == 0)
116 0 : return 0;
117 :
118 : #ifdef USE_DESCRIPTOR_PASSING
119 33 : cmptr = CMSG_FIRSTHDR (&msg);
120 33 : 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 33 : 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 : memset (&control_u, 0, sizeof (control_u));
210 :
211 6 : msg.msg_name = NULL;
212 6 : msg.msg_namelen = 0;
213 6 : msg.msg_iovlen = 1;
214 6 : msg.msg_iov = &iovec;
215 6 : iovec.iov_base = buffer;
216 6 : iovec.iov_len = strlen (buffer);
217 :
218 6 : msg.msg_control = control_u.control;
219 6 : msg.msg_controllen = sizeof (control_u.control);
220 6 : cmptr = CMSG_FIRSTHDR (&msg);
221 6 : cmptr->cmsg_len = CMSG_LEN(sizeof(int));
222 6 : cmptr->cmsg_level = SOL_SOCKET;
223 6 : cmptr->cmsg_type = SCM_RIGHTS;
224 :
225 6 : memcpy (CMSG_DATA (cmptr), &fd, sizeof (fd));
226 :
227 6 : len = _assuan_sendmsg (ctx, ctx->outbound.fd, &msg, 0);
228 6 : if (len < 0)
229 : {
230 0 : int saved_errno = errno;
231 0 : TRACE1 (ctx, ASSUAN_LOG_SYSIO, "uds_sendfd", ctx,
232 : "uds_sendfd: %s", strerror (errno));
233 0 : errno = saved_errno;
234 0 : return _assuan_error (ctx, gpg_err_code_from_syserror ());
235 : }
236 : else
237 6 : return 0;
238 : #else
239 : return _assuan_error (ctx, GPG_ERR_NOT_IMPLEMENTED);
240 : #endif
241 : }
242 :
243 :
244 : static gpg_error_t
245 6 : uds_receivefd (assuan_context_t ctx, assuan_fd_t *fd)
246 : {
247 : #ifdef USE_DESCRIPTOR_PASSING
248 : int i;
249 :
250 6 : if (!ctx->uds.pendingfdscount)
251 : {
252 0 : TRACE0 (ctx, ASSUAN_LOG_SYSIO, "uds_receivefd", ctx,
253 : "no pending file descriptors");
254 0 : return _assuan_error (ctx, GPG_ERR_ASS_GENERAL);
255 : }
256 6 : assert (ctx->uds.pendingfdscount <= DIM(ctx->uds.pendingfds));
257 :
258 6 : *fd = ctx->uds.pendingfds[0];
259 6 : for (i=1; i < ctx->uds.pendingfdscount; i++)
260 0 : ctx->uds.pendingfds[i-1] = ctx->uds.pendingfds[i];
261 6 : ctx->uds.pendingfdscount--;
262 :
263 6 : return 0;
264 : #else
265 : return _assuan_error (ctx, GPG_ERR_NOT_IMPLEMENTED);
266 : #endif
267 : }
268 :
269 :
270 : /* Close all pending fds. */
271 : void
272 6 : _assuan_uds_close_fds (assuan_context_t ctx)
273 : {
274 : int i;
275 :
276 6 : for (i = 0; i < ctx->uds.pendingfdscount; i++)
277 0 : _assuan_close (ctx, ctx->uds.pendingfds[i]);
278 6 : ctx->uds.pendingfdscount = 0;
279 6 : }
280 :
281 : /* Deinitialize the unix domain socket I/O functions. */
282 : void
283 6 : _assuan_uds_deinit (assuan_context_t ctx)
284 : {
285 6 : _assuan_uds_close_fds (ctx);
286 6 : }
287 :
288 :
289 : /* Helper function to initialize a context for domain I/O. */
290 : void
291 2 : _assuan_init_uds_io (assuan_context_t ctx)
292 : {
293 2 : ctx->engine.readfnc = uds_reader;
294 2 : ctx->engine.writefnc = uds_writer;
295 2 : ctx->engine.sendfd = uds_sendfd;
296 2 : ctx->engine.receivefd = uds_receivefd;
297 :
298 2 : ctx->uds.pendingfdscount = 0;
299 2 : }
300 :
|