1#include <u.h>2#include <libc.h>3#include <auth.h>4#include <libsec.h>56char *post;7char *file;8int ircfd = -1; // the irc server9int logfd;10int enctls = 0; // ssl/tls11int port = 6667;12QLock lck;13char *channels[256];14int tchans;15Thumbprint *thumb;1617char *ccert;18char *server;19char *passwd;20char *nickname;21char *realname;22char *username;23char *mode = "foo";24char *unused = "bar";2526void ircsrv(void);27void logger(void);28void die(void*, char*);29void reconnect(void);30void joinhandler(char*);3132void33usage(void)34{35 fprint(2, "usage: %s [-e] [-t thumb] [-c cert] [-p port] [-s service] [-f file] [-P pass] [-n nickname] [-r realname ] server\n", argv0);36 exits("usage");37}3839void40killall(void)41{42 postnote(PNGROUP, getpid(), "quit");43 while(waitpid() != -1)44 ;45 remove(post);46 exits(nil);47}4849void50die(void *, char *)51{52 killall();53}5455void56main(int argc, char *argv[])57{58 char *tmp, *tfp = nil;59 int p[2], fd;6061 ARGBEGIN{62 case 'c':63 ccert = EARGF(usage());64 break;65 case 't':66 tfp = EARGF(usage());67 break;68 case 'p':69 port = atoi(EARGF(usage()));70 break;71 case 'f':72 file = EARGF(usage());73 break;74 case 's':75 post = EARGF(usage());76 break;77 case 'r':78 realname = EARGF(usage());79 break;80 case 'e':81 enctls = 1;82 break;83 case 'n':84 nickname = strdup(EARGF(usage()));85 break;86 case 'P':87 passwd = EARGF(usage());88 /* try to obfuscate the password so ps -a won't see it */89 tmp = passwd;90 passwd = smprint("%s", tmp);91 if(passwd)92 memset(tmp, '\0', strlen(tmp));93 else94 passwd = tmp;95 break;96 default:97 usage();98 }ARGEND;99100 if(argc < 1)101 usage();102103 server = argv[0];104 username = getuser();105 if(nickname == nil)106 nickname = strdup(username);107108 if(tfp != nil) {109 thumb = initThumbprints(tfp, nil);110 if(thumb == nil)111 sysfatal("initThumbprints: %r");112 }113114 if(post == nil)115 post = smprint("/srv/%sirc", username);116 else117 post = smprint("/srv/%s", post);118119 if(file == nil)120 file = smprint("/tmp/%sirc", username);121122 if((logfd = create(file, OWRITE, 0600 | DMAPPEND)) < 0)123 sysfatal("create(%s): %r", file);124125 if((fd = create(post, OWRITE, 0600)) < 0)126 sysfatal("create(%s): %r", post);127 if(pipe(p) == -1)128 sysfatal("pipe: %r");129 fprint(fd, "%d", p[1]);130 close(fd);131 close(p[1]);132 close(0);133 close(1);134 close(2);135 dup(p[0], 0);136137 if(rfork(RFMEM|RFFDG|RFREND|RFPROC|RFNOTEG|RFCENVG|RFNOWAIT) == 0) {138 notify(die);139 reconnect();140 switch(rfork(RFPROC|RFMEM)){141 case -1:142 sysfatal("rfork: %r");143 case 0:144 notify(die);145 logger();146 break;147 default:148 ircsrv();149 break;150 }151 }152 exits(nil);153}154155long156readln(int fd, void *vp, long len)157{158 char *b = vp;159 while(len > 0 && read(fd, b, 1) > 0){160 if(*b++ == '\n')161 break;162 len--;163 }164 return b - (char*)vp;165}166167void168reregister(void)169{170 int n;171 char nbuf[32];172173 strncpy(nbuf, nickname, sizeof(nbuf) - 2);174 switch(nbuf[strlen(nbuf) - 1]) {175 case '0':176 case '1':177 case '2':178 case '3':179 case '4':180 case '5':181 case '6':182 case '7':183 case '8':184 nbuf[strlen(nbuf) - 1]++;185 break;186 case '9':187 qlock(&lck);188 fprint(logfd, "can not register nick, bailing out\n");189 qunlock(&lck);190 die(nil, nil);191 default:192 n = strlen(nbuf);193 nbuf[n] = '0';194 nbuf[n+1] = '\0';195 break;196 }197 qlock(&lck);198 fprint(ircfd, "NICK %s\r\n", nbuf);199 fprint(logfd, "NICK %s\r\n", nickname);200 qunlock(&lck);201}202203void204reconnect(void)205{206 char addr[128];207 uchar hash[SHA1dlen];208 TLSconn conn;209 int i;210211 snprint(addr, sizeof(addr), "tcp!%s!%d", server, port);212213 if(ircfd >= 0)214 close(ircfd);215 if((ircfd = dial(addr, nil, nil, nil)) < 0)216 sysfatal("dial: %r");217 if(enctls > 0) {218 memset(&conn, 0, sizeof(conn));219 conn.serverName = server;220 if(ccert != nil) {221 conn.cert = readcert(ccert, &conn.certlen);222 if(conn.cert == nil)223 sysfatal("readcert: %r");224 }225226 ircfd = tlsClient(ircfd, &conn);227 if(ircfd < 0)228 sysfatal("tls: %r");229230 if(thumb) {231 if(conn.cert == nil || conn.certlen <= 0)232 sysfatal("server did not provide TLS certificate");233 sha1(conn.cert, conn.certlen, hash, nil);234 if(!okThumbprint(hash, thumb))235 sysfatal("server certificate not recognized");236 }237238 free(conn.cert);239 free(conn.sessionID);240 }241 qlock(&lck);242 if(passwd && strcmp(passwd, ""))243 fprint(ircfd, "PASS %s\r\n", passwd);244 fprint(ircfd, "USER %s %s %s :%s\r\n",245 username, mode, unused, realname);246 fprint(ircfd, "NICK %s\r\n", nickname);247 for(i = 0; i < tchans; i++)248 fprint(ircfd, "JOIN %s\r\n", channels[i]);249 qunlock(&lck);250}251252253void254logger(void)255{256 char buf[513];257 char *f[3];258 long n;259260 for(;;){261 while((n = readln(ircfd, buf, sizeof(buf)-1)) > 0){262 write(logfd, buf, n);263 buf[n] = 0;264 n = tokenize(buf, f, nelem(f));265 if(n == 3 && *f[0] == ':' && !cistrcmp(f[1], "PING")){266 qlock(&lck);267 fprint(ircfd, "PONG %s\r\n", f[2]);268 fprint(logfd, "PONG %s\r\n", f[2]);269 qunlock(&lck);270 } else if(n == 2 && !cistrcmp(f[0], "PING")){271 qlock(&lck);272 fprint(ircfd, "PONG %s\r\n", f[1]);273 fprint(logfd, "PONG %s\r\n", f[1]);274 qunlock(&lck);275 } else if(n == 3 && atoi(f[1]) == 433) {276 reregister();277 }278 }279 reconnect();280 }281}282283void284ircsrv(void)285{286 char buf[512];287 long n;288289 while((n = readln(0, buf, sizeof(buf)-1)) > 0){290 joinhandler(buf);291 qlock(&lck);292 if(write(logfd, buf, n) != n)293 fprint(2, "write to irclog: %r\n");294 if(write(ircfd, buf, n) != n)295 fprint(2, "write to ircserver: %r\n");296 qunlock(&lck);297 }298 killall();299}300301void302joinhandler(char *buf)303{304 char *toks[4];305 char *crap;306307 if(strlen(buf) > 4){308 crap = strdup(buf);309 crap[strlen(crap)-2] = '\0';310 if(tokenize(crap, toks, 4) >= 2)311 if(strncmp(crap, "JOIN", 4) == 0)312 if(tchans < 256){313 channels[tchans] = strdup(toks[1]);314 tchans++;315 }316 free(crap);317 }318}