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 = strchr(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;
181 rc = getaddrinfo(host, service, &hint, &rai);
189 while (iai != NULL) {
190 fd = socket(iai->ai_family, iai->ai_socktype, iai->ai_protocol);
193 rc = bind(fd, iai->ai_addr, iai->ai_addrlen);
195 rc = connect(fd, iai->ai_addr, iai->ai_addrlen);
210 * open a systemd socket for server
212 * @param spec the specification of the systemd name
214 * @return the file descriptor number of the socket or -1 in case of error
216 static int open_systemd(const char *spec)
218 #if defined(NO_SYSTEMD_ACTIVATION)
219 errno = EAFNOSUPPORT;
222 return afb_systemd_fds_for(spec);
226 /******************************************************************************/
229 * Get the entry of the uri by searching to its prefix
231 * @param uri the searched uri
232 * @param offset where to store the prefix length
234 * @return the found entry or the default one
236 static struct entry *get_entry(const char *uri, int *offset)
238 int l, i = (int)(sizeof entries / sizeof * entries);
246 l = (int)strlen(entries[i].prefix);
247 if (!strncmp(uri, entries[i].prefix, l))
256 * open socket for client or server
258 * @param uri the specification of the socket
259 * @param server 0 for client, server otherwise
261 * @return the file descriptor number of the socket or -1 in case of error
263 static int open_uri(const char *uri, int server)
269 /* search for the entry */
270 e = get_entry(uri, &offset);
274 api = strstr(uri, as_api);
276 uri = strndupa(uri, api - uri);
278 /* open the socket */
281 fd = open_unix(uri, server);
284 fd = open_tcp(uri, server);
288 fd = open_systemd(uri);
295 errno = EAFNOSUPPORT;
303 fcntl(fd, F_SETFD, FD_CLOEXEC);
304 fcntl(fd, F_SETFL, O_NONBLOCK);
306 if (!e->noreuseaddr) {
308 setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &rc, sizeof rc);
317 * open socket for client or server
319 * @param uri the specification of the socket
320 * @param server 0 for client, server otherwise
322 * @return the file descriptor number of the socket or -1 in case of error
324 int afb_socket_open(const char *uri, int server)
326 int fd = open_uri(uri, server);
328 ERROR("can't open %s socket for %s", server ? "server" : "client", uri);
333 * open socket for client or server
335 * @param uri the specification of the socket
336 * @param server 0 for client, server otherwise
338 * @return the fdev of the socket or NULL in case of error
340 struct fdev *afb_socket_open_fdev(const char *uri, int server)
345 fd = afb_socket_open(uri, server);
349 fdev = afb_fdev_create(fd);
352 ERROR("can't make %s socket for %s", server ? "server" : "client", uri);
359 * Get the api name of the uri
361 * @param uri the specification of the socket
363 * @return the api name or NULL if none can be deduced
365 const char *afb_socket_api(const char *uri)
371 entry = get_entry(uri, &offset);
373 uri += (entry->type == Type_Unix && *uri == '@');
374 api = strstr(uri, as_api);
376 api += sizeof as_api - 1;
378 api = strrchr(uri, '/');
383 if (strchr(api, ':'))