summaryrefslogtreecommitdiff
path: root/loki.c
diff options
context:
space:
mode:
authordaniel <daniel@planethacker.net>2025-05-07 09:45:50 -0700
committerdaniel <daniel@planethacker.net>2025-05-07 09:45:50 -0700
commiteeac69b2168c5a65f9608771006ccc43033cbd23 (patch)
tree1dc44a6016b607085a691768810d551045df9901 /loki.c
initial commitHEADmain
Diffstat (limited to 'loki.c')
-rw-r--r--loki.c362
1 files changed, 362 insertions, 0 deletions
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 */