"Fossies" - the Fresh Open Source Software Archive

Member "rgdbm-2.1.42/gdbmd.c" (21 Jun 2007, 59109 Bytes) of package /linux/privat/old/rgdbm-2.1.42.tgz:


As a special service "Fossies" has tried to format the requested source page into HTML format using (guessed) C and C++ source code syntax highlighting (style: standard) with prefixed line numbers and code folding option. Alternatively you can here view or download the uninterpreted source code file. For more information about "gdbmd.c" see the Fossies "Dox" file reference documentation.

    1 /* 
    2  * RGDBM gdbmd daemon code (C) Peter T. Breuer 2007.
    3  *
    4  * This code is releasd under the GNU General Public Licence (GPL)
    5  * version 2 or such later version as the FSF may release in the
    6  * fullness of time (when this licence automatically becomes THAT
    7  * licence). See GPL_V2 file in the code archive for details.
    8  *
    9  * This file implements a daemon that handles incoming connections from
   10  * an rgdbm library API on a remote machine. We listen on a TCP port,
   11  * and split off a TCP (or SSL) socket and thread to handle the
   12  * incoming, in time-honoured unix fashion. We do NOT run as root, and
   13  * we do NOT do chroot, setuid or other rooty things. We do "go
   14  * daemon", i.e. cd to /, and shunt all stdio to /dev/null, start a new
   15  * session group with us as leader, etc (is there an etc?).
   16  *
   17  * We run as a special user probably with home if anywhere in
   18  * /var/lib/gdbm. Any incoming must issue a CONNECT or a HANDSHAKE
   19  * request the first thing they do and get themselves authenticated and
   20  * the session open. Anything else will fail because the session isn't
   21  * open yet, and any * failure causes the daemon thread dealing with the
   22  * incoming to die.
   23  *
   24  * Authentication is against fake system passwd and group files in
   25  * /var/lib/gdbm/ctrl, by default. The CONNECT request is the old
   26  * authentication mode in which passwords come across in the clear
   27  * (possibly under SSL :) and the HANDSHAKE request is the new
   28  * authentication mode in which passwords come across encrypted.
   29  * Nowadays the client will not attempt CONNECT if the password is
   30  * nonempty. I suppose the daemon is always willing to respond,
   31  * however.
   32  *
   33  * All requests consist of a header, and body, and a magic stop codon.
   34  * The header itself begins with a start codon. Incorrect magic
   35  * will cause the request to be errored, but the connection won't shut
   36  * down. Instead it will try and resynchronise by going past the next
   37  * stop codon (which is the one still to come in this request if the error
   38  * happened early in the stream) on the incoming stream.  So with luck
   39  * just one request will get errored.  "Get errored" means that an error
   40  * reply will be sent back over the net. The body of a request consists
   41  * of zero or more datums, each consisting of a 4-byte size followed by raw
   42  * data to that length in bytes.
   43  *
   44  * The header contains the request type and the count of following datums.
   45  *
   46  * The CONNECT and HANDSHAKE requests follow the same format as all
   47  * other requests, i.e. that just described above.
   48  *
   49  * CONNECT and HANDSHAKE both have the same overall format. They
   50  * both contain in their body, in this order:
   51  *
   52  *    1) a datum holding the requested working directory, dir
   53  *    2) a datum holding a user[.group][/password] authentication string.
   54  *
   55  * The differences are in timing. The CONNECT sends all this data to us at
   56  * once and then waits for our response (an error reply or a non-error
   57  * reply .. those are distinguished only by the result value carried in
   58  * the reply, which is negative for an error).
   59  *
   60  * The HANDSHAKE sends all data up to and including the "/' of the
   61  * "/password", and then pauses. We reply early with an OK reply at
   62  * this point, but the reply conceals a salt in various parts of the
   63  * reply header. That salt is what we have the putative authenticating
   64  * user's password encoded with in the passwd file on disk in our ctrl
   65  * area. We also send along some randomly generated bytes concealed
   66  * elsewhere in the reply header.
   67  *
   68  * The client on the other side then encrypts the clear password with
   69  * our salt to get the same encrypted password as we have on disk. They
   70  * don't send it across like that, though.
   71  *
   72  * The client encrypts the encrypted password a second time
   73  * using our randomly generated data, and sends the result across.
   74  * We encrypt our version of the encrypted password as we have it in
   75  * the paswrod file using the same randomly generated data as we sent
   76  * over and compare. If it matches, we have authenticated the incomer.
   77  *
   78  * If both sides only have unix crypt available, not md5-enabled crypt,
   79  * then this is fairly weak protection against replay attacks, becuase
   80  * there are only 64^2 = 4096 different final encodings that can be requested
   81  * from the client. However, usually md5 encoding will be available at
   82  * least on the client side, which allows us to use up to 8 bytes in
   83  * requesting an encoding from the client, or 64^8 = 2^48 different
   84  * encodings.
   85  *
   86  * If we have the password encoded on disk with md5-crypt, and the
   87  * client does not have md5-crypt, then there will be trouble as the
   88  * client will not be able to authenticate via HANDSHAKE as it won't be
   89  * able to reproduce our encrypted password internally. (TODO - fix that 
   90  * with some code in aux.c to help the client do md5-crypt).
   91  *
   92  * We set bit 30 (the penultimate, nearly most significant, bit) of the
   93  * result field in the reply to a HANDSHAKE to indicate that we need
   94  * the client to use md5-crypt to obtain the encrypted password from
   95  * out salt. It's unset if unix-crypt is being requested of the client
   96  * (because that's what we have). The lower 30 bits (below bit 30)
   97  * contain the first 5 chars of the salt (possibly padded, if it is a
   98  * unix salt, which only has 2 chars) at 6 bits per chars. The chars
   99  * are a-zA-Z0-9./ and are coded in that order, first char in the
  100  * lowest bits.
  101  *
  102  * The remaining 18 bits of a md5-salt are in the reply type field,
  103  * offset 12 bits from the low end, so they can't affect the sign and
  104  * that leaves 2^12 = 4096 different kinds of reply/request types that
  105  * may be delivered.
  106  *
  107  * Other requests are the mundane kind expected to be able to do remote
  108  * gdbm(3). FETCH
  109  *
  110  */
  111 
  112 
  113 #include "config.h"
  114 
  115 #include <stdio.h>
  116 #if STDC_HEADERS
  117 # include <stdlib.h>
  118 # include <stdarg.h>
  119 # include <string.h>
  120 #else
  121 # ifndef HAVE_STRCHR
  122 #  define strchr index
  123 #  define strrchr rindex
  124 # endif
  125 char *strchr (), *strrchr ();
  126 # ifndef HAVE_MEMCPY
  127 #  define memcpy(d, s, n) bcopy ((s), (d), (n))
  128 #  define memmove(d, s, n) bcopy ((s), (d), (n))
  129 # endif
  130 #endif          /* STDC_HEADERS */
  131 #ifdef HAVE_SYS_TYPES_H
  132 # include <sys/types.h>          /* for read, write etc. */
  133 #endif          /* defined(HAVE_SYS_TYPES_H) */
  134 #ifdef HAVE_UNISTD_H
  135 # include <unistd.h>             /* also for read, write etc. */
  136 #endif          /* defined(HAVE_UNISTD_H) */
  137 char *crypt(const char *key, const char *salt); // easier than _XOPEN_SOURCE
  138 
  139 #include <sys/stat.h>           /* for stat struct */
  140 #include <sys/socket.h>         /* for SOL_* */
  141 #include <errno.h>              /* for EINVAL etc. */
  142 
  143 /* datum is typedef'd in each of dbm,h ndbm.h gdbm.h, so can only use one ! */
  144 
  145 #ifdef HAVE_GDBM_H         /* won't get far without gdbm.h */
  146 # include <gdbm.h>
  147 #elif defined(HAVE_NDBM_H) /* worth checking for ndbm too */
  148 # include <ndbm.h>
  149 #elif defined(HAVE_DBM_H)  /* even worth checking for dbm */
  150 # include <dbm.h>
  151 #endif
  152 
  153 /*
  154  * We won't include dbm.h if we have either of gdbm.h or ndbm.h, which
  155  * are better but incompatible, so include its protos here as we might
  156  * still want to fall back to using its functions.
  157  */
  158 #if (defined(HAVE_GDBM_H) || defined(HAVE_NBDM_H))
  159 extern int      dbminit (char *name);
  160 extern datum    fetch (datum key);
  161 extern int      store (datum key, datum content);
  162 extern int      delete (datum key);
  163 extern datum    firstkey (void);
  164 extern datum    nextkey (datum key);
  165 extern int      dbmclose (void);
  166 #endif /* (defined(HAVE_GDBM_H)||defined(HAVE_NBDM_H)) */
  167 
  168 /*
  169  * We won't include ndbm.h if we have gdbm.h, which
  170  * are better but incompatible, so include its protos here as we might
  171  * still want to fall back to using its functions.
  172  */
  173 #if defined(HAVE_GDBM_H)
  174                            /* won't include ndbm.h, so give its protos here */
  175 
  176 typedef char DBM;          /* we only use DBM * - so this is good enough */
  177 
  178                            /* may need to define storage modes */
  179 # ifndef DBM_INSERT
  180 #  define  DBM_INSERT  0
  181 # endif /* !defined(DBM_INSERT) */
  182 
  183 # ifndef DBM_REPLACE
  184 #  define  DBM_REPLACE 1
  185 # endif /* !defined(DBM_REPLACE) */
  186 
  187 extern DBM *    dbm_open(char *name, int flags, int mode);
  188 extern void     dbm_close(DBM *file);
  189 extern datum    dbm_fetch(DBM *file, datum key);
  190 extern int      dbm_store(DBM *file, datum key, datum content, int flags);
  191 extern int      dbm_delete(DBM *file, datum key);
  192 extern datum    dbm_firstkey(DBM *file);
  193 extern datum    dbm_nextkey(DBM *file, datum key);
  194 extern int      dbm_error(DBM *file);
  195 extern int      dbm_clearerr(DBM *file);
  196 # ifndef dbm_error
  197 #  define          dbm_error(dbf)  (0)
  198 # endif
  199 # ifndef dbm_clearerr
  200 #  define          dbm_clearerr(dbf) (0)
  201 # endif
  202 extern int      dbm_pagfno(DBM *file);
  203 extern int      dbm_dirfno(DBM *file);
  204 extern int      dbm_rdonly(DBM *file);
  205 #endif /*defined(HAVE_GDBM_H) */
  206 
  207 #include <pwd.h>                /* for passwd struct */
  208 #include <grp.h>                /* for group struct */
  209 #include <time.h>               /* for time(2) */
  210 #include <signal.h>             /* for SIGPIPE etc */
  211 #include <arpa/inet.h>          /* for socklen_t */
  212 #include <netinet/in.h>         /* for ntohl(3) */
  213 #include <netinet/tcp.h>        /* for TCP_CORK, TCP_LINGER2*/
  214 
  215 #include "my.h"
  216 #include "auth.h"
  217 #include "make.h"
  218 #include "files.h"
  219 
  220 #include "sock.h"
  221 #include "io.h"                 /* for MAGIC */
  222 #include "rgdbm.h"
  223 #include "xfr.h"
  224 #include "sps.h"
  225 #include "sup.h"
  226 #include "gdbmd.h"
  227 
  228 #ifndef HAVE_TYPE_SOCKLEN_T
  229 typedef unsigned socklen_t;
  230 #endif          /* defined(HAVE_TYPE_SOCKLEN_T) */
  231 
  232 #define GDBMD_PERR(f, s...)  \
  233    ({ \
  234      gdbmd_errlen = \
  235        snprintf(gdbmd_error, sizeof(gdbmd_error), "%s#%d %s:\n\t" f, __FILE__, __LINE__, __FUNCTION__ , ## s); \
  236      fprintf(stderr, "%s", gdbmd_error); \
  237    })
  238 #define GDBMD_ADD_PERR(f, s...)  \
  239    ({ \
  240      int __n = \
  241        snprintf(gdbmd_error + gdbmd_errlen, sizeof(gdbmd_error) - gdbmd_errlen, "%s#%d %s:\n\t" f, __FILE__, __LINE__, __FUNCTION__ , ## s); \
  242      fprintf(stderr, "%s", gdbmd_error + gdbmd_errlen); \
  243      gdbmd_errlen += __n; \
  244    })
  245 
  246 
  247 #if DEBUG > 0
  248 #  define GDBMD_DEBUG(f, s...) \
  249    GDBMD_PERR("(DEBUG) " f , ## s)
  250 #else
  251 #  define GDBMD_DEBUG(f, s...) \
  252    /* nada */
  253 #endif
  254 #define GDBMD_WARN(f, s...) \
  255    GDBMD_PERR("(WARNING)" f , ## s)
  256 #define GDBMD_INFO(f, s...) \
  257    GDBMD_PERR("(INFO) " f , ## s)
  258 #define GDBMD_ERROR(f, s...) \
  259    GDBMD_PERR("(ERROR) " f , ## s)
  260 #define GDBMD_ADD_ERROR(f, s...) \
  261    GDBMD_ADD_PERR("(ERROR) " f , ## s)
  262 
  263 
  264 /* --------------------- begin globals --------------------------- */
  265 
  266 /*
  267  * list of gdbm database handles that may be open at one time. GDBMD
  268  * _FILES_MAX is defined in gdbmd.h.
  269  */
  270 #ifdef HAVE_GDBM_H
  271 static GDBM_FILE file[GDBMD_FILES_MAX];
  272 #endif /* defined(HAVE_GDBM_H) */
  273 #ifdef HAVE_NDBM_H
  274 static DBM * dbm_file[GDBMD_FILES_MAX];
  275 #endif /* defined(HAVE_NDBM_H) */
  276 #ifdef HAVE_DBM_H
  277 static int db_file[GDBMD_FILES_MAX];
  278 static int db_opened; // count
  279 #endif /* defined(HAVE_DBM_H) */
  280 /*
  281  * total number of files open in session
  282  */
  283 static int file_count;
  284 /*
  285  *  index of last file mentioned
  286  */
  287 static int last_file = -1;
  288 
  289 /*
  290  * global params possibly reset by options, and the defaults for them
  291  */
  292 char *rgdbm_datadir = GDBMD_DATADIR; // dflt working dir. See gdbmd.h
  293 char *rgdbm_ctrldir = GDBMD_CTRLDIR; // dflt control dir. See gdbmd.h
  294 
  295 static int godaemon = 1;
  296 static short port   = RGDBM_PORT;    // dflt listening port. See io.h
  297 
  298 /*
  299  * The last error message is stored here in a buffer, for possible
  300  * retrieval
  301  */
  302 static char gdbmd_error[1024];
  303 static int  gdbmd_errlen;
  304 
  305 /*
  306  * fake values for our connecting thread. This is who the incoming
  307  * session says it is, and who we have authenticated it as (against
  308  * passwd files in the ctrl area, not system files).
  309  */
  310 static int uid = -1;
  311 static int gid = -1;
  312 
  313 /*
  314  * A simple 1/0 boolean to say if this session is live or not.
  315  * We mustn't and can't connect twice at once in a single thread.
  316  */
  317 static int connected;
  318 
  319 /* --------------------- end   globals --------------------------- */
  320 
  321 
  322 
  323 
  324 /*
  325  * send a message @msg in reply to request @req, containing result
  326  * value @res.
  327  */
  328 static int
  329 rgdbm_send_msg_reply(struct io_ctx *ctx, int res, struct rgdbm_request *req, char *msg) 
  330 {
  331     datum content; // we'll fill this and send it back in the reply
  332 
  333     content.dsize = msg ? strlen(msg) + 1 : 0;
  334     content.dptr  = msg;
  335 
  336     return rgdbm_send_reply(ctx, res, req, 1, content);
  337 }
  338 
  339 /*
  340  * send the last error message in reply to request @req, containing result
  341  * value @err.
  342  */
  343 static int
  344 rgdbm_send_error_reply(struct io_ctx *ctx, int err, struct rgdbm_request *req) 
  345 {
  346     return rgdbm_send_msg_reply(ctx, err, req, gdbmd_error);
  347 }
  348 
  349 /*
  350  * safely get a string from a datum, including final NUL. Check it's OK.
  351  * This makes nothing new .. simply checks the packaging is OK.
  352  */
  353 static char *
  354 getstr (datum x)
  355 {
  356 
  357     if (!x.dptr) {
  358         if (x.dsize != 0)
  359             GDBMD_ERROR ("cannot verify NULL string\n");
  360         return NULL;
  361     }
  362     if (x.dsize <= 0) {
  363         GDBMD_ERROR ("cannot verify string length %d\n", x.dsize);
  364         return NULL;
  365     }
  366     if (x.dptr[x.dsize - 1]) {
  367         GDBMD_ERROR ("string '%*s...' has no terminating null\n",
  368                      x.dsize, x.dptr);
  369         return NULL;
  370     }
  371     return x.dptr;
  372 }
  373 
  374 #ifndef MIN
  375 # define MIN(x,y) ({ typeof(x) _x = (x); typeof(y) _y = (y); _x<_y?_x:_y; })
  376 #endif /* !defined(MIN) */
  377 
  378 /*
  379  * return 0 or 1 for success. 0 means that this was an ordinary "get
  380  * header, datums, stop bits". 1 means that we just got the header. -ve
  381  * means a failure. If we return 1. then somebody else will have to go
  382  * get the body and stop bits. We get the header only whenever we
  383  * receive a HANDSHAKE, for the first time. Otherwise we get all. That
  384  * allows the handshake to control what goes by bit by bit.
  385  */
  386 static int
  387 start_request (struct io_ctx *ctx, struct rgdbm_request *req, datum * key,
  388                   datum * content)
  389 {
  390     int err;    // internal error value
  391 
  392     if (!req)
  393         return -EINVAL;
  394 
  395     // prepare to get and send partial data
  396 
  397     err = rgdbm_get_request_header (ctx, req);
  398     if (err < 0)
  399         return err;
  400 
  401     if (req->type != RGDBM_HANDSHAKE) {
  402 
  403         // Get rest of request as usual
  404 
  405         datum *d[2] = { key, content, };
  406 
  407         err = rgdbm_get_request_trailer (ctx, req->count, 2, d);
  408         if (err < 0)
  409             return err;
  410     }
  411 
  412     // drop through to switch, and remember to set seqno after
  413     return 1;
  414 }
  415 
  416 /*
  417  * set the working dir, user id and group id of the authenticated
  418  * incoming user in this thread.
  419  */
  420 static int
  421 setsession(char *dir, uid_t new_uid, gid_t new_gid) {
  422 
  423     int err;  // internal error value
  424 
  425     // chdir Make the dir if necessary.
  426     err = chdir (dir);
  427     if (err < 0) {
  428         err = mkdir (dir, 0750);
  429         if (err < 0) {
  430             err = -errno;
  431             GDBMD_ERROR ("could not cd %s\n", dir);
  432             return err;
  433         }
  434     }
  435 
  436     // set fake thread owner uid and group gid
  437     uid = new_uid;
  438     gid = new_gid;
  439     return 0;
  440 }
  441 
  442 
  443 /*
  444  * authenticate a login to @dir by @user.@group with @pass.
  445  *
  446  * If successful, chdir to @dir and set uid and gid globals for this
  447  * thread. Also set the connect global if successful. Danger Will
  448  * Robinson.
  449  *
  450  * return >= 0 for success, < 0 for failure. We leave a message if we
  451  * fail.
  452  *
  453  * This is intended to be used in the treatment of CONNECT.
  454  */
  455 static int
  456 authconnect (char *dir, char *user, char *group, char *pass)
  457 {
  458     int err;            // internal error value
  459     char *tmp;          // holds temporary strings
  460     struct passwd *pwe; // an entry in passwd file
  461 
  462     if (connected) {
  463         GDBMD_ERROR ("already connected!\n");
  464         return -EINVAL;
  465     }
  466 
  467     // look up passwd for user locally
  468     if (pwe = auth (user, pass), !pwe) {
  469         GDBMD_ERROR ("authorisztion for user %s refused\n", user);
  470         return -EINVAL;
  471     }
  472 
  473     // make the dir absolute
  474     tmp = authdir (dir, pwe->pw_uid);
  475     if (!tmp) {
  476         GDBMD_ERROR ("authorisztion for dir %s refused\n", dir);
  477         return -EINVAL;
  478     }
  479     dir = tmp;
  480 
  481     err = setsession(dir, pwe->pw_uid,
  482       group? ({ 
  483         struct group *gre = mygetgrnam (group);
  484         gid_t this_gid;
  485         // use principal group if don't know the one supplied
  486         this_gid = gre ? gre->gr_gid : pwe->pw_gid;
  487         if (gre)
  488             free (gre);
  489         this_gid;
  490       }) : pwe->pw_gid
  491     );
  492     free (dir);
  493 
  494     if (err < 0)
  495         return err;
  496 
  497     // success
  498     connected = 1;
  499     return 0;
  500 }
  501 
  502 /* get the encrypted password (but don't authenticate it)
  503  * in a HANDSHAKE exchange.
  504  *
  505  * We have already got the handshake request header and the dir and the
  506  * user[.group][/passwd] info up to and including the '/'. So we now
  507  * send out an OK reply with the salt info for that user hidden in the 
  508  * reply. For the unix crypt kind of password, we send out the 12 bits
  509  * significant in the 2char salt in the result field (while keeping it
  510  * positive). For an md5 password, we only have 31 bits available
  511  * there, and there are up to, I suppose, 8*6 = 48 bits in a md5 salt,
  512  * so we have a problem! But we could pinch some from the type or the
  513  * seqno field, as this will only be the 0th request. Type is probably
  514  * safest.
  515  */
  516 static int64_t
  517 get_pass(struct io_ctx *ctx, int len, char *user)
  518 {
  519     int err;                // internal error value
  520     int n;                  // counter along incoming auth string
  521     struct passwd *pwe;     // passwd file entry
  522     uint64_t salt;          // either 8 or 2 6-bit items
  523     struct rgdbm_reply rpl; // we send out this reply header
  524 
  525     if (!user)
  526         return -EINVAL;
  527 
  528     for (n = 0; n < len; n++) {
  529         err = rgdbm_get_datum_data(ctx, 1, user + n);
  530         if (err < 0)
  531             return err;
  532         if (user[n] == '/')
  533             break;
  534     }
  535 
  536     if (n >= len)   // no pass
  537         return -EINVAL;
  538 
  539     // looks like it was unix
  540     user[n] = 0; 
  541 
  542     pwe = mygetpwnam(user);
  543 
  544     user[n] = '/'; 
  545  
  546     if (!pwe)
  547         return -EINVAL; // .. don't know user.
  548 
  549     if (strncmp(pwe->pw_passwd, "$1$", 3) == 0) {
  550 
  551         // md5 passwd here
  552         int i;
  553         salt = 0;
  554 
  555         for (i = 0; i < 8 && pwe->pw_passwd[i+3] && pwe->pw_passwd[i+3] != '$'; i++) {
  556             salt |= ((uint64_t)alpha2num64(pwe->pw_passwd[i+3])) << (6*i);
  557         }
  558         // md5 passwd here
  559 
  560         // 8 bytes salt is the maximum. 
  561         salt |= 1LL << 62; // set bit 62
  562 
  563     } else {
  564         // unix passwd here
  565         // return 12 bits of salt in the result value
  566         salt = alpha2num64(pwe->pw_passwd[1]) << 6
  567              | alpha2num64(pwe->pw_passwd[0]);
  568 
  569 
  570         // can fill the remaining 36 bits, or at least some of them, with a
  571         // key to use in the client!
  572 
  573         salt |= ((uint64_t) random() ) << 12;
  574         if (RAND_MAX < (1<<30) - 1 || RAND_MAX < (1LL<<36) - 1) {
  575             // need at least 6 bits more randomness 
  576             salt |= ((uint64_t) random() ) << 42;
  577         }
  578         salt &= ~(3LL << 62); // unix signalled by bit 62 unset
  579     }
  580 
  581     // maybe want a sign that this is unix password in there too
  582 
  583     // send an initial server hello.
  584     rpl.seqno = 0;
  585     rpl.count = 0;
  586     // the 30 bottom bits of the salt are in the result
  587     rpl.result = salt & 0x3fffffffLL; // 30 bits
  588     if (salt & (1LL << 62)) {
  589         rpl.result |= 1 << 30; // set bit 30 for md5
  590     }
  591     // the remaining 18 bits of the 48 bit salt at 12bit offset in the type
  592     rpl.type = RGDBM_HANDSHAKE | (((salt >> 30) & ((1<<18)-1)) << 12);
  593     err = rgdbm_send_reply_header(ctx, &rpl);
  594     if (err < 0)
  595         return err;
  596 
  597     // at this point the server is still due to send out a stop codon later
  598     // now get the doubly encrypted passwd, since we gave clue
  599 
  600     err = rgdbm_get_datum_data(ctx, len - n - 1, user + n + 1);
  601     if (err < 0)
  602         return err;
  603 
  604     // get the client's stop
  605     err = rgdbm_get_stop(ctx);
  606     if (err < 0)
  607         return err;
  608                 
  609     return salt;
  610 }
  611 
  612 /*
  613  * Authenticate @user for access to @dir given ENCRYPTED password 2pass
  614  *
  615  * the other side sends us an encrypted version of OUR unix-enccrypted
  616  * password. we have to decrypt it and check it, for which we probably
  617  * need to have the other side use the encrypted password and salt as a
  618  * key to encode a known phrase with, and we check that.
  619  *
  620  * The encrypted password and salt itself provides 64*64 (salt)
  621  * and presumably there are then (128 - 32 - 1) = 95 printable
  622  * characters repeated 11 times in the crypted part of the passwd.
  623  * Though it looks like there are only 64 possible outcomes per letter
  624  * in that too, which would produce 64^11 or 2^88 bits of apparanet
  625  * randomness. A DES key is 8 odd parity bytes, or 2^56 bits. I guess
  626  * one could extract 56 bits from the 88 by throwing away 32, which is
  627  * an integer length.
  628  */
  629 static int
  630 authcryptconnect (char *dir, char *user, char *group, char *pass, uint64_t seed)
  631 {
  632     int err;
  633     struct passwd *pwe;
  634     char *tmp;
  635 
  636     pwe = mygetpwnam(user);
  637 
  638     if (!pwe) {
  639         // .. don't know user. Get the rest of the data and
  640         GDBMD_ERROR ("could not recognize user %s\n", user);
  641         return -EINVAL;
  642     }
  643 
  644     // make the dir absolute
  645     tmp = authdir (dir, pwe->pw_uid);
  646     if (!tmp) {
  647         GDBMD_ERROR ("not authorised to enter dir %s\n", dir);
  648         return -EINVAL;
  649     }
  650     dir = tmp;
  651 
  652     // here is where we check the passwd
  653 
  654     char salt[16];
  655     char *buf = pwe->pw_passwd;
  656 
  657     int k;
  658     if (strncmp(buf,"$1$",3)==0) {
  659         k = 3 + (strchr(buf + 3, '$') - (buf + 3)) + 1;
  660     } else if (strncmp(buf,"$0$",3)==0) {
  661         k = 3 + (strchr(buf + 3, '$') - (buf + 3)) + 1;
  662     } else {
  663         k = 2;
  664     }
  665 
  666     if (strncmp(pass,"$1$",3) == 0) {
  667 
  668         salt[0]  = '$';
  669         salt[1]  = '1';
  670         salt[2]  = '$';
  671         salt[3]  = num2alpha64((seed >> 0) & 63);  // unix/md5 salt 0
  672         salt[4]  = num2alpha64((seed >> 6) & 63);  // unix/md5 salt 1
  673         salt[5]  = num2alpha64((seed >> 12) & 63); // rand/md5 salt 2
  674         salt[6]  = num2alpha64((seed >> 18) & 63); // rand/md5 salt 3
  675         salt[7]  = buf[k + ((seed >> 24) & 63) % (strlen(buf) - k)];
  676         salt[8]  = buf[k + ((seed >> 30) & 63) % (strlen(buf) - k)];
  677         salt[9]  = buf[k + ((seed >> 36) & 63) % (strlen(buf) - k)];
  678         salt[10] = buf[k + ((seed >> 42) & 63) % (strlen(buf) - k)];
  679         salt[11] = '$';
  680         salt[12] = 0;
  681         memcpy(pass + 3, salt + 3, 8);
  682 
  683     } else if (strncmp(pass,"$0$",3) == 0) {
  684         salt[0] = num2alpha64((seed >> 12) & 63);
  685         salt[1] = num2alpha64((seed >> 18) & 63);
  686         salt[2] = 0;
  687         memcpy(pass + 3, salt, 2);
  688     } else {
  689         salt[0] = num2alpha64((seed >> 12) & 63);
  690         salt[1] = num2alpha64((seed >> 18) & 63);
  691         salt[2] = 0;
  692         memcpy(pass, salt, 2);
  693     }
  694 
  695     buf = crypt(buf + k, salt);
  696     if (!buf) {
  697         GDBMD_ERROR ("could not encrypt passwd for user %s\n", user);
  698         return -EINVAL;
  699     }
  700 
  701     if (strcmp(buf, pass) != 0) {
  702         GDBMD_ERROR ("wrong passwd for user %s\n", user);
  703         return -EINVAL;
  704     }
  705 
  706     err = setsession(dir, pwe->pw_uid,
  707       group? ({ 
  708         struct group *gre = mygetgrnam (group);
  709         gid_t this_gid;
  710         // use principal group if don't know the one supplied
  711         this_gid = gre ? gre->gr_gid : pwe->pw_gid;
  712         if (gre)
  713             free (gre);
  714         this_gid;
  715       }) : pwe->pw_gid
  716     );
  717     free (dir);
  718 
  719     if (err < 0)
  720         return err;
  721 
  722     // success
  723     connected = 1;
  724 
  725     return 0;
  726 }
  727 
  728 static int
  729 loop (struct io_ctx *ctx)
  730 {
  731     int    err;
  732     struct rgdbm_request req;
  733 
  734     datum  key     = { .dsize = -1, .dptr = NULL, };
  735     datum  content = { .dsize = -1, .dptr = NULL, };
  736 
  737     void cleanup(void) {
  738 
  739         if (key.dptr >= 0 && key.dptr)
  740             free (key.dptr);
  741 
  742         if (content.dptr >= 0 && content.dptr)
  743             free (content.dptr);
  744     }
  745 
  746     GDBMD_DEBUG ("get request\n");
  747 
  748     // maybe deal with initial connect specially. Don't wait for the
  749     // whole request but send out clues to guide the rest as we receive
  750     // partial data.
  751     
  752     err = start_request(ctx, &req, &key, &content);
  753     if (err < 0) {
  754         cleanup();
  755         return err;
  756     }
  757         
  758     switch (req.type) {
  759 
  760       case RGDBM_OPEN:
  761       {
  762           char   *name; // the db to be opened
  763           int     i;
  764           // S_IRWXU|S_IRWXG|S_IRWXO  =  511
  765           int mode = req.tableno & (S_IRWXU|S_IRWXG|S_IRWXO);
  766           int bs   = req.tableno & ~(S_IRWXU|S_IRWXG|S_IRWXO);
  767           char *tmp;
  768           int creat; // will we have to create a new db?
  769 #ifdef HAVE_GDBM_H
  770           GDBM_FILE dbf = 0;
  771 #endif /* HAVE_GDBM_H */
  772 #ifdef HAVE_NDBM_H
  773           DBM *dbm = NULL;
  774 #endif /* HAVE_NDBM_H */
  775 #ifdef HAVE_DBM_H
  776           int db_err = -1;
  777 #endif /* HAVE_DBM_H */
  778 
  779           GDBMD_DEBUG ("open request type %d\n", req.type);
  780 
  781           name = getstr(key);
  782           if (!name) {
  783               // error message made in getstr
  784               err = rgdbm_send_error_reply(ctx, -EINVAL, &req);
  785               cleanup();
  786               return err;
  787           }
  788 
  789           for (i = 0; i < GDBMD_FILES_MAX; i++) {
  790               if (1
  791 #ifdef HAVE_GDBM_H
  792                  && !file[i]
  793 #endif /* HAVE_GDBM_H */
  794 #ifdef HAVE_NDBM_H
  795                  && !dbm_file[i]
  796 #endif /* HAVE_NDBM_H */
  797 #ifdef HAVE_DBM_H
  798                  && !db_file[i]
  799 #endif /* HAVE_NDBM_H */
  800               )
  801                   break;
  802           }
  803 
  804           if (i < 0 || i >= GDBMD_FILES_MAX) {
  805               GDBMD_ERROR ("too many (%d) open dbs\n", file_count);
  806               err = rgdbm_send_error_reply(ctx, -EINVAL, &req);
  807               cleanup();
  808               return err;
  809           }
  810 
  811           // relativize the name to the rgdbm_datadir (working dir) and check it
  812           tmp = authdb(name, req.flags & 0xf, uid);
  813           if (!tmp) {
  814               // error message made by relativize
  815               err = rgdbm_send_error_reply(ctx, -ENOMEM, &req);
  816               cleanup();
  817               return err;
  818           }
  819           name = tmp;
  820 
  821           // check to see if the file exists yet
  822           creat = (access(name, F_OK) < 0);
  823 
  824 #ifdef HAVE_GDBM_H
  825           dbf = gdbm_open (name, bs, req.flags, mode, NULL);
  826 #endif /* HAVE_GDBM_H */
  827 #ifdef HAVE_NDBM_H
  828           if (!create
  829 # ifdef HAVE_GDBM_H
  830               && !dbf
  831 # endif /* HAVE_GDBM_H */
  832           ) {
  833               /* might want to open an existing ndbm db */
  834               int flags = 0;
  835 
  836               switch (req.flags & 0xf)
  837                 case RGDBM_READER:
  838                   flags = O_RDONLY;
  839                   break;
  840                 case RGDBM_WRITER:
  841                   flags = O_RDWR;
  842                   break;
  843                 case RGDBM_WRCREAT:
  844                   flags = O_RDWR | O_CREAT;
  845                   break;
  846                 case RGDBM_NEWDB:
  847                   flags = O_RDWR | O_CREAT | O_TRUNC;
  848                   break;
  849                 default:
  850                   GDBMD_ERROR("unknown flag %d\n", req.flags & 0xf);
  851                   err = rgdbm_send_error_reply(ctx, -EINVAL, &req);
  852                   cleanup();
  853                   return err;
  854               } 
  855 
  856               dbm = dbm_open (name, flags, mode);
  857           }
  858 #endif /* HAVE_NDBM_H */
  859 #ifdef HAVE_DBM_H
  860           if (!creat
  861 # ifdef HAVE_GDBM_H
  862               && !dbf
  863 # endif /* HAVE_GDBM_H */
  864 # ifdef HAVE_NDBM_H
  865               && !dbm
  866 # endif /* HAVE_NDBM_H */
  867               && db_opened < 1
  868           ) {
  869               /* might want to open an existing dbm db */
  870               db_err = dbminit (name);
  871           }
  872 #endif /* HAVE_DBM_H */
  873           if (1
  874 #ifdef HAVE_GDBM_H
  875               && !dbf
  876 #endif /* HAVE_GDBM_H */
  877 #ifdef HAVE_NDBM_H
  878               && !dbm
  879 #endif /* HAVE_NDBM_H */
  880 #ifdef HAVE_DBM_H
  881               && db_err < 0
  882 #endif /* HAVE_DBM_H */
  883           ) {
  884               GDBMD_ERROR("could not open db '%s'\n", name);
  885               err = rgdbm_send_error_reply(ctx, -EINVAL, &req);
  886               cleanup();
  887               return err;
  888           }
  889 
  890           if (creat) {
  891               // apparently we made it OK, since we're here.
  892               err = register_file(name, uid, gid, bs, mode);
  893               if (err < 0) {
  894                   GDBMD_ERROR("could not update files db '%s/%s'\n", rgdbm_ctrldir, GDBMD_FILES);
  895                   err = rgdbm_send_error_reply(ctx, err, &req);
  896                   cleanup();
  897                   return err;
  898               }
  899           }
  900 
  901 #ifdef HAVE_DBM_H
  902           if (db_err >= 0) {
  903               db_file[i] = 1;
  904               db_opened++;
  905           }
  906 #endif /* HAVE_DBM_H */
  907 #ifdef HAVE_NDBM_H
  908           if (dbm) {
  909               dbm_file[i] = dbm;
  910           }
  911 #endif /* HAVE_NDBM_H */
  912 #ifdef HAVE_GDBM_H
  913           if (dbf) {
  914               file[i] = dbf;
  915           }
  916 #endif /* HAVE_GDBM_H */
  917           file_count++;
  918 
  919           err = rgdbm_send_reply(ctx, i, &req, 0);
  920           GDBMD_DEBUG ("finished open %s res %d\n", name, err);
  921           cleanup();
  922           return err;
  923       }
  924           break;
  925 
  926       case RGDBM_CLOSE:
  927       {
  928 
  929           int     i = req.tableno;      // this is the descriptor to close
  930 
  931           GDBMD_DEBUG ("close request type %d\n", req.type);
  932 
  933           if (i < 0 || i >= file_count || i > GDBMD_FILES_MAX) {
  934               GDBMD_ERROR ("request close desc out of range (%d)\n", i);
  935               err = rgdbm_send_error_reply(ctx, -EINVAL, &req);
  936               cleanup();
  937               return err;
  938           }
  939           last_file = i;
  940 #ifdef HAVE_GDBM_H
  941           if (file[i]) {
  942               gdbm_close (file[i]);
  943               file[i] = 0;
  944               err = rgdbm_send_reply(ctx, 0, &req, 0);
  945               cleanup();
  946               return err;
  947           }
  948 #endif /* HAVE_GDBM_H */
  949 #ifdef HAVE_DBM_H
  950           if (db_file[i]) {
  951               dbmclose ();
  952               db_file[i] = 0;
  953               db_opened--;
  954               err = rgdbm_send_reply(ctx, 0, &req, 0);
  955               cleanup();
  956               return err;
  957           }
  958 #endif /* HAVE_DBM_H */
  959 #ifdef HAVE_NDBM_H
  960           if (dbm_file[i]) {
  961               dbm_close (dbm_file[i]);
  962               dbm_file[i] = NULL;
  963               err = rgdbm_send_reply(ctx, 0, &req, 0);
  964               cleanup();
  965               return err;
  966           }
  967 #endif /* HAVE_NDBM_H */
  968           GDBMD_ERROR ("request close of closed desc (%d)\n", i);
  969           err = rgdbm_send_error_reply(ctx, -EINVAL, &req);
  970           cleanup();
  971           return err;
  972       }
  973           break;
  974 
  975       /* these take one dbf pointer as parameter */
  976       case RGDBM_FDESC:
  977       case RGDBM_REORGANIZE:
  978       case RGDBM_SYNC:
  979       {
  980 
  981           int     i = req.tableno;
  982           int err;
  983 
  984           GDBMD_DEBUG ("reorganize/sync/fdesc request type %d\n", req.type);
  985 
  986           if (i < 0 || i >= file_count || i > GDBMD_FILES_MAX) {
  987               GDBMD_ERROR ("request sync/reorg desc out of range (%d)\n", i);
  988               err = rgdbm_send_error_reply(ctx, -EINVAL, &req);
  989               cleanup();
  990               return err;
  991           }
  992           last_file = i;
  993           if (1
  994 #ifdef HAVE_GDBM_H
  995               && !file[i]
  996 #endif /* HAVE_NDBM_H */
  997           ) {
  998               GDBMD_ERROR ("request fdesc of closed desc (%d)\n", i);
  999               err = rgdbm_send_error_reply(ctx, -EINVAL, &req);
 1000               cleanup();
 1001               return err;
 1002           }
 1003           switch(req.type) {
 1004 
 1005             case RGDBM_REORGANIZE:
 1006 #ifdef HAVE_GDBM_H
 1007               if (file[i]) {
 1008                   err = gdbm_reorganize (file[i]);
 1009                   err = rgdbm_send_reply(ctx, err, &req, 0);
 1010                   cleanup();
 1011                   return err;
 1012               }
 1013 #endif /* HAVE_NDBM_H */
 1014               break;
 1015             case RGDBM_SYNC:
 1016 #ifdef HAVE_GDBM_H
 1017               if (file[i]) {
 1018                   gdbm_sync (file[i]);
 1019                   err = 0;
 1020                   err = rgdbm_send_reply(ctx, err, &req, 0);
 1021                   cleanup();
 1022                   return err;
 1023               }
 1024 #endif /* HAVE_NDBM_H */
 1025               break;
 1026 #ifdef HAVE_GDBM_FDESC
 1027             case RGDBM_FDESC:
 1028 # ifdef HAVE_GDBM_H
 1029               if (file[i]) {
 1030                   err = gdbm_fdesc (file[i]);
 1031                   err = rgdbm_send_reply(ctx, err, &req, 0);
 1032                   cleanup();
 1033                   return err;
 1034               }
 1035 # endif /* HAVE_NDBM_H */
 1036               break;
 1037 #endif /* defined(HAVE_GDBM_FDESC) */
 1038             default:
 1039               GDBMD_ERROR ("unknown sync/fdesc type (%d)\n", req.type);
 1040               err = -EINVAL;
 1041               break;
 1042           }
 1043           err = rgdbm_send_error_reply(ctx, -EINVAL, &req);
 1044           cleanup();
 1045           return err;
 1046       }
 1047           break;
 1048 
 1049       case RGDBM_DISCONNECT:
 1050       {
 1051           int i;
 1052 
 1053           for (i = 0; i < GDBMD_FILES_MAX; i++ ) {
 1054 
 1055 #ifdef HAVE_GDBM_H
 1056              if (file[i]) {
 1057                  gdbm_close(file[i]);
 1058                  file[i] = NULL;
 1059              }
 1060 #endif /* HAVE_GDBM_H */
 1061 #ifdef HAVE_DBM_H
 1062              if (db_file[i]) {
 1063                  dbmclose();
 1064                  db_file[i] = 0;
 1065                  db_opened--;
 1066              }
 1067 #endif /* HAVE_DBM_H */
 1068 #ifdef HAVE_NDBM_H
 1069              if (dbm_file[i]) {
 1070                  dbm_close(dbm_file[i]);
 1071                  dbm_file[i] = NULL;
 1072              }
 1073 #endif /* HAVE_NDBM_H */
 1074           }
 1075           ctx->disconnect(ctx);
 1076           connected = 0;       // brief glory of use
 1077           _exit(0);
 1078           return 0;
 1079       }
 1080           break;
 1081 
 1082       case RGDBM_CONNECT:
 1083       {
 1084           char *dir;
 1085           char *user;
 1086           char *pass;           // break out of user if present as user[/pass]
 1087           char *group;
 1088 
 1089           GDBMD_DEBUG ("connect request type %d\n", req.type);
 1090 
 1091           if (connected) {
 1092               GDBMD_ERROR("already connected\n");
 1093               err = rgdbm_send_error_reply(ctx, -EINVAL, &req);
 1094               cleanup();
 1095               return err;
 1096           }
 1097 
 1098           /*
 1099            * the client gets the salt, and can now send out a username
 1100            * with the passwd to follow. It knows that the password will
 1101            * be a 13 character string after a /, so it can send the /
 1102            * and wait.
 1103            */
 1104 
 1105           // read dirname. len includes terminating null so len 0 is imposs
 1106           dir = getstr(key);
 1107           if (!dir) {
 1108               // error message made in getstr
 1109               err = rgdbm_send_error_reply(ctx, -EINVAL, &req);
 1110               cleanup();
 1111               return err;
 1112           }
 1113 
 1114           // read username. len includes terminating null so len 0 is imposs
 1115           user = getstr(content);
 1116           if (!user) {
 1117               // error message made in getstr
 1118               err = rgdbm_send_error_reply(ctx, -EINVAL, &req);
 1119               cleanup();
 1120               return err;
 1121           }
 1122 
 1123           // break out a passwd if present in user[/pass]
 1124           pass = strchr(user, '/');
 1125           if (pass)
 1126               *pass++ = 0;
 1127 
 1128           // break out a group if present as user[.group]
 1129           group = strrchr(user, '.');
 1130           if (group)
 1131               *group++ = 0;
 1132 
 1133           // do the whole lot here, including setting the chdir and the
 1134           // uid and gid for the thread.
 1135           if (authconnect(dir, user, group, pass) < 0) {
 1136               // message made in authconnect
 1137               err = rgdbm_send_error_reply(ctx, -EINVAL, &req);
 1138               cleanup();
 1139               return err;
 1140           }
 1141 
 1142           err = rgdbm_send_reply(ctx, err, &req, 0);
 1143           cleanup();
 1144           return err;
 1145       }
 1146           break;
 1147 
 1148       case RGDBM_HANDSHAKE:
 1149       {
 1150           char *dir;
 1151           char *user;
 1152           char *group;
 1153           char *pass;
 1154           int len;
 1155 
 1156           // at this point we just got the req header
 1157           if (req.count != 2)
 1158               return -EINVAL;
 1159 
 1160           // get the dir datum
 1161           key = rgdbm_get_datum(ctx);
 1162           if (key.dsize < 0)
 1163               return key.dsize;
 1164 
 1165           // read dirname. len includes terminating null so len 0 is imposs
 1166           dir = getstr(key);
 1167           if (!dir) {
 1168               // error message made in getstr
 1169               err = rgdbm_send_error_reply(ctx, -EINVAL, &req);
 1170               cleanup();
 1171               return err;
 1172           }
 1173 
 1174           // get the user/passwd datum SIZE only
 1175           len = rgdbm_get_datum_size(ctx);
 1176           if (len < 0)
 1177               return len;
 1178 
 1179           user = malloc(len + 128); // leave space for a longer crypted return
 1180           if (!user)
 1181               return -ENOMEM;
 1182           content.dsize = len;
 1183           content.dptr  = user;
 1184           // from now on we can do cleanup() if required
 1185          
 1186           // send new salt, part reply and get rest of user/pass into buffer
 1187           int64_t salt = get_pass(ctx, len, user);
 1188           if (salt < 0) {
 1189               // message build by get_pass
 1190               err = rgdbm_send_error_reply(ctx, -EINVAL, &req);
 1191               cleanup();
 1192               return err;
 1193           }
 1194 
 1195         /* if there was a following passwd, it takes 1 + 2 + 11 = 14 bytes,
 1196          * salt included, if it's a standard passwd. If it's a md5 passwd,
 1197          * it takes 1 + 3 + 8 + 1 + 22 = 35 bytes, salt included. In both
 1198          * cases the passwd part of user/passwd starts with the '/'.
 1199          */
 1200 
 1201           pass = strchr(user, '/');
 1202           if (pass)
 1203               *pass++ = 0;
 1204           
 1205           group = strrchr(user, '.');
 1206           if (group)
 1207               *group++ = 0;
 1208 
 1209           err = authcryptconnect(dir, user, group, pass, salt);
 1210           if (err < 0) {
 1211               // message made in authconnect
 1212               err = rgdbm_send_error_reply(ctx, -EINVAL, &req);
 1213               cleanup();
 1214               return err;
 1215           }
 1216      
 1217         // we already said OK and just need to send stop in order to confirm
 1218           err = rgdbm_send_stop(ctx);
 1219           cleanup();
 1220           return err;
 1221       }
 1222           break;
 1223 
 1224 
 1225       /* this takes one dbf pointer and one weird "datum" parameter */
 1226       case RGDBM_SETOPT:
 1227       {
 1228 
 1229           int     i = req.tableno;
 1230           int err;
 1231 
 1232           GDBMD_DEBUG ("setopt request type %d\n", req.type);
 1233 
 1234           if (i < 0 || i >= file_count || i > GDBMD_FILES_MAX) {
 1235               GDBMD_ERROR ("request close desc out of range (%d)\n", i);
 1236               err = rgdbm_send_error_reply(ctx, -EINVAL, &req);
 1237               cleanup();
 1238               return err;
 1239           }
 1240           last_file = i;
 1241 
 1242           if (1
 1243 #ifdef HAVE_GDBM_H
 1244               && !file[i]
 1245 #endif /* HAVE_GDBM_H */
 1246 #ifdef HAVE_NDBM_H
 1247               && !dbm_file[i]
 1248 #endif /* HAVE_NDBM_H */
 1249 #ifdef HAVE_DBM_H
 1250               && !db_file[i]
 1251 #endif /* HAVE_DBM_H */
 1252           ) {
 1253               GDBMD_ERROR ("request setopt of closed desc (%d)\n", i);
 1254               err = rgdbm_send_error_reply(ctx, -EINVAL, &req);
 1255               cleanup();
 1256               return err;
 1257           }
 1258 
 1259           switch(req.flags) {
 1260 
 1261             case RGDBM_RDONLY:
 1262               err = -EINVAL;
 1263               if (key.dsize > 0) {
 1264 
 1265                   int val = 0;
 1266                   if (key.dsize <= sizeof(int)) {
 1267                       memcpy(&val, key.dptr, key.dsize);
 1268                   } else {
 1269                       memcpy(&val, key.dptr, sizeof(int));
 1270                   }
 1271                       // no need to swap bytes
 1272                   if (val != 0) {
 1273 #ifdef HAVE_NDBM_H
 1274                       if (dbm_file[i])
 1275                           err = dbm_rdonly(dbm_file[i]);
 1276 #endif /* HAVE_GDBM_H */
 1277                   }
 1278               }
 1279               err = rgdbm_send_reply(ctx, err, &req, 0);
 1280               cleanup();
 1281               return err;
 1282 
 1283             default:
 1284 
 1285               err = -EINVAL;
 1286 #ifdef HAVE_GDBM_H
 1287               if (file[i]) {
 1288                   if (key.dsize > 0) {
 1289                       err = gdbm_setopt (file[i], req.flags, (int *)key.dptr, key.dsize);
 1290                   } else {
 1291                       err = gdbm_setopt (file[i], req.flags, NULL, 0);
 1292                   }
 1293               }
 1294 #endif /* HAVE_GDBM_H */
 1295               err = rgdbm_send_reply(ctx, err, &req, 0);
 1296               cleanup();
 1297               return err;
 1298           }
 1299       }
 1300           break;
 1301 
 1302 
 1303       /* these take one dbf pointer and two datum parameters */
 1304       case RGDBM_INSERT:
 1305       case RGDBM_REPLACE:
 1306       {
 1307           int     i = req.tableno;      // this is the descriptor to close
 1308 
 1309           GDBMD_DEBUG ("insert/replace request type %d\n", req.type);
 1310 
 1311           if (i < 0 || i >= file_count || i > GDBMD_FILES_MAX) {
 1312               GDBMD_ERROR ("request invalid desc (%d)\n", i);
 1313               err = rgdbm_send_error_reply(ctx, -EINVAL, &req);
 1314               goto store_return;
 1315           }
 1316           last_file = i;
 1317 
 1318           if (1
 1319 #ifdef HAVE_GDBM_H
 1320              && !file[i]
 1321 #endif /* HAVE_GDBM_H */
 1322 #ifdef HAVE_NDBM_H
 1323              && !dbm_file[i]
 1324 #endif /* HAVE_NDBM_H */
 1325 #ifdef HAVE_DBM_H
 1326              && !db_file[i]
 1327 #endif /* HAVE_DBM_H */
 1328           ) {
 1329               GDBMD_ERROR ("request invalid desc (%d)\n", i);
 1330               err = rgdbm_send_error_reply(ctx, -EINVAL, &req);
 1331               goto store_return;
 1332           }
 1333 
 1334           err = -EINVAL;
 1335 
 1336           switch (req.type) {
 1337 
 1338             case RGDBM_INSERT:
 1339 #ifdef HAVE_GDBM_H
 1340               if (file[i]) {
 1341                   err = gdbm_store (file[i], key, content, GDBM_INSERT);
 1342                   err = rgdbm_send_reply(ctx, err, &req, 0);
 1343                   goto store_return;
 1344               }
 1345 #endif /* HAVE_GDBM_H */
 1346 #ifdef HAVE_DBM_H
 1347               if (db_file[i]) {
 1348                   err = store (key, content);
 1349                   err = rgdbm_send_reply(ctx, err, &req, 0);
 1350                   goto store_return;
 1351               }
 1352 #endif /* HAVE_DBM_H */
 1353 #ifdef HAVE_NDBM_H
 1354               if (dbm_file[i]) {
 1355                   err = dbm_store (ndbm_file[i], key, content, DBM_INSERT);
 1356                   err = rgdbm_send_reply(ctx, err, &req, 0);
 1357                   goto store_return;
 1358               }
 1359 #endif /* HAVE_NDBM_H */
 1360               break;
 1361 
 1362             case RGDBM_REPLACE:
 1363 #ifdef HAVE_GDBM_H
 1364               if (file[i]) {
 1365                   err = gdbm_store (file[i], key, content, GDBM_REPLACE);
 1366                   err = rgdbm_send_reply(ctx, err, &req, 0);
 1367                   goto store_return;
 1368               }
 1369 #endif /* HAVE_GDBM_H */
 1370 #ifdef HAVE_DBM_H
 1371               if (db_file[i]) {
 1372                   err = store (key, content);
 1373                   err = rgdbm_send_reply(ctx, err, &req, 0);
 1374                   goto store_return;
 1375               }
 1376 #endif /* HAVE_DBM_H */
 1377 #ifdef HAVE_NDBM_H
 1378               if (dbm_file[i]) {
 1379                   err = dbm_store (ndbm_file[i], key, content, DBM_REPLACE);
 1380                   err = rgdbm_send_reply(ctx, err, &req, 0);
 1381                   goto store_return;
 1382               }
 1383 #endif /* HAVE_NDBM_H */
 1384               break;
 1385 
 1386             default:
 1387               GDBMD_ERROR ("unknown storage type %d\n", req.type);
 1388               err = rgdbm_send_error_reply(ctx, -EINVAL, &req);
 1389               goto store_return;
 1390               break;
 1391           }
 1392 
 1393  store_return:
 1394           cleanup();
 1395           return err;
 1396       }
 1397           break;
 1398 
 1399       /* these take no parameters at all */
 1400       case RGDBM_VERSION:
 1401       case RGDBM_ERRNO:
 1402       case RGDBM_STRERROR:
 1403       {
 1404           switch (req.type) {
 1405 
 1406 #ifdef HAVE_GDBM_H
 1407             case RGDBM_VERSION:
 1408               err = rgdbm_send_msg_reply(ctx, 0, &req, gdbm_version);
 1409               cleanup();
 1410               return err;
 1411 #endif /* HAVE_GDBM_H */
 1412 
 1413             case RGDBM_ERRNO:
 1414 #ifdef HAVE_GDBM_H
 1415               err = gdbm_errno;
 1416 #elif defined(HAVE_NDBM_H)
 1417               if (last_file >= 0 && dbm_file[last_file]) {
 1418                   err = dbm_error (dbm_file[last_file]);
 1419               } else {
 1420                   err = 0;
 1421               }
 1422 #else
 1423               err = 0;
 1424 #endif /* HAVE_GDBM_H */
 1425               // they're all positive, still ...
 1426               err = rgdbm_send_reply(ctx, err, &req, 0);
 1427               cleanup();
 1428               return err;
 1429 
 1430 #ifdef HAVE_GDBM_H
 1431             case RGDBM_STRERROR:
 1432             {
 1433               char *msg;
 1434 
 1435               if (req.flags < 0) {
 1436                   if (!gdbmd_error)
 1437                       GDBMD_ERROR("no error message to send!\n");
 1438                   // just return last message
 1439                   err = rgdbm_send_error_reply(ctx, 0, &req);
 1440                   cleanup();
 1441                   return err;
 1442               }
 1443 
 1444               msg = (char *)gdbm_strerror(req.flags);
 1445 
 1446               if (msg) {
 1447                   err = rgdbm_send_msg_reply(ctx, 0, &req, msg);
 1448               } else {
 1449                   if (!gdbmd_error)
 1450                       GDBMD_ERROR("no error message to send!\n");
 1451                   err = rgdbm_send_error_reply(ctx, 0, &req);
 1452               }
 1453               cleanup();
 1454               return err;
 1455             }
 1456 #endif /* HAVE_GDBM_H */
 1457 
 1458             default:
 1459               GDBMD_ERROR ("unknown version/errno/strerr type %d\n", req.type);
 1460               err = rgdbm_send_error_reply(ctx, -EINVAL, &req);
 1461               cleanup();
 1462               return err;
 1463           }
 1464 
 1465       }
 1466           break;
 1467 
 1468       /* these take one dbf pointer and one (or no) key parameter */
 1469       case RGDBM_FETCH:
 1470       case RGDBM_EXISTS:
 1471       case RGDBM_DELETE:
 1472       case RGDBM_FIRSTKEY:
 1473       case RGDBM_NEXTKEY:
 1474       {
 1475 
 1476           int     i = req.tableno;      // this is the descriptor to close
 1477 
 1478           GDBMD_DEBUG ("fetch/exists/delete request type %d\n", req.type);
 1479 
 1480           if (i < 0 || i >= file_count || i > GDBMD_FILES_MAX) {
 1481               GDBMD_ERROR ("request invalid desc (%d)\n", i);
 1482               err = rgdbm_send_error_reply(ctx, -EINVAL, &req);
 1483               cleanup();
 1484               return err;
 1485           }
 1486           last_file = i;
 1487 
 1488           if (1
 1489 #ifdef HAVE_GDBM_H
 1490              && !file[i]
 1491 #endif /* HAVE_GDBM_H */
 1492 #ifdef HAVE_NDBM_H
 1493              && !dbm_file[i]
 1494 #endif /* HAVE_NDBM_H */
 1495 #ifdef HAVE_DBM_H
 1496              && !db_file[i]
 1497 #endif /* HAVE_DBM_H */
 1498           ) {
 1499               GDBMD_ERROR ("request invalid desc (%d)\n", i);
 1500               err = rgdbm_send_error_reply(ctx, -EINVAL, &req);
 1501               cleanup();
 1502               return err;
 1503           }
 1504 
 1505           switch (req.type) {
 1506 
 1507             case RGDBM_FIRSTKEY:
 1508 
 1509               if (key.dptr) {
 1510                   // don't cover over somebody's attempt to make us leak mem
 1511                   free(key.dptr);
 1512                   key.dptr = NULL;
 1513               }
 1514 #ifdef HAVE_GDBM_H
 1515               if (file[i]) {
 1516                   key = gdbm_firstkey (file[i]);
 1517                   if (!key.dptr) {
 1518                       err = rgdbm_send_reply(ctx, 0, &req, 0);
 1519                       goto firstkey_return;
 1520                   }
 1521                   err = rgdbm_send_reply(ctx, 0, &req, 1, key);
 1522                   goto firstkey_return;
 1523               }
 1524 #endif /* HAVE_GDBM_H */
 1525 #ifdef HAVE_DBM_H
 1526               if (db_file[i]) {
 1527                   key = firstkey ();
 1528                   if (!key.dptr) {
 1529                       err = rgdbm_send_reply(ctx, 0, &req, 0);
 1530                       goto firstkey_return;
 1531                   }
 1532                   err = rgdbm_send_reply(ctx, 0, &req, 1, key);
 1533                   goto firstkey_return;
 1534               }
 1535 #endif /* HAVE_DBM_H */
 1536 #ifdef HAVE_NDBM_H
 1537               if (dbm_file[i]) {
 1538                   key = dbm_firstkey (dbm_file[i]);
 1539                   if (!key.dptr) {
 1540                       err = rgdbm_send_reply(ctx, 0, &req, 0);
 1541                       goto firstkey_return;
 1542                   }
 1543                   err = rgdbm_send_reply(ctx, 0, &req, 1, key);
 1544                   goto firstkey_return;
 1545               }
 1546 #endif /* HAVE_NDBM_H */
 1547               err = rgdbm_send_error_reply(ctx, -EINVAL, &req);
 1548  firstkey_return:
 1549               cleanup();
 1550               return err;
 1551 
 1552             case RGDBM_NEXTKEY:
 1553               if (content.dptr) {
 1554                   // don't cover over somebody's attempt to make us leak mem
 1555                   free(content.dptr);
 1556                   content.dptr = NULL;
 1557               }
 1558 #ifdef HAVE_GDBM_H
 1559               if (file[i]) {
 1560                   content = gdbm_nextkey (file[i], key);
 1561                   if (!content.dptr) {
 1562                       err = rgdbm_send_reply(ctx, 0, &req, 0);
 1563                       goto nextkey_return;
 1564                   }
 1565                   err = rgdbm_send_reply(ctx, 0, &req, 1, content);
 1566                   goto nextkey_return;
 1567               }
 1568 #endif /* HAVE_GDBM_H */
 1569 #ifdef HAVE_DBM_H
 1570               if (db_file[i]) {
 1571                   content = nextkey (key);
 1572                   if (!content.dptr) {
 1573                       err = rgdbm_send_reply(ctx, 0, &req, 0);
 1574                       goto nextkey_return;
 1575                   }
 1576                   err = rgdbm_send_reply(ctx, 0, &req, 1, content);
 1577                   goto nextkey_return;
 1578               }
 1579 #endif /* HAVE_DBM_H */
 1580 #ifdef HAVE_NDBM_H
 1581               if (dbm_file[i]) {
 1582                   content = dbm_nextkey (file[i]);
 1583                   if (!content.dptr) {
 1584                       err = rgdbm_send_reply(ctx, 0, &req, 0);
 1585                       goto nextkey_return;
 1586                   }
 1587                   err = rgdbm_send_reply(ctx, 0, &req, 1, content);
 1588                   goto nextkey_return;
 1589               }
 1590 #endif /* HAVE_NDBM_H */
 1591               err = rgdbm_send_error_reply(ctx, -EINVAL, &req);
 1592  nextkey_return:
 1593               cleanup();
 1594               return err;
 1595 
 1596             case RGDBM_FETCH:
 1597 
 1598               if (content.dptr) {
 1599                   // don't cover over somebody's attempt to make us leak mem
 1600                   free(content.dptr);
 1601                   content.dptr = NULL;
 1602               }
 1603 #ifdef HAVE_GDBM_H
 1604               if (file[i]) {
 1605                   content = gdbm_fetch (file[i], key);
 1606                   if (!content.dptr) {
 1607                       err = rgdbm_send_reply(ctx, 0, &req, 0);
 1608                       goto fetch_return;
 1609                   }
 1610                   err = rgdbm_send_reply(ctx, 0, &req, 1, content);
 1611                   goto fetch_return;
 1612               }
 1613 #endif /* HAVE_GDBM_H */
 1614 #ifdef HAVE_DBM_H
 1615               if (db_file[i]) {
 1616                   content = fetch (key);
 1617                   if (!content.dptr) {
 1618                       err = rgdbm_send_reply(ctx, 0, &req, 0);
 1619                       goto fetch_return;
 1620                   }
 1621                   err = rgdbm_send_reply(ctx, 0, &req, 1, content);
 1622                   goto fetch_return;
 1623               }
 1624 #endif /* HAVE_DBM_H */
 1625 #ifdef HAVE_NDBM_H
 1626               if (dbm_file[i]) {
 1627                   content = dbm_fetch (file[i], key);
 1628                   if (!content.dptr) {
 1629                       err = rgdbm_send_reply(ctx, 0, &req, 0);
 1630                       goto fetch_return;
 1631                   }
 1632                   err = rgdbm_send_reply(ctx, 0, &req, 1, content);
 1633                   goto fetch_return;
 1634               }
 1635 #endif /* HAVE_NDBM_H */
 1636               err = rgdbm_send_error_reply(ctx, -EINVAL, &req);
 1637  fetch_return:
 1638               cleanup();
 1639               return err;
 1640 
 1641             case RGDBM_EXISTS:
 1642 #ifdef HAVE_GDBM_H
 1643               if (file[i]) {
 1644                   err = rgdbm_send_reply(ctx, !!gdbm_exists (file[i], key), &req, 0);
 1645                   goto exists_return;
 1646               }
 1647 #endif /* HAVE_GDBM_H */
 1648               if (content.dptr) {
 1649                   // don't cover over somebody's attempt to make us leak mem
 1650                   free(content.dptr);
 1651                   content.dptr = NULL;
 1652               }
 1653 #ifdef HAVE_DBM_H
 1654               if (db_file[i]) {
 1655                   content = fetch (key);
 1656                   err = rgdbm_send_reply(ctx, content.dptr != NULL, &req, 0);
 1657                   goto exists_return;
 1658               }
 1659 #endif /* HAVE_DBM_H */
 1660 #ifdef HAVE_NDBM_H
 1661               if (dbm_file[i]) {
 1662                   content = dbm_fetch (file[i], key);
 1663                   err = rgdbm_send_reply(ctx, content.dptr != NULL, &req, 0);
 1664                   goto exists_return;
 1665               }
 1666 #endif /* HAVE_NDBM_H */
 1667               err = rgdbm_send_error_reply(ctx, -EINVAL, &req);
 1668  exists_return:
 1669               cleanup();
 1670               return err;
 1671 
 1672             case RGDBM_DELETE:
 1673 #ifdef HAVE_GDBM_H
 1674               if (file[i]) {
 1675                   err = gdbm_delete (file[i], key);
 1676                   err = rgdbm_send_reply(ctx, err, &req, 0);
 1677                   goto delete_return;
 1678               }
 1679 #endif /* HAVE_GDBM_H */
 1680 #ifdef HAVE_DBM_H
 1681               if (db_file[i]) {
 1682                   err = delete (key);
 1683                   err = rgdbm_send_reply(ctx, err, &req, 0);
 1684                   goto delete_return;
 1685               }
 1686 #endif /* HAVE_DBM_H */
 1687 #ifdef HAVE_NDBM_H
 1688               if (dbm_file[i]) {
 1689                   err = dbm_delete (dbm_file[i], key);
 1690                   err = rgdbm_send_reply(ctx, err, &req, 0);
 1691                   goto delete_return;
 1692               }
 1693 #endif /* HAVE_NDBM_H */
 1694               err = rgdbm_send_error_reply(ctx, -EINVAL, &req);
 1695  delete_return:
 1696               cleanup();
 1697               return err;
 1698 
 1699             default:
 1700               GDBMD_ERROR ("unknown fetch/delete type %d\n", req.type);
 1701               err = rgdbm_send_error_reply(ctx, -EINVAL, &req);
 1702               cleanup();
 1703               return err;
 1704           }
 1705 
 1706       }
 1707           break;
 1708 
 1709       default:
 1710       {
 1711         GDBMD_ERROR ("unknown request type %d\n", req.type);
 1712         err = rgdbm_send_error_reply(ctx, -EINVAL, &req);
 1713         cleanup();
 1714         return err;
 1715       }
 1716           break;
 1717     }                           //esw
 1718 
 1719     return 0;
 1720 }
 1721 
 1722 static void
 1723 usage(void) {
 1724     GDBMD_ERROR("gdbmd [ -F ] [ -p port ] [ -D workdir ] [ -C ctrldir ]\n");
 1725 }
 1726 
 1727 /*
 1728  * examine commandline args and set globals 
 1729  */
 1730 static int
 1731 cmdline (int argc, char **argv)
 1732 {
 1733     int c;
 1734 
 1735     while (c = getopt (argc, argv, "C:D:Fp:"), c != -1) {
 1736 
 1737         switch (c) {
 1738           case 'C':
 1739               rgdbm_ctrldir = optarg;
 1740               break;
 1741           case 'D':
 1742               rgdbm_datadir = optarg;
 1743               break;
 1744           case 'F':
 1745               godaemon = 0;
 1746               break;
 1747           case 'p':
 1748               port = atoi (optarg);
 1749               if (port <= 0) {
 1750                   GDBMD_ERROR ("port %d out of range\n", port);
 1751                   return -EINVAL;
 1752               }
 1753               break;
 1754           case '?':
 1755           default:
 1756               usage ();
 1757               return -EINVAL;
 1758         }
 1759     }
 1760     return 0;
 1761 }
 1762 
 1763 
 1764 
 1765 int main (int argc, char **argv)
 1766 {
 1767 
 1768     int     sock, csock;
 1769     struct sockaddr_in inaddr;
 1770     socklen_t addrlen = sizeof (inaddr);
 1771     int err;
 1772     char *tmp;
 1773 
 1774     extern char *optarg;
 1775     extern int optind, opterr, optopt;
 1776     int flags;
 1777 
 1778     static const int true  = 1;
 1779     static const struct linger linger = {
 1780       .l_onoff = 0,    /* linger active/inactive */
 1781       .l_linger = 0,   /* how many seconds to linger for */
 1782     };
 1783 
 1784     if (geteuid() == 0) {
 1785         GDBMD_ERROR ("sorry - this daemon does not want to work as root!\n");
 1786         exit(1);
 1787     }
 1788 
 1789     if (cmdline(argc, argv) < 0) {
 1790         exit(2);
 1791     }
 1792 
 1793 #ifdef HAVE_OPENSSL_SSL_H
 1794     flags = RGDBM_CRYPTO_SSL_OR_NONE;
 1795 #else
 1796     flags = RGDBM_CRYPTO_NONE;
 1797 #endif
 1798 
 1799     tmp = realpath(rgdbm_datadir, NULL);
 1800     if (!tmp) {
 1801         if (mkdir(rgdbm_datadir, 0750) < 0) {
 1802             GDBMD_ERROR ("error in resolving working directory %s (%m)\n",
 1803                 rgdbm_datadir);
 1804             exit(3);
 1805         }
 1806     }
 1807     rgdbm_datadir = tmp;
 1808 
 1809     tmp = realpath(rgdbm_ctrldir, NULL);
 1810     if (!tmp) {
 1811         if (mkdir(rgdbm_ctrldir, 0750) < 0) {
 1812             GDBMD_ERROR ("error in resolving control directory %s (%m)\n",
 1813                 rgdbm_ctrldir);
 1814             exit(4);
 1815         }
 1816     }
 1817     rgdbm_ctrldir = tmp;
 1818 
 1819     if (godaemon && daemon (0, 0) < 0) {
 1820         GDBMD_ERROR ("error in going daemon (%m)\n");
 1821         exit (5);
 1822     }
 1823 
 1824     if (chdir(rgdbm_datadir) < 0) {
 1825         GDBMD_ERROR ("could not chdir to  %s (%m)\n", GDBMD_DATADIR);
 1826         exit(6);
 1827     }
 1828 
 1829     make(); // bring the files db up to date wrt the ascii files list, if req
 1830 
 1831     sock = socket (PF_INET, SOCK_STREAM, IPPROTO_TCP);
 1832     if (sock < 0) {
 1833         GDBMD_ERROR ("error in opening socket (%m)\n");
 1834         exit (7);
 1835     }
 1836 
 1837     // don't be shy about retaking the socket. It's ours
 1838     setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, &true, sizeof(true));
 1839     // don't hang around after death
 1840     setsockopt(sock, SOL_SOCKET, SO_LINGER, &linger, sizeof(linger));
 1841 
 1842     memset (&inaddr, 0, addrlen);
 1843 
 1844     inaddr.sin_family = AF_INET;
 1845     inaddr.sin_port = htons (port);
 1846     inaddr.sin_addr.s_addr = 0;
 1847 
 1848     if (bind (sock, (struct sockaddr *)&inaddr, addrlen) < 0) {
 1849         GDBMD_ERROR ("error in binding to socket on port %d (%m)\n", port);
 1850         exit (8);
 1851     }
 1852 
 1853     if (listen (sock, GDBMD_LISTEN_QUEUE_LENGTH) < 0) {
 1854         GDBMD_ERROR ("error in listening on open socket (%m)\n");
 1855         exit (9);
 1856     }
 1857 
 1858     signal(SIGCHLD, SIG_IGN); // FIXME. Maybe SIGPIPE too?
 1859 
 1860     while (1) {
 1861 
 1862         static const struct timeval tv =
 1863             { .tv_sec = GDBMD_SNDTIMEO, .tv_usec = 0, };
 1864         static const int linger2 = 1; // 1s
 1865         struct io_ctx *ctx;
 1866         int n;
 1867 
 1868  retry:
 1869         if (!(flags & 0xf)) {
 1870             GDBMD_ERROR ("error in server accept (%m)\n");
 1871             _exit (12);
 1872         }
 1873 
 1874         csock = accept (sock, (struct sockaddr *)&inaddr, &addrlen);
 1875         if (csock < 0) {
 1876             GDBMD_ERROR ("error in accept on open socket (%m)\n");
 1877             _exit (10);
 1878         }
 1879 
 1880         ctx = new_io_ctx(csock);
 1881         if (!ctx) {
 1882             GDBMD_ERROR ("no memory for %ldB\n", (long)sizeof(*ctx));
 1883             _exit(11);
 1884         }
 1885 
 1886         // last bit set. flags & 0xf is nonzero
 1887         n = fls(flags & 0xf);
 1888 
 1889         err = ctx->accept(ctx, (flags & ~0xf) | (1 << n));
 1890         if (err < 0) {
 1891             flags &= ~(1 << n);
 1892             ctx->disconnect(ctx);
 1893             free(ctx);
 1894             goto retry;
 1895         }
 1896 
 1897         if (godaemon) {
 1898             int pid = fork();
 1899             if (pid < 0) {
 1900                 GDBMD_ERROR ("error in fork to handle new connection (%m)\n");
 1901                 _exit (14);
 1902             }
 1903             if (pid > 0) {
 1904                 close(csock);
 1905                 continue; // we are the proud parent
 1906             }
 1907         }
 1908 
 1909         // we are the child, or the original if we didn't fork
 1910 
 1911         srandom(time(NULL));
 1912 
 1913         // send keep-alives on the child socket
 1914         setsockopt(csock, SOL_SOCKET, SO_KEEPALIVE, &true, sizeof(true));
 1915         // timeout if can't send
 1916         setsockopt(csock, SOL_SOCKET, SO_SNDTIMEO, &tv, sizeof(tv));
 1917         // don't be shy about retaking the socket. It's ours
 1918         setsockopt(csock, SOL_SOCKET, SO_REUSEADDR, &true, sizeof(true));
 1919         // don't hang around after death
 1920         setsockopt(csock, SOL_SOCKET, SO_LINGER, &linger, sizeof(linger));
 1921         // don't hang around after death either
 1922 #ifdef TCP_LINGER2
 1923         setsockopt(csock, SOL_SOCKET, TCP_LINGER2, &linger2, sizeof(linger2));
 1924 #endif /* TCP_LINGER2 */
 1925 
 1926         while (1) {
 1927             err = loop (ctx);
 1928             if (err < 0) {
 1929                 if (ctx)
 1930                     ctx->disconnect(ctx);
 1931                 _exit (15);
 1932             }
 1933             // go round loop again
 1934         }
 1935         // no way out but death for child
 1936         return 0;
 1937     }
 1938     return 0;
 1939 }
 1940 
 1941