blob: 8a8829b753b68c85c44c89ac3d8f3dfea3f40e73 [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;
57 hints.ai_socktype = type;
58 hints.ai_flags = 0;
59 hints.ai_protocol = proto;
60
Pablo Neira Ayuso0849c9a2011-06-09 15:04:30 +020061 if (flags & OSMO_SOCK_F_BIND)
Harald Weltef9e07462011-05-31 17:47:54 +020062 hints.ai_flags |= AI_PASSIVE;
63
Harald Welte33cb71a2011-05-21 18:54:32 +020064 rc = getaddrinfo(host, portbuf, &hints, &result);
65 if (rc != 0) {
66 perror("getaddrinfo returned NULL");
67 return -EINVAL;
68 }
69
70 for (rp = result; rp != NULL; rp = rp->ai_next) {
71 sfd = socket(rp->ai_family, rp->ai_socktype, rp->ai_protocol);
72 if (sfd == -1)
73 continue;
Pablo Neira Ayuso0849c9a2011-06-09 15:04:30 +020074 if (flags & OSMO_SOCK_F_NONBLOCK) {
75 if (ioctl(sfd, FIONBIO, (unsigned char *)&on) < 0) {
76 perror("cannot set this socket unblocking");
77 close(sfd);
78 return -EINVAL;
79 }
80 }
81 if (flags & OSMO_SOCK_F_CONNECT) {
82 rc = connect(sfd, rp->ai_addr, rp->ai_addrlen);
83 if (rc != -1 || (rc == -1 && errno == EINPROGRESS))
Harald Welte33cb71a2011-05-21 18:54:32 +020084 break;
85 } else {
Pablo Neira Ayuso0849c9a2011-06-09 15:04:30 +020086 rc = setsockopt(sfd, SOL_SOCKET, SO_REUSEADDR,
87 &on, sizeof(on));
88 if (rc < 0) {
89 perror("cannot setsockopt socket");
90 break;
91 }
Harald Welte33cb71a2011-05-21 18:54:32 +020092 if (bind(sfd, rp->ai_addr, rp->ai_addrlen) != -1)
93 break;
94 }
95 close(sfd);
96 }
97 freeaddrinfo(result);
98
99 if (rp == NULL) {
100 perror("unable to connect/bind socket");
101 return -ENODEV;
102 }
Harald Welte68b15742011-05-22 21:47:29 +0200103
104 setsockopt(sfd, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on));
105
106 /* Make sure to call 'listen' on a bound, connection-oriented sock */
Pablo Neira Ayuso0849c9a2011-06-09 15:04:30 +0200107 if (flags & OSMO_SOCK_F_BIND) {
Harald Welte68b15742011-05-22 21:47:29 +0200108 switch (type) {
109 case SOCK_STREAM:
110 case SOCK_SEQPACKET:
111 listen(sfd, 10);
112 break;
113 }
114 }
115 return sfd;
116}
117
Harald Welteba6988b2011-08-17 12:46:48 +0200118/*! \brief Initialize a socket and fill \ref osmo_fd
119 * \param[out] osmocom file descriptor (will be filled in)
120 * \param[in] family Address Family like AF_INET, AF_INET6, AF_UNSPEC
121 * \param[in] type Socket type like SOCK_DGRAM, SOCK_STREAM
122 * \param[in] proto Protocol like IPPROTO_TCP, IPPROTO_UDP
123 * \param[in] host remote host name or IP address in string form
124 * \param[in] port remote port number in host byte order
125 * \param[in] flags flags like \ref OSMO_SOCK_F_CONNECT
126 *
127 * This function creates (and optionall binds/connects) a socket using
128 * \ref osmo_sock_init, but also fills the \a ofd structure.
129 */
Harald Welte68b15742011-05-22 21:47:29 +0200130int osmo_sock_init_ofd(struct osmo_fd *ofd, int family, int type, int proto,
Pablo Neira Ayuso0849c9a2011-06-09 15:04:30 +0200131 const char *host, uint16_t port, unsigned int flags)
Harald Welte68b15742011-05-22 21:47:29 +0200132{
133 int sfd, rc;
134
Pablo Neira Ayuso0849c9a2011-06-09 15:04:30 +0200135 sfd = osmo_sock_init(family, type, proto, host, port, flags);
Harald Welte68b15742011-05-22 21:47:29 +0200136 if (sfd < 0)
137 return sfd;
138
139 ofd->fd = sfd;
140 ofd->when = BSC_FD_READ;
141
142 rc = osmo_fd_register(ofd);
143 if (rc < 0) {
144 close(sfd);
145 return rc;
146 }
147
Harald Welte33cb71a2011-05-21 18:54:32 +0200148 return sfd;
149}
150
Harald Welteba6988b2011-08-17 12:46:48 +0200151/*! \brief Initialize a socket and fill \ref sockaddr
152 * \param[out] ss socket address (will be filled in)
153 * \param[in] type Socket type like SOCK_DGRAM, SOCK_STREAM
154 * \param[in] proto Protocol like IPPROTO_TCP, IPPROTO_UDP
155 * \param[in] flags flags like \ref OSMO_SOCK_F_CONNECT
156 *
157 * This function creates (and optionall binds/connects) a socket using
158 * \ref osmo_sock_init, but also fills the \a ss structure.
159 */
Harald Welte33cb71a2011-05-21 18:54:32 +0200160int osmo_sock_init_sa(struct sockaddr *ss, uint16_t type,
Pablo Neira Ayuso0849c9a2011-06-09 15:04:30 +0200161 uint8_t proto, unsigned int flags)
Harald Welte33cb71a2011-05-21 18:54:32 +0200162{
163 char host[NI_MAXHOST];
164 uint16_t port;
165 struct sockaddr_in *sin;
166 struct sockaddr_in6 *sin6;
167 int s, sa_len;
168
169 /* determine port and host from ss */
170 switch (ss->sa_family) {
171 case AF_INET:
172 sin = (struct sockaddr_in *) ss;
173 sa_len = sizeof(struct sockaddr_in);
174 port = ntohs(sin->sin_port);
175 break;
176 case AF_INET6:
177 sin6 = (struct sockaddr_in6 *) ss;
178 sa_len = sizeof(struct sockaddr_in6);
179 port = ntohs(sin6->sin6_port);
180 break;
181 default:
182 return -EINVAL;
183 }
Harald Welte33cb71a2011-05-21 18:54:32 +0200184
185 s = getnameinfo(ss, sa_len, host, NI_MAXHOST,
186 NULL, 0, NI_NUMERICHOST);
187 if (s != 0) {
188 perror("getnameinfo failed");
189 return s;
190 }
191
Pablo Neira Ayuso0849c9a2011-06-09 15:04:30 +0200192 return osmo_sock_init(ss->sa_family, type, proto, host, port, flags);
Harald Welte33cb71a2011-05-21 18:54:32 +0200193}
194
195static int sockaddr_equal(const struct sockaddr *a,
Harald Weltee4764422011-05-22 12:25:57 +0200196 const struct sockaddr *b, unsigned int len)
Harald Welte33cb71a2011-05-21 18:54:32 +0200197{
198 struct sockaddr_in *sin_a, *sin_b;
199 struct sockaddr_in6 *sin6_a, *sin6_b;
200
201 if (a->sa_family != b->sa_family)
202 return 0;
203
204 switch (a->sa_family) {
205 case AF_INET:
206 sin_a = (struct sockaddr_in *)a;
207 sin_b = (struct sockaddr_in *)b;
208 if (!memcmp(&sin_a->sin_addr, &sin_b->sin_addr,
209 sizeof(struct in_addr)))
210 return 1;
211 break;
212 case AF_INET6:
213 sin6_a = (struct sockaddr_in6 *)a;
214 sin6_b = (struct sockaddr_in6 *)b;
215 if (!memcmp(&sin6_a->sin6_addr, &sin6_b->sin6_addr,
216 sizeof(struct in6_addr)))
217 return 1;
218 break;
219 }
220 return 0;
221}
222
Harald Welteba6988b2011-08-17 12:46:48 +0200223/*! \brief Determine if the given address is a local address
224 * \param[in] addr Socket Address
225 * \param[in] addrlen Length of socket address in bytes
226 * \returns 1 if address is local, 0 otherwise.
227 */
Harald Welte33cb71a2011-05-21 18:54:32 +0200228int osmo_sockaddr_is_local(struct sockaddr *addr, socklen_t addrlen)
229{
230 struct ifaddrs *ifaddr, *ifa;
231
232 if (getifaddrs(&ifaddr) == -1) {
233 perror("getifaddrs");
234 return -EIO;
235 }
236
237 for (ifa = ifaddr; ifa != NULL; ifa = ifa->ifa_next) {
Harald Welte4d3a7b12011-05-24 21:31:53 +0200238 if (!ifa->ifa_addr)
239 continue;
Harald Welte33cb71a2011-05-21 18:54:32 +0200240 if (sockaddr_equal(ifa->ifa_addr, addr, addrlen))
241 return 1;
242 }
243
244 return 0;
245}
Harald Weltee4764422011-05-22 12:25:57 +0200246
247#endif /* HAVE_SYS_SOCKET_H */
Harald Welteba6988b2011-08-17 12:46:48 +0200248
249/*! }@ */