I am currently studying TCP multiclient echo server and client using fork, thread, multiplexing IO and so on.
Below are the simple server and client using fork().
server_fork.c
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/socket.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <signal.h>
#include <arpa/inet.h>
static const int BUFSIZE = 1024;
int readn(int fd, char *buf, short n);
int main(void)
{
int cnt = 0;
int listenFD, connectFD;
struct sockaddr_in listenSocket, connectSocket;
char buffer [BUFSIZE];
if ((listenFD = socket(PF_INET, SOCK_STREAM, 0)) < 0) {
perror("socket() error\n");
exit(0);
}
if (setsockopt(listenFD, SOL_SOCKET, SO_REUSEADDR, &(int){ 1 }, sizeof(int)) < 0) {
perror("sockopt error\n");
exit(0);
}
memset(&listenSocket, 0, sizeof(listenSocket));
listenSocket.sin_family = AF_INET;
listenSocket.sin_addr.s_addr = inet_addr("0.0.0.0");
listenSocket.sin_port = htons(7777);
if (bind(listenFD, (struct sockaddr *)&listenSocket, sizeof(listenSocket)) < 0) {
perror("bind() error\n");
exit(0);
}
if (listen(listenFD, 1) < 0) {
perror("listen() error\n");
exit(0);
}
signal(SIGCHLD, SIG_IGN);
int connectSocketLen;
short readLen;
pid_t pid;
while (1) {
connectSocketLen = sizeof(connectSocket);
if ((connectFD = accept(listenFD, (struct sockaddr *)&connectSocket,
&connectSocketLen)) < 0) {
perror("accept() error\n");
exit(0);
}
pid = fork();
cnt++;
if (pid == 0) {
close(listenFD);
while (1) {
memset(buffer, 0, BUFSIZE);
if (readn(connectFD, buffer, 2) == 0) {
break;
}
readLen = (*(short *)&buffer);
if(readLen != 12)
printf("[%d] : %d\n", cnt, readLen);
if (readn(connectFD, buffer, readLen) == 0) {
break;
}
buffer[readLen] = 0;
int n;
if ((n = write(connectFD, buffer, readLen)) <= 0) {
perror("!!");
}
sleep(0);
}
close(connectFD);
exit(0);
}
else if (pid > 0) {
close(connectFD);
}
else {
perror("fork() error\n");
exit(0);
}
}
close(listenFD);
return 0;
}
int readn(int fd, char *buf, short n)
{
short sp = 0, readed;
while (n) {
readed = read(fd, buf + sp, n);
if (readed <= 0) {
return 0;
}
n -= readed;
sp += readed;
}
return 1;
}
client.c
#include <arpa/inet.h>
#include <sys/socket.h>
#include <sys/types.h>
#include <stdio.h>
#include <signal.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <time.h>
static const int bufSize = 1024;
int main(int argc,char *argv[])
{
signal(SIGCHLD, SIG_IGN);
fork();
fork();
fork();
fork();
fork();
fork();
fork();
fork();
//fork();
//fork();
char length[2], recvBuf[bufSize];
char buf[]="hello, world\0";
short len = strlen(buf);
sprintf(length,"%c",len);
int client_sockfd, size, i, n, state;
uint64_t delta_us = 0;
struct sockaddr_in server_addr;
struct timespec start, end;
client_sockfd = socket(PF_INET, SOCK_STREAM, 0);
memset(&server_addr, 0, sizeof server_addr);
server_addr.sin_family = AF_INET;
server_addr.sin_port = htons(7777);
inet_pton(AF_INET, "127.0.0.1", &server_addr.sin_addr.s_addr);
state = connect(client_sockfd, (struct sockaddr *)&server_addr,
sizeof server_addr);
if (state < 0) {
perror("connect err");
exit(1);
}
for (i=0;i<10;i++) {
clock_gettime(CLOCK_MONOTONIC_RAW, &start);
n = write(client_sockfd, length, sizeof length);
if (n<=0) {
perror("write err");
exit(1);
}
n = write(client_sockfd, buf, *((short *)&length));
if (n<=0) {
perror("write err");
exit(1);
}
n = read(client_sockfd, recvBuf, *((short *)&length));
if (n<=0) {
perror("read err");
exit(1);
}
clock_gettime(CLOCK_MONOTONIC_RAW, &end);
delta_us += (end.tv_sec - start.tv_sec) * 1000000 +
(end.tv_nsec - start.tv_nsec)/1000;
printf("%lu\n", delta_us);
sleep(1);
}
return 0;
}
The client first transmits the length of the message represented by 2 bytes.
Then client sends a "hello, world" message to the server in buf.
The server first reads 2 bytes through readn () and reads the message as much.
But in all cases (fork, thread, multiplexing IO... whatever), I have a common difficulty.
The problem is that : In the above source code, the length of the message("hello, world" is 12. so it is expected to read 12 in the first readn () of the server.
In fact, when the number of clients is small (the number of forks on the client is 7 or less), it works fine.
However, if the number of clients increases, the value of readLen will be 25960 on some connections. 25960 is the value that represents "he" in hex.
Why is this problem happening when there are many clients?
I wonder if it is the same as sending a TCP packet at one time and dividing it two times.
Below is a tcpdump capture of the above problem situation.
I'm sorry that I could not upload the image right away.
tcpdump
On the client side, you have multiple problems surrounding how you send the message length. Starting here:
char length[2], recvBuf[bufSize];
char buf[]="hello, world\0";
short len = strlen(buf);
sprintf(length,"%c",len);
Your sprintf format promises that the third argument will be of type char (promoted to int), and instructs it to output the corresponding character into the string. In fact, the argument is a short (promoted to int), and this mismatch produces undefined behavior.
In practice, the overall sprintf call is probably equivalent to this:
length[0] = (char)(int)len;
length[1] = '\0';
That has implementation-defined characteristics if char is a signed type, but in any event, it cannot capture a length greater than the maximum value that can be represented by an unsigned char.
The client goes on to do this:
n = write(client_sockfd, length, sizeof length);
That's not inherently wrong, but it does fail to accommodate the possibility of a short write. Moreover, the server does not interpret this part of the message in a manner consistent with the way it was prepared:
if (readn(connectFD, buffer, 2) == 0) {
break;
}
readLen = (*(short *)&buffer);
As it turns out, that combination might happen to work if the server uses a 16-bit, little-endian representation for type short (subject to the restriction I already described on representable message length) and an execution character set compatible with the client's, but those are not safe assumptions for network software in general.
In part, you seem to be missing an important point about read() and write() and char pointers: a char * can be used to read the representation of an object of any type, so you do not need to move your data into a char array in order to send or receive it.
Overall, this would be a more appropriate approach:
// Client:
uint16_t len = strlen(buf); // risk: buf may be too long
uint16_t len_n = htons(len);
int n = writen(client_sockfd, &len_n, 2); // a write() analog of readn()
// ... handle possible error ...
// Sever:
uint16_t len_n;
int n = readn(connectFD, &len_n, 2);
// ... possible handle error ...
uint16_t readLen = ntohs(len_n);
Note that there is no need to copy the length into a separate char array to send it, nor to receive it into a char array. On the other hand, note also the use of a specified-size data type (uint16_t) on both sides, and the use of htons() and ntohs() to ensure that the client and server interpret the bytes of the data the same way. Furthermore, note the use of a write analog of readn() to send the data, which otherwise could be sent incompletely.
By the same token the client and server should both use the (hypothetical) writen() function to send the text of the message, and just like the server, the client should use readn() to read it. Failing to account for the possibility of short reads and writes is a significant flaw, especially in a protocol such as yours whose message boundaries are not easily distinguishable from data. An unnoticed short read or write will cause your client and server to fall out of sync, with no easy way to recover.
Apart from the problems already noticed by #JohnBollinger, you only use a listen window of 1 in server.c when all your forked client processes try to connect at the same time. It caused plenty of connect : conn reset by peer errors in my tests. I had to use a window greater than 64 on my FreeBSD (no errors at 256) to get rid of them :
if (listen(listenFD, 256) < 0) {
perror("listen() error\n");
exit(0);
}
Related
We are working on a project where we want to communicate with a server.
This is our function to communicate with the server, but somehow it does not read the incoming messages correctly all the time.
Sometimes in the buffer there is something like:
(Server sends)"+ Client version acClient: ID 38ail6ii3s8jc"
instead of:
(Server sends)"+ Client version accepted - please send Game-ID to join"
(We send)"Client: ID 38ail6ii3s8jc"
So, I think the error is within the char *receiveAnswer(int sock) function.
#include <stdio.h>
#include <stdlib.h>
#include <sys/socket.h>
#include <sys/types.h>
#include <string.h>
#define BUFFERSIZE 1024
#define bzeroNew(b,len) (memset((b), '\0', (len)), (void) 0) //buffer loeschen
#define VERSION "VERSION 3.4\n"
#include "functions.h"
char buffer[BUFFERSIZE];
int prologEnd = 0;
int proof;
//liest von Server eine Nachricht ein und speichert sie im buffer ab
char *receiveAnswer(int sock) {
bzeroNew(buffer, BUFFERSIZE);
if(recv(sock, buffer, sizeof(buffer), 0) < 0) {
perror("ERROR: Empfangen fehlgeschlagen\n");
}
printf("%s", buffer);
return buffer;
}
void sendResponse(int sock, char* message) {
bzeroNew(buffer, BUFFERSIZE);
strcpy(buffer, message);
proof = send(sock, buffer, strlen(buffer), 0);
if(proof < 0) {
perror("ERROR: Senden fehlgeschlagen\n");
}
printf("Client: %s\n", buffer);
receiveAnswer(sock);
}
int performConnection(int sock, char* gameID) {
bzeroNew(buffer, BUFFERSIZE);
receiveAnswer(sock);
while(strncmp(buffer, "+", 1) == 0 && prologEnd == 0) {
if(strncmp(buffer, "+ MNM Gameserver", 16) == 0) {
receiveAnswer(sock);
sendResponse(sock, VERSION);
}
else if(strncmp(buffer, "+ Client", 8) == 0) {
sendResponse(sock, gameID);
}
else if(strncmp(buffer, "+ PLAYING", 9) == 0) {
sendResponse(sock, "PLAYER\n");
receiveAnswer(sock);
}
else if(strncmp(buffer, "+ YOU", 5) == 0) {
receiveAnswer(sock);
printf("\n");
prologEnd = 1;
}
else if(strncmp(buffer, "+ TOTAL", 7) == 0) {
receiveAnswer(sock);
receiveAnswer(sock);
prologEnd = 1;
}
}
bzeroNew(buffer, BUFFERSIZE);
return 0;
}
This is our main() function, but I think the error is within the file above:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/socket.h>
#include <sys/types.h>
#include <sys/wait.h> // für Warten auf Kindprozess
#include <unistd.h>
#include <netdb.h>
#include <netinet/in.h>
#include <fcntl.h>
#include <limits.h>
#include <signal.h>
// für Shared Memory:
#include <sys/ipc.h>
#include <sys/shm.h>
#include "functions.h"
#include "sharedMemory.h"
// dublicat, brauchen wir das?
#define GAMEKINDNAME "NMMorris"
#define HOSTNAME "sysprak.priv.lab.nm.ifi.lmu.de"
#define PORTNUMBER 1357
int main (int argc, char *argv[]) {
char gamekindname[256] = "NMMorris";
char hostname[256] = "sysprak.priv.lab.nm.ifi.lmu.de";
int portnumber = 1357;
char* gameID = argv[2];
char playerNumber[256];
char configFile[256] = "client.conf" ;
int fd[2]; // TODO: fd und client_fd vereinen
//gameID formatieren
char bufferGameID[64];
strcpy(bufferGameID, "ID ");
strcat(bufferGameID, gameID);
strcpy(gameID, bufferGameID);
strcat(gameID, "\n");
int i;
char tmp[256];
//Argumente einlesen und an Variablen übergeben
for(i = 3; i < 7; i++) {
strcpy(tmp, argv[i]);
if (strcmp(tmp, "-p") == 0){
strcpy(playerNumber, argv[i+1]);
} else if (strcmp(tmp, "-conf") == 0){
strcpy(configFile, argv[i+1]);
}
}
config configMain = readConfig(configFile);
strcpy(gamekindname, configMain.gameKind);
strcpy(hostname, configMain.hostServerName);
portnumber = configMain.portNmbr;
printf("\n>>>Config File Data<<<\n");
printf("HostServerName: %s\n", hostname);
printf("PortNumber: %d\n", portnumber);
printf("GameKind: %s\n\n ", gamekindname);
//From here: sockets
int sock, client_fd;
struct sockaddr_in serv_addr;
struct hostent *server;
sock = socket(AF_INET, SOCK_STREAM, 0);
if (sock < 0) {
perror("\nERROR: Socket creation error \n");
return - 1;
}
//ipAdresse nachschauen
server = gethostbyname(hostname);
if (server == NULL)
{
perror("ERROR: no such host\n");
}
memset(&serv_addr,0,sizeof(serv_addr));
serv_addr.sin_family = AF_INET;
serv_addr.sin_port = htons(portnumber);
memcpy(&serv_addr.sin_addr.s_addr,server->h_addr,server->h_length);
if ((client_fd = connect(sock, (struct sockaddr*) &serv_addr, sizeof(serv_addr))) < 0) {
perror("ERROR: Connection Failed \n");
return -1;
}
printf(">>> Mit Host : %s verbunden <<<\n", hostname);
if(performConnection(sock, gameID) != 0) {
perror("performConnection Failed\n");
} // TODO: verlagern
close(client_fd);
return 0;
// Shared Memory Segment erstellen
int shmid_game = shmget(KEY, sizeof(gameInfo), IPC_CREAT | SHM_R | SHM_W);
if (shmid_game == -1) {
perror("Error while creating shared memory segment");
exit(EXIT_FAILURE);
} else {
printf("Creation successful\n");
}
int shmid_player = shmget(KEY, sizeof(playerInfo), IPC_CREAT | SHM_R | SHM_W);
if (shmid_player == -1) {
perror("Error while creating shared memory segment");
exit(EXIT_FAILURE);
} else {
printf("Creation successful\n");
}
// Prozess mit SHM verbinden
void* shm_game = shmat(shmid_game, 0, 0);
if (shm_game == NULL) {
perror("Error while attaching shared memory segment");
exit(EXIT_FAILURE);
} else {
printf("Attachment successful\n");
}
void* shm_player = shmat(shmid_player, 0, 0);
if (shm_player == NULL) {
perror("Error while attaching shared memory segment");
exit(EXIT_FAILURE);
} else {
printf("Attachment successful\n");
}
// Kindprozess (Connector) erstellen
pid_t pid;
pid = fork();
if (pid < 0) {
fprintf(stderr, "Fehler bei Erstellung des Kindprozesses.\n");
} else if (pid == 0) { // Kindprozess (Connector)
close(fd[1]);
performConnection(sock, gameID);
} else { // Elternprozess (Thinker)
close(fd[0]);
}
return 0;
}
TCP is a stream-oriented protocol, not a message-oriented one. A message sent as 100 bytes can be received as 1 100-byte read or as 100 1-byte reads, or any combination in between. This means that you must keep looping on the recv() until you have the whole message. That, in turn, means you need to know when a message is completely received. Either prepend the message's length before the message, use a fixed-size message, or have a unique recognizable terminator at the end of the message.
recv() returns the number of bytes that it has written into your buffer -- which is to say, it returns the number of bytes that it currently has available to give to you at the time you called it. Importantly, that will often be less than the number of bytes you requested, so it is mandatory that you check the return value of recv() to find out how many bytes you actually received, and not just assume that the value returned by recv() is equal to sizeof(buffer).
OTOH if you want recv() to not return until sizeof(buffer) bytes have been successfully read, you can pass the MSG_WAITALL flag to recv() in the fourth argument.
From the recv man page:
This flag requests that the operation block until the full
request is satisfied. However, the call may still return
less data than requested if a signal is caught, an error
or disconnect occurs, or the next data to be received is
of a different type than that returned. This flag has no
effect for datagram sockets.
It is not guaranteed that recv() will get all of the bytes sent at once. The convention is to call it in a loop until you've read all the bytes.
NB that recv() returns 0 when the client is stalling or closed the connection, and -1 on a read error.
Handling partial send()s:
int sendall(int s, char *buf, int *len)
{
int total = 0; // how many bytes we've sent
int bytesleft = *len; // how many we have left to send
int n;
while(total < *len) {
n = send(s, buf+total, bytesleft, 0);
if (n == -1) { break; }
total += n;
bytesleft -= n;
}
*len = total; // return number actually sent here
return n==-1?-1:0; // return -1 on failure, 0 on success
}
— From Beej's guide to Network Programming
The above code snippet calls send() in a loop until all the data has been sent.
You can now write a similar recv_all function that calls recv() in a loop until it has read all the data.
Handling partial recv()s:
Perhaps something like this:
/* Synopsis: Calls recv() in a loop to ensure
* len bytes have been read. Stores
* the total number of bytes sent in len.
*
* Returns: 0 on failure, 1 otherwise.
*/
static int recv_all(int sockfd, char *buf, size_t *len)
{
size_t bytes_left = *len;
size_t total = 0;
ssize_t rv = 0;
errno = 0;
while (total < *len) {
rv = recv(sockfd, buf + total, bytes_left, 0);
if (rv == 0) { /* Client closed the connection or is stalling */
return 0;
} else if (rv == -1) { /* A read error */
perror("recv");
return 0;
}
total += rv;
bytes_left -= rv;
}
*len = total;
return 1;
}
recv() may also return 0 when 0 characters were read. In that case, len can be compared against the original len to see if the call to recv() was successful.
Note: The above recv_all function has not been tested, and hence is not guaranteed to be bug free. It's meant to be an example.
So I am learning about buffer overflow attacks in C. I understand what they are and I can find a buffer overflow vulnerability in a simple C code. Simple is fine :).
But this code seems to go beyond my definition of 'simple'.
So far, I understand that in this C code, buffer overflow vulnerabilities can happen mainly in the line: strcpy(retstr, "Process Error."); but there is an if statement above the line that I think protects against buffer overflow at this line.
I would appreciate any help in finding the buffer overflow vulnerability in this code.
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <sys/wait.h>
#include <stdio.h>
#include <stdlib.h>
#include <netdb.h>
#include <string.h>
#include <fcntl.h>
#include <unistd.h>
#include <sys/stat.h>
#include <time.h>
#define CANBUFSIZE 106
#define MSGBUFSIZE 256
#define TIMEBUFSIZE 128
char msgbuf[MSGBUFSIZE];
char canarybuf[CANBUFSIZE];
void get_time(char* format, char* retstr, unsigned received)
{
// memory for our local copy of the timestring
char timebuf[TIMEBUFSIZE];
time_t curtime;
// if the format string esceeds our local buffer ...
if(strlen(format) > TIMEBUFSIZE)
{
strcpy(retstr,"Process Error.");
return;
}
// otherwise create a local working copy
memcpy(timebuf,format,received);
// Get the current time.
curtime = time (NULL);
// Convert it to local time representation.
// and convert the format string to the real timestring
struct tm *loctime = localtime (&curtime);
strftime(retstr,TIMEBUFSIZE,timebuf,loctime);
return;
}
int main(int argc, char** argv)
{
int port; // the portnumber of our service
struct in_addr bind_addr; // bind address of the server
int sd; // the socketdescriptor
struct sockaddr_in addr; // address of our service
struct sockaddr_in addr_from; //address of the client
int addrlen = sizeof(addr_from);
int pid; // our process id
int sid; // our session id
unsigned received; // number of bytes received from network
// resolve command line arguments
if(argc != 3)
{
printf("Usage: timeservice <bind address> <portnum>\n");
return 1;
}
if (inet_aton(argv[1], &bind_addr) == 0)
{
fprintf(stderr, "Invalid bind address\n");
exit(EXIT_FAILURE);
}
port = atoi(argv[2]);
if ((port < 1024) || (port > 65535))
{
printf("Portrange has to be between 1024 and 65535.\n");
exit(EXIT_FAILURE);
}
// forking to background
pid = fork();
if(pid < 0)
{
printf("fork() failed\n");
exit(EXIT_FAILURE);
}
// we are parent
else if(pid > 0)
{
return 0;
}
/*
* we are the child process
* because of the termination of our parent, we need a new session id,
* else we are zombie
*/
sid = setsid();
if (sid < 0) {
return 1;
}
/*
* since we are a system service we have to close all standard file
* descriptors
*/
close(STDIN_FILENO);
close(STDOUT_FILENO);
close(STDERR_FILENO);
// create an udp socket
if((sd = socket(PF_INET,SOCK_DGRAM,IPPROTO_UDP)) < 0)
{
return 1;
}
// clear the memory of our addr struct
memset(&addr,0,sizeof(addr));
// Protocol Family = IPv4
addr.sin_family = PF_INET;
// Listen on bindAddr and bindPort only
addr.sin_addr.s_addr = bind_addr.s_addr;
addr.sin_port = htons(port);
// bind to the udp socket
if(bind(sd,(struct sockaddr*)&addr,sizeof(addr)) != 0)
{
return 1;
}
for(;;)
{
// prepare memory
memset(&msgbuf, 0, sizeof(msgbuf));
received = recvfrom(sd,msgbuf,MSGBUFSIZE,MSG_WAITALL,
(struct sockaddr*)&addr_from,(socklen_t*) &addrlen);
// fork a new child
pid = fork();
// we are parent
if (pid > 0)
{
// wait for the child to finish
waitpid(pid,NULL,0);
}
else
{
/*
* we are inside the child process
*/
// reserve some memory for our response
char * returnstr = (char*) malloc(TIMEBUFSIZE);
// analyse the client request and format the time string
get_time(msgbuf, returnstr, received);
// send our response to the client
sendto(sd,returnstr,strlen(returnstr)+1,MSG_DONTWAIT,
(struct sockaddr *) &addr_from, addrlen);
free(returnstr);
return EXIT_SUCCESS;
}
}
close(sd);
return 0;
}
There is a discrepancy in get_time: strlen is used to check the "size" of the incoming buffer, but memcpy is used with a user-supplied received argument. It suffices to pass a buffer with a NUL byte within the first TIMEBUFSIZE bytes.
You can trigger the crash directly in code if you do:
received = 256;
memset(msgbuf, 'A', MSGBUFSIZE);
msgbuf[0] = 0;
this will "fill up" msgbuf with 256 bytes and then keep writing for 128 bytes more, overwriting the return address on the stack to an address of your choice. Because the first byte is a NUL, the strlen check passes.
If you want to trigger this on the actual binary, you probably need something like: (assuming it runs on localhost:1234)
perl -MIO::Socket::IP -E '
$buf = "\0" . ("A"x255);
my $s = IO::Socket::IP->new(PeerHost => "127.0.0.1", PeerPort => 1234, Type => SOCK_DGRAM);
$s->autoflush(1);
print $s $buf;
'
and then of course you need to modify the buffer to perform actual code flow
As an assignment of a lab , I want first to find the vulnaribility of the following code , then run in as a sample timeserver and then attack it using buffer overflow . But the first problem is not knowing where exactly to start .
I know that there are some functions that could be harmfull in that C program ( such as " strftime " , or " memcoy " , or "strcpy" ) but I can not select which one is the most proper one to start with .
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <sys/wait.h>
#include <stdio.h>
#include <stdlib.h>
#include <netdb.h>
#include <string.h>
#include <fcntl.h>
#include <unistd.h>
#include <sys/stat.h>
#include <time.h>
#define CANBUFSIZE 106
#define MSGBUFSIZE 256
#define TIMEBUFSIZE 128
char msgbuf[MSGBUFSIZE];
char canarybuf[CANBUFSIZE];
void get_time(char* format, char* retstr, unsigned received)
{
// memory for our local copy of the timestring
char timebuf[TIMEBUFSIZE];
time_t curtime;
// if the format string esceeds our local buffer ...
if(strlen(format) > TIMEBUFSIZE)
{
strcpy(retstr,"Process Error.");
return;
}
// otherwise create a local working copy
memcpy(timebuf,format,received);
// Get the current time.
curtime = time (NULL);
// Convert it to local time representation.
// and convert the format string to the real timestring
struct tm *loctime = localtime (&curtime);
strftime(retstr,TIMEBUFSIZE,timebuf,loctime);
return;
}
int main(int argc, char** argv)
{
int port; // the portnumber of our service
struct in_addr bind_addr; // bind address of the server
int sd; // the socketdescriptor
struct sockaddr_in addr; // address of our service
struct sockaddr_in addr_from; //address of the client
int addrlen = sizeof(addr_from);
int pid; // our process id
int sid; // our session id
unsigned received; // number of bytes received from network
// resolve command line arguments
if(argc != 3)
{
printf("Usage: timeservice <bind address> <portnum>\n");
return 1;
}
if (inet_aton(argv[1], &bind_addr) == 0)
{
fprintf(stderr, "Invalid bind address\n");
exit(EXIT_FAILURE);
}
port = atoi(argv[2]);
if ((port < 1024) || (port > 65535))
{
printf("Portrange has to be between 1024 and 65535.\n");
exit(EXIT_FAILURE);
}
// forking to background
pid = fork();
if(pid < 0)
{
printf("fork() failed\n");
exit(EXIT_FAILURE);
}
// we are parent
else if(pid > 0)
{
return 0;
}
/*
* we are the child process
* because of the termination of our parent, we need a new session id,
* else we are zombie
*/
sid = setsid();
if (sid < 0) {
return 1;
}
/*
* since we are a system service we have to close all standard file
* descriptors
*/
close(STDIN_FILENO);
close(STDOUT_FILENO);
close(STDERR_FILENO);
// create an udp socket
if((sd = socket(PF_INET,SOCK_DGRAM,IPPROTO_UDP)) < 0)
{
return 1;
}
// clear the memory of our addr struct
memset(&addr,0,sizeof(addr));
// Protocol Family = IPv4
addr.sin_family = PF_INET;
// Listen on bindAddr and bindPort only
addr.sin_addr.s_addr = bind_addr.s_addr;
addr.sin_port = htons(port);
// bind to the udp socket
if(bind(sd,(struct sockaddr*)&addr,sizeof(addr)) != 0)
{
return 1;
}
for(;;)
{
// prepare memory
memset(&msgbuf, 0, sizeof(msgbuf));
received = recvfrom(sd,msgbuf,MSGBUFSIZE,MSG_WAITALL,
(struct sockaddr*)&addr_from,(socklen_t*) &addrlen);
// fork a new child
pid = fork();
// we are parent
if (pid > 0)
{
// wait for the child to finish
waitpid(pid,NULL,0);
}
else
{
/*
* we are inside the child process
*/
// reserve some memory for our response
char * returnstr = (char*) malloc(TIMEBUFSIZE);
// analyse the client request and format the time string
get_time(msgbuf, returnstr, received);
// send our response to the client
sendto(sd,returnstr,strlen(returnstr)+1,MSG_DONTWAIT,
(struct sockaddr *) &addr_from, addrlen);
free(returnstr);
return EXIT_SUCCESS;
}
}
close(sd);
return 0;
}
I compiled the file using gcc and run the localserver with ./timeserver 127.0.0.1 2222 and then connects to that with nc -u 127.0.0.1 2222 . Then I'm now able to input some strings of format strings to the server and take some results back . For example if I use %d in the command line it will show me a date/time in a special format .
I know that this is the place that I should force some strings and crash the program but I can not find exact string of that .
The return value of recvfrom is not checked, which is a mistake. If recvfrom gets an error, then the return value is -1. Since the type of received is unsigned, a second mistake, this will appear to be a large positive value. The memcpy call will cause the program to fail.
I'm trying to dev a little tool in C which includes HTTP Requests, but I have a problem I can't solve because I can't exactly find my error :/
This is a little part of my tool and I think that the function "http_request" has a problem with some HTML response.
Sometimes I have a segfault, sometimes a "free invalid next size" ... I'm thinking that my pointer has not correctly used.
I've try to reproduce the bug with very long string but nothing is happening..
(I think my problem is in the part /* receive the response */)
Here's the code:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <netdb.h>
#define BUF_SIZE 256
char *http_request(char *host, int port, char *r_http)
{
struct hostent *server;
struct sockaddr_in serv_addr;
int sockfd;
/* create the socket */
sockfd = socket(AF_INET, SOCK_STREAM, 0);
if (sockfd < 0) printf("ERROR opening socket");
/* lookup the ip address */
server = gethostbyname(host);
if (server == NULL)
{
printf("ERROR, no such host");
return NULL;
}
/* fill in the structure */
memset(&serv_addr,0,sizeof(serv_addr));
serv_addr.sin_family = AF_INET;
serv_addr.sin_port = htons(port);
memcpy(&serv_addr.sin_addr.s_addr,server->h_addr,server->h_length);
/* connect the socket */
if (connect(sockfd,(struct sockaddr *)&serv_addr,sizeof(serv_addr)) < 0)
{
printf("ERROR connecting");
return NULL;
}
/* send the request */
int sent = 0,total = strlen(r_http), bytes, received;
do {
bytes = write(sockfd,r_http+sent,total-sent);
if (bytes < 0)
printf("ERROR writing message to socket");
if (bytes == 0)
break;
sent+=bytes;
} while (sent < total);
/* receive the response */
char *response = malloc(sizeof(char)*BUF_SIZE), *tmp_response = malloc(sizeof(char)*BUF_SIZE), rec_data[BUF_SIZE]={0};
// unsigned int new_size = BUF_SIZE;
size_t new_size = BUF_SIZE;
while((bytes = read(sockfd,rec_data,BUF_SIZE)))
{
/* Check if error or end of receipt */
if (bytes < 0 || bytes == 0)
{
if(bytes < 0)
printf("ERROR reading response from socket");
break;
}
/* Process vars */
if(new_size == BUF_SIZE)
snprintf(response,(BUF_SIZE+1),"%s",rec_data);
else {
tmp_response = realloc(tmp_response,sizeof(char)*strlen(response));
memset(tmp_response,0,sizeof(tmp_response));
snprintf(tmp_response,(new_size+1),"%s",response);
response = realloc(response,sizeof(char)*strlen(tmp_response)+sizeof(char)*strlen(rec_data));
memset(response,0,sizeof(response));
snprintf(response,(new_size+1),"%s%s",tmp_response,rec_data);
}
new_size+=BUF_SIZE;
memset(rec_data,0,sizeof(rec_data));
}
/* close the socket */
close(sockfd);
/* free space */
free(r_http);
free(tmp_response);
// free(response);
return response;
}
char *http_get(char *host, int port, char *get_request)
{
char *base_http = "GET %s HTTP/1.0\r\n\r\n", *r_http = malloc(sizeof(char)*strlen(base_http)+sizeof(char)*strlen(get_request));
sprintf(r_http,base_http,get_request);
return http_request(host,port,r_http);
}
int main(int argc, char *argv[], char *envp[])
{
char *resp = http_get("127.0.0.1",80,"/test.html");
printf("Response: |%s|\n",resp);
return 0;
}
The main problem: Your realloc sizes are consistently one off - You forgot that snprintf will need to have space for the 0 byte at the string end (strlen will always give you one byte less than you actually need to store the string)
Other (more marginal) problems:
You let snprintf (which is quite an expensive function) do the job of a simple memcpy
I don't really see the purpose for having a secondary buffer tmp_response - you could simply use rec_data for that. You would also get rid of one realloc call in your code.
Also quite some of the memset (which is used with wrong arguments anyhow) is unnecessary - Just copy the string over, there is not much purpose for clearing the buffers to 0 first, as long as you make sure you copy the string end around consistently.
Thanks all for you attention!
I've try to refactor my code with your adivce but I've some problem..
I've bug with little response I don't have the last char and sometime the response is in double (concat with the response header)
I've replace snprintf by memcpy and strncat, remove tmp buffer, send my var by reference into memset and add 1 byte space allocation for 0x00 at end of string.
If you see any error even small please tell me :)
My memset is correctly used now?
This is my new loop:
while((bytes = read(sockfd,rec_data,BUF_SIZE)))
{
/* Check if error or end of receipt */
if (bytes < 0 || bytes == 0)
{
if(bytes < 0)
error("ERROR reading response from socket");
break;
}
/* Process vars */
if(new_size == BUF_SIZE)
{
memcpy(response,rec_data,strlen(rec_data)+1);
response[strlen(response)-1]=0x00;
}else
{
response = realloc(response,new_size+1);
strncat(response,rec_data,BUF_SIZE);
memset(&rec_data,0,BUF_SIZE);
}
new_size += BUF_SIZE;
}
I am trying to send audio file from one computer to other using socket programming in c. When I send simple string without any framing information such as header or tailer it gets sent perfectly. But when I try to send the same information with some header information like size of the socket_data or packet_no. it doesn't get sent properly. Even the terminal output is SAME on both the machines but the file which gets created is totally different and unplayable. I have used serializing concept to send packet. Am attaching codes. Please comment whats going wrong.
Server.c
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <fcntl.h>
void set_socket(struct sockaddr_in *socket, int type, int host_short, int addr_type)
{
socket -> sin_family = type;
socket -> sin_port = htons(host_short);
socket -> sin_addr.s_addr = htonl(addr_type);
}
void serialize(char *buffer, int count, char *data)
{
int i=0, j=0;
char temp1[20];
sprintf(temp1, "%d", count);
while(temp1[i] != '\0')
{
buffer[j++] = temp1[i++];
}
buffer[j++]=' ';
for(i=0; data[i] != '\0'; i++)
{
buffer[j++] = data[i];
}
buffer[j] = '\0';
printf("BUFFER =%ld\n", sizeof(buffer));
}
int main()
{
int sid = 0, bid = 0, fp;
char *send_data = (char *)malloc(1024*sizeof(char));
char temp[1024];
char *receive_data = (char *)malloc(1024*sizeof(char));
int fd, count, cnt=0;
struct sockaddr_in server_socket, client_socket;
int size = sizeof(client_socket);
if((sid = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP)) == -1)
{
printf("Connection error..\n");
exit(1);
}
set_socket(&server_socket, AF_INET, 6000, INADDR_ANY);
if((bid = bind(sid, (struct sockaddr *)&server_socket, sizeof(struct sockaddr))) == -1)
{
printf("Binding error..\n");
exit(1);
}
printf("I am waiting for client..\n");
recvfrom(sid, receive_data, 1024, 0,(struct sockaddr *)&client_socket, &size);
printf("received data is : %s\n", receive_data);
fd = open(receive_data, O_RDONLY);
printf("size = %ld\n", sizeof(send_data));
while((count=read(fd, temp, 500)) != 0)
{
printf("I am inside the loop : %d\n", cnt++);
serialize(send_data, count, temp);
printf("Serialized : %s\n", send_data);
sendto(sid, send_data, 1024, 0, (struct sockaddr *)&client_socket, size);
}
printf("I am outside the loop : %d\n", count);
strcpy(temp, "ENDOFFILE");
serialize(send_data, sizeof(temp), temp);
sendto(sid, send_data, 1024, 0, (struct sockaddr *)&client_socket, size);
fcloseall();
close(sid);
close(fd);
return 0;
}
Client.c
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <netdb.h>
#include <fcntl.h>
void set_socket(struct sockaddr_in *socket, int type, int host_short)
{
socket -> sin_family = type;
socket -> sin_port = htons(host_short);
}
void deserialize(char *buffer, int *size, char *data)
{
int i=0, j=0;
char temp1[20];
while(buffer[i] != ' ')
{
temp1[j++] = buffer[i++];
}
temp1[j] = '\0';
printf("\nINT : %s\n", temp1);
*size = atoi(temp1);
i++;
j=0;
while(buffer[i] != '\0')
{
data[j++] = buffer[i++];
}
data[j++] = '\0';
}
int main()
{
int sid = 0, bid = 0, con = 0;
char *send_data = (char *)malloc(1024*sizeof(char));
char *receive_data = (char *)malloc(1024*sizeof(char));
char *temp = (char *)malloc(1024*sizeof(char));
struct hostent *host;
struct sockaddr_in server_socket;
int size = sizeof(server_socket);
if((sid = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP)) == -1)
{
printf("Connection error at client side..\n");
exit(1);
}
set_socket(&server_socket, AF_INET, 6000);
if (inet_aton("127.0.0.1", &server_socket.sin_addr)==0)
{
fprintf(stderr, "inet_aton() failed\n");
exit(1);
}
printf("Enter the name of the file you want to see : ");
scanf("%s", send_data);
int fd = open("sanket.mp3", O_CREAT | O_RDWR, S_IRUSR | S_IWUSR | S_IXUSR);
sendto(sid, send_data, 1024, 0, (struct sockaddr *)&server_socket, size);
printf("================= Contents of the File =====================\n");
while(1)
{
int size;
recvfrom(sid, temp, 1024, 0, (struct sockaddr *)&server_socket, &size);
printf("Deserialize it : %s\n",temp);
deserialize(temp, &size, receive_data);
if(!strcmp(receive_data, "ENDOFFILE"))
{
printf("============================================================\n");
break;
}
else
write(fd, receive_data, size);
}
fcloseall();
close(sid);
return 0;
}
When I checked the size of the sent and received file, sizes are same but the contents are different, thus I am unable to play received audio file.
You seem to be reading from a binary data-file, yet inside your serialize function you are treating the data as-if it were null-terminated string data. For instance, this loop inside serialize:
for(i=0; data[i] != '\0'; i++)
{
buffer[j++] = data[i];
}
will terminate on the first zero-value it encounters. If this is true binary data from your audio file though, then I'm sure you'll get 0 values that are actual audio data rather than indicating the end of the buffer. Instead of terminating on a NULL-value, you should be terminating on the size of the buffer that you're passing to serialize that was read in your call to read in the while-loop. That way you can be sure you are getting all the data that was read from your read call packed into your send-buffer.
Secondly, printing sizeof(buffer), when buffer is a pointer, will only print the size of a pointer-type, not the size of the actual buffer the pointer is pointing to. Again, you're going to have to explicitly pass that value to your serialize function.
Third, you're terminating the buffer with a null-value ... again, that's not going to be a good idea based on the first point about this being raw binary data and not null-terminated strings. You should either come up with some type of string to indicate the end-of-transmission in the buffer that would be a set of values that would be impossible to be part of the data, or you should explicitly read the number of bytes that are in the "count" that you've embedded in the packet data.
Finally, you're not really serializing your data ... the concept of serializing typically means to transfer the data in a platform-independent way. You're simply packing up the bytes read and sending them across the network, assuming that the receiving side has the same endianness, etc. A fairly simple serialization approach would do something like creating ASCII strings from all the data-values, with the downside that this will create quite a bit of data-bloat. There are other cross-platform standards for serialized data such as JSON, SOAP, etc.