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