├── .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 |
87 |
88 |
89 | endlessh service
90 |
91 |
92 |
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 |
--------------------------------------------------------------------------------