From aa5efff20302f2e232315dd6c2b21ca343c59b92 Mon Sep 17 00:00:00 2001 From: Ben Gras Date: Mon, 19 Jun 2006 14:55:09 +0000 Subject: [PATCH] Initial revision --- commands/ftp101/Makefile | 42 ++ commands/ftp101/README | 39 ++ commands/ftp101/crc.c | 132 +++++ commands/ftp101/file.c | 934 +++++++++++++++++++++++++++++++ commands/ftp101/file.h | 33 ++ commands/ftp101/ftp.1 | 148 +++++ commands/ftp101/ftp.c | 404 +++++++++++++ commands/ftp101/ftp.h | 39 ++ commands/ftp101/local.c | 137 +++++ commands/ftp101/local.h | 15 + commands/ftp101/net.c | 567 +++++++++++++++++++ commands/ftp101/net.h | 15 + commands/ftp101/other.c | 173 ++++++ commands/ftp101/other.h | 17 + commands/ftp101/xfer.c | 305 ++++++++++ commands/ftp101/xfer.h | 8 + commands/httpd0995/Makefile | 65 +++ commands/httpd0995/README | 252 +++++++++ commands/httpd0995/SECURITY | 52 ++ commands/httpd0995/cgiexec.c | 255 +++++++++ commands/httpd0995/config.c | 841 ++++++++++++++++++++++++++++ commands/httpd0995/config.h | 86 +++ commands/httpd0995/dir2html.c | 187 +++++++ commands/httpd0995/dir2html.sh | 42 ++ commands/httpd0995/http.h | 118 ++++ commands/httpd0995/http_status.5 | 72 +++ commands/httpd0995/httpd.8 | 124 ++++ commands/httpd0995/httpd.c | 175 ++++++ commands/httpd0995/httpd.conf | 166 ++++++ commands/httpd0995/httpd.conf.5 | 334 +++++++++++ commands/httpd0995/httpd.mtype | 40 ++ commands/httpd0995/httpd0995.txt | 59 ++ commands/httpd0995/net.c | 240 ++++++++ commands/httpd0995/net.h | 17 + commands/httpd0995/pass.c | 213 +++++++ commands/httpd0995/pass.h | 18 + commands/httpd0995/police.c | 407 ++++++++++++++ commands/httpd0995/process.c | 90 +++ commands/httpd0995/proxy.c | 292 ++++++++++ commands/httpd0995/reply.c | 189 +++++++ commands/httpd0995/request.c | 369 ++++++++++++ commands/httpd0995/utility.c | 265 +++++++++ commands/httpd0995/utility.h | 19 + 43 files changed, 7995 insertions(+) create mode 100644 commands/ftp101/Makefile create mode 100644 commands/ftp101/README create mode 100644 commands/ftp101/crc.c create mode 100644 commands/ftp101/file.c create mode 100644 commands/ftp101/file.h create mode 100644 commands/ftp101/ftp.1 create mode 100644 commands/ftp101/ftp.c create mode 100644 commands/ftp101/ftp.h create mode 100644 commands/ftp101/local.c create mode 100644 commands/ftp101/local.h create mode 100644 commands/ftp101/net.c create mode 100644 commands/ftp101/net.h create mode 100644 commands/ftp101/other.c create mode 100644 commands/ftp101/other.h create mode 100644 commands/ftp101/xfer.c create mode 100644 commands/ftp101/xfer.h create mode 100644 commands/httpd0995/Makefile create mode 100644 commands/httpd0995/README create mode 100644 commands/httpd0995/SECURITY create mode 100644 commands/httpd0995/cgiexec.c create mode 100644 commands/httpd0995/config.c create mode 100644 commands/httpd0995/config.h create mode 100644 commands/httpd0995/dir2html.c create mode 100755 commands/httpd0995/dir2html.sh create mode 100644 commands/httpd0995/http.h create mode 100644 commands/httpd0995/http_status.5 create mode 100644 commands/httpd0995/httpd.8 create mode 100644 commands/httpd0995/httpd.c create mode 100644 commands/httpd0995/httpd.conf create mode 100644 commands/httpd0995/httpd.conf.5 create mode 100644 commands/httpd0995/httpd.mtype create mode 100644 commands/httpd0995/httpd0995.txt create mode 100644 commands/httpd0995/net.c create mode 100644 commands/httpd0995/net.h create mode 100644 commands/httpd0995/pass.c create mode 100644 commands/httpd0995/pass.h create mode 100644 commands/httpd0995/police.c create mode 100644 commands/httpd0995/process.c create mode 100644 commands/httpd0995/proxy.c create mode 100644 commands/httpd0995/reply.c create mode 100644 commands/httpd0995/request.c create mode 100644 commands/httpd0995/utility.c create mode 100644 commands/httpd0995/utility.h diff --git a/commands/ftp101/Makefile b/commands/ftp101/Makefile new file mode 100644 index 000000000..f854352e9 --- /dev/null +++ b/commands/ftp101/Makefile @@ -0,0 +1,42 @@ +# Makefile for ftp +# +# 01/25/96 Initial Release Michael Temari +# 03/08/00 Michael Temari, +# 02/07/05 v. 1.01 Michael Temari, + +CFLAGS= -O -D_MINIX -D_POSIX_SOURCE -m +LDFLAGS=-i +BINDIR=/usr/bin +PROG= ftp +MANPAGE= /usr/local/man/man1 + +OBJS= ftp.o local.o file.o xfer.o other.o net.o crc.o + +all: $(PROG) + +$(PROG): $(OBJS) + $(CC) $(LDFLAGS) -o $@ $(OBJS) + install -S 8kw $@ + +clean: + rm -f $(PROG) $(OBJS) + +install: $(BINDIR)/$(PROG) + +$(BINDIR)/$(PROG): $(PROG) + install -cs -o bin $? $@ + +installman: $(MANPAGE) + + cp ftp.1 /usr/local/man/man1 + makewhatis /usr/local/man + +crc.o: crc.c + $(CC) -c $(CFLAGS) -DCRC_ONLY $? + +ftp.o: ftp.c ftp.h local.h file.h other.h net.h +local.o: local.c ftp.h local.h +file.o: file.c ftp.h file.h net.h +other.o: other.c ftp.h other.h +net.o: net.c ftp.h xfer.h net.h +xfer.o: xfer.c ftp.h xfer.h diff --git a/commands/ftp101/README b/commands/ftp101/README new file mode 100644 index 000000000..a60017728 --- /dev/null +++ b/commands/ftp101/README @@ -0,0 +1,39 @@ +ftp101 --- An FTP client program for Minix 2.0 +written by Michael Temari release 1.01a 2006-06-07 + +Full download: ftp101.tar.Z + +FTP is the File Transfer Protocol client that allows you to connect to +a remote FTP server. + +This version should work equally well with Minix 2 and Minix 3. + +Release 1.01a 2006-06-07: minor documentation edits +Release 1.01 2005-02-07: minor bug fix +Release 1.00 2003-12-14: added "ver" command to display current version + and an ftp(1) man page. + +Installation: unpack the tarball, it will create an ftp101 directory. Although +this is a replacement for the ftp client provided in the Minix 2.0.0 and later +distributions, it is suggested you unpack and compile in the /usr/local/src +directory. + +zcat < ftp101.tar.Z | tar xvfp - + +Invoking make -n will show you what the Makefile will do. + +make (or make ftp) compiles a new ftp binary, leaving it in the source +directory. + +make install compiles the binary and installs it in /usr/bin. + +make installman installs the man page in /usr/local/man/man1. The +directory must exist. + +Note: there is a bug in the version of the ftp client distributed with +Minix 2.0.2 and 2.0.3 that causes a \r (0xd) character to be appended +to file names in the destination directory when files are downloaded in +binary mode using the mget command. The bug was corrected in a release +prior to 1.00. + +Notes by ASW revised 2006-06-07 diff --git a/commands/ftp101/crc.c b/commands/ftp101/crc.c new file mode 100644 index 000000000..b6974cc22 --- /dev/null +++ b/commands/ftp101/crc.c @@ -0,0 +1,132 @@ +/* Compute checksum Author: Johan W. Stevenson */ + +/* Copyright 1988 by Johan W. Stevenson */ + +#include +#include +#include + +#if !CRC_ONLY + +int errs; + +#if __STDC__ +int main(int argc, char **argv); +void crc(char *fname); +#else +void crc(); +#endif + +int main(argc, argv) +int argc; +char **argv; +{ + char line[256]; + + if (argc <= 1) + crc((char *) 0); + else if (argc == 2 && strcmp(argv[1], "-") == 0) + while (fgets(line, sizeof line, stdin) != NULL) { + if (line[strlen(line) - 1] == '\n') + line[strlen(line) - 1] = '\0'; + crc(line); + } + else + do { + crc(argv[1]); + argv++; + argc--; + } while (argc > 1); + return(errs != 0); +} + +#endif +/* Crctab calculated by Mark G. Mendel, Network Systems Corporation */ +static unsigned short crctab[256] = { + 0x0000, 0x1021, 0x2042, 0x3063, 0x4084, 0x50a5, 0x60c6, 0x70e7, + 0x8108, 0x9129, 0xa14a, 0xb16b, 0xc18c, 0xd1ad, 0xe1ce, 0xf1ef, + 0x1231, 0x0210, 0x3273, 0x2252, 0x52b5, 0x4294, 0x72f7, 0x62d6, + 0x9339, 0x8318, 0xb37b, 0xa35a, 0xd3bd, 0xc39c, 0xf3ff, 0xe3de, + 0x2462, 0x3443, 0x0420, 0x1401, 0x64e6, 0x74c7, 0x44a4, 0x5485, + 0xa56a, 0xb54b, 0x8528, 0x9509, 0xe5ee, 0xf5cf, 0xc5ac, 0xd58d, + 0x3653, 0x2672, 0x1611, 0x0630, 0x76d7, 0x66f6, 0x5695, 0x46b4, + 0xb75b, 0xa77a, 0x9719, 0x8738, 0xf7df, 0xe7fe, 0xd79d, 0xc7bc, + 0x48c4, 0x58e5, 0x6886, 0x78a7, 0x0840, 0x1861, 0x2802, 0x3823, + 0xc9cc, 0xd9ed, 0xe98e, 0xf9af, 0x8948, 0x9969, 0xa90a, 0xb92b, + 0x5af5, 0x4ad4, 0x7ab7, 0x6a96, 0x1a71, 0x0a50, 0x3a33, 0x2a12, + 0xdbfd, 0xcbdc, 0xfbbf, 0xeb9e, 0x9b79, 0x8b58, 0xbb3b, 0xab1a, + 0x6ca6, 0x7c87, 0x4ce4, 0x5cc5, 0x2c22, 0x3c03, 0x0c60, 0x1c41, + 0xedae, 0xfd8f, 0xcdec, 0xddcd, 0xad2a, 0xbd0b, 0x8d68, 0x9d49, + 0x7e97, 0x6eb6, 0x5ed5, 0x4ef4, 0x3e13, 0x2e32, 0x1e51, 0x0e70, + 0xff9f, 0xefbe, 0xdfdd, 0xcffc, 0xbf1b, 0xaf3a, 0x9f59, 0x8f78, + 0x9188, 0x81a9, 0xb1ca, 0xa1eb, 0xd10c, 0xc12d, 0xf14e, 0xe16f, + 0x1080, 0x00a1, 0x30c2, 0x20e3, 0x5004, 0x4025, 0x7046, 0x6067, + 0x83b9, 0x9398, 0xa3fb, 0xb3da, 0xc33d, 0xd31c, 0xe37f, 0xf35e, + 0x02b1, 0x1290, 0x22f3, 0x32d2, 0x4235, 0x5214, 0x6277, 0x7256, + 0xb5ea, 0xa5cb, 0x95a8, 0x8589, 0xf56e, 0xe54f, 0xd52c, 0xc50d, + 0x34e2, 0x24c3, 0x14a0, 0x0481, 0x7466, 0x6447, 0x5424, 0x4405, + 0xa7db, 0xb7fa, 0x8799, 0x97b8, 0xe75f, 0xf77e, 0xc71d, 0xd73c, + 0x26d3, 0x36f2, 0x0691, 0x16b0, 0x6657, 0x7676, 0x4615, 0x5634, + 0xd94c, 0xc96d, 0xf90e, 0xe92f, 0x99c8, 0x89e9, 0xb98a, 0xa9ab, + 0x5844, 0x4865, 0x7806, 0x6827, 0x18c0, 0x08e1, 0x3882, 0x28a3, + 0xcb7d, 0xdb5c, 0xeb3f, 0xfb1e, 0x8bf9, 0x9bd8, 0xabbb, 0xbb9a, + 0x4a75, 0x5a54, 0x6a37, 0x7a16, 0x0af1, 0x1ad0, 0x2ab3, 0x3a92, + 0xfd2e, 0xed0f, 0xdd6c, 0xcd4d, 0xbdaa, 0xad8b, 0x9de8, 0x8dc9, + 0x7c26, 0x6c07, 0x5c64, 0x4c45, 0x3ca2, 0x2c83, 0x1ce0, 0x0cc1, + 0xef1f, 0xff3e, 0xcf5d, 0xdf7c, 0xaf9b, 0xbfba, 0x8fd9, 0x9ff8, + 0x6e17, 0x7e36, 0x4e55, 0x5e74, 0x2e93, 0x3eb2, 0x0ed1, 0x1ef0 +}; + +/* Updcrc macro derived from article Copyright (C) 1986 Stephen Satchell. + * NOTE: First argument must be in range 0 to 255. + * Second argument is referenced twice. + * + * Programmers may incorporate any or all code into their programs, + * giving proper credit within the source. Publication of the + * source routines is permitted so long as proper credit is given + * to Stephen Satchell, Satchell Evaluations and Chuck Forsberg, + * Omen Technology. + */ + +#define updcrc(cp, crc) ( crctab[((crc >> 8) & 255)] ^ (crc << 8) ^ cp) + +#if CRC_ONLY +unsigned short crc(char *fname) +#else +void crc(fname) +char *fname; +#endif +{ + register int c; + register long len = 0; + register unsigned short crc = 0; + register FILE *fp; + +#if CRC_ONLY + if((fp = fopen(fname, "r")) == NULL) + return 0; +#else + if (fname == NULL) + fp = stdin; + else if ((fp = fopen(fname, "r")) == NULL) { + fprintf(stderr, "crc: cannot open %s\n", fname); + errs++; + return; + } +#endif + while ((c = getc(fp)) != EOF) { + len++; + crc = updcrc(c, crc); + } +#if CRC_ONLY + fclose(fp); + return crc; +#else + printf("%05u %6ld", crc, len); + if (fname) { + printf(" %s", fname); + fclose(fp); + } + printf("\n"); +#endif +} diff --git a/commands/ftp101/file.c b/commands/ftp101/file.c new file mode 100644 index 000000000..9fd282863 --- /dev/null +++ b/commands/ftp101/file.c @@ -0,0 +1,934 @@ +/* file.c Copyright 1992-2000 by Michael Temari All Rights Reserved + * + * This file is part of ftp. + * + * + * 01/25/96 Initial Release Michael Temari, + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "ftp.h" +#include "file.h" +#include "net.h" + +_PROTOTYPE(static char *dir, (char *path, int full)); +_PROTOTYPE(static int asciisize, (int fd, off_t *filesize)); +_PROTOTYPE(static off_t asciisetsize, (int fd, off_t filesize)); +_PROTOTYPE(static int cloneit, (char *file, int mode)); + +#if (__WORD_SIZE == 4) +static char buffer[8192]; +#else +static char buffer[2048]; +#endif +static char line2[512]; + +static char *dir(path, full) +char *path; +int full; +{ +char cmd[128]; +static char name[32]; + + tmpnam(name); + + if(full) + sprintf(cmd, "ls -l %s > %s", path, name); + else + sprintf(cmd, "ls -dA %s > %s", path, name); + + system(cmd); + + return(name); +} + +int DOascii() +{ +int s; + + if(DOcmdcheck()) + return(0); + + s = DOcommand("TYPE", "A"); + + type = TYPE_A; + + return(s); +} + +int DObinary() +{ +int s; + + if(DOcmdcheck()) + return(0); + + s = DOcommand("TYPE", "I"); + + type = TYPE_I; + + return(s); +} + +int DOblock() +{ +int s; + + if(DOcmdcheck()) + return(0); + + s = DOcommand("MODE", "B"); + + mode = MODE_B; + + return(s); +} + +int DOstream() +{ +int s; + + if(DOcmdcheck()) + return(0); + + s = DOcommand("MODE", "S"); + + mode = MODE_S; + + return(s); +} + +int DOpwd() +{ +int s; + + if(DOcmdcheck()) + return(0); + + s = DOcommand("PWD", ""); + + if(s == 500 || s == 502) + s = DOcommand("XPWD", ""); + + return(s); +} + +int DOcd() +{ +char *path; +int s; + + if(DOcmdcheck()) + return(0); + + path = cmdargv[1]; + + if(cmdargc < 2) { + if(readline("Path: ", line2, sizeof(line2)) < 0) + return(-1); + path = line2; + } + + if(!strcmp(path, "..")) + s = DOcommand("CDUP", ""); + else + s = DOcommand("CWD", path); + + if(s == 500 || s == 502) { + if(!strcmp(path, "..")) + s = DOcommand("XCUP", ""); + else + s = DOcommand("XCWD", path); + } + + return(s); +} + +int DOmkdir() +{ +char *path; +int s; + + if(DOcmdcheck()) + return(0); + + path = cmdargv[1]; + + if(cmdargc < 2) { + if(readline("Directory: ", line2, sizeof(line2)) < 0) + return(-1); + path = line2; + } + + s = DOcommand("MKD", path); + + if(s == 500 || s == 502) + s = DOcommand("XMKD", path); + + return(s); +} + +int DOrmdir() +{ +char *path; +int s; + + if(DOcmdcheck()) + return(0); + + path = cmdargv[1]; + + if(cmdargc < 2) { + if(readline("Directory: ", line2, sizeof(line2)) < 0) + return(-1); + path = line2; + } + + s = DOcommand("RMD", path); + + if(s == 500 || s == 502) + s = DOcommand("XRMD", path); + + return(s); +} + +int DOdelete() +{ +char *file; + + if(DOcmdcheck()) + return(0); + + file = cmdargv[1]; + + if(cmdargc < 2) { + if(readline("File: ", line2, sizeof(line2)) < 0) + return(-1); + file = line2; + } + + return(DOcommand("DELE", file)); +} + +int DOmdtm() +{ +char *file; + + if(DOcmdcheck()) + return(0); + + file = cmdargv[1]; + + if(cmdargc < 2) { + if(readline("File: ", line2, sizeof(line2)) < 0) + return(-1); + file = line2; + } + + return(DOcommand("MDTM", file)); +} + +int DOsize() +{ +char *file; + + if(DOcmdcheck()) + return(0); + + file = cmdargv[1]; + + if(cmdargc < 2) { + if(readline("File: ", line2, sizeof(line2)) < 0) + return(-1); + file = line2; + } + + return(DOcommand("SIZE", file)); +} + +int DOstat() +{ +char *file; + + if(cmdargc < 2) + if(!linkopen) { + printf("You must \"OPEN\" a connection first.\n"); + return(0); + } else + return(DOcommand("STAT", "")); + + if(DOcmdcheck()) + return(0); + + file = cmdargv[1]; + + if(cmdargc < 2) { + if(readline("File: ", line2, sizeof(line2)) < 0) + return(-1); + file = line2; + } + + return(DOcommand("STAT", file)); +} + +int DOlist() +{ +char *path; +char *local; +int fd; +int s; + + if(DOcmdcheck()) + return(0); + + path = cmdargv[1]; + + if(cmdargc < 2) + path = ""; + + if(cmdargc < 3) + local = ""; + else + local = cmdargv[2]; + + if(*local == '\0') + fd = 1; + else + fd = open(local, O_WRONLY | O_CREAT | O_TRUNC, 0666); + + if(fd < 0) { + printf("Could not open local file %s. Error %s\n", local, strerror(errno)); + return(0); + } + + s = DOdata("LIST", path, RETR, fd); + + if(fd > 2) + close(fd); + + return(s); +} + +int DOnlst() +{ +char *path; +char *local; +int fd; +int s; + + if(DOcmdcheck()) + return(0); + + path = cmdargv[1]; + + if(cmdargc < 2) + path = ""; + + if(cmdargc < 3) + local = ""; + else + local = cmdargv[2]; + + if(*local == '\0') + fd = 1; + else + fd = open(local, O_WRONLY | O_CREAT | O_TRUNC, 0666); + + if(fd < 0) { + printf("Could not open local file %s. Error %s\n", local, strerror(errno)); + return(0); + } + + s = DOdata("NLST", path, RETR, fd); + + if(fd > 2) + close(fd); + + return(s); +} + +int DOretr() +{ +char *file, *localfile; +int fd; +int s; + + if(DOcmdcheck()) + return(0); + + file = cmdargv[1]; + + if(cmdargc < 2) { + if(readline("Remote File: ", line2, sizeof(line2)) < 0) + return(-1); + file = line2; + } + + if(cmdargc < 3) + localfile = file; + else + localfile = cmdargv[2]; + + fd = open(localfile, O_WRONLY | O_CREAT | O_TRUNC, 0666); + + if(fd < 0) { + printf("Could not open local file %s. Error %s\n", localfile, strerror(errno)); + return(0); + } + + s = DOdata("RETR", file, RETR, fd); + + close(fd); + + return(s); +} + +int DOrretr() +{ +char *file, *localfile; +int fd; +int s; +off_t filesize; +char restart[16]; + + if(DOcmdcheck()) + return(0); + + file = cmdargv[1]; + + if(cmdargc < 2) { + if(readline("Remote File: ", line2, sizeof(line2)) < 0) + return(-1); + file = line2; + } + + if(cmdargc < 3) + localfile = file; + else + localfile = cmdargv[2]; + + fd = open(localfile, O_RDWR); + + if(fd < 0) { + printf("Could not open local file %s. Error %s\n", localfile, strerror(errno)); + return(0); + } + + if(type == TYPE_A) { + if(asciisize(fd, &filesize)) { + printf("Could not determine ascii file size of %s\n", localfile); + close(fd); + return(0); + } + } else + filesize = lseek(fd, 0, SEEK_END); + + sprintf(restart, "%lu", filesize); + + s = DOcommand("REST", restart); + + if(s != 350) { + close(fd); + return(s); + } + + s = DOdata("RETR", file, RETR, fd); + + close(fd); + + return(s); +} + +char *ttime(time_t t) +{ +struct tm *tm; +static char tbuf[16]; + + tm = localtime(&t); + + sprintf(tbuf, "%04d%02d%02d%02d%02d.%02d", + tm->tm_year+1900, tm->tm_mon+1, tm->tm_mday, + tm->tm_hour, tm->tm_min, tm->tm_sec); + + return(tbuf); +} + +static int cloneit(file, mode) +char *file; +int mode; +{ +int opr; +int s; +int ss; +struct stat st; +static unsigned short lcrc; +static unsigned short ccrc; +static unsigned long csize; +static char ft; +static unsigned long maj; +static unsigned long min; +static unsigned long uid; +static unsigned long gid; +static unsigned long fmode; +static unsigned long size; +static unsigned long mtime; +struct utimbuf ut; +unsigned short crc(char *fname); + + if(mode == 1) { + /* see if file exists locally */ + ss = stat(file, &st); + + opr = printreply; + printreply = 0; + s = DOcommand("SITE FDET", file); + printreply = opr; + + if((s / 100) != 2) + return(-1); + + sscanf(reply, "%*d %c%lu%lu%lu%lu%lu%lu%lu", + &ft, &maj, &min, &uid, &gid, &fmode, &size, &mtime); + + if(ft == 'f') { + opr = printreply; + printreply = 0; + s = DOcommand("SITE CCRC", file); + printreply = opr; + if((s / 100) != 2) + return(-1); + + sscanf(reply, "%*d %*s%u%lu", &ccrc, &csize); + if(ss < 0) return(-1); + lcrc = crc(file); + if(size != csize || size != st.st_size || ccrc != lcrc) + return(-1); + } else + if(ss < 0 && ft == 'd') { + s = mkdir(file, fmode); + printf("mkdir %s\n", file); + } else + if((ss < 0) && (ft == 'b' || ft == 'c' || ft == 'p')) { + s = mknod(file, fmode, maj << 8 | min); + printf("mknod %c %u %u\n", file, maj, min); + } else + return(0); + } + ss = stat(file, &st); + if(ss < 0) + return(-1); + if(st.st_uid != uid || st.st_gid != gid) { + s = chown(file, uid, gid); + printf("chown %u:%u %s\n", uid, gid, file); + } + if(st.st_mode != fmode) { + s = chmod(file, fmode); + printf("chmod %04o %s\n", fmode, file); + } + if(st.st_mtime != mtime) { + ut.actime = mtime; + ut.modtime = mtime; + s = utime(file, &ut); + printf("touch -m -t %s %s\n", ttime(mtime), file); + } + + return(0); +} + +int DOMretr() +{ +char *files; +int fd, s; +char *p; +FILE *fp; +char name[32]; + + if(DOcmdcheck()) + return(0); + + files = cmdargv[1]; + + if(cmdargc < 2) { + if(readline("Files: ", line2, sizeof(line2)) < 0) + return(-1); + files = line2; + } + + tmpnam(name); + + fd = open(name, O_WRONLY | O_CREAT | O_TRUNC, 0666); + + if(fd < 0) { + printf("Could not open local file %s. Error %s\n", name, strerror(errno)); + return(0); + } + + s = DOdata("NLST", files, RETR, fd); + + close(fd); + + if(s == 226 || s == 250) { + fp = fopen(name, "r"); + unlink(name); + if(fp == (FILE *)NULL) { + printf("Unable to open file listing.\n"); + return(0); + } + while(fgets(line2, sizeof(line2), fp) != (char *)NULL) { + p = line2 + strlen(line2) - 1; + if(p >= line2 && (*p == '\r' || *p == '\n')) *p-- = '\0'; + if(p >= line2 && (*p == '\r' || *p == '\n')) *p-- = '\0'; + printf("Retrieving file: %s\n", line2); fflush(stdout); + fd = open(line2, O_WRONLY | O_CREAT | O_TRUNC, 0666); + if(fd < 0) + printf("Unable to open local file %s\n", line2); + else { + s = DOdata("RETR", line2, RETR, fd); + close(fd); + if(s < 0) break; + } + } + fclose(fp); + } else + unlink(name); + + return(s); +} + +int DOappe() +{ +char *file, *remotefile; +int fd; +int s; + + if(DOcmdcheck()) + return(0); + + file = cmdargv[1]; + + if(cmdargc < 2) { + if(readline("Local File: ", line2, sizeof(line2)) < 0) + return(-1); + file = line2; + } + + if(cmdargc < 3) + remotefile = file; + else + remotefile = cmdargv[2]; + + fd = open(file, O_RDONLY); + + if(fd < 0) { + printf("Could not open local file %s. Error %s\n", file, strerror(errno)); + return(0); + } + + s = DOdata("APPE", remotefile, STOR, fd); + + close(fd); + + return(s); +} + +int DOstor() +{ +char *file, *remotefile; +int fd; +int s; + + if(DOcmdcheck()) + return(0); + + file = cmdargv[1]; + + if(cmdargc < 2) { + if(readline("Local File: ", line2, sizeof(line2)) < 0) + return(-1); + file = line2; + } + + if(cmdargc < 3) + remotefile = file; + else + remotefile = cmdargv[2]; + + fd = open(file, O_RDONLY); + + if(fd < 0) { + printf("Could not open local file %s. Error %s\n", file, strerror(errno)); + return(0); + } + + s = DOdata("STOR", remotefile, STOR, fd); + + close(fd); + + return(s); +} + +int DOrstor() +{ +char *file, *remotefile; +int fd; +int s; +off_t filesize, rmtsize; +char restart[16]; + + if(DOcmdcheck()) + return(0); + + file = cmdargv[1]; + + if(cmdargc < 2) { + if(readline("Local File: ", line2, sizeof(line2)) < 0) + return(-1); + file = line2; + } + + if(cmdargc < 3) + remotefile = file; + else + remotefile = cmdargv[2]; + + s = DOcommand("SIZE", remotefile); + + if(s != 215) + return(s); + + rmtsize = atol(reply+4); + + fd = open(file, O_RDONLY); + + if(fd < 0) { + printf("Could not open local file %s. Error %s\n", file, strerror(errno)); + return(0); + } + + if(type == TYPE_A) + filesize = asciisetsize(fd, rmtsize); + else + filesize = lseek(fd, rmtsize, SEEK_SET); + + if(filesize != rmtsize) { + printf("Could not set file start of %s\n", file); + close(fd); + return(0); + } + + sprintf(restart, "%lu", rmtsize); + + s = DOcommand("REST", restart); + + if(s != 350) { + close(fd); + return(s); + } + + s = DOdata("STOR", remotefile, STOR, fd); + + close(fd); + + return(s); +} + +int DOstou() +{ +char *file, *remotefile; +int fd; +int s; + + if(DOcmdcheck()) + return(0); + + file = cmdargv[1]; + + if(cmdargc < 2) { + if(readline("Local File: ", line2, sizeof(line2)) < 0) + return(-1); + file = line2; + } + + if(cmdargc < 3) + remotefile = file; + else + remotefile = cmdargv[2]; + + fd = open(file, O_RDONLY); + + if(fd < 0) { + printf("Could not open local file %s. Error %s\n", file, strerror(errno)); + return(0); + } + + s = DOdata("STOU", remotefile, STOR, fd); + + close(fd); + + return(s); +} + +int DOMstor() +{ +char *files; +char *name; +char *p; +int fd, s; +FILE *fp; + + if(DOcmdcheck()) + return(0); + + files = cmdargv[1]; + + if(cmdargc < 2) { + if(readline("Files: ", line2, sizeof(line2)) < 0) + return(-1); + files = line2; + } + + name = dir(files, 0); + + fp = fopen(name, "r"); + + if(fp == (FILE *)NULL) { + printf("Unable to open listing file.\n"); + return(0); + } + + while(fgets(line2, sizeof(line2), fp) != (char *)NULL) { + p = line2 + strlen(line2) - 1; + if(p >= line2 && (*p == '\r' || *p == '\n')) *p-- = '\0'; + if(p >= line2 && (*p == '\r' || *p == '\n')) *p-- = '\0'; + printf("Sending file: %s\n", line2); fflush(stdout); + fd = open(line2, O_RDONLY); + if(fd < 0) + printf("Unable to open local file %s\n", line2); + else { + s = DOdata("STOR", line2, STOR, fd); + close(fd); + if(s < 0) break; + } + } + fclose(fp); + unlink(name); + + return(s); +} + +static int asciisize(fd, filesize) +int fd; +off_t *filesize; +{ +unsigned long count; +char *p, *pp; +int cnt; + + count = 0; + + while((cnt = read(fd, buffer, sizeof(buffer))) > 0) { + p = buffer; pp = buffer + cnt; + count += cnt; + while(p < pp) + if(*p++ == '\n') + count++; + } + + if(cnt == 0) { + *filesize = count; + return(0); + } + + return(-1); +} + +static off_t asciisetsize(fd, filesize) +int fd; +off_t filesize; +{ +off_t sp; +int s; + + sp = 0; + + while(sp < filesize) { + s = read(fd, buffer, 1); + if(s < 0) + return(-1); + if(s == 0) break; + sp++; + if(*buffer == '\n') + sp++; + } + + return(sp); +} + +int DOclone() +{ +char *files; +int fd, s; +char *p; +FILE *fp; +char name[32]; + + if(DOcmdcheck()) + return(0); + + files = cmdargv[1]; + + tmpnam(name); + + fd = open(name, O_WRONLY | O_CREAT | O_TRUNC, 0666); + + if(fd < 0) { + printf("Could not open local file %s. Error %s\n", name, strerror(errno)); + return(0); + } + + s = DOdata("NLST", files, RETR, fd); + + close(fd); + + if(s == 226 || s == 250) { + fp = fopen(name, "r"); + unlink(name); + if(fp == (FILE *)NULL) { + printf("Unable to open file listing.\n"); + return(0); + } + while(fgets(line2, sizeof(line2), fp) != (char *)NULL) { + p = line2 + strlen(line2) - 1; + if(p >= line2 && (*p == '\r' || *p == '\n')) *p-- = '\0'; + if(p >= line2 && (*p == '\r' || *p == '\n')) *p-- = '\0'; + cmdargv[1] = line2; + if(cloneit(line2, 1)) { + printf("Retrieving file: %s\n", line2); fflush(stdout); + fd = open(line2, O_WRONLY | O_CREAT | O_TRUNC, 0666); + if(fd < 0) + printf("Unable to open local file %s\n", line2); + else { + s = DOdata("RETR", line2, RETR, fd); + close(fd); + if(s < 0) break; + } + s = cloneit(line2, 2); + } + } + fclose(fp); + } else + unlink(name); + + return(s); +} diff --git a/commands/ftp101/file.h b/commands/ftp101/file.h new file mode 100644 index 000000000..13b46218b --- /dev/null +++ b/commands/ftp101/file.h @@ -0,0 +1,33 @@ +/* file.h Copyright 1992-2000 by Michael Temari All Rights Reserved + * + * This file is part of ftp. + * + * + * 01/25/96 Initial Release Michael Temari, + */ + +_PROTOTYPE(int recvfile, (int fd, int fdin)); +_PROTOTYPE(int sendfile, (int fd, int fdout)); +_PROTOTYPE(int DOascii, (void)); +_PROTOTYPE(int DObinary, (void)); +_PROTOTYPE(int DOblock, (void)); +_PROTOTYPE(int DOstream, (void)); +_PROTOTYPE(int DOpwd, (void)); +_PROTOTYPE(int DOcd, (void)); +_PROTOTYPE(int DOmkdir, (void)); +_PROTOTYPE(int DOrmdir, (void)); +_PROTOTYPE(int DOdelete, (void)); +_PROTOTYPE(int DOmdtm, (void)); +_PROTOTYPE(int DOsize, (void)); +_PROTOTYPE(int DOstat, (void)); +_PROTOTYPE(int DOlist, (void)); +_PROTOTYPE(int DOnlst, (void)); +_PROTOTYPE(int DOretr, (void)); +_PROTOTYPE(int DOrretr, (void)); +_PROTOTYPE(int DOMretr, (void)); +_PROTOTYPE(int DOappe, (void)); +_PROTOTYPE(int DOstor, (void)); +_PROTOTYPE(int DOrstor, (void)); +_PROTOTYPE(int DOstou, (void)); +_PROTOTYPE(int DOMstor, (void)); +_PROTOTYPE(int DOclone, (void)); diff --git a/commands/ftp101/ftp.1 b/commands/ftp101/ftp.1 new file mode 100644 index 000000000..078b6a813 --- /dev/null +++ b/commands/ftp101/ftp.1 @@ -0,0 +1,148 @@ +.TH FTP 1 +.SH NAME +ftp \- a File Transfer Protocol client for Minix +.SH SYNOPSIS +.B ftp +.RI [ server_name ] +.SH DESCRIPTION +.B Ftp +is a File Transfer Protocol client for Minix written by Michael Temari. +.P +There are no command line options for +.B ftp +except for the optional server name, which may be either a numeric IP address +or a domain name resolvable by DNS. +.P +If a server name is specified a connection attempt will be made, and you +will be prompted for a user name and password by the remote system. +Following the login (or immediately, if no server name was specified), the +.br +.B ftp> +.br +prompt is displayed. The following commands are accepted at the prompt: +.P +Command: Description +.br +! Escape to a shell +.br +append Append a file to remote host +.br +ascii Set file transfer type to ascii +.br +binary Set file transfer type to binary +.br +block Set file transfer mode to block +.br +bye Close connection and exit +.br +cd Change directory on remote host +.br +close Close connection +.br +clone Clone a file +.br +del Remove file on remote host +.br +dir Display long form remote host directory listing +.br +exit Close connection and exit +.br +get Retrieve a file from remote host +.br +help Display this text +.br +lcd Change directory on local host +.br +ldir Display long form local host directory listing +.br +lls Display local host directory listing +.br +lmkdir Create directory on local host +.br +lpwd Display current directory on local host +.br +lrmdir Remove directory on local host +.br +ls Display remote host directory listing +.br +mget Retrieve multiple files from remote host +.br +mkdir Create directory on remote host +.br +mod Get file modification time +.br +mput Send multiple files to remote host +.br +noop Send the ftp NOOP command +.br +open Open connection to remote host +.br +pass Enter remote user password +.br +passive Toggle passive mode +.br +put Send a file to remote host +.br +putu Send a file to remote host(unique) +.br +pwd Display current directory on remote host +.br +quit Close connection and exit +.br +quote Send raw ftp command to remote host +.br +reget Restart a partial file retrieve from remote host +.br +remotehelp Display ftp commands implemented on remote host +.br +reput Restart a partial file send to remote host +.br +rm Remove file on remote host +.br +rmdir Remove directory on remote host +.br +site Send a site specific command +.br +size Get file size information +.br +status Get connection/file status information +.br +stream Set file transfer mode to stream +.br +system Get remote system type information +.br +user Enter remote user information +.br +ver Display client version information + +.SH "SEE ALSO" +.BR ftpd (8), +.BR ftpget (1). +.SH NOTES +The FTP protocol passes unencrypted usernames and passwords to clients, +so they are potentially exposed to evildoers with network sniffers. So be +wary of using this to exchange files between your own accounts. Obviously +if you have a root account on another system and the remote system will +accept a login as root this is extremely dangerous. (Many ftp servers will +not allow a connection by root). +.P +Text-mode (ASCII) transfers are the default mode, be sure to enter the +"binary" command if you are downloading a program file or a compressed +archive, in fact anything other than a text file from a machine with a +different text-file format than Minix uses. +.P +If you are behind a firewall you probably need to use passive mode to +successfully transfer files. + +.SH BUGS +None are known, but there may be some unknown ones. Version 1.00 corrects +a bug in previous versions that would append a \\r (0xd) character to file +names on the destination when an mget transfer was used in binary mode. + +.SH AUTHOR +The Minix httpd server was created by and is maintained by Michael Temari +. The earliest version was released in 1992, for use +with Michael's TNet networking extensions for Minix 1.5. +.P +Man page compiled by Al Woodhull +.\" updated 2006-06-18 diff --git a/commands/ftp101/ftp.c b/commands/ftp101/ftp.c new file mode 100644 index 000000000..20c1661b9 --- /dev/null +++ b/commands/ftp101/ftp.c @@ -0,0 +1,404 @@ +/* ftp.c Copyright 1992-2000 by Michael Temari All Rights Reserved + * + * ftp An ftp client program for use with TNET. + * + * Usage: ftp [host] + * + * Version: 0.10 06/21/92 (pre-release not yet completed) + * 0.20 07/01/92 + * 0.30 01/15/96 (Minix 1.7.1 initial release) + * 0.40 08/27/96 + * 0.50 03/08/00 + * 1.00 12/12/03 (added ver command) + * 1.01 02/07/05 + * + * Author: Michael Temari, + */ + +#include +#include +#include +#include +#include +#include + +#include "ftp.h" +#include "local.h" +#include "file.h" +#include "other.h" +#include "net.h" + +char *FtpVersion = "1.01 02/07/05"; + +int linkopen; +int loggedin; +int type; +int format; +int mode; +int structure; +int passive; +int atty; + +int cmdargc; +char *cmdargv[NUMARGS]; + +int printreply = 1; +char reply[1024]; + +_PROTOTYPE(static void makeargs, (char *buff)); +_PROTOTYPE(int DOver, (void)); +_PROTOTYPE(int DOhelp, (void)); +_PROTOTYPE(static int getline, (char *line, int len)); +_PROTOTYPE(int main, (int argc, char *argv[])); + + +static void makeargs(buff) +char *buff; +{ +int i; +char *p; + + for(i = 0; i < NUMARGS; i++) + cmdargv[i] = (char *)0; + + p = buff + strlen(buff) - 1; + while(p >= buff) + if(*p == '\r' || *p == '\n' || isspace(*p)) + *p-- = '\0'; + else + break; + + p = buff; + cmdargc = 0; + while(cmdargc < NUMARGS) { + while(*p && isspace(*p)) + p++; + if(*p == '\0') + break; + cmdargv[cmdargc++] = p; + while(*p && !isspace(*p)) { + if(cmdargc == 1) + *p = tolower(*p); + p++; + } + if(*p == '\0') + break; + *p = '\0'; + p++; + } +} + +int readline(prompt, buff, len) +char *prompt; +char *buff; +int len; +{ +char *p; + + printf(prompt); fflush(stdout); + + if(fgets(buff, len, stdin) == (char *)NULL) { + printf("\nEnd of file on input!\n"); + return(-1); + } + + p = buff + strlen(buff) - 1; + while(p >= buff) + if(*p == '\r' || *p == '\n' || isspace(*p)) + *p-- = '\0'; + else + break; + + if(!atty) { + printf("%s\n", buff); + fflush(stdout); + } + + return(0); +} + +static int getline(line, len) +char *line; +int len; +{ +int s; +int gotcr; + + /* leave room for at end for null */ + len--; + + /* got to be able to put in at least 1 character */ + if(len < 1) + return(-1); + + gotcr = 0; + while(len-- > 0) { + s = read(ftpcomm_fd, line, 1); + if(s != 1) + return(-1); + if(*line == '\n') + break; + gotcr = (*line == '\r'); + line++; + } + if(gotcr) + --line; + + *line = '\0'; + + return(0); +} + +int DOgetreply() +{ +int firsttime; +int s; +char code[4]; + + do { + firsttime = 1; + do { + if((s = getline(reply, sizeof(reply))) < 0) + return(s); + if(printreply) { + printf("%s\n", reply); + fflush(stdout); + } + if(firsttime) { + firsttime = 0; + strncpy(code, reply, 3); + code[3] = '\0'; + } + } while(strncmp(reply, code, 3) || reply[3] == '-'); + s = atoi(code); + } while(s < 200 && s != 125 && s != 150); + + return(s); +} + +int DOcmdcheck() +{ + if(!linkopen) { + printf("You must \"OPEN\" a connection first.\n"); + return(1); + } + + if(!loggedin) { + printf("You must login first.\n"); + return(1); + } + + return(0); +} + +int DOcommand(ftpcommand, ftparg) +char *ftpcommand; +char *ftparg; +{ +int s; +#if 1 +static char ss[64]; + if(*ftparg) + sprintf(ss, "%s %s\r\n", ftpcommand, ftparg); + else + sprintf(ss, "%s\r\n", ftpcommand); + + s = write(ftpcomm_fd, ss, strlen(ss)); + if(s != strlen(ss)) + return(-1); + +#else + s = write(ftpcomm_fd, ftpcommand, strlen(ftpcommand)); + if(s != strlen(ftpcommand)) + return(-1); + + if(*ftparg) { + s = write(ftpcomm_fd, " ", 1); + if(s != 1) + return(-1); + + s = write(ftpcomm_fd, ftparg, strlen(ftparg)); + if(s != strlen(ftparg)) + return(-1); + } + + s = write(ftpcomm_fd, "\r\n", 2); + if(s != 2) + return(-1); +#endif + + return(DOgetreply()); +} + +int DOver() +{ + printf("FTP Version %s\n", FtpVersion); + return(0); +} + +int DOhelp() +{ +char junk[10]; + + printf("Command: Description\n"); + printf("! Escape to a shell\n"); + printf("append Append a file to remote host\n"); + printf("ascii Set file transfer type to ascii\n"); + printf("binary Set file transfer type to binary\n"); + printf("block Set file transfer mode to block\n"); + printf("bye Close connection and exit\n"); + printf("cd Change directory on remote host\n"); + printf("close Close connection\n"); + printf("clone Clone a file\n"); + printf("del Remove file on remote host\n"); + printf("dir Display long form remote host directory listing\n"); + printf("exit Close connection and exit\n"); + printf("get Retrieve a file from remote host\n"); + printf("help Display this text\n"); + + if(readline("Press ENTER to continue... ", junk, sizeof(junk))) + return(-1); + + printf("lcd Change directory on local host\n"); + printf("ldir Display long form local host directory listing\n"); + printf("lls Display local host directory listing\n"); + printf("lmkdir Create directory on local host\n"); + printf("lpwd Display current directory on local host\n"); + printf("lrmdir Remove directory on local host\n"); + printf("ls Display remote host directory listing\n"); + printf("mget Retrieve multiple files from remote host\n"); + printf("mkdir Create directory on remote host\n"); + printf("mod Get file modification time\n"); + printf("mput Send multiple files to remote host\n"); + printf("noop Send the ftp NOOP command\n"); + + if(readline("Press ENTER to continue... ", junk, sizeof(junk))) + return(-1); + + printf("open Open connection to remote host\n"); + printf("pass Enter remote user password\n"); + printf("passive Toggle passive mode\n"); + printf("put Send a file to remote host\n"); + printf("putu Send a file to remote host(unique)\n"); + printf("pwd Display current directory on remote host\n"); + printf("quit Close connection and exit\n"); + printf("quote Send raw ftp command to remote host\n"); + printf("reget Restart a partial file retrieve from remote host\n"); + printf("remotehelp Display ftp commands implemented on remote host\n"); + printf("reput Restart a partial file send to remote host\n"); + printf("rm Remove file on remote host\n"); + printf("rmdir Remove directory on remote host\n"); + + if(readline("Press ENTER to continue... ", junk, sizeof(junk))) + return(-1); + + printf("site Send a site specific command\n"); + printf("size Get file size information\n"); + printf("status Get connection/file status information\n"); + printf("stream Set file transfer mode to stream\n"); + printf("system Get remote system type information\n"); + printf("user Enter remote user information\n"); + printf("ver Display client version information\n"); + + return(0); +} + +struct commands { + char *name; + _PROTOTYPE(int (*func), (void)); +}; + +static struct commands commands[] = { + "!", DOlshell, + "append", DOappe, + "ascii", DOascii, + "binary", DObinary, + "block", DOblock, + "bye", DOquit, + "cd", DOcd, + "close", DOclose, + "clone", DOclone, + "del", DOdelete, + "dir", DOlist, + "exit", DOquit, + "get", DOretr, + "help", DOhelp, + "lcd", DOlcd, + "ldir", DOllist, + "lls", DOlnlst, + "lmkdir", DOlmkdir, + "lpwd", DOlpwd, + "lrmdir", DOlrmdir, + "ls", DOnlst, + "mget", DOMretr, + "mkdir", DOmkdir, + "mod", DOmdtm, + "mput", DOMstor, + "noop", DOnoop, + "open", DOopen, + "pass", DOpass, + "passive", DOpassive, + "put", DOstor, + "putu", DOstou, + "pwd", DOpwd, + "quit", DOquit, + "quote", DOquote, + "reget", DOrretr, + "remotehelp", DOremotehelp, + "reput", DOrstor, + "rm", DOdelete, + "rmdir", DOrmdir, + "site", DOsite, + "size", DOsize, + "status", DOstat, + "stream", DOstream, + "system", DOsyst, + "user", DOuser, + "ver", DOver, + "", (int (*)())0 +}; + +int main(argc, argv) +int argc; +char *argv[]; +{ +int s; +struct commands *cmd; +static char buffer[128]; + + if(NETinit()) + return(-1); + + FTPinit(); + + s = 0; + + if(argc > 1) { + sprintf(buffer, "open %s ", argv[1]); + makeargs(buffer); + s = DOopen(); + if(atty && s > 0) { + sprintf(buffer, "user"); + makeargs(buffer); + s = DOuser(); + } + } + + while(s >= 0) { + s = readline("ftp>", buffer, sizeof(buffer)); + if(s < 0) break; + makeargs(buffer); + if(cmdargc == 0) continue; + for(cmd = commands; *cmd->name != '\0'; cmd++) + if(!strcmp(cmdargv[0], cmd->name)) + break; + if(*cmd->name != '\0') + s = (*cmd->func)(); + else { + s = 0; + printf("Command \"%s\" not recognized.\n", cmdargv[0]); + } + } + + return(s); +} diff --git a/commands/ftp101/ftp.h b/commands/ftp101/ftp.h new file mode 100644 index 000000000..ee7287181 --- /dev/null +++ b/commands/ftp101/ftp.h @@ -0,0 +1,39 @@ +/* ftp.h Copyright 1992-2000 by Michael Temari All Rights Reserved + * + * This file is part of ftp. + * + * + * 01/25/96 Initial Release Michael Temari, + */ + +extern int linkopen; +extern int loggedin; +extern int type; +extern int format; +extern int mode; +extern int structure; +extern int passive; +extern int atty; + +#define NUMARGS 10 +extern int cmdargc; +extern char *cmdargv[NUMARGS]; + +extern int printreply; +extern char reply[1024]; + +#define RETR 0 +#define STOR 1 + +#define TYPE_A 0 +#define TYPE_I 1 + +#define MODE_S 0 +#define MODE_B 1 + +#define MODE_B_EOF 64 + +_PROTOTYPE(int readline, (char *prompt, char *buff, int len)); +_PROTOTYPE(int DOgetreply, (void)); +_PROTOTYPE(int DOcmdcheck, (void)); +_PROTOTYPE(int DOcommand, (char *ftpcommand, char *ftparg)); diff --git a/commands/ftp101/local.c b/commands/ftp101/local.c new file mode 100644 index 000000000..08b1099ec --- /dev/null +++ b/commands/ftp101/local.c @@ -0,0 +1,137 @@ +/* local.c Copyright 1992-2000 by Michael Temari All Rights Reserved + * + * This file is part of ftp. + * + * + * 01/25/96 Initial Release Michael Temari, + */ + +#include +#include +#include +#include +#include +#include +#include + +#include "ftp.h" +#include "local.h" + +static char line2[512]; + +_PROTOTYPE(static void dodir, (char *path, int full)); + +int DOlpwd() +{ + if(getcwd(line2, sizeof(line2)) == (char *)NULL) + printf("Could not determine local directory. %s\n", strerror(errno)); + else + printf("Current local directory: %s\n", line2); + + return(0); +} + +int DOlcd() +{ +char *path; +int s; + + path = cmdargv[1]; + + if(cmdargc < 2) { + if(readline("Path: ", line2, sizeof(line2)) < 0) + return(-1); + path = line2; + } + + if(chdir(path)) + printf("Could not change local directory. %s\n", strerror(errno)); + else + return(DOlpwd()); + + return(0); +} + +int DOlmkdir() +{ +char *path; +int s; + + path = cmdargv[1]; + + if(cmdargc < 2) { + if(readline("Path: ", line2, sizeof(line2)) < 0) + return(-1); + path = line2; + } + + if(mkdir(path, 0777)) + printf("Could not make directory %s. %s\n", path, strerror(errno)); + else + printf("Directory created.\n"); + + return(0); +} + +int DOlrmdir() +{ +char *path; +int s; + + path = cmdargv[1]; + + if(cmdargc < 2) { + if(readline("Path: ", line2, sizeof(line2)) < 0) + return(-1); + path = line2; + } + + if(rmdir(path)) + printf("Could not remove directory %s. %s\n", path, strerror(errno)); + else + printf("Directory removed.\n"); + + return(0); +} + +int DOllist(void) +{ + dodir(".", 1); + + return(0); +} + +int DOlnlst(void) +{ + dodir(".", 0); + + return(0); +} + +int DOlshell(void) +{ + (void) system("$SHELL"); + + return(0); +} + +static void dodir(path, full) +char *path; +int full; +{ +static char cmd[128]; +static char name[32]; + + (void) tmpnam(name); + + if(full) + sprintf(cmd, "ls -l %s > %s", path, name); + else + sprintf(cmd, "ls %s > %s", path, name); + + (void) system(cmd); + sprintf(cmd, "more %s", name); + (void) system(cmd); + sprintf(cmd, "rm %s", name); + (void) system(cmd); +} diff --git a/commands/ftp101/local.h b/commands/ftp101/local.h new file mode 100644 index 000000000..c55a8a9ad --- /dev/null +++ b/commands/ftp101/local.h @@ -0,0 +1,15 @@ +/* local.h Copyright 1992-2000 by Michael Temari All Rights Reserved + * + * This file is part of ftp. + * + * + * 01/25/96 Initial Release Michael Temari, + */ + +_PROTOTYPE(int DOlpwd, (void)); +_PROTOTYPE(int DOlcd, (void)); +_PROTOTYPE(int DOlmkdir, (void)); +_PROTOTYPE(int DOlrmdir, (void)); +_PROTOTYPE(int DOllist, (void)); +_PROTOTYPE(int DOlnlst, (void)); +_PROTOTYPE(int DOlshell, (void)); diff --git a/commands/ftp101/net.c b/commands/ftp101/net.c new file mode 100644 index 000000000..b2c9e87dc --- /dev/null +++ b/commands/ftp101/net.c @@ -0,0 +1,567 @@ +/* net.c Copyright 1992-2000 by Michael Temari All Rights Reserved + * + * This file is part of ftp. + * + * + * 01/25/96 Initial Release Michael Temari, + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "ftp.h" +#include "xfer.h" +#include "net.h" + +_PROTOTYPE(void donothing, (int sig)); + +int ftpcomm_fd; +static ipaddr_t myip; +static ipaddr_t hostip; +static char host[256]; +static int lpid; + +int NETinit() +{ +int s; +char *tcp_device; +int tcp_fd; +nwio_tcpconf_t nwio_tcpconf; + + /* All this just to get our ip address */ + + if((tcp_device = getenv("TCP_DEVICE")) == (char *)NULL) + tcp_device = TCP_DEVICE; + + tcp_fd = open(tcp_device, O_RDWR); + if(tcp_fd < 0) { + perror("ftp: Could not open tcp_device"); + return(-1); + } + s = ioctl(tcp_fd, NWIOGTCPCONF, &nwio_tcpconf); + if(s < 0) { + perror("ftp: Could not get tcp configuration"); + return(-1); + } + + myip = nwio_tcpconf.nwtc_locaddr; + + close(tcp_fd); + + return(0); +} + +int DOopen() +{ +nwio_tcpconf_t tcpconf; +nwio_tcpcl_t tcpcopt; +char *tcp_device; +tcpport_t port; +int s; +struct hostent *hp; +struct servent *servent; + + if(linkopen) { + printf("Use \"CLOSE\" to close the connection first.\n"); + return(0); + } + + if(cmdargc < 2) { + if(readline("Host: ", host, sizeof(host)) < 0) + return(-1); + } else + strncpy(host, cmdargv[1], sizeof(host)); + + if((servent = getservbyname("ftp", "tcp")) == (struct servent *)NULL) { + fprintf(stderr, "ftp: Could not find ftp tcp service\n"); + port = htons(21); + } else + port = (tcpport_t)servent->s_port; + + hp = gethostbyname(host); + if(hp == (struct hostent *)NULL) { + hostip = (ipaddr_t)0; + printf("Unresolved host %s\n", host); + return(0); + } else + memcpy((char *) &hostip, (char *) hp->h_addr, hp->h_length); + + /* This HACK allows the server to establish data connections correctly */ + /* when using the loopback device to talk to ourselves */ + if((hostip & NTOHL(0xFF000000)) == inet_addr("127.0.0.0")) + hostip = myip; + + if((tcp_device = getenv("TCP_DEVICE")) == NULL) + tcp_device = "/dev/tcp"; + + if((ftpcomm_fd = open(tcp_device, O_RDWR)) < 0) { + perror("ftp: open error on tcp device"); + return(-1); + } + + tcpconf.nwtc_flags = NWTC_LP_SEL | NWTC_SET_RA | NWTC_SET_RP; + tcpconf.nwtc_remaddr = hostip; + tcpconf.nwtc_remport = port; + + s = ioctl(ftpcomm_fd, NWIOSTCPCONF, &tcpconf); + if(s < 0) { + perror("ftp: ioctl error on NWIOSTCPCONF"); + close(ftpcomm_fd); + return(-1); + } + + tcpcopt.nwtcl_flags = 0; + + s = ioctl(ftpcomm_fd, NWIOTCPCONN, &tcpcopt); + if(s < 0) { + perror("ftp: ioctl error on NWIOTCPCONN"); + close(ftpcomm_fd); + return(-1); + } + + s = ioctl(ftpcomm_fd, NWIOGTCPCONF, &tcpconf); + if(s < 0) { + perror("ftp: ioctl error on NWIOGTCPCONF"); + close(ftpcomm_fd); + return(-1); + } + + s = DOgetreply(); + + if(s < 0) { + close(ftpcomm_fd); + return(s); + } + + if(s != 220) { + close(ftpcomm_fd); + return(0); + } + + linkopen = 1; + + return(s); +} + +int DOclose() +{ + if(!linkopen) { + printf("You can't close a connection that isn't open.\n"); + return(0); + } + + close(ftpcomm_fd); + + linkopen = 0; + loggedin = 0; + + return(0); +} + +int DOquit() +{ +int s; + + if(linkopen) { + s = DOcommand("QUIT", ""); + s = DOclose(); + } + + printf("FTP done.\n"); + + exit(0); +} + +void donothing(sig) +int sig; +{ +} + +int DOdata(datacom, file, direction, fd) +char *datacom; +char *file; +int direction; /* RETR or STOR */ +int fd; +{ +nwio_tcpconf_t tcpconf; +nwio_tcpcl_t tcplopt, tcpcopt; +char *tcp_device; +static int ftpdata_fd = -1; +char *buff; +ipaddr_t ripaddr; +tcpport_t rport; +static tcpport_t lport = HTONS(0xF000); +int s; +int i; +int wpid; +int cs; +int pfd[2]; +char dummy; +char port[32]; +int wasopen; + +#ifdef DEBUG + printf("DOdata %s %s %d %d\n", datacom, file, direction, fd); +#endif + + ripaddr = hostip; + rport = HTONS(20); + + /* here we set up a connection to listen on if not passive mode */ + /* otherwise we use this to connect for passive mode */ + + if((tcp_device = getenv("TCP_DEVICE")) == NULL) + tcp_device = "/dev/tcp"; + + if(ftpdata_fd >= 0 && mode != MODE_B) { + close(ftpdata_fd); + ftpdata_fd = -1; + } + + wasopen = (ftpdata_fd >= 0); + +#ifdef DEBUG + printf("wasopen = %d\n", wasopen); +#endif + + if(wasopen) + goto WASOPEN; + +#ifdef DEBUG + printf("b4 open = %d\n", ftpdata_fd); +#endif + + if(ftpdata_fd == -1) + if((ftpdata_fd = open(tcp_device, O_RDWR)) < 0) { + perror("ftp: open error on tcp device"); + return(0); + } + +#ifdef DEBUG + printf("at open = %d\n", ftpdata_fd); +#endif + + if(passive) { +#ifdef DEBUG + printf("b4 PASV command\n"); +#endif + s = DOcommand("PASV", ""); +#ifdef DEBUG + printf("PASV command returned %d\n", s); +#endif + if(s != 227) { + close(ftpdata_fd); + ftpdata_fd = -1; + return(s); + } + /* decode host and port */ + buff = reply; + while(*buff && (*buff != '(')) buff++; + buff++; + ripaddr = (ipaddr_t)0; + for(i = 0; i < 4; i++) { + ripaddr = (ripaddr << 8) + (ipaddr_t)atoi(buff); + if((buff = strchr(buff, ',')) == (char *)0) { + printf("Could not parse PASV reply\n"); + return(0); + } + buff++; + } + rport = (tcpport_t)atoi(buff); + if((buff = strchr(buff, ',')) == (char *)0) { + printf("Could not parse PASV reply\n"); + return(0); + } + buff++; + rport = (rport << 8) + (tcpport_t)atoi(buff); + ripaddr = ntohl(ripaddr); + rport = ntohs(rport); +#ifdef DEBUG + printf("PASV %08x %04x\n", ripaddr, rport); +#endif + } + + while(1) { + tcpconf.nwtc_flags = NWTC_SET_RA | NWTC_SET_RP; + if(passive || ntohs(lport) >= 0xF000) { + tcpconf.nwtc_flags |= NWTC_LP_SEL; + } else { + /* For no good reason Sun hosts don't like it if they have to + * connect to the same port twice in a short time... + */ + lport = htons(ntohs(lport) + 1); + tcpconf.nwtc_flags |= NWTC_LP_SET; + tcpconf.nwtc_locport = lport; + } + + tcpconf.nwtc_remaddr = ripaddr; + tcpconf.nwtc_remport = rport; + +#ifdef DEBUG + printf("b4 STCPCONF locport = %d\n", lport); +#endif + + s = ioctl(ftpdata_fd, NWIOSTCPCONF, &tcpconf); +#ifdef DEBUG + printf("at STCPCONF %d %d\n", s, errno); +#endif + if(s < 0) { + if(errno == EADDRINUSE) continue; + perror("ftp: ioctl error on NWIOSTCPCONF"); + close(ftpdata_fd); + ftpdata_fd = -1; + return(0); + } + break; + } + +#ifdef DEBUG + printf("b4 GTCPCONF\n"); +#endif + s = ioctl(ftpdata_fd, NWIOGTCPCONF, &tcpconf); +#ifdef DEBUG + printf("at GTCPCONF %d %d\n", s, errno); +#endif + if(s < 0) { + perror("ftp: ioctl error on NWIOGTCPCONF"); + close(ftpdata_fd); + ftpdata_fd = -1; + return(0); + } + lport = tcpconf.nwtc_locport; + +#ifdef DEBUG + printf("lport = %04x\n", lport); +#endif + + if(passive) { + /* passive mode we connect to them */ + tcplopt.nwtcl_flags = 0; +#ifdef DEBUG + printf("Doing TCPCONN\n"); +#endif + s = ioctl(ftpdata_fd, NWIOTCPCONN, &tcpcopt); +#ifdef DEBUG + printf("TCPCONN %d %d\n", s, errno); +#endif + if(s < 0) { + perror("ftp: error on ioctl NWIOTCPCONN"); + close(ftpdata_fd); + ftpdata_fd = -1; + return(0); + } +#ifdef DEBUG + printf("GTCPCONF\n"); +#endif + s = ioctl(ftpdata_fd, NWIOGTCPCONF, &tcpconf); +#ifdef DEBUG + printf("GTCPCONF %d %d\n", s, errno); +#endif + if(s < 0) { + perror("ftp: error on ioctl NWIOGTCPCONF"); + close(ftpdata_fd); + ftpdata_fd = -1; + return(0); + } + } else { + /* we listen for them */ + tcplopt.nwtcl_flags = 0; +#ifdef DEBUG + printf("Listen\n"); +#endif + + if (pipe(pfd) < 0) { + perror("ftp: could not create a pipe"); + close(ftpdata_fd); + ftpdata_fd = -1; + return(0); + } + lpid = fork(); + if(lpid < 0) { + perror("ftp: could not fork listener"); + close(ftpdata_fd); + ftpdata_fd = -1; + close(pfd[0]); + close(pfd[1]); + return(0); + } else if(lpid == 0) { +#ifdef DEBUG + printf("Child here\n"); +#endif + close(pfd[0]); + signal(SIGALRM, donothing); + alarm(15); + close(pfd[1]); +#ifdef DEBUG + printf("child TCPLISTEN\n"); +#endif + s = ioctl(ftpdata_fd, NWIOTCPLISTEN, &tcplopt); + alarm(0); +#ifdef DEBUG + printf("listen %d %d\n", s, errno); +#endif + if(s < 0) + exit(errno); /* error */ + else + exit(0); /* connection made */ + } +#ifdef DEBUG + printf("Fork = %d\n", lpid); +#endif + /* Wait for the pipe to close, then the listener is ready (almost). */ + close(pfd[1]); + (void) read(pfd[0], &dummy, 1); + close(pfd[0]); + while(1) { + wpid = waitpid(lpid, &cs, WNOHANG); +#ifdef DEBUG + printf("waitpid %d %d\n", wpid, cs); + printf("GTCPCONF loop\n"); +#endif + if(wpid != 0) break; + signal(SIGALRM, donothing); + alarm(1); + s = ioctl(ftpdata_fd, NWIOGTCPCONF, &tcpconf); + alarm(0); +#ifdef DEBUG + printf("GTCPCONF loop %d %d\n", s, errno); +#endif + if(s == -1) break; + sleep(1); + } +#ifdef DEBUG + printf("GTCPCONF = %d\n", s); +#endif + } + +#define hiword(x) ((u16_t)((x) >> 16)) +#define loword(x) ((u16_t)(x & 0xffff)) +#define hibyte(x) (((x) >> 8) & 0xff) +#define lobyte(x) ((x) & 0xff) + + if(!passive) { + if(wpid != 0) { + close(ftpdata_fd); + ftpdata_fd = -1; + cs = (cs >> 8) & 0x00ff; + printf("Child listener error %s\n", strerror(cs)); + return(0); + } + sprintf(port, "%u,%u,%u,%u,%u,%u", + hibyte(hiword(ntohl(myip))), lobyte(hiword(ntohl(myip))), + hibyte(loword(ntohl(myip))), lobyte(loword(ntohl(myip))), + hibyte(ntohs(lport)), lobyte(ntohs(lport))); +#ifdef DEBUG + printf("sending port command %s\n", port); +#endif + s = DOcommand("PORT", port); +#ifdef DEBUG + printf("port command = %d\n", s); +#endif + if(s != 200) { + close(ftpdata_fd); + ftpdata_fd = -1; + kill(lpid, SIGKILL); + (void) wait(&cs); + return(s); + } + } + +WASOPEN: + +#ifdef DEBUG + printf("doing data command %s %s\n", datacom, file); +#endif + s = DOcommand(datacom, file); +#ifdef DEBUG + printf("do command reply %d\n", s); +#endif + if(s == 125 || s == 150) { + if(!passive && !wasopen) { + while(1) { +#ifdef DEBUG + printf("Waiting for child %d\n", lpid); +#endif + s = wait(&cs); +#ifdef DEBUG + printf("Wait returned %d cs=%d errno=%d\n", s, cs, errno); +#endif + if(s < 0 || s == lpid) + break; + } + if(s < 0) { + perror("wait error:"); + close(ftpdata_fd); + ftpdata_fd = -1; + kill(lpid, SIGKILL); + (void) wait(&cs); + return(s); + } + if((cs & 0x00ff)) { + printf("Child listener failed %04x\n", cs); + close(ftpdata_fd); + ftpdata_fd = -1; + return(-1); + } + cs = (cs >> 8) & 0x00ff; + if(cs) { + printf("Child listener error %s\n", strerror(cs)); + close(ftpdata_fd); + ftpdata_fd = -1; + return(DOgetreply()); + } + } +#ifdef DEBUG + printf("Before recvfile/sendfile call\n"); +#endif + switch(direction) { + case RETR: + s = recvfile(fd, ftpdata_fd); + break; + case STOR: + s = sendfile(fd, ftpdata_fd); + break; + } +#ifdef DEBUG + printf("send/recieve %d\n", s); +#endif + if(mode != MODE_B) { + close(ftpdata_fd); + ftpdata_fd = -1; + } + + s = DOgetreply(); +#ifdef DEBUG + printf("send/recieve reply %d\n", s); +#endif + if(mode == MODE_B && s == 226) { + close(ftpdata_fd); + ftpdata_fd = -1; + } + } else { + if(!passive) { + kill(lpid, SIGKILL); + (void) wait(&cs); + } + close(ftpdata_fd); + ftpdata_fd = -1; + } + + return(s); +} diff --git a/commands/ftp101/net.h b/commands/ftp101/net.h new file mode 100644 index 000000000..ffe343903 --- /dev/null +++ b/commands/ftp101/net.h @@ -0,0 +1,15 @@ +/* net.h Copyright 1992-2000 by Michael Temari All Rights Reserved + * + * This file is part of ftp. + * + * + * 01/25/96 Initial Release Michael Temari, + */ + +_PROTOTYPE(int NETinit, (void)); +_PROTOTYPE(int DOopen, (void)); +_PROTOTYPE(int DOclose, (void)); +_PROTOTYPE(int DOquit, (void)); +_PROTOTYPE(int DOdata, (char *datacom, char *file, int direction, int fd)); + +extern int ftpcomm_fd; diff --git a/commands/ftp101/other.c b/commands/ftp101/other.c new file mode 100644 index 000000000..8718da757 --- /dev/null +++ b/commands/ftp101/other.c @@ -0,0 +1,173 @@ +/* other.c Copyright 1992-2000 by Michael Temari All Rights Reserved + * + * ftp An ftp client program for use with TNET. + * + * Author: Michael Temari, + */ + +#include +#include +#include +#include +#include +#include +#include + +#include "ftp.h" +#include "other.h" + +_PROTOTYPE(static int docmdargs, (char *cmd, int fa)); + +void FTPinit() +{ + linkopen = 0; + loggedin = 0; + type = TYPE_A; + format = 0; + mode = MODE_S; + structure = 0; + passive = 0; + atty = isatty(0); +} + +int DOpass() +{ +int s; +struct termios oldtty, newtty; +char *pass; +char password[64]; + + if(!linkopen) { + printf("You must \"OPEN\" a connection first.\n"); + return(0); + } + + pass = cmdargv[1]; + + s = 0; + + if(cmdargc < 2) { + if(atty) { + tcgetattr(fileno(stdout), &oldtty); + newtty = oldtty; + newtty.c_lflag &= ~ECHO; + tcsetattr(fileno(stdout), TCSANOW, &newtty); + } + s = readline("Password: ", password, sizeof(password)); + if(atty) { + tcsetattr(fileno(stdout), TCSANOW, &oldtty); + printf("\n"); + } + pass = password; + } + + if(s < 0) + return(-1); + + s = DOcommand("PASS", pass); + + if(s == 230) + loggedin = 1; + + return(s); +} + +int DOuser() +{ +char *user; +int s; +char username[32]; + + if(!linkopen) { + printf("You must \"OPEN\" a connection first.\n"); + return(0); + } + + loggedin = 0; + + user = cmdargv[1]; + + s = 0; + + if(cmdargc < 2) { + if(readline("Username: ", username, sizeof(username)) < 0) + return(-1); + user = username; + } + + s = DOcommand("USER", user); + + if(atty && s == 331) { + cmdargv[0] = "password"; + cmdargc = 1; + return(DOpass()); + } + + if(s == 230) + loggedin = 1; + + return(s); +} + +int DOnoop() +{ + if(DOcmdcheck()) + return(0); + + return(DOcommand("NOOP", "")); +} + +int DOpassive() +{ + passive = 1 - passive; + + printf("Passive mode is now %s\n", (passive ? "ON" : "OFF")); + + return(0); +} + +int DOsyst() +{ + if(DOcmdcheck()) + return(0); + + return(DOcommand("SYST", "")); +} + +int DOremotehelp() +{ + if(!linkopen) { + printf("You must \"OPEN\" a connection first.\n"); + return(0); + } + + return(DOcommand("HELP", "")); +} + +static int docmdargs(cmd, fa) +char *cmd; +int fa; +{ +int i; +static char args[512]; + + args[0] = '\0'; + + for(i = fa; i < cmdargc; i++) { + if(i != fa) + strcat(args, " "); + strcat(args, cmdargv[i]); + } + + return(DOcommand(cmd, args)); +} + +int DOquote() +{ + return(docmdargs(cmdargv[1], 2)); +} + +int DOsite() +{ + return(docmdargs("SITE", 1)); +} diff --git a/commands/ftp101/other.h b/commands/ftp101/other.h new file mode 100644 index 000000000..e8b46f08a --- /dev/null +++ b/commands/ftp101/other.h @@ -0,0 +1,17 @@ +/* other.h Copyright 1992-2000 by Michael Temari All Rights Reserved + * + * This file is part of ftp. + * + * + * 01/25/96 Initial Release Michael Temari, + */ + +_PROTOTYPE(void FTPinit, (void)); +_PROTOTYPE(int DOpass, (void)); +_PROTOTYPE(int DOuser, (void)); +_PROTOTYPE(int DOnoop, (void)); +_PROTOTYPE(int DOpassive, (void)); +_PROTOTYPE(int DOsyst, (void)); +_PROTOTYPE(int DOremotehelp, (void)); +_PROTOTYPE(int DOquote, (void)); +_PROTOTYPE(int DOsite, (void)); diff --git a/commands/ftp101/xfer.c b/commands/ftp101/xfer.c new file mode 100644 index 000000000..35d9f6979 --- /dev/null +++ b/commands/ftp101/xfer.c @@ -0,0 +1,305 @@ +/* xfer.c Copyright 1992-2000 by Michael Temari All Rights Reserved + * + * This file is part of ftp. + * + * + * 03/14/00 Initial Release Michael Temari, + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "ftp.h" +#include "xfer.h" + +_PROTOTYPE(static int asciisend, (int fd, int fdout)); +_PROTOTYPE(static int binarysend, (int fd, int fdout)); +_PROTOTYPE(static int asciirecv, (int fd, int fdin)); +_PROTOTYPE(static int binaryrecv, (int fd, int fdin)); + +#if (__WORD_SIZE == 4) +static char buffer[8192]; +static char bufout[8192]; +#else +static char buffer[2048]; +static char bufout[2048]; +#endif + +static int asciisend(fd, fdout) +int fd; +int fdout; +{ +int s, len; +char c; +char *p; +char *op, *ope; +unsigned long total=0L; +char block[3]; + + if(atty) { + printf("Sent "); + fflush(stdout); + } + + op = bufout; + ope = bufout + sizeof(bufout) - 3; + + while((s = read(fd, buffer, sizeof(buffer))) > 0) { + total += (long)s; + p = buffer; + while(s-- > 0) { + c = *p++; + if(c == '\n') { + *op++ = '\r'; + total++; + } + *op++ = c; + if(op >= ope) { + if(mode == MODE_B) { + block[0] = '\0'; + *(u16_t *)&block[1] = htons(op - bufout); + write(fdout, block, sizeof(block)); + } + write(fdout, bufout, op - bufout); + op = bufout; + } + } + if(atty) { + printf("%8lu bytes\b\b\b\b\b\b\b\b\b\b\b\b\b\b", total); + fflush(stdout); + } + } + if(op > bufout) { + if(mode == MODE_B) { + block[0] = MODE_B_EOF; + *(u16_t *)&block[1] = htons(op - bufout); + write(fdout, block, sizeof(block)); + } + write(fdout, bufout, op - bufout); + } else if(mode == MODE_B) { + block[0] = MODE_B_EOF; + *(u16_t *)&block[1] = htons(0); + write(fdout, block, sizeof(block)); + s = 0; + } + if(atty) { + printf("\n"); + fflush(stdout); + } + + return(s); +} + +static int binarysend(fd, fdout) +int fd; +int fdout; +{ +int s; +unsigned long total=0L; +char block[3]; + + if(atty) { + printf("Sent "); + fflush(stdout); + } + + while((s = read(fd, buffer, sizeof(buffer))) > 0) { + if(mode == MODE_B) { + block[0] = MODE_B_EOF; + *(u16_t *)&block[1] = htons(s); + write(fdout, block, sizeof(block)); + } + write(fdout, buffer, s); + total += (long)s; + if(atty) { + printf("%8lu bytes\b\b\b\b\b\b\b\b\b\b\b\b\b\b", total); + fflush(stdout); + } + } + if(mode == MODE_B) { + block[0] = MODE_B_EOF; + *(u16_t *)&block[1] = htons(0); + write(fdout, block, sizeof(block)); + s = 0; + } + if(atty) { + printf("\n"); + fflush(stdout); + } + + return(s); +} + +int sendfile(fd, fdout) +int fd; +int fdout; +{ +int s; + + switch(type) { + case TYPE_A: + s = asciisend(fd, fdout); + break; + default: + s = binarysend(fd, fdout); + } + + if(s < 0) + return(-1); + else + return(0); +} + +static int asciirecv(fd, fdin) +int fd; +int fdin; +{ +int s, len; +int gotcr; +char c; +char *p; +char *op, *ope; +unsigned long total=0L; +char block[3]; +unsigned short cnt; + + if(isatty && fd > 2) { + printf("Received "); + fflush(stdout); + } + gotcr = 0; + op = bufout; ope = bufout + sizeof(bufout) - 3; + cnt = 0; + while(1) { + if(mode != MODE_B) + cnt = sizeof(buffer); + else + if(cnt == 0) { + s = read(fdin, block, sizeof(block)); + cnt = ntohs(*(u16_t *)&block[1]); + s = 0; + if(cnt == 0 && block[0] & MODE_B_EOF) + break; + } + s = read(fdin, buffer, cnt > sizeof(buffer) ? sizeof(buffer) : cnt); + if(s <= 0) break; + cnt -= s; + total += (long)s; + p = buffer; + while(s-- > 0) { + c = *p++; + if(gotcr) { + gotcr = 0; + if(c != '\n') + *op++ = '\r'; + } + if(c == '\r') + gotcr = 1; + else + *op++ = c; + if(op >= ope) { + write(fd, bufout, op - bufout); + op = bufout; + } + } + if(atty && fd > 2) { + printf("%8lu bytes\b\b\b\b\b\b\b\b\b\b\b\b\b\b", total); + fflush(stdout); + } + if(cnt == 0 && mode == MODE_B && block[0] & MODE_B_EOF) { + s = 0; + break; + } + } + if(gotcr) + *op++ = '\r'; + if(op > bufout) + write(fd, bufout, op - bufout); + if(atty && fd > 2) { + printf("\n"); + fflush(stdout); + } + if((mode == MODE_B && cnt != 0) || s != 0) + return(-1); + else + return(0); +} + +static binaryrecv(fd, fdin) +int fd; +int fdin; +{ +int s; +unsigned long total=0L; +char block[3]; +unsigned short cnt; + + if(atty && fd > 2) { + printf("Received "); + fflush(stdout); + } + cnt = 0; + while(1) { + if(mode != MODE_B) + cnt = sizeof(buffer); + else + if(cnt == 0) { + s = read(fdin, block, sizeof(block)); + cnt = ntohs(*(u16_t *)&block[1]); + s = 0; + if(cnt == 0 && block[0] & MODE_B_EOF) + break; + } + s = read(fdin, buffer, cnt > sizeof(buffer) ? sizeof(buffer) : cnt); + if(s <= 0) break; + cnt -= s; + total += (long)s; + write(fd, buffer, s); + if(atty && fd > 2) { + printf("%8lu bytes\b\b\b\b\b\b\b\b\b\b\b\b\b\b", total); + fflush(stdout); + } + if(cnt == 0 && mode == MODE_B && block[0] & MODE_B_EOF) { + s = 0; + break; + } + } + if(atty && fd > 2) { + printf("\n"); + fflush(stdout); + } + if((mode == MODE_B && cnt != 0) || s != 0) + return(-1); + else + return(0); +} + +int recvfile(fd, fdin) +int fd; +int fdin; +{ +int s; + + switch(type) { + case TYPE_A: + s = asciirecv(fd, fdin); + break; + default: + s = binaryrecv(fd, fdin); + } + + if(s < 0) + return(-1); + else + return(0); +} diff --git a/commands/ftp101/xfer.h b/commands/ftp101/xfer.h new file mode 100644 index 000000000..696eaccde --- /dev/null +++ b/commands/ftp101/xfer.h @@ -0,0 +1,8 @@ +/* xfer.h Copyright 1992-2000 by Michael Temari All Rights Reserved + * + * This file is part of ftp. + * + */ + +_PROTOTYPE(int recvfile, (int fd, int fdin)); +_PROTOTYPE(int sendfile, (int fd, int fdout)); diff --git a/commands/httpd0995/Makefile b/commands/httpd0995/Makefile new file mode 100644 index 000000000..19b52c4cb --- /dev/null +++ b/commands/httpd0995/Makefile @@ -0,0 +1,65 @@ +# Makefile for httpd +# +# 02/17/1996 Michael Temari +# 07/07/1996 Initial Release Michael Temari +# 12/29/2002 Michael Temari +# 07/07/2003 Al Woodhull +# + +CFLAGS= -O -D_MINIX -D_POSIX_SOURCE -DDAEMON=1 +#CFLAGS= -O -D_MINIX -D_POSIX_SOURCE -DDAEMON=1 -DDEBUG=9 +LDFLAGS=-i +BINDIR=/usr/local/bin + +HTTPD_OBJS= httpd.o utility.o request.o process.o reply.o \ + police.o cgiexec.o net.o config.o pass.o proxy.o +DIR2HTML_OBJS= dir2html.o + +all: httpd dir2html + +httpd: $(HTTPD_OBJS) + $(CC) $(LDFLAGS) -o $@ $(HTTPD_OBJS) + install -S 16kw $@ + +dir2html: $(DIR2HTML_OBJS) + $(CC) $(LDFLAGS) -o $@ $(DIR2HTML_OBJS) + install -S 8kw $@ + +clean: + rm -f httpd $(HTTPD_OBJS) dir2html $(DIR2HTML_OBJS) *.bak + +install: $(BINDIR)/httpd $(BINDIR)/in.httpd $(BINDIR)/dir2html + +tar: + tar cvf ../httpd.tar Makefile README *.c *.h *.sh *.conf *.mtype + +$(BINDIR)/httpd: httpd + install -cs -o bin $? $@ + +$(BINDIR)/in.httpd: $(BINDIR)/httpd + install -l $? $@ + +$(BINDIR)/dir2html: dir2html + install -cs -o bin $? $@ + +httpd.o: httpd.c http.h utility.h net.h config.h +utility.o: utility.c utility.h config.h +request.o: request.c http.h utility.h config.h +process.o: process.c http.h utility.h +reply.o: reply.c http.h utility.h net.h config.h +police.o: police.c http.h utility.h config.h pass.h +cgiexec.o: cgiexec.c http.h config.h +net.o: net.c net.h +config.o: config.c utility.h config.h +pass.o: pass.c pass.h +proxy.o: proxy.c http.h +dir2html.o: dir2html.c + +installman: + mkdir -p /usr/local/man/man5 + mkdir -p /usr/local/man/man8 + cp -p httpd.conf.5 http_status.5 /usr/local/man/man5 + cp -p httpd.8 /usr/local/man/man8 + makewhatis /usr/local/man + + diff --git a/commands/httpd0995/README b/commands/httpd0995/README new file mode 100644 index 000000000..c5574012f --- /dev/null +++ b/commands/httpd0995/README @@ -0,0 +1,252 @@ +httpd documentation 7/16/96 by Michael Temari +updated 2006-06-01 by Al Woodhull + +DISCLAIMER: + +Use at own risk etc... + + +COMMENTS: + +Please send me any bug reports, comments, questions, etc... My email +address is Michael@TemWare.Com + + +BACKGROUND: + +httpd is a World Wide Web (WWW) server. I wrote it from scratch so +the setup and configuration will not be like other web servers though +hopefully by reading this document there will be no problems in getting +my web server up and running on your Minix system. + + +COMPILING: + +To compile httpd all you need to do is type "make" in the httpd source +directory. There should be no errors or warnings. If you should run +out of memory when compiling try adding the -m option to the CFLAGS +list in the Makefile. + + +INSTALLING: + +To install httpd all you need to do is type "make install" in the httpd +source directory. By default the place to install httpd is into +/usr/local/bin. If you would like to change this then change BINDIR in +the Makefile. Httpd will be linked to in.httpd, which is the preferred +name for a program started by the tcpd internet access control program. +The program dir2html is also installed -- this provides a directory +listing when a web client accesses a directory which does not contain a +file named index.html (or an alternative designated in /etc/httpd.conf). +The man pages are installed by typing "make installman". + +CONFIGURING: + +Before running httpd it must be configured. The name of the default +configuration file is /etc/httpd.conf or you may pass the configuration +file name to httpd. Upon starting up, httpd will parse the configuration +file and then process requests. This README file and the sample httpd.conf +may also help in configuring. The httpd.conf.5 man page presents the same +information for reference use. + + +The configuration file is an ascii file which consists of lines of the +following form: + +directive LWS [parameters separated by LWS] + +NOTE: LWS denotes Linear White Space which is spaces and/or tabs + +The following are valid configuration file directives: + serverroot redirect user chroot logfile dbgfile dirsend direxec vhost + auth proxyauth vpath include mtype + +To make the file more readable, on directives which occupy multiple +lines you may omit the directive on lines after the first and begin +these lines with LWS. + + +serverroot path + +The serverroot directive sets the translation for // to the given path. + + +redirect url + +If redirect is defined in the configuration file then all request urls +will be redirected. For example, if in the configuration file of +minix1.hampshire.edu this line appears: + redirect http://minix1.woodhull.com/ +a request for http://minix1.hampshire.edu/some/page will return a 301 error +which is a redirect permanent to http://minix1.woodhull.com/some/page. + + +user username + +The user directive causes the server to run as the given username, otherwise +the server will run as whoever started it (normally root). + + +chroot directory + +The chroot directive causes the server to chroot to the given directory after +the configuration and log files have been opened. Normally this will be the +home directory of the given username in the user directive. +NOTE: /~user will be translated to the home directory of the given user + // will be translated to the serverroot directory +NOTE: if this directive is used then beware of the consequences. + + +logfile filename + +The logfile directive tells the server where to log http transactions. +NOTE: the file must exist to enable logging + + +dbgfile filename + +The dbgfile directive tells the server where to log debug http transactions. +NOTE: the file must exist to enable logging + +dirsend filelist + +The dirsend directive tells the server that when a directory is requested +that it should send the first file that it finds in the directory from the +filelist for the request. + + +direxec program + +The direxec directive tells the server that when a directory is requested +and no file is found from the dirsend directive that it should run the +given program. +NOTE: the program normally generates a directory listing on the fly +NOTE: the program access is considered X with no access restrictions. + + +vhost hostname VhostRoot + +vhost is for defining access for virtual hosts. If none are configured then +any host is accepted. If specified then access is only granted for requests +for hosts which are configured here. In the Vpath section below the /// gets +translated to the corresponding VhostRoot. + + +auth authname authdescription access [passwdfile [users]] + +The auth directive sets up different authorizations with the server. The +authname is the name given to the authorization and is case insensitive. +The authdescription is the description of the authorization and is what +the user will see when asked to enter a username and password. The +access is one or more of (rwx). R tells the server the url can be +read. W tells the server the url can be overwritten. X tells the server +that the url can and should be executed. Access is in addition to normal +unix security considerations. For instance a file that can be written to +that does not have the W access will have an error returned. The +passwdfile is the name of the passwdfile to validate users against. If +the passwdfile is given as '.' then the system password file will be used +which is /etc/passwd. If no passwdfile is given then no authorization is +allowed for anyone. If no users are given then any validated users is +authorized, otherwise only the given users are allowed. + + +proxyauth authname authdescription access [passwdfile [users]] + +proxyauth defines any access authorization to be used for Proxy access +authname = Same as auth above +authdescription = Same as auth above +access = Must be R to allow proxy +passwdfile = Same as auth above +users = Same as auth above + + +vpath from to [auth [access]] + +The vpath directive sets up url path translations and authorizations. A +requested url that matches from will be translated to to with the given +auth and access. If auth does not exist then the url will have no access. +If access is not given then the access is taken from the auth record (see +above). A '.' in place of the to means that the server should use a +translation from another vpath record, but associate the given auth and +access with the requested url. A '*' maybe at the end only of the from +which is a wildcard match. For example if the from has /AB* then any of +/ABCDEF or /AB or /ABmichael will match, but /AD or /a will not. The +requested url is first checked against each vpath record until an exact +match (meaning url match from and from had no '*') is found or the end of +the list. Therefore a wildcard match will match the last from is the list +in which it matched. +NOTE: if at the beginning of the to field + /~user will get translated to the home directory of the given user + // wile get translated to the serverroot directory + + +include filename + +The include directive tells the server to read configuration information +from the given filename. +NOTE: normally mtype directives are included from another file + + +mtype mimetype extensions + +The mtype directive tells the server what mimetype to associate with files +which have any of the given extensions. If no match is found then the file +will be treated as application/octet-stream. +NOTE: normally you get mtype directives in included file + + + +USAGE: + + httpd [-v|-t] [configuration-file] + +The -t tells the server to just parse the configuration file so that you +can test it to see if it is the way you want it. You may also pass the +name of your configuration file if it is not the default /etc/httpd.conf. + +The -v option prints the server version and then exits. + + +STARTING: + +First of all httpd is a server and therefore you will need to start it +with tcpd. Tcpd is a program which listens for incoming TCP connections +on the passed port and when a connection comes in it forks and starts the +given daemon program. Therefore to start httpd you use: + + tcpd http /usr/local/bin/in.httpd & + +You will more than likely have this line in your /etc/rc or /etc/rc.net +file so that whenever your system is restarted the web server will also +be started. The first parameter http is the port that tcpd is going +to be listening for connections on. Here http (which should be defined +in /etc/services as 80) is the standard port for a web server. The second +parameter is the program that tcpd will fork and exec when a connection +comes in. The program will then have its stdin and stderr connected to +the client Then the web server program will start running with the tcpd +program waiting for the next connection. Currently there is no ability to +limit the number of simultaneous web servers running. NOTE: At some point +I will be adding the ability for httpd to start itself without the need of +tcpd. That way httpd will already be in memory and have parsed its +configuration file. + +In Minix 2.0.3 and later versions you may use: + + daemonize tcpd http /usr/local/bin/in.httpd + +(daemonize is a shell function defined in /usr/etc/rc which starts programs +as daemons). + + +FINAL WORDS + +I wanted to get the server out as soon as possible so I hurried up and +created this document to help out. Hopefully it will HELP more than +it HURTS. If anyone is interested in writing man pages for httpd or any +of the other network programs please let me know. + + +Michael Temari +Michael@TemWare.Com + +Please note also the SECURITY document in this directory. (asw 2003-07-05) diff --git a/commands/httpd0995/SECURITY b/commands/httpd0995/SECURITY new file mode 100644 index 000000000..88c73056c --- /dev/null +++ b/commands/httpd0995/SECURITY @@ -0,0 +1,52 @@ +SECURITY NOTE + +Al Woodhull updated 2006-06-01 + +Running a web server is fun, but it's also not without risks. If, like +many Minix users, you are a guest on someone else's network, you need +to be very careful to operate your server in ways that will not put +your system at risk or interfere with others on the net. Here are some +points to consider: + +- Be sure to touch /usr/adm/httpd.log (or whatever you specify as the log +file in httpd.conf) before you start your web server for the first time +-- nothing will be logged if the log file does not exist. Then look at +your log file frequently and be alert for any unusual activity. + +- You may also want to be sure that you have provided a /etc/serv.access +file. This file can be used to limit access only to permitted nodes or +networks, or to deny access to specified nodes or networks (see the +serv.access (5) man page). Also, even if your /etc/serv.access file is +empty, if it is present tcpd will exec its paranoid twin tcpdp, which +will refuse service if the connecting IP address cannot be associated +with a name. + +- If you enable proxy webserving, be very careful, it can be used by +people you don't know to visit sites that don't welcome visitors whose +identity is hidden. This may cause your network host and ultimately you +some unpleasantness. + +- The Minix httpd can also support CGI applications. These are also +dangerous -- a CGI application allows someone else to execute a program +on your computer. Make sure anything you allow this way cannot be +abused. Many security violations are due to effects of input that was not +expected by the original author of a program. + +- It's an understatement to say that Minix is not a well-known +operating system. There are not many Minix systems operating as +servers on the internet. A consequence of this is that there few, if +any, people engaged in finding ways to attack weaknesses in Minix. But +the idea of "security through obscurity" is deprecated by serious +computer security experts. Any operating system or program of any +degree of complexity is likely to have bugs or features that can be +exploited in ways the original programmers did not foresee. You can't +count on the "good guys" being the first ones to discover a risk. +There are two things you should be sure to do if you are running a +network server of any kind: + +(1) be alert for new versions of the program that may fix bugs +discovered by other users, and + +(2) be sure to report to the program author or maintainer anything you +observe that looks like a bug or a way the program can be misused. + diff --git a/commands/httpd0995/cgiexec.c b/commands/httpd0995/cgiexec.c new file mode 100644 index 000000000..856a51200 --- /dev/null +++ b/commands/httpd0995/cgiexec.c @@ -0,0 +1,255 @@ +/* cgiexec.c by Michael Temari 02/17/96 + * + * This file is part of httpd. + * + * 02/17/1996 Michael Temari + * 07/07/1996 Initial Release Michael Temari + * 12/29/2002 Michael Temari + * 02/08/2005 Michael Temari + * + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "http.h" +#include "config.h" +#include "net.h" + +_PROTOTYPE(char **cgienv, (struct http_request *rq, struct http_reply *rp)); +_PROTOTYPE(static int addenv, (char *name, char *value, char **buf, int *len)); + +int cgiexec(rq, rp) +struct http_request *rq; +struct http_reply *rp; +{ +struct stat st; +char *prog; +int cmdpid; +int status; +char *argv[5]; +int ifds[2]; +int ofds[2]; +static char cmd[2048]; +char **cmdenv; +int dirflag = 0; + + if(stat(rp->realurl, &st)) { + if(errno == EACCES) + rp->status = HTTP_STATUS_FORBIDDEN; + else + rp->status = HTTP_STATUS_NOT_FOUND; + strcpy(rp->statusmsg, strerror(errno)); + return(-1); + } + + if((st.st_mode & S_IFMT) == S_IFDIR) + if(direxec != NULL) { + prog = direxec; dirflag = 1; + } else + return(0); + else + prog = rp->realurl; + + /* check if prog is allowed to be exec'd */ + if(!dirflag && !(rp->urlaccess & URLA_EXEC)) + return(0); + + /* if cannot exec mode then return */ + if( (st.st_mode & S_IXUSR) == 0 && + (st.st_mode & S_IXGRP) == 0 && + (st.st_mode & S_IXOTH) == 0 ) + return(0); + + if((cmdenv = cgienv(rq, rp)) == NULL) { + rp->status = HTTP_STATUS_SERVER_ERROR; + strcpy(rp->statusmsg, "Could not setup cgi environment"); + return(-1); + } + + argv[0] = prog; + argv[1] = rp->realurl; + argv[2] = rq->url; + argv[3] = (char *)NULL; + + if(pipe(ifds) < 0) { + rp->status = HTTP_STATUS_NOT_FOUND; + strcpy(rp->statusmsg, strerror(errno)); + return(-1); + } + + if(pipe(ofds) < 0) { + rp->status = HTTP_STATUS_NOT_FOUND; + strcpy(rp->statusmsg, strerror(errno)); + close(ifds[0]); close(ifds[1]); + return(-1); + } + + if((cmdpid = fork()) < 0) { + close(ifds[0]); close(ofds[0]); + close(ifds[1]); close(ofds[1]); + rp->status = HTTP_STATUS_NOT_FOUND; + strcpy(rp->statusmsg, strerror(errno)); + return(-1); + } + + /* We don't know how much data is going to be passed back */ + rp->size = 0; + + if(cmdpid == 0) { /* Child */ +#if 0 + if((cmdpid = fork()) < 0) { + close(ifds[0]); close(ofds[0]); + close(ifds[1]); close(ofds[1]); + exit(-1); + } + if(cmdpid != 0) { + close(ifds[0]); close(ofds[0]); + close(ifds[1]); close(ofds[1]); + exit(0); + } +#endif + setsid(); + close(ifds[0]); close(ofds[1]); + dup2(ofds[0], 0); + dup2(ifds[1], 1); + dup2(ifds[1], 2); + close(ifds[1]); close(ofds[0]); + execve(argv[0], argv, cmdenv); + exit(0); + } + +#if 0 + /* Get rid of Zombie child */ + (void) wait(&status); +#endif + + close(ifds[1]); close(ofds[0]); + + rp->fd = ifds[0]; + rp->ofd = ofds[1]; + rp->pid = cmdpid; + + if(rp->urlaccess & URLA_HEADERS) + rp->headers = -1; + + return(-1); +} + +char **cgienv(rq, rp) +struct http_request *rq; +struct http_reply *rp; +{ +static char buffer[4096]; +char *p, *p2; +char **e; +int len; +char temp[20]; + + p = buffer; + len = sizeof(buffer); + + if(addenv("PATH", "/usr/local/bin:/bin:/usr/bin", &p, &len)) return(NULL); + if(getenv("TZ") != (char *)NULL) + if(addenv("TZ", getenv("TZ"), &p, &len)) return(NULL); + + /* HACK - some of these are hardcoded and should not be MAT 3/17/96 */ + + /* HTTP_ */ + + if(addenv("SERVER_SOFTWARE", "Temari httpd/1.0", &p, &len)) return(NULL); + if(addenv("SERVER_NAME", myhostname, &p, &len)) return(NULL); + if(addenv("GATEWAY_INTERFACE", "CGI/1.1", &p, &len)) return(NULL); + if(addenv("SERVER_PROTOCOL", "HTTP/1.0", &p, &len)) return(NULL); + if(rq->port) + sprintf(temp, "%u", rq->port); + else + strcpy(temp, "80"); + if(addenv("SERVER_PORT", temp, &p, &len)) return(NULL); + switch(rq->method) { + case HTTP_METHOD_GET: + if(addenv("REQUEST_METHOD", "GET", &p, &len)) return(NULL); + break; + case HTTP_METHOD_POST: + if(addenv("REQUEST_METHOD", "POST", &p, &len)) return(NULL); + break; + case HTTP_METHOD_HEAD: + if(addenv("REQUEST_METHOD", "HEAD", &p, &len)) return(NULL); + break; + case HTTP_METHOD_PUT: + if(addenv("REQUEST_METHOD", "PUT", &p, &len)) return(NULL); + break; + default: + if(addenv("REQUEST_METHOD", "UNKNOWN", &p, &len)) return(NULL); + } + if(addenv("PATH_INFO", "?", &p, &len)) return(NULL); + if(addenv("PATH_TRANSLATED", "?", &p, &len)) return(NULL); + if(addenv("SCRIPT_NAME", rq->url, &p, &len)) return(NULL); + if(addenv("QUERY_STRING", rq->query, &p, &len)) return(NULL); + if(addenv("REMOTE_HOST", rmthostname, &p, &len)) return(NULL); + if(addenv("REMOTE_ADDR", rmthostaddr, &p, &len)) return(NULL); + if(rq->authuser != (char *)NULL) + if(addenv("AUTH_USER", rq->authuser, &p, &len)) return(NULL); + /* AUTH_TYPE */ + /* REMOTE_USER */ + /* REMOTE_IDENT */ + if(rq->method == HTTP_METHOD_POST) { + if(addenv("CONTENT_TYPE", "application/x-www-form-urlencoded", &p, &len)) return(NULL); + sprintf(temp, "%lu", rq->size); + if(addenv("CONTENT_LENGTH", temp, &p, &len)) return(NULL); + } + /* COOKIE */ + if(rq->cookie[0] != '\0') + if(addenv("COOKIE", rq->cookie, &p, &len)) return(NULL); + /* HOST */ + if(addenv("HOST", rq->host, &p, &len)) return(NULL); + + if(len < 1) return(NULL); + *p++ = '\0'; + + p2 = buffer; + e = (char **)p; + while(*p2) { + if(len < sizeof(e)) return(NULL); + len -= sizeof(e); + *e++ = p2; + while(*p2) p2++; + p2++; + } + if(len < sizeof(e)) return(NULL); + *e++ = NULL; + + return((char **)p); +} + +static int addenv(name, value, buf, len) +char *name; +char *value; +char **buf; +int *len; +{ +char *p; +int size; + + p = *buf; + + size = strlen(name)+1+strlen(value)+1; + + if(size > *len) + return(-1); + + sprintf(p, "%s=%s", name, value); + + p += size; + *buf = p; + *len -= size; + + return(0); +} diff --git a/commands/httpd0995/config.c b/commands/httpd0995/config.c new file mode 100644 index 000000000..4c818745a --- /dev/null +++ b/commands/httpd0995/config.c @@ -0,0 +1,841 @@ +/* config.c by Michael Temari 02/26/96 + * + * This file is part of httpd. + * + * 02/26/1996 Michael Temari + * 07/07/1996 Initial Release Michael Temari + * 12/29/2002 Michael Temari + * 05/14/2006 Michael Temari + * + */ +#include +#include +#include +#include +#include +#include + +#include "utility.h" +#include "config.h" + +struct mtype *mtype = NULL; +struct msufx *msufx = NULL; +struct vhost *vhost = NULL; +struct vpath *vpath = NULL; +struct dirsend *dirsend = NULL; +struct auth *auth = NULL; +struct auth *proxyauth = NULL; +char *direxec = NULL; +char *srvrroot = ""; +char *Redirect = NULL; +char *LogFile = NULL; +char *DbgFile = NULL; +char *User = NULL; +char *Chroot = NULL; + +_PROTOTYPE(static int doconfig, (char *cfg_file)); +_PROTOTYPE(static int doinclude, (char *parms[], int np)); +_PROTOTYPE(static int domtype, (char *parms[], int np)); +_PROTOTYPE(static struct auth *findauth, (char *name)); +_PROTOTYPE(static int dovhost, (char *parms[], int np)); +_PROTOTYPE(static int dovpath, (char *parms[], int np)); +_PROTOTYPE(static int dosrvrroot, (char *parms[], int np)); +_PROTOTYPE(static int doredirect, (char *parms[], int np)); +_PROTOTYPE(static int dodirsend, (char *parms[], int np)); +_PROTOTYPE(static int dodirexec, (char *parms[], int np)); +_PROTOTYPE(static char *subvpath, (char *s)); +_PROTOTYPE(static int dologfile, (char *parms[], int np)); +_PROTOTYPE(static int dodbgfile, (char *parms[], int np)); +_PROTOTYPE(static int douser, (char *parms[], int np)); +_PROTOTYPE(static int dochroot, (char *parms[], int np)); +_PROTOTYPE(static int adduser, (struct auth *pauth, char *user)); +_PROTOTYPE(static int doauth, (char *parms[], int np)); +_PROTOTYPE(static int doproxyauth, (char *parms[], int np)); + +int readconfig(cfg_file, testing) +char *cfg_file; +int testing; +{ +int s; +char *cfg; +struct msufx *ps; +struct mtype *pt; +struct vhost *ph; +struct vpath *pv; +struct dirsend *pd; +struct auth *pa; + + cfg = HTTPD_CONFIG_FILE; + if(cfg_file != (char *)NULL) + if(*cfg_file) + cfg = cfg_file; + + s = doconfig(cfg); + + if(testing) { + printf("ServerRoot: %s\n", srvrroot); + printf("Redirect: %s\n", Redirect == NULL ? "" : Redirect); + printf("UserName: %s\n", User == NULL ? "" : User); + printf("Chroot: %s\n", Chroot == NULL ? "" : Chroot); + printf("LogFile: %s\n", LogFile == NULL ? "" : LogFile); + printf("DbgFile: %s\n", DbgFile == NULL ? "" : DbgFile); + printf("DirSend:"); + for(pd = dirsend; pd != NULL; pd = pd->next) + printf(" %s", pd->file); + printf("\n"); + printf("DirExec: %s\n", direxec == NULL ? "" : direxec); + for(ph = vhost; ph != NULL; ph = ph->next) + printf("VHost: %s %s\n", ph->hname, ph->root); + for(pa = auth; pa != NULL; pa = pa->next) + printf("Auth: %s %s %d %s\n", + pa->name, pa->desc, pa->urlaccess, pa->passwdfile); + for(pa = proxyauth; pa != NULL; pa = pa->next) + printf("ProxyAuth: %s %s %d %s\n", + pa->name, pa->desc, pa->urlaccess, pa->passwdfile); + for(pv = vpath; pv != NULL; pv = pv->next) + printf("Vpath: %s %s %s %d\n", + pv->from, pv->to, pv->auth->name, pv->urlaccess); + for(pt = mtype; pt != NULL; pt = pt->next) { + printf("MType: %s :", pt->mimetype); + for(ps = pt->msufx; ps != NULL; ps = ps->tnext) + printf(" '%s'", ps->suffix); + printf("\n"); + } + for(ps = msufx; ps != NULL; ps = ps->snext) + printf("Suffix: %s\t%s\n", ps->suffix, ps->mtype->mimetype); + } + + return(s); +} + +static int doconfig(cfg_file) +char *cfg_file; +{ +FILE *fp; +int np; +int s; +char *p; +char ltype[40]; +char *parms[30]; +static char buffer[2048]; + + if((fp = fopen(cfg_file, "r")) == (FILE *)NULL) { + fprintf(stderr, "httpd: Could not read %s config file.\n", cfg_file); + return(-1); + } + + *ltype = '\0'; + + while(fgets(buffer, sizeof(buffer), fp) != (char *)NULL) { + if(buffer[0] == '#') continue; /* skip comments */ + np = getparms(buffer, parms, sizeof(parms)/sizeof(parms[0])); + if(np == 0) continue; /* blank line */ + if(parms[0] == (char *)NULL) + parms[0] = ltype; + else { + p = parms[0]; + while(*p) *p++ = tolower(*p); + strncpy(ltype, parms[0], sizeof(ltype)); + } + s = 0; + if(!strcmp(parms[0], "mtype")) s = domtype(parms, np); + else + if(!strcmp(parms[0], "vhost")) s = dovhost(parms, np); + else + if(!strcmp(parms[0], "vpath")) s = dovpath(parms, np); + else + if(!strcmp(parms[0], "serverroot")) s = dosrvrroot(parms, np); + else + if(!strcmp(parms[0], "redirect")) s = doredirect(parms, np); + else + if(!strcmp(parms[0], "dirsend")) s = dodirsend(parms, np); + else + if(!strcmp(parms[0], "direxec")) s = dodirexec(parms, np); + else + if(!strcmp(parms[0], "logfile")) s = dologfile(parms, np); + else + if(!strcmp(parms[0], "dbgfile")) s = dodbgfile(parms, np); + else + if(!strcmp(parms[0], "user")) s = douser(parms, np); + else + if(!strcmp(parms[0], "chroot")) s = dochroot(parms, np); + else + if(!strcmp(parms[0], "auth")) s = doauth(parms, np); + else + if(!strcmp(parms[0], "proxyauth")) s = doproxyauth(parms, np); + else + if(!strcmp(parms[0], "include")) s = doinclude(parms, np); + else + fprintf(stderr, "httpd: Unknown directive: %s\n", parms[0]); + if(s) { + fprintf(stderr, "httpd: Error processing config file\n"); + fclose(fp); + return(-1); + } + } + + fclose(fp); + + return(0); +} + +static int doinclude(parms, np) +char *parms[]; +int np; +{ +char *p; + + if(np < 2) return(0); + + p = subvpath(parms[1]); + + return(doconfig(p)); +} + +static int domtype(parms, np) +char *parms[]; +int np; +{ +int i; +struct mtype *pt, *lpt, *newpt; +struct msufx *ps, *lps, *newps, *psend; + + if(np < 2) return(0); + + + /* check if this mime type already exists in the list */ + for(pt = mtype, lpt = NULL; pt != NULL; lpt = pt, pt = pt->next) + if(!strcmp(parms[1], pt->mimetype)) + break; + + if(pt == NULL) { /* not there so add it */ + newpt = malloc(sizeof(struct mtype)); + if(newpt == NULL) { + fprintf(stderr, "httpd: malloc failed in domtype\n"); + return(-1); + } + newpt->mimetype = malloc(strlen(parms[1])+1); + if(newpt->mimetype == NULL) { + fprintf(stderr, "httpd: malloc failed in domtype\n"); + return(-1); + } + strcpy(newpt->mimetype, parms[1]); + newpt->msufx = NULL; + newpt->next = NULL; + if(lpt == NULL) + mtype = newpt; + else + lpt->next = newpt; + } else + newpt = pt; + + /* find end of suffix list */ + for(ps = newpt->msufx, lps = NULL; ps != NULL; lps = ps, ps = ps->tnext) ; + psend = lps; + + /* if no suffix given then add empty suffix for default */ + if(np == 2) + strcpy(parms[np++], ""); + + /* add each suffix to the mime type */ + for(i = 2; i < np; i++) { + /* a suffix can only be for a single mime type */ + for(ps = msufx, lps = NULL; ps != NULL; lps = ps, ps = ps->snext) { + if(!strcmp(ps->suffix, parms[i])) { + fprintf(stderr, "httpd: Suffix already found\n"); + return(-1); + } + if(strlen(parms[i]) > strlen(ps->suffix)) break; + } + newps = malloc(sizeof(struct msufx)); + if(newps == NULL) { + fprintf(stderr, "httpd: malloc failed in domtype\n"); + return(-1); + } + newps->suffix = malloc(strlen(parms[i])+1); + if(newps->suffix == NULL) { + fprintf(stderr, "httpd: malloc failed in domtype\n"); + return(-1); + } + strcpy(newps->suffix, parms[i]); + newps->mtype = newpt; + newps->snext = NULL; + newps->tnext = NULL; + if(lps == NULL) { + msufx = newps; + newps->snext = ps; + } else { + lps->snext = newps; + newps->snext = ps; + } + if(psend == NULL) + newpt->msufx = newps; + else + psend->tnext = newps; + psend = newps; + } + + return(0); +} + +static struct auth *findauth(name) +char *name; +{ +char lname[80]; +char *p, *p2; +struct auth *a = NULL; + + if(sizeof(lname) < (strlen(name)+1)) { + fprintf(stderr, "httpd: lname too small in findauth\n"); + return(a); + } + p = name; p2 = lname; + while(*p) + *p2++ = tolower(*p++); + *p2 = '\0'; + + for(a = auth; a != NULL; a = a->next) + if(!strcmp(a->name, lname)) break; + + return(a); +} + +static int dovhost(parms, np) +char *parms[]; +int np; +{ +char *hname, *root; +struct vhost *ph, *lph, *newph; + + if(np < 2) return(0); + + hname = parms[1]; + + if(np < 3) + root = ""; + else + root = parms[2]; + + for(ph = vhost, lph = NULL; ph != NULL; lph = ph, ph = ph->next) + ; + + newph = malloc(sizeof(struct vhost)); + if(newph == NULL) { + fprintf(stderr, "httpd: malloc failed in dovhost\n"); + return(-1); + } + newph->hname = malloc(strlen(hname)+1); + if(newph->hname == NULL) { + fprintf(stderr, "httpd: malloc failed in dovhost\n"); + return(-1); + } + strcpy(newph->hname, hname); + + root = subvpath(root); + + newph->root = malloc(strlen(root)+1); + if(newph->root == NULL) { + fprintf(stderr, "httpd: malloc failed in dovhost\n"); + return(-1); + } + strcpy(newph->root, root); + + if(np > 3) + if(parms[3][0] != '#') { + fprintf(stderr, "httpd: junk at end of vhost line\n"); + return(-1); + } + + newph->next = NULL; + if(lph == NULL) { + vhost = newph; + newph->next = ph; + } else { + lph->next = newph; + newph->next = ph; + } + + return(0); +} + +static int dovpath(parms, np) +char *parms[]; +int np; +{ +char *from, *to; +struct vpath *pv, *lpv, *newpv; + + if(np < 3) return(0); + + from = parms[1]; + to = parms[2]; + + for(pv = vpath, lpv = NULL; pv != NULL; lpv = pv, pv = pv->next) + ; + + newpv = malloc(sizeof(struct vpath)); + if(newpv == NULL) { + fprintf(stderr, "httpd: malloc failed in dovpath\n"); + return(-1); + } + newpv->from = malloc(strlen(from)+1); + if(newpv->from == NULL) { + fprintf(stderr, "httpd: malloc failed in dovpath\n"); + return(-1); + } + strcpy(newpv->from, from); + + to = subvpath(to); + + newpv->to = malloc(strlen(to)+1); + if(newpv->to == NULL) { + fprintf(stderr, "httpd: malloc failed in dovpath\n"); + return(-1); + } + strcpy(newpv->to, to); + + newpv->auth = NULL; + newpv->urlaccess = -1; + + if(np > 3) + if(parms[3][0] != '#') { + newpv->auth = findauth(parms[3]); + if(np > 4) + if(parms[4][0] != '#') { + newpv->urlaccess = mkurlaccess(parms[4]); + if(np > 5) + if(parms[5][0] != '#') { + fprintf(stderr, "httpd: junk at end of vpath line\n"); + return(-1); + } + } + } + + newpv->next = NULL; + if(lpv == NULL) { + vpath = newpv; + newpv->next = pv; + } else { + lpv->next = newpv; + newpv->next = pv; + } + + return(0); +} + +static int dosrvrroot(parms, np) +char *parms[]; +int np; +{ +char *newroot; + + if(np < 2) return(0); + + newroot = subvpath(parms[1]); + + srvrroot = malloc(strlen(newroot)+1); + if(srvrroot == NULL) { + fprintf(stderr, "httpd: malloc failed in dosrvrroot\n"); + return(-1); + } + strcpy(srvrroot, newroot); + if(srvrroot[strlen(srvrroot)-1] == '/') + srvrroot[strlen(srvrroot)-1] = '\0'; + + return(0); +} + +static int doredirect(parms, np) +char *parms[]; +int np; +{ +char *redir; + + if(np < 2) return(0); + + redir = subvpath(parms[1]); + + Redirect = malloc(strlen(redir)+1); + if(Redirect == NULL) { + fprintf(stderr, "httpd: malloc failed in doredirect\n"); + return(-1); + } + strcpy(Redirect, redir); + if(Redirect[strlen(Redirect)-1] == '/') + Redirect[strlen(Redirect)-1] = '\0'; + + return(0); +} + +static int dodirsend(parms, np) +char *parms[]; +int np; +{ +char *file; +int i; +struct dirsend *pd, *lpd, *npd; + + if(np < 2) return(0); + + /* find end of the list */ + for(pd = dirsend, lpd = NULL; pd != NULL; lpd = pd, pd = pd->next) ; + + for(i = 1; i < np; i++) { + file = parms[i]; + if(file[0] == '#') break; + npd = malloc(sizeof(struct dirsend)); + if(npd == NULL) { + fprintf(stderr, "httpd: malloc failed in dodirsend\n"); + return(-1); + } + npd->file = malloc(strlen(file)+1); + if(npd->file == NULL) { + fprintf(stderr, "httpd: malloc failed in dodirsend\n"); + return(-1); + } + strcpy(npd->file, file); + npd->next = NULL; + if(lpd == NULL) + dirsend = npd; + else + lpd->next = npd; + lpd = npd; + } + + return(0); +} + +static int dodirexec(parms, np) +char *parms[]; +int np; +{ +char *file; + + if(np < 2) return(0); + + if(direxec != NULL) { + fprintf(stderr, "httpd: Error direxec line already present\n"); + return(-1); + } + + file = subvpath(parms[1]); + + direxec = malloc(strlen(file)+1); + + if(direxec == NULL) { + fprintf(stderr, "httpd: malloc failed in dodirexec\n"); + return(-1); + } + + strcpy(direxec, file); + + if(np > 2) + if(parms[2][0] != '#') { + fprintf(stderr, "httpd: garbage on end of direxec line\n"); + return(-1); + } + + return(0); +} + +static char *subvpath(s) +char *s; +{ +char *p, *p2; +int len; +static char buffer[1024]; +char user[80]; +struct passwd *pwd; + + /* replace beginning // with srvrroot */ + if(s[0] == '/' && s[1] == '/') + /* but not /// if we have VHOST's */ + if(vhost == NULL || s[2] != '/') { + strcpy(buffer, srvrroot); + strncat(buffer, s+1, sizeof(buffer) - strlen(buffer)); + buffer[sizeof(buffer)-1] = '\0'; + return(buffer); + } + + if(s[0] != '/' || s[1] != '~') return(s); + + /* replace beginning /~user with user home directory */ + p = s + 2; + p2 = user; + len = sizeof(user) - 1; + while(*p && *p != '/' && len-- > 0) *p2++ = *p++; + *p2 = '\0'; + if(*p != '\0' && *p != '/') return(s); + if((pwd = getpwnam(user)) == (struct passwd *)NULL) return(s); + strcpy(buffer, pwd->pw_dir); + strncat(buffer, p, sizeof(buffer) - strlen(buffer)); + buffer[sizeof(buffer)-1] = '\0'; + + return(buffer); +} + +static int dologfile(parms, np) +char *parms[]; +int np; +{ +char *p; + + if(np < 2) return(0); + + p = subvpath(parms[1]); + LogFile = malloc(strlen(p)+1); + if(LogFile == NULL) { + fprintf(stderr, "httpd: malloc failed in dologfile\n"); + return(-1); + } + strcpy(LogFile, p); + + return(0); +} + +static int dodbgfile(parms, np) +char *parms[]; +int np; +{ +char *p; + + if(np < 2) return(0); + + p = subvpath(parms[1]); + DbgFile = malloc(strlen(p)+1); + if(DbgFile == NULL) { + fprintf(stderr, "httpd: malloc failed in dodbgfile\n"); + return(-1); + } + strcpy(DbgFile, p); + + return(0); +} + +static int douser(parms, np) +char *parms[]; +int np; +{ + if(np < 2) return(0); + + User = malloc(strlen(parms[1])+1); + if(User == NULL) { + fprintf(stderr, "httpd: malloc failed in douser\n"); + return(-1); + } + strcpy(User, parms[1]); + + return(0); +} + +static int dochroot(parms, np) +char *parms[]; +int np; +{ +char *newroot; + + if(np < 2) return(0); + + newroot = subvpath(parms[1]); + + Chroot = malloc(strlen(newroot)+1); + if(Chroot == NULL) { + fprintf(stderr, "httpd: malloc failed in dochroot\n"); + return(-1); + } + strcpy(Chroot, newroot); + + return(0); +} + +static int adduser(pauth, user) +struct auth *pauth; +char *user; +{ +struct authuser *pa, *lpa, *newpa; + + for(pa = pauth->users, lpa = NULL; pa != NULL; lpa = pa, pa = pa->next) + ; + + newpa = malloc(sizeof(struct authuser)); + if(newpa == NULL) { + fprintf(stderr, "httpd: malloc failed in adduser\n"); + return(-1); + } + newpa->user = malloc(strlen(user)+1); + if(newpa->user == NULL) { + fprintf(stderr, "httpd: malloc failed in adduser\n"); + return(-1); + } + strcpy(newpa->user, user); + + newpa->next = NULL; + if(lpa == NULL) { + pauth->users = newpa; + newpa->next = pa; + } else { + lpa->next = newpa; + newpa->next = pa; + } + + return(0); +} + +static int doauth(parms, np) +char *parms[]; +int np; +{ +int i; +char *name, *desc, *pf; +char *p, *p2; +struct auth *pa, *lpa, *newpa; + + if(np < 3) return(0); + + name = parms[1]; + desc = parms[2]; + + for(pa = auth, lpa = NULL; pa != NULL; lpa = pa, pa = pa->next) + ; + + newpa = malloc(sizeof(struct auth)); + if(newpa == NULL) { + fprintf(stderr, "httpd: malloc failed in doauth\n"); + return(-1); + } + newpa->name = malloc(strlen(name)+1); + if(newpa->name == NULL) { + fprintf(stderr, "httpd: malloc failed in doauth\n"); + return(-1); + } + p = name; p2 = newpa->name; + while(*p) + *p2++ = tolower(*p++); + *p2 = '\0'; + + newpa->desc = malloc(strlen(desc)+1); + if(newpa->desc == NULL) { + fprintf(stderr, "httpd: malloc failed in doauth\n"); + return(-1); + } + strcpy(newpa->desc, desc); + + newpa->urlaccess = mkurlaccess(parms[3]); + newpa->passwdfile = NULL; + newpa->users = NULL; + + if(np > 4) + if(parms[4][0] != '#') { + if(!strcmp(parms[4], ".")) + pf = "/etc/passwd"; + else + pf = subvpath(parms[4]); + newpa->passwdfile = malloc(strlen(pf)+1); + if(newpa->passwdfile == NULL) { + fprintf(stderr, "httpd: malloc failed in doauth\n"); + return(-1); + } + strcpy(newpa->passwdfile, pf); + i = 5; + while(i < np) { + if(parms[i][0] == '#') + break; + if(adduser(newpa, parms[i])) + return(-1); + i++; + } + } + + newpa->next = NULL; + if(lpa == NULL) { + auth = newpa; + newpa->next = pa; + } else { + lpa->next = newpa; + newpa->next = pa; + } + + return(0); +} + +static int doproxyauth(parms, np) +char *parms[]; +int np; +{ +int i; +char *name, *desc, *pf; +char *p, *p2; +struct auth *pa, *lpa, *newpa; + + if(np < 3) return(0); + + name = parms[1]; + desc = parms[2]; + + if(proxyauth != (struct auth *)NULL) { + fprintf(stderr, "httpd: ProxyAuth defined multiple times using 1st only\n"); + return(0); + } + + for(pa = proxyauth, lpa = NULL; pa != NULL; lpa = pa, pa = pa->next) + ; + + newpa = malloc(sizeof(struct auth)); + if(newpa == NULL) { + fprintf(stderr, "httpd: malloc failed in doproxyauth\n"); + return(-1); + } + newpa->name = malloc(strlen(name)+1); + if(newpa->name == NULL) { + fprintf(stderr, "httpd: malloc failed in doproxyauth\n"); + return(-1); + } + p = name; p2 = newpa->name; + while(*p) + *p2++ = tolower(*p++); + *p2 = '\0'; + + newpa->desc = malloc(strlen(desc)+1); + if(newpa->desc == NULL) { + fprintf(stderr, "httpd: malloc failed in doproxyauth\n"); + return(-1); + } + strcpy(newpa->desc, desc); + + newpa->urlaccess = mkurlaccess(parms[3]); + newpa->passwdfile = NULL; + newpa->users = NULL; + + if(np > 4) + if(parms[4][0] != '#') { + if(!strcmp(parms[4], ".")) + pf = "/etc/passwd"; + else + pf = subvpath(parms[4]); + newpa->passwdfile = malloc(strlen(pf)+1); + if(newpa->passwdfile == NULL) { + fprintf(stderr, "httpd: malloc failed in doauth\n"); + return(-1); + } + strcpy(newpa->passwdfile, pf); + i = 5; + while(i < np) { + if(parms[i][0] == '#') + break; + if(adduser(newpa, parms[i])) + return(-1); + i++; + } + } + + newpa->next = NULL; + if(lpa == NULL) { + proxyauth = newpa; + newpa->next = pa; + } else { + lpa->next = newpa; + newpa->next = pa; + } + + return(0); +} diff --git a/commands/httpd0995/config.h b/commands/httpd0995/config.h new file mode 100644 index 000000000..7beeb8316 --- /dev/null +++ b/commands/httpd0995/config.h @@ -0,0 +1,86 @@ +/* config.h + * + * This file is part of httpd. + * + * 02/26/1996 Michael Temari + * 07/07/1996 Initial Release Michael Temari + * 12/29/2002 Michael Temari + * 07/04/2003 Al Woodhull + * 02/08/2005 Michael Temari + * 05/14/2006 Michael Temari + * + */ + +#define VERSION "Minix httpd 0.995" + +struct authuser { + char *user; + struct authuser *next; +}; + +struct auth { + char *name; + char *desc; + int urlaccess; + char *passwdfile; + struct authuser *users; + struct auth *next; +}; + +struct msufx { + char *suffix; + struct mtype *mtype; + struct msufx *snext; + struct msufx *tnext; +}; + +struct mtype { + char *mimetype; + struct msufx *msufx; + struct mtype *next; +}; + +struct vhost { + char *hname; + char *root; + struct vhost *next; +}; + +struct vpath { + char *from; + char *to; + struct auth *auth; + int urlaccess; + struct vpath *next; +}; + +struct dirsend { + char *file; + struct dirsend *next; +}; + +/* urlaccess bits */ + +#define URLA_READ 1 +#define URLA_WRITE 2 +#define URLA_EXEC 4 +#define URLA_HEADERS 8 + +#define HTTPD_CONFIG_FILE "/etc/httpd.conf" + +_PROTOTYPE(int readconfig, (char *cfg_file, int testing)); + +extern struct mtype *mtype; +extern struct msufx *msufx; +extern struct vhost *vhost; +extern struct vpath *vpath; +extern struct dirsend *dirsend; +extern struct auth *auth; +extern struct auth *proxyauth; +extern char *direxec; +extern char *srvrroot; +extern char *Redirect; +extern char *LogFile; +extern char *DbgFile; +extern char *User; +extern char *Chroot; diff --git a/commands/httpd0995/dir2html.c b/commands/httpd0995/dir2html.c new file mode 100644 index 000000000..36634d68a --- /dev/null +++ b/commands/httpd0995/dir2html.c @@ -0,0 +1,187 @@ +/* dir2html.c by Michael Temari 3/3/96 */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +typedef struct namelist { /* Obviously a list of names. */ + struct namelist *next; + char name[1]; +} namelist_t; + +_PROTOTYPE(static void sort, (namelist_t **anl)); +_PROTOTYPE(static namelist_t *collect, (char *dir)); +_PROTOTYPE(int main, (int argc, char *argv[])); + +static void sort(anl) +namelist_t **anl; +/* A stable mergesort disguised as line noise. Must be called like this: + * if (L != NULL && L->next != NULL) sort(&L); + */ +{ + /* static */ namelist_t *nl1, **mid; /* Need not be local */ + namelist_t *nl2; + + nl1 = *(mid = &(*anl)->next); + do { + if ((nl1 = nl1->next) == NULL) break; + mid = &(*mid)->next; + } while ((nl1 = nl1->next) != NULL); + + nl2 = *mid; + *mid = NULL; + + if ((*anl)->next != NULL) sort(anl); + if (nl2->next != NULL) sort(&nl2); + + nl1 = *anl; + for (;;) { + if (strcmp(nl1->name, nl2->name) <= 0) { + if ((nl1 = *(anl = &nl1->next)) == NULL) { + *anl = nl2; + break; + } + } else { + *anl = nl2; + nl2 = *(anl = &nl2->next); + *anl = nl1; + if (nl2 == NULL) break; + } + } +} + +static namelist_t *collect(dir) +char *dir; +/* Return a sorted list of directory entries. Returns null with errno != 0 + * on error. + */ +{ + namelist_t *names, **pn = &names; + DIR *dp; + struct dirent *entry; + + if ((dp = opendir(dir)) == NULL) return NULL; + + while ((entry = readdir(dp)) != NULL) { + if (strcmp(entry->d_name, ".") == 0) continue; + *pn = malloc(offsetof(namelist_t, name) + strlen(entry->d_name) + 1); + if (*pn == NULL) { + closedir(dp); + errno = ENOMEM; + return NULL; + } + strcpy((*pn)->name, entry->d_name); + pn = &(*pn)->next; + } + closedir(dp); + *pn = NULL; + if (names != NULL && names->next != NULL) sort(&names); + errno = 0; + return names; +} + +int main(argc, argv) +int argc; +char *argv[]; +{ + namelist_t *np; + char *rpath, *vpath; + static char cwd[1024]; + static char work[64]; + char *filename; + struct stat st; + struct tm *tmp; + static char month[][4] = { + "Jan", "Feb", "Mar", "Apr", "May", "Jun", + "Jul", "Aug", "Sep", "Oct", "Nov", "Dec", + }; + + if(argc > 1) { + rpath = argv[1]; + if (chdir(rpath) < 0) { + fprintf(stderr, "dir2html: %s: %s\n", rpath, strerror(errno)); + return(-1); + } + } else { + if(getcwd(cwd, sizeof(cwd)) == NULL) { + fprintf(stderr, "dir2html: getcwd(): %s", strerror(errno)); + return(-1); + } + rpath = cwd; + } + + if(argc > 2) { + vpath = argv[2]; + } else { + vpath = rpath; + } + + if ((np = collect(".")) == NULL && errno != 0) { + fprintf(stderr, "dir2html: %s: %s\n", vpath, strerror(errno)); + return(-1); + } + + printf("Index of %s\n", vpath); + printf("\n"); + printf("

Index of %s

\n", vpath); + + printf("
\n");
+    printf("%-22s  %-17s  %s\n", "Name", "Last modified", "Size/Type");
+    printf("
\n"); + + while (np != NULL) { + errno = 0; + filename = np->name; + np= np->next; + + if (stat(filename, &st) < 0) continue; + + printf("", + filename, S_ISDIR(st.st_mode) ? "/" : ""); + sprintf(work, "%.23s%s", + filename, S_ISDIR(st.st_mode) ? "/" : ""); + if (strcmp(filename, "..") == 0) strcpy(work, "Parent Directory"); + printf("%-22.22s%s", + work, strlen(work) > 22 ? ">" : " "); + tmp = localtime(&st.st_mtime); + printf(" %02d %s %d %02d:%02d", + tmp->tm_mday, month[tmp->tm_mon], 1900+tmp->tm_year, + tmp->tm_hour, tmp->tm_min); + if (S_ISREG(st.st_mode)) { + if (st.st_size < 10240) { + sprintf(work, "%lu ", (unsigned long) st.st_size); + } else + if (st.st_size < 10240 * 1024L) { + sprintf(work, "%luK", + ((unsigned long) st.st_size - 1) / 1024 + 1); + } else { + sprintf(work, "%luM", + ((unsigned long) st.st_size - 1) / (1024 * 1024L) + 1); + } + } else { + strcpy(work, + S_ISDIR(st.st_mode) ? "[dir]" : + S_ISBLK(st.st_mode) ? "[block]" : + S_ISCHR(st.st_mode) ? "[char]" : + S_ISFIFO(st.st_mode) ? "[pipe]" : + "[???]"); + } + printf(" %8s\n", work); + } + + printf("
\n"); + + printf("
\n"); + printf("Minix httpd 0.99\n"); + printf("\n"); + printf("\n"); + + return(0); +} diff --git a/commands/httpd0995/dir2html.sh b/commands/httpd0995/dir2html.sh new file mode 100755 index 000000000..5a4b101fb --- /dev/null +++ b/commands/httpd0995/dir2html.sh @@ -0,0 +1,42 @@ +# dir2html.sh by Michael Temari 03/03/96 +# +#!/bin/sh +if [ $# != 0 ] +then +cd $1 +fi +dname=`pwd` +fdname=$2 +if [ $dname != / ] +then + dname=${dname}/ +fi +echo "" +echo "" +echo Directory of $fdname +echo "" +echo "

" +echo Directory of $fdname +echo "

" +echo "
" +# +ls $dname | +{ +while read fname +do +lname=$fdname$fname +echo "

" +echo -n "" +echo -n $fname +echo "
" +echo "

" +done +} +echo "
" +echo "
" +echo Directory Generated at `date` +echo "
" +echo "" +exit 0 diff --git a/commands/httpd0995/http.h b/commands/httpd0995/http.h new file mode 100644 index 000000000..bf868f3ab --- /dev/null +++ b/commands/httpd0995/http.h @@ -0,0 +1,118 @@ +/* http.h + * + * This file is part of httpd. + * + * 02/17/1996 Michael Temari + * 07/07/1996 Initial Release Michael Temari + * 12/29/2002 Michael Temari + * + */ + +#define INDEX_FILE_NAME "index.html" + +#define HTTP_REQUEST_TYPE_SIMPLE 0 +#define HTTP_REQUEST_TYPE_FULL 1 +#define HTTP_REQUEST_TYPE_PROXY 2 + +#define HTTP_METHOD_UNKNOWN 0 +#define HTTP_METHOD_OPTIONS 1 +#define HTTP_METHOD_GET 2 +#define HTTP_METHOD_HEAD 3 +#define HTTP_METHOD_POST 4 +#define HTTP_METHOD_PUT 5 +#define HTTP_METHOD_PATCH 6 +#define HTTP_METHOD_COPY 7 +#define HTTP_METHOD_MOVE 8 +#define HTTP_METHOD_DELETE 9 +#define HTTP_METHOD_LINK 10 +#define HTTP_METHOD_UNLINK 11 +#define HTTP_METHOD_TRACE 12 +#define HTTP_METHOD_WRAPPED 13 + +#define HTTP_STATUS_OK 200 +#define HTTP_STATUS_CREATED 201 +#define HTTP_STATUS_ACCEPTED 202 +#define HTTP_STATUS_NO_CONTENT 204 +#define HTTP_STATUS_MOVED_PERM 301 +#define HTTP_STATUS_MOVED_TEMP 302 +#define HTTP_STATUS_NOT_MODIFIED 304 +#define HTTP_STATUS_USE_PROXY 305 +#define HTTP_STATUS_BAD_REQUEST 400 +#define HTTP_STATUS_UNAUTHORIZED 401 +#define HTTP_STATUS_FORBIDDEN 403 +#define HTTP_STATUS_NOT_FOUND 404 +#define HTTP_STATUS_METHOD_NOT_ALLOWED 405 +#define HTTP_STATUS_PROXY_AUTH_REQRD 407 +#define HTTP_STATUS_LENGTH_REQUIRED 411 +#define HTTP_STATUS_SERVER_ERROR 500 +#define HTTP_STATUS_NOT_IMPLEMENTED 501 +#define HTTP_STATUS_BAD_GATEWAY 502 +#define HTTP_STATUS_SERVICE_UNAVAILABLE 503 +#define HTTP_STATUS_GATEWAY_TIMEOUT 504 +#define HTTP_STATUS_UNSUPPORTED_VERSION 505 + +struct http_request { + int type; + int method; + char uri[256]; + char url[256]; + char query[256]; + char host[256]; + int port; + char useragent[256]; + int vmajor; + int vminor; + time_t ifmodsince; + off_t size; + time_t msgdate; + int keepopen; + char wwwauth[128]; + char authuser[128]; + char authpass[128]; + char cookie[128]; +}; + +struct http_reply { + int status; + char statusmsg[128]; + int keepopen; + int headers; + char *mtype; + char realurl[256]; + struct auth *auth; + int urlaccess; + off_t size; + time_t modtime; + int fd; + int ofd; + int pid; +}; + +/* from httpd.c */ + +extern FILE *stdlog; +extern FILE *dbglog; + +/* from reply.c */ + +_PROTOTYPE(int sendreply, (struct http_reply *rp, struct http_request *rq)); + +/* from request.c */ + +_PROTOTYPE(int getrequest, (struct http_request *rq)); + +/* from process.c */ + +_PROTOTYPE(int processrequest, (struct http_request *rq, struct http_reply *rp)); + +/* from police.c */ + +_PROTOTYPE(int police, (struct http_request *rq, struct http_reply *rp)); + +/* from cgiexec.c */ + +_PROTOTYPE(int cgiexec, (struct http_request *rq, struct http_reply *rp)); + +/* from proxy.c */ + +_PROTOTYPE(void proxy, (struct http_request *rq, struct http_reply *rp)); diff --git a/commands/httpd0995/http_status.5 b/commands/httpd0995/http_status.5 new file mode 100644 index 000000000..5301a5793 --- /dev/null +++ b/commands/httpd0995/http_status.5 @@ -0,0 +1,72 @@ +.TH HTTP_STATUS 5 +.SH NAME +http_status \- HTTP status numbers and their meanings +.SH DESCRIPTION +These are the HTTP status numbers defined in +.BI http.h +in the source directory, +.BI /usr/local/src/httpdxxx. +The message you see on your screen when a page cannot be accessed is +normally generated by your browser. +.P +HTTP_STATUS_OK 200 +.br +HTTP_STATUS_CREATED 201 +.br +HTTP_STATUS_ACCEPTED 202 +.br +HTTP_STATUS_NO_CONTENT 204 +.br +HTTP_STATUS_MOVED_PERM 301 +.br +HTTP_STATUS_MOVED_TEMP 302 +.br +HTTP_STATUS_NOT_MODIFIED 304 +.br +HTTP_STATUS_USE_PROXY 305 +.br +HTTP_STATUS_BAD_REQUEST 400 +.br +HTTP_STATUS_UNAUTHORIZED 401 +.br +HTTP_STATUS_FORBIDDEN 403 +.br +HTTP_STATUS_NOT_FOUND 404 +.br +HTTP_STATUS_METHOD_NOT_ALLOWED 405 +.br +HTTP_STATUS_PROXY_AUTH_REQRD 407 +.br +HTTP_STATUS_LENGTH_REQUIRED 411 +.br +HTTP_STATUS_SERVER_ERROR 500 +.br +HTTP_STATUS_NOT_IMPLEMENTED 501 +.br +HTTP_STATUS_BAD_GATEWAY 502 +.br +HTTP_STATUS_SERVICE_UNAVAILABLE 503 +.br +HTTP_STATUS_GATEWAY_TIMEOUT 504 +.br +HTTP_STATUS_UNSUPPORTED_VERSION 505 +.br + +.SH FILES +.TP 25n +.B /usr/local/src/httpdxxx/http.h +.SH "SEE ALSO" +The definitive source of information on the HTTP protocol is the +.B "World Wide Web Consortium" +web page at +.B http://www.w3c.org . +.P +A draft version of the HTTP 1.1 specification is available on the Minix1 +websites. For more information on status codes go to this URL: +.B http://minix1.woodhull.com/http11.html#Status-Codes +.SH AUTHOR +The Minix httpd server was created by and is maintained by Michael Temari + +.P +Man page compiled by Al Woodhull +.\"updated 2006-06-01 diff --git a/commands/httpd0995/httpd.8 b/commands/httpd0995/httpd.8 new file mode 100644 index 000000000..04fed2a6a --- /dev/null +++ b/commands/httpd0995/httpd.8 @@ -0,0 +1,124 @@ +.TH HTTPD 8 +.SH NAME +httpd, in.httpd, dir2html \- a web server for Minix 2 and Minix 3 +.SH SYNOPSIS +.B httpd +.RB [\-t|\-v] +.RI [ config_file ] +.P +.B "tcpd http /usr/local/bin/in.httpd &" +.P +.B dir2html +.RB [directory] +.SH DESCRIPTION +.B Httpd +is a World Wide Web (WWW) server written by Michael Temari. It was +written from scratch so the setup and configuration will not be like +other web servers. +.P +.B In.httpd +is linked to +.B httpd. +This alternate name is used to indicate the program is a server that is +started by +.B tcpd (8), +a program which listens for incoming TCP connections on the passed +port (defined in +.BI /etc/services ). +When a connection comes in +.B tcpd +forks and starts the given daemon program, after possibly checking for access +restrictions and logging the connection. Therefore, to enable +.B in.httpd +to start you use (in a startup script): +.P +.B "tcpd http /usr/local/bin/in.httpd &" +.P +or +.P +.B "daemonize tcpd http /usr/local/bin/in.httpd" +.P +.B (daemonize +is a shell function defined in +.BI/usr/etc/rc +in Minix 2.0.3 and later releases which starts programs as daemons). +To enable or reenable +.B in.httpd +from the command line a user a system administrator should use +.B intr (8), +like this: +.P +.B "intr -d tcpd http /usr/local/bin/in.httpd &" +.P +to start +.B tcpd +as a daemon (getting input from /dev/null, writing output to /dev/log, +and not part of a process group). +.P +.B Dir2html +is an accessory program that produces a directory listing formatted as +web page for the current directory or for a directory specified as an +argument. It is called by +.B httpd +when a web client references a directory that includes no index.html +file (or whatever alternative to index.html that may be defined in +/etc/httpd.conf). Since it writes to standard output it may also be called +as a standalone program. +.P +Options for +.B httpd +are: +.SH OPTIONS +.TP +.B \-t +This tells the server to parse the configuration file so that you can +see if it is the way you want it. You may also pass the name of your +configuration file if it is not the default /etc/httpd.conf. +.TP +.B \-v +Shows the server version, then exits. +.TP +.B config_file +normally /etc/httpd.conf +.SH FILES +.TP 25n +.B /etc/httpd.conf +The configuration file. +.P +.B /etc/httpd.mtype +Extension to configuration file defining MIME types. +.P +.B /usr/adm/httpd.log +Log file. The file must exist for logging to begin. +.SH "SEE ALSO" +.BR httpd.conf (5), +.BR http_status (5), +.BR serv.access (5), +.BR intr (8), +.BR tcpd (8). +.SH NOTES +This server has been tested on both Minix 2 and Minix 3. +.P +Running a server exposed to the Internet is risky to the host system and +to the local network. Consult with the owner of your net before you go +public. Read the +.B SECURITY +document in the source directory. +.P +The +.B tcpd (8) +man page needs to be written. The important thing to know is that if +the access control file +.B /etc/serv.access +exists tcpd will exec its paranoid twin, tcpdp, which will deny access from +any IP for which a name cannot be found. +.SH BUGS +None are known, but there are surely some unknown ones. Be careful! +.SH AUTHOR +The Minix httpd server was created by and is maintained by Michael Temari + +.P +This man page was compiled by Al Woodhull +.P +.\" updated 2006-06-17 + diff --git a/commands/httpd0995/httpd.c b/commands/httpd0995/httpd.c new file mode 100644 index 000000000..bbe767264 --- /dev/null +++ b/commands/httpd0995/httpd.c @@ -0,0 +1,175 @@ +/* httpd.c + * + * httpd A Server implementing the HTTP protocol. + * + * usage: tcpd http httpd & + * + * 02/17/1996 Michael Temari + * 07/07/1996 Initial Release Michael Temari + * 12/29/2002 Michael Temari + * 07/04/2003 Al Woodhull + * + */ + +#include +#include +#include +#include +#include +#include +#include "http.h" +#include "utility.h" +#include "net.h" +#include "config.h" + +FILE *stdlog = (FILE *)NULL; +FILE *dbglog = (FILE *)NULL; + +char umsg[80]; + +_PROTOTYPE(int main, (int argc, char *argv[])); + +struct http_request request; +struct http_reply reply; + +int main(argc, argv) +int argc; +char *argv[]; +{ +char *prog; +int opt_t; +char *cfg = (char *)NULL; +struct passwd *pwd; +int s; + + strcpy(umsg, "Usage: "); + strcat(umsg, argv[0]); + strcat(umsg, " [-t|v] [config_file]\n"); + + /* parse program name */ + prog = strrchr(*argv, '/'); + if(prog == (char *)NULL) + prog = *argv; + else + prog++; + argv++; + argc--; + + /* Any options */ + if(argc) + if(argv[0][0] == '-') { + switch (argv[0][1]) { + case 't' : opt_t = 1; + argv++; + argc--; + break; + case 'v' : fprintf(stderr, VERSION"\n"); + exit(EXIT_SUCCESS); + break; + default : fprintf(stderr, VERSION"\n"); + fprintf(stderr, umsg); + exit(EXIT_FAILURE); + } + } + + /* Did they specify an alternate configuration file? */ + if(argc) { + cfg = *argv++; + argc--; + } + + /* Read the configuration settings */ + if(readconfig(cfg, opt_t)) { + fprintf(stderr, "httpd: Error reading configuration file.\n"); + return(-1); + } + + /* Option t is to test configuration only */ + if(opt_t) + return(0); + + /* Open log file for append if it exists */ + if(LogFile != NULL) + if((stdlog = fopen(LogFile, "r")) != (FILE *)NULL) { + fclose(stdlog); + stdlog = fopen(LogFile, "a"); + } + + /* Open debug log file for append if it exists */ + if(DbgFile != NULL) + if((dbglog = fopen(DbgFile, "r")) != (FILE *)NULL) { + fclose(dbglog); + dbglog = fopen(DbgFile, "a"); + } + +#if 0 + /* Get some network information */ + GetNetInfo(); +#endif + + /* If user defined then prepare to secure as user given */ + if(User != NULL) + if((pwd = getpwnam(User)) == (struct passwd *)NULL) { + fprintf(stderr, "httpd: unable to find user %s\n", User); + return(-1); + } + + /* If Chroot defined then secure even more by doing a chroot */ + if(Chroot != NULL) { + if(chroot(Chroot)) { + fprintf(stderr, "httpd: unable to chroot\n"); + return(-1); + } + if(chdir("/")) { + fprintf(stderr, "httpd: unable to chroot\n"); + return(-1); + } + } + + /* If user defined then secure as user given */ + if(User != NULL) + if(setgid(pwd->pw_gid) || setuid(pwd->pw_uid)) { + fprintf(stderr, "httpd: unable to set user\n"); + return(-1); + } + +#if DAEMON + /* Standalone? */ + if (strncmp(prog, "in.", 3) != 0) { + /* Does not start with "in.", so not started from inetd/tcpd. */ + /* XXX - Port name/number should be a config file option. */ + daemonloop("http"); + } +#endif + + /* Get some network information */ + GetNetInfo(); + + /* log a connection */ + if(dbglog != (FILE *)NULL) { + fprintf(dbglog, "CONNECT: %d %s %s\n", getpid(), + rmthostname, logdate((time_t *)NULL)); + fflush(dbglog); + } + + /* loop getting, processing and replying to requests */ + while(!(s = getrequest(&request))) { + if(processrequest(&request, &reply)) break; + if(stdlog != (FILE *)NULL) { + fprintf(stdlog, "%s %s %d %d %s\n", + logdate((time_t *)NULL), rmthostname, + request.method, reply.status, request.url); + fflush(stdlog); + } + if(sendreply(&reply, &request)) break; + if(!reply.keepopen) break; + } + if(s == 1 && stdlog != (FILE *)NULL) { + fprintf(stdlog, "%s %s %d %d %s\n", + logdate((time_t *)NULL), rmthostname, + request.method, 999, request.url); + fflush(stdlog); + } + + return(0); +} diff --git a/commands/httpd0995/httpd.conf b/commands/httpd0995/httpd.conf new file mode 100644 index 000000000..78a2d877b --- /dev/null +++ b/commands/httpd0995/httpd.conf @@ -0,0 +1,166 @@ +# httpd.conf Sample httpd.conf file By Michael Temari 2006-06-01 + +#serverroot path +# +# path = sets the translation for // +# +# these have special meaning if at beginning of path +# +# /~user = gets replaced with user home directory + +#redirect http://SomeWhere.Else.Com/ + +#redirect url +# +# url = Will redirect entire website via error code 301 MOVED PERM to +# url and original url path of request. + +serverroot /~www + +#user username +# +# if present the server will run as the given username otherwise the +# server will run as who ever started it (normally root). + +user www + +#chroot directory +# +# if present the server will be chroot'ed to the given directory name +# normally the home directory of username given above. Be aware if this +# is set then you can only access stuff off this new root. +# +# these have special meaning if at beginning of the directory +# +# // = gets replaced by serverroot directory +# /~user = gets replaced with user home directory + +#chroot /~www + +#logfile filename +# +# the file must exist also and a log of http transactions will be kept +# +# these have special meaning if at the beginning of the filename +# +# // = gets replaced by serverroot directory +# /~user = gets replaced with user home directory + +logfile /usr/adm/httpd.log + +#dbgfile filename +# +# the file must exist also and a debug log of http transactions will be kept +# +# these have special meaning if at the beginning of the filename +# +# // = gets replaced by serverroot directory +# /~user = gets replaced with user home directory + +dbgfile /usr/adm/httpd.dbg + +# dirsend [list of files to try until 1st one is found] +# + +dirsend index.htm + +# direxec [script to run for automatic directory page output] +# +# +# these have special meaning if at beginning of script +# +# // = gets replaced by serverroot directory +# /~user = gets replaced with user home directory +# + +direxec /usr/local/bin/dir2html + +# vhost hostname VhostRoot +# +# vhost is for defining access for virtual hosts. If none are configured then +# any host is accepted. If specified then access is only granted for requests +# for hosts which are configured here. In the Vpath section below the /// gets +# translated to the corresponding VhostRoot. + +# vhost temware.dyndns.org //doc/ +# vhost minix.homeip.net //doc2/ + +# auth authname authdescription access [passwdfile [users]] +# +# auth defines any access authorization to be used in vpath +# +# authname = name to give to this authorization (case insensitive) +# authdescription = Description of this authorization +# access = r=read, w=write, x=execute, h=headers (other than rwxh then no access) +# NOTE: r=file should be read, w=file can be overwrote +# NOTE: x=file should be executed +# NOTE: h=headers (executing program will produce all http headers) +# NOTE: access is on top of unix access +# passwdfile = Name of passwd file to be used (. means /etc/passwd) +# (if none given then no user check) +# users = valid users for this authorization (if none given then all users valid) +# +# these have special meaning if at beginning of passwdfile +# +# // = gets replaced by serverroot directory +# /~user = gets replaced with user home directory +# + +auth AnyBody AnyBody R + System System_User R . + Uploads Uploads RW . root + +# proxyauth authname authdescription access [passwdfile [users]] +# +# proxyauth defines any access authorization to be used for Proxy access +# +# authname = Same as auth above +# authdescription = Same as auth above +# access = Must be R to allow proxy +# passwdfile = Same as auth above +# users = Same as auth above +# + +# proxyauth +# Proxy Proxy R . + + +# vpath from to auth +# +# vpath sets up a list of url path translations and authorizations +# +# from = user specified url +# *=wildcard, $=wildard, but keep path for passing to program +# to = real location +# auth = authname from above (null for no authorization) +# access = r-read, w-write, x-execute (if not given taken from auth record) +# = h-headers (executing program will produce all http headers) +# +# these have special meaning if at beginning of to or serverroot fields +# +# // = gets replaced by serverroot directory +# /// = gets replaced by vhost root directory if configured otherwise same as // +# . = specified authorization only, use other translation elsewhere +# /~user = gets replaced with user home directory +# + +vpath * . AnyBody + /* /// AnyBody + /index.htm . AnyBody X + /ip . AnyBody X + /c1d1$ //exec/cdrom AnyBody X + /c1d2$ //exec/cdrom AnyBody X + /uploads* . Uploads + /src* /usr/src AnyBody R + +# include filename +# +# include tells the server to continue parsing configuration information +# in the given filename +# +# these have special meaning if at beginning of filename +# +# // = gets replaced by serverroot directory +# /~user = gets replaced with user home directory + +include //etc/httpd.mtype diff --git a/commands/httpd0995/httpd.conf.5 b/commands/httpd0995/httpd.conf.5 new file mode 100644 index 000000000..c8b96c180 --- /dev/null +++ b/commands/httpd0995/httpd.conf.5 @@ -0,0 +1,334 @@ +.TH HTTPD.CONF 5 +.SH NAME +httpd.conf httpd.mtype \- configuration files for the Minix httpd web server +.SH SYNOPSIS +.B /etc/httpd.conf +.B /etc/httpd.mtype +.SH DESCRIPTION +.B /etc/httpd.conf +is the configuration file for the Minix httpd web server written by +Michael Temari. A sample version is included with the distribution +archive and is unpacked in the source directory (normally +.BI /usr/local/src/httpdxxx). +Also provided is an example +.B httpd.mtype +file. This is an extension of the main configuration file which is normally +included when the main file is read. +.P +The makefile does not install +.B httpd.conf +and +.B httpd.mtype +automatically. The sample files included in the distribution are only +examples, you must copy and edit them for the needs of your own +installation. +.SH CONFIGURATION FILE FORMAT +.B httpd.conf +is an ascii file which consists of lines of the following form: +.P +.B directive LWS [parameters separated by LWS] +.br +NOTE: LWS denotes Linear White Space which is spaces and/or tabs +.SH CONFIGURATION FILE DIRECTIVES +The following are valid configuration file directives (listed in the order +they appear in the sample +.B httpd.conf +file provided in the distribution): +.P +.B serverroot redirect user chroot logfile dbgfile dirsend direxec +.B vhost auth proxyauth vpath include mtype +.P +To make the file more readable, for directives which occupy multiple +lines you may eliminate the directive on lines after the first and begin +these lines with LWS. + +.SH DESCRIPTIONS OF DIRECTIVES +.P +.B serverroot path + +The +.B serverroot +directive sets the translation for +.B // +to the given +.B path. + +.B redirect url + +The +.B redirect +directive will redirect the entire website via error code +"301 MOVED PERM" to specified url and original path of request. + +.B user username + +The +.B user +directive causes the server to run as the given +.B username +otherwise the server will run as whoever started it (normally root). + +.B chroot directory + +The +.B chroot +directive causes the server to chroot to the given directory after +the configuration and log files have been opened. Normally this will be the +home directory of the given username in the user directive. +.br +NOTE: +.B /~user +will be translated to the home directory of +.B user. +.br +NOTE: +.B // +will be translated to the serverroot directory. +.br +NOTE: if this directive is used then beware of the consequences. + +.B logfile filename + +The +.B logfile +directive tells the server where to log http transactions. +.br +NOTE: the log file must exist to enable logging. + +.B dbgfile filename + +The +.B dbgfile +directive tells the server where to log debugging of http transactions. +.br +NOTE: the debug log file must exist to enable debug logging. + +.B dirsend filelist + +The +.B dirsend +directive tells the server that when a directory is requested +that it should send the first file that it finds in the directory from the +.B filelist +for the request. + +.B direxec program + +The +.B direxec +directive tells the server that when a directory is requested +and no file is found from the +.B dirsend +directive that it should run the given +.B program. +.br +NOTE: the program normally generates a directory listing on the fly using +the +.B dir2html +program. +.br +NOTE: the program access is considered +.B X +with no access restrictions. + +.B vhost hostname vhostroot + +The +.B vhost +directive is for defining access for virtual hosts. If none are configured +then any host is accepted. If specified then access is only granted for +requests for hosts which are configured here. In the +.B vpath +section below the +.B /// +gets translated to the corresponding +.B vhostroot. + + +.B auth authname authdescription access [passwdfile [users]] + +The +.B auth +directive sets up different authorizations with the server. The +.B authname +is the name given to the authorization and is case insensitive. +The +.B authdescription +is the description of the authorization and is what +the user will see when asked to enter a username and password. The +access is one or more of +.B (RWX). +.B R +tells the server the URL can be read. +.B W +tells the server the URL can be overwritten. +.B X +tells the server +that the URL can and should be executed. Access is in addition to normal +Unix security considerations. For instance a file that can be written to +that does not have the +.B W +access will have an error returned. The +.B passwdfile +is the name of the password file to validate users against. If +.B passwdfile +is given as +.B '.' +then the system password file +.B (/etc/passwd) +will be used. If no +.B passwdfile +is given then no authorization is allowed for anyone. If no +.B users +are given then any validated user is authorized, otherwise only the given +.B users +are allowed. + +.B proxyauth authname authdescription access [passwdfile [users]] + +The +.B proxyauth +directive defines access authorization to be used for Proxy access. +.br +.B authname += Same as auth above +.br +.B authdescription += Same as auth above +.br +.B access += Must be R to allow proxy +.br +.B passwdfile += Same as auth above +.br +.B users += Same as auth above + +.B vpath from to [auth [access]] + +The +.B vpath +directive sets up URL path translations and authorizations. A +requested URL that matches +.B from +will be translated to +.B to +with the given +.B auth +and +.B access. +If +.B auth +does not exist then the URL will have no +.B access. +If +.B access +is not given then the access is taken from the +.B auth +record (see above). A +.B '.' +in place of the +.B to +means that the server should use a translation from another +.B vpath +record, but associate the given +.B auth +and access with the requested URL. A +.B '*' +may be at the end only of the +.B from +to provide a wildcard match. For example if the +.B from +has +.B /AB* +then any of +.B /ABCDEF +or +.B /AB +or +.B /ABmichael +will match, but +.B /AD or +.B /a +will not. The requested URL is first checked against each +.B vpath +record until an exact match (meaning URL match +.B from +and +.B from +had no +.B '*') +is found or the end of the list. Therefore a wildcard match will match +the last +.B from in the list in which it matched. +.br +NOTE: if at the beginning of the to field +.br + /~user will get translated to the home directory of the given user +.br + // will get translated to the serverroot directory + +.B include filename + +The +.B include +directive tells the server to read configuration information +from the given filename. +.br +NOTE: normally you get +.B mtype +directives in an included file. + +.B mtype mimetype extensions + +The +.B mtype +directive tells the server what +.B mimetype +to associate with files which have any of the given +.B extensions. +If no match is found then the file will be treated as +.B application/octet-stream. + + +.SH FILES +.B /etc/httpd.conf +.B /etc/httpd.mtype +.B /etc/passwd +.SH "SEE ALSO" +.BR httpd (8) +.BR http_status (5) +.SH NOTES +The source directory contains a commented sample +.B httpd.conf +and +.B httpd.mtype +files. +.P +You can run the server as +.B httpd -t /etc/httpd.conf +to see whether the configuration file is being parsed correctly. +.P +Although standard Minix does not have a graphical interface to support +browsers such as Netscape and Microsoft Internet Explorer, the +.B lynx +browser can be used on 32-bit Minix systems with enough memory. You can point +lynx to your own site to browse your own pages. +When debugging a web server there is nothing quite like browsing your own +pages to see whether things are working right. That said, be aware that +different web browsers may vary in how they interpet standard web page +features, and will certainly vary in how they interpret "extensions" to +the HTML standards. So checking a page with several browsers on several +platforms is always a good idea. +.SH BUGS +Not really a bug, but you can get in trouble if a real directory you want +to access shares the first part of its name with a +.B vpath +definition. You just have to pay attention to the directory names you use. +.SH AUTHOR +The Minix httpd server was created by and is maintained by Michael Temari + +.P +Man page was compiled by Al Woodhull +.\" updated 2006-06-01 diff --git a/commands/httpd0995/httpd.mtype b/commands/httpd0995/httpd.mtype new file mode 100644 index 000000000..d39335ad1 --- /dev/null +++ b/commands/httpd0995/httpd.mtype @@ -0,0 +1,40 @@ +# mime types By Michael Temari 12/29/2002 Ver 0.40 + +# if a file extension is not found in the configuration below the default +# mime type will be application/octet-stream. This default can be changed +# by entering a line with a mime type only with no extension. + +mtype + application/octet-stream + application/compress .Z + application/msword .doc + application/octet-stream .bin .exe + application/pdf .pdf + application/postscript .ps .ai .eps + application/smil .smil + application/x-gtar .gtar + application/x-gzip .gz + application/x-sh .sh + application/x-pn-realaudio .ra .ram + application/x-tar .tar + application/zip .zip + audio/basic .au .snd + audio/mpeg .mp3 + audio/x-aiff .aif .aiff .aifc + audio/x-midi .mid + audio/x-wav .wav + image/bmp .bmp + image/gif .gif + image/jpeg .jpg .jpeg .jpe + image/png .png + image/tiff .tiff .tif + image/x-rgb .rgb + image/x-xbitmap .xbm + multipart/x-www-form-urlencoded .wfu + text/html .html .htm + text/plain .txt .c .h + text/richtext .rtf .rtx + video/mpeg .mpg .mpeg .mpe + video/quicktime .qt .mov + video/x-msvideo .avi + video/x-sgi-movie .movie diff --git a/commands/httpd0995/httpd0995.txt b/commands/httpd0995/httpd0995.txt new file mode 100644 index 000000000..fc30e9a14 --- /dev/null +++ b/commands/httpd0995/httpd0995.txt @@ -0,0 +1,59 @@ +httpd0995 --- A www server for Minix 2 and Minix 3 +written by Michael Temari release 0.995 2006-05-14 + +Httpd is a World Wide Web (WWW) server. I wrote it from scratch so +the setup and configuration will not be like other web servers though +hopefully by reading this document there will be no problems in getting +my web server up and running on your Minix system. + +Earlier versions of this web server were in use for many years on +minix1.hampshire.edu and minix1.bio.umass.edu. + +Installation: unpack the tarball in /usr/local/src or another directory +of your choice: + + zcat < httpd0995.taz | tar xvfp - + +An httpd0995 directory will be created and files will be unpacked +there. The README file explains compilation, installation, +configuration, and use. Please also read the SECURITY file if you plan +to make your system accessible over the net. + +Changes for release 0.995: + +- a redirect capability has been added. If redirect is defined in the +configuration file then all request url's will be redirected to that +host with the original request. For instance, if in the configuration +file of minix1.hampshire.edu this line appears: + redirect http://minix1.woodhull.com/ +a request of http://minix1.hampshire.edu/some/page will return a 301 error +which is a redirect permanent to: http://minix.woodhull.com/some/page + +- several documentation files and man pages have been updated. + +Changes for release 0.994: + +- calling CGI programs has been made more secure. + +Changes for release 0.993: + +- a new method of authorizing proxy. You will no longer need the Proxy +entry in Auth and can remove the http://* entry in vpath. The old way +allowed for having different authorizations depending on what URLs were +asked for via proxy, i.e., you could allow proxy access only to +http:://www.hampshire.edu/. Now it is just a simple authorization for +allowing proxy or not. + +- avoids using a Minix 2.0.3 library call that was not present in Minix +2.0.2, and thus can be compiled with either of the two most recent +Minix releases. + +- a -v option has been added to display the current version then exit. + +- man pages added, other documentation updated. + +Changes for release 0.99: You can set a default in the httpd.mtype +file. A mime type with no extensions on a line will be the default. +Previously recompilation was needed to change the default mime type. + +updated 2006-06-01 (ASW) diff --git a/commands/httpd0995/net.c b/commands/httpd0995/net.c new file mode 100644 index 000000000..4d2638593 --- /dev/null +++ b/commands/httpd0995/net.c @@ -0,0 +1,240 @@ +/* net.c + * + * This file is part of httpd. + * + * 01/25/1996 Michael Temari + * 07/07/1996 Initial Release Michael Temari + * 12/29/2002 Michael Temari + * + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "net.h" + +_PROTOTYPE(static void release, (int *fd)); + +ipaddr_t myipaddr, rmtipaddr; +tcpport_t myport, rmtport; +char myhostname[256]; +char rmthostname[256]; +char rmthostaddr[3+1+3+1+3+1+3+1]; + +void GetNetInfo() +{ +nwio_tcpconf_t tcpconf; +int s; +struct hostent *hostent; + + /* Ask the system what our hostname is. */ + if(gethostname(myhostname, sizeof(myhostname)) < 0) + strcpy(myhostname, "unknown"); + + /* lets get our ip address and the clients ip address */ + s = ioctl(0, NWIOGTCPCONF, &tcpconf); + if(s < 0) { + myipaddr = 0; + myport = 0; + rmtipaddr = 0; + rmtport = 0; + strcpy(rmthostname, "??Unknown??"); + strcpy(rmthostaddr, "???.???.???.???"); + return; + } + + myipaddr = tcpconf.nwtc_locaddr; + myport = tcpconf.nwtc_locport; + rmtipaddr = tcpconf.nwtc_remaddr; + rmtport = tcpconf.nwtc_remport; + + /* Look up the host name of the remote host. */ + hostent = gethostbyaddr((char *) &rmtipaddr, sizeof(rmtipaddr), AF_INET); + if(!hostent) + strncpy(rmthostname, inet_ntoa(rmtipaddr), sizeof(rmthostname)-1); + else + strncpy(rmthostname, hostent->h_name, sizeof(rmthostname)-1); + + strcpy(rmthostaddr, inet_ntoa(rmtipaddr)); + + rmthostname[sizeof(rmthostname)-1] = '\0'; + + return; +} + +static void release(fd) +int *fd; +{ + if(*fd != -1) { + close(*fd); + *fd= -1; + } +} + +void daemonloop(service) +char *service; +{ +tcpport_t port; +struct nwio_tcpcl tcplistenopt; +struct nwio_tcpconf tcpconf; +struct nwio_tcpopt tcpopt; +struct servent *servent; +char *tcp_device; +int tcp_fd, client_fd, r; +int pfd[2]; +unsigned stall= 0; + + if((servent= getservbyname(service, "tcp")) == NULL) { + unsigned long p; + char *end; + + p = strtoul(service, &end, 0); + if(p <= 0 || p > 0xFFFF || *end != 0) { + fprintf(stderr, "httpd: %s: Unknown service\n", service); + exit(1); + } + port= htons((tcpport_t) p); + } else + port= servent->s_port; + + /* No client yet. */ + client_fd= -1; + + while (1) { + if((tcp_device = getenv("TCP_DEVICE")) == NULL) + tcp_device = TCP_DEVICE; + if ((tcp_fd= open(tcp_device, O_RDWR)) < 0) { + fprintf(stderr, "httpd: Can't open %s: %s", + tcp_device, strerror(errno)); + if (errno == ENOENT || errno == ENODEV + || errno == ENXIO) { + exit(1); + } + goto bad; + } + + tcpconf.nwtc_flags= NWTC_LP_SET | NWTC_UNSET_RA | NWTC_UNSET_RP; + tcpconf.nwtc_locport= port; + + if (ioctl(tcp_fd, NWIOSTCPCONF, &tcpconf) < 0) { + fprintf(stderr, "httpd: Can't configure TCP channel", + strerror(errno)); + exit(1); + } + +#ifdef NWTO_DEL_RST + tcpopt.nwto_flags= NWTO_DEL_RST; + + if (ioctl(tcp_fd, NWIOSTCPOPT, &tcpopt) < 0) { + fprintf(stderr, "httpd: Can't set TCP options", + strerror(errno)); + exit(1); + } +#endif + + if (client_fd != -1) { + /* We have a client, so start a server for it. */ + +#ifdef NWTO_DEL_RST + tcpopt.nwto_flags= 0; + (void) ioctl(client_fd, NWIOSTCPOPT, &tcpopt); +#endif + + fflush(NULL); + + /* Create a pipe to serve as an error indicator. */ + if (pipe(pfd) < 0) { + fprintf(stderr, "httpd: pipe", strerror(errno)); + goto bad; + } + + /* Fork twice to daemonize child. */ + switch (fork()) { + case -1: + fprintf(stderr, "httpd: fork", strerror(errno)); + close(pfd[0]); + close(pfd[1]); + goto bad; + case 0: + close(tcp_fd); + close(pfd[0]); + switch (fork()) { + case -1: + fprintf(stderr, "httpd: fork", + strerror(errno)); + write(pfd[1], &errno, sizeof(errno)); + exit(1); + case 0: + break; + default: + exit(0); + } + dup2(client_fd, 0); + dup2(client_fd, 1); + close(client_fd); + close(pfd[1]); + + /* Break out of the daemon loop, continuing with + * the normal httpd code to serve the client. + */ + return; + + default: + release(&client_fd); + close(pfd[1]); + wait(NULL); + r= read(pfd[0], &errno, sizeof(errno)); + close(pfd[0]); + if (r != 0) goto bad; + break; + } + } + + /* Wait for a new connection. */ + tcplistenopt.nwtcl_flags= 0; + + while (ioctl(tcp_fd, NWIOTCPLISTEN, &tcplistenopt) < 0) { + if (errno != EAGAIN) { + fprintf(stderr, "httpd: Unable to listen: %s", + strerror(errno)); + } + goto bad; + } + + /* We got a connection. */ + client_fd= tcp_fd; + tcp_fd= -1; + + /* All is well, no need to stall. */ + stall= 0; + continue; + + bad: + /* All is not well, release resources. */ + release(&tcp_fd); + release(&client_fd); + + /* Wait a bit if this happens more than once. */ + if (stall != 0) { + sleep(stall); + stall <<= 1; + } else { + stall= 1; + } + } +} diff --git a/commands/httpd0995/net.h b/commands/httpd0995/net.h new file mode 100644 index 000000000..255fd051b --- /dev/null +++ b/commands/httpd0995/net.h @@ -0,0 +1,17 @@ +/* net.h + * + * This file is part of httpd. + * + * + * 01/25/1996 Michael Temari + * 07/07/1996 Initial Release Michael Temari + * 12/29/2002 Michael Temari + * + */ + +_PROTOTYPE(void GetNetInfo, (void)); +_PROTOTYPE(void daemonloop, (char *service)); + +extern char myhostname[256]; +extern char rmthostname[256]; +extern char rmthostaddr[3+1+3+1+3+1+3+1]; diff --git a/commands/httpd0995/pass.c b/commands/httpd0995/pass.c new file mode 100644 index 000000000..c7011322e --- /dev/null +++ b/commands/httpd0995/pass.c @@ -0,0 +1,213 @@ +/* pass.c + * + * This file is part of httpd. + * + * 07/07/1996 Initial Release Michael Temari + * 12/29/2002 Michael Temari + * + */ +#include +#include +#include +#include +#include +#include +#include +#include +#ifdef _MINIX +#include +#endif + +#define STD_PASSWD_FILE "/etc/passwd" + +#include "pass.h" + +static char buffer[1024]; +static char *pwduser; +static char *pwdpass; +static char *pwde[4]; + +_PROTOTYPE(static int getuser, (char *pwdfile, char *user)); + +static int getuser(pwdfile, user) +char *pwdfile; +char *user; +{ +FILE *fp; +char *p; +int i; + + if((fp = fopen(pwdfile, "r")) == (FILE *)NULL) + return(-1); + + for(i = 0; i < 4; i ++) pwde[i] = ""; + + while(1) { + if(fgets(buffer, sizeof(buffer), fp) == (char *)NULL) { + fclose(fp); + return(-1); + } + p = buffer; + pwduser = p; + while(*p && *p != ':') p++; + if(*p != ':') continue; + *p++ = '\0'; + if(strcmp(pwduser, user)) continue; + pwdpass = p; + while(*p && *p != ':' && *p != '\r' && *p != '\n') p++; + if(*p == ':') + *p++ = '\0'; + else { + if(*p) *p = '\0'; + fclose(fp); + } + for(i = 0; i < 4; i++) { + pwde[i] = p; + while(*p && *p != ':' && *p != '\r' && *p != '\n') p++; + if(*p == ':') + *p++ = '\0'; + else { + if(*p) *p = '\0'; + break; + } + } + fclose(fp); + + return(0); + } +} + +int passfile(pwdfile) +char *pwdfile; +{ +FILE *fp; + + if(!strcmp(pwdfile, STD_PASSWD_FILE)) + return(0); + + if((fp = fopen(pwdfile, "r")) == (FILE *)NULL) + return(-1); + + fclose(fp); + + return(0); +} + +int passuser(pwdfile, user) +char *pwdfile; +char *user; +{ + if(!strcmp(pwdfile, STD_PASSWD_FILE)) + if(getpwnam(user) == (struct passwd *)NULL) + return(-1); + else + return(0); + + return(getuser(pwdfile, user)); +} + +int passnone(pwdfile, user) +char *pwdfile; +char *user; +{ +struct passwd *pwd; + + if(!strcmp(pwdfile, STD_PASSWD_FILE)) + if((pwd = getpwnam(user)) == (struct passwd *)NULL) + return(-1); + else + if(!strcmp(pwd->pw_passwd, crypt("", pwd->pw_passwd))) + return(-1); + else + return(0); + + if(getuser(pwdfile, user)) + return(-1); + + if(!strcmp(pwdpass, crypt("", pwdpass))) + return(-1); + else + return(0); +} + +int passpass(pwdfile, user, pass) +char *pwdfile; +char *user; +char *pass; +{ +struct passwd *pwd; + + if(!strcmp(pwdfile, STD_PASSWD_FILE)) + if((pwd = getpwnam(user)) == (struct passwd *)NULL) + return(-1); + else + if(strcmp(pwd->pw_passwd, crypt(pass, pwd->pw_passwd))) + return(-1); + else + return(0); + + if(getuser(pwdfile, user)) + return(-1); + + if(strcmp(pwdpass, crypt(pass, pwdpass))) + return(-1); + else + return(0); +} + +int passadd(pwdfile, user, pass, e1, e2, e3, e4) +char *pwdfile; +char *user; +char *pass; +char *e1; +char *e2; +char *e3; +char *e4; +{ +FILE *fp; +time_t salt; +char sl[2]; +int cn; +char *ee1; +char *ee2; +char *ee3; +char *ee4; + + + if(pwdfile == (char *)NULL || + user == (char *)NULL || + pass == (char *)NULL) + return(PASS_ERROR); + + if(!strcmp(pwdfile, STD_PASSWD_FILE)) + return(PASS_ERROR); + + if(!getuser(pwdfile, user)) + return(PASS_USEREXISTS); + + time(&salt); + sl[0] = (salt & 077) + '.'; + sl[1] = ((salt >> 6) & 077) + '.'; + for (cn = 0; cn < 2; cn++) { + if (sl[cn] > '9') sl[cn] += 7; + if (sl[cn] > 'Z') sl[cn] += 6; + } + + if(e1 == (char *)NULL) ee1 = ""; else ee1 = e1; + if(e2 == (char *)NULL) ee2 = ""; else ee2 = e2; + if(e3 == (char *)NULL) ee3 = ""; else ee3 = e3; + if(e4 == (char *)NULL) ee4 = ""; else ee4 = e4; + + /* XXX need to add locking mechanics to add new user */ + + if((fp = fopen(pwdfile, "a")) == (FILE *)NULL) + return(PASS_ERROR); + + fprintf(fp, "%s:%s:%s:%s:%s:%s\n", user, crypt(pass, sl), ee1, ee2, ee3, ee4); + + fclose(fp); + + /* XXX need to add unlocking mechanics to add new user */ + + return(PASS_GOOD); +} diff --git a/commands/httpd0995/pass.h b/commands/httpd0995/pass.h new file mode 100644 index 000000000..22a4e1dc8 --- /dev/null +++ b/commands/httpd0995/pass.h @@ -0,0 +1,18 @@ +/* pass.h + * + * This file is part of httpd. + * + * 07/07/1996 Initial Release Michael Temari + * 12/29/2002 Initial Release Michael Temari + * + */ + +_PROTOTYPE(int passfile, (char *pwdfile)); +_PROTOTYPE(int passuser, (char *pwdfile, char *user)); +_PROTOTYPE(int passnone, (char *pwdfile, char *user)); +_PROTOTYPE(int passpass, (char *pwdfile, char *user, char *pass)); +_PROTOTYPE(int passadd, (char *pwdfile, char *user, char *pass, char *e1, char *e2, char *e3, char *e4)); + +#define PASS_GOOD 0 +#define PASS_USEREXISTS 1 +#define PASS_ERROR -1 diff --git a/commands/httpd0995/police.c b/commands/httpd0995/police.c new file mode 100644 index 000000000..b71af8374 --- /dev/null +++ b/commands/httpd0995/police.c @@ -0,0 +1,407 @@ +/* police.c + * + * This file is part of httpd. + * + * 02/17/1996 Michael Temari + * 07/07/1996 Initial Release Michael Temari + * 12/29/2002 Michael Temari + * + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "http.h" +#include "utility.h" +#include "config.h" +#include "pass.h" + +#define MATCH_NONE 0 +#define MATCH_WILD 1 +#define MATCH_FULL 2 + +_PROTOTYPE(static int authaccess, (struct http_request *rq, struct http_reply *rp)); +_PROTOTYPE(static void purl, (struct http_request *rq, struct http_reply *rp)); +_PROTOTYPE(static char *virt, (char *to, char *host)); + +static int authaccess(rq, rp) +struct http_request *rq; +struct http_reply *rp; +{ +struct auth *auth; +struct authuser *pu; + + /* set authorization to be checked against */ + if(rq->type == HTTP_REQUEST_TYPE_PROXY) + auth = proxyauth; + else + auth = rp->auth; + + /* no authorization so no access to anyone */ + if(auth == NULL) { + rp->status = HTTP_STATUS_FORBIDDEN; + strcpy(rp->statusmsg, "No Authoriation"); + return(-1); + } + + /* access must be R for PROXY */ + if(rq->type == HTTP_REQUEST_TYPE_PROXY) + if(!(auth->urlaccess & URLA_READ)) { + rp->status = HTTP_STATUS_FORBIDDEN; + strcpy(rp->statusmsg, "Proxy not authorized"); + return(-1); + } + + /* no password file so it is a free for all */ + if(auth->passwdfile == NULL) + return(0); + + /* they did not give us an authorized user */ + if(rq->authuser[0] == '\0') { + if(rq->type == HTTP_REQUEST_TYPE_PROXY) + rp->status = HTTP_STATUS_PROXY_AUTH_REQRD; + else + rp->status = HTTP_STATUS_UNAUTHORIZED; + strcpy(rp->statusmsg, "No Authorized User Given"); + return(-1); + } + + /* check if user okay */ + pu = auth->users; + if(pu == NULL) + ; /* no user list we allow anyone in file */ + else { + while(pu != NULL) { + if(!strcmp(pu->user, rq->authuser)) + break; + pu = pu->next; + } + /* user is not in list so no access */ + if(pu == NULL) { + if(rq->type == HTTP_REQUEST_TYPE_PROXY) + rp->status = HTTP_STATUS_PROXY_AUTH_REQRD; + else + rp->status = HTTP_STATUS_UNAUTHORIZED; + strcpy(rp->statusmsg, "Forbidden User not authorized"); + return(-1); + } + } + + /* check if password file exists, if not no access */ + if(passfile(auth->passwdfile)) { + rp->status = HTTP_STATUS_FORBIDDEN; + strcpy(rp->statusmsg, "Invalid passwd file"); + return(-1); + } + + /* check if user in password file, if not no access */ + if(passuser(auth->passwdfile, rq->authuser)) { + if(rq->type == HTTP_REQUEST_TYPE_PROXY) + rp->status = HTTP_STATUS_PROXY_AUTH_REQRD; + else + rp->status = HTTP_STATUS_UNAUTHORIZED; + strcpy(rp->statusmsg, "Forbidden Bad User"); + return(-1); + } + + /* check if a password exists, if not no access */ + if(passnone(auth->passwdfile, rq->authuser)) { + if(rq->type == HTTP_REQUEST_TYPE_PROXY) + rp->status = HTTP_STATUS_PROXY_AUTH_REQRD; + else + rp->status = HTTP_STATUS_UNAUTHORIZED; + strcpy(rp->statusmsg, "Forbidden no password"); + return(-1); + } + + /* check if password matches, if not no access */ + if(passpass(auth->passwdfile, rq->authuser, rq->authpass)) { + if(rq->type == HTTP_REQUEST_TYPE_PROXY) + rp->status = HTTP_STATUS_PROXY_AUTH_REQRD; + else + rp->status = HTTP_STATUS_UNAUTHORIZED; + strcpy(rp->statusmsg, "Forbidden bad password"); + return(-1); + } + + /* whew, all the checks passed so I guess we let them have it */ + return(0); +} + +int police(rq, rp) +struct http_request *rq; +struct http_reply *rp; +{ +int size; +struct stat st; +struct dirsend *ds; + + purl(rq, rp); + + rp->mtype = "text/html"; + +#ifdef DEBUG + fprintf(stderr, "httpd: Trying %s\n", rp->realurl); +#endif + + /* now check authorizations */ + if(authaccess(rq, rp)) { + /* Don't give them any details why authorization failed */ + strcpy(rp->statusmsg, "No Access Granted"); + return(0); + } + + /* a proxy request only needs an authorization check */ + if(rq->type == HTTP_REQUEST_TYPE_PROXY) + return(0); + + /* check access to real url */ + if(stat(rp->realurl, &st)) { + if(errno == EACCES) + rp->status = HTTP_STATUS_FORBIDDEN; + else + rp->status = HTTP_STATUS_NOT_FOUND; + strcpy(rp->statusmsg, strerror(errno)); + /* a PUT and NOT FOUND is okay since we are creating */ + if(rq->method != HTTP_METHOD_PUT || rp->status != HTTP_STATUS_NOT_FOUND) + return(0); + } + + /* If it is a directory do the appropriate thang! */ + if(rq->method == HTTP_METHOD_GET || rq->method == HTTP_METHOD_HEAD) + if((st.st_mode & S_IFMT) == S_IFDIR) { + if(rq->url[strlen(rq->url) - 1] != '/') { + strncat(rq->url, "/", sizeof(rq->url) - strlen(rq->url)); + rp->status = HTTP_STATUS_MOVED_TEMP; + sprintf(rp->statusmsg, "Moved to %s", rq->url); + return(0); + } + size = strlen(rq->url); + ds = dirsend; + while(ds != NULL) { + strncpy(rq->url+size, ds->file, sizeof(rq->url)-size); + purl(rq, rp); + if(stat(rp->realurl, &st)) { + if(errno == EACCES) + rp->status = HTTP_STATUS_FORBIDDEN; + else + if(errno != ENOENT) + rp->status = HTTP_STATUS_NOT_FOUND; + } else + break; + if(rp->status != HTTP_STATUS_OK) { + strcpy(rp->statusmsg, strerror(errno)); + return(0); + } + ds = ds->next; + } + if(ds == NULL) { + rq->url[size] = '\0'; + purl(rq, rp); + if(stat(rp->realurl, &st)) { + if(errno == EACCES) + rp->status = HTTP_STATUS_FORBIDDEN; + else + rp->status = HTTP_STATUS_NOT_FOUND; + strcpy(rp->statusmsg, strerror(errno)); + return(0); + } + } + } + + if(rq->method == HTTP_METHOD_PUT && !(rp->urlaccess & URLA_WRITE)) { + rp->status = HTTP_STATUS_METHOD_NOT_ALLOWED; + strcpy(rp->statusmsg, "Method not allowed"); + return(0); + } + + if(rp->status == HTTP_STATUS_OK) { + /* Here is where we check if it is a program or script to run */ + if(cgiexec(rq, rp)) + return(0); + + if((st.st_mode & S_IFMT) == S_IFDIR) { + rp->status = HTTP_STATUS_NOT_FOUND; + strcpy(rp->statusmsg, "Directory listing not available"); + return(0); + } + + if((st.st_mode & S_IFMT) != S_IFREG) { + rp->status = HTTP_STATUS_NOT_FOUND; + strcpy(rp->statusmsg, "Not a regular file"); + return(0); + } + } + + /* open the URL for updating */ + if(rq->method == HTTP_METHOD_PUT) { + rp->status = HTTP_STATUS_OK; + strcpy(rp->statusmsg, "OK"); + rp->ofd = open(rp->realurl, O_WRONLY | O_CREAT | O_TRUNC); + if(rp->ofd < 0) { + if(errno == EACCES) + rp->status = HTTP_STATUS_FORBIDDEN; + else + rp->status = HTTP_STATUS_NOT_FOUND; + strcpy(rp->statusmsg, strerror(errno)); + return(0); + } + return(0); + } + + if(!(rp->urlaccess & URLA_READ)) { + rp->status = HTTP_STATUS_FORBIDDEN; + strcpy(rp->statusmsg, "No way..."); + return(0); + } + + rp->mtype = mimetype(rp->realurl); + + rp->size = st.st_size; + rp->modtime = st.st_mtime; + + /* open the url if it is a file */ + rp->fd = open(rp->realurl, O_RDONLY); + if(rp->fd < 0) { + if(errno == EACCES) + rp->status = HTTP_STATUS_FORBIDDEN; + else + rp->status = HTTP_STATUS_NOT_FOUND; + strcpy(rp->statusmsg, strerror(errno)); + return(0); + } + + return(0); +} + +static void purl(rq, rp) +struct http_request *rq; +struct http_reply *rp; +{ +struct vpath *pv; +int gotreal, gotperm; +char *p; +int match; +int len; + + gotreal = 0; gotperm = 0; + +#ifdef DEBUG + fprintf(stderr, "httpd: Processing url = \"%s\"\n", rq->url); +#endif + + /* remove any .. references */ + p = rq->url; + while(*p) { + while(*p && *p != '/') p++; + if(*p != '/') continue; + p++; + if(*p != '.') continue; + p++; + if(*p != '.') continue; + p++; + strcpy(p - 3, p); + p = p - 3; + } + + for(pv = vpath; pv != NULL; pv = pv->next) { + len = strlen(pv->from) - 1; + if(pv->from[len] == '*' || pv->from[len] == '$') + if(len == 0) + match = MATCH_WILD; + else + match = strncmp(rq->url, pv->from, len) ? MATCH_NONE : MATCH_WILD; + else + if(!strcmp(rq->url, pv->from)) + match = MATCH_FULL; + else + match = MATCH_NONE; +#ifdef DEBUG + fprintf(stderr, "httpd: Trying \"%s\" %d %d %d %s\n", + pv->from, match, gotreal, gotperm, pv->auth->name); +#endif + if(match != MATCH_NONE) { + gotperm = 1; + rp->auth = pv->auth; + if(pv->urlaccess == -1 && rp->auth != NULL) + rp->urlaccess = rp->auth->urlaccess; + else + rp->urlaccess = pv->urlaccess; + if(strcmp(pv->to, ".")) { + gotreal = 1; + strncpy(rp->realurl, virt(pv->to, rq->host), sizeof(rp->realurl)); + rp->realurl[sizeof(rp->realurl)-1] = '\0'; + if(match == MATCH_WILD && pv->from[len] != '$') { + strncat(rp->realurl, rq->url+len, sizeof(rp->realurl) - strlen(rp->realurl)); + rp->realurl[sizeof(rp->realurl)-1] = '\0'; + } + } + } + if(match == MATCH_FULL) break; + } + + if(rp->urlaccess == -1) rp->urlaccess = mkurlaccess(""); + + if(!gotreal) { + strncpy(rp->realurl, rq->url, sizeof(rp->realurl)); + rp->realurl[sizeof(rp->realurl)-1] = '\0'; + } + + if(!gotperm) + rp->auth = NULL; + +#ifdef DEBUG + fprintf(stderr, "DEBUG: url = \"%s\" realurl = \"%s\" auth = \"%s\"\n", + rq->url, rp->realurl, ((rp->auth == NULL) ? "No Access" : rp->auth->name)); + fprintf(stderr, "DEBUG: query = %s\n", rq->query); +#endif + + return; +} + +static char *virt(to, host) +char *to; +char *host; +{ +static char vroot[256]; +struct vhost *ph; + +#ifdef DEBUG +fprintf(stderr, "virt: %s %s\n", to, host); +#endif + + if(vhost == NULL) return(to); + + if(to[0] != '/') return(to); + if(to[1] != '/') return(to); + if(to[2] != '/') return(to); + + vroot[0] = '\0'; + + for(ph = vhost; ph != NULL; ph = ph->next) { +#ifdef DEBUG + fprintf(stderr, "ph: %s %s %s\n", ph->hname, ph->root, vroot); +#endif + if(!strcmp(ph->hname, "*") && vroot[0] == '\0') + strncpy(vroot, ph->root, sizeof(vroot)); + if(!strcasecmp(ph->hname, host)) { + strncpy(vroot, ph->root, sizeof(vroot)); + break; + } + } + + strncat(vroot, to+3, sizeof(vroot)); + +#ifdef DEBUG + fprintf(stderr, "vroot: %s\n", vroot); +#endif + + return(vroot); +} diff --git a/commands/httpd0995/process.c b/commands/httpd0995/process.c new file mode 100644 index 000000000..665fc70a8 --- /dev/null +++ b/commands/httpd0995/process.c @@ -0,0 +1,90 @@ +/* process.c + * + * This file is part of httpd. + * + * 02/17/1996 Michael Temari + * 07/07/1996 Initial Relase Michael Temari + * 12/29/2002 Michael Temari + * 05/14/2006 Michael Temari + * + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "config.h" +#include "http.h" +#include "utility.h" + +int processrequest(rq, rp) +struct http_request *rq; +struct http_reply *rp; +{ + /* clear out http_reply */ + memset(rp, 0, sizeof(*rp)); + rp->status = HTTP_STATUS_OK; + strcpy(rp->statusmsg, "OK"); + rp->modtime = (time_t) -1; + rp->urlaccess = -1; + rp->size = 0; + rp->fd = -1; + rp->ofd = -1; + rp->pid = 0; + + if(Redirect != NULL) { + rp->status = HTTP_STATUS_MOVED_PERM; + strcpy(rp->realurl, Redirect); + strcat(rp->realurl, rq->uri); + strcpy(rq->url, rp->realurl); + return(0); + } + + /* Simple requests can only be a GET */ + if(rq->type == HTTP_REQUEST_TYPE_SIMPLE && rq->method != HTTP_METHOD_GET) { + rp->status = HTTP_STATUS_BAD_REQUEST; + strcpy(rp->statusmsg, "Bad request"); + return(0); + } + + /* I don't know this method */ + if(rq->method == HTTP_METHOD_UNKNOWN) { + rp->status = HTTP_STATUS_NOT_IMPLEMENTED; + strcpy(rp->statusmsg, "Method not implemented"); + return(0); + } + + /* Check for access and real location of url */ + if(police(rq, rp)) + return(-1); + + /* We're done if there was an error accessing the url */ + if(rp->status != HTTP_STATUS_OK) + return(0); + + /* Check to see if we have a newer version for them */ + if(rq->method == HTTP_METHOD_GET) + if(rq->ifmodsince != (time_t) -1) + if(rq->ifmodsince < time((time_t *)NULL)) + if(rp->modtime != (time_t) -1 && rp->modtime <= rq->ifmodsince) { + rp->status = HTTP_STATUS_NOT_MODIFIED; + strcpy(rp->statusmsg, "Not modified"); + close(rp->fd); + rp->fd = -1; + return(0); + } + + rp->status = HTTP_STATUS_OK; + strcpy(rp->statusmsg, "OK"); + + if(rp->size != 0) + rp->keepopen = rq->keepopen; + + return(0); +} diff --git a/commands/httpd0995/proxy.c b/commands/httpd0995/proxy.c new file mode 100644 index 000000000..d93acee9e --- /dev/null +++ b/commands/httpd0995/proxy.c @@ -0,0 +1,292 @@ +/* proxy.c Copyright 2000 by Michael Temari All Rights Reserved */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "config.h" +#include "http.h" +#include "utility.h" +#include "net.h" + +_PROTOTYPE(static int connect, (char *host)); +#if 0 +_PROTOTYPE(static int readline, (char *p, int len)); +#endif +_PROTOTYPE(static int sendout, (int fd, char *data)); + +static int connect(host) +char *host; +{ +nwio_tcpconf_t tcpconf; +nwio_tcpcl_t tcpcopt; +char *tcp_device; +int netfd; +ipaddr_t nethost; +tcpport_t netport = 0; +struct hostent *hp; +struct servent *sp; +char *p; +int s; +int tries; + + p = host; + while(*p && *p != ':') p++; + if(*p == ':') { + *p++ = '\0'; + netport = htons(atoi(p)); + } + + if((hp = gethostbyname(host)) == (struct hostent *)NULL) { + fprintf(stderr, "Unknown host %s!\n", host); + return(-1); + } else + memcpy((char *) &nethost, (char *) hp->h_addr, hp->h_length); + + /* Now, to which port must we connect? */ + if(netport == 0) + if((sp = getservbyname("http", "tcp")) == (struct servent *)NULL) { + fprintf(stderr, "HTTP port is unknown????\n"); + return(-1); + } else + netport = sp->s_port; + + /* Connect to the host */ + if((tcp_device = getenv("TCP_DEVICE")) == NULL) + tcp_device = TCP_DEVICE; + + if((netfd = open(tcp_device, O_RDWR)) < 0) { + perror("httpget: opening tcp"); + return(-1); + } + + tcpconf.nwtc_flags = NWTC_LP_SEL | NWTC_SET_RA | NWTC_SET_RP; + tcpconf.nwtc_remaddr = nethost; + tcpconf.nwtc_remport = netport; + + s = ioctl(netfd, NWIOSTCPCONF, &tcpconf); + if(s < 0) { + perror("httpget: NWIOSTCPCONF"); + close(netfd); + return(-1); + } + + s = ioctl(netfd, NWIOGTCPCONF, &tcpconf); + if(s < 0) { + perror("httpget: NWIOGTCPCONF"); + close(netfd); + return(-1); + } + + tcpcopt.nwtcl_flags = 0; + + tries = 0; + do { + s = ioctl(netfd, NWIOTCPCONN, &tcpcopt); + if(s == -1 && errno == EAGAIN) { + if(tries++ >= 10) + break; + sleep(10); + } else + break; + } while(1); + + if(s < 0) { + perror("httpget: NWIOTCPCONN"); + close(netfd); + return(-1); + } + + return(netfd); +} + +char buffer[8192]; + +#if 0 +static int readline(p, len) +char *p; +int len; +{ +int c; +int cr = 0; +int n = 0; + + len--; + if(len < 0) return(-1); + while(len > 0 && (c = getchar()) != EOF) { + if(c == '\n' && cr) { + *p = '\0'; + return(n); + } + if(c == '\r') { + cr = 1; + continue; + } + n++; + *p++ = c; + } + *p = '\0'; + return(n); +} +#endif + +static int sendout(fd, data) +int fd; +char *data; +{ + if(strlen(data) > 0) + write(fd, data, strlen(data)); + write(fd, "\r\n", 2); + if(dbglog != (FILE *)NULL) { + fprintf(dbglog, "REPLY: %s\n", data); + fflush(dbglog); + } + + return(0); +} + +void proxy(rq, rp) +struct http_request *rq; +struct http_reply *rp; +{ +int s; +char *p; +char *ps; +char *b; +char *host; +static char user[256]; +static char pass[256]; +char *url; +char *at; +int fd; +int bad; + + while(1) { + bad = 0; + p = rq->uri; + if(tolower(*p++) != 'h') bad++; + if(tolower(*p++) != 't') bad++; + if(tolower(*p++) != 't') bad++; + if(tolower(*p++) != 'p') bad++; + if(tolower(*p++) != ':') bad++; + if(tolower(*p++) != '/') bad++; + if(tolower(*p++) != '/') bad++; + if(bad) { + sprintf(buffer, "HTTP/%d.%d 400 Bad Request", + rq->vmajor, rq->vminor); + sendout(1, buffer); + sendout(1, ""); + sendout(1, "Proxy Request was not http:"); + return; + } + host = p; + while(*p && *p != '/') p++; + url = p; + *url = '\0'; + at = strchr(host, '@'); + if(at != (char *)NULL) { + *at = '\0'; + p = host; + while(*p && *p != ':') p++; + if(*p) + *p++ = '\0'; + strcpy(user, host); + strcpy(pass, p); + host = at + 1; + } else { + user[0] = '\0'; + pass[0] = '\0'; + } + + fd = connect(host); + if(fd < 0) { + sprintf(buffer, "HTTP/%d.%d 400 Bad Request", + rq->vmajor, rq->vminor); + sendout(1, buffer); + sendout(1, ""); + sendout(1, "Could not connect to host"); + return; + } + if(rq->method == HTTP_METHOD_GET) + write(fd, "GET ", 4); else + if(rq->method == HTTP_METHOD_POST) + write(fd, "POST ", 5); + *url = '/'; + if(strlen(url) > 0) + write(fd, url, strlen(url)); + write(fd, " ", 1); + sprintf(buffer, "HTTP/%d.%d", rq->vmajor, rq->vminor); + sendout(fd, buffer); + if(rq->ifmodsince != -1) { + write(fd, "If-Mod-Since: ", 14); + sendout(fd, httpdate(&rq->ifmodsince)); + } + if(rq->size != 0) { + sendout(fd, "Content-Type: application/x-www-form-urlencoded"); + sprintf(buffer, "Content-Length: %lu", rq->size); + sendout(fd, buffer); + } + if(*rq->cookie) { + sprintf(buffer, "Cookie: %s", rq->cookie); + sendout(fd, buffer); + } + if(*rq->useragent) { + sprintf(buffer, "User-Agent: %s", rq->useragent); + sendout(fd, buffer); + } + if(*rq->host) { + sprintf(buffer, "Host: %s", rq->host); + sendout(fd, buffer); + } + if(*rq->wwwauth) { + sprintf(buffer, "Authorization: %s", rq->wwwauth); + sendout(fd, buffer); + } + sprintf(buffer, "X-Forwarded-From: %s", rmthostaddr); + sendout(fd, buffer); + sendout(fd, ""); + if(rq->size != 0) { + if(stdlog != (FILE *)NULL) { + fprintf(stdlog, "%s %s %d %d ", + logdate((time_t *)NULL), rmthostname, + rq->method, rp->status); + fprintf(stdlog, "proxy %s?", rq->uri); + } + while((s = read(0, buffer, rq->size > + sizeof(buffer) ? sizeof(buffer) : rq->size)) > 0) { + write(fd, buffer, s); + rq->size -= s; + b = buffer; + if(stdlog != (FILE *)NULL) + while(s--) fputc(*b++, stdlog); + if(rq->size == 0) break; + } + if(stdlog != (FILE *)NULL) { + fprintf(stdlog, "\n"); + fflush(stdlog); + } + } + while((s = read(fd, buffer, sizeof(buffer))) > 0) { + write(1, buffer, s); + } + close(fd); + return; + } +} diff --git a/commands/httpd0995/reply.c b/commands/httpd0995/reply.c new file mode 100644 index 000000000..ba46535b6 --- /dev/null +++ b/commands/httpd0995/reply.c @@ -0,0 +1,189 @@ +/* reply.c + * + * This file is part of httpd. + * + * 02/17/1996 Michael Temari + * 07/07/1996 Initial Release Michael Temari + * 12/29/2002 Michael Temari + * + */ +#include +#include +#include +#include +#include +#include + +#include "http.h" +#include "utility.h" +#include "net.h" +#include "config.h" + +#define SERVER "Server: "VERSION + +_PROTOTYPE(static void GotAlarm, (int sig)); +_PROTOTYPE(static int sendout, (char *data)); + +static void GotAlarm(sig) +int sig; +{ +} + +static int sendout(data) +char *data; +{ + if(strlen(data) > 0) + write(1, data, strlen(data)); + write(1, "\r\n", 2); + if(dbglog != (FILE *)NULL) { + fprintf(dbglog, "REPLY: %s\n", data); + fflush(dbglog); + } + + return(0); +} + +int sendreply(rp, rq) +struct http_reply *rp; +struct http_request *rq; +{ +int s; +int s2; +int e; +static char buffer[8192]; + + if(rq->type != HTTP_REQUEST_TYPE_PROXY) + /* We're receiving data from a */ + if(rq->method == HTTP_METHOD_POST || + (rq->method == HTTP_METHOD_PUT && rp->status == HTTP_STATUS_OK)) { + if(rq->type != HTTP_REQUEST_TYPE_FULL) + return(0); + if(rq->method == HTTP_METHOD_PUT) + rp->status = HTTP_STATUS_CREATED; + else + rp->status = HTTP_STATUS_OK; + while(rq->size != 0) { + s = read(0, buffer, (rq->size > sizeof(buffer)) ? sizeof(buffer) : rq->size); + if(s <= 0) { + rp->status = HTTP_STATUS_SERVER_ERROR; + strcpy(rp->statusmsg, strerror(errno)); + close(rp->fd); + close(rp->ofd); + break; + } + rq->size -= s; + s2 = write(rp->ofd, buffer, s); + if(s2 != s) break; + } + } + + if(rp->status != HTTP_STATUS_OK && rp->status != HTTP_STATUS_CREATED && + rp->status != HTTP_STATUS_NOT_MODIFIED) + rp->keepopen = 0; + + if(rp->status == HTTP_STATUS_NOT_MODIFIED) { + sprintf(buffer, "

Error %03d %s

", + rp->status, rp->statusmsg); + rp->size = strlen(buffer); + rp->keepopen = rq->keepopen; + } + + if(!rp->headers) { + + if((rq->type == HTTP_REQUEST_TYPE_PROXY && rp->status != HTTP_STATUS_OK) || + rq->type == HTTP_REQUEST_TYPE_FULL) { + sprintf(buffer, "HTTP/%d.%d %03d %s", + rq->vmajor, rq->vminor, rp->status, rp->statusmsg); + sendout(buffer); + sendout(SERVER); + if(rp->status == HTTP_STATUS_MOVED_PERM || + rp->status == HTTP_STATUS_MOVED_TEMP) { +#if 1 + sprintf(buffer, "Location: %s", rq->url); +#else + sprintf(buffer, "Location: http://%s%s", myhostname, rq->url); +#endif + sendout(buffer); + } + if(rp->keepopen) + sendout("Connection: Keep-Alive"); + else + sendout("Connection: Close"); + if(rp->status == HTTP_STATUS_UNAUTHORIZED && rp->auth != NULL) { + sprintf(buffer, "WWW-Authenticate: Basic realm=\"%s\"", rp->auth->desc); + sendout(buffer); + } + if(rp->status == HTTP_STATUS_PROXY_AUTH_REQRD && proxyauth != NULL) { + sprintf(buffer, "Proxy-Authenticate: Basic realm=\"%s\"", proxyauth->desc); + sendout(buffer); + } + if(rp->modtime != (time_t) -1) { + sprintf(buffer, "Last-Modified: %s", httpdate(&rp->modtime)); + sendout(buffer); + } + if(rp->size != 0) { + sprintf(buffer, "Content-Length: %lu", rp->size); + sendout(buffer); + } + if(rp->status == HTTP_STATUS_OK) { + sprintf(buffer, "Content-Type: %s", rp->mtype); + sendout(buffer); + } else + sendout("Content-Type: text/html"); + if(!rp->headers) + sendout(""); + } else + if(rp->status != HTTP_STATUS_OK) + return(0); + } + + if(rp->status != HTTP_STATUS_OK && rp->status != HTTP_STATUS_CREATED) { + sprintf(buffer, "

Error %03d %s

", + rp->status, rp->statusmsg); + sendout(buffer); + return(0); + } + + if(rq->type == HTTP_REQUEST_TYPE_PROXY) { + proxy(rq, rp); + return(0); + } + + /* send out entity body */ + if(rq->method == HTTP_METHOD_GET || rq->method == HTTP_METHOD_POST) { + errno = 0; + while(1) { + alarm(0); + signal(SIGALRM, GotAlarm); + alarm(10); + s = read(rp->fd, buffer, sizeof(buffer)); + e = errno; + alarm(0); + if(s > 0) { + s2 = write(1, buffer, s); + e = errno; + if(s2 != s) break; + continue; + } + if(s == 0) break; + if(s < 0 && e != EINTR) break; + signal(SIGALRM, GotAlarm); + alarm(2); + s = read(0, buffer, 1); + e = errno; + alarm(0); + if(s < 0 && e != EINTR) break; + } + } + + close(rp->fd); + rp->fd = -1; + if(rp->ofd != -1) + close(rp->ofd); + if(rp->pid != 0 && e != 0) { + kill(-rp->pid, SIGHUP); + rp->pid = 0; + } + + return(0); +} diff --git a/commands/httpd0995/request.c b/commands/httpd0995/request.c new file mode 100644 index 000000000..2b5b36a4f --- /dev/null +++ b/commands/httpd0995/request.c @@ -0,0 +1,369 @@ +/* request.c + * + * This file is part of httpd. + * + * 02/17/1996 Michael Temari + * 07/07/1996 Initial Release Michael Temari + * 12/29/2002 Michael Temari + * + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#ifdef _MINIX +#include +#endif +#include + +#include "http.h" +#include "utility.h" +#include "config.h" + +_PROTOTYPE(static void Timeout, (int sig)); +_PROTOTYPE(static int getline, (char *buffer, int size)); +_PROTOTYPE(static void authorize, (char *p, struct http_request *rq)); +_PROTOTYPE(static void decurl, (char *u)); + +static int TimeOut; + +static void Timeout(sig) +int sig; +{ + TimeOut = 1; +} + +static int getline(buffer, size) +char *buffer; +int size; +{ +char *p; +int s; + + p = buffer; + + while(p < (buffer + size - 1)) { + TimeOut = 0; + signal(SIGALRM, Timeout); + alarm(5*60); + s = read(0, p, 1); + alarm(0); + if(TimeOut) + return(-1); + if(s != 1) + return(-1); + if(*p == '\n') break; + p++; + } + *++p = '\0'; + + p = &buffer[strlen(buffer) - 1]; + if(p >= buffer && (*p == '\r' || *p == '\n')) *p-- ='\0'; + if(p >= buffer && (*p == '\r' || *p == '\n')) *p-- ='\0'; + + return(strlen(buffer)); +} + +static void authorize(p, rq) +char *p; +struct http_request *rq; +{ +char *s; + + if(toupper(*p++) == 'B' && + toupper(*p++) == 'A' && + toupper(*p++) == 'S' && + toupper(*p++) == 'I' && + toupper(*p++) == 'C' && + toupper(*p++) == ' ') ; + else + return; + + s = decode64(p); + + if((p = strchr(s, ':')) == (char *)NULL) + p = ""; + else + *p++ = '\0'; + + strncpy(rq->authuser, s, sizeof(rq->authuser)); + strncpy(rq->authpass, p, sizeof(rq->authpass)); + + return; +} + +int getrequest(rq) +struct http_request *rq; +{ +static char line[4096]; +char *p, *p2, *ps; +int s, len; +struct vhost *ph; + + /* get request, it may be simple */ + + s = getline(line, sizeof(line)); + if(s < 0) + return(-1); + + if(dbglog != (FILE *)NULL) { + fprintf(dbglog, "REQUEST: %s\n", line); + fflush(dbglog); + } + + /* clear http_request */ + memset(rq, 0, sizeof(*rq)); + rq->ifmodsince = (time_t) -1; + + /* assume simple request */ + rq->type = HTTP_REQUEST_TYPE_SIMPLE; + + /* parse the method */ + p = line; + while(*p && !LWS(*p)) { + *p = toupper(*p); + p++; + } + if(*p) *p++ = '\0'; + + if(!strcmp(line, "GET")) + rq->method = HTTP_METHOD_GET; else + if(!strcmp(line, "HEAD")) + rq->method = HTTP_METHOD_HEAD; else + if(!strcmp(line, "POST")) + rq->method = HTTP_METHOD_POST; else + if(!strcmp(line, "PUT")) + rq->method = HTTP_METHOD_PUT; else +#if 0 + if(!strcmp(line, "OPTIONS")) + rq->method = HTTP_METHOD_OPTIONS; else + if(!strcmp(line, "PATCH")) + rq->method = HTTP_METHOD_PATCH; else + if(!strcmp(line, "COPY")) + rq->method = HTTP_METHOD_COPY; else + if(!strcmp(line, "MOVE")) + rq->method = HTTP_METHOD_MOVE; else + if(!strcmp(line, "DELETE")) + rq->method = HTTP_METHOD_DELETE; else + if(!strcmp(line, "LINK")) + rq->method = HTTP_METHOD_LINK; else + if(!strcmp(line, "UNLINK")) + rq->method = HTTP_METHOD_UNLINK; else + if(!strcmp(line, "TRACE")) + rq->method = HTTP_METHOD_TRACE; else + if(!strcmp(line, "WRAPPED")) + rq->method = HTTP_METHOD_WRAPPED; else +#endif + rq->method = HTTP_METHOD_UNKNOWN; + + /* parse the requested URI */ + p2 = rq->uri; + len = sizeof(rq->uri) - 1; + while(*p && !LWS(*p) && len > 0) { + *p2++ = *p++; + len--; + } + *p2 = '\0'; + + /* eat up any leftovers if uri was too big */ + while(*p && !LWS(*p)) + p++; + + /* save for continued processing later */ + ps = p; + + /* parse the requested URL */ + p = rq->uri; + p2 = rq->url; + len = sizeof(rq->url) - 1; + while(*p && !LWS(*p) && *p != '?' && len > 0) { + *p2++ = *p++; + len--; + } + *p2 = '\0'; + + /* See if there is a query string */ + if(*p == '?') { + p++; + p2 = rq->query; + len = sizeof(rq->query) - 1; + while(*p && !LWS(*p) && len > 0) { + *p2++ = *p++; + len--; + } + } + + /* eat up any leftovers */ + while(*p && !LWS(*p)) p++; + + if(rq->url[0] == '\0') { + rq->url[0] = '/'; + rq->url[1] = '\0'; + } + + /* url is a decoded copy of the uri */ + decurl(rq->url); + + /* restore and continue processing */ + p = ps; + + /* if this is true it is a simple request */ + if(*p == '\0') + return(0); + + /* parse HTTP version */ + while(*p && LWS(*p)) p++; + if(toupper(*p++) != 'H') return(0); + if(toupper(*p++) != 'T') return(0); + if(toupper(*p++) != 'T') return(0); + if(toupper(*p++) != 'P') return(0); + if( *p++ != '/') return(0); + + /* version major */ + rq->vmajor = 0; + while((*p >= '0') && (*p <= '9')) + rq->vmajor = rq->vmajor * 10 + (*p++ - '0'); + if(*p != '.') + return(0); + p++; + + /* version minor */ + rq->vminor = 0; + while((*p >= '0') && (*p <= '9')) + rq->vminor = rq->vminor * 10 + (*p++ - '0'); + if(*p) + return(0); + + rq->type = HTTP_REQUEST_TYPE_FULL; + + p = rq->uri; + + /* check if it is a proxy request */ + if(toupper(*p++) == 'H' && + toupper(*p++) == 'T' && + toupper(*p++) == 'T' && + toupper(*p++) == 'P' && + toupper(*p++) == ':') + rq->type = HTTP_REQUEST_TYPE_PROXY; + + /* parse any header fields */ + while((s = getline(line, sizeof(line))) > 0) { + if(toupper(line[0]) == 'A' && + toupper(line[1]) == 'U') + if(dbglog != (FILE *)NULL) { + fprintf(dbglog, "REQUEST: Authorization:\n"); + fflush(dbglog); + } else ; + else + if(dbglog != (FILE *)NULL) { + fprintf(dbglog, "REQUEST: %s\n", line); + fflush(dbglog); + } + p = line; + while(*p && *p != ':') { + *p = toupper(*p); + p++; + } + if(*p != ':') continue; /* bad header field, skip it */ + *p++ = '\0'; + while(*p && LWS(*p)) p++; + + /* header field value parsing here */ + if(!strcmp(line, "HOST")) { + strncpy(rq->host, p, sizeof(rq->host)); + p2 = strrchr(rq->host, ':'); + if(p2 != (char *)NULL) { + *p2++ = '\0'; + rq->port = atoi(p2); + } + /* if unknown virtual host then exit quietly */ + for(ph = vhost; ph != NULL; ph = ph->next) { + if(!strcasecmp(ph->hname, "*")) break; + if(!strcasecmp(ph->hname, rq->host)) break; + } + if(rq->type != HTTP_REQUEST_TYPE_PROXY) + if(ph == NULL && vhost != NULL) return(1); + } else + if(!strcmp(line, "USER-AGENT")) + strncpy(rq->useragent, p, sizeof(rq->useragent)); else + if(!strcmp(line, "CONNECTION")) + rq->keepopen = strcasecmp(p, "Keep-Alive") ? 0 : 1; else + if(!strcmp(line, "IF-MODIFIED-SINCE")) + rq->ifmodsince = httptime(p); else + if(!strcmp(line, "CONTENT-LENGTH")) + rq->size = atol(p); else + if(!strcmp(line, "AUTHORIZATION")) { + strncpy(rq->wwwauth, p, sizeof(rq->wwwauth)); + if(rq->type != HTTP_REQUEST_TYPE_PROXY) + authorize(p, rq); + } else + if(!strcmp(line, "PROXY-AUTHORIZATION")) { + if(rq->type == HTTP_REQUEST_TYPE_PROXY) + authorize(p, rq); + } else + if(!strcmp(line, "DATE")) + rq->msgdate = httptime(p); else + if(!strcmp(line, "COOKIE")) { + strncpy(rq->cookie, p, sizeof(rq->cookie)-1); + rq->cookie[sizeof(rq->cookie)-1] = '\0'; + } + } + + if(rq->type != HTTP_REQUEST_TYPE_PROXY) + if(*rq->host == '\0' && vhost != NULL) return(1); + + if(dbglog != (FILE *)NULL && rq->authuser[0] != '\0') { + fprintf(dbglog, "REQUEST: AuthUser=%s\n", rq->authuser); + fflush(dbglog); + } + + if(s < 0) { + fprintf(stderr, "httpd: getrequest: Error getline (header fields)\n"); + return(-1); + } + + return(0); +} + +static void decurl(u) +char *u; +{ +char *p; +char h1, h2; +char c; + + p = u; + while(*p) { + switch(*p) { + case '\0': + c = '\0'; + break; + case '+': + c = ' '; + p++; + break; + case '%': + h1 = '0'; + h2 = '0'; + p++; + h1 = tolower(*p); + if(*p) p++; + h2 = tolower(*p); + if(*p) p++; + c = (h1 > '9') ? (10 + h1 - 'a') : (h1 - '0'); + c = 16 * c + ((h2 > '9') ? (10 + h2 - 'a') : (h2 - '0')); + break; + default: + c = *p++; + } + *u++ = c; + } + *u = '\0'; +} diff --git a/commands/httpd0995/utility.c b/commands/httpd0995/utility.c new file mode 100644 index 000000000..543a9d053 --- /dev/null +++ b/commands/httpd0995/utility.c @@ -0,0 +1,265 @@ +/* utility.c + * + * This file is part of httpd + * + * 02/17/1996 Michael Temari + * 07/07/1996 Initial Release Michael Temari + * 12/29/2002 Initial Release Michael Temari + * + */ +#include +#include +#include +#include +#include +#include + +#include "utility.h" +#include "config.h" + +const char *days[] = { "Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat" }; +const char *months[] = { "Jan", "Feb", "Mar", "Apr", "May", "Jun", + "Jul", "Aug", "Sep", "Oct", "Nov", "Dec" }; + +char *logdate(t) +time_t *t; +{ +time_t worktime; +struct tm *tm; +static char datebuffer[80]; + + if(t == (time_t *)NULL) + (void) time(&worktime); + else + worktime = *t; + + tm = localtime(&worktime); + + sprintf(datebuffer, "%4d%02d%02d%02d%02d%02d", + 1900+tm->tm_year, + tm->tm_mon + 1, + tm->tm_mday, + tm->tm_hour, tm->tm_min, tm->tm_sec); + + return(datebuffer); +} + +char *httpdate(t) +time_t *t; +{ +time_t worktime; +struct tm *tm; +static char datebuffer[80]; + + if(t == (time_t *)NULL) + (void) time(&worktime); + else + worktime = *t; + + tm = gmtime(&worktime); + + sprintf(datebuffer, "%s, %02d %s %4d %02d:%02d:%02d GMT", + days[tm->tm_wday], + tm->tm_mday, months[tm->tm_mon], 1900+tm->tm_year, + tm->tm_hour, tm->tm_min, tm->tm_sec); + + return(datebuffer); +} + +time_t httptime(p) +char *p; +{ +time_t worktime, gtime, ltime; +struct tm tm; +struct tm *tm2; +int i; + + worktime = (time_t) -1; + + tm.tm_yday = 0; + tm.tm_isdst = -1; + + /* day of week */ + for(i = 0; i < 7; i++) + if(!strncmp(p, days[i], 3)) break; + if(i < 7) + tm.tm_wday = i; + else + return(worktime); + while(*p && *p != ' ') p++; + if(!*p) return(worktime); + while(*p && *p == ' ') p++; + if(!*p) return(worktime); + + if(*p >= '0' && *p <= '9') { + /* day */ + if(*(p+1) >= '0' && *(p+1) <= '9') + tm.tm_mday = 10 * (*p - '0') + (*(p+1) - '0'); + else + return(worktime); + p += 3; + /* month */ + for(i = 0; i < 12; i++) + if(!strncmp(p, months[i], 3)) break; + if(i < 12) + tm.tm_mon = i; + else + return(worktime); + p += 3; + if(!*p++) return(worktime); + /* year */ + tm.tm_year = atoi(p); + while(*p && *p != ' ') p++; + if(*p) p++; + } else { + /* day */ + tm.tm_mday = atoi(p); + while(*p && *p != ' ') p++; + while(*p && *p == ' ') p++; + if(!*p) return(worktime); + } + + /* hour */ + if(*p < '0' || *p > '9' || *(p+1) < '0' || *(p+1) > '9' || *(p+2) != ':') return(worktime); + tm.tm_hour = 10 * (*p - '0') + (*(p+1) - '0'); + p += 3; + + /* minute */ + if(*p < '0' || *p > '9' || *(p+1) < '0' || *(p+1) > '9' || *(p+2) != ':') return(worktime); + tm.tm_min = 10 * (*p - '0') + (*(p+1) - '0'); + p += 3; + + /* second */ + if(*p < '0' || *p > '9' || *(p+1) < '0' || *(p+1) > '9' || *(p+2) != ' ') return(worktime); + tm.tm_sec = 10 * (*p - '0') + (*(p+1) - '0'); + p += 3; + while(*p && *p == ' ') p++; + if(!*p) return(worktime); + + if(*p >= '0' && *p <= '9') + tm.tm_year = atoi(p); + else + if(*p++ != 'G' || *p++ != 'M' || *p++ != 'T') + return(worktime); + + if(tm.tm_year == 0) + return(worktime); + + if(tm.tm_year > 1900) + tm.tm_year -= 1900; + + worktime = mktime(&tm); + + gtime = mktime(gmtime(&worktime)); + tm2 = localtime(&worktime); + tm2->tm_isdst = 0; + ltime = mktime(tm2); + + worktime = worktime - (gtime - ltime); + + return(worktime); +} + +char *mimetype(url) +char *url; +{ +char *p; +struct msufx *ps; +char *dmt; + + dmt = (char *) NULL; + p = url; + while(*p) { + if(*p != '.') { + p++; + continue; + } + for(ps = msufx; ps != NULL; ps = ps->snext) + if(!strcmp(ps->suffix, "") && dmt == (char *) NULL) + dmt = ps->mtype->mimetype; + else + if(!strcmp(p, ps->suffix)) + return(ps->mtype->mimetype); + p++; + } + + if(dmt == (char *) NULL) + dmt = "application/octet-stream"; + + return(dmt); +} + +char *decode64(p) +char *p; +{ +static char decode[80]; +char c[4]; +int i; +int d; + + i = 0; + d = 0; + + while(*p) { + if(*p >= 'A' && *p <= 'Z') c[i++] = *p++ - 'A'; else + if(*p >= 'a' && *p <= 'z') c[i++] = *p++ - 'a' + 26; else + if(*p >= '0' && *p <= '9') c[i++] = *p++ - '0' + 52; else + if(*p == '+') c[i++] = *p++ - '+' + 62; else + if(*p == '/') c[i++] = *p++ - '/' + 63; else + if(*p == '=') c[i++] = *p++ - '='; else + return(""); + if(i < 4) continue; + decode[d++] = ((c[0] << 2) | (c[1] >> 4)); + decode[d++] = ((c[1] << 4) | (c[2] >> 2)); + decode[d++] = ((c[2] << 6) | c[3]); + decode[d] = '\0'; + i = 0; + } + + return(decode); +} + +int getparms(p, parms, maxparms) +char *p; +char *parms[]; +int maxparms; +{ +int np; + + np = 0; + + if(LWS(*p)) { + while(*p && LWS(*p)) p++; + if(!*p) return(0); + parms[np++] = (char *)NULL; + } else + np = 0; + + while(np < maxparms && *p) { + parms[np++] = p; + while(*p && !LWS(*p)) p++; + if(*p) *p++ = '\0'; + while(*p && LWS(*p)) p++; + } + + return(np); +} + +int mkurlaccess(p) +char *p; +{ +int ua; + + ua = 0; + + while(*p) { + if(toupper(*p) == 'R') ua |= URLA_READ; else + if(toupper(*p) == 'W') ua |= URLA_WRITE; else + if(toupper(*p) == 'X') ua |= URLA_EXEC; else + if(toupper(*p) == 'H') ua |= URLA_HEADERS; else + return(0); + p++; + } + + return(ua); +} diff --git a/commands/httpd0995/utility.h b/commands/httpd0995/utility.h new file mode 100644 index 000000000..a66160ead --- /dev/null +++ b/commands/httpd0995/utility.h @@ -0,0 +1,19 @@ +/* utility.h + * + * This file is part of httpd. + * + * 02/17/1996 Michael Temari + * 07/07/1996 Initial Release Michael Temari + * 12/29/2002 Michael Temari + * + */ + +#define LWS(c) ((c == ' ') || (c == '\t') || (c == '\r') || (c == '\n')) + +_PROTOTYPE(char *logdate, (time_t *t)); +_PROTOTYPE(char *httpdate, (time_t *t)); +_PROTOTYPE(time_t httptime, (char *p)); +_PROTOTYPE(char *mimetype, (char *url)); +_PROTOTYPE(char *decode64, (char *p)); +_PROTOTYPE(int getparms, (char *p, char *parms[], int maxparms)); +_PROTOTYPE(int mkurlaccess, (char *p)); -- 2.44.0