2 * Copyright (C) 2015-2019 "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"
41 /******************************************************************************/
58 * Structure for known entries
62 /** the known prefix */
65 /** the type of the entry */
68 /** should not set SO_REUSEADDR for servers */
69 unsigned noreuseaddr: 1;
71 /** should not call listen for servers */
76 * The known entries with the default one at the first place
78 static struct entry entries[] = {
96 * It is possible to set explicit api name instead of using the
99 static const char as_api[] = "?as-api=";
101 /******************************************************************************/
104 * open a unix domain socket for client or server
106 * @param spec the specification of the path (prefix with @ for abstract)
107 * @param server 0 for client, server otherwise
109 * @return the file descriptor number of the socket or -1 in case of error
111 static int open_unix(const char *spec, int server)
113 int fd, rc, abstract;
114 struct sockaddr_un addr;
117 abstract = spec[0] == '@';
119 /* check the length */
120 length = strlen(spec);
122 errno = ENAMETOOLONG;
126 /* remove the file on need */
127 if (server && !abstract)
130 /* create a socket */
131 fd = socket(AF_UNIX, SOCK_STREAM, 0);
135 /* prepare address */
136 memset(&addr, 0, sizeof addr);
137 addr.sun_family = AF_UNIX;
138 strcpy(addr.sun_path, spec);
140 addr.sun_path[0] = 0; /* implement abstract sockets */
143 rc = bind(fd, (struct sockaddr *) &addr, (socklen_t)(sizeof addr));
145 rc = connect(fd, (struct sockaddr *) &addr, (socklen_t)(sizeof addr));
155 * open a tcp socket for client or server
157 * @param spec the specification of the host:port/...
158 * @param server 0 for client, server otherwise
160 * @return the file descriptor number of the socket or -1 in case of error
162 static int open_tcp(const char *spec, int server, int reuseaddr)
165 const char *service, *host, *tail;
166 struct addrinfo hint, *rai, *iai;
169 tail = strchrnul(spec, '/');
170 service = strchr(spec, ':');
171 if (tail == NULL || service == NULL || tail < service) {
175 host = strndupa(spec, service++ - spec);
176 service = strndupa(service, tail - service);
179 memset(&hint, 0, sizeof hint);
180 hint.ai_family = AF_INET;
181 hint.ai_socktype = SOCK_STREAM;
183 hint.ai_flags = AI_PASSIVE;
184 if (host[0] == 0 || (host[0] == '*' && host[1] == 0))
187 rc = getaddrinfo(host, service, &hint, &rai);
195 while (iai != NULL) {
196 fd = socket(iai->ai_family, iai->ai_socktype, iai->ai_protocol);
201 setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &rc, sizeof rc);
203 rc = bind(fd, iai->ai_addr, iai->ai_addrlen);
205 rc = connect(fd, iai->ai_addr, iai->ai_addrlen);
220 * open a systemd socket for server
222 * @param spec the specification of the systemd name
224 * @return the file descriptor number of the socket or -1 in case of error
226 static int open_systemd(const char *spec)
228 #if defined(NO_SYSTEMD_ACTIVATION)
229 errno = EAFNOSUPPORT;
232 return systemd_fds_for(spec);
236 /******************************************************************************/
239 * Get the entry of the uri by searching to its prefix
241 * @param uri the searched uri
242 * @param offset where to store the prefix length
243 * @param scheme the default scheme to use if none is set in uri (can be NULL)
245 * @return the found entry or the default one
247 static struct entry *get_entry(const char *uri, int *offset, const char *scheme)
251 /* search as prefix of URI */
252 i = (int)(sizeof entries / sizeof * entries);
255 len = (int)strlen(entries[i].prefix);
256 if (!strncmp(uri, entries[i].prefix, len))
257 goto end; /* found */
260 /* not a prefix of uri */
263 /* search default scheme if given and valid */
264 if (scheme && *scheme) {
265 deflen = (int)strlen(scheme);
266 deflen += (scheme[deflen - 1] != ':'); /* add virtual trailing colon */
267 i = (int)(sizeof entries / sizeof * entries);
270 if (deflen == (int)strlen(entries[i].prefix)
271 && !strncmp(scheme, entries[i].prefix, deflen - 1))
272 goto end; /* found */
282 * open socket for client or server
284 * @param uri the specification of the socket
285 * @param server 0 for client, server otherwise
286 * @param scheme the default scheme to use if none is set in uri (can be NULL)
288 * @return the file descriptor number of the socket or -1 in case of error
290 static int open_uri(const char *uri, int server, const char *scheme)
296 /* search for the entry */
297 e = get_entry(uri, &offset, scheme);
301 api = strstr(uri, as_api);
303 uri = strndupa(uri, api - uri);
305 /* open the socket */
308 fd = open_unix(uri, server);
311 fd = open_tcp(uri, server, !e->noreuseaddr);
315 fd = open_systemd(uri);
322 errno = EAFNOSUPPORT;
330 fcntl(fd, F_SETFD, FD_CLOEXEC);
331 fcntl(fd, F_SETFL, O_NONBLOCK);
340 * open socket for client or server
342 * @param uri the specification of the socket
343 * @param server 0 for client, server otherwise
344 * @param scheme the default scheme to use if none is set in uri (can be NULL)
346 * @return the file descriptor number of the socket or -1 in case of error
348 int afb_socket_open_scheme(const char *uri, int server, const char *scheme)
350 int fd = open_uri(uri, server, scheme);
352 ERROR("can't open %s socket for %s", server ? "server" : "client", uri);
357 * open socket for client or server
359 * @param uri the specification of the socket
360 * @param server 0 for client, server otherwise
361 * @param scheme the default scheme to use if none is set in uri (can be NULL)
363 * @return the fdev of the socket or NULL in case of error
365 struct fdev *afb_socket_open_fdev_scheme(const char *uri, int server, const char *scheme)
370 fd = afb_socket_open_scheme(uri, server, scheme);
374 fdev = afb_fdev_create(fd);
377 ERROR("can't make %s socket for %s", server ? "server" : "client", uri);
384 * Get the api name of the uri
386 * @param uri the specification of the socket
388 * @return the api name or NULL if none can be deduced
390 const char *afb_socket_api(const char *uri)
396 entry = get_entry(uri, &offset, NULL);
398 uri += (entry->type == Type_Unix && *uri == '@');
399 api = strstr(uri, as_api);
401 api += sizeof as_api - 1;
403 api = strrchr(uri, '/');
408 if (strchr(api, ':'))