/****************************************************************************** * MiniWeb - a mini and high efficiency HTTP server implementation * Developed by Stanley Huang * Based on the original MiniWeb developed and hosted at * https://sourceforge.net/projects/miniweb/ * Distributed under BSD license * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. ******************************************************************************/ #include #include #include #include #include #include #include #include "httpd.h" #include "httpint.h" //////////////////////////////////////////////////////////////////////////// // global variables //////////////////////////////////////////////////////////////////////////// const char* status200[] = { "OK", /* 200 */ "Created", /* 201 */ "Accepted", /* 202 */ "Non-Authoritative Information", /* 203 */ "No Content", /* 204 */ "Reset Content", /* 205 */ "Partial Content", /* 206 */ }; const char* status300[] = { "Multiple Choices", /* 300 */ "Moved Permanently", /* 301 */ "Found", /* 302 */ "See Other", /* 303 */ "Not Modified", /* 304 */ "Use Proxy", /* 305 */ "", /* 306 */ "Temporary Redirect", /* 307 */ }; const char* status400[] = { "Bad Request", /* 400 */ "Unauthorized", /* 401 */ "", /* 402 */ "Forbidden", /* 403 */ "Not Found", /* 404 */ "Method Not Allowed", /* 405 */ "Not Acceptable", /* 406 */ "Proxy Authentication Required", /* 407 */ "Request Timeout", /* 408 */ "Conflict", /* 409 */ "Gone", /* 410 */ "Length Required", /* 411 */ "Precondition Failed", /* 412 */ "Request Entity Too Large", /* 413 */ "Request-URI Too Long", /* 414 */ }; const char* status500[] = { "Internal Server Error", /* 500 */ "Not Implemented", /* 501 */ "Bad Gateway", /* 502 */ "Service Unavailable", /* 503 */ "Gateway Timeout", /* 504 */ }; const char* contentTypeTable[]={ "application/octet-stream", "text/html", "text/xml", "text/plain", "application/vnd.mozilla.xul+xml", "text/css", "application/x-javascript", "image/png", "image/jpeg", "image/gif", "application/x-shockwave-flash", "audio/mpeg", "video/mpeg", "video/avi", "video/mp4", "video/quicktime", "video/x-mpeg-avc", "video/flv", "video/MP2T", "video/3gpp", "video/x-ms-asf", "application/octet-stream", "application/x-datastream", "application/x-mpegURL", "application/sdp", "application/binhex", "application/json", }; const char* defaultPages[]={"index.html"}; //////////////////////////////////////////////////////////////////////////// // API callsc //////////////////////////////////////////////////////////////////////////// const char *dayNames="Sun\0Mon\0Tue\0Wed\0Thu\0Fri\0Sat"; const char *monthNames="Jan\0Feb\0Mar\0Apr\0May\0Jun\0Jul\0Aug\0Sep\0Oct\0Nov\0Dec"; const char *httpDateTimeFormat="%s, %02d %s %d %02d:%02d:%02d GMT"; char* mwGetVarValue(HttpVariables* vars, const char *varname, const char *defval) { if (vars && varname) { int i; for (i=0; (vars+i)->name; i++) { if (!strcmp((vars+i)->name,varname)) { return (vars+i)->value; } } } return (char*)defval; } int mwGetVarValueInt(HttpVariables* vars, const char *varname, int defval) { if (vars && varname) { int i; for (i=0; (vars+i)->name; i++) { if (!strcmp((vars+i)->name,varname)) { char *p = (vars+i)->value; return *p ? atoi(p) : defval; } } } return defval; } int64_t mwGetVarValueInt64(HttpVariables* vars, const char *varname) { if (vars && varname) { int i; for (i = 0; (vars + i)->name; i++) { if (!strcmp((vars + i)->name, varname)) { char *p = (vars + i)->value; return *p ? atoll(p) : 0; } } } return 0; } float mwGetVarValueFloat(HttpVariables* vars, const char *varname) { if (vars && varname) { int i; for (i = 0; (vars + i)->name; i++) { if (!strcmp((vars + i)->name, varname)) { char *p = (vars + i)->value; return *p ? (float)atof(p) : 0; } } } return 0; } static unsigned int hex2uint32(const char *p) { register char c; register unsigned int i = 0; for(c=*p;;){ if (c>='A' && c<='F') c-=7; else if (c>='a' && c<='f') c-=39; else if (c<'0' || c>'9') break; i=(i<<4)|(c&0xF); c=*(++p); } return i; } unsigned int mwGetVarValueHex(HttpVariables* vars, const char *varname, unsigned int defval) { int i; if (vars && varname) { for (i=0; (vars+i)->name; i++) { if (!strcmp((vars+i)->name,varname)) { char *p = (vars+i)->value; return p ? hex2uint32(p) : defval; } } } return defval; } int mwGetHttpDateTime(time_t timer, char *buf, int bufsize) { struct tm *btm; btm=gmtime(&timer); return snprintf(buf, bufsize, httpDateTimeFormat, dayNames+(btm->tm_wday<<2), btm->tm_mday, monthNames+(btm->tm_mon<<2), 1900+btm->tm_year, btm->tm_hour, btm->tm_min, btm->tm_sec); } void mwInitParam(HttpParam* hp, int port, const char* pchWebPath) { memset(hp, 0, sizeof(HttpParam)); hp->httpPort = port; hp->maxClients = HTTP_MAX_CLIENTS_DEFAULT; if (hp->pchWebPath) { free(hp->pchWebPath); hp->pchWebPath = 0; } hp->pchWebPath = strdup(pchWebPath ? pchWebPath : ""); } //////////////////////////////////////////////////////////////////////////// // mwServerStart // Start the webserver //////////////////////////////////////////////////////////////////////////// int mwServerStart(HttpParam* hp) { if (hp->bWebserverRunning) { return -1; } if (hp->maxClients == 0) { SYSLOG(LOG_INFO,"Maximum clients not set\n"); return -1; } if (!(hp->listenSocket=_mwStartListening(hp))) { return -1; } hp->hsSocketQueue = calloc(hp->maxClients, sizeof(HttpSocket)); hp->bKillWebserver=FALSE; hp->bWebserverRunning=TRUE; return 0; } //////////////////////////////////////////////////////////////////////////// // mwServerShutdown // Shutdown the webserver //////////////////////////////////////////////////////////////////////////// int mwServerShutdown(HttpParam* hp) { int i; if (hp->bKillWebserver || !hp->bWebserverRunning) return -1; _mwCloseAllConnections(hp); // signal webserver thread to quit hp->bKillWebserver=TRUE; // and wait for thread to exit for (i=0;hp->bWebserverRunning && i<30;i++) msleep(100); return 0; } int mwGetLocalFileName(HttpFilePath* hfp) { char ch; char *p = (char*)hfp->cFilePath; char *s = (char*)hfp->pchHttpPath; char *upLevel = NULL; hfp->pchExt=NULL; hfp->fTailSlash=0; if (*s == '~') { s++; } else if (hfp->pchRootPath) { p+=_mwStrCopy(hfp->cFilePath,hfp->pchRootPath); if (*(p-1)!=SLASH) { *p=SLASH; *(++p)=0; } } while ((ch=*s) && ch!='?' && (int)(p-hfp->cFilePath)cFilePath)-1) { if (ch=='%') { *(p++) = _mwDecodeCharacter(++s); s += 2; } else if (ch=='/') { *p=SLASH; upLevel=(++p); while (*(++s)=='/'); continue; } else if (ch=='+') { *(p++)=' '; s++; } else if (ch=='.') { if (upLevel && !memcmp(s+1, "./", 2)) { s+=2; p=upLevel; } else { *(p++)='.'; hfp->pchExt=p; while (*(++s)=='.'); //avoid '..' appearing in filename for security issue } } else { *(p++)=*(s++); } } if (*(p-1)==SLASH) { p--; hfp->fTailSlash=1; } *p=0; return (int)(p - hfp->cFilePath); } //////////////////////////////////////////////////////////////////////////// // Internal (private) helper functions //////////////////////////////////////////////////////////////////////////// SOCKET _mwStartListening(HttpParam* hp) { SOCKET listenSocket; struct sockaddr_in sinAddress; int iRc; // create a new socket listenSocket=socket(AF_INET,SOCK_STREAM,0); if (listenSocket <= 0) { return 0; } #ifndef ARDUINO // allow reuse of port number { int iOptVal=1; iRc=setsockopt(listenSocket,SOL_SOCKET,SO_REUSEADDR, (char*)&iOptVal,sizeof(iOptVal)); if (iRc<0) return 0; } #endif // bind it to the http port memset(&sinAddress,0,sizeof(struct sockaddr_in)); sinAddress.sin_family=AF_INET; // INADDR_ANY is 0 //sinAddress.sin_addr.s_addr=htonl(hp->dwBindIP); sinAddress.sin_addr.s_addr = hp->hlBindIP; sinAddress.sin_port = htons(hp->httpPort); // http port iRc=bind(listenSocket,(struct sockaddr*)&sinAddress, sizeof(struct sockaddr_in)); if (iRc<0) { return 0; } #ifndef WIN32 // set to non-blocking to avoid lockout issue (see Section 15.6 // in Unix network programming book) { int iSockFlags; iSockFlags = fcntl(listenSocket, F_GETFL, 0); iSockFlags |= O_NONBLOCK; fcntl(listenSocket, F_SETFL, iSockFlags); } #else { u_long iMode = 1; // non-blocking mode ioctlsocket(listenSocket, FIONBIO, &iMode); } #endif // listen on the socket for incoming calls if (listen(listenSocket, hp->maxClients)) { return 0; } // create UDP socket if (hp->udpPort) { hp->udpSocket = socket(AF_INET, SOCK_DGRAM, 0); memset(&sinAddress, 0, sizeof(struct sockaddr_in)); sinAddress.sin_family = AF_INET; sinAddress.sin_addr.s_addr = hp->hlBindIP; sinAddress.sin_port = htons(hp->udpPort); // UDP port bind(hp->udpSocket, (struct sockaddr*)&sinAddress, sizeof(struct sockaddr_in)); } return listenSocket; } void _mwInitSocketData(HttpSocket *phsSocket) { memset(&phsSocket->response,0,sizeof(HttpResponse)); if (!phsSocket->buffer) phsSocket->buffer = malloc(HTTP_BUFFER_SIZE); phsSocket->request.startByte = 0; phsSocket->request.pucHost = 0; phsSocket->request.pucReferer = 0; phsSocket->request.pucTransport = 0; phsSocket->request.pucPath = 0; phsSocket->request.headerSize = 0; phsSocket->request.payloadSize = 0; phsSocket->request.iCSeq = 0; phsSocket->request.pucAuthInfo = NULL; phsSocket->response.statusCode = 200; phsSocket->fp = 0; phsSocket->flags = 0; phsSocket->pucData = phsSocket->buffer; phsSocket->contentLength = 0; phsSocket->bufferSize = HTTP_BUFFER_SIZE; phsSocket->handler = NULL; phsSocket->mimeType = NULL; } static int _mwGetConnFromIP(HttpParam* hp, IPADDR ip) { int i; int connThisIP = 0; for (i = 0; i < hp->maxClients; i++) { if (!hp->hsSocketQueue[i].socket) continue; if (hp->hsSocketQueue[i].ipAddr.laddr == ip.laddr) { connThisIP++; } } return connThisIP; } void _mwCloseAllConnections(HttpParam* hp) { int i; if (hp->listenSocket) { closesocket(hp->listenSocket); hp->listenSocket = 0; } for (i = 0; i < hp->maxClients; i++) { if (hp->hsSocketQueue[i].socket) { closesocket(hp->hsSocketQueue[i].socket); hp->hsSocketQueue[i].socket = 0; } } } //////////////////////////////////////////////////////////////////////////// // _mwHttpThread // Webserver independant processing thread. Handles all connections //////////////////////////////////////////////////////////////////////////// void mwHttpLoop(HttpParam *hp, uint32_t timeout) { HttpSocket *phsSocketCur; SOCKET socket; struct sockaddr_in sinaddr; int iRc; int i; time_t tmCurrentTime; SOCKET iSelectMaxFds; fd_set fdsSelectRead; fd_set fdsSelectWrite; // clear descriptor sets FD_ZERO(&fdsSelectRead); FD_ZERO(&fdsSelectWrite); FD_SET(hp->listenSocket,&fdsSelectRead); iSelectMaxFds=hp->listenSocket; if (hp->udpSocket) { FD_SET(hp->udpSocket, &fdsSelectRead); if (hp->udpSocket > iSelectMaxFds) iSelectMaxFds = hp->udpSocket; } // get current time tmCurrentTime=time(NULL); // build descriptor sets and close timed out sockets for (i = 0; i < hp->maxClients; i++) { phsSocketCur = hp->hsSocketQueue + i; // get socket fd socket = phsSocketCur->socket; if (!socket) continue; { int iError=0; socklen_t iOptSize = sizeof(int); if (getsockopt(socket,SOL_SOCKET,SO_ERROR,(char*)&iError,&iOptSize)) { // if a socket contains a error, close it SYSLOG(LOG_INFO,"[%d] Socket no longer vaild.\n",socket); phsSocketCur->flags=FLAG_CONN_CLOSE; _mwCloseSocket(hp, phsSocketCur); continue; } } // check expiration timer (for non-listening, in-use sockets) if (tmCurrentTime > phsSocketCur->tmExpirationTime) { // close connection phsSocketCur->flags=FLAG_CONN_CLOSE; _mwCloseSocket(hp, phsSocketCur); } else { if (ISFLAGSET(phsSocketCur,FLAG_SENDING)) { // add to write descriptor set FD_SET(socket,&fdsSelectWrite); } else { // add to read descriptor set FD_SET(socket,&fdsSelectRead); } // check if new max socket if (socket>iSelectMaxFds) { iSelectMaxFds=socket; } } } { struct timeval tvSelectWait; // initialize select delay tvSelectWait.tv_sec = timeout / 1000; tvSelectWait.tv_usec = (timeout % 1000) * 1000; // note: using timeval here -> usec not nsec // and check sockets (may take a while!) iRc=select(iSelectMaxFds+1, &fdsSelectRead, &fdsSelectWrite, NULL, &tvSelectWait); } if (iRc <= 0) { return; } // check if any udp socket to read if (hp->udpSocket && FD_ISSET(hp->udpSocket, &fdsSelectRead)) { hp->pfnIncomingUDP(hp); } // check which sockets are read/write able for (i = 0; i < hp->maxClients; i++) { BOOL bRead; BOOL bWrite; phsSocketCur = hp->hsSocketQueue + i; // get socket fd socket = phsSocketCur->socket; if (!socket) continue; // get read/write status for socket bRead = FD_ISSET_BOOL(socket, &fdsSelectRead); bWrite = FD_ISSET_BOOL(socket, &fdsSelectWrite); if (bRead || bWrite) { iRc = -1; if (ISFLAGSET(phsSocketCur,FLAG_SENDING) && bWrite) { iRc=_mwProcessWriteSocket(hp, phsSocketCur); } else if (bRead) { SETFLAG(phsSocketCur, FLAG_RECEIVING); iRc=_mwProcessReadSocket(hp,phsSocketCur); } if (iRc == 0) { // reset expiration timer phsSocketCur->tmExpirationTime = time(NULL) + HTTP_EXPIRATION_TIME; } else { if (iRc == -1) { SETFLAG(phsSocketCur, FLAG_CONN_CLOSE); } _mwCloseSocket(hp, phsSocketCur); } } } // check if any socket to accept and accept the socket if (FD_ISSET(hp->listenSocket, &fdsSelectRead)) do { // find empty slot phsSocketCur = 0; for (i = 0; i < hp->maxClients; i++) { if (hp->hsSocketQueue[i].socket == 0) { phsSocketCur = hp->hsSocketQueue + i; break; } } if (!phsSocketCur) { // all slots occupied // find longest waiting idle socket and close it time_t earliest = 0; for (i = 0; i < hp->maxClients; i++) { if (!ISFLAGSET((hp->hsSocketQueue + i), FLAG_RECEIVING | FLAG_SENDING) && (earliest == 0 || hp->hsSocketQueue[i].tmExpirationTime < earliest)) { phsSocketCur = hp->hsSocketQueue + i; earliest = hp->hsSocketQueue[i].tmExpirationTime; } } if (!phsSocketCur) { SYSLOG(LOG_INFO,"Connection denied\n"); break; } else { SETFLAG(phsSocketCur, FLAG_CONN_CLOSE); _mwCloseSocket(hp, phsSocketCur); } } phsSocketCur->socket = _mwAcceptSocket(hp,&sinaddr); if (phsSocketCur->socket == 0) break; phsSocketCur->ipAddr.laddr=ntohl(sinaddr.sin_addr.s_addr); SYSLOG(LOG_INFO,"[%d] Client IP: %d.%d.%d.%d\n", phsSocketCur->socket, phsSocketCur->ipAddr.caddr[3], phsSocketCur->ipAddr.caddr[2], phsSocketCur->ipAddr.caddr[1], phsSocketCur->ipAddr.caddr[0]); hp->stats.clientCount++; //fill structure with data _mwInitSocketData(phsSocketCur); phsSocketCur->tmExpirationTime = time(NULL) + HTTP_EXPIRATION_TIME; //update max client count if (hp->stats.clientCount>hp->stats.clientCountMax) hp->stats.clientCountMax=hp->stats.clientCount; } while(0); } // end of _mwHttpThread void mwServerExit(HttpParam* hp) { int i; // cleanup _mwCloseAllConnections(hp); for (i = 0; i < hp->maxClients; i++) { if (hp->hsSocketQueue[i].buffer) free(hp->hsSocketQueue[i].buffer); } free(hp->hsSocketQueue); hp->hsSocketQueue = 0; // clear state vars hp->bKillWebserver = FALSE; hp->bWebserverRunning = FALSE; } //////////////////////////////////////////////////////////////////////////// // _mwAcceptSocket // Accept an incoming connection //////////////////////////////////////////////////////////////////////////// SOCKET _mwAcceptSocket(HttpParam* hp,struct sockaddr_in *sinaddr) { socklen_t namelen=sizeof(struct sockaddr); SOCKET socket = accept(hp->listenSocket, (struct sockaddr*)sinaddr,&namelen); #ifndef WIN32 // set to non-blocking to stop sends from locking up thread { int iSockFlags; iSockFlags = fcntl(socket, F_GETFL, 0); iSockFlags |= O_NONBLOCK; fcntl(socket, F_SETFL, iSockFlags); } #endif return socket; } // end of _mwAcceptSocket int _mwBuildHttpHeader(HttpParam* hp, HttpSocket *phsSocket, time_t contentDateTime, char* buffer) { char *p = buffer; char *end = buffer + 512; const char *status; BOOL keepalive = !ISFLAGSET(phsSocket,FLAG_CONN_CLOSE); if (phsSocket->response.statusCode >= 200 && phsSocket->response.statusCode < 200 + sizeof(status200) / sizeof(status200[0])) { status = status200[phsSocket->response.statusCode - 200]; } else if (phsSocket->response.statusCode >= 300 && phsSocket->response.statusCode < 300 + sizeof(status300) / sizeof(status300[0])) { status = status300[phsSocket->response.statusCode - 300]; } else if (phsSocket->response.statusCode >= 400 && phsSocket->response.statusCode < 400 + sizeof(status400) / sizeof(status400[0])) { status = status400[phsSocket->response.statusCode - 400]; } else if (phsSocket->response.statusCode >= 500 && phsSocket->response.statusCode < 500 + sizeof(status500) / sizeof(status500[0])) { status = status500[phsSocket->response.statusCode - 500]; } else { status = ""; } p += snprintf(p, end - p, HTTP200_HEADER, #ifdef ENABLE_RTSP (phsSocket->flags & (FLAG_REQUEST_DESCRIBE | FLAG_REQUEST_SETUP)) ? "RTSP/1.0" : "HTTP/1.1", #else "HTTP/1.1", #endif phsSocket->response.statusCode, status, HTTP_SERVER_NAME, keepalive ? "keep-alive" : "close"); if (keepalive) { p += snprintf(p, end - p, "Keep-Alive: timeout=%u, max=1000\r\n", HTTP_KEEPALIVE_TIME); } if (!(hp->flags & FLAG_DISABLE_RANGE)) { p += snprintf(p, end - p, "Accept-Ranges: bytes\r\n"); } if ((int)contentDateTime > 0) { p += snprintf(p, end - p, "Last-Modified: "); p += mwGetHttpDateTime(contentDateTime, p, end - p); strcpy(p, "\r\n"); p+=2; } if (phsSocket->request.iCSeq) { p += snprintf(p, end - p, "CSeq: %d\r\n", phsSocket->request.iCSeq); } if (phsSocket->response.contentLength > 0) { p += snprintf(p, end - p, "Content-Type: %s\r\n", phsSocket->mimeType ? phsSocket->mimeType : contentTypeTable[phsSocket->response.fileType]); if (phsSocket->request.startByte) { p += snprintf(p, end - p, "Content-Range: bytes %u-%u/*\r\n", phsSocket->request.startByte, phsSocket->response.contentLength); } } if (!(phsSocket->flags & FLAG_CHUNK)) { p+=snprintf(p, end - p,"Content-Length: %u\r\n", phsSocket->response.contentLength); } else { p += sprintf(p, "Transfer-Encoding: chunked\r\n"); } if (phsSocket->response.statusCode == 301 || phsSocket->response.statusCode == 307) { p += sprintf(p, "Location: %s\r\n", phsSocket->pucData); } strcpy(p, "\r\n"); return (int)(p- buffer + 2); } int mwParseQueryString(UrlHandlerParam* up) { if (up->iVarCount==-1) { //parsing variables from query string char *p,*s; // get start of query string s = strchr(up->pucRequest, '?'); if (s) { *(s++) = 0; } else if (ISFLAGSET(up->hs,FLAG_REQUEST_POST)){ s = up->hs->request.pucPayload; if (s && s[0] == '<') s = 0; } if (s && *s) { int i; int n = 1; //get number of variables for (p = s; *p ; p++) { if (*p < 32 || *p > 127) return 0; if (*p == '&') n++; } up->pxVars = calloc(n + 1, sizeof(HttpVariables)); up->iVarCount = n; //store variable name and value for (i = 0, p = s; i < n; p++) { switch (*p) { case '=': if (!(up->pxVars + i)->name) { *p = 0; (up->pxVars + i)->name = s; s=p+1; } break; case 0: case '&': *p = 0; if ((up->pxVars + i)->name) { (up->pxVars + i)->value = s; mwDecodeString(s); } else { (up->pxVars + i)->name = s; (up->pxVars + i)->value = p; } s = p + 1; i++; break; } } (up->pxVars + n)->name = NULL; } } return up->iVarCount; } //////////////////////////////////////////////////////////////////////////// // _mwBase64Encode // buffer size of out_str is (in_len * 4 / 3 + 1) //////////////////////////////////////////////////////////////////////////// void _mwBase64Encode(const char *in_str, int in_len, char *out_str) { const char base64[] ="ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"; int curr_out_len = 0; int i = 0; char a, b, c; out_str[0] = '\0'; if (in_len <= 0) return; while (i < in_len) { a = in_str[i]; b = (i + 1 >= in_len) ? 0 : in_str[i + 1]; c = (i + 2 >= in_len) ? 0 : in_str[i + 2]; if (i + 2 < in_len) { out_str[curr_out_len++] = (base64[(a >> 2) & 0x3F]); out_str[curr_out_len++] = (base64[((a << 4) & 0x30) + ((b >> 4) & 0xf)]); out_str[curr_out_len++] = (base64[((b << 2) & 0x3c) + ((c >> 6) & 0x3)]); out_str[curr_out_len++] = (base64[c & 0x3F]); } else if (i + 1 < in_len) { out_str[curr_out_len++] = (base64[(a >> 2) & 0x3F]); out_str[curr_out_len++] = (base64[((a << 4) & 0x30) + ((b >> 4) & 0xf)]); out_str[curr_out_len++] = (base64[((b << 2) & 0x3c) + ((c >> 6) & 0x3)]); out_str[curr_out_len++] = '='; } else { out_str[curr_out_len++] = (base64[(a >> 2) & 0x3F]); out_str[curr_out_len++] = (base64[((a << 4) & 0x30) + ((b >> 4) & 0xf)]); out_str[curr_out_len++] = '='; out_str[curr_out_len++] = '='; } i += 3; } out_str[curr_out_len] = '\0'; } //////////////////////////////////////////////////////////////////////////// // _mwGetBaisAuthorization // RETURN VALUE: Authorization string, need to free by caller //////////////////////////////////////////////////////////////////////////// int _mwGetBaisAuthorization(const char* username, const char* password, char* out /*OUT*/) { const char prefix[] = "Basic "; int len = (int)(strlen(username) + 1 + strlen(password)); int out_len = sizeof(prefix) + (len * 4 / 3 + 1) + 2; char *tmp, *p; if (out_len >= MAX_AUTH_INFO_LEN) return -1; tmp = (char*)malloc(len + 1); sprintf(tmp, "%s:%s", username, password); strcpy(out, prefix); p = out + sizeof(prefix) - 1; _mwBase64Encode(tmp, len, p); p += strlen(p); p[0] = '\r'; p[1] = '\n'; p[2] = '\0'; free(tmp); return 0; } //////////////////////////////////////////////////////////////////////////// // _mwSend401AuthorizationRequired //////////////////////////////////////////////////////////////////////////// void _mwSend401AuthorizationRequired(HttpParam* hp, HttpSocket* phsSocket, int reason) { char hdr[128]; const char *body = NULL; int hdrsize = 0; int bodylen; if (reason == AUTH_REQUIRED) { body = "Authentication required"; } else { body = "Authentication failed"; } bodylen = strlen(body); hdrsize = snprintf(hdr, sizeof(hdr), HTTP401_HEADER, "Login", bodylen); SYSLOG(LOG_INFO,"[%d] Authorization Required\n",phsSocket->socket); // send Authorization Required send(phsSocket->socket, hdr, hdrsize, 0); send(phsSocket->socket, body, bodylen, 0); //Below is the work around SETFLAG(phsSocket, FLAG_CONN_CLOSE); _mwCloseSocket(hp, phsSocket); } //////////////////////////////////////////////////////////////////////////// // _mwBasicAuthorizationHandlers // Basic Authorization implement // RETURN VALUE: // -1 (failed) // 0 (no need to authorization) // 1 (successed) // 2 (Authorization needed) //////////////////////////////////////////////////////////////////////////// int _mwBasicAuthorizationHandlers(HttpParam* hp, HttpSocket* phsSocket) { AuthHandler* pah; char* path = phsSocket->request.pucPath; int ret = AUTH_NO_NEED; if (phsSocket->ipAddr.laddr == INADDR_LOOPBACK) { return ret; } for (pah = hp->pxAuthHandler; pah && pah->pchUrlPrefix; pah++) { //printf("\req path:%s prefix:0x%x, username:0x%x, password:0x%x\n", path, pah->pchUrlPrefix, pah->pchUsername, pah->pchPassword); if (*pah->pchUrlPrefix && strncmp(path, pah->pchUrlPrefix, strlen(pah->pchUrlPrefix)) != 0) continue; if (pah->pchUsername == NULL || *pah->pchUsername == '\0' || pah->pchPassword == NULL || *pah->pchPassword == '\0') continue; if (*pah->pchAuthString == '\0') { if (_mwGetBaisAuthorization(pah->pchUsername, pah->pchPassword, pah->pchAuthString) != 0) continue; } if (phsSocket->request.pucAuthInfo == NULL) { ret = AUTH_REQUIRED; //Need to auth break; } else if (strncmp(phsSocket->request.pucAuthInfo, pah->pchAuthString, strlen(pah->pchAuthString)) == 0) { //FIXME: phsSocket->request.pucAuthInfo = pah->pchOtherInfo ? pah->pchOtherInfo : "group=admin"; ret = AUTH_SUCCESSED; //successed break; } else { ret = AUTH_FAILED; //Failed, try next } } return ret; } int _mwCheckUrlHandlers(HttpParam* hp, HttpSocket* phsSocket) { UrlHandler* puh; UrlHandlerParam up; int ret=0; char* path = phsSocket->request.pucPath; while (*path && *path == '/') path++; up.pxVars=NULL; for (puh=hp->pxUrlHandler; puh && puh->pchUrlPrefix; puh++) { size_t prefixLen=strlen(puh->pchUrlPrefix); if (puh->pfnUrlHandler && ((prefixLen == 0 && *path == 0) || (prefixLen && !strncmp(path,puh->pchUrlPrefix,prefixLen)))) { //URL prefix matches memset(&up, 0, sizeof(up)); up.hp=hp; up.hs = phsSocket; up.bufSize=(int)phsSocket->bufferSize; up.pucRequest=path+prefixLen; up.pucHeader=phsSocket->buffer; up.pucBuffer=phsSocket->pucData; up.pucBuffer[0]=0; up.pucPayload = phsSocket->request.pucPayload; up.payloadSize = phsSocket->request.payloadSize; up.iVarCount=-1; phsSocket->handler = puh; if (strchr(up.pucRequest, '?')) mwParseQueryString(&up); ret=(*puh->pfnUrlHandler)(&up); if (!ret) continue; if (phsSocket->response.statusCode >= 500) phsSocket->flags |= FLAG_CONN_CLOSE; phsSocket->flags|=ret; phsSocket->response.fileType = up.contentType; if (ret & FLAG_DATA_RAW) { SETFLAG(phsSocket, FLAG_DATA_RAW); phsSocket->pucData=up.pucBuffer; phsSocket->contentLength=up.contentLength; phsSocket->response.contentLength=up.contentLength; if (ret & FLAG_TO_FREE) { phsSocket->ptr=up.pucBuffer; //keep the pointer which will be used to free memory later } } else if (ret & FLAG_DATA_STREAM) { SETFLAG(phsSocket, FLAG_DATA_STREAM); phsSocket->pucData = up.pucBuffer; phsSocket->contentLength = up.contentLength; phsSocket->response.contentLength = 0; } else if (ret & FLAG_DATA_FILE) { SETFLAG(phsSocket, FLAG_DATA_FILE); if (up.pucBuffer[0]) { free(phsSocket->request.pucPath); phsSocket->request.pucPath=strdup(up.pucBuffer); } } else if (ret & FLAG_DATA_REDIRECT) { phsSocket->pucData = up.pucBuffer; } _mwFreeJSONPairs(&up); break; } } if (up.pxVars) free(up.pxVars); return ret; } //////////////////////////////////////////////////////////////////////////// // _mwProcessReadSocket // Process a socket (read) //////////////////////////////////////////////////////////////////////////// int _mwProcessReadSocket(HttpParam* hp, HttpSocket* phsSocket) { int iLength = recv(phsSocket->socket, phsSocket->pucData+phsSocket->contentLength, (int)(phsSocket->bufferSize - phsSocket->contentLength - 1), 0); if (iLength <= 0) { return -1; } // add in new data received phsSocket->contentLength += iLength; phsSocket->pucData[phsSocket->contentLength] = 0; // check if end of request if (phsSocket->request.headerSize==0) { int i=0; char *path = 0; char *headerEnd = strstr(phsSocket->buffer, HTTP_HEADER_END); if (!headerEnd) return 0; // reach the end of the header //check request type if (!memcmp(phsSocket->buffer, "GET", 3)) { SETFLAG(phsSocket,FLAG_REQUEST_GET); path = phsSocket->pucData + 5; } else if (!memcmp(phsSocket->buffer, "POST", 4)) { SETFLAG(phsSocket,FLAG_REQUEST_POST); path = phsSocket->pucData + 6; } else { SYSLOG(LOG_INFO,"[%d] Unsupported method\n",phsSocket->socket); phsSocket->request.pucPath = 0; return -1; } // count connections from this IP and duplicated connection if (hp->maxClientsPerIP && _mwGetConnFromIP(hp, phsSocket->ipAddr) > hp->maxClientsPerIP) { // too many connection from the same IP SYSLOG(LOG_INFO,"[%d] Too many connections from same IP\n", phsSocket->socket); send(phsSocket->socket, HTTP403_HEADER, sizeof(HTTP403_HEADER) - 1, 0); return -1; } phsSocket->request.headerSize = (int)(headerEnd - phsSocket->buffer + 4); if (_mwParseHttpHeader(phsSocket)) { SYSLOG(LOG_INFO,"Error parsing request\n"); return -1; } else { int pathLen; if ((hp->flags & FLAG_DISABLE_RANGE) && phsSocket->request.startByte > 0) { send(phsSocket->socket, HTTP403_HEADER, sizeof(HTTP403_HEADER) - 1, 0); return -1; } // keep request path for (i = 0; i < MAX_REQUEST_PATH_LEN; i++) { if ((path[i] == ' ' && (!strncmp(path + i + 1, "HTTP/", 5) || !strncmp(path + i + 1, "RTSP/", 5))) || path[i] == '\r') { break; } } pathLen = i; if (pathLen >= MAX_REQUEST_PATH_LEN) { return -1; } phsSocket->request.pucPath = malloc(pathLen + 1); memcpy(phsSocket->request.pucPath, path, pathLen); phsSocket->request.pucPath[pathLen] = 0; SYSLOG(LOG_INFO, "[%d] Request path: %s\n", phsSocket->socket, phsSocket->request.pucPath); if (ISFLAGSET(phsSocket,FLAG_REQUEST_POST)) { if (!phsSocket->request.pucPayload) { // first receive of payload, prepare for next receive if (phsSocket->request.payloadSize > MAX_POST_PAYLOAD_SIZE) phsSocket->request.payloadSize = MAX_POST_PAYLOAD_SIZE; phsSocket->bufferSize = phsSocket->request.payloadSize + 1; phsSocket->request.pucPayload = malloc(phsSocket->bufferSize); phsSocket->pucData = phsSocket->request.pucPayload; // payload length already received phsSocket->contentLength -= phsSocket->request.headerSize; // copy already received payload to payload buffer if (phsSocket->contentLength > phsSocket->request.payloadSize) { phsSocket->contentLength = phsSocket->request.payloadSize; } memcpy(phsSocket->request.pucPayload, phsSocket->buffer + phsSocket->request.headerSize, phsSocket->contentLength); phsSocket->request.pucPayload[phsSocket->contentLength]=0; } } } } if (phsSocket->request.payloadSize > 0 && phsSocket->contentLength < (int)phsSocket->request.payloadSize) { // there is more data return 0; } // add header zero terminator phsSocket->buffer[phsSocket->request.headerSize]=0; if (phsSocket->request.headerSize) { phsSocket->pucData = phsSocket->buffer + phsSocket->request.headerSize + 4; phsSocket->bufferSize = HTTP_BUFFER_SIZE - phsSocket->request.headerSize - 4; } else { phsSocket->pucData = phsSocket->buffer; phsSocket->bufferSize = HTTP_BUFFER_SIZE; } hp->stats.reqCount++; phsSocket->reqCount++; if (hp->pxAuthHandler != NULL) { int ret = _mwBasicAuthorizationHandlers(hp, phsSocket); switch (ret) { case AUTH_NO_NEED: //No need to auth case AUTH_SUCCESSED: //Successed break; case AUTH_REQUIRED: //Authorization needed case AUTH_FAILED: default://Failed _mwSend401AuthorizationRequired(hp, phsSocket, ret); return 0; } } if (!hp->pxUrlHandler || !_mwCheckUrlHandlers(hp,phsSocket)) SETFLAG(phsSocket,FLAG_DATA_FILE); // set state to SENDING (actual sending will occur on next select) CLRFLAG(phsSocket,FLAG_RECEIVING) if (phsSocket->request.iHttpVer == 0) { CLRFLAG(phsSocket, FLAG_CHUNK); } if (ISFLAGSET(phsSocket,FLAG_DATA_RAW | FLAG_DATA_STREAM)) { SETFLAG(phsSocket,FLAG_SENDING); return _mwStartSendRawData(hp, phsSocket); } else if (ISFLAGSET(phsSocket,FLAG_DATA_FILE)) { SETFLAG(phsSocket,FLAG_SENDING); return _mwStartSendFile(hp,phsSocket); } else if (ISFLAGSET(phsSocket,FLAG_DATA_REDIRECT)) { _mwRedirect(phsSocket, phsSocket->pucData); return 0; } SYSLOG(LOG_INFO,"Invalid data flag specified\n"); return -1; } // end of _mwProcessReadSocket //////////////////////////////////////////////////////////////////////////// // _mwProcessWriteSocket // Process a socket (write) //////////////////////////////////////////////////////////////////////////// int _mwProcessWriteSocket(HttpParam *hp, HttpSocket* phsSocket) { if (phsSocket->contentLength<=0 && !ISFLAGSET(phsSocket,FLAG_DATA_STREAM)) { return 1; } //SYSLOG(LOG_INFO,"[%d] sending data\n",phsSocket->socket); if (ISFLAGSET(phsSocket,FLAG_DATA_RAW|FLAG_DATA_STREAM)) { return _mwSendRawDataChunk(hp, phsSocket); } else if (ISFLAGSET(phsSocket,FLAG_DATA_FILE)) { return _mwSendFileChunk(hp, phsSocket); } else { SYSLOG(LOG_INFO,"Invalid content source\n"); return -1; } } // end of _mwProcessWriteSocket //////////////////////////////////////////////////////////////////////////// // _mwCloseSocket // Close an open connection //////////////////////////////////////////////////////////////////////////// void _mwCloseSocket(HttpParam* hp, HttpSocket* phsSocket) { if (phsSocket->socket == 0) return; if (phsSocket->fp) { fclose(phsSocket->fp); phsSocket->fp = 0; hp->stats.openedFileCount--; } if (phsSocket->request.pucPayload) { free(phsSocket->request.pucPayload); phsSocket->request.pucPayload = 0; } if (phsSocket->handler && (ISFLAGSET(phsSocket,FLAG_DATA_STREAM) || ISFLAGSET(phsSocket,FLAG_CLOSE_CALLBACK | FLAG_CONN_CLOSE) == (FLAG_CLOSE_CALLBACK | FLAG_CONN_CLOSE))) { UrlHandlerParam up; UrlHandler* pfnHandler = (UrlHandler*)phsSocket->handler; memset(&up, 0, sizeof(up)); up.hs = phsSocket; up.hp = hp; //notify the handler of closed connection (pfnHandler->pfnUrlHandler)(&up); //unbind handler phsSocket->handler = 0; } if (ISFLAGSET(phsSocket,FLAG_TO_FREE) && phsSocket->ptr) { free(phsSocket->ptr); phsSocket->ptr=NULL; CLRFLAG(phsSocket, FLAG_TO_FREE); } if (phsSocket->request.pucPath) { free(phsSocket->request.pucPath); phsSocket->request.pucPath = 0; } if (!ISFLAGSET(phsSocket,FLAG_CONN_CLOSE) && phsSocket->reqCount < HTTP_KEEPALIVE_MAX) { _mwInitSocketData(phsSocket); //reset flag bits phsSocket->tmExpirationTime=time(NULL)+HTTP_KEEPALIVE_TIME; return; } closesocket(phsSocket->socket); phsSocket->reqCount = 0; hp->stats.clientCount--; SYSLOG(LOG_INFO,"[%d] Socket closed, %u connections\n",phsSocket->socket, hp->stats.clientCount); phsSocket->socket = 0; phsSocket->reqCount=0; } // end of _mwCloseSocket int _mwStrCopy(char *dest, const char *src) { int i; for (i=0; src[i]; i++) { dest[i]=src[i]; } dest[i]=0; return i; } int _mwStrHeadMatch(char** pbuf1, const char* buf2) { unsigned int i; char* buf1 = *pbuf1; int x; for(i=0;buf2[i];i++) { if ((x=toupper((int)buf1[i])-toupper((int)buf2[i]))) return 0; } *pbuf1 = buf1 + i; return i; } void _mwSendErrorPage(SOCKET socket, const char* header, const char* body) { char hdr[128]; int len = (int)strlen(body); int hdrsize = snprintf(hdr, sizeof(hdr), header, HTTP_SERVER_NAME, len); send(socket, hdr, hdrsize, 0); send(socket, body, len, 0); } #ifdef WIN32 #define OPEN_FLAG O_RDONLY|0x8000 #else #define OPEN_FLAG O_RDONLY #endif //////////////////////////////////////////////////////////////////////////// // _mwStartSendFile // Setup for sending of a file //////////////////////////////////////////////////////////////////////////// int _mwStartSendFile2(HttpParam* hp, HttpSocket* phsSocket, const char* filePath) { struct stat st = { 0 }; HttpFilePath hfp; BOOL isDirPath = FALSE; if (filePath == NULL) return -1; if (!ISFLAGSET(phsSocket, FLAG_ABSOLUTE_PATH)) { hfp.pchRootPath = hp->pchWebPath; hfp.pchHttpPath = filePath; mwGetLocalFileName(&hfp); } else { strncpy(hfp.cFilePath, filePath, sizeof(hfp.cFilePath) - 1); } if (stat(hfp.cFilePath, &st) == 0) { isDirPath = (st.st_mode & S_IFDIR); } if (!*filePath) isDirPath = TRUE; // open file if (!isDirPath) { phsSocket->fp = fopen(hfp.cFilePath, "rb"); } if (!phsSocket->fp) { char *p; if (!isDirPath) { // file/dir not found return -1; } //requesting for directory, first try opening default pages for (p = hfp.cFilePath; *p; p++); *(p++)=SLASH; for (int i = 0; ifp = fopen(hfp.cFilePath, "rb"); if (phsSocket->fp) { hfp.pchExt = strchr(defaultPages[i], '.') + 1; break; } } if (!phsSocket->fp && (hp->flags & FLAG_DIR_LISTING)) { SETFLAG(phsSocket,FLAG_DATA_RAW); if (!hfp.fTailSlash) { p=phsSocket->request.pucPath; while(*p) p++; //seek to the end of the string strcpy(p, "/"); //add a tailing slash while(--p>(char*)phsSocket->request.pucPath) { if (*p=='/') { p++; break; } } _mwRedirect(phsSocket,p); return _mwStartSendRawData(hp, phsSocket); } } } if (phsSocket->fp) { hp->stats.openedFileCount++; fseek(phsSocket->fp, 0, SEEK_END); long fileSize = ftell(phsSocket->fp); phsSocket->response.contentLength = fileSize - phsSocket->request.startByte; if (phsSocket->response.contentLength <= 0) { phsSocket->request.startByte = 0; phsSocket->response.contentLength = fileSize; } if (phsSocket->request.startByte) { fseek(phsSocket->fp, (long)phsSocket->request.startByte, SEEK_SET); phsSocket->response.statusCode = 206; } else { fseek(phsSocket->fp, 0, SEEK_SET); } if (!phsSocket->response.fileType && hfp.pchExt) { phsSocket->response.fileType=mwGetContentType(hfp.pchExt); } } else { return -1; } //SYSLOG(LOG_INFO,"File/requested size: %d/%d\n",st.st_size,phsSocket->response.contentLength); // build http header phsSocket->contentLength=_mwBuildHttpHeader( hp, phsSocket, st.st_mtime, phsSocket->pucData); phsSocket->response.headerBytes = phsSocket->contentLength; phsSocket->response.sentBytes = 0; return 0; } // end of _mwStartSendFile2 //////////////////////////////////////////////////////////////////////////// // _mwStartSendFile // Setup for sending of a file //////////////////////////////////////////////////////////////////////////// int _mwStartSendFile(HttpParam* hp, HttpSocket* phsSocket) { int ret; #if MAX_OPEN_FILES if (hp->stats.openedFileCount >= MAX_OPEN_FILES) return 0; #endif ret = _mwStartSendFile2(hp, phsSocket, phsSocket->request.pucPath); if (ret != 0) { SYSLOG(LOG_INFO,"[%d] Not found - %s\n",phsSocket->socket, phsSocket->request.pucPath); _mwSendErrorPage(phsSocket->socket, HTTP404_HEADER, HTTP404_BODY); } return ret; } //end of _mwStartSendFile //////////////////////////////////////////////////////////////////////////// // _mwSendFileChunk // Send a chunk of a file //////////////////////////////////////////////////////////////////////////// int _mwSendFileChunk(HttpParam *hp, HttpSocket* phsSocket) { int iBytesWritten; int iBytesRead; if (phsSocket->contentLength > 0) { if ((phsSocket->flags & FLAG_CHUNK) && ISFLAGSET(phsSocket, FLAG_HEADER_SENT)) { char buf[16]; iBytesRead = snprintf(buf, sizeof(buf), "%X\r\n", phsSocket->contentLength); iBytesWritten = send(phsSocket->socket, buf, iBytesRead, 0); } // send a chunk of data iBytesWritten=send(phsSocket->socket, phsSocket->pucData,(int)phsSocket->contentLength, 0); if (iBytesWritten<=0) { // close connection SETFLAG(phsSocket,FLAG_CONN_CLOSE); fclose(phsSocket->fp); hp->stats.openedFileCount--; phsSocket->fp = 0; return -1; } SETFLAG(phsSocket, FLAG_HEADER_SENT); hp->stats.totalSentBytes+=iBytesWritten; phsSocket->response.sentBytes+=iBytesWritten; phsSocket->pucData+=iBytesWritten; phsSocket->contentLength-=iBytesWritten; // if only partial data sent just return wait the remaining data to be sent next time if (phsSocket->contentLength>0) return 0; } // used all buffered data - load next chunk of file phsSocket->pucData=phsSocket->buffer; iBytesRead = fread(phsSocket->buffer, 1, HTTP_BUFFER_SIZE, phsSocket->fp); if (iBytesRead == -1 && errno == 8) return 0; // try reading again next time if (iBytesRead<=0) { // finished with a file int remainBytes = (int)(phsSocket->response.contentLength + phsSocket->response.headerBytes - phsSocket->response.sentBytes); if (remainBytes > 0) { if (remainBytes>HTTP_BUFFER_SIZE) remainBytes=HTTP_BUFFER_SIZE; memset(phsSocket->buffer,0,remainBytes); phsSocket->contentLength=remainBytes; return 0; } else { if (phsSocket->flags & FLAG_CHUNK) { send(phsSocket->socket, "0\r\n\r\n", 5, 0); } if (phsSocket->fp) { fclose(phsSocket->fp); phsSocket->fp = 0; hp->stats.openedFileCount--; } return 1; } } phsSocket->contentLength=iBytesRead; return 0; } // end of _mwSendFileChunk //////////////////////////////////////////////////////////////////////////// // _mwStartSendRawData // Start sending raw data blocks //////////////////////////////////////////////////////////////////////////// int _mwStartSendRawData(HttpParam *hp, HttpSocket* phsSocket) { if (ISFLAGSET(phsSocket, FLAG_CUSTOM_HEADER)) { return _mwSendRawDataChunk(hp, phsSocket); } else { char header[HTTP200_HDR_EST_SIZE]; int offset=0,hdrsize,bytes; hdrsize=_mwBuildHttpHeader(hp, phsSocket, 0, header); // send http header do { bytes=send(phsSocket->socket, header+offset, hdrsize-offset, 0); if (bytes<=0) break; offset+=bytes; hp->stats.totalSentBytes+=bytes; } while (offsetflags & FLAG_CHUNK) { char buf[16]; int bytes = snprintf(buf, sizeof(buf), "%x\r\n", phsSocket->contentLength); iBytesWritten = send(phsSocket->socket, buf, bytes, 0); if (iBytesWritten<=0) return -1; hp->stats.totalSentBytes+=iBytesWritten; } // send a chunk of data if (phsSocket->contentLength > 0) { iBytesWritten=(int)send(phsSocket->socket, phsSocket->pucData, phsSocket->contentLength, 0); if (iBytesWritten<=0) { // failure - close connection return -1; } else { hp->stats.totalSentBytes+=iBytesWritten; phsSocket->response.sentBytes+=iBytesWritten; phsSocket->pucData+=iBytesWritten; phsSocket->contentLength-=iBytesWritten; } } else { if (ISFLAGSET(phsSocket,FLAG_DATA_STREAM) && phsSocket->handler) { //load next chuck of raw data UrlHandlerParam up; UrlHandler* pfnHandler = (UrlHandler*)phsSocket->handler; memset(&up, 0, sizeof(up)); up.hs = phsSocket; up.hp = hp; up.pucBuffer=phsSocket->buffer; up.bufSize=HTTP_BUFFER_SIZE; if ((pfnHandler->pfnUrlHandler)(&up) == 0) { if (phsSocket->flags & FLAG_CHUNK) { iBytesWritten = send(phsSocket->socket, "0\r\n\r\n", 5, 0); if (iBytesWritten<=0) return -1; } hp->stats.totalSentBytes+=iBytesWritten; SETFLAG(phsSocket, FLAG_CONN_CLOSE); return 1; // EOF } phsSocket->contentLength = up.contentLength; phsSocket->pucData = up.pucBuffer; } else { if (phsSocket->flags & FLAG_CHUNK) { iBytesWritten = send(phsSocket->socket, "0\r\n\r\n", 5, 0); if (iBytesWritten<=0) return -1; hp->stats.totalSentBytes+=iBytesWritten; } return 1; } } return 0; } // end of _mwSendRawDataChunk //////////////////////////////////////////////////////////////////////////// // _mwRedirect // Setup for redirect to another file //////////////////////////////////////////////////////////////////////////// void _mwRedirect(HttpSocket* phsSocket, char* pchPath) { /* char* path; // raw (not file) data send mode SETFLAG(phsSocket,FLAG_DATA_RAW); // messages is HTML phsSocket->response.fileType=HTTPFILETYPE_HTML; // build redirect message SYSLOG(LOG_INFO,"[%d] Http redirection to %s\n",phsSocket->socket,pchPath); path = (pchPath == (char*)phsSocket->pucData) ? strdup(pchPath) : (char*)pchPath; phsSocket->contentLength=snprintf(phsSocket->pucData, 512, HTTPBODY_REDIRECT,path); phsSocket->response.contentLength=phsSocket->contentLength; if (path != pchPath) free(path); */ char* url = strdup(pchPath); int n = snprintf(phsSocket->pucData, phsSocket->contentLength, HTTP301_HEADER, HTTP_SERVER_NAME, url); free(url); send(phsSocket->socket, phsSocket->pucData, n, 0); } // end of _mwRedirect //////////////////////////////////////////////////////////////////////////// // _mwStrStrNoCase // Case insensitive version of ststr //////////////////////////////////////////////////////////////////////////// char* _mwStrStrNoCase(char* pchHaystack, char* pchNeedle) { char* pchReturn=NULL; while(*pchHaystack!='\0' && pchReturn==NULL) { if (toupper((int)*pchHaystack)==toupper((int)pchNeedle[0])) { char* pchTempHay=pchHaystack; char* pchTempNeedle=pchNeedle; // start of match while(*pchTempHay!='\0') { if(toupper((int)*pchTempHay)!=toupper((int)*pchTempNeedle)) { // failed to match break; } pchTempNeedle++; pchTempHay++; if (*pchTempNeedle=='\0') { // completed match pchReturn=pchHaystack; break; } } } pchHaystack++; } return pchReturn; } // end of _mwStrStrNoCase //////////////////////////////////////////////////////////////////////////// // _mwDecodeCharacter // Convert and individual character //////////////////////////////////////////////////////////////////////////// char _mwDecodeCharacter(char* s) { register unsigned char v; if (!*s) return 0; if (*s>='a' && *s<='f') v = *s-('a'-'A'+7); else if (*s>='A' && *s<='F') v = *s-7; else v = *s; if (*(++s)==0) return v; v <<= 4; if (*s>='a' && *s<='f') v |= (*s-('a'-'A'+7)) & 0xf; else if (*s>='A' && *s<='F') v |= (*s-7) & 0xf; else v |= *s & 0xf; return v; } // end of _mwDecodeCharacter //////////////////////////////////////////////////////////////////////////// // _mwDecodeString // This function converts URLd characters back to ascii. For example // %3A is '.' //////////////////////////////////////////////////////////////////////////// void mwDecodeString(char* pchString) { int bEnd=FALSE; char* pchInput=pchString; char* pchOutput=pchString; do { switch (*pchInput) { case '%': if (*(pchInput+1)=='\0' || *(pchInput+2)=='\0') { // something not right - terminate the string and abort *pchOutput='\0'; bEnd=TRUE; } else { *pchOutput=_mwDecodeCharacter(pchInput+1); pchInput+=3; } break; case '+': *pchOutput=' '; pchInput++; break; case '\0': bEnd=TRUE; // drop through default: // copy character *pchOutput=*pchInput; pchInput++; } pchOutput++; } while (!bEnd); } // end of mwDecodeString int mwGetContentType(const char *pchExtname) { DWORD dwExt = 0; // check type of file requested if (pchExtname[1]=='\0') { return HTTPFILETYPE_OCTET; } else if (pchExtname[2]=='\0') { memcpy(&dwExt, pchExtname, 2); switch (GETDWORD(pchExtname) & 0xffdfdf) { case FILEEXT_JS: return HTTPFILETYPE_JS; case FILEEXT_TS: return HTTPFILETYPE_TS; } } else if (pchExtname[3]=='\0' || pchExtname[3]=='?') { //identify 3-char file extensions memcpy(&dwExt, pchExtname, sizeof(dwExt)); switch (dwExt & 0xffdfdfdf) { case FILEEXT_HTM: return HTTPFILETYPE_HTML; case FILEEXT_XML: return HTTPFILETYPE_XML; case FILEEXT_XSL: return HTTPFILETYPE_XML; case FILEEXT_TEXT: return HTTPFILETYPE_TEXT; case FILEEXT_XUL: return HTTPFILETYPE_XUL; case FILEEXT_CSS: return HTTPFILETYPE_CSS; case FILEEXT_PNG: return HTTPFILETYPE_PNG; case FILEEXT_JPG: return HTTPFILETYPE_JPEG; case FILEEXT_GIF: return HTTPFILETYPE_GIF; case FILEEXT_SWF: return HTTPFILETYPE_SWF; case FILEEXT_MPA: return HTTPFILETYPE_MPA; case FILEEXT_MPG: return HTTPFILETYPE_MPEG; case FILEEXT_AVI: return HTTPFILETYPE_AVI; case FILEEXT_MP4: return HTTPFILETYPE_MP4; case FILEEXT_MOV: return HTTPFILETYPE_MOV; case FILEEXT_264: return HTTPFILETYPE_264; case FILEEXT_FLV: return HTTPFILETYPE_FLV; case FILEEXT_3GP: return HTTPFILETYPE_3GP; case FILEEXT_ASF: return HTTPFILETYPE_ASF; case FILEEXT_SDP: return HTTPFILETYPE_SDP; } } else if (pchExtname[4]=='\0' || pchExtname[4]=='?') { memcpy(&dwExt, pchExtname, sizeof(dwExt)); //logic-and with 0xdfdfdfdf gets the uppercase of 4 chars switch (dwExt & 0xdfdfdfdf) { case FILEEXT_HTML: return HTTPFILETYPE_HTML; case FILEEXT_MPEG: return HTTPFILETYPE_MPEG; case FILEEXT_M3U8: return HTTPFILETYPE_M3U8; } } return HTTPFILETYPE_OCTET; } int _mwGrabToken(char *pchToken, char chDelimiter, char *pchBuffer, int iMaxTokenSize) { char *p=pchToken; int iCharCopied=0; while (*p && *p!=chDelimiter && iCharCopied < iMaxTokenSize - 1) { *(pchBuffer++)=*(p++); iCharCopied++; } *pchBuffer='\0'; return (*p==chDelimiter)?iCharCopied:0; } int _mwParseHttpHeader(HttpSocket* phsSocket) { int iLen; char *p=phsSocket->buffer; //pointer to header data HttpRequest *req=&phsSocket->request; CLRFLAG(phsSocket, FLAG_MULTIPART); p = strstr(phsSocket->buffer, "HTTP/1."); if (!p) return -1; p += 7; req->iHttpVer = (*p - '0'); //parse the rest of header for(;;) { //look for a valid field name while (*p && *p!='\r') p++; //travel to '\r' if (!*p || !memcmp(p, HTTP_HEADER_END, sizeof(HTTP_HEADER_END))) break; p+=2; //skip "\r\n" if (_mwStrHeadMatch(&p,"Connection: ")) { if (_mwStrHeadMatch(&p,"close")) { SETFLAG(phsSocket,FLAG_CONN_CLOSE); } else if (_mwStrHeadMatch(&p,"Keep-Alive")) { CLRFLAG(phsSocket,FLAG_CONN_CLOSE); //FIXME!! } } else if (_mwStrHeadMatch(&p, "Content-Length: ")) { char buf[32]; p+=_mwGrabToken(p,'\r',buf,sizeof(buf)); phsSocket->request.payloadSize = atoi(buf); } else if (_mwStrHeadMatch(&p, "CSeq: ")) { phsSocket->request.iCSeq = atoi(p); } else if (_mwStrHeadMatch(&p,"Referer: ")) { phsSocket->request.pucReferer= p; } else if (_mwStrHeadMatch(&p,"Range: bytes=")) { char buf[32]; int iEndByte; iLen=_mwGrabToken(p,'-',buf,sizeof(buf)); if (iLen==0) continue; p+=iLen; phsSocket->request.startByte=atoi(buf); iLen=_mwGrabToken(p,'/',buf,sizeof(buf)); if (iLen==0) continue; p+=iLen; iEndByte = atoi(buf); if (iEndByte > 0) phsSocket->response.contentLength = (int)(iEndByte-phsSocket->request.startByte+1); } else if (_mwStrHeadMatch(&p,"Host: ")) { phsSocket->request.pucHost = p; } else if (_mwStrHeadMatch(&p,"Transport: ")) { phsSocket->request.pucTransport = p; } else if (_mwStrHeadMatch(&p,"Authorization: ")) { phsSocket->request.pucAuthInfo = p; } else if (_mwStrHeadMatch(&p,"X-Forwarded-For: ")) { int i; for (i = 3; i >= 0 && *p; i--) { phsSocket->ipAddr.caddr[i] = atoi(p); while (*p && *p != '\r' && *(p++) != '.'); } } } return 0; //end of header } //////////////////////////////////////////////////////////////////////////// // _mwCheckAuthentication // Check if a connected peer is authenticated //////////////////////////////////////////////////////////////////////////// BOOL _mwCheckAuthentication(HttpParam *hp, HttpSocket* phsSocket) { if (!ISFLAGSET(phsSocket,FLAG_AUTHENTICATION)) return TRUE; if (hp->dwAuthenticatedNode != phsSocket->ipAddr.laddr) { // Not authenticated hp->stats.authFailCount++; return FALSE; } // Extend authentication period hp->tmAuthExpireTime = time(NULL) + HTTPAUTHTIMEOUT; return TRUE; } //////////////////////////// END OF FILE ///////////////////////////////////