blob: 1a1d71dc5cf492193c790733182cc0b232a45072 [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
21#include <stdio.h>
22#include <unistd.h>
23#include <stdint.h>
24#include <string.h>
25#include <errno.h>
26#include <netdb.h>
27#include <ifaddrs.h>
28
Harald Welteba6988b2011-08-17 12:46:48 +020029/*! \brief Initialize a socket (including bind/connect)
30 * \param[in] family Address Family like AF_INET, AF_INET6, AF_UNSPEC
31 * \param[in] type Socket type like SOCK_DGRAM, SOCK_STREAM
32 * \param[in] proto Protocol like IPPROTO_TCP, IPPROTO_UDP
33 * \param[in] host remote host name or IP address in string form
34 * \param[in] port remote port number in host byte order
35 * \param[in] flags flags like \ref OSMO_SOCK_F_CONNECT
36 *
37 * This function creates a new socket of the designated \a family, \a
38 * type and \a proto and optionally binds or connects it, depending on
39 * the value of \a flags parameter.
40 */
Harald Welte33cb71a2011-05-21 18:54:32 +020041int osmo_sock_init(uint16_t family, uint16_t type, uint8_t proto,
Pablo Neira Ayuso0849c9a2011-06-09 15:04:30 +020042 const char *host, uint16_t port, unsigned int flags)
Harald Welte33cb71a2011-05-21 18:54:32 +020043{
44 struct addrinfo hints, *result, *rp;
Harald Welte68b15742011-05-22 21:47:29 +020045 int sfd, rc, on = 1;
Harald Welte33cb71a2011-05-21 18:54:32 +020046 char portbuf[16];
47
Pablo Neira Ayuso0849c9a2011-06-09 15:04:30 +020048 if ((flags & (OSMO_SOCK_F_BIND | OSMO_SOCK_F_CONNECT)) ==
49 (OSMO_SOCK_F_BIND | OSMO_SOCK_F_CONNECT))
50 return -EINVAL;
51
Harald Welte33cb71a2011-05-21 18:54:32 +020052 sprintf(portbuf, "%u", port);
53 memset(&hints, 0, sizeof(struct addrinfo));
54 hints.ai_family = family;
55 hints.ai_socktype = type;
56 hints.ai_flags = 0;
57 hints.ai_protocol = proto;
58
Pablo Neira Ayuso0849c9a2011-06-09 15:04:30 +020059 if (flags & OSMO_SOCK_F_BIND)
Harald Weltef9e07462011-05-31 17:47:54 +020060 hints.ai_flags |= AI_PASSIVE;
61
Harald Welte33cb71a2011-05-21 18:54:32 +020062 rc = getaddrinfo(host, portbuf, &hints, &result);
63 if (rc != 0) {
64 perror("getaddrinfo returned NULL");
65 return -EINVAL;
66 }
67
68 for (rp = result; rp != NULL; rp = rp->ai_next) {
69 sfd = socket(rp->ai_family, rp->ai_socktype, rp->ai_protocol);
70 if (sfd == -1)
71 continue;
Pablo Neira Ayuso0849c9a2011-06-09 15:04:30 +020072 if (flags & OSMO_SOCK_F_NONBLOCK) {
73 if (ioctl(sfd, FIONBIO, (unsigned char *)&on) < 0) {
74 perror("cannot set this socket unblocking");
75 close(sfd);
76 return -EINVAL;
77 }
78 }
79 if (flags & OSMO_SOCK_F_CONNECT) {
80 rc = connect(sfd, rp->ai_addr, rp->ai_addrlen);
81 if (rc != -1 || (rc == -1 && errno == EINPROGRESS))
Harald Welte33cb71a2011-05-21 18:54:32 +020082 break;
83 } else {
Pablo Neira Ayuso0849c9a2011-06-09 15:04:30 +020084 rc = setsockopt(sfd, SOL_SOCKET, SO_REUSEADDR,
85 &on, sizeof(on));
86 if (rc < 0) {
87 perror("cannot setsockopt socket");
88 break;
89 }
Harald Welte33cb71a2011-05-21 18:54:32 +020090 if (bind(sfd, rp->ai_addr, rp->ai_addrlen) != -1)
91 break;
92 }
93 close(sfd);
94 }
95 freeaddrinfo(result);
96
97 if (rp == NULL) {
98 perror("unable to connect/bind socket");
99 return -ENODEV;
100 }
Harald Welte68b15742011-05-22 21:47:29 +0200101
102 setsockopt(sfd, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on));
103
104 /* Make sure to call 'listen' on a bound, connection-oriented sock */
Pablo Neira Ayuso0849c9a2011-06-09 15:04:30 +0200105 if (flags & OSMO_SOCK_F_BIND) {
Harald Welte68b15742011-05-22 21:47:29 +0200106 switch (type) {
107 case SOCK_STREAM:
108 case SOCK_SEQPACKET:
109 listen(sfd, 10);
110 break;
111 }
112 }
113 return sfd;
114}
115
Harald Welteba6988b2011-08-17 12:46:48 +0200116/*! \brief Initialize a socket and fill \ref osmo_fd
117 * \param[out] osmocom file descriptor (will be filled in)
118 * \param[in] family Address Family like AF_INET, AF_INET6, AF_UNSPEC
119 * \param[in] type Socket type like SOCK_DGRAM, SOCK_STREAM
120 * \param[in] proto Protocol like IPPROTO_TCP, IPPROTO_UDP
121 * \param[in] host remote host name or IP address in string form
122 * \param[in] port remote port number in host byte order
123 * \param[in] flags flags like \ref OSMO_SOCK_F_CONNECT
124 *
125 * This function creates (and optionall binds/connects) a socket using
126 * \ref osmo_sock_init, but also fills the \a ofd structure.
127 */
Harald Welte68b15742011-05-22 21:47:29 +0200128int osmo_sock_init_ofd(struct osmo_fd *ofd, int family, int type, int proto,
Pablo Neira Ayuso0849c9a2011-06-09 15:04:30 +0200129 const char *host, uint16_t port, unsigned int flags)
Harald Welte68b15742011-05-22 21:47:29 +0200130{
131 int sfd, rc;
132
Pablo Neira Ayuso0849c9a2011-06-09 15:04:30 +0200133 sfd = osmo_sock_init(family, type, proto, host, port, flags);
Harald Welte68b15742011-05-22 21:47:29 +0200134 if (sfd < 0)
135 return sfd;
136
137 ofd->fd = sfd;
138 ofd->when = BSC_FD_READ;
139
140 rc = osmo_fd_register(ofd);
141 if (rc < 0) {
142 close(sfd);
143 return rc;
144 }
145
Harald Welte33cb71a2011-05-21 18:54:32 +0200146 return sfd;
147}
148
Harald Welteba6988b2011-08-17 12:46:48 +0200149/*! \brief Initialize a socket and fill \ref sockaddr
150 * \param[out] ss socket address (will be filled in)
151 * \param[in] type Socket type like SOCK_DGRAM, SOCK_STREAM
152 * \param[in] proto Protocol like IPPROTO_TCP, IPPROTO_UDP
153 * \param[in] flags flags like \ref OSMO_SOCK_F_CONNECT
154 *
155 * This function creates (and optionall binds/connects) a socket using
156 * \ref osmo_sock_init, but also fills the \a ss structure.
157 */
Harald Welte33cb71a2011-05-21 18:54:32 +0200158int osmo_sock_init_sa(struct sockaddr *ss, uint16_t type,
Pablo Neira Ayuso0849c9a2011-06-09 15:04:30 +0200159 uint8_t proto, unsigned int flags)
Harald Welte33cb71a2011-05-21 18:54:32 +0200160{
161 char host[NI_MAXHOST];
162 uint16_t port;
163 struct sockaddr_in *sin;
164 struct sockaddr_in6 *sin6;
165 int s, sa_len;
166
167 /* determine port and host from ss */
168 switch (ss->sa_family) {
169 case AF_INET:
170 sin = (struct sockaddr_in *) ss;
171 sa_len = sizeof(struct sockaddr_in);
172 port = ntohs(sin->sin_port);
173 break;
174 case AF_INET6:
175 sin6 = (struct sockaddr_in6 *) ss;
176 sa_len = sizeof(struct sockaddr_in6);
177 port = ntohs(sin6->sin6_port);
178 break;
179 default:
180 return -EINVAL;
181 }
Harald Welte33cb71a2011-05-21 18:54:32 +0200182
183 s = getnameinfo(ss, sa_len, host, NI_MAXHOST,
184 NULL, 0, NI_NUMERICHOST);
185 if (s != 0) {
186 perror("getnameinfo failed");
187 return s;
188 }
189
Pablo Neira Ayuso0849c9a2011-06-09 15:04:30 +0200190 return osmo_sock_init(ss->sa_family, type, proto, host, port, flags);
Harald Welte33cb71a2011-05-21 18:54:32 +0200191}
192
193static int sockaddr_equal(const struct sockaddr *a,
Harald Weltee4764422011-05-22 12:25:57 +0200194 const struct sockaddr *b, unsigned int len)
Harald Welte33cb71a2011-05-21 18:54:32 +0200195{
196 struct sockaddr_in *sin_a, *sin_b;
197 struct sockaddr_in6 *sin6_a, *sin6_b;
198
199 if (a->sa_family != b->sa_family)
200 return 0;
201
202 switch (a->sa_family) {
203 case AF_INET:
204 sin_a = (struct sockaddr_in *)a;
205 sin_b = (struct sockaddr_in *)b;
206 if (!memcmp(&sin_a->sin_addr, &sin_b->sin_addr,
207 sizeof(struct in_addr)))
208 return 1;
209 break;
210 case AF_INET6:
211 sin6_a = (struct sockaddr_in6 *)a;
212 sin6_b = (struct sockaddr_in6 *)b;
213 if (!memcmp(&sin6_a->sin6_addr, &sin6_b->sin6_addr,
214 sizeof(struct in6_addr)))
215 return 1;
216 break;
217 }
218 return 0;
219}
220
Harald Welteba6988b2011-08-17 12:46:48 +0200221/*! \brief Determine if the given address is a local address
222 * \param[in] addr Socket Address
223 * \param[in] addrlen Length of socket address in bytes
224 * \returns 1 if address is local, 0 otherwise.
225 */
Harald Welte33cb71a2011-05-21 18:54:32 +0200226int osmo_sockaddr_is_local(struct sockaddr *addr, socklen_t addrlen)
227{
228 struct ifaddrs *ifaddr, *ifa;
229
230 if (getifaddrs(&ifaddr) == -1) {
231 perror("getifaddrs");
232 return -EIO;
233 }
234
235 for (ifa = ifaddr; ifa != NULL; ifa = ifa->ifa_next) {
Harald Welte4d3a7b12011-05-24 21:31:53 +0200236 if (!ifa->ifa_addr)
237 continue;
Harald Welte33cb71a2011-05-21 18:54:32 +0200238 if (sockaddr_equal(ifa->ifa_addr, addr, addrlen))
239 return 1;
240 }
241
242 return 0;
243}
Harald Weltee4764422011-05-22 12:25:57 +0200244
245#endif /* HAVE_SYS_SOCKET_H */
Harald Welteba6988b2011-08-17 12:46:48 +0200246
247/*! }@ */