cf788de6225c54ac7421ad70b1abade0c79bfd27
[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 "systemd.h"
36 #include "fdev.h"
37 #include "verbose.h"
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 Unix */
54         Type_Unix
55 };
56
57 /**
58  * Structure for known entries
59  */
60 struct entry
61 {
62         /** the known prefix */
63         const char *prefix;
64
65         /** the type of the entry */
66         unsigned type: 2;
67
68         /** should not set SO_REUSEADDR for servers */
69         unsigned noreuseaddr: 1;
70
71         /** should not call listen for servers */
72         unsigned nolisten: 1;
73 };
74
75 /**
76  * The known entries with the default one at the first place
77  */
78 static struct entry entries[] = {
79         {
80                 .prefix = "tcp:",
81                 .type = Type_Inet
82         },
83         {
84                 .prefix = "sd:",
85                 .type = Type_Systemd,
86                 .noreuseaddr = 1,
87                 .nolisten = 1
88         },
89         {
90                 .prefix = "unix:",
91                 .type = Type_Unix
92         }
93 };
94
95 /**
96  * It is possible to set explicit api name instead of using the
97  * default one.
98  */
99 static const char as_api[] = "?as-api=";
100
101 /******************************************************************************/
102
103 /**
104  * open a unix domain socket for client or server
105  *
106  * @param spec the specification of the path (prefix with @ for abstract)
107  * @param server 0 for client, server otherwise
108  *
109  * @return the file descriptor number of the socket or -1 in case of error
110  */
111 static int open_unix(const char *spec, int server)
112 {
113         int fd, rc, abstract;
114         struct sockaddr_un addr;
115         size_t length;
116
117         abstract = spec[0] == '@';
118
119         /* check the length */
120         length = strlen(spec);
121         if (length >= 108) {
122                 errno = ENAMETOOLONG;
123                 return -1;
124         }
125
126         /* remove the file on need */
127         if (server && !abstract)
128                 unlink(spec);
129
130         /* create a  socket */
131         fd = socket(AF_UNIX, SOCK_STREAM, 0);
132         if (fd < 0)
133                 return fd;
134
135         /* prepare address  */
136         memset(&addr, 0, sizeof addr);
137         addr.sun_family = AF_UNIX;
138         strcpy(addr.sun_path, spec);
139         if (abstract)
140                 addr.sun_path[0] = 0; /* implement abstract sockets */
141
142         if (server) {
143                 rc = bind(fd, (struct sockaddr *) &addr, (socklen_t)(sizeof addr));
144         } else {
145                 rc = connect(fd, (struct sockaddr *) &addr, (socklen_t)(sizeof addr));
146         }
147         if (rc < 0) {
148                 close(fd);
149                 return rc;
150         }
151         return fd;
152 }
153
154 /**
155  * open a tcp socket for client or server
156  *
157  * @param spec the specification of the host:port/...
158  * @param server 0 for client, server otherwise
159  *
160  * @return the file descriptor number of the socket or -1 in case of error
161  */
162 static int open_tcp(const char *spec, int server, int reuseaddr)
163 {
164         int rc, fd;
165         const char *service, *host, *tail;
166         struct addrinfo hint, *rai, *iai;
167
168         /* scan the uri */
169         tail = strchrnul(spec, '/');
170         service = strchr(spec, ':');
171         if (tail == NULL || service == NULL || tail < service) {
172                 errno = EINVAL;
173                 return -1;
174         }
175         host = strndupa(spec, service++ - spec);
176         service = strndupa(service, tail - service);
177
178         /* get addr */
179         memset(&hint, 0, sizeof hint);
180         hint.ai_family = AF_INET;
181         hint.ai_socktype = SOCK_STREAM;
182         if (server) {
183                 hint.ai_flags = AI_PASSIVE;
184                 if (host[0] == 0 || (host[0] == '*' && host[1] == 0))
185                         host = NULL;
186         }
187         rc = getaddrinfo(host, service, &hint, &rai);
188         if (rc != 0) {
189                 errno = EINVAL;
190                 return -1;
191         }
192
193         /* get the socket */
194         iai = rai;
195         while (iai != NULL) {
196                 fd = socket(iai->ai_family, iai->ai_socktype, iai->ai_protocol);
197                 if (fd >= 0) {
198                         if (server) {
199                                 if (reuseaddr) {
200                                         rc = 1;
201                                         setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &rc, sizeof rc);
202                                 }
203                                 rc = bind(fd, iai->ai_addr, iai->ai_addrlen);
204                         } else {
205                                 rc = connect(fd, iai->ai_addr, iai->ai_addrlen);
206                         }
207                         if (rc == 0) {
208                                 freeaddrinfo(rai);
209                                 return fd;
210                         }
211                         close(fd);
212                 }
213                 iai = iai->ai_next;
214         }
215         freeaddrinfo(rai);
216         return -1;
217 }
218
219 /**
220  * open a systemd socket for server
221  *
222  * @param spec the specification of the systemd name
223  *
224  * @return the file descriptor number of the socket or -1 in case of error
225  */
226 static int open_systemd(const char *spec)
227 {
228 #if defined(NO_SYSTEMD_ACTIVATION)
229         errno = EAFNOSUPPORT;
230         return -1;
231 #else
232         return systemd_fds_for(spec);
233 #endif
234 }
235
236 /******************************************************************************/
237
238 /**
239  * Get the entry of the uri by searching to its prefix
240  *
241  * @param uri the searched uri
242  * @param offset where to store the prefix length
243  *
244  * @return the found entry or the default one
245  */
246 static struct entry *get_entry(const char *uri, int *offset)
247 {
248         int l, i = (int)(sizeof entries / sizeof * entries);
249
250         for (;;) {
251                 if (!i) {
252                         l = 0;
253                         break;
254                 }
255                 i--;
256                 l = (int)strlen(entries[i].prefix);
257                 if (!strncmp(uri, entries[i].prefix, l))
258                         break;
259         }
260
261         *offset = l;
262         return &entries[i];
263 }
264
265 /**
266  * open socket for client or server
267  *
268  * @param uri the specification of the socket
269  * @param server 0 for client, server otherwise
270  *
271  * @return the file descriptor number of the socket or -1 in case of error
272  */
273 static int open_uri(const char *uri, int server)
274 {
275         int fd, offset;
276         struct entry *e;
277         const char *api;
278
279         /* search for the entry */
280         e = get_entry(uri, &offset);
281
282         /* get the names */
283         uri += offset;
284         api = strstr(uri, as_api);
285         if (api)
286                 uri = strndupa(uri, api - uri);
287
288         /* open the socket */
289         switch (e->type) {
290         case Type_Unix:
291                 fd = open_unix(uri, server);
292                 break;
293         case Type_Inet:
294                 fd = open_tcp(uri, server, !e->noreuseaddr);
295                 break;
296         case Type_Systemd:
297                 if (server)
298                         fd = open_systemd(uri);
299                 else {
300                         errno = EINVAL;
301                         fd = -1;
302                 }
303                 break;
304         default:
305                 errno = EAFNOSUPPORT;
306                 fd = -1;
307                 break;
308         }
309         if (fd < 0)
310                 return -1;
311
312         /* set it up */
313         fcntl(fd, F_SETFD, FD_CLOEXEC);
314         fcntl(fd, F_SETFL, O_NONBLOCK);
315         if (server) {
316                 if (!e->nolisten)
317                         listen(fd, BACKLOG);
318         }
319         return fd;
320 }
321
322 /**
323  * open socket for client or server
324  *
325  * @param uri the specification of the socket
326  * @param server 0 for client, server otherwise
327  *
328  * @return the file descriptor number of the socket or -1 in case of error
329  */
330 int afb_socket_open(const char *uri, int server)
331 {
332         int fd = open_uri(uri, server);
333         if (fd < 0)
334                 ERROR("can't open %s socket for %s", server ? "server" : "client", uri);
335         return fd;
336 }
337
338 /**
339  * open socket for client or server
340  *
341  * @param uri the specification of the socket
342  * @param server 0 for client, server otherwise
343  *
344  * @return the fdev of the socket or NULL in case of error
345  */
346 struct fdev *afb_socket_open_fdev(const char *uri, int server)
347 {
348         struct fdev *fdev;
349         int fd;
350
351         fd = afb_socket_open(uri, server);
352         if (fd < 0)
353                 fdev = NULL;
354         else {
355                 fdev = afb_fdev_create(fd);
356                 if (!fdev) {
357                         close(fd);
358                         ERROR("can't make %s socket for %s", server ? "server" : "client", uri);
359                 }
360         }
361         return fdev;
362 }
363
364 /**
365  * Get the api name of the uri
366  *
367  * @param uri the specification of the socket
368  *
369  * @return the api name or NULL if none can be deduced
370  */
371 const char *afb_socket_api(const char *uri)
372 {
373         int offset;
374         const char *api;
375         struct entry *entry;
376
377         entry = get_entry(uri, &offset);
378         uri += offset;
379         uri += (entry->type == Type_Unix && *uri == '@');
380         api = strstr(uri, as_api);
381         if (api)
382                 api += sizeof as_api - 1;
383         else {
384                 api = strrchr(uri, '/');
385                 if (api)
386                         api++;
387                 else
388                         api = uri;
389                 if (strchr(api, ':'))
390                         api = NULL;
391         }
392         return api;
393 }