nph-offload.c
author Ryan C. Gordon <icculus@icculus.org>
Mon, 21 Jan 2013 04:07:39 -0500
changeset 139 59d83397bad9
parent 136 d914c6063504
permissions -rw-r--r--
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
     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 
   129 #define GVERSION "1.1.6"
   130 #define GSERVERSTRING "nph-offload.c/" GVERSION
   131 
   132 #include "offload_server_config.h"
   133 
   134 #define OFFLOAD_NUMSTR2(x) #x
   135 #define OFFLOAD_NUMSTR(x) OFFLOAD_NUMSTR2(x)
   136 
   137 #define GBASESERVERPORTSTR OFFLOAD_NUMSTR(GBASESERVERPORT)
   138 
   139 #ifdef __GNUC__
   140 #define ISPRINTF(x,y) __attribute__((format (printf, x, y)))
   141 #else
   142 #define ISPRINTF(x,y)
   143 #endif
   144 
   145 #ifdef max
   146 #undef max
   147 #endif
   148 
   149 // some getaddrinfo() flags that may not exist...
   150 #ifndef AI_ALL
   151 #define AI_ALL 0
   152 #endif
   153 #ifndef AI_ADDRCONFIG
   154 #define AI_ADDRCONFIG 0
   155 #endif
   156 #ifndef AI_NUMERICSERV
   157 #define AI_NUMERICSERV 0
   158 #endif
   159 #ifndef AI_V4MAPPED
   160 #define AI_V4MAPPED 0
   161 #endif
   162 
   163 typedef int8_t int8;
   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;
   171 
   172 extern char **environ;
   173 
   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;
   188 
   189 #if !GNOCACHE
   190 static char *GMetaDataPath = NULL;
   191 #endif
   192 
   193 
   194 static void failure_location(const char *, const char *, const char *);
   195 static inline void failure(const char *httperr, const char *errmsg)
   196 {
   197     failure_location(httperr, errmsg, NULL);
   198 } // failure
   199 
   200 
   201 #if ( ((GDEBUG) && (GDEBUGTOFILE)) == 0 )
   202 #define getDebugFilePointer() (NULL)
   203 #else
   204 static FILE *getDebugFilePointer(void)
   205 {
   206     if (GDebugFilePointer == NULL)
   207     {
   208         char buf[PATH_MAX];
   209         snprintf(buf, sizeof(buf), GDEBUGDIR "/debug-%d", (int) getpid());
   210         GDebugFilePointer = fopen(buf, "a");
   211     } // if
   212     return GDebugFilePointer;
   213 } // getDebugFilePointer
   214 #endif
   215 
   216 
   217 #if ((!GDEBUG) && defined(__GNUC__))
   218 #define debugEcho(fmt, ...) do {} while (0)
   219 #else
   220 static void debugEcho(const char *fmt, ...) ISPRINTF(1, 2);
   221 static void debugEcho(const char *fmt, ...)
   222 {
   223     #if GDEBUG
   224         #if !GDEBUGTOFILE
   225         FILE *fp = stdout;
   226         #else
   227         FILE *fp = getDebugFilePointer();
   228         #endif
   229         if (fp != NULL)
   230         {
   231             if (GIsCacheProcess)
   232                 fputs("(cache process) ", fp);
   233 
   234             va_list ap;
   235             va_start(ap, fmt);
   236             vfprintf(fp, fmt, ap);
   237             va_end(ap);
   238             fputs("\n", fp);
   239             fflush(fp);
   240         } // else
   241     #endif
   242 } // debugEcho
   243 #endif
   244 
   245 
   246 static void *createSemaphore(const int initialVal)
   247 {
   248     void *retval = NULL;
   249     const int value = initialVal ? 0 : 1;
   250     int created = 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         created = 0;
   256         debugEcho("(semaphore already exists, just opening existing one.)");
   257         retval = sem_open("SEM-" SHM_NAME, 0);
   258     } // if
   259 
   260     if (retval == (void *) SEM_FAILED)
   261         return NULL;
   262 
   263     return retval;
   264 } // createSemaphore
   265 
   266 
   267 static void getSemaphore(void)
   268 {
   269     debugEcho("grabbing semaphore...(owned %d time(s).)", GSemaphoreOwned);
   270     if (GSemaphoreOwned++ > 0)
   271         return;
   272 
   273     if (GSemaphore != NULL)
   274     {
   275         if (sem_wait(GSemaphore) == -1)
   276             failure("503 Service Unavailable", "Couldn't lock semaphore.");
   277     } // if
   278     else
   279     {
   280         debugEcho("(have to create semaphore...)");
   281         GSemaphore = createSemaphore(0);
   282         if (GSemaphore == NULL)
   283             failure("503 Service Unavailable", "Couldn't allocate semaphore.");
   284     } // else
   285 } // getSemaphore
   286 
   287 
   288 static void putSemaphore(void)
   289 {
   290     if (GSemaphoreOwned == 0)
   291         return;
   292 
   293     if (--GSemaphoreOwned == 0)
   294     {
   295         if (GSemaphore != NULL)
   296         {
   297             if (sem_post(GSemaphore) == -1)
   298                 failure("503 Service Unavailable", "Couldn't unlock semaphore.");
   299         } // if
   300     } // if
   301     debugEcho("released semaphore...(now owned %d time(s).)", GSemaphoreOwned);
   302 } // putSemaphore
   303 
   304 
   305 static inline int process_dead(const pid_t pid)
   306 {
   307     return ( (pid <= 0) || ((kill(pid, 0) == -1) && (errno == ESRCH)) );
   308 } // process_dead
   309 
   310 
   311 #if GMAXDUPEDOWNLOADS <= 0
   312 #define setDownloadRecord()
   313 #define removeDownloadRecord()
   314 #else
   315 
   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
   322 //  anyhow.   :)
   323 #define MAX_DOWNLOAD_RECORDS 512
   324 
   325 typedef struct
   326 {
   327     pid_t pid;
   328     uint8 sha1[20];
   329 } DownloadRecord;
   330 
   331 static DownloadRecord *GAllDownloads = NULL;
   332 static DownloadRecord *GMyDownload = NULL;
   333 
   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" \
   338 
   339 typedef struct
   340 {
   341     uint32 state[5];
   342     uint32 count[2];
   343     uint8 buffer[64];
   344 } Sha1;
   345 
   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]);
   349 
   350 static void setDownloadRecord()
   351 {
   352     const pid_t mypid = getpid();
   353     int dupes = 0;
   354     int i = 0;
   355     int fd = -1;
   356     Sha1 sha1data;
   357     uint8 sha1[20];
   358     DownloadRecord *downloads = NULL;
   359     const size_t maplen = sizeof (DownloadRecord) * MAX_DOWNLOAD_RECORDS;
   360     if (GRemoteAddr == NULL)
   361         return;  // oh well.
   362 
   363     GAllDownloads = GMyDownload = NULL;
   364 
   365     getSemaphore();
   366 
   367     fd = shm_open("/" SHM_NAME, (O_CREAT|O_EXCL|O_RDWR), (S_IREAD|S_IWRITE));
   368     if (fd < 0)
   369     {
   370         fd = shm_open("/" SHM_NAME, (O_CREAT|O_RDWR),(S_IREAD|S_IWRITE));
   371         if (fd < 0)
   372         {
   373             putSemaphore();
   374             debugEcho("shm_open() failed: %s", strerror(errno));
   375             return;  // oh well.
   376         } // if
   377     } // if
   378 
   379     ftruncate(fd, maplen);
   380 
   381     void *ptr = mmap(0, maplen, (PROT_READ|PROT_WRITE), MAP_SHARED, fd, 0);
   382     close(fd);  // mapping remains.
   383     if (ptr == MAP_FAILED)
   384     {
   385         putSemaphore();
   386         debugEcho("mmap() failed: %s", strerror(errno));
   387         return;
   388     } // if
   389 
   390     GAllDownloads = downloads = (DownloadRecord *) ptr;
   391 
   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);
   396 
   397     for (i = 0; i < MAX_DOWNLOAD_RECORDS; i++, downloads++)
   398     {
   399         const pid_t pid = downloads->pid;
   400 
   401         if (pid <= 0)  // unused slot.
   402             GMyDownload = downloads;  // take slot.
   403 
   404         else if (memcmp(downloads->sha1, sha1, sizeof (sha1)) == 0)
   405         {
   406             // make sure this isn't a killed process.
   407             if ( (pid == mypid) || (process_dead(pid)) )
   408             {
   409                 debugEcho("pid #%d died at some point.", (int) pid);
   410                 downloads->pid = 0;
   411                 GMyDownload = downloads;   // take slot.
   412             } // if
   413             else
   414             {
   415                 debugEcho("pid #%d still alive, dupe slot.", (int) pid);
   416                 dupes++;
   417             } // else
   418         } // else if
   419     } // for
   420 
   421     debugEcho("Saw %d dupes.", dupes);
   422 
   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.");
   427     else
   428     {
   429         debugEcho("Got download slot #%d", (int) (GMyDownload-GAllDownloads));
   430         GMyDownload->pid = mypid;
   431         memcpy(GMyDownload->sha1, sha1, sizeof (sha1));
   432     } // else
   433 
   434     putSemaphore();
   435 } // setDownloadRecord
   436 
   437 
   438 static void removeDownloadRecord()
   439 {
   440     if (!GAllDownloads)
   441         return;
   442 
   443     getSemaphore();
   444     if (GMyDownload != NULL)
   445         GMyDownload->pid = 0;
   446     putSemaphore();
   447     munmap(GAllDownloads, sizeof (DownloadRecord) * MAX_DOWNLOAD_RECORDS);
   448 
   449     GAllDownloads = GMyDownload = NULL;
   450 } // removeDownloadRecord
   451 #endif
   452 
   453 // strftime()'s "%a" gives you locale-dependent strings...
   454 static const char *GWeekday[] = {
   455     "Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat",
   456 };
   457 
   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",
   462 };
   463 
   464 static void make_date_header(char *buf, const size_t buflen)
   465 {
   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
   472 
   473 
   474 #if GDEBUG
   475 static void printf_date_header(FILE *out)
   476 {
   477     char buf[128];
   478     if (out == NULL)
   479         return;
   480     make_date_header(buf, sizeof (buf));
   481     fprintf(out, "%s", buf);
   482 } // printf_date_header
   483 #endif
   484 
   485 
   486 static void terminate(void);
   487 
   488 static void write_string(const int fd, const char *str)
   489 {
   490     size_t avail = strlen(str);
   491     while (avail > 0)
   492     {
   493         ssize_t rc = write(fd, str, avail);
   494         if ((rc == -1) && (errno == EINTR))
   495             continue;
   496 
   497         if (rc <= 0)
   498         {
   499             debugEcho("write_string(): write() failed! (%s)\n", strerror(errno));
   500             terminate();
   501         } // if
   502 
   503         avail -= rc;
   504         str += rc;
   505     } // while
   506 } // write_string
   507 
   508 
   509 static void write_header(const char *key, const char *val)
   510 {
   511     write_string(GSocket, key);
   512     write_string(GSocket, val);
   513     write_string(GSocket, "\r\n");
   514 } // write_header
   515 
   516 
   517 static void write_date_header(void)
   518 {
   519     char buf[128];
   520     make_date_header(buf, sizeof (buf));
   521     write_string(GSocket, buf);
   522 } // write_date_header
   523 
   524 
   525 static int64 atoi64(const char *str)
   526 {
   527     int64 retval = 0;
   528     int64 mult = 1;
   529     int i = 0;
   530 
   531     while (*str == ' ')
   532         str++;
   533 
   534     if (*str == '-')
   535     {
   536         mult = -1;
   537         str++;
   538     } // if
   539 
   540     while (1)
   541     {
   542         const char ch = str[i];
   543         if ((ch < '0') || (ch > '9'))
   544             break;
   545         i++;
   546     } // for
   547 
   548     while (--i >= 0)
   549     {
   550         const char ch = str[i];
   551         retval += ((int64) (ch - '0')) * mult;
   552         mult *= 10;
   553     } // while
   554 
   555     return retval;
   556 } // atoi64
   557 
   558 
   559 static void *xmalloc(const size_t len)
   560 {
   561     void *ptr = malloc(len);
   562     if (ptr == NULL)
   563         failure("500 Internal Server Error", "Out of memory.");
   564     return ptr;
   565 } // xmalloc
   566 
   567 static char *xstrdup(const char *str)
   568 {
   569     char *ptr = (char *) xmalloc(strlen(str) + 1);
   570     strcpy(ptr, str);
   571     return ptr;
   572 } // xstrdup
   573 
   574 
   575 static char *makeStr(const char *fmt, ...) ISPRINTF(1, 2);
   576 static char *makeStr(const char *fmt, ...)
   577 {
   578     va_list ap;
   579 
   580     char ch;
   581     va_start(ap, fmt);
   582     const int len = vsnprintf(&ch, 1, fmt, ap);
   583     va_end(ap);
   584 
   585     char *retval = (char *) xmalloc(len + 1);
   586     va_start(ap, fmt);
   587     vsnprintf(retval, len + 1, fmt, ap);
   588     va_end(ap);
   589 
   590     return retval;
   591 } // makeStr
   592 
   593 
   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.
   597 typedef struct list
   598 {
   599     const char *key;
   600     const char *value;
   601     struct list *next;
   602 } list;
   603 
   604 static const char *listSet(list **l, const char *key, const char *value)
   605 {
   606     // maybe substring of current item, so copy it before we free() anything.
   607     const char *newvalue = xstrdup(value);
   608 
   609     list *item = *l;
   610     while (item)
   611     {
   612         if (strcmp(item->key, key) == 0)
   613             break;
   614         item = item->next;
   615     } // while
   616 
   617     if (item != NULL)
   618         free((void *) item->value);
   619     else
   620     {
   621         item = (list *) xmalloc(sizeof (list));
   622         item->key = xstrdup(key);
   623         item->next = *l;
   624         *l = item;
   625     } // else
   626 
   627     item->value = newvalue;
   628     return newvalue;
   629 } // listSet
   630 
   631 
   632 static const char *listFind(const list *l, const char *key)
   633 {
   634     const list *item = l;
   635     while (item)
   636     {
   637         if (strcmp(item->key, key) == 0)
   638             break;
   639         item = item->next;
   640     } // while
   641     return item ? item->value : NULL;
   642 } // listFind
   643 
   644 
   645 static void listFree(list **l)
   646 {
   647     list *item = *l;
   648     while (item)
   649     {
   650         list *next = item->next;
   651         free((void *) item->key);
   652         free((void *) item->value);
   653         free(item);
   654         item = next;
   655     } // while
   656 
   657     *l = NULL;
   658 } // listFree
   659 
   660 
   661 #if GSETPROCTITLE
   662     #if defined(__linux__)
   663         // okay.
   664     #else
   665         #warning GSETPROCTITLE not currently supported on this platform.
   666         #undef GSETPROCTITLE
   667         #define GSETPROCTITLE 0
   668     #endif
   669 #endif
   670 
   671 #if !GSETPROCTITLE
   672 #define copyEnv(x) getenv(x)
   673 #define freeEnvCopies()
   674 #else
   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)
   681 {
   682     const char *retval = listFind(GEnvCopies, key);
   683     if ((retval == NULL) && (!GNoMoreGetEnv))
   684     {
   685         const char *envr = getenv(key);
   686         if (envr != NULL)
   687             retval = listSet(&GEnvCopies, key, envr);
   688     } // if
   689     return retval;
   690 } // copyEnv
   691 
   692 static inline void freeEnvCopies(void)
   693 {
   694     listFree(&GEnvCopies);
   695 } // freeEnvCopies
   696 #endif
   697 
   698 
   699 #if !GLOGACTIVITY
   700 #define outputLogEntry()
   701 #else
   702 static void outputLogEntry(void)
   703 {
   704     FILE *out = fopen(GLOGFILE, "a");
   705     if (out == NULL)
   706         debugEcho("Failed to open log file for append!");
   707     else
   708     {
   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);
   714         fprintf(out,
   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 : "",
   723             Guri ? Guri : "",
   724             (GReqVersion && *GReqVersion) ? " " : "",
   725             GReqVersion ? GReqVersion : "",
   726             GHttpStatus, (long long) GBytesSent,
   727             GReferer ? GReferer : "-",
   728             GUserAgent ? GUserAgent : "-");
   729         fclose(out);
   730     } // else
   731 } // outputLogEntry
   732 #endif
   733 
   734 
   735 static void terminate(void)
   736 {
   737     if (!GIsCacheProcess)
   738     {
   739         debugEcho("offload program is terminating...");
   740         removeDownloadRecord();
   741         outputLogEntry();
   742         while (GSemaphoreOwned > 0)
   743             putSemaphore();
   744     } // if
   745 
   746     if (GDebugFilePointer != NULL)
   747         fclose(GDebugFilePointer);
   748 
   749     #if GLISTENPORT
   750     char ch = 0;
   751     shutdown(GSocket, SHUT_RDWR);
   752     while (recv(GSocket, &ch, sizeof (ch), 0) > 0) {}
   753     close(GSocket);
   754     #endif
   755 
   756     if (stdin) fclose(stdin);
   757     if (stdout) fclose(stdout);
   758     if (stderr) fclose(stderr);
   759     stdin = stdout = stderr = NULL;
   760 
   761     freeEnvCopies();
   762 
   763     exit(0);
   764 } // terminate
   765 
   766 
   767 static void failure_location(const char *httperr, const char *errmsg,
   768                              const char *location)
   769 {
   770     if (strncasecmp(httperr, "HTTP", 4) == 0)
   771     {
   772         const char *ptr = strchr(httperr, ' ');
   773         if (ptr != NULL)
   774             httperr = ptr+1;
   775     } // if
   776 
   777     if (!GHttpStatus)
   778         GHttpStatus = atoi(httperr);
   779 
   780     debugEcho("failure() called:");
   781     debugEcho("  %s", httperr);
   782     debugEcho("  %s", errmsg);
   783 
   784     if (GSocket != -1)
   785     {
   786         write_header("HTTP/1.1 ", httperr);
   787         write_header("Status: ", httperr);
   788         write_header("Server: ", httperr);
   789         write_date_header();
   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;
   797     } // if
   798 
   799     terminate();
   800 } // failure_location
   801 
   802 
   803 static int invalidContentRange(const int64 startRange, const int64 endRange,
   804                                const int64 max)
   805 {
   806     if ((startRange < 0) || (startRange >= max))
   807         return 1;
   808     else if ((endRange < 0) || (endRange >= max))
   809         return 1;
   810     else if (startRange > endRange)
   811         return 1;
   812     return 0;
   813 } // invalidContentRange
   814 
   815 
   816 #if !GDEBUG
   817 #define debugInit(argc, argv, envp)
   818 #else
   819 static void debugInit(int argc, char **argv, char **envp)
   820 {
   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");
   825     write_date_header();
   826     write_header("Server: ", GSERVERSTRING);
   827     write_header("Connection: ", "close");
   828     write_header("", "");
   829     GHttpStatus = 200;
   830     #endif
   831 
   832     debugEcho("%s", "");
   833     debugEcho("%s", "");
   834     debugEcho("%s", "");
   835     debugEcho("Offload Debug Run!");
   836     debugEcho("%s", "");
   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());
   848     debugEcho("%s", "");
   849     debugEcho("%s", "");
   850 
   851     int i;
   852     debugEcho("Command line: %d items...", argc);
   853     for (i = 0; i < argc; i++)
   854         debugEcho(" argv[%d] = '%s'", i, argv[i]);
   855     debugEcho("%s", "");
   856     debugEcho("%s", "");
   857     debugEcho("Environment...");
   858     for (i = 0; envp[i]; i++)
   859         debugEcho(" %s", envp[i]);
   860     debugEcho("%s", "");
   861     debugEcho("%s", "");
   862 } // debugInit
   863 #endif
   864 
   865 
   866 static void readHeaders(const int fd, list **headers)
   867 {
   868     const time_t endtime = time(NULL) + GTIMEOUT;
   869     int br = 0;
   870     char buf[1024];
   871     int seenresponse = 0;
   872     while (1)
   873     {
   874         const time_t now = time(NULL);
   875         int rc = -1;
   876         fd_set rfds;
   877 
   878         if (endtime >= now)
   879         {
   880             struct timeval tv;
   881             FD_ZERO(&rfds);
   882             FD_SET(fd, &rfds);
   883             tv.tv_sec = endtime - now;
   884             tv.tv_usec = 0;
   885             rc = select(fd+1, &rfds, NULL, NULL, &tv);
   886         } // if
   887 
   888         if ((rc <= 0) || (FD_ISSET(fd, &rfds) == 0))
   889             failure("503 Service Unavailable", "Timeout while talking to offload host.");
   890 
   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.");
   895 
   896         if (buf[br] == '\r')
   897             ;  // ignore these.
   898         else if (buf[br] == '\n')
   899         {
   900             char *ptr = NULL;
   901             if (br == 0)  // empty line, end of headers.
   902                 return;
   903             buf[br] = '\0';
   904             if (seenresponse)
   905             {
   906                 ptr = strchr(buf, ':');
   907                 if (ptr != NULL)
   908                 {
   909                     *(ptr++) = '\0';
   910                     while (*ptr == ' ')
   911                         ptr++;
   912                     listSet(headers, buf, ptr);
   913                 } // if
   914             } // if
   915             else
   916             {
   917                 listSet(headers, "response", buf);
   918                 if (strncasecmp(buf, "HTTP/", 5) == 0)
   919                 {
   920                     ptr = strchr(buf + 5, ' ');
   921                     if (ptr != NULL)
   922                     {
   923                         char *start = ptr + 1;
   924                         ptr = strchr(start, ' ');
   925                         if (ptr != NULL)
   926                             *ptr = '\0';
   927                         listSet(headers, "response_code", start);
   928                         ptr = start;
   929                     } // if
   930                 } // if
   931                 seenresponse = 1;
   932             } // else
   933 
   934             if (ptr == NULL)
   935                 failure("503 Service Unavailable", "Bogus response from offload host server.");
   936 
   937             br = 0;
   938         } // if
   939         else
   940         {
   941             br++;
   942             if (br >= sizeof (buf))
   943                 failure("503 Service Unavailable", "Buffer overflow.");
   944         } // else
   945     } // while
   946 } // readHeaders
   947 
   948 
   949 static void doWrite(const int fd, const char *str)
   950 {
   951     const int len = strlen(str);
   952     int bw = 0;
   953     const time_t endtime = time(NULL) + GTIMEOUT;
   954     while (bw < len)
   955     {
   956         const time_t now = time(NULL);
   957         int rc = -1;
   958         fd_set wfds;
   959 
   960         if (endtime >= now)
   961         {
   962             struct timeval tv;
   963             FD_ZERO(&wfds);
   964             FD_SET(fd, &wfds);
   965             tv.tv_sec = endtime - now;
   966             tv.tv_usec = 0;
   967             rc = select(fd+1, NULL, &wfds, NULL, &tv);
   968         } // if
   969 
   970         if ((rc <= 0) || (FD_ISSET(fd, &wfds) == 0))
   971             failure("503 Service Unavailable", "Timeout while talking to offload base server.");
   972 
   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.");
   976         bw += rc;
   977     } // while
   978 } // doWrite
   979 
   980 
   981 static int doHttp(const char *method, list **headers)
   982 {
   983     int rc = -1;
   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;
   989 
   990     struct addrinfo *dns = NULL;
   991     if ((rc = getaddrinfo(GBASESERVERIP, GBASESERVERPORTSTR, &hints, &dns)) != 0)
   992     {
   993         debugEcho("getaddrinfo failure: %s", gai_strerror(rc));
   994         failure("503 Service Unavailable", "Offload base server hostname lookup failure.");
   995     } // if
   996 
   997     int fd = -1;
   998     struct addrinfo *addr;
   999     for (addr = dns; addr != NULL; addr = addr->ai_next)
  1000     {
  1001         fd = socket(addr->ai_family, addr->ai_socktype, addr->ai_protocol);
  1002         if (fd != -1)
  1003         {
  1004             if (connect(fd, addr->ai_addr, addr->ai_addrlen) == 0)
  1005                 break;
  1006             close(fd);
  1007             fd = -1;
  1008         } // if
  1009     } // for
  1010     freeaddrinfo(dns);
  1011 
  1012     if (fd == -1)
  1013         failure("503 Service Unavailable", "Couldn't connect to offload base server.");
  1014 
  1015     doWrite(fd, method);
  1016     doWrite(fd, " ");
  1017     doWrite(fd, Guri);
  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);
  1025     return fd;
  1026 } // doHttp
  1027 
  1028 
  1029 static void http_head(list **head)
  1030 {
  1031     const int fd = doHttp("HEAD", head);
  1032     if (fd != -1)
  1033         close(fd);
  1034 } // http_head
  1035 
  1036 static const char *makeNum(int64 num)
  1037 {
  1038     static char buf[64];
  1039     snprintf(buf, sizeof (buf), "%lld", (long long) num);
  1040     return buf;
  1041 } // makeNum
  1042 
  1043 
  1044 
  1045 #if !GNOCACHE
  1046 static int http_get(list **head)
  1047 {
  1048     list *headers = NULL;
  1049     const int fd = doHttp("GET", &headers);
  1050 
  1051     if ((head == NULL) || (fd == -1))
  1052         listFree(&headers);
  1053 
  1054     if (head != NULL)
  1055         *head = headers;
  1056     return fd;
  1057 } // http_get
  1058 
  1059 
  1060 static list *loadMetadata(const char *fname)
  1061 {
  1062     list *retval = NULL;
  1063     struct stat statbuf;
  1064     int fd = open(fname, O_RDONLY);
  1065     if (fd == -1)
  1066         return NULL;
  1067 
  1068     if (fstat(fd, &statbuf) == -1)
  1069     {
  1070         close(fd);
  1071         return NULL;
  1072     } // if
  1073 
  1074     char *buf = (char *) xmalloc(statbuf.st_size + 1);
  1075     if (read(fd, buf, statbuf.st_size) != statbuf.st_size)
  1076     {
  1077         free(buf);
  1078         close(fd);
  1079         return NULL;
  1080     } // if
  1081 
  1082     buf[statbuf.st_size] = '\0';
  1083     close(fd);
  1084 
  1085     char *ptr = buf;
  1086     int total = 0;
  1087     while (1)
  1088     {
  1089         char *key = ptr;
  1090         ptr = strchr(ptr, '\n');
  1091         if (ptr == NULL)
  1092             break;
  1093         *(ptr++) = '\0';
  1094         char *value = ptr;
  1095         ptr = strchr(ptr, '\n');
  1096         if (ptr == NULL)
  1097             break;
  1098         *(ptr++) = '\0';
  1099         if (*key != '\0')
  1100             listSet(&retval, key, value);
  1101         debugEcho("Loaded metadata '%s' => '%s'", key, value);
  1102         total++;
  1103     } // while
  1104 
  1105     free(buf);
  1106     debugEcho("Loaded %d metadata pair(s).", total);
  1107 
  1108     return retval;
  1109 } // loadMetadata
  1110 
  1111 
  1112 static int cachedMetadataMostRecent(const list *metadata, const list *head)
  1113 {
  1114     const char *contentlength = listFind(metadata, "Content-Length");
  1115     if (!contentlength)
  1116         return 0;
  1117 
  1118     const char *etag = listFind(metadata, "ETag");
  1119     if (!etag)
  1120         return 0;
  1121 
  1122     const char *lastmodified = listFind(metadata, "Last-Modified");
  1123     if (!lastmodified)
  1124         return 0;
  1125 
  1126     if (strcmp(contentlength, listFind(head, "Content-Length")) != 0)
  1127         return 0;
  1128 
  1129     if (strcmp(etag, listFind(head, "ETag")) != 0)
  1130         return 0;
  1131 
  1132     if (strcmp(lastmodified, listFind(head, "Last-Modified")) != 0)
  1133     {
  1134         const char *isweak = listFind(metadata, "X-Offload-Is-Weak");
  1135         if ( (!isweak) || (strcmp(isweak, "0") != 0) )
  1136             return 0;
  1137     } // if
  1138 
  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)
  1144         return 0;
  1145 
  1146     const int64 fsize = statbuf.st_size;
  1147     if (fsize != atoi64(contentlength))
  1148     {
  1149         // whoa, we were supposed to cache this!
  1150         const char *cacher = listFind(metadata, "X-Offload-Caching-PID");
  1151         if (!cacher)
  1152             return 0;
  1153 
  1154         const int cacherpid = atoi(cacher);
  1155         if (process_dead(cacherpid))
  1156         {
  1157             debugEcho("Caching process ID died!");
  1158             return 0;
  1159         } // if
  1160     } // if
  1161 
  1162     return 1;
  1163 } // cachedMetadataMostRecent
  1164 
  1165 
  1166 static void nukeRequestFromCache(void)
  1167 {
  1168     debugEcho("Nuking request from cache...");
  1169     getSemaphore();
  1170     if (GMetaDataPath != NULL)
  1171         unlink(GMetaDataPath);
  1172     if (GFilePath != NULL)
  1173         unlink(GFilePath);
  1174     putSemaphore();
  1175 } // nukeRequestFromCache
  1176 
  1177 
  1178 static char *etagToCacheFname(const char *etag)
  1179 {
  1180     static const char chs[] = { ' ', '\t', 0x0B, '\"', '\'' };
  1181     char *retval = xstrdup(etag);
  1182     int i, j;
  1183 
  1184     for (i = 0; retval[i]; i++)
  1185     {
  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;
  1190         if (j == total)
  1191             break;
  1192     } // for
  1193 
  1194     if (i != 0)
  1195         memmove(retval, retval + i, strlen(retval + i) + 1);
  1196 
  1197     for (i = strlen(retval) - 1; i >= 0; i--)
  1198     {
  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;
  1203         if (j == total)
  1204             break;
  1205     } // for
  1206 
  1207     retval[i+1] = '\0';
  1208 
  1209     return retval;
  1210 } // etagToCacheFname
  1211 
  1212 
  1213 static int selectReadable(const int fd)
  1214 {
  1215     const time_t endtime = time(NULL) + GTIMEOUT;
  1216     fd_set rfds;
  1217     int rc = 0;
  1218 
  1219     while (1)
  1220     {
  1221         struct timeval tv;
  1222         const time_t now = time(NULL);
  1223 
  1224         if (endtime >= now)
  1225         {
  1226             FD_ZERO(&rfds);
  1227             FD_SET(fd, &rfds);
  1228             tv.tv_sec = endtime - now;
  1229             tv.tv_usec = 0;
  1230             rc = select(fd+1, &rfds, NULL, NULL, &tv);
  1231             if ((rc < 0) && (errno == EINTR))
  1232                 continue;   // just try again with adjusted timeout.
  1233         } // if
  1234 
  1235         break;
  1236     } // while
  1237 
  1238     if ((rc <= 0) || (FD_ISSET(fd, &rfds) == 0))
  1239     {
  1240         debugEcho("select() failed");
  1241         return 0;
  1242     } // if
  1243 
  1244     return 1;
  1245 } // selectReadable
  1246 
  1247 
  1248 static void cacheFailure(const char *err)
  1249 {
  1250     debugEcho("%s", err);
  1251     nukeRequestFromCache();
  1252     terminate();
  1253 } // cacheFailure
  1254 
  1255 
  1256 static void cacheProcessSig(int sig)
  1257 {
  1258     char errbuf[128];
  1259     snprintf(errbuf, sizeof (errbuf), "caught signal #%d!", sig);
  1260     cacheFailure(errbuf);
  1261 } // cacheProcessSig
  1262 
  1263 
  1264 static inline int64 Min(const int64 a, const int64 b)
  1265 {
  1266     return (a < b) ? a : b;
  1267 } // Min
  1268 
  1269 
  1270 static pid_t cacheFork(const int sock, FILE *cacheio, const int64 max)
  1271 {
  1272     debugEcho("Cache needs refresh...pulling from base server...");
  1273 
  1274     const pid_t pid = fork();
  1275 
  1276     if (pid != 0)  // don't need these any more...
  1277     {
  1278         fclose(cacheio);
  1279         close(sock);
  1280     } // if
  1281 
  1282     if (pid == -1)  // failed!
  1283     {
  1284         nukeRequestFromCache();
  1285         failure("500 Internal Server Error", "Couldn't fork for caching.");
  1286         return pid;
  1287     } // if
  1288 
  1289     else if (pid != 0)  // we're the parent.
  1290     {
  1291         debugEcho("fork()'d caching process! new pid is (%d).", (int) pid);
  1292         return pid;
  1293     } // else if
  1294 
  1295     // we're the child.
  1296     GIsCacheProcess = 1;
  1297     debugEcho("caching process (%d) starting up!", (int) getpid());
  1298 
  1299     #if GMAXDUPEDOWNLOADS > 0
  1300     if (GAllDownloads != NULL)
  1301         munmap(GAllDownloads, sizeof (DownloadRecord) * MAX_DOWNLOAD_RECORDS);
  1302     GAllDownloads = GMyDownload = NULL;
  1303     #endif
  1304 
  1305     #if GLISTENPORT
  1306     if (GSocket != -1)
  1307     {
  1308         close(GSocket);
  1309         GSocket = -1;
  1310     } // if
  1311     #endif
  1312 
  1313     if (stdin) fclose(stdin);
  1314     if (stdout) fclose(stdout);
  1315     if (stderr) fclose(stderr);
  1316     stdin = stdout = stderr = NULL;
  1317 
  1318     chdir("/");
  1319     setsid();
  1320 
  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);
  1331 
  1332     #if GSETPROCTITLE
  1333         #ifdef __linux__
  1334         {
  1335             snprintf(GArgv[0], GMaxArgvLen, "offload: %s CACHE %s", GBASESERVER, Guri);
  1336             char *p = &GArgv[0][strlen(GArgv[0])];
  1337             while(p < GLastArgv)
  1338                 *(p++) = '\0';
  1339             GArgv[1] = NULL;
  1340         }
  1341         #endif
  1342     #endif
  1343 
  1344     int64 br = 0;
  1345     while (br < max)
  1346     {
  1347         int len = 0;
  1348         char data[32 * 1024];
  1349         const int readsize = (int) Min(sizeof (data), (max - br));
  1350 
  1351         if (readsize == 0)
  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");
  1361         br += len;
  1362         debugEcho("wrote %d bytes to the cache.", len);
  1363     } // while
  1364 
  1365     if (fclose(cacheio) == EOF)
  1366         cacheFailure("fclose() failed");
  1367 
  1368     debugEcho("Successfully cached! Terminating!");
  1369     terminate();  // always die.
  1370     return -1;
  1371 } // cacheFork
  1372 #endif  // #if !GNOCACHE
  1373 
  1374 
  1375 static int serverMainline(int argc, char **argv, char **envp)
  1376 {
  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)
  1388         GReqMethod = "GET";
  1389     if (GReqVersion == NULL)
  1390         GReqVersion = "";
  1391 
  1392     debugInit(argc, argv, envp);
  1393 
  1394     if ((Guri == NULL) || (*Guri != '/'))
  1395         failure("500 Internal Server Error", "Bad request URI");
  1396 
  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: /");
  1400 
  1401     // !!! FIXME: favicon?
  1402 
  1403     #if GSETPROCTITLE
  1404         #ifdef __linux__
  1405         {
  1406             // This nastiness inspired by proftpd.
  1407             int i;
  1408             for (i = 0; i < argc; i++)
  1409             {
  1410                 if (!i || (GLastArgv + 1 == argv[i]))
  1411                     GLastArgv = argv[i] + strlen(argv[i]);
  1412             } // for
  1413             for (i = 0; envp[i] != NULL; i++)
  1414             {
  1415                 if ((GLastArgv + 1) == envp[i])
  1416                     GLastArgv = envp[i] + strlen(envp[i]);
  1417             } // for
  1418 
  1419             extern char *__progname, *__progname_full;
  1420             __progname = xstrdup("offload");
  1421             __progname_full = xstrdup(argv[0]);
  1422             GNoMoreGetEnv = 1;
  1423             static char *nullenv = NULL;
  1424             envp = environ = &nullenv;
  1425 
  1426             GArgv = argv;
  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)
  1431                 *(p++) = '\0';
  1432             GArgv[1] = NULL;
  1433         }
  1434         #endif
  1435     #endif
  1436 
  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.");
  1441 
  1442     if (!ishead)
  1443         setDownloadRecord();
  1444 
  1445     list *head = NULL;
  1446     http_head(&head);
  1447 
  1448     #if GDEBUG
  1449     {
  1450         debugEcho("The HTTP HEAD from %s ...", GBASESERVER);
  1451         list *item;
  1452         for (item = head; item; item = item->next)
  1453             debugEcho("   '%s' => '%s'", item->key, item->value);
  1454     }
  1455     #endif
  1456 
  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;
  1463 
  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.");
  1470 
  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?
  1475     {
  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);
  1480     } // if
  1481 
  1482     // !!! FIXME: Check Cache-Control, Pragma no-cache
  1483 
  1484     int io = -1;
  1485 
  1486     if (ishead)
  1487         debugEcho("This is a HEAD request to the offload server.");
  1488 
  1489     // Partial content:
  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";
  1496 
  1497     if (ifrange != NULL)
  1498     {
  1499         // !!! FIXME: handle this.
  1500         debugEcho("Client set If-Range: [%s]...unsupported!", ifrange);
  1501         httprange = NULL;
  1502     } // if
  1503 
  1504     if (httprange != NULL)
  1505     {
  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");
  1511         else
  1512         {
  1513             httprange += 6;
  1514             char *pos = strchr(httprange, '-');
  1515             if (pos != NULL)
  1516             {
  1517                 *(pos++) = '\0';
  1518                 startRange = *httprange == '\0' ? 0 : atoi64(httprange);
  1519                 endRange = *pos == '\0' ? max-1 : atoi64(pos);
  1520                 responseCode = "206 Partial Content";
  1521                 reportRange = 1;
  1522             } // if
  1523         } // else
  1524     } // if
  1525 
  1526     if (endRange >= max)  // apparently, this is legal to request.
  1527         endRange = max - 1;
  1528 
  1529     debugEcho("We are feeding the client bytes %lld to %lld of %lld",
  1530                 (long long) startRange, (long long) endRange, (long long) max);
  1531 
  1532     if (invalidContentRange(startRange, endRange, max))
  1533         failure("400 Bad Request", "Bad content range requested.");
  1534 
  1535 #if GNOCACHE
  1536 
  1537     GFilePath = makeStr("%s%s", GOFFLOADDIR, Guri);
  1538     debugEcho("file to send is %s", GFilePath);
  1539     list *metadata = head;
  1540     head = NULL;
  1541     io = open(GFilePath, O_RDONLY);
  1542     if (io == -1)
  1543         failure("500 Internal Server Error", "Couldn't access cached data.");
  1544 
  1545 #else
  1546 
  1547     char *etagFname = etagToCacheFname(etag);
  1548     GFilePath = makeStr("%s/filedata-%s", GOFFLOADDIR, etagFname);
  1549     GMetaDataPath = makeStr("%s/metadata-%s", GOFFLOADDIR, etagFname);
  1550     free(etagFname);
  1551 
  1552     listSet(&head, "X-Offload-Orig-URL", Guri);
  1553     listSet(&head, "X-Offload-Hostname", GBASESERVER);
  1554 
  1555     debugEcho("metadata cache is %s", GMetaDataPath);
  1556     debugEcho("file cache is %s", GFilePath);
  1557 
  1558     list *metadata = NULL;
  1559 
  1560     if (ishead)
  1561         metadata = head;
  1562     else
  1563     {
  1564         getSemaphore();
  1565 
  1566         metadata = loadMetadata(GMetaDataPath);
  1567         if (cachedMetadataMostRecent(metadata, head))
  1568         {
  1569             listFree(&head);
  1570             debugEcho("File is cached.");
  1571         } // if
  1572 
  1573         else
  1574         {
  1575             listFree(&metadata);
  1576 
  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!
  1579 
  1580             FILE *cacheio = fopen(GFilePath, "wb");
  1581             if (cacheio == NULL)
  1582             {
  1583                 close(io);
  1584                 failure("500 Internal Server Error", "Couldn't update cached data.");
  1585             } // if
  1586 
  1587             FILE *metaout = fopen(GMetaDataPath, "wb");
  1588             if (metaout == NULL)
  1589             {
  1590                 fclose(cacheio);
  1591                 close(sock);
  1592                 nukeRequestFromCache();
  1593                 failure("500 Internal Server Error", "Couldn't update metadata.");
  1594             } // if
  1595 
  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.
  1601             //
  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");
  1605 
  1606             const pid_t pid = cacheFork(sock, cacheio, max);
  1607             listSet(&head, "X-Offload-Caching-PID", makeNum(pid));
  1608 
  1609             list *item;
  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
  1613 
  1614             metadata = head;
  1615         } // else
  1616 
  1617         putSemaphore();
  1618 
  1619         head = NULL;   // we either moved this to (metadata) or free()d it.
  1620 
  1621         io = open(GFilePath, O_RDONLY);
  1622         if (io == -1)
  1623             failure("500 Internal Server Error", "Couldn't access cached data.");
  1624     } // else
  1625 
  1626 #endif
  1627 
  1628     if (!GHttpStatus)
  1629         GHttpStatus = atoi(responseCode);
  1630 
  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"));
  1641     if (reportRange)
  1642     {
  1643         char rangestr[128];
  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);
  1647     } // if
  1648     write_header("", "");
  1649 
  1650     listFree(&metadata);
  1651 
  1652     if (ishead)
  1653     {
  1654         debugEcho("This was a HEAD request to offload server, so we're done.");
  1655         terminate();
  1656     } // if
  1657 
  1658     int64 br = 0;
  1659     endRange++;
  1660     time_t lastReadTime = time(NULL);
  1661     while (br < endRange)
  1662     {
  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);
  1668 
  1669         if (readsize > (endRange - br))
  1670             readsize = (endRange - br);
  1671 
  1672         if (readsize == 0)
  1673         {
  1674             debugEcho("readsize is unexpectedly zero.");
  1675             break;  // Shouldn't hit, but just in case...
  1676         } // if
  1677 
  1678         struct stat statbuf;
  1679         if (fstat(io, &statbuf) == -1)
  1680         {
  1681             debugEcho("fstat() failed.");
  1682             break;
  1683         } // if
  1684 
  1685         const int64 cursize = statbuf.st_size;
  1686         const time_t now = time(NULL);
  1687         if (cursize < max)
  1688         {
  1689             if ((cursize - br) <= 0)  // may be caching on another process.
  1690             {
  1691                 if (now > (lastReadTime + GTIMEOUT))
  1692                 {
  1693                     debugEcho("timeout: cache file seems to have stalled.");
  1694                     // !!! FIXME: maybe try to kill() the cache process?
  1695                     break;   // oh well, give up.
  1696                 } // if
  1697 
  1698                 sleep(1);   // wait awhile...
  1699                 continue;   // ...then try again.
  1700             } // if
  1701         } // else
  1702 
  1703         lastReadTime = now;
  1704 
  1705         const int len = read(io, data, readsize);
  1706         if (len <= 0)
  1707         {
  1708             debugEcho("read() failed");
  1709             break;   // select() and fstat() should have caught this...
  1710         } // if
  1711 
  1712         // see if the remote end shutdown their end of the socket
  1713         //  (web browser user hit cancel, etc).
  1714         int deadsocket = 0;
  1715 
  1716         #if GLISTENPORT
  1717         while (1)
  1718         {
  1719             char onebyte = 0;
  1720             const ssize_t recvval = recv(GSocket, &onebyte, sizeof (onebyte), MSG_DONTWAIT);
  1721             deadsocket = (recvval == 0);
  1722             if (deadsocket)
  1723                 debugEcho("EOF on socket!");
  1724             if ( ((recvval < 0) && (errno == EAGAIN)) || (deadsocket) )
  1725                 break;
  1726         } // while
  1727         #else
  1728         if ( (feof(stdout)) || (ferror(stdout)) )
  1729         {
  1730             debugEcho("EOF or error on stdout!");
  1731             deadsocket = 1;
  1732         } // if
  1733         #endif
  1734 
  1735         if (deadsocket)
  1736             break;
  1737 
  1738         if ((br >= startRange) && (br < endRange))
  1739         {
  1740             #if ((!GLISTENPORT) && (GDEBUG) && (!GDEBUGTOFILE))
  1741             debugEcho("Would have written %d bytes", len);
  1742             GBytesSent += len;
  1743             #else
  1744             const int bw = (int) write(GSocket, data, len);
  1745             debugEcho("Wrote %d bytes", bw);
  1746             GBytesSent += (int64) bw;
  1747             if (bw != len)
  1748             {
  1749                 debugEcho("FAILED to write %d bytes to client!", len-bw);
  1750                 break;
  1751             } // else
  1752             #endif
  1753         } // else if
  1754 
  1755         br += len;
  1756     } // while
  1757 
  1758     debugEcho("closing cache file...");
  1759     close(io);
  1760 
  1761     debugEcho("Transfer loop is complete.");
  1762 
  1763     if (br != endRange)
  1764     {
  1765         debugEcho("Bogus transfer! Sent %lld, wanted to send %lld!",
  1766                   (long long) br, (long long) endRange);
  1767     } // else
  1768 
  1769     terminate();  // done!
  1770     return 0;
  1771 } // serverMainline
  1772 
  1773 
  1774 #if GLISTENPORT
  1775 #define GLISTENPORTSTR OFFLOAD_NUMSTR(GLISTENPORT)
  1776 static const char *readClientHeaders(const int fd, const struct sockaddr *addr)
  1777 {
  1778     debugEcho("Reading request headers...");
  1779 
  1780     // !!! FIXME: do this without network-specifics?
  1781     void *ipptr = NULL;
  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;
  1786 
  1787     char remoteaddr[64] = { '\0' };
  1788     int trusted = 0;
  1789     if ((!ipptr) || (!inet_ntop(addr->sa_family, ipptr, remoteaddr, sizeof (remoteaddr))))
  1790         debugEcho("Don't know remote address!");
  1791     else
  1792     {
  1793         debugEcho("Remote address is %s", remoteaddr);
  1794 
  1795         static const char *trust[] = { GLISTENTRUSTFWD };
  1796         const int total = sizeof (trust) / sizeof (trust[0]);
  1797         int i;
  1798         for (i = 0; i < total; i++)
  1799         {
  1800             if ((trust[i]) && (strcmp(trust[i], remoteaddr) == 0))
  1801                 break;
  1802         } // for
  1803         trusted = (i < total);
  1804         debugEcho("This address %s a trusted proxy.", trusted ? "is" : "is not");
  1805     } // else
  1806 
  1807     const time_t endtime = time(NULL) + GTIMEOUT;
  1808     int br = 0;
  1809     char buf[1024];
  1810     int seenresponse = 0;
  1811     while (1)
  1812     {
  1813         const time_t now = time(NULL);
  1814         int rc = -1;
  1815         fd_set rfds;
  1816 
  1817         if (endtime >= now)
  1818         {
  1819             struct timeval tv;
  1820             FD_ZERO(&rfds);
  1821             FD_SET(fd, &rfds);
  1822             tv.tv_sec = endtime - now;
  1823             tv.tv_usec = 0;
  1824             rc = select(fd+1, &rfds, NULL, NULL, &tv);
  1825         } // if
  1826 
  1827         if ((rc <= 0) || (FD_ISSET(fd, &rfds) == 0))
  1828             return "Timeout while talking to client.";
  1829 
  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.";
  1834 
  1835         if (buf[br] == '\r')
  1836             ;  // ignore these.
  1837         else if (buf[br] == '\n')
  1838         {
  1839             char *ptr = NULL;
  1840             if (br == 0)  // empty line, end of headers.
  1841                 break;
  1842 
  1843             buf[br] = '\0';
  1844             if (seenresponse)
  1845             {
  1846                 debugEcho("Saw request line from client: '%s'", buf);
  1847 
  1848                 ptr = strchr(buf, ':');
  1849                 if (ptr != NULL)
  1850                 {
  1851                     *(ptr++) = '\0';
  1852                     while (*ptr == ' ')
  1853                         ptr++;
  1854 
  1855                     if (strcasecmp(buf, "X-Forwarded-For") == 0)
  1856                     {
  1857                         if (trusted)
  1858                             snprintf(remoteaddr, sizeof (remoteaddr), "%s", ptr);
  1859                     } // if
  1860 
  1861                     else if (strcasecmp(buf, "User-Agent") == 0)
  1862                         setenv("HTTP_USER_AGENT", ptr, 1);
  1863 
  1864                     else if (strcasecmp(buf, "Range") == 0)
  1865                         setenv("HTTP_RANGE", ptr, 1);
  1866 
  1867                     else if (strcasecmp(buf, "If-Range") == 0)
  1868                         setenv("HTTP_IF_RANGE", ptr, 1);
  1869 
  1870                     else if (strcasecmp(buf, "Referer") == 0)
  1871                         setenv("HTTP_REFERER", ptr, 1);
  1872 
  1873                     // we currently don't care about anything else.
  1874                 } // if
  1875             } // if
  1876 
  1877             else
  1878             {
  1879                 ptr = strchr(buf, ' ');
  1880                 if (ptr != NULL)
  1881                 {
  1882                     *(ptr++) = '\0';
  1883                     while (*ptr == ' ')
  1884                         ptr++;
  1885                     setenv("REQUEST_METHOD", buf, 1);
  1886                     const char *start = ptr;
  1887                     ptr = strchr(ptr, ' ');
  1888                     if (ptr != NULL)
  1889                     {
  1890                         *(ptr++) = '\0';
  1891                         while (*ptr == ' ')
  1892                             ptr++;
  1893                         setenv("REQUEST_URI", start, 1);
  1894                         if (strncasecmp(ptr, "HTTP/", 5) == 0)
  1895                             setenv("REQUEST_VERSION", ptr, 1);
  1896                         else
  1897                             ptr = NULL;  // fail below.
  1898                     } // if
  1899                 } // if
  1900                 seenresponse = 1;
  1901             } // else
  1902 
  1903             if (ptr == NULL)
  1904                 return "Bogus request from client.";
  1905 
  1906             br = 0;
  1907         } // if
  1908         else
  1909         {
  1910             br++;
  1911             if (br >= sizeof (buf))
  1912                 return "Buffer overflow.";
  1913         } // else
  1914     } // while
  1915 
  1916     if (remoteaddr[0])
  1917         setenv("REMOTE_ADDR", remoteaddr, 1);
  1918 
  1919     debugEcho("done parsing request headers");
  1920     return NULL;
  1921 } // readClientHeaders
  1922 
  1923 
  1924 static void daemonChildSig(int sig)
  1925 {
  1926     debugEcho("caught signal #%d!", sig);
  1927     terminate();
  1928 } // daemonChildSig
  1929 
  1930 
  1931 static inline void daemonChild(const int fd, const struct sockaddr *addr,
  1932                                int argc, char **argv)
  1933 {
  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);
  1944 
  1945     GSocket = fd;
  1946 
  1947     debugEcho("New child running to handle incoming request.");
  1948 
  1949     if (readClientHeaders(GSocket, addr) == NULL)  // NULL == no error.
  1950         serverMainline(argc, argv, environ);
  1951 
  1952     terminate();
  1953 } // daemonChild
  1954 
  1955 
  1956 #if !GLISTENDAEMONIZE
  1957 #define daemonToBackground()
  1958 #else
  1959 static void daemonToBackground(void)
  1960 {
  1961     const pid_t backpid = fork();
  1962     if (backpid > 0)  // parent.
  1963         exit(0);
  1964 
  1965     else if (backpid == -1)
  1966     {
  1967         fprintf(stderr, "Failed to fork(): %s\n", strerror(errno));
  1968         exit(1);
  1969     } // if
  1970 
  1971     // we're the child. Welcome to the background.
  1972     fclose(stdin);
  1973     fclose(stdout);
  1974     fclose(stderr);
  1975     stdin = stderr = stdout = NULL;
  1976     chdir("/");
  1977     setsid();
  1978 }
  1979 #endif
  1980 
  1981 
  1982 static int daemonListenSocket(void)
  1983 {
  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;
  1989 
  1990     int rc = -1;
  1991     struct addrinfo *dns = NULL;
  1992     if ((rc = getaddrinfo(GLISTENADDR, GLISTENPORTSTR, &hints, &dns)) != 0)
  1993     {
  1994         if (stderr != NULL)
  1995             fprintf(stderr, "getaddrinfo failure: %s\n", gai_strerror(rc));
  1996         return -1;
  1997     } // if
  1998 
  1999     int fd = -1;
  2000     struct addrinfo *addr;
  2001     for (addr = dns; addr != NULL; addr = addr->ai_next)
  2002     {
  2003         fd = socket(addr->ai_family, addr->ai_socktype, addr->ai_protocol);
  2004         if (fd != -1)
  2005         {
  2006             int on = 1;
  2007             setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on));
  2008             if (bind(fd, addr->ai_addr, addr->ai_addrlen) == 0)
  2009             {
  2010                 if (listen(fd, 16) == 0)
  2011                     break;
  2012             } // if
  2013 
  2014             close(fd);
  2015             fd = -1;
  2016         } // if
  2017     } // for
  2018     freeaddrinfo(dns);
  2019 
  2020     if (fd == -1)
  2021     {
  2022         if (stderr != NULL)
  2023             fprintf(stderr, "Failed to bind socket.\n");
  2024     } // if
  2025 
  2026     return fd;
  2027 } // daemonListenSocket
  2028 
  2029 
  2030 static inline int daemonMainline(int argc, char **argv, char **envp)
  2031 {
  2032     signal(SIGCHLD, SIG_IGN);
  2033     daemonToBackground();
  2034 
  2035     const int fd = daemonListenSocket();
  2036     if (fd == -1)
  2037         return 2;
  2038 
  2039     while (1)  // loop forever.
  2040     {
  2041         struct sockaddr addr;
  2042         socklen_t addrlen = sizeof (addr);
  2043         const int newfd = accept(fd, &addr, &addrlen);
  2044         if (newfd != -1)
  2045         {
  2046             const pid_t pid = fork();
  2047             if (pid != 0)  // we're NOT the child.
  2048                 close(newfd);
  2049             else
  2050             {
  2051                 close(fd);
  2052                 daemonChild(newfd, &addr, argc, argv);
  2053                 terminate();  // just in case.
  2054             } // else
  2055         } // if
  2056     } // while
  2057 
  2058     return 0;
  2059 } // daemonMainline
  2060 #endif
  2061 
  2062 
  2063 int main(int argc, char **argv, char **envp)
  2064 {
  2065     #if !GLISTENPORT
  2066     GSocket = fileno(stdout);
  2067     return serverMainline(argc, argv, envp);
  2068     #else
  2069     return daemonMainline(argc, argv, envp);
  2070     #endif
  2071 } // main
  2072 
  2073 
  2074 // end of nph-offload.c ...
  2075 
  2076 
  2077 
  2078 #if GMAXDUPEDOWNLOADS > 0
  2079 
  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.
  2083 
  2084 /*
  2085 SHA-1 in C
  2086 By Steve Reid <steve@edmweb.com>
  2087 100% Public Domain
  2088 */
  2089 
  2090 #define rol(value, bits) (((value) << (bits)) | ((value) >> (32 - (bits))))
  2091 
  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))
  2097 #else
  2098 #define blk0(i) block->l[i]
  2099 #endif
  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))
  2102 
  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);
  2109 
  2110 
  2111 /* Hash a single 512-bit block. This is the core of the algorithm. */
  2112 
  2113 static void Sha1_transform(uint32 state[5], const uint8 buffer[64])
  2114 {
  2115     uint32 a, b, c, d, e;
  2116     typedef union {
  2117         uint8 c[64];
  2118         uint32 l[16];
  2119     } CHAR64LONG16;
  2120     CHAR64LONG16* block;
  2121     static uint8 workspace[64];
  2122     block = (CHAR64LONG16*)workspace;
  2123     memcpy(block, buffer, 64);
  2124     /* Copy context->state[] to working vars */
  2125     a = state[0];
  2126     b = state[1];
  2127     c = state[2];
  2128     d = state[3];
  2129     e = state[4];
  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[] */
  2152     state[0] += a;
  2153     state[1] += b;
  2154     state[2] += c;
  2155     state[3] += d;
  2156     state[4] += e;
  2157     /* Wipe variables */
  2158     a = b = c = d = e = 0;
  2159 }
  2160 
  2161 static void Sha1_init(Sha1 *context)
  2162 {
  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;
  2170 }
  2171 
  2172 
  2173 /* Run your data through this. */
  2174 
  2175 static void Sha1_append(Sha1 *context, const uint8 *data, uint32 len)
  2176 {
  2177     uint32 i, j;
  2178 
  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]);
  2187         }
  2188         j = 0;
  2189     }
  2190     else i = 0;
  2191     memcpy(&context->buffer[j], &data[i], len - i);
  2192 }
  2193 
  2194 
  2195 /* Add padding and return the message digest. */
  2196 
  2197 static void Sha1_finish(Sha1 *context, uint8 digest[20])
  2198 {
  2199     uint32 i, j;
  2200     uint8 finalcount[8];
  2201 
  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 */
  2205     }
  2206     Sha1_append(context, (uint8 *)"\200", 1);
  2207     while ((context->count[0] & 504) != 448) {
  2208         Sha1_append(context, (uint8 *)"\0", 1);
  2209     }
  2210     Sha1_append(context, finalcount, 8);  /* Should cause a Sha1_transform() */
  2211     for (i = 0; i < 20; i++) {
  2212         digest[i] = (uint8)
  2213          ((context->state[i>>2] >> ((3-(i & 3)) * 8) ) & 255);
  2214     }
  2215     /* Wipe variables */
  2216     i = j = 0;
  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);
  2222 }
  2223 
  2224 #endif
  2225