Version in base suite: 0.4.5-1 Base version: xmpp-dns_0.4.5-1 Target version: xmpp-dns_0.6.0-1~bpo13+1 Base file: /srv/ftp-master.debian.org/ftp/pool/main/x/xmpp-dns/xmpp-dns_0.4.5-1.dsc Target file: /srv/ftp-master.debian.org/policy/pool/main/x/xmpp-dns/xmpp-dns_0.6.0-1~bpo13+1.dsc .gitlab-ci.yml | 24 +++ CHANGELOG.md | 46 ++++++ README.md | 21 ++- debian/changelog | 83 ++++++++++++ debian/control | 9 - debian/gbp.conf | 2 go.mod | 9 + go.sum | 8 - linkrel.go | 4 main.go | 354 +++++++++++++++++++++++++++++++++++++++------------- man/xmpp-dns.1 | 39 ++++- man/xmpp-dns.1.html | 34 +++- man/xmpp-dns.1.ronn | 19 ++ 13 files changed, 527 insertions(+), 125 deletions(-) dpkg-source: warning: cannot verify inline signature for /srv/release.debian.org/tmp/tmp4emp7rlh/xmpp-dns_0.4.5-1.dsc: no acceptable signature found dpkg-source: warning: cannot verify inline signature for /srv/release.debian.org/tmp/tmp4emp7rlh/xmpp-dns_0.6.0-1~bpo13+1.dsc: no acceptable signature found diff -Nru xmpp-dns-0.4.5/.gitlab-ci.yml xmpp-dns-0.6.0/.gitlab-ci.yml --- xmpp-dns-0.4.5/.gitlab-ci.yml 2024-12-11 20:41:01.000000000 +0000 +++ xmpp-dns-0.6.0/.gitlab-ci.yml 2026-01-15 19:07:10.000000000 +0000 @@ -3,7 +3,7 @@ # 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 +image: golang:trixie variables: # Please edit to your GitLab project @@ -26,19 +26,35 @@ - release format: + # image: registry.gitlab.com/gitlab-org/gitlab-build-images:golangci-lint-alpine stage: test + before_script: + - DEB_RELEASE=$(env -i bash -c '. /etc/os-release; echo $VERSION_CODENAME') + - echo "deb https://deb.debian.org/debian/ $DEB_RELEASE-backports main" >> /etc/apt/sources.list + - apt -qq update; apt -qq install -y codespell + - go install mvdan.cc/gofumpt@latest script: - go fmt $(go list ./... | grep -v /vendor/) - go vet $(go list ./... | grep -v /vendor/) - go test -race $(go list ./... | grep -v /vendor/) + - codespell *.go *.md man/*.ronn + - gofumpt -d . + # Taken from https://gitlab.com/gitlab-org/gitlab/-/merge_requests/20404/diffs#diff-content-8071267ea32ba69f24a8bd50bcbddf972c295ce3 + # Use default .golangci.yml file from the image if one is not present in the project root. + #- '[ -e .golangci.yml ] || cp /golangci/.golangci.yml .' + #- golangci-lint run + #allow_failure: true + artifacts: + expire_in: 1 year compile: stage: build only: - tags before_script: - - echo "deb https://deb.debian.org/debian/ bookworm-backports main" >> /etc/apt/sources.list - - apt-get -qq update && apt-get -qq install -y upx-ucl + - DEB_RELEASE=$(env -i bash -c '. /etc/os-release; echo $VERSION_CODENAME') + - echo "deb https://deb.debian.org/debian/ $DEB_RELEASE-backports main" >> /etc/apt/sources.list + - apt -qq update; apt -qq install -y upx-ucl || true script: - echo "${CI_JOB_ID}" > CI_JOB_ID.txt - env GOOS=linux GOARCH=amd64 go build -buildmode=pie -ldflags "-s -w -extldflags '-static'" -o $CI_PROJECT_DIR/linux-amd64/xmpp-dns @@ -66,3 +82,5 @@ --assets-link "{\"name\":\"Linux amd64\",\"url\":\"https://salsa.debian.org/mdosch/xmpp-dns/-/jobs/`cat CI_JOB_ID.txt`/artifacts/file/linux-amd64/xmpp-dns\"}" \ --assets-link "{\"name\":\"Linux arm64\",\"url\":\"https://salsa.debian.org/mdosch/xmpp-dns/-/jobs/`cat CI_JOB_ID.txt`/artifacts/file/linux-arm64/xmpp-dns\"}" \ --assets-link "{\"name\":\"Windows amd64\",\"url\":\"https://salsa.debian.org/mdosch/xmpp-dns/-/jobs/`cat CI_JOB_ID.txt`/artifacts/file/win64/xmpp-dns.exe\"}" + artifacts: + expire_in: 2 years diff -Nru xmpp-dns-0.4.5/CHANGELOG.md xmpp-dns-0.6.0/CHANGELOG.md --- xmpp-dns-0.4.5/CHANGELOG.md 2024-12-11 20:41:01.000000000 +0000 +++ xmpp-dns-0.6.0/CHANGELOG.md 2026-01-15 19:07:10.000000000 +0000 @@ -1,5 +1,51 @@ # Changelog +## [0.6.0] 2026-01-15 +### Added +- Add flag `--client-dtls` for adding arbitrary targets for C2S with direct TLS. +- Add flag `--client-stls` for adding arbitrary targets for C2S with StartTLS. +- Add flag `--server-dtls` for adding arbitrary targets for S2S with direct TLS. +- Add flag `--server-stls` for adding arbitrary targets for S2S with StartTLS. + +## [0.5.6] 2026-01-11 +### Changed +- Update link relations to version of 2025-10-14. +- Only resolve IPs when `-v` is set. + +## [0.5.5] 2026-01-02 +### Added +- Print expiry date when certificate expires within the next 14 days (only xmpp and xmpps connections). +- Add flag `--force-fallback` to enforce checks on standard ports, even if SRV records are provided. + +## [0.5.4] 2025-10-14 +### Changed +- Fix calculation of remaining days for certificate expiry warning. + +## [0.5.3] 2025-09-24 +### Added +- Print warning if certificate expires in less than 14 days (only xmpp and xmpps connections). + +### Changed +- Improve BOSH connection speed. +- BOSH + Websocket: Show warning if connection is unencrypted. + +## [0.5.2] 2025-09-04 +### Added +- Print TLS version after successful connection. + +## [0.5.1] 2025-08-13 +### Changed +- No changes to v0.5.0, only created a new tag due to CI issues. + +## [0.5.0] 2025-08-13 +### Added +- Add support for punycode domains. + +### Changed +- Support ALPN for BOSH and websocket connections. +- BOSH + websocket: Check certificate validity for target domain not service domain. +- Update link relations to version of 2025-03-27. + ## [0.4.5] 2024-12-11 ### Changed - Host Meta (2): Improve output format. diff -Nru xmpp-dns-0.4.5/README.md xmpp-dns-0.6.0/README.md --- xmpp-dns-0.4.5/README.md 2024-12-11 20:41:01.000000000 +0000 +++ xmpp-dns-0.6.0/README.md 2026-01-15 19:07:10.000000000 +0000 @@ -33,19 +33,34 @@ ## usage ``` -Usage: xmpp-dns [-46cfstv] [--dot] [--help] [--no-color] [--resolver value] [--timeout value] [--tls-version value] [--version] [parameters ...] +Usage: xmpp-dns [-46cfstv] [--client-dtls value] [--client-stls value] [--dot] [--force-fallback] [--help] [--no-color] [--resolver value] [--server-dtls value] [--server-stls value] [--timeout value] [--tls-version value] [--version] [xmpp server] -4 Resolve IPv4. -6 Resolve IPv6. -c, --client Show client SRV records. - --dot Use DoT. - -f, --fallback Check fallback (Standard ports on A/AAAA records) if no SRV + --client-dtls=value + Add an arbitrary target for C2S with direct TLS in the form + domain:port. Can be invoked several times. + --client-stls=value + Add an arbitrary target for C2S with StartTLS in the form + domain:port. Can be invoked several times. + --dot Use DNSoverTLS (DoT), see also "--resolver". + -f, --fallback Additionally check fallback (Standard ports on A/AAAA records) if no SRV records are provided. + --force-fallback + Check fallback (Standard ports on A/AAAA records) even if + SRV records are provided. --help Show help. --no-color Don't colorize output. --resolver=value Custom resolver e.g. "1.1.1.1" for common DNS or "5.1.66.255#dot.ffmuc.net" for usage with "--dot". -s, --server Show server SRV records. + --server-dtls=value + Add an arbitrary target for S2S with direct TLS in the form + domain:port. Can be invoked several times. + --server-stls=value + Add an arbitrary target for S2S with StartTLS in the form + domain:port. Can be invoked several times. -t Test connection and certificates. --timeout=value Connection timeout in seconds. [60] diff -Nru xmpp-dns-0.4.5/debian/changelog xmpp-dns-0.6.0/debian/changelog --- xmpp-dns-0.4.5/debian/changelog 2024-12-11 20:43:22.000000000 +0000 +++ xmpp-dns-0.6.0/debian/changelog 2026-01-17 15:39:19.000000000 +0000 @@ -1,3 +1,86 @@ +xmpp-dns (0.6.0-1~bpo13+1) trixie-backports; urgency=medium + + * Rebuild for trixie-backports. + + -- Martin Dosch Sat, 17 Jan 2026 15:39:19 +0000 + +xmpp-dns (0.6.0-1) unstable; urgency=medium + + * New upstream version 0.6.0 + + -- Martin Dosch Thu, 15 Jan 2026 19:10:50 +0000 + +xmpp-dns (0.5.6-1) unstable; urgency=medium + + * New upstream version 0.5.6 + + -- Martin Dosch Sun, 11 Jan 2026 10:15:47 +0000 + +xmpp-dns (0.5.5-1) unstable; urgency=medium + + * New upstream version 0.5.5 + * d/control: Bump standards version to 4.7.3 (no change). + + -- Martin Dosch Fri, 02 Jan 2026 22:11:08 +0000 + +xmpp-dns (0.5.4-1) unstable; urgency=medium + + * New upstream version 0.5.4 + + -- Martin Dosch Tue, 14 Oct 2025 20:23:54 +0000 + +xmpp-dns (0.5.3-1~bpo13+1) trixie-backports; urgency=medium + + * Rebuild for trixie-backports. + + -- Martin Dosch Sun, 28 Sep 2025 06:27:06 +0000 + +xmpp-dns (0.5.3-1) unstable; urgency=medium + + * New upstream version 0.5.3 + + -- Martin Dosch Wed, 24 Sep 2025 18:11:04 +0000 + +xmpp-dns (0.5.2-1~bpo13+1) trixie-backports; urgency=medium + + * Rebuild for trixie-backports. + + -- Martin Dosch Tue, 23 Sep 2025 17:13:43 +0000 + +xmpp-dns (0.5.2-1) unstable; urgency=medium + + * New upstream version 0.5.2 + * d/control: Add Static-Built-Using. + * d/control: Add new dependency golang-github-gorilla-websocket-dev. + * d/control: Remove trailing '.' in synopsis. + + -- Martin Dosch Thu, 04 Sep 2025 20:50:23 +0000 + +xmpp-dns (0.5.1-1~bpo13+3) trixie-backports; urgency=medium + + [ Martin Dosch ] + * Rebuild for trixie-backports. + + [ Simon Josefsson ] + * Set debian-branch for trixie-backports. + + -- Simon Josefsson Fri, 29 Aug 2025 21:41:10 +0200 + +xmpp-dns (0.5.1-1) unstable; urgency=medium + + * New upstream version 0.5.1 + * d/control: Bump standards version to 4.7.2 (no change). + + -- Martin Dosch Wed, 13 Aug 2025 11:33:02 +0000 + +xmpp-dns (0.5.0~beta1-1) experimental; urgency=medium + + * New upstream version 0.5.0~beta1 + * d/control: Increase build-dep on golang to >=1.21. + * d/control: Bump Standards-Version to 4.7.1 (no change). + + -- Martin Dosch Mon, 21 Jul 2025 19:36:01 +0000 + xmpp-dns (0.4.5-1) unstable; urgency=medium * New upstream version 0.4.5 diff -Nru xmpp-dns-0.4.5/debian/control xmpp-dns-0.6.0/debian/control --- xmpp-dns-0.4.5/debian/control 2024-12-11 20:42:12.000000000 +0000 +++ xmpp-dns-0.6.0/debian/control 2026-01-05 08:43:51.000000000 +0000 @@ -8,13 +8,14 @@ Build-Depends: debhelper-compat (= 13), dh-sequence-golang, - golang-any, + golang-any (>= 2:1.21~), golang-debian-mdosch-xmppsrv-dev (>= 0.3.2~), + golang-github-gorilla-websocket-dev, golang-github-pborman-getopt-dev, golang-golang-x-net-dev (>= 1:0.22.0~), ronn, Testsuite: autopkgtest-pkg-go -Standards-Version: 4.7.0 +Standards-Version: 4.7.3 Vcs-Browser: https://salsa.debian.org/go-team/packages/xmpp-dns Vcs-Git: https://salsa.debian.org/go-team/packages/xmpp-dns.git Homepage: https://salsa.debian.org/mdosch/xmpp-dns @@ -28,6 +29,8 @@ ${shlibs:Depends}, Built-Using: ${misc:Built-Using}, -Description: CLI tool to check XMPP SRV records. +Static-Built-Using: + ${misc:Static-Built-Using}, +Description: CLI tool to check XMPP SRV records Xmpp-dns can look up XMPP SRV records, resolve the IPs, test the connection and the certificates. diff -Nru xmpp-dns-0.4.5/debian/gbp.conf xmpp-dns-0.6.0/debian/gbp.conf --- xmpp-dns-0.4.5/debian/gbp.conf 2024-12-09 20:43:28.000000000 +0000 +++ xmpp-dns-0.6.0/debian/gbp.conf 2026-01-17 15:38:26.000000000 +0000 @@ -1,3 +1,3 @@ [DEFAULT] -debian-branch = debian/sid +debian-branch = debian/trixie-backports dist = DEP14 diff -Nru xmpp-dns-0.4.5/go.mod xmpp-dns-0.6.0/go.mod --- xmpp-dns-0.4.5/go.mod 2024-12-11 20:41:01.000000000 +0000 +++ xmpp-dns-0.6.0/go.mod 2026-01-15 19:07:10.000000000 +0000 @@ -1,9 +1,14 @@ module salsa.debian.org/mdosch/xmpp-dns -go 1.23.4 +go 1.24.0 + +toolchain go1.24.4 require ( + github.com/gorilla/websocket v1.5.3 github.com/pborman/getopt/v2 v2.1.0 - golang.org/x/net v0.32.0 + golang.org/x/net v0.49.0 salsa.debian.org/mdosch/xmppsrv v0.3.3 ) + +require golang.org/x/text v0.33.0 // indirect diff -Nru xmpp-dns-0.4.5/go.sum xmpp-dns-0.6.0/go.sum --- xmpp-dns-0.4.5/go.sum 2024-12-11 20:41:01.000000000 +0000 +++ xmpp-dns-0.6.0/go.sum 2026-01-15 19:07:10.000000000 +0000 @@ -1,6 +1,10 @@ +github.com/gorilla/websocket v1.5.3 h1:saDtZ6Pbx/0u+bgYQ3q96pZgCzfhKXGPqt7kZ72aNNg= +github.com/gorilla/websocket v1.5.3/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE= github.com/pborman/getopt/v2 v2.1.0 h1:eNfR+r+dWLdWmV8g5OlpyrTYHkhVNxHBdN2cCrJmOEA= github.com/pborman/getopt/v2 v2.1.0/go.mod h1:4NtW75ny4eBw9fO1bhtNdYTlZKYX5/tBLtsOpwKIKd0= -golang.org/x/net v0.32.0 h1:ZqPmj8Kzc+Y6e0+skZsuACbx+wzMgo5MQsJh9Qd6aYI= -golang.org/x/net v0.32.0/go.mod h1:CwU0IoeOlnQQWJ6ioyFrfRuomB8GKF6KbYXZVyeXNfs= +golang.org/x/net v0.49.0 h1:eeHFmOGUTtaaPSGNmjBKpbng9MulQsJURQUAfUwY++o= +golang.org/x/net v0.49.0/go.mod h1:/ysNB2EvaqvesRkuLAyjI1ycPZlQHM3q01F02UY/MV8= +golang.org/x/text v0.33.0 h1:B3njUFyqtHDUI5jMn1YIr5B0IE2U0qck04r6d4KPAxE= +golang.org/x/text v0.33.0/go.mod h1:LuMebE6+rBincTi9+xWTY8TztLzKHc/9C1uBCG27+q8= salsa.debian.org/mdosch/xmppsrv v0.3.3 h1:F8FGyw1Q1LkAs/UbIXd6Obd33q2CKWrIxxrzvuLSVuM= salsa.debian.org/mdosch/xmppsrv v0.3.3/go.mod h1:udWXnWFa9zkcyN9YSB/u44BCnnRDpeQ0eDy3MVLjHZQ= diff -Nru xmpp-dns-0.4.5/linkrel.go xmpp-dns-0.6.0/linkrel.go --- xmpp-dns-0.4.5/linkrel.go 2024-12-11 20:41:01.000000000 +0000 +++ xmpp-dns-0.6.0/linkrel.go 2026-01-15 19:07:10.000000000 +0000 @@ -11,7 +11,7 @@ func isKnownLinkRel(s string) bool { // https://www.iana.org/assignments/link-relations/link-relations.xhtml - // as of 2024-10-07 - linkRels := []string{"about", "acl", "alternate", "amphtml", "appendix", "apple-touch-icon", "apple-touch-startup-image", "archives", "author", "blocked-by", "bookmark", "c2pa-manifest", "canonical", "chapter", "cite-as", "collection", "compression-dictionary", "contents", "convertedfrom", "copyright", "create-form", "current", "deprecation", "describedby", "describes", "disclosure", "dns-prefetch", "duplicate", "edit", "edit-form", "edit-media", "enclosure", "external", "first", "glossary", "help", "hosts", "hub", "ice-server", "icon", "index", "intervalafter", "intervalbefore", "intervalcontains", "intervaldisjoint", "intervalduring", "intervalequals", "intervalfinishedby", "intervalfinishes", "intervalin", "intervalmeets", "intervalmetby", "intervaloverlappedby", "intervaloverlaps", "intervalstartedby", "intervalstarts", "item", "last", "latest-version", "license", "linkset", "lrdd", "manifest", "mask-icon", "me", "media-feed", "memento", "micropub", "modulepreload", "monitor", "monitor-group", "next", "next-archive", "nofollow", "noopener", "noreferrer", "opener", "openid2.local_id", "openid2.provider", "original", "p3pv1", "payment", "pingback", "preconnect", "predecessor-version", "prefetch", "preload", "prerender", "prev", "preview", "previous", "prev-archive", "privacy-policy", "profile", "publication", "related", "restconf", "replies", "ruleinput", "search", "section", "self", "service", "service-desc", "service-doc", "service-meta", "sip-trunking-capability", "sponsored", "start", "status", "stylesheet", "subsection", "successor-version", "sunset", "tag", "terms-of-service", "timegate", "timemap", "type", "ugc", "up", "version-history", "via", "webmention", "working-copy", "working-copy-of"} + // as of 2025-10-14 + linkRels := []string{"about", "acl", "alternate", "amphtml", "api-catalog", "appendix", "apple-touch-icon", "apple-touch-startup-image", "archives", "author", "blocked-by", "bookmark", "c2pa-manifest", "canonical", "chapter", "cite-as", "collection", "compression-dictionary", "contents", "convertedfrom", "copyright", "create-form", "current", "deprecation", "describedby", "describes", "disclosure", "dns-prefetch", "duplicate", "edit", "edit-form", "edit-media", "enclosure", "external", "first", "glossary", "help", "hosts", "hub", "ice-server", "icon", "index", "intervalafter", "intervalbefore", "intervalcontains", "intervaldisjoint", "intervalduring", "intervalequals", "intervalfinishedby", "intervalfinishes", "intervalin", "intervalmeets", "intervalmetby", "intervaloverlappedby", "intervaloverlaps", "intervalstartedby", "intervalstarts", "item", "last", "latest-version", "license", "linkset", "lrdd", "manifest", "mask-icon", "me", "media-feed", "memento", "micropub", "modulepreload", "monitor", "monitor-group", "next", "next-archive", "nofollow", "noopener", "noreferrer", "opener", "openid2.local_id", "openid2.provider", "original", "p3pv1", "payment", "pingback", "preconnect", "predecessor-version", "prefetch", "preload", "prerender", "prev", "preview", "previous", "prev-archive", "privacy-policy", "profile", "publication", "rdap-active", "rdap-bottom", "rdap-down", "rdap-top", "rdap-up", "related", "restconf", "replies", "ruleinput", "search", "section", "self", "service", "service-desc", "service-doc", "service-meta", "sip-trunking-capability", "sponsored", "start", "status", "stylesheet", "subsection", "successor-version", "sunset", "tag", "terms-of-service", "timegate", "timemap", "type", "ugc", "up", "version-history", "via", "webmention", "working-copy", "working-copy-of"} return slices.Contains(linkRels, strings.ToLower(s)) } diff -Nru xmpp-dns-0.4.5/main.go xmpp-dns-0.6.0/main.go --- xmpp-dns-0.4.5/main.go 2024-12-11 20:41:01.000000000 +0000 +++ xmpp-dns-0.6.0/main.go 2026-01-15 19:07:10.000000000 +0000 @@ -20,16 +20,18 @@ "net/url" "os" "runtime" + "strconv" "strings" "time" + "github.com/gorilla/websocket" // BSD-2-Clause "github.com/pborman/getopt/v2" // BSD-3-Clause - "golang.org/x/net/websocket" // BSD-3-Clause + "golang.org/x/net/idna" // BSD-3-Clause "salsa.debian.org/mdosch/xmppsrv" // BSD-2-Clause ) const ( - version = "0.4.5" + version = "0.6.0" nsBOSH = "urn:xmpp:alt-connections:xbosh" nsC2SdTLS = "urn:xmpp:alt-connections:tls" nsC2SQuic = "urn:xmpp:alt-connections:quic" @@ -69,7 +71,11 @@ // statusOK will print [OK] in green color. statusOK = "Test: [\033[32mOK\033[00m]" // statusNOK will print [Not OK] in red color. - statusNOK = "Test: [\033[31mNot OK\033[00m]" + statusNOK = "Test: [\033[31mNot OK\033[00m]" + colorGreen = "\033[32m" + colorRed = "\033[31m" + colorYellow = "\033[33m" + colorReset = "\033[00m" ) func main() { @@ -85,12 +91,18 @@ flagTLSVersion := getopt.IntLong("tls-version", 0, 12, "Minimal TLS version. 10 (TSLv1.0), 11 (TLSv1.1), 12 (TLSv1.2) or 13 (TLSv1.3).") flagVersion := getopt.BoolLong("version", 0, "Show version information.") - flagFallback := getopt.BoolLong("fallback", 'f', "Check fallback (Standard ports on A/AAAA records)"+ + flagFallback := getopt.BoolLong("fallback", 'f', "Additionally check fallback (Standard ports on A/AAAA records)"+ " if no SRV records are provided.") + flagForceFallback := getopt.BoolLong("force-fallback", 0, "Check fallback (Standard ports on A/AAAA records)"+ + " even if SRV records are provided.") flagTimeout := getopt.IntLong("timeout", 0, 60, "Connection timeout in seconds.") flagResolver := getopt.StringLong("resolver", 0, "", "Custom resolver e.g. \"1.1.1.1\" for common DNS"+ " or \"5.1.66.255#dot.ffmuc.net\" for usage with \"--dot\".") flagDoT := getopt.BoolLong("dot", 0, "Use DNSoverTLS (DoT), see also \"--resolver\".") + flagClientDTLS := getopt.ListLong("client-dtls", 0, "Add an arbitrary target for C2S with direct TLS in the form domain:port. Can be invoked several times.") + flagClientSTLS := getopt.ListLong("client-stls", 0, "Add an arbitrary target for C2S with StartTLS in the form domain:port. Can be invoked several times.") + flagServerDTLS := getopt.ListLong("server-dtls", 0, "Add an arbitrary target for S2S with direct TLS in the form domain:port. Can be invoked several times.") + flagServerSTLS := getopt.ListLong("server-stls", 0, "Add an arbitrary target for S2S with StartTLS in the form domain:port. Can be invoked several times.") // Parse command line flags. getopt.Parse() @@ -114,6 +126,10 @@ if *flagNoColor || runtime.GOOS == "windows" { statusOK = "Test: [OK]" statusNOK = "Test: [Not OK]" + colorGreen = "" + colorRed = "" + colorYellow = "" + colorReset = "" } // If connection test is required we'll also show IPs. @@ -121,18 +137,18 @@ *flagVerbose = true } - // If neither IPv4 nor Ipv6 is specified we'll use both. - if !*flagV4 && !*flagV6 { - *flagV4 = true - *flagV6 = true - } - // If either IPv4 or IPv6 is specified but the verbose flag // is not set, we'll just set it. if !*flagVerbose && (*flagV4 || *flagV6) { *flagVerbose = true } + // If neither IPv4 nor Ipv6 is specified we'll use both. + if !*flagV4 && !*flagV6 { + *flagV4 = true + *flagV6 = true + } + // If DoT is enabled a resolver must be set. if *flagDoT && *flagResolver == "" { log.Fatal("A resolver must be specified for DoT.") @@ -140,12 +156,18 @@ // Read server from command line. server := getopt.Args() + fmt.Println("args:", server) switch count := len(server); { case count == 0: log.Fatal("Please specify a server.") case count > 1: log.Fatal("Please specify only one server.") } + // Convert server domain to ASCII. + serverASCII, err := idna.ToASCII(server[0]) + if err != nil { + log.Fatal(err) + } // Configure DNS resolver c := xmppsrv.Config{ @@ -158,7 +180,7 @@ // Set TLS config var tlsConfig tls.Config - tlsConfig.ServerName = server[0] + tlsConfig.ServerName = serverASCII tlsConfig.InsecureSkipVerify = false switch *flagTLSVersion { case 10: @@ -181,56 +203,158 @@ } if *flagClient { - clientRecords, err := c.LookupClient(server[0]) + var fallback bool + clientRecords, err := c.LookupClient(serverASCII) if err != nil && len(clientRecords) == 0 { fmt.Println(err) if *flagFallback && *flagTest { + fallback = true fmt.Println("Trying fallback ports.") fmt.Println() - clientRecords = []xmppsrv.SRV{ - { - Type: "xmpp-client", - Target: server[0], - Port: 5222, - }, + } + } + if len(*flagClientDTLS) > 0 { + for _, cdtls := range *flagClientDTLS { + target := strings.SplitN(cdtls, ":", 2) + port, err := strconv.ParseUint(target[1], 10, 16) + if err != nil { + fmt.Println(err) + continue + } + cr := xmppsrv.SRV{ + Type: "xmpps-client", + Target: target[0], + Port: uint16(port), } + clientRecords = append(clientRecords, cr) } } + if len(*flagClientSTLS) > 0 { + for _, cstls := range *flagClientSTLS { + target := strings.SplitN(cstls, ":", 2) + port, err := strconv.ParseUint(target[1], 10, 16) + if err != nil { + fmt.Println(err) + continue + } + cr := xmppsrv.SRV{ + Type: "xmpp-client", + Target: target[0], + Port: uint16(port), + } + clientRecords = append(clientRecords, cr) + } + } + if *flagForceFallback && !fallback && *flagTest { + if len(clientRecords) > 0 { + var standardPort bool + for _, r := range clientRecords { + if r.Type == "xmpp-client" && + (r.Target == server[0] || + r.Target == server[0]+".") && + r.Port == 5222 { + standardPort = true + } + } + if !standardPort { + fallback = true + } + } + } + if fallback { + cr := xmppsrv.SRV{ + Type: "xmpp-client", + Target: server[0], + Port: 5222, + } + clientRecords = append(clientRecords, cr) + } checkRecord(clientRecords, *flagVerbose, *flagV4, *flagV6, *flagTest, - &tlsConfig, timeout) + server[0], &tlsConfig, timeout) } if *flagServer { + var fallback bool if *flagClient { fmt.Println() } - serverRecords, err := c.LookupServer(server[0]) + serverRecords, err := c.LookupServer(serverASCII) if err != nil && len(serverRecords) == 0 { fmt.Println(err) if *flagFallback && *flagTest { + fallback = true fmt.Println("Trying fallback ports.") fmt.Println() - serverRecords = []xmppsrv.SRV{ - { - Type: "xmpp-server", - Target: server[0], - Port: 5269, - }, + } + } + if len(*flagServerDTLS) > 0 { + for _, sdtls := range *flagServerDTLS { + target := strings.SplitN(sdtls, ":", 2) + port, err := strconv.ParseUint(target[1], 10, 16) + if err != nil { + fmt.Println(err) + continue + } + sr := xmppsrv.SRV{ + Type: "xmpps-server", + Target: target[0], + Port: uint16(port), } + serverRecords = append(serverRecords, sr) } } + if len(*flagServerSTLS) > 0 { + for _, sstls := range *flagServerSTLS { + target := strings.SplitN(sstls, ":", 2) + port, err := strconv.ParseUint(target[1], 10, 16) + if err != nil { + fmt.Println(err) + continue + } + sr := xmppsrv.SRV{ + Type: "xmpp-server", + Target: target[0], + Port: uint16(port), + } + serverRecords = append(serverRecords, sr) + } + } + if *flagForceFallback && !fallback && *flagTest { + if len(serverRecords) > 0 { + var standardPort bool + for _, r := range serverRecords { + if r.Type == "xmpp-server" && + (r.Target == serverASCII || + r.Target == serverASCII+".") && + r.Port == 5269 { + standardPort = true + } + } + if !standardPort { + fallback = true + } + } + } + if fallback { + sr := xmppsrv.SRV{ + Type: "xmpp-server", + Target: serverASCII, + Port: 5269, + } + serverRecords = append(serverRecords, sr) + } checkRecord(serverRecords, *flagVerbose, *flagV4, *flagV6, *flagTest, - &tlsConfig, timeout) + server[0], &tlsConfig, timeout) } checkHostmeta(server[0], *flagVerbose, *flagV4, *flagV6, - *flagClient, *flagServer, *flagTest, &tlsConfig, timeout, c) + *flagClient, *flagServer, *flagTest, server[0], &tlsConfig, timeout, c) checkHostmeta2(server[0], *flagVerbose, *flagV4, *flagV6, - *flagClient, *flagServer, *flagTest, &tlsConfig, timeout, c) + *flagClient, *flagServer, *flagTest, server[0], &tlsConfig, timeout, c) } func checkRecord(records []xmppsrv.SRV, verbose bool, ipv4 bool, ipv6 bool, test bool, - tlsConfig *tls.Config, timeout time.Duration, + serverName string, tlsConfig *tls.Config, timeout time.Duration, ) { for count, record := range records { if count > 0 { @@ -238,7 +362,7 @@ } printRecord(record) if verbose && record.Target != "." { - printIP(record, ipv4, ipv6, test, tlsConfig, timeout) + printIP(record, ipv4, ipv6, test, serverName, tlsConfig, timeout) } } } @@ -250,7 +374,7 @@ } func printIP(record xmppsrv.SRV, ipv4 bool, ipv6 bool, test bool, - tlsConfig *tls.Config, timeout time.Duration, + serverName string, tlsConfig *tls.Config, timeout time.Duration, ) { addresses, err := net.LookupIP(record.Target) switch { @@ -278,21 +402,21 @@ if err == nil { switch record.Type { case "xmpp-client": - startTLS("client", c, tlsConfig, timeout) - c.Close() + startTLS("client", serverName, c, tlsConfig, timeout) + _ = c.Close() case "xmpps-client": tlsConfig.NextProtos = []string{"xmpp-client"} - directTLS("client", c, tlsConfig, timeout) - c.Close() + directTLS("client", serverName, c, tlsConfig, timeout) + _ = c.Close() case "xmpp-server": - startTLS("server", c, tlsConfig, timeout) - c.Close() + startTLS("server", serverName, c, tlsConfig, timeout) + _ = c.Close() case "xmpps-server": tlsConfig.NextProtos = []string{"xmpp-server"} - directTLS("server", c, tlsConfig, timeout) - c.Close() + directTLS("server", serverName, c, tlsConfig, timeout) + _ = c.Close() default: - c.Close() + _ = c.Close() } } } @@ -310,7 +434,7 @@ return c, err } -func startTLS(recordType string, c net.Conn, tlsConfig *tls.Config, timeout time.Duration) { +func startTLS(recordType string, serverName string, c net.Conn, tlsConfig *tls.Config, timeout time.Duration) { // Created with https://github.com/miku/zek type Proceed struct { XMLName xml.Name `xml:"proceed"` @@ -336,7 +460,7 @@ } startStream := "" + serverName + "' version='1.0'>" _, err := c.Write([]byte(startStream)) if err != nil { fmt.Println(statusNOK) @@ -389,14 +513,14 @@ fmt.Println("Server sent failure.") return } - _, err = c.Write([]byte(fmt.Sprintf("", nsStartTLS))) + _, err = fmt.Fprintf(c, "", nsStartTLS) if err != nil { fmt.Println(statusNOK) fmt.Println(err) return } - for !(serverProceed.XMLName.Local == "proceed" && - serverProceed.Xmlns == nsStartTLS) { + for serverProceed.XMLName.Local != "proceed" && + serverProceed.Xmlns != nsStartTLS { _, err = c.Read(buf) if err != nil { fmt.Println(statusNOK) @@ -440,11 +564,11 @@ } } else { checkCertExpiry(d) - d.Close() + _ = d.Close() } } -func directTLS(recordType string, conn net.Conn, tlsConfig *tls.Config, timeout time.Duration) { +func directTLS(recordType string, serverName string, conn net.Conn, tlsConfig *tls.Config, timeout time.Duration) { var serverStreamError StreamError c := tls.Client(conn, tlsConfig) ctx := context.Background() @@ -473,7 +597,7 @@ } startStream := "" + serverName + "' version='1.0'>" _, err := c.Write([]byte(startStream)) if err != nil { fmt.Println(statusNOK) @@ -524,8 +648,8 @@ } } -func checkHostmeta(server string, verbose bool, ipv4 bool, ipv6 bool, c2s bool, s2s bool, - test bool, tlsConfig *tls.Config, timeout time.Duration, dnsConfig xmppsrv.Config, +func checkHostmeta(server string, verbose bool, ipv4 bool, ipv6 bool, c2s bool, s2s bool, test bool, + serverName string, tlsConfig *tls.Config, timeout time.Duration, dnsConfig xmppsrv.Config, ) { type hostmetaXML struct { XMLName xml.Name `xml:"XRD"` @@ -549,7 +673,6 @@ if resp.StatusCode == 404 { return } - defer resp.Body.Close() body, err := io.ReadAll(resp.Body) if err != nil { return @@ -591,11 +714,12 @@ fmt.Printf("Unknown connection method: %s\n", link.Rel) } } + _ = resp.Body.Close() } } -func checkHostmeta2(server string, verbose bool, ipv4 bool, ipv6 bool, c2s bool, s2s bool, - test bool, tlsConfig *tls.Config, timeout time.Duration, dnsConfig xmppsrv.Config, +func checkHostmeta2(server string, verbose bool, ipv4 bool, ipv6 bool, c2s bool, s2s bool, test bool, + serverName string, tlsConfig *tls.Config, timeout time.Duration, dnsConfig xmppsrv.Config, ) { type hostmetaJSON struct { XMPP struct { @@ -625,7 +749,6 @@ if resp.StatusCode == 404 { return } - defer resp.Body.Close() body, err := io.ReadAll(resp.Body) if err != nil { return @@ -705,8 +828,8 @@ continue } tlsConfig.NextProtos = []string{"xmpp-client"} - directTLS("client", c, tlsConfig, timeout) - c.Close() + directTLS("client", serverName, c, tlsConfig, timeout) + _ = c.Close() } } } @@ -742,8 +865,8 @@ continue } tlsConfig.NextProtos = []string{"xmpp-server"} - directTLS("client", c, tlsConfig, timeout) - c.Close() + directTLS("client", serverName, c, tlsConfig, timeout) + _ = c.Close() } } } @@ -810,6 +933,7 @@ } } } + _ = resp.Body.Close() } func checkBOSH(server string, target string, ipv4 bool, ipv6 bool, tlsConfig *tls.Config, @@ -851,9 +975,8 @@ return } bodyXML := `` + `' to='` + server + `' ver='1.6' wait='0' ack='1' xml:lang='en'` + + ` xmlns='` + nsHTTPBind + `'/>` body := []byte(bodyXML) req, err := http.NewRequest("POST", target, bytes.NewBuffer(body)) if err != nil { @@ -861,6 +984,8 @@ fmt.Println(err) return } + tlsConfig.NextProtos = []string{"http/1.1", "h2"} + tlsConfig.ServerName = boshURI.Hostname() httpClient, err := getHTTPClient(boshURI.Hostname(), boshURI.Port(), ipv4, ipv6, timeout, tlsConfig, srvConfig) if err != nil { fmt.Println(statusNOK) @@ -873,7 +998,6 @@ fmt.Println(err) return } - defer resp.Body.Close() respBody, err := io.ReadAll(resp.Body) if err != nil { return @@ -901,7 +1025,14 @@ boshOpen.From, server) return } - fmt.Println(statusOK) + if resp.TLS != nil { + fmt.Print(statusOK) + printTLSVersion(resp.TLS.Version) + } else { + fmt.Printf("%s (%sno TLS%s)", statusNOK, colorRed, colorReset) + } + fmt.Println() + _ = resp.Body.Close() } func checkWebSocket(server string, target string, tlsConfig *tls.Config, timeout time.Duration, targetType string) { @@ -916,6 +1047,7 @@ } var wsOpen wsOpenXML var framing, from string + var dialer websocket.Dialer switch targetType { case "c2s": framing = nsXMPPFraming @@ -935,35 +1067,39 @@ fmt.Println(err) return } + headers := http.Header{} + headers.Add("Origin", origURI.String()) wsTLSConfig := &tls.Config{ MinVersion: tlsConfig.MinVersion, - ServerName: server, + ServerName: wsURI.Hostname(), InsecureSkipVerify: false, } - wsConfig := &websocket.Config{ - Location: wsURI, - Origin: origURI, - Version: 13, - TlsConfig: wsTLSConfig, - } - wsConfig.Protocol = append(wsConfig.Protocol, "xmpp") + // Websocket connections currently fail when using HTTP/2, but also none of the + // alternative libraries support it, so currently it is disabled. + // TODO: Switch library, once one supports it. + // See: + // https://github.com/coder/websocket/issues/4 + // https://github.com/gorilla/websocket/issues/417 + // wsTLSConfig.NextProtos = []string{"http/1.1", "h2"} + wsTLSConfig.NextProtos = []string{"http/1.1"} + dialer.TLSClientConfig = wsTLSConfig + dialer.Subprotocols = append(dialer.Subprotocols, "xmpp") ctx, cancel := context.WithTimeout(context.Background(), timeout) defer cancel() - wsConn, err := wsConfig.DialContext(ctx) + wsConn, resp, err := dialer.DialContext(ctx, wsURI.String(), headers) if err != nil { fmt.Println(statusNOK) fmt.Println(err) return } - _, err = wsConn.Write([]byte(fmt.Sprintf( + err = wsConn.WriteMessage(websocket.TextMessage, []byte(fmt.Sprintf( ``, framing, server, from))) if err != nil { fmt.Println(statusNOK) fmt.Println(err) return } - buf := make([]byte, 4096) - _, err = wsConn.Read(buf) + _, buf, err := wsConn.ReadMessage() if err != nil { fmt.Println(statusNOK) fmt.Println(err) @@ -981,13 +1117,22 @@ fmt.Printf("Server identifies as %s instead of %s.\n", wsOpen.From, server) return - } else if !wsConn.IsClientConn() { - _ = wsConn.Close() - fmt.Println(statusNOK) - fmt.Println("WS connection is no client connection.") - return } - fmt.Println(statusOK) + if resp.TLS != nil { + fmt.Print(statusOK) + printTLSVersion(resp.TLS.Version) + fmt.Println() + } else { + state := getTLS(wsConn) + if state != nil { + fmt.Print(statusOK) + printTLSVersion(state.Version) + fmt.Println() + } else { + fmt.Printf("%s (%sno TLS%s)", statusNOK, colorRed, colorReset) + fmt.Println() + } + } err = wsConn.Close() if err != nil { fmt.Println(err) @@ -1049,11 +1194,31 @@ expiry := c.ConnectionState().PeerCertificates[0].NotAfter start := c.ConnectionState().PeerCertificates[0].NotBefore now := time.Now() - if now.Before(expiry) && now.After(start) { - fmt.Println(statusOK) - } else { - fmt.Println(statusNOK) - fmt.Println("Valid from", start, "to", expiry) + switch { + case time.Until(expiry).Hours() < 336: + fmt.Printf("[%sOK%s]", colorYellow, colorReset) + printTLSVersion(c.ConnectionState().Version) + fmt.Printf(" (Cert expires in %d days at %s)\n", + int(time.Until(expiry).Hours())/24, expiry.Truncate(time.Second).String()) + case now.Before(expiry) && now.After(start): + fmt.Print(statusOK) + printTLSVersion(c.ConnectionState().Version) + fmt.Println() + default: + fmt.Print(statusNOK) + printTLSVersion(c.ConnectionState().Version) + fmt.Printf(" (valid from %s to %s)", start, expiry) + } +} + +func printTLSVersion(tv uint16) { + switch tv { + case tls.VersionTLS13: + fmt.Printf(" (%s%s%s)", colorGreen, tls.VersionName(tv), colorReset) + case tls.VersionTLS12: + fmt.Printf(" (%s%s%s)", colorYellow, tls.VersionName(tv), colorReset) + default: + fmt.Printf(" (%s%s%s)", colorRed, tls.VersionName(tv), colorReset) } } @@ -1089,3 +1254,18 @@ } return string(id) } + +func getTLS(c any) *tls.ConnectionState { + for c != nil { + if tc, ok := c.(*tls.Conn); ok { + state := tc.ConnectionState() + return &state + } + wc, ok := c.(interface{ NetConn() net.Conn }) + if !ok { + break + } + c = wc.NetConn() + } + return nil +} diff -Nru xmpp-dns-0.4.5/man/xmpp-dns.1 xmpp-dns-0.6.0/man/xmpp-dns.1 --- xmpp-dns-0.4.5/man/xmpp-dns.1 2024-12-11 20:41:01.000000000 +0000 +++ xmpp-dns-0.6.0/man/xmpp-dns.1 2026-01-15 19:07:10.000000000 +0000 @@ -1,10 +1,10 @@ -.\" generated with Ronn-NG/v0.9.1 -.\" http://github.com/apjanke/ronn-ng/tree/0.9.1 -.TH "XMPP\-DNS" "1" "April 2024" "" +.\" generated with Ronn-NG/v0.10.1 +.\" http://github.com/apjanke/ronn-ng/tree/0.10.1 +.TH "XMPP\-DNS" "1" "January 2026" "" .SH "NAME" \fBxmpp\-dns\fR \- A CLI tool to check XMPP SRV records\. .SH "SYNOPSIS" -\fBxmpp\-dns\fR [\-46cfstv] [\-\-help] [\-\-no\-color] [\-\-timeout value] [\-\-tls\-version value] [\-\-version] [parameters \|\.\|\.\|\.] +\fBxmpp\-dns\fR [\-46cfstv] [\-\-client\-dtls value] [\-\-client\-stls value] [\-\-dot] [\-\-force\-fallback] [\-\-help] [\-\-no\-color] [\-\-resolver value] [\-\-server\-dtls value] [\-\-server\-stls value] [\-\-timeout value] [\-\-tls\-version value] [\-\-version] \fBxmpp\-server\fR .SH "DESCRIPTION" A little CLI tool to check SRV records for XMPP\. Beside showing the records it can also try to connect to the server and also test StartTLS and direct TLS\. .SH "OPTIONS" @@ -18,11 +18,20 @@ \fB\-c\fR, \fB\-\-client\fR Show client SRV records\. .TP +\fB\-\-client\-dtls\fR=[\fIvalue\fR] +Add an arbitrary target for C2S with direct TLS in the form \fBdomain:port\fR\. Can be invoked several times\. +.TP +\fB\-\-client\-stls\fR=[\fIvalue\fR] +Add an arbitrary target for C2S with StartTLS in the form \fBdomain:port\fR\. Can be invoked several times\. +.TP \fB\-\-dot\fR Use DoT, see also \fB\-\-resolver\fR\. .TP \fB\-f\fR, \fB\-\-fallback\fR -Check fallback (Standard ports on A/AAAA records) if no SRV records are provided\. +Check fallback (standard ports on A/AAAA records) if no SRV records are provided\. +.TP +\fB\-\-force\-fallback\fR +Additionally heck fallback (standard ports on A/AAAA records) even if SRV records are provided\. .TP \fB\-\-help\fR Show help\. @@ -36,6 +45,12 @@ \fB\-s\fR, \fB\-\-server\fR Show server SRV records\. .TP +\fB\-\-server\-dtls\fR=[\fIvalue\fR] +Add an arbitrary target for S2S with direct TLS in the form \fBdomain:port\fR\. Can be invoked several times\. +.TP +\fB\-\-server\-stls\fR=[\fIvalue\fR] +Add an arbitrary target for S2S with StartTLS in the form \fBdomain:port\fR\. Can be invoked several times\. +.TP \fB\-t\fR Test connection and certificates\. .TP @@ -55,12 +70,18 @@ There are no shell completions yet (contributions welcome) but for zsh it is possible to automatically create completions from \fB\-\-help\fR which might work good enough\. .P Just place the following in your \fB~/\.zshrc\fR or \fB~/\.zshrc\.local\fR: -.P -\fBcompdef _gnu_generic xmpp\-dns\fR +.IP "" 4 +.nf +compdef _gnu_generic xmpp\-dns +.fi +.IP "" 0 .SS "FISH" There are no shell completions yet, but FISH can generate them from the man page with following command: -.P -\fBfish_update_completions\fR +.IP "" 4 +.nf +fish_update_completions +.fi +.IP "" 0 .SH "CHAT" There is no dedicated chat for \fBxmpp\-dns\fR, but feel free to join \fIhttps://join\.jabber\.network/#go\-sendxmpp@chat\.mdosch\.de?join\fR\. .SH "AUTHOR" diff -Nru xmpp-dns-0.4.5/man/xmpp-dns.1.html xmpp-dns-0.6.0/man/xmpp-dns.1.html --- xmpp-dns-0.4.5/man/xmpp-dns.1.html 2024-12-11 20:41:01.000000000 +0000 +++ xmpp-dns-0.6.0/man/xmpp-dns.1.html 2026-01-15 19:07:10.000000000 +0000 @@ -1,8 +1,8 @@ - - + + xmpp-dns(1) - A CLI tool to check XMPP SRV records.