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"
38 /******************************************************************************/
41 /******************************************************************************/
53 /** type virtual socket of L4 */
61 * Structure for known entries
65 /** the known prefix */
68 /** the type of the entry */
71 /** should not set SO_REUSEADDR for servers */
72 unsigned noreuseaddr: 1;
74 /** should not call listen for servers */
79 * The known entries with the default one at the first place
81 static struct entry entries[] = {
103 * It is possible to set explicit api name instead of using the
106 static const char as_api[] = "?as-api=";
108 /******************************************************************************/
111 * open a unix domain socket for client or server
113 * @param spec the specification of the path (prefix with @ for abstract)
114 * @param server 0 for client, server otherwise
116 * @return the file descriptor number of the socket or -1 in case of error
118 static int open_unix(const char *spec, int server)
120 int fd, rc, abstract;
121 struct sockaddr_un addr;
124 abstract = spec[0] == '@';
126 /* check the length */
127 length = strlen(spec);
129 errno = ENAMETOOLONG;
133 /* remove the file on need */
134 if (server && !abstract)
137 /* create a socket */
138 fd = socket(AF_UNIX, SOCK_STREAM, 0);
142 /* prepare address */
143 memset(&addr, 0, sizeof addr);
144 addr.sun_family = AF_UNIX;
145 strcpy(addr.sun_path, spec);
147 addr.sun_path[0] = 0; /* implement abstract sockets */
150 rc = bind(fd, (struct sockaddr *) &addr, (socklen_t)(sizeof addr));
152 rc = connect(fd, (struct sockaddr *) &addr, (socklen_t)(sizeof addr));
162 * open a tcp socket for client or server
164 * @param spec the specification of the host:port/...
165 * @param server 0 for client, server otherwise
167 * @return the file descriptor number of the socket or -1 in case of error
169 static int open_tcp(const char *spec, int server, int reuseaddr)
172 const char *service, *host, *tail;
173 struct addrinfo hint, *rai, *iai;
176 tail = strchrnul(spec, '/');
177 service = strchr(spec, ':');
178 if (tail == NULL || service == NULL || tail < service) {
182 host = strndupa(spec, service++ - spec);
183 service = strndupa(service, tail - service);
186 memset(&hint, 0, sizeof hint);
187 hint.ai_family = AF_INET;
188 hint.ai_socktype = SOCK_STREAM;
190 hint.ai_flags = AI_PASSIVE;
191 if (host[0] == 0 || (host[0] == '*' && host[1] == 0))
194 rc = getaddrinfo(host, service, &hint, &rai);
202 while (iai != NULL) {
203 fd = socket(iai->ai_family, iai->ai_socktype, iai->ai_protocol);
208 setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &rc, sizeof rc);
210 rc = bind(fd, iai->ai_addr, iai->ai_addrlen);
212 rc = connect(fd, iai->ai_addr, iai->ai_addrlen);
226 /******************************************************************************/
232 * open a systemd socket for server
234 * @param spec the specification of the systemd name
236 * @return the file descriptor number of the socket or -1 in case of error
238 static int open_systemd(const char *spec)
240 return systemd_fds_for(spec);
244 /******************************************************************************/
249 unsigned short sl4_family;
255 enum address_families_l4
257 AF_VIO_SOCK = 50, /* virtio-based sockets, must match the number in Linux */
258 PF_VIO_SOCK = AF_VIO_SOCK,
261 #define DEFAULT_L4VSOCK_PORT 7777
264 * open a L4 VSOCK socket for client or server
266 * @param spec the specification of the path (prefix with @ for abstract)
267 * @param server 0 for client, server otherwise
269 * @return the file descriptor number of the socket or -1 in case of error
271 static int open_l4(const char *spec, int server)
274 struct sockaddr_l4 addr;
275 const char *port, *slash;
276 unsigned short portnum;
280 port = strchr(spec, ':');
281 slash = strchr(spec, '/');
282 if (port && slash && slash < port) {
288 if (rc <= 0 && rc > UINT16_MAX) {
292 portnum = (unsigned short)rc;
293 length = port - spec;
295 portnum = DEFAULT_L4VSOCK_PORT;
296 length = slash ? slash - spec : strlen(spec);
299 /* check the length */
300 if (length >= sizeof addr.name) {
301 errno = ENAMETOOLONG;
305 /* create a socket */
306 fd = socket(PF_VIO_SOCK, SOCK_STREAM, 0);
310 /* prepare address */
311 memset(&addr, 0, sizeof addr);
312 addr.sl4_family = AF_VIO_SOCK;
314 memcpy(addr.name, spec, length);
317 rc = bind(fd, (struct sockaddr *) &addr, (socklen_t)(sizeof addr));
319 rc = connect(fd, (struct sockaddr *) &addr, (socklen_t)(sizeof addr));
330 /******************************************************************************/
333 * Get the entry of the uri by searching to its prefix
335 * @param uri the searched uri
336 * @param offset where to store the prefix length
338 * @return the found entry or the default one
340 static struct entry *get_entry(const char *uri, int *offset)
342 int l, i = (int)(sizeof entries / sizeof * entries);
350 l = (int)strlen(entries[i].prefix);
351 if (!strncmp(uri, entries[i].prefix, l))
360 * open socket for client or server
362 * @param uri the specification of the socket
363 * @param server 0 for client, server otherwise
365 * @return the file descriptor number of the socket or -1 in case of error
367 static int open_uri(const char *uri, int server)
373 /* search for the entry */
374 e = get_entry(uri, &offset);
378 api = strstr(uri, as_api);
380 uri = strndupa(uri, api - uri);
382 /* open the socket */
385 fd = open_unix(uri, server);
388 fd = open_tcp(uri, server, !e->noreuseaddr);
393 fd = open_systemd(uri);
402 fd = open_l4(uri, server);
406 errno = EAFNOSUPPORT;
414 fcntl(fd, F_SETFD, FD_CLOEXEC);
415 fcntl(fd, F_SETFL, O_NONBLOCK);
424 * open socket for client or server
426 * @param uri the specification of the socket
427 * @param server 0 for client, server otherwise
429 * @return the file descriptor number of the socket or -1 in case of error
431 int afb_socket_open(const char *uri, int server)
433 int fd = open_uri(uri, server);
435 ERROR("can't open %s socket for %s", server ? "server" : "client", uri);
440 * open socket for client or server
442 * @param uri the specification of the socket
443 * @param server 0 for client, server otherwise
445 * @return the fdev of the socket or NULL in case of error
447 struct fdev *afb_socket_open_fdev(const char *uri, int server)
452 fd = afb_socket_open(uri, server);
456 fdev = afb_fdev_create(fd);
459 ERROR("can't make %s socket for %s", server ? "server" : "client", uri);
466 * Get the api name of the uri
468 * @param uri the specification of the socket
470 * @return the api name or NULL if none can be deduced
472 const char *afb_socket_api(const char *uri)
478 entry = get_entry(uri, &offset);
480 uri += (entry->type == Type_Unix && *uri == '@');
481 api = strstr(uri, as_api);
483 api += sizeof as_api - 1;
485 api = strrchr(uri, '/');
490 if (strchr(api, ':'))