Multilink support.
authorPaul Mackerras <paulus@samba.org>
Tue, 4 Apr 2000 07:06:53 +0000 (07:06 +0000)
committerPaul Mackerras <paulus@samba.org>
Tue, 4 Apr 2000 07:06:53 +0000 (07:06 +0000)
We now put details of each connection in a tdb database.
At present the values we put in the database are just
the same as the environment strings.

14 files changed:
pppd/Makefile.linux
pppd/auth.c
pppd/ipcp.c
pppd/ipv6cp.c
pppd/lcp.c
pppd/lcp.h
pppd/main.c
pppd/options.c
pppd/patchlevel.h
pppd/pathnames.h
pppd/pppd.h
pppd/sys-linux.c
pppd/tdb.c [new file with mode: 0644]
pppd/tdb.h [new file with mode: 0644]

index f36d841686276b3fb9c4589d4403c3df23e84f50..1a0662c1959e058b94f97edb64e89cb4f333d785 100644 (file)
@@ -1,6 +1,6 @@
 #
 # pppd makefile for Linux
-# $Id: Makefile.linux,v 1.36 2000/03/27 06:02:59 paulus Exp $
+# $Id: Makefile.linux,v 1.37 2000/04/04 07:06:48 paulus Exp $
 #
 
 # Default installation locations
@@ -9,12 +9,13 @@ MANDIR = /usr/man
 
 PPPDSRCS = main.c magic.c fsm.c lcp.c ipcp.c upap.c chap.c md5.c ccp.c \
           ipxcp.c auth.c options.c sys-linux.c md4.c chap_ms.c cbcp.c \
-          demand.c utils.c
+          demand.c utils.c multilink.c tdb.c
 HEADERS =  callout.h pathnames.h patchlevel.h chap.h md5.h chap_ms.h md4.h \
-          ipxcp.h cbcp.h
+          ipxcp.h cbcp.h tdb.h
 MANPAGES = pppd.8
 PPPDOBJS = main.o magic.o fsm.o lcp.o ipcp.o upap.o chap.o md5.o ccp.o \
-          auth.o options.o demand.o utils.o sys-linux.o ipxcp.o
+          auth.o options.o demand.o utils.o sys-linux.o ipxcp.o multilink.o \
+          tdb.o
 
 all: pppd
 
@@ -50,7 +51,7 @@ PLUGIN=y
 
 INCLUDE_DIRS= -I../include
 
-COMPILE_FLAGS= -D_linux_=1 -DHAVE_PATHS_H -DIPX_CHANGE -DHAVE_MULTILINK
+COMPILE_FLAGS= -D_linux_=1 -DHAVE_PATHS_H -DIPX_CHANGE -DHAVE_MULTILINK -DHAVE_MMAP
 
 CFLAGS= $(COPTS) $(COMPILE_FLAGS) $(INCLUDE_DIRS)
 
index 3d466e2e52fa097f95b72dac09ab50d4d71a9ea1..e97b48eaf439e730eed5ce80383f90e73ed282c5 100644 (file)
@@ -32,7 +32,7 @@
  * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
  */
 
-#define RCSID  "$Id: auth.c,v 1.62 2000/03/27 06:02:59 paulus Exp $"
+#define RCSID  "$Id: auth.c,v 1.63 2000/04/04 07:06:49 paulus Exp $"
 
 #include <stdio.h>
 #include <stddef.h>
@@ -510,6 +510,12 @@ start_networks()
 
     new_phase(PHASE_NETWORK);
 
+#ifdef HAVE_MULTILINK
+    if (multilink)
+       if (mp_join_bundle())
+           return;
+#endif /* HAVE_MULTILINK */
+
 #if 0
     if (!demand)
        set_filters(&pass_filter, &active_filter);
@@ -571,7 +577,7 @@ auth_peer_success(unit, protocol, name, namelen)
        namelen = sizeof(peer_authname) - 1;
     BCOPY(name, peer_authname, namelen);
     peer_authname[namelen] = 0;
-    script_setenv("PEERNAME", peer_authname);
+    script_setenv("PEERNAME", peer_authname, 0);
 
     /*
      * If there is no more authentication still to be done,
index b5749ac68363846dfd4fb3347c77536ad05bc9c4..829b2c97b436a6a6801669bc7080f10a76c38301 100644 (file)
@@ -17,7 +17,7 @@
  * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
  */
 
-#define RCSID  "$Id: ipcp.c,v 1.52 1999/12/23 01:25:33 paulus Exp $"
+#define RCSID  "$Id: ipcp.c,v 1.53 2000/04/04 07:06:49 paulus Exp $"
 
 /*
  * TODO:
@@ -1389,15 +1389,15 @@ ipcp_up(f)
        ipcp_close(f->unit, "Could not determine local IP address");
        return;
     }
-    script_setenv("IPLOCAL", ip_ntoa(go->ouraddr));
-    script_setenv("IPREMOTE", ip_ntoa(ho->hisaddr));
+    script_setenv("IPLOCAL", ip_ntoa(go->ouraddr), 0);
+    script_setenv("IPREMOTE", ip_ntoa(ho->hisaddr), 1);
 
     if (usepeerdns && (go->dnsaddr[0] || go->dnsaddr[1])) {
-       script_setenv("USEPEERDNS", "1");
+       script_setenv("USEPEERDNS", "1", 0);
        if (go->dnsaddr[0])
-           script_setenv("DNS1", ip_ntoa(go->dnsaddr[0]));
+           script_setenv("DNS1", ip_ntoa(go->dnsaddr[0]), 0);
        if (go->dnsaddr[1])
-           script_setenv("DNS2", ip_ntoa(go->dnsaddr[1]));
+           script_setenv("DNS2", ip_ntoa(go->dnsaddr[1]), 0);
        create_resolv(go->dnsaddr[0], go->dnsaddr[1]);
     }
 
@@ -1423,13 +1423,13 @@ ipcp_up(f)
            ipcp_clear_addrs(f->unit, wo->ouraddr, wo->hisaddr);
            if (go->ouraddr != wo->ouraddr) {
                warn("Local IP address changed to %I", go->ouraddr);
-               script_setenv("OLDIPLOCAL", ip_ntoa(wo->ouraddr));
+               script_setenv("OLDIPLOCAL", ip_ntoa(wo->ouraddr), 0);
                wo->ouraddr = go->ouraddr;
            } else
                script_unsetenv("OLDIPLOCAL");
            if (ho->hisaddr != wo->hisaddr) {
                warn("Remote IP address changed to %I", ho->hisaddr);
-               script_setenv("OLDIPREMOTE", ip_ntoa(wo->hisaddr));
+               script_setenv("OLDIPREMOTE", ip_ntoa(wo->hisaddr), 0);
                wo->hisaddr = ho->hisaddr;
            } else
                script_unsetenv("OLDIPREMOTE");
index 7bf5e5a3943203f451aa78aaa6e160707bf1782b..98cae543fe29eb9d88658bb6c2ff12b9eee3c3e7 100644 (file)
  * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
  * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
  *
- * $Id: ipv6cp.c,v 1.7 1999/10/08 01:08:18 masputra Exp $ 
+ * $Id: ipv6cp.c,v 1.8 2000/04/04 07:06:50 paulus Exp $ 
  */
 
-#define RCSID  "$Id: ipv6cp.c,v 1.7 1999/10/08 01:08:18 masputra Exp $"
+#define RCSID  "$Id: ipv6cp.c,v 1.8 2000/04/04 07:06:50 paulus Exp $"
 
 /*
  * TODO: 
@@ -1120,8 +1120,8 @@ ipv6cp_up(f)
            return;
        }
     }
-    script_setenv("LLLOCAL", llv6_ntoa(go->ourid));
-    script_setenv("LLREMOTE", llv6_ntoa(ho->hisid));
+    script_setenv("LLLOCAL", llv6_ntoa(go->ourid), 0);
+    script_setenv("LLREMOTE", llv6_ntoa(ho->hisid), 0);
 
 #ifdef IPV6CP_COMP
     /* set tcp compression */
index 231b7e4552861ea828cb361e9923bccf83c061d3..3092b5ada9f2a0a0939d3a52380950a204d125a5 100644 (file)
@@ -17,7 +17,7 @@
  * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
  */
 
-#define RCSID  "$Id: lcp.c,v 1.48 2000/03/27 06:02:59 paulus Exp $";
+#define RCSID  "$Id: lcp.c,v 1.49 2000/04/04 07:06:50 paulus Exp $";
 
 /*
  * TODO:
@@ -44,6 +44,11 @@ bool lax_recv = 0;           /* accept control chars in asyncmap */
 
 static int setescape __P((char **));
 
+#ifdef HAVE_MULTILINK
+bool   noendpoint = 0;         /* don't send/accept endpoint discriminator */
+static int setendpoint __P((char **));
+#endif /* HAVE_MULTILINK */
+
 static option_t lcp_option_list[] = {
     /* LCP options */
     { "noaccomp", o_bool, &lcp_wantoptions[0].neg_accompression,
@@ -108,12 +113,19 @@ static option_t lcp_option_list[] = {
     { "receive-all", o_bool, &lax_recv,
       "Accept all received control characters", 1 },
 #ifdef HAVE_MULTILINK
+    { "mrru", o_int, &lcp_wantoptions[0].mrru,
+      "Maximum received packet size for multilink bundle",
+      0, &lcp_wantoptions[0].neg_mrru },
     { "mpshortseq", o_bool, &lcp_wantoptions[0].neg_ssnhf,
       "Use short sequence numbers in multilink headers",
       OPT_A2COPY, &lcp_allowoptions[0].neg_ssnhf },
     { "nompshortseq", o_bool, &lcp_wantoptions[0].neg_ssnhf,
       "Don't use short sequence numbers in multilink headers",
       OPT_A2COPY, &lcp_allowoptions[0].neg_ssnhf },
+    { "endpoint", o_special, setendpoint,
+      "Endpoint discriminator for multilink" },
+    { "noendpoint", o_bool, &noendpoint,
+      "Don't send or accept multilink endpoint discriminator", 1 },
 #endif /* HAVE_MULTILINK */
     {NULL}
 };
@@ -258,6 +270,20 @@ setescape(argv)
     return ret;
 }
 
+#ifdef HAVE_MULTILINK
+static int
+setendpoint(argv)
+    char **argv;
+{
+    if (str_to_epdisc(&lcp_wantoptions[0].endpoint, *argv)) {
+       lcp_wantoptions[0].neg_endpoint = 1;
+       return 1;
+    }
+    option_error("Can't parse '%s' as an endpoint discriminator", *argv);
+    return 0;
+}
+#endif /* HAVE_MULTILINK */
+
 /*
  * lcp_init - Initialize LCP.
  */
@@ -275,41 +301,33 @@ lcp_init(unit)
 
     fsm_init(f);
 
-    wo->passive = 0;
-    wo->silent = 0;
-    wo->restart = 0;                   /* Set to 1 in kernels or multi-line
-                                          implementations */
+    BZERO(wo, sizeof(*wo));
     wo->neg_mru = 1;
     wo->mru = DEFMRU;
     wo->neg_asyncmap = 1;
-    wo->asyncmap = 0;
-    wo->neg_chap = 0;                  /* Set to 1 on server */
-    wo->neg_upap = 0;                  /* Set to 1 on server */
     wo->chap_mdtype = CHAP_DIGEST_MD5;
     wo->neg_magicnumber = 1;
     wo->neg_pcompression = 1;
     wo->neg_accompression = 1;
-    wo->neg_lqr = 0;                   /* no LQR implementation yet */
-    wo->neg_cbcp = 0;
 
+    BZERO(ao, sizeof(*ao));
     ao->neg_mru = 1;
     ao->mru = MAXMRU;
     ao->neg_asyncmap = 1;
-    ao->asyncmap = 0;
     ao->neg_chap = 1;
     ao->chap_mdtype = CHAP_DIGEST_MD5;
     ao->neg_upap = 1;
     ao->neg_magicnumber = 1;
     ao->neg_pcompression = 1;
     ao->neg_accompression = 1;
-    ao->neg_lqr = 0;                   /* no LQR implementation yet */
 #ifdef CBCP_SUPPORT
     ao->neg_cbcp = 1;
-#else
-    ao->neg_cbcp = 0;
+#endif
+#ifdef HAVE_MULTILINK
+    ao->neg_endpoint = 1;
 #endif
 
-    memset(xmit_accm[unit], 0, sizeof(xmit_accm[0]));
+    BZERO(xmit_accm[unit], sizeof(xmit_accm[0]));
     xmit_accm[unit][3] = 0x60000000;
 }
 
@@ -539,12 +557,16 @@ lcp_resetci(f)
     fsm *f;
 {
     lcp_options *wo = &lcp_wantoptions[f->unit];
+    lcp_options *go = &lcp_gotoptions[f->unit];
 
     wo->magicnumber = magic();
     wo->numloops = 0;
-    if (!wo->neg_multilink)
-       wo->neg_ssnhf = 0;
-    lcp_gotoptions[f->unit] = *wo;
+    *go = *wo;
+    if (!multilink) {
+       go->neg_mrru = 0;
+       go->neg_ssnhf = 0;
+       go->neg_endpoint = 0;
+    }
     peer_mru[f->unit] = PPP_MRU;
     auth_reset(f->unit);
 }
@@ -578,9 +600,9 @@ lcp_cilen(f)
            LENCILONG(go->neg_magicnumber) +
            LENCIVOID(go->neg_pcompression) +
            LENCIVOID(go->neg_accompression) +
-           LENCISHORT(go->neg_multilink) +
+           LENCISHORT(go->neg_mrru) +
            LENCIVOID(go->neg_ssnhf) +
-           (go->neg_endpoint? CILEN_CHAR + go->endp_len: 0));
+           (go->neg_endpoint? CILEN_CHAR + go->endpoint.length: 0));
 }
 
 
@@ -653,10 +675,10 @@ lcp_addci(f, ucp, lenp)
     ADDCILONG(CI_MAGICNUMBER, go->neg_magicnumber, go->magicnumber);
     ADDCIVOID(CI_PCOMPRESSION, go->neg_pcompression);
     ADDCIVOID(CI_ACCOMPRESSION, go->neg_accompression);
-    ADDCISHORT(CI_MRRU, go->neg_multilink, go->mrru);
+    ADDCISHORT(CI_MRRU, go->neg_mrru, go->mrru);
     ADDCIVOID(CI_SSNHF, go->neg_ssnhf);
-    ADDCIENDP(CI_EPDISC, go->neg_endpoint, go->endp_class, go->endpoint,
-             go->endp_len);
+    ADDCIENDP(CI_EPDISC, go->neg_endpoint, go->endpoint.class,
+             go->endpoint.value, go->endpoint.length);
 
     if (ucp - start_ucp != *lenp) {
        /* this should never happen, because peer_mtu should be 1500 */
@@ -800,10 +822,10 @@ lcp_ackci(f, p, len)
     ACKCILONG(CI_MAGICNUMBER, go->neg_magicnumber, go->magicnumber);
     ACKCIVOID(CI_PCOMPRESSION, go->neg_pcompression);
     ACKCIVOID(CI_ACCOMPRESSION, go->neg_accompression);
-    ACKCISHORT(CI_MRRU, go->neg_multilink, go->mrru);
+    ACKCISHORT(CI_MRRU, go->neg_mrru, go->mrru);
     ACKCIVOID(CI_SSNHF, go->neg_ssnhf);
-    ACKCIENDP(CI_EPDISC, go->neg_endpoint, go->endp_class, go->endpoint,
-             go->endp_len);
+    ACKCIENDP(CI_EPDISC, go->neg_endpoint, go->endpoint.class,
+             go->endpoint.value, go->endpoint.length);
 
     /*
      * If there are any remaining CIs, then this packet is bad.
@@ -1052,8 +1074,8 @@ lcp_nakci(f, p, len)
      * Nak for MRRU option - accept their value if it is smaller
      * than the one we want.
      */
-    if (go->neg_multilink) {
-       NAKCISHORT(CI_MRRU, neg_multilink,
+    if (go->neg_mrru) {
+       NAKCISHORT(CI_MRRU, neg_mrru,
                   if (cishort <= wo->mrru)
                       try.mrru = cishort;
                   );
@@ -1134,7 +1156,7 @@ lcp_nakci(f, p, len)
                goto bad;
            break;
        case CI_MRRU:
-           if (go->neg_multilink || no.neg_multilink || cilen != CILEN_SHORT)
+           if (go->neg_mrru || no.neg_mrru || cilen != CILEN_SHORT)
                goto bad;
            break;
        case CI_SSNHF:
@@ -1286,7 +1308,7 @@ lcp_rejci(f, p, len)
        p[1] == CILEN_CHAR + vlen) { \
        int i; \
        len -= CILEN_CHAR + vlen; \
-       INCPTR(p[1], p); \
+       INCPTR(2, p); \
        GETCHAR(cichar, p); \
        if (cichar != class) \
            goto bad; \
@@ -1309,10 +1331,10 @@ lcp_rejci(f, p, len)
     REJCILONG(CI_MAGICNUMBER, neg_magicnumber, go->magicnumber);
     REJCIVOID(CI_PCOMPRESSION, neg_pcompression);
     REJCIVOID(CI_ACCOMPRESSION, neg_accompression);
-    REJCISHORT(CI_MRRU, neg_multilink, go->mrru);
+    REJCISHORT(CI_MRRU, neg_mrru, go->mrru);
     REJCIVOID(CI_SSNHF, neg_ssnhf);
-    REJCIENDP(CI_EPDISC, neg_endpoint, go->endp_class, go->endpoint,
-             go->endp_len);
+    REJCIENDP(CI_EPDISC, neg_endpoint, go->endpoint.class,
+             go->endpoint.value, go->endpoint.length);
 
     /*
      * If there are any remaining CIs, then this packet is bad.
@@ -1596,7 +1618,7 @@ lcp_reqci(f, inp, lenp, reject_if_disagree)
            break;
 
        case CI_MRRU:
-           if (!ao->neg_multilink ||
+           if (!ao->neg_mrru || !multilink ||
                cilen != CILEN_SHORT) {
                orc = CONFREJ;
                break;
@@ -1604,12 +1626,12 @@ lcp_reqci(f, inp, lenp, reject_if_disagree)
 
            GETSHORT(cishort, p);
            /* possibly should insist on a minimum/maximum MRRU here */
-           ho->neg_multilink = 1;
+           ho->neg_mrru = 1;
            ho->mrru = cishort;
            break;
 
        case CI_SSNHF:
-           if (!ao->neg_ssnhf ||
+           if (!ao->neg_ssnhf || !multilink ||
                cilen != CILEN_VOID) {
                orc = CONFREJ;
                break;
@@ -1618,7 +1640,7 @@ lcp_reqci(f, inp, lenp, reject_if_disagree)
            break;
 
        case CI_EPDISC:
-           if (!ao->neg_endpoint ||
+           if (!ao->neg_endpoint || !multilink ||
                cilen < CILEN_CHAR ||
                cilen > CILEN_CHAR + MAX_ENDP_LEN) {
                orc = CONFREJ;
@@ -1627,9 +1649,9 @@ lcp_reqci(f, inp, lenp, reject_if_disagree)
            GETCHAR(cichar, p);
            cilen -= CILEN_CHAR;
            ho->neg_endpoint = 1;
-           ho->endp_class = cichar;
-           ho->endp_len = cilen;
-           BCOPY(p, ho->endpoint, cilen);
+           ho->endpoint.class = cichar;
+           ho->endpoint.length = cilen;
+           BCOPY(p, ho->endpoint.value, cilen);
            INCPTR(cilen, p);
            break;
 
@@ -1783,10 +1805,6 @@ static char *lcp_codenames[] = {
     "EchoReq", "EchoRep", "DiscReq"
 };
 
-static char *endp_class_names[] = {
-    "null", "local", "IP", "MAC", "magic", "phone"
-};
-
 static int
 lcp_printpkt(p, plen, printer, arg)
     u_char *p;
@@ -1796,7 +1814,6 @@ lcp_printpkt(p, plen, printer, arg)
 {
     int code, id, len, olen, i;
     u_char *pstart, *optend;
-    u_char cichar;
     u_short cishort;
     u_int32_t cilong;
 
@@ -1939,13 +1956,17 @@ lcp_printpkt(p, plen, printer, arg)
                break;
            case CI_EPDISC:
                if (olen >= CILEN_CHAR) {
+                   struct epdisc epd;
                    p += 2;
-                   GETCHAR(cichar, p);
-                   if (cichar <= 5)
-                       printer(arg, "endpoint [%s]:",
-                               endp_class_names[cichar]);
-                   else
-                       printer(arg, "endpoint [%d]:");
+                   GETCHAR(epd.class, p);
+                   epd.length = olen - CILEN_CHAR;
+                   if (epd.length > MAX_ENDP_LEN)
+                       epd.length = MAX_ENDP_LEN;
+                   if (epd.length > 0) {
+                       BCOPY(p, epd.value, epd.length);
+                       p += epd.length;
+                   }
+                   printer(arg, "endpoint [%s]", epdisc_to_str(&epd));
                }
                break;
            }
index f2e7c983e80a0ed8cc1d7b21dde9bbf283f483c5..e52bdf571bae51e7939b6c90b38538ecea653e71 100644 (file)
@@ -16,7 +16,7 @@
  * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
  * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
  *
- * $Id: lcp.h,v 1.14 2000/03/27 06:03:00 paulus Exp $
+ * $Id: lcp.h,v 1.15 2000/04/04 07:06:51 paulus Exp $
  */
 
 /*
@@ -43,9 +43,6 @@
 #define DISCREQ                11      /* Discard Request */
 #define CBCP_OPT       6       /* Use callback control protocol */
 
-/* maximum length of endpoint discriminator value */
-#define MAX_ENDP_LEN   20
-
 /*
  * The state of options is described by an lcp_options structure.
  */
@@ -62,7 +59,7 @@ typedef struct lcp_options {
     bool neg_accompression;    /* HDLC Address/Control Field Compression? */
     bool neg_lqr;              /* Negotiate use of Link Quality Reports */
     bool neg_cbcp;             /* Negotiate use of CBCP */
-    bool neg_multilink;                /* negotiate multilink (MRRU) */
+    bool neg_mrru;             /* negotiate multilink MRRU */
     bool neg_ssnhf;            /* negotiate short sequence numbers */
     bool neg_endpoint;         /* negotiate endpoint discriminator */
     int  mru;                  /* Value of MRU */
@@ -72,9 +69,7 @@ typedef struct lcp_options {
     u_int32_t magicnumber;
     int  numloops;             /* Number of loops during magic number neg. */
     u_int32_t lqr_period;      /* Reporting period for LQR 1/100ths second */
-    int  endp_class;           /* endpoint discriminator class */
-    int  endp_len;             /* endpoint discriminator length */
-    u_char endpoint[MAX_ENDP_LEN];     /* endpoint discriminator value */
+    struct epdisc endpoint;    /* endpoint discriminator */
 } lcp_options;
 
 extern fsm lcp_fsm[];
index 982ab55c4e4198ab401793a79f47ff72337e151e..5b57e99a00c6fbda3b66e7df2c5805d5b984d921 100644 (file)
@@ -17,7 +17,7 @@
  * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
  */
 
-#define RCSID  "$Id: main.c,v 1.91 2000/03/27 06:03:01 paulus Exp $"
+#define RCSID  "$Id: main.c,v 1.92 2000/04/04 07:06:51 paulus Exp $"
 
 #include <stdio.h>
 #include <ctype.h>
@@ -55,6 +55,7 @@
 #include "ccp.h"
 #include "pathnames.h"
 #include "patchlevel.h"
+#include "tdb.h"
 
 #ifdef CBCP_SUPPORT
 #include "cbcp.h"
@@ -96,6 +97,8 @@ int unsuccess;                        /* # unsuccessful connection attempts */
 int do_callback;               /* != 0 if we should do callback next */
 int doing_callback;            /* != 0 if we are doing callback */
 char *callback_script;         /* script for doing callback */
+TDB_CONTEXT *pppdb;            /* database for storing status etc. */
+char db_key[32];
 
 int (*holdoff_hook) __P((void)) = NULL;
 int (*new_phase_hook) __P((int)) = NULL;
@@ -154,6 +157,7 @@ static struct subprocess *children;
 
 /* Prototypes for procedures local to this file. */
 
+static void setup_signals __P((void));
 static void create_pidfile __P((void));
 static void create_linkpidfile __P((void));
 static void cleanup __P((void));
@@ -172,6 +176,9 @@ static void holdoff_end __P((void *));
 static int device_script __P((char *, int, int, int));
 static int reap_kids __P((int waitfor));
 static void record_child __P((int, char *, void (*) (void *), void *));
+static void update_db_entry __P((void));
+static void add_db_key __P((const char *));
+static void delete_db_key __P((const char *));
 static int open_socket __P((char *));
 static int start_charshunt __P((int, int));
 static void charshunt_done __P((void *));
@@ -224,7 +231,6 @@ main(argc, argv)
     char *argv[];
 {
     int i, fdflags, t;
-    struct sigaction sa;
     char *p, *connector;
     struct passwd *pw;
     struct timeval timo;
@@ -264,7 +270,7 @@ main(argc, argv)
     uid = getuid();
     privileged = uid == 0;
     slprintf(numbuf, sizeof(numbuf), "%d", uid);
-    script_setenv("ORIG_UID", numbuf);
+    script_setenv("ORIG_UID", numbuf, 0);
 
     ngroups = getgroups(NGROUPS_MAX, groups);
 
@@ -344,6 +350,9 @@ main(argc, argv)
     if (!sys_check_options())
        exit(EXIT_OPTION_ERROR);
     auth_check_options();
+#ifdef HAVE_MULTILINK
+    mp_check_options();
+#endif
     for (i = 0; (protp = protocols[i]) != NULL; ++i)
        if (protp->check_options != NULL)
            (*protp->check_options)();
@@ -400,8 +409,6 @@ main(argc, argv)
        && S_ISCHR(statbuf.st_mode) && statbuf.st_rdev == devstat.st_rdev)
        log_to_fd = -1;
 
-    script_setenv("DEVICE", devnam);
-
     /*
      * Initialize system-dependent stuff.
      */
@@ -409,6 +416,18 @@ main(argc, argv)
     if (debug)
        setlogmask(LOG_UPTO(LOG_DEBUG));
 
+    pppdb = tdb_open(_PATH_PPPDB, 0, TDB_CLEAR_IF_FIRST, O_RDWR|O_CREAT, 0644);
+    if (pppdb != NULL) {
+       slprintf(db_key, sizeof(db_key), "pppd%d", getpid());
+       update_db_entry();
+    } else {
+       warn("Warning: couldn't open ppp database %s", _PATH_PPPDB);
+       if (multilink) {
+           warn("Warning: disabling multilink");
+           multilink = 0;
+       }
+    }
+
     /*
      * Detach ourselves from the terminal, if required,
      * and identify who is running us.
@@ -425,81 +444,12 @@ main(argc, argv)
     }
     syslog(LOG_NOTICE, "pppd %s.%d%s started by %s, uid %d",
           VERSION, PATCHLEVEL, IMPLEMENTATION, p, uid);
-    script_setenv("PPPLOGNAME", p);
+    script_setenv("PPPLOGNAME", p, 0);
 
-    /*
-     * Compute mask of all interesting signals and install signal handlers
-     * for each.  Only one signal handler may be active at a time.  Therefore,
-     * all other signals should be masked when any handler is executing.
-     */
-    sigemptyset(&mask);
-    sigaddset(&mask, SIGHUP);
-    sigaddset(&mask, SIGINT);
-    sigaddset(&mask, SIGTERM);
-    sigaddset(&mask, SIGCHLD);
-    sigaddset(&mask, SIGUSR2);
+    if (devnam[0])
+       script_setenv("DEVICE", devnam, 1);
 
-#define SIGNAL(s, handler)     do { \
-       sa.sa_handler = handler; \
-       if (sigaction(s, &sa, NULL) < 0) \
-           fatal("Couldn't establish signal handler (%d): %m", s); \
-    } while (0)
-
-    sa.sa_mask = mask;
-    sa.sa_flags = 0;
-    SIGNAL(SIGHUP, hup);               /* Hangup */
-    SIGNAL(SIGINT, term);              /* Interrupt */
-    SIGNAL(SIGTERM, term);             /* Terminate */
-    SIGNAL(SIGCHLD, chld);
-
-    SIGNAL(SIGUSR1, toggle_debug);     /* Toggle debug flag */
-    SIGNAL(SIGUSR2, open_ccp);         /* Reopen CCP */
-
-    /*
-     * Install a handler for other signals which would otherwise
-     * cause pppd to exit without cleaning up.
-     */
-    SIGNAL(SIGABRT, bad_signal);
-    SIGNAL(SIGALRM, bad_signal);
-    SIGNAL(SIGFPE, bad_signal);
-    SIGNAL(SIGILL, bad_signal);
-    SIGNAL(SIGPIPE, bad_signal);
-    SIGNAL(SIGQUIT, bad_signal);
-    SIGNAL(SIGSEGV, bad_signal);
-#ifdef SIGBUS
-    SIGNAL(SIGBUS, bad_signal);
-#endif
-#ifdef SIGEMT
-    SIGNAL(SIGEMT, bad_signal);
-#endif
-#ifdef SIGPOLL
-    SIGNAL(SIGPOLL, bad_signal);
-#endif
-#ifdef SIGPROF
-    SIGNAL(SIGPROF, bad_signal);
-#endif
-#ifdef SIGSYS
-    SIGNAL(SIGSYS, bad_signal);
-#endif
-#ifdef SIGTRAP
-    SIGNAL(SIGTRAP, bad_signal);
-#endif
-#ifdef SIGVTALRM
-    SIGNAL(SIGVTALRM, bad_signal);
-#endif
-#ifdef SIGXCPU
-    SIGNAL(SIGXCPU, bad_signal);
-#endif
-#ifdef SIGXFSZ
-    SIGNAL(SIGXFSZ, bad_signal);
-#endif
-
-    /*
-     * Apparently we can get a SIGPIPE when we call syslog, if
-     * syslogd has died and been restarted.  Ignoring it seems
-     * be sufficient.
-     */
-    signal(SIGPIPE, SIG_IGN);
+    setup_signals();
 
     waiting = 0;
 
@@ -513,12 +463,7 @@ main(argc, argv)
         * Open the loopback channel and set it up to be the ppp interface.
         */
        fd_loop = open_ppp_loopback();
-
-       syslog(LOG_INFO, "Using interface ppp%d", ifunit);
-       slprintf(ifname, sizeof(ifname), "ppp%d", ifunit);
-       script_setenv("IFNAME", ifname);
-
-       create_pidfile();       /* write pid to file */
+       set_ifunit(1);
 
        /*
         * Configure the interface and mark it up, etc.
@@ -766,7 +711,7 @@ main(argc, argv)
        }
 
        slprintf(numbuf, sizeof(numbuf), "%d", baud_rate);
-       script_setenv("SPEED", numbuf);
+       script_setenv("SPEED", numbuf, 0);
 
        /* run welcome script, if any */
        if (welcomer && welcomer[0]) {
@@ -781,14 +726,8 @@ main(argc, argv)
            goto disconnect;
        }
 
-       if (!demand && ifunit >= 0) {
-           
-           info("Using interface ppp%d", ifunit);
-           slprintf(ifname, sizeof(ifname), "ppp%d", ifunit);
-           script_setenv("IFNAME", ifname);
-
-           create_pidfile();   /* write pid to file */
-       }
+       if (!demand && ifunit >= 0)
+           set_ifunit(1);
 
        /*
         * Start opening the connection and wait for
@@ -973,6 +912,105 @@ main(argc, argv)
     return 0;
 }
 
+/*
+ * setup_signals - initialize signal handling.
+ */
+static void
+setup_signals()
+{
+    struct sigaction sa;
+    sigset_t mask;
+
+    /*
+     * Compute mask of all interesting signals and install signal handlers
+     * for each.  Only one signal handler may be active at a time.  Therefore,
+     * all other signals should be masked when any handler is executing.
+     */
+    sigemptyset(&mask);
+    sigaddset(&mask, SIGHUP);
+    sigaddset(&mask, SIGINT);
+    sigaddset(&mask, SIGTERM);
+    sigaddset(&mask, SIGCHLD);
+    sigaddset(&mask, SIGUSR2);
+
+#define SIGNAL(s, handler)     do { \
+       sa.sa_handler = handler; \
+       if (sigaction(s, &sa, NULL) < 0) \
+           fatal("Couldn't establish signal handler (%d): %m", s); \
+    } while (0)
+
+    sa.sa_mask = mask;
+    sa.sa_flags = 0;
+    SIGNAL(SIGHUP, hup);               /* Hangup */
+    SIGNAL(SIGINT, term);              /* Interrupt */
+    SIGNAL(SIGTERM, term);             /* Terminate */
+    SIGNAL(SIGCHLD, chld);
+
+    SIGNAL(SIGUSR1, toggle_debug);     /* Toggle debug flag */
+    SIGNAL(SIGUSR2, open_ccp);         /* Reopen CCP */
+
+    /*
+     * Install a handler for other signals which would otherwise
+     * cause pppd to exit without cleaning up.
+     */
+    SIGNAL(SIGABRT, bad_signal);
+    SIGNAL(SIGALRM, bad_signal);
+    SIGNAL(SIGFPE, bad_signal);
+    SIGNAL(SIGILL, bad_signal);
+    SIGNAL(SIGPIPE, bad_signal);
+    SIGNAL(SIGQUIT, bad_signal);
+    SIGNAL(SIGSEGV, bad_signal);
+#ifdef SIGBUS
+    SIGNAL(SIGBUS, bad_signal);
+#endif
+#ifdef SIGEMT
+    SIGNAL(SIGEMT, bad_signal);
+#endif
+#ifdef SIGPOLL
+    SIGNAL(SIGPOLL, bad_signal);
+#endif
+#ifdef SIGPROF
+    SIGNAL(SIGPROF, bad_signal);
+#endif
+#ifdef SIGSYS
+    SIGNAL(SIGSYS, bad_signal);
+#endif
+#ifdef SIGTRAP
+    SIGNAL(SIGTRAP, bad_signal);
+#endif
+#ifdef SIGVTALRM
+    SIGNAL(SIGVTALRM, bad_signal);
+#endif
+#ifdef SIGXCPU
+    SIGNAL(SIGXCPU, bad_signal);
+#endif
+#ifdef SIGXFSZ
+    SIGNAL(SIGXFSZ, bad_signal);
+#endif
+
+    /*
+     * Apparently we can get a SIGPIPE when we call syslog, if
+     * syslogd has died and been restarted.  Ignoring it seems
+     * be sufficient.
+     */
+    signal(SIGPIPE, SIG_IGN);
+}
+
+/*
+ * set_ifunit - do things we need to do once we know which ppp
+ * unit we are using.
+ */
+void
+set_ifunit(iskey)
+    int iskey;
+{
+    info("Using interface ppp%d", ifunit);
+    slprintf(ifname, sizeof(ifname), "ppp%d", ifunit);
+    script_setenv("IFNAME", ifname, iskey);
+    create_pidfile();  /* write pid to file */
+    create_linkpidfile();
+}
+
 /*
  * detach - detach us from the controlling terminal.
  */
@@ -1040,9 +1078,7 @@ create_pidfile()
        pidfilename[0] = 0;
     }
     slprintf(numbuf, sizeof(numbuf), "%d", getpid());
-    script_setenv("PPPD_PID", numbuf);
-    if (linkpidfile[0])
-       create_linkpidfile();
+    script_setenv("PPPD_PID", numbuf, 1);
 }
 
 static void
@@ -1056,14 +1092,14 @@ create_linkpidfile()
             _PATH_VARRUN, linkname);
     if ((pidfile = fopen(linkpidfile, "w")) != NULL) {
        fprintf(pidfile, "%d\n", getpid());
-       if (pidfilename[0])
+       if (ifname[0])
            fprintf(pidfile, "%s\n", ifname);
        (void) fclose(pidfile);
     } else {
        error("Failed to create pid file %s: %m", linkpidfile);
        linkpidfile[0] = 0;
     }
-    script_setenv("LINKNAME", linkname);
+    script_setenv("LINKNAME", linkname, 1);
 }
 
 /*
@@ -1290,6 +1326,14 @@ cleanup()
 
     if (locked)
        unlock();
+
+    if (pppdb != NULL) {
+       TDB_DATA key;
+
+       key.dptr = db_key;
+       key.dsize = strlen(db_key);
+       tdb_delete(pppdb, key);
+    }
 }
 
 /*
@@ -1338,11 +1382,11 @@ update_link_stats(u)
     link_stats_valid = 1;
 
     slprintf(numbuf, sizeof(numbuf), "%d", link_connect_time);
-    script_setenv("CONNECT_TIME", numbuf);
+    script_setenv("CONNECT_TIME", numbuf, 0);
     slprintf(numbuf, sizeof(numbuf), "%d", link_stats.bytes_out);
-    script_setenv("BYTES_SENT", numbuf);
+    script_setenv("BYTES_SENT", numbuf, 0);
     slprintf(numbuf, sizeof(numbuf), "%d", link_stats.bytes_in);
-    script_setenv("BYTES_RCVD", numbuf);
+    script_setenv("BYTES_RCVD", numbuf, 0);
 }
 
 
@@ -1881,10 +1925,12 @@ novm(msg)
  * for scripts that we run (e.g. ip-up, auth-up, etc.)
  */
 void
-script_setenv(var, value)
+script_setenv(var, value, iskey)
     char *var, *value;
+    int iskey;
 {
-    size_t vl = strlen(var) + strlen(value) + 2;
+    size_t varl = strlen(var);
+    size_t vl = varl + strlen(value) + 2;
     int i;
     char *p, *newstring;
 
@@ -1896,7 +1942,11 @@ script_setenv(var, value)
     /* check if this variable is already set */
     if (script_env != 0) {
        for (i = 0; (p = script_env[i]) != 0; ++i) {
-           if (strncmp(p, var, vl) == 0 && p[vl] == '=') {
+           if (strncmp(p, var, varl) == 0 && p[varl] == '=') {
+               if (iskey && pppdb != NULL) {
+                   delete_db_key(p);
+                   add_db_key(newstring);
+               }
                free(p);
                script_env[i] = newstring;
                return;
@@ -1924,6 +1974,12 @@ script_setenv(var, value)
 
     script_env[i] = newstring;
     script_env[i+1] = 0;
+
+    if (pppdb != NULL) {
+       if (iskey)
+           add_db_key(newstring);
+       update_db_entry();
+    }
 }
 
 /*
@@ -1948,6 +2004,70 @@ script_unsetenv(var)
            break;
        }
     }
+    if (pppdb != NULL)
+       update_db_entry();
+}
+
+/*
+ * update_db_entry - update our entry in the database.
+ */
+static void
+update_db_entry()
+{
+    TDB_DATA key, dbuf;
+    int vlen, i;
+    char *p, *q, *vbuf;
+
+    if (script_env == NULL)
+       return;
+    vlen = 0;
+    for (i = 0; (p = script_env[i]) != 0; ++i)
+       vlen += strlen(p) + 1;
+    vbuf = malloc(vlen);
+    if (vbuf == 0)
+       novm("database entry");
+    q = vbuf;
+    for (i = 0; (p = script_env[i]) != 0; ++i)
+       q += slprintf(q, vbuf + vlen - q, "%s;", p);
+
+    key.dptr = db_key;
+    key.dsize = strlen(db_key);
+    dbuf.dptr = vbuf;
+    dbuf.dsize = vlen;
+    if (tdb_store(pppdb, key, dbuf, TDB_REPLACE))
+       error("tdb_store failed: %s", tdb_error(pppdb));
+
+}
+
+/*
+ * add_db_key - add a key that we can use to look up our database entry.
+ */
+static void
+add_db_key(str)
+    const char *str;
+{
+    TDB_DATA key, dbuf;
+
+    key.dptr = (char *) str;
+    key.dsize = strlen(str);
+    dbuf.dptr = db_key;
+    dbuf.dsize = strlen(db_key);
+    if (tdb_store(pppdb, key, dbuf, TDB_REPLACE))
+       error("tdb_store key failed: %s", tdb_error(pppdb));
+}
+
+/*
+ * delete_db_key - delete a key for looking up our database entry.
+ */
+static void
+delete_db_key(str)
+    const char *str;
+{
+    TDB_DATA key;
+
+    key.dptr = (char *) str;
+    key.dsize = strlen(str);
+    tdb_delete(pppdb, key);
 }
 
 /*
index 7ee1e07f46e5dc5952ff4d676d1474d8ca023468..07dd6d02d3b0d255d4424b9e8416990dcbe6dd9e 100644 (file)
@@ -17,7 +17,7 @@
  * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
  */
 
-#define RCSID  "$Id: options.c,v 1.71 2000/03/27 06:03:03 paulus Exp $"
+#define RCSID  "$Id: options.c,v 1.72 2000/04/04 07:06:52 paulus Exp $"
 
 #include <ctype.h>
 #include <stdio.h>
@@ -105,6 +105,7 @@ int connect_delay = 1000;   /* wait this many ms after connect script */
 int    max_data_rate;          /* max bytes/sec through charshunt */
 int    req_unit = -1;          /* requested interface unit */
 bool   multilink = 0;          /* Enable multilink operation */
+char   *bundle_name = NULL;    /* bundle name for multilink */
 
 extern option_t auth_options[];
 extern struct stat devstat;
@@ -289,6 +290,8 @@ option_t general_options[] = {
       "Enable multilink operation", 1 },
     { "nomp", o_bool, &multilink,
       "Disable multilink operation", 0 },
+    { "bundle", o_string, &bundle_name,
+      "Bundle name for multilink" },
 #endif /* HAVE_MULTILINK */
 #ifdef PLUGIN
     { "plugin", o_special, loadplugin,
@@ -1486,39 +1489,20 @@ static int
 setnetmask(argv)
     char **argv;
 {
-    u_int32_t mask, b;
-    int n, ok;
-    char *p, *endp;
+    u_int32_t mask;
+    int n;
+    char *p;
 
     /*
      * Unfortunately, if we use inet_addr, we can't tell whether
      * a result of all 1s is an error or a valid 255.255.255.255.
      */
     p = *argv;
-    ok = 0;
-    mask = 0;
-    for (n = 3;; --n) {
-       b = strtoul(p, &endp, 0);
-       if (endp == p)
-           break;
-       if (b > 255) {
-           if (n == 3) {
-               /* accept e.g. 0xffffff00 */
-               p = endp;
-               mask = b;
-           }
-           break;
-       }
-       mask |= b << (n * 8);
-       p = endp;
-       if (*p != '.' || n == 0)
-           break;
-       ++p;
-    }
+    n = parse_dotted_ip(p, &mask);
 
     mask = htonl(mask);
 
-    if (*p != 0 || (netmask & ~mask) != 0) {
+    if (n == 0 || p[n] != 0 || (netmask & ~mask) != 0) {
        option_error("invalid netmask value '%s'", *argv);
        return 0;
     }
@@ -1527,6 +1511,39 @@ setnetmask(argv)
     return (1);
 }
 
+int
+parse_dotted_ip(p, vp)
+    char *p;
+    u_int32_t *vp;
+{
+    int n;
+    u_int32_t v, b;
+    char *endp, *p0 = p;
+
+    v = 0;
+    for (n = 3;; --n) {
+       b = strtoul(p, &endp, 0);
+       if (endp == p)
+           return 0;
+       if (b > 255) {
+           if (n < 3)
+               return 0;
+           /* accept e.g. 0xffffff00 */
+           *vp = b;
+           return endp - p0;
+       }
+       v |= b << (n * 8);
+       p = endp;
+       if (n == 0)
+           break;
+       if (*p != '.')
+           return 0;
+       ++p;
+    }
+    *vp = v;
+    return p - p0;
+}
+
 static int
 setxonxoff(argv)
     char **argv;
index eed91c108c2bcf6fd613a9eb81f98e6f50f9b1d8..72b601bd46722cab04baa259c4a3b48cecd5f51c 100644 (file)
@@ -1,6 +1,6 @@
-/* $Id: patchlevel.h,v 1.45 1999/12/23 01:41:08 paulus Exp $ */
-#define        PATCHLEVEL      11
+/* $Id: patchlevel.h,v 1.46 2000/04/04 07:06:52 paulus Exp $ */
+#define        PATCHLEVEL      0
 
-#define VERSION                "2.3"
-#define IMPLEMENTATION ""
-#define DATE           "23 December 1999"
+#define VERSION                "2.4"
+#define IMPLEMENTATION "b1"
+#define DATE           "30 March 2000"
index 4aaba6e490547e750f789cc9373a51c845bdef05..416e9ed787f8ae72070275bdc0971a018f773c1c 100644 (file)
@@ -1,18 +1,18 @@
 /*
  * define path names
  *
- * $Id: pathnames.h,v 1.12 1999/11/19 09:46:08 masputra Exp $
+ * $Id: pathnames.h,v 1.13 2000/04/04 07:06:52 paulus Exp $
  */
 
 #ifdef HAVE_PATHS_H
 #include <paths.h>
 
-#else
+#else /* HAVE_PATHS_H */
 #ifndef _PATH_VARRUN
 #define _PATH_VARRUN   "/etc/ppp/"
 #endif
 #define _PATH_DEVNULL  "/dev/null"
-#endif
+#endif /* HAVE_PATHS_H */
 
 #ifndef _ROOT_PATH
 #define _ROOT_PATH
 #define _PATH_IPXUP     _ROOT_PATH "/etc/ppp/ipx-up"
 #define _PATH_IPXDOWN   _ROOT_PATH "/etc/ppp/ipx-down"
 #endif /* IPX_CHANGE */
+
+#ifdef __STDC__
+#define _PATH_PPPDB    _ROOT_PATH _PATH_VARRUN "pppd.tdb"
+#else /* __STDC__ */
+#ifdef HAVE_PATHS_H
+#define _PATH_PPPDB    "/var/run/pppd.tdb"
+#else
+#define _PATH_PPPDB    "/etc/ppp/pppd.tdb"
+#endif
+#endif /* __STDC__ */
index 538b8cda2bc02d38761cf2d1ff926266d71f4f73..465a63ee237c4750dc7dada85d26214bc880fa7b 100644 (file)
@@ -16,7 +16,7 @@
  * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
  * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
  *
- * $Id: pppd.h,v 1.51 2000/03/27 06:03:06 paulus Exp $
+ * $Id: pppd.h,v 1.52 2000/04/04 07:06:53 paulus Exp $
  */
 
 /*
@@ -136,6 +136,22 @@ struct wordlist {
     char               *word;
 };
 
+/* An endpoint discriminator, used with multilink. */
+#define MAX_ENDP_LEN   20      /* maximum length of discriminator value */
+struct epdisc {
+    unsigned char      class;
+    unsigned char      length;
+    unsigned char      value[MAX_ENDP_LEN];
+};
+
+/* values for epdisc.class */
+#define EPD_NULL       0       /* null discriminator, no data */
+#define EPD_LOCAL      1
+#define EPD_IP         2
+#define EPD_MAC                3
+#define EPD_MAGIC      4
+#define EPD_PHONENUM   5
+
 /*
  * Global variables.
  */
@@ -219,6 +235,8 @@ extern int  connect_delay;  /* Time to delay after connect script */
 extern int     max_data_rate;  /* max bytes/sec through charshunt */
 extern int     req_unit;       /* interface unit number to use */
 extern bool    multilink;      /* enable multilink operation */
+extern bool    noendpoint;     /* don't send or accept endpt. discrim. */
+extern char    *bundle_name;   /* bundle name for multilink */
 
 #ifdef PPP_FILTER
 extern struct  bpf_program pass_filter;   /* Filter for pkts to pass */
@@ -296,6 +314,7 @@ extern struct protent *protocols[];
  */
 
 /* Procedures exported from main.c. */
+void set_ifunit __P((int));    /* set stuff that depends on ifunit */
 void detach __P((void));       /* Detach from controlling tty */
 void die __P((int));           /* Cleanup and exit */
 void quit __P((void));         /* like die(1) */
@@ -309,7 +328,7 @@ pid_t run_program __P((char *prog, char **args, int must_exist,
                                /* Run program prog with args in child */
 void reopen_log __P((void));   /* (re)open the connection to syslog */
 void update_link_stats __P((int)); /* Get stats at link termination */
-void script_setenv __P((char *, char *));      /* set script env var */
+void script_setenv __P((char *, char *, int)); /* set script env var */
 void script_unsetenv __P((char *));            /* unset script env var */
 void new_phase __P((int));     /* signal start of new phase */
 
@@ -367,6 +386,12 @@ void demand_rexmit __P((int));     /* retransmit saved frames for an NP */
 int  loop_chars __P((unsigned char *, int)); /* process chars from loopback */
 int  loop_frame __P((unsigned char *, int)); /* should we bring link up? */
 
+/* Procedures exported from multilink.c */
+void mp_check_options __P((void)); /* Check multilink-related options */
+int  mp_join_bundle __P((void));  /* join our link to an appropriate bundle */
+char *epdisc_to_str __P((struct epdisc *)); /* string from endpoint discrim. */
+int  str_to_epdisc __P((struct epdisc *, char *)); /* endpt disc. from str */
+
 /* Procedures exported from sys-*.c */
 void sys_init __P((void));     /* Do system-dependent initialization */
 void sys_cleanup __P((void));  /* Restore system state before exiting */
@@ -378,6 +403,8 @@ int  open_ppp_loopback __P((void)); /* Open loopback for demand-dialling */
 int  establish_ppp __P((int)); /* Turn serial port into a ppp interface */
 void restore_loop __P((void)); /* Transfer ppp unit back to loopback */
 void disestablish_ppp __P((int)); /* Restore port to normal operation */
+void make_new_bundle __P((int, int, int, int)); /* Create new bundle */
+int  bundle_attach __P((int)); /* Attach link to existing bundle */
 void clean_check __P((void));  /* Check if line was 8-bit clean */
 void set_up_tty __P((int, int)); /* Set up port's speed, parameters, etc. */
 void restore_tty __P((int));   /* Restore port's original parameters */
@@ -444,6 +471,8 @@ int  set_filters __P((struct bpf_program *pass, struct bpf_program *active));
 int  sipxfaddr __P((int, unsigned long, unsigned char *));
 int  cipxfaddr __P((int));
 #endif
+int  get_if_hwaddr __P((u_char *addr, char *name));
+char *get_first_ethernet __P((void));
 
 /* Procedures exported from options.c */
 int  parse_args __P((int argc, char **argv));
@@ -462,6 +491,7 @@ void option_error __P((char *fmt, ...));
 int int_option __P((char *, int *));
                                /* Simplified number_option for decimal ints */
 void add_options __P((option_t *)); /* Add extra options */
+int parse_dotted_ip __P((char *, u_int32_t *));
 
 /*
  * This structure is used to store information about certain
index 1e52e3bddf8ab4bd53fb26855c42c0f3c8d20c62..532fb130fd82b0d5a123d2a0ce749094acfb3de3 100644 (file)
@@ -149,6 +149,7 @@ static char proxy_arp_dev[16];              /* Device for proxy arp entry */
 static u_int32_t our_old_addr;         /* for detecting address changes */
 static int     dynaddr_set;            /* 1 if ip_dynaddr set */
 static int     looped;                 /* 1 if using loop */
+static int     link_mtu;               /* mtu for the link (not bundle) */
 
 static struct utsname utsname; /* for the kernel version */
 static int kernel_version;
@@ -176,6 +177,7 @@ static int get_ether_addr (u_int32_t ipaddr, struct sockaddr *hwaddr,
 static void decode_version (char *buf, int *version, int *mod, int *patch);
 static int set_kdebugflag(int level);
 static int ppp_registered(void);
+static int make_ppp_unit(void);
 
 extern u_char  inpacket_buf[]; /* borrowed from main.c */
 
@@ -418,17 +420,8 @@ int establish_ppp (int tty_fd)
            /*
             * Create a new PPP unit.
             */
-           ifunit = req_unit;
-           x = ioctl(ppp_dev_fd, PPPIOCNEWUNIT, &ifunit);
-           if (x < 0 && req_unit >= 0 && errno == EEXIST) {
-               warn("Couldn't allocate PPP unit %d as it is already in use");
-               ifunit = -1;
-               x = ioctl(ppp_dev_fd, PPPIOCNEWUNIT, &ifunit);
-           }
-           if (x < 0) {
-               error("Couldn't create new ppp unit: %m");
+           if (make_ppp_unit() < 0)
                goto err_close;
-           }
        }
 
        if (looped)
@@ -529,7 +522,7 @@ void disestablish_ppp(int tty_fd)
     if (new_style_driver) {
        close(ppp_fd);
        ppp_fd = -1;
-       if (!looped && ioctl(ppp_dev_fd, PPPIOCDETACH) < 0)
+       if (!looped && ifunit >= 0 && ioctl(ppp_dev_fd, PPPIOCDETACH) < 0)
            error("Couldn't release PPP unit: %m");
        if (!multilink)
            remove_fd(ppp_dev_fd);
@@ -537,30 +530,87 @@ void disestablish_ppp(int tty_fd)
 }
 
 /*
- * bundle_attach - attach our link to a given PPP unit.
+ * make_ppp_unit - make a new ppp unit for ppp_dev_fd.
+ * Assumes new_style_driver.
  */
-int bundle_attach(int ifnum)
+static int make_ppp_unit()
 {
-    int mrru = 1500;
+       int x;
 
-    if (!new_style_driver)
-       return -1;
+       ifunit = req_unit;
+       x = ioctl(ppp_dev_fd, PPPIOCNEWUNIT, &ifunit);
+       if (x < 0 && req_unit >= 0 && errno == EEXIST) {
+               warn("Couldn't allocate PPP unit %d as it is already in use");
+               ifunit = -1;
+               x = ioctl(ppp_dev_fd, PPPIOCNEWUNIT, &ifunit);
+       }
+       if (x < 0)
+               error("Couldn't create new ppp unit: %m");
+       return x;
+}
 
-    if (ioctl(ppp_dev_fd, PPPIOCATTACH, &ifnum) < 0) {
-       error("Couldn't attach to interface unit %d: %m\n", ifnum);
-       return -1;
-    }
-    set_flags(ppp_dev_fd, get_flags(ppp_dev_fd) | SC_MULTILINK);
-    if (ioctl(ppp_dev_fd, PPPIOCSMRRU, &mrru) < 0)
-       error("Couldn't set interface MRRU: %m");
+/*
+ * make_new_bundle - create a new PPP unit (i.e. a bundle)
+ * and connect our channel to it.  This should only get called
+ * if `multilink' was set at the time establish_ppp was called.
+ */
+void make_new_bundle(int mrru, int mtru, int rssn, int tssn)
+{
+       int flags;
+       struct ifreq ifr;
+
+       if (looped || !new_style_driver)
+               return;
+       dbglog("make_new_bundle(%d,%d,%d,%d)", mrru, mtru, rssn, tssn);
+
+       /* make us a ppp unit */
+       if (make_ppp_unit() < 0)
+               die(1);
+
+       /* set the mrru, mtu and flags */
+       if (ioctl(ppp_dev_fd, PPPIOCSMRRU, &mrru) < 0)
+               error("Couldn't set MRRU: %m");
+       flags = get_flags(ppp_dev_fd);
+       flags &= ~(SC_MP_SHORTSEQ | SC_MP_XSHORTSEQ);
+       flags |= (rssn? SC_MP_SHORTSEQ: 0) | (tssn? SC_MP_XSHORTSEQ: 0);
+
+       if (mtru > 0 && mtru != link_mtu) {
+               memset(&ifr, 0, sizeof(ifr));
+               slprintf(ifr.ifr_name, sizeof(ifr.ifr_name), "ppp%d", ifunit);
+               ifr.ifr_mtu = mtru;
+               if (ioctl(sock_fd, SIOCSIFMTU, &ifr) < 0)
+                       error("Couldn't set interface MTU: %m");
+               flags |= SC_MULTILINK;
+       }
 
-    if (ioctl(ppp_fd, PPPIOCCONNECT, &ifnum) < 0) {
-       error("Couldn't connect to interface unit %d: %m", ifnum);
-       return -1;
-    }
+       set_flags(ppp_dev_fd, flags);
 
-    dbglog("bundle_attach succeeded");
-    return 0;
+       /* connect up the channel */
+       if (ioctl(ppp_fd, PPPIOCCONNECT, &ifunit) < 0)
+               fatal("Couldn't attach to PPP unit %d: %m", ifunit);
+       add_fd(ppp_dev_fd);
+}
+
+/*
+ * bundle_attach - attach our link to a given PPP unit.
+ */
+int bundle_attach(int ifnum)
+{
+       if (!new_style_driver)
+               return -1;
+
+       if (ioctl(ppp_dev_fd, PPPIOCATTACH, &ifnum) < 0) {
+               if (errno == ENXIO)
+                       return 0;       /* doesn't still exist */
+               fatal("Couldn't attach to interface unit %d: %m\n", ifnum);
+       }
+       if (ioctl(ppp_fd, PPPIOCCONNECT, &ifnum) < 0)
+               fatal("Couldn't connect to interface unit %d: %m", ifnum);
+       set_flags(ppp_dev_fd, get_flags(ppp_dev_fd) | SC_MULTILINK);
+
+       ifunit = ifnum;
+       dbglog("bundle_attach succeeded");
+       return 1;
 }
 
 /********************************************************************
@@ -855,7 +905,7 @@ void output (int unit, unsigned char *p, int len)
        p += 2;
        len -= 2;
        proto = (p[0] << 8) + p[1];
-       if (!multilink && proto != PPP_LCP)
+       if (ifunit >= 0 && !(proto >= 0xc000 || proto == PPP_CCPFRAG))
            fd = ppp_dev_fd;
     }
     if (write(fd, p, len) < 0) {
@@ -926,7 +976,7 @@ int read_packet (unsigned char *buf)
        if (nr < 0 && errno != EWOULDBLOCK && errno != EIO)
            error("read: %m");
     }
-    if (nr < 0 && new_style_driver && !multilink) {
+    if (nr < 0 && new_style_driver && ifunit >= 0) {
        /* N.B. we read ppp_fd first since LCP packets come in there. */
        nr = read(ppp_dev_fd, buf, len);
        if (nr < 0 && errno != EWOULDBLOCK && errno != EIO)
@@ -988,7 +1038,8 @@ void ppp_send_config (int unit,int mtu,u_int32_t asyncmap,int pcomp,int accomp)
        
     if (ifunit >= 0 && ioctl(sock_fd, SIOCSIFMTU, (caddr_t) &ifr) < 0)
        fatal("ioctl(SIOCSIFMTU): %m(%d)", errno);
-       
+    link_mtu = mtu;
+
     if (!still_ppp())
        return;
     SYSDEBUG ((LOG_DEBUG, "send_config: asyncmap = %lx\n", asyncmap));
@@ -1600,6 +1651,38 @@ static int get_ether_addr (u_int32_t ipaddr,
     return 1;
 }
 
+/*
+ * get_if_hwaddr - get the hardware address for the specified
+ * network interface device.
+ */
+int
+get_if_hwaddr(u_char *addr, char *name)
+{
+       struct ifreq ifreq;
+       int ret, sock_fd;
+
+       sock_fd = socket(AF_INET, SOCK_DGRAM, 0);
+       if (sock_fd < 0)
+               return 0;
+       memset(&ifreq.ifr_hwaddr, 0, sizeof(struct sockaddr));
+       strlcpy(ifreq.ifr_name, name, sizeof(ifreq.ifr_name));
+       ret = ioctl(sock_fd, SIOCGIFHWADDR, &ifreq);
+       close(sock_fd);
+       if (ret >= 0)
+               memcpy(addr, ifreq.ifr_hwaddr.sa_data, 6);
+       return ret;
+}
+
+/*
+ * get_first_ethernet - return the name of the first ethernet-style
+ * interface on this system.
+ */
+char *
+get_first_ethernet()
+{
+       return "eth0";
+}
+
 /********************************************************************
  *
  * Return user specified netmask, modified by any mask we might determine
@@ -2355,20 +2438,13 @@ get_pty(master_fdp, slave_fdp, slave_name, uid)
 int
 open_ppp_loopback(void)
 {
-    int flags, x;
+    int flags;
 
     looped = 1;
     if (new_style_driver) {
        /* allocate ourselves a ppp unit */
-       ifunit = req_unit;
-       x = ioctl(ppp_dev_fd, PPPIOCNEWUNIT, &ifunit);
-       if (x < 0 && req_unit >= 0 && errno == EEXIST) {
-           warn("Couldn't allocate PPP unit %d as it is already in use");
-           ifunit = -1;
-           x = ioctl(ppp_dev_fd, PPPIOCNEWUNIT, &ifunit);
-       }
-       if (x < 0)
-           fatal("Couldn't create PPP unit: %m");
+       if (make_ppp_unit() < 0)
+           die(1);
        set_flags(ppp_dev_fd, SC_LOOP_TRAFFIC);
        set_kdebugflag(kdebugflag);
        ppp_fd = -1;
diff --git a/pppd/tdb.c b/pppd/tdb.c
new file mode 100644 (file)
index 0000000..7fd5829
--- /dev/null
@@ -0,0 +1,1282 @@
+/* 
+ * Database functions
+ * Copyright (C) Andrew Tridgell 1999
+ * 
+ * Redistribution and use in source and binary forms are permitted
+ * provided that the above copyright notice and this paragraph are
+ * duplicated in all such forms AND provided that this software or
+ * any derived work is only used as part of the PPP daemon (pppd)
+ * and related utilities.
+ * The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
+ * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
+ *
+ * Note: this software is also available under the Gnu Public License
+ * version 2 or later.
+ */
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <string.h>
+#include <fcntl.h>
+#include <errno.h>
+#include <sys/mman.h>
+#include <sys/stat.h>
+#include "tdb.h"
+
+#define TDB_VERSION (0x26011967 + 1)
+#define TDB_MAGIC (0x26011999U)
+#define TDB_FREE_MAGIC (~TDB_MAGIC)
+#define TDB_ALIGN 4
+#define MIN_REC_SIZE (2*sizeof(struct list_struct) + TDB_ALIGN)
+#define DEFAULT_HASH_SIZE 128
+#define TDB_PAGE_SIZE 0x2000
+#define TDB_LEN_MULTIPLIER 10
+#define FREELIST_TOP (sizeof(struct tdb_header))
+
+#define LOCK_SET 1
+#define LOCK_CLEAR 0
+
+/* lock offsets */
+#define GLOBAL_LOCK 0
+#define ACTIVE_LOCK 4
+#define LIST_LOCK_BASE 1024
+
+#define BUCKET(hash) ((hash) % tdb->header.hash_size)
+
+#ifndef MAP_FILE
+#define MAP_FILE 0
+#endif
+
+/* the body of the database is made of one list_struct for the free space
+   plus a separate data list for each hash value */
+struct list_struct {
+       tdb_len rec_len; /* total byte length of record */
+       tdb_off next; /* offset of the next record in the list */
+       tdb_len key_len; /* byte length of key */
+       tdb_len data_len; /* byte length of data */
+       unsigned full_hash; /* the full 32 bit hash of the key */
+       unsigned magic;   /* try to catch errors */
+       /*
+          the following union is implied 
+          union {
+              char record[rec_len];
+             struct {
+               char key[key_len];
+               char data[data_len];
+             }
+           }
+       */
+};
+
+/* a null data record - useful for error returns */
+static TDB_DATA null_data;
+
+/* a byte range locking function - return 0 on success
+   this functions locks/unlocks 1 byte at the specified offset */
+static int tdb_brlock(TDB_CONTEXT *tdb, tdb_off offset, 
+                     int set, int rw_type, int lck_type)
+{
+#if NOLOCK
+       return 0;
+#else
+       struct flock fl;
+
+        if (tdb->fd == -1) return 0;   /* for in memory tdb */
+
+       if (tdb->read_only) return -1;
+
+       fl.l_type = set==LOCK_SET?rw_type:F_UNLCK;
+       fl.l_whence = SEEK_SET;
+       fl.l_start = offset;
+       fl.l_len = 1;
+       fl.l_pid = 0;
+
+       if (fcntl(tdb->fd, lck_type, &fl) != 0) {
+#if TDB_DEBUG
+               if (lck_type == F_SETLKW) {
+                       printf("lock %d failed at %d (%s)\n", 
+                              set, offset, strerror(errno));
+               }
+#endif
+               tdb->ecode = TDB_ERR_LOCK;
+               return -1;
+       }
+       return 0;
+#endif
+}
+
+/* lock a list in the database. list -1 is the alloc list */
+static int tdb_lock(TDB_CONTEXT *tdb, int list)
+{
+       if (list < -1 || list >= (int)tdb->header.hash_size) {
+#if TDB_DEBUG
+               printf("bad list %d\n", list);
+#endif
+               return -1;
+       }
+       if (tdb->locked[list+1] == 0) {
+               if (tdb_brlock(tdb, LIST_LOCK_BASE + 4*list, LOCK_SET, 
+                              F_WRLCK, F_SETLKW) != 0) {
+                       return -1;
+               }
+       }
+       tdb->locked[list+1]++;
+       return 0;
+}
+
+/* unlock the database. */
+static int tdb_unlock(TDB_CONTEXT *tdb, int list)
+{
+       if (list < -1 || list >= (int)tdb->header.hash_size) {
+#if TDB_DEBUG
+               printf("bad unlock list %d\n", list);
+#endif
+               return -1;
+       }
+
+       if (tdb->locked[list+1] == 0) {
+#if TDB_DEBUG
+               printf("not locked %d\n", list);
+#endif
+               tdb->ecode = TDB_ERR_LOCK;
+               return -1;
+       }
+       if (tdb->locked[list+1] == 1) {
+               if (tdb_brlock(tdb, LIST_LOCK_BASE + 4*list, LOCK_CLEAR, 
+                              F_WRLCK, F_SETLKW) != 0) {
+                       return -1;
+               }
+       }
+       tdb->locked[list+1]--;
+       return 0;
+}
+
+/* the hash algorithm - turn a key into an integer
+   This is based on the hash agorithm from gdbm */
+static unsigned tdb_hash(TDB_DATA *key)
+{
+       unsigned value; /* Used to compute the hash value.  */
+       unsigned   i;   /* Used to cycle through random values. */
+
+       /* Set the initial value from the key size. */
+       value = 0x238F13AF * key->dsize;
+       for (i=0; i < key->dsize; i++) {
+               value = (value + (key->dptr[i] << (i*5 % 24)));
+       }
+
+       value = (1103515243 * value + 12345);  
+
+       return value;
+}
+
+/* find the top of the hash chain for an open database */
+static tdb_off tdb_hash_top(TDB_CONTEXT *tdb, unsigned hash)
+{
+       tdb_off ret;
+       hash = BUCKET(hash);
+       ret = FREELIST_TOP + (hash+1)*sizeof(tdb_off);
+       return ret;
+}
+
+
+/* check for an out of bounds access - if it is out of bounds then
+   see if the database has been expanded by someone else and expand
+   if necessary */
+static int tdb_oob(TDB_CONTEXT *tdb, tdb_off offset)
+{
+       struct stat st;
+       if ((offset <= tdb->map_size) || (tdb->fd == -1)) return 0;
+
+       fstat(tdb->fd, &st);
+       if (st.st_size <= (ssize_t)offset) {
+               tdb->ecode = TDB_ERR_IO;
+               return -1;
+       }
+
+#if HAVE_MMAP
+       if (tdb->map_ptr) {
+               munmap(tdb->map_ptr, tdb->map_size);
+               tdb->map_ptr = NULL;
+       }
+#endif
+
+       tdb->map_size = st.st_size;
+#if HAVE_MMAP
+       tdb->map_ptr = (void *)mmap(NULL, tdb->map_size, 
+                                   tdb->read_only?PROT_READ:PROT_READ|PROT_WRITE,
+                                   MAP_SHARED | MAP_FILE, tdb->fd, 0);
+#endif 
+       return 0;
+}
+
+
+/* write a lump of data at a specified offset */
+static int tdb_write(TDB_CONTEXT *tdb, tdb_off offset, const char *buf, tdb_len len)
+{
+       if (tdb_oob(tdb, offset + len) != 0) {
+               /* oops - trying to write beyond the end of the database! */
+               return -1;
+       }
+
+       if (tdb->map_ptr) {
+               memcpy(offset + (char *)tdb->map_ptr, buf, len);
+       } else {
+               if (lseek(tdb->fd, offset, SEEK_SET) != offset ||
+                   write(tdb->fd, buf, len) != (ssize_t)len) {
+                       tdb->ecode = TDB_ERR_IO;
+                       return -1;
+               }
+       }
+       return 0;
+}
+
+/* read a lump of data at a specified offset */
+static int tdb_read(TDB_CONTEXT *tdb, tdb_off offset, char *buf, tdb_len len)
+{
+       if (tdb_oob(tdb, offset + len) != 0) {
+               /* oops - trying to read beyond the end of the database! */
+               return -1;
+       }
+
+       if (tdb->map_ptr) {
+               memcpy(buf, offset + (char *)tdb->map_ptr, len);
+       } else {
+               if (lseek(tdb->fd, offset, SEEK_SET) != offset ||
+                   read(tdb->fd, buf, len) != (ssize_t)len) {
+                       tdb->ecode = TDB_ERR_IO;
+                       return -1;
+               }
+       }
+       return 0;
+}
+
+
+/* read a lump of data, allocating the space for it */
+static char *tdb_alloc_read(TDB_CONTEXT *tdb, tdb_off offset, tdb_len len)
+{
+       char *buf;
+
+       buf = (char *)malloc(len);
+
+       if (!buf) {
+               tdb->ecode = TDB_ERR_OOM;
+               return NULL;
+       }
+
+       if (tdb_read(tdb, offset, buf, len) == -1) {
+               free(buf);
+               return NULL;
+       }
+       
+       return buf;
+}
+
+/* convenience routine for writing a record */
+static int rec_write(TDB_CONTEXT *tdb, tdb_off offset, struct list_struct *rec)
+{
+       return tdb_write(tdb, offset, (char *)rec, sizeof(*rec));
+}
+
+/* convenience routine for writing a tdb_off */
+static int ofs_write(TDB_CONTEXT *tdb, tdb_off offset, tdb_off *d)
+{
+       return tdb_write(tdb, offset, (char *)d, sizeof(*d));
+}
+
+/* read a tdb_off from the store */
+static int ofs_read(TDB_CONTEXT *tdb, tdb_off offset, tdb_off *d)
+{
+       return tdb_read(tdb, offset, (char *)d, sizeof(*d));
+}
+
+/* read a record and check for simple errors */
+static int rec_read(TDB_CONTEXT *tdb, tdb_off offset, struct list_struct *rec)
+{
+       if (tdb_read(tdb, offset, (char *)rec, sizeof(*rec)) == -1) return -1;
+       if (rec->magic != TDB_MAGIC) {
+#if TDB_DEBUG
+               printf("bad magic 0x%08x at offset %d\n",
+                      rec->magic, offset);
+#endif
+               tdb->ecode = TDB_ERR_CORRUPT;
+               return -1;
+       }
+       if (tdb_oob(tdb, rec->next) != 0) {
+               return -1;
+       }
+       return 0;
+}
+
+/* expand the database at least length bytes by expanding the
+   underlying file and doing the mmap again if necessary */
+static int tdb_expand(TDB_CONTEXT *tdb, tdb_off length)
+{
+       struct list_struct rec;
+       tdb_off offset, ptr;
+       char b = 0;
+
+       tdb_lock(tdb,-1);
+
+       /* make sure we know about any previous expansions by another
+           process */
+       tdb_oob(tdb,tdb->map_size + 1);
+
+       /* always make room for at least 10 more records */
+       length *= TDB_LEN_MULTIPLIER;
+
+       /* and round the database up to a multiple of TDB_PAGE_SIZE */
+       length = ((tdb->map_size + length + TDB_PAGE_SIZE) & ~(TDB_PAGE_SIZE - 1)) - tdb->map_size;
+
+       /* expand the file itself */
+        if (tdb->fd != -1) {
+            lseek(tdb->fd, tdb->map_size + length - 1, SEEK_SET);
+            if (write(tdb->fd, &b, 1) != 1) goto fail;
+        }
+
+       /* form a new freelist record */
+       offset = FREELIST_TOP;
+       rec.rec_len = length - sizeof(rec);
+       rec.magic = TDB_FREE_MAGIC;
+       if (ofs_read(tdb, offset, &rec.next) == -1) {
+               goto fail;
+       }
+
+#if HAVE_MMAP
+       if (tdb->fd != -1 && tdb->map_ptr) {
+               munmap(tdb->map_ptr, tdb->map_size);
+               tdb->map_ptr = NULL;
+       }
+#endif
+
+       tdb->map_size += length;
+
+        if (tdb->fd == -1) {
+            tdb->map_ptr = realloc(tdb->map_ptr, tdb->map_size);
+        }
+
+       /* write it out */
+       if (rec_write(tdb, tdb->map_size - length, &rec) == -1) {
+               goto fail;
+       }
+
+       /* link it into the free list */
+       ptr = tdb->map_size - length;
+       if (ofs_write(tdb, offset, &ptr) == -1) goto fail;
+
+#if HAVE_MMAP
+        if (tdb->fd != -1) {
+            tdb->map_ptr = (void *)mmap(NULL, tdb->map_size, 
+                                        PROT_READ|PROT_WRITE,
+                                        MAP_SHARED | MAP_FILE, tdb->fd, 0);
+        }
+#endif
+
+       tdb_unlock(tdb, -1);
+       return 0;
+
+ fail:
+       tdb_unlock(tdb,-1);
+       return -1;
+}
+
+/* allocate some space from the free list. The offset returned points
+   to a unconnected list_struct within the database with room for at
+   least length bytes of total data
+
+   0 is returned if the space could not be allocated
+ */
+static tdb_off tdb_allocate(TDB_CONTEXT *tdb, tdb_len length)
+{
+       tdb_off offset, rec_ptr, last_ptr;
+       struct list_struct rec, lastrec, newrec;
+
+       tdb_lock(tdb, -1);
+
+ again:
+       last_ptr = 0;
+       offset = FREELIST_TOP;
+
+       /* read in the freelist top */
+       if (ofs_read(tdb, offset, &rec_ptr) == -1) {
+               goto fail;
+       }
+
+       /* keep looking until we find a freelist record that is big
+           enough */
+       while (rec_ptr) {
+               if (tdb_read(tdb, rec_ptr, (char *)&rec, sizeof(rec)) == -1) {
+                       goto fail;
+               }
+
+               if (rec.magic != TDB_FREE_MAGIC) {
+#if TDB_DEBUG
+                       printf("bad magic 0x%08x in free list\n", rec.magic);
+#endif
+                       goto fail;
+               }
+
+               if (rec.rec_len >= length) {
+                       /* found it - now possibly split it up  */
+                       if (rec.rec_len > length + MIN_REC_SIZE) {
+                               length = (length + TDB_ALIGN) & ~(TDB_ALIGN-1);
+
+                               newrec.rec_len = rec.rec_len - (sizeof(rec) + length);
+                               newrec.next = rec.next;
+                               newrec.magic = TDB_FREE_MAGIC;
+
+                               rec.rec_len = length;
+                               rec.next = rec_ptr + sizeof(rec) + rec.rec_len;
+                               
+                               if (rec_write(tdb, rec.next, &newrec) == -1) {
+                                       goto fail;
+                               }
+
+                               if (rec_write(tdb, rec_ptr, &rec) == -1) {
+                                       goto fail;
+                               }
+                       }
+
+                       /* remove it from the list */
+                       if (last_ptr == 0) {
+                               offset = FREELIST_TOP;
+
+                               if (ofs_write(tdb, offset, &rec.next) == -1) {
+                                       goto fail;
+                               }                               
+                       } else {
+                               lastrec.next = rec.next;
+                               if (rec_write(tdb, last_ptr, &lastrec) == -1) {
+                                       goto fail;
+                               }
+                       }
+
+                       /* all done - return the new record offset */
+                       tdb_unlock(tdb, -1);
+                       return rec_ptr;
+               }
+
+               /* move to the next record */
+               lastrec = rec;
+               last_ptr = rec_ptr;
+               rec_ptr = rec.next;
+       }
+
+       /* we didn't find enough space. See if we can expand the
+          database and if we can then try again */
+       if (tdb_expand(tdb, length + sizeof(rec)) == 0) goto again;
+
+ fail:
+#if TDB_DEBUG
+       printf("tdb_allocate failed for size %u\n", length);
+#endif
+       tdb_unlock(tdb, -1);
+       return 0;
+}
+
+/* initialise a new database with a specified hash size */
+static int tdb_new_database(TDB_CONTEXT *tdb, int hash_size)
+{
+       struct tdb_header header;
+       tdb_off offset;
+       int i, size = 0;
+       tdb_off buf[16];
+
+        /* create the header */
+        memset(&header, 0, sizeof(header));
+        memcpy(header.magic_food, TDB_MAGIC_FOOD, strlen(TDB_MAGIC_FOOD)+1);
+        header.version = TDB_VERSION;
+        header.hash_size = hash_size;
+        lseek(tdb->fd, 0, SEEK_SET);
+        ftruncate(tdb->fd, 0);
+        
+        if (tdb->fd != -1 && write(tdb->fd, &header, sizeof(header)) != 
+            sizeof(header)) {
+            tdb->ecode = TDB_ERR_IO;
+            return -1;
+        } else size += sizeof(header);
+       
+        /* the freelist and hash pointers */
+        offset = 0;
+        memset(buf, 0, sizeof(buf));
+
+        for (i=0;(hash_size+1)-i >= 16; i += 16) {
+            if (tdb->fd != -1 && write(tdb->fd, buf, sizeof(buf)) != 
+                sizeof(buf)) {
+                tdb->ecode = TDB_ERR_IO;
+                return -1;
+            } else size += sizeof(buf);
+        }
+
+        for (;i<hash_size+1; i++) {
+            if (tdb->fd != -1 && write(tdb->fd, buf, sizeof(tdb_off)) != 
+                sizeof(tdb_off)) {
+                tdb->ecode = TDB_ERR_IO;
+                return -1;
+            } else size += sizeof(tdb_off);
+        }
+
+        if (tdb->fd == -1) {
+            tdb->map_ptr = calloc(size, 1);
+            tdb->map_size = size;
+            if (tdb->map_ptr == NULL) {
+                tdb->ecode = TDB_ERR_IO;
+                return -1;
+            }
+            memcpy(&tdb->header, &header, sizeof(header));
+        }
+
+#if TDB_DEBUG
+       printf("initialised database of hash_size %u\n", 
+              hash_size);
+#endif
+       return 0;
+}
+
+/* Returns 0 on fail.  On success, return offset of record, and fills
+   in rec */
+static tdb_off tdb_find(TDB_CONTEXT *tdb, TDB_DATA key, unsigned int hash,
+                       struct list_struct *rec)
+{
+       tdb_off offset, rec_ptr;
+       
+       /* find the top of the hash chain */
+       offset = tdb_hash_top(tdb, hash);
+
+       /* read in the hash top */
+       if (ofs_read(tdb, offset, &rec_ptr) == -1)
+               return 0;
+
+       /* keep looking until we find the right record */
+       while (rec_ptr) {
+               if (rec_read(tdb, rec_ptr, rec) == -1)
+                       return 0;
+
+               if (hash == rec->full_hash && key.dsize == rec->key_len) {
+                       char *k;
+                       /* a very likely hit - read the key */
+                       k = tdb_alloc_read(tdb, rec_ptr + sizeof(*rec), 
+                                          rec->key_len);
+
+                       if (!k)
+                               return 0;
+
+                       if (memcmp(key.dptr, k, key.dsize) == 0) {
+                               free(k);
+                               return rec_ptr;
+                       }
+                       free(k);
+               }
+
+               /* move to the next record */
+               rec_ptr = rec->next;
+       }
+       return 0;
+}
+
+/* 
+   return an error string for the last tdb error
+*/
+char *tdb_error(TDB_CONTEXT *tdb)
+{
+       int i;
+       static struct {
+               enum TDB_ERROR ecode;
+               char *estring;
+       } emap[] = {
+               {TDB_SUCCESS, "Success"},
+               {TDB_ERR_CORRUPT, "Corrupt database"},
+               {TDB_ERR_IO, "IO Error"},
+               {TDB_ERR_LOCK, "Locking error"},
+               {TDB_ERR_OOM, "Out of memory"},
+               {TDB_ERR_EXISTS, "Record exists"},
+               {-1, NULL}};
+        if (tdb != NULL) {
+            for (i=0;emap[i].estring;i++) {
+               if (tdb->ecode == emap[i].ecode) return emap[i].estring;
+            }
+        } else {
+            return "Invalid tdb context";
+        }
+       return "Invalid error code";
+}
+
+
+/* update an entry in place - this only works if the new data size
+   is <= the old data size and the key exists.
+   on failure return -1
+*/
+int tdb_update(TDB_CONTEXT *tdb, TDB_DATA key, TDB_DATA dbuf)
+{
+       unsigned hash;
+       struct list_struct rec;
+       tdb_off rec_ptr;
+       int ret = -1;
+
+        if (tdb == NULL) {
+#ifdef TDB_DEBUG
+            printf("tdb_update() called with null context\n");
+#endif
+            return -1;
+        }
+
+       /* find which hash bucket it is in */
+       hash = tdb_hash(&key);
+
+       tdb_lock(tdb, BUCKET(hash));
+       rec_ptr = tdb_find(tdb, key, hash, &rec);
+
+       if (!rec_ptr)
+               goto out;
+
+       /* must be long enough */
+       if (rec.rec_len < key.dsize + dbuf.dsize)
+               goto out;
+
+       if (tdb_write(tdb, rec_ptr + sizeof(rec) + rec.key_len,
+                     dbuf.dptr, dbuf.dsize) == -1)
+               goto out;
+
+       if (dbuf.dsize != rec.data_len) {
+               /* update size */
+               rec.data_len = dbuf.dsize;
+               ret = rec_write(tdb, rec_ptr, &rec);
+       } else
+               ret = 0;
+
+ out:
+       tdb_unlock(tdb, BUCKET(hash));
+       return ret;
+}
+
+/* find an entry in the database given a key */
+TDB_DATA tdb_fetch(TDB_CONTEXT *tdb, TDB_DATA key)
+{
+       unsigned hash;
+       tdb_off rec_ptr;
+       struct list_struct rec;
+       TDB_DATA ret = null_data;
+
+        if (tdb == NULL) {
+#ifdef TDB_DEBUG
+            printf("tdb_fetch() called with null context\n");
+#endif
+            return null_data;
+        }
+
+       /* find which hash bucket it is in */
+       hash = tdb_hash(&key);
+
+       tdb_lock(tdb, BUCKET(hash));
+       rec_ptr = tdb_find(tdb, key, hash, &rec);
+
+       if (rec_ptr) {
+               ret.dptr = tdb_alloc_read(tdb,
+                                         rec_ptr + sizeof(rec) + rec.key_len,
+                                         rec.data_len);
+               ret.dsize = rec.data_len;
+       }
+       
+       tdb_unlock(tdb, BUCKET(hash));
+       return ret;
+}
+
+/* check if an entry in the database exists 
+
+   note that 1 is returned if the key is found and 0 is returned if not found
+   this doesn't match the conventions in the rest of this module, but is
+   compatible with gdbm
+*/
+int tdb_exists(TDB_CONTEXT *tdb, TDB_DATA key)
+{
+       unsigned hash;
+       tdb_off rec_ptr;
+       struct list_struct rec;
+       
+        if (tdb == NULL) {
+#ifdef TDB_DEBUG
+            printf("tdb_exists() called with null context\n");
+#endif
+            return 0;
+        }
+
+       /* find which hash bucket it is in */
+       hash = tdb_hash(&key);
+
+       tdb_lock(tdb, BUCKET(hash));
+       rec_ptr = tdb_find(tdb, key, hash, &rec);
+       tdb_unlock(tdb, BUCKET(hash));
+
+       return rec_ptr != 0;
+}
+
+/* traverse the entire database - calling fn(tdb, key, data) on each element.
+   return -1 on error or the record count traversed
+   if fn is NULL then it is not called
+   a non-zero return value from fn() indicates that the traversal should stop
+  */
+int tdb_traverse(TDB_CONTEXT *tdb, int (*fn)(TDB_CONTEXT *tdb, TDB_DATA key, TDB_DATA dbuf, void* state), void* state)
+{
+       int count = 0;
+       unsigned h;
+       tdb_off offset, rec_ptr;
+       struct list_struct rec;
+       char *data;
+       TDB_DATA key, dbuf;
+
+        if (tdb == NULL) {
+#ifdef TDB_DEBUG
+            printf("tdb_traverse() called with null context\n");
+#endif
+            return -1;
+        }
+
+       /* loop over all hash chains */
+       for (h = 0; h < tdb->header.hash_size; h++) {
+               tdb_lock(tdb, BUCKET(h));
+
+               /* read in the hash top */
+               offset = tdb_hash_top(tdb, h);
+               if (ofs_read(tdb, offset, &rec_ptr) == -1) {
+                       goto fail;
+               }
+
+               /* traverse all records for this hash */
+               while (rec_ptr) {
+                       if (rec_read(tdb, rec_ptr, &rec) == -1) {
+                               goto fail;
+                       }
+
+                       /* now read the full record */
+                       data = tdb_alloc_read(tdb, rec_ptr + sizeof(rec), 
+                                            rec.key_len + rec.data_len);
+                       if (!data) {
+                               goto fail;
+                       }
+
+                       key.dptr = data;
+                       key.dsize = rec.key_len;
+                       dbuf.dptr = data + rec.key_len;
+                       dbuf.dsize = rec.data_len;
+                       count++;
+
+                       if (fn && fn(tdb, key, dbuf, state) != 0) {
+                               /* they want us to stop traversing */
+                               free(data);
+                               tdb_unlock(tdb, BUCKET(h));
+                               return count;
+                       }
+
+                       /* a miss - drat */
+                       free(data);
+
+                       /* move to the next record */
+                       rec_ptr = rec.next;
+               }
+               tdb_unlock(tdb, BUCKET(h));
+       }
+
+       /* return the number traversed */
+       return count;
+
+ fail:
+       tdb_unlock(tdb, BUCKET(h));
+       return -1;
+}
+
+
+/* find the first entry in the database and return its key */
+TDB_DATA tdb_firstkey(TDB_CONTEXT *tdb)
+{
+       tdb_off offset, rec_ptr;
+       struct list_struct rec;
+       unsigned hash;
+       TDB_DATA ret;
+
+        if (tdb == NULL) {
+#ifdef TDB_DEBUG
+            printf("tdb_firstkey() called with null context\n");
+#endif
+            return null_data;
+        }
+
+       /* look for a non-empty hash chain */
+       for (hash = 0, rec_ptr = 0; 
+            hash < tdb->header.hash_size;
+            hash++) {
+               /* find the top of the hash chain */
+               offset = tdb_hash_top(tdb, hash);
+
+               tdb_lock(tdb, BUCKET(hash));
+
+               /* read in the hash top */
+               if (ofs_read(tdb, offset, &rec_ptr) == -1) {
+                       goto fail;
+               }
+
+               if (rec_ptr) break;
+
+               tdb_unlock(tdb, BUCKET(hash));
+       }
+
+       if (rec_ptr == 0) return null_data;
+
+       /* we've found a non-empty chain, now read the record */
+       if (rec_read(tdb, rec_ptr, &rec) == -1) {
+               goto fail;
+       }
+
+       /* allocate and read the key space */
+       ret.dptr = tdb_alloc_read(tdb, rec_ptr + sizeof(rec), rec.key_len);
+       ret.dsize = rec.key_len;
+       tdb_unlock(tdb, BUCKET(hash));
+       return ret;
+
+ fail:
+       tdb_unlock(tdb, BUCKET(hash));
+       return null_data;
+}
+
+/* find the next entry in the database, returning its key */
+TDB_DATA tdb_nextkey(TDB_CONTEXT *tdb, TDB_DATA key)
+{
+       unsigned hash, hbucket;
+       tdb_off rec_ptr, offset;
+       struct list_struct rec;
+       TDB_DATA ret;
+
+        if (tdb == NULL) {
+#ifdef TDB_DEBUG
+            printf("tdb_nextkey() called with null context\n");
+#endif
+            return null_data;
+        }
+
+       /* find which hash bucket it is in */
+       hash = tdb_hash(&key);
+       hbucket = BUCKET(hash);
+       
+       tdb_lock(tdb, hbucket);
+       rec_ptr = tdb_find(tdb, key, hash, &rec);
+       if (rec_ptr) {
+               /* we want the next record after this one */
+               rec_ptr = rec.next;
+       }
+
+       /* not found or last in hash: look for next non-empty hash chain */
+       while (rec_ptr == 0) {
+               tdb_unlock(tdb, hbucket);
+
+               if (++hbucket >= tdb->header.hash_size - 1)
+                       return null_data;
+
+               offset = tdb_hash_top(tdb, hbucket);
+               tdb_lock(tdb, hbucket);
+               /* read in the hash top */
+               if (ofs_read(tdb, offset, &rec_ptr) == -1) {
+                       tdb_unlock(tdb, hbucket);
+                       return null_data;
+               }
+       }
+
+       /* Read the record. */
+       if (rec_read(tdb, rec_ptr, &rec) == -1) {
+               tdb_unlock(tdb, hbucket);
+               return null_data;
+       }
+       /* allocate and read the key */
+       ret.dptr = tdb_alloc_read(tdb, rec_ptr + sizeof(rec), rec.key_len);
+       ret.dsize = rec.key_len;
+       tdb_unlock(tdb, hbucket);
+
+       return ret;
+}
+
+/* delete an entry in the database given a key */
+int tdb_delete(TDB_CONTEXT *tdb, TDB_DATA key)
+{
+       unsigned hash;
+       tdb_off offset, rec_ptr, last_ptr;
+       struct list_struct rec, lastrec;
+       char *data = NULL;
+
+        if (tdb == NULL) {
+#ifdef TDB_DEBUG
+            printf("tdb_delete() called with null context\n");
+#endif
+            return -1;
+        }
+
+       /* find which hash bucket it is in */
+       hash = tdb_hash(&key);
+
+       tdb_lock(tdb, BUCKET(hash));
+
+       /* find the top of the hash chain */
+       offset = tdb_hash_top(tdb, hash);
+
+       /* read in the hash top */
+       if (ofs_read(tdb, offset, &rec_ptr) == -1) {
+               goto fail;
+       }
+
+       last_ptr = 0;
+
+       /* keep looking until we find the right record */
+       while (rec_ptr) {
+               if (rec_read(tdb, rec_ptr, &rec) == -1) {
+                       goto fail;
+               }
+
+               if (hash == rec.full_hash && key.dsize == rec.key_len) {
+                       /* a very likely hit - read the record and full key */
+                       data = tdb_alloc_read(tdb, rec_ptr + sizeof(rec), 
+                                            rec.key_len);
+                       if (!data) {
+                               goto fail;
+                       }
+
+                       if (memcmp(key.dptr, data, key.dsize) == 0) {
+                               /* a definite match - delete it */
+                               if (last_ptr == 0) {
+                                       offset = tdb_hash_top(tdb, hash);
+                                       if (ofs_write(tdb, offset, &rec.next) == -1) {
+                                               goto fail;
+                                       }
+                               } else {
+                                       lastrec.next = rec.next;
+                                       if (rec_write(tdb, last_ptr, &lastrec) == -1) {
+                                               goto fail;
+                                       }                                       
+                               }
+                               tdb_unlock(tdb, BUCKET(hash));
+                               tdb_lock(tdb, -1);
+                               /* and recover the space */
+                               offset = FREELIST_TOP;
+                               if (ofs_read(tdb, offset, &rec.next) == -1) {
+                                       goto fail2;
+                               }
+                               rec.magic = TDB_FREE_MAGIC;
+                               if (rec_write(tdb, rec_ptr, &rec) == -1) {
+                                       goto fail2;
+                               }
+                               if (ofs_write(tdb, offset, &rec_ptr) == -1) {
+                                       goto fail2;
+                               }
+
+                               /* yipee - all done */
+                               free(data);
+                               tdb_unlock(tdb, -1);
+                               return 0;
+                       }
+
+                       /* a miss - drat */
+                       free(data);
+                       data = NULL;
+               }
+
+               /* move to the next record */
+               last_ptr = rec_ptr;
+               lastrec = rec;
+               rec_ptr = rec.next;
+       }
+
+ fail:
+       if (data) free(data);
+       tdb_unlock(tdb, BUCKET(hash));
+       return -1;
+
+ fail2:
+       if (data) free(data);
+       tdb_unlock(tdb, -1);
+       return -1;
+}
+
+
+/* store an element in the database, replacing any existing element
+   with the same key 
+
+   return 0 on success, -1 on failure
+*/
+int tdb_store(TDB_CONTEXT *tdb, TDB_DATA key, TDB_DATA dbuf, int flag)
+{
+       struct list_struct rec;
+       unsigned hash;
+       tdb_off rec_ptr, offset;
+       char *p = NULL;
+
+        if (tdb == NULL) {
+#ifdef TDB_DEBUG
+            printf("tdb_store() called with null context\n");
+#endif
+            return -1;
+        }
+
+       /* find which hash bucket it is in */
+       hash = tdb_hash(&key);
+
+       /* check for it existing */
+       if (flag == TDB_INSERT && tdb_exists(tdb, key)) {
+               tdb->ecode = TDB_ERR_EXISTS;
+               return -1;
+       }
+
+       /* first try in-place update */
+       if (flag != TDB_INSERT && tdb_update(tdb, key, dbuf) == 0) {
+               return 0;
+       }
+
+       rec_ptr = tdb_allocate(tdb, key.dsize + dbuf.dsize);
+       if (rec_ptr == 0) {
+               return -1;
+       }
+
+       tdb_lock(tdb, BUCKET(hash));
+
+       /* delete any existing record - if it doesn't exist we don't care */
+       if (flag != TDB_INSERT) {
+               tdb_delete(tdb, key);
+       }
+
+       /* read the newly created record */
+       if (tdb_read(tdb, rec_ptr, (char *)&rec, sizeof(rec)) == -1) {
+               goto fail;
+       }
+
+       if (rec.magic != TDB_FREE_MAGIC) goto fail;
+
+       /* find the top of the hash chain */
+       offset = tdb_hash_top(tdb, hash);
+
+       /* read in the hash top diretcly into our next pointer */
+       if (ofs_read(tdb, offset, &rec.next) == -1) {
+               goto fail;
+       }
+
+       rec.key_len = key.dsize;
+       rec.data_len = dbuf.dsize;
+       rec.full_hash = hash;
+       rec.magic = TDB_MAGIC;
+
+       p = (char *)malloc(sizeof(rec) + key.dsize + dbuf.dsize);
+       if (!p) {
+               tdb->ecode = TDB_ERR_OOM;
+               goto fail;
+       }
+
+       memcpy(p, &rec, sizeof(rec));
+       memcpy(p+sizeof(rec), key.dptr, key.dsize);
+       memcpy(p+sizeof(rec)+key.dsize, dbuf.dptr, dbuf.dsize);
+
+       if (tdb_write(tdb, rec_ptr, p, sizeof(rec)+key.dsize+dbuf.dsize) == -1)
+               goto fail;
+
+       free(p); 
+       p = NULL;
+
+       /* and point the top of the hash chain at it */
+       if (ofs_write(tdb, offset, &rec_ptr) == -1) goto fail;
+
+       tdb_unlock(tdb, BUCKET(hash));
+       return 0;
+
+ fail:
+#if TDB_DEBUG
+       printf("store failed for hash 0x%08x in bucket %u\n", hash, BUCKET(hash));
+#endif
+       if (p) free(p);
+       tdb_unlock(tdb, BUCKET(hash));
+       return -1;
+}
+
+
+/* open the database, creating it if necessary 
+
+   The open_flags and mode are passed straight to the open call on the database
+   file. A flags value of O_WRONLY is invalid
+
+   The hash size is advisory, use zero for a default value. 
+
+   return is NULL on error
+*/
+TDB_CONTEXT *tdb_open(char *name, int hash_size, int tdb_flags,
+                     int open_flags, mode_t mode)
+{
+       TDB_CONTEXT tdb, *ret;
+       struct stat st;
+
+       memset(&tdb, 0, sizeof(tdb));
+
+       tdb.fd = -1;
+       tdb.name = NULL;
+       tdb.map_ptr = NULL;
+
+       if ((open_flags & O_ACCMODE) == O_WRONLY) {
+               goto fail;
+       }
+
+       if (hash_size == 0) hash_size = DEFAULT_HASH_SIZE;
+
+       tdb.read_only = ((open_flags & O_ACCMODE) == O_RDONLY);
+
+        if (name != NULL) {
+            tdb.fd = open(name, open_flags, mode);
+            if (tdb.fd == -1) {
+               goto fail;
+            }
+        }
+
+       /* ensure there is only one process initialising at once */
+       tdb_brlock(&tdb, GLOBAL_LOCK, LOCK_SET, F_WRLCK, F_SETLKW);
+       
+       if (tdb_flags & TDB_CLEAR_IF_FIRST) {
+               /* we need to zero the database if we are the only
+                  one with it open */
+               if (tdb_brlock(&tdb, ACTIVE_LOCK, LOCK_SET, F_WRLCK, F_SETLK) == 0) {
+                       ftruncate(tdb.fd, 0);
+                       tdb_brlock(&tdb, ACTIVE_LOCK, LOCK_CLEAR, F_WRLCK, F_SETLK);
+               }
+       }
+
+       /* leave this lock in place */
+       tdb_brlock(&tdb, ACTIVE_LOCK, LOCK_SET, F_RDLCK, F_SETLKW);
+
+       if (read(tdb.fd, &tdb.header, sizeof(tdb.header)) != sizeof(tdb.header) ||
+           strcmp(tdb.header.magic_food, TDB_MAGIC_FOOD) != 0 ||
+           tdb.header.version != TDB_VERSION) {
+               /* its not a valid database - possibly initialise it */
+               if (!(open_flags & O_CREAT)) {
+                       goto fail;
+               }
+               if (tdb_new_database(&tdb, hash_size) == -1) goto fail;
+
+               lseek(tdb.fd, 0, SEEK_SET);
+               if (tdb.fd != -1 && read(tdb.fd, &tdb.header, 
+                                         sizeof(tdb.header)) != 
+                                         sizeof(tdb.header)) 
+                    goto fail;
+       }
+
+        if (tdb.fd != -1) {
+            fstat(tdb.fd, &st);
+
+            /* map the database and fill in the return structure */
+            tdb.name = (char *)strdup(name);
+            tdb.map_size = st.st_size;
+        }
+
+        tdb.locked = (int *)calloc(tdb.header.hash_size+1, 
+                                   sizeof(tdb.locked[0]));
+        if (!tdb.locked) {
+            goto fail;
+        }
+
+#if HAVE_MMAP
+        if (tdb.fd != -1) {
+            tdb.map_ptr = (void *)mmap(NULL, st.st_size, 
+                                       tdb.read_only? PROT_READ : PROT_READ|PROT_WRITE,
+                                       MAP_SHARED | MAP_FILE, tdb.fd, 0);
+        }
+#endif
+
+       ret = (TDB_CONTEXT *)malloc(sizeof(tdb));
+       if (!ret) goto fail;
+
+       *ret = tdb;
+
+#if TDB_DEBUG
+       printf("mapped database of hash_size %u map_size=%u\n", 
+              hash_size, tdb.map_size);
+#endif
+
+       tdb_brlock(&tdb, GLOBAL_LOCK, LOCK_CLEAR, F_WRLCK, F_SETLKW);
+       return ret;
+
+ fail:
+        if (tdb.name) free(tdb.name);
+       if (tdb.fd != -1) close(tdb.fd);
+       if (tdb.map_ptr) munmap(tdb.map_ptr, tdb.map_size);
+
+       return NULL;
+}
+
+/* close a database */
+int tdb_close(TDB_CONTEXT *tdb)
+{
+       if (!tdb) return -1;
+
+       if (tdb->name) free(tdb->name);
+       if (tdb->fd != -1) close(tdb->fd);
+       if (tdb->locked) free(tdb->locked);
+
+       if (tdb->map_ptr) {
+            if (tdb->fd != -1) {
+                munmap(tdb->map_ptr, tdb->map_size);
+            } else {
+                free(tdb->map_ptr);
+            }
+        }
+
+       memset(tdb, 0, sizeof(*tdb));
+       free(tdb);
+
+       return 0;
+}
+
+/* lock the database. If we already have it locked then don't do anything */
+int tdb_writelock(TDB_CONTEXT *tdb)
+{
+        if (tdb == NULL) {
+#ifdef TDB_DEBUG
+            printf("tdb_writelock() called with null context\n");
+#endif
+            return -1;
+        }
+
+       return tdb_lock(tdb, -1);
+}
+
+/* unlock the database. */
+int tdb_writeunlock(TDB_CONTEXT *tdb)
+{
+        if (tdb == NULL) {
+#ifdef TDB_DEBUG
+            printf("tdb_writeunlock() called with null context\n");
+#endif
+            return -1;
+        }
+
+       return tdb_unlock(tdb, -1);
+}
+
+/* lock one hash chain. This is meant to be used to reduce locking
+   contention - it cannot guarantee how many records will be locked */
+int tdb_lockchain(TDB_CONTEXT *tdb, TDB_DATA key)
+{
+        if (tdb == NULL) {
+#ifdef TDB_DEBUG
+            printf("tdb_lockchain() called with null context\n");
+#endif
+            return -1;
+        }
+
+       return tdb_lock(tdb, BUCKET(tdb_hash(&key)));
+}
+
+
+/* unlock one hash chain */
+int tdb_unlockchain(TDB_CONTEXT *tdb, TDB_DATA key)
+{
+        if (tdb == NULL) {
+#ifdef TDB_DEBUG
+            printf("tdb_unlockchain() called with null context\n");
+#endif
+            return -1;
+        }
+
+       return tdb_unlock(tdb, BUCKET(tdb_hash(&key)));
+}
diff --git a/pppd/tdb.h b/pppd/tdb.h
new file mode 100644 (file)
index 0000000..56ae0ac
--- /dev/null
@@ -0,0 +1,77 @@
+#define STANDALONE     1
+/* 
+ * Database functions
+ * Copyright (C) Andrew Tridgell 1999
+ * 
+ * Redistribution and use in source and binary forms are permitted
+ * provided that the above copyright notice and this paragraph are
+ * duplicated in all such forms AND provided that this software or
+ * any derived work is only used as part of the PPP daemon (pppd)
+ * and related utilities.
+ * The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
+ * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
+ *
+ * Note: this software is also available under the Gnu Public License
+ * version 2 or later.
+ */
+
+typedef unsigned tdb_len;
+typedef unsigned tdb_off;
+
+#define TDB_MAGIC_FOOD "TDB file\n"
+
+/* this is stored at the front of every database */
+struct tdb_header {
+       char magic_food[32]; /* for /etc/magic */
+       unsigned version; /* version of the code */
+       unsigned hash_size; /* number of hash entries */
+};
+
+typedef struct {
+       char *dptr;
+       size_t dsize;
+} TDB_DATA;
+
+/* this is the context structure that is returned from a db open */
+typedef struct {
+       char *name; /* the name of the database */
+       void *map_ptr; /* where it is currently mapped */
+       int fd; /* open file descriptor for the database */
+       tdb_len map_size; /* how much space has been mapped */
+       int read_only; /* opened read-only */
+       int *locked; /* set if we have a chain locked */
+       int ecode; /* error code for last tdb error */
+       struct tdb_header header; /* a cached copy of the header */
+} TDB_CONTEXT;
+
+/* flags to tdb_store() */
+#define TDB_REPLACE 1
+#define TDB_INSERT 2
+
+/* flags for tdb_open() */
+#define TDB_CLEAR_IF_FIRST 1
+
+/* error codes */
+enum TDB_ERROR {TDB_SUCCESS=0, TDB_ERR_CORRUPT, TDB_ERR_IO, TDB_ERR_LOCK, 
+               TDB_ERR_OOM, TDB_ERR_EXISTS};
+
+#if STANDALONE
+TDB_CONTEXT *tdb_open(char *name, int hash_size, int tdb_flags,
+                     int open_flags, mode_t mode);
+char *tdb_error(TDB_CONTEXT *tdb);
+int tdb_writelock(TDB_CONTEXT *tdb);
+int tdb_writeunlock(TDB_CONTEXT *tdb);
+TDB_DATA tdb_fetch(TDB_CONTEXT *tdb, TDB_DATA key);
+int tdb_delete(TDB_CONTEXT *tdb, TDB_DATA key);
+int tdb_store(TDB_CONTEXT *tdb, TDB_DATA key, TDB_DATA dbuf, int flag);
+int tdb_close(TDB_CONTEXT *tdb);
+TDB_DATA tdb_firstkey(TDB_CONTEXT *tdb);
+TDB_DATA tdb_nextkey(TDB_CONTEXT *tdb, TDB_DATA key);
+int tdb_traverse(TDB_CONTEXT *tdb, 
+       int (*fn)(TDB_CONTEXT *tdb, TDB_DATA key, TDB_DATA dbuf, void *state),
+       void *state);
+int tdb_exists(TDB_CONTEXT *tdb, TDB_DATA key);
+#endif