diff options
Diffstat (limited to 'src/socket.c')
-rw-r--r-- | src/socket.c | 143 |
1 files changed, 143 insertions, 0 deletions
diff --git a/src/socket.c b/src/socket.c new file mode 100644 index 00000000..bd4914fd --- /dev/null +++ b/src/socket.c @@ -0,0 +1,143 @@ +#include "../config.h" + +#include <osmocom/core/logging.h> +#include <osmocom/core/select.h> +#include <osmocom/core/socket.h> + +#include <arpa/inet.h> +#include <sys/socket.h> +#include <sys/types.h> +#include <netinet/in.h> + +#include <stdio.h> +#include <unistd.h> +#include <stdint.h> +#include <string.h> +#include <errno.h> +#include <netdb.h> +#include <ifaddrs.h> + +int osmo_sock_init(uint16_t family, uint16_t type, uint8_t proto, + const char *host, uint16_t port, int connect0_bind1) +{ + struct addrinfo hints, *result, *rp; + int sfd, rc; + char portbuf[16]; + + sprintf(portbuf, "%u", port); + memset(&hints, 0, sizeof(struct addrinfo)); + hints.ai_family = family; + hints.ai_socktype = type; + hints.ai_flags = 0; + hints.ai_protocol = proto; + + rc = getaddrinfo(host, portbuf, &hints, &result); + if (rc != 0) { + perror("getaddrinfo returned NULL"); + return -EINVAL; + } + + for (rp = result; rp != NULL; rp = rp->ai_next) { + sfd = socket(rp->ai_family, rp->ai_socktype, rp->ai_protocol); + if (sfd == -1) + continue; + if (connect0_bind1 == 0) { + if (connect(sfd, rp->ai_addr, rp->ai_addrlen) != -1) + break; + } else { + if (bind(sfd, rp->ai_addr, rp->ai_addrlen) != -1) + break; + } + close(sfd); + } + freeaddrinfo(result); + + if (rp == NULL) { + perror("unable to connect/bind socket"); + return -ENODEV; + } + return sfd; +} + +int osmo_sock_init_sa(struct sockaddr *ss, uint16_t type, + uint8_t proto, int connect0_bind1) +{ + char host[NI_MAXHOST]; + uint16_t port; + struct sockaddr_in *sin; + struct sockaddr_in6 *sin6; + int s, sa_len; + + /* determine port and host from ss */ + switch (ss->sa_family) { + case AF_INET: + sin = (struct sockaddr_in *) ss; + sa_len = sizeof(struct sockaddr_in); + port = ntohs(sin->sin_port); + break; + case AF_INET6: + sin6 = (struct sockaddr_in6 *) ss; + sa_len = sizeof(struct sockaddr_in6); + port = ntohs(sin6->sin6_port); + break; + default: + return -EINVAL; + } + fprintf(stderr, "==> PORT = %u\n", port); + + s = getnameinfo(ss, sa_len, host, NI_MAXHOST, + NULL, 0, NI_NUMERICHOST); + if (s != 0) { + perror("getnameinfo failed"); + return s; + } + + return osmo_sock_init(ss->sa_family, type, proto, host, + port, connect0_bind1); +} + +static int sockaddr_equal(const struct sockaddr *a, + const struct sockaddr *b, socklen_t len) +{ + struct sockaddr_in *sin_a, *sin_b; + struct sockaddr_in6 *sin6_a, *sin6_b; + + if (a->sa_family != b->sa_family) + return 0; + + switch (a->sa_family) { + case AF_INET: + sin_a = (struct sockaddr_in *)a; + sin_b = (struct sockaddr_in *)b; + if (!memcmp(&sin_a->sin_addr, &sin_b->sin_addr, + sizeof(struct in_addr))) + return 1; + break; + case AF_INET6: + sin6_a = (struct sockaddr_in6 *)a; + sin6_b = (struct sockaddr_in6 *)b; + if (!memcmp(&sin6_a->sin6_addr, &sin6_b->sin6_addr, + sizeof(struct in6_addr))) + return 1; + break; + } + return 0; +} + +/* determine if the given address is a local address */ +int osmo_sockaddr_is_local(struct sockaddr *addr, socklen_t addrlen) +{ + struct ifaddrs *ifaddr, *ifa; + + if (getifaddrs(&ifaddr) == -1) { + perror("getifaddrs"); + return -EIO; + } + + for (ifa = ifaddr; ifa != NULL; ifa = ifa->ifa_next) { + if (sockaddr_equal(ifa->ifa_addr, addr, addrlen)) + return 1; + } + + return 0; +} |