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