afb-socket: Improvement
[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)
162 {
163         int rc, fd;
164         const char *service, *host, *tail;
165         struct addrinfo hint, *rai, *iai;
166
167         /* scan the uri */
168         tail = strchr(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         rc = getaddrinfo(host, service, &hint, &rai);
182         if (rc != 0) {
183                 errno = EINVAL;
184                 return -1;
185         }
186
187         /* get the socket */
188         iai = rai;
189         while (iai != NULL) {
190                 fd = socket(iai->ai_family, iai->ai_socktype, iai->ai_protocol);
191                 if (fd >= 0) {
192                         if (server) {
193                                 rc = bind(fd, iai->ai_addr, iai->ai_addrlen);
194                         } else {
195                                 rc = connect(fd, iai->ai_addr, iai->ai_addrlen);
196                         }
197                         if (rc == 0) {
198                                 freeaddrinfo(rai);
199                                 return fd;
200                         }
201                         close(fd);
202                 }
203                 iai = iai->ai_next;
204         }
205         freeaddrinfo(rai);
206         return -1;
207 }
208
209 /**
210  * open a systemd socket for server
211  *
212  * @param spec the specification of the systemd name
213  *
214  * @return the file descriptor number of the socket or -1 in case of error
215  */
216 static int open_systemd(const char *spec)
217 {
218 #if defined(NO_SYSTEMD_ACTIVATION)
219         errno = EAFNOSUPPORT;
220         fd = -1;
221 #else
222         return afb_systemd_fds_for(spec);
223 #endif
224 }
225
226 /******************************************************************************/
227
228 /**
229  * Get the entry of the uri by searching to its prefix
230  *
231  * @param uri the searched uri
232  * @param offset where to store the prefix length
233  *
234  * @return the found entry or the default one
235  */
236 static struct entry *get_entry(const char *uri, int *offset)
237 {
238         int l, i = (int)(sizeof entries / sizeof * entries);
239
240         for (;;) {
241                 if (!i) {
242                         l = 0;
243                         break;
244                 }
245                 i--;
246                 l = (int)strlen(entries[i].prefix);
247                 if (!strncmp(uri, entries[i].prefix, l))
248                         break;
249         }
250
251         *offset = l;
252         return &entries[i];
253 }
254
255 /**
256  * open socket for client or server
257  *
258  * @param uri the specification of the socket
259  * @param server 0 for client, server otherwise
260  *
261  * @return the file descriptor number of the socket or -1 in case of error
262  */
263 static int open_uri(const char *uri, int server)
264 {
265         int fd, rc, offset;
266         struct entry *e;
267         const char *api;
268
269         /* search for the entry */
270         e = get_entry(uri, &offset);
271
272         /* get the names */
273         uri += offset;
274         api = strstr(uri, as_api);
275         if (api)
276                 uri = strndupa(uri, api - uri);
277
278         /* open the socket */
279         switch (e->type) {
280         case Type_Unix:
281                 fd = open_unix(uri, server);
282                 break;
283         case Type_Inet:
284                 fd = open_tcp(uri, server);
285                 break;
286         case Type_Systemd:
287                 if (server)
288                         fd = open_systemd(uri);
289                 else {
290                         errno = EINVAL;
291                         fd = -1;
292                 }
293                 break;
294         default:
295                 errno = EAFNOSUPPORT;
296                 fd = -1;
297                 break;
298         }
299         if (fd < 0)
300                 return -1;
301
302         /* set it up */
303         fcntl(fd, F_SETFD, FD_CLOEXEC);
304         fcntl(fd, F_SETFL, O_NONBLOCK);
305         if (server) {
306                 if (!e->noreuseaddr) {
307                         rc = 1;
308                         setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &rc, sizeof rc);
309                 }
310                 if (!e->nolisten)
311                         listen(fd, BACKLOG);
312         }
313         return fd;
314 }
315
316 /**
317  * open socket for client or server
318  *
319  * @param uri the specification of the socket
320  * @param server 0 for client, server otherwise
321  *
322  * @return the file descriptor number of the socket or -1 in case of error
323  */
324 int afb_socket_open(const char *uri, int server)
325 {
326         int fd = open_uri(uri, server);
327         if (fd < 0)
328                 ERROR("can't open %s socket for %s", server ? "server" : "client", uri);
329         return fd;
330 }
331
332 /**
333  * open socket for client or server
334  *
335  * @param uri the specification of the socket
336  * @param server 0 for client, server otherwise
337  *
338  * @return the fdev of the socket or NULL in case of error
339  */
340 struct fdev *afb_socket_open_fdev(const char *uri, int server)
341 {
342         struct fdev *fdev;
343         int fd;
344
345         fd = afb_socket_open(uri, server);
346         if (fd < 0)
347                 fdev = NULL;
348         else {
349                 fdev = afb_fdev_create(fd);
350                 if (!fdev) {
351                         close(fd);
352                         ERROR("can't make %s socket for %s", server ? "server" : "client", uri);
353                 }
354         }
355         return fdev;
356 }
357
358 /**
359  * Get the api name of the uri
360  *
361  * @param uri the specification of the socket
362  *
363  * @return the api name or NULL if none can be deduced
364  */
365 const char *afb_socket_api(const char *uri)
366 {
367         int offset;
368         const char *api;
369
370         get_entry(uri, &offset);
371         uri += offset;
372         api = strstr(uri, as_api);
373         if (api)
374                 api += sizeof as_api - 1;
375         else {
376                 api = strrchr(uri, '/');
377                 if (api)
378                         api++;
379                 else
380                         api = uri;
381                 if (strchr(api, ':'))
382                         api = NULL;
383         }
384         return api;
385 }