I have an idea for a project based on the Apache module API. So, I am writing my first Apache module to learn the API- just a simple hostname lookup:
URL: http://localhost/hostname/stackoverflow.com
Response data:
Host: stackoverflow.com
Address: 69.59.196.211
Address type: AF_INET
When the request does not include a hostname after the handler path, I just want the Response data to say:
"Hostname not found in request."
All of this is working, except one thing: the error response keeps getting appended to on subsequent requests that produce the error result. This does not happen with the hostname lookup result (when the hostname is provided). Example of the issue:
Request 1: http://192.168.1.3/hostname
Response data:
Hostname not found in request.
Request 2: http://192.168.1.3/hostname
Response data:
Hostname not found in request.
Hostname not found in request.
Request 3: http://192.168.1.3/hostname
Response data:
Hostname not found in request.
Hostname not found in request.
Hostname not found in request.
I'm sure I've done something wrong with my output buffer in the error case - anyone able to point out the problem? Here is the code:
#include <sys/types.h>
#include <netdb.h>
#include <stdio.h>
#include <stdarg.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include "httpd.h"
#include "http_log.h"
#include "http_config.h"
static void log_info(request_rec *r, const char *fmt, ...){
char log_msg[100];
memset(log_msg,100,0x00);
va_list args;
va_start(args,fmt);
vsprintf(log_msg,fmt,args);
va_end(args);
ap_log_error(APLOG_MARK,APLOG_INFO,APR_SUCCESS,r->server,log_msg,NULL);
}
static void log_err(request_rec *r, const char *fmt, ...){
char log_msg[100];
memset(log_msg,100,0x00);
va_list args;
va_start(args,fmt);
vsprintf(log_msg,fmt,args);
va_end(args);
ap_log_error(APLOG_MARK,APLOG_ERR,APR_SUCCESS,r->server,log_msg,NULL);
}
int count_chrinstr(const char *haystack, const char needle){
int n=0;
char *next;
while( (next=strchr(haystack,needle)) != NULL ){
haystack=next+1;
n++;
}
return n;
}
void parse_uri(char *uri, char *pieces[]){
int i=0;
char *next_tok=strtok(uri,"/");
while( next_tok != NULL ){
pieces[i]=next_tok;
i++;
next_tok=strtok(NULL,"/");
}
}
void lookup_hostname(request_rec *r, char *output){
int num_parts=count_chrinstr(r->uri,'/');
log_info(r,"Number of parts: %d",num_parts);
if(num_parts<2){
log_err(r,"Hostname not found in request, exiting.",NULL);
strcat(output,"Hostname not found in request.<br>\n");
return;
}
char *pieces[num_parts];
parse_uri(r->uri,pieces);
char *host_entered=pieces[1];
struct hostent *h=gethostbyname(host_entered);
//host
output += sprintf(output,"Host: %s<br>\n", h->h_name);
//aliases
int i=0;
while(h->h_aliases[i] != NULL){
output += sprintf(output,"Alias: %s<br>\n",h->h_aliases[i]);
i++;
}
//addresses
i=0;
while(h->h_addr_list[i] != NULL){
char *net_addr=inet_ntoa( *(struct in_addr*)(h->h_addr_list[i]));
output += sprintf(output,"Address: %s<br>\n",net_addr);
i++;
}
log_info(r,"Added addresses to output.",NULL);
//address type
if(h->h_addrtype != NULL){
switch(h->h_addrtype){
case 2:
strcat(output,"Address type: AF_INET<br>\n");
break;
case 10:
strcat(output,"Address type: AF_INET6<br>\n");
break;
default:
strcat(output,"Address type: Unknown<br>\n");
break;
}
}
}
static int hostname_handler(request_rec *r) {
if (!r->handler || strcasecmp(r->handler, "hostname") != 0) {
return DECLINED;
}
if (r->method_number != M_GET) {
return HTTP_METHOD_NOT_ALLOWED;
}
char result[10000];
memset(result,10000,0x00);
log_info(r,"Starting hostname lookup.",NULL);
lookup_hostname(r,result);
ap_set_content_type(r, "text/html");
ap_rputs(result, r);
ap_finalize_request_protocol(r);
return OK;
}
static void hostname_hooks(apr_pool_t *pool) {
ap_hook_handler(hostname_handler, NULL, NULL, APR_HOOK_MIDDLE);
}
module AP_MODULE_DECLARE_DATA hostname_module = {
STANDARD20_MODULE_STUFF,
NULL,
NULL,
NULL,
NULL,
NULL,
hostname_hooks
};
Thanks in advance,
-aj
You have the parameters to memset
around the wrong way:
memset(result,10000,0x00);
That line uses a count of zero, which means it does nothing. Instead of using memset, you could just do this:
char result[10000] = { 0 };
This will initialise the entire array to zero (objects in C are never partially initialised).
Alternatively, you could set just the first character to zero, since that will also make strcat
do the right thing:
char result[10000];
result[0] = '\0';
(Also, you should be passing the buffer size to lookup_hostname
and using strncat
/ snprintf
instead of strcat
/ sprintf
).