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 <mvet...@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.2
+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

Reply via email to