├── .gitignore ├── Dockerfile ├── Makefile ├── README.md ├── UNLICENSE ├── endlessh.1 ├── endlessh.c └── util ├── endlessh.service ├── openbsd ├── README.md └── endlessh ├── pivot.py ├── schema.sql └── smf ├── README ├── endlessh.conf ├── endlessh.xml └── init.endlessh /.gitignore: -------------------------------------------------------------------------------- 1 | endlessh 2 | -------------------------------------------------------------------------------- /Dockerfile: -------------------------------------------------------------------------------- 1 | FROM alpine:3.9 as builder 2 | RUN apk add --no-cache build-base 3 | ADD endlessh.c Makefile / 4 | RUN make 5 | 6 | 7 | FROM alpine:3.9 8 | 9 | COPY --from=builder /endlessh / 10 | 11 | EXPOSE 2222/tcp 12 | 13 | ENTRYPOINT ["/endlessh"] 14 | 15 | CMD ["-v"] 16 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | .POSIX: 2 | CC = cc 3 | CFLAGS = -std=c99 -Wall -Wextra -Wno-missing-field-initializers -Os 4 | CPPFLAGS = 5 | LDFLAGS = -ggdb3 6 | LDLIBS = 7 | PREFIX = /usr/local 8 | 9 | all: endlessh 10 | 11 | endlessh: endlessh.c 12 | $(CC) $(CFLAGS) $(CPPFLAGS) $(LDFLAGS) -o $@ endlessh.c $(LDLIBS) 13 | 14 | install: endlessh 15 | install -d $(DESTDIR)$(PREFIX)/bin 16 | install -m 755 endlessh $(DESTDIR)$(PREFIX)/bin/ 17 | install -d $(DESTDIR)$(PREFIX)/share/man/man1 18 | install -m 644 endlessh.1 $(DESTDIR)$(PREFIX)/share/man/man1/ 19 | 20 | clean: 21 | rm -rf endlessh 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Endlessh: an SSH tarpit 2 | 3 | Endlessh is an SSH tarpit [that *very* slowly sends an endless, random 4 | SSH banner][np]. It keeps SSH clients locked up for hours or even days 5 | at a time. The purpose is to put your real SSH server on another port 6 | and then let the script kiddies get stuck in this tarpit instead of 7 | bothering a real server. 8 | 9 | Since the tarpit is in the banner before any cryptographic exchange 10 | occurs, this program doesn't depend on any cryptographic libraries. It's 11 | a simple, single-threaded, standalone C program. It uses `poll()` to 12 | trap multiple clients at a time. 13 | 14 | ## Usage 15 | 16 | Usage information is printed with `-h`. 17 | 18 | ``` 19 | Usage: endlessh [-vhs] [-d MS] [-f CONFIG] [-l LEN] [-m LIMIT] [-p PORT] 20 | -4 Bind to IPv4 only 21 | -6 Bind to IPv6 only 22 | -d INT Message millisecond delay [10000] 23 | -f Set and load config file [/etc/endlessh/config] 24 | -h Print this help message and exit 25 | -l INT Maximum banner line length (3-255) [32] 26 | -m INT Maximum number of clients [4096] 27 | -p INT Listening port [2222] 28 | -s Print diagnostics to syslog instead of standard output 29 | -v Print diagnostics (repeatable) 30 | ``` 31 | 32 | Argument order matters. The configuration file is loaded when the `-f` 33 | argument is processed, so only the options that follow will override the 34 | configuration file. 35 | 36 | By default no log messages are produced. The first `-v` enables basic 37 | logging and a second `-v` enables debugging logging (noisy). All log 38 | messages are sent to standard output by default. `-s` causes them to be 39 | sent to syslog. 40 | 41 | endlessh -v >endlessh.log 2>endlessh.err 42 | 43 | A SIGTERM signal will gracefully shut down the daemon, allowing it to 44 | write a complete, consistent log. 45 | 46 | A SIGHUP signal requests a reload of the configuration file (`-f`). 47 | 48 | A SIGUSR1 signal will print connections stats to the log. 49 | 50 | ## Sample Configuration File 51 | 52 | The configuration file has similar syntax to OpenSSH. 53 | 54 | ``` 55 | # The port on which to listen for new SSH connections. 56 | Port 2222 57 | 58 | # The endless banner is sent one line at a time. This is the delay 59 | # in milliseconds between individual lines. 60 | Delay 10000 61 | 62 | # The length of each line is randomized. This controls the maximum 63 | # length of each line. Shorter lines may keep clients on for longer if 64 | # they give up after a certain number of bytes. 65 | MaxLineLength 32 66 | 67 | # Maximum number of connections to accept at a time. Connections beyond 68 | # this are not immediately rejected, but will wait in the queue. 69 | MaxClients 4096 70 | 71 | # Set the detail level for the log. 72 | # 0 = Quiet 73 | # 1 = Standard, useful log messages 74 | # 2 = Very noisy debugging information 75 | LogLevel 0 76 | 77 | # Set the family of the listening socket 78 | # 0 = Use IPv4 Mapped IPv6 (Both v4 and v6, default) 79 | # 4 = Use IPv4 only 80 | # 6 = Use IPv6 only 81 | BindFamily 0 82 | ``` 83 | 84 | ## Build issues 85 | 86 | Some more esoteric systems require extra configuration when building. 87 | 88 | ### RHEL 6 / CentOS 6 89 | 90 | This system uses a version of glibc older than 2.17 (December 2012), and 91 | `clock_gettime(2)` is still in librt. For these systems you will need to 92 | link against librt: 93 | 94 | make LDLIBS=-lrt 95 | 96 | ### Solaris / illumos 97 | 98 | These systems don't include all the necessary functionality in libc and 99 | the linker requires some extra libraries: 100 | 101 | make CC=gcc LDLIBS='-lnsl -lrt -lsocket' 102 | 103 | If you're not using GCC or Clang, also override `CFLAGS` and `LDFLAGS` 104 | to remove GCC-specific options. For example, on Solaris: 105 | 106 | make CFLAGS=-fast LDFLAGS= LDLIBS='-lnsl -lrt -lsocket' 107 | 108 | The feature test macros on these systems isn't reliable, so you may also 109 | need to use `-D__EXTENSIONS__` in `CFLAGS`. 110 | 111 | ### OpenBSD 112 | 113 | The man page needs to go into a different path for OpenBSD's `man` command: 114 | 115 | ``` 116 | diff --git a/Makefile b/Makefile 117 | index 119347a..dedf69d 100644 118 | --- a/Makefile 119 | +++ b/Makefile 120 | @@ -14,8 +14,8 @@ endlessh: endlessh.c 121 | install: endlessh 122 | install -d $(DESTDIR)$(PREFIX)/bin 123 | install -m 755 endlessh $(DESTDIR)$(PREFIX)/bin/ 124 | - install -d $(DESTDIR)$(PREFIX)/share/man/man1 125 | - install -m 644 endlessh.1 $(DESTDIR)$(PREFIX)/share/man/man1/ 126 | + install -d $(DESTDIR)$(PREFIX)/man/man1 127 | + install -m 644 endlessh.1 $(DESTDIR)$(PREFIX)/man/man1/ 128 | 129 | clean: 130 | rm -rf endlessh 131 | ``` 132 | 133 | [np]: https://nullprogram.com/blog/2019/03/22/ 134 | -------------------------------------------------------------------------------- /UNLICENSE: -------------------------------------------------------------------------------- 1 | This is free and unencumbered software released into the public domain. 2 | 3 | Anyone is free to copy, modify, publish, use, compile, sell, or 4 | distribute this software, either in source code form or as a compiled 5 | binary, for any purpose, commercial or non-commercial, and by any 6 | means. 7 | 8 | In jurisdictions that recognize copyright laws, the author or authors 9 | of this software dedicate any and all copyright interest in the 10 | software to the public domain. We make this dedication for the benefit 11 | of the public at large and to the detriment of our heirs and 12 | successors. We intend this dedication to be an overt act of 13 | relinquishment in perpetuity of all present and future rights to this 14 | software under copyright law. 15 | 16 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 17 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 18 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. 19 | IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR 20 | OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, 21 | ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR 22 | OTHER DEALINGS IN THE SOFTWARE. 23 | 24 | For more information, please refer to 25 | -------------------------------------------------------------------------------- /endlessh.1: -------------------------------------------------------------------------------- 1 | .Dd $Mdocdate: January 29 2020 $ 2 | .Dt ENDLESSH 1 3 | .Os 4 | .Sh NAME 5 | .Nm endless 6 | .Nd An SSH tarpit 7 | .Sh SYNOPSIS 8 | .Nm endless 9 | .Op Fl 46chsvV 10 | .Op Fl d Ar delay 11 | .Op Fl f Ar config 12 | .Op Fl l Ar max banner length 13 | .Op Fl m Ar max clients 14 | .Op Fl p Ar port 15 | .Sh DESCRIPTION 16 | .Nm 17 | is an SSH tarpit that very slowly 18 | sends an endless, random SSH banner. 19 | .Pp 20 | .Nm 21 | keeps SSH clients locked up for hours or even days at a time. 22 | The purpose is to put your real SSH server on another port 23 | and then let the script kiddies get stuck in this tarpit 24 | instead of bothering a real server. 25 | .Pp 26 | Since the tarpit is in the banner before any cryptographic 27 | exchange occurs, this program doesn't depend on any cryptographic 28 | libraries. It's a simple, single-threaded, standalone C program. 29 | It uses poll() to trap multiple clients at a time. 30 | .Pp 31 | The options are as follows: 32 | .Bl -tag -width Ds 33 | .It Fl 4 34 | Forces 35 | .Nm 36 | to use IPv4 addresses only. 37 | .It Fl 6 38 | Forces 39 | .Nm 40 | to use IPv6 addresses only. 41 | .It Fl d Ar delay 42 | Message milliseconds delay. Default: 10000 43 | .It Fl f Ar config 44 | Set and load config file. 45 | By default 46 | .Nm 47 | looks for /etc/endlessh/config. 48 | .It Fl h 49 | Print the help message and exit. 50 | .It Fl l Ar max banner length 51 | Maximum banner line length (3-255). Default: 32 52 | .It Fl m Ar max clients 53 | Maximum number of clients. Default: 4096 54 | .It Fl p Ar port 55 | Set the listening port. By default 56 | .Nm 57 | listens on port 2222. 58 | .It Fl s 59 | Print diagnostics to syslog. By default 60 | .Nm 61 | prints them to standard output. 62 | .It Fl v 63 | Print diagnostics. Can be specified up to twice to increase verbosity. 64 | .It Fl V 65 | Causes 66 | .Nm 67 | to print version information and exit. 68 | .El 69 | .Pp 70 | If 71 | .Nm 72 | receives the SIGTERM signal it will gracefully shut 73 | down the daemon, allowing it to write a complete, consistent log. 74 | .Pp 75 | A SIGHUP signal requests a reload of its configuration file. 76 | .Pp 77 | A SIGUSR1 signal will print connections stats to the log. 78 | .Sh FILES 79 | .Bl -tag -width /etc/endlessh/config -compact 80 | .It Pa /etc/endlessh/config 81 | The default 82 | .Nm 83 | configuration file. 84 | .El 85 | -------------------------------------------------------------------------------- /endlessh.c: -------------------------------------------------------------------------------- 1 | /* Endlessh: an SSH tarpit 2 | * 3 | * This is free and unencumbered software released into the public domain. 4 | */ 5 | #if defined(__OpenBSD__) 6 | # define _BSD_SOURCE /* for pledge(2) and unveil(2) */ 7 | #else 8 | # define _XOPEN_SOURCE 600 9 | #endif 10 | 11 | #include 12 | #include 13 | #include 14 | #include 15 | #include 16 | #include 17 | #include 18 | #include 19 | 20 | #include 21 | #include 22 | #include 23 | #include 24 | #include 25 | #include 26 | #include 27 | #include 28 | 29 | #define ENDLESSH_VERSION 1.1 30 | 31 | #define DEFAULT_PORT 2222 32 | #define DEFAULT_DELAY 10000 /* milliseconds */ 33 | #define DEFAULT_MAX_LINE_LENGTH 32 34 | #define DEFAULT_MAX_CLIENTS 4096 35 | 36 | #if defined(__FreeBSD__) 37 | # define DEFAULT_CONFIG_FILE "/usr/local/etc/endlessh.config" 38 | #else 39 | # define DEFAULT_CONFIG_FILE "/etc/endlessh/config" 40 | #endif 41 | 42 | #define DEFAULT_BIND_FAMILY AF_UNSPEC 43 | 44 | #define XSTR(s) STR(s) 45 | #define STR(s) #s 46 | 47 | static long long 48 | epochms(void) 49 | { 50 | struct timespec tv; 51 | clock_gettime(CLOCK_REALTIME, &tv); 52 | return tv.tv_sec * 1000ULL + tv.tv_nsec / 1000000ULL; 53 | } 54 | 55 | static enum loglevel { 56 | log_none, 57 | log_info, 58 | log_debug 59 | } loglevel = log_none; 60 | 61 | static void (*logmsg)(enum loglevel level, const char *, ...); 62 | 63 | static void 64 | logstdio(enum loglevel level, const char *format, ...) 65 | { 66 | if (loglevel >= level) { 67 | int save = errno; 68 | 69 | /* Print a timestamp */ 70 | long long now = epochms(); 71 | time_t t = now / 1000; 72 | char date[64]; 73 | struct tm tm[1]; 74 | strftime(date, sizeof(date), "%Y-%m-%dT%H:%M:%S", gmtime_r(&t, tm)); 75 | printf("%s.%03lldZ ", date, now % 1000); 76 | 77 | /* Print the rest of the log message */ 78 | va_list ap; 79 | va_start(ap, format); 80 | vprintf(format, ap); 81 | va_end(ap); 82 | fputc('\n', stdout); 83 | 84 | errno = save; 85 | } 86 | } 87 | 88 | static void 89 | logsyslog(enum loglevel level, const char *format, ...) 90 | { 91 | static const int prio_map[] = { LOG_NOTICE, LOG_INFO, LOG_DEBUG }; 92 | 93 | if (loglevel >= level) { 94 | int save = errno; 95 | 96 | /* Output the log message */ 97 | va_list ap; 98 | va_start(ap, format); 99 | char buf[256]; 100 | vsnprintf(buf, sizeof buf, format, ap); 101 | va_end(ap); 102 | syslog(prio_map[level], "%s", buf); 103 | 104 | errno = save; 105 | } 106 | } 107 | 108 | static struct { 109 | long long connects; 110 | long long milliseconds; 111 | long long bytes_sent; 112 | } statistics; 113 | 114 | struct client { 115 | char ipaddr[INET6_ADDRSTRLEN]; 116 | long long connect_time; 117 | long long send_next; 118 | long long bytes_sent; 119 | struct client *next; 120 | int port; 121 | int fd; 122 | }; 123 | 124 | static struct client * 125 | client_new(int fd, long long send_next) 126 | { 127 | struct client *c = malloc(sizeof(*c)); 128 | if (c) { 129 | c->ipaddr[0] = 0; 130 | c->connect_time = epochms(); 131 | c->send_next = send_next; 132 | c->bytes_sent = 0; 133 | c->next = 0; 134 | c->fd = fd; 135 | c->port = 0; 136 | 137 | /* Set the smallest possible recieve buffer. This reduces local 138 | * resource usage and slows down the remote end. 139 | */ 140 | int value = 1; 141 | int r = setsockopt(fd, SOL_SOCKET, SO_RCVBUF, &value, sizeof(value)); 142 | logmsg(log_debug, "setsockopt(%d, SO_RCVBUF, %d) = %d", fd, value, r); 143 | if (r == -1) 144 | logmsg(log_debug, "errno = %d, %s", errno, strerror(errno)); 145 | 146 | /* Get IP address */ 147 | struct sockaddr_storage addr; 148 | socklen_t len = sizeof(addr); 149 | if (getpeername(fd, (struct sockaddr *)&addr, &len) != -1) { 150 | if (addr.ss_family == AF_INET) { 151 | struct sockaddr_in *s = (struct sockaddr_in *)&addr; 152 | c->port = ntohs(s->sin_port); 153 | inet_ntop(AF_INET, &s->sin_addr, 154 | c->ipaddr, sizeof(c->ipaddr)); 155 | } else { 156 | struct sockaddr_in6 *s = (struct sockaddr_in6 *)&addr; 157 | c->port = ntohs(s->sin6_port); 158 | inet_ntop(AF_INET6, &s->sin6_addr, 159 | c->ipaddr, sizeof(c->ipaddr)); 160 | } 161 | } 162 | } 163 | return c; 164 | } 165 | 166 | static void 167 | client_destroy(struct client *client) 168 | { 169 | logmsg(log_debug, "close(%d)", client->fd); 170 | long long dt = epochms() - client->connect_time; 171 | logmsg(log_info, 172 | "CLOSE host=%s port=%d fd=%d " 173 | "time=%lld.%03lld bytes=%lld", 174 | client->ipaddr, client->port, client->fd, 175 | dt / 1000, dt % 1000, 176 | client->bytes_sent); 177 | statistics.milliseconds += dt; 178 | close(client->fd); 179 | free(client); 180 | } 181 | 182 | static void 183 | statistics_log_totals(struct client *clients) 184 | { 185 | long long milliseconds = statistics.milliseconds; 186 | for (long long now = epochms(); clients; clients = clients->next) 187 | milliseconds += now - clients->connect_time; 188 | logmsg(log_info, "TOTALS connects=%lld seconds=%lld.%03lld bytes=%lld", 189 | statistics.connects, 190 | milliseconds / 1000, 191 | milliseconds % 1000, 192 | statistics.bytes_sent); 193 | } 194 | 195 | struct fifo { 196 | struct client *head; 197 | struct client *tail; 198 | int length; 199 | }; 200 | 201 | static void 202 | fifo_init(struct fifo *q) 203 | { 204 | q->head = q->tail = 0; 205 | q->length = 0; 206 | } 207 | 208 | static struct client * 209 | fifo_pop(struct fifo *q) 210 | { 211 | struct client *removed = q->head; 212 | q->head = q->head->next; 213 | removed->next = 0; 214 | if (!--q->length) 215 | q->tail = 0; 216 | return removed; 217 | } 218 | 219 | static void 220 | fifo_append(struct fifo *q, struct client *c) 221 | { 222 | if (!q->tail) { 223 | q->head = q->tail = c; 224 | } else { 225 | q->tail->next = c; 226 | q->tail = c; 227 | } 228 | q->length++; 229 | } 230 | 231 | static void 232 | fifo_destroy(struct fifo *q) 233 | { 234 | struct client *c = q->head; 235 | while (c) { 236 | struct client *dead = c; 237 | c = c->next; 238 | client_destroy(dead); 239 | } 240 | q->head = q->tail = 0; 241 | q->length = 0; 242 | } 243 | 244 | static void 245 | die(void) 246 | { 247 | fprintf(stderr, "endlessh: fatal: %s\n", strerror(errno)); 248 | exit(EXIT_FAILURE); 249 | } 250 | 251 | static unsigned 252 | rand16(unsigned long s[1]) 253 | { 254 | s[0] = s[0] * 1103515245UL + 12345UL; 255 | return (s[0] >> 16) & 0xffff; 256 | } 257 | 258 | static int 259 | randline(char *line, int maxlen, unsigned long s[1]) 260 | { 261 | int len = 3 + rand16(s) % (maxlen - 2); 262 | for (int i = 0; i < len - 2; i++) 263 | line[i] = 32 + rand16(s) % 95; 264 | line[len - 2] = 13; 265 | line[len - 1] = 10; 266 | if (memcmp(line, "SSH-", 4) == 0) 267 | line[0] = 'X'; 268 | return len; 269 | } 270 | 271 | static volatile sig_atomic_t running = 1; 272 | 273 | static void 274 | sigterm_handler(int signal) 275 | { 276 | (void)signal; 277 | running = 0; 278 | } 279 | 280 | static volatile sig_atomic_t reload = 0; 281 | 282 | static void 283 | sighup_handler(int signal) 284 | { 285 | (void)signal; 286 | reload = 1; 287 | } 288 | 289 | static volatile sig_atomic_t dumpstats = 0; 290 | 291 | static void 292 | sigusr1_handler(int signal) 293 | { 294 | (void)signal; 295 | dumpstats = 1; 296 | } 297 | 298 | struct config { 299 | int port; 300 | int delay; 301 | int max_line_length; 302 | int max_clients; 303 | int bind_family; 304 | }; 305 | 306 | #define CONFIG_DEFAULT { \ 307 | .port = DEFAULT_PORT, \ 308 | .delay = DEFAULT_DELAY, \ 309 | .max_line_length = DEFAULT_MAX_LINE_LENGTH, \ 310 | .max_clients = DEFAULT_MAX_CLIENTS, \ 311 | .bind_family = DEFAULT_BIND_FAMILY, \ 312 | } 313 | 314 | static void 315 | config_set_port(struct config *c, const char *s, int hardfail) 316 | { 317 | errno = 0; 318 | char *end; 319 | long tmp = strtol(s, &end, 10); 320 | if (errno || *end || tmp < 1 || tmp > 65535) { 321 | fprintf(stderr, "endlessh: Invalid port: %s\n", s); 322 | if (hardfail) 323 | exit(EXIT_FAILURE); 324 | } else { 325 | c->port = tmp; 326 | } 327 | } 328 | 329 | static void 330 | config_set_delay(struct config *c, const char *s, int hardfail) 331 | { 332 | errno = 0; 333 | char *end; 334 | long tmp = strtol(s, &end, 10); 335 | if (errno || *end || tmp < 1 || tmp > INT_MAX) { 336 | fprintf(stderr, "endlessh: Invalid delay: %s\n", s); 337 | if (hardfail) 338 | exit(EXIT_FAILURE); 339 | } else { 340 | c->delay = tmp; 341 | } 342 | } 343 | 344 | static void 345 | config_set_max_clients(struct config *c, const char *s, int hardfail) 346 | { 347 | errno = 0; 348 | char *end; 349 | long tmp = strtol(s, &end, 10); 350 | if (errno || *end || tmp < 1 || tmp > INT_MAX) { 351 | fprintf(stderr, "endlessh: Invalid max clients: %s\n", s); 352 | if (hardfail) 353 | exit(EXIT_FAILURE); 354 | } else { 355 | c->max_clients = tmp; 356 | } 357 | } 358 | 359 | static void 360 | config_set_max_line_length(struct config *c, const char *s, int hardfail) 361 | { 362 | errno = 0; 363 | char *end; 364 | long tmp = strtol(s, &end, 10); 365 | if (errno || *end || tmp < 3 || tmp > 255) { 366 | fprintf(stderr, "endlessh: Invalid line length: %s\n", s); 367 | if (hardfail) 368 | exit(EXIT_FAILURE); 369 | } else { 370 | c->max_line_length = tmp; 371 | } 372 | } 373 | 374 | static void 375 | config_set_bind_family(struct config *c, const char *s, int hardfail) 376 | { 377 | switch (*s) { 378 | case '4': 379 | c->bind_family = AF_INET; 380 | break; 381 | case '6': 382 | c->bind_family = AF_INET6; 383 | break; 384 | case '0': 385 | c->bind_family = AF_UNSPEC; 386 | break; 387 | default: 388 | fprintf(stderr, "endlessh: Invalid address family: %s\n", s); 389 | if (hardfail) 390 | exit(EXIT_FAILURE); 391 | break; 392 | } 393 | } 394 | 395 | enum config_key { 396 | KEY_INVALID, 397 | KEY_PORT, 398 | KEY_DELAY, 399 | KEY_MAX_LINE_LENGTH, 400 | KEY_MAX_CLIENTS, 401 | KEY_LOG_LEVEL, 402 | KEY_BIND_FAMILY, 403 | }; 404 | 405 | static enum config_key 406 | config_key_parse(const char *tok) 407 | { 408 | static const char *const table[] = { 409 | [KEY_PORT] = "Port", 410 | [KEY_DELAY] = "Delay", 411 | [KEY_MAX_LINE_LENGTH] = "MaxLineLength", 412 | [KEY_MAX_CLIENTS] = "MaxClients", 413 | [KEY_LOG_LEVEL] = "LogLevel", 414 | [KEY_BIND_FAMILY] = "BindFamily" 415 | }; 416 | for (size_t i = 1; i < sizeof(table) / sizeof(*table); i++) 417 | if (!strcmp(tok, table[i])) 418 | return i; 419 | return KEY_INVALID; 420 | } 421 | 422 | static void 423 | config_load(struct config *c, const char *file, int hardfail) 424 | { 425 | long lineno = 0; 426 | FILE *f = fopen(file, "r"); 427 | if (f) { 428 | char line[256]; 429 | while (fgets(line, sizeof(line), f)) { 430 | lineno++; 431 | 432 | /* Remove comments */ 433 | char *comment = strchr(line, '#'); 434 | if (comment) 435 | *comment = 0; 436 | 437 | /* Parse tokes on line */ 438 | char *save = 0; 439 | char *tokens[3]; 440 | int ntokens = 0; 441 | for (; ntokens < 3; ntokens++) { 442 | char *tok = strtok_r(ntokens ? 0 : line, " \r\n", &save); 443 | if (!tok) 444 | break; 445 | tokens[ntokens] = tok; 446 | } 447 | 448 | switch (ntokens) { 449 | case 0: /* Empty line */ 450 | continue; 451 | case 1: 452 | fprintf(stderr, "%s:%ld: Missing value\n", file, lineno); 453 | if (hardfail) exit(EXIT_FAILURE); 454 | continue; 455 | case 2: /* Expected */ 456 | break; 457 | case 3: 458 | fprintf(stderr, "%s:%ld: Too many values\n", file, lineno); 459 | if (hardfail) exit(EXIT_FAILURE); 460 | continue; 461 | } 462 | 463 | enum config_key key = config_key_parse(tokens[0]); 464 | switch (key) { 465 | case KEY_INVALID: 466 | fprintf(stderr, "%s:%ld: Unknown option '%s'\n", 467 | file, lineno, tokens[0]); 468 | break; 469 | case KEY_PORT: 470 | config_set_port(c, tokens[1], hardfail); 471 | break; 472 | case KEY_DELAY: 473 | config_set_delay(c, tokens[1], hardfail); 474 | break; 475 | case KEY_MAX_LINE_LENGTH: 476 | config_set_max_line_length(c, tokens[1], hardfail); 477 | break; 478 | case KEY_MAX_CLIENTS: 479 | config_set_max_clients(c, tokens[1], hardfail); 480 | break; 481 | case KEY_BIND_FAMILY: 482 | config_set_bind_family(c, tokens[1], hardfail); 483 | break; 484 | case KEY_LOG_LEVEL: { 485 | errno = 0; 486 | char *end; 487 | long v = strtol(tokens[1], &end, 10); 488 | if (errno || *end || v < log_none || v > log_debug) { 489 | fprintf(stderr, "%s:%ld: Invalid log level '%s'\n", 490 | file, lineno, tokens[1]); 491 | if (hardfail) exit(EXIT_FAILURE); 492 | } else { 493 | loglevel = v; 494 | } 495 | } break; 496 | } 497 | } 498 | 499 | fclose(f); 500 | } 501 | } 502 | 503 | static void 504 | config_log(const struct config *c) 505 | { 506 | logmsg(log_info, "Port %d", c->port); 507 | logmsg(log_info, "Delay %d", c->delay); 508 | logmsg(log_info, "MaxLineLength %d", c->max_line_length); 509 | logmsg(log_info, "MaxClients %d", c->max_clients); 510 | logmsg(log_info, "BindFamily %s", 511 | c->bind_family == AF_INET6 ? "IPv6 Only" : 512 | c->bind_family == AF_INET ? "IPv4 Only" : 513 | "IPv4 Mapped IPv6"); 514 | } 515 | 516 | static void 517 | usage(FILE *f) 518 | { 519 | fprintf(f, "Usage: endlessh [-vh] [-46] [-d MS] [-f CONFIG] [-l LEN] " 520 | "[-m LIMIT] [-p PORT]\n"); 521 | fprintf(f, " -4 Bind to IPv4 only\n"); 522 | fprintf(f, " -6 Bind to IPv6 only\n"); 523 | fprintf(f, " -d INT Message millisecond delay [" 524 | XSTR(DEFAULT_DELAY) "]\n"); 525 | fprintf(f, " -f Set and load config file [" 526 | DEFAULT_CONFIG_FILE "]\n"); 527 | fprintf(f, " -h Print this help message and exit\n"); 528 | fprintf(f, " -l INT Maximum banner line length (3-255) [" 529 | XSTR(DEFAULT_MAX_LINE_LENGTH) "]\n"); 530 | fprintf(f, " -m INT Maximum number of clients [" 531 | XSTR(DEFAULT_MAX_CLIENTS) "]\n"); 532 | fprintf(f, " -p INT Listening port [" XSTR(DEFAULT_PORT) "]\n"); 533 | fprintf(f, " -v Print diagnostics to standard output " 534 | "(repeatable)\n"); 535 | fprintf(f, " -V Print version information and exit\n"); 536 | } 537 | 538 | static void 539 | print_version(void) 540 | { 541 | puts("Endlessh " XSTR(ENDLESSH_VERSION)); 542 | } 543 | 544 | static int 545 | server_create(int port, int family) 546 | { 547 | int r, s, value; 548 | 549 | s = socket(family == AF_UNSPEC ? AF_INET6 : family, SOCK_STREAM, 0); 550 | logmsg(log_debug, "socket() = %d", s); 551 | if (s == -1) die(); 552 | 553 | /* Socket options are best effort, allowed to fail */ 554 | value = 1; 555 | r = setsockopt(s, SOL_SOCKET, SO_REUSEADDR, &value, sizeof(value)); 556 | logmsg(log_debug, "setsockopt(%d, SO_REUSEADDR, true) = %d", s, r); 557 | if (r == -1) 558 | logmsg(log_debug, "errno = %d, %s", errno, strerror(errno)); 559 | 560 | /* 561 | * With OpenBSD IPv6 sockets are always IPv6-only, so the socket option 562 | * is read-only (not modifiable). 563 | * http://man.openbsd.org/ip6#IPV6_V6ONLY 564 | */ 565 | #ifndef __OpenBSD__ 566 | if (family == AF_INET6 || family == AF_UNSPEC) { 567 | errno = 0; 568 | value = (family == AF_INET6); 569 | r = setsockopt(s, IPPROTO_IPV6, IPV6_V6ONLY, &value, sizeof(value)); 570 | logmsg(log_debug, "setsockopt(%d, IPV6_V6ONLY, true) = %d", s, r); 571 | if (r == -1) 572 | logmsg(log_debug, "errno = %d, %s", errno, strerror(errno)); 573 | } 574 | #endif 575 | 576 | if (family == AF_INET) { 577 | struct sockaddr_in addr4 = { 578 | .sin_family = AF_INET, 579 | .sin_port = htons(port), 580 | .sin_addr = {INADDR_ANY} 581 | }; 582 | r = bind(s, (void *)&addr4, sizeof(addr4)); 583 | } else { 584 | struct sockaddr_in6 addr6 = { 585 | .sin6_family = AF_INET6, 586 | .sin6_port = htons(port), 587 | .sin6_addr = in6addr_any 588 | }; 589 | r = bind(s, (void *)&addr6, sizeof(addr6)); 590 | } 591 | logmsg(log_debug, "bind(%d, port=%d) = %d", s, port, r); 592 | if (r == -1) die(); 593 | 594 | r = listen(s, INT_MAX); 595 | logmsg(log_debug, "listen(%d) = %d", s, r); 596 | if (r == -1) die(); 597 | 598 | return s; 599 | } 600 | 601 | /* Write a line to a client, returning client if it's still up. */ 602 | static struct client * 603 | sendline(struct client *client, int max_line_length, unsigned long *rng) 604 | { 605 | char line[256]; 606 | int len = randline(line, max_line_length, rng); 607 | for (;;) { 608 | ssize_t out = write(client->fd, line, len); 609 | logmsg(log_debug, "write(%d) = %d", client->fd, (int)out); 610 | if (out == -1) { 611 | if (errno == EINTR) { 612 | continue; /* try again */ 613 | } else if (errno == EAGAIN || errno == EWOULDBLOCK) { 614 | return client; /* don't care */ 615 | } else { 616 | client_destroy(client); 617 | return 0; 618 | } 619 | } else { 620 | client->bytes_sent += out; 621 | statistics.bytes_sent += out; 622 | return client; 623 | } 624 | } 625 | } 626 | 627 | 628 | int 629 | main(int argc, char **argv) 630 | { 631 | logmsg = logstdio; 632 | struct config config = CONFIG_DEFAULT; 633 | const char *config_file = DEFAULT_CONFIG_FILE; 634 | 635 | #if defined(__OpenBSD__) 636 | unveil(config_file, "r"); /* return ignored as the file may not exist */ 637 | if (pledge("inet stdio rpath unveil", 0) == -1) 638 | die(); 639 | #endif 640 | 641 | config_load(&config, config_file, 1); 642 | 643 | int option; 644 | while ((option = getopt(argc, argv, "46d:f:hl:m:p:svV")) != -1) { 645 | switch (option) { 646 | case '4': 647 | config_set_bind_family(&config, "4", 1); 648 | break; 649 | case '6': 650 | config_set_bind_family(&config, "6", 1); 651 | break; 652 | case 'd': 653 | config_set_delay(&config, optarg, 1); 654 | break; 655 | case 'f': 656 | config_file = optarg; 657 | 658 | #if defined(__OpenBSD__) 659 | unveil(config_file, "r"); 660 | if (unveil(0, 0) == -1) 661 | die(); 662 | #endif 663 | 664 | config_load(&config, optarg, 1); 665 | break; 666 | case 'h': 667 | usage(stdout); 668 | exit(EXIT_SUCCESS); 669 | break; 670 | case 'l': 671 | config_set_max_line_length(&config, optarg, 1); 672 | break; 673 | case 'm': 674 | config_set_max_clients(&config, optarg, 1); 675 | break; 676 | case 'p': 677 | config_set_port(&config, optarg, 1); 678 | break; 679 | case 's': 680 | logmsg = logsyslog; 681 | break; 682 | case 'v': 683 | if (loglevel < log_debug) 684 | loglevel++; 685 | break; 686 | case 'V': 687 | print_version(); 688 | exit(EXIT_SUCCESS); 689 | break; 690 | default: 691 | usage(stderr); 692 | exit(EXIT_FAILURE); 693 | } 694 | } 695 | 696 | if (argv[optind]) { 697 | fprintf(stderr, "endlessh: too many arguments\n"); 698 | exit(EXIT_FAILURE); 699 | } 700 | 701 | if (logmsg == logsyslog) { 702 | /* Prepare the syslog */ 703 | const char *prog = strrchr(argv[0], '/'); 704 | prog = prog ? prog + 1 : argv[0]; 705 | openlog(prog, LOG_PID, LOG_DAEMON); 706 | } else { 707 | /* Set output (log) to line buffered */ 708 | setvbuf(stdout, 0, _IOLBF, 0); 709 | } 710 | 711 | /* Log configuration */ 712 | config_log(&config); 713 | 714 | /* Install the signal handlers */ 715 | signal(SIGPIPE, SIG_IGN); 716 | { 717 | struct sigaction sa = {.sa_handler = sigterm_handler}; 718 | int r = sigaction(SIGTERM, &sa, 0); 719 | if (r == -1) 720 | die(); 721 | } 722 | { 723 | struct sigaction sa = {.sa_handler = sighup_handler}; 724 | int r = sigaction(SIGHUP, &sa, 0); 725 | if (r == -1) 726 | die(); 727 | } 728 | { 729 | struct sigaction sa = {.sa_handler = sigusr1_handler}; 730 | int r = sigaction(SIGUSR1, &sa, 0); 731 | if (r == -1) 732 | die(); 733 | } 734 | 735 | struct fifo fifo[1]; 736 | fifo_init(fifo); 737 | 738 | unsigned long rng = epochms(); 739 | 740 | int server = server_create(config.port, config.bind_family); 741 | 742 | while (running) { 743 | if (reload) { 744 | /* Configuration reload requested (SIGHUP) */ 745 | int oldport = config.port; 746 | int oldfamily = config.bind_family; 747 | config_load(&config, config_file, 0); 748 | config_log(&config); 749 | if (oldport != config.port || oldfamily != config.bind_family) { 750 | close(server); 751 | server = server_create(config.port, config.bind_family); 752 | } 753 | reload = 0; 754 | } 755 | if (dumpstats) { 756 | /* print stats requested (SIGUSR1) */ 757 | statistics_log_totals(fifo->head); 758 | dumpstats = 0; 759 | } 760 | 761 | /* Enqueue clients that are due for another message */ 762 | int timeout = -1; 763 | long long now = epochms(); 764 | while (fifo->head) { 765 | if (fifo->head->send_next <= now) { 766 | struct client *c = fifo_pop(fifo); 767 | if (sendline(c, config.max_line_length, &rng)) { 768 | c->send_next = now + config.delay; 769 | fifo_append(fifo, c); 770 | } 771 | } else { 772 | timeout = fifo->head->send_next - now; 773 | break; 774 | } 775 | } 776 | 777 | /* Wait for next event */ 778 | struct pollfd fds = {server, POLLIN, 0}; 779 | int nfds = fifo->length < config.max_clients; 780 | logmsg(log_debug, "poll(%d, %d)", nfds, timeout); 781 | int r = poll(&fds, nfds, timeout); 782 | logmsg(log_debug, "= %d", r); 783 | if (r == -1) { 784 | switch (errno) { 785 | case EINTR: 786 | logmsg(log_debug, "EINTR"); 787 | continue; 788 | default: 789 | fprintf(stderr, "endlessh: fatal: %s\n", strerror(errno)); 790 | exit(EXIT_FAILURE); 791 | } 792 | } 793 | 794 | /* Check for new incoming connections */ 795 | if (fds.revents & POLLIN) { 796 | int fd = accept(server, 0, 0); 797 | logmsg(log_debug, "accept() = %d", fd); 798 | statistics.connects++; 799 | if (fd == -1) { 800 | const char *msg = strerror(errno); 801 | switch (errno) { 802 | case EMFILE: 803 | case ENFILE: 804 | config.max_clients = fifo->length; 805 | logmsg(log_info, 806 | "MaxClients %d", 807 | fifo->length); 808 | break; 809 | case ECONNABORTED: 810 | case EINTR: 811 | case ENOBUFS: 812 | case ENOMEM: 813 | case EPROTO: 814 | fprintf(stderr, "endlessh: warning: %s\n", msg); 815 | break; 816 | default: 817 | fprintf(stderr, "endlessh: fatal: %s\n", msg); 818 | exit(EXIT_FAILURE); 819 | } 820 | } else { 821 | long long send_next = epochms() + config.delay; 822 | struct client *client = client_new(fd, send_next); 823 | int flags = fcntl(fd, F_GETFL, 0); /* cannot fail */ 824 | fcntl(fd, F_SETFL, flags | O_NONBLOCK); /* cannot fail */ 825 | if (!client) { 826 | fprintf(stderr, "endlessh: warning: out of memory\n"); 827 | close(fd); 828 | } else { 829 | fifo_append(fifo, client); 830 | logmsg(log_info, "ACCEPT host=%s port=%d fd=%d n=%d/%d", 831 | client->ipaddr, client->port, client->fd, 832 | fifo->length, config.max_clients); 833 | } 834 | } 835 | } 836 | } 837 | 838 | fifo_destroy(fifo); 839 | statistics_log_totals(0); 840 | 841 | if (logmsg == logsyslog) 842 | closelog(); 843 | } 844 | -------------------------------------------------------------------------------- /util/endlessh.service: -------------------------------------------------------------------------------- 1 | [Unit] 2 | Description=Endlessh SSH Tarpit 3 | Documentation=man:endlessh(1) 4 | Requires=network-online.target 5 | 6 | [Service] 7 | Type=simple 8 | Restart=always 9 | RestartSec=30sec 10 | ExecStart=/usr/local/bin/endlessh 11 | KillSignal=SIGTERM 12 | 13 | # Stop trying to restart the service if it restarts too many times in a row 14 | StartLimitInterval=5min 15 | StartLimitBurst=4 16 | 17 | StandardOutput=journal 18 | StandardError=journal 19 | StandardInput=null 20 | 21 | PrivateTmp=true 22 | PrivateDevices=true 23 | ProtectSystem=full 24 | ProtectHome=true 25 | InaccessiblePaths=/run /var 26 | 27 | ## If you want Endlessh to bind on ports < 1024 28 | ## 1) run: 29 | ## setcap 'cap_net_bind_service=+ep' /usr/local/bin/endlessh 30 | ## 2) uncomment following line 31 | #AmbientCapabilities=CAP_NET_BIND_SERVICE 32 | ## 3) comment following line 33 | PrivateUsers=true 34 | 35 | NoNewPrivileges=true 36 | ConfigurationDirectory=endlessh 37 | ProtectKernelTunables=true 38 | ProtectKernelModules=true 39 | ProtectControlGroups=true 40 | MemoryDenyWriteExecute=true 41 | 42 | [Install] 43 | WantedBy=multi-user.target 44 | 45 | -------------------------------------------------------------------------------- /util/openbsd/README.md: -------------------------------------------------------------------------------- 1 | # Running `endlessh` on OpenBSD 2 | 3 | ## Covering IPv4 and IPv6 4 | 5 | If you want to cover both IPv4 and IPv6 you'll need to run *two* instances of 6 | `endlessh` due to OpenBSD limitations. Here's how I did it: 7 | 8 | - copy the `endlessh` script to `rc.d` twice, as `endlessh` and `endlessh6` 9 | - copy the `config` file to `/etc/endlessh` twice, as `config` and `config6` 10 | - use `BindFamily 4` in `config` 11 | - use `BindFamily 6` in `config6` 12 | - in `rc.conf.local` force `endlessh6` to load `config6` like so: 13 | 14 | ``` 15 | endlessh6_flags=-s -f /etc/endlessh/config6 16 | endlessh_flags=-s 17 | ``` 18 | 19 | ## Covering more than 128 connections 20 | 21 | The defaults in OpenBSD only allow for 128 open file descriptors per process, 22 | so regardless of the `MaxClients` setting in `/etc/config` you'll end up with 23 | something like 124 clients at the most. 24 | You can increase these limits in `/etc/login.conf` for `endlessh` (and 25 | `endlessh6`) like so: 26 | 27 | ``` 28 | endlessh:\ 29 | :openfiles=1024:\ 30 | :tc=daemon: 31 | endlessh6:\ 32 | :openfiles=1024:\ 33 | :tc=daemon: 34 | ``` 35 | -------------------------------------------------------------------------------- /util/openbsd/endlessh: -------------------------------------------------------------------------------- 1 | #!/bin/ksh 2 | # 3 | 4 | daemon="/usr/local/bin/endlessh" 5 | rc_bg=YES 6 | 7 | . /etc/rc.d/rc.subr 8 | 9 | rc_cmd $1 10 | -------------------------------------------------------------------------------- /util/pivot.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | 3 | # This script accepts a log on standard input and produces a CSV table 4 | # with one connection per row. 5 | # 6 | # $ util/pivot.py 0: 35 | print('warning: %d hanging entries' % len(table), file=sys.stderr) 36 | -------------------------------------------------------------------------------- /util/schema.sql: -------------------------------------------------------------------------------- 1 | CREATE TABLE IF NOT EXISTS log ( 2 | host TEXT, 3 | port INTEGER, 4 | time REAL, 5 | bytes INTEGER 6 | ); 7 | .mode csv 8 | .import /dev/stdin log 9 | -------------------------------------------------------------------------------- /util/smf/README: -------------------------------------------------------------------------------- 1 | Solaris SMF installation 2 | ======================== 3 | 4 | Before installing SMF: 5 | 6 | 1. Put endlessh binary to /usr/local/bin 7 | 2. Edit endlessh.conf and put it to /usr/local/etc 8 | 9 | To install SMF: 10 | 11 | 1. Put endlessh.xml to /var/svc/manifest/network 12 | 2. Run svccfg import endlessh.xml 13 | 3. Put init.endlessh to /lib/svc/method 14 | 4. Run svcadm enable endlessh 15 | 16 | Note: Log will write to /var/log/endlessh.log by default. 17 | 18 | To uninstall SMF: 19 | 20 | 1. Run svcadm disable endlessh 21 | 2. rm -f /lib/svc/method/init.endlessh 22 | 3. svccfg delete svc:/network/endlessh:default 23 | 4. rm -f /var/svc/manifest/network/endlessh.xml 24 | 25 | Enjoy! :) -------------------------------------------------------------------------------- /util/smf/endlessh.conf: -------------------------------------------------------------------------------- 1 | # The port on which to listen for new SSH connections. 2 | Port 22 3 | 4 | # The endless banner is sent one line at a time. This is the delay 5 | # in milliseconds between individual lines. 6 | Delay 10000 7 | 8 | # The length of each line is randomized. This controls the maximum 9 | # length of each line. Shorter lines may keep clients on for longer if 10 | # they give up after a certain number of bytes. 11 | MaxLineLength 32 12 | 13 | # Maximum number of connections to accept at a time. Connections beyond 14 | # this are not immediately rejected, but will wait in the queue. 15 | MaxClients 4096 16 | 17 | # Set the detail level for the log. 18 | # 0 = Quiet 19 | # 1 = Standard, useful log messages 20 | # 2 = Very noisy debugging information 21 | LogLevel 1 22 | 23 | # Set the family of the listening socket 24 | # 0 = Use IPv4 Mapped IPv6 (Both v4 and v6, default) 25 | # 4 = Use IPv4 only 26 | # 6 = Use IPv6 only 27 | BindFamily 0 28 | -------------------------------------------------------------------------------- /util/smf/endlessh.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 7 | 8 | 9 | 13 | 14 | 15 | 16 | 17 | 18 | 22 | 24 | 25 | 26 | 30 | 31 | 32 | 33 | 37 | 38 | 39 | 40 | 44 | 45 | 46 | 47 | 51 | 52 | 53 | 54 | 59 | 60 | 65 | 66 | 71 | 72 | 77 | 78 | 79 | 80 | 82 | 83 | 84 | 85 | 86 | 93 | 94 | 95 | 96 | -------------------------------------------------------------------------------- /util/smf/init.endlessh: -------------------------------------------------------------------------------- 1 | #!/sbin/sh 2 | 3 | # 4 | # Control Method for endlessh (/lib/svc/method/init.endlessh) 5 | # Written by Yuri Voinov (C) 2007,2019 6 | # 7 | # ident "@(#)endlessh.sh 1.8 19/27/03 YV" 8 | # 9 | 10 | ############# 11 | # Variables # 12 | ############# 13 | 14 | # Base installation directory 15 | BASE_DIR="/usr/local" 16 | BASE_CONFIG_DIR=$BASE_DIR"/etc" 17 | 18 | # endlessh files paths 19 | ENDLESSH_PATH="$BASE_DIR""/bin" 20 | ENDLESSH_CONF_PATH="$BASE_CONFIG_DIR" 21 | 22 | # endlessh files 23 | ENDLESSH_BIN_FILE="endlessh" 24 | ENDLESSH_CONF_FILE=$ENDLESSH_BIN_FILE".conf" 25 | 26 | # Daemon settings 27 | ENDLESSH_CONF="$ENDLESSH_CONF_PATH/$ENDLESSH_CONF_FILE" 28 | 29 | # Log 30 | LOG_DIR="/var/log" 31 | LOGFILE=$LOG_DIR/$ENDLESSH_BIN_FILE".log" 32 | 33 | # 34 | # OS Commands location variables 35 | # 36 | CUT=`which cut` 37 | ECHO=`which echo` 38 | KILL=`which kill` 39 | PGREP=`which pgrep` 40 | UNAME=`which uname` 41 | 42 | # OS release 43 | OS_VER=`$UNAME -r|$CUT -f2 -d"."` 44 | OS_NAME=`$UNAME -s|$CUT -f1 -d" "` 45 | 46 | ############### 47 | # Subroutines # 48 | ############### 49 | 50 | check_endlessh () 51 | { 52 | # Check endlessh installed 53 | program=$1 54 | if [ ! -f "$ENDLESSH_PATH/$program" -a ! -x "$ENDLESSH_PATH/$program" ]; then 55 | $ECHO "ERROR: endlessh not found!" 56 | $ECHO "Exiting..." 57 | exit 1 58 | fi 59 | } 60 | 61 | check_os () 62 | { 63 | # Check OS version 64 | if [ ! "$OS_NAME" = "SunOS" -a ! "$OS_VER" -lt "10" ]; then 65 | $ECHO "ERROR: Unsupported OS $OS_NAME $OS_VER" 66 | $ECHO "Exiting..." 67 | exit 1 68 | fi 69 | } 70 | 71 | checkconf () 72 | { 73 | # Check endlessh config file 74 | config=$1 75 | if [ -f "$ENDLESSH_CONF_PATH"/"$config" ]; then 76 | $ECHO "1" 77 | else 78 | $ECHO "0" 79 | fi 80 | } 81 | 82 | startproc() 83 | { 84 | # Start endlessh daemon 85 | program=$1 86 | if [ "`checkconf $ENDLESSH_CONF_FILE`" != "1" ]; then 87 | $ECHO "ERROR: Config file $ENDLESSH_CONF_PATH/$ENDLESSH_CONF_FILE not found." 88 | $ECHO "Exiting..." 89 | exit 2 90 | else 91 | $ENDLESSH_PATH/$program -f $ENDLESSH_CONF_PATH/$ENDLESSH_CONF_FILE -v >$LOGFILE & 92 | fi 93 | } 94 | 95 | stopproc() 96 | { 97 | # Stop endlessh daemon 98 | program=$1 99 | if [ "`checkconf $ENDLESSH_CONF_FILE`" != "1" ]; then 100 | $ECHO "ERROR: Config file $ENDLESSH_CONF_PATH/$ENDLESSH_CONF_FILE not found." 101 | $ECHO "Exiting..." 102 | exit 2 103 | else 104 | $KILL -s TERM `$PGREP $program`>/dev/null 2>&1 105 | fi 106 | } 107 | 108 | ############## 109 | # Main block # 110 | ############## 111 | 112 | # Check endlessh installed 113 | check_endlessh $ENDLESSH_BIN_FILE 114 | 115 | # Check OS version 116 | check_os 117 | 118 | case "$1" in 119 | "start") 120 | startproc $ENDLESSH_BIN_FILE 121 | ;; 122 | "stop") 123 | stopproc $ENDLESSH_BIN_FILE 124 | ;; 125 | "refresh") 126 | $KILL -s HUP `$PGREP $ENDLESSH_BIN_FILE`>/dev/null 2>&1 127 | ;; 128 | "restart") 129 | stopproc $ENDLESSH_BIN_FILE 130 | startproc $ENDLESSH_BIN_FILE 131 | ;; 132 | *) 133 | $ECHO "Usage: $0 { start | stop | restart | refresh }" 134 | exit 1 135 | esac 136 | 137 | exit 0 138 | --------------------------------------------------------------------------------