blob: c93224af5813f3fd0a4baabcd3994c47de1fca2f [file] [log] [blame]
Harald Welte33cb71a2011-05-21 18:54:32 +02001#include "../config.h"
2
Harald Welteba6988b2011-08-17 12:46:48 +02003/*! \addtogroup socket
4 * @{
5 */
6
7/*! \file socket.c
8 * \brief Osmocom socket convenience functions
9 */
10
Harald Weltee4764422011-05-22 12:25:57 +020011#ifdef HAVE_SYS_SOCKET_H
12
Harald Welte33cb71a2011-05-21 18:54:32 +020013#include <osmocom/core/logging.h>
14#include <osmocom/core/select.h>
15#include <osmocom/core/socket.h>
16
Pablo Neira Ayuso0849c9a2011-06-09 15:04:30 +020017#include <sys/ioctl.h>
Harald Welte33cb71a2011-05-21 18:54:32 +020018#include <sys/socket.h>
19#include <sys/types.h>
Álvaro Neira Ayuso5ade61a2014-03-24 13:02:00 +010020#include <sys/un.h>
Harald Welte33cb71a2011-05-21 18:54:32 +020021
Holger Hans Peter Freyther47723482011-11-09 11:26:15 +010022#include <netinet/in.h>
23
Harald Welte33cb71a2011-05-21 18:54:32 +020024#include <stdio.h>
25#include <unistd.h>
26#include <stdint.h>
27#include <string.h>
28#include <errno.h>
29#include <netdb.h>
30#include <ifaddrs.h>
31
Harald Welteba6988b2011-08-17 12:46:48 +020032/*! \brief Initialize a socket (including bind/connect)
33 * \param[in] family Address Family like AF_INET, AF_INET6, AF_UNSPEC
34 * \param[in] type Socket type like SOCK_DGRAM, SOCK_STREAM
35 * \param[in] proto Protocol like IPPROTO_TCP, IPPROTO_UDP
36 * \param[in] host remote host name or IP address in string form
37 * \param[in] port remote port number in host byte order
38 * \param[in] flags flags like \ref OSMO_SOCK_F_CONNECT
39 *
40 * This function creates a new socket of the designated \a family, \a
41 * type and \a proto and optionally binds or connects it, depending on
42 * the value of \a flags parameter.
43 */
Harald Welte33cb71a2011-05-21 18:54:32 +020044int osmo_sock_init(uint16_t family, uint16_t type, uint8_t proto,
Pablo Neira Ayuso0849c9a2011-06-09 15:04:30 +020045 const char *host, uint16_t port, unsigned int flags)
Harald Welte33cb71a2011-05-21 18:54:32 +020046{
47 struct addrinfo hints, *result, *rp;
Harald Welte68b15742011-05-22 21:47:29 +020048 int sfd, rc, on = 1;
Harald Welte33cb71a2011-05-21 18:54:32 +020049 char portbuf[16];
50
Pablo Neira Ayuso0849c9a2011-06-09 15:04:30 +020051 if ((flags & (OSMO_SOCK_F_BIND | OSMO_SOCK_F_CONNECT)) ==
52 (OSMO_SOCK_F_BIND | OSMO_SOCK_F_CONNECT))
53 return -EINVAL;
54
Harald Welte33cb71a2011-05-21 18:54:32 +020055 sprintf(portbuf, "%u", port);
56 memset(&hints, 0, sizeof(struct addrinfo));
57 hints.ai_family = family;
Pablo Neira Ayusoe04a14d2013-01-14 00:12:28 +010058 if (type == SOCK_RAW) {
59 /* Workaround for glibc, that returns EAI_SERVICE (-8) if
60 * SOCK_RAW and IPPROTO_GRE is used.
61 */
62 hints.ai_socktype = SOCK_DGRAM;
63 hints.ai_protocol = IPPROTO_UDP;
64 } else {
65 hints.ai_socktype = type;
66 hints.ai_protocol = proto;
67 }
Harald Welte33cb71a2011-05-21 18:54:32 +020068
Pablo Neira Ayuso0849c9a2011-06-09 15:04:30 +020069 if (flags & OSMO_SOCK_F_BIND)
Harald Weltef9e07462011-05-31 17:47:54 +020070 hints.ai_flags |= AI_PASSIVE;
71
Harald Welte33cb71a2011-05-21 18:54:32 +020072 rc = getaddrinfo(host, portbuf, &hints, &result);
73 if (rc != 0) {
74 perror("getaddrinfo returned NULL");
75 return -EINVAL;
76 }
77
78 for (rp = result; rp != NULL; rp = rp->ai_next) {
Pablo Neira Ayusoe04a14d2013-01-14 00:12:28 +010079 /* Workaround for glibc again */
80 if (type == SOCK_RAW) {
81 rp->ai_socktype = SOCK_RAW;
82 rp->ai_protocol = proto;
83 }
84
Harald Welte33cb71a2011-05-21 18:54:32 +020085 sfd = socket(rp->ai_family, rp->ai_socktype, rp->ai_protocol);
86 if (sfd == -1)
87 continue;
Pablo Neira Ayuso0849c9a2011-06-09 15:04:30 +020088 if (flags & OSMO_SOCK_F_NONBLOCK) {
89 if (ioctl(sfd, FIONBIO, (unsigned char *)&on) < 0) {
90 perror("cannot set this socket unblocking");
91 close(sfd);
92 return -EINVAL;
93 }
94 }
95 if (flags & OSMO_SOCK_F_CONNECT) {
96 rc = connect(sfd, rp->ai_addr, rp->ai_addrlen);
97 if (rc != -1 || (rc == -1 && errno == EINPROGRESS))
Harald Welte33cb71a2011-05-21 18:54:32 +020098 break;
99 } else {
Pablo Neira Ayuso0849c9a2011-06-09 15:04:30 +0200100 rc = setsockopt(sfd, SOL_SOCKET, SO_REUSEADDR,
101 &on, sizeof(on));
102 if (rc < 0) {
103 perror("cannot setsockopt socket");
104 break;
105 }
Harald Welte33cb71a2011-05-21 18:54:32 +0200106 if (bind(sfd, rp->ai_addr, rp->ai_addrlen) != -1)
107 break;
108 }
109 close(sfd);
110 }
111 freeaddrinfo(result);
112
113 if (rp == NULL) {
114 perror("unable to connect/bind socket");
115 return -ENODEV;
116 }
Harald Welte68b15742011-05-22 21:47:29 +0200117
118 setsockopt(sfd, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on));
119
120 /* Make sure to call 'listen' on a bound, connection-oriented sock */
Pablo Neira Ayuso0849c9a2011-06-09 15:04:30 +0200121 if (flags & OSMO_SOCK_F_BIND) {
Harald Welte68b15742011-05-22 21:47:29 +0200122 switch (type) {
123 case SOCK_STREAM:
124 case SOCK_SEQPACKET:
125 listen(sfd, 10);
126 break;
127 }
128 }
129 return sfd;
130}
131
Harald Welteba6988b2011-08-17 12:46:48 +0200132/*! \brief Initialize a socket and fill \ref osmo_fd
Katerina Barone-Adesic28c6a02013-02-15 13:27:59 +0100133 * \param[out] ofd file descriptor (will be filled in)
Harald Welteba6988b2011-08-17 12:46:48 +0200134 * \param[in] family Address Family like AF_INET, AF_INET6, AF_UNSPEC
135 * \param[in] type Socket type like SOCK_DGRAM, SOCK_STREAM
136 * \param[in] proto Protocol like IPPROTO_TCP, IPPROTO_UDP
137 * \param[in] host remote host name or IP address in string form
138 * \param[in] port remote port number in host byte order
139 * \param[in] flags flags like \ref OSMO_SOCK_F_CONNECT
140 *
141 * This function creates (and optionall binds/connects) a socket using
142 * \ref osmo_sock_init, but also fills the \a ofd structure.
143 */
Harald Welte68b15742011-05-22 21:47:29 +0200144int osmo_sock_init_ofd(struct osmo_fd *ofd, int family, int type, int proto,
Pablo Neira Ayuso0849c9a2011-06-09 15:04:30 +0200145 const char *host, uint16_t port, unsigned int flags)
Harald Welte68b15742011-05-22 21:47:29 +0200146{
147 int sfd, rc;
148
Pablo Neira Ayuso0849c9a2011-06-09 15:04:30 +0200149 sfd = osmo_sock_init(family, type, proto, host, port, flags);
Harald Welte68b15742011-05-22 21:47:29 +0200150 if (sfd < 0)
151 return sfd;
152
153 ofd->fd = sfd;
154 ofd->when = BSC_FD_READ;
155
156 rc = osmo_fd_register(ofd);
157 if (rc < 0) {
158 close(sfd);
159 return rc;
160 }
161
Harald Welte33cb71a2011-05-21 18:54:32 +0200162 return sfd;
163}
164
Harald Welteba6988b2011-08-17 12:46:48 +0200165/*! \brief Initialize a socket and fill \ref sockaddr
166 * \param[out] ss socket address (will be filled in)
167 * \param[in] type Socket type like SOCK_DGRAM, SOCK_STREAM
168 * \param[in] proto Protocol like IPPROTO_TCP, IPPROTO_UDP
169 * \param[in] flags flags like \ref OSMO_SOCK_F_CONNECT
170 *
171 * This function creates (and optionall binds/connects) a socket using
172 * \ref osmo_sock_init, but also fills the \a ss structure.
173 */
Harald Welte33cb71a2011-05-21 18:54:32 +0200174int osmo_sock_init_sa(struct sockaddr *ss, uint16_t type,
Pablo Neira Ayuso0849c9a2011-06-09 15:04:30 +0200175 uint8_t proto, unsigned int flags)
Harald Welte33cb71a2011-05-21 18:54:32 +0200176{
177 char host[NI_MAXHOST];
178 uint16_t port;
179 struct sockaddr_in *sin;
180 struct sockaddr_in6 *sin6;
181 int s, sa_len;
182
183 /* determine port and host from ss */
184 switch (ss->sa_family) {
185 case AF_INET:
186 sin = (struct sockaddr_in *) ss;
187 sa_len = sizeof(struct sockaddr_in);
188 port = ntohs(sin->sin_port);
189 break;
190 case AF_INET6:
191 sin6 = (struct sockaddr_in6 *) ss;
192 sa_len = sizeof(struct sockaddr_in6);
193 port = ntohs(sin6->sin6_port);
194 break;
195 default:
196 return -EINVAL;
197 }
Harald Welte33cb71a2011-05-21 18:54:32 +0200198
199 s = getnameinfo(ss, sa_len, host, NI_MAXHOST,
200 NULL, 0, NI_NUMERICHOST);
201 if (s != 0) {
202 perror("getnameinfo failed");
203 return s;
204 }
205
Pablo Neira Ayuso0849c9a2011-06-09 15:04:30 +0200206 return osmo_sock_init(ss->sa_family, type, proto, host, port, flags);
Harald Welte33cb71a2011-05-21 18:54:32 +0200207}
208
209static int sockaddr_equal(const struct sockaddr *a,
Harald Weltee4764422011-05-22 12:25:57 +0200210 const struct sockaddr *b, unsigned int len)
Harald Welte33cb71a2011-05-21 18:54:32 +0200211{
212 struct sockaddr_in *sin_a, *sin_b;
213 struct sockaddr_in6 *sin6_a, *sin6_b;
214
215 if (a->sa_family != b->sa_family)
216 return 0;
217
218 switch (a->sa_family) {
219 case AF_INET:
220 sin_a = (struct sockaddr_in *)a;
221 sin_b = (struct sockaddr_in *)b;
222 if (!memcmp(&sin_a->sin_addr, &sin_b->sin_addr,
223 sizeof(struct in_addr)))
224 return 1;
225 break;
226 case AF_INET6:
227 sin6_a = (struct sockaddr_in6 *)a;
228 sin6_b = (struct sockaddr_in6 *)b;
229 if (!memcmp(&sin6_a->sin6_addr, &sin6_b->sin6_addr,
230 sizeof(struct in6_addr)))
231 return 1;
232 break;
233 }
234 return 0;
235}
236
Harald Welteba6988b2011-08-17 12:46:48 +0200237/*! \brief Determine if the given address is a local address
238 * \param[in] addr Socket Address
239 * \param[in] addrlen Length of socket address in bytes
240 * \returns 1 if address is local, 0 otherwise.
241 */
Harald Weltebc32d052012-04-08 11:31:32 +0200242int osmo_sockaddr_is_local(struct sockaddr *addr, unsigned int addrlen)
Harald Welte33cb71a2011-05-21 18:54:32 +0200243{
244 struct ifaddrs *ifaddr, *ifa;
245
246 if (getifaddrs(&ifaddr) == -1) {
247 perror("getifaddrs");
248 return -EIO;
249 }
250
251 for (ifa = ifaddr; ifa != NULL; ifa = ifa->ifa_next) {
Harald Welte4d3a7b12011-05-24 21:31:53 +0200252 if (!ifa->ifa_addr)
253 continue;
Harald Welte33cb71a2011-05-21 18:54:32 +0200254 if (sockaddr_equal(ifa->ifa_addr, addr, addrlen))
255 return 1;
256 }
257
258 return 0;
259}
Harald Weltee4764422011-05-22 12:25:57 +0200260
Álvaro Neira Ayuso5ade61a2014-03-24 13:02:00 +0100261/*! \brief Initialize a unix domain socket (including bind/connect)
262 * \param[in] type Socket type like SOCK_DGRAM, SOCK_STREAM
263 * \param[in] proto Protocol like IPPROTO_TCP, IPPROTO_UDP
264 * \param[in] socket_path path to identify the socket
265 * \param[in] flags flags like \ref OSMO_SOCK_F_CONNECT
266 *
267 * This function creates a new unix domain socket, \a
268 * type and \a proto and optionally binds or connects it, depending on
269 * the value of \a flags parameter.
270 */
271int osmo_sock_unix_init(uint16_t type, uint8_t proto,
272 const char *socket_path, unsigned int flags)
273{
274 struct sockaddr_un local;
275 int sfd, rc, on = 1;
276 unsigned int namelen;
277
278 if ((flags & (OSMO_SOCK_F_BIND | OSMO_SOCK_F_CONNECT)) ==
279 (OSMO_SOCK_F_BIND | OSMO_SOCK_F_CONNECT))
280 return -EINVAL;
281
282 local.sun_family = AF_UNIX;
283 strncpy(local.sun_path, socket_path, sizeof(local.sun_path));
284 local.sun_path[sizeof(local.sun_path) - 1] = '\0';
285
286#if defined(BSD44SOCKETS) || defined(__UNIXWARE__)
287 local.sun_len = strlen(local.sun_path);
288#endif
289#if defined(BSD44SOCKETS) || defined(SUN_LEN)
290 namelen = SUN_LEN(&local);
291#else
292 namelen = strlen(local.sun_path) +
293 offsetof(struct sockaddr_un, sun_path);
294#endif
295
296 sfd = socket(AF_UNIX, type, proto);
297 if (sfd < 0)
298 return -1;
299
300 if (flags & OSMO_SOCK_F_CONNECT) {
301 rc = connect(sfd, (struct sockaddr *)&local, namelen);
302 if (rc < 0)
303 goto err;
304 } else {
305 unlink(local.sun_path);
306 rc = bind(sfd, (struct sockaddr *)&local, namelen);
307 if (rc < 0)
308 goto err;
309 }
310
311 if (flags & OSMO_SOCK_F_NONBLOCK) {
312 if (ioctl(sfd, FIONBIO, (unsigned char *)&on) < 0) {
313 perror("cannot set this socket unblocking");
314 close(sfd);
315 return -EINVAL;
316 }
317 }
318
319 if (flags & OSMO_SOCK_F_BIND) {
320 rc = listen(sfd, 10);
321 if (rc < 0)
322 goto err;
323 }
324
325 return sfd;
326err:
327 close(sfd);
328 return -1;
329}
330
331/*! \brief Initialize a unix domain socket and fill \ref osmo_fd
332 * \param[out] ofd file descriptor (will be filled in)
333 * \param[in] type Socket type like SOCK_DGRAM, SOCK_STREAM
334 * \param[in] proto Protocol like IPPROTO_TCP, IPPROTO_UDP
335 * \param[in] socket_path path to identify the socket
336 * \param[in] flags flags like \ref OSMO_SOCK_F_CONNECT
337 *
338 * This function creates (and optionall binds/connects) a socket using
339 * \ref osmo_sock_unix_init, but also fills the \a ofd structure.
340 */
341int osmo_sock_unix_init_ofd(struct osmo_fd *ofd, uint16_t type, uint8_t proto,
342 const char *socket_path, unsigned int flags)
343{
344 int sfd, rc;
345
346 sfd = osmo_sock_unix_init(type, proto, socket_path, flags);
347 if (sfd < 0)
348 return sfd;
349
350 ofd->fd = sfd;
351 ofd->when = BSC_FD_READ;
352
353 rc = osmo_fd_register(ofd);
354 if (rc < 0) {
355 close(sfd);
356 return rc;
357 }
358
359 return sfd;
360}
361
Harald Weltee4764422011-05-22 12:25:57 +0200362#endif /* HAVE_SYS_SOCKET_H */
Harald Welteba6988b2011-08-17 12:46:48 +0200363
Sylvain Munautdca7d2c2012-04-18 21:53:23 +0200364/*! @} */