Add support for L4Re Virtio Sockets
[src/app-framework-binder.git] / src / afb-socket.c
1 /*
2  * Copyright (C) 2015-2019 "IoT.bzh"
3  * Author José Bollo <jose.bollo@iot.bzh>
4  *
5  * Licensed under the Apache License, Version 2.0 (the "License");
6  * you may not use this file except in compliance with the License.
7  * You may obtain a copy of the License at
8  *
9  *   http://www.apache.org/licenses/LICENSE-2.0
10  *
11  * Unless required by applicable law or agreed to in writing, software
12  * distributed under the License is distributed on an "AS IS" BASIS,
13  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14  * See the License for the specific language governing permissions and
15  * limitations under the License.
16  */
17
18 #define _GNU_SOURCE
19
20 #include <stdlib.h>
21 #include <string.h>
22 #include <assert.h>
23 #include <fcntl.h>
24 #include <unistd.h>
25 #include <errno.h>
26 #include <endian.h>
27 #include <netdb.h>
28 #include <sys/types.h>
29 #include <sys/socket.h>
30 #include <sys/un.h>
31
32 #include "afb-fdev.h"
33 #include "afb-socket.h"
34
35 #include "fdev.h"
36 #include "verbose.h"
37
38 /******************************************************************************/
39 #define BACKLOG  5
40
41 /******************************************************************************/
42
43 /**
44  * known types
45  */
46 enum type {
47         /** type internet */
48         Type_Inet,
49
50         /** type systemd */
51         Type_Systemd,
52
53         /** type virtual socket of L4 */
54         Type_L4,
55
56         /** type Unix */
57         Type_Unix
58 };
59
60 /**
61  * Structure for known entries
62  */
63 struct entry
64 {
65         /** the known prefix */
66         const char *prefix;
67
68         /** the type of the entry */
69         unsigned type: 2;
70
71         /** should not set SO_REUSEADDR for servers */
72         unsigned noreuseaddr: 1;
73
74         /** should not call listen for servers */
75         unsigned nolisten: 1;
76 };
77
78 /**
79  * The known entries with the default one at the first place
80  */
81 static struct entry entries[] = {
82         {
83                 .prefix = "tcp:",
84                 .type = Type_Inet
85         },
86         {
87                 .prefix = "sd:",
88                 .type = Type_Systemd,
89                 .noreuseaddr = 1,
90                 .nolisten = 1
91         },
92         {
93                 .prefix = "l4vsock:",
94                 .type = Type_L4
95         },
96         {
97                 .prefix = "unix:",
98                 .type = Type_Unix
99         }
100 };
101
102 /**
103  * It is possible to set explicit api name instead of using the
104  * default one.
105  */
106 static const char as_api[] = "?as-api=";
107
108 /******************************************************************************/
109
110 /**
111  * open a unix domain socket for client or server
112  *
113  * @param spec the specification of the path (prefix with @ for abstract)
114  * @param server 0 for client, server otherwise
115  *
116  * @return the file descriptor number of the socket or -1 in case of error
117  */
118 static int open_unix(const char *spec, int server)
119 {
120         int fd, rc, abstract;
121         struct sockaddr_un addr;
122         size_t length;
123
124         abstract = spec[0] == '@';
125
126         /* check the length */
127         length = strlen(spec);
128         if (length >= 108) {
129                 errno = ENAMETOOLONG;
130                 return -1;
131         }
132
133         /* remove the file on need */
134         if (server && !abstract)
135                 unlink(spec);
136
137         /* create a  socket */
138         fd = socket(AF_UNIX, SOCK_STREAM, 0);
139         if (fd < 0)
140                 return fd;
141
142         /* prepare address  */
143         memset(&addr, 0, sizeof addr);
144         addr.sun_family = AF_UNIX;
145         strcpy(addr.sun_path, spec);
146         if (abstract)
147                 addr.sun_path[0] = 0; /* implement abstract sockets */
148
149         if (server) {
150                 rc = bind(fd, (struct sockaddr *) &addr, (socklen_t)(sizeof addr));
151         } else {
152                 rc = connect(fd, (struct sockaddr *) &addr, (socklen_t)(sizeof addr));
153         }
154         if (rc < 0) {
155                 close(fd);
156                 return rc;
157         }
158         return fd;
159 }
160
161 /**
162  * open a tcp socket for client or server
163  *
164  * @param spec the specification of the host:port/...
165  * @param server 0 for client, server otherwise
166  *
167  * @return the file descriptor number of the socket or -1 in case of error
168  */
169 static int open_tcp(const char *spec, int server, int reuseaddr)
170 {
171         int rc, fd;
172         const char *service, *host, *tail;
173         struct addrinfo hint, *rai, *iai;
174
175         /* scan the uri */
176         tail = strchrnul(spec, '/');
177         service = strchr(spec, ':');
178         if (tail == NULL || service == NULL || tail < service) {
179                 errno = EINVAL;
180                 return -1;
181         }
182         host = strndupa(spec, service++ - spec);
183         service = strndupa(service, tail - service);
184
185         /* get addr */
186         memset(&hint, 0, sizeof hint);
187         hint.ai_family = AF_INET;
188         hint.ai_socktype = SOCK_STREAM;
189         if (server) {
190                 hint.ai_flags = AI_PASSIVE;
191                 if (host[0] == 0 || (host[0] == '*' && host[1] == 0))
192                         host = NULL;
193         }
194         rc = getaddrinfo(host, service, &hint, &rai);
195         if (rc != 0) {
196                 errno = EINVAL;
197                 return -1;
198         }
199
200         /* get the socket */
201         iai = rai;
202         while (iai != NULL) {
203                 fd = socket(iai->ai_family, iai->ai_socktype, iai->ai_protocol);
204                 if (fd >= 0) {
205                         if (server) {
206                                 if (reuseaddr) {
207                                         rc = 1;
208                                         setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &rc, sizeof rc);
209                                 }
210                                 rc = bind(fd, iai->ai_addr, iai->ai_addrlen);
211                         } else {
212                                 rc = connect(fd, iai->ai_addr, iai->ai_addrlen);
213                         }
214                         if (rc == 0) {
215                                 freeaddrinfo(rai);
216                                 return fd;
217                         }
218                         close(fd);
219                 }
220                 iai = iai->ai_next;
221         }
222         freeaddrinfo(rai);
223         return -1;
224 }
225
226 /******************************************************************************/
227 #if WITH_SYSTEMD
228
229 #include "systemd.h"
230
231 /**
232  * open a systemd socket for server
233  *
234  * @param spec the specification of the systemd name
235  *
236  * @return the file descriptor number of the socket or -1 in case of error
237  */
238 static int open_systemd(const char *spec)
239 {
240         return systemd_fds_for(spec);
241 }
242 #endif
243
244 /******************************************************************************/
245 #if WITH_L4VSOCK
246
247 struct sockaddr_l4
248 {
249   unsigned short sl4_family;
250   unsigned short port;
251   char name[8];
252   char _pad[4];
253 };
254
255 enum address_families_l4
256 {
257   AF_VIO_SOCK = 50,  /* virtio-based sockets, must match the number in Linux */
258   PF_VIO_SOCK = AF_VIO_SOCK,
259 };
260
261 #define DEFAULT_L4VSOCK_PORT 7777
262
263 /**
264  * open a L4 VSOCK socket for client or server
265  *
266  * @param spec the specification of the path (prefix with @ for abstract)
267  * @param server 0 for client, server otherwise
268  *
269  * @return the file descriptor number of the socket or -1 in case of error
270  */
271 static int open_l4(const char *spec, int server)
272 {
273         int fd, rc;
274         struct sockaddr_l4 addr;
275         const char *port, *slash;
276         unsigned short portnum;
277         size_t length;
278
279         /* scan the spec */
280         port = strchr(spec, ':');
281         slash = strchr(spec, '/');
282         if (port && slash && slash < port) {
283                 errno = EINVAL;
284                 return -1;
285         }
286         if (port) {
287                 rc = atoi(port + 1);
288                 if (rc <= 0 && rc > UINT16_MAX) {
289                         errno = EINVAL;
290                         return -1;
291                 }
292                 portnum = (unsigned short)rc;
293                 length = port - spec;
294         } else {
295                 portnum = DEFAULT_L4VSOCK_PORT;
296                 length = slash ? slash - spec : strlen(spec);
297         }
298
299         /* check the length */
300         if (length >= sizeof addr.name) {
301                 errno = ENAMETOOLONG;
302                 return -1;
303         }
304
305         /* create a  socket */
306         fd = socket(PF_VIO_SOCK, SOCK_STREAM, 0);
307         if (fd < 0)
308                 return fd;
309
310         /* prepare address  */
311         memset(&addr, 0, sizeof addr);
312         addr.sl4_family = AF_VIO_SOCK;
313         addr.port = portnum;
314         memcpy(addr.name, spec, length);
315
316         if (server) {
317                 rc = bind(fd, (struct sockaddr *) &addr, (socklen_t)(sizeof addr));
318         } else {
319                 rc = connect(fd, (struct sockaddr *) &addr, (socklen_t)(sizeof addr));
320         }
321         if (rc < 0) {
322                 close(fd);
323                 return rc;
324         }
325         return fd;
326 }
327
328 #endif
329
330 /******************************************************************************/
331
332 /**
333  * Get the entry of the uri by searching to its prefix
334  *
335  * @param uri the searched uri
336  * @param offset where to store the prefix length
337  *
338  * @return the found entry or the default one
339  */
340 static struct entry *get_entry(const char *uri, int *offset)
341 {
342         int l, i = (int)(sizeof entries / sizeof * entries);
343
344         for (;;) {
345                 if (!i) {
346                         l = 0;
347                         break;
348                 }
349                 i--;
350                 l = (int)strlen(entries[i].prefix);
351                 if (!strncmp(uri, entries[i].prefix, l))
352                         break;
353         }
354
355         *offset = l;
356         return &entries[i];
357 }
358
359 /**
360  * open socket for client or server
361  *
362  * @param uri the specification of the socket
363  * @param server 0 for client, server otherwise
364  *
365  * @return the file descriptor number of the socket or -1 in case of error
366  */
367 static int open_uri(const char *uri, int server)
368 {
369         int fd, offset;
370         struct entry *e;
371         const char *api;
372
373         /* search for the entry */
374         e = get_entry(uri, &offset);
375
376         /* get the names */
377         uri += offset;
378         api = strstr(uri, as_api);
379         if (api)
380                 uri = strndupa(uri, api - uri);
381
382         /* open the socket */
383         switch (e->type) {
384         case Type_Unix:
385                 fd = open_unix(uri, server);
386                 break;
387         case Type_Inet:
388                 fd = open_tcp(uri, server, !e->noreuseaddr);
389                 break;
390 #if WITH_SYSTEMD
391         case Type_Systemd:
392                 if (server)
393                         fd = open_systemd(uri);
394                 else {
395                         errno = EINVAL;
396                         fd = -1;
397                 }
398                 break;
399 #endif
400 #if WITH_L4VSOCK
401         case Type_L4:
402                 fd = open_l4(uri, server);
403                 break;
404 #endif
405         default:
406                 errno = EAFNOSUPPORT;
407                 fd = -1;
408                 break;
409         }
410         if (fd < 0)
411                 return -1;
412
413         /* set it up */
414         fcntl(fd, F_SETFD, FD_CLOEXEC);
415         fcntl(fd, F_SETFL, O_NONBLOCK);
416         if (server) {
417                 if (!e->nolisten)
418                         listen(fd, BACKLOG);
419         }
420         return fd;
421 }
422
423 /**
424  * open socket for client or server
425  *
426  * @param uri the specification of the socket
427  * @param server 0 for client, server otherwise
428  *
429  * @return the file descriptor number of the socket or -1 in case of error
430  */
431 int afb_socket_open(const char *uri, int server)
432 {
433         int fd = open_uri(uri, server);
434         if (fd < 0)
435                 ERROR("can't open %s socket for %s", server ? "server" : "client", uri);
436         return fd;
437 }
438
439 /**
440  * open socket for client or server
441  *
442  * @param uri the specification of the socket
443  * @param server 0 for client, server otherwise
444  *
445  * @return the fdev of the socket or NULL in case of error
446  */
447 struct fdev *afb_socket_open_fdev(const char *uri, int server)
448 {
449         struct fdev *fdev;
450         int fd;
451
452         fd = afb_socket_open(uri, server);
453         if (fd < 0)
454                 fdev = NULL;
455         else {
456                 fdev = afb_fdev_create(fd);
457                 if (!fdev) {
458                         close(fd);
459                         ERROR("can't make %s socket for %s", server ? "server" : "client", uri);
460                 }
461         }
462         return fdev;
463 }
464
465 /**
466  * Get the api name of the uri
467  *
468  * @param uri the specification of the socket
469  *
470  * @return the api name or NULL if none can be deduced
471  */
472 const char *afb_socket_api(const char *uri)
473 {
474         int offset;
475         const char *api;
476         struct entry *entry;
477
478         entry = get_entry(uri, &offset);
479         uri += offset;
480         uri += (entry->type == Type_Unix && *uri == '@');
481         api = strstr(uri, as_api);
482         if (api)
483                 api += sizeof as_api - 1;
484         else {
485                 api = strrchr(uri, '/');
486                 if (api)
487                         api++;
488                 else
489                         api = uri;
490                 if (strchr(api, ':'))
491                         api = NULL;
492         }
493         return api;
494 }