C: socket connection timeout - c

I have a simple program to check if a port is open, but I want to shorten the timeout length on the socket connection because the default is far too long. I'm not sure how to do this though. Here's the code:
#include <sys/socket.h>
#include <sys/time.h>
#include <sys/types.h>
#include <arpa/inet.h>
#include <netinet/in.h>
#include <errno.h>
#include <fcntl.h>
#include <stdio.h>
#include <netdb.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
int main(int argc, char **argv) {
u_short port; /* user specified port number */
char addr[1023]; /* will be a copy of the address entered by u */
struct sockaddr_in address; /* the libc network address data structure */
short int sock = -1; /* file descriptor for the network socket */
if (argc != 3) {
fprintf(stderr, "Usage %s <port_num> <address>", argv[0]);
return EXIT_FAILURE;
}
address.sin_addr.s_addr = inet_addr(argv[2]); /* assign the address */
address.sin_port = htons(atoi(argv[2])); /* translate int2port num */
sock = socket(AF_INET, SOCK_STREAM, 0);
if (connect(sock,(struct sockaddr *)&address,sizeof(address)) == 0) {
printf("%i is open\n", port);
}
close(sock);
return 0;
}

Set the socket non-blocking, and use select() (which takes a timeout parameter). If a non-blocking socket is trying to connect, then select() will indicate that the socket is writeable when the connect() finishes (either successfully or unsuccessfully). You then use getsockopt() to determine the outcome of the connect():
int main(int argc, char **argv) {
u_short port; /* user specified port number */
char *addr; /* will be a pointer to the address */
struct sockaddr_in address; /* the libc network address data structure */
short int sock = -1; /* file descriptor for the network socket */
fd_set fdset;
struct timeval tv;
if (argc != 3) {
fprintf(stderr, "Usage %s <port_num> <address>\n", argv[0]);
return EXIT_FAILURE;
}
port = atoi(argv[1]);
addr = argv[2];
address.sin_family = AF_INET;
address.sin_addr.s_addr = inet_addr(addr); /* assign the address */
address.sin_port = htons(port); /* translate int2port num */
sock = socket(AF_INET, SOCK_STREAM, 0);
fcntl(sock, F_SETFL, O_NONBLOCK);
connect(sock, (struct sockaddr *)&address, sizeof(address));
FD_ZERO(&fdset);
FD_SET(sock, &fdset);
tv.tv_sec = 10; /* 10 second timeout */
tv.tv_usec = 0;
if (select(sock + 1, NULL, &fdset, NULL, &tv) == 1)
{
int so_error;
socklen_t len = sizeof so_error;
getsockopt(sock, SOL_SOCKET, SO_ERROR, &so_error, &len);
if (so_error == 0) {
printf("%s:%d is open\n", addr, port);
}
}
close(sock);
return 0;
}

This article might help:
Connect with timeout (or another use for select() )
Looks like you put the socket into non-blocking mode until you've connected, and then put it back into blocking mode once the connection's established.
void connect_w_to(void) {
int res;
struct sockaddr_in addr;
long arg;
fd_set myset;
struct timeval tv;
int valopt;
socklen_t lon;
// Create socket
soc = socket(AF_INET, SOCK_STREAM, 0);
if (soc < 0) {
fprintf(stderr, "Error creating socket (%d %s)\n", errno, strerror(errno));
exit(0);
}
addr.sin_family = AF_INET;
addr.sin_port = htons(2000);
addr.sin_addr.s_addr = inet_addr("192.168.0.1");
// Set non-blocking
if( (arg = fcntl(soc, F_GETFL, NULL)) < 0) {
fprintf(stderr, "Error fcntl(..., F_GETFL) (%s)\n", strerror(errno));
exit(0);
}
arg |= O_NONBLOCK;
if( fcntl(soc, F_SETFL, arg) < 0) {
fprintf(stderr, "Error fcntl(..., F_SETFL) (%s)\n", strerror(errno));
exit(0);
}
// Trying to connect with timeout
res = connect(soc, (struct sockaddr *)&addr, sizeof(addr));
if (res < 0) {
if (errno == EINPROGRESS) {
fprintf(stderr, "EINPROGRESS in connect() - selecting\n");
do {
tv.tv_sec = 15;
tv.tv_usec = 0;
FD_ZERO(&myset);
FD_SET(soc, &myset);
res = select(soc+1, NULL, &myset, NULL, &tv);
if (res < 0 && errno != EINTR) {
fprintf(stderr, "Error connecting %d - %s\n", errno, strerror(errno));
exit(0);
}
else if (res > 0) {
// Socket selected for write
lon = sizeof(int);
if (getsockopt(soc, SOL_SOCKET, SO_ERROR, (void*)(&valopt), &lon) < 0) {
fprintf(stderr, "Error in getsockopt() %d - %s\n", errno, strerror(errno));
exit(0);
}
// Check the value returned...
if (valopt) {
fprintf(stderr, "Error in delayed connection() %d - %s\n", valopt, strerror(valopt)
);
exit(0);
}
break;
}
else {
fprintf(stderr, "Timeout in select() - Cancelling!\n");
exit(0);
}
} while (1);
}
else {
fprintf(stderr, "Error connecting %d - %s\n", errno, strerror(errno));
exit(0);
}
}
// Set to blocking mode again...
if( (arg = fcntl(soc, F_GETFL, NULL)) < 0) {
fprintf(stderr, "Error fcntl(..., F_GETFL) (%s)\n", strerror(errno));
exit(0);
}
arg &= (~O_NONBLOCK);
if( fcntl(soc, F_SETFL, arg) < 0) {
fprintf(stderr, "Error fcntl(..., F_SETFL) (%s)\n", strerror(errno));
exit(0);
}
// I hope that is all
}

The answers about using select()/poll() are right and code should be written this way to be portable.
However, since you're on Linux, you can do this:
int synRetries = 2; // Send a total of 3 SYN packets => Timeout ~7s
setsockopt(fd, IPPROTO_TCP, TCP_SYNCNT, &synRetries, sizeof(synRetries));
See man 7 tcp and man setsockopt.
I used this to speed up the connect-timeout in a program I needed to patch quickly. Hacking it to timeout via select()/poll() was not an option.

Here is a modern connect_with_timeout implementation, using poll, with proper error and signal handling:
#include <sys/socket.h>
#include <fcntl.h>
#include <poll.h>
#include <time.h>
int connect_with_timeout(int sockfd, const struct sockaddr *addr, socklen_t addrlen, unsigned int timeout_ms) {
int rc = 0;
// Set O_NONBLOCK
int sockfd_flags_before;
if((sockfd_flags_before=fcntl(sockfd,F_GETFL,0)<0)) return -1;
if(fcntl(sockfd,F_SETFL,sockfd_flags_before | O_NONBLOCK)<0) return -1;
// Start connecting (asynchronously)
do {
if (connect(sockfd, addr, addrlen)<0) {
// Did connect return an error? If so, we'll fail.
if ((errno != EWOULDBLOCK) && (errno != EINPROGRESS)) {
rc = -1;
}
// Otherwise, we'll wait for it to complete.
else {
// Set a deadline timestamp 'timeout' ms from now (needed b/c poll can be interrupted)
struct timespec now;
if(clock_gettime(CLOCK_MONOTONIC, &now)<0) { rc=-1; break; }
struct timespec deadline = { .tv_sec = now.tv_sec,
.tv_nsec = now.tv_nsec + timeout_ms*1000000l};
// Wait for the connection to complete.
do {
// Calculate how long until the deadline
if(clock_gettime(CLOCK_MONOTONIC, &now)<0) { rc=-1; break; }
int ms_until_deadline = (int)( (deadline.tv_sec - now.tv_sec)*1000l
+ (deadline.tv_nsec - now.tv_nsec)/1000000l);
if(ms_until_deadline<0) { rc=0; break; }
// Wait for connect to complete (or for the timeout deadline)
struct pollfd pfds[] = { { .fd = sockfd, .events = POLLOUT } };
rc = poll(pfds, 1, ms_until_deadline);
// If poll 'succeeded', make sure it *really* succeeded
if(rc>0) {
int error = 0; socklen_t len = sizeof(error);
int retval = getsockopt(sockfd, SOL_SOCKET, SO_ERROR, &error, &len);
if(retval==0) errno = error;
if(error!=0) rc=-1;
}
}
// If poll was interrupted, try again.
while(rc==-1 && errno==EINTR);
// Did poll timeout? If so, fail.
if(rc==0) {
errno = ETIMEDOUT;
rc=-1;
}
}
}
} while(0);
// Restore original O_NONBLOCK state
if(fcntl(sockfd,F_SETFL,sockfd_flags_before)<0) return -1;
// Success
return rc;
}

On Linux you can also use:
struct timeval timeout;
timeout.tv_sec = 7; // after 7 seconds connect() will timeout
timeout.tv_usec = 0;
setsockopt(fd, SOL_SOCKET, SO_SNDTIMEO, &timeout, sizeof(timeout));
connect(...)
Don't forget to clear SO_SNDTIMEO after connect() if you don't need it.

This one has parametrized ip, port, timeout in seconds, handle connection errors and give you connection time in milliseconds:
#include <sys/socket.h>
#include <sys/time.h>
#include <sys/types.h>
#include <arpa/inet.h>
#include <netinet/in.h>
#include <errno.h>
#include <fcntl.h>
#include <stdio.h>
#include <netdb.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <time.h>
int main(int argc, char **argv) {
struct sockaddr_in addr_s;
char *addr;
short int fd=-1;
int port;
fd_set fdset;
struct timeval tv;
int rc;
int so_error;
socklen_t len;
struct timespec tstart={0,0}, tend={0,0};
int seconds;
if (argc != 4) {
fprintf(stderr, "Usage: %s <ip> <port> <timeout_seconds>\n", argv[0]);
return 1;
}
addr = argv[1];
port = atoi(argv[2]);
seconds = atoi(argv[3]);
addr_s.sin_family = AF_INET; // utilizzo IPv4
addr_s.sin_addr.s_addr = inet_addr(addr);
addr_s.sin_port = htons(port);
clock_gettime(CLOCK_MONOTONIC, &tstart);
fd = socket(AF_INET, SOCK_STREAM, 0);
fcntl(fd, F_SETFL, O_NONBLOCK); // setup non blocking socket
// make the connection
rc = connect(fd, (struct sockaddr *)&addr_s, sizeof(addr_s));
if ((rc == -1) && (errno != EINPROGRESS)) {
fprintf(stderr, "Error: %s\n", strerror(errno));
close(fd);
return 1;
}
if (rc == 0) {
// connection has succeeded immediately
clock_gettime(CLOCK_MONOTONIC, &tend);
printf("socket %s:%d connected. It took %.5f seconds\n",
addr, port, (((double)tend.tv_sec + 1.0e-9*tend.tv_nsec) - ((double)tstart.tv_sec + 1.0e-9*tstart.tv_nsec)));
close(fd);
return 0;
} /*else {
// connection attempt is in progress
} */
FD_ZERO(&fdset);
FD_SET(fd, &fdset);
tv.tv_sec = seconds;
tv.tv_usec = 0;
rc = select(fd + 1, NULL, &fdset, NULL, &tv);
switch(rc) {
case 1: // data to read
len = sizeof(so_error);
getsockopt(fd, SOL_SOCKET, SO_ERROR, &so_error, &len);
if (so_error == 0) {
clock_gettime(CLOCK_MONOTONIC, &tend);
printf("socket %s:%d connected. It took %.5f seconds\n",
addr, port, (((double)tend.tv_sec + 1.0e-9*tend.tv_nsec) - ((double)tstart.tv_sec + 1.0e-9*tstart.tv_nsec)));
close(fd);
return 0;
} else { // error
printf("socket %s:%d NOT connected: %s\n", addr, port, strerror(so_error));
}
break;
case 0: //timeout
fprintf(stderr, "connection timeout trying to connect to %s:%d\n", addr, port);
break;
}
close(fd);
return 0;
}

The two socket options SO_RCVTIMEO and SO_SNDTIMEO have no effect on connect. Below is a link to the screenshot which includes this explanation, here I am just briefing it. The apt way of implementing timeouts with connect are using signal or select or poll.
Signals
connect can be interrupted by a self generated signal SIGALRM by using syscall (wrapper) alarm. But, a signal disposition should be installed for the same signal otherwise the program would be terminated. The code goes like this...
#include<stdio.h>
#include<stdlib.h>
#include<unistd.h>
#include<sys/socket.h>
#include<netinet/in.h>
#include<signal.h>
#include<errno.h>
static void signal_handler(int signo)
{
return; // Do nothing just interrupt.
}
int main()
{
/* Register signal handler */
struct sigaction act, oact;
act.sa_handler = signal_handler;
sigemptyset(&act.sa_mask);
act.sa_flags = 0;
#ifdef SA_INTERRUPT
act.sa_flags |= SA_INTERRUPT;
#endif
if(sigaction(SIGALRM, &act, &oact) < 0) // Error registering signal handler.
{
fprintf(stderr, "Error registering signal disposition\n");
exit(1);
}
/* Prepare your socket and sockaddr structures */
int sockfd;
struct sockaddr* servaddr;
/* Implementing timeout connect */
int sec = 30;
if(alarm(sec) != 0)
fprintf(stderr, "Already timer was set\n");
if(connect(sockfd, servaddr, sizeof(struct sockaddr)) < 0)
{
if(errno == EINTR)
fprintf(stderr, "Connect timeout\n");
else
fprintf(stderr, "Connect failed\n");
close(sockfd);
exit(1);
}
alarm(0); /* turn off the alarm */
sigaction(SIGALRM, &oact, NULL); /* Restore the default actions of SIGALRM */
/* Use socket */
/* End program */
close(sockfd);
return 0;
}
Select or Poll
As already some users provided nice explanation on how to use select to achieve connect timeout, it would not be necessary for me to reiterate the same. poll can be used in the same way. However, there are few mistakes that are common in all of the answers, which I would like to address.
Even though socket is non-blocking, if the server to which we are connecting is on the same local machine, connect may return with success. So it is advised to check the return value of connect before calling select.
Berkeley-derived implementations (and POSIX) have the following rules for non-blocking sockets and connect.
When the connection completes successfully, the descriptor becomes writable (p. 531 of TCPv2).
When the connection establishment encounters an error, the descriptor becomes both readable and writable (p. 530 of TCPv2).
So the code should handle these cases, here I just code the necessary modifications.
/* All the code stays */
/* Modifications at connect */
int conn_ret = connect(sockfd, servaddr, sizeof(struct sockdaddr));
if(conn_ret == 0)
goto done;
/* Modifications at select */
int sec = 30;
for( ; ; )
{
struct timeval timeo;
timeo.tv_sec = sec;
timeo.tv_usec = 0;
fd_set wr_set, rd_set;
FDZERO(&wr_set);
FD_SET(sockfd, &wr_set);
rd_set = wr_set;
int sl_ret = select(sockfd + 1, &rd_set, &wr_set, NULL, &timeo);
/* All the code stays */
}
done:
/* Use your socket */

Is there anything wrong with Nahuel Greco's solution aside from the compilation error?
If I change one line
// Compilation error
setsockopt(fd, SO_SNDTIMEO, &timeout, sizeof(timeout));
to
// Fixed?
setsockopt(fd, SOL_SOCKET, SO_SNDTIMEO, &timeout, sizeof(timeout));
then it seems to work as advertised - socket() returns a timeout error.
Resulting code:
struct timeval timeout;
timeout.tv_sec = 7; // after 7 seconds connect() will timeout
timeout.tv_usec = 0;
setsockopt(fd, SOL_SOCKET, SO_SNDTIMEO, &timeout, sizeof(timeout));
connect(...)
I'm not versed enough to know the tradeoffs are between a send timeout and a non-blocking socket, but I'm curious to learn.

Related

No response from recv when using nonblocking socket

I am trying to create a portscanner in c. If the port is open, I want to get a response from the server. When I use regular blocking sockets, this works fine. For example, I know that for a certain address on my network, if I check port 80, it will return the html page to me when I call recv. I have tested this, and it works correctly every time.
However, I want to use nonblocking sockets, because sometimes certain servers will not respond and will cause the program to hang. I was able to get the nonblocking sockets to (kindof) work (the code is currently commented out below). I could see which ports were open, which were closed, and which timed out, but I was not able to get a response from the server (even though I know it should send one). What am I doing wrong?
tl;dr: When using nonblocking sockets (vs blocking), recv doesn't return any data.
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netdb.h>
#include <fcntl.h>
#define MAX_LEN 100000
int main(int argc, char **argv)
{
int sock, test_sock;
struct sockaddr_in server_addr;
struct hostent *hp;
char buf[MAX_LEN];
int num_bytes;
int err_code;
int START_PORT = 1;
int END_PORT = 100;
fd_set fdset;
struct timeval tv;
int opts;
// resolve server name for its IP address, etc.
hp = gethostbyname(argv[1]);
if (NULL == hp) {
perror("gethostbyname");
exit(2);
}
//printf("Here1\n");
// build remote server addr/port
memset(&server_addr, 0, sizeof(server_addr));
server_addr.sin_family = AF_INET;
memcpy(&server_addr.sin_addr, hp->h_addr, hp->h_length);
//server_addr.sin_port = htons(atoi(argv[2]));
test_sock = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
for(int i=START_PORT; i<=END_PORT; i++) {
printf("Here2\n");
sock = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP); //created the tcp socket
//opts = fcntl(sock, F_SETFL, O_NONBLOCK);
printf("Here3\n");
if (sock < 0)
{
perror("Socket()\n");
exit(1);
}
server_addr.sin_port = htons(i);
// connect to server
printf("Here4\n");
err_code = connect(sock, (struct sockaddr *)&server_addr, sizeof(server_addr));
printf("Here5\n");
/* ... */
if (err_code < 0) {
printf("Port %d: connection refused\n", i);
//exit(3);
} else {
printf("Port %d:\n", i);
memset(buf, 0, MAX_LEN);
// Create message to send
char message[256];
strcpy(message, "GET / HTTP/1.0\r\nHost: ");
strcat(message, argv[1]);
strcat(message, "\r\n\r\n");
unsigned total_bytes_sent = 0;
num_bytes = send(sock, message, strlen(message), 0);
if (num_bytes < 0) {
perror("send");
exit(4);
}
unsigned total_bytes_received = 0;
while(1) {
num_bytes = recv(sock, buf+total_bytes_received, MAX_LEN, 0);
if(num_bytes <= 0){
break;
}
total_bytes_received += num_bytes;
}
// display received ack message
//printf("Port %d:\n", i);
fflush(stdout);
write(1, buf, total_bytes_received);
printf("\n");
printf("Done...\n");
}
close(sock);
}
// close sock to release resource
close(sock);
return 0;
}
SOLUTION
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netdb.h>
#include <errno.h>
#include <fcntl.h>
#define MAX_LEN 100000
int main(int argc, char **argv)
{
int sock, sock_test;
struct sockaddr_in server_addr;
struct hostent *hp;
char buf[MAX_LEN];
int num_bytes;
int err_code;
int START_PORT = 1;
int END_PORT = 100;
int valid = 1;
fd_set fdset;
struct timeval tv;
// resolve server name for its IP address, etc.
hp = gethostbyname(argv[1]);
if (NULL == hp) {
perror("gethostbyname");
exit(2);
}
// build remote server addr/port
memset(&server_addr, 0, sizeof(server_addr));
server_addr.sin_family = AF_INET;
memcpy(&server_addr.sin_addr, hp->h_addr, hp->h_length);
for(int i=START_PORT; i<=END_PORT; i++) {
sock_test = socket(AF_INET, SOCK_STREAM, 0);
if (sock_test < 0)
{
perror("Socket()\n");
exit(1);
}
fcntl(sock_test, F_SETFL, O_NONBLOCK);
server_addr.sin_port = htons(i);
connect(sock_test, (struct sockaddr *)&server_addr, sizeof(server_addr));
FD_ZERO(&fdset);
FD_SET(sock_test, &fdset);
tv.tv_sec = 3;
tv.tv_usec = 0;
if (select(sock_test + 1, NULL, &fdset, NULL, &tv) == 1)
{
int so_error;
socklen_t len = sizeof so_error;
getsockopt(sock_test, SOL_SOCKET, SO_ERROR, &so_error, &len);
if (so_error == 0) {
printf("%s:%d is open\n", argv[1], i);
memset(buf, 0, MAX_LEN);
// Create message to send
char message[256];
strcpy(message, "GET / HTTP/1.0\r\nHost: ");
strcat(message, argv[1]);
strcat(message, "\r\n\r\n");
printf("Here6\n");
unsigned total_bytes_sent = 0;
num_bytes = send(sock_test, message, strlen(message), 0);
printf("Here7\n");
int retry = 3;
unsigned total_bytes_received = 0;
while(retry) {
num_bytes = recv(sock_test, buf+total_bytes_received, MAX_LEN, 0);
if (0 == num_bytes)
{
/* socket has been closed by peer */
break;
}
else if(-1 == num_bytes)
{
if ((EAGAIN == errno) || (EWOULDBLOCK == errno))
{
/* no data to be read on socket */
retry--;
/* wait one second */
sleep(1);
}
else
{
/* other error */
perror("recv");
break;
}
}
else
{
total_bytes_received += num_bytes;
}
}
// display received ack message
//printf("Port %d:\n", i);
fflush(stdout);
write(1, buf, total_bytes_received);
printf("\n");
printf("Done...\n");
}
else
{
//printf("%s:%d is closed\n", argv[1], i);
}
} else {
printf("timed out\n");
valid = 0; //set the boolean flag to false
}
close(sock_test);
}
// close sock to release resource
close(sock_test);
return 0;
}
As pointed in comments, in non-blocking mode, you have to handle cases when
server is not ready to send data.
For man recv(3)
Return Value
Upon successful completion, recv() shall return the length of the message in bytes. If no messages are available to be received and the peer has performed an orderly shutdown, recv() shall return 0. Otherwise, -1 shall be returned and errno set to indicate the error.
Errors
The recv() function shall fail if:
EAGAIN or EWOULDBLOCK
The socket's file descriptor is marked O_NONBLOCK and no data is waiting to be received; or MSG_OOB is set and no out-of-band data is available and either the socket's file descriptor is marked O_NONBLOCK or the socket does not support blocking to await out-of-band data.
Since your client may try to read before the server send something, you must
adapt your code to wait:
/* maximum number of retry, one second per retry */
int retry = 10;
unsigned total_bytes_received = 0;
while(retry) {
num_bytes = recv(sock, buf+total_bytes_received, MAX_LEN, 0);
if (0 == num_bytes)
{
/* socket has been closed by peer */
break;
}
else if(-1 == num_bytes)
{
if ((EAGAIN == errno) || (EWOULDBLOCK == errno))
{
/* no data to be read on socket */
retry--;
/* wait one second */
sleep(1);
}
else
{
/* other error */
perror("recv");
break;
}
}
else
{
total_bytes_received += num_bytes;
}
}

Multicasting in C: Binary does not receive when using addrinfo

I have this funny little problem in two nearly identical programs. What I am trying to do is send some data on Multicast socket and receive it. For now, I am okay if the sender receives the message (I'll set the option to not receive later).
I have two implementation cases. In the first approach, I am using the traditional way of initializing a sockaddr structure and then binding to, and also joining a multicast group on the same socket. This, however, is IPv4/IPv6 dependent and to circumvent that, I tried to use addrinfo structures in the second variant of the program. Both programs are given below.
The problem is, the messages are being received in the first use case, where I am using the regular sockaddr while, there is no message being received/socket descriptor being set in the second case. Could somebody help me out and explain why is this happening?
Variant 1 (with sockaddr)
#include<stdio.h>
#include <stdlib.h>
#include <sys/socket.h>
#include <stdio.h>
#include <string.h>
#include <arpa/inet.h>
#include <netinet/in.h>
#include <sys/param.h>
#include <unistd.h>
#include <errno.h>
#include <time.h>
#include <fcntl.h> /* for nonblocking */
#include <netinet/tcp.h>
fd_set hm_tprt_conn_set;
main()
{
struct ip_mreq mreq;
struct sockaddr_in mc_addr;
int sock_fd ;
int val;
int reuse = 1;
struct sockaddr_in ip;
struct sockaddr_in src_addr;
int total_bytes_rcvd=0;
unsigned int length;
unsigned char buf[50];
int op_complete = 0;
int os_error;
struct timeval select_timeout;
fd_set read_set;
int32_t nready; //Number of ready descriptors
time_t time_val;
length = sizeof (src_addr);
sock_fd = socket(AF_INET, SOCK_DGRAM,0);
if(sock_fd == -1)
{
printf("\n Error Opening UDP MCAST socket");
perror("\n Cause is ");
exit(0);
}
printf("\n Setting the socket to non-blocking mode");
val = fcntl(sock_fd, F_GETFL , 0);
val = fcntl(sock_fd, F_SETFL, val | O_NONBLOCK);
if (val == -1)
{
printf("\n Error while setting socket to non-blocking mode");
perror("Cause is ");
sock_fd = -1;
exit(0);
} //end if val == -1
if (setsockopt(sock_fd,SOL_SOCKET,SO_REUSEADDR, &reuse, sizeof(reuse)) == -1)
{
fprintf(stderr, "setsockopt: %d\n", errno);
perror("Cause is ");
exit(0);
}
FD_SET(sock_fd, &hm_tprt_conn_set);
printf("\n Construct a mcast address structure");
/* construct a multicast address structure */
memset(&mc_addr, 0, sizeof(mc_addr));
mc_addr.sin_family = AF_INET;
mc_addr.sin_addr.s_addr = htonl(INADDR_ANY);
mc_addr.sin_port = htons(4936);
memset(&ip, 0, sizeof(ip));
ip.sin_family = AF_INET;
ip.sin_addr.s_addr = inet_addr("224.0.0.203")/*htonl(INADDR_ANY)*/;
ip.sin_port = htons(4936);
printf("\n Bind the multicast address structure and port to the recieving socket ");
if (bind( sock_fd, (struct sockaddr*) &mc_addr, sizeof(mc_addr)) == -1)
{
fprintf(stderr, "bind: %d\n", errno);
perror("\n Cause is ");
exit(0);
}
mreq.imr_multiaddr.s_addr = inet_addr("224.0.0.203");
mreq.imr_interface.s_addr = htonl(INADDR_ANY);
if(setsockopt(sock_fd, IPPROTO_IP, IP_ADD_MEMBERSHIP,&mreq, sizeof(mreq)) == -1)
{
fprintf(stderr, "setsockopt: %d\n", errno);
perror("\n Cause is ");
exit(0);
}
printf("\nCreated Recv Socket: %d", sock_fd);
fflush(stdout);
memset(&src_addr, 0, sizeof(mc_addr));
while(1){
/* Send a multicast */
time_val = time(NULL);
snprintf(buf, sizeof(buf), "Hello: %s", ctime(&time_val));
total_bytes_rcvd = sendto(sock_fd,
buf,
sizeof(buf),
0,
(struct sockaddr *)&ip,
length );
printf("\n%d bytes sent.", total_bytes_rcvd);
/* perform select */
select_timeout.tv_sec = 0;
select_timeout.tv_usec = 5000000;
read_set = hm_tprt_conn_set;
nready = select(sock_fd+1, &read_set, NULL, NULL, &select_timeout);
if(nready == 0)
{
/***************************************************************************/
/* No descriptors are ready */
/***************************************************************************/
continue;
}
else if(nready == -1)
{
perror("Error Occurred on select() call.");
continue;
}
if(FD_ISSET(sock_fd, &read_set))
{
printf("\n Recv the data");
total_bytes_rcvd = recvfrom(sock_fd,
buf,
sizeof(buf),
0,
(struct sockaddr *)&src_addr,
&length );
printf("%s: message = \" %s \"\n", inet_ntoa(src_addr.sin_addr), buf);
printf("\n total byte recieved %d", total_bytes_rcvd);
/***************************************************************************/
/* If select returned 1, and it was a listen socket, it makes sense to poll*/
/* again by breaking out and use select again. */
/***************************************************************************/
if(--nready <=0)
{
printf("\nNo more incoming requests.");
continue;
}
}//end select on listenfd
}
}
Variant 2 (with addrinfo)
#include<stdio.h>
#include <stdlib.h>
#include <sys/socket.h>
#include <stdio.h>
#include <string.h>
#include <arpa/inet.h>
#include <netinet/in.h>
#include <sys/param.h>
#include <unistd.h>
#include <errno.h>
#include <time.h>
#include <fcntl.h> /* for nonblocking */
#include <netinet/tcp.h>
#include <netdb.h> /* AI_PASSIVE and other Macros for getaddrinfo() */
fd_set hm_tprt_conn_set;
main()
{
struct addrinfo hints, *res, *ressave;
char target[128] = "127.0.0.1";
char service[128] = "4936";
struct ip_mreq mreq;
int sock_fd ;
int val;
int reuse = 1;
struct sockaddr_in ip;
struct sockaddr_in src_addr;
int total_bytes_rcvd=0;
unsigned int length;
unsigned char buf[50];
int op_complete = 0;
int os_error;
struct timeval select_timeout;
fd_set read_set;
int32_t nready; //Number of ready descriptors
time_t time_val;
length = sizeof (src_addr);
sock_fd = socket(AF_INET, SOCK_DGRAM,0);
if(sock_fd == -1)
{
printf("\n Error Opening UDP MCAST socket");
perror("\n Cause is ");
exit(0);
}
printf("\n Setting the socket to non-blocking mode");
val = fcntl(sock_fd, F_GETFL , 0);
val = fcntl(sock_fd, F_SETFL, val | O_NONBLOCK);
if (val == -1)
{
printf("\n Error while setting socket to non-blocking mode");
perror("Cause is ");
sock_fd = -1;
exit(0);
} //end if val == -1
if (setsockopt(sock_fd,SOL_SOCKET,SO_REUSEADDR, &reuse, sizeof(reuse)) == -1)
{
fprintf(stderr, "setsockopt: %d\n", errno);
perror("Cause is ");
exit(0);
}
FD_SET(sock_fd, &hm_tprt_conn_set);
printf("\n Construct a mcast address structure");
/* construct a multicast address structure */
hints.ai_family = AF_INET;
hints.ai_socktype = SOCK_DGRAM;
hints.ai_protocol = IPPROTO_UDP;
if((os_error = getaddrinfo(target, service, &hints, &res)) !=0)
{
printf("\n%s",gai_strerror(os_error));
exit(0);
}
ressave = res;
if(bind(sock_fd, res->ai_addr, res->ai_addrlen) != 0)
{
perror("Error binding to port");
close(sock_fd);
sock_fd = -1;
}
mreq.imr_multiaddr.s_addr = inet_addr("224.0.0.203");
mreq.imr_interface.s_addr = htonl(INADDR_ANY);
if(setsockopt(sock_fd, IPPROTO_IP, IP_ADD_MEMBERSHIP,&mreq, sizeof(mreq)) == -1)
{
fprintf(stderr, "setsockopt: %d\n", errno);
perror("Cause is ");
exit(0);
}
/* Set Destination address */
memset(&ip, 0, sizeof(ip));
ip.sin_family = AF_INET;
ip.sin_addr.s_addr = inet_addr("224.0.0.203")/*htonl(INADDR_ANY)*/;
ip.sin_port = htons(4936);
/* Set to zero address where addresses of sender will be received */
memset(&src_addr, 0, sizeof(src_addr));
while(1){
/* Send a multicast */
time_val = time(NULL);
snprintf(buf, sizeof(buf), "Hello: %s", ctime(&time_val));
total_bytes_rcvd = sendto(sock_fd,
buf,
sizeof(buf),
0,
(struct sockaddr *)&ip,
length );
printf("\n%d bytes sent.", total_bytes_rcvd);
/* perform select */
select_timeout.tv_sec = 0;
select_timeout.tv_usec = 5000000;
read_set = hm_tprt_conn_set;
nready = select(sock_fd+1, &read_set, NULL, NULL, &select_timeout);
if(nready == 0)
{
/***************************************************************************/
/* No descriptors are ready */
/***************************************************************************/
continue;
}
else if(nready == -1)
{
perror("Error Occurred on select() call.");
continue;
}
if(FD_ISSET(sock_fd, &read_set))
{
printf("\n Recv the data");
total_bytes_rcvd = recvfrom(sock_fd,
buf,
sizeof(buf),
0,
(struct sockaddr *)&src_addr,
&length );
printf("%s: message = \" %s \"\n", inet_ntoa(src_addr.sin_addr), buf);
printf("\n total byte recieved %d", total_bytes_rcvd);
/***************************************************************************/
/* If select returned 1, and it was a listen socket, it makes sense to poll*/
/* again by breaking out and use select again. */
/***************************************************************************/
if(--nready <=0)
{
printf("\nNo more incoming requests.");
continue;
}
}//end select on listenfd
}
}
The difference is that in the first variant you're binding to INADDR_ANY, while in the second variant you're binding to 127.0.0.1. Failing to bind to INADDR_ANY means you won't receive any multicast data.
You can fix this with the following:
hints.ai_family = AF_INET;
hints.ai_socktype = SOCK_DGRAM;
hints.ai_protocol = IPPROTO_UDP;
hints.ai_flags = AI_PASSIVE | AI_NUMERICSERV;
if((os_error = getaddrinfo(NULL, service, &hints, &res)) !=0)
{
printf("\n%s",gai_strerror(os_error));
exit(0);
}
From the man page for getaddrinfo regarding AI_PASSIVE:
If node is NULL, the network address in each socket structure is initialized according to the AI_PASSIVE flag, which is set in
hints.ai_flags. The network address in each socket structure will be
left unspecified if AI_PASSIVE flag is set. This is used by server
applications, which intend to accept client connections on any network
address. The network address will be set to the loopback interface
address if the AI_PASSIVE flag is not set. This is used by client
applications, which intend to connect to a server running on the same
network host.
While in this case you are sending to the same host, multicast data does not go out on the localhost interface by default. You would need to call setsockopt with the IP_MULTICAST_IF option to set the outgoing multicast interface.
With this change, I was able to send and receive with the second variant.
Before you can bind() you need to have a working socket. You will need to cycle through all the results. Here's what's missing on your code.
ressave = res;
sock = socket(ressave->ai_family, ressave->ai_socktype, ressave->ai_protocol);
while(ressave != NULL && (sock < 0 || connect(sock, ressave->ai_addr, ressave->ai_addrlen) < 0)) {
close(sock);
if((ressave = ressave->ai_next) != NULL)
sock = socket(ressave->ai_family, ressave->ai_socktype, ressave->ai_protocol);
}
At this point you have either found a working socket sock or not. When ressave is not NULL then the value of socket sock is valid.

send() socket operation on non-socket

While trying to implement a server and client, I noticed that the client was writing to stdout instead
of through the network. I figured out that the file descriptor being returned by connect() is always zero, which explains why it was writing to stdout. But I can not figure out why connect() always returns zero instead of a valid socket. All the articles on the web I found with the same problem were due to precedence issues with wrapping if() around the connect() call. But I haven't done that, any help would be appreciated.
server code
int setUpServer(struct fuzzerObj *ptr, int *firstClient)
{
/* Declarations */
int hostSocket, yes = 1, rtrn, clientfd;
union
{
struct sockaddr_in in;
}address;
/* Create Socket */
hostSocket = socket(AF_INET, SOCK_STREAM, 0);
if(hostSocket < 0)
{
errorHandler("Could not create socket\n", FUNCTION_ID_SET_UP_SERVER);
return -1;
}
/* Reuse Address */
rtrn = setsockopt(hostSocket, SOL_SOCKET, SO_REUSEADDR, &yes, sizeof(yes));
if(rtrn < 0)
{
errorHandler("Couldn't Reuse Address\n", FUNCTION_ID_SET_UP_SERVER);
return -1;
}
/* Set Up Struct */
address.in.sin_len = sizeof(address.in);
address.in.sin_family = AF_INET;
address.in.sin_port = htons(BBPORT_NUMBER);
address.in.sin_addr.s_addr = htonl(INADDR_ANY);
memset(address.in.sin_zero, 0, sizeof(address.in.sin_zero));
/* Bind Address to Socket */
rtrn = bind(hostSocket, (struct sockaddr*) &address, address.in.sin_len);
if(rtrn < 0)
{
errorHandler("Can't Bind Address to Socket\n", FUNCTION_ID_SET_UP_SERVER);
return -1;
}
/* listen */
rtrn = listen(hostSocket, ptr->numberOfClients);
if(rtrn < 0)
{
errorHandler("Can't Listen\n", FUNCTION_ID_SET_UP_SERVER);
return -1;
}
while(1)
{
rtrn = acceptClient(hostSocket, &clientfd);
if(rtrn < 0)
{
printf("Can't Accept Client\n");
return -1;
}
break;
}
*firstClient = clientfd;
return 0;
}
client code
#include <CoreFoundation/CoreFoundation.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <arpa/inet.h>
#include <netdb.h>
#include <netinet/in.h>
#include <stdbool.h>
#include <sys/types.h>
#include <unistd.h>
#include <errno.h>
#define BLACKBOX_PORT 9696
int main(int argc, char *argv[])
{
/* Check To See If an argument was passed */
if(argc < 2)
{
printf("No enough Arguments\n");
return -1;
}
/* Declaration's */
const char *ip = argv[1];
int sockfd, fd, rtrn;
char *outBuf;
struct sockaddr_in servaddr;
/* Get Socket to Connect to Fuzz Server */
sockfd = socket(AF_INET, SOCK_STREAM, 0);
if(sockfd < 0)
{
perror("Can't Create Socket");
return -1;
}
/* Fill Out Struct */
servaddr.sin_family = AF_INET;
servaddr.sin_port = htons(BLACKBOX_PORT);
inet_pton(AF_INET, ip, &servaddr.sin_addr);
/* Attempt Connection */
fd = connect(sockfd,(struct sockaddr *)&servaddr, sizeof(servaddr));
if(fd < 0)
{
perror("Can not connect to BlackBox Fuzz server");
return -1;
}
/* Allocate Space in Memory for Outgoing Connection */
rtrn = asprintf(&outBuf, "Mac OSX 10.9\n");
if(rtrn < 0)
{
perror("Copy Error");
return -1;
}
/* Send Data to Fuzzer via Socket */
rtrn = send(fd, outBuf, strlen(outBuf), 0);
if(rtrn < 0)
{
perror("Can't write Data to BlackBox Server");
return -1;
}
free(outBuf);
return 0;
}
Upon successfully calling connect(), the sockfd is connected. The 0 return value indicates that the call is successful. If the value was not 0, it would have indicated an error, and that the connection attempt has failed or is not yet completed (if the connect is non-blocking).
After determining that connect() has succeeded, call send() on the sockfd, not on the return value of the connect() call.
rtrn = send(sockfd, outBuf, strlen(outBuf), 0);

select() fails under heavy condition for non_blocking manual timeout connect() calls

I had to implement a application health check mechanism , I used non blocking socket with select although I achieved success
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <fcntl.h>
#include <netdb.h>
#include <netinet/in.h>
#include <errno.h>
#include <sys/socket.h>
#include <sys/types.h>
#include <arpa/inet.h>
int connect_tout(char * hostname1, int port, int timeoutval)
{
char *hostname = hostname1; /* pointer to name of server */
struct sockaddr_in saddr; /* socket address */
int s, i;
fd_set fd_r, fd_w;
struct timeval timeout;
int flags;
timeout.tv_sec = timeoutval;
timeout.tv_usec = 0;
memset(&saddr, 0, sizeof(saddr));
saddr.sin_family = AF_INET;
saddr.sin_port = htons(port);
saddr.sin_addr.s_addr = inet_addr(hostname1);
s = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
/* set the socket fd to non-blocking mode */
fcntl(s, F_SETFL, (flags = fcntl(s, F_GETFL)) | O_NONBLOCK);
connect(s, (struct sockaddr *)&saddr, sizeof(saddr));
FD_ZERO(&fd_r);
FD_ZERO(&fd_w);
FD_SET(s, &fd_r);
FD_SET(s, &fd_w);
/* timeout durring connect() ?? */
select(s+1, &fd_r, &fd_w, NULL, &timeout);
if(FD_ISSET(s, &fd_w))
{
printf("ALIVE\n");
}
else
{
printf("Conect TIMEOUT \n");
close(s);
return errno;
}
i = connect(s, (struct sockaddr *)&saddr, sizeof(saddr));
if(i)
{
printf("Conect failed errno:%d\n",errno);
perror("connect:");
close(s);
return errno;
}
else
{
printf("Connect passed and OK \n");
close(s);
return 1;
}
close(s);
return 1;
}
int main (int argc, char *argv[])
{
int ret;
if (argc < 3)
{
printf("Usage: %s [host] [port] [timout]\n", argv[0]);
exit(1);
}
char *hostname = argv[1]; /* pointer to name of server <IP address>*/
connect_tout(hostname, atoi(argv[2]), atoi(argv[3]));
return 0;
}
BUT my problem comes when my code running machine goes under very high fd usage . NOTE: Opening many fds at a time at my system is common behaviour . then this piece fails each time
if(FD_ISSET(s, &fd_w))
{
printf("ALIVE\n");
}
else
{
close(s);
return errno;
printf("Conect TIMEOUT\n");
}
As I said in such environment it fails by saying TIMEOUT , I want to know why select is failing by not determining ready discripters so soon and that is each time. Is FD_ISSET() may also fall under doubt ?
P S : This runs well when system is under normal number of fds . Sorry for bad program I just pasted here my sample working code. I will check for errors later
For non-blocking connect() usage, you do not call it again after getting writeable notification. Instead, you should check the error status of the socket with getsockopt() with the SO_ERROR option.
You are not checking the return values of any of your calls, and this makes it impossible for your code to actually determine any failures correctly. Note that you do not check the case if the passed in timeout itself is 0, which would cause select() to return immediately with the instantaneous status of the socket. Note that checking for readable notification of the connecting socket is not documented by the socket API.
int s = socket(PF_INET, SOCK_STREAM, 0);
assert(!(s < 0));
int r = fcntl(s, F_SETFL, fcntl(s, F_GETFL, 0)|O_NONBLOCK);
assert(r == 0);
r = connect(s, (struct sockaddr *)&saddr, sizeof(saddr));
if (r < 0) {
if (errno == EINPROGRESS) {
FD_ZERO(&fd_w);
FD_SET(s, &fd_w);
r = select(s+1, NULL, &fd_w, NULL, NULL);
if (r < 0) {
perror("select");
abort();
}
assert(r == 1);
assert(FD_ISSET(s, &fd_w));
int erropt = -1;
socklen_t errlen = sizeof(erropt);
r = getsockopt(s, SOL_SOCKET, SO_ERROR, &erropt, &errlen);
assert(r == 0);
if (erropt != 0) {
errno = erropt;
perror("connect[after select]");
abort();
}
/* connect succeeded asynchronously */
} else {
perror("connect[direct call]");
abort();
}
} else {
/* connect succeeded synchronously */
}

C: epoll port forwarding proxy doesn't forward all data

I have got this code and I'm trying to use it as a part of my project. The details of the project are not important for now, but what I'm trying to do is to use this port forwarding proxy as proxy between the browser and the local http server.
So if I type http: //127.0.0.1:8999/ in my browser I want to get web page from 127.0.0.1:8888 back. This only works for small web pages (no images, small html files,...). When I try to do this on webpage with a few images, they don't get transmitted or are transmitted only partially.
I have also tested with telnet and a web site with a big html file. I have connected to the proxy and send the HEAD method request. I got all the meta-data, as expected. When I tried GET method I got only part of the web page's html file back.
Could anyone point me in the right direction? I'm really not sure what I am doing wrong.
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netdb.h>
#include <unistd.h>
#include <fcntl.h>
#include <sys/epoll.h>
#include <errno.h>
#include <signal.h>
#include <assert.h>
#define FALSE 0
#define TRUE 1
#define EPOLL_QUEUE_LEN 256
#define SERVER_PORT 7000
#define LISTEN_PORT 8667 // Proxy Server Listens for this port
#define FORWARD_PORT 8888 // Proxy Server forwards all port LISTEN_PORT data to this port
#define BUFLEN 1024
//Globals
int fd_server;
int sent = 0; // for how many requests were processed to client
int forwardSockets[EPOLL_QUEUE_LEN];
int internalSockets[EPOLL_QUEUE_LEN];
// Function prototypes
static void SystemFatal (const char* message);
static int forwardData (int fd);
void close_fd (int);
// This is the main function which handles the epoll loop and
// accepts connections
// Also handles the data processing back to the client.
int main (int argc, char* argv[])
{
int i, arg, src_port, forwardSD, dest_port;
int num_fds, fd_new, epoll_fd;
int linenum=0;
static struct epoll_event events[EPOLL_QUEUE_LEN], event;
struct sockaddr_in addr, remote_addr;
socklen_t addr_size = sizeof(struct sockaddr_in);
struct sigaction act;
struct hostent *hp;
struct sockaddr_in server_fwd;
char *host;
char line[256], ip[256];
FILE *fp;
fp=fopen("port_forward_config", "r");
//src_port = LISTEN_PORT; // Use the default listen port
while(fgets(line, 256, fp) != NULL)
{
linenum++;
if(line[0] == '#') continue;
sscanf(line, "%s %d %d", &ip, &src_port, &dest_port);
{
fprintf(stderr, "Syntax error, line %d\n", linenum);
continue;
}
}
printf("Reading Config File...\n");
printf("IP %s LPORT %d DPORT %d\n", ip, src_port, dest_port);
host = ip;
// set up the signal handler to close the server socket when CTRL-c is received
act.sa_handler = close_fd;
act.sa_flags = 0;
if ((sigemptyset (&act.sa_mask) == -1 || sigaction (SIGINT, &act, NULL) == -1))
{
perror ("Failed to set SIGINT handler");
exit (EXIT_FAILURE);
}
//--------------------------------------------------------------------------------------------
// Create the listening socket
fd_server = socket (AF_INET, SOCK_STREAM, 0);
if (fd_server == -1)
{
SystemFatal("socket");
}
// set SO_REUSEADDR so port can be resused imemediately after exit, i.e., after CTRL-c
arg = 1;
if (setsockopt (fd_server, SOL_SOCKET, SO_REUSEADDR, &arg, sizeof(arg)) == -1)
{
SystemFatal("setsockopt");
}
// Make the server listening socket non-blocking
if (fcntl (fd_server, F_SETFL, O_NONBLOCK | fcntl (fd_server, F_GETFL, 0)) == -1)
{
SystemFatal("fcntl");
}
// Bind to the specified listening port
memset (&addr, 0, sizeof (struct sockaddr_in));
addr.sin_family = AF_INET;
addr.sin_addr.s_addr = htonl(INADDR_ANY);
addr.sin_port = htons(src_port);
if (bind (fd_server, (struct sockaddr*) &addr, sizeof(addr)) == -1)
{
SystemFatal("bind");
}
// Listen for fd_news; SOMAXCONN is 128 by default
if (listen (fd_server, SOMAXCONN) == -1)
{
SystemFatal("listen");
}
//---------------------------------------------------------------------------------------------
// Create the epoll file descriptor
epoll_fd = epoll_create(EPOLL_QUEUE_LEN);
if (epoll_fd == -1)
{
SystemFatal("epoll_create");
}
// Add the server socket to the epoll event loop
event.events = EPOLLIN | EPOLLERR | EPOLLHUP | EPOLLET;
event.data.fd = fd_server;
if (epoll_ctl (epoll_fd, EPOLL_CTL_ADD, fd_server, &event) == -1)
{
SystemFatal("epoll_ctl");
}
// Execute the epoll event loop
while (TRUE)
{
//struct epoll_event events[MAX_EVENTS];
num_fds = epoll_wait (epoll_fd, events, EPOLL_QUEUE_LEN, -1);
if (num_fds < 0)
{
SystemFatal ("Error in epoll_wait!");
}
for (i = 0; i < num_fds; i++)
{
// Case 1: Error condition
if (events[i].events & (EPOLLHUP | EPOLLERR))
{
fputs("epoll: EPOLLERR", stderr);
//close(events[i].data.fd);
continue;
}
assert (events[i].events & EPOLLIN);
//-----------------------------------------------------------------------------------------
// Case 2: Server is receiving a connection request
if (events[i].data.fd == fd_server)
{
//socklen_t addr_size = sizeof(remote_addr);
fd_new = accept (fd_server, (struct sockaddr*) &remote_addr, &addr_size);
if (fd_new == -1)
{
if (errno != EAGAIN && errno != EWOULDBLOCK)
{
perror("accept");
}
continue;
}
//------------------------------------------------------------------------------------------------
// Make the fd_new non-blocking
if (fcntl (fd_new, F_SETFL, O_NONBLOCK | fcntl(fd_new, F_GETFL, 0)) == -1)
{
SystemFatal("fcntl");
}
// Add the new socket descriptor to the epoll loop
event.data.fd = fd_new;
if (epoll_ctl (epoll_fd, EPOLL_CTL_ADD, fd_new, &event) == -1)
{
SystemFatal ("epoll_ctl");
}
//+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
printf(" Remote Address: %s\n", inet_ntoa(remote_addr.sin_addr));
//close(fd_new);
dest_port = FORWARD_PORT; // Used the default forward port
// create internal connection
printf("Trying to create forward socket\n");
if ((forwardSD = socket(AF_INET, SOCK_STREAM, 0)) == -1) {
perror("Cannot create forward socket.");
exit(1);
}
printf("Binding...\n");
bzero((char *)&server_fwd, sizeof(struct sockaddr_in));
server_fwd.sin_family = AF_INET;
server_fwd.sin_port = htons(dest_port);
//host = "192.168.0.10";
if ((hp = gethostbyname(host)) == NULL) {
printf("Failed to get host name");
}
bcopy(hp->h_addr, (char *)&server_fwd.sin_addr, hp->h_length);
printf("Connecting to interal machine.\n");
printf("Server Forward Port: %d\n", ntohs(server_fwd.sin_port));
printf("Server Forward IP: %s\n", inet_ntoa(server_fwd.sin_addr));
// Connecting to interal machine
if (connect (forwardSD, (struct sockaddr *)&server_fwd, sizeof(server_fwd)) == -1) {
perror("connect failed");
exit(1);
}
// Add the new socket descriptor to the epoll loop
event.data.fd = forwardSD;
if (epoll_ctl (epoll_fd, EPOLL_CTL_ADD, forwardSD, &event) == -1)
{
SystemFatal ("epoll_ctl");
}
printf ("Connected: Server: %s\n", hp->h_name);
forwardSockets[fd_new] = forwardSD;
internalSockets[forwardSD] = fd_new;
// end internal connection
//+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
continue;
}
// Case 3: One of the sockets has read data
if (!forwardData(events[i].data.fd))
{
// epoll will remove the fd from its set
// automatically when the fd is closed
close (events[i].data.fd);
}
}
}
close(fd_server);
exit (EXIT_SUCCESS);
}
/*
This function clears a socket if it has data waiting to be processed.
It takes the string sent from the client and parses it. After it has
done that it will send back to the client the amount of data it requested
for however many requests it defined as well.
*/
static int forwardData (int fd)
{
int n, bytes_to_read;
char *bp, buf[BUFLEN];
int forwardData;
printf ("Forwarding :\n");
//check if internal or external connection to send data back to.
if(forwardSockets[fd] != 0){
forwardData = forwardSockets[fd];
}
if(internalSockets[fd] != 0){
forwardData = internalSockets[fd];
}
bp = buf;
bytes_to_read = BUFLEN;
while ((n = recv (fd, bp, bytes_to_read, 0)) > 0) {
bp += n;
bytes_to_read -= n;
send (forwardData, buf, n, 0);
return TRUE;
}
return TRUE;
}
// Prints the error stored in errno and aborts the program.
static void SystemFatal(const char* message)
{
perror (message);
exit (EXIT_FAILURE);
}
// close fd
void close_fd (int signo)
{
close(fd_server);
exit (EXIT_SUCCESS);
}
It seems like you're using the EPOLLET (edge triggered) flag in all of your epoll events. This flag will cause epoll to only return events when a file descriptor changes from unavailable to available.
Furthermore, in your forwardData function, you receive BUFLEN bytes and then return TRUE. If there are more bytes available, you will never forward them, as your events are edge triggered and will not be resent.
Try modifying forwardData (perhaps remove the return TRUE in the loop) so that all readable data is forwarded before returning.

Resources