this commit does not work
This commit is contained in:
parent
a825e63113
commit
45dbc0c109
1
main.go
1
main.go
@ -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 {
|
||||||
|
108
sircd/client.go
108
sircd/client.go
@ -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,16 +131,17 @@ 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)
|
||||||
} else {
|
c.ParseInputBuffer()
|
||||||
c.log.Debugf("error parsing input buffer: ", err.Error())
|
} else {
|
||||||
}
|
c.log.Debugf("error parsing input buffer: ", err.Error())
|
||||||
|
}
|
||||||
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())
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *ircClient) ParseSingleInput(line string) *ircMessage {
|
func (c *ircClient) ParseSingleInput(line string) *ircMessage {
|
||||||
return NewIrcMessageFromString(line, c)
|
return NewIrcMessageFromString(line, c)
|
||||||
}
|
}
|
||||||
|
35
sircd/irc.go
Normal file
35
sircd/irc.go
Normal 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
|
||||||
|
}
|
@ -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()
|
|
||||||
}
|
|
||||||
*/
|
|
@ -1,65 +1,69 @@
|
|||||||
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
|
||||||
tags string
|
tags string
|
||||||
source string
|
source string
|
||||||
raw string
|
raw string
|
||||||
command string
|
command string
|
||||||
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
|
||||||
|
ircregex, err := regexp.Compile(`^(\@(\S+) )?(?:[:](\S+) )?(\S+)(?: ([^:].+?))?(?: [:](.+))?$`)
|
||||||
|
|
||||||
//FIXME do this at compile or start time instead of every message
|
if err != nil {
|
||||||
ircregex, err := regexp.Compile(`^(\@(\S+) )?(?:[:](\S+) )?(\S+)(?: ([^:].+?))?(?: [:](.+))?$`)
|
panic("can't happen")
|
||||||
|
}
|
||||||
|
|
||||||
if err != nil {
|
line = strings.TrimRight(line, "\r\n")
|
||||||
panic("can't happen")
|
|
||||||
}
|
|
||||||
|
|
||||||
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]
|
||||||
fmt.Printf("%+v\n", match)
|
fmt.Printf("%+v\n", match)
|
||||||
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])
|
||||||
}
|
}
|
||||||
if len(match[6]) > 0 {
|
if len(match[6]) > 0 {
|
||||||
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, ","))
|
||||||
}
|
}
|
||||||
|
@ -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()
|
|
||||||
*/
|
|
||||||
|
@ -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
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user