blob: 19c513a8fea4df854dc37e3a0f588b40b17608af [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);
121 return -EINVAL;
122 }
123 }
124 if (flags & OSMO_SOCK_F_CONNECT) {
125 rc = connect(sfd, rp->ai_addr, rp->ai_addrlen);
126 if (rc != -1 || (rc == -1 && errno == EINPROGRESS))
Harald Welte33cb71a2011-05-21 18:54:32 +0200127 break;
128 } else {
Pablo Neira Ayuso0849c9a2011-06-09 15:04:30 +0200129 rc = setsockopt(sfd, SOL_SOCKET, SO_REUSEADDR,
130 &on, sizeof(on));
131 if (rc < 0) {
Neels Hofmeyrf0f07d92016-08-22 13:34:23 +0200132 fprintf(stderr,
133 "cannot setsockopt socket:"
134 " %s:%u: %s\n",
135 host, port, strerror(errno));
Pablo Neira Ayuso0849c9a2011-06-09 15:04:30 +0200136 break;
137 }
Harald Welte33cb71a2011-05-21 18:54:32 +0200138 if (bind(sfd, rp->ai_addr, rp->ai_addrlen) != -1)
139 break;
140 }
141 close(sfd);
142 }
143 freeaddrinfo(result);
144
145 if (rp == NULL) {
Neels Hofmeyrf0f07d92016-08-22 13:34:23 +0200146 fprintf(stderr, "unable to connect/bind socket: %s:%u: %s\n",
147 host, port, strerror(errno));
Harald Welte33cb71a2011-05-21 18:54:32 +0200148 return -ENODEV;
149 }
Harald Welte68b15742011-05-22 21:47:29 +0200150
151 setsockopt(sfd, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on));
152
153 /* Make sure to call 'listen' on a bound, connection-oriented sock */
Pablo Neira Ayuso0849c9a2011-06-09 15:04:30 +0200154 if (flags & OSMO_SOCK_F_BIND) {
Harald Welte68b15742011-05-22 21:47:29 +0200155 switch (type) {
156 case SOCK_STREAM:
157 case SOCK_SEQPACKET:
158 listen(sfd, 10);
159 break;
160 }
161 }
162 return sfd;
163}
164
Max862ba652014-10-13 14:54:25 +0200165/*! \brief fill \ref osmo_fd for a give sfd
166 * \param[out] ofd file descriptor (will be filled in)
167 * \param[in] sfd socket file descriptor
Harald Welte2d2e2cc2016-04-25 12:11:20 +0200168 * \returns socket fd on success; negative on error
Max862ba652014-10-13 14:54:25 +0200169 *
170 * This function fills the \a ofd structure.
171 */
172static inline int osmo_fd_init_ofd(struct osmo_fd *ofd, int sfd)
173{
174 int rc;
175
176 if (sfd < 0)
177 return sfd;
178
179 ofd->fd = sfd;
180 ofd->when = BSC_FD_READ;
181
182 rc = osmo_fd_register(ofd);
183 if (rc < 0) {
184 close(sfd);
185 return rc;
186 }
187
188 return sfd;
189}
190
Harald Welteba6988b2011-08-17 12:46:48 +0200191/*! \brief Initialize a socket and fill \ref osmo_fd
Katerina Barone-Adesic28c6a02013-02-15 13:27:59 +0100192 * \param[out] ofd file descriptor (will be filled in)
Harald Welteba6988b2011-08-17 12:46:48 +0200193 * \param[in] family Address Family like AF_INET, AF_INET6, AF_UNSPEC
194 * \param[in] type Socket type like SOCK_DGRAM, SOCK_STREAM
195 * \param[in] proto Protocol like IPPROTO_TCP, IPPROTO_UDP
196 * \param[in] host remote host name or IP address in string form
197 * \param[in] port remote port number in host byte order
198 * \param[in] flags flags like \ref OSMO_SOCK_F_CONNECT
Harald Welte2d2e2cc2016-04-25 12:11:20 +0200199 * \returns socket fd on success; negative on error
Harald Welteba6988b2011-08-17 12:46:48 +0200200 *
201 * This function creates (and optionall binds/connects) a socket using
202 * \ref osmo_sock_init, but also fills the \a ofd structure.
203 */
Harald Welte68b15742011-05-22 21:47:29 +0200204int osmo_sock_init_ofd(struct osmo_fd *ofd, int family, int type, int proto,
Pablo Neira Ayuso0849c9a2011-06-09 15:04:30 +0200205 const char *host, uint16_t port, unsigned int flags)
Harald Welte68b15742011-05-22 21:47:29 +0200206{
Max862ba652014-10-13 14:54:25 +0200207 return osmo_fd_init_ofd(ofd, osmo_sock_init(family, type, proto, host, port, flags));
Harald Welte33cb71a2011-05-21 18:54:32 +0200208}
209
Harald Welteba6988b2011-08-17 12:46:48 +0200210/*! \brief Initialize a socket and fill \ref sockaddr
211 * \param[out] ss socket address (will be filled in)
212 * \param[in] type Socket type like SOCK_DGRAM, SOCK_STREAM
213 * \param[in] proto Protocol like IPPROTO_TCP, IPPROTO_UDP
214 * \param[in] flags flags like \ref OSMO_SOCK_F_CONNECT
Harald Welte2d2e2cc2016-04-25 12:11:20 +0200215 * \returns socket fd on success; negative on error
Harald Welteba6988b2011-08-17 12:46:48 +0200216 *
217 * This function creates (and optionall binds/connects) a socket using
218 * \ref osmo_sock_init, but also fills the \a ss structure.
219 */
Harald Welte33cb71a2011-05-21 18:54:32 +0200220int osmo_sock_init_sa(struct sockaddr *ss, uint16_t type,
Pablo Neira Ayuso0849c9a2011-06-09 15:04:30 +0200221 uint8_t proto, unsigned int flags)
Harald Welte33cb71a2011-05-21 18:54:32 +0200222{
223 char host[NI_MAXHOST];
224 uint16_t port;
225 struct sockaddr_in *sin;
226 struct sockaddr_in6 *sin6;
227 int s, sa_len;
228
229 /* determine port and host from ss */
230 switch (ss->sa_family) {
231 case AF_INET:
232 sin = (struct sockaddr_in *) ss;
233 sa_len = sizeof(struct sockaddr_in);
234 port = ntohs(sin->sin_port);
235 break;
236 case AF_INET6:
237 sin6 = (struct sockaddr_in6 *) ss;
238 sa_len = sizeof(struct sockaddr_in6);
239 port = ntohs(sin6->sin6_port);
240 break;
241 default:
242 return -EINVAL;
243 }
Harald Welte33cb71a2011-05-21 18:54:32 +0200244
245 s = getnameinfo(ss, sa_len, host, NI_MAXHOST,
246 NULL, 0, NI_NUMERICHOST);
247 if (s != 0) {
248 perror("getnameinfo failed");
249 return s;
250 }
251
Pablo Neira Ayuso0849c9a2011-06-09 15:04:30 +0200252 return osmo_sock_init(ss->sa_family, type, proto, host, port, flags);
Harald Welte33cb71a2011-05-21 18:54:32 +0200253}
254
255static int sockaddr_equal(const struct sockaddr *a,
Harald Weltee4764422011-05-22 12:25:57 +0200256 const struct sockaddr *b, unsigned int len)
Harald Welte33cb71a2011-05-21 18:54:32 +0200257{
258 struct sockaddr_in *sin_a, *sin_b;
259 struct sockaddr_in6 *sin6_a, *sin6_b;
260
261 if (a->sa_family != b->sa_family)
262 return 0;
263
264 switch (a->sa_family) {
265 case AF_INET:
266 sin_a = (struct sockaddr_in *)a;
267 sin_b = (struct sockaddr_in *)b;
268 if (!memcmp(&sin_a->sin_addr, &sin_b->sin_addr,
269 sizeof(struct in_addr)))
270 return 1;
271 break;
272 case AF_INET6:
273 sin6_a = (struct sockaddr_in6 *)a;
274 sin6_b = (struct sockaddr_in6 *)b;
275 if (!memcmp(&sin6_a->sin6_addr, &sin6_b->sin6_addr,
276 sizeof(struct in6_addr)))
277 return 1;
278 break;
279 }
280 return 0;
281}
282
Harald Welteba6988b2011-08-17 12:46:48 +0200283/*! \brief Determine if the given address is a local address
284 * \param[in] addr Socket Address
285 * \param[in] addrlen Length of socket address in bytes
286 * \returns 1 if address is local, 0 otherwise.
287 */
Harald Weltebc32d052012-04-08 11:31:32 +0200288int osmo_sockaddr_is_local(struct sockaddr *addr, unsigned int addrlen)
Harald Welte33cb71a2011-05-21 18:54:32 +0200289{
290 struct ifaddrs *ifaddr, *ifa;
291
292 if (getifaddrs(&ifaddr) == -1) {
293 perror("getifaddrs");
294 return -EIO;
295 }
296
297 for (ifa = ifaddr; ifa != NULL; ifa = ifa->ifa_next) {
Harald Welte4d3a7b12011-05-24 21:31:53 +0200298 if (!ifa->ifa_addr)
299 continue;
Harald Welte33cb71a2011-05-21 18:54:32 +0200300 if (sockaddr_equal(ifa->ifa_addr, addr, addrlen))
301 return 1;
302 }
303
304 return 0;
305}
Harald Weltee4764422011-05-22 12:25:57 +0200306
Álvaro Neira Ayuso5ade61a2014-03-24 13:02:00 +0100307/*! \brief Initialize a unix domain socket (including bind/connect)
308 * \param[in] type Socket type like SOCK_DGRAM, SOCK_STREAM
309 * \param[in] proto Protocol like IPPROTO_TCP, IPPROTO_UDP
310 * \param[in] socket_path path to identify the socket
311 * \param[in] flags flags like \ref OSMO_SOCK_F_CONNECT
Harald Welte2d2e2cc2016-04-25 12:11:20 +0200312 * \returns socket fd on success; negative on error
Álvaro Neira Ayuso5ade61a2014-03-24 13:02:00 +0100313 *
314 * This function creates a new unix domain socket, \a
315 * type and \a proto and optionally binds or connects it, depending on
316 * the value of \a flags parameter.
317 */
318int osmo_sock_unix_init(uint16_t type, uint8_t proto,
319 const char *socket_path, unsigned int flags)
320{
321 struct sockaddr_un local;
322 int sfd, rc, on = 1;
323 unsigned int namelen;
324
325 if ((flags & (OSMO_SOCK_F_BIND | OSMO_SOCK_F_CONNECT)) ==
326 (OSMO_SOCK_F_BIND | OSMO_SOCK_F_CONNECT))
327 return -EINVAL;
328
329 local.sun_family = AF_UNIX;
330 strncpy(local.sun_path, socket_path, sizeof(local.sun_path));
331 local.sun_path[sizeof(local.sun_path) - 1] = '\0';
332
333#if defined(BSD44SOCKETS) || defined(__UNIXWARE__)
334 local.sun_len = strlen(local.sun_path);
335#endif
336#if defined(BSD44SOCKETS) || defined(SUN_LEN)
337 namelen = SUN_LEN(&local);
338#else
339 namelen = strlen(local.sun_path) +
340 offsetof(struct sockaddr_un, sun_path);
341#endif
342
343 sfd = socket(AF_UNIX, type, proto);
344 if (sfd < 0)
345 return -1;
346
347 if (flags & OSMO_SOCK_F_CONNECT) {
348 rc = connect(sfd, (struct sockaddr *)&local, namelen);
349 if (rc < 0)
350 goto err;
351 } else {
352 unlink(local.sun_path);
353 rc = bind(sfd, (struct sockaddr *)&local, namelen);
354 if (rc < 0)
355 goto err;
356 }
357
358 if (flags & OSMO_SOCK_F_NONBLOCK) {
359 if (ioctl(sfd, FIONBIO, (unsigned char *)&on) < 0) {
360 perror("cannot set this socket unblocking");
361 close(sfd);
362 return -EINVAL;
363 }
364 }
365
366 if (flags & OSMO_SOCK_F_BIND) {
367 rc = listen(sfd, 10);
368 if (rc < 0)
369 goto err;
370 }
371
372 return sfd;
373err:
374 close(sfd);
375 return -1;
376}
377
378/*! \brief Initialize a unix domain socket and fill \ref osmo_fd
379 * \param[out] ofd file descriptor (will be filled in)
380 * \param[in] type Socket type like SOCK_DGRAM, SOCK_STREAM
381 * \param[in] proto Protocol like IPPROTO_TCP, IPPROTO_UDP
382 * \param[in] socket_path path to identify the socket
383 * \param[in] flags flags like \ref OSMO_SOCK_F_CONNECT
Harald Welte2d2e2cc2016-04-25 12:11:20 +0200384 * \returns socket fd on success; negative on error
Álvaro Neira Ayuso5ade61a2014-03-24 13:02:00 +0100385 *
386 * This function creates (and optionall binds/connects) a socket using
387 * \ref osmo_sock_unix_init, but also fills the \a ofd structure.
388 */
389int osmo_sock_unix_init_ofd(struct osmo_fd *ofd, uint16_t type, uint8_t proto,
390 const char *socket_path, unsigned int flags)
391{
Max862ba652014-10-13 14:54:25 +0200392 return osmo_fd_init_ofd(ofd, osmo_sock_unix_init(type, proto, socket_path, flags));
Álvaro Neira Ayuso5ade61a2014-03-24 13:02:00 +0100393}
394
Harald Weltee4764422011-05-22 12:25:57 +0200395#endif /* HAVE_SYS_SOCKET_H */
Harald Welteba6988b2011-08-17 12:46:48 +0200396
Sylvain Munautdca7d2c2012-04-18 21:53:23 +0200397/*! @} */