blob: f1fcccd502b4f0689e77e2d450d80979b6f55060 [file] [log] [blame]
Harald Welte33cb71a2011-05-21 18:54:32 +02001#include "../config.h"
2
Harald Weltee4764422011-05-22 12:25:57 +02003#ifdef HAVE_SYS_SOCKET_H
4
Harald Welte33cb71a2011-05-21 18:54:32 +02005#include <osmocom/core/logging.h>
6#include <osmocom/core/select.h>
7#include <osmocom/core/socket.h>
8
Pablo Neira Ayuso0849c9a2011-06-09 15:04:30 +02009#include <sys/ioctl.h>
Harald Welte33cb71a2011-05-21 18:54:32 +020010#include <sys/socket.h>
11#include <sys/types.h>
Harald Welte33cb71a2011-05-21 18:54:32 +020012
13#include <stdio.h>
14#include <unistd.h>
15#include <stdint.h>
16#include <string.h>
17#include <errno.h>
18#include <netdb.h>
19#include <ifaddrs.h>
20
21int osmo_sock_init(uint16_t family, uint16_t type, uint8_t proto,
Pablo Neira Ayuso0849c9a2011-06-09 15:04:30 +020022 const char *host, uint16_t port, unsigned int flags)
Harald Welte33cb71a2011-05-21 18:54:32 +020023{
24 struct addrinfo hints, *result, *rp;
Harald Welte68b15742011-05-22 21:47:29 +020025 int sfd, rc, on = 1;
Harald Welte33cb71a2011-05-21 18:54:32 +020026 char portbuf[16];
27
Pablo Neira Ayuso0849c9a2011-06-09 15:04:30 +020028 if ((flags & (OSMO_SOCK_F_BIND | OSMO_SOCK_F_CONNECT)) ==
29 (OSMO_SOCK_F_BIND | OSMO_SOCK_F_CONNECT))
30 return -EINVAL;
31
Harald Welte33cb71a2011-05-21 18:54:32 +020032 sprintf(portbuf, "%u", port);
33 memset(&hints, 0, sizeof(struct addrinfo));
34 hints.ai_family = family;
35 hints.ai_socktype = type;
36 hints.ai_flags = 0;
37 hints.ai_protocol = proto;
38
Pablo Neira Ayuso0849c9a2011-06-09 15:04:30 +020039 if (flags & OSMO_SOCK_F_BIND)
Harald Weltef9e07462011-05-31 17:47:54 +020040 hints.ai_flags |= AI_PASSIVE;
41
Harald Welte33cb71a2011-05-21 18:54:32 +020042 rc = getaddrinfo(host, portbuf, &hints, &result);
43 if (rc != 0) {
44 perror("getaddrinfo returned NULL");
45 return -EINVAL;
46 }
47
48 for (rp = result; rp != NULL; rp = rp->ai_next) {
49 sfd = socket(rp->ai_family, rp->ai_socktype, rp->ai_protocol);
50 if (sfd == -1)
51 continue;
Pablo Neira Ayuso0849c9a2011-06-09 15:04:30 +020052 if (flags & OSMO_SOCK_F_NONBLOCK) {
53 if (ioctl(sfd, FIONBIO, (unsigned char *)&on) < 0) {
54 perror("cannot set this socket unblocking");
55 close(sfd);
56 return -EINVAL;
57 }
58 }
59 if (flags & OSMO_SOCK_F_CONNECT) {
60 rc = connect(sfd, rp->ai_addr, rp->ai_addrlen);
61 if (rc != -1 || (rc == -1 && errno == EINPROGRESS))
Harald Welte33cb71a2011-05-21 18:54:32 +020062 break;
63 } else {
Pablo Neira Ayuso0849c9a2011-06-09 15:04:30 +020064 rc = setsockopt(sfd, SOL_SOCKET, SO_REUSEADDR,
65 &on, sizeof(on));
66 if (rc < 0) {
67 perror("cannot setsockopt socket");
68 break;
69 }
Harald Welte33cb71a2011-05-21 18:54:32 +020070 if (bind(sfd, rp->ai_addr, rp->ai_addrlen) != -1)
71 break;
72 }
73 close(sfd);
74 }
75 freeaddrinfo(result);
76
77 if (rp == NULL) {
78 perror("unable to connect/bind socket");
79 return -ENODEV;
80 }
Harald Welte68b15742011-05-22 21:47:29 +020081
82 setsockopt(sfd, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on));
83
84 /* Make sure to call 'listen' on a bound, connection-oriented sock */
Pablo Neira Ayuso0849c9a2011-06-09 15:04:30 +020085 if (flags & OSMO_SOCK_F_BIND) {
Harald Welte68b15742011-05-22 21:47:29 +020086 switch (type) {
87 case SOCK_STREAM:
88 case SOCK_SEQPACKET:
89 listen(sfd, 10);
90 break;
91 }
92 }
93 return sfd;
94}
95
96int osmo_sock_init_ofd(struct osmo_fd *ofd, int family, int type, int proto,
Pablo Neira Ayuso0849c9a2011-06-09 15:04:30 +020097 const char *host, uint16_t port, unsigned int flags)
Harald Welte68b15742011-05-22 21:47:29 +020098{
99 int sfd, rc;
100
Pablo Neira Ayuso0849c9a2011-06-09 15:04:30 +0200101 sfd = osmo_sock_init(family, type, proto, host, port, flags);
Harald Welte68b15742011-05-22 21:47:29 +0200102 if (sfd < 0)
103 return sfd;
104
105 ofd->fd = sfd;
106 ofd->when = BSC_FD_READ;
107
108 rc = osmo_fd_register(ofd);
109 if (rc < 0) {
110 close(sfd);
111 return rc;
112 }
113
Harald Welte33cb71a2011-05-21 18:54:32 +0200114 return sfd;
115}
116
117int osmo_sock_init_sa(struct sockaddr *ss, uint16_t type,
Pablo Neira Ayuso0849c9a2011-06-09 15:04:30 +0200118 uint8_t proto, unsigned int flags)
Harald Welte33cb71a2011-05-21 18:54:32 +0200119{
120 char host[NI_MAXHOST];
121 uint16_t port;
122 struct sockaddr_in *sin;
123 struct sockaddr_in6 *sin6;
124 int s, sa_len;
125
126 /* determine port and host from ss */
127 switch (ss->sa_family) {
128 case AF_INET:
129 sin = (struct sockaddr_in *) ss;
130 sa_len = sizeof(struct sockaddr_in);
131 port = ntohs(sin->sin_port);
132 break;
133 case AF_INET6:
134 sin6 = (struct sockaddr_in6 *) ss;
135 sa_len = sizeof(struct sockaddr_in6);
136 port = ntohs(sin6->sin6_port);
137 break;
138 default:
139 return -EINVAL;
140 }
Harald Welte33cb71a2011-05-21 18:54:32 +0200141
142 s = getnameinfo(ss, sa_len, host, NI_MAXHOST,
143 NULL, 0, NI_NUMERICHOST);
144 if (s != 0) {
145 perror("getnameinfo failed");
146 return s;
147 }
148
Pablo Neira Ayuso0849c9a2011-06-09 15:04:30 +0200149 return osmo_sock_init(ss->sa_family, type, proto, host, port, flags);
Harald Welte33cb71a2011-05-21 18:54:32 +0200150}
151
152static int sockaddr_equal(const struct sockaddr *a,
Harald Weltee4764422011-05-22 12:25:57 +0200153 const struct sockaddr *b, unsigned int len)
Harald Welte33cb71a2011-05-21 18:54:32 +0200154{
155 struct sockaddr_in *sin_a, *sin_b;
156 struct sockaddr_in6 *sin6_a, *sin6_b;
157
158 if (a->sa_family != b->sa_family)
159 return 0;
160
161 switch (a->sa_family) {
162 case AF_INET:
163 sin_a = (struct sockaddr_in *)a;
164 sin_b = (struct sockaddr_in *)b;
165 if (!memcmp(&sin_a->sin_addr, &sin_b->sin_addr,
166 sizeof(struct in_addr)))
167 return 1;
168 break;
169 case AF_INET6:
170 sin6_a = (struct sockaddr_in6 *)a;
171 sin6_b = (struct sockaddr_in6 *)b;
172 if (!memcmp(&sin6_a->sin6_addr, &sin6_b->sin6_addr,
173 sizeof(struct in6_addr)))
174 return 1;
175 break;
176 }
177 return 0;
178}
179
180/* determine if the given address is a local address */
181int osmo_sockaddr_is_local(struct sockaddr *addr, socklen_t addrlen)
182{
183 struct ifaddrs *ifaddr, *ifa;
184
185 if (getifaddrs(&ifaddr) == -1) {
186 perror("getifaddrs");
187 return -EIO;
188 }
189
190 for (ifa = ifaddr; ifa != NULL; ifa = ifa->ifa_next) {
Harald Welte4d3a7b12011-05-24 21:31:53 +0200191 if (!ifa->ifa_addr)
192 continue;
Harald Welte33cb71a2011-05-21 18:54:32 +0200193 if (sockaddr_equal(ifa->ifa_addr, addr, addrlen))
194 return 1;
195 }
196
197 return 0;
198}
Harald Weltee4764422011-05-22 12:25:57 +0200199
200#endif /* HAVE_SYS_SOCKET_H */