nph-offload.c
author Ryan C. Gordon <icculus@icculus.org>
Tue, 01 Oct 2013 04:05:03 +0000
changeset 142 2b13ec4e6eae
parent 141 78e574af0a2e
permissions -rw-r--r--
Fixed compiler warning.
     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
     5 //  server.
     6 //
     7 // It works like this:
     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
    30 //    the rest shows up.
    31 
    32 
    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.
    38 
    39 
    40 //
    41 // Installation:
    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).
    44 //
    45 // If you're building this as a standalone server, set the options you want,
    46 //  compile it and start it running.
    47 //
    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.
    51 //
    52 // Assuming this program was at /www/cgi-bin/index.cgi, you would want to add
    53 //  this to Apache's config:
    54 //
    55 //   AliasMatch ^.*$ "/www/cgi-bin/index.cgi"
    56 //
    57 // You might need a "AddHandler cgi-script .cgi" or some other magic.
    58 //
    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:
    62 //
    63 //   ErrorDocument 404 /cgi-bin/index.cgi
    64 //
    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.
    69 //
    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.
    74 //
    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.
    78 //
    79 // Restart the server so the AliasMatch configuration tweak is picked up.
    80 //
    81 //
    82 // This file is written by Ryan C. Gordon (icculus@icculus.org).
    83 
    84 /*
    85  * Building:
    86  *
    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:
    89  *
    90  *    #!/bin/sh
    91  *
    92  *    exec gcc \
    93  *    -DGDEBUG=0 \
    94  *    -DGDEBUGTOFILE=1 \
    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 \
   104  *    -DGLOGACTIVITY=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
   107  */
   108 
   109 #include <stdio.h>
   110 #include <stdlib.h>
   111 #include <string.h>
   112 #include <unistd.h>
   113 #include <stdarg.h>
   114 #include <stdint.h>
   115 #include <time.h>
   116 #include <errno.h>
   117 #include <semaphore.h>
   118 #include <limits.h>
   119 #include <fcntl.h>
   120 #include <signal.h>
   121 #include <sys/types.h>
   122 #include <sys/stat.h>
   123 #include <sys/socket.h>
   124 #include <sys/mman.h>
   125 #include <netdb.h>
   126 #include <netinet/in.h>
   127 #include <arpa/inet.h>
   128 #include <utime.h>
   129 
   130 #define GVERSION "1.1.6"
   131 #define GSERVERSTRING "nph-offload.c/" GVERSION
   132 
   133 #include "offload_server_config.h"
   134 
   135 #define OFFLOAD_NUMSTR2(x) #x
   136 #define OFFLOAD_NUMSTR(x) OFFLOAD_NUMSTR2(x)
   137 
   138 #define GBASESERVERPORTSTR OFFLOAD_NUMSTR(GBASESERVERPORT)
   139 
   140 #ifdef __GNUC__
   141 #define ISPRINTF(x,y) __attribute__((format (printf, x, y)))
   142 #else
   143 #define ISPRINTF(x,y)
   144 #endif
   145 
   146 #ifdef max
   147 #undef max
   148 #endif
   149 
   150 // some getaddrinfo() flags that may not exist...
   151 #ifndef AI_ALL
   152 #define AI_ALL 0
   153 #endif
   154 #ifndef AI_ADDRCONFIG
   155 #define AI_ADDRCONFIG 0
   156 #endif
   157 #ifndef AI_NUMERICSERV
   158 #define AI_NUMERICSERV 0
   159 #endif
   160 #ifndef AI_V4MAPPED
   161 #define AI_V4MAPPED 0
   162 #endif
   163 
   164 typedef int8_t int8;
   165 typedef uint8_t uint8;
   166 typedef int16_t int16;
   167 typedef uint16_t uint16;
   168 typedef int32_t int32;
   169 typedef uint32_t uint32;
   170 typedef int64_t int64;
   171 typedef uint64_t uint64;
   172 
   173 extern char **environ;
   174 
   175 static int GIsCacheProcess = 0;
   176 static int GHttpStatus = 0;
   177 static int64 GBytesSent = 0;
   178 static const char *Guri = NULL;
   179 static const char *GRemoteAddr = NULL;
   180 static const char *GReferer = NULL;
   181 static const char *GUserAgent = NULL;
   182 static const char *GReqVersion = NULL;
   183 static const char *GReqMethod = NULL;
   184 static char *GFilePath = NULL;
   185 static void *GSemaphore = NULL;
   186 static int GSemaphoreOwned = 0;
   187 static FILE *GDebugFilePointer = NULL;
   188 static int GSocket = -1;
   189 
   190 #if !GNOCACHE
   191 static char *GMetaDataPath = NULL;
   192 #endif
   193 
   194 
   195 static void failure_location(const char *, const char *, const char *);
   196 static inline void failure(const char *httperr, const char *errmsg)
   197 {
   198     failure_location(httperr, errmsg, NULL);
   199 } // failure
   200 
   201 
   202 #if ( ((GDEBUG) && (GDEBUGTOFILE)) == 0 )
   203 #define getDebugFilePointer() (NULL)
   204 #else
   205 static FILE *getDebugFilePointer(void)
   206 {
   207     if (GDebugFilePointer == NULL)
   208     {
   209         char buf[PATH_MAX];
   210         snprintf(buf, sizeof(buf), GDEBUGDIR "/debug-%d", (int) getpid());
   211         GDebugFilePointer = fopen(buf, "a");
   212     } // if
   213     return GDebugFilePointer;
   214 } // getDebugFilePointer
   215 #endif
   216 
   217 
   218 #if ((!GDEBUG) && defined(__GNUC__))
   219 #define debugEcho(fmt, ...) do {} while (0)
   220 #else
   221 static void debugEcho(const char *fmt, ...) ISPRINTF(1, 2);
   222 static void debugEcho(const char *fmt, ...)
   223 {
   224     #if GDEBUG
   225         #if !GDEBUGTOFILE
   226         FILE *fp = stdout;
   227         #else
   228         FILE *fp = getDebugFilePointer();
   229         #endif
   230         if (fp != NULL)
   231         {
   232             if (GIsCacheProcess)
   233                 fputs("(cache process) ", fp);
   234 
   235             va_list ap;
   236             va_start(ap, fmt);
   237             vfprintf(fp, fmt, ap);
   238             va_end(ap);
   239             fputs("\n", fp);
   240             fflush(fp);
   241         } // else
   242     #endif
   243 } // debugEcho
   244 #endif
   245 
   246 
   247 static void *createSemaphore(const int initialVal)
   248 {
   249     void *retval = NULL;
   250     const int value = initialVal ? 0 : 1;
   251 
   252     retval = sem_open("SEM-" SHM_NAME, O_CREAT | O_EXCL, 0600, value);
   253     if ((retval == (void *) SEM_FAILED) && (errno == EEXIST))
   254     {
   255         debugEcho("(semaphore already exists, just opening existing one.)");
   256         retval = sem_open("SEM-" SHM_NAME, 0);
   257     } // if
   258 
   259     if (retval == (void *) SEM_FAILED)
   260         return NULL;
   261 
   262     return retval;
   263 } // createSemaphore
   264 
   265 
   266 static void getSemaphore(void)
   267 {
   268     debugEcho("grabbing semaphore...(owned %d time(s).)", GSemaphoreOwned);
   269     if (GSemaphoreOwned++ > 0)
   270         return;
   271 
   272     if (GSemaphore != NULL)
   273     {
   274         if (sem_wait(GSemaphore) == -1)
   275             failure("503 Service Unavailable", "Couldn't lock semaphore.");
   276     } // if
   277     else
   278     {
   279         debugEcho("(have to create semaphore...)");
   280         GSemaphore = createSemaphore(0);
   281         if (GSemaphore == NULL)
   282             failure("503 Service Unavailable", "Couldn't allocate semaphore.");
   283     } // else
   284 } // getSemaphore
   285 
   286 
   287 static void putSemaphore(void)
   288 {
   289     if (GSemaphoreOwned == 0)
   290         return;
   291 
   292     if (--GSemaphoreOwned == 0)
   293     {
   294         if (GSemaphore != NULL)
   295         {
   296             if (sem_post(GSemaphore) == -1)
   297                 failure("503 Service Unavailable", "Couldn't unlock semaphore.");
   298         } // if
   299     } // if
   300     debugEcho("released semaphore...(now owned %d time(s).)", GSemaphoreOwned);
   301 } // putSemaphore
   302 
   303 
   304 static inline int process_dead(const pid_t pid)
   305 {
   306     return ( (pid <= 0) || ((kill(pid, 0) == -1) && (errno == ESRCH)) );
   307 } // process_dead
   308 
   309 
   310 #if GMAXDUPEDOWNLOADS <= 0
   311 #define setDownloadRecord()
   312 #define removeDownloadRecord()
   313 #else
   314 
   315 // we can track this many concurrent connections in a block of shared memory.
   316 //  If DownloadRecord is 24 bytes, then 512 records is 12 kilobytes (usually,
   317 //  three pages of memory). If you actually have more than this many concurrent
   318 //  connections then we'll just stop checking for dupes in things that didn't
   319 //  fit in the table...frankly, if your server is still standing with 512
   320 //  active HTTP downloads, you probably don't care about download accelerators
   321 //  anyhow.   :)
   322 #define MAX_DOWNLOAD_RECORDS 512
   323 
   324 typedef struct
   325 {
   326     pid_t pid;
   327     uint8 sha1[20];
   328 } DownloadRecord;
   329 
   330 static DownloadRecord *GAllDownloads = NULL;
   331 static DownloadRecord *GMyDownload = NULL;
   332 
   333 #define DUPE_FORBID_TEXT \
   334     "403 Forbidden - " GSERVERSTRING "\n\n" \
   335     "Your network address has too many connections for this specific file.\n" \
   336     "Please disable any 'download accelerators' and try again.\n\n" \
   337 
   338 typedef struct
   339 {
   340     uint32 state[5];
   341     uint32 count[2];
   342     uint8 buffer[64];
   343 } Sha1;
   344 
   345 static void Sha1_init(Sha1 *context);
   346 static void Sha1_append(Sha1 *context, const uint8 *data, uint32 len);
   347 static void Sha1_finish(Sha1 *context, uint8 digest[20]);
   348 
   349 static void setDownloadRecord()
   350 {
   351     const pid_t mypid = getpid();
   352     int dupes = 0;
   353     int i = 0;
   354     int fd = -1;
   355     Sha1 sha1data;
   356     uint8 sha1[20];
   357     DownloadRecord *downloads = NULL;
   358     const size_t maplen = sizeof (DownloadRecord) * MAX_DOWNLOAD_RECORDS;
   359     if (GRemoteAddr == NULL)
   360         return;  // oh well.
   361 
   362     GAllDownloads = GMyDownload = NULL;
   363 
   364     getSemaphore();
   365 
   366     fd = shm_open("/" SHM_NAME, (O_CREAT|O_EXCL|O_RDWR), (S_IREAD|S_IWRITE));
   367     if (fd < 0)
   368     {
   369         fd = shm_open("/" SHM_NAME, (O_CREAT|O_RDWR),(S_IREAD|S_IWRITE));
   370         if (fd < 0)
   371         {
   372             putSemaphore();
   373             debugEcho("shm_open() failed: %s", strerror(errno));
   374             return;  // oh well.
   375         } // if
   376     } // if
   377 
   378     ftruncate(fd, maplen);
   379 
   380     void *ptr = mmap(0, maplen, (PROT_READ|PROT_WRITE), MAP_SHARED, fd, 0);
   381     close(fd);  // mapping remains.
   382     if (ptr == MAP_FAILED)
   383     {
   384         putSemaphore();
   385         debugEcho("mmap() failed: %s", strerror(errno));
   386         return;
   387     } // if
   388 
   389     GAllDownloads = downloads = (DownloadRecord *) ptr;
   390 
   391     Sha1_init(&sha1data);
   392     Sha1_append(&sha1data, (const uint8 *) GRemoteAddr, strlen(GRemoteAddr) + 1);
   393     Sha1_append(&sha1data, (const uint8 *) Guri, strlen(Guri) + 1);
   394     Sha1_finish(&sha1data, sha1);
   395 
   396     for (i = 0; i < MAX_DOWNLOAD_RECORDS; i++, downloads++)
   397     {
   398         const pid_t pid = downloads->pid;
   399 
   400         if (pid <= 0)  // unused slot.
   401             GMyDownload = downloads;  // take slot.
   402 
   403         else if (memcmp(downloads->sha1, sha1, sizeof (sha1)) == 0)
   404         {
   405             // make sure this isn't a killed process.
   406             if ( (pid == mypid) || (process_dead(pid)) )
   407             {
   408                 debugEcho("pid #%d died at some point.", (int) pid);
   409                 downloads->pid = 0;
   410                 GMyDownload = downloads;   // take slot.
   411             } // if
   412             else
   413             {
   414                 debugEcho("pid #%d still alive, dupe slot.", (int) pid);
   415                 dupes++;
   416             } // else
   417         } // else if
   418     } // for
   419 
   420     debugEcho("Saw %d dupes.", dupes);
   421 
   422     if (dupes >= GMAXDUPEDOWNLOADS)
   423         failure("403 Forbidden", DUPE_FORBID_TEXT);  // will put semaphore.
   424     else if (GMyDownload == NULL)    // Have fun, downloader accelerator!
   425         debugEcho("no free download slots! Can't add ourselves.");
   426     else
   427     {
   428         debugEcho("Got download slot #%d", (int) (GMyDownload-GAllDownloads));
   429         GMyDownload->pid = mypid;
   430         memcpy(GMyDownload->sha1, sha1, sizeof (sha1));
   431     } // else
   432 
   433     putSemaphore();
   434 } // setDownloadRecord
   435 
   436 
   437 static void removeDownloadRecord()
   438 {
   439     if (!GAllDownloads)
   440         return;
   441 
   442     getSemaphore();
   443     if (GMyDownload != NULL)
   444         GMyDownload->pid = 0;
   445     putSemaphore();
   446     munmap(GAllDownloads, sizeof (DownloadRecord) * MAX_DOWNLOAD_RECORDS);
   447 
   448     GAllDownloads = GMyDownload = NULL;
   449 } // removeDownloadRecord
   450 #endif
   451 
   452 // strftime()'s "%a" gives you locale-dependent strings...
   453 static const char *GWeekday[] = {
   454     "Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat",
   455 };
   456 
   457 // strftime()'s "%b" gives you locale-dependent strings...
   458 static const char *GMonth[] = {
   459     "Jan", "Feb", "Mar", "Apr", "May", "Jun",
   460     "Jul", "Aug", "Sep", "Oct", "Nov", "Dec",
   461 };
   462 
   463 static void make_date_header(char *buf, const size_t buflen)
   464 {
   465     time_t now = time(NULL);
   466     const struct tm *tm = gmtime(&now);
   467     snprintf(buf, buflen, "Date: %s, %02d %s %d %02d:%02d:%02d GMT\r\n",
   468              GWeekday[tm->tm_wday], tm->tm_mday, GMonth[tm->tm_mon],
   469              tm->tm_year+1900, tm->tm_hour, tm->tm_min, tm->tm_sec);
   470 } // make_date_header
   471 
   472 
   473 #if GDEBUG
   474 static void printf_date_header(FILE *out)
   475 {
   476     char buf[128];
   477     if (out == NULL)
   478         return;
   479     make_date_header(buf, sizeof (buf));
   480     fprintf(out, "%s", buf);
   481 } // printf_date_header
   482 #endif
   483 
   484 
   485 static void terminate(void);
   486 
   487 static void write_string(const int fd, const char *str)
   488 {
   489     size_t avail = strlen(str);
   490     while (avail > 0)
   491     {
   492         ssize_t rc = write(fd, str, avail);
   493         if ((rc == -1) && (errno == EINTR))
   494             continue;
   495 
   496         if (rc <= 0)
   497         {
   498             debugEcho("write_string(): write() failed! (%s)\n", strerror(errno));
   499             terminate();
   500         } // if
   501 
   502         avail -= rc;
   503         str += rc;
   504     } // while
   505 } // write_string
   506 
   507 
   508 static void write_header(const char *key, const char *val)
   509 {
   510     write_string(GSocket, key);
   511     write_string(GSocket, val);
   512     write_string(GSocket, "\r\n");
   513 } // write_header
   514 
   515 
   516 static void write_date_header(void)
   517 {
   518     char buf[128];
   519     make_date_header(buf, sizeof (buf));
   520     write_string(GSocket, buf);
   521 } // write_date_header
   522 
   523 
   524 static int64 atoi64(const char *str)
   525 {
   526     int64 retval = 0;
   527     int64 mult = 1;
   528     int i = 0;
   529 
   530     while (*str == ' ')
   531         str++;
   532 
   533     if (*str == '-')
   534     {
   535         mult = -1;
   536         str++;
   537     } // if
   538 
   539     while (1)
   540     {
   541         const char ch = str[i];
   542         if ((ch < '0') || (ch > '9'))
   543             break;
   544         i++;
   545     } // for
   546 
   547     while (--i >= 0)
   548     {
   549         const char ch = str[i];
   550         retval += ((int64) (ch - '0')) * mult;
   551         mult *= 10;
   552     } // while
   553 
   554     return retval;
   555 } // atoi64
   556 
   557 
   558 static void *xmalloc(const size_t len)
   559 {
   560     void *ptr = malloc(len);
   561     if (ptr == NULL)
   562         failure("500 Internal Server Error", "Out of memory.");
   563     return ptr;
   564 } // xmalloc
   565 
   566 static char *xstrdup(const char *str)
   567 {
   568     char *ptr = (char *) xmalloc(strlen(str) + 1);
   569     strcpy(ptr, str);
   570     return ptr;
   571 } // xstrdup
   572 
   573 
   574 static char *makeStr(const char *fmt, ...) ISPRINTF(1, 2);
   575 static char *makeStr(const char *fmt, ...)
   576 {
   577     va_list ap;
   578 
   579     char ch;
   580     va_start(ap, fmt);
   581     const int len = vsnprintf(&ch, 1, fmt, ap);
   582     va_end(ap);
   583 
   584     char *retval = (char *) xmalloc(len + 1);
   585     va_start(ap, fmt);
   586     vsnprintf(retval, len + 1, fmt, ap);
   587     va_end(ap);
   588 
   589     return retval;
   590 } // makeStr
   591 
   592 
   593 // a hashtable would be more sane, but really, we're talking about a handful
   594 //  of items, so this is probably the lower memory option, and it's fast
   595 //  enough for the simplicity.
   596 typedef struct list
   597 {
   598     const char *key;
   599     const char *value;
   600     struct list *next;
   601 } list;
   602 
   603 static const char *listSet(list **l, const char *key, const char *value)
   604 {
   605     // maybe substring of current item, so copy it before we free() anything.
   606     const char *newvalue = xstrdup(value);
   607 
   608     list *item = *l;
   609     while (item)
   610     {
   611         if (strcmp(item->key, key) == 0)
   612             break;
   613         item = item->next;
   614     } // while
   615 
   616     if (item != NULL)
   617         free((void *) item->value);
   618     else
   619     {
   620         item = (list *) xmalloc(sizeof (list));
   621         item->key = xstrdup(key);
   622         item->next = *l;
   623         *l = item;
   624     } // else
   625 
   626     item->value = newvalue;
   627     return newvalue;
   628 } // listSet
   629 
   630 
   631 static const char *listFind(const list *l, const char *key)
   632 {
   633     const list *item = l;
   634     while (item)
   635     {
   636         if (strcmp(item->key, key) == 0)
   637             break;
   638         item = item->next;
   639     } // while
   640     return item ? item->value : NULL;
   641 } // listFind
   642 
   643 
   644 static void listFree(list **l)
   645 {
   646     list *item = *l;
   647     while (item)
   648     {
   649         list *next = item->next;
   650         free((void *) item->key);
   651         free((void *) item->value);
   652         free(item);
   653         item = next;
   654     } // while
   655 
   656     *l = NULL;
   657 } // listFree
   658 
   659 
   660 #if GSETPROCTITLE
   661     #if defined(__linux__)
   662         // okay.
   663     #else
   664         #warning GSETPROCTITLE not currently supported on this platform.
   665         #undef GSETPROCTITLE
   666         #define GSETPROCTITLE 0
   667     #endif
   668 #endif
   669 
   670 #if !GSETPROCTITLE
   671 #define copyEnv(x) getenv(x)
   672 #define freeEnvCopies()
   673 #else
   674 static char **GArgv = NULL;
   675 static char *GLastArgv = NULL;
   676 static int GMaxArgvLen = 0;
   677 static int GNoMoreGetEnv = 0;
   678 static list *GEnvCopies = NULL;
   679 static const char *copyEnv(const char *key)
   680 {
   681     const char *retval = listFind(GEnvCopies, key);
   682     if ((retval == NULL) && (!GNoMoreGetEnv))
   683     {
   684         const char *envr = getenv(key);
   685         if (envr != NULL)
   686             retval = listSet(&GEnvCopies, key, envr);
   687     } // if
   688     return retval;
   689 } // copyEnv
   690 
   691 static inline void freeEnvCopies(void)
   692 {
   693     listFree(&GEnvCopies);
   694 } // freeEnvCopies
   695 #endif
   696 
   697 
   698 #if !GLOGACTIVITY
   699 #define outputLogEntry()
   700 #else
   701 static void outputLogEntry(void)
   702 {
   703     FILE *out = fopen(GLOGFILE, "a");
   704     if (out == NULL)
   705         debugEcho("Failed to open log file for append!");
   706     else
   707     {
   708         // Apache Combined Log Format:
   709         //  http://httpd.apache.org/docs/1.3/logs.html#combined
   710         // !!! FIXME: auth and identd?
   711         time_t now = time(NULL);
   712         const struct tm *tm = localtime(&now);
   713         fprintf(out,
   714             "%s - - [%02d/%s/%d:%02d:%02d:%02d %c%02d%02d]"
   715             " \"%s %s%s%s\" %d %lld \"%s\" \"%s\"\n",
   716             GRemoteAddr, tm->tm_mday, GMonth[tm->tm_mon],
   717             tm->tm_year+1900, tm->tm_hour, tm->tm_min,
   718             tm->tm_sec, (tm->tm_gmtoff < 0) ? '-' : '+',
   719             (int) (abs((int) tm->tm_gmtoff) / (60*60)),
   720             (int) (abs((int) tm->tm_gmtoff) % (60*60)),
   721             GReqMethod ? GReqMethod : "",
   722             Guri ? Guri : "",
   723             (GReqVersion && *GReqVersion) ? " " : "",
   724             GReqVersion ? GReqVersion : "",
   725             GHttpStatus, (long long) GBytesSent,
   726             GReferer ? GReferer : "-",
   727             GUserAgent ? GUserAgent : "-");
   728         fclose(out);
   729     } // else
   730 } // outputLogEntry
   731 #endif
   732 
   733 
   734 static void terminate(void)
   735 {
   736     if (!GIsCacheProcess)
   737     {
   738         debugEcho("offload program is terminating...");
   739         removeDownloadRecord();
   740         outputLogEntry();
   741         while (GSemaphoreOwned > 0)
   742             putSemaphore();
   743     } // if
   744 
   745     if (GDebugFilePointer != NULL)
   746         fclose(GDebugFilePointer);
   747 
   748     #if GLISTENPORT
   749     char ch = 0;
   750     shutdown(GSocket, SHUT_RDWR);
   751     while (recv(GSocket, &ch, sizeof (ch), 0) > 0) {}
   752     close(GSocket);
   753     #endif
   754 
   755     if (stdin) fclose(stdin);
   756     if (stdout) fclose(stdout);
   757     if (stderr) fclose(stderr);
   758     stdin = stdout = stderr = NULL;
   759 
   760     freeEnvCopies();
   761 
   762     exit(0);
   763 } // terminate
   764 
   765 
   766 static void failure_location(const char *httperr, const char *errmsg,
   767                              const char *location)
   768 {
   769     if (strncasecmp(httperr, "HTTP", 4) == 0)
   770     {
   771         const char *ptr = strchr(httperr, ' ');
   772         if (ptr != NULL)
   773             httperr = ptr+1;
   774     } // if
   775 
   776     if (!GHttpStatus)
   777         GHttpStatus = atoi(httperr);
   778 
   779     debugEcho("failure() called:");
   780     debugEcho("  %s", httperr);
   781     debugEcho("  %s", errmsg);
   782 
   783     if (GSocket != -1)
   784     {
   785         write_header("HTTP/1.1 ", httperr);
   786         write_header("Status: ", httperr);
   787         write_header("Server: ", httperr);
   788         write_date_header();
   789         if (location != NULL)
   790             write_header("Location: ", location);
   791         write_header("Connection: ", "close");
   792         write_header("Content-type: ", "text/plain; charset=utf-8");
   793         write_header("", "");
   794         write_header("", errmsg);
   795         GBytesSent += strlen(errmsg) + 2;
   796     } // if
   797 
   798     terminate();
   799 } // failure_location
   800 
   801 
   802 static int invalidContentRange(const int64 startRange, const int64 endRange,
   803                                const int64 max)
   804 {
   805     if ((startRange < 0) || (startRange >= max))
   806         return 1;
   807     else if ((endRange < 0) || (endRange >= max))
   808         return 1;
   809     else if (startRange > endRange)
   810         return 1;
   811     return 0;
   812 } // invalidContentRange
   813 
   814 
   815 #if !GDEBUG
   816 #define debugInit(argc, argv, envp)
   817 #else
   818 static void debugInit(int argc, char **argv, char **envp)
   819 {
   820     #if ((!GLISTENPORT) && (!GDEBUGTOFILE))
   821     write_header("HTTP/1.1 ", "200 OK");
   822     write_header("Status: ", "200 OK");
   823     write_header("Content-type: ", "text/plain; charset=utf-8");
   824     write_date_header();
   825     write_header("Server: ", GSERVERSTRING);
   826     write_header("Connection: ", "close");
   827     write_header("", "");
   828     GHttpStatus = 200;
   829     #endif
   830 
   831     debugEcho("%s", "");
   832     debugEcho("%s", "");
   833     debugEcho("%s", "");
   834     debugEcho("Offload Debug Run!");
   835     debugEcho("%s", "");
   836     printf_date_header(getDebugFilePointer());
   837     debugEcho("I am: %s", GSERVERSTRING);
   838     debugEcho("Base server: %s", GBASESERVER);
   839     debugEcho("User wants to get: %s", Guri);
   840     debugEcho("Request from address: %s", GRemoteAddr);
   841     debugEcho("Client User-Agent: %s", GUserAgent);
   842     debugEcho("Referrer string: %s", GReferer);
   843     debugEcho("Request method: %s", GReqMethod);
   844     debugEcho("Timeout for HTTP HEAD request is %d", GTIMEOUT);
   845     debugEcho("Data cache goes in %s", GOFFLOADDIR);
   846     debugEcho("My PID: %d\n", (int) getpid());
   847     debugEcho("%s", "");
   848     debugEcho("%s", "");
   849 
   850     int i;
   851     debugEcho("Command line: %d items...", argc);
   852     for (i = 0; i < argc; i++)
   853         debugEcho(" argv[%d] = '%s'", i, argv[i]);
   854     debugEcho("%s", "");
   855     debugEcho("%s", "");
   856     debugEcho("Environment...");
   857     for (i = 0; envp[i]; i++)
   858         debugEcho(" %s", envp[i]);
   859     debugEcho("%s", "");
   860     debugEcho("%s", "");
   861 } // debugInit
   862 #endif
   863 
   864 
   865 static void readHeaders(const int fd, list **headers)
   866 {
   867     const time_t endtime = time(NULL) + GTIMEOUT;
   868     int br = 0;
   869     char buf[1024];
   870     int seenresponse = 0;
   871     while (1)
   872     {
   873         const time_t now = time(NULL);
   874         int rc = -1;
   875         fd_set rfds;
   876 
   877         if (endtime >= now)
   878         {
   879             struct timeval tv;
   880             FD_ZERO(&rfds);
   881             FD_SET(fd, &rfds);
   882             tv.tv_sec = endtime - now;
   883             tv.tv_usec = 0;
   884             rc = select(fd+1, &rfds, NULL, NULL, &tv);
   885         } // if
   886 
   887         if ((rc <= 0) || (FD_ISSET(fd, &rfds) == 0))
   888             failure("503 Service Unavailable", "Timeout while talking to offload host.");
   889 
   890         // we can only read one byte at a time, since we don't want to
   891         //  read past end of headers, into actual content, here.
   892         if (read(fd, buf + br, 1) != 1)
   893             failure("503 Service Unavailable", "Read error while talking to offload host.");
   894 
   895         if (buf[br] == '\r')
   896             ;  // ignore these.
   897         else if (buf[br] == '\n')
   898         {
   899             char *ptr = NULL;
   900             if (br == 0)  // empty line, end of headers.
   901                 return;
   902             buf[br] = '\0';
   903             if (seenresponse)
   904             {
   905                 ptr = strchr(buf, ':');
   906                 if (ptr != NULL)
   907                 {
   908                     *(ptr++) = '\0';
   909                     while (*ptr == ' ')
   910                         ptr++;
   911                     listSet(headers, buf, ptr);
   912                 } // if
   913             } // if
   914             else
   915             {
   916                 listSet(headers, "response", buf);
   917                 if (strncasecmp(buf, "HTTP/", 5) == 0)
   918                 {
   919                     ptr = strchr(buf + 5, ' ');
   920                     if (ptr != NULL)
   921                     {
   922                         char *start = ptr + 1;
   923                         ptr = strchr(start, ' ');
   924                         if (ptr != NULL)
   925                             *ptr = '\0';
   926                         listSet(headers, "response_code", start);
   927                         ptr = start;
   928                     } // if
   929                 } // if
   930                 seenresponse = 1;
   931             } // else
   932 
   933             if (ptr == NULL)
   934                 failure("503 Service Unavailable", "Bogus response from offload host server.");
   935 
   936             br = 0;
   937         } // if
   938         else
   939         {
   940             br++;
   941             if (br >= sizeof (buf))
   942                 failure("503 Service Unavailable", "Buffer overflow.");
   943         } // else
   944     } // while
   945 } // readHeaders
   946 
   947 
   948 static void doWrite(const int fd, const char *str)
   949 {
   950     const int len = strlen(str);
   951     int bw = 0;
   952     const time_t endtime = time(NULL) + GTIMEOUT;
   953     while (bw < len)
   954     {
   955         const time_t now = time(NULL);
   956         int rc = -1;
   957         fd_set wfds;
   958 
   959         if (endtime >= now)
   960         {
   961             struct timeval tv;
   962             FD_ZERO(&wfds);
   963             FD_SET(fd, &wfds);
   964             tv.tv_sec = endtime - now;
   965             tv.tv_usec = 0;
   966             rc = select(fd+1, NULL, &wfds, NULL, &tv);
   967         } // if
   968 
   969         if ((rc <= 0) || (FD_ISSET(fd, &wfds) == 0))
   970             failure("503 Service Unavailable", "Timeout while talking to offload base server.");
   971 
   972         rc = write(fd, str + bw, len - bw);
   973         if (rc <= 0)  // error? closed connection?
   974             failure("503 Service Unavailable", "Write error while talking to offload base server.");
   975         bw += rc;
   976     } // while
   977 } // doWrite
   978 
   979 
   980 static int doHttp(const char *method, list **headers)
   981 {
   982     int rc = -1;
   983     struct addrinfo hints;
   984     memset(&hints, '\0', sizeof (hints));
   985     hints.ai_family = PF_UNSPEC;
   986     hints.ai_socktype = SOCK_STREAM;
   987     hints.ai_flags = AI_NUMERICSERV | AI_V4MAPPED | AI_ALL | AI_ADDRCONFIG;
   988 
   989     struct addrinfo *dns = NULL;
   990     if ((rc = getaddrinfo(GBASESERVERIP, GBASESERVERPORTSTR, &hints, &dns)) != 0)
   991     {
   992         debugEcho("getaddrinfo failure: %s", gai_strerror(rc));
   993         failure("503 Service Unavailable", "Offload base server hostname lookup failure.");
   994     } // if
   995 
   996     int fd = -1;
   997     struct addrinfo *addr;
   998     for (addr = dns; addr != NULL; addr = addr->ai_next)
   999     {
  1000         fd = socket(addr->ai_family, addr->ai_socktype, addr->ai_protocol);
  1001         if (fd != -1)
  1002         {
  1003             if (connect(fd, addr->ai_addr, addr->ai_addrlen) == 0)
  1004                 break;
  1005             close(fd);
  1006             fd = -1;
  1007         } // if
  1008     } // for
  1009     freeaddrinfo(dns);
  1010 
  1011     if (fd == -1)
  1012         failure("503 Service Unavailable", "Couldn't connect to offload base server.");
  1013 
  1014     doWrite(fd, method);
  1015     doWrite(fd, " ");
  1016     doWrite(fd, Guri);
  1017     doWrite(fd, " HTTP/1.1\r\n");
  1018     doWrite(fd, "Host: " GBASESERVER "\r\n");
  1019     doWrite(fd, "User-Agent: " GSERVERSTRING "\r\n");
  1020     doWrite(fd, "Connection: close\r\n");
  1021     doWrite(fd, "X-Mod-Offload-Bypass: true\r\n");
  1022     doWrite(fd, "\r\n");
  1023     readHeaders(fd, headers);
  1024     return fd;
  1025 } // doHttp
  1026 
  1027 
  1028 static void http_head(list **head)
  1029 {
  1030     const int fd = doHttp("HEAD", head);
  1031     if (fd != -1)
  1032         close(fd);
  1033 } // http_head
  1034 
  1035 static const char *makeNum(int64 num)
  1036 {
  1037     static char buf[64];
  1038     snprintf(buf, sizeof (buf), "%lld", (long long) num);
  1039     return buf;
  1040 } // makeNum
  1041 
  1042 
  1043 
  1044 #if !GNOCACHE
  1045 static int http_get(list **head)
  1046 {
  1047     list *headers = NULL;
  1048     const int fd = doHttp("GET", &headers);
  1049 
  1050     if ((head == NULL) || (fd == -1))
  1051         listFree(&headers);
  1052 
  1053     if (head != NULL)
  1054         *head = headers;
  1055     return fd;
  1056 } // http_get
  1057 
  1058 
  1059 static list *loadMetadata(const char *fname)
  1060 {
  1061     list *retval = NULL;
  1062     struct stat statbuf;
  1063     int fd = open(fname, O_RDONLY);
  1064     if (fd == -1)
  1065         return NULL;
  1066 
  1067     if (fstat(fd, &statbuf) == -1)
  1068     {
  1069         close(fd);
  1070         return NULL;
  1071     } // if
  1072 
  1073     char *buf = (char *) xmalloc(statbuf.st_size + 1);
  1074     if (read(fd, buf, statbuf.st_size) != statbuf.st_size)
  1075     {
  1076         free(buf);
  1077         close(fd);
  1078         return NULL;
  1079     } // if
  1080 
  1081     buf[statbuf.st_size] = '\0';
  1082     close(fd);
  1083 
  1084     char *ptr = buf;
  1085     int total = 0;
  1086     while (1)
  1087     {
  1088         char *key = ptr;
  1089         ptr = strchr(ptr, '\n');
  1090         if (ptr == NULL)
  1091             break;
  1092         *(ptr++) = '\0';
  1093         char *value = ptr;
  1094         ptr = strchr(ptr, '\n');
  1095         if (ptr == NULL)
  1096             break;
  1097         *(ptr++) = '\0';
  1098         if (*key != '\0')
  1099             listSet(&retval, key, value);
  1100         debugEcho("Loaded metadata '%s' => '%s'", key, value);
  1101         total++;
  1102     } // while
  1103 
  1104     free(buf);
  1105     debugEcho("Loaded %d metadata pair(s).", total);
  1106 
  1107     return retval;
  1108 } // loadMetadata
  1109 
  1110 
  1111 static int cachedMetadataMostRecent(const list *metadata, const list *head)
  1112 {
  1113     const char *contentlength = listFind(metadata, "Content-Length");
  1114     if (!contentlength)
  1115         return 0;
  1116 
  1117     const char *etag = listFind(metadata, "ETag");
  1118     if (!etag)
  1119         return 0;
  1120 
  1121     const char *lastmodified = listFind(metadata, "Last-Modified");
  1122     if (!lastmodified)
  1123         return 0;
  1124 
  1125     if (strcmp(contentlength, listFind(head, "Content-Length")) != 0)
  1126         return 0;
  1127 
  1128     if (strcmp(etag, listFind(head, "ETag")) != 0)
  1129         return 0;
  1130 
  1131     if (strcmp(lastmodified, listFind(head, "Last-Modified")) != 0)
  1132     {
  1133         const char *isweak = listFind(metadata, "X-Offload-Is-Weak");
  1134         if ( (!isweak) || (strcmp(isweak, "0") != 0) )
  1135             return 0;
  1136     } // if
  1137 
  1138     // See if file size != Content-Length, and if it isn't,
  1139     //  see if X-Offload-Caching-PID still exists. If process
  1140     //  is missing, assume transfer died and recache.
  1141     struct stat statbuf;
  1142     if (stat(GFilePath, &statbuf) == -1)
  1143         return 0;
  1144 
  1145     const int64 fsize = statbuf.st_size;
  1146     if (fsize != atoi64(contentlength))
  1147     {
  1148         // whoa, we were supposed to cache this!
  1149         const char *cacher = listFind(metadata, "X-Offload-Caching-PID");
  1150         if (!cacher)
  1151             return 0;
  1152 
  1153         const int cacherpid = atoi(cacher);
  1154         if (process_dead(cacherpid))
  1155         {
  1156             debugEcho("Caching process ID died!");
  1157             return 0;
  1158         } // if
  1159     } // if
  1160 
  1161     return 1;
  1162 } // cachedMetadataMostRecent
  1163 
  1164 
  1165 static void nukeRequestFromCache(void)
  1166 {
  1167     debugEcho("Nuking request from cache...");
  1168     getSemaphore();
  1169     if (GMetaDataPath != NULL)
  1170         unlink(GMetaDataPath);
  1171     if (GFilePath != NULL)
  1172         unlink(GFilePath);
  1173     putSemaphore();
  1174 } // nukeRequestFromCache
  1175 
  1176 
  1177 static char *etagToCacheFname(const char *etag)
  1178 {
  1179     static const char chs[] = { ' ', '\t', 0x0B, '\"', '\'' };
  1180     char *retval = xstrdup(etag);
  1181     int i, j;
  1182 
  1183     for (i = 0; retval[i]; i++)
  1184     {
  1185         const char ch = retval[i];
  1186         const int total = (sizeof (chs) / sizeof (chs[0]));
  1187         for (j = 0; j < total; j++)
  1188             if (ch == chs[j]) break;
  1189         if (j == total)
  1190             break;
  1191     } // for
  1192 
  1193     if (i != 0)
  1194         memmove(retval, retval + i, strlen(retval + i) + 1);
  1195 
  1196     for (i = strlen(retval) - 1; i >= 0; i--)
  1197     {
  1198         const char ch = retval[i];
  1199         const int total = (sizeof (chs) / sizeof (chs[0]));
  1200         for (j = 0; j < total; j++)
  1201             if (ch == chs[j]) break;
  1202         if (j == total)
  1203             break;
  1204     } // for
  1205 
  1206     retval[i+1] = '\0';
  1207 
  1208     return retval;
  1209 } // etagToCacheFname
  1210 
  1211 
  1212 static int selectReadable(const int fd)
  1213 {
  1214     const time_t endtime = time(NULL) + GTIMEOUT;
  1215     fd_set rfds;
  1216     int rc = 0;
  1217 
  1218     while (1)
  1219     {
  1220         struct timeval tv;
  1221         const time_t now = time(NULL);
  1222 
  1223         if (endtime >= now)
  1224         {
  1225             FD_ZERO(&rfds);
  1226             FD_SET(fd, &rfds);
  1227             tv.tv_sec = endtime - now;
  1228             tv.tv_usec = 0;
  1229             rc = select(fd+1, &rfds, NULL, NULL, &tv);
  1230             if ((rc < 0) && (errno == EINTR))
  1231                 continue;   // just try again with adjusted timeout.
  1232         } // if
  1233 
  1234         break;
  1235     } // while
  1236 
  1237     if ((rc <= 0) || (FD_ISSET(fd, &rfds) == 0))
  1238     {
  1239         debugEcho("select() failed");
  1240         return 0;
  1241     } // if
  1242 
  1243     return 1;
  1244 } // selectReadable
  1245 
  1246 
  1247 static void cacheFailure(const char *err)
  1248 {
  1249     debugEcho("%s", err);
  1250     nukeRequestFromCache();
  1251     terminate();
  1252 } // cacheFailure
  1253 
  1254 
  1255 static void cacheProcessSig(int sig)
  1256 {
  1257     char errbuf[128];
  1258     snprintf(errbuf, sizeof (errbuf), "caught signal #%d!", sig);
  1259     cacheFailure(errbuf);
  1260 } // cacheProcessSig
  1261 
  1262 
  1263 static inline int64 Min(const int64 a, const int64 b)
  1264 {
  1265     return (a < b) ? a : b;
  1266 } // Min
  1267 
  1268 
  1269 static pid_t cacheFork(const int sock, FILE *cacheio, const int64 max)
  1270 {
  1271     debugEcho("Cache needs refresh...pulling from base server...");
  1272 
  1273     const pid_t pid = fork();
  1274 
  1275     if (pid != 0)  // don't need these any more...
  1276     {
  1277         fclose(cacheio);
  1278         close(sock);
  1279     } // if
  1280 
  1281     if (pid == -1)  // failed!
  1282     {
  1283         nukeRequestFromCache();
  1284         failure("500 Internal Server Error", "Couldn't fork for caching.");
  1285         return pid;
  1286     } // if
  1287 
  1288     else if (pid != 0)  // we're the parent.
  1289     {
  1290         debugEcho("fork()'d caching process! new pid is (%d).", (int) pid);
  1291         return pid;
  1292     } // else if
  1293 
  1294     // we're the child.
  1295     GIsCacheProcess = 1;
  1296     debugEcho("caching process (%d) starting up!", (int) getpid());
  1297 
  1298     #if GMAXDUPEDOWNLOADS > 0
  1299     if (GAllDownloads != NULL)
  1300         munmap(GAllDownloads, sizeof (DownloadRecord) * MAX_DOWNLOAD_RECORDS);
  1301     GAllDownloads = GMyDownload = NULL;
  1302     #endif
  1303 
  1304     #if GLISTENPORT
  1305     if (GSocket != -1)
  1306     {
  1307         close(GSocket);
  1308         GSocket = -1;
  1309     } // if
  1310     #endif
  1311 
  1312     if (stdin) fclose(stdin);
  1313     if (stdout) fclose(stdout);
  1314     if (stderr) fclose(stderr);
  1315     stdin = stdout = stderr = NULL;
  1316 
  1317     chdir("/");
  1318     setsid();
  1319 
  1320     // try to clean up in most fatal cases.
  1321     signal(SIGHUP, cacheProcessSig);
  1322     signal(SIGINT, cacheProcessSig);
  1323     signal(SIGTERM, cacheProcessSig);
  1324     signal(SIGPIPE, cacheProcessSig);
  1325     signal(SIGQUIT, cacheProcessSig);
  1326     signal(SIGTRAP, cacheProcessSig);
  1327     signal(SIGABRT, cacheProcessSig);
  1328     signal(SIGBUS, cacheProcessSig);
  1329     signal(SIGSEGV, cacheProcessSig);
  1330 
  1331     #if GSETPROCTITLE
  1332         #ifdef __linux__
  1333         {
  1334             snprintf(GArgv[0], GMaxArgvLen, "offload: %s CACHE %s", GBASESERVER, Guri);
  1335             char *p = &GArgv[0][strlen(GArgv[0])];
  1336             while(p < GLastArgv)
  1337                 *(p++) = '\0';
  1338             GArgv[1] = NULL;
  1339         }
  1340         #endif
  1341     #endif
  1342 
  1343     int64 br = 0;
  1344     while (br < max)
  1345     {
  1346         int len = 0;
  1347         char data[32 * 1024];
  1348         const int readsize = (int) Min(sizeof (data), (max - br));
  1349 
  1350         if (readsize == 0)
  1351             cacheFailure("readsize is unexpectedly zero.");
  1352         else if (!selectReadable(sock))
  1353             cacheFailure("network timeout");
  1354         else if ((len = read(sock, data, sizeof (data))) <= 0)
  1355             cacheFailure("network read error");
  1356         else if (fwrite(data, len, 1, cacheio) != 1)
  1357             cacheFailure("fwrite() failed");
  1358         else if (fflush(cacheio) == EOF)
  1359             cacheFailure("fflush() failed");
  1360         br += len;
  1361         debugEcho("wrote %d bytes to the cache.", len);
  1362     } // while
  1363 
  1364     if (fclose(cacheio) == EOF)
  1365         cacheFailure("fclose() failed");
  1366 
  1367     debugEcho("Successfully cached! Terminating!");
  1368     terminate();  // always die.
  1369     return -1;
  1370 } // cacheFork
  1371 #endif  // #if !GNOCACHE
  1372 
  1373 
  1374 static int serverMainline(int argc, char **argv, char **envp)
  1375 {
  1376     const char *httprange = copyEnv("HTTP_RANGE");
  1377     const char *ifrange = copyEnv("HTTP_IF_RANGE");
  1378     Guri = copyEnv("REQUEST_URI");
  1379     GRemoteAddr = copyEnv("REMOTE_ADDR");
  1380     GReferer = copyEnv("HTTP_REFERER");
  1381     GUserAgent = copyEnv("HTTP_USER_AGENT");
  1382     GReqVersion = copyEnv("REQUEST_VERSION");
  1383     GReqMethod = copyEnv("REDIRECT_REQUEST_METHOD");
  1384     if (GReqMethod == NULL)
  1385         GReqMethod = copyEnv("REQUEST_METHOD");
  1386     if (GReqMethod == NULL)
  1387         GReqMethod = "GET";
  1388     if (GReqVersion == NULL)
  1389         GReqVersion = "";
  1390 
  1391     debugInit(argc, argv, envp);
  1392 
  1393     if ((Guri == NULL) || (*Guri != '/'))
  1394         failure("500 Internal Server Error", "Bad request URI");
  1395 
  1396     // Feed a fake robots.txt to keep webcrawlers out of the offload server.
  1397     if (strcmp(Guri, "/robots.txt") == 0)
  1398         failure("200 OK", "User-agent: *\nDisallow: /");
  1399 
  1400     // !!! FIXME: favicon?
  1401 
  1402     #if GSETPROCTITLE
  1403         #ifdef __linux__
  1404         {
  1405             // This nastiness inspired by proftpd.
  1406             int i;
  1407             for (i = 0; i < argc; i++)
  1408             {
  1409                 if (!i || (GLastArgv + 1 == argv[i]))
  1410                     GLastArgv = argv[i] + strlen(argv[i]);
  1411             } // for
  1412             for (i = 0; envp[i] != NULL; i++)
  1413             {
  1414                 if ((GLastArgv + 1) == envp[i])
  1415                     GLastArgv = envp[i] + strlen(envp[i]);
  1416             } // for
  1417 
  1418             extern char *__progname, *__progname_full;
  1419             __progname = xstrdup("offload");
  1420             __progname_full = xstrdup(argv[0]);
  1421             GNoMoreGetEnv = 1;
  1422             static char *nullenv = NULL;
  1423             envp = environ = &nullenv;
  1424 
  1425             GArgv = argv;
  1426             GMaxArgvLen = (GLastArgv - GArgv[0]) - 2;
  1427             snprintf(GArgv[0], GMaxArgvLen, "offload: %s %s %s %s", GBASESERVER, GRemoteAddr, GReqMethod, Guri);
  1428             char *p = &GArgv[0][strlen(GArgv[0])];
  1429             while(p < GLastArgv)
  1430                 *(p++) = '\0';
  1431             GArgv[1] = NULL;
  1432         }
  1433         #endif
  1434     #endif
  1435 
  1436     const int isget = (strcasecmp(GReqMethod, "GET") == 0);
  1437     const int ishead = (strcasecmp(GReqMethod, "HEAD") == 0);
  1438     if ( (strchr(Guri, '?') != NULL) || ((!isget) && (!ishead)) )
  1439         failure("403 Forbidden", "Offload server doesn't do dynamic content.");
  1440 
  1441     if (!ishead)
  1442         setDownloadRecord();
  1443 
  1444     list *head = NULL;
  1445     http_head(&head);
  1446 
  1447     #if GDEBUG
  1448     {
  1449         debugEcho("The HTTP HEAD from %s ...", GBASESERVER);
  1450         list *item;
  1451         for (item = head; item; item = item->next)
  1452             debugEcho("   '%s' => '%s'", item->key, item->value);
  1453     }
  1454     #endif
  1455 
  1456     const char *responsecodestr = listFind(head, "response_code");
  1457     const char *response = listFind(head, "response");
  1458     const char *etag = listFind(head, "ETag");
  1459     const char *contentlength = listFind(head, "Content-Length");
  1460     const char *lastmodified = listFind(head, "Last-Modified");
  1461     const int iresponse = responsecodestr ? atoi(responsecodestr) : 0;
  1462 
  1463     if ((iresponse == 401) || (listFind(head, "WWW-Authenticate")))
  1464         failure("403 Forbidden", "Offload server doesn't do protected content.");
  1465     else if (iresponse != 200)
  1466         failure_location(response, response, listFind(head, "Location"));
  1467     else if ((!etag) || (!contentlength) || (!lastmodified))
  1468         failure("403 Forbidden", "Offload server doesn't do dynamic content.");
  1469 
  1470     listSet(&head, "X-Offload-Orig-ETag", etag);
  1471     if ((strlen(etag) <= 2) || (strncasecmp(etag, "W/", 2) != 0))
  1472         listSet(&head, "X-Offload-Is-Weak", "0");
  1473     else  // a "weak" ETag?
  1474     {
  1475         debugEcho("There's a weak ETag on this request.");
  1476         listSet(&head, "X-Offload-Is-Weak", "1");
  1477         etag = listSet(&head, "ETag", etag + 2);
  1478         debugEcho("Chopped ETag to be [%s]", etag);
  1479     } // if
  1480 
  1481     // !!! FIXME: Check Cache-Control, Pragma no-cache
  1482 
  1483     int io = -1;
  1484 
  1485     if (ishead)
  1486         debugEcho("This is a HEAD request to the offload server.");
  1487 
  1488     // Partial content:
  1489     // Does client want a range (download resume, "web accelerators", etc)?
  1490     const int64 max = atoi64(contentlength);
  1491     int64 startRange = 0;
  1492     int64 endRange = max-1;
  1493     int reportRange = 0;
  1494     char *responseCode = "200 OK";
  1495 
  1496     if (ifrange != NULL)
  1497     {
  1498         // !!! FIXME: handle this.
  1499         debugEcho("Client set If-Range: [%s]...unsupported!", ifrange);
  1500         httprange = NULL;
  1501     } // if
  1502 
  1503     if (httprange != NULL)
  1504     {
  1505         debugEcho("There's a HTTP_RANGE specified: [%s].", httprange);
  1506         if (strncasecmp(httprange, "bytes=", 6) != 0)
  1507             failure("400 Bad Request", "Only ranges of 'bytes' accepted.");
  1508         else if (strchr(httprange, ',') != NULL)
  1509             failure("400 Bad Request", "Multiple ranges not currently supported");
  1510         else
  1511         {
  1512             httprange += 6;
  1513             char *pos = strchr(httprange, '-');
  1514             if (pos != NULL)
  1515             {
  1516                 *(pos++) = '\0';
  1517                 startRange = *httprange == '\0' ? 0 : atoi64(httprange);
  1518                 endRange = *pos == '\0' ? max-1 : atoi64(pos);
  1519                 responseCode = "206 Partial Content";
  1520                 reportRange = 1;
  1521             } // if
  1522         } // else
  1523     } // if
  1524 
  1525     if (endRange >= max)  // apparently, this is legal to request.
  1526         endRange = max - 1;
  1527 
  1528     debugEcho("We are feeding the client bytes %lld to %lld of %lld",
  1529                 (long long) startRange, (long long) endRange, (long long) max);
  1530 
  1531     if (invalidContentRange(startRange, endRange, max))
  1532         failure("400 Bad Request", "Bad content range requested.");
  1533 
  1534 #if GNOCACHE
  1535 
  1536     GFilePath = makeStr("%s%s", GOFFLOADDIR, Guri);
  1537     debugEcho("file to send is %s", GFilePath);
  1538     list *metadata = head;
  1539     head = NULL;
  1540     io = open(GFilePath, O_RDONLY);
  1541     if (io == -1)
  1542         failure("500 Internal Server Error", "Couldn't access cached data.");
  1543 
  1544 #else
  1545 
  1546     char *etagFname = etagToCacheFname(etag);
  1547     GFilePath = makeStr("%s/filedata-%s", GOFFLOADDIR, etagFname);
  1548     GMetaDataPath = makeStr("%s/metadata-%s", GOFFLOADDIR, etagFname);
  1549     free(etagFname);
  1550 
  1551     listSet(&head, "X-Offload-Orig-URL", Guri);
  1552     listSet(&head, "X-Offload-Hostname", GBASESERVER);
  1553 
  1554     debugEcho("metadata cache is %s", GMetaDataPath);
  1555     debugEcho("file cache is %s", GFilePath);
  1556 
  1557     list *metadata = NULL;
  1558 
  1559     if (ishead)
  1560         metadata = head;
  1561     else
  1562     {
  1563         getSemaphore();
  1564 
  1565         metadata = loadMetadata(GMetaDataPath);
  1566         if (cachedMetadataMostRecent(metadata, head))
  1567         {
  1568             listFree(&head);
  1569             debugEcho("File is cached.");
  1570             utime(GFilePath, NULL);  // update to latest time so we know what's being requested most.
  1571             utime(GMetaDataPath, NULL);  // update to latest time so we know what's being requested most.
  1572         } // if
  1573 
  1574         else
  1575         {
  1576             listFree(&metadata);
  1577 
  1578             // we need to pull a new copy from the base server...
  1579             const int sock = http_get(NULL);  // !!! FIXME: may block, don't hold semaphore here!
  1580 
  1581             FILE *cacheio = fopen(GFilePath, "wb");
  1582             if (cacheio == NULL)
  1583             {
  1584                 close(io);
  1585                 failure("500 Internal Server Error", "Couldn't update cached data.");
  1586             } // if
  1587 
  1588             FILE *metaout = fopen(GMetaDataPath, "wb");
  1589             if (metaout == NULL)
  1590             {
  1591                 fclose(cacheio);
  1592                 close(sock);
  1593                 nukeRequestFromCache();
  1594                 failure("500 Internal Server Error", "Couldn't update metadata.");
  1595             } // if
  1596 
  1597             // !!! FIXME: This is a race condition...may change between HEAD
  1598             // !!! FIXME:  request and actual HTTP grab. We should really
  1599             // !!! FIXME:  just use this for comparison once, and if we are
  1600             // !!! FIXME:  recaching, throw this out and use the headers from the
  1601             // !!! FIXME:  actual HTTP grab when really updating the metadata.
  1602             //
  1603             // !!! FIXME: Also, write to temp file and rename in case of write failure!
  1604             if (!listFind(head, "Content-Type"))  // make sure this is sane.
  1605                 listSet(&head, "Content-Type", "application/octet-stream");
  1606 
  1607             const pid_t pid = cacheFork(sock, cacheio, max);
  1608             listSet(&head, "X-Offload-Caching-PID", makeNum(pid));
  1609 
  1610             list *item;
  1611             for (item = head; item; item = item->next)
  1612                 fprintf(metaout, "%s\n%s\n", item->key, item->value);
  1613             fclose(metaout);  // !!! FIXME: check for errors
  1614 
  1615             metadata = head;
  1616         } // else
  1617 
  1618         putSemaphore();
  1619 
  1620         head = NULL;   // we either moved this to (metadata) or free()d it.
  1621 
  1622         io = open(GFilePath, O_RDONLY);
  1623         if (io == -1)
  1624             failure("500 Internal Server Error", "Couldn't access cached data.");
  1625     } // else
  1626 
  1627 #endif
  1628 
  1629     if (!GHttpStatus)
  1630         GHttpStatus = atoi(responseCode);
  1631 
  1632     write_header("HTTP/1.1 ", responseCode);
  1633     write_header("Status: ", responseCode);
  1634     write_date_header();
  1635     write_header("Server: ", GSERVERSTRING);
  1636     write_header("Connection: ", "close");
  1637     write_header("ETag: ", listFind(metadata, "ETag"));
  1638     write_header("Last-Modified: ", listFind(metadata, "Last-Modified"));
  1639     write_header("Content-Length: ", makeNum((endRange - startRange) + 1));
  1640     write_header("Accept-Ranges: ", "bytes");
  1641     write_header("Content-Type: ", listFind(metadata, "Content-Type"));
  1642     if (reportRange)
  1643     {
  1644         char rangestr[128];
  1645         snprintf(rangestr, sizeof (rangestr), "bytes %lld-%lld/%lld",
  1646                (long long) startRange, (long long) endRange, (long long) max);
  1647         write_header("Content-Range: ", rangestr);
  1648     } // if
  1649     write_header("", "");
  1650 
  1651     listFree(&metadata);
  1652 
  1653     if (ishead)
  1654     {
  1655         debugEcho("This was a HEAD request to offload server, so we're done.");
  1656         terminate();
  1657     } // if
  1658 
  1659     int64 br = 0;
  1660     endRange++;
  1661     time_t lastReadTime = time(NULL);
  1662     while (br < endRange)
  1663     {
  1664         // !!! FIXME: sendfile and TCP_CORK?
  1665         char data[32 * 1024];
  1666         int64 readsize = startRange - br;
  1667         if ((readsize <= 0) || (readsize > sizeof (data)))
  1668             readsize = sizeof (data);
  1669 
  1670         if (readsize > (endRange - br))
  1671             readsize = (endRange - br);
  1672 
  1673         if (readsize == 0)
  1674         {
  1675             debugEcho("readsize is unexpectedly zero.");
  1676             break;  // Shouldn't hit, but just in case...
  1677         } // if
  1678 
  1679         struct stat statbuf;
  1680         if (fstat(io, &statbuf) == -1)
  1681         {
  1682             debugEcho("fstat() failed.");
  1683             break;
  1684         } // if
  1685 
  1686         const int64 cursize = statbuf.st_size;
  1687         const time_t now = time(NULL);
  1688         if (cursize < max)
  1689         {
  1690             if ((cursize - br) <= 0)  // may be caching on another process.
  1691             {
  1692                 if (now > (lastReadTime + GTIMEOUT))
  1693                 {
  1694                     debugEcho("timeout: cache file seems to have stalled.");
  1695                     // !!! FIXME: maybe try to kill() the cache process?
  1696                     break;   // oh well, give up.
  1697                 } // if
  1698 
  1699                 sleep(1);   // wait awhile...
  1700                 continue;   // ...then try again.
  1701             } // if
  1702         } // else
  1703 
  1704         lastReadTime = now;
  1705 
  1706         const int len = read(io, data, readsize);
  1707         if (len <= 0)
  1708         {
  1709             debugEcho("read() failed");
  1710             break;   // select() and fstat() should have caught this...
  1711         } // if
  1712 
  1713         // see if the remote end shutdown their end of the socket
  1714         //  (web browser user hit cancel, etc).
  1715         int deadsocket = 0;
  1716 
  1717         #if GLISTENPORT
  1718         while (1)
  1719         {
  1720             char onebyte = 0;
  1721             const ssize_t recvval = recv(GSocket, &onebyte, sizeof (onebyte), MSG_DONTWAIT);
  1722             deadsocket = (recvval == 0);
  1723             if (deadsocket)
  1724                 debugEcho("EOF on socket!");
  1725             if ( ((recvval < 0) && (errno == EAGAIN)) || (deadsocket) )
  1726                 break;
  1727         } // while
  1728         #else
  1729         if ( (feof(stdout)) || (ferror(stdout)) )
  1730         {
  1731             debugEcho("EOF or error on stdout!");
  1732             deadsocket = 1;
  1733         } // if
  1734         #endif
  1735 
  1736         if (deadsocket)
  1737             break;
  1738 
  1739         if ((br >= startRange) && (br < endRange))
  1740         {
  1741             #if ((!GLISTENPORT) && (GDEBUG) && (!GDEBUGTOFILE))
  1742             debugEcho("Would have written %d bytes", len);
  1743             GBytesSent += len;
  1744             #else
  1745             const int bw = (int) write(GSocket, data, len);
  1746             debugEcho("Wrote %d bytes", bw);
  1747             GBytesSent += (int64) bw;
  1748             if (bw != len)
  1749             {
  1750                 debugEcho("FAILED to write %d bytes to client!", len-bw);
  1751                 break;
  1752             } // else
  1753             #endif
  1754         } // else if
  1755 
  1756         br += len;
  1757     } // while
  1758 
  1759     debugEcho("closing cache file...");
  1760     close(io);
  1761 
  1762     debugEcho("Transfer loop is complete.");
  1763 
  1764     if (br != endRange)
  1765     {
  1766         debugEcho("Bogus transfer! Sent %lld, wanted to send %lld!",
  1767                   (long long) br, (long long) endRange);
  1768     } // else
  1769 
  1770     terminate();  // done!
  1771     return 0;
  1772 } // serverMainline
  1773 
  1774 
  1775 #if GLISTENPORT
  1776 #define GLISTENPORTSTR OFFLOAD_NUMSTR(GLISTENPORT)
  1777 static const char *readClientHeaders(const int fd, const struct sockaddr *addr)
  1778 {
  1779     debugEcho("Reading request headers...");
  1780 
  1781     // !!! FIXME: do this without network-specifics?
  1782     void *ipptr = NULL;
  1783     if (addr->sa_family == AF_INET)
  1784         ipptr = &((struct sockaddr_in *) addr)->sin_addr;
  1785     else if (addr->sa_family == AF_INET6)
  1786         ipptr = &((struct sockaddr_in6 *) addr)->sin6_addr;
  1787 
  1788     char remoteaddr[64] = { '\0' };
  1789     int trusted = 0;
  1790     if ((!ipptr) || (!inet_ntop(addr->sa_family, ipptr, remoteaddr, sizeof (remoteaddr))))
  1791         debugEcho("Don't know remote address!");
  1792     else
  1793     {
  1794         debugEcho("Remote address is %s", remoteaddr);
  1795 
  1796         static const char *trust[] = { GLISTENTRUSTFWD };
  1797         const int total = sizeof (trust) / sizeof (trust[0]);
  1798         int i;
  1799         for (i = 0; i < total; i++)
  1800         {
  1801             if ((trust[i]) && (strcmp(trust[i], remoteaddr) == 0))
  1802                 break;
  1803         } // for
  1804         trusted = (i < total);
  1805         debugEcho("This address %s a trusted proxy.", trusted ? "is" : "is not");
  1806     } // else
  1807 
  1808     const time_t endtime = time(NULL) + GTIMEOUT;
  1809     int br = 0;
  1810     char buf[1024];
  1811     int seenresponse = 0;
  1812     while (1)
  1813     {
  1814         const time_t now = time(NULL);
  1815         int rc = -1;
  1816         fd_set rfds;
  1817 
  1818         if (endtime >= now)
  1819         {
  1820             struct timeval tv;
  1821             FD_ZERO(&rfds);
  1822             FD_SET(fd, &rfds);
  1823             tv.tv_sec = endtime - now;
  1824             tv.tv_usec = 0;
  1825             rc = select(fd+1, &rfds, NULL, NULL, &tv);
  1826         } // if
  1827 
  1828         if ((rc <= 0) || (FD_ISSET(fd, &rfds) == 0))
  1829             return "Timeout while talking to client.";
  1830 
  1831         // we can only read one byte at a time, since we don't want to
  1832         //  read past end of headers, into actual content, here.
  1833         if (read(fd, buf + br, 1) != 1)
  1834             return "Read error while talking to client.";
  1835 
  1836         if (buf[br] == '\r')
  1837             ;  // ignore these.
  1838         else if (buf[br] == '\n')
  1839         {
  1840             char *ptr = NULL;
  1841             if (br == 0)  // empty line, end of headers.
  1842                 break;
  1843 
  1844             buf[br] = '\0';
  1845             if (seenresponse)
  1846             {
  1847                 debugEcho("Saw request line from client: '%s'", buf);
  1848 
  1849                 ptr = strchr(buf, ':');
  1850                 if (ptr != NULL)
  1851                 {
  1852                     *(ptr++) = '\0';
  1853                     while (*ptr == ' ')
  1854                         ptr++;
  1855 
  1856                     if (strcasecmp(buf, "X-Forwarded-For") == 0)
  1857                     {
  1858                         if (trusted)
  1859                             snprintf(remoteaddr, sizeof (remoteaddr), "%s", ptr);
  1860                     } // if
  1861 
  1862                     else if (strcasecmp(buf, "User-Agent") == 0)
  1863                         setenv("HTTP_USER_AGENT", ptr, 1);
  1864 
  1865                     else if (strcasecmp(buf, "Range") == 0)
  1866                         setenv("HTTP_RANGE", ptr, 1);
  1867 
  1868                     else if (strcasecmp(buf, "If-Range") == 0)
  1869                         setenv("HTTP_IF_RANGE", ptr, 1);
  1870 
  1871                     else if (strcasecmp(buf, "Referer") == 0)
  1872                         setenv("HTTP_REFERER", ptr, 1);
  1873 
  1874                     // we currently don't care about anything else.
  1875                 } // if
  1876             } // if
  1877 
  1878             else
  1879             {
  1880                 ptr = strchr(buf, ' ');
  1881                 if (ptr != NULL)
  1882                 {
  1883                     *(ptr++) = '\0';
  1884                     while (*ptr == ' ')
  1885                         ptr++;
  1886                     setenv("REQUEST_METHOD", buf, 1);
  1887                     const char *start = ptr;
  1888                     ptr = strchr(ptr, ' ');
  1889                     if (ptr != NULL)
  1890                     {
  1891                         *(ptr++) = '\0';
  1892                         while (*ptr == ' ')
  1893                             ptr++;
  1894                         setenv("REQUEST_URI", start, 1);
  1895                         if (strncasecmp(ptr, "HTTP/", 5) == 0)
  1896                             setenv("REQUEST_VERSION", ptr, 1);
  1897                         else
  1898                             ptr = NULL;  // fail below.
  1899                     } // if
  1900                 } // if
  1901                 seenresponse = 1;
  1902             } // else
  1903 
  1904             if (ptr == NULL)
  1905                 return "Bogus request from client.";
  1906 
  1907             br = 0;
  1908         } // if
  1909         else
  1910         {
  1911             br++;
  1912             if (br >= sizeof (buf))
  1913                 return "Buffer overflow.";
  1914         } // else
  1915     } // while
  1916 
  1917     if (remoteaddr[0])
  1918         setenv("REMOTE_ADDR", remoteaddr, 1);
  1919 
  1920     debugEcho("done parsing request headers");
  1921     return NULL;
  1922 } // readClientHeaders
  1923 
  1924 
  1925 static void daemonChildSig(int sig)
  1926 {
  1927     debugEcho("caught signal #%d!", sig);
  1928     terminate();
  1929 } // daemonChildSig
  1930 
  1931 
  1932 static inline void daemonChild(const int fd, const struct sockaddr *addr,
  1933                                int argc, char **argv)
  1934 {
  1935     // try to clean up in most fatal cases.
  1936     signal(SIGHUP, daemonChildSig);
  1937     signal(SIGINT, daemonChildSig);
  1938     signal(SIGTERM, daemonChildSig);
  1939     signal(SIGPIPE, daemonChildSig);
  1940     signal(SIGQUIT, daemonChildSig);
  1941     signal(SIGTRAP, daemonChildSig);
  1942     signal(SIGABRT, daemonChildSig);
  1943     signal(SIGBUS, daemonChildSig);
  1944     signal(SIGSEGV, daemonChildSig);
  1945 
  1946     GSocket = fd;
  1947 
  1948     debugEcho("New child running to handle incoming request.");
  1949 
  1950     if (readClientHeaders(GSocket, addr) == NULL)  // NULL == no error.
  1951         serverMainline(argc, argv, environ);
  1952 
  1953     terminate();
  1954 } // daemonChild
  1955 
  1956 
  1957 #if !GLISTENDAEMONIZE
  1958 #define daemonToBackground()
  1959 #else
  1960 static void daemonToBackground(void)
  1961 {
  1962     const pid_t backpid = fork();
  1963     if (backpid > 0)  // parent.
  1964         exit(0);
  1965 
  1966     else if (backpid == -1)
  1967     {
  1968         fprintf(stderr, "Failed to fork(): %s\n", strerror(errno));
  1969         exit(1);
  1970     } // if
  1971 
  1972     // we're the child. Welcome to the background.
  1973     fclose(stdin);
  1974     fclose(stdout);
  1975     fclose(stderr);
  1976     stdin = stderr = stdout = NULL;
  1977     chdir("/");
  1978     setsid();
  1979 }
  1980 #endif
  1981 
  1982 
  1983 static int daemonListenSocket(void)
  1984 {
  1985     struct addrinfo hints;
  1986     memset(&hints, '\0', sizeof (hints));
  1987     hints.ai_family = GLISTENFAMILY;
  1988     hints.ai_socktype = SOCK_STREAM;
  1989     hints.ai_flags = AI_NUMERICSERV | AI_V4MAPPED | AI_ALL | AI_ADDRCONFIG | AI_PASSIVE;
  1990 
  1991     int rc = -1;
  1992     struct addrinfo *dns = NULL;
  1993     if ((rc = getaddrinfo(GLISTENADDR, GLISTENPORTSTR, &hints, &dns)) != 0)
  1994     {
  1995         if (stderr != NULL)
  1996             fprintf(stderr, "getaddrinfo failure: %s\n", gai_strerror(rc));
  1997         return -1;
  1998     } // if
  1999 
  2000     int fd = -1;
  2001     struct addrinfo *addr;
  2002     for (addr = dns; addr != NULL; addr = addr->ai_next)
  2003     {
  2004         fd = socket(addr->ai_family, addr->ai_socktype, addr->ai_protocol);
  2005         if (fd != -1)
  2006         {
  2007             int on = 1;
  2008             setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on));
  2009             if (bind(fd, addr->ai_addr, addr->ai_addrlen) == 0)
  2010             {
  2011                 if (listen(fd, 16) == 0)
  2012                     break;
  2013             } // if
  2014 
  2015             close(fd);
  2016             fd = -1;
  2017         } // if
  2018     } // for
  2019     freeaddrinfo(dns);
  2020 
  2021     if (fd == -1)
  2022     {
  2023         if (stderr != NULL)
  2024             fprintf(stderr, "Failed to bind socket.\n");
  2025     } // if
  2026 
  2027     return fd;
  2028 } // daemonListenSocket
  2029 
  2030 
  2031 static inline int daemonMainline(int argc, char **argv, char **envp)
  2032 {
  2033     signal(SIGCHLD, SIG_IGN);
  2034     daemonToBackground();
  2035 
  2036     const int fd = daemonListenSocket();
  2037     if (fd == -1)
  2038         return 2;
  2039 
  2040     while (1)  // loop forever.
  2041     {
  2042         struct sockaddr addr;
  2043         socklen_t addrlen = sizeof (addr);
  2044         const int newfd = accept(fd, &addr, &addrlen);
  2045         if (newfd != -1)
  2046         {
  2047             const pid_t pid = fork();
  2048             if (pid != 0)  // we're NOT the child.
  2049                 close(newfd);
  2050             else
  2051             {
  2052                 close(fd);
  2053                 daemonChild(newfd, &addr, argc, argv);
  2054                 terminate();  // just in case.
  2055             } // else
  2056         } // if
  2057     } // while
  2058 
  2059     return 0;
  2060 } // daemonMainline
  2061 #endif
  2062 
  2063 
  2064 int main(int argc, char **argv, char **envp)
  2065 {
  2066     #if !GLISTENPORT
  2067     GSocket = fileno(stdout);
  2068     return serverMainline(argc, argv, envp);
  2069     #else
  2070     return daemonMainline(argc, argv, envp);
  2071     #endif
  2072 } // main
  2073 
  2074 
  2075 // end of nph-offload.c ...
  2076 
  2077 
  2078 
  2079 #if GMAXDUPEDOWNLOADS > 0
  2080 
  2081 // SHA-1 code originally from ftp://ftp.funet.fi/pub/crypt/hash/sha/sha1.c
  2082 //  License: public domain.
  2083 //  I cleaned it up a little for my specific purposes. --ryan.
  2084 
  2085 /*
  2086 SHA-1 in C
  2087 By Steve Reid <steve@edmweb.com>
  2088 100% Public Domain
  2089 */
  2090 
  2091 #define rol(value, bits) (((value) << (bits)) | ((value) >> (32 - (bits))))
  2092 
  2093 /* blk0() and blk() perform the initial expand. */
  2094 /* I got the idea of expanding during the round function from SSLeay */
  2095 #if !PLATFORM_BIGENDIAN
  2096 #define blk0(i) (block->l[i] = (rol(block->l[i],24)&0xFF00FF00) \
  2097     |(rol(block->l[i],8)&0x00FF00FF))
  2098 #else
  2099 #define blk0(i) block->l[i]
  2100 #endif
  2101 #define blk(i) (block->l[i&15] = rol(block->l[(i+13)&15]^block->l[(i+8)&15] \
  2102     ^block->l[(i+2)&15]^block->l[i&15],1))
  2103 
  2104 /* (R0+R1), R2, R3, R4 are the different operations used in SHA1 */
  2105 #define R0(v,w,x,y,z,i) z+=((w&(x^y))^y)+blk0(i)+0x5A827999+rol(v,5);w=rol(w,30);
  2106 #define R1(v,w,x,y,z,i) z+=((w&(x^y))^y)+blk(i)+0x5A827999+rol(v,5);w=rol(w,30);
  2107 #define R2(v,w,x,y,z,i) z+=(w^x^y)+blk(i)+0x6ED9EBA1+rol(v,5);w=rol(w,30);
  2108 #define R3(v,w,x,y,z,i) z+=(((w|x)&y)|(w&x))+blk(i)+0x8F1BBCDC+rol(v,5);w=rol(w,30);
  2109 #define R4(v,w,x,y,z,i) z+=(w^x^y)+blk(i)+0xCA62C1D6+rol(v,5);w=rol(w,30);
  2110 
  2111 
  2112 /* Hash a single 512-bit block. This is the core of the algorithm. */
  2113 
  2114 static void Sha1_transform(uint32 state[5], const uint8 buffer[64])
  2115 {
  2116     uint32 a, b, c, d, e;
  2117     typedef union {
  2118         uint8 c[64];
  2119         uint32 l[16];
  2120     } CHAR64LONG16;
  2121     CHAR64LONG16* block;
  2122     static uint8 workspace[64];
  2123     block = (CHAR64LONG16*)workspace;
  2124     memcpy(block, buffer, 64);
  2125     /* Copy context->state[] to working vars */
  2126     a = state[0];
  2127     b = state[1];
  2128     c = state[2];
  2129     d = state[3];
  2130     e = state[4];
  2131     /* 4 rounds of 20 operations each. Loop unrolled. */
  2132     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);
  2133     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);
  2134     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);
  2135     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);
  2136     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);
  2137     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);
  2138     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);
  2139     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);
  2140     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);
  2141     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);
  2142     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);
  2143     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);
  2144     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);
  2145     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);
  2146     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);
  2147     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);
  2148     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);
  2149     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);
  2150     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);
  2151     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);
  2152     /* Add the working vars back into context.state[] */
  2153     state[0] += a;
  2154     state[1] += b;
  2155     state[2] += c;
  2156     state[3] += d;
  2157     state[4] += e;
  2158     /* Wipe variables */
  2159     a = b = c = d = e = 0;
  2160 }
  2161 
  2162 static void Sha1_init(Sha1 *context)
  2163 {
  2164     /* SHA1 initialization constants */
  2165     context->state[0] = 0x67452301;
  2166     context->state[1] = 0xEFCDAB89;
  2167     context->state[2] = 0x98BADCFE;
  2168     context->state[3] = 0x10325476;
  2169     context->state[4] = 0xC3D2E1F0;
  2170     context->count[0] = context->count[1] = 0;
  2171 }
  2172 
  2173 
  2174 /* Run your data through this. */
  2175 
  2176 static void Sha1_append(Sha1 *context, const uint8 *data, uint32 len)
  2177 {
  2178     uint32 i, j;
  2179 
  2180     j = (context->count[0] >> 3) & 63;
  2181     if ((context->count[0] += len << 3) < (len << 3)) context->count[1]++;
  2182     context->count[1] += (len >> 29);
  2183     if ((j + len) > 63) {
  2184         memcpy(&context->buffer[j], data, (i = 64-j));
  2185         Sha1_transform(context->state, context->buffer);
  2186         for ( ; i + 63 < len; i += 64) {
  2187             Sha1_transform(context->state, &data[i]);
  2188         }
  2189         j = 0;
  2190     }
  2191     else i = 0;
  2192     memcpy(&context->buffer[j], &data[i], len - i);
  2193 }
  2194 
  2195 
  2196 /* Add padding and return the message digest. */
  2197 
  2198 static void Sha1_finish(Sha1 *context, uint8 digest[20])
  2199 {
  2200     uint32 i, j;
  2201     uint8 finalcount[8];
  2202 
  2203     for (i = 0; i < 8; i++) {
  2204         finalcount[i] = (uint8)((context->count[(i >= 4 ? 0 : 1)]
  2205          >> ((3-(i & 3)) * 8) ) & 255);  /* Endian independent */
  2206     }
  2207     Sha1_append(context, (uint8 *)"\200", 1);
  2208     while ((context->count[0] & 504) != 448) {
  2209         Sha1_append(context, (uint8 *)"\0", 1);
  2210     }
  2211     Sha1_append(context, finalcount, 8);  /* Should cause a Sha1_transform() */
  2212     for (i = 0; i < 20; i++) {
  2213         digest[i] = (uint8)
  2214          ((context->state[i>>2] >> ((3-(i & 3)) * 8) ) & 255);
  2215     }
  2216     /* Wipe variables */
  2217     i = j = 0;
  2218     memset(context->buffer, 0, 64);
  2219     memset(context->state, 0, 20);
  2220     memset(context->count, 0, 8);
  2221     memset(&finalcount, 0, 8);
  2222     Sha1_transform(context->state, context->buffer);
  2223 }
  2224 
  2225 #endif
  2226