Search code examples
c++network-programmingtcpwinsockwinsock2

TCP SYN RAW socket receive packet error 10022


I'm porting my program from Unix to Windows, and I have a problem with the recvfrom() function to accept a packet. I haven't written anything with Winsock until now. I think I've done everything, according to the manual.

Also, as I read from the manual, a TCP RAW socket does not need binding via bind(), but it still doesn't work.

#define NESCA

int
ncread(const char* dest_ip, int recv_timeout_ms, unsigned char **buffer, bool debug,
      int dest_port, int source_port, bool packet_trace)
{
    /*Create a structure and pass the recipient's IP into it.*/
    struct in_addr dest; dest.s_addr = inet_addr(dest_ip);

    /*Time buffer.*/
    unsigned char *read_buffer = *buffer;

    WSADATA wsaData;
    int iResult = WSAStartup(MAKEWORD(2, 2), &wsaData);
    if (iResult != NO_ERROR) {
        wprintf(L"WSAStartup failed with error %d\n", iResult);
        return -1;
    }
    int sock = socket(AF_INET, SOCK_RAW, IPPROTO_TCP);
    if (sock == -1) {return SOCKET_ERROR;}

#ifdef NESCA
    /*Function similar to the entry below*/
    int result = set_socket_timeout(sock, recv_timeout_ms, 1, 1);
#else
    struct timeval timeout;
    timeout.tv_sec = recv_timeout_ms / 1000;
    timeout.tv_usec = (recv_timeout_ms % 1000) * 1000;
    int result = setsockopt(sock, SOL_SOCKET, SO_RCVTIMEO, (const char*)&timeout, sizeof(timeout));
#endif
    if (result < 0)
    {
        std::cout << "timeout infinity\n\n";
        /*Timeout.*/
        fuck_fd(sock);
        return -1;
    }


    /*To compare the IPs.*/
    struct sockaddr saddr;
    int saddr_size = sizeof(saddr);
    auto start_time = std::chrono::steady_clock::now();

    /*An endless loop, on accepting any packets from the system at all.*/
    for (;;)
    {
       /*Receive packet to buffer.*/
       int data_size = recvfrom(sock, (char*)read_buffer, READ_BUFFER_SIZE, 0, (sockaddr*)&saddr,
       if (data_size == -1)
       {
            /*ERROR: 10022*/
            wprintf(L"recvfrom failed with error %d\n", WSAGetLastError());
            fuck_fd(sock);
            return READ_ERROR;
       }

       /*Obtaining the IP header of the received packet.*/
       struct ip_header *iph = (struct ip_header*)read_buffer;
       unsigned short iphdrlen = (iph->ihl) * 4;
       if (iphdrlen < 20)
       {
          fuck_fd(sock);
          return IP_HEADER_LEN_ERROR;
       }

       /*Retrieve the sender's IP from it.*/
       struct sockaddr_in source;
       memset(&source, 0, sizeof(source));
       source.sin_addr.s_addr = iph->saddr;

       /*Compare the IP to see if it matches the one we sent it to.
        * This is to discard other packets.*/
       if (source.sin_addr.s_addr != dest.s_addr)
       {
          if (debug) {std::cout << "Got the wrong package.\n";}

          /*A windowless for timeout might work here.*/
          auto current_time = std::chrono::steady_clock::now();
          auto elapsed_time = std::chrono::duration_cast<std::chrono::milliseconds>(current_time - start_time).count();
          if (elapsed_time >= recv_timeout_ms)
          {
                  std::cout << "timeout infinity\n\n";
             fuck_fd(sock);
             return INFINITY_TIMEOUT_EXITED;
          }

          /*If it's the wrong one, it catches again until it times out,
           * on a windowless loop. Or until the right packet arrives.*/
          continue;
       }
       else 
       {
#ifdef NESCA
          if (packet_trace)
          {
            struct in_addr addr;
            addr.s_addr = iph->saddr;
            std::string source_ip = inet_ntoa(addr);
            addr.s_addr = iph->daddr;
            std::string dest_ip = inet_ntoa(addr);

            struct tcp_header *tcph = (struct tcp_header*)(buffer+ iphdrlen);
            unsigned short id = ntohs(iph->id);
            unsigned int seq = ntohl(tcph->seq);
            unsigned int iplen = ntohs(iph->tot_len);
            packet_trace1.lock();
            np3.nlog_packet_trace("RCVD", "TCP", source_ip, dest_ip, dest_port,               source_port, "", iph->ttl, id, tcph->window, seq, iplen);
            packet_trace1.unlock();
          }
#endif
          /*If it's the right packet.
           * Fill the buffer with it.*/
          *buffer = read_buffer;
          fuck_fd(sock);
          return SUCCESS_READ;
       }
    }
    /*oh.*/
    fuck_fd(sock);
    return READ_ERROR;
}

Close sock function:

int fuck_fd(int fd)
{
#ifdef _WIN32
    WSACleanup();
    closesocket(fd);
#else
    close(fd);
#endif

Solution

  • Error 10022 is WSAEINVAL:

    Return code/value Description
    WSAEINVAL
    10022
    Invalid argument.
    Some invalid argument was supplied (for example, specifying an invalid level to the setsockopt function). In some instances, it also refers to the current state of the socket — for instance, calling accept on a socket that is not listening.

    Per Microsoft's recvfrom() documentation:

    If no error occurs, recvfrom returns the number of bytes received. If the connection has been gracefully closed, the return value is zero. Otherwise, a value of SOCKET_ERROR is returned, and a specific error code can be retrieved by calling WSAGetLastError.

    Error code Meaning
    WSAEINVAL The socket has not been bound with bind, or an unknown flag was specified, or MSG_OOB was specified for a socket with SO_OOBINLINE enabled, or (for byte stream-style sockets only) len was zero or negative.

    We can rule out the flags, since you are not using any.

    And, we can probably rule out the len parameter, assuming READ_BUFFER_SIZE is a positive value, and that your buffer really does point to a valid memory block of at least READ_BUFFER_SIZE bytes in size, and that READ_BUFFER_SIZE is large enough to handle the data you are receiving.

    And, we can rule out bind(), per Microsoft's TCP/IP raw sockets documentation:

    On Windows 7, Windows Vista, Windows XP with Service Pack 2 (SP2), and Windows XP with Service Pack 3 (SP3), the ability to send traffic over raw sockets has been restricted in several ways:

    ...

    • A call to the bind function with a raw socket for the IPPROTO_TCP protocol is not allowed.

      Note
      The bind function with a raw socket is allowed for other protocols (IPPROTO_IP, IPPROTO_UDP, or IPPROTO_SCTP, for example).

    So, what does that leave? recvfrom() is complaining about an invalid parameter, and the only unknown parameter in this code is the fromlen parameter, since the code you have posted is not showing what you are passing in for that parameter. recvfrom() takes 6 parameters, but the code provided is showing only 5 values being passed in. What are you passing in for the fromlen parameter? You should be passing in the address of your saddr_size variable. Do note that you should be resetting that variable's value every time you call recvfrom(), but you are only setting its value when you first declare it.

    In any case, knowing that Microsoft imposes various restrictions on using TCP with RAW sockets, you should consider using IPPROTO_RAW or IPPROTO_IP instead, and then just filter out any non-TCP traffic you are not interested in.


    On a side note: you should not be calling closesocket() after WSACleanup(), you have those two calls reversed. And, you are not calling WSACleanup() if socket() fails. This code should not be calling WSAStartup()/WSACleanup() to begin with, they belong at program startup/shutdown only, such as in your main()/WinMain() function.