Complete Communications Engineering

This can be done using the socket option PKTINFO and the function WSARecvMsg.  Both of these work similar to their Linux counterparts.  One difference is that the WSARecvMsg function can’t be called directly.  Instead, the program must obtain a function pointer for it using the WSAIoctl function.  This function requires a socket handle as an argument, but any socket using the same socket service provider should return the same function pointer.  Therefore, it is possible to get the function pointer at startup and continue using it until the program shuts down.  The following code example demonstrates receiving a packet using WSARecvMsg and getting the packet’s local address:

void get_wsarecvmsg_fptr (void)

{

    GUID guidWSARecvMsg = WSAID_WSARECVMSG;

    DWORD dwBytesRecvd = 0;

    SOCKET s = socket(AF_INET, SOCK_STREAM, 0);

   

    /* Get WSARecvMsg function pointer, store in g_pfnWSARecvMsg */

    WSAIoctl(s, SIO_GET_EXTENSION_FUNCTION_POINTER,

             &guidWSARecvMsg, sizeof(guidWSARecvMsg),

             &g_pfnWSARecvMsg, sizeof(g_pfnWSARecvMsg),

             &dwBytesRecvd, NULL, NULL);

    closesocket(s);

}

 

void enable_pktinfo (SOCKET s)

{

    int sockopt = 1;    /* Enable PKTINFO */

    setsockopt(s, IPPROTO_IP, IP_PKTINFO, (char*)&sockopt, sizeof(sockopt));

}

 

int recv_localaddr (SOCKET s, uint8_t *buf, size_t buf_sz,

                    struct sockaddr_in *remote_addr,

                    struct sockaddr_in *local_addr)

{

    DWORD bytes_received;

    WSAMSG msg = {0};

    WSABUF sbuf = {0};

    uint8_t cmdbuf[512];

    WSACMSGHDR *cmsg;

    PIN_PKTINFO pi;

 

    /* Fill out the msghdr structure */

    sbuf.buf = (char FAR *)buf;

    sbuf.len = (u_long)buf_sz;

    msg.name = (LPSOCKADDR)remote_addr;

    msg.namelen = sizeof(*remote_addr);

    msg.lpBuffers = &sbuf;

    msg.dwBufferCount = 1;

    msg.Control.buf = (char FAR *)cmdbuf;

    msg.Control.len = (u_long)sizeof(cmdbuf);

 

    /* Receive a packet */

    (g_pfnWSARecvMsg)(s, &msg, &bytes_received, NULL, NULL);

 

    /* Parse the header info, look for the local address */

    cmsg = WSA_CMSG_FIRSTHDR(&msg);

    for (; cmsg != NULL; cmsg = WSA_CMSG_NXTHDR(&msg, cmsg)) {

        if ((cmsg->cmsg_level == IPPROTO_IP) &&

            (cmsg->cmsg_type == IP_PKTINFO)) {

            pi = (PIN_PKTINFO)WSA_CMSG_DATA(cmsg);

            local_addr->sin_family = AF_INET;

            local_addr->sin_addr = pi->ipi_addr;

            break;

        }

    }

 

    return (int)bytes_received;

}

The function get_wsarecvmsg_fptr calls WSAIoctl to get the function pointer.  This can be done at startup, and the pointer can be kept for use later.  The function enable_pktinfo enables the PKTINFO option for a socket.  This is required for WSARecvMsg to return the local address.  The function recv_localaddr demonstrates reading a packet along with its local address information.  It first fills out a WSAMSG structure which is passed to the WSARecvMsg function.  This includes a buffer where ancillary data from the packet will be stored.  The buffer must be large enough for all of the ancillary data that WSARecvMsg is expected to generate.  The ancillary data is parsed using macros.  The code looks for a specific message type and copies its info into local_addr.