1 Commits

Author SHA1 Message Date
Christopher Wellons
30e3ff1f32 Example of making the first line special 2019-03-26 10:48:21 -04:00
9 changed files with 196 additions and 615 deletions

View File

@@ -3,18 +3,9 @@ CC = cc
CFLAGS = -std=c99 -Wall -Wextra -Wno-missing-field-initializers -Os
LDFLAGS = -ggdb3
LDLIBS =
PREFIX = /usr/local
all: endlessh
endlessh: endlessh.c
$(CC) $(LDFLAGS) $(CFLAGS) -o $@ endlessh.c $(LDLIBS)
install: endlessh
install -d $(DESTDIR)$(PREFIX)/bin
install -m 755 endlessh $(DESTDIR)$(PREFIX)/bin/
install -d $(DESTDIR)$(PREFIX)/share/man/man1
install -m 644 endlessh.1 $(DESTDIR)$(PREFIX)/share/man/man1/
clean:
rm -rf endlessh

View File

@@ -19,8 +19,6 @@ Usage information is printed with `-h`.
```
Usage: endlessh [-vh] [-d MS] [-f CONFIG] [-l LEN] [-m LIMIT] [-p PORT]
-4 Bind to IPv4 only
-6 Bind to IPv6 only
-d INT Message millisecond delay [10000]
-f Set and load config file [/etc/endlessh/config]
-h Print this help message and exit
@@ -45,8 +43,6 @@ write a complete, consistent log.
A SIGHUP signal requests a reload of the configuration file (`-f`).
A SIGUSR1 signal will print connections stats to the log.
## Sample Configuration File
The configuration file has similar syntax to OpenSSH.
@@ -73,40 +69,15 @@ MaxClients 4096
# 1 = Standard, useful log messages
# 2 = Very noisy debugging information
LogLevel 0
# Set the family of the listening socket
# 0 = Use IPv4 Mapped IPv6 (Both v4 and v6, default)
# 4 = Use IPv4 only
# 6 = Use IPv6 only
BindFamily 0
```
## Build issues
Some more esoteric systems require extra configuration when building.
### RHEL 6 / CentOS 6
This system uses a version of glibc older than 2.17 (December 2012), and
`clock_gettime(2)` is still in librt. For these systems you will need to
link against librt:
RHEL 6 and CentOS 6 use a version of glibc older than 2.17 (December
2012), and `clock_gettime(2)` is still in librt. For these systems you
will need to link against librt:
make LDLIBS=-lrt
### Solaris / illumos
These systems don't include all the necessary functionality in libc and
the linker requires some extra libraries:
make CC=gcc LDLIBS='-lnsl -lrt -lsocket'
If you're not using GCC or Clang, also override `CFLAGS` and `LDFLAGS`
to remove GCC-specific options. For example, on Solaris:
make CFLAGS=-fast LDFLAGS= LDLIBS='-lnsl -lrt -lsocket'
The feature test macros on these systems isn't reliable, so you may also
need to use `-D__EXTENSIONS__` in `CFLAGS`.
[np]: https://nullprogram.com/blog/2019/03/22/

View File

@@ -1,81 +0,0 @@
.Dd $Mdocdate: April 12 2019 $
.Dt ENDLESSH 1
.Os
.Sh NAME
.Nm endless
.Nd An SSH tarpit
.Sh SYNOPSIS
.Nm endless
.Op Fl 46chvV
.Op Fl d Ar delay
.Op Fl f Ar config
.Op Fl l Ar max banner length
.Op Fl m Ar max clients
.Op Fl p Ar port
.Sh DESCRIPTION
.Nm
is an SSH tarpit that very slowly
sends an endless, random SSH banner.
.Pp
.Nm
keeps SSH clients locked up for hours or even days at a time.
The purpose is to put your real SSH server on another port
and then let the script kiddies get stuck in this tarpit
instead of bothering a real server.
.Pp
Since the tarpit is in the banner before any cryptographic
exchange occurs, this program doesn't depend on any cryptographic
libraries. It's a simple, single-threaded, standalone C program.
It uses poll() to trap multiple clients at a time.
.Pp
The options are as follows:
.Bl -tag -width Ds
.It Fl 4
Forces
.Nm
to use IPv4 addresses only.
.It Fl 6
Forces
.Nm
to use IPv6 addresses only.
.It Fl d Ar delay
Message milliseconds delay. Default: 10000
.It Fl f Ar config
Set and load config file.
By default
.Nm
looks for /etc/endlessh/config.
.It Fl h
Print the help message and exit.
.It Fl l Ar max banner length
Maximum banner line length (3-255). Default: 32
.It Fl m Ar max clients
Maximum number of clients. Default: 4096
.It Fl p Ar port
Set the listening port. By default
.Nm
listens on port 2222.
.It Fl v
Print diagnostics to standard output. Can be specified
numerous times to increase verbosity.
.It Fl V
Causes
.Nm
to print version information and exit.
.El
.El
.Pp
If
.Nm
receives the SIGTERM signal it will gracefully shut
down the daemon, allowing it to write a complete, consistent log.
.Pp
A SIGHUP signal requests a reload of its configuration file.
.Pp
A SIGUSR1 signal will print connections stats to the log.
.Sh FILES
.Bl -tag -width /etc/endlessh/config -compact
.It Pa /etc/endlessh/config
The default
.Nm
configuration file.

View File

@@ -1,4 +1,22 @@
#define _XOPEN_SOURCE 600
#ifdef __FreeBSD__
# define _WITH_GETLINE
/* The MSG_DONTWAIT send(2) flag is non-standard, but widely available.
* However, FreeBSD doesn't define this flag when using POSIX feature
* test macros. Normally feature test macros are required to expose
* POSIX functionality, though FreeBSD isn't strict about this. In a
* sense it's technically correct to hide a non-standard flag when
* asking for strict standards compliance, but this behavior makes this
* flag impossible to use in portable programs, at least without this
* sort of special case.
*
* To get the prototype for getline(3), we need either a POSIX feature
* test macro or use the FreeBSD-specific _WITH_GETLINE macro. Since we
* can't use the former, we'll have to go with the latter.
*/
#else
# define _POSIX_C_SOURCE 200809L
#endif
#include <time.h>
#include <errno.h>
#include <stdio.h>
@@ -9,27 +27,28 @@
#include <string.h>
#include <poll.h>
#include <fcntl.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <netinet/in.h>
#define ENDLESSH_VERSION 1.0
#define ENDLESSH_VERSION 0.1
#define DEFAULT_PORT 2222
#define DEFAULT_DELAY 10000 /* milliseconds */
#define DEFAULT_MAX_LINE_LENGTH 32
#define DEFAULT_MAX_CLIENTS 4096
#define DEFAULT_CONFIG_FILE "/etc/endlessh/config"
#define DEFAULT_BIND_FAMILY AF_UNSPEC
#define FLAG_SPECIAL_SENT (1 << 0)
#define SPECIAL_MESSAGE "Stay awhile and listen\r\n"
#define XSTR(s) STR(s)
#define STR(s) #s
static long long
epochms(void)
uepoch(void)
{
struct timespec tv;
clock_gettime(CLOCK_REALTIME, &tv);
@@ -49,7 +68,7 @@ logmsg(enum loglevel level, const char *format, ...)
int save = errno;
/* Print a timestamp */
long long now = epochms();
long long now = uepoch();
time_t t = now / 1000;
char date[64];
struct tm tm[1];
@@ -67,12 +86,6 @@ logmsg(enum loglevel level, const char *format, ...)
}
}
struct {
long long connects;
long long milliseconds;
long long bytes_sent;
} statistics;
struct client {
char ipaddr[INET6_ADDRSTRLEN];
long long connect_time;
@@ -81,6 +94,7 @@ struct client {
struct client *next;
int port;
int fd;
int flags;
};
static struct client *
@@ -89,12 +103,12 @@ client_new(int fd, long long send_next)
struct client *c = malloc(sizeof(*c));
if (c) {
c->ipaddr[0] = 0;
c->connect_time = epochms();
c->connect_time = uepoch();
c->send_next = send_next;
c->bytes_sent = 0;
c->next = 0;
c->fd = fd;
c->port = 0;
c->flags = 0;
/* Set the smallest possible recieve buffer. This reduces local
* resource usage and slows down the remote end.
@@ -129,57 +143,59 @@ static void
client_destroy(struct client *client)
{
logmsg(LOG_DEBUG, "close(%d)", client->fd);
long long dt = epochms() - client->connect_time;
long long dt = uepoch() - client->connect_time;
logmsg(LOG_INFO,
"CLOSE host=%s port=%d fd=%d "
"time=%lld.%03lld bytes=%lld",
client->ipaddr, client->port, client->fd,
dt / 1000, dt % 1000,
client->bytes_sent);
statistics.milliseconds += dt;
close(client->fd);
free(client);
}
static void
statistics_log_totals(struct client *clients)
{
long long milliseconds = statistics.milliseconds;
for (long long now = epochms(); clients; clients = clients->next)
milliseconds += now - clients->connect_time;
logmsg(LOG_INFO, "TOTALS connects=%lld seconds=%lld.%03lld bytes=%lld",
statistics.connects,
milliseconds / 1000,
milliseconds % 1000,
statistics.bytes_sent);
}
struct fifo {
struct queue {
struct client *head;
struct client *tail;
int length;
};
static void
fifo_init(struct fifo *q)
queue_init(struct queue *q)
{
q->head = q->tail = 0;
q->length = 0;
}
static struct client *
fifo_pop(struct fifo *q)
queue_remove(struct queue *q, int fd)
{
struct client *removed = q->head;
q->head = q->head->next;
removed->next = 0;
if (!--q->length)
q->tail = 0;
return removed;
/* Yes, this is a linear search, but the element we're looking for
* is virtually always one of the first few elements.
*/
struct client *c;
struct client *prev = 0;
for (c = q->head; c; prev = c, c = c->next) {
if (c->fd == fd) {
if (!--q->length) {
q->head = q->tail = 0;
} else if (q->tail == c) {
q->tail = prev;
prev->next = 0;
} else if (prev) {
prev->next = c->next;
} else {
q->head = c->next;
}
c->next = 0;
break;
}
}
return c;
}
static void
fifo_append(struct fifo *q, struct client *c)
queue_append(struct queue *q, struct client *c)
{
if (!q->tail) {
q->head = q->tail = c;
@@ -191,7 +207,7 @@ fifo_append(struct fifo *q, struct client *c)
}
static void
fifo_destroy(struct fifo *q)
queue_destroy(struct queue *q)
{
struct client *c = q->head;
while (c) {
@@ -203,6 +219,56 @@ fifo_destroy(struct fifo *q)
q->length = 0;
}
struct pollvec {
size_t cap;
size_t fill;
struct pollfd *fds;
};
static void
pollvec_init(struct pollvec *v)
{
v->cap = 4;
v->fill = 0;
v->fds = malloc(v->cap * sizeof(v->fds[0]));
if (!v->fds) {
fprintf(stderr, "endlessh: pollvec initialization failed\n");
exit(EXIT_FAILURE);
}
}
static void
pollvec_clear(struct pollvec *v)
{
v->fill = 0;
}
static int
pollvec_push(struct pollvec *v, int fd, short events)
{
if (v->cap == v->fill) {
size_t size = v->cap * 2 * sizeof(v->fds[0]);
if (size < v->cap * sizeof(v->fds[0]))
return 0; // overflow
struct pollfd *grow = realloc(v->fds, size);
if (!grow)
return 0;
v->fds = grow;
v->cap *= 2;
}
v->fds[v->fill].fd = fd;
v->fds[v->fill].events = events;
v->fill++;
return 1;
}
static void
pollvec_free(struct pollvec *v)
{
free(v->fds);
v->fds = 0;
}
static void
die(void)
{
@@ -248,21 +314,11 @@ sighup_handler(int signal)
reload = 1;
}
static volatile sig_atomic_t dumpstats = 0;
static void
sigusr1_handler(int signal)
{
(void)signal;
dumpstats = 1;
}
struct config {
int port;
int delay;
int max_line_length;
int max_clients;
int bind_family;
};
#define CONFIG_DEFAULT { \
@@ -270,7 +326,6 @@ struct config {
.delay = DEFAULT_DELAY, \
.max_line_length = DEFAULT_MAX_LINE_LENGTH, \
.max_clients = DEFAULT_MAX_CLIENTS, \
.bind_family = DEFAULT_BIND_FAMILY, \
}
static void
@@ -333,27 +388,6 @@ config_set_max_line_length(struct config *c, const char *s, int hardfail)
}
}
static void
config_set_bind_family(struct config *c, const char *s, int hardfail)
{
switch (*s) {
case '4':
c->bind_family = AF_INET;
break;
case '6':
c->bind_family = AF_INET6;
break;
case '0':
c->bind_family = AF_UNSPEC;
break;
default:
fprintf(stderr, "endlessh: Invalid address family: %s\n", s);
if (hardfail)
exit(EXIT_FAILURE);
break;
}
}
enum config_key {
KEY_INVALID,
KEY_PORT,
@@ -361,7 +395,6 @@ enum config_key {
KEY_MAX_LINE_LENGTH,
KEY_MAX_CLIENTS,
KEY_LOG_LEVEL,
KEY_BIND_FAMILY,
};
static enum config_key
@@ -373,7 +406,6 @@ config_key_parse(const char *tok)
[KEY_MAX_LINE_LENGTH] = "MaxLineLength",
[KEY_MAX_CLIENTS] = "MaxClients",
[KEY_LOG_LEVEL] = "LogLevel",
[KEY_BIND_FAMILY] = "BindFamily"
};
for (size_t i = 1; i < sizeof(table) / sizeof(*table); i++)
if (!strcmp(tok, table[i]))
@@ -387,8 +419,9 @@ config_load(struct config *c, const char *file, int hardfail)
long lineno = 0;
FILE *f = fopen(file, "r");
if (f) {
char line[256];
while (fgets(line, sizeof(line), f)) {
size_t len = 0;
char *line = 0;
while (getline(&line, &len, f) != -1) {
lineno++;
/* Remove comments */
@@ -440,9 +473,6 @@ config_load(struct config *c, const char *file, int hardfail)
case KEY_MAX_CLIENTS:
config_set_max_clients(c, tokens[1], hardfail);
break;
case KEY_BIND_FAMILY:
config_set_bind_family(c, tokens[1], hardfail);
break;
case KEY_LOG_LEVEL: {
errno = 0;
char *end;
@@ -458,6 +488,7 @@ config_load(struct config *c, const char *file, int hardfail)
}
}
free(line);
fclose(f);
}
}
@@ -469,19 +500,13 @@ config_log(const struct config *c)
logmsg(LOG_INFO, "Delay %ld", c->delay);
logmsg(LOG_INFO, "MaxLineLength %d", c->max_line_length);
logmsg(LOG_INFO, "MaxClients %d", c->max_clients);
logmsg(LOG_INFO, "BindFamily %s",
c->bind_family == AF_INET6 ? "IPv6 Only" :
c->bind_family == AF_INET ? "IPv4 Only" :
"IPv4 Mapped IPv6");
}
static void
usage(FILE *f)
{
fprintf(f, "Usage: endlessh [-vh] [-46] [-d MS] [-f CONFIG] [-l LEN] "
fprintf(f, "Usage: endlessh [-vh] [-d MS] [-f CONFIG] [-l LEN] "
"[-m LIMIT] [-p PORT]\n");
fprintf(f, " -4 Bind to IPv4 only\n");
fprintf(f, " -6 Bind to IPv6 only\n");
fprintf(f, " -d INT Message millisecond delay ["
XSTR(DEFAULT_DELAY) "]\n");
fprintf(f, " -f Set and load config file ["
@@ -504,11 +529,11 @@ print_version(void)
}
static int
server_create(int port, int family)
server_create(int port)
{
int r, s, value;
s = socket(family == AF_UNSPEC ? AF_INET6 : family, SOCK_STREAM, 0);
s = socket(AF_INET6, SOCK_STREAM, 0);
logmsg(LOG_DEBUG, "socket() = %d", s);
if (s == -1) die();
@@ -519,37 +544,12 @@ server_create(int port, int family)
if (r == -1)
logmsg(LOG_DEBUG, "errno = %d, %s", errno, strerror(errno));
/*
* With OpenBSD IPv6 sockets are always IPv6-only, so the socket option
* is read-only (not modifiable).
* http://man.openbsd.org/ip6#IPV6_V6ONLY
*/
#ifndef __OpenBSD__
if (family == AF_INET6 || family == AF_UNSPEC) {
errno = 0;
value = (family == AF_INET6);
r = setsockopt(s, IPPROTO_IPV6, IPV6_V6ONLY, &value, sizeof(value));
logmsg(LOG_DEBUG, "setsockopt(%d, IPV6_V6ONLY, true) = %d", s, r);
if (r == -1)
logmsg(LOG_DEBUG, "errno = %d, %s", errno, strerror(errno));
}
#endif
if (family == AF_INET) {
struct sockaddr_in addr4 = {
.sin_family = AF_INET,
.sin_port = htons(port),
.sin_addr = {INADDR_ANY}
};
r = bind(s, (void *)&addr4, sizeof(addr4));
} else {
struct sockaddr_in6 addr6 = {
.sin6_family = AF_INET6,
.sin6_port = htons(port),
.sin6_addr = in6addr_any
};
r = bind(s, (void *)&addr6, sizeof(addr6));
}
struct sockaddr_in6 addr = {
.sin6_family = AF_INET6,
.sin6_port = htons(port),
.sin6_addr = in6addr_any
};
r = bind(s, (void *)&addr, sizeof(addr));
logmsg(LOG_DEBUG, "bind(%d, port=%d) = %d", s, port, r);
if (r == -1) die();
@@ -560,33 +560,6 @@ server_create(int port, int family)
return s;
}
/* Write a line to a client, returning client if it's still up. */
static struct client *
sendline(struct client *client, int max_line_length, unsigned long *rng)
{
char line[256];
int len = randline(line, max_line_length, rng);
for (;;) {
ssize_t out = write(client->fd, line, len);
logmsg(LOG_DEBUG, "write(%d) = %d", client->fd, (int)out);
if (out == -1) {
if (errno == EINTR) {
continue; /* try again */
} else if (errno == EAGAIN || errno == EWOULDBLOCK) {
return client; /* don't care */
} else {
client_destroy(client);
return 0;
}
} else {
client->bytes_sent += out;
statistics.bytes_sent += out;
return client;
}
}
}
int
main(int argc, char **argv)
{
@@ -595,14 +568,8 @@ main(int argc, char **argv)
config_load(&config, config_file, 1);
int option;
while ((option = getopt(argc, argv, "46d:f:hl:m:p:vV")) != -1) {
while ((option = getopt(argc, argv, "d:f:hl:m:p:vV")) != -1) {
switch (option) {
case '4':
config_set_bind_family(&config, "4", 1);
break;
case '6':
config_set_bind_family(&config, "6", 1);
break;
case 'd':
config_set_delay(&config, optarg, 1);
break;
@@ -624,7 +591,8 @@ main(int argc, char **argv)
config_set_port(&config, optarg, 1);
break;
case 'v':
loglevel++;
if (!loglevel++)
setvbuf(stdout, 0, _IOLBF, 0);
break;
case 'V':
print_version();
@@ -641,9 +609,6 @@ main(int argc, char **argv)
exit(EXIT_FAILURE);
}
/* Set output (log) to line buffered */
setvbuf(stdout, 0, _IOLBF, 0);
/* Log configuration */
config_log(&config);
@@ -661,60 +626,53 @@ main(int argc, char **argv)
if (r == -1)
die();
}
{
struct sigaction sa = {.sa_handler = sigusr1_handler};
int r = sigaction(SIGUSR1, &sa, 0);
if (r == -1)
die();
}
struct fifo fifo[1];
fifo_init(fifo);
struct queue queue[1];
queue_init(queue);
unsigned long rng = epochms();
struct pollvec pollvec[1];
pollvec_init(pollvec);
int server = server_create(config.port, config.bind_family);
unsigned long rng = uepoch();
int server = server_create(config.port);
while (running) {
if (reload) {
/* Configuration reload requested (SIGHUP) */
int oldport = config.port;
int oldfamily = config.bind_family;
config_load(&config, config_file, 0);
config_log(&config);
if (oldport != config.port || oldfamily != config.bind_family) {
if (oldport != config.port) {
close(server);
server = server_create(config.port, config.bind_family);
server = server_create(config.port);
}
reload = 0;
}
if (dumpstats) {
/* print stats requested (SIGUSR1) */
statistics_log_totals(fifo->head);
dumpstats = 0;
}
/* Enqueue the listening socket first */
pollvec_clear(pollvec);
if (queue->length < config.max_clients)
pollvec_push(pollvec, server, POLLIN);
else
pollvec_push(pollvec, -1, 0);
/* Enqueue clients that are due for another message */
int timeout = -1;
long long now = epochms();
while (fifo->head) {
if (fifo->head->send_next <= now) {
struct client *c = fifo_pop(fifo);
if (sendline(c, config.max_line_length, &rng)) {
c->send_next = now + config.delay;
fifo_append(fifo, c);
}
long long now = uepoch();
for (struct client *c = queue->head; c; c = c->next) {
if (c->send_next <= now) {
pollvec_push(pollvec, c->fd, POLLOUT);
} else {
timeout = fifo->head->send_next - now;
timeout = c->send_next - now;
break;
}
}
/* Wait for next event */
struct pollfd fds = {server, POLLIN, 0};
int nfds = fifo->length < config.max_clients;
logmsg(LOG_DEBUG, "poll(%d, %d)", nfds, timeout);
int r = poll(&fds, nfds, timeout);
logmsg(LOG_DEBUG, "poll(%zu, %d)%s", pollvec->fill, timeout,
queue->length >= config.max_clients ? " (no accept)" : "");
int r = poll(pollvec->fds, pollvec->fill, timeout);
logmsg(LOG_DEBUG, "= %d", r);
if (r == -1) {
switch (errno) {
@@ -728,19 +686,18 @@ main(int argc, char **argv)
}
/* Check for new incoming connections */
if (fds.revents & POLLIN) {
if (pollvec->fds[0].revents & POLLIN) {
int fd = accept(server, 0, 0);
logmsg(LOG_DEBUG, "accept() = %d", fd);
statistics.connects++;
if (fd == -1) {
const char *msg = strerror(errno);
switch (errno) {
case EMFILE:
case ENFILE:
config.max_clients = fifo->length;
config.max_clients = queue->length;
logmsg(LOG_INFO,
"MaxClients %d",
fifo->length);
queue->length);
break;
case ECONNABORTED:
case EINTR:
@@ -754,22 +711,59 @@ main(int argc, char **argv)
exit(EXIT_FAILURE);
}
} else {
long long send_next = epochms() + config.delay;
long long send_next = uepoch() + config.delay / 2;
struct client *client = client_new(fd, send_next);
int flags = fcntl(fd, F_GETFL, 0); /* cannot fail */
fcntl(fd, F_SETFL, flags | O_NONBLOCK); /* cannot fail */
if (!client) {
fprintf(stderr, "endlessh: warning: out of memory\n");
close(fd);
}
fifo_append(fifo, client);
queue_append(queue, client);
logmsg(LOG_INFO, "ACCEPT host=%s port=%d fd=%d n=%d/%d",
client->ipaddr, client->port, client->fd,
fifo->length, config.max_clients);
queue->length, config.max_clients);
}
}
/* Write lines to ready clients */
for (size_t i = 1; i < pollvec->fill; i++) {
short fd = pollvec->fds[i].fd;
short revents = pollvec->fds[i].revents;
struct client *client = queue_remove(queue, fd);
if (revents & POLLHUP) {
client_destroy(client);
} else if (revents & POLLOUT) {
char line[256];
int len;
if (!(client->flags & FLAG_SPECIAL_SENT)) {
static const char special[] = SPECIAL_MESSAGE;
len = sizeof(special) - 1;
memcpy(line, special, len);
client->flags |= FLAG_SPECIAL_SENT;
} else {
len = randline(line, config.max_line_length, &rng);
}
for (;;) {
/* Don't really care if send is short */
ssize_t out = send(fd, line, len, MSG_DONTWAIT);
if (out == -1 && errno == EINTR) {
continue; /* try again */
} else if (out == -1) {
client_destroy(client);
break;
} else {
logmsg(LOG_DEBUG, "send(%d) = %d", fd, (int)out);
client->bytes_sent += out;
client->send_next = uepoch() + config.delay;
queue_append(queue, client);
break;
}
}
}
}
}
fifo_destroy(fifo);
statistics_log_totals(0);
pollvec_free(pollvec);
queue_destroy(queue);
}

View File

@@ -1,9 +0,0 @@
#!/bin/ksh
#
daemon="/usr/local/bin/endlessh"
rc_bg=YES
. /etc/rc.d/rc.subr
rc_cmd $1

View File

@@ -1,25 +0,0 @@
Solaris SMF installation
========================
Before installing SMF:
1. Put endlessh binary to /usr/local/bin
2. Edit endlessh.conf and put it to /usr/local/etc
To install SMF:
1. Put endlessh.xml to /var/svc/manifest/network
2. Run svccfg import endlessh.xml
3. Put init.endlessh to /lib/svc/method
4. Run svcadm enable endlessh
Note: Log will write to /var/log/endlessh.log by default.
To uninstall SMF:
1. Run svcadm disable endlessh
2. rm -f /lib/svc/method/init.endlessh
3. svccfg delete svc:/network/endlessh:default
4. rm -f /var/svc/manifest/network/endlessh.xml
Enjoy! :)

View File

@@ -1,27 +0,0 @@
# The port on which to listen for new SSH connections.
Port 22
# The endless banner is sent one line at a time. This is the delay
# in milliseconds between individual lines.
Delay 10000
# The length of each line is randomized. This controls the maximum
# length of each line. Shorter lines may keep clients on for longer if
# they give up after a certain number of bytes.
MaxLineLength 32
# Maximum number of connections to accept at a time. Connections beyond
# this are not immediately rejected, but will wait in the queue.
MaxClients 4096
# Set the detail level for the log.
# 0 = Quiet
# 1 = Standard, useful log messages
# 2 = Very noisy debugging information
LogLevel 1
# Set the family of the listening socket
# 0 = Use IPv4 Mapped IPv6 (Both v4 and v6, default)
# 4 = Use IPv4 only
# 6 = Use IPv6 only
BindFamily 0

View File

@@ -1,96 +0,0 @@
<?xml version="1.0"?>
<!DOCTYPE service_bundle SYSTEM "/usr/share/lib/xml/dtd/service_bundle.dtd.1">
<!-- Manifest-file for endlessh, put this file in
/var/svc/manifest/network/endlessh.xml
and run #svccfg import /var/svc/manifest/network/endlessh.xml
Fixed by Yuri Voinov (C) 2007,2019 -->
<service_bundle type='manifest' name='endlessh'>
<service
name='network/endlessh'
type='service'
version='1'>
<create_default_instance enabled='false' />
<single_instance />
<dependency name='fs-local'
grouping='require_all'
restart_on='none'
type='service'>
<service_fmri
value='svc:/system/filesystem/local' />
</dependency>
<dependency name='net-loopback'
grouping='require_all'
restart_on='none'
type='service'>
<service_fmri value='svc:/network/loopback' />
</dependency>
<dependency name='net-physical'
grouping='require_all'
restart_on='none'
type='service'>
<service_fmri value='svc:/network/physical' />
</dependency>
<dependency name='utmp'
grouping='require_all'
restart_on='none'
type='service'>
<service_fmri value='svc:/system/utmp' />
</dependency>
<dependency name='endlessh_config_data'
grouping='require_all'
restart_on='refresh'
type='path'>
<service_fmri value='file://localhost/usr/local/etc/endlessh.conf' />
</dependency>
<exec_method
type='method'
name='start'
exec='/lib/svc/method/init.endlessh %m'
timeout_seconds='60'/>
<exec_method
type='method'
name='stop'
exec=':kill'
timeout_seconds='60' />
<exec_method
type='method'
name='restart'
exec='/lib/svc/method/init.endlessh %m'
timeout_seconds='60' />
<exec_method
type='method'
name='refresh'
exec='/lib/svc/method/init.endlessh %m'
timeout_seconds='60' />
<property_group name='general' type='framework'>
<!-- to start stop endlessh -->
<propval name='action_authorization' type='astring'
value='solaris.smf.manage' />
</property_group>
<stability value='Unstable' />
<template>
<common_name>
<loctext xml:lang='C'>
endlessh service
</loctext>
</common_name>
</template>
</service>
</service_bundle>

View File

@@ -1,137 +0,0 @@
#!/sbin/sh
#
# Control Method for endlessh (/lib/svc/method/init.endlessh)
# Written by Yuri Voinov (C) 2007,2019
#
# ident "@(#)endlessh.sh 1.8 19/27/03 YV"
#
#############
# Variables #
#############
# Base installation directory
BASE_DIR="/usr/local"
BASE_CONFIG_DIR=$BASE_DIR"/etc"
# endlessh files paths
ENDLESSH_PATH="$BASE_DIR""/bin"
ENDLESSH_CONF_PATH="$BASE_CONFIG_DIR"
# endlessh files
ENDLESSH_BIN_FILE="endlessh"
ENDLESSH_CONF_FILE=$ENDLESSH_BIN_FILE".conf"
# Daemon settings
ENDLESSH_CONF="$ENDLESSH_CONF_PATH/$ENDLESSH_CONF_FILE"
# Log
LOG_DIR="/var/log"
LOGFILE=$LOG_DIR/$ENDLESSH_BIN_FILE".log"
#
# OS Commands location variables
#
CUT=`which cut`
ECHO=`which echo`
KILL=`which kill`
PGREP=`which pgrep`
UNAME=`which uname`
# OS release
OS_VER=`$UNAME -r|$CUT -f2 -d"."`
OS_NAME=`$UNAME -s|$CUT -f1 -d" "`
###############
# Subroutines #
###############
check_endlessh ()
{
# Check endlessh installed
program=$1
if [ ! -f "$ENDLESSH_PATH/$program" -a ! -x "$ENDLESSH_PATH/$program" ]; then
$ECHO "ERROR: endlessh not found!"
$ECHO "Exiting..."
exit 1
fi
}
check_os ()
{
# Check OS version
if [ ! "$OS_NAME" = "SunOS" -a ! "$OS_VER" -lt "10" ]; then
$ECHO "ERROR: Unsupported OS $OS_NAME $OS_VER"
$ECHO "Exiting..."
exit 1
fi
}
checkconf ()
{
# Check endlessh config file
config=$1
if [ -f "$ENDLESSH_CONF_PATH"/"$config" ]; then
$ECHO "1"
else
$ECHO "0"
fi
}
startproc()
{
# Start endlessh daemon
program=$1
if [ "`checkconf $ENDLESSH_CONF_FILE`" != "1" ]; then
$ECHO "ERROR: Config file $ENDLESSH_CONF_PATH/$ENDLESSH_CONF_FILE not found."
$ECHO "Exiting..."
exit 2
else
$ENDLESSH_PATH/$program -f $ENDLESSH_CONF_PATH/$ENDLESSH_CONF_FILE -v >$LOGFILE &
fi
}
stopproc()
{
# Stop endlessh daemon
program=$1
if [ "`checkconf $ENDLESSH_CONF_FILE`" != "1" ]; then
$ECHO "ERROR: Config file $ENDLESSH_CONF_PATH/$ENDLESSH_CONF_FILE not found."
$ECHO "Exiting..."
exit 2
else
$KILL -s TERM `$PGREP $program`>/dev/null 2>&1
fi
}
##############
# Main block #
##############
# Check endlessh installed
check_endlessh $ENDLESSH_BIN_FILE
# Check OS version
check_os
case "$1" in
"start")
startproc $ENDLESSH_BIN_FILE
;;
"stop")
stopproc $ENDLESSH_BIN_FILE
;;
"refresh")
$KILL -s HUP `$PGREP $ENDLESSH_BIN_FILE`>/dev/null 2>&1
;;
"restart")
stopproc $ENDLESSH_BIN_FILE
startproc $ENDLESSH_BIN_FILE
;;
*)
$ECHO "Usage: $0 { start | stop | restart | refresh }"
exit 1
esac
exit 0