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