2011-05-21 16:54:32 +00:00
|
|
|
#include "../config.h"
|
|
|
|
|
2011-08-17 10:46:48 +00:00
|
|
|
/*! \addtogroup socket
|
|
|
|
* @{
|
|
|
|
*/
|
|
|
|
|
|
|
|
/*! \file socket.c
|
|
|
|
* \brief Osmocom socket convenience functions
|
|
|
|
*/
|
|
|
|
|
2011-05-22 10:25:57 +00:00
|
|
|
#ifdef HAVE_SYS_SOCKET_H
|
|
|
|
|
2011-05-21 16:54:32 +00:00
|
|
|
#include <osmocom/core/logging.h>
|
|
|
|
#include <osmocom/core/select.h>
|
|
|
|
#include <osmocom/core/socket.h>
|
|
|
|
|
2011-06-09 13:04:30 +00:00
|
|
|
#include <sys/ioctl.h>
|
2011-05-21 16:54:32 +00:00
|
|
|
#include <sys/socket.h>
|
|
|
|
#include <sys/types.h>
|
|
|
|
|
2011-11-09 10:26:15 +00:00
|
|
|
#include <netinet/in.h>
|
|
|
|
|
2011-05-21 16:54:32 +00:00
|
|
|
#include <stdio.h>
|
|
|
|
#include <unistd.h>
|
|
|
|
#include <stdint.h>
|
|
|
|
#include <string.h>
|
|
|
|
#include <errno.h>
|
|
|
|
#include <netdb.h>
|
|
|
|
#include <ifaddrs.h>
|
|
|
|
|
2011-08-17 10:46:48 +00:00
|
|
|
/*! \brief Initialize a socket (including bind/connect)
|
|
|
|
* \param[in] family Address Family like AF_INET, AF_INET6, AF_UNSPEC
|
|
|
|
* \param[in] type Socket type like SOCK_DGRAM, SOCK_STREAM
|
|
|
|
* \param[in] proto Protocol like IPPROTO_TCP, IPPROTO_UDP
|
|
|
|
* \param[in] host remote host name or IP address in string form
|
|
|
|
* \param[in] port remote port number in host byte order
|
|
|
|
* \param[in] flags flags like \ref OSMO_SOCK_F_CONNECT
|
|
|
|
*
|
|
|
|
* This function creates a new socket of the designated \a family, \a
|
|
|
|
* type and \a proto and optionally binds or connects it, depending on
|
|
|
|
* the value of \a flags parameter.
|
|
|
|
*/
|
2011-05-21 16:54:32 +00:00
|
|
|
int osmo_sock_init(uint16_t family, uint16_t type, uint8_t proto,
|
2011-06-09 13:04:30 +00:00
|
|
|
const char *host, uint16_t port, unsigned int flags)
|
2011-05-21 16:54:32 +00:00
|
|
|
{
|
|
|
|
struct addrinfo hints, *result, *rp;
|
2011-05-22 19:47:29 +00:00
|
|
|
int sfd, rc, on = 1;
|
2011-05-21 16:54:32 +00:00
|
|
|
char portbuf[16];
|
|
|
|
|
2011-06-09 13:04:30 +00:00
|
|
|
if ((flags & (OSMO_SOCK_F_BIND | OSMO_SOCK_F_CONNECT)) ==
|
|
|
|
(OSMO_SOCK_F_BIND | OSMO_SOCK_F_CONNECT))
|
|
|
|
return -EINVAL;
|
|
|
|
|
2011-05-21 16:54:32 +00:00
|
|
|
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;
|
|
|
|
|
2011-06-09 13:04:30 +00:00
|
|
|
if (flags & OSMO_SOCK_F_BIND)
|
2011-05-31 15:47:54 +00:00
|
|
|
hints.ai_flags |= AI_PASSIVE;
|
|
|
|
|
2011-05-21 16:54:32 +00:00
|
|
|
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;
|
2011-06-09 13:04:30 +00:00
|
|
|
if (flags & OSMO_SOCK_F_NONBLOCK) {
|
|
|
|
if (ioctl(sfd, FIONBIO, (unsigned char *)&on) < 0) {
|
|
|
|
perror("cannot set this socket unblocking");
|
|
|
|
close(sfd);
|
|
|
|
return -EINVAL;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if (flags & OSMO_SOCK_F_CONNECT) {
|
|
|
|
rc = connect(sfd, rp->ai_addr, rp->ai_addrlen);
|
|
|
|
if (rc != -1 || (rc == -1 && errno == EINPROGRESS))
|
2011-05-21 16:54:32 +00:00
|
|
|
break;
|
|
|
|
} else {
|
2011-06-09 13:04:30 +00:00
|
|
|
rc = setsockopt(sfd, SOL_SOCKET, SO_REUSEADDR,
|
|
|
|
&on, sizeof(on));
|
|
|
|
if (rc < 0) {
|
|
|
|
perror("cannot setsockopt socket");
|
|
|
|
break;
|
|
|
|
}
|
2011-05-21 16:54:32 +00:00
|
|
|
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;
|
|
|
|
}
|
2011-05-22 19:47:29 +00:00
|
|
|
|
|
|
|
setsockopt(sfd, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on));
|
|
|
|
|
|
|
|
/* Make sure to call 'listen' on a bound, connection-oriented sock */
|
2011-06-09 13:04:30 +00:00
|
|
|
if (flags & OSMO_SOCK_F_BIND) {
|
2011-05-22 19:47:29 +00:00
|
|
|
switch (type) {
|
|
|
|
case SOCK_STREAM:
|
|
|
|
case SOCK_SEQPACKET:
|
|
|
|
listen(sfd, 10);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return sfd;
|
|
|
|
}
|
|
|
|
|
2011-08-17 10:46:48 +00:00
|
|
|
/*! \brief Initialize a socket and fill \ref osmo_fd
|
|
|
|
* \param[out] osmocom file descriptor (will be filled in)
|
|
|
|
* \param[in] family Address Family like AF_INET, AF_INET6, AF_UNSPEC
|
|
|
|
* \param[in] type Socket type like SOCK_DGRAM, SOCK_STREAM
|
|
|
|
* \param[in] proto Protocol like IPPROTO_TCP, IPPROTO_UDP
|
|
|
|
* \param[in] host remote host name or IP address in string form
|
|
|
|
* \param[in] port remote port number in host byte order
|
|
|
|
* \param[in] flags flags like \ref OSMO_SOCK_F_CONNECT
|
|
|
|
*
|
|
|
|
* This function creates (and optionall binds/connects) a socket using
|
|
|
|
* \ref osmo_sock_init, but also fills the \a ofd structure.
|
|
|
|
*/
|
2011-05-22 19:47:29 +00:00
|
|
|
int osmo_sock_init_ofd(struct osmo_fd *ofd, int family, int type, int proto,
|
2011-06-09 13:04:30 +00:00
|
|
|
const char *host, uint16_t port, unsigned int flags)
|
2011-05-22 19:47:29 +00:00
|
|
|
{
|
|
|
|
int sfd, rc;
|
|
|
|
|
2011-06-09 13:04:30 +00:00
|
|
|
sfd = osmo_sock_init(family, type, proto, host, port, flags);
|
2011-05-22 19:47:29 +00:00
|
|
|
if (sfd < 0)
|
|
|
|
return sfd;
|
|
|
|
|
|
|
|
ofd->fd = sfd;
|
|
|
|
ofd->when = BSC_FD_READ;
|
|
|
|
|
|
|
|
rc = osmo_fd_register(ofd);
|
|
|
|
if (rc < 0) {
|
|
|
|
close(sfd);
|
|
|
|
return rc;
|
|
|
|
}
|
|
|
|
|
2011-05-21 16:54:32 +00:00
|
|
|
return sfd;
|
|
|
|
}
|
|
|
|
|
2011-08-17 10:46:48 +00:00
|
|
|
/*! \brief Initialize a socket and fill \ref sockaddr
|
|
|
|
* \param[out] ss socket address (will be filled in)
|
|
|
|
* \param[in] type Socket type like SOCK_DGRAM, SOCK_STREAM
|
|
|
|
* \param[in] proto Protocol like IPPROTO_TCP, IPPROTO_UDP
|
|
|
|
* \param[in] flags flags like \ref OSMO_SOCK_F_CONNECT
|
|
|
|
*
|
|
|
|
* This function creates (and optionall binds/connects) a socket using
|
|
|
|
* \ref osmo_sock_init, but also fills the \a ss structure.
|
|
|
|
*/
|
2011-05-21 16:54:32 +00:00
|
|
|
int osmo_sock_init_sa(struct sockaddr *ss, uint16_t type,
|
2011-06-09 13:04:30 +00:00
|
|
|
uint8_t proto, unsigned int flags)
|
2011-05-21 16:54:32 +00:00
|
|
|
{
|
|
|
|
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;
|
|
|
|
}
|
|
|
|
|
|
|
|
s = getnameinfo(ss, sa_len, host, NI_MAXHOST,
|
|
|
|
NULL, 0, NI_NUMERICHOST);
|
|
|
|
if (s != 0) {
|
|
|
|
perror("getnameinfo failed");
|
|
|
|
return s;
|
|
|
|
}
|
|
|
|
|
2011-06-09 13:04:30 +00:00
|
|
|
return osmo_sock_init(ss->sa_family, type, proto, host, port, flags);
|
2011-05-21 16:54:32 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
static int sockaddr_equal(const struct sockaddr *a,
|
2011-05-22 10:25:57 +00:00
|
|
|
const struct sockaddr *b, unsigned int len)
|
2011-05-21 16:54:32 +00:00
|
|
|
{
|
|
|
|
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;
|
|
|
|
}
|
|
|
|
|
2011-08-17 10:46:48 +00:00
|
|
|
/*! \brief Determine if the given address is a local address
|
|
|
|
* \param[in] addr Socket Address
|
|
|
|
* \param[in] addrlen Length of socket address in bytes
|
|
|
|
* \returns 1 if address is local, 0 otherwise.
|
|
|
|
*/
|
2012-04-08 09:31:32 +00:00
|
|
|
int osmo_sockaddr_is_local(struct sockaddr *addr, unsigned int addrlen)
|
2011-05-21 16:54:32 +00:00
|
|
|
{
|
|
|
|
struct ifaddrs *ifaddr, *ifa;
|
|
|
|
|
|
|
|
if (getifaddrs(&ifaddr) == -1) {
|
|
|
|
perror("getifaddrs");
|
|
|
|
return -EIO;
|
|
|
|
}
|
|
|
|
|
|
|
|
for (ifa = ifaddr; ifa != NULL; ifa = ifa->ifa_next) {
|
2011-05-24 19:31:53 +00:00
|
|
|
if (!ifa->ifa_addr)
|
|
|
|
continue;
|
2011-05-21 16:54:32 +00:00
|
|
|
if (sockaddr_equal(ifa->ifa_addr, addr, addrlen))
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
2011-05-22 10:25:57 +00:00
|
|
|
|
|
|
|
#endif /* HAVE_SYS_SOCKET_H */
|
2011-08-17 10:46:48 +00:00
|
|
|
|
2012-04-18 19:53:23 +00:00
|
|
|
/*! @} */
|