blob: 844ebd3b6bb058ce70ca50458974b903c9ac3b3a [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
60 *
61 * This function creates a new socket of the designated \a family, \a
62 * type and \a proto and optionally binds or connects it, depending on
63 * the value of \a flags parameter.
64 */
Harald Welte33cb71a2011-05-21 18:54:32 +020065int osmo_sock_init(uint16_t family, uint16_t type, uint8_t proto,
Pablo Neira Ayuso0849c9a2011-06-09 15:04:30 +020066 const char *host, uint16_t port, unsigned int flags)
Harald Welte33cb71a2011-05-21 18:54:32 +020067{
68 struct addrinfo hints, *result, *rp;
Harald Welte68b15742011-05-22 21:47:29 +020069 int sfd, rc, on = 1;
Harald Welte33cb71a2011-05-21 18:54:32 +020070 char portbuf[16];
71
Pablo Neira Ayuso0849c9a2011-06-09 15:04:30 +020072 if ((flags & (OSMO_SOCK_F_BIND | OSMO_SOCK_F_CONNECT)) ==
73 (OSMO_SOCK_F_BIND | OSMO_SOCK_F_CONNECT))
74 return -EINVAL;
75
Harald Welte33cb71a2011-05-21 18:54:32 +020076 sprintf(portbuf, "%u", port);
77 memset(&hints, 0, sizeof(struct addrinfo));
78 hints.ai_family = family;
Pablo Neira Ayusoe04a14d2013-01-14 00:12:28 +010079 if (type == SOCK_RAW) {
80 /* Workaround for glibc, that returns EAI_SERVICE (-8) if
81 * SOCK_RAW and IPPROTO_GRE is used.
82 */
83 hints.ai_socktype = SOCK_DGRAM;
84 hints.ai_protocol = IPPROTO_UDP;
85 } else {
86 hints.ai_socktype = type;
87 hints.ai_protocol = proto;
88 }
Harald Welte33cb71a2011-05-21 18:54:32 +020089
Pablo Neira Ayuso0849c9a2011-06-09 15:04:30 +020090 if (flags & OSMO_SOCK_F_BIND)
Harald Weltef9e07462011-05-31 17:47:54 +020091 hints.ai_flags |= AI_PASSIVE;
92
Harald Welte33cb71a2011-05-21 18:54:32 +020093 rc = getaddrinfo(host, portbuf, &hints, &result);
94 if (rc != 0) {
95 perror("getaddrinfo returned NULL");
96 return -EINVAL;
97 }
98
99 for (rp = result; rp != NULL; rp = rp->ai_next) {
Pablo Neira Ayusoe04a14d2013-01-14 00:12:28 +0100100 /* Workaround for glibc again */
101 if (type == SOCK_RAW) {
102 rp->ai_socktype = SOCK_RAW;
103 rp->ai_protocol = proto;
104 }
105
Harald Welte33cb71a2011-05-21 18:54:32 +0200106 sfd = socket(rp->ai_family, rp->ai_socktype, rp->ai_protocol);
107 if (sfd == -1)
108 continue;
Pablo Neira Ayuso0849c9a2011-06-09 15:04:30 +0200109 if (flags & OSMO_SOCK_F_NONBLOCK) {
110 if (ioctl(sfd, FIONBIO, (unsigned char *)&on) < 0) {
111 perror("cannot set this socket unblocking");
112 close(sfd);
113 return -EINVAL;
114 }
115 }
116 if (flags & OSMO_SOCK_F_CONNECT) {
117 rc = connect(sfd, rp->ai_addr, rp->ai_addrlen);
118 if (rc != -1 || (rc == -1 && errno == EINPROGRESS))
Harald Welte33cb71a2011-05-21 18:54:32 +0200119 break;
120 } else {
Pablo Neira Ayuso0849c9a2011-06-09 15:04:30 +0200121 rc = setsockopt(sfd, SOL_SOCKET, SO_REUSEADDR,
122 &on, sizeof(on));
123 if (rc < 0) {
124 perror("cannot setsockopt socket");
125 break;
126 }
Harald Welte33cb71a2011-05-21 18:54:32 +0200127 if (bind(sfd, rp->ai_addr, rp->ai_addrlen) != -1)
128 break;
129 }
130 close(sfd);
131 }
132 freeaddrinfo(result);
133
134 if (rp == NULL) {
135 perror("unable to connect/bind socket");
136 return -ENODEV;
137 }
Harald Welte68b15742011-05-22 21:47:29 +0200138
139 setsockopt(sfd, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on));
140
141 /* Make sure to call 'listen' on a bound, connection-oriented sock */
Pablo Neira Ayuso0849c9a2011-06-09 15:04:30 +0200142 if (flags & OSMO_SOCK_F_BIND) {
Harald Welte68b15742011-05-22 21:47:29 +0200143 switch (type) {
144 case SOCK_STREAM:
145 case SOCK_SEQPACKET:
146 listen(sfd, 10);
147 break;
148 }
149 }
150 return sfd;
151}
152
Harald Welteba6988b2011-08-17 12:46:48 +0200153/*! \brief Initialize a socket and fill \ref osmo_fd
Katerina Barone-Adesic28c6a02013-02-15 13:27:59 +0100154 * \param[out] ofd file descriptor (will be filled in)
Harald Welteba6988b2011-08-17 12:46:48 +0200155 * \param[in] family Address Family like AF_INET, AF_INET6, AF_UNSPEC
156 * \param[in] type Socket type like SOCK_DGRAM, SOCK_STREAM
157 * \param[in] proto Protocol like IPPROTO_TCP, IPPROTO_UDP
158 * \param[in] host remote host name or IP address in string form
159 * \param[in] port remote port number in host byte order
160 * \param[in] flags flags like \ref OSMO_SOCK_F_CONNECT
161 *
162 * This function creates (and optionall binds/connects) a socket using
163 * \ref osmo_sock_init, but also fills the \a ofd structure.
164 */
Harald Welte68b15742011-05-22 21:47:29 +0200165int osmo_sock_init_ofd(struct osmo_fd *ofd, int family, int type, int proto,
Pablo Neira Ayuso0849c9a2011-06-09 15:04:30 +0200166 const char *host, uint16_t port, unsigned int flags)
Harald Welte68b15742011-05-22 21:47:29 +0200167{
168 int sfd, rc;
169
Pablo Neira Ayuso0849c9a2011-06-09 15:04:30 +0200170 sfd = osmo_sock_init(family, type, proto, host, port, flags);
Harald Welte68b15742011-05-22 21:47:29 +0200171 if (sfd < 0)
172 return sfd;
173
174 ofd->fd = sfd;
175 ofd->when = BSC_FD_READ;
176
177 rc = osmo_fd_register(ofd);
178 if (rc < 0) {
179 close(sfd);
180 return rc;
181 }
182
Harald Welte33cb71a2011-05-21 18:54:32 +0200183 return sfd;
184}
185
Harald Welteba6988b2011-08-17 12:46:48 +0200186/*! \brief Initialize a socket and fill \ref sockaddr
187 * \param[out] ss socket address (will be filled in)
188 * \param[in] type Socket type like SOCK_DGRAM, SOCK_STREAM
189 * \param[in] proto Protocol like IPPROTO_TCP, IPPROTO_UDP
190 * \param[in] flags flags like \ref OSMO_SOCK_F_CONNECT
191 *
192 * This function creates (and optionall binds/connects) a socket using
193 * \ref osmo_sock_init, but also fills the \a ss structure.
194 */
Harald Welte33cb71a2011-05-21 18:54:32 +0200195int osmo_sock_init_sa(struct sockaddr *ss, uint16_t type,
Pablo Neira Ayuso0849c9a2011-06-09 15:04:30 +0200196 uint8_t proto, unsigned int flags)
Harald Welte33cb71a2011-05-21 18:54:32 +0200197{
198 char host[NI_MAXHOST];
199 uint16_t port;
200 struct sockaddr_in *sin;
201 struct sockaddr_in6 *sin6;
202 int s, sa_len;
203
204 /* determine port and host from ss */
205 switch (ss->sa_family) {
206 case AF_INET:
207 sin = (struct sockaddr_in *) ss;
208 sa_len = sizeof(struct sockaddr_in);
209 port = ntohs(sin->sin_port);
210 break;
211 case AF_INET6:
212 sin6 = (struct sockaddr_in6 *) ss;
213 sa_len = sizeof(struct sockaddr_in6);
214 port = ntohs(sin6->sin6_port);
215 break;
216 default:
217 return -EINVAL;
218 }
Harald Welte33cb71a2011-05-21 18:54:32 +0200219
220 s = getnameinfo(ss, sa_len, host, NI_MAXHOST,
221 NULL, 0, NI_NUMERICHOST);
222 if (s != 0) {
223 perror("getnameinfo failed");
224 return s;
225 }
226
Pablo Neira Ayuso0849c9a2011-06-09 15:04:30 +0200227 return osmo_sock_init(ss->sa_family, type, proto, host, port, flags);
Harald Welte33cb71a2011-05-21 18:54:32 +0200228}
229
230static int sockaddr_equal(const struct sockaddr *a,
Harald Weltee4764422011-05-22 12:25:57 +0200231 const struct sockaddr *b, unsigned int len)
Harald Welte33cb71a2011-05-21 18:54:32 +0200232{
233 struct sockaddr_in *sin_a, *sin_b;
234 struct sockaddr_in6 *sin6_a, *sin6_b;
235
236 if (a->sa_family != b->sa_family)
237 return 0;
238
239 switch (a->sa_family) {
240 case AF_INET:
241 sin_a = (struct sockaddr_in *)a;
242 sin_b = (struct sockaddr_in *)b;
243 if (!memcmp(&sin_a->sin_addr, &sin_b->sin_addr,
244 sizeof(struct in_addr)))
245 return 1;
246 break;
247 case AF_INET6:
248 sin6_a = (struct sockaddr_in6 *)a;
249 sin6_b = (struct sockaddr_in6 *)b;
250 if (!memcmp(&sin6_a->sin6_addr, &sin6_b->sin6_addr,
251 sizeof(struct in6_addr)))
252 return 1;
253 break;
254 }
255 return 0;
256}
257
Harald Welteba6988b2011-08-17 12:46:48 +0200258/*! \brief Determine if the given address is a local address
259 * \param[in] addr Socket Address
260 * \param[in] addrlen Length of socket address in bytes
261 * \returns 1 if address is local, 0 otherwise.
262 */
Harald Weltebc32d052012-04-08 11:31:32 +0200263int osmo_sockaddr_is_local(struct sockaddr *addr, unsigned int addrlen)
Harald Welte33cb71a2011-05-21 18:54:32 +0200264{
265 struct ifaddrs *ifaddr, *ifa;
266
267 if (getifaddrs(&ifaddr) == -1) {
268 perror("getifaddrs");
269 return -EIO;
270 }
271
272 for (ifa = ifaddr; ifa != NULL; ifa = ifa->ifa_next) {
Harald Welte4d3a7b12011-05-24 21:31:53 +0200273 if (!ifa->ifa_addr)
274 continue;
Harald Welte33cb71a2011-05-21 18:54:32 +0200275 if (sockaddr_equal(ifa->ifa_addr, addr, addrlen))
276 return 1;
277 }
278
279 return 0;
280}
Harald Weltee4764422011-05-22 12:25:57 +0200281
Álvaro Neira Ayuso5ade61a2014-03-24 13:02:00 +0100282/*! \brief Initialize a unix domain socket (including bind/connect)
283 * \param[in] type Socket type like SOCK_DGRAM, SOCK_STREAM
284 * \param[in] proto Protocol like IPPROTO_TCP, IPPROTO_UDP
285 * \param[in] socket_path path to identify the socket
286 * \param[in] flags flags like \ref OSMO_SOCK_F_CONNECT
287 *
288 * This function creates a new unix domain socket, \a
289 * type and \a proto and optionally binds or connects it, depending on
290 * the value of \a flags parameter.
291 */
292int osmo_sock_unix_init(uint16_t type, uint8_t proto,
293 const char *socket_path, unsigned int flags)
294{
295 struct sockaddr_un local;
296 int sfd, rc, on = 1;
297 unsigned int namelen;
298
299 if ((flags & (OSMO_SOCK_F_BIND | OSMO_SOCK_F_CONNECT)) ==
300 (OSMO_SOCK_F_BIND | OSMO_SOCK_F_CONNECT))
301 return -EINVAL;
302
303 local.sun_family = AF_UNIX;
304 strncpy(local.sun_path, socket_path, sizeof(local.sun_path));
305 local.sun_path[sizeof(local.sun_path) - 1] = '\0';
306
307#if defined(BSD44SOCKETS) || defined(__UNIXWARE__)
308 local.sun_len = strlen(local.sun_path);
309#endif
310#if defined(BSD44SOCKETS) || defined(SUN_LEN)
311 namelen = SUN_LEN(&local);
312#else
313 namelen = strlen(local.sun_path) +
314 offsetof(struct sockaddr_un, sun_path);
315#endif
316
317 sfd = socket(AF_UNIX, type, proto);
318 if (sfd < 0)
319 return -1;
320
321 if (flags & OSMO_SOCK_F_CONNECT) {
322 rc = connect(sfd, (struct sockaddr *)&local, namelen);
323 if (rc < 0)
324 goto err;
325 } else {
326 unlink(local.sun_path);
327 rc = bind(sfd, (struct sockaddr *)&local, namelen);
328 if (rc < 0)
329 goto err;
330 }
331
332 if (flags & OSMO_SOCK_F_NONBLOCK) {
333 if (ioctl(sfd, FIONBIO, (unsigned char *)&on) < 0) {
334 perror("cannot set this socket unblocking");
335 close(sfd);
336 return -EINVAL;
337 }
338 }
339
340 if (flags & OSMO_SOCK_F_BIND) {
341 rc = listen(sfd, 10);
342 if (rc < 0)
343 goto err;
344 }
345
346 return sfd;
347err:
348 close(sfd);
349 return -1;
350}
351
352/*! \brief Initialize a unix domain socket and fill \ref osmo_fd
353 * \param[out] ofd file descriptor (will be filled in)
354 * \param[in] type Socket type like SOCK_DGRAM, SOCK_STREAM
355 * \param[in] proto Protocol like IPPROTO_TCP, IPPROTO_UDP
356 * \param[in] socket_path path to identify the socket
357 * \param[in] flags flags like \ref OSMO_SOCK_F_CONNECT
358 *
359 * This function creates (and optionall binds/connects) a socket using
360 * \ref osmo_sock_unix_init, but also fills the \a ofd structure.
361 */
362int osmo_sock_unix_init_ofd(struct osmo_fd *ofd, uint16_t type, uint8_t proto,
363 const char *socket_path, unsigned int flags)
364{
365 int sfd, rc;
366
367 sfd = osmo_sock_unix_init(type, proto, socket_path, flags);
368 if (sfd < 0)
369 return sfd;
370
371 ofd->fd = sfd;
372 ofd->when = BSC_FD_READ;
373
374 rc = osmo_fd_register(ofd);
375 if (rc < 0) {
376 close(sfd);
377 return rc;
378 }
379
380 return sfd;
381}
382
Harald Weltee4764422011-05-22 12:25:57 +0200383#endif /* HAVE_SYS_SOCKET_H */
Harald Welteba6988b2011-08-17 12:46:48 +0200384
Sylvain Munautdca7d2c2012-04-18 21:53:23 +0200385/*! @} */