afb-socket: Relax requirement of tcp ending slash
[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 = 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                                 rc = bind(fd, iai->ai_addr, iai->ai_addrlen);
199                         } else {
200                                 rc = connect(fd, iai->ai_addr, iai->ai_addrlen);
201                         }
202                         if (rc == 0) {
203                                 freeaddrinfo(rai);
204                                 return fd;
205                         }
206                         close(fd);
207                 }
208                 iai = iai->ai_next;
209         }
210         freeaddrinfo(rai);
211         return -1;
212 }
213
214 /**
215  * open a systemd socket for server
216  *
217  * @param spec the specification of the systemd name
218  *
219  * @return the file descriptor number of the socket or -1 in case of error
220  */
221 static int open_systemd(const char *spec)
222 {
223 #if defined(NO_SYSTEMD_ACTIVATION)
224         errno = EAFNOSUPPORT;
225         return -1;
226 #else
227         return afb_systemd_fds_for(spec);
228 #endif
229 }
230
231 /******************************************************************************/
232
233 /**
234  * Get the entry of the uri by searching to its prefix
235  *
236  * @param uri the searched uri
237  * @param offset where to store the prefix length
238  *
239  * @return the found entry or the default one
240  */
241 static struct entry *get_entry(const char *uri, int *offset)
242 {
243         int l, i = (int)(sizeof entries / sizeof * entries);
244
245         for (;;) {
246                 if (!i) {
247                         l = 0;
248                         break;
249                 }
250                 i--;
251                 l = (int)strlen(entries[i].prefix);
252                 if (!strncmp(uri, entries[i].prefix, l))
253                         break;
254         }
255
256         *offset = l;
257         return &entries[i];
258 }
259
260 /**
261  * open socket for client or server
262  *
263  * @param uri the specification of the socket
264  * @param server 0 for client, server otherwise
265  *
266  * @return the file descriptor number of the socket or -1 in case of error
267  */
268 static int open_uri(const char *uri, int server)
269 {
270         int fd, rc, offset;
271         struct entry *e;
272         const char *api;
273
274         /* search for the entry */
275         e = get_entry(uri, &offset);
276
277         /* get the names */
278         uri += offset;
279         api = strstr(uri, as_api);
280         if (api)
281                 uri = strndupa(uri, api - uri);
282
283         /* open the socket */
284         switch (e->type) {
285         case Type_Unix:
286                 fd = open_unix(uri, server);
287                 break;
288         case Type_Inet:
289                 fd = open_tcp(uri, server);
290                 break;
291         case Type_Systemd:
292                 if (server)
293                         fd = open_systemd(uri);
294                 else {
295                         errno = EINVAL;
296                         fd = -1;
297                 }
298                 break;
299         default:
300                 errno = EAFNOSUPPORT;
301                 fd = -1;
302                 break;
303         }
304         if (fd < 0)
305                 return -1;
306
307         /* set it up */
308         fcntl(fd, F_SETFD, FD_CLOEXEC);
309         fcntl(fd, F_SETFL, O_NONBLOCK);
310         if (server) {
311                 if (!e->noreuseaddr) {
312                         rc = 1;
313                         setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &rc, sizeof rc);
314                 }
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 }