blob: cdafadd01b20d999ec489f497d51fb7d17cebad3 [file] [log] [blame]
Harald Welte468b6432014-09-11 13:05:51 +08001/*
2 * (C) 2011 by Harald Welte <laforge@gnumonks.org>
3 *
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>
37
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 Welteba6988b2011-08-17 12:46:48 +020053/*! \brief Initialize a socket (including bind/connect)
54 * \param[in] family Address Family like AF_INET, AF_INET6, AF_UNSPEC
55 * \param[in] type Socket type like SOCK_DGRAM, SOCK_STREAM
56 * \param[in] proto Protocol like IPPROTO_TCP, IPPROTO_UDP
57 * \param[in] host remote host name or IP address in string form
58 * \param[in] port remote port number in host byte order
59 * \param[in] flags flags like \ref OSMO_SOCK_F_CONNECT
Harald Welte2d2e2cc2016-04-25 12:11:20 +020060 * \returns socket file descriptor on success; negative on error
Harald Welteba6988b2011-08-17 12:46:48 +020061 *
62 * This function creates a new socket of the designated \a family, \a
63 * type and \a proto and optionally binds or connects it, depending on
64 * the value of \a flags parameter.
65 */
Harald Welte33cb71a2011-05-21 18:54:32 +020066int osmo_sock_init(uint16_t family, uint16_t type, uint8_t proto,
Pablo Neira Ayuso0849c9a2011-06-09 15:04:30 +020067 const char *host, uint16_t port, unsigned int flags)
Harald Welte33cb71a2011-05-21 18:54:32 +020068{
69 struct addrinfo hints, *result, *rp;
Harald Welte68b15742011-05-22 21:47:29 +020070 int sfd, rc, on = 1;
Harald Welte33cb71a2011-05-21 18:54:32 +020071 char portbuf[16];
72
Pablo Neira Ayuso0849c9a2011-06-09 15:04:30 +020073 if ((flags & (OSMO_SOCK_F_BIND | OSMO_SOCK_F_CONNECT)) ==
Neels Hofmeyrf0f07d92016-08-22 13:34:23 +020074 (OSMO_SOCK_F_BIND | OSMO_SOCK_F_CONNECT)) {
75 fprintf(stderr, "invalid: both bind and connect flags set:"
Neels Hofmeyrb7f191f2016-08-29 11:22:03 +020076 " %s:%u\n", host, port);
Pablo Neira Ayuso0849c9a2011-06-09 15:04:30 +020077 return -EINVAL;
Neels Hofmeyrf0f07d92016-08-22 13:34:23 +020078 }
Pablo Neira Ayuso0849c9a2011-06-09 15:04:30 +020079
Harald Welte33cb71a2011-05-21 18:54:32 +020080 sprintf(portbuf, "%u", port);
81 memset(&hints, 0, sizeof(struct addrinfo));
82 hints.ai_family = family;
Pablo Neira Ayusoe04a14d2013-01-14 00:12:28 +010083 if (type == SOCK_RAW) {
84 /* Workaround for glibc, that returns EAI_SERVICE (-8) if
85 * SOCK_RAW and IPPROTO_GRE is used.
86 */
87 hints.ai_socktype = SOCK_DGRAM;
88 hints.ai_protocol = IPPROTO_UDP;
89 } else {
90 hints.ai_socktype = type;
91 hints.ai_protocol = proto;
92 }
Harald Welte33cb71a2011-05-21 18:54:32 +020093
Pablo Neira Ayuso0849c9a2011-06-09 15:04:30 +020094 if (flags & OSMO_SOCK_F_BIND)
Harald Weltef9e07462011-05-31 17:47:54 +020095 hints.ai_flags |= AI_PASSIVE;
96
Harald Welte33cb71a2011-05-21 18:54:32 +020097 rc = getaddrinfo(host, portbuf, &hints, &result);
98 if (rc != 0) {
Neels Hofmeyrf0f07d92016-08-22 13:34:23 +020099 fprintf(stderr, "getaddrinfo returned NULL: %s:%u: %s\n",
100 host, port, strerror(errno));
Harald Welte33cb71a2011-05-21 18:54:32 +0200101 return -EINVAL;
102 }
103
104 for (rp = result; rp != NULL; rp = rp->ai_next) {
Pablo Neira Ayusoe04a14d2013-01-14 00:12:28 +0100105 /* Workaround for glibc again */
106 if (type == SOCK_RAW) {
107 rp->ai_socktype = SOCK_RAW;
108 rp->ai_protocol = proto;
109 }
110
Harald Welte33cb71a2011-05-21 18:54:32 +0200111 sfd = socket(rp->ai_family, rp->ai_socktype, rp->ai_protocol);
112 if (sfd == -1)
113 continue;
Pablo Neira Ayuso0849c9a2011-06-09 15:04:30 +0200114 if (flags & OSMO_SOCK_F_NONBLOCK) {
115 if (ioctl(sfd, FIONBIO, (unsigned char *)&on) < 0) {
Neels Hofmeyrf0f07d92016-08-22 13:34:23 +0200116 fprintf(stderr,
117 "cannot set this socket unblocking:"
118 " %s:%u: %s\n",
119 host, port, strerror(errno));
Pablo Neira Ayuso0849c9a2011-06-09 15:04:30 +0200120 close(sfd);
Harald Welte0fc315d2016-11-26 10:18:24 +0100121 freeaddrinfo(result);
Pablo Neira Ayuso0849c9a2011-06-09 15:04:30 +0200122 return -EINVAL;
123 }
124 }
125 if (flags & OSMO_SOCK_F_CONNECT) {
126 rc = connect(sfd, rp->ai_addr, rp->ai_addrlen);
127 if (rc != -1 || (rc == -1 && errno == EINPROGRESS))
Harald Welte33cb71a2011-05-21 18:54:32 +0200128 break;
129 } else {
Pablo Neira Ayuso0849c9a2011-06-09 15:04:30 +0200130 rc = setsockopt(sfd, SOL_SOCKET, SO_REUSEADDR,
131 &on, sizeof(on));
132 if (rc < 0) {
Neels Hofmeyrf0f07d92016-08-22 13:34:23 +0200133 fprintf(stderr,
134 "cannot setsockopt socket:"
135 " %s:%u: %s\n",
136 host, port, strerror(errno));
Pablo Neira Ayuso0849c9a2011-06-09 15:04:30 +0200137 break;
138 }
Harald Welte33cb71a2011-05-21 18:54:32 +0200139 if (bind(sfd, rp->ai_addr, rp->ai_addrlen) != -1)
140 break;
141 }
142 close(sfd);
143 }
144 freeaddrinfo(result);
145
146 if (rp == NULL) {
Neels Hofmeyrf0f07d92016-08-22 13:34:23 +0200147 fprintf(stderr, "unable to connect/bind socket: %s:%u: %s\n",
148 host, port, strerror(errno));
Harald Welte33cb71a2011-05-21 18:54:32 +0200149 return -ENODEV;
150 }
Harald Welte68b15742011-05-22 21:47:29 +0200151
152 setsockopt(sfd, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on));
153
154 /* Make sure to call 'listen' on a bound, connection-oriented sock */
Pablo Neira Ayuso0849c9a2011-06-09 15:04:30 +0200155 if (flags & OSMO_SOCK_F_BIND) {
Harald Welte68b15742011-05-22 21:47:29 +0200156 switch (type) {
157 case SOCK_STREAM:
158 case SOCK_SEQPACKET:
159 listen(sfd, 10);
160 break;
161 }
162 }
163 return sfd;
164}
165
Max862ba652014-10-13 14:54:25 +0200166/*! \brief fill \ref osmo_fd for a give sfd
167 * \param[out] ofd file descriptor (will be filled in)
168 * \param[in] sfd socket file descriptor
Harald Welte2d2e2cc2016-04-25 12:11:20 +0200169 * \returns socket fd on success; negative on error
Max862ba652014-10-13 14:54:25 +0200170 *
171 * This function fills the \a ofd structure.
172 */
173static inline int osmo_fd_init_ofd(struct osmo_fd *ofd, int sfd)
174{
175 int rc;
176
177 if (sfd < 0)
178 return sfd;
179
180 ofd->fd = sfd;
181 ofd->when = BSC_FD_READ;
182
183 rc = osmo_fd_register(ofd);
184 if (rc < 0) {
185 close(sfd);
186 return rc;
187 }
188
189 return sfd;
190}
191
Harald Welteba6988b2011-08-17 12:46:48 +0200192/*! \brief Initialize a socket and fill \ref osmo_fd
Katerina Barone-Adesic28c6a02013-02-15 13:27:59 +0100193 * \param[out] ofd file descriptor (will be filled in)
Harald Welteba6988b2011-08-17 12:46:48 +0200194 * \param[in] family Address Family like AF_INET, AF_INET6, AF_UNSPEC
195 * \param[in] type Socket type like SOCK_DGRAM, SOCK_STREAM
196 * \param[in] proto Protocol like IPPROTO_TCP, IPPROTO_UDP
197 * \param[in] host remote host name or IP address in string form
198 * \param[in] port remote port number in host byte order
199 * \param[in] flags flags like \ref OSMO_SOCK_F_CONNECT
Harald Welte2d2e2cc2016-04-25 12:11:20 +0200200 * \returns socket fd on success; negative on error
Harald Welteba6988b2011-08-17 12:46:48 +0200201 *
202 * This function creates (and optionall binds/connects) a socket using
203 * \ref osmo_sock_init, but also fills the \a ofd structure.
204 */
Harald Welte68b15742011-05-22 21:47:29 +0200205int osmo_sock_init_ofd(struct osmo_fd *ofd, int family, int type, int proto,
Pablo Neira Ayuso0849c9a2011-06-09 15:04:30 +0200206 const char *host, uint16_t port, unsigned int flags)
Harald Welte68b15742011-05-22 21:47:29 +0200207{
Max862ba652014-10-13 14:54:25 +0200208 return osmo_fd_init_ofd(ofd, osmo_sock_init(family, type, proto, host, port, flags));
Harald Welte33cb71a2011-05-21 18:54:32 +0200209}
210
Harald Welteba6988b2011-08-17 12:46:48 +0200211/*! \brief Initialize a socket and fill \ref sockaddr
212 * \param[out] ss socket address (will be filled in)
213 * \param[in] type Socket type like SOCK_DGRAM, SOCK_STREAM
214 * \param[in] proto Protocol like IPPROTO_TCP, IPPROTO_UDP
215 * \param[in] flags flags like \ref OSMO_SOCK_F_CONNECT
Harald Welte2d2e2cc2016-04-25 12:11:20 +0200216 * \returns socket fd on success; negative on error
Harald Welteba6988b2011-08-17 12:46:48 +0200217 *
218 * This function creates (and optionall binds/connects) a socket using
219 * \ref osmo_sock_init, but also fills the \a ss structure.
220 */
Harald Welte33cb71a2011-05-21 18:54:32 +0200221int osmo_sock_init_sa(struct sockaddr *ss, uint16_t type,
Pablo Neira Ayuso0849c9a2011-06-09 15:04:30 +0200222 uint8_t proto, unsigned int flags)
Harald Welte33cb71a2011-05-21 18:54:32 +0200223{
224 char host[NI_MAXHOST];
225 uint16_t port;
226 struct sockaddr_in *sin;
227 struct sockaddr_in6 *sin6;
228 int s, sa_len;
229
230 /* determine port and host from ss */
231 switch (ss->sa_family) {
232 case AF_INET:
233 sin = (struct sockaddr_in *) ss;
234 sa_len = sizeof(struct sockaddr_in);
235 port = ntohs(sin->sin_port);
236 break;
237 case AF_INET6:
238 sin6 = (struct sockaddr_in6 *) ss;
239 sa_len = sizeof(struct sockaddr_in6);
240 port = ntohs(sin6->sin6_port);
241 break;
242 default:
243 return -EINVAL;
244 }
Harald Welte33cb71a2011-05-21 18:54:32 +0200245
246 s = getnameinfo(ss, sa_len, host, NI_MAXHOST,
247 NULL, 0, NI_NUMERICHOST);
248 if (s != 0) {
249 perror("getnameinfo failed");
250 return s;
251 }
252
Pablo Neira Ayuso0849c9a2011-06-09 15:04:30 +0200253 return osmo_sock_init(ss->sa_family, type, proto, host, port, flags);
Harald Welte33cb71a2011-05-21 18:54:32 +0200254}
255
256static int sockaddr_equal(const struct sockaddr *a,
Harald Weltee4764422011-05-22 12:25:57 +0200257 const struct sockaddr *b, unsigned int len)
Harald Welte33cb71a2011-05-21 18:54:32 +0200258{
259 struct sockaddr_in *sin_a, *sin_b;
260 struct sockaddr_in6 *sin6_a, *sin6_b;
261
262 if (a->sa_family != b->sa_family)
263 return 0;
264
265 switch (a->sa_family) {
266 case AF_INET:
267 sin_a = (struct sockaddr_in *)a;
268 sin_b = (struct sockaddr_in *)b;
269 if (!memcmp(&sin_a->sin_addr, &sin_b->sin_addr,
270 sizeof(struct in_addr)))
271 return 1;
272 break;
273 case AF_INET6:
274 sin6_a = (struct sockaddr_in6 *)a;
275 sin6_b = (struct sockaddr_in6 *)b;
276 if (!memcmp(&sin6_a->sin6_addr, &sin6_b->sin6_addr,
277 sizeof(struct in6_addr)))
278 return 1;
279 break;
280 }
281 return 0;
282}
283
Harald Welteba6988b2011-08-17 12:46:48 +0200284/*! \brief Determine if the given address is a local address
285 * \param[in] addr Socket Address
286 * \param[in] addrlen Length of socket address in bytes
287 * \returns 1 if address is local, 0 otherwise.
288 */
Harald Weltebc32d052012-04-08 11:31:32 +0200289int osmo_sockaddr_is_local(struct sockaddr *addr, unsigned int addrlen)
Harald Welte33cb71a2011-05-21 18:54:32 +0200290{
291 struct ifaddrs *ifaddr, *ifa;
292
293 if (getifaddrs(&ifaddr) == -1) {
294 perror("getifaddrs");
295 return -EIO;
296 }
297
298 for (ifa = ifaddr; ifa != NULL; ifa = ifa->ifa_next) {
Harald Welte4d3a7b12011-05-24 21:31:53 +0200299 if (!ifa->ifa_addr)
300 continue;
Harald Welte33cb71a2011-05-21 18:54:32 +0200301 if (sockaddr_equal(ifa->ifa_addr, addr, addrlen))
302 return 1;
303 }
304
305 return 0;
306}
Harald Weltee4764422011-05-22 12:25:57 +0200307
Álvaro Neira Ayuso5ade61a2014-03-24 13:02:00 +0100308/*! \brief Initialize a unix domain socket (including bind/connect)
309 * \param[in] type Socket type like SOCK_DGRAM, SOCK_STREAM
310 * \param[in] proto Protocol like IPPROTO_TCP, IPPROTO_UDP
311 * \param[in] socket_path path to identify the socket
312 * \param[in] flags flags like \ref OSMO_SOCK_F_CONNECT
Harald Welte2d2e2cc2016-04-25 12:11:20 +0200313 * \returns socket fd on success; negative on error
Álvaro Neira Ayuso5ade61a2014-03-24 13:02:00 +0100314 *
315 * This function creates a new unix domain socket, \a
316 * type and \a proto and optionally binds or connects it, depending on
317 * the value of \a flags parameter.
318 */
319int osmo_sock_unix_init(uint16_t type, uint8_t proto,
320 const char *socket_path, unsigned int flags)
321{
322 struct sockaddr_un local;
323 int sfd, rc, on = 1;
324 unsigned int namelen;
325
326 if ((flags & (OSMO_SOCK_F_BIND | OSMO_SOCK_F_CONNECT)) ==
327 (OSMO_SOCK_F_BIND | OSMO_SOCK_F_CONNECT))
328 return -EINVAL;
329
330 local.sun_family = AF_UNIX;
331 strncpy(local.sun_path, socket_path, sizeof(local.sun_path));
332 local.sun_path[sizeof(local.sun_path) - 1] = '\0';
333
334#if defined(BSD44SOCKETS) || defined(__UNIXWARE__)
335 local.sun_len = strlen(local.sun_path);
336#endif
337#if defined(BSD44SOCKETS) || defined(SUN_LEN)
338 namelen = SUN_LEN(&local);
339#else
340 namelen = strlen(local.sun_path) +
341 offsetof(struct sockaddr_un, sun_path);
342#endif
343
344 sfd = socket(AF_UNIX, type, proto);
345 if (sfd < 0)
346 return -1;
347
348 if (flags & OSMO_SOCK_F_CONNECT) {
349 rc = connect(sfd, (struct sockaddr *)&local, namelen);
350 if (rc < 0)
351 goto err;
352 } else {
353 unlink(local.sun_path);
354 rc = bind(sfd, (struct sockaddr *)&local, namelen);
355 if (rc < 0)
356 goto err;
357 }
358
359 if (flags & OSMO_SOCK_F_NONBLOCK) {
360 if (ioctl(sfd, FIONBIO, (unsigned char *)&on) < 0) {
361 perror("cannot set this socket unblocking");
362 close(sfd);
363 return -EINVAL;
364 }
365 }
366
367 if (flags & OSMO_SOCK_F_BIND) {
368 rc = listen(sfd, 10);
369 if (rc < 0)
370 goto err;
371 }
372
373 return sfd;
374err:
375 close(sfd);
376 return -1;
377}
378
379/*! \brief Initialize a unix domain socket and fill \ref osmo_fd
380 * \param[out] ofd file descriptor (will be filled in)
381 * \param[in] type Socket type like SOCK_DGRAM, SOCK_STREAM
382 * \param[in] proto Protocol like IPPROTO_TCP, IPPROTO_UDP
383 * \param[in] socket_path path to identify the socket
384 * \param[in] flags flags like \ref OSMO_SOCK_F_CONNECT
Harald Welte2d2e2cc2016-04-25 12:11:20 +0200385 * \returns socket fd on success; negative on error
Álvaro Neira Ayuso5ade61a2014-03-24 13:02:00 +0100386 *
387 * This function creates (and optionall binds/connects) a socket using
388 * \ref osmo_sock_unix_init, but also fills the \a ofd structure.
389 */
390int osmo_sock_unix_init_ofd(struct osmo_fd *ofd, uint16_t type, uint8_t proto,
391 const char *socket_path, unsigned int flags)
392{
Max862ba652014-10-13 14:54:25 +0200393 return osmo_fd_init_ofd(ofd, osmo_sock_unix_init(type, proto, socket_path, flags));
Álvaro Neira Ayuso5ade61a2014-03-24 13:02:00 +0100394}
395
Harald Weltee4764422011-05-22 12:25:57 +0200396#endif /* HAVE_SYS_SOCKET_H */
Harald Welteba6988b2011-08-17 12:46:48 +0200397
Sylvain Munautdca7d2c2012-04-18 21:53:23 +0200398/*! @} */