mirror of
https://github.com/cwinfo/yggdrasil-go.git
synced 2025-03-28 20:13:49 +00:00
107 lines
2.1 KiB
Go
107 lines
2.1 KiB
Go
package core
|
|
|
|
import (
|
|
"context"
|
|
"crypto/tls"
|
|
"fmt"
|
|
"net"
|
|
"net/url"
|
|
"time"
|
|
|
|
"github.com/Arceliar/phony"
|
|
"github.com/quic-go/quic-go"
|
|
)
|
|
|
|
type linkQUIC struct {
|
|
phony.Inbox
|
|
*links
|
|
tlsconfig *tls.Config
|
|
quicconfig *quic.Config
|
|
}
|
|
|
|
type linkQUICStream struct {
|
|
quic.Connection
|
|
quic.Stream
|
|
}
|
|
|
|
type linkQUICListener struct {
|
|
*quic.Listener
|
|
ch <-chan *linkQUICStream
|
|
}
|
|
|
|
func (l *linkQUICListener) Accept() (net.Conn, error) {
|
|
qs := <-l.ch
|
|
if qs == nil {
|
|
return nil, context.Canceled
|
|
}
|
|
return qs, nil
|
|
}
|
|
|
|
func (l *links) newLinkQUIC() *linkQUIC {
|
|
lt := &linkQUIC{
|
|
links: l,
|
|
tlsconfig: l.core.config.tls.Clone(),
|
|
quicconfig: &quic.Config{
|
|
MaxIdleTimeout: time.Minute,
|
|
KeepAlivePeriod: time.Second * 20,
|
|
TokenStore: quic.NewLRUTokenStore(255, 255),
|
|
},
|
|
}
|
|
return lt
|
|
}
|
|
|
|
func (l *linkQUIC) dial(ctx context.Context, url *url.URL, info linkInfo, options linkOptions) (net.Conn, error) {
|
|
tlsconfig := l.tlsconfig.Clone()
|
|
return l.links.findSuitableIP(url, func(hostname string, ip net.IP, port int) (net.Conn, error) {
|
|
tlsconfig.ServerName = hostname
|
|
hostport := net.JoinHostPort(ip.String(), fmt.Sprintf("%d", port))
|
|
qc, err := quic.DialAddr(ctx, hostport, l.tlsconfig, l.quicconfig)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
qs, err := qc.OpenStreamSync(ctx)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
return &linkQUICStream{
|
|
Connection: qc,
|
|
Stream: qs,
|
|
}, nil
|
|
})
|
|
}
|
|
|
|
func (l *linkQUIC) listen(ctx context.Context, url *url.URL, _ string) (net.Listener, error) {
|
|
ql, err := quic.ListenAddr(url.Host, l.tlsconfig, l.quicconfig)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
ch := make(chan *linkQUICStream)
|
|
lql := &linkQUICListener{
|
|
Listener: ql,
|
|
ch: ch,
|
|
}
|
|
go func() {
|
|
for {
|
|
qc, err := ql.Accept(ctx)
|
|
switch err {
|
|
case context.Canceled, context.DeadlineExceeded:
|
|
ql.Close()
|
|
fallthrough
|
|
case quic.ErrServerClosed:
|
|
return
|
|
case nil:
|
|
qs, err := qc.AcceptStream(ctx)
|
|
if err != nil {
|
|
_ = qc.CloseWithError(1, fmt.Sprintf("stream error: %s", err))
|
|
continue
|
|
}
|
|
ch <- &linkQUICStream{
|
|
Connection: qc,
|
|
Stream: qs,
|
|
}
|
|
}
|
|
}
|
|
}()
|
|
return lql, nil
|
|
}
|