liveserver

simple live web server for local developments
git clone https://noulin.net/git/liveserver.git
Log | Files | Refs | README | LICENSE

commit 5c65f44299826c55b268501d35bffae5ecfb7d99
parent d61d2bc59df349564f399318a10fe7345b9c7a2c
Author: Remy Noulin <loader2x@gmail.com>
Date:   Sun, 12 Nov 2017 21:14:48 +0100

initial commit

.gitignore    |   2 +
index.html    |  16 ++
inoty.h       |  10 ++
live-server.c | 448 +++++++++++++++++++++++++++++++++++++++++++++++++++++
package.yml   |  20 +++
page2.html    |  14 ++
scott.jpg     | Bin 0 -> 23251 bytes
server.c      | 448 +++++++++++++++++++++++++++++++++++++++++++++++++++++
server.log    |   8 +
utilities.c   | 482 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
utilities.h   |  74 +++++++++
11 files changed, 1522 insertions(+)

Diffstat:
M.gitignore | 2++
Aindex.html | 16++++++++++++++++
Ainoty.h | 10++++++++++
Alive-server.c | 448+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Apackage.yml | 21+++++++++++++++++++++
Apage2.html | 14++++++++++++++
Ascott.jpg | 0
Aserver.c | 448+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Aserver.log | 8++++++++
Autilities.c | 482+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Autilities.h | 74++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
11 files changed, 1523 insertions(+), 0 deletions(-)

diff --git a/.gitignore b/.gitignore @@ -1,3 +1,5 @@ +a + # Prerequisites *.d diff --git a/index.html b/index.html @@ -0,0 +1,16 @@ +<!DOCTYPE html> +<html lang="en"> +<head> +<title>Oscar's web server example</title> +</head> +<BODY BGCOLOR="grey"> + <center> + <h1>Welcome to the default homepage!</h1> + <img src="scott.jpg" /> + <p>Here's an image of scott pilgrim... he's really cool and you can learn about him <a href="http://scottpilgrim.com">here</a>.</p> + </center> + <p> + Just some basic default page. For another page... go <a href="page2.html">here</a>. + </p> +</body> +</html> diff --git a/inoty.h b/inoty.h @@ -0,0 +1,10 @@ + +#include <sys/inotify.h> + +#define EVENT_SIZE ( sizeof (struct inotify_event) ) +#define EVENT_BUF_LEN ( 1024 * ( EVENT_SIZE + 16 ) ) + +#define inotiFy_init inotify_init +#define inotiFy_add_watch inotify_add_watch +#define inotiFy_event inotify_event +#define inotiFy_rm_watch inotify_rm_watch diff --git a/live-server.c b/live-server.c @@ -0,0 +1,448 @@ +#! /usr/bin/env sheepy + +/* + * project: miniweb + * author: Oscar Sanchez (oms1005@gmail.com) + * HTTP Server + * WORKS ON BROWSERS TOO! + * Inspired by IBM's nweb +*/ + +#include <stdio.h> +#include <stdlib.h> +#include <unistd.h> +#include <errno.h> +#include <string.h> +#include <fcntl.h> +#include <signal.h> +#include <sys/types.h> +#include <sys/socket.h> +#include <netinet/in.h> +#include <arpa/inet.h> +#include "inoty.h" + +#include "libsheepyObject.h" +#include "utilities.h" + +#define BUFSIZE 8096 +#define ERROR 42 +#define SORRY 43 +#define LOG 44 + +i64 waitTime = 0; + +smallArrayt *dirList; + +const char injected[] = "<!-- Code injected by live-server -->\n\ +<script type=\"text/javascript\">\n\ + // <![CDATA[ <-- For SVG support\n\ + if ('WebSocket' in window) {\n\ + (function() {\n\ + function refreshCSS() {\n\ + var sheets = [].slice.call(document.getElementsByTagName(\"link\"));\n\ + var head = document.getElementsByTagName(\"head\")[0];\n\ + for (var i = 0; i < sheets.length; ++i) {\n\ + var elem = sheets[i];\n\ + head.removeChild(elem);\n\ + var rel = elem.rel;\n\ + if (elem.href && typeof rel != \"string\" || rel.length == 0 || rel.toLowerCase() == \"stylesheet\") {\n\ + var url = elem.href.replace(/(&|\\?)_cacheOverride=\\d+/, '');\n\ + elem.href = url + (url.indexOf('?') >= 0 ? '&' : '?') + '_cacheOverride=' + (new Date().valueOf());\n\ + }\n\ + head.appendChild(elem);\n\ + }\n\ + }\n\ + var protocol = window.location.protocol === 'http:' ? 'ws://' : 'wss://';\n\ + var address = protocol + window.location.host + window.location.pathname + '/ws';\n\ + var socket = new WebSocket(address);\n\ + socket.onmessage = function(msg) {\n\ + if (msg.data == 'reload') window.location.reload();\n\ + else if (msg.data == 'refreshcss') refreshCSS();\n\ + };\n\ + console.log('Live reload enabled.');\n\ + })();\n\ + }\n\ + // ]]>\n\ +</script>\n\ +"; + +struct { + char *ext; + char *filetype; +} extensions [] = { + {"gif", "image/gif" }, + {"jpg", "image/jpeg"}, + {"jpeg","image/jpeg"}, + {"png", "image/png" }, + {"zip", "image/zip" }, + {"gz", "image/gz" }, + {"tar", "image/tar" }, + {"htm", "text/html" }, + {"html","text/html" }, + {"php", "image/php" }, + {"cgi", "text/cgi" }, + {"asp", "text/asp" }, + {"jsp", "image/jsp" }, + {"xml", "text/xml" }, + {"js", "text/js" }, + {"css", "text/css" }, + {"//ws", "websocket"}, + + {0,0} }; + +#define WEBSOCKET_SERVER_RESPONSE "258EAFA5-E914-47DA-95CA-C5AB0DC85B11" + +#define WEBSOCK_PACKAGE_NAME "s" +#define WEBSOCK_PACKAGE_VERSION "0" + +void slog(int type, char *s1, char *s2, int num) +{ + int fd ; + char logbuffer[BUFSIZE*2]; + + switch (type) { + case ERROR: (void)sprintf(logbuffer,"ERROR: %s:%s Errno=%d exiting pid=%d",s1, s2, errno,getpid()); break; + case SORRY: + (void)sprintf(logbuffer, "<HTML><BODY><H1>Web Server Sorry: %s %s</H1></BODY></HTML>\r\n", s1, s2); + (void)write(num,logbuffer,strlen(logbuffer)); + (void)sprintf(logbuffer,"SORRY: %s:%s",s1, s2); + break; + case LOG: (void)sprintf(logbuffer," INFO: %s:%s:%d",s1, s2,num); break; + } + + if((fd = open("server.log", O_CREAT| O_WRONLY | O_APPEND,0644)) >= 0) { + (void)write(fd,logbuffer,strlen(logbuffer)); + (void)write(fd,"\n",1); + (void)close(fd); + } + if(type == ERROR || type == SORRY) exit(3); +} + +void web(int fd, int hit) +{ + int j, file_fd, buflen, len; + long i, ret; + char * fstr; + static char buffer[BUFSIZE+1]; + + ret =read(fd,buffer,BUFSIZE); + if(ret == 0 || ret == -1) { + slog(SORRY,"failed to read browser request","",fd); + } + if(ret > 0 && ret < BUFSIZE) + buffer[ret]=0; + else buffer[0]=0; + + smallStringt *reqS = allocG(buffer); + smallArrayt *req = splitG(reqS, "\r\n"); + char *clientKey = NULL; + char *origin = NULL; + char *protocol = NULL; // protocol not parsed, firefox 52 doesnt send it + forEachSmallArray(req, S) { + castS(s,S); + if (startsWithG(s, "Sec-WebSocket-Key:")) { + clientKey = ssGet(s) + 19; + } + if(startsWithSG(s, "Origin:")) { + origin = ssGet(s) + 8; + } + finishG(s); + } + + for(i=0;i<ret;i++) + if(buffer[i] == '\r' || buffer[i] == '\n') + buffer[i]='*'; + slog(LOG,"request",buffer,hit); + + if( strncmp(buffer,"GET ",4) && strncmp(buffer,"get ",4) ) + slog(SORRY,"Only simple GET operation supported",buffer,fd); + + for(i=4;i<BUFSIZE;i++) { + if(buffer[i] == ' ') { + buffer[i] = 0; + break; + } + } + + for(j=0;j<i-1;j++) + if(buffer[j] == '.' && buffer[j+1] == '.') + slog(SORRY,"Parent directory (..) path names not supported",buffer,fd); + + if( !strncmp(&buffer[0],"GET /\0",6) || !strncmp(&buffer[0],"get /\0",6) ) + (void)strcpy(buffer,"GET /index.html"); + + bool isHTML = false; + bool isWebsocket = false; + + buflen=strlen(buffer); + fstr = (char *)0; + for(i=0;extensions[i].ext != 0;i++) { + len = strlen(extensions[i].ext); + if( !strncmp(&buffer[buflen-len], extensions[i].ext, len)) { + fstr =extensions[i].filetype; + if (startsWithG(extensions[i].ext, "htm")) isHTML = true; + else if (eqS(extensions[i].ext, "//ws")) isWebsocket = true; + break; + } + } + if(fstr == 0) slog(SORRY,"file extension type not supported",buffer,fd); + + if (isWebsocket) { + // WEBSOCKET HANDLING + + slog(LOG,"websocket","detected",hit); + slog(LOG,"key",clientKey,hit); + + SHA1Context shactx; + SHA1Reset(&shactx); + + char buf[1024]; + sprintf(buf, "%s%s", clientKey, WEBSOCKET_SERVER_RESPONSE); + + SHA1Input(&shactx, (unsigned char *) buf, strlen(buf)); + SHA1Result(&shactx); + + char sha1buf[45]; + unsigned char sha1mac[20]; + + sprintf(sha1buf, "%08x%08x%08x%08x%08x", shactx.Message_Digest[0], + shactx.Message_Digest[1], shactx.Message_Digest[2], + shactx.Message_Digest[3], shactx.Message_Digest[4]); + + for (i16 n = 0; n < (strlen(sha1buf) / 2); n++) { + sscanf(sha1buf + (n * 2), "%02hhx", sha1mac + n); + } + + char base64buf[256]; + base64_encode(sha1mac, 20, base64buf, 256); + memset(buf, 0, 1024); + snprintf(buf, 1024, "HTTP/1.1 101 Switching Protocols\r\n" + "Server: %s/%s\r\n" + "Upgrade: websocket\r\n" + "%s%s%s" + "Connection: Upgrade\r\n" + "Sec-WebSocket-Accept: %s\r\n%s%s" + "Access-Control-Allow-Headers: content-type\r\n\r\n", WEBSOCK_PACKAGE_NAME, + WEBSOCK_PACKAGE_VERSION, origin ? "Access-Control-Allow-Origin: " : "", origin ? origin : "", origin ? "\r\n" : "", base64buf, protocol ? protocol : "", protocol ? "\r\n" : ""); + + (void)write(fd,buf,strlen(buf)); + + // INOTIFY SETUP + + // wait for changes and reload page + enum {yes, no}; + i8 foundRelevantUpdate = no; + while (foundRelevantUpdate == no) { + // creating the INOTIFY instance + int length; + char ibuffer[EVENT_BUF_LEN]; + int ifd = inotiFy_init(); + if (ifd == -1) { + perror("inotiFy_init error: "); + } + + int wd; // watch descriptor + + forEachSmallArray(dirList, E) { + castS(e, E); + wd = inotiFy_add_watch( ifd, ssGet(e), IN_CREATE | IN_DELETE | IN_MODIFY); + finishG(e); + } + + // read to determine the event change happens on “.” directory. Actually this read blocks until the change event occurs + length = read( ifd, ibuffer, EVENT_BUF_LEN );; + + if (length == -1) { + perror("read error: "); + } + + u32 i = 0; + // actually read return the list of change events happens. Here, read the change event one by one and process it accordingly. + while (( i < length )) { + struct inotiFy_event *event = ( struct inotiFy_event * ) &ibuffer[ i ]; + + for(i=0;extensions[i].ext != 0;i++) { + if (endsWithG(event->name, extensions[i].ext)) { + if ((event->mask & IN_CREATE) || (event->mask & IN_DELETE) || (event->mask & IN_MODIFY)) { + foundRelevantUpdate = yes; + } + } + } + + i += EVENT_SIZE + event->len; + } + + + // removing the “/tmp” directory from the watch list. + inotiFy_rm_watch( ifd, wd ); + + // closing the INOTIFY instance + close(ifd); + } + + if (waitTime > 0) { + // wait before reloading + sleep(waitTime); + } + + // WEBSOCKET SEND RELOAD TO CLIENT + + // send reload + u32 fin = 1; + u32 op = 1; + u32 plen = 6; + u32 blen = plen+2; + + buf[0] = (1 << 7) + fin; // 0x81 + buf[1] = plen; + /* uint16_t *b = (uint16_t*)buf; */ + /* *b = (plen << 9) + (op << 4) + fin; */ + strcpy(buf+2, "reload"); + + (void)write(fd,buf,blen); + } else { + + // SERVE REGULAR HTTP REQUESTS + + char *serveFile = &buffer[5]; + + if (isHTML) { + size_t endBodyAt = 0; + createAllocateSmallArray(f); + readFileG(f, &buffer[5]); + enumerateSmallArray(f, S, n) { + castS(s, S); + if (hasG(lowerG(s), "</body>")) { + endBodyAt = n; + finishG(s); + break; + } + finishG(s); + } + injectSG(f, endBodyAt, injected); + writeFileG(f, "a"); + terminateG(f); + serveFile = "a"; + } + + if(( file_fd = open(serveFile,O_RDONLY)) == -1) + slog(SORRY, "failed to open file",serveFile,fd); + + slog(LOG,"SEND",serveFile,hit); + + (void)sprintf(buffer,"HTTP/1.0 200 OK\r\nContent-Type: %s\r\n\r\n", fstr); + (void)write(fd,buffer,strlen(buffer)); + + while ((ret = read(file_fd, buffer, BUFSIZE)) > 0 ) { + (void)write(fd,buffer,ret); + } + } + + terminateManyG(reqS, req); + +#ifdef LINUX + sleep(1); +#endif + exit(1); +} + + +int main(int argc, char **argv) +{ + int i, port, pid, listenfd, socketfd, hit; + size_t length; + static struct sockaddr_in cli_addr; + static struct sockaddr_in serv_addr; + char *portS; + char *dir = NULL; + + // defaults + port = 8080; + portS = "8080"; + + forEachS(argv, a) { + if (startsWithG(a, "-port=")) { + portS = a+6; + port = parseInt(portS); + } + else if (startsWithG(a, "-path=")) { + dir = a+6; + } + else if (startsWithG(a, "-wait=")) { + waitTime = parseInt(a+6); + } + else if (startsWithG(a, "--help") or startsWithG(a, "-h") or startsWithG(a, "-?")) { + printf("usage: server [-port=80] [-path=server_directory] [-wait=1]\n" + "\twait unit is second.\n" + "\tExample: server -port=8080 -path=./ -wait=1\n\n" + "\tOnly Supports:"); + for(i=0;extensions[i].ext != 0;i++) + (void)printf(" %s",extensions[i].ext); + + (void)printf("\n\tNot Supported: directories / /etc /bin /lib /tmp /usr /dev /sbin \n" + ); + XSUCCESS + } + } + + if (dir) { + if( !strncmp(dir,"/" ,2 ) || !strncmp(dir,"/etc", 5 ) || + !strncmp(dir,"/bin",5 ) || !strncmp(dir,"/lib", 5 ) || + !strncmp(dir,"/tmp",5 ) || !strncmp(dir,"/usr", 5 ) || + !strncmp(dir,"/dev",5 ) || !strncmp(dir,"/sbin",6) ){ + (void)printf("ERROR: Bad top directory %s, see server -?\n",dir); + exit(3); + } + if(chdir(dir) == -1){ + (void)printf("ERROR: Can't Change to directory %s\n",dir); + exit(4); + } + } + + dirList = allocG(rtSmallArrayt); + execO("find . -type d", dirList, NULL); + + if(fork() != 0) + return 0; + (void)signal(SIGCLD, SIG_IGN); + (void)signal(SIGHUP, SIG_IGN); + for(i=0;i<32;i++) + (void)close(i); + (void)setpgrp(); + + slog(LOG,"http server starting",portS,getpid()); + + if((listenfd = socket(AF_INET, SOCK_STREAM,0)) <0) + slog(ERROR, "system call","socket",0); + + if(port < 0 || port >60000) + slog(ERROR,"Invalid port number try [1,60000]",portS,0); + 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) + slog(ERROR,"system call","bind",0); + if( listen(listenfd,64) <0) + slog(ERROR,"system call","listen",0); + + for(hit=1; ;hit++) { + length = sizeof(cli_addr); + if((socketfd = accept(listenfd, (struct sockaddr *)&cli_addr, (socklen_t *)&length)) < 0) + slog(ERROR,"system call","accept",0); + + if((pid = fork()) < 0) { + slog(ERROR,"system call","fork",0); + } + else { + if(pid == 0) { + (void)close(listenfd); + web(socketfd,hit); + } else { + (void)close(socketfd); + } + } + } + + terminateG(dirList); + +} diff --git a/package.yml b/package.yml @@ -0,0 +1,20 @@ +--- + name: live-server + version: 0.0.1 + description: "explaination" + bin: ./live-server.c + #cflags: -DA + #lflags: -lpcre + repository: + type: git + url: git+https://github.com/USER/live-server.git + keywords: + #- utility + - command + author: Anonymous + license: MIT + bugs: + url: https://github.com/USER/live-server/issues + homepage: https://github.com/USER/live-server#readme + #dependencies: + # md4c: + \ No newline at end of file diff --git a/page2.html b/page2.html @@ -0,0 +1,14 @@ +<!DOCTYPE html> +<html lang="en"> +<title>Oscar's web server example</title> +<BODY BGCOLOR="grey"> +<center> +<H1>PAGE 2</H1> +<p> +This is page 2! Welcome :) +</p> + +</center> +</table> +</BODY> +</HTML> diff --git a/scott.jpg b/scott.jpg Binary files differ. diff --git a/server.c b/server.c @@ -0,0 +1,448 @@ +#! /usr/bin/env sheepy + +/* + * project: miniweb + * author: Oscar Sanchez (oms1005@gmail.com) + * HTTP Server + * WORKS ON BROWSERS TOO! + * Inspired by IBM's nweb +*/ + +#include <stdio.h> +#include <stdlib.h> +#include <unistd.h> +#include <errno.h> +#include <string.h> +#include <fcntl.h> +#include <signal.h> +#include <sys/types.h> +#include <sys/socket.h> +#include <netinet/in.h> +#include <arpa/inet.h> +#include "inoty.h" + +#include "libsheepyObject.h" +#include "utilities.h" + +#define BUFSIZE 8096 +#define ERROR 42 +#define SORRY 43 +#define LOG 44 + +i64 waitTime = 0; + +smallArrayt *dirList; + +const char injected[] = "<!-- Code injected by live-server -->\n\ +<script type=\"text/javascript\">\n\ + // <![CDATA[ <-- For SVG support\n\ + if ('WebSocket' in window) {\n\ + (function() {\n\ + function refreshCSS() {\n\ + var sheets = [].slice.call(document.getElementsByTagName(\"link\"));\n\ + var head = document.getElementsByTagName(\"head\")[0];\n\ + for (var i = 0; i < sheets.length; ++i) {\n\ + var elem = sheets[i];\n\ + head.removeChild(elem);\n\ + var rel = elem.rel;\n\ + if (elem.href && typeof rel != \"string\" || rel.length == 0 || rel.toLowerCase() == \"stylesheet\") {\n\ + var url = elem.href.replace(/(&|\\?)_cacheOverride=\\d+/, '');\n\ + elem.href = url + (url.indexOf('?') >= 0 ? '&' : '?') + '_cacheOverride=' + (new Date().valueOf());\n\ + }\n\ + head.appendChild(elem);\n\ + }\n\ + }\n\ + var protocol = window.location.protocol === 'http:' ? 'ws://' : 'wss://';\n\ + var address = protocol + window.location.host + window.location.pathname + '/ws';\n\ + var socket = new WebSocket(address);\n\ + socket.onmessage = function(msg) {\n\ + if (msg.data == 'reload') window.location.reload();\n\ + else if (msg.data == 'refreshcss') refreshCSS();\n\ + };\n\ + console.log('Live reload enabled.');\n\ + })();\n\ + }\n\ + // ]]>\n\ +</script>\n\ +"; + +struct { + char *ext; + char *filetype; +} extensions [] = { + {"gif", "image/gif" }, + {"jpg", "image/jpeg"}, + {"jpeg","image/jpeg"}, + {"png", "image/png" }, + {"zip", "image/zip" }, + {"gz", "image/gz" }, + {"tar", "image/tar" }, + {"htm", "text/html" }, + {"html","text/html" }, + {"php", "image/php" }, + {"cgi", "text/cgi" }, + {"asp", "text/asp" }, + {"jsp", "image/jsp" }, + {"xml", "text/xml" }, + {"js", "text/js" }, + {"css", "text/css" }, + {"//ws", "websocket"}, + + {0,0} }; + +#define WEBSOCKET_SERVER_RESPONSE "258EAFA5-E914-47DA-95CA-C5AB0DC85B11" + +#define WEBSOCK_PACKAGE_NAME "s" +#define WEBSOCK_PACKAGE_VERSION "0" + +void slog(int type, char *s1, char *s2, int num) +{ + int fd ; + char logbuffer[BUFSIZE*2]; + + switch (type) { + case ERROR: (void)sprintf(logbuffer,"ERROR: %s:%s Errno=%d exiting pid=%d",s1, s2, errno,getpid()); break; + case SORRY: + (void)sprintf(logbuffer, "<HTML><BODY><H1>Web Server Sorry: %s %s</H1></BODY></HTML>\r\n", s1, s2); + (void)write(num,logbuffer,strlen(logbuffer)); + (void)sprintf(logbuffer,"SORRY: %s:%s",s1, s2); + break; + case LOG: (void)sprintf(logbuffer," INFO: %s:%s:%d",s1, s2,num); break; + } + + if((fd = open("server.log", O_CREAT| O_WRONLY | O_APPEND,0644)) >= 0) { + (void)write(fd,logbuffer,strlen(logbuffer)); + (void)write(fd,"\n",1); + (void)close(fd); + } + if(type == ERROR || type == SORRY) exit(3); +} + +void web(int fd, int hit) +{ + int j, file_fd, buflen, len; + long i, ret; + char * fstr; + static char buffer[BUFSIZE+1]; + + ret =read(fd,buffer,BUFSIZE); + if(ret == 0 || ret == -1) { + slog(SORRY,"failed to read browser request","",fd); + } + if(ret > 0 && ret < BUFSIZE) + buffer[ret]=0; + else buffer[0]=0; + + smallStringt *reqS = allocG(buffer); + smallArrayt *req = splitG(reqS, "\r\n"); + char *clientKey = NULL; + char *origin = NULL; + char *protocol = NULL; // protocol not parsed, firefox 52 doesnt send it + forEachSmallArray(req, S) { + castS(s,S); + if (startsWithG(s, "Sec-WebSocket-Key:")) { + clientKey = ssGet(s) + 19; + } + if(startsWithSG(s, "Origin:")) { + origin = ssGet(s) + 8; + } + finishG(s); + } + + for(i=0;i<ret;i++) + if(buffer[i] == '\r' || buffer[i] == '\n') + buffer[i]='*'; + slog(LOG,"request",buffer,hit); + + if( strncmp(buffer,"GET ",4) && strncmp(buffer,"get ",4) ) + slog(SORRY,"Only simple GET operation supported",buffer,fd); + + for(i=4;i<BUFSIZE;i++) { + if(buffer[i] == ' ') { + buffer[i] = 0; + break; + } + } + + for(j=0;j<i-1;j++) + if(buffer[j] == '.' && buffer[j+1] == '.') + slog(SORRY,"Parent directory (..) path names not supported",buffer,fd); + + if( !strncmp(&buffer[0],"GET /\0",6) || !strncmp(&buffer[0],"get /\0",6) ) + (void)strcpy(buffer,"GET /index.html"); + + bool isHTML = false; + bool isWebsocket = false; + + buflen=strlen(buffer); + fstr = (char *)0; + for(i=0;extensions[i].ext != 0;i++) { + len = strlen(extensions[i].ext); + if( !strncmp(&buffer[buflen-len], extensions[i].ext, len)) { + fstr =extensions[i].filetype; + if (startsWithG(extensions[i].ext, "htm")) isHTML = true; + else if (eqS(extensions[i].ext, "//ws")) isWebsocket = true; + break; + } + } + if(fstr == 0) slog(SORRY,"file extension type not supported",buffer,fd); + + if (isWebsocket) { + // WEBSOCKET HANDLING + + slog(LOG,"websocket","detected",hit); + slog(LOG,"key",clientKey,hit); + + SHA1Context shactx; + SHA1Reset(&shactx); + + char buf[1024]; + sprintf(buf, "%s%s", clientKey, WEBSOCKET_SERVER_RESPONSE); + + SHA1Input(&shactx, (unsigned char *) buf, strlen(buf)); + SHA1Result(&shactx); + + char sha1buf[45]; + unsigned char sha1mac[20]; + + sprintf(sha1buf, "%08x%08x%08x%08x%08x", shactx.Message_Digest[0], + shactx.Message_Digest[1], shactx.Message_Digest[2], + shactx.Message_Digest[3], shactx.Message_Digest[4]); + + for (i16 n = 0; n < (strlen(sha1buf) / 2); n++) { + sscanf(sha1buf + (n * 2), "%02hhx", sha1mac + n); + } + + char base64buf[256]; + base64_encode(sha1mac, 20, base64buf, 256); + memset(buf, 0, 1024); + snprintf(buf, 1024, "HTTP/1.1 101 Switching Protocols\r\n" + "Server: %s/%s\r\n" + "Upgrade: websocket\r\n" + "%s%s%s" + "Connection: Upgrade\r\n" + "Sec-WebSocket-Accept: %s\r\n%s%s" + "Access-Control-Allow-Headers: content-type\r\n\r\n", WEBSOCK_PACKAGE_NAME, + WEBSOCK_PACKAGE_VERSION, origin ? "Access-Control-Allow-Origin: " : "", origin ? origin : "", origin ? "\r\n" : "", base64buf, protocol ? protocol : "", protocol ? "\r\n" : ""); + + (void)write(fd,buf,strlen(buf)); + + // INOTIFY SETUP + + // wait for changes and reload page + enum {yes, no}; + i8 foundRelevantUpdate = no; + while (foundRelevantUpdate == no) { + // creating the INOTIFY instance + int length; + char ibuffer[EVENT_BUF_LEN]; + int ifd = inotiFy_init(); + if (ifd == -1) { + perror("inotiFy_init error: "); + } + + int wd; // watch descriptor + + forEachSmallArray(dirList, E) { + castS(e, E); + wd = inotiFy_add_watch( ifd, ssGet(e), IN_CREATE | IN_DELETE | IN_MODIFY); + finishG(e); + } + + // read to determine the event change happens on “.” directory. Actually this read blocks until the change event occurs + length = read( ifd, ibuffer, EVENT_BUF_LEN );; + + if (length == -1) { + perror("read error: "); + } + + u32 i = 0; + // actually read return the list of change events happens. Here, read the change event one by one and process it accordingly. + while (( i < length )) { + struct inotiFy_event *event = ( struct inotiFy_event * ) &ibuffer[ i ]; + + for(i=0;extensions[i].ext != 0;i++) { + if (endsWithG(event->name, extensions[i].ext)) { + if ((event->mask & IN_CREATE) || (event->mask & IN_DELETE) || (event->mask & IN_MODIFY)) { + foundRelevantUpdate = yes; + } + } + } + + i += EVENT_SIZE + event->len; + } + + + // removing the “/tmp” directory from the watch list. + inotiFy_rm_watch( ifd, wd ); + + // closing the INOTIFY instance + close(ifd); + } + + if (waitTime > 0) { + // wait before reloading + sleep(waitTime); + } + + // WEBSOCKET SEND RELOAD TO CLIENT + + // send reload + u32 fin = 1; + u32 op = 1; + u32 plen = 6; + u32 blen = plen+2; + + buf[0] = (1 << 7) + fin; // 0x81 + buf[1] = plen; + /* uint16_t *b = (uint16_t*)buf; */ + /* *b = (plen << 9) + (op << 4) + fin; */ + strcpy(buf+2, "reload"); + + (void)write(fd,buf,blen); + } else { + + // SERVE REGULAR HTTP REQUESTS + + char *serveFile = &buffer[5]; + + if (isHTML) { + size_t endBodyAt = 0; + createAllocateSmallArray(f); + readFileG(f, &buffer[5]); + enumerateSmallArray(f, S, n) { + castS(s, S); + if (hasG(lowerG(s), "</body>")) { + endBodyAt = n; + finishG(s); + break; + } + finishG(s); + } + injectSG(f, endBodyAt, injected); + writeFileG(f, "a"); + terminateG(f); + serveFile = "a"; + } + + if(( file_fd = open(serveFile,O_RDONLY)) == -1) + slog(SORRY, "failed to open file",serveFile,fd); + + slog(LOG,"SEND",serveFile,hit); + + (void)sprintf(buffer,"HTTP/1.0 200 OK\r\nContent-Type: %s\r\n\r\n", fstr); + (void)write(fd,buffer,strlen(buffer)); + + while ((ret = read(file_fd, buffer, BUFSIZE)) > 0 ) { + (void)write(fd,buffer,ret); + } + } + + terminateManyG(reqS, req); + +#ifdef LINUX + sleep(1); +#endif + exit(1); +} + + +int main(int argc, char **argv) +{ + int i, port, pid, listenfd, socketfd, hit; + size_t length; + static struct sockaddr_in cli_addr; + static struct sockaddr_in serv_addr; + char *portS; + char *dir = NULL; + + // defaults + port = 8080; + portS = "8080"; + + forEachS(argv, a) { + if (startsWithG(a, "-port=")) { + portS = a+6; + port = parseInt(portS); + } + else if (startsWithG(a, "-path=")) { + dir = a+6; + } + else if (startsWithG(a, "-wait=")) { + waitTime = parseInt(a+6); + } + else if (startsWithG(a, "--help") or startsWithG(a, "-h") or startsWithG(a, "-?")) { + printf("usage: server [-port=80] [-path=server_directory] [-wait=1]\n" + "\twait unit is second.\n" + "\tExample: server -port=8080 -path=./ -wait=1\n\n" + "\tOnly Supports:"); + for(i=0;extensions[i].ext != 0;i++) + (void)printf(" %s",extensions[i].ext); + + (void)printf("\n\tNot Supported: directories / /etc /bin /lib /tmp /usr /dev /sbin \n" + ); + XSUCCESS + } + } + + if (dir) { + if( !strncmp(dir,"/" ,2 ) || !strncmp(dir,"/etc", 5 ) || + !strncmp(dir,"/bin",5 ) || !strncmp(dir,"/lib", 5 ) || + !strncmp(dir,"/tmp",5 ) || !strncmp(dir,"/usr", 5 ) || + !strncmp(dir,"/dev",5 ) || !strncmp(dir,"/sbin",6) ){ + (void)printf("ERROR: Bad top directory %s, see server -?\n",dir); + exit(3); + } + if(chdir(dir) == -1){ + (void)printf("ERROR: Can't Change to directory %s\n",dir); + exit(4); + } + } + + dirList = allocG(rtSmallArrayt); + execO("find . -type d", dirList, NULL); + + if(fork() != 0) + return 0; + (void)signal(SIGCLD, SIG_IGN); + (void)signal(SIGHUP, SIG_IGN); + for(i=0;i<32;i++) + (void)close(i); + (void)setpgrp(); + + slog(LOG,"http server starting",portS,getpid()); + + if((listenfd = socket(AF_INET, SOCK_STREAM,0)) <0) + slog(ERROR, "system call","socket",0); + + if(port < 0 || port >60000) + slog(ERROR,"Invalid port number try [1,60000]",portS,0); + 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) + slog(ERROR,"system call","bind",0); + if( listen(listenfd,64) <0) + slog(ERROR,"system call","listen",0); + + for(hit=1; ;hit++) { + length = sizeof(cli_addr); + if((socketfd = accept(listenfd, (struct sockaddr *)&cli_addr, (socklen_t *)&length)) < 0) + slog(ERROR,"system call","accept",0); + + if((pid = fork()) < 0) { + slog(ERROR,"system call","fork",0); + } + else { + if(pid == 0) { + (void)close(listenfd); + web(socketfd,hit); + } else { + (void)close(socketfd); + } + } + } + + terminateG(dirList); + +} diff --git a/server.log b/server.log @@ -0,0 +1,8 @@ + INFO: http server starting:8080:21304 + INFO: request:GET / HTTP/1.1**Host: 127.0.0.1:8080**User-Agent: Mozilla/5.0 (X11; Linux x86_64; rv:52.0) Gecko/20100101 Firefox/52.0**Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8**Accept-Language: en-US,en;q=0.5**Accept-Encoding: gzip, deflate**Connection: keep-alive**Upgrade-Insecure-Requests: 1**Cache-Control: max-age=0****:1 + INFO: SEND:a:1 + INFO: request:GET /scott.jpg HTTP/1.1**Host: 127.0.0.1:8080**User-Agent: Mozilla/5.0 (X11; Linux x86_64; rv:52.0) Gecko/20100101 Firefox/52.0**Accept: */***Accept-Language: en-US,en;q=0.5**Accept-Encoding: gzip, deflate**Referer: http://127.0.0.1:8080/**Connection: keep-alive**Cache-Control: max-age=0****:2 + INFO: SEND:scott.jpg:2 + INFO: request:GET //ws HTTP/1.1**Host: 127.0.0.1:8080**User-Agent: Mozilla/5.0 (X11; Linux x86_64; rv:52.0) Gecko/20100101 Firefox/52.0**Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8**Accept-Language: en-US,en;q=0.5**Accept-Encoding: gzip, deflate**Sec-WebSocket-Version: 13**Origin: http://127.0.0.1:8080**Sec-WebSocket-Extensions: permessage-deflate**Sec-WebSocket-Key: WXZtbQjAV6Mm+b4CAr4VeQ==**Connection: keep-alive, Upgrade**Pragma: no-cache**Cache-Control: no-cache**Upgrade: websocket****:3 + INFO: websocket:detected:3 + INFO: key:WXZtbQjAV6Mm+b4CAr4VeQ==:3 diff --git a/utilities.c b/utilities.c @@ -0,0 +1,482 @@ +/* + utilities.c + + Jonathan D. Hall - jhall@futuresouth.us + Copyright 2015 Future South Technologies + + This file is part of libwebsock2. + + libwebsock2 is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + libwebsock2 is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with libwebsock2. If not, see <http://www.gnu.org/licenses/>. + +*/ + +#include "utilities.h" + +#define UTF8_ACCEPT 0 +#define UTF8_REJECT 1 + +#define SHA1CircularShift(bits,word) \ +((((word) << (bits)) & 0xFFFFFFFF) | \ +((word) >> (32-(bits)))) + +uint32_t decode(uint32_t *state, uint32_t *codep, uint32_t byte); + +//these functions assume little endian machine as they're only used on windows +uint16_t lws_htobe16(uint16_t x) +{ + return ((x & 0x00ff) << 8) | ((x & 0xff00) >> 8); +} + +uint16_t lws_be16toh(uint16_t x) +{ + return ((x & 0x00ff) << 8) | ((x & 0xff00) >> 8); +} + +uint64_t lws_htobe64(uint64_t x) +{ + return (x >> 56) | + ((x << 40) & 0x00ff000000000000LL) | + ((x << 24) & 0x0000ff0000000000LL) | + ((x << 8) & 0x000000ff00000000LL) | + ((x >> 8) & 0x00000000ff000000LL) | + ((x >> 24) & 0x0000000000ff0000LL) | + ((x >> 40) & 0x000000000000ff00LL) | + (x << 56); +} + +uint64_t lws_be64toh(uint64_t x) +{ + return (x >> 56) | + ((x << 40) & 0x00ff000000000000LL) | + ((x << 24) & 0x0000ff0000000000LL) | + ((x << 8) & 0x000000ff00000000LL) | + ((x >> 8) & 0x00000000ff000000LL) | + ((x >> 24) & 0x0000000000ff0000LL) | + ((x >> 40) & 0x000000000000ff00LL) | + (x << 56); +} + +int validate_utf8_sequence(uint8_t *s) +{ + uint32_t codepoint; + uint32_t state = 0; + + for(; *s; ++s) { + decode(&state, &codepoint, *s); + } + + return state == UTF8_ACCEPT; +} + +const uint8_t utf8d[] = { + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, // 00..1f + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, // 20..3f + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, // 40..5f + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, // 60..7f + 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9, // 80..9f + 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7, // a0..bf + 8,8,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2, // c0..df + 0xa,0x3,0x3,0x3,0x3,0x3,0x3,0x3,0x3,0x3,0x3,0x3,0x3,0x4,0x3,0x3, // e0..ef + 0xb,0x6,0x6,0x6,0x5,0x8,0x8,0x8,0x8,0x8,0x8,0x8,0x8,0x8,0x8,0x8, // f0..ff + 0x0,0x1,0x2,0x3,0x5,0x8,0x7,0x1,0x1,0x1,0x4,0x6,0x1,0x1,0x1,0x1, // s0..s0 + 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,0,1,1,1,1,1,0,1,0,1,1,1,1,1,1, // s1..s2 + 1,2,1,1,1,1,1,2,1,2,1,1,1,1,1,1,1,1,1,1,1,1,1,2,1,1,1,1,1,1,1,1, // s3..s4 + 1,2,1,1,1,1,1,1,1,2,1,1,1,1,1,1,1,1,1,1,1,1,1,3,1,3,1,1,1,1,1,1, // s5..s6 + 1,3,1,1,1,1,1,3,1,3,1,1,1,1,1,1,1,3,1,1,1,1,1,1,1,1,1,1,1,1,1,1, // s7..s8 +}; + +uint32_t decode(uint32_t* state, uint32_t* codep, uint32_t byte) +{ + uint32_t type = utf8d[byte]; + + *codep = (*state != UTF8_ACCEPT) ? + (byte & 0x3fu) | (*codep << 6) : + (0xff >> type) & (byte); + + *state = utf8d[256 + *state*16 + type]; + return *state; +} + +const char *BASE64_CHARS = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"; + +void _base64_encode_triple(unsigned char triple[3], char result[4]) +{ + int tripleValue, i; + + tripleValue = triple[0]; + tripleValue *= 256; + tripleValue += triple[1]; + tripleValue *= 256; + tripleValue += triple[2]; + + for (i=0; i<4; i++) + { + result[3-i] = BASE64_CHARS[tripleValue%64]; + tripleValue /= 64; + } +} + +int base64_encode(unsigned char *source, size_t sourcelen, char *target, size_t targetlen) +{ + if ((sourcelen+2)/3*4 > targetlen-1) + return 0; + + while (sourcelen >= 3) + { + _base64_encode_triple(source, target); + sourcelen -= 3; + source += 3; + target += 4; + } + + if (sourcelen > 0) + { + unsigned char temp[3]; + memset(temp, 0, sizeof(temp)); + memcpy(temp, source, sourcelen); + _base64_encode_triple(temp, target); + target[3] = '='; + if (sourcelen == 1) + target[2] = '='; + + target += 4; + } + + target[0] = 0; + + return 1; +} + +int _base64_char_value(char base64char) +{ + if (base64char >= 'A' && base64char <= 'Z') + return base64char-'A'; + if (base64char >= 'a' && base64char <= 'z') + return base64char-'a'+26; + if (base64char >= '0' && base64char <= '9') + return base64char-'0'+2*26; + if (base64char == '+') + return 2*26+10; + if (base64char == '/') + return 2*26+11; + return -1; +} + +int _base64_decode_triple(char quadruple[4], unsigned char *result) +{ + int i, triple_value, bytes_to_decode = 3, only_equals_yet = 1; + int char_value[4]; + + for (i=0; i<4; i++) + char_value[i] = _base64_char_value(quadruple[i]); + + for (i=3; i>=0; i--) + { + if (char_value[i]<0) + { + if (only_equals_yet && quadruple[i]=='=') + { + char_value[i]=0; + bytes_to_decode--; + continue; + } + return 0; + } + only_equals_yet = 0; + } + + if (bytes_to_decode < 0) + bytes_to_decode = 0; + + triple_value = char_value[0]; + triple_value *= 64; + triple_value += char_value[1]; + triple_value *= 64; + triple_value += char_value[2]; + triple_value *= 64; + triple_value += char_value[3]; + + for (i=bytes_to_decode; i<3; i++) + triple_value /= 256; + for (i=bytes_to_decode-1; i>=0; i--) + { + result[i] = triple_value%256; + triple_value /= 256; + } + + return bytes_to_decode; +} + +size_t base64_decode(char *source, unsigned char *target, size_t targetlen) +{ + char *src, *tmpptr; + char quadruple[4]; + unsigned char tmpresult[3]; + int i, tmplen = 3; + size_t converted = 0; + + src = (char *)malloc(strlen(source)+5); + if (src == NULL) + return -1; + strcpy(src, source); + strcat(src, "===="); + tmpptr = src; + + while (tmplen == 3) + { + for (i=0; i<4; i++) + { + while (*tmpptr != '=' && _base64_char_value(*tmpptr)<0) + tmpptr++; + + quadruple[i] = *(tmpptr++); + } + + tmplen = _base64_decode_triple(quadruple, tmpresult); + + if (targetlen < tmplen) + { + free(src); + return -1; + } + + memcpy(target, tmpresult, tmplen); + target += tmplen; + targetlen -= tmplen; + converted += tmplen; + } + + free(src); + return converted; +} + +void SHA1Reset(SHA1Context *context) +{ + context->Length_Low = 0; + context->Length_High = 0; + context->Message_Block_Index = 0; + + context->Message_Digest[0] = 0x67452301; + context->Message_Digest[1] = 0xEFCDAB89; + context->Message_Digest[2] = 0x98BADCFE; + context->Message_Digest[3] = 0x10325476; + context->Message_Digest[4] = 0xC3D2E1F0; + + context->Computed = 0; + context->Corrupted = 0; +} + +int SHA1Result(SHA1Context *context) +{ + + if (context->Corrupted) + { + return 0; + } + + if (!context->Computed) + { + SHA1PadMessage(context); + context->Computed = 1; + } + + return 1; +} + +void SHA1Input( SHA1Context *context, + const unsigned char *message_array, + unsigned length) +{ + if (!length) + { + return; + } + + if (context->Computed || context->Corrupted) + { + context->Corrupted = 1; + return; + } + + while(length-- && !context->Corrupted) + { + context->Message_Block[context->Message_Block_Index++] = + (*message_array & 0xFF); + + context->Length_Low += 8; + /* Force it to 32 bits */ + context->Length_Low &= 0xFFFFFFFF; + if (context->Length_Low == 0) + { + context->Length_High++; + /* Force it to 32 bits */ + context->Length_High &= 0xFFFFFFFF; + if (context->Length_High == 0) + { + /* Message is too long */ + context->Corrupted = 1; + } + } + + if (context->Message_Block_Index == 64) + { + SHA1ProcessMessageBlock(context); + } + + message_array++; + } +} + +void SHA1ProcessMessageBlock(SHA1Context *context) +{ + const unsigned K[] = /* Constants defined in SHA-1 */ + { + 0x5A827999, + 0x6ED9EBA1, + 0x8F1BBCDC, + 0xCA62C1D6 + }; + int t; /* Loop counter */ + unsigned temp; /* Temporary word value */ + unsigned W[80]; /* Word sequence */ + unsigned A, B, C, D, E; /* Word buffers */ + + /* + * Initialize the first 16 words in the array W + */ + for(t = 0; t < 16; t++) + { + W[t] = ((unsigned) context->Message_Block[t * 4]) << 24; + W[t] |= ((unsigned) context->Message_Block[t * 4 + 1]) << 16; + W[t] |= ((unsigned) context->Message_Block[t * 4 + 2]) << 8; + W[t] |= ((unsigned) context->Message_Block[t * 4 + 3]); + } + + for(t = 16; t < 80; t++) + { + W[t] = SHA1CircularShift(1,W[t-3] ^ W[t-8] ^ W[t-14] ^ W[t-16]); + } + + A = context->Message_Digest[0]; + B = context->Message_Digest[1]; + C = context->Message_Digest[2]; + D = context->Message_Digest[3]; + E = context->Message_Digest[4]; + + for(t = 0; t < 20; t++) + { + temp = SHA1CircularShift(5,A) + + ((B & C) | ((~B) & D)) + E + W[t] + K[0]; + temp &= 0xFFFFFFFF; + E = D; + D = C; + C = SHA1CircularShift(30,B); + B = A; + A = temp; + } + + for(t = 20; t < 40; t++) + { + temp = SHA1CircularShift(5,A) + (B ^ C ^ D) + E + W[t] + K[1]; + temp &= 0xFFFFFFFF; + E = D; + D = C; + C = SHA1CircularShift(30,B); + B = A; + A = temp; + } + + for(t = 40; t < 60; t++) + { + temp = SHA1CircularShift(5,A) + + ((B & C) | (B & D) | (C & D)) + E + W[t] + K[2]; + temp &= 0xFFFFFFFF; + E = D; + D = C; + C = SHA1CircularShift(30,B); + B = A; + A = temp; + } + + for(t = 60; t < 80; t++) + { + temp = SHA1CircularShift(5,A) + (B ^ C ^ D) + E + W[t] + K[3]; + temp &= 0xFFFFFFFF; + E = D; + D = C; + C = SHA1CircularShift(30,B); + B = A; + A = temp; + } + + context->Message_Digest[0] = + (context->Message_Digest[0] + A) & 0xFFFFFFFF; + context->Message_Digest[1] = + (context->Message_Digest[1] + B) & 0xFFFFFFFF; + context->Message_Digest[2] = + (context->Message_Digest[2] + C) & 0xFFFFFFFF; + context->Message_Digest[3] = + (context->Message_Digest[3] + D) & 0xFFFFFFFF; + context->Message_Digest[4] = + (context->Message_Digest[4] + E) & 0xFFFFFFFF; + + context->Message_Block_Index = 0; +} + +void SHA1PadMessage(SHA1Context *context) +{ + /* + * Check to see if the current message block is too small to hold + * the initial padding bits and length. If so, we will pad the + * block, process it, and then continue padding into a second + * block. + */ + if (context->Message_Block_Index > 55) + { + context->Message_Block[context->Message_Block_Index++] = 0x80; + while(context->Message_Block_Index < 64) + { + context->Message_Block[context->Message_Block_Index++] = 0; + } + + SHA1ProcessMessageBlock(context); + + while(context->Message_Block_Index < 56) + { + context->Message_Block[context->Message_Block_Index++] = 0; + } + } + else + { + context->Message_Block[context->Message_Block_Index++] = 0x80; + while(context->Message_Block_Index < 56) + { + context->Message_Block[context->Message_Block_Index++] = 0; + } + } + + /* + * Store the message length as the last 8 octets + */ + context->Message_Block[56] = (context->Length_High >> 24) & 0xFF; + context->Message_Block[57] = (context->Length_High >> 16) & 0xFF; + context->Message_Block[58] = (context->Length_High >> 8) & 0xFF; + context->Message_Block[59] = (context->Length_High) & 0xFF; + context->Message_Block[60] = (context->Length_Low >> 24) & 0xFF; + context->Message_Block[61] = (context->Length_Low >> 16) & 0xFF; + context->Message_Block[62] = (context->Length_Low >> 8) & 0xFF; + context->Message_Block[63] = (context->Length_Low) & 0xFF; + + SHA1ProcessMessageBlock(context); +} diff --git a/utilities.h b/utilities.h @@ -0,0 +1,74 @@ +/* + utilities.h + + Jonathan D. Hall - jhall@futuresouth.us + Copyright 2015 Future South Technologies + + This file is part of libwebsock2. + + libwebsock2 is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + libwebsock2 is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with libwebsock2. If not, see <http://www.gnu.org/licenses/>. + +*/ + +/* + * SHA1 functions written by Paul E. Jones <paulej@packetizer.com> + * Copyright (C) 1998, 2009 - All Rights Reserved + */ + +#ifndef _utilities_h +#define _utilities_h + +#include "libsheepyObject.h" + +typedef struct SHA1Context +{ + unsigned Message_Digest[5]; /* Message Digest (output) */ + + unsigned Length_Low; /* Message length in bits */ + unsigned Length_High; /* Message length in bits */ + + unsigned char Message_Block[64]; /* 512-bit message blocks */ + int Message_Block_Index; /* Index into message block array */ + + int Computed; /* Is the digest computed? */ + int Corrupted; /* Is the message digest corruped? */ +} SHA1Context; + + +/* UTF prototypes */ + +int validate_utf8_sequence(uint8_t *s); +uint16_t lws_htobe16(uint16_t x); +uint16_t lws_be16toh(uint16_t x); +uint64_t lws_htobe64(uint64_t x); +uint64_t lws_be64toh(uint64_t x); + + +/* Base64 prototypes */ + +int base64_encode(unsigned char *source, size_t sourcelen, char *target, size_t targetlen); +void _base64_encode_triple(unsigned char triple[3], char result[4]); +int _base64_char_value(char base64char); +int _base64_decode_triple(char quadruple[4], unsigned char *result); +size_t base64_decode(char *source, unsigned char *target, size_t targetlen); + +/* SHA1 prototypes */ + +void SHA1Reset(SHA1Context *); +int SHA1Result(SHA1Context *); +void SHA1Input( SHA1Context *, const unsigned char *, unsigned); +void SHA1ProcessMessageBlock(SHA1Context *); +void SHA1PadMessage(SHA1Context *); + +#endif /* utilities_h */