13 Commits

Author SHA1 Message Date
Will Storey
b5ef6cc54d v1.13.0 2019-07-08 18:37:41 -07:00
Will Storey
fe77d1c963 Include Go version in version string 2019-07-08 18:37:10 -07:00
Will Storey
a5eeda0549 Update .gitignore 2019-07-06 13:03:11 -07:00
Will Storey
e1e0b15be9 v1.12.0 2019-07-06 12:58:50 -07:00
Will Storey
c5d6477825 Stop logging client reads/writes 2019-07-06 12:35:16 -07:00
Will Storey
0560dd406c Mention dependency update in changelog 2019-07-06 12:33:52 -07:00
Will Storey
ea0e612d3e Start client writer immediately
This lets us actually send messages to clients as they're ready during
setup.
2019-07-01 14:13:42 -07:00
Will Storey
6c093ca0b0 Send notices during connect immediately
This was confusing to clients if the DNS lookup was not instant.
2019-07-01 12:05:09 -07:00
Will Storey
242479c791 Update dependencies 2019-04-19 11:57:58 -07:00
Will Storey
e99b37e322 tests: Retry linking due to flakey build 2019-04-19 11:57:41 -07:00
Will Storey
19dfea1dc0 Convert some tests to testify 2019-04-19 11:06:23 -07:00
Will Storey
23084c52ca Update dependencies 2019-04-19 10:56:39 -07:00
Will Storey
b25e3fcb12 doc: Clarify release instructions 2019-01-01 12:57:12 -08:00
11 changed files with 159 additions and 105 deletions

3
.gitignore vendored
View File

@@ -1,2 +1,3 @@
catbox
*.swp
/catbox
/test-net

View File

@@ -1,3 +1,16 @@
# 1.13.0 (2019-07-08)
* Include Go version in version string.
# 1.12.0 (2019-07-06)
* Update dependencies.
* Send messages during connect immediately rather than only after we've
performed our reverse DNS lookup.
* Stop logging client reads/writes.
# 1.11.0 (2019-01-01)
* No longer automatically rehash once a week. I changed my mind about this!

View File

@@ -1,9 +1,9 @@
# Releasing
* Bump values in `version.go`
* Update `CHANGELOG.md`
* Commit
* `git commit -m vx.y.z`
* `git push`
* `git tag -a vx.y.z -m vx.y.z`
* `export GITHUB_TOKEN=tokenhere`
* `goreleaser`
* Don't need to push tags I believe, goreleaser does it.
* Don't need to push tags. goreleaser does it.

8
go.mod
View File

@@ -1,7 +1,9 @@
module github.com/horgh/catbox
require (
github.com/horgh/config v0.0.0-20190101202014-d9e8eabe6dbb
github.com/horgh/irc v0.0.0-20190101203129-f09ebee6408d
github.com/pkg/errors v0.8.0
github.com/horgh/config v0.0.0-20190101204049-770bc48a3bdf
github.com/horgh/irc v0.0.0-20190101204118-d089b0b5b5c5
github.com/pkg/errors v0.8.1
github.com/stretchr/objx v0.2.0 // indirect
github.com/stretchr/testify v1.3.0
)

15
go.sum
View File

@@ -1,13 +1,28 @@
github.com/davecgh/go-spew v1.1.0 h1:ZDRjVQ15GmhC3fiQ8ni8+OwkZQO4DARzQgrnXU1Liz8=
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/horgh/config v0.0.0-20180303191532-3d1f920eb228 h1:R302KZFIBabAYgFZ0hgqRTeCF43Lm5rir+UnJYW3idQ=
github.com/horgh/config v0.0.0-20180303191532-3d1f920eb228/go.mod h1:DSwQKBmwAzGuDhYajjeJshx5PCPCJfSZJXtbV+8/nck=
github.com/horgh/config v0.0.0-20190101202014-d9e8eabe6dbb h1:u6pj1d0h6XSjJ84iixIMvSZT1fbLC1g4qqkV54EMOfo=
github.com/horgh/config v0.0.0-20190101202014-d9e8eabe6dbb/go.mod h1:DSwQKBmwAzGuDhYajjeJshx5PCPCJfSZJXtbV+8/nck=
github.com/horgh/config v0.0.0-20190101204049-770bc48a3bdf h1:/jDikK0Oteboi7/Z6uzan5aQhiqwMwKTIA+5ZooDclk=
github.com/horgh/config v0.0.0-20190101204049-770bc48a3bdf/go.mod h1:DSwQKBmwAzGuDhYajjeJshx5PCPCJfSZJXtbV+8/nck=
github.com/horgh/irc v0.0.0-20180101050313-f421bdb90dcc h1:FXH8Jqdcz9BbR94qHrCVGA5FhbcWNC+HpIXYwVgOc2I=
github.com/horgh/irc v0.0.0-20180101050313-f421bdb90dcc/go.mod h1:UqEB9NVUSZzN4ESuQX3yEvi80Mgg2O4kttl8oU9+nds=
github.com/horgh/irc v0.0.0-20190101203129-f09ebee6408d h1:ANDjU4bIeLO80xssAxig8qftG6ohyg08IqsIPnKqafg=
github.com/horgh/irc v0.0.0-20190101203129-f09ebee6408d/go.mod h1:JLhFcwXOnpvhMer1MERfJuFIoJnADayDWe0VkMN3LP4=
github.com/horgh/irc v0.0.0-20190101204118-d089b0b5b5c5 h1:wndND79llNLTZZW/Xcg9oKMk/NuGMo+pAX+LKg1mZF8=
github.com/horgh/irc v0.0.0-20190101204118-d089b0b5b5c5/go.mod h1:JLhFcwXOnpvhMer1MERfJuFIoJnADayDWe0VkMN3LP4=
github.com/pkg/errors v0.8.0 h1:WdK/asTD0HN+q6hsWO3/vpuAkAr+tw6aNJNDFFf0+qw=
github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
github.com/pkg/errors v0.8.1 h1:iURUrRGxPUNPdy5/HRSm+Yj6okJ6UtLINN0Q9M4+h3I=
github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/objx v0.2.0/go.mod h1:qt09Ya8vawLte6SNmTgCsAVtYtaKzEcn8ATUoHMkEqE=
github.com/stretchr/testify v1.3.0 h1:TivCn/peBQ7UY8ooIcPgZFpTNSz0Q2U6UrFlUfqbe0Q=
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=

View File

@@ -176,8 +176,6 @@ func (c *LocalClient) readLoop() {
break
}
log.Printf("Client %s: Read: %s", c, strings.TrimSuffix(buf, "\r\n"))
message, err := irc.ParseMessage(buf)
if err != nil {
c.Catbox.noticeOpers(fmt.Sprintf("Invalid message from client %s: %s", c,
@@ -249,8 +247,6 @@ Loop:
c.Catbox.newEvent(Event{Type: DeadClientEvent, Client: c, Error: err})
break Loop
}
log.Printf("Client %s: Sent: %s", c, strings.TrimSuffix(buf, "\r\n"))
case <-c.Catbox.ShutdownChan:
break Loop
}
@@ -383,7 +379,8 @@ func (c *LocalClient) registerUser() {
lu.messageFromServer("002", []string{
fmt.Sprintf("Your host is %s, running version %s",
lu.Catbox.Config.ServerName,
Version),
lu.Catbox.version(),
),
})
// 003 RPL_CREATED
@@ -396,7 +393,7 @@ func (c *LocalClient) registerUser() {
lu.messageFromServer("004", []string{
// It seems ambiguous if these are to be separate parameters.
lu.Catbox.Config.ServerName,
Version,
lu.Catbox.version(),
// User modes we support.
"ioC",
// Channel modes we support.

View File

@@ -2094,8 +2094,6 @@ func (u *LocalUser) versionCommand(m irc.Message) {
// Comments are free form. But I use similar to what ratbox does. See its doc
// server-version-info.
version := fmt.Sprintf("%s.", Version)
// H HUB, M IDLE_FROM_MSG, TS supports TS, 6 TS6, o TS only
comments := fmt.Sprintf("HM TS6o %s", string(u.Catbox.Config.TS6SID))
@@ -2104,7 +2102,7 @@ func (u *LocalUser) versionCommand(m irc.Message) {
Command: "351",
Params: []string{
u.User.DisplayNick,
version,
u.Catbox.version(),
u.Catbox.Config.ServerName,
comments,
},

50
main.go
View File

@@ -9,6 +9,7 @@ import (
"os"
"os/signal"
"path/filepath"
"runtime"
"strings"
"sync"
"syscall"
@@ -633,16 +634,19 @@ func (cb *Catbox) introduceClient(conn net.Conn) {
client := NewLocalClient(cb, id, conn)
msgs := []string{
fmt.Sprintf("*** Processing your connection to %s",
cb.Config.ServerName),
}
cb.WG.Add(1)
go client.writeLoop()
sendAuthNotice(
client,
"*** Processing your connection to "+cb.Config.ServerName,
)
if client.isTLS() {
tlsVersion, tlsCipherSuite, err := client.getTLSState()
if err != nil {
log.Printf("Client %s: %s", client, err)
_ = conn.Close() // nolint: gosec
close(client.WriteChan)
return
}
@@ -656,48 +660,44 @@ func (cb *Catbox) introduceClient(conn net.Conn) {
"Your SSL/TLS version is %s. This server requires at least TLS 1.2. Contact %s if this is a problem.",
tlsVersion, cb.Config.AdminEmail)})
close(client.WriteChan)
cb.WG.Add(1)
go client.writeLoop()
return
}
msgs = append(msgs, fmt.Sprintf("*** Connected with %s (%s)", tlsVersion,
tlsCipherSuite))
sendAuthNotice(
client,
fmt.Sprintf("*** Connected with %s (%s)", tlsVersion, tlsCipherSuite),
)
}
msgs = append(msgs, "*** Looking up your hostname...")
sendAuthNotice(client, "*** Looking up your hostname...")
hostname := lookupHostname(context.TODO(), client.Conn.IP)
if len(hostname) > 0 {
msgs = append(msgs, "*** Found your hostname")
sendAuthNotice(client, "*** Found your hostname")
client.Hostname = hostname
} else {
msgs = append(msgs, "*** Couldn't look up your hostname")
}
for _, msg := range msgs {
client.WriteChan <- irc.Message{
Command: "NOTICE",
Params: []string{"AUTH", msg},
}
sendAuthNotice(client, "*** Couldn't look up your hostname")
}
// Inform the main server goroutine about the client.
//
// Do this after sending any messages to the client's channel as it is
// possible the channel will be closed by the server (such as during
// shutdown).
cb.newEvent(Event{Type: NewClientEvent, Client: client})
// Start read goroutine (endlessly read messages from the client) and write
// goroutine (endlessly write messages to the client).
cb.WG.Add(1)
go client.readLoop()
cb.WG.Add(1)
go client.writeLoop()
}()
}
func sendAuthNotice(c *LocalClient, m string) {
c.WriteChan <- irc.Message{
Command: "NOTICE",
Params: []string{"AUTH", m},
}
}
// Return true if the server is shutting down.
func (cb *Catbox) isShuttingDown() bool {
// No messages get sent to this channel, so if we receive a message on it,
@@ -1706,3 +1706,5 @@ func sendMessages(messages []Message) {
m.Target.maybeQueueMessage(m.Message)
}
}
func (cb *Catbox) version() string { return Version + "-" + runtime.Version() }

View File

@@ -16,6 +16,8 @@ import (
"sync"
"syscall"
"time"
"github.com/pkg/errors"
)
// Catbox holds information about a harnessed catbox.
@@ -281,11 +283,14 @@ func (c *Catbox) linkServer(other *Catbox) error {
return fmt.Errorf("error writing server conf: %s: %s", serversConf, err)
}
if err := c.Command.Process.Signal(syscall.SIGHUP); err != nil {
return fmt.Errorf("error sending SIGHUP: %s", err)
}
return c.rehash()
}
return nil
func (c *Catbox) rehash() error {
return errors.Wrap(
c.Command.Process.Signal(syscall.SIGHUP),
"error sending SIGHUP",
)
}
func waitForLog(ch <-chan string, re *regexp.Regexp) bool {

View File

@@ -7,6 +7,7 @@ import (
"time"
"github.com/horgh/irc"
"github.com/stretchr/testify/require"
)
// Test that clients get TS when running MODE on a channel they are on.
@@ -15,56 +16,72 @@ import (
// another server gets the same TS
func TestMODETS(t *testing.T) {
catbox1, err := harnessCatbox("irc1.example.org", "001")
if err != nil {
t.Fatalf("error harnessing catbox: %s", err)
}
require.NoError(t, err, "harness catbox")
defer catbox1.stop()
catbox2, err := harnessCatbox("irc2.example.org", "002")
if err != nil {
t.Fatalf("error harnessing catbox: %s", err)
}
require.NoError(t, err, "harness catbox")
defer catbox2.stop()
if err := catbox1.linkServer(catbox2); err != nil {
t.Fatalf("error linking catbox1 to catbox2: %s", err)
}
if err := catbox2.linkServer(catbox1); err != nil {
t.Fatalf("error linking catbox2 to catbox1: %s", err)
}
err = catbox1.linkServer(catbox2)
require.NoError(t, err, "link catbox1 to catbox2")
err = catbox2.linkServer(catbox1)
require.NoError(t, err, "link catbox2 to catbox1")
// Wait until we link.
//
// Retry rehashing as I observed a failing build where the second server did
// not receive the SIGHUP, yet didn't exit. I'm not sure how that can happen
// other than perhaps a race in signal.Notify() such that the signal handler
// is registered so the HUP gets received but not delivered to the channel.
linkRE := regexp.MustCompile(`Established link to irc2\.`)
if !waitForLog(catbox1.LogChan, linkRE) {
t.Fatalf("failed to see servers link")
var attempts int
for {
if waitForLog(catbox1.LogChan, linkRE) {
break
}
attempts++
if attempts >= 5 {
require.Fail(t, "failed to link")
}
require.NoError(t, err, catbox1.rehash(), "rehash catbox1")
require.NoError(t, err, catbox2.rehash(), "rehash catbox2")
}
client1 := NewClient("client1", "127.0.0.1", catbox1.Port)
recvChan1, sendChan1, _, err := client1.Start()
if err != nil {
t.Fatalf("error starting client: %s", err)
}
require.NoError(t, err, "start client")
defer client1.Stop()
if waitForMessage(t, recvChan1, irc.Message{Command: irc.ReplyWelcome},
"welcome from %s", client1.GetNick()) == nil {
t.Fatalf("client1 did not get welcome")
}
require.NotNil(
t,
waitForMessage(
t,
recvChan1,
irc.Message{Command: irc.ReplyWelcome},
"welcome from %s",
client1.GetNick(),
),
"client gets welcome",
)
sendChan1 <- irc.Message{
Command: "JOIN",
Params: []string{"#test"},
}
if waitForMessage(
require.NotNil(
t,
recvChan1,
irc.Message{
Command: "JOIN",
Params: []string{"#test"},
},
"%s received JOIN #test", client1.GetNick(),
) == nil {
t.Fatalf("client1 did not receive JOIN message")
}
waitForMessage(
t,
recvChan1,
irc.Message{
Command: "JOIN",
Params: []string{"#test"},
},
"%s received JOIN #test", client1.GetNick(),
),
"client gets JOIN message",
)
sendChan1 <- irc.Message{
Command: "MODE",
@@ -78,17 +95,13 @@ func TestMODETS(t *testing.T) {
},
"%s received 329 response after MODE command", client1.GetNick(),
)
if creationTimeMessage == nil {
t.Fatalf("client1 did not receive 329 response")
}
require.NotNil(t, creationTimeMessage, "client receives 329 response")
creationTimeString := ""
creationTime := time.Time{}
if len(creationTimeMessage.Params) >= 3 {
ct, err := strconv.ParseInt(creationTimeMessage.Params[2], 10, 64)
if err != nil {
t.Fatalf("error parsing 329 response unixtime: %s", err)
}
require.NoError(t, err, "parse 329 response unixtime")
creationTimeString = creationTimeMessage.Params[2]
creationTime = time.Unix(ct, 0)
}
@@ -103,39 +116,49 @@ func TestMODETS(t *testing.T) {
},
)
if time.Since(creationTime) > 30*time.Second {
t.Fatalf("channel creation time is too far in the past: %s", creationTime)
}
require.True(
t,
time.Since(creationTime) <= 30*time.Second,
"channel creation time is new enough",
)
// Try a client on the other server and ensure they get the same time.
client2 := NewClient("client2", "127.0.0.1", catbox2.Port)
recvChan2, sendChan2, _, err := client2.Start()
if err != nil {
t.Fatalf("error starting client: %s", err)
}
require.NoError(t, err, "start client 2")
defer client2.Stop()
if waitForMessage(t, recvChan2, irc.Message{Command: irc.ReplyWelcome},
"welcome from %s", client2.GetNick()) == nil {
t.Fatalf("client2 did not get welcome")
}
require.NotNil(
t,
waitForMessage(
t,
recvChan2,
irc.Message{Command: irc.ReplyWelcome},
"welcome from %s",
client2.GetNick(),
),
"client 2 gets welcome",
)
sendChan2 <- irc.Message{
Command: "JOIN",
Params: []string{"#test"},
}
if waitForMessage(
require.NotNil(
t,
recvChan2,
irc.Message{
Command: "JOIN",
Params: []string{"#test"},
},
"%s received JOIN #test", client2.GetNick(),
) == nil {
t.Fatalf("client2 did not receive JOIN message")
}
waitForMessage(
t,
recvChan2,
irc.Message{
Command: "JOIN",
Params: []string{"#test"},
},
"%s received JOIN #test",
client2.GetNick(),
),
"client 2 gets JOIN message",
)
sendChan2 <- irc.Message{
Command: "MODE",
@@ -149,9 +172,7 @@ func TestMODETS(t *testing.T) {
},
"%s received 329 response after MODE command", client2.GetNick(),
)
if creationTimeMessage == nil {
t.Fatalf("client2 did not receive 329 response")
}
require.NotNil(t, creationTimeMessage, "client 2 receives 329 response")
messageIsEqual(
t,

View File

@@ -2,7 +2,7 @@ package main
// CreatedDate is the date we're built. This would be nice to generate
// dynamically, but I don't want to complicate the build.
const CreatedDate = "2019-01-01"
const CreatedDate = "2019-07-08"
// Version is our version.
const Version = "catbox-1.11.0"
const Version = "catbox-1.13.0"