socket-dynamic: when sending from port zero, allocate a free port dynamically
This commit is contained in:
@@ -325,14 +325,61 @@ METHOD(socket_t, receiver, status_t,
|
||||
return FAILED;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the port allocated dynamically using bind()
|
||||
*/
|
||||
static bool get_dynamic_port(int fd, int family, u_int16_t *port)
|
||||
{
|
||||
union {
|
||||
struct sockaddr_storage ss;
|
||||
struct sockaddr s;
|
||||
struct sockaddr_in sin;
|
||||
struct sockaddr_in6 sin6;
|
||||
} addr;
|
||||
socklen_t addrlen;
|
||||
|
||||
addrlen = sizeof(addr);
|
||||
if (getsockname(fd, &addr.s, &addrlen) != 0)
|
||||
{
|
||||
DBG1(DBG_NET, "unable to getsockname: %s", strerror(errno));
|
||||
return FALSE;
|
||||
}
|
||||
switch (family)
|
||||
{
|
||||
case AF_INET:
|
||||
if (addrlen != sizeof(addr.sin) || addr.sin.sin_family != family)
|
||||
{
|
||||
break;
|
||||
}
|
||||
*port = ntohs(addr.sin.sin_port);
|
||||
return TRUE;
|
||||
case AF_INET6:
|
||||
if (addrlen != sizeof(addr.sin6) || addr.sin6.sin6_family != family)
|
||||
{
|
||||
break;
|
||||
}
|
||||
*port = ntohs(addr.sin6.sin6_port);
|
||||
return TRUE;
|
||||
default:
|
||||
return FALSE;
|
||||
}
|
||||
DBG1(DBG_NET, "received invalid getsockname() result");
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
/**
|
||||
* open a socket to send and receive packets
|
||||
*/
|
||||
static int open_socket(private_socket_dynamic_socket_t *this,
|
||||
int family, u_int16_t port)
|
||||
int family, u_int16_t *port)
|
||||
{
|
||||
union {
|
||||
struct sockaddr_storage ss;
|
||||
struct sockaddr s;
|
||||
struct sockaddr_in sin;
|
||||
struct sockaddr_in6 sin6;
|
||||
} addr;
|
||||
int on = TRUE;
|
||||
struct sockaddr_storage addr;
|
||||
socklen_t addrlen;
|
||||
u_int sol, pktinfo = 0;
|
||||
int fd;
|
||||
@@ -342,27 +389,21 @@ static int open_socket(private_socket_dynamic_socket_t *this,
|
||||
switch (family)
|
||||
{
|
||||
case AF_INET:
|
||||
{
|
||||
struct sockaddr_in *sin = (struct sockaddr_in *)&addr;
|
||||
sin->sin_family = AF_INET;
|
||||
sin->sin_addr.s_addr = INADDR_ANY;
|
||||
sin->sin_port = htons(port);
|
||||
addrlen = sizeof(struct sockaddr_in);
|
||||
addr.sin.sin_family = AF_INET;
|
||||
addr.sin.sin_addr.s_addr = INADDR_ANY;
|
||||
addr.sin.sin_port = htons(*port);
|
||||
addrlen = sizeof(addr.sin);
|
||||
sol = SOL_IP;
|
||||
pktinfo = IP_PKTINFO;
|
||||
break;
|
||||
}
|
||||
case AF_INET6:
|
||||
{
|
||||
struct sockaddr_in6 *sin6 = (struct sockaddr_in6 *)&addr;
|
||||
sin6->sin6_family = AF_INET6;
|
||||
memset(&sin6->sin6_addr, 0, sizeof(sin6->sin6_addr));
|
||||
sin6->sin6_port = htons(port);
|
||||
addrlen = sizeof(struct sockaddr_in6);
|
||||
addr.sin6.sin6_family = AF_INET6;
|
||||
memset(&addr.sin6.sin6_addr, 0, sizeof(addr.sin6));
|
||||
addr.sin6.sin6_port = htons(*port);
|
||||
addrlen = sizeof(addr.sin6);
|
||||
sol = SOL_IPV6;
|
||||
pktinfo = IPV6_RECVPKTINFO;
|
||||
break;
|
||||
}
|
||||
default:
|
||||
return 0;
|
||||
}
|
||||
@@ -380,13 +421,17 @@ static int open_socket(private_socket_dynamic_socket_t *this,
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* bind the socket */
|
||||
if (bind(fd, (struct sockaddr *)&addr, addrlen) < 0)
|
||||
if (bind(fd, &addr.s, addrlen) < 0)
|
||||
{
|
||||
DBG1(DBG_NET, "unable to bind socket: %s", strerror(errno));
|
||||
close(fd);
|
||||
return 0;
|
||||
}
|
||||
if (*port == 0 && !get_dynamic_port(fd, family, port))
|
||||
{
|
||||
close(fd);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* get additional packet info on receive */
|
||||
if (setsockopt(fd, sol, pktinfo, &on, sizeof(on)) < 0)
|
||||
@@ -404,15 +449,40 @@ static int open_socket(private_socket_dynamic_socket_t *this,
|
||||
|
||||
/* enable UDP decapsulation on each socket */
|
||||
if (!hydra->kernel_interface->enable_udp_decap(hydra->kernel_interface,
|
||||
fd, family, port))
|
||||
fd, family, *port))
|
||||
{
|
||||
DBG1(DBG_NET, "enabling UDP decapsulation for %s on port %d failed",
|
||||
family == AF_INET ? "IPv4" : "IPv6", port);
|
||||
family == AF_INET ? "IPv4" : "IPv6", *port);
|
||||
}
|
||||
|
||||
return fd;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the first usable socket for an address family
|
||||
*/
|
||||
static dynsock_t *get_any_socket(private_socket_dynamic_socket_t *this,
|
||||
int family)
|
||||
{
|
||||
dynsock_t *key, *value, *found = NULL;
|
||||
enumerator_t *enumerator;
|
||||
|
||||
this->lock->read_lock(this->lock);
|
||||
enumerator = this->sockets->create_enumerator(this->sockets);
|
||||
while (enumerator->enumerate(enumerator, &key, &value))
|
||||
{
|
||||
if (value->family == family)
|
||||
{
|
||||
found = value;
|
||||
break;
|
||||
}
|
||||
}
|
||||
enumerator->destroy(enumerator);
|
||||
this->lock->unlock(this->lock);
|
||||
|
||||
return found;
|
||||
}
|
||||
|
||||
/**
|
||||
* Find/Create a socket to send from host
|
||||
*/
|
||||
@@ -433,7 +503,15 @@ static dynsock_t *find_socket(private_socket_dynamic_socket_t *this,
|
||||
{
|
||||
return skt;
|
||||
}
|
||||
fd = open_socket(this, family, port);
|
||||
if (!port)
|
||||
{
|
||||
skt = get_any_socket(this, family);
|
||||
if (skt)
|
||||
{
|
||||
return skt;
|
||||
}
|
||||
}
|
||||
fd = open_socket(this, family, &port);
|
||||
if (!fd)
|
||||
{
|
||||
return NULL;
|
||||
@@ -457,7 +535,7 @@ METHOD(socket_t, sender, status_t,
|
||||
{
|
||||
dynsock_t *skt;
|
||||
host_t *src, *dst;
|
||||
int port, family;
|
||||
int family;
|
||||
ssize_t len;
|
||||
chunk_t data;
|
||||
struct msghdr msg;
|
||||
@@ -467,9 +545,7 @@ METHOD(socket_t, sender, status_t,
|
||||
src = packet->get_source(packet);
|
||||
dst = packet->get_destination(packet);
|
||||
family = src->get_family(src);
|
||||
port = src->get_port(src);
|
||||
port = port ?: CHARON_UDP_PORT;
|
||||
skt = find_socket(this, family, port);
|
||||
skt = find_socket(this, family, src->get_port(src));
|
||||
if (!skt)
|
||||
{
|
||||
return FAILED;
|
||||
@@ -597,4 +673,3 @@ socket_dynamic_socket_t *socket_dynamic_socket_create()
|
||||
|
||||
return &this->public;
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user