//******************** askserver.c ******************/
// Aruna Sujith Karunarathna
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <errno.h>
#include <string.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <netdb.h>
#include <arpa/inet.h>
#include <sys/wait.h>
#include <signal.h>
#include <fcntl.h>
#include <sys/sendfile.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <netinet/tcp.h>
#include <limits.h>
#include <unistd.h>
#include <assert.h>
#include "ini.h"
#define CONFIGURATION_FILE "configuration/ask.ini"
typedef struct{
int version;
int backlog;
int max_header_size;
const char* http_version;
const char* root_dir;
const char* default_page;
const char* error_page;
} configuration;
configuration config;
void sigchld_handler(int s){
while(waitpid(-1, NULL, WNOHANG) > 0);
}
// get sockaddr, IPv4 or IPv6:
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);
}
static int handler(void* user, const char* section, const char* name,const char* value){
configuration* pconfig = (configuration*)user;
#define MATCH(s, n) strcasecmp(section, s) == 0 && strcasecmp(name, n) == 0
if (MATCH("protocol", "version")) {
pconfig->version = atoi(value);
} else if (MATCH("web", "http_version")) {
pconfig->http_version = strdup(value);
} else if (MATCH("web", "root_dir")) {
pconfig->root_dir = strdup(value);
} else if (MATCH("web", "default_page")) {
pconfig->default_page = strdup(value);
} else if (MATCH("web", "error_page")) {
pconfig->error_page = strdup(value);
} else if (MATCH("web", "backlog")) {
pconfig->backlog = atoi(value);
} else if (MATCH("web", "max_header_size")) {
pconfig->max_header_size = atoi(value);
} else {
return 0; /* unknown section/name, error */
}
return 1;
}
char* get_extension(char* file_name){
char* extension;
extension = strchr(file_name,'.')+1 ;
printf("EXTENSION %s \n",extension);
return extension;
}
char* get_content_type(char* extension){
char* type;
if(strcmp(extension,"html")==0)
type = "text/html";
else if(strcmp(extension,"css")==0)
type = "text/css";
else if(strcmp(extension,"txt")==0)
type = "application/text";
else if(strcmp(extension,"pdf")==0)
type = "application/pdf";
else if(strcmp(extension,"zip")==0)
type = "application/zip";
else if(strcmp(extension,"xml")==0)
type = "application/xml";
else if(strcmp(extension,"js")==0)
type = "application/javascript";
else if(strcmp(extension,"jpg")==0)
type = "image/jpg";
else if(strcmp(extension,"png")==0)
type = "image/png";
else if(strcmp(extension,"exe")==0)
type = "application/octet-stream";
else if(strcmp(extension,"ico")==0)
type = "image/x-icon";
else if(strcmp(extension,"php")==0)
type = "text/html";
printf("TYPE %s ",type);
return type;
}
int set_header(char *header,int status_code,char *file_name,int file_length){
if(status_code==404){
//printf("%s %d File Not Found\n",config.http_version,status_code);
sprintf(header,
"%s %d File Not Found\n"
"\n",config.http_version,status_code);
printf("\nHEADER MESSAGE %s \n",header);
return -1;
}
if(status_code==400){
sprintf(header,
"%s %d Bad Request\n"
"\n",config.http_version,status_code);
return -1;
}
if(status_code==501){
sprintf(header,"%s %d POST Not Implemented\n "
"\n POST Not Implemented",config.http_version,status_code);
return -1;
}
char *extension;
extension = get_extension(file_name);
char *content_type;
content_type = get_content_type(extension);
sprintf(header,"%s %d OK \n"
"Content-Type: %s\n"
"Content-Length: %i\n"
"\n",config.http_version,status_code,content_type ,file_length);
printf("\nHEADER MESSAGE %s \n",header);
}
char* check_request(char * request){
char* ptr;
ptr = strstr(request," HTTP/");
// printf("CHK REQUEST %s \n",request);
if(ptr == NULL) {
printf("Not HTTP request\n");
}else{
/* *** HTTP request received *** */
/* *** check for GET request *** */
if(strncmp(request,"GET ",4) == 0) {
ptr="GET";
}
/* *** check for POST request, give 501 error if received *** */
else if(strncmp(request,"POST ",5) == 0){
printf("501 Method not implemented\n");
ptr="POST";
}
}
return ptr;
}
char* get_path(char *request)
{
int i=0;
// printf("REQUEST %s \n",request);
char *token = NULL;
token = strtok(request, " ");
token = strtok(NULL, " ");
return token;
}
int send_content(char* file_name, int socket, int status_code){
int sent_size;
char header[config.max_header_size];
int open_file; /* file descriptor for source file */
struct stat stat_buf; /* hold information about input file */
off_t offset = 0; /* byte offset used by sendfile */
int return_code; /* return code from sendfile */
open_file = open(file_name, O_RDONLY);/* check that source file exists and can be opened */
fstat(open_file, &stat_buf); /* get size and permissions of the source file */
set_header(header,status_code,file_name,(int)stat_buf.st_size);
sent_size=send(socket, header, strlen(header), 0);
/* copy file using sendfile ####################################### */
// sendfile(destination, source, offset,size);
return_code = sendfile (socket, open_file, &offset, stat_buf.st_size);
printf("file size %d Bytes \n\n",return_code);
if (return_code == -1) {
fprintf(stderr, "error from sendfile: %s\n", strerror(errno));
}
else if (return_code != stat_buf.st_size) {
fprintf(stderr, "incomplete transfer from sendfile: %d of %d bytes\n", return_code, (int)stat_buf.st_size);
}
/* clean up and exit */
close(socket);
close(open_file);
return 0;
}
int main(int argc, char* argv[]){
if (ini_parse(CONFIGURATION_FILE , handler, &config) < 0) {
printf("Can't load 'ask.ini'\n");
return 1;
}
printf("Config loaded from 'ask.ini':\nversion=%d\nbacklog=%d\nmax_header_size=%d \nhttp_version=%s\n",
config.version, config.backlog, config.max_header_size, config.http_version);
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;
struct sigaction sa;
int yes=1;
char s[INET6_ADDRSTRLEN];
int rv;
memset(&hints, 0, sizeof hints);
hints.ai_family = AF_UNSPEC;
hints.ai_socktype = SOCK_STREAM;
hints.ai_flags = AI_PASSIVE; // use my IP
if ((rv = getaddrinfo(NULL, argv[1], &hints, &servinfo)) != 0) {
fprintf(stderr, "getaddrinfo: %s\n", gai_strerror(rv));
return 1;
}
// 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");
exit(1);
}
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 2;
}
freeaddrinfo(servinfo); // all done with this structure
if (listen(sockfd, config.backlog) == -1) {
perror("listen");
exit(1);
}
sa.sa_handler = sigchld_handler; // reap all dead processes
sigemptyset(&sa.sa_mask);
sa.sa_flags = SA_RESTART;
if (sigaction(SIGCHLD, &sa, NULL) == -1) {
perror("sigaction");
exit(1);
}
printf("ASK_SERVER: waiting for connections...\n");
while(1) { // main accept() loop
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);
printf("ASK_SERVER: got connection from %s\n", s);
if (!fork()) { // this is the child process
close(sockfd); // child doesn't need the listener
int buffer_size =1024;
char* char_buffer = malloc(buffer_size);
//get the client request
read(new_fd,char_buffer,buffer_size);
char *path;
//printf("REQUEST ------ %s \n",char_buffer);
//printf("CHECK_REQUEST %s \n",check_request(char_buffer));
char* request_type = check_request(char_buffer);
printf("REQUEST TYPE ::::::: %s\n",request_type);
char* header=malloc(200);
memset(header,0,200);
if(request_type==NULL){
set_header(header,400,NULL,0);
send(new_fd, header, strlen(header), 0);
return 0;
}
else if(strcmp(request_type,"POST")==0){
set_header(header,501,NULL,0);
send(new_fd, header, strlen(header), 0);
return 0;
}
path = get_path(char_buffer);
if(strcmp("/",path)==0){
path = (char*)config.default_page;
// path = config.default_page;
}
char *root_dir=malloc(100);
memset(root_dir,0,sizeof root_dir);
strcpy(root_dir,config.root_dir);
path=strcat(root_dir,path);
//printf("11111111111 real %s \n", path);
/* ******* REQUEST FILE FOUND ********** */
if(access(path, F_OK ) != -1){
send_content(path,new_fd,200);
//printf("OKKKKKKKKK %s \n", path);
}
else{
memset(root_dir,0,100);
strcpy(root_dir,config.root_dir);
path=strcat(root_dir,"/");
path=strcat(root_dir,config.error_page);
//printf("ERRRRRRRRRRORRR %s \n",path);
send_content(path,new_fd,404);
}
// if (send(new_fd, "Hello, world!", 13, 0) == -1)
// perror("send");
free(char_buffer);
close(new_fd);
exit(0);
}
close(new_fd); // parent doesn't need this
}
printf("Config loaded from 'ask.ini': version=%d, backlog=%d,max_header_size=%d , http_version=%s\n",
config.version, config.backlog, config.max_header_size, config.http_version);
return 0;
}