socket-win: Implement a Windows socket plugin using Winsock2
This commit is contained in:
parent
8c55f8ef42
commit
fb0b539084
|
@ -206,6 +206,7 @@ ARG_ENABL_SET([kernel-klips], [enable the KLIPS kernel interface.])
|
||||||
ARG_ENABL_SET([kernel-libipsec],[enable the libipsec kernel interface.])
|
ARG_ENABL_SET([kernel-libipsec],[enable the libipsec kernel interface.])
|
||||||
ARG_DISBL_SET([socket-default], [disable default socket implementation for charon.])
|
ARG_DISBL_SET([socket-default], [disable default socket implementation for charon.])
|
||||||
ARG_ENABL_SET([socket-dynamic], [enable dynamic socket implementation for charon])
|
ARG_ENABL_SET([socket-dynamic], [enable dynamic socket implementation for charon])
|
||||||
|
ARG_ENABL_SET([socket-win], [enable Winsock2 based socket implementation for charon])
|
||||||
# configuration/control plugins
|
# configuration/control plugins
|
||||||
ARG_DISBL_SET([stroke], [disable charons stroke configuration backend.])
|
ARG_DISBL_SET([stroke], [disable charons stroke configuration backend.])
|
||||||
ARG_ENABL_SET([smp], [enable SMP configuration and control interface. Requires libxml.])
|
ARG_ENABL_SET([smp], [enable SMP configuration and control interface. Requires libxml.])
|
||||||
|
@ -1215,6 +1216,7 @@ ADD_PLUGIN([kernel-netlink], [h charon starter nm cmd])
|
||||||
ADD_PLUGIN([resolve], [h charon cmd])
|
ADD_PLUGIN([resolve], [h charon cmd])
|
||||||
ADD_PLUGIN([socket-default], [c charon nm cmd])
|
ADD_PLUGIN([socket-default], [c charon nm cmd])
|
||||||
ADD_PLUGIN([socket-dynamic], [c charon cmd])
|
ADD_PLUGIN([socket-dynamic], [c charon cmd])
|
||||||
|
ADD_PLUGIN([socket-win], [c charon])
|
||||||
ADD_PLUGIN([farp], [c charon])
|
ADD_PLUGIN([farp], [c charon])
|
||||||
ADD_PLUGIN([stroke], [c charon])
|
ADD_PLUGIN([stroke], [c charon])
|
||||||
ADD_PLUGIN([vici], [c charon])
|
ADD_PLUGIN([vici], [c charon])
|
||||||
|
@ -1417,6 +1419,7 @@ AM_CONDITIONAL(USE_IMC_SWID, test x$imc_swid = xtrue)
|
||||||
AM_CONDITIONAL(USE_IMV_SWID, test x$imv_swid = xtrue)
|
AM_CONDITIONAL(USE_IMV_SWID, test x$imv_swid = xtrue)
|
||||||
AM_CONDITIONAL(USE_SOCKET_DEFAULT, test x$socket_default = xtrue)
|
AM_CONDITIONAL(USE_SOCKET_DEFAULT, test x$socket_default = xtrue)
|
||||||
AM_CONDITIONAL(USE_SOCKET_DYNAMIC, test x$socket_dynamic = xtrue)
|
AM_CONDITIONAL(USE_SOCKET_DYNAMIC, test x$socket_dynamic = xtrue)
|
||||||
|
AM_CONDITIONAL(USE_SOCKET_WIN, test x$socket_win = xtrue)
|
||||||
AM_CONDITIONAL(USE_FARP, test x$farp = xtrue)
|
AM_CONDITIONAL(USE_FARP, test x$farp = xtrue)
|
||||||
AM_CONDITIONAL(USE_ADDRBLOCK, test x$addrblock = xtrue)
|
AM_CONDITIONAL(USE_ADDRBLOCK, test x$addrblock = xtrue)
|
||||||
AM_CONDITIONAL(USE_UNITY, test x$unity = xtrue)
|
AM_CONDITIONAL(USE_UNITY, test x$unity = xtrue)
|
||||||
|
@ -1646,6 +1649,7 @@ AC_CONFIG_FILES([
|
||||||
src/libcharon/plugins/tnc_pdp/Makefile
|
src/libcharon/plugins/tnc_pdp/Makefile
|
||||||
src/libcharon/plugins/socket_default/Makefile
|
src/libcharon/plugins/socket_default/Makefile
|
||||||
src/libcharon/plugins/socket_dynamic/Makefile
|
src/libcharon/plugins/socket_dynamic/Makefile
|
||||||
|
src/libcharon/plugins/socket_win/Makefile
|
||||||
src/libcharon/plugins/farp/Makefile
|
src/libcharon/plugins/farp/Makefile
|
||||||
src/libcharon/plugins/smp/Makefile
|
src/libcharon/plugins/smp/Makefile
|
||||||
src/libcharon/plugins/sql/Makefile
|
src/libcharon/plugins/sql/Makefile
|
||||||
|
|
|
@ -195,6 +195,13 @@ if MONOLITHIC
|
||||||
endif
|
endif
|
||||||
endif
|
endif
|
||||||
|
|
||||||
|
if USE_SOCKET_WIN
|
||||||
|
SUBDIRS += plugins/socket_win
|
||||||
|
if MONOLITHIC
|
||||||
|
libcharon_la_LIBADD += plugins/socket_win/libstrongswan-socket-win.la
|
||||||
|
endif
|
||||||
|
endif
|
||||||
|
|
||||||
if USE_FARP
|
if USE_FARP
|
||||||
SUBDIRS += plugins/farp
|
SUBDIRS += plugins/farp
|
||||||
if MONOLITHIC
|
if MONOLITHIC
|
||||||
|
|
|
@ -0,0 +1,21 @@
|
||||||
|
AM_CPPFLAGS = \
|
||||||
|
-I${linux_headers} \
|
||||||
|
-I$(top_srcdir)/src/libstrongswan \
|
||||||
|
-I$(top_srcdir)/src/libhydra \
|
||||||
|
-I$(top_srcdir)/src/libcharon
|
||||||
|
|
||||||
|
AM_CFLAGS = \
|
||||||
|
$(PLUGIN_CFLAGS)
|
||||||
|
|
||||||
|
if MONOLITHIC
|
||||||
|
noinst_LTLIBRARIES = libstrongswan-socket-win.la
|
||||||
|
else
|
||||||
|
plugin_LTLIBRARIES = libstrongswan-socket-win.la
|
||||||
|
endif
|
||||||
|
|
||||||
|
libstrongswan_socket_win_la_SOURCES = \
|
||||||
|
socket_win_socket.h socket_win_socket.c \
|
||||||
|
socket_win_plugin.h socket_win_plugin.c
|
||||||
|
|
||||||
|
libstrongswan_socket_win_la_LDFLAGS = -module -avoid-version
|
||||||
|
libstrongswan_socket_win_la_LIBADD = -lws2_32
|
|
@ -0,0 +1,75 @@
|
||||||
|
/*
|
||||||
|
* 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.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "socket_win_plugin.h"
|
||||||
|
#include "socket_win_socket.h"
|
||||||
|
|
||||||
|
#include <daemon.h>
|
||||||
|
|
||||||
|
typedef struct private_socket_win_plugin_t private_socket_win_plugin_t;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Private data of socket plugin
|
||||||
|
*/
|
||||||
|
struct private_socket_win_plugin_t {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Implements plugin interface
|
||||||
|
*/
|
||||||
|
socket_win_plugin_t public;
|
||||||
|
};
|
||||||
|
|
||||||
|
METHOD(plugin_t, get_name, char*,
|
||||||
|
private_socket_win_plugin_t *this)
|
||||||
|
{
|
||||||
|
return "socket-win";
|
||||||
|
}
|
||||||
|
|
||||||
|
METHOD(plugin_t, destroy, void,
|
||||||
|
private_socket_win_plugin_t *this)
|
||||||
|
{
|
||||||
|
free(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
METHOD(plugin_t, get_features, int,
|
||||||
|
private_socket_win_plugin_t *this, plugin_feature_t *features[])
|
||||||
|
{
|
||||||
|
static plugin_feature_t f[] = {
|
||||||
|
PLUGIN_CALLBACK(socket_register, socket_win_socket_create),
|
||||||
|
PLUGIN_PROVIDE(CUSTOM, "socket"),
|
||||||
|
};
|
||||||
|
*features = f;
|
||||||
|
return countof(f);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create instance of socket-win plugin
|
||||||
|
*/
|
||||||
|
plugin_t *socket_win_plugin_create()
|
||||||
|
{
|
||||||
|
private_socket_win_plugin_t *this;
|
||||||
|
|
||||||
|
INIT(this,
|
||||||
|
.public = {
|
||||||
|
.plugin = {
|
||||||
|
.get_name = _get_name,
|
||||||
|
.get_features = _get_features,
|
||||||
|
.destroy = _destroy,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|
||||||
|
return &this->public.plugin;
|
||||||
|
}
|
|
@ -0,0 +1,42 @@
|
||||||
|
/*
|
||||||
|
* 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.
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @defgroup socket_win socket_win
|
||||||
|
* @ingroup cplugins
|
||||||
|
*
|
||||||
|
* @defgroup socket_win_plugin socket_win_plugin
|
||||||
|
* @{ @ingroup socket_win
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef SOCKET_WIN_PLUGIN_H_
|
||||||
|
#define SOCKET_WIN_PLUGIN_H_
|
||||||
|
|
||||||
|
#include <plugins/plugin.h>
|
||||||
|
|
||||||
|
typedef struct socket_win_plugin_t socket_win_plugin_t;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Winsock2 based socket implementation plugin.
|
||||||
|
*/
|
||||||
|
struct socket_win_plugin_t {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* implements plugin interface
|
||||||
|
*/
|
||||||
|
plugin_t plugin;
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif /** SOCKET_WIN_PLUGIN_H_ @}*/
|
|
@ -0,0 +1,508 @@
|
||||||
|
/*
|
||||||
|
* 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 <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 (*WSASendMsg)(SOCKET, LPWSAMSG, DWORD, LPDWORD,
|
||||||
|
LPWSAOVERLAPPED, LPWSAOVERLAPPED_COMPLETION_ROUTINE);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* WSARecvMsg function
|
||||||
|
*/
|
||||||
|
int (*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;
|
||||||
|
}
|
||||||
|
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;
|
||||||
|
}
|
|
@ -0,0 +1,44 @@
|
||||||
|
/*
|
||||||
|
* 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.
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @defgroup socket_win_socket socket_win_socket
|
||||||
|
* @{ @ingroup socket_win
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef SOCKET_WIN_SOCKET_H_
|
||||||
|
#define SOCKET_WIN_SOCKET_H_
|
||||||
|
|
||||||
|
typedef struct socket_win_socket_t socket_win_socket_t;
|
||||||
|
|
||||||
|
#include <network/socket.h>
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Winsock2 based socket implementation.
|
||||||
|
*/
|
||||||
|
struct socket_win_socket_t {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Implements the socket_t interface.
|
||||||
|
*/
|
||||||
|
socket_t socket;
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create a socket_win_socket instance.
|
||||||
|
*/
|
||||||
|
socket_win_socket_t *socket_win_socket_create();
|
||||||
|
|
||||||
|
#endif /** SOCKET_WIN_SOCKET_H_ @}*/
|
Loading…
Reference in New Issue