#include <u.h>#include <libc.h>#include <auth.h>#include <libsec.h> char *post;char *file;int ircfd = -1; // the irc serverint logfd;int debugfd = -1;int enctls = 0; // ssl/tlsQLock lck;char *channels[256];int tchans;int rconn; char *server;char *passwd;char *nspasswd;char *nickname;char *realname;char *username;char *mode = "foo";char *unused = "bar"; void ircsrv(void);void logger(void);void die(void*, char*);void reconnect(void);void joinhandler(char*); voidusage(void){ fprint(2, "usage: %s [-e] [-s service] [-f file] [-p pass] [-P nickserv password] [-n nickname] [-D debug log] [net!]ircserver[!port]\n", argv0); exits("usage");} voidkillall(void){ postnote(PNGROUP, getpid(), "quit"); while(waitpid() != -1) ; remove(post); exits(nil);} voiddie(void *, char *){ killall();} voidmain(int argc, char *argv[]){ char *tmp; char *debugfn; int p[2], fd; rconn = 0; ARGBEGIN{ case 'f': file = EARGF(usage()); break; case 's': post = EARGF(usage()); break; case 'r': realname = EARGF(usage()); break; case 'e': enctls = 1; break; case 'P': /* do whats stated below. Don't want your passwords to get out there! */ tmp = EARGF(usage()); nspasswd = smprint("%s", tmp); if(nspasswd) memset(tmp, '\0', strlen(tmp)); else nspasswd = tmp; break; case 'n': nickname = strdup(EARGF(usage())); break; case 'p': passwd = EARGF(usage()); /* try to obfuscate the password so ps -a won't see it */ tmp = passwd; passwd = smprint("%s", tmp); if(passwd) memset(tmp, '\0', strlen(tmp)); else passwd = tmp; break; case 'D': debugfn = EARGF(usage()); debugfd = create(debugfn, OWRITE, 0600 | DMAPPEND); if(debugfd < 0) sysfatal("create(%s): %r", debugfn); break; default: usage(); }ARGEND; if(argc < 1) usage(); server = argv[0]; username = getuser(); if(nickname == nil) nickname = strdup(username); if(debugfd == -1){ debugfd = open("/dev/null", OWRITE); if(debugfd < 0) sysfatal("open(/dev/null): %r"); } if(post == nil) post = smprint("/srv/%sirc", username); else post = smprint("/srv/%s", post); if(file == nil) file = smprint("/tmp/%sirc", username); if((logfd = create(file, OWRITE, 0600 | DMAPPEND)) < 0) sysfatal("create(%s): %r", file); if((fd = create(post, OWRITE, 0600)) < 0) sysfatal("create(%s): %r", post); if(pipe(p) == -1) sysfatal("pipe: %r"); fprint(fd, "%d", p[1]); close(fd); close(p[1]); close(0); close(1); close(2); dup(p[0], 0); if(rfork(RFMEM|RFFDG|RFREND|RFPROC|RFNOTEG|RFCENVG|RFNOWAIT) == 0) { notify(die); reconnect(); switch(rfork(RFPROC|RFMEM)){ case -1: sysfatal("rfork: %r"); case 0: notify(die); logger(); break; default: ircsrv(); break; } } exits(nil);} longreadln(int fd, void *vp, long len){ char *b = vp; while(len > 0 && read(fd, b, 1) > 0){ if(*b++ == '\n') break; len--; } return b - (char*)vp;} voidreregister(void){ int n; char nbuf[32]; strncpy(nbuf, nickname, sizeof(nbuf) - 2); switch(nbuf[strlen(nbuf) - 1]) { case '0': case '1': case '2': case '3': case '4': case '5': case '6': case '7': case '8': nbuf[strlen(nbuf) - 1]++; break; case '9': qlock(&lck); fprint(logfd, "can not register nick, bailing out\n"); qunlock(&lck); die(nil, nil); default: n = strlen(nbuf); nbuf[n] = '0'; nbuf[n+1] = '\0'; break; } qlock(&lck); fprint(ircfd, "NICK %s\r\n", nbuf); fprint(logfd, "NICK %s\r\n", nickname); qunlock(&lck);} voidreconnect(void){ TLSconn *conn; int i; if(ircfd >= 0) close(ircfd); if((ircfd = dial(netmkaddr(server, "tcp", "6667"), nil, nil, nil)) < 0) sysfatal("dial %r"); if(enctls > 0) { conn = (TLSconn *)mallocz(sizeof *conn, 1); ircfd = tlsClient(ircfd, conn); if (ircfd < 0) { sysfatal ("tls: %r"); } } qlock(&lck); if(passwd && strcmp(passwd, "")) fprint(ircfd, "PASS %s\r\n", passwd); fprint(ircfd, "USER %s %s %s :%s\r\n", username, mode, unused, realname); fprint(ircfd, "NICK %s\r\n", nickname); if(nspasswd && strcmp(nspasswd, "")){ fprint(ircfd, "PRIVMSG nickserv :identify %s\r\n", nspasswd); fprint(logfd, "PRIVMSG nicksrv :identify PASSWORD\r\n"); } for(i = 0; i < tchans; i++) fprint(ircfd, "JOIN %s\r\n", channels[i]); rconn++; qunlock(&lck);} voidlogger(void){ char buf[513]; char *f[3]; long n; for(;;){ while((n = readln(ircfd, buf, sizeof(buf)-1)) > 0){ write(logfd, buf, n); buf[n] = 0; n = tokenize(buf, f, nelem(f)); if(n == 3 && *f[0] == ':' && !cistrcmp(f[1], "PING")){ qlock(&lck); fprint(ircfd, "PONG %s\r\n", f[2]); fprint(logfd, "PONG %s\r\n", f[2]); qunlock(&lck); } else if(n == 2 && !cistrcmp(f[0], "PING")){ qlock(&lck); fprint(ircfd, "PONG %s\r\n", f[1]); fprint(logfd, "PONG %s\r\n", f[1]); qunlock(&lck); } else if(n == 3 && atoi(f[1]) == 433) { reregister(); } } if((rconn >= 1) && (rconn < 50)) sleep(5000); else if (rconn > 500) { fprint(logfd, "Error: too many reconnect attempts\r\n"); killall(); } reconnect(); }} voidircsrv(void){ char buf[512]; long n; while((n = readln(0, buf, sizeof(buf)-1)) > 0){ joinhandler(buf); qlock(&lck); if(write(logfd, buf, n) != n) fprint(2, "write to irclog: %r\n"); if(write(ircfd, buf, n) != n) fprint(2, "write to ircserver: %r\n"); qunlock(&lck); } killall();} voidjoinhandler(char *buf){ char *toks[4]; char *crap; if(strlen(buf) > 4){ crap = strdup(buf); crap[strlen(crap)-2] = '\0'; if(tokenize(crap, toks, 4) >= 2) if(strncmp(crap, "JOIN", 4) == 0) if(tchans < 256){ fprint(debugfd, "adding channel %s to join list\n", toks[1]); channels[tchans] = strdup(toks[1]); tchans++; } else { fprint(debugfd, "join list full\n"); } free(crap); }}
#include <u.h>
#include <libc.h>
#include <auth.h>
#include <libsec.h>
char *post;
char *file;
int ircfd = -1; // the irc server
int logfd;
int debugfd = -1;
int enctls = 0; // ssl/tls
QLock lck;
char *channels[256];
int tchans;
int rconn;
char *server;
char *passwd;
char *nspasswd;
char *nickname;
char *realname;
char *username;
char *mode = "foo";
char *unused = "bar";
void ircsrv(void);
void logger(void);
void die(void*, char*);
void reconnect(void);
void joinhandler(char*);
void
usage(void)
{
fprint(2, "usage: %s [-e] [-s service] [-f file] [-p pass] [-P nickserv password] [-n nickname] [-D debug log] [net!]ircserver[!port]\n", argv0);
exits("usage");
}
void
killall(void)
{
postnote(PNGROUP, getpid(), "quit");
while(waitpid() != -1)
;
remove(post);
exits(nil);
}
void
die(void *, char *)
{
killall();
}
void
main(int argc, char *argv[])
{
char *tmp;
char *debugfn;
int p[2], fd;
rconn = 0;
ARGBEGIN{
case 'f':
file = EARGF(usage());
break;
case 's':
post = EARGF(usage());
break;
case 'r':
realname = EARGF(usage());
break;
case 'e':
enctls = 1;
break;
case 'P':
/* do whats stated below. Don't want your passwords to get out there! */
tmp = EARGF(usage());
nspasswd = smprint("%s", tmp);
if(nspasswd)
memset(tmp, '\0', strlen(tmp));
else
nspasswd = tmp;
break;
case 'n':
nickname = strdup(EARGF(usage()));
break;
case 'p':
passwd = EARGF(usage());
/* try to obfuscate the password so ps -a won't see it */
tmp = passwd;
passwd = smprint("%s", tmp);
if(passwd)
memset(tmp, '\0', strlen(tmp));
else
passwd = tmp;
break;
case 'D':
debugfn = EARGF(usage());
debugfd = create(debugfn, OWRITE, 0600 | DMAPPEND);
if(debugfd < 0)
sysfatal("create(%s): %r", debugfn);
break;
default:
usage();
}ARGEND;
if(argc < 1)
usage();
server = argv[0];
username = getuser();
if(nickname == nil)
nickname = strdup(username);
if(debugfd == -1){
debugfd = open("/dev/null", OWRITE);
if(debugfd < 0)
sysfatal("open(/dev/null): %r");
}
if(post == nil)
post = smprint("/srv/%sirc", username);
else
post = smprint("/srv/%s", post);
if(file == nil)
file = smprint("/tmp/%sirc", username);
if((logfd = create(file, OWRITE, 0600 | DMAPPEND)) < 0)
sysfatal("create(%s): %r", file);
if((fd = create(post, OWRITE, 0600)) < 0)
sysfatal("create(%s): %r", post);
if(pipe(p) == -1)
sysfatal("pipe: %r");
fprint(fd, "%d", p[1]);
close(fd);
close(p[1]);
close(0);
close(1);
close(2);
dup(p[0], 0);
if(rfork(RFMEM|RFFDG|RFREND|RFPROC|RFNOTEG|RFCENVG|RFNOWAIT) == 0) {
notify(die);
reconnect();
switch(rfork(RFPROC|RFMEM)){
case -1:
sysfatal("rfork: %r");
case 0:
notify(die);
logger();
break;
default:
ircsrv();
break;
}
}
exits(nil);
}
long
readln(int fd, void *vp, long len)
{
char *b = vp;
while(len > 0 && read(fd, b, 1) > 0){
if(*b++ == '\n')
break;
len--;
}
return b - (char*)vp;
}
void
reregister(void)
{
int n;
char nbuf[32];
strncpy(nbuf, nickname, sizeof(nbuf) - 2);
switch(nbuf[strlen(nbuf) - 1]) {
case '0':
case '1':
case '2':
case '3':
case '4':
case '5':
case '6':
case '7':
case '8':
nbuf[strlen(nbuf) - 1]++;
break;
case '9':
qlock(&lck);
fprint(logfd, "can not register nick, bailing out\n");
qunlock(&lck);
die(nil, nil);
default:
n = strlen(nbuf);
nbuf[n] = '0';
nbuf[n+1] = '\0';
break;
}
qlock(&lck);
fprint(ircfd, "NICK %s\r\n", nbuf);
fprint(logfd, "NICK %s\r\n", nickname);
qunlock(&lck);
}
void
reconnect(void)
{
TLSconn *conn;
int i;
if(ircfd >= 0)
close(ircfd);
if((ircfd = dial(netmkaddr(server, "tcp", "6667"), nil, nil, nil)) < 0)
sysfatal("dial %r");
if(enctls > 0) {
conn = (TLSconn *)mallocz(sizeof *conn, 1);
ircfd = tlsClient(ircfd, conn);
if (ircfd < 0) { sysfatal ("tls: %r"); }
}
qlock(&lck);
if(passwd && strcmp(passwd, ""))
fprint(ircfd, "PASS %s\r\n", passwd);
fprint(ircfd, "USER %s %s %s :%s\r\n",
username, mode, unused, realname);
fprint(ircfd, "NICK %s\r\n", nickname);
if(nspasswd && strcmp(nspasswd, "")){
fprint(ircfd, "PRIVMSG nickserv :identify %s\r\n", nspasswd);
fprint(logfd, "PRIVMSG nicksrv :identify PASSWORD\r\n");
}
for(i = 0; i < tchans; i++)
fprint(ircfd, "JOIN %s\r\n", channels[i]);
rconn++;
qunlock(&lck);
}
void
logger(void)
{
char buf[513];
char *f[3];
long n;
for(;;){
while((n = readln(ircfd, buf, sizeof(buf)-1)) > 0){
write(logfd, buf, n);
buf[n] = 0;
n = tokenize(buf, f, nelem(f));
if(n == 3 && *f[0] == ':' && !cistrcmp(f[1], "PING")){
qlock(&lck);
fprint(ircfd, "PONG %s\r\n", f[2]);
fprint(logfd, "PONG %s\r\n", f[2]);
qunlock(&lck);
} else if(n == 2 && !cistrcmp(f[0], "PING")){
qlock(&lck);
fprint(ircfd, "PONG %s\r\n", f[1]);
fprint(logfd, "PONG %s\r\n", f[1]);
qunlock(&lck);
} else if(n == 3 && atoi(f[1]) == 433) {
reregister();
}
}
if((rconn >= 1) && (rconn < 50))
sleep(5000);
else if (rconn > 500) {
fprint(logfd, "Error: too many reconnect attempts\r\n");
killall();
}
reconnect();
}
}
void
ircsrv(void)
{
char buf[512];
long n;
while((n = readln(0, buf, sizeof(buf)-1)) > 0){
joinhandler(buf);
qlock(&lck);
if(write(logfd, buf, n) != n)
fprint(2, "write to irclog: %r\n");
if(write(ircfd, buf, n) != n)
fprint(2, "write to ircserver: %r\n");
qunlock(&lck);
}
killall();
}
void
joinhandler(char *buf)
{
char *toks[4];
char *crap;
if(strlen(buf) > 4){
crap = strdup(buf);
crap[strlen(crap)-2] = '\0';
if(tokenize(crap, toks, 4) >= 2)
if(strncmp(crap, "JOIN", 4) == 0)
if(tchans < 256){
fprint(debugfd, "adding channel %s to join list\n", toks[1]);
channels[tchans] = strdup(toks[1]);
tchans++;
} else {
fprint(debugfd, "join list full\n");
}
free(crap);
}
}