gsmtap: rework GSMTAP API to be more future-proof
[osmocom-bb.git] / src / socket.c
1 #include "../config.h"
2
3 #include <osmocom/core/logging.h>
4 #include <osmocom/core/select.h>
5 #include <osmocom/core/socket.h>
6
7 #include <arpa/inet.h>
8 #include <sys/socket.h>
9 #include <sys/types.h>
10 #include <netinet/in.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;
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         return sfd;
60 }
61
62 int osmo_sock_init_sa(struct sockaddr *ss, uint16_t type,
63                       uint8_t proto, int connect0_bind1)
64 {
65         char host[NI_MAXHOST];
66         uint16_t port;
67         struct sockaddr_in *sin;
68         struct sockaddr_in6 *sin6;
69         int s, sa_len;
70
71         /* determine port and host from ss */
72         switch (ss->sa_family) {
73         case AF_INET:
74                 sin = (struct sockaddr_in *) ss;
75                 sa_len = sizeof(struct sockaddr_in);
76                 port = ntohs(sin->sin_port);
77                 break;
78         case AF_INET6:
79                 sin6 = (struct sockaddr_in6 *) ss;
80                 sa_len = sizeof(struct sockaddr_in6);
81                 port = ntohs(sin6->sin6_port);
82                 break;
83         default:
84                 return -EINVAL;
85         }
86         fprintf(stderr, "==> PORT = %u\n", port);
87
88         s = getnameinfo(ss, sa_len, host, NI_MAXHOST,
89                         NULL, 0, NI_NUMERICHOST);
90         if (s != 0) {
91                 perror("getnameinfo failed");
92                 return s;
93         }
94
95         return osmo_sock_init(ss->sa_family, type, proto, host,
96                               port, connect0_bind1);
97 }
98
99 static int sockaddr_equal(const struct sockaddr *a,
100                           const struct sockaddr *b, socklen_t len)
101 {
102         struct sockaddr_in *sin_a, *sin_b;
103         struct sockaddr_in6 *sin6_a, *sin6_b;
104
105         if (a->sa_family != b->sa_family)
106                 return 0;
107
108         switch (a->sa_family) {
109         case AF_INET:
110                 sin_a = (struct sockaddr_in *)a;
111                 sin_b = (struct sockaddr_in *)b;
112                 if (!memcmp(&sin_a->sin_addr, &sin_b->sin_addr,
113                             sizeof(struct in_addr)))
114                         return 1;
115                 break;
116         case AF_INET6:
117                 sin6_a = (struct sockaddr_in6 *)a;
118                 sin6_b = (struct sockaddr_in6 *)b;
119                 if (!memcmp(&sin6_a->sin6_addr, &sin6_b->sin6_addr,
120                             sizeof(struct in6_addr)))
121                         return 1;
122                 break;
123         }
124         return 0;
125 }
126
127 /* determine if the given address is a local address */
128 int osmo_sockaddr_is_local(struct sockaddr *addr, socklen_t addrlen)
129 {
130         struct ifaddrs *ifaddr, *ifa;
131
132         if (getifaddrs(&ifaddr) == -1) {
133                 perror("getifaddrs");
134                 return -EIO;
135         }
136
137         for (ifa = ifaddr; ifa != NULL; ifa = ifa->ifa_next) {
138                 if (sockaddr_equal(ifa->ifa_addr, addr, addrlen))
139                         return 1;
140         }
141
142         return 0;
143 }