chiark / gitweb /
Import release 0.03 v0.03
authorStephen Early <steve@greenend.org.uk>
Wed, 19 Sep 2001 23:24:00 +0000 (00:24 +0100)
committerStephen Early <steve@greenend.org.uk>
Wed, 18 May 2011 12:13:55 +0000 (13:13 +0100)
39 files changed:
INSTALL [new file with mode: 0644]
Makefile.in [new file with mode: 0644]
NOTES [new file with mode: 0644]
TODO [new file with mode: 0644]
conffile.c [new file with mode: 0644]
conffile.fl [new file with mode: 0644]
conffile.h [new file with mode: 0644]
conffile.y [new file with mode: 0644]
conffile_internal.h [new file with mode: 0644]
config.h.bot [new file with mode: 0644]
config.h.in [new file with mode: 0644]
config.h.top [new file with mode: 0644]
configure [new file with mode: 0755]
configure.in [new file with mode: 0644]
dh.c [new file with mode: 0644]
install.sh [new file with mode: 0755]
md5.c [new file with mode: 0644]
md5.h [new file with mode: 0644]
modules.c [new file with mode: 0644]
modules.h [new file with mode: 0644]
myrddin.pub [new file with mode: 0644]
netlink.c [new file with mode: 0644]
private-key [new file with mode: 0644]
random.c [new file with mode: 0644]
resolver.c [new file with mode: 0644]
rsa.c [new file with mode: 0644]
secnet.c [new file with mode: 0644]
secnet.h [new file with mode: 0644]
serpent.c [new file with mode: 0644]
serpent.h [new file with mode: 0644]
serpentsboxes.h [new file with mode: 0644]
site.c [new file with mode: 0644]
testconfig [new file with mode: 0644]
testconfigz [new file with mode: 0644]
testsites [new file with mode: 0644]
transform.c [new file with mode: 0644]
udp.c [new file with mode: 0644]
util.c [new file with mode: 0644]
util.h [new file with mode: 0644]

diff --git a/INSTALL b/INSTALL
new file mode 100644 (file)
index 0000000..88b1cdb
--- /dev/null
+++ b/INSTALL
@@ -0,0 +1,76 @@
+INSTALLATION INSTRUCTIONS for SECNET
+
+Ensure that you have libgmp2-dev and adns installed (and bison and
+flex, and for that matter gcc...).
+
+If you intend to configure secnet to obtain packets from the kernel
+through userv-ipif, install and configure userv-ipif. It is part of
+userv-utils, available from ftp.chiark.greenend.org.uk in
+/users/ian/userv
+
+Then, to install secnet do
+
+$ ./configure
+$ make
+# cp secnet /usr/local/sbin/secnet
+# mkdir /etc/secnet
+# cp example.conf /etc/secnet/secnet.conf
+# cd /etc/secnet
+# ssh-keygen -f key -N ""
+
+(When upgrading, just install the new /usr/local/sbin/secnet; keep
+your current configuration file.)
+
+If you intend to start secnet as root, I suggest you create an userid
+for it to run as once it's ready to drop its privileges. Example:
+# adduser --system --no-create-home secnet
+
+Generate a site file fragment for your site, and submit it for
+inclusion in the vpn-sites file. Download the vpn-sites file.
+
+* Constructing a site file fragment
+
+You need the following information:
+
+1.  a short name for your site, eg. "greenend". This is used to
+identify your site in the vpn-sites file.
+
+2.  the name your site will use in the key setup protocol,
+eg. "greenend" (these two will usually be similar or the same).
+
+3.  the DNS name of the machine that will be the "front-end" for your
+secnet installation. This will typically be the name of the gateway
+machine for your network, eg. sinister.dynamic.greenend.org.uk
+
+secnet does not actually have to run on this machine, as long as the
+machine can be configured to forward UDP packets to the machine that
+is running secnet.
+
+4.  the port number used to contact secnet at your site. This is the
+port number on the front-end machine, and does not necessarily have to
+match the port number on the machine running secnet.
+
+5.  the list of networks accessible at your site over the VPN.
+
+6.  the public part of the RSA key you generated during installation
+(in /etc/secnet/key.pub if you followed the installation
+instructions). This file contains three numbers and a comment on one
+line. The first number is the key length in bits, and can be
+ignored. The second number (typically small) is the encryption key
+'e', and the third number (large) is the modulus 'n'.
+
+If you are running secnet on a particularly slow machine, you may like
+to specify a larger value for the key setup retry timeout than the
+default, to prevent unnecessary retransmissions of key setup
+packets. See the notes in the example configuration file for more on
+this.
+
+The site file fragment should look something like this:
+
+shortname {
+               name "sitename";
+               address "your.public.address.org.uk";
+               port 5678;
+               networks "172.18.45.0/24";
+               key rsa-public("35","153279875126380522437827076871354104097683702803616313419670959273217685015951590424876274370401136371563604396779864283483623325238228723798087715987495590765759771552692972297669972616769731553560605291312242789575053620182470998166393580503400960149506261455420521811814445675652857085993458063584337404329");
+       };
diff --git a/Makefile.in b/Makefile.in
new file mode 100644 (file)
index 0000000..27b5636
--- /dev/null
@@ -0,0 +1,52 @@
+.DUMMY:        all clean realclean dist install
+
+PACKAGE:=secnet
+VERSION:=0.03
+
+@SET_MAKE@
+
+srcdir:=@srcdir@
+VPATH:=@srcdir@
+
+CFLAGS:=@CFLAGS@ @DEFS@ -DVERSION=\"$(VERSION)\" -Wall
+
+LDFLAGS:=@LDFLAGS@
+
+LDLIBS:=@LIBS@
+
+TARGETS:=secnet
+
+OBJECTS:=secnet.o util.o conffile.yy.o conffile.tab.o conffile.o modules.o \
+       resolver.o random.o udp.o site.o transform.o netlink.o rsa.o dh.o \
+       serpent.o md5.o
+
+%.c:   %.y
+
+%.yy.c:        %.fl
+       flex -o$@ $<
+
+%.tab.c:       %.y
+       bison -d $<
+
+RM:=@RM@
+
+all:   $(TARGETS)
+
+secnet:        $(OBJECTS)
+
+clean:
+       $(RM) -f $(srcdir)/*.o $(srcdir)/*~ $(srcdir)/*.yy.c \
+               $(srcdir)/*.tab.[ch]
+
+realclean:     clean
+       $(RM) -f $(TARGETS) $(srcdir)/Makefile $(srcdir)/config.h \
+       $(srcdir)/config.log $(srcdir)/config.status $(srcdir)/config.cache \
+       $(srcdir)/Makefile.bak core
+
+dist:  realclean
+       (cd .. ; ln -s $(PACKAGE) $(PACKAGE)-$(VERSION) ; tar hcf - \
+       $(PACKAGE)-$(VERSION) | \
+       gzip -9 > $(PACKAGE)-$(VERSION).tar.gz ; rm $(PACKAGE)-$(VERSION) )
+
+conffile.yy.c: conffile.fl conffile.tab.c
+conffile.tab.c:        conffile.y
diff --git a/NOTES b/NOTES
new file mode 100644 (file)
index 0000000..272e359
--- /dev/null
+++ b/NOTES
@@ -0,0 +1,194 @@
+#* Design of new, multi-subnet secnet protocol
+
+Like the first version, we're tunnelling IP packets inside UDP
+packets. To defeat various restrictions which may be imposed on us by
+network providers (like the prohibition of incoming TCP connections)
+we're sticking with UDP for everything this time, including key setup.
+
+Other new features include being able to deal with subnets hidden
+behind changing 'real' IP addresses, and the ability to choose
+algorithms and keys per pair of communicating sites.
+
+** Configuration and structure
+
+The network is made up from a number of 'sites'. These are collections
+of machines with private IP addresses. The new secnet code runs on
+machines which have interfaces on the private site network and some
+way of accessing the 'real' internet.
+
+Each end of a tunnel is identified by a name. Often it will be
+convenient for every gateway machine to use the same name for each
+tunnel endpoint, but this is not vital. Individual tunnels are
+identified by their two endpoint names.
+
+
+The configuration is held in memory as a data structure as follows:
+
+The root is a Dictionary. Dictionaries hold (key,value) pairs. Keys
+are atoms. Values are lists, dictionaries or closures. Lists can hold
+the following types: string, number.
+
+Closures cannot be constructed directly; they are added to the
+'default' dictionary before the configuration file is read. Invocation
+of a closure can return any type of value.
+
+
+Configuration file format: the file describes a dictionary.
+
+key value;
+
+value is item[,item...]
+
+item can be "string", number, path (looks up in dictionary),
+{dictionary}, value(value), value{dictionary}.  If item is a list it
+is copied into the list - we can't have lists of lists.
+
+A path is [/]key[\[index\]][/key[\[index\]]...], defining a lookup
+from the current dictionary (or parents) or the root. If a key refers
+to a list of more than one item then an index number (base 0) in
+square brackets can be used to specify the list item number.
+
+Items of the form value1(value2) invoke executable value1 with an
+argument of value2. The return value can be a string or dictionary,
+but not a list. (Invocation happens after the entire configuration
+file has been read.)
+
+Items of the form value{dict} invoke executable value with an argument
+of a single-element list, containing dict. It's just syntactic sugar
+for value({dict}).
+
+
+When a key is used (rather than defined) it is looked up in the
+current dictionary, and if it isn't found it is looked up in the
+(lexical) parent, until the root is reached. 
+
+
+
+
+What sorts of crypto-related things do we need to define?
+
+sources of randomness
+block algorithms
+block cipher modes?
+hash functions
+padding functions
+public key signature algorithms
+public key crypto key stores
+key setup algorithms
+
+
+** Protocols
+
+*** Protocol environment:
+
+Each gateway machine serves a particular, well-known set of private IP
+addresses (i.e. the agreement over which addresses it serves is
+outside the scope of this discussion). Each gateway machine has an IP
+address on the interconnecting network (usually the Internet), which
+may be dynamically allocated and may change at any point.
+
+Each gateway knows the RSA public keys of the other gateways with
+which it wishes to communicate. The mechanism by which this happens is
+outside the scope of this discussion. There exists a means by which
+each gateway can look up the probable IP address of any other.
+
+*** Protocol goals:
+
+The ultimate goal of the protocol is for the originating gateway
+machine to be able to forward packets from its section of the private
+network to the appropriate gateway machine for the destination
+machine, in such a way that it can be sure that the packets are being
+sent to the correct destination machine, the destination machine can
+be sure that the source of the packets is the originating gateway
+machine, and the contents of the packets cannot be understood other
+than by the two communicating gateways.
+
+XXX not sure about the address-change stuff; leave it out of the first
+version of the protocol. From experience, IP addresses seem to be
+quite stable so the feature doesn't gain us much.
+
+**** Protocol sub-goal 1: establish a shared key
+
+Definitions:
+
+A is the originating gateway machine
+B is the destination gateway machine
+PK_A is the public RSA key of A
+PK_B is the public RSA key of B
+PK_A^-1 is the private RSA key of A
+PK_B^-1 is the private RSA key of B
+x is the fresh private DH key of A
+y is the fresh private DH key of B
+k is g^xy mod m
+g and m are generator and modulus for Diffie-Hellman
+nA is a nonce generated by A
+nB is a nonce generated by B
+iA is an index generated by A, to be used in packets sent from B to A
+iB is an index generated by B, to be used in packets sent from A to B
+i? is appropriate index for receiver
+
+Note that 'i' may be re-used from one session to the next, whereas 'n'
+is always fresh.
+
+Messages:
+
+1) A->B: *,iA,msg1,A,B,nA
+
+2) B->A: iA,iB,msg2,B,A,nB,nA
+
+(The order of B and A reverses in alternate messages so that the same
+code can be used to construct them...)
+
+3) A->B: {iB,iA,msg3,A,B,nA,nB,g^x mod m}_PK_A^-1
+
+If message 1 was a replay then A will not generate message 3, because
+it doesn't recognise nA.
+
+If message 2 was from an attacker then B will not generate message 4,
+because it doesn't recognise nB.
+
+4) B->A: {iA,iB,msg4,B,A,nB,nA,g^y mod m}_PK_B^-1
+
+At this point, A and B share a key, k. B must keep retransmitting
+message 4 until it receives a packet encrypted using key k.
+
+5) A: iB,iA,msg5,(ping/msg5)_k
+
+6) B: iA,iB,msg6,(pong/msg6)_k
+
+(Note that these are encrypted using the same transform that's used
+for normal traffic, so they include sequence number, MAC, etc.)
+
+The ping and pong messages can be used by either end of the tunnel at
+any time, but using msg0 as the unencrypted message type indicator.
+
+**** Protocol sub-goal 2: end the use of a shared key
+
+7) i?,i?,msg0,(end-session/msg7,A,B)_k
+
+This message can be sent by either party. Once sent, k can be
+forgotten. Once received and checked, k can be forgotten. No need to
+retransmit or confirm reception. It is suggested that this message be
+sent when a key times out, or the tunnel is forcibly terminated for
+some reason.
+
+8) i?,i?,NAK/msg8
+
+If the link-layer can't work out what to do with a packet (session has
+gone away, etc.) it can transmit a NAK back to the sender.  The sender
+can then try to verify whether the session is alive by sending ping
+packets, and forget the key if it isn't. Potential denial-of-service
+if the attacker can stop the ping/pong packets getting through (the
+key will be forgotten and another key setup must take place), but if
+they can delete packets then we've lost anyway...
+
+The attacker can of course forge NAKs since they aren't protected. But
+if they can only forge packets then they won't be able to stop the
+ping/pong working. Trust in NAKs can be rate-limited...
+
+Alternative idea: if you receive a packet you can't decode, because
+there's no key established, then initiate key setup...
+
+**** Protocol sub-goal 3: send a packet
+
+9) i?,i?,msg0,(send-packet/msg9,packet)_k
diff --git a/TODO b/TODO
new file mode 100644 (file)
index 0000000..b75c633
--- /dev/null
+++ b/TODO
@@ -0,0 +1,24 @@
+conffile.c: deal with line numbers from included conffiles correctly
+
+dh.c: change format to binary from decimal string
+
+netlink.c: initial implementation done, needs basic router functionality
+adding. Can wait. Also support tun device.
+
+random.c: test
+
+resolver.c: done
+
+rsa.c: check padding type, change format to binary from decimal string
+
+secnet.c: done
+
+site.c: the site_incoming() routing could be implemented much more
+cleanly using a table. There's still quite a lot of redundancy in this
+file.
+
+transform.c: done
+
+udp.c: done
+
+util.c: sort out logging
diff --git a/conffile.c b/conffile.c
new file mode 100644 (file)
index 0000000..746b654
--- /dev/null
@@ -0,0 +1,776 @@
+/*
+ * $Log$
+ */
+
+/* conffile.c - process the configuration file */
+
+/* #define DUMP_PARSE_TREE */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "secnet.h"
+#include "conffile.h"
+#include "conffile_internal.h"
+#include "util.h"
+#include "modules.h"
+
+static struct cloc no_loc={"none",0};
+
+struct atomlist {
+    struct atomlist *next;
+    atom_t a;
+};
+
+struct entry {
+    struct entry *next;
+    atom_t key;
+    list_t *val;
+};
+
+struct searchlist {
+    struct dict *d;
+    struct searchlist *next;
+};
+
+struct dict {
+    struct dict *parent;
+    struct searchlist *search;
+    struct entry *entries;
+    uint32_t size;
+};
+
+static struct atomlist *atoms=NULL;
+
+static void process_alist(dict_t *context, struct p_node *c);
+static list_t *process_invocation(dict_t *context, struct p_node *i);
+
+static list_t *dict_ilookup_primitive(dict_t *dict, atom_t key)
+{
+    struct entry *i;
+    for (i=dict->entries; i; i=i->next) {
+       if (key==i->key) return i->val;
+    }
+    return NULL;
+}
+
+static list_t *dict_ilookup(dict_t *dict, atom_t key)
+{
+    dict_t *d;
+    list_t *v;
+
+    v=dict_ilookup_primitive(dict, key);
+    if (v) return v;
+    /* Check dictionaries in search path */
+/* XXX */
+    /* Check lexical parents */
+    for (d=dict; d; d=d->parent) {
+       v=dict_ilookup_primitive(d, key);
+       if (v) return v;
+    }
+    return NULL;
+}
+
+static void dict_iadd(dict_t *dict, atom_t key, list_t *val)
+{
+    struct entry *e;
+    /* XXX May want to permit redefinition of keys in the future */
+    /* (although it could be very confusing) */
+    if (dict_ilookup_primitive(dict, key)) {
+       fatal("duplicate key \"%s\" in dictionary\n",key);
+    }
+    e=safe_malloc(sizeof(*e),"dict_add");
+    e->next=dict->entries;
+    e->key=key;
+    e->val=val;
+    dict->entries=e;
+    dict->size++;
+}
+
+/***** Functions beyond this point are private to the config system *****/
+
+static dict_t *dict_new(dict_t *parent)
+{
+    dict_t *d;
+
+    d=safe_malloc(sizeof(*d),"dict_new");
+    d->parent=parent;
+    d->search=NULL;
+    d->entries=NULL;
+    d->size=0;
+    return d;
+}
+
+static struct p_node *node_copy(struct p_node *n)
+{
+    struct p_node *r;
+    r=safe_malloc(sizeof(*r),"node_copy");
+    *r=*n;
+    return r;
+}
+
+static struct p_node *list_reverse(struct p_node *list)
+{
+    struct p_node *rl=NULL, *i, *n;
+
+    for (i=list; i; i=i->r) {
+       n=node_copy(i);
+       n->r=rl;
+       rl=n;
+    }
+    return rl;
+}
+
+/* Since we use left-recursion in the parser for efficiency, sequences
+   end up "backwards" in the parse tree. Rather than have complicated
+   code for, eg. processing assignments in the right order, we reverse
+   these sequences here. */
+static void ptree_mangle(struct p_node *t)
+{
+    if (!t) return;
+    ptree_mangle(t->l);
+    ptree_mangle(t->r);
+    switch (t->type) {
+    case T_DICT:
+       /* ASSERT !t->l || t->l->type==T_ALIST */
+       /* ASSERT !t->r || t->r->type==T_LISTITEM */
+       t->l=list_reverse(t->l);
+       t->r=list_reverse(t->r);
+       break;
+    case T_ASSIGNMENT:
+       /* ASSERT t->l->type==T_KEY */
+       /* ASSERT t->r->type==T_LISTITEM */
+       t->r=list_reverse(t->r);
+       break;
+    case T_ABSPATH:
+    case T_RELPATH:
+       /* ASSERT t->l==NULL */
+       /* ASSERT t->r->type==T_PATHELEM */
+       t->r=list_reverse(t->r);
+       break;
+    case T_EXEC:
+       /* ASSERT t->l */
+       /* ASSERT t->r->type==T_LISTITEM */
+       t->r=list_reverse(t->r);
+       break;
+    }
+}
+
+#ifdef DUMP_PARSE_TREE
+/* Convert a node type to a string, for parse tree dump */
+static string_t ntype(uint32_t type)
+{
+    switch(type) {
+    case T_STRING:     return "T_STRING";
+    case T_NUMBER:     return "T_NUMBER";
+    case T_KEY:        return "T_KEY";
+    case T_ASSIGNMENT: return "T_ASSIGNMENT";
+    case T_LISTITEM:   return "T_LISTITEM";
+    case T_EXEC:       return "T_EXEC";
+    case T_PATHELEM:   return "T_PATHELEM";
+    case T_ABSPATH:    return "T_ABSPATH";
+    case T_RELPATH:    return "T_RELPATH";
+    case T_DICT:       return "T_DICT";
+    case T_ALIST:      return "T_ALIST";
+    case T_ERROR:      return "T_ERROR";
+    }
+    return "**unknown**";
+}
+
+static void ptree_indent(uint32_t amount)
+{
+    uint32_t i;
+    for (i=0; i<amount; i++) printf("  . ");
+}
+
+static void ptree_dump(struct p_node *n, uint32_t d)
+{
+    if (!n) {
+       printf("NULL\n");
+       return;
+    }
+    
+    if (n->type<10) {
+       switch(n->type) {
+       case T_STRING: printf("T_STRING: \"%s\" (%s line %d)\n",
+                             n->data.string,n->loc.file,n->loc.line); break;
+       case T_NUMBER: printf("T_NUMBER: %d (%s line %d)\n",
+                             n->data.number, n->loc.file,n->loc.line); break;
+       case T_KEY:    printf("T_KEY:    %s (%s line %d)\n",
+                             n->data.key, n->loc.file,n->loc.line); break;
+       default:       printf("**unknown primitive type**\n"); break;
+       }
+    } else {
+       printf("%s: (%s line %d)\n",ntype(n->type),n->loc.file,n->loc.line);
+       ptree_indent(d);
+       printf("  |-"); ptree_dump(n->l, d+1);
+       ptree_indent(d);
+       printf("  +-"); ptree_dump(n->r, d+1);
+    }
+}
+
+#endif /* DUMP_PARSE_TREE */
+
+static dict_t *dict_find_root(dict_t *d)
+{
+    dict_t *i;
+
+    for (i=d; i->parent; i=i->parent);
+    return i;
+}
+
+static list_t *dict_lookup_path(dict_t *context, struct p_node *p)
+{
+    dict_t *i;
+    list_t *l;
+
+    /* ASSERT p->type==T_PATHELEM */
+    /* ASSERT p->l->type==T_KEY */
+    l=dict_ilookup(context, p->l->data.key);
+    if (!l) {
+       cfgfatal(p->loc,"conffile","can't find key %s\n",
+                p->l->data.key);
+    }
+
+    while (p->r) {
+       if (l->item->type != t_dict) {
+           cfgfatal(p->loc,"conffile","path element \"%s\" "
+                    "is not a dictionary\n",p->l->data.key);
+       }
+       i=l->item->data.dict; /* First thing in list */
+
+       p=p->r;
+       l=dict_ilookup_primitive(i, p->l->data.key);
+       if (!l) {
+           cfgfatal(p->loc,"conffile","can't find key %s\n",
+                    p->l->data.key);
+       }
+    }
+    return l;
+}
+
+static item_t *new_item(enum types type, struct cloc loc)
+{
+    item_t *i;
+
+    i=safe_malloc(sizeof(*i),"new_item");
+    i->type=type;
+    i->loc=loc;
+    return i;
+}
+
+static list_t *process_item(dict_t *context, struct p_node *i)
+{
+    item_t *item=NULL;
+
+    switch (i->type) {
+    case T_STRING:
+       item=new_item(t_string, i->loc);
+       item->data.string=i->data.string; /* XXX maybe strcpy */
+       break;
+    case T_NUMBER:
+       item=new_item(t_number, i->loc);
+       item->data.number=i->data.number;
+       break;
+    case T_ABSPATH:
+       context=dict_find_root(context);
+       /* falls through */
+    case T_RELPATH:
+       return dict_lookup_path(context, i->r);
+       /* returns immediately */
+       break;
+    case T_DICT:
+       item=new_item(t_dict, i->loc);
+       item->data.dict=dict_new(context);
+/* XXX dict_add_searchpath(context,process_ilist(context, i->r)); */
+       process_alist(item->data.dict, i->l);
+       break;
+    case T_EXEC:
+       return process_invocation(context, i);
+       /* returns immediately */
+       break;
+    default:
+#ifdef DUMP_PARSE_TREE
+       ptree_dump(i,0);
+       fatal("process_item: invalid node type for a list item (%s)\n",
+             ntype(i->type));
+#else
+       fatal("process_item: list item has invalid node type %d - recompile "
+             "with DUMP_PARSE_TREE defined in conffile.c for more "
+             "detailed debug output",i->type);
+#endif /* DUMP_PARSE_TREE */
+       break;
+    }
+    return list_append(NULL,item);
+}
+
+static list_t *process_ilist(dict_t *context, struct p_node *l)
+{
+    struct p_node *i;
+    list_t *r;
+
+    /* ASSERT l->type==T_LISTITEM */
+
+    r=list_new();
+
+    for (i=l; i; i=i->r) {
+       r=list_append_list(r,process_item(context,i->l));
+    }
+    return r;
+}
+       
+static list_t *process_invocation(dict_t *context, struct p_node *i)
+{
+    list_t *cll;
+    item_t *cl;
+    list_t *args;
+
+    /* ASSERT i->type==T_EXEC */
+    /* ASSERT i->r->type==T_LISTITEM */
+    /* XXX it might be null too */
+    cll=process_item(context,i->l);
+    cl=cll->item;
+    if (cl->type != t_closure) {
+       cfgfatal(i->l->loc,"conffile","only closures can be invoked\n");
+    }
+    args=process_ilist(context, i->r);
+    return cl->data.closure->apply(cl->data.closure, i->loc, context, args);
+}
+
+static void process_alist(dict_t *context, struct p_node *c)
+{
+    struct p_node *i;
+    atom_t k;
+    list_t *l;
+
+    if (!c) return; /* NULL assignment lists are valid (empty dictionary) */
+
+    /* ASSERT c->type==T_ALIST */
+    if (c->type!=T_ALIST) {
+       fatal("invalid node type in assignment list\n");
+    }
+
+    for (i=c; i; i=i->r) {
+       /* ASSERT i->l && i->l->type==T_ASSIGNMENT */
+       /* ASSERT i->l->l->type==T_KEY */
+       /* ASSERT i->l->r->type==T_LISTITEM */
+       k=i->l->l->data.key;
+       l=process_ilist(context, i->l->r);
+       dict_iadd(context, k, l);
+    }
+}
+
+/* Take a list of items; turn any dictionaries in this list into lists */
+static list_t *makelist(closure_t *self, struct cloc loc,
+                       dict_t *context, list_t *args)
+{
+    list_t *r=NULL, *i;
+    struct entry *e;
+    
+    for (i=args; i; i=i->next) {
+       if (i->item->type==t_dict) {
+           /* Convert */
+           for (e=i->item->data.dict->entries; e; e=e->next) {
+               r=list_append_list(r, e->val);
+           }
+       } else {
+           r=list_append_list(r, list_append(NULL,i->item));
+       }
+    }
+    return r;
+}
+
+/* Read a file and turn it into a string */
+static list_t *readfile(closure_t *self, struct cloc loc,
+                       dict_t *context, list_t *args)
+{
+    FILE *f;
+    string_t filename;
+    long length;
+    item_t *r;
+
+    r=list_elem(args,0);
+    if (!r) {
+       cfgfatal(loc,"readfile","you must supply a filename\n");
+    }
+    if (r->type!=t_string) {
+       cfgfatal(loc,"readfile","filename must be a string\n");
+    }
+    filename=r->data.string;
+    f=fopen(filename,"rb");
+    if (!f) {
+       fatal_perror("readfile (%s:%d): cannot open file \"%s\"",
+                    loc.file,loc.line, filename);
+    }
+    if (fseek(f, 0, SEEK_END)!=0) {
+       fatal_perror("readfile (%s:%d): fseek(SEEK_END)",loc.file,loc.line);
+    }
+    length=ftell(f);
+    if (length<0) {
+       fatal_perror("readfile (%s:%d): ftell()",loc.file,loc.line);
+    }
+    if (fseek(f, 0, SEEK_SET)!=0) {
+       fatal_perror("readfile (%s:%d): fseek(SEEK_SET)",loc.file,loc.line);
+    }
+    r=new_item(t_string,loc);
+    r->data.string=safe_malloc(length+1,"readfile");
+    if (fread(r->data.string,length,1,f)!=1) {
+       fatal("readfile (%s:%d): fread: could not read all of file\n",
+             loc.file,loc.line);
+    }
+    r->data.string[length]=0;
+    if (fclose(f)!=0) {
+       fatal_perror("readfile (%s:%d): fclose",loc.file,loc.line);
+    }
+    return list_append(NULL,r);
+}
+    
+static dict_t *process_config(struct p_node *c)
+{
+    dict_t *root;
+    dict_t *context;
+    item_t *i;
+    list_t *false;
+    list_t *true;
+
+    root=dict_new(NULL);
+    context=root;
+
+    /* Predefined keys for boolean values */
+    i=new_item(t_bool,no_loc);
+    i->data.bool=False;
+    false=list_append(NULL,i);
+    i=new_item(t_bool,no_loc);
+    i->data.bool=True;
+    true=list_append(NULL,i);
+    dict_add(root,"false",false);
+    dict_add(root,"False",false);
+    dict_add(root,"FALSE",false);
+    dict_add(root,"no",false);
+    dict_add(root,"No",false);
+    dict_add(root,"NO",false);
+    dict_add(root,"true",true);
+    dict_add(root,"True",true);
+    dict_add(root,"TRUE",true);
+    dict_add(root,"yes",true);
+    dict_add(root,"Yes",true);
+    dict_add(root,"YES",true);
+
+    add_closure(root,"makelist",makelist);
+    add_closure(root,"readfile",readfile);
+
+    init_builtin_modules(root);
+
+    process_alist(context, c);
+
+    return root;
+}
+
+/***** Externally accessible functions */
+
+atom_t intern(string_t s)
+{
+    struct atomlist *i;
+
+    for (i=atoms; i; i=i->next) {
+       if (strcmp(i->a, s)==0) break;
+    }
+
+    if (!i) {
+       /* Did't find it; create a new one */
+       i=safe_malloc(sizeof(*i),"intern: alloc list entry");
+       i->a=safe_strdup(s,"intern: alloc string");
+       i->next=atoms;
+       atoms=i;
+    }
+    return i->a;
+}
+
+list_t *dict_lookup(dict_t *dict, string_t key)
+{
+    return dict_ilookup(dict, intern(key));
+}
+
+list_t *dict_lookup_primitive(dict_t *dict, string_t key)
+{
+    return dict_ilookup_primitive(dict, intern(key));
+}
+
+void dict_add(dict_t *dict, string_t key, list_t *val)
+{
+    dict_iadd(dict,intern(key),val);
+}
+
+string_t *dict_keys(dict_t *dict)
+{
+    atom_t *r, *j;
+    struct entry *i;
+    r=safe_malloc(sizeof(*r)*(dict->size+1),"dict_keys");
+    for (i=dict->entries, j=r; i; i=i->next, j++) {
+       *j=i->key;
+    }
+    *j=NULL;
+    return r;
+}
+
+
+/* List-related functions */
+
+list_t *list_new(void)
+{
+    return NULL;
+}
+
+list_t *list_append_list(list_t *a, list_t *b)
+{
+    list_t *i;
+
+    if (!a) return b;
+    for (i=a; i->next; i=i->next);
+    i->next=b;
+    return a;
+}
+
+list_t *list_append(list_t *list, item_t *item)
+{
+    list_t *l;
+
+    l=safe_malloc(sizeof(*l),"list_append");
+    l->item=item;
+    l->next=NULL;
+
+    return list_append_list(list,l);
+}
+
+item_t *list_elem(list_t *l, uint32_t index)
+{
+    if (!l) return NULL;
+    if (index==0) return l->item;
+    return list_elem(l->next, index-1);
+}
+
+list_t *new_closure(closure_t *cl)
+{
+    item_t *i;
+
+    i=new_item(t_closure,no_loc);
+    i->data.closure=cl;
+    return list_append(NULL,i);
+}
+
+void add_closure(dict_t *dict, string_t name, apply_fn apply)
+{
+    closure_t *c;
+    c=safe_malloc(sizeof(*c),"add_closure");
+    c->description=name;
+    c->apply=apply;
+    c->interface=NULL;
+
+    dict_add(dict,name,new_closure(c));
+}
+
+void *find_cl_if(dict_t *dict, string_t name, uint32_t type,
+                bool_t fail_if_invalid, string_t desc, struct cloc loc)
+{
+    list_t *l;
+    item_t *i;
+    closure_t *cl;
+
+    l=dict_lookup(dict,name);
+    if (!l) {
+       if (!fail_if_invalid) return NULL;
+       cfgfatal(loc,desc,"closure \"%s\" not found\n",name);
+    }
+    i=list_elem(l,0);
+    if (i->type!=t_closure) {
+       if (!fail_if_invalid) return NULL;
+       cfgfatal(loc,desc,"\"%s\" must be a closure\n",name);
+    }
+    cl=i->data.closure;
+    if (cl->type!=type) {
+       if (!fail_if_invalid) return NULL;
+       cfgfatal(loc,desc,"\"%s\" is the wrong type of closure\n",name);
+    }
+    return cl->interface;
+}
+
+/* Convenience functions for modules reading configuration dictionaries */
+item_t *dict_find_item(dict_t *dict, string_t key, bool_t required,
+                      string_t desc, struct cloc loc)
+{
+    list_t *l;
+    item_t *i;
+
+    l=dict_lookup(dict,key);
+    if (!l) {
+       if (!required) return NULL;
+       cfgfatal(loc,desc,"required parameter \"%s\" not found\n",key);
+    }
+    i=list_elem(l,0);
+    return i;
+}
+
+string_t dict_read_string(dict_t *dict, string_t key, bool_t required,
+                         string_t desc, struct cloc loc)
+{
+    item_t *i;
+    string_t r;
+
+    i=dict_find_item(dict,key,required,desc,loc);
+    if (!i) return NULL;
+    if (i->type!=t_string) {
+       cfgfatal(loc,desc,"\"%s\" must be a string\n",key);
+    }
+    r=i->data.string;
+    return r;
+}
+
+uint32_t dict_read_number(dict_t *dict, string_t key, bool_t required,
+                         string_t desc, struct cloc loc, uint32_t def)
+{
+    item_t *i;
+    uint32_t r;
+
+    i=dict_find_item(dict,key,required,desc,loc);
+    if (!i) return def;
+    if (i->type!=t_number) {
+       cfgfatal(loc,desc,"\"%s\" must be a number\n",key);
+    }
+    r=i->data.number;
+    return r;
+}
+
+bool_t dict_read_bool(dict_t *dict, string_t key, bool_t required,
+                     string_t desc, struct cloc loc, bool_t def)
+{
+    item_t *i;
+    bool_t r;
+
+    i=dict_find_item(dict,key,required,desc,loc);
+    if (!i) return def;
+    if (i->type!=t_bool) {
+       cfgfatal(loc,desc,"\"%s\" must be a boolean\n",key);
+    }
+    r=i->data.bool;
+    return r;
+}
+
+static struct subnet string_to_subnet(item_t *i, string_t desc)
+{
+    struct subnet s;
+    uint32_t a, b, c, d, n;
+    uint32_t match;
+
+    /* i is not guaranteed to be a string */
+    if (i->type!=t_string) {
+       cfgfatal(i->loc,desc,"expecting a string (subnet specification)\n");
+    }
+
+    /* We expect strings of the form "a.b.c.d[/n]", i.e. the dots are
+       NOT optional. The subnet mask is optional; if missing it is assumed
+       to be /32. */
+    match=sscanf(i->data.string,"%u.%u.%u.%u/%u", &a, &b, &c, &d, &n);
+    if (match<4) {
+       cfgfatal(i->loc,desc,"\"%s\" is not a valid "
+                "subnet specification\n",i->data.string);
+    }
+    if (match<5) {
+       n=32;
+    }
+    if (a>255 || b>255 || c>255 || d>255 || n>32) {
+       cfgfatal(i->loc,desc,"\"%s\": range error\n",i->data.string);
+    }
+    s.prefix=(a<<24)|(b<<16)|(c<<8)|(d);
+    s.mask=(~0UL << (32-n));
+    if (s.prefix & ~s.mask) {
+       cfgfatal(i->loc,desc,"\"%s\": prefix not fully contained "
+                "in mask\n",i->data.string);
+    }
+    return s;
+}
+
+uint32_t string_to_ipaddr(item_t *i, string_t desc)
+{
+    uint32_t a, b, c, d;
+    uint32_t match;
+
+    /* i is not guaranteed to be a string */
+    if (i->type!=t_string) {
+       cfgfatal(i->loc,desc,"expecting a string (IP address)\n");
+    }
+
+    match=sscanf(i->data.string,"%u.%u.%u.%u", &a, &b, &c, &d);
+    if (match<4) {
+       cfgfatal(i->loc,desc,"\"%s\" is not a valid "
+                "IP address\n",i->data.string);
+    }
+    if (a>255 || b>255 || c>255 || d>255) {
+       cfgfatal(i->loc,desc,"\"%s\": range error\n",i->data.string);
+    }
+    return (a<<24)|(b<<16)|(c<<8)|(d);
+}
+
+void dict_read_subnet_list(dict_t *dict, string_t key, bool_t required,
+                          string_t desc, struct cloc loc,
+                          struct subnet_list *sl)
+{
+    list_t *l, *li;
+    item_t *i;
+    uint32_t e=0;
+
+    sl->entries=0;
+    sl->list=NULL;
+    l=dict_lookup(dict, key);
+    if (!l) {
+       if (!required) return;
+       cfgfatal(loc,desc,"required parameter \"%s\" not found\n",key);
+    }
+    /* Count the items in the list */
+    for (li=l; li; li=li->next) e++;
+    if (e==0) return;
+    sl->entries=e;
+    sl->list=safe_malloc(sizeof(struct subnet)*e, "dict_read_subnet_list");
+    e=0;
+    /* Fill in the list */
+    for (li=l; li; li=li->next) {
+       i=li->item;
+       if (i->type!=t_string) {
+           cfgfatal(loc,desc,"parameter \"%s\": all elements must "
+                    "be strings\n",key);
+       }
+       sl->list[e++]=string_to_subnet(i,desc);
+    }
+}
+
+dict_t *read_conffile(char *name)
+{
+    FILE *conffile;
+    struct p_node *config;
+
+    if (strcmp(name,"-")==0) {
+       conffile=stdin;
+    } else {
+       conffile=fopen(name,"r");
+       if (!conffile)
+           fatal_perror("Cannot open configuration file \"%s\"",name);
+    }
+    config_lineno=1;
+    config_file=name;
+    config=parse_conffile(conffile);
+    fclose(conffile);
+
+#ifdef DUMP_PARSE_TREE
+    printf("*** config file parse tree BEFORE MANGLE\n");
+    ptree_dump(config,0);
+#endif /* DUMP_PARSE_TREE */
+    /* The root of the configuration is a T_ALIST, which needs reversing
+       before we mangle because it isn't the child of a T_DICT. */
+    config=list_reverse(config);
+    ptree_mangle(config);
+#ifdef DUMP_PARSE_TREE
+    printf("\n\n*** config file parse tree AFTER MANGLE\n");
+    ptree_dump(config,0);
+#endif /* DUMP_PARSE_TREE */
+    return process_config(config);
+}
diff --git a/conffile.fl b/conffile.fl
new file mode 100644 (file)
index 0000000..d191ffc
--- /dev/null
@@ -0,0 +1,101 @@
+/* the "incl" state is used for picking up the name of an include file */
+%x incl
+
+%{
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "conffile_internal.h"
+#include "conffile.tab.h"
+#include "util.h"
+
+#define MAX_INCLUDE_DEPTH 10
+YY_BUFFER_STATE include_stack[MAX_INCLUDE_DEPTH];
+int include_stack_ptr=0;
+
+uint32_t config_lineno=0;
+string_t config_file="xxx";
+
+static struct p_node *leafnode(uint32_t type)
+{
+       struct p_node *r;
+
+       r=safe_malloc(sizeof(*r),"leafnode");
+       r->type=type;
+       r->loc.file=config_file;
+       r->loc.line=config_lineno;
+       r->l=NULL; r->r=NULL;
+       return r;
+}
+
+static struct p_node *keynode(atom_t key)
+{
+       struct p_node *r;
+       r=leafnode(T_KEY);
+       r->data.key=intern(key);
+       return r;
+}
+
+static struct p_node *stringnode(string_t string)
+{
+       struct p_node *r;
+       r=leafnode(T_STRING);
+       string++;
+       string[strlen(string)-1]=0;
+       r->data.string=safe_strdup(string,"stringnode");
+       return r;
+}
+
+static struct p_node *numnode(string_t number)
+{
+       struct p_node *r;
+       r=leafnode(T_NUMBER);
+       r->data.number=atoi(number);
+       return r;
+}
+
+%}
+
+%%
+include                        BEGIN(incl);
+<incl>[ \t]*           /* eat the whitespace */
+<incl>[^ \t\n]+                { /* got the include filename */
+       if (include_stack_ptr >= MAX_INCLUDE_DEPTH) {
+               fatal("Configuration file includes nested too deeply");
+       }
+       include_stack[include_stack_ptr++]=
+               YY_CURRENT_BUFFER;
+       yyin=fopen(yytext,"r");
+       if (!yyin) {
+               fatal("Can't open included file %s",yytext);
+       }
+       yy_switch_to_buffer(yy_create_buffer(yyin, YY_BUF_SIZE));
+       BEGIN(INITIAL);
+       }
+<<EOF>>                {
+       if (--include_stack_ptr < 0) {
+               yyterminate();
+               }
+       else {
+               fclose(yyin);
+               yy_delete_buffer(YY_CURRENT_BUFFER);
+               yy_switch_to_buffer(include_stack[include_stack_ptr]);
+       }
+       }
+\"[^\"]*\"             yylval=stringnode(yytext); return TOK_STRING;
+
+[[:alpha:]_][[:alnum:]\-_]*    yylval=keynode(yytext); return TOK_KEY;
+
+[[:digit:]]+           yylval=numnode(yytext); return TOK_NUMBER;
+
+       /* Eat comments */
+\#.*\n                 config_lineno++;
+       /* Count lines */
+\n                     config_lineno++;
+       /* Eat whitespace */
+[[:blank:]\j]
+
+       /* Return all unclaimed single characters to the parser */
+.                      return *yytext;
+
diff --git a/conffile.h b/conffile.h
new file mode 100644 (file)
index 0000000..d7468a5
--- /dev/null
@@ -0,0 +1,12 @@
+/*
+ * $Log$
+ */
+
+#ifndef conffile_h
+#define conffile_h
+
+#include "secnet.h"
+
+extern dict_t *read_conffile(char *conffile);
+
+#endif /* conffile_h */
diff --git a/conffile.y b/conffile.y
new file mode 100644 (file)
index 0000000..b6b246c
--- /dev/null
@@ -0,0 +1,106 @@
+%token TOK_STRING
+%token TOK_NUMBER
+%token TOK_KEY
+
+%start input
+
+%{
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include "secnet.h"
+#include "conffile_internal.h"
+#include "util.h"
+#define YYERROR_VERBOSE
+
+static struct p_node *node(uint32_t type, struct p_node *l, struct p_node *r);
+
+static struct p_node *result;
+
+static void yyerror(char *s);
+
+%}
+
+%%
+
+input:           assignments { result = $1; }
+               ;
+
+assignments:     assignments assignment { $$=node(T_ALIST, $2, $1); }
+               | assignment { $$=node(T_ALIST, $1, NULL); }
+               ;
+
+searchpath:      /* empty */ { $$ = NULL; }
+               | '<' list '>' { $$ = $2; }
+               ;
+
+dict:            searchpath '{' assignments '}'
+       { $$ = node(T_DICT, $3, $1); }
+               | searchpath '{' '}' { $$ = node(T_DICT, NULL, $1); }
+               ;
+
+path:            '/' pathelements { $$ = node(T_ABSPATH, NULL, $2); }
+               | pathelements { $$ = node(T_RELPATH, NULL, $1); }
+               ;
+
+pathelements:    pathelements '/' TOK_KEY { $$ = node(T_PATHELEM, $3, $1); }
+               | TOK_KEY { $$ = node(T_PATHELEM, $1, NULL); }
+               ;
+
+exec:            item '(' list ')' { $$ = node(T_EXEC, $1, $3); }
+               | item '(' ')' { $$ = node(T_EXEC, $1, NULL); }
+               | item dict
+       { $$ = node(T_EXEC, $1, node(T_LISTITEM, $2, NULL)); }
+               ;
+
+list:            list ',' item { $$ = node(T_LISTITEM, $3, $1); }
+               | item { $$ = node(T_LISTITEM, $1, NULL); }
+               ;
+
+assignment:      TOK_KEY '=' list ';' { $$ = node(T_ASSIGNMENT, $1, $3); }
+               | TOK_KEY list ';' { $$ = node(T_ASSIGNMENT, $1, $2); }
+               | error ';' { $$ = node(T_ERROR, NULL, NULL); }
+               | error '}' { $$ = node(T_ERROR, NULL, NULL); }
+               | error ')' { $$ = node(T_ERROR, NULL, NULL); }
+               ;
+
+item:            TOK_STRING
+               | TOK_NUMBER
+               | path
+               | dict
+               | exec
+               ;
+
+%%
+
+static void yyerror(char *s)
+{
+       Message(M_FATAL,"config file %s line %d: %s\n",config_file,
+               config_lineno,s);
+}
+
+struct p_node *parse_conffile(FILE *conffile)
+{
+       yyin=conffile;
+       if (yyparse()!=0) {
+               fatal("Configuration file parsing failed\n");
+       }
+       if (yynerrs>0) {
+               fatal("%d error%s encountered in configuration file\n",
+               yynerrs,yynerrs==1?"":"s");
+       }
+       return result;
+}
+
+static struct p_node *node(uint32_t type, struct p_node *l, struct p_node *r)
+{
+       struct p_node *rv;
+
+       rv=safe_malloc(sizeof(*rv),"p_node");
+       rv->type=type;
+       rv->loc.file=config_file;
+       rv->loc.line=config_lineno;
+       rv->l=l;
+       rv->r=r;
+       return rv;
+}
diff --git a/conffile_internal.h b/conffile_internal.h
new file mode 100644 (file)
index 0000000..710fd96
--- /dev/null
@@ -0,0 +1,54 @@
+/*
+ * $Log$
+ */
+
+#ifndef conffile_internal_h
+#define conffile_internal_h
+
+#include <stdio.h>
+#include "secnet.h"
+
+extern FILE *yyin;
+
+typedef string_t atom_t;
+
+/* Parse tree for configuration file */
+
+#define YYSTYPE struct p_node *
+
+#define T_STRING 1
+#define T_NUMBER 2
+#define T_KEY    3
+#define T_ASSIGNMENT 10
+#define T_LISTITEM   11
+#define T_EXEC       12
+#define T_PATHELEM   13
+#define T_ABSPATH    14
+#define T_RELPATH    15
+#define T_DICT       16
+#define T_ALIST      17
+#define T_ERROR      20
+
+struct p_node {
+    uint32_t type;
+    struct cloc loc;
+    union {
+       atom_t key;
+       string_t string;
+       uint32_t number;
+    } data;
+    struct p_node *l;
+    struct p_node *r;
+};
+
+extern int yylex(void);
+extern string_t config_file;
+extern uint32_t config_lineno;
+
+/* Keys in dictionaries are 'atoms', which are constructed from strings
+   using this call. Atoms may be compared using '=='. */
+extern atom_t intern(string_t string);
+
+extern struct p_node *parse_conffile(FILE *conffile);
+
+#endif /* conffile_internal_h */
diff --git a/config.h.bot b/config.h.bot
new file mode 100644 (file)
index 0000000..adb0c7b
--- /dev/null
@@ -0,0 +1,32 @@
+/* -*- c -*- */
+
+/* These are from config.h.bot, pasted onto the end of config.h.in. */
+
+#ifdef HAVE_SYS_CDEFS_H
+#include <sys/cdefs.h>
+#endif
+
+#if 0
+/* Use the definitions: */
+
+/* Give us an unsigned 32-bit data type. */
+#if SIZEOF_UNSIGNED_LONG==4
+#define UWORD32 unsigned long
+#elif SIZEOF_UNSIGNED_INT==4
+#define UWORD32 unsigned int
+#else
+#error I do not know what to use for a UWORD32.
+#endif
+
+/* An unsigned 8-bit data type */
+#if SIZEOF_UNSIGNED_CHAR==1
+#define UBYTE8 unsigned char
+#else
+#error I do not know what to use for a UBYTE8
+#endif
+
+
+#endif /* 0 */
+
+
+#endif /* _CONFIG_H */
diff --git a/config.h.in b/config.h.in
new file mode 100644 (file)
index 0000000..a3b3cef
--- /dev/null
@@ -0,0 +1,90 @@
+/* config.h.in.  Generated automatically from configure.in by autoheader.  */
+/***************************************************************************
+ *
+ *              Part II Project, "A secure, private IP network"
+ *              Stephen Early <sde1000@cam.ac.uk>
+ *   
+ *
+ *     $RCSfile$
+ *
+ *  Description: 
+ *
+ *    Copyright: (C) Stephen Early 1995
+ *
+ *    $Revision$
+ *
+ *        $Date$
+ *
+ *       $State$
+ *
+ ***************************************************************************/
+
+/* $Log$
+ */
+
+#ifndef _CONFIG_H
+#define _CONFIG_H
+
+
+/* Define to empty if the keyword does not work.  */
+#undef const
+
+/* Define if you don't have vprintf but do have _doprnt.  */
+#undef HAVE_DOPRNT
+
+/* Define if you have the vprintf function.  */
+#undef HAVE_VPRINTF
+
+/* Define to `int' if <sys/types.h> doesn't define.  */
+#undef pid_t
+
+/* Define to `unsigned' if <sys/types.h> doesn't define.  */
+#undef size_t
+
+/* Define if you have the ANSI C header files.  */
+#undef STDC_HEADERS
+
+/* Define if your processor stores words with the most significant
+   byte first (like Motorola and SPARC, unlike Intel and VAX).  */
+#undef WORDS_BIGENDIAN
+
+/* Define if you have the adns library (-ladns).  */
+#undef HAVE_LIBADNS
+
+/* Define if you have the fl library (-lfl).  */
+#undef HAVE_LIBFL
+
+/* Define if you have the gmp library (-lgmp).  */
+#undef HAVE_LIBGMP
+/* -*- c -*- */
+
+/* These are from config.h.bot, pasted onto the end of config.h.in. */
+
+#ifdef HAVE_SYS_CDEFS_H
+#include <sys/cdefs.h>
+#endif
+
+#if 0
+/* Use the definitions: */
+
+/* Give us an unsigned 32-bit data type. */
+#if SIZEOF_UNSIGNED_LONG==4
+#define UWORD32 unsigned long
+#elif SIZEOF_UNSIGNED_INT==4
+#define UWORD32 unsigned int
+#else
+#error I do not know what to use for a UWORD32.
+#endif
+
+/* An unsigned 8-bit data type */
+#if SIZEOF_UNSIGNED_CHAR==1
+#define UBYTE8 unsigned char
+#else
+#error I do not know what to use for a UBYTE8
+#endif
+
+
+#endif /* 0 */
+
+
+#endif /* _CONFIG_H */
diff --git a/config.h.top b/config.h.top
new file mode 100644 (file)
index 0000000..0cea96d
--- /dev/null
@@ -0,0 +1,26 @@
+/***************************************************************************
+ *
+ *              Part II Project, "A secure, private IP network"
+ *              Stephen Early <sde1000@cam.ac.uk>
+ *   
+ *
+ *     $RCSfile$
+ *
+ *  Description: 
+ *
+ *    Copyright: (C) Stephen Early 1995
+ *
+ *    $Revision$
+ *
+ *        $Date$
+ *
+ *       $State$
+ *
+ ***************************************************************************/
+
+/* $Log$
+ */
+
+#ifndef _CONFIG_H
+#define _CONFIG_H
+
diff --git a/configure b/configure
new file mode 100755 (executable)
index 0000000..7a08124
--- /dev/null
+++ b/configure
@@ -0,0 +1,1924 @@
+#! /bin/sh
+
+# Guess values for system-dependent variables and create Makefiles.
+# Generated automatically using autoconf version 2.13 
+# Copyright (C) 1992, 93, 94, 95, 96 Free Software Foundation, Inc.
+#
+# This configure script is free software; the Free Software Foundation
+# gives unlimited permission to copy, distribute and modify it.
+
+# Defaults:
+ac_help=
+ac_default_prefix=/usr/local
+# Any additions from configure.in:
+
+# Initialize some variables set by options.
+# The variables have the same names as the options, with
+# dashes changed to underlines.
+build=NONE
+cache_file=./config.cache
+exec_prefix=NONE
+host=NONE
+no_create=
+nonopt=NONE
+no_recursion=
+prefix=NONE
+program_prefix=NONE
+program_suffix=NONE
+program_transform_name=s,x,x,
+silent=
+site=
+srcdir=
+target=NONE
+verbose=
+x_includes=NONE
+x_libraries=NONE
+bindir='${exec_prefix}/bin'
+sbindir='${exec_prefix}/sbin'
+libexecdir='${exec_prefix}/libexec'
+datadir='${prefix}/share'
+sysconfdir='${prefix}/etc'
+sharedstatedir='${prefix}/com'
+localstatedir='${prefix}/var'
+libdir='${exec_prefix}/lib'
+includedir='${prefix}/include'
+oldincludedir='/usr/include'
+infodir='${prefix}/info'
+mandir='${prefix}/man'
+
+# Initialize some other variables.
+subdirs=
+MFLAGS= MAKEFLAGS=
+SHELL=${CONFIG_SHELL-/bin/sh}
+# Maximum number of lines to put in a shell here document.
+ac_max_here_lines=12
+
+ac_prev=
+for ac_option
+do
+
+  # If the previous option needs an argument, assign it.
+  if test -n "$ac_prev"; then
+    eval "$ac_prev=\$ac_option"
+    ac_prev=
+    continue
+  fi
+
+  case "$ac_option" in
+  -*=*) ac_optarg=`echo "$ac_option" | sed 's/[-_a-zA-Z0-9]*=//'` ;;
+  *) ac_optarg= ;;
+  esac
+
+  # Accept the important Cygnus configure options, so we can diagnose typos.
+
+  case "$ac_option" in
+
+  -bindir | --bindir | --bindi | --bind | --bin | --bi)
+    ac_prev=bindir ;;
+  -bindir=* | --bindir=* | --bindi=* | --bind=* | --bin=* | --bi=*)
+    bindir="$ac_optarg" ;;
+
+  -build | --build | --buil | --bui | --bu)
+    ac_prev=build ;;
+  -build=* | --build=* | --buil=* | --bui=* | --bu=*)
+    build="$ac_optarg" ;;
+
+  -cache-file | --cache-file | --cache-fil | --cache-fi \
+  | --cache-f | --cache- | --cache | --cach | --cac | --ca | --c)
+    ac_prev=cache_file ;;
+  -cache-file=* | --cache-file=* | --cache-fil=* | --cache-fi=* \
+  | --cache-f=* | --cache-=* | --cache=* | --cach=* | --cac=* | --ca=* | --c=*)
+    cache_file="$ac_optarg" ;;
+
+  -datadir | --datadir | --datadi | --datad | --data | --dat | --da)
+    ac_prev=datadir ;;
+  -datadir=* | --datadir=* | --datadi=* | --datad=* | --data=* | --dat=* \
+  | --da=*)
+    datadir="$ac_optarg" ;;
+
+  -disable-* | --disable-*)
+    ac_feature=`echo $ac_option|sed -e 's/-*disable-//'`
+    # Reject names that are not valid shell variable names.
+    if test -n "`echo $ac_feature| sed 's/[-a-zA-Z0-9_]//g'`"; then
+      { echo "configure: error: $ac_feature: invalid feature name" 1>&2; exit 1; }
+    fi
+    ac_feature=`echo $ac_feature| sed 's/-/_/g'`
+    eval "enable_${ac_feature}=no" ;;
+
+  -enable-* | --enable-*)
+    ac_feature=`echo $ac_option|sed -e 's/-*enable-//' -e 's/=.*//'`
+    # Reject names that are not valid shell variable names.
+    if test -n "`echo $ac_feature| sed 's/[-_a-zA-Z0-9]//g'`"; then
+      { echo "configure: error: $ac_feature: invalid feature name" 1>&2; exit 1; }
+    fi
+    ac_feature=`echo $ac_feature| sed 's/-/_/g'`
+    case "$ac_option" in
+      *=*) ;;
+      *) ac_optarg=yes ;;
+    esac
+    eval "enable_${ac_feature}='$ac_optarg'" ;;
+
+  -exec-prefix | --exec_prefix | --exec-prefix | --exec-prefi \
+  | --exec-pref | --exec-pre | --exec-pr | --exec-p | --exec- \
+  | --exec | --exe | --ex)
+    ac_prev=exec_prefix ;;
+  -exec-prefix=* | --exec_prefix=* | --exec-prefix=* | --exec-prefi=* \
+  | --exec-pref=* | --exec-pre=* | --exec-pr=* | --exec-p=* | --exec-=* \
+  | --exec=* | --exe=* | --ex=*)
+    exec_prefix="$ac_optarg" ;;
+
+  -gas | --gas | --ga | --g)
+    # Obsolete; use --with-gas.
+    with_gas=yes ;;
+
+  -help | --help | --hel | --he)
+    # Omit some internal or obsolete options to make the list less imposing.
+    # This message is too long to be a string in the A/UX 3.1 sh.
+    cat << EOF
+Usage: configure [options] [host]
+Options: [defaults in brackets after descriptions]
+Configuration:
+  --cache-file=FILE       cache test results in FILE
+  --help                  print this message
+  --no-create             do not create output files
+  --quiet, --silent       do not print \`checking...' messages
+  --version               print the version of autoconf that created configure
+Directory and file names:
+  --prefix=PREFIX         install architecture-independent files in PREFIX
+                          [$ac_default_prefix]
+  --exec-prefix=EPREFIX   install architecture-dependent files in EPREFIX
+                          [same as prefix]
+  --bindir=DIR            user executables in DIR [EPREFIX/bin]
+  --sbindir=DIR           system admin executables in DIR [EPREFIX/sbin]
+  --libexecdir=DIR        program executables in DIR [EPREFIX/libexec]
+  --datadir=DIR           read-only architecture-independent data in DIR
+                          [PREFIX/share]
+  --sysconfdir=DIR        read-only single-machine data in DIR [PREFIX/etc]
+  --sharedstatedir=DIR    modifiable architecture-independent data in DIR
+                          [PREFIX/com]
+  --localstatedir=DIR     modifiable single-machine data in DIR [PREFIX/var]
+  --libdir=DIR            object code libraries in DIR [EPREFIX/lib]
+  --includedir=DIR        C header files in DIR [PREFIX/include]
+  --oldincludedir=DIR     C header files for non-gcc in DIR [/usr/include]
+  --infodir=DIR           info documentation in DIR [PREFIX/info]
+  --mandir=DIR            man documentation in DIR [PREFIX/man]
+  --srcdir=DIR            find the sources in DIR [configure dir or ..]
+  --program-prefix=PREFIX prepend PREFIX to installed program names
+  --program-suffix=SUFFIX append SUFFIX to installed program names
+  --program-transform-name=PROGRAM
+                          run sed PROGRAM on installed program names
+EOF
+    cat << EOF
+Host type:
+  --build=BUILD           configure for building on BUILD [BUILD=HOST]
+  --host=HOST             configure for HOST [guessed]
+  --target=TARGET         configure for TARGET [TARGET=HOST]
+Features and packages:
+  --disable-FEATURE       do not include FEATURE (same as --enable-FEATURE=no)
+  --enable-FEATURE[=ARG]  include FEATURE [ARG=yes]
+  --with-PACKAGE[=ARG]    use PACKAGE [ARG=yes]
+  --without-PACKAGE       do not use PACKAGE (same as --with-PACKAGE=no)
+  --x-includes=DIR        X include files are in DIR
+  --x-libraries=DIR       X library files are in DIR
+EOF
+    if test -n "$ac_help"; then
+      echo "--enable and --with options recognized:$ac_help"
+    fi
+    exit 0 ;;
+
+  -host | --host | --hos | --ho)
+    ac_prev=host ;;
+  -host=* | --host=* | --hos=* | --ho=*)
+    host="$ac_optarg" ;;
+
+  -includedir | --includedir | --includedi | --included | --include \
+  | --includ | --inclu | --incl | --inc)
+    ac_prev=includedir ;;
+  -includedir=* | --includedir=* | --includedi=* | --included=* | --include=* \
+  | --includ=* | --inclu=* | --incl=* | --inc=*)
+    includedir="$ac_optarg" ;;
+
+  -infodir | --infodir | --infodi | --infod | --info | --inf)
+    ac_prev=infodir ;;
+  -infodir=* | --infodir=* | --infodi=* | --infod=* | --info=* | --inf=*)
+    infodir="$ac_optarg" ;;
+
+  -libdir | --libdir | --libdi | --libd)
+    ac_prev=libdir ;;
+  -libdir=* | --libdir=* | --libdi=* | --libd=*)
+    libdir="$ac_optarg" ;;
+
+  -libexecdir | --libexecdir | --libexecdi | --libexecd | --libexec \
+  | --libexe | --libex | --libe)
+    ac_prev=libexecdir ;;
+  -libexecdir=* | --libexecdir=* | --libexecdi=* | --libexecd=* | --libexec=* \
+  | --libexe=* | --libex=* | --libe=*)
+    libexecdir="$ac_optarg" ;;
+
+  -localstatedir | --localstatedir | --localstatedi | --localstated \
+  | --localstate | --localstat | --localsta | --localst \
+  | --locals | --local | --loca | --loc | --lo)
+    ac_prev=localstatedir ;;
+  -localstatedir=* | --localstatedir=* | --localstatedi=* | --localstated=* \
+  | --localstate=* | --localstat=* | --localsta=* | --localst=* \
+  | --locals=* | --local=* | --loca=* | --loc=* | --lo=*)
+    localstatedir="$ac_optarg" ;;
+
+  -mandir | --mandir | --mandi | --mand | --man | --ma | --m)
+    ac_prev=mandir ;;
+  -mandir=* | --mandir=* | --mandi=* | --mand=* | --man=* | --ma=* | --m=*)
+    mandir="$ac_optarg" ;;
+
+  -nfp | --nfp | --nf)
+    # Obsolete; use --without-fp.
+    with_fp=no ;;
+
+  -no-create | --no-create | --no-creat | --no-crea | --no-cre \
+  | --no-cr | --no-c)
+    no_create=yes ;;
+
+  -no-recursion | --no-recursion | --no-recursio | --no-recursi \
+  | --no-recurs | --no-recur | --no-recu | --no-rec | --no-re | --no-r)
+    no_recursion=yes ;;
+
+  -oldincludedir | --oldincludedir | --oldincludedi | --oldincluded \
+  | --oldinclude | --oldinclud | --oldinclu | --oldincl | --oldinc \
+  | --oldin | --oldi | --old | --ol | --o)
+    ac_prev=oldincludedir ;;
+  -oldincludedir=* | --oldincludedir=* | --oldincludedi=* | --oldincluded=* \
+  | --oldinclude=* | --oldinclud=* | --oldinclu=* | --oldincl=* | --oldinc=* \
+  | --oldin=* | --oldi=* | --old=* | --ol=* | --o=*)
+    oldincludedir="$ac_optarg" ;;
+
+  -prefix | --prefix | --prefi | --pref | --pre | --pr | --p)
+    ac_prev=prefix ;;
+  -prefix=* | --prefix=* | --prefi=* | --pref=* | --pre=* | --pr=* | --p=*)
+    prefix="$ac_optarg" ;;
+
+  -program-prefix | --program-prefix | --program-prefi | --program-pref \
+  | --program-pre | --program-pr | --program-p)
+    ac_prev=program_prefix ;;
+  -program-prefix=* | --program-prefix=* | --program-prefi=* \
+  | --program-pref=* | --program-pre=* | --program-pr=* | --program-p=*)
+    program_prefix="$ac_optarg" ;;
+
+  -program-suffix | --program-suffix | --program-suffi | --program-suff \
+  | --program-suf | --program-su | --program-s)
+    ac_prev=program_suffix ;;
+  -program-suffix=* | --program-suffix=* | --program-suffi=* \
+  | --program-suff=* | --program-suf=* | --program-su=* | --program-s=*)
+    program_suffix="$ac_optarg" ;;
+
+  -program-transform-name | --program-transform-name \
+  | --program-transform-nam | --program-transform-na \
+  | --program-transform-n | --program-transform- \
+  | --program-transform | --program-transfor \
+  | --program-transfo | --program-transf \
+  | --program-trans | --program-tran \
+  | --progr-tra | --program-tr | --program-t)
+    ac_prev=program_transform_name ;;
+  -program-transform-name=* | --program-transform-name=* \
+  | --program-transform-nam=* | --program-transform-na=* \
+  | --program-transform-n=* | --program-transform-=* \
+  | --program-transform=* | --program-transfor=* \
+  | --program-transfo=* | --program-transf=* \
+  | --program-trans=* | --program-tran=* \
+  | --progr-tra=* | --program-tr=* | --program-t=*)
+    program_transform_name="$ac_optarg" ;;
+
+  -q | -quiet | --quiet | --quie | --qui | --qu | --q \
+  | -silent | --silent | --silen | --sile | --sil)
+    silent=yes ;;
+
+  -sbindir | --sbindir | --sbindi | --sbind | --sbin | --sbi | --sb)
+    ac_prev=sbindir ;;
+  -sbindir=* | --sbindir=* | --sbindi=* | --sbind=* | --sbin=* \
+  | --sbi=* | --sb=*)
+    sbindir="$ac_optarg" ;;
+
+  -sharedstatedir | --sharedstatedir | --sharedstatedi \
+  | --sharedstated | --sharedstate | --sharedstat | --sharedsta \
+  | --sharedst | --shareds | --shared | --share | --shar \
+  | --sha | --sh)
+    ac_prev=sharedstatedir ;;
+  -sharedstatedir=* | --sharedstatedir=* | --sharedstatedi=* \
+  | --sharedstated=* | --sharedstate=* | --sharedstat=* | --sharedsta=* \
+  | --sharedst=* | --shareds=* | --shared=* | --share=* | --shar=* \
+  | --sha=* | --sh=*)
+    sharedstatedir="$ac_optarg" ;;
+
+  -site | --site | --sit)
+    ac_prev=site ;;
+  -site=* | --site=* | --sit=*)
+    site="$ac_optarg" ;;
+
+  -srcdir | --srcdir | --srcdi | --srcd | --src | --sr)
+    ac_prev=srcdir ;;
+  -srcdir=* | --srcdir=* | --srcdi=* | --srcd=* | --src=* | --sr=*)
+    srcdir="$ac_optarg" ;;
+
+  -sysconfdir | --sysconfdir | --sysconfdi | --sysconfd | --sysconf \
+  | --syscon | --sysco | --sysc | --sys | --sy)
+    ac_prev=sysconfdir ;;
+  -sysconfdir=* | --sysconfdir=* | --sysconfdi=* | --sysconfd=* | --sysconf=* \
+  | --syscon=* | --sysco=* | --sysc=* | --sys=* | --sy=*)
+    sysconfdir="$ac_optarg" ;;
+
+  -target | --target | --targe | --targ | --tar | --ta | --t)
+    ac_prev=target ;;
+  -target=* | --target=* | --targe=* | --targ=* | --tar=* | --ta=* | --t=*)
+    target="$ac_optarg" ;;
+
+  -v | -verbose | --verbose | --verbos | --verbo | --verb)
+    verbose=yes ;;
+
+  -version | --version | --versio | --versi | --vers)
+    echo "configure generated by autoconf version 2.13"
+    exit 0 ;;
+
+  -with-* | --with-*)
+    ac_package=`echo $ac_option|sed -e 's/-*with-//' -e 's/=.*//'`
+    # Reject names that are not valid shell variable names.
+    if test -n "`echo $ac_package| sed 's/[-_a-zA-Z0-9]//g'`"; then
+      { echo "configure: error: $ac_package: invalid package name" 1>&2; exit 1; }
+    fi
+    ac_package=`echo $ac_package| sed 's/-/_/g'`
+    case "$ac_option" in
+      *=*) ;;
+      *) ac_optarg=yes ;;
+    esac
+    eval "with_${ac_package}='$ac_optarg'" ;;
+
+  -without-* | --without-*)
+    ac_package=`echo $ac_option|sed -e 's/-*without-//'`
+    # Reject names that are not valid shell variable names.
+    if test -n "`echo $ac_package| sed 's/[-a-zA-Z0-9_]//g'`"; then
+      { echo "configure: error: $ac_package: invalid package name" 1>&2; exit 1; }
+    fi
+    ac_package=`echo $ac_package| sed 's/-/_/g'`
+    eval "with_${ac_package}=no" ;;
+
+  --x)
+    # Obsolete; use --with-x.
+    with_x=yes ;;
+
+  -x-includes | --x-includes | --x-include | --x-includ | --x-inclu \
+  | --x-incl | --x-inc | --x-in | --x-i)
+    ac_prev=x_includes ;;
+  -x-includes=* | --x-includes=* | --x-include=* | --x-includ=* | --x-inclu=* \
+  | --x-incl=* | --x-inc=* | --x-in=* | --x-i=*)
+    x_includes="$ac_optarg" ;;
+
+  -x-libraries | --x-libraries | --x-librarie | --x-librari \
+  | --x-librar | --x-libra | --x-libr | --x-lib | --x-li | --x-l)
+    ac_prev=x_libraries ;;
+  -x-libraries=* | --x-libraries=* | --x-librarie=* | --x-librari=* \
+  | --x-librar=* | --x-libra=* | --x-libr=* | --x-lib=* | --x-li=* | --x-l=*)
+    x_libraries="$ac_optarg" ;;
+
+  -*) { echo "configure: error: $ac_option: invalid option; use --help to show usage" 1>&2; exit 1; }
+    ;;
+
+  *)
+    if test -n "`echo $ac_option| sed 's/[-a-z0-9.]//g'`"; then
+      echo "configure: warning: $ac_option: invalid host type" 1>&2
+    fi
+    if test "x$nonopt" != xNONE; then
+      { echo "configure: error: can only configure for one host and one target at a time" 1>&2; exit 1; }
+    fi
+    nonopt="$ac_option"
+    ;;
+
+  esac
+done
+
+if test -n "$ac_prev"; then
+  { echo "configure: error: missing argument to --`echo $ac_prev | sed 's/_/-/g'`" 1>&2; exit 1; }
+fi
+
+trap 'rm -fr conftest* confdefs* core core.* *.core $ac_clean_files; exit 1' 1 2 15
+
+# File descriptor usage:
+# 0 standard input
+# 1 file creation
+# 2 errors and warnings
+# 3 some systems may open it to /dev/tty
+# 4 used on the Kubota Titan
+# 6 checking for... messages and results
+# 5 compiler messages saved in config.log
+if test "$silent" = yes; then
+  exec 6>/dev/null
+else
+  exec 6>&1
+fi
+exec 5>./config.log
+
+echo "\
+This file contains any messages produced by compilers while
+running configure, to aid debugging if configure makes a mistake.
+" 1>&5
+
+# Strip out --no-create and --no-recursion so they do not pile up.
+# Also quote any args containing shell metacharacters.
+ac_configure_args=
+for ac_arg
+do
+  case "$ac_arg" in
+  -no-create | --no-create | --no-creat | --no-crea | --no-cre \
+  | --no-cr | --no-c) ;;
+  -no-recursion | --no-recursion | --no-recursio | --no-recursi \
+  | --no-recurs | --no-recur | --no-recu | --no-rec | --no-re | --no-r) ;;
+  *" "*|*"     "*|*[\[\]\~\#\$\^\&\*\(\)\{\}\\\|\;\<\>\?]*)
+  ac_configure_args="$ac_configure_args '$ac_arg'" ;;
+  *) ac_configure_args="$ac_configure_args $ac_arg" ;;
+  esac
+done
+
+# NLS nuisances.
+# Only set these to C if already set.  These must not be set unconditionally
+# because not all systems understand e.g. LANG=C (notably SCO).
+# Fixing LC_MESSAGES prevents Solaris sh from translating var values in `set'!
+# Non-C LC_CTYPE values break the ctype check.
+if test "${LANG+set}"   = set; then LANG=C;   export LANG;   fi
+if test "${LC_ALL+set}" = set; then LC_ALL=C; export LC_ALL; fi
+if test "${LC_MESSAGES+set}" = set; then LC_MESSAGES=C; export LC_MESSAGES; fi
+if test "${LC_CTYPE+set}"    = set; then LC_CTYPE=C;    export LC_CTYPE;    fi
+
+# confdefs.h avoids OS command line length limits that DEFS can exceed.
+rm -rf conftest* confdefs.h
+# AIX cpp loses on an empty file, so make sure it contains at least a newline.
+echo > confdefs.h
+
+# A filename unique to this package, relative to the directory that
+# configure is in, which we can look for to find out if srcdir is correct.
+ac_unique_file=secnet.c
+
+# Find the source files, if location was not specified.
+if test -z "$srcdir"; then
+  ac_srcdir_defaulted=yes
+  # Try the directory containing this script, then its parent.
+  ac_prog=$0
+  ac_confdir=`echo $ac_prog|sed 's%/[^/][^/]*$%%'`
+  test "x$ac_confdir" = "x$ac_prog" && ac_confdir=.
+  srcdir=$ac_confdir
+  if test ! -r $srcdir/$ac_unique_file; then
+    srcdir=..
+  fi
+else
+  ac_srcdir_defaulted=no
+fi
+if test ! -r $srcdir/$ac_unique_file; then
+  if test "$ac_srcdir_defaulted" = yes; then
+    { echo "configure: error: can not find sources in $ac_confdir or .." 1>&2; exit 1; }
+  else
+    { echo "configure: error: can not find sources in $srcdir" 1>&2; exit 1; }
+  fi
+fi
+srcdir=`echo "${srcdir}" | sed 's%\([^/]\)/*$%\1%'`
+
+# Prefer explicitly selected file to automatically selected ones.
+if test -z "$CONFIG_SITE"; then
+  if test "x$prefix" != xNONE; then
+    CONFIG_SITE="$prefix/share/config.site $prefix/etc/config.site"
+  else
+    CONFIG_SITE="$ac_default_prefix/share/config.site $ac_default_prefix/etc/config.site"
+  fi
+fi
+for ac_site_file in $CONFIG_SITE; do
+  if test -r "$ac_site_file"; then
+    echo "loading site script $ac_site_file"
+    . "$ac_site_file"
+  fi
+done
+
+if test -r "$cache_file"; then
+  echo "loading cache $cache_file"
+  . $cache_file
+else
+  echo "creating cache $cache_file"
+  > $cache_file
+fi
+
+ac_ext=c
+# CFLAGS is not in ac_cpp because -g, -O, etc. are not valid cpp options.
+ac_cpp='$CPP $CPPFLAGS'
+ac_compile='${CC-cc} -c $CFLAGS $CPPFLAGS conftest.$ac_ext 1>&5'
+ac_link='${CC-cc} -o conftest${ac_exeext} $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS 1>&5'
+cross_compiling=$ac_cv_prog_cc_cross
+
+ac_exeext=
+ac_objext=o
+if (echo "testing\c"; echo 1,2,3) | grep c >/dev/null; then
+  # Stardent Vistra SVR4 grep lacks -e, says ghazi@caip.rutgers.edu.
+  if (echo -n testing; echo 1,2,3) | sed s/-n/xn/ | grep xn >/dev/null; then
+    ac_n= ac_c='
+' ac_t='       '
+  else
+    ac_n=-n ac_c= ac_t=
+  fi
+else
+  ac_n= ac_c='\c' ac_t=
+fi
+
+
+
+
+# From configure.in Id
+
+ac_ext=c
+# CFLAGS is not in ac_cpp because -g, -O, etc. are not valid cpp options.
+ac_cpp='$CPP $CPPFLAGS'
+ac_compile='${CC-cc} -c $CFLAGS $CPPFLAGS conftest.$ac_ext 1>&5'
+ac_link='${CC-cc} -o conftest${ac_exeext} $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS 1>&5'
+cross_compiling=$ac_cv_prog_cc_cross
+
+
+echo $ac_n "checking whether ${MAKE-make} sets \${MAKE}""... $ac_c" 1>&6
+echo "configure:538: checking whether ${MAKE-make} sets \${MAKE}" >&5
+set dummy ${MAKE-make}; ac_make=`echo "$2" | sed 'y%./+-%__p_%'`
+if eval "test \"`echo '$''{'ac_cv_prog_make_${ac_make}_set'+set}'`\" = set"; then
+  echo $ac_n "(cached) $ac_c" 1>&6
+else
+  cat > conftestmake <<\EOF
+all:
+       @echo 'ac_maketemp="${MAKE}"'
+EOF
+# GNU make sometimes prints "make[1]: Entering...", which would confuse us.
+eval `${MAKE-make} -f conftestmake 2>/dev/null | grep temp=`
+if test -n "$ac_maketemp"; then
+  eval ac_cv_prog_make_${ac_make}_set=yes
+else
+  eval ac_cv_prog_make_${ac_make}_set=no
+fi
+rm -f conftestmake
+fi
+if eval "test \"`echo '$ac_cv_prog_make_'${ac_make}_set`\" = yes"; then
+  echo "$ac_t""yes" 1>&6
+  SET_MAKE=
+else
+  echo "$ac_t""no" 1>&6
+  SET_MAKE="MAKE=${MAKE-make}"
+fi
+
+# Extract the first word of "gcc", so it can be a program name with args.
+set dummy gcc; ac_word=$2
+echo $ac_n "checking for $ac_word""... $ac_c" 1>&6
+echo "configure:567: checking for $ac_word" >&5
+if eval "test \"`echo '$''{'ac_cv_prog_CC'+set}'`\" = set"; then
+  echo $ac_n "(cached) $ac_c" 1>&6
+else
+  if test -n "$CC"; then
+  ac_cv_prog_CC="$CC" # Let the user override the test.
+else
+  IFS="${IFS=  }"; ac_save_ifs="$IFS"; IFS=":"
+  ac_dummy="$PATH"
+  for ac_dir in $ac_dummy; do
+    test -z "$ac_dir" && ac_dir=.
+    if test -f $ac_dir/$ac_word; then
+      ac_cv_prog_CC="gcc"
+      break
+    fi
+  done
+  IFS="$ac_save_ifs"
+fi
+fi
+CC="$ac_cv_prog_CC"
+if test -n "$CC"; then
+  echo "$ac_t""$CC" 1>&6
+else
+  echo "$ac_t""no" 1>&6
+fi
+
+if test -z "$CC"; then
+  # Extract the first word of "cc", so it can be a program name with args.
+set dummy cc; ac_word=$2
+echo $ac_n "checking for $ac_word""... $ac_c" 1>&6
+echo "configure:597: checking for $ac_word" >&5
+if eval "test \"`echo '$''{'ac_cv_prog_CC'+set}'`\" = set"; then
+  echo $ac_n "(cached) $ac_c" 1>&6
+else
+  if test -n "$CC"; then
+  ac_cv_prog_CC="$CC" # Let the user override the test.
+else
+  IFS="${IFS=  }"; ac_save_ifs="$IFS"; IFS=":"
+  ac_prog_rejected=no
+  ac_dummy="$PATH"
+  for ac_dir in $ac_dummy; do
+    test -z "$ac_dir" && ac_dir=.
+    if test -f $ac_dir/$ac_word; then
+      if test "$ac_dir/$ac_word" = "/usr/ucb/cc"; then
+        ac_prog_rejected=yes
+       continue
+      fi
+      ac_cv_prog_CC="cc"
+      break
+    fi
+  done
+  IFS="$ac_save_ifs"
+if test $ac_prog_rejected = yes; then
+  # We found a bogon in the path, so make sure we never use it.
+  set dummy $ac_cv_prog_CC
+  shift
+  if test $# -gt 0; then
+    # We chose a different compiler from the bogus one.
+    # However, it has the same basename, so the bogon will be chosen
+    # first if we set CC to just the basename; use the full file name.
+    shift
+    set dummy "$ac_dir/$ac_word" "$@"
+    shift
+    ac_cv_prog_CC="$@"
+  fi
+fi
+fi
+fi
+CC="$ac_cv_prog_CC"
+if test -n "$CC"; then
+  echo "$ac_t""$CC" 1>&6
+else
+  echo "$ac_t""no" 1>&6
+fi
+
+  if test -z "$CC"; then
+    case "`uname -s`" in
+    *win32* | *WIN32*)
+      # Extract the first word of "cl", so it can be a program name with args.
+set dummy cl; ac_word=$2
+echo $ac_n "checking for $ac_word""... $ac_c" 1>&6
+echo "configure:648: checking for $ac_word" >&5
+if eval "test \"`echo '$''{'ac_cv_prog_CC'+set}'`\" = set"; then
+  echo $ac_n "(cached) $ac_c" 1>&6
+else
+  if test -n "$CC"; then
+  ac_cv_prog_CC="$CC" # Let the user override the test.
+else
+  IFS="${IFS=  }"; ac_save_ifs="$IFS"; IFS=":"
+  ac_dummy="$PATH"
+  for ac_dir in $ac_dummy; do
+    test -z "$ac_dir" && ac_dir=.
+    if test -f $ac_dir/$ac_word; then
+      ac_cv_prog_CC="cl"
+      break
+    fi
+  done
+  IFS="$ac_save_ifs"
+fi
+fi
+CC="$ac_cv_prog_CC"
+if test -n "$CC"; then
+  echo "$ac_t""$CC" 1>&6
+else
+  echo "$ac_t""no" 1>&6
+fi
+ ;;
+    esac
+  fi
+  test -z "$CC" && { echo "configure: error: no acceptable cc found in \$PATH" 1>&2; exit 1; }
+fi
+
+echo $ac_n "checking whether the C compiler ($CC $CFLAGS $LDFLAGS) works""... $ac_c" 1>&6
+echo "configure:680: checking whether the C compiler ($CC $CFLAGS $LDFLAGS) works" >&5
+
+ac_ext=c
+# CFLAGS is not in ac_cpp because -g, -O, etc. are not valid cpp options.
+ac_cpp='$CPP $CPPFLAGS'
+ac_compile='${CC-cc} -c $CFLAGS $CPPFLAGS conftest.$ac_ext 1>&5'
+ac_link='${CC-cc} -o conftest${ac_exeext} $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS 1>&5'
+cross_compiling=$ac_cv_prog_cc_cross
+
+cat > conftest.$ac_ext << EOF
+
+#line 691 "configure"
+#include "confdefs.h"
+
+main(){return(0);}
+EOF
+if { (eval echo configure:696: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; } && test -s conftest${ac_exeext}; then
+  ac_cv_prog_cc_works=yes
+  # If we can't run a trivial program, we are probably using a cross compiler.
+  if (./conftest; exit) 2>/dev/null; then
+    ac_cv_prog_cc_cross=no
+  else
+    ac_cv_prog_cc_cross=yes
+  fi
+else
+  echo "configure: failed program was:" >&5
+  cat conftest.$ac_ext >&5
+  ac_cv_prog_cc_works=no
+fi
+rm -fr conftest*
+ac_ext=c
+# CFLAGS is not in ac_cpp because -g, -O, etc. are not valid cpp options.
+ac_cpp='$CPP $CPPFLAGS'
+ac_compile='${CC-cc} -c $CFLAGS $CPPFLAGS conftest.$ac_ext 1>&5'
+ac_link='${CC-cc} -o conftest${ac_exeext} $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS 1>&5'
+cross_compiling=$ac_cv_prog_cc_cross
+
+echo "$ac_t""$ac_cv_prog_cc_works" 1>&6
+if test $ac_cv_prog_cc_works = no; then
+  { echo "configure: error: installation or configuration problem: C compiler cannot create executables." 1>&2; exit 1; }
+fi
+echo $ac_n "checking whether the C compiler ($CC $CFLAGS $LDFLAGS) is a cross-compiler""... $ac_c" 1>&6
+echo "configure:722: checking whether the C compiler ($CC $CFLAGS $LDFLAGS) is a cross-compiler" >&5
+echo "$ac_t""$ac_cv_prog_cc_cross" 1>&6
+cross_compiling=$ac_cv_prog_cc_cross
+
+echo $ac_n "checking whether we are using GNU C""... $ac_c" 1>&6
+echo "configure:727: checking whether we are using GNU C" >&5
+if eval "test \"`echo '$''{'ac_cv_prog_gcc'+set}'`\" = set"; then
+  echo $ac_n "(cached) $ac_c" 1>&6
+else
+  cat > conftest.c <<EOF
+#ifdef __GNUC__
+  yes;
+#endif
+EOF
+if { ac_try='${CC-cc} -E conftest.c'; { (eval echo configure:736: \"$ac_try\") 1>&5; (eval $ac_try) 2>&5; }; } | egrep yes >/dev/null 2>&1; then
+  ac_cv_prog_gcc=yes
+else
+  ac_cv_prog_gcc=no
+fi
+fi
+
+echo "$ac_t""$ac_cv_prog_gcc" 1>&6
+
+if test $ac_cv_prog_gcc = yes; then
+  GCC=yes
+else
+  GCC=
+fi
+
+ac_test_CFLAGS="${CFLAGS+set}"
+ac_save_CFLAGS="$CFLAGS"
+CFLAGS=
+echo $ac_n "checking whether ${CC-cc} accepts -g""... $ac_c" 1>&6
+echo "configure:755: checking whether ${CC-cc} accepts -g" >&5
+if eval "test \"`echo '$''{'ac_cv_prog_cc_g'+set}'`\" = set"; then
+  echo $ac_n "(cached) $ac_c" 1>&6
+else
+  echo 'void f(){}' > conftest.c
+if test -z "`${CC-cc} -g -c conftest.c 2>&1`"; then
+  ac_cv_prog_cc_g=yes
+else
+  ac_cv_prog_cc_g=no
+fi
+rm -f conftest*
+
+fi
+
+echo "$ac_t""$ac_cv_prog_cc_g" 1>&6
+if test "$ac_test_CFLAGS" = set; then
+  CFLAGS="$ac_save_CFLAGS"
+elif test $ac_cv_prog_cc_g = yes; then
+  if test "$GCC" = yes; then
+    CFLAGS="-g -O2"
+  else
+    CFLAGS="-g"
+  fi
+else
+  if test "$GCC" = yes; then
+    CFLAGS="-O2"
+  else
+    CFLAGS=
+  fi
+fi
+
+# Extract the first word of "rm", so it can be a program name with args.
+set dummy rm; ac_word=$2
+echo $ac_n "checking for $ac_word""... $ac_c" 1>&6
+echo "configure:789: checking for $ac_word" >&5
+if eval "test \"`echo '$''{'ac_cv_path_RM'+set}'`\" = set"; then
+  echo $ac_n "(cached) $ac_c" 1>&6
+else
+  case "$RM" in
+  /*)
+  ac_cv_path_RM="$RM" # Let the user override the test with a path.
+  ;;
+  ?:/*)                         
+  ac_cv_path_RM="$RM" # Let the user override the test with a dos path.
+  ;;
+  *)
+  IFS="${IFS=  }"; ac_save_ifs="$IFS"; IFS=":"
+  ac_dummy="$PATH"
+  for ac_dir in $ac_dummy; do 
+    test -z "$ac_dir" && ac_dir=.
+    if test -f $ac_dir/$ac_word; then
+      ac_cv_path_RM="$ac_dir/$ac_word"
+      break
+    fi
+  done
+  IFS="$ac_save_ifs"
+  ;;
+esac
+fi
+RM="$ac_cv_path_RM"
+if test -n "$RM"; then
+  echo "$ac_t""$RM" 1>&6
+else
+  echo "$ac_t""no" 1>&6
+fi
+
+echo $ac_n "checking how to run the C preprocessor""... $ac_c" 1>&6
+echo "configure:822: checking how to run the C preprocessor" >&5
+# On Suns, sometimes $CPP names a directory.
+if test -n "$CPP" && test -d "$CPP"; then
+  CPP=
+fi
+if test -z "$CPP"; then
+if eval "test \"`echo '$''{'ac_cv_prog_CPP'+set}'`\" = set"; then
+  echo $ac_n "(cached) $ac_c" 1>&6
+else
+    # This must be in double quotes, not single quotes, because CPP may get
+  # substituted into the Makefile and "${CC-cc}" will confuse make.
+  CPP="${CC-cc} -E"
+  # On the NeXT, cc -E runs the code through the compiler's parser,
+  # not just through cpp.
+  cat > conftest.$ac_ext <<EOF
+#line 837 "configure"
+#include "confdefs.h"
+#include <assert.h>
+Syntax Error
+EOF
+ac_try="$ac_cpp conftest.$ac_ext >/dev/null 2>conftest.out"
+{ (eval echo configure:843: \"$ac_try\") 1>&5; (eval $ac_try) 2>&5; }
+ac_err=`grep -v '^ *+' conftest.out | grep -v "^conftest.${ac_ext}\$"`
+if test -z "$ac_err"; then
+  :
+else
+  echo "$ac_err" >&5
+  echo "configure: failed program was:" >&5
+  cat conftest.$ac_ext >&5
+  rm -rf conftest*
+  CPP="${CC-cc} -E -traditional-cpp"
+  cat > conftest.$ac_ext <<EOF
+#line 854 "configure"
+#include "confdefs.h"
+#include <assert.h>
+Syntax Error
+EOF
+ac_try="$ac_cpp conftest.$ac_ext >/dev/null 2>conftest.out"
+{ (eval echo configure:860: \"$ac_try\") 1>&5; (eval $ac_try) 2>&5; }
+ac_err=`grep -v '^ *+' conftest.out | grep -v "^conftest.${ac_ext}\$"`
+if test -z "$ac_err"; then
+  :
+else
+  echo "$ac_err" >&5
+  echo "configure: failed program was:" >&5
+  cat conftest.$ac_ext >&5
+  rm -rf conftest*
+  CPP="${CC-cc} -nologo -E"
+  cat > conftest.$ac_ext <<EOF
+#line 871 "configure"
+#include "confdefs.h"
+#include <assert.h>
+Syntax Error
+EOF
+ac_try="$ac_cpp conftest.$ac_ext >/dev/null 2>conftest.out"
+{ (eval echo configure:877: \"$ac_try\") 1>&5; (eval $ac_try) 2>&5; }
+ac_err=`grep -v '^ *+' conftest.out | grep -v "^conftest.${ac_ext}\$"`
+if test -z "$ac_err"; then
+  :
+else
+  echo "$ac_err" >&5
+  echo "configure: failed program was:" >&5
+  cat conftest.$ac_ext >&5
+  rm -rf conftest*
+  CPP=/lib/cpp
+fi
+rm -f conftest*
+fi
+rm -f conftest*
+fi
+rm -f conftest*
+  ac_cv_prog_CPP="$CPP"
+fi
+  CPP="$ac_cv_prog_CPP"
+else
+  ac_cv_prog_CPP="$CPP"
+fi
+echo "$ac_t""$CPP" 1>&6
+
+echo $ac_n "checking for ANSI C header files""... $ac_c" 1>&6
+echo "configure:902: checking for ANSI C header files" >&5
+if eval "test \"`echo '$''{'ac_cv_header_stdc'+set}'`\" = set"; then
+  echo $ac_n "(cached) $ac_c" 1>&6
+else
+  cat > conftest.$ac_ext <<EOF
+#line 907 "configure"
+#include "confdefs.h"
+#include <stdlib.h>
+#include <stdarg.h>
+#include <string.h>
+#include <float.h>
+EOF
+ac_try="$ac_cpp conftest.$ac_ext >/dev/null 2>conftest.out"
+{ (eval echo configure:915: \"$ac_try\") 1>&5; (eval $ac_try) 2>&5; }
+ac_err=`grep -v '^ *+' conftest.out | grep -v "^conftest.${ac_ext}\$"`
+if test -z "$ac_err"; then
+  rm -rf conftest*
+  ac_cv_header_stdc=yes
+else
+  echo "$ac_err" >&5
+  echo "configure: failed program was:" >&5
+  cat conftest.$ac_ext >&5
+  rm -rf conftest*
+  ac_cv_header_stdc=no
+fi
+rm -f conftest*
+
+if test $ac_cv_header_stdc = yes; then
+  # SunOS 4.x string.h does not declare mem*, contrary to ANSI.
+cat > conftest.$ac_ext <<EOF
+#line 932 "configure"
+#include "confdefs.h"
+#include <string.h>
+EOF
+if (eval "$ac_cpp conftest.$ac_ext") 2>&5 |
+  egrep "memchr" >/dev/null 2>&1; then
+  :
+else
+  rm -rf conftest*
+  ac_cv_header_stdc=no
+fi
+rm -f conftest*
+
+fi
+
+if test $ac_cv_header_stdc = yes; then
+  # ISC 2.0.2 stdlib.h does not declare free, contrary to ANSI.
+cat > conftest.$ac_ext <<EOF
+#line 950 "configure"
+#include "confdefs.h"
+#include <stdlib.h>
+EOF
+if (eval "$ac_cpp conftest.$ac_ext") 2>&5 |
+  egrep "free" >/dev/null 2>&1; then
+  :
+else
+  rm -rf conftest*
+  ac_cv_header_stdc=no
+fi
+rm -f conftest*
+
+fi
+
+if test $ac_cv_header_stdc = yes; then
+  # /bin/cc in Irix-4.0.5 gets non-ANSI ctype macros unless using -ansi.
+if test "$cross_compiling" = yes; then
+  :
+else
+  cat > conftest.$ac_ext <<EOF
+#line 971 "configure"
+#include "confdefs.h"
+#include <ctype.h>
+#define ISLOWER(c) ('a' <= (c) && (c) <= 'z')
+#define TOUPPER(c) (ISLOWER(c) ? 'A' + ((c) - 'a') : (c))
+#define XOR(e, f) (((e) && !(f)) || (!(e) && (f)))
+int main () { int i; for (i = 0; i < 256; i++)
+if (XOR (islower (i), ISLOWER (i)) || toupper (i) != TOUPPER (i)) exit(2);
+exit (0); }
+
+EOF
+if { (eval echo configure:982: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; } && test -s conftest${ac_exeext} && (./conftest; exit) 2>/dev/null
+then
+  :
+else
+  echo "configure: failed program was:" >&5
+  cat conftest.$ac_ext >&5
+  rm -fr conftest*
+  ac_cv_header_stdc=no
+fi
+rm -fr conftest*
+fi
+
+fi
+fi
+
+echo "$ac_t""$ac_cv_header_stdc" 1>&6
+if test $ac_cv_header_stdc = yes; then
+  cat >> confdefs.h <<\EOF
+#define STDC_HEADERS 1
+EOF
+
+fi
+
+ac_aux_dir=
+for ac_dir in $srcdir $srcdir/.. $srcdir/../..; do
+  if test -f $ac_dir/install-sh; then
+    ac_aux_dir=$ac_dir
+    ac_install_sh="$ac_aux_dir/install-sh -c"
+    break
+  elif test -f $ac_dir/install.sh; then
+    ac_aux_dir=$ac_dir
+    ac_install_sh="$ac_aux_dir/install.sh -c"
+    break
+  fi
+done
+if test -z "$ac_aux_dir"; then
+  { echo "configure: error: can not find install-sh or install.sh in $srcdir $srcdir/.. $srcdir/../.." 1>&2; exit 1; }
+fi
+ac_config_guess=$ac_aux_dir/config.guess
+ac_config_sub=$ac_aux_dir/config.sub
+ac_configure=$ac_aux_dir/configure # This should be Cygnus configure.
+
+# Find a good install program.  We prefer a C program (faster),
+# so one script is as good as another.  But avoid the broken or
+# incompatible versions:
+# SysV /etc/install, /usr/sbin/install
+# SunOS /usr/etc/install
+# IRIX /sbin/install
+# AIX /bin/install
+# AIX 4 /usr/bin/installbsd, which doesn't work without a -g flag
+# AFS /usr/afsws/bin/install, which mishandles nonexistent args
+# SVR4 /usr/ucb/install, which tries to use the nonexistent group "staff"
+# ./install, which can be erroneously created by make from ./install.sh.
+echo $ac_n "checking for a BSD compatible install""... $ac_c" 1>&6
+echo "configure:1036: checking for a BSD compatible install" >&5
+if test -z "$INSTALL"; then
+if eval "test \"`echo '$''{'ac_cv_path_install'+set}'`\" = set"; then
+  echo $ac_n "(cached) $ac_c" 1>&6
+else
+    IFS="${IFS=        }"; ac_save_IFS="$IFS"; IFS=":"
+  for ac_dir in $PATH; do
+    # Account for people who put trailing slashes in PATH elements.
+    case "$ac_dir/" in
+    /|./|.//|/etc/*|/usr/sbin/*|/usr/etc/*|/sbin/*|/usr/afsws/bin/*|/usr/ucb/*) ;;
+    *)
+      # OSF1 and SCO ODT 3.0 have their own names for install.
+      # Don't use installbsd from OSF since it installs stuff as root
+      # by default.
+      for ac_prog in ginstall scoinst install; do
+        if test -f $ac_dir/$ac_prog; then
+         if test $ac_prog = install &&
+            grep dspmsg $ac_dir/$ac_prog >/dev/null 2>&1; then
+           # AIX install.  It has an incompatible calling convention.
+           :
+         else
+           ac_cv_path_install="$ac_dir/$ac_prog -c"
+           break 2
+         fi
+       fi
+      done
+      ;;
+    esac
+  done
+  IFS="$ac_save_IFS"
+
+fi
+  if test "${ac_cv_path_install+set}" = set; then
+    INSTALL="$ac_cv_path_install"
+  else
+    # As a last resort, use the slow shell script.  We don't cache a
+    # path for INSTALL within a source directory, because that will
+    # break other packages using the cache if that directory is
+    # removed, or if the path is relative.
+    INSTALL="$ac_install_sh"
+  fi
+fi
+echo "$ac_t""$INSTALL" 1>&6
+
+# Use test -z because SunOS4 sh mishandles braces in ${var-val}.
+# It thinks the first close brace ends the variable substitution.
+test -z "$INSTALL_PROGRAM" && INSTALL_PROGRAM='${INSTALL}'
+
+test -z "$INSTALL_SCRIPT" && INSTALL_SCRIPT='${INSTALL_PROGRAM}'
+
+test -z "$INSTALL_DATA" && INSTALL_DATA='${INSTALL} -m 644'
+
+echo $ac_n "checking for pid_t""... $ac_c" 1>&6
+echo "configure:1089: checking for pid_t" >&5
+if eval "test \"`echo '$''{'ac_cv_type_pid_t'+set}'`\" = set"; then
+  echo $ac_n "(cached) $ac_c" 1>&6
+else
+  cat > conftest.$ac_ext <<EOF
+#line 1094 "configure"
+#include "confdefs.h"
+#include <sys/types.h>
+#if STDC_HEADERS
+#include <stdlib.h>
+#include <stddef.h>
+#endif
+EOF
+if (eval "$ac_cpp conftest.$ac_ext") 2>&5 |
+  egrep "(^|[^a-zA-Z_0-9])pid_t[^a-zA-Z_0-9]" >/dev/null 2>&1; then
+  rm -rf conftest*
+  ac_cv_type_pid_t=yes
+else
+  rm -rf conftest*
+  ac_cv_type_pid_t=no
+fi
+rm -f conftest*
+
+fi
+echo "$ac_t""$ac_cv_type_pid_t" 1>&6
+if test $ac_cv_type_pid_t = no; then
+  cat >> confdefs.h <<\EOF
+#define pid_t int
+EOF
+
+fi
+
+echo $ac_n "checking for size_t""... $ac_c" 1>&6
+echo "configure:1122: checking for size_t" >&5
+if eval "test \"`echo '$''{'ac_cv_type_size_t'+set}'`\" = set"; then
+  echo $ac_n "(cached) $ac_c" 1>&6
+else
+  cat > conftest.$ac_ext <<EOF
+#line 1127 "configure"
+#include "confdefs.h"
+#include <sys/types.h>
+#if STDC_HEADERS
+#include <stdlib.h>
+#include <stddef.h>
+#endif
+EOF
+if (eval "$ac_cpp conftest.$ac_ext") 2>&5 |
+  egrep "(^|[^a-zA-Z_0-9])size_t[^a-zA-Z_0-9]" >/dev/null 2>&1; then
+  rm -rf conftest*
+  ac_cv_type_size_t=yes
+else
+  rm -rf conftest*
+  ac_cv_type_size_t=no
+fi
+rm -f conftest*
+
+fi
+echo "$ac_t""$ac_cv_type_size_t" 1>&6
+if test $ac_cv_type_size_t = no; then
+  cat >> confdefs.h <<\EOF
+#define size_t unsigned
+EOF
+
+fi
+
+echo $ac_n "checking for vprintf""... $ac_c" 1>&6
+echo "configure:1155: checking for vprintf" >&5
+if eval "test \"`echo '$''{'ac_cv_func_vprintf'+set}'`\" = set"; then
+  echo $ac_n "(cached) $ac_c" 1>&6
+else
+  cat > conftest.$ac_ext <<EOF
+#line 1160 "configure"
+#include "confdefs.h"
+/* System header to define __stub macros and hopefully few prototypes,
+    which can conflict with char vprintf(); below.  */
+#include <assert.h>
+/* Override any gcc2 internal prototype to avoid an error.  */
+/* We use char because int might match the return type of a gcc2
+    builtin and then its argument prototype would still apply.  */
+char vprintf();
+
+int main() {
+
+/* The GNU C library defines this for functions which it implements
+    to always fail with ENOSYS.  Some functions are actually named
+    something starting with __ and the normal name is an alias.  */
+#if defined (__stub_vprintf) || defined (__stub___vprintf)
+choke me
+#else
+vprintf();
+#endif
+
+; return 0; }
+EOF
+if { (eval echo configure:1183: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; } && test -s conftest${ac_exeext}; then
+  rm -rf conftest*
+  eval "ac_cv_func_vprintf=yes"
+else
+  echo "configure: failed program was:" >&5
+  cat conftest.$ac_ext >&5
+  rm -rf conftest*
+  eval "ac_cv_func_vprintf=no"
+fi
+rm -f conftest*
+fi
+
+if eval "test \"`echo '$ac_cv_func_'vprintf`\" = yes"; then
+  echo "$ac_t""yes" 1>&6
+  cat >> confdefs.h <<\EOF
+#define HAVE_VPRINTF 1
+EOF
+
+else
+  echo "$ac_t""no" 1>&6
+fi
+
+if test "$ac_cv_func_vprintf" != yes; then
+echo $ac_n "checking for _doprnt""... $ac_c" 1>&6
+echo "configure:1207: checking for _doprnt" >&5
+if eval "test \"`echo '$''{'ac_cv_func__doprnt'+set}'`\" = set"; then
+  echo $ac_n "(cached) $ac_c" 1>&6
+else
+  cat > conftest.$ac_ext <<EOF
+#line 1212 "configure"
+#include "confdefs.h"
+/* System header to define __stub macros and hopefully few prototypes,
+    which can conflict with char _doprnt(); below.  */
+#include <assert.h>
+/* Override any gcc2 internal prototype to avoid an error.  */
+/* We use char because int might match the return type of a gcc2
+    builtin and then its argument prototype would still apply.  */
+char _doprnt();
+
+int main() {
+
+/* The GNU C library defines this for functions which it implements
+    to always fail with ENOSYS.  Some functions are actually named
+    something starting with __ and the normal name is an alias.  */
+#if defined (__stub__doprnt) || defined (__stub____doprnt)
+choke me
+#else
+_doprnt();
+#endif
+
+; return 0; }
+EOF
+if { (eval echo configure:1235: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; } && test -s conftest${ac_exeext}; then
+  rm -rf conftest*
+  eval "ac_cv_func__doprnt=yes"
+else
+  echo "configure: failed program was:" >&5
+  cat conftest.$ac_ext >&5
+  rm -rf conftest*
+  eval "ac_cv_func__doprnt=no"
+fi
+rm -f conftest*
+fi
+
+if eval "test \"`echo '$ac_cv_func_'_doprnt`\" = yes"; then
+  echo "$ac_t""yes" 1>&6
+  cat >> confdefs.h <<\EOF
+#define HAVE_DOPRNT 1
+EOF
+
+else
+  echo "$ac_t""no" 1>&6
+fi
+
+fi
+
+echo $ac_n "checking for working const""... $ac_c" 1>&6
+echo "configure:1260: checking for working const" >&5
+if eval "test \"`echo '$''{'ac_cv_c_const'+set}'`\" = set"; then
+  echo $ac_n "(cached) $ac_c" 1>&6
+else
+  cat > conftest.$ac_ext <<EOF
+#line 1265 "configure"
+#include "confdefs.h"
+
+int main() {
+
+/* Ultrix mips cc rejects this.  */
+typedef int charset[2]; const charset x;
+/* SunOS 4.1.1 cc rejects this.  */
+char const *const *ccp;
+char **p;
+/* NEC SVR4.0.2 mips cc rejects this.  */
+struct point {int x, y;};
+static struct point const zero = {0,0};
+/* AIX XL C 1.02.0.0 rejects this.
+   It does not let you subtract one const X* pointer from another in an arm
+   of an if-expression whose if-part is not a constant expression */
+const char *g = "string";
+ccp = &g + (g ? g-g : 0);
+/* HPUX 7.0 cc rejects these. */
+++ccp;
+p = (char**) ccp;
+ccp = (char const *const *) p;
+{ /* SCO 3.2v4 cc rejects this.  */
+  char *t;
+  char const *s = 0 ? (char *) 0 : (char const *) 0;
+
+  *t++ = 0;
+}
+{ /* Someone thinks the Sun supposedly-ANSI compiler will reject this.  */
+  int x[] = {25, 17};
+  const int *foo = &x[0];
+  ++foo;
+}
+{ /* Sun SC1.0 ANSI compiler rejects this -- but not the above. */
+  typedef const int *iptr;
+  iptr p = 0;
+  ++p;
+}
+{ /* AIX XL C 1.02.0.0 rejects this saying
+     "k.c", line 2.27: 1506-025 (S) Operand must be a modifiable lvalue. */
+  struct s { int j; const int *ap[3]; };
+  struct s *b; b->j = 5;
+}
+{ /* ULTRIX-32 V3.1 (Rev 9) vcc rejects this */
+  const int foo = 10;
+}
+
+; return 0; }
+EOF
+if { (eval echo configure:1314: \"$ac_compile\") 1>&5; (eval $ac_compile) 2>&5; }; then
+  rm -rf conftest*
+  ac_cv_c_const=yes
+else
+  echo "configure: failed program was:" >&5
+  cat conftest.$ac_ext >&5
+  rm -rf conftest*
+  ac_cv_c_const=no
+fi
+rm -f conftest*
+fi
+
+echo "$ac_t""$ac_cv_c_const" 1>&6
+if test $ac_cv_c_const = no; then
+  cat >> confdefs.h <<\EOF
+#define const 
+EOF
+
+fi
+
+echo $ac_n "checking whether byte ordering is bigendian""... $ac_c" 1>&6
+echo "configure:1335: checking whether byte ordering is bigendian" >&5
+if eval "test \"`echo '$''{'ac_cv_c_bigendian'+set}'`\" = set"; then
+  echo $ac_n "(cached) $ac_c" 1>&6
+else
+  ac_cv_c_bigendian=unknown
+# See if sys/param.h defines the BYTE_ORDER macro.
+cat > conftest.$ac_ext <<EOF
+#line 1342 "configure"
+#include "confdefs.h"
+#include <sys/types.h>
+#include <sys/param.h>
+int main() {
+
+#if !BYTE_ORDER || !BIG_ENDIAN || !LITTLE_ENDIAN
+ bogus endian macros
+#endif
+; return 0; }
+EOF
+if { (eval echo configure:1353: \"$ac_compile\") 1>&5; (eval $ac_compile) 2>&5; }; then
+  rm -rf conftest*
+  # It does; now see whether it defined to BIG_ENDIAN or not.
+cat > conftest.$ac_ext <<EOF
+#line 1357 "configure"
+#include "confdefs.h"
+#include <sys/types.h>
+#include <sys/param.h>
+int main() {
+
+#if BYTE_ORDER != BIG_ENDIAN
+ not big endian
+#endif
+; return 0; }
+EOF
+if { (eval echo configure:1368: \"$ac_compile\") 1>&5; (eval $ac_compile) 2>&5; }; then
+  rm -rf conftest*
+  ac_cv_c_bigendian=yes
+else
+  echo "configure: failed program was:" >&5
+  cat conftest.$ac_ext >&5
+  rm -rf conftest*
+  ac_cv_c_bigendian=no
+fi
+rm -f conftest*
+else
+  echo "configure: failed program was:" >&5
+  cat conftest.$ac_ext >&5
+fi
+rm -f conftest*
+if test $ac_cv_c_bigendian = unknown; then
+if test "$cross_compiling" = yes; then
+    { echo "configure: error: can not run test program while cross compiling" 1>&2; exit 1; }
+else
+  cat > conftest.$ac_ext <<EOF
+#line 1388 "configure"
+#include "confdefs.h"
+main () {
+  /* Are we little or big endian?  From Harbison&Steele.  */
+  union
+  {
+    long l;
+    char c[sizeof (long)];
+  } u;
+  u.l = 1;
+  exit (u.c[sizeof (long) - 1] == 1);
+}
+EOF
+if { (eval echo configure:1401: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; } && test -s conftest${ac_exeext} && (./conftest; exit) 2>/dev/null
+then
+  ac_cv_c_bigendian=no
+else
+  echo "configure: failed program was:" >&5
+  cat conftest.$ac_ext >&5
+  rm -fr conftest*
+  ac_cv_c_bigendian=yes
+fi
+rm -fr conftest*
+fi
+
+fi
+fi
+
+echo "$ac_t""$ac_cv_c_bigendian" 1>&6
+if test $ac_cv_c_bigendian = yes; then
+  cat >> confdefs.h <<\EOF
+#define WORDS_BIGENDIAN 1
+EOF
+
+fi
+
+
+echo $ac_n "checking for mpz_init_set_str in -lgmp2""... $ac_c" 1>&6
+echo "configure:1426: checking for mpz_init_set_str in -lgmp2" >&5
+ac_lib_var=`echo gmp2'_'mpz_init_set_str | sed 'y%./+-%__p_%'`
+if eval "test \"`echo '$''{'ac_cv_lib_$ac_lib_var'+set}'`\" = set"; then
+  echo $ac_n "(cached) $ac_c" 1>&6
+else
+  ac_save_LIBS="$LIBS"
+LIBS="-lgmp2  $LIBS"
+cat > conftest.$ac_ext <<EOF
+#line 1434 "configure"
+#include "confdefs.h"
+/* Override any gcc2 internal prototype to avoid an error.  */
+/* We use char because int might match the return type of a gcc2
+    builtin and then its argument prototype would still apply.  */
+char mpz_init_set_str();
+
+int main() {
+mpz_init_set_str()
+; return 0; }
+EOF
+if { (eval echo configure:1445: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; } && test -s conftest${ac_exeext}; then
+  rm -rf conftest*
+  eval "ac_cv_lib_$ac_lib_var=yes"
+else
+  echo "configure: failed program was:" >&5
+  cat conftest.$ac_ext >&5
+  rm -rf conftest*
+  eval "ac_cv_lib_$ac_lib_var=no"
+fi
+rm -f conftest*
+LIBS="$ac_save_LIBS"
+
+fi
+if eval "test \"`echo '$ac_cv_lib_'$ac_lib_var`\" = yes"; then
+  echo "$ac_t""yes" 1>&6
+    ac_tr_lib=HAVE_LIB`echo gmp2 | sed -e 's/[^a-zA-Z0-9_]/_/g' \
+    -e 'y/abcdefghijklmnopqrstuvwxyz/ABCDEFGHIJKLMNOPQRSTUVWXYZ/'`
+  cat >> confdefs.h <<EOF
+#define $ac_tr_lib 1
+EOF
+
+  LIBS="-lgmp2 $LIBS"
+
+else
+  echo "$ac_t""no" 1>&6
+fi
+
+echo $ac_n "checking for yywrap in -lfl""... $ac_c" 1>&6
+echo "configure:1473: checking for yywrap in -lfl" >&5
+ac_lib_var=`echo fl'_'yywrap | sed 'y%./+-%__p_%'`
+if eval "test \"`echo '$''{'ac_cv_lib_$ac_lib_var'+set}'`\" = set"; then
+  echo $ac_n "(cached) $ac_c" 1>&6
+else
+  ac_save_LIBS="$LIBS"
+LIBS="-lfl  $LIBS"
+cat > conftest.$ac_ext <<EOF
+#line 1481 "configure"
+#include "confdefs.h"
+/* Override any gcc2 internal prototype to avoid an error.  */
+/* We use char because int might match the return type of a gcc2
+    builtin and then its argument prototype would still apply.  */
+char yywrap();
+
+int main() {
+yywrap()
+; return 0; }
+EOF
+if { (eval echo configure:1492: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; } && test -s conftest${ac_exeext}; then
+  rm -rf conftest*
+  eval "ac_cv_lib_$ac_lib_var=yes"
+else
+  echo "configure: failed program was:" >&5
+  cat conftest.$ac_ext >&5
+  rm -rf conftest*
+  eval "ac_cv_lib_$ac_lib_var=no"
+fi
+rm -f conftest*
+LIBS="$ac_save_LIBS"
+
+fi
+if eval "test \"`echo '$ac_cv_lib_'$ac_lib_var`\" = yes"; then
+  echo "$ac_t""yes" 1>&6
+    ac_tr_lib=HAVE_LIB`echo fl | sed -e 's/[^a-zA-Z0-9_]/_/g' \
+    -e 'y/abcdefghijklmnopqrstuvwxyz/ABCDEFGHIJKLMNOPQRSTUVWXYZ/'`
+  cat >> confdefs.h <<EOF
+#define $ac_tr_lib 1
+EOF
+
+  LIBS="-lfl $LIBS"
+
+else
+  echo "$ac_t""no" 1>&6
+fi
+
+echo $ac_n "checking for adns_init in -ladns""... $ac_c" 1>&6
+echo "configure:1520: checking for adns_init in -ladns" >&5
+ac_lib_var=`echo adns'_'adns_init | sed 'y%./+-%__p_%'`
+if eval "test \"`echo '$''{'ac_cv_lib_$ac_lib_var'+set}'`\" = set"; then
+  echo $ac_n "(cached) $ac_c" 1>&6
+else
+  ac_save_LIBS="$LIBS"
+LIBS="-ladns  $LIBS"
+cat > conftest.$ac_ext <<EOF
+#line 1528 "configure"
+#include "confdefs.h"
+/* Override any gcc2 internal prototype to avoid an error.  */
+/* We use char because int might match the return type of a gcc2
+    builtin and then its argument prototype would still apply.  */
+char adns_init();
+
+int main() {
+adns_init()
+; return 0; }
+EOF
+if { (eval echo configure:1539: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; } && test -s conftest${ac_exeext}; then
+  rm -rf conftest*
+  eval "ac_cv_lib_$ac_lib_var=yes"
+else
+  echo "configure: failed program was:" >&5
+  cat conftest.$ac_ext >&5
+  rm -rf conftest*
+  eval "ac_cv_lib_$ac_lib_var=no"
+fi
+rm -f conftest*
+LIBS="$ac_save_LIBS"
+
+fi
+if eval "test \"`echo '$ac_cv_lib_'$ac_lib_var`\" = yes"; then
+  echo "$ac_t""yes" 1>&6
+    ac_tr_lib=HAVE_LIB`echo adns | sed -e 's/[^a-zA-Z0-9_]/_/g' \
+    -e 'y/abcdefghijklmnopqrstuvwxyz/ABCDEFGHIJKLMNOPQRSTUVWXYZ/'`
+  cat >> confdefs.h <<EOF
+#define $ac_tr_lib 1
+EOF
+
+  LIBS="-ladns $LIBS"
+
+else
+  echo "$ac_t""no" 1>&6
+fi
+
+
+trap '' 1 2 15
+cat > confcache <<\EOF
+# This file is a shell script that caches the results of configure
+# tests run on this system so they can be shared between configure
+# scripts and configure runs.  It is not useful on other systems.
+# If it contains results you don't want to keep, you may remove or edit it.
+#
+# By default, configure uses ./config.cache as the cache file,
+# creating it if it does not exist already.  You can give configure
+# the --cache-file=FILE option to use a different cache file; that is
+# what configure does when it calls configure scripts in
+# subdirectories, so they share the cache.
+# Giving --cache-file=/dev/null disables caching, for debugging configure.
+# config.status only pays attention to the cache file if you give it the
+# --recheck option to rerun configure.
+#
+EOF
+# The following way of writing the cache mishandles newlines in values,
+# but we know of no workaround that is simple, portable, and efficient.
+# So, don't put newlines in cache variables' values.
+# Ultrix sh set writes to stderr and can't be redirected directly,
+# and sets the high bit in the cache file unless we assign to the vars.
+(set) 2>&1 |
+  case `(ac_space=' '; set | grep ac_space) 2>&1` in
+  *ac_space=\ *)
+    # `set' does not quote correctly, so add quotes (double-quote substitution
+    # turns \\\\ into \\, and sed turns \\ into \).
+    sed -n \
+      -e "s/'/'\\\\''/g" \
+      -e "s/^\\([a-zA-Z0-9_]*_cv_[a-zA-Z0-9_]*\\)=\\(.*\\)/\\1=\${\\1='\\2'}/p"
+    ;;
+  *)
+    # `set' quotes correctly as required by POSIX, so do not add quotes.
+    sed -n -e 's/^\([a-zA-Z0-9_]*_cv_[a-zA-Z0-9_]*\)=\(.*\)/\1=${\1=\2}/p'
+    ;;
+  esac >> confcache
+if cmp -s $cache_file confcache; then
+  :
+else
+  if test -w $cache_file; then
+    echo "updating cache $cache_file"
+    cat confcache > $cache_file
+  else
+    echo "not updating unwritable cache $cache_file"
+  fi
+fi
+rm -f confcache
+
+trap 'rm -fr conftest* confdefs* core core.* *.core $ac_clean_files; exit 1' 1 2 15
+
+test "x$prefix" = xNONE && prefix=$ac_default_prefix
+# Let make expand exec_prefix.
+test "x$exec_prefix" = xNONE && exec_prefix='${prefix}'
+
+# Any assignment to VPATH causes Sun make to only execute
+# the first set of double-colon rules, so remove it if not needed.
+# If there is a colon in the path, we need to keep it.
+if test "x$srcdir" = x.; then
+  ac_vpsub='/^[        ]*VPATH[        ]*=[^:]*$/d'
+fi
+
+trap 'rm -f $CONFIG_STATUS conftest*; exit 1' 1 2 15
+
+DEFS=-DHAVE_CONFIG_H
+
+# Without the "./", some shells look in PATH for config.status.
+: ${CONFIG_STATUS=./config.status}
+
+echo creating $CONFIG_STATUS
+rm -f $CONFIG_STATUS
+cat > $CONFIG_STATUS <<EOF
+#! /bin/sh
+# Generated automatically by configure.
+# Run this file to recreate the current configuration.
+# This directory was configured as follows,
+# on host `(hostname || uname -n) 2>/dev/null | sed 1q`:
+#
+# $0 $ac_configure_args
+#
+# Compiler output produced by configure, useful for debugging
+# configure, is in ./config.log if it exists.
+
+ac_cs_usage="Usage: $CONFIG_STATUS [--recheck] [--version] [--help]"
+for ac_option
+do
+  case "\$ac_option" in
+  -recheck | --recheck | --rechec | --reche | --rech | --rec | --re | --r)
+    echo "running \${CONFIG_SHELL-/bin/sh} $0 $ac_configure_args --no-create --no-recursion"
+    exec \${CONFIG_SHELL-/bin/sh} $0 $ac_configure_args --no-create --no-recursion ;;
+  -version | --version | --versio | --versi | --vers | --ver | --ve | --v)
+    echo "$CONFIG_STATUS generated by autoconf version 2.13"
+    exit 0 ;;
+  -help | --help | --hel | --he | --h)
+    echo "\$ac_cs_usage"; exit 0 ;;
+  *) echo "\$ac_cs_usage"; exit 1 ;;
+  esac
+done
+
+ac_given_srcdir=$srcdir
+ac_given_INSTALL="$INSTALL"
+
+trap 'rm -fr `echo "Makefile config.h" | sed "s/:[^ ]*//g"` conftest*; exit 1' 1 2 15
+EOF
+cat >> $CONFIG_STATUS <<EOF
+
+# Protect against being on the right side of a sed subst in config.status.
+sed 's/%@/@@/; s/@%/@@/; s/%g\$/@g/; /@g\$/s/[\\\\&%]/\\\\&/g;
+ s/@@/%@/; s/@@/@%/; s/@g\$/%g/' > conftest.subs <<\\CEOF
+$ac_vpsub
+$extrasub
+s%@SHELL@%$SHELL%g
+s%@CFLAGS@%$CFLAGS%g
+s%@CPPFLAGS@%$CPPFLAGS%g
+s%@CXXFLAGS@%$CXXFLAGS%g
+s%@FFLAGS@%$FFLAGS%g
+s%@DEFS@%$DEFS%g
+s%@LDFLAGS@%$LDFLAGS%g
+s%@LIBS@%$LIBS%g
+s%@exec_prefix@%$exec_prefix%g
+s%@prefix@%$prefix%g
+s%@program_transform_name@%$program_transform_name%g
+s%@bindir@%$bindir%g
+s%@sbindir@%$sbindir%g
+s%@libexecdir@%$libexecdir%g
+s%@datadir@%$datadir%g
+s%@sysconfdir@%$sysconfdir%g
+s%@sharedstatedir@%$sharedstatedir%g
+s%@localstatedir@%$localstatedir%g
+s%@libdir@%$libdir%g
+s%@includedir@%$includedir%g
+s%@oldincludedir@%$oldincludedir%g
+s%@infodir@%$infodir%g
+s%@mandir@%$mandir%g
+s%@SET_MAKE@%$SET_MAKE%g
+s%@CC@%$CC%g
+s%@RM@%$RM%g
+s%@CPP@%$CPP%g
+s%@INSTALL_PROGRAM@%$INSTALL_PROGRAM%g
+s%@INSTALL_SCRIPT@%$INSTALL_SCRIPT%g
+s%@INSTALL_DATA@%$INSTALL_DATA%g
+
+CEOF
+EOF
+
+cat >> $CONFIG_STATUS <<\EOF
+
+# Split the substitutions into bite-sized pieces for seds with
+# small command number limits, like on Digital OSF/1 and HP-UX.
+ac_max_sed_cmds=90 # Maximum number of lines to put in a sed script.
+ac_file=1 # Number of current file.
+ac_beg=1 # First line for current file.
+ac_end=$ac_max_sed_cmds # Line after last line for current file.
+ac_more_lines=:
+ac_sed_cmds=""
+while $ac_more_lines; do
+  if test $ac_beg -gt 1; then
+    sed "1,${ac_beg}d; ${ac_end}q" conftest.subs > conftest.s$ac_file
+  else
+    sed "${ac_end}q" conftest.subs > conftest.s$ac_file
+  fi
+  if test ! -s conftest.s$ac_file; then
+    ac_more_lines=false
+    rm -f conftest.s$ac_file
+  else
+    if test -z "$ac_sed_cmds"; then
+      ac_sed_cmds="sed -f conftest.s$ac_file"
+    else
+      ac_sed_cmds="$ac_sed_cmds | sed -f conftest.s$ac_file"
+    fi
+    ac_file=`expr $ac_file + 1`
+    ac_beg=$ac_end
+    ac_end=`expr $ac_end + $ac_max_sed_cmds`
+  fi
+done
+if test -z "$ac_sed_cmds"; then
+  ac_sed_cmds=cat
+fi
+EOF
+
+cat >> $CONFIG_STATUS <<EOF
+
+CONFIG_FILES=\${CONFIG_FILES-"Makefile"}
+EOF
+cat >> $CONFIG_STATUS <<\EOF
+for ac_file in .. $CONFIG_FILES; do if test "x$ac_file" != x..; then
+  # Support "outfile[:infile[:infile...]]", defaulting infile="outfile.in".
+  case "$ac_file" in
+  *:*) ac_file_in=`echo "$ac_file"|sed 's%[^:]*:%%'`
+       ac_file=`echo "$ac_file"|sed 's%:.*%%'` ;;
+  *) ac_file_in="${ac_file}.in" ;;
+  esac
+
+  # Adjust a relative srcdir, top_srcdir, and INSTALL for subdirectories.
+
+  # Remove last slash and all that follows it.  Not all systems have dirname.
+  ac_dir=`echo $ac_file|sed 's%/[^/][^/]*$%%'`
+  if test "$ac_dir" != "$ac_file" && test "$ac_dir" != .; then
+    # The file is in a subdirectory.
+    test ! -d "$ac_dir" && mkdir "$ac_dir"
+    ac_dir_suffix="/`echo $ac_dir|sed 's%^\./%%'`"
+    # A "../" for each directory in $ac_dir_suffix.
+    ac_dots=`echo $ac_dir_suffix|sed 's%/[^/]*%../%g'`
+  else
+    ac_dir_suffix= ac_dots=
+  fi
+
+  case "$ac_given_srcdir" in
+  .)  srcdir=.
+      if test -z "$ac_dots"; then top_srcdir=.
+      else top_srcdir=`echo $ac_dots|sed 's%/$%%'`; fi ;;
+  /*) srcdir="$ac_given_srcdir$ac_dir_suffix"; top_srcdir="$ac_given_srcdir" ;;
+  *) # Relative path.
+    srcdir="$ac_dots$ac_given_srcdir$ac_dir_suffix"
+    top_srcdir="$ac_dots$ac_given_srcdir" ;;
+  esac
+
+  case "$ac_given_INSTALL" in
+  [/$]*) INSTALL="$ac_given_INSTALL" ;;
+  *) INSTALL="$ac_dots$ac_given_INSTALL" ;;
+  esac
+
+  echo creating "$ac_file"
+  rm -f "$ac_file"
+  configure_input="Generated automatically from `echo $ac_file_in|sed 's%.*/%%'` by configure."
+  case "$ac_file" in
+  *Makefile*) ac_comsub="1i\\
+# $configure_input" ;;
+  *) ac_comsub= ;;
+  esac
+
+  ac_file_inputs=`echo $ac_file_in|sed -e "s%^%$ac_given_srcdir/%" -e "s%:% $ac_given_srcdir/%g"`
+  sed -e "$ac_comsub
+s%@configure_input@%$configure_input%g
+s%@srcdir@%$srcdir%g
+s%@top_srcdir@%$top_srcdir%g
+s%@INSTALL@%$INSTALL%g
+" $ac_file_inputs | (eval "$ac_sed_cmds") > $ac_file
+fi; done
+rm -f conftest.s*
+
+# These sed commands are passed to sed as "A NAME B NAME C VALUE D", where
+# NAME is the cpp macro being defined and VALUE is the value it is being given.
+#
+# ac_d sets the value in "#define NAME VALUE" lines.
+ac_dA='s%^\([  ]*\)#\([        ]*define[       ][      ]*\)'
+ac_dB='\([     ][      ]*\)[^  ]*%\1#\2'
+ac_dC='\3'
+ac_dD='%g'
+# ac_u turns "#undef NAME" with trailing blanks into "#define NAME VALUE".
+ac_uA='s%^\([  ]*\)#\([        ]*\)undef\([    ][      ]*\)'
+ac_uB='\([     ]\)%\1#\2define\3'
+ac_uC=' '
+ac_uD='\4%g'
+# ac_e turns "#undef NAME" without trailing blanks into "#define NAME VALUE".
+ac_eA='s%^\([  ]*\)#\([        ]*\)undef\([    ][      ]*\)'
+ac_eB='$%\1#\2define\3'
+ac_eC=' '
+ac_eD='%g'
+
+if test "${CONFIG_HEADERS+set}" != set; then
+EOF
+cat >> $CONFIG_STATUS <<EOF
+  CONFIG_HEADERS="config.h"
+EOF
+cat >> $CONFIG_STATUS <<\EOF
+fi
+for ac_file in .. $CONFIG_HEADERS; do if test "x$ac_file" != x..; then
+  # Support "outfile[:infile[:infile...]]", defaulting infile="outfile.in".
+  case "$ac_file" in
+  *:*) ac_file_in=`echo "$ac_file"|sed 's%[^:]*:%%'`
+       ac_file=`echo "$ac_file"|sed 's%:.*%%'` ;;
+  *) ac_file_in="${ac_file}.in" ;;
+  esac
+
+  echo creating $ac_file
+
+  rm -f conftest.frag conftest.in conftest.out
+  ac_file_inputs=`echo $ac_file_in|sed -e "s%^%$ac_given_srcdir/%" -e "s%:% $ac_given_srcdir/%g"`
+  cat $ac_file_inputs > conftest.in
+
+EOF
+
+# Transform confdefs.h into a sed script conftest.vals that substitutes
+# the proper values into config.h.in to produce config.h.  And first:
+# Protect against being on the right side of a sed subst in config.status.
+# Protect against being in an unquoted here document in config.status.
+rm -f conftest.vals
+cat > conftest.hdr <<\EOF
+s/[\\&%]/\\&/g
+s%[\\$`]%\\&%g
+s%#define \([A-Za-z_][A-Za-z0-9_]*\) *\(.*\)%${ac_dA}\1${ac_dB}\1${ac_dC}\2${ac_dD}%gp
+s%ac_d%ac_u%gp
+s%ac_u%ac_e%gp
+EOF
+sed -n -f conftest.hdr confdefs.h > conftest.vals
+rm -f conftest.hdr
+
+# This sed command replaces #undef with comments.  This is necessary, for
+# example, in the case of _POSIX_SOURCE, which is predefined and required
+# on some systems where configure will not decide to define it.
+cat >> conftest.vals <<\EOF
+s%^[   ]*#[    ]*undef[        ][      ]*[a-zA-Z_][a-zA-Z_0-9]*%/* & */%
+EOF
+
+# Break up conftest.vals because some shells have a limit on
+# the size of here documents, and old seds have small limits too.
+
+rm -f conftest.tail
+while :
+do
+  ac_lines=`grep -c . conftest.vals`
+  # grep -c gives empty output for an empty file on some AIX systems.
+  if test -z "$ac_lines" || test "$ac_lines" -eq 0; then break; fi
+  # Write a limited-size here document to conftest.frag.
+  echo '  cat > conftest.frag <<CEOF' >> $CONFIG_STATUS
+  sed ${ac_max_here_lines}q conftest.vals >> $CONFIG_STATUS
+  echo 'CEOF
+  sed -f conftest.frag conftest.in > conftest.out
+  rm -f conftest.in
+  mv conftest.out conftest.in
+' >> $CONFIG_STATUS
+  sed 1,${ac_max_here_lines}d conftest.vals > conftest.tail
+  rm -f conftest.vals
+  mv conftest.tail conftest.vals
+done
+rm -f conftest.vals
+
+cat >> $CONFIG_STATUS <<\EOF
+  rm -f conftest.frag conftest.h
+  echo "/* $ac_file.  Generated automatically by configure.  */" > conftest.h
+  cat conftest.in >> conftest.h
+  rm -f conftest.in
+  if cmp -s $ac_file conftest.h 2>/dev/null; then
+    echo "$ac_file is unchanged"
+    rm -f conftest.h
+  else
+    # Remove last slash and all that follows it.  Not all systems have dirname.
+      ac_dir=`echo $ac_file|sed 's%/[^/][^/]*$%%'`
+      if test "$ac_dir" != "$ac_file" && test "$ac_dir" != .; then
+      # The file is in a subdirectory.
+      test ! -d "$ac_dir" && mkdir "$ac_dir"
+    fi
+    rm -f $ac_file
+    mv conftest.h $ac_file
+  fi
+fi; done
+
+EOF
+cat >> $CONFIG_STATUS <<EOF
+
+EOF
+cat >> $CONFIG_STATUS <<\EOF
+
+exit 0
+EOF
+chmod +x $CONFIG_STATUS
+rm -fr confdefs* $ac_clean_files
+test "$no_create" = yes || ${CONFIG_SHELL-/bin/sh} $CONFIG_STATUS || exit 1
+
diff --git a/configure.in b/configure.in
new file mode 100644 (file)
index 0000000..6c85df9
--- /dev/null
@@ -0,0 +1,25 @@
+dnl Process this file with autoconf to produce a configure script.
+
+AC_INIT(secnet.c)
+AC_CONFIG_HEADER(config.h)
+
+AC_REVISION($Id$)
+
+AC_LANG_C
+
+AC_PROG_MAKE_SET
+AC_PROG_CC
+AC_PATH_PROG(RM,rm)
+AC_STDC_HEADERS
+AC_PROG_INSTALL
+AC_PID_T
+AC_SIZE_T
+AC_VPRINTF
+AC_C_CONST
+AC_C_BIGENDIAN
+
+AC_CHECK_LIB(gmp2,mpz_init_set_str)
+AC_CHECK_LIB(fl,yywrap)
+AC_CHECK_LIB(adns,adns_init)
+
+AC_OUTPUT(Makefile)
diff --git a/dh.c b/dh.c
new file mode 100644 (file)
index 0000000..eb9ae21
--- /dev/null
+++ b/dh.c
@@ -0,0 +1,151 @@
+/***************************************************************************
+ *
+ *              Part II Project, "A secure, private IP network"
+ *              Stephen Early <sde1000@cam.ac.uk>
+ *   
+ *
+ *     $RCSfile: dh.c,v $
+ *
+ *  Description: Diffie-Hellman implementation
+ *
+ *    Copyright: (C) Stephen Early 1995
+ *
+ *    $Revision: 1.3 $
+ *
+ *        $Date: 1996/05/16 18:38:54 $
+ *
+ *       $State: Exp $
+ *
+ ***************************************************************************/
+
+/*
+ * $Log: dh.c,v $
+ * Revision 1.3  1996/05/16 18:38:54  sde1000
+ * Removed unused hexdigits variable.
+ *
+ * Revision 1.2  1996/04/14 16:33:52  sde1000
+ * Moved mpbin/mpstring functions into util.c
+ *
+ * Revision 1.1  1996/04/14 16:21:47  sde1000
+ * Initial revision
+ *
+ */
+
+#include <stdio.h>
+#include <gmp.h>
+
+#include "secnet.h"
+#include "util.h"
+
+struct dh {
+    closure_t cl;
+    struct dh_if ops;
+    struct cloc loc;
+    MP_INT p,g; /* prime modulus and generator */
+};
+
+static string_t dh_makepublic(void *sst, uint8_t *secret, uint32_t secretlen)
+{
+    struct dh *st=sst;
+    string_t r;
+    MP_INT a, b; /* a is secret key, b is public key */
+
+    mpz_init(&a);
+    mpz_init(&b);
+
+    read_mpbin(&a, secret, secretlen);
+
+    mpz_powm(&b, &st->g, &a, &st->p);
+
+    r=write_mpstring(&b);
+
+    mpz_clear(&a);
+    mpz_clear(&b);
+    return r;
+}
+
+static void dh_makeshared(void *sst, uint8_t *secret, uint32_t secretlen,
+                         string_t rempublic, uint8_t *sharedsecret,
+                         uint32_t buflen)
+{
+    struct dh *st=sst;
+    MP_INT a, b, c;
+
+    mpz_init(&a);
+    mpz_init(&b);
+    mpz_init(&c);
+
+    read_mpbin(&a, secret, secretlen);
+    mpz_set_str(&b, rempublic, 16);
+
+    mpz_powm(&c, &b, &a, &st->p);
+
+    write_mpbin(&c,sharedsecret,buflen);
+
+    mpz_clear(&a);
+    mpz_clear(&b);
+    mpz_clear(&c);
+}
+
+static list_t *dh_apply(closure_t *self, struct cloc loc, dict_t *context,
+                       list_t *args)
+{
+    struct dh *st;
+    string_t p,g;
+    item_t *i;
+
+    st=safe_malloc(sizeof(*st),"dh_apply");
+    st->cl.description="dh";
+    st->cl.type=CL_DH;
+    st->cl.apply=NULL;
+    st->cl.interface=&st->ops;
+    st->ops.st=st;
+    st->ops.makepublic=dh_makepublic;
+    st->ops.makeshared=dh_makeshared;
+    st->loc=loc;
+    /* We have two string arguments: the first is the modulus, and the
+       second is the generator. Both are in hex. */
+    i=list_elem(args,0);
+    if (i) {
+       if (i->type!=t_string) {
+           cfgfatal(i->loc,"diffie-hellman","first argument must be a "
+                    "string\n");
+       }
+       p=i->data.string;
+       if (mpz_init_set_str(&st->p,p,16)!=0) {
+           cfgfatal(i->loc,"diffie-hellman","\"%s\" is not a hex number "
+                    "string\n",p);
+       }
+    } else {
+       cfgfatal(loc,"diffie-hellman","you must provide a prime modulus\n");
+    }
+    
+    i=list_elem(args,1);
+    if (i) {
+       if (i->type!=t_string) {
+           cfgfatal(i->loc,"diffie-hellman","second argument must be a "
+                    "string\n");
+       }
+       g=i->data.string;
+       if (mpz_init_set_str(&st->g,g,16)!=0) {
+           cfgfatal(i->loc,"diffie-hellman","\"%s\" is not a hex number "
+                    "string\n",g);
+       }
+    } else {
+       cfgfatal(loc,"diffie-hellman","you must provide a generator\n");
+    }
+
+    /* Test that the modulus is really prime */
+    if (mpz_probab_prime_p(&st->p,5)==0) {
+       cfgfatal(loc,"diffie-hellman","modulus must be a prime\n");
+    }
+    st->ops.len=mpz_sizeinbase(&st->p,2)/8;
+
+    return new_closure(&st->cl);
+}
+
+init_module dh_module;
+void dh_module(dict_t *dict)
+{
+    add_closure(dict,"diffie-hellman",dh_apply);
+}
diff --git a/install.sh b/install.sh
new file mode 100755 (executable)
index 0000000..0ff4b6a
--- /dev/null
@@ -0,0 +1,119 @@
+#!/bin/sh
+
+#
+# install - install a program, script, or datafile
+# This comes from X11R5; it is not part of GNU.
+#
+# $XConsortium: install.sh,v 1.2 89/12/18 14:47:22 jim Exp $
+#
+# This script is compatible with the BSD install script, but was written
+# from scratch.
+#
+
+
+# set DOITPROG to echo to test this script
+
+# Don't use :- since 4.3BSD and earlier shells don't like it.
+doit="${DOITPROG-}"
+
+
+# put in absolute paths if you don't have them in your path; or use env. vars.
+
+mvprog="${MVPROG-mv}"
+cpprog="${CPPROG-cp}"
+chmodprog="${CHMODPROG-chmod}"
+chownprog="${CHOWNPROG-chown}"
+chgrpprog="${CHGRPPROG-chgrp}"
+stripprog="${STRIPPROG-strip}"
+rmprog="${RMPROG-rm}"
+
+instcmd="$mvprog"
+chmodcmd=""
+chowncmd=""
+chgrpcmd=""
+stripcmd=""
+rmcmd="$rmprog -f"
+mvcmd="$mvprog"
+src=""
+dst=""
+
+while [ x"$1" != x ]; do
+    case $1 in
+       -c) instcmd="$cpprog"
+           shift
+           continue;;
+
+       -m) chmodcmd="$chmodprog $2"
+           shift
+           shift
+           continue;;
+
+       -o) chowncmd="$chownprog $2"
+           shift
+           shift
+           continue;;
+
+       -g) chgrpcmd="$chgrpprog $2"
+           shift
+           shift
+           continue;;
+
+       -s) stripcmd="$stripprog"
+           shift
+           continue;;
+
+       *)  if [ x"$src" = x ]
+           then
+               src=$1
+           else
+               dst=$1
+           fi
+           shift
+           continue;;
+    esac
+done
+
+if [ x"$src" = x ]
+then
+       echo "install:  no input file specified"
+       exit 1
+fi
+
+if [ x"$dst" = x ]
+then
+       echo "install:  no destination specified"
+       exit 1
+fi
+
+
+# If destination is a directory, append the input filename; if your system
+# does not like double slashes in filenames, you may need to add some logic
+
+if [ -d $dst ]
+then
+       dst="$dst"/`basename $src`
+fi
+
+# Make a temp file name in the proper directory.
+
+dstdir=`dirname $dst`
+dsttmp=$dstdir/#inst.$$#
+
+# Move or copy the file name to the temp name
+
+$doit $instcmd $src $dsttmp
+
+# and set any options; do chmod last to preserve setuid bits
+
+if [ x"$chowncmd" != x ]; then $doit $chowncmd $dsttmp; fi
+if [ x"$chgrpcmd" != x ]; then $doit $chgrpcmd $dsttmp; fi
+if [ x"$stripcmd" != x ]; then $doit $stripcmd $dsttmp; fi
+if [ x"$chmodcmd" != x ]; then $doit $chmodcmd $dsttmp; fi
+
+# Now rename the file to the real destination.
+
+$doit $rmcmd $dst
+$doit $mvcmd $dsttmp $dst
+
+
+exit 0
diff --git a/md5.c b/md5.c
new file mode 100644 (file)
index 0000000..ab00d26
--- /dev/null
+++ b/md5.c
@@ -0,0 +1,306 @@
+/*
+ * This code implements the MD5 message-digest algorithm.
+ * The algorithm is due to Ron Rivest.  This code was
+ * written by Colin Plumb in 1993, no copyright is claimed.
+ * This code is in the public domain; do with it what you wish.
+ *
+ * Equivalent code is available from RSA Data Security, Inc.
+ * This code has been tested against that, and is equivalent,
+ * except that you don't need to include two pages of legalese
+ * with every copy.
+ *
+ * To compute the message digest of a chunk of bytes, declare an
+ * MD5Context structure, pass it to MD5Init, call MD5Update as
+ * needed on buffers full of bytes, and then call MD5Final, which
+ * will fill a supplied 16-byte array with the digest.
+ *
+ * Changed so as no longer to depend on Colin Plumb's `usual.h' header
+ * definitions; now uses stuff from dpkg's config.h.
+ *  - Ian Jackson <ijackson@nyx.cs.du.edu>.
+ * Still in the public domain.
+ */
+
+#include <string.h>            /* for memcpy() */
+#include <sys/types.h>         /* for stupid systems */
+#include <netinet/in.h>                /* for ntohl() */
+
+#include "secnet.h"
+#include "config.h"
+#include "md5.h"
+
+#ifdef WORDS_BIGENDIAN
+static void
+byteSwap(uint32_t *buf, unsigned words)
+{
+       md5byte *p = (md5byte *)buf;
+
+       do {
+               *buf++ = (uint32_t)((unsigned)p[3] << 8 | p[2]) << 16 |
+                       ((unsigned)p[1] << 8 | p[0]);
+               p += 4;
+       } while (--words);
+}
+#else
+#define byteSwap(buf,words)
+#endif
+
+/*
+ * Start MD5 accumulation.  Set bit count to 0 and buffer to mysterious
+ * initialization constants.
+ */
+static void
+MD5Init(struct MD5Context *ctx)
+{
+       ctx->buf[0] = 0x67452301;
+       ctx->buf[1] = 0xefcdab89;
+       ctx->buf[2] = 0x98badcfe;
+       ctx->buf[3] = 0x10325476;
+
+       ctx->bytes[0] = 0;
+       ctx->bytes[1] = 0;
+}
+
+/*
+ * Update context to reflect the concatenation of another buffer full
+ * of bytes.
+ */
+static void
+MD5Update(struct MD5Context *ctx, md5byte const *buf, unsigned len)
+{
+       uint32_t t;
+
+       /* Update byte count */
+
+       t = ctx->bytes[0];
+       if ((ctx->bytes[0] = t + len) < t)
+               ctx->bytes[1]++;        /* Carry from low to high */
+
+       t = 64 - (t & 0x3f);    /* Space available in ctx->in (at least 1) */
+       if (t > len) {
+               memcpy((md5byte *)ctx->in + 64 - t, buf, len);
+               return;
+       }
+       /* First chunk is an odd size */
+       memcpy((md5byte *)ctx->in + 64 - t, buf, t);
+       byteSwap(ctx->in, 16);
+       MD5Transform(ctx->buf, ctx->in);
+       buf += t;
+       len -= t;
+
+       /* Process data in 64-byte chunks */
+       while (len >= 64) {
+               memcpy(ctx->in, buf, 64);
+               byteSwap(ctx->in, 16);
+               MD5Transform(ctx->buf, ctx->in);
+               buf += 64;
+               len -= 64;
+       }
+
+       /* Handle any remaining bytes of data. */
+       memcpy(ctx->in, buf, len);
+}
+
+/*
+ * Final wrapup - pad to 64-byte boundary with the bit pattern 
+ * 1 0* (64-bit count of bits processed, MSB-first)
+ */
+static void
+MD5Final(md5byte digest[16], struct MD5Context *ctx)
+{
+       int count = ctx->bytes[0] & 0x3f;       /* Number of bytes in ctx->in */
+       md5byte *p = (md5byte *)ctx->in + count;
+
+       /* Set the first char of padding to 0x80.  There is always room. */
+       *p++ = 0x80;
+
+       /* Bytes of padding needed to make 56 bytes (-8..55) */
+       count = 56 - 1 - count;
+
+       if (count < 0) {        /* Padding forces an extra block */
+               memset(p, 0, count + 8);
+               byteSwap(ctx->in, 16);
+               MD5Transform(ctx->buf, ctx->in);
+               p = (md5byte *)ctx->in;
+               count = 56;
+       }
+       memset(p, 0, count);
+       byteSwap(ctx->in, 14);
+
+       /* Append length in bits and transform */
+       ctx->in[14] = ctx->bytes[0] << 3;
+       ctx->in[15] = ctx->bytes[1] << 3 | ctx->bytes[0] >> 29;
+       MD5Transform(ctx->buf, ctx->in);
+
+       byteSwap(ctx->buf, 4);
+       memcpy(digest, ctx->buf, 16);
+       memset(ctx, 0, sizeof(ctx));    /* In case it's sensitive */
+}
+
+#ifndef ASM_MD5
+
+/* The four core functions - F1 is optimized somewhat */
+
+/* #define F1(x, y, z) (x & y | ~x & z) */
+#define F1(x, y, z) (z ^ (x & (y ^ z)))
+#define F2(x, y, z) F1(z, x, y)
+#define F3(x, y, z) (x ^ y ^ z)
+#define F4(x, y, z) (y ^ (x | ~z))
+
+/* This is the central step in the MD5 algorithm. */
+#define MD5STEP(f,w,x,y,z,in,s) \
+        (w += f(x,y,z) + in, w = (w<<s | w>>(32-s)) + x)
+
+/*
+ * The core of the MD5 algorithm, this alters an existing MD5 hash to
+ * reflect the addition of 16 longwords of new data.  MD5Update blocks
+ * the data and converts bytes into longwords for this routine.
+ */
+static void
+MD5Transform(uint32_t buf[4], uint32_t const in[16])
+{
+       register uint32_t a, b, c, d;
+
+       a = buf[0];
+       b = buf[1];
+       c = buf[2];
+       d = buf[3];
+
+       MD5STEP(F1, a, b, c, d, in[0] + 0xd76aa478, 7);
+       MD5STEP(F1, d, a, b, c, in[1] + 0xe8c7b756, 12);
+       MD5STEP(F1, c, d, a, b, in[2] + 0x242070db, 17);
+       MD5STEP(F1, b, c, d, a, in[3] + 0xc1bdceee, 22);
+       MD5STEP(F1, a, b, c, d, in[4] + 0xf57c0faf, 7);
+       MD5STEP(F1, d, a, b, c, in[5] + 0x4787c62a, 12);
+       MD5STEP(F1, c, d, a, b, in[6] + 0xa8304613, 17);
+       MD5STEP(F1, b, c, d, a, in[7] + 0xfd469501, 22);
+       MD5STEP(F1, a, b, c, d, in[8] + 0x698098d8, 7);
+       MD5STEP(F1, d, a, b, c, in[9] + 0x8b44f7af, 12);
+       MD5STEP(F1, c, d, a, b, in[10] + 0xffff5bb1, 17);
+       MD5STEP(F1, b, c, d, a, in[11] + 0x895cd7be, 22);
+       MD5STEP(F1, a, b, c, d, in[12] + 0x6b901122, 7);
+       MD5STEP(F1, d, a, b, c, in[13] + 0xfd987193, 12);
+       MD5STEP(F1, c, d, a, b, in[14] + 0xa679438e, 17);
+       MD5STEP(F1, b, c, d, a, in[15] + 0x49b40821, 22);
+
+       MD5STEP(F2, a, b, c, d, in[1] + 0xf61e2562, 5);
+       MD5STEP(F2, d, a, b, c, in[6] + 0xc040b340, 9);
+       MD5STEP(F2, c, d, a, b, in[11] + 0x265e5a51, 14);
+       MD5STEP(F2, b, c, d, a, in[0] + 0xe9b6c7aa, 20);
+       MD5STEP(F2, a, b, c, d, in[5] + 0xd62f105d, 5);
+       MD5STEP(F2, d, a, b, c, in[10] + 0x02441453, 9);
+       MD5STEP(F2, c, d, a, b, in[15] + 0xd8a1e681, 14);
+       MD5STEP(F2, b, c, d, a, in[4] + 0xe7d3fbc8, 20);
+       MD5STEP(F2, a, b, c, d, in[9] + 0x21e1cde6, 5);
+       MD5STEP(F2, d, a, b, c, in[14] + 0xc33707d6, 9);
+       MD5STEP(F2, c, d, a, b, in[3] + 0xf4d50d87, 14);
+       MD5STEP(F2, b, c, d, a, in[8] + 0x455a14ed, 20);
+       MD5STEP(F2, a, b, c, d, in[13] + 0xa9e3e905, 5);
+       MD5STEP(F2, d, a, b, c, in[2] + 0xfcefa3f8, 9);
+       MD5STEP(F2, c, d, a, b, in[7] + 0x676f02d9, 14);
+       MD5STEP(F2, b, c, d, a, in[12] + 0x8d2a4c8a, 20);
+
+       MD5STEP(F3, a, b, c, d, in[5] + 0xfffa3942, 4);
+       MD5STEP(F3, d, a, b, c, in[8] + 0x8771f681, 11);
+       MD5STEP(F3, c, d, a, b, in[11] + 0x6d9d6122, 16);
+       MD5STEP(F3, b, c, d, a, in[14] + 0xfde5380c, 23);
+       MD5STEP(F3, a, b, c, d, in[1] + 0xa4beea44, 4);
+       MD5STEP(F3, d, a, b, c, in[4] + 0x4bdecfa9, 11);
+       MD5STEP(F3, c, d, a, b, in[7] + 0xf6bb4b60, 16);
+       MD5STEP(F3, b, c, d, a, in[10] + 0xbebfbc70, 23);
+       MD5STEP(F3, a, b, c, d, in[13] + 0x289b7ec6, 4);
+       MD5STEP(F3, d, a, b, c, in[0] + 0xeaa127fa, 11);
+       MD5STEP(F3, c, d, a, b, in[3] + 0xd4ef3085, 16);
+       MD5STEP(F3, b, c, d, a, in[6] + 0x04881d05, 23);
+       MD5STEP(F3, a, b, c, d, in[9] + 0xd9d4d039, 4);
+       MD5STEP(F3, d, a, b, c, in[12] + 0xe6db99e5, 11);
+       MD5STEP(F3, c, d, a, b, in[15] + 0x1fa27cf8, 16);
+       MD5STEP(F3, b, c, d, a, in[2] + 0xc4ac5665, 23);
+
+       MD5STEP(F4, a, b, c, d, in[0] + 0xf4292244, 6);
+       MD5STEP(F4, d, a, b, c, in[7] + 0x432aff97, 10);
+       MD5STEP(F4, c, d, a, b, in[14] + 0xab9423a7, 15);
+       MD5STEP(F4, b, c, d, a, in[5] + 0xfc93a039, 21);
+       MD5STEP(F4, a, b, c, d, in[12] + 0x655b59c3, 6);
+       MD5STEP(F4, d, a, b, c, in[3] + 0x8f0ccc92, 10);
+       MD5STEP(F4, c, d, a, b, in[10] + 0xffeff47d, 15);
+       MD5STEP(F4, b, c, d, a, in[1] + 0x85845dd1, 21);
+       MD5STEP(F4, a, b, c, d, in[8] + 0x6fa87e4f, 6);
+       MD5STEP(F4, d, a, b, c, in[15] + 0xfe2ce6e0, 10);
+       MD5STEP(F4, c, d, a, b, in[6] + 0xa3014314, 15);
+       MD5STEP(F4, b, c, d, a, in[13] + 0x4e0811a1, 21);
+       MD5STEP(F4, a, b, c, d, in[4] + 0xf7537e82, 6);
+       MD5STEP(F4, d, a, b, c, in[11] + 0xbd3af235, 10);
+       MD5STEP(F4, c, d, a, b, in[2] + 0x2ad7d2bb, 15);
+       MD5STEP(F4, b, c, d, a, in[9] + 0xeb86d391, 21);
+
+       buf[0] += a;
+       buf[1] += b;
+       buf[2] += c;
+       buf[3] += d;
+}
+
+#endif
+
+static void *md5_init(void)
+{
+    struct MD5Context *ctx;
+
+    ctx=safe_malloc(sizeof(*ctx),"md5_init");
+    MD5Init(ctx);
+
+    return ctx;
+}
+
+static void md5_update(void *sst, uint8_t const *buf, uint32_t len)
+{
+    struct MD5Context *ctx=sst;
+
+    MD5Update(ctx,buf,len);
+}
+
+static void md5_final(void *sst, uint8_t *digest)
+{
+    struct MD5Context *ctx=sst;
+
+    MD5Final(digest,ctx);
+    free(ctx);
+}
+
+struct md5 {
+    closure_t cl;
+    struct hash_if ops;
+};
+
+init_module md5_module;
+void md5_module(dict_t *dict)
+{
+    struct md5 *st;
+    void *ctx;
+    string_t testinput="12345\n";
+    uint8_t expected[16]=
+       {0xd5,0x77,0x27,0x3f,0xf8,0x85,0xc3,0xf8,
+        0x4d,0xad,0xb8,0x57,0x8b,0xb4,0x13,0x99};
+    uint8_t digest[16];
+    int i;
+
+    st=safe_malloc(sizeof(*st),"netlink_module");
+    st->cl.description="md5";
+    st->cl.type=CL_HASH;
+    st->cl.apply=NULL;
+    st->cl.interface=&st->ops;
+    st->ops.len=16;
+    st->ops.init=md5_init;
+    st->ops.update=md5_update;
+    st->ops.final=md5_final;
+
+    dict_add(dict,"md5",new_closure(&st->cl));
+
+    ctx=md5_init();
+    md5_update(ctx,testinput,strlen(testinput));
+    md5_final(ctx,digest);
+    for (i=0; i<16; i++) {
+       if (digest[i]!=expected[i]) {
+           fatal("md5 module failed self-test\n");
+       }
+    }
+}
diff --git a/md5.h b/md5.h
new file mode 100644 (file)
index 0000000..4dd562c
--- /dev/null
+++ b/md5.h
@@ -0,0 +1,40 @@
+/*
+ * This is the header file for the MD5 message-digest algorithm.
+ * The algorithm is due to Ron Rivest.  This code was
+ * written by Colin Plumb in 1993, no copyright is claimed.
+ * This code is in the public domain; do with it what you wish.
+ *
+ * Equivalent code is available from RSA Data Security, Inc.
+ * This code has been tested against that, and is equivalent,
+ * except that you don't need to include two pages of legalese
+ * with every copy.
+ *
+ * To compute the message digest of a chunk of bytes, declare an
+ * MD5Context structure, pass it to MD5Init, call MD5Update as
+ * needed on buffers full of bytes, and then call MD5Final, which
+ * will fill a supplied 16-byte array with the digest.
+ *
+ * Changed so as no longer to depend on Colin Plumb's `usual.h'
+ * header definitions; now uses stuff from dpkg's config.h
+ *  - Ian Jackson <ijackson@nyx.cs.du.edu>.
+ * Still in the public domain.
+ */
+
+#ifndef MD5_H
+#define MD5_H
+
+#define md5byte unsigned char
+
+struct MD5Context {
+       uint32_t buf[4];
+       uint32_t bytes[2];
+       uint32_t in[16];
+};
+
+static void MD5Init(struct MD5Context *context);
+static void MD5Update(struct MD5Context *context,
+                     md5byte const *buf, unsigned len);
+static void MD5Final(unsigned char digest[16], struct MD5Context *context);
+static void MD5Transform(uint32_t buf[4], uint32_t const in[16]);
+
+#endif /* !MD5_H */
diff --git a/modules.c b/modules.c
new file mode 100644 (file)
index 0000000..0ddf017
--- /dev/null
+++ b/modules.c
@@ -0,0 +1,27 @@
+#include "config.h"
+#include "secnet.h"
+
+extern init_module resolver_module;
+extern init_module random_module;
+extern init_module udp_module;
+extern init_module util_module;
+extern init_module site_module;
+extern init_module transform_module;
+extern init_module netlink_module;
+extern init_module rsa_module;
+extern init_module dh_module;
+extern init_module md5_module;
+
+void init_builtin_modules(dict_t *dict)
+{
+    resolver_module(dict);
+    random_module(dict);
+    udp_module(dict);
+    util_module(dict);
+    site_module(dict);
+    transform_module(dict);
+    netlink_module(dict);
+    rsa_module(dict);
+    dh_module(dict);
+    md5_module(dict);
+}
diff --git a/modules.h b/modules.h
new file mode 100644 (file)
index 0000000..dab50de
--- /dev/null
+++ b/modules.h
@@ -0,0 +1,6 @@
+#ifndef modules_h
+#define modules_h
+
+extern void init_builtin_modules(dict_t *dict);
+
+#endif /* modules_h */
diff --git a/myrddin.pub b/myrddin.pub
new file mode 100644 (file)
index 0000000..6736d15
--- /dev/null
@@ -0,0 +1 @@
+1024 35 154107175724781677184264293617887954015562225725852111745852699493257053099810379926047345975839848434403852210573185384327420788855664167034282567346429150999373740871227795773749618022407366186555483566435251279808390618987056868368084933125373643004284007109877210578088697520329039753099981203724057693543 steve@myrddin
diff --git a/netlink.c b/netlink.c
new file mode 100644 (file)
index 0000000..7db3559
--- /dev/null
+++ b/netlink.c
@@ -0,0 +1,467 @@
+/* User-kernel network link */
+
+/* We support a variety of methods: userv-ipif, ipif on its own (when
+   we run as root), SLIP to a pty, an external netlink daemon. There
+   is a performance/security tradeoff. */
+
+/* When dealing with SLIP (to a pty, or ipif) we have separate rx, tx
+   and client buffers. When receiving we may read() any amount, not
+   just whole packets. When transmitting we need to bytestuff anyway,
+   and may be part-way through receiving. */
+
+/* Each netlink device is actually a router, with its own IP
+   address. We should eventually do things like decreasing the TTL and
+   recalculating the header checksum, generating ICMP, responding to
+   pings, etc. but for now we can get away without them. We should
+   implement this stuff no matter how we get the packets to/from the
+   kernel. */
+
+/* This is where we have the anti-spoofing paranoia - before sending a
+   packet to the kernel we check that the tunnel it came over could
+   reasonably have produced it. */
+
+#include <stdio.h>
+#include <string.h>
+#include <unistd.h>
+#include <fcntl.h>
+
+#include "secnet.h"
+#include "util.h"
+
+#define DEFAULT_BUFSIZE 2048
+
+#define SLIP_END    192
+#define SLIP_ESC    219
+#define SLIP_ESCEND 220
+#define SLIP_ESCESC 221
+
+struct netlink_client {
+    struct subnet_list *networks;
+    netlink_deliver_fn *deliver;
+    void *dst;
+    struct netlink_client *next;
+};
+
+struct userv {
+    closure_t cl;
+    struct netlink_if ops;
+    uint32_t max_start_pad;
+    uint32_t max_end_pad;
+    int txfd; /* We transmit to userv */
+    int rxfd; /* We receive from userv */
+    struct netlink_client *clients;
+    string_t name;
+    string_t userv_path;
+    string_t service_user;
+    string_t service_name;
+    struct subnet_list networks;
+    uint32_t local_address;
+    uint32_t secnet_address;
+    uint32_t mtu;
+    uint32_t txbuflen;
+    struct buffer_if *buff; /* We unstuff received packets into here
+                              and send them to the site code. */
+    bool_t pending_esc;
+};
+
+static int userv_beforepoll(void *sst, struct pollfd *fds, int *nfds_io,
+                           int *timeout_io, const struct timeval *tv_now,
+                           uint64_t *now)
+{
+    struct userv *st=sst;
+    *nfds_io=2;
+    fds[0].fd=st->txfd;
+    fds[0].events=POLLERR; /* Might want to pick up POLLOUT sometime */
+    fds[1].fd=st->rxfd;
+    fds[1].events=POLLIN|POLLERR|POLLHUP;
+    return 0;
+}
+
+static void process_local_packet(struct userv *st)
+{
+    uint32_t source,dest;
+    struct netlink_client *c;
+
+    source=ntohl(*(uint32_t *)(st->buff->start+12));
+    dest=ntohl(*(uint32_t *)(st->buff->start+16));
+
+/*    printf("process_local_packet source=%s dest=%s len=%d\n",
+      ipaddr_to_string(source),ipaddr_to_string(dest),
+      st->buff->size); */
+    if (!subnet_match(&st->networks,source)) {
+       string_t s,d;
+       s=ipaddr_to_string(source);
+       d=ipaddr_to_string(dest);
+       Message(M_WARNING,"%s: outgoing packet with bad source address "
+               "(s=%s,d=%s)\n",st->name,s,d);
+       free(s); free(d);
+       return;
+    }
+    for (c=st->clients; c; c=c->next) {
+       if (subnet_match(c->networks,dest)) {
+           c->deliver(c->dst,c,st->buff);
+           BUF_ALLOC(st->buff,"netlink:process_local_packet");
+           return;
+       }
+    }
+    if (dest==st->secnet_address) {
+       printf("%s: secnet received packet of len %d from %s\n",st->name,
+              st->buff->size,ipaddr_to_string(source));
+       return;
+    }
+    {
+       string_t s,d;
+       s=ipaddr_to_string(source);
+       d=ipaddr_to_string(dest);
+       Message(M_WARNING,"%s: outgoing packet with bad destination address "
+                         "(s=%s,d=%s)\n",st->name,s,d);
+       free(s); free(d);
+       return;
+    }
+}
+
+static void userv_afterpoll(void *sst, struct pollfd *fds, int nfds,
+                           const struct timeval *tv_now, uint64_t *now)
+{
+    struct userv *st=sst;
+    uint8_t rxbuf[DEFAULT_BUFSIZE];
+    int l,i;
+
+    if (fds[1].revents&POLLERR) {
+       printf("userv_afterpoll: hup!\n");
+    }
+    if (fds[1].revents&POLLIN) {
+       l=read(st->rxfd,rxbuf,DEFAULT_BUFSIZE);
+       if (l<0) {
+           fatal_perror("userv_afterpoll: read(rxfd)");
+       }
+       if (l==0) {
+           fatal("userv_afterpoll: read(rxfd)=0; userv gone away?\n");
+       }
+       /* XXX really crude unstuff code */
+       /* XXX check for buffer overflow */
+       for (i=0; i<l; i++) {
+           if (st->pending_esc) {
+               st->pending_esc=False;
+               switch(rxbuf[i]) {
+               case SLIP_ESCEND:
+                   *(uint8_t *)buf_append(st->buff,1)=SLIP_END;
+                   break;
+               case SLIP_ESCESC:
+                   *(uint8_t *)buf_append(st->buff,1)=SLIP_ESC;
+                   break;
+               default:
+                   fatal("userv_afterpoll: bad SLIP escape character\n");
+               }
+           } else {
+               switch (rxbuf[i]) {
+               case SLIP_END:
+                   if (st->buff->size>0) process_local_packet(st);
+                   BUF_ASSERT_USED(st->buff);
+                   buffer_init(st->buff,st->max_start_pad);
+                   break;
+               case SLIP_ESC:
+                   st->pending_esc=True;
+                   break;
+               default:
+                   *(uint8_t *)buf_append(st->buff,1)=rxbuf[i];
+                   break;
+               }
+           }
+       }
+    }
+    return;
+}
+
+static void userv_phase_hook(void *sst, uint32_t newphase)
+{
+    struct userv *st=sst;
+    pid_t child;
+    int c_stdin[2];
+    int c_stdout[2];
+    string_t addrs;
+    string_t nets;
+    string_t s;
+    struct netlink_client *c;
+    int i;
+
+    /* This is where we actually invoke userv - all the networks we'll
+       be using should already have been registered. */
+
+    addrs=safe_malloc(512,"userv_phase_hook:addrs");
+    snprintf(addrs,512,"%s,%s,%d,slip",ipaddr_to_string(st->local_address),
+            ipaddr_to_string(st->secnet_address),st->mtu);
+
+    nets=safe_malloc(1024,"userv_phase_hook:nets");
+    *nets=0;
+    for (c=st->clients; c; c=c->next) {
+       for (i=0; i<c->networks->entries; i++) {
+           s=subnet_to_string(&c->networks->list[i]);
+           strcat(nets,s);
+           strcat(nets,",");
+           free(s);
+       }
+    }
+    nets[strlen(nets)-1]=0;
+
+    Message(M_INFO,"\nuserv_phase_hook: %s %s %s %s %s\n",st->userv_path,
+          st->service_user,st->service_name,addrs,nets);
+
+    /* Allocate buffer, plus space for padding. Make sure we end up
+       with the start of the packet well-aligned. */
+    /* ALIGN(st->max_start_pad,16); */
+    /* ALIGN(st->max_end_pad,16); */
+
+    st->pending_esc=False;
+
+    /* Invoke userv */
+    if (pipe(c_stdin)!=0) {
+       fatal_perror("userv_phase_hook: pipe(c_stdin)");
+    }
+    if (pipe(c_stdout)!=0) {
+       fatal_perror("userv_phase_hook: pipe(c_stdout)");
+    }
+    st->txfd=c_stdin[1];
+    st->rxfd=c_stdout[0];
+
+    child=fork();
+    if (child==-1) {
+       fatal_perror("userv_phase_hook: fork()");
+    }
+    if (child==0) {
+       char **argv;
+
+       /* We are the child. Modify our stdin and stdout, then exec userv */
+       dup2(c_stdin[0],0);
+       dup2(c_stdout[1],1);
+       close(c_stdin[1]);
+       close(c_stdout[0]);
+
+       /* The arguments are:
+          userv
+          service-user
+          service-name
+          local-addr,secnet-addr,mtu,protocol
+          route1,route2,... */
+       argv=malloc(sizeof(*argv)*6);
+       argv[0]=st->userv_path;
+       argv[1]=st->service_user;
+       argv[2]=st->service_name;
+       argv[3]=addrs;
+       argv[4]=nets;
+       argv[5]=NULL;
+       execvp(st->userv_path,argv);
+       perror("netlink-userv-ipif: execvp");
+
+       exit(1);
+    }
+    /* We are the parent... */
+          
+    /* Register for poll() */
+    register_for_poll(st, userv_beforepoll, userv_afterpoll, 2, "netlink");
+}
+
+static void *userv_regnets(void *sst, struct subnet_list *nets,
+                          netlink_deliver_fn *deliver, void *dst,
+                          uint32_t max_start_pad, uint32_t max_end_pad)
+{
+    struct userv *st=sst;
+    struct netlink_client *c;
+
+    Message(M_DEBUG_CONFIG,"userv_regnets: request for %d networks, "
+           "max_start_pad=%d, max_end_pad=%d\n",
+           nets->entries,max_start_pad,max_end_pad);
+
+    c=safe_malloc(sizeof(*c),"userv_regnets");
+    c->networks=nets;
+    c->deliver=deliver;
+    c->dst=dst;
+    c->next=st->clients;
+    st->clients=c;
+    if (max_start_pad > st->max_start_pad) st->max_start_pad=max_start_pad;
+    if (max_end_pad > st->max_end_pad) st->max_end_pad=max_end_pad;
+
+    return c;
+}
+
+static void userv_deliver(void *sst, void *cid, struct buffer_if *buf)
+{
+    struct userv *st=sst;
+    struct netlink_client *client=cid;
+    uint8_t txbuf[DEFAULT_BUFSIZE];
+
+    uint32_t source,dest;
+    uint8_t *i;
+    uint32_t j;
+
+    source=ntohl(*(uint32_t *)(buf->start+12));
+    dest=ntohl(*(uint32_t *)(buf->start+16));
+
+    /* Check that the packet source is in 'nets' and its destination is
+       in client->networks */
+    if (!subnet_match(client->networks,source)) {
+       string_t s,d;
+       s=ipaddr_to_string(source);
+       d=ipaddr_to_string(dest);
+       Message(M_WARNING,"%s: incoming packet with bad source address "
+               "(s=%s,d=%s)\n",st->name,s,d);
+       free(s); free(d);
+       return;
+    }
+    if (!subnet_match(&st->networks,dest)) {
+       string_t s,d;
+       s=ipaddr_to_string(source);
+       d=ipaddr_to_string(dest);
+       Message(M_WARNING,"%s: incoming packet with bad destination address "
+               "(s=%s,d=%s)\n",st->name,s,d);
+       free(s); free(d);
+       return;
+    }
+
+    /* Really we should decrease TTL, check it's above zero, and
+       recalculate header checksum here. If it gets down to zero,
+       generate an ICMP time-exceeded and send the new packet back to
+       the originating tunnel. XXX check buffer usage! */
+
+    /* (Basically do full IP packet forwarding stuff. Except that we
+       know any packet passed in here is destined for the local
+       machine; only exception is if it's destined for us.) */
+
+    if (dest==st->secnet_address) {
+       printf("%s: incoming tunneled packet for secnet!\n",st->name);
+       return;
+    }
+
+    /* Now spit the packet at userv-ipif: SLIP start marker, then
+       bytestuff the packet, then SLIP end marker */
+    /* XXX crunchy bytestuff code */
+    j=0;
+    txbuf[j++]=SLIP_END;
+    for (i=buf->start; i<(buf->start+buf->size); i++) {
+       switch (*i) {
+       case SLIP_END:
+           txbuf[j++]=SLIP_ESC;
+           txbuf[j++]=SLIP_ESCEND;
+           break;
+       case SLIP_ESC:
+           txbuf[j++]=SLIP_ESC;
+           txbuf[j++]=SLIP_ESCESC;
+           break;
+       default:
+           txbuf[j++]=*i;
+           break;
+       }
+    }
+    txbuf[j++]=SLIP_END;
+    if (write(st->txfd,txbuf,j)<0) {
+       fatal_perror("userv_deliver: write()");
+    }
+
+    return;
+}
+
+static list_t *userv_apply(closure_t *self, struct cloc loc, dict_t *context,
+                          list_t *args)
+{
+    struct userv *st;
+    item_t *item;
+    dict_t *dict;
+
+    st=safe_malloc(sizeof(*st),"userv_apply (netlink)");
+    st->cl.description="userv-netlink";
+    st->cl.type=CL_NETLINK;
+    st->cl.apply=NULL;
+    st->cl.interface=&st->ops;
+    st->ops.st=st;
+    st->ops.regnets=userv_regnets;
+    st->ops.deliver=userv_deliver;
+    st->max_start_pad=0;
+    st->max_end_pad=0;
+    st->rxfd=-1; st->txfd=-1;
+    st->clients=NULL;
+
+    /* First parameter must be a dict */
+    item=list_elem(args,0);
+    if (!item || item->type!=t_dict)
+       cfgfatal(loc,"userv-ipif","parameter must be a dictionary\n");
+    
+    dict=item->data.dict;
+    st->name=dict_read_string(dict,"name",False,"userv-netlink",loc);
+    st->userv_path=dict_read_string(dict,"userv-path",False,"userv-netlink",
+                                   loc);
+    st->service_user=dict_read_string(dict,"service-user",False,
+                                     "userv-netlink",loc);
+    st->service_name=dict_read_string(dict,"service-name",False,
+                                     "userv-netlink",loc);
+    if (!st->name) st->name="netlink-userv-ipif";
+    if (!st->userv_path) st->userv_path="userv";
+    if (!st->service_user) st->service_user="root";
+    if (!st->service_name) st->service_name="ipif";
+    dict_read_subnet_list(dict, "networks", True, "userv-netlink", loc,
+                         &st->networks);
+    st->local_address=string_to_ipaddr(
+       dict_find_item(dict,"local-address", True, "userv-netlink", loc),
+       "userv-netlink");
+    st->secnet_address=string_to_ipaddr(
+       dict_find_item(dict,"secnet-address", True, "userv-netlink", loc),
+       "userv-netlink");
+    if (!subnet_match(&st->networks,st->local_address)) {
+       cfgfatal(loc,"netlink-userv-ipif","local-address must be in "
+             "local networks\n");
+    }
+    st->mtu=dict_read_number(dict, "mtu", False, "userv-netlink", loc, 1000);
+    st->buff=find_cl_if(dict,"buffer",CL_BUFFER,True,"userv-netlink",loc);
+    BUF_ALLOC(st->buff,"netlink:userv_apply");
+
+    add_hook(PHASE_DROPPRIV,userv_phase_hook,st);
+
+    return new_closure(&st->cl);
+}
+
+struct null {
+    closure_t cl;
+    struct netlink_if ops;
+};
+
+static void *null_regnets(void *sst, struct subnet_list *nets,
+                         netlink_deliver_fn *deliver, void *dst,
+                         uint32_t max_start_pad, uint32_t max_end_pad)
+{
+    Message(M_DEBUG_CONFIG,"null_regnets: request for %d networks, "
+           "max_start_pad=%d, max_end_pad=%d\n",
+           nets->entries,max_start_pad,max_end_pad);
+    return NULL;
+}
+
+static void null_deliver(void *sst, void *cid, struct buffer_if *buf)
+{
+    return;
+}
+
+static list_t *null_apply(closure_t *self, struct cloc loc, dict_t *context,
+                         list_t *args)
+{
+    struct null *st;
+
+    st=safe_malloc(sizeof(*st),"null_apply (netlink)");
+    st->cl.description="null-netlink";
+    st->cl.type=CL_NETLINK;
+    st->cl.apply=NULL;
+    st->cl.interface=&st->ops;
+    st->ops.st=st;
+    st->ops.regnets=null_regnets;
+    st->ops.deliver=null_deliver;
+
+    return new_closure(&st->cl);
+}
+
+init_module netlink_module;
+void netlink_module(dict_t *dict)
+{
+    add_closure(dict,"userv-ipif",userv_apply);
+#if 0
+    add_closure(dict,"pty-slip",ptyslip_apply);
+    add_closure(dict,"slipd",slipd_apply);
+#endif /* 0 */
+    add_closure(dict,"null-netlink",null_apply);
+}
diff --git a/private-key b/private-key
new file mode 100644 (file)
index 0000000..f764068
Binary files /dev/null and b/private-key differ
diff --git a/random.c b/random.c
new file mode 100644 (file)
index 0000000..1fd19ec
--- /dev/null
+++ b/random.c
@@ -0,0 +1,83 @@
+/* $Log$
+ */
+
+#include <stdio.h>
+#include <fcntl.h>
+#include <string.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <unistd.h>
+
+#include "secnet.h"
+
+struct rgen_data {
+    closure_t cl;
+    struct random_if ops;
+    struct cloc loc;
+    int fd;
+};
+
+static random_fn random_generate;
+static bool_t random_generate(void *data, uint32_t bytes, uint8_t *buff)
+{
+    struct rgen_data *st=data;
+
+    /* XXX XXX error checking */
+    read(st->fd,buff,bytes);
+
+    return True;
+}
+
+static list_t *random_apply(closure_t *self, struct cloc loc,
+                           dict_t *context, list_t *args)
+{
+    struct rgen_data *st;
+    item_t *arg1, *arg2;
+    string_t filename=NULL;
+
+    st=safe_malloc(sizeof(*st),"random_apply");
+
+    st->cl.description="randomsource";
+    st->cl.type=CL_RANDOMSRC;
+    st->cl.apply=NULL;
+    st->cl.interface=&st->ops;
+    st->ops.st=st;
+    st->ops.blocking=False;
+    st->ops.generate=random_generate;
+    st->loc=loc;
+
+    arg1=list_elem(args,0);
+    arg2=list_elem(args,1);
+
+    if (!arg1) {
+       fatal("randomsource: requires a filename\n");
+    }
+    if (arg1->type != t_string) {
+       cfgfatal(arg1->loc,"randomsource",
+                "filename (arg1) must be a string\n");
+    }
+    filename=arg1->data.string;
+
+    if (arg2) {
+       if (arg2->type != t_bool) {
+           cfgfatal(arg2->loc,"randomsource",
+                    "blocking parameter (arg2) must be bool\n");
+       }
+       st->ops.blocking=arg2->data.bool;
+    }
+
+    if (!filename) {
+       fatal("randomsource requires a filename");
+    }
+    st->fd=open(filename,O_RDONLY);
+    if (st->fd<0) {
+       fatal_perror("randomsource (%s:%d): cannot open %s",arg1->loc.file,
+                    arg1->loc.line,filename);
+    }
+    return new_closure(&st->cl);
+}
+
+void random_module(dict_t *d)
+{
+    add_closure(d,"randomfile",random_apply);
+}
diff --git a/resolver.c b/resolver.c
new file mode 100644 (file)
index 0000000..9e0530c
--- /dev/null
@@ -0,0 +1,125 @@
+/* Name resolution using adns */
+
+#include <errno.h>
+#include "secnet.h"
+#include <adns.h>
+
+struct adns {
+    closure_t cl;
+    struct resolver_if ops;
+    struct cloc loc;
+    adns_state ast;
+};
+
+struct query {
+    void *cst;
+    resolve_answer_fn *answer;
+    adns_query query;
+};
+
+static bool_t resolve_request(void *sst, string_t name,
+                             resolve_answer_fn *cb, void *cst)
+{
+    struct adns *st=sst;
+    struct query *q;
+    int rv;
+
+    q=safe_malloc(sizeof *q,"resolve_request");
+    q->cst=cst;
+    q->answer=cb;
+
+    rv=adns_submit(st->ast, name, adns_r_a, 0, q, &q->query);
+
+    return rv==0;
+}
+
+static int resolver_beforepoll(void *sst, struct pollfd *fds, int *nfds_io,
+                              int *timeout_io, const struct timeval *tv_now,
+                              uint64_t *now)
+{
+    struct adns *st=sst;
+    return adns_beforepoll(st->ast, fds, nfds_io, timeout_io, tv_now);
+}
+
+static void resolver_afterpoll(void *sst, struct pollfd *fds, int nfds,
+                              const struct timeval *tv_now, uint64_t *now)
+{
+    struct adns *st=sst;
+    adns_query aq;
+    adns_answer *ans;
+    void *qp;
+    struct query *q;
+    int rv;
+
+    adns_afterpoll(st->ast, fds, nfds, tv_now);
+
+    while (True) {
+       aq=NULL;
+       rv=adns_check(st->ast, &aq, &ans, &qp);
+       if (rv==0) {
+           q=qp;
+           if (ans->status!=adns_s_ok) {
+               q->answer(q->cst,NULL); /* Failure */
+               free(q);
+               free(ans);
+           } else {
+               q->answer(q->cst,ans->rrs.inaddr);
+               free(q);
+               free(ans);
+           }
+       } else if (rv==EAGAIN || rv==ESRCH) {
+           break;
+       } else {
+           fatal("resolver_afterpoll: adns_check() returned %d\n",rv);
+       }
+    }
+
+    return;
+}
+
+/* Initialise adns, using parameters supplied */
+static list_t *adnsresolver_apply(closure_t *self, struct cloc loc,
+                                 dict_t *context, list_t *args)
+{
+    struct adns *st;
+    dict_t *d;
+    item_t *i;
+    string_t conf;
+
+    st=safe_malloc(sizeof(*st),"adnsresolver_apply");
+    st->cl.description="adns";
+    st->cl.type=CL_RESOLVER;
+    st->cl.apply=NULL;
+    st->cl.interface=&st->ops;
+    st->loc=loc;
+    st->ops.st=st;
+    st->ops.request=resolve_request;
+
+    i=list_elem(args,0);
+    if (!i || i->type!=t_dict) {
+       cfgfatal(st->loc,"adns","first argument must be a dictionary\n");
+    }
+    d=i->data.dict;
+    conf=dict_read_string(d,"config",False,"adns",loc);
+
+    if (conf) {
+       if (adns_init_strcfg(&st->ast, 0, 0, conf)) {
+           fatal_perror("Failed to initialise ADNS");
+       }
+    } else {
+       if (adns_init(&st->ast, 0, 0)) {
+           fatal_perror("Failed to initialise ADNS");
+       }
+    }
+
+    register_for_poll(st, resolver_beforepoll, resolver_afterpoll,
+                     ADNS_POLLFDS_RECOMMENDED+5,"resolver");
+
+    return new_closure(&st->cl);
+}
+
+init_module resolver_module;
+void resolver_module(dict_t *dict)
+{
+    add_closure(dict,"adns",adnsresolver_apply);
+}
diff --git a/rsa.c b/rsa.c
new file mode 100644 (file)
index 0000000..d1107a9
--- /dev/null
+++ b/rsa.c
@@ -0,0 +1,338 @@
+/***************************************************************************
+ *
+ *              Part II Project, "A secure, private IP network"
+ *              Stephen Early <sde1000@cam.ac.uk>
+ *   
+ *
+ *     $RCSfile: rsa.c,v $
+ *
+ *  Description: RSA signature making and checking functions
+ *
+ *    Copyright: (C) Stephen Early 1995
+ *
+ *    $Revision: 1.1 $
+ *
+ *        $Date: 1996/05/16 18:40:14 $
+ *
+ *       $State: Exp $
+ *
+ ***************************************************************************/
+
+/* $Log: rsa.c,v $
+ * Revision 1.1  1996/05/16 18:40:14  sde1000
+ * Initial revision
+ *
+ */
+
+#include <stdio.h>
+#include <gmp.h>
+#include "secnet.h"
+#include "util.h"
+
+#define AUTHFILE_ID_STRING "SSH PRIVATE KEY FILE FORMAT 1.1\n"
+
+struct rsapriv {
+    closure_t cl;
+    struct rsaprivkey_if ops;
+    struct cloc loc;
+    MP_INT d;
+    MP_INT n;
+};
+struct rsapub {
+    closure_t cl;
+    struct rsapubkey_if ops;
+    struct cloc loc;
+    MP_INT e;
+    MP_INT n;
+};
+/* Sign data. NB data must be smaller than modulus */
+
+static char *hexchars="0123456789abcdef";
+
+static string_t rsa_sign(void *sst, uint8_t *data, uint32_t datalen)
+{
+    struct rsapriv *st=sst;
+    MP_INT a, b;
+    char buff[2048];
+    int msize, i;
+    string_t signature;
+
+    mpz_init(&a);
+    mpz_init(&b);
+
+    msize=mpz_sizeinbase(&st->n, 16);
+
+    strcpy(buff,"0001");
+
+    for (i=0; i<datalen; i++) {
+       buff[4+i*2]=hexchars[(data[i]&0xf0)>>4];
+       buff[5+i*2]=hexchars[data[i]&0xf];
+    }
+    buff[4+datalen*2]=0;
+
+    for (i=datalen*2+4; i<msize; i++)
+       buff[i]='f';
+
+    buff[msize]=0;
+
+    mpz_set_str(&a, buff, 16);
+
+    mpz_powm(&b, &a, &st->d, &st->n);
+
+    signature=write_mpstring(&b);
+
+    mpz_clear(&b);
+    mpz_clear(&a);
+    return signature;
+}
+
+static bool_t rsa_sig_check(void *sst, uint8_t *data, uint32_t datalen,
+                           string_t signature)
+{
+    struct rsapub *st=sst;
+    MP_INT a, b, c;
+    char buff[2048];
+    int msize, i;
+    bool_t ok;
+
+    mpz_init(&a);
+    mpz_init(&b);
+    mpz_init(&c);
+
+    msize=mpz_sizeinbase(&st->n, 16);
+
+    strcpy(buff,"0001");
+
+    for (i=0; i<datalen; i++) {
+       buff[4+i*2]=hexchars[(data[i]&0xf0)>>4];
+       buff[5+i*2]=hexchars[data[i]&0xf];
+    }
+    buff[4+datalen*2]=0;
+
+    for (i=datalen*2+4; i<msize; i++)
+       buff[i]='f';
+
+    buff[msize]=0;
+
+    mpz_set_str(&a, buff, 16);
+
+    mpz_set_str(&b, signature, 16);
+
+    mpz_powm(&c, &b, &st->e, &st->n);
+
+    ok=(mpz_cmp(&a, &c)==0);
+
+    mpz_clear(&c);
+    mpz_clear(&b);
+    mpz_clear(&a);
+
+    return ok;
+}
+
+static list_t *rsapub_apply(closure_t *self, struct cloc loc, dict_t *context,
+                           list_t *args)
+{
+    struct rsapub *st;
+    item_t *i;
+    string_t e,n;
+
+    st=safe_malloc(sizeof(*st),"rsapub_apply");
+    st->cl.description="rsapub";
+    st->cl.type=CL_RSAPUBKEY;
+    st->cl.apply=NULL;
+    st->cl.interface=&st->ops;
+    st->ops.st=st;
+    st->ops.check=rsa_sig_check;
+    st->loc=loc;
+
+    i=list_elem(args,0);
+    if (i) {
+       if (i->type!=t_string) {
+           cfgfatal(i->loc,"rsa-public","first argument must be a string");
+       }
+       e=i->data.string;
+       if (mpz_init_set_str(&st->e,e,10)!=0) {
+           cfgfatal(i->loc,"rsa-public","encryption key \"%s\" is not a "
+                    "decimal number string\n",e);
+       }
+    } else {
+       cfgfatal(loc,"rsa-public","you must provide an encryption key\n");
+    }
+    
+    i=list_elem(args,1);
+    if (i) {
+       if (i->type!=t_string) {
+           cfgfatal(i->loc,"rsa-public","second argument must be a string");
+       }
+       n=i->data.string;
+       if (mpz_init_set_str(&st->n,n,10)!=0) {
+           cfgfatal(i->loc,"rsa-public","modulus \"%s\" is not a decimal "
+                    "number string\n",n);
+       }
+    } else {
+       cfgfatal(loc,"rsa-public","you must provide a modulus\n");
+    }
+    return new_closure(&st->cl);
+}
+
+static uint32_t keyfile_get_int(FILE *f)
+{
+    uint32_t r;
+    r=fgetc(f)<<24;
+    r|=fgetc(f)<<16;
+    r|=fgetc(f)<<8;
+    r|=fgetc(f);
+    return r;
+}
+
+static uint16_t keyfile_get_short(FILE *f)
+{
+    uint16_t r;
+    r=fgetc(f)<<8;
+    r|=fgetc(f);
+    return r;
+}
+
+static list_t *rsapriv_apply(closure_t *self, struct cloc loc, dict_t *context,
+                            list_t *args)
+{
+    struct rsapriv *st;
+    FILE *f;
+    string_t filename;
+    item_t *i;
+    long length;
+    uint8_t *b, *c;
+    int cipher_type;
+    MP_INT e,sig,plain,check;
+
+    st=safe_malloc(sizeof(*st),"rsapriv_apply");
+    st->cl.description="rsapriv";
+    st->cl.type=CL_RSAPRIVKEY;
+    st->cl.apply=NULL;
+    st->cl.interface=&st->ops;
+    st->ops.st=st;
+    st->ops.sign=rsa_sign;
+    st->loc=loc;
+
+    /* Argument is filename pointing to SSH1 private key file */
+    i=list_elem(args,0);
+    if (i) {
+       if (i->type!=t_string) {
+           cfgfatal(i->loc,"rsa-public","first argument must be a string");
+       }
+       filename=i->data.string;
+    } else {
+       filename=""; /* Make compiler happy */
+       cfgfatal(loc,"rsa-private","you must provide a filename\n");
+    }
+
+    f=fopen(filename,"rb");
+    if (!f) {
+       fatal_perror("rsa-private (%s:%d): cannot open file \"%s\"",
+                    loc.file,loc.line,filename);
+    }
+
+    /* Check that the ID string is correct */
+    length=strlen(AUTHFILE_ID_STRING)+1;
+    b=safe_malloc(length,"rsapriv_apply");
+    if (fread(b,length,1,f)!=1 || memcmp(b,AUTHFILE_ID_STRING,length)!=0) {
+       cfgfatal(loc,"rsa-private","file \"%s\" is not a "
+                "SSH1 private keyfile\n",filename);
+    }
+    free(b);
+
+    cipher_type=fgetc(f);
+    keyfile_get_int(f); /* "Reserved data" */
+    if (cipher_type != 0) {
+       cfgfatal(loc,"rsa-private","we don't support encrypted keyfiles\n");
+    }
+
+    /* Read the public key */
+    keyfile_get_int(f); /* Not sure what this is */
+    length=(keyfile_get_short(f)+7)/8;
+    if (length>1024) {
+       cfgfatal(loc,"rsa-private","implausible length %ld for modulus\n",
+                length);
+    }
+    b=safe_malloc(length,"rsapriv_apply");
+    if (fread(b,length,1,f) != 1) {
+       cfgfatal(loc,"rsa-private","error reading modulus\n");
+    }
+    mpz_init(&st->n);
+    read_mpbin(&st->n,b,length);
+    free(b);
+    length=(keyfile_get_short(f)+7)/8;
+    if (length>1024) {
+       cfgfatal(loc,"rsa-private","implausible length %ld for e\n",length);
+    }
+    b=safe_malloc(length,"rsapriv_apply");
+    if (fread(b,length,1,f)!=1) {
+       cfgfatal(loc,"rsa-private","error reading e\n");
+    }
+    mpz_init(&e);
+    read_mpbin(&e,b,length);
+    free(b);
+    
+    length=keyfile_get_int(f);
+    if (length>1024) {
+       cfgfatal(loc,"rsa-private","implausibly long (%ld) key comment\n",
+                length);
+    }
+    c=safe_malloc(length+1,"rsapriv_apply");
+    if (fread(c,length,1,f)!=1) {
+       cfgfatal(loc,"rsa-private","error reading key comment\n");
+    }
+    c[length]=0;
+
+    /* Check that the next two pairs of characters are identical - the
+       keyfile is not encrypted, so they should be */
+    if (keyfile_get_short(f) != keyfile_get_short(f)) {
+       cfgfatal(loc,"rsa-private","corrupt keyfile\n");
+    }
+
+    /* Read d */
+    length=(keyfile_get_short(f)+7)/8;
+    if (length>1024) {
+       cfgfatal(loc,"rsa-private","implausibly long (%ld) decryption key\n",
+                length);
+    }
+    b=safe_malloc(length,"rsapriv_apply");
+    if (fread(b,length,1,f)!=1) {
+       cfgfatal(loc,"rsa-private","error reading decryption key\n");
+    }
+    mpz_init(&st->d);
+    read_mpbin(&st->d,b,length);
+    free(b);
+    
+    if (fclose(f)!=0) {
+       fatal_perror("rsa-private (%s:%d): fclose",loc.file,loc.line);
+    }
+
+    /* Now do trial signature/check to make sure it's a real keypair:
+       sign the comment string! */
+    mpz_init(&sig);
+    mpz_init(&plain);
+    mpz_init(&check);
+    read_mpbin(&plain,c,strlen(c));
+    mpz_powm(&sig, &plain, &st->d, &st->n);
+    mpz_powm(&check, &sig, &e, &st->n);
+    if (mpz_cmp(&plain,&check)!=0) {
+       cfgfatal(loc,"rsa-private","file \"%s\" does not contain a "
+                "valid RSA key!\n",filename);
+    }
+    mpz_clear(&sig);
+    mpz_clear(&plain);
+    mpz_clear(&check);
+
+    free(c);
+    mpz_clear(&e);
+
+    return new_closure(&st->cl);
+}
+
+init_module rsa_module;
+void rsa_module(dict_t *dict)
+{
+    add_closure(dict,"rsa-private",rsapriv_apply);
+    add_closure(dict,"rsa-public",rsapub_apply);
+}
diff --git a/secnet.c b/secnet.c
new file mode 100644 (file)
index 0000000..0df6e09
--- /dev/null
+++ b/secnet.c
@@ -0,0 +1,359 @@
+/* $Log: secnet.c,v $
+ * Revision 1.1  1996/03/13 22:27:41  sde1000
+ * Initial revision
+ *
+ */
+
+static char *version="secnet version " VERSION " $Date: 1996/03/13 22:27:41 $";
+
+#include <stdio.h>
+#include <string.h>
+#include <getopt.h>
+#include <stdarg.h>
+#include <errno.h>
+#include <sys/socket.h>
+#include <sys/poll.h>
+#include <sys/time.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+#include <adns.h>
+#include <pwd.h>
+#include <sys/types.h>
+
+#include "secnet.h"
+#include "util.h"
+#include "conffile.h"
+#include "modules.h"
+
+/* Command-line options (possibly config-file options too) */
+static char *configfile="/etc/secnet/secnet.conf";
+static char *userid=NULL;
+static uid_t uid=0;
+static bool_t background=True;
+static char *pidfile=NULL;
+
+/* Structures dealing with poll() call */
+struct poll_interest {
+    beforepoll_fn *before;
+    afterpoll_fn *after;
+    void *state;
+    uint32_t max_nfds;
+    uint32_t nfds;
+    string_t desc;
+    struct poll_interest *next;
+};
+static struct poll_interest *reg=NULL;
+static uint32_t total_nfds=10;
+
+static bool_t finished=False;
+
+/* Parse the command line options */
+static void parse_options(int argc, char **argv)
+{
+    int c;
+
+    while (True) {
+       int option_index = 0;
+       static struct option long_options[] = {
+           {"verbose", 0, 0, 'v'},
+           {"nowarnings", 0, 0, 'w'},
+           {"help", 0, 0, 2},
+           {"version", 0, 0, 1},
+           {"nodetach", 0, 0, 'n'},
+           {"silent", 0, 0, 'f'},
+           {"quiet", 0, 0, 'f'},
+           {"debug", 1, 0, 'd'},
+           {"config", 1, 0, 'c'},
+           {0,0,0,0}
+       };
+
+       c=getopt_long(argc, argv, "vwdnc:ft:",
+                     long_options, &option_index);
+       if (c==-1)
+           break;
+
+       switch(c) {
+       case 2:
+           /* Help */
+           fprintf(stderr,
+                   "Usage: secnet [OPTION]...\n\n"
+                   "  -f, --silent, --quiet   suppress error messages\n"
+                   "  -w, --nowarnings        suppress warnings\n"
+                   "  -v, --verbose           output extra diagnostics\n"
+                   "  -c, --config=filename   specify a configuration file\n"
+                   "  -n, --nodetach          do not run in background\n"
+                   "  -d, --debug=item,...    set debug options\n"
+                   "      --help              display this help and exit\n"
+                   "      --version           output version information and exit\n"
+               );
+           exit(0);
+           break;
+      
+       case 1:
+           /* Version */
+           fprintf(stderr,"%s\n",version);
+           exit(0);
+           break;
+
+       case 'v':
+           message_level|=M_INFO|M_WARNING|M_ERROR|M_FATAL;
+           break;
+
+       case 'n':
+           background=False;
+           break;
+
+       case 'd':
+           message_level|=M_DEBUG_CONFIG|M_DEBUG_PHASE;
+           break;
+
+       case 'f':
+           message_level=M_FATAL;
+           break;
+
+       case 'c':
+           if (optarg)
+               configfile=safe_strdup(optarg,"config_filename");
+           else
+               fatal("secnet: no config filename specified");
+           break;
+
+       case '?':
+           break;
+
+       default:
+           Message(M_WARNING,"secnet: Unknown getopt code %c\n",c);
+       }
+    }
+
+    if (argc-optind != 0) {
+       Message(M_WARNING,"secnet: You gave extra command line parameters, "
+               "which were ignored.\n");
+    }
+}
+
+static void setup(dict_t *config)
+{
+    list_t *l;
+    item_t *site;
+    dict_t *system;
+    struct log_if *log;
+    struct passwd *pw;
+    struct cloc loc;
+    int i;
+
+    l=dict_lookup(config,"system");
+
+    if (!l || list_elem(l,0)->type!=t_dict) {
+       fatal("configuration does not include a \"system\" dictionary\n");
+    }
+    system=list_elem(l,0)->data.dict;
+    loc=list_elem(l,0)->loc;
+
+    /* Arrange systemwide log facility */
+    l=dict_lookup(system,"log");
+    if (!l) {
+       fatal("configuration does not include a system/log facility\n");
+    }
+    log=init_log(l);
+    log->log(log->st,LOG_DEBUG,"setup: logging started");
+
+    /* Who are we supposed to run as? */
+    userid=dict_read_string(system,"userid",False,"system",loc);
+    if (userid) {
+       do {
+           pw=getpwent();
+           if (pw && strcmp(pw->pw_name,userid)==0) {
+               uid=pw->pw_uid;
+               break;
+           }
+       } while(pw);
+       endpwent();
+       if (uid==0) {
+           fatal("userid \"%s\" not found\n",userid);
+       }
+    }
+
+    /* Pidfile name */
+    pidfile=dict_read_string(system,"pidfile",False,"system",loc);
+
+    /* Go along site list, starting sites */
+    l=dict_lookup(config,"sites");
+    if (!l) {
+       fatal("configuration did not define any remote sites\n");
+    }
+    i=0;
+    while ((site=list_elem(l, i++))) {
+       struct site_if *s;
+       if (site->type!=t_closure) {
+           cfgfatal(site->loc,"system","non-closure in site list");
+       }
+       if (site->data.closure->type!=CL_SITE) {
+           cfgfatal(site->loc,"system","non-site closure in site list");
+       }
+       s=site->data.closure->interface;
+       s->control(s->st,True);
+    }
+}
+
+void register_for_poll(void *st, beforepoll_fn *before,
+                      afterpoll_fn *after, uint32_t max_nfds, string_t desc)
+{
+    struct poll_interest *i;
+
+    i=safe_malloc(sizeof(*i),"register_for_poll");
+    i->before=before;
+    i->after=after;
+    i->state=st;
+    i->max_nfds=max_nfds;
+    i->nfds=0;
+    i->desc=desc;
+    total_nfds+=max_nfds;
+    i->next=reg;
+    reg=i;
+    return;
+}
+
+static void system_phase_hook(void *sst, uint32_t newphase)
+{
+    if (newphase==PHASE_SHUTDOWN && pidfile) {
+       /* Try to unlink the pidfile; don't care if it fails */
+       unlink(pidfile);
+    }
+}
+
+static void run(void)
+{
+    struct timeval tv_now;
+    uint64_t now;
+    struct poll_interest *i;
+    int rv, nfds, remain, idx;
+    int timeout;
+    struct pollfd *fds;
+
+    fds=alloca(sizeof(*fds)*total_nfds);
+    if (!fds) {
+       fatal("run: couldn't alloca\n");
+    }
+
+    while (!finished) {
+       if (gettimeofday(&tv_now, NULL)!=0) {
+           fatal_perror("main loop: gettimeofday");
+       }
+       now=(tv_now.tv_sec*1000)+(tv_now.tv_usec/1000);
+       idx=0;
+       for (i=reg; i; i=i->next) {
+           i->after(i->state, fds+idx, i->nfds, &tv_now, &now);
+           idx+=i->nfds;
+       }
+       remain=total_nfds;
+       idx=0;
+       timeout=-1;
+       for (i=reg; i; i=i->next) {
+           nfds=remain;
+           rv=i->before(i->state, fds+idx, &nfds, &timeout, &tv_now, &now);
+           if (rv!=0) {
+               /* XXX we need to handle this properly: increase the
+                  nfds available */
+               fatal("run: beforepoll_fn (%s) returns %d\n",i->desc,rv);
+           }
+           if (timeout<-1) {
+               fatal("run: beforepoll_fn (%s) set timeout to %d\n",timeout);
+           }
+           idx+=nfds;
+           remain-=nfds;
+           i->nfds=nfds;
+       }
+       do {
+           rv=poll(fds, idx, timeout);
+           if (rv<0) {
+               if (errno!=EINTR) {
+                   fatal_perror("run: poll");
+               }
+           }
+       } while (rv<0);
+    }
+}
+
+static void droppriv(void)
+{
+    FILE *pf=NULL;
+    pid_t p;
+
+    add_hook(PHASE_SHUTDOWN,system_phase_hook,NULL);
+
+    /* Background now, if we're supposed to: we may be unable to write the
+       pidfile if we don't. */
+    if (background) {
+       printf("goto background\n");
+       /* Open the pidfile before forking - that way the parent can tell
+          whether it succeeds */
+       if (pidfile) {
+           pf=fopen(pidfile,"w");
+           if (!pf) {
+               fatal_perror("cannot open pidfile \"%s\"",pidfile);
+           }
+       } else {
+           Message(M_WARNING,"secnet: no pidfile configured, but "
+                   "backgrounding anyway\n");
+       }
+       p=fork();
+       if (p>0) {
+           if (pf) {
+               /* Parent process - write pidfile, exit */
+               fprintf(pf,"%d\n",p);
+               fclose(pf);
+           }
+           exit(0);
+       } else if (p==0) {
+           /* Child process - all done, just carry on */
+           if (pf) fclose(pf);
+           printf("child\n");
+       } else {
+           /* Error */
+           fatal_perror("cannot fork");
+           exit(1);
+       }
+    } else {
+       if (pidfile) {
+           pf=fopen(pidfile,"w");
+           if (!pf) {
+               fatal_perror("cannot open pidfile \"%s\"",pidfile);
+           }
+           fprintf(pf,"%d\n",getpid());
+           fclose(pf);
+       }
+    }
+
+    /* Drop privilege now, if configured to do so */
+    if (uid!=0) {
+       if (setuid(uid)!=0) {
+           fatal_perror("can't set uid to \"%s\"",userid);
+       }
+    }
+}
+
+int main(int argc, char **argv)
+{
+    dict_t *config;
+
+    enter_phase(PHASE_GETOPTS);
+    parse_options(argc,argv);
+
+    enter_phase(PHASE_READCONFIG);
+    config=read_conffile(configfile);
+
+    enter_phase(PHASE_SETUP);
+    setup(config);
+    
+    enter_phase(PHASE_DROPPRIV);
+    droppriv();
+
+    enter_phase(PHASE_RUN);
+    run();
+
+    enter_phase(PHASE_SHUTDOWN);
+
+    return 0;
+}
+
diff --git a/secnet.h b/secnet.h
new file mode 100644 (file)
index 0000000..ce5f9d6
--- /dev/null
+++ b/secnet.h
@@ -0,0 +1,401 @@
+/* Core interface of secnet, to be used by all modules */
+
+#ifndef secnet_h
+#define secnet_h
+
+#include <stdlib.h>
+#include <stdint.h>
+#include <stdarg.h>
+#include <syslog.h>
+#include <sys/poll.h>
+#include <netinet/in.h>
+#include "config.h"
+
+
+typedef char *string_t;
+typedef enum {False,True} bool_t;
+
+#define ASSERT(x) do { if (!(x)) { fatal("assertion failed line " __LINE__ \
+                                        " file " __FILE__ "\n"); } while(0)
+
+/***** SHARED types *****/
+
+/* These are stored in HOST byte order */
+struct subnet {
+    uint32_t prefix;
+    uint32_t mask;
+};
+
+struct subnet_list {
+    uint32_t entries;
+    struct subnet *list;
+};
+
+/* Match an address (in HOST byte order) with a subnet list.
+   Returns True if matched. */
+extern bool_t subnet_match(struct subnet_list *list, uint32_t address);
+
+/***** END of shared types *****/
+
+/***** CONFIGURATION support *****/
+
+typedef struct dict dict_t;        /* Configuration dictionary */
+typedef struct closure closure_t;
+typedef struct item item_t;
+typedef struct list list_t;        /* A list of items */
+
+/* Configuration file location, for error-reporting */
+struct cloc {
+    string_t file;
+    uint32_t line;
+};
+
+/* Modules export closures, which can be invoked from the configuration file.
+   "Invoking" a closure usually returns another closure (of a different
+   type), but can actually return any configuration object. */
+typedef list_t *(apply_fn)(closure_t *self, struct cloc loc,
+                          dict_t *context, list_t *data);
+struct closure {
+    string_t description; /* For debugging */
+    uint32_t type; /* Central registry... */
+    apply_fn *apply;
+    void *interface; /* Interface for use inside secnet; depends on type */
+};
+
+enum types { t_null, t_bool, t_string, t_number, t_dict, t_closure };
+struct item {
+    enum types type;
+    union {
+       bool_t bool;
+       string_t string;
+       uint32_t number;
+       dict_t *dict;
+       closure_t *closure;
+    } data;
+    struct cloc loc;
+};
+
+struct list {
+    item_t *item;
+    struct list *next;
+};
+
+/* In the following two lookup functions, NULL means 'not found' */
+/* Lookup a value in the specified dictionary, or its parents */
+extern list_t *dict_lookup(dict_t *dict, string_t key);
+/* Lookup a value in just the specified dictionary */
+extern list_t *dict_lookup_primitive(dict_t *dict, string_t key);
+/* Add a value to the specified dictionary */
+extern void dict_add(dict_t *dict, string_t key, list_t *val);
+/* Obtain an array of keys in the dictionary. malloced; caller frees */
+extern string_t *dict_keys(dict_t *dict);
+
+/* List-manipulation functions */
+extern list_t *list_new(void);
+extern list_t *list_append(list_t *a, item_t *i);
+extern list_t *list_append_list(list_t *a, list_t *b);
+/* Returns an item from the list (index starts at 0), or NULL */
+extern item_t *list_elem(list_t *l, uint32_t index);
+
+/* Convenience functions */
+extern list_t *new_closure(closure_t *cl);
+extern void add_closure(dict_t *dict, string_t name, apply_fn apply);
+extern void *find_cl_if(dict_t *dict, string_t name, uint32_t type,
+                       bool_t fail_if_invalid, string_t desc,
+                       struct cloc loc);
+extern item_t *dict_find_item(dict_t *dict, string_t key, bool_t required,
+                             string_t desc, struct cloc loc);
+extern string_t dict_read_string(dict_t *dict, string_t key, bool_t required,
+                                string_t desc, struct cloc loc);
+extern uint32_t dict_read_number(dict_t *dict, string_t key, bool_t required,
+                                string_t desc, struct cloc loc, uint32_t def);
+extern bool_t dict_read_bool(dict_t *dict, string_t key, bool_t required,
+                            string_t desc, struct cloc loc, bool_t def);
+extern void dict_read_subnet_list(dict_t *dict, string_t key, bool_t required,
+                                 string_t desc, struct cloc loc,
+                                 struct subnet_list *sl);
+extern uint32_t string_to_ipaddr(item_t *i, string_t desc);
+
+/***** END of configuration support *****/
+
+/***** UTILITY functions *****/
+
+#define M_WARNING      1
+#define M_ERROR                2
+#define M_FATAL                4
+#define M_INFO         8
+#define M_DEBUG_CONFIG 16
+#define M_DEBUG_PHASE  32
+
+extern void fatal(char *message, ...);
+extern void fatal_perror(char *message, ...);
+extern void fatal_status(int status, char *message, ...);
+extern void fatal_perror_status(int status, char *message, ...);
+extern void cfgfatal(struct cloc loc, string_t facility, char *message, ...);
+
+extern char *safe_strdup(char *string, char *message);
+extern void *safe_malloc(size_t size, char *message);
+
+extern void Message(uint32_t class, char *message, ...);
+
+extern string_t ipaddr_to_string(uint32_t addr);
+extern string_t subnet_to_string(struct subnet *sn);
+
+/***** END of utility functions *****/
+
+/***** SCHEDULING support */
+
+/* "now" is current program time, in milliseconds. It is derived
+   (once) from tv_now. If nfds_io is insufficient for your needs, set
+   it to the required number and return ERANGE. timeout is in milliseconds;
+   if it is too high then lower it. It starts at -1 (==infinite) */
+typedef int beforepoll_fn(void *st, struct pollfd *fds, int *nfds_io,
+                         int *timeout_io, const struct timeval *tv_now,
+                         uint64_t *now);
+typedef void afterpoll_fn(void *st, struct pollfd *fds, int nfds,
+                         const struct timeval *tv_now, uint64_t *now);
+
+/* Register interest in the main loop of the program. Before a call
+   to poll() your supplied beforepoll function will be called. After
+   the call to poll() the supplied afterpoll function will be called.
+   max_nfds is a _hint_ about the maximum number of struct pollfd
+   structures you may require - you can always ask for more in
+   *nfds_io. */
+extern void register_for_poll(void *st, beforepoll_fn *before,
+                             afterpoll_fn *after, uint32_t max_nfds,
+                             string_t desc);
+
+/***** END of scheduling support */
+
+/***** PROGRAM LIFETIME support */
+
+/* The secnet program goes through a number of phases in its lifetime.
+   Module code may arrange to be called just as various phases are
+   entered. */
+
+#define PHASE_INIT          0
+#define PHASE_GETOPTS       1  /* Process command-line arguments */
+#define PHASE_READCONFIG    2  /* Parse and process configuration file */
+#define PHASE_SETUP         3  /* Process information in configuration */
+#define PHASE_DROPPRIV      4  /* Last chance for privileged operations */
+#define PHASE_RUN           5
+#define PHASE_SHUTDOWN      6  /* About to die; delete key material, etc. */
+#define NR_PHASES           7
+
+typedef void hook_fn(void *self, uint32_t newphase);
+bool_t add_hook(uint32_t phase, hook_fn *f, void *state);
+bool_t remove_hook(uint32_t phase, hook_fn *f, void *state);
+
+/***** END of program lifetime support *****/
+
+/***** MODULE support *****/
+
+/* Module initialisation function type - modules export one function of
+   this type which is called to initialise them. For dynamically loaded
+   modules it's called "secnet_module". */
+typedef void (init_module)(dict_t *dict);
+
+/***** END of module support *****/
+
+/***** CLOSURE TYPES and interface definitions *****/
+
+#define CL_PURE        0
+#define CL_RESOLVER    1
+#define CL_RANDOMSRC   2
+#define CL_RSAPUBKEY   3
+#define CL_RSAPRIVKEY  4
+#define CL_COMM        5
+#define CL_IPIF        6
+#define CL_LOG         7
+#define CL_SITE        8
+#define CL_TRANSFORM   9
+#define CL_NETLINK    10
+#define CL_DH         11
+#define CL_HASH       12
+#define CL_BUFFER     13
+
+struct buffer_if;
+
+/* PURE closure requires no interface */
+
+/* RESOLVER interface */
+
+/* Answers to queries are delivered to a function of this
+   type. 'address' will be NULL if there was a problem with the query. It
+   will be freed once resolve_answer_fn returns. It is in network byte
+   order. */
+typedef void resolve_answer_fn(void *st, struct in_addr *addr);
+typedef bool_t resolve_request_fn(void *st, string_t name,
+                                 resolve_answer_fn *cb, void *cst);
+struct resolver_if {
+    void *st;
+    resolve_request_fn *request;
+};
+
+/* RANDOMSRC interface */
+
+/* Return some random data. Returns TRUE for success. */
+typedef bool_t random_fn(void *st, uint32_t bytes, uint8_t *buff);
+
+struct random_if {
+    void *st;
+    bool_t blocking;
+    random_fn *generate;
+};
+
+/* RSAPUBKEY interface */
+
+typedef bool_t rsa_checksig_fn(void *st, uint8_t *data, uint32_t datalen,
+                              string_t signature);
+struct rsapubkey_if {
+    void *st;
+    rsa_checksig_fn *check;
+};
+
+/* RSAPRIVKEY interface */
+
+typedef string_t rsa_makesig_fn(void *st, uint8_t *data, uint32_t datalen);
+struct rsaprivkey_if {
+    void *st;
+    rsa_makesig_fn *sign;
+};
+
+/* COMM interface */
+
+/* Return True if the packet was processed, and shouldn't be passed to
+   any other potential receivers. */
+typedef bool_t comm_notify_fn(void *state, struct buffer_if *buf,
+                           struct sockaddr_in *source);
+typedef void comm_request_notify_fn(void *commst, void *nst,
+                                   comm_notify_fn *fn);
+typedef void comm_release_notify_fn(void *commst, void *nst,
+                                   comm_notify_fn *fn);
+typedef bool_t comm_sendmsg_fn(void *commst, struct buffer_if *buf,
+                              struct sockaddr_in *dest);
+struct comm_if {
+    void *st;
+    comm_request_notify_fn *request_notify;
+    comm_release_notify_fn *release_notify;
+    comm_sendmsg_fn *sendmsg;
+};
+
+/* LOG interface */
+
+typedef void log_msg_fn(void *st, int priority, char *message, ...);
+typedef void log_vmsg_fn(void *st, int priority, char *message, va_list args);
+struct log_if {
+    void *st;
+    log_msg_fn *log;
+    log_vmsg_fn *vlog;
+};
+/* (convenience function, defined in util.c) */
+extern void log(struct log_if *lf, int priority, char *message, ...);
+
+/* SITE interface */
+
+/* Pretty much a placeholder; allows starting and stopping of processing,
+   key expiry, etc. */
+typedef void site_control_fn(void *st, bool_t run);
+typedef uint32_t site_status_fn(void *st);
+struct site_if {
+    void *st;
+    site_control_fn *control;
+    site_status_fn *status;
+};
+
+/* TRANSFORM interface */
+
+/* A reversable transformation. Transforms buffer in-place; may add
+   data to start or end. Maximum amount of data to be added specified
+   in max_start_pad and max_end_pad. (Reverse transformations decrease
+   length, of course.)  Transformations may be key-dependent, in which
+   case key material is passed in at initialisation time. They may
+   also depend on internal factors (eg. time) and keep internal
+   state. A struct transform_if only represents a particular type of
+   transformation; instances of the transformation (eg. with
+   particular key material) have a different C type. */
+
+typedef struct transform_inst_if *transform_createinstance_fn(void *st);
+typedef bool_t transform_setkey_fn(void *st, uint8_t *key, uint32_t keylen);
+typedef void transform_delkey_fn(void *st);
+typedef void transform_destroyinstance_fn(void *st);
+/* Returns 0 for 'all is well', any other value for a problem */
+typedef uint32_t transform_apply_fn(void *st, struct buffer_if *buf,
+                                   char **errmsg);
+
+struct transform_inst_if {
+    void *st;
+    transform_setkey_fn *setkey;
+    transform_delkey_fn *delkey;
+    transform_apply_fn *forwards;
+    transform_apply_fn *reverse;
+    transform_destroyinstance_fn *destroy;
+};
+
+struct transform_if {
+    void *st;
+    uint32_t max_start_pad;
+    uint32_t max_end_pad;
+    uint32_t keylen;
+    transform_createinstance_fn *create;
+};
+
+/* NETLINK interface */
+
+/* Used by netlink to deliver to site, and by site to deliver to netlink.
+   cid is the client identifier returned by netlink_regnets_fn */
+typedef void netlink_deliver_fn(void *st, void *cid, struct buffer_if *buf);
+/* Register for packets from specified networks. Return value is client
+   identifier. */
+typedef void *netlink_regnets_fn(void *st, struct subnet_list *networks,
+                                netlink_deliver_fn *deliver, void *dst,
+                                uint32_t max_start_pad, uint32_t max_end_pad);
+
+struct netlink_if {
+    void *st;
+    netlink_regnets_fn *regnets;
+    netlink_deliver_fn *deliver;
+};
+
+/* DH interface */
+
+/* Returns public key as a malloced hex string */
+typedef string_t dh_makepublic_fn(void *st, uint8_t *secret,
+                                 uint32_t secretlen);
+/* Fills buffer (up to buflen) with shared secret */
+typedef void dh_makeshared_fn(void *st, uint8_t *secret,
+                             uint32_t secretlen, string_t rempublic,
+                             uint8_t *sharedsecret, uint32_t buflen);
+struct dh_if {
+    void *st;
+    uint32_t len; /* Approximate size of modulus in bytes */
+    dh_makepublic_fn *makepublic;
+    dh_makeshared_fn *makeshared;
+};
+
+/* HASH interface */
+
+typedef void *hash_init_fn(void);
+typedef void hash_update_fn(void *st, uint8_t const *buf, uint32_t len);
+typedef void hash_final_fn(void *st, uint8_t *digest);
+struct hash_if {
+    uint32_t len; /* Hash output length in bytes */
+    hash_init_fn *init;
+    hash_update_fn *update;
+    hash_final_fn *final;
+};
+
+/* BUFFER interface */
+
+struct buffer_if {
+    bool_t free;
+    string_t owner; /* Set to constant string */
+    uint32_t flags; /* How paranoid should we be? */
+    struct cloc loc; /* Where we were defined */
+    uint8_t *base;
+    uint8_t *start;
+    uint32_t size; /* Size of buffer contents */
+    uint32_t len; /* Total length allocated at base */
+};
+
+#endif /* secnet_h */
diff --git a/serpent.c b/serpent.c
new file mode 100644 (file)
index 0000000..51b27ba
--- /dev/null
+++ b/serpent.c
@@ -0,0 +1,320 @@
+/*
+ * This file is
+ *   Copyright (C) 1998 Ross Anderson, Eli Biham, Lars Knudsen
+ *
+ * For more information see http://www.cl.cam.ac.uk/users/rja14/serpent.html
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2, or (at your option)
+ *  any later version.
+ *  
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *  
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software Foundation,
+ *  Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. 
+ *
+ */
+
+#include "secnet.h"
+
+#include "serpent.h"
+#include "serpentsboxes.h"
+
+void serpent_makekey(struct keyInstance *key, int keyLen,
+           uint8_t *keyMaterial)
+{
+    uint32_t i,j;
+    uint32_t w[132],k[132];
+
+    for(i=0; i<keyLen/32; i++)
+       w[i]=keyMaterial[i];
+    if(keyLen<256)
+       w[i]=(keyMaterial[i]&((1L<<((keyLen&31)))-1))|(1L<<((keyLen&31)));
+    for(i++; i<8; i++)
+       w[i]=0;
+    for(i=8; i<16; i++)
+       w[i]=ROL(w[i-8]^w[i-5]^w[i-3]^w[i-1]^PHI^(i-8),11);
+    for(i=0; i<8; i++)
+       w[i]=w[i+8];
+    for(i=8; i<132; i++)
+       w[i]=ROL(w[i-8]^w[i-5]^w[i-3]^w[i-1]^PHI^i,11);
+
+    RND03(w[  0], w[  1], w[  2], w[  3], k[  0], k[  1], k[  2], k[  3]);
+    RND02(w[  4], w[  5], w[  6], w[  7], k[  4], k[  5], k[  6], k[  7]);
+    RND01(w[  8], w[  9], w[ 10], w[ 11], k[  8], k[  9], k[ 10], k[ 11]);
+    RND00(w[ 12], w[ 13], w[ 14], w[ 15], k[ 12], k[ 13], k[ 14], k[ 15]);
+    RND31(w[ 16], w[ 17], w[ 18], w[ 19], k[ 16], k[ 17], k[ 18], k[ 19]);
+    RND30(w[ 20], w[ 21], w[ 22], w[ 23], k[ 20], k[ 21], k[ 22], k[ 23]);
+    RND29(w[ 24], w[ 25], w[ 26], w[ 27], k[ 24], k[ 25], k[ 26], k[ 27]);
+    RND28(w[ 28], w[ 29], w[ 30], w[ 31], k[ 28], k[ 29], k[ 30], k[ 31]);
+    RND27(w[ 32], w[ 33], w[ 34], w[ 35], k[ 32], k[ 33], k[ 34], k[ 35]);
+    RND26(w[ 36], w[ 37], w[ 38], w[ 39], k[ 36], k[ 37], k[ 38], k[ 39]);
+    RND25(w[ 40], w[ 41], w[ 42], w[ 43], k[ 40], k[ 41], k[ 42], k[ 43]);
+    RND24(w[ 44], w[ 45], w[ 46], w[ 47], k[ 44], k[ 45], k[ 46], k[ 47]);
+    RND23(w[ 48], w[ 49], w[ 50], w[ 51], k[ 48], k[ 49], k[ 50], k[ 51]);
+    RND22(w[ 52], w[ 53], w[ 54], w[ 55], k[ 52], k[ 53], k[ 54], k[ 55]);
+    RND21(w[ 56], w[ 57], w[ 58], w[ 59], k[ 56], k[ 57], k[ 58], k[ 59]);
+    RND20(w[ 60], w[ 61], w[ 62], w[ 63], k[ 60], k[ 61], k[ 62], k[ 63]);
+    RND19(w[ 64], w[ 65], w[ 66], w[ 67], k[ 64], k[ 65], k[ 66], k[ 67]);
+    RND18(w[ 68], w[ 69], w[ 70], w[ 71], k[ 68], k[ 69], k[ 70], k[ 71]);
+    RND17(w[ 72], w[ 73], w[ 74], w[ 75], k[ 72], k[ 73], k[ 74], k[ 75]);
+    RND16(w[ 76], w[ 77], w[ 78], w[ 79], k[ 76], k[ 77], k[ 78], k[ 79]);
+    RND15(w[ 80], w[ 81], w[ 82], w[ 83], k[ 80], k[ 81], k[ 82], k[ 83]);
+    RND14(w[ 84], w[ 85], w[ 86], w[ 87], k[ 84], k[ 85], k[ 86], k[ 87]);
+    RND13(w[ 88], w[ 89], w[ 90], w[ 91], k[ 88], k[ 89], k[ 90], k[ 91]);
+    RND12(w[ 92], w[ 93], w[ 94], w[ 95], k[ 92], k[ 93], k[ 94], k[ 95]);
+    RND11(w[ 96], w[ 97], w[ 98], w[ 99], k[ 96], k[ 97], k[ 98], k[ 99]);
+    RND10(w[100], w[101], w[102], w[103], k[100], k[101], k[102], k[103]);
+    RND09(w[104], w[105], w[106], w[107], k[104], k[105], k[106], k[107]);
+    RND08(w[108], w[109], w[110], w[111], k[108], k[109], k[110], k[111]);
+    RND07(w[112], w[113], w[114], w[115], k[112], k[113], k[114], k[115]);
+    RND06(w[116], w[117], w[118], w[119], k[116], k[117], k[118], k[119]);
+    RND05(w[120], w[121], w[122], w[123], k[120], k[121], k[122], k[123]);
+    RND04(w[124], w[125], w[126], w[127], k[124], k[125], k[126], k[127]);
+    RND03(w[128], w[129], w[130], w[131], k[128], k[129], k[130], k[131]);
+
+    for(i=0; i<=32; i++)
+       for(j=0; j<4; j++)
+           key->subkeys[i][j] = k[4*i+j];
+}
+
+void serpent_encrypt(struct keyInstance *key,
+                    uint32_t plaintext[4], 
+                    uint32_t ciphertext[4])
+{
+    register uint32_t x0, x1, x2, x3;
+    register uint32_t y0, y1, y2, y3;
+
+    x0=plaintext[0];
+    x1=plaintext[1];
+    x2=plaintext[2];
+    x3=plaintext[3];
+
+    /* Start to encrypt the plaintext x */
+    keying(x0, x1, x2, x3, key->subkeys[ 0]);
+    RND00(x0, x1, x2, x3, y0, y1, y2, y3);
+    transform(y0, y1, y2, y3, x0, x1, x2, x3);
+    keying(x0, x1, x2, x3, key->subkeys[ 1]);
+    RND01(x0, x1, x2, x3, y0, y1, y2, y3);
+    transform(y0, y1, y2, y3, x0, x1, x2, x3);
+    keying(x0, x1, x2, x3, key->subkeys[ 2]);
+    RND02(x0, x1, x2, x3, y0, y1, y2, y3);
+    transform(y0, y1, y2, y3, x0, x1, x2, x3);
+    keying(x0, x1, x2, x3, key->subkeys[ 3]);
+    RND03(x0, x1, x2, x3, y0, y1, y2, y3);
+    transform(y0, y1, y2, y3, x0, x1, x2, x3);
+    keying(x0, x1, x2, x3, key->subkeys[ 4]);
+    RND04(x0, x1, x2, x3, y0, y1, y2, y3);
+    transform(y0, y1, y2, y3, x0, x1, x2, x3);
+    keying(x0, x1, x2, x3, key->subkeys[ 5]);
+    RND05(x0, x1, x2, x3, y0, y1, y2, y3);
+    transform(y0, y1, y2, y3, x0, x1, x2, x3);
+    keying(x0, x1, x2, x3, key->subkeys[ 6]);
+    RND06(x0, x1, x2, x3, y0, y1, y2, y3);
+    transform(y0, y1, y2, y3, x0, x1, x2, x3);
+    keying(x0, x1, x2, x3, key->subkeys[ 7]);
+    RND07(x0, x1, x2, x3, y0, y1, y2, y3);
+    transform(y0, y1, y2, y3, x0, x1, x2, x3);
+    keying(x0, x1, x2, x3, key->subkeys[ 8]);
+    RND08(x0, x1, x2, x3, y0, y1, y2, y3);
+    transform(y0, y1, y2, y3, x0, x1, x2, x3);
+    keying(x0, x1, x2, x3, key->subkeys[ 9]);
+    RND09(x0, x1, x2, x3, y0, y1, y2, y3);
+    transform(y0, y1, y2, y3, x0, x1, x2, x3);
+    keying(x0, x1, x2, x3, key->subkeys[10]);
+    RND10(x0, x1, x2, x3, y0, y1, y2, y3);
+    transform(y0, y1, y2, y3, x0, x1, x2, x3);
+    keying(x0, x1, x2, x3, key->subkeys[11]);
+    RND11(x0, x1, x2, x3, y0, y1, y2, y3);
+    transform(y0, y1, y2, y3, x0, x1, x2, x3);
+    keying(x0, x1, x2, x3, key->subkeys[12]);
+    RND12(x0, x1, x2, x3, y0, y1, y2, y3);
+    transform(y0, y1, y2, y3, x0, x1, x2, x3);
+    keying(x0, x1, x2, x3, key->subkeys[13]);
+    RND13(x0, x1, x2, x3, y0, y1, y2, y3);
+    transform(y0, y1, y2, y3, x0, x1, x2, x3);
+    keying(x0, x1, x2, x3, key->subkeys[14]);
+    RND14(x0, x1, x2, x3, y0, y1, y2, y3);
+    transform(y0, y1, y2, y3, x0, x1, x2, x3);
+    keying(x0, x1, x2, x3, key->subkeys[15]);
+    RND15(x0, x1, x2, x3, y0, y1, y2, y3);
+    transform(y0, y1, y2, y3, x0, x1, x2, x3);
+    keying(x0, x1, x2, x3, key->subkeys[16]);
+    RND16(x0, x1, x2, x3, y0, y1, y2, y3);
+    transform(y0, y1, y2, y3, x0, x1, x2, x3);
+    keying(x0, x1, x2, x3, key->subkeys[17]);
+    RND17(x0, x1, x2, x3, y0, y1, y2, y3);
+    transform(y0, y1, y2, y3, x0, x1, x2, x3);
+    keying(x0, x1, x2, x3, key->subkeys[18]);
+    RND18(x0, x1, x2, x3, y0, y1, y2, y3);
+    transform(y0, y1, y2, y3, x0, x1, x2, x3);
+    keying(x0, x1, x2, x3, key->subkeys[19]);
+    RND19(x0, x1, x2, x3, y0, y1, y2, y3);
+    transform(y0, y1, y2, y3, x0, x1, x2, x3);
+    keying(x0, x1, x2, x3, key->subkeys[20]);
+    RND20(x0, x1, x2, x3, y0, y1, y2, y3);
+    transform(y0, y1, y2, y3, x0, x1, x2, x3);
+    keying(x0, x1, x2, x3, key->subkeys[21]);
+    RND21(x0, x1, x2, x3, y0, y1, y2, y3);
+    transform(y0, y1, y2, y3, x0, x1, x2, x3);
+    keying(x0, x1, x2, x3, key->subkeys[22]);
+    RND22(x0, x1, x2, x3, y0, y1, y2, y3);
+    transform(y0, y1, y2, y3, x0, x1, x2, x3);
+    keying(x0, x1, x2, x3, key->subkeys[23]);
+    RND23(x0, x1, x2, x3, y0, y1, y2, y3);
+    transform(y0, y1, y2, y3, x0, x1, x2, x3);
+    keying(x0, x1, x2, x3, key->subkeys[24]);
+    RND24(x0, x1, x2, x3, y0, y1, y2, y3);
+    transform(y0, y1, y2, y3, x0, x1, x2, x3);
+    keying(x0, x1, x2, x3, key->subkeys[25]);
+    RND25(x0, x1, x2, x3, y0, y1, y2, y3);
+    transform(y0, y1, y2, y3, x0, x1, x2, x3);
+    keying(x0, x1, x2, x3, key->subkeys[26]);
+    RND26(x0, x1, x2, x3, y0, y1, y2, y3);
+    transform(y0, y1, y2, y3, x0, x1, x2, x3);
+    keying(x0, x1, x2, x3, key->subkeys[27]);
+    RND27(x0, x1, x2, x3, y0, y1, y2, y3);
+    transform(y0, y1, y2, y3, x0, x1, x2, x3);
+    keying(x0, x1, x2, x3, key->subkeys[28]);
+    RND28(x0, x1, x2, x3, y0, y1, y2, y3);
+    transform(y0, y1, y2, y3, x0, x1, x2, x3);
+    keying(x0, x1, x2, x3, key->subkeys[29]);
+    RND29(x0, x1, x2, x3, y0, y1, y2, y3);
+    transform(y0, y1, y2, y3, x0, x1, x2, x3);
+    keying(x0, x1, x2, x3, key->subkeys[30]);
+    RND30(x0, x1, x2, x3, y0, y1, y2, y3);
+    transform(y0, y1, y2, y3, x0, x1, x2, x3);
+    keying(x0, x1, x2, x3, key->subkeys[31]);
+    RND31(x0, x1, x2, x3, y0, y1, y2, y3);
+    x0 = y0; x1 = y1; x2 = y2; x3 = y3;
+    keying(x0, x1, x2, x3, key->subkeys[32]);
+    /* The ciphertext is now in x */
+
+    ciphertext[0] = x0;
+    ciphertext[1] = x1;
+    ciphertext[2] = x2;
+    ciphertext[3] = x3;
+}
+
+void serpent_decrypt(struct keyInstance *key,
+                    uint32_t ciphertext[4],
+                    uint32_t plaintext[4])
+{
+    register uint32_t x0, x1, x2, x3;
+    register uint32_t y0, y1, y2, y3;
+
+    x0=ciphertext[0];
+    x1=ciphertext[1];
+    x2=ciphertext[2];
+    x3=ciphertext[3];
+
+    /* Start to decrypt the ciphertext x */
+    keying(x0, x1, x2, x3, key->subkeys[32]);
+    InvRND31(x0, x1, x2, x3, y0, y1, y2, y3);
+    keying(y0, y1, y2, y3, key->subkeys[31]);
+    inv_transform(y0, y1, y2, y3, x0, x1, x2, x3);
+    InvRND30(x0, x1, x2, x3, y0, y1, y2, y3);
+    keying(y0, y1, y2, y3, key->subkeys[30]);
+    inv_transform(y0, y1, y2, y3, x0, x1, x2, x3);
+    InvRND29(x0, x1, x2, x3, y0, y1, y2, y3);
+    keying(y0, y1, y2, y3, key->subkeys[29]);
+    inv_transform(y0, y1, y2, y3, x0, x1, x2, x3);
+    InvRND28(x0, x1, x2, x3, y0, y1, y2, y3);
+    keying(y0, y1, y2, y3, key->subkeys[28]);
+    inv_transform(y0, y1, y2, y3, x0, x1, x2, x3);
+    InvRND27(x0, x1, x2, x3, y0, y1, y2, y3);
+    keying(y0, y1, y2, y3, key->subkeys[27]);
+    inv_transform(y0, y1, y2, y3, x0, x1, x2, x3);
+    InvRND26(x0, x1, x2, x3, y0, y1, y2, y3);
+    keying(y0, y1, y2, y3, key->subkeys[26]);
+    inv_transform(y0, y1, y2, y3, x0, x1, x2, x3);
+    InvRND25(x0, x1, x2, x3, y0, y1, y2, y3);
+    keying(y0, y1, y2, y3, key->subkeys[25]);
+    inv_transform(y0, y1, y2, y3, x0, x1, x2, x3);
+    InvRND24(x0, x1, x2, x3, y0, y1, y2, y3);
+    keying(y0, y1, y2, y3, key->subkeys[24]);
+    inv_transform(y0, y1, y2, y3, x0, x1, x2, x3);
+    InvRND23(x0, x1, x2, x3, y0, y1, y2, y3);
+    keying(y0, y1, y2, y3, key->subkeys[23]);
+    inv_transform(y0, y1, y2, y3, x0, x1, x2, x3);
+    InvRND22(x0, x1, x2, x3, y0, y1, y2, y3);
+    keying(y0, y1, y2, y3, key->subkeys[22]);
+    inv_transform(y0, y1, y2, y3, x0, x1, x2, x3);
+    InvRND21(x0, x1, x2, x3, y0, y1, y2, y3);
+    keying(y0, y1, y2, y3, key->subkeys[21]);
+    inv_transform(y0, y1, y2, y3, x0, x1, x2, x3);
+    InvRND20(x0, x1, x2, x3, y0, y1, y2, y3);
+    keying(y0, y1, y2, y3, key->subkeys[20]);
+    inv_transform(y0, y1, y2, y3, x0, x1, x2, x3);
+    InvRND19(x0, x1, x2, x3, y0, y1, y2, y3);
+    keying(y0, y1, y2, y3, key->subkeys[19]);
+    inv_transform(y0, y1, y2, y3, x0, x1, x2, x3);
+    InvRND18(x0, x1, x2, x3, y0, y1, y2, y3);
+    keying(y0, y1, y2, y3, key->subkeys[18]);
+    inv_transform(y0, y1, y2, y3, x0, x1, x2, x3);
+    InvRND17(x0, x1, x2, x3, y0, y1, y2, y3);
+    keying(y0, y1, y2, y3, key->subkeys[17]);
+    inv_transform(y0, y1, y2, y3, x0, x1, x2, x3);
+    InvRND16(x0, x1, x2, x3, y0, y1, y2, y3);
+    keying(y0, y1, y2, y3, key->subkeys[16]);
+    inv_transform(y0, y1, y2, y3, x0, x1, x2, x3);
+    InvRND15(x0, x1, x2, x3, y0, y1, y2, y3);
+    keying(y0, y1, y2, y3, key->subkeys[15]);
+    inv_transform(y0, y1, y2, y3, x0, x1, x2, x3);
+    InvRND14(x0, x1, x2, x3, y0, y1, y2, y3);
+    keying(y0, y1, y2, y3, key->subkeys[14]);
+    inv_transform(y0, y1, y2, y3, x0, x1, x2, x3);
+    InvRND13(x0, x1, x2, x3, y0, y1, y2, y3);
+    keying(y0, y1, y2, y3, key->subkeys[13]);
+    inv_transform(y0, y1, y2, y3, x0, x1, x2, x3);
+    InvRND12(x0, x1, x2, x3, y0, y1, y2, y3);
+    keying(y0, y1, y2, y3, key->subkeys[12]);
+    inv_transform(y0, y1, y2, y3, x0, x1, x2, x3);
+    InvRND11(x0, x1, x2, x3, y0, y1, y2, y3);
+    keying(y0, y1, y2, y3, key->subkeys[11]);
+    inv_transform(y0, y1, y2, y3, x0, x1, x2, x3);
+    InvRND10(x0, x1, x2, x3, y0, y1, y2, y3);
+    keying(y0, y1, y2, y3, key->subkeys[10]);
+    inv_transform(y0, y1, y2, y3, x0, x1, x2, x3);
+    InvRND09(x0, x1, x2, x3, y0, y1, y2, y3);
+    keying(y0, y1, y2, y3, key->subkeys[ 9]);
+    inv_transform(y0, y1, y2, y3, x0, x1, x2, x3);
+    InvRND08(x0, x1, x2, x3, y0, y1, y2, y3);
+    keying(y0, y1, y2, y3, key->subkeys[ 8]);
+    inv_transform(y0, y1, y2, y3, x0, x1, x2, x3);
+    InvRND07(x0, x1, x2, x3, y0, y1, y2, y3);
+    keying(y0, y1, y2, y3, key->subkeys[ 7]);
+    inv_transform(y0, y1, y2, y3, x0, x1, x2, x3);
+    InvRND06(x0, x1, x2, x3, y0, y1, y2, y3);
+    keying(y0, y1, y2, y3, key->subkeys[ 6]);
+    inv_transform(y0, y1, y2, y3, x0, x1, x2, x3);
+    InvRND05(x0, x1, x2, x3, y0, y1, y2, y3);
+    keying(y0, y1, y2, y3, key->subkeys[ 5]);
+    inv_transform(y0, y1, y2, y3, x0, x1, x2, x3);
+    InvRND04(x0, x1, x2, x3, y0, y1, y2, y3);
+    keying(y0, y1, y2, y3, key->subkeys[ 4]);
+    inv_transform(y0, y1, y2, y3, x0, x1, x2, x3);
+    InvRND03(x0, x1, x2, x3, y0, y1, y2, y3);
+    keying(y0, y1, y2, y3, key->subkeys[ 3]);
+    inv_transform(y0, y1, y2, y3, x0, x1, x2, x3);
+    InvRND02(x0, x1, x2, x3, y0, y1, y2, y3);
+    keying(y0, y1, y2, y3, key->subkeys[ 2]);
+    inv_transform(y0, y1, y2, y3, x0, x1, x2, x3);
+    InvRND01(x0, x1, x2, x3, y0, y1, y2, y3);
+    keying(y0, y1, y2, y3, key->subkeys[ 1]);
+    inv_transform(y0, y1, y2, y3, x0, x1, x2, x3);
+    InvRND00(x0, x1, x2, x3, y0, y1, y2, y3);
+    x0 = y0; x1 = y1; x2 = y2; x3 = y3;
+    keying(x0, x1, x2, x3, key->subkeys[ 0]);
+    /* The plaintext is now in x */
+
+    plaintext[0] = x0;
+    plaintext[1] = x1;
+    plaintext[2] = x2;
+    plaintext[3] = x3;
+}
diff --git a/serpent.h b/serpent.h
new file mode 100644 (file)
index 0000000..dbbd36e
--- /dev/null
+++ b/serpent.h
@@ -0,0 +1,19 @@
+#ifndef serpent_h
+#define serpent_h
+
+struct keyInstance {
+      uint32_t key[8];             /* The key in binary */
+      uint32_t subkeys[33][4]; /* Serpent subkeys */
+};
+
+/*  Function protoypes  */
+void serpent_makekey(struct keyInstance *key, int keyLen,
+                    uint8_t *keyMaterial);
+
+void serpent_encrypt(struct keyInstance *key, uint32_t plaintext[4],
+                    uint32_t ciphertext[4]);
+
+void serpent_decrypt(struct keyInstance *key, uint32_t ciphertext[4],
+                    uint32_t plaintext[4]);
+
+#endif /* serpent_h */
diff --git a/serpentsboxes.h b/serpentsboxes.h
new file mode 100644 (file)
index 0000000..57450f3
--- /dev/null
@@ -0,0 +1,493 @@
+/*
+ * This file is
+ *   Copyright (C) 1998 Ross Anderson, Eli Biham, Lars Knudsen
+ *
+ * For more information see http://www.cl.cam.ac.uk/users/rja14/serpent.html
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2, or (at your option)
+ *  any later version.
+ *  
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *  
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software Foundation,
+ *  Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. 
+ *
+ */
+
+
+/* S0:   3  8 15  1 10  6  5 11 14 13  4  2  7  0  9 12 */
+
+/* depth = 5,7,4,2, Total gates=18 */
+#define RND00(a,b,c,d,w,x,y,z) \
+       { register unsigned long t02, t03, t05, t06, t07, t08, t09, t11, t12, t13, t14, t15, t17, t01;\
+       t01 = b   ^ c  ; \
+       t02 = a   | d  ; \
+       t03 = a   ^ b  ; \
+       z   = t02 ^ t01; \
+       t05 = c   | z  ; \
+       t06 = a   ^ d  ; \
+       t07 = b   | c  ; \
+       t08 = d   & t05; \
+       t09 = t03 & t07; \
+       y   = t09 ^ t08; \
+       t11 = t09 & y  ; \
+       t12 = c   ^ d  ; \
+       t13 = t07 ^ t11; \
+       t14 = b   & t06; \
+       t15 = t06 ^ t13; \
+       w   =     ~ t15; \
+       t17 = w   ^ t14; \
+       x   = t12 ^ t17; }
+
+/* InvS0:  13  3 11  0 10  6  5 12  1 14  4  7 15  9  8  2 */
+
+/* depth = 8,4,3,6, Total gates=19 */
+#define InvRND00(a,b,c,d,w,x,y,z) \
+       { register unsigned long t02, t03, t04, t05, t06, t08, t09, t10, t12, t13, t14, t15, t17, t18, t01;\
+       t01 = c   ^ d  ; \
+       t02 = a   | b  ; \
+       t03 = b   | c  ; \
+       t04 = c   & t01; \
+       t05 = t02 ^ t01; \
+       t06 = a   | t04; \
+       y   =     ~ t05; \
+       t08 = b   ^ d  ; \
+       t09 = t03 & t08; \
+       t10 = d   | y  ; \
+       x   = t09 ^ t06; \
+       t12 = a   | t05; \
+       t13 = x   ^ t12; \
+       t14 = t03 ^ t10; \
+       t15 = a   ^ c  ; \
+       z   = t14 ^ t13; \
+       t17 = t05 & t13; \
+       t18 = t14 | t17; \
+       w   = t15 ^ t18; }
+
+/* S1:  15 12  2  7  9  0  5 10  1 11 14  8  6 13  3  4 */
+
+/* depth = 10,7,3,5, Total gates=18 */
+#define RND01(a,b,c,d,w,x,y,z) \
+       { register unsigned long t02, t03, t04, t05, t06, t07, t08, t10, t11, t12, t13, t16, t17, t01;\
+       t01 = a   | d  ; \
+       t02 = c   ^ d  ; \
+       t03 =     ~ b  ; \
+       t04 = a   ^ c  ; \
+       t05 = a   | t03; \
+       t06 = d   & t04; \
+       t07 = t01 & t02; \
+       t08 = b   | t06; \
+       y   = t02 ^ t05; \
+       t10 = t07 ^ t08; \
+       t11 = t01 ^ t10; \
+       t12 = y   ^ t11; \
+       t13 = b   & d  ; \
+       z   =     ~ t10; \
+       x   = t13 ^ t12; \
+       t16 = t10 | x  ; \
+       t17 = t05 & t16; \
+       w   = c   ^ t17; }
+
+/* InvS1:   5  8  2 14 15  6 12  3 11  4  7  9  1 13 10  0 */
+
+/* depth = 7,4,5,3, Total gates=18 */
+#define InvRND01(a,b,c,d,w,x,y,z) \
+       { register unsigned long t02, t03, t04, t05, t06, t07, t08, t09, t10, t11, t14, t15, t17, t01;\
+       t01 = a   ^ b  ; \
+       t02 = b   | d  ; \
+       t03 = a   & c  ; \
+       t04 = c   ^ t02; \
+       t05 = a   | t04; \
+       t06 = t01 & t05; \
+       t07 = d   | t03; \
+       t08 = b   ^ t06; \
+       t09 = t07 ^ t06; \
+       t10 = t04 | t03; \
+       t11 = d   & t08; \
+       y   =     ~ t09; \
+       x   = t10 ^ t11; \
+       t14 = a   | y  ; \
+       t15 = t06 ^ x  ; \
+       z   = t01 ^ t04; \
+       t17 = c   ^ t15; \
+       w   = t14 ^ t17; }
+
+/* S2:   8  6  7  9  3 12 10 15 13  1 14  4  0 11  5  2 */
+
+/* depth = 3,8,11,7, Total gates=16 */
+#define RND02(a,b,c,d,w,x,y,z) \
+       { register unsigned long t02, t03, t05, t06, t07, t08, t09, t10, t12, t13, t14, t01;\
+       t01 = a   | c  ; \
+       t02 = a   ^ b  ; \
+       t03 = d   ^ t01; \
+       w   = t02 ^ t03; \
+       t05 = c   ^ w  ; \
+       t06 = b   ^ t05; \
+       t07 = b   | t05; \
+       t08 = t01 & t06; \
+       t09 = t03 ^ t07; \
+       t10 = t02 | t09; \
+       x   = t10 ^ t08; \
+       t12 = a   | d  ; \
+       t13 = t09 ^ x  ; \
+       t14 = b   ^ t13; \
+       z   =     ~ t09; \
+       y   = t12 ^ t14; }
+
+/* InvS2:  12  9 15  4 11 14  1  2  0  3  6 13  5  8 10  7 */
+
+/* depth = 3,6,8,3, Total gates=18 */
+#define InvRND02(a,b,c,d,w,x,y,z) \
+       { register unsigned long t02, t03, t04, t06, t07, t08, t09, t10, t11, t12, t15, t16, t17, t01;\
+       t01 = a   ^ d  ; \
+       t02 = c   ^ d  ; \
+       t03 = a   & c  ; \
+       t04 = b   | t02; \
+       w   = t01 ^ t04; \
+       t06 = a   | c  ; \
+       t07 = d   | w  ; \
+       t08 =     ~ d  ; \
+       t09 = b   & t06; \
+       t10 = t08 | t03; \
+       t11 = b   & t07; \
+       t12 = t06 & t02; \
+       z   = t09 ^ t10; \
+       x   = t12 ^ t11; \
+       t15 = c   & z  ; \
+       t16 = w   ^ x  ; \
+       t17 = t10 ^ t15; \
+       y   = t16 ^ t17; }
+
+/* S3:   0 15 11  8 12  9  6  3 13  1  2  4 10  7  5 14 */
+
+/* depth = 8,3,5,5, Total gates=18 */
+#define RND03(a,b,c,d,w,x,y,z) \
+       { register unsigned long t02, t03, t04, t05, t06, t07, t08, t09, t10, t11, t13, t14, t15, t01;\
+       t01 = a   ^ c  ; \
+       t02 = a   | d  ; \
+       t03 = a   & d  ; \
+       t04 = t01 & t02; \
+       t05 = b   | t03; \
+       t06 = a   & b  ; \
+       t07 = d   ^ t04; \
+       t08 = c   | t06; \
+       t09 = b   ^ t07; \
+       t10 = d   & t05; \
+       t11 = t02 ^ t10; \
+       z   = t08 ^ t09; \
+       t13 = d   | z  ; \
+       t14 = a   | t07; \
+       t15 = b   & t13; \
+       y   = t08 ^ t11; \
+       w   = t14 ^ t15; \
+       x   = t05 ^ t04; }
+
+/* InvS3:   0  9 10  7 11 14  6 13  3  5 12  2  4  8 15  1 */
+
+/* depth = 3,6,4,4, Total gates=17 */
+#define InvRND03(a,b,c,d,w,x,y,z) \
+       { register unsigned long t02, t03, t04, t05, t06, t07, t09, t11, t12, t13, t14, t16, t01;\
+       t01 = c   | d  ; \
+       t02 = a   | d  ; \
+       t03 = c   ^ t02; \
+       t04 = b   ^ t02; \
+       t05 = a   ^ d  ; \
+       t06 = t04 & t03; \
+       t07 = b   & t01; \
+       y   = t05 ^ t06; \
+       t09 = a   ^ t03; \
+       w   = t07 ^ t03; \
+       t11 = w   | t05; \
+       t12 = t09 & t11; \
+       t13 = a   & y  ; \
+       t14 = t01 ^ t05; \
+       x   = b   ^ t12; \
+       t16 = b   | t13; \
+       z   = t14 ^ t16; }
+
+/* S4:   1 15  8  3 12  0 11  6  2  5  4 10  9 14  7 13 */
+
+/* depth = 6,7,5,3, Total gates=19 */
+#define RND04(a,b,c,d,w,x,y,z) \
+       { register unsigned long t02, t03, t04, t05, t06, t08, t09, t10, t11, t12, t13, t14, t15, t16, t01;\
+       t01 = a   | b  ; \
+       t02 = b   | c  ; \
+       t03 = a   ^ t02; \
+       t04 = b   ^ d  ; \
+       t05 = d   | t03; \
+       t06 = d   & t01; \
+       z   = t03 ^ t06; \
+       t08 = z   & t04; \
+       t09 = t04 & t05; \
+       t10 = c   ^ t06; \
+       t11 = b   & c  ; \
+       t12 = t04 ^ t08; \
+       t13 = t11 | t03; \
+       t14 = t10 ^ t09; \
+       t15 = a   & t05; \
+       t16 = t11 | t12; \
+       y   = t13 ^ t08; \
+       x   = t15 ^ t16; \
+       w   =     ~ t14; }
+
+/* InvS4:   5  0  8  3 10  9  7 14  2 12 11  6  4 15 13  1 */
+
+/* depth = 6,4,7,3, Total gates=17 */
+#define InvRND04(a,b,c,d,w,x,y,z) \
+       { register unsigned long t02, t03, t04, t05, t06, t07, t09, t10, t11, t12, t13, t15, t01;\
+       t01 = b   | d  ; \
+       t02 = c   | d  ; \
+       t03 = a   & t01; \
+       t04 = b   ^ t02; \
+       t05 = c   ^ d  ; \
+       t06 =     ~ t03; \
+       t07 = a   & t04; \
+       x   = t05 ^ t07; \
+       t09 = x   | t06; \
+       t10 = a   ^ t07; \
+       t11 = t01 ^ t09; \
+       t12 = d   ^ t04; \
+       t13 = c   | t10; \
+       z   = t03 ^ t12; \
+       t15 = a   ^ t04; \
+       y   = t11 ^ t13; \
+       w   = t15 ^ t09; }
+
+/* S5:  15  5  2 11  4 10  9 12  0  3 14  8 13  6  7  1 */
+
+/* depth = 4,6,8,6, Total gates=17 */
+#define RND05(a,b,c,d,w,x,y,z) \
+       { register unsigned long t02, t03, t04, t05, t07, t08, t09, t10, t11, t12, t13, t14, t01;\
+       t01 = b   ^ d  ; \
+       t02 = b   | d  ; \
+       t03 = a   & t01; \
+       t04 = c   ^ t02; \
+       t05 = t03 ^ t04; \
+       w   =     ~ t05; \
+       t07 = a   ^ t01; \
+       t08 = d   | w  ; \
+       t09 = b   | t05; \
+       t10 = d   ^ t08; \
+       t11 = b   | t07; \
+       t12 = t03 | w  ; \
+       t13 = t07 | t10; \
+       t14 = t01 ^ t11; \
+       y   = t09 ^ t13; \
+       x   = t07 ^ t08; \
+       z   = t12 ^ t14; }
+
+/* InvS5:   8 15  2  9  4  1 13 14 11  6  5  3  7 12 10  0 */
+
+/* depth = 4,6,9,7, Total gates=17 */
+#define InvRND05(a,b,c,d,w,x,y,z) \
+       { register unsigned long t02, t03, t04, t05, t07, t08, t09, t10, t12, t13, t15, t16, t01;\
+       t01 = a   & d  ; \
+       t02 = c   ^ t01; \
+       t03 = a   ^ d  ; \
+       t04 = b   & t02; \
+       t05 = a   & c  ; \
+       w   = t03 ^ t04; \
+       t07 = a   & w  ; \
+       t08 = t01 ^ w  ; \
+       t09 = b   | t05; \
+       t10 =     ~ b  ; \
+       x   = t08 ^ t09; \
+       t12 = t10 | t07; \
+       t13 = w   | x  ; \
+       z   = t02 ^ t12; \
+       t15 = t02 ^ t13; \
+       t16 = b   ^ d  ; \
+       y   = t16 ^ t15; }
+
+/* S6:   7  2 12  5  8  4  6 11 14  9  1 15 13  3 10  0 */
+
+/* depth = 8,3,6,3, Total gates=19 */
+#define RND06(a,b,c,d,w,x,y,z) \
+       { register unsigned long t02, t03, t04, t05, t07, t08, t09, t10, t11, t12, t13, t15, t17, t18, t01;\
+       t01 = a   & d  ; \
+       t02 = b   ^ c  ; \
+       t03 = a   ^ d  ; \
+       t04 = t01 ^ t02; \
+       t05 = b   | c  ; \
+       x   =     ~ t04; \
+       t07 = t03 & t05; \
+       t08 = b   & x  ; \
+       t09 = a   | c  ; \
+       t10 = t07 ^ t08; \
+       t11 = b   | d  ; \
+       t12 = c   ^ t11; \
+       t13 = t09 ^ t10; \
+       y   =     ~ t13; \
+       t15 = x   & t03; \
+       z   = t12 ^ t07; \
+       t17 = a   ^ b  ; \
+       t18 = y   ^ t15; \
+       w   = t17 ^ t18; }
+
+/* InvS6:  15 10  1 13  5  3  6  0  4  9 14  7  2 12  8 11 */
+
+/* depth = 5,3,8,6, Total gates=19 */
+#define InvRND06(a,b,c,d,w,x,y,z) \
+       { register unsigned long t02, t03, t04, t05, t06, t07, t08, t09, t12, t13, t14, t15, t16, t17, t01;\
+       t01 = a   ^ c  ; \
+       t02 =     ~ c  ; \
+       t03 = b   & t01; \
+       t04 = b   | t02; \
+       t05 = d   | t03; \
+       t06 = b   ^ d  ; \
+       t07 = a   & t04; \
+       t08 = a   | t02; \
+       t09 = t07 ^ t05; \
+       x   = t06 ^ t08; \
+       w   =     ~ t09; \
+       t12 = b   & w  ; \
+       t13 = t01 & t05; \
+       t14 = t01 ^ t12; \
+       t15 = t07 ^ t13; \
+       t16 = d   | t02; \
+       t17 = a   ^ x  ; \
+       z   = t17 ^ t15; \
+       y   = t16 ^ t14; }
+
+/* S7:   1 13 15  0 14  8  2 11  7  4 12 10  9  3  5  6 */
+
+/* depth = 10,7,10,4, Total gates=19 */
+#define RND07(a,b,c,d,w,x,y,z) \
+       { register unsigned long t02, t03, t04, t05, t06, t08, t09, t10, t11, t13, t14, t15, t16, t17, t01;\
+       t01 = a   & c  ; \
+       t02 =     ~ d  ; \
+       t03 = a   & t02; \
+       t04 = b   | t01; \
+       t05 = a   & b  ; \
+       t06 = c   ^ t04; \
+       z   = t03 ^ t06; \
+       t08 = c   | z  ; \
+       t09 = d   | t05; \
+       t10 = a   ^ t08; \
+       t11 = t04 & z  ; \
+       x   = t09 ^ t10; \
+       t13 = b   ^ x  ; \
+       t14 = t01 ^ x  ; \
+       t15 = c   ^ t05; \
+       t16 = t11 | t13; \
+       t17 = t02 | t14; \
+       w   = t15 ^ t17; \
+       y   = a   ^ t16; }
+
+/* InvS7:   3  0  6 13  9 14 15  8  5 12 11  7 10  1  4  2 */
+
+/* depth = 9,7,3,3, Total gates=18 */
+#define InvRND07(a,b,c,d,w,x,y,z) \
+       { register unsigned long t02, t03, t04, t06, t07, t08, t09, t10, t11, t13, t14, t15, t16, t01;\
+       t01 = a   & b  ; \
+       t02 = a   | b  ; \
+       t03 = c   | t01; \
+       t04 = d   & t02; \
+       z   = t03 ^ t04; \
+       t06 = b   ^ t04; \
+       t07 = d   ^ z  ; \
+       t08 =     ~ t07; \
+       t09 = t06 | t08; \
+       t10 = b   ^ d  ; \
+       t11 = a   | d  ; \
+       x   = a   ^ t09; \
+       t13 = c   ^ t06; \
+       t14 = c   & t11; \
+       t15 = d   | x  ; \
+       t16 = t01 | t10; \
+       w   = t13 ^ t15; \
+       y   = t14 ^ t16; }
+
+#define RND08(a,b,c,d,e,f,g,h) RND00(a,b,c,d,e,f,g,h)
+#define RND09(a,b,c,d,e,f,g,h) RND01(a,b,c,d,e,f,g,h)
+#define RND10(a,b,c,d,e,f,g,h) RND02(a,b,c,d,e,f,g,h)
+#define RND11(a,b,c,d,e,f,g,h) RND03(a,b,c,d,e,f,g,h)
+#define RND12(a,b,c,d,e,f,g,h) RND04(a,b,c,d,e,f,g,h)
+#define RND13(a,b,c,d,e,f,g,h) RND05(a,b,c,d,e,f,g,h)
+#define RND14(a,b,c,d,e,f,g,h) RND06(a,b,c,d,e,f,g,h)
+#define RND15(a,b,c,d,e,f,g,h) RND07(a,b,c,d,e,f,g,h)
+#define RND16(a,b,c,d,e,f,g,h) RND00(a,b,c,d,e,f,g,h)
+#define RND17(a,b,c,d,e,f,g,h) RND01(a,b,c,d,e,f,g,h)
+#define RND18(a,b,c,d,e,f,g,h) RND02(a,b,c,d,e,f,g,h)
+#define RND19(a,b,c,d,e,f,g,h) RND03(a,b,c,d,e,f,g,h)
+#define RND20(a,b,c,d,e,f,g,h) RND04(a,b,c,d,e,f,g,h)
+#define RND21(a,b,c,d,e,f,g,h) RND05(a,b,c,d,e,f,g,h)
+#define RND22(a,b,c,d,e,f,g,h) RND06(a,b,c,d,e,f,g,h)
+#define RND23(a,b,c,d,e,f,g,h) RND07(a,b,c,d,e,f,g,h)
+#define RND24(a,b,c,d,e,f,g,h) RND00(a,b,c,d,e,f,g,h)
+#define RND25(a,b,c,d,e,f,g,h) RND01(a,b,c,d,e,f,g,h)
+#define RND26(a,b,c,d,e,f,g,h) RND02(a,b,c,d,e,f,g,h)
+#define RND27(a,b,c,d,e,f,g,h) RND03(a,b,c,d,e,f,g,h)
+#define RND28(a,b,c,d,e,f,g,h) RND04(a,b,c,d,e,f,g,h)
+#define RND29(a,b,c,d,e,f,g,h) RND05(a,b,c,d,e,f,g,h)
+#define RND30(a,b,c,d,e,f,g,h) RND06(a,b,c,d,e,f,g,h)
+#define RND31(a,b,c,d,e,f,g,h) RND07(a,b,c,d,e,f,g,h)
+
+#define InvRND08(a,b,c,d,e,f,g,h) InvRND00(a,b,c,d,e,f,g,h)
+#define InvRND09(a,b,c,d,e,f,g,h) InvRND01(a,b,c,d,e,f,g,h)
+#define InvRND10(a,b,c,d,e,f,g,h) InvRND02(a,b,c,d,e,f,g,h)
+#define InvRND11(a,b,c,d,e,f,g,h) InvRND03(a,b,c,d,e,f,g,h)
+#define InvRND12(a,b,c,d,e,f,g,h) InvRND04(a,b,c,d,e,f,g,h)
+#define InvRND13(a,b,c,d,e,f,g,h) InvRND05(a,b,c,d,e,f,g,h)
+#define InvRND14(a,b,c,d,e,f,g,h) InvRND06(a,b,c,d,e,f,g,h)
+#define InvRND15(a,b,c,d,e,f,g,h) InvRND07(a,b,c,d,e,f,g,h)
+#define InvRND16(a,b,c,d,e,f,g,h) InvRND00(a,b,c,d,e,f,g,h)
+#define InvRND17(a,b,c,d,e,f,g,h) InvRND01(a,b,c,d,e,f,g,h)
+#define InvRND18(a,b,c,d,e,f,g,h) InvRND02(a,b,c,d,e,f,g,h)
+#define InvRND19(a,b,c,d,e,f,g,h) InvRND03(a,b,c,d,e,f,g,h)
+#define InvRND20(a,b,c,d,e,f,g,h) InvRND04(a,b,c,d,e,f,g,h)
+#define InvRND21(a,b,c,d,e,f,g,h) InvRND05(a,b,c,d,e,f,g,h)
+#define InvRND22(a,b,c,d,e,f,g,h) InvRND06(a,b,c,d,e,f,g,h)
+#define InvRND23(a,b,c,d,e,f,g,h) InvRND07(a,b,c,d,e,f,g,h)
+#define InvRND24(a,b,c,d,e,f,g,h) InvRND00(a,b,c,d,e,f,g,h)
+#define InvRND25(a,b,c,d,e,f,g,h) InvRND01(a,b,c,d,e,f,g,h)
+#define InvRND26(a,b,c,d,e,f,g,h) InvRND02(a,b,c,d,e,f,g,h)
+#define InvRND27(a,b,c,d,e,f,g,h) InvRND03(a,b,c,d,e,f,g,h)
+#define InvRND28(a,b,c,d,e,f,g,h) InvRND04(a,b,c,d,e,f,g,h)
+#define InvRND29(a,b,c,d,e,f,g,h) InvRND05(a,b,c,d,e,f,g,h)
+#define InvRND30(a,b,c,d,e,f,g,h) InvRND06(a,b,c,d,e,f,g,h)
+#define InvRND31(a,b,c,d,e,f,g,h) InvRND07(a,b,c,d,e,f,g,h)
+
+/* Linear transformations and key mixing: */
+
+#define ROL(x,n) ((((unsigned long)(x))<<(n))| \
+                  (((unsigned long)(x))>>(32-(n))))
+#define ROR(x,n) ((((unsigned long)(x))<<(32-(n)))| \
+                  (((unsigned long)(x))>>(n)))
+
+#define transform(x0, x1, x2, x3, y0, y1, y2, y3) \
+      y0 = ROL(x0, 13); \
+      y2 = ROL(x2, 3); \
+      y1 = x1 ^ y0 ^ y2; \
+      y3 = x3 ^ y2 ^ ((unsigned long)y0)<<3; \
+      y1 = ROL(y1, 1); \
+      y3 = ROL(y3, 7); \
+      y0 = y0 ^ y1 ^ y3; \
+      y2 = y2 ^ y3 ^ ((unsigned long)y1<<7); \
+      y0 = ROL(y0, 5); \
+      y2 = ROL(y2, 22)
+
+#define inv_transform(x0, x1, x2, x3, y0, y1, y2, y3) \
+      y2 = ROR(x2, 22);\
+      y0 = ROR(x0, 5); \
+      y2 = y2 ^ x3 ^ ((unsigned long)x1<<7); \
+      y0 = y0 ^ x1 ^ x3; \
+      y3 = ROR(x3, 7); \
+      y1 = ROR(x1, 1); \
+      y3 = y3 ^ y2 ^ ((unsigned long)y0)<<3; \
+      y1 = y1 ^ y0 ^ y2; \
+      y2 = ROR(y2, 3); \
+      y0 = ROR(y0, 13)
+
+#define keying(x0, x1, x2, x3, subkey) \
+                         x0^=subkey[0];x1^=subkey[1]; \
+                         x2^=subkey[2];x3^=subkey[3]
+
+/* PHI: Constant used in the key schedule */
+#define PHI 0x9e3779b9L
diff --git a/site.c b/site.c
new file mode 100644 (file)
index 0000000..c63ca8b
--- /dev/null
+++ b/site.c
@@ -0,0 +1,1161 @@
+/* site.c - manage communication with a remote network site */
+
+#include <stdio.h>
+#include <sys/mman.h>
+
+#include "secnet.h"
+#include "util.h"
+
+#define SETUP_BUFFER_LEN 2048
+
+#define DEFAULT_KEY_LIFETIME 15000
+#define DEFAULT_SETUP_RETRIES 5
+#define DEFAULT_SETUP_TIMEOUT 500
+#define DEFAULT_WAIT_TIME 10000
+
+/* Each site can be in one of several possible states. */
+
+/* States:
+   SITE_STOP         - nothing is allowed to happen; tunnel is down;
+                       all session keys have been erased
+     -> SITE_RUN upon external instruction
+   SITE_RUN          - site up, maybe with valid key
+     -> SITE_RESOLVE upon outgoing packet and no valid key
+         we start name resolution for the other end of the tunnel
+     -> SITE_SENTMSG2 upon valid incoming message 1 and suitable time
+         we send an appropriate message 2
+   SITE_RESOLVE      - waiting for name resolution
+     -> SITE_SENTMSG1 upon successful resolution
+         we send an appropriate message 1
+     -> SITE_SENTMSG2 upon valid incoming message 1 (then abort resolution)
+         we abort resolution and 
+     -> SITE_WAIT on timeout or resolution failure
+   SITE_SENTMSG1
+     -> SITE_SENTMSG2 upon valid incoming message 1 from higher priority end
+     -> SITE_SENTMSG3 upon valid incoming message 2
+     -> SITE_WAIT on timeout
+   SITE_SENTMSG2
+     -> SITE_SENTMSG4 upon valid incoming message 3
+     -> SITE_WAIT on timeout
+   SITE_SENTMSG3
+     -> SITE_SENTMSG5 upon valid incoming message 4
+     -> SITE_WAIT on timeout
+   SITE_SENTMSG4
+     -> SITE_RUN upon valid incoming message 5
+     -> SITE_WAIT on timeout
+   SITE_SENTMSG5
+     -> SITE_RUN upon valid incoming message 6
+     -> SITE_WAIT on timeout
+   SITE_WAIT         - failed to establish key; do nothing for a while
+     -> SITE_RUN on timeout
+   */
+
+#define SITE_STOP     0
+#define SITE_RUN      1
+#define SITE_RESOLVE  2
+#define SITE_SENTMSG1 3
+#define SITE_SENTMSG2 4
+#define SITE_SENTMSG3 5
+#define SITE_SENTMSG4 6
+#define SITE_SENTMSG5 7
+#define SITE_WAIT     8
+
+static string_t state_name(uint32_t state)
+{
+    switch (state) {
+    case 0: return "SITE_STOP";
+    case 1: return "SITE_RUN";
+    case 2: return "SITE_RESOLVE";
+    case 3: return "SITE_SENTMSG1";
+    case 4: return "SITE_SENTMSG2";
+    case 5: return "SITE_SENTMSG3";
+    case 6: return "SITE_SENTMSG4";
+    case 7: return "SITE_SENTMSG5";
+    case 8: return "SITE_WAIT";
+    default: return "*bad state*";
+    }
+}
+
+#define LABEL_MSG0 0x00020200
+#define LABEL_MSG1 0x01010101
+#define LABEL_MSG2 0x02020202
+#define LABEL_MSG3 0x03030303
+#define LABEL_MSG4 0x04040404
+#define LABEL_MSG5 0x05050505
+#define LABEL_MSG6 0x06060606
+#define LABEL_MSG7 0x07070707
+#define LABEL_MSG8 0x08080808
+#define LABEL_MSG9 0x09090909
+
+#define NONCELEN 8
+
+#define LOG_UNEXPECTED    0x00000001
+#define LOG_SETUP_INIT    0x00000002
+#define LOG_SETUP_TIMEOUT 0x00000004
+#define LOG_ACTIVATE_KEY  0x00000008
+#define LOG_TIMEOUT_KEY   0x00000010
+#define LOG_SECURITY      0x00000020
+#define LOG_STATE         0x00000040
+#define LOG_DROP          0x00000080
+#define LOG_DUMP          0x00000100
+#define LOG_ERROR         0x00000400
+
+struct site {
+    closure_t cl;
+    struct site_if ops;
+/* configuration information */
+    string_t localname;
+    string_t remotename;
+    string_t address; /* DNS name for bootstrapping, optional */
+    int remoteport;
+    struct netlink_if *netlink;
+    struct comm_if *comm;
+    struct resolver_if *resolver;
+    struct log_if *log;
+    struct random_if *random;
+    struct rsaprivkey_if *privkey;
+    struct subnet_list remotenets;
+    struct rsapubkey_if *pubkey;
+    struct transform_if *transform;
+    struct dh_if *dh;
+    struct hash_if *hash;
+    void *netlink_cid;
+
+    uint32_t setup_retries; /* How many times to send setup packets */
+    uint32_t setup_timeout; /* Initial timeout for setup packets */
+    uint32_t wait_timeout; /* How long to wait if setup unsuccessful */
+    uint32_t key_lifetime; /* How long a key lasts once set up */
+
+    uint8_t *setupsig; /* Expected signature of incoming MSG1 packets */
+    uint32_t setupsiglen; /* Allows us to discard packets quickly if
+                            they are not for us */
+    bool_t setup_priority; /* Do we have precedence if both sites emit
+                             message 1 simultaneously? */
+    uint32_t log_events;
+
+/* runtime information */
+    uint32_t state;
+    uint64_t now; /* Most recently seen time */
+
+    uint32_t remote_session_id;
+    struct transform_inst_if *current_transform;
+    bool_t current_valid;
+    uint64_t current_key_timeout; /* End of life of current key */
+    struct sockaddr_in peer; /* Current address of peer */
+    bool_t peer_valid; /* Peer address becomes invalid when key times out,
+                         but only if we have a DNS name for our peer */
+
+    uint32_t setup_session_id;
+    struct sockaddr_in setup_peer;
+    uint8_t localN[NONCELEN]; /* Nonces for key exchange */
+    uint8_t remoteN[NONCELEN];
+    struct buffer_if buffer; /* Current outgoing key exchange packet */
+    uint32_t retries; /* Number of retries remaining */
+    uint64_t timeout; /* Timeout for current state */
+    uint8_t *dhsecret;
+    uint8_t *sharedsecret;
+
+    struct transform_inst_if *new_transform; /* For key setup/verify */
+};
+
+static void slog(struct site *st, uint32_t event, string_t msg, ...)
+{
+    va_list ap;
+    uint8_t buf[240];
+
+    va_start(ap,msg);
+
+    if (event&st->log_events) {
+       vsnprintf(buf,240,msg,ap);
+       st->log->log(st->log->st,0,"%s<->%s: %s",st->localname,st->remotename,
+                    buf);
+    }
+    va_end(ap);
+}
+
+static void enter_state_run(struct site *st);
+static bool_t enter_state_resolve(struct site *st);
+static bool_t enter_state_sentmsg1(struct site *st);
+static bool_t enter_state_sentmsg2(struct site *st);
+static bool_t enter_state_sentmsg3(struct site *st);
+static bool_t enter_state_sentmsg4(struct site *st);
+static bool_t enter_state_sentmsg5(struct site *st);
+static void enter_state_wait(struct site *st);
+
+#define CHECK_AVAIL(b,l) do { if ((b)->size<(l)) return False; } while(0)
+#define CHECK_EMPTY(b) do { if ((b)->size!=0) return False; } while(0)
+#define CHECK_TYPE(b,t) do { uint32_t type; \
+    CHECK_AVAIL((b),4); \
+    type=*(uint32_t *)buf_unprepend((b),4); \
+    if (type!=(t)) return False; } while(0)
+
+struct msg {
+    uint8_t *hashstart;
+    uint32_t dest;
+    uint32_t source;
+    uint32_t remlen;
+    uint8_t *remote;
+    uint32_t loclen;
+    uint8_t *local;
+    uint8_t *nR;
+    uint8_t *nL;
+    uint32_t pklen;
+    uint8_t *pk;
+    uint32_t hashlen;
+    uint32_t siglen;
+    uint8_t *sig;
+};
+
+/* Build any of msg1 to msg4. msg5 and msg6 are built from the inside out
+   using a transform. */
+static bool_t generate_msg(struct site *st, uint32_t type, string_t what)
+{
+    void *hst;
+    uint8_t *hash=alloca(st->hash->len);
+    string_t dhpub, sig;
+
+    st->retries=st->setup_retries;
+    BUF_ALLOC(&st->buffer,what);
+    buffer_init(&st->buffer,0);
+    *(uint32_t *)buf_append(&st->buffer,4)=
+       (type==LABEL_MSG1?0:st->setup_session_id);
+    *(uint32_t *)buf_append(&st->buffer,4)=(uint32_t)st;
+    *(uint32_t *)buf_append(&st->buffer,4)=type;
+    buf_append_string(&st->buffer,st->localname);
+    buf_append_string(&st->buffer,st->remotename);
+    memcpy(buf_append(&st->buffer,NONCELEN),st->localN,NONCELEN);
+    if (type==LABEL_MSG1) return True;
+    memcpy(buf_append(&st->buffer,NONCELEN),st->remoteN,NONCELEN);
+    if (type==LABEL_MSG2) return True;
+    dhpub=st->dh->makepublic(st->dh->st,st->dhsecret,st->dh->len);
+    buf_append_string(&st->buffer,dhpub);
+    free(dhpub);
+    hst=st->hash->init();
+    st->hash->update(hst,st->buffer.start,st->buffer.size);
+    st->hash->final(hst,hash);
+    sig=st->privkey->sign(st->privkey->st,hash,st->hash->len);
+    buf_append_string(&st->buffer,sig);
+    free(sig);
+    return True;
+}
+
+static bool_t unpick_msg(struct site *st, uint32_t type,
+                        struct buffer_if *msg, struct msg *m)
+{
+    m->hashstart=msg->start;
+    CHECK_AVAIL(msg,4);
+    m->dest=*(uint32_t *)buf_unprepend(msg,4);
+    CHECK_AVAIL(msg,4);
+    m->source=*(uint32_t *)buf_unprepend(msg,4);
+    CHECK_TYPE(msg,type);
+    CHECK_AVAIL(msg,2);
+    m->remlen=ntohs(*(uint16_t *)buf_unprepend(msg,2));
+    CHECK_AVAIL(msg,m->remlen);
+    m->remote=buf_unprepend(msg,m->remlen);
+    CHECK_AVAIL(msg,2);
+    m->loclen=ntohs(*(uint16_t *)buf_unprepend(msg,2));
+    CHECK_AVAIL(msg,m->loclen);
+    m->local=buf_unprepend(msg,m->loclen);
+    CHECK_AVAIL(msg,NONCELEN);
+    m->nR=buf_unprepend(msg,NONCELEN);
+    if (type==LABEL_MSG1) {
+       CHECK_EMPTY(msg);
+       return True;
+    }
+    CHECK_AVAIL(msg,NONCELEN);
+    m->nL=buf_unprepend(msg,NONCELEN);
+    if (type==LABEL_MSG2) {
+       CHECK_EMPTY(msg);
+       return True;
+    }
+    CHECK_AVAIL(msg,2);
+    m->pklen=ntohs(*(uint16_t *)buf_unprepend(msg,2));
+    CHECK_AVAIL(msg,m->pklen);
+    m->pk=buf_unprepend(msg,m->pklen);
+    m->hashlen=msg->start-m->hashstart;
+    CHECK_AVAIL(msg,2);
+    m->siglen=ntohs(*(uint16_t *)buf_unprepend(msg,2));
+    CHECK_AVAIL(msg,m->siglen);
+    m->sig=buf_unprepend(msg,m->siglen);
+    CHECK_EMPTY(msg);
+    return True;
+}
+
+static bool_t generate_msg1(struct site *st)
+{
+    st->random->generate(st->random->st,NONCELEN,st->localN);
+    return generate_msg(st,LABEL_MSG1,"site:MSG1");
+}
+
+static bool_t process_msg1(struct site *st, struct buffer_if *msg1,
+                          struct sockaddr_in *src)
+{
+    struct msg m;
+
+    /* We've already determined we're in an appropriate state to
+       process an incoming MSG1, and that the MSG1 has correct values
+       of A and B. */
+
+    if (!unpick_msg(st,LABEL_MSG1,msg1,&m)) return False;
+
+    /* XXX save src as our peer address here? */
+    st->setup_peer=*src;
+
+    st->setup_session_id=m.source;
+    memcpy(st->remoteN,m.nR,NONCELEN);
+    return True;
+}
+
+static bool_t generate_msg2(struct site *st)
+{
+    st->random->generate(st->random->st,NONCELEN,st->localN);
+    return generate_msg(st,LABEL_MSG2,"site:MSG2");
+}
+
+static bool_t process_msg2(struct site *st, struct buffer_if *msg2,
+                          struct sockaddr_in *src)
+{
+    struct msg m;
+
+    if (!unpick_msg(st,LABEL_MSG2,msg2,&m)) return False;
+
+    /* Check that the site names and our nonce have been sent
+       back correctly, and then store our peer's nonce. */ 
+    if (memcmp(m.remote,st->remotename,strlen(st->remotename)!=0)) {
+       slog(st,LOG_SECURITY,"msg2: bad B (remote site name)");
+       return False;
+    }
+    if (memcmp(m.local,st->localname,strlen(st->localname)!=0)) {
+       slog(st,LOG_SECURITY,"msg2: bad A (local site name)");
+       return False;
+    }
+    if (memcmp(m.nL,st->localN,NONCELEN)!=0) {
+       slog(st,LOG_SECURITY,"msg2: bad nA (locally generated nonce)");
+       return False;
+    }
+    st->setup_session_id=m.source;
+    memcpy(st->remoteN,m.nR,NONCELEN);
+    return True;
+}
+
+static bool_t generate_msg3(struct site *st)
+{
+    /* Now we have our nonce and their nonce. Think of a secret key,
+       and create message number 3. */
+    st->random->generate(st->random->st,st->dh->len,st->dhsecret);
+    return generate_msg(st,LABEL_MSG3,"site:MSG3");
+}
+
+static bool_t process_msg3(struct site *st, struct buffer_if *msg3,
+                          struct sockaddr_in *src)
+{
+    struct msg m;
+    uint8_t *hash=alloca(st->hash->len);
+    void *hst;
+
+    if (!unpick_msg(st,LABEL_MSG3,msg3,&m)) return False;
+
+    /* Check that the site names and nonces have been sent back
+       correctly */
+    if (memcmp(m.remote,st->remotename,strlen(st->remotename)!=0)) {
+       slog(st,LOG_SECURITY,"msg3: bad A (remote site name)");
+       return False;
+    }
+    if (memcmp(m.local,st->localname,strlen(st->localname)!=0)) {
+       slog(st,LOG_SECURITY,"msg3: bad B (local site name)");
+       return False;
+    }
+    if (memcmp(m.nR,st->remoteN,NONCELEN)!=0) {
+       slog(st,LOG_SECURITY,"msg3: bad nA (remotely generated nonce)");
+       return False;
+    }
+    if (memcmp(m.nL,st->localN,NONCELEN)!=0) {
+       slog(st,LOG_SECURITY,"msg3: bad nB (locally generated nonce)");
+       return False;
+    }
+    
+    /* Check signature and store g^x mod m */
+    hst=st->hash->init();
+    st->hash->update(hst,m.hashstart,m.hashlen);
+    st->hash->final(hst,hash);
+    /* Terminate signature with a '0' - cheating, but should be ok */
+    m.sig[m.siglen]=0;
+    if (!st->pubkey->check(st->pubkey->st,hash,st->hash->len,m.sig)) {
+       slog(st,LOG_SECURITY,"msg3 signature failed check!");
+       return False;
+    }
+
+    /* Terminate their DH public key with a '0' */
+    m.pk[m.pklen]=0;
+    /* Invent our DH secret key */
+    st->random->generate(st->random->st,st->dh->len,st->dhsecret);
+
+    /* Generate the shared key */
+    st->dh->makeshared(st->dh->st,st->dhsecret,st->dh->len,m.pk,
+                      st->sharedsecret,st->transform->keylen);
+
+    /* Set up the transform */
+    st->new_transform->setkey(st->new_transform->st,st->sharedsecret,
+                             st->transform->keylen);
+
+    return True;
+}
+
+static bool_t generate_msg4(struct site *st)
+{
+    /* We have both nonces, their public key and our private key. Generate
+       our public key, sign it and send it to them. */
+    return generate_msg(st,LABEL_MSG4,"site:MSG4");
+}
+
+static bool_t process_msg4(struct site *st, struct buffer_if *msg4,
+                          struct sockaddr_in *src)
+{
+    struct msg m;
+    uint8_t *hash=alloca(st->hash->len);
+    void *hst;
+
+    if (!unpick_msg(st,LABEL_MSG4,msg4,&m)) return False;
+
+    /* Check that the site names and nonces have been sent back
+       correctly */
+    if (memcmp(m.remote,st->remotename,strlen(st->remotename)!=0)) {
+       slog(st,LOG_SECURITY,"msg4: bad B (remote site name)");
+       return False;
+    }
+    if (memcmp(m.local,st->localname,strlen(st->localname)!=0)) {
+       slog(st,LOG_SECURITY,"msg4: bad A (local site name)");
+       return False;
+    }
+    if (memcmp(m.nR,st->remoteN,NONCELEN)!=0) {
+       slog(st,LOG_SECURITY,"msg4: bad nB (remotely generated nonce)");
+       return False;
+    }
+    if (memcmp(m.nL,st->localN,NONCELEN)!=0) {
+       slog(st,LOG_SECURITY,"msg4: bad nA (locally generated nonce)");
+       return False;
+    }
+    
+    /* Check signature and store g^x mod m */
+    hst=st->hash->init();
+    st->hash->update(hst,m.hashstart,m.hashlen);
+    st->hash->final(hst,hash);
+    /* Terminate signature with a '0' - cheating, but should be ok */
+    m.sig[m.siglen]=0;
+    if (!st->pubkey->check(st->pubkey->st,hash,st->hash->len,m.sig)) {
+       slog(st,LOG_SECURITY,"msg4 signature failed check!");
+       return False;
+    }
+
+    /* Terminate their DH public key with a '0' */
+    m.pk[m.pklen]=0;
+    /* Generate the shared key */
+    st->dh->makeshared(st->dh->st,st->dhsecret,st->dh->len,m.pk,
+                      st->sharedsecret,st->transform->keylen);
+    /* Set up the transform */
+    st->new_transform->setkey(st->new_transform->st,st->sharedsecret,
+                             st->transform->keylen);
+
+    return True;
+}
+
+static bool_t generate_msg5(struct site *st)
+{
+    string_t transform_err;
+
+    BUF_ALLOC(&st->buffer,"site:MSG5");
+    /* We are going to add three words to the transformed message */
+    buffer_init(&st->buffer,st->transform->max_start_pad+(4*3));
+    *(uint32_t *)buf_append(&st->buffer,4)=LABEL_MSG5;
+    st->new_transform->forwards(st->new_transform->st,&st->buffer,
+                               &transform_err);
+    *(uint32_t *)buf_prepend(&st->buffer,4)=LABEL_MSG5;
+    *(uint32_t *)buf_prepend(&st->buffer,4)=(uint32_t)st;
+    *(uint32_t *)buf_prepend(&st->buffer,4)=st->setup_session_id;
+
+    st->retries=st->setup_retries;
+    return True;
+}
+
+struct msg0 {
+    uint32_t dest;
+    uint32_t source;
+    uint32_t type;
+};
+
+static bool_t unpick_msg0(struct site *st, struct buffer_if *msg0,
+                         struct msg0 *m)
+{
+    CHECK_AVAIL(msg0,4);
+    m->dest=*(uint32_t *)buf_unprepend(msg0,4);
+    CHECK_AVAIL(msg0,4);
+    m->source=*(uint32_t *)buf_unprepend(msg0,4);
+    CHECK_AVAIL(msg0,4);
+    m->type=*(uint32_t *)buf_unprepend(msg0,4);
+    return True;
+    /* Leaves transformed part of buffer untouched */
+}
+
+static bool_t process_msg5(struct site *st, struct buffer_if *msg5,
+                          struct sockaddr_in *src)
+{
+    struct msg0 m;
+    string_t transform_err;
+
+    if (!unpick_msg0(st,msg5,&m)) return False;
+
+    if (st->new_transform->reverse(st->new_transform->st,
+                                  msg5,&transform_err)) {
+       /* There's a problem */
+       slog(st,LOG_SECURITY,"process_msg5: transform: %s",transform_err);
+       return False;
+    }
+    /* Buffer should now contain untransformed PING packet data */
+    CHECK_AVAIL(msg5,4);
+    if ((*(uint32_t *)buf_unprepend(msg5,4))!=LABEL_MSG5) {
+       slog(st,LOG_SECURITY,"MSG5/PING packet contained invalid data");
+       return False;
+    }
+    CHECK_EMPTY(msg5);
+    return True;
+}
+
+static bool_t generate_msg6(struct site *st)
+{
+    string_t transform_err;
+
+    BUF_ALLOC(&st->buffer,"site:MSG6");
+    /* We are going to add three words to the transformed message */
+    buffer_init(&st->buffer,st->transform->max_start_pad+(4*3));
+    *(uint32_t *)buf_append(&st->buffer,4)=LABEL_MSG6;
+    st->new_transform->forwards(st->new_transform->st,&st->buffer,
+                               &transform_err);
+    *(uint32_t *)buf_prepend(&st->buffer,4)=LABEL_MSG6;
+    *(uint32_t *)buf_prepend(&st->buffer,4)=(uint32_t)st;
+    *(uint32_t *)buf_prepend(&st->buffer,4)=st->setup_session_id;
+
+    st->retries=1; /* Peer will retransmit MSG5 if necessary */
+    return True;
+}
+
+static bool_t process_msg6(struct site *st, struct buffer_if *msg6,
+                          struct sockaddr_in *src)
+{
+    struct msg0 m;
+    string_t transform_err;
+
+    if (!unpick_msg0(st,msg6,&m)) return False;
+
+    if (st->new_transform->reverse(st->new_transform->st,
+                                  msg6,&transform_err)) {
+       /* There's a problem */
+       slog(st,LOG_SECURITY,"process_msg6: transform: %s",transform_err);
+       return False;
+    }
+    /* Buffer should now contain untransformed PING packet data */
+    CHECK_AVAIL(msg6,4);
+    if ((*(uint32_t *)buf_unprepend(msg6,4))!=LABEL_MSG6) {
+       slog(st,LOG_SECURITY,"MSG6/PONG packet contained invalid data");
+       return False;
+    }
+    CHECK_EMPTY(msg6);
+    return True;
+}
+
+static bool_t process_msg0(struct site *st, struct buffer_if *msg0,
+                          struct sockaddr_in *src)
+{
+    struct msg0 m;
+    string_t transform_err;
+    uint32_t type;
+
+    if (!st->current_valid) {
+       slog(st,LOG_DROP,"incoming message but no current key -> dropping");
+       if (st->state==SITE_RUN) {
+           slog(st,LOG_SETUP_INIT|LOG_STATE,
+                "now initiating setup of new key");
+           return enter_state_resolve(st);
+       }
+       return False;
+    }
+
+    if (!unpick_msg0(st,msg0,&m)) return False;
+
+    if (st->current_transform->reverse(st->current_transform->st,
+                                      msg0,&transform_err)) {
+       /* There's a problem */
+       slog(st,LOG_SECURITY,"transform: %s",transform_err);
+       return False;
+    }
+    CHECK_AVAIL(msg0,4);
+    type=*(uint32_t *)buf_unprepend(msg0,4);
+    switch(type) {
+    case LABEL_MSG9:
+       /* Deliver to netlink layer */
+       st->netlink->deliver(st->netlink->st,st->netlink_cid,msg0);
+       return True;
+       break;
+    default:
+       slog(st,LOG_SECURITY,"incoming message of type %08x (unknown)",type);
+       break;
+    }
+    return False;
+}
+
+static void dump_packet(struct site *st, struct buffer_if *buf,
+                       struct sockaddr_in *addr, bool_t incoming)
+{
+    uint32_t dest=*(uint32_t *)buf->start;
+    uint32_t source=*(uint32_t *)(buf->start+4);
+    uint32_t msgtype=*(uint32_t *)(buf->start+8);
+
+    if (st->log_events & LOG_DUMP)
+       log(st->log,0,"(%s,%s): %s: %08x<-%08x: %08x:",
+           st->localname,st->remotename,incoming?"incoming":"outgoing",
+           dest,source,msgtype);
+}
+
+static uint32_t site_status(void *st)
+{
+    return 0;
+}
+
+static bool_t send_msg(struct site *st)
+{
+    if (st->retries>0) {
+       dump_packet(st,&st->buffer,&st->setup_peer,False);
+       st->comm->sendmsg(st->comm->st,&st->buffer,&st->setup_peer);
+       st->timeout=st->now+st->setup_timeout;
+       st->retries--;
+       return True;
+    } else {
+       slog(st,LOG_SETUP_TIMEOUT,"timed out sending key setup packet");
+       enter_state_wait(st);
+       return False;
+    }
+}
+
+static void site_resolve_callback(void *sst, struct in_addr *address)
+{
+    struct site *st=sst;
+
+    if (st->state!=SITE_RESOLVE) {
+       slog(st,LOG_UNEXPECTED,"site_resolve_callback called unexpectedly");
+       return;
+    }
+    if (address) {
+       memset(&st->setup_peer,0,sizeof(st->setup_peer));
+       st->setup_peer.sin_family=AF_INET;
+       st->setup_peer.sin_port=htons(st->remoteport);
+       st->setup_peer.sin_addr=*address;
+       enter_state_sentmsg1(st);
+    } else {
+       /* Resolution failed */
+       slog(st,LOG_ERROR,"resolution of %s failed",st->address);
+       enter_state_run(st);
+    }
+}
+
+static void activate_new_key(struct site *st)
+{
+    struct transform_inst_if *t;
+
+    t=st->current_transform;
+    st->current_transform=st->new_transform;
+    st->new_transform=t;
+
+    t->delkey(t->st);
+    st->state=SITE_RUN;
+    st->timeout=0;
+    st->current_valid=True;
+    st->current_key_timeout=st->now+st->key_lifetime;
+    st->peer=st->setup_peer;
+    st->peer_valid=True;
+    st->remote_session_id=st->setup_session_id;
+
+    slog(st,LOG_ACTIVATE_KEY,"new key activated");
+}
+
+static void state_assert(struct site *st, bool_t ok)
+{
+    if (!ok) fatal("state_assert\n");
+}
+
+static void enter_state_stop(struct site *st)
+{
+    st->state=SITE_STOP;
+    st->timeout=0;
+    st->current_transform->delkey(st->current_transform->st);
+    st->current_valid=False;
+    st->current_key_timeout=0;
+    
+    st->peer_valid=False;
+    
+    st->new_transform->delkey(st->new_transform->st);
+}
+
+static void enter_state_run(struct site *st)
+{
+    slog(st,LOG_STATE,"entering state RUN");
+    st->state=SITE_RUN;
+    st->timeout=0;
+    /* XXX get rid of key setup data */
+}
+
+static bool_t enter_state_resolve(struct site *st)
+{
+    state_assert(st,st->state==SITE_RUN);
+    slog(st,LOG_STATE,"entering state RESOLVE");
+    st->state=SITE_RESOLVE;
+    st->resolver->request(st->resolver->st,st->address,
+                         site_resolve_callback,st);
+    return True;
+}
+
+static bool_t enter_state_sentmsg1(struct site *st)
+{
+    state_assert(st,st->state==SITE_RUN || st->state==SITE_RESOLVE);
+    slog(st,LOG_STATE,"entering state SENTMSG1");
+    if (generate_msg1(st) && send_msg(st)) {
+       st->state=SITE_SENTMSG1;
+       return True;
+    }
+    slog(st,LOG_ERROR,"error entering state SENTMSG1");
+    st->buffer.free=False; /* Can't tell which it was, but enter_state_wait()
+                             will do a BUF_FREE() */
+    enter_state_wait(st);
+    return False;
+}
+
+static bool_t enter_state_sentmsg2(struct site *st)
+{
+    state_assert(st,st->state==SITE_RUN || st->state==SITE_RESOLVE ||
+                st->state==SITE_SENTMSG1 || st->state==SITE_WAIT);
+    slog(st,LOG_STATE,"entering state SENTMSG2");
+    if (generate_msg2(st) && send_msg(st)) {
+       st->state=SITE_SENTMSG2;
+       return True;
+    }
+    slog(st,LOG_ERROR,"error entering state SENTMSG2");
+    st->buffer.free=False;
+    enter_state_wait(st);
+    return False;
+}
+
+static bool_t enter_state_sentmsg3(struct site *st)
+{
+    state_assert(st,st->state==SITE_SENTMSG1);
+    slog(st,LOG_STATE,"entering state SENTMSG3");
+    BUF_FREE(&st->buffer); /* Free message 1 */
+    if (generate_msg3(st) && send_msg(st)) {
+       st->state=SITE_SENTMSG3;
+       return True;
+    }
+    slog(st,LOG_ERROR,"error entering state SENTMSG3");
+    st->buffer.free=False;
+    enter_state_wait(st);
+    return False;
+}
+
+static bool_t enter_state_sentmsg4(struct site *st)
+{
+    state_assert(st,st->state==SITE_SENTMSG2);
+    slog(st,LOG_STATE,"entering state SENTMSG4");
+    BUF_FREE(&st->buffer); /* Free message 2 */
+    if (generate_msg4(st) && send_msg(st)) {
+       st->state=SITE_SENTMSG4;
+       return True;
+    }
+    slog(st,LOG_ERROR,"error entering state SENTMSG4");
+    st->buffer.free=False;
+    enter_state_wait(st);
+    return False;
+}
+
+static bool_t enter_state_sentmsg5(struct site *st)
+{
+    state_assert(st,st->state==SITE_SENTMSG3);
+    slog(st,LOG_STATE,"entering state SENTMSG5");
+    BUF_FREE(&st->buffer); /* Free message 3 */
+
+    if (generate_msg5(st) && send_msg(st)) {
+       st->state=SITE_SENTMSG5;
+       return True;
+    }
+    slog(st,LOG_ERROR,"error entering state SENTMSG5");
+    st->buffer.free=False;
+    enter_state_wait(st);
+    
+    return False;
+}
+
+static bool_t send_msg6(struct site *st)
+{
+    state_assert(st,st->state==SITE_SENTMSG4);
+    slog(st,LOG_STATE,"entering state RUN after sending msg6");
+    BUF_FREE(&st->buffer); /* Free message 4 */
+    if (generate_msg6(st) && send_msg(st)) {
+       BUF_FREE(&st->buffer); /* Never reused */
+       st->timeout=0; /* Never retransmit */
+       activate_new_key(st);
+       return True;
+    }
+    slog(st,LOG_ERROR,"error entering state RUN after sending msg6");
+    st->buffer.free=False;
+    enter_state_wait(st);
+    return False;
+}
+
+/* We go into this state if our peer becomes uncommunicative. Similar to
+   the "stop" state, we forget all session keys for a while, before
+   re-entering the "run" state. */
+static void enter_state_wait(struct site *st)
+{
+    slog(st,LOG_STATE,"entering state WAIT");
+    st->timeout=st->now+st->wait_timeout;
+    st->state=SITE_WAIT;
+    st->peer_valid=False;
+    BUF_FREE(&st->buffer); /* will have had an outgoing packet in it */
+    /* XXX Erase keys etc. */
+}
+
+static int site_beforepoll(void *sst, struct pollfd *fds, int *nfds_io,
+                          int *timeout_io, const struct timeval *tv_now,
+                          uint64_t *now)
+{
+    struct site *st=sst;
+
+    *nfds_io=0; /* We don't use any file descriptors */
+    st->now=*now;
+
+    /* Work out when our next timeout is. The earlier of 'timeout' or
+       'current_key_timeout'. A stored value of '0' indicates no timeout
+       active. */
+    if (st->timeout && st->timeout-*now < *timeout_io) {
+       *timeout_io=st->timeout-*now;
+    }
+
+    if (st->current_key_timeout && st->current_key_timeout-*now < *timeout_io)
+       *timeout_io=st->current_key_timeout-*now;
+
+    return 0; /* success */
+}
+
+/* NB site_afterpoll will be called before site_beforepoll is ever called */
+static void site_afterpoll(void *sst, struct pollfd *fds, int nfds,
+                          const struct timeval *tv_now, uint64_t *now)
+{
+    struct site *st=sst;
+
+    st->now=*now;
+    if (st->timeout && *now>st->timeout) {
+       /* Do stuff */
+       st->timeout=0;
+       if (st->state>=SITE_SENTMSG1 && st->state<=SITE_SENTMSG5)
+           send_msg(st);
+       else if (st->state==SITE_WAIT) {
+           enter_state_run(st);
+       } else {
+           slog(st,LOG_ERROR,"site_afterpoll: unexpected timeout, state=%d",
+                st->state);
+       }
+    }
+    if (st->current_key_timeout && *now>st->current_key_timeout) {
+       slog(st,LOG_TIMEOUT_KEY,"maximum key life exceeded; session closed");
+       st->current_valid=False;
+       st->current_transform->delkey(st->current_transform->st);
+       st->current_key_timeout=0;
+    }
+}
+
+/* This function is called by the netlink device to deliver packets
+   intended for the remote network. The packet is in "raw" wire
+   format, but is guaranteed to be word-aligned. */
+static void site_outgoing(void *sst, void *cid, struct buffer_if *buf)
+{
+    struct site *st=sst;
+    string_t transform_err;
+    
+    if (st->state==SITE_STOP) {
+       BUF_FREE(buf);
+       return;
+    }
+
+    /* In all other states we consider delivering the packet if we have
+       a valid key and a valid address to send it to. */
+    if (st->current_valid && st->peer_valid) {
+       /* Transform it and send it */
+       *(uint32_t *)buf_prepend(buf,4)=LABEL_MSG9;
+       st->current_transform->forwards(st->current_transform->st,
+                                       buf, &transform_err);
+       *(uint32_t *)buf_prepend(buf,4)=LABEL_MSG0;
+       *(uint32_t *)buf_prepend(buf,4)=(uint32_t)st;
+       *(uint32_t *)buf_prepend(buf,4)=st->remote_session_id;
+       st->comm->sendmsg(st->comm->st,buf,&st->peer);
+       BUF_FREE(buf);
+       return;
+    }
+
+    if (st->state==SITE_RUN) {
+       BUF_FREE(buf); /* We throw the outgoing packet away */
+       slog(st,LOG_SETUP_INIT,"initiating key exchange");
+       enter_state_resolve(st);
+       return;
+    }
+
+    /* Otherwise we're in the middle of key setup or a wait - just
+       throw the outgoing packet away */
+    slog(st,LOG_DROP,"discarding outgoing packet");
+    BUF_FREE(buf);
+    return;
+}
+
+/* This function is called by the communication device to deliver
+   packets from our peers. */
+static bool_t site_incoming(void *sst, struct buffer_if *buf,
+                           struct sockaddr_in *source)
+{
+    struct site *st=sst;
+    uint32_t dest=*(uint32_t *)buf->start;
+
+    if (dest==0) {
+       if (buf->size<(st->setupsiglen+8+NONCELEN)) return False;
+       /* It could be for any site - it should have LABEL_MSG1 and
+          might have our name and our peer's name in it */
+       if (memcmp(buf->start+8,st->setupsig,st->setupsiglen)==0) {
+           dump_packet(st,buf,source,True);
+           /* It's addressed to us. Decide what to do about it. */
+           if (st->state==SITE_RUN || st->state==SITE_RESOLVE ||
+               st->state==SITE_WAIT) {
+               /* We should definitely process it */
+               if (process_msg1(st,buf,source)) {
+                   slog(st,LOG_SETUP_INIT,"key setup initiated by peer");
+                   enter_state_sentmsg2(st);
+               } else {
+                   slog(st,LOG_ERROR,"failed to process incoming msg1");
+               }
+               BUF_FREE(buf);
+               return True;
+           }
+           if (st->state==SITE_SENTMSG1) {
+               /* We've just sent a message 1! They may have crossed on
+                  the wire. If we have priority then we ignore the
+                  incoming one, otherwise we process it as usual. */
+               if (st->setup_priority) {
+                   BUF_FREE(buf);
+                   slog(st,LOG_DUMP,"crossed msg1s; we are higher "
+                        "priority => ignore incoming msg1");
+                   return True;
+               } else {
+                   slog(st,LOG_DUMP,"crossed msg1s; we are lower "
+                        "priority => use incoming msg1");
+                   if (process_msg1(st,buf,source)) {
+                       BUF_FREE(&st->buffer); /* Free our old message 1 */
+                       enter_state_sentmsg2(st);
+                   } else {
+                       slog(st,LOG_ERROR,"failed to process an incoming "
+                            "crossed msg1 (we have low priority)");
+                   }
+                   BUF_FREE(buf);
+                   return True;
+               }
+           }
+           /* The message 1 was received at an unexpected stage of the
+              key setup. XXX POLICY - what do we do? */
+           slog(st,LOG_UNEXPECTED,"unexpected incoming message 1");
+           BUF_FREE(buf);
+           return True;
+       }
+       return False; /* Not for us. */
+    }
+    if (dest==(uint32_t)st) {
+       uint32_t msgtype=*(uint32_t *)(buf->start+8);
+       /* Explicitly addressed to us */
+       if (msgtype!=LABEL_MSG0) dump_packet(st,buf,source,True);
+       switch (msgtype) {
+       case LABEL_MSG0:
+           process_msg0(st,buf,source);
+           break;
+       case LABEL_MSG1:
+           /* Setup packet: should not have been explicitly addressed
+              to us */
+           slog(st,LOG_SECURITY,"incoming explicitly addressed msg1");
+           break;
+       case LABEL_MSG2:
+           /* Setup packet: expected only in state SENTMSG1 */
+           if (st->state!=SITE_SENTMSG1) {
+               slog(st,LOG_UNEXPECTED,"unexpected MSG2");
+           } else if (process_msg2(st,buf,source))
+               enter_state_sentmsg3(st);
+           else {
+               slog(st,LOG_SECURITY,"invalid MSG2");
+           }
+           break;
+       case LABEL_MSG3:
+           /* Setup packet: expected only in state SENTMSG2 */
+           if (st->state!=SITE_SENTMSG2) {
+               slog(st,LOG_UNEXPECTED,"unexpected MSG3");
+           } else if (process_msg3(st,buf,source))
+               enter_state_sentmsg4(st);
+           else {
+               slog(st,LOG_SECURITY,"invalid MSG3");
+           }
+           break;
+       case LABEL_MSG4:
+           /* Setup packet: expected only in state SENTMSG3 */
+           if (st->state!=SITE_SENTMSG3) {
+               slog(st,LOG_UNEXPECTED,"unexpected MSG4");
+           } else if (process_msg4(st,buf,source))
+               enter_state_sentmsg5(st);
+           else {
+               slog(st,LOG_SECURITY,"invalid MSG4");
+           }
+           break;
+       case LABEL_MSG5:
+           /* Setup packet: expected only in state SENTMSG4 or RUN */
+           if (st->state!=SITE_SENTMSG4 && st->state!=SITE_RUN) {
+               slog(st,LOG_UNEXPECTED,"unexpected MSG5");
+           } else if (process_msg5(st,buf,source)) {
+               send_msg6(st);
+           } else {
+               slog(st,LOG_SECURITY,"invalid MSG5");
+           }
+           break;
+       case LABEL_MSG6:
+           /* Setup packet: expected only in state SENTMSG5 */
+           if (st->state!=SITE_SENTMSG5) {
+               slog(st,LOG_UNEXPECTED,"unexpected MSG6");
+           } else if (process_msg6(st,buf,source)) {
+               BUF_FREE(&st->buffer); /* Free message 5 */
+               activate_new_key(st);
+           } else {
+               slog(st,LOG_SECURITY,"invalid MSG6");
+           }
+           break;
+       case LABEL_MSG8:
+           /* NAK packet: enter state where we ping and check for response */
+           slog(st,LOG_ERROR,"received a NAK");
+           break;
+       default:
+           slog(st,LOG_SECURITY,"received message of unknown type 0x%08x",
+                msgtype);
+           break;
+       }
+       BUF_FREE(buf);
+       return True;
+    }
+
+    return False;
+}
+
+static void site_control(void *vst, bool_t run)
+{
+    struct site *st=vst;
+    if (run) enter_state_run(st);
+    else enter_state_stop(st);
+}
+
+static list_t *site_apply(closure_t *self, struct cloc loc, dict_t *context,
+                         list_t *args)
+{
+    struct site *st;
+    item_t *item;
+    dict_t *dict;
+
+    st=safe_malloc(sizeof(*st),"site_apply");
+
+    st->cl.description="site";
+    st->cl.type=CL_SITE;
+    st->cl.apply=NULL;
+    st->cl.interface=&st->ops;
+    st->ops.st=st;
+    st->ops.control=site_control;
+    st->ops.status=site_status;
+
+    /* First parameter must be a dict */
+    item=list_elem(args,0);
+    if (!item || item->type!=t_dict)
+       cfgfatal(loc,"site","parameter must be a dictionary\n");
+    
+    dict=item->data.dict;
+    st->netlink=find_cl_if(dict,"netlink",CL_NETLINK,True,"site",loc);
+    st->comm=find_cl_if(dict,"comm",CL_COMM,True,"site",loc);
+    st->resolver=find_cl_if(dict,"resolver",CL_RESOLVER,True,"site",loc);
+    st->log=find_cl_if(dict,"log",CL_LOG,True,"site",loc);
+    st->random=find_cl_if(dict,"random",CL_RANDOMSRC,True,"site",loc);
+
+    st->localname=dict_read_string(dict, "local-name", True, "site", loc);
+    st->privkey=find_cl_if(dict,"local-key",CL_RSAPRIVKEY,True,"site",loc);
+    st->remoteport=dict_read_number(dict,"port",True,"site",loc,0);
+
+    st->remotename=dict_read_string(dict, "name", True, "site", loc);
+    st->address=dict_read_string(dict, "address", False, "site", loc);
+    dict_read_subnet_list(dict, "networks", True, "site", loc,
+                         &st->remotenets);
+    st->pubkey=find_cl_if(dict,"key",CL_RSAPUBKEY,True,"site",loc);
+
+    st->transform=
+       find_cl_if(dict,"transform",CL_TRANSFORM,True,"site",loc);
+
+    st->dh=find_cl_if(dict,"dh",CL_DH,True,"site",loc);
+    st->hash=find_cl_if(dict,"hash",CL_HASH,True,"site",loc);
+
+    st->key_lifetime=dict_read_number(dict,"key-lifetime",
+                                     False,"site",loc,DEFAULT_KEY_LIFETIME);
+    st->setup_retries=dict_read_number(dict,"setup-retries",
+                                      False,"site",loc,DEFAULT_SETUP_RETRIES);
+    st->setup_timeout=dict_read_number(dict,"setup-timeout",
+                                      False,"site",loc,DEFAULT_SETUP_TIMEOUT);
+    st->wait_timeout=dict_read_number(dict,"wait-time",
+                                     False,"site",loc,DEFAULT_WAIT_TIME);
+    /* XXX should be configurable */
+    st->log_events=LOG_SECURITY|LOG_ERROR|
+       LOG_ACTIVATE_KEY|LOG_TIMEOUT_KEY|LOG_SETUP_INIT|LOG_SETUP_TIMEOUT;
+
+    /* The information we expect to see in incoming messages of type 1 */
+    st->setupsiglen=strlen(st->remotename)+strlen(st->localname)+8;
+    st->setupsig=safe_malloc(st->setupsiglen,"site_apply");
+    *(uint32_t *)&(st->setupsig[0])=LABEL_MSG1;
+    *(uint16_t *)&(st->setupsig[4])=htons(strlen(st->remotename));
+    memcpy(&st->setupsig[6],st->remotename,strlen(st->remotename));
+    *(uint16_t *)&(st->setupsig[6+strlen(st->remotename)])=
+       htons(strlen(st->localname));
+    memcpy(&st->setupsig[8+strlen(st->remotename)],st->localname,
+          strlen(st->localname));
+    st->setup_priority=(strcmp(st->localname,st->remotename)>0);
+
+    buffer_new(&st->buffer,SETUP_BUFFER_LEN);
+
+    /* We are interested in poll(), but only for timeouts. We don't have
+       any fds of our own. */
+    register_for_poll(st, site_beforepoll, site_afterpoll, 0, "site");
+    st->timeout=0;
+
+    st->current_valid=False;
+    st->current_key_timeout=0;
+    st->peer_valid=False;
+    /* XXX mlock these */
+    st->dhsecret=safe_malloc(st->dh->len,"site:dhsecret");
+    st->sharedsecret=safe_malloc(st->transform->keylen,"site:sharedsecret");
+
+    /* We need to register the remote networks with the netlink device */
+    st->netlink_cid=st->netlink->regnets(st->netlink->st, &st->remotenets,
+                                        site_outgoing, st,
+                                        st->transform->max_start_pad+(4*4),
+                                        st->transform->max_end_pad);
+
+    st->comm->request_notify(st->comm->st, st, site_incoming);
+
+    st->current_transform=st->transform->create(st->transform->st);
+    st->new_transform=st->transform->create(st->transform->st);
+
+    enter_state_stop(st);
+
+    return new_closure(&st->cl);
+}
+
+init_module site_module;
+void site_module(dict_t *dict)
+{
+    add_closure(dict,"site",site_apply);
+}
diff --git a/testconfig b/testconfig
new file mode 100644 (file)
index 0000000..4541199
--- /dev/null
@@ -0,0 +1,158 @@
+# secnet configuration file
+
+# This file defines a dictionary full of configuration information for
+# secnet. Two keys must be defined in this file for secnet to
+# start. One is "system", a dictionary containing systemwide control
+# parameters. The other is "sites", a list of all the sites that you
+# intend to communicate with.
+
+# Other files can be included inline by writing "include filename" at
+# the start of a line.
+
+# The configuration file has a fairly simple syntax:
+# key definition;  or key = definition; (the "=" is optional)
+# ...sets 'key' in the current dictionary to 'definition'.
+# 
+# "key" is [[:alpha:]_][[:alnum:]\-_]*
+# 
+# definition may be one of the following:
+#  a string, in quotes
+#  a number, in decimal
+#  a dictionary, in { }
+#  a path to a key that already exists, to reference that definition
+#  a "closure", followed by arguments
+#
+# paths are key1/key2/key3... (starting from wherever we find key1, i.e. in
+#  the current dictionary or any of its parents)
+# alternatively /key1/key2/key3... (to start from the root)
+# 
+# closures are followed by an argument list in ( ), and may return
+# whatever type they like (including other closures)
+#
+# closure { definitions } is short for closure({definitions}).
+#
+# Whenever secnet looks for a key it checks the (lexical) parent dictionaries
+# as well until it finds it or reaches the root. This is useful for setting
+# defaults for large collections of dictionaries (eg. defining sites).
+#
+# It is also permissible to list other dictionaries before a dictionary
+# definition, eg. <defaults,otherdefaults>{definitions}. These will be
+# searched in order for keys, before the lexical parent. (Not yet implemented)
+# 
+# secnet predefines some keys in the root dictionary; some useful ones are:
+#  yes, true, True, TRUE:   the boolean value True
+#  no, false, False, FALSE: the boolean value False
+#  makelist:   turns a dictionary (arg1) into a list (return value)
+#  readfile:   reads a file (arg1) and returns it as a string
+#
+# secnet modules also predefine keys, eg. "adns", "randomfile", etc.
+# See the module documentation for more information.
+
+# After the configuration file is read, secnet looks for particular keys
+# in configuration space to tell it what to do:
+#  system:     system-wide parameters (control, logging, etc.)
+#  sites:      a list of sites with which to communicate
+
+# Log facility
+log logfile("secnet","local2"); # Not yet implemented, goes to stderr
+
+# Systemwide configuration (all other configuration is per-site):
+# log          a log facility for program messages
+# userid       who we try to run as after setup
+# pidfile
+system {
+#      userid "steve";
+#      pidfile "/var/run/secnet.pid";
+       pidfile "foo.pid";
+};
+
+# Parameters for each remote site (arguments to the site() closure):
+#  things we configure locally
+# buffer                buffer for constructing/sending/receiving packets
+# netlink              user/kernel netlink device for this tunnel
+# comm                 UDP communication
+# resolver             resolver to use for name lookups
+# log                  a log destination for this connection
+# log-events           string list: which events we log
+# random                a source of randomness
+
+#  our local configuration visible to the outside world
+# local-name           string: how we identify ourselves to them
+# local-key            our own private RSA key
+# local-port           port number we listen on
+
+#  their configuration visible to us
+# name                 string: how they identify themselves
+# address              string: use with resolver to find their IP address
+# networks             string list: their networks for us
+# key                  the remote site's RSA public key
+# port                 port we send to to contact remote site
+
+#  things both ends must agree on
+# transform             routine for bulk encryption
+# dh                   Diffie-Hellman parameters
+# hash                 secure hash function
+
+#  things both ends ought to agree on, but don't have to
+# key-lifetime          max session key lifetime, in milliseconds
+# setup-retries         max retransmits of a key setup packet
+# setup-timeout         wait between retransmits of key setup packets, in ms
+# wait-time             wait between unsuccessful key setup attempts, in ms
+
+netlink userv-ipif {
+       name "userv-ipif"; # Printed in log messages from this netlink
+       # userv-path "/usr/bin/userv";
+       # service-user "root";
+       # service-name "ipif";
+
+       # local networks served by this netlink device
+       # incoming tunneled packets for other networks will be discarded
+       networks "192.168.73.0/24", "192.168.1.0/24", "172.19.71.0/24";
+       local-address "192.168.73.72"; # IP address of interface
+       secnet-address "192.168.73.73"; # IP address of secnet
+       mtu 1400;
+
+       buffer sysbuffer(); # userv/ipif needs a buffer to build incoming
+               # packets from the netlink device before passing them
+               # to the site layer
+};
+comm udp {
+       port 1234;
+       buffer sysbuffer(4096,{lockdown=yes;});
+};
+resolver adns {
+       config="wibble wobble";
+};
+# log is defined earlier - we share it with the system
+log-events "init","up","down";
+random randomfile("/dev/urandom",no);
+
+local-name "myrddin";
+local-key rsa-private("private-key");
+
+transform serpent256-cbc {
+       max-sequence-skew 10;
+};
+
+dh diffie-hellman("8db5f2c15ac96d9f3382d1ef4688fba14dc7908ae7dfd71a9cfe7f479a75d506dc53f159aeaf488bde073fe544bc91c099f101fcf60074f30c06e36263c03ca9e07931ce3fc235fe1171dc6d9316fb097bd4362891e2c36e234e7c16b038fd97b1f165c710e90537de66ee4f54001f5712b050d4e07de3fba07607b19b64f6c3","2");
+hash md5;
+
+key-lifetime 20000;
+
+zealot {
+               name "zealot";
+               address "zealot.sinister.greenend.org.uk";
+               port 5678;
+               networks "192.168.73.74/32", "192.168.73.75/32";
+               key rsa-public("35","131453873229748492184986747327990913828179255774895541667982108408897406369168730551214152673574619385573519088922707364993860644376262000057302119569116289693520981276177337391324943049983046703853106890057346878967444626093102422836819979338760420960495059950787838142162794317002315919126174831103379472833");
+       };
+
+myrddin {
+               name "myrddin";
+               address "myrddin.sinister.greenend.org.uk";
+               port 1234;
+               networks "192.168.73.72/32", "192.168.73.73/32";
+               key rsa-public("35","154107175724781677184264293617887954015562225725852111745852699493257053099810379926047345975839848434403852210573185384327420788855664167034282567346429150999373740871227795773749618022407366186555483566435251279808390618987056868368084933125373643004284007109877210578088697520329039753099981203724057693543");
+       };
+
+sites site(zealot);
diff --git a/testconfigz b/testconfigz
new file mode 100644 (file)
index 0000000..f2a66a6
--- /dev/null
@@ -0,0 +1,153 @@
+# secnet configuration file
+
+# This file defines a dictionary full of configuration information for
+# secnet. Two keys must be defined in this file for secnet to
+# start. One is "system", a dictionary containing systemwide control
+# parameters. The other is "sites", a list of all the sites that you
+# intend to communicate with.
+
+# Other files can be included inline by writing "include filename" at
+# the start of a line.
+
+# The configuration file has a fairly simple syntax:
+# key definition;  or key = definition; (the "=" is optional)
+# ...sets 'key' in the current dictionary to 'definition'.
+# 
+# "key" is [[:alpha:]_][[:alnum:]\-_]*
+# 
+# definition may be one of the following:
+#  a string, in quotes
+#  a number, in decimal
+#  a dictionary, in { }
+#  a path to a key that already exists, to reference that definition
+#  a "closure", followed by arguments
+#
+# paths are key1/key2/key3... (starting from wherever we find key1, i.e. in
+#  the current dictionary or any of its parents)
+# alternatively /key1/key2/key3... (to start from the root)
+# 
+# closures are followed by an argument list in ( ), and may return
+# whatever type they like (including other closures)
+#
+# closure { definitions } is short for closure({definitions}).
+#
+# Whenever secnet looks for a key it checks the (lexical) parent dictionaries
+# as well until it finds it or reaches the root. This is useful for setting
+# defaults for large collections of dictionaries (eg. defining sites).
+#
+# It is also permissible to list other dictionaries before a dictionary
+# definition, eg. <defaults,otherdefaults>{definitions}. These will be
+# searched in order for keys, before the lexical parent. (Not yet implemented)
+# 
+# secnet predefines some keys in the root dictionary; some useful ones are:
+#  yes, true, True, TRUE:   the boolean value True
+#  no, false, False, FALSE: the boolean value False
+#  makelist:   turns a dictionary (arg1) into a list (return value)
+#  readfile:   reads a file (arg1) and returns it as a string
+#
+# secnet modules also predefine keys, eg. "adns", "randomfile", etc.
+# See the module documentation for more information.
+
+# After the configuration file is read, secnet looks for particular keys
+# in configuration space to tell it what to do:
+#  system:     system-wide parameters (control, logging, etc.)
+#  sites:      a list of sites with which to communicate
+
+# Log facility
+log logfile("secnet","local2"); # Not yet implemented, goes to stderr
+
+# Systemwide configuration (all other configuration is per-site):
+# log          a log facility for program messages
+# userid       who we try to run as after setup
+# pidfile
+system {
+#      userid "tunnel";
+#      pidfile "/var/run/secnet.pid";
+};
+
+# Parameters for each remote site (arguments to the site() closure):
+#  things we configure locally
+# buffer                buffer for constructing/sending/receiving packets
+# netlink              user/kernel netlink device for this tunnel
+# comm                 UDP communication
+# resolver             resolver to use for name lookups
+# log                  a log destination for this connection
+# log-events           string list: which events we log
+# random                a source of randomness
+
+#  our local configuration visible to the outside world
+# local-name           string: how we identify ourselves to them
+# local-key            our own private RSA key
+# local-port           port number we listen on
+
+#  their configuration visible to us
+# name                 string: how they identify themselves
+# address              string: use with resolver to find their IP address
+# networks             string list: their networks for us
+# key                  the remote site's RSA public key
+# port                 port we send to to contact remote site
+
+#  things both ends must agree on
+# transform             routine for bulk encryption
+# dh                   Diffie-Hellman parameters
+# hash                 secure hash function
+
+# A buffer for all sites to share, to construct outgoing packets
+buffer sysbuffer(4096,{lockdown=yes;});
+
+netlink userv-ipif {
+       name "userv-ipif"; # Printed in log messages from this netlink
+       # userv-path "/usr/bin/userv";
+       # service-user "root";
+       # service-name "ipif";
+
+       # local networks served by this netlink device
+       # incoming tunneled packets for other networks will be discarded
+       networks "192.168.73.74/32";
+       local-address "192.168.73.74"; # IP address of interface
+       secnet-address "192.168.73.75"; # IP address of secnet
+       mtu 1400;
+
+       buffer sysbuffer(); # userv/ipif needs a buffer to build incoming
+               # packets from the netlink device before passing them
+               # to the site layer
+};
+comm udp {
+       port 5678;
+       # buffer shared with sites
+};
+resolver adns {
+       noenv=yes;      # yes is a name for the boolean "true"
+       nameservers "127.0.0.1","192.168.73.4";
+};
+# log is defined earlier - we share it with the system
+log-events "init","up","down";
+random randomfile("/dev/urandom",no);
+
+local-name "zealot";
+local-key rsa-private("private-key");
+
+transform serpent256-cbc {
+       max-sequence-skew 10;
+};
+
+dh diffie-hellman("8db5f2c15ac96d9f3382d1ef4688fba14dc7908ae7dfd71a9cfe7f479a75d506dc53f159aeaf488bde073fe544bc91c099f101fcf60074f30c06e36263c03ca9e07931ce3fc235fe1171dc6d9316fb097bd4362891e2c36e234e7c16b038fd97b1f165c710e90537de66ee4f54001f5712b050d4e07de3fba07607b19b64f6c3","2");
+hash md5;
+
+zealot {
+               name "zealot";
+               address "zealot.sinister.greenend.org.uk";
+               port 5678;
+               networks "192.168.73.74/32", "192.168.73.75/32";
+               key rsa-public("35","131453873229748492184986747327990913828179255774895541667982108408897406369168730551214152673574619385573519088922707364993860644376262000057302119569116289693520981276177337391324943049983046703853106890057346878967444626093102422836819979338760420960495059950787838142162794317002315919126174831103379472833");
+       };
+
+myrddin {
+               name "myrddin";
+               address "myrddin.sinister.greenend.org.uk";
+               port 1234;
+               networks "192.168.73.72/32", "192.168.73.73/32";
+               key rsa-public("35","154107175724781677184264293617887954015562225725852111745852699493257053099810379926047345975839848434403852210573185384327420788855664167034282567346429150999373740871227795773749618022407366186555483566435251279808390618987056868368084933125373643004284007109877210578088697520329039753099981203724057693543");
+       };
+
+sites site(myrddin);
diff --git a/testsites b/testsites
new file mode 100644 (file)
index 0000000..e3ca05f
--- /dev/null
+++ b/testsites
@@ -0,0 +1,27 @@
+# This is secnet's equivalent of the 'vpn-sites' file
+
+# Global parameters for all sites (can be overridden if some sites want to
+# use other parameters)
+
+sinister-vpn {
+
+dh diffie-hellman("8db5f2c15ac96d9f3382d1ef4688fba14dc7908ae7dfd71a9cfe7f479a75d506dc53f159aeaf488bde073fe544bc91c099f101fcf60074f30c06e36263c03ca9e07931ce3fc235fe1171dc6d9316fb097bd4362891e2c36e234e7c16b038fd97b1f165c710e90537de66ee4f54001f5712b050d4e07de3fba07607b19b64f6c3","2");
+hash md5;
+
+# All the remote sites we might want to talk to
+groad {
+               name "gilbert-road";
+               address "relativity.dynamic.greenend.org.uk";
+               port 5678;
+               networks "172.18.45.0/24";
+               key rsa-public("35","153279875126380522437827076871354104097683702803616313419670959273217685015951590424876274370401136371563604396779864283483623325238228723798087715987495590765759771552692972297669972616769731553560605291312242789575053620182470998166393580503400960149506261455420521811814445675652857085993458063584337404329");
+       };
+chiark {
+               name "chiark";
+               address "chiark.greenend.org.uk";
+               port 2345;
+               networks "172.31.80.8/32";
+               key rsa-public("35","127129251486595848418110457412760173306108766728826928401101049305981756206590497728003410607079784801800518655333869748411389535602962935662455800359479854817411356280256727700339726067467542581881498715223842814199845818940876020358790270431574802728927549159072714772035370848962882690573372309719699356913");
+       };
+
+};
diff --git a/transform.c b/transform.c
new file mode 100644 (file)
index 0000000..f94f5ae
--- /dev/null
@@ -0,0 +1,342 @@
+/* Transform module - bulk data transformation */
+
+/* For now it's hard-coded to do sequence
+   number/pkcs5/serpent-cbcmac/serpent with a 256 bit key for each
+   instance of serpent. We also require key material for the IVs for
+   cbcmac and cbc. Hack: we're not using full 128-bit IVs, we're just
+   using 32 bits and encrypting to get the full IV to save space in
+   the packets sent over the wire. */
+
+#include <stdio.h>
+#include "secnet.h"
+#include "util.h"
+#include "serpent.h"
+
+/* Required key length in bytes */
+#define REQUIRED_KEYLEN ((512+64+32)/8)
+
+struct transform {
+    closure_t cl;
+    uint32_t line;
+    struct transform_if ops;
+    uint32_t max_seq_skew;
+};
+
+struct transform_inst {
+    struct transform_inst_if ops;
+    struct keyInstance cryptkey;
+    struct keyInstance mackey;
+    uint32_t cryptiv;
+    uint32_t maciv;
+    uint32_t sendseq;
+    uint32_t lastrecvseq;
+    uint32_t max_skew;
+    bool_t keyed;
+};
+
+#define PKCS5_MASK 15
+
+static bool_t transform_setkey(void *sst, uint8_t *key, uint32_t keylen)
+{
+    struct transform_inst *ti=sst;
+
+    if (keylen<REQUIRED_KEYLEN) {
+       Message(M_ERROR,"transform_create: insufficient key material supplied "
+               "(need %d bytes, got %d)\n",REQUIRED_KEYLEN,keylen);
+       return False;
+    }
+
+#if 0
+    {
+       int i;
+       printf("Setting key to: ");
+       for (i=0; i<keylen; i++)
+           printf("%02x",key[i]);
+       printf("\n");
+    }
+#endif /* 0 */
+
+    serpent_makekey(&ti->cryptkey,256,key);
+    serpent_makekey(&ti->mackey,256,key+32);
+    ti->cryptiv=*(uint32_t *)(key+64);
+    ti->maciv=*(uint32_t *)(key+68);
+    ti->sendseq=*(uint32_t *)(key+72);
+    ti->lastrecvseq=ti->sendseq;
+    ti->keyed=True;
+
+    return True;
+}
+
+static void transform_delkey(void *sst)
+{
+    struct transform_inst *ti=sst;
+
+    memset(&ti->cryptkey,0,sizeof(ti->cryptkey));
+    memset(&ti->mackey,0,sizeof(ti->mackey));
+    ti->keyed=False;
+}
+
+static uint32_t transform_forward(void *sst, struct buffer_if *buf,
+                                 char **errmsg)
+{
+    struct transform_inst *ti=sst;
+    uint8_t *padp;
+    int padlen;
+    uint32_t iv[4];
+    uint32_t macplain[4];
+    uint32_t macacc[4];
+    uint32_t *n, *p;
+
+    if (!ti->keyed) {
+       *errmsg="transform unkeyed";
+       return 1;
+    }
+
+    /* Sequence number */
+    *(uint32_t *)buf_prepend(buf,4)=htonl(ti->sendseq);
+    ti->sendseq++;
+
+    /* PKCS5, stolen from IWJ */
+                                    /* eg with blocksize=4 mask=3 mask+2=5   */
+                                    /* msgsize    20    21    22    23   24  */
+    padlen= PKCS5_MASK-buf->size;   /*           -17   -18   -19   -16  -17  */
+    padlen &= PKCS5_MASK;           /*             3     2     1     0    3  */
+    padlen++;                       /*             4     3     2     1    4  */
+
+    padp=buf_append(buf,padlen);
+    memset(padp,padlen,padlen);
+
+    /* Serpent-CBCMAC. We expand the IV from 32-bit to 128-bit using
+       one encryption. Then we do the MAC and append the result. We don't
+       bother sending the IV - it's the same each time. (If we wanted to send
+       it we've have to add 16 bytes to each message, not 4, so that the
+       message stays a multiple of 16 bytes long.) */
+    memset(iv,0,16);
+    iv[0]=ti->maciv;
+    serpent_encrypt(&ti->mackey,iv,macacc);
+
+    /* CBCMAC: encrypt in CBC mode. The MAC is the last encrypted
+       block encrypted once again. */
+    for (n=(uint32_t *)buf->start; n<(uint32_t *)(buf->start+buf->size); n+=4)
+    {
+       macplain[0]=macacc[0]^n[0];
+       macplain[1]=macacc[1]^n[1];
+       macplain[2]=macacc[2]^n[2];
+       macplain[3]=macacc[3]^n[3];
+       serpent_encrypt(&ti->mackey,macplain,macacc);
+    }
+    serpent_encrypt(&ti->mackey,macacc,macacc);
+    memcpy(buf_append(buf,16),macacc,16);
+
+    /* Serpent-CBC. We expand the ID as for CBCMAC, do the encryption,
+       and prepend the IV before increasing it. */
+    memset(iv,0,16);
+    iv[0]=ti->cryptiv;
+    serpent_encrypt(&ti->cryptkey,iv,iv);
+
+    /* CBC: each block is XORed with the previous encrypted block (or the IV)
+       before being encrypted. */
+    p=iv;
+    for (n=(uint32_t *)buf->start; n<(uint32_t *)(buf->start+buf->size); n+=4)
+    {
+       n[0]=p[0]^n[0];
+       n[1]=p[1]^n[1];
+       n[2]=p[2]^n[2];
+       n[3]=p[3]^n[3];
+       serpent_encrypt(&ti->cryptkey,n,n);
+       p=n;
+    }
+
+    *(uint32_t *)buf_prepend(buf,4)=ti->cryptiv;
+    ti->cryptiv++;
+
+    return 0;
+}
+
+static uint32_t transform_reverse(void *sst, struct buffer_if *buf,
+                                 char **errmsg)
+{
+    struct transform_inst *ti=sst;
+    uint8_t *padp;
+    unsigned padlen;
+    int i;
+    uint32_t seqnum, skew;
+    uint32_t iv[4];
+    uint32_t pct[4];
+    uint32_t macplain[4];
+    uint32_t macacc[4];
+    uint32_t *n;
+    uint32_t *macexpected;
+
+    if (!ti->keyed) {
+       *errmsg="transform unkeyed";
+       return 1;
+    }
+
+    /* CBC */
+    memset(iv,0,16);
+    iv[0]=*(uint32_t *)buf_unprepend(buf,4);
+    serpent_encrypt(&ti->cryptkey,iv,iv);
+    /* XXX assert bufsize is multiple of blocksize */
+    for (n=(uint32_t *)buf->start; n<(uint32_t *)(buf->start+buf->size); n+=4)
+    {
+       pct[0]=n[0]; pct[1]=n[1]; pct[2]=n[2]; pct[3]=n[3];
+       serpent_decrypt(&ti->cryptkey,n,n);
+       n[0]=iv[0]^n[0];
+       n[1]=iv[1]^n[1];
+       n[2]=iv[2]^n[2];
+       n[3]=iv[3]^n[3];
+       iv[0]=pct[0]; iv[1]=pct[1]; iv[2]=pct[2]; iv[3]=pct[3];
+    }
+
+    /* CBCMAC */
+    macexpected=buf_unappend(buf,16);
+    memset(iv,0,16);
+    iv[0]=ti->maciv;
+    serpent_encrypt(&ti->mackey,iv,macacc);
+
+    /* CBCMAC: encrypt in CBC mode. The MAC is the last encrypted
+       block encrypted once again. */
+    for (n=(uint32_t *)buf->start; n<(uint32_t *)(buf->start+buf->size); n+=4)
+    {
+       macplain[0]=macacc[0]^n[0];
+       macplain[1]=macacc[1]^n[1];
+       macplain[2]=macacc[2]^n[2];
+       macplain[3]=macacc[3]^n[3];
+       serpent_encrypt(&ti->mackey,macplain,macacc);
+    }
+    serpent_encrypt(&ti->mackey,macacc,macacc);
+    if (memcmp(macexpected,macacc,16)!=0) {
+       *errmsg="invalid MAC";
+       return 1;
+    }
+
+    /* PKCS5, stolen from IWJ */
+
+    padp=buf_unappend(buf,1);
+    padlen=*padp;
+    if (!padlen || (padlen > PKCS5_MASK+1)) {
+       *errmsg="pkcs5: invalid length";
+       return 1;
+    }
+
+    padp=buf_unappend(buf,padlen-1);
+    for (i=0; i<padlen-1; i++) {
+       if (*++padp != padlen) {
+           *errmsg="pkcs5: corrupted padding";
+           return 1;
+       }
+    }
+
+    /* Sequence number must be within max_skew of lastrecvseq; lastrecvseq
+       is only allowed to increase. */
+    seqnum=ntohl(*(uint32_t *)buf_unprepend(buf,4));
+    skew=seqnum-ti->lastrecvseq;
+    if (skew<10) {
+       /* Ok */
+       ti->lastrecvseq=seqnum;
+    } else if ((0-skew)<10) {
+       /* Ok */
+    } else {
+       /* Too much skew */
+       *errmsg="seqnum: too much skew";
+       return 1;
+    }
+    
+    return 0;
+}
+
+static void transform_destroy(void *sst)
+{
+    struct transform_inst *st=sst;
+
+    memset(st,0,sizeof(*st)); /* Destroy key material */
+    free(st);
+}
+
+static struct transform_inst_if *transform_create(void *sst)
+{
+    struct transform_inst *ti;
+    struct transform *st=sst;
+
+    ti=safe_malloc(sizeof(*ti),"transform_create");
+    /* mlock XXX */
+
+    ti->ops.st=ti;
+    ti->ops.setkey=transform_setkey;
+    ti->ops.delkey=transform_delkey;
+    ti->ops.forwards=transform_forward;
+    ti->ops.reverse=transform_reverse;
+    ti->ops.destroy=transform_destroy;
+    ti->max_skew=st->max_seq_skew;
+    ti->keyed=False;
+
+    return &ti->ops;
+}
+
+static list_t *transform_apply(closure_t *self, struct cloc loc,
+                              dict_t *context, list_t *args)
+{
+    struct transform *st;
+    item_t *item;
+    dict_t *dict;
+
+    st=safe_malloc(sizeof(*st),"serpent");
+    st->cl.description="serpent-cbc256";
+    st->cl.type=CL_TRANSFORM;
+    st->cl.apply=NULL;
+    st->cl.interface=&st->ops;
+    st->ops.st=st;
+    st->ops.max_start_pad=28; /* 4byte seqnum, 16byte pad, 4byte MACIV,
+                                4byte IV */
+    st->ops.max_end_pad=16; /* 16byte CBCMAC */
+
+    /* We need 256*2 bits for serpent keys, 32 bits for CBC-IV and 32 bits
+       for CBCMAC-IV, and 32 bits for init sequence number */
+    st->ops.keylen=REQUIRED_KEYLEN;
+    st->ops.create=transform_create;
+
+    /* First parameter must be a dict */
+    item=list_elem(args,0);
+    if (!item || item->type!=t_dict)
+       cfgfatal(loc,"userv-ipif","parameter must be a dictionary\n");
+    
+    dict=item->data.dict;
+    st->max_seq_skew=dict_read_number(dict, "max-sequence-skew",
+                                     False, "serpent-cbc256", loc, 10);
+
+    return new_closure(&st->cl);
+}
+
+init_module transform_module;
+void transform_module(dict_t *dict)
+{
+    struct keyInstance k;
+    uint8_t data[32];
+    uint32_t plaintext[4];
+    uint32_t ciphertext[4];
+
+    /* Serpent self-test */
+    memset(data,0,32);
+    serpent_makekey(&k,256,data);
+    plaintext[0]=0x00000000;
+    plaintext[1]=0x00000001;
+    plaintext[2]=0x00000002;
+    plaintext[3]=0x00000003;
+    serpent_encrypt(&k,plaintext,ciphertext);
+    if (ciphertext[3]!=0x7ca73bb0 ||
+       ciphertext[2]!=0x83C31E69 ||
+       ciphertext[1]!=0xec52bd82 ||
+       ciphertext[0]!=0x27a46120) {
+       fatal("transform_module: serpent failed self-test (encrypt)\n");
+    }
+    serpent_decrypt(&k,ciphertext,plaintext);
+    if (plaintext[0]!=0 ||
+       plaintext[1]!=1 ||
+       plaintext[2]!=2 ||
+       plaintext[3]!=3) {
+       fatal("transform_module: serpent failed self-test (decrypt)\n");
+    }
+
+    add_closure(dict,"serpent256-cbc",transform_apply);
+}
diff --git a/udp.c b/udp.c
new file mode 100644 (file)
index 0000000..177ce1c
--- /dev/null
+++ b/udp.c
@@ -0,0 +1,211 @@
+/* UDP send/receive module for secnet */
+
+/* This module enables sites to communicate by sending UDP
+ * packets. When an instance of the module is created we can
+ * optionally bind to a particular local IP address (not implemented
+ * yet).
+ *
+ * Sites register an interest in local port numbers for receiving
+ * packets, and can also send packets. We don't care about the source
+ * port number for sending packets. 
+ *
+ * Packets are offered to registered receivers in turn. Once one
+ * accepts it, it isn't offered to any more. */
+
+#include <stdio.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <string.h>
+#include <errno.h>
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+
+#include "secnet.h"
+#include "util.h"
+
+static beforepoll_fn udp_beforepoll;
+static afterpoll_fn udp_afterpoll;
+static comm_request_notify_fn request_notify;
+static comm_release_notify_fn release_notify;
+static comm_sendmsg_fn udp_sendmsg;
+
+/* The UDP module exports a pure closure which can be used to construct a
+ * UDP send/receive module. Arguments:
+ */
+
+struct notify_list {
+    comm_notify_fn *fn;
+    void *state;
+    struct notify_list *next;
+};
+
+struct udp {
+    closure_t cl;
+    struct comm_if ops;
+    struct cloc loc;
+    int fd;
+    struct buffer_if *rbuf;
+    struct notify_list *notify;
+};
+
+static int udp_beforepoll(void *state, struct pollfd *fds, int *nfds_io,
+                         int *timeout_io, const struct timeval *tv,
+                         uint64_t *now)
+{
+    struct udp *st=state;
+    if (*nfds_io<1) {
+       *nfds_io=1;
+       return ERANGE;
+    }
+    *nfds_io=1;
+    fds->fd=st->fd;
+    fds->events=POLLIN;
+    return 0;
+}
+
+static void udp_afterpoll(void *state, struct pollfd *fds, int nfds,
+                         const struct timeval *tv, uint64_t *now)
+{
+    struct udp *st=state;
+    struct sockaddr_in from;
+    int fromlen;
+    struct notify_list *n;
+    bool_t done;
+    int rv;
+
+    if (nfds && (fds->revents & POLLIN)) {
+       do {
+           fromlen=sizeof(from);
+           BUF_ASSERT_FREE(st->rbuf);
+           BUF_ALLOC(st->rbuf,"udp_afterpoll");
+           rv=recvfrom(st->fd, st->rbuf->start, st->rbuf->len, 0,
+                       (struct sockaddr *)&from, &fromlen);
+           if (rv>0) {
+               st->rbuf->size=rv;
+               done=False;
+               for (n=st->notify; n; n=n->next) {
+                   if (n->fn(n->state, st->rbuf, &from)) {
+                       done=True;
+                       break;
+                   }
+               }
+               if (!done) {
+                   /* XXX manufacture and send NAK packet */
+                   Message(M_WARNING,"Need to send NAK\n");
+                   BUF_FREE(st->rbuf);
+               }
+               BUF_ASSERT_FREE(st->rbuf);
+           } else {
+               BUF_FREE(st->rbuf);
+           }
+       } while (rv>=0);
+    }
+}
+
+static void request_notify(void *commst, void *nst, comm_notify_fn *fn)
+{
+    struct udp *st=commst;
+    struct notify_list *n;
+    
+    n=safe_malloc(sizeof(*n),"request_notify");
+    n->fn=fn;
+    n->state=nst;
+    n->next=st->notify;
+    st->notify=n;
+}
+
+static void release_notify(void *commst, void *nst, comm_notify_fn *fn)
+{
+    struct udp *st=commst;
+    struct notify_list *n, **p, *t;
+
+    /* XXX untested */
+    p=&st->notify;
+    for (n=st->notify; n; )
+    {
+       if (n->state==nst && n->fn==fn) {
+           t=n;
+           *p=n->next;
+           n=n->next;
+           free(t);
+       } else {
+           p=&n->next;
+           n=n->next;
+       }
+    }
+}
+
+static bool_t udp_sendmsg(void *commst, struct buffer_if *buf,
+                         struct sockaddr_in *dest)
+{
+    struct udp *st=commst;
+
+    /* XXX fix error reporting */
+    sendto(st->fd, buf->start, buf->size, 0,
+          (struct sockaddr *)dest, sizeof(*dest));
+
+    return True;
+}
+
+static list_t *udp_apply(closure_t *self, struct cloc loc, dict_t *context,
+                        list_t *args)
+{
+    struct udp *st;
+    item_t *i;
+    dict_t *d;
+    uint16_t local_port=0;
+    struct sockaddr_in addr;
+
+    st=safe_malloc(sizeof(*st),"udp_apply(st)");
+    st->loc=loc;
+    st->cl.description="udp";
+    st->cl.type=CL_COMM;
+    st->cl.apply=NULL;
+    st->cl.interface=&st->ops;
+    st->ops.st=st;
+    st->ops.request_notify=request_notify;
+    st->ops.release_notify=release_notify;
+    st->ops.sendmsg=udp_sendmsg;
+
+    i=list_elem(args,0);
+    if (!i || i->type!=t_dict) {
+       cfgfatal(st->loc,"udp","first argument must be a dictionary\n");
+    }
+    d=i->data.dict;
+
+    local_port=dict_read_number(d,"port",False,"udp",st->loc,0);
+    st->rbuf=find_cl_if(d,"buffer",CL_BUFFER,True,"udp",st->loc);
+
+    st->fd=socket(AF_INET, SOCK_DGRAM, 0);
+    if (st->fd<0) {
+       fatal_perror("udp_apply (%s:%d): socket",loc.file,loc.line);
+    }
+    if (fcntl(st->fd, F_SETFL, fcntl(st->fd, F_GETFL)|O_NONBLOCK)==-1) {
+       fatal_perror("udp_apply (%s:%d): fcntl(set O_NONBLOCK)",
+                    loc.file,loc.line);
+    }
+    if (fcntl(st->fd, F_SETFD, FD_CLOEXEC)==-1) {
+       fatal_perror("udp_apply (%s:%d): fcntl(set FD_CLOEXEC)",
+                    loc.file,loc.line);
+    }
+
+    memset(&addr, 0, sizeof(addr));
+    addr.sin_family=AF_INET;
+    if (local_port) {
+       addr.sin_port=htons(local_port);
+    }
+    if (bind(st->fd, (struct sockaddr *)&addr, sizeof(addr))!=0) {
+       fatal_perror("udp_apply (%s:%d): bind",loc.file,loc.line);
+    }
+
+    register_for_poll(st,udp_beforepoll,udp_afterpoll,1,"udp");
+
+    return new_closure(&st->cl);
+}
+
+init_module udp_module;
+void udp_module(dict_t *dict)
+{
+    add_closure(dict,"udp",udp_apply);
+}
diff --git a/util.c b/util.c
new file mode 100644 (file)
index 0000000..7e5a39a
--- /dev/null
+++ b/util.c
@@ -0,0 +1,572 @@
+/* $Log: util.c,v $
+ * Revision 1.2  1996/04/14 16:34:36  sde1000
+ * Added syslog support
+ * mpbin/mpstring functions moved from dh.c
+ *
+ * Revision 1.1  1996/03/14 17:05:03  sde1000
+ * Initial revision
+ *
+ */
+
+#include "config.h"
+#include <stdio.h>
+#include <stdarg.h>
+#include <string.h>
+#include <errno.h>
+#include <syslog.h>
+#include <unistd.h>
+#include <values.h>
+#include <assert.h>
+#include "util.h"
+#include "secnet.h"
+
+#define MIN_BUFFER_SIZE 64
+#define DEFAULT_BUFFER_SIZE 4096
+#define MAX_BUFFER_SIZE 131072
+
+static char *hexdigits="0123456789abcdef";
+
+uint32_t message_level=M_WARNING|M_ERROR|M_FATAL;
+uint32_t syslog_level=M_WARNING|M_ERROR|M_FATAL;
+static uint32_t current_phase=0;
+
+struct phase_hook {
+    hook_fn *fn;
+    void *state;
+    struct phase_hook *next;
+};
+
+static struct phase_hook *hooks[NR_PHASES]={NULL,};
+
+static void vMessage(uint32_t class, char *message, va_list args)
+{
+    FILE *dest=stdout;
+    if (class & message_level) {
+       if (class&M_FATAL || class&M_ERROR || class&M_WARNING) {
+           dest=stderr;
+       }
+       vfprintf(dest,message,args);
+    }
+/* XXX do something about syslog output here */
+#if 0
+    /* Maybe send message to syslog */
+    vsprintf(buff, message, args);
+    /* XXX Send each line as a separate log entry */
+    log(syslog_prio[level], buff);
+#endif /* 0 */
+}  
+
+void Message(uint32_t class, char *message, ...)
+{
+    va_list ap;
+
+    va_start(ap,message);
+
+    vMessage(class,message,ap);
+
+    va_end(ap);
+}
+
+static void vfatal(int status, bool_t perror, char *message, va_list args)
+{
+    int err;
+
+    err=errno;
+
+    enter_phase(PHASE_SHUTDOWN);
+    if (perror) {
+       Message(M_FATAL, "secnet fatal error: ");
+       vMessage(M_FATAL, message, args);
+       Message(M_FATAL, ": %s\n",strerror(err));
+    }
+    else {
+       Message(M_FATAL, "secnet fatal error: ");
+       vMessage(M_FATAL,message,args);
+    }
+    exit(status);
+}
+
+void fatal(char *message, ...)
+{
+    va_list args;
+    va_start(args,message);
+    vfatal(current_phase,False,message,args);
+    va_end(args);
+}
+
+void fatal_status(int status, char *message, ...)
+{
+    va_list args;
+    va_start(args,message);
+    vfatal(status,False,message,args);
+    va_end(args);
+}
+
+void fatal_perror(char *message, ...)
+{
+    va_list args;
+    va_start(args,message);
+    vfatal(current_phase,True,message,args);
+    va_end(args);
+}
+
+void fatal_perror_status(int status, char *message, ...)
+{
+    va_list args;
+    va_start(args,message);
+    vfatal(status,True,message,args);
+    va_end(args);
+}
+
+void cfgfatal(struct cloc loc, string_t facility, char *message, ...)
+{
+    va_list args;
+
+    va_start(args,message);
+
+    enter_phase(PHASE_SHUTDOWN);
+
+    if (loc.file && loc.line) {
+       Message(M_FATAL, "config error (%s, %s:%d): ",facility,loc.file,
+               loc.line);
+    } else if (!loc.file && loc.line) {
+       Message(M_FATAL, "config error (%s, line %d): ",facility,loc.line);
+    } else {
+       Message(M_FATAL, "config error (%s): ",facility);
+    }
+    
+    vMessage(M_FATAL,message,args);
+    va_end(args);
+    exit(current_phase);
+}
+
+char *safe_strdup(char *s, char *message)
+{
+    char *d;
+    d=strdup(s);
+    if (!d) {
+       fatal_perror(message);
+    }
+    return d;
+}
+
+void *safe_malloc(size_t size, char *message)
+{
+    void *r;
+    r=malloc(size);
+    if (!r) {
+       fatal_perror(message);
+    }
+    return r;
+}
+
+/* Convert a buffer into its MP_INT representation */
+void read_mpbin(MP_INT *a, uint8_t *bin, int binsize)
+{
+    char *buff;
+    int i;
+
+    buff=safe_malloc(binsize*2 + 1,"read_mpbin");
+
+    for (i=0; i<binsize; i++) {
+       buff[i*2]=hexdigits[(bin[i] & 0xf0) >> 4];
+       buff[i*2+1]=hexdigits[(bin[i] & 0xf)];
+    }
+    buff[binsize*2]=0;
+
+    mpz_set_str(a, buff, 16);
+    free(buff);
+}
+
+/* Convert a MP_INT into a hex string */
+char *write_mpstring(MP_INT *a)
+{
+    char *buff;
+
+    buff=safe_malloc(mpz_sizeinbase(a,16)+2,"write_mpstring");
+    mpz_get_str(buff, 16, a);
+    return buff;
+}
+
+static uint8_t hexval(uint8_t c)
+{
+    switch (c) {
+    case '0': return 0;
+    case '1': return 1;
+    case '2': return 2;
+    case '3': return 3;
+    case '4': return 4;
+    case '5': return 5;
+    case '6': return 6;
+    case '7': return 7;
+    case '8': return 8;
+    case '9': return 9;
+    case 'a': return 10;
+    case 'A': return 10;
+    case 'b': return 11;
+    case 'B': return 11;
+    case 'c': return 12;
+    case 'C': return 12;
+    case 'd': return 13;
+    case 'D': return 13;
+    case 'e': return 14;
+    case 'E': return 14;
+    case 'f': return 15;
+    case 'F': return 15;
+    }
+    return -1;
+}
+
+/* Convert a MP_INT into a buffer; return length; truncate if necessary */
+uint32_t write_mpbin(MP_INT *a, uint8_t *buffer, uint32_t buflen)
+{
+    char *hb;
+    int i,j,l;
+    
+    if (buflen==0) return 0;
+    hb=write_mpstring(a);
+    
+    l=strlen(hb);
+    i=0; j=0;
+    if (l&1) {
+       /* The number starts with a half-byte */
+       buffer[i++]=hexval(hb[j++]);
+    }
+    for (; hb[j] && i<buflen; i++) {
+       buffer[i]=(hexval(hb[j])<<4)|hexval(hb[j+1]);
+       j+=2;
+    }
+    free(hb);
+    return i;
+}
+
+bool_t subnet_match(struct subnet_list *list, uint32_t address)
+{
+    uint32_t i;
+    for (i=0; i<list->entries; i++) {
+       if (list->list[i].prefix == (address&list->list[i].mask)) return True;
+    }
+    return False;
+}
+
+/* The string buffer must be at least 16 bytes long */
+string_t ipaddr_to_string(uint32_t addr)
+{
+    uint8_t a,b,c,d;
+    string_t s;
+
+    s=safe_malloc(16,"ipaddr_to_string");
+    a=addr>>24;
+    b=addr>>16;
+    c=addr>>8;
+    d=addr;
+    snprintf(s, 16, "%d.%d.%d.%d", a, b, c, d);
+    return s;
+}
+
+string_t subnet_to_string(struct subnet *sn)
+{
+    uint32_t mask=sn->mask, addr=sn->prefix;
+    uint8_t a,b,c,d;
+    string_t s;
+    int i;
+
+    s=safe_malloc(19,"subnet_to_string");
+    a=addr>>24;
+    b=addr>>16;
+    c=addr>>8;
+    d=addr;
+    for (i=0; mask; i++) {
+       mask=(mask<<1);
+    }
+    snprintf(s, 19, "%d.%d.%d.%d/%d", a, b, c, d, i);
+    return s;
+}
+
+/* Take a list of log closures and merge them */
+struct loglist {
+    struct log_if *l;
+    struct loglist *next;
+};
+
+static void log_vmulti(void *state, int priority, char *message, va_list args)
+{
+    struct loglist *st=state, *i;
+
+    for (i=st; i; i=i->next) {
+       i->l->vlog(i->l->st,priority,message,args);
+    }
+}
+
+static void log_multi(void *st, int priority, char *message, ...)
+{
+    va_list ap;
+
+    va_start(ap,message);
+
+    log_vmulti(st,priority,message,ap);
+
+    va_end(ap);
+}
+
+struct log_if *init_log(list_t *ll)
+{
+    int i=0;
+    item_t *item;
+    closure_t *cl;
+    struct loglist *l=NULL, *n;
+    struct log_if *r;
+
+    while ((item=list_elem(ll,i++))) {
+       if (item->type!=t_closure) {
+           cfgfatal(item->loc,"init_log","item is not a closure");
+       }
+       cl=item->data.closure;
+       if (cl->type!=CL_LOG) {
+           cfgfatal(item->loc,"init_log","closure is not a logger");
+       }
+       n=safe_malloc(sizeof(*n),"init_log");
+       n->l=cl->interface;
+       n->next=l;
+       l=n;
+    }
+    if (!l) {
+       fatal("init_log: none of the items in the list are loggers");
+    }
+    r=safe_malloc(sizeof(*r), "init_log");
+    r->st=l;
+    r->log=log_multi;
+    r->vlog=log_vmulti;
+    return r;
+}
+
+struct logfile {
+    closure_t cl;
+    struct log_if ops;
+    FILE *f;
+};
+
+static void logfile_vlog(void *state, int priority, char *message,
+                        va_list args)
+{
+    struct logfile *st=state;
+
+    vfprintf(st->f,message,args);
+    fprintf(st->f,"\n");
+}
+
+static void logfile_log(void *state, int priority, char *message, ...)
+{
+    va_list ap;
+
+    va_start(ap,message);
+    logfile_vlog(state,priority,message,ap);
+    va_end(ap);
+}
+
+static list_t *logfile_apply(closure_t *self, struct cloc loc, dict_t *context,
+                            list_t *data)
+{
+    struct logfile *st;
+    
+    st=safe_malloc(sizeof(*st),"logfile_apply");
+    st->cl.description="logfile";
+    st->cl.type=CL_LOG;
+    st->cl.apply=NULL;
+    st->cl.interface=&st->ops;
+    st->ops.st=st;
+    st->ops.log=logfile_log;
+    st->ops.vlog=logfile_vlog;
+    st->f=stderr; /* XXX ignore args */
+
+    return new_closure(&st->cl);
+}
+       
+static char *phases[NR_PHASES]={
+    "PHASE_INIT",
+    "PHASE_GETOPTS",
+    "PHASE_READCONFIG",
+    "PHASE_SETUP",
+    "PHASE_DROPPRIV",
+    "PHASE_RUN",
+    "PHASE_SHUTDOWN"
+};
+
+void enter_phase(uint32_t new_phase)
+{
+    struct phase_hook *i;
+
+    Message(M_DEBUG_PHASE,"entering %s... ", phases[new_phase]);
+    current_phase=new_phase;
+
+    for (i=hooks[new_phase]; i; i=i->next)
+       i->fn(i->state, new_phase);
+    Message(M_DEBUG_PHASE,"now in %s\n",phases[new_phase]);
+}
+
+bool_t add_hook(uint32_t phase, hook_fn *fn, void *state)
+{
+    struct phase_hook *h;
+
+    h=safe_malloc(sizeof(*h),"add_hook");
+    h->fn=fn;
+    h->state=state;
+    h->next=hooks[phase];
+    hooks[phase]=h;
+    return True;
+}
+
+bool_t remove_hook(uint32_t phase, hook_fn *fn, void *state)
+{
+    fatal("remove_hook: not implemented\n");
+
+    return False;
+}
+
+void log(struct log_if *lf, int priority, char *message, ...)
+{
+    va_list ap;
+    
+    va_start(ap,message);
+    lf->vlog(lf->st,priority,message,ap);
+    va_end(ap);
+}
+
+struct buffer {
+    closure_t cl;
+    struct buffer_if ops;
+};
+
+void buffer_assert_free(struct buffer_if *buffer, string_t file, uint32_t line)
+{
+    if (!buffer->free) {
+       fatal("BUF_ASSERT_FREE, %s line %d, owned by %s\n",
+             file,line,buffer->owner);
+    }
+}
+
+void buffer_assert_used(struct buffer_if *buffer, string_t file, uint32_t line)
+{
+    if (buffer->free) {
+       fatal("BUF_ASSERT_USED, %s line %d, last owned by %s\n",
+             file,line,buffer->owner);
+    }
+}
+
+void buffer_init(struct buffer_if *buffer, uint32_t max_start_pad)
+{
+    buffer->start=buffer->base+max_start_pad;
+    buffer->size=0;
+}
+
+void *buf_append(struct buffer_if *buf, uint32_t amount) {
+    void *p;
+    p=buf->start + buf->size;
+    buf->size+=amount;
+    return p;
+}
+
+void *buf_prepend(struct buffer_if *buf, uint32_t amount) {
+    buf->size+=amount;
+    return buf->start-=amount;
+}
+
+void *buf_unappend(struct buffer_if *buf, uint32_t amount) {
+    if (buf->size < amount) return 0;
+    return buf->start+(buf->size-=amount);
+}
+
+void *buf_unprepend(struct buffer_if *buf, uint32_t amount) {
+    void *p;
+    p=buf->start;
+    buf->start+=amount;
+    buf->size-=amount;
+    return p;
+}
+
+/* Append a two-byte length and the string to the buffer. Length is in
+   network byte order. */
+void buf_append_string(struct buffer_if *buf, string_t s)
+{
+    uint16_t len;
+
+    len=strlen(s);
+    *(uint16_t *)buf_append(buf,2)=htons(len);
+    memcpy(buf_append(buf,len),s,len);
+}
+
+void buffer_new(struct buffer_if *buf, uint32_t len)
+{
+    buf->free=True;
+    buf->owner=NULL;
+    buf->flags=0;
+    buf->loc.file=NULL;
+    buf->loc.line=0;
+    buf->size=0;
+    buf->len=len;
+    buf->start=NULL;
+    buf->base=safe_malloc(len,"buffer_new");
+}
+
+static list_t *buffer_apply(closure_t *self, struct cloc loc, dict_t *context,
+                           list_t *args)
+{
+    struct buffer *st;
+    item_t *item;
+    dict_t *dict;
+    bool_t lockdown=False;
+    
+    st=safe_malloc(sizeof(*st),"buffer_apply");
+    st->cl.description="buffer";
+    st->cl.type=CL_BUFFER;
+    st->cl.apply=NULL;
+    st->cl.interface=&st->ops;
+    st->ops.free=True;
+    st->ops.owner=NULL;
+    st->ops.flags=0;
+    st->ops.loc=loc;
+    st->ops.size=0;
+    st->ops.len=DEFAULT_BUFFER_SIZE;
+    st->ops.start=NULL;
+
+    /* First argument, if present, is buffer length */
+    item=list_elem(args,0);
+    if (item) {
+       if (item->type!=t_number) {
+           cfgfatal(st->ops.loc,"buffer","first parameter must be a "
+                    "number (buffer size)\n");
+       }
+       st->ops.len=item->data.number;
+       if (st->ops.len<MIN_BUFFER_SIZE) {
+           cfgfatal(st->ops.loc,"buffer","ludicrously small buffer size\n");
+       }
+       if (st->ops.len>MAX_BUFFER_SIZE) {
+           cfgfatal(st->ops.loc,"buffer","ludicrously large buffer size\n");
+       }
+    }
+    /* Second argument, if present, is a dictionary */
+    item=list_elem(args,1);
+    if (item) {
+       if (item->type!=t_dict) {
+           cfgfatal(st->ops.loc,"buffer","second parameter must be a "
+                    "dictionary\n");
+       }
+       dict=item->data.dict;
+       lockdown=dict_read_bool(dict,"lockdown",False,"buffer",st->ops.loc,
+                               False);
+    }
+
+    st->ops.base=safe_malloc(st->ops.len,"buffer");
+    if (lockdown) {
+       Message(M_WARNING,"buffer: XXX lockdown\n");
+    }
+    
+    return new_closure(&st->cl);
+}
+
+init_module util_module;
+void util_module(dict_t *dict)
+{
+    add_closure(dict,"logfile",logfile_apply);
+    add_closure(dict,"sysbuffer",buffer_apply);
+}
diff --git a/util.h b/util.h
new file mode 100644 (file)
index 0000000..a43cb08
--- /dev/null
+++ b/util.h
@@ -0,0 +1,53 @@
+/* $Log: util.h,v $
+ * Revision 1.1  1996/03/14 17:05:12  sde1000
+ * Initial revision
+ *
+ */
+
+#ifndef util_h
+#define util_h
+
+#include <stdlib.h>
+#include <stdint.h>
+#include "secnet.h"
+#include "config.h"
+#include <gmp.h>
+
+extern uint32_t message_level;
+extern uint32_t syslog_level;
+
+#define BUF_ASSERT_FREE(buf) do { buffer_assert_free((buf), \
+                                                    __FILE__,__LINE__); } \
+while(0)
+#define BUF_ASSERT_USED(buf) do { buffer_assert_used((buf), \
+                                                    __FILE__,__LINE__); } \
+while(0)
+#define BUF_ALLOC(buf,own) do { buffer_assert_free((buf),__FILE__,__LINE__); \
+        (buf)->free=False; (buf)->owner=(own); (buf)->start=(buf)->base; \
+        (buf)->size=0; } while(0)
+#define BUF_FREE(buf) do { (buf)->free=True; } while(0)
+
+extern void buffer_assert_free(struct buffer_if *buffer, string_t file,
+                              uint32_t line);
+extern void buffer_assert_used(struct buffer_if *buffer, string_t file,
+                              uint32_t line);
+extern void buffer_new(struct buffer_if *buffer, uint32_t len);
+extern void buffer_init(struct buffer_if *buffer, uint32_t max_start_pad);
+extern void *buf_append(struct buffer_if *buf, uint32_t amount);
+extern void *buf_prepend(struct buffer_if *buf, uint32_t amount);
+extern void *buf_unappend(struct buffer_if *buf, uint32_t amount);
+extern void *buf_unprepend(struct buffer_if *buf, uint32_t amount);
+
+extern void buf_append_string(struct buffer_if *buf, string_t s);
+
+extern void enter_phase(uint32_t new_phase);
+
+extern void read_mpbin(MP_INT *a, uint8_t *bin, int binsize);
+
+extern char *write_mpstring(MP_INT *a);
+
+extern uint32_t write_mpbin(MP_INT *a, uint8_t *buffer, uint32_t buflen);
+
+extern struct log_if *init_log(list_t *loglist);
+
+#endif /* util_h */