pax_global_header00006660000000000000000000000064145525371000014514gustar00rootroot0000000000000052 comment=0e8b28d2fb84a418b5fc39355d1fb2acfa28bc25 xmppsrv-v0.2.6/000077500000000000000000000000001455253710000134265ustar00rootroot00000000000000xmppsrv-v0.2.6/.gitlab-ci.yml000066400000000000000000000026621455253710000160700ustar00rootroot00000000000000# To contribute improvements to CI/CD templates, please follow the Development guide at: # https://docs.gitlab.com/ee/development/cicd/templates.html # This specific template is located at: # https://gitlab.com/gitlab-org/gitlab/-/blob/master/lib/gitlab/ci/templates/Go.gitlab-ci.yml image: golang:latest variables: # Please edit to your GitLab project REPO_NAME: salsa.debian.org/mdosch/xmppsrv # The problem is that to be able to use go get, one needs to put # the repository in the $GOPATH. So for example if your gitlab domain # is gitlab.com, and that your repository is namespace/project, and # the default GOPATH being /go, then you'd need to have your # repository in /go/src/gitlab.com/namespace/project # Thus, making a symbolic link corrects this. before_script: - mkdir -p $GOPATH/src/$(dirname $REPO_NAME) - ln -svf $CI_PROJECT_DIR $GOPATH/src/$REPO_NAME - cd $GOPATH/src/$REPO_NAME stages: - test - release format: stage: test script: - go fmt $(go list ./... | grep -v /vendor/) - go vet $(go list ./... | grep -v /vendor/) - go test -race $(go list ./... | grep -v /vendor/) release: stage: release image: registry.gitlab.com/gitlab-org/release-cli:latest only: - tags script: - | release-cli create --name "Release $CI_COMMIT_TAG" --tag-name $CI_COMMIT_TAG --description="`head -n $(expr "$(grep -nm2 "^## " CHANGELOG.md|awk '(NR>1) {print $1}'|cut -f1 -d:) - 2"|bc) CHANGELOG.md`" xmppsrv-v0.2.6/CHANGELOG.md000066400000000000000000000020701455253710000152360ustar00rootroot00000000000000# Changelog ## [0.2.6] 2024-01-19 ### Changed - Don't return an error if either of the xmpp or xmpps look ups returns a result. ## [0.2.5] 2023-03-27 ### Changed - Don't use CNAMES of xmpp domain for xmpp SRV lookups. ## [0.2.4] ### Changed - The changes are the same as in 0.2.3 but a new release is created as not all changes were in 0.2.3 due to a mistake in the release process. ## [0.2.3] ### Added - Add CNAME lookup. ### Changed - Check resolver IP for validity. - Improve error strings. - Always return the error if one of the lookups failed. ## [0.2.2] ### Changed - Added error handling for wrong resolver and DoT configuration. ## [0.2.1] ### Changed - Check that DoT server string split at `#` returns 2 values. ## [0.2.0] ### Added - Support for custom DNS resolvers and DoT. ### Changed - Don't capitalize error strings. - Don't end error strings with punctuation. ## [0.1.1] ### Changed - Don't sort SRV records with identical priority by weight. - Fix a typo in error message when no server SRV records are found. ## [0.1.0] ### Added - Initial release xmppsrv-v0.2.6/LICENSE000066400000000000000000000024441455253710000144370ustar00rootroot00000000000000BSD 2-Clause License Copyright (c) Martin Dosch All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. xmppsrv-v0.2.6/README.md000066400000000000000000000022231455253710000147040ustar00rootroot00000000000000# xmppsrv Lookup xmpp srv records ## usage Import the library into your go project by adding `salsa.debian.org/mdosch/xmppsrv` to your imports. All functions return `[]SRV` with `SRV` being the following struct: ``` type SRV struct { Type string Target string Port uint16 Priority uint16 Weight uint16 } ``` Type is either `xmpp-client`, `xmpps-client`, `xmpp-server` or `xmpps-server`. The functions `LookupXmppServer`, `LookupXmppsServer`, `LookupXmppClient` and `LookupXmppsClient` are called with the server name and return the respective SRV records. The function `LookupClient` and `LookupServer` are also called with the server name but return `xmpp` and `xmpps` SRV records ordered by priority and weight. To configure a custom resolver or to use DoT all the lookup functions implement an interface on the Config type: ``` type Config struct { Resolver string DoT bool } ``` Set Resolver to an IP address and DoT to false to use the DNS resolver on port 53 on the given IP address. Set Resolver to an IP and domain name (e.g. "5.1.66.255#dot.ffmuc.net") and DoT to true to use DNSoverTLS (DoT) on that resolver. xmppsrv-v0.2.6/go.mod000066400000000000000000000000601455253710000145300ustar00rootroot00000000000000module salsa.debian.org/mdosch/xmppsrv go 1.16 xmppsrv-v0.2.6/xmppsrv.go000066400000000000000000000350231455253710000154770ustar00rootroot00000000000000// Copyright Martin Dosch. // Use of this source code is governed by the BSD-2-clause // license that can be found in the LICENSE file. package xmppsrv import ( "context" "crypto/tls" "errors" "fmt" "math/rand" "net" "sort" "strings" "time" ) // The Config type is used to configure which resolver is used. // Set Resolver to an IP address and DoT to false to use the // DNS resolver on port 53 on the given IP address. // Set Resolver to an IP and domain name (e.g. "5.1.66.255#dot.ffmuc.net") // and DoT to true to use DNSoverTLS (DoT) on that resolver. type Config struct { Resolver string DoT bool } // The SRV type is basically the same as net.SRV (see // https://pkg.go.dev/net#SRV but it adds the string Type // which contains either xmpp-client, xmpps-client, // xmpp-server or xmpps-server. // This is especially useful for the function LookupXmppClient // which returns a mix of xmpp-client and xmpps-client // records. type SRV struct { Type string Target string Port uint16 Priority uint16 Weight uint16 } 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 { return o[i].Priority < o[j].Priority } func getCNAME(server string) (string, error) { var cname string // Lookup CNAME for i := 0; i <= 5; i++ { cname, err := net.LookupCNAME(server) // Return the requested domain unchanged if the // CNAME record can't be resolved. This can // happen if there is no CNAME record and no // A/AAAA record. But there could still be SRV // records. if cname == "" { return server, nil } if cname == server { return cname, err } server = cname } return cname, errors.New("CNAME loop detected") } func getCNAMEDoT(server string, resolver string) (string, error) { var cname string var err error var d tls.Dialer s := strings.Split(resolver, "#") if len(s) != 2 { return cname, errors.New("wrong DoT server syntax") } ip := net.ParseIP(s[0]) if ip == nil { return cname, errors.New("invalid resolver IP") } dnsServer := net.JoinHostPort(s[0], "853") d.Config = &tls.Config{ ServerName: s[1], } r := &net.Resolver{ Dial: func(cont context.Context, network, address string) (net.Conn, error) { return d.DialContext(cont, "tcp", dnsServer) }, } for i := 0; i <= 5; i++ { cname, err = r.LookupCNAME(context.Background(), server) // Return the requested domain unchanged if the // CNAME record can't be resolved. This can // happen if there is no CNAME record and no // A/AAAA record. But there could still be SRV // records. if cname == "" { return server, nil } if cname == server { return cname, err } server = cname } return cname, errors.New("CNAME loop detected") } func getCNAMECustomResolver(server string, resolver string) (string, error) { var cname string var err error var d net.Dialer ip := net.ParseIP(resolver) if ip == nil { return cname, errors.New("invalid resolver IP") } dnsServer := net.JoinHostPort(resolver, "53") r := &net.Resolver{ Dial: func(cont context.Context, network, address string) (net.Conn, error) { return d.DialContext(cont, "udp", dnsServer) }, } for i := 0; i <= 5; i++ { cname, err = r.LookupCNAME(context.Background(), server) // Return the requested domain unchanged if the // CNAME record can't be resolved. This can // happen if there is no CNAME record and no // A/AAAA record. But there could still be SRV // records. if cname == "" { return server, nil } if cname == server { return cname, err } server = cname } return cname, errors.New("CNAME loop detected") } func getSRVDoT(server string, srvType string, resolver string, ) ([]SRV, error) { var records []SRV var err error var d tls.Dialer s := strings.Split(resolver, "#") if len(s) != 2 { return records, errors.New("wrong DoT server syntax") } ip := net.ParseIP(s[0]) if ip == nil { return records, errors.New("invalid resolver IP") } dnsServer := net.JoinHostPort(s[0], "853") d.Config = &tls.Config{ ServerName: s[1], } r := &net.Resolver{ Dial: func(cont context.Context, network, address string) (net.Conn, error) { return d.DialContext(cont, "tcp", dnsServer) }, } _, addr, err := r.LookupSRV(context.Background(), srvType, "tcp", server) if err != nil { return records, err } if len(addr) > 0 { for _, adr := range addr { records = append(records, SRV{ srvType, adr.Target, adr.Port, adr.Priority, adr.Weight, }) } } return records, err } func getSRVCustomResolver(server string, srvType string, resolver string, ) ([]SRV, error) { var records []SRV var err error var d net.Dialer ip := net.ParseIP(resolver) if ip == nil { return records, errors.New("invalid resolver IP") } dnsServer := net.JoinHostPort(resolver, "53") r := &net.Resolver{ Dial: func(cont context.Context, network, address string) (net.Conn, error) { return d.DialContext(cont, "udp", dnsServer) }, } _, addr, err := r.LookupSRV(context.Background(), srvType, "tcp", server) if err != nil { return records, err } if len(addr) > 0 { for _, adr := range addr { records = append(records, SRV{ srvType, adr.Target, adr.Port, adr.Priority, adr.Weight, }) } } return records, err } func getSRV(server string, srvType string) ([]SRV, error) { var records []SRV var err error // Look up SRV records. _, addr, err := net.LookupSRV(srvType, "tcp", server) if err != nil { return records, err } if len(addr) > 0 { for _, adr := range addr { records = append(records, SRV{ srvType, adr.Target, adr.Port, adr.Priority, adr.Weight, }) } } return records, err } // LookupXmppServer returns the xmpp-server SRV records. func LookupXmppServer(server string) ([]SRV, error) { // Look up xmpp-server SRV records, err := getSRV(server, "xmpp-server") return records, err } // LookupXmppServer returns the xmpp-server SRV records. func (c *Config) LookupXmppServer(server string) ([]SRV, error) { var records []SRV var err error // Look up xmpp-server SRV switch { case c.Resolver == "": records, err = getSRV(server, "xmpp-server") case c.Resolver != "" && !c.DoT: records, err = getSRVCustomResolver(server, "xmpp-server", c.Resolver) case c.Resolver != "" && c.DoT: records, err = getSRVDoT(server, "xmpp-server", c.Resolver) default: return records, errors.New("xmppsrv: invalid resolver setting") } return records, err } // LookupXmppsServer returns the xmpps-server SRV records. func LookupXmppsServer(server string) ([]SRV, error) { // Look up xmpps-server SRV records, err := getSRV(server, "xmpps-server") return records, err } // LookupCNAME returns the final target name. func (c *Config) LookupCNAME(server string) (string, error) { var cname string var err error switch { case c.Resolver == "": // Look up CNAME cname, err = getCNAME(server) case c.Resolver != "" && !c.DoT: // Look up CNAME cname, err = getCNAMECustomResolver(server, c.Resolver) case c.Resolver != "" && c.DoT: // Look up CNMAE cname, err = getCNAMEDoT(server, c.Resolver) default: return cname, errors.New("xmppsrv: invalid resolver setting") } return cname, err } // LookupXmppsServer returns the xmpps-server SRV records. func (c *Config) LookupXmppsServer(server string) ([]SRV, error) { var records []SRV var err error switch { case c.Resolver == "": // Look up xmpps-server SRV records, err = getSRV(server, "xmpps-server") case c.Resolver != "" && !c.DoT: // Look up xmpps-server SRV records, err = getSRVCustomResolver(server, "xmpps-server", c.Resolver) case c.Resolver != "" && c.DoT: // Look up xmpps-server SRV records, err = getSRVDoT(server, "xmpps-server", c.Resolver) default: return records, errors.New("xmppsrv: invalid resolver setting") } return records, err } // LookupXmppClient returns the xmpp-server SRV records. func LookupXmppClient(server string) ([]SRV, error) { // Look up xmpp-client SRV records, err := getSRV(server, "xmpp-client") return records, err } // LookupXmppClient returns the xmpp-server SRV records. func (c *Config) LookupXmppClient(server string) ([]SRV, error) { var records []SRV var err error switch { case c.Resolver == "": // Look up xmpp-client SRV records, err = getSRV(server, "xmpp-client") case c.Resolver != "" && !c.DoT: // Look up xmpp-client SRV records, err = getSRVCustomResolver(server, "xmpp-client", c.Resolver) case c.Resolver != "" && c.DoT: // Look up xmpp-client SRV records, err = getSRVDoT(server, "xmpp-client", c.Resolver) default: return records, errors.New("xmppsrv: invalid resolver setting") } return records, err } // LookupXmppsClient returns the xmpp-server SRV records. func LookupXmppsClient(server string) ([]SRV, error) { // Look up xmpps-client SRV records, err := getSRV(server, "xmpps-client") return records, err } // LookupXmppsClient returns the xmpp-server SRV records. func (c *Config) LookupXmppsClient(server string) ([]SRV, error) { var records []SRV var err error switch { case c.Resolver == "": // Look up xmpps-client SRV records, err = getSRV(server, "xmpps-client") case c.Resolver != "" && !c.DoT: // Look up xmpps-client SRV records, err = getSRVCustomResolver(server, "xmpps-client", c.Resolver) case c.Resolver != "" && c.DoT: // Look up xmpps-client SRV records, err = getSRVDoT(server, "xmpps-client", c.Resolver) default: return records, errors.New("xmppsrv: invalid resolver setting") } return records, err } // LookupClient returns the xmpp-client and xmpps-client SRV records sorted by // priority and weight. func LookupClient(server string) ([]SRV, error) { var records, records2 []SRV var err, err2 error rand.Seed(time.Now().UnixNano()) // Look up xmpp-client SRV records, err = getSRV(server, "xmpp-client") // Look up xmpps-client SRV records. records2, err2 = getSRV(server, "xmpps-client") switch rand.Intn(2) { case 0: records = append(records, records2...) case 1: records = append(records2, records...) default: records = append(records, records2...) } if err != nil && err2 != nil { return records, fmt.Errorf("failure in xmpp-client lookup: %v\n"+ "failure in xmpps-client lookup: %v", err, err2) } switch len(records) { case 0: return records, errors.New("no client records found") case 1: return records, nil default: // Sort xmpp- and xmpps-client SRV records according to the priority // and weight. sort.Sort(byPriority(records)) } return records, nil } // LookupClient returns the xmpp-client and xmpps-client SRV records sorted by // priority and weight. func (c *Config) LookupClient(server string) ([]SRV, error) { var records, records2 []SRV var err, err2 error rand.Seed(time.Now().UnixNano()) switch { case c.Resolver == "": // Look up xmpp-client SRV records, err = getSRV(server, "xmpp-client") // Look up xmpps-client SRV records. records2, err2 = getSRV(server, "xmpps-client") case c.Resolver != "" && !c.DoT: // Look up xmpp-client SRV records, err = getSRVCustomResolver(server, "xmpp-client", c.Resolver) // Look up xmpps-client SRV records. records2, err2 = getSRVCustomResolver(server, "xmpps-client", c.Resolver) case c.Resolver != "" && c.DoT: // Look up xmpp-client SRV records, err = getSRVDoT(server, "xmpp-client", c.Resolver) // Look up xmpps-client SRV records. records2, err2 = getSRVDoT(server, "xmpps-client", c.Resolver) default: return records, errors.New("xmppsrv: invalid resolver setting") } switch rand.Intn(2) { case 0: records = append(records, records2...) case 1: records = append(records2, records...) default: records = append(records, records2...) } if err != nil && err2 != nil { return records, fmt.Errorf("failure in xmpp-client lookup: %v\n"+ "failure in xmpps-client lookup: %v", err, err2) } switch len(records) { case 0: return records, errors.New("no client records found") case 1: return records, nil default: // Sort xmpp- and xmpps-client SRV records according to the priority // and weight. sort.Sort(byPriority(records)) } return records, nil } // LookupServer returns the xmpp-server and xmpps-server SRV records sorted by // priority and weight. func LookupServer(server string) ([]SRV, error) { var err, err2 error rand.Seed(time.Now().UnixNano()) // Look up xmpp-client SRV records, err := getSRV(server, "xmpp-server") // Look up xmpps-client SRV records. records2, err2 := getSRV(server, "xmpps-server") switch rand.Intn(2) { case 0: records = append(records, records2...) case 1: records = append(records2, records...) default: records = append(records, records2...) } if err != nil && err2 != nil { return records, fmt.Errorf("failure in xmpp-server lookup: %v\n"+ "failure in xmpps-server lookup: %v", err, err2) } switch len(records) { case 0: return records, errors.New("no server records found") case 1: return records, nil default: // Sort xmpp- and xmpps-server SRV records according to the priority // and weight. sort.Sort(byPriority(records)) } return records, nil } // LookupServer returns the xmpp-server and xmpps-server SRV records sorted by // priority and weight. func (c *Config) LookupServer(server string) ([]SRV, error) { var records, records2 []SRV var err, err2 error rand.Seed(time.Now().UnixNano()) switch { case c.Resolver == "": // Look up xmpp-client SRV records, err = getSRV(server, "xmpp-server") // Look up xmpps-client SRV records. records2, err2 = getSRV(server, "xmpps-server") case c.Resolver != "" && !c.DoT: // Look up xmpp-client SRV records, err = getSRVCustomResolver(server, "xmpp-server", c.Resolver) // Look up xmpps-client SRV records. records2, err2 = getSRVCustomResolver(server, "xmpps-server", c.Resolver) case c.Resolver != "" && c.DoT: // Look up xmpp-client SRV records, err = getSRVDoT(server, "xmpp-server", c.Resolver) // Look up xmpps-client SRV records. records2, err2 = getSRVDoT(server, "xmpps-server", c.Resolver) default: return records, errors.New("xmppsrv: invalid resolver setting") } switch rand.Intn(2) { case 0: records = append(records, records2...) case 1: records = append(records2, records...) default: records = append(records, records2...) } if err != nil && err2 != nil { return records, fmt.Errorf("failure in xmpp-server lookup: %v\n"+ "failure in xmpps-server lookup: %v", err, err2) } switch len(records) { case 0: return records, errors.New("no server records found") case 1: return records, nil default: // Sort xmpp- and xmpps-server SRV records according to the priority // and weight. sort.Sort(byPriority(records)) } return records, nil }