Proxy: Add Hysteria 2 inbound & transport (supports listening port range, Salamander finalmask) (#5679)

https://github.com/XTLS/Xray-core/pull/5679#issuecomment-3888548778

Closes https://github.com/XTLS/Xray-core/issues/5605
This commit is contained in:
LjhAUMEM
2026-02-12 22:56:06 +08:00
committed by GitHub
parent 7abad3fac0
commit 6a909b2507
20 changed files with 1341 additions and 85 deletions

View File

@@ -0,0 +1,129 @@
package account
import (
"sync"
"github.com/xtls/xray-core/common/errors"
"github.com/xtls/xray-core/common/protocol"
"google.golang.org/protobuf/proto"
)
func (a *Account) AsAccount() (protocol.Account, error) {
return &MemoryAccount{
Auth: a.Auth,
}, nil
}
type MemoryAccount struct {
Auth string
}
func (a *MemoryAccount) Equals(another protocol.Account) bool {
if account, ok := another.(*MemoryAccount); ok {
return a.Auth == account.Auth
}
return false
}
func (a *MemoryAccount) ToProto() proto.Message {
return &Account{
Auth: a.Auth,
}
}
type Validator struct {
emails map[string]struct{}
users map[string]*protocol.MemoryUser
mutex sync.Mutex
}
func NewValidator() *Validator {
return &Validator{
emails: make(map[string]struct{}),
users: make(map[string]*protocol.MemoryUser),
}
}
func (v *Validator) Add(u *protocol.MemoryUser) error {
v.mutex.Lock()
defer v.mutex.Unlock()
if u.Email != "" {
if _, ok := v.emails[u.Email]; ok {
return errors.New("User ", u.Email, " already exists.")
}
v.emails[u.Email] = struct{}{}
}
v.users[u.Account.(*MemoryAccount).Auth] = u
return nil
}
func (v *Validator) Del(email string) error {
if email == "" {
return errors.New("Email must not be empty.")
}
v.mutex.Lock()
defer v.mutex.Unlock()
if _, ok := v.emails[email]; !ok {
return errors.New("User ", email, " not found.")
}
delete(v.emails, email)
for key, user := range v.users {
if user.Email == email {
delete(v.users, key)
break
}
}
return nil
}
func (v *Validator) Get(auth string) *protocol.MemoryUser {
v.mutex.Lock()
defer v.mutex.Unlock()
return v.users[auth]
}
func (v *Validator) GetByEmail(email string) *protocol.MemoryUser {
if email == "" {
return nil
}
v.mutex.Lock()
defer v.mutex.Unlock()
if _, ok := v.emails[email]; ok {
for _, user := range v.users {
if user.Email == email {
return user
}
}
}
return nil
}
func (v *Validator) GetAll() []*protocol.MemoryUser {
v.mutex.Lock()
defer v.mutex.Unlock()
var users = make([]*protocol.MemoryUser, 0, len(v.users))
for _, user := range v.users {
users = append(users, user)
}
return users
}
func (v *Validator) GetCount() int64 {
v.mutex.Lock()
defer v.mutex.Unlock()
return int64(len(v.users))
}

View File

@@ -0,0 +1,123 @@
// Code generated by protoc-gen-go. DO NOT EDIT.
// versions:
// protoc-gen-go v1.36.11
// protoc v6.33.5
// source: proxy/hysteria/account/config.proto
package account
import (
protoreflect "google.golang.org/protobuf/reflect/protoreflect"
protoimpl "google.golang.org/protobuf/runtime/protoimpl"
reflect "reflect"
sync "sync"
unsafe "unsafe"
)
const (
// Verify that this generated code is sufficiently up-to-date.
_ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion)
// Verify that runtime/protoimpl is sufficiently up-to-date.
_ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20)
)
type Account struct {
state protoimpl.MessageState `protogen:"open.v1"`
Auth string `protobuf:"bytes,1,opt,name=auth,proto3" json:"auth,omitempty"`
unknownFields protoimpl.UnknownFields
sizeCache protoimpl.SizeCache
}
func (x *Account) Reset() {
*x = Account{}
mi := &file_proxy_hysteria_account_config_proto_msgTypes[0]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
func (x *Account) String() string {
return protoimpl.X.MessageStringOf(x)
}
func (*Account) ProtoMessage() {}
func (x *Account) ProtoReflect() protoreflect.Message {
mi := &file_proxy_hysteria_account_config_proto_msgTypes[0]
if x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil {
ms.StoreMessageInfo(mi)
}
return ms
}
return mi.MessageOf(x)
}
// Deprecated: Use Account.ProtoReflect.Descriptor instead.
func (*Account) Descriptor() ([]byte, []int) {
return file_proxy_hysteria_account_config_proto_rawDescGZIP(), []int{0}
}
func (x *Account) GetAuth() string {
if x != nil {
return x.Auth
}
return ""
}
var File_proxy_hysteria_account_config_proto protoreflect.FileDescriptor
const file_proxy_hysteria_account_config_proto_rawDesc = "" +
"\n" +
"#proxy/hysteria/account/config.proto\x12\x1bxray.proxy.hysteria.account\"\x1d\n" +
"\aAccount\x12\x12\n" +
"\x04auth\x18\x01 \x01(\tR\x04authBs\n" +
"\x1fcom.xray.proxy.hysteria.accountP\x01Z0github.com/xtls/xray-core/proxy/hysteria/account\xaa\x02\x1bXray.Proxy.Hysteria.Accountb\x06proto3"
var (
file_proxy_hysteria_account_config_proto_rawDescOnce sync.Once
file_proxy_hysteria_account_config_proto_rawDescData []byte
)
func file_proxy_hysteria_account_config_proto_rawDescGZIP() []byte {
file_proxy_hysteria_account_config_proto_rawDescOnce.Do(func() {
file_proxy_hysteria_account_config_proto_rawDescData = protoimpl.X.CompressGZIP(unsafe.Slice(unsafe.StringData(file_proxy_hysteria_account_config_proto_rawDesc), len(file_proxy_hysteria_account_config_proto_rawDesc)))
})
return file_proxy_hysteria_account_config_proto_rawDescData
}
var file_proxy_hysteria_account_config_proto_msgTypes = make([]protoimpl.MessageInfo, 1)
var file_proxy_hysteria_account_config_proto_goTypes = []any{
(*Account)(nil), // 0: xray.proxy.hysteria.account.Account
}
var file_proxy_hysteria_account_config_proto_depIdxs = []int32{
0, // [0:0] is the sub-list for method output_type
0, // [0:0] is the sub-list for method input_type
0, // [0:0] is the sub-list for extension type_name
0, // [0:0] is the sub-list for extension extendee
0, // [0:0] is the sub-list for field type_name
}
func init() { file_proxy_hysteria_account_config_proto_init() }
func file_proxy_hysteria_account_config_proto_init() {
if File_proxy_hysteria_account_config_proto != nil {
return
}
type x struct{}
out := protoimpl.TypeBuilder{
File: protoimpl.DescBuilder{
GoPackagePath: reflect.TypeOf(x{}).PkgPath(),
RawDescriptor: unsafe.Slice(unsafe.StringData(file_proxy_hysteria_account_config_proto_rawDesc), len(file_proxy_hysteria_account_config_proto_rawDesc)),
NumEnums: 0,
NumMessages: 1,
NumExtensions: 0,
NumServices: 0,
},
GoTypes: file_proxy_hysteria_account_config_proto_goTypes,
DependencyIndexes: file_proxy_hysteria_account_config_proto_depIdxs,
MessageInfos: file_proxy_hysteria_account_config_proto_msgTypes,
}.Build()
File_proxy_hysteria_account_config_proto = out.File
file_proxy_hysteria_account_config_proto_goTypes = nil
file_proxy_hysteria_account_config_proto_depIdxs = nil
}

View File

@@ -0,0 +1,11 @@
syntax = "proto3";
package xray.proxy.hysteria.account;
option csharp_namespace = "Xray.Proxy.Hysteria.Account";
option go_package = "github.com/xtls/xray-core/proxy/hysteria/account";
option java_package = "com.xray.proxy.hysteria.account";
option java_multiple_files = true;
message Account {
string auth = 1;
}

View File

@@ -135,6 +135,7 @@ func (c *Client) Process(ctx context.Context, link *transport.Link, dialer inter
if err := buf.Copy(link.Reader, writer, buf.UpdateActivity(timer)); err != nil {
return errors.New("failed to transport all UDP request").Base(err)
}
return nil
}
@@ -143,12 +144,14 @@ func (c *Client) Process(ctx context.Context, link *transport.Link, dialer inter
reader := &UDPReader{
Reader: conn,
buf: make([]byte, MaxUDPSize),
df: &Defragger{},
}
if err := buf.Copy(reader, link.Writer, buf.UpdateActivity(timer)); err != nil {
return errors.New("failed to transport all UDP response").Base(err)
}
return nil
}
@@ -178,7 +181,6 @@ type UDPWriter struct {
func (w *UDPWriter) sendMsg(msg *UDPMessage) error {
msgN := msg.Serialize(w.buf)
if msgN < 0 {
// Message larger than buffer, silent drop
return nil
}
_, err := w.Writer.Write(w.buf[:msgN])
@@ -192,10 +194,12 @@ func (w *UDPWriter) WriteMultiBuffer(mb buf.MultiBuffer) error {
if b == nil {
break
}
addr := w.addr
if b.UDP != nil {
addr = b.UDP.NetAddr()
}
msg := &UDPMessage{
SessionID: 0,
PacketID: 0,
@@ -204,47 +208,58 @@ func (w *UDPWriter) WriteMultiBuffer(mb buf.MultiBuffer) error {
Addr: addr,
Data: b.Bytes(),
}
if err := w.sendMsg(msg); err != nil {
var errTooLarge *quic.DatagramTooLargeError
if go_errors.As(err, &errTooLarge) {
msg.PacketID = uint16(rand.Intn(0xFFFF)) + 1
fMsgs := FragUDPMessage(msg, int(errTooLarge.MaxDatagramPayloadSize))
for _, fMsg := range fMsgs {
err := w.sendMsg(&fMsg)
if err != nil {
b.Release()
buf.ReleaseMulti(mb)
return err
}
err := w.sendMsg(msg)
var errTooLarge *quic.DatagramTooLargeError
if go_errors.As(err, &errTooLarge) {
msg.PacketID = uint16(rand.Intn(0xFFFF)) + 1
fMsgs := FragUDPMessage(msg, int(errTooLarge.MaxDatagramPayloadSize))
for _, fMsg := range fMsgs {
err := w.sendMsg(&fMsg)
if err != nil {
b.Release()
buf.ReleaseMulti(mb)
return err
}
} else {
b.Release()
buf.ReleaseMulti(mb)
return err
}
} else if err != nil {
b.Release()
buf.ReleaseMulti(mb)
return err
}
b.Release()
}
return nil
}
type UDPReader struct {
Reader io.Reader
df *Defragger
Reader io.Reader
buf []byte
df *Defragger
firstMsg *UDPMessage
firstDest *net.Destination
}
func (r *UDPReader) ReadMultiBuffer() (buf.MultiBuffer, error) {
if r.firstMsg != nil {
buffer := buf.New()
buffer.Write(r.firstMsg.Data)
buffer.UDP = r.firstDest
r.firstMsg = nil
return buf.MultiBuffer{buffer}, nil
}
for {
b := buf.New()
_, err := b.ReadFrom(r.Reader)
n, err := r.Reader.Read(r.buf)
if err != nil {
b.Release()
return nil, err
}
msg, err := ParseUDPMessage(b.Bytes())
msg, err := ParseUDPMessage(r.buf[:n])
if err != nil {
b.Release()
continue
}
@@ -253,7 +268,11 @@ func (r *UDPReader) ReadMultiBuffer() (buf.MultiBuffer, error) {
continue
}
dest, _ := net.ParseDestination("udp:" + dfMsg.Addr)
dest, err := net.ParseDestination("udp:" + dfMsg.Addr)
if err != nil {
errors.LogDebug(context.Background(), dfMsg.Addr, " ParseDestination err ", err)
continue
}
buffer := buf.New()
buffer.Write(dfMsg.Data)

View File

@@ -5,6 +5,6 @@ import (
)
var (
tcpRequestPadding = padding.Padding{Min: 64, Max: 512}
// tcpResponsePadding = padding.Padding{Min: 128, Max: 1024}
tcpRequestPadding = padding.Padding{Min: 64, Max: 512}
tcpResponsePadding = padding.Padding{Min: 128, Max: 1024}
)

View File

@@ -74,14 +74,60 @@ func (x *ClientConfig) GetServer() *protocol.ServerEndpoint {
return nil
}
type ServerConfig struct {
state protoimpl.MessageState `protogen:"open.v1"`
Users []*protocol.User `protobuf:"bytes,1,rep,name=users,proto3" json:"users,omitempty"`
unknownFields protoimpl.UnknownFields
sizeCache protoimpl.SizeCache
}
func (x *ServerConfig) Reset() {
*x = ServerConfig{}
mi := &file_proxy_hysteria_config_proto_msgTypes[1]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
func (x *ServerConfig) String() string {
return protoimpl.X.MessageStringOf(x)
}
func (*ServerConfig) ProtoMessage() {}
func (x *ServerConfig) ProtoReflect() protoreflect.Message {
mi := &file_proxy_hysteria_config_proto_msgTypes[1]
if x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil {
ms.StoreMessageInfo(mi)
}
return ms
}
return mi.MessageOf(x)
}
// Deprecated: Use ServerConfig.ProtoReflect.Descriptor instead.
func (*ServerConfig) Descriptor() ([]byte, []int) {
return file_proxy_hysteria_config_proto_rawDescGZIP(), []int{1}
}
func (x *ServerConfig) GetUsers() []*protocol.User {
if x != nil {
return x.Users
}
return nil
}
var File_proxy_hysteria_config_proto protoreflect.FileDescriptor
const file_proxy_hysteria_config_proto_rawDesc = "" +
"\n" +
"\x1bproxy/hysteria/config.proto\x12\x13xray.proxy.hysteria\x1a!common/protocol/server_spec.proto\"f\n" +
"\x1bproxy/hysteria/config.proto\x12\x13xray.proxy.hysteria\x1a!common/protocol/server_spec.proto\x1a\x1acommon/protocol/user.proto\"f\n" +
"\fClientConfig\x12\x18\n" +
"\aversion\x18\x01 \x01(\x05R\aversion\x12<\n" +
"\x06server\x18\x02 \x01(\v2$.xray.common.protocol.ServerEndpointR\x06serverB[\n" +
"\x06server\x18\x02 \x01(\v2$.xray.common.protocol.ServerEndpointR\x06server\"@\n" +
"\fServerConfig\x120\n" +
"\x05users\x18\x01 \x03(\v2\x1a.xray.common.protocol.UserR\x05usersB[\n" +
"\x17com.xray.proxy.hysteriaP\x01Z(github.com/xtls/xray-core/proxy/hysteria\xaa\x02\x13Xray.Proxy.Hysteriab\x06proto3"
var (
@@ -96,18 +142,21 @@ func file_proxy_hysteria_config_proto_rawDescGZIP() []byte {
return file_proxy_hysteria_config_proto_rawDescData
}
var file_proxy_hysteria_config_proto_msgTypes = make([]protoimpl.MessageInfo, 1)
var file_proxy_hysteria_config_proto_msgTypes = make([]protoimpl.MessageInfo, 2)
var file_proxy_hysteria_config_proto_goTypes = []any{
(*ClientConfig)(nil), // 0: xray.proxy.hysteria.ClientConfig
(*protocol.ServerEndpoint)(nil), // 1: xray.common.protocol.ServerEndpoint
(*ServerConfig)(nil), // 1: xray.proxy.hysteria.ServerConfig
(*protocol.ServerEndpoint)(nil), // 2: xray.common.protocol.ServerEndpoint
(*protocol.User)(nil), // 3: xray.common.protocol.User
}
var file_proxy_hysteria_config_proto_depIdxs = []int32{
1, // 0: xray.proxy.hysteria.ClientConfig.server:type_name -> xray.common.protocol.ServerEndpoint
1, // [1:1] is the sub-list for method output_type
1, // [1:1] is the sub-list for method input_type
1, // [1:1] is the sub-list for extension type_name
1, // [1:1] is the sub-list for extension extendee
0, // [0:1] is the sub-list for field type_name
2, // 0: xray.proxy.hysteria.ClientConfig.server:type_name -> xray.common.protocol.ServerEndpoint
3, // 1: xray.proxy.hysteria.ServerConfig.users:type_name -> xray.common.protocol.User
2, // [2:2] is the sub-list for method output_type
2, // [2:2] is the sub-list for method input_type
2, // [2:2] is the sub-list for extension type_name
2, // [2:2] is the sub-list for extension extendee
0, // [0:2] is the sub-list for field type_name
}
func init() { file_proxy_hysteria_config_proto_init() }
@@ -121,7 +170,7 @@ func file_proxy_hysteria_config_proto_init() {
GoPackagePath: reflect.TypeOf(x{}).PkgPath(),
RawDescriptor: unsafe.Slice(unsafe.StringData(file_proxy_hysteria_config_proto_rawDesc), len(file_proxy_hysteria_config_proto_rawDesc)),
NumEnums: 0,
NumMessages: 1,
NumMessages: 2,
NumExtensions: 0,
NumServices: 0,
},

View File

@@ -7,8 +7,13 @@ option java_package = "com.xray.proxy.hysteria";
option java_multiple_files = true;
import "common/protocol/server_spec.proto";
import "common/protocol/user.proto";
message ClientConfig {
int32 version = 1;
xray.common.protocol.ServerEndpoint server = 2;
}
message ServerConfig {
repeated xray.common.protocol.User users = 1;
}

View File

@@ -2,12 +2,15 @@ package ctx
import (
"context"
"github.com/xtls/xray-core/proxy/hysteria/account"
)
type key int
const (
requireDatagram key = iota
validator
)
func ContextWithRequireDatagram(ctx context.Context, udp bool) context.Context {
@@ -21,3 +24,12 @@ func RequireDatagramFromContext(ctx context.Context) bool {
_, ok := ctx.Value(requireDatagram).(struct{})
return ok
}
func ContextWithValidator(ctx context.Context, v *account.Validator) context.Context {
return context.WithValue(ctx, validator, v)
}
func ValidatorFromContext(ctx context.Context) *account.Validator {
v, _ := ctx.Value(validator).(*account.Validator)
return v
}

View File

@@ -11,8 +11,6 @@ import (
)
const (
FrameTypeTCPRequest = 0x401
// Max length values are for preventing DoS attacks
MaxAddressLength = 2048
@@ -28,22 +26,49 @@ const (
)
// TCPRequest format:
// 0x401 (QUIC varint)
// Address length (QUIC varint)
// Address (bytes)
// Padding length (QUIC varint)
// Padding (bytes)
func ReadTCPRequest(r io.Reader) (string, error) {
bReader := quicvarint.NewReader(r)
addrLen, err := quicvarint.Read(bReader)
if err != nil {
return "", err
}
if addrLen == 0 || addrLen > MaxAddressLength {
return "", errors.New("invalid address length")
}
addrBuf := make([]byte, addrLen)
_, err = io.ReadFull(r, addrBuf)
if err != nil {
return "", err
}
paddingLen, err := quicvarint.Read(bReader)
if err != nil {
return "", err
}
if paddingLen > MaxPaddingLength {
return "", errors.New("invalid padding length")
}
if paddingLen > 0 {
_, err = io.CopyN(io.Discard, r, int64(paddingLen))
if err != nil {
return "", err
}
}
return string(addrBuf), nil
}
func WriteTCPRequest(w io.Writer, addr string) error {
padding := tcpRequestPadding.String()
paddingLen := len(padding)
addrLen := len(addr)
sz := int(quicvarint.Len(FrameTypeTCPRequest)) +
int(quicvarint.Len(uint64(addrLen))) + addrLen +
sz := int(quicvarint.Len(uint64(addrLen))) + addrLen +
int(quicvarint.Len(uint64(paddingLen))) + paddingLen
buf := make([]byte, sz)
i := varintPut(buf, FrameTypeTCPRequest)
i += varintPut(buf[i:], uint64(addrLen))
i := varintPut(buf, uint64(addrLen))
i += copy(buf[i:], addr)
i += varintPut(buf[i:], uint64(paddingLen))
copy(buf[i:], padding)
@@ -96,6 +121,26 @@ func ReadTCPResponse(r io.Reader) (bool, string, error) {
return status[0] == 0, string(msgBuf), nil
}
func WriteTCPResponse(w io.Writer, ok bool, msg string) error {
padding := tcpResponsePadding.String()
paddingLen := len(padding)
msgLen := len(msg)
sz := 1 + int(quicvarint.Len(uint64(msgLen))) + msgLen +
int(quicvarint.Len(uint64(paddingLen))) + paddingLen
buf := make([]byte, sz)
if ok {
buf[0] = 0
} else {
buf[0] = 1
}
i := varintPut(buf[1:], uint64(msgLen))
i += copy(buf[1+i:], msg)
i += varintPut(buf[1+i:], uint64(paddingLen))
copy(buf[1+i:], padding)
_, err := w.Write(buf)
return err
}
// UDPMessage format:
// Session ID (uint32 BE)
// Packet ID (uint16 BE)

198
proxy/hysteria/server.go Normal file
View File

@@ -0,0 +1,198 @@
package hysteria
import (
"context"
"io"
"time"
"github.com/xtls/xray-core/common"
"github.com/xtls/xray-core/common/buf"
"github.com/xtls/xray-core/common/errors"
"github.com/xtls/xray-core/common/log"
"github.com/xtls/xray-core/common/net"
"github.com/xtls/xray-core/common/protocol"
"github.com/xtls/xray-core/common/session"
"github.com/xtls/xray-core/core"
"github.com/xtls/xray-core/features/policy"
"github.com/xtls/xray-core/features/routing"
"github.com/xtls/xray-core/proxy/hysteria/account"
"github.com/xtls/xray-core/transport"
"github.com/xtls/xray-core/transport/internet/hysteria"
"github.com/xtls/xray-core/transport/internet/stat"
)
type Server struct {
config *ServerConfig
validator *account.Validator
policyManager policy.Manager
}
func NewServer(ctx context.Context, config *ServerConfig) (*Server, error) {
validator := account.NewValidator()
for _, user := range config.Users {
u, err := user.ToMemoryUser()
if err != nil {
return nil, errors.New("failed to get hysteria user").Base(err).AtError()
}
if err := validator.Add(u); err != nil {
return nil, errors.New("failed to add user").Base(err).AtError()
}
}
v := core.MustFromContext(ctx)
s := &Server{
config: config,
validator: validator,
policyManager: v.GetFeature(policy.ManagerType()).(policy.Manager),
}
return s, nil
}
func (s *Server) HysteriaInboundValidator() *account.Validator {
return s.validator
}
func (s *Server) AddUser(ctx context.Context, u *protocol.MemoryUser) error {
return s.validator.Add(u)
}
func (s *Server) RemoveUser(ctx context.Context, e string) error {
return s.validator.Del(e)
}
func (s *Server) GetUser(ctx context.Context, email string) *protocol.MemoryUser {
return s.validator.GetByEmail(email)
}
func (s *Server) GetUsers(ctx context.Context) []*protocol.MemoryUser {
return s.validator.GetAll()
}
func (s *Server) GetUsersCount(context.Context) int64 {
return s.validator.GetCount()
}
func (s *Server) Network() []net.Network {
return []net.Network{net.Network_TCP}
}
func (s *Server) Process(ctx context.Context, network net.Network, conn stat.Connection, dispatcher routing.Dispatcher) error {
inbound := session.InboundFromContext(ctx)
inbound.Name = "hysteria"
inbound.CanSpliceCopy = 3
var useremail string
var userlevel uint32
type User interface{ User() *protocol.MemoryUser }
if v, ok := conn.(User); ok {
inbound.User = v.User()
if inbound.User != nil {
useremail = inbound.User.Email
userlevel = inbound.User.Level
}
}
iConn := stat.TryUnwrapStatsConn(conn)
if _, ok := iConn.(*hysteria.InterUdpConn); ok {
r := io.Reader(conn)
b := make([]byte, MaxUDPSize)
df := &Defragger{}
var firstMsg *UDPMessage
var firstDest net.Destination
for {
n, err := r.Read(b)
if err != nil {
return err
}
msg, err := ParseUDPMessage(b[:n])
if err != nil {
continue
}
dfMsg := df.Feed(msg)
if dfMsg == nil {
continue
}
firstMsg = dfMsg
firstDest, err = net.ParseDestination("udp:" + firstMsg.Addr)
if err != nil {
errors.LogDebug(context.Background(), dfMsg.Addr, " ParseDestination err ", err)
continue
}
break
}
reader := &UDPReader{
Reader: r,
buf: b,
df: df,
firstMsg: firstMsg,
firstDest: &firstDest,
}
writer := &UDPWriter{
Writer: conn,
buf: make([]byte, MaxUDPSize),
addr: firstMsg.Addr,
}
return dispatcher.DispatchLink(ctx, firstDest, &transport.Link{
Reader: reader,
Writer: writer,
})
} else {
sessionPolicy := s.policyManager.ForLevel(userlevel)
common.Must(conn.SetReadDeadline(time.Now().Add(sessionPolicy.Timeouts.Handshake)))
addr, err := ReadTCPRequest(conn)
if err != nil {
log.Record(&log.AccessMessage{
From: conn.RemoteAddr(),
To: "",
Status: log.AccessRejected,
Reason: err,
})
return errors.New("failed to create request from: ", conn.RemoteAddr()).Base(err)
}
common.Must(conn.SetReadDeadline(time.Time{}))
dest, err := net.ParseDestination("tcp:" + addr)
if err != nil {
return err
}
ctx = log.ContextWithAccessMessage(ctx, &log.AccessMessage{
From: conn.RemoteAddr(),
To: dest,
Status: log.AccessAccepted,
Reason: "",
Email: useremail,
})
errors.LogInfo(ctx, "tunnelling request to ", dest)
bufferedWriter := buf.NewBufferedWriter(buf.NewWriter(conn))
err = WriteTCPResponse(bufferedWriter, true, "")
if err != nil {
return errors.New("failed to write response").Base(err)
}
if err := bufferedWriter.SetBuffered(false); err != nil {
return err
}
return dispatcher.DispatchLink(ctx, dest, &transport.Link{
Reader: buf.NewReader(conn),
Writer: bufferedWriter,
})
}
}
func init() {
common.Must(common.RegisterConfig((*ServerConfig)(nil), func(ctx context.Context, config interface{}) (interface{}, error) {
return NewServer(ctx, config.(*ServerConfig))
}))
}