little bit better, runs now
This commit is contained in:
parent
45dbc0c109
commit
7ae48d42c4
1
.gitignore
vendored
Normal file
1
.gitignore
vendored
Normal file
@ -0,0 +1 @@
|
|||||||
|
/ircd
|
16
Makefile
16
Makefile
@ -1,7 +1,19 @@
|
|||||||
|
VERSION := $(shell git rev-parse --short HEAD)
|
||||||
|
BUILDTIME := $(shell date -u '+%Y-%m-%dT%H:%M:%SZ')
|
||||||
|
|
||||||
|
GOLDFLAGS += -X main.Version=$(VERSION)
|
||||||
|
GOLDFLAGS += -X main.Buildtime=$(BUILDTIME)
|
||||||
|
GOFLAGS = -ldflags "$(GOLDFLAGS)"
|
||||||
|
|
||||||
default: run
|
default: run
|
||||||
|
|
||||||
run: *.go */*.go
|
run: build
|
||||||
go run main.go
|
./ircd
|
||||||
|
|
||||||
|
build: ./ircd
|
||||||
|
|
||||||
|
./ircd: *.go */*.go
|
||||||
|
go build -o $@ $(GOFLAGS) .
|
||||||
|
|
||||||
fmt:
|
fmt:
|
||||||
go fmt *.go
|
go fmt *.go
|
||||||
|
42
main.go
42
main.go
@ -1,18 +1,44 @@
|
|||||||
package main
|
package main
|
||||||
|
|
||||||
|
import "fmt"
|
||||||
import "github.com/sirupsen/logrus"
|
import "github.com/sirupsen/logrus"
|
||||||
import "github.com/sneak/sircd/sircd"
|
import "github.com/sneak/sircd/sircd"
|
||||||
import "time"
|
import "github.com/spf13/viper"
|
||||||
|
|
||||||
|
var Version string
|
||||||
|
var Buildtime string
|
||||||
|
|
||||||
func main() {
|
func main() {
|
||||||
var log = logrus.New()
|
|
||||||
|
// set up logging
|
||||||
|
log := logrus.New()
|
||||||
log.SetLevel(logrus.DebugLevel)
|
log.SetLevel(logrus.DebugLevel)
|
||||||
|
log.SetReportCaller(true)
|
||||||
log.Println("sircd starting up")
|
log.Println("sircd starting up")
|
||||||
s := sircd.NewSircd()
|
|
||||||
s.SetServerName("irc.example.com")
|
c := viper.New()
|
||||||
s.SetLogger(log)
|
// default config variables
|
||||||
go s.Start()
|
c.SetDefault("myhostname", "irc.example.com")
|
||||||
for s.Running {
|
c.SetDefault("network", "ExampleNet")
|
||||||
time.Sleep(1 * time.Second)
|
c.SetDefault("admin", "webmaster@example.com")
|
||||||
|
c.SetDefault("version", Version)
|
||||||
|
c.SetDefault("buildtime", Buildtime)
|
||||||
|
|
||||||
|
// read config file
|
||||||
|
c.SetConfigName("sircd") // name of config file (without extension)
|
||||||
|
c.AddConfigPath("/etc/sircd/") // path to look for the config file in
|
||||||
|
c.AddConfigPath("$HOME/.config/sircd") // call multiple times to add many search paths
|
||||||
|
c.AddConfigPath(".") // optionally look for config in the working directory
|
||||||
|
err := c.ReadInConfig() // Find and read the config file
|
||||||
|
if err != nil { // Handle errors reading the config file
|
||||||
|
panic(fmt.Errorf("Fatal error config file: %s \n", err))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// instantiate server
|
||||||
|
s := sircd.New(c)
|
||||||
|
// give it our logger
|
||||||
|
s.SetLogger(log)
|
||||||
|
|
||||||
|
// run it
|
||||||
|
s.Start()
|
||||||
}
|
}
|
||||||
|
3
sircd.yaml
Normal file
3
sircd.yaml
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
myhostname: irc.example.com
|
||||||
|
network: ExampleNet
|
||||||
|
admin: webmaster@example.com
|
@ -2,14 +2,14 @@ package sircd
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"bytes"
|
"bytes"
|
||||||
|
"fmt"
|
||||||
"github.com/google/uuid"
|
"github.com/google/uuid"
|
||||||
"github.com/sirupsen/logrus"
|
"github.com/sirupsen/logrus"
|
||||||
"net"
|
"net"
|
||||||
"strings"
|
"strings"
|
||||||
"fmt"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
func newIrcClient(conn net.Conn, log *logrus.Logger, mc chan *ircMessage, s *sircd) *ircClient {
|
func newIrcClient(conn net.Conn, log *logrus.Logger, mc chan *ircMessage, s *ircd) *ircClient {
|
||||||
c := new(ircClient)
|
c := new(ircClient)
|
||||||
c.Id = uuid.New().String()
|
c.Id = uuid.New().String()
|
||||||
c.conn = conn
|
c.conn = conn
|
||||||
@ -31,7 +31,7 @@ type ircClient struct {
|
|||||||
inputBytes *bytes.Buffer
|
inputBytes *bytes.Buffer
|
||||||
outputBytes *bytes.Buffer
|
outputBytes *bytes.Buffer
|
||||||
mc chan *ircMessage
|
mc chan *ircMessage
|
||||||
server *sircd
|
server *ircd
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *ircClient) ServerName() string {
|
func (c *ircClient) ServerName() string {
|
||||||
@ -129,6 +129,8 @@ func (c *ircClient) AppendInputBuffer(input []byte) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (c *ircClient) ParseInputBuffer() {
|
func (c *ircClient) ParseInputBuffer() {
|
||||||
|
//FIXME update a timer here on the client when a line is parsed
|
||||||
|
//successfully and time them out after a while
|
||||||
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())
|
||||||
line, err := c.inputBytes.ReadString(byte('\n'))
|
line, err := c.inputBytes.ReadString(byte('\n'))
|
||||||
@ -136,7 +138,8 @@ func (c *ircClient) ParseInputBuffer() {
|
|||||||
c.mc <- c.ParseSingleInput(line)
|
c.mc <- c.ParseSingleInput(line)
|
||||||
c.ParseInputBuffer()
|
c.ParseInputBuffer()
|
||||||
} else {
|
} else {
|
||||||
c.log.Debugf("error parsing input buffer: ", err.Error())
|
// error parsing input buffer, probably don't have a full line yet
|
||||||
|
return
|
||||||
}
|
}
|
||||||
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())
|
||||||
|
81
sircd/irc.go
81
sircd/irc.go
@ -1,6 +1,8 @@
|
|||||||
package sircd
|
package sircd
|
||||||
|
|
||||||
func (s *sircd) processIRCMessage(m *ircMessage) {
|
func (s *ircd) processIRCMessage(m *ircMessage) {
|
||||||
|
// FIXME put all of these in a map of string->function
|
||||||
|
// and dispatch them that way instead
|
||||||
switch m.command {
|
switch m.command {
|
||||||
case "CAP":
|
case "CAP":
|
||||||
s.processCAPCommand(m)
|
s.processCAPCommand(m)
|
||||||
@ -11,25 +13,88 @@ func (s *sircd) processIRCMessage(m *ircMessage) {
|
|||||||
default:
|
default:
|
||||||
s.processUnknownCommand(m)
|
s.processUnknownCommand(m)
|
||||||
}
|
}
|
||||||
s.log.Infof(m.String())
|
s.log.Infof("client<%s> sent %+v", m.from.Id, m)
|
||||||
s.log.Infof("%+v", m)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *sircd) processUnknownCommand(m *ircMessage) {
|
func (s *ircd) sayHello(c *ircClient) {
|
||||||
|
//001 welcome
|
||||||
|
//002 host/version decl
|
||||||
|
//003, 004, 005, 251, 252, 253, 254, 255, 256, 266
|
||||||
|
//375 (begin motd)
|
||||||
|
//372 motd (repeating)
|
||||||
|
//376 end motd
|
||||||
|
// change user mode (from server) :sneak!sneak@butt9q8.a.b.IP MODE sneak :+x
|
||||||
|
/*
|
||||||
|
:irc.butt.es 001 sneak :Welcome to the Buttes IRC Network sneak!sneak@1.2.3.4
|
||||||
|
:irc.butt.es 002 sneak :Your host is irc.butt.es, running version InspIRCd-3
|
||||||
|
:irc.butt.es 003 sneak :This server was created 09:33:24 Aug 05 2019
|
||||||
|
:irc.butt.es 004 sneak irc.butt.es InspIRCd-3 BHIRSWcghiorswxz ACFHIMNOPQRSTXYZbcefijklmnoprstvwz :FHIXYZbefjklovw
|
||||||
|
:irc.butt.es 005 sneak ACCEPT=30 AWAYLEN=200 CALLERID=g CASEMAPPING=ascii CHANLIMIT=#:20 CHANMODES=IXYZbew,k,FHfjl,ACMNOPQRSTcimnprstz CHANNELLEN=64 CHANTYPES=# ELIST=CMNTU ESILENCE=CcdiNnPpTtx EXCEPTS=e EXTBAN=,ACGNOQRSTUacjmprz :are supported by this server
|
||||||
|
:irc.butt.es 005 sneak HOSTLEN=64 INVEX=I KEYLEN=32 KICKLEN=255 LINELEN=512 MAXLIST=IXbew:512 MAXTARGETS=20 MODES=20 NAMESX NETWORK=Buttes NICKLEN=32 OPERLOG OVERRIDE :are supported by this server
|
||||||
|
:irc.butt.es 005 sneak PREFIX=(ov)@+ REMOVE SAFELIST SECURELIST SILENCE=32 STATUSMSG=@+ TOPICLEN=500 USERIP USERLEN=11 VBANLIST WHOX :are supported by this server
|
||||||
|
:irc.butt.es 251 sneak :There are 34 users and 33 invisible on 2 servers
|
||||||
|
:irc.butt.es 252 sneak 2 :operator(s) online
|
||||||
|
:irc.butt.es 253 sneak 1 :unknown connections
|
||||||
|
:irc.butt.es 254 sneak 33 :channels formed
|
||||||
|
:irc.butt.es 255 sneak :I have 60 clients and 1 servers
|
||||||
|
:irc.butt.es 265 sneak :Current local users: 60 Max: 65
|
||||||
|
:irc.butt.es 266 sneak :Current global users: 67 Max: 72
|
||||||
|
:irc.butt.es 375 sneak :irc.butt.es message of the day
|
||||||
|
:irc.butt.es 372 sneak :- ____ _ _ _____ _____ _____ ____
|
||||||
|
:irc.butt.es 372 sneak :- | __ )| | | |_ _|_ _| ____/ ___|
|
||||||
|
:irc.butt.es 372 sneak :- | _ \| | | | | | | | | _| \___ \
|
||||||
|
|
||||||
|
:irc.butt.es 376 sneak :End of message of the day.
|
||||||
|
:irc.butt.es 396 sneak butt9q8.a.b.IP :is now your displayed host
|
||||||
|
:sneak!sneak@butt9q8.a.b.IP MODE sneak :+x
|
||||||
|
*/
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *ircd) processUnknownCommand(m *ircMessage) {
|
||||||
m.from.RespUnknownCommand(m.command)
|
m.from.RespUnknownCommand(m.command)
|
||||||
|
if m.from.session.CheckState("init") {
|
||||||
|
// if they are sending weird commands before NICK/USER, just drop
|
||||||
|
// them
|
||||||
|
// FIXME send an error
|
||||||
|
m.from.Kill()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *sircd) processNICKCommand(m *ircMessage) {
|
func (s *ircd) processNICKCommand(m *ircMessage) {
|
||||||
//FIXME check if nick is in use
|
//FIXME check if nick is in use
|
||||||
//FIXME check if nick is valid
|
//FIXME check if nick is valid
|
||||||
|
s.log.Infof("%+v", m)
|
||||||
|
s.log.Infof("%+v", m.params)
|
||||||
|
s.log.Infof("%+v", m.from)
|
||||||
|
s.log.Infof("%+v", m.from.session)
|
||||||
|
if len(m.params) == 1 {
|
||||||
m.from.session.SetNick(m.params[0])
|
m.from.session.SetNick(m.params[0])
|
||||||
|
s.log.Infof("conn<%s> NICK %s", m.from.Id, m.params[0])
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *sircd) processUSERCommand(m *ircMessage) {
|
func (s *ircd) processUSERCommand(m *ircMessage) {
|
||||||
m.from.session.SetUserInfo(m.params[0])
|
// FIXME fail/disconnect if nick not yet set
|
||||||
|
// FIXME fail if run a second time
|
||||||
|
// FIXME tbh the client connection should be an FSM that limits usage of
|
||||||
|
// certain valid commands per-state
|
||||||
|
//params: <username> <hostname> <servername> <realname>
|
||||||
|
if !m.from.session.CheckState("init") {
|
||||||
|
// can only do this when going init->normal
|
||||||
|
// FIXME send an error
|
||||||
|
m.from.Kill()
|
||||||
|
}
|
||||||
|
|
||||||
|
if m.from.session.SetUserInfo(m.params) { //this changes the session state
|
||||||
|
s.log.Infof("%+v", m.from.session)
|
||||||
|
s.sayHello(m.from)
|
||||||
|
} else {
|
||||||
|
// FIXME send an error
|
||||||
|
m.from.Kill() // bye felicia
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *sircd) processCAPCommand(m *ircMessage) {
|
func (s *ircd) processCAPCommand(m *ircMessage) {
|
||||||
s.log.Debugln("ignoring CAP command, unsupported")
|
s.log.Debugln("ignoring CAP command, unsupported")
|
||||||
// pass
|
// pass
|
||||||
}
|
}
|
||||||
|
@ -56,7 +56,9 @@ func parseIrcLine(line string) (*ircMessage, error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func NewIrcMessageFromString(line string, from *ircClient) *ircMessage {
|
func NewIrcMessageFromString(line string, from *ircClient) *ircMessage {
|
||||||
|
|
||||||
msg, err := parseIrcLine(line)
|
msg, err := parseIrcLine(line)
|
||||||
|
msg.from = from
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
panic("wat")
|
panic("wat")
|
||||||
|
@ -1,11 +1,10 @@
|
|||||||
package sircd
|
package sircd
|
||||||
|
|
||||||
import (
|
import "github.com/sirupsen/logrus"
|
||||||
"github.com/sirupsen/logrus"
|
import "github.com/spf13/viper"
|
||||||
"net"
|
import "net"
|
||||||
)
|
|
||||||
|
|
||||||
type sircd struct {
|
type ircd struct {
|
||||||
Running bool
|
Running bool
|
||||||
serverName string
|
serverName string
|
||||||
log *logrus.Logger
|
log *logrus.Logger
|
||||||
@ -17,6 +16,7 @@ type sircd struct {
|
|||||||
newClients chan *ircClient
|
newClients chan *ircClient
|
||||||
deadClients chan *ircClient
|
deadClients chan *ircClient
|
||||||
messageQueue chan *ircMessage
|
messageQueue chan *ircMessage
|
||||||
|
c *viper.Viper
|
||||||
}
|
}
|
||||||
|
|
||||||
const (
|
const (
|
||||||
@ -25,23 +25,25 @@ const (
|
|||||||
CONN_TYPE = "tcp"
|
CONN_TYPE = "tcp"
|
||||||
)
|
)
|
||||||
|
|
||||||
func NewSircd() *sircd {
|
func New(config *viper.Viper) *ircd {
|
||||||
s := new(sircd)
|
s := new(ircd)
|
||||||
s.Running = true
|
s.Running = true
|
||||||
s.ircClients = make(map[*ircClient]bool)
|
s.ircClients = make(map[*ircClient]bool)
|
||||||
|
s.c = config
|
||||||
|
s.serverName = s.c.GetString("myhostname")
|
||||||
return s
|
return s
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *sircd) SetLogger(logger *logrus.Logger) {
|
func (s *ircd) SetLogger(logger *logrus.Logger) {
|
||||||
s.log = logger
|
s.log = logger
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *sircd) SetServerName(name string) {
|
func (s *ircd) SetServerName(name string) {
|
||||||
s.serverName = name
|
s.serverName = name
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *sircd) Start() {
|
func (s *ircd) Start() {
|
||||||
|
s.log.Infof("sircd version=%s buildtime=%s starting.", s.c.GetString("version"), s.c.GetString("buildtime"))
|
||||||
s.newClients = make(chan *ircClient, 128)
|
s.newClients = make(chan *ircClient, 128)
|
||||||
s.deadClients = make(chan *ircClient, 128)
|
s.deadClients = make(chan *ircClient, 128)
|
||||||
s.messageQueue = make(chan *ircMessage, 128)
|
s.messageQueue = make(chan *ircMessage, 128)
|
||||||
@ -51,7 +53,7 @@ func (s *sircd) Start() {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
s.log.Fatalln("Error listening:", err.Error())
|
s.log.Fatalln("Error listening:", err.Error())
|
||||||
}
|
}
|
||||||
s.log.Println("Listening for irc on " + CONN_HOST + ":" + CONN_PORT)
|
s.log.Println("Listening for irc proto on " + CONN_HOST + ":" + CONN_PORT)
|
||||||
|
|
||||||
go func() {
|
go func() {
|
||||||
conn, err := s.ircListener.Accept()
|
conn, err := s.ircListener.Accept()
|
||||||
@ -61,10 +63,22 @@ func (s *sircd) Start() {
|
|||||||
s.newClients <- newIrcClient(conn, s.log, s.messageQueue, s)
|
s.newClients <- newIrcClient(conn, s.log, s.messageQueue, s)
|
||||||
}()
|
}()
|
||||||
|
|
||||||
go s.mainloop()
|
// FIXME have this do 'go s.handlemessages()' and process an input channel
|
||||||
|
// of messages from all clients in its own goroutine, and then call
|
||||||
|
// directly into the main loop here and only do client i/o in this
|
||||||
|
// goroutine
|
||||||
|
go s.processMessages()
|
||||||
|
s.mainLoop()
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *sircd) mainloop() {
|
func (s *ircd) processMessages() {
|
||||||
|
for {
|
||||||
|
message := <-s.messageQueue
|
||||||
|
s.processIRCMessage(message)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *ircd) mainLoop() {
|
||||||
for {
|
for {
|
||||||
select {
|
select {
|
||||||
case client := <-s.newClients:
|
case client := <-s.newClients:
|
||||||
@ -76,6 +90,7 @@ func (s *sircd) mainloop() {
|
|||||||
nbyte, err := client.conn.Read(buf)
|
nbyte, err := client.conn.Read(buf)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
//this will surface it in the deadClients channel
|
//this will surface it in the deadClients channel
|
||||||
|
//reader below
|
||||||
client.Kill()
|
client.Kill()
|
||||||
break
|
break
|
||||||
} else {
|
} else {
|
||||||
@ -89,8 +104,6 @@ func (s *sircd) mainloop() {
|
|||||||
case deadClient := <-s.deadClients:
|
case deadClient := <-s.deadClients:
|
||||||
delete(s.ircClients, deadClient)
|
delete(s.ircClients, deadClient)
|
||||||
deadClient.CleanupAndClose()
|
deadClient.CleanupAndClose()
|
||||||
case message := <-s.messageQueue:
|
|
||||||
s.processIRCMessage(message)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,25 +1,67 @@
|
|||||||
package sircd
|
package sircd
|
||||||
|
|
||||||
type ircNick string
|
import log "github.com/sirupsen/logrus"
|
||||||
type ircRealName string
|
|
||||||
type hostname string
|
|
||||||
|
|
||||||
type ircUserSession struct {
|
type ircUserSession struct {
|
||||||
//FIXME add a mutex and protect during writes
|
//FIXME add a mutex and protect during writes
|
||||||
nick ircNick
|
nick string
|
||||||
realname ircRealName
|
user string
|
||||||
host hostname
|
specifiedHostname string
|
||||||
|
realName string
|
||||||
|
remoteHost string
|
||||||
|
state string
|
||||||
|
// FIXME make the connection check its state for invalidity periodically
|
||||||
|
// and .Kill() it if the irc protocol session wants the client
|
||||||
|
// connection gone
|
||||||
|
invalid bool
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewIrcUserSession() *ircUserSession {
|
func NewIrcUserSession() *ircUserSession {
|
||||||
// FIXME get conn.RemoteAddr passed in and stringify it here and put it
|
// FIXME get conn.RemoteAddr passed in and stringify it here and put it
|
||||||
// in the session
|
// in the session
|
||||||
s := new(ircUserSession)
|
s := new(ircUserSession)
|
||||||
s.nick = '*' //default for s2c messages pre-NICK
|
s.nick = "*" //default for s2c messages pre-NICK
|
||||||
|
s.state = "init"
|
||||||
|
s.invalid = false
|
||||||
return s
|
return s
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *ircUserSession) SetNick(input string) {
|
func (s *ircUserSession) CheckState(wantedState string) bool {
|
||||||
|
if s.state == wantedState {
|
||||||
|
return true
|
||||||
|
} else {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *ircUserSession) SetState(newState string) bool {
|
||||||
|
log.Infof("state changing: %s->%s", s.state, newState) //FIXME remove this
|
||||||
|
s.state = newState
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *ircUserSession) SetNick(input string) bool {
|
||||||
// FIXME check for valid nick-ness
|
// FIXME check for valid nick-ness
|
||||||
s.nick = input
|
s.nick = input
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *ircUserSession) SetUserInfo(params []string) bool {
|
||||||
|
if !s.CheckState("init") {
|
||||||
|
// can only do this when going init->normal
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
if s.nick == "*" {
|
||||||
|
// must set nick first
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
if len(params) >= 4 {
|
||||||
|
s.user = params[0]
|
||||||
|
s.specifiedHostname = params[1]
|
||||||
|
s.realName = params[3]
|
||||||
|
s.SetState("normal")
|
||||||
|
return true
|
||||||
|
} else {
|
||||||
|
return false
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user