2 * Copyright (C) 2015-2018 "IoT.bzh"
3 * Author José Bollo <jose.bollo@iot.bzh>
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
9 * http://www.apache.org/licenses/LICENSE-2.0
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.
28 #include <sys/types.h>
29 #include <sys/socket.h>
33 #include "afb-socket.h"
34 #include "afb-systemd.h"
40 /******************************************************************************/
57 * Structure for known entries
61 /** the known prefix */
64 /** the type of the entry */
67 /** should not set SO_REUSEADDR for servers */
68 unsigned noreuseaddr: 1;
70 /** should not call listen for servers */
75 * The known entries with the default one at the first place
77 static struct entry entries[] = {
95 * It is possible to set explicit api name instead of using the
98 static const char as_api[] = "?as-api=";
100 /******************************************************************************/
103 * open a unix domain socket for client or server
105 * @param spec the specification of the path (prefix with @ for abstract)
106 * @param server 0 for client, server otherwise
108 * @return the file descriptor number of the socket or -1 in case of error
110 static int open_unix(const char *spec, int server)
112 int fd, rc, abstract;
113 struct sockaddr_un addr;
116 abstract = spec[0] == '@';
118 /* check the length */
119 length = strlen(spec);
121 errno = ENAMETOOLONG;
125 /* remove the file on need */
126 if (server && !abstract)
129 /* create a socket */
130 fd = socket(AF_UNIX, SOCK_STREAM, 0);
134 /* prepare address */
135 memset(&addr, 0, sizeof addr);
136 addr.sun_family = AF_UNIX;
137 strcpy(addr.sun_path, spec);
139 addr.sun_path[0] = 0; /* implement abstract sockets */
142 rc = bind(fd, (struct sockaddr *) &addr, (socklen_t)(sizeof addr));
144 rc = connect(fd, (struct sockaddr *) &addr, (socklen_t)(sizeof addr));
154 * open a tcp socket for client or server
156 * @param spec the specification of the host:port/...
157 * @param server 0 for client, server otherwise
159 * @return the file descriptor number of the socket or -1 in case of error
161 static int open_tcp(const char *spec, int server)
164 const char *service, *host, *tail;
165 struct addrinfo hint, *rai, *iai;
168 tail = strchrnul(spec, '/');
169 service = strchr(spec, ':');
170 if (tail == NULL || service == NULL || tail < service) {
174 host = strndupa(spec, service++ - spec);
175 service = strndupa(service, tail - service);
178 memset(&hint, 0, sizeof hint);
179 hint.ai_family = AF_INET;
180 hint.ai_socktype = SOCK_STREAM;
182 hint.ai_flags = AI_PASSIVE;
183 if (host[0] == 0 || (host[0] == '*' && host[1] == 0))
186 rc = getaddrinfo(host, service, &hint, &rai);
194 while (iai != NULL) {
195 fd = socket(iai->ai_family, iai->ai_socktype, iai->ai_protocol);
198 rc = bind(fd, iai->ai_addr, iai->ai_addrlen);
200 rc = connect(fd, iai->ai_addr, iai->ai_addrlen);
215 * open a systemd socket for server
217 * @param spec the specification of the systemd name
219 * @return the file descriptor number of the socket or -1 in case of error
221 static int open_systemd(const char *spec)
223 #if defined(NO_SYSTEMD_ACTIVATION)
224 errno = EAFNOSUPPORT;
227 return afb_systemd_fds_for(spec);
231 /******************************************************************************/
234 * Get the entry of the uri by searching to its prefix
236 * @param uri the searched uri
237 * @param offset where to store the prefix length
239 * @return the found entry or the default one
241 static struct entry *get_entry(const char *uri, int *offset)
243 int l, i = (int)(sizeof entries / sizeof * entries);
251 l = (int)strlen(entries[i].prefix);
252 if (!strncmp(uri, entries[i].prefix, l))
261 * open socket for client or server
263 * @param uri the specification of the socket
264 * @param server 0 for client, server otherwise
266 * @return the file descriptor number of the socket or -1 in case of error
268 static int open_uri(const char *uri, int server)
274 /* search for the entry */
275 e = get_entry(uri, &offset);
279 api = strstr(uri, as_api);
281 uri = strndupa(uri, api - uri);
283 /* open the socket */
286 fd = open_unix(uri, server);
289 fd = open_tcp(uri, server);
293 fd = open_systemd(uri);
300 errno = EAFNOSUPPORT;
308 fcntl(fd, F_SETFD, FD_CLOEXEC);
309 fcntl(fd, F_SETFL, O_NONBLOCK);
311 if (!e->noreuseaddr) {
313 setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &rc, sizeof rc);
322 * open socket for client or server
324 * @param uri the specification of the socket
325 * @param server 0 for client, server otherwise
327 * @return the file descriptor number of the socket or -1 in case of error
329 int afb_socket_open(const char *uri, int server)
331 int fd = open_uri(uri, server);
333 ERROR("can't open %s socket for %s", server ? "server" : "client", uri);
338 * open socket for client or server
340 * @param uri the specification of the socket
341 * @param server 0 for client, server otherwise
343 * @return the fdev of the socket or NULL in case of error
345 struct fdev *afb_socket_open_fdev(const char *uri, int server)
350 fd = afb_socket_open(uri, server);
354 fdev = afb_fdev_create(fd);
357 ERROR("can't make %s socket for %s", server ? "server" : "client", uri);
364 * Get the api name of the uri
366 * @param uri the specification of the socket
368 * @return the api name or NULL if none can be deduced
370 const char *afb_socket_api(const char *uri)
376 entry = get_entry(uri, &offset);
378 uri += (entry->type == Type_Unix && *uri == '@');
379 api = strstr(uri, as_api);
381 api += sizeof as_api - 1;
383 api = strrchr(uri, '/');
388 if (strchr(api, ':'))