586 lines
17 KiB
C
586 lines
17 KiB
C
/*++
|
|
|
|
Copyright (c) 1999, Microsoft Corporation
|
|
|
|
Module Name:
|
|
|
|
sample\socket.c
|
|
Assumes an IPv4 IPADDRESS for now...
|
|
|
|
Abstract:
|
|
|
|
The file contains functions to deal with sockets.
|
|
Assumes an IPv4 IPADDRESS for now...
|
|
|
|
--*/
|
|
|
|
#include "pchsample.h"
|
|
#pragma hdrstop
|
|
|
|
|
|
#define START_SAMPLE_IO() ENTER_SAMPLE_API()
|
|
|
|
#define FINISH_SAMPLE_IO() LEAVE_SAMPLE_API()
|
|
|
|
|
|
|
|
////////////////////////////////////////
|
|
// CALLBACKFUNCTIONS
|
|
////////////////////////////////////////
|
|
|
|
VOID
|
|
WINAPI
|
|
SocketCallbackSendCompletion (
|
|
IN DWORD dwErr,
|
|
IN DWORD dwNumSent,
|
|
IN LPOVERLAPPED lpOverlapped
|
|
)
|
|
/*++
|
|
|
|
Routine Description
|
|
This routine is invoked by the I/O system upon completion of an
|
|
operation. Runs in the context of an RTUTILS.DLL worker thread.
|
|
|
|
Locks
|
|
None.
|
|
|
|
Arguments:
|
|
dwErr system-supplied error code
|
|
dwNumSent system-supplied byte-count
|
|
lpOverlapped caller-supplied context area
|
|
|
|
Return Value:
|
|
None.
|
|
|
|
--*/
|
|
{
|
|
PPACKET pPacket = CONTAINING_RECORD(lpOverlapped, PACKET, oOverlapped);
|
|
|
|
TRACE3(ENTER, "Entering SocketCallbackSendCompletion: %u %u 0x%08x",
|
|
dwErr, dwNumSent, lpOverlapped);
|
|
PacketDisplay(pPacket);
|
|
|
|
|
|
if ((dwErr != NO_ERROR) or // operation aborted
|
|
(dwNumSent != pPacket->wsaBuffer.len)) // data not sent entirely
|
|
{
|
|
TRACE1(NETWORK, "Error %u sending packet", dwErr);
|
|
LOGERR0(SENDTO_FAILED, dwErr);
|
|
|
|
}
|
|
|
|
PacketDestroy(pPacket); // destroy the packet structure
|
|
|
|
|
|
TRACE0(LEAVE, "Leaving SocketCallbackSendCompletion");
|
|
|
|
FINISH_SAMPLE_IO();
|
|
}
|
|
|
|
|
|
|
|
////////////////////////////////////////
|
|
// APIFUNCTIONS
|
|
////////////////////////////////////////
|
|
|
|
|
|
DWORD
|
|
SocketCreate (
|
|
IN IPADDRESS ipAddress,
|
|
IN HANDLE hEvent,
|
|
OUT SOCKET *psSocket)
|
|
/*++
|
|
|
|
Routine Description
|
|
Creates a socket.
|
|
|
|
Locks
|
|
None
|
|
|
|
Arguments
|
|
ipAddress ip address to bind the socket to
|
|
hEvent the event to set when a packet arrives
|
|
psSocket address of the socket to create
|
|
|
|
Return Value
|
|
NO_ERROR success
|
|
Error Code o/w
|
|
|
|
--*/
|
|
{
|
|
DWORD dwErr = NO_ERROR;
|
|
PCHAR pszBuffer;
|
|
SOCKADDR_IN sinSockAddr;
|
|
BOOL bDontLinger, bReuse, bLoopback;
|
|
struct linger lLinger = { 0, 0 };
|
|
UCHAR ucTTL;
|
|
struct ip_mreq imMulticast;
|
|
|
|
|
|
// validate parameters
|
|
if ((!psSocket or (*psSocket != INVALID_SOCKET)) or
|
|
!IP_VALID(ipAddress) or
|
|
(hEvent is INVALID_HANDLE_VALUE))
|
|
return ERROR_INVALID_PARAMETER;
|
|
|
|
// default return value
|
|
*psSocket = INVALID_SOCKET;
|
|
|
|
|
|
pszBuffer = INET_NTOA(ipAddress);
|
|
|
|
do // breakout loop
|
|
{
|
|
// create socket
|
|
*psSocket = WSASocket(AF_INET, // address family
|
|
SOCK_RAW, // type
|
|
PROTO_IP_SAMPLE, // protocol
|
|
NULL,
|
|
0,
|
|
WSA_FLAG_OVERLAPPED);
|
|
if(*psSocket is INVALID_SOCKET)
|
|
{
|
|
dwErr = WSAGetLastError();
|
|
TRACE0(NETWORK, "Could not create socket");
|
|
break;
|
|
}
|
|
|
|
// associate the socket with our I/O completion port. the callback
|
|
// function is invoked when an overlapped I/O operation completes.
|
|
// this would be the send operation!
|
|
dwErr = SetIoCompletionProc((HANDLE) *psSocket,
|
|
SocketCallbackSendCompletion);
|
|
if (dwErr != NO_ERROR)
|
|
{
|
|
TRACE0(NETWORK, "Could not associate callback function");
|
|
break;
|
|
}
|
|
|
|
|
|
// set SO_LINGER to off
|
|
// do not linger on close waiting for unsent data to be sent
|
|
bDontLinger = TRUE;
|
|
if (setsockopt(*psSocket,
|
|
SOL_SOCKET,
|
|
SO_DONTLINGER,
|
|
(const char *) &bDontLinger,
|
|
sizeof(BOOL)) is SOCKET_ERROR)
|
|
{
|
|
TRACE2(NETWORK,
|
|
"Error %u setting linger option on %s, continuing...",
|
|
WSAGetLastError(), pszBuffer);
|
|
}
|
|
|
|
// set to SO_REUSEADDR
|
|
// allow socket to be bound to an address that is already in use
|
|
bReuse = TRUE;
|
|
if (setsockopt(*psSocket,
|
|
SOL_SOCKET,
|
|
SO_REUSEADDR,
|
|
(const char *) &bReuse,
|
|
sizeof(BOOL)) is SOCKET_ERROR)
|
|
{
|
|
TRACE2(NETWORK,
|
|
"Error %u seting reuse option on %s, continuing...",
|
|
WSAGetLastError(), pszBuffer);
|
|
}
|
|
|
|
// bind to the specified IPv4 addresses
|
|
ZeroMemory(&sinSockAddr, sizeof(SOCKADDR_IN));
|
|
sinSockAddr.sin_family = AF_INET;
|
|
sinSockAddr.sin_addr.s_addr = ipAddress;
|
|
sinSockAddr.sin_port = 0;
|
|
if (bind(*psSocket, (LPSOCKADDR) &sinSockAddr, sizeof(SOCKADDR_IN))
|
|
is SOCKET_ERROR)
|
|
{
|
|
dwErr = WSAGetLastError();
|
|
TRACE0(NETWORK, "Could not bind socket");
|
|
LOGERR1(BIND_IF_FAILED, pszBuffer, dwErr);
|
|
break;
|
|
}
|
|
|
|
|
|
// allow multicast traffic to be sent out this interface
|
|
if (setsockopt(*psSocket,
|
|
IPPROTO_IP,
|
|
IP_MULTICAST_IF,
|
|
(const char *) &sinSockAddr.sin_addr,
|
|
sizeof(IN_ADDR)) is SOCKET_ERROR)
|
|
{
|
|
dwErr = WSAGetLastError();
|
|
TRACE2(NETWORK,
|
|
"Error %u setting interface %s as multicast...",
|
|
dwErr, pszBuffer);
|
|
LOGERR1(SET_MCAST_IF_FAILED, pszBuffer, dwErr);
|
|
break;
|
|
}
|
|
|
|
// set loopback to ignore self generated packets.
|
|
bLoopback = FALSE;
|
|
if (setsockopt(*psSocket,
|
|
IPPROTO_IP,
|
|
IP_MULTICAST_LOOP,
|
|
(const char *) &bLoopback,
|
|
sizeof(BOOL)) is SOCKET_ERROR)
|
|
{
|
|
TRACE2(NETWORK,
|
|
"Error %u setting loopback to FALSE on %s, continuing...",
|
|
WSAGetLastError(), pszBuffer);
|
|
}
|
|
|
|
// set TTL to 1 to restrict packet to within subnet (default anyway)
|
|
ucTTL = 1;
|
|
if (setsockopt(*psSocket,
|
|
IPPROTO_IP,
|
|
IP_MULTICAST_TTL,
|
|
(const char *) &ucTTL,
|
|
sizeof(UCHAR)) is SOCKET_ERROR)
|
|
{
|
|
TRACE2(NETWORK,
|
|
"Error %u setting mcast ttl to 1 on %s, continuing...",
|
|
WSAGetLastError(), pszBuffer);
|
|
}
|
|
|
|
// join the multicast session on SAMPLE_PROTOCOL_MULTICAST_GROUP.
|
|
imMulticast.imr_multiaddr.s_addr = SAMPLE_PROTOCOL_MULTICAST_GROUP;
|
|
imMulticast.imr_interface.s_addr = ipAddress;
|
|
if (setsockopt(*psSocket,
|
|
IPPROTO_IP,
|
|
IP_ADD_MEMBERSHIP,
|
|
(const char *) &imMulticast,
|
|
sizeof(struct ip_mreq)) is SOCKET_ERROR)
|
|
{
|
|
dwErr = WSAGetLastError();
|
|
TRACE0(NETWORK, "Could not join multicast group on socket");
|
|
LOGERR1(JOIN_GROUP_FAILED, pszBuffer, dwErr);
|
|
break;
|
|
}
|
|
|
|
|
|
// associate hReceiveEvent with the receive network event
|
|
if (WSAEventSelect(*psSocket, (WSAEVENT) hEvent, FD_READ)
|
|
is SOCKET_ERROR)
|
|
{
|
|
dwErr = WSAGetLastError();
|
|
TRACE1(NETWORK, "Error %u calling WSAEventSelect()", dwErr);
|
|
LOGERR0(EVENTSELECT_FAILED, dwErr);
|
|
break;
|
|
}
|
|
} while(FALSE);
|
|
|
|
if (dwErr != NO_ERROR)
|
|
{
|
|
TRACE2(NETWORK, "Error %u creating socket for %s", dwErr, pszBuffer);
|
|
LOGERR0(CREATE_SOCKET_FAILED, dwErr);
|
|
SocketDestroy(*psSocket);
|
|
*psSocket = INVALID_SOCKET;
|
|
}
|
|
|
|
return dwErr;
|
|
}
|
|
|
|
|
|
|
|
DWORD
|
|
SocketDestroy (
|
|
IN SOCKET sSocket)
|
|
/*++
|
|
|
|
Routine Description
|
|
Closes a socket.
|
|
|
|
Locks
|
|
None
|
|
|
|
Arguments
|
|
sSocket the socket to close
|
|
|
|
Return Value
|
|
NO_ERROR success
|
|
Error Code o/w
|
|
|
|
--*/
|
|
{
|
|
DWORD dwErr = NO_ERROR;
|
|
|
|
if (sSocket is INVALID_SOCKET)
|
|
return NO_ERROR;
|
|
|
|
/*
|
|
// closing a socket with closesocket also cancels the association and
|
|
// selection of network events specified in WSAEventSelect. redundant!
|
|
if (WSAEventSelect(sSocket, (WSAEVENT) NULL, 0) is SOCKET_ERROR)
|
|
{
|
|
dwErr = WSAGetLastError();
|
|
TRACE1(NETWORK, "Error %u clearing socket-event association", dwErr);
|
|
LOGERR0(EVENTSELECT_FAILED, dwErr);
|
|
}
|
|
*/
|
|
|
|
// close the socket
|
|
if (closesocket(sSocket) != NO_ERROR)
|
|
{
|
|
dwErr = WSAGetLastError();
|
|
TRACE1(NETWORK, "Error %u closing socket", dwErr);
|
|
LOGERR0(DESTROY_SOCKET_FAILED, dwErr);
|
|
}
|
|
|
|
return dwErr;
|
|
}
|
|
|
|
|
|
|
|
DWORD
|
|
SocketSend (
|
|
IN SOCKET sSocket,
|
|
IN IPADDRESS ipDestination,
|
|
IN PPACKET pPacket)
|
|
/*++
|
|
|
|
Routine Description
|
|
Send a packet to its destination and destroy it.
|
|
Asynchronous.
|
|
|
|
Locks
|
|
None
|
|
|
|
Arguments
|
|
sSocket the socket to send the packet over
|
|
ipDestination the packet's destination
|
|
pPacket the packet to send out
|
|
|
|
Return Value
|
|
NO_ERROR success
|
|
Error Code o/w
|
|
|
|
--*/
|
|
{
|
|
DWORD dwErr = NO_ERROR;
|
|
SOCKADDR_IN sinDestination;
|
|
DWORD dwScratch;
|
|
|
|
if (!START_SAMPLE_IO()) { return ERROR_CAN_NOT_COMPLETE; }
|
|
|
|
|
|
// validate parameters
|
|
if ((sSocket is INVALID_SOCKET) or !pPacket)
|
|
return ERROR_INVALID_PARAMETER;
|
|
|
|
|
|
ZeroMemory(&sinDestination, sizeof(SOCKADDR_IN));
|
|
sinDestination.sin_family = AF_INET;
|
|
sinDestination.sin_addr.s_addr = ipDestination;
|
|
sinDestination.sin_port = 0;
|
|
dwErr = WSASendTo(sSocket,
|
|
&(pPacket->wsaBuffer), // buffer and length
|
|
1, // only one wsabuf exists
|
|
&dwScratch, // unused
|
|
0, // no flags
|
|
(PSOCKADDR) &sinDestination,
|
|
sizeof(SOCKADDR_IN),
|
|
&pPacket->oOverlapped, // context upon completion
|
|
NULL); // no completion routine
|
|
|
|
// completion routine (SocketCallbackSendCompletion) queued
|
|
if (((dwErr is SOCKET_ERROR) and (WSAGetLastError() is WSA_IO_PENDING)) or
|
|
(dwErr is NO_ERROR))
|
|
{
|
|
return NO_ERROR;
|
|
}
|
|
|
|
// completion routine (SocketCallbackSendCompletion) not queued
|
|
dwErr = WSAGetLastError();
|
|
TRACE1(NETWORK, "Error %u sending packet", dwErr);
|
|
LOGERR0(SENDTO_FAILED, dwErr);
|
|
|
|
FINISH_SAMPLE_IO();
|
|
|
|
return dwErr;
|
|
}
|
|
|
|
|
|
|
|
DWORD
|
|
SocketReceive (
|
|
IN SOCKET sSocket,
|
|
IN PPACKET pPacket)
|
|
/*++
|
|
|
|
Routine Description
|
|
Receive a packet on a socket.
|
|
|
|
This routine is written so that it could dynamically allocate a buffer,
|
|
not knowing a priori the maximum size of the protocol's packet. It is
|
|
synchronous, treating sSocket as a non overlapped socket.
|
|
|
|
Locks
|
|
None
|
|
|
|
Arguments
|
|
sSocket the socket to send the packet over
|
|
pPacket the packet to send out
|
|
|
|
Return Value
|
|
NO_ERROR success
|
|
Error Code o/w
|
|
|
|
--*/
|
|
#define IPv4_PREVIEW_SIZE 4
|
|
#define IPv4_LENGTH_OFFSET 2
|
|
{
|
|
DWORD dwErr = NO_ERROR;
|
|
BYTE rgbyPreview[IPv4_PREVIEW_SIZE];
|
|
DWORD dwNumReceived, dwFlags;
|
|
INT iSourceLength;
|
|
SOCKADDR_IN sinSource;
|
|
DWORD dwPacketLength;
|
|
|
|
// validate parameters
|
|
if ((sSocket is INVALID_SOCKET) or !pPacket)
|
|
return ERROR_INVALID_PARAMETER;
|
|
|
|
do // breakout loop
|
|
{
|
|
// read enuf to figure length
|
|
pPacket->wsaBuffer.buf = rgbyPreview;
|
|
pPacket->wsaBuffer.len = IPv4_PREVIEW_SIZE;
|
|
dwFlags = MSG_PEEK;
|
|
iSourceLength = sizeof(SOCKADDR_IN);
|
|
|
|
dwErr = WSARecvFrom(sSocket,
|
|
&(pPacket->wsaBuffer), // buffer and length
|
|
1, // only one wsabuf exists
|
|
&dwNumReceived, // # bytes received
|
|
&dwFlags, // flags
|
|
(PSOCKADDR) &sinSource,
|
|
&iSourceLength,
|
|
NULL, // no context
|
|
NULL); // no completion routine
|
|
if (dwErr != SOCKET_ERROR) // there should have been an error
|
|
break;
|
|
dwErr = WSAGetLastError();
|
|
if (dwErr != WSAEMSGSIZE) // of this kind
|
|
break;
|
|
if (dwNumReceived != pPacket->wsaBuffer.len)
|
|
{
|
|
dwErr = ERROR_CAN_NOT_COMPLETE;
|
|
break;
|
|
}
|
|
|
|
|
|
// calculate the packet length
|
|
dwPacketLength = ntohs(*((PUSHORT)(rgbyPreview + IPv4_LENGTH_OFFSET)));
|
|
if (dwPacketLength > MAX_PACKET_LENGTH)
|
|
{
|
|
dwErr = ERROR_CAN_NOT_COMPLETE;
|
|
break;
|
|
}
|
|
|
|
// read the entire packet, the buffer could be dynamically allocated
|
|
pPacket->wsaBuffer.buf = pPacket->rgbyBuffer;
|
|
pPacket->wsaBuffer.len = dwPacketLength;
|
|
dwFlags = 0;
|
|
iSourceLength = sizeof(SOCKADDR_IN);
|
|
|
|
dwErr = WSARecvFrom(sSocket,
|
|
&(pPacket->wsaBuffer), // buffer and length
|
|
1, // only one wsabuf exists
|
|
&dwNumReceived, // # bytes received
|
|
&dwFlags, // flags
|
|
(PSOCKADDR) &sinSource,
|
|
&iSourceLength,
|
|
NULL, // no context
|
|
NULL); // no completion routine
|
|
if (dwErr is SOCKET_ERROR)
|
|
{
|
|
dwErr = WSAGetLastError();
|
|
break;
|
|
}
|
|
if (dwPacketLength != dwNumReceived)
|
|
{
|
|
dwErr = ERROR_CAN_NOT_COMPLETE;
|
|
break;
|
|
}
|
|
|
|
pPacket->ipSource = sinSource.sin_addr.s_addr;
|
|
pPacket->wsaBuffer.len = dwNumReceived;
|
|
|
|
dwErr = NO_ERROR;
|
|
} while (FALSE);
|
|
|
|
if (dwErr != NO_ERROR)
|
|
{
|
|
TRACE1(NETWORK, "Error %u receiving packet", dwErr);
|
|
LOGERR0(RECVFROM_FAILED, dwErr);
|
|
}
|
|
|
|
return dwErr;
|
|
}
|
|
|
|
|
|
|
|
BOOL
|
|
SocketReceiveEvent (
|
|
IN SOCKET sSocket)
|
|
/*++
|
|
|
|
Routine Description
|
|
Indicates whether a receive event occured on a socket. The recording
|
|
of network events commences when WSAEventSelect is called with a
|
|
nonzero lNetworkEvents parameter (i.e. the socket is activated) and
|
|
remains in effect until another call is made to WSAEventSelect with the
|
|
lNetworkEvents parameter set to zero (i.e. the socket is deactivated).
|
|
|
|
Locks
|
|
None
|
|
|
|
Arguments
|
|
sSocket the socket to check for packet reception
|
|
|
|
Return Value
|
|
TRUE receive event did occur
|
|
FALSE o/w
|
|
|
|
--*/
|
|
{
|
|
DWORD dwErr = NO_ERROR;
|
|
WSANETWORKEVENTS wsaEvent;
|
|
|
|
// validate parameters
|
|
if (sSocket is INVALID_SOCKET)
|
|
return ERROR_INVALID_PARAMETER;
|
|
|
|
do // breakout loop
|
|
{
|
|
// enumerate network events to see if any packets have arrived on
|
|
dwErr = WSAEnumNetworkEvents(sSocket, NULL, &wsaEvent);
|
|
if (dwErr != NO_ERROR)
|
|
{
|
|
TRACE1(NETWORK, "Error %u enumerating network events", dwErr);
|
|
LOGERR0(ENUM_NETWORK_EVENTS_FAILED, dwErr);
|
|
break;
|
|
}
|
|
|
|
// see if the input bit is set
|
|
if (!(wsaEvent.lNetworkEvents & FD_READ))
|
|
{
|
|
dwErr = SOCKET_ERROR;
|
|
break;
|
|
}
|
|
|
|
// the input bit is set, now see if there was an error
|
|
dwErr = wsaEvent.iErrorCode[FD_READ_BIT];
|
|
if (dwErr != NO_ERROR)
|
|
{
|
|
TRACE1(NETWORK, "Error %u in input record", dwErr);
|
|
LOGERR0(INPUT_RECORD_ERROR, dwErr);
|
|
break;
|
|
}
|
|
} while (FALSE);
|
|
|
|
if (dwErr is NO_ERROR)
|
|
return TRUE;
|
|
|
|
return FALSE;
|
|
}
|