1020 lines
22 KiB
C
1020 lines
22 KiB
C
|
/*++
|
||
|
|
||
|
Copyright (c) 2001-2002 Microsoft Corporation
|
||
|
|
||
|
Module Name:
|
||
|
|
||
|
io.c
|
||
|
|
||
|
Abstract:
|
||
|
|
||
|
This module contains teredo I/O management functions.
|
||
|
|
||
|
Socket management, overlapped completion indication, and buffer management
|
||
|
ideas were originally implemented for tftpd by JeffV.
|
||
|
|
||
|
Author:
|
||
|
|
||
|
Mohit Talwar (mohitt) Wed Oct 24 14:05:36 2001
|
||
|
|
||
|
Environment:
|
||
|
|
||
|
User mode only.
|
||
|
|
||
|
--*/
|
||
|
|
||
|
#include "precomp.h"
|
||
|
#pragma hdrstop
|
||
|
|
||
|
|
||
|
WCHAR TeredoTunnelDeviceName[] = L"\\\\.\\\\Tun0";
|
||
|
|
||
|
|
||
|
DWORD
|
||
|
GetPreferredSourceAddress(
|
||
|
IN PSOCKADDR_IN Destination,
|
||
|
OUT PSOCKADDR_IN Source
|
||
|
)
|
||
|
{
|
||
|
int BytesReturned;
|
||
|
|
||
|
if (WSAIoctl(
|
||
|
g_sIPv4Socket, SIO_ROUTING_INTERFACE_QUERY,
|
||
|
Destination, sizeof(SOCKADDR_IN), Source, sizeof(SOCKADDR_IN),
|
||
|
&BytesReturned, NULL, NULL) == SOCKET_ERROR) {
|
||
|
return WSAGetLastError();
|
||
|
}
|
||
|
|
||
|
//
|
||
|
// When the source is local, the node is configured as the teredo server.
|
||
|
// Hence it needs to explicitly specify the port to bind to. Assign here!
|
||
|
//
|
||
|
if ((Source->sin_addr.s_addr == Destination->sin_addr.s_addr) ||
|
||
|
(Source->sin_addr.s_addr == htonl(INADDR_LOOPBACK))) {
|
||
|
*Source = *Destination;
|
||
|
}
|
||
|
|
||
|
return NO_ERROR;
|
||
|
}
|
||
|
|
||
|
|
||
|
__inline
|
||
|
DWORD
|
||
|
TeredoResolveServer(
|
||
|
IN PTEREDO_IO TeredoIo
|
||
|
)
|
||
|
/*++
|
||
|
|
||
|
Routine Description:
|
||
|
|
||
|
Resolve the teredo IPv4 server address and UDP service port.
|
||
|
|
||
|
Arguments:
|
||
|
|
||
|
TeredoIo - Supplies the I/O state.
|
||
|
|
||
|
Return Value:
|
||
|
|
||
|
NO_ERROR or failure code.
|
||
|
|
||
|
--*/
|
||
|
{
|
||
|
struct addrinfo *Addresses;
|
||
|
DWORD Error;
|
||
|
|
||
|
//
|
||
|
// Resolve the teredo server name.
|
||
|
//
|
||
|
Error = GetAddrInfoW(TeredoServerName, NULL, NULL, &Addresses);
|
||
|
if (Error == NO_ERROR) {
|
||
|
Error = ERROR_INCORRECT_ADDRESS;
|
||
|
|
||
|
if (Addresses->ai_family == AF_INET) {
|
||
|
TeredoIo->ServerAddress.sin_addr =
|
||
|
((PSOCKADDR_IN) Addresses->ai_addr)->sin_addr;
|
||
|
TeredoIo->ServerAddress.sin_port = TEREDO_PORT;
|
||
|
Error = NO_ERROR;
|
||
|
} else if (Addresses->ai_family == AF_INET6) {
|
||
|
PIN6_ADDR Ipv6Address;
|
||
|
IN_ADDR Ipv4Address;
|
||
|
USHORT Port;
|
||
|
|
||
|
//
|
||
|
// Extract server's IPv4 address and port from the IPv6 address.
|
||
|
//
|
||
|
Ipv6Address = &(((PSOCKADDR_IN6) Addresses->ai_addr)->sin6_addr);
|
||
|
if (TeredoServicePrefix(Ipv6Address)) {
|
||
|
TeredoParseAddress(Ipv6Address, &Ipv4Address, &Port);
|
||
|
if (Port == TEREDO_PORT) {
|
||
|
TeredoIo->ServerAddress.sin_addr = Ipv4Address;
|
||
|
TeredoIo->ServerAddress.sin_port = Port;
|
||
|
Error = NO_ERROR;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
freeaddrinfo(Addresses);
|
||
|
}
|
||
|
|
||
|
return Error;
|
||
|
}
|
||
|
|
||
|
|
||
|
PTEREDO_PACKET
|
||
|
TeredoCreatePacket(
|
||
|
IN PTEREDO_IO TeredoIo
|
||
|
)
|
||
|
/*++
|
||
|
|
||
|
Routine Description:
|
||
|
|
||
|
Creates a teredo packet.
|
||
|
|
||
|
Arguments:
|
||
|
|
||
|
TeredoIo - Supplies the I/O state.
|
||
|
|
||
|
Return Value:
|
||
|
|
||
|
Returns the created packet.
|
||
|
|
||
|
--*/
|
||
|
{
|
||
|
PTEREDO_PACKET Packet = (PTEREDO_PACKET) HeapAlloc(
|
||
|
TeredoIo->PacketHeap, 0, sizeof(TEREDO_PACKET) + IPV6_TEREDOMTU);
|
||
|
if (Packet == NULL) {
|
||
|
return NULL;
|
||
|
}
|
||
|
|
||
|
TeredoInitializePacket(Packet);
|
||
|
Packet->Buffer.len = IPV6_TEREDOMTU;
|
||
|
|
||
|
//
|
||
|
// Obtain a reference on the teredo object for each outstanding packet.
|
||
|
//
|
||
|
(*TeredoIo->Reference)();
|
||
|
|
||
|
return Packet;
|
||
|
}
|
||
|
|
||
|
|
||
|
VOID
|
||
|
TeredoDestroyPacket(
|
||
|
IN PTEREDO_IO TeredoIo,
|
||
|
IN PTEREDO_PACKET Packet
|
||
|
)
|
||
|
/*++
|
||
|
|
||
|
Routine Description:
|
||
|
|
||
|
Destroys a teredo packet.
|
||
|
|
||
|
Arguments:
|
||
|
|
||
|
TeredoIo - Supplies the I/O state.
|
||
|
|
||
|
Packet - Supplies the packet to destroy.
|
||
|
|
||
|
Return Value:
|
||
|
|
||
|
None.
|
||
|
|
||
|
--*/
|
||
|
{
|
||
|
ASSERT(Packet->Type != TEREDO_PACKET_BUBBLE);
|
||
|
ASSERT(Packet->Type != TEREDO_PACKET_MULTICAST);
|
||
|
HeapFree(TeredoIo->PacketHeap, 0, Packet);
|
||
|
(*TeredoIo->Dereference)();
|
||
|
}
|
||
|
|
||
|
|
||
|
ULONG
|
||
|
TeredoPostReceives(
|
||
|
IN PTEREDO_IO TeredoIo,
|
||
|
IN PTEREDO_PACKET Packet OPTIONAL
|
||
|
)
|
||
|
/*++
|
||
|
|
||
|
Routine Description:
|
||
|
|
||
|
Post an asynchronous receive request on the UDP socket.
|
||
|
|
||
|
NOTE: The supplied packet (if any) is destroyed if there are already
|
||
|
enough (TEREDO_HIGH_WATER_MARK) receives posted on the UDP socket.
|
||
|
|
||
|
Arguments:
|
||
|
|
||
|
TeredoIo - Supplies the I/O state.
|
||
|
|
||
|
Packet - Supplies the packet to reuse, or NULL.
|
||
|
|
||
|
Return Value:
|
||
|
|
||
|
Returns the number of receives posted on the UDP socket.
|
||
|
|
||
|
--*/
|
||
|
{
|
||
|
ULONG Count = 0, PostedReceives = TeredoIo->PostedReceives;
|
||
|
DWORD Error;
|
||
|
|
||
|
//
|
||
|
// Attempt to post as many receives as required to...
|
||
|
// 1. - either - have high water-mark number of posted receives.
|
||
|
// 2. - or - satisfy the current burst of packets.
|
||
|
//
|
||
|
while (PostedReceives < TEREDO_HIGH_WATER_MARK) {
|
||
|
//
|
||
|
// Allocate the Packet if we're not reusing one.
|
||
|
//
|
||
|
if (Packet == NULL) {
|
||
|
Packet = TeredoCreatePacket(TeredoIo);
|
||
|
if (Packet == NULL) {
|
||
|
return PostedReceives;
|
||
|
}
|
||
|
}
|
||
|
Packet->Type = TEREDO_PACKET_RECEIVE;
|
||
|
|
||
|
ZeroMemory((PUCHAR) &(Packet->Overlapped), sizeof(OVERLAPPED));
|
||
|
Error = WSARecvFrom(
|
||
|
TeredoIo->Socket,
|
||
|
&(Packet->Buffer),
|
||
|
1,
|
||
|
NULL,
|
||
|
&(Packet->Flags),
|
||
|
(PSOCKADDR) &(Packet->SocketAddress),
|
||
|
&(Packet->SocketAddressLength),
|
||
|
&(Packet->Overlapped),
|
||
|
NULL);
|
||
|
if (Error == SOCKET_ERROR) {
|
||
|
Error = WSAGetLastError();
|
||
|
}
|
||
|
switch (Error) {
|
||
|
case NO_ERROR:
|
||
|
//
|
||
|
// The completion routine will have already been scheduled.
|
||
|
//
|
||
|
PostedReceives =
|
||
|
InterlockedIncrement(&(TeredoIo->PostedReceives));
|
||
|
if (Count++ > TEREDO_LOW_WATER_MARK) {
|
||
|
//
|
||
|
// Enough already!
|
||
|
//
|
||
|
return PostedReceives;
|
||
|
}
|
||
|
Packet = NULL;
|
||
|
continue;
|
||
|
|
||
|
case WSA_IO_PENDING:
|
||
|
//
|
||
|
// The overlapped operation has been successfully initiated.
|
||
|
// Completion will be indicated at a later time.
|
||
|
//
|
||
|
PostedReceives =
|
||
|
InterlockedIncrement(&(TeredoIo->PostedReceives));
|
||
|
return PostedReceives;
|
||
|
|
||
|
case WSAECONNRESET:
|
||
|
//
|
||
|
// A previous send operation resulted in an ICMP "Port Unreachable"
|
||
|
// message. But why let that stop us? Post the same packet again.
|
||
|
//
|
||
|
continue;
|
||
|
|
||
|
default:
|
||
|
//
|
||
|
// The overlapped operation was not successfully initiated.
|
||
|
// No completion indication will occur.
|
||
|
//
|
||
|
goto Bail;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
Bail:
|
||
|
if (Packet != NULL) {
|
||
|
TeredoDestroyPacket(TeredoIo, Packet);
|
||
|
}
|
||
|
|
||
|
return PostedReceives;
|
||
|
}
|
||
|
|
||
|
|
||
|
VOID
|
||
|
CALLBACK
|
||
|
TeredoReceiveNotification(
|
||
|
IN PVOID Parameter,
|
||
|
IN BOOLEAN TimerOrWaitFired
|
||
|
)
|
||
|
/*++
|
||
|
|
||
|
Routine Description:
|
||
|
|
||
|
Callback for when there are pending read notifications on the UDP socket.
|
||
|
We attempt to post more packets.
|
||
|
|
||
|
Arguments:
|
||
|
|
||
|
Parameter - Supplies the I/O state.
|
||
|
|
||
|
TimerOrWaitFired - Ignored.
|
||
|
|
||
|
Return Value:
|
||
|
|
||
|
None.
|
||
|
|
||
|
--*/
|
||
|
{
|
||
|
ULONG Old, New;
|
||
|
PTEREDO_IO TeredoIo = Cast(Parameter, TEREDO_IO);
|
||
|
|
||
|
New = TeredoIo->PostedReceives;
|
||
|
|
||
|
while(New < TEREDO_LOW_WATER_MARK) {
|
||
|
//
|
||
|
// If this fails, the event triggering this callback will stop
|
||
|
// signalling due to a lack of a successful WSARecvFrom. This will
|
||
|
// likely occur during low-memory or stress conditions. When the
|
||
|
// system returns to normal, the low water-mark packets will be
|
||
|
// reposted, thus re-enabling the event which triggers this callback.
|
||
|
//
|
||
|
Old = New;
|
||
|
New = TeredoPostReceives(TeredoIo, NULL);
|
||
|
if (New == Old) {
|
||
|
//
|
||
|
// There is no change in the number of posted receive packets.
|
||
|
//
|
||
|
return;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
|
||
|
PTEREDO_PACKET
|
||
|
TeredoTransmitPacket(
|
||
|
IN PTEREDO_IO TeredoIo,
|
||
|
IN PTEREDO_PACKET Packet
|
||
|
)
|
||
|
/*++
|
||
|
|
||
|
Routine Description:
|
||
|
|
||
|
Post an asynchronous transmit request on the UDP socket.
|
||
|
|
||
|
Arguments:
|
||
|
|
||
|
TeredoIo - Supplies the I/O state.
|
||
|
|
||
|
Packet - Supplies the packet to transmit.
|
||
|
|
||
|
Return Value:
|
||
|
|
||
|
Returns the supplied packet if the transmit completed or failed;
|
||
|
NULL if the transmit will complete asynchronously.
|
||
|
|
||
|
--*/
|
||
|
{
|
||
|
DWORD Error, Bytes;
|
||
|
|
||
|
ASSERT((Packet->Type == TEREDO_PACKET_BUBBLE) ||
|
||
|
(Packet->Type == TEREDO_PACKET_BOUNCE) ||
|
||
|
(Packet->Type == TEREDO_PACKET_TRANSMIT) ||
|
||
|
(Packet->Type == TEREDO_PACKET_MULTICAST));
|
||
|
|
||
|
//
|
||
|
// Try sending it non-blocking.
|
||
|
//
|
||
|
Error = WSASendTo(
|
||
|
TeredoIo->Socket, &(Packet->Buffer), 1, &Bytes, Packet->Flags,
|
||
|
(PSOCKADDR) &(Packet->SocketAddress), Packet->SocketAddressLength,
|
||
|
NULL, NULL);
|
||
|
if ((Error != SOCKET_ERROR) || (WSAGetLastError() != WSAEWOULDBLOCK)) {
|
||
|
return Packet;
|
||
|
}
|
||
|
|
||
|
//
|
||
|
// WSASendTo threatens to block, so we send it overlapped.
|
||
|
//
|
||
|
ZeroMemory((PUCHAR) &(Packet->Overlapped), sizeof(OVERLAPPED));
|
||
|
Error = WSASendTo(
|
||
|
TeredoIo->Socket, &(Packet->Buffer), 1, &Bytes, Packet->Flags,
|
||
|
(PSOCKADDR) &(Packet->SocketAddress), Packet->SocketAddressLength,
|
||
|
&(Packet->Overlapped), NULL);
|
||
|
if ((Error != SOCKET_ERROR) || (WSAGetLastError() != WSA_IO_PENDING)) {
|
||
|
return Packet;
|
||
|
}
|
||
|
|
||
|
//
|
||
|
// The overlapped operation has been successfully initiated.
|
||
|
// Completion will be indicated at a later time.
|
||
|
//
|
||
|
return NULL;
|
||
|
}
|
||
|
|
||
|
|
||
|
VOID
|
||
|
TeredoDestroySocket(
|
||
|
IN PTEREDO_IO TeredoIo
|
||
|
)
|
||
|
/*++
|
||
|
|
||
|
Routine Description:
|
||
|
|
||
|
Close the UDP socket.
|
||
|
|
||
|
Arguments:
|
||
|
|
||
|
TeredoIo - Supplies the I/O state.
|
||
|
|
||
|
Return Value:
|
||
|
|
||
|
None.
|
||
|
|
||
|
--*/
|
||
|
{
|
||
|
if (TeredoIo->ReceiveEventWait != NULL) {
|
||
|
UnregisterWait(TeredoIo->ReceiveEventWait);
|
||
|
TeredoIo->ReceiveEventWait = NULL;
|
||
|
}
|
||
|
|
||
|
if (TeredoIo->ReceiveEvent != NULL) {
|
||
|
CloseHandle(TeredoIo->ReceiveEvent);
|
||
|
TeredoIo->ReceiveEvent = NULL;
|
||
|
}
|
||
|
|
||
|
if (TeredoIo->Socket != INVALID_SOCKET) {
|
||
|
//
|
||
|
// Close the socket. This will disable the FD_READ event select,
|
||
|
// as well as cancel all pending overlapped operations.
|
||
|
//
|
||
|
closesocket(TeredoIo->Socket);
|
||
|
TeredoIo->Socket = INVALID_SOCKET;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
|
||
|
DWORD
|
||
|
TeredoCreateSocket(
|
||
|
IN PTEREDO_IO TeredoIo
|
||
|
)
|
||
|
/*++
|
||
|
|
||
|
Routine Description:
|
||
|
|
||
|
Open the UDP socket for receives and transmits.
|
||
|
|
||
|
Arguments:
|
||
|
|
||
|
TeredoIo - Supplies the I/O state.
|
||
|
|
||
|
Return Value:
|
||
|
|
||
|
NO_ERROR or failure code.
|
||
|
|
||
|
--*/
|
||
|
{
|
||
|
DWORD Error;
|
||
|
struct ip_mreq Multicast;
|
||
|
BOOL Loopback;
|
||
|
|
||
|
//
|
||
|
// Create the socket.
|
||
|
//
|
||
|
TeredoIo->Socket = WSASocket(
|
||
|
AF_INET, SOCK_DGRAM, 0, NULL, 0, WSA_FLAG_OVERLAPPED);
|
||
|
if (TeredoIo->Socket == INVALID_SOCKET) {
|
||
|
return GetLastError();
|
||
|
}
|
||
|
|
||
|
//
|
||
|
// Bind the socket on the correct address and port.
|
||
|
//
|
||
|
if (bind(
|
||
|
TeredoIo->Socket,
|
||
|
(PSOCKADDR) &(TeredoIo->SourceAddress),
|
||
|
sizeof(SOCKADDR_IN)) == SOCKET_ERROR) {
|
||
|
goto Bail;
|
||
|
}
|
||
|
|
||
|
//
|
||
|
// Register for completion callbacks on the socket.
|
||
|
//
|
||
|
if (!BindIoCompletionCallback(
|
||
|
(HANDLE) TeredoIo->Socket, TeredoIo->IoCompletionCallback, 0)) {
|
||
|
goto Bail;
|
||
|
}
|
||
|
|
||
|
//
|
||
|
// Select the socket for read notifications so we know when to post
|
||
|
// more packets. This also sets the socket to nonblocking mode.
|
||
|
//
|
||
|
TeredoIo->ReceiveEvent = CreateEvent(NULL, FALSE, FALSE, NULL);
|
||
|
if (TeredoIo->ReceiveEvent == NULL) {
|
||
|
goto Bail;
|
||
|
}
|
||
|
|
||
|
if (WSAEventSelect(
|
||
|
TeredoIo->Socket,
|
||
|
TeredoIo->ReceiveEvent,
|
||
|
FD_READ) == SOCKET_ERROR) {
|
||
|
goto Bail;
|
||
|
}
|
||
|
|
||
|
if (!RegisterWaitForSingleObject(
|
||
|
&(TeredoIo->ReceiveEventWait),
|
||
|
TeredoIo->ReceiveEvent,
|
||
|
TeredoReceiveNotification,
|
||
|
(PVOID) TeredoIo,
|
||
|
INFINITE,
|
||
|
0)) {
|
||
|
goto Bail;
|
||
|
}
|
||
|
|
||
|
//
|
||
|
// Prepost low water-mark number of receive Packets. If the FD_READ event
|
||
|
// signals on the socket before we're done, we'll exceed the low water-mark
|
||
|
// here but that's really quite harmless.
|
||
|
//
|
||
|
SetEvent(TeredoIo->ReceiveEvent);
|
||
|
|
||
|
//
|
||
|
// See if there is a multicast group to join.
|
||
|
//
|
||
|
if (IN4_MULTICAST(TeredoIo->Group)) {
|
||
|
//
|
||
|
// Default TTL of multicast packets is 1, so don't bother setting it.
|
||
|
// Set loopback to ignore self generated multicast packets.
|
||
|
// Failure is not fatal!
|
||
|
//
|
||
|
Loopback = FALSE;
|
||
|
(VOID) setsockopt(
|
||
|
TeredoIo->Socket,
|
||
|
IPPROTO_IP,
|
||
|
IP_MULTICAST_LOOP,
|
||
|
(const CHAR *) &Loopback,
|
||
|
sizeof(BOOL));
|
||
|
|
||
|
//
|
||
|
// Join the multicast group on the native interface.
|
||
|
// Failure is not fatal!
|
||
|
//
|
||
|
Multicast.imr_multiaddr = TeredoIo->Group;
|
||
|
Multicast.imr_interface = TeredoIo->SourceAddress.sin_addr;
|
||
|
(VOID) setsockopt(
|
||
|
TeredoIo->Socket,
|
||
|
IPPROTO_IP,
|
||
|
IP_ADD_MEMBERSHIP,
|
||
|
(const CHAR *) &Multicast,
|
||
|
sizeof(struct ip_mreq));
|
||
|
}
|
||
|
|
||
|
return NO_ERROR;
|
||
|
|
||
|
Bail:
|
||
|
Error = GetLastError();
|
||
|
TeredoDestroySocket(TeredoIo);
|
||
|
return Error;
|
||
|
}
|
||
|
|
||
|
|
||
|
BOOL
|
||
|
TeredoPostRead(
|
||
|
IN PTEREDO_IO TeredoIo,
|
||
|
IN PTEREDO_PACKET Packet OPTIONAL
|
||
|
)
|
||
|
/*++
|
||
|
|
||
|
Routine Description:
|
||
|
|
||
|
Post an asynchronous read request on the TUN interface device.
|
||
|
|
||
|
Arguments:
|
||
|
|
||
|
TeredoIo - Supplies the I/O state.
|
||
|
|
||
|
Packet - Supplies the packet to reuse, or NULL.
|
||
|
|
||
|
Return Value:
|
||
|
|
||
|
TRUE if a read was successfully posted, FALSE otherwise.
|
||
|
|
||
|
--*/
|
||
|
{
|
||
|
BOOL Success;
|
||
|
|
||
|
//
|
||
|
// Allocate the Packet if we're not reusing one.
|
||
|
//
|
||
|
if (Packet == NULL) {
|
||
|
Packet = TeredoCreatePacket(TeredoIo);
|
||
|
if (Packet == NULL) {
|
||
|
return FALSE;
|
||
|
}
|
||
|
}
|
||
|
Packet->Type = TEREDO_PACKET_READ;
|
||
|
|
||
|
ZeroMemory((PUCHAR) &(Packet->Overlapped), sizeof(OVERLAPPED));
|
||
|
Success = ReadFile(
|
||
|
TeredoIo->TunnelDevice,
|
||
|
Packet->Buffer.buf,
|
||
|
Packet->Buffer.len,
|
||
|
NULL,
|
||
|
&(Packet->Overlapped));
|
||
|
if (Success || (GetLastError() == ERROR_IO_PENDING)) {
|
||
|
//
|
||
|
// On success, the completion routine will have already been scheduled.
|
||
|
//
|
||
|
return TRUE;
|
||
|
}
|
||
|
|
||
|
TeredoDestroyPacket(TeredoIo, Packet);
|
||
|
return FALSE;
|
||
|
}
|
||
|
|
||
|
|
||
|
PTEREDO_PACKET
|
||
|
TeredoWritePacket(
|
||
|
IN PTEREDO_IO TeredoIo,
|
||
|
IN PTEREDO_PACKET Packet
|
||
|
)
|
||
|
/*++
|
||
|
|
||
|
Routine Description:
|
||
|
|
||
|
Post an asynchronous write request on the TUN interface device.
|
||
|
|
||
|
Arguments:
|
||
|
|
||
|
TeredoIo - Supplies the I/O state.
|
||
|
|
||
|
Packet - Supplies the packet to write.
|
||
|
|
||
|
Return Value:
|
||
|
|
||
|
Returns the supplied packet if the write failed;
|
||
|
NULL if the write will complete asynchronously.
|
||
|
|
||
|
--*/
|
||
|
{
|
||
|
BOOL Success;
|
||
|
|
||
|
ASSERT(Packet->Type == TEREDO_PACKET_WRITE);
|
||
|
|
||
|
ZeroMemory((PUCHAR) &(Packet->Overlapped), sizeof(OVERLAPPED));
|
||
|
Success = WriteFile(
|
||
|
TeredoIo->TunnelDevice,
|
||
|
Packet->Buffer.buf,
|
||
|
Packet->Buffer.len,
|
||
|
NULL,
|
||
|
&(Packet->Overlapped));
|
||
|
if (Success || (GetLastError() == ERROR_IO_PENDING)) {
|
||
|
//
|
||
|
// On success, the completion routine will have already been scheduled.
|
||
|
//
|
||
|
return NULL;
|
||
|
}
|
||
|
|
||
|
return Packet;
|
||
|
}
|
||
|
|
||
|
|
||
|
VOID
|
||
|
TeredoCloseDevice(
|
||
|
IN PTEREDO_IO TeredoIo
|
||
|
)
|
||
|
/*++
|
||
|
|
||
|
Routine Description:
|
||
|
|
||
|
Close the TUN interface device.
|
||
|
|
||
|
Arguments:
|
||
|
|
||
|
TeredoIo - Supplies the I/O state.
|
||
|
|
||
|
Return Value:
|
||
|
|
||
|
None.
|
||
|
|
||
|
--*/
|
||
|
{
|
||
|
//
|
||
|
// Close the device. This will cancel all pending overlapped operations.
|
||
|
//
|
||
|
if (TeredoIo->TunnelDevice != INVALID_HANDLE_VALUE) {
|
||
|
CloseHandle(TeredoIo->TunnelDevice);
|
||
|
TeredoIo->TunnelDevice = INVALID_HANDLE_VALUE;
|
||
|
wcscpy(TeredoIo->TunnelInterface, L"");
|
||
|
}
|
||
|
}
|
||
|
|
||
|
|
||
|
DWORD
|
||
|
TeredoOpenDevice(
|
||
|
IN PTEREDO_IO TeredoIo
|
||
|
)
|
||
|
/*++
|
||
|
|
||
|
Routine Description:
|
||
|
|
||
|
Open the TUN interface device for reads and writes.
|
||
|
|
||
|
Arguments:
|
||
|
|
||
|
None.
|
||
|
|
||
|
Return Value:
|
||
|
|
||
|
NO_ERROR or failure code.
|
||
|
|
||
|
--*/
|
||
|
{
|
||
|
DWORD Error;
|
||
|
ULONG i;
|
||
|
|
||
|
TeredoIo->TunnelDevice = CreateFile(
|
||
|
TeredoTunnelDeviceName,
|
||
|
GENERIC_READ | GENERIC_WRITE,
|
||
|
0,
|
||
|
NULL,
|
||
|
OPEN_EXISTING,
|
||
|
FILE_FLAG_OVERLAPPED,
|
||
|
NULL);
|
||
|
if (TeredoIo->TunnelDevice == INVALID_HANDLE_VALUE) {
|
||
|
return GetLastError();
|
||
|
}
|
||
|
|
||
|
//
|
||
|
// Register for completion callbacks on the tun device.
|
||
|
//
|
||
|
if (!BindIoCompletionCallback(
|
||
|
TeredoIo->TunnelDevice, TeredoIo->IoCompletionCallback, 0)) {
|
||
|
Error = GetLastError();
|
||
|
goto Bail;
|
||
|
}
|
||
|
|
||
|
//
|
||
|
// Post a fixed number of reads on the device.
|
||
|
//
|
||
|
for (i = 0; i < TEREDO_LOW_WATER_MARK; i++) {
|
||
|
if (!TeredoPostRead(TeredoIo, NULL)) {
|
||
|
break;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if (i != 0) {
|
||
|
return NO_ERROR;
|
||
|
}
|
||
|
Error = ERROR_READ_FAULT;
|
||
|
|
||
|
//
|
||
|
// We couldn't post a single read on the device. What good is it?
|
||
|
//
|
||
|
|
||
|
Bail:
|
||
|
TeredoCloseDevice(TeredoIo);
|
||
|
return Error;
|
||
|
}
|
||
|
|
||
|
|
||
|
VOID
|
||
|
TeredoStopIo(
|
||
|
IN PTEREDO_IO TeredoIo
|
||
|
)
|
||
|
/*++
|
||
|
|
||
|
Routine Description:
|
||
|
|
||
|
Stop I/O processing.
|
||
|
|
||
|
Arguments:
|
||
|
|
||
|
TeredoIo - Supplies the I/O state.
|
||
|
|
||
|
Return Value:
|
||
|
|
||
|
None.
|
||
|
|
||
|
--*/
|
||
|
{
|
||
|
if (TeredoIo->TunnelDevice != INVALID_HANDLE_VALUE) {
|
||
|
TeredoCloseDevice(TeredoIo);
|
||
|
}
|
||
|
|
||
|
if (TeredoIo->Socket != INVALID_SOCKET) {
|
||
|
TeredoDestroySocket(TeredoIo);
|
||
|
}
|
||
|
|
||
|
TeredoIo->ServerAddress.sin_port = 0;
|
||
|
TeredoIo->ServerAddress.sin_addr.s_addr = htonl(INADDR_ANY);
|
||
|
TeredoIo->SourceAddress.sin_addr.s_addr = htonl(INADDR_ANY);
|
||
|
}
|
||
|
|
||
|
|
||
|
DWORD
|
||
|
TeredoStartIo(
|
||
|
IN PTEREDO_IO TeredoIo
|
||
|
)
|
||
|
/*++
|
||
|
|
||
|
Routine Description:
|
||
|
|
||
|
Start I/O processing.
|
||
|
|
||
|
Arguments:
|
||
|
|
||
|
TeredoIo - Supplies the I/O state.
|
||
|
|
||
|
Return Value:
|
||
|
|
||
|
NO_ERROR or failure code.
|
||
|
|
||
|
--*/
|
||
|
{
|
||
|
DWORD Error;
|
||
|
|
||
|
//
|
||
|
// Resolve the teredo server name and service name.
|
||
|
//
|
||
|
Error = TeredoResolveServer(TeredoIo);
|
||
|
if (Error != NO_ERROR) {
|
||
|
Trace1(ERR, _T("TeredoResolveServer: %u"), Error);
|
||
|
return Error;
|
||
|
}
|
||
|
|
||
|
//
|
||
|
// Get the preferred source address to the teredo server.
|
||
|
//
|
||
|
Error = GetPreferredSourceAddress(
|
||
|
&(TeredoIo->ServerAddress), &(TeredoIo->SourceAddress));
|
||
|
if (Error != NO_ERROR) {
|
||
|
Trace1(ERR, _T("GetPreferredSourceAddress: %u"), Error);
|
||
|
goto Bail;
|
||
|
}
|
||
|
|
||
|
//
|
||
|
// Create the UDP Socket.
|
||
|
//
|
||
|
Error = TeredoCreateSocket(TeredoIo);
|
||
|
if (Error != NO_ERROR) {
|
||
|
Trace1(ERR, _T("TeredoCreateSocket: %u"), Error);
|
||
|
goto Bail;
|
||
|
}
|
||
|
|
||
|
//
|
||
|
// Open the TunnelDevice.
|
||
|
//
|
||
|
Error = TeredoOpenDevice(TeredoIo);
|
||
|
if (Error != NO_ERROR) {
|
||
|
Trace1(ERR, _T("TeredoOpenDevice: %u"), Error);
|
||
|
goto Bail;
|
||
|
}
|
||
|
|
||
|
return NO_ERROR;
|
||
|
|
||
|
Bail:
|
||
|
TeredoStopIo(TeredoIo);
|
||
|
return Error;
|
||
|
}
|
||
|
|
||
|
|
||
|
DWORD
|
||
|
TeredoRefreshSocket(
|
||
|
IN PTEREDO_IO TeredoIo
|
||
|
)
|
||
|
/*++
|
||
|
|
||
|
Routine Description:
|
||
|
|
||
|
Refresh the I/O state upon deletion of SourceAddress.
|
||
|
|
||
|
Arguments:
|
||
|
|
||
|
TeredoIo - Supplies the I/O state.
|
||
|
|
||
|
Return Value:
|
||
|
|
||
|
NO_ERROR if the I/O state is successfully refreshed, o/w failure code.
|
||
|
The caller is responsible for cleaning up the I/O state upon failure.
|
||
|
|
||
|
--*/
|
||
|
{
|
||
|
DWORD Error;
|
||
|
SOCKADDR_IN Old = TeredoIo->SourceAddress;
|
||
|
|
||
|
//
|
||
|
// Let's re-resolve the teredo server address and port.
|
||
|
// Refresh might have been triggered by a change in server/service name.
|
||
|
//
|
||
|
Error = TeredoResolveServer(TeredoIo);
|
||
|
if (Error != NO_ERROR) {
|
||
|
return Error;
|
||
|
}
|
||
|
|
||
|
//
|
||
|
// Get the preferred source address to the teredo server.
|
||
|
//
|
||
|
Error = GetPreferredSourceAddress(
|
||
|
&(TeredoIo->ServerAddress), &(TeredoIo->SourceAddress));
|
||
|
if (Error != NO_ERROR) {
|
||
|
return Error;
|
||
|
}
|
||
|
|
||
|
if (IN4_SOCKADDR_EQUAL(&(TeredoIo->SourceAddress), &Old)) {
|
||
|
//
|
||
|
// No change to the bound address and port. Whew!
|
||
|
//
|
||
|
return NO_ERROR;
|
||
|
}
|
||
|
|
||
|
//
|
||
|
// Destroy the old UDP socket.
|
||
|
//
|
||
|
TeredoDestroySocket(TeredoIo);
|
||
|
|
||
|
//
|
||
|
// Create a new UDP socket, bound to the new address and port.
|
||
|
//
|
||
|
Error = TeredoCreateSocket(TeredoIo);
|
||
|
if (Error != NO_ERROR) {
|
||
|
return Error;
|
||
|
}
|
||
|
|
||
|
return NO_ERROR;
|
||
|
}
|
||
|
|
||
|
|
||
|
DWORD
|
||
|
TeredoInitializeIo(
|
||
|
IN PTEREDO_IO TeredoIo,
|
||
|
IN IN_ADDR Group,
|
||
|
IN PTEREDO_REFERENCE Reference,
|
||
|
IN PTEREDO_DEREFERENCE Dereference,
|
||
|
IN LPOVERLAPPED_COMPLETION_ROUTINE IoCompletionCallback
|
||
|
)
|
||
|
/*++
|
||
|
|
||
|
Routine Description:
|
||
|
|
||
|
Initialize the I/O state.
|
||
|
|
||
|
Arguments:
|
||
|
|
||
|
TeredoIo - Supplies the I/O state.
|
||
|
|
||
|
Group - Supplies the multicast group to join (or INADDR_ANY).
|
||
|
|
||
|
Return Value:
|
||
|
|
||
|
NO_ERROR or failure code.
|
||
|
|
||
|
--*/
|
||
|
{
|
||
|
#if DBG
|
||
|
TeredoIo->Signature = TEREDO_IO_SIGNATURE;
|
||
|
#endif // DBG
|
||
|
|
||
|
|
||
|
TeredoIo->PostedReceives = 0;
|
||
|
TeredoIo->ReceiveEvent = TeredoIo->ReceiveEventWait = NULL;
|
||
|
TeredoIo->Group = Group;
|
||
|
ZeroMemory(&(TeredoIo->ServerAddress), sizeof(SOCKADDR_IN));
|
||
|
TeredoIo->ServerAddress.sin_family = AF_INET;
|
||
|
ZeroMemory(&(TeredoIo->SourceAddress), sizeof(SOCKADDR_IN));
|
||
|
TeredoIo->SourceAddress.sin_family = AF_INET;
|
||
|
TeredoIo->Socket = INVALID_SOCKET;
|
||
|
TeredoIo->TunnelDevice = INVALID_HANDLE_VALUE;
|
||
|
wcscpy(TeredoIo->TunnelInterface, L"");
|
||
|
TeredoIo->Reference = Reference;
|
||
|
TeredoIo->Dereference = Dereference;
|
||
|
TeredoIo->IoCompletionCallback = IoCompletionCallback;
|
||
|
|
||
|
TeredoIo->PacketHeap = HeapCreate(0, 0, 0);
|
||
|
if (TeredoIo->PacketHeap == NULL) {
|
||
|
return GetLastError();
|
||
|
}
|
||
|
return NO_ERROR;
|
||
|
}
|
||
|
|
||
|
|
||
|
VOID
|
||
|
TeredoCleanupIo(
|
||
|
IN PTEREDO_IO TeredoIo
|
||
|
)
|
||
|
/*++
|
||
|
|
||
|
Routine Description:
|
||
|
|
||
|
Cleanup the I/O state.
|
||
|
|
||
|
Arguments:
|
||
|
|
||
|
TeredoIo - Supplies the I/O state.
|
||
|
|
||
|
Return Value:
|
||
|
|
||
|
None.
|
||
|
|
||
|
--*/
|
||
|
{
|
||
|
HeapDestroy(TeredoIo->PacketHeap);
|
||
|
TeredoIo->PacketHeap = NULL;
|
||
|
}
|