diff options
Diffstat (limited to 'src/proxy/proxytrack.c')
-rw-r--r-- | src/proxy/proxytrack.c | 1621 |
1 files changed, 1621 insertions, 0 deletions
diff --git a/src/proxy/proxytrack.c b/src/proxy/proxytrack.c new file mode 100644 index 0000000..7604804 --- /dev/null +++ b/src/proxy/proxytrack.c @@ -0,0 +1,1621 @@ +/* ------------------------------------------------------------ */ +/* +HTTrack Website Copier, Offline Browser for Windows and Unix +Copyright (C) Xavier Roche and other contributors + +This program 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 2 +of the License, or any later version. + +This program 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 this program; if not, write to the Free Software +Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + +Please visit our Website: http://www.httrack.com +*/ + +/* ------------------------------------------------------------ */ +/* File: ProxyTrack, httrack cache-based proxy */ +/* Author: Xavier Roche */ +/* ------------------------------------------------------------ */ + + +/* + +/\/\/\/\/\/\/\/\/\/\/\/\/\ PENDING WORK /\/\/\/\/\/\/\/\/\/\/\/\/\ +- Etag update handling +- Other cache archive handling (.arc) +- Live plug/unplug of archives +- Listing +\/\/\/\/\/\/\/\/\/\/\/\/\/ PENDING WORK \/\/\/\/\/\/\/\/\/\/\/\/\/ + +*/ + +/* +Architecture rough draft +Xavier Roche 2005 + +Aim: Building a sub-proxy to be linked with other top level proxies (such as Squid) +Basic design: Classical HTTP/1.0 proxy server, with ICP server support +Internal data design: HTTrack cache indexing in fast hashtables, with 'pluggable' design (add/removal of caches on-the-fly) + + +Index structure organization: +----------------------------- + +foo/hts-cache/new.zip -----> Index[0] \ +bar/hts-cache/new.zip -----> Index[1] > Central Index Lookup (CIL) +baz/hts-cache/new.zip -----> Index[2] / +.. -----> .. + +Indexes are hashtables with URL (STRING) -> INTEGER lookup. + +URL -----> CIL Ask for index ID +URL -----> Index[ID] Ask for index properties (ZIP cache index) + + +Lookup of an entry: +------------------- + +ID = CIL[URL] +If ID is valid Then + return SUCCESS +Else + return FAILURE +EndIf + + +Fetching of an entry: +--------------------- + +RESOURCE = null +ID = CIL[URL] +If ID is valid Then + OFFSET = Index[ID][URL] + If OFFSET is valid Then + RESOURCE = Fetch(ID, OFFSET) + EndIf +EndIf + + +Removal of index N: +------------------- + +For all entries in Index[N] + URL(key) -----> Lookup all other caches + Found: Replace in CIL + Not Found: Delete entry in CIL +Done +Delete Index[N] + + +Adding of index N: +------------------ + +Build Index[N] +For all entries in Index[N] + URL(key) -----> Lookup in CIL + Found: Do nothing if corresponding Cache is newer than this one + Not Found: Add/Replace entry in CIL +Done + +Remark: If no cache newer than the added one is found, all entries can be added without any lookup (optim) + +*/ + +/* HTTrack definitions */ +#include "htsbase.h" +#include "htsnet.h" +#include "htslib.h" +#include "htsglobal.h" +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <time.h> +#include <fcntl.h> +#if HTS_WIN +#else +#include <arpa/inet.h> +#endif +#ifndef _WIN32 +#include <signal.h> +#endif +/* END specific definitions */ + +/* String */ +#include "proxystrings.h" + +/* Network base */ +#include "htsbasenet.h" + +/* définitions globales */ +#include "htsglobal.h" + +/* htslib */ +/*#include "htslib.h"*/ + +/* HTTrack Website Copier Library */ +#include "httrack-library.h" + +/* htsweb */ +#include "htsinthash.h" + +/* ProxyTrack */ +#include "proxytrack.h" + +/* Store manager */ +#include "../minizip/mztools.h" +#include "store.h" + +/* threads */ +#ifdef _WIN32 +#include <process.h> /* _beginthread, _endthread */ +#else +#include <pthread.h> +#endif + +/* External references */ +// htsErrorCallback htsCallbackErr = NULL; +int htsMemoryFastXfr = 1; /* fast xfr by default */ +void abortLog__fnc(char* msg, char* file, int line); +void abortLog__fnc(char* msg, char* file, int line) { + FILE* fp = fopen("CRASH.TXT", "wb"); + if (!fp) fp = fopen("/tmp/CRASH.TXT", "wb"); + if (!fp) fp = fopen("C:\\CRASH.TXT", "wb"); + if (!fp) fp = fopen("CRASH.TXT", "wb"); + if (fp) { + fprintf(fp, "HTTrack " HTTRACK_VERSIONID " closed at '%s', line %d\r\n", file, line); + fprintf(fp, "Reason:\r\n%s\r\n", msg); + fflush(fp); + fclose(fp); + } +} +// HTSEXT_API t_abortLog abortLog__ = abortLog__fnc; /* avoid VC++ inlining */ +#define webhttrack_lock(A) do{}while(0) + +/* Static definitions */ + +static int linputsoc(T_SOC soc, char* s, int max) { + int c; + int j=0; + do { + unsigned char ch; + if (recv(soc, &ch, 1, 0) == 1) { + c = ch; + } else { + c = EOF; + } + if (c!=EOF) { + switch(c) { + case 13: break; // sauter CR + case 10: c=-1; break; + case 9: case 12: break; // sauter ces caractères + default: s[j++]=(char) c; break; + } + } + } while((c!=-1) && (c!=EOF) && (j<(max-1))); + s[j]='\0'; + return j; +} + +static int check_readinput_t(T_SOC soc, int timeout) { + if (soc != INVALID_SOCKET) { + fd_set fds; // poll structures + struct timeval tv; // structure for select + FD_ZERO(&fds); + FD_SET(soc,&fds); + tv.tv_sec=timeout; + tv.tv_usec=0; + select((int)(soc + 1),&fds,NULL,NULL,&tv); + if (FD_ISSET(soc,&fds)) + return 1; + else + return 0; + } else + return 0; +} + +static int linputsoc_t(T_SOC soc, char* s, int max, int timeout) { + if (check_readinput_t(soc, timeout)) { + return linputsoc(soc, s, max); + } + return -1; +} + +static void unescapeini(char* s, String* tempo) { + int i; + char lastc=0; + for (i=0;i<(int) strlen(s);i++) { + if (s[i]=='%' && s[i+1]=='%') { + i++; + StringAddchar(*tempo, lastc = '%'); + } else if (s[i]=='%') { + char hc; + i++; + hc = (char) ehex(s+i); + if (!is_retorsep(hc) || !is_retorsep(lastc)) { + StringAddchar(*tempo, lastc = (char) hc); + } + i++; // sauter 2 caractères finalement + } + else + StringAddchar(*tempo, lastc = s[i]); + } +} + +static int gethost(const char* hostname, SOCaddr *server, size_t server_size) { + if (hostname != NULL && *hostname != '\0') { +#if HTS_INET6==0 + /* + ipV4 resolver + */ + t_hostent* hp=gethostbyname(hostname); + if (hp!=NULL) { + if ( (hp->h_length) && ( ((unsigned int) hp->h_length) <= buffer->addr_maxlen) ) { + SOCaddr_copyaddr(server, server_size, hp->h_addr_list[0], hp->h_length); + return 1; + } + } +#else + /* + ipV6 resolver + */ + struct addrinfo* res = NULL; + struct addrinfo hints; + memset(&hints, 0, sizeof(hints)); +#if 0 + if (IPV6_resolver == 1) // V4 only (for bogus V6 entries) + hints.ai_family = PF_INET; + else if (IPV6_resolver == 2) // V6 only (for testing V6 only) + hints.ai_family = PF_INET6; + else +#endif + hints.ai_family = PF_UNSPEC; + hints.ai_socktype = SOCK_STREAM; + hints.ai_protocol = IPPROTO_TCP; + if (getaddrinfo(hostname, NULL, &hints, &res) == 0) { + if (res) { + if ( (res->ai_addr) && (res->ai_addrlen) ) { + SOCaddr_copyaddr(*server, server_size, res->ai_addr, res->ai_addrlen); + freeaddrinfo(res); + return 1; + } + } + } + if (res) { + freeaddrinfo(res); + } + +#endif + } + return 0; +} + +static String getip(SOCaddr *server, int serverLen) { + String s = STRING_EMPTY; +#if HTS_INET6==0 + unsigned int sizeMax = sizeof("999.999.999.999:65535"); +#else + unsigned int sizeMax = sizeof("ffff:ffff:ffff:ffff:ffff:ffff:ffff:65535"); +#endif + char * dotted = malloc(sizeMax + 1); + unsigned short port = ntohs(SOCaddr_sinport(*server)); + if (dotted == NULL) { + CRITICAL("memory exhausted"); + return s; + } + SOCaddr_inetntoa(dotted, sizeMax, *server, serverLen); + sprintf(dotted + strlen(dotted), ":%d", port); + StringAttach(&s, &dotted); + return s; +} + + +static T_SOC smallserver_init(const char* adr, int port, int family) { + SOCaddr server; + size_t server_size = sizeof(server); + + memset(&server, 0, sizeof(server)); + SOCaddr_initany(server, server_size); + if (gethost(adr, &server, server_size)) { // host name + T_SOC soc = INVALID_SOCKET; + if ( (soc = socket(SOCaddr_sinfamily(server), family, 0)) != INVALID_SOCKET) { + SOCaddr_initport(server, port); + if ( bind(soc,(struct sockaddr*) &server, (int)server_size) == 0 ) { + if (family != SOCK_STREAM + || listen(soc, 10) >=0 ) { + return soc; + } else { +#ifdef _WIN32 + closesocket(soc); +#else + close(soc); +#endif + soc=INVALID_SOCKET; + } + } else { +#ifdef _WIN32 + closesocket(soc); +#else + close(soc); +#endif + soc=INVALID_SOCKET; + } + } + } + return INVALID_SOCKET; +} + +static int proxytrack_start(PT_Indexes indexes, T_SOC soc, T_SOC socICP); +int proxytrack_main(char* proxyAddr, int proxyPort, + char* icpAddr, int icpPort, + PT_Indexes index) { + int returncode = 0; + T_SOC soc = smallserver_init(proxyAddr, proxyPort, SOCK_STREAM); + T_SOC socICP = smallserver_init(proxyAddr, icpPort, SOCK_DGRAM); + if (soc != INVALID_SOCKET + && socICP != INVALID_SOCKET) + { + char url[HTS_URLMAXSIZE*2]; + char method[32]; + char data[32768]; + url[0]=method[0]=data[0]='\0'; + // + printf("HTTP Proxy installed on %s:%d/\n", proxyAddr, proxyPort); + printf("ICP Proxy installed on %s:%d/\n", icpAddr, icpPort); +#ifndef _WIN32 + { + pid_t pid = getpid(); + printf("PID=%d\n", (int)pid); + } +#endif + fflush(stdout); + fflush(stderr); + // + if (!proxytrack_start(index, soc, socICP)) { + fprintf(stderr, "Unable to create the server: %s\n", strerror(errno)); +#ifdef _WIN32 + closesocket(soc); +#else + close(soc); +#endif + printf("Done\n"); + returncode = 1; + } else { + returncode = 0; + } + } else { + fprintf(stderr, "Unable to initialize a temporary server : %s\n", strerror(errno)); + returncode = 1; + } + printf("EXITED\n"); + fflush(stdout); + fflush(stderr); + return returncode; +} + +static const char* GetHttpMessage(int statuscode) { + // Erreurs HTTP, selon RFC + switch( statuscode) { + case 100: return "Continue"; break; + case 101: return "Switching Protocols"; break; + case 200: return "OK"; break; + case 201: return "Created"; break; + case 202: return "Accepted"; break; + case 203: return "Non-Authoritative Information"; break; + case 204: return "No Content"; break; + case 205: return "Reset Content"; break; + case 206: return "Partial Content"; break; + case 207: return "Multi-Status"; break; + case 300: return "Multiple Choices"; break; + case 301: return "Moved Permanently"; break; + case 302: return "Moved Temporarily"; break; + case 303: return "See Other"; break; + case 304: return "Not Modified"; break; + case 305: return "Use Proxy"; break; + case 306: return "Undefined 306 error"; break; + case 307: return "Temporary Redirect"; break; + case 400: return "Bad Request"; break; + case 401: return "Unauthorized"; break; + case 402: return "Payment Required"; break; + case 403: return "Forbidden"; break; + case 404: return "Not Found"; break; + case 405: return "Method Not Allowed"; break; + case 406: return "Not Acceptable"; break; + case 407: return "Proxy Authentication Required"; break; + case 408: return "Request Time-out"; break; + case 409: return "Conflict"; break; + case 410: return "Gone"; break; + case 411: return "Length Required"; break; + case 412: return "Precondition Failed"; break; + case 413: return "Request Entity Too Large"; break; + case 414: return "Request-URI Too Large"; break; + case 415: return "Unsupported Media Type"; break; + case 416: return "Requested Range Not Satisfiable"; break; + case 417: return "Expectation Failed"; break; + case 500: return "Internal Server Error"; break; + case 501: return "Not Implemented"; break; + case 502: return "Bad Gateway"; break; + case 503: return "Service Unavailable"; break; + case 504: return "Gateway Time-out"; break; + case 505: return "HTTP Version Not Supported"; break; + default: return "Unknown HTTP Error"; break; + } +} + +#ifndef NO_WEBDAV +static void proxytrack_add_DAV_Item(String *item, String *buff, + const char* filename, + unsigned long int size, + time_t timestamp, + const char* mime, + int isDir, + int isRoot, + int isDefault) +{ + struct tm * timetm; + if (timestamp == (time_t) 0 || timestamp == (time_t) -1) { + timestamp = time(NULL); + } + if ((timetm = gmtime(×tamp)) != NULL) { + char tms[256 + 1]; + const char * name; + strftime(tms, 256, "%a, %d %b %Y %H:%M:%S GMT", timetm); /* Sun, 18 Sep 2005 11:45:45 GMT */ + + if (mime == NULL || *mime == 0) + mime = "application/octet-stream"; + + StringLength(*buff) = 0; + escapexml(filename, buff); + + name = strrchr(StringBuff(*buff), '/'); + if (name != NULL) + name++; + if (name == NULL || *name == 0) { + if (strcmp(mime, "text/html") == 0) + name = "Default Document for the Folder.html"; + else + name = "Default Document for the Folder"; + } + + StringRoom(*item, 1024); + sprintf(StringBuff(*item), + "<response xmlns=\"DAV:\">\r\n" + "<href>/webdav%s%s</href>\r\n" + "<propstat>\r\n" + "<prop>\r\n" + "<displayname>%s</displayname>\r\n" + "<iscollection>%d</iscollection>\r\n" + "<haschildren>%d</haschildren>\r\n" + "<isfolder>%d</isfolder>\r\n" + "<resourcetype>%s</resourcetype>\r\n" + "<creationdate>%d-%02d-%02dT%02d:%02d:%02dZ</creationdate>\r\n" + "<getlastmodified>%s</getlastmodified>\r\n" + "<supportedlock></supportedlock>\r\n" + "<lockdiscovery/>\r\n" + "<getcontenttype>%s</getcontenttype>\r\n" + "<getcontentlength>%d</getcontentlength>\r\n" + "<isroot>%d</isroot>\r\n" + "</prop>\r\n" + "<status>HTTP/1.1 200 OK</status>\r\n" + "</propstat>\r\n" + "</response>\r\n", + /* */ + ( StringBuff(*buff)[0] == '/' ) ? "" : "/", StringBuff(*buff), + name, + isDir ? 1 : 0, + isDir ? 1 : 0, + isDir ? 1 : 0, + isDir ? "<collection/>" : "", + timetm->tm_year + 1900, timetm->tm_mon + 1, timetm->tm_mday, timetm->tm_hour, timetm->tm_min, timetm->tm_sec, + tms, + isDir ? "httpd/unix-directory" : mime, + (int)size, + isRoot ? 1 : 0 + ); + StringLength(*item) = (int) strlen(StringBuff(*item)); + } +} + +/* Convert a RFC822 time to time_t */ +time_t get_time_rfc822(const char* s) { + struct tm result; + /* */ + char months[]="jan feb mar apr may jun jul aug sep oct nov dec"; + char str[256]; + char* a; + int i; + /* */ + int result_mm=-1; + int result_dd=-1; + int result_n1=-1; + int result_n2=-1; + int result_n3=-1; + int result_n4=-1; + /* */ + + if ((int) strlen(s) > 200) + return (time_t)0; + for(i = 0 ; s[i] != 0 ; i++) { + if (s[i] >= 'A' && s[i] <= 'Z') + str[i] = s[i] + ('a' - 'A'); + else + str[i] = s[i]; + } + str[i] = 0; + /* éliminer :,- */ + while( (a=strchr(str,'-')) ) *a=' '; + while( (a=strchr(str,':')) ) *a=' '; + while( (a=strchr(str,',')) ) *a=' '; + /* tokeniser */ + a=str; + while(*a) { + char *first,*last; + char tok[256]; + /* découper mot */ + while(*a==' ') a++; /* sauter espaces */ + first=a; + while((*a) && (*a!=' ')) a++; + last=a; + tok[0]='\0'; + if (first!=last) { + char* pos; + strncat(tok,first,(int) (last - first)); + /* analyser */ + if ( (pos=strstr(months,tok)) ) { /* month always in letters */ + result_mm=((int) (pos - months))/4; + } else { + int number; + if (sscanf(tok,"%d",&number) == 1) { /* number token */ + if (result_dd<0) /* day always first number */ + result_dd=number; + else if (result_n1<0) + result_n1=number; + else if (result_n2<0) + result_n2=number; + else if (result_n3<0) + result_n3=number; + else if (result_n4<0) + result_n4=number; + } /* sinon, bruit de fond(+1GMT for exampel) */ + } + } + } + if ((result_n1>=0) && (result_mm>=0) && (result_dd>=0) && (result_n2>=0) && (result_n3>=0) && (result_n4>=0)) { + if (result_n4>=1000) { /* Sun Nov 6 08:49:37 1994 */ + result.tm_year=result_n4-1900; + result.tm_hour=result_n1; + result.tm_min=result_n2; + result.tm_sec=max(result_n3,0); + } else { /* Sun, 06 Nov 1994 08:49:37 GMT or Sunday, 06-Nov-94 08:49:37 GMT */ + result.tm_hour=result_n2; + result.tm_min=result_n3; + result.tm_sec=max(result_n4,0); + if (result_n1<=50) /* 00 means 2000 */ + result.tm_year=result_n1+100; + else if (result_n1<1000) /* 99 means 1999 */ + result.tm_year=result_n1; + else /* 2000 */ + result.tm_year=result_n1-1900; + } + result.tm_isdst=0; /* assume GMT */ + result.tm_yday=-1; /* don't know */ + result.tm_wday=-1; /* don't know */ + result.tm_mon=result_mm; + result.tm_mday=result_dd; + return mktime(&result); + } + return (time_t) 0; +} + +static PT_Element proxytrack_process_DAV_Request(PT_Indexes indexes, const char * urlFull, int depth) { + const char * file = jump_protocol_and_auth(urlFull); + if ( (file = strchr(file, '/')) == NULL) + return NULL; + + if (strncmp(file, "/webdav", 7) != 0) { + PT_Element elt = PT_ElementNew(); + elt->statuscode = 405; + strcpy(elt->msg, "Method Not Allowed"); + return elt; + } + + /* Skip /webdav */ + file += 7; + + /* */ + { + PT_Element elt = PT_ElementNew(); + int i, isDir; + String url = STRING_EMPTY; + String response = STRING_EMPTY; + String item = STRING_EMPTY; + String itemUrl = STRING_EMPTY; + String buff = STRING_EMPTY; + StringClear(response); + StringClear(item); + StringClear(itemUrl); + StringClear(buff); + + /* Canonize URL */ + StringStrcpy(url, file + ((file[0] == '/') ? 1 : 0)); + if (StringLength(url) > 0) { + if (StringBuff(url)[StringLength(url) - 1] == '/') { + StringBuff(url)[StringLength(url) - 1] = '\0'; + StringLength(url)--; + } + } + + /* Form response */ + StringRoom(response, 1024); + sprintf(StringBuff(response), + "<?xml version=\"1.0\" encoding=\"utf-8\"?>\r\n" + "<multistatus xmlns=\"DAV:\">\r\n"); + StringLength(response) = (int) strlen(StringBuff(response)); + /* */ + + /* Root */ + StringLength(item) = 0; + proxytrack_add_DAV_Item(&item, &buff, + StringBuff(url), /*size*/0, /*timestamp*/(time_t) 0, /*mime*/NULL, /*isDir*/1, /*isRoot*/1, /*isDefault*/0); + StringMemcat(response, StringBuff(item), StringLength(item)); + + /* Childrens (Depth > 0) */ + if (depth > 0) { + time_t timestampRep = (time_t) -1; + const char * prefix = StringBuff(url); + unsigned int prefixLen = (unsigned int) strlen(prefix); + char ** list = PT_Enumerate(indexes, prefix, 0); + if (list != NULL) { + for(isDir = 1 ; isDir >= 0 ; isDir--) { + for(i = 0 ; list[i] != NULL ; i++) { + const char * thisUrl = list[i]; + const char * mimeType = "application/octet-stream"; + unsigned int thisUrlLen = (unsigned int) strlen(thisUrl); + int thisIsDir = (thisUrl[thisUrlLen - 1] == '/') ? 1 : 0; + + /* Item URL */ + StringRoom(itemUrl, thisUrlLen + prefixLen + sizeof("/webdav/") + 1); + StringClear(itemUrl); + sprintf(StringBuff(itemUrl), "/%s/%s", prefix, thisUrl); + if (!thisIsDir) + StringLength(itemUrl) = (int) strlen(StringBuff(itemUrl)); + else + StringLength(itemUrl) = (int) strlen(StringBuff(itemUrl)) - 1; + StringBuff(itemUrl)[StringLength(itemUrl)] = '\0'; + + if (thisIsDir == isDir) { + unsigned long size = 0; + time_t timestamp = (time_t) 0; + PT_Element file = NULL; + + /* Item stats */ + if (!isDir) { + file = PT_ReadIndex(indexes, StringBuff(itemUrl) + 1, FETCH_HEADERS); + if (file != NULL && file->statuscode == 200 ) { + size = file->size; + if (file->lastmodified) { + timestamp = get_time_rfc822(file->lastmodified); + } + if (timestamp == (time_t) 0) { + if (timestampRep == (time_t) -1) { + timestampRep = 0; + if (file->indexId != -1) { + timestampRep = PT_Index_Timestamp(PT_GetIndex(indexes, file->indexId)); + } + } + timestamp = timestampRep; + } + if (file->contenttype) { + mimeType = file->contenttype; + } + } + } + + /* Add item */ + StringLength(item) = 0; + proxytrack_add_DAV_Item(&item, &buff, + StringBuff(itemUrl), size, timestamp, mimeType, isDir, /*isRoot*/0, /*isDefault*/(thisUrlLen == 0)); + StringMemcat(response, StringBuff(item), StringLength(item)); + + /* Wipe element */ + if (file != NULL) + PT_Element_Delete(&file); + } + } + } + PT_Enumerate_Delete(&list); + } /* items != NULL */ + } /* Depth > 0 */ + + /* End of responses */ + StringStrcat(response, + "</multistatus>\r\n" + ); + + StringFree(item); + StringFree(itemUrl); + StringFree(url); + StringFree(buff); + + elt->size = StringLength(response); + elt->adr = StringAcquire(&response); + elt->statuscode = 207; /* Multi-Status */ + strcpy(elt->charset, "utf-8"); + strcpy(elt->contenttype, "text/xml"); + strcpy(elt->msg, "Multi-Status"); + StringFree(response); + + fprintf(stderr, "RESPONSE:\n%s\n", elt->adr); + + return elt; + } + return NULL; +} +#endif + +static PT_Element proxytrack_process_HTTP_List(PT_Indexes indexes, const char * url) { + char ** list = PT_Enumerate(indexes, url, 0); + if (list != NULL) { + PT_Element elt = PT_ElementNew(); + int i, isDir; + String html = STRING_EMPTY; + StringClear(html); + StringStrcat(html, + "<html>" + PROXYTRACK_COMMENT_HEADER + DISABLE_IE_FRIENDLY_HTTP_ERROR_MESSAGES + "<head>\r\n" + "<title>ProxyTrack " PROXYTRACK_VERSION " Catalog</title>" + "</head>\r\n" + "<body>\r\n" + "<h3>Directory index:</h3><br />" + "<br />" + "<hr>" + "<tt>[DIR] <a href=\"..\">..</a></tt><br />" + ); + for(isDir = 1 ; isDir >= 0 ; isDir--) { + for(i = 0 ; list[i] != NULL ; i++) { + char * thisUrl = list[i]; + unsigned int thisUrlLen = (unsigned int) strlen(thisUrl); + int thisIsDir = (thisUrl[thisUrlLen - 1] == '/') ? 1 : 0; + if (thisIsDir == isDir) { + if (isDir) + StringStrcat(html, "<tt>[DIR] "); + else + StringStrcat(html, "<tt> "); + StringStrcat(html, "<a href=\""); + if (isDir) { + StringStrcat(html, "http://proxytrack/"); + } + StringStrcat(html, url); + StringStrcat(html, list[i]); + StringStrcat(html, "\">"); + StringStrcat(html, list[i]); + StringStrcat(html, "</a></tt><br />"); + } + } + } + StringStrcat(html, + "</body>" + "</html>"); + PT_Enumerate_Delete(&list); + elt->size = StringLength(html); + elt->adr = StringAcquire(&html); + elt->statuscode = 200; + strcpy(elt->charset, "iso-8859-1"); + strcpy(elt->contenttype, "text/html"); + strcpy(elt->msg, "OK"); + StringFree(html); + return elt; + } + return NULL; +} + +static void proxytrack_process_HTTP(PT_Indexes indexes, T_SOC soc_c) { + int timeout=30; + int retour=0; + int willexit=0; + int buffer_size = 32768; + char * buffer = (char*)malloc(buffer_size); + int line1Size = 1024; + char * line1 = (char*)malloc(line1Size); + int lineSize = 8192; + char * line = (char*)malloc(lineSize); + int length = 0; + int keepAlive = 1; + + String url = STRING_EMPTY; + String urlRedirect = STRING_EMPTY; + String headers = STRING_EMPTY; + String output = STRING_EMPTY; + String host = STRING_EMPTY; + String localhost = STRING_EMPTY; +#ifndef NO_WEBDAV + String davHeaders = STRING_EMPTY; + String davRequest = STRING_EMPTY; +#endif + + StringRoom(localhost, 256); + if (gethostname(StringBuff(localhost), StringCapacity(localhost) - 1) == 0) { + StringLength(localhost) = (int) strlen(StringBuff(localhost)); + } else { + StringStrcpy(localhost, "localhost"); + } + +#ifdef _DEBUG + Sleep(1000); +#endif + + if (buffer == NULL || line == NULL || line1 == NULL) { + CRITICAL("proxytrack_process_HTTP:memory exhausted"); +#if HTS_WIN + closesocket(soc_c); +#else + close(soc_c); +#endif + return ; + } + + do { + const char* msgError = NULL; + int msgCode = 0; + PT_Element element = NULL; + char* command; + char* proto; + char* surl; + int directHit = 0; + int headRequest = 0; + int listRequest = 0; +#ifndef NO_WEBDAV + int davDepth = 0; +#endif + + /* Clear context */ + line[0] = line1[0] = '\0'; + buffer[0] = '\0'; + command = line1; + StringClear(url); + StringClear(urlRedirect); + StringClear(headers); + StringClear(output); + StringClear(host); +#ifndef NO_WEBDAV + StringClear(davHeaders); + StringClear(davRequest); +#endif + + /* line1: "GET http://www.example.com/ HTTP/1.0" */ + if (linputsoc_t(soc_c, line1, line1Size - 2, timeout) > 0 + && ( surl = strchr(line1, ' ') ) + && !(*surl = '\0') + && ++surl + && (proto = strchr(surl, ' ')) && !(*proto = '\0') && ++proto) + { + /* Flush headers */ + while(linputsoc_t(soc_c, line, lineSize - 2, timeout) > 0 + && line[0] != 0) + { + int p; + if ((p = strfield(line, "Content-length:"))!=0) { + if (sscanf(line+p, "%d", &length) != 1) { + msgCode = 500; + msgError = "Bad HTTP Content-Length Field"; + keepAlive = 0; + length = 0; + } + } else if (strcasecmp(line, "Connection: close") == 0) { + keepAlive = 0; + } else if (strcasecmp(line, "Connection: keep-alive") == 0) { + keepAlive = 1; + } else if ((p = strfield(line, "Host:"))) { + char* chost = line + p; + if (*chost == ' ') + chost++; + StringStrcpy(host, chost); + } +#ifndef NO_WEBDAV + else if ((p = strfield(line, "Depth: "))) { + char* depth = line + p; + if (sscanf(depth, "%d", &davDepth) != 1) { + davDepth = 0; + } + } +#endif + } + + /* Flush body */ +#ifndef NO_WEBDAV + if (length > 0) { + if (length < 32768) { + StringRoom(davRequest, length + 1); + if (recv(soc_c, StringBuff(davRequest), length, 0) == length) { + StringBuff(davRequest)[length] = 0; + } else { + msgCode = 500; + msgError = "Posted Data Read Error"; + keepAlive = 0; + } + } else { + msgCode = 500; + msgError = "Posted Data Too Large"; + keepAlive = 0; + } + } +#endif + + /* Switch protocol ID */ + if (strcasecmp(command, "post") == 0) { +#ifndef NO_WEBDAV + msgCode = 404; +#else + msgCode = 501; + keepAlive = 0; +#endif + msgError = "Proxy Error (POST Request Forbidden)"; + } + else if (strcasecmp(command, "get") == 0) { + headRequest = 0; + } + else if (strcasecmp(command, "head") == 0) { + headRequest = 1; + } +#ifndef NO_WEBDAV + else if (strcasecmp(command, "options") == 0) { + const char * options = "GET, HEAD, OPTIONS, POST, PROPFIND, TRACE" + ", MKCOL, DELETE, PUT"; /* Not supported */ + msgCode = 200; + StringRoom(headers, 8192); + sprintf(StringBuff(headers), + "HTTP/1.1 %d %s\r\n" + "DAV: 1, 2\r\n" + "MS-Author-Via: DAV\r\n" + "Cache-Control: private\r\n" + "Allow: %s\r\n", + msgCode, GetHttpMessage(msgCode), options); + StringLength(headers) = (int) strlen(StringBuff(headers)); + } + else if (strcasecmp(command, "propfind") == 0) { + if (davDepth > 1) { + msgCode = 403; + msgError = "DAV Depth Limit Forbidden"; + } else { + fprintf(stderr, "DEBUG: DAV-DATA=<%s>\n", StringBuff(davRequest)); + listRequest = 2; /* propfind */ + } + } + else if (strcasecmp(command, "mkcol") == 0 + || strcasecmp(command, "delete") == 0 + || strcasecmp(command, "put") == 0 + || strcasecmp(command, "proppatch") == 0 + || strcasecmp(command, "lock") == 0 + || strcasecmp(command, "unlock") == 0 + || strcasecmp(command, "copy") == 0 + || strcasecmp(command, "trace") == 0) + { + msgCode = 403; + msgError = "Method Forbidden"; + } +#endif + else { + msgCode = 501; + msgError = "Proxy Error (Unsupported or Unknown HTTP Command Request)"; + keepAlive = 0; + } + if (strcasecmp(proto, "http/1.1") == 0) { + keepAlive = 1; + } else if (strcasecmp(proto, "http/1.0") == 0) { + keepAlive = 0; + } else { + msgCode = 505; + msgError = "Proxy Error (Unknown HTTP Version)"; + keepAlive = 0; + } + + /* Post-process request */ + if (link_has_authority(surl)) { + const unsigned int prefixLen = sizeof("http://proxytrack/") - 1; + if (strncasecmp(surl, "http://proxytrack/", prefixLen) == 0) { + directHit = 1; /* Another direct hit hack */ + } + StringStrcpy(url, surl); + } else { + if (StringLength(host) > 0) { + /* Direct hit */ + if ( +#ifndef NO_WEBDAV + listRequest != 2 + && +#endif + strncasecmp(StringBuff(host), StringBuff(localhost), StringLength(localhost)) == 0 + && + (StringBuff(host)[StringLength(localhost)] == '\0' + || StringBuff(host)[StringLength(localhost)] == ':') + && surl[0] == '/' + ) + { + const char * toHit = surl + 1; + if (strncmp(toHit, "webdav/", 7) == 0) { + toHit += 7; + } + /* Direct hit */ + directHit = 1; + StringStrcpy(url, ""); + if (!link_has_authority(toHit)) + StringStrcat(url, "http://"); + StringStrcat(url, toHit); + } else { + /* Transparent proxy */ + StringStrcpy(url, "http://"); + StringStrcat(url, StringBuff(host)); + StringStrcat(url, surl); + } + } else { + msgCode = 500; + msgError = "Transparent Proxy Error ('Host' HTTP Request Header Field Missing)"; + keepAlive = 0; + } + } + + /* Response */ + if (msgCode == 0) { + if (listRequest == 1) { + element = proxytrack_process_HTTP_List(indexes, StringBuff(url)); + } +#ifndef NO_WEBDAV + else if (listRequest == 2) { + if ((element = proxytrack_process_DAV_Request(indexes, StringBuff(url), davDepth)) != NULL) { + msgCode = element->statuscode; + StringRoom(davHeaders, 1024); + sprintf(StringBuff(davHeaders), + "DAV: 1, 2\r\n" + "MS-Author-Via: DAV\r\n" + "Cache-Control: private\r\n"); + StringLength(davHeaders) = (int) strlen(StringBuff(davHeaders)); + } + } +#endif + else { + element = PT_ReadIndex(indexes, StringBuff(url), FETCH_BODY); + } + if (element == NULL +#ifndef NO_WEBDAV + && listRequest == 2 +#endif + && StringLength(url) > 0 + && StringBuff(url)[StringLength(url) - 1] == '/' + ) + { + element = PT_Index_HTML_BuildRootInfo(indexes); + if (element != NULL) { + element->statuscode = 404; /* HTML page, but in error */ + } + } + if (element != NULL) { + msgCode = element->statuscode; + StringRoom(headers, 8192); + sprintf(StringBuff(headers), + "HTTP/1.1 %d %s\r\n" +#ifndef NO_WEBDAV + "%s" +#endif + "Content-Type: %s%s%s%s\r\n" + "%s%s%s" + "%s%s%s" + "%s%s%s", + /* */ + msgCode, + element->msg, +#ifndef NO_WEBDAV + /* DAV */ + StringBuff(davHeaders), +#endif + /* Content-type: foo; [ charset=bar ] */ + element->contenttype, + ( ( element->charset[0]) ? "; charset=\"" : ""), + element->charset, + ( ( element->charset[0]) ? "\"" : ""), + /* location */ + ( ( element->location != NULL && element->location[0]) ? "Location: " : ""), + ( ( element->location != NULL && element->location[0]) ? element->location : ""), + ( ( element->location != NULL && element->location[0]) ? "\r\n" : ""), + /* last-modified */ + ( ( element->lastmodified[0]) ? "Last-Modified: " : ""), + ( ( element->lastmodified[0]) ? element->lastmodified : ""), + ( ( element->lastmodified[0]) ? "\r\n" : ""), + /* etag */ + ( ( element->etag[0]) ? "ETag: " : ""), + ( ( element->etag[0]) ? element->etag : ""), + ( ( element->etag[0]) ? "\r\n" : "") + ); + StringLength(headers) = (int) strlen(StringBuff(headers)); + } else { + /* No query string, no ending / : check the the <url>/ page */ + if (StringLength(url) > 0 && StringBuff(url)[StringLength(url) - 1] != '/' && strchr(StringBuff(url), '?') == NULL) { + StringStrcpy(urlRedirect, StringBuff(url)); + StringStrcat(urlRedirect, "/"); + if (PT_LookupIndex(indexes, StringBuff(urlRedirect))) { + msgCode = 301; /* Moved Permanently */ + StringRoom(headers, 8192); + sprintf(StringBuff(headers), + "HTTP/1.1 %d %s\r\n" + "Content-Type: text/html\r\n" + "Location: %s\r\n", + /* */ + msgCode, + GetHttpMessage(msgCode), + StringBuff(urlRedirect) + ); + StringLength(headers) = (int) strlen(StringBuff(headers)); + /* */ + StringRoom(output, 1024 + sizeof(PROXYTRACK_COMMENT_HEADER) + sizeof(DISABLE_IE_FRIENDLY_HTTP_ERROR_MESSAGES)); + sprintf(StringBuff(output), + "<html>" + PROXYTRACK_COMMENT_HEADER + DISABLE_IE_FRIENDLY_HTTP_ERROR_MESSAGES + "<head>" + "<title>ProxyTrack - Page has moved</title>" + "</head>\r\n" + "<body>" + "<h3>The correct location is:</h3><br />" + "<b><a href=\"%s\">%s</a></b><br />" + "<br />" + "<br />\r\n" + "<i>Generated by ProxyTrack " PROXYTRACK_VERSION ", (C) Xavier Roche and other contributors</i>" + "\r\n" + "</body>" + "</header>", + StringBuff(urlRedirect), + StringBuff(urlRedirect)); + StringLength(output) = (int) strlen(StringBuff(output)); + } + } + if (msgCode == 0) { + msgCode = 404; + msgError = "Not Found in this cache"; + } + } + } + } else { + msgCode = 500; + msgError = "Server Error"; + keepAlive = 0; + } + if (StringLength(headers) == 0) { + if (msgCode == 0) { + msgCode = 500; + msgError = "Internal Proxy Error"; + } else if (msgError == NULL) { + msgError = GetHttpMessage(msgCode); + } + StringRoom(headers, 256); + sprintf(StringBuff(headers), + "HTTP/1.1 %d %s\r\n" + "Content-type: text/html\r\n", + msgCode, + msgError); + StringLength(headers) = (int) strlen(StringBuff(headers)); + StringRoom(output, 1024 + sizeof(PROXYTRACK_COMMENT_HEADER) + sizeof(DISABLE_IE_FRIENDLY_HTTP_ERROR_MESSAGES)); + sprintf(StringBuff(output), + "<html>" + PROXYTRACK_COMMENT_HEADER + DISABLE_IE_FRIENDLY_HTTP_ERROR_MESSAGES + "<head>" + "<title>ProxyTrack - HTTP Proxy Error %d</title>" + "</head>\r\n" + "<body>" + "<h3>A proxy error has occured while processing the request.</h3><br />" + "<b>Error HTTP %d: <i>%s</i></b><br />" + "<br />" + "<br />\r\n" + "<i>Generated by ProxyTrack " PROXYTRACK_VERSION ", (C) Xavier Roche and other contributors</i>" + "\r\n" + "</body>" + "</html>", + msgCode, + msgCode, + msgError); + StringLength(output) = (int) strlen(StringBuff(output)); + } + { + char tmp[20 + 1]; /* 2^64 = 18446744073709551616 */ + unsigned int dataSize = 0; + if (!headRequest) { + dataSize = StringLength(output); + if (dataSize == 0 && element != NULL) { + dataSize = element->size; + } + } + sprintf(tmp, "%d", (int) dataSize); + StringStrcat(headers, "Content-length: "); + StringStrcat(headers, tmp); + StringStrcat(headers, "\r\n"); + } + if (keepAlive) { + StringStrcat(headers, + "Connection: Keep-Alive\r\n" + "Proxy-Connection: Keep-Alive\r\n"); + } else { + StringStrcat(headers, + "Connection: Close\r\n" + "Proxy-Connection: Close\r\n"); + } + if (msgCode != 500) + StringStrcat(headers, "X-Cache: HIT from "); + else + StringStrcat(headers, "X-Cache: MISS from "); + StringStrcat(headers, StringBuff(localhost)); + StringStrcat(headers, "\r\n"); + + /* Logging */ + { + const char * contentType = "text/html"; + unsigned long int size = StringLength(output) ? StringLength(output) : ( element ? element->size : 0 ); + /* */ + String ip = STRING_EMPTY; + SOCaddr serverClient; + int lenServerClient = (int) sizeof(serverClient); + memset(&serverClient, 0, sizeof(serverClient)); + if (getsockname(soc_c, (struct sockaddr*) &serverClient, &lenServerClient) == 0) { + ip = getip(&serverClient, lenServerClient); + } else { + StringStrcpy(ip, "unknown"); + } + if (element != NULL && element->contenttype[0] != '\0') { + contentType = element->contenttype; + } + LOG("HTTP %s %d %d %s %s %s" _ StringBuff(ip) _ msgCode _ (int)size _ command _ StringBuff(url) _ contentType); + StringFree(ip); + } + + /* Send reply */ + StringStrcat(headers, "Server: ProxyTrack " PROXYTRACK_VERSION " (HTTrack " HTTRACK_VERSIONID ")\r\n"); + StringStrcat(headers, "\r\n"); /* Headers separator */ + if (send(soc_c, StringBuff(headers), StringLength(headers), 0) != StringLength(headers) + || ( !headRequest && StringLength(output) > 0 && send(soc_c, StringBuff(output), StringLength(output), 0) != StringLength(output)) + || ( !headRequest && StringLength(output) == 0 && element != NULL && element->adr != NULL && send(soc_c, element->adr, element->size, 0) != element->size) + ) + { + keepAlive = 0; /* Error, abort connection */ + } + PT_Element_Delete(&element); + + /* Shutdown (FIN) and wait until confirmed */ + if (!keepAlive) { + char c; +#ifdef _WIN32 + shutdown(soc_c, SD_SEND); +#else + shutdown(soc_c, 1); +#endif + while(recv(soc_c, ((char*)&c), 1, 0) > 0); + } + } while(keepAlive); + +#if HTS_WIN + closesocket(soc_c); +#else + close(soc_c); +#endif + + StringFree(url); + StringFree(urlRedirect); + StringFree(headers); + StringFree(output); + StringFree(host); + + if (buffer) + free(buffer); +} + +#ifdef _WIN32 +#define PTHREAD_RETURN +#define PTHREAD_TYPE void +#define PTHREAD_TYPE_FNC __cdecl +#else +#define PTHREAD_RETURN NULL +#define PTHREAD_TYPE void* +#define PTHREAD_TYPE_FNC +#endif + +/* Generic threaded function start */ +static int startThread(PTHREAD_TYPE (PTHREAD_TYPE_FNC * funct)(void* ), + void* param) +{ + if (param != NULL) { +#ifdef _WIN32 + if (_beginthread(funct, 0, param) == -1) { + free(param); + return 0; + } + return 1; +#else + pthread_t handle = 0; + int retcode; + retcode = pthread_create(&handle, NULL, funct, param); + if (retcode != 0) { /* error */ + free(param); + return 0; + } else { + /* detach the thread from the main process so that is can be independent */ + pthread_detach(handle); + return 1; + } +#endif + } else { + return 0; + } +} + +/* Generic socket/index structure */ +typedef struct proxytrack_process_th_p { + T_SOC soc_c; + PT_Indexes indexes; + void (*process)(PT_Indexes indexes, T_SOC soc_c); +} proxytrack_process_th_p; + +/* Generic socket/index function stub */ +static PTHREAD_TYPE PTHREAD_TYPE_FNC proxytrack_process_th(void* param_) { + proxytrack_process_th_p *param = (proxytrack_process_th_p *) param_; + T_SOC soc_c = param->soc_c; + PT_Indexes indexes = param->indexes; + void (*process)(PT_Indexes indexes, T_SOC soc_c) = param->process; + free(param); + process(indexes, soc_c); + return PTHREAD_RETURN ; +} + +/* Process generic socket/index operation */ +static int proxytrack_process_generic(void (*process)(PT_Indexes indexes, T_SOC soc_c), + PT_Indexes indexes, T_SOC soc_c) +{ + proxytrack_process_th_p *param = calloc(sizeof(proxytrack_process_th_p), 1); + if (param != NULL) { + param->soc_c = soc_c; + param->indexes = indexes; + param->process = process; + return startThread(proxytrack_process_th, param); + } else { + CRITICAL("proxytrack_process_generic:Memory exhausted"); + return 0; + } + return 0; +} + +/* Process HTTP proxy requests */ +static int proxytrack_process_HTTP_threaded(PT_Indexes indexes, T_SOC soc) { + return proxytrack_process_generic(proxytrack_process_HTTP, indexes, soc); +} + +/* HTTP Server */ +static int proxytrack_start_HTTP(PT_Indexes indexes, T_SOC soc) { + while(soc != INVALID_SOCKET) { + T_SOC soc_c; + struct sockaddr clientAddr; + int clientAddrLen = sizeof(struct sockaddr); + memset(&clientAddr, 0, sizeof(clientAddr)); + if ( (soc_c = accept(soc, &clientAddr, &clientAddrLen)) != INVALID_SOCKET) { + if (!proxytrack_process_HTTP_threaded(indexes, soc_c)) { + CRITICAL("proxytrack_start_HTTP::Can not fork a thread"); + } + } + } + if (soc != INVALID_SOCKET) { +#ifdef _WIN32 + closesocket(soc); +#else + close(soc); +#endif + } + return 1; +} + +/* Network order is big endian */ +#define READ_NET16(buffer) ( ( ((unsigned char*)buffer)[0] << 8 ) + ((unsigned char*)buffer)[1] ) +#define READ_NET32(buffer) ( ( READ_NET16(buffer) << 16 ) + READ_NET16(((unsigned char*)buffer) + 2) ) +#define WRITE_NET8(buffer, value) do { \ + ((unsigned char*)buffer)[0] = (unsigned char)(value); \ +} while(0) +#define WRITE_NET16(buffer, value) do { \ + ((unsigned char*)buffer)[0] = (((unsigned short)(value)) >> 8) & 0xff; \ + ((unsigned char*)buffer)[1] = ((unsigned short)(value)) & 0xff; \ +} while(0) +#define WRITE_NET32(buffer, value) do { \ + WRITE_NET16(buffer, ( ((unsigned int)(value)) >> 16 ) & 0xffff); \ + WRITE_NET16(((unsigned char*)buffer) + 2, ( ((unsigned int)(value)) ) & 0xffff); \ +} while(0) + +static int ICP_reply(struct sockaddr * clientAddr, + int clientAddrLen, + T_SOC soc, + /* */ + unsigned char Opcode, + unsigned char Version, + unsigned short Message_Length, + unsigned int Request_Number, + unsigned int Options, + unsigned int Option_Data, + unsigned int Sender_Host_Address, + unsigned char *Message + ) +{ + int ret = 0; + unsigned long int BufferSize; + unsigned char * buffer; + if (Message_Length == 0 && Message != NULL) /* We have to get the message size */ + Message_Length = (unsigned int) strlen(Message) + 1; /* NULL terminated */ + BufferSize = 20 + Message_Length; + buffer = malloc(BufferSize); + if (buffer != NULL) { + WRITE_NET8(&buffer[0], Opcode); + WRITE_NET8(&buffer[1], Version); + WRITE_NET16(&buffer[2], Message_Length); + WRITE_NET32(&buffer[4], Request_Number); + WRITE_NET32(&buffer[8], Options); + WRITE_NET32(&buffer[12], Option_Data); + WRITE_NET32(&buffer[16], Sender_Host_Address); + if (Message != NULL && Message_Length > 0) { + memcpy(buffer + 20, Message, Message_Length); + } + if (sendto(soc, buffer, BufferSize, 0, clientAddr, clientAddrLen) == BufferSize) { + ret = 1; + } + free(buffer); + } + return ret; +} + +/* ICP Server */ +static int proxytrack_start_ICP(PT_Indexes indexes, T_SOC soc) { + /* "ICP messages MUST not exceed 16,384 octets in length." (RFC2186) */ + int bufferSize = 16384; + unsigned char * buffer = (unsigned char*) malloc(bufferSize + 1); + if (buffer == NULL) { + CRITICAL("proxytrack_start_ICP:memory exhausted"); +#ifdef _WIN32 + closesocket(soc); +#else + close(soc); +#endif + return -1; + } + while(soc != INVALID_SOCKET) { + struct sockaddr clientAddr; + int clientAddrLen = sizeof(struct sockaddr); + int n; + memset(&clientAddr, 0, sizeof(clientAddr)); + n = recvfrom(soc, (char*)buffer, bufferSize, 0, &clientAddr, &clientAddrLen); + if (n != -1) { + const char * LogRequest = "ERROR"; + const char * LogReply = "ERROR"; + unsigned char * UrlRequest = NULL; + if (n >= 20) { + enum { + ICP_OP_MIN = 0, + ICP_OP_INVALID = 0, + ICP_OP_QUERY = 1, + ICP_OP_HIT = 2, + ICP_OP_MISS = 3, + ICP_OP_ERR = 4, + ICP_OP_SECHO = 10, + ICP_OP_DECHO = 11, + ICP_OP_MISS_NOFETCH = 21, + ICP_OP_DENIED = 22, + ICP_OP_HIT_OBJ = 23, + ICP_OP_MAX = ICP_OP_HIT_OBJ + }; + unsigned char Opcode = buffer[0]; + unsigned char Version = buffer[1]; + unsigned short Message_Length = READ_NET16(&buffer[2]); + unsigned int Request_Number = READ_NET32(&buffer[4]); /* Session ID */ + unsigned int Options = READ_NET32(&buffer[8]); + unsigned int Option_Data = READ_NET32(&buffer[12]); /* ICP_FLAG_SRC_RTT */ + unsigned int Sender_Host_Address = READ_NET32(&buffer[16]); /* ignored */ + unsigned char* Payload = &buffer[20]; + buffer[bufferSize] = '\0'; /* Ensure payload is NULL terminated */ + if (Message_Length <= bufferSize - 20) { + if (Opcode >= ICP_OP_MIN && Opcode <= ICP_OP_MAX) { + if (Version == 2) { + switch(Opcode) { + case ICP_OP_QUERY: + { + unsigned int UrlRequestSize; + UrlRequest = &Payload[4]; + UrlRequestSize = (unsigned int)strlen((char*)UrlRequest); + LogRequest = "ICP_OP_QUERY"; + if (indexes == NULL) { + ICP_reply(&clientAddr, clientAddrLen, soc, ICP_OP_DENIED, Version, 0, Request_Number, 0, 0, 0, UrlRequest); + LogReply = "ICP_OP_DENIED"; + } else if (PT_LookupIndex(indexes, UrlRequest)) { + ICP_reply(&clientAddr, clientAddrLen, soc, ICP_OP_HIT, Version, 0, Request_Number, 0, 0, 0, UrlRequest); + LogReply = "ICP_OP_HIT"; + } else { + if (UrlRequestSize > 0 && UrlRequest[UrlRequestSize - 1] != '/' && strchr(UrlRequest, '?') == NULL) { + char * UrlRedirect = malloc(UrlRequestSize + 1 + 1); + if (UrlRedirect != NULL) { + sprintf(UrlRedirect, "%s/", UrlRequest); + if (PT_LookupIndex(indexes, UrlRedirect)) { /* We'll generate a redirect */ + ICP_reply(&clientAddr, clientAddrLen, soc, ICP_OP_HIT, Version, 0, Request_Number, 0, 0, 0, UrlRequest); + LogReply = "ICP_OP_HIT"; + free(UrlRedirect); + break; + } + free(UrlRedirect); + } + } + /* We won't retrive the cache MISS online, no way! */ + ICP_reply(&clientAddr, clientAddrLen, soc, ICP_OP_MISS_NOFETCH, Version, 0, Request_Number, 0, 0, 0, UrlRequest); + LogReply = "ICP_OP_MISS_NOFETCH"; + } + } + break; + case ICP_OP_SECHO: + { + UrlRequest = &Payload[4]; + LogRequest = "ICP_OP_QUERY"; + LogReply = "ICP_OP_QUERY"; + ICP_reply(&clientAddr, clientAddrLen, soc, ICP_OP_SECHO, Version, 0, Request_Number, 0, 0, 0, UrlRequest); + } + break; + default: + LogRequest = "NOTIMPLEMENTED"; + LogReply = "ICP_OP_ERR"; + ICP_reply(&clientAddr, clientAddrLen, soc, ICP_OP_ERR, Version, 0, Request_Number, 0, 0, 0, NULL); + break; + } + } else { + ICP_reply(&clientAddr, clientAddrLen, soc, ICP_OP_ERR, 2, 0, Request_Number, 0, 0, 0, NULL); + } + } /* Ignored (RFC2186) */ + } else { + ICP_reply(&clientAddr, clientAddrLen, soc, ICP_OP_ERR, Version, 0, Request_Number, 0, 0, 0, NULL); + } + } + + /* Logging */ + { + String ip = STRING_EMPTY; + SOCaddr serverClient; + int lenServerClient = (int) sizeof(serverClient); + SOCaddr_copyaddr(serverClient, lenServerClient, &clientAddr, clientAddrLen); + if (lenServerClient > 0) { + ip = getip(&serverClient, lenServerClient); + } else { + StringStrcpy(ip, "unknown"); + } + LOG("ICP %s %s/%s %s" _ StringBuff(ip) _ LogRequest _ LogReply _ (UrlRequest ? UrlRequest : "-") ); + StringFree(ip); + } + + } + } + if (soc != INVALID_SOCKET) { +#ifdef _WIN32 + closesocket(soc); +#else + close(soc); +#endif + } + free(buffer); + return 1; +} + +static int proxytrack_start(PT_Indexes indexes, T_SOC soc, T_SOC socICP) { + int ret = 1; + if (proxytrack_process_generic(proxytrack_start_ICP, indexes, socICP)) { + //if (!proxytrack_process_generic(proxytrack_start_HTTP, indexes, soc)) + if (!proxytrack_start_HTTP(indexes, soc)) { + ret = 0; + } + } else { + ret = 0; + } + return ret; +} + |