blob: 6ff00f0d063c0681a3c4c6060972b3240dd56c2d [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>
Harald Welte33cb71a2011-05-21 18:54:32 +020020
Holger Hans Peter Freyther47723482011-11-09 11:26:15 +010021#include <netinet/in.h>
22
Harald Welte33cb71a2011-05-21 18:54:32 +020023#include <stdio.h>
24#include <unistd.h>
25#include <stdint.h>
26#include <string.h>
27#include <errno.h>
28#include <netdb.h>
29#include <ifaddrs.h>
30
Harald Welteba6988b2011-08-17 12:46:48 +020031/*! \brief Initialize a socket (including bind/connect)
32 * \param[in] family Address Family like AF_INET, AF_INET6, AF_UNSPEC
33 * \param[in] type Socket type like SOCK_DGRAM, SOCK_STREAM
34 * \param[in] proto Protocol like IPPROTO_TCP, IPPROTO_UDP
35 * \param[in] host remote host name or IP address in string form
36 * \param[in] port remote port number in host byte order
37 * \param[in] flags flags like \ref OSMO_SOCK_F_CONNECT
38 *
39 * This function creates a new socket of the designated \a family, \a
40 * type and \a proto and optionally binds or connects it, depending on
41 * the value of \a flags parameter.
42 */
Harald Welte33cb71a2011-05-21 18:54:32 +020043int osmo_sock_init(uint16_t family, uint16_t type, uint8_t proto,
Pablo Neira Ayuso0849c9a2011-06-09 15:04:30 +020044 const char *host, uint16_t port, unsigned int flags)
Harald Welte33cb71a2011-05-21 18:54:32 +020045{
46 struct addrinfo hints, *result, *rp;
Harald Welte68b15742011-05-22 21:47:29 +020047 int sfd, rc, on = 1;
Harald Welte33cb71a2011-05-21 18:54:32 +020048 char portbuf[16];
49
Pablo Neira Ayuso0849c9a2011-06-09 15:04:30 +020050 if ((flags & (OSMO_SOCK_F_BIND | OSMO_SOCK_F_CONNECT)) ==
51 (OSMO_SOCK_F_BIND | OSMO_SOCK_F_CONNECT))
52 return -EINVAL;
53
Harald Welte33cb71a2011-05-21 18:54:32 +020054 sprintf(portbuf, "%u", port);
55 memset(&hints, 0, sizeof(struct addrinfo));
56 hints.ai_family = family;
Pablo Neira Ayusoe04a14d2013-01-14 00:12:28 +010057 if (type == SOCK_RAW) {
58 /* Workaround for glibc, that returns EAI_SERVICE (-8) if
59 * SOCK_RAW and IPPROTO_GRE is used.
60 */
61 hints.ai_socktype = SOCK_DGRAM;
62 hints.ai_protocol = IPPROTO_UDP;
63 } else {
64 hints.ai_socktype = type;
65 hints.ai_protocol = proto;
66 }
Harald Welte33cb71a2011-05-21 18:54:32 +020067
Pablo Neira Ayuso0849c9a2011-06-09 15:04:30 +020068 if (flags & OSMO_SOCK_F_BIND)
Harald Weltef9e07462011-05-31 17:47:54 +020069 hints.ai_flags |= AI_PASSIVE;
70
Harald Welte33cb71a2011-05-21 18:54:32 +020071 rc = getaddrinfo(host, portbuf, &hints, &result);
72 if (rc != 0) {
73 perror("getaddrinfo returned NULL");
74 return -EINVAL;
75 }
76
77 for (rp = result; rp != NULL; rp = rp->ai_next) {
Pablo Neira Ayusoe04a14d2013-01-14 00:12:28 +010078 /* Workaround for glibc again */
79 if (type == SOCK_RAW) {
80 rp->ai_socktype = SOCK_RAW;
81 rp->ai_protocol = proto;
82 }
83
Harald Welte33cb71a2011-05-21 18:54:32 +020084 sfd = socket(rp->ai_family, rp->ai_socktype, rp->ai_protocol);
85 if (sfd == -1)
86 continue;
Pablo Neira Ayuso0849c9a2011-06-09 15:04:30 +020087 if (flags & OSMO_SOCK_F_NONBLOCK) {
88 if (ioctl(sfd, FIONBIO, (unsigned char *)&on) < 0) {
89 perror("cannot set this socket unblocking");
90 close(sfd);
91 return -EINVAL;
92 }
93 }
94 if (flags & OSMO_SOCK_F_CONNECT) {
95 rc = connect(sfd, rp->ai_addr, rp->ai_addrlen);
96 if (rc != -1 || (rc == -1 && errno == EINPROGRESS))
Harald Welte33cb71a2011-05-21 18:54:32 +020097 break;
98 } else {
Pablo Neira Ayuso0849c9a2011-06-09 15:04:30 +020099 rc = setsockopt(sfd, SOL_SOCKET, SO_REUSEADDR,
100 &on, sizeof(on));
101 if (rc < 0) {
102 perror("cannot setsockopt socket");
103 break;
104 }
Harald Welte33cb71a2011-05-21 18:54:32 +0200105 if (bind(sfd, rp->ai_addr, rp->ai_addrlen) != -1)
106 break;
107 }
108 close(sfd);
109 }
110 freeaddrinfo(result);
111
112 if (rp == NULL) {
113 perror("unable to connect/bind socket");
114 return -ENODEV;
115 }
Harald Welte68b15742011-05-22 21:47:29 +0200116
117 setsockopt(sfd, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on));
118
119 /* Make sure to call 'listen' on a bound, connection-oriented sock */
Pablo Neira Ayuso0849c9a2011-06-09 15:04:30 +0200120 if (flags & OSMO_SOCK_F_BIND) {
Harald Welte68b15742011-05-22 21:47:29 +0200121 switch (type) {
122 case SOCK_STREAM:
123 case SOCK_SEQPACKET:
124 listen(sfd, 10);
125 break;
126 }
127 }
128 return sfd;
129}
130
Harald Welteba6988b2011-08-17 12:46:48 +0200131/*! \brief Initialize a socket and fill \ref osmo_fd
Katerina Barone-Adesic28c6a02013-02-15 13:27:59 +0100132 * \param[out] ofd file descriptor (will be filled in)
Harald Welteba6988b2011-08-17 12:46:48 +0200133 * \param[in] family Address Family like AF_INET, AF_INET6, AF_UNSPEC
134 * \param[in] type Socket type like SOCK_DGRAM, SOCK_STREAM
135 * \param[in] proto Protocol like IPPROTO_TCP, IPPROTO_UDP
136 * \param[in] host remote host name or IP address in string form
137 * \param[in] port remote port number in host byte order
138 * \param[in] flags flags like \ref OSMO_SOCK_F_CONNECT
139 *
140 * This function creates (and optionall binds/connects) a socket using
141 * \ref osmo_sock_init, but also fills the \a ofd structure.
142 */
Harald Welte68b15742011-05-22 21:47:29 +0200143int osmo_sock_init_ofd(struct osmo_fd *ofd, int family, int type, int proto,
Pablo Neira Ayuso0849c9a2011-06-09 15:04:30 +0200144 const char *host, uint16_t port, unsigned int flags)
Harald Welte68b15742011-05-22 21:47:29 +0200145{
146 int sfd, rc;
147
Pablo Neira Ayuso0849c9a2011-06-09 15:04:30 +0200148 sfd = osmo_sock_init(family, type, proto, host, port, flags);
Harald Welte68b15742011-05-22 21:47:29 +0200149 if (sfd < 0)
150 return sfd;
151
152 ofd->fd = sfd;
153 ofd->when = BSC_FD_READ;
154
155 rc = osmo_fd_register(ofd);
156 if (rc < 0) {
157 close(sfd);
158 return rc;
159 }
160
Harald Welte33cb71a2011-05-21 18:54:32 +0200161 return sfd;
162}
163
Harald Welteba6988b2011-08-17 12:46:48 +0200164/*! \brief Initialize a socket and fill \ref sockaddr
165 * \param[out] ss socket address (will be filled in)
166 * \param[in] type Socket type like SOCK_DGRAM, SOCK_STREAM
167 * \param[in] proto Protocol like IPPROTO_TCP, IPPROTO_UDP
168 * \param[in] flags flags like \ref OSMO_SOCK_F_CONNECT
169 *
170 * This function creates (and optionall binds/connects) a socket using
171 * \ref osmo_sock_init, but also fills the \a ss structure.
172 */
Harald Welte33cb71a2011-05-21 18:54:32 +0200173int osmo_sock_init_sa(struct sockaddr *ss, uint16_t type,
Pablo Neira Ayuso0849c9a2011-06-09 15:04:30 +0200174 uint8_t proto, unsigned int flags)
Harald Welte33cb71a2011-05-21 18:54:32 +0200175{
176 char host[NI_MAXHOST];
177 uint16_t port;
178 struct sockaddr_in *sin;
179 struct sockaddr_in6 *sin6;
180 int s, sa_len;
181
182 /* determine port and host from ss */
183 switch (ss->sa_family) {
184 case AF_INET:
185 sin = (struct sockaddr_in *) ss;
186 sa_len = sizeof(struct sockaddr_in);
187 port = ntohs(sin->sin_port);
188 break;
189 case AF_INET6:
190 sin6 = (struct sockaddr_in6 *) ss;
191 sa_len = sizeof(struct sockaddr_in6);
192 port = ntohs(sin6->sin6_port);
193 break;
194 default:
195 return -EINVAL;
196 }
Harald Welte33cb71a2011-05-21 18:54:32 +0200197
198 s = getnameinfo(ss, sa_len, host, NI_MAXHOST,
199 NULL, 0, NI_NUMERICHOST);
200 if (s != 0) {
201 perror("getnameinfo failed");
202 return s;
203 }
204
Pablo Neira Ayuso0849c9a2011-06-09 15:04:30 +0200205 return osmo_sock_init(ss->sa_family, type, proto, host, port, flags);
Harald Welte33cb71a2011-05-21 18:54:32 +0200206}
207
208static int sockaddr_equal(const struct sockaddr *a,
Harald Weltee4764422011-05-22 12:25:57 +0200209 const struct sockaddr *b, unsigned int len)
Harald Welte33cb71a2011-05-21 18:54:32 +0200210{
211 struct sockaddr_in *sin_a, *sin_b;
212 struct sockaddr_in6 *sin6_a, *sin6_b;
213
214 if (a->sa_family != b->sa_family)
215 return 0;
216
217 switch (a->sa_family) {
218 case AF_INET:
219 sin_a = (struct sockaddr_in *)a;
220 sin_b = (struct sockaddr_in *)b;
221 if (!memcmp(&sin_a->sin_addr, &sin_b->sin_addr,
222 sizeof(struct in_addr)))
223 return 1;
224 break;
225 case AF_INET6:
226 sin6_a = (struct sockaddr_in6 *)a;
227 sin6_b = (struct sockaddr_in6 *)b;
228 if (!memcmp(&sin6_a->sin6_addr, &sin6_b->sin6_addr,
229 sizeof(struct in6_addr)))
230 return 1;
231 break;
232 }
233 return 0;
234}
235
Harald Welteba6988b2011-08-17 12:46:48 +0200236/*! \brief Determine if the given address is a local address
237 * \param[in] addr Socket Address
238 * \param[in] addrlen Length of socket address in bytes
239 * \returns 1 if address is local, 0 otherwise.
240 */
Harald Weltebc32d052012-04-08 11:31:32 +0200241int osmo_sockaddr_is_local(struct sockaddr *addr, unsigned int addrlen)
Harald Welte33cb71a2011-05-21 18:54:32 +0200242{
243 struct ifaddrs *ifaddr, *ifa;
244
245 if (getifaddrs(&ifaddr) == -1) {
246 perror("getifaddrs");
247 return -EIO;
248 }
249
250 for (ifa = ifaddr; ifa != NULL; ifa = ifa->ifa_next) {
Harald Welte4d3a7b12011-05-24 21:31:53 +0200251 if (!ifa->ifa_addr)
252 continue;
Harald Welte33cb71a2011-05-21 18:54:32 +0200253 if (sockaddr_equal(ifa->ifa_addr, addr, addrlen))
254 return 1;
255 }
256
257 return 0;
258}
Harald Weltee4764422011-05-22 12:25:57 +0200259
260#endif /* HAVE_SYS_SOCKET_H */
Harald Welteba6988b2011-08-17 12:46:48 +0200261
Sylvain Munautdca7d2c2012-04-18 21:53:23 +0200262/*! @} */