c6402d42ac3d3bb45cbe36193f1985564668151a
[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 static int open_unix(const char *spec, int server)
43 {
44         int fd, rc, abstract;
45         struct sockaddr_un addr;
46         size_t length;
47
48         abstract = spec[0] == '@';
49
50         /* check the length */
51         length = strlen(spec);
52         if (length >= 108) {
53                 errno = ENAMETOOLONG;
54                 return -1;
55         }
56
57         /* remove the file on need */
58         if (server && !abstract)
59                 unlink(spec);
60
61         /* create a  socket */
62         fd = socket(AF_UNIX, SOCK_STREAM, 0);
63         if (fd < 0)
64                 return fd;
65
66         /* prepare address  */
67         memset(&addr, 0, sizeof addr);
68         addr.sun_family = AF_UNIX;
69         strcpy(addr.sun_path, spec);
70         if (abstract)
71                 addr.sun_path[0] = 0; /* implement abstract sockets */
72
73         if (server) {
74                 rc = bind(fd, (struct sockaddr *) &addr, (socklen_t)(sizeof addr));
75         } else {
76                 rc = connect(fd, (struct sockaddr *) &addr, (socklen_t)(sizeof addr));
77         }
78         if (rc < 0) {
79                 close(fd);
80                 return rc;
81         }
82         return fd;
83 }
84
85 static int open_inet(const char *spec, int server)
86 {
87         int rc, fd;
88         const char *service, *host, *api;
89         struct addrinfo hint, *rai, *iai;
90
91         /* scan the uri */
92         api = strrchr(spec, '/');
93         service = strrchr(spec, ':');
94         if (api == NULL || service == NULL || api < service) {
95                 errno = EINVAL;
96                 return -1;
97         }
98         host = strndupa(spec, service++ - spec);
99         service = strndupa(service, api - service);
100
101         /* get addr */
102         memset(&hint, 0, sizeof hint);
103         hint.ai_family = AF_INET;
104         hint.ai_socktype = SOCK_STREAM;
105         rc = getaddrinfo(host, service, &hint, &rai);
106         if (rc != 0) {
107                 errno = EINVAL;
108                 return -1;
109         }
110
111         /* get the socket */
112         iai = rai;
113         while (iai != NULL) {
114                 fd = socket(iai->ai_family, iai->ai_socktype, iai->ai_protocol);
115                 if (fd >= 0) {
116                         if (server) {
117                                 rc = bind(fd, iai->ai_addr, iai->ai_addrlen);
118                         } else {
119                                 rc = connect(fd, iai->ai_addr, iai->ai_addrlen);
120                         }
121                         if (rc == 0) {
122                                 freeaddrinfo(rai);
123                                 return fd;
124                         }
125                         close(fd);
126                 }
127                 iai = iai->ai_next;
128         }
129         freeaddrinfo(rai);
130         return -1;
131 }
132
133 static int open_systemd(const char *spec)
134 {
135 #if defined(NO_SYSTEMD_ACTIVATION)
136         errno = EAFNOSUPPORT;
137         fd = -1;
138 #else
139         return afb_systemd_fds_for(spec);
140 #endif
141 }
142
143 /******************************************************************************/
144
145 enum type {
146         Type_Inet,
147         Type_Systemd,
148         Type_Unix
149 };
150
151 struct entry
152 {
153         const char *prefix;
154         unsigned type: 2;
155         unsigned noreuseaddr: 1;
156         unsigned nolisten: 1;
157 };
158
159 static struct entry entries[] = { /* default at first place */
160         {
161                 .prefix = "tcp:",
162                 .type = Type_Inet
163         },
164         {
165                 .prefix = "sd:",
166                 .type = Type_Systemd,
167                 .noreuseaddr = 1,
168                 .nolisten = 1
169         },
170         {
171                 .prefix = "unix:",
172                 .type = Type_Unix
173         }
174 };
175
176 /******************************************************************************/
177
178 /* get the entry of the uri by searching to its prefix */
179 static struct entry *get_entry(const char *uri, int *offset)
180 {
181         int l, search = 1, i = (int)(sizeof entries / sizeof * entries);
182
183         while (search) {
184                 if (!i) {
185                         l = 0;
186                         search = 0;
187                 } else {
188                         i--;
189                         l = (int)strlen(entries[i].prefix);
190                         search = strncmp(uri, entries[i].prefix, l);
191                 }
192         }
193
194         *offset = l;
195         return &entries[i];
196 }
197
198 static int open_any(const char *uri, int server)
199 {
200         int fd, rc, offset;
201         struct entry *e;
202
203         /* search for the entry */
204         e = get_entry(uri, &offset);
205
206         /* get the names */
207
208         uri += offset;
209
210         /* open the socket */
211         switch (e->type) {
212         case Type_Unix:
213                 fd = open_unix(uri, server);
214                 break;
215         case Type_Inet:
216                 fd = open_inet(uri, server);
217                 break;
218         case Type_Systemd:
219                 fd = open_systemd(uri);
220                 break;
221         default:
222                 errno = EAFNOSUPPORT;
223                 fd = -1;
224                 break;
225         }
226         if (fd < 0)
227                 return -1;
228
229         /* set it up */
230         fcntl(fd, F_SETFD, FD_CLOEXEC);
231         fcntl(fd, F_SETFL, O_NONBLOCK);
232         if (server) {
233                 if (!e->noreuseaddr) {
234                         rc = 1;
235                         setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &rc, sizeof rc);
236                 }
237                 if (!e->nolisten)
238                         listen(fd, BACKLOG);
239         }
240         return fd;
241 }
242
243 struct fdev *afb_socket_open(const char *uri, int server)
244 {
245         int fd;
246         struct fdev *fdev;
247
248         fd = open_any(uri, server);
249         if (fd < 0)
250                 goto error;
251
252         fdev = afb_fdev_create(fd);
253         if (!fdev)
254                 goto error2;
255
256         return fdev;
257
258 error2:
259         close(fd);
260 error:
261         ERROR("can't make %s socket for %s", server ? "server" : "client", uri);
262         return NULL;
263 }
264