diff options
| author | daniel <daniel@planethacker.net> | 2025-05-07 09:45:50 -0700 |
|---|---|---|
| committer | daniel <daniel@planethacker.net> | 2025-05-07 09:45:50 -0700 |
| commit | eeac69b2168c5a65f9608771006ccc43033cbd23 (patch) | |
| tree | 1dc44a6016b607085a691768810d551045df9901 /loki.c | |
Diffstat (limited to 'loki.c')
| -rw-r--r-- | loki.c | 362 |
1 files changed, 362 insertions, 0 deletions
@@ -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 */ |
