/*++ 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; }