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