blob: 567939b7329f1d5732275b1409f6f4b3daa1c1b3 [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
Max862ba652014-10-13 14:54:25 +0200153/*! \brief fill \ref osmo_fd for a give sfd
154 * \param[out] ofd file descriptor (will be filled in)
155 * \param[in] sfd socket file descriptor
156 *
157 * This function fills the \a ofd structure.
158 */
159static inline int osmo_fd_init_ofd(struct osmo_fd *ofd, int sfd)
160{
161 int rc;
162
163 if (sfd < 0)
164 return sfd;
165
166 ofd->fd = sfd;
167 ofd->when = BSC_FD_READ;
168
169 rc = osmo_fd_register(ofd);
170 if (rc < 0) {
171 close(sfd);
172 return rc;
173 }
174
175 return sfd;
176}
177
Harald Welteba6988b2011-08-17 12:46:48 +0200178/*! \brief Initialize a socket and fill \ref osmo_fd
Katerina Barone-Adesic28c6a02013-02-15 13:27:59 +0100179 * \param[out] ofd file descriptor (will be filled in)
Harald Welteba6988b2011-08-17 12:46:48 +0200180 * \param[in] family Address Family like AF_INET, AF_INET6, AF_UNSPEC
181 * \param[in] type Socket type like SOCK_DGRAM, SOCK_STREAM
182 * \param[in] proto Protocol like IPPROTO_TCP, IPPROTO_UDP
183 * \param[in] host remote host name or IP address in string form
184 * \param[in] port remote port number in host byte order
185 * \param[in] flags flags like \ref OSMO_SOCK_F_CONNECT
186 *
187 * This function creates (and optionall binds/connects) a socket using
188 * \ref osmo_sock_init, but also fills the \a ofd structure.
189 */
Harald Welte68b15742011-05-22 21:47:29 +0200190int osmo_sock_init_ofd(struct osmo_fd *ofd, int family, int type, int proto,
Pablo Neira Ayuso0849c9a2011-06-09 15:04:30 +0200191 const char *host, uint16_t port, unsigned int flags)
Harald Welte68b15742011-05-22 21:47:29 +0200192{
Max862ba652014-10-13 14:54:25 +0200193 return osmo_fd_init_ofd(ofd, osmo_sock_init(family, type, proto, host, port, flags));
Harald Welte33cb71a2011-05-21 18:54:32 +0200194}
195
Harald Welteba6988b2011-08-17 12:46:48 +0200196/*! \brief Initialize a socket and fill \ref sockaddr
197 * \param[out] ss socket address (will be filled in)
198 * \param[in] type Socket type like SOCK_DGRAM, SOCK_STREAM
199 * \param[in] proto Protocol like IPPROTO_TCP, IPPROTO_UDP
200 * \param[in] flags flags like \ref OSMO_SOCK_F_CONNECT
201 *
202 * This function creates (and optionall binds/connects) a socket using
203 * \ref osmo_sock_init, but also fills the \a ss structure.
204 */
Harald Welte33cb71a2011-05-21 18:54:32 +0200205int osmo_sock_init_sa(struct sockaddr *ss, uint16_t type,
Pablo Neira Ayuso0849c9a2011-06-09 15:04:30 +0200206 uint8_t proto, unsigned int flags)
Harald Welte33cb71a2011-05-21 18:54:32 +0200207{
208 char host[NI_MAXHOST];
209 uint16_t port;
210 struct sockaddr_in *sin;
211 struct sockaddr_in6 *sin6;
212 int s, sa_len;
213
214 /* determine port and host from ss */
215 switch (ss->sa_family) {
216 case AF_INET:
217 sin = (struct sockaddr_in *) ss;
218 sa_len = sizeof(struct sockaddr_in);
219 port = ntohs(sin->sin_port);
220 break;
221 case AF_INET6:
222 sin6 = (struct sockaddr_in6 *) ss;
223 sa_len = sizeof(struct sockaddr_in6);
224 port = ntohs(sin6->sin6_port);
225 break;
226 default:
227 return -EINVAL;
228 }
Harald Welte33cb71a2011-05-21 18:54:32 +0200229
230 s = getnameinfo(ss, sa_len, host, NI_MAXHOST,
231 NULL, 0, NI_NUMERICHOST);
232 if (s != 0) {
233 perror("getnameinfo failed");
234 return s;
235 }
236
Pablo Neira Ayuso0849c9a2011-06-09 15:04:30 +0200237 return osmo_sock_init(ss->sa_family, type, proto, host, port, flags);
Harald Welte33cb71a2011-05-21 18:54:32 +0200238}
239
240static int sockaddr_equal(const struct sockaddr *a,
Harald Weltee4764422011-05-22 12:25:57 +0200241 const struct sockaddr *b, unsigned int len)
Harald Welte33cb71a2011-05-21 18:54:32 +0200242{
243 struct sockaddr_in *sin_a, *sin_b;
244 struct sockaddr_in6 *sin6_a, *sin6_b;
245
246 if (a->sa_family != b->sa_family)
247 return 0;
248
249 switch (a->sa_family) {
250 case AF_INET:
251 sin_a = (struct sockaddr_in *)a;
252 sin_b = (struct sockaddr_in *)b;
253 if (!memcmp(&sin_a->sin_addr, &sin_b->sin_addr,
254 sizeof(struct in_addr)))
255 return 1;
256 break;
257 case AF_INET6:
258 sin6_a = (struct sockaddr_in6 *)a;
259 sin6_b = (struct sockaddr_in6 *)b;
260 if (!memcmp(&sin6_a->sin6_addr, &sin6_b->sin6_addr,
261 sizeof(struct in6_addr)))
262 return 1;
263 break;
264 }
265 return 0;
266}
267
Harald Welteba6988b2011-08-17 12:46:48 +0200268/*! \brief Determine if the given address is a local address
269 * \param[in] addr Socket Address
270 * \param[in] addrlen Length of socket address in bytes
271 * \returns 1 if address is local, 0 otherwise.
272 */
Harald Weltebc32d052012-04-08 11:31:32 +0200273int osmo_sockaddr_is_local(struct sockaddr *addr, unsigned int addrlen)
Harald Welte33cb71a2011-05-21 18:54:32 +0200274{
275 struct ifaddrs *ifaddr, *ifa;
276
277 if (getifaddrs(&ifaddr) == -1) {
278 perror("getifaddrs");
279 return -EIO;
280 }
281
282 for (ifa = ifaddr; ifa != NULL; ifa = ifa->ifa_next) {
Harald Welte4d3a7b12011-05-24 21:31:53 +0200283 if (!ifa->ifa_addr)
284 continue;
Harald Welte33cb71a2011-05-21 18:54:32 +0200285 if (sockaddr_equal(ifa->ifa_addr, addr, addrlen))
286 return 1;
287 }
288
289 return 0;
290}
Harald Weltee4764422011-05-22 12:25:57 +0200291
Álvaro Neira Ayuso5ade61a2014-03-24 13:02:00 +0100292/*! \brief Initialize a unix domain socket (including bind/connect)
293 * \param[in] type Socket type like SOCK_DGRAM, SOCK_STREAM
294 * \param[in] proto Protocol like IPPROTO_TCP, IPPROTO_UDP
295 * \param[in] socket_path path to identify the socket
296 * \param[in] flags flags like \ref OSMO_SOCK_F_CONNECT
297 *
298 * This function creates a new unix domain socket, \a
299 * type and \a proto and optionally binds or connects it, depending on
300 * the value of \a flags parameter.
301 */
302int osmo_sock_unix_init(uint16_t type, uint8_t proto,
303 const char *socket_path, unsigned int flags)
304{
305 struct sockaddr_un local;
306 int sfd, rc, on = 1;
307 unsigned int namelen;
308
309 if ((flags & (OSMO_SOCK_F_BIND | OSMO_SOCK_F_CONNECT)) ==
310 (OSMO_SOCK_F_BIND | OSMO_SOCK_F_CONNECT))
311 return -EINVAL;
312
313 local.sun_family = AF_UNIX;
314 strncpy(local.sun_path, socket_path, sizeof(local.sun_path));
315 local.sun_path[sizeof(local.sun_path) - 1] = '\0';
316
317#if defined(BSD44SOCKETS) || defined(__UNIXWARE__)
318 local.sun_len = strlen(local.sun_path);
319#endif
320#if defined(BSD44SOCKETS) || defined(SUN_LEN)
321 namelen = SUN_LEN(&local);
322#else
323 namelen = strlen(local.sun_path) +
324 offsetof(struct sockaddr_un, sun_path);
325#endif
326
327 sfd = socket(AF_UNIX, type, proto);
328 if (sfd < 0)
329 return -1;
330
331 if (flags & OSMO_SOCK_F_CONNECT) {
332 rc = connect(sfd, (struct sockaddr *)&local, namelen);
333 if (rc < 0)
334 goto err;
335 } else {
336 unlink(local.sun_path);
337 rc = bind(sfd, (struct sockaddr *)&local, namelen);
338 if (rc < 0)
339 goto err;
340 }
341
342 if (flags & OSMO_SOCK_F_NONBLOCK) {
343 if (ioctl(sfd, FIONBIO, (unsigned char *)&on) < 0) {
344 perror("cannot set this socket unblocking");
345 close(sfd);
346 return -EINVAL;
347 }
348 }
349
350 if (flags & OSMO_SOCK_F_BIND) {
351 rc = listen(sfd, 10);
352 if (rc < 0)
353 goto err;
354 }
355
356 return sfd;
357err:
358 close(sfd);
359 return -1;
360}
361
362/*! \brief Initialize a unix domain socket and fill \ref osmo_fd
363 * \param[out] ofd file descriptor (will be filled in)
364 * \param[in] type Socket type like SOCK_DGRAM, SOCK_STREAM
365 * \param[in] proto Protocol like IPPROTO_TCP, IPPROTO_UDP
366 * \param[in] socket_path path to identify the socket
367 * \param[in] flags flags like \ref OSMO_SOCK_F_CONNECT
368 *
369 * This function creates (and optionall binds/connects) a socket using
370 * \ref osmo_sock_unix_init, but also fills the \a ofd structure.
371 */
372int osmo_sock_unix_init_ofd(struct osmo_fd *ofd, uint16_t type, uint8_t proto,
373 const char *socket_path, unsigned int flags)
374{
Max862ba652014-10-13 14:54:25 +0200375 return osmo_fd_init_ofd(ofd, osmo_sock_unix_init(type, proto, socket_path, flags));
Álvaro Neira Ayuso5ade61a2014-03-24 13:02:00 +0100376}
377
Harald Weltee4764422011-05-22 12:25:57 +0200378#endif /* HAVE_SYS_SOCKET_H */
Harald Welteba6988b2011-08-17 12:46:48 +0200379
Sylvain Munautdca7d2c2012-04-18 21:53:23 +0200380/*! @} */