blob: 28d9b1c4f6bed121e754aabc05d3fd4424da3bd2 [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 * @{
Neels Hofmeyr17518fe2017-06-20 04:35:06 +020026 * Osmocom socket convenience functions.
27 *
28 * \file socket.c */
Harald Welte96e2a002017-06-12 21:44:18 +020029
Harald Weltee4764422011-05-22 12:25:57 +020030#ifdef HAVE_SYS_SOCKET_H
31
Harald Welte33cb71a2011-05-21 18:54:32 +020032#include <osmocom/core/logging.h>
33#include <osmocom/core/select.h>
34#include <osmocom/core/socket.h>
Harald Welte48f55832017-01-26 00:03:10 +010035#include <osmocom/core/talloc.h>
Harald Welte33cb71a2011-05-21 18:54:32 +020036
Pablo Neira Ayuso0849c9a2011-06-09 15:04:30 +020037#include <sys/ioctl.h>
Harald Welte33cb71a2011-05-21 18:54:32 +020038#include <sys/socket.h>
39#include <sys/types.h>
Álvaro Neira Ayuso5ade61a2014-03-24 13:02:00 +010040#include <sys/un.h>
Harald Welte33cb71a2011-05-21 18:54:32 +020041
Holger Hans Peter Freyther47723482011-11-09 11:26:15 +010042#include <netinet/in.h>
43
Harald Welte33cb71a2011-05-21 18:54:32 +020044#include <stdio.h>
45#include <unistd.h>
46#include <stdint.h>
47#include <string.h>
48#include <errno.h>
49#include <netdb.h>
50#include <ifaddrs.h>
51
Harald Weltedda70fc2017-04-08 20:52:33 +020052static struct addrinfo *addrinfo_helper(uint16_t family, uint16_t type, uint8_t proto,
53 const char *host, uint16_t port, bool passive)
54{
55 struct addrinfo hints, *result;
56 char portbuf[16];
57 int rc;
58
59 snprintf(portbuf, sizeof(portbuf), "%u", port);
60 memset(&hints, 0, sizeof(struct addrinfo));
61 hints.ai_family = family;
62 if (type == SOCK_RAW) {
63 /* Workaround for glibc, that returns EAI_SERVICE (-8) if
64 * SOCK_RAW and IPPROTO_GRE is used.
65 */
66 hints.ai_socktype = SOCK_DGRAM;
67 hints.ai_protocol = IPPROTO_UDP;
68 } else {
69 hints.ai_socktype = type;
70 hints.ai_protocol = proto;
71 }
72
73 if (passive)
74 hints.ai_flags |= AI_PASSIVE;
75
76 rc = getaddrinfo(host, portbuf, &hints, &result);
77 if (rc != 0) {
78 LOGP(DLGLOBAL, LOGL_ERROR, "getaddrinfo returned NULL: %s:%u: %s\n",
79 host, port, strerror(errno));
80 return NULL;
81 }
82
83 return result;
84}
85
86static int socket_helper(const struct addrinfo *rp, unsigned int flags)
87{
88 int sfd, on = 1;
89
90 sfd = socket(rp->ai_family, rp->ai_socktype, rp->ai_protocol);
91 if (sfd == -1)
92 return sfd;
93 if (flags & OSMO_SOCK_F_NONBLOCK) {
94 if (ioctl(sfd, FIONBIO, (unsigned char *)&on) < 0) {
95 LOGP(DLGLOBAL, LOGL_ERROR,
96 "cannot set this socket unblocking: %s\n",
97 strerror(errno));
98 close(sfd);
99 sfd = -EINVAL;
100 }
101 }
102 return sfd;
103}
104
105
Neels Hofmeyr87e45502017-06-20 00:17:59 +0200106/*! Initialize a socket (including bind and/or connect)
Harald Weltedda70fc2017-04-08 20:52:33 +0200107 * \param[in] family Address Family like AF_INET, AF_INET6, AF_UNSPEC
108 * \param[in] type Socket type like SOCK_DGRAM, SOCK_STREAM
109 * \param[in] proto Protocol like IPPROTO_TCP, IPPROTO_UDP
110 * \param[in] local_host local host name or IP address in string form
111 * \param[in] local_port local port number in host byte order
112 * \param[in] remote_host remote host name or IP address in string form
113 * \param[in] remote_port remote port number in host byte order
114 * \param[in] flags flags like \ref OSMO_SOCK_F_CONNECT
115 * \returns socket file descriptor on success; negative on error
116 *
117 * This function creates a new socket of the designated \a family, \a
118 * type and \a proto and optionally binds it to the \a local_host and \a
119 * local_port as well as optionally connects it to the \a remote_host
120 * and \q remote_port, depending on the value * of \a flags parameter.
121 *
122 * As opposed to \ref osmo_sock_init(), this function allows to combine
123 * the \ref OSMO_SOCK_F_BIND and \ref OSMO_SOCK_F_CONNECT flags. This
124 * is useful if you want to connect to a remote host/port, but still
125 * want to bind that socket to either a specific local alias IP and/or a
126 * specific local source port.
127 *
128 * You must specify either \ref OSMO_SOCK_F_BIND, or \ref
129 * OSMO_SOCK_F_CONNECT, or both.
130 *
131 * If \ref OSMO_SOCK_F_NONBLOCK is specified, the socket will be set to
132 * non-blocking mode.
133 */
134int osmo_sock_init2(uint16_t family, uint16_t type, uint8_t proto,
135 const char *local_host, uint16_t local_port,
136 const char *remote_host, uint16_t remote_port, unsigned int flags)
137{
138 struct addrinfo *result, *rp;
139 int sfd = -1, rc, on = 1;
140
141 if ((flags & (OSMO_SOCK_F_BIND | OSMO_SOCK_F_CONNECT)) == 0) {
142 LOGP(DLGLOBAL, LOGL_ERROR, "invalid: you have to specify either "
143 "BIND or CONNECT flags\n");
144 return -EINVAL;
145 }
146
147 /* figure out local side of socket */
148 if (flags & OSMO_SOCK_F_BIND) {
149 result = addrinfo_helper(family, type, proto, local_host, local_port, true);
150 if (!result)
151 return -EINVAL;
152
153 for (rp = result; rp != NULL; rp = rp->ai_next) {
154 /* Workaround for glibc again */
155 if (type == SOCK_RAW) {
156 rp->ai_socktype = SOCK_RAW;
157 rp->ai_protocol = proto;
158 }
159
160 sfd = socket_helper(rp, flags);
161 if (sfd < 0)
162 continue;
163
164 rc = setsockopt(sfd, SOL_SOCKET, SO_REUSEADDR,
165 &on, sizeof(on));
166 if (rc < 0) {
167 LOGP(DLGLOBAL, LOGL_ERROR,
168 "cannot setsockopt socket:"
169 " %s:%u: %s\n",
170 local_host, local_port, strerror(errno));
171 break;
172 }
173 if (bind(sfd, rp->ai_addr, rp->ai_addrlen) != -1)
174 break;
175 close(sfd);
176 }
177 freeaddrinfo(result);
178 if (rp == NULL) {
179 LOGP(DLGLOBAL, LOGL_ERROR, "unable to bind socket: %s:%u: %s\n",
180 local_host, local_port, strerror(errno));
181 return -ENODEV;
182 }
183 }
184
185 /* figure out remote side of socket */
186 if (flags & OSMO_SOCK_F_CONNECT) {
187 result = addrinfo_helper(family, type, proto, remote_host, remote_port, false);
188 if (!result) {
189 close(sfd);
190 return -EINVAL;
191 }
192
193 for (rp = result; rp != NULL; rp = rp->ai_next) {
194 /* Workaround for glibc again */
195 if (type == SOCK_RAW) {
196 rp->ai_socktype = SOCK_RAW;
197 rp->ai_protocol = proto;
198 }
199
200 if (!sfd) {
201 sfd = socket_helper(rp, flags);
202 if (sfd < 0)
203 continue;
204 }
205
206 rc = connect(sfd, rp->ai_addr, rp->ai_addrlen);
207 if (rc != -1 || (rc == -1 && errno == EINPROGRESS))
208 break;
209
210 close(sfd);
211 sfd = -1;
212 }
213 freeaddrinfo(result);
214 if (rp == NULL) {
215 LOGP(DLGLOBAL, LOGL_ERROR, "unable to connect socket: %s:%u: %s\n",
216 remote_host, remote_port, strerror(errno));
217 return -ENODEV;
218 }
219 }
220
221 /* Make sure to call 'listen' on a bound, connection-oriented sock */
222 if ((flags & (OSMO_SOCK_F_BIND|OSMO_SOCK_F_CONNECT)) == OSMO_SOCK_F_BIND) {
223 switch (type) {
224 case SOCK_STREAM:
225 case SOCK_SEQPACKET:
226 listen(sfd, 10);
227 break;
228 }
229 }
230 return sfd;
231}
232
233
Neels Hofmeyr87e45502017-06-20 00:17:59 +0200234/*! Initialize a socket (including bind/connect)
Harald Welteba6988b2011-08-17 12:46:48 +0200235 * \param[in] family Address Family like AF_INET, AF_INET6, AF_UNSPEC
236 * \param[in] type Socket type like SOCK_DGRAM, SOCK_STREAM
237 * \param[in] proto Protocol like IPPROTO_TCP, IPPROTO_UDP
238 * \param[in] host remote host name or IP address in string form
239 * \param[in] port remote port number in host byte order
240 * \param[in] flags flags like \ref OSMO_SOCK_F_CONNECT
Harald Welte2d2e2cc2016-04-25 12:11:20 +0200241 * \returns socket file descriptor on success; negative on error
Harald Welteba6988b2011-08-17 12:46:48 +0200242 *
243 * This function creates a new socket of the designated \a family, \a
244 * type and \a proto and optionally binds or connects it, depending on
245 * the value of \a flags parameter.
246 */
Harald Welte33cb71a2011-05-21 18:54:32 +0200247int osmo_sock_init(uint16_t family, uint16_t type, uint8_t proto,
Pablo Neira Ayuso0849c9a2011-06-09 15:04:30 +0200248 const char *host, uint16_t port, unsigned int flags)
Harald Welte33cb71a2011-05-21 18:54:32 +0200249{
Harald Weltedda70fc2017-04-08 20:52:33 +0200250 struct addrinfo *result, *rp;
Harald Welte68b15742011-05-22 21:47:29 +0200251 int sfd, rc, on = 1;
Harald Welte33cb71a2011-05-21 18:54:32 +0200252
Pablo Neira Ayuso0849c9a2011-06-09 15:04:30 +0200253 if ((flags & (OSMO_SOCK_F_BIND | OSMO_SOCK_F_CONNECT)) ==
Neels Hofmeyrf0f07d92016-08-22 13:34:23 +0200254 (OSMO_SOCK_F_BIND | OSMO_SOCK_F_CONNECT)) {
Philipp Maier6f0f5602017-02-09 14:09:06 +0100255 LOGP(DLGLOBAL, LOGL_ERROR, "invalid: both bind and connect flags set:"
Neels Hofmeyrb7f191f2016-08-29 11:22:03 +0200256 " %s:%u\n", host, port);
Pablo Neira Ayuso0849c9a2011-06-09 15:04:30 +0200257 return -EINVAL;
Neels Hofmeyrf0f07d92016-08-22 13:34:23 +0200258 }
Pablo Neira Ayuso0849c9a2011-06-09 15:04:30 +0200259
Harald Weltedda70fc2017-04-08 20:52:33 +0200260 result = addrinfo_helper(family, type, proto, host, port, flags & OSMO_SOCK_F_BIND);
261 if (!result) {
Philipp Maier6f0f5602017-02-09 14:09:06 +0100262 LOGP(DLGLOBAL, LOGL_ERROR, "getaddrinfo returned NULL: %s:%u: %s\n",
Neels Hofmeyrf0f07d92016-08-22 13:34:23 +0200263 host, port, strerror(errno));
Harald Welte33cb71a2011-05-21 18:54:32 +0200264 return -EINVAL;
265 }
266
267 for (rp = result; rp != NULL; rp = rp->ai_next) {
Pablo Neira Ayusoe04a14d2013-01-14 00:12:28 +0100268 /* Workaround for glibc again */
269 if (type == SOCK_RAW) {
270 rp->ai_socktype = SOCK_RAW;
271 rp->ai_protocol = proto;
272 }
273
Harald Weltedda70fc2017-04-08 20:52:33 +0200274 sfd = socket_helper(rp, flags);
Harald Welte33cb71a2011-05-21 18:54:32 +0200275 if (sfd == -1)
276 continue;
Harald Weltedda70fc2017-04-08 20:52:33 +0200277
Pablo Neira Ayuso0849c9a2011-06-09 15:04:30 +0200278 if (flags & OSMO_SOCK_F_CONNECT) {
279 rc = connect(sfd, rp->ai_addr, rp->ai_addrlen);
280 if (rc != -1 || (rc == -1 && errno == EINPROGRESS))
Harald Welte33cb71a2011-05-21 18:54:32 +0200281 break;
282 } else {
Pablo Neira Ayuso0849c9a2011-06-09 15:04:30 +0200283 rc = setsockopt(sfd, SOL_SOCKET, SO_REUSEADDR,
284 &on, sizeof(on));
285 if (rc < 0) {
Philipp Maier6f0f5602017-02-09 14:09:06 +0100286 LOGP(DLGLOBAL, LOGL_ERROR,
Neels Hofmeyrf0f07d92016-08-22 13:34:23 +0200287 "cannot setsockopt socket:"
288 " %s:%u: %s\n",
289 host, port, strerror(errno));
Pablo Neira Ayuso0849c9a2011-06-09 15:04:30 +0200290 break;
291 }
Harald Welte33cb71a2011-05-21 18:54:32 +0200292 if (bind(sfd, rp->ai_addr, rp->ai_addrlen) != -1)
293 break;
294 }
295 close(sfd);
296 }
297 freeaddrinfo(result);
298
299 if (rp == NULL) {
Philipp Maier6f0f5602017-02-09 14:09:06 +0100300 LOGP(DLGLOBAL, LOGL_ERROR, "unable to connect/bind socket: %s:%u: %s\n",
Neels Hofmeyrf0f07d92016-08-22 13:34:23 +0200301 host, port, strerror(errno));
Harald Welte33cb71a2011-05-21 18:54:32 +0200302 return -ENODEV;
303 }
Harald Welte68b15742011-05-22 21:47:29 +0200304
305 setsockopt(sfd, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on));
306
307 /* Make sure to call 'listen' on a bound, connection-oriented sock */
Pablo Neira Ayuso0849c9a2011-06-09 15:04:30 +0200308 if (flags & OSMO_SOCK_F_BIND) {
Harald Welte68b15742011-05-22 21:47:29 +0200309 switch (type) {
310 case SOCK_STREAM:
311 case SOCK_SEQPACKET:
312 listen(sfd, 10);
313 break;
314 }
315 }
316 return sfd;
317}
318
Neels Hofmeyr87e45502017-06-20 00:17:59 +0200319/*! fill \ref osmo_fd for a give sfd
Max862ba652014-10-13 14:54:25 +0200320 * \param[out] ofd file descriptor (will be filled in)
321 * \param[in] sfd socket file descriptor
Harald Welte2d2e2cc2016-04-25 12:11:20 +0200322 * \returns socket fd on success; negative on error
Max862ba652014-10-13 14:54:25 +0200323 *
324 * This function fills the \a ofd structure.
325 */
326static inline int osmo_fd_init_ofd(struct osmo_fd *ofd, int sfd)
327{
328 int rc;
329
330 if (sfd < 0)
331 return sfd;
332
333 ofd->fd = sfd;
334 ofd->when = BSC_FD_READ;
335
336 rc = osmo_fd_register(ofd);
337 if (rc < 0) {
338 close(sfd);
339 return rc;
340 }
341
342 return sfd;
343}
344
Neels Hofmeyr87e45502017-06-20 00:17:59 +0200345/*! Initialize a socket and fill \ref osmo_fd
Katerina Barone-Adesic28c6a02013-02-15 13:27:59 +0100346 * \param[out] ofd file descriptor (will be filled in)
Harald Welteba6988b2011-08-17 12:46:48 +0200347 * \param[in] family Address Family like AF_INET, AF_INET6, AF_UNSPEC
348 * \param[in] type Socket type like SOCK_DGRAM, SOCK_STREAM
349 * \param[in] proto Protocol like IPPROTO_TCP, IPPROTO_UDP
350 * \param[in] host remote host name or IP address in string form
351 * \param[in] port remote port number in host byte order
352 * \param[in] flags flags like \ref OSMO_SOCK_F_CONNECT
Harald Welte2d2e2cc2016-04-25 12:11:20 +0200353 * \returns socket fd on success; negative on error
Harald Welteba6988b2011-08-17 12:46:48 +0200354 *
355 * This function creates (and optionall binds/connects) a socket using
356 * \ref osmo_sock_init, but also fills the \a ofd structure.
357 */
Harald Welte68b15742011-05-22 21:47:29 +0200358int osmo_sock_init_ofd(struct osmo_fd *ofd, int family, int type, int proto,
Pablo Neira Ayuso0849c9a2011-06-09 15:04:30 +0200359 const char *host, uint16_t port, unsigned int flags)
Harald Welte68b15742011-05-22 21:47:29 +0200360{
Max862ba652014-10-13 14:54:25 +0200361 return osmo_fd_init_ofd(ofd, osmo_sock_init(family, type, proto, host, port, flags));
Harald Welte33cb71a2011-05-21 18:54:32 +0200362}
363
Neels Hofmeyr87e45502017-06-20 00:17:59 +0200364/*! Initialize a socket and fill \ref osmo_fd
Pau Espin Pedrol75989e62017-05-26 12:39:53 +0200365 * \param[out] ofd file descriptor (will be filled in)
366 * \param[in] family Address Family like AF_INET, AF_INET6, AF_UNSPEC
367 * \param[in] type Socket type like SOCK_DGRAM, SOCK_STREAM
368 * \param[in] proto Protocol like IPPROTO_TCP, IPPROTO_UDP
369 * \param[in] local_host local host name or IP address in string form
370 * \param[in] local_port local port number in host byte order
371 * \param[in] remote_host remote host name or IP address in string form
372 * \param[in] remote_port remote port number in host byte order
373 * \param[in] flags flags like \ref OSMO_SOCK_F_CONNECT
374 * \returns socket fd on success; negative on error
375 *
376 * This function creates (and optionall binds/connects) a socket using
377 * \ref osmo_sock_init2, but also fills the \a ofd structure.
378 */
379int osmo_sock_init2_ofd(struct osmo_fd *ofd, int family, int type, int proto,
380 const char *local_host, uint16_t local_port,
381 const char *remote_host, uint16_t remote_port, unsigned int flags)
382{
383 return osmo_fd_init_ofd(ofd, osmo_sock_init2(family, type, proto, local_host,
384 local_port, remote_host, remote_port, flags));
385}
386
Neels Hofmeyr87e45502017-06-20 00:17:59 +0200387/*! Initialize a socket and fill \ref sockaddr
Harald Welteba6988b2011-08-17 12:46:48 +0200388 * \param[out] ss socket address (will be filled in)
389 * \param[in] type Socket type like SOCK_DGRAM, SOCK_STREAM
390 * \param[in] proto Protocol like IPPROTO_TCP, IPPROTO_UDP
391 * \param[in] flags flags like \ref OSMO_SOCK_F_CONNECT
Harald Welte2d2e2cc2016-04-25 12:11:20 +0200392 * \returns socket fd on success; negative on error
Harald Welteba6988b2011-08-17 12:46:48 +0200393 *
394 * This function creates (and optionall binds/connects) a socket using
395 * \ref osmo_sock_init, but also fills the \a ss structure.
396 */
Harald Welte33cb71a2011-05-21 18:54:32 +0200397int osmo_sock_init_sa(struct sockaddr *ss, uint16_t type,
Pablo Neira Ayuso0849c9a2011-06-09 15:04:30 +0200398 uint8_t proto, unsigned int flags)
Harald Welte33cb71a2011-05-21 18:54:32 +0200399{
400 char host[NI_MAXHOST];
401 uint16_t port;
402 struct sockaddr_in *sin;
403 struct sockaddr_in6 *sin6;
404 int s, sa_len;
405
406 /* determine port and host from ss */
407 switch (ss->sa_family) {
408 case AF_INET:
409 sin = (struct sockaddr_in *) ss;
410 sa_len = sizeof(struct sockaddr_in);
411 port = ntohs(sin->sin_port);
412 break;
413 case AF_INET6:
414 sin6 = (struct sockaddr_in6 *) ss;
415 sa_len = sizeof(struct sockaddr_in6);
416 port = ntohs(sin6->sin6_port);
417 break;
418 default:
419 return -EINVAL;
420 }
Harald Welte33cb71a2011-05-21 18:54:32 +0200421
422 s = getnameinfo(ss, sa_len, host, NI_MAXHOST,
423 NULL, 0, NI_NUMERICHOST);
424 if (s != 0) {
Philipp Maier6f0f5602017-02-09 14:09:06 +0100425 LOGP(DLGLOBAL, LOGL_ERROR, "getnameinfo failed:"
426 " %s\n", strerror(errno));
Harald Welte33cb71a2011-05-21 18:54:32 +0200427 return s;
428 }
429
Pablo Neira Ayuso0849c9a2011-06-09 15:04:30 +0200430 return osmo_sock_init(ss->sa_family, type, proto, host, port, flags);
Harald Welte33cb71a2011-05-21 18:54:32 +0200431}
432
433static int sockaddr_equal(const struct sockaddr *a,
Harald Weltee4764422011-05-22 12:25:57 +0200434 const struct sockaddr *b, unsigned int len)
Harald Welte33cb71a2011-05-21 18:54:32 +0200435{
436 struct sockaddr_in *sin_a, *sin_b;
437 struct sockaddr_in6 *sin6_a, *sin6_b;
438
439 if (a->sa_family != b->sa_family)
440 return 0;
441
442 switch (a->sa_family) {
443 case AF_INET:
444 sin_a = (struct sockaddr_in *)a;
445 sin_b = (struct sockaddr_in *)b;
446 if (!memcmp(&sin_a->sin_addr, &sin_b->sin_addr,
447 sizeof(struct in_addr)))
448 return 1;
449 break;
450 case AF_INET6:
451 sin6_a = (struct sockaddr_in6 *)a;
452 sin6_b = (struct sockaddr_in6 *)b;
453 if (!memcmp(&sin6_a->sin6_addr, &sin6_b->sin6_addr,
454 sizeof(struct in6_addr)))
455 return 1;
456 break;
457 }
458 return 0;
459}
460
Neels Hofmeyr87e45502017-06-20 00:17:59 +0200461/*! Determine if the given address is a local address
Harald Welteba6988b2011-08-17 12:46:48 +0200462 * \param[in] addr Socket Address
463 * \param[in] addrlen Length of socket address in bytes
464 * \returns 1 if address is local, 0 otherwise.
465 */
Harald Weltebc32d052012-04-08 11:31:32 +0200466int osmo_sockaddr_is_local(struct sockaddr *addr, unsigned int addrlen)
Harald Welte33cb71a2011-05-21 18:54:32 +0200467{
468 struct ifaddrs *ifaddr, *ifa;
469
470 if (getifaddrs(&ifaddr) == -1) {
Philipp Maier6f0f5602017-02-09 14:09:06 +0100471 LOGP(DLGLOBAL, LOGL_ERROR, "getifaddrs:"
472 " %s\n", strerror(errno));
Harald Welte33cb71a2011-05-21 18:54:32 +0200473 return -EIO;
474 }
475
476 for (ifa = ifaddr; ifa != NULL; ifa = ifa->ifa_next) {
Harald Welte4d3a7b12011-05-24 21:31:53 +0200477 if (!ifa->ifa_addr)
478 continue;
Harald Welte33cb71a2011-05-21 18:54:32 +0200479 if (sockaddr_equal(ifa->ifa_addr, addr, addrlen))
480 return 1;
481 }
482
483 return 0;
484}
Harald Weltee4764422011-05-22 12:25:57 +0200485
Neels Hofmeyr87e45502017-06-20 00:17:59 +0200486/*! Initialize a unix domain socket (including bind/connect)
Álvaro Neira Ayuso5ade61a2014-03-24 13:02:00 +0100487 * \param[in] type Socket type like SOCK_DGRAM, SOCK_STREAM
488 * \param[in] proto Protocol like IPPROTO_TCP, IPPROTO_UDP
489 * \param[in] socket_path path to identify the socket
490 * \param[in] flags flags like \ref OSMO_SOCK_F_CONNECT
Harald Welte2d2e2cc2016-04-25 12:11:20 +0200491 * \returns socket fd on success; negative on error
Álvaro Neira Ayuso5ade61a2014-03-24 13:02:00 +0100492 *
493 * This function creates a new unix domain socket, \a
494 * type and \a proto and optionally binds or connects it, depending on
495 * the value of \a flags parameter.
496 */
497int osmo_sock_unix_init(uint16_t type, uint8_t proto,
498 const char *socket_path, unsigned int flags)
499{
500 struct sockaddr_un local;
501 int sfd, rc, on = 1;
502 unsigned int namelen;
503
504 if ((flags & (OSMO_SOCK_F_BIND | OSMO_SOCK_F_CONNECT)) ==
505 (OSMO_SOCK_F_BIND | OSMO_SOCK_F_CONNECT))
506 return -EINVAL;
507
508 local.sun_family = AF_UNIX;
509 strncpy(local.sun_path, socket_path, sizeof(local.sun_path));
510 local.sun_path[sizeof(local.sun_path) - 1] = '\0';
511
512#if defined(BSD44SOCKETS) || defined(__UNIXWARE__)
513 local.sun_len = strlen(local.sun_path);
514#endif
515#if defined(BSD44SOCKETS) || defined(SUN_LEN)
516 namelen = SUN_LEN(&local);
517#else
518 namelen = strlen(local.sun_path) +
519 offsetof(struct sockaddr_un, sun_path);
520#endif
521
522 sfd = socket(AF_UNIX, type, proto);
523 if (sfd < 0)
524 return -1;
525
526 if (flags & OSMO_SOCK_F_CONNECT) {
527 rc = connect(sfd, (struct sockaddr *)&local, namelen);
528 if (rc < 0)
529 goto err;
530 } else {
531 unlink(local.sun_path);
532 rc = bind(sfd, (struct sockaddr *)&local, namelen);
533 if (rc < 0)
534 goto err;
535 }
536
537 if (flags & OSMO_SOCK_F_NONBLOCK) {
538 if (ioctl(sfd, FIONBIO, (unsigned char *)&on) < 0) {
Philipp Maier6f0f5602017-02-09 14:09:06 +0100539 LOGP(DLGLOBAL, LOGL_ERROR,
540 "cannot set this socket unblocking: %s\n",
541 strerror(errno));
Álvaro Neira Ayuso5ade61a2014-03-24 13:02:00 +0100542 close(sfd);
543 return -EINVAL;
544 }
545 }
546
547 if (flags & OSMO_SOCK_F_BIND) {
548 rc = listen(sfd, 10);
549 if (rc < 0)
550 goto err;
551 }
552
553 return sfd;
554err:
555 close(sfd);
556 return -1;
557}
558
Neels Hofmeyr87e45502017-06-20 00:17:59 +0200559/*! Initialize a unix domain socket and fill \ref osmo_fd
Álvaro Neira Ayuso5ade61a2014-03-24 13:02:00 +0100560 * \param[out] ofd file descriptor (will be filled in)
561 * \param[in] type Socket type like SOCK_DGRAM, SOCK_STREAM
562 * \param[in] proto Protocol like IPPROTO_TCP, IPPROTO_UDP
563 * \param[in] socket_path path to identify the socket
564 * \param[in] flags flags like \ref OSMO_SOCK_F_CONNECT
Harald Welte2d2e2cc2016-04-25 12:11:20 +0200565 * \returns socket fd on success; negative on error
Álvaro Neira Ayuso5ade61a2014-03-24 13:02:00 +0100566 *
567 * This function creates (and optionall binds/connects) a socket using
568 * \ref osmo_sock_unix_init, but also fills the \a ofd structure.
569 */
570int osmo_sock_unix_init_ofd(struct osmo_fd *ofd, uint16_t type, uint8_t proto,
571 const char *socket_path, unsigned int flags)
572{
Max862ba652014-10-13 14:54:25 +0200573 return osmo_fd_init_ofd(ofd, osmo_sock_unix_init(type, proto, socket_path, flags));
Álvaro Neira Ayuso5ade61a2014-03-24 13:02:00 +0100574}
575
Neels Hofmeyr87e45502017-06-20 00:17:59 +0200576/*! Get address/port information on soocket in dyn-alloc string
Harald Welte48f55832017-01-26 00:03:10 +0100577 * \param[in] ctx talloc context from which to allocate string buffer
578 * \param[in] fd file descriptor of socket
579 * \returns string identifying the connection of this socket
580 */
581char *osmo_sock_get_name(void *ctx, int fd)
582{
583 struct sockaddr sa_l, sa_r;
584 socklen_t sa_len_l = sizeof(sa_l);
585 socklen_t sa_len_r = sizeof(sa_r);
586 char hostbuf_l[64], hostbuf_r[64];
587 char portbuf_l[16], portbuf_r[16];
588 int rc;
589
590 rc = getsockname(fd, &sa_l, &sa_len_l);
591 if (rc < 0)
592 return NULL;
593
594 rc = getnameinfo(&sa_l, sa_len_l, hostbuf_l, sizeof(hostbuf_l),
595 portbuf_l, sizeof(portbuf_l),
596 NI_NUMERICHOST | NI_NUMERICSERV);
597 if (rc < 0)
598 return NULL;
599
600 rc = getpeername(fd, &sa_r, &sa_len_r);
601 if (rc < 0)
602 goto local_only;
603
604 rc = getnameinfo(&sa_r, sa_len_r, hostbuf_r, sizeof(hostbuf_r),
605 portbuf_r, sizeof(portbuf_r),
606 NI_NUMERICHOST | NI_NUMERICSERV);
607 if (rc < 0)
608 goto local_only;
609
Neels Hofmeyr1f82d0a2017-06-21 22:54:46 +0200610 return talloc_asprintf(ctx, "(r=%s:%s<->l=%s:%s)", hostbuf_r, portbuf_r,
Harald Welte48f55832017-01-26 00:03:10 +0100611 hostbuf_l, portbuf_l);
612
613local_only:
Neels Hofmeyr1f82d0a2017-06-21 22:54:46 +0200614 return talloc_asprintf(ctx, "(r=NULL<->l=%s:%s)", hostbuf_l, portbuf_l);
Harald Welte48f55832017-01-26 00:03:10 +0100615}
616
Harald Weltee4764422011-05-22 12:25:57 +0200617#endif /* HAVE_SYS_SOCKET_H */
Harald Welteba6988b2011-08-17 12:46:48 +0200618
Sylvain Munautdca7d2c2012-04-18 21:53:23 +0200619/*! @} */