strongswan/src/libcharon/plugins/socket_win/socket_win_socket.c

520 lines
12 KiB
C

/*
* Copyright (C) 2013 Martin Willi
* Copyright (C) 2013 revosec AG
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by the
* Free Software Foundation; either version 2 of the License, or (at your
* option) any later version. See <http://www.fsf.org/copyleft/gpl.txt>.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* for more details.
*/
/* for WSAID_WSASENDMSG, Windows 7 */
#define _WIN32_WINNT 0x0601
#include "socket_win_socket.h"
#include <library.h>
#include <hydra.h>
#include <threading/thread.h>
#include <daemon.h>
#include <mswsock.h>
/* Maximum size of a packet */
#define MAX_PACKET 10000
/* number of sockets in use */
#define SOCKET_COUNT 2
/* missing on MinGW */
#ifndef IPV6_V6ONLY
# define IPV6_V6ONLY 27
#endif
/* GUIDS to lookup WSASend/RecvMsg */
static GUID WSARecvMsgGUID = WSAID_WSARECVMSG;
static GUID WSASendMsgGUID = WSAID_WSASENDMSG;
typedef struct private_socket_win_socket_t private_socket_win_socket_t;
/**
* Private data of an socket_t object
*/
struct private_socket_win_socket_t {
/**
* public functions
*/
socket_win_socket_t public;
/**
* Port for each socket
*/
u_int16_t ports[SOCKET_COUNT];
/**
* IPv4/IPv6 dual-use sockets
*/
SOCKET socks[SOCKET_COUNT];
/**
* Events to wait for socket data
*/
HANDLE events[SOCKET_COUNT];
/**
* Maximum packet size to receive
*/
int max_packet;
/**
* WSASendMsg function
*/
int WINAPI (*WSASendMsg)(SOCKET, LPWSAMSG, DWORD, LPDWORD,
LPWSAOVERLAPPED, LPWSAOVERLAPPED_COMPLETION_ROUTINE);
/**
* WSARecvMsg function
*/
int WINAPI (*WSARecvMsg)(SOCKET, LPWSAMSG, LPDWORD,
LPWSAOVERLAPPED, LPWSAOVERLAPPED_COMPLETION_ROUTINE);
};
METHOD(socket_t, receiver, status_t,
private_socket_win_socket_t *this, packet_t **out)
{
WSAOVERLAPPED overlapped[SOCKET_COUNT] = {};
char buf[this->max_packet], cbuf[128];
bool old;
DWORD i, len, flags, err;
WSAMSG msg;
WSABUF data;
WSACMSGHDR *cmsg;
SOCKADDR_IN6 addr;
host_t *src = NULL, *dst = NULL;
packet_t *pkt;
data.buf = buf;
data.len = sizeof(buf);
memset(&msg, 0, sizeof(msg));
msg.name = (struct sockaddr*)&addr;
msg.namelen = sizeof(addr);
msg.lpBuffers = &data;
msg.dwBufferCount = 1;
msg.Control.buf = cbuf;
msg.Control.len = sizeof(cbuf);
for (i = 0; i < SOCKET_COUNT; i++)
{
overlapped[i].hEvent = this->events[i];
if (this->socks[i] != INVALID_SOCKET)
{
if (this->WSARecvMsg(this->socks[i], &msg, NULL,
&overlapped[i], NULL) == SOCKET_ERROR)
{
err = WSAGetLastError();
if (err != WSA_IO_PENDING)
{
DBG1(DBG_NET, "reading from socket failed: %d", err);
return FAILED;
}
}
}
}
old = thread_cancelability(TRUE);
i = WSAWaitForMultipleEvents(SOCKET_COUNT, this->events,
FALSE, INFINITE, TRUE);
thread_cancelability(old);
if (i < WSA_WAIT_EVENT_0 || i >= WSA_WAIT_EVENT_0 + SOCKET_COUNT)
{
DBG1(DBG_NET, "waiting on sockets failed: %d", WSAGetLastError());
return FAILED;
}
i -= WSA_WAIT_EVENT_0;
/* WSAEvents must be reset manually */
WSAResetEvent(this->events[i]);
if (!WSAGetOverlappedResult(this->socks[i], &overlapped[i],
&len, FALSE, &flags))
{
err = WSAGetLastError();
/* ignore WSAECONNRESET; this is returned for any ICMP port unreachable,
* for a packet we sent, but is most likely not related to the packet
* we try to receive. */
if (err != WSAECONNRESET)
{
DBG1(DBG_NET, "getting socket result failed: %d", err);
}
return FAILED;
}
if (len >= sizeof(buf))
{
DBG1(DBG_NET, "receive buffer too small, packet discarded");
return FAILED;
}
DBG3(DBG_NET, "received packet %b", buf, (int)len);
for (cmsg = WSA_CMSG_FIRSTHDR(&msg); dst == NULL && cmsg != NULL;
cmsg = WSA_CMSG_NXTHDR(&msg, cmsg))
{
if (cmsg->cmsg_level == IPPROTO_IP &&
cmsg->cmsg_type == IP_PKTINFO)
{
struct in_pktinfo *pktinfo;
struct sockaddr_in sin = {
.sin_family = AF_INET,
};
pktinfo = (struct in_pktinfo*)WSA_CMSG_DATA(cmsg);
sin.sin_addr = pktinfo->ipi_addr;
sin.sin_port = htons(this->ports[i]);
dst = host_create_from_sockaddr((struct sockaddr*)&sin);
}
if (cmsg->cmsg_level == IPPROTO_IPV6 &&
cmsg->cmsg_type == IPV6_PKTINFO)
{
struct in6_pktinfo *pktinfo;
struct sockaddr_in6 sin = {
.sin6_family = AF_INET6,
};
pktinfo = (struct in6_pktinfo*)WSA_CMSG_DATA(cmsg);
sin.sin6_addr = pktinfo->ipi6_addr;
sin.sin6_port = htons(this->ports[i]);
dst = host_create_from_sockaddr((struct sockaddr*)&sin);
}
}
if (!dst)
{
DBG1(DBG_NET, "receiving IP destination address failed");
return FAILED;
}
switch (dst->get_family(dst))
{
case AF_INET6:
src = host_create_from_sockaddr((struct sockaddr*)&addr);
break;
case AF_INET:
/* extract v4 address from mapped v6 */
src = host_create_from_chunk(AF_INET,
chunk_create(addr.sin6_addr.u.Byte + 12, 4),
ntohs(addr.sin6_port));
break;
}
if (!src)
{
DBG1(DBG_NET, "receiving IP source address failed");
dst->destroy(dst);
return FAILED;
}
pkt = packet_create();
pkt->set_source(pkt, src);
pkt->set_destination(pkt, dst);
DBG2(DBG_NET, "received packet: from %#H to %#H", src, dst);
pkt->set_data(pkt, chunk_clone(chunk_create(buf, len)));
*out = pkt;
return SUCCESS;
}
METHOD(socket_t, sender, status_t,
private_socket_win_socket_t *this, packet_t *packet)
{
u_int16_t port;
int i = -1, j;
host_t *src, *dst;
WSAMSG msg;
DWORD len;
WSABUF data;
WSACMSGHDR *cmsg;
SOCKADDR_IN6 addr = {
.sin6_family = AF_INET6,
.sin6_addr = {
.u = {
.Byte = {
/* v6-mapped-v4 by default */
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0xFF,0xFF,0x00,0x00,0x00,0x00,
},
},
},
};
char buf[WSA_CMSG_SPACE(max(sizeof(struct in6_pktinfo),
sizeof(struct in_pktinfo)))];
src = packet->get_source(packet);
dst = packet->get_destination(packet);
data.len = packet->get_data(packet).len;
data.buf = packet->get_data(packet).ptr;
DBG2(DBG_NET, "sending packet: from %#H to %#H", src, dst);
DBG3(DBG_NET, "sending packet %b", data.buf, (int)data.len);
port = src->get_port(src);
for (j = 0; j < SOCKET_COUNT; j++)
{
if (!port || this->ports[j] == port)
{
i = j;
break;
}
}
if (i == -1)
{
DBG1(DBG_NET, "no socket found to send packet from port %u", port);
return FAILED;
}
/* copy destination IPv6, or last 32 bits of mapped IPv4 address */
len = dst->get_address(dst).len;
if (len > sizeof(addr.sin6_addr))
{
return FAILED;
}
memcpy(addr.sin6_addr.u.Byte + sizeof(addr.sin6_addr) - len,
dst->get_address(dst).ptr, len);
addr.sin6_port = htons(dst->get_port(dst));
memset(&msg, 0, sizeof(msg));
msg.name = (struct sockaddr*)&addr;
msg.namelen = sizeof(addr);
msg.lpBuffers = &data;
msg.dwBufferCount = 1;
if (!src->is_anyaddr(src))
{
memset(buf, 0, sizeof(buf));
msg.Control.buf = buf;
switch (src->get_family(src))
{
case AF_INET:
{
struct in_pktinfo *pktinfo;
SOCKADDR_IN *sin;
msg.Control.len = WSA_CMSG_SPACE(sizeof(*pktinfo));
cmsg = WSA_CMSG_FIRSTHDR(&msg);
cmsg->cmsg_level = IPPROTO_IP;
cmsg->cmsg_type = IP_PKTINFO;
cmsg->cmsg_len = WSA_CMSG_LEN(sizeof(*pktinfo));
pktinfo = (struct in_pktinfo*)WSA_CMSG_DATA(cmsg);
sin = (SOCKADDR_IN*)src->get_sockaddr(src);
pktinfo->ipi_addr = sin->sin_addr;
break;
}
case AF_INET6:
{
struct in6_pktinfo *pktinfo;
SOCKADDR_IN6 *sin;
msg.Control.len = WSA_CMSG_SPACE(sizeof(*pktinfo));
cmsg = WSA_CMSG_FIRSTHDR(&msg);
cmsg->cmsg_level = IPPROTO_IPV6;
cmsg->cmsg_type = IPV6_PKTINFO;
cmsg->cmsg_len = WSA_CMSG_LEN(sizeof(*pktinfo));
pktinfo = (struct in6_pktinfo*)WSA_CMSG_DATA(cmsg);
sin = (SOCKADDR_IN6*)src->get_sockaddr(src);
pktinfo->ipi6_addr = sin->sin6_addr;
break;
}
}
}
if (this->WSASendMsg(this->socks[i], &msg, 0, &len,
NULL, NULL) == SOCKET_ERROR)
{
DBG1(DBG_NET, "sending packet failed: %d", WSAGetLastError());
return FAILED;
}
return SUCCESS;
}
METHOD(socket_t, get_port, u_int16_t,
private_socket_win_socket_t *this, bool nat)
{
return this->ports[nat != 0];
}
METHOD(socket_t, supported_families, socket_family_t,
private_socket_win_socket_t *this)
{
return SOCKET_FAMILY_IPV4 | SOCKET_FAMILY_IPV6;
}
/**
* Open an IPv4/IPv6 dual-use socket to send and receive packets
*/
static SOCKET open_socket(private_socket_win_socket_t *this, int i)
{
SOCKADDR_IN6 addr = {
.sin6_family = AF_INET6,
.sin6_port = htons(this->ports[i]),
};
int addrlen = sizeof(addr);
BOOL on = TRUE, off = FALSE;
DWORD dwon = TRUE;
SOCKET s;
s = WSASocket(AF_INET6, SOCK_DGRAM, IPPROTO_UDP,
NULL, 0, WSA_FLAG_OVERLAPPED);
if (s == INVALID_SOCKET)
{
DBG1(DBG_NET, "creating socket failed: %d", WSAGetLastError());
return INVALID_SOCKET;
}
/* enable IPv4 on IPv6 socket */
if (setsockopt(s, IPPROTO_IPV6, IPV6_V6ONLY,
(const char*)&off, sizeof(off)) == SOCKET_ERROR)
{
DBG1(DBG_NET, "using dual-mode socket failed: %d", WSAGetLastError());
closesocket(s);
return INVALID_SOCKET;
}
if (setsockopt(s, SOL_SOCKET, SO_REUSEADDR,
(const char*)&on, sizeof(on)) == SOCKET_ERROR)
{
DBG1(DBG_NET, "enabling SO_REUSEADDR failed: %d", WSAGetLastError());
closesocket(s);
return INVALID_SOCKET;
}
if (bind(s, (const struct sockaddr*)&addr, addrlen) == SOCKET_ERROR)
{
DBG1(DBG_NET, "unable to bind socket: %d", WSAGetLastError());
closesocket(s);
return INVALID_SOCKET;
}
/* retrieve randomly allocated port if needed */
if (this->ports[i] == 0)
{
if (getsockname(s, (struct sockaddr*)&addr,
&addrlen) == SOCKET_ERROR)
{
DBG1(DBG_NET, "unable to determine port: %d", WSAGetLastError());
closesocket(s);
return INVALID_SOCKET;
}
this->ports[i] = ntohs(addr.sin6_port);
}
/* PKTINFO is required for both protocol families */
if (setsockopt(s, IPPROTO_IP, IP_PKTINFO,
(char*)&dwon, sizeof(dwon)) == SOCKET_ERROR)
{
DBG1(DBG_NET, "unable to set IP_PKTINFO: %d", WSAGetLastError());
closesocket(s);
return INVALID_SOCKET;
}
if (setsockopt(s, IPPROTO_IPV6, IPV6_PKTINFO,
(char*)&dwon, sizeof(dwon)) == SOCKET_ERROR)
{
DBG1(DBG_NET, "unable to set IP6_PKTINFO: %d", WSAGetLastError());
closesocket(s);
return INVALID_SOCKET;
}
if (!hydra->kernel_interface->bypass_socket(hydra->kernel_interface,
s, AF_INET))
{
DBG1(DBG_NET, "installing IPv4 IKE bypass policy failed");
}
if (!hydra->kernel_interface->bypass_socket(hydra->kernel_interface,
s, AF_INET6))
{
DBG1(DBG_NET, "installing IPv6 IKE bypass policy failed");
}
return s;
}
METHOD(socket_t, destroy, void,
private_socket_win_socket_t *this)
{
int i;
for (i = 0; i < SOCKET_COUNT; i++)
{
if (this->socks[i] != INVALID_SOCKET)
{
closesocket(this->socks[i]);
}
if (this->events[i] != WSA_INVALID_EVENT)
{
WSACloseEvent(this->events[i]);
}
}
free(this);
}
/*
* See header for description
*/
socket_win_socket_t *socket_win_socket_create()
{
private_socket_win_socket_t *this;
DWORD len;
int i;
INIT(this,
.public = {
.socket = {
.send = _sender,
.receive = _receiver,
.get_port = _get_port,
.supported_families = _supported_families,
.destroy = _destroy,
},
},
.ports = {
lib->settings->get_int(lib->settings,
"%s.port", CHARON_UDP_PORT, lib->ns),
lib->settings->get_int(lib->settings,
"%s.port_nat_t", CHARON_NATT_PORT, lib->ns),
},
.max_packet = lib->settings->get_int(lib->settings,
"%s.max_packet", MAX_PACKET, lib->ns),
);
for (i = 0; i < SOCKET_COUNT; i++)
{
this->socks[i] = open_socket(this, i);
this->events[i] = WSACreateEvent();
}
for (i = 0; i < SOCKET_COUNT; i++)
{
if (this->events[i] == WSA_INVALID_EVENT ||
this->socks[i] == INVALID_SOCKET)
{
DBG1(DBG_NET, "creating socket failed: %d", WSAGetLastError());
destroy(this);
return NULL;
}
}
if (WSAIoctl(this->socks[0], SIO_GET_EXTENSION_FUNCTION_POINTER,
&WSASendMsgGUID, sizeof(WSASendMsgGUID), &this->WSASendMsg,
sizeof(this->WSASendMsg), &len, NULL, NULL) != 0 ||
WSAIoctl(this->socks[0], SIO_GET_EXTENSION_FUNCTION_POINTER,
&WSARecvMsgGUID, sizeof(WSARecvMsgGUID), &this->WSARecvMsg,
sizeof(this->WSARecvMsg), &len, NULL, NULL) != 0)
{
DBG1(DBG_NET, "send/recvmsg() lookup failed: %d", WSAGetLastError());
destroy(this);
return NULL;
}
return &this->public;
}