socket: getaddrinfo(): set AI_PASSIVE if we want to bind
[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/socket.h>
10 #include <sys/types.h>
11
12 #include <stdio.h>
13 #include <unistd.h>
14 #include <stdint.h>
15 #include <string.h>
16 #include <errno.h>
17 #include <netdb.h>
18 #include <ifaddrs.h>
19
20 int osmo_sock_init(uint16_t family, uint16_t type, uint8_t proto,
21                    const char *host, uint16_t port, int connect0_bind1)
22 {
23         struct addrinfo hints, *result, *rp;
24         int sfd, rc, on = 1;
25         char portbuf[16];
26
27         sprintf(portbuf, "%u", port);
28         memset(&hints, 0, sizeof(struct addrinfo));
29         hints.ai_family = family;
30         hints.ai_socktype = type;
31         hints.ai_flags = 0;
32         hints.ai_protocol = proto;
33
34         if (connect0_bind1)
35                 hints.ai_flags |= AI_PASSIVE;
36
37         rc = getaddrinfo(host, portbuf, &hints, &result);
38         if (rc != 0) {
39                 perror("getaddrinfo returned NULL");
40                 return -EINVAL;
41         }
42
43         for (rp = result; rp != NULL; rp = rp->ai_next) {
44                 sfd = socket(rp->ai_family, rp->ai_socktype, rp->ai_protocol);
45                 if (sfd == -1)
46                         continue;
47                 if (connect0_bind1 == 0) {
48                         if (connect(sfd, rp->ai_addr, rp->ai_addrlen) != -1)
49                                 break;
50                 } else {
51                         if (bind(sfd, rp->ai_addr, rp->ai_addrlen) != -1)
52                                 break;
53                 }
54                 close(sfd);
55         }
56         freeaddrinfo(result);
57
58         if (rp == NULL) {
59                 perror("unable to connect/bind socket");
60                 return -ENODEV;
61         }
62
63         setsockopt(sfd, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on));
64
65         /* Make sure to call 'listen' on a bound, connection-oriented sock */
66         if (connect0_bind1 == 1) {
67                 switch (type) {
68                 case SOCK_STREAM:
69                 case SOCK_SEQPACKET:
70                         listen(sfd, 10);
71                         break;
72                 }
73         }
74         return sfd;
75 }
76
77 int osmo_sock_init_ofd(struct osmo_fd *ofd, int family, int type, int proto,
78                         const char *host, uint16_t port, int connect0_bind1)
79 {
80         int sfd, rc;
81
82         sfd = osmo_sock_init(family, type, proto, host, port, connect0_bind1);
83         if (sfd < 0)
84                 return sfd;
85
86         ofd->fd = sfd;
87         ofd->when = BSC_FD_READ;
88
89         rc = osmo_fd_register(ofd);
90         if (rc < 0) {
91                 close(sfd);
92                 return rc;
93         }
94
95         return sfd;
96 }
97
98 int osmo_sock_init_sa(struct sockaddr *ss, uint16_t type,
99                       uint8_t proto, int connect0_bind1)
100 {
101         char host[NI_MAXHOST];
102         uint16_t port;
103         struct sockaddr_in *sin;
104         struct sockaddr_in6 *sin6;
105         int s, sa_len;
106
107         /* determine port and host from ss */
108         switch (ss->sa_family) {
109         case AF_INET:
110                 sin = (struct sockaddr_in *) ss;
111                 sa_len = sizeof(struct sockaddr_in);
112                 port = ntohs(sin->sin_port);
113                 break;
114         case AF_INET6:
115                 sin6 = (struct sockaddr_in6 *) ss;
116                 sa_len = sizeof(struct sockaddr_in6);
117                 port = ntohs(sin6->sin6_port);
118                 break;
119         default:
120                 return -EINVAL;
121         }
122
123         s = getnameinfo(ss, sa_len, host, NI_MAXHOST,
124                         NULL, 0, NI_NUMERICHOST);
125         if (s != 0) {
126                 perror("getnameinfo failed");
127                 return s;
128         }
129
130         return osmo_sock_init(ss->sa_family, type, proto, host,
131                               port, connect0_bind1);
132 }
133
134 static int sockaddr_equal(const struct sockaddr *a,
135                           const struct sockaddr *b, unsigned int len)
136 {
137         struct sockaddr_in *sin_a, *sin_b;
138         struct sockaddr_in6 *sin6_a, *sin6_b;
139
140         if (a->sa_family != b->sa_family)
141                 return 0;
142
143         switch (a->sa_family) {
144         case AF_INET:
145                 sin_a = (struct sockaddr_in *)a;
146                 sin_b = (struct sockaddr_in *)b;
147                 if (!memcmp(&sin_a->sin_addr, &sin_b->sin_addr,
148                             sizeof(struct in_addr)))
149                         return 1;
150                 break;
151         case AF_INET6:
152                 sin6_a = (struct sockaddr_in6 *)a;
153                 sin6_b = (struct sockaddr_in6 *)b;
154                 if (!memcmp(&sin6_a->sin6_addr, &sin6_b->sin6_addr,
155                             sizeof(struct in6_addr)))
156                         return 1;
157                 break;
158         }
159         return 0;
160 }
161
162 /* determine if the given address is a local address */
163 int osmo_sockaddr_is_local(struct sockaddr *addr, socklen_t addrlen)
164 {
165         struct ifaddrs *ifaddr, *ifa;
166
167         if (getifaddrs(&ifaddr) == -1) {
168                 perror("getifaddrs");
169                 return -EIO;
170         }
171
172         for (ifa = ifaddr; ifa != NULL; ifa = ifa->ifa_next) {
173                 if (!ifa->ifa_addr)
174                         continue;
175                 if (sockaddr_equal(ifa->ifa_addr, addr, addrlen))
176                         return 1;
177         }
178
179         return 0;
180 }
181
182 #endif /* HAVE_SYS_SOCKET_H */