home *** CD-ROM | disk | FTP | other *** search
- /* Nathan Laredo - "Green" - gt7080a@prism.gatech.edu */
- /* mini-client, semi-raw input, formatted output */
- /* supports none of pre-2.7.2 protocol in formatting */
- /* the documentation takes up lots of the space here */
-
- /* modified Apr 21, 1993 "xxx". Changed to a special- */
- /* purpose encryption program */
-
- #include <stdio.h>
- #ifdef pyr
- #include <strings.h>
- #else
- #include <string.h>
- #endif
- #include <errno.h>
- #include <sys/types.h>
- #include <sys/time.h>
- #include <sys/socket.h>
- #include <netinet/in.h>
- #include <netdb.h>
- #include "sock.h"
-
- char *encode(),*decode();
- char *en_crypt(),*de_crypt(); /* external, in crypt.c */
-
- #define KEYLEN 24+1
- #define MAXKEYS 30
- #define HUGE 1024
- #define SECKEY "secret" /* secret key file, should be a variable */
-
- char keys[MAXKEYS][KEYLEN]; /* keys */
- unsigned int sers[MAXKEYS]; /* serial numbers */
-
- int s,d; /* IRC socket, DCC socket */
- char buf[512]; /* global text data buffer */
- char curchan[256]; /* current active channel */
- char localhost[64]; /* the local machine's name */
- int dcchost,dccsock; /* for implementing DCC */
- char dccbuf[2048]; /* buffer for incomming */
- char dccname[512]; /* filename for dcc transfer */
- unsigned long int dcclength; /* dcc reply/check */
- char inputbuf[512]; /* buffer for user input */
- char IRCNAME[32]; /* storage for current nick */
- fd_set readfs, orig;
- int sok=1; /* socket ok flag */
-
- char *token[1024]; /* worst case: 1 2 3 4 5 .. etc 512 chars */
-
- /* casecmp(a,b)
- a,b - null terminated strings.
- does a non-case sensitive compare
- */
- #define To_lower(a) (isupper(a)?tolower(a):(a))
-
- casecmp(a,b)
- char *a,*b;
- {
- while(*a && *b)
- if(To_lower(*a) != To_lower(*b))
- return (*b-*a); /* doesnt really matter if they are diff cases here*/
- else {
- a++,b++;
- }
- return(0);
- }
-
- /* asctobin(str,len)
- str - ascii string (null terminated)
- len - int *, RETURN length of binary block
- returns: char * to binary block data in static storage.
- coding:
- high nybble - 'a'=0 to 'p'=15
- low nybble - 'A'=0 to 'P'=15
- NULL returned for bad encoding.
- */
- char *asctobin(str,len)
- char *str;
- int *len;
- {
- static char buf[HUGE];
- char a,b;
- int i;
-
- for(i=0;;) {
- a=*str++;
- while(a==' '||a=='\n') a=*str++;
- b=*str++;
- if(a=='\0' || b=='\0') {
- *len=i;
- return(buf);
- }
- if (a<'a'||a>'p' || b<'A'||b>'P')
- return(0);
- buf[i++] = ((a-'a')<<4)|(b-'A');
- }
- }
-
- /* bintoasc(str,len)
- str - a pointer to a binary block
- len - length of binary block in bytes
- return - char * to a string that is ascii, null-terminated
- coding -
- high nybble 'a'=0 to 'p'=15
- low nybble 'A'=0 to 'P'=15
- */
- char *bintoasc(str,len)
- int len;
- char *str;
- {
- static char buf[HUGE];
- int i;
-
- for(i=0;len-- >0;str++) {
- buf[i++]=((*str&0xf0)>>4) + 'a';
- buf[i++]=(*str&0xf) + 'A';
- }
- buf[i]='\0';
- return(buf);
- }
-
- /* encode(str)
- str - an ascii null-terminated string
- returned - char * an encoded null terminated ascii string
- encoding:
- CLIPPER:xxxx:yyyyyyyyyyyyy
- xxxx - serial number of key used
- yyyyy- ascii coded, encrypted text message
- */
- char *encode(str)
- char *str;
- {
- int l,ser,a;
- static char buf[HUGE];
- char *p;
-
- set_key(keys[0]); /* use our key and our serial number */
- a=strlen(str)-1;
- if(str[a]=='\n') str[a]='\0';
- str[a++]='\0';
- p=en_crypt(str ,a,&l);
- sprintf(buf,"CLIPPER:%d:",sers[0]);
- strcat(buf,bintoasc(p,l));
- strcat(buf,"\n");
- return(buf);
- }
-
- /* decode(ar,len)
- ar - array of words like argv[]
- len - number of words, like argc
- return - char * to a decoded ascii null-termianted string
- coding: see encode()
- error codes are returned as human readable strings.
-
- CLIPPER:xxxx:yyyyyy
- x - ascii serial number
- y - ascii encoded binary data, crypted
- SKPJACK:xxxx:yyyy:zzzz
- x - nick name of destination
- y - serial number of key being received
- z - ascii encoded binary data, encrypted with rsa
- in 'nick's public key , contains the key
- needed to read messages from nick
- */
- char *decode(ar,len)
- char *ar[];
- int len;
- {
- char *p; /* lots of chars */
- static char buf[HUGE];
- int i,ser,l,a,itsakey=0;
-
- buf[0]='\0';
- for(i=0;i<len;i++) { /* put it into a single string */
- strcat(buf,ar[i]);
- strcat(buf," "); /* spaces seperate tokens */
- }
- if(strncmp(buf,"SKPJACK:",8)==0)
- itsakey=1; /* someones sending a key */
- else if(strncmp(buf,"CLIPPER:",8))
- return(0); /* not encoded */
- for(i=8;buf[i]!=':'&&buf[i]!='\0';i++); /* jump past ser # */
- if(buf[i]!=':') {
- return("*Badly Formed*\n");
- }
- buf[i++]='\0';
- ser=atoi(buf+8); /* this is ser # */
-
- if(itsakey && casecmp(buf+8,IRCNAME)==0) { /* new key sent to us */
- ser=atoi(buf+i);
- for(;buf[i]!=':'&&buf[i]!='\0';i++) ;
- if(buf[i++]!=':') return("*Newkey: badly formed*");
- p=asctobin(buf+i,&len);
- if(!p)
- return("*new key: bad coding*");
- memcpy(buf,p,len); /* copy binary data */
- if(do_rsa(SECKEY,buf,len,HUGE)<0)
- return("*new key: couldnt decrypt (rsa)*");
- for(i=0;i<MAXKEYS;i++)
- if(sers[i]==0 || sers[i]==ser) break;
- if(i==MAXKEYS) return ("*new key: out of table entries*");
- /* *never* receive a key we already have */
- /* this could be a trick */
- if(sers[i]==ser) return("*new key: already have it!*");
- sers[i]= ser;
- memcpy(keys[i],buf,KEYLEN);
- return("*New Key installed*");
- }
- if (itsakey) printf("Saw key for %s\n",buf+8);
- if (itsakey) return("*Key received, but not for us*");
-
- /* else its a message , try to decode */
- a=key(ser); /* find the key */
- if(a==-1) return("*Dont Have the Key*\n");
- set_key(keys[a]);
- p=asctobin(buf+i,&len); /* decrypt it */
- if(!p) return("*Bad Encoding*");
- sprintf(buf,"<E> %s",de_crypt(p,len,&l));
- return(buf);
- }
-
- /* key(ser)
- ser = serial number
- returned - index to the key with serial number ser, else -1
- */
- int key(ser)
- int ser;
- {
- int i;
-
- for(i=0;i<MAXKEYS;i++)
- if(ser == sers[i]) return(i);
- return(-1);
- }
-
- /* sendkey(line)
- line - char *, everything after /key on the command line
- parsed to 'nick' and the optional 'filename'
- filename is set to nick if it doesnt exist.
- encodes our key and serial number with nick's public
- key and sends it over the current channel for him
- to receive
- */
- sendkey(line) /* handle /key nick [filename] */
- char *line;
- {
- char *file,*nick,*p;
- char buf[1024];
- int len;
-
- while(*line==' ') line++;
- nick=line;
- while(*line!=' '&&*line!='\0'&&*line!='\n') line++;
- if(*line=='\n') *line='\0';
- if(*line=='\0')
- file=nick;
- else {
- *line++='\0';
- file=line;
- while(*line!=' '&&*line!='\0'&&*line!='\n') line++;
- *line='\0';
- }
- if(*nick=='\0') {
- printf("*ERROR* nick missing, /key nick [file]");
- return;
- }
-
- memcpy(buf,keys[0],KEYLEN);
- len=do_rsa(file,buf,KEYLEN,1024);
- if(len<0) {
- printf("*ERROR* dont have public key for %s\n",file);
- return; /* couldnt send it, RSA failed */
- }
- p=bintoasc(buf,len);
- sprintf(buf,"PRIVMSG %s SKPJACK:%s:%d:%s\n",
- curchan,nick,sers[0],p);
- writeln(buf); /* send it to irc */
- }
-
-
- int call_socket(hostname)
- char *hostname;
- {
- struct sockaddr_in sa;
- struct hostent *hp;
- int a, s;
-
- bzero(&sa, sizeof(sa));
- sa.sin_family = AF_INET;
- sa.sin_addr.s_addr = inet_addr(hostname);
- if (sa.sin_addr.s_addr ==-1) {
- if ((hp=gethostbyname(hostname))==NULL) {
- errno=ECONNREFUSED;
- return(-1);
- }
- sa.sin_family = hp->h_addrtype;
- bcopy(hp->h_addr, (char *)&sa.sin_addr, hp->h_length);
- }
- sa.sin_port = htons((u_short)DEFAULTPORT);
-
- if((s=socket(sa.sin_family, SOCK_STREAM, 0)) < 0)
- return(-1);
- if(connect(s, &sa, sizeof(sa)) < 0) {
- close(s);
- return(-1);
- }
- return(s);
- }
-
- int dcc_socket(host,sock)
- unsigned long int host;
- int sock;
- {
- struct sockaddr_in sa;
- int a, d;
-
- bzero(&sa, sizeof(sa));
- bcopy(&host, (char *)&sa.sin_addr, sizeof(host));
- sa.sin_family = AF_INET;
- sa.sin_port = htons((u_short)sock);
-
- if((d=socket(PF_INET, SOCK_STREAM, 0)) < 0)
- return(-1);
- if(connect(s, &sa, sizeof(sa)) < 0) {
- close(d);
- return(-1);
- }
- return(d);
- }
-
-
- int readln(buf)
- char *buf;
- {
- int to=0;
- char c;
- do { /* will never overflow 'cause
- server can't send more than 512 bytes */
- if(read(s, &c, 1)<1) return(0);
- buf[to++] = c;
- } while (c != '\n');
- buf[to-1] = '\0';
- return(1);
- }
-
- int writeln(buf)
- char *buf;
- {
- int to=0;
- if( write(s, buf, strlen(buf)) < to )
- return(0);
- return(1);
- }
-
- int dcc_getblock(so,fi)
- char *so,*fi;
- { char r;
- if (r=read(so, dccbuf, 2048)) {
- dcclength += r;
- printf("[%08x]",dcclength);
- write(so, htons((unsigned long int) dcclength), sizeof(dcclength));
- write(fi, dccbuf, r);
- return(1);
- } /* if block is still there */
- close(fi); close(so);
- printf("DCC successful!\n");
- return (0); /* done */
- }
-
- dojoin() /* had to separate because the language is dumb */
- {
- if(strcmp(token[0],IRCNAME)==0) {
- printf("*** Current channel is now %s",token[2]);
- strcpy(curchan,token[2]);
- } /* case change current channel (nick=ircnick) */
- else printf("*** %s has joined channel %s",token[0],token[2]);
- } /* end of dojoin */
-
- dopart() /* see above */
- {
- if(strcmp(token[0],IRCNAME)==0) {
- if(strcmp(curchan,token[2])==0) { /* yur leaving your curent channel */
- printf("*** Current channel is now invalid until you use join");
- /* you could probably implement a get last channel in if you wanted */
- strcpy(curchan,"=invalid"); /* literally :-) */
- } /* case invalidate current channel */
- } /* damn I hate this */
- else printf("*** %s has left channel %s",token[0],token[2]);
- } /* end of part garbage */
-
- donick()
- {
- if(strcmp(token[0],IRCNAME)==0) { strcpy(IRCNAME,token[2]);
- printf("*** You have changed your nickname to %s", token[2]);
- } /* if you're doing this to yourself */
- else printf("*** %s is now known as %s",token[0],token[2]);
- } /* I hate this language - if only it could read my mind */
-
- doprivmsg(tokencount)
- int tokencount;
- { int i;
- char *p;
-
- if(*(++token[3])=='\01') /* ctcp reply */
- printf("*** CTCP MESSAGE FROM %s: ",token[0]);
- else {
- printf("<%s-%s> ",token[0],token[2]);
- /* decrypt here */
- p=decode(token+3,tokencount-3);
- if(p) { /* if not encoded drop through */
- printf("%s",p);
- return;
- }
- }
- for(i=3;i<tokencount; i++) printf("%s ",token[i]);
-
- /* DO CTCP GOES HERE (INCLUDES DCC) */
- } /* privmsg */
-
- donotice(tokencount)
- int tokencount;
- { int i;
- if(*(++token[3])=='\01') /* ctcp reply */
- printf("*** CTCP REPLY FROM %s: ",token[0]);
- /* if there's a . in nick we KNOW it's not a user */
- else if (strchr(token[0],'.')==0) printf("-%s- ",token[0]);
- for(i=3;i<tokencount; i++) printf("%s ",token[i]);
- } /* notice */
-
- int spitout(servstr) /* filter line to make more pleasing and spit out */
- char *servstr;
- { int i;
- char *temp;
- int tokencount=0;
- if (strncmp(servstr,"PING",4)==0) { /* make pings/pongs transparent */
- temp=strncpy(servstr,"PO",2);
- return(writeln(strcat(temp,"\n"))); /* needs new line-gone before */
- }
- /* tokenize */
- token[0]=strtok(servstr," "); tokencount++;
- while(token[tokencount++]=strtok(NULL, " "));
- tokencount -= 1; /* need to fix for newline */
- /* each token contains exactly one word, and only one now */
- if(*token[0] != ':') { /* notice message from server usually */
- for(i=0;i<tokencount; i++) printf("%s ",token[i]);
- printf("\n");
- return(0);
- } /* if first char not : */
- else token[0]++; /* point at next char past colon */
- if(temp=strchr(token[0],'!')) *temp='\0'; /* strip address if there */
-
- /* main parsing stuff - follows parse.c in ircII pretty closely */
-
- if(strcmp(token[1],"PRIVMSG")==0) doprivmsg(tokencount);
- else if(strcmp(token[1],"NOTICE")==0) donotice(tokencount);
- else if(strlen(token[1])==3) /* server message, just print */
- for(i=3;i<tokencount; i++) printf("%s ",token[i]);
- else if(strcmp(token[1],"JOIN")==0) dojoin();
- else if(strcmp(token[1],"PART")==0) dopart();
- else if(strcmp(token[1],"QUIT")==0) {
- printf("*** signoff (%s)",token[0]);
- for(i=2;i<tokencount; i++) printf(" %s",token[i]);
- } /* if someone's leaving irc */
- else if(strcmp(token[1],"TOPIC")==0) {
- printf("*** %s has changed the topic on %s to",token[0],token[2]);
- for(i=3;i<tokencount; i++) printf(" %s",token[i]); }
- else if(strcmp(token[1],"INVITE")==0)
- printf("*** You have been invited to join channel %s by %s",token[2],
- token[0]);
- else if(strcmp(token[1],"NICK")==0) donick();
- else if(strcmp(token[1],"KILL")==0) /* Hmmm, never got one, but hell */
- printf("*** %s killed by %s",token[2],token[0]);
- else if(strcmp(token[1],"MODE")==0) /* well, there are mode changes */
- printf("*** Mode change on %s by %s to %s",token[2],token[0],token[3]);
- else if(strcmp(token[1],"KICK")==0)
- printf("*** %s has kicked %s from %s",token[0], token[2], token[3]);
- else if(strncmp(token[1],"ERROR",5)==0) {
- printf("*** ERROR:");
- for(i=2;i<tokencount; i++) printf(" %s",token[i]); }
- else /* if all else fails */
- { printf("***"); for(i=0;i<tokencount; i++) printf(" %s",token[i]); }
- putchar('\n'); /* if you get a blank line at this point this code sucks */
- return(0);
- }
-
- int dottyinput()
- {
- char c;
- int to=0;
- do {
- if(read(1, &c, 1)<1) return(0);
- inputbuf[to++] = c;
- } while (c != '\n');
- inputbuf[to] = '\0';
- if (inputbuf[0]==COMMANDCHAR){
- if(strncmp(inputbuf+1,"key",3)==0)
- sendkey(inputbuf+4);
- else
- writeln(inputbuf+1);
- }
- else {
-
- /* encrypt here */
- sprintf(buf,"PRIVMSG %s %s",curchan,encode(inputbuf));
- writeln(buf);
- } /* no cmd character tried default */
- return(1);
- }
-
- main(argc, argv)
- int argc;
- char **argv;
- {
- char hostname[64];
- char *logfile=NULL;
- int c, errflag;
- extern int optind, opterr;
- extern char *optarg;
- char line[512];
-
- int i;
-
- /* pick random 8 bit key -> K */
- /* encrypt crypt(K,K) -> serial number */
- /* pick random 8 bits L */
- /* encrypt crypt(L,K) -> our DES key */
- srand(time(0));
- for(i=0;i<KEYLEN;i++)
- keys[0][i]= (char)((rand()&0xff00)>>8);
- set_key(keys[0]);
- en_crypt(keys[0],KEYLEN,&i);
- sers[0] = (int) *((int *)keys[0]); /* pick serial number */
- /* if(sers[0]<0) sers[0]=-sers[0]; /* problem with negative ser #'s */
- for(i=0;i<KEYLEN;i++)
- keys[0][i]= (char)((rand()&0xff00)>>8);
- en_crypt(keys[0],KEYLEN,&i);
-
- if(getenv("IRCNICK")==NULL || getenv("LOGNAME")==NULL ||
- getenv("IRCNAME")==NULL) {
- printf("The following settings in your environment are not set properly:\n");
- if (getenv("IRCNICK")==NULL) printf("IRCNICK should be set with a nick\n");
- if (getenv("LOGNAME")==NULL) printf("LOGNAME should contain user id\n");
- if (getenv("IRCNAME")==NULL) printf("IRCNAME should contain real name\n");
- exit(0);
- }
- if(argc>1) { /* assume only one param, hostname */
- if (strchr(argv[1],'.')==0) { /* shouldn't a host have a period? */
- fprintf(stderr,"usage: %s ircservername initialchannel\n", argv[0]);
- exit(0); }
- strcpy(hostname,argv[1]); }
- else strcpy(hostname,DEFAULTSERVER);
- gethostname(localhost, 64);
- if ((s=call_socket(hostname))==-1) {
- fprintf(stderr, "Could not connect to %s, aborting\n", hostname);
- exit(0);
- }
- sprintf(buf, "NICK %s\n", getenv("IRCNICK"));
- writeln(buf);
- sprintf(buf, "USER %s 1 1 %s\n", getenv("LOGNAME"), getenv("IRCNAME"));
- writeln(buf);
- strcpy(curchan,"=invalid");
- strncpy(IRCNAME,getenv("IRCNICK"),sizeof(IRCNAME));
- if(argc>2) /* well we'll call this the channel to join */
- { sprintf(buf, "JOIN %s\n", argv[2]); writeln(buf); }
- if(argc>3) { /* assume you don't know what the hell you want */
- fprintf(stderr,"usage: %s ircservername initialchannel\n", argv[0]);
- exit(0); }
- FD_ZERO(&readfs);
- FD_SET(s,&readfs);
- FD_SET(1,&readfs);
- orig = readfs;
- while(sok) {
- /* notice how when one character is there, we assume a whole line is
- waiting for us to read. This is because we're doing cooked i/o to
- keep resources minimal */
- if(select(FD_SETSIZE, &readfs, NULL, NULL, NULL)) {
- if(FD_ISSET(1,&readfs)) if(!dottyinput()) return(1);
- if(FD_ISSET(s,&readfs)) {
- sok = readln(line);
- if (sok) spitout(line);
- } /* if s */
- } /* if select */
- readfs = orig;
- }
- return(1); /* assume that these files will be properly closed */
- }
-