Introduction
Introduction Statistics Contact Development Disclaimer Help
Initial commit at 0.16 of geomyidae. - geomyidae - A small C-based gopherd.
git clone git://bitreich.org/geomyidae/ git://enlrupgkhuxnvlhsf6lc3fziv5h2hhfri…
Log
Files
Refs
Tags
README
LICENSE
---
commit 97230aa165bee0bffae624707ff98d6098da1823
Author: Christoph Lohmann <[email protected]>
Date: Fri, 19 Nov 2010 09:20:38 +0100
Initial commit at 0.16 of geomyidae.
Diffstat:
A LICENSE | 24 ++++++++++++++++++++++++
A Makefile | 56 +++++++++++++++++++++++++++++…
A README | 43 ++++++++++++++++++++++++++++++
A arg.h | 19 +++++++++++++++++++
A geomyidae.8 | 407 +++++++++++++++++++++++++++++…
A handlr.c | 196 +++++++++++++++++++++++++++++…
A handlr.h | 20 ++++++++++++++++++++
A ind.c | 287 +++++++++++++++++++++++++++++…
A ind.h | 48 +++++++++++++++++++++++++++++…
A index.gph | 6 ++++++
A main.c | 385 +++++++++++++++++++++++++++++…
A rc.d/Archlinux.conf.d | 4 ++++
A rc.d/Archlinux.rc.d | 38 +++++++++++++++++++++++++++++…
A rc.d/Gentoo.conf.d | 5 +++++
A rc.d/Gentoo.init.d | 17 +++++++++++++++++
A rc.d/NetBSD.rc.d | 55 +++++++++++++++++++++++++++++…
A rc.d/README | 5 +++++
17 files changed, 1615 insertions(+), 0 deletions(-)
---
diff --git a/LICENSE b/LICENSE
@@ -0,0 +1,24 @@
+MIT/X Consortium License
+
+© 2006-2010 Christoph Lohmann <[email protected]>
+© 2007 Jeff Woodall <[email protected]>
+© 2008 J. A. Neitzel <[email protected]>
+© 2010 James Penketh <[email protected]>
+
+Permission is hereby granted, free of charge, to any person obtaining a
+copy of this software and associated documentation files (the "Software"),
+to deal in the Software without restriction, including without limitation
+the rights to use, copy, modify, merge, publish, distribute, sublicense,
+and/or sell copies of the Software, and to permit persons to whom the
+Software is furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in
+all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+DEALINGS IN THE SOFTWARE.
diff --git a/Makefile b/Makefile
@@ -0,0 +1,56 @@
+PROGRAM = geomyidae
+VERSION = 0.16
+
+PREFIX ?= /usr
+BINDIR ?= $(PREFIX)/bin
+MANDIR ?= $(PREFIX)/man/man8
+
+#CPPFLAGS += -D_BSD_SOURCE
+CFLAGS += -O2 -Wall -I. -I/usr/include
+LDFLAGS += -L/usr/lib -L. -lc
+
+CFILES = main.c ind.c handlr.c
+
+OBJECTS = ${CFILES:.c=.o}
+
+all: $(PROGRAM)
+
+${PROGRAM}: ${OBJECTS}
+ ${CC} ${LDFLAGS} -o ${PROGRAM} ${OBJECTS}
+
+.SUFFIXES : .c .h
+
+.c.o :
+ ${CC} ${CFLAGS} ${CPPFLAGS} -c $<
+.c :
+ ${CC} ${CFLAGS} ${CPPFLAGS} -c $<
+
+clean :
+ @rm -f *.o ${PROGRAM} core *~
+
+install: $(PROGRAM)
+ @mkdir -p ${DESTDIR}${BINDIR}
+ @cp -f ${PROGRAM} ${DESTDIR}${BINDIR}
+ @strip ${DESTDIR}${BINDIR}/${PROGRAM}
+ @chmod 755 ${DESTDIR}${BINDIR}/${PROGRAM}
+ @mkdir -p ${DESTDIR}${MANDIR}
+ @cp -f geomyidae.8 ${DESTDIR}${MANDIR}
+ @chmod 644 ${DESTDIR}${MANDIR}/${PROGRAM}.8
+
+uninstall:
+ @rm -f ${DESTDIR}${BINDIR}/${PROGRAM}
+ @rm -f ${DESTDIR}${MANDIR}/${PROGRAM}.8
+
+dist: clean
+ @mkdir -p "${PROGRAM}-${VERSION}"
+ @cp -r rc.d README LICENSE index.gph Makefile geomyidae.8 \
+ *.c *.h "${PROGRAM}-${VERSION}"
+ @chmod 755 "${PROGRAM}-${VERSION}"
+ @chmod 744 "${PROGRAM}-${VERSION}"/*
+ @tar -cf "${PROGRAM}-${VERSION}.tar" "${PROGRAM}-${VERSION}"
+ @gzip "${PROGRAM}-${VERSION}.tar"
+ @mv "${PROGRAM}-${VERSION}.tar.gz" "${PROGRAM}-${VERSION}.tgz"
+ @rm -rf "${PROGRAM}-${VERSION}"
+
+.PHONY: all clean dist install uninstall
+
diff --git a/README b/README
@@ -0,0 +1,43 @@
+A gopherd for Linux/BSD.
+
+Features:
+ * gopher menus (see index.gph for an example)
+ * dir listings (if no index.gph was found)
+ * cgi support (.cgi files are executed)
+ * search support in CGI files
+ * logging (-l option) and loglevels (-v option)
+
+Usage:
+
+ geomyidae [-d] [-l logfile] [-v loglvl] [-b htdocs] [-p port] [-o spor…
+ [-u user] [-g group] [-h host] [-i IP]
+ -d don't fork into background
+ -l logfile setting this will turn on logging into logfi…
+ -v loglevel see below (default 7)
+ -b htdocs the htdocs root for serving files (default
+ /var/gopher)
+ -p port set the port where geomyidae should lis…
+ (default 70)
+ -o sport set the port that should be shown in the dir
+ listings
+ -u user which user rights the serving children …
+ -g group which group rights the serving children should…
+ -i IP IP which geomyidae should bind to
+ -h host host that should be used in the dir lis…
+
+Loglevels:
+
+ 0 - no logging
+ 1 - served plain files
+ 2 - dir listings
+ 4 - HTTP redirects
+ 8 - not found queries
+
+ 1 + 2 + 4 = 7 (files + dir listings + HTTP)
+
+Init scripts:
+ The rc.d directory includes startup scripts for various distributions.
+
+
+Have fun!
+
diff --git a/arg.h b/arg.h
@@ -0,0 +1,19 @@
+#ifndef ARG_H
+#define ARG_H
+
+#define USED(x) ((void)(x))
+
+extern char *argv0;
+
+#define ARGBEGIN for(argv0 = *argv, argv++, argc--;\
+ argv[0] && argv[0][0]=='-' && argv[0][1];\
+ argc--, argv++) {\
+ char _argc;\
+ _argc = argv[0][1];\
+ switch(_argc)
+#define ARGEND USED(_argc);} USED(argv);USED(argc);
+#define EARGF(x) ((argv[1] == nil)? ((x), abort(), (char *)0) :\
+ (argc--, argv++, argv[0]))
+
+#endif
+
diff --git a/geomyidae.8 b/geomyidae.8
@@ -0,0 +1,407 @@
+.\" geomyidae.8 handcrafted in GNU groff -mdoc using nvi
+.\"
+.Dd March 9, 2008
+.Dt GEOMYIDAE 8
+.Os
+.
+.Sh NAME
+.Nm geomyidae
+.Nd a gopher daemon for Linux/BSD
+.
+.Sh SYNOPSIS
+.Nm
+.Bk -words
+.Op Fl d
+.Op Fl l Ar logfile
+.Op Fl v Ar loglevel
+.Op Fl b Ar base
+.Op Fl p Ar port
+.Op Fl o Ar sport
+.Op Fl u Ar user
+.Op Fl g Ar group
+.Op Fl h Ar host
+.Op Fl i Ar IP
+.Ek
+.
+.Sh DESCRIPTION
+.Nm
+is a daemon for serving the protocol specified in
+.Em RFC 1436
+(Gopher). Under 1000 lines of C by design, it is lightweight yet supports
+dynamic content, automatic file/directory indexing, logging and privilege
+separation.
+.
+.Sh IMPLEMENTATION
+Installation is straightforward: grab the zipped tar file, expand it in
+an appropriate temp directory, change to the
+.Qq "../geomyidae-x.xx"
+directory, tweak the Makefile if desired (installs in
+.Qq "/usr/bin"
+by default), then run the
+.Sq "make ; make install"
+commands. The resulting executable should be run by root.
+.
+.Ss Installation example:
+.Pp
+.Bd -literal
+ % wget http://www.r-36.net/src/geomyidae/geomyidae-current.tgz;
+ % tar -xzvf geomyidae-*.tgz;
+ % cd geomyidae-*;
+ % make; sudo make install;
+ % sudo mkdir -p /var/gopher;
+ % sudo cp index.gph /var/gopher;
+ % sudo eomyidae -l /var/log/geomyidae.log -b /var/gopher -p 70;
+ % tail -f /var/log/geomyidae.log;
+ #
+ # Use whatever gopher client you like to gopher to
+ # gopher://localhost
+ #
+ # Have fun!
+.Ed
+.
+.Ss Running
+Geomyidae should normally be started by root. Though, it can be started
+by a regular user provided that the base directory and its contents are owned
+by the same user. Geomyidae will only serve content within the base directory
+tree and will drop privileges to the
+.Fl u Ar user
+and
+.Fl g Ar group
+values if set. See
+.Ic OPTIONS
+below for specifics.
+.
+.Sh OPTIONS
+geomyidae options and default settings:
+.Pp
+.Bl -tag -width ".Fl test Ao Ar string Ac"
+.
+.It Fl d
+Don't fork into background.
+.
+.It Fl l Ar logfile
+Specify the file where the log output will be written (default: no default).
+.
+.It Fl v Ar loglevel
+Set the logging level (default: 15).
+.
+.Bd -literal
+Loglevels:
+ 0 - no logging
+ 1 - served plain files
+ 2 - directory listings
+ 4 - HTTP redirects
+ 8 - errors (e.g., not found)
+ e.g.:
+ 1 + 2 + 4 + 8 = 15
+ (files + directories + HTTP + errors)
+.Ed
+.
+.It Fl b Ar base
+Root directory to serve (default: /var/gopher)
+.
+.It Fl p Ar port
+Port geomyidae should listen on (default: 70)
+.
+.It Fl o Ar sport
+Port geomyidae displays within base directory (default: 70).
+.
+Use in conjunction with
+.Ic -p
+for obfuscating actual port neomyidae is running on.
+.
+.It Fl u Ar user
+Sets the user to which privileges drop when geomyidae is ready
+to accept network connections (default: user geomyidae runs as).
+Helps improve security by reducing privileges during request
+processing.
+.
+.It Fl g Ar group
+Sets the group to which privileges drop when geomyidae is ready
+to accept network connections (default: group geomyidae runs as).
+Helps improve security by reducing privileges during request
+processing.
+.
+.It Fl h Ar host
+Host to use in directory listings (default: localhost)
+.
+.It Fl i Ar IP
+IP to which geomyidae binds to (default: 127.0.0.1)
+.El
+.
+.Sh FORMATTING
+Structured Gopher space(s) can be created with geomyidae through the
+use of special indexing files of the form
+.Ic <name>.gph
+which, if present, geomyidae uses to format and/or filter the contents of
+the base directory (/var/gopher by default) and create gopher menus.
+However, index files are
+.Em not
+required: if no .gph files are found geomyidae simply lists the directory
+contents in alphanumeric order. In addition, a directory can utilize
+multiple index files to create a layered gopher environment without the
+use of sub-directories: ie. pictures.gph, music.gph, documents.gph could
+be "directories" within main.gph, yet all reside in /var/gopher along with
+their respective files (*.jpg, *.mp3, *.pdf for example).
+.
+.Ss Anatomy of an index.gph file
+In general, each line of an index.gph file has the following structure:
+.Pp
+.Bl -inset -offset indent
+.It Ic [<type>|<desc>|<path>|<port>]
+.El
+.Pp
+where,
+.Bl -inset -offset indent
+.It Ic <type>
+= A valid gopher Item Type.
+.Pp
+Some common Gopher Types as defined in
+.Em RFC 1436
+:
+.
+.Bd -literal
+ 0 Item is a file
+ 1 Gopher directory
+ 3 Error
+ 7 Item is an Index-Search server.
+ 8 Item points to a text-based telnet session.
+ 9 Binary file. Client reads until TCP connection closes!
+ g GIF format graphics file.
+ I Indeterminate image file. Client decides how to display.
+.Ed
+.Pp
+In addition, geomyidae provides these:
+.Bd -literal
+ h Item is a hypertext (HTTP) link
+ i Informational Item (used for descriptive purposes)
+.Ed
+.
+.Pp
+Note: geomyidae doesn't require "informational" text to be formally
+Typed as "[i|...]"; any line
+.Em not
+beginning with "[" is treated as informational, greatly simplifying the
+formatting of index.gph files.
+If a line begins with "t", this "t" is left out. This measurement is
+there to allow lines "informational" text beginning with "[".
+.
+.It Ic <desc>
+= description of gopher item. Most printable characters should work.
+.
+.It Ic <path>
+= full path to gopher item (base value is / ). Use the "Err" path for
+items not intended to be served.
+.
+.It Ic <host>
+= hostname or IP hosting the gopher item. Must be resolvable for the
+intended clients.
+.
+.It Ic <port>
+= TCP port number ( usually 70)
+.
+May be omitted if defaults are used.
+.El
+.
+.Ss index.gph Example
+A root.gph file for a server running on host=frog.bog, port=70. Note use
+of optional [i]nformational Item (line 2) for vertical space insertion:
+.Pp
+.Bd -literal -offset indent
+Welcome to Frog.bog
+[i||||]
+[0|About this server|about.txt|frog.bog|70]
+[0|Daily Log|/dtail.cgi|frog.bog|70]
+[1|Phlog: like a blog, but not|/PHLOG|frog.bog|70]
+[9|Some binary file|widget.exe|frog.bog|70]
+[I|Snowflake picture|snowflake.jpg|frog.bog|70]
+
+Links and Searches
+[1|Go to R-36.net|/|gopher.r-36.net|70]
+[h|Go to NetBSD.org|URL:http://netbsd.org|frog.bog|70]
+[7|Query US Weather by Zipcode|/weather.cgi?|frog.bog|70]
+[7|Search Veronica II|/v2/vs|gopher.floodgap.com|70]
+[8|Telnet to SDF Public Access Unix System||freeshell.org|23]
+.Ed
+.
+.Pp
+The above looks something like this in a text-based gopher client:
+.Pp
+.Bl -tag -width ".It Ic WIDTHS" -compact -offset indent
+.D1 Welcome to Frog.bog
+.Pp
+.It Ic (FILE)
+About this server
+.It Ic (FILE)
+Daily Log
+.It Ic (DIR)
+Phlog: like a blog, but not
+.It Ic (BIN)
+Some binary file
+.It Ic (IMG)
+Snowflake picture
+.El
+.Pp
+.Bl -tag -width ".It Ic WIDTHS" -compact -offset indent
+.D1 Links and Searches
+.It Ic (DIR)
+Go to R-36.net
+.It Ic (HTML)
+Go to NetBSD.org
+.It Ic (?)
+Query US Weather by Zipcode
+.It Ic (?)
+Search Veronica II
+.It Ic (TEL)
+Telnet to SDF Public Access Unix System
+.El
+.Pp
+.Sh USING DYNAMIC CONTENT
+Dynamic content can be generated under geomyidae by simply creating a file
+in for form of
+.Ic <name>.cgi
+in a directory that is being served. Such files are run as a shell script.
+(See below for description.)
+.Pp
+ex. dtail.cgi - prints daily log entries
+.
+.Bd -literal -offset indent
+#!/bin/sh -e
+echo "Logged activity for `date '+%A, %B %d, %Y'` :"
+echo "==============================================="
+LOG="/var/log/gopherd.log"
+DATE=`date "+%a %b %d"`
+/usr/bin/grep "$DATE" $LOG
+exit 0
+.Ed
+.
+.Pp
+Geomyidae supports two variable queries. The basic form is
+.Pp
+.D1 executable.cgi{argv[1]}?{argv[2]}
+.Pp
+where
+.Pp
+.D1 argv[1] = strings (everything before the '?' in the query)
+.D1 argv[2] = arguments (everything behind the '?' in the query)
+.Pp
+A search query request must have an item Type of "7" to be called
+from an index.gph file. It may also need a "?" suffix in the <path>
+field.
+.
+.Pp
+ex. hello.cgi - say hello to user
+.
+.Bd -literal -offset indent
+#!/bin/sh
+NAME=$1
+echo ""
+echo Hello $NAME - welcome to Frog.bog
+exit 0
+.Ed
+.
+.Pp
+Call the above with the following index.gph entry:
+.Pp
+.D1 [7|Hello You - Please enter your name|/hello.cgi?|frog.bog|70]
+.
+.Pp
+And do a simple
+.Xr snarf 1
+query:
+.Pp
+.D1 % snarf Qo gopher://frog.bog/7/hello.cgi?Christoph Qc -
+.D1 Hello Christoph - welcome to Frog.bog
+.Dl %
+.
+.Sh LOG FILES
+.Pp
+The log file (/var/log/gopherd.log is default) has the following structure:
+.
+.Pp
+.Ic [<date>|<IP:port>] <item path> <query> (<status>)
+.
+.Pp
+where,
+.
+.Bl -inset
+.It Ic <date>
+= access date and time (std 'date' format)
+.Bl -inset -offset indent
+ex.
+.Qq "Sun Feb 17 06:11:10 PST 2008"
+.El
+.It Ic <IP:port>
+= client IP address and port served
+.Bl -inset -offset indent
+ex.
+.Qq "24.208.18.127:16857"
+.El
+.Pp
+.It Ic <item path>
+= full path to item served
+.Bl -inset -offset indent
+ex.
+.D1 Qo "/PICS/simple2.jpg" Qc for an image file
+.D1 Qo "/PICS" Qc for a directory access
+.El
+.It Ic <query>
+= query term submitted (Type 7 requests only)
+.Bl -inset -offset indent
+ex.
+.Dl % snarf Qq "gopher://frog.bog/7/hello.cgi?Christoph"
+.Dl would log Qo "Christoph" Qc as the query term.
+.El
+.It Ic (<status>)
+= status of client request
+.Bl -inset -offset indent
+ex. - some common status entries:
+.El
+.Pp
+.Bl -hang -width XXXXXXXXXXXXXXXX -compact -offset XXXXXXXXXXXX
+.It Qo (serving) Qc
+=> a successful request
+.It Qo (not found) Qc
+=> an unsuccessful request
+.It Qo (HTTP redirect) Qc
+=> web link redirect (Type h)
+.It Qo (dir listing) Qc
+=> unindexed directory listing
+.El
+.El
+.
+.Sh FILES
+README, LICENSE, index.gph
+.
+.Sh "SEE ALSO"
+Links for further information on gopher:
+.Pp
+.D1 Pa gopher://gopher.gopherproject.org
+.D1 Pa http://www.gopherproject.org
+.Pp
+.Sh STANDARDS
+.Em Internet RFC 1436
+.
+.Sh HISTORY
+Geomyidae started as a Linux/BSD port of the Plan 9 gopherd_P9 server.
+Originally called gopherd_BSD, the name was later changed to Geomyidae
+(latin), the taxonomic family of burrowing rodents known as "pocket
+gophers" which are in fact the true gophers. Because of inconsitencies
+and the UNIX culture, the name was changed to lowercase in 2010.
+.
+.Sh AUTHORS
+See LICENSE file for authors in the distribution.
+.
+.Sh LICENSE
+Geomyidae is released under the MIT/X Consortium License.
+.
+.Sh BUGS
+Geomyidae occasionally aborts silently if too many simultaneous
+requests are made. Limiting gopher traffic via firewall rules
+may help.
+.Pp
+Dynamic content functionality may vary across gopher clients.
+.
+.Ss "Reporting Bugs"
+Report bugs to:
+.An "Christoph Lohmann" Aq [email protected]
diff --git a/handlr.c b/handlr.c
@@ -0,0 +1,196 @@
+/*
+ * Copy me if you can.
+ * by 20h
+ */
+
+#include <unistd.h>
+#include <memory.h>
+#include <netdb.h>
+#include <netinet/in.h>
+#include <fcntl.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/socket.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <dirent.h>
+#include "ind.h"
+#include "arg.h"
+
+void
+handledir(int sock, char *path, char *port, char *base, char *args,
+ char *sear)
+{
+ char *pa, *file, *e, *addr, *par, *b;
+ struct dirent **dirent;
+ int ndir, i;
+ struct stat st;
+ filetype *type;
+
+ USED(sear);
+ addr = nil;
+
+ pa = gstrdup(path);
+ e = strrchr(pa, '/');
+ if(e != nil) {
+ *e = '\0';
+
+ if(args == nil) {
+ addr = gmallocz(512, 2);
+ if(gethostname(addr, 512) == -1) {
+ perror("gethostname");
+ close(sock);
+ free(addr);
+ free(pa);
+ return;
+ }
+ } else
+ addr = gstrdup(args);
+
+ par = gstrdup(pa);
+ b = strrchr(par + strlen(base), '/');
+ if(b != nil) {
+ *b = '\0';
+ tprintf(sock, "1..\t%s\t%s\t%s\r\n",
+ par + strlen(base), addr, port);
+ }
+ free(par);
+
+ ndir = scandir(pa, &dirent, 0, alphasort);
+ if(ndir < 0) {
+ perror("scandir");
+ close(sock);
+ free(addr);
+ free(pa);
+ return;
+ } else {
+ for(i = 0; i < ndir; i++) {
+ if(dirent[i]->d_name[0] == '.') {
+ free(dirent[i]);
+ continue;
+ }
+
+ type = gettype(dirent[i]->d_name);
+ file = smprintf("%s/%s", pa,
+ dirent[i]->d_name);
+ if(stat(file, &st) >= 0 && S_ISDIR(st.st_mode))
+ type = gettype("index.gph");
+ e = file + strlen(base);
+ tprintf(sock, "%c%s\t%s\t%s\t%s\r\n", *type->t…
+ dirent[i]->d_name, e, addr, port);
+ free(file);
+ free(dirent[i]);
+ }
+ free(dirent);
+ }
+ tprintf(sock, "\r\n");
+ }
+
+ if(addr != nil)
+ free(addr);
+ free(pa);
+ close(sock);
+ return;
+}
+
+void
+handlegph(int sock, char *file, char *port, char *base, char *args,
+ char *sear)
+{
+ Indexs *act;
+ int i;
+ char addr[512];
+
+ USED(base);
+ USED(args);
+ USED(sear);
+
+ act = scanfile(file);
+ if(act != nil) {
+ if(args == nil) {
+ if(gethostname(addr, sizeof(addr)) == -1) {
+ perror("gethostname");
+ close(sock);
+ return;
+ }
+ } else
+ snprintf(addr, sizeof(addr), "%s", args);
+
+
+ for(i = 0; i < act->num; i++) {
+ if(!strncmp(act->n[i]->e[3], "server", 6)) {
+ free(act->n[i]->e[3]);
+ act->n[i]->e[3] = gstrdup(addr);
+ }
+ if(!strncmp(act->n[i]->e[4], "port", 4)) {
+ free(act->n[i]->e[4]);
+ act->n[i]->e[4] = gstrdup(port);
+ }
+ tprintf(sock, "%.1s%s\t%s\t%s\t%s\r\n",
+ act->n[i]->e[0], act->n[i]->e[1],
+ act->n[i]->e[2], act->n[i]->e[3],
+ act->n[i]->e[4]);
+
+ freeelem(act->n[i]);
+ act->n[i] = nil;
+ }
+ tprintf(sock, "\r\n.\r\n\r\n");
+
+ freeindex(act);
+ }
+
+ close(sock);
+ return;
+}
+
+void
+handlebin(int sock, char *file, char *port, char *base, char *args,
+ char *sear)
+{
+ char sendb[1024];
+ int len, fd;
+
+ len = -1;
+ USED(port);
+ USED(base);
+ USED(args);
+ USED(sear);
+
+ fd = open(file, O_RDONLY);
+ if(fd >= 0) {
+ while((len = read(fd, sendb, sizeof(sendb))) > 0)
+ send(sock, sendb, len, 0);
+ close(fd);
+ }
+
+ close(sock);
+ return;
+}
+
+void
+handlecgi(int sock, char *file, char *port, char *base, char *args,
+ char *sear)
+{
+ char *p;
+
+ USED(port);
+ USED(base);
+
+ p = strrchr(file, '/');
+ if(p == nil)
+ p = file;
+
+ dup2(sock, 1);
+ dup2(sock, 0);
+ dup2(sock, 2);
+
+ if(sear == nil)
+ sear = "";
+
+ execl(file, p, sear, args, nil);
+
+ close(sock);
+ return;
+}
+
diff --git a/handlr.h b/handlr.h
@@ -0,0 +1,20 @@
+/*
+ * Copy me if you can.
+ * by 20h
+ */
+
+#ifndef HANDLR_H
+#define HANDLR_H
+
+#define nil NULL
+
+void handledir(int sock, char *path, char *port, char *base, char *args,
+ char *sear);
+void handlegph(int sock, char *file, char *port, char *base, char *args,
+ char *sear);
+void handlebin(int sock, char *file, char *port, char *base, char *args,
+ char *sear);
+void handlecgi(int sock, char *file, char *port, char *base, char *args,
+ char *sear);
+
+#endif
diff --git a/ind.c b/ind.c
@@ -0,0 +1,287 @@
+/*
+ * Copy me if you can.
+ * by 20h
+ */
+
+#include <unistd.h>
+#include <stdarg.h>
+#include <string.h>
+#include <memory.h>
+#include <fcntl.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include "ind.h"
+#include "handlr.h"
+
+filetype type[] = {
+ {"default", "0", handlebin},
+ {"gph", "1", handlegph},
+ {"cgi", "0", handlecgi},
+ {"bin", "9", handlebin},
+ {"tgz", "9", handlebin},
+ {"gz", "9", handlebin},
+ {"jpg", "I", handlebin},
+ {"gif", "g", handlebin},
+ {"png", "I", handlebin},
+ {"bmp", "I", handlebin},
+ {"txt", "0", handlebin},
+ {"html", "0", handlebin},
+ {"htm", "0", handlebin},
+ {"xhtml", "0", handlebin},
+ {"css", "0", handlebin},
+ {nil, nil, nil},
+};
+
+filetype *
+gettype(char *filename)
+{
+ char *end;
+ int i;
+
+ end = strrchr(filename, '.');
+ if(end == nil)
+ return &type[0];
+ end++;
+
+ for(i = 0; type[i].end != nil; i++)
+ if(!strncasecmp(end, type[i].end, strlen(type[i].end)))
+ return &type[i];
+
+ return &type[0];
+}
+
+void *
+gmallocz(int l, int d)
+{
+ char *ret;
+
+ ret = malloc(l);
+ if(ret == nil) {
+ perror("malloc");
+ exit(1);
+ }
+
+ if(d)
+ memset(ret, 0, l);
+
+ return (void *)ret;
+}
+
+char *
+gstrdup(char *str)
+{
+ char *ret;
+
+ ret = strdup(str);
+ if(ret == nil) {
+ perror("strdup");
+ exit(1);
+ }
+
+ return ret;
+}
+
+char *
+readln(int fd)
+{
+ char *ret;
+ int len;
+
+ len = 1;
+
+ ret = malloc(2);
+ while(read(fd, &ret[len - 1], 1) > 0 && ret[len - 1] != '\n')
+ ret = realloc(ret, ++len + 1);
+ if(ret[len - 1] != '\n') {
+ free(ret);
+ return nil;
+ }
+ ret[len - 1] = '\0';
+
+ return ret;
+}
+
+void
+freeelem(Elems *e)
+{
+
+ if(e != nil) {
+ if(e->e != nil) {
+ for(;e->num > 0; e->num--)
+ if(e->e[e->num - 1] != nil)
+ free(e->e[e->num - 1]);
+ free(e->e);
+ }
+ free(e);
+ }
+ return;
+}
+
+void
+freeindex(Indexs *i)
+{
+
+ if(i != nil) {
+ if(i->n != nil) {
+ for(;i->num > 0; i->num--)
+ if(i->n[i->num - 1] != nil)
+ freeelem(i->n[i->num - 1]);
+ free(i->n);
+ }
+ free(i);
+ }
+
+ return;
+}
+
+void
+addelem(Elems *e, char *s)
+{
+
+ e->num++;
+ e->e = realloc(e->e, sizeof(char *) * e->num);
+ e->e[e->num - 1] = gmallocz(strlen(s) + 1, 0);
+ strcpy(e->e[e->num - 1], s);
+
+ return;
+}
+
+Elems *
+getadv(char *str)
+{
+ char *b, *e;
+ Elems *ret;
+
+ ret = gmallocz(sizeof(Elems), 2);
+ if(*str != '[') {
+ b = str;
+ if(*str == 't')
+ b++;
+ addelem(ret, "i");
+ addelem(ret, b);
+ addelem(ret, "Err");
+ addelem(ret, "server");
+ addelem(ret, "port");
+
+ return ret;
+ }
+
+ b = str + 1;
+ while((e = strchr(b, '|')) != nil) {
+ *e = '\0';
+ e++;
+ addelem(ret, b);
+ b = e;
+ }
+
+ e = strchr(b, ']');
+ if(e != nil) {
+ *e = '\0';
+ addelem(ret, b);
+ }
+ if(ret->e == nil) {
+ free(ret);
+ return nil;
+ }
+
+ return ret;
+}
+
+Indexs *
+scanfile(char *fname)
+{
+ char *ln;
+ int fd;
+ Indexs *ret;
+ Elems *el;
+
+ fd = open(fname, O_RDONLY);
+ if(fd < 0)
+ return nil;
+
+ ret = gmallocz(sizeof(Indexs), 2);
+
+ while((ln = readln(fd)) != nil) {
+ el = getadv(ln);
+ free(ln);
+ if(el == nil)
+ continue;
+
+ ret->num++;
+ ret->n = realloc(ret->n, sizeof(Elems) * ret->num);
+ ret->n[ret->num - 1] = el;
+ el = nil;
+ }
+ close(fd);
+
+ if(ret->n == nil) {
+ free(ret);
+ return nil;
+ }
+
+ return ret;
+}
+
+void
+tprintf(int fd, char *fmt, ...)
+{
+ va_list fmtargs;
+ int fd2;
+ FILE *fp;
+
+ fd2 = dup(fd);
+ fp = fdopen(fd2, "w");
+ if(fp == nil) {
+ perror("fdopen");
+ return;
+ }
+
+ va_start(fmtargs, fmt);
+ vfprintf(fp, fmt, fmtargs);
+ va_end(fmtargs);
+
+ fclose(fp);
+
+ return;
+}
+
+int
+initlogging(char *logf)
+{
+ int fd;
+
+ fd = open(logf, O_APPEND | O_WRONLY | O_CREAT, 0644);
+
+ return fd;
+}
+
+void
+stoplogging(int fd)
+{
+
+ close(fd);
+
+ return;
+}
+
+char *
+smprintf(char *fmt, ...)
+{
+ va_list fmtargs;
+ char *ret;
+ int size;
+
+ ret = "";
+
+ va_start(fmtargs, fmt);
+ size = vsnprintf(ret, 0, fmt, fmtargs);
+ va_end(fmtargs);
+
+ ret = gmallocz(++size, 2);
+ va_start(fmtargs, fmt);
+ vsnprintf(ret, size, fmt, fmtargs);
+ va_end(fmtargs);
+
+ return ret;
+}
+
diff --git a/ind.h b/ind.h
@@ -0,0 +1,48 @@
+/*
+ * Copy me if you can.
+ * by 20h
+ */
+
+#ifndef IND_H
+#define IND_H
+
+#include <stdio.h>
+#define nil NULL
+
+extern int glfd;
+
+typedef struct Elems Elems;
+struct Elems {
+ char **e;
+ int num;
+};
+
+typedef struct Indexs Indexs;
+struct Indexs {
+ Elems **n;
+ int num;
+};
+
+typedef struct filetype filetype;
+struct filetype {
+ char *end;
+ char *type;
+ void (* f)(int, char *, char *, char *, char *, char *);
+};
+
+filetype *gettype(char *filename);
+void *gmallocz(int l, int d);
+char *gstrdup(char *str);
+Indexs *scanfile(char *fname);
+Elems *getadv(char *str);
+void addelem(Elems *e, char *s);
+void freeindex(Indexs *i);
+void freeelem(Elems *e);
+char *readln(int fd);
+void tprintf(int fd, char *fmt, ...);
+int initlogging(char *logf);
+void stoplogging(int fd);
+char *smprintf(char *fmt, ...);
+
+#endif
+
diff --git a/index.gph b/index.gph
@@ -0,0 +1,6 @@
+comment
+tcomment
+[1|R-36|/|server|port]
+[0|file - comment|/file.dat|server|port]
+[h|http://www.heise.de|URL:http://www.heise.de|server|port]
+
diff --git a/main.c b/main.c
@@ -0,0 +1,385 @@
+/*
+ * Copy me if you can.
+ * by 20h
+ */
+
+#include <unistd.h>
+#include <dirent.h>
+#include <memory.h>
+#include <netdb.h>
+#include <netinet/in.h>
+#include <fcntl.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <sys/socket.h>
+#include <sys/stat.h>
+#include <sys/wait.h>
+#include <sys/types.h>
+#include <signal.h>
+#include <time.h>
+#include <pwd.h>
+#include <grp.h>
+#include "ind.h"
+#include "handlr.h"
+#include "arg.h"
+
+enum {
+ NOLOG = 0,
+ FILES = 1,
+ DIRS = 2,
+ HTTP = 4,
+ ERRORS = 8,
+};
+
+int glfd = -1;
+int loglvl = 15;
+
+char *argv0;
+char *stdbase = "/var/gopher";
+char *stdport = "70";
+char *indexf = "/index.gph";
+char *err = "0Sorry, but the requested token could not be found\tErr"
+ "\tlocalhost\t70\r\n.\r\n\r\n";
+char *htredir = "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n"
+ "<!DOCTYPE html PUBLIC \"-//W3C//DTD XHTML 1.0 Transitional//E…
+ " \"DTD/xhtml-transitional.dtd\">\n"
+ "<html xmlns=\"http://www.w3.org/1999/xhtml\" lang=\"en\">\n"
+ " <head>\n"
+ " <title>gopher redirect</title>\n"
+ "\n"
+ " <meta http-equiv=\"Refresh\" content=\"1;url=%s\" />\n"
+ " </head>\n"
+ " <body>\n"
+ " This page is for redirecting you to: <a href=\"%s\">%s</a…
+ " </body>\n"
+ "</html>\n";
+
+int
+dropprivileges(struct group *gr, struct passwd *pw)
+{
+
+ if(gr != nil)
+ if(setgroups(1, &gr->gr_gid) != 0 || setgid(gr->gr_gid) != 0)
+ return -1;
+ if(pw != nil) {
+ if(gr == nil) {
+ if(setgroups(1, &pw->pw_gid) != 0 ||
+ setgid(pw->pw_gid) != 0)
+ return -1;
+ }
+ if(setuid(pw->pw_uid) != 0)
+ return -1;
+ }
+
+ return 0;
+}
+
+char *
+securepath(char *p, int len)
+{
+ int i;
+
+ if(len < 2)
+ return p;
+
+ for(i = 1; i < strlen(p); i++) {
+ if(p[i - 1] == '.' && p[i] == '.') {
+ if(p[i - 2] == '/')
+ p[i] = '/';
+ if(p[i + 1] == '/')
+ p[i] = '/';
+ if(len == 2)
+ p[i] = '/';
+ }
+ }
+
+ return p;
+}
+
+void
+logentry(char *host, char *port, char *qry, char *status)
+{
+ time_t tim;
+ struct tm *ptr;
+ char timstr[128];
+
+ if(glfd >= 0) {
+ tim = time(0);
+ ptr = localtime(&tim);
+
+ strftime(timstr, sizeof(timstr), "%a %b %d %H:%M:%S %Z %Y",
+ ptr);
+
+ tprintf(glfd, "[%s|%s:%s] %s (%s)\n",
+ timstr, host, port, qry, status);
+ }
+
+ return;
+}
+
+void
+handlerequest(int sock, char *base, char *ohost, char *port, char *clienth,
+ char *clientp)
+{
+ struct stat dir;
+ char recvc[1024], recvb[1024], path[1024], *args, *sear, *c;
+ int len, fd;
+ filetype *type;
+
+ memset(&dir, 0, sizeof(dir));
+ memset(recvb, 0, sizeof(recvb));
+ memset(recvc, 0, sizeof(recvc));
+
+ len = recv(sock, recvb, sizeof(recvb), 0);
+ if(len > 1) {
+ if(recvb[len - 2] == '\r')
+ recvb[len - 2] = '\0';
+ if(recvb[len - 1] == '\n')
+ recvb[len - 1] = '\0';
+ }
+ strcpy(recvc, recvb);
+
+ if(!strncmp(recvb, "URL:", 4)) {
+ len = snprintf(path, sizeof(path), htredir,
+ recvb + 4, recvb + 4, recvb + 4);
+ if(len > sizeof(path))
+ len = sizeof(path);
+ send(sock, path, len, 0);
+ if(loglvl & HTTP)
+ logentry(clienth, clientp, recvc, "HTTP redirect");
+ return;
+ }
+
+ sear = strchr(recvb, '\t');
+ if(sear != nil)
+ *sear++ = '\0';
+ args = strchr(recvb, '?');
+ if(args != nil)
+ *args++ = '\0';
+ else
+ args = ohost;
+
+ securepath(recvb, len - 2);
+ snprintf(path, sizeof(path), "%s%s", base, recvb);
+ if(stat(path, &dir) != -1 && S_ISDIR(dir.st_mode))
+ strncat(path, indexf, sizeof(path) - strlen(path));
+
+ fd = open(path, O_RDONLY);
+ if(fd >= 0) {
+ close(fd);
+ if(loglvl & FILES)
+ logentry(clienth, clientp, recvc, "serving");
+
+ c = strrchr(path, '/');
+ if(c == nil)
+ c = path;
+ type = gettype(c);
+ type->f(sock, path, port, base, args, sear);
+ } else {
+ if(S_ISDIR(dir.st_mode)) {
+ handledir(sock, path, port, base, args, sear);
+ if(loglvl & DIRS)
+ logentry(clienth, clientp, recvc,
+ "dir listing");
+ return;
+ }
+
+ send(sock, err, strlen(err), 0);
+ if(loglvl & ERRORS)
+ logentry(clienth, clientp, recvc, "not found");
+ close(sock);
+ }
+
+ return;
+}
+
+void
+hndlsigchld(int signo)
+{
+ int status;
+
+ while(waitpid(-1, &status, WNOHANG) > 0);
+
+ return;
+}
+
+void
+usage(void)
+{
+
+ tprintf(2, "usage: %s [-d] [-l logfile] [-v loglvl] [-b base]"
+ " [-p port] [-o sport] [-u user] [-g group] [-h host]"
+ " [-i IP]\n",
+ argv0);
+
+ exit(1);
+}
+
+int
+main(int argc, char *argv[])
+{
+ struct addrinfo hints, *ai;
+ struct sockaddr_storage clt;
+ socklen_t cltlen;
+ int sock, list, opt, dofork;
+ char *port, *base, *logfile, clienth[NI_MAXHOST], clientp[NI_MAXSERV];
+ char *user, *group, *bindip, *ohost, *sport;
+ struct passwd *us;
+ struct group *gr;
+
+ base = stdbase;
+ port = stdport;
+ dofork = 1;
+ logfile = nil;
+ user = nil;
+ group = nil;
+ us = nil;
+ gr = nil;
+ bindip = nil;
+ ohost = nil;
+ sport = port;
+
+ ARGBEGIN {
+ case 'b':
+ base = EARGF(usage());
+ break;
+ case 'p':
+ port = EARGF(usage());
+ break;
+ case 'l':
+ logfile = EARGF(usage());
+ break;
+ case 'd':
+ dofork = 0;
+ break;
+ case 'v':
+ loglvl = atoi(EARGF(usage()));
+ break;
+ case 'u':
+ user = EARGF(usage());
+ break;
+ case 'g':
+ group = EARGF(usage());
+ break;
+ case 'i':
+ bindip = EARGF(usage());
+ break;
+ case 'h':
+ ohost = EARGF(usage());
+ break;
+ case 'o':
+ sport = EARGF(usage());
+ break;
+ default:
+ usage();
+ } ARGEND;
+
+ if(group != nil) {
+ if((gr = getgrnam(group)) == nil) {
+ perror("no such group");
+ return 1;
+ }
+ }
+
+ if(user != nil) {
+ if((us = getpwnam(user)) == nil) {
+ perror("no such user");
+ return 1;
+ }
+ }
+
+ if(dofork && fork() != 0)
+ return 0;
+
+ if(logfile != nil) {
+ glfd = initlogging(logfile);
+ if(glfd < 0) {
+ perror("initlogging");
+ return 1;
+ }
+ }
+
+ memset(&hints, 0, sizeof(hints));
+ hints.ai_flags |= AI_PASSIVE;
+ hints.ai_socktype = SOCK_STREAM;
+ hints.ai_protocol = IPPROTO_TCP;
+ hints.ai_family = AF_INET;
+ if(getaddrinfo(bindip, port, &hints, &ai)) {
+ perror("getaddrinfo");
+ return 1;
+ }
+ if(ai == nil) {
+ perror("getaddrinfo");
+ return 1;
+ }
+
+ list = socket(ai->ai_family, ai->ai_socktype, ai->ai_protocol);
+ if(list < 0) {
+ perror("socket");
+ return 1;
+ }
+
+ opt = 1;
+ if(setsockopt(list, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt))) {
+ perror("setsockopt");
+ return 1;
+ }
+
+ if(bind(list, ai->ai_addr, ai->ai_addrlen)) {
+ perror("bind");
+ return 1;
+ }
+
+ if(listen(list, 255)) {
+ perror("listen");
+ return 1;
+ }
+
+ freeaddrinfo(ai);
+
+ if(dropprivileges(gr, us) < 0) {
+ perror("cannot drop privileges");
+ return 1;
+ }
+
+ if(dofork) {
+ signal(SIGINT, SIG_IGN);
+ signal(SIGQUIT, SIG_IGN);
+ }
+ signal(SIGCHLD, hndlsigchld);
+
+ cltlen = sizeof(clt);
+ for(;;) {
+ sock = accept(list, (struct sockaddr *)&clt, &cltlen);
+ if(sock < 0) {
+ perror("accept");
+ close(list);
+ return 1;
+ }
+
+ getnameinfo((struct sockaddr *)&clt, cltlen, clienth,
+ sizeof(clienth), clientp, sizeof(clientp),
+ NI_NUMERICHOST);
+
+ switch(fork()) {
+ case -1:
+ perror("fork");
+ close(sock);
+ break;
+ case 0:
+ handlerequest(sock, base, ohost, sport, clienth,
+ clientp);
+ return 1;
+ default:
+ wait(&opt);
+ close(sock);
+ break;
+ }
+ }
+
+ close(list);
+ if(logfile != nil)
+ stoplogging(glfd);
+ return 0;
+}
+
diff --git a/rc.d/Archlinux.conf.d b/rc.d/Archlinux.conf.d
@@ -0,0 +1,4 @@
+#
+# Parameters to be passed to geomyidae
+#
+GEOMYIDAE_ARGS="-u nobody -g nobody -b /srv/gopher -o 70 -l /var/log/geomyidae…
diff --git a/rc.d/Archlinux.rc.d b/rc.d/Archlinux.rc.d
@@ -0,0 +1,38 @@
+#!/bin/bash
+
+. /etc/rc.conf
+. /etc/rc.d/functions
+. /etc/conf.d/geomyidae
+
+PID=`pidof -o %PPID /usr/bin/geomyidae`
+case "$1" in
+ start)
+ stat_busy "Starting geomyidae"
+ [ -z "$PID" ] && /usr/bin/geomyidae $GEOMYIDAE_ARGS 2>&1
+ if [ $? -gt 0 ]; then
+ stat_fail
+ else
+ PID=`pidof -o %PPID /usr/bin/geomyidae`
+ echo $PID >/var/run/geomyidae.pid
+ add_daemon geomyidae
+ stat_done
+ fi
+ ;;
+ stop)
+ stat_busy "Stopping geomyidae"
+ [ ! -z "$PID" ] && kill $PID &>/dev/null
+ if [ $? -gt 0 ]; then
+ stat_fail
+ else
+ rm_daemon geomyidae
+ stat_done
+ fi
+ ;;
+ restart)
+ $0 stop
+ $0 start
+ ;;
+ *)
+ echo "usage: $0 {start|stop|restart}"
+esac
+exit 0
diff --git a/rc.d/Gentoo.conf.d b/rc.d/Gentoo.conf.d
@@ -0,0 +1,5 @@
+#
+# Parameters to be passed to geomyidae
+#
+GEOMYIDAE_ARGS="-u gopherd -g gopherd -b /var/gopher -o 70 -l /var/log/geomyid…
+
diff --git a/rc.d/Gentoo.init.d b/rc.d/Gentoo.init.d
@@ -0,0 +1,17 @@
+#!/sbin/runscript
+# Copyright 1999-2010 Gentoo Foundation
+# Distributed under the terms of the GNU General Public License v2
+# $Header: $
+
+start(){
+ ebegin "Starting geomyidae"
+ [ -n "$GEOMYIDAE_ARGS" ] && GEOMYIDAE_ARGS="-- $GEOMYIDAE_ARGS"
+ start-stop-daemon --start --pidfile /var/run/geomyidae.pid --exec /usr/sbi…
+ eend $? "Failed to start geomyidae"
+}
+
+stop(){
+ ebegin "Stopping geomyidae"
+ start-stop-daemon --stop --pidfile /var/run/geomyidae.pid
+ eend $? "Failed to stop geomyidae"
+}
diff --git a/rc.d/NetBSD.rc.d b/rc.d/NetBSD.rc.d
@@ -0,0 +1,55 @@
+#!/bin/sh
+#
+
+# REQUIRE: local
+# PROVIDE: geomyidae
+
+$_rc_subr_loaded . /etc/rc.subr
+
+name="geomyidae"
+rcvar=$name
+command="/usr/pkg/sbin/${name}"
+
+#####################################################
+# Geomyidae Options Section - "?" => geomyidae(8) #
+# Uncomment & define options (defaults are shown) #
+#####################################################
+#
+#LOGFILE="-l /var/log/gopherd.log"
+#LOGLEVEL="-v 15"
+#HTDOCS="-b /var/gopher"
+#PORT="-p 70"
+#SPORT="-o 70"
+#USR="-u $USER"
+#GRP="-g $GROUP"
+#HOST="-h localhost"
+#IP="-i 127.0.0.1"
+
+######################################################
+# Now remove any UNDEFINED options from line below: #
+######################################################
+#
+command_args="$LOGFILE $LOGLEVEL $HTDOCS $PORT $SPORT $USR $GRP $HOST $IP"
+
+
+######################################################
+# Uncomment this section if a PID file is desired #
+######################################################
+
+#pidfile="/var/run/${name}.pid"
+#start_cmd="geomyidae_start"
+#
+#geomyidae_start()
+#{
+# echo "Starting $name"
+# $command $command_args
+# pgrep -x $name > $pidfile
+#}
+
+######################################################
+# Lastly, add the following to /etc/rc.conf: #
+# "geomyidae=YES" (without the quotes) #
+######################################################
+
+load_rc_config $name
+run_rc_command "$1"
diff --git a/rc.d/README b/rc.d/README
@@ -0,0 +1,5 @@
+These are init scripts, used in various distribution, for Geomyidae.
+
+All files are examples sent in by users. Please review them before
+using.
+
You are viewing proxied material from bitreich.org. The copyright of proxied material belongs to its original authors. Any comments or complaints in relation to proxied material should be directed to the original authors of the content concerned. Please see the disclaimer for more details.