diff --git a/README.md b/README.md index c2c4e7c..13af0a5 100644 --- a/README.md +++ b/README.md @@ -13,7 +13,7 @@ A user-friendly IRC bouncer. ## Usage go run ./cmd/sojuctl create-user - go run ./cmd/soju + go run ./cmd/soju -listen irc+insecure://127.0.0.1:6667 Then connect with username `/chat.freenode.net` and join `#soju`. diff --git a/cmd/soju/main.go b/cmd/soju/main.go index 5f80fb4..1eced71 100644 --- a/cmd/soju/main.go +++ b/cmd/soju/main.go @@ -5,15 +5,17 @@ import ( "flag" "log" "net" + "net/url" + "strings" "git.sr.ht/~emersion/soju" "git.sr.ht/~emersion/soju/config" ) func main() { - var addr, configPath string + var listen, configPath string var debug bool - flag.StringVar(&addr, "listen", "", "listening address") + flag.StringVar(&listen, "listen", "", "listening address") flag.StringVar(&configPath, "config", "", "path to configuration file") flag.BoolVar(&debug, "debug", false, "enable debug logging") flag.Parse() @@ -29,8 +31,11 @@ func main() { cfg = config.Defaults() } - if addr != "" { - cfg.Addr = addr + if listen != "" { + cfg.Listen = append(cfg.Listen, listen) + } + if len(cfg.Listen) == 0 { + cfg.Listen = []string{":6697"} } db, err := soju.OpenSQLDB(cfg.SQLDriver, cfg.SQLSource) @@ -38,24 +43,13 @@ func main() { log.Fatalf("failed to open database: %v", err) } - var ln net.Listener + var tlsCfg *tls.Config if cfg.TLS != nil { cert, err := tls.LoadX509KeyPair(cfg.TLS.CertPath, cfg.TLS.KeyPath) if err != nil { log.Fatalf("failed to load TLS certificate and key: %v", err) } - - tlsCfg := &tls.Config{Certificates: []tls.Certificate{cert}} - ln, err = tls.Listen("tcp", cfg.Addr, tlsCfg) - if err != nil { - log.Fatalf("failed to start TLS listener: %v", err) - } - } else { - var err error - ln, err = net.Listen("tcp", cfg.Addr) - if err != nil { - log.Fatalf("failed to start listener: %v", err) - } + tlsCfg = &tls.Config{Certificates: []tls.Certificate{cert}} } srv := soju.NewServer(db) @@ -64,11 +58,50 @@ func main() { srv.LogPath = cfg.LogPath srv.Debug = debug - log.Printf("server listening on %q", cfg.Addr) - go func() { - if err := srv.Run(); err != nil { - log.Fatal(err) + for _, listen := range cfg.Listen { + listenURI := listen + if !strings.Contains(listenURI, ":/") { + // This is a raw domain name, make it an URL with an empty scheme + listenURI = "//" + listenURI } - }() - log.Fatal(srv.Serve(ln)) + u, err := url.Parse(listenURI) + if err != nil { + log.Fatalf("failed to parse listen URI %q: %v", listen, err) + } + + switch u.Scheme { + case "ircs", "": + if tlsCfg == nil { + log.Fatalf("failed to listen on %q: missing TLS configuration", listen) + } + host := u.Host + if _, _, err := net.SplitHostPort(host); err != nil { + host = host + ":6697" + } + ln, err := tls.Listen("tcp", host, tlsCfg) + if err != nil { + log.Fatalf("failed to start TLS listener on %q: %v", listen, err) + } + go func() { + log.Fatal(srv.Serve(ln)) + }() + case "irc+insecure": + host := u.Host + if _, _, err := net.SplitHostPort(host); err != nil { + host = host + ":6667" + } + ln, err := net.Listen("tcp", host) + if err != nil { + log.Fatalf("failed to start listener on %q: %v", listen, err) + } + go func() { + log.Fatal(srv.Serve(ln)) + }() + default: + log.Fatalf("failed to listen on %q: unsupported scheme", listen) + } + + log.Printf("server listening on %q", listen) + } + log.Fatal(srv.Run()) } diff --git a/config/config.go b/config/config.go index 72c4a3b..81f7722 100644 --- a/config/config.go +++ b/config/config.go @@ -14,7 +14,7 @@ type TLS struct { } type Server struct { - Addr string + Listen []string Hostname string TLS *TLS SQLDriver string @@ -28,7 +28,6 @@ func Defaults() *Server { hostname = "localhost" } return &Server{ - Addr: ":6667", Hostname: hostname, SQLDriver: "sqlite3", SQLSource: "soju.db", @@ -68,9 +67,11 @@ func Parse(r io.Reader) (*Server, error) { for _, d := range directives { switch d.Name { case "listen": - if err := d.parseParams(&srv.Addr); err != nil { + var uri string + if err := d.parseParams(&uri); err != nil { return nil, err } + srv.Listen = append(srv.Listen, uri) case "hostname": if err := d.parseParams(&srv.Hostname); err != nil { return nil, err diff --git a/doc/soju.1.scd b/doc/soju.1.scd index db0a84d..8306439 100644 --- a/doc/soju.1.scd +++ b/doc/soju.1.scd @@ -56,15 +56,25 @@ be done by adding a "@" suffix to the username. Enable debug logging (this will leak sensitive information such as passwords). -*-listen*
- Listening address (default: ":6667"). +*-listen* + Listening URI (default: ":6697"). # CONFIG FILE The config file has one directive per line. -*listen*
- Listening address (default: ":6667"). +*listen* + Listening URI (default: ":6697"). + + The following URIs are supported: + + - _[ircs://][host][:port]_ listens with TLS over TCP (default port if + omitted: 6697) + - _irc+insecure://[host][:port]_ listens with plain-text over TCP (default + port if omitted: 6667) + + If the scheme is omitted, "ircs" is assumed. If multiple *listen* + directives are specified, soju will listen on each of them. *hostname* Server hostname (default: system hostname). @@ -97,6 +107,7 @@ abbreviated form, for instance *network* can be abbreviated as *net* or just Connect to a new network at _addr_. _-addr_ is mandatory. _addr_ supports several connection types: + - _[ircs://]host[:port]_ connects with TLS over TCP - _irc+insecure://host[:port]_ connects with plain-text TCP