More HTTP server configuration: allow user-chosen address+port and
authorsimon <simon@cda61777-01e9-0310-a592-d414129be87e>
Sat, 1 Nov 2008 15:21:40 +0000 (15:21 +0000)
committersimon <simon@cda61777-01e9-0310-a592-d414129be87e>
Sat, 1 Nov 2008 15:21:40 +0000 (15:21 +0000)
HTTP Basic authentication data.

git-svn-id: svn://svn.tartarus.org/sgt/agedu@8240 cda61777-01e9-0310-a592-d414129be87e

TODO
agedu.c
httpd.c
httpd.h

diff --git a/TODO b/TODO
index 07a84f1..9b93ca2 100644 (file)
--- a/TODO
+++ b/TODO
@@ -3,12 +3,6 @@ TODO list for agedu
 
 Before it's non-embarrassingly releasable:
 
- - add more configurable options
-    + server address in httpd mode
-    + HTTP authentication: specify username and/or password, the
-      latter by at least some means which doesn't involve it showing
-      up in "ps"
-
  - more flexible running modes
     + combined scan+dump mode which doesn't even generate an index
       file (nearly indistinguishable from find(1))
@@ -71,6 +65,8 @@ Before it's non-embarrassingly releasable:
 
 Future directions:
 
+ - IPv6 support in the HTTP server
+
  - run-time configuration in the HTTP server
     * I think this probably works by having a configuration form, or
       a link pointing to one, somewhere on the report page. If you
diff --git a/agedu.c b/agedu.c
index 607d425..c88fdf5 100644 (file)
--- a/agedu.c
+++ b/agedu.c
@@ -273,9 +273,16 @@ static void text_query(const void *mappedfile, const char *querydir,
         HELPARG("age") HELPOPT("[--text] include only files older than this") \
     VAL(AGERANGE) SHORT(r) LONG(age_range) LONG(range) LONG(ages) \
         HELPARG("age[-age]") HELPOPT("[--html,--web] set limits of colour coding") \
+    VAL(SERVERADDR) LONG(address) LONG(addr) LONG(server_address) \
+              LONG(server_addr) \
+        HELPARG("addr[:port]") HELPOPT("[--web] specify HTTP server address") \
     VAL(AUTH) LONG(auth) LONG(http_auth) LONG(httpd_auth) \
               LONG(server_auth) LONG(web_auth) \
         HELPARG("type") HELPOPT("[--web] specify HTTP authentication method") \
+    VAL(AUTHFILE) LONG(auth_file) \
+        HELPARG("filename") HELPOPT("[--web] read HTTP Basic user/pass from file") \
+    VAL(AUTHFD) LONG(auth_fd) \
+        HELPARG("fd") HELPOPT("[--web] read HTTP Basic user/pass from fd") \
     HELPPFX("also") \
     NOVAL(HELP) SHORT(h) LONG(help) HELPOPT("display this help text") \
     NOVAL(VERSION) SHORT(V) LONG(version) HELPOPT("report version number") \
@@ -408,6 +415,9 @@ int main(int argc, char **argv)
     time_t now = time(NULL);
     time_t textcutoff = now, htmlnewest = now, htmloldest = now;
     int htmlautoagerange = 1;
+    const char *httpserveraddr = NULL;
+    int httpserverport = 0;
+    const char *httpauthdata = NULL;
     int auth = HTTPD_AUTH_MAGIC | HTTPD_AUTH_BASIC;
     int progress = 1;
     struct inclusion_exclusion *inex = NULL;
@@ -608,6 +618,21 @@ int main(int argc, char **argv)
                        htmlautoagerange = 0;
                    }
                    break;
+                 case OPT_SERVERADDR:
+                   {
+                       char *port;
+                       if (optval[0] == '[' &&
+                           (port = strchr(optval, ']')) != NULL)
+                           port++;
+                       else
+                           port = optval;
+                       port += strcspn(port, ":");
+                       if (port)
+                           *port++ = '\0';
+                       httpserveraddr = optval;
+                       httpserverport = atoi(port);
+                   }
+                   break;
                  case OPT_AUTH:
                    if (!strcmp(optval, "magic"))
                        auth = HTTPD_AUTH_MAGIC;
@@ -638,6 +663,51 @@ int main(int argc, char **argv)
                        return 1;
                    }
                    break;
+                 case OPT_AUTHFILE:
+                 case OPT_AUTHFD:
+                   {
+                       int fd;
+                       char namebuf[40];
+                       const char *name;
+                       char *authbuf;
+                       int authlen, authsize;
+                       int ret;
+
+                       if (optid == OPT_AUTHFILE) {
+                           fd = open(optval, O_RDONLY);
+                           if (fd < 0) {
+                               fprintf(stderr, "%s: %s: open: %s\n", PNAME,
+                                       optval, strerror(errno));
+                               return 1;
+                           }
+                           name = optval;
+                       } else {
+                           fd = atoi(optval);
+                           name = namebuf;
+                           sprintf(namebuf, "fd %d", fd);
+                       }
+
+                       authlen = 0;
+                       authsize = 256;
+                       authbuf = snewn(authsize, char);
+                       while ((ret = read(fd, authbuf+authlen,
+                                          authsize-authlen)) > 0) {
+                           authlen += ret;
+                           if ((authsize - authlen) < (authsize / 16)) {
+                               authsize = authlen * 3 / 2 + 4096;
+                               authbuf = sresize(authbuf, authsize, char);
+                           }
+                       }
+                       if (ret < 0) {
+                           fprintf(stderr, "%s: %s: read: %s\n", PNAME,
+                                   name, strerror(errno));
+                           return 1;
+                       }
+                       if (optid == OPT_AUTHFILE)
+                           close(fd);
+                       httpauthdata = authbuf;
+                   }
+                   break;
                  case OPT_INCLUDE:
                  case OPT_INCLUDEPATH:
                  case OPT_EXCLUDE:
@@ -865,7 +935,8 @@ int main(int argc, char **argv)
        }
        triewalk_free(tw);
     } else if (mode == HTTPD) {
-       struct html_config cfg;
+       struct html_config pcfg;
+       struct httpd_config dcfg;
 
        fd = open(filename, O_RDONLY);
        if (fd < 0) {
@@ -884,11 +955,14 @@ int main(int argc, char **argv)
            return 1;
        }
 
-       cfg.format = NULL;
-       cfg.autoage = htmlautoagerange;
-       cfg.oldest = htmloldest;
-       cfg.newest = htmlnewest;
-       run_httpd(mappedfile, auth, &cfg);
+       dcfg.address = httpserveraddr;
+       dcfg.port = httpserverport;
+       dcfg.basicauthdata = httpauthdata;
+       pcfg.format = NULL;
+       pcfg.autoage = htmlautoagerange;
+       pcfg.oldest = htmloldest;
+       pcfg.newest = htmlnewest;
+       run_httpd(mappedfile, auth, &dcfg, &pcfg);
     }
 
     return 0;
diff --git a/httpd.c b/httpd.c
index b08f6ef..d03e1c5 100644 (file)
--- a/httpd.c
+++ b/httpd.c
@@ -417,11 +417,12 @@ static void base64_encode_atom(unsigned char *data, int n, char *out)
        out[3] = '=';
 }
 
-void run_httpd(const void *t, int authmask, const struct html_config *incfg)
+void run_httpd(const void *t, int authmask, const struct httpd_config *dcfg,
+              const struct html_config *incfg)
 {
     int fd;
     int authtype;
-    char *authstring = NULL, authbuf[512];
+    char *authstring = NULL;
     unsigned long ipaddr;
     struct fd *f;
     struct sockaddr_in addr;
@@ -440,13 +441,18 @@ void run_httpd(const void *t, int authmask, const struct html_config *incfg)
        exit(1);
     }
     addr.sin_family = AF_INET;
-    srand(0L);
-    ipaddr = 0x7f000000;
-    ipaddr += (1 + rand() % 255) << 16;
-    ipaddr += (1 + rand() % 255) << 8;
-    ipaddr += (1 + rand() % 255);
-    addr.sin_addr.s_addr = htonl(ipaddr);
-    addr.sin_port = htons(0);
+    if (!dcfg->address) {
+       srand(0L);
+       ipaddr = 0x7f000000;
+       ipaddr += (1 + rand() % 255) << 16;
+       ipaddr += (1 + rand() % 255) << 8;
+       ipaddr += (1 + rand() % 255);
+       addr.sin_addr.s_addr = htonl(ipaddr);
+       addr.sin_port = htons(0);
+    } else {
+       addr.sin_addr.s_addr = inet_addr(dcfg->address);
+       addr.sin_port = dcfg->port ? htons(dcfg->port) : 80;
+    }
     addrlen = sizeof(addr);
     if (bind(fd, (struct sockaddr *)&addr, addrlen) < 0) {
        fprintf(stderr, "bind: %s\n", strerror(errno));
@@ -464,73 +470,98 @@ void run_httpd(const void *t, int authmask, const struct html_config *incfg)
     if ((authmask & HTTPD_AUTH_MAGIC) &&
        (check_owning_uid(fd, 1) == getuid())) {
        authtype = HTTPD_AUTH_MAGIC;
-       printf("Using Linux /proc/net magic authentication\n");
+       if (authmask != HTTPD_AUTH_MAGIC)
+           printf("Using Linux /proc/net magic authentication\n");
     } else if ((authmask & HTTPD_AUTH_BASIC)) {
-       char username[128], password[128], userpass[259];
+       char username[128], password[128], userpassbuf[259];
+       const char *userpass;
        const char *rname;
        unsigned char passbuf[10];
        int i, j, k, fd;
 
        authtype = HTTPD_AUTH_BASIC;
 
-       sprintf(username, "agedu");
-       rname = "/dev/urandom";
-       fd = open(rname, O_RDONLY);
-       if (fd < 0) {
-           int err = errno;
-           rname = "/dev/random";
+       if (authmask != HTTPD_AUTH_BASIC)
+           printf("Using HTTP Basic authentication\n");
+
+       if (dcfg->basicauthdata) {
+           userpass = dcfg->basicauthdata;
+       } else {
+           sprintf(username, "agedu");
+           rname = "/dev/urandom";
            fd = open(rname, O_RDONLY);
            if (fd < 0) {
-               int err2 = errno;
-               fprintf(stderr, "/dev/urandom: open: %s\n", strerror(err));
-               fprintf(stderr, "/dev/random: open: %s\n", strerror(err2));
-               exit(1);
+               int err = errno;
+               rname = "/dev/random";
+               fd = open(rname, O_RDONLY);
+               if (fd < 0) {
+                   int err2 = errno;
+                   fprintf(stderr, "/dev/urandom: open: %s\n", strerror(err));
+                   fprintf(stderr, "/dev/random: open: %s\n", strerror(err2));
+                   exit(1);
+               }
            }
-       }
-       for (i = 0; i < 10 ;) {
-           j = read(fd, passbuf + i, 10 - i);
-           if (j <= 0) {
-               fprintf(stderr, "%s: read: %s\n", rname,
-                       j < 0 ? strerror(errno) : "unexpected EOF");
-               exit(1);
+           for (i = 0; i < 10 ;) {
+               j = read(fd, passbuf + i, 10 - i);
+               if (j <= 0) {
+                   fprintf(stderr, "%s: read: %s\n", rname,
+                           j < 0 ? strerror(errno) : "unexpected EOF");
+                   exit(1);
+               }
+               i += j;
            }
-           i += j;
-       }
-       close(fd);
-       for (i = 0; i < 16; i++) {
-           /* 32 characters out of the 36 alphanumerics gives me the
-            * latitude to discard i,l,o for being too numeric-looking,
-            * and w because it has two too many syllables and one too
-            * many presidential associations. */
-           static const char chars[32] = "0123456789abcdefghjkmnpqrstuvxyz";
-           int v = 0;
-
-           k = i / 8 * 5;
-           for (j = 0; j < 5; j++)
-               v |= ((passbuf[k+j] >> (i%8)) & 1) << j;
-
-           password[i] = chars[v];
-       }
-       password[i] = '\0';
+           close(fd);
+           for (i = 0; i < 16; i++) {
+               /*
+                * 32 characters out of the 36 alphanumerics gives
+                * me the latitude to discard i,l,o for being too
+                * numeric-looking, and w because it has two too
+                * many syllables and one too many presidential
+                * associations.
+                */
+               static const char chars[32] =
+                   "0123456789abcdefghjkmnpqrstuvxyz";
+               int v = 0;
+
+               k = i / 8 * 5;
+               for (j = 0; j < 5; j++)
+                   v |= ((passbuf[k+j] >> (i%8)) & 1) << j;
+
+               password[i] = chars[v];
+           }
+           password[i] = '\0';
+
+           sprintf(userpassbuf, "%s:%s", username, password);
+           userpass = userpassbuf;
 
-       printf("Using HTTP Basic authentication\nUsername: %s\nPassword: %s\n",
-              username, password);
+           printf("Username: %s\nPassword: %s\n", username, password);
+       }
 
-       k = sprintf(userpass, "%s:%s", username, password);
+       k = strlen(userpass);
+       authstring = snewn(k * 4 / 3 + 16, char);
        for (i = j = 0; i < k ;) {
            int s = k-i < 3 ? k-i : 3;
-           base64_encode_atom((unsigned char *)(userpass+i), s, authbuf+j);
+           base64_encode_atom((unsigned char *)(userpass+i), s, authstring+j);
            i += s;
            j += 4;
        }
-       authbuf[j] = '\0';
-       authstring = authbuf;
-    } else {
+       authstring[j] = '\0';
+    } else if ((authmask & HTTPD_AUTH_NONE)) {
        authtype = HTTPD_AUTH_NONE;
-       printf("Web server is unauthenticated\n");
+       if (authmask != HTTPD_AUTH_NONE)
+           printf("Web server is unauthenticated\n");
+    } else {
+       fprintf(stderr, "agedu: authentication method not supported\n");
+       exit(1);
+    }
+    if (!dcfg->address) {
+       if (ntohs(addr.sin_port) == 80) {
+           printf("URL: http://%s/\n", inet_ntoa(addr.sin_addr));
+       } else {
+           printf("URL: http://%s:%d/\n",
+                  inet_ntoa(addr.sin_addr), ntohs(addr.sin_port));
+       }
     }
-    printf("URL: http://%s:%d/\n",
-          inet_ntoa(addr.sin_addr), ntohs(addr.sin_port));
 
     /*
      * Now construct an fd structure to hold it.
diff --git a/httpd.h b/httpd.h
index 467803f..1683ebb 100644 (file)
--- a/httpd.h
+++ b/httpd.h
@@ -7,4 +7,11 @@
 #define HTTPD_AUTH_BASIC 2
 #define HTTPD_AUTH_NONE  4
 
-void run_httpd(const void *t, int authmask, const struct html_config *cfg);
+struct httpd_config {
+    const char *address;
+    int port;
+    const char *basicauthdata;
+};
+
+void run_httpd(const void *t, int authmask, const struct httpd_config *dcfg,
+              const struct html_config *pcfg);