Revert various debian related changes
[osmocom-bb.git] / src / socket.c
1 #include "../config.h"
2
3 #ifdef HAVE_SYS_SOCKET_H
4
5 #include <osmocom/core/logging.h>
6 #include <osmocom/core/select.h>
7 #include <osmocom/core/socket.h>
8
9 #include <sys/ioctl.h>
10 #include <sys/socket.h>
11 #include <sys/types.h>
12
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
21 int osmo_sock_init(uint16_t family, uint16_t type, uint8_t proto,
22                    const char *host, uint16_t port, unsigned int flags)
23 {
24         struct addrinfo hints, *result, *rp;
25         int sfd, rc, on = 1;
26         char portbuf[16];
27
28         if ((flags & (OSMO_SOCK_F_BIND | OSMO_SOCK_F_CONNECT)) ==
29                      (OSMO_SOCK_F_BIND | OSMO_SOCK_F_CONNECT))
30                 return -EINVAL;
31
32         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
39         if (flags & OSMO_SOCK_F_BIND)
40                 hints.ai_flags |= AI_PASSIVE;
41
42         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;
52                 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))
62                                 break;
63                 } else {
64                         rc = setsockopt(sfd, SOL_SOCKET, SO_REUSEADDR,
65                                                         &on, sizeof(on));
66                         if (rc < 0) {
67                                 perror("cannot setsockopt socket");
68                                 break;
69                         }
70                         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         }
81
82         setsockopt(sfd, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on));
83
84         /* Make sure to call 'listen' on a bound, connection-oriented sock */
85         if (flags & OSMO_SOCK_F_BIND) {
86                 switch (type) {
87                 case SOCK_STREAM:
88                 case SOCK_SEQPACKET:
89                         listen(sfd, 10);
90                         break;
91                 }
92         }
93         return sfd;
94 }
95
96 int osmo_sock_init_ofd(struct osmo_fd *ofd, int family, int type, int proto,
97                         const char *host, uint16_t port, unsigned int flags)
98 {
99         int sfd, rc;
100
101         sfd = osmo_sock_init(family, type, proto, host, port, flags);
102         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
114         return sfd;
115 }
116
117 int osmo_sock_init_sa(struct sockaddr *ss, uint16_t type,
118                       uint8_t proto, unsigned int flags)
119 {
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         }
141
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
149         return osmo_sock_init(ss->sa_family, type, proto, host, port, flags);
150 }
151
152 static int sockaddr_equal(const struct sockaddr *a,
153                           const struct sockaddr *b, unsigned int len)
154 {
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 */
181 int 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) {
191                 if (!ifa->ifa_addr)
192                         continue;
193                 if (sockaddr_equal(ifa->ifa_addr, addr, addrlen))
194                         return 1;
195         }
196
197         return 0;
198 }
199
200 #endif /* HAVE_SYS_SOCKET_H */