diff --git a/ircconn.go b/ircconn.go new file mode 100644 index 0000000..f8749bd --- /dev/null +++ b/ircconn.go @@ -0,0 +1,84 @@ +// vim:ts=4:sts=4:sw=4:noet:tw=72 + +package main + +import ( + "bufio" + "net" + "strings" + "time" +) + +type IRCConn struct { + conn net.Conn + read chan string + write chan string + done chan bool + closed bool +} + +func NewIRCConn(conn net.Conn, done chan bool) *IRCConn { + read := make(chan string, 1024) + write := make(chan string, 1024) + + c := &IRCConn{conn: conn, read: read, write: write, done: done, closed: false} + + go c.reader() + go c.writer() + + return c +} + +func (c *IRCConn) Read() string { + select { + case s := <-c.read: + return s + default: + return "" + } +} + +func (c *IRCConn) Write(s string) { + c.write <- s +} + +func (c *IRCConn) Close() { + c.conn.Close() + c.closed = true +} + +func (c *IRCConn) reader() { + input := bufio.NewReader(c.conn) + for { + s, err := input.ReadString('\n') + if err != nil { + c.done <- true + return + } + s = strings.Trim(s, "\r\n") + c.read <- s + } +} + +func (c *IRCConn) writer() { + for { + select { + case line := <-c.write: + written := 0 + bytes := []byte(line + "\r\n") + for written < len(line) { + n, err := c.conn.Write(bytes[written:]) + if err != nil { + c.done <- true + return + } + written += n + } + default: + if c.closed { + return + } + time.Sleep(1 * time.Millisecond) + } + } +} diff --git a/main.go b/main.go new file mode 100644 index 0000000..befd224 --- /dev/null +++ b/main.go @@ -0,0 +1,111 @@ +// vim:ts=4:sts=4:sw=4:noet:tw=72 + +package main + +import ( + "flag" + "fmt" + "log" + "net" + "strings" + "time" + + "code.dnix.de/an/irc" + "code.dnix.de/an/xlog" +) + +var saddr = flag.String("server", "irc.example.com:6667", "Server address") +var laddr = flag.String("listen", "0.0.0.0:6667", "Listen address") + +func init() { + flag.Parse() +} + +func main() { + listen, err := net.Listen("tcp", *laddr) + if err != nil { + xlog.Fatal(err.Error()) + return + } + for { + cconn, err := listen.Accept() + if err != nil { + log.Println(err) + continue + } + go bounce(cconn, saddr) + } +} + +func bounce(cconn net.Conn, saddr *string) { + done := make(chan bool, 5) + + client := NewIRCConn(cconn, done) + defer func() { + client.Close() + }() + + sconn, err := net.Dial("tcp", *saddr) + if err != nil { + log.Println(err) + return + } + server := NewIRCConn(sconn, done) + defer func() { + server.Close() + }() + + if !login(server, client) { + return + } + + go relay(server, client) + go relay(client, server) + + <-done +} + +func login(server, client *IRCConn) bool { + nick := "" + user := "" + pass := "" + for { + s := client.Read() + msg := irc.Parse(s) + msg.Cmd = strings.ToUpper(msg.Cmd) + switch msg.Cmd { + case "PASS": + pass = msg.Args[0] + case "USER": + user = msg.Args[0] + case "NICK": + nick = msg.Args[0] + } + if nick != "" && user != "" { + if authenticate(nick, pass) { + server.Write(fmt.Sprintf("NICK %s", nick)) + server.Write(fmt.Sprintf("USER %s %s %s :%s", user, user, user, user)) + return true + } else { + return false + } + } + } +} + +func authenticate(name, password string) bool { + return true +} + +func relay(conn1 *IRCConn, conn2 *IRCConn) { + for { + if conn1.closed || conn2.closed { + return + } + s := conn1.Read() + if s != "" { + conn2.Write(s) + } + time.Sleep(1 * time.Millisecond) + } +}