16 Commits

Author SHA1 Message Date
idk
a85762e2c7 fork from catbox 2021-10-21 16:05:00 -04:00
idk
186e949cb0 fork from catbox 2021-10-21 16:04:07 -04:00
idk
67698b4fa2 fork from catbox 2021-10-21 16:03:43 -04:00
idk
666d4b3fc9 Make catbox a library, add I2P support to catbox 2021-10-19 00:30:38 -04:00
Will Storey
07cbec85ee Update dependencies 2019-10-27 15:06:46 -07:00
Will Storey
0c960873e7 Use Go 1.13 2019-10-27 15:06:46 -07:00
Will Storey
8c414e2cc8 Support TLS 1.3 2019-10-27 15:06:43 -07:00
Will Storey
c15d706a4e Update go.sum 2019-10-27 14:18:10 -07:00
Will Storey
c4bc115f49 Recommend running via systemd service 2019-10-27 14:18:03 -07:00
Will Storey
558e0208f1 Stop publishing arm releases 2019-07-08 18:39:33 -07:00
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
27 changed files with 364 additions and 191 deletions

3
.gitignore vendored
View File

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

View File

@@ -4,7 +4,6 @@ builds:
- linux
goarch:
- amd64
- arm
archive:
wrap_in_directory: true
files:

View File

@@ -1,7 +1,20 @@
# 1.11.1
# 1.14.0
* Stop publishing arm releases.
* Support TLS 1.3.
# 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)

View File

@@ -1,11 +1,11 @@
![catbox](doc/catbox-with-text.png)
![terrarium](doc/terrarium-with-text.png)
[![Build
Status](https://travis-ci.org/horgh/catbox.svg)](https://travis-ci.org/horgh/catbox)
Status](https://travis-ci.org/eyedeekay/terrarium.svg)](https://travis-ci.org/eyedeekay/terrarium)
[![Go Report
Card](https://goreportcard.com/badge/github.com/horgh/catbox)](https://goreportcard.com/report/github.com/horgh/catbox)
Card](https://goreportcard.com/badge/i2pgit.org/idk/terrarium)](https://goreportcard.com/report/i2pgit.org/idk/terrarium)
catbox is an IRC server with a focus on being small and understandable. The
terrarium is an IRC server with a focus on being small and understandable. The
goal is security.
@@ -17,24 +17,33 @@ goal is security.
* K: line style connection banning
* TLS
catbox implements enough of [RFC 1459](https://tools.ietf.org/html/rfc1459)
terrarium implements enough of [RFC 1459](https://tools.ietf.org/html/rfc1459)
to be recognisable as IRC and be minimally functional. I likely won't add
much more and don't intend it to be complete. If I don't think something is
required it likely won't be here.
# Installation
1. Download catbox from the Releases tab on GitHub, or build from source
1. Download terrarium from the Releases tab on GitHub, or build from source
(`go build`).
2. Configure catbox through config files. There are example configs in the
2. Configure terrarium through config files. There are example configs in the
`conf` directory. All settings are optional and have defaults.
3. Run it, e.g. `./catbox -conf catbox.conf`. I typically run catbox
inside tmux using [this program](bin/tmux-run.sh).
3. Run it, e.g. `./terrarium -conf terrarium.conf`. You might run it via systemd
via a service such as:
```
[Service]
ExecStart=/home/ircd/terrarium/terrarium -conf /home/ircd/terrarium/terrarium.conf
Restart=always
[Install]
WantedBy=default.target
```
# Configuration
## catbox.conf
## terrarium.conf
Global server settings.
@@ -70,4 +79,4 @@ ircd-ratbox, the IRC daemon I used in the past.
# Logo
catbox logo (c) 2017 Bee
terrarium logo (c) 2017 Bee

View File

@@ -1,4 +1,4 @@
package main
package terrarium
import (
"flag"
@@ -13,7 +13,7 @@ type Args struct {
ListenFD int
}
func getArgs() *Args {
func GetArgs() *Args {
configFile := flag.String("conf", "", "Configuration file.")
fd := flag.Int("listen-fd", -1,
"File descriptor with listening port to use (optional).")

View File

@@ -1,24 +0,0 @@
#!/bin/bash
#
# This is a way to run catbox in a tmux session.
#
# It runs in such a way as if catbox exits, the tmux window stays around so
# that we can inspect catbox's recent output. This is useful for debugging.
#
# It would probably be better to run catbox via systemd or something, but I
# don't want catbox's output to be logged anywhere. I only want recent output
# to be accessible. Possibly systemd could be made to do that, but anyway.
set -e
tmux start-server
tmux new-session -d -s catbox
tmux set-option -g set-remain-on-exit on
tmux set-option -g history-limit 10000
tmux set-option -g prefix2 C-a
tmux set-option -g prefix C-a
tmux bind-key C-a send-prefix
tmux set-window-option -g mode-keys vi
tmux new-window /home/ircd/catbox/catbox -conf /home/ircd/catbox/catbox.conf

View File

@@ -1,4 +1,4 @@
package main
package terrarium
import "github.com/horgh/irc"

55
cmd/terrarium/catbox.go Normal file
View File

@@ -0,0 +1,55 @@
package main
import (
"log"
"os"
"path/filepath"
"syscall"
"i2pgit.org/idk/terrarium"
)
func main() {
log.SetFlags(log.Ldate | log.Ltime)
log.SetOutput(os.Stdout)
args := terrarium.GetArgs()
if args == nil {
os.Exit(1)
}
binPath, err := filepath.Abs(os.Args[0])
if err != nil {
log.Fatalf("Unable to determine absolute path to binary: %s: %s",
os.Args[0], err)
}
cb, err := terrarium.NewCatbox(args.ConfigFile)
if err != nil {
log.Fatal(err)
}
if err := cb.Start(args.ListenFD); err != nil {
log.Fatal(err)
}
if cb.Restart {
log.Printf("Shutdown completed. Restarting...")
if err := syscall.Exec( // nolint: gas
binPath,
[]string{
binPath,
"-conf",
cb.ConfigFile,
},
nil,
); err != nil {
log.Fatalf("Restart failed: %s", err)
}
log.Fatalf("not reached")
}
log.Printf("Server shutdown cleanly.")
}

61
conf/catbox-i2p.conf Normal file
View File

@@ -0,0 +1,61 @@
# The main terrarium config.
#
# The commented options are the defaults which are used if you do not specify
# the option.
# Host to listen on.
#listen-host = 0.0.0.0
# Port to listen on. Set -1 to not listen.
#listen-port = -1
# Port to listen on (TLS). Set -1 to not listen.
#listen-port-tls = -1
listen-i2p = terrarium.i2p
sam-address = 127.0.0.1:7656
# File containing server certificate for TLS. PEM encoded.
# Must be set if you have a TLS listen port.
#certificate-file =
# File containing server key for TLS. PEM encoded.
# Must be set if you have a TLS listen port.
#key-file =
# Name server goes by.
server-name = irc.terrarium.i2p
# Short info line (shown in WHOIS).
#server-info = IRC
# MOTD. Only one line at this time.
#motd = Hello this is terrarium
# Maximum nick length. RFCs say 9, but longer is okay.
#max-nick-length = 9
# Maximum period of time a client can be idle before we ping it.
#ping-time = 30s
# Maximum period of time a client can be idle before we consider it dead.
#dead-time = 240s
# Time to wait between attempts connecting to servers (minimum).
#connect-attempt-time = 60s
# TS6 SID. Must be unique in the network. Format: [0-9][A-Z0-9]{2}
#ts6-sid = 000
# Administrator's email. It gets displayed in some errors.
#admin-email =
# Path to opers configuration. This defines server operators.
#opers-config =
# Path to servers configuration. This defines servers to link with.
#servers-config =
# Path to the users configuration. This defines spoofs and whether users are
# exempt from flood protection.
#users-config =

View File

@@ -1,4 +1,4 @@
# The main catbox config.
# The main terrarium config.
#
# The commented options are the defaults which are used if you do not specify
# the option.
@@ -27,7 +27,7 @@
#server-info = IRC
# MOTD. Only one line at this time.
#motd = Hello this is catbox
#motd = Hello this is terrarium
# Maximum nick length. RFCs say 9, but longer is okay.
#max-nick-length = 9

View File

@@ -1,4 +1,4 @@
package main
package terrarium
import (
"fmt"
@@ -18,6 +18,11 @@ type Config struct {
KeyFile string
ServerName string
// Listen on Hidden Service addresses
ListenI2P string
ListenI2PTLS string
SAMAddress string
// Description of server. This shows in WHOIS, etc.
ServerInfo string
@@ -101,6 +106,21 @@ func checkAndParseConfig(file string) (*Config, error) {
c.ListenPortTLS = m["listen-port-tls"]
}
c.ListenI2P = "-1"
if m["listen-i2p"] != "" {
c.ListenI2P = m["listen-i2p"]
}
c.ListenI2PTLS = "-1"
if m["listen-i2p-tls"] != "" {
c.ListenI2PTLS = m["listen-i2p-tls"]
}
c.SAMAddress = "127.0.0.1:7656"
if m["sam-address"] != "" {
c.SAMAddress = m["sam-address"]
}
if m["certificate-file"] != "" {
c.CertificateFile = m["certificate-file"]
}
@@ -119,7 +139,7 @@ func checkAndParseConfig(file string) (*Config, error) {
c.ServerInfo = m["server-info"]
}
c.MOTD = "Hello this is catbox"
c.MOTD = "Hello this is terrarium"
if m["motd"] != "" {
c.MOTD = m["motd"]
}

12
go.mod
View File

@@ -1,9 +1,15 @@
module github.com/horgh/catbox
module i2pgit.org/idk/terrarium
require (
github.com/davecgh/go-spew v1.1.1 // indirect
github.com/eyedeekay/sam3 v0.32.32
github.com/horgh/config v0.0.0-20190101204049-770bc48a3bdf
github.com/horgh/irc v0.0.0-20190101204118-d089b0b5b5c5
github.com/kr/pretty v0.1.0 // indirect
github.com/pkg/errors v0.8.1
github.com/stretchr/objx v0.2.0 // indirect
github.com/stretchr/testify v1.3.0
github.com/stretchr/testify v1.4.0
gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15 // indirect
gopkg.in/yaml.v2 v2.2.4 // indirect
)
go 1.13

28
go.sum
View File

@@ -1,28 +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 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
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/eyedeekay/sam3 v0.32.32 h1:9Ea1Ere5O8Clx8zYxKnvhrWy7R96Q4FvxlPskYf8VW0=
github.com/eyedeekay/sam3 v0.32.32/go.mod h1:qRA9KIIVxbrHlkj+ZB+OoxFGFgdKeGp1vSgPw26eOVU=
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/kr/pretty v0.1.0 h1:L/CwN0zerZDmRFUapSPitk6f+Q3+0za1rQkzVuMiMFI=
github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE=
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
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=
github.com/stretchr/testify v1.4.0 h1:2E4SXV/wtOkTonXsotYi4li6zVWxYlZuYNCXe9XRJyk=
github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
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/check.v1 v1.0.0-20190902080502-41f04d3bba15 h1:YR8cESwS4TdDjEe65xsg0ogRM/Nc3DYOhEAlW+xobZo=
gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v2 v2.2.4 h1:/eiJrUcujPVeJ3xlSWaiNi3uSVmDGBK1pDHUHAnao1I=
gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=

View File

@@ -1,4 +1,4 @@
package main
package terrarium
import (
"fmt"

View File

@@ -1,4 +1,4 @@
package main
package terrarium
import (
"crypto/tls"
@@ -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.
@@ -433,13 +430,13 @@ func (c *LocalClient) registerUser() {
})
// Send a CLICONN message. This is a custom command I built into ratbox
// so that local opers can know about remote connections. For catbox we
// so that local opers can know about remote connections. For terrarium we
// don't need to handle this to know about remote connections as I inform
// local operators about remote users connecting in the UID command, but to
// allow my ratbox servers to know about connections to ratbox, send CLICONN
// (for now). If I ever stop running all ratbox servers on my network, this
// can be removed.
// catbox should propagate this command though.
// terrarium should propagate this command though.
server.maybeQueueMessage(irc.Message{
Prefix: string(u.UID),
Command: "CLICONN",

View File

@@ -1,4 +1,4 @@
package main
package terrarium
import (
"fmt"

View File

@@ -1,4 +1,4 @@
package main
package terrarium
import (
"fmt"
@@ -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,
},

144
main.go
View File

@@ -1,14 +1,15 @@
package main
package terrarium
import (
"context"
"crypto/tls"
"fmt"
"io/ioutil"
"log"
"net"
"os"
"os/signal"
"path/filepath"
"runtime"
"strings"
"sync"
"syscall"
@@ -18,6 +19,10 @@ import (
"github.com/pkg/errors"
)
import (
"github.com/eyedeekay/sam3/helper"
)
// Catbox holds the state for this local server.
// I put everything global to a server in an instance of struct rather than
// have global variables.
@@ -94,6 +99,10 @@ type Catbox struct {
Listener net.Listener
TLSListener net.Listener
// I2P Streaming and I2P+TLS listeners.
I2PListener net.Listener
I2PListenerTLS net.Listener
// WaitGroup to ensure all goroutines clean up before we end.
WG sync.WaitGroup
@@ -197,52 +206,7 @@ const ExcessFloodThreshold = 50
// from a user.
const ChanModesPerCommand = 4
func main() {
log.SetFlags(log.Ldate | log.Ltime)
log.SetOutput(os.Stdout)
args := getArgs()
if args == nil {
os.Exit(1)
}
binPath, err := filepath.Abs(os.Args[0])
if err != nil {
log.Fatalf("Unable to determine absolute path to binary: %s: %s",
os.Args[0], err)
}
cb, err := newCatbox(args.ConfigFile)
if err != nil {
log.Fatal(err)
}
if err := cb.start(args.ListenFD); err != nil {
log.Fatal(err)
}
if cb.Restart {
log.Printf("Shutdown completed. Restarting...")
if err := syscall.Exec( // nolint: gas
binPath,
[]string{
binPath,
"-conf",
cb.ConfigFile,
},
nil,
); err != nil {
log.Fatalf("Restart failed: %s", err)
}
log.Fatalf("not reached")
}
log.Printf("Server shutdown cleanly.")
}
func newCatbox(configFile string) (*Catbox, error) {
func NewCatbox(configFile string) (*Catbox, error) {
cb := Catbox{
ConfigFile: configFile,
LocalClients: make(map[uint64]*LocalClient),
@@ -327,7 +291,7 @@ func (cb *Catbox) loadCertificate() error {
//
// We open the TCP port, start goroutines, and then receive messages on our
// channels.
func (cb *Catbox) start(listenFD int) error {
func (cb *Catbox) Start(listenFD int) error {
if listenFD == -1 && cb.Config.ListenPort == "-1" &&
cb.Config.ListenPortTLS == "-1" {
log.Fatalf("You must set a listen port.")
@@ -372,6 +336,49 @@ func (cb *Catbox) start(listenFD int) error {
go cb.acceptConnections(cb.TLSListener)
}
// I2P Listener
if cb.Config.ListenI2P != "-1" {
ln, err := sam.I2PListener(cb.Config.ListenI2P, cb.Config.SAMAddress, cb.Config.ListenI2P)
if err != nil {
return fmt.Errorf("unable to listen (I2P): %s", err)
}
cb.I2PListener = ln
err = ioutil.WriteFile(cb.Config.ListenI2P+".i2paddresshelper", []byte("http://"+cb.Config.ListenI2P+"/?i2paddresshelper="+cb.I2PListener.Addr().String()), 0644)
if err != nil {
return fmt.Errorf("unable to write I2P addresshelper link to file: %s", err)
}
if strings.HasSuffix(cb.Config.ServerName, ".i2p") {
err = ioutil.WriteFile(cb.Config.ServerName+".i2paddresshelper", []byte("http://"+cb.Config.ServerName+"/?i2paddresshelper="+cb.I2PListener.Addr().String()), 0644)
if err != nil {
return fmt.Errorf("unable to write I2P addresshelper link to file: %s", err)
}
}
cb.WG.Add(1)
go cb.acceptConnections(cb.Listener)
}
// I2P Listener with TLS
if cb.Config.ListenI2PTLS != "-1" {
ln, err := sam.I2PListener(cb.Config.ListenI2P, cb.Config.SAMAddress, cb.Config.ListenI2P)
if err != nil {
return fmt.Errorf("unable to listen (I2P): %s", err)
}
tlsln := tls.NewListener(ln, cb.TLSConfig)
cb.I2PListenerTLS = tlsln
err = ioutil.WriteFile(cb.Config.ListenI2P+".i2paddresshelper", []byte("http://"+cb.Config.ListenI2P+"?i2paddresshelper="+cb.I2PListener.Addr().String()), 0644)
if err != nil {
return fmt.Errorf("unable to write I2P addresshelper link to file: %s", err)
}
if strings.HasSuffix(cb.Config.ServerName, ".i2p") {
err = ioutil.WriteFile(cb.Config.ServerName+".i2paddresshelper", []byte("http://"+cb.Config.ServerName+"?i2paddresshelper="+cb.I2PListener.Addr().String()), 0644)
if err != nil {
return fmt.Errorf("unable to write I2P addresshelper link to file: %s", err)
}
}
cb.WG.Add(1)
go cb.acceptConnections(cb.Listener)
}
// Alarm is a goroutine to wake up this one periodically so we can do things
// like ping clients.
cb.WG.Add(1)
@@ -413,7 +420,7 @@ func (cb *Catbox) start(listenFD int) error {
}
}()
log.Printf("catbox started")
log.Printf("terrarium started")
cb.eventLoop()
// We don't need to drain any channels. None close that will have any
@@ -649,7 +656,7 @@ func (cb *Catbox) introduceClient(conn net.Conn) {
return
}
if tlsVersion != "TLS 1.2" {
if tlsVersion != "TLS 1.2" && tlsVersion != "TLS 1.3" {
cb.noticeOpers(fmt.Sprintf("Rejecting client %s using %s",
client.Conn.IP, tlsVersion))
// Send ERROR and start up the writer to try to let them get it. Don't
@@ -996,13 +1003,34 @@ func (cb *Catbox) connectToServer(linkInfo *ServerDefinition) {
var err error
if linkInfo.TLS {
cb.noticeOpers(fmt.Sprintf("Connecting to %s with TLS...", linkInfo.Name))
if strings.HasSuffix(linkInfo.Hostname, ".i2p") {
cb.noticeOpers(fmt.Sprintf("Connecting to %s with I2P and TLS...", linkInfo.Name))
dialer := &net.Dialer{
Timeout: cb.Config.DeadTime,
cb.noticeOpers(fmt.Sprintf("Connecting to %s with I2P...",
linkInfo.Name))
I2PSession, err := sam.I2PStreamSession(cb.Config.ListenI2P+"-tls-"+linkInfo.Hostname, cb.Config.SAMAddress, cb.Config.ListenI2P+"-tls-"+linkInfo.Hostname)
if err == nil {
conn, err = I2PSession.Dial("tcp", linkInfo.Hostname)
if err == nil {
conn = tls.Client(conn, cb.TLSConfig)
}
}
} else {
cb.noticeOpers(fmt.Sprintf("Connecting to %s with TLS...", linkInfo.Name))
dialer := &net.Dialer{
Timeout: cb.Config.DeadTime,
}
conn, err = tls.DialWithDialer(dialer, "tcp",
fmt.Sprintf("%s:%d", linkInfo.Hostname, linkInfo.Port), cb.TLSConfig)
}
} else if strings.HasSuffix(linkInfo.Hostname, ".i2p") {
cb.noticeOpers(fmt.Sprintf("Connecting to %s with I2P...",
linkInfo.Name))
I2PSession, err := sam.I2PStreamSession(cb.Config.ListenI2P+"-"+linkInfo.Hostname, cb.Config.SAMAddress, cb.Config.ListenI2P+"-"+linkInfo.Hostname)
if err == nil {
conn, err = I2PSession.Dial("tcp", linkInfo.Hostname)
}
conn, err = tls.DialWithDialer(dialer, "tcp",
fmt.Sprintf("%s:%d", linkInfo.Hostname, linkInfo.Port), cb.TLSConfig)
} else {
cb.noticeOpers(fmt.Sprintf("Connecting to %s without TLS...",
linkInfo.Name))
@@ -1029,7 +1057,7 @@ func (cb *Catbox) connectToServer(linkInfo *ServerDefinition) {
return
}
if tlsVersion != "TLS 1.2" {
if tlsVersion != "TLS 1.2" && tlsVersion != "TLS 1.3" {
cb.noticeOpers(fmt.Sprintf(
"Disconnecting from %s because of TLS version: %s", linkInfo.Name,
tlsVersion))
@@ -1705,3 +1733,5 @@ func sendMessages(messages []Message) {
m.Target.maybeQueueMessage(m.Message)
}
}
func (cb *Catbox) version() string { return Version + "-" + runtime.Version() }

View File

@@ -1,4 +1,4 @@
package main
package terrarium
import (
"fmt"

2
net.go
View File

@@ -1,4 +1,4 @@
package main
package terrarium
import (
"bufio"

View File

@@ -1,4 +1,4 @@
package main
package terrarium
import "fmt"

View File

@@ -20,7 +20,7 @@ import (
"github.com/pkg/errors"
)
// Catbox holds information about a harnessed catbox.
// Catbox holds information about a harnessed terrarium.
type Catbox struct {
Name string
SID string
@@ -33,19 +33,19 @@ type Catbox struct {
LogChan <-chan string
}
const catboxDir = ".."
const terrariumDir = ".."
func harnessCatbox(
name,
sid string,
) (*Catbox, error) {
if err := buildCatbox(); err != nil {
return nil, fmt.Errorf("error building catbox: %s", err)
return nil, fmt.Errorf("error building terrarium: %s", err)
}
catbox, err := startCatbox(name, sid)
terrarium, err := startCatbox(name, sid)
if err != nil {
return nil, fmt.Errorf("error starting catbox: %s", err)
return nil, fmt.Errorf("error starting terrarium: %s", err)
}
var wg sync.WaitGroup
@@ -53,34 +53,34 @@ func harnessCatbox(
logChan := make(chan string, 1024)
wg.Add(1)
go logReader(&wg, fmt.Sprintf("%s stderr", name), catbox.Stderr, logChan)
go logReader(&wg, fmt.Sprintf("%s stderr", name), terrarium.Stderr, logChan)
wg.Add(1)
go logReader(&wg, fmt.Sprintf("%s stdout", name), catbox.Stdout, logChan)
go logReader(&wg, fmt.Sprintf("%s stdout", name), terrarium.Stdout, logChan)
wg.Add(1)
go func() {
defer wg.Done()
if err := catbox.Command.Wait(); err != nil {
log.Printf("catbox exited: %s", err)
if err := terrarium.Command.Wait(); err != nil {
log.Printf("terrarium exited: %s", err)
}
}()
catbox.WaitGroup = &wg
catbox.LogChan = logChan
terrarium.WaitGroup = &wg
terrarium.LogChan = logChan
// It is important to wait for catbox to fully start. If we don't, then
// It is important to wait for terrarium to fully start. If we don't, then
// certain things we do in tests will not work well. For example, trying to
// reload the conf by sending a SIGHUP will kill the process.
startedRE := regexp.MustCompile(
`^\d{4}/\d{2}/\d{2} \d{2}:\d{2}:\d{2} catbox started$`)
`^\d{4}/\d{2}/\d{2} \d{2}:\d{2}:\d{2} terrarium started$`)
if !waitForLog(logChan, startedRE) {
catbox.stop()
return nil, fmt.Errorf("error waiting for catbox to start")
terrarium.stop()
return nil, fmt.Errorf("error waiting for terrarium to start")
}
return catbox, nil
return terrarium, nil
}
var builtCatbox bool
@@ -91,12 +91,12 @@ func buildCatbox() error {
}
cmd := exec.Command("go", "build")
cmd.Dir = catboxDir
cmd.Dir = terrariumDir
log.Printf("Running %s in [%s]...", cmd.Args, cmd.Dir)
output, err := cmd.CombinedOutput()
if err != nil {
return fmt.Errorf("error building catbox: %s: %s", err, output)
return fmt.Errorf("error building terrarium: %s: %s", err, output)
}
builtCatbox = true
@@ -112,7 +112,7 @@ func startCatbox(
return nil, fmt.Errorf("error retrieving a temporary directory: %s", err)
}
catboxConf := filepath.Join(tmpDir, "catbox.conf")
terrariumConf := filepath.Join(tmpDir, "terrarium.conf")
listener, port, err := getRandomPort()
if err != nil {
@@ -120,15 +120,15 @@ func startCatbox(
return nil, fmt.Errorf("error opening random port: %s", err)
}
catbox, err := runCatbox(catboxConf, listener, port, name, sid)
terrarium, err := runCatbox(terrariumConf, listener, port, name, sid)
if err != nil {
_ = os.RemoveAll(tmpDir)
_ = listener.Close()
return nil, fmt.Errorf("error running catbox: %s", err)
return nil, fmt.Errorf("error running terrarium: %s", err)
}
catbox.ConfigDir = tmpDir
return catbox, nil
terrarium.ConfigDir = tmpDir
return terrarium, nil
}
func getRandomPort() (net.Listener, uint16, error) {
@@ -161,12 +161,12 @@ func runCatbox(
return nil, err
}
cmd := exec.Command("./catbox",
cmd := exec.Command("./terrarium",
"-conf", conf,
"-listen-fd", "3",
)
cmd.Dir = catboxDir
cmd.Dir = terrariumDir
f, err := ln.(*net.TCPListener).File()
if err != nil {
@@ -257,7 +257,7 @@ func logReader(
func (c *Catbox) stop() {
if err := c.Command.Process.Kill(); err != nil {
log.Printf("error killing catbox: %s", err)
log.Printf("error killing terrarium: %s", err)
}
c.WaitGroup.Wait()
@@ -267,7 +267,7 @@ func (c *Catbox) stop() {
}
func (c *Catbox) linkServer(other *Catbox) error {
conf := filepath.Join(c.ConfigDir, "catbox.conf")
conf := filepath.Join(c.ConfigDir, "terrarium.conf")
serversConf := filepath.Join(c.ConfigDir, "servers.conf")
extra := fmt.Sprintf("servers-config = %s", serversConf)

View File

@@ -11,20 +11,20 @@ import (
// Test one client sending a message to another client.
func TestPRIVMSG(t *testing.T) {
catbox, err := harnessCatbox("irc.example.org", "000")
terrarium, err := harnessCatbox("irc.example.org", "000")
if err != nil {
t.Fatalf("error harnessing catbox: %s", err)
t.Fatalf("error harnessing terrarium: %s", err)
}
defer catbox.stop()
defer terrarium.stop()
client1 := NewClient("client1", "127.0.0.1", catbox.Port)
client1 := NewClient("client1", "127.0.0.1", terrarium.Port)
recvChan1, sendChan1, _, err := client1.Start()
if err != nil {
t.Fatalf("error starting client: %s", err)
}
defer client1.Stop()
client2 := NewClient("client2", "127.0.0.1", catbox.Port)
client2 := NewClient("client2", "127.0.0.1", terrarium.Port)
recvChan2, _, _, err := client2.Start()
if err != nil {
t.Fatalf("error starting client: %s", err)

View File

@@ -15,18 +15,18 @@ import (
// Also test that the TS gets propagated between servers and a client on
// another server gets the same TS
func TestMODETS(t *testing.T) {
catbox1, err := harnessCatbox("irc1.example.org", "001")
require.NoError(t, err, "harness catbox")
defer catbox1.stop()
terrarium1, err := harnessCatbox("irc1.example.org", "001")
require.NoError(t, err, "harness terrarium")
defer terrarium1.stop()
catbox2, err := harnessCatbox("irc2.example.org", "002")
require.NoError(t, err, "harness catbox")
defer catbox2.stop()
terrarium2, err := harnessCatbox("irc2.example.org", "002")
require.NoError(t, err, "harness terrarium")
defer terrarium2.stop()
err = catbox1.linkServer(catbox2)
require.NoError(t, err, "link catbox1 to catbox2")
err = catbox2.linkServer(catbox1)
require.NoError(t, err, "link catbox2 to catbox1")
err = terrarium1.linkServer(terrarium2)
require.NoError(t, err, "link terrarium1 to terrarium2")
err = terrarium2.linkServer(terrarium1)
require.NoError(t, err, "link terrarium2 to terrarium1")
// Wait until we link.
//
@@ -37,18 +37,18 @@ func TestMODETS(t *testing.T) {
linkRE := regexp.MustCompile(`Established link to irc2\.`)
var attempts int
for {
if waitForLog(catbox1.LogChan, linkRE) {
if waitForLog(terrarium1.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")
require.NoError(t, err, terrarium1.rehash(), "rehash terrarium1")
require.NoError(t, err, terrarium2.rehash(), "rehash terrarium2")
}
client1 := NewClient("client1", "127.0.0.1", catbox1.Port)
client1 := NewClient("client1", "127.0.0.1", terrarium1.Port)
recvChan1, sendChan1, _, err := client1.Start()
require.NoError(t, err, "start client")
defer client1.Stop()
@@ -110,7 +110,7 @@ func TestMODETS(t *testing.T) {
t,
creationTimeMessage,
&irc.Message{
Prefix: catbox1.Name,
Prefix: terrarium1.Name,
Command: "329",
Params: []string{client1.GetNick(), "#test", creationTimeString},
},
@@ -124,7 +124,7 @@ func TestMODETS(t *testing.T) {
// Try a client on the other server and ensure they get the same time.
client2 := NewClient("client2", "127.0.0.1", catbox2.Port)
client2 := NewClient("client2", "127.0.0.1", terrarium2.Port)
recvChan2, sendChan2, _, err := client2.Start()
require.NoError(t, err, "start client 2")
defer client2.Stop()
@@ -178,7 +178,7 @@ func TestMODETS(t *testing.T) {
t,
creationTimeMessage2,
&irc.Message{
Prefix: catbox2.Name,
Prefix: terrarium2.Name,
Command: "329",
Params: []string{client2.GetNick(), "#test", creationTimeString},
},

View File

@@ -1,4 +1,4 @@
package main
package terrarium
import (
"fmt"

10
util.go
View File

@@ -1,4 +1,4 @@
package main
package terrarium
import (
"context"
@@ -439,6 +439,8 @@ func tlsVersionToString(version uint16) string {
return "TLS 1.1"
case tls.VersionTLS12:
return "TLS 1.2"
case tls.VersionTLS13:
return "TLS 1.3"
default:
return fmt.Sprintf("Unknown TLS version %x", version)
}
@@ -490,6 +492,12 @@ func cipherSuiteToString(suite uint16) string {
return "TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305"
case tls.TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305:
return "TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305"
case tls.TLS_AES_128_GCM_SHA256:
return "TLS_AES_128_GCM_SHA256"
case tls.TLS_AES_256_GCM_SHA384:
return "TLS_AES_256_GCM_SHA384"
case tls.TLS_CHACHA20_POLY1305_SHA256:
return "TLS_CHACHA20_POLY1305_SHA256"
default:
return fmt.Sprintf("Unknown cipher suite %x", suite)
}

View File

@@ -1,8 +1,8 @@
package main
package terrarium
// 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 = "terrarium-1.13.0"