Patched to compile on Apache2.
1 // This is a C program that handles offloading of bandwidth from a web
2 // server. It's a sort of poor-man's Akamai. It doesn't need anything
3 // terribly complex (a webserver with cgi-bin support, a writable directory).
4 // It can run as a cgi-bin program, or as a quick-and-dirty standalone HTTP
8 // - You have a webserver with dynamic content, and static content that
9 // may change arbitrarily (i.e. - various users making changes to their
10 // homepages, etc). This server is under a lot of load, mostly from
11 // the static content, which tends to be big. There may be multiple virtual
12 // hosts on this machine. We call this the "base" server.
13 // - You have at least one other webserver that you can use to offload some
14 // of the bandwidth. We call this the "offload" server.
15 // - You set up an Apache module (mod_offload) on the first server.
16 // mod_offload inserts itself into the request chain, and decides if a
17 // given file is safe static content (real file, not a script/cgi, no
18 // password, etc). In those cases, it sends a 302 redirect, pointing the
19 // client to the offload server.
20 // - The offload server gets a request from the redirected client. It then
21 // sends an HTTP HEAD request for the file in question to the base server
22 // while the client waits. It decides if it has the right file based on
23 // the HEAD. If it does, it serves the cached file.
24 // - If the file is out of date, or doesn't exist on the offload server, it
25 // sends a regular HTTP request for it to the base server and
26 // begins caching it. While caching it, it also feeds it to the client
27 // that has been waiting.
28 // - If another request comes in while the file is being cached, it will
29 // stream what is already there from disk, and then continue to feed as
33 // !!! FIXME: issues to work out.
34 // - Need to have a way to clean out old files. If x.zip is on the base,
35 // gets cached, and then is deleted, it'll stay on the offload server
36 // forever. Getting a 404 from the HEAD request will clean it out, but
37 // the offload server needs to know to do that.
42 // You need a Unix-like system, like Linux, BSD, or Mac OS X. This won't
43 // work on Windows (try the PHP version there, you might have some luck).
45 // If you're building this as a standalone server, set the options you want,
46 // compile it and start it running.
48 // If you want to run as a cgi-bin program:
49 // You need Apache (or whatever) to push every web request to this program,
50 // presumably in a virtual host, if not the entire server.
52 // Assuming this program was at /www/cgi-bin/index.cgi, you would want to add
53 // this to Apache's config:
55 // AliasMatch ^.*$ "/www/cgi-bin/index.cgi"
57 // You might need a "AddHandler cgi-script .cgi" or some other magic.
59 // If you don't have control over the virtual host's config file, you can't
60 // use AliasMatch, but if you can put an .htaccess file in the root of the
61 // virtual host, you can get away with this:
63 // ErrorDocument 404 /cgi-bin/index.cgi
65 // This will make all missing files (everything) run the script, which will
66 // then cache and distribute the correct content, including overriding the
67 // 404 status code with the correct one. Be careful about files that DO exist
68 // in that vhost directory, though. They won't offload.
70 // In this case, Apache will report the correct status message to the client,
71 // but log all offloaded files as 404 Not Found. This can't be helped. Run
72 // the server as standalone on a different port and have Apache proxy to it,
73 // or don't use Apache at all.
75 // You can offload multiple base servers with one box: set up one virtual host
76 // on the offload server for each base server. This lets each base server
77 // have its own cache and configuration.
79 // Restart the server so the AliasMatch configuration tweak is picked up.
82 // This file is written by Ryan C. Gordon (icculus@icculus.org).
87 * Edit offload_server_config.h to fit your needs, or override #defines
88 * on the command line. I use a shell script that looks like this:
95 * -DGDEBUGDIR='"/home/icculus/offload2.icculus.org/logs"' \
96 * -DSHM_NAME='"mod-offload-offload2-icculus-org"' \
97 * -DGBASESERVER='"icculus.org"' \
98 * -DGBASESERVERIP='"67.106.77.212"' \
99 * -DGLISTENPORT=9090 \
100 * -DGLISTENDAEMONIZE=1 \
101 * -DGLISTENTRUSTFWD='"127.0.0.1", "66.33.209.154"' \
102 * -DGOFFLOADDIR='"/home/icculus/offload2.icculus.org/cache"' \
103 * -DGMAXDUPEDOWNLOADS=1 \
105 * -DGLOGFILE='"/home/icculus/offload2.icculus.org/logs/access.log"' \
106 * -g -O0 -Wall -o offload-daemon /home/icculus/mod_offload/nph-offload.c -lrt
117 #include <semaphore.h>
121 #include <sys/types.h>
122 #include <sys/stat.h>
123 #include <sys/socket.h>
124 #include <sys/mman.h>
126 #include <netinet/in.h>
127 #include <arpa/inet.h>
129 #define GVERSION "1.1.6"
130 #define GSERVERSTRING "nph-offload.c/" GVERSION
132 #include "offload_server_config.h"
134 #define OFFLOAD_NUMSTR2(x) #x
135 #define OFFLOAD_NUMSTR(x) OFFLOAD_NUMSTR2(x)
137 #define GBASESERVERPORTSTR OFFLOAD_NUMSTR(GBASESERVERPORT)
140 #define ISPRINTF(x,y) __attribute__((format (printf, x, y)))
142 #define ISPRINTF(x,y)
149 // some getaddrinfo() flags that may not exist...
153 #ifndef AI_ADDRCONFIG
154 #define AI_ADDRCONFIG 0
156 #ifndef AI_NUMERICSERV
157 #define AI_NUMERICSERV 0
160 #define AI_V4MAPPED 0
164 typedef uint8_t uint8;
165 typedef int16_t int16;
166 typedef uint16_t uint16;
167 typedef int32_t int32;
168 typedef uint32_t uint32;
169 typedef int64_t int64;
170 typedef uint64_t uint64;
172 extern char **environ;
174 static int GIsCacheProcess = 0;
175 static int GHttpStatus = 0;
176 static int64 GBytesSent = 0;
177 static const char *Guri = NULL;
178 static const char *GRemoteAddr = NULL;
179 static const char *GReferer = NULL;
180 static const char *GUserAgent = NULL;
181 static const char *GReqVersion = NULL;
182 static const char *GReqMethod = NULL;
183 static char *GFilePath = NULL;
184 static void *GSemaphore = NULL;
185 static int GSemaphoreOwned = 0;
186 static FILE *GDebugFilePointer = NULL;
187 static int GSocket = -1;
190 static char *GMetaDataPath = NULL;
194 static void failure_location(const char *, const char *, const char *);
195 static inline void failure(const char *httperr, const char *errmsg)
197 failure_location(httperr, errmsg, NULL);
201 #if ( ((GDEBUG) && (GDEBUGTOFILE)) == 0 )
202 #define getDebugFilePointer() (NULL)
204 static FILE *getDebugFilePointer(void)
206 if (GDebugFilePointer == NULL)
209 snprintf(buf, sizeof(buf), GDEBUGDIR "/debug-%d", (int) getpid());
210 GDebugFilePointer = fopen(buf, "a");
212 return GDebugFilePointer;
213 } // getDebugFilePointer
217 #if ((!GDEBUG) && defined(__GNUC__))
218 #define debugEcho(fmt, ...) do {} while (0)
220 static void debugEcho(const char *fmt, ...) ISPRINTF(1, 2);
221 static void debugEcho(const char *fmt, ...)
227 FILE *fp = getDebugFilePointer();
232 fputs("(cache process) ", fp);
236 vfprintf(fp, fmt, ap);
246 static void *createSemaphore(const int initialVal)
249 const int value = initialVal ? 0 : 1;
252 retval = sem_open("SEM-" SHM_NAME, O_CREAT | O_EXCL, 0600, value);
253 if ((retval == (void *) SEM_FAILED) && (errno == EEXIST))
256 debugEcho("(semaphore already exists, just opening existing one.)");
257 retval = sem_open("SEM-" SHM_NAME, 0);
260 if (retval == (void *) SEM_FAILED)
267 static void getSemaphore(void)
269 debugEcho("grabbing semaphore...(owned %d time(s).)", GSemaphoreOwned);
270 if (GSemaphoreOwned++ > 0)
273 if (GSemaphore != NULL)
275 if (sem_wait(GSemaphore) == -1)
276 failure("503 Service Unavailable", "Couldn't lock semaphore.");
280 debugEcho("(have to create semaphore...)");
281 GSemaphore = createSemaphore(0);
282 if (GSemaphore == NULL)
283 failure("503 Service Unavailable", "Couldn't allocate semaphore.");
288 static void putSemaphore(void)
290 if (GSemaphoreOwned == 0)
293 if (--GSemaphoreOwned == 0)
295 if (GSemaphore != NULL)
297 if (sem_post(GSemaphore) == -1)
298 failure("503 Service Unavailable", "Couldn't unlock semaphore.");
301 debugEcho("released semaphore...(now owned %d time(s).)", GSemaphoreOwned);
305 static inline int process_dead(const pid_t pid)
307 return ( (pid <= 0) || ((kill(pid, 0) == -1) && (errno == ESRCH)) );
311 #if GMAXDUPEDOWNLOADS <= 0
312 #define setDownloadRecord()
313 #define removeDownloadRecord()
316 // we can track this many concurrent connections in a block of shared memory.
317 // If DownloadRecord is 24 bytes, then 512 records is 12 kilobytes (usually,
318 // three pages of memory). If you actually have more than this many concurrent
319 // connections then we'll just stop checking for dupes in things that didn't
320 // fit in the table...frankly, if your server is still standing with 512
321 // active HTTP downloads, you probably don't care about download accelerators
323 #define MAX_DOWNLOAD_RECORDS 512
331 static DownloadRecord *GAllDownloads = NULL;
332 static DownloadRecord *GMyDownload = NULL;
334 #define DUPE_FORBID_TEXT \
335 "403 Forbidden - " GSERVERSTRING "\n\n" \
336 "Your network address has too many connections for this specific file.\n" \
337 "Please disable any 'download accelerators' and try again.\n\n" \
346 static void Sha1_init(Sha1 *context);
347 static void Sha1_append(Sha1 *context, const uint8 *data, uint32 len);
348 static void Sha1_finish(Sha1 *context, uint8 digest[20]);
350 static void setDownloadRecord()
352 const pid_t mypid = getpid();
358 DownloadRecord *downloads = NULL;
359 const size_t maplen = sizeof (DownloadRecord) * MAX_DOWNLOAD_RECORDS;
360 if (GRemoteAddr == NULL)
363 GAllDownloads = GMyDownload = NULL;
367 fd = shm_open("/" SHM_NAME, (O_CREAT|O_EXCL|O_RDWR), (S_IREAD|S_IWRITE));
370 fd = shm_open("/" SHM_NAME, (O_CREAT|O_RDWR),(S_IREAD|S_IWRITE));
374 debugEcho("shm_open() failed: %s", strerror(errno));
379 ftruncate(fd, maplen);
381 void *ptr = mmap(0, maplen, (PROT_READ|PROT_WRITE), MAP_SHARED, fd, 0);
382 close(fd); // mapping remains.
383 if (ptr == MAP_FAILED)
386 debugEcho("mmap() failed: %s", strerror(errno));
390 GAllDownloads = downloads = (DownloadRecord *) ptr;
392 Sha1_init(&sha1data);
393 Sha1_append(&sha1data, (const uint8 *) GRemoteAddr, strlen(GRemoteAddr) + 1);
394 Sha1_append(&sha1data, (const uint8 *) Guri, strlen(Guri) + 1);
395 Sha1_finish(&sha1data, sha1);
397 for (i = 0; i < MAX_DOWNLOAD_RECORDS; i++, downloads++)
399 const pid_t pid = downloads->pid;
401 if (pid <= 0) // unused slot.
402 GMyDownload = downloads; // take slot.
404 else if (memcmp(downloads->sha1, sha1, sizeof (sha1)) == 0)
406 // make sure this isn't a killed process.
407 if ( (pid == mypid) || (process_dead(pid)) )
409 debugEcho("pid #%d died at some point.", (int) pid);
411 GMyDownload = downloads; // take slot.
415 debugEcho("pid #%d still alive, dupe slot.", (int) pid);
421 debugEcho("Saw %d dupes.", dupes);
423 if (dupes >= GMAXDUPEDOWNLOADS)
424 failure("403 Forbidden", DUPE_FORBID_TEXT); // will put semaphore.
425 else if (GMyDownload == NULL) // Have fun, downloader accelerator!
426 debugEcho("no free download slots! Can't add ourselves.");
429 debugEcho("Got download slot #%d", (int) (GMyDownload-GAllDownloads));
430 GMyDownload->pid = mypid;
431 memcpy(GMyDownload->sha1, sha1, sizeof (sha1));
435 } // setDownloadRecord
438 static void removeDownloadRecord()
444 if (GMyDownload != NULL)
445 GMyDownload->pid = 0;
447 munmap(GAllDownloads, sizeof (DownloadRecord) * MAX_DOWNLOAD_RECORDS);
449 GAllDownloads = GMyDownload = NULL;
450 } // removeDownloadRecord
453 // strftime()'s "%a" gives you locale-dependent strings...
454 static const char *GWeekday[] = {
455 "Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat",
458 // strftime()'s "%b" gives you locale-dependent strings...
459 static const char *GMonth[] = {
460 "Jan", "Feb", "Mar", "Apr", "May", "Jun",
461 "Jul", "Aug", "Sep", "Oct", "Nov", "Dec",
464 static void make_date_header(char *buf, const size_t buflen)
466 time_t now = time(NULL);
467 const struct tm *tm = gmtime(&now);
468 snprintf(buf, buflen, "Date: %s, %02d %s %d %02d:%02d:%02d GMT\r\n",
469 GWeekday[tm->tm_wday], tm->tm_mday, GMonth[tm->tm_mon],
470 tm->tm_year+1900, tm->tm_hour, tm->tm_min, tm->tm_sec);
471 } // make_date_header
475 static void printf_date_header(FILE *out)
480 make_date_header(buf, sizeof (buf));
481 fprintf(out, "%s", buf);
482 } // printf_date_header
486 static void terminate(void);
488 static void write_string(const int fd, const char *str)
490 size_t avail = strlen(str);
493 ssize_t rc = write(fd, str, avail);
494 if ((rc == -1) && (errno == EINTR))
499 debugEcho("write_string(): write() failed! (%s)\n", strerror(errno));
509 static void write_header(const char *key, const char *val)
511 write_string(GSocket, key);
512 write_string(GSocket, val);
513 write_string(GSocket, "\r\n");
517 static void write_date_header(void)
520 make_date_header(buf, sizeof (buf));
521 write_string(GSocket, buf);
522 } // write_date_header
525 static int64 atoi64(const char *str)
542 const char ch = str[i];
543 if ((ch < '0') || (ch > '9'))
550 const char ch = str[i];
551 retval += ((int64) (ch - '0')) * mult;
559 static void *xmalloc(const size_t len)
561 void *ptr = malloc(len);
563 failure("500 Internal Server Error", "Out of memory.");
567 static char *xstrdup(const char *str)
569 char *ptr = (char *) xmalloc(strlen(str) + 1);
575 static char *makeStr(const char *fmt, ...) ISPRINTF(1, 2);
576 static char *makeStr(const char *fmt, ...)
582 const int len = vsnprintf(&ch, 1, fmt, ap);
585 char *retval = (char *) xmalloc(len + 1);
587 vsnprintf(retval, len + 1, fmt, ap);
594 // a hashtable would be more sane, but really, we're talking about a handful
595 // of items, so this is probably the lower memory option, and it's fast
596 // enough for the simplicity.
604 static const char *listSet(list **l, const char *key, const char *value)
606 // maybe substring of current item, so copy it before we free() anything.
607 const char *newvalue = xstrdup(value);
612 if (strcmp(item->key, key) == 0)
618 free((void *) item->value);
621 item = (list *) xmalloc(sizeof (list));
622 item->key = xstrdup(key);
627 item->value = newvalue;
632 static const char *listFind(const list *l, const char *key)
634 const list *item = l;
637 if (strcmp(item->key, key) == 0)
641 return item ? item->value : NULL;
645 static void listFree(list **l)
650 list *next = item->next;
651 free((void *) item->key);
652 free((void *) item->value);
662 #if defined(__linux__)
665 #warning GSETPROCTITLE not currently supported on this platform.
667 #define GSETPROCTITLE 0
672 #define copyEnv(x) getenv(x)
673 #define freeEnvCopies()
675 static char **GArgv = NULL;
676 static char *GLastArgv = NULL;
677 static int GMaxArgvLen = 0;
678 static int GNoMoreGetEnv = 0;
679 static list *GEnvCopies = NULL;
680 static const char *copyEnv(const char *key)
682 const char *retval = listFind(GEnvCopies, key);
683 if ((retval == NULL) && (!GNoMoreGetEnv))
685 const char *envr = getenv(key);
687 retval = listSet(&GEnvCopies, key, envr);
692 static inline void freeEnvCopies(void)
694 listFree(&GEnvCopies);
700 #define outputLogEntry()
702 static void outputLogEntry(void)
704 FILE *out = fopen(GLOGFILE, "a");
706 debugEcho("Failed to open log file for append!");
709 // Apache Combined Log Format:
710 // http://httpd.apache.org/docs/1.3/logs.html#combined
711 // !!! FIXME: auth and identd?
712 time_t now = time(NULL);
713 const struct tm *tm = localtime(&now);
715 "%s - - [%02d/%s/%d:%02d:%02d:%02d %c%02d%02d]"
716 " \"%s %s%s%s\" %d %lld \"%s\" \"%s\"\n",
717 GRemoteAddr, tm->tm_mday, GMonth[tm->tm_mon],
718 tm->tm_year+1900, tm->tm_hour, tm->tm_min,
719 tm->tm_sec, (tm->tm_gmtoff < 0) ? '-' : '+',
720 (int) (abs((int) tm->tm_gmtoff) / (60*60)),
721 (int) (abs((int) tm->tm_gmtoff) % (60*60)),
722 GReqMethod ? GReqMethod : "",
724 (GReqVersion && *GReqVersion) ? " " : "",
725 GReqVersion ? GReqVersion : "",
726 GHttpStatus, (long long) GBytesSent,
727 GReferer ? GReferer : "-",
728 GUserAgent ? GUserAgent : "-");
735 static void terminate(void)
737 if (!GIsCacheProcess)
739 debugEcho("offload program is terminating...");
740 removeDownloadRecord();
742 while (GSemaphoreOwned > 0)
746 if (GDebugFilePointer != NULL)
747 fclose(GDebugFilePointer);
751 shutdown(GSocket, SHUT_RDWR);
752 while (recv(GSocket, &ch, sizeof (ch), 0) > 0) {}
756 if (stdin) fclose(stdin);
757 if (stdout) fclose(stdout);
758 if (stderr) fclose(stderr);
759 stdin = stdout = stderr = NULL;
767 static void failure_location(const char *httperr, const char *errmsg,
768 const char *location)
770 if (strncasecmp(httperr, "HTTP", 4) == 0)
772 const char *ptr = strchr(httperr, ' ');
778 GHttpStatus = atoi(httperr);
780 debugEcho("failure() called:");
781 debugEcho(" %s", httperr);
782 debugEcho(" %s", errmsg);
786 write_header("HTTP/1.1 ", httperr);
787 write_header("Status: ", httperr);
788 write_header("Server: ", httperr);
790 if (location != NULL)
791 write_header("Location: ", location);
792 write_header("Connection: ", "close");
793 write_header("Content-type: ", "text/plain; charset=utf-8");
794 write_header("", "");
795 write_header("", errmsg);
796 GBytesSent += strlen(errmsg) + 2;
800 } // failure_location
803 static int invalidContentRange(const int64 startRange, const int64 endRange,
806 if ((startRange < 0) || (startRange >= max))
808 else if ((endRange < 0) || (endRange >= max))
810 else if (startRange > endRange)
813 } // invalidContentRange
817 #define debugInit(argc, argv, envp)
819 static void debugInit(int argc, char **argv, char **envp)
821 #if ((!GLISTENPORT) && (!GDEBUGTOFILE))
822 write_header("HTTP/1.1 ", "200 OK");
823 write_header("Status: ", "200 OK");
824 write_header("Content-type: ", "text/plain; charset=utf-8");
826 write_header("Server: ", GSERVERSTRING);
827 write_header("Connection: ", "close");
828 write_header("", "");
835 debugEcho("Offload Debug Run!");
837 printf_date_header(getDebugFilePointer());
838 debugEcho("I am: %s", GSERVERSTRING);
839 debugEcho("Base server: %s", GBASESERVER);
840 debugEcho("User wants to get: %s", Guri);
841 debugEcho("Request from address: %s", GRemoteAddr);
842 debugEcho("Client User-Agent: %s", GUserAgent);
843 debugEcho("Referrer string: %s", GReferer);
844 debugEcho("Request method: %s", GReqMethod);
845 debugEcho("Timeout for HTTP HEAD request is %d", GTIMEOUT);
846 debugEcho("Data cache goes in %s", GOFFLOADDIR);
847 debugEcho("My PID: %d\n", (int) getpid());
852 debugEcho("Command line: %d items...", argc);
853 for (i = 0; i < argc; i++)
854 debugEcho(" argv[%d] = '%s'", i, argv[i]);
857 debugEcho("Environment...");
858 for (i = 0; envp[i]; i++)
859 debugEcho(" %s", envp[i]);
866 static void readHeaders(const int fd, list **headers)
868 const time_t endtime = time(NULL) + GTIMEOUT;
871 int seenresponse = 0;
874 const time_t now = time(NULL);
883 tv.tv_sec = endtime - now;
885 rc = select(fd+1, &rfds, NULL, NULL, &tv);
888 if ((rc <= 0) || (FD_ISSET(fd, &rfds) == 0))
889 failure("503 Service Unavailable", "Timeout while talking to offload host.");
891 // we can only read one byte at a time, since we don't want to
892 // read past end of headers, into actual content, here.
893 if (read(fd, buf + br, 1) != 1)
894 failure("503 Service Unavailable", "Read error while talking to offload host.");
898 else if (buf[br] == '\n')
901 if (br == 0) // empty line, end of headers.
906 ptr = strchr(buf, ':');
912 listSet(headers, buf, ptr);
917 listSet(headers, "response", buf);
918 if (strncasecmp(buf, "HTTP/", 5) == 0)
920 ptr = strchr(buf + 5, ' ');
923 char *start = ptr + 1;
924 ptr = strchr(start, ' ');
927 listSet(headers, "response_code", start);
935 failure("503 Service Unavailable", "Bogus response from offload host server.");
942 if (br >= sizeof (buf))
943 failure("503 Service Unavailable", "Buffer overflow.");
949 static void doWrite(const int fd, const char *str)
951 const int len = strlen(str);
953 const time_t endtime = time(NULL) + GTIMEOUT;
956 const time_t now = time(NULL);
965 tv.tv_sec = endtime - now;
967 rc = select(fd+1, NULL, &wfds, NULL, &tv);
970 if ((rc <= 0) || (FD_ISSET(fd, &wfds) == 0))
971 failure("503 Service Unavailable", "Timeout while talking to offload base server.");
973 rc = write(fd, str + bw, len - bw);
974 if (rc <= 0) // error? closed connection?
975 failure("503 Service Unavailable", "Write error while talking to offload base server.");
981 static int doHttp(const char *method, list **headers)
984 struct addrinfo hints;
985 memset(&hints, '\0', sizeof (hints));
986 hints.ai_family = PF_UNSPEC;
987 hints.ai_socktype = SOCK_STREAM;
988 hints.ai_flags = AI_NUMERICSERV | AI_V4MAPPED | AI_ALL | AI_ADDRCONFIG;
990 struct addrinfo *dns = NULL;
991 if ((rc = getaddrinfo(GBASESERVERIP, GBASESERVERPORTSTR, &hints, &dns)) != 0)
993 debugEcho("getaddrinfo failure: %s", gai_strerror(rc));
994 failure("503 Service Unavailable", "Offload base server hostname lookup failure.");
998 struct addrinfo *addr;
999 for (addr = dns; addr != NULL; addr = addr->ai_next)
1001 fd = socket(addr->ai_family, addr->ai_socktype, addr->ai_protocol);
1004 if (connect(fd, addr->ai_addr, addr->ai_addrlen) == 0)
1013 failure("503 Service Unavailable", "Couldn't connect to offload base server.");
1015 doWrite(fd, method);
1018 doWrite(fd, " HTTP/1.1\r\n");
1019 doWrite(fd, "Host: " GBASESERVER "\r\n");
1020 doWrite(fd, "User-Agent: " GSERVERSTRING "\r\n");
1021 doWrite(fd, "Connection: close\r\n");
1022 doWrite(fd, "X-Mod-Offload-Bypass: true\r\n");
1023 doWrite(fd, "\r\n");
1024 readHeaders(fd, headers);
1029 static void http_head(list **head)
1031 const int fd = doHttp("HEAD", head);
1036 static const char *makeNum(int64 num)
1038 static char buf[64];
1039 snprintf(buf, sizeof (buf), "%lld", (long long) num);
1046 static int http_get(list **head)
1048 list *headers = NULL;
1049 const int fd = doHttp("GET", &headers);
1051 if ((head == NULL) || (fd == -1))
1060 static list *loadMetadata(const char *fname)
1062 list *retval = NULL;
1063 struct stat statbuf;
1064 int fd = open(fname, O_RDONLY);
1068 if (fstat(fd, &statbuf) == -1)
1074 char *buf = (char *) xmalloc(statbuf.st_size + 1);
1075 if (read(fd, buf, statbuf.st_size) != statbuf.st_size)
1082 buf[statbuf.st_size] = '\0';
1090 ptr = strchr(ptr, '\n');
1095 ptr = strchr(ptr, '\n');
1100 listSet(&retval, key, value);
1101 debugEcho("Loaded metadata '%s' => '%s'", key, value);
1106 debugEcho("Loaded %d metadata pair(s).", total);
1112 static int cachedMetadataMostRecent(const list *metadata, const list *head)
1114 const char *contentlength = listFind(metadata, "Content-Length");
1118 const char *etag = listFind(metadata, "ETag");
1122 const char *lastmodified = listFind(metadata, "Last-Modified");
1126 if (strcmp(contentlength, listFind(head, "Content-Length")) != 0)
1129 if (strcmp(etag, listFind(head, "ETag")) != 0)
1132 if (strcmp(lastmodified, listFind(head, "Last-Modified")) != 0)
1134 const char *isweak = listFind(metadata, "X-Offload-Is-Weak");
1135 if ( (!isweak) || (strcmp(isweak, "0") != 0) )
1139 // See if file size != Content-Length, and if it isn't,
1140 // see if X-Offload-Caching-PID still exists. If process
1141 // is missing, assume transfer died and recache.
1142 struct stat statbuf;
1143 if (stat(GFilePath, &statbuf) == -1)
1146 const int64 fsize = statbuf.st_size;
1147 if (fsize != atoi64(contentlength))
1149 // whoa, we were supposed to cache this!
1150 const char *cacher = listFind(metadata, "X-Offload-Caching-PID");
1154 const int cacherpid = atoi(cacher);
1155 if (process_dead(cacherpid))
1157 debugEcho("Caching process ID died!");
1163 } // cachedMetadataMostRecent
1166 static void nukeRequestFromCache(void)
1168 debugEcho("Nuking request from cache...");
1170 if (GMetaDataPath != NULL)
1171 unlink(GMetaDataPath);
1172 if (GFilePath != NULL)
1175 } // nukeRequestFromCache
1178 static char *etagToCacheFname(const char *etag)
1180 static const char chs[] = { ' ', '\t', 0x0B, '\"', '\'' };
1181 char *retval = xstrdup(etag);
1184 for (i = 0; retval[i]; i++)
1186 const char ch = retval[i];
1187 const int total = (sizeof (chs) / sizeof (chs[0]));
1188 for (j = 0; j < total; j++)
1189 if (ch == chs[j]) break;
1195 memmove(retval, retval + i, strlen(retval + i) + 1);
1197 for (i = strlen(retval) - 1; i >= 0; i--)
1199 const char ch = retval[i];
1200 const int total = (sizeof (chs) / sizeof (chs[0]));
1201 for (j = 0; j < total; j++)
1202 if (ch == chs[j]) break;
1210 } // etagToCacheFname
1213 static int selectReadable(const int fd)
1215 const time_t endtime = time(NULL) + GTIMEOUT;
1222 const time_t now = time(NULL);
1228 tv.tv_sec = endtime - now;
1230 rc = select(fd+1, &rfds, NULL, NULL, &tv);
1231 if ((rc < 0) && (errno == EINTR))
1232 continue; // just try again with adjusted timeout.
1238 if ((rc <= 0) || (FD_ISSET(fd, &rfds) == 0))
1240 debugEcho("select() failed");
1248 static void cacheFailure(const char *err)
1250 debugEcho("%s", err);
1251 nukeRequestFromCache();
1256 static void cacheProcessSig(int sig)
1259 snprintf(errbuf, sizeof (errbuf), "caught signal #%d!", sig);
1260 cacheFailure(errbuf);
1261 } // cacheProcessSig
1264 static inline int64 Min(const int64 a, const int64 b)
1266 return (a < b) ? a : b;
1270 static pid_t cacheFork(const int sock, FILE *cacheio, const int64 max)
1272 debugEcho("Cache needs refresh...pulling from base server...");
1274 const pid_t pid = fork();
1276 if (pid != 0) // don't need these any more...
1282 if (pid == -1) // failed!
1284 nukeRequestFromCache();
1285 failure("500 Internal Server Error", "Couldn't fork for caching.");
1289 else if (pid != 0) // we're the parent.
1291 debugEcho("fork()'d caching process! new pid is (%d).", (int) pid);
1296 GIsCacheProcess = 1;
1297 debugEcho("caching process (%d) starting up!", (int) getpid());
1299 #if GMAXDUPEDOWNLOADS > 0
1300 if (GAllDownloads != NULL)
1301 munmap(GAllDownloads, sizeof (DownloadRecord) * MAX_DOWNLOAD_RECORDS);
1302 GAllDownloads = GMyDownload = NULL;
1313 if (stdin) fclose(stdin);
1314 if (stdout) fclose(stdout);
1315 if (stderr) fclose(stderr);
1316 stdin = stdout = stderr = NULL;
1321 // try to clean up in most fatal cases.
1322 signal(SIGHUP, cacheProcessSig);
1323 signal(SIGINT, cacheProcessSig);
1324 signal(SIGTERM, cacheProcessSig);
1325 signal(SIGPIPE, cacheProcessSig);
1326 signal(SIGQUIT, cacheProcessSig);
1327 signal(SIGTRAP, cacheProcessSig);
1328 signal(SIGABRT, cacheProcessSig);
1329 signal(SIGBUS, cacheProcessSig);
1330 signal(SIGSEGV, cacheProcessSig);
1335 snprintf(GArgv[0], GMaxArgvLen, "offload: %s CACHE %s", GBASESERVER, Guri);
1336 char *p = &GArgv[0][strlen(GArgv[0])];
1337 while(p < GLastArgv)
1348 char data[32 * 1024];
1349 const int readsize = (int) Min(sizeof (data), (max - br));
1352 cacheFailure("readsize is unexpectedly zero.");
1353 else if (!selectReadable(sock))
1354 cacheFailure("network timeout");
1355 else if ((len = read(sock, data, sizeof (data))) <= 0)
1356 cacheFailure("network read error");
1357 else if (fwrite(data, len, 1, cacheio) != 1)
1358 cacheFailure("fwrite() failed");
1359 else if (fflush(cacheio) == EOF)
1360 cacheFailure("fflush() failed");
1362 debugEcho("wrote %d bytes to the cache.", len);
1365 if (fclose(cacheio) == EOF)
1366 cacheFailure("fclose() failed");
1368 debugEcho("Successfully cached! Terminating!");
1369 terminate(); // always die.
1372 #endif // #if !GNOCACHE
1375 static int serverMainline(int argc, char **argv, char **envp)
1377 const char *httprange = copyEnv("HTTP_RANGE");
1378 const char *ifrange = copyEnv("HTTP_IF_RANGE");
1379 Guri = copyEnv("REQUEST_URI");
1380 GRemoteAddr = copyEnv("REMOTE_ADDR");
1381 GReferer = copyEnv("HTTP_REFERER");
1382 GUserAgent = copyEnv("HTTP_USER_AGENT");
1383 GReqVersion = copyEnv("REQUEST_VERSION");
1384 GReqMethod = copyEnv("REDIRECT_REQUEST_METHOD");
1385 if (GReqMethod == NULL)
1386 GReqMethod = copyEnv("REQUEST_METHOD");
1387 if (GReqMethod == NULL)
1389 if (GReqVersion == NULL)
1392 debugInit(argc, argv, envp);
1394 if ((Guri == NULL) || (*Guri != '/'))
1395 failure("500 Internal Server Error", "Bad request URI");
1397 // Feed a fake robots.txt to keep webcrawlers out of the offload server.
1398 if (strcmp(Guri, "/robots.txt") == 0)
1399 failure("200 OK", "User-agent: *\nDisallow: /");
1401 // !!! FIXME: favicon?
1406 // This nastiness inspired by proftpd.
1408 for (i = 0; i < argc; i++)
1410 if (!i || (GLastArgv + 1 == argv[i]))
1411 GLastArgv = argv[i] + strlen(argv[i]);
1413 for (i = 0; envp[i] != NULL; i++)
1415 if ((GLastArgv + 1) == envp[i])
1416 GLastArgv = envp[i] + strlen(envp[i]);
1419 extern char *__progname, *__progname_full;
1420 __progname = xstrdup("offload");
1421 __progname_full = xstrdup(argv[0]);
1423 static char *nullenv = NULL;
1424 envp = environ = &nullenv;
1427 GMaxArgvLen = (GLastArgv - GArgv[0]) - 2;
1428 snprintf(GArgv[0], GMaxArgvLen, "offload: %s %s %s %s", GBASESERVER, GRemoteAddr, GReqMethod, Guri);
1429 char *p = &GArgv[0][strlen(GArgv[0])];
1430 while(p < GLastArgv)
1437 const int isget = (strcasecmp(GReqMethod, "GET") == 0);
1438 const int ishead = (strcasecmp(GReqMethod, "HEAD") == 0);
1439 if ( (strchr(Guri, '?') != NULL) || ((!isget) && (!ishead)) )
1440 failure("403 Forbidden", "Offload server doesn't do dynamic content.");
1443 setDownloadRecord();
1450 debugEcho("The HTTP HEAD from %s ...", GBASESERVER);
1452 for (item = head; item; item = item->next)
1453 debugEcho(" '%s' => '%s'", item->key, item->value);
1457 const char *responsecodestr = listFind(head, "response_code");
1458 const char *response = listFind(head, "response");
1459 const char *etag = listFind(head, "ETag");
1460 const char *contentlength = listFind(head, "Content-Length");
1461 const char *lastmodified = listFind(head, "Last-Modified");
1462 const int iresponse = responsecodestr ? atoi(responsecodestr) : 0;
1464 if ((iresponse == 401) || (listFind(head, "WWW-Authenticate")))
1465 failure("403 Forbidden", "Offload server doesn't do protected content.");
1466 else if (iresponse != 200)
1467 failure_location(response, response, listFind(head, "Location"));
1468 else if ((!etag) || (!contentlength) || (!lastmodified))
1469 failure("403 Forbidden", "Offload server doesn't do dynamic content.");
1471 listSet(&head, "X-Offload-Orig-ETag", etag);
1472 if ((strlen(etag) <= 2) || (strncasecmp(etag, "W/", 2) != 0))
1473 listSet(&head, "X-Offload-Is-Weak", "0");
1474 else // a "weak" ETag?
1476 debugEcho("There's a weak ETag on this request.");
1477 listSet(&head, "X-Offload-Is-Weak", "1");
1478 etag = listSet(&head, "ETag", etag + 2);
1479 debugEcho("Chopped ETag to be [%s]", etag);
1482 // !!! FIXME: Check Cache-Control, Pragma no-cache
1487 debugEcho("This is a HEAD request to the offload server.");
1490 // Does client want a range (download resume, "web accelerators", etc)?
1491 const int64 max = atoi64(contentlength);
1492 int64 startRange = 0;
1493 int64 endRange = max-1;
1494 int reportRange = 0;
1495 char *responseCode = "200 OK";
1497 if (ifrange != NULL)
1499 // !!! FIXME: handle this.
1500 debugEcho("Client set If-Range: [%s]...unsupported!", ifrange);
1504 if (httprange != NULL)
1506 debugEcho("There's a HTTP_RANGE specified: [%s].", httprange);
1507 if (strncasecmp(httprange, "bytes=", 6) != 0)
1508 failure("400 Bad Request", "Only ranges of 'bytes' accepted.");
1509 else if (strchr(httprange, ',') != NULL)
1510 failure("400 Bad Request", "Multiple ranges not currently supported");
1514 char *pos = strchr(httprange, '-');
1518 startRange = *httprange == '\0' ? 0 : atoi64(httprange);
1519 endRange = *pos == '\0' ? max-1 : atoi64(pos);
1520 responseCode = "206 Partial Content";
1526 if (endRange >= max) // apparently, this is legal to request.
1529 debugEcho("We are feeding the client bytes %lld to %lld of %lld",
1530 (long long) startRange, (long long) endRange, (long long) max);
1532 if (invalidContentRange(startRange, endRange, max))
1533 failure("400 Bad Request", "Bad content range requested.");
1537 GFilePath = makeStr("%s%s", GOFFLOADDIR, Guri);
1538 debugEcho("file to send is %s", GFilePath);
1539 list *metadata = head;
1541 io = open(GFilePath, O_RDONLY);
1543 failure("500 Internal Server Error", "Couldn't access cached data.");
1547 char *etagFname = etagToCacheFname(etag);
1548 GFilePath = makeStr("%s/filedata-%s", GOFFLOADDIR, etagFname);
1549 GMetaDataPath = makeStr("%s/metadata-%s", GOFFLOADDIR, etagFname);
1552 listSet(&head, "X-Offload-Orig-URL", Guri);
1553 listSet(&head, "X-Offload-Hostname", GBASESERVER);
1555 debugEcho("metadata cache is %s", GMetaDataPath);
1556 debugEcho("file cache is %s", GFilePath);
1558 list *metadata = NULL;
1566 metadata = loadMetadata(GMetaDataPath);
1567 if (cachedMetadataMostRecent(metadata, head))
1570 debugEcho("File is cached.");
1575 listFree(&metadata);
1577 // we need to pull a new copy from the base server...
1578 const int sock = http_get(NULL); // !!! FIXME: may block, don't hold semaphore here!
1580 FILE *cacheio = fopen(GFilePath, "wb");
1581 if (cacheio == NULL)
1584 failure("500 Internal Server Error", "Couldn't update cached data.");
1587 FILE *metaout = fopen(GMetaDataPath, "wb");
1588 if (metaout == NULL)
1592 nukeRequestFromCache();
1593 failure("500 Internal Server Error", "Couldn't update metadata.");
1596 // !!! FIXME: This is a race condition...may change between HEAD
1597 // !!! FIXME: request and actual HTTP grab. We should really
1598 // !!! FIXME: just use this for comparison once, and if we are
1599 // !!! FIXME: recaching, throw this out and use the headers from the
1600 // !!! FIXME: actual HTTP grab when really updating the metadata.
1602 // !!! FIXME: Also, write to temp file and rename in case of write failure!
1603 if (!listFind(head, "Content-Type")) // make sure this is sane.
1604 listSet(&head, "Content-Type", "application/octet-stream");
1606 const pid_t pid = cacheFork(sock, cacheio, max);
1607 listSet(&head, "X-Offload-Caching-PID", makeNum(pid));
1610 for (item = head; item; item = item->next)
1611 fprintf(metaout, "%s\n%s\n", item->key, item->value);
1612 fclose(metaout); // !!! FIXME: check for errors
1619 head = NULL; // we either moved this to (metadata) or free()d it.
1621 io = open(GFilePath, O_RDONLY);
1623 failure("500 Internal Server Error", "Couldn't access cached data.");
1629 GHttpStatus = atoi(responseCode);
1631 write_header("HTTP/1.1 ", responseCode);
1632 write_header("Status: ", responseCode);
1633 write_date_header();
1634 write_header("Server: ", GSERVERSTRING);
1635 write_header("Connection: ", "close");
1636 write_header("ETag: ", listFind(metadata, "ETag"));
1637 write_header("Last-Modified: ", listFind(metadata, "Last-Modified"));
1638 write_header("Content-Length: ", makeNum((endRange - startRange) + 1));
1639 write_header("Accept-Ranges: ", "bytes");
1640 write_header("Content-Type: ", listFind(metadata, "Content-Type"));
1644 snprintf(rangestr, sizeof (rangestr), "bytes %lld-%lld/%lld",
1645 (long long) startRange, (long long) endRange, (long long) max);
1646 write_header("Content-Range: ", rangestr);
1648 write_header("", "");
1650 listFree(&metadata);
1654 debugEcho("This was a HEAD request to offload server, so we're done.");
1660 time_t lastReadTime = time(NULL);
1661 while (br < endRange)
1663 // !!! FIXME: sendfile and TCP_CORK?
1664 char data[32 * 1024];
1665 int64 readsize = startRange - br;
1666 if ((readsize <= 0) || (readsize > sizeof (data)))
1667 readsize = sizeof (data);
1669 if (readsize > (endRange - br))
1670 readsize = (endRange - br);
1674 debugEcho("readsize is unexpectedly zero.");
1675 break; // Shouldn't hit, but just in case...
1678 struct stat statbuf;
1679 if (fstat(io, &statbuf) == -1)
1681 debugEcho("fstat() failed.");
1685 const int64 cursize = statbuf.st_size;
1686 const time_t now = time(NULL);
1689 if ((cursize - br) <= 0) // may be caching on another process.
1691 if (now > (lastReadTime + GTIMEOUT))
1693 debugEcho("timeout: cache file seems to have stalled.");
1694 // !!! FIXME: maybe try to kill() the cache process?
1695 break; // oh well, give up.
1698 sleep(1); // wait awhile...
1699 continue; // ...then try again.
1705 const int len = read(io, data, readsize);
1708 debugEcho("read() failed");
1709 break; // select() and fstat() should have caught this...
1712 // see if the remote end shutdown their end of the socket
1713 // (web browser user hit cancel, etc).
1720 const ssize_t recvval = recv(GSocket, &onebyte, sizeof (onebyte), MSG_DONTWAIT);
1721 deadsocket = (recvval == 0);
1723 debugEcho("EOF on socket!");
1724 if ( ((recvval < 0) && (errno == EAGAIN)) || (deadsocket) )
1728 if ( (feof(stdout)) || (ferror(stdout)) )
1730 debugEcho("EOF or error on stdout!");
1738 if ((br >= startRange) && (br < endRange))
1740 #if ((!GLISTENPORT) && (GDEBUG) && (!GDEBUGTOFILE))
1741 debugEcho("Would have written %d bytes", len);
1744 const int bw = (int) write(GSocket, data, len);
1745 debugEcho("Wrote %d bytes", bw);
1746 GBytesSent += (int64) bw;
1749 debugEcho("FAILED to write %d bytes to client!", len-bw);
1758 debugEcho("closing cache file...");
1761 debugEcho("Transfer loop is complete.");
1765 debugEcho("Bogus transfer! Sent %lld, wanted to send %lld!",
1766 (long long) br, (long long) endRange);
1769 terminate(); // done!
1775 #define GLISTENPORTSTR OFFLOAD_NUMSTR(GLISTENPORT)
1776 static const char *readClientHeaders(const int fd, const struct sockaddr *addr)
1778 debugEcho("Reading request headers...");
1780 // !!! FIXME: do this without network-specifics?
1782 if (addr->sa_family == AF_INET)
1783 ipptr = &((struct sockaddr_in *) addr)->sin_addr;
1784 else if (addr->sa_family == AF_INET6)
1785 ipptr = &((struct sockaddr_in6 *) addr)->sin6_addr;
1787 char remoteaddr[64] = { '\0' };
1789 if ((!ipptr) || (!inet_ntop(addr->sa_family, ipptr, remoteaddr, sizeof (remoteaddr))))
1790 debugEcho("Don't know remote address!");
1793 debugEcho("Remote address is %s", remoteaddr);
1795 static const char *trust[] = { GLISTENTRUSTFWD };
1796 const int total = sizeof (trust) / sizeof (trust[0]);
1798 for (i = 0; i < total; i++)
1800 if ((trust[i]) && (strcmp(trust[i], remoteaddr) == 0))
1803 trusted = (i < total);
1804 debugEcho("This address %s a trusted proxy.", trusted ? "is" : "is not");
1807 const time_t endtime = time(NULL) + GTIMEOUT;
1810 int seenresponse = 0;
1813 const time_t now = time(NULL);
1822 tv.tv_sec = endtime - now;
1824 rc = select(fd+1, &rfds, NULL, NULL, &tv);
1827 if ((rc <= 0) || (FD_ISSET(fd, &rfds) == 0))
1828 return "Timeout while talking to client.";
1830 // we can only read one byte at a time, since we don't want to
1831 // read past end of headers, into actual content, here.
1832 if (read(fd, buf + br, 1) != 1)
1833 return "Read error while talking to client.";
1835 if (buf[br] == '\r')
1837 else if (buf[br] == '\n')
1840 if (br == 0) // empty line, end of headers.
1846 debugEcho("Saw request line from client: '%s'", buf);
1848 ptr = strchr(buf, ':');
1855 if (strcasecmp(buf, "X-Forwarded-For") == 0)
1858 snprintf(remoteaddr, sizeof (remoteaddr), "%s", ptr);
1861 else if (strcasecmp(buf, "User-Agent") == 0)
1862 setenv("HTTP_USER_AGENT", ptr, 1);
1864 else if (strcasecmp(buf, "Range") == 0)
1865 setenv("HTTP_RANGE", ptr, 1);
1867 else if (strcasecmp(buf, "If-Range") == 0)
1868 setenv("HTTP_IF_RANGE", ptr, 1);
1870 else if (strcasecmp(buf, "Referer") == 0)
1871 setenv("HTTP_REFERER", ptr, 1);
1873 // we currently don't care about anything else.
1879 ptr = strchr(buf, ' ');
1885 setenv("REQUEST_METHOD", buf, 1);
1886 const char *start = ptr;
1887 ptr = strchr(ptr, ' ');
1893 setenv("REQUEST_URI", start, 1);
1894 if (strncasecmp(ptr, "HTTP/", 5) == 0)
1895 setenv("REQUEST_VERSION", ptr, 1);
1897 ptr = NULL; // fail below.
1904 return "Bogus request from client.";
1911 if (br >= sizeof (buf))
1912 return "Buffer overflow.";
1917 setenv("REMOTE_ADDR", remoteaddr, 1);
1919 debugEcho("done parsing request headers");
1921 } // readClientHeaders
1924 static void daemonChildSig(int sig)
1926 debugEcho("caught signal #%d!", sig);
1931 static inline void daemonChild(const int fd, const struct sockaddr *addr,
1932 int argc, char **argv)
1934 // try to clean up in most fatal cases.
1935 signal(SIGHUP, daemonChildSig);
1936 signal(SIGINT, daemonChildSig);
1937 signal(SIGTERM, daemonChildSig);
1938 signal(SIGPIPE, daemonChildSig);
1939 signal(SIGQUIT, daemonChildSig);
1940 signal(SIGTRAP, daemonChildSig);
1941 signal(SIGABRT, daemonChildSig);
1942 signal(SIGBUS, daemonChildSig);
1943 signal(SIGSEGV, daemonChildSig);
1947 debugEcho("New child running to handle incoming request.");
1949 if (readClientHeaders(GSocket, addr) == NULL) // NULL == no error.
1950 serverMainline(argc, argv, environ);
1956 #if !GLISTENDAEMONIZE
1957 #define daemonToBackground()
1959 static void daemonToBackground(void)
1961 const pid_t backpid = fork();
1962 if (backpid > 0) // parent.
1965 else if (backpid == -1)
1967 fprintf(stderr, "Failed to fork(): %s\n", strerror(errno));
1971 // we're the child. Welcome to the background.
1975 stdin = stderr = stdout = NULL;
1982 static int daemonListenSocket(void)
1984 struct addrinfo hints;
1985 memset(&hints, '\0', sizeof (hints));
1986 hints.ai_family = GLISTENFAMILY;
1987 hints.ai_socktype = SOCK_STREAM;
1988 hints.ai_flags = AI_NUMERICSERV | AI_V4MAPPED | AI_ALL | AI_ADDRCONFIG | AI_PASSIVE;
1991 struct addrinfo *dns = NULL;
1992 if ((rc = getaddrinfo(GLISTENADDR, GLISTENPORTSTR, &hints, &dns)) != 0)
1995 fprintf(stderr, "getaddrinfo failure: %s\n", gai_strerror(rc));
2000 struct addrinfo *addr;
2001 for (addr = dns; addr != NULL; addr = addr->ai_next)
2003 fd = socket(addr->ai_family, addr->ai_socktype, addr->ai_protocol);
2007 setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on));
2008 if (bind(fd, addr->ai_addr, addr->ai_addrlen) == 0)
2010 if (listen(fd, 16) == 0)
2023 fprintf(stderr, "Failed to bind socket.\n");
2027 } // daemonListenSocket
2030 static inline int daemonMainline(int argc, char **argv, char **envp)
2032 signal(SIGCHLD, SIG_IGN);
2033 daemonToBackground();
2035 const int fd = daemonListenSocket();
2039 while (1) // loop forever.
2041 struct sockaddr addr;
2042 socklen_t addrlen = sizeof (addr);
2043 const int newfd = accept(fd, &addr, &addrlen);
2046 const pid_t pid = fork();
2047 if (pid != 0) // we're NOT the child.
2052 daemonChild(newfd, &addr, argc, argv);
2053 terminate(); // just in case.
2063 int main(int argc, char **argv, char **envp)
2066 GSocket = fileno(stdout);
2067 return serverMainline(argc, argv, envp);
2069 return daemonMainline(argc, argv, envp);
2074 // end of nph-offload.c ...
2078 #if GMAXDUPEDOWNLOADS > 0
2080 // SHA-1 code originally from ftp://ftp.funet.fi/pub/crypt/hash/sha/sha1.c
2081 // License: public domain.
2082 // I cleaned it up a little for my specific purposes. --ryan.
2086 By Steve Reid <steve@edmweb.com>
2090 #define rol(value, bits) (((value) << (bits)) | ((value) >> (32 - (bits))))
2092 /* blk0() and blk() perform the initial expand. */
2093 /* I got the idea of expanding during the round function from SSLeay */
2094 #if !PLATFORM_BIGENDIAN
2095 #define blk0(i) (block->l[i] = (rol(block->l[i],24)&0xFF00FF00) \
2096 |(rol(block->l[i],8)&0x00FF00FF))
2098 #define blk0(i) block->l[i]
2100 #define blk(i) (block->l[i&15] = rol(block->l[(i+13)&15]^block->l[(i+8)&15] \
2101 ^block->l[(i+2)&15]^block->l[i&15],1))
2103 /* (R0+R1), R2, R3, R4 are the different operations used in SHA1 */
2104 #define R0(v,w,x,y,z,i) z+=((w&(x^y))^y)+blk0(i)+0x5A827999+rol(v,5);w=rol(w,30);
2105 #define R1(v,w,x,y,z,i) z+=((w&(x^y))^y)+blk(i)+0x5A827999+rol(v,5);w=rol(w,30);
2106 #define R2(v,w,x,y,z,i) z+=(w^x^y)+blk(i)+0x6ED9EBA1+rol(v,5);w=rol(w,30);
2107 #define R3(v,w,x,y,z,i) z+=(((w|x)&y)|(w&x))+blk(i)+0x8F1BBCDC+rol(v,5);w=rol(w,30);
2108 #define R4(v,w,x,y,z,i) z+=(w^x^y)+blk(i)+0xCA62C1D6+rol(v,5);w=rol(w,30);
2111 /* Hash a single 512-bit block. This is the core of the algorithm. */
2113 static void Sha1_transform(uint32 state[5], const uint8 buffer[64])
2115 uint32 a, b, c, d, e;
2120 CHAR64LONG16* block;
2121 static uint8 workspace[64];
2122 block = (CHAR64LONG16*)workspace;
2123 memcpy(block, buffer, 64);
2124 /* Copy context->state[] to working vars */
2130 /* 4 rounds of 20 operations each. Loop unrolled. */
2131 R0(a,b,c,d,e, 0); R0(e,a,b,c,d, 1); R0(d,e,a,b,c, 2); R0(c,d,e,a,b, 3);
2132 R0(b,c,d,e,a, 4); R0(a,b,c,d,e, 5); R0(e,a,b,c,d, 6); R0(d,e,a,b,c, 7);
2133 R0(c,d,e,a,b, 8); R0(b,c,d,e,a, 9); R0(a,b,c,d,e,10); R0(e,a,b,c,d,11);
2134 R0(d,e,a,b,c,12); R0(c,d,e,a,b,13); R0(b,c,d,e,a,14); R0(a,b,c,d,e,15);
2135 R1(e,a,b,c,d,16); R1(d,e,a,b,c,17); R1(c,d,e,a,b,18); R1(b,c,d,e,a,19);
2136 R2(a,b,c,d,e,20); R2(e,a,b,c,d,21); R2(d,e,a,b,c,22); R2(c,d,e,a,b,23);
2137 R2(b,c,d,e,a,24); R2(a,b,c,d,e,25); R2(e,a,b,c,d,26); R2(d,e,a,b,c,27);
2138 R2(c,d,e,a,b,28); R2(b,c,d,e,a,29); R2(a,b,c,d,e,30); R2(e,a,b,c,d,31);
2139 R2(d,e,a,b,c,32); R2(c,d,e,a,b,33); R2(b,c,d,e,a,34); R2(a,b,c,d,e,35);
2140 R2(e,a,b,c,d,36); R2(d,e,a,b,c,37); R2(c,d,e,a,b,38); R2(b,c,d,e,a,39);
2141 R3(a,b,c,d,e,40); R3(e,a,b,c,d,41); R3(d,e,a,b,c,42); R3(c,d,e,a,b,43);
2142 R3(b,c,d,e,a,44); R3(a,b,c,d,e,45); R3(e,a,b,c,d,46); R3(d,e,a,b,c,47);
2143 R3(c,d,e,a,b,48); R3(b,c,d,e,a,49); R3(a,b,c,d,e,50); R3(e,a,b,c,d,51);
2144 R3(d,e,a,b,c,52); R3(c,d,e,a,b,53); R3(b,c,d,e,a,54); R3(a,b,c,d,e,55);
2145 R3(e,a,b,c,d,56); R3(d,e,a,b,c,57); R3(c,d,e,a,b,58); R3(b,c,d,e,a,59);
2146 R4(a,b,c,d,e,60); R4(e,a,b,c,d,61); R4(d,e,a,b,c,62); R4(c,d,e,a,b,63);
2147 R4(b,c,d,e,a,64); R4(a,b,c,d,e,65); R4(e,a,b,c,d,66); R4(d,e,a,b,c,67);
2148 R4(c,d,e,a,b,68); R4(b,c,d,e,a,69); R4(a,b,c,d,e,70); R4(e,a,b,c,d,71);
2149 R4(d,e,a,b,c,72); R4(c,d,e,a,b,73); R4(b,c,d,e,a,74); R4(a,b,c,d,e,75);
2150 R4(e,a,b,c,d,76); R4(d,e,a,b,c,77); R4(c,d,e,a,b,78); R4(b,c,d,e,a,79);
2151 /* Add the working vars back into context.state[] */
2157 /* Wipe variables */
2158 a = b = c = d = e = 0;
2161 static void Sha1_init(Sha1 *context)
2163 /* SHA1 initialization constants */
2164 context->state[0] = 0x67452301;
2165 context->state[1] = 0xEFCDAB89;
2166 context->state[2] = 0x98BADCFE;
2167 context->state[3] = 0x10325476;
2168 context->state[4] = 0xC3D2E1F0;
2169 context->count[0] = context->count[1] = 0;
2173 /* Run your data through this. */
2175 static void Sha1_append(Sha1 *context, const uint8 *data, uint32 len)
2179 j = (context->count[0] >> 3) & 63;
2180 if ((context->count[0] += len << 3) < (len << 3)) context->count[1]++;
2181 context->count[1] += (len >> 29);
2182 if ((j + len) > 63) {
2183 memcpy(&context->buffer[j], data, (i = 64-j));
2184 Sha1_transform(context->state, context->buffer);
2185 for ( ; i + 63 < len; i += 64) {
2186 Sha1_transform(context->state, &data[i]);
2191 memcpy(&context->buffer[j], &data[i], len - i);
2195 /* Add padding and return the message digest. */
2197 static void Sha1_finish(Sha1 *context, uint8 digest[20])
2200 uint8 finalcount[8];
2202 for (i = 0; i < 8; i++) {
2203 finalcount[i] = (uint8)((context->count[(i >= 4 ? 0 : 1)]
2204 >> ((3-(i & 3)) * 8) ) & 255); /* Endian independent */
2206 Sha1_append(context, (uint8 *)"\200", 1);
2207 while ((context->count[0] & 504) != 448) {
2208 Sha1_append(context, (uint8 *)"\0", 1);
2210 Sha1_append(context, finalcount, 8); /* Should cause a Sha1_transform() */
2211 for (i = 0; i < 20; i++) {
2213 ((context->state[i>>2] >> ((3-(i & 3)) * 8) ) & 255);
2215 /* Wipe variables */
2217 memset(context->buffer, 0, 64);
2218 memset(context->state, 0, 20);
2219 memset(context->count, 0, 8);
2220 memset(&finalcount, 0, 8);
2221 Sha1_transform(context->state, context->buffer);