// Copyright (c) 2016-present Cloud // // This program is free software; you can redistribute it and/or // modify it under the terms of version 3 of the GNU General Public // License as published by the Free Software Foundation. // // This program is distributed in the hope that it will be useful, but // WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU // General Public License for more details. // // You should have received a copy of the GNU General Public License // along with this program. If not, see . package brook import ( "context" "crypto/tls" "net" "time" "github.com/quic-go/quic-go" ) func QUICDialUDP(src, dst, addr string, tc *tls.Config, idleTime int) (net.Conn, error) { var rc *net.UDPConn var err error if src == "" || dst == "" { rc, err = ListenUDP("udp", nil) } if src != "" && dst != "" { rc, err = NATListenUDP("udp", src, dst) } if err != nil { return nil, err } raddr, err := Resolve("udp", addr) if err != nil { rc.Close() return nil, err } rc1, err := quic.Dial(context.Background(), rc, raddr, tc, &quic.Config{MaxIdleTimeout: time.Duration(idleTime) * time.Second, EnableDatagrams: true}) if err != nil { rc.Close() return nil, err } return &QUICConn{ UDPConn: rc, Conn: rc1, LAddr: rc1.LocalAddr(), RAddr: rc1.RemoteAddr(), }, nil } func QUICDialTCP(src, dst, addr string, tc *tls.Config, idleTime int) (net.Conn, error) { var rc *net.UDPConn var err error if src == "" || dst == "" { rc, err = ListenUDP("udp", nil) } if src != "" && dst != "" { rc, err = NATListenUDP("udp", src, dst) } if err != nil { return nil, err } raddr, err := Resolve("udp", addr) if err != nil { rc.Close() return nil, err } rc1, err := quic.Dial(context.Background(), rc, raddr, tc, &quic.Config{MaxIdleTimeout: time.Duration(idleTime) * time.Second}) if err != nil { rc.Close() return nil, err } s, err := rc1.OpenStreamSync(context.Background()) if err != nil { rc1.CloseWithError(0, err.Error()) rc.Close() return nil, err } return &QUICConn{ UDPConn: rc, Conn: rc1, Stream: s, LAddr: &net.TCPAddr{ IP: rc1.LocalAddr().(*net.UDPAddr).IP, Port: rc1.LocalAddr().(*net.UDPAddr).Port, Zone: rc1.LocalAddr().(*net.UDPAddr).Zone, }, RAddr: &net.TCPAddr{ IP: rc1.RemoteAddr().(*net.UDPAddr).IP, Port: rc1.RemoteAddr().(*net.UDPAddr).Port, Zone: rc1.RemoteAddr().(*net.UDPAddr).Zone, }, }, nil } type QUICConn struct { UDPConn *net.UDPConn Conn quic.Connection Stream quic.Stream LAddr net.Addr RAddr net.Addr } func (c *QUICConn) Read(b []byte) (int, error) { if c.Stream != nil { return c.Stream.Read(b) } b1, err := c.Conn.ReceiveDatagram(context.Background()) if err != nil { return 0, err } i := copy(b, b1) return i, nil } func (c *QUICConn) Write(b []byte) (int, error) { if c.Stream != nil { return c.Stream.Write(b) } if err := c.Conn.SendDatagram(b); err != nil { return 0, err } return len(b), nil } func (c *QUICConn) Close() error { if c.Stream != nil { c.Stream.CancelRead(0) c.Stream.Close() } if c.Conn != nil { c.Conn.CloseWithError(0, "close") } if c.UDPConn != nil { c.UDPConn.Close() } return nil } func (c *QUICConn) LocalAddr() net.Addr { return c.LAddr } func (c *QUICConn) RemoteAddr() net.Addr { return c.RAddr } func (c *QUICConn) SetDeadline(t time.Time) error { if c.Stream != nil { return c.Stream.SetDeadline(t) } // prefer quic.Config.MaxIdleTimeout return nil } func (c *QUICConn) SetReadDeadline(t time.Time) error { if c.Stream != nil { return c.Stream.SetReadDeadline(t) } return nil } func (c *QUICConn) SetWriteDeadline(t time.Time) error { if c.Stream != nil { return c.Stream.SetWriteDeadline(t) } return nil }