blob: 3c5548fff6415aecf5330ab4590e99e4c1eb1c4b [file] [log] [blame]
Harald Welte468b6432014-09-11 13:05:51 +08001/*
Harald Welte48f55832017-01-26 00:03:10 +01002 * (C) 2011-2017 by Harald Welte <laforge@gnumonks.org>
Harald Welte468b6432014-09-11 13:05:51 +08003 *
4 * All Rights Reserved
5 *
6 * This program is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation; either version 2 of the License, or
9 * (at your option) any later version.
10 *
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
15 *
16 * You should have received a copy of the GNU General Public License along
17 * with this program; if not, write to the Free Software Foundation, Inc.,
18 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
19 *
20 */
21
Harald Welte33cb71a2011-05-21 18:54:32 +020022#include "../config.h"
23
Harald Welteba6988b2011-08-17 12:46:48 +020024/*! \addtogroup socket
25 * @{
26 */
27
28/*! \file socket.c
29 * \brief Osmocom socket convenience functions
30 */
31
Harald Weltee4764422011-05-22 12:25:57 +020032#ifdef HAVE_SYS_SOCKET_H
33
Harald Welte33cb71a2011-05-21 18:54:32 +020034#include <osmocom/core/logging.h>
35#include <osmocom/core/select.h>
36#include <osmocom/core/socket.h>
Harald Welte48f55832017-01-26 00:03:10 +010037#include <osmocom/core/talloc.h>
Harald Welte33cb71a2011-05-21 18:54:32 +020038
Pablo Neira Ayuso0849c9a2011-06-09 15:04:30 +020039#include <sys/ioctl.h>
Harald Welte33cb71a2011-05-21 18:54:32 +020040#include <sys/socket.h>
41#include <sys/types.h>
Álvaro Neira Ayuso5ade61a2014-03-24 13:02:00 +010042#include <sys/un.h>
Harald Welte33cb71a2011-05-21 18:54:32 +020043
Holger Hans Peter Freyther47723482011-11-09 11:26:15 +010044#include <netinet/in.h>
45
Harald Welte33cb71a2011-05-21 18:54:32 +020046#include <stdio.h>
47#include <unistd.h>
48#include <stdint.h>
49#include <string.h>
50#include <errno.h>
51#include <netdb.h>
52#include <ifaddrs.h>
53
Harald Welteba6988b2011-08-17 12:46:48 +020054/*! \brief Initialize a socket (including bind/connect)
55 * \param[in] family Address Family like AF_INET, AF_INET6, AF_UNSPEC
56 * \param[in] type Socket type like SOCK_DGRAM, SOCK_STREAM
57 * \param[in] proto Protocol like IPPROTO_TCP, IPPROTO_UDP
58 * \param[in] host remote host name or IP address in string form
59 * \param[in] port remote port number in host byte order
60 * \param[in] flags flags like \ref OSMO_SOCK_F_CONNECT
Harald Welte2d2e2cc2016-04-25 12:11:20 +020061 * \returns socket file descriptor on success; negative on error
Harald Welteba6988b2011-08-17 12:46:48 +020062 *
63 * This function creates a new socket of the designated \a family, \a
64 * type and \a proto and optionally binds or connects it, depending on
65 * the value of \a flags parameter.
66 */
Harald Welte33cb71a2011-05-21 18:54:32 +020067int osmo_sock_init(uint16_t family, uint16_t type, uint8_t proto,
Pablo Neira Ayuso0849c9a2011-06-09 15:04:30 +020068 const char *host, uint16_t port, unsigned int flags)
Harald Welte33cb71a2011-05-21 18:54:32 +020069{
70 struct addrinfo hints, *result, *rp;
Harald Welte68b15742011-05-22 21:47:29 +020071 int sfd, rc, on = 1;
Harald Welte33cb71a2011-05-21 18:54:32 +020072 char portbuf[16];
73
Pablo Neira Ayuso0849c9a2011-06-09 15:04:30 +020074 if ((flags & (OSMO_SOCK_F_BIND | OSMO_SOCK_F_CONNECT)) ==
Neels Hofmeyrf0f07d92016-08-22 13:34:23 +020075 (OSMO_SOCK_F_BIND | OSMO_SOCK_F_CONNECT)) {
76 fprintf(stderr, "invalid: both bind and connect flags set:"
Neels Hofmeyrb7f191f2016-08-29 11:22:03 +020077 " %s:%u\n", host, port);
Pablo Neira Ayuso0849c9a2011-06-09 15:04:30 +020078 return -EINVAL;
Neels Hofmeyrf0f07d92016-08-22 13:34:23 +020079 }
Pablo Neira Ayuso0849c9a2011-06-09 15:04:30 +020080
Harald Welte33cb71a2011-05-21 18:54:32 +020081 sprintf(portbuf, "%u", port);
82 memset(&hints, 0, sizeof(struct addrinfo));
83 hints.ai_family = family;
Pablo Neira Ayusoe04a14d2013-01-14 00:12:28 +010084 if (type == SOCK_RAW) {
85 /* Workaround for glibc, that returns EAI_SERVICE (-8) if
86 * SOCK_RAW and IPPROTO_GRE is used.
87 */
88 hints.ai_socktype = SOCK_DGRAM;
89 hints.ai_protocol = IPPROTO_UDP;
90 } else {
91 hints.ai_socktype = type;
92 hints.ai_protocol = proto;
93 }
Harald Welte33cb71a2011-05-21 18:54:32 +020094
Pablo Neira Ayuso0849c9a2011-06-09 15:04:30 +020095 if (flags & OSMO_SOCK_F_BIND)
Harald Weltef9e07462011-05-31 17:47:54 +020096 hints.ai_flags |= AI_PASSIVE;
97
Harald Welte33cb71a2011-05-21 18:54:32 +020098 rc = getaddrinfo(host, portbuf, &hints, &result);
99 if (rc != 0) {
Neels Hofmeyrf0f07d92016-08-22 13:34:23 +0200100 fprintf(stderr, "getaddrinfo returned NULL: %s:%u: %s\n",
101 host, port, strerror(errno));
Harald Welte33cb71a2011-05-21 18:54:32 +0200102 return -EINVAL;
103 }
104
105 for (rp = result; rp != NULL; rp = rp->ai_next) {
Pablo Neira Ayusoe04a14d2013-01-14 00:12:28 +0100106 /* Workaround for glibc again */
107 if (type == SOCK_RAW) {
108 rp->ai_socktype = SOCK_RAW;
109 rp->ai_protocol = proto;
110 }
111
Harald Welte33cb71a2011-05-21 18:54:32 +0200112 sfd = socket(rp->ai_family, rp->ai_socktype, rp->ai_protocol);
113 if (sfd == -1)
114 continue;
Pablo Neira Ayuso0849c9a2011-06-09 15:04:30 +0200115 if (flags & OSMO_SOCK_F_NONBLOCK) {
116 if (ioctl(sfd, FIONBIO, (unsigned char *)&on) < 0) {
Neels Hofmeyrf0f07d92016-08-22 13:34:23 +0200117 fprintf(stderr,
118 "cannot set this socket unblocking:"
119 " %s:%u: %s\n",
120 host, port, strerror(errno));
Pablo Neira Ayuso0849c9a2011-06-09 15:04:30 +0200121 close(sfd);
Harald Welte0fc315d2016-11-26 10:18:24 +0100122 freeaddrinfo(result);
Pablo Neira Ayuso0849c9a2011-06-09 15:04:30 +0200123 return -EINVAL;
124 }
125 }
126 if (flags & OSMO_SOCK_F_CONNECT) {
127 rc = connect(sfd, rp->ai_addr, rp->ai_addrlen);
128 if (rc != -1 || (rc == -1 && errno == EINPROGRESS))
Harald Welte33cb71a2011-05-21 18:54:32 +0200129 break;
130 } else {
Pablo Neira Ayuso0849c9a2011-06-09 15:04:30 +0200131 rc = setsockopt(sfd, SOL_SOCKET, SO_REUSEADDR,
132 &on, sizeof(on));
133 if (rc < 0) {
Neels Hofmeyrf0f07d92016-08-22 13:34:23 +0200134 fprintf(stderr,
135 "cannot setsockopt socket:"
136 " %s:%u: %s\n",
137 host, port, strerror(errno));
Pablo Neira Ayuso0849c9a2011-06-09 15:04:30 +0200138 break;
139 }
Harald Welte33cb71a2011-05-21 18:54:32 +0200140 if (bind(sfd, rp->ai_addr, rp->ai_addrlen) != -1)
141 break;
142 }
143 close(sfd);
144 }
145 freeaddrinfo(result);
146
147 if (rp == NULL) {
Neels Hofmeyrf0f07d92016-08-22 13:34:23 +0200148 fprintf(stderr, "unable to connect/bind socket: %s:%u: %s\n",
149 host, port, strerror(errno));
Harald Welte33cb71a2011-05-21 18:54:32 +0200150 return -ENODEV;
151 }
Harald Welte68b15742011-05-22 21:47:29 +0200152
153 setsockopt(sfd, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on));
154
155 /* Make sure to call 'listen' on a bound, connection-oriented sock */
Pablo Neira Ayuso0849c9a2011-06-09 15:04:30 +0200156 if (flags & OSMO_SOCK_F_BIND) {
Harald Welte68b15742011-05-22 21:47:29 +0200157 switch (type) {
158 case SOCK_STREAM:
159 case SOCK_SEQPACKET:
160 listen(sfd, 10);
161 break;
162 }
163 }
164 return sfd;
165}
166
Max862ba652014-10-13 14:54:25 +0200167/*! \brief fill \ref osmo_fd for a give sfd
168 * \param[out] ofd file descriptor (will be filled in)
169 * \param[in] sfd socket file descriptor
Harald Welte2d2e2cc2016-04-25 12:11:20 +0200170 * \returns socket fd on success; negative on error
Max862ba652014-10-13 14:54:25 +0200171 *
172 * This function fills the \a ofd structure.
173 */
174static inline int osmo_fd_init_ofd(struct osmo_fd *ofd, int sfd)
175{
176 int rc;
177
178 if (sfd < 0)
179 return sfd;
180
181 ofd->fd = sfd;
182 ofd->when = BSC_FD_READ;
183
184 rc = osmo_fd_register(ofd);
185 if (rc < 0) {
186 close(sfd);
187 return rc;
188 }
189
190 return sfd;
191}
192
Harald Welteba6988b2011-08-17 12:46:48 +0200193/*! \brief Initialize a socket and fill \ref osmo_fd
Katerina Barone-Adesic28c6a02013-02-15 13:27:59 +0100194 * \param[out] ofd file descriptor (will be filled in)
Harald Welteba6988b2011-08-17 12:46:48 +0200195 * \param[in] family Address Family like AF_INET, AF_INET6, AF_UNSPEC
196 * \param[in] type Socket type like SOCK_DGRAM, SOCK_STREAM
197 * \param[in] proto Protocol like IPPROTO_TCP, IPPROTO_UDP
198 * \param[in] host remote host name or IP address in string form
199 * \param[in] port remote port number in host byte order
200 * \param[in] flags flags like \ref OSMO_SOCK_F_CONNECT
Harald Welte2d2e2cc2016-04-25 12:11:20 +0200201 * \returns socket fd on success; negative on error
Harald Welteba6988b2011-08-17 12:46:48 +0200202 *
203 * This function creates (and optionall binds/connects) a socket using
204 * \ref osmo_sock_init, but also fills the \a ofd structure.
205 */
Harald Welte68b15742011-05-22 21:47:29 +0200206int osmo_sock_init_ofd(struct osmo_fd *ofd, int family, int type, int proto,
Pablo Neira Ayuso0849c9a2011-06-09 15:04:30 +0200207 const char *host, uint16_t port, unsigned int flags)
Harald Welte68b15742011-05-22 21:47:29 +0200208{
Max862ba652014-10-13 14:54:25 +0200209 return osmo_fd_init_ofd(ofd, osmo_sock_init(family, type, proto, host, port, flags));
Harald Welte33cb71a2011-05-21 18:54:32 +0200210}
211
Harald Welteba6988b2011-08-17 12:46:48 +0200212/*! \brief Initialize a socket and fill \ref sockaddr
213 * \param[out] ss socket address (will be filled in)
214 * \param[in] type Socket type like SOCK_DGRAM, SOCK_STREAM
215 * \param[in] proto Protocol like IPPROTO_TCP, IPPROTO_UDP
216 * \param[in] flags flags like \ref OSMO_SOCK_F_CONNECT
Harald Welte2d2e2cc2016-04-25 12:11:20 +0200217 * \returns socket fd on success; negative on error
Harald Welteba6988b2011-08-17 12:46:48 +0200218 *
219 * This function creates (and optionall binds/connects) a socket using
220 * \ref osmo_sock_init, but also fills the \a ss structure.
221 */
Harald Welte33cb71a2011-05-21 18:54:32 +0200222int osmo_sock_init_sa(struct sockaddr *ss, uint16_t type,
Pablo Neira Ayuso0849c9a2011-06-09 15:04:30 +0200223 uint8_t proto, unsigned int flags)
Harald Welte33cb71a2011-05-21 18:54:32 +0200224{
225 char host[NI_MAXHOST];
226 uint16_t port;
227 struct sockaddr_in *sin;
228 struct sockaddr_in6 *sin6;
229 int s, sa_len;
230
231 /* determine port and host from ss */
232 switch (ss->sa_family) {
233 case AF_INET:
234 sin = (struct sockaddr_in *) ss;
235 sa_len = sizeof(struct sockaddr_in);
236 port = ntohs(sin->sin_port);
237 break;
238 case AF_INET6:
239 sin6 = (struct sockaddr_in6 *) ss;
240 sa_len = sizeof(struct sockaddr_in6);
241 port = ntohs(sin6->sin6_port);
242 break;
243 default:
244 return -EINVAL;
245 }
Harald Welte33cb71a2011-05-21 18:54:32 +0200246
247 s = getnameinfo(ss, sa_len, host, NI_MAXHOST,
248 NULL, 0, NI_NUMERICHOST);
249 if (s != 0) {
250 perror("getnameinfo failed");
251 return s;
252 }
253
Pablo Neira Ayuso0849c9a2011-06-09 15:04:30 +0200254 return osmo_sock_init(ss->sa_family, type, proto, host, port, flags);
Harald Welte33cb71a2011-05-21 18:54:32 +0200255}
256
257static int sockaddr_equal(const struct sockaddr *a,
Harald Weltee4764422011-05-22 12:25:57 +0200258 const struct sockaddr *b, unsigned int len)
Harald Welte33cb71a2011-05-21 18:54:32 +0200259{
260 struct sockaddr_in *sin_a, *sin_b;
261 struct sockaddr_in6 *sin6_a, *sin6_b;
262
263 if (a->sa_family != b->sa_family)
264 return 0;
265
266 switch (a->sa_family) {
267 case AF_INET:
268 sin_a = (struct sockaddr_in *)a;
269 sin_b = (struct sockaddr_in *)b;
270 if (!memcmp(&sin_a->sin_addr, &sin_b->sin_addr,
271 sizeof(struct in_addr)))
272 return 1;
273 break;
274 case AF_INET6:
275 sin6_a = (struct sockaddr_in6 *)a;
276 sin6_b = (struct sockaddr_in6 *)b;
277 if (!memcmp(&sin6_a->sin6_addr, &sin6_b->sin6_addr,
278 sizeof(struct in6_addr)))
279 return 1;
280 break;
281 }
282 return 0;
283}
284
Harald Welteba6988b2011-08-17 12:46:48 +0200285/*! \brief Determine if the given address is a local address
286 * \param[in] addr Socket Address
287 * \param[in] addrlen Length of socket address in bytes
288 * \returns 1 if address is local, 0 otherwise.
289 */
Harald Weltebc32d052012-04-08 11:31:32 +0200290int osmo_sockaddr_is_local(struct sockaddr *addr, unsigned int addrlen)
Harald Welte33cb71a2011-05-21 18:54:32 +0200291{
292 struct ifaddrs *ifaddr, *ifa;
293
294 if (getifaddrs(&ifaddr) == -1) {
295 perror("getifaddrs");
296 return -EIO;
297 }
298
299 for (ifa = ifaddr; ifa != NULL; ifa = ifa->ifa_next) {
Harald Welte4d3a7b12011-05-24 21:31:53 +0200300 if (!ifa->ifa_addr)
301 continue;
Harald Welte33cb71a2011-05-21 18:54:32 +0200302 if (sockaddr_equal(ifa->ifa_addr, addr, addrlen))
303 return 1;
304 }
305
306 return 0;
307}
Harald Weltee4764422011-05-22 12:25:57 +0200308
Álvaro Neira Ayuso5ade61a2014-03-24 13:02:00 +0100309/*! \brief Initialize a unix domain socket (including bind/connect)
310 * \param[in] type Socket type like SOCK_DGRAM, SOCK_STREAM
311 * \param[in] proto Protocol like IPPROTO_TCP, IPPROTO_UDP
312 * \param[in] socket_path path to identify the socket
313 * \param[in] flags flags like \ref OSMO_SOCK_F_CONNECT
Harald Welte2d2e2cc2016-04-25 12:11:20 +0200314 * \returns socket fd on success; negative on error
Álvaro Neira Ayuso5ade61a2014-03-24 13:02:00 +0100315 *
316 * This function creates a new unix domain socket, \a
317 * type and \a proto and optionally binds or connects it, depending on
318 * the value of \a flags parameter.
319 */
320int osmo_sock_unix_init(uint16_t type, uint8_t proto,
321 const char *socket_path, unsigned int flags)
322{
323 struct sockaddr_un local;
324 int sfd, rc, on = 1;
325 unsigned int namelen;
326
327 if ((flags & (OSMO_SOCK_F_BIND | OSMO_SOCK_F_CONNECT)) ==
328 (OSMO_SOCK_F_BIND | OSMO_SOCK_F_CONNECT))
329 return -EINVAL;
330
331 local.sun_family = AF_UNIX;
332 strncpy(local.sun_path, socket_path, sizeof(local.sun_path));
333 local.sun_path[sizeof(local.sun_path) - 1] = '\0';
334
335#if defined(BSD44SOCKETS) || defined(__UNIXWARE__)
336 local.sun_len = strlen(local.sun_path);
337#endif
338#if defined(BSD44SOCKETS) || defined(SUN_LEN)
339 namelen = SUN_LEN(&local);
340#else
341 namelen = strlen(local.sun_path) +
342 offsetof(struct sockaddr_un, sun_path);
343#endif
344
345 sfd = socket(AF_UNIX, type, proto);
346 if (sfd < 0)
347 return -1;
348
349 if (flags & OSMO_SOCK_F_CONNECT) {
350 rc = connect(sfd, (struct sockaddr *)&local, namelen);
351 if (rc < 0)
352 goto err;
353 } else {
354 unlink(local.sun_path);
355 rc = bind(sfd, (struct sockaddr *)&local, namelen);
356 if (rc < 0)
357 goto err;
358 }
359
360 if (flags & OSMO_SOCK_F_NONBLOCK) {
361 if (ioctl(sfd, FIONBIO, (unsigned char *)&on) < 0) {
362 perror("cannot set this socket unblocking");
363 close(sfd);
364 return -EINVAL;
365 }
366 }
367
368 if (flags & OSMO_SOCK_F_BIND) {
369 rc = listen(sfd, 10);
370 if (rc < 0)
371 goto err;
372 }
373
374 return sfd;
375err:
376 close(sfd);
377 return -1;
378}
379
380/*! \brief Initialize a unix domain socket and fill \ref osmo_fd
381 * \param[out] ofd file descriptor (will be filled in)
382 * \param[in] type Socket type like SOCK_DGRAM, SOCK_STREAM
383 * \param[in] proto Protocol like IPPROTO_TCP, IPPROTO_UDP
384 * \param[in] socket_path path to identify the socket
385 * \param[in] flags flags like \ref OSMO_SOCK_F_CONNECT
Harald Welte2d2e2cc2016-04-25 12:11:20 +0200386 * \returns socket fd on success; negative on error
Álvaro Neira Ayuso5ade61a2014-03-24 13:02:00 +0100387 *
388 * This function creates (and optionall binds/connects) a socket using
389 * \ref osmo_sock_unix_init, but also fills the \a ofd structure.
390 */
391int osmo_sock_unix_init_ofd(struct osmo_fd *ofd, uint16_t type, uint8_t proto,
392 const char *socket_path, unsigned int flags)
393{
Max862ba652014-10-13 14:54:25 +0200394 return osmo_fd_init_ofd(ofd, osmo_sock_unix_init(type, proto, socket_path, flags));
Álvaro Neira Ayuso5ade61a2014-03-24 13:02:00 +0100395}
396
Harald Welte48f55832017-01-26 00:03:10 +0100397/*! \brief Get address/port information on soocket in dyn-alloc string
398 * \param[in] ctx talloc context from which to allocate string buffer
399 * \param[in] fd file descriptor of socket
400 * \returns string identifying the connection of this socket
401 */
402char *osmo_sock_get_name(void *ctx, int fd)
403{
404 struct sockaddr sa_l, sa_r;
405 socklen_t sa_len_l = sizeof(sa_l);
406 socklen_t sa_len_r = sizeof(sa_r);
407 char hostbuf_l[64], hostbuf_r[64];
408 char portbuf_l[16], portbuf_r[16];
409 int rc;
410
411 rc = getsockname(fd, &sa_l, &sa_len_l);
412 if (rc < 0)
413 return NULL;
414
415 rc = getnameinfo(&sa_l, sa_len_l, hostbuf_l, sizeof(hostbuf_l),
416 portbuf_l, sizeof(portbuf_l),
417 NI_NUMERICHOST | NI_NUMERICSERV);
418 if (rc < 0)
419 return NULL;
420
421 rc = getpeername(fd, &sa_r, &sa_len_r);
422 if (rc < 0)
423 goto local_only;
424
425 rc = getnameinfo(&sa_r, sa_len_r, hostbuf_r, sizeof(hostbuf_r),
426 portbuf_r, sizeof(portbuf_r),
427 NI_NUMERICHOST | NI_NUMERICSERV);
428 if (rc < 0)
429 goto local_only;
430
431 return talloc_asprintf(ctx, "(%s:%s<->%s:%s)", hostbuf_r, portbuf_r,
432 hostbuf_l, portbuf_l);
433
434local_only:
435 return talloc_asprintf(ctx, "(NULL<->%s:%s)", hostbuf_l, portbuf_l);
436}
437
Harald Weltee4764422011-05-22 12:25:57 +0200438#endif /* HAVE_SYS_SOCKET_H */
Harald Welteba6988b2011-08-17 12:46:48 +0200439
Sylvain Munautdca7d2c2012-04-18 21:53:23 +0200440/*! @} */