Reputation: 15
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
Reputation: 598134
You have several bugs in your code:
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.
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.
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
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