UDP server with poll() - c

I'm coding udp server, which can listen multiple ports. As the method of paralleling I use function poll(). So server get two ports in argc, and then try to listen the range of ports. Then open msg.txt, where saves all the information, which he get from clients. A then waiting for them. But the problem is that with poll(), my server can listen only one port. But if I delete this function, server accept messages from all ports. What can be a problem?Here's my code:
#include <sys/types.h>
#include <sys/socket.h>
#include <sys/time.h>
#include <sys/select.h>
#include <netdb.h>
#include <errno.h>
#include <stdio.h>
#include <string.h>
#include <fcntl.h>
#include <sys/poll.h>
int set_non_block_mode(int s)
{
int fl = fcntl(s, F_GETFL, 0);
return fcntl(s, F_SETFL, fl | O_NONBLOCK);
}
void s_close(int s)
{
close(s);
}
int sock_err(const char *function, int s)
{
int err = errno;
fprintf(stderr, "%s: socket error: %d\n", function, err);
return -1;
}
struct client
{
char id[30];
int msgs[20];
struct cl *next;
char send[81];
int amount;
int answer;
};
struct client *addnew(struct client *head)
{
struct client *tmp = (struct client *)calloc(1, sizeof(struct client));
memset(tmp->msgs, 0, 20);
memset(tmp->send, '\0', 81);
memset(tmp->id, '\0', 30);
tmp->amount = 0;
tmp->answer = 0;
tmp->next = NULL;
return tmp;
}
struct client *search(struct client *head, unsigned int ip, int port)
{
char str[30];
sprintf(str, "%u.%u.%u.%u:%d", (ip >> 24) & 0xFF, (ip >> 16) & 0xFF, (ip >> 8) & 0xFF, (ip)&0xFF, port);
struct client *tmp = head;
if (tmp!=NULL){
while (1)
{
if (strcmp(tmp->id, str) == 0)
{
return tmp;
}
if(tmp->next==NULL)break;
tmp = tmp->next;
}
tmp->next = addnew(head);
tmp = tmp->next;
strcpy(tmp->id, str);
return tmp;
} else {
tmp = addnew(head);
strcpy(tmp->id, str);
return tmp;
}
}
int parcing(FILE *text, char *buffer, int s, struct sockaddr_in *addr, struct client *head, int port)
{
int flags = MSG_NOSIGNAL;
int addrlen = sizeof(*addr);
unsigned int ip = ntohl((*addr).sin_addr.s_addr);
struct client *tmp = search(head, ip, port);
int n;
memcpy(&n, buffer, 4);
n = ntohl(n);
if (tmp->msgs[n] == 0)
{
tmp->msgs[n] = 1;
tmp->answer++;
n = htonl(n);
memcpy(&tmp->send[4 * tmp->amount], tmp->send, 4 * tmp->amount);
tmp->amount++;
memcpy(tmp->send, &n, 4);
fprintf(text, "%s %d:%d:", tmp->id, buffer[4], buffer[5]);
memcpy(&n, &buffer[6], 2);
n = ntohs(n);
fprintf(text, "%hu ", n);
memcpy(&n, &buffer[8], 2);
n = ntohs(n);
fprintf(text, "%hi ", n);
for (int i = 10; i < 22; i++)
fprintf(text, "%c", buffer[i]);
fprintf(text, " %s\n", &buffer[22]);
sendto(s, tmp->send, 4 * tmp->amount, flags, (struct sockaddr *)addr, addrlen);
if (strcmp(&buffer[22], "stop") == 0)
return 0;
return 1;
}
else
{
sendto(s, tmp->send, 4 * tmp->amount, flags, (struct sockaddr *)addr, addrlen);
}
}
int main(int argc, char *argv[])
{
int i;
int flags = MSG_NOSIGNAL;
int start, end;
if (argc == 3)
{
start = atoi(argv[1]);
end = atoi(argv[2]);
}
else
{
start = atoi(argv[1]);
end = start;
}
//start = 8035;
//end = start;
int AmountOfPorts = end - start + 1;
struct pollfd *pfd = (struct pollfd *)calloc(AmountOfPorts, sizeof(struct pollfd));
int *s = (int *)calloc(AmountOfPorts, sizeof(int));
struct sockaddr_in *addr = (struct sockaddr_in *)calloc(AmountOfPorts, sizeof(struct sockaddr_in));
for (int i = 0; i < AmountOfPorts; i++)
{
s[i] = socket(AF_INET, SOCK_DGRAM, 0);
if (s[i] < 0)
return sock_err("socket", s[i]);
set_non_block_mode(s[i]);
memset(&addr[i], 0, sizeof(addr));
addr[i].sin_family = AF_INET;
addr[i].sin_port = htons(start + i);
addr[i].sin_addr.s_addr = htonl(INADDR_ANY);
if (bind(s[i], (struct sockaddr *)&addr[i], sizeof(addr[i])) < 0)
return sock_err("bind", s[i]);
pfd[i].fd = s[i];
pfd[i].events = POLLIN | POLLOUT;
}
int g = 1;
struct client *head = NULL;
struct client *tmp = NULL;
FILE *text = fopen("msg1.txt", "w");
char buffer[131072] = {0};
int addrlen = 0;
do
{
int ev_cnt = poll(pfd, sizeof(pfd) / sizeof(pfd[0]), 100000);
if (ev_cnt > 0)
{
for (i = 0; i < AmountOfPorts; i++)
{
if (pfd[i].revents & POLLHUP)
{
s_close(s[i]);
}
if (pfd[i].revents & POLLERR)
{
s_close(s[i]);
}
if (pfd[i].revents & POLLIN)
{
addrlen = sizeof(addr[i]);
recvfrom(s[i], buffer, sizeof(buffer), 0, (struct sockaddr *)(&addr[i]), &addrlen);
if (rcv > 0)
{
if (!parcing(text, buffer, s[i], &addr[i], head, start + i))
{
g = 0;
break;
}
}
if (pfd[i].revents & POLLOUT)
{
addrlen = sizeof(addr[i]);
unsigned int ip = ntohl((*addr).sin_addr.s_addr);
tmp = search(head, ip, start+i);
sendto(s[i], tmp->send, 4 * tmp->amount, flags, (struct sockaddr *)&addr[i], addrlen);
}
}
}
}
else
{
}
} while (g);
fclose(text);
for (int i = 0; i < AmountOfPorts; i++)
s_close(s[i]);
return 0;
}

You declare pfd as a pointer to a struct pollfd:
struct pollfd *pfd;
Therefore the following construct will not work as intended (for that to work, pfd would have to be an array: struct pollfd pfd[N]):
sizeof(pfd) / sizeof(pfd[0]) //in call to poll
The above expression could be numerically expressed like this (dependend on your machine and os):
//sizeof(struct pollfd*) / sizeof(struct pollfd)
8 / (4 + 2 + 2) => 8 / 8 => 1
//your expectation:
16 / 8 => 2
That means, poll will listen only to the first pollfd, since the parameter nfds is set to 1.
Note: I didn't fully checked your code, but since you're using pointers (calloc) and sizeof a lot, make sure that you did not made the same mistake at other places. Remember pointer != array.

Related

sendto creating a segmentation fault when sending a packet

I'm trying to send a packet through UDP, however I'm getting a seg fault on line 198:
sendto(socketfd, buffer_str, total_len, 0, res->ai_addr, res->ai_addrlen);
And I'm not quite sure what is causing it. I've run the program through GDB, and none of the arguments seem to have anything wrong with them. the file I'm attempting to send is just a simple txt file which has the text "Lorem ipsum dolor sit amet" in it.
The first sendto on line 76 works absolutely fine, as does the recv from on line 78. When I tried replacing the sendto on 198 with the code from like 76, I got the same segfault.
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <netinet/in.h>
#include <netdb.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <sys/time.h>
struct packet {
unsigned int total_frag;
unsigned int frag_no;
unsigned int size;
char* filename;
char filedata[1000];
};
struct node {
struct node* next;
struct packet data;
};
struct list {
struct node* head;
};
int main(int argc, char *argv[]) {
printf("ftp <file name>\n");
struct sockaddr_storage their_addr;
socklen_t addr_size = sizeof(their_addr);
struct addrinfo hints, *res;
char* port = "5050";//argv[2];
struct sockaddr_in *serverAddr;
serverAddr = malloc(sizeof(struct sockaddr_in));
memset(&hints, 0, sizeof(hints));
memset(serverAddr, 0, sizeof(*serverAddr));
serverAddr->sin_family = AF_INET;
hints.ai_family = AF_INET;
hints.ai_socktype = SOCK_DGRAM;
hints.ai_addr = (struct sockaddr *) serverAddr;
getaddrinfo("ug168.eecg.utoronto.ca"/*argv[1]*/, port, &hints, &res);
int socketfd = socket(PF_INET, SOCK_DGRAM, 0);
char fileName[100];
scanf("%s", fileName);
int fd = open(fileName, O_RDWR);
if(fd == -1) {
return 0;
}
struct timeval pre_time, post_time;
gettimeofday(&pre_time, NULL);
sendto(socketfd, (char*)"ftp", 3, 0, res->ai_addr, res->ai_addrlen);
char buf[1000];
int length = recvfrom(socketfd, buf, 1000, 0, (struct sockaddr *)&serverAddr, &addr_size);
gettimeofday(&post_time, NULL);
buf[length] = '\0';
if(strcmp("yes", buf) == 0) {
printf("A file transfer can start\n");
}
printf("seconds : %ld\nmicro seconds : %ld\n", post_time.tv_sec - pre_time.tv_sec, post_time.tv_usec - pre_time.tv_usec);
FILE *file;
file = fopen(fileName, "rb"); //rb = read as binary
fseek(file, 0, SEEK_END);
int fileLength = ftell(file);
fseek(file, 0, SEEK_SET);
char buffer[fileLength];
fread(buffer, fileLength, 1, file);
fclose(file);
int frag_total = fileLength / 1000;
if(fileLength % 1000 != 0) {
frag_total++;
}
struct list packet_list;
packet_list.head = malloc(sizeof(struct node));
struct node* curr = packet_list.head;
for(int i = 0; i < frag_total; i++) {
curr->data.total_frag = frag_total;
curr->data.frag_no = i + 1;
if(i == frag_total - 1) {
curr->data.size = fileLength % 1000;
} else {
curr->data.size = 1000;
}
curr->data.filename = malloc(sizeof(char) * (strlen(fileName) + 1));
strcpy(curr->data.filename, fileName);
if(i == frag_total - 1) {
//copy data equal to remainder
for(int j = 0; j < fileLength % 1000; j++) {
curr->data.filedata[j] = buffer[1000 * i + j];
}
} else {
//copy 1000 bytes
for(int j = 0; j < 1000; j++) {
curr->data.filedata[j] = buffer[1000 * i + j];
}
}
if(i < frag_total - 1) {
curr->next = malloc(sizeof(struct node));
curr = curr->next;
}
}
curr = packet_list.head;
while(curr != NULL) {
int j = curr->data.total_frag;
int frag_total_len = 0;
while(j != 0) {
j /= 10;
frag_total_len++;
}
j = curr->data.frag_no;
int frag_no_len = 0;
while(j != 0) {
j /= 10;
frag_no_len++;
}
j = curr->data.size;
int size_len = 0;
while(j != 0) {
j /= 10;
size_len++;
}
int name_len = strlen(curr->data.filename);
int total_len = frag_total_len + frag_no_len + size_len + name_len + 4 + curr->data.size; //4 bc 4 colons
char buffer_str[total_len];
j = 0;
char strbuf[total_len];
sprintf(strbuf, "%d", curr->data.total_frag);
for(int k = j; k < j + frag_total_len; k++) {
buffer_str[k] = strbuf[k-j];
}
j += frag_total_len;
buffer_str[j] = ':';
j++;
sprintf(strbuf, "%d", curr->data.frag_no);
for(int k = j; k < j + frag_no_len; k++) {
buffer_str[k] = strbuf[k-j];
}
j += frag_no_len;
buffer_str[j] = ':';
j++;
sprintf(strbuf, "%d", curr->data.size);
for(int k = j; k < j + size_len; k++) {
buffer_str[k] = strbuf[k-j];
}
j += size_len;
buffer_str[j] = ':';
j++;
for(int k = 0 ; k < name_len; k++, j++) {
buffer_str[j] = curr->data.filename[k];
}
buffer_str[j] = ':';
j++;
for(int k = 0; k < curr->data.size; k++, j++) {
buffer_str[j] = curr->data.filedata[k];
}
sendto(socketfd, buffer_str, total_len, 0, res->ai_addr, res->ai_addrlen);
curr = curr->next;
}
return 0;
}
I'm not really sure what could be causing this
The local variable res is overwritten by the call
int length = recvfrom(socketfd, buf, 1000, 0, (struct sockaddr *)&serverAddr, &addr_size);
this is because the address of the pointer serverAddr is passed instead of the value of the pointer:
int length = recvfrom(socketfd, buf, 1000, 0, (struct sockaddr *)serverAddr, &addr_size);
You'll probably see that the program now segfaults for another reason: The buffer handling fails for short files with less than two fragments, because curr->next is uninitialized.
The original posted code omitted error checking for getaddrinfo(), which is returning an error code and left res unchanged; that led to an uninitialized pointer being dereferenced when calling sendto(); example revision:
int rv = getaddrinfo("ug168.eecg.utoronto.ca"/*argv[1]*/, port, &hints, &res);
if (rv) {
fprintf(stderr, "getaddrinfo error, rv %d\n", rv);
return (1);
}

Is there a way I can set the TTL (time to live) as an argument when compiling this small ping application

I am building a small application for the ICMP protocol (ping) using Echo request and reply. The Time to live (TTL) is set by default to the receiver's TTL and I was wondering if I can get help in setting a custom TTL as an argument when compiling the code. Thank you in advance for your valuable help.
TO COMPILE:
gcc -o MyPing file_name.c
sudo ./myping www.google.com
Code starts here:
Blockquote
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <netdb.h>
#include <unistd.h>
#include <signal.h>
#include <sys/socket.h>
#include <sys/time.h>
#include <arpa/inet.h>
#include <netinet/ip.h>
#include <netinet/ip_icmp.h>
#define BUFFER_SIZE 1024
#define ICMP_HEADER_LENGTH 8
#define DATA_LENGTH (64 - ICMP_HEADER_LENGTH)
int msg_count;
int msg_received_count;
int seq = 0;
pid_t pid;
int sock;
struct addrinfo *host;
int get_addrinfo_v4(const char *host, struct addrinfo **result) {
struct addrinfo hints;
memset(&hints, 0, sizeof(hints));
hints.ai_family = AF_INET;
hints.ai_flags = AI_CANONNAME;
return getaddrinfo(host, NULL, &hints, result);
}
const char *get_sockaddr_text(
const struct sockaddr *address,
char *text, socklen_t text_length
) {
return inet_ntop(
address->sa_family,
&(((struct sockaddr_in *)address)->sin_addr),
text,
text_length
);
}
double timeval_to_ms(const struct timeval *time) {
return (time->tv_sec * 1000.0) + (time->tv_usec / 1000.0);
}
u_short checksum(u_short *data, int length) {
register int data_left = length;
register u_short *p = data;
register int sum = 0;
u_short answer = 0;
while (data_left > 1) {
sum += *p;
p++;
data_left -= 2;
}
if (data_left == 1) {
*(u_char *)(&answer) = *(u_char *)p;
sum += answer;
}
sum = (sum >> 16) + (sum & 0xffff);
sum += (sum >> 16);
answer = ~sum;
return answer;
}
void alarm_handler(int signal_number) {
int icmp_packet_length = DATA_LENGTH + ICMP_HEADER_LENGTH;
char send_buffer[BUFFER_SIZE];
memset(send_buffer + ICMP_HEADER_LENGTH, 0, DATA_LENGTH);
struct icmp *icmp_packet = (struct icmp *)send_buffer;
icmp_packet->icmp_type = ICMP_ECHO;
icmp_packet->icmp_code = 0;
icmp_packet->icmp_id = pid;
icmp_packet->icmp_seq = seq++;
gettimeofday((struct timeval *)icmp_packet->icmp_data, NULL);
icmp_packet->icmp_cksum = 0;
icmp_packet->icmp_cksum = checksum((u_short *)icmp_packet, icmp_packet_length);
sendto(sock, send_buffer, icmp_packet_length, 0, host->ai_addr, host->ai_addrlen);
alarm(1);
msg_count++;
}
int main(int argc, char **argv) {
if (argc != 2) {
printf("usage: %s host\n", argv[0]);
exit(EXIT_FAILURE);
}
pid = getpid() & 0xffff;
int status = get_addrinfo_v4(argv[1], &host);
if (status != 0) {
printf("error: %s\n", gai_strerror(status));
exit(EXIT_FAILURE);
}
sock = socket(host->ai_family, SOCK_RAW, IPPROTO_ICMP);
if (sock < 0) {
perror("socket");
exit(EXIT_FAILURE);
}
setuid(getuid());
struct sigaction action;
action.sa_handler = alarm_handler;
if (sigaction(SIGALRM, &action, NULL) < 0) {
perror("signal");
exit(EXIT_FAILURE);
}
char send_ip[INET_ADDRSTRLEN];
get_sockaddr_text(host->ai_addr, send_ip, sizeof(send_ip));
printf(
"PING %s (%s): %d data bytes\n",
host->ai_canonname,
send_ip,
DATA_LENGTH
);
alarm_handler(SIGALRM);
char receive_buffer[BUFFER_SIZE];
struct sockaddr receive_address;
char control_buffer[BUFFER_SIZE];
struct iovec iov;
iov.iov_base = receive_buffer;
iov.iov_len = sizeof(receive_buffer);
struct msghdr msg;
msg.msg_name = &receive_address;
msg.msg_namelen = sizeof(receive_address);
msg.msg_iov = &iov;
msg.msg_iovlen = 1;
msg.msg_control = control_buffer;
msg.msg_controllen = sizeof(control_buffer);
struct timeval receive_time;
char receive_ip[INET_ADDRSTRLEN];
for ( ; ; ) {
ssize_t n = recvmsg(sock, &msg, 0);
if (n > 0) {
struct ip *ip_packet = (struct ip *)receive_buffer;
if (ip_packet->ip_p == IPPROTO_ICMP) {
int ip_header_length = ip_packet->ip_hl << 2;
int icmp_packet_length = n - ip_header_length;
if (icmp_packet_length >= 16) {
struct icmp *icmp_packet = (struct icmp *)(receive_buffer + ip_header_length);
if (
icmp_packet->icmp_type == ICMP_ECHOREPLY &&
icmp_packet->icmp_id == pid
) {
gettimeofday(&receive_time, NULL);
struct timeval *send_time = (struct timeval *)icmp_packet->icmp_data;
msg_received_count++;
printf(
"%d bytes from %s: icmp_seq=%u ttl=%d time=%.3f ms\n",
icmp_packet_length,
get_sockaddr_text(&receive_address, receive_ip, sizeof(receive_ip)),
icmp_packet->icmp_seq,
ip_packet->ip_ttl,
timeval_to_ms(&receive_time) - timeval_to_ms(send_time) );
}
printf("\n%d packets sent, %d packets received, %f percent packet loss. \n\n",
msg_count, msg_received_count, ((msg_count - msg_received_count)/msg_count) * 100.0);
}
}
}
}
return EXIT_SUCCESS;
}
Tl;dr
setsockopt() can be used to set a different TTL value on your ICMP socket.
For example:
/* sets TTL to 100 */
setsockopt(sock, IPPROTO_ICMP, IP_TTL, &(int){100}, sizeof(int));

Maximum segment size after sending ICMP packets

I am developing a script for sending and receiving data through the ICMP Protocol. I cut, encrypt, code in base64, and forge packets. My code works fine but if I try to send large file, ICMP packets are not sent anymore (around the 1021th packets, it seems that I have reached the maximum segment size 65495 bytes.
Here is the screenshot of a wireshark capture when the event appears:
If it may help here is my C implementation for forging and sending packets (I read from files the data to send):
#include <stdio.h>
#include <stdlib.h>
#include <fcntl.h>
#include <errno.h>
#include <sys/socket.h>
#include <resolv.h>
#include <netdb.h>
#include <string.h>
#include <netinet/in.h>
#include <netinet/ip_icmp.h>
#include <arpa/inet.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <unistd.h>
#include <dirent.h>
#include <errno.h>
#include <limits.h>
#include <time.h>
#define PACKET_SIZE 128
struct packet
{
struct icmphdr hdr;
char msg[PACKET_SIZE-sizeof(struct icmphdr)];
};
int pid=-1;
struct protoent *proto=NULL;
/*--------------------------------------------------------------------*/
/*--- checksum - standard 1s complement checksum ---*/
/*--------------------------------------------------------------------*/
unsigned short checksum(void *b, int len)
{ unsigned short *buf = b;
unsigned int sum=0;
unsigned short result;
for ( sum = 0; len > 1; len -= 2 )
sum += *buf++;
if ( len == 1 )
sum += *(unsigned char*)buf;
sum = (sum >> 16) + (sum & 0xFFFF);
sum += (sum >> 16);
result = ~sum;
return result;
}
/*--------------------------------------------------------------------*/
/*--- listener - separate process to listen for and collect messages--*/
/*--------------------------------------------------------------------*/
void listener(void)
{ int sd;
struct sockaddr_in addr;
unsigned char buf[1024];
sd = socket(AF_INET, SOCK_RAW, proto->p_proto);
if ( sd < 0 )
{
perror("socket");
exit(0);
}
for (;;)
{ int bytes, len=sizeof(addr);
bzero(buf, sizeof(buf));
bytes = recvfrom(sd, buf, sizeof(buf), 0, (struct sockaddr*)&addr, &len);
if ( bytes > 0 )
printf("***Got Reply***\n");
else
perror("recvfrom");
}
exit(0);
}
/*--------------------------------------------------------------------*/
/*--- ping - Create message and send it. ---*/
/*--------------------------------------------------------------------*/
int my_ping(struct sockaddr_in *addr, char *pkt, int cnt)
{
const int val=255;
int i, u, sd;
struct packet pckt;
struct sockaddr_in r_addr;
sd = socket(AF_INET, SOCK_RAW, proto->p_proto);
if ( sd < 0 )
return 1;
if ( setsockopt(sd, SOL_IP, IP_TTL, &val, sizeof(val)) != 0)
return 2;
if ( fcntl(sd, F_SETFL, O_NONBLOCK) != 0 )
return 3;
int len=sizeof(r_addr);
bzero(&pckt, sizeof(pckt));
pckt.hdr.type = ICMP_ECHO;
pckt.hdr.un.echo.id = pid;
for(i = 0; pkt[i]; i++){
pckt.msg[i] = pkt[i];
}
u = i;
for ( i = u; i < sizeof(pckt.msg); i++ )
pckt.msg[i] = '0';
//pckt.msg[i] = 0;
pckt.hdr.un.echo.sequence = cnt++;
pckt.hdr.checksum = checksum(&pckt, sizeof(pckt));
if ( sendto(sd, &pckt, sizeof(pckt), 0, (struct sockaddr*)addr, sizeof(*addr)) <= 0 )
return 4;
return 0;
}
/*--------------------------------------------------------------------*/
/*--- ping - Create message and send it. ---*/
/*--------------------------------------------------------------------*/
int sending(struct sockaddr_in *addr, char* folderPath)
{
int status, packetSent = 0, nbTries = 0;
FILE *entry_file;
char buffer[BUFSIZ];
struct dirent **namelist;
char pathFile[PATH_MAX + 1];
int i,n;
char pkt[2048];
char *pktTemp;
int nbPkts = 1;
/* Scanning the in directory */
n = scandir(folderPath, &namelist, 0, alphasort);
printf("n == %d \n", n);
if (n < 0){
fprintf(stderr, "Scan directory can not be performed\n");
return(3);
}else {
for (i = 0; i < n; i++) {
/* On linux/Unix we don't want current and parent directories
* * On windows machine too, thanks Greg Hewgill
* */
if (!strcmp (namelist[i]->d_name, ".") || !strcmp (namelist[i]->d_name, ".."))
continue;
pathFile[0] = '\0';
strncat(pathFile, folderPath,PATH_MAX);
strncat(pathFile, "/",PATH_MAX);
strncat(pathFile, namelist[i]->d_name, PATH_MAX);
printf("\n----------------\n");
printf("File path read: %s \n", pathFile);
entry_file = fopen(pathFile, "r");
if (entry_file == NULL)
{
fprintf(stderr, "Failed to open entry file \n");
return(3);
}
pkt[0] = '\0';
packetSent = 0;
while((pktTemp = fgets(buffer, BUFSIZ, entry_file)) != NULL){
strncat(pkt,pktTemp, 2048);
}
printf("Sending Packet %d with message : \"%s\" \n", nbPkts, pkt);
printf("----------------\n");
while(packetSent == 0 && nbTries < 3){
if((status = my_ping(addr, pkt, nbPkts)) == 0){
packetSent = 1;
nbPkts++;
}else {
nbTries++;
}
usleep(50000);
}
if(packetSent == 0){
fprintf(stderr, "Connection has been lost, check if the host is alived\n");
}
/* When you finish with the file, close it */
fclose(entry_file);
free(namelist[i]);
}
free(namelist);
}
return 0;
}
/*--------------------------------------------------------------------*/
/*--- main - look up host and start ping processes. ---*/
/*--------------------------------------------------------------------*/
int main(int argc, char *argv[])
{
struct hostent *hname;
struct sockaddr_in addr;
if ( argc != 3 )
{
printf("usage: %s <addr>\n", argv[0]);
exit(0);
}
if ( argc > 2 )
{
pid = getpid();
proto = getprotobyname("ICMP");
hname = gethostbyname(argv[1]);
bzero(&addr, sizeof(addr));
addr.sin_family = hname->h_addrtype;
addr.sin_port = 0;
addr.sin_addr.s_addr = *(long*)hname->h_addr;
if ( fork() == 0 )
listener();
else
sending(&addr, argv[2]);
wait(0);
}
else
printf("Usage: ./bin <hostname> <folderPath>\n");
return 0;
}

Sockets behavior differently between BSD (Mac OS X and OpenBSD) and Linux (Ubuntu)

I wrote a man-in-the-middle/proxy server initially on my mac. Essentially the proxy creates a socket and waits for a connection, then connects to another application. This works flawlessly on in OS X and in OpenBSD; however, when porting the proxy to Ubuntu, it doesn't work as intended.
There is two instances of this proxy running, listening on two separate ports. When this is ran on Ubuntu, all the traffic goes through a single port. I also run into a problem when setting the socket to nonblocking (through fcntl) that sometimes it fails with "Invalid Argument"
I am using sys/socket.
Any pitfalls during this port that I am missing?
EDIT:
I believe there are two problems. One being the Invalid Argument, the other being that traffic is being pushed to different ports.
Service 1 binds to Proxy instance 1 which then binds back to the appropriate service on the black box, which kicks off service 2. However, for some reason on Ubuntu it connects to the Instance 1 which is listening on the incorrect port.
EDIT Solution to Invalid Argument for fcntl:
Found out why i was getting the invalid argument, sadly i'm still having the other issue.
fcntl(fd, cmd, arg)
cmd - F_SETFL(long)
I was passing in a pointer to an int instead of the long primitive.
EDIT:
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <errno.h>
#include <netdb.h>
#include <string.h>
#include <signal.h>
#include <assert.h>
#include <syslog.h>
#include <sys/types.h>
#include <sys/select.h>
#include <sys/file.h>
#include <sys/ioctl.h>
#include <sys/param.h>
#include <sys/socket.h>
#include <sys/stat.h>
#include <sys/time.h>
#include <sys/wait.h>
#include <netinet/in.h>
#include <arpa/ftp.h>
#include <arpa/inet.h>
#include <arpa/telnet.h>
void cleanup(int sig)
{
syslog(LOG_INFO, "Cleaning up...");
exit(0);
}
void sigreap(int sig)
{
int status;
pid_t p;
while ((p = waitpid(-1, &status, WNOHANG)) > 0) {
syslog(LOG_INFO, "sigreap: pid=%d, status=%d\n", (int) p, status);
}
/* doh! */
signal(SIGCHLD, sigreap);
}
void set_nonblock(int fd)
{
long fl;
int x;
fl = fcntl(fd, F_GETFL);
if (fl < 0) {
syslog(LOG_ERR, "fcntl F_GETFL: FD %d: %s", fd, strerror(errno));
exit(1);
}
fl |= O_NONBLOCK;
x = fcntl(fd, F_SETFL, fl);
if (x < 0) {
syslog(LOG_ERR, "fcntl F_SETFL: FD %d: %s", fd, strerror(errno));
exit(1);
}
}
int create_server_sock(char *addr, int port)
{
int addrlen, s, on = 1, x;
static struct sockaddr_in client_addr;
s = socket(AF_INET, SOCK_STREAM, 0);
if (s < 0)
perror("socket"), exit(1);
addrlen = sizeof(client_addr);
memset(&client_addr, '\0', addrlen);
client_addr.sin_family = AF_INET;
client_addr.sin_addr.s_addr = INADDR_ANY; //inet_addr(addr);
client_addr.sin_port = htons(port);
setsockopt(s, SOL_SOCKET, SO_REUSEADDR, &on, 4);
x = bind(s, (struct sockaddr *) &client_addr, addrlen);
if (x < 0)
perror("bind"), exit(1);
x = listen(s, 5);
if (x < 0)
perror("listen"), exit(1);
return s;
}
int open_remote_host(char *host, int port)
{
struct sockaddr_in rem_addr;
int len, s, x;
struct hostent *H;
int on = 1;
H = gethostbyname(host);
if (!H)
return (-2);
len = sizeof(rem_addr);
s = socket(AF_INET, SOCK_STREAM, 0);
if (s < 0)
return s;
setsockopt(s, SOL_SOCKET, SO_REUSEADDR, &on, 4);
len = sizeof(rem_addr);
memset(&rem_addr, '\0', len);
rem_addr.sin_family = AF_INET;
memcpy(&rem_addr.sin_addr, H->h_addr, H->h_length);
rem_addr.sin_port = htons(port);
x = connect(s, (struct sockaddr *) &rem_addr, len);
if (x < 0) {
close(s);
return x;
}
set_nonblock(s);
return s;
}
int get_hinfo_from_sockaddr(struct sockaddr_in addr, int len, char *fqdn)
{
struct hostent *hostinfo;
hostinfo = gethostbyaddr((char *) &addr.sin_addr.s_addr, len, AF_INET);
if (!hostinfo) {
sprintf(fqdn, "%s", inet_ntoa(addr.sin_addr));
return 0;
}
if (hostinfo && fqdn)
sprintf(fqdn, "%s [%s]", hostinfo->h_name, inet_ntoa(addr.sin_addr));
return 0;
}
int wait_for_connection(int s)
{
int newsock;
socklen_t len;
static struct sockaddr_in peer;
len = sizeof(struct sockaddr);
newsock = accept(s, (struct sockaddr *) &peer, &len);
/* dump_sockaddr (peer, len); */
if (newsock < 0) {
if (errno != EINTR)
perror("accept");
}
get_hinfo_from_sockaddr(peer, len, client_hostname);
set_nonblock(newsock);
return (newsock);
}
static int print_bytes(char * buf, ssize_t length)
{
int i = 0, ascii_off = 0, hex_off = 0;
char * hex_bytes = (char *) calloc(32*2,1);
char * ascii_bytes = (char *) calloc(32*2,1);
for( i = 0; i < length; i++)
{
hex_off += sprintf(hex_bytes+hex_off,"%02X ",(unsigned char)buf[i]);
if(buf[i] >= '!' && buf[i] <= '~')
ascii_off += sprintf(ascii_bytes+ascii_off,"%c ",buf[i]);
else
ascii_off += sprintf(ascii_bytes+ascii_off,". ");
if( ((i+1) % 16 == 0) || i == length-1 )
{
fprintf(stderr,"%-48s\t%s\n",hex_bytes,ascii_bytes);
free(hex_bytes);
free(ascii_bytes);
hex_bytes = (char *) calloc(32*2,1);
ascii_bytes = (char *) calloc(32*2,1);
ascii_off = 0;
hex_off = 0;
if(i != length-1)
fprintf(stderr,"\t");
}
}
free(hex_bytes);
free(ascii_bytes);
return 0;
}
int mywrite(int fd, char *buf, int *len)
{
int x = write(fd, buf, *len);
print_bytes(buf,*len);
if (x < 0)
return x;
if (x == 0)
return x;
if (x != *len)
memmove(buf, buf+x, (*len)-x);
*len -= x;
return x;
}
void service_client(int fd1, int fd2, int injfd)
{
int maxfd;
cfd = fd1;
sfd = fd2;
char *sbuf;
char *cbuf;
int x, n;
int cbo = 0;
int sbo = 0;
int ibo = 0;
fd_set R;
int max_clients = 30;
int i = 0,s = 0, addrlen;
struct sockaddr_in address;
sbuf = malloc(BUF_SIZE);
cbuf = malloc(BUF_SIZE);
cntrlbuf = calloc(1,BUF_SIZE);
char * injbuf = malloc(BUF_SIZE);
maxfd = cfd > sfd ? cfd : sfd;
maxfd = injfd > maxfd ? injfd : maxfd;
maxfd++;
maxfd++;
struct inj_con * ptr;
while (1) {
struct timeval to;
if (cbo) {
process_packet(cbuf,&cbo,sfd);
}
if (sbo) {
process_packet(sbuf,&sbo,cfd);
}
if (ibo) {
process_packet(injbuf,&ibo,cfd);
}
if (cntrlo) {
fprintf(stderr,"\033[33;1mControl->(%d), len = 0x%x (%d):\033[0m\n\t",cfd,cntrlo,cntrlo);
if (mywrite(cfd, cntrlbuf, &cntrlo) < 0 && errno != EWOULDBLOCK) {
syslog(LOG_ERR, "write %d: %s", cfd, strerror(errno));
exit(1);
}
}
FD_ZERO(&R);
if (cbo < BUF_SIZE)
FD_SET(cfd, &R);
if (sbo < BUF_SIZE)
FD_SET(sfd, &R);
if (ibo < BUF_SIZE)
FD_SET(injfd, &R);
to.tv_sec = 0;
to.tv_usec = 1000;
x = select(max_clients+3, &R, 0, 0, &to);
if (x > 0 || cntrl_q->item_count > 0) {
if (FD_ISSET(injfd, &R)) {
int new_socket;
if((new_socket = accept(injfd, (struct sockaddr *) &address, (socklen_t *) &addrlen)) < 0)
{
perror("accept");
exit(1);
}
// Truncated
//
}
char * temp_pkt;
if (FD_ISSET(cfd, &R)) {
temp_pkt = (char *) calloc(BUF_SIZE,1);
n = read(cfd, temp_pkt, BUF_SIZE);
syslog(LOG_INFO, "read %d bytes from CLIENT (%d)", n, cfd);
if (n > 0) {
push_msg(s_q,temp_pkt,n);
} else {
free(temp_pkt);
close(cfd);
close(sfd);
close_injection_sockets();
close(injfd);
_exit(0);
}
}
if (FD_ISSET(sfd, &R)) {
temp_pkt = (char *) calloc(BUF_SIZE,1);
n = read(sfd, temp_pkt, BUF_SIZE);
syslog(LOG_INFO, "read %d bytes from SERVER (%d)\n", n, sfd);
if (n > 0) {
push_msg(c_q,temp_pkt,n);
} else {
free(temp_pkt);
close(sfd);
close(cfd);
close_injection_sockets();
close(injfd);
_exit(0);
}
}
if(cntrlo == 0 && cntrl_q->front != NULL)
{
struct msg * tmp = next_msg(cntrl_q);
if(tmp != NULL)
{
memcpy(cntrlbuf,tmp->msg,tmp->len);
cntrlo += tmp->len;
free(tmp->msg);
free(tmp);
}
}
if(sbo == 0 && c_q->front != NULL)
{
struct msg * tmp = next_msg(c_q);
if(tmp != NULL)
{
memcpy(sbuf,tmp->msg,tmp->len);
sbo += tmp->len;
free(tmp->msg);
free(tmp);
}
}
if(cbo == 0 && s_q->front != NULL)
{
struct msg * tmp = next_msg(s_q);
if(tmp != NULL)
{
memcpy(cbuf,tmp->msg,tmp->len);
cbo += tmp->len;
free(tmp->msg);
free(tmp);
}
}
if(ibo == 0 && inj_q->front != NULL)
{
struct msg * tmp = next_msg(inj_q);
if(tmp != NULL)
{
memcpy(injbuf,tmp->msg,tmp->len);
ibo += tmp->len;
free(tmp->msg);
free(tmp);
}
}
} else if (x < 0 && errno != EINTR) {
close(sfd);
close(cfd);
_exit(0);
}
}
}
static int create_injection_sock(int injectionport)
{
struct sockaddr_in serv_addr;
int portno = injectionport;
int sockfd = socket(AF_INET, SOCK_STREAM, 0);
if (sockfd <0)
{
perror("ERROR: opening socket");
exit(1);
}
bzero((char *) &serv_addr, sizeof(serv_addr));
serv_addr.sin_family = AF_INET;
serv_addr.sin_addr.s_addr = INADDR_ANY;
serv_addr.sin_port = htons(portno);
if (bind(sockfd, (struct sockaddr *) &serv_addr, sizeof(serv_addr)) < 0)
{
perror("ERROR: on bind");
exit(1);
}
if (listen(sockfd,5) < 0 )
{
perror("listen injection");
exit(1);
}
return sockfd;
}
int main(int argc, char *argv[])
{
if (!(5 == argc || 6 == argc)) {
fprintf(stderr, "usage: %s laddr lport rhost rport [injectionport]\n", argv[0]);
exit(1);
}
char *localaddr = strdup(argv[1]);
int localport = atoi(argv[2]);
char *remoteaddr = strdup(argv[3]);
int remoteport = atoi(argv[4]);
int injectionport;
if(argc == 6)
injectionport = atoi(argv[5]);
int client, server;
int master_sock;
int injection_sock = -1;
cntrl_q = (struct item_queue *) calloc(1,sizeof(struct item_queue));
inj_q = (struct item_queue *) calloc(1,sizeof(struct item_queue));
s_q = (struct item_queue *) calloc(1,sizeof(struct item_queue));
c_q = (struct item_queue *) calloc(1,sizeof(struct item_queue));
identities = (struct item_queue *) calloc(1,sizeof(struct item_queue));
assert(localaddr);
assert(localport > 0);
assert(remoteaddr);
assert(remoteport > 0);
if(argc == 6)
assert(injectionport > 0);
openlog(argv[0], LOG_PID, LOG_LOCAL4);
signal(SIGINT, cleanup);
signal(SIGCHLD, sigreap);
if(argc == 6)
injection_sock = create_injection_sock(injectionport);
master_sock = create_server_sock(localaddr, localport);
for (;;) {
if ((client = wait_for_connection(master_sock)) < 0)
continue;
if ((server = open_remote_host(remoteaddr, remoteport)) < 0)
continue;
if (!fork()) {
service_client(client, server, injection_sock);
}
close(client);
close(server);
}
}
Just a guess, but I think your problem is the way you're handling the injection socket. I don't know what you deleted in the // TRUNCATED code block, but basically what you're doing is this:
// Parent process -- bind & listen on the injection socket
injfd = socket(...);
bind(injfd, ...);
listen(injfd, ...);
...
while (1)
{
client = wait_for_connection();
...
if (!fork())
{
// Each child process
...
if (stuff && FD_ISSET(injfd, &R)) {
new_socket = accept(injfd);
...
}
...
}
...
}
In other words, all of your child processes are listening on the same injection socket and trying to accept connections. I'm not sure if this even well-defined behavior, but even in the best case, when a new connection arrives on the injection port, the process that is going to accept the connection could be random and uncontrollable. It could be any of the child processes or even the parent process.
In order to control that behavior, you need to decide who should be listening for connections on the injection socket. If only the parent should be listening, then only the parent should call accept() on it or pass it as an argument to select(). Likewise, if only a particular child should be listening, then only that child should call accept() or pass it to select(). All other processes that are ignoring that socket should close() it at their earliest convenience (e.g. immediately after fork() returns) to avoid leaking file descriptors.
Turns out that SO_REUSEADDR socket option was the issue. I removed me setting that socket option and everything worked out.
This Lead me to the solution.

C - Simple ipv6 udp server using select to listen on multiple ports. Receiving message from one port, not the other

Here is my code.
#include <sys/socket.h>
#include <netinet/in.h>
#include <sys/select.h>
#include <stdio.h>
#include <string.h>
int max(int socket_handle[]);
int main( void )
{
int max_clients_allowed = 2;
int socket_handle[max_clients_allowed];
int client_handle[max_clients_allowed];
struct sockaddr_in6 server_address[max_clients_allowed];
struct sockaddr_in6 client_address[max_clients_allowed];
char buffer[1000];
socklen_t client_length;
fd_set read_handles;
struct timeval timeout_interval;
int bytes_received;
int port_number = 9000;
int retval;
int i;
printf("Hello, human.\n");
for (i = 0; i < max_clients_allowed; i++)
{
printf("Creating socket%d on port: %d\n", i, port_number + i);
socket_handle[i] = socket(PF_INET6,SOCK_DGRAM,0);
memset( &server_address[i], 0, sizeof( server_address[i] ) );
server_address[i].sin6_family = AF_INET6;
server_address[i].sin6_addr=in6addr_any;
server_address[i].sin6_port=htons( port_number + i );
if(bind( socket_handle[i], (struct sockaddr *)&server_address[i], sizeof( server_address[i] )) < 0)
{
perror("Unable to bind.");
return -1;
}
else
{
printf("Bind %d successful.\n", i);
}
}
while (1) {
FD_ZERO(&read_handles);
FD_SET(socket_handle[0], &read_handles);
FD_SET(socket_handle[1], &read_handles);
timeout_interval.tv_sec = 2;
timeout_interval.tv_usec = 500000;
retval = select(max(socket_handle) + 1, &read_handles, NULL, NULL, &timeout_interval);
if (retval == -1)
{
printf("Select error\n");
//error
}
else if ((retval = 0))
{
printf("timeout\n");
}
else
{
//good
client_length = sizeof(struct sockaddr*);
for (i = 0; i < max_clients_allowed; i++)
{
if (FD_ISSET(socket_handle[i], &read_handles))
{
if((bytes_received = recvfrom(socket_handle[i],buffer,sizeof(buffer),0,(struct sockaddr *)&client_address[i], (socklen_t*)&client_length)) < 0)
{
perror("Error in recvfrom.");
break;
}
printf("\nData received:");
printf("\n--------------\n");
printf("%s", buffer);
}
}
}
}
}
int max(int socket_handle[])
{
if (socket_handle[0] > socket_handle[1])
{
return socket_handle[0];
}
return socket_handle[1];
}
This code is supposed to bind to port 9000 and 9001. It them uses select to see which sockets has incoming data, and then prints the message.
I'm assuming it has something to do with my recvfrom function. I've tried playing around with the parameters with no avail.
Another problem might be when I am setting up the sockets, I am using sin6.addr = in6addr_any. I'm pretty sure PF_INET6 and AF_INET6 are right, but that could be an issue too. I've been playing with this for a bit but I can't find the bug. I would be really grateful if someone could point out my mistake so I could fix it. I know I'm on the cusp of getting this finished.
This is a homework problem, the teacher gave us the test program and all it does is simply send a message on port 9000.
When calling recvfrom(), you are setting the client_length to the wrong value on input. It needs to be set to sizeof(client_address[I]) instead. You also need to reset the client_length each time you call recvfrom().
When you print the received buffer, you need to take bytes_received into account since the buffer will not be null-terminated (unless the client is sending null-terminated data).
Try this instead:
#include <sys/socket.h>
#include <netinet/in.h>
#include <sys/select.h>
#include <stdio.h>
#include <string.h>
int main( void )
{
int max_servers = 2;
int server_handle[max_servers];
int max_server_handle = 0;
struct sockaddr_in6 server_address[max_servers];
struct sockaddr_in6 client_address[max_servers];
char buffer[1000];
socklen_t client_length;
fd_set read_handles;
struct timeval timeout_interval;
int bytes_received;
int port_number = 9000;
int retval;
int i;
printf("Hello, human.\n");
for (i = 0; i < max_servers; i++)
{
printf("Creating socket %d on port: %d\n", i, port_number + i);
server_handle[i] = socket(PF_INET6, SOCK_DGRAM, 0);
if (server_handle[i] < 0)
{
perror("Unable to create socket.");
return -1;
}
if (server_handle[i] > max_server_handle)
max_server_handle = server_handle[i];
memset( &server_address[i], 0, sizeof( server_address[i] ) );
server_address[i].sin6_family = AF_INET6;
server_address[i].sin6_addr = in6addr_any;
server_address[i].sin6_port = htons( port_number + i );
if (bind( server_handle[i], (struct sockaddr *)&server_address[i], sizeof( server_address[i] )) < 0)
{
perror("Unable to bind.");
return -1;
}
printf("Bind %d successful.\n", i);
}
while (1)
{
FD_ZERO(&read_handles);
for (i = 0; i < max_servers; i++)
FD_SET(server_handle[i], &read_handles);
timeout_interval.tv_sec = 2;
timeout_interval.tv_usec = 500000;
retval = select(max_server_handle + 1, &read_handles, NULL, NULL, &timeout_interval);
if (retval == -1)
{
printf("Select error\n");
//error
}
else if (retval == 0)
{
printf("timeout\n");
}
else
{
//good
for (i = 0; i < max_servers; i++)
{
if (FD_ISSET(server_handle[i], &read_handles))
{
client_length = sizeof(client_address[i]);
if ((bytes_received = recvfrom(server_handle[i], buffer, sizeof(buffer), 0, (struct sockaddr *)&client_address[i], &client_length)) < 0)
{
perror("Error in recvfrom.");
break;
}
printf("\nData received on socket %d:", i);
printf("\n--------------\n");
printf("%.*s", bytes_received, buffer);
}
}
}
}
}

Resources