NCSA HTTPd 0.5

This commit is contained in:
I am not me 2013-03-13 02:06:49 -04:00
commit 951a80ef8d
25 changed files with 4395 additions and 0 deletions

134
INSTALL Normal file
View File

@ -0,0 +1,134 @@
INSTALLATION
------------
Unless you're installing this server standalone, you'll need to be able
to edit /etc/services and /etc/inetd.conf.
If you're on a Sun Sparc, a DECstation 3000/5000 or DECstation
AXP, an IBM RS/6000, or an SGI, you can download a binary of the
server from ftp.ncsa.uiuc.edu, /Mosaic/ncsa_httpd. The binaries have
been compiled with the following options:
All error files are in /usr/local/etc/httpd
The config file is /usr/local/etc/httpd/httpd.conf
The default port number is 80.
The maximum number of aliases is 20.
The name of a directory's index file is index.html.
The server does not truncate lines.
Errors are not logged to syslog.
HTTP accesses are logged to /usr/local/etc/httpd/access_log
The annotation server support is turned off.
The executables named httpd do not have gopher support, while
those named httpd.gopher do have it.
If you want to compile the source yourself, do that first. The
httpd.h file should have fairly straightforward instructions for what
you need to configure. The Makefile has various options for different
platforms, once again it should be fairly straightforward.
If you have any problems, or things you would like to see changed so
as to make them more understandable at this stage, please e-mail
httpd@ncsa.uiuc.edu with suggestions.
Once you have a binary
......................
1. Decide where you want your server to live. A good place is
/usr/local/etc/httpd, because that's where the error and config files
are expected to be. We recommend placing your server under the name
/usr/local/etc/httpd/httpd, and that way all of your httpd stuff is in
one place.
1.5. Decide if you want to run the server standalone, or from inetd. If
you are not sure, running standalone is generally faster. If you are choosing
an inetd based server, skip forward to step 2.
To use the server standalone, simply execute it with the -s and -p options. The
-p followed by a number tells the server what port to listen to. In order to
listen to ports with a number < 1024, you'll need to be root to run the server
initially.
Example:
httpd -s -p 8081
To start a standalone server listening to port 8081.
Skip to step 4.
2. Edit /etc/services, and place a line in there which tells inetd
which port you want your http server to listen to. Make sure the
define for ANN_PORT in httpd.h and this line agree. Example:
http 80/tcp
3. Edit /etc/inetd.conf, and place a line in there which tells inetd
to fire off a server each time someone connects to that port. Example:
http stream tcp nowait nobody /usr/local/etc/httpd/httpd httpd
assuming you decided to put the server binary in /usr/local/etc/httpd.
It is an excellent idea to run httpd under user ID 'nobody' (as shown
above), as there's not much damage 'nobody' can do.
4. If you are running with access logging enabled, make sure
/usr/local/etc/httpd/access_log (or the appropriate path/file) is
writeable by the user ID being used to run httpd (usually, nobody).
If this file is not writeable by that user ID, httpd will not work.
5. Place the error message files where the server is expecting them.
If you used a precompiled binary, create the directory
/usr/local/etc/httpd/errors, and copy all of the html files from this
distribution in there. If you didn't use the precompiled one, create
whatever directory you decided to put them under. Be sure they are
readable. You can feel free to edit the messages to specify problem
report addresses or whatnot. If you are compiling the server from the
source, you can put these wherever you like.
6. Create a httpd.conf in /usr/local/etc/httpd/httpd.conf for your
server. If you are compiling from the source, the #define CONFIG_FILE
tells where the server will look for this file by default. The server
can also be started with the -f flag and a filename to override this
default.
The syntax for the httpd.conf file is very simple. Every line which
begins with a # sign is a comment. Besides that, lines are of the
format:
alias:realdir
Where alias is the name which should be seen by the clients, and
realdir is the real server directory which should be mapped to that
location. Examples:
/foo:/bletch
Makes it so that all client requests beginning with /foo are mapped to
the real directory /bletch.
/:/port4/web-docs
This has the effect of mapping all client requests into the directory
/port4/web-docs, which makes for easy centralization.
Aliases are cumulative; once you map something, all of your subsequent
aliases will have the previous ones expanded in the alias name before
they themselves are interpreted. For example:
/:/port4/web-docs
/foo:/port4/web-docs/bletch
This would re-map all access to /port4/web-docs/foo to
/port4/web-docs/bletch.
To deny access to a directory, map it to something non-existent like /deny.
httpd@ncsa.uiuc.edu

39
Makefile Normal file
View File

@ -0,0 +1,39 @@
# Makefile for NCSA's httpd.
# For normal machines with ANSI compilers
CC= cc
# For Suns or other non-ANSI platforms
#CC= gcc
# For optimization
#CFLAGS= -O2
# For debugging information
#CFLAGS= -g
# If your system does not have strdup(), then do this
# CFLAGS = -O2 -DNEED_STRDUP
# Place here any flags you may need upon linking, such as a flag to
# prevent dynamic linking (if desired)
LFLAGS=
# Place here any extra libraries you may need to link to. You
# shouldn't have to.
EXTRA_LIBS=
# You shouldn't have to edit anything else.
OBJS=http_config.o httpd.o http_request.o util.o http_dir.o http_gopher.o \
ann_request.o ann_set.o
.c.o:
$(CC) -c $(CFLAGS) $(DEFINES) $<
all: httpd
httpd: $(OBJS)
$(CC) $(LFLAGS) -o httpd $(OBJS) $(EXTRA_LIBS)
$(OBJS): Makefile httpd.h
clean:
rm -f httpd $(OBJS)

111
README Normal file
View File

@ -0,0 +1,111 @@
NCSA HTTP server
================
Beta Release 0.5
Final release for Gopher and Group Annotation features. In future releases,
Gopher support will not reappear, and Group Annotation features will reappear
in a more protocol-supported fashion.
This code is in the public domain. Specifically, we give to the public
domain all rights for future licensing of the source code, all resale
rights, and all publishing rights.
We ask, but do not require, that the following message be included in
all derived works:
Portions developed at the National Center for Supercomputing
Applications at the University of Illinois at Urbana-Champaign.
THE UNIVERSITY OF ILLINOIS GIVES NO WARRANTY, EXPRESSED OR IMPLIED,
FOR THE SOFTWARE AND/OR DOCUMENTATION PROVIDED, INCLUDING, WITHOUT
LIMITATION, WARRANTY OF MERCHANTABILITY AND WARRANTY OF FITNESS FOR A
PARTICULAR PURPOSE.
Now that that's out of the way, we can move on to more interesting
things.
OVERVIEW
--------
You've found the Beta release of NCSA's HTTP server. This code is
in testing and development, but is currently fully functional as a
simple HTTP server.
NEW IN RELEASE 0.5
------------------
o Slight bug in gopher support fixed
o Standalone support added
NEW IN RELEASE 0.4
------------------
o Added annotation server support. See README.GROUP-ANNOTATIONS.
o Added access logging capability.
o Added HTTP/1.0 compatibility (3rd+ args of GET command ignored).
o Changed handling of full hostnames.
o Fixed bug with index.html not being recognized unless a trailing
slash was appended to the directory name.
o Fixed bug with extraneous null appended to end of file data
stream.
o Fixed bug handling directory URL's with trailing slashes.
o Made a little more Ultrix-friendly.
NEW IN RELEASE 0.3
------------------
o Security hole (should be the last) fixed
o Nearly complete drop-in gopher support. See README.GOPHER for details
o Not being able to find the config file does not send back a path
NEW IN RELEASE 0.2
------------------
o Two security holes fixed
o Double slashes in root index fixed
o Not being able to find the error files no longer hangs the server
FEATURES
--------
o Directory aliasing and denial. The server can be configured to
map different directories to different places in the filesystem, and
easily deny access to a particular directory or file by mapping it to
something which does not exist. The interface is designed to be simple.
o Automatic directory index files and on-the-fly index generation.
If the server finds that the client is requesting a directory, it
first checks for a file, by default called "index.html" but which can
be compiled to any name, and if it does not find such a file it
generates an HTML index of the directory and sends it back to the
client.
o Drop-in gopher support. This server is able to automatically
interpret gopher directory structures and construct, on the fly, HTML
indexes of them. Should be faster than gopherd in HTML mode.
o Group annotation server capabilities. See
README.GROUP-ANNOTATIONS for more details.
o Access logging. See httpd.h for more details.
o Designed to be small and low-impact: the entire source is
3,000 lines, with full gopher, annotation server, and logging support.
FUTURE PLANS
------------
o HTTP2 (HTTP/1.0) support.
o On the fly uncompression.
That should be all you need. E-mail problems, suggestions for features
or documentation, cash donations, food, etc. to httpd@ncsa.uiuc.edu.
We encourage you to try and break the server, or compromise its
security, and let us know how you did it so we can fix it.
Rob, Eric, Marc
httpd@ncsa.uiuc.edu

66
README.GOPHER Normal file
View File

@ -0,0 +1,66 @@
This code is in the public domain. Specifically, we give to the public
domain all rights for future licensing of the source code, all resale
rights, and all publishing rights.
We ask, but do not require, that the following message be included in
all derived works:
Portions developed at the National Center for Supercomputing
Applications at the University of Illinois at Urbana-Champaign.
THE UNIVERSITY OF ILLINOIS GIVES NO WARRANTY, EXPRESSED OR IMPLIED,
FOR THE SOFTWARE AND/OR DOCUMENTATION PROVIDED, INCLUDING, WITHOUT
LIMITATION, WARRANTY OF MERCHANTABILITY AND WARRANTY OF FITNESS FOR A
PARTICULAR PURPOSE.
OVERVIEW OF GOPHER SUPPORT
--------------------------
As of beta release 0.3, this server will automatically generate
HTML listings of a gopher directory without going through gopherd.
This means that . files which are links are processed, and .cap files
are processed. Any request which begins with /gopher-data will be
considered a gopher request if gopher is turned on. Therefore, mapping
/gopher-data to the location of your server's data will allow you to
access /gopher-data and get a gopher listing in return.
These features are new to 0.3, and I would appreciate any and all
feedback on its usefulness, features and/or lack thereof, problems,
and suggestions.
NOTE THAT THE GOPHER PROTOCOL IS NOT SUPPORTED in this server;
only httpd is supported; thus, Gopher clients will not be able to talk
to this server. The point of this feature is to allow existing Gopher
data hierarchies to be painlessly transferred into a WWW environment.
KNOWN PROBLEMS
--------------
o Shell scripts are not executed; i.e. a file of type 0 with #! at
the beginning is returned plaintext instead of being executed.
o There is a slight ordering inconsistency between this server and
the real gopherd, i.e. all numbered are placed first, in order, THEN
the unnumbered files are placed in alphabetical order. The gopher
server fills in the holes with alphabetized entries.
o Not all gopher features are currently supported, namely the ph
interface.
o Files of type 's', 'G', 'I', and any files which are special and
need to be treated in a special way are sent back to the client.
Therefore, if the file does not have the proper extension the client
may not recognize it. i.e. if a GIF does not have the .gif extension
NCSA Mosaic probably won't recognize it.
INSTALLATION
------------
See the file INSTALL for normal and gopher installations. Be sure
you define GROK_GOPHER in httpd.h if you are compiling the server, or
that you use the binary with gopher support if you are using a
precompiled one.
httpd@ncsa.uiuc.edu

49
README.GROUP-ANNOTATIONS Normal file
View File

@ -0,0 +1,49 @@
This code is in the public domain. Specifically, we give to the public
domain all rights for future licensing of the source code, all resale
rights, and all publishing rights.
We ask, but do not require, that the following message be included in
all derived works:
Portions developed at the National Center for Supercomputing
Applications at the University of Illinois at Urbana-Champaign.
THE UNIVERSITY OF ILLINOIS GIVES NO WARRANTY, EXPRESSED OR IMPLIED,
FOR THE SOFTWARE AND/OR DOCUMENTATION PROVIDED, INCLUDING, WITHOUT
LIMITATION, WARRANTY OF MERCHANTABILITY AND WARRANTY OF FITNESS FOR A
PARTICULAR PURPOSE.
OVERVIEW OF GROUP ANNOTATION SERVER SUPPORT
-------------------------------------------
As of beta release 0.4, this server can optionally be compiled to
serve as a group annotation server as well as a normal HTTP server.
Users of NCSA Mosaic for X version 1.1 and later can set their
annotationServer resource to the string "machine.name:port" (e.g.,
"www.foo.com:80") to take advantage of a given group annotation
server. Other WWW browsers may support use of group annotation
servers in the future.
If you wish to set up httpd to act as a group annotation server,
#define ANNOTATIONS in httpd.h and set ANN_DIR, ANN_PORT, and
ANN_VIRTUAL_DIR to appropriate values. Make sure that the directory
named by ANN_DIR (and virtually named by ANN_VIRTUAL_DIR) is writeable
by the user ID you're running httpd under (normally 'nobody'), and
make sure enough disk space is free in the partition holding that
directory to handle anticipated group annotation activity.
THE GROUP ANNOTATION SUPPORT OR PROTOCOL MAY CHANGE AT ANY TIME. IT
IS NOT AN OFFICIAL OR SUPPORTED PROTOCOL. Among other things, we have
yet to address matters of scale, server-based logging, tracking, and
notification activities, garbage collection/expiration, and
authentication. The current server is merely an experimental proof of
concept, and it works fine given its design restraints.
We would greatly appreciate being notified of any serious use of the
group annotation capability in research, educational, academic, or
commercial environments, as well as general comments and suggestions.
httpd@ncsa.uiuc.edu

729
ann_request.c Normal file
View File

@ -0,0 +1,729 @@
/*
* ann_request.c: functions to get and process annotation requests
*
* Eric Bina
*
*/
#include "httpd.h"
#include <time.h>
#ifdef ANNOTATIONS
/*
* All the recognized arguments that you can pass in an annotation
* request.
*/
static char *ArgUrl, *ArgGroup, *ArgUser, *ArgTitle, *ArgDate, *ArgAudio;
static int ArgLength;
#define HASH_TABLE_SIZE 1037
/*
* Hash a url to a number used to name a log file.
*/
static int HashUrl (char *url)
{
int len, i, val;
if (!url)
return 0;
len = strlen (url);
if (!len)
return 0;
val = 0;
for (i = 0; i < 10; i++)
val += url[(i * val + 7) % len];
return val % HASH_TABLE_SIZE;
}
#define BUFF_SIZE 8192
/*
* Function to read from the passed FILE * until the characer
* cstop is reached, EOF, or LINEFEED. Return the length of
* the value read (not counting the trailing '\0') in sz.
* If escapes is 1, backslash escapes the character immediately after it.
*/
char *readto(FILE *in, char cstop, int *sz, int escapes)
{
char *buf;
int i, size;
*sz = 0;
buf = (char *)malloc(BUFF_SIZE * sizeof(char));
if (buf == NULL)
{
return(NULL);
}
for (i=0; i<BUFF_SIZE; i++)
{
int val;
val = fgetc(in);
if ((escapes)&&(val == '\\'))
{
val = fgetc(in);
}
else if (val == cstop)
{
buf[i] = '\0';
break;
}
else if (val == LINEFEED)
{
return(NULL);
}
buf[i] = (char)val;
}
size = i;
while (i == BUFF_SIZE)
{
char *tptr;
tptr = buf;
buf = (char *)malloc((size + BUFF_SIZE) * sizeof(char));
if (buf == NULL)
{
return(NULL);
}
bcopy(tptr, buf, size);
free(tptr);
tptr = (char *)(buf + size);
for (i=0; i<BUFF_SIZE; i++)
{
int val;
val = fgetc(in);
if ((escapes)&&(val == '\\'))
{
val = fgetc(in);
}
else if (val == cstop)
{
tptr[i] = '\0';
break;
}
else if (val == LINEFEED)
{
return(NULL);
}
tptr[i] = (char)val;
}
size += i;
}
*sz = size;
return(buf);
}
/*
* Read the next value from the annotation request input stream.
* Values are everything from the '=' to the ';', unless the first
* character is a '"', in which case it is everything up to the
* closing quote.
*/
char *read_val(FILE *in)
{
int val;
int vsize;
char *argval;
char *tptr;
argval = NULL;
val = fgetc(in);
if (val == '\"')
{
int junk;
argval = readto(in, '\"', &vsize, 1);
if (argval == NULL)
{
return(NULL);
}
tptr = readto(in, ';', &junk, 0);
free(tptr);
}
else
{
char *tptr;
tptr = readto(in, ';', &vsize, 0);
if (tptr == NULL)
{
return(NULL);
}
argval = (char *)malloc((vsize + 2) * sizeof(char));
if (argval == NULL)
{
return(NULL);
}
argval[0] = (char)val;
strcpy(&argval[1], tptr);
free(tptr);
vsize++;
}
return(argval);
}
/*
* Process the annotation request input stream, and set the values of
* all the known accepted parameters.
*/
void ReadArgs(FILE *in, FILE *out)
{
int ret, size;
char *arg;
time_t time_val;
char *time_string;
ArgUrl = NULL;
ArgGroup = NULL;
ArgUser = NULL;
ArgTitle = NULL;
ArgDate = NULL;
ArgAudio = NULL;
ArgLength = -1;
arg = readto(in, '=', &size, 0);
if (arg == NULL)
{
client_error(out, BAD_ANN_REQUEST);
}
while (arg[0] != '\0')
{
if (strncmp(arg, "url", 3) == 0)
{
ArgUrl = read_val(in);
if (ArgUrl == NULL)
{
client_error(out, BAD_ANN_REQUEST);
}
}
else if (strncmp(arg, "group", 5) == 0)
{
ArgGroup = read_val(in);
if (ArgGroup == NULL)
{
client_error(out, BAD_ANN_REQUEST);
}
}
else if (strncmp(arg, "user", 4) == 0)
{
ArgUser = read_val(in);
if (ArgUser == NULL)
{
client_error(out, BAD_ANN_REQUEST);
}
}
else if (strncmp(arg, "title", 5) == 0)
{
ArgTitle = read_val(in);
if (ArgTitle == NULL)
{
client_error(out, BAD_ANN_REQUEST);
}
}
else if (strncmp(arg, "date", 4) == 0)
{
ArgDate = read_val(in);
if (ArgDate == NULL)
{
client_error(out, BAD_ANN_REQUEST);
}
}
else if (strncmp(arg, "audio", 5) == 0)
{
ArgAudio = read_val(in);
if (ArgAudio == NULL)
{
client_error(out, BAD_ANN_REQUEST);
}
}
else if (strncmp(arg, "length", 6) == 0)
{
char *tptr;
tptr = read_val(in);
if (tptr == NULL)
{
client_error(out, BAD_ANN_REQUEST);
}
ArgLength = atoi(tptr);
free(tptr);
}
else
{
char *unknown;
unknown = read_val(in);
if (unknown == NULL)
{
client_error(out, BAD_ANN_REQUEST);
}
free(unknown);
}
free(arg);
arg = readto(in, '=', &size, 0);
if (arg == NULL)
{
client_error(out, BAD_ANN_REQUEST);
}
}
/*
* We allow the client to pass us the date, but we ignore
* it and calculate our own.
*/
time_val = time(NULL);
time_string = ctime(&time_val);
time_string[strlen(time_string) - 1] = '\0';
ArgDate = strdup(time_string);
if (ArgDate == NULL)
{
client_error(out, BAD_ANN_REQUEST);
}
}
/*
* We know we have received a SET_ANN request. Process it.
*/
void Set_request(FILE *in, FILE *out)
{
int indx;
int ret, hash;
LogData *lptr;
char *data;
/*
* Read the args, and make sure we have a URL to
* set the annotation on.
*/
ReadArgs(in, out);
if (ArgUrl == NULL)
{
client_error(out, BAD_ANN_REQUEST);
}
/*
fprintf(out, "<PRE>\n");
fprintf(out, "Got a Set-Url-Annotations Request\n");
fprintf(out, "URL = %s\nGroup = %s\nUser = %s\nDate = %s\n",
ArgUrl, ArgGroup, ArgUser, ArgDate);
fprintf(out, "Length = %d\n", ArgLength);
*/
data = NULL;
/*
* Read the data for this new annotation.
*/
if (ArgLength > 0)
{
data = (char *)malloc(ArgLength * sizeof(char));
if (data == NULL)
{
client_error(out, BAD_ANN_REQUEST);
}
ret = fread(data, sizeof(char), ArgLength, in);
if (ret != ArgLength)
{
client_error(out, BAD_ANN_REQUEST);
}
}
/*
* Lock the logfile prepratory to setting the new annotation.
*/
hash = HashUrl(ArgUrl);
lptr = GetLogData(hash);
if (lptr == NULL)
{
client_error(out, ANN_SERVER_ERROR);
}
indx = MaxIndex(lptr);
/*
* If this is an audio annotation, make the real sound file it
* points to, and then put in a standard text annotation.
*/
if (ArgAudio != NULL)
{
char *url;
/* Looks like ArgAudio is a string corresponding to
the type of audio -- we should use this. @@@ */
url = WriteAudioData(hash, (indx + 1),
data, ArgLength, ArgAudio, 0);
if (url == NULL)
{
UnlockIt();
client_error(out, ANN_SERVER_ERROR);
}
if (data)
{
free((char *)data);
}
data = (char *)malloc(strlen("This is an audio annotation. <P>\n\nTo hear the annotation, go <A HREF=\"%s\">here</A>. <P>\n") +
strlen(url) + 1);
if (data == NULL)
{
UnlockIt();
client_error(out, ANN_SERVER_ERROR);
}
sprintf(data, "This is an audio annotation. <P>\n\nTo hear the annotation, go <A HREF=\"%s\">here</A>. <P>\n", url);
ArgLength = strlen(data) + 1;
free(url);
}
/*
* Write the annotation, modify and write the log.
* Unlock the lock, and free memory.
*/
ret = WriteAnnData(ArgUrl, hash, (indx + 1),
ArgTitle, ArgUser, ArgDate, data, ArgLength, 0);
if (ret < 0)
{
UnlockIt();
client_error(out, ANN_SERVER_ERROR);
}
indx = ret;
ret = AddLogData(lptr, indx, ArgUrl);
if (ret != 1)
{
UnlockIt();
client_error(out, ANN_SERVER_ERROR);
}
ret = WriteLogData(lptr);
if (ret < 0)
{
client_error(out, ANN_SERVER_ERROR);
}
if ((ArgLength > 0)&&(data != NULL))
{
free(data);
}
FreeLogData(lptr);
}
/*
* We know we have received a GET_ANN request. Process it.
*/
void Get_request(FILE *in, FILE *out)
{
int i;
int ret, hash;
LogData *lptr;
int *alist, acnt;
/*
* Read the args, and make sure we have a URL to
* get the annotations on.
*/
ReadArgs(in, out);
if (ArgUrl == NULL)
{
client_error(out, BAD_ANN_REQUEST);
}
/*
fprintf(out, "<PLAINTEXT>\n");
fprintf(out, "Got a Get-Url-Annotations Request\n");
fprintf(out, "URL = %s\nGroup = %s\nUser = %s\nDate = %s\n",
ArgUrl, ArgGroup, ArgUser, ArgDate);
fprintf(out, "Length = %d\n", ArgLength);
*/
/*
* Read and lock the log file.
*/
hash = HashUrl(ArgUrl);
lptr = GetLogData(hash);
if (lptr == NULL)
{
client_error(out, ANN_SERVER_ERROR);
}
ret = FindLogData(lptr, ArgUrl, &alist, &acnt);
/*
* If there are annotation on this URL, we need to read each one,
* and extract the title, user, and date to make the list
* of group annotation to be placed at the end of the
* document.
*/
if ((ret > 0)&&(acnt > 0))
{
char *host;
host = full_hostname();
fprintf(out, "<H2>Group Annotations</H2>\n");
fprintf(out, "<UL>\n");
for (i=0; i<acnt; i++)
{
ret = ReadAnnData(hash, alist[i],
&ArgTitle, &ArgUser, &ArgDate);
if (ret != 1)
{
UnlockIt();
client_error(out, ANN_SERVER_ERROR);
}
fprintf(out, "<LI> <A HREF=\"http://%s:%d%s/%d-%d.html\">%s</A> (%s)\n",
host, ANN_PORT, ANN_VIRTUAL_DIR, hash,
alist[i],
ArgTitle, ArgDate);
}
fprintf(out, "</UL>\n");
}
else
{
}
UnlockIt();
}
/*
* We know we have received a CHANGE_ANN request. Process it.
*/
void Change_request(FILE *in, FILE *out)
{
int i, ret, hash;
LogData *lptr;
char *file, *data, *ptr;
char *url;
/*
* Read the args, and make sure we have a URL to
* change the annotation on.
*/
ReadArgs(in, out);
if (ArgUrl == NULL)
{
client_error(out, BAD_ANN_REQUEST);
}
/*
fprintf(out, "<PRE>\n");
fprintf(out, "Got a Delete-Url-Annotations Request\n");
fprintf(out, "URL = %s\nGroup = %s\nUser = %s\nDate = %s\n",
ArgUrl, ArgGroup, ArgUser, ArgDate);
fprintf(out, "Length = %d\n", ArgLength);
*/
data = NULL;
/*
* Get the data for the new annotation.
*/
if (ArgLength > 0)
{
data = (char *)malloc(ArgLength * sizeof(char));
if (data == NULL)
{
client_error(out, BAD_ANN_REQUEST);
}
ret = fread(data, sizeof(char), ArgLength, in);
if (ret != ArgLength)
{
client_error(out, BAD_ANN_REQUEST);
}
}
/*
* Construct the full path to the annotation to
* be changed.
*/
file = (char *)malloc(MAX_STRING_LEN * sizeof(char));
if (file == NULL)
{
client_error(out, BAD_ANN_REQUEST);
}
ptr = strrchr(ArgUrl, '/');
if (ptr == NULL)
{
sprintf(file, "%s/%s", ANN_DIR, ArgUrl);
if (sscanf(ArgUrl, "%d-%d.html", &hash, &i) !=
2)
{
free(file);
client_error(out, BAD_ANN_REQUEST);
}
}
else
{
sprintf(file, "%s%s", ANN_DIR, ptr);
ptr++;
if (sscanf(ptr, "%d-%d.html", &hash, &i) !=
2)
{
free(file);
client_error(out, BAD_ANN_REQUEST);
}
}
/*
* Read and lock the logfile.
*/
lptr = GetLogData(hash);
if (lptr == NULL)
{
client_error(out, ANN_SERVER_ERROR);
}
url = HashtoUrl(lptr, i);
if (url == NULL)
{
UnlockIt();
client_error(out, ANN_SERVER_ERROR);
}
/*
* Remove the old annotation.
*/
ret = unlink(file);
if (ret != 0)
{
fprintf(out, "Deletion of %s failed.\n", file);
}
else
{
fprintf(out, "Deletion of %s success!\n", file);
}
free(file);
/*
* Write the new annotation.
*/
ret = WriteAnnData(url, hash, i,
ArgTitle, ArgUser, ArgDate, data, ArgLength, 1);
free(url);
if (ret < 0)
{
UnlockIt();
client_error(out, ANN_SERVER_ERROR);
}
UnlockIt();
free(data);
}
/*
* We know we have received a DELETE_ANN request. Process it.
*/
void Delete_request(FILE *in, FILE *out)
{
int i, ret, hash;
char *data, *ptr;
LogData *lptr;
/*
* Read the args, and make sure we have a URL to
* delete
*/
ReadArgs(in, out);
if (ArgUrl == NULL)
{
client_error(out, BAD_ANN_REQUEST);
}
/*
fprintf(out, "<PRE>\n");
fprintf(out, "Got a Delete-Url-Annotations Request\n");
fprintf(out, "URL = %s\nGroup = %s\nUser = %s\nDate = %s\n",
ArgUrl, ArgGroup, ArgUser, ArgDate);
fprintf(out, "Length = %d\n", ArgLength);
*/
/*
* Construct the full path to the annotation to be deleted.
*/
data = (char *)malloc(MAX_STRING_LEN * sizeof(char));
if (data == NULL)
{
client_error(out, BAD_ANN_REQUEST);
}
ptr = strrchr(ArgUrl, '/');
if (ptr == NULL)
{
sprintf(data, "%s/%s", ANN_DIR, ArgUrl);
if (sscanf(ArgUrl, "%d-%d.html", &hash, &i) !=
2)
{
free(data);
client_error(out, BAD_ANN_REQUEST);
}
}
else
{
sprintf(data, "%s%s", ANN_DIR, ptr);
ptr++;
if (sscanf(ptr, "%d-%d.html", &hash, &i) !=
2)
{
free(data);
client_error(out, BAD_ANN_REQUEST);
}
}
/*
* Read an lock the log file
*/
lptr = GetLogData(hash);
if (lptr == NULL)
{
client_error(out, ANN_SERVER_ERROR);
}
/*
* Delete this annotation from the logfile.
*/
ret = DeleteLogData(lptr, i);
if (ret != 1)
{
UnlockIt();
client_error(out, ANN_SERVER_ERROR);
}
/*
* Delete the actual annotation, and any associated
* audio annotations.
*/
ret = unlink(data);
if (ret != 0)
{
fprintf(out, "Deletion of %s failed.\n", data);
}
else
{
char afile[MAX_STRING_LEN];
fprintf(out, "Deletion of %s success!\n", data);
/*
* Remove any associated audio files
*/
sprintf(afile, "%s.au", data);
unlink(afile);
sprintf(afile, "%s.aiff", data);
unlink(afile);
}
free(data);
/*
* Write the new log, unlock it, and free the data.
*/
ret = WriteLogData(lptr);
if (ret < 0)
{
client_error(out, ANN_SERVER_ERROR);
}
FreeLogData(lptr);
}
#endif /* ANNOTATIONS */

1052
ann_set.c Normal file

File diff suppressed because it is too large Load Diff

129
contrib/gopher_fixes Normal file
View File

@ -0,0 +1,129 @@
9a10
> #define ROOT_MENU "Root Menu"
11d11
<
149a150,165
> /* /gopher-data/ hack by Ian C Blenke */
> if(ar[i]->host[0]) {
> char host[MAX_STRING_LEN];
>
> HOSTNAME(host,MAX_STRING_LEN);
> if(strncmp(ar[i]->host, host, strlen(host))==0)
> /* problem: if we fall through here we may get // */
> if(ar[i]->path[0]) {
> fprintf(fd,
> "<LI> <A NAME=%d HREF=\"/gopher-data%s\"",
> lnum++, (ar[i]->path)+1);
> fprintf(fd,">%s</A>%c",ar[i]->name,LINEFEED);
> break;
> } /* fall through if not type 0 or 1 */
> }
>
291a308,359
> /* GN - Patch #1 - for looks - Ian C. Blenke */
> void get_link_name_from_dir(char *dir, char *munge, char *return_name)
> {
> char munged_name[MAX_STRING_LEN];
> int i, len, count;
> char dir_name[MAX_STRING_LEN];
> gopher_ent head;
> gopher_ent *p, *r;
>
> /* strip out the actual parent path name */
> len=strlen(dir);
> while(len>0)
> {
> if(dir[len]=='/') break;
> len--;
> }
> strncpy(dir_name, dir, len);
> dir_name[len]='\0';
> /* strip out the virtual parent path name */
> strcpy(munged_name, munge+GOPHER_LOC_LEN);
> len=strlen(munged_name);
> if((len==0)||(len==1))
> {
> strcpy(return_name, ROOT_MENU);
> return;
> }
> while(len>1)
> {
> if(munged_name[len]=='/') break;
> len--;
> }
> munged_name[len-1]='\0';
> strcpy(munged_name, munged_name+len);
>
> /* parse the parent menu file */
> head.next=NULL;
> count=add_links_from_file(dir_name, "menu", &head);
> p=&head;
>
> /* loop through the chain */
> for(i=0; i<count; i++)
> {
> if(strcmp((p->path)+2, munged_name)==0)
> strcpy(return_name, p->name);
> r=p->next;
> free(p);
> p=r;
> if(p==NULL) break;
> }
> return;
> }
>
293,294c361
< DIR *d;
< struct DIR_TYPE *dstruct;
---
> FILE *fmenu;
298a366,367
> char menu_name[MAX_STRING_LEN];
> char parent_name[MAX_STRING_LEN];
305a375
> make_full_path(name, "menu", menu_name);
307,308c377,378
< if(!(d=opendir(name)))
< client_error(fd,BAD_FILE);
---
> if(!(fmenu=fopen(menu_name, "r")))
> client_error(fd, BAD_FILE);
310,311c380
< while(dstruct=readdir(d)) {
< char *fn;
---
> fclose(fmenu);
313,315c382
< fn=dstruct->d_name;
< if(ignore(fn))
< continue;
---
> num_entries=add_links_from_file(name, "menu", &head);
317,329c384,391
< if(fn[0]=='.') {
< if(!strcmp(fn,".cap"))
< cap_file=1;
< else
< num_entries+=add_links_from_file(name,fn,&head);
< }
< else {
< add_local_link(fn,&head);
< num_entries++;
< }
< }
< fprintf(fd,"<TITLE> Gopher Index of %s</TITLE>%c",unmunged_name,LINEFEED);
< fprintf(fd,"<H1>Gopher Index of %s</H1>%c",unmunged_name,LINEFEED);
---
> get_link_name_from_dir(name, unmunged_name, parent_name);
> fprintf(fd,"<HEADER>%c",LINEFEED);
> fprintf(fd,"<TITLE> %s </TITLE>%c",parent_name,LINEFEED);
> fprintf(fd,"<BASE HREF=\"http://su102w.ess.harris.com\">%c", LINEFEED);
> fprintf(fd,"</HEADER>%c",LINEFEED);
> fprintf(fd,"<BODY>%c", LINEFEED);
> fprintf(fd,"<H1>Harris ESS InfoServer Gopher</H1>%c",LINEFEED);
> fprintf(fd,"<H2> %s </H2>%c", parent_name, LINEFEED);
338c400
< closedir(d);
---
> fprintf(fd,"</BODY>%c", LINEFEED);

340
contrib/listen.c Normal file
View File

@ -0,0 +1,340 @@
/*
* Startup program for netrek. Listens for connections, then forks off
* servers. Based on code written by Brett McCoy, but heavily modified.
*
* To compile, first change the defines for SERVER, LOGFILE, DEF_PORT to what
* you want. Then use:
*
* cc -O listen.c -o listen
*
* 'listen' only accepts one option, '-p' to override the default port to
* listen on. Use it like: 'listen -p 2592'.
*
* Note that descriptor 2 is duped to descriptor 1, so that stdout and
* stderr go to the same file.
*/
#include <sys/types.h>
#include <sys/time.h>
#include <sys/socket.h>
#include <sys/ioctl.h>
#include <sys/stat.h>
#include <sys/wait.h>
#include <netdb.h>
#include <errno.h>
#include <time.h>
#include <netinet/in.h>
#include <fcntl.h>
#include <varargs.h>
#include <stdio.h>
#include <signal.h>
#include <string.h>
#define SERVER "/stg1/rob/netrek/lib/ntserv" /* filename of server */
#define LOGFILE "/dev/null" /* filename of log file */
#define DEF_PORT 2592 /* port to listen on */
int listenSock;
short port = DEF_PORT;
char *program;
char * dateTime();
void detach();
void getConnections();
void getListenSock();
void multClose();
void reaper();
void terminate();
/*
* Error reporting functions ripped from my library.
*/
void syserr();
void warnerr();
void fatlerr();
void err();
char *lasterr();
struct hostent * gethostbyaddr();
char * inet_ntoa();
main(argc, argv)
int argc;
char *argv[];
{
int i;
for (i = 1; i < argc; i++) {
if (argv[i][0] == '-') {
if (strcmp(argv[i]+1, "p") == 0) {
port = atoi(argv[i+1]);
} else {
fatlerr(1, 0, "unrecognized option: %s.", argv[i]);
}
}
}
program = argv[0]; /* let err functions know our name */
detach(); /* detach from terminal, close files, etc. */
getListenSock();
signal(SIGCHLD, reaper);
signal(SIGTERM, terminate);
while (1) {
getConnections();
}
}
/***********************************************************************
* Detach process in various ways.
*/
void
detach() {
int fd, rc, mode;
mode = S_IRUSR | S_IRGRP | S_IROTH | S_IWUSR;
if ((fd = open(LOGFILE, O_WRONLY|O_CREAT|O_APPEND, mode)) == -1)
syserr(1, "detach", "couldn't open log file. [%s]", dateTime());
dup2(fd, 2);
dup2(fd, 1);
multClose(1, 2, -1); /* close all other file descriptors */
warnerr(0, "started at %s on port %d.", dateTime(), port);
/* fork once to escape the shells job control */
if ((rc = fork()) > 0)
exit(0);
else if (rc < 0)
syserr(1, "detach", "couldn't fork. [%s]", dateTime());
/* now detach from the controlling terminal */
if ((fd = open("/dev/tty", O_RDWR, 0)) == -1) {
warnerr("detach", "couldn't open tty, assuming still okay. [%s]",
dateTime());
return;
}
ioctl(fd, TIOCNOTTY, 0);
close(fd);
setsid(); /* make us a new process group/session */
}
/***********************************************************************
*/
void
getListenSock() {
struct sockaddr_in addr;
if ((listenSock = socket(AF_INET, SOCK_STREAM, 0)) < 0)
syserr(1, "getListenSock", "can't create listen socket. [%s]",
dateTime());
addr.sin_family = AF_INET;
addr.sin_addr.s_addr = INADDR_ANY;
addr.sin_port = htons(port);
if (bind(listenSock, (struct sockaddr *) &addr, sizeof(addr)) < 0)
syserr(1, "getListenSock", "can't bind listen socket. [%s]",
dateTime());
if (listen(listenSock, 5) != 0)
syserr(1, "getListenSock", "can't listen to socket. [%s]", dateTime());
}
/***********************************************************************
*/
void
getConnections() {
int len, sock, pid;
struct sockaddr_in addr;
struct hostent *he;
char host[100];
len = sizeof(addr);
while ((sock = accept(listenSock, (struct sockaddr *) &addr, &len)) < 0) {
/* if we got interrupted by a dying child, just try again */
if (errno == EINTR)
continue;
else
syserr(1, "getConnections",
"accept() on listen socket failed. [%s]", dateTime());
}
/* get the host name */
he = gethostbyaddr((char *) &addr.sin_addr.s_addr,
sizeof(addr.sin_addr.s_addr), AF_INET);
if (he != 0)
strcpy(host, he->h_name);
else
strcpy(host, inet_ntoa((u_long) &addr.sin_addr));
warnerr(0, "connection from %s. [%s]", host, dateTime());
/* fork off a server */
if ((pid = fork()) == 0) {
dup2(sock, 0);
multClose(0, 1, 2, -1); /* close everything else */
if (execl(SERVER, SERVER, host, 0) != 0)
syserr(1, "getConnections", "couldn't execl %s as the server. [%s]",
SERVER, dateTime());
} else if (pid < 0)
syserr(1, "getConnections", "can't fork. [%s]", dateTime());
close(sock);
}
/***********************************************************************
* Returns a string containing the date and time. String area is static
* and reused.
*/
char *
dateTime() {
time_t t;
char *s;
time(&t);
s = ctime(&t);
s[24] = '\0'; /* wipe-out the newline */
return s;
}
/***********************************************************************
* Handler for SIGTERM. Closes and shutdowns everything.
*/
void
terminate() {
int s;
fatlerr(1, "terminate", "killed. [%s]", dateTime());
/* shutdown and close everything */
for (s = getdtablesize(); s >= 0; s--) {
shutdown(s, 2);
close(s);
}
}
/***********************************************************************
* Waits on zombie children.
*/
void
reaper() {
while (wait3(0, WNOHANG, 0) > 0);
}
/***********************************************************************
* Close all file descriptors except the ones specified in the argument list.
* The list of file descriptors is terminated with -1 as the last arg.
*/
void
multClose(va_alist)
va_dcl
{
va_list args;
int fds[100], nfds, fd, ts, i, j;
/* get all descriptors to be saved into the array fds */
va_start(args);
for (nfds = 0; nfds < 99; nfds++) {
if ((fd = va_arg(args, int)) == -1)
break;
else
fds[nfds] = fd;
}
ts = getdtablesize();
/* close all descriptors, but first check the fds array to see if this
* one is an exception */
for (i = 0; i < ts; i++) {
for (j = 0; j < nfds; j++)
if (i == fds[j]) break;
if (j == nfds) close(i);
}
}
/***********************************************************************
* Error reporting functions taken from my library.
*/
extern int sys_nerr;
extern char *sys_errlist[];
extern int errno;
void
syserr(va_alist)
va_dcl
{
va_list args;
int rc;
va_start(args);
rc = va_arg(args, int);
err(args);
if (errno < sys_nerr)
fprintf(stderr, " system message: %s\n", sys_errlist[errno]);
exit(rc);
}
void
warnerr(va_alist)
va_dcl
{
va_list args;
va_start(args);
err(args);
}
void
fatlerr(va_alist)
va_dcl
{
va_list args;
int rc;
va_start(args);
rc = va_arg(args, int);
err(args);
exit(rc);
}
void
err(args)
va_list args;
{
char *func, *fmt;
if (program != 0)
fprintf(stderr, "%s", program);
func = va_arg(args, char *);
if (func != 0 && strcmp(func, "") != 0)
fprintf(stderr, "(%s)", func);
fprintf(stderr, ": ");
fmt = va_arg(args, char *);
vfprintf(stderr, fmt, args);
fputc('\n', stderr);
fflush(stderr);
}
char *
lasterr() {
if (errno < sys_nerr)
return sys_errlist[errno];
else
return "No message text for this error.";
}

224
contrib/standalone.c Normal file
View File

@ -0,0 +1,224 @@
/* Main program for httpd - allows operation as a standalone program
* without running from inetd or as an inetd child.
*/
#include <stdio.h>
#include <errno.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <sys/param.h>
#include <sys/wait.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <netdb.h>
#include <string.h>
#include <signal.h>
extern int getopt(int argc, char ** argv, const char * optstring);
extern char *optarg;
extern int optind, opterr;
#include "httpd.h"
void
DoNothing()
{
return;
}
void
SetAlarm()
{
alarm(10);
}
int
main(int argc, char ** argv)
{
int srvsock;
struct sockaddr_in iso;
int srvport;
char * progname;
char * config_file=CONFIG_FILE;
char * portname = "http";
int standalone = 0;
int inetd = 0;
int badarg = 0;
int c;
char * errptr;
struct sigaction sa;
memset((void *)&sa, 0, sizeof(struct sigaction));
sa.sa_handler = DoNothing;
sigfillset(&sa.sa_mask);
sa.sa_flags = 0;
sigaction(SIGCLD, &sa, NULL);
sa.sa_handler = SetAlarm;
sigaction(SIGALRM, &sa, NULL);
alarm(10);
errno = 0;
progname = strrchr(argv[0], '/');
if (progname == NULL) {
progname = argv[0];
} else {
++progname;
}
while ((c = getopt(argc, argv, "Hf:p:si")) != EOF) {
switch(c){
case 'f':
config_file = optarg;
break;
case 'H':
fprintf(stderr,"\
%s - a simple http daemon\n\
Usage: %s [-H] [-f file] [-p port] [-s] [-i]\n\
\n\
-H print this message and exit\n\
-f file specify name of config file (default is %s)\n\
-p port specify symbolic or numeric port to listen on (default is http)\n\
-s act as a standalone server rather than run under inetd\n\
-i assume startup was from inetd.\n\
\n\
Note that one of the -i or -s options must be given.\n",
progname, progname, CONFIG_FILE);
exit(0);
break;
case 'p':
portname = optarg;
break;
case 's':
standalone = 1;
inetd = 0;
break;
case 'i':
inetd = 1;
standalone = 0;
break;
default:
badarg = 1;
break;
}
}
argc -= optind;
argv += optind;
if (argc > 0) {
badarg = 1;
}
if (badarg) {
fprintf(stderr, "%s: incorrect arguments, use -H option for help.\n",
progname);
exit(2);
}
if (standalone == 0 && inetd == 0) {
fprintf(stderr,
"%s: must give one of -i or -s options (see -H for help).\n",
progname);
exit(2);
}
if (inetd) {
/* If this is supposed to run from inetd, just start processing requests.
*/
read_config(config_file,stdout);
process_request(stdin,stdout);
fclose(stdin);
fclose(stdout);
exit(0);
}
/* This must be a standalone server - do the inetd work myself, forking
* off a child to handle each new connection.
*/
srvport = (int)strtol(portname, &errptr, 10);
if (errptr == NULL || errptr == portname || *errptr != '\0') {
struct servent *sp;
sp = getservbyname(portname, "tcp");
if (sp == NULL) {
fprintf(stderr, "%s: unable to lookup service \"%s\".\n",
progname, portname);
exit(2);
}
srvport = sp->s_port;
}
srvsock = socket(AF_INET, SOCK_STREAM, 0);
if (srvsock < 0) {
fprintf(stderr, "%s: socket() call failed with errno %d (%s)\n",
progname, errno, strerror(errno));
exit(2);
}
memset(&iso, 0, sizeof(iso));
iso.sin_family = AF_INET;
iso.sin_addr.s_addr = INADDR_ANY;
iso.sin_port = srvport;
errno = 0;
if (bind(srvsock, (struct sockaddr *)&iso, sizeof(iso)) == -1) {
fprintf(stderr, "%s: bind() call failed with errno %d (%s)\n",
progname, errno, strerror(errno));
exit(2);
}
errno = 0;
if (listen(srvsock, 5) == -1) {
fprintf(stderr, "%s: listen() call failed with errno %d (%s)\n",
progname, errno, strerror(errno));
exit(2);
}
for ( ; ; ) {
struct sockaddr_in fromaddr;
int fromlen;
int newfd;
int childpid;
memset(&fromaddr, 0, sizeof(fromaddr));
fromaddr.sin_family = AF_INET;
fromlen = sizeof(fromaddr);
errno = 0;
newfd = accept(srvsock, (struct sockaddr *)&fromaddr, &fromlen);
if (newfd < 0) {
if (errno == EINTR) {
/* This must have been a SIGCLD or SIGALRM awakening us from a
* deep slumber. Just reap children to get rid of zombies and
* loop again.
*/
int stat;
int rval;
while ((rval = waitpid((pid_t)-1, &stat, WNOHANG)) > 0);
} else {
fprintf(stderr, "%s: accept() call failed with errno %d (%s)\n",
progname, errno, strerror(errno));
exit(2);
}
} else {
childpid = fork();
if (childpid < 0) {
fprintf(stderr, "%s: fork() call failed with errno %d (%s)\n",
progname, errno, strerror(errno));
exit(2);
} else if (childpid == 0) {
/* This is the child. Set stdin and stderr to newfd and
* process httpd type stuff. Alos clear any alarms and
* signal handlers.
*/
alarm(0);
sa.sa_handler = SIG_DFL;
sigaction(SIGALRM, &sa, NULL);
sigaction(SIGCLD, &sa, NULL);
close(0);
close(1);
dup2(newfd,0);
dup2(newfd,1);
close(newfd);
setvbuf(stdout, NULL, _IOLBF, BUFSIZ);
read_config(config_file,stderr);
process_request(stdin,stdout);
fclose(stdin);
fclose(stdout);
exit(0);
} else {
/* In parent, just close the new socket and listen again
*/
close(newfd);
}
}
}
}

View File

@ -0,0 +1,5 @@
<TITLE>Annotation error</TITLE>
<H1>Bad Annotation Request</H1>
Your World Wide Web browser sent an annotation request which this server was unable to understand.

View File

@ -0,0 +1,6 @@
<TITLE>Misconfigured Annotation Server</TITLE>
<H1>Misconfigured Annotation Server</H1>
This annotation server has not been set up properly or has been corrupted.
Please contact the administrators to inform them of this fact.

7
errors/bad_file.html Normal file
View File

@ -0,0 +1,7 @@
<TITLE>Bad File Request</TITLE>
<H1>Bad File Request</H1>
Your browser attempted to access a file which doesn't exist on this
server, or a file for which it does not have permission to read.

6
errors/bad_request.html Normal file
View File

@ -0,0 +1,6 @@
<TITLE>Browser error</TITLE>
<H1>Bad Request</H1>
Your World Wide Web browser sent a request which this server was unable
to understand.

6
errors/bad_server.html Normal file
View File

@ -0,0 +1,6 @@
<TITLE>Misconfigured Server</TITLE>
<H1>Misconfigured Server</H1>
This server has not been set up properly by the maintainers of this node.
Please contact the administrators to inform them of this fact.

8
errors/gopher_error.html Normal file
View File

@ -0,0 +1,8 @@
<TITLE>Gopher Setup Error</TITLE>
<H1>Gopher Setup Error</H1>
This server had a problem reading a gopher directory. Please contact its
administrators. <P>

View File

@ -0,0 +1,7 @@
<TITLE>Unimplemented Request</TITLE>
<H1>Unimplemented Request</H1>
Your browser requested a service which this server does not know how to
resolve.

94
http_config.c Normal file
View File

@ -0,0 +1,94 @@
/*
* http_config.c: auxillary functions for reading httpd's config file
* and converting filenames into a namespace
*
* Rob McCool 3/21/93
*
*/
#include "httpd.h"
typedef struct {
char name[MAX_STRING_LEN];
char real[MAX_STRING_LEN];
} alias;
static int num_aliases;
alias aliases[MAX_ALIASES];
char original_name[MAX_STRING_LEN]; /* !!! FIX THIS !!! */
void unmunge_name(char *name)
{
strcpy(name,original_name);
}
void translate_name(char *name)
{
register int x,l;
strcpy(original_name,name);
for(x=0;x<num_aliases;x++) {
l=strlen(aliases[x].name);
if(!(strncmp(name,aliases[x].name,l)))
strsubfirst(l,name,aliases[x].real);
}
}
/* debugging */
void dump_aliases()
{
register int x;
char blorf[MAX_STRING_LEN];
for(x=0;x<num_aliases;x++)
printf("%s:%s\n",aliases[x].name,aliases[x].real);
strcpy(blorf,"./../.././.././.././../etc/passwd");
translate_name(blorf);
printf("translated %s to %s\n","./../.././.././.././../etc/passwd",blorf);
strcpy(blorf,"../.././../../../../../././../././etc/passwd");
translate_name(blorf);
}
int read_config(char *name, FILE *errors)
{
FILE *cfg;
char config_line[MAX_STRING_LEN];
if(!(cfg=fopen(name,"r")))
server_error(errors,CONFIG_FILENAME);
num_aliases=0;
while(fgets(config_line,MAX_STRING_LEN,cfg))
if((config_line[0] != '\0') && (config_line[0] != '#') &&
(config_line[0] != '\n')) {
register int x=0,y,z;
x=0;
while((config_line[x] != '\0') && (config_line[x] != ':'))
x++;
if(config_line[x] == '\0')
server_error(errors,CONFIG_SYNTAX);
config_line[x]='\0';
strcpy(aliases[num_aliases].name,config_line);
y=x+1;
if(config_line[y]=='\0') server_error(errors,CONFIG_SYNTAX);
while(config_line[y] != '\0') y++;
if(config_line[y-1]=='\n')
config_line[--y]='\0';
strcpy_nocrlf(aliases[num_aliases].real,&config_line[x+1]);
if((x==1) && (config_line[y-1] != '/')) {
aliases[num_aliases].real[y-2]='/'; /* y-x-1 */
aliases[num_aliases].real[y]='\0'; /* y-x-1 +1 */
}
translate_name(aliases[num_aliases].name);
num_aliases++;
}
}

125
http_dir.c Normal file
View File

@ -0,0 +1,125 @@
/*
* http_dir.c: Handles the on-the-fly html index generation
*
* Rob McCool
* 3/23/93
*
*/
/* httpd.h includes proper directory file */
#include "httpd.h"
void output_directories(char **ar,int n,char *name,FILE *fd)
{
int x,lnum=0;
if(!strcmp(name,"/")) name[0]='\0';
for(x=0;x<n;x++) {
if(strcmp(ar[x],".")) {
fprintf(fd,"<LI> <A NAME=%d HREF=\"",++lnum);
if(strcmp(ar[x],".."))
{
if (name[strlen(name)-1] == '/')
fprintf(fd,"%s%s\">%s</A>",name,ar[x],ar[x]);
else
fprintf(fd,"%s/%s\">%s</A>",name,ar[x],ar[x]);
}
else {
char blorf[MAX_STRING_LEN];
strcpy(blorf,name);
if (name[strlen(name)-1] == '/')
strcat(blorf,"..");
else
strcat(blorf,"/..");
getparents(blorf);
if(blorf[0]=='\0') {
blorf[0]='/';
blorf[1]='\0';
}
fprintf(fd,"%s\">%s</A>",blorf,"Parent Directory");
}
fputc(LINEFEED,fd);
}
}
}
int dsortf(char **s1,char **s2)
{
return(strcmp(*s1,*s2));
}
void index_directory(char *name, FILE *fd)
{
DIR *d;
struct DIR_TYPE *dstruct;
int num_ent=0,x;
struct ent *head,*p,*q;
char **ar;
char unmunged_name[MAX_STRING_LEN];
strcpy(unmunged_name,name);
unmunge_name(unmunged_name);
#ifdef GROK_GOPHER
if(!strncmp(unmunged_name,GOPHER_LOCATION,GOPHER_LOC_LEN)) {
gopher_index(name,fd);
return;
}
#endif
if(!(d=opendir(name)))
client_error(fd,BAD_FILE);
/* MUST RE-TRANSLATE DIRECTORY NAME SO USER DOES NOT SEE TRUE NAMES */
strcpy(name,unmunged_name);
/* Spew HTML preamble */
fprintf(fd,"<TITLE>Index of %s</TITLE>",name);
fputc(LINEFEED,fd);
fprintf(fd,"<H1>Index of %s</H1>",name);
fputc(LINEFEED,fd);
fprintf(fd,"<UL>");
fputc(LINEFEED,fd);
/*
* Since we don't know how many dir. entries there are, put them into a
* linked list and then arrayificate them so qsort can use them.
*/
head=(struct ent *)malloc(sizeof(struct ent));
q=head;
while(dstruct=readdir(d)) {
p=(struct ent *)malloc(sizeof(struct ent));
p->name=strdup(dstruct->d_name);
p->next=NULL;
q->next=p;
q=p;
num_ent++;
}
ar=(char **) malloc(num_ent*sizeof(char *));
p=head;
x=0;
while(p=p->next)
ar[x++]=p->name;
qsort((void *)ar,num_ent,sizeof(char *),
#ifdef ULTRIX_BRAIN_DEATH
(int (*))dsortf);
#else
(int (*)(const void *,const void *))dsortf);
#endif
output_directories(ar,num_ent,name,fd);
free(ar);
p=head->next;
q=head;
while(p=p->next) {
free(q->name);
free(q);
q=p;
}
closedir(d);
fprintf(fd,"</UL>");
}

341
http_gopher.c Normal file
View File

@ -0,0 +1,341 @@
/*
* http_gopher.c: groks gopher directories and spits back HTML links
*
* This is big and ugly.
*
* Rob McCool (robm@ncsa.uiuc.edu)
*/
#include "httpd.h"
#ifdef GROK_GOPHER
/* Great green globs of greasy grimy gopher guts... */
typedef struct glist {
char type;
char name[MAX_STRING_LEN];
int number;
char host[MAX_STRING_LEN];
int port;
char path[MAX_STRING_LEN];
struct glist *next;
} gopher_ent;
/* Number is optional */
#define PATH 1
#define TYPE (1<<1)
#define NAME (1<<2)
#define PORT (1<<3)
#define HOST (1<<4)
#define ALL (PATH | TYPE | NAME | PORT | HOST)
#define NUM_IGNORE 9
static char *ignore_files[]=
{
".",
"..",
"bin",
"core",
"dev",
"etc",
"home",
"usr",
"lost+found"
};
static FILE *fd;
int ignore(char *name) {
int x;
for(x=0;x<NUM_IGNORE;x++)
if(!strcmp(ignore_files[x],name))
return 1;
return 0;
}
void process_cap_file(char *name,gopher_ent **ary,int n) {
char path[MAX_STRING_LEN];
DIR *d;FILE *f;
struct DIR_TYPE *cap;
int l,i;
make_full_path(name,".cap",path);
l=strlen(path);
if(!(d=opendir(path)))
return;
path[l++]='/';
while(cap=readdir(d)) {
strcpy(&path[l],cap->d_name);
if(!is_directory(path)) {
if(f=fopen(path,"r")) {
while(!feof(f)) {
char line[MAX_STRING_LEN];
fgets(line,MAX_STRING_LEN,f);
if(!strncmp(line,"Name=",5)) {
for(i=0;i<n;i++) {
if(!strcmp(ary[i]->path,cap->d_name)) {
strcpy_nocrlf(ary[i]->name,&line[5]);
break;
}
}
}
if(!strncmp(line,"Numb=",5)) {
for(i=0;i<n;i++) {
if(!strcmp(ary[i]->path,cap->d_name)) {
ary[i]->number=atoi(&line[5]);
break;
}
}
}
}
fclose(f);
}
}
}
closedir(d);
}
int gsortf(gopher_ent **g1,gopher_ent **g2) {
register int i1=(*g1)->number,i2=(*g2)->number;
if((i1 >= 0) && (i2 >= 0)) {
if(i1 == i2) return 0;
else if(i1 > i2) return 1;
else return -1;
}
else if(i1 >= 0) return -1;
else if(i2 >= 0) return 1;
else return(strcmp((*g1)->name,(*g2)->name));
}
gopher_ent **sort_list(gopher_ent *head,char *name,int n,int cap_file) {
gopher_ent **ret;
gopher_ent *p=head->next;
int i=0;
if(!(ret=(gopher_ent **)malloc(n*sizeof(gopher_ent *))))
server_error(fd,MEMORY);
while(p) {
ret[i++]=p;
p=p->next;
}
if(cap_file) process_cap_file(name,ret,n);
qsort((void *)ret,n,sizeof(gopher_ent *),
#ifdef ULTRIX_BRAIN_DEATH
(int (*))gsortf);
#else
(int (*)(const void *,const void *))gsortf);
#endif
return ret;
}
void spew_and_free_array(char *name,gopher_ent **ar,int n) {
int lnum=0,i;
if(name[0]=='/' && !(name[1])) name[0]='\0';
for(i=0;i<n;i++) {
switch(ar[i]->type) {
case '0':
case '1':
case '4':
case '5':
case '6':
case '9':
case 'g':
case 'I':
case 'h':
case 's':
/* THIS NEEDS WORK... */
if(!strncmp(ar[i]->path,"ftp:",4)) {
int y=4;
fprintf(fd,"<LI> <A NAME=%d HREF=\"file://",lnum++);
while(ar[i]->path[y]!='@' && ar[i]->path[y])
fputc(ar[i]->path[y++],fd);
if(ar[i]->path[y])
while(ar[i]->path[++y])
fputc(ar[i]->path[y],fd);
else
fputc('/',fd);
fprintf(fd,"\">%s</A>%c",ar[i]->name,LINEFEED);
break;
}
if(ar[i]->host[0]) {
char *host;
host = full_hostname();
/* problem: if we fall through here we may get // */
if(strcmp(host,ar[i]->host)) {
if(ar[i]->path[0]) {
fprintf(fd,
"<LI> <A NAME=%d HREF=\"gopher://%s:%d/%c%s\"",
lnum++,ar[i]->host,ar[i]->port,ar[i]->path[0],
ar[i]->path);
}
else {
fprintf(fd,"<LI> <A NAME=%d HREF=\"gopher://%s:%d/\"",
lnum++,ar[i]->host,ar[i]->port);
}
fprintf(fd,">%s</A>%c",ar[i]->name,LINEFEED);
break;
}
}
/* check the path to see if it starts with 0/ or 1/ */
if((ar[i]->path[0] == '0') && (ar[i]->path[1] == '/'))
strsubfirst(2,ar[i]->path,"");
else if((ar[i]->path[0] == '1') && (ar[i]->path[1] == '/'))
strsubfirst(2,ar[i]->path,"");
fprintf(fd,"<LI> <A NAME=%d HREF=\"%s/%s\">%s</A>%c",
lnum++,name,ar[i]->path,ar[i]->name,LINEFEED);
break;
case '8':
fprintf(fd,"<LI> <A NAME=%d HREF=\"telnet://%s:%d/\">%s</A>%c",
lnum++,ar[i]->host,ar[i]->port,ar[i]->name,LINEFEED);
break;
case 'T':
fprintf(fd,"<LI> <A NAME=%d HREF=\"tn3270://%s:%d/\">%s</A>%c",
lnum++,ar[i]->host,ar[i]->port,ar[i]->name,LINEFEED);
break;
}
free(ar[i]);
}
free(ar);
}
gopher_ent *add_local_link(char *name, gopher_ent *head) {
gopher_ent *p;
if(!(p=(gopher_ent *)malloc(sizeof(gopher_ent))))
server_error(fd,MEMORY);
p->type='0';
p->number=-1;
strcpy(p->path,name);
strcpy(p->name,name);
p->host[0]='\0';
p->port=-1;
p->next=head->next;
head->next=p;
}
int add_links_from_file(char *dir,char *name,gopher_ent *head) {
char tstr[MAX_STRING_LEN];
FILE *f;
gopher_ent *p;
int n=0;
make_full_path(dir,name,tstr);
/* Gopher is stupid. */
if(!(f=fopen(tstr,"r"))) {
return;
}
while(!feof(f)) {
int g;
if(!(p=(gopher_ent *)malloc(sizeof(gopher_ent))))
server_error(fd,MEMORY);
p->number=-1;
g=0;
while(g!=ALL) {
if(feof(f)) {
free(p);
p=NULL;
break;
}
fgets(tstr,MAX_STRING_LEN,f);
if(!strncmp(tstr,"Name=",5)) {
strcpy_nocrlf(p->name,&tstr[5]);
g|=NAME;
}
else if(!strncmp(tstr,"Host=",5)) {
strcpy_nocrlf(p->host,&tstr[5]);
g|=HOST;
}
else if(!strncmp(tstr,"Path=",5)) {
strcpy_nocrlf(p->path,&tstr[5]);
g|=PATH;
}
else if(!strncmp(tstr,"Port=",5)) {
if(tstr[5])
sscanf(&tstr[5],"%d",&p->port);
else p->port=-1;
g|=PORT;
}
else if(!strncmp(tstr,"Type=",5)) {
p->type=tstr[5];
g|=TYPE;
}
else if(!strncmp(tstr,"Numb=",5)) {
if(tstr[5])
sscanf(&tstr[5],"%d",&p->number);
}
}
if(p) {
p->next=head->next;
head->next=p;
n++;
}
}
return(n);
}
void gopher_index(char *name, FILE *outfile) {
DIR *d;
struct DIR_TYPE *dstruct;
gopher_ent head;
gopher_ent **sorted;
int num_entries;
char unmunged_name[MAX_STRING_LEN];
int cap_file=0;
fd=outfile;
head.next=NULL;
strcpy(unmunged_name,name);
unmunge_name(unmunged_name);
if(!(d=opendir(name)))
client_error(fd,BAD_FILE);
while(dstruct=readdir(d)) {
char *fn;
fn=dstruct->d_name;
if(ignore(fn))
continue;
if(fn[0]=='.') {
if(!strcmp(fn,".cap"))
cap_file=1;
else
num_entries+=add_links_from_file(name,fn,&head);
}
else {
add_local_link(fn,&head);
num_entries++;
}
}
fprintf(fd,"<TITLE> Gopher Index of %s</TITLE>%c",unmunged_name,LINEFEED);
fprintf(fd,"<H1>Gopher Index of %s</H1>%c",unmunged_name,LINEFEED);
fprintf(fd,"<UL>%c",LINEFEED);
if(num_entries) {
sorted=sort_list(&head,name,num_entries,cap_file);
spew_and_free_array(unmunged_name,sorted,num_entries);
}
fprintf(fd,"</UL>%c",LINEFEED);
closedir(d);
}
#endif

303
http_request.c Normal file
View File

@ -0,0 +1,303 @@
/*
* http_request.c: functions to get and process requests
*
* Rob McCool 3/21/93
*
*/
#include "httpd.h"
#ifdef LOGGING
#include <arpa/inet.h> /* for inet_ntoa */
#include <time.h> /* for ctime */
#endif /* LOGGING */
#ifdef ANNOTATIONS
#define COMMANDS 5
#else
#define COMMANDS 1
#endif
static char *commands[]={
"GET ",
#ifdef ANNOTATIONS
"ANN_GET ",
"ANN_SET ",
"ANN_CHANGE ",
"ANN_DELETE ",
#endif
};
#ifdef OLD
void send_fd(FILE *f, FILE *fd)
{
int num_chars=0;
while(!feof(f)) {
char c;
c=fgetc(f);
#ifdef TRUNCATE_LEN
++num_chars;
if((c==LINEFEED) || (c==CRETURN) || (num_chars==TRUNCATE_LEN)) {
fputc(LINEFEED,fd);
if(c==CRETURN && !feof(f)) {
++num_chars;
c=fgetc(f);
if(c!=LINEFEED)
fputc(c,fd);
}
else if(num_chars==TRUNCATE_LEN) {
while((c!=LINEFEED) && !feof(f))
c=fgetc(f);
}
num_chars=0;
}
else
#endif
fputc(c,fd);
}
}
#endif
void send_fd(FILE *f, FILE *fd)
{
int num_chars=0;
while (1)
{
char c;
c = fgetc(f);
if (feof(f))
goto done;
#ifdef TRUNCATE_LEN
++num_chars;
if((c==LINEFEED) || (c==CRETURN) || (num_chars==TRUNCATE_LEN)) {
fputc(LINEFEED,fd);
if(c==CRETURN && !feof(f)) {
++num_chars;
c=fgetc(f);
if(c!=LINEFEED)
fputc(c,fd);
}
else if(num_chars==TRUNCATE_LEN) {
while((c!=LINEFEED) && !feof(f))
c=fgetc(f);
}
num_chars=0;
}
else
#endif
fputc(c,fd);
}
done:
return;
}
void send_file(char *file, FILE *fd) {
FILE *f;
if(!(f=fopen(file,"r")))
client_error(fd,BAD_FILE);
send_fd(f,fd);
}
void send_error_file(char *file,FILE *fd) {
FILE *f;
if(!(f=fopen(file,"r"))) {
fprintf(fd,"<TITLE>Bad configuration</TITLE>%c",LINEFEED);
fprintf(fd,"<H1>Bad configuration</H1>%c",LINEFEED);
fprintf(fd,
"%cThis server cannot find an error file to send you.<P>%c",
LINEFEED,LINEFEED);
exit(1);
}
send_fd(f,fd);
}
/* GET */
void send_node(char *file,FILE *fd)
{
struct stat finfo;
if(stat(file,&finfo) == -1)
client_error(fd,BAD_FILE);
if(S_ISDIR(finfo.st_mode)) {
char name[MAX_STRING_LEN];
strcpy(name,file);
/* Bugfix in case there's no trailing slash. */
if (name[strlen(name)-1] != '/')
strcat(name, "/");
strcat(name,HTML_DIR_CONTENT);
if(stat(name,&finfo) == -1)
/* index file not found; get dir. list and send it back as HTML */
index_directory(file,fd);
else send_file(name,fd);
}
else if(S_ISREG(finfo.st_mode))
send_file(file,fd);
else client_error(fd,BAD_FILE);
}
void process_request(FILE *in, FILE *out)
{
char cmd_line[MAX_STRING_LEN],*ret;
register int x,y,z;
#ifdef LOGGING
char *who_called = NULL;
struct sockaddr addr;
int len, retval;
FILE *fp;
time_t time_val = time(NULL);
char *time_string = ctime(&time_val);
#endif /* LOGGING */
for (x=0; x<MAX_STRING_LEN; x++)
{
cmd_line[x] = (char)fgetc(in);
if ((cmd_line[x] == ' ')||(cmd_line[x] == LINEFEED))
{
break;
}
}
if ((x == MAX_STRING_LEN)||(cmd_line[x] == LINEFEED))
{
client_error(out,BAD_REQUEST);
}
cmd_line[x + 1] = '\0';
/* Currently, this does not take into account execution of different cmds */
for(y=0;y<COMMANDS;y++) {
if(!(strncmp(commands[y],cmd_line,strlen(commands[y])))) {
int val;
val = fgetc(in);
if (val != '/')
{
client_error(out,BAD_FILE); /* FIX */
}
#ifdef ANNOTATIONS
switch(y) {
case 0: /* GET */
break;
case 1: /* ANN_GET */
Get_request(in, out);
return;
break;
case 2: /* ANN_SET */
Set_request(in, out);
return;
break;
case 3: /* ANN_CHANGE */
Change_request(in, out);
return;
break;
case 4: /* ANN_DELETE */
Delete_request(in, out);
return;
break;
}
#endif
if(!(ret=(char *)malloc(MAX_STRING_LEN*sizeof(char))))
server_error(out,MEMORY);
x++;
cmd_line[x] = '/';
z = x;
x++;
val = fgetc(in);
while ((x < (MAX_STRING_LEN - 1))&&(val != EOF)&&
(val != LINEFEED)&&(val != CRETURN)&&(!isspace(val)))
{
cmd_line[x] = (char)val;
x++;
val = fgetc(in);
}
cmd_line[x] = '\0';
#ifdef LOGGING
/*
* Get a sockaddr structure filled in for our peer.
*/
len = sizeof(struct sockaddr);
retval = getpeername(fileno(stdin), &addr, &len);
if (retval == 0)
{
struct in_addr *iaddr;
struct hostent *hptr;
iaddr = &(((struct sockaddr_in *)&addr)->sin_addr);
hptr = gethostbyaddr((char *)iaddr,
sizeof(struct in_addr), AF_INET);
if (hptr != NULL)
{
who_called = (char *)malloc(strlen(hptr->h_name) + 1);
strcpy(who_called, hptr->h_name);
}
else
{
char *iname;
iname = inet_ntoa(*iaddr);
if (iname != NULL)
{
who_called = (char *)malloc(strlen(iname) + 1);
strcpy(who_called, iname);
}
else
{
who_called = (char *)malloc(
strlen("UNKNOWN_CALLER") + 1);
strcpy(who_called, "UNKNOWN_CALLER");
}
}
}
else
{
who_called = (char *)malloc(strlen("UNKNOWN_CALLER") + 1);
strcpy(who_called, "UNKNOWN_CALLER");
}
fp = fopen(LOGFILE, "a");
if (fp)
{
time_string[strlen(time_string) - 1] = '\0';
fprintf(fp, "%s\t[%s] %s\n", who_called, time_string, cmd_line);
fclose(fp);
}
#endif /* LOGGING */
strcpy_nocrlf(ret, &cmd_line[z]);
getparents(ret);
translate_name(ret);
switch(y) {
case 0: /* GET */
if(ret[0]!='/') client_error(out,BAD_FILE); /* FIX */
send_node(ret,out);
break;
}
return;
}
}
client_error(out,BAD_REQUEST);
}

160
httpd.c Normal file
View File

@ -0,0 +1,160 @@
/*
* httpd.c: simple http daemon for answering WWW file requests
*
*
* Rob McCool 3/21/93
*
*/
#include "httpd.h"
#include <signal.h>
#include <sys/wait.h>
#include <errno.h>
void usage(char *bin) {
fprintf(stderr,"Usage: %s [-s] [-f filename] [-p portnum] [-u userid] [-g groupid]\n",bin);
fprintf(stderr,"-f filename : specify an alternate config file\n");
fprintf(stderr,"-s : Run as standalone server instead of from inetd\n");
fprintf(stderr,"-p portnumber : alternate port number.\n");
fprintf(stderr,"(server must initially run as root for ports under 1024.)\n");
fprintf(stderr,"\nThe following two options require the daemon to be initially run as root:\n");
fprintf(stderr,"-u userid : run as named user-id\n");
fprintf(stderr,"-g groupid : run as named group-id\n");
fprintf(stderr,"A user or group number may be specified with a # as the first character.\n");
exit(1);
}
void detach() {
int x;
if((x = fork()) > 0)
exit(0);
else if(x == -1)
server_error(stdout,FORK);
#ifdef BSD
setpgrp(0,getpid());
if((x=open("/dev/tty",O_RDWR,0)) >= 0) {
ioctl(x,TIOCNOTTY,0);
close(x);
setsid();
}
#else
setpgrp();
#endif
}
#ifdef BSD
void ign() {
int status;
pid_t pid;
while( (pid = wait3(&status, WNOHANG, NULL)) > 0);
}
#endif
void standalone_main(int port, uid_t uid, gid_t gid) {
int sd,csd, clen,pid;
struct sockaddr_in sa_server,sa_client;
detach();
if ((sd = socket(AF_INET,SOCK_STREAM,IPPROTO_TCP)) == -1)
server_error(stdout,SOCKET);
bzero((char *) &sa_server, sizeof(sa_server));
sa_server.sin_family=AF_INET;
sa_server.sin_addr.s_addr=htonl(INADDR_ANY);
sa_server.sin_port=htons(port);
if(bind(sd,(struct sockaddr *) &sa_server,sizeof(sa_server)) == -1) {
perror("bind");
server_error(stdout,SOCKET_BIND);
}
listen(sd,5);
#ifdef BSD
signal(SIGCHLD,(void (*)())ign);
#else
signal(SIGCHLD,SIG_IGN);
#endif
/* we're finished getting the socket, switch to running userid */
setuid(uid);
setgid(gid);
while(1) {
retry:
clen=sizeof(sa_client);
if((csd=accept(sd,&sa_client,&clen)) == -1) {
if(errno == EINTR) {
#ifdef BSD
ign();
#endif
goto retry;
}
perror("accept");
server_error(stdout,SOCKET_ACCEPT);
}
if((pid = fork()) == -1)
server_error(stdout,FORK);
else if(!pid) {
close(0);
close(1);
dup2(csd,0);
dup2(csd,1);
close(sd);
process_request(stdin,stdout);
fclose(stdin);
fclose(stdout);
exit(0);
}
close(csd);
}
}
extern char *optarg;
extern int optind;
main(int argc, char *argv[])
{
char config_file[MAX_STRING_LEN];
int standalone=0,c,port = HTTPD_PORT;
uid_t uid;
gid_t gid;
uid = uname2id(DEFAULT_USER);
gid = gname2id(DEFAULT_GROUP);
strcpy(config_file,CONFIG_FILE);
while((c = getopt(argc,argv,"sf:p:u:g:")) != -1) {
switch(c) {
case 's':
standalone=1;
break;
case 'f':
strcpy(config_file,optarg);
break;
case 'p':
port = atoi(optarg);
break;
case 'u':
uid = uname2id(optarg);
break;
case 'g':
gid = gname2id(optarg);
break;
case '?':
usage(argv[0]);
}
}
read_config(config_file,stdout);
if(standalone)
standalone_main(port,uid,gid);
else
process_request(stdin,stdout);
fclose(stdin);
fclose(stdout);
exit(0);
}

31
httpd.conf Normal file
View File

@ -0,0 +1,31 @@
# Sample httpd.conf file
#
# Only directories can be mapped.
#
# The format is:
#
# alias:real
#
# where alias is the name that appears to the user, and real is the
# object's real location. Files are specified by an alias with no trailing
# /, and directories need trailing slashes the real name.
#
# Aliases are self-stacking: If you first map something, then map
# something else, the second will be interpreted with all of the previous
# aliases expanded.
#
# To deny permission to a directory, map it to /deny or something
# non-existent.
#
# For instance, to map the root directory to /u/web-docs, you would use the
# line:
# /:/u/web-docs/
#
#
# After that line, all real directory names will be relative to the new root,
# and all incoming requests will be relative to that new root.
#
# Then, if you want to deny permission to /u/web-docs/private, you would use:
# /private:/deny
/:/tmp

256
httpd.h Normal file
View File

@ -0,0 +1,256 @@
/*
* httpd.h: header for simple (ha! not anymore) http daemon
*/
/* ----------------------------- config file ------------------------------ */
/* Define this to be the default configuration file */
#define CONFIG_FILE "/usr/local/etc/httpd/httpd.conf"
/* -------------- Port number for server running standalone --------------- */
#define HTTPD_PORT 80
/* --------- Default user name and group name running standalone ---------- */
/* --- These may be specified as numbers by placing a # before a number --- */
#define DEFAULT_USER "nobody"
#define DEFAULT_GROUP "#-1"
/* Define if your system calls are BSD-style */
#undef BSD
/* ---------------------------- access logging ---------------------------- */
/* If you want to have server access logging, define this. */
#define LOGGING
#ifdef LOGGING
/* The name of the log file -- this must be writeable by the userid
the server is run under (usually 'nobody'). */
#define LOGFILE "/usr/local/etc/httpd/access_log"
#endif /* LOGGING */
/* ---------------------------- gopher support ---------------------------- */
/* Define this if you want to support . links and .cap gopherisms */
/* This has other effects, see README.GOPHER for details. */
#undef GROK_GOPHER
#ifdef GROK_GOPHER
/* Set this to the port # of your gopher server */
#define GOPHERPORT 70
/* This is the virtual location of the Gopher data directory. You should
establish a mapping from this to your real Gopher data directory in
httpd.conf, as explained in README.GOPHER. */
#define GOPHER_LOCATION "/gopher-data"
#define GOPHER_LOC_LEN 12
#endif /* GROK_GOPHER */
/* ----------------------- group annotation support ----------------------- */
/* Define this if you want this server to serve as a group annotation
server. See README for details. */
#undef ANNOTATIONS
/* #define ANNOTATIONS */
#ifdef ANNOTATIONS
/* The physical directory where all group annotations will be stored.
THIS MUST BE WRITEABLE by the user ID under which you run httpd
(usually 'nobody'). */
#define ANN_DIR "/usr/local/etc/httpd/annotations"
/* The virtual directory (i.e., from the outside coming into the server)
where the group annotations will be located.
E.g., if you normally map / to /usr/local/etc/httpd in httpd.conf,
and you have ANN_DIR set to /usr/local/etc/httpd/annotations, then
this should be "/annotations" (note the slash).
This means that the annotation directory must be browsable
according to the standard rules in httpd.conf. */
#define ANN_VIRTUAL_DIR "/annotations"
/* This is your port number, same number used in /etc/services. */
#define ANN_PORT 80
#endif
/* ------------------------- other customizations ------------------------- */
/* Define this if you want errors logged to syslog and your system has the
syslog() system call and syslog.h */
#undef SYSLOG
/* The default string lengths */
#define MAX_STRING_LEN 256
/* The maximum number of aliases in the config file */
#define MAX_ALIASES 20
/* Define this to be what your HTML directory content files are called */
#define HTML_DIR_CONTENT "index.html"
/* Set this to the hardcoded fully qualified machine name.
You may #undef this if you choose, and httpd will then use the
gethostname() and gethostbyname() calls to figure out the fully
qualified machine name on the fly; HOWEVER, gethostbyname() called
on the name of the local host coredumps on some systems. */
/* #define HARDCODED_HOSTNAME "machine.foo.edu" */
#undef HARDCODED_HOSTNAME
/*
* The number of characters to truncate the outgoing file's lines to
* "Well behaved" servers should truncate this to 80. Some link pathnames
* tend to go ballistic, so I would set this higher.
*
* If you want to serve JPEG's and other such binary files, #undef this. */
#undef TRUNCATE_LEN
/*
* The particular directory style your system supports. If you have dirent.h
* in /usr/include (POSIX) or /usr/include/sys (SYSV), #include
* that file and define DIR_TYPE to be dirent. Otherwise, if you have
* /usr/include/sys/dir.h, define DIR_TYPE to be direct and include that
* file. If you have neither, I'm confused.
*/
#include <dirent.h>
#define DIR_TYPE dirent
/* ------------------------------ error urls ------------------------------ */
/* Error files. BE SURE THESE WORK! NOTE THAT ALIASES ARE NOT TRANSLATED! */
#define SERVER_ERROR "/usr/local/etc/httpd/errors/bad_server.html"
#define ANN_SERVER_ERROR "/usr/local/etc/httpd/errors/bad_ann_server.html"
#define BAD_REQUEST "/usr/local/etc/httpd/errors/bad_request.html"
#define BAD_ANN_REQUEST "/usr/local/etc/httpd/errors/bad_ann_request.html"
#define BAD_FILE "/usr/local/etc/httpd/errors/bad_file.html"
#define UNIMPLEMENTED "/usr/local/etc/httpd/errors/unimplemented.html"
#define GOPHER_ERROR "/usr/local/etc/httpd/errors/gopher.html"
/* ----------------------------- other stuff ------------------------------ */
/* You shouldn't have to edit anything below this line. */
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/file.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <netdb.h>
#include <sys/ioctl.h>
#ifdef NEXT_BRAIN_DEATH
#define S_ISDIR(m) (((m)&(S_IFMT)) == (S_IFDIR))
#define S_ISREG(m) (((m)&(S_IFMT)) == (S_IFREG))
#endif
#ifdef ultrix
#define ULTRIX_BRAIN_DEATH
#define NEED_STRDUP
#endif
#ifdef SYSLOG
#include <syslog.h>
#endif
/* Just in case your linefeed isn't the one the other end is expecting. */
#define LINEFEED 10
#define CRETURN 13
/* Messages which are logged upon system errors. */
#define CONFIG_FILENAME \
"httpd: configuration file not found or permission denied"
#define CONFIG_SYNTAX "httpd: syntax error in configuration file"
#define MEMORY "httpd: ran out of memory"
#define FORK "httpd: could not fork"
#define SOCKET "httpd: socket problem"
#define SOCKET_BIND "httpd: socket bind problem"
#define SOCKET_ACCEPT "httpd: connection accept problem"
#define BAD_USERNAME "httpd: bad username"
#define BAD_GROUPNAME "httpd: bad groupname"
#ifdef SYSLOG
#define msg_out(fd,msg) (syslog(LOG_ERR,(msg)))
#else
#if 1
#define msg_out(fd,msg) fprintf((fd),(msg))
#else
#define msg_out(fd,msg) (fprintf((fd),(msg)),send_error_file((msg),(fd)))
#endif
#endif
/* log_error prints a server error to the appropriate places and exits */
#define server_error(fd,msg) (msg_out(fd,msg),send_error_file(msg,fd),\
exit(0))
#define client_error(fd,msg) (send_error_file(msg,fd),exit(0))
struct ent {
char *name;
struct ent *next;
};
typedef struct {
char *url;
int acnt;
int *ann_array;
} LogRec;
typedef struct {
int hash;
int cnt;
LogRec *logs;
} LogData;
/* Function prototypes. */
extern int read_config(char *name, FILE *errors);
extern void send_node(char *name, FILE *fd);
extern void translate_name(char *name);
extern void process_request(FILE *in, FILE *out);
extern void Get_request(FILE *in, FILE *out);
extern void Set_request(FILE *in, FILE *out);
extern void Change_request(FILE *in, FILE *out);
extern void Delete_request(FILE *in, FILE *out);
extern void strsubfirst(int start,char *dest, char *src);
extern void strcpy_nocrlf(char *dest, char *src);
extern char *strsub(char *src, char *oldstr, char *newstr);
extern void make_full_path(char *src1,char *src2,char *dst);
extern int is_directory(char *name);
extern char *full_hostname(void);
extern void index_directory(char *name, FILE *fd);
extern void getparents(char *name);
extern void gopher_index(char *name,FILE *fd);
extern void send_error_file(char *file,FILE *fd);
extern void send_file(char *file,FILE *fd);
extern void send_error_file(char *file,FILE *fd);
extern LogData *GetLogData(int hash);
extern int FindLogData(LogData *lptr, char *url, int **alist, int *acnt);
extern int AddLogData(LogData *lptr, int indx, char *url);
extern char *HashtoUrl(LogData *lptr, int indx);
extern int WriteLogData(LogData *lptr);
extern void FreeLogData(LogData *lptr);
extern char *WriteAudioData(int hash, int indx, char *data, int len, char *type, int no_change);
extern int WriteAnnData(char *url, int hash, int indx, char *title, char *user, char *date, char *data, int len, int no_change);
extern int ReadAnnData(int hash, int indx, char **title, char **user, char **date);
extern int DeleteLogData(LogData *lptr, int indx);
extern void UnlockIt(void);
extern uid_t uname2id(char *name);
extern gid_t gname2id(char *name);
#ifdef NEED_STRDUP
extern char *strdup (char *str);
#endif

167
util.c Normal file
View File

@ -0,0 +1,167 @@
/*
* str.c: string utility things
*
* 3/21/93 Rob McCool
*
*/
#include <string.h>
#include "httpd.h"
#include <pwd.h>
#include <grp.h>
void strcpy_nocrlf(char *dest, char *src)
{
register char c = *src;
while((c!='\0') && (c!=LINEFEED) && (c!=CRETURN))
c=(*dest++ = *src++);
if(c!='\0') *(dest-1)='\0';
}
void strsubfirst(int start,char *dest, char *src)
{
char tmp[MAX_STRING_LEN];
strcpy(tmp,&dest[start]);
strcpy(dest,src);
strcpy(&dest[strlen(src)],tmp);
}
/*
* Parse .. so we don't compromise security
*/
void getparents(char *name)
{
int l=0,w=0;
const char *lookfor="..";
while(name[l]!='\0') {
if(name[l]!=lookfor[w]) (w>0 ? (l-=(w-1),w=0) : l++);
else {
if(lookfor[++w]=='\0') {
if((name[l+1]=='\0') || (name[l+1]=='/')) {
register int m=l+1,n;
l=l-3;
if(l>=0) {
while((l!=0) && (name[l]!='/')) --l;
}
else l=0;
n=l;
while(name[n]=name[m]) (++n,++m);
w=0;
}
else w=0;
}
else ++l;
}
}
}
char *strsub(char *src, char *oldstr, char *newstr) {
register char *t=src,*o,*nsrc=src;
int oldlen,newlen;
char *tosender;
oldlen=strlen(oldstr);
newlen=strlen(newstr);
/* If the new string is longer, assume the worst: our new string has to
* replace every letter in the source with the new. If not, don't thrash
* malloc */
if(oldlen<newlen)
nsrc=(char *)malloc(strlen(src)+(newlen-oldlen)*(strlen(src)/oldlen));
tosender=nsrc;
o=oldstr;
while(*t) {
if(*t != *o) *nsrc++ = *t++;
else {
register char *tmp=++t;
register int n=oldlen-1;
++o; /* advance o (we advanced t above) */
while((*tmp++ == *o++) && n) {
--n;
}
tmp=newstr;
if(!n) { /* we matched */
while(*nsrc++ = *tmp++);
t+=oldlen-1;nsrc--;
}
else *nsrc++ = *(t-1); /* we missed, so put the char into new str */
o=oldstr; /* no matter what, we damaged o */
}
}
*nsrc='\0';
/* not calling free is very anti-social */
if(oldlen<newlen) free(src);
return tosender;
}
void make_full_path(char *src1,char *src2,char *dst) {
while(*dst++ = *src1++);
*(dst-1)='/';
while(*dst++ = *src2++);
}
int is_directory(char *path) {
struct stat finfo;
if(stat(path,&finfo) == -1)
return 0; /* in error condition, just return no */
return(S_ISDIR(finfo.st_mode));
}
char *full_hostname (void)
{
#ifdef HARDCODED_HOSTNAME
return HARDCODED_HOSTNAME;
#else
char str[128];
int len = 128;
gethostname (str, len);
return gethostbyname(str)->h_name;
#endif
}
#ifdef NEED_STRDUP
char *strdup (char *str)
{
char *dup;
dup = (char *)malloc (strlen (str) + 1);
dup = strcpy (dup, str);
return dup;
}
#endif
uid_t uname2id(char *name) {
struct passwd *ent;
if(name[0] == '#')
return(atoi(&name[1]));
if(!(ent = getpwnam(name)))
server_error(stdout,BAD_USERNAME);
else return(ent->pw_uid);
}
gid_t gname2id(char *name) {
struct group *ent;
if(name[0] == '#')
return(atoi(&name[1]));
if(!(ent = getgrnam(name)))
server_error(stdout,BAD_GROUPNAME);
else return(ent->gr_gid);
}