slidescript/src/network.c

479 lines
15 KiB
C

/*
SlideScript - minimalistic top-down scripting language.
(C) Copyright 2014-2022 Chris Dorman - some rights reserved (GPLv2)
View README file supplied with this software for more details
*/
#include "inc/deps.h"
#include "inc/network.h"
#include "inc/lexer.h"
#include "inc/util.h"
// Structure of file types for nethttp
// mimetype struct, searched through when checking mimetype
struct {
char *ext;
char *filetype;
} file_extensions [] = {
{"gif", "image/gif" },
{"jpg", "image/jpeg" },
{"jpeg","image/jpeg" },
{"png", "image/png" },
{"zip", "application/zip" },
{"gz", "application/gzip" },
{"tar", "application/x-tar" },
{"htm", "text/html" },
{"html","text/html" },
{"cgi", "text/cgi" },
{"asp","text/asp" },
{"xml", "text/xml" },
{"js", "text/js" },
{"css", "text/css" },
{"c", "text/plain" },
{"h", "text/plain" },
{"md", "text/plain" },
{"txt", "text/plain" },
{"ss", "text/plain" },
{"sh", "text/plain" },
{"ogg", "audio/ogg" },
{"mp3", "audio/mpeg" },
{0,0}
};
/* Serve process function, on connection catch buffer from socket */
char *snet_process_connection(int sockfd)
{
char *buff = qmalloc(QM_SS, 1024);
char *endbuff = qmalloc(QM_SS, 1024);
int rr, wr, ii;
bzero(buff, 1024);
bzero(endbuff, 1024);
usleep(50000);
ii = 1;
// read the message from client and copy it in buffer
while ( (rr = read(sockfd, buff, 1024)) > 0)
{
if(ii > 1)
{
endbuff = qrealloc(endbuff, (strlen(endbuff) + 1024));
//printf("%s\n", buff);
memcpy(&endbuff[(1024 * ii) - 1024], buff, rr);
if(rr < 1024) break;
}
else
{
memcpy(endbuff, buff, rr);
if(rr < 1024) break;
}
if(rr == 1024)
{
ii++;
continue;
}
}
// if msg contains "Exit" then server exit, meets search, respond.
if (strncmp("exit", buff, 4) == 0)
{
wr = write(sockfd, "ss:ok", 5);
if(wr == -1) syn_warn("ss:warn:network write error");
syn_warn("ss:server:client called exit");
return NULL;
}
else
{
wr = write(sockfd, "ss:ok", 5);
if(wr == -1) syn_warn("ss:warn:network write error");
// Get random filename for lastly pushing to save file
char *randTokenName = malloc(17);
FILE *socket_save = NULL;
gen_random_string(randTokenName, 16);
socket_save = fopen(randTokenName, "wb");
if(strlen(endbuff) > 0)
{
fprintf(socket_save, "%s", endbuff);
printf("ss:server:client buffer saved as '%s'\n", randTokenName);
fflush(stdout);
}
fclose(socket_save);
free(randTokenName); // Free malloc, i'm done
}
return "ok";
}
/* Main netlisten function */
void snet_listen(int port)
{
int sockfd, connfd, length, pid;
struct sockaddr_in servaddr;
struct sockaddr_in cliaddr;
// socket create and verification
sockfd = socket(AF_INET, SOCK_STREAM, 0);
if (sockfd == -1)
{
syn_error("ss:error:network socket creation failed");
}
bzero(&servaddr, sizeof(servaddr));
// assign IP, PORT
servaddr.sin_family = AF_INET;
servaddr.sin_addr.s_addr = htonl(INADDR_ANY);
servaddr.sin_port = htons(port);
// Binding newly created socket to given IP and verification
if ((bind(sockfd, (struct sockaddr*)&servaddr, sizeof(servaddr))) != 0)
{
syn_error("ss:error:network bind failed");
}
// Now server is ready to listen and verification
if ((listen(sockfd, 64)) != 0)
{
syn_error("ss:error:network listen failed");
}
else
{
printf("ss:server:listening on '%d'\n", port);
fflush(stdout);
}
length = sizeof(cliaddr);
// Accept the data packet from client and verification
while ( (connfd = accept(sockfd, (struct sockaddr*)&cliaddr, (socklen_t*) &length)) )
{
char connIp[33];
struct sockaddr_in* pV4Addr = (struct sockaddr_in*)&cliaddr;
struct in_addr ipAddr = pV4Addr->sin_addr;
if((pid = fork()) == -1)
{
syn_warn("ss:warn:netlisten failed to fork");
close(connfd);
continue;
}
else if(pid > 0)
{
close(connfd);
continue;
}
else if(pid == 0)
{
inet_ntop(AF_INET, &ipAddr, connIp, 32);
printf("ss:server:connection from %s\n", connIp);
char *sc_return = snet_process_connection(connfd);
if(sc_return == NULL)
{
break;
}
close(connfd);
}
}
// After chatting close the socket
close(sockfd);
}
// Main nettoss function
void snet_toss(char *address, int port, char *string)
{
int sockfd;
struct sockaddr_in servaddr;
// socket create and varification
sockfd = socket(AF_INET, SOCK_STREAM, 0);
if (sockfd == -1)
{
syn_warn("ss:warn:network, failed to create socket");
return;
}
bzero(&servaddr, sizeof(servaddr));
// assign IP, PORT
servaddr.sin_family = AF_INET;
servaddr.sin_addr.s_addr = inet_addr(address);
servaddr.sin_port = htons(port);
// connect the client socket to server socket
if (connect(sockfd, (struct sockaddr*)&servaddr, sizeof(servaddr)) != 0)
{
syn_warn("ss:warn:network, failed to connect to socket");
close(sockfd);
return;
}
else
{
printf("ss:client:connected to %s:%d\n", address, port);
fflush(stdout);
}
// If write reports -1, exit on error.
if(write(sockfd, string, strlen(string)) < 0)
{
syn_warn("ss:warn:nettoss, socket write error!");
}
while(sockfd != -1)
{
char *buf = malloc(128);
bzero(buf, 128);
usleep(500000);
int listener = read(sockfd, buf, 128);
if(listener < 0)
{
syn_warn("ss:warn:nettoss, server killed socket");
free(buf);
break;
}
else if(listener > 0)
{
if(strncmp("kill", buf, 4) == 0)
{
printf("ss:client: server called %s!\n", buf);
free(buf);
break;
}
else if(strncmp("ss:ok", buf, 5) == 0)
{
free(buf);
fflush(stdout);
break;
}
else
{
printf("ss:server: %s\n", buf);
fflush(stdout);
free(buf);
break;
}
}
}
// close the socket after pushing content
close(sockfd);
}
// Process connection of nethttp
void snet_http_process(int fd, int forkval)
{
int j, file_fd, buflen, len;
long i, filesize;
char *fstr = NULL;
static char buffer[HTTP_BUFSIZE+1];
// Check to see if file is corrupted
filesize = read(fd,buffer,HTTP_BUFSIZE);
if(filesize == 0 || filesize == -1) {
sprintf(buffer,"HTTP/1.0 200 OK\r\nContent-Type: text/html\r\n\r\n");
if(write(fd,buffer,strlen(buffer)) < 0) syn_error("ss:net:http socket write error");
sprintf(buffer,"<html><head><title>ss:http:missed header</title></head><body><h2>301: Missed header call</h2>" \
"<p>Could not locate file</p><br /><br /><center>ss:http:%s</center></body></html>", VERSION);
if(write(fd,buffer,strlen(buffer)) < 0) syn_error("ss:net:http socket write error");
exit(1);
}
if(filesize > 0 && filesize < HTTP_BUFSIZE) {
buffer[filesize]=0;
} else {
buffer[0]=0;
}
for(i=0;i<filesize;i++) {
if(buffer[i] == '\r' || buffer[i] == '\n') {
buffer[i]='*';
}
}
if(strncmp(buffer,"GET ",4) && strncmp(buffer,"get ",4)) {
sprintf(buffer,"HTTP/1.0 501 Not Implemented\r\nContent-Type: text/html\r\n\r\n");
if(write(fd,buffer,strlen(buffer)) < 0) syn_error("ss:net:http socket write error");
sprintf(buffer,"<html><head><title>ss:http: server not implemented</title></head><body>" \
"<h2>501: Not Implemented</h2><p>ss:http only supports simple GET requests via HTTP/1.0</p>" \
"<br /><br /><center>ss:http:%s</center></body></html>", VERSION);
if(write(fd,buffer,strlen(buffer)) < 0) syn_error("ss:net:http socket write error");
exit(1);
}
// This gets the file name from "GET /..."
for(i=4;i<HTTP_BUFSIZE;i++) {
if(buffer[i] == ' ') {
buffer[i] = 0;
break;
}
}
// Check if connection is trying to view in directories behind webserver root
for(j=0;j<i-1;j++) {
if(buffer[j] == '.' && buffer[j+1] == '.') {
sprintf(buffer,"HTTP/1.0 200 OK\r\nContent-Type: text/html\r\n\r\n");
if(write(fd,buffer,strlen(buffer)) < 0) syn_error("ss:net:http socket write error");
sprintf(buffer,"<html><head><title>ss:http: parent directory not available</title></head><body><h2>FORBIDDEN: parent directory</h2>" \
"<p>You do not have access to view ../. Blocked.</p><br /><br /><center>ss:http:%s</center></body></html>", VERSION);
if(write(fd,buffer,strlen(buffer)) < 0) syn_error("ss:net:http socket write error");
exit(1);
}
}
// If client is connecting with GET / (root of website) then send index
if( !strncmp(&buffer[0],"GET /\0",6) || !strncmp(&buffer[0],"get /\0",6) ) {
if(file_exists("index.html")) {
strcpy(buffer,"GET /index.html");
} else {
sprintf(buffer,"HTTP/1.0 200 OK\r\nContent-Type: text/html\r\n\r\n");
if(write(fd,buffer,strlen(buffer)) < 0) syn_error("ss:net:http socket write error");
sprintf(buffer,"<html><head><title>ss:http: file not found</title></head><body><h2>File not found</h2>" \
"<p>Could not locate index.html</p><br /><br /><center>ss:http:%s</center></body></html>", VERSION);
if(write(fd,buffer,strlen(buffer)) < 0) syn_error("ss:net:http socket write error");
exit(1);
}
}
// Get file mimetype
buflen=strlen(buffer);
for(i=0;file_extensions[i].ext != 0;i++) {
len = strlen(file_extensions[i].ext);
if(!strncmp(&buffer[buflen-len], file_extensions[i].ext, len)) {
fstr = file_extensions[i].filetype;
break;
}
}
if(fstr == NULL)
{
fstr = "application/octet-stream";
}
if(forkval == 0)
{
printf("ss:net:http request %s, %s\n", &buffer[5], fstr);
fflush(stdout);
}
if(is_dir(&buffer[5]) == 1)
{
sprintf(buffer,"HTTP/1.0 200 OK\r\nContent-Type: text/html\r\n\r\n");
if(write(fd,buffer,strlen(buffer)) < 0) syn_error("ss:net:http socket write error");
sprintf(buffer,"<html><head><title>ss:http: directory</title></head><body><h2>Directory</h2>" \
"<p>No directory listings at this time...</p><br /><br /><center>ss:http:%s</center></body></html>", VERSION);
if(write(fd,buffer,strlen(buffer)) < 0) syn_error("ss:net:http socket write error");
exit(1);
}
// See if the web server can open requested file from browser
if(( file_fd = open(&buffer[5],O_RDONLY)) == -1) {
sprintf(buffer,"HTTP/1.0 404 Not Found\r\nContent-Type: text/html\r\n\r\n");
if(write(fd,buffer,strlen(buffer)) < 0) syn_error("ss:net:http socket write error");
sprintf(buffer,"<html><head><title>ss:http:file not found</title></head><body><h2>404: File not found</h2>" \
"<p>Could not locate file</p><br /><br /><center>ss:http:%s</center></body></html>", VERSION);
if(write(fd,buffer,strlen(buffer)) < 0) syn_error("ss:net:http socket write error");
exit(1);
}
sprintf(buffer,"HTTP/1.0 200 OK\r\nContent-Type: %s\r\n\r\n", fstr);
if(write(fd,buffer,strlen(buffer)) < 0) syn_error("ss:net:http socket write error");
while ((filesize = read(file_fd, buffer, HTTP_BUFSIZE)) > 0) {
if(write(fd,buffer,filesize) < 0) syn_error("ss:net:http socket write error");
}
#ifdef LINUX
sleep(1);
#endif
exit(0);
}
// Main function for nethttp //
void snet_http(int port, int forkval)
{
// Intergers for ports, sockets, etc
int i, pid, listenfd, socketfd, hit;
socklen_t length;
// structs for socket addresses, these are always needed!
static struct sockaddr_in cli_addr;
static struct sockaddr_in serv_addr;
// This forks the httpd process into the background, if forkval > 0
if(forkval > 0)
{
if(fork() != 0)
syn_error("ss:net:http forked");
#ifndef BSD
signal(SIGCLD, SIG_IGN);
#endif
signal(SIGHUP, SIG_IGN);
for(i=0;i<32;i++) {
close(i);
}
}
setpgid(0, 0);
if(forkval < 1)
{
printf("ss:starting httpd...\n");
fflush(stdout);
}
// Check if socket succeeded to open
if((listenfd = socket(AF_INET, SOCK_STREAM,0)) <0) {
printf("ss:network: failed to open socket\n");
fflush(stdout);
exit(1);
}
// check if valid port number
if(port < 0 || port >65534) {
syn_error("ss:net:http incorrect port range (vaild 1 - 65534)");
}
// binds the socket to a specific port and address
// in this case, 0.0.0.0 and the port you choose
serv_addr.sin_family = AF_INET;
serv_addr.sin_addr.s_addr = htonl(INADDR_ANY);
serv_addr.sin_port = htons(port);
if(bind(listenfd, (struct sockaddr *)&serv_addr,sizeof(serv_addr)) <0) {
syn_error("ss:net:http failed to bind to socket");
}
if( listen(listenfd,64) <0) {
syn_error("ss:net:http failed to listen to socket");
}
for(hit=1; ;hit++) {
length = sizeof(cli_addr);
// Check if server succeeded to accept connection
if((socketfd = accept(listenfd, (struct sockaddr *)&cli_addr, &length)) < 0) {
syn_error("ss:net:http failed to accept connection");
}
// Fork, and die if server fails to fork process
if((pid = fork()) < 0) {
syn_error("ss:net:http failed to fork connection");
}
else {
if(pid == 0) {
close(listenfd);
snet_http_process(socketfd, forkval);
} else {
close(socketfd);
}
}
}
}