this commit does not work

This commit is contained in:
Jeffrey Paul 2019-08-21 08:15:12 +02:00
parent a825e63113
commit 45dbc0c109
7 changed files with 206 additions and 106 deletions

View File

@ -9,6 +9,7 @@ func main() {
log.SetLevel(logrus.DebugLevel) log.SetLevel(logrus.DebugLevel)
log.Println("sircd starting up") log.Println("sircd starting up")
s := sircd.NewSircd() s := sircd.NewSircd()
s.SetServerName("irc.example.com")
s.SetLogger(log) s.SetLogger(log)
go s.Start() go s.Start()
for s.Running { for s.Running {

View File

@ -5,20 +5,25 @@ import (
"github.com/google/uuid" "github.com/google/uuid"
"github.com/sirupsen/logrus" "github.com/sirupsen/logrus"
"net" "net"
"strings"
"fmt"
) )
func newIrcClient(conn net.Conn, log *logrus.Logger, mc chan *ircMessage) *ircClient { func newIrcClient(conn net.Conn, log *logrus.Logger, mc chan *ircMessage, s *sircd) *ircClient {
c := new(ircClient) c := new(ircClient)
c.Id = uuid.New().String() c.Id = uuid.New().String()
c.conn = conn c.conn = conn
c.log = log c.log = log
c.mc = mc c.mc = mc
c.server = s
c.inputBytes = new(bytes.Buffer) c.inputBytes = new(bytes.Buffer)
c.outputBytes = new(bytes.Buffer) c.outputBytes = new(bytes.Buffer)
c.session = NewIrcUserSession()
return c return c
} }
type ircClient struct { type ircClient struct {
//FIXME add a mutex and protect during writes
Id string Id string
conn net.Conn conn net.Conn
log *logrus.Logger log *logrus.Logger
@ -26,11 +31,93 @@ type ircClient struct {
inputBytes *bytes.Buffer inputBytes *bytes.Buffer
outputBytes *bytes.Buffer outputBytes *bytes.Buffer
mc chan *ircMessage mc chan *ircMessage
server *sircd
} }
func (c *ircClient) Close() { func (c *ircClient) ServerName() string {
return c.server.serverName
}
func (c *ircClient) Kill() {
//server will remove it from the server-managed client list
//and will call c.CleanupAndClose() when it pops from this channel
c.server.deadClients <- c
}
func (c *ircClient) CleanupAndClose() {
//client cleanup //client cleanup
c.log.Infof("client %d disconnect", c.Id) //FIXME truncate client buffers and whatnot here
// server object already handles removing it from the list of clients
// for the server, so don't do it here
c.log.Infof("client=%s cleanup", c.Id)
c.conn.Close()
}
func (c *ircClient) AppendStringToOutputBuffer(input string) {
// woo boilerplate because no polymorphism
c.AppendBytesToOutputBuffer([]byte(input))
}
func (c *ircClient) AppendBytesToOutputBuffer(input []byte) {
c.outputBytes.Write(input)
}
func (c *ircClient) FlushOutputBuffer() bool {
numBytes, err := c.outputBytes.WriteTo(c.conn)
if err != nil {
c.log.Debugf("failed to write completely to client<%s>, marking dead", c.Id)
c.Kill()
return false
} else {
c.log.Debugf("wrote %d bytes to client<%s>", numBytes, c.Id)
return true
}
}
func (c *ircClient) RespUnknownCommand(command string) {
//421 == unknown command
c.SendServerMessage(421, []string{command, "Unknown command. Please try your call again later."})
}
func (c *ircClient) SendServerMessage(code uint16, params []string) bool {
//FIXME this is terrible hacky duct tape code, redo it
p := ""
if len(params) > 0 {
// pop off last item for separate processing
lastelem := params[len(params)-1]
params = params[:len(params)-1]
for _, elem := range params {
if strings.ContainsAny(elem, " \t\r\n") {
c.log.Errorf("wtf %s", elem)
return false
}
}
if strings.ContainsAny(lastelem, "\t\r\n") {
c.log.Errorf("wtf %s", lastelem)
return false
}
if strings.ContainsAny(lastelem, " ") {
lastelem = fmt.Sprintf(":%s", lastelem)
}
p = strings.Join(params, " ")
if len(p) > 0 {
p = " " + lastelem
} else {
p = lastelem
}
}
sendstr := fmt.Sprintf(":%s %s %d", c.ServerName(), code, c.session.nick)
if len(p) > 0 {
sendstr = sendstr + " " + p
}
sendstr = sendstr + "\r\n"
c.log.Debugf("sending string '%s' to client<%s>", sendstr, c.Id)
c.AppendStringToOutputBuffer(sendstr)
return c.FlushOutputBuffer()
} }
func (c *ircClient) AppendInputBuffer(input []byte) { func (c *ircClient) AppendInputBuffer(input []byte) {
@ -44,9 +131,10 @@ func (c *ircClient) AppendInputBuffer(input []byte) {
func (c *ircClient) ParseInputBuffer() { func (c *ircClient) ParseInputBuffer() {
c.log.Debugf("my input buffer is %d bytes", c.inputBytes.Len()) c.log.Debugf("my input buffer is %d bytes", c.inputBytes.Len())
c.log.Debugf("my input buffer is: '%s'", c.inputBytes.String()) c.log.Debugf("my input buffer is: '%s'", c.inputBytes.String())
for line, err := c.inputBytes.ReadString(byte('\n')) line, err := c.inputBytes.ReadString(byte('\n'))
if err == nil { if err == nil {
c.mc <- c.ParseSingleInput(line) //FIXME make this return val, err c.mc <- c.ParseSingleInput(line)
c.ParseInputBuffer()
} else { } else {
c.log.Debugf("error parsing input buffer: ", err.Error()) c.log.Debugf("error parsing input buffer: ", err.Error())
} }

35
sircd/irc.go Normal file
View File

@ -0,0 +1,35 @@
package sircd
func (s *sircd) processIRCMessage(m *ircMessage) {
switch m.command {
case "CAP":
s.processCAPCommand(m)
case "NICK":
s.processNICKCommand(m)
case "USER":
s.processUSERCommand(m)
default:
s.processUnknownCommand(m)
}
s.log.Infof(m.String())
s.log.Infof("%+v", m)
}
func (s *sircd) processUnknownCommand(m *ircMessage) {
m.from.RespUnknownCommand(m.command)
}
func (s *sircd) processNICKCommand(m *ircMessage) {
//FIXME check if nick is in use
//FIXME check if nick is valid
m.from.session.SetNick(m.params[0])
}
func (s *sircd) processUSERCommand(m *ircMessage) {
m.from.session.SetUserInfo(m.params[0])
}
func (s *sircd) processCAPCommand(m *ircMessage) {
s.log.Debugln("ignoring CAP command, unsupported")
// pass
}

View File

@ -1,30 +0,0 @@
package sircd
func (s *sircd) processIRCMessage(m *ircMessage) {
s.log.Info("not implemented")
//for client, _ := range s.ircClients {
//}
}
/*
import "net"
case publish := <-publishes:
for conn, _ := range conns {
go func(conn net.Conn) {
totalWritten := 0
for totalWritten < len(publish) {
writtenThisCall, err := conn.Write(publish[totalWritten:])
if err != nil {
deadConns <- conn
break
}
totalWritten += writtenThisCall
}
}(conn)
}
}
}
listener.Close()
}
*/

View File

@ -1,12 +1,12 @@
package sircd package sircd
import ( import (
"strings" "errors"
"fmt" "fmt"
"regexp" "regexp"
"strings"
) )
type ircMessage struct { type ircMessage struct {
from *ircClient from *ircClient
//received //received
@ -17,8 +17,7 @@ type ircMessage struct {
params []string params []string
} }
func NewIrcMessageFromString (line string, from *ircClient) *ircMessage { func parseIrcLine(line string) (*ircMessage, error) {
//FIXME do this at compile or start time instead of every message //FIXME do this at compile or start time instead of every message
ircregex, err := regexp.Compile(`^(\@(\S+) )?(?:[:](\S+) )?(\S+)(?: ([^:].+?))?(?: [:](.+))?$`) ircregex, err := regexp.Compile(`^(\@(\S+) )?(?:[:](\S+) )?(\S+)(?: ([^:].+?))?(?: [:](.+))?$`)
@ -27,18 +26,17 @@ func NewIrcMessageFromString (line string, from *ircClient) *ircMessage {
} }
line = strings.TrimRight(line, "\r\n") line = strings.TrimRight(line, "\r\n")
m := new(ircMessage) m := new(ircMessage)
m.raw = line m.raw = line
if ircregex.MatchString(m.raw) == false { if ircregex.MatchString(m.raw) == false {
m.command = "UNKNOWN" return nil, errors.New("parse error")
return m
} }
matches := ircregex.FindAllStringSubmatch(m.raw, -1) matches := ircregex.FindAllStringSubmatch(m.raw, -1)
if len(matches) == 0 { if len(matches) == 0 {
m.command = "UNKNOWN" return nil, errors.New("parse error")
return m
} }
match := matches[0] match := matches[0]
@ -46,7 +44,7 @@ func NewIrcMessageFromString (line string, from *ircClient) *ircMessage {
fmt.Printf("%+v\n", len(match)) fmt.Printf("%+v\n", len(match))
m.tags = match[2] m.tags = match[2]
m.source = match[3] m.source = match[3]
m.command = match[4] m.command = strings.ToUpper(match[4])
if len(match[5]) > 0 { if len(match[5]) > 0 {
m.params = strings.Fields(match[5]) m.params = strings.Fields(match[5])
} }
@ -54,12 +52,18 @@ func NewIrcMessageFromString (line string, from *ircClient) *ircMessage {
m.params = append(m.params, match[6]) m.params = append(m.params, match[6])
} }
fmt.Printf("%+v\n", &m) return m, nil
fmt.Println(m)
return m
} }
func NewIrcMessageFromString(line string, from *ircClient) *ircMessage {
msg, err := parseIrcLine(line)
if err != nil {
panic("wat")
}
return msg
}
func (m *ircMessage) String() string { func (m *ircMessage) String() string {
return fmt.Sprintf("IRCMessage<%s>('%s')", m.command, m.raw) return fmt.Sprintf("IRCMessage<%s>('%s')", m.command, strings.Join(m.params, ","))
} }

View File

@ -7,6 +7,7 @@ import (
type sircd struct { type sircd struct {
Running bool Running bool
serverName string
log *logrus.Logger log *logrus.Logger
netName string netName string
ircPort uint16 ircPort uint16
@ -31,8 +32,12 @@ func NewSircd() *sircd {
return s return s
} }
func (s *sircd) SetLogger(l *logrus.Logger) { func (s *sircd) SetLogger(logger *logrus.Logger) {
s.log = l s.log = logger
}
func (s *sircd) SetServerName(name string) {
s.serverName = name
} }
func (s *sircd) Start() { func (s *sircd) Start() {
@ -53,7 +58,7 @@ func (s *sircd) Start() {
if err != nil { if err != nil {
s.log.Panicf("Error accepting: ", err.Error()) s.log.Panicf("Error accepting: ", err.Error())
} }
s.newClients <- newIrcClient(conn, s.log, s.messageQueue) s.newClients <- newIrcClient(conn, s.log, s.messageQueue, s)
}() }()
go s.mainloop() go s.mainloop()
@ -70,7 +75,8 @@ func (s *sircd) mainloop() {
for { for {
nbyte, err := client.conn.Read(buf) nbyte, err := client.conn.Read(buf)
if err != nil { if err != nil {
s.deadClients <- client //this will surface it in the deadClients channel
client.Kill()
break break
} else { } else {
fragment := make([]byte, nbyte) fragment := make([]byte, nbyte)
@ -82,29 +88,9 @@ func (s *sircd) mainloop() {
}() }()
case deadClient := <-s.deadClients: case deadClient := <-s.deadClients:
delete(s.ircClients, deadClient) delete(s.ircClients, deadClient)
deadClient.Close() deadClient.CleanupAndClose()
case message := <-s.messageQueue: case message := <-s.messageQueue:
s.processIRCMessage(message) s.processIRCMessage(message)
} }
} }
} }
/*
case publish := <-publishes:
for conn, _ := range conns {
go func(conn net.Conn) {
totalWritten := 0
for totalWritten < len(publish) {
writtenThisCall, err := conn.Write(publish[totalWritten:])
if err != nil {
deadConns <- conn
break
}
totalWritten += writtenThisCall
}
}(conn)
}
}
}
listener.Close()
*/

View File

@ -2,8 +2,24 @@ package sircd
type ircNick string type ircNick string
type ircRealName string type ircRealName string
type hostname string
type ircUserSession struct { type ircUserSession struct {
//FIXME add a mutex and protect during writes
nick ircNick nick ircNick
realname ircRealName realname ircRealName
host hostname
}
func NewIrcUserSession() *ircUserSession {
// FIXME get conn.RemoteAddr passed in and stringify it here and put it
// in the session
s := new(ircUserSession)
s.nick = '*' //default for s2c messages pre-NICK
return s
}
func (s *ircUserSession) SetNick(input string) {
// FIXME check for valid nick-ness
s.nick = input
} }