home *** CD-ROM | disk | FTP | other *** search
/ Geek Gadgets 1 / ADE-1.bin / ade-dist / inetutils-1.2-src.tgz / tar.out / fsf / inetutils / inetd / inetd.c < prev    next >
C/C++ Source or Header  |  1996-09-28  |  31KB  |  1,293 lines

  1. /*
  2.  * Copyright (c) 1983, 1991, 1993, 1994
  3.  *    The Regents of the University of California.  All rights reserved.
  4.  *
  5.  * Redistribution and use in source and binary forms, with or without
  6.  * modification, are permitted provided that the following conditions
  7.  * are met:
  8.  * 1. Redistributions of source code must retain the above copyright
  9.  *    notice, this list of conditions and the following disclaimer.
  10.  * 2. Redistributions in binary form must reproduce the above copyright
  11.  *    notice, this list of conditions and the following disclaimer in the
  12.  *    documentation and/or other materials provided with the distribution.
  13.  * 3. All advertising materials mentioning features or use of this software
  14.  *    must display the following acknowledgement:
  15.  *    This product includes software developed by the University of
  16.  *    California, Berkeley and its contributors.
  17.  * 4. Neither the name of the University nor the names of its contributors
  18.  *    may be used to endorse or promote products derived from this software
  19.  *    without specific prior written permission.
  20.  *
  21.  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
  22.  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
  23.  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
  24.  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
  25.  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
  26.  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
  27.  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
  28.  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
  29.  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
  30.  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
  31.  * SUCH DAMAGE.
  32.  */
  33.  
  34. #ifndef lint
  35. static char copyright[] =
  36. "@(#) Copyright (c) 1983, 1991, 1993, 1994\n\
  37.     The Regents of the University of California.  All rights reserved.\n";
  38. #endif /* not lint */
  39.  
  40. #ifndef lint
  41. static char sccsid[] = "@(#)inetd.c    8.4 (Berkeley) 4/13/94";
  42. #endif /* not lint */
  43.  
  44. /*
  45.  * Inetd - Internet super-server
  46.  *
  47.  * This program invokes all internet services as needed.  Connection-oriented
  48.  * services are invoked each time a connection is made, by creating a process.
  49.  * This process is passed the connection as file descriptor 0 and is expected
  50.  * to do a getpeername to find out the source host and port.
  51.  *
  52.  * Datagram oriented services are invoked when a datagram
  53.  * arrives; a process is created and passed a pending message
  54.  * on file descriptor 0.  Datagram servers may either connect
  55.  * to their peer, freeing up the original socket for inetd
  56.  * to receive further messages on, or ``take over the socket'',
  57.  * processing all arriving datagrams and, eventually, timing
  58.  * out.     The first type of server is said to be ``multi-threaded'';
  59.  * the second type of server ``single-threaded''. 
  60.  *
  61.  * Inetd uses a configuration file which is read at startup
  62.  * and, possibly, at some later time in response to a hangup signal.
  63.  * The configuration file is ``free format'' with fields given in the
  64.  * order shown below.  Continuation lines for an entry must being with
  65.  * a space or tab.  All fields must be present in each entry.
  66.  *
  67.  *    service name            must be in /etc/services or must
  68.  *                    name a tcpmux service
  69.  *    socket type            stream/dgram/raw/rdm/seqpacket
  70.  *    protocol            must be in /etc/protocols
  71.  *    wait/nowait            single-threaded/multi-threaded
  72.  *    user                user to run daemon as
  73.  *    server program            full path name
  74.  *    server program arguments    maximum of MAXARGS (20)
  75.  *
  76.  * TCP services without official port numbers are handled with the
  77.  * RFC1078-based tcpmux internal service. Tcpmux listens on port 1 for
  78.  * requests. When a connection is made from a foreign host, the service
  79.  * requested is passed to tcpmux, which looks it up in the servtab list
  80.  * and returns the proper entry for the service. Tcpmux returns a
  81.  * negative reply if the service doesn't exist, otherwise the invoked
  82.  * server is expected to return the positive reply if the service type in
  83.  * inetd.conf file has the prefix "tcpmux/". If the service type has the
  84.  * prefix "tcpmux/+", tcpmux will return the positive reply for the
  85.  * process; this is for compatibility with older server code, and also
  86.  * allows you to invoke programs that use stdin/stdout without putting any
  87.  * special server code in them. Services that use tcpmux are "nowait"
  88.  * because they do not have a well-known port and hence cannot listen
  89.  * for new requests.
  90.  *
  91.  * Comment lines are indicated by a `#' in column 1.
  92.  */
  93.  
  94. #ifdef HAVE_CONFIG_H
  95. #include <config.h>
  96. #endif
  97.  
  98. #include <sys/param.h>
  99. #include <sys/stat.h>
  100. #include <sys/ioctl.h>
  101. #include <sys/socket.h>
  102. #include <sys/wait.h>
  103. #include <sys/time.h>
  104. #include <sys/resource.h>
  105.  
  106. #include <netinet/in.h>
  107. #include <arpa/inet.h>
  108.  
  109. #include <errno.h>
  110. #include <fcntl.h>
  111. #include <netdb.h>
  112. #include <pwd.h>
  113. #include <signal.h>
  114. #include <stdio.h>
  115. #include <stdlib.h>
  116. #include <string.h>
  117. #include <syslog.h>
  118. #include <unistd.h>
  119. #include <getopt.h>
  120.  
  121. #define    TOOMANY        40        /* don't start more than TOOMANY */
  122. #define    CNT_INTVL    60        /* servers in CNT_INTVL sec. */
  123. #define    RETRYTIME    (60*10)        /* retry after bind or server fail */
  124.  
  125. #define    SIGBLOCK    (sigmask(SIGCHLD)|sigmask(SIGHUP)|sigmask(SIGALRM))
  126.  
  127.  
  128. int    debug = 0;
  129. int    nsock, maxsock;
  130. fd_set    allsock;
  131. int    options;
  132. int    timingout;
  133. int    toomany = TOOMANY;
  134. struct    servent *sp;
  135.  
  136. struct    servtab {
  137.     char    *se_service;        /* name of service */
  138.     int    se_socktype;        /* type of socket to use */
  139.     char    *se_proto;        /* protocol used */
  140.     short    se_wait;        /* single threaded server */
  141.     short    se_checked;        /* looked at during merge */
  142.     char    *se_user;        /* user name to run as */
  143.     struct    biltin *se_bi;        /* if built-in, description */
  144.     char    *se_server;        /* server program */
  145. #define    MAXARGV 20
  146.     char    *se_argv[MAXARGV+1];    /* program arguments */
  147.     int    se_fd;            /* open descriptor */
  148.     int    se_type;        /* type */
  149.     struct    sockaddr_in se_ctrladdr;/* bound address */
  150.     int    se_count;        /* number started since se_time */
  151.     struct    timeval se_time;    /* start of se_count */
  152.     struct    servtab *se_next;
  153. } *servtab;
  154.  
  155. #define NORM_TYPE    0
  156. #define MUX_TYPE    1
  157. #define MUXPLUS_TYPE    2
  158. #define ISMUX(sep)    (((sep)->se_type == MUX_TYPE) || \
  159.              ((sep)->se_type == MUXPLUS_TYPE))
  160. #define ISMUXPLUS(sep)    ((sep)->se_type == MUXPLUS_TYPE)
  161.  
  162.  
  163. void        chargen_dg __P((int, struct servtab *));
  164. void        chargen_stream __P((int, struct servtab *));
  165. void        close_sep __P((struct servtab *));
  166. void        config __P((int));
  167. void        daytime_dg __P((int, struct servtab *));
  168. void        daytime_stream __P((int, struct servtab *));
  169. void        discard_dg __P((int, struct servtab *));
  170. void        discard_stream __P((int, struct servtab *));
  171. void        echo_dg __P((int, struct servtab *));
  172. void        echo_stream __P((int, struct servtab *));
  173. void        endconfig __P((void));
  174. struct servtab *enter __P((struct servtab *));
  175. void        freeconfig __P((struct servtab *));
  176. struct servtab *getconfigent __P((void));
  177. void        machtime_dg __P((int, struct servtab *));
  178. void        machtime_stream __P((int, struct servtab *));
  179. char           *newstr __P((char *));
  180. char           *nextline __P((FILE *));
  181. void        print_service __P((char *, struct servtab *));
  182. void        reapchild __P((int));
  183. void        retry __P((int));
  184. int        setconfig __P((void));
  185. void        setup __P((struct servtab *));
  186. char           *sskip __P((char **));
  187. char           *skip __P((char **));
  188. struct servtab *tcpmux __P((int));
  189.  
  190. struct biltin {
  191.     char    *bi_service;        /* internally provided service name */
  192.     int    bi_socktype;        /* type of socket supported */
  193.     short    bi_fork;        /* 1 if should fork before call */
  194.     short    bi_wait;        /* 1 if should wait for child */
  195.     void    (*bi_fn)();        /* function which performs it */
  196. } biltins[] = {
  197.     /* Echo received data */
  198.     { "echo",    SOCK_STREAM,    1, 0,    echo_stream },
  199.     { "echo",    SOCK_DGRAM,    0, 0,    echo_dg },
  200.  
  201.     /* Internet /dev/null */
  202.     { "discard",    SOCK_STREAM,    1, 0,    discard_stream },
  203.     { "discard",    SOCK_DGRAM,    0, 0,    discard_dg },
  204.  
  205.     /* Return 32 bit time since 1970 */
  206.     { "time",    SOCK_STREAM,    0, 0,    machtime_stream },
  207.     { "time",    SOCK_DGRAM,    0, 0,    machtime_dg },
  208.  
  209.     /* Return human-readable time */
  210.     { "daytime",    SOCK_STREAM,    0, 0,    daytime_stream },
  211.     { "daytime",    SOCK_DGRAM,    0, 0,    daytime_dg },
  212.  
  213.     /* Familiar character generator */
  214.     { "chargen",    SOCK_STREAM,    1, 0,    chargen_stream },
  215.     { "chargen",    SOCK_DGRAM,    0, 0,    chargen_dg },
  216.  
  217.     { "tcpmux",    SOCK_STREAM,    1, 0,    (void (*)())tcpmux },
  218.  
  219.     { NULL }
  220. };
  221.  
  222. #define NUMINT    (sizeof(intab) / sizeof(struct inent))
  223. char    *CONFIG = PATH_INETDCONF;
  224. char    **Argv;
  225. char     *LastArg;
  226.  
  227. int
  228. main(argc, argv, envp)
  229.     int argc;
  230.     char *argv[], *envp[];
  231. {
  232.     struct servtab *sep;
  233.     struct passwd *pwd;
  234. #ifdef HAVE_SIGACTION
  235.     struct sigaction sa;
  236. #else
  237. #ifdef HAVE_SIGVEC
  238.     struct sigvec sv;
  239. #endif
  240. #endif    
  241.     int tmpint, ch, dofork;
  242.     pid_t pid;
  243.     char buf[50];
  244.  
  245.     Argv = argv;
  246.     if (envp == 0 || *envp == 0)
  247.         envp = argv;
  248.     while (*envp)
  249.         envp++;
  250.     LastArg = envp[-1] + strlen(envp[-1]);
  251.  
  252.     openlog("inetd", LOG_PID | LOG_NOWAIT, LOG_DAEMON);
  253.  
  254.     while ((ch = getopt(argc, argv, "dR:")) != EOF)
  255.         switch(ch) {
  256.         case 'd':
  257.             debug = 1;
  258.             options |= SO_DEBUG;
  259.             break;
  260.         case 'R': {    /* invocation rate */
  261.             char *p;
  262.  
  263.             tmpint = strtol(optarg, &p, 0);
  264.             if (tmpint < 1 || *p)
  265.                 syslog(LOG_ERR,
  266.                      "-R %s: bad value for service invocation rate",
  267.                     optarg);
  268.             else
  269.                 toomany = tmpint;
  270.             break;
  271.         }
  272.         case '?':
  273.         default:
  274.             syslog(LOG_ERR,
  275.                 "usage: inetd [-d] [-R rate] [conf-file]");
  276.             exit(1);
  277.         }
  278.     argc -= optind;
  279.     argv += optind;
  280.  
  281.     if (argc > 0)
  282.         CONFIG = argv[0];
  283.     if (debug == 0) {
  284.         daemon(0, 0);
  285.     }
  286.  
  287. #ifdef HAVE_SIGACTION
  288.     bzero (&sa, sizeof (sa));
  289.     sa.sa_mask = SIGBLOCK;
  290. #ifdef SA_RESTART
  291.     sa.sa_flags = SA_RESTART;
  292. #endif
  293.     sa.sa_handler = retry;
  294.     sigaction (SIGALRM, &sa, (struct sigaction *)0);
  295.     config (SIGHUP);
  296.     sa.sa_handler = config;
  297.     sigaction (SIGHUP, &sa, (struct sigaction *)0);
  298.     sa.sa_handler = reapchild;
  299.     sigaction (SIGCHLD, &sa, (struct sigaction *)0);
  300. #else
  301. #ifdef HAVE_SIGVEC
  302.     memset(&sv, 0, sizeof(sv));
  303.     sv.sv_mask = SIGBLOCK;
  304.     sv.sv_handler = retry;
  305.     sigvec(SIGALRM, &sv, (struct sigvec *)0);
  306.     config(SIGHUP);
  307.     sv.sv_handler = config;
  308.     sigvec(SIGHUP, &sv, (struct sigvec *)0);
  309.     sv.sv_handler = reapchild;
  310.     sigvec(SIGCHLD, &sv, (struct sigvec *)0);
  311. #else /* !HAVE_SIGVEC */
  312.     signal (SIGALRM, retry);
  313.     config (SIGHUP);
  314.     signal (SIGHUP, config);
  315.     signal (SIGCHLD, reapchild);
  316. #endif /* HAVE_SIGVEC */
  317. #endif /* HAVE_SIGACTION */
  318.  
  319.     {
  320.         /* space for daemons to overwrite environment for ps */
  321. #define    DUMMYSIZE    100
  322.         char dummy[DUMMYSIZE];
  323.  
  324.         (void)memset(dummy, 'x', sizeof(DUMMYSIZE) - 1);
  325.         dummy[DUMMYSIZE - 1] = '\0';
  326.         (void)setenv("inetd_dummy", dummy, 1);
  327.     }
  328.  
  329.     for (;;) {
  330.         int n, ctrl;
  331.         fd_set readable;
  332.  
  333.         if (nsock == 0) {
  334.         (void) sigblock(SIGBLOCK);
  335.         while (nsock == 0)
  336.             sigpause(0L);
  337.         (void) sigsetmask(0L);
  338.         }
  339.         readable = allsock;
  340.         if ((n = select(maxsock + 1, &readable, (fd_set *)0,
  341.         (fd_set *)0, (struct timeval *)0)) <= 0) {
  342.             if (n < 0 && errno != EINTR)
  343.             syslog(LOG_WARNING, "select: %m");
  344.             sleep(1);
  345.             continue;
  346.         }
  347.         for (sep = servtab; n && sep; sep = sep->se_next)
  348.             if (sep->se_fd != -1 && FD_ISSET(sep->se_fd, &readable)) {
  349.             n--;
  350.             if (debug)
  351.                 fprintf(stderr, "someone wants %s\n",
  352.                 sep->se_service);
  353.             if (!sep->se_wait && sep->se_socktype == SOCK_STREAM) {
  354.                 ctrl = accept(sep->se_fd, (struct sockaddr *)0,
  355.                 (int *)0);
  356.                 if (debug)
  357.                     fprintf(stderr, "accept, ctrl %d\n", ctrl);
  358.                 if (ctrl < 0) {
  359.                     if (errno != EINTR)
  360.                         syslog(LOG_WARNING,
  361.                         "accept (for %s): %m",
  362.                         sep->se_service);
  363.                     continue;
  364.                 }
  365.                 /*
  366.                  * Call tcpmux to find the real service to exec.
  367.                  */
  368.                 if (sep->se_bi &&
  369.                 sep->se_bi->bi_fn == (void (*)()) tcpmux) {
  370.                     sep = tcpmux(ctrl);
  371.                     if (sep == NULL) {
  372.                         close(ctrl);
  373.                         continue;
  374.                     }
  375.                 }
  376.             } else
  377.                 ctrl = sep->se_fd;
  378.             (void) sigblock(SIGBLOCK);
  379.             pid = 0;
  380.             dofork = (sep->se_bi == 0 || sep->se_bi->bi_fork);
  381.             if (dofork) {
  382.                 if (sep->se_count++ == 0)
  383.                 (void)gettimeofday(&sep->se_time,
  384.                     (struct timezone *)0);
  385.                 else if (sep->se_count >= toomany) {
  386.                 struct timeval now;
  387.  
  388.                 (void)gettimeofday(&now, (struct timezone *)0);
  389.                 if (now.tv_sec - sep->se_time.tv_sec >
  390.                     CNT_INTVL) {
  391.                     sep->se_time = now;
  392.                     sep->se_count = 1;
  393.                 } else {
  394.                     syslog(LOG_ERR,
  395.             "%s/%s server failing (looping), service terminated",
  396.                         sep->se_service, sep->se_proto);
  397.                     close_sep(sep);
  398.                     sigsetmask(0L);
  399.                     if (!timingout) {
  400.                         timingout = 1;
  401.                         alarm(RETRYTIME);
  402.                     }
  403.                     continue;
  404.                 }
  405.                 }
  406.                 pid = fork();
  407.             }
  408.             if (pid < 0) {
  409.                 syslog(LOG_ERR, "fork: %m");
  410.                 if (!sep->se_wait &&
  411.                 sep->se_socktype == SOCK_STREAM)
  412.                     close(ctrl);
  413.                 sigsetmask(0L);
  414.                 sleep(1);
  415.                 continue;
  416.             }
  417.             if (pid && sep->se_wait) {
  418.                 sep->se_wait = pid;
  419.                 if (sep->se_fd >= 0) {
  420.                 FD_CLR(sep->se_fd, &allsock);
  421.                     nsock--;
  422.                 }
  423.             }
  424.             sigsetmask(0L);
  425.             if (pid == 0) {
  426.                 if (debug && dofork)
  427.                 setsid();
  428.                 if (dofork) {
  429.                 if (debug)
  430.                     fprintf(stderr, "+ Closing from %d\n",
  431.                         maxsock);
  432.                 for (tmpint = maxsock; tmpint > 2; tmpint--)
  433.                     if (tmpint != ctrl)
  434.                         close(tmpint);
  435.                 }
  436.                 if (sep->se_bi)
  437.                 (*sep->se_bi->bi_fn)(ctrl, sep);
  438.                 else {
  439.                 if (debug)
  440.                     fprintf(stderr, "%d execl %s\n",
  441.                         getpid(), sep->se_server);
  442.                 dup2(ctrl, 0);
  443.                 close(ctrl);
  444.                 dup2(0, 1);
  445.                 dup2(0, 2);
  446.                 if ((pwd = getpwnam(sep->se_user)) == NULL) {
  447.                     syslog(LOG_ERR,
  448.                         "%s/%s: %s: No such user",
  449.                         sep->se_service, sep->se_proto,
  450.                         sep->se_user);
  451.                     if (sep->se_socktype != SOCK_STREAM)
  452.                         recv(0, buf, sizeof (buf), 0);
  453.                     _exit(1);
  454.                 }
  455.                 if (pwd->pw_uid) {
  456.                     if (setgid(pwd->pw_gid) < 0) {
  457.                         syslog(LOG_ERR,
  458.                           "%s: can't set gid %d: %m", 
  459.                           sep->se_service, pwd->pw_gid);
  460.                         _exit(1);
  461.                     }
  462.                     (void) initgroups(pwd->pw_name,
  463.                             pwd->pw_gid);
  464.                     if (setuid(pwd->pw_uid) < 0) {
  465.                         syslog(LOG_ERR,
  466.                           "%s: can't set uid %d: %m", 
  467.                           sep->se_service, pwd->pw_uid);
  468.                         _exit(1);
  469.                     }
  470.                 }
  471.                 execv(sep->se_server, sep->se_argv);
  472.                 if (sep->se_socktype != SOCK_STREAM)
  473.                     recv(0, buf, sizeof (buf), 0);
  474.                 syslog(LOG_ERR,
  475.                     "cannot execute %s: %m", sep->se_server);
  476.                 _exit(1);
  477.                 }
  478.             }
  479.             if (!sep->se_wait && sep->se_socktype == SOCK_STREAM)
  480.                 close(ctrl);
  481.         }
  482.     }
  483. }
  484.  
  485. void
  486. reapchild(signo)
  487.     int signo;
  488. {
  489.     int status;
  490.     pid_t pid;
  491.     struct servtab *sep;
  492.  
  493.     for (;;) {
  494.         pid = wait3(&status, WNOHANG, (struct rusage *)0);
  495.         if (pid <= 0)
  496.             break;
  497.         if (debug)
  498.             fprintf(stderr, "%d reaped, status %#x\n", 
  499.                 pid, status);
  500.         for (sep = servtab; sep; sep = sep->se_next)
  501.             if (sep->se_wait == pid) {
  502.                 if (status)
  503.                     syslog(LOG_WARNING,
  504.                         "%s: exit status 0x%x",
  505.                         sep->se_server, status);
  506.                 if (debug)
  507.                     fprintf(stderr, "restored %s, fd %d\n",
  508.                         sep->se_service, sep->se_fd);
  509.                 FD_SET(sep->se_fd, &allsock);
  510.                 nsock++;
  511.                 sep->se_wait = 1;
  512.             }
  513.     }
  514. }
  515.  
  516. void
  517. config(signo)
  518.     int signo;
  519. {
  520.     struct servtab *sep, *cp, **sepp;
  521.     struct passwd *pwd;
  522.     long omask;
  523.  
  524.     if (!setconfig()) {
  525.         syslog(LOG_ERR, "%s: %m", CONFIG);
  526.         return;
  527.     }
  528.     for (sep = servtab; sep; sep = sep->se_next)
  529.         sep->se_checked = 0;
  530.     while (cp = getconfigent()) {
  531.         if ((pwd = getpwnam(cp->se_user)) == NULL) {
  532.             syslog(LOG_ERR,
  533.                 "%s/%s: No such user '%s', service ignored",
  534.                 cp->se_service, cp->se_proto, cp->se_user);
  535.             continue;
  536.         }
  537.         for (sep = servtab; sep; sep = sep->se_next)
  538.             if (strcmp(sep->se_service, cp->se_service) == 0 &&
  539.                 strcmp(sep->se_proto, cp->se_proto) == 0)
  540.                 break;
  541.         if (sep != 0) {
  542.             int i;
  543.  
  544.             omask = sigblock(SIGBLOCK);
  545.             /*
  546.              * sep->se_wait may be holding the pid of a daemon
  547.              * that we're waiting for.  If so, don't overwrite
  548.              * it unless the config file explicitly says don't 
  549.              * wait.
  550.              */
  551.             if (cp->se_bi == 0 && 
  552.                 (sep->se_wait == 1 || cp->se_wait == 0))
  553.                 sep->se_wait = cp->se_wait;
  554. #define SWAP(a, b) { char *c = a; a = b; b = c; }
  555.             if (cp->se_user)
  556.                 SWAP(sep->se_user, cp->se_user);
  557.             if (cp->se_server)
  558.                 SWAP(sep->se_server, cp->se_server);
  559.             for (i = 0; i < MAXARGV; i++)
  560.                 SWAP(sep->se_argv[i], cp->se_argv[i]);
  561.             sigsetmask(omask);
  562.             freeconfig(cp);
  563.             if (debug)
  564.                 print_service("REDO", sep);
  565.         } else {
  566.             sep = enter(cp);
  567.             if (debug)
  568.                 print_service("ADD ", sep);
  569.         }
  570.         sep->se_checked = 1;
  571.         if (ISMUX(sep)) {
  572.             sep->se_fd = -1;
  573.             continue;
  574.         }
  575.         sp = getservbyname(sep->se_service, sep->se_proto);
  576.         if (sp == 0) {
  577.             syslog(LOG_ERR, "%s/%s: unknown service",
  578.                 sep->se_service, sep->se_proto);
  579.             sep->se_checked = 0;
  580.             continue;
  581.         }
  582.         if (sp->s_port != sep->se_ctrladdr.sin_port) {
  583.             sep->se_ctrladdr.sin_family = AF_INET;
  584.             sep->se_ctrladdr.sin_port = sp->s_port;
  585.             if (sep->se_fd >= 0)
  586.                 close_sep(sep);
  587.         }
  588.         if (sep->se_fd == -1)
  589.             setup(sep);
  590.     }
  591.     endconfig();
  592.     /*
  593.      * Purge anything not looked at above.
  594.      */
  595.     omask = sigblock(SIGBLOCK);
  596.     sepp = &servtab;
  597.     while (sep = *sepp) {
  598.         if (sep->se_checked) {
  599.             sepp = &sep->se_next;
  600.             continue;
  601.         }
  602.         *sepp = sep->se_next;
  603.         if (sep->se_fd >= 0)
  604.             close_sep(sep);
  605.         if (debug)
  606.             print_service("FREE", sep);
  607.         freeconfig(sep);
  608.         free((char *)sep);
  609.     }
  610.     (void) sigsetmask(omask);
  611. }
  612.  
  613. void
  614. retry(signo)
  615.     int signo;
  616. {
  617.     struct servtab *sep;
  618.  
  619.     timingout = 0;
  620.     for (sep = servtab; sep; sep = sep->se_next)
  621.         if (sep->se_fd == -1)
  622.             setup(sep);
  623. }
  624.  
  625. void
  626. setup(sep)
  627.     struct servtab *sep;
  628. {
  629.     int on = 1;
  630.  
  631.     if ((sep->se_fd = socket(AF_INET, sep->se_socktype, 0)) < 0) {
  632.         if (debug)
  633.             fprintf(stderr, "socket failed on %s/%s: %s\n", 
  634.                 sep->se_service, sep->se_proto,
  635.                 strerror(errno));
  636.         syslog(LOG_ERR, "%s/%s: socket: %m",
  637.             sep->se_service, sep->se_proto);
  638.         return;
  639.     }
  640. #define    turnon(fd, opt) \
  641. setsockopt(fd, SOL_SOCKET, opt, (char *)&on, sizeof (on))
  642.     if (strcmp(sep->se_proto, "tcp") == 0 && (options & SO_DEBUG) &&
  643.         turnon(sep->se_fd, SO_DEBUG) < 0)
  644.         syslog(LOG_ERR, "setsockopt (SO_DEBUG): %m");
  645.     if (turnon(sep->se_fd, SO_REUSEADDR) < 0)
  646.         syslog(LOG_ERR, "setsockopt (SO_REUSEADDR): %m");
  647. #undef turnon
  648.     if (bind(sep->se_fd, (struct sockaddr *)&sep->se_ctrladdr,
  649.         sizeof (sep->se_ctrladdr)) < 0) {
  650.         if (debug)
  651.             fprintf(stderr, "bind failed on %s/%s: %s\n",
  652.                 sep->se_service, sep->se_proto,
  653.                 strerror(errno));
  654.         syslog(LOG_ERR, "%s/%s: bind: %m",
  655.             sep->se_service, sep->se_proto);
  656.         (void) close(sep->se_fd);
  657.         sep->se_fd = -1;
  658.         if (!timingout) {
  659.             timingout = 1;
  660.             alarm(RETRYTIME);
  661.         }
  662.         return;
  663.     }
  664.     if (sep->se_socktype == SOCK_STREAM)
  665.         listen(sep->se_fd, 10);
  666.     FD_SET(sep->se_fd, &allsock);
  667.     nsock++;
  668.     if (sep->se_fd > maxsock)
  669.         maxsock = sep->se_fd;
  670.     if (debug) {
  671.         fprintf(stderr, "registered %s on %d\n",
  672.             sep->se_server, sep->se_fd);
  673.     }
  674. }
  675.  
  676. /*
  677.  * Finish with a service and its socket.
  678.  */
  679. void
  680. close_sep(sep)
  681.     struct servtab *sep;
  682. {
  683.     if (sep->se_fd >= 0) {
  684.         nsock--;
  685.         FD_CLR(sep->se_fd, &allsock);
  686.         (void) close(sep->se_fd);
  687.         sep->se_fd = -1;
  688.     }
  689.     sep->se_count = 0;
  690.     /*
  691.      * Don't keep the pid of this running deamon: when reapchild()
  692.      * reaps this pid, it would erroneously increment nsock.
  693.      */
  694.     if (sep->se_wait > 1)
  695.         sep->se_wait = 1;
  696. }
  697.  
  698. struct servtab *
  699. enter(cp)
  700.     struct servtab *cp;
  701. {
  702.     struct servtab *sep;
  703.     long omask;
  704.  
  705.     sep = (struct servtab *)malloc(sizeof (*sep));
  706.     if (sep == (struct servtab *)0) {
  707.         syslog(LOG_ERR, "Out of memory.");
  708.         exit(-1);
  709.     }
  710.     *sep = *cp;
  711.     sep->se_fd = -1;
  712.     omask = sigblock(SIGBLOCK);
  713.     sep->se_next = servtab;
  714.     servtab = sep;
  715.     sigsetmask(omask);
  716.     return (sep);
  717. }
  718.  
  719. FILE    *fconfig = NULL;
  720. struct    servtab serv;
  721. #ifdef LINE_MAX
  722. char    line[LINE_MAX];
  723. #else
  724. char     line[2048];
  725. #endif
  726.  
  727. int
  728. setconfig()
  729. {
  730.  
  731.     if (fconfig != NULL) {
  732.         fseek(fconfig, 0L, SEEK_SET);
  733.         return (1);
  734.     }
  735.     fconfig = fopen(CONFIG, "r");
  736.     return (fconfig != NULL);
  737. }
  738.  
  739. void
  740. endconfig()
  741. {
  742.     if (fconfig) {
  743.         (void) fclose(fconfig);
  744.         fconfig = NULL;
  745.     }
  746. }
  747.  
  748. struct servtab *
  749. getconfigent()
  750. {
  751.     struct servtab *sep = &serv;
  752.     int argc;
  753.     char *cp, *arg;
  754.     static char TCPMUX_TOKEN[] = "tcpmux/";
  755. #define MUX_LEN        (sizeof(TCPMUX_TOKEN)-1)
  756.  
  757. more:
  758.     while ((cp = nextline(fconfig)) && (*cp == '#' || *cp == '\0'))
  759.         ;
  760.     if (cp == NULL)
  761.         return ((struct servtab *)0);
  762.     /*
  763.      * clear the static buffer, since some fields (se_ctrladdr,
  764.      * for example) don't get initialized here.
  765.      */
  766.     memset((caddr_t)sep, 0, sizeof *sep);
  767.     arg = skip(&cp);
  768.     if (cp == NULL) {
  769.         /* got an empty line containing just blanks/tabs. */
  770.         goto more;
  771.     }
  772.     if (strncmp(arg, TCPMUX_TOKEN, MUX_LEN) == 0) {
  773.         char *c = arg + MUX_LEN;
  774.         if (*c == '+') {
  775.             sep->se_type = MUXPLUS_TYPE;
  776.             c++;
  777.         } else
  778.             sep->se_type = MUX_TYPE;
  779.         sep->se_service = newstr(c);
  780.     } else {
  781.         sep->se_service = newstr(arg);
  782.         sep->se_type = NORM_TYPE;
  783.     }
  784.     arg = sskip(&cp);
  785.     if (strcmp(arg, "stream") == 0)
  786.         sep->se_socktype = SOCK_STREAM;
  787.     else if (strcmp(arg, "dgram") == 0)
  788.         sep->se_socktype = SOCK_DGRAM;
  789.     else if (strcmp(arg, "rdm") == 0)
  790.         sep->se_socktype = SOCK_RDM;
  791.     else if (strcmp(arg, "seqpacket") == 0)
  792.         sep->se_socktype = SOCK_SEQPACKET;
  793.     else if (strcmp(arg, "raw") == 0)
  794.         sep->se_socktype = SOCK_RAW;
  795.     else
  796.         sep->se_socktype = -1;
  797.     sep->se_proto = newstr(sskip(&cp));
  798.     arg = sskip(&cp);
  799.     sep->se_wait = strcmp(arg, "wait") == 0;
  800.     if (ISMUX(sep)) {
  801.         /*
  802.          * Silently enforce "nowait" for TCPMUX services since
  803.          * they don't have an assigned port to listen on.
  804.          */
  805.         sep->se_wait = 0;
  806.  
  807.         if (strcmp(sep->se_proto, "tcp")) {
  808.             syslog(LOG_ERR, 
  809.                 "%s: bad protocol for tcpmux service %s",
  810.                 CONFIG, sep->se_service);
  811.             goto more;
  812.         }
  813.         if (sep->se_socktype != SOCK_STREAM) {
  814.             syslog(LOG_ERR, 
  815.                 "%s: bad socket type for tcpmux service %s",
  816.                 CONFIG, sep->se_service);
  817.             goto more;
  818.         }
  819.     }
  820.     sep->se_user = newstr(sskip(&cp));
  821.     sep->se_server = newstr(sskip(&cp));
  822.     if (strcmp(sep->se_server, "internal") == 0) {
  823.         struct biltin *bi;
  824.  
  825.         for (bi = biltins; bi->bi_service; bi++)
  826.             if (bi->bi_socktype == sep->se_socktype &&
  827.                 strcmp(bi->bi_service, sep->se_service) == 0)
  828.                 break;
  829.         if (bi->bi_service == 0) {
  830.             syslog(LOG_ERR, "internal service %s unknown",
  831.                 sep->se_service);
  832.             goto more;
  833.         }
  834.         sep->se_bi = bi;
  835.         sep->se_wait = bi->bi_wait;
  836.     } else
  837.         sep->se_bi = NULL;
  838.     argc = 0;
  839.     for (arg = skip(&cp); cp; arg = skip(&cp))
  840.         if (argc < MAXARGV)
  841.             sep->se_argv[argc++] = newstr(arg);
  842.     while (argc <= MAXARGV)
  843.         sep->se_argv[argc++] = NULL;
  844.     return (sep);
  845. }
  846.  
  847. void
  848. freeconfig(cp)
  849.     struct servtab *cp;
  850. {
  851.     int i;
  852.  
  853.     if (cp->se_service)
  854.         free(cp->se_service);
  855.     if (cp->se_proto)
  856.         free(cp->se_proto);
  857.     if (cp->se_user)
  858.         free(cp->se_user);
  859.     if (cp->se_server)
  860.         free(cp->se_server);
  861.     for (i = 0; i < MAXARGV; i++)
  862.         if (cp->se_argv[i])
  863.             free(cp->se_argv[i]);
  864. }
  865.  
  866.  
  867. /*
  868.  * Safe skip - if skip returns null, log a syntax error in the
  869.  * configuration file and exit.
  870.  */
  871. char *
  872. sskip(cpp)
  873.     char **cpp;
  874. {
  875.     char *cp;
  876.  
  877.     cp = skip(cpp);
  878.     if (cp == NULL) {
  879.         syslog(LOG_ERR, "%s: syntax error", CONFIG);
  880.         exit(-1);
  881.     }
  882.     return (cp);
  883. }
  884.  
  885. char *
  886. skip(cpp)
  887.     char **cpp;
  888. {
  889.     char *cp = *cpp;
  890.     char *start;
  891.  
  892. again:
  893.     while (*cp == ' ' || *cp == '\t')
  894.         cp++;
  895.     if (*cp == '\0') {
  896.         int c;
  897.  
  898.         c = getc(fconfig);
  899.         (void) ungetc(c, fconfig);
  900.         if (c == ' ' || c == '\t')
  901.             if (cp = nextline(fconfig))
  902.                 goto again;
  903.         *cpp = (char *)0;
  904.         return ((char *)0);
  905.     }
  906.     start = cp;
  907.     while (*cp && *cp != ' ' && *cp != '\t')
  908.         cp++;
  909.     if (*cp != '\0')
  910.         *cp++ = '\0';
  911.     *cpp = cp;
  912.     return (start);
  913. }
  914.  
  915. char *
  916. nextline(fd)
  917.     FILE *fd;
  918. {
  919.     char *cp;
  920.  
  921.     if (fgets(line, sizeof (line), fd) == NULL)
  922.         return ((char *)0);
  923.     cp = strchr(line, '\n');
  924.     if (cp)
  925.         *cp = '\0';
  926.     return (line);
  927. }
  928.  
  929. char *
  930. newstr(cp)
  931.     char *cp;
  932. {
  933.     if (cp = strdup(cp ? cp : ""))
  934.         return (cp);
  935.     syslog(LOG_ERR, "strdup: %m");
  936.     exit(-1);
  937. }
  938.  
  939. void
  940. set_proc_title(a, s)
  941.     char *a;
  942.     int s;
  943. {
  944.     int size;
  945.     char *cp;
  946.     struct sockaddr_in sin;
  947.     char buf[80];
  948.  
  949.     cp = Argv[0];
  950.     size = sizeof(sin);
  951.     if (getpeername(s, (struct sockaddr *)&sin, &size) == 0)
  952.         (void) sprintf(buf, "-%s [%s]", a, inet_ntoa(sin.sin_addr)); 
  953.     else
  954.         (void) sprintf(buf, "-%s", a); 
  955.     strncpy(cp, buf, LastArg - cp);
  956.     cp += strlen(cp);
  957.     while (cp < LastArg)
  958.         *cp++ = ' ';
  959. }
  960.  
  961. /*
  962.  * Internet services provided internally by inetd:
  963.  */
  964. #define    BUFSIZE    8192
  965.  
  966. /* ARGSUSED */
  967. void
  968. echo_stream(s, sep)        /* Echo service -- echo data back */
  969.     int s;
  970.     struct servtab *sep;
  971. {
  972.     char buffer[BUFSIZE];
  973.     int i;
  974.  
  975.     set_proc_title(sep->se_service, s);
  976.     while ((i = read(s, buffer, sizeof(buffer))) > 0 &&
  977.         write(s, buffer, i) > 0)
  978.         ;
  979.     exit(0);
  980. }
  981.  
  982. /* ARGSUSED */
  983. void
  984. echo_dg(s, sep)            /* Echo service -- echo data back */
  985.     int s;
  986.     struct servtab *sep;
  987. {
  988.     char buffer[BUFSIZE];
  989.     int i, size;
  990.     struct sockaddr sa;
  991.  
  992.     size = sizeof(sa);
  993.     if ((i = recvfrom(s, buffer, sizeof(buffer), 0, &sa, &size)) < 0)
  994.         return;
  995.     (void) sendto(s, buffer, i, 0, &sa, sizeof(sa));
  996. }
  997.  
  998. /* ARGSUSED */
  999. void
  1000. discard_stream(s, sep)        /* Discard service -- ignore data */
  1001.     int s;
  1002.     struct servtab *sep;
  1003. {
  1004.     int ret;
  1005.     char buffer[BUFSIZE];
  1006.  
  1007.     set_proc_title(sep->se_service, s);
  1008.     while (1) {
  1009.         while ((ret = read(s, buffer, sizeof(buffer))) > 0)
  1010.             ;
  1011.         if (ret == 0 || errno != EINTR)
  1012.             break;
  1013.     }
  1014.     exit(0);
  1015. }
  1016.  
  1017. /* ARGSUSED */
  1018. void
  1019. discard_dg(s, sep)        /* Discard service -- ignore data */
  1020.     int s;
  1021.     struct servtab *sep;
  1022. {
  1023.     char buffer[BUFSIZE];
  1024.  
  1025.     (void) read(s, buffer, sizeof(buffer));
  1026. }
  1027.  
  1028. #include <ctype.h>
  1029. #define LINESIZ 72
  1030. char ring[128];
  1031. char *endring;
  1032.  
  1033. void
  1034. initring()
  1035. {
  1036.     int i;
  1037.  
  1038.     endring = ring;
  1039.  
  1040.     for (i = 0; i <= 128; ++i)
  1041.         if (isprint(i))
  1042.             *endring++ = i;
  1043. }
  1044.  
  1045. /* ARGSUSED */
  1046. void
  1047. chargen_stream(s, sep)        /* Character generator */
  1048.     int s;
  1049.     struct servtab *sep;
  1050. {
  1051.     int len;
  1052.     char *rs, text[LINESIZ+2];
  1053.  
  1054.     set_proc_title(sep->se_service, s);
  1055.  
  1056.     if (!endring) {
  1057.         initring();
  1058.         rs = ring;
  1059.     }
  1060.  
  1061.     text[LINESIZ] = '\r';
  1062.     text[LINESIZ + 1] = '\n';
  1063.     for (rs = ring;;) {
  1064.         if ((len = endring - rs) >= LINESIZ)
  1065.             memmove(text, rs, LINESIZ);
  1066.         else {
  1067.             memmove(text, rs, len);
  1068.             memmove(text + len, ring, LINESIZ - len);
  1069.         }
  1070.         if (++rs == endring)
  1071.             rs = ring;
  1072.         if (write(s, text, sizeof(text)) != sizeof(text))
  1073.             break;
  1074.     }
  1075.     exit(0);
  1076. }
  1077.  
  1078. /* ARGSUSED */
  1079. void
  1080. chargen_dg(s, sep)        /* Character generator */
  1081.     int s;
  1082.     struct servtab *sep;
  1083. {
  1084.     struct sockaddr sa;
  1085.     static char *rs;
  1086.     int len, size;
  1087.     char text[LINESIZ+2];
  1088.  
  1089.     if (endring == 0) {
  1090.         initring();
  1091.         rs = ring;
  1092.     }
  1093.  
  1094.     size = sizeof(sa);
  1095.     if (recvfrom(s, text, sizeof(text), 0, &sa, &size) < 0)
  1096.         return;
  1097.  
  1098.     if ((len = endring - rs) >= LINESIZ)
  1099.         memmove(text, rs, LINESIZ);
  1100.     else {
  1101.         memmove(text, rs, len);
  1102.         memmove(text + len, ring, LINESIZ - len);
  1103.     }
  1104.     if (++rs == endring)
  1105.         rs = ring;
  1106.     text[LINESIZ] = '\r';
  1107.     text[LINESIZ + 1] = '\n';
  1108.     (void) sendto(s, text, sizeof(text), 0, &sa, sizeof(sa));
  1109. }
  1110.  
  1111. /*
  1112.  * Return a machine readable date and time, in the form of the
  1113.  * number of seconds since midnight, Jan 1, 1900.  Since gettimeofday
  1114.  * returns the number of seconds since midnight, Jan 1, 1970,
  1115.  * we must add 2208988800 seconds to this figure to make up for
  1116.  * some seventy years Bell Labs was asleep.
  1117.  */
  1118.  
  1119. long
  1120. machtime()
  1121. {
  1122.     struct timeval tv;
  1123.  
  1124.     if (gettimeofday(&tv, (struct timezone *)0) < 0) {
  1125.         if (debug)
  1126.             fprintf(stderr, "Unable to get time of day\n");
  1127.         return (0L);
  1128.     }
  1129. #define    OFFSET ((u_long)25567 * 24*60*60)
  1130.     return (htonl((long)(tv.tv_sec + OFFSET)));
  1131. #undef OFFSET
  1132. }
  1133.  
  1134. /* ARGSUSED */
  1135. void
  1136. machtime_stream(s, sep)
  1137.     int s;
  1138.     struct servtab *sep;
  1139. {
  1140.     long result;
  1141.  
  1142.     result = machtime();
  1143.     (void) write(s, (char *) &result, sizeof(result));
  1144. }
  1145.  
  1146. /* ARGSUSED */
  1147. void
  1148. machtime_dg(s, sep)
  1149.     int s;
  1150.     struct servtab *sep;
  1151. {
  1152.     long result;
  1153.     struct sockaddr sa;
  1154.     int size;
  1155.  
  1156.     size = sizeof(sa);
  1157.     if (recvfrom(s, (char *)&result, sizeof(result), 0, &sa, &size) < 0)
  1158.         return;
  1159.     result = machtime();
  1160.     (void) sendto(s, (char *) &result, sizeof(result), 0, &sa, sizeof(sa));
  1161. }
  1162.  
  1163. /* ARGSUSED */
  1164. void
  1165. daytime_stream(s, sep)        /* Return human-readable time of day */
  1166.     int s;
  1167.     struct servtab *sep;
  1168. {
  1169.     char buffer[256];
  1170.     time_t clock;
  1171.  
  1172.     clock = time((time_t *) 0);
  1173.  
  1174.     (void) sprintf(buffer, "%.24s\r\n", ctime(&clock));
  1175.     (void) write(s, buffer, strlen(buffer));
  1176. }
  1177.  
  1178. /* ARGSUSED */
  1179. void
  1180. daytime_dg(s, sep)        /* Return human-readable time of day */
  1181.     int s;
  1182.     struct servtab *sep;
  1183. {
  1184.     char buffer[256];
  1185.     time_t clock;
  1186.     struct sockaddr sa;
  1187.     int size;
  1188.  
  1189.     clock = time((time_t *) 0);
  1190.  
  1191.     size = sizeof(sa);
  1192.     if (recvfrom(s, buffer, sizeof(buffer), 0, &sa, &size) < 0)
  1193.         return;
  1194.     (void) sprintf(buffer, "%.24s\r\n", ctime(&clock));
  1195.     (void) sendto(s, buffer, strlen(buffer), 0, &sa, sizeof(sa));
  1196. }
  1197.  
  1198. /*
  1199.  * print_service:
  1200.  *    Dump relevant information to stderr
  1201.  */
  1202. void
  1203. print_service(action, sep)
  1204.     char *action;
  1205.     struct servtab *sep;
  1206. {
  1207.     fprintf(stderr,
  1208.         "%s: %s proto=%s, wait=%d, user=%s builtin=%x server=%s\n",
  1209.         action, sep->se_service, sep->se_proto,
  1210.         sep->se_wait, sep->se_user, (int)sep->se_bi, sep->se_server);
  1211. }
  1212.  
  1213. /*
  1214.  *  Based on TCPMUX.C by Mark K. Lottor November 1988
  1215.  *  sri-nic::ps:<mkl>tcpmux.c
  1216.  */
  1217.  
  1218.  
  1219. static int        /* # of characters upto \r,\n or \0 */
  1220. fd_getline(fd, buf, len)
  1221.     int fd;
  1222.     char *buf;
  1223.     int len;
  1224. {
  1225.     int count = 0, n;
  1226.  
  1227.     do {
  1228.         n = read(fd, buf, len-count);
  1229.         if (n == 0)
  1230.             return (count);
  1231.         if (n < 0)
  1232.             return (-1);
  1233.         while (--n >= 0) {
  1234.             if (*buf == '\r' || *buf == '\n' || *buf == '\0')
  1235.                 return (count);
  1236.             count++;
  1237.             buf++;
  1238.         }
  1239.     } while (count < len);
  1240.     return (count);
  1241. }
  1242.  
  1243. #define MAX_SERV_LEN    (256+2)        /* 2 bytes for \r\n */
  1244.  
  1245. #define strwrite(fd, buf)    (void) write(fd, buf, sizeof(buf)-1)
  1246.  
  1247. struct servtab *
  1248. tcpmux(s)
  1249.     int s;
  1250. {
  1251.     struct servtab *sep;
  1252.     char service[MAX_SERV_LEN+1];
  1253.     int len;
  1254.  
  1255.     /* Get requested service name */
  1256.     if ((len = fd_getline(s, service, MAX_SERV_LEN)) < 0) {
  1257.         strwrite(s, "-Error reading service name\r\n");
  1258.         return (NULL);
  1259.     }
  1260.     service[len] = '\0';
  1261.  
  1262.     if (debug)
  1263.         fprintf(stderr, "tcpmux: someone wants %s\n", service);
  1264.  
  1265.     /*
  1266.      * Help is a required command, and lists available services,
  1267.      * one per line.
  1268.      */
  1269.     if (!strcasecmp(service, "help")) {
  1270.         for (sep = servtab; sep; sep = sep->se_next) {
  1271.             if (!ISMUX(sep))
  1272.                 continue;
  1273.             (void)write(s,sep->se_service,strlen(sep->se_service));
  1274.             strwrite(s, "\r\n");
  1275.         }
  1276.         return (NULL);
  1277.     }
  1278.  
  1279.     /* Try matching a service in inetd.conf with the request */
  1280.     for (sep = servtab; sep; sep = sep->se_next) {
  1281.         if (!ISMUX(sep))
  1282.             continue;
  1283.         if (!strcasecmp(service, sep->se_service)) {
  1284.             if (ISMUXPLUS(sep)) {
  1285.                 strwrite(s, "+Go\r\n");
  1286.             }
  1287.             return (sep);
  1288.         }
  1289.     }
  1290.     strwrite(s, "-Service not available\r\n");
  1291.     return (NULL);
  1292. }
  1293.