# This is a shell archive. Save it in a file, remove anything before # this line, and then unpack it by entering "sh file". Note, it may # create directories; files and directories will be owned by you and # have default permissions. # # This archive contains: # # pserv # pserv/files # pserv/files/patch-main.c # pserv/files/patch-Makefile # pserv/files/patch-main.h # pserv/files/patch-mime_types.dat # pserv/files/patch-pserv.conf # pserv/files/patch-handlers.c # pserv/files/patch-handlers.h # pserv/files/patch-mime.c # pserv/files/pserv.sh # pserv/Makefile # pserv/distinfo # pserv/pkg-descr # pserv/pkg-plist # echo c - pserv mkdir -p pserv > /dev/null 2>&1 echo c - pserv/files mkdir -p pserv/files > /dev/null 2>&1 echo x - pserv/files/patch-main.c sed 's/^X//' >pserv/files/patch-main.c << 'END-of-pserv/files/patch-main.c' X--- main.c.orig Mon Sep 22 10:39:24 2003 X+++ main.c Thu Oct 16 14:00:02 2003 X@@ -23,6 +23,7 @@ X char defaultFileName[MAX_PATH_LEN+1]; X char logFileName[MAX_PATH_LEN+1]; X char mimeTypesFileName[MAX_PATH_LEN+1]; X+char phpFileName[MAX_PATH_LEN+1]; X char cgiRoot[MAX_PATH_LEN+1]; /* root for CGI scripts exec */ X struct timeval sockTimeVal; X mimeData *mimeArray; /* here we will hold all MIME data, inited once, never to be changed */ X@@ -206,10 +207,10 @@ X int reqSize; X int readLines; X int tokenEnd; X- X- /* we copy the header lines to an array for easier parsing */ X+ X+ /* we copy the header lines to an array for easier parsing */ X /* but first we make sure that our string has a newline and an end */ X- req[BUFFER_SIZE] = '\0'; X+ req[BUFFER_SIZE] = '\0'; X reqSize = strlen(req); X req[reqSize] = '\n'; X reqSize++; X@@ -230,7 +231,7 @@ X for (k = 0; k < readLines; k++) X printf("%d - |%s|\n", k, reqArray[k]); X #endif X- X+ X /* first line: method, path and protocol version */ X /* we copy to a temporary buffer to be more secure against overflows */ X i = j = 0; X@@ -246,7 +247,7 @@ X else X tokenEnd = NO; X i++; X- X+ X /* we look for the document address */ X j = 0; X reqStruct->documentAddress[0] = '\0'; X@@ -259,14 +260,14 @@ X else X token[j] = '\0'; /* to make sure we have a string */ X /* now we need to convert some escapings from the path like %20 */ X- convertPercents(token, j); X+ convertPercents(token, j); X strcpy(reqStruct->documentAddress, token); /* copy back */ X if (reqArray[0][i] == '\0') X tokenEnd = YES; X else X tokenEnd = NO; X i++; X- X+ X /* we need now to separate path from query string ("?" separated) */ X if (reqArray[0][i-1] == '?') X { X@@ -282,7 +283,7 @@ X i++; X } X } X- X+ X /* we analyze the HTTP protocol version */ X /* default is 0.9 since that version didn't report itself */ X strcpy(reqStruct->protocolVersion, "HTTP/0.9"); X@@ -306,10 +307,13 @@ X else if (!strncmp(reqArray[1], "Connection: Keep-Alive", strlen("Connection: keep-alive"))) X reqStruct->keepAlive = YES; X X- /* user-agent, content-length and else */ X+ /* user-agent, content-length, content-type, cookie and else */ X i = 1; X j = NO; X reqStruct->userAgent[0] = '\0'; X+ reqStruct->contentLength = -1; X+ reqStruct->contentType[0] = '\0'; X+ reqStruct->cookie[0] = '\0'; X while (i < readLines) X { X if (!strncmp(reqArray[i], "User-Agent:", strlen("User-Agent:"))) X@@ -317,14 +321,28 @@ X strncpy(reqStruct->userAgent, &reqArray[i][strlen("User-Agent: ")], USER_AGENT_LEN - 1); X reqStruct->userAgent[USER_AGENT_LEN] = '\0'; X } X- else if (!strncmp(reqArray[i], "Content-Length:", strlen("Content-length:")) || !strncmp(reqArray[i], "Content-length:", strlen("Content-length:"))) X- { X- strcpy(token, &reqArray[i][strlen("Content-length: ")]); X- sscanf(token, "%ld", &(reqStruct->contentLength)); X+ else if (!strncmp(reqArray[i], "Content-Length:", strlen("Content-length:")) || !strncmp(reqArray[i], "Content-length:", strlen("Content-length:"))) X+ { X+ strcpy(token, &reqArray[i][strlen("Content-length: ")]); X+ sscanf(token, "%ld", &(reqStruct->contentLength)); X #ifdef PRINTF_DEBUG X- printf("content length %ld\n", reqStruct->contentLength); X+ printf("content length %ld\n", reqStruct->contentLength); X #endif X- } X+ } X+ else if (!strncmp(reqArray[i], "Content-Type:", strlen("Content-type:")) || !strncmp(reqArray[i], "Content-type:", strlen("Content-type:"))) X+ { X+ strncpy(reqStruct->contentType, &reqArray[i][strlen("Content-type: ")], CONTENT_TYPE_LEN - 1); X+#ifdef PRINTF_DEBUG X+ printf("content type %s\n", reqStruct->contentType); X+#endif X+ } X+ else if (!strncmp(reqArray[i], "Cookie:", strlen("Cookie:"))) X+ { X+ strncpy(reqStruct->cookie, &reqArray[i][strlen("Cookie: ")], MAX_COOKIE_LEN - 1); X+#ifdef PRINTF_DEBUG X+ printf("cookie %s\n", reqStruct->cookie); X+#endif X+ } X i++; X } X /* if we didn't find a User-Aget we fill in a (N)ot(R)ecognized */ X@@ -414,18 +432,39 @@ X /* we append the default file name */ X strcat(completeFilePath, defaultFileName); X analyzeExtension(mimeType, completeFilePath); X- dumpFile(sock, completeFilePath, mimeType, req); X+#ifdef PHP X+ if (strncmp(mimeType, "application/x-httpd-php", 23)) X+#endif X+ dumpFile(sock, completeFilePath, mimeType, req); X+#ifdef PHP X+ else X+ phpHandler(port, sock, phpFileName, completeFilePath, req, NULL); X+#endif X } X #else X /* we append the default file name */ X strcat(completeFilePath, defaultFileName); X analyzeExtension(mimeType, completeFilePath); X- dumpFile(sock, completeFilePath, mimeType, req); X+#ifdef PHP X+ if (strncmp(mimeType, "application/x-httpd-php", 23)) X+#endif X+ dumpFile(sock, completeFilePath, mimeType, req); X+#ifdef PHP X+ else X+ phpHandler(port, sock, phpFileName, completeFilePath, req, NULL); X+#endif X #endif X } else X { /* it is a plain file */ X analyzeExtension(mimeType, completeFilePath); X- dumpFile(sock, completeFilePath, mimeType, req); X+#ifdef PHP X+ if (strncmp(mimeType, "application/x-httpd-php", 23)) X+#endif X+ dumpFile(sock, completeFilePath, mimeType, req); X+#ifdef PHP X+ else X+ phpHandler(port, sock, phpFileName, completeFilePath, req, NULL); X+#endif X } X } X } else if (!strcmp(req.method, "HEAD")) X@@ -494,7 +533,14 @@ X strcat(completeFilePath, defaultFileName); X } X analyzeExtension(mimeType, completeFilePath); X- dumpHeader(sock, completeFilePath, mimeType, req); X+#ifdef PHP X+ if (strncmp(mimeType, "application/x-httpd-php", 23)) X+#endif X+ dumpFile(sock, completeFilePath, mimeType, req); X+#ifdef PHP X+ else X+ phpHandler(port, sock, phpFileName, completeFilePath, req, NULL); X+#endif X } X } else if (!strcmp(req.method, "POST")) X { X@@ -507,13 +553,6 @@ X int readFinished; X X printf("Handling of POST method\n"); X- /* first we check if the path contains the directory selected for cgi's and in case handle it */ X- if (strncmp(req.documentAddress, CGI_MATCH_STRING, strlen(CGI_MATCH_STRING))) X- { X- /* non cgi POST is not supported */ X- sayError(sock, UNHANDLED_METHOD, "", req); X- return -1; X- } X #ifdef PRINTF_DEBUG X printf ("begin of post handling\n"); X X@@ -523,9 +562,15 @@ X totalRead = 0; X stuckCounter = 0; X timeOutCounter = 0; X- while (!readFinished) X- { X- howMany = recv(newSocket, tempBuff, BUFFER_SIZE, 0); X+ X+ /* SECURITY: Avoid malicious Content-Length -- check \r\n\r\n\0 also */ X+ if (req.contentLength < 0 || req.contentLength >= BUFFER_SIZE-5) { X+ sayError(sock, 500, "", req); X+ return -1; X+ } X+ X+ /* SECURITY: Remove loop to prevent buffer overflow */ X+ howMany = recv(newSocket, tempBuff, req.contentLength+5, 0); X tempBuff[howMany] = '\0'; /* seems that some Unices need this */ X #ifdef PRINTF_DEBUG X printf ("read: %d\n%s\n", howMany, tempBuff); X@@ -579,16 +624,15 @@ X if (howMany == req.contentLength) X readFinished = YES; X } X- } X #ifdef PRINTF_DEBUG X- printf("total read %d\n", totalRead); X+ printf("total read %d\n", totalRead); X #endif X- if (totalRead == 0) X- { X- printf("Request read error\n"); X- } else X- { X- if (buff[totalRead - 1] != '\n') /* we need a trailing \n or the script will wait forever */ X+ if (totalRead == 0) X+ { X+ printf("Request read error\n"); X+ } else X+ { X+ if (buff[totalRead - 1] != '\n') /* we need a trailing \n or the script will wait forever */ X { X buff[totalRead++] = '\n'; X buff[totalRead] = '\0'; X@@ -596,7 +640,77 @@ X #ifdef PRINTF_DEBUG X printf("buff: |%s|\n", buff); X #endif X- cgiHandler(port, sock, req, buff); X+ if (!strncmp(req.documentAddress, CGI_MATCH_STRING, strlen(CGI_MATCH_STRING))) X+ { X+ cgiHandler(port, sock, req, buff); X+ } else X+ { X+#ifdef PHP X+ strcpy(completeFilePath, homePath); X+ strcat(completeFilePath, req.documentAddress); X+ /* now we check if the given path tries to get out of the root */ X+ { X+ int i,j; X+ int sL; X+ char dirName[MAX_PATH_LEN+1]; X+ int depthCount = 0; X+ X+ sL = strlen(req.documentAddress); X+ dirName[0] = '\0'; X+ if (sL > 3) { X+ dirName[0] = req.documentAddress[1]; X+ dirName[1] = req.documentAddress[2]; X+ dirName[2] = req.documentAddress[3]; X+ dirName[3] ='\0'; X+ if (!strcmp(dirName, "../")) X+ { X+ sayError(sock, FORBIDDEN, req.documentAddress, req); X+ return -1; X+ } X+ } X+ j = 0; X+ for (i = 1; i < sL; i++) { X+ if (req.documentAddress[i] == '/') X+ { X+ dirName[j] = '\0'; X+ if (strcmp(dirName, "..")) X+ depthCount ++; X+ else X+ depthCount--; X+ j = 0; X+ } else X+ dirName[j++] = req.documentAddress[i]; X+ } X+ if (depthCount < 0) X+ { X+ sayError(sock, FORBIDDEN, req.documentAddress, req); X+ return -1; X+ } X+ } X+ /* now we check if the given file is a directory or a plain file */ X+ stat(completeFilePath, &fileStats); X+ if ((fileStats.st_mode & S_IFDIR) == S_IFDIR) X+ { X+ /* if does not end with a slash, we get an error */ X+ if(completeFilePath[strlen(completeFilePath)-1] != '/') X+ { X+ sayError(sock, NOT_FOUND, req.documentAddress, req); X+ return -1; X+ } X+ /* we append the default file name */ X+ strcat(completeFilePath, defaultFileName); X+ } X+ analyzeExtension(mimeType, completeFilePath); X+ if (strncmp(mimeType, "application/x-httpd-php", 23)) X+ { X+#endif X+ /* non cgi POST is not supported */ X+ sayError(sock, UNHANDLED_METHOD, "", req); X+ return -1; X+#ifdef PHP X+ } else phpHandler(port, sock, phpFileName, completeFilePath, req, buff); X+#endif X+ } X } X } else X { X@@ -625,7 +739,7 @@ X f = fopen(configFile, "r"); X if (f == NULL) X { X- printf("Error opening config file. Setting defaults.\n"); X+ printf("Config file not found. Setting defaults.\n"); X *serverPort = DEFAULT_PORT; X *maxChildren = DEFAULT_MAX_CHILDREN; X strcpy(homePath, DEFAULT_DOCS_LOCATION); X@@ -634,7 +748,9 @@ X sockTimeVal.tv_usec = DEFAULT_USEC_TO; X strcpy(logFileName, DEFAULT_LOG_FILE); X strcpy(mimeTypesFileName, DEFAULT_MIME_FILE); X+ strcpy(phpFileName, DEFAULT_PHP_FILE); X strcpy(cgiRoot, DEFAULT_CGI_ROOT); X+ initMimeTypes(); X return -1; X } X if (!feof(f)) fscanf(f, "%s %s", str1, str2); X@@ -735,11 +851,25 @@ X if (mimeTypesFileName == NULL) X { X strcpy(mimeTypesFileName, DEFAULT_MIME_FILE); X- printf("Error reading mimeTypesFileName from file, setting default, %s\n", mimeTypesFileName); X+ printf("Error reading mimeTypesFile from file, setting default, %s\n", mimeTypesFileName); X } X } else { X strcpy(mimeTypesFileName, DEFAULT_MIME_FILE); X- printf("Error reading mimeTypesFileName from file, setting default, %s\n", mimeTypesFileName); X+ printf("Error reading mimeTypesFile from file, setting default, %s\n", mimeTypesFileName); X+ } X+ if (!feof(f)) fscanf(f, "%s %s", str1, str2); X+ if (str1 != NULL && str2 != NULL && !strcmp(str1, "phpFile")) X+ { X+ sscanf(str2, "%s", phpFileName); X+ if (logFileName == NULL) X+ { X+ strcpy(phpFileName, DEFAULT_LOG_FILE); X+ printf("Error reading phpFile from file, setting default, %s\n", phpFileName); X+ } X+ } else X+ { X+ strcpy(phpFileName, DEFAULT_PHP_FILE); X+ printf("Error reading phpFile from file, setting default, %s\n", phpFileName); X } X if (!feof(f)) fscanf(f, "%s %s", str1, str2); X if (str1 != NULL && str2 != NULL && !strcmp(str1, "cgiRoot")) X@@ -775,6 +905,7 @@ X int readFinished; X struct request gottenReq; X int isKeepAlive; X+ int bool; X struct sockaddr_in listenName; /* data struct for the listen port */ X struct sockaddr_in acceptedSockStruct; /* sockaddr for the internetworking */ X int acceptedSocketLen; /* size of the structure */ X@@ -808,9 +939,16 @@ X printf("socket creation error occoured\n"); X return -1; X } X+ bool = 1; X+ error = setsockopt (theSocket, SOL_SOCKET, SO_REUSEADDR, &bool, sizeof(bool)); X+ if (error == -1) X+ { if (errno == EADDRINUSE) X+ printf("set socket option error occoured\n"); X+ return -1; X+ } X error = bind (theSocket, (struct sockaddr*) &listenName, sizeof(listenName)); X if (error == -1) X- { X+ { if (errno == EADDRINUSE) X printf("socket binding error occoured\n"); X return -2; X } END-of-pserv/files/patch-main.c echo x - pserv/files/patch-Makefile sed 's/^X//' >pserv/files/patch-Makefile << 'END-of-pserv/files/patch-Makefile' X--- Makefile.orig Mon Sep 8 20:05:54 2003 X+++ Makefile Thu Oct 16 14:24:50 2003 X@@ -1,11 +1,11 @@ X #Change the following to your needs X-CC = gcc X+CC ?= gcc X #insert here flags, eg. optimizations X-CFLAGS = -Wall -O3 X-LIBS = -lnsl -lsocket X SRCS = main.c handlers.c mime.c log.c X OBJS = main.o handlers.o mime.o log.o X PROGRAM = pserv X+ X+all : $(PROGRAM) X X $(PROGRAM) : $(OBJS) X $(CC) -o $(PROGRAM) $(OBJS) $(LIBS) END-of-pserv/files/patch-Makefile echo x - pserv/files/patch-main.h sed 's/^X//' >pserv/files/patch-main.h << 'END-of-pserv/files/patch-main.h' X--- main.h.orig Fri Sep 19 00:36:03 2003 X+++ main.h Thu Oct 16 13:52:18 2003 X@@ -24,11 +24,12 @@ X X X /* --- CPP parsing options --- */ X-#define PRINTF_DEBUG /* enable this to print some debugging messages */ X+#undef PRINTF_DEBUG /* enable this to print some debugging messages */ X #undef ON_THE_FLY_CONVERSION /* enable this for line ending conversion */ X #undef BRAIN_DEAD_CAST /* if your compiler is brainwashed and does not cast standard types.h structures */ X #define FORKING_SERVER /* enables to fork for every request */ X #define AUTO_INDEX /* enables auto-index of directories */ X+#define PHP /* enables transparent PHP support */ X X /* --- Configure options --- */ X #define CONFIG_FILE_NAME "pserv.conf" X@@ -39,19 +40,19 @@ X #define MIME_TYPE_DEFAULT "application/octet-stream" X X /* configuration file location */ X-#define DEFAULT_CONFIG_LOCATION "/export/home/multix/pserv/" X-//#define DEFAULT_CONFIG_LOCATION "/Users/multix/Documents/code/pserv/" X+#define DEFAULT_CONFIG_LOCATION "/usr/local/etc/" X X /* hard-wired defaults, if loading of config file fails */ X-#define DEFAULT_PORT 2000 X+#define DEFAULT_PORT 80 X #define DEFAULT_MAX_CHILDREN 5 X-#define DEFAULT_DOCS_LOCATION "/export/home/multix/public_html" X+#define DEFAULT_DOCS_LOCATION "/usr/local/www/data" X #define DEFAULT_FILE_NAME "index.html" X #define DEFAULT_SEC_TO 1 X #define DEFAULT_USEC_TO 100 X-#define DEFAULT_LOG_FILE "/export/home/multix/pserv/pserv.log" X-#define DEFAULT_MIME_FILE "/export/home/multix/pserv/mime_types.dat" X-#define DEFAULT_CGI_ROOT "/export/home/multix/public_html/cgi-bin" X+#define DEFAULT_LOG_FILE "/var/log/pserv.log" X+#define DEFAULT_MIME_FILE "/usr/local/etc/mime.types" X+#define DEFAULT_PHP_FILE "/usr/local/bin/php" X+#define DEFAULT_CGI_ROOT "/usr/local/www/cgi-bin" X #define DEFAULT_SERVER_NAME "localhost" X X /* amount of connections queued in listening */ X@@ -120,9 +121,11 @@ X #define ADDRESS_LEN 16 X #define METHOD_LEN 16 X #define PROTOCOL_LEN 16 X+#define CONTENT_TYPE_LEN 256 X #define USER_AGENT_LEN 256 X #define MAX_QUERY_STRING_LEN 1024 X #define MAX_PATH_LEN 1024 X+#define MAX_COOKIE_LEN 4096 X X struct request X { X@@ -133,7 +136,9 @@ X char protocolVersion[PROTOCOL_LEN+1]; X int keepAlive; X char userAgent[USER_AGENT_LEN+1]; X+ char cookie[MAX_COOKIE_LEN+1]; X long int contentLength; X+ char contentType[CONTENT_TYPE_LEN+1]; X char rest[BUFFER_SIZE+1]; X }; X END-of-pserv/files/patch-main.h echo x - pserv/files/patch-mime_types.dat sed 's/^X//' >pserv/files/patch-mime_types.dat << 'END-of-pserv/files/patch-mime_types.dat' X--- mime_types.dat.orig Wed Oct 15 15:57:08 2003 X+++ mime_types.dat Wed Oct 15 15:57:22 2003 X@@ -14,3 +14,4 @@ X tar application/x-tar X lha application/octet-stream X lzh application/octet-stream X+php application/x-httpd-php END-of-pserv/files/patch-mime_types.dat echo x - pserv/files/patch-pserv.conf sed 's/^X//' >pserv/files/patch-pserv.conf << 'END-of-pserv/files/patch-pserv.conf' X--- pserv.conf.orig Wed Oct 15 16:18:05 2003 X+++ pserv.conf Wed Oct 15 16:20:01 2003 X@@ -1,9 +1,10 @@ X-port 80 X+port 80 X maxChildren 4 X-documentsPath /export/home/multix/public_html X+documentsPath %%PREFIX%%/www/data X defaultFile index.html X secTimeout 1 X uSecTimeout 100000 X-logFile /export/home/multix/pserv/pserv.log X-mimeTypesFile /export/home/multix/pserv/mime_types.dat X-cgiRoot /export/home/multix/public_html/cgi-bin X+logFile /var/log/pserv.log X+mimeTypesFile %%PREFIX%%/etc/mime.types X+phpFile %%LOCALBASE%%/bin/php X+cgiRoot %%PREFIX%%/www/cgi-bin END-of-pserv/files/patch-pserv.conf echo x - pserv/files/patch-handlers.c sed 's/^X//' >pserv/files/patch-handlers.c << 'END-of-pserv/files/patch-handlers.c' X--- handlers.c.orig Thu Sep 18 15:26:48 2003 X+++ handlers.c Thu Oct 16 14:16:05 2003 X@@ -24,6 +24,7 @@ X #endif X X extern char cgiRoot[MAX_PATH_LEN+1]; /* root for CGI scripts exec */ X+extern char homePath[MAX_PATH_LEN+1]; /* root for PHP scripts exec */ X extern int port; /* server port */ X extern char defaultFileName[MAX_PATH_LEN+1]; /* default name for index, default or similar file */ X X@@ -262,12 +263,33 @@ X newArgv[i] = NULL; /* we correctly terminate argv */ X X i = 0; X+ if (req.contentLength != -1) X+ { X+ sprintf(newEnvp[i++], "CONTENT_LENGTH=%ld", req.contentLength); X+ strcpy(newEnvp[i], "CONTENT_TYPE="); X+ strcat(newEnvp[i++], req.contentType); X+ } X+ strcpy(newEnvp[i], "SERVER_NAME="); X+ strcat(newEnvp[i++], DEFAULT_SERVER_NAME); X strcpy(newEnvp[i], "SERVER_SOFTWARE="); X strcat(newEnvp[i], SERVER_SOFTWARE_STR); X strcat(newEnvp[i], "/"); X strcat(newEnvp[i++], SERVER_VERSION_STR); X+ strcpy(newEnvp[i], "SERVER_PROTOCOL="); X+ strcat(newEnvp[i++], req.protocolVersion); X strcpy(newEnvp[i], "REQUEST_METHOD="); X strcat(newEnvp[i++], req.method); X+ strcpy(newEnvp[i], "REMOTE_ADDR="); X+ strcat(newEnvp[i++], req.address); X+ strcpy(newEnvp[i], "HTTP_USER_AGENT="); X+ strcat(newEnvp[i++], req.userAgent); X+ if (req.cookie[0] != '\0') X+ { X+ strcpy(newEnvp[i], "HTTP_COOKIE="); X+ strcat(newEnvp[i++], req.cookie); X+ } X+ strcpy(newEnvp[i], "SCRIPT_FILENAME="); X+ strcat(newEnvp[i++], completedPath); X strcpy(newEnvp[i], "SCRIPT_NAME="); X strcat(newEnvp[i++], req.documentAddress); X strcpy(newEnvp[i], "GATEWAY_INTERFACE="); X@@ -302,13 +324,256 @@ X execve(completedPath, newArgv, newEnvp); X /* we reach this line only if an execution error occoured */ X /* logging will happen in the father */ X- printf("\n