尝试在UDP上进行TLS:关于大消息的wsarecv错误

huangapple go评论77阅读模式
英文:

Attempt to do TLS over UDP: wsarecv error about large message

问题

我想基于UDP建立TLS连接,就像这样:

package main

import (
	"crypto/tls"
	"fmt"
	"io"
	"net"
	"time"
)

func handleConn(conn net.Conn) {
	defer conn.Close()

	var buf []byte = make([]byte, 2000)
	for {
		if n, err := conn.Read(buf); err != nil && err != io.EOF {
			panic(err)
		} else {
			conn.Write(buf[:n])
		}
	}
}

func server() {
	cert, err := tls.LoadX509KeyPair("./serve.cert.pem", "./serve.key.pem")
	if err != nil {
		panic(err)
	}
	config := &tls.Config{Certificates: []tls.Certificate{cert}}

	conn, err := net.DialUDP("udp", &net.UDPAddr{Port: 19986}, &net.UDPAddr{Port: 19987})
	if err != nil {
		panic(err)
	}
	tconn := tls.Server(conn, config)
	defer tconn.Close()

	if err = tconn.Handshake(); err != nil {
		panic(err)
	}
	handleConn(tconn)
}

func main() {

	go server()

	time.Sleep(time.Second)
	client()
}

func client() {
	conf := &tls.Config{
		InsecureSkipVerify: true,
	}

	conn, err := net.DialUDP("udp", &net.UDPAddr{Port: 19987}, &net.UDPAddr{Port: 19986})
	if err != nil {
		panic(err)
	}
	tconn := tls.Client(conn, conf)
	defer tconn.Close()

	if err = tconn.Handshake(); err != nil {
		panic(err)
	}

	_, err = conn.Write([]byte("hello"))
	if err != nil {
		panic(err)
	}
	buf := make([]byte, 100)
	n, err := conn.Read(buf)
	if err != nil {
		panic(err)
	}
	fmt.Println(string(buf[:n]))
}

错误信息:

panic: read udp 127.0.0.1:19987->127.0.0.1:19986: wsarecv: A message sent on a datagram socket was larger than the internal message buffer or some other network limit, or the buffer used to receive a datagram into was smaller than the datagram itself.

goroutine 1 [running]:
main.client()
        D:/OneDrive/code/go/ctest/main.go:65 +0x2d1
main.main()
        D:/OneDrive/code/go/ctest/main.go:49 +0x34
exit status 2

附录:
serve.cert.pem

-----BEGIN CERTIFICATE-----
MIIBbjCCARSgAwIBAgIRAI+jBYEYS5aBXDUedBt7PKYwCgYIKoZIzj0EAwIwEjEQ
MA4GA1UEChMHQWNtZSBDbzAeFw0yMjAxMDkxNzQxMjBaFw0yMzAxMDkxNzQxMjBa
MBIxEDAOBgNVBAoTB0FjbWUgQ28wWTATBgcqhkjOPQIBBggqhkjOPQMBBwNCAAQy
l1/gWhTxZ3rS/XJOMLHhmkQp64EtPrEgq9SjKDpWBZQC+kNZdM5xzJrv3bLqcyOS
JywZfEpTZzW7sxko4maBo0swSTAOBgNVHQ8BAf8EBAMCB4AwEwYDVR0lBAwwCgYI
KwYBBQUHAwEwDAYDVR0TAQH/BAIwADAUBgNVHREEDTALgglsb2NhbGhvc3QwCgYI
KoZIzj0EAwIDSAAwRQIhAICxMC8o603GwL3bf42EXrtPP5/LtEIc/hjdJpilqc3b
AiBTEdrE+/oCgUjsxV2RFj1+42CTGtcav4sJyCPjme0N/w==
-----END CERTIFICATE-----

serve.key.pem

-----BEGIN PRIVATE KEY-----
MIGHAgEAMBMGByqGSM49AgEGCCqGSM49AwEHBG0wawIBAQQgmH+BleetLN1fK0dy
JpedWG8C2yxtb7gEEAwvdwXf6FihRANCAAQyl1/gWhTxZ3rS/XJOMLHhmkQp64Et
PrEgq9SjKDpWBZQC+kNZdM5xzJrv3bLqcyOSJywZfEpTZzW7sxko4maB
-----END PRIVATE KEY-----

我尝试设置UDP连接的缓冲区,但没有效果!证书只有558字节,但UDP的容量为65536字节。然后,我调试发现:在crypt/tls/conn.go>readRecordOrCCS c.readFromUntil(c.conn, recordHeaderLen)中,recordHeaderLen是常量5,它添加了bytes.MinRead;因此,读取器缓冲区的长度为517。实际上,数据大小约为700字节。
那么,我该怎么办?

英文:

I want get tls conn base on udp, just like:

package main

import (
	"crypto/tls"
	"fmt"
	"io"
	"net"
	"time"
)

func handleConn(conn net.Conn) {
	defer conn.Close()

	var buf []byte = make([]byte, 2000)
	for {
		if n, err := conn.Read(buf); err != nil && err != io.EOF {
			panic(err)
		} else {
			conn.Write(buf[:n])
		}
	}
}

func server() {
	cert, err := tls.LoadX509KeyPair("./serve.cert.pem", "./serve.key.pem")
	if err != nil {
		panic(err)
	}
	config := &tls.Config{Certificates: []tls.Certificate{cert}}

	conn, err := net.DialUDP("udp", &net.UDPAddr{Port: 19986}, &net.UDPAddr{Port: 19987})
	if err != nil {
		panic(err)
	}
	tconn := tls.Server(conn, config)
	defer tconn.Close()

	if err = tconn.Handshake(); err != nil {
		panic(err)
	}
	handleConn(tconn)
}

func main() {

	go server()

	time.Sleep(time.Second)
	client()
}

func client() {
	conf := &tls.Config{
		InsecureSkipVerify: true,
	}

	conn, err := net.DialUDP("udp", &net.UDPAddr{Port: 19987}, &net.UDPAddr{Port: 19986})
	if err != nil {
		panic(err)
	}
	tconn := tls.Client(conn, conf)
	defer tconn.Close()

	if err = tconn.Handshake(); err != nil {
		panic(err)
	}

	_, err = conn.Write([]byte("hello"))
	if err != nil {
		panic(err)
	}
	buf := make([]byte, 100)
	n, err := conn.Read(buf)
	if err != nil {
		panic(err)
	}
	fmt.Println(string(buf[:n]))
}

panic:

panic: read udp 127.0.0.1:19987->127.0.0.1:19986: wsarecv: A message sent on a datagram socket was larger than the internal message buffer or some other network limit, or the buffer used to receive a datagram into was smaller than the datagram itself.

goroutine 1 [running]:
main.client()
        D:/OneDrive/code/go/ctest/main.go:65 +0x2d1
main.main()
        D:/OneDrive/code/go/ctest/main.go:49 +0x34
exit status 2

Appendix:
serve.cert.pem

-----BEGIN CERTIFICATE-----
MIIBbjCCARSgAwIBAgIRAI+jBYEYS5aBXDUedBt7PKYwCgYIKoZIzj0EAwIwEjEQ
MA4GA1UEChMHQWNtZSBDbzAeFw0yMjAxMDkxNzQxMjBaFw0yMzAxMDkxNzQxMjBa
MBIxEDAOBgNVBAoTB0FjbWUgQ28wWTATBgcqhkjOPQIBBggqhkjOPQMBBwNCAAQy
l1/gWhTxZ3rS/XJOMLHhmkQp64EtPrEgq9SjKDpWBZQC+kNZdM5xzJrv3bLqcyOS
JywZfEpTZzW7sxko4maBo0swSTAOBgNVHQ8BAf8EBAMCB4AwEwYDVR0lBAwwCgYI
KwYBBQUHAwEwDAYDVR0TAQH/BAIwADAUBgNVHREEDTALgglsb2NhbGhvc3QwCgYI
KoZIzj0EAwIDSAAwRQIhAICxMC8o603GwL3bf42EXrtPP5/LtEIc/hjdJpilqc3b
AiBTEdrE+/oCgUjsxV2RFj1+42CTGtcav4sJyCPjme0N/w==
-----END CERTIFICATE-----

serve.key.pem

-----BEGIN PRIVATE KEY-----
MIGHAgEAMBMGByqGSM49AgEGCCqGSM49AwEHBG0wawIBAQQgmH+BleetLN1fK0dy
JpedWG8C2yxtb7gEEAwvdwXf6FihRANCAAQyl1/gWhTxZ3rS/XJOMLHhmkQp64Et
PrEgq9SjKDpWBZQC+kNZdM5xzJrv3bLqcyOSJywZfEpTZzW7sxko4maB
-----END PRIVATE KEY-----

I try set udp conn's buff, useless!
The certificate only 558 Bytes, but the UDP has a capacity of 65536 Bytes. then, I debug found: in crypt/tls/conn.go>readRecordOrCCS c.readFromUntil(c.conn, recordHeaderLen), the recordHeaderLen is constant 5, it add bytes.MinRead; so the reader buff's len is 517. Actually data size about 700 Bytes.
So? What should I do?

答案1

得分: 2

你不能合理地期望让TLS(和SSL)在另一个协议的应用层上运行。简单来说,这意味着TLS对传输其字节的特定协议是无感知的,但作为应用层的客户端,它对底层堆栈有两个要求:传输任意的不透明数据流,确保稳健性和有序传递。

在TCP/IP世界中,这意味着使用TCP,因为UDP既不提供TLS所期望的属性之一。

然而,一些解决方案成功地在UDP上使用了TLS,OpenVPN就是一个典型的例子。它在握手和初始密钥交换时使用TLS,同时将UDP作为默认和推荐的传输协议。但是,所有这些解决方案都在UDP上实现了一个层,为其提供了通常在TCP中找到的属性:这些层处理了UDP帧的丢失、重排序、重复和损坏,并向上层“导出”了一个适用于应用层的协议。

这基本上意味着,如果你想在UDP上使用TLS,你首先需要实现一个自定义类型,该类型具有net.Conn接口,它将使用UDP关联来传输字节,并向其客户端“导出”一个具有稳健性和有序传递属性的连接,就像net.TCPConn一样。

英文:

You cannot sensibly expect to make the thing work: TLS (and SSL) is designed to be carried out using the application layer of another protocol.
What this means in simpler terms is that while TLS is oblivious to what particular protocol transports its bytes, as a client of the application layer, it expects two properties from the underlying stack: it transports arbitrary opaque streams of data, ensuring robustness and in-order delivery.
In the TCP/IP world, this means TCP because UDP does not provide neither of the properties TLS expects.

Still, some solutions do successfully employ TLS over UDP, with OpenVPN being a prime example of this—it uses TLS for handshake and initial key exchange while having UDP as its default, and recommended, transport protocol,—but all of them have a layer implemented over UDP which "arms" it with the properties typically found in TCP: such layers deal with dropped, reordered, duplicated and corrupted UDP frames, "exporting" to the upper layer a protocol which is OK to use for application layer.

This basically means than if you're after using TLS over UDP, you first need to implemet a custom type having the net.Conn interface which would use a UDP association to shovel the bytes back and forth and "export" to its client a connection which has the properties of robustness and in-order delivery—just as net.TCPConn does.

答案2

得分: -1

我明白了!只需要将UDP连接转换为流式IO。

package main

import (
	"bufio"
	"crypto/tls"
	"fmt"
	"io"
	"net"
	"time"
)

func handleConn(conn net.Conn) {
	defer conn.Close()

	var buf []byte = make([]byte, 2000)
	for {
		if n, err := conn.Read(buf); err != nil && err != io.EOF {
			panic(err)
		} else {
			fmt.Println(string(buf[:n]))
			conn.Write(append([]byte("got: "), buf[:n]...))
		}
	}
}

func server() {
	cert, err := tls.LoadX509KeyPair("./serve.cert.pem", "./serve.key.pem")
	if err != nil {
		panic(err)
	}
	config := &tls.Config{Certificates: []tls.Certificate{cert}}

	conn, err := net.DialUDP("udp", &net.UDPAddr{Port: 19986}, &net.UDPAddr{Port: 19987})
	if err != nil {
		panic(err)
	}
	sconn := NewSconn(conn)
	tconn := tls.Server(sconn, config)
	defer tconn.Close()

	handleConn(tconn)
}

func main() {

	go server()

	time.Sleep(time.Second)
	client()
}

func client() {
	conf := &tls.Config{
		InsecureSkipVerify: true,
		CipherSuites: []uint16{
			tls.TLS_AES_128_GCM_SHA256,
		},
	}

	conn, err := net.DialUDP("udp", &net.UDPAddr{Port: 19987}, &net.UDPAddr{Port: 19986})
	if err != nil {
		panic(err)
	}
	sconn := NewSconn(conn)
	tconn := tls.Client(sconn, conf)
	defer tconn.Close()

	_, err = tconn.Write([]byte("hello"))
	if err != nil {
		panic(err)
	}

	buf := make([]byte, 100)
	n, err := tconn.Read(buf)
	if err != nil {
		panic(err)
	}
	fmt.Println(string(buf[:n]))
}

//
//
//
//
// --------------------------------------------------
type sconn struct {
	conn net.Conn

	buf  *bytes.Buffer
	rLen int
	lock sync.Mutex
}

func NewSconn(conn net.Conn) *sconn {
	return &sconn{
		conn: conn,

		buf:  bytes.NewBuffer(nil),
		rLen: 2000,
		lock: sync.Mutex{},
	}
}

func (s *sconn) Read(b []byte) (n int, err error) {
	s.lock.Lock()
	defer s.lock.Unlock()

	if s.buf.Len() != 0 {
		n, err = s.buf.Read(b)
	} else {
		s.push(&s.rLen)
		n, err = s.buf.Read(b)

	}
	return n, err
}

// push push UDP package to buff
func (s *sconn) push(rLen *int) (err error) {
	defer func() {
		if e := recover(); e != nil {
			if *rLen < 65536 {
				*rLen = *rLen + 2000
				s.push(rLen)
			} else {
				err = errors.New(fmt.Sprintln(e))
			}
		}
	}()
	var tmp []byte = make([]byte, *rLen)
	if n, err := s.conn.Read(tmp); err != nil {
		return err
	} else {
		_, err := s.buf.Write(tmp[:n])
		return err
	}
}

func (s *sconn) Write(b []byte) (n int, err error) {

	for i := 0; i < len(b); i = i + 512 {
		j := i + 512
		if j > len(b) {
			j = len(b)
		}
		if n, err = s.conn.Write(b[i:j]); err != nil {
			return n, err
		}
	}
	return len(b), nil
}

func (s *sconn) Close() error                       { return nil } // only close TLS conn, don't close UDP conn
func (s *sconn) LocalAddr() net.Addr                { return s.conn.LocalAddr() }
func (s *sconn) RemoteAddr() net.Addr               { return s.conn.RemoteAddr() }
func (s *sconn) SetDeadline(t time.Time) error      { return s.conn.SetDeadline(t) }
func (s *sconn) SetReadDeadline(t time.Time) error  { return s.conn.SetReadDeadline(t) }
func (s *sconn) SetWriteDeadline(t time.Time) error { return s.conn.SetWriteDeadline(t) }

希望对你有帮助!

英文:

I GOT IT!

just make udp conn become to stream io.

package main

import (
	&quot;bufio&quot;
	&quot;crypto/tls&quot;
	&quot;fmt&quot;
	&quot;io&quot;
	&quot;net&quot;
	&quot;time&quot;
)

func handleConn(conn net.Conn) {
	defer conn.Close()

	var buf []byte = make([]byte, 2000)
	for {
		if n, err := conn.Read(buf); err != nil &amp;&amp; err != io.EOF {
			panic(err)
		} else {
			fmt.Println(string(buf[:n]))
			conn.Write(append([]byte(&quot;got: &quot;), buf[:n]...))
		}
	}
}

func server() {
	cert, err := tls.LoadX509KeyPair(&quot;./serve.cert.pem&quot;, &quot;./serve.key.pem&quot;)
	if err != nil {
		panic(err)
	}
	config := &amp;tls.Config{Certificates: []tls.Certificate{cert}}

	conn, err := net.DialUDP(&quot;udp&quot;, &amp;net.UDPAddr{Port: 19986}, &amp;net.UDPAddr{Port: 19987})
	if err != nil {
		panic(err)
	}
	sconn := NewSconn(conn)
	tconn := tls.Server(sconn, config)
	defer tconn.Close()

	handleConn(tconn)
}

func main() {

	go server()

	time.Sleep(time.Second)
	client()
}

func client() {
	conf := &amp;tls.Config{
		InsecureSkipVerify: true,
		CipherSuites: []uint16{
			tls.TLS_AES_128_GCM_SHA256,
		},
	}

	conn, err := net.DialUDP(&quot;udp&quot;, &amp;net.UDPAddr{Port: 19987}, &amp;net.UDPAddr{Port: 19986})
	if err != nil {
		panic(err)
	}
	sconn := NewSconn(conn)
	tconn := tls.Client(sconn, conf)
	defer tconn.Close()

	_, err = tconn.Write([]byte(&quot;hello&quot;))
	if err != nil {
		panic(err)
	}

	buf := make([]byte, 100)
	n, err := tconn.Read(buf)
	if err != nil {
		panic(err)
	}
	fmt.Println(string(buf[:n]))
}

//
//
//
//
// --------------------------------------------------
type sconn struct {
	conn net.Conn

	buf  *bytes.Buffer
	rLen int
	lock sync.Mutex
}

func NewSconn(conn net.Conn) *sconn {
	return &amp;sconn{
		conn: conn,

		buf:  bytes.NewBuffer(nil),
		rLen: 2000,
		lock: sync.Mutex{},
	}
}

func (s *sconn) Read(b []byte) (n int, err error) {
	s.lock.Lock()
	defer s.lock.Unlock()

	if s.buf.Len() != 0 {
		n, err = s.buf.Read(b)
	} else {
		s.push(&amp;s.rLen)
		n, err = s.buf.Read(b)

	}
	return n, err
}

// push push UDP package to buff
func (s *sconn) push(rLen *int) (err error) {
	defer func() {
		if e := recover(); e != nil {
			if *rLen &lt; 65536 {
				*rLen = *rLen + 2000
				s.push(rLen)
			} else {
				err = errors.New(fmt.Sprintln(e))
			}
		}
	}()
	var tmp []byte = make([]byte, *rLen)
	if n, err := s.conn.Read(tmp); err != nil {
		return err
	} else {
		_, err := s.buf.Write(tmp[:n])
		return err
	}
}

func (s *sconn) Write(b []byte) (n int, err error) {

	for i := 0; i &lt; len(b); i = i + 512 {
		j := i + 512
		if j &gt; len(b) {
			j = len(b)
		}
		if n, err = s.conn.Write(b[i:j]); err != nil {
			return n, err
		}
	}
	return len(b), nil
}

func (s *sconn) Close() error                       { return nil } // only close TLS conn, don&#39;t close UDP conn
func (s *sconn) LocalAddr() net.Addr                { return s.conn.LocalAddr() }
func (s *sconn) RemoteAddr() net.Addr               { return s.conn.RemoteAddr() }
func (s *sconn) SetDeadline(t time.Time) error      { return s.conn.SetDeadline(t) }
func (s *sconn) SetReadDeadline(t time.Time) error  { return s.conn.SetReadDeadline(t) }
func (s *sconn) SetWriteDeadline(t time.Time) error { return s.conn.SetWriteDeadline(t) }

huangapple
  • 本文由 发表于 2022年1月16日 22:49:21
  • 转载请务必保留本文链接:https://go.coder-hub.com/70731161.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定