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;
}
Related
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
This question already has answers here:
C comparing char to "\n" warning: comparison between pointer and integer
(2 answers)
Closed 4 years ago.
I'm currently working with sockets in C, and it seems we can't get the output to buffer correctly. How it works is that a client sends a string to a server in pieces. The server waits until it finds a newline character in the string. Once it has its string, it replaces the newline character with a null terminator, then prints the buffer. The program moves the extra data to the front of the buffer and repeats the process.
Unfortunately, my program does not want to replace the newline with the null terminator.
All attempts to test the code end with the string being outputed in pieces
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <unistd.h>
#include <arpa/inet.h>
#ifndef PORT
#define PORT 30000
#endif
int setup(void) {
int on = 1, status;
struct sockaddr_in self;
int listenfd;
if ((listenfd = socket(AF_INET, SOCK_STREAM, 0)) == -1) {
perror("socket");
exit(1);
}
// Make sure we can reuse the port immediately after the
// server terminates.
status = setsockopt(listenfd, SOL_SOCKET, SO_REUSEADDR,
(const char *) &on, sizeof(on));
if(status == -1) {
perror("setsockopt -- REUSEADDR");
}
self.sin_family = AF_INET;
self.sin_addr.s_addr = INADDR_ANY;
self.sin_port = htons(PORT);
memset(&self.sin_zero, 0, sizeof(self.sin_zero)); // Initialize sin_zero to 0
printf("Listening on %d\n", PORT);
if (bind(listenfd, (struct sockaddr *)&self, sizeof(self)) == -1) {
perror("bind"); // probably means port is in use
exit(1);
}
if (listen(listenfd, 5) == -1) {
perror("listen");
exit(1);
}
return listenfd;
}
/*
* Search the first inbuf characters of buf for a network newline ("\r\n").
* Return the location of the '\r' if the network newline is found,
* or -1 otherwise.
* Definitely do not use strchr or any other string function in here. (Why not?)
*/
int find_network_newline(const char *buf, int inbuf) {
int i = 0;
int found = 0;
int location = 0;
while(i < inbuf && found == 0){
if(buf[i] == "\r"){
location = i;
found = 1;
}
i++;
}
if(found = 1){
return location;
}
return -1; // return the location of '\r' if found
}
int main(void) {
int listenfd;
int fd, nbytes;
char buf[30];
int inbuf; // how many bytes currently in buffer?
int room; // how much room left in buffer?
char *after; // pointer to position after the (valid) data in buf
int where; // location of network newline
struct sockaddr_in peer;
socklen_t socklen;
listenfd = setup();
while (1) {
socklen = sizeof(peer);
// Note that we're passing in valid pointers for the second and third
// arguments to accept here, so we can actually store and use client
// information.
if ((fd = accept(listenfd, (struct sockaddr *)&peer, &socklen)) < 0) {
perror("accept");
} else {
printf("New connection on port %d\n", ntohs(peer.sin_port));
// Receive messages
inbuf = 0; // buffer is empty; has no bytes
room = sizeof(buf); // room == capacity of the whole buffer
after = buf; // start writing at beginning of buf
while ((nbytes = read(fd, after, room)) > 0) {
// Step 2: update inbuf (how many bytes were just added?)
inbuf = inbuf + nbytes;
// Step 3: call find_network_newline, store result in variable "where"
where = find_network_newline(buf, inbuf);
if (where >= 0) { // OK. we have a full line
// Step 4: output the full line, not including the "\r\n",
// using print statement below.
// Be sure to put a '\0' in the correct place first;
// otherwise you'll get junk in the output.
// (Replace the "\r\n" with appropriate characters so the
// message prints correctly to stdout.)
buf[where] = "\0";
printf("Next message: %s", buf);
// Note that we could have also used write to avoid having to
// put the '\0' in the buffer. Try using write later!
// Step 5: update inbuf and remove the full line from the buffer
// There might be stuff after the line, so don't just do inbuf = 0
// You want to move the stuff after the full line to the beginning
// of the buffer. A loop can do it, or you can use memmove.
// memmove(destination, source, number_of_bytes)
after += where;
memmove(buf, after, 30);
inbuf = strlen(buf);
}
// Step 6: update room and after, in preparation for the next read
room = 30 - inbuf;
after = buf;
}
close(fd);
}
}
return 0;
}
it is because you are comparing buf[i] to a string not to characther.
if(buf[i] == "\r")
should be
if(buf[i] == '\r')
and
buf[where] = "\0";
should be
buf[where] = '\0';
Try using '\0' instead of "\0", i think it should work.
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);
}
I am trying to learn C and I can't get these apps working. I am creating 2 apps client/server, where the client connects to a server via specified port, and sends a file name (text) to the server. The server then takes the file name, runs it through a word count function and then responds to the client with filename/line/word/character count. I have gotten the client and the server to connect and communicate but my problem is that I can't seem to pass the client input to the wordcount function properly. Also, I'm not sure my function will return properly as I haven't found an appropriate method of returning a crafted string. Any help would be appreciated. Thanks!
#include <ctype.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
void error(const char *msg){
perror(msg);
exit(1);
}
char * wordcount(char *cfile){
int i = 0;
int ccount = 0;
int wcount = 0;
int lcount = 0;
char *fn = malloc(strlen(cfile+7));
sprintf(fn, "/Files/%s", cfile);
FILE *cfilename = fopen (fn, "r");
while ((i = fgetc(cfilename)) != EOF){
if (i == '\n') {
lcount++;
}
if (i == '\t' || i == ' '){
wcount++;
}
ccount++;
}
printf("%c contains %d words, %d characters and %d lines.\n", cfile, wcount, ccount, lcount);
return 0;
}
int main(int argc, char *argv[]){
int sock, newSock, portno, n;
struct sockaddr_in serv_addr, cli_addr;
socklen_t clilen;
char buffer[256];
int index = 5;
int lowPortNum = 2500 + (10 * index);
int highPortNum = 2500 + (10 * index) + 9;
/* Check for proper amount of args */
if (argc < 2){
fprintf(stderr, "ERROR: No port specified. Exiting...\n");
printf("NOTE: Port must be between %d & %d.\n",lowPortNum,highPortNum);
exit(1);
}
/* Create socket by using args to form components */
sock = socket(AF_INET , SOCK_STREAM , 0);
if (sock < 0){
error("ERROR: Could not create socket");
}
bzero((char *) &serv_addr, sizeof(serv_addr));
portno = atoi(argv[1]);
serv_addr.sin_family = AF_INET;
serv_addr.sin_addr.s_addr = INADDR_ANY;
serv_addr.sin_port = htons(portno);
if(bind(sock, (struct sockaddr *) &serv_addr, sizeof(serv_addr)) < 0){
error("ERROR: Binding failed.");
return 1;
}
/* Start listening on socket */
listen(sock, 5);
puts("Server is waiting for connection...");
clilen = sizeof(cli_addr);
/* Accept connection from incoming client */
newSock = accept(sock, (struct sockaddr *) &cli_addr, &clilen);
if (newSock < 0){
error("ERROR: Accept failed.");
}
/* Read message from the client */
bzero(buffer, 256);
if (read(newSock, buffer, 255) < 0){
error("ERROR: Cannot read from socket.");
}
//debug
printf("client msg read: %s\n",buffer);
/* Send message to the client */
//wordcount(buffer);
if (write(newSock , wordcount(buffer) , 100) < 0){
error("ERROR: Cannot write to socket.");
}
close(newSock);
close(sock);
return 0;
}
Firstly, I think you've made a mistake here:
char *fn = malloc(strlen(cfile+7));
sprintf(fn, "/Files/%s", cfile);
You probably meant this:
char fn[strlen(cfile) + 8];
sprintf(fn, "/Files/%s", cfile);
You might notice that I've rearranged it a little; I think you wanted to add 7 to the return value of strlen, not to the argument of strlen. I've written 8 instead, because the extra 1 is for a '\0' which goes at the end of your string; that's extremely important. When you're crafting strings, always remember to make space for the '\0'.
Additionally, I've changed your malloc to a variable-length array. You really don't need to use dynamic storage duration (e.g. malloc) for this; try to prefer automatic storage duration unless you absolutely need dynamic storage duration.
Especially considering that your code leaks memory (which is the precise reason to avoid it unless you absolutely need it). Perhaps valgrind would be a useful tool in your development environment? Always remember to free any memory you have mallocd.
There's another error here:
FILE *cfilename = fopen (fn, "r");
while ((i = fgetc(cfilename)) != EOF){
Supposing fopen returns NULL (probably to indicate that the file doesn't exist), the calls to fgetc following it are clearly going to fail in disastrous ways. I think you meant something like this:
FILE *cfilename = fopen (fn, "r");
if (cfilename == NULL) {
/* XXX: HANDLE THIS ERROR! We'll get to this later... */
}
while ((i = fgetc(cfilename)) != EOF){
... and similarly, you've forgotten to fclose that file that was fopend. Always remember to fclose files that you have fopend.
... my problem is that I can't seem to pass the client input to the wordcount function properly
Providing the mistakes mentioned earlier are fixed, you should be able to safely pass the message received from your socket to wordcount as you have in your comment, without crashes or resource leaks: wordcount(buffer);...
You can try that if you like, but bear with me for a moment longer because you have other requirements to assess.
I'm not sure my function will return properly as I haven't found an appropriate method of returning a crafted string.
Think about how standard library functions handle this. You've used one of them here: sprintf(fn, "/Files/%s", cfile);. By accepting the destination (fn) for the string as an argument, sprintf allows you to use whichever storage duration you like. Additionally, this allows sprintf to return some other int value (which you can look up in the sprintf manual in your own time)...
If you design your function to write to a destination pointed to by an argument, like sprintf (and others) do, you'll be able to use your function however you like (e.g. automatic or dynamic storage duration?), too. You'll be able to return an int value indicating success or failure (e.g. when the fopen call fails), too.
Consider the following function, which doesn't even need a return value because there are no error modes:
typedef unsigned long long ullong;
void fcount(FILE *f, ullong *char_count, ullong *word_count, ullong *line_count)
{
rewind(f);
*char_count = 0;
*word_count = 0;
*line_count = 0;
for (;;) {
int c = fgetc(f);
switch (c) {
case EOF: return;
case '\n': (*line_count)++;
case '\t':
case ' ': (*word_count)++;
default: (*char_count)++;
}
}
}
Now consider this wrapper of that function, which does require a return value
int count(char *destination, char *filename) {
char fn[strlen(filename) + 8];
sprintf(fn, "/files/%s", filename);
FILE *f = fopen(fn, "r");
if (f == NULL) {
/* Note: This exit code is defined within <stdlib.h> */
return EXIT_FAILURE;
}
ullong char_count, word_count, line_count;
count(f, &char_count, &word_count, &line_count);
fclose(f);
sprintf(destination, "%s contains %llu words, %llu characters and %llu lines.\n", filename, word_count, char_count, line_count);
return EXIT_SUCCESS;
}
Now you can tell if your function fails or succeeds, just like many of the standard library functions! Yay!
char buf[128];
count(buf, file_name);
printf("%s", buf);
You can also access the string you intended to write... Is this all flowing together?
I have looked around like crazy but don't get a real answer. I got one example, but that depended on the individuals own library so not much good.
At first I wanted to get the default gateway of an interface, but since different IP's could be routed differently I quickly understood that what I want it get the gateway to use for a given destination IP by using an AF_ROUTE socket and the rtm_type RTM_GET.
Does anyone have an example where I actually end up with a string containing the gateways IP (or mac address)? The gateway entry seem to be in hex but also encoded in /proc/net/route, where I guess the AF_ROUTE socket get's it info from (but via the kernel I guess).
Thanx in advance
and p.s.
I just started using stack overflow and I must say, all of you guys are great! Fast replies and good ones! You are my new best friends ;)
This is OS specific, there's no unified(or ANSI C) API for this.
Assuming Linux, the best way is to just parse /proc/net/route , look for the entry where Destination is 00000000 , the default gateway is in the Gateway column , where you can read the hex representation of the gateway IP address (in big endian , I believe)
If you want to do this via more specific API calls, you'll have to go through quite some hoops, here's an example program:
#include <netinet/in.h>
#include <net/if.h>
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/socket.h>
#include <sys/ioctl.h>
#include <linux/netlink.h>
#include <linux/rtnetlink.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#define BUFSIZE 8192
char gateway[255];
struct route_info {
struct in_addr dstAddr;
struct in_addr srcAddr;
struct in_addr gateWay;
char ifName[IF_NAMESIZE];
};
int readNlSock(int sockFd, char *bufPtr, int seqNum, int pId)
{
struct nlmsghdr *nlHdr;
int readLen = 0, msgLen = 0;
do {
/* Recieve response from the kernel */
if ((readLen = recv(sockFd, bufPtr, BUFSIZE - msgLen, 0)) < 0) {
perror("SOCK READ: ");
return -1;
}
nlHdr = (struct nlmsghdr *) bufPtr;
/* Check if the header is valid */
if ((NLMSG_OK(nlHdr, readLen) == 0)
|| (nlHdr->nlmsg_type == NLMSG_ERROR)) {
perror("Error in recieved packet");
return -1;
}
/* Check if the its the last message */
if (nlHdr->nlmsg_type == NLMSG_DONE) {
break;
} else {
/* Else move the pointer to buffer appropriately */
bufPtr += readLen;
msgLen += readLen;
}
/* Check if its a multi part message */
if ((nlHdr->nlmsg_flags & NLM_F_MULTI) == 0) {
/* return if its not */
break;
}
} while ((nlHdr->nlmsg_seq != seqNum) || (nlHdr->nlmsg_pid != pId));
return msgLen;
}
/* For printing the routes. */
void printRoute(struct route_info *rtInfo)
{
char tempBuf[512];
/* Print Destination address */
if (rtInfo->dstAddr.s_addr != 0)
strcpy(tempBuf, inet_ntoa(rtInfo->dstAddr));
else
sprintf(tempBuf, "*.*.*.*\t");
fprintf(stdout, "%s\t", tempBuf);
/* Print Gateway address */
if (rtInfo->gateWay.s_addr != 0)
strcpy(tempBuf, (char *) inet_ntoa(rtInfo->gateWay));
else
sprintf(tempBuf, "*.*.*.*\t");
fprintf(stdout, "%s\t", tempBuf);
/* Print Interface Name*/
fprintf(stdout, "%s\t", rtInfo->ifName);
/* Print Source address */
if (rtInfo->srcAddr.s_addr != 0)
strcpy(tempBuf, inet_ntoa(rtInfo->srcAddr));
else
sprintf(tempBuf, "*.*.*.*\t");
fprintf(stdout, "%s\n", tempBuf);
}
void printGateway()
{
printf("%s\n", gateway);
}
/* For parsing the route info returned */
void parseRoutes(struct nlmsghdr *nlHdr, struct route_info *rtInfo)
{
struct rtmsg *rtMsg;
struct rtattr *rtAttr;
int rtLen;
rtMsg = (struct rtmsg *) NLMSG_DATA(nlHdr);
/* If the route is not for AF_INET or does not belong to main routing table
then return. */
if ((rtMsg->rtm_family != AF_INET) || (rtMsg->rtm_table != RT_TABLE_MAIN))
return;
/* get the rtattr field */
rtAttr = (struct rtattr *) RTM_RTA(rtMsg);
rtLen = RTM_PAYLOAD(nlHdr);
for (; RTA_OK(rtAttr, rtLen); rtAttr = RTA_NEXT(rtAttr, rtLen)) {
switch (rtAttr->rta_type) {
case RTA_OIF:
if_indextoname(*(int *) RTA_DATA(rtAttr), rtInfo->ifName);
break;
case RTA_GATEWAY:
rtInfo->gateWay.s_addr= *(u_int *) RTA_DATA(rtAttr);
break;
case RTA_PREFSRC:
rtInfo->srcAddr.s_addr= *(u_int *) RTA_DATA(rtAttr);
break;
case RTA_DST:
rtInfo->dstAddr .s_addr= *(u_int *) RTA_DATA(rtAttr);
break;
}
}
//printf("%s\n", inet_ntoa(rtInfo->dstAddr));
if (rtInfo->dstAddr.s_addr == 0)
sprintf(gateway, (char *) inet_ntoa(rtInfo->gateWay));
//printRoute(rtInfo);
return;
}
int main()
{
struct nlmsghdr *nlMsg;
struct rtmsg *rtMsg;
struct route_info *rtInfo;
char msgBuf[BUFSIZE];
int sock, len, msgSeq = 0;
/* Create Socket */
if ((sock = socket(PF_NETLINK, SOCK_DGRAM, NETLINK_ROUTE)) < 0)
perror("Socket Creation: ");
memset(msgBuf, 0, BUFSIZE);
/* point the header and the msg structure pointers into the buffer */
nlMsg = (struct nlmsghdr *) msgBuf;
rtMsg = (struct rtmsg *) NLMSG_DATA(nlMsg);
/* Fill in the nlmsg header*/
nlMsg->nlmsg_len = NLMSG_LENGTH(sizeof(struct rtmsg)); // Length of message.
nlMsg->nlmsg_type = RTM_GETROUTE; // Get the routes from kernel routing table .
nlMsg->nlmsg_flags = NLM_F_DUMP | NLM_F_REQUEST; // The message is a request for dump.
nlMsg->nlmsg_seq = msgSeq++; // Sequence of the message packet.
nlMsg->nlmsg_pid = getpid(); // PID of process sending the request.
/* Send the request */
if (send(sock, nlMsg, nlMsg->nlmsg_len, 0) < 0) {
printf("Write To Socket Failed...\n");
return -1;
}
/* Read the response */
if ((len = readNlSock(sock, msgBuf, msgSeq, getpid())) < 0) {
printf("Read From Socket Failed...\n");
return -1;
}
/* Parse and print the response */
rtInfo = (struct route_info *) malloc(sizeof(struct route_info));
//fprintf(stdout, "Destination\tGateway\tInterface\tSource\n");
for (; NLMSG_OK(nlMsg, len); nlMsg = NLMSG_NEXT(nlMsg, len)) {
memset(rtInfo, 0, sizeof(struct route_info));
parseRoutes(nlMsg, rtInfo);
}
free(rtInfo);
close(sock);
printGateway();
return 0;
}
Maybe this is very old question but I had same problem and I can't find better result. Finally I solved my problem with these code that it has a few changes. So I decide to share it.
char* GetGatewayForInterface(const char* interface)
{
char* gateway = NULL;
char cmd [1000] = {0x0};
sprintf(cmd,"route -n | grep %s | grep 'UG[ \t]' | awk '{print $2}'", interface);
FILE* fp = popen(cmd, "r");
char line[256]={0x0};
if(fgets(line, sizeof(line), fp) != NULL)
gateway = string(line);
pclose(fp);
}
I decided to go the "quick-and-dirty" way to start with and read out the ip from /proc/net/route using netstat -rm.
I thought I'd share my function... Note however that there is some error in it and prehaps you could help me find it and I'll edit this to be without faults. The function take a iface name like eth0 and returns the ip of the gateway used by that iface.
char* GetGatewayForInterface(const char* interface) {
char* gateway = NULL;
FILE* fp = popen("netstat -rn", "r");
char line[256]={0x0};
while(fgets(line, sizeof(line), fp) != NULL)
{
/*
* Get destination.
*/
char* destination;
destination = strndup(line, 15);
/*
* Extract iface to compare with the requested one
* todo: fix for iface names longer than eth0, eth1 etc
*/
char* iface;
iface = strndup(line + 73, 4);
// Find line with the gateway
if(strcmp("0.0.0.0 ", destination) == 0 && strcmp(iface, interface) == 0) {
// Extract gateway
gateway = strndup(line + 16, 15);
}
free(destination);
free(iface);
}
pclose(fp);
return gateway;
}
The problem with this function is that when I leave pclose in there it causes a memory corruption chrash. But it works if I remove the pclose call (but that would not be a good solution beacuse the stream would remain open.. hehe). So if anyone can spot the error I'll edit the function with the correct version. I'm no C guru and gets a bit confused about all the memory fiddling ;)