• C

C Programming: Returning array of char pointers

I'm having difficulties returning an array of char pointers. Specifically, I need to return the array "Lines", and am getting error messages.

What do I need to do to modify the code below to make this work?

Errors are referring to the return statement below saying:
return from incompatible pointer type.
function returns address of local variable

char * foo(const char *config){
	char *Lines[100];
	char *ch;
	...
	read(fd, buffer, size);
	ch = buffer;
	...
	Lines[LineCount++] = ch;
	
	return Lines
}

void main(char *conf) {
	...
	char * configString = readConfig(conf);
	...
}

Open in new window

LVL 8
pzozulkaAsked:
Who is Participating?
I wear a lot of hats...

"The solutions and answers provided on Experts Exchange have been extremely helpful to me over the last few years. I wear a lot of hats - Developer, Database Administrator, Help Desk, etc., so I know a lot of things but not a lot about one thing. Experts Exchange gives me answers from people who do know a lot about one thing, in a easy to use platform." -Todd S.

gt2847cSr. Security ConsultantCommented:
My C's a bit rusty, but I see several problems with the code snippet above.

foo is returning a pointer to a char, Lines is an array of 100 elements, where each element is a pointer to a char. That's where the return from incompatible pointer type error comes from.  Correctly specified is:
char ** foo(const char *config) {

Open in new window

foo is also returning the address of the local variable Lines.  That var is being allocated locally on the stack and not out of the heap.  When the function returns, the memory for the Lines variable goes away so you pointer returned from foo is pointing at a random place in memory (this is bad).  Since you are wanting to return this array, you'll need to allocate memory for it.  Also, you'll need to remember to free it when you're done with it (avoid memory leaks).  Remember to #include<malloc.h>
	char **Lines;
	...
	Lines = malloc( sizeof(char*) * 100);
	/* Check to make sure malloc actually gave you memory */
        if(!Lines) return NULL;
	...
	/* Do read and other stuff here */
	...
	return Lines;

Open in new window

You left out the code bits for the creation of your buffer for read(), so not sure if you properly allocated and freed that memory or not.  

Last, the main function is not correct.  The correct definition for main is:
int main(int argc, char *argv[]) {}

Open in new window

argc is the count of arguments and arvg is the array of strings for those arguments.  argv[0] should always be the name of the program being run.  arvg[0] matters more if you have a do everything piece of code and you need to know how you were called so you react as expected - BusyBox is an example of this.  It would appear you're looking for a single argument on the command line, so you'll want to look in argv[1] for that:
int main(int argc, char *argv[]) {
	...
	/* Remember to check existence of a var before trying to use it */
	if(argc < 2)
	{
		/* Return a non-zero value to the OS to indicate a problem */
		printf("Insufficient Arguments\n");
		return(1); 
	}
	char * configString = readConfig(argv[1]);
	...
	/* Remember to free your allocated memory - most OSs will handle 
	    this on exit for you, but it's a good practice to do it yourself. */
	return( 0 );
}

Open in new window

C does allow using void for main, but if you're going to the effort of writing code in C vs a managed language like Java, C#, etc, you should probably be returning a value to indicate success or failure on your code...  Just my $0.02 worth...
0

Experts Exchange Solution brought to you by

Your issues matter to us.

Facing a tech roadblock? Get the help and guidance you need from experienced professionals who care. Ask your question anytime, anywhere, with no hassle.

Start your 7-day free trial
phoffricCommented:
>> char *ch;
You can name a variable to almost whatever you want, but I have always seen "ch" to refer to a char type and never to a pointer type. Keeping with meaningful names will help others read your code faster (and even yourself after not looking at it for weeks).
0
pzozulkaAuthor Commented:
I'm still having a minor problem.

I made the recommended changes, and now getting the below error for the line in main(): char *configString = ...
warning: initialization from incompatible pointer type

Let me know if I need to create a new question for this. Thanks much.

#include ...
...
#include <malloc.h>

char ** foo(const char *config) {

        char **Lines;
	...
	Lines = malloc( sizeof(char*) * 100);
	/* Check to make sure malloc actually gave you memory */
        if(!Lines) return NULL;
	...
	/* Do read and other stuff here */
	...
	return Lines;
}

int main(int argc, char *argv[]) {
        ...
        char * configString = readConfig(argv[1]);
        ...
}

Open in new window

0
Increase Security & Decrease Risk with NSPM Tools

Analyst firm, Enterprise Management Associates (EMA) reveals significant benefits to enterprises when using Network Security Policy Management (NSPM) solutions, while organizations without, experienced issues including non standard security policies and failed cloud migrations

evilrixSenior Software Engineer (Avast)Commented:
What is the return type of readConfig? I don't see this defined anywhere in either your last post or any other code sample posted by anyone.
0
pzozulkaAuthor Commented:
sorry, readConfig is the actual code in my program. I'm using foo as an example here, so it should be:

int main(int argc, char *argv[]) {
        ...
        char * configString = foo(argv[1]);
        ...
}

Open in new window

0
gt2847cSr. Security ConsultantCommented:
foo() as originally defined is returning an array of 100 char pointers, so
int main(int argc, char * arvg[]) {
	...
	char ** configString = foo(argv[1]);
	...
}

Open in new window

You can then treat configString as an array of char * (string).

	...
	printf( configString[0] );
	...

Open in new window

This would print the first string.  Based on your original code, the array elements of configString[] would be the buffer memory returned from your read() calls.  Left out for brevity, but you'd obviously want to check that your array returned from foo() actually had allocated elements before using it.  Otherwise, it could be an uninitialized pointer and be anywhere in memory.
0
pzozulkaAuthor Commented:
Is this also allowed:
char * configString = (char *) foo(argv[1]);

Open in new window

0
gt2847cSr. Security ConsultantCommented:
The compiler probably won't complain, but you probably won't get what you're expecting...  If you recast the return value of foo() to a character pointer, you will be telling the compiler to treat the memory block holding the array as a string.  The contents of configString would be whatever the contents of the block of memory holding the array translates to as charaters.

Normally, the return value of foo properly handled is like this:

char **lines = foo( argv[0] );

Where lines is a memory address of a block of memory sized to hold 100 char *.  Each element of that array is (assuming it was properly initialized) a pointer to a block of memory holding the character string returned from the read() call.  I attached a picture that should give you a rough idea of what it looks like.
pointers.png
0
evilrixSenior Software Engineer (Avast)Commented:
Does "lines" really actually need to be a discrete array of lines or (as your original code would suggest) do you just need a text file read into memory? If it's the latter you can do this in a far simpler way! Something like this (untested, just a quick example):

char * foo(const char *config)
{
        int N = 100;
	int size = N;
	char *Lines = malloc(size * sizeof(char));
	char * ins = Lines;
        int cnt = 0;

        // read in N bytes at a time (block reads are more efficient)
	while((cnt = read(fd, itr, N)) == N)
        {
              // grow buffer
              size += N;
              Lines = realloc(Lines, size * sizeof(char));

              // set new insert point
              ins += N;
        }

        if(cnt < 0)
        {
             /* there was an error */
             free (Lines);
             return NULL;
        }
	
	return Lines
}

// main functions have to return int [the ANSI Standard says so!]
int main(char *conf) {
	...
	char * configString = readConfig(conf);
        if(!configString) { return -1; }
	...
        free (configString );
        return 0;
}

Open in new window

0
gt2847cSr. Security ConsultantCommented:
Several points to your code and comments evilrix...

Just reading between the lines with the comments that pzozulka made, it would appear that the intent is to read configuration items from a file.  That is just a guess, but if it is correct then gulping down the file into a chunk of memory isn't going to help much.  Additionally, depending on the size of the file, all those reallocs may make a mess out of the heap.  If you are just wanting to grab the whole file, you'd be better off making a file system call to determine the size of the file and allocate a buffer based on that.

Onto the code...

The while loop will effectively ignore the last bits of the file if the last read doesn't contain exactly 'N' bytes.  Also, the rest of the while loop would need to be adjusted to account for cnt being less than N but greater than zero.

Your code comment about block reads and efficiency is mostly correct but incomplete.  In order for it to be more efficient, the read size should be a multiple of the block size of the device you're reading from.  A fraction of the block size will also work especially if the device driver (or device itself) caches.  If you randomly select a "block read size" that is not a multiple or proper fraction of the block size, you're not going to gain anything and might degrade performance.

The main function declaration as written
int main(char * conf) {

Open in new window

isn't correct.  Permissible variations (defined by standards) are:
void main() {...}
void main( void ) {...}

int main() {...}
int main( void ) {...}

int main( int argc, char **argv ) {...}
void main( int argc, char **argv ) {...}

int main( int argc, char *argv[] ) {...}
void main( int argc, char *argv[] ) {...}

Open in new window

There are several variations I've heard of for different OS platforms that include char *env[] as the third argument and I believe Mac OS X has a fourth argument (no idea what for, I don't hack Mac code).
0
evilrixSenior Software Engineer (Avast)Commented:
@gt2847c

>> Several points to your code and comments evilrix...
Well, I feel honoured that a non-SME in the C Programming zone, with a total of 179,527 points, hardly any of which have been earned in the C Programming zone, would like to instruct the 5th highest ranked C Programming expert on how to improve his quick and dirty, completely untested (and noted as such), by way of a quick example code snippet!

Wait... let me get comfortable and then we can begin!

>> gulping down the file into a chunk of memory isn't going to help much.
Ah yes, misplaced opinion. Ok... that really depends on what it is the OP plans to do with the file once it has been consumed, doesn't it? I have no strong feeling one way or the other, just observing that this *might* be a simpler method. You'll note I waited until *after* the question had been resolved before doing so... to ensure I didn't detract from the focus of the original question nor introduce additional things that might just confuse the asker when they are already confused enough.

You'll also note that I clearly posed the question about whether this may or may not be preferable and I'd say that it would be - just that it might be! Ultimately, that's really up to the asker... not you... to make that decision. For all we know they hadn't even considered this as a possible way to do this - after all, as experts we are expected to know more than the asker and, sometimes, that means we can observe a different solution than what they are trying to do... because they just hadn't thought of it.

>> depending on the size of the file, all those reallocs may make a mess out of the heap.

Unless you live in 1980 and have a computer with a memory size of a few MB this is so just not true! We're talking about a file with a 100 or so lines... but even if we weren't do you actually know how realloc works? Let's review...

When you originally allocate memory from the free store the heap allocator will choose from a number of different strategies to decide which free block to give you. Generally, the size of the free block will be greater than what you need. When you realloc all that happens is your quota in the current free block is just extended (note, this isn't a guarantee but more often than not this is what happens). In these cases the pointer returned by realloc is the same as the pointer it was originally given to reallocate.

http://www.cplusplus.com/reference/cstdlib/realloc/

"Return Value
A pointer to the reallocated memory block, which may be either the same as ptr or a new location."

Unless the existing free block is exceeded, this does not actually require any further "heap allocation" and so results in no fragmentation. Only once you exceed the size of the current free block does a new allocation happen. Of course, this can only happen if the memory requested is contiguous - and it will be in the model I proposed, unlike allocation a bunch of lines from the heap. This will almost certainly require a new allocation each time!

Put another way, how to you propose to allocate memory for each of the 100 lines if it's not from the heap? You've created an dynamic array of char pointers. To read in the file each line will need to be allocated memory. Either way, you'll need to do additional allocations.

>> If you are just wanting to grab the whole file, you'd be better off making a file system call to determine the size of the file and allocate a buffer based on that.

Wonderful, except it's a text file and so there is no way of figure it out the files size in a cross-platform way. Trying to get the size of a text file, unless opened in binary mode, will result in undefined behaviour. If you open it in binary mode you'll need to do additional work to take case of the difference in line endings between platforms when trying to figure out the size of the file.

http://www.cplusplus.com/reference/cstdio/ftell/?kw=ftell
"For text streams, the numerical value may not be meaningful"

American National Standard for Information Systems Programming Language C88/89:
The ftell function obtains the current value of the file position
indicator for the stream pointed to by stream .  For a binary stream,
the value is the number of characters from the beginning of the file.
For a text stream, its file position indicator contains unspecified
information
, usable by the fseek function for returning the file
position indicator for the stream to its position at the time of the
ftell call; the difference between two such return values is not
necessarily a meaningful measure of the number of characters written
or read.

It may be possible to use platform specific functions to get this information but given that this zone is the C Programming zone and not the [insert your favourite platform here] Programming zone that would be a little presumptuous and, largely, off-topic!

Ok, you could use this as a rough method to limit memory reallocations but since we're talking about a config file of approx. 100 lines this is a perfect example of premature optimisation (being the root of all evil). Keep it simple, add code complexity if and when needed. I assure you that in this case the extra complexity is absolutely not needed!

>> Onto the code...
You mean the code that I made perfectly clear was untested and posted just as a quick hack example? The code that is clearly the ops original code that I have just very quickly hacked in the browser window to provide an idea - one that clearly worked since you seem to have got the gist of where I was going with this!

>> The while loop will effectively ignore the last bits of the file
Yes, that is true but note I made it perfectly clear that  this code is "(untested, just a quick example". It was not meant to be line for line perfect... the fact that it's not even valid C doe must be the biggest clue to this, no?

>> Your code comment about block reads and efficiency is mostly correct but incomplete.
Again, it was a quick observation, not a dissertation! You might want to try reading between the lines!

>> The main function declaration as written
>> int main(char * conf) {
>> isn't correct.

Yes, that is true... and was a slip of the keyboard; however, I'll give you that. If anything, of all the things you decided needed pointing out this is the only valid point because that line is just plan wrong (although, again, I am at pains to point out this was a typo!).

However,...
 
>> Permissible variations (defined by standards) are:
>> void main() {...}

WRONG

>> void main( void ) {...}

WRONG

>> int main() {...}

WRONG

>> int main( void ) {...}

WRONG

>> int main( int argc, char **argv ) {...}

CORRECT

>> void main( int argc, char **argv ) {...}

WRONG

>> int main( int argc, char *argv[] ) {...}

CORRECT

>> void main( int argc, char *argv[] ) {...}

WRONG

The ANSI C Standard prescribes only two possible variations of main and any other variation is, at best, non-portable and, at worse, going to blow up in your face!

To clarify, the ONLY formats main is allowed to take are:

int main(int argv, char * argv[])
int main(void)

Note, that int main(int argv, char ** argv) is just another way of writing int main(int argv, char * argv[]) and, as such, does not constitute a third alternative.

The main problem here is that if the return type is anything other than int and the runtime framework tries to pop the stack to get the return value of main (which is often used as the exit code for an application) it will result in undefined behaviour, which could either manifest as absolutely nothing or, worse case, your application crashing inexplicably.

But, please don't take my word for it!

American National Standard for Information Systems Programming Language C88/89:


"Program startup"

   The function called at program startup is named main .  The
implementation declares no prototype for this function.  It can be
defined with no parameters:

         int main(void) { /*...*/ }

or with two parameters (referred to here as argc and argv , though any
names may be used, as they are local to the function in which they are
declared):

         int main(int argc, char *argv[]) { /*...*/ }


You might also want to read: http://users.aber.ac.uk/auj/voidmain.cgi

gt2847c, I'm all for being critical of other's posts; however, I would urge you to bear in mind the following:

1. No one likes a smart arse
2. If you are going to be critical make sure you observe the context of the original post
3. Be sure your critique is actually valid and correct
4. Don't make authoritative statements from the standards documents that are wrong!
5. Be sure that your own posts are valid and correct before wasting time with others

It was absolutely clear that my code post was a quick and dirty example, that was untested and hacked together from the original post by the asker. I made this perfectly clear because it was only meant to observe there is another way to do this - it was not meant to be a ceramic example of doing so. That was pretty clear and obvious - so why you felt the need to post your follow up comment is, frankly, beyond me.

I came across this thread when it was first posted but saw both you and Paul were on it so I decided to skip it (yes, unlike most experts I don't go dumping into a thread that other experts are working on). I only posted my final observation once the question had been closed you you'd got your points. Seems that my courtesy was not appreciated so I'll be sure to, in future, avoid the provision of such courtesy. I'll also be more than happy to examine with great care, each and every code post you make, and point out the flaws as I see fit. I'm sure that both you and the asker will appreciate that!

By all means point out an obvious problem in someone's post if it exists but be mindful of the fact that the other expert may, just may, know a little more than what you do about the subject matter at hand and observe that (a) you might actually be wrong and (b) even if you're not there is a good chance the error or omission was nothing more than a typo.

You'll note that Paul made an observation about your use of "ch" as a variable name. Now, as it happens I'd don't actually agree with his observation but note that the tone of his suggestion was helpful and did not appear to suggest you had no idea what you are doing! Frankly, there are a number of problems with a lot of what you've posted here but for the most part none of the issues are going to cause the asker a problem so I've chosen to ignore them. I'd be very happy to rip your comments to pieces; however, if you feel that you might learn something from that!

@pzozulka, apologies for my little mini rant, but I get very put out by so-called experts who feel it is necessary to be so condescending for absolutely no good reason (other than to make themselves feel better, I guess).

On a different note, if you'd like to know how to do what you are trying to do properly and safely please post back here with details of what you're actually trying to achieve (because, up until now it appears we've been guessing) and I'll be only too please to provide you a fully tested standards compliant solution that will be both efficient (in terms of time and space complexity) an will actually do what you want it to do in a simple a way as possible.

Note: this post almost certainly contains typo's, errors and omissions because (a) I am only human, (b) it is first think in the morning and I am tired, (c) I didn't run it through a spelling or grammar checker and (d) because I wanted to give the other experts something to do in the guise of wading through my long post and finding fault with it. Enjoy!

-Rx.
0
gt2847cSr. Security ConsultantCommented:
Evilrix - I'm sorry you feel the way you do.  It was not my intent to start (or continue) a rant.  I was attempting to help what appeared to be a newer C programmer with their difficulty.  Given the repeated misunderstanding the person was having with the previous code examples, I was trying to point out items to the user so that they didn't come back with questions as to why it did not work as expected.

I was not, in any way, attempting to be a SA as you put it.  I'm sorry if you judged it that way.  I was trying to help.  I make mistakes as do all who work in computers.  I was attempting to point out details for the user's consumption so that they might understand what we were trying to explain.  The comments were not intended to be a personal criticism, however it seems you have chosen to take it in that respect.  Additionally, I do not provide assistance to those at EE for the points, for freebies, or anything else (you may look to see that I have not asked questions here or asked for anything at all really).  I attempt to assist others because in the past, others have done so for me.  I am simply paying it forward.  My goal here is not to be a top scorer or best anyone else...

I shall leave it at that.  

I apologize to the user as we have distracted you from the question at hand.
0
evilrixSenior Software Engineer (Avast)Commented:
Ok. Fair enough.

Your post seemed to be to the contrary but I am not in the habit of holding a grudge and life is too short for me to keep ranting :)

That being the case, it's forgotten and we're all good.

Thanks for getting back to me.

All the best.

-Rx.
0
gt2847cSr. Security ConsultantCommented:
Thank you for your understanding.  As such, I shall endeavour to be more mindful of my writing so as to not appear as being condescending or otherwise being unpleasant.  Whether motives are intended or not, people form opinions based on what they are able to see, which in this case are the responses to posts rather than personal interactions.  So I shall try harder to read my posts from an objective perspective rather than inferring "what I meant to say".
0
pzozulkaAuthor Commented:
Thanks to all of you for your help. I'm still struggling a bit.


Below is the actual foo() method I was referring to called fetchHTML(). Every time I call it, it seems to do everything correctly according to the logs, but the main calling method -- communicate() -- gets nothing back, and nothing in it's logs.


char ** fetchHTML(char *uri, char *host) {

	FILE *fpHTML=NULL;
	// Open a log file in write mode.
	fpHTML = fopen ("/home/users1/pz951772/html_log.txt", "w+");
	fprintf(fpHTML, "Logging info...\n");
	
	struct hostent *h;
	struct in_addr **addr_list;
	
	if((h = gethostbyname(host)) == NULL) {
		fprintf(fpHTML, "Couldn't get host by name.\n");
		fflush(fpHTML);
		return NULL;
	}
	else {
		fprintf(fpHTML, "gethostbyname() success.\n");
	}
	addr_list = (struct in_addr **)h->h_addr_list;
	
	const char *ipAddr = inet_ntoa(*addr_list[0]); //gethostbyname
	fprintf(fpHTML, "IP addr: %s\n", ipAddr);
	
	socklen_t peer_addr_size;

	int conStatus;
	const char *protocolName = "tcp";
	
	int sockfd;
	struct protoent *myProtocol = getprotobyname(protocolName);
	struct sockaddr_in sockIP4addr;
	struct sockaddr *peerSockAddr;

	int max_input = 4000;
	char **buffer;
	buffer = malloc( max_input * sizeof(char*));
	if(!buffer) {
		fprintf(fpHTML, "Couldn't malloc buffer.\n");
		fflush(fpHTML);
		return NULL;
	}
	else {
		fprintf(fpHTML, "malloc buffer success.\n");
	}
		
		
	size_t count = 4000;
	ssize_t bytes_read;
	size_t requestLen;

	sockIP4addr.sin_family = AF_INET;
	sockIP4addr.sin_port = htons(80);
	// store IP addr in sockIP4addr structure
	inet_pton(AF_INET, ipAddr, &sockIP4addr.sin_addr);

	sockfd = socket(AF_INET,SOCK_STREAM,myProtocol->p_proto);

	if(sockfd == -1)
		fprintf(fpHTML, "socket() error: %s\n", strerror(errno));
	else {
		fprintf(fpHTML, "socket() success.\n");
	}
	
	peer_addr_size = sizeof(struct sockaddr_in);
	peerSockAddr = (struct sockaddr*)&sockIP4addr;

	// Connect
	conStatus = connect(sockfd, peerSockAddr, peer_addr_size);

	if(conStatus == -1)
	  fprintf(fpHTML, "connect() error: %s\n", strerror(errno));
	else {
		fprintf(fpHTML, "connect() success.\n");
	}
	  
	char *strRequest;
	int myB;
	if((myB = sprintf(strRequest, "GET %s HTTP/1.1\r\nHOST: %s\r\n\r\n",uri,host)) < 0)
		fprintf(fpHTML, "sprintf() error: %s\n", strerror(errno));
	else {
		fprintf(fpHTML, "sprintf() success.\n");
	}
	fflush(fpHTML);
	
	requestLen = strlen(strRequest);
	
	fprintf(fpHTML, "%s\n", strRequest);
	
	if((write(sockfd, strRequest, requestLen)) < 0)
		fprintf(fpHTML, "write() error: %s\n", strerror(errno));
	else {
		fprintf(fpHTML, "write() success.\n");
	}
	
	if(( bytes_read = read(sockfd, buffer, count)) == -1) {
		fprintf(fpHTML, "read() error: %s\n", strerror(errno));
	}
	else {
	  buffer[bytes_read]='\0';
	  fprintf(fpHTML, "read() success.\n");
	  fprintf(fpHTML, "%s\n", buffer);
	}
	
	fflush(fpHTML);
	return buffer;

}

Open in new window


The method that calls fetchHTML() method on line 156 below, called readConfig().
int readConfig(const char *config, char *host) {
   
   FILE *configfp=NULL;
   
   struct stat st;
   ssize_t bytes_read;
   size_t count = 400;
   char *buffer;
   char *ch;
   char *Lines[100];
   char LastChar;
   int fd;
   int LineCount;
   off_t size;
   
   stat(config, &st);
   size = st.st_size;
   
   buffer = (char *) malloc(size + 1);
   
   // Open a log config file in write mode.
   configfp = fopen ("/home/users1/pz951772/config_log.txt", "w+");
   fprintf(configfp, "Config Log...\n");

   // open config file
   if( (fd = open(config, O_RDWR)) == -1 ) {
		fprintf(configfp,"open() error: %s: %s\n",strerror(errno),config);
		fflush(configfp);
   }
   
   // read config file line by line
   if(( bytes_read = read(fd, buffer, size)) == -1) {
		fprintf(configfp, "read() error: %s\n", strerror(errno));
		fflush(configfp);
   }
   else if( bytes_read == 0) {   /* EOF */
		fprintf(configfp, "zero bytes read.\n");
		fflush(configfp);
   }
   else {
		
		// with \r\n in each end of line in buffer, file is one long string
		// make all lines end with 0, and make them behave as strings
		buffer[size] = '\0';
		
		for(ch = buffer; *ch; ++ch) {
			if(*ch == '\r' || *ch == '\n')
				*ch = '\0';
		}
		
		// build array of pointers, pointing to each line in config
		for(LineCount=0,LastChar='\0',ch=buffer;ch-buffer < size; ++ch){
			if(LastChar == '\0') {
				if(*ch) // ignore consecutive zero characters
					Lines[LineCount++] = ch;
			}
			LastChar = *ch;
		}		
		close(fd);
		
		// parse host to strip http:// and any dirs
		// *************************************
		char src[50];
		char dest[100];
		char *httpProto = "http://";
		
		if((strstr(host, httpProto)) != NULL) { //does host contain http://
			memset(dest, '\0', sizeof(dest));
			strcpy(dest, host+strlen(httpProto));
			fprintf(configfp,"Stripped HTTP:\n%s\n",dest);
			// http:// stripped -- use dest
		}
		// *************************************
		
		// copy chars off to the hostName until you find "/" or '\0'
		// *************************************
		char request[50];
		char hostName[100];
		int a = 0, b = 0, forwardSlash = 0;
		
		// **********USE DEST************
		
		if((strstr(host, httpProto)) != NULL) { // host contains http://
			for(a=0; dest[a] != '\0'; a++) { //copy domain name into hostName
				if(dest[a] != '/') {
					hostName[a] = dest[a];
				}
				else { // forward slash found
					forwardSlash = 1;
					break;
				}
			}
			if(dest[a] == '\0')
				hostName[a] = '\0'; 
			
			while( (forwardSlash == 1) && (dest[a] != '\0')) { 
				request[b] = dest[a];
				a++;
				b++;
			}
			if( (forwardSlash == 1) && (dest[a] == '\0'))
				request[b] = '\0';
		}
		// **********USE HOST***************
		
		else { // host didn't contain http:// to begin with
			for(a=0; host[a] != '\0'; a++) { //copy domain name into hostName
				if(host[a] != '/') {
					hostName[a] = host[a];
				}
				else { // forward slash found
					forwardSlash = 1;
					break;
				}
			}
			if(host[a] == '\0')
				hostName[a] = '\0';
				
			while( (forwardSlash == 1) && (host[a] != '\0')) { 
				request[b] = host[a];
				a++;
				b++;
			}
			if( (forwardSlash == 1) && (host[a] == '\0'))
				request[b] = '\0';
		}
		
		if(forwardSlash == 0) {
			request[0] = '/';
			request[1] = '\0';
		}
		
		fprintf(configfp,"Host:\n%s\n",host);
		fprintf(configfp,"Domain:\n%s\n",hostName);
		fprintf(configfp,"Request: \n%s\n",request);
		// *************************************
		int i,j,k,m=0;
		char *t1;
		char **html;
		for(i=0; i < LineCount; i++) { // for each line, 
									   // check if hostName in config file
									   // and fetch HTML
									   
			if(*Lines[i] != '#') { // ignore comment lines

				fprintf(configfp,"%s\n",Lines[i]);
				fprintf(configfp,"%s\n",host);
				char *p;
				//if( p[19] != '~' )
				if((p = strstr(Lines[i], host)) != NULL) {
					// found host in config file
					fprintf(configfp, "R:%c %d\n",p[19],p[19]);
					
					if(strstr(Lines[i], "redirected") != NULL) {
					// ***************************************
						if((html=fetchHTML(request, hostName)) != NULL)
							fprintf(configfp,"Redirected:\n%s\n\n",html);
						else
							fprintf(configfp,"fetchHTML() error.\n");
						fflush(configfp);
						return 1;
					}
					
					else if(strstr(Lines[i], "filtered") != NULL) {
					// ***************************************
						fflush(configfp);
						return 2;
					}
					
					else if(strstr(Lines[i], "cached") != NULL) {
					// ***************************************
						if((html=fetchHTML(request, hostName)) != NULL)
							fprintf(configfp,"Cached:\n%s\n\n",html);
						else
							fprintf(configfp,"fetchHTML() error.\n");
						fflush(configfp);
						return 3;
					}
				}
				else { // couldn't find host in config file
					m = -1;
				}
			}
		}
		// ***************************************
		if((html=fetchHTML(request, hostName)) != NULL)
			fprintf(configfp,"Not in config:\n%s\n\n",html);
		else
			fprintf(configfp,"fetchHTML() error.\n");
			
		fflush(configfp);
		fclose(configfp);
		
		return m;
   }
}

Open in new window


Below is the main method, making the call to the readConfig() method on line 11:
void communicate(const char *port, const char *conf) {
	int max_input = 200;
	char buffer[max_input];
	
	if(( bytes_read = read(csockfd, buffer, count)) == -1)
			fprintf(fp, "read() error %s\n", strerror(errno));
	else {
		buffer[bytes_read]='\0';
		int hostExist;
		
		hostExist = readConfig(conf, buffer);
		
		if( hostExist == 1)
			reply = "host redirected\n";
		else if( hostExist == 2)
			reply = "host filtered\n";
		else if( hostExist == 3)
			reply = "host cached\n";
		else if( hostExist == -1)
			reply = "host does not exist\n";
		fprintf(fp, "%s\n", reply);
	}
}

Open in new window

0
pzozulkaAuthor Commented:
Below is my entire C file. I think the problem is happening at line 407 when readConfig() is called.

Below the code is the log created inside the readConfig() method. It seems to do everything correctly.

#include <stdlib.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <sys/stat.h>
#include <dirent.h>
#include <fcntl.h>
#include <string.h>
#include <stdio.h>
#include <pwd.h>
#include <grp.h>
#include <netdb.h>
#include <netinet/in.h>
#include <netinet/ip.h>
#include <arpa/inet.h>
#include <syslog.h>
#include <errno.h>
#include <malloc.h>

#define DAEMON_NAME "vdaemon"

char ** fetchHTML(char *uri, char *host) {

	FILE *fpHTML=NULL;
	// Open a log file in write mode.
	fpHTML = fopen ("/home/users1/pz951772/html_log.txt", "w+");
	fprintf(fpHTML, "Logging info...\n");
	
	struct hostent *h;
	struct in_addr **addr_list;
	
	if((h = gethostbyname(host)) == NULL) {
		fprintf(fpHTML, "Couldn't get host by name.\n");
		fflush(fpHTML);
		return NULL;
	}
	else {
		fprintf(fpHTML, "gethostbyname() success.\n");
	}
	addr_list = (struct in_addr **)h->h_addr_list;
	
	const char *ipAddr = inet_ntoa(*addr_list[0]); //gethostbyname
	fprintf(fpHTML, "IP addr: %s\n", ipAddr);
	
	socklen_t peer_addr_size;

	int conStatus;
	const char *protocolName = "tcp";
	
	int sockfd;
	struct protoent *myProtocol = getprotobyname(protocolName);
	struct sockaddr_in sockIP4addr;
	struct sockaddr *peerSockAddr;

	int max_input = 4000;
	char **buffer;
	buffer = malloc( max_input * sizeof(char*));
	if(!buffer) {
		fprintf(fpHTML, "Couldn't malloc buffer.\n");
		fflush(fpHTML);
		return NULL;
	}
	else {
		fprintf(fpHTML, "malloc buffer success.\n");
	}
		
		
	size_t count = 4000;
	ssize_t bytes_read;
	size_t requestLen;

	sockIP4addr.sin_family = AF_INET;
	sockIP4addr.sin_port = htons(80);
	// store IP addr in sockIP4addr structure
	inet_pton(AF_INET, ipAddr, &sockIP4addr.sin_addr);

	sockfd = socket(AF_INET,SOCK_STREAM,myProtocol->p_proto);

	if(sockfd == -1)
		fprintf(fpHTML, "socket() error: %s\n", strerror(errno));
	else {
		fprintf(fpHTML, "socket() success.\n");
	}
	
	peer_addr_size = sizeof(struct sockaddr_in);
	peerSockAddr = (struct sockaddr*)&sockIP4addr;

	// Connect
	conStatus = connect(sockfd, peerSockAddr, peer_addr_size);

	if(conStatus == -1)
	  fprintf(fpHTML, "connect() error: %s\n", strerror(errno));
	else {
		fprintf(fpHTML, "connect() success.\n");
	}
	  
	char *strRequest;
	int myB;
	if((myB = sprintf(strRequest, "GET %s HTTP/1.1\r\nHOST: %s\r\n\r\n",uri,host)) < 0)
		fprintf(fpHTML, "sprintf() error: %s\n", strerror(errno));
	else {
		fprintf(fpHTML, "sprintf() success.\n");
	}
	fflush(fpHTML);
	
	requestLen = strlen(strRequest);
	
	fprintf(fpHTML, "%s\n", strRequest);
	
	if((write(sockfd, strRequest, requestLen)) < 0)
		fprintf(fpHTML, "write() error: %s\n", strerror(errno));
	else {
		fprintf(fpHTML, "write() success.\n");
	}
	
	if(( bytes_read = read(sockfd, buffer, count)) == -1) {
		fprintf(fpHTML, "read() error: %s\n", strerror(errno));
	}
	else {
	  buffer[bytes_read]='\0';
	  fprintf(fpHTML, "read() success.\n");
	  fprintf(fpHTML, "%s\n", buffer);
	}
	
	fflush(fpHTML);
	return buffer;

}

int readConfig(const char *config, char *host) {
   
   FILE *configfp=NULL;
   
   struct stat st;
   ssize_t bytes_read;
   size_t count = 400;
   char *buffer;
   char *ch;
   char *Lines[100];
   char LastChar;
   int fd;
   int LineCount;
   off_t size;
   
   stat(config, &st);
   size = st.st_size;
   
   buffer = (char *) malloc(size + 1);
   
   // Open a log config file in write mode.
   configfp = fopen ("/home/users1/pz951772/config_log.txt", "w+");
   fprintf(configfp, "Config Log...\n");

   // open config file
   if( (fd = open(config, O_RDWR)) == -1 ) {
		fprintf(configfp,"open() error: %s: %s\n",strerror(errno),config);
		fflush(configfp);
   }
   
   // read config file line by line
   if(( bytes_read = read(fd, buffer, size)) == -1) {
		fprintf(configfp, "read() error: %s\n", strerror(errno));
		fflush(configfp);
   }
   else if( bytes_read == 0) {   /* EOF */
		fprintf(configfp, "zero bytes read.\n");
		fflush(configfp);
   }
   else {
		
		// with \r\n in each end of line in buffer, file is one long string
		// make all lines end with 0, and make them behave as strings
		buffer[size] = '\0';
		
		for(ch = buffer; *ch; ++ch) {
			if(*ch == '\r' || *ch == '\n')
				*ch = '\0';
		}
		
		// build array of pointers, pointing to each line in config
		for(LineCount=0,LastChar='\0',ch=buffer;ch-buffer < size; ++ch){
			if(LastChar == '\0') {
				if(*ch) // ignore consecutive zero characters
					Lines[LineCount++] = ch;
			}
			LastChar = *ch;
		}		
		close(fd);
		
		// parse host to strip http:// and any dirs
		// *************************************
		char src[50];
		char dest[100];
		char *httpProto = "http://";
		
		if((strstr(host, httpProto)) != NULL) { //does host contain http://
			memset(dest, '\0', sizeof(dest));
			strcpy(dest, host+strlen(httpProto));
			fprintf(configfp,"Stripped HTTP:\n%s\n",dest);
			// http:// stripped -- use dest
		}
		// *************************************
		
		// copy chars off to the hostName until you find "/" or '\0'
		// *************************************
		char request[50];
		char hostName[100];
		int a = 0, b = 0, forwardSlash = 0;
		
		// **********USE DEST************
		
		if((strstr(host, httpProto)) != NULL) { // host contains http://
			for(a=0; dest[a] != '\0'; a++) { //copy domain name into hostName
				if(dest[a] != '/') {
					hostName[a] = dest[a];
				}
				else { // forward slash found
					forwardSlash = 1;
					break;
				}
			}
			if(dest[a] == '\0')
				hostName[a] = '\0'; 
			
			while( (forwardSlash == 1) && (dest[a] != '\0')) { 
				request[b] = dest[a];
				a++;
				b++;
			}
			if( (forwardSlash == 1) && (dest[a] == '\0'))
				request[b] = '\0';
		}
		// **********USE HOST***************
		
		else { // host didn't contain http:// to begin with
			for(a=0; host[a] != '\0'; a++) { //copy domain name into hostName
				if(host[a] != '/') {
					hostName[a] = host[a];
				}
				else { // forward slash found
					forwardSlash = 1;
					break;
				}
			}
			if(host[a] == '\0')
				hostName[a] = '\0';
				
			while( (forwardSlash == 1) && (host[a] != '\0')) { 
				request[b] = host[a];
				a++;
				b++;
			}
			if( (forwardSlash == 1) && (host[a] == '\0'))
				request[b] = '\0';
		}
		
		if(forwardSlash == 0) {
			request[0] = '/';
			request[1] = '\0';
		}
		
		fprintf(configfp,"Host:\n%s\n",host);
		fprintf(configfp,"Domain:\n%s\n",hostName);
		fprintf(configfp,"Request: \n%s\n",request);
		// *************************************
		int i,j,k,m=0;
		char *t1;
		char **html;
		for(i=0; i < LineCount; i++) { // for each line, 
									   // check if hostName in config file
									   // and fetch HTML
									   
			if(*Lines[i] != '#') { // ignore comment lines

				fprintf(configfp,"%s\n",Lines[i]);
				fprintf(configfp,"%s\n",host);
				char *p;
				//if( p[19] != '~' )
				if((p = strstr(Lines[i], host)) != NULL) {
					// found host in config file
					fprintf(configfp, "R:%c %d\n",p[19],p[19]);
					
					if(strstr(Lines[i], "redirected") != NULL) {
					// ***************************************
						if((html=fetchHTML(request, hostName)) != NULL)
							fprintf(configfp,"Redirected:\n%s\n\n",html);
						else
							fprintf(configfp,"fetchHTML() error.\n");
						
						fprintf(configfp, "\nReidrected: Return 1\n");
						fflush(configfp);
						return 1;
					}
					
					else if(strstr(Lines[i], "filtered") != NULL) {
					// ***************************************
						fflush(configfp);
						return 2;
					}
					
					else if(strstr(Lines[i], "cached") != NULL) {
					// ***************************************
						if((html=fetchHTML(request, hostName)) != NULL)
							fprintf(configfp,"Cached:\n%s\n\n",html);
						else
							fprintf(configfp,"fetchHTML() error.\n");
						fflush(configfp);
						return 3;
					}
				}
				else { // couldn't find host in config file
					m = -1;
				}
			}
		}
		// ***************************************
		if((html=fetchHTML(request, hostName)) != NULL)
			fprintf(configfp,"Not in config:\n%s\n\n",html);
		else
			fprintf(configfp,"fetchHTML() error.\n");
			
		fflush(configfp);
		fclose(configfp);
		
		return m;
   }
}

void communicate(const char *port, const char *conf) {
   FILE *fp=NULL;
   socklen_t peer_addr_size;

   const char *protocolName = "tcp";
   const char *ipAddr = "127.0.0.1";
   int portNo = atoi(port); //argc = 5
   int sockfd, csockfd, bindStatus, listenStatus, conStatus;
   int mybacklog = 50;
   int my_inet_addr_len = 16;
   struct protoent *myProtocol = getprotobyname(protocolName);
   struct sockaddr_in sockIP4addr, peerIP4addr;
   struct sockaddr *mySockAddr, *peerSockAddr;
   
   int max_input = 200;
   char buffer[max_input];
   size_t count = 200;
   ssize_t bytes_read;
   size_t blen;
   char peerIPstr[INET_ADDRSTRLEN];
   
   // Open a log file in write mode.
   fp = fopen ("/home/users1/pz951772/log.txt", "w+");
   fprintf(fp, "Logging info...\n");
   fflush(fp);
   
   sockIP4addr.sin_family = AF_INET;
   sockIP4addr.sin_port = htons(portNo);
   inet_pton(AF_INET, ipAddr, &sockIP4addr.sin_addr);

   // Create Socket
   sockfd = socket(AF_INET,SOCK_STREAM,myProtocol->p_proto);
   
   if(sockfd == -1)
      perror("socket() error");
   else
      fprintf(fp, "socket() success\n");
   
   mySockAddr = (struct sockaddr*)&sockIP4addr;
   
   // Bind Socket
   bindStatus = bind(sockfd, mySockAddr, sizeof(struct sockaddr_in));
   
   if(bindStatus == -1)
      fprintf(fp, "bind() error %s\n", strerror(errno));
   else
      fprintf(fp, "bind() success\n");
    
	// Listen
   if (listen(sockfd, mybacklog) == -1)
      fprintf(fp, "listen() error: %s\n", strerror(errno));
   else
      fprintf(fp, "listen() success\n");

   peer_addr_size = sizeof(struct sockaddr_in);
   peerSockAddr = (struct sockaddr*)&peerIP4addr;
   
   // Accept connections - peerSockAddr is filled in with RemoteAddr
   csockfd = accept(sockfd, peerSockAddr, &peer_addr_size);
   
   if(csockfd == -1)
      fprintf(fp, "accept() error %s\n", strerror(errno));
   else {
      fprintf(fp, "Accepted()\nPeer port: %d\nMy port: %d\n", ntohs(peerIP4addr.sin_port),ntohs(sockIP4addr.sin_port));
	  fprintf(fp, "Peer IP: %s\n", inet_ntop(AF_INET, &peerIP4addr.sin_addr, peerIPstr, INET_ADDRSTRLEN));
   }

   char *reply;
   do
   {
      fprintf(fp, "Logging info while...\n");

      if(( bytes_read = read(csockfd, buffer, count)) == -1)
			fprintf(fp, "read() error %s\n", strerror(errno));
	   else {
			buffer[bytes_read]='\0';
			int hostExist;
			
			if((hostExist = readConfig(conf, buffer) == -1)) {
				reply = "host does not exist\n";
			}
			else if( hostExist == 1) {
				reply = "host redirected\n";
			}
			else if( hostExist == 2) {
				reply = "host filtered\n";
			}
			else if( hostExist == 3) {
				reply = "host cached\n";
			}
				
			fprintf(fp, "%s\n", reply);
			fflush(fp);
       }
	  sleep(1);
	  blen = strlen(reply);
      if((write(csockfd, reply, blen)) < 0)
	     fprintf(fp, "write() error %s\n", strerror(errno));
	  else
	     fprintf(fp, "write() success: %s\n", reply);
		 
	  fflush(fp);
		 
   } while (0);
   
   fclose(fp);
   
}

int main( int argc, char *argv[] ) {

   //Set our Logging Mask and open the Log
   /*setlogmask(LOG_UPTO(LOG_NOTICE));
   openlog(DAEMON_NAME, LOG_CONS | LOG_NDELAY | LOG_PERROR | LOG_PID, LOG_USER);
   
   syslog(LOG_INFO, "Entering Daemon");
   */
   pid_t process_id = 0, sid = 0;
	  
   // Get Current Working Directory
   long sizeCWD;
   char *bufCWD;
   char *ptrCWD;
   
   sizeCWD = pathconf(".", _PC_PATH_MAX);
   
   if(( bufCWD = (char *)malloc((size_t)sizeCWD)) != NULL)
		ptrCWD = getcwd(bufCWD, (size_t)sizeCWD);

   // Specify absolute path for config file
   strcat(ptrCWD, "/");
   strcat(ptrCWD, argv[2]);

   // Create child process
   process_id = fork();
   
   // fork() failure
   if (process_id < 0)
   {
      printf("fork failed!\n");
	  // return exit status
	  exit(1);
   }
   
   // PARENT PROCESS -- KILL IT
   if (process_id > 0)
   {
      printf("process_id of child process %d \n", process_id);
	  // return success in exit status
	  exit(0);
   }
   
   //unmask the file mode
   umask(0);
   
   // set new session
   sid = setsid();
   if(sid < 0)
   {
      // Return failure
	  exit(1);
   }
   
   // Change the current working directory to root
   chdir("/");
   
   // Close stdin. stdout and stderr
   close(STDIN_FILENO);
   close(STDOUT_FILENO);
   close(STDERR_FILENO);
   

   
   //-------------------
   //Main Process
   //-------------------
   
   communicate(argv[4], ptrCWD);
   
   //Close the log
   //closelog();
   
}

Open in new window




Log from readConfig() method:

$ cat config_log.txt
Config Log...
Host:
www.csun.edu
Domain:
www.csun.edu
Request:
/
http://www.csun.edu/            redirected      130.166.238.195, redwing.csun.edu
www.csun.edu
R:r 114
Redirected:
HTTP/1.1 200 OK
Server: nginx
Content-Type: text/html; charset=utf-8
X-Drupal-Cache: HIT
Etag: "1382901259-1"
Content-Language: en
X-UA-Compatible: IE=Edge,chrome=1
X-Generator: Drupal 7 (http://drupal.org)
Link: <http://www.csun.edu/>; rel="canonical",<http://www.csun.edu/>; rel="shortlink"
Cache-Control: public, max-age=900
Expires: Sun, 19 Nov 1978 05:00:00 GMT
Vary: Cookie,Accept-Encoding
Last-Modified: Sun, 27 Oct 2013 19:14:19 GMT
X-AH-Environment: prod
Transfer-Encoding: chunked
Date: Sun, 27 Oct 2013 22:14:16 GMT
X-Varnish: 769592409 769580308
Age: 724
Via: 1.1 varnish
Connection: keep-alive
X-Cache: HIT
X-Cache-Hits: 448
Set-Cookie: BIGipServerSF_IT_ACQUIA=2948199222.20480.0000; path=/

008000
<!DOCTYPE html>
<!--[if IEMobile 7]><html class="iem7"  lang="en" dir="ltr"><![endif]-->
<!--[if lte IE 6]><html class="lt-ie9 lt-ie8 lt-ie7"  lang="en" dir="ltr"><![endif]-->
<!--[if (IE 7)&(!IEMobile)]><html class="lt-ie9 lt-ie8"  lang="en" dir="ltr"><![endif]-->
<!--[if IE 8]><html class="lt-ie9"  lang="en" dir="ltr"><![endif]-->
<!--[if (gte IE 9)|(gt IEMobile 7)]><!--><html  lang="en" dir="ltr" prefix="fb: http://www.facebook.com/2008/fbml content: http://purl.org/rss/1.0/modules/content/ dc: http://purl.org/dc/terms/ foaf: http://xmlns.com/foaf/0.1/ og: http://ogp.me/ns# rdfs: http://www.w3.org/2000/01/rdf-schema# sioc: http://rdfs.org/sioc/ns# sioct: http://rdfs.org/sioc/types# skos: http://www.w3.org/2004/02/skos/core# xsd: http://www.w3.org/2001/XMLSchema#"><


Reidrected: Return 1

Open in new window

0
pzozulkaAuthor Commented:
I'll just create a new question as this one kind of spiraled. Thanks all.
0
evilrixSenior Software Engineer (Avast)Commented:
>> I'll just create a new question as this one kind of spiraled

Heh. I see you have some experts helping you on this so I'll step back for the moment. I'll take a look a little later today and if you are no closer to reaching a working solution I'll see if I can add anything useful to the mix :)

Good luck.
0
pzozulkaAuthor Commented:
Thanks much. I'm very close to completion and it's due by midnight PST.
0
It's more than this solution.Get answers and train to solve all your tech problems - anytime, anywhere.Try it for free Edge Out The Competitionfor your dream job with proven skills and certifications.Get started today Stand Outas the employee with proven skills.Start learning today for free Move Your Career Forwardwith certification training in the latest technologies.Start your trial today
C

From novice to tech pro — start learning today.