Script 'mail_helper' called by obssrc Hello community, here is the log from the commit of package xmpp-dns for openSUSE:Factory checked in at 2024-08-19 23:44:52 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Comparing /work/SRC/openSUSE:Factory/xmpp-dns (Old) and /work/SRC/openSUSE:Factory/.xmpp-dns.new.2698 (New) ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Package is "xmpp-dns" Mon Aug 19 23:44:52 2024 rev:12 rq:1194637 version:0.4.0 Changes: -------- --- /work/SRC/openSUSE:Factory/xmpp-dns/xmpp-dns.changes 2024-04-08 17:51:55.184905419 +0200 +++ /work/SRC/openSUSE:Factory/.xmpp-dns.new.2698/xmpp-dns.changes 2024-08-19 23:45:21.717781395 +0200 @@ -1,0 +2,12 @@ +Mon Aug 19 05:51:44 UTC 2024 - Michael Vetter <mvetter@suse.com> + +- Update to 0.4.0: + Added: + * Support for XEP-0487 (Host Meta 2). + * Support for XEP-0156 (Discovering Alternative XMPP Connection Methods). + * Support for connecting over websockets. + Changed: + * Only check standard ports as fallback. + * Simplify output. + +------------------------------------------------------------------- Old: ---- xmpp-dns-0.3.11.tar.gz New: ---- xmpp-dns-0.4.0.tar.gz ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Other differences: ------------------ ++++++ xmpp-dns.spec ++++++ --- /var/tmp/diff_new_pack.fGYq4s/_old 2024-08-19 23:45:22.261804174 +0200 +++ /var/tmp/diff_new_pack.fGYq4s/_new 2024-08-19 23:45:22.261804174 +0200 @@ -17,7 +17,7 @@ Name: xmpp-dns -Version: 0.3.11 +Version: 0.4.0 Release: 0 Summary: A CLI tool to check XMPP SRV records License: BSD-2-Clause ++++++ _service ++++++ --- /var/tmp/diff_new_pack.fGYq4s/_old 2024-08-19 23:45:22.293805514 +0200 +++ /var/tmp/diff_new_pack.fGYq4s/_new 2024-08-19 23:45:22.297805681 +0200 @@ -3,7 +3,7 @@ <param name="url">https://salsa.debian.org/mdosch/xmpp-dns.git</param> <param name="scm">git</param> <param name="exclude">.git</param> - <param name="revision">v0.3.11</param> + <param name="revision">v0.4.0</param> <param name="versionformat">@PARENT_TAG@</param> <param name="changesgenerate">disable</param> <param name="versionrewrite-pattern">v(.*)</param> ++++++ vendor.tar.gz ++++++ diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/vendor/golang.org/x/net/LICENSE new/vendor/golang.org/x/net/LICENSE --- old/vendor/golang.org/x/net/LICENSE 1970-01-01 01:00:00.000000000 +0100 +++ new/vendor/golang.org/x/net/LICENSE 2024-08-19 07:51:29.000000000 +0200 @@ -0,0 +1,27 @@ +Copyright 2009 The Go Authors. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are +met: + + * Redistributions of source code must retain the above copyright +notice, this list of conditions and the following disclaimer. + * 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. + * Neither the name of Google LLC nor the names of its +contributors may be used to endorse or promote products derived from +this software without specific prior written permission. + +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 +OWNER 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. diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/vendor/golang.org/x/net/PATENTS new/vendor/golang.org/x/net/PATENTS --- old/vendor/golang.org/x/net/PATENTS 1970-01-01 01:00:00.000000000 +0100 +++ new/vendor/golang.org/x/net/PATENTS 2024-08-19 07:51:29.000000000 +0200 @@ -0,0 +1,22 @@ +Additional IP Rights Grant (Patents) + +"This implementation" means the copyrightable works distributed by +Google as part of the Go project. + +Google hereby grants to You a perpetual, worldwide, non-exclusive, +no-charge, royalty-free, irrevocable (except as stated in this section) +patent license to make, have made, use, offer to sell, sell, import, +transfer and otherwise run, modify and propagate the contents of this +implementation of Go, where such license applies only to those patent +claims, both currently owned or controlled by Google and acquired in +the future, licensable by Google that are necessarily infringed by this +implementation of Go. This grant does not include claims that would be +infringed only as a consequence of further modification of this +implementation. If you or your agent or exclusive licensee institute or +order or agree to the institution of patent litigation against any +entity (including a cross-claim or counterclaim in a lawsuit) alleging +that this implementation of Go or any code incorporated within this +implementation of Go constitutes direct or contributory patent +infringement, or inducement of patent infringement, then any patent +rights granted to you under this License for this implementation of Go +shall terminate as of the date such litigation is filed. diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/vendor/golang.org/x/net/websocket/client.go new/vendor/golang.org/x/net/websocket/client.go --- old/vendor/golang.org/x/net/websocket/client.go 1970-01-01 01:00:00.000000000 +0100 +++ new/vendor/golang.org/x/net/websocket/client.go 2024-08-19 07:51:29.000000000 +0200 @@ -0,0 +1,139 @@ +// Copyright 2009 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package websocket + +import ( + "bufio" + "context" + "io" + "net" + "net/http" + "net/url" + "time" +) + +// DialError is an error that occurs while dialling a websocket server. +type DialError struct { + *Config + Err error +} + +func (e *DialError) Error() string { + return "websocket.Dial " + e.Config.Location.String() + ": " + e.Err.Error() +} + +// NewConfig creates a new WebSocket config for client connection. +func NewConfig(server, origin string) (config *Config, err error) { + config = new(Config) + config.Version = ProtocolVersionHybi13 + config.Location, err = url.ParseRequestURI(server) + if err != nil { + return + } + config.Origin, err = url.ParseRequestURI(origin) + if err != nil { + return + } + config.Header = http.Header(make(map[string][]string)) + return +} + +// NewClient creates a new WebSocket client connection over rwc. +func NewClient(config *Config, rwc io.ReadWriteCloser) (ws *Conn, err error) { + br := bufio.NewReader(rwc) + bw := bufio.NewWriter(rwc) + err = hybiClientHandshake(config, br, bw) + if err != nil { + return + } + buf := bufio.NewReadWriter(br, bw) + ws = newHybiClientConn(config, buf, rwc) + return +} + +// Dial opens a new client connection to a WebSocket. +func Dial(url_, protocol, origin string) (ws *Conn, err error) { + config, err := NewConfig(url_, origin) + if err != nil { + return nil, err + } + if protocol != "" { + config.Protocol = []string{protocol} + } + return DialConfig(config) +} + +var portMap = map[string]string{ + "ws": "80", + "wss": "443", +} + +func parseAuthority(location *url.URL) string { + if _, ok := portMap[location.Scheme]; ok { + if _, _, err := net.SplitHostPort(location.Host); err != nil { + return net.JoinHostPort(location.Host, portMap[location.Scheme]) + } + } + return location.Host +} + +// DialConfig opens a new client connection to a WebSocket with a config. +func DialConfig(config *Config) (ws *Conn, err error) { + return config.DialContext(context.Background()) +} + +// DialContext opens a new client connection to a WebSocket, with context support for timeouts/cancellation. +func (config *Config) DialContext(ctx context.Context) (*Conn, error) { + if config.Location == nil { + return nil, &DialError{config, ErrBadWebSocketLocation} + } + if config.Origin == nil { + return nil, &DialError{config, ErrBadWebSocketOrigin} + } + + dialer := config.Dialer + if dialer == nil { + dialer = &net.Dialer{} + } + + client, err := dialWithDialer(ctx, dialer, config) + if err != nil { + return nil, &DialError{config, err} + } + + // Cleanup the connection if we fail to create the websocket successfully + success := false + defer func() { + if !success { + _ = client.Close() + } + }() + + var ws *Conn + var wsErr error + doneConnecting := make(chan struct{}) + go func() { + defer close(doneConnecting) + ws, err = NewClient(config, client) + if err != nil { + wsErr = &DialError{config, err} + } + }() + + // The websocket.NewClient() function can block indefinitely, make sure that we + // respect the deadlines specified by the context. + select { + case <-ctx.Done(): + // Force the pending operations to fail, terminating the pending connection attempt + _ = client.SetDeadline(time.Now()) + <-doneConnecting // Wait for the goroutine that tries to establish the connection to finish + return nil, &DialError{config, ctx.Err()} + case <-doneConnecting: + if wsErr == nil { + success = true // Disarm the deferred connection cleanup + } + return ws, wsErr + } +} diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/vendor/golang.org/x/net/websocket/dial.go new/vendor/golang.org/x/net/websocket/dial.go --- old/vendor/golang.org/x/net/websocket/dial.go 1970-01-01 01:00:00.000000000 +0100 +++ new/vendor/golang.org/x/net/websocket/dial.go 2024-08-19 07:51:29.000000000 +0200 @@ -0,0 +1,29 @@ +// Copyright 2015 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package websocket + +import ( + "context" + "crypto/tls" + "net" +) + +func dialWithDialer(ctx context.Context, dialer *net.Dialer, config *Config) (conn net.Conn, err error) { + switch config.Location.Scheme { + case "ws": + conn, err = dialer.DialContext(ctx, "tcp", parseAuthority(config.Location)) + + case "wss": + tlsDialer := &tls.Dialer{ + NetDialer: dialer, + Config: config.TlsConfig, + } + + conn, err = tlsDialer.DialContext(ctx, "tcp", parseAuthority(config.Location)) + default: + err = ErrBadScheme + } + return +} diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/vendor/golang.org/x/net/websocket/hybi.go new/vendor/golang.org/x/net/websocket/hybi.go --- old/vendor/golang.org/x/net/websocket/hybi.go 1970-01-01 01:00:00.000000000 +0100 +++ new/vendor/golang.org/x/net/websocket/hybi.go 2024-08-19 07:51:29.000000000 +0200 @@ -0,0 +1,582 @@ +// Copyright 2011 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package websocket + +// This file implements a protocol of hybi draft. +// http://tools.ietf.org/html/draft-ietf-hybi-thewebsocketprotocol-17 + +import ( + "bufio" + "bytes" + "crypto/rand" + "crypto/sha1" + "encoding/base64" + "encoding/binary" + "fmt" + "io" + "net/http" + "net/url" + "strings" +) + +const ( + websocketGUID = "258EAFA5-E914-47DA-95CA-C5AB0DC85B11" + + closeStatusNormal = 1000 + closeStatusGoingAway = 1001 + closeStatusProtocolError = 1002 + closeStatusUnsupportedData = 1003 + closeStatusFrameTooLarge = 1004 + closeStatusNoStatusRcvd = 1005 + closeStatusAbnormalClosure = 1006 + closeStatusBadMessageData = 1007 + closeStatusPolicyViolation = 1008 + closeStatusTooBigData = 1009 + closeStatusExtensionMismatch = 1010 + + maxControlFramePayloadLength = 125 +) + +var ( + ErrBadMaskingKey = &ProtocolError{"bad masking key"} + ErrBadPongMessage = &ProtocolError{"bad pong message"} + ErrBadClosingStatus = &ProtocolError{"bad closing status"} + ErrUnsupportedExtensions = &ProtocolError{"unsupported extensions"} + ErrNotImplemented = &ProtocolError{"not implemented"} + + handshakeHeader = map[string]bool{ + "Host": true, + "Upgrade": true, + "Connection": true, + "Sec-Websocket-Key": true, + "Sec-Websocket-Origin": true, + "Sec-Websocket-Version": true, + "Sec-Websocket-Protocol": true, + "Sec-Websocket-Accept": true, + } +) + +// A hybiFrameHeader is a frame header as defined in hybi draft. +type hybiFrameHeader struct { + Fin bool + Rsv [3]bool + OpCode byte + Length int64 + MaskingKey []byte + + data *bytes.Buffer +} + +// A hybiFrameReader is a reader for hybi frame. +type hybiFrameReader struct { + reader io.Reader + + header hybiFrameHeader + pos int64 + length int +} + +func (frame *hybiFrameReader) Read(msg []byte) (n int, err error) { + n, err = frame.reader.Read(msg) + if frame.header.MaskingKey != nil { + for i := 0; i < n; i++ { + msg[i] = msg[i] ^ frame.header.MaskingKey[frame.pos%4] + frame.pos++ + } + } + return n, err +} + +func (frame *hybiFrameReader) PayloadType() byte { return frame.header.OpCode } + +func (frame *hybiFrameReader) HeaderReader() io.Reader { + if frame.header.data == nil { + return nil + } + if frame.header.data.Len() == 0 { + return nil + } + return frame.header.data +} + +func (frame *hybiFrameReader) TrailerReader() io.Reader { return nil } + +func (frame *hybiFrameReader) Len() (n int) { return frame.length } + +// A hybiFrameReaderFactory creates new frame reader based on its frame type. +type hybiFrameReaderFactory struct { + *bufio.Reader +} + +// NewFrameReader reads a frame header from the connection, and creates new reader for the frame. +// See Section 5.2 Base Framing protocol for detail. +// http://tools.ietf.org/html/draft-ietf-hybi-thewebsocketprotocol-17#section-5... +func (buf hybiFrameReaderFactory) NewFrameReader() (frame frameReader, err error) { + hybiFrame := new(hybiFrameReader) + frame = hybiFrame + var header []byte + var b byte + // First byte. FIN/RSV1/RSV2/RSV3/OpCode(4bits) + b, err = buf.ReadByte() + if err != nil { + return + } + header = append(header, b) + hybiFrame.header.Fin = ((header[0] >> 7) & 1) != 0 + for i := 0; i < 3; i++ { + j := uint(6 - i) + hybiFrame.header.Rsv[i] = ((header[0] >> j) & 1) != 0 + } + hybiFrame.header.OpCode = header[0] & 0x0f + + // Second byte. Mask/Payload len(7bits) + b, err = buf.ReadByte() + if err != nil { + return + } + header = append(header, b) + mask := (b & 0x80) != 0 + b &= 0x7f + lengthFields := 0 + switch { + case b <= 125: // Payload length 7bits. + hybiFrame.header.Length = int64(b) + case b == 126: // Payload length 7+16bits + lengthFields = 2 + case b == 127: // Payload length 7+64bits + lengthFields = 8 + } + for i := 0; i < lengthFields; i++ { + b, err = buf.ReadByte() + if err != nil { + return + } + if lengthFields == 8 && i == 0 { // MSB must be zero when 7+64 bits + b &= 0x7f + } + header = append(header, b) + hybiFrame.header.Length = hybiFrame.header.Length*256 + int64(b) + } + if mask { + // Masking key. 4 bytes. + for i := 0; i < 4; i++ { + b, err = buf.ReadByte() + if err != nil { + return + } + header = append(header, b) + hybiFrame.header.MaskingKey = append(hybiFrame.header.MaskingKey, b) + } + } + hybiFrame.reader = io.LimitReader(buf.Reader, hybiFrame.header.Length) + hybiFrame.header.data = bytes.NewBuffer(header) + hybiFrame.length = len(header) + int(hybiFrame.header.Length) + return +} + +// A HybiFrameWriter is a writer for hybi frame. +type hybiFrameWriter struct { + writer *bufio.Writer + + header *hybiFrameHeader +} + +func (frame *hybiFrameWriter) Write(msg []byte) (n int, err error) { + var header []byte + var b byte + if frame.header.Fin { + b |= 0x80 + } + for i := 0; i < 3; i++ { + if frame.header.Rsv[i] { + j := uint(6 - i) + b |= 1 << j + } + } + b |= frame.header.OpCode + header = append(header, b) + if frame.header.MaskingKey != nil { + b = 0x80 + } else { + b = 0 + } + lengthFields := 0 + length := len(msg) + switch { + case length <= 125: + b |= byte(length) + case length < 65536: + b |= 126 + lengthFields = 2 + default: + b |= 127 + lengthFields = 8 + } + header = append(header, b) + for i := 0; i < lengthFields; i++ { + j := uint((lengthFields - i - 1) * 8) + b = byte((length >> j) & 0xff) + header = append(header, b) + } + if frame.header.MaskingKey != nil { + if len(frame.header.MaskingKey) != 4 { + return 0, ErrBadMaskingKey + } + header = append(header, frame.header.MaskingKey...) + frame.writer.Write(header) + data := make([]byte, length) + for i := range data { + data[i] = msg[i] ^ frame.header.MaskingKey[i%4] + } + frame.writer.Write(data) + err = frame.writer.Flush() + return length, err + } + frame.writer.Write(header) + frame.writer.Write(msg) + err = frame.writer.Flush() + return length, err +} + +func (frame *hybiFrameWriter) Close() error { return nil } + +type hybiFrameWriterFactory struct { + *bufio.Writer + needMaskingKey bool +} + +func (buf hybiFrameWriterFactory) NewFrameWriter(payloadType byte) (frame frameWriter, err error) { + frameHeader := &hybiFrameHeader{Fin: true, OpCode: payloadType} + if buf.needMaskingKey { + frameHeader.MaskingKey, err = generateMaskingKey() + if err != nil { + return nil, err + } + } + return &hybiFrameWriter{writer: buf.Writer, header: frameHeader}, nil +} + +type hybiFrameHandler struct { + conn *Conn + payloadType byte +} + +func (handler *hybiFrameHandler) HandleFrame(frame frameReader) (frameReader, error) { + if handler.conn.IsServerConn() { + // The client MUST mask all frames sent to the server. + if frame.(*hybiFrameReader).header.MaskingKey == nil { + handler.WriteClose(closeStatusProtocolError) + return nil, io.EOF + } + } else { + // The server MUST NOT mask all frames. + if frame.(*hybiFrameReader).header.MaskingKey != nil { + handler.WriteClose(closeStatusProtocolError) + return nil, io.EOF + } + } + if header := frame.HeaderReader(); header != nil { + io.Copy(io.Discard, header) + } + switch frame.PayloadType() { + case ContinuationFrame: + frame.(*hybiFrameReader).header.OpCode = handler.payloadType + case TextFrame, BinaryFrame: + handler.payloadType = frame.PayloadType() + case CloseFrame: + return nil, io.EOF + case PingFrame, PongFrame: + b := make([]byte, maxControlFramePayloadLength) + n, err := io.ReadFull(frame, b) + if err != nil && err != io.EOF && err != io.ErrUnexpectedEOF { + return nil, err + } + io.Copy(io.Discard, frame) + if frame.PayloadType() == PingFrame { + if _, err := handler.WritePong(b[:n]); err != nil { + return nil, err + } + } + return nil, nil + } + return frame, nil +} + +func (handler *hybiFrameHandler) WriteClose(status int) (err error) { + handler.conn.wio.Lock() + defer handler.conn.wio.Unlock() + w, err := handler.conn.frameWriterFactory.NewFrameWriter(CloseFrame) + if err != nil { + return err + } + msg := make([]byte, 2) + binary.BigEndian.PutUint16(msg, uint16(status)) + _, err = w.Write(msg) + w.Close() + return err +} + +func (handler *hybiFrameHandler) WritePong(msg []byte) (n int, err error) { + handler.conn.wio.Lock() + defer handler.conn.wio.Unlock() + w, err := handler.conn.frameWriterFactory.NewFrameWriter(PongFrame) + if err != nil { + return 0, err + } + n, err = w.Write(msg) + w.Close() + return n, err +} + +// newHybiConn creates a new WebSocket connection speaking hybi draft protocol. +func newHybiConn(config *Config, buf *bufio.ReadWriter, rwc io.ReadWriteCloser, request *http.Request) *Conn { + if buf == nil { + br := bufio.NewReader(rwc) + bw := bufio.NewWriter(rwc) + buf = bufio.NewReadWriter(br, bw) + } + ws := &Conn{config: config, request: request, buf: buf, rwc: rwc, + frameReaderFactory: hybiFrameReaderFactory{buf.Reader}, + frameWriterFactory: hybiFrameWriterFactory{ + buf.Writer, request == nil}, + PayloadType: TextFrame, + defaultCloseStatus: closeStatusNormal} + ws.frameHandler = &hybiFrameHandler{conn: ws} + return ws +} + +// generateMaskingKey generates a masking key for a frame. +func generateMaskingKey() (maskingKey []byte, err error) { + maskingKey = make([]byte, 4) + if _, err = io.ReadFull(rand.Reader, maskingKey); err != nil { + return + } + return +} + +// generateNonce generates a nonce consisting of a randomly selected 16-byte +// value that has been base64-encoded. +func generateNonce() (nonce []byte) { + key := make([]byte, 16) + if _, err := io.ReadFull(rand.Reader, key); err != nil { + panic(err) + } + nonce = make([]byte, 24) + base64.StdEncoding.Encode(nonce, key) + return +} + +// removeZone removes IPv6 zone identifier from host. +// E.g., "[fe80::1%en0]:8080" to "[fe80::1]:8080" +func removeZone(host string) string { + if !strings.HasPrefix(host, "[") { + return host + } + i := strings.LastIndex(host, "]") + if i < 0 { + return host + } + j := strings.LastIndex(host[:i], "%") + if j < 0 { + return host + } + return host[:j] + host[i:] +} + +// getNonceAccept computes the base64-encoded SHA-1 of the concatenation of +// the nonce ("Sec-WebSocket-Key" value) with the websocket GUID string. +func getNonceAccept(nonce []byte) (expected []byte, err error) { + h := sha1.New() + if _, err = h.Write(nonce); err != nil { + return + } + if _, err = h.Write([]byte(websocketGUID)); err != nil { + return + } + expected = make([]byte, 28) + base64.StdEncoding.Encode(expected, h.Sum(nil)) + return +} + +// Client handshake described in draft-ietf-hybi-thewebsocket-protocol-17 +func hybiClientHandshake(config *Config, br *bufio.Reader, bw *bufio.Writer) (err error) { + bw.WriteString("GET " + config.Location.RequestURI() + " HTTP/1.1\r\n") + + // According to RFC 6874, an HTTP client, proxy, or other + // intermediary must remove any IPv6 zone identifier attached + // to an outgoing URI. + bw.WriteString("Host: " + removeZone(config.Location.Host) + "\r\n") + bw.WriteString("Upgrade: websocket\r\n") + bw.WriteString("Connection: Upgrade\r\n") + nonce := generateNonce() + if config.handshakeData != nil { + nonce = []byte(config.handshakeData["key"]) + } + bw.WriteString("Sec-WebSocket-Key: " + string(nonce) + "\r\n") + bw.WriteString("Origin: " + strings.ToLower(config.Origin.String()) + "\r\n") + + if config.Version != ProtocolVersionHybi13 { + return ErrBadProtocolVersion + } + + bw.WriteString("Sec-WebSocket-Version: " + fmt.Sprintf("%d", config.Version) + "\r\n") + if len(config.Protocol) > 0 { + bw.WriteString("Sec-WebSocket-Protocol: " + strings.Join(config.Protocol, ", ") + "\r\n") + } + // TODO(ukai): send Sec-WebSocket-Extensions. + err = config.Header.WriteSubset(bw, handshakeHeader) + if err != nil { + return err + } + + bw.WriteString("\r\n") + if err = bw.Flush(); err != nil { + return err + } + + resp, err := http.ReadResponse(br, &http.Request{Method: "GET"}) + if err != nil { + return err + } + if resp.StatusCode != 101 { + return ErrBadStatus + } + if strings.ToLower(resp.Header.Get("Upgrade")) != "websocket" || + strings.ToLower(resp.Header.Get("Connection")) != "upgrade" { + return ErrBadUpgrade + } + expectedAccept, err := getNonceAccept(nonce) + if err != nil { + return err + } + if resp.Header.Get("Sec-WebSocket-Accept") != string(expectedAccept) { + return ErrChallengeResponse + } + if resp.Header.Get("Sec-WebSocket-Extensions") != "" { + return ErrUnsupportedExtensions + } + offeredProtocol := resp.Header.Get("Sec-WebSocket-Protocol") + if offeredProtocol != "" { + protocolMatched := false + for i := 0; i < len(config.Protocol); i++ { + if config.Protocol[i] == offeredProtocol { + protocolMatched = true + break + } + } + if !protocolMatched { + return ErrBadWebSocketProtocol + } + config.Protocol = []string{offeredProtocol} + } + + return nil +} + +// newHybiClientConn creates a client WebSocket connection after handshake. +func newHybiClientConn(config *Config, buf *bufio.ReadWriter, rwc io.ReadWriteCloser) *Conn { + return newHybiConn(config, buf, rwc, nil) +} + +// A HybiServerHandshaker performs a server handshake using hybi draft protocol. +type hybiServerHandshaker struct { + *Config + accept []byte +} + +func (c *hybiServerHandshaker) ReadHandshake(buf *bufio.Reader, req *http.Request) (code int, err error) { + c.Version = ProtocolVersionHybi13 + if req.Method != "GET" { + return http.StatusMethodNotAllowed, ErrBadRequestMethod + } + // HTTP version can be safely ignored. + + if strings.ToLower(req.Header.Get("Upgrade")) != "websocket" || + !strings.Contains(strings.ToLower(req.Header.Get("Connection")), "upgrade") { + return http.StatusBadRequest, ErrNotWebSocket + } + + key := req.Header.Get("Sec-Websocket-Key") + if key == "" { + return http.StatusBadRequest, ErrChallengeResponse + } + version := req.Header.Get("Sec-Websocket-Version") + switch version { + case "13": + c.Version = ProtocolVersionHybi13 + default: + return http.StatusBadRequest, ErrBadWebSocketVersion + } + var scheme string + if req.TLS != nil { + scheme = "wss" + } else { + scheme = "ws" + } + c.Location, err = url.ParseRequestURI(scheme + "://" + req.Host + req.URL.RequestURI()) + if err != nil { + return http.StatusBadRequest, err + } + protocol := strings.TrimSpace(req.Header.Get("Sec-Websocket-Protocol")) + if protocol != "" { + protocols := strings.Split(protocol, ",") + for i := 0; i < len(protocols); i++ { + c.Protocol = append(c.Protocol, strings.TrimSpace(protocols[i])) + } + } + c.accept, err = getNonceAccept([]byte(key)) + if err != nil { + return http.StatusInternalServerError, err + } + return http.StatusSwitchingProtocols, nil +} + +// Origin parses the Origin header in req. +// If the Origin header is not set, it returns nil and nil. +func Origin(config *Config, req *http.Request) (*url.URL, error) { + var origin string + switch config.Version { + case ProtocolVersionHybi13: + origin = req.Header.Get("Origin") + } + if origin == "" { + return nil, nil + } + return url.ParseRequestURI(origin) +} + +func (c *hybiServerHandshaker) AcceptHandshake(buf *bufio.Writer) (err error) { + if len(c.Protocol) > 0 { + if len(c.Protocol) != 1 { + // You need choose a Protocol in Handshake func in Server. + return ErrBadWebSocketProtocol + } + } + buf.WriteString("HTTP/1.1 101 Switching Protocols\r\n") + buf.WriteString("Upgrade: websocket\r\n") + buf.WriteString("Connection: Upgrade\r\n") + buf.WriteString("Sec-WebSocket-Accept: " + string(c.accept) + "\r\n") + if len(c.Protocol) > 0 { + buf.WriteString("Sec-WebSocket-Protocol: " + c.Protocol[0] + "\r\n") + } + // TODO(ukai): send Sec-WebSocket-Extensions. + if c.Header != nil { + err := c.Header.WriteSubset(buf, handshakeHeader) + if err != nil { + return err + } + } + buf.WriteString("\r\n") + return buf.Flush() +} + +func (c *hybiServerHandshaker) NewServerConn(buf *bufio.ReadWriter, rwc io.ReadWriteCloser, request *http.Request) *Conn { + return newHybiServerConn(c.Config, buf, rwc, request) +} + +// newHybiServerConn returns a new WebSocket connection speaking hybi draft protocol. +func newHybiServerConn(config *Config, buf *bufio.ReadWriter, rwc io.ReadWriteCloser, request *http.Request) *Conn { + return newHybiConn(config, buf, rwc, request) +} diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/vendor/golang.org/x/net/websocket/server.go new/vendor/golang.org/x/net/websocket/server.go --- old/vendor/golang.org/x/net/websocket/server.go 1970-01-01 01:00:00.000000000 +0100 +++ new/vendor/golang.org/x/net/websocket/server.go 2024-08-19 07:51:29.000000000 +0200 @@ -0,0 +1,113 @@ +// Copyright 2009 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package websocket + +import ( + "bufio" + "fmt" + "io" + "net/http" +) + +func newServerConn(rwc io.ReadWriteCloser, buf *bufio.ReadWriter, req *http.Request, config *Config, handshake func(*Config, *http.Request) error) (conn *Conn, err error) { + var hs serverHandshaker = &hybiServerHandshaker{Config: config} + code, err := hs.ReadHandshake(buf.Reader, req) + if err == ErrBadWebSocketVersion { + fmt.Fprintf(buf, "HTTP/1.1 %03d %s\r\n", code, http.StatusText(code)) + fmt.Fprintf(buf, "Sec-WebSocket-Version: %s\r\n", SupportedProtocolVersion) + buf.WriteString("\r\n") + buf.WriteString(err.Error()) + buf.Flush() + return + } + if err != nil { + fmt.Fprintf(buf, "HTTP/1.1 %03d %s\r\n", code, http.StatusText(code)) + buf.WriteString("\r\n") + buf.WriteString(err.Error()) + buf.Flush() + return + } + if handshake != nil { + err = handshake(config, req) + if err != nil { + code = http.StatusForbidden + fmt.Fprintf(buf, "HTTP/1.1 %03d %s\r\n", code, http.StatusText(code)) + buf.WriteString("\r\n") + buf.Flush() + return + } + } + err = hs.AcceptHandshake(buf.Writer) + if err != nil { + code = http.StatusBadRequest + fmt.Fprintf(buf, "HTTP/1.1 %03d %s\r\n", code, http.StatusText(code)) + buf.WriteString("\r\n") + buf.Flush() + return + } + conn = hs.NewServerConn(buf, rwc, req) + return +} + +// Server represents a server of a WebSocket. +type Server struct { + // Config is a WebSocket configuration for new WebSocket connection. + Config + + // Handshake is an optional function in WebSocket handshake. + // For example, you can check, or don't check Origin header. + // Another example, you can select config.Protocol. + Handshake func(*Config, *http.Request) error + + // Handler handles a WebSocket connection. + Handler +} + +// ServeHTTP implements the http.Handler interface for a WebSocket +func (s Server) ServeHTTP(w http.ResponseWriter, req *http.Request) { + s.serveWebSocket(w, req) +} + +func (s Server) serveWebSocket(w http.ResponseWriter, req *http.Request) { + rwc, buf, err := w.(http.Hijacker).Hijack() + if err != nil { + panic("Hijack failed: " + err.Error()) + } + // The server should abort the WebSocket connection if it finds + // the client did not send a handshake that matches with protocol + // specification. + defer rwc.Close() + conn, err := newServerConn(rwc, buf, req, &s.Config, s.Handshake) + if err != nil { + return + } + if conn == nil { + panic("unexpected nil conn") + } + s.Handler(conn) +} + +// Handler is a simple interface to a WebSocket browser client. +// It checks if Origin header is valid URL by default. +// You might want to verify websocket.Conn.Config().Origin in the func. +// If you use Server instead of Handler, you could call websocket.Origin and +// check the origin in your Handshake func. So, if you want to accept +// non-browser clients, which do not send an Origin header, set a +// Server.Handshake that does not check the origin. +type Handler func(*Conn) + +func checkOrigin(config *Config, req *http.Request) (err error) { + config.Origin, err = Origin(config, req) + if err == nil && config.Origin == nil { + return fmt.Errorf("null origin") + } + return err +} + +// ServeHTTP implements the http.Handler interface for a WebSocket +func (h Handler) ServeHTTP(w http.ResponseWriter, req *http.Request) { + s := Server{Handler: h, Handshake: checkOrigin} + s.serveWebSocket(w, req) +} diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/vendor/golang.org/x/net/websocket/websocket.go new/vendor/golang.org/x/net/websocket/websocket.go --- old/vendor/golang.org/x/net/websocket/websocket.go 1970-01-01 01:00:00.000000000 +0100 +++ new/vendor/golang.org/x/net/websocket/websocket.go 2024-08-19 07:51:29.000000000 +0200 @@ -0,0 +1,448 @@ +// Copyright 2009 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// Package websocket implements a client and server for the WebSocket protocol +// as specified in RFC 6455. +// +// This package currently lacks some features found in an alternative +// and more actively maintained WebSocket package: +// +// https://pkg.go.dev/nhooyr.io/websocket +package websocket // import "golang.org/x/net/websocket" + +import ( + "bufio" + "crypto/tls" + "encoding/json" + "errors" + "io" + "net" + "net/http" + "net/url" + "sync" + "time" +) + +const ( + ProtocolVersionHybi13 = 13 + ProtocolVersionHybi = ProtocolVersionHybi13 + SupportedProtocolVersion = "13" + + ContinuationFrame = 0 + TextFrame = 1 + BinaryFrame = 2 + CloseFrame = 8 + PingFrame = 9 + PongFrame = 10 + UnknownFrame = 255 + + DefaultMaxPayloadBytes = 32 << 20 // 32MB +) + +// ProtocolError represents WebSocket protocol errors. +type ProtocolError struct { + ErrorString string +} + +func (err *ProtocolError) Error() string { return err.ErrorString } + +var ( + ErrBadProtocolVersion = &ProtocolError{"bad protocol version"} + ErrBadScheme = &ProtocolError{"bad scheme"} + ErrBadStatus = &ProtocolError{"bad status"} + ErrBadUpgrade = &ProtocolError{"missing or bad upgrade"} + ErrBadWebSocketOrigin = &ProtocolError{"missing or bad WebSocket-Origin"} + ErrBadWebSocketLocation = &ProtocolError{"missing or bad WebSocket-Location"} + ErrBadWebSocketProtocol = &ProtocolError{"missing or bad WebSocket-Protocol"} + ErrBadWebSocketVersion = &ProtocolError{"missing or bad WebSocket Version"} + ErrChallengeResponse = &ProtocolError{"mismatch challenge/response"} + ErrBadFrame = &ProtocolError{"bad frame"} + ErrBadFrameBoundary = &ProtocolError{"not on frame boundary"} + ErrNotWebSocket = &ProtocolError{"not websocket protocol"} + ErrBadRequestMethod = &ProtocolError{"bad method"} + ErrNotSupported = &ProtocolError{"not supported"} +) + +// ErrFrameTooLarge is returned by Codec's Receive method if payload size +// exceeds limit set by Conn.MaxPayloadBytes +var ErrFrameTooLarge = errors.New("websocket: frame payload size exceeds limit") + +// Addr is an implementation of net.Addr for WebSocket. +type Addr struct { + *url.URL +} + +// Network returns the network type for a WebSocket, "websocket". +func (addr *Addr) Network() string { return "websocket" } + +// Config is a WebSocket configuration +type Config struct { + // A WebSocket server address. + Location *url.URL + + // A Websocket client origin. + Origin *url.URL + + // WebSocket subprotocols. + Protocol []string + + // WebSocket protocol version. + Version int + + // TLS config for secure WebSocket (wss). + TlsConfig *tls.Config + + // Additional header fields to be sent in WebSocket opening handshake. + Header http.Header + + // Dialer used when opening websocket connections. + Dialer *net.Dialer + + handshakeData map[string]string +} + +// serverHandshaker is an interface to handle WebSocket server side handshake. +type serverHandshaker interface { + // ReadHandshake reads handshake request message from client. + // Returns http response code and error if any. + ReadHandshake(buf *bufio.Reader, req *http.Request) (code int, err error) + + // AcceptHandshake accepts the client handshake request and sends + // handshake response back to client. + AcceptHandshake(buf *bufio.Writer) (err error) + + // NewServerConn creates a new WebSocket connection. + NewServerConn(buf *bufio.ReadWriter, rwc io.ReadWriteCloser, request *http.Request) (conn *Conn) +} + +// frameReader is an interface to read a WebSocket frame. +type frameReader interface { + // Reader is to read payload of the frame. + io.Reader + + // PayloadType returns payload type. + PayloadType() byte + + // HeaderReader returns a reader to read header of the frame. + HeaderReader() io.Reader + + // TrailerReader returns a reader to read trailer of the frame. + // If it returns nil, there is no trailer in the frame. + TrailerReader() io.Reader + + // Len returns total length of the frame, including header and trailer. + Len() int +} + +// frameReaderFactory is an interface to creates new frame reader. +type frameReaderFactory interface { + NewFrameReader() (r frameReader, err error) +} + +// frameWriter is an interface to write a WebSocket frame. +type frameWriter interface { + // Writer is to write payload of the frame. + io.WriteCloser +} + +// frameWriterFactory is an interface to create new frame writer. +type frameWriterFactory interface { + NewFrameWriter(payloadType byte) (w frameWriter, err error) +} + +type frameHandler interface { + HandleFrame(frame frameReader) (r frameReader, err error) + WriteClose(status int) (err error) +} + +// Conn represents a WebSocket connection. +// +// Multiple goroutines may invoke methods on a Conn simultaneously. +type Conn struct { + config *Config + request *http.Request + + buf *bufio.ReadWriter + rwc io.ReadWriteCloser + + rio sync.Mutex + frameReaderFactory + frameReader + + wio sync.Mutex + frameWriterFactory + + frameHandler + PayloadType byte + defaultCloseStatus int + + // MaxPayloadBytes limits the size of frame payload received over Conn + // by Codec's Receive method. If zero, DefaultMaxPayloadBytes is used. + MaxPayloadBytes int +} + +// Read implements the io.Reader interface: +// it reads data of a frame from the WebSocket connection. +// if msg is not large enough for the frame data, it fills the msg and next Read +// will read the rest of the frame data. +// it reads Text frame or Binary frame. +func (ws *Conn) Read(msg []byte) (n int, err error) { + ws.rio.Lock() + defer ws.rio.Unlock() +again: + if ws.frameReader == nil { + frame, err := ws.frameReaderFactory.NewFrameReader() + if err != nil { + return 0, err + } + ws.frameReader, err = ws.frameHandler.HandleFrame(frame) + if err != nil { + return 0, err + } + if ws.frameReader == nil { + goto again + } + } + n, err = ws.frameReader.Read(msg) + if err == io.EOF { + if trailer := ws.frameReader.TrailerReader(); trailer != nil { + io.Copy(io.Discard, trailer) + } + ws.frameReader = nil + goto again + } + return n, err +} + +// Write implements the io.Writer interface: +// it writes data as a frame to the WebSocket connection. +func (ws *Conn) Write(msg []byte) (n int, err error) { + ws.wio.Lock() + defer ws.wio.Unlock() + w, err := ws.frameWriterFactory.NewFrameWriter(ws.PayloadType) + if err != nil { + return 0, err + } + n, err = w.Write(msg) + w.Close() + return n, err +} + +// Close implements the io.Closer interface. +func (ws *Conn) Close() error { + err := ws.frameHandler.WriteClose(ws.defaultCloseStatus) + err1 := ws.rwc.Close() + if err != nil { + return err + } + return err1 +} + +// IsClientConn reports whether ws is a client-side connection. +func (ws *Conn) IsClientConn() bool { return ws.request == nil } + +// IsServerConn reports whether ws is a server-side connection. +func (ws *Conn) IsServerConn() bool { return ws.request != nil } + +// LocalAddr returns the WebSocket Origin for the connection for client, or +// the WebSocket location for server. +func (ws *Conn) LocalAddr() net.Addr { + if ws.IsClientConn() { + return &Addr{ws.config.Origin} + } + return &Addr{ws.config.Location} +} + +// RemoteAddr returns the WebSocket location for the connection for client, or +// the Websocket Origin for server. +func (ws *Conn) RemoteAddr() net.Addr { + if ws.IsClientConn() { + return &Addr{ws.config.Location} + } + return &Addr{ws.config.Origin} +} + +var errSetDeadline = errors.New("websocket: cannot set deadline: not using a net.Conn") + +// SetDeadline sets the connection's network read & write deadlines. +func (ws *Conn) SetDeadline(t time.Time) error { + if conn, ok := ws.rwc.(net.Conn); ok { + return conn.SetDeadline(t) + } + return errSetDeadline +} + +// SetReadDeadline sets the connection's network read deadline. +func (ws *Conn) SetReadDeadline(t time.Time) error { + if conn, ok := ws.rwc.(net.Conn); ok { + return conn.SetReadDeadline(t) + } + return errSetDeadline +} + +// SetWriteDeadline sets the connection's network write deadline. +func (ws *Conn) SetWriteDeadline(t time.Time) error { + if conn, ok := ws.rwc.(net.Conn); ok { + return conn.SetWriteDeadline(t) + } + return errSetDeadline +} + +// Config returns the WebSocket config. +func (ws *Conn) Config() *Config { return ws.config } + +// Request returns the http request upgraded to the WebSocket. +// It is nil for client side. +func (ws *Conn) Request() *http.Request { return ws.request } + +// Codec represents a symmetric pair of functions that implement a codec. +type Codec struct { + Marshal func(v interface{}) (data []byte, payloadType byte, err error) + Unmarshal func(data []byte, payloadType byte, v interface{}) (err error) +} + +// Send sends v marshaled by cd.Marshal as single frame to ws. +func (cd Codec) Send(ws *Conn, v interface{}) (err error) { + data, payloadType, err := cd.Marshal(v) + if err != nil { + return err + } + ws.wio.Lock() + defer ws.wio.Unlock() + w, err := ws.frameWriterFactory.NewFrameWriter(payloadType) + if err != nil { + return err + } + _, err = w.Write(data) + w.Close() + return err +} + +// Receive receives single frame from ws, unmarshaled by cd.Unmarshal and stores +// in v. The whole frame payload is read to an in-memory buffer; max size of +// payload is defined by ws.MaxPayloadBytes. If frame payload size exceeds +// limit, ErrFrameTooLarge is returned; in this case frame is not read off wire +// completely. The next call to Receive would read and discard leftover data of +// previous oversized frame before processing next frame. +func (cd Codec) Receive(ws *Conn, v interface{}) (err error) { + ws.rio.Lock() + defer ws.rio.Unlock() + if ws.frameReader != nil { + _, err = io.Copy(io.Discard, ws.frameReader) + if err != nil { + return err + } + ws.frameReader = nil + } +again: + frame, err := ws.frameReaderFactory.NewFrameReader() + if err != nil { + return err + } + frame, err = ws.frameHandler.HandleFrame(frame) + if err != nil { + return err + } + if frame == nil { + goto again + } + maxPayloadBytes := ws.MaxPayloadBytes + if maxPayloadBytes == 0 { + maxPayloadBytes = DefaultMaxPayloadBytes + } + if hf, ok := frame.(*hybiFrameReader); ok && hf.header.Length > int64(maxPayloadBytes) { + // payload size exceeds limit, no need to call Unmarshal + // + // set frameReader to current oversized frame so that + // the next call to this function can drain leftover + // data before processing the next frame + ws.frameReader = frame + return ErrFrameTooLarge + } + payloadType := frame.PayloadType() + data, err := io.ReadAll(frame) + if err != nil { + return err + } + return cd.Unmarshal(data, payloadType, v) +} + +func marshal(v interface{}) (msg []byte, payloadType byte, err error) { + switch data := v.(type) { + case string: + return []byte(data), TextFrame, nil + case []byte: + return data, BinaryFrame, nil + } + return nil, UnknownFrame, ErrNotSupported +} + +func unmarshal(msg []byte, payloadType byte, v interface{}) (err error) { + switch data := v.(type) { + case *string: + *data = string(msg) + return nil + case *[]byte: + *data = msg + return nil + } + return ErrNotSupported +} + +/* +Message is a codec to send/receive text/binary data in a frame on WebSocket connection. +To send/receive text frame, use string type. +To send/receive binary frame, use []byte type. + +Trivial usage: + + import "websocket" + + // receive text frame + var message string + websocket.Message.Receive(ws, &message) + + // send text frame + message = "hello" + websocket.Message.Send(ws, message) + + // receive binary frame + var data []byte + websocket.Message.Receive(ws, &data) + + // send binary frame + data = []byte{0, 1, 2} + websocket.Message.Send(ws, data) +*/ +var Message = Codec{marshal, unmarshal} + +func jsonMarshal(v interface{}) (msg []byte, payloadType byte, err error) { + msg, err = json.Marshal(v) + return msg, TextFrame, err +} + +func jsonUnmarshal(msg []byte, payloadType byte, v interface{}) (err error) { + return json.Unmarshal(msg, v) +} + +/* +JSON is a codec to send/receive JSON data in a frame from a WebSocket connection. + +Trivial usage: + + import "websocket" + + type T struct { + Msg string + Count int + } + + // receive JSON type T + var data T + websocket.JSON.Receive(ws, &data) + + // send JSON type T + websocket.JSON.Send(ws, data) +*/ +var JSON = Codec{jsonMarshal, jsonUnmarshal} diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/vendor/modules.txt new/vendor/modules.txt --- old/vendor/modules.txt 2024-04-08 08:15:36.000000000 +0200 +++ new/vendor/modules.txt 2024-08-19 07:51:29.000000000 +0200 @@ -1,6 +1,9 @@ # github.com/pborman/getopt/v2 v2.1.0 ## explicit github.com/pborman/getopt/v2 +# golang.org/x/net v0.28.0 +## explicit +golang.org/x/net/websocket # salsa.debian.org/mdosch/xmppsrv v0.2.6 ## explicit salsa.debian.org/mdosch/xmppsrv ++++++ xmpp-dns-0.3.11.tar.gz -> xmpp-dns-0.4.0.tar.gz ++++++ diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/xmpp-dns-0.3.11/.gitlab-ci.yml new/xmpp-dns-0.4.0/.gitlab-ci.yml --- old/xmpp-dns-0.3.11/.gitlab-ci.yml 2024-04-07 13:22:46.000000000 +0200 +++ new/xmpp-dns-0.4.0/.gitlab-ci.yml 2024-08-18 12:32:15.000000000 +0200 @@ -36,21 +36,21 @@ 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 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 + - upx $CI_PROJECT_DIR/linux-amd64/xmpp-dns || true - env GOOS=linux GOARCH=arm64 go build -buildmode=pie -ldflags "-s -w -extldflags '-static'" -o $CI_PROJECT_DIR/linux-arm64/xmpp-dns - - env GOOS=linux GOARCH=386 go build -ldflags "-s -w -extldflags '-static'" -o $CI_PROJECT_DIR/linux-386/xmpp-dns - - env GOOS=linux GOARCH=arm go build -ldflags "-s -w -extldflags '-static'" -o $CI_PROJECT_DIR/linux-arm/xmpp-dns - - env GOOS=windows GOARCH=386 go build -buildmode=pie -ldflags "-s -w -extldflags '-static'" -o $CI_PROJECT_DIR/win386/xmpp-dns.exe + - upx $CI_PROJECT_DIR/linux-arm64/xmpp-dns || true - env GOOS=windows GOARCH=amd64 go build -buildmode=pie -ldflags "-s -w -extldflags '-static'" -o $CI_PROJECT_DIR/win64/xmpp-dns.exe + - upx $CI_PROJECT_DIR/win64/xmpp-dns.exe || true artifacts: paths: - linux-amd64/xmpp-dns - linux-arm64/xmpp-dns - - linux-386/xmpp-dns - - linux-arm/xmpp-dns - - win386/xmpp-dns.exe - win64/xmpp-dns.exe - CI_JOB_ID.txt expire_in: 2 years @@ -65,7 +65,4 @@ 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`" \ --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\":\"Linux 386\",\"url\":\"https://salsa.debian.org/mdosch/xmpp-dns/-/jobs/`cat CI_JOB_ID.txt`/artifacts/file/linux-386/xmpp-dns\"}" \ - --assets-link "{\"name\":\"Linux arm\",\"url\":\"https://salsa.debian.org/mdosch/xmpp-dns/-/jobs/`cat CI_JOB_ID.txt`/artifacts/file/linux-arm/xmpp-dns\"}" \ - --assets-link "{\"name\":\"Windows 386\",\"url\":\"https://salsa.debian.org/mdosch/xmpp-dns/-/jobs/`cat CI_JOB_ID.txt`/artifacts/file/win386/xmpp-dns.exe\"}" \ --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\"}" diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/xmpp-dns-0.3.11/CHANGELOG.md new/xmpp-dns-0.4.0/CHANGELOG.md --- old/xmpp-dns-0.3.11/CHANGELOG.md 2024-04-07 13:22:46.000000000 +0200 +++ new/xmpp-dns-0.4.0/CHANGELOG.md 2024-08-18 12:32:15.000000000 +0200 @@ -1,5 +1,15 @@ # Changelog +## [0.4.0] 2024-08-18 +### Added +- Support for XEP-0487 (Host Meta 2). +- Support for XEP-0156 (Discovering Alternative XMPP Connection Methods). +- Support for connecting over websockets. + +### Changed +- Only check standard ports as fallback. +- Simplify output. + ## [0.3.11] 2024-04-07 ### Changed - Properly detect all stream errors. diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/xmpp-dns-0.3.11/go.mod new/xmpp-dns-0.4.0/go.mod --- old/xmpp-dns-0.3.11/go.mod 2024-04-07 13:22:46.000000000 +0200 +++ new/xmpp-dns-0.4.0/go.mod 2024-08-18 12:32:15.000000000 +0200 @@ -4,5 +4,6 @@ require ( github.com/pborman/getopt/v2 v2.1.0 + golang.org/x/net v0.28.0 salsa.debian.org/mdosch/xmppsrv v0.2.6 ) diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/xmpp-dns-0.3.11/go.sum new/xmpp-dns-0.4.0/go.sum --- old/xmpp-dns-0.3.11/go.sum 2024-04-07 13:22:46.000000000 +0200 +++ new/xmpp-dns-0.4.0/go.sum 2024-08-18 12:32:15.000000000 +0200 @@ -1,4 +1,70 @@ +github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= 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= +github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY= +golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= +golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= +golang.org/x/crypto v0.13.0/go.mod h1:y6Z2r+Rw4iayiXXAIxJIDAJ1zMW4yaTpebo8fPOliYc= +golang.org/x/crypto v0.19.0/go.mod h1:Iy9bg/ha4yyC70EfRS8jz+B6ybOBKMaSxLj6P6oBDfU= +golang.org/x/crypto v0.23.0/go.mod h1:CKFgDieR+mRhux2Lsu27y0fO304Db0wZe70UKqHu0v8= +golang.org/x/crypto v0.26.0/go.mod h1:GY7jblb9wI+FOo5y8/S2oY4zWP07AkOJ4+jxCqdqn54= +golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4= +golang.org/x/mod v0.8.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= +golang.org/x/mod v0.12.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= +golang.org/x/mod v0.15.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c= +golang.org/x/mod v0.17.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c= +golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= +golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= +golang.org/x/net v0.6.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs= +golang.org/x/net v0.10.0/go.mod h1:0qNGK6F8kojg2nk9dLZ2mShWaEBan6FAoqfSigmmuDg= +golang.org/x/net v0.15.0/go.mod h1:idbUs1IY1+zTqbi8yxTbhexhEEk5ur9LInksu6HrEpk= +golang.org/x/net v0.21.0/go.mod h1:bIjVDfnllIU7BJ2DNgfnXvpSvtn8VRwhlsaeUTyUS44= +golang.org/x/net v0.25.0/go.mod h1:JkAGAh7GEvH74S6FOH42FLoXpXbE/aqXSrIQjXgsiwM= +golang.org/x/net v0.28.0 h1:a9JDOJc5GMUJ0+UDqmLT86WiEy7iWyIhz8gz8E4e5hE= +golang.org/x/net v0.28.0/go.mod h1:yqtgsTWOOnlGLG9GFRrK3++bGOUEkNBoHZc8MEDWPNg= +golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.3.0/go.mod h1:FU7BRWz2tNW+3quACPkgCx/L+uEAv1htQ0V83Z9Rj+Y= +golang.org/x/sync v0.6.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= +golang.org/x/sync v0.7.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= +golang.org/x/sync v0.8.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= +golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.12.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.17.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= +golang.org/x/sys v0.20.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= +golang.org/x/sys v0.23.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= +golang.org/x/telemetry v0.0.0-20240228155512-f48c80bd79b2/go.mod h1:TeRTkGYfJXctD9OcfyVLyj2J3IxLnKwHJR8f4D8a3YE= +golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= +golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= +golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k= +golang.org/x/term v0.8.0/go.mod h1:xPskH00ivmX89bAKVGSKKtLOWNx2+17Eiy94tnKShWo= +golang.org/x/term v0.12.0/go.mod h1:owVbMEjm3cBLCHdkQu9b1opXd4ETQWc3BhuQGKgXgvU= +golang.org/x/term v0.17.0/go.mod h1:lLRBjIVuehSbZlaOtGMbcMncT+aqLLLmKrsjNrUguwk= +golang.org/x/term v0.20.0/go.mod h1:8UkIAJTvZgivsXaD6/pH6U9ecQzZ45awqEOzuCvwpFY= +golang.org/x/term v0.23.0/go.mod h1:DgV24QBUrK6jhZXl+20l6UWznPlwAHm1Q1mGHtydmSk= +golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= +golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= +golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= +golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= +golang.org/x/text v0.9.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8= +golang.org/x/text v0.13.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE= +golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU= +golang.org/x/text v0.15.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU= +golang.org/x/text v0.17.0/go.mod h1:BuEKDfySbSR4drPmRPG/7iBdf8hvFMuRexcpahXilzY= +golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc= +golang.org/x/tools v0.6.0/go.mod h1:Xwgl3UAJ/d3gWutnCtw505GrjyAbvKui8lOU390QaIU= +golang.org/x/tools v0.13.0/go.mod h1:HvlwmtVNQAhOuCjW7xxvovg8wbNq7LwfXh/k7wXUl58= +golang.org/x/tools v0.21.1-0.20240508182429-e35e4ccd0d2d/go.mod h1:aiJjzUbINMkxbQROHiO6hDPo2LHcIPhhQsa9DLh0yGk= +golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= salsa.debian.org/mdosch/xmppsrv v0.2.6 h1:+S7ZxRP7BQwQHGJvYhr408UD0XGAd7+RZaVA/xN4EIc= salsa.debian.org/mdosch/xmppsrv v0.2.6/go.mod h1:udWXnWFa9zkcyN9YSB/u44BCnnRDpeQ0eDy3MVLjHZQ= diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/xmpp-dns-0.3.11/main.go new/xmpp-dns-0.4.0/main.go --- old/xmpp-dns-0.3.11/main.go 2024-04-07 13:22:46.000000000 +0200 +++ new/xmpp-dns-0.4.0/main.go 2024-08-18 12:32:15.000000000 +0200 @@ -7,21 +7,28 @@ import ( "context" "crypto/tls" + "encoding/json" "encoding/xml" "fmt" + "io" "log" "net" + "net/http" + "net/url" "os" "runtime" "strings" "time" "github.com/pborman/getopt/v2" // BSD-3-Clause + "golang.org/x/net/websocket" // BSD-3-Clause "salsa.debian.org/mdosch/xmppsrv" // BSD-2-Clause ) const ( - version = "0.3.11" + version = "0.4.0" + nsWebsocket = "urn:xmpp:alt-connections:websocket" + nsBOSH = "urn:xmpp:alt-connections:xbosh" ) // Created with https://github.com/miku/zek @@ -172,21 +179,14 @@ Target: server[0], Port: 5222, }, - { - Type: "xmpps-client", - Target: server[0], - Port: 5223, - }, - { - Type: "xmpps-client", - Target: server[0], - Port: 443, - }, } } } checkRecord(clientRecords, *flagVerbose, *flagV4, *flagV6, *flagTest, &tlsConfig, timeout) + + checkHostmeta(server[0], *flagVerbose, *flagV4, + *flagV6, *flagTest, &tlsConfig, timeout) } if *flagServer { @@ -205,11 +205,6 @@ Target: server[0], Port: 5269, }, - { - Type: "xmpps-server", - Target: server[0], - Port: 5270, - }, } } } @@ -292,11 +287,10 @@ func connectionTest(server string, transport string, timeout time.Duration) (net.Conn, error) { c, err := net.DialTimeout(transport, server, timeout) if err != nil { - fmt.Println("Connection:", statusNOK) + fmt.Println("Test:", statusNOK) fmt.Println(err) return c, err } - fmt.Println("Connection:", statusOK) return c, err } @@ -329,7 +323,7 @@ tlsConfig.ServerName + "' version='1.0'>" _, err := c.Write([]byte(startStream)) if err != nil { - fmt.Println("StartTLS:", statusNOK) + fmt.Println("Test:", statusNOK) fmt.Println(err) return } @@ -345,11 +339,12 @@ case <-ctx.Done(): return default: - fmt.Println("StartTLS:", statusNOK) + fmt.Println("Test:", statusNOK) if err.Error() == "EOF" { err = xml.Unmarshal(buf, &serverStreamError) if err == nil { - fmt.Printf("StreamError: %s: %s\n", serverStreamError.Error.Any.Local, + fmt.Printf("stream error: %s: %s\n", + serverStreamError.Error.Any.Local, serverStreamError.Error.Text.Text) } else { fmt.Println("Server closed connection.") @@ -367,20 +362,20 @@ case <-time.After(timeout): } if buf == nil { - fmt.Println("StartTLS:", statusNOK) + fmt.Println("Test:", statusNOK) fmt.Println("Timeout while waiting for server reply.") cancel() return } _ = xml.Unmarshal(buf, &serverFailure) if serverFailure.XMLName.Local == "failure" { - fmt.Println("StartTLS:", statusNOK) + fmt.Println("Test:", statusNOK) fmt.Println("Server sent failure.") return } _, err = c.Write([]byte("<starttls xmlns='urn:ietf:params:xml:ns:xmpp-tls'/>")) if err != nil { - fmt.Println("StartTLS:", statusNOK) + fmt.Println("Test:", statusNOK) fmt.Println(err) return } @@ -388,11 +383,12 @@ serverProceed.Xmlns == "urn:ietf:params:xml:ns:xmpp-tls") { _, err = c.Read(buf) if err != nil { - fmt.Println("StartTLS:", statusNOK) + fmt.Println("Test:", statusNOK) if err.Error() == "EOF" { err = xml.Unmarshal(buf, &serverStreamError) if err == nil { - fmt.Printf("StreamError: %s: %s\n", serverStreamError.Error.Any.Local, + fmt.Printf("stream error: %s: %s\n", + serverStreamError.Error.Any.Local, serverStreamError.Error.Text.Text) } else { fmt.Println("Server closed connection.") @@ -405,7 +401,7 @@ _ = xml.Unmarshal(buf, &serverProceed) _ = xml.Unmarshal(buf, &serverFailure) if serverFailure.XMLName.Local == "failure" { - fmt.Println("StartTLS:", statusNOK) + fmt.Println("Test:", statusNOK) fmt.Println("Server sent failure.") return } @@ -417,17 +413,16 @@ if err != nil { switch err.Error() { case "EOF": - fmt.Println("StartTLS:", statusNOK) + fmt.Println("Test:", statusNOK) fmt.Println("Server closed connection during handshake.") case "context deadline exceeded": - fmt.Println("StartTLS:", statusNOK) + fmt.Println("Test:", statusNOK) fmt.Println("Timeout during handshake.") default: - fmt.Println("STartTLS:", statusNOK) + fmt.Println("Test:", statusNOK) fmt.Println(err) } } else { - fmt.Println("StartTLS:", statusOK) checkCertExpiry(d) d.Close() } @@ -443,13 +438,13 @@ if err != nil { switch err.Error() { case "EOF": - fmt.Println("TLS:", statusNOK) + fmt.Println("Test:", statusNOK) fmt.Println("Server closed connection during handshake.") case "context deadline exceeded": - fmt.Println("TLS:", statusNOK) + fmt.Println("Test:", statusNOK) fmt.Println("Timeout during handshake.") default: - fmt.Println("TLS:", statusNOK) + fmt.Println("Test:", statusNOK) fmt.Println(err) } } else { @@ -464,7 +459,7 @@ tlsConfig.ServerName + "' version='1.0'>" _, err := c.Write([]byte(startStream)) if err != nil { - fmt.Println("TLS:", statusNOK) + fmt.Println("Test:", statusNOK) fmt.Println(err) return } @@ -475,7 +470,7 @@ for { _, err = c.Read(buf) if err != nil { - fmt.Println("TLS:", statusNOK) + fmt.Println("Test:", statusNOK) if err.Error() == "EOF" { err = xml.Unmarshal(buf, &serverStreamError) if err == nil { @@ -495,7 +490,6 @@ "xmlns:stream='http://etherx.jabber.org/streams'") || strings.Contains(strings.ToLower(string(buf[:])), `xmlns:stream="http://etherx.jabber.org/streams"`)) { - fmt.Println("TLS:", statusOK) checkCertExpiry(c) cancel3() break @@ -507,20 +501,213 @@ select { case <-ctx3.Done(): case <-time.After(timeout): - fmt.Println("TLS:", statusNOK) + fmt.Println("Test:", statusNOK) fmt.Println("Timeout during XMPP stream negotiation.") } } } +func checkHostmeta(server string, verbose bool, ipv4 bool, ipv6 bool, test bool, tlsConfig *tls.Config, timeout time.Duration) { + type hostmetaXML struct { + XMLName xml.Name `xml:"XRD"` + Text string `xml:",chardata"` + Xmlns string `xml:"xmlns,attr"` + Link []struct { + Text string `xml:",chardata"` + Rel string `xml:"rel,attr"` + Href string `xml:"href,attr"` + } `xml:"Link"` + } + type hostmetaJSON struct { + Links []struct { + Rel string `json:"rel"` + Href string `json:"href"` + } `json:"links"` + } + + var hostmeta hostmetaXML + var connType string + switch { + case ipv4: + connType = "tcp4" + case ipv6: + connType = "tcp6" + default: + connType = "tcp" + } + httpTLSConfig := &tls.Config{ + MinVersion: tlsConfig.MinVersion, + ServerName: server, + InsecureSkipVerify: false, + } + httpTransport := &http.Transport{ + DialContext: func(ctx context.Context, network, addr string) (net.Conn, error) { + return net.Dial(connType, net.JoinHostPort(server, "443")) + }, + IdleConnTimeout: timeout, + TLSClientConfig: httpTLSConfig, + TLSHandshakeTimeout: timeout, + } + httpClient := &http.Client{Transport: httpTransport} + resp, err := httpClient.Get("https://" + server + "/.well-known/host-meta") + if err != nil { + fmt.Printf("Host-meta: failed to request host-meta file: %v\n", err) + return + } + if resp.StatusCode == 404 { + return + } + defer resp.Body.Close() + body, err := io.ReadAll(resp.Body) + if err != nil { + return + } + err = xml.Unmarshal(body, &hostmeta) + if err != nil { + return + } + fmt.Println("\nHost-meta:") + for _, link := range hostmeta.Link { + switch link.Rel { + case nsWebsocket: + fmt.Println("Websocket:", link.Href) + if test { + checkWebSocket(server, link.Href, tlsConfig, timeout) + } + case nsBOSH: + fmt.Println("BOSH:", link.Href) + if test { + fmt.Println("Testing BOSH is currently not supported.") + } + } + } + resp, err = httpClient.Get("https://" + server + "/.well-known/host-meta.json") + if err != nil { + fmt.Printf("Host-meta2: failed to request host-meta file: %v\n", err) + return + } + if resp.StatusCode == 404 { + return + } + defer resp.Body.Close() + body, err = io.ReadAll(resp.Body) + if err != nil { + return + } + hostmeta2 := hostmetaJSON{} + err = json.Unmarshal(body, &hostmeta2) + if err != nil { + return + } + fmt.Println("\nHost-meta2:") + for _, link := range hostmeta2.Links { + switch link.Rel { + case nsWebsocket: + fmt.Println("Websocket:", link.Href) + if test { + checkWebSocket(server, link.Href, tlsConfig, timeout) + } + case nsBOSH: + fmt.Println("BOSH:", link.Href) + if test { + fmt.Println("Testing BOSH is currently not supported.") + } + } + } +} + +func checkWebSocket(server string, target string, tlsConfig *tls.Config, timeout time.Duration) { + type wsOpenXML struct { + XMLName xml.Name `xml:"open"` + Text string `xml:",chardata"` + Xmlns string `xml:"xmlns,attr"` + ID string `xml:"id,attr"` + Version string `xml:"version,attr"` + Lang string `xml:"lang,attr"` + From string `xml:"from,attr"` + } + var wsOpen wsOpenXML + wsURI, err := url.ParseRequestURI(target) + if err != nil { + fmt.Println("Test:", statusNOK) + fmt.Println(err) + return + } + origURI, err := url.ParseRequestURI("https://" + server) + if err != nil { + fmt.Println("Test:", statusNOK) + fmt.Println(err) + return + } + wsTLSConfig := &tls.Config{ + MinVersion: tlsConfig.MinVersion, + ServerName: server, + InsecureSkipVerify: false, + } + wsConf := &websocket.Config{ + Location: wsURI, + Origin: origURI, + Version: 13, + TlsConfig: wsTLSConfig, + } + wsConf.Protocol = append(wsConf.Protocol, "xmpp") + ctx, cancel := context.WithTimeout(context.Background(), timeout) + defer cancel() + wsConn, err := wsConf.DialContext(ctx) + if err != nil { + fmt.Println("Test:", statusNOK) + fmt.Println(err) + return + } + _, err = wsConn.Write([]byte(fmt.Sprintf( + `<open xmlns="urn:ietf:params:xml:ns:xmpp-framing" to="%s" version="1.0" />`, server))) + if err != nil { + fmt.Println("Test:", statusNOK) + fmt.Println(err) + return + } + buf := make([]byte, 4096) + _, err = wsConn.Read(buf) + if err != nil { + fmt.Println("Test:", statusNOK) + fmt.Println(err) + return + } + err = xml.Unmarshal(buf, &wsOpen) + if err != nil { + fmt.Println("Test:", statusNOK) + fmt.Println(err) + return + } + if wsOpen.From != server { + _ = wsConn.Close() + fmt.Println("Test:", statusNOK) + fmt.Printf("Server identifies as %s instead of %s.\n", + wsOpen.From, server) + return + } else { + if !wsConn.IsClientConn() { + _ = wsConn.Close() + fmt.Println("Test:", statusNOK) + fmt.Println("WS connection is no client connection.") + return + } + } + fmt.Println("Test:", statusOK) + err = wsConn.Close() + if err != nil { + fmt.Println(err) + } +} + func checkCertExpiry(c *tls.Conn) { expiry := c.ConnectionState().PeerCertificates[0].NotAfter start := c.ConnectionState().PeerCertificates[0].NotBefore now := time.Now() if now.Before(expiry) && now.After(start) { - fmt.Println("Certificate:", statusOK) + fmt.Println("Test:", statusOK) } else { - fmt.Println("Certificate:", statusNOK) + fmt.Println("Test:", statusNOK) fmt.Println("Valid from", start, "to", expiry) } } diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/xmpp-dns-0.3.11/man/xmpp-dns.1 new/xmpp-dns-0.4.0/man/xmpp-dns.1 --- old/xmpp-dns-0.3.11/man/xmpp-dns.1 2024-04-07 13:22:46.000000000 +0200 +++ new/xmpp-dns-0.4.0/man/xmpp-dns.1 2024-08-18 12:32:15.000000000 +0200 @@ -1,6 +1,6 @@ .\" generated with Ronn-NG/v0.9.1 .\" http://github.com/apjanke/ronn-ng/tree/0.9.1 -.TH "XMPP\-DNS" "1" "May 2023" "" +.TH "XMPP\-DNS" "1" "April 2024" "" .SH "NAME" \fBxmpp\-dns\fR \- A CLI tool to check XMPP SRV records\. .SH "SYNOPSIS" @@ -57,6 +57,10 @@ Just place the following in your \fB~/\.zshrc\fR or \fB~/\.zshrc\.local\fR: .P \fBcompdef _gnu_generic xmpp\-dns\fR +.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 .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 -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/xmpp-dns-0.3.11/man/xmpp-dns.1.html new/xmpp-dns-0.4.0/man/xmpp-dns.1.html --- old/xmpp-dns-0.3.11/man/xmpp-dns.1.html 2024-04-07 13:22:46.000000000 +0200 +++ new/xmpp-dns-0.4.0/man/xmpp-dns.1.html 2024-08-18 12:32:15.000000000 +0200 @@ -141,6 +141,14 @@ compdef _gnu_generic xmpp-dns </code></p> +<h3 id="FISH">FISH</h3> + +<p>There are no shell completions yet, but FISH can generate them from the man page with following command:</p> + +<p><code> +fish_update_completions +</code></p> + <h2 id="CHAT">CHAT</h2> <p>There is no dedicated chat for <code>xmpp-dns</code>, but feel free to join @@ -161,7 +169,7 @@ <ol class='man-decor man-foot man foot'> <li class='tl'></li> - <li class='tc'>May 2023</li> + <li class='tc'>April 2024</li> <li class='tr'>xmpp-dns(1)</li> </ol> diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/xmpp-dns-0.3.11/man/xmpp-dns.1.ronn new/xmpp-dns-0.4.0/man/xmpp-dns.1.ronn --- old/xmpp-dns-0.3.11/man/xmpp-dns.1.ronn 2024-04-07 13:22:46.000000000 +0200 +++ new/xmpp-dns-0.4.0/man/xmpp-dns.1.ronn 2024-08-18 12:32:15.000000000 +0200 @@ -69,6 +69,14 @@ compdef _gnu_generic xmpp-dns ``` +### FISH + +There are no shell completions yet, but FISH can generate them from the man page with following command: + +``` +fish_update_completions +``` + ## CHAT There is no dedicated chat for `xmpp-dns`, but feel free to join