windows-nt/Source/XPSP1/NT/net/rras/ip/sample/socket.c
2020-09-26 16:20:57 +08:00

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;
}