[
  {
    "path": ".gitignore",
    "content": "endlessh\n"
  },
  {
    "path": "Dockerfile",
    "content": "FROM alpine:3.9 as builder\nRUN apk add --no-cache build-base\nADD endlessh.c Makefile /\nRUN make\n\n\nFROM alpine:3.9\n\nCOPY --from=builder /endlessh /\n\nEXPOSE 2222/tcp\n\nENTRYPOINT [\"/endlessh\"]\n\nCMD [\"-v\"]\n"
  },
  {
    "path": "Makefile",
    "content": ".POSIX:\nCC       = cc\nCFLAGS   = -std=c99 -Wall -Wextra -Wno-missing-field-initializers -Os\nCPPFLAGS =\nLDFLAGS  = -ggdb3\nLDLIBS   =\nPREFIX   = /usr/local\n\nall: endlessh\n\nendlessh: endlessh.c\n\t$(CC) $(CFLAGS) $(CPPFLAGS) $(LDFLAGS) -o $@ endlessh.c $(LDLIBS)\n\ninstall: endlessh\n\tinstall -d $(DESTDIR)$(PREFIX)/bin\n\tinstall -m 755 endlessh $(DESTDIR)$(PREFIX)/bin/\n\tinstall -d $(DESTDIR)$(PREFIX)/share/man/man1\n\tinstall -m 644 endlessh.1 $(DESTDIR)$(PREFIX)/share/man/man1/\n\nclean:\n\trm -rf endlessh\n"
  },
  {
    "path": "README.md",
    "content": "# Endlessh: an SSH tarpit\n\nEndlessh is an SSH tarpit [that *very* slowly sends an endless, random\nSSH banner][np]. It keeps SSH clients locked up for hours or even days\nat a time. The purpose is to put your real SSH server on another port\nand then let the script kiddies get stuck in this tarpit instead of\nbothering a real server.\n\nSince the tarpit is in the banner before any cryptographic exchange\noccurs, this program doesn't depend on any cryptographic libraries. It's\na simple, single-threaded, standalone C program. It uses `poll()` to\ntrap multiple clients at a time.\n\n## Usage\n\nUsage information is printed with `-h`.\n\n```\nUsage: endlessh [-vhs] [-d MS] [-f CONFIG] [-l LEN] [-m LIMIT] [-p PORT]\n  -4        Bind to IPv4 only\n  -6        Bind to IPv6 only\n  -d INT    Message millisecond delay [10000]\n  -f        Set and load config file [/etc/endlessh/config]\n  -h        Print this help message and exit\n  -l INT    Maximum banner line length (3-255) [32]\n  -m INT    Maximum number of clients [4096]\n  -p INT    Listening port [2222]\n  -s        Print diagnostics to syslog instead of standard output\n  -v        Print diagnostics (repeatable)\n```\n\nArgument order matters. The configuration file is loaded when the `-f`\nargument is processed, so only the options that follow will override the\nconfiguration file.\n\nBy default no log messages are produced. The first `-v` enables basic\nlogging and a second `-v` enables debugging logging (noisy). All log\nmessages are sent to standard output by default. `-s` causes them to be\nsent to syslog.\n\n    endlessh -v >endlessh.log 2>endlessh.err\n\nA SIGTERM signal will gracefully shut down the daemon, allowing it to\nwrite a complete, consistent log.\n\nA SIGHUP signal requests a reload of the configuration file (`-f`).\n\nA SIGUSR1 signal will print connections stats to the log.\n\n## Sample Configuration File\n\nThe configuration file has similar syntax to OpenSSH.\n\n```\n# The port on which to listen for new SSH connections.\nPort 2222\n\n# The endless banner is sent one line at a time. This is the delay\n# in milliseconds between individual lines.\nDelay 10000\n\n# The length of each line is randomized. This controls the maximum\n# length of each line. Shorter lines may keep clients on for longer if\n# they give up after a certain number of bytes.\nMaxLineLength 32\n\n# Maximum number of connections to accept at a time. Connections beyond\n# this are not immediately rejected, but will wait in the queue.\nMaxClients 4096\n\n# Set the detail level for the log.\n#   0 = Quiet\n#   1 = Standard, useful log messages\n#   2 = Very noisy debugging information\nLogLevel 0\n\n# Set the family of the listening socket\n#   0 = Use IPv4 Mapped IPv6 (Both v4 and v6, default)\n#   4 = Use IPv4 only\n#   6 = Use IPv6 only\nBindFamily 0\n```\n\n## Build issues\n\nSome more esoteric systems require extra configuration when building.\n\n### RHEL 6 / CentOS 6\n\nThis system uses a version of glibc older than 2.17 (December 2012), and\n`clock_gettime(2)` is still in librt. For these systems you will need to\nlink against librt:\n\n    make LDLIBS=-lrt\n\n### Solaris / illumos\n\nThese systems don't include all the necessary functionality in libc and\nthe linker requires some extra libraries:\n\n    make CC=gcc LDLIBS='-lnsl -lrt -lsocket'\n\nIf you're not using GCC or Clang, also override `CFLAGS` and `LDFLAGS`\nto remove GCC-specific options. For example, on Solaris:\n\n    make CFLAGS=-fast LDFLAGS= LDLIBS='-lnsl -lrt -lsocket'\n\nThe feature test macros on these systems isn't reliable, so you may also\nneed to use `-D__EXTENSIONS__` in `CFLAGS`.\n\n### OpenBSD\n\nThe man page needs to go into a different path for OpenBSD's `man` command:\n\n```\ndiff --git a/Makefile b/Makefile\nindex 119347a..dedf69d 100644\n--- a/Makefile\n+++ b/Makefile\n@@ -14,8 +14,8 @@ endlessh: endlessh.c\n install: endlessh\n        install -d $(DESTDIR)$(PREFIX)/bin\n        install -m 755 endlessh $(DESTDIR)$(PREFIX)/bin/\n-       install -d $(DESTDIR)$(PREFIX)/share/man/man1\n-       install -m 644 endlessh.1 $(DESTDIR)$(PREFIX)/share/man/man1/\n+       install -d $(DESTDIR)$(PREFIX)/man/man1\n+       install -m 644 endlessh.1 $(DESTDIR)$(PREFIX)/man/man1/\n\n clean:\n        rm -rf endlessh\n```\n\n[np]: https://nullprogram.com/blog/2019/03/22/\n"
  },
  {
    "path": "UNLICENSE",
    "content": "This is free and unencumbered software released into the public domain.\n\nAnyone is free to copy, modify, publish, use, compile, sell, or\ndistribute this software, either in source code form or as a compiled\nbinary, for any purpose, commercial or non-commercial, and by any\nmeans.\n\nIn jurisdictions that recognize copyright laws, the author or authors\nof this software dedicate any and all copyright interest in the\nsoftware to the public domain. We make this dedication for the benefit\nof the public at large and to the detriment of our heirs and\nsuccessors. We intend this dedication to be an overt act of\nrelinquishment in perpetuity of all present and future rights to this\nsoftware under copyright law.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND,\nEXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF\nMERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.\nIN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR\nOTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,\nARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR\nOTHER DEALINGS IN THE SOFTWARE.\n\nFor more information, please refer to <http://unlicense.org/>\n"
  },
  {
    "path": "endlessh.1",
    "content": ".Dd $Mdocdate: January 29 2020 $\n.Dt ENDLESSH 1\n.Os\n.Sh NAME\n.Nm endless\n.Nd An SSH tarpit\n.Sh SYNOPSIS\n.Nm endless\n.Op Fl 46chsvV\n.Op Fl d Ar delay\n.Op Fl f Ar config\n.Op Fl l Ar max banner length\n.Op Fl m Ar max clients\n.Op Fl p Ar port\n.Sh DESCRIPTION\n.Nm\nis an SSH tarpit that very slowly\nsends an endless, random SSH banner.\n.Pp\n.Nm\nkeeps SSH clients locked up for hours or even days at a time.\nThe purpose is to put your real SSH server on another port\nand then let the script kiddies get stuck in this tarpit\ninstead of bothering a real server.\n.Pp\nSince the tarpit is in the banner before any cryptographic\nexchange occurs, this program doesn't depend on any cryptographic\nlibraries. It's a simple, single-threaded, standalone C program.\nIt uses poll() to trap multiple clients at a time.\n.Pp\nThe options are as follows:\n.Bl -tag -width Ds\n.It Fl 4\nForces\n.Nm\nto use IPv4 addresses only.\n.It Fl 6\nForces\n.Nm\nto use IPv6 addresses only.\n.It Fl d Ar delay\nMessage milliseconds delay. Default: 10000\n.It Fl f Ar config\nSet and load config file.\nBy default\n.Nm\nlooks for /etc/endlessh/config.\n.It Fl h\nPrint the help message and exit.\n.It Fl l Ar max banner length\nMaximum banner line length (3-255). Default: 32\n.It Fl m Ar max clients\nMaximum number of clients. Default: 4096\n.It Fl p Ar port\nSet the listening port. By default\n.Nm\nlistens on port 2222.\n.It Fl s\nPrint diagnostics to syslog. By default\n.Nm\nprints them to standard output.\n.It Fl v\nPrint diagnostics. Can be specified up to twice to increase verbosity.\n.It Fl V\nCauses\n.Nm\nto print version information and exit.\n.El\n.Pp\nIf\n.Nm\nreceives the SIGTERM signal it will gracefully shut\ndown the daemon, allowing it to write a complete, consistent log.\n.Pp\nA SIGHUP signal requests a reload of its configuration file.\n.Pp\nA SIGUSR1 signal will print connections stats to the log.\n.Sh FILES\n.Bl -tag -width /etc/endlessh/config -compact\n.It Pa /etc/endlessh/config\nThe default\n.Nm\nconfiguration file.\n.El\n"
  },
  {
    "path": "endlessh.c",
    "content": "/* Endlessh: an SSH tarpit\n *\n * This is free and unencumbered software released into the public domain.\n */\n#if defined(__OpenBSD__)\n#  define _BSD_SOURCE  /* for pledge(2) and unveil(2) */\n#else\n#  define _XOPEN_SOURCE 600\n#endif\n\n#include <time.h>\n#include <errno.h>\n#include <stdio.h>\n#include <limits.h>\n#include <signal.h>\n#include <stdarg.h>\n#include <stdlib.h>\n#include <string.h>\n\n#include <poll.h>\n#include <fcntl.h>\n#include <unistd.h>\n#include <sys/types.h>\n#include <sys/socket.h>\n#include <arpa/inet.h>\n#include <netinet/in.h>\n#include <syslog.h>\n\n#define ENDLESSH_VERSION           1.1\n\n#define DEFAULT_PORT              2222\n#define DEFAULT_DELAY            10000  /* milliseconds */\n#define DEFAULT_MAX_LINE_LENGTH     32\n#define DEFAULT_MAX_CLIENTS       4096\n\n#if defined(__FreeBSD__)\n#  define DEFAULT_CONFIG_FILE \"/usr/local/etc/endlessh.config\"\n#else\n#  define DEFAULT_CONFIG_FILE \"/etc/endlessh/config\"\n#endif\n\n#define DEFAULT_BIND_FAMILY  AF_UNSPEC\n\n#define XSTR(s) STR(s)\n#define STR(s) #s\n\nstatic long long\nepochms(void)\n{\n    struct timespec tv;\n    clock_gettime(CLOCK_REALTIME, &tv);\n    return tv.tv_sec * 1000ULL + tv.tv_nsec / 1000000ULL;\n}\n\nstatic enum loglevel {\n    log_none,\n    log_info,\n    log_debug\n} loglevel = log_none;\n\nstatic void (*logmsg)(enum loglevel level, const char *, ...);\n\nstatic void\nlogstdio(enum loglevel level, const char *format, ...)\n{\n    if (loglevel >= level) {\n        int save = errno;\n\n        /* Print a timestamp */\n        long long now = epochms();\n        time_t t = now / 1000;\n        char date[64];\n        struct tm tm[1];\n        strftime(date, sizeof(date), \"%Y-%m-%dT%H:%M:%S\", gmtime_r(&t, tm));\n        printf(\"%s.%03lldZ \", date, now % 1000);\n\n        /* Print the rest of the log message */\n        va_list ap;\n        va_start(ap, format);\n        vprintf(format, ap);\n        va_end(ap);\n        fputc('\\n', stdout);\n\n        errno = save;\n    }\n}\n\nstatic void\nlogsyslog(enum loglevel level, const char *format, ...)\n{\n    static const int prio_map[] = { LOG_NOTICE, LOG_INFO, LOG_DEBUG };\n\n    if (loglevel >= level) {\n        int save = errno;\n\n        /* Output the log message */\n        va_list ap;\n        va_start(ap, format);\n        char buf[256];\n        vsnprintf(buf, sizeof buf, format, ap);\n        va_end(ap);\n        syslog(prio_map[level], \"%s\", buf);\n\n        errno = save;\n    }\n}\n\nstatic struct {\n    long long connects;\n    long long milliseconds;\n    long long bytes_sent;\n} statistics;\n\nstruct client {\n    char ipaddr[INET6_ADDRSTRLEN];\n    long long connect_time;\n    long long send_next;\n    long long bytes_sent;\n    struct client *next;\n    int port;\n    int fd;\n};\n\nstatic struct client *\nclient_new(int fd, long long send_next)\n{\n    struct client *c = malloc(sizeof(*c));\n    if (c) {\n        c->ipaddr[0] = 0;\n        c->connect_time = epochms();\n        c->send_next = send_next;\n        c->bytes_sent = 0;\n        c->next = 0;\n        c->fd = fd;\n        c->port = 0;\n\n        /* Set the smallest possible recieve buffer. This reduces local\n         * resource usage and slows down the remote end.\n         */\n        int value = 1;\n        int r = setsockopt(fd, SOL_SOCKET, SO_RCVBUF, &value, sizeof(value));\n        logmsg(log_debug, \"setsockopt(%d, SO_RCVBUF, %d) = %d\", fd, value, r);\n        if (r == -1)\n            logmsg(log_debug, \"errno = %d, %s\", errno, strerror(errno));\n\n        /* Get IP address */\n        struct sockaddr_storage addr;\n        socklen_t len = sizeof(addr);\n        if (getpeername(fd, (struct sockaddr *)&addr, &len) != -1) {\n            if (addr.ss_family == AF_INET) {\n                struct sockaddr_in *s = (struct sockaddr_in *)&addr;\n                c->port = ntohs(s->sin_port);\n                inet_ntop(AF_INET, &s->sin_addr,\n                          c->ipaddr, sizeof(c->ipaddr));\n            } else {\n                struct sockaddr_in6 *s = (struct sockaddr_in6 *)&addr;\n                c->port = ntohs(s->sin6_port);\n                inet_ntop(AF_INET6, &s->sin6_addr,\n                          c->ipaddr, sizeof(c->ipaddr));\n            }\n        }\n    }\n    return c;\n}\n\nstatic void\nclient_destroy(struct client *client)\n{\n    logmsg(log_debug, \"close(%d)\", client->fd);\n    long long dt = epochms() - client->connect_time;\n    logmsg(log_info,\n            \"CLOSE host=%s port=%d fd=%d \"\n            \"time=%lld.%03lld bytes=%lld\",\n            client->ipaddr, client->port, client->fd,\n            dt / 1000, dt % 1000,\n            client->bytes_sent);\n    statistics.milliseconds += dt;\n    close(client->fd);\n    free(client);\n}\n\nstatic void\nstatistics_log_totals(struct client *clients)\n{\n    long long milliseconds = statistics.milliseconds;\n    for (long long now = epochms(); clients; clients = clients->next)\n        milliseconds += now - clients->connect_time;\n    logmsg(log_info, \"TOTALS connects=%lld seconds=%lld.%03lld bytes=%lld\",\n           statistics.connects,\n           milliseconds / 1000,\n           milliseconds % 1000,\n           statistics.bytes_sent);\n}\n\nstruct fifo {\n    struct client *head;\n    struct client *tail;\n    int length;\n};\n\nstatic void\nfifo_init(struct fifo *q)\n{\n    q->head = q->tail = 0;\n    q->length = 0;\n}\n\nstatic struct client *\nfifo_pop(struct fifo *q)\n{\n    struct client *removed = q->head;\n    q->head = q->head->next;\n    removed->next = 0;\n    if (!--q->length)\n        q->tail = 0;\n    return removed;\n}\n\nstatic void\nfifo_append(struct fifo *q, struct client *c)\n{\n    if (!q->tail) {\n        q->head = q->tail = c;\n    } else {\n        q->tail->next = c;\n        q->tail = c;\n    }\n    q->length++;\n}\n\nstatic void\nfifo_destroy(struct fifo *q)\n{\n    struct client *c = q->head;\n    while (c) {\n        struct client *dead = c;\n        c = c->next;\n        client_destroy(dead);\n    }\n    q->head = q->tail = 0;\n    q->length = 0;\n}\n\nstatic void\ndie(void)\n{\n    fprintf(stderr, \"endlessh: fatal: %s\\n\", strerror(errno));\n    exit(EXIT_FAILURE);\n}\n\nstatic unsigned\nrand16(unsigned long s[1])\n{\n    s[0] = s[0] * 1103515245UL + 12345UL;\n    return (s[0] >> 16) & 0xffff;\n}\n\nstatic int\nrandline(char *line, int maxlen, unsigned long s[1])\n{\n    int len = 3 + rand16(s) % (maxlen - 2);\n    for (int i = 0; i < len - 2; i++)\n        line[i] = 32 + rand16(s) % 95;\n    line[len - 2] = 13;\n    line[len - 1] = 10;\n    if (memcmp(line, \"SSH-\", 4) == 0)\n        line[0] = 'X';\n    return len;\n}\n\nstatic volatile sig_atomic_t running = 1;\n\nstatic void\nsigterm_handler(int signal)\n{\n    (void)signal;\n    running = 0;\n}\n\nstatic volatile sig_atomic_t reload = 0;\n\nstatic void\nsighup_handler(int signal)\n{\n    (void)signal;\n    reload = 1;\n}\n\nstatic volatile sig_atomic_t dumpstats = 0;\n\nstatic void\nsigusr1_handler(int signal)\n{\n    (void)signal;\n    dumpstats = 1;\n}\n\nstruct config {\n    int port;\n    int delay;\n    int max_line_length;\n    int max_clients;\n    int bind_family;\n};\n\n#define CONFIG_DEFAULT { \\\n    .port            = DEFAULT_PORT, \\\n    .delay           = DEFAULT_DELAY, \\\n    .max_line_length = DEFAULT_MAX_LINE_LENGTH, \\\n    .max_clients     = DEFAULT_MAX_CLIENTS, \\\n    .bind_family     = DEFAULT_BIND_FAMILY, \\\n}\n\nstatic void\nconfig_set_port(struct config *c, const char *s, int hardfail)\n{\n    errno = 0;\n    char *end;\n    long tmp = strtol(s, &end, 10);\n    if (errno || *end || tmp < 1 || tmp > 65535) {\n        fprintf(stderr, \"endlessh: Invalid port: %s\\n\", s);\n        if (hardfail)\n            exit(EXIT_FAILURE);\n    } else {\n        c->port = tmp;\n    }\n}\n\nstatic void\nconfig_set_delay(struct config *c, const char *s, int hardfail)\n{\n    errno = 0;\n    char *end;\n    long tmp = strtol(s, &end, 10);\n    if (errno || *end || tmp < 1 || tmp > INT_MAX) {\n        fprintf(stderr, \"endlessh: Invalid delay: %s\\n\", s);\n        if (hardfail)\n            exit(EXIT_FAILURE);\n    } else {\n        c->delay = tmp;\n    }\n}\n\nstatic void\nconfig_set_max_clients(struct config *c, const char *s, int hardfail)\n{\n    errno = 0;\n    char *end;\n    long tmp = strtol(s, &end, 10);\n    if (errno || *end || tmp < 1 || tmp > INT_MAX) {\n        fprintf(stderr, \"endlessh: Invalid max clients: %s\\n\", s);\n        if (hardfail)\n            exit(EXIT_FAILURE);\n    } else {\n        c->max_clients = tmp;\n    }\n}\n\nstatic void\nconfig_set_max_line_length(struct config *c, const char *s, int hardfail)\n{\n    errno = 0;\n    char *end;\n    long tmp = strtol(s, &end, 10);\n    if (errno || *end || tmp < 3 || tmp > 255) {\n        fprintf(stderr, \"endlessh: Invalid line length: %s\\n\", s);\n        if (hardfail)\n            exit(EXIT_FAILURE);\n    } else {\n        c->max_line_length = tmp;\n    }\n}\n\nstatic void\nconfig_set_bind_family(struct config *c, const char *s, int hardfail)\n{\n  switch (*s) {\n      case '4':\n          c->bind_family = AF_INET;\n          break;\n      case '6':\n          c->bind_family = AF_INET6;\n          break;\n      case '0':\n          c->bind_family = AF_UNSPEC;\n          break;\n      default:\n          fprintf(stderr, \"endlessh: Invalid address family: %s\\n\", s);\n          if (hardfail)\n              exit(EXIT_FAILURE);\n          break;\n  }\n}\n\nenum config_key {\n    KEY_INVALID,\n    KEY_PORT,\n    KEY_DELAY,\n    KEY_MAX_LINE_LENGTH,\n    KEY_MAX_CLIENTS,\n    KEY_LOG_LEVEL,\n    KEY_BIND_FAMILY,\n};\n\nstatic enum config_key\nconfig_key_parse(const char *tok)\n{\n    static const char *const table[] = {\n        [KEY_PORT]            = \"Port\",\n        [KEY_DELAY]           = \"Delay\",\n        [KEY_MAX_LINE_LENGTH] = \"MaxLineLength\",\n        [KEY_MAX_CLIENTS]     = \"MaxClients\",\n        [KEY_LOG_LEVEL]       = \"LogLevel\",\n        [KEY_BIND_FAMILY]     = \"BindFamily\"\n    };\n    for (size_t i = 1; i < sizeof(table) / sizeof(*table); i++)\n        if (!strcmp(tok, table[i]))\n            return i;\n    return KEY_INVALID;\n}\n\nstatic void\nconfig_load(struct config *c, const char *file, int hardfail)\n{\n    long lineno = 0;\n    FILE *f = fopen(file, \"r\");\n    if (f) {\n        char line[256];\n        while (fgets(line, sizeof(line), f)) {\n            lineno++;\n\n            /* Remove comments */\n            char *comment = strchr(line, '#');\n            if (comment)\n                *comment = 0;\n\n            /* Parse tokes on line */\n            char *save = 0;\n            char *tokens[3];\n            int ntokens = 0;\n            for (; ntokens < 3; ntokens++) {\n                char *tok = strtok_r(ntokens ? 0 : line, \" \\r\\n\", &save);\n                if (!tok)\n                    break;\n                tokens[ntokens] = tok;\n            }\n\n            switch (ntokens) {\n                case 0: /* Empty line */\n                    continue;\n                case 1:\n                    fprintf(stderr, \"%s:%ld: Missing value\\n\", file, lineno);\n                    if (hardfail) exit(EXIT_FAILURE);\n                    continue;\n                case 2: /* Expected */\n                    break;\n                case 3:\n                    fprintf(stderr, \"%s:%ld: Too many values\\n\", file, lineno);\n                    if (hardfail) exit(EXIT_FAILURE);\n                    continue;\n            }\n\n            enum config_key key = config_key_parse(tokens[0]);\n            switch (key) {\n                case KEY_INVALID:\n                    fprintf(stderr, \"%s:%ld: Unknown option '%s'\\n\",\n                            file, lineno, tokens[0]);\n                    break;\n                case KEY_PORT:\n                    config_set_port(c, tokens[1], hardfail);\n                    break;\n                case KEY_DELAY:\n                    config_set_delay(c, tokens[1], hardfail);\n                    break;\n                case KEY_MAX_LINE_LENGTH:\n                    config_set_max_line_length(c, tokens[1], hardfail);\n                    break;\n                case KEY_MAX_CLIENTS:\n                    config_set_max_clients(c, tokens[1], hardfail);\n                    break;\n                case KEY_BIND_FAMILY:\n                    config_set_bind_family(c, tokens[1], hardfail);\n                    break;\n                case KEY_LOG_LEVEL: {\n                    errno = 0;\n                    char *end;\n                    long v = strtol(tokens[1], &end, 10);\n                    if (errno || *end || v < log_none || v > log_debug) {\n                        fprintf(stderr, \"%s:%ld: Invalid log level '%s'\\n\",\n                                file, lineno, tokens[1]);\n                        if (hardfail) exit(EXIT_FAILURE);\n                    } else {\n                        loglevel = v;\n                    }\n                } break;\n            }\n        }\n\n        fclose(f);\n    }\n}\n\nstatic void\nconfig_log(const struct config *c)\n{\n    logmsg(log_info, \"Port %d\", c->port);\n    logmsg(log_info, \"Delay %d\", c->delay);\n    logmsg(log_info, \"MaxLineLength %d\", c->max_line_length);\n    logmsg(log_info, \"MaxClients %d\", c->max_clients);\n    logmsg(log_info, \"BindFamily %s\",\n        c->bind_family == AF_INET6 ? \"IPv6 Only\" :\n        c->bind_family == AF_INET  ? \"IPv4 Only\" :\n                                \"IPv4 Mapped IPv6\");\n}\n\nstatic void\nusage(FILE *f)\n{\n    fprintf(f, \"Usage: endlessh [-vh] [-46] [-d MS] [-f CONFIG] [-l LEN] \"\n                               \"[-m LIMIT] [-p PORT]\\n\");\n    fprintf(f, \"  -4        Bind to IPv4 only\\n\");\n    fprintf(f, \"  -6        Bind to IPv6 only\\n\");\n    fprintf(f, \"  -d INT    Message millisecond delay [\"\n            XSTR(DEFAULT_DELAY) \"]\\n\");\n    fprintf(f, \"  -f        Set and load config file [\"\n            DEFAULT_CONFIG_FILE \"]\\n\");\n    fprintf(f, \"  -h        Print this help message and exit\\n\");\n    fprintf(f, \"  -l INT    Maximum banner line length (3-255) [\"\n            XSTR(DEFAULT_MAX_LINE_LENGTH) \"]\\n\");\n    fprintf(f, \"  -m INT    Maximum number of clients [\"\n            XSTR(DEFAULT_MAX_CLIENTS) \"]\\n\");\n    fprintf(f, \"  -p INT    Listening port [\" XSTR(DEFAULT_PORT) \"]\\n\");\n    fprintf(f, \"  -v        Print diagnostics to standard output \"\n            \"(repeatable)\\n\");\n    fprintf(f, \"  -V        Print version information and exit\\n\");\n}\n\nstatic void\nprint_version(void)\n{\n    puts(\"Endlessh \" XSTR(ENDLESSH_VERSION));\n}\n\nstatic int\nserver_create(int port, int family)\n{\n    int r, s, value;\n\n    s = socket(family == AF_UNSPEC ? AF_INET6 : family, SOCK_STREAM, 0);\n    logmsg(log_debug, \"socket() = %d\", s);\n    if (s == -1) die();\n\n    /* Socket options are best effort, allowed to fail */\n    value = 1;\n    r = setsockopt(s, SOL_SOCKET, SO_REUSEADDR, &value, sizeof(value));\n    logmsg(log_debug, \"setsockopt(%d, SO_REUSEADDR, true) = %d\", s, r);\n    if (r == -1)\n        logmsg(log_debug, \"errno = %d, %s\", errno, strerror(errno));\n\n    /*\n     * With OpenBSD IPv6 sockets are always IPv6-only, so the socket option\n     * is read-only (not modifiable).\n     * http://man.openbsd.org/ip6#IPV6_V6ONLY\n     */\n#ifndef __OpenBSD__\n    if (family == AF_INET6 || family == AF_UNSPEC) {\n        errno = 0;\n        value = (family == AF_INET6);\n        r = setsockopt(s, IPPROTO_IPV6, IPV6_V6ONLY, &value, sizeof(value));\n        logmsg(log_debug, \"setsockopt(%d, IPV6_V6ONLY, true) = %d\", s, r);\n        if (r == -1)\n            logmsg(log_debug, \"errno = %d, %s\", errno, strerror(errno));\n    }\n#endif\n\n    if (family == AF_INET) {\n        struct sockaddr_in addr4 = {\n            .sin_family = AF_INET,\n            .sin_port = htons(port),\n            .sin_addr = {INADDR_ANY}\n        };\n        r = bind(s, (void *)&addr4, sizeof(addr4));\n    } else {\n        struct sockaddr_in6 addr6 = {\n            .sin6_family = AF_INET6,\n            .sin6_port = htons(port),\n            .sin6_addr = in6addr_any\n        };\n        r = bind(s, (void *)&addr6, sizeof(addr6));\n    }\n    logmsg(log_debug, \"bind(%d, port=%d) = %d\", s, port, r);\n    if (r == -1) die();\n\n    r = listen(s, INT_MAX);\n    logmsg(log_debug, \"listen(%d) = %d\", s, r);\n    if (r == -1) die();\n\n    return s;\n}\n\n/* Write a line to a client, returning client if it's still up. */\nstatic struct client *\nsendline(struct client *client, int max_line_length, unsigned long *rng)\n{\n    char line[256];\n    int len = randline(line, max_line_length, rng);\n    for (;;) {\n        ssize_t out = write(client->fd, line, len);\n        logmsg(log_debug, \"write(%d) = %d\", client->fd, (int)out);\n        if (out == -1) {\n            if (errno == EINTR) {\n                continue;      /* try again */\n            } else if (errno == EAGAIN || errno == EWOULDBLOCK) {\n                return client; /* don't care */\n            } else {\n                client_destroy(client);\n                return 0;\n            }\n        } else {\n            client->bytes_sent += out;\n            statistics.bytes_sent += out;\n            return client;\n        }\n    }\n}\n\n\nint\nmain(int argc, char **argv)\n{\n    logmsg = logstdio;\n    struct config config = CONFIG_DEFAULT;\n    const char *config_file = DEFAULT_CONFIG_FILE;\n\n#if defined(__OpenBSD__)\n    unveil(config_file, \"r\"); /* return ignored as the file may not exist */\n    if (pledge(\"inet stdio rpath unveil\", 0) == -1)\n        die();\n#endif\n\n    config_load(&config, config_file, 1);\n\n    int option;\n    while ((option = getopt(argc, argv, \"46d:f:hl:m:p:svV\")) != -1) {\n        switch (option) {\n            case '4':\n                config_set_bind_family(&config, \"4\", 1);\n                break;\n            case '6':\n                config_set_bind_family(&config, \"6\", 1);\n                break;\n            case 'd':\n                config_set_delay(&config, optarg, 1);\n                break;\n            case 'f':\n                config_file = optarg;\n\n#if defined(__OpenBSD__)\n                unveil(config_file, \"r\");\n                if (unveil(0, 0) == -1)\n                    die();\n#endif\n\n                config_load(&config, optarg, 1);\n                break;\n            case 'h':\n                usage(stdout);\n                exit(EXIT_SUCCESS);\n                break;\n            case 'l':\n                config_set_max_line_length(&config, optarg, 1);\n                break;\n            case 'm':\n                config_set_max_clients(&config, optarg, 1);\n                break;\n            case 'p':\n                config_set_port(&config, optarg, 1);\n                break;\n            case 's':\n                logmsg = logsyslog;\n                break;\n            case 'v':\n                if (loglevel < log_debug)\n                    loglevel++;\n                break;\n            case 'V':\n                print_version();\n                exit(EXIT_SUCCESS);\n                break;\n            default:\n                usage(stderr);\n                exit(EXIT_FAILURE);\n        }\n    }\n\n    if (argv[optind]) {\n        fprintf(stderr, \"endlessh: too many arguments\\n\");\n        exit(EXIT_FAILURE);\n    }\n\n    if (logmsg == logsyslog) {\n        /* Prepare the syslog */\n        const char *prog = strrchr(argv[0], '/');\n        prog = prog ? prog + 1 : argv[0];\n        openlog(prog, LOG_PID, LOG_DAEMON);\n    } else {\n        /* Set output (log) to line buffered */\n        setvbuf(stdout, 0, _IOLBF, 0);\n    }\n\n    /* Log configuration */\n    config_log(&config);\n\n    /* Install the signal handlers */\n    signal(SIGPIPE, SIG_IGN);\n    {\n        struct sigaction sa = {.sa_handler = sigterm_handler};\n        int r = sigaction(SIGTERM, &sa, 0);\n        if (r == -1)\n            die();\n    }\n    {\n        struct sigaction sa = {.sa_handler = sighup_handler};\n        int r = sigaction(SIGHUP, &sa, 0);\n        if (r == -1)\n            die();\n    }\n    {\n        struct sigaction sa = {.sa_handler = sigusr1_handler};\n        int r = sigaction(SIGUSR1, &sa, 0);\n        if (r == -1)\n            die();\n    }\n\n    struct fifo fifo[1];\n    fifo_init(fifo);\n\n    unsigned long rng = epochms();\n\n    int server = server_create(config.port, config.bind_family);\n\n    while (running) {\n        if (reload) {\n            /* Configuration reload requested (SIGHUP) */\n            int oldport = config.port;\n            int oldfamily = config.bind_family;\n            config_load(&config, config_file, 0);\n            config_log(&config);\n            if (oldport != config.port || oldfamily != config.bind_family) {\n                close(server);\n                server = server_create(config.port, config.bind_family);\n            }\n            reload = 0;\n        }\n        if (dumpstats) {\n            /* print stats requested (SIGUSR1) */\n            statistics_log_totals(fifo->head);\n            dumpstats = 0;\n        }\n\n        /* Enqueue clients that are due for another message */\n        int timeout = -1;\n        long long now = epochms();\n        while (fifo->head) {\n            if (fifo->head->send_next <= now) {\n                struct client *c = fifo_pop(fifo);\n                if (sendline(c, config.max_line_length, &rng)) {\n                    c->send_next = now + config.delay;\n                    fifo_append(fifo, c);\n                }\n            } else {\n                timeout = fifo->head->send_next - now;\n                break;\n            }\n        }\n\n        /* Wait for next event */\n        struct pollfd fds = {server, POLLIN, 0};\n        int nfds = fifo->length < config.max_clients;\n        logmsg(log_debug, \"poll(%d, %d)\", nfds, timeout);\n        int r = poll(&fds, nfds, timeout);\n        logmsg(log_debug, \"= %d\", r);\n        if (r == -1) {\n            switch (errno) {\n                case EINTR:\n                    logmsg(log_debug, \"EINTR\");\n                    continue;\n                default:\n                    fprintf(stderr, \"endlessh: fatal: %s\\n\", strerror(errno));\n                    exit(EXIT_FAILURE);\n            }\n        }\n\n        /* Check for new incoming connections */\n        if (fds.revents & POLLIN) {\n            int fd = accept(server, 0, 0);\n            logmsg(log_debug, \"accept() = %d\", fd);\n            statistics.connects++;\n            if (fd == -1) {\n                const char *msg = strerror(errno);\n                switch (errno) {\n                    case EMFILE:\n                    case ENFILE:\n                        config.max_clients = fifo->length;\n                        logmsg(log_info,\n                                \"MaxClients %d\",\n                                fifo->length);\n                        break;\n                    case ECONNABORTED:\n                    case EINTR:\n                    case ENOBUFS:\n                    case ENOMEM:\n                    case EPROTO:\n                        fprintf(stderr, \"endlessh: warning: %s\\n\", msg);\n                        break;\n                    default:\n                        fprintf(stderr, \"endlessh: fatal: %s\\n\", msg);\n                        exit(EXIT_FAILURE);\n                }\n            } else {\n                long long send_next = epochms() + config.delay;\n                struct client *client = client_new(fd, send_next);\n                int flags = fcntl(fd, F_GETFL, 0);      /* cannot fail */\n                fcntl(fd, F_SETFL, flags | O_NONBLOCK); /* cannot fail */\n                if (!client) {\n                    fprintf(stderr, \"endlessh: warning: out of memory\\n\");\n                    close(fd);\n                } else {\n                    fifo_append(fifo, client);\n                    logmsg(log_info, \"ACCEPT host=%s port=%d fd=%d n=%d/%d\",\n                            client->ipaddr, client->port, client->fd,\n                            fifo->length, config.max_clients);\n                }\n            }\n        }\n    }\n\n    fifo_destroy(fifo);\n    statistics_log_totals(0);\n\n    if (logmsg == logsyslog)\n        closelog();\n}\n"
  },
  {
    "path": "util/endlessh.service",
    "content": "[Unit]\nDescription=Endlessh SSH Tarpit\nDocumentation=man:endlessh(1)\nRequires=network-online.target\n\n[Service]\nType=simple\nRestart=always\nRestartSec=30sec\nExecStart=/usr/local/bin/endlessh\nKillSignal=SIGTERM\n\n# Stop trying to restart the service if it restarts too many times in a row\nStartLimitInterval=5min\nStartLimitBurst=4\n\nStandardOutput=journal\nStandardError=journal\nStandardInput=null\n\nPrivateTmp=true\nPrivateDevices=true\nProtectSystem=full\nProtectHome=true\nInaccessiblePaths=/run /var\n\n## If you want Endlessh to bind on ports < 1024\n## 1) run: \n##     setcap 'cap_net_bind_service=+ep' /usr/local/bin/endlessh\n## 2) uncomment following line\n#AmbientCapabilities=CAP_NET_BIND_SERVICE\n## 3) comment following line\nPrivateUsers=true\n\nNoNewPrivileges=true\nConfigurationDirectory=endlessh\nProtectKernelTunables=true\nProtectKernelModules=true\nProtectControlGroups=true\nMemoryDenyWriteExecute=true\n\n[Install]\nWantedBy=multi-user.target\n\n"
  },
  {
    "path": "util/openbsd/README.md",
    "content": "# Running `endlessh` on OpenBSD\n\n## Covering IPv4 and IPv6\n\nIf you want to cover both IPv4 and IPv6 you'll need to run *two* instances of\n`endlessh` due to OpenBSD limitations. Here's how I did it:\n\n- copy the `endlessh` script to `rc.d` twice, as `endlessh` and `endlessh6`\n- copy the `config` file to `/etc/endlessh` twice, as `config` and `config6`\n  - use `BindFamily 4` in `config`\n  - use `BindFamily 6` in `config6`\n- in `rc.conf.local` force `endlessh6` to load `config6` like so:\n\n```\nendlessh6_flags=-s -f /etc/endlessh/config6\nendlessh_flags=-s\n```\n\n## Covering more than 128 connections\n\nThe defaults in OpenBSD only allow for 128 open file descriptors per process,\nso regardless of the `MaxClients` setting in `/etc/config` you'll end up with\nsomething like 124 clients at the most.\nYou can increase these limits in `/etc/login.conf` for `endlessh` (and\n`endlessh6`) like so:\n\n```\nendlessh:\\\n\t:openfiles=1024:\\\n\t:tc=daemon:\nendlessh6:\\\n\t:openfiles=1024:\\\n\t:tc=daemon:\n```\n"
  },
  {
    "path": "util/pivot.py",
    "content": "#!/usr/bin/env python3\n\n# This script accepts a log on standard input and produces a CSV table\n# with one connection per row.\n#\n#   $ util/pivot.py <log | sqlite3 -init util/schema.sql log.db\n\nimport sys\nimport pyrfc3339\n\ntable = {}\nfor line in sys.stdin:\n    parts = line.split(' ')\n    entry = {}\n    entry['logtime'] = pyrfc3339.parse(parts[0])\n    action = parts[1]\n    if action == 'ACCEPT' or action == 'CLOSE':\n        for item in parts[2:]:\n            key, value = item.split('=')\n            entry[key] = value\n        if action == 'ACCEPT':\n            table[entry['fd']] = entry\n        else:\n            accept = table[entry['fd']]\n            del table[entry['fd']]\n            delta = (entry['logtime'] - accept['logtime']).total_seconds()\n            host = entry['host']\n            port = entry['port']\n            if host.startswith('::ffff:'):\n                host = host[7:]\n            nbytes = int(entry['bytes'])\n            print('%s,%s,%.3f,%d' % (host, port, delta, nbytes))\n\nif len(table) > 0:\n    print('warning: %d hanging entries' % len(table), file=sys.stderr)\n"
  },
  {
    "path": "util/schema.sql",
    "content": "CREATE TABLE IF NOT EXISTS log (\n    host TEXT,\n    port INTEGER,\n    time REAL,\n    bytes INTEGER\n);\n.mode csv\n.import /dev/stdin log\n"
  },
  {
    "path": "util/smf/README",
    "content": "Solaris SMF installation\n========================\n\nBefore installing SMF:\n\n1. Put endlessh binary to /usr/local/bin\n2. Edit endlessh.conf and put it to /usr/local/etc\n\nTo install SMF:\n\n1. Put endlessh.xml to /var/svc/manifest/network\n2. Run svccfg import endlessh.xml\n3. Put init.endlessh to /lib/svc/method\n4. Run svcadm enable endlessh\n\nNote: Log will write to /var/log/endlessh.log by default.\n\nTo uninstall SMF:\n\n1. Run svcadm disable endlessh\n2. rm -f /lib/svc/method/init.endlessh\n3. svccfg delete svc:/network/endlessh:default\n4. rm -f /var/svc/manifest/network/endlessh.xml\n\nEnjoy! :)"
  },
  {
    "path": "util/smf/endlessh.conf",
    "content": "# The port on which to listen for new SSH connections.\nPort 22\n\n# The endless banner is sent one line at a time. This is the delay\n# in milliseconds between individual lines.\nDelay 10000\n\n# The length of each line is randomized. This controls the maximum\n# length of each line. Shorter lines may keep clients on for longer if\n# they give up after a certain number of bytes.\nMaxLineLength 32\n\n# Maximum number of connections to accept at a time. Connections beyond\n# this are not immediately rejected, but will wait in the queue.\nMaxClients 4096\n\n# Set the detail level for the log.\n#   0 = Quiet\n#   1 = Standard, useful log messages\n#   2 = Very noisy debugging information\nLogLevel 1\n\n# Set the family of the listening socket\n#   0 = Use IPv4 Mapped IPv6 (Both v4 and v6, default)\n#   4 = Use IPv4 only\n#   6 = Use IPv6 only\nBindFamily 0\n"
  },
  {
    "path": "util/smf/endlessh.xml",
    "content": "<?xml version=\"1.0\"?>\n<!DOCTYPE service_bundle SYSTEM \"/usr/share/lib/xml/dtd/service_bundle.dtd.1\">\n<!--   Manifest-file for endlessh, put this file in\n       /var/svc/manifest/network/endlessh.xml\n       and run #svccfg import /var/svc/manifest/network/endlessh.xml\n       Fixed by Yuri Voinov (C) 2007,2019 -->\n<service_bundle type='manifest' name='endlessh'>\n\n<service\n        name='network/endlessh'\n        type='service'\n        version='1'>\n\n        <create_default_instance enabled='false' />\n\n        <single_instance />\n\n        <dependency name='fs-local'\n                grouping='require_all'\n                restart_on='none'\n                type='service'>\n                <service_fmri\n                        value='svc:/system/filesystem/local' />\n        </dependency>\n\n        <dependency name='net-loopback'\n                grouping='require_all'\n                restart_on='none'\n                type='service'>\n                <service_fmri value='svc:/network/loopback' />\n        </dependency>\n\n        <dependency name='net-physical'\n                grouping='require_all'\n                restart_on='none'\n                type='service'>\n                <service_fmri value='svc:/network/physical' />\n        </dependency>\n\n        <dependency name='utmp'\n                grouping='require_all'\n                restart_on='none'\n                type='service'>\n                <service_fmri value='svc:/system/utmp' />\n        </dependency>\n\n        <dependency name='endlessh_config_data'\n                grouping='require_all'\n                restart_on='refresh'\n                type='path'>\n                <service_fmri value='file://localhost/usr/local/etc/endlessh.conf' />\n        </dependency>\n\n        <exec_method\n                type='method'\n                name='start'\n                exec='/lib/svc/method/init.endlessh %m'\n                timeout_seconds='60'/>\n\n        <exec_method\n                type='method'\n                name='stop'\n                exec=':kill'\n                timeout_seconds='60' />\n\n        <exec_method\n                type='method'\n                name='restart'\n                exec='/lib/svc/method/init.endlessh %m'\n                timeout_seconds='60' />\n\n        <exec_method\n                type='method'\n                name='refresh'\n                exec='/lib/svc/method/init.endlessh %m'\n                timeout_seconds='60' />\n\n        <property_group name='general' type='framework'>\n                <!-- to start stop endlessh -->\n                <propval name='action_authorization' type='astring'\n                        value='solaris.smf.manage' />\n        </property_group>\n\n        <stability value='Unstable' />\n\n        <template>\n                <common_name>\n                        <loctext xml:lang='C'>\n                        endlessh service\n                        </loctext>\n                </common_name>\n        </template>\n\n</service>\n\n</service_bundle>"
  },
  {
    "path": "util/smf/init.endlessh",
    "content": "#!/sbin/sh\n\n#\n# Control Method for endlessh (/lib/svc/method/init.endlessh)\n# Written by Yuri Voinov (C) 2007,2019\n#\n# ident \"@(#)endlessh.sh    1.8    19/27/03 YV\"\n#\n\n#############\n# Variables #\n#############\n\n# Base installation directory\nBASE_DIR=\"/usr/local\"\nBASE_CONFIG_DIR=$BASE_DIR\"/etc\"\n\n# endlessh files paths  \nENDLESSH_PATH=\"$BASE_DIR\"\"/bin\"\nENDLESSH_CONF_PATH=\"$BASE_CONFIG_DIR\"\n\n# endlessh files\nENDLESSH_BIN_FILE=\"endlessh\"\nENDLESSH_CONF_FILE=$ENDLESSH_BIN_FILE\".conf\"\n\n# Daemon settings\nENDLESSH_CONF=\"$ENDLESSH_CONF_PATH/$ENDLESSH_CONF_FILE\"\n\n# Log\nLOG_DIR=\"/var/log\"\nLOGFILE=$LOG_DIR/$ENDLESSH_BIN_FILE\".log\"\n\n#   \n# OS Commands location variables\n#\nCUT=`which cut`\nECHO=`which echo`\nKILL=`which kill`\nPGREP=`which pgrep`\nUNAME=`which uname`\n\n# OS release\nOS_VER=`$UNAME -r|$CUT -f2 -d\".\"`\nOS_NAME=`$UNAME -s|$CUT -f1 -d\" \"`\n\n###############\n# Subroutines #\n###############\n\ncheck_endlessh ()\n{\n # Check endlessh installed\n program=$1\n if [ ! -f \"$ENDLESSH_PATH/$program\" -a ! -x \"$ENDLESSH_PATH/$program\" ]; then\n  $ECHO \"ERROR: endlessh not found!\"\n  $ECHO \"Exiting...\"\n  exit 1\n fi\n}\n\ncheck_os ()\n{\n # Check OS version\n if [ ! \"$OS_NAME\" = \"SunOS\" -a ! \"$OS_VER\" -lt \"10\" ]; then\n  $ECHO \"ERROR: Unsupported OS $OS_NAME $OS_VER\"\n  $ECHO \"Exiting...\"\n  exit 1\n fi\n}\n\ncheckconf ()\n{\n# Check endlessh config file\n config=$1\n if [ -f \"$ENDLESSH_CONF_PATH\"/\"$config\" ]; then\n  $ECHO \"1\"\n else\n  $ECHO \"0\"\n fi  \n}\n\nstartproc() \n{\n# Start endlessh daemon\n program=$1\n if [ \"`checkconf $ENDLESSH_CONF_FILE`\" != \"1\" ]; then\n  $ECHO \"ERROR: Config file $ENDLESSH_CONF_PATH/$ENDLESSH_CONF_FILE not found.\"  \n  $ECHO \"Exiting...\"\n  exit 2\n else\n  $ENDLESSH_PATH/$program -f $ENDLESSH_CONF_PATH/$ENDLESSH_CONF_FILE -v >$LOGFILE &\n fi\n}\n\nstopproc() \n{\n# Stop endlessh daemon\n program=$1\n if [ \"`checkconf $ENDLESSH_CONF_FILE`\" != \"1\" ]; then\n  $ECHO \"ERROR: Config file $ENDLESSH_CONF_PATH/$ENDLESSH_CONF_FILE not found.\"  \n  $ECHO \"Exiting...\"\n  exit 2\n else\n  $KILL -s TERM `$PGREP $program`>/dev/null 2>&1\n fi\n}\n\n##############\n# Main block #\n##############\n\n# Check endlessh installed\ncheck_endlessh $ENDLESSH_BIN_FILE\n\n# Check OS version\ncheck_os\n\ncase \"$1\" in\n\"start\")\n  startproc $ENDLESSH_BIN_FILE\n  ;;\n\"stop\")\n  stopproc $ENDLESSH_BIN_FILE\n  ;;\n\"refresh\")\n  $KILL -s HUP `$PGREP $ENDLESSH_BIN_FILE`>/dev/null 2>&1\n  ;;\n\"restart\")\n  stopproc $ENDLESSH_BIN_FILE\n  startproc $ENDLESSH_BIN_FILE\n  ;;\n*)\n  $ECHO \"Usage: $0 { start | stop | restart | refresh }\"\n  exit 1\nesac\n\nexit 0\n"
  }
]