/* Copyright (C) 2003 Martin J. Muench */ #include "config.h" #include "chat.h" #include "curses.h" #include "sendicmp.h" #include "cipherwrap.h" #include "wrap.h" static void chat_abort(const char *, ...); extern WINDOW *input, *output; int chat(int rawsock, const char *host, const char *nick, int icmptype, const unsigned char *key) { /* generic stuff */ int icmpseq=4, auth=0, af=0; unsigned char *payload=NULL; unsigned char buf[1024]; char hostnick[16], /* nick of chatpartner */ text[CIPHERBUF-strlen(SID)]; /* plaintext buffer, fuck C89 ;) */ fd_set readset; /* select handle */ struct icmp *icmp=NULL; struct ip *ip=NULL; memset(hostnick, 0, sizeof(hostnick)); wprintw(output, ">>> Connecting to %s...\n", host); wrefresh(output); if(!encipher_send(rawsock, host, icmptype, icmpseq, key, "%s", PING)) { chat_abort("Error in encipher_send()"); return 0; } FD_ZERO(&readset); /* Start main chat */ while(1) { FD_SET(rawsock, &readset); FD_SET(STDIN_FILENO, &readset); if(select(rawsock + 1, &readset, NULL, NULL, NULL) < 0) { chat_abort("Select failed: %s", strerror(errno)); return 0; } /* Check for data on rawsock handle */ if (FD_ISSET(rawsock, &readset)) { memset(buf, 0, sizeof(buf)); /* no cast for af, some systems do not have socklen_t */ if (recvfrom(rawsock,buf,sizeof(buf)-1,0,(struct sockaddr *)&saddr,&af) < 0) { chat_abort("Recvfrom failed: %s", strerror(errno)); return 0; } ip=(struct ip *)buf; icmp=(struct icmp *)(buf + sizeof(struct ip)); if(ip == NULL || icmp == NULL) continue; /* * If packet has our icmpseq it is probably a kernel reply * to one of our *_request packets and should be ignored */ if(auth > 0) { if(icmp->icmp_seq == icmpseq) continue; } #ifdef DEBUG debug("Received valid packet from '%s' with icmp_type %d", inet_ntoa(ip->ip_src), icmp->icmp_type); #endif payload = buf + sizeof(struct ip) + sizeof(struct icmp); if(payload == NULL) continue; if(!decipher_string(key, (unsigned char *)text, payload)) continue; /* Received authentication request */ if(!strcmp(text,PING) && (!auth || auth == 1)) { #ifdef DEBUG debug("Received authentication request from '%s'", inet_ntoa(ip->ip_src)); #endif /* Send authentication reply*/ icmpseq=5; if(!encipher_send(rawsock, host, icmptype, icmpseq, key, "%s", PONG)) { chat_abort("encipher_send() failed."); return 0; } auth=1; if(!encipher_send(rawsock, host, icmptype, icmpseq, key, "/hnick %s", nick)) { chat_abort("encipher_send() failed."); return 0; } } /* Received authentication reply */ else if(!strcmp(text,PONG) && (!auth || auth==1) && icmp->icmp_seq != icmpseq) { #ifdef DEBUG debug("Received authentication reply from '%s'", inet_ntoa(ip->ip_src)); #endif if (!auth) { icmpseq=4; if(!encipher_send(rawsock, host, icmptype, icmpseq, key, "%s", PONG)) { chat_abort("encipher_send() failed."); return 0; } if(!encipher_send(rawsock, host, icmptype, icmpseq, key, "/hnick %s", nick)) { chat_abort("encipher_send() failed."); return 0; } } auth=2; } /* Received special packet (leading '/') */ else if(text[0] == '/') { if(!strncmp(text, "/quit", strlen("/quit"))) { wprintw(output, ">>> %s has left the chat\n", hostnick); wrefresh(output); /* reset authentication indicator */ auth=0; } /* Other side set nickname (first time) */ else if(!strncmp(text, "/hnick", strlen("/hnick"))) { memset(hostnick, 0, sizeof(hostnick)); strncpy(hostnick, text + strlen("/hnick "), sizeof(hostnick)-1); wprintw(output, ">>> %s is connected\n", hostnick); wrefresh(output); } /* Other side changed nickname */ else if(!strncmp(text, "/setname", strlen("/setname"))) { wprintw(output, ">>> %s is now known as %s\n", hostnick, text+strlen("/setname ")); wrefresh(output); memset(hostnick, 0, sizeof(hostnick)); strncpy(hostnick, text + strlen("/setname "), sizeof(hostnick)-1); } } /* normal text, print */ else if (auth) { wprintw(output, "::: %s : %s\n", hostnick, text); wrefresh(output); } } /* Check for data on stdin */ else if (FD_ISSET(STDIN_FILENO, &readset)) { memset(text, 0, sizeof(text)); if(wgetnstr(input, text, sizeof(text)-1) == ERR) { chat_abort("wgetnstr failed: %s\n", strerror(errno)); return 0; } /* command detection */ if(text[0] == '/') { /* user requests exit */ if(!strncmp(text, "/quit", strlen("/quit"))) { if(!encipher_send(rawsock, host, icmptype, icmpseq, key, "/quit")) { chat_abort("encipher_send() failed."); return 0; } return 1; } /* user changes nickname */ else if(!strncmp(text, "/setname", strlen("/setname"))) { if(!encipher_send(rawsock, host, icmptype, icmpseq, key, "%s", text)) { chat_abort("encipher_send() failed."); return 0; } } /* change of icmp type */ else if(!strncmp(text, "/setcode", strlen("/setcode"))) { icmptype=atoi(text+strlen("/setcode ")); wprintw(output, ">>> You are now using icmp_type %d\n", icmptype); wrefresh(output); } /* display help screen */ else if(!strncmp(text, "/help", strlen("/help"))) { wprintw(output, "%s", HELP); wrefresh(output); } else { wprintw(output, ">>> Unknown command: %s\n", text); wrefresh(output); } } /* Normal text, send to host */ else { wprintw(output, "::: %s : %s\n", nick, text); wrefresh(output); if(!encipher_send(rawsock, host, icmptype, icmpseq, key, "%s", text)) { chat_abort("encipher_send() failed."); return 0; } } /* Clear Input */ werase(input); wmove(input, 0, 0); wrefresh(input); } /* FD_ISSET */ } /* while(1) */ return 1; } static void chat_abort(const char *fmt, ...) { char buffer[512]; va_list ap; end_curses(); memset(buffer, 0, sizeof(buffer)); va_start(ap, fmt); vsnprintf(buffer, sizeof(buffer)-1, fmt, ap); va_end(ap); fprintf(stderr, ">>> %s\n", buffer); }