2016-07-17 21:52:03 +00:00
|
|
|
// vim:ts=4:sts=4:sw=4:noet:tw=72
|
|
|
|
|
2016-07-18 21:33:33 +00:00
|
|
|
package ircd
|
2014-11-22 13:21:30 +00:00
|
|
|
|
|
|
|
import (
|
2017-08-22 05:23:30 +00:00
|
|
|
"bytes"
|
2014-11-22 13:21:30 +00:00
|
|
|
"fmt"
|
|
|
|
"io"
|
|
|
|
"net"
|
|
|
|
"strings"
|
|
|
|
"time"
|
|
|
|
|
2016-07-17 21:52:03 +00:00
|
|
|
"code.dnix.de/an/irc"
|
|
|
|
"code.dnix.de/an/xlog"
|
|
|
|
)
|
2014-11-22 13:21:30 +00:00
|
|
|
|
2016-07-18 19:22:03 +00:00
|
|
|
type Client interface {
|
|
|
|
Name() string
|
2016-07-27 13:17:54 +00:00
|
|
|
Password() string
|
2016-07-21 08:30:44 +00:00
|
|
|
Register(bool)
|
2016-07-18 19:22:03 +00:00
|
|
|
Send(*irc.Message)
|
|
|
|
Receive(*irc.Message)
|
|
|
|
Destroy()
|
|
|
|
}
|
|
|
|
|
|
|
|
type RemoteClient struct {
|
2014-11-22 13:21:30 +00:00
|
|
|
server *Server
|
|
|
|
|
|
|
|
name string
|
|
|
|
password string
|
|
|
|
modes string
|
|
|
|
|
|
|
|
isSSL bool
|
|
|
|
isRegistered bool
|
|
|
|
isAuthed bool
|
|
|
|
isClosed bool
|
|
|
|
|
2016-07-21 08:30:44 +00:00
|
|
|
receive chan *irc.Message
|
|
|
|
registered chan bool
|
2014-11-22 13:21:30 +00:00
|
|
|
|
2016-07-17 21:52:03 +00:00
|
|
|
conn net.Conn
|
|
|
|
writeq chan string
|
2014-11-22 13:21:30 +00:00
|
|
|
}
|
|
|
|
|
2016-07-20 16:17:13 +00:00
|
|
|
func NewRemoteClient(sv *Server, conn net.Conn) *RemoteClient {
|
2016-07-18 19:22:03 +00:00
|
|
|
cl := new(RemoteClient)
|
2016-07-21 08:30:44 +00:00
|
|
|
|
2016-07-20 16:17:13 +00:00
|
|
|
cl.server = sv
|
2014-11-22 13:21:30 +00:00
|
|
|
|
|
|
|
cl.name = ""
|
|
|
|
cl.password = ""
|
|
|
|
cl.modes = ""
|
|
|
|
|
2016-07-28 22:21:24 +00:00
|
|
|
cl.receive = make(chan *irc.Message, 1024)
|
2016-07-21 08:30:44 +00:00
|
|
|
cl.registered = make(chan bool)
|
2014-11-22 13:21:30 +00:00
|
|
|
|
|
|
|
cl.isRegistered = false
|
|
|
|
cl.isAuthed = false
|
|
|
|
cl.isClosed = false
|
|
|
|
cl.conn = conn
|
2016-07-28 22:21:24 +00:00
|
|
|
cl.writeq = make(chan string, 1024)
|
2014-11-22 13:21:30 +00:00
|
|
|
|
|
|
|
go cl.connReader()
|
|
|
|
go cl.connWriter()
|
2016-07-21 22:00:35 +00:00
|
|
|
go cl.dispatcher()
|
2014-11-22 13:21:30 +00:00
|
|
|
|
2016-07-18 19:22:03 +00:00
|
|
|
xlog.Info("RemoteClient connected")
|
2014-11-22 13:21:30 +00:00
|
|
|
|
|
|
|
return cl
|
|
|
|
}
|
|
|
|
|
2016-07-18 19:22:03 +00:00
|
|
|
func (cl *RemoteClient) Name() string {
|
2014-11-22 13:21:30 +00:00
|
|
|
return cl.name
|
|
|
|
}
|
|
|
|
|
2016-07-27 13:17:54 +00:00
|
|
|
func (cl *RemoteClient) Password() string {
|
|
|
|
return cl.password
|
|
|
|
}
|
|
|
|
|
2016-07-21 08:30:44 +00:00
|
|
|
func (cl *RemoteClient) Register(success bool) {
|
|
|
|
cl.registered <- success
|
|
|
|
}
|
|
|
|
|
2016-07-18 19:22:03 +00:00
|
|
|
func (cl *RemoteClient) Send(msg *irc.Message) {
|
2016-07-17 21:52:03 +00:00
|
|
|
msg.Pre = cl.name
|
2016-07-21 22:00:35 +00:00
|
|
|
cl.server.Dispatch(msg)
|
2014-11-22 13:21:30 +00:00
|
|
|
}
|
|
|
|
|
2016-07-18 19:22:03 +00:00
|
|
|
func (cl *RemoteClient) Receive(msg *irc.Message) {
|
2014-11-22 13:21:30 +00:00
|
|
|
cl.receive <- msg
|
|
|
|
}
|
|
|
|
|
2016-07-18 19:22:03 +00:00
|
|
|
func (cl *RemoteClient) AddMode(mode string) {
|
2014-11-22 13:21:30 +00:00
|
|
|
cl.modes = cl.modes + mode
|
|
|
|
}
|
|
|
|
|
2016-07-18 19:22:03 +00:00
|
|
|
func (cl *RemoteClient) DelMode(mode string) {
|
2014-11-22 13:21:30 +00:00
|
|
|
cl.modes = strings.Replace(cl.modes, mode, "", -1)
|
|
|
|
}
|
|
|
|
|
2016-07-18 19:22:03 +00:00
|
|
|
func (cl *RemoteClient) HasMode(mode string) bool {
|
2014-11-22 13:21:30 +00:00
|
|
|
return strings.IndexRune(cl.modes, rune(mode[0])) != -1
|
|
|
|
}
|
|
|
|
|
2016-07-18 19:22:03 +00:00
|
|
|
func (cl *RemoteClient) Destroy() {
|
|
|
|
if cl.isClosed {
|
|
|
|
return
|
|
|
|
}
|
|
|
|
cl.isClosed = true
|
|
|
|
close(cl.writeq)
|
|
|
|
cl.conn.Write([]byte("ERROR :Closing link"))
|
|
|
|
cl.conn.Close()
|
|
|
|
if cl.name != "" {
|
|
|
|
xlog.Info("RemoteClient '%s' disconnected", cl.name)
|
|
|
|
} else {
|
|
|
|
xlog.Info("RemoteClient disconnected")
|
|
|
|
}
|
2014-11-22 13:21:30 +00:00
|
|
|
}
|
|
|
|
|
2016-07-21 22:00:35 +00:00
|
|
|
func (cl *RemoteClient) dispatcher() {
|
2014-11-22 13:21:30 +00:00
|
|
|
for {
|
2017-08-22 05:23:30 +00:00
|
|
|
time.Sleep(10 * time.Millisecond)
|
2014-11-22 13:21:30 +00:00
|
|
|
if cl.isClosed {
|
|
|
|
return
|
|
|
|
}
|
|
|
|
select {
|
|
|
|
case msg := <-cl.receive:
|
|
|
|
cl.writeMsg(msg)
|
2016-07-21 08:30:44 +00:00
|
|
|
case success := <-cl.registered:
|
|
|
|
if !success {
|
2016-07-22 13:56:50 +00:00
|
|
|
cl.name = ""
|
2016-07-21 08:30:44 +00:00
|
|
|
cl.Destroy()
|
|
|
|
xlog.Debug("User registration failed: '%s'", cl.name)
|
|
|
|
return
|
|
|
|
}
|
|
|
|
xlog.Debug("User '%s' registered", cl.name)
|
2014-11-22 13:21:30 +00:00
|
|
|
default:
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-08-22 05:23:30 +00:00
|
|
|
/*func (cl *RemoteClient) pinger() {
|
|
|
|
for {
|
|
|
|
time.Sleep(30 * time.Second)
|
|
|
|
if cl.isClosed {
|
|
|
|
return
|
|
|
|
}
|
|
|
|
cl.writeLine("PING %s", cl.server.host)
|
|
|
|
}
|
|
|
|
}*/
|
|
|
|
|
2016-07-18 19:22:03 +00:00
|
|
|
func (cl *RemoteClient) connReader() {
|
2014-11-22 13:21:30 +00:00
|
|
|
for {
|
2017-08-22 05:23:30 +00:00
|
|
|
time.Sleep(10 * time.Millisecond)
|
|
|
|
buf := make([]byte, 4096)
|
2016-07-18 12:26:46 +00:00
|
|
|
if cl.isClosed {
|
|
|
|
xlog.Debug("connReader: exiting")
|
|
|
|
return
|
|
|
|
}
|
2017-08-22 05:23:30 +00:00
|
|
|
cl.conn.SetReadDeadline(time.Now().Add(time.Second * 5))
|
|
|
|
n, err := cl.conn.Read(buf)
|
2014-11-22 13:21:30 +00:00
|
|
|
if err == io.EOF {
|
2016-07-18 12:26:46 +00:00
|
|
|
xlog.Info("connReader: Connection closed by peer")
|
2016-07-21 22:00:35 +00:00
|
|
|
cl.server.DelClient(cl)
|
2014-11-22 13:21:30 +00:00
|
|
|
return
|
|
|
|
}
|
|
|
|
if err != nil {
|
2017-08-22 05:23:30 +00:00
|
|
|
continue
|
|
|
|
}
|
|
|
|
raw := buf[:n]
|
|
|
|
raw = bytes.Replace(raw, []byte("\r\n"), []byte("\n"), -1)
|
|
|
|
raw = bytes.Replace(raw, []byte("\r"), []byte("\n"), -1)
|
|
|
|
lines := bytes.Split(raw, []byte("\n"))
|
|
|
|
for _, line := range lines {
|
|
|
|
if len(line) > 0 {
|
|
|
|
cl.handleCmd(string(line))
|
|
|
|
}
|
2014-11-22 13:21:30 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2016-07-18 19:22:03 +00:00
|
|
|
func (cl *RemoteClient) connWriter() {
|
2014-11-22 13:21:30 +00:00
|
|
|
for line := range cl.writeq {
|
2017-08-22 05:23:30 +00:00
|
|
|
time.Sleep(10 * time.Millisecond)
|
|
|
|
if cl.isClosed {
|
|
|
|
return
|
|
|
|
}
|
2014-11-22 13:21:30 +00:00
|
|
|
written := 0
|
|
|
|
bytes := []byte(line + "\r\n")
|
|
|
|
for written < len(line) {
|
2017-08-22 05:23:30 +00:00
|
|
|
cl.conn.SetWriteDeadline(time.Now().Add(time.Second * 5))
|
2014-11-22 13:21:30 +00:00
|
|
|
n, err := cl.conn.Write(bytes[written:])
|
|
|
|
if err == io.EOF {
|
2016-07-18 12:26:46 +00:00
|
|
|
xlog.Info("connWriter: Connection closed by peer")
|
2016-07-21 22:00:35 +00:00
|
|
|
cl.server.DelClient(cl)
|
2014-11-22 13:21:30 +00:00
|
|
|
return
|
|
|
|
} else if err != nil {
|
2016-07-18 12:26:46 +00:00
|
|
|
xlog.Error("connWriter: %s", err.Error())
|
2016-07-21 22:00:35 +00:00
|
|
|
cl.server.DelClient(cl)
|
2014-11-22 13:21:30 +00:00
|
|
|
return
|
|
|
|
}
|
|
|
|
written += n
|
|
|
|
}
|
|
|
|
}
|
2016-07-18 12:26:46 +00:00
|
|
|
xlog.Debug("connWriter: exiting")
|
2014-11-22 13:21:30 +00:00
|
|
|
}
|
|
|
|
|
2016-07-18 19:22:03 +00:00
|
|
|
func (cl *RemoteClient) writeMsg(msg *irc.Message) {
|
2017-08-22 05:23:30 +00:00
|
|
|
clid := strings.ToLower(msg.Pre)
|
|
|
|
if _, exists := cl.server.clients[clid]; exists {
|
|
|
|
msg.Pre = msg.Pre + "!" + msg.Pre + "@" + cl.server.clHosts[clid]
|
|
|
|
}
|
2016-07-18 19:22:03 +00:00
|
|
|
cl.writeLine(msg.String())
|
|
|
|
}
|
|
|
|
|
|
|
|
func (cl *RemoteClient) writeLine(format string, a ...interface{}) {
|
|
|
|
cl.writeq <- fmt.Sprintf(format, a...)
|
|
|
|
}
|
|
|
|
|
2016-07-20 16:17:13 +00:00
|
|
|
func (cl *RemoteClient) handleCmd(s string) {
|
2016-07-18 12:26:46 +00:00
|
|
|
msg := irc.Parse(s)
|
2016-07-21 08:30:44 +00:00
|
|
|
msg.Cmd = strings.ToUpper(msg.Cmd)
|
2016-07-20 16:17:13 +00:00
|
|
|
if cl.name != "" {
|
2016-07-18 12:26:46 +00:00
|
|
|
cl.Send(msg)
|
2016-07-20 16:17:13 +00:00
|
|
|
return
|
|
|
|
}
|
|
|
|
// If client name is not set, intercept cmds until we get one
|
|
|
|
switch msg.Cmd {
|
|
|
|
case "PASS":
|
|
|
|
cl.password = msg.Args[0]
|
|
|
|
case "NICK":
|
|
|
|
cl.name = msg.Args[0]
|
2016-07-21 22:00:35 +00:00
|
|
|
cl.server.AddClient(cl)
|
2016-07-20 16:17:13 +00:00
|
|
|
case "USER":
|
2016-07-18 12:26:46 +00:00
|
|
|
}
|
2014-11-22 13:21:30 +00:00
|
|
|
}
|
|
|
|
|
2016-07-18 19:22:03 +00:00
|
|
|
func checkAuth(cl *RemoteClient) {
|
2014-11-22 13:21:30 +00:00
|
|
|
if cl.name == "" || cl.password == "" {
|
|
|
|
return
|
|
|
|
}
|
|
|
|
}
|