blob: 9d22d61dcba180999868a4774abfe7e2bc97ee1d [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 Weltedda70fc2017-04-08 20:52:33 +020054static struct addrinfo *addrinfo_helper(uint16_t family, uint16_t type, uint8_t proto,
55 const char *host, uint16_t port, bool passive)
56{
57 struct addrinfo hints, *result;
58 char portbuf[16];
59 int rc;
60
61 snprintf(portbuf, sizeof(portbuf), "%u", port);
62 memset(&hints, 0, sizeof(struct addrinfo));
63 hints.ai_family = family;
64 if (type == SOCK_RAW) {
65 /* Workaround for glibc, that returns EAI_SERVICE (-8) if
66 * SOCK_RAW and IPPROTO_GRE is used.
67 */
68 hints.ai_socktype = SOCK_DGRAM;
69 hints.ai_protocol = IPPROTO_UDP;
70 } else {
71 hints.ai_socktype = type;
72 hints.ai_protocol = proto;
73 }
74
75 if (passive)
76 hints.ai_flags |= AI_PASSIVE;
77
78 rc = getaddrinfo(host, portbuf, &hints, &result);
79 if (rc != 0) {
80 LOGP(DLGLOBAL, LOGL_ERROR, "getaddrinfo returned NULL: %s:%u: %s\n",
81 host, port, strerror(errno));
82 return NULL;
83 }
84
85 return result;
86}
87
88static int socket_helper(const struct addrinfo *rp, unsigned int flags)
89{
90 int sfd, on = 1;
91
92 sfd = socket(rp->ai_family, rp->ai_socktype, rp->ai_protocol);
93 if (sfd == -1)
94 return sfd;
95 if (flags & OSMO_SOCK_F_NONBLOCK) {
96 if (ioctl(sfd, FIONBIO, (unsigned char *)&on) < 0) {
97 LOGP(DLGLOBAL, LOGL_ERROR,
98 "cannot set this socket unblocking: %s\n",
99 strerror(errno));
100 close(sfd);
101 sfd = -EINVAL;
102 }
103 }
104 return sfd;
105}
106
107
108/*! \brief Initialize a socket (including bind and/or connect)
109 * \param[in] family Address Family like AF_INET, AF_INET6, AF_UNSPEC
110 * \param[in] type Socket type like SOCK_DGRAM, SOCK_STREAM
111 * \param[in] proto Protocol like IPPROTO_TCP, IPPROTO_UDP
112 * \param[in] local_host local host name or IP address in string form
113 * \param[in] local_port local port number in host byte order
114 * \param[in] remote_host remote host name or IP address in string form
115 * \param[in] remote_port remote port number in host byte order
116 * \param[in] flags flags like \ref OSMO_SOCK_F_CONNECT
117 * \returns socket file descriptor on success; negative on error
118 *
119 * This function creates a new socket of the designated \a family, \a
120 * type and \a proto and optionally binds it to the \a local_host and \a
121 * local_port as well as optionally connects it to the \a remote_host
122 * and \q remote_port, depending on the value * of \a flags parameter.
123 *
124 * As opposed to \ref osmo_sock_init(), this function allows to combine
125 * the \ref OSMO_SOCK_F_BIND and \ref OSMO_SOCK_F_CONNECT flags. This
126 * is useful if you want to connect to a remote host/port, but still
127 * want to bind that socket to either a specific local alias IP and/or a
128 * specific local source port.
129 *
130 * You must specify either \ref OSMO_SOCK_F_BIND, or \ref
131 * OSMO_SOCK_F_CONNECT, or both.
132 *
133 * If \ref OSMO_SOCK_F_NONBLOCK is specified, the socket will be set to
134 * non-blocking mode.
135 */
136int osmo_sock_init2(uint16_t family, uint16_t type, uint8_t proto,
137 const char *local_host, uint16_t local_port,
138 const char *remote_host, uint16_t remote_port, unsigned int flags)
139{
140 struct addrinfo *result, *rp;
141 int sfd = -1, rc, on = 1;
142
143 if ((flags & (OSMO_SOCK_F_BIND | OSMO_SOCK_F_CONNECT)) == 0) {
144 LOGP(DLGLOBAL, LOGL_ERROR, "invalid: you have to specify either "
145 "BIND or CONNECT flags\n");
146 return -EINVAL;
147 }
148
149 /* figure out local side of socket */
150 if (flags & OSMO_SOCK_F_BIND) {
151 result = addrinfo_helper(family, type, proto, local_host, local_port, true);
152 if (!result)
153 return -EINVAL;
154
155 for (rp = result; rp != NULL; rp = rp->ai_next) {
156 /* Workaround for glibc again */
157 if (type == SOCK_RAW) {
158 rp->ai_socktype = SOCK_RAW;
159 rp->ai_protocol = proto;
160 }
161
162 sfd = socket_helper(rp, flags);
163 if (sfd < 0)
164 continue;
165
166 rc = setsockopt(sfd, SOL_SOCKET, SO_REUSEADDR,
167 &on, sizeof(on));
168 if (rc < 0) {
169 LOGP(DLGLOBAL, LOGL_ERROR,
170 "cannot setsockopt socket:"
171 " %s:%u: %s\n",
172 local_host, local_port, strerror(errno));
173 break;
174 }
175 if (bind(sfd, rp->ai_addr, rp->ai_addrlen) != -1)
176 break;
177 close(sfd);
178 }
179 freeaddrinfo(result);
180 if (rp == NULL) {
181 LOGP(DLGLOBAL, LOGL_ERROR, "unable to bind socket: %s:%u: %s\n",
182 local_host, local_port, strerror(errno));
183 return -ENODEV;
184 }
185 }
186
187 /* figure out remote side of socket */
188 if (flags & OSMO_SOCK_F_CONNECT) {
189 result = addrinfo_helper(family, type, proto, remote_host, remote_port, false);
190 if (!result) {
191 close(sfd);
192 return -EINVAL;
193 }
194
195 for (rp = result; rp != NULL; rp = rp->ai_next) {
196 /* Workaround for glibc again */
197 if (type == SOCK_RAW) {
198 rp->ai_socktype = SOCK_RAW;
199 rp->ai_protocol = proto;
200 }
201
202 if (!sfd) {
203 sfd = socket_helper(rp, flags);
204 if (sfd < 0)
205 continue;
206 }
207
208 rc = connect(sfd, rp->ai_addr, rp->ai_addrlen);
209 if (rc != -1 || (rc == -1 && errno == EINPROGRESS))
210 break;
211
212 close(sfd);
213 sfd = -1;
214 }
215 freeaddrinfo(result);
216 if (rp == NULL) {
217 LOGP(DLGLOBAL, LOGL_ERROR, "unable to connect socket: %s:%u: %s\n",
218 remote_host, remote_port, strerror(errno));
219 return -ENODEV;
220 }
221 }
222
223 /* Make sure to call 'listen' on a bound, connection-oriented sock */
224 if ((flags & (OSMO_SOCK_F_BIND|OSMO_SOCK_F_CONNECT)) == OSMO_SOCK_F_BIND) {
225 switch (type) {
226 case SOCK_STREAM:
227 case SOCK_SEQPACKET:
228 listen(sfd, 10);
229 break;
230 }
231 }
232 return sfd;
233}
234
235
Harald Welteba6988b2011-08-17 12:46:48 +0200236/*! \brief Initialize a socket (including bind/connect)
237 * \param[in] family Address Family like AF_INET, AF_INET6, AF_UNSPEC
238 * \param[in] type Socket type like SOCK_DGRAM, SOCK_STREAM
239 * \param[in] proto Protocol like IPPROTO_TCP, IPPROTO_UDP
240 * \param[in] host remote host name or IP address in string form
241 * \param[in] port remote port number in host byte order
242 * \param[in] flags flags like \ref OSMO_SOCK_F_CONNECT
Harald Welte2d2e2cc2016-04-25 12:11:20 +0200243 * \returns socket file descriptor on success; negative on error
Harald Welteba6988b2011-08-17 12:46:48 +0200244 *
245 * This function creates a new socket of the designated \a family, \a
246 * type and \a proto and optionally binds or connects it, depending on
247 * the value of \a flags parameter.
248 */
Harald Welte33cb71a2011-05-21 18:54:32 +0200249int osmo_sock_init(uint16_t family, uint16_t type, uint8_t proto,
Pablo Neira Ayuso0849c9a2011-06-09 15:04:30 +0200250 const char *host, uint16_t port, unsigned int flags)
Harald Welte33cb71a2011-05-21 18:54:32 +0200251{
Harald Weltedda70fc2017-04-08 20:52:33 +0200252 struct addrinfo *result, *rp;
Harald Welte68b15742011-05-22 21:47:29 +0200253 int sfd, rc, on = 1;
Harald Welte33cb71a2011-05-21 18:54:32 +0200254
Pablo Neira Ayuso0849c9a2011-06-09 15:04:30 +0200255 if ((flags & (OSMO_SOCK_F_BIND | OSMO_SOCK_F_CONNECT)) ==
Neels Hofmeyrf0f07d92016-08-22 13:34:23 +0200256 (OSMO_SOCK_F_BIND | OSMO_SOCK_F_CONNECT)) {
Philipp Maier6f0f5602017-02-09 14:09:06 +0100257 LOGP(DLGLOBAL, LOGL_ERROR, "invalid: both bind and connect flags set:"
Neels Hofmeyrb7f191f2016-08-29 11:22:03 +0200258 " %s:%u\n", host, port);
Pablo Neira Ayuso0849c9a2011-06-09 15:04:30 +0200259 return -EINVAL;
Neels Hofmeyrf0f07d92016-08-22 13:34:23 +0200260 }
Pablo Neira Ayuso0849c9a2011-06-09 15:04:30 +0200261
Harald Weltedda70fc2017-04-08 20:52:33 +0200262 result = addrinfo_helper(family, type, proto, host, port, flags & OSMO_SOCK_F_BIND);
263 if (!result) {
Philipp Maier6f0f5602017-02-09 14:09:06 +0100264 LOGP(DLGLOBAL, LOGL_ERROR, "getaddrinfo returned NULL: %s:%u: %s\n",
Neels Hofmeyrf0f07d92016-08-22 13:34:23 +0200265 host, port, strerror(errno));
Harald Welte33cb71a2011-05-21 18:54:32 +0200266 return -EINVAL;
267 }
268
269 for (rp = result; rp != NULL; rp = rp->ai_next) {
Pablo Neira Ayusoe04a14d2013-01-14 00:12:28 +0100270 /* Workaround for glibc again */
271 if (type == SOCK_RAW) {
272 rp->ai_socktype = SOCK_RAW;
273 rp->ai_protocol = proto;
274 }
275
Harald Weltedda70fc2017-04-08 20:52:33 +0200276 sfd = socket_helper(rp, flags);
Harald Welte33cb71a2011-05-21 18:54:32 +0200277 if (sfd == -1)
278 continue;
Harald Weltedda70fc2017-04-08 20:52:33 +0200279
Pablo Neira Ayuso0849c9a2011-06-09 15:04:30 +0200280 if (flags & OSMO_SOCK_F_CONNECT) {
281 rc = connect(sfd, rp->ai_addr, rp->ai_addrlen);
282 if (rc != -1 || (rc == -1 && errno == EINPROGRESS))
Harald Welte33cb71a2011-05-21 18:54:32 +0200283 break;
284 } else {
Pablo Neira Ayuso0849c9a2011-06-09 15:04:30 +0200285 rc = setsockopt(sfd, SOL_SOCKET, SO_REUSEADDR,
286 &on, sizeof(on));
287 if (rc < 0) {
Philipp Maier6f0f5602017-02-09 14:09:06 +0100288 LOGP(DLGLOBAL, LOGL_ERROR,
Neels Hofmeyrf0f07d92016-08-22 13:34:23 +0200289 "cannot setsockopt socket:"
290 " %s:%u: %s\n",
291 host, port, strerror(errno));
Pablo Neira Ayuso0849c9a2011-06-09 15:04:30 +0200292 break;
293 }
Harald Welte33cb71a2011-05-21 18:54:32 +0200294 if (bind(sfd, rp->ai_addr, rp->ai_addrlen) != -1)
295 break;
296 }
297 close(sfd);
298 }
299 freeaddrinfo(result);
300
301 if (rp == NULL) {
Philipp Maier6f0f5602017-02-09 14:09:06 +0100302 LOGP(DLGLOBAL, LOGL_ERROR, "unable to connect/bind socket: %s:%u: %s\n",
Neels Hofmeyrf0f07d92016-08-22 13:34:23 +0200303 host, port, strerror(errno));
Harald Welte33cb71a2011-05-21 18:54:32 +0200304 return -ENODEV;
305 }
Harald Welte68b15742011-05-22 21:47:29 +0200306
307 setsockopt(sfd, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on));
308
309 /* Make sure to call 'listen' on a bound, connection-oriented sock */
Pablo Neira Ayuso0849c9a2011-06-09 15:04:30 +0200310 if (flags & OSMO_SOCK_F_BIND) {
Harald Welte68b15742011-05-22 21:47:29 +0200311 switch (type) {
312 case SOCK_STREAM:
313 case SOCK_SEQPACKET:
314 listen(sfd, 10);
315 break;
316 }
317 }
318 return sfd;
319}
320
Max862ba652014-10-13 14:54:25 +0200321/*! \brief fill \ref osmo_fd for a give sfd
322 * \param[out] ofd file descriptor (will be filled in)
323 * \param[in] sfd socket file descriptor
Harald Welte2d2e2cc2016-04-25 12:11:20 +0200324 * \returns socket fd on success; negative on error
Max862ba652014-10-13 14:54:25 +0200325 *
326 * This function fills the \a ofd structure.
327 */
328static inline int osmo_fd_init_ofd(struct osmo_fd *ofd, int sfd)
329{
330 int rc;
331
332 if (sfd < 0)
333 return sfd;
334
335 ofd->fd = sfd;
336 ofd->when = BSC_FD_READ;
337
338 rc = osmo_fd_register(ofd);
339 if (rc < 0) {
340 close(sfd);
341 return rc;
342 }
343
344 return sfd;
345}
346
Harald Welteba6988b2011-08-17 12:46:48 +0200347/*! \brief Initialize a socket and fill \ref osmo_fd
Katerina Barone-Adesic28c6a02013-02-15 13:27:59 +0100348 * \param[out] ofd file descriptor (will be filled in)
Harald Welteba6988b2011-08-17 12:46:48 +0200349 * \param[in] family Address Family like AF_INET, AF_INET6, AF_UNSPEC
350 * \param[in] type Socket type like SOCK_DGRAM, SOCK_STREAM
351 * \param[in] proto Protocol like IPPROTO_TCP, IPPROTO_UDP
352 * \param[in] host remote host name or IP address in string form
353 * \param[in] port remote port number in host byte order
354 * \param[in] flags flags like \ref OSMO_SOCK_F_CONNECT
Harald Welte2d2e2cc2016-04-25 12:11:20 +0200355 * \returns socket fd on success; negative on error
Harald Welteba6988b2011-08-17 12:46:48 +0200356 *
357 * This function creates (and optionall binds/connects) a socket using
358 * \ref osmo_sock_init, but also fills the \a ofd structure.
359 */
Harald Welte68b15742011-05-22 21:47:29 +0200360int osmo_sock_init_ofd(struct osmo_fd *ofd, int family, int type, int proto,
Pablo Neira Ayuso0849c9a2011-06-09 15:04:30 +0200361 const char *host, uint16_t port, unsigned int flags)
Harald Welte68b15742011-05-22 21:47:29 +0200362{
Max862ba652014-10-13 14:54:25 +0200363 return osmo_fd_init_ofd(ofd, osmo_sock_init(family, type, proto, host, port, flags));
Harald Welte33cb71a2011-05-21 18:54:32 +0200364}
365
Pau Espin Pedrol75989e62017-05-26 12:39:53 +0200366/*! \brief Initialize a socket and fill \ref osmo_fd
367 * \param[out] ofd file descriptor (will be filled in)
368 * \param[in] family Address Family like AF_INET, AF_INET6, AF_UNSPEC
369 * \param[in] type Socket type like SOCK_DGRAM, SOCK_STREAM
370 * \param[in] proto Protocol like IPPROTO_TCP, IPPROTO_UDP
371 * \param[in] local_host local host name or IP address in string form
372 * \param[in] local_port local port number in host byte order
373 * \param[in] remote_host remote host name or IP address in string form
374 * \param[in] remote_port remote port number in host byte order
375 * \param[in] flags flags like \ref OSMO_SOCK_F_CONNECT
376 * \returns socket fd on success; negative on error
377 *
378 * This function creates (and optionall binds/connects) a socket using
379 * \ref osmo_sock_init2, but also fills the \a ofd structure.
380 */
381int osmo_sock_init2_ofd(struct osmo_fd *ofd, int family, int type, int proto,
382 const char *local_host, uint16_t local_port,
383 const char *remote_host, uint16_t remote_port, unsigned int flags)
384{
385 return osmo_fd_init_ofd(ofd, osmo_sock_init2(family, type, proto, local_host,
386 local_port, remote_host, remote_port, flags));
387}
388
Harald Welteba6988b2011-08-17 12:46:48 +0200389/*! \brief Initialize a socket and fill \ref sockaddr
390 * \param[out] ss socket address (will be filled in)
391 * \param[in] type Socket type like SOCK_DGRAM, SOCK_STREAM
392 * \param[in] proto Protocol like IPPROTO_TCP, IPPROTO_UDP
393 * \param[in] flags flags like \ref OSMO_SOCK_F_CONNECT
Harald Welte2d2e2cc2016-04-25 12:11:20 +0200394 * \returns socket fd on success; negative on error
Harald Welteba6988b2011-08-17 12:46:48 +0200395 *
396 * This function creates (and optionall binds/connects) a socket using
397 * \ref osmo_sock_init, but also fills the \a ss structure.
398 */
Harald Welte33cb71a2011-05-21 18:54:32 +0200399int osmo_sock_init_sa(struct sockaddr *ss, uint16_t type,
Pablo Neira Ayuso0849c9a2011-06-09 15:04:30 +0200400 uint8_t proto, unsigned int flags)
Harald Welte33cb71a2011-05-21 18:54:32 +0200401{
402 char host[NI_MAXHOST];
403 uint16_t port;
404 struct sockaddr_in *sin;
405 struct sockaddr_in6 *sin6;
406 int s, sa_len;
407
408 /* determine port and host from ss */
409 switch (ss->sa_family) {
410 case AF_INET:
411 sin = (struct sockaddr_in *) ss;
412 sa_len = sizeof(struct sockaddr_in);
413 port = ntohs(sin->sin_port);
414 break;
415 case AF_INET6:
416 sin6 = (struct sockaddr_in6 *) ss;
417 sa_len = sizeof(struct sockaddr_in6);
418 port = ntohs(sin6->sin6_port);
419 break;
420 default:
421 return -EINVAL;
422 }
Harald Welte33cb71a2011-05-21 18:54:32 +0200423
424 s = getnameinfo(ss, sa_len, host, NI_MAXHOST,
425 NULL, 0, NI_NUMERICHOST);
426 if (s != 0) {
Philipp Maier6f0f5602017-02-09 14:09:06 +0100427 LOGP(DLGLOBAL, LOGL_ERROR, "getnameinfo failed:"
428 " %s\n", strerror(errno));
Harald Welte33cb71a2011-05-21 18:54:32 +0200429 return s;
430 }
431
Pablo Neira Ayuso0849c9a2011-06-09 15:04:30 +0200432 return osmo_sock_init(ss->sa_family, type, proto, host, port, flags);
Harald Welte33cb71a2011-05-21 18:54:32 +0200433}
434
435static int sockaddr_equal(const struct sockaddr *a,
Harald Weltee4764422011-05-22 12:25:57 +0200436 const struct sockaddr *b, unsigned int len)
Harald Welte33cb71a2011-05-21 18:54:32 +0200437{
438 struct sockaddr_in *sin_a, *sin_b;
439 struct sockaddr_in6 *sin6_a, *sin6_b;
440
441 if (a->sa_family != b->sa_family)
442 return 0;
443
444 switch (a->sa_family) {
445 case AF_INET:
446 sin_a = (struct sockaddr_in *)a;
447 sin_b = (struct sockaddr_in *)b;
448 if (!memcmp(&sin_a->sin_addr, &sin_b->sin_addr,
449 sizeof(struct in_addr)))
450 return 1;
451 break;
452 case AF_INET6:
453 sin6_a = (struct sockaddr_in6 *)a;
454 sin6_b = (struct sockaddr_in6 *)b;
455 if (!memcmp(&sin6_a->sin6_addr, &sin6_b->sin6_addr,
456 sizeof(struct in6_addr)))
457 return 1;
458 break;
459 }
460 return 0;
461}
462
Harald Welteba6988b2011-08-17 12:46:48 +0200463/*! \brief Determine if the given address is a local address
464 * \param[in] addr Socket Address
465 * \param[in] addrlen Length of socket address in bytes
466 * \returns 1 if address is local, 0 otherwise.
467 */
Harald Weltebc32d052012-04-08 11:31:32 +0200468int osmo_sockaddr_is_local(struct sockaddr *addr, unsigned int addrlen)
Harald Welte33cb71a2011-05-21 18:54:32 +0200469{
470 struct ifaddrs *ifaddr, *ifa;
471
472 if (getifaddrs(&ifaddr) == -1) {
Philipp Maier6f0f5602017-02-09 14:09:06 +0100473 LOGP(DLGLOBAL, LOGL_ERROR, "getifaddrs:"
474 " %s\n", strerror(errno));
Harald Welte33cb71a2011-05-21 18:54:32 +0200475 return -EIO;
476 }
477
478 for (ifa = ifaddr; ifa != NULL; ifa = ifa->ifa_next) {
Harald Welte4d3a7b12011-05-24 21:31:53 +0200479 if (!ifa->ifa_addr)
480 continue;
Harald Welte33cb71a2011-05-21 18:54:32 +0200481 if (sockaddr_equal(ifa->ifa_addr, addr, addrlen))
482 return 1;
483 }
484
485 return 0;
486}
Harald Weltee4764422011-05-22 12:25:57 +0200487
Álvaro Neira Ayuso5ade61a2014-03-24 13:02:00 +0100488/*! \brief Initialize a unix domain socket (including bind/connect)
489 * \param[in] type Socket type like SOCK_DGRAM, SOCK_STREAM
490 * \param[in] proto Protocol like IPPROTO_TCP, IPPROTO_UDP
491 * \param[in] socket_path path to identify the socket
492 * \param[in] flags flags like \ref OSMO_SOCK_F_CONNECT
Harald Welte2d2e2cc2016-04-25 12:11:20 +0200493 * \returns socket fd on success; negative on error
Álvaro Neira Ayuso5ade61a2014-03-24 13:02:00 +0100494 *
495 * This function creates a new unix domain socket, \a
496 * type and \a proto and optionally binds or connects it, depending on
497 * the value of \a flags parameter.
498 */
499int osmo_sock_unix_init(uint16_t type, uint8_t proto,
500 const char *socket_path, unsigned int flags)
501{
502 struct sockaddr_un local;
503 int sfd, rc, on = 1;
504 unsigned int namelen;
505
506 if ((flags & (OSMO_SOCK_F_BIND | OSMO_SOCK_F_CONNECT)) ==
507 (OSMO_SOCK_F_BIND | OSMO_SOCK_F_CONNECT))
508 return -EINVAL;
509
510 local.sun_family = AF_UNIX;
511 strncpy(local.sun_path, socket_path, sizeof(local.sun_path));
512 local.sun_path[sizeof(local.sun_path) - 1] = '\0';
513
514#if defined(BSD44SOCKETS) || defined(__UNIXWARE__)
515 local.sun_len = strlen(local.sun_path);
516#endif
517#if defined(BSD44SOCKETS) || defined(SUN_LEN)
518 namelen = SUN_LEN(&local);
519#else
520 namelen = strlen(local.sun_path) +
521 offsetof(struct sockaddr_un, sun_path);
522#endif
523
524 sfd = socket(AF_UNIX, type, proto);
525 if (sfd < 0)
526 return -1;
527
528 if (flags & OSMO_SOCK_F_CONNECT) {
529 rc = connect(sfd, (struct sockaddr *)&local, namelen);
530 if (rc < 0)
531 goto err;
532 } else {
533 unlink(local.sun_path);
534 rc = bind(sfd, (struct sockaddr *)&local, namelen);
535 if (rc < 0)
536 goto err;
537 }
538
539 if (flags & OSMO_SOCK_F_NONBLOCK) {
540 if (ioctl(sfd, FIONBIO, (unsigned char *)&on) < 0) {
Philipp Maier6f0f5602017-02-09 14:09:06 +0100541 LOGP(DLGLOBAL, LOGL_ERROR,
542 "cannot set this socket unblocking: %s\n",
543 strerror(errno));
Álvaro Neira Ayuso5ade61a2014-03-24 13:02:00 +0100544 close(sfd);
545 return -EINVAL;
546 }
547 }
548
549 if (flags & OSMO_SOCK_F_BIND) {
550 rc = listen(sfd, 10);
551 if (rc < 0)
552 goto err;
553 }
554
555 return sfd;
556err:
557 close(sfd);
558 return -1;
559}
560
561/*! \brief Initialize a unix domain socket and fill \ref osmo_fd
562 * \param[out] ofd file descriptor (will be filled in)
563 * \param[in] type Socket type like SOCK_DGRAM, SOCK_STREAM
564 * \param[in] proto Protocol like IPPROTO_TCP, IPPROTO_UDP
565 * \param[in] socket_path path to identify the socket
566 * \param[in] flags flags like \ref OSMO_SOCK_F_CONNECT
Harald Welte2d2e2cc2016-04-25 12:11:20 +0200567 * \returns socket fd on success; negative on error
Álvaro Neira Ayuso5ade61a2014-03-24 13:02:00 +0100568 *
569 * This function creates (and optionall binds/connects) a socket using
570 * \ref osmo_sock_unix_init, but also fills the \a ofd structure.
571 */
572int osmo_sock_unix_init_ofd(struct osmo_fd *ofd, uint16_t type, uint8_t proto,
573 const char *socket_path, unsigned int flags)
574{
Max862ba652014-10-13 14:54:25 +0200575 return osmo_fd_init_ofd(ofd, osmo_sock_unix_init(type, proto, socket_path, flags));
Álvaro Neira Ayuso5ade61a2014-03-24 13:02:00 +0100576}
577
Harald Welte48f55832017-01-26 00:03:10 +0100578/*! \brief Get address/port information on soocket in dyn-alloc string
579 * \param[in] ctx talloc context from which to allocate string buffer
580 * \param[in] fd file descriptor of socket
581 * \returns string identifying the connection of this socket
582 */
583char *osmo_sock_get_name(void *ctx, int fd)
584{
585 struct sockaddr sa_l, sa_r;
586 socklen_t sa_len_l = sizeof(sa_l);
587 socklen_t sa_len_r = sizeof(sa_r);
588 char hostbuf_l[64], hostbuf_r[64];
589 char portbuf_l[16], portbuf_r[16];
590 int rc;
591
592 rc = getsockname(fd, &sa_l, &sa_len_l);
593 if (rc < 0)
594 return NULL;
595
596 rc = getnameinfo(&sa_l, sa_len_l, hostbuf_l, sizeof(hostbuf_l),
597 portbuf_l, sizeof(portbuf_l),
598 NI_NUMERICHOST | NI_NUMERICSERV);
599 if (rc < 0)
600 return NULL;
601
602 rc = getpeername(fd, &sa_r, &sa_len_r);
603 if (rc < 0)
604 goto local_only;
605
606 rc = getnameinfo(&sa_r, sa_len_r, hostbuf_r, sizeof(hostbuf_r),
607 portbuf_r, sizeof(portbuf_r),
608 NI_NUMERICHOST | NI_NUMERICSERV);
609 if (rc < 0)
610 goto local_only;
611
612 return talloc_asprintf(ctx, "(%s:%s<->%s:%s)", hostbuf_r, portbuf_r,
613 hostbuf_l, portbuf_l);
614
615local_only:
616 return talloc_asprintf(ctx, "(NULL<->%s:%s)", hostbuf_l, portbuf_l);
617}
618
Harald Weltee4764422011-05-22 12:25:57 +0200619#endif /* HAVE_SYS_SOCKET_H */
Harald Welteba6988b2011-08-17 12:46:48 +0200620
Sylvain Munautdca7d2c2012-04-18 21:53:23 +0200621/*! @} */