Jason Brown
Jason Brown

Reputation: 15

Linux socket send() error EAGAIN

I am designing an HTTP tunnel using a proxy and the HTTP CONNECT request. About 90% of the time, when I try to send my connect request CONNECT 192.168.1.68:3001 HTTP/1.1\r\n\r\n the send fails, returning errno = 11, "Try Again".

I am using non-blocking sockets, and the socket is closed then re-opened between connect attempts. I don't think the send buffer is full, because I am not sending much data. I'm confused why it is sometimes connecting and sometimes not (and there doesn't appear to be a pattern, sometimes it will connect twice in a row, sometimes there are 10 failures between connects).

What are some of the reasons that I could be getting this error?

Edit:

(Code)

if (connect(sock, pSockAddr, iSockAddrSize)) {
    ...
    char message[80];
    sprintf(message, "CONNECT 192.168.1.68:3001 HTTP/1.1\r\n\r\n");
    printf("*J* Message loaded.\n"); //*J*
    printf("*J* %s\n", message); //*J*
    if (send(sock, message, strlen(message) + 1, 0) < 0) printf("*J* Send Failed: %d\n", errno); 
    else printf("*J* Data Sent.\n");
    }
...
printf("\n*J* Close Socket (RESET HOST COMMAND)");
closesocket(sock);

(Output)

[SUCCESS]
*J* Starting connect...
*J* Message loaded.
*J* CONNECT 192.168.1.68:3001 HTTP/1.1


*J* Data Sent.
*J* rv = 1
*J* Ending connect...

[FAIL]
*J* Starting connect...
*J* Message loaded.
*J* CONNECT 192.168.1.68:3001 HTTP/1.1


*J* Send Failed: 11
*J* Ending connect...

Upvotes: 0

Views: 12320

Answers (2)

Remy Lebeau
Remy Lebeau

Reputation: 598134

You have several bugs in your code:

  1. connect() returns 0 on success and -1 on error. An if statement evaluates to true for any non-zero value. Which means your code is sending the CONNECT message when connect() is failing with an error. However, not all errors are fatal. In particular, since you are using a non-blocking socket, an EINPROGRESS/WSAEWOULDBLOCK error indicates the connection is still pending and you need to use select() or (e)poll() to wait for the socket to actually finish connecting (or fail). So, your code would only work if connect() connects asynchronously and just happens to fully connect to the proxy before you have a chance to call send(). But this is behavior you should not rely on. You need to handle errors correctly.

  2. the return value of send() specifies how many bytes were actually accepted, or -1 on error. Fewer bytes than requested may be accepted (especially on a non-blocking socket). And you need to handle non-fatal errors, in particular (WSA)EWOULDBLOCK (and EAGAIN, and possibly also EINTR, on non-Windows platforms), which means the socket was unable to accept new data at the moment send() was called, so call send() again. You should be calling send() in a loop until all bytes have been sent, or a fatal error occurs.

  3. Even if you were send()'ing data correctly, your use of + 1 after strlen() is also wrong. You are including the null terminator in the data to be sent, but that is not part of the CONNECT protocol. The null terminator will be treated by the proxy as application data and will be forwarded as-is through the tunnel to the next server (if the tunnel is successfully opened), breaking communications with that server.

With that said, try something more like the following instead. Your use of closeocket() indicates you are programming for Windows, so this is Windows-specific:

int ret, err;
...
ret = connect(sock, pSockAddr, iSockAddrSize);
if (ret == -1)
{
    err = WSAGetLastError();

    if (err == WSAEWOULDBLOCK)
    {
        fd_set wfd;
        FD_ZERO(&wfd);
        FD_SET(sock, &wfd);

        fd_set efd;
        FD_ZERO(&efd);
        FD_SET(sock, &efd);

        timeval timeout;
        timeout.tv_sec = ...;
        timeout.tv_usec = ...;

        ret = select(0, NULL, &wfd, &wfd, &timeout);

        if (ret == 0)
        {
            printf("*J* Connect Timeout\n"); 
            // handle timeout as needed ...
            return;
        }

        if ((ret > 0) && FD_ISSET(sock, &efd))
        {
            err = 0;
            getsockopt(sock, SOL_SOCKET, SO_ERROR, (char*)&err, sizeof(err));
            ret = -1;
        }
    }

    if (ret == -1)
    {
        printf("*J* Connect Failed: %d\n", err); 
        // handle fatal error as needed ...
        return;
    }
}

...

char message[80];
sprintf(message, "CONNECT 192.168.1.68:3001 HTTP/1.1\r\n\r\n");
printf("*J* Message loaded.\n"); //*J*
printf("*J* %s\n", message); //*J*

char *pmsg = message;
int len = strlen(message);

do
{
    ret = send(sock, pmsg, len, 0);
    if (ret == -1)
    {
        err = getLastSocketError();    

        if (err == WSAEWOULDBLOCK)
        {
            fd_set wfd;
            FD_ZERO(&wfd);
            FD_SET(sock, &wfd);

            timeval timeout;
            timeout.tv_sec = ...;
            timeout.tv_usec = ...;

            ret = select(0, NULL, &wfd, NULL, &timeout);

            if (ret > 0)
                continue;

            if (ret == 0)
            {
                printf("*J* Send Timeout\n"); 
                // handle timeout as needed ...
                return;
            }

            err = WSAGetLastError();
        }

        printf("*J* Send Failed: %d\n", err); 
        // handle fatal error as needed ...
        return;
    }
    else
    {
        pmsg += ret;
        len += ret;
    }
}
while (len > 0);

printf("*J* Data Sent\n");

...

printf("*J* Close Socket\n");
closesocket(sock);

If you want something a little more cross-platform, try this instead (or use a cross-platform socket library instead):

int getLastSocketError()
{
    #ifdef WINDOWS
    return WSAGetLastError();
    #else
    return errno;
    #endif
};

int ret, err;
...
ret = connect(sock, pSockAddr, iSockAddrSize);
if (ret == -1)
{
    err = getLastSocketError();    

    if (
        #ifdef WINDOWS
        err == WSAEWOULDBLOCK
        #else
        err == EINPROGRESS
        #endif
        )
    {
        #ifndef WINDOWS
        do {
        #endif

        fd_set wfd;
        FD_ZERO(&wfd);
        FD_SET(sock, &wfd);

        #ifdef WINDOWS
        fd_set efd;
        FD_ZERO(&efd);
        FD_SET(sock, &efd);
        #endif

        timeval timeout;
        timeout.tv_sec = ...;
        timeout.tv_usec = ...;

        #ifdef WINDOWS
        ret = select(0, NULL, &wfd, &wfd, &timeout);
        #else
        ret = select(sock+1, NULL, &wfd, NULL, &timeout);
        #endif

        if (ret == 0)
        {
            printf("*J* Connect Timeout\n"); 
            // handle timeout as needed ...
            return;
        }

        #ifdef WINDOWS
        if ((ret > 0) && FD_ISSET(sock, &efd))
        {
            err = 0;
            getsockopt(sock, SOL_SOCKET, SO_ERROR, (char*)&err, sizeof(err));
            ret = -1;
        }
        #endif

        #ifndef WINDOWS
        } while ((ret == -1) && (errno == EINTR));
        #endif
    }

    if (ret == -1)
    {
        printf("*J* Connect Failed: %d\n", err); 
        // handle fatal error as needed ...
        return;
    }
}

...

char message[80];
sprintf(message, "CONNECT 192.168.1.68:3001 HTTP/1.1\r\n\r\n");
printf("*J* Message loaded.\n"); //*J*
printf("*J* %s\n", message); //*J*

char *pmsg = message;
int len = strlen(message);

do
{
    ret = send(sock, pmsg, len, 0);
    if (ret == -1)
    {
        err = getLastSocketError();    

        if (
            #ifdef WINDOWS
            err == WSAEWOULDBLOCK
            #else
            (err == EWOULDBLOCK) || (err == EAGAIN)
            #endif
            )
        {
            fd_set wfd;
            FD_ZERO(&wfd);
            FD_SET(sock, &wfd);

            timeval timeout;
            timeout.tv_sec = ...;
            timeout.tv_usec = ...;

            ret = select(
                #ifdef WINDOWS
                0
                #else
                sock+1
                #endif
                , NULL, &wfd, NULL, &timeout);

            if (ret > 0)
                continue;

            if (ret == 0)
            {
                printf("*J* Send Timeout\n"); 
                // handle timeout as needed ...
                return;
            }

            err = getLastSocketError();
        }

        #ifndef WINDOWS
        if (err != EINTR)
        #endif
        {
            printf("*J* Send Failed: %d\n", err); 
            // handle fatal error as needed ...
            return;
        }
    }
    else
    {
        pmsg += ret;
        len += ret;
    }
}
while (len > 0);

printf("*J* Data Sent\n");

...

printf("*J* Close Socket\n");

#ifdef WINDOWS
closesocket(sock);
#else
close(sock);
#endif

Upvotes: 4

Steffen Ullrich
Steffen Ullrich

Reputation: 123561

if (connect(sock, pSockAddr, iSockAddrSize)) {
    ...
    if (send(sock, message, strlen(message) + 1, 0) < 0) printf("*J* Send Failed: %d\n", errno); 

The block within if (connect(.. is entered when connect returns a non-zero value. Since connect returns 0 on success and -1 on error (permanent or temporarily) this means that the block is entered when connect is not (yet) successful. Calling send in this case will in most cases fail since the connection is not yet done but in some lucky cases might succeed if the connection was successful in the meantime.

The intend was probably to enter the block if the connect succeded. In this case the correct handling would have been

if (0 == connect(sock, pSockAddr, iSockAddrSize)) {
     ...

Of course, since non-blocking sockets are used it is unlikely that the connect will succeed immediately so there would be a proper handling of non-blocking connect required.

Upvotes: 0

Related Questions