1 /*-
2 * Copyright (c) 2009-2010 The FreeBSD Foundation
3 * All rights reserved.
4 *
5 * This software was developed by Pawel Jakub Dawidek under sponsorship from
6 * the FreeBSD Foundation.
7 *
8 * Redistribution and use in source and binary forms, with or without
9 * modification, are permitted provided that the following conditions
10 * are met:
11 * 1. Redistributions of source code must retain the above copyright
12 * notice, this list of conditions and the following disclaimer.
13 * 2. Redistributions in binary form must reproduce the above copyright
14 * notice, this list of conditions and the following disclaimer in the
15 * documentation and/or other materials provided with the distribution.
16 *
17 * THIS SOFTWARE IS PROVIDED BY THE AUTHORS AND CONTRIBUTORS ``AS IS'' AND
18 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
19 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
20 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE LIABLE
21 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
22 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
23 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
24 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
25 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
26 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
27 * SUCH DAMAGE.
28 */
29
30 /* UDS - UNIX Domain Socket */
31
32 #include <config/config.h>
33
34 #include <sys/types.h>
35 #include <sys/socket.h>
36 #include <sys/un.h>
37
38 #include <errno.h>
39 #include <stdbool.h>
40 #include <stdint.h>
41 #include <stdio.h>
42 #include <string.h>
43 #include <unistd.h>
44
45 #ifndef HAVE_STRLCPY
46 #include <compat/strlcpy.h>
47 #endif
48
49 #include "pjdlog.h"
50 #include "proto_impl.h"
51
52 #define UDS_CTX_MAGIC 0xd541c
53 struct uds_ctx {
54 int uc_magic;
55 struct sockaddr_un uc_sun;
56 int uc_fd;
57 int uc_side;
58 #define UDS_SIDE_CLIENT 0
59 #define UDS_SIDE_SERVER_LISTEN 1
60 #define UDS_SIDE_SERVER_WORK 2
61 pid_t uc_owner;
62 };
63
64 static void uds_close(void *ctx);
65
66 static int
uds_addr(const char * addr,struct sockaddr_un * sunp)67 uds_addr(const char *addr, struct sockaddr_un *sunp)
68 {
69
70 if (addr == NULL)
71 return (-1);
72
73 if (strncasecmp(addr, "uds://", 6) == 0)
74 addr += 6;
75 else if (strncasecmp(addr, "unix://", 7) == 0)
76 addr += 7;
77 else if (addr[0] == '/' && /* If it starts from /... */
78 strstr(addr, "://") == NULL)/* ...and there is no prefix... */
79 ; /* ...we assume its us. */
80 else
81 return (-1);
82
83 sunp->sun_family = AF_UNIX;
84 if (strlcpy(sunp->sun_path, addr, sizeof(sunp->sun_path)) >=
85 sizeof(sunp->sun_path)) {
86 return (ENAMETOOLONG);
87 }
88 #ifdef HAVE_SOCKADDR_STORAGE_SS_LEN
89 sunp->sun_len = SUN_LEN(sunp);
90 #endif
91
92 return (0);
93 }
94
95 static int
uds_common_setup(const char * addr,int side,struct uds_ctx ** uctxp)96 uds_common_setup(const char *addr, int side, struct uds_ctx **uctxp)
97 {
98 struct uds_ctx *uctx;
99 int error;
100
101 uctx = malloc(sizeof(*uctx));
102 if (uctx == NULL)
103 return (errno);
104
105 /* Parse given address. */
106 error = uds_addr(addr, &uctx->uc_sun);
107 if (error != 0) {
108 free(uctx);
109 return (error);
110 }
111
112 uctx->uc_fd = socket(AF_UNIX, SOCK_STREAM, 0);
113 if (uctx->uc_fd == -1) {
114 error = errno;
115 free(uctx);
116 return (error);
117 }
118
119 uctx->uc_side = side;
120 uctx->uc_owner = 0;
121 uctx->uc_magic = UDS_CTX_MAGIC;
122 *uctxp = uctx;
123
124 return (0);
125 }
126
127 static int
uds_connect(const char * srcaddr,const char * dstaddr,int timeout,void ** ctxp)128 uds_connect(const char *srcaddr, const char *dstaddr, int timeout, void **ctxp)
129 {
130 struct uds_ctx *uctx;
131 int error;
132
133 PJDLOG_ASSERT(dstaddr != NULL);
134 PJDLOG_ASSERT(timeout >= -1);
135
136 error = uds_common_setup(dstaddr, UDS_SIDE_CLIENT, &uctx);
137 if (error != 0)
138 return (error);
139
140 PJDLOG_ASSERT(srcaddr == NULL);
141
142 if (connect(uctx->uc_fd, (struct sockaddr *)&uctx->uc_sun,
143 sizeof(uctx->uc_sun)) == -1) {
144 error = errno;
145 uds_close(uctx);
146 return (error);
147 }
148
149 *ctxp = uctx;
150
151 return (0);
152 }
153
154 static int
uds_connect_wait(void * ctx,int timeout)155 uds_connect_wait(void *ctx, int timeout)
156 {
157 struct uds_ctx *uctx = ctx;
158
159 PJDLOG_ASSERT(uctx != NULL);
160 PJDLOG_ASSERT(uctx->uc_magic == UDS_CTX_MAGIC);
161 PJDLOG_ASSERT(uctx->uc_side == UDS_SIDE_CLIENT);
162 PJDLOG_ASSERT(uctx->uc_fd >= 0);
163 PJDLOG_ASSERT(timeout >= 0);
164
165 return (0);
166 }
167
168 static int
uds_server(const char * addr,void ** ctxp)169 uds_server(const char *addr, void **ctxp)
170 {
171 struct uds_ctx *uctx;
172 int error;
173
174 error = uds_common_setup(addr, UDS_SIDE_SERVER_LISTEN, &uctx);
175 if (error != 0)
176 return (error);
177
178 (void)unlink(uctx->uc_sun.sun_path);
179 if (bind(uctx->uc_fd, (struct sockaddr *)&uctx->uc_sun,
180 sizeof(uctx->uc_sun)) == -1) {
181 error = errno;
182 uds_close(uctx);
183 return (error);
184 }
185 uctx->uc_owner = getpid();
186 if (listen(uctx->uc_fd, 8) == -1) {
187 error = errno;
188 uds_close(uctx);
189 return (error);
190 }
191
192 *ctxp = uctx;
193
194 return (0);
195 }
196
197 static int
uds_accept(void * ctx,void ** newctxp)198 uds_accept(void *ctx, void **newctxp)
199 {
200 struct uds_ctx *uctx = ctx;
201 struct uds_ctx *newuctx;
202 socklen_t fromlen;
203 int error;
204
205 PJDLOG_ASSERT(uctx != NULL);
206 PJDLOG_ASSERT(uctx->uc_magic == UDS_CTX_MAGIC);
207 PJDLOG_ASSERT(uctx->uc_side == UDS_SIDE_SERVER_LISTEN);
208 PJDLOG_ASSERT(uctx->uc_fd >= 0);
209
210 newuctx = malloc(sizeof(*newuctx));
211 if (newuctx == NULL)
212 return (errno);
213
214 fromlen = sizeof(newuctx->uc_sun);
215 newuctx->uc_fd = accept(uctx->uc_fd,
216 (struct sockaddr *)&newuctx->uc_sun, &fromlen);
217 if (newuctx->uc_fd < 0) {
218 error = errno;
219 free(newuctx);
220 return (error);
221 }
222
223 newuctx->uc_side = UDS_SIDE_SERVER_WORK;
224 newuctx->uc_magic = UDS_CTX_MAGIC;
225 *newctxp = newuctx;
226
227 return (0);
228 }
229
230 static int
uds_send(void * ctx,const unsigned char * data,size_t size,int fd)231 uds_send(void *ctx, const unsigned char *data, size_t size, int fd)
232 {
233 struct uds_ctx *uctx = ctx;
234
235 PJDLOG_ASSERT(uctx != NULL);
236 PJDLOG_ASSERT(uctx->uc_magic == UDS_CTX_MAGIC);
237 PJDLOG_ASSERT(uctx->uc_fd >= 0);
238
239 return (proto_common_send(uctx->uc_fd, data, size, fd));
240 }
241
242 static int
uds_recv(void * ctx,unsigned char * data,size_t size,int * fdp)243 uds_recv(void *ctx, unsigned char *data, size_t size, int *fdp)
244 {
245 struct uds_ctx *uctx = ctx;
246
247 PJDLOG_ASSERT(uctx != NULL);
248 PJDLOG_ASSERT(uctx->uc_magic == UDS_CTX_MAGIC);
249 PJDLOG_ASSERT(uctx->uc_fd >= 0);
250
251 return (proto_common_recv(uctx->uc_fd, data, size, fdp));
252 }
253
254 static int
uds_descriptor(const void * ctx)255 uds_descriptor(const void *ctx)
256 {
257 const struct uds_ctx *uctx = ctx;
258
259 PJDLOG_ASSERT(uctx != NULL);
260 PJDLOG_ASSERT(uctx->uc_magic == UDS_CTX_MAGIC);
261
262 return (uctx->uc_fd);
263 }
264
265 static void
uds_local_address(const void * ctx,char * addr,size_t size)266 uds_local_address(const void *ctx, char *addr, size_t size)
267 {
268 const struct uds_ctx *uctx = ctx;
269 struct sockaddr_un sun;
270 socklen_t sunlen;
271
272 PJDLOG_ASSERT(uctx != NULL);
273 PJDLOG_ASSERT(uctx->uc_magic == UDS_CTX_MAGIC);
274 PJDLOG_ASSERT(addr != NULL);
275
276 sunlen = sizeof(sun);
277 if (getsockname(uctx->uc_fd, (struct sockaddr *)&sun, &sunlen) < 0) {
278 PJDLOG_VERIFY(strlcpy(addr, "N/A", size) < size);
279 return;
280 }
281 PJDLOG_ASSERT(sun.sun_family == AF_UNIX);
282 if (sun.sun_path[0] == '\0') {
283 PJDLOG_VERIFY(strlcpy(addr, "N/A", size) < size);
284 return;
285 }
286 PJDLOG_VERIFY(snprintf(addr, size, "uds://%s", sun.sun_path) < (ssize_t)size);
287 }
288
289 static void
uds_remote_address(const void * ctx,char * addr,size_t size)290 uds_remote_address(const void *ctx, char *addr, size_t size)
291 {
292 const struct uds_ctx *uctx = ctx;
293 struct sockaddr_un sun;
294 socklen_t sunlen;
295
296 PJDLOG_ASSERT(uctx != NULL);
297 PJDLOG_ASSERT(uctx->uc_magic == UDS_CTX_MAGIC);
298 PJDLOG_ASSERT(addr != NULL);
299
300 sunlen = sizeof(sun);
301 if (getpeername(uctx->uc_fd, (struct sockaddr *)&sun, &sunlen) < 0) {
302 PJDLOG_VERIFY(strlcpy(addr, "N/A", size) < size);
303 return;
304 }
305 PJDLOG_ASSERT(sun.sun_family == AF_UNIX);
306 if (sun.sun_path[0] == '\0') {
307 PJDLOG_VERIFY(strlcpy(addr, "N/A", size) < size);
308 return;
309 }
310 snprintf(addr, size, "uds://%s", sun.sun_path);
311 }
312
313 static void
uds_close(void * ctx)314 uds_close(void *ctx)
315 {
316 struct uds_ctx *uctx = ctx;
317
318 PJDLOG_ASSERT(uctx != NULL);
319 PJDLOG_ASSERT(uctx->uc_magic == UDS_CTX_MAGIC);
320
321 if (uctx->uc_fd >= 0)
322 close(uctx->uc_fd);
323 /*
324 * Unlink the socket only if we are the owner and this is descriptor
325 * we listen on.
326 */
327 if (uctx->uc_side == UDS_SIDE_SERVER_LISTEN &&
328 uctx->uc_owner == getpid()) {
329 PJDLOG_ASSERT(uctx->uc_sun.sun_path[0] != '\0');
330 if (unlink(uctx->uc_sun.sun_path) == -1) {
331 pjdlog_errno(LOG_WARNING,
332 "Unable to unlink socket file %s",
333 uctx->uc_sun.sun_path);
334 }
335 }
336 uctx->uc_owner = 0;
337 uctx->uc_magic = 0;
338 free(uctx);
339 }
340
341 static struct proto uds_proto = {
342 .prt_name = "uds",
343 .prt_connect = uds_connect,
344 .prt_connect_wait = uds_connect_wait,
345 .prt_server = uds_server,
346 .prt_accept = uds_accept,
347 .prt_send = uds_send,
348 .prt_recv = uds_recv,
349 .prt_descriptor = uds_descriptor,
350 .prt_local_address = uds_local_address,
351 .prt_remote_address = uds_remote_address,
352 .prt_close = uds_close
353 };
354
355 static __constructor void
uds_ctor(void)356 uds_ctor(void)
357 {
358
359 proto_register(&uds_proto, false);
360 }
361