Logo Search packages:      
Sourcecode: netdiag version File versions

discard.c

/* standalone discard and other services
   2000.10.16 Rafal Maszkowski <rzm@icm.edu.pl>
   based on Richard Stevens unpv12e/tcpcliserv/tcpserv09.c

   Adding a new service: see MODE_TEMPLATE

   TODO:
   - binding a specific IPv[46] local address
*/

#include <sys/types.h>
#include <sys/wait.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <signal.h>
#include <errno.h>
#include <getopt.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <strings.h>    /* for Solaris */
#include "compat.h"

/* Following could be derived from SOMAXCONN in <sys/socket.h>, but many
   kernels still #define it as 5, while actually supporting many more */
#define LISTENQ         1024  /* 2nd argument to listen() */

/* Following shortens all the type casts of pointer arguments */
#define     SA    struct sockaddr

/* Put the program in the background, and dissociate from the controlling
   terminal.  If NOCHDIR is zero, do `chdir ("/")'.  If NOCLOSE is zero,
   redirects stdin, stdout, and stderr to /dev/null.  */
/* extern int daemon __P ((int __nochdir, int __noclose)); */

#define     MODE_ECHO   1
#define     MODE_DISCARD      2
#define     MODE_CHARGEN      3
#define     MODE_TEMPLATE     999

#define     CHARGEN_START     32
#define     CHARGEN_STOP      126
#define     CHARGEN_WIDTH     72

int   defports[] = { 0, 7, 9, 19, 0 };

int   opt_proto = SOCK_STREAM;

void
process(int fdin, int fdout, int opt_mode) {
      int   bytes, ch, ind;
      char  buf[4096];

      switch(opt_mode) {
      case MODE_ECHO:         for ( ; ; ) {
                  if ((bytes = read(fdin, buf, sizeof(buf))) <= 0) break;
                  if ((bytes = write(fdin, buf, bytes)) <= 0) break;
            }
            break;

      case MODE_CHARGEN:      
            for (ch = CHARGEN_START; ch<=CHARGEN_STOP; ch++) buf[ch-CHARGEN_START] = ch;
            bcopy(buf, buf+CHARGEN_STOP-CHARGEN_START+1, CHARGEN_STOP-CHARGEN_START+1);
            for (ind = 0; ; ind++) {
                  if ((bytes = write(fdout, buf+ind, CHARGEN_WIDTH)) <= 0) break;
                  if ((bytes = write(fdout, "\r\n", 2)) <= 0) break;
                  if (ind>CHARGEN_STOP-CHARGEN_START) ind = 0;
            }
            break;

      case MODE_TEMPLATE:     
            strncpy(buf, "Just a template for new services.\r\n", sizeof(buf));
            write(fdout, buf, strlen(buf));
            break;

      default: /* MODE_DISCARD */   for ( ; ; ) {
                  if ((bytes = read(fdin, buf, sizeof(buf))) <= 0) break;
            }
      }
}

void
sig_chld(int signo) {
      pid_t pid;
      int   stat;

      while ( (pid = waitpid(-1, &stat, WNOHANG)) > 0) {
            /* printf("child %d terminated\n", pid); */
      }
      return;
}

int
name2mode(char *name) {
      if (strncmp(name, "discard",  8)==0) return MODE_DISCARD;
      if (strncmp(name, "echo",     5)==0) return MODE_ECHO;
      if (strncmp(name, "chargen",  8)==0) return MODE_CHARGEN;
      if (strncmp(name, "template", 9)==0) return MODE_TEMPLATE;
      return -1;
}

void
usage(char *name) {
      fprintf(stderr, "\n");
      fprintf(stderr, "Usage: %s [options]\n\n", name);
      fprintf(stderr, "%s is a standalone echo/discard/chargen service.\n", name);
      fprintf(stderr, "\n");
      fprintf(stderr, "Options:\n");
      fprintf(stderr, "\n");
#ifdef HAVE_GETADDRINFO
        fprintf(stderr, "-4, --ipv4         bind ipv4 address (default)\n");
        fprintf(stderr, "-6, --ipv6         bind ipv6 address (means: both?)\n");
#endif /* HAVE_GETADDRINFO */
      fprintf(stderr, "-f, --foreground   do not be a daemon (ignored with -i)\n");
      fprintf(stderr, "-h, --help         this help\n");
      fprintf(stderr, "-i, --inetd        runs as an inetd service\n");
      fprintf(stderr, "-m MODE            MODE can be echo, discard or chargen (default: %s)\n", name);
      fprintf(stderr, "-p PORT            listen on PORT instead of default 7/9/19\n");
        fprintf(stderr, "-t, --tcp          use TCP%s\n", opt_proto==SOCK_STREAM ? " (default)" : "" );
        fprintf(stderr, "-u, --udp          use UDP%s\n", opt_proto==SOCK_DGRAM  ? " (default)" : "" );
      exit(0);
}

void usage_small(char *name) {
      fprintf(stderr, "type %s --help for help\n", name);
      exit(0);
}

int
main(int argc, char **argv)
{
      int               listenfd, connfd, opt_fg = 0, opt_inetd = 0,
            opt_port = -1, opt_mode = MODE_DISCARD, on = 1, opt_family = AF_INET;
      pid_t             childpid;
      void              sig_chld(int);
      char              fname[100], *name = NULL;

      /* changing default, based on program name */
      strncpy(fname, argv[0], sizeof(fname));
      if ( (name = rindex(fname, '/')) ) {
            name++;
      } else {
            name = fname;
      }
      opt_mode = name2mode(name);

      while (1) {
            static const struct option long_options[] = {
                  { "foreground", no_argument,        NULL, 'f' },
                  { "help",   no_argument,            NULL, 'h' },
                  { "inetd",  no_argument,            NULL, 'i' },
                  { "ipv4",   no_argument,            NULL, '4' },
                  { "ipv6",   no_argument,            NULL, '6' },
                  { "mode",   required_argument,      NULL, 'm' },
                  { "tcp",    no_argument,            NULL, 't' },
                  { "udp",    no_argument,            NULL, 'u' },
                  { NULL,           0,                NULL, 0 } };
            int     optchar;

            if ((optchar = getopt_long (argc, argv, "46fhim:p:tu", long_options, NULL)) == -1) break;
            switch (optchar) {
#ifdef HAVE_GETADDRINFO
                  case '4':   opt_family = AF_INET;         break;
                  case '6':   opt_family = AF_INET6;        break;
#endif /* HAVE_GETADDRINFO */
                  case 'f':       opt_fg = 1;               break;
                  case 'h':       usage(name);              break;
                  case 'i':       opt_inetd = 1;                  break;
                  case 'm':   if ( (opt_mode = name2mode(optarg)) == -1 ) { printf("Unknown mode\n"); exit(1); }
                                                      break;
                  case 'p': opt_port = atoi(optarg);
                        if ((opt_port < 0) || (opt_port > 65535)) {
                              printf("Local port must be in 0..65535 range\n"); exit(1);
                        }                             break;
                  case 't':   opt_proto = SOCK_STREAM;      break;
                  case 'u':   opt_proto = SOCK_DGRAM;       break;
                  default:    usage_small(name);
            }
      }

        if (opt_port == -1) opt_port = defports[opt_mode];

      if (opt_inetd) {
            process(STDIN_FILENO, STDOUT_FILENO, opt_mode);
            exit(0);
      }

      if (!opt_fg) daemon(0, 1);

      if ( (listenfd = socket(opt_family, opt_proto, 0)) == -1 ) {
            printf("socket(%d): %s\n", opt_family, strerror(errno)); exit(1);
      }

      setsockopt(listenfd, SOL_SOCKET, SO_REUSEADDR, (char *)&on, sizeof(on));

      if (opt_family == AF_INET) {
            struct sockaddr_in      servaddr;
            bzero(&servaddr, sizeof(servaddr));
            servaddr.sin_family      = opt_family;
            servaddr.sin_addr.s_addr = htonl(INADDR_ANY);
            servaddr.sin_port        = htons(opt_port);
            if (bind(listenfd, (SA *) &servaddr, sizeof(servaddr)) == -1) {
                  printf("bind(%d): %s\n", opt_family, strerror(errno)); exit(1);
            }
#ifdef HAVE_GETADDRINFO
      } else {
            struct sockaddr_in6     servaddr;
            bzero(&servaddr, sizeof(servaddr));
            servaddr.sin6_family = opt_family;
            servaddr.sin6_flowinfo = 0;
            servaddr.sin6_port = htons(opt_port);
            servaddr.sin6_addr = in6addr_any;  /* structure assignment */
            if (bind(listenfd, (SA *) &servaddr, sizeof(servaddr)) == -1) {
                  printf("bind(%d): %s\n", opt_family, strerror(errno)); exit(1);
            }
#endif /* HAVE_GETADDRINFO */
      }

      listen(listenfd, LISTENQ);

      signal(SIGCHLD, sig_chld);

      for ( ; ; ) {
      socklen_t         clilen;
      struct sockaddr_in      cliaddr;
            clilen = sizeof(cliaddr);
            if ( (connfd = accept(listenfd, (SA *) &cliaddr, &clilen)) < 0) {
                  if (errno == EINTR) continue;       /* back to for() */
                  else strerror(errno);
            }

            if ( (childpid = fork()) == 0) {    /* child process */
                  close(listenfd);  /* close listening socket */
                  process(connfd, connfd, opt_mode);  /* process the request */
                  exit(0);
            }
            close(connfd);                /* parent closes connected socket */
      }

      return 0;
}

Generated by  Doxygen 1.6.0   Back to index