diff --git a/README.md b/README.md index 46c47c6..8f1f7db 100644 --- a/README.md +++ b/README.md @@ -36,12 +36,10 @@ The account data is expected at `~/.sendxmpprc` if no other configuration file l ```plain username: -jserver: -port: password: ``` -`jserver` and `port` might not be necessary depending on the used server. It is also possible to +If this is not sufficient to connect you might also specify `jserver` and `port`. It is also possible to use a password manager. In this case the `password` setting should be replaced by the `eval_password` setting: diff --git a/connect.go b/connect.go new file mode 100644 index 0000000..81b732e --- /dev/null +++ b/connect.go @@ -0,0 +1,101 @@ +// Copyright 2018 - 2021 Martin Dosch. +// Use of this source code is governed by the BSD-2-clause +// license that can be found in the LICENSE file.package main + +package main + +import ( + "fmt" + "net" + "sort" + "strings" + + "github.com/mattn/go-xmpp" // BSD-3-Clause +) + +type srv struct { + host string + xmpps bool + port uint16 + priority uint16 + weight uint16 +} +type srvMixed []srv + +type byPriority []srv + +func (o byPriority) Len() int { return len(o) } +func (o byPriority) Swap(i, j int) { o[i], o[j] = o[j], o[i] } +func (o byPriority) Less(i, j int) bool { + if o[i].priority == o[j].priority { + return o[i].weight > o[j].weight + } else { + return o[i].priority < o[j].priority + } +} + +func connect(options xmpp.Options, directTLS bool) (*xmpp.Client, error) { + + // Look up SRV records if server is not specified manually. + srvMixed := srvMixed{} + if options.Host == "" { + server := options.User[strings.LastIndex(options.User, "@")+1:] + // Look up xmpp-client SRV records. + if _, addrs, err := net.LookupSRV("xmpp-client", "tcp", server); err == nil { + if len(addrs) > 0 { + for _, adr := range addrs { + srvMixed = append(srvMixed, srv{adr.Target, false, + adr.Port, adr.Priority, adr.Weight}) + } + } + } + // Look up xmpps-client SRV records. + if _, addrs, err := net.LookupSRV("xmpps-client", "tcp", server); err == nil { + if len(addrs) > 0 { + for _, adr := range addrs { + srvMixed = append(srvMixed, srv{adr.Target, true, + adr.Port, adr.Priority, adr.Weight}) + } + } + } + if len(srvMixed) > 0 { + // Sort xmpp- and xmpps-client SRV records according to the priority + // and wight. + sort.Sort(byPriority(srvMixed)) + for _, adr := range srvMixed { + if !directTLS && !adr.xmpps { + // Use StartTLS + options.NoTLS = true + options.StartTLS = true + options.Host = fmt.Sprintf("%s:%d", adr.host, adr.port) + // Connect to server + client, err := options.NewClient() + if err == nil { + return client, err + } + } else if adr.xmpps { + // Use direct TLS + options.NoTLS = false + options.StartTLS = false + options.Host = fmt.Sprintf("%s:%d", adr.host, adr.port) + // Connect to server + client, err := options.NewClient() + if err == nil { + return client, err + } + + } + } + } + // Try port 5222 if no xmpp-client SRV records are provided. + options.NoTLS = true + options.StartTLS = true + options.Host = fmt.Sprintf("%s:%d", server, 5222) + // Connect to server + client, err := options.NewClient() + return client, err + } + // Connect to server + client, err := options.NewClient() + return client, err +} diff --git a/go-sendxmpp.go b/go-sendxmpp.go index 885919c..2819054 100644 --- a/go-sendxmpp.go +++ b/go-sendxmpp.go @@ -8,10 +8,8 @@ import ( "bufio" "crypto/tls" "errors" - "fmt" "io" "log" - "net" "os" "os/exec" "os/user" @@ -292,30 +290,6 @@ func main() { server = *flagServer } - // Determine server part if no server is specified. - if server == "" { - server = strings.Split(user, "@")[1] - - // Lookup SRV record for xmpps-client if direct TLS is requested - if *flagTLS { - if _, addrs, err := net.LookupSRV("xmpps-client", "tcp", server); err == nil { - - if len(addrs) > 0 { - // Default to first record - server = fmt.Sprintf("%s:%d", addrs[0].Target, addrs[0].Port) - defP := addrs[0].Priority - for _, adr := range addrs { - if adr.Priority < defP { - server = fmt.Sprintf("%s:%d", adr.Target, adr.Port) - defP = adr.Priority - } - } - } - } - } - - } - // Overwrite password if specified via command line flag. if *flagPassword != "" { password = *flagPassword @@ -334,20 +308,23 @@ func main() { // Use ALPN var tlsConfig tls.Config - tlsConfig.ServerName = strings.Split(user, "@")[1] + tlsConfig.ServerName = user[strings.LastIndex(user, "@")+1:] tlsConfig.NextProtos = append(tlsConfig.NextProtos, "xmpp-client") tlsConfig.InsecureSkipVerify = *flagSkipVerify // Set XMPP connection options. options := xmpp.Options{ - Host: server, - User: user, - Resource: *flagResource, - Password: password, - NoTLS: !*flagTLS, - StartTLS: !*flagTLS, - Debug: *flagDebug, - TLSConfig: &tlsConfig, + Host: server, + User: user, + // TODO: Check whether the timeout is reasonable or maybe make + // it configurable + DialTimeout: 1 * time.Second, + Resource: *flagResource, + Password: password, + NoTLS: !*flagTLS, + StartTLS: !*flagTLS, + Debug: *flagDebug, + TLSConfig: &tlsConfig, } // Read message from file. @@ -359,7 +336,7 @@ func main() { } // Connect to server. - client, err := options.NewClient() + client, err := connect(options, *flagTLS) if err != nil { log.Fatal(err) }