Reputation: 113
if(port == 443){ // https
strcpy(buffer, "HTTP/1.0 200 Connection established\r\n\r\n");
write(client, buffer, strlen(buffer));
tunnel(server, client, buffer);
return;
}
tunnel() looks like below
void tunnel(int server, int client, char* buffer){
int servercon, clientcon;
int x;
x = fcntl(server, F_GETFL, 0);
fcntl(server, F_SETFL, x|O_NONBLOCK);
x = fcntl(client, F_GETFL, 0);
fcntl(client, F_SETFL, x|O_NONBLOCK);
while(true){
cout << "test" << endl;
clientcon = read(client, buffer, BUFSIZE);
write(server, buffer, clientcon);
servercon = read(server, buffer, BUFSIZE);
write(client, buffer, servercon);
if(???) // This condition
break;
}
close(client);
close(server);
}
I'm trying to implement tunneling when the proxy server receives CONNECT method. This code works well (HTTPS websites are successfully loaded) However, the problem is that the infinite while() loop can not be escaped because read() function sometimes never returns 0.
As a break condition, I tried several options.
1) servercon == 0 (Sometimes read() function never returns 0)
2) servercon == -1 (Secure connection failed occurs. I don't know why)
3) Add a variable i as a timer, and i<100000 (It works, but too inefficient)
How should I set the break condition?
(Make the socket non-blocking is necessary. Without it, the program stops at read() function forever. I don't know the reason, but making the socket non-blocking solved the problem.)
Upvotes: 1
Views: 415
Reputation: 595971
Your tunnel()
loop is completely ignoring the return values of read()
and write()
. Try something more like this instead:
bool writeAll(int sckt, void *buffer, size_t buflen)
{
char *pbuffer = (char*) buffer;
while (buflen > 0)
{
ssize_t numSent = write(sckt, pbuffer, buflen);
if (numSent < 0) {
if (errno == EAGAIN || errno == EWOULDBLOCK || errno == EINTR)
continue;
return false;
}
pbuffer += numSent;
buflen -= numSent;
}
return true;
}
...
strcpy(buffer, "HTTP/1.0 200 Connection established\r\n\r\n");
if (writeAll(client, buffer, strlen(buffer)))
tunnel(server, client, buffer);
close(client);
close(server);
return;
...
void tunnel(int server, int client, char *buffer)
{
int maxFD = max(server, client) + 1;
ssize_t numRead;
FD_SET fd;
int x = fcntl(server, F_GETFL, 0);
fcntl(server, F_SETFL, x | O_NONBLOCK);
x = fcntl(client, F_GETFL, 0);
fcntl(client, F_SETFL, x | O_NONBLOCK);
do
{
cout << "test" << endl;
FD_ZERO(&fd);
FD_SET(&fd, server);
FD_SET(&fd, client);
x = select(maxFD, &fd, NULL, NULL, NULL);
if (x < 0) break;
if (FD_ISSET(&fd, client))
{
numRead = read(client, buffer, BUFSIZE);
if (numRead <= 0) break;
if (!writeAll(server, buffer, numRead))
break;
}
if (FD_ISSET(&fd, server))
{
numRead = read(server, buffer, BUFSIZE);
if (numRead <= 0) break;
if (!writeAll(client, buffer, numRead))
break;
}
}
while (true);
}
As for why your reads are not always returning 0 when you are expecting, that is most likely due to the client and server using an HTTP keep-alive to keep the tunnel connection open after the server has sent its response, so the client can send subsequent requests to the same server using the same TCP connection. Establishing a new TCP connection, and even a new HTTPS session, on every HTTP/S request is time-consuming and wastes bandwidth as it involves many round trips for the TCP and TLS handshakes. So the default behavior of HTTP 1.1 and later is to keep a connection open unless either party explicitly states a close is wanted via a Connection: close
header.
Upvotes: 1