I am writing a socks4 proxy in C and everything works well, like I tested the data forwarding with a simple echo server as the remote peer.
But now I am not sure how to handle the data if the other protocols are used as they have different package sizes.
How do I know if the transmission is complete or how do I have to handle that everything is transmitted properly to the remote peer and vice versa to the client?
Here is my thread connection handler function:
// Contains the client socket and remote peer socket after successful connect:
typedef struct socks_connection {
int client_sock;
int target_sock;
} socks_connection;
void *socks_connection_thread(void *sockets) {
printf("Thread started\n");
socks_connection conn = *(socks_connection*) sockets;
uint8_t buf[512];
int rbytes;
int wbytes;
for(;;) {
rbytes = recv(conn.client_sock, buf, sizeof(buf), 0);
printf("read: %d\n", rbytes);
if(rbytes < 0) {
perror("read");
}
wbytes = send(conn.target_sock, buf, rbytes, 0);
printf("send: %d\n", wbytes);
if(rbytes < 0) {
perror("send");
}
rbytes = recv(conn.target_sock, buf, sizeof(buf), 0);
if(rbytes < 0) {
perror("read");
}
printf("read: %d\n", rbytes);
wbytes = send(conn.client_sock, buf, rbytes, 0);
printf("send: %d\n", wbytes);
if(rbytes < 0) {
perror("send");
}
}
}
EDIT: I am trying to use poll()
instead of select()
and I want to know if it is normal for poll
to use less code and additional work like i.e. FD_SET, FD_ISSET
etc.
#define MAX_SOCKETS 2
#define DEFAULT_TIMEOUT (3 * 60 * 1000)
#define CLIENT_POLL 0
#define REMOTE_POLL 1
void *socks_connection_thread(void *pipefd) {
pthread_detach(pthread_self());
socks_connection *conn = pipefd;
int rc = 0;
int timeout = DEFAULT_TIMEOUT;
struct pollfd pfds[MAX_SOCKETS];
nfds_t nfds = MAX_SOCKETS;
uint8_t client_buf[1024];
size_t client_buf_size = 0;
uint8_t target_buf[1024];
size_t target_buf_size = 0;
ssize_t num_bytes;
memset(&pfds, 0, sizeof(pfds));
int opt = 1;
ioctl(conn->client_sock, FIONBIO, &opt);
ioctl(conn->target_sock, FIONBIO, &opt);
pfds[CLIENT_POLL].fd = conn->client_sock;
pfds[CLIENT_POLL].events = POLLIN;
pfds[REMOTE_POLL].fd = conn->target_sock;
pfds[REMOTE_POLL].events = POLLIN;
for(;;) {
if(socksshutdown) break;
/* waiting for some events */
rc = poll(pfds, MAX_SOCKETS, timeout);
if(rc < 0) {
fprintf(stderr, "poll() failed: %s\n", strerror(errno));
break;
}
if(rc == 0) {
fprintf(stderr, "poll() timed out. End Connection\n");
break;
}
/* there is something to read form the client side */
if(pfds[CLIENT_POLL].revents & POLLIN)
{
num_bytes = readFromSocket(conn->client_sock, client_buf, sizeof(client_buf));
if(num_bytes < 0) break; // client connection lost
if(num_bytes > 0) {
printf("read from client: %s (%ld)\n", client_buf, num_bytes);
client_buf_size += num_bytes;
}
num_bytes = sendToSocket(conn->target_sock, client_buf, num_bytes);
if(num_bytes < 0) break;
if(num_bytes > 0) {
printf("forward to remote peer: %s (%ld)\n", client_buf, num_bytes);
}
pfds[CLIENT_POLL].revents = 0;
}
/* there is something to read from the remote side */
else if(pfds[REMOTE_POLL].revents & POLLIN)
{
//printf("Got data from remote.\n");
num_bytes = readFromSocket(conn->target_sock, target_buf, sizeof(target_buf));
if (num_bytes < 0) break; // remote connection lost
if (num_bytes > 0) {
printf("read from client: %s (%ld)\n", target_buf, num_bytes);
target_buf_size += num_bytes;
}
num_bytes = sendToSocket(conn->client_sock, target_buf, target_buf_size);
if (num_bytes < 0) break;
if (num_bytes > 0) {
printf("forward to client: %s (%ld)\n", target_buf, num_bytes);
// remove the sent bytes...
}
pfds[REMOTE_POLL].revents = 0;
} else {
// unexpected event result appeared so close the connection
break;
}
}
// all done
close(conn->client_sock);
close(conn->target_sock);
printf("Thread terminating\n");
}