Getaddrinfo issue in C - c

I'm working on a proxy application in C for a project. I have an issue with getaddrinfo() being unsuccessful when I pass it the parsed host name. If I hard code the host, for example "www.google.ca" it doesn't error but when given the URL (from a GET request that the code receives) it does produce an error (The exact error is "Unknown name or service"). I've tried debugging in NetBeans and as far as I can tell the parsed URL is no different from the one I've hard coded. Below is the code that I'm using:
Snippet of code that receives the request and tries to forward it:
...
//Message is received in the code before this
if (get_host(message, &url) == 0)
{
//Tries to open a socket to the parsed URL. This is where the issue happens
forawrd_fd = create_forward_socket(url, "80");
}
...
The get host function:
int get_host(char *request, char **host_url)
{
char url[BUFFER_SIZE];
if(sscanf(request, "%*s %s HTTP/1.1\r\n", url) != 1)
{
return -1;
}
else
{
int len = strlen(url);
//If there is a / at the end of the URL remove it
if(url[len-1] == '/')
{
printf("%c%c\n", url[len-2], url[len-1]);
url[len-1] = '\0';
printf("%s\n", url);
}
*host_url = &url;
//If the start of the string is http:// remove it
if(url[0] == 'h' && url[1] == 't' && url[2] == 't'&& url[3] == 'p')
{
*host_url += 7;
}
return 0;
}
}
Function that gets the file descriptor and makes the getaddrinfo
int create_forward_socket(char* url, const char* port)
{
//Status variable needed for some calls
int status, socket_fd, received_data;
//Setup address info structs
struct addrinfo hints;
struct addrinfo *result, *current;
//Initialize our hints.
memset(&hints, 0, sizeof hints);
//IPv4 or IPv6 we don't
hints.ai_family = AF_UNSPEC;
//We want a stream socket not datagram
hints.ai_socktype = SOCK_STREAM;
//Whatever this means (I forget but again the interwebs says...)
hints.ai_flags = AI_PASSIVE;
//Get a linked list of address info that we will choose from
if ((status = getaddrinfo(url, port, &hints, &result)) != 0) //Status here is -2 when called with the parsed URL
{
return -1;
}
for (current = result; current != NULL; current = current->ai_next)
{
if ((socket_fd = socket(current->ai_family, current->ai_socktype, current->ai_protocol)) != -1)
{
if (connect(socket_fd, current->ai_addr, current->ai_addrlen) != -1)
{
//We found a usable socket
break;
}
else
{
close(socket_fd);
}
}
}
if (current == NULL)
{
return -2;
}
else
{
return socket_fd;
}
}
Any help would be appreciated. If more of my code is needed please let me know. I included only what I thought was important so the post wasn't too long.

My guess is that you are returning a pointer to a local variable. See, url is a local variable, and the *host_url = url; line (I'm assuming that this is an output parameter) will return it to the caller. But local variables will be destroyed just when the function returns, and then, *host_url will point to the middle of nowhere.
The code calling get_host() is like:
char *host;
get_host(req, &host);
//call getaddrinfo with host
But host will not point to valid memory outside of get_host.
The solution is that the caller allocates the buffer:
int get_host(char *request, char *url)
{ ... }
And when you call it:
char host[BUFFER_SIZE];
get_host(req, host);
//call getaddrinfo with host

Related

Socket address showing 0.0.0.0, causing failure to send message

I've been working from Beejs Network examples, introducing a few customizations. In particular, I'm trying to use a single structure to store the necessary information related to communications/sockets. I think I'm having trouble populating an addrinfo structure and using it with sendto for a UDP socket. Bellow is my code, which compiles fine, but it fails with the message outlined below
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netdb.h>
#include <netinet/in.h>
#include <arpa/inet.h>
// Definitions
#define COM_MSG_SIZE 1024
#define COM_HOST_SIZE 128
struct com_socket
{
char *type;
int descriptor;
struct addrinfo addr;
};
void COM_error(char *msg) {
perror(msg);
exit(0);
}
int main()
{
int status;
struct com_socket COM_client;
char addr_str[COM_HOST_SIZE];
// ---------------------------------------------
// Initialize socket
COM_client.type = "UDP";
char *hostname = "192.168.0.110";
char *port_num = "4000";
printf("Creating socket...");
if(strcmp(COM_client.type, "UDP") == 0)
{
COM_client.descriptor = socket(AF_INET, SOCK_DGRAM, 0);
}
// Error check
if(COM_client.descriptor < 0)
{
COM_error(" ERROR opening socket");
}
printf(" Success\n");
//------------------------------------------------------------------------------------------
// Define hints
struct addrinfo hints;
hints.ai_family = AF_INET; // AF_UNSPEC "unspecified" or can use IPv6 = AF_INET6, IPv4 = AF_INET
hints.ai_socktype = SOCK_DGRAM; // Socket type: SOCK_STREAM or SOCK_DGRAM or 0 = auto
hints.ai_flags = AI_CANONNAME;
hints.ai_protocol = 0; // 0 = auto
hints.ai_canonname = NULL;
hints.ai_addr = NULL;
hints.ai_addrlen = 0;
hints.ai_next = NULL;
// Get the linked list of address info
struct addrinfo *host_list;
printf("Building host address list...");
status = getaddrinfo(hostname,port_num,&hints,&host_list);
// returns 0 if succeeds
if (status != 0)
{
COM_error(" ERROR getaddrinfo: %s\n");
}
printf(" Success\n");
//------------------------------------------------------------------------------------------
// Select address
int count = 1;
struct addrinfo *entry;
// Loop through each entry in the "linked list" and pull the necessary one
for (entry = host_list; entry != NULL; entry = entry->ai_next)
{
// Print the list of potential IP addresses
if( NULL == inet_ntop( AF_INET, &((struct sockaddr_in *) entry->ai_addr)->sin_addr, addr_str, sizeof(addr_str) ) )
{
COM_error(" ERROR with inet_ntop\n");
}
printf(" Address entry %d: %s",count,addr_str);
// Update counter
count = count + 1;
// Choose which one to copy
if(strncmp(addr_str,"192.",(size_t) 4) == 0)
{
//memcpy(COM_client.addr,entry, sizeof(struct addrinfo));
COM_client.addr = *entry;
// COM_client.addr.ai_addr = entry->ai_addr;
// COM_client.addr.ai_addrlen = entry->ai_addrlen;
// COM_client.addr.ai_canonname = entry->ai_canonname;
// COM_client.addr.ai_family = entry->ai_family;
// COM_client.addr.ai_flags = entry->ai_flags;
// COM_client.addr.ai_protocol = entry->ai_protocol;
// COM_client.addr.ai_socktype = entry->ai_socktype;
if( inet_ntop( AF_INET, &((struct sockaddr_in *) COM_client.addr.ai_addr)->sin_addr, addr_str, sizeof(addr_str) ) == NULL )
{
COM_error(" ERROR with arguments to inet_ntop\n");
}
printf(" <--------- selected* (%s) \n",addr_str);
break;
}
else
{
printf("\n");
}
}
// Clean
freeaddrinfo(host_list);
//-------------------------------------------------------
char *buffer;
char msg[COM_MSG_SIZE];
strncpy(msg,"BEGIN",COM_MSG_SIZE);
printf("ENTER `COM_msg_send` address length %d\n",COM_client.addr.ai_addrlen);
buffer = calloc(COM_MSG_SIZE+1, sizeof(char));
printf("AFTER calloc `COM_msg_send` address length %d\n",COM_client.addr.ai_addrlen);
// Check to see if we were successful
if (buffer == NULL)
{
printf("ERROR Could not allocate required memory\n");
exit(1);
}
// Copy message to buffer
strncpy(buffer,msg,COM_MSG_SIZE);
printf("Message input: %s Message to be sent: %s\n",msg,buffer);
if( inet_ntop( AF_INET, &((struct sockaddr_in *) COM_client.addr.ai_addr)->sin_addr, addr_str, sizeof(addr_str) ) == NULL )
{
COM_error(" ERROR with arguments to inet_ntop\n");
}
printf("SEND to address (%s) \n",addr_str);
// Send the buffer to the destination address
if(strcmp(COM_client.type, "UDP") == 0)
{
status = sendto(COM_client.descriptor, buffer, strlen(buffer), 0, COM_client.addr.ai_addr, COM_client.addr.ai_addrlen);
// Error check
if (status < 0)
{
COM_error("ERROR could not send message");
}
}
// Free buffer memory
free(buffer);
//---------------------------------------------------------
close(COM_client.descriptor);
return 0;
}
Here is the output showing messages from the print statements as well as the failure
Creating socket... Success
Building host address list... Success
Address entry 1: 192.168.0.110 <--------- selected* (192.168.0.110)
ENTER `COM_msg_send` address length 16
AFTER calloc `COM_msg_send` address length 16
Message input: BEGIN Message to be sent: BEGIN
L1 = 16 L2 = 16
SEND to address (0.0.0.0)
ERROR could not send message: Invalid argument
Showing SEND to address (0.0.0.0), it appears that something is wrong with the address stored in the structure COM_client. Specifically, I believe I'm having trouble with this part
//memcpy(COM_client.addr,entry, sizeof(struct addrinfo));
COM_client.addr = *entry;
// COM_client.addr.ai_addr = entry->ai_addr;
// COM_client.addr.ai_addrlen = entry->ai_addrlen;
// COM_client.addr.ai_canonname = entry->ai_canonname;
// COM_client.addr.ai_family = entry->ai_family;
// COM_client.addr.ai_flags = entry->ai_flags;
// COM_client.addr.ai_protocol = entry->ai_protocol;
// COM_client.addr.ai_socktype = entry->ai_socktype;
As you can see, I've tried various things, all of which fail. I want to continue to use the COM_client structure approach as my intention is to make the code more modular in which I can pass the structure containing all the necessary communication information.
This line
COM_client.addr = *entry;
"tries" to copy a struct addrinfo, which it in fact does, but as it contains pointers and "only" copies the pointers' values. The memory those pointers point to had been allocated by getaddrinfo() and thus will be deallocates by the call to freeaddrinfo() leaving the pointers inside the copy dangle afterwards.
To get around this you need to perform a "deep copy" of a struct addrinfo.
This for example can be done like so:
/* Does a deep copy to where pdst point from where pscr points to.
Returns 0 on success and -1 on error. Sets errno. */
int addrinfo_copy(struct addrinfo * pdst, struct addrinfo * psrc)
{
int result = 0; /* Be optimistic. */
assert(pdst);
assert(psrc);
*pdst = *pscr; /* Copy all. Note that the pointer elements copied
need to be recreated. See below ... */
do
{
pdst->ai_addr = malloc(psrc->ai_addrlen);
if (!pdst->ai_addr)
{
result = -1;
break;
}
memcpy(pdst->ai_addr, psrc->ai_addr, psrc->ai_addrlen);
pdst->ai_canonname = strdup(psrc->ai_canonname); /* Assumes POSIX. */
if (!pdst->ai_canonname)
{
result = -1;
break;
}
} while (0);
return result;
}
To get rid of such a copy you need something like this:
/* Deallocates and sets to a 0/NULL what had been created by
addrinfo_copy(). */
void addrinfo_free(struct addrinfo * p)
{
assert(p);
free(p->ai_addr);
free(p->canonname);
memset(p, 0, sizeof *p); /* In fact not necessary. */
}
Use it like this:
struct addrinfo * entry, * entry_copy;
/* Set entry to something returned by getaddrinfo (). */
...
if (-1 = addrinfo_copy(entry_copy, entry))
{
perror("addrinfo_copy() failed"):
exit(EXIT_FAILURE);
}
/* Deallocate all results returned by getaddrinfo(). */
freeaddrinfo(...);
/* Still use entry_copy here. */
...
/* Clean up. */
addrinfo_free(entry_copy);
As a final note:
If when doing C you observe obscure sudden/unexpected changes in memory content this all most ever dues to having messed up memory management by writing and/or reading to "wrong" memory. This some times happened way long before those changes in memory become obvious and/or in code (seemingly) completely unrelated to where you observe such changes in memory.

Repeated SSL_connect in C causes SIGSEGV

I have the following code, and I am getting SIGSEGV on the line:
if ( SSL_connect(ssl) == FAIL )
The fault Im getting is:
Program received signal SIGSEGV, Segmentation fault.
0x00007ffffe5a41e0 in __GI___libc_malloc (bytes=104) at malloc.c:2926
2926 malloc.c: No such file or directory.
The program basically is designed to take loads of data and push it into firebase.
The first one element, is to check if we are registered, the next bit is to actually do the registration.
Cutting the program back to basics, we have the following opening gambit:
int main(int argc, char *argv[]) {
int iRegistered = checkRegistered();
int result = registerCar();
}
If we swap those two lines, so we register before we check the registration, then we don't get a SIGSEGV.
Here's the checkRegistration function:
int checkRegistered() {
int firebaseRegistered = 0;
char *carId;
carId = (char *) malloc(256);
strcpy(carId, "aabbccddeeffgg" );
char *payload;
payload = (char *) malloc(1024);
sprintf(payload, "{ \"carid\": \"%s\" }", carId);
char *response;
response = (char *) malloc(1024);
int result = firebase("isCarRegistered", payload, &response);
if (result == 0) {
// Process JSON Response
cJSON *json = cJSON_Parse(response);
if (json == NULL) {
//
} else {
cJSON *json_registered = NULL;
json_registered = cJSON_GetObjectItemCaseSensitive(json, "registered");
firebaseRegistered = json_registered->valueint;
}
}
free(response);
free(payload);
free(carId);
return firebaseRegistered;
}
And the registerCar function.
They're basically mostly the same format - construct a message, send it to firebase, process the JSON response. We use cJSON to decompile the data returned from Firebase, though we could potentially use it to also compile. But one thing at a time.
You'll see a number of free() statements - I've been trying to work out how best to complete this - ie, generate a char* locally, pass by reference ** to a function, let the function perform the malloc/realloc based on the sizes it can calculate and then we can free it from the calling code once we have dealth with the data. Though I also get a SIGSEGV from that as well.
int registerCar() {
int iResponse = 0;
char *carId;
carId = (char *) malloc(256);
char *authCode;
authCode = (char *) malloc(12);
char *payload;
payload = (char *) malloc(1024);
sprintf(payload, "{ }");
char *response;
response = (char *) malloc(1024);
int result = firebase("registerCar", payload, &response);
if (result == 0) {
// Process JSON Response
cJSON *json = cJSON_Parse(response);
if (json == NULL) {
//
} else {
cJSON *json_auth = NULL;
cJSON *json_car = NULL;
json_auth = cJSON_GetObjectItemCaseSensitive(json, "authcode");
json_car = cJSON_GetObjectItemCaseSensitive(json, "carid");
iResponse = 1;
}
}
free(response);
free(payload);
return iResponse;
}
Here's the firebase routine, it takes a function, a payload and generates a response. Interestingly here, char firebaseLocal and charfirebaseMessage is not always null before the initial malloc.
int firebase(char *firebaseFunction, char *firebasePayload, char **firebaseResponse) {
char buf[1024];
char *firebaseLocal;
char *firebaseMessage;
firebaseMessage = (char *) malloc(1024);
SSL_CTX *ctx;
int server;
SSL *ssl;
int bytes;
ctx = InitCTX();
server = OpenConnection(HOST, atoi(PORT));
ssl = SSL_new(ctx); /* create new SSL connection state */
SSL_set_fd(ssl, server); /* attach the socket descriptor */
if ( SSL_connect(ssl) == FAIL ) /* perform the connection */
ERR_print_errors_fp(stderr);
else {
ShowCerts(ssl); /* get any certs */
char *firebasePost;
generatePostMessage(firebaseFunction, firebasePayload, &firebasePost);
SSL_write(ssl, firebasePost, strlen(firebasePost));
bytes = SSL_read(ssl, buf, sizeof(buf)); /* get reply & decrypt */
buf[bytes] = 0;
//SSL_free(ssl); /* release connection state */
strcpy(firebaseMessage, buf);
firebaseLocal = strstr(firebaseMessage, "\r\n\r\n");
if (firebaseLocal != NULL) {
firebaseLocal +=4;
}
strcpy(*firebaseResponse, firebaseLocal);
}
free(firebaseMessage);
close(server); /* close socket */
SSL_CTX_free(ctx); /* release context */
return 0;
}
This is from an implementation I found on secure sockets.
int OpenConnection(const char *hostname, int port)
{ int sd;
struct hostent *host;
struct sockaddr_in addr;
if ( (host = gethostbyname(hostname)) == NULL )
{
perror(hostname);
abort();
}
sd = socket(PF_INET, SOCK_STREAM, 0);
bzero(&addr, sizeof(addr));
addr.sin_family = AF_INET;
addr.sin_port = htons(port);
addr.sin_addr.s_addr = *(long*)(host->h_addr);
if ( connect(sd, (struct sockaddr*)&addr, sizeof(addr)) != 0 )
{
close(sd);
perror(hostname);
abort();
}
return sd;
}
This is from an implementation I found on secure sockets.
SSL_CTX* InitCTX(void)
{
SSL_METHOD *method;
SSL_CTX *ctx;
SSL_library_init();
OpenSSL_add_all_algorithms(); /* Load cryptos, et.al. */
SSL_load_error_strings(); /* Bring in and register error messages */
method = TLSv1_2_client_method(); /* Create new client-method instance */
ctx = SSL_CTX_new(method); /* Create new context */
if ( ctx == NULL )
{
ERR_print_errors_fp(stderr);
abort();
}
return ctx;
}
This is from an implementation I found on secure sockets.
void ShowCerts(SSL* ssl)
{ X509 *cert;
char *line;
cert = SSL_get_peer_certificate(ssl); /* get the server's certificate */
if ( cert != NULL )
{
printf("Server certificates:\n");
line = X509_NAME_oneline(X509_get_subject_name(cert), 0, 0);
printf("Subject: %s\n", line);
free(line); /* free the malloc'ed string */
line = X509_NAME_oneline(X509_get_issuer_name(cert), 0, 0);
printf("Issuer: %s\n", line);
free(line); /* free the malloc'ed string */
X509_free(cert); /* free the malloc'ed certificate copy */
}
else
printf("Info: No client certificates configured.\n");
}
This is something that I wrote to generate a post message from message
void generatePostMessage(char *firebaseFunction, char *firebaseMessage, char **response) {
int intPayloadSize = strlen(firebaseMessage);
char *charPayloadSize;
charPayloadSize = (char *) malloc(8);
sprintf(charPayloadSize, "%d", intPayloadSize);
char *postmessage = "POST /%s HTTP/1.1\r\n"
"Host: us-central1-carconnect-e763e.cloudfunctions.net\r\n"
"User-Agent: USER_AGENT\r\n"
"Content-Type: application/json\r\n"
"Accept: text/plain\r\n"
"Content-Length: %d\r\n\r\n"
"%s";
// Allocate size of postmessage less the inserts, plus the payload size, plus the payload size digits, plus null
int responseLength = (strlen(postmessage) - 4) + intPayloadSize + strlen(charPayloadSize)+1;
// Round up Four Bytes.
int responseIncrease = responseLength % 4;
if (responseIncrease > 0) {
responseLength += (4 - responseIncrease);
}
*response = (char *) malloc(responseLength);
sprintf(*response, postmessage, firebaseFunction, intPayloadSize, firebaseMessage);
}
As advised, whether the registration or registration check is called first, the first call works fine.
If I perform the registration before the check, then both commands work fine. Further testing also does confirm the problem is the registration check. I can perform registration several times without fail. The registration check and any follow up calls fail completely at the SSL_connect line. I don't know why.
The SSL_free command in the firebase connection always fails. I also get a SIGSEGV if I try to free(firebasePost) after the SSL_Write - which suggests I cannot free a pointer that has been passed by reference and mallocced in a function.
Part of me wonders whether any of this is caused by the fact Im debugging on Windows. I've always had problems with malloc() on Windows just not working the way I would expect.
The problem, or at least one of them, is in generatePostMessage. Not enough buffer is allocated for response. sprintf will then run off the end of the allocated buffer and cause heap corruption, which manifests itself on next invocation of malloc. Try:
int responseLength = strlen(firebaseFunction) + (strlen(postmessage) - 4) + intPayloadSize + strlen(charPayloadSize)+1;

Reading from named pipe freezes when piping tshark output

I'm trying to implement a web prefetching system. The purpose of a system like this is to “predict” future requests and prefetch them.
The system builds a predictive model from web navigation logs (Squid access.log files). The model is a dependency graph, where a node representing URL A has an arc to a node representing URL B if URL B has been requested immediately after URL A.
Once the model is built, the system receives queries of URLs requested by users, and make “predictions” based on the graph. Predictions are resources (URLs) very likely to be requested in the future. So, based on predictions, the system prefetches these resources to store them in cache prior to users' requests.
I'm using the following testing scenario:
A process simulate multiple clients, requesting URLs in a file using libcurl. The process runs in a different PC from the prefetching system. PCs are connected directly via an ethernet cable
Requests made by the client simulator are always the same URLs in the same relative time from the first request made. All requests are going to port 3128 (Prefetch PC Squid listen port) (port 80 DNAT to port 3128 in the client).
The prefetching system runs in a CentOS 6.3 box, kernel 2.6.32-71.el6.i686, 2 core Intel Pentium 4 3.00GHz processor, 4 GB RAM.
The prefetching system is one process with multiple threads. The main thread creates the predictive model and generates predictions based on queries. A “listener” thread reads URLs requested by users and prefetches predicted URLs using libcurl. “Listening” means reading from a named pipe (called url_fifo) URLs captured live on an interface using tshark:
stdbuf -o0 tshark -i eth1 tcp port 3128 and "tcp[((tcp[12:1] & 0xf0) >> 2):4] = 0x47455420" -T fields -e http.request.full_uri >> url_fifo
Each 10 minutes (1 cycle) the model is updated based on requests from the last cycle. The client tells the system when a cycle ends and so the model is updated. Once the model is updated, the system tells the client to start requesting URLs from the next cycle.
Here is the situation: Sometimes reading from the named pipe freezes. No URLs are read from the pipe even though tshark keeps capturing URLs and redirecting them to the named pipe. After an hour (or a couple of hours) all “buffered” URLs are read in less than 10 minutes. After that, reading from the pipe keeps going ok again. This situation doesn't happen always (50% of times freezes, 50% no).
It seems that there is a buffering issue, since tshark keeps capturing URLs and all requests are correctly logged in Squid's access.log.
In the beginning, I ran tshark with the -l option, so that its output becomes line buffered. Then I started using stdbuf -o0 (no buffering). Anyway the situation still happens.
In the system code, I also tried opening and reading the named pipe as a stream (FILE *) and set the stream as no buffered or line buffered (using setvbuf() function). The situation still happened.
In some cycles requests are faster than in other cycles. Anyway, it doesn't seems to be a fast producer slow consumer issue, since in many repetitions of the test all URLs are correctly read and processed without any freezes.
Is there something am I missing related to named pipes and buffering? I'd really appreciate some guidance.
Assume networking (interfaces, routing, iptables, squid) is ok. I've not had any issues related to it.
Code (assume necessary header files are included):
functions.c
#define BUFLEN 512
#define QUEUE_LEN 64
#define THREADS_LEN 2
pthread_mutex_t model_lock;
pthread_cond_t model_cond, listen_cond;
pthread_t queries_thread, listen_thread;
short int model_is_updating, model_can_update, program_shutdown;
/* Program execution statistics */
Status * program_status;
/* Thread pool */
threadpool_t *pool;
/* program execution */
int
run(void)
{
Graph_Adj_List * gr = NULL; /* Graph as an adjacency list */
char ** reports = NULL;
unsigned report_counter = 0;
/* Init program status */
program_status = status_init();
/* Load list of custom web navigation reports to be used to build the initial
* version of the predictive model */
reports = file_load_reports(program_config.reports_file);
if (!reports)
return 0;
/* Init mutex and cond */
pthread_mutex_init(&model_lock, NULL);
pthread_cond_init(&model_cond, NULL);
pthread_cond_init(&listen_cond, NULL);
/* Lock */
pthread_mutex_lock (&model_lock);
/* Start first cycle */
status_start_cycle(program_status);
/* Create initial version of the predictive model */
gr = create_model_from_files(reports, &report_counter, program_config.reports_limit);
if (!gr)
{
/* Unlock */
pthread_mutex_unlock (&model_lock);
return 0;
}
/* Unlock */
pthread_mutex_unlock (&model_lock);
/* Start threads */
if (pthread_create(&queries_thread, NULL, fifo_predictions_threaded, (void *)gr) ||
pthread_create(&listen_thread, NULL, listen_end_of_cycle, NULL))
program_shutdown = 1;
/* main loop */
while(!program_shutdown)
{
/* lock */
pthread_mutex_lock (&model_lock);
/* wait for clients' announcement of the end of requests from current cycle */
while (!model_can_update)
pthread_cond_wait(&model_cond, &model_lock);
/* set updating flag */
model_is_updating = 1;
/* Update predictive model, based on Squid's access.log from (about to finish)
* current cycle */
adj_list_update_access(gr, program_config.access_file);
/* Save statistics related to the current cycle and finish it */
status_finish_cycle(program_status);
/* Check if last custom report has been read */
if (!reports[report_counter])
{
program_shutdown = 1;
pthread_mutex_unlock (&model_lock);
break;
}
/* Start a new cycle */
status_start_cycle(program_status);
/* Read a new custom report and update the predictive model */
update_model(gr, reports[report_counter]);
report_counter++;
/* Updating is done */
model_is_updating = 0;
/* Model can't be updated until client announces the end of the cycle
* that has just started */
model_can_update = 0;
/* Tell client to start sending requests from the new cycle */
if (!signal_start_cycle())
{
program_shutdown = 1;
pthread_mutex_unlock (&model_lock);
break;
}
/* Signal listener thread that a new cycle has begin */
pthread_cond_signal(&listen_cond);
/* Unlock */
pthread_mutex_unlock (&model_lock);
}
/* Finish threads */
pthread_cancel(listen_thread);
pthread_cancel(queries_thread);
pthread_join(listen_thread, NULL);
pthread_join(queries_thread, NULL);
/* Free memory */
adj_list_free_all2(&gr);
file_reports_free_all(&reports);
pthread_cond_destroy(&model_cond);
pthread_cond_destroy(&listen_cond);
pthread_mutex_destroy(&model_lock);
status_free(&program_status);
return 1;
}
void *
fifo_predictions_threaded(void * data)
{
Graph_Adj_List * gr = (Graph_Adj_List *) data;
/* Set thread cancel type */
pthread_setcanceltype(PTHREAD_CANCEL_ASYNCHRONOUS, NULL);
while (!program_shutdown)
{
pthread_mutex_lock(&model_lock);
/* Pause reading from named pipe while the model is being updated */
while(model_is_updating)
pthread_cond_wait(&listen_cond, &model_lock);
pthread_mutex_unlock(&model_lock);
/* Read URLs from named pipe */
fifo_predictions(gr, program_config.fifo);
}
pthread_exit(NULL);
return NULL;
}
int
fifo_predictions(Graph_Adj_List * gr, const u8 * fifo)
{
u8 cad[BUFLEN] = { '\0' };
u8 * ini = NULL, * fin = NULL, * fullurl = NULL;
int i, fifo_descriptor, read_urls = 0, fullurl_len = 0, incomplete_url = 1;
FILE * fifo_file = NULL;
/* Open fifo in blocking mode */
fifo_descriptor = open(CHAR_CAST fifo, O_RDONLY);
/* Open fifo as a stream */
// fifo_file = fopen(fifo, "r");
// if (!fifo_file)
if (fifo_descriptor == -1)
return 0;
/* If fifo is opened as a stream, set it line buffered */
// setlinebuf(fifo_file);
do
{
if ((i = read(fifo_descriptor, cad, BUFLEN - 1)) == -1)
// if ( fgets(cad, BUFLEN-1, fifo_file) == NULL)
ERROR(__FILE__, __FUNCTION__, __LINE__, "Fifo read error");
else
{
// i = strlen(cad);
cad[i] = '\0';
read_urls = 0;
if (i > 0)
{
int j = 0;
for (j = 0, ini = cad, fin = NULL ; cad[j] != '\0'; j++)
{
if (cad[j] == '\n')
{
/* Save URL */
fin = &cad[j];
ini = (*ini == '\n' ? ini + 1 : ini);
/* Check if string is a continuation of the previously read URL */
read_urls = fin - ini;
read_urls = read_urls >= 0 ? read_urls : 0;
/* Save URL in fullurl string */
fullurl = realloc(fullurl, fullurl_len + read_urls + 1);
memcpy(&fullurl[fullurl_len], ini, read_urls);
fullurl[fullurl_len + read_urls] = '\0';
ini = fin;
incomplete_url = fullurl_len = 0;
/* Ask the model for predictions and fetch them */
fetch_url_predictions2(gr, fullurl);
u8_free(&fullurl);
} else
incomplete_url = 1;
}
if (incomplete_url)
{
ini = (*ini == '\n' ? ini + 1 : ini);
read_urls = &cad[j] - ini;
read_urls = read_urls >= 0 ? read_urls : 0;
fullurl = realloc(fullurl, fullurl_len + read_urls);
memcpy(&fullurl[fullurl_len], ini, read_urls);
fullurl_len += read_urls;
}
}
}
} while (i > 0);
close(fifo_descriptor);
// fclose (fifo_file);
return 1;
}
int
fetch_url_predictions2(Graph_Adj_List * gr, u8 * in_url)
{
String * string_url = NULL;
Headnode * head = NULL;
LinkedList * list = NULL;
LinkedListElem * elem = NULL;
/* Use custom string type */
string_url = string_create_no_len(in_url);
if (!string_url)
return 0;
pthread_mutex_lock(&model_lock);
/* Get URL node */
head = adj_list_get_node(gr, string_url);
if (head)
{
/* Get predictions (URLs) as a linked list */
list = adj_list_predictions_to_list(head);
if (!list)
{
string_free_all(&string_url);
return 0;
}
pthread_mutex_unlock(&model_lock);
/* Callback fetches URLs */
list->callback = &curl_callback_void;
if (!pool)
pool = threadpool_create(THREADS_LEN, QUEUE_LEN, 0);
/* Load URLs to be fetched to threadpool's task queue */
for (elem = list->first; elem; elem = elem->next)
{
CallbackArg arg;
arg.data = arg.copy(elem->data);
threadpool_add_copy_arg(pool, list->callback, &arg, 1, sizeof(arg), 0);
}
linked_list_free_all(&list);
}
pthread_mutex_unlock(&model_lock);
string_free_all(&string_url);
return 1;
}
fetch.c
void
curl_callback_void(void * data)
{
CallbackArg * arg = (CallbackArg *) data;
char * url = (char *) arg->data;
fetch_url(url);
}
static size_t
write_data(void *buffer, size_t size, size_t nmemb, void *userp)
{
return size * nmemb;
}
int
fetch_url(char * url)
{
CURL *curl;
CURLcode res;
struct timeval time;
char * time_string = NULL;
curl = curl_easy_init();
if (curl)
{
curl_easy_setopt(curl, CURLOPT_URL, url);
curl_easy_setopt(curl, CURLOPT_FOLLOWLOCATION, 1L);
curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, &write_data);
curl_easy_setopt(curl, CURLOPT_WRITEDATA, NULL);
curl_easy_setopt(curl, CURLOPT_FAILONERROR, 1);
curl_easy_setopt(curl, CURLOPT_NOPROGRESS, 1);
curl_easy_setopt(curl, CURLOPT_NOSIGNAL, 1);
curl_easy_setopt(curl, CURLOPT_CONNECTTIMEOUT, 15);
curl_easy_setopt(curl, CURLOPT_TIMEOUT, 10);
/* Perform the request, res will get the return code */
res = curl_easy_perform(curl);
gettimeofday(&time, NULL);
time_string = timeval_to_str(&time);
/* Check for errors */
if (res != CURLE_OK)
{
fprintf(stderr, "\ntime %s curl_easy_perform() (url %s) failed: %s\n",
time_string, url, curl_easy_strerror(res));
}
else
{
fprintf(stderr, "\ntime %s curl_easy_perform() (url %s) fetched ok\n",
time_string, url);
}
fflush(stderr);
free (time_string);
curl_easy_cleanup(curl);
}
return 0;
}
network.c
/*
* Code based on Beej's Networking Guide
*/
#define MSG_LEN 5
#define QUEUE_SIZE 5
extern pthread_mutex_t model_lock;
extern pthread_cond_t model_cond;
extern short int model_can_update, program_shutdown;
extern Config program_config;
// get sockaddr, IPv4 or IPv6:
static void *
get_in_addr(struct sockaddr *sa) {
if (sa->sa_family == AF_INET) {
return &(((struct sockaddr_in*) sa)->sin_addr);
}
return &(((struct sockaddr_in6*) sa)->sin6_addr);
}
void *
listen_end_of_cycle(void * data)
{
int sockfd, new_fd; // listen on sock_fd, new connection on new_fd
struct addrinfo hints, *servinfo, *p;
struct sockaddr_storage their_addr; // connector's address information
socklen_t sin_size;
int yes = 1;
char s[INET_ADDRSTRLEN], msg[MSG_LEN], *str = NULL;
int rv;
int read_bytes;
struct timeval actual_time;
/* Set thread cancel type */
pthread_setcanceltype(PTHREAD_CANCEL_ASYNCHRONOUS, NULL );
memset(&hints, 0, sizeof hints);
hints.ai_family = AF_INET;
hints.ai_socktype = SOCK_STREAM;
hints.ai_flags = AI_PASSIVE; // use my IP
if ((rv = getaddrinfo(NULL, program_config.listen_port, &hints, &servinfo))
!= 0) {
fprintf(stderr, "getaddrinfo: %s\n", gai_strerror(rv));
return "error";
}
// loop through all the results and bind to the first we can
for (p = servinfo; p != NULL ; p = p->ai_next) {
if ((sockfd = socket(p->ai_family, p->ai_socktype, p->ai_protocol))
== -1) {
perror("server: socket");
continue;
}
if (setsockopt(sockfd, SOL_SOCKET, SO_REUSEADDR, &yes, sizeof(int))
== -1) {
perror("setsockopt");
return "error";
}
if (bind(sockfd, p->ai_addr, p->ai_addrlen) == -1) {
close(sockfd);
perror("server: bind");
continue;
}
break;
}
if (p == NULL ) {
fprintf(stderr, "server: failed to bind\n");
return "error";
}
freeaddrinfo(servinfo); // all done with this structure
if (listen(sockfd, QUEUE_SIZE) == -1) {
perror("listen");
return "error";
}
while (!program_shutdown)
{
sin_size = sizeof their_addr;
new_fd = accept(sockfd, (struct sockaddr *) &their_addr, &sin_size);
if (new_fd == -1) {
perror("accept");
continue;
}
inet_ntop(their_addr.ss_family,
get_in_addr((struct sockaddr *) &their_addr), s, sizeof s);
if ((read_bytes = recv(new_fd, msg, MSG_LEN - 1, 0)) == -1) {
perror("recv");
continue;
}
close(new_fd);
msg[read_bytes] = '\0';
/* Check received message */
if (strcmp(msg, "DONE")) {
perror("Not valid message");
continue;
}
printf("\ngot \"DONE\" from %s\n", s);
fflush(stdout);
/* Lock */
pthread_mutex_lock(&model_lock);
/* Flag used by main thread to allow model update */
model_can_update = 1;
/* Signal model can be updated */
pthread_cond_signal(&model_cond);
/* Unlock */
pthread_mutex_unlock(&model_lock);
}
close(sockfd);
pthread_exit(NULL);
return "ok";
}
int signal_start_cycle(void) {
int sockfd;
struct addrinfo hints, *servinfo, *p;
int rv;
char s[INET6_ADDRSTRLEN], *str = NULL;
struct timeval actual_time, aux_time;
struct timeval connect_timeout = { 15, 0 }, max_connect_time = { 0, 0 };
short int connected = 0;
memset(&hints, 0, sizeof hints);
hints.ai_family = AF_INET;
hints.ai_socktype = SOCK_STREAM;
if ((rv = getaddrinfo(program_config.client_ip, program_config.client_port,
&hints, &servinfo)) != 0) {
fprintf(stderr, "getaddrinfo: %s\n", gai_strerror(rv));
return 0;
}
gettimeofday(&aux_time, NULL);
timeval_add(aux_time, connect_timeout, &max_connect_time);
/* Try several times to connect to the remote side */
do {
// loop through all the results and connect to the first we can
for (p = servinfo; p != NULL ; p = p->ai_next) {
if ((sockfd = socket(p->ai_family, p->ai_socktype, p->ai_protocol))
== -1) {
perror("client: socket");
continue;
}
gettimeofday(&actual_time, NULL )
printf("\ntrying to connect %s\n", program_config.client_ip);
fflush(stdout);
if (connect(sockfd, p->ai_addr, p->ai_addrlen) == -1) {
close(sockfd);
perror("client: connect");
continue;
}
connected = 1;
break;
}
} while (!connected && !timeval_greater_than(actual_time, max_connect_time));
if (p == NULL ) {
fprintf(stderr, "client: failed to connect\n");
return 0;
}
inet_ntop(p->ai_family, get_in_addr((struct sockaddr *) p->ai_addr), s,
sizeof s);
printf("\nMAIN THREAD: connecting to %s\n", s);
fflush(stdout);
freeaddrinfo(servinfo); // all done with this structure
if (send(sockfd, "DONE", 4, 0) == -1)
{
perror("send");
return 0;
}
printf("\nsent \"DONE\" to %s\n", s);
fflush(stdout);
close(sockfd);
return 1;
}

How to get the file name of each of the connections (webpage)

We are trying to write a multi-threaded web server but we don't know how to get the file name from each of the HTTP requests (web pages) to a simple server. We are also concerned about the size of each of these files as well. Any idea?
Here is our main for the server.c file:
int main(int argc, char *argv[])
{
int listenfd, connfd, port, clientlen;
struct sockaddr_in clientaddr;
getargs(&port, argc, argv);
listenfd = Open_listenfd(port);
thread_pool_init();
for(;;){
pthread_mutex_lock(&pool_lock);
while(buf_count == request_limit)
pthread_cond_wait(&signal_worker, &pool_lock);
clientlen = sizeof(clientaddr);
connfd = Accept(listenfd, (SA *)&clientaddr, (socklen_t *) &clientlen);
//get/parse html file name here
//get file size using stat
put(connfd);
pthread_cond_signal(&signal_worker);
pthread_mutex_unlock(&pool_lock);
}
Our open_connection code in client.c which sends HTTP requests to the server.c file, looks like this :
void * open_connection( ){
clientfd = Open_clientfd(host, port);
clientSend(clientfd, filename);
pthread_mutex_lock(&lock);
clientPrint(clientfd);
pthread_mutex_unlock(&lock);
Close(clientfd);
sem_post(&cond);
return NULL;
}
//Send an HTTP request for the specified file
void clientSend(int fd, char *filename)
{
char buf[MAXLINE];
char hostname[MAXLINE];
Gethostname(hostname, MAXLINE);
//Form and send the HTTP request
sprintf(buf, "GET %s HTTP/1.1\n", filename);
sprintf(buf, "%shost: %s\n\r\n", buf, hostname);
Rio_writen(fd, buf, strlen(buf));
}
Once you receive the HTTP request you need to parse it to retrieve the name of the file requested and then send the file back to the client.
I post a simple code that can be used to handle HTTP request which I have used in one of my experiment. It is really simple, it does not take into account a lot of different characteristics of the HTTP protocol, basically it works only with GET request, but it may be a good starting point.
The recv_request is a function that reads the request from the socket used to communicate with the client.
#define PORT 80
#define WEBROOT "/var/www/localhost/htdocs/"
void handle_connection(int sockfd, struct sockaddr_in *client_addr_ptr) {
unsigned char *ptr, request[REQUEST], resource[REQUEST];
int fd, length;
memset(request, 0, REQUEST);
memset(resource, 0, REQUEST);
length = recv_request(sockfd, request);
printf("Got request from %s:%d lenght: %d \n", inet_ntoa(client_addr_ptr->sin_addr), ntohs(client_addr_ptr->sin_port),length);
puts("--------------------------------\n");
printf("%.*s", 500, request);
puts("--------------------------------");
ptr = strstr(request, " HTTP/"); // search for valid looking request
if(ptr == NULL) { // then this isn't valid HTTP
printf(" NOT HTTP!\n");
} else {
*ptr = 0; // terminate the buffer at the end of the URL
ptr = NULL; // set ptr to NULL (used to flag for an invalid request)
if(strncmp(request, "GET ", 4) == 0) // get request
ptr = request+4; // ptr is the URL
if(strncmp(request, "HEAD ", 5) == 0) // head request
ptr = request+5; // ptr is the URL
if(ptr == NULL) { // then this is not a recognized request
printf("\tUNKNOWN REQUEST!\n");
} else { // valid request, with ptr pointing to the resource name
if (ptr[strlen(ptr) - 1] == '/') // for resources ending with '/'
strcat(ptr, "index.html"); // add 'index.html' to the end
strcpy(resource, WEBROOT); // begin resource with web root path
strcat(resource, ptr); // and join it with resource path
fd = open(resource, O_RDONLY, 0); // try to open the file
printf("Opening \'%s\'\t", resource);
if(fd == -1) { // if file is not found
printf(" 404 Not Found\n");
send_string(sockfd, "HTTP/1.0 404 NOT FOUND\r\n");
send_string(sockfd, "Server: Tiny webserver\r\n\r\n");
send_string(sockfd, "<html><head><title>404 Not Found</title></head>");
send_string(sockfd, "<body><h1>URL not found</h1></body></html>\r\n");
} else { // otherwise, serve up the file
printf(" 200 OK\n\n");
send_string(sockfd, "HTTP/1.0 200 OK\r\n");
send_string(sockfd, "Server: Tiny webserver\r\n\r\n");
if(ptr == request + 4) { // then this is a GET request
if( (length = get_file_size(fd)) == -1)
fatal("getting resource file size");
if( (ptr = (unsigned char *) malloc(length)) == NULL)
fatal("allocating memory for reading resource");
read(fd, ptr, length); // read the file into memory
write(sockfd, ptr, length); // send it to socket
free(ptr); // free file memory
}
close(fd); // close the file
} // end if block for file found/not found
} // end if block for valid request
} // end if block for valid HTTP
shutdown(sockfd, SHUT_RDWR); // close the socket gracefully
return;
}
You should give a look at the curl library.

Getting gateway to use for a given ip in ANSI C

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 ;)

Resources