initial commit

This commit is contained in:
ginuerzh
2022-03-14 20:27:14 +08:00
commit 9397cb5351
175 changed files with 16196 additions and 0 deletions

View File

@ -0,0 +1,148 @@
// Code generated by protoc-gen-go. DO NOT EDIT.
// versions:
// protoc-gen-go v1.26.0
// protoc v3.12.4
// source: gost.proto
package proto
import (
protoreflect "google.golang.org/protobuf/reflect/protoreflect"
protoimpl "google.golang.org/protobuf/runtime/protoimpl"
reflect "reflect"
sync "sync"
)
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 Chunk struct {
state protoimpl.MessageState
sizeCache protoimpl.SizeCache
unknownFields protoimpl.UnknownFields
Data []byte `protobuf:"bytes,1,opt,name=data,proto3" json:"data,omitempty"`
}
func (x *Chunk) Reset() {
*x = Chunk{}
if protoimpl.UnsafeEnabled {
mi := &file_gost_proto_msgTypes[0]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
}
func (x *Chunk) String() string {
return protoimpl.X.MessageStringOf(x)
}
func (*Chunk) ProtoMessage() {}
func (x *Chunk) ProtoReflect() protoreflect.Message {
mi := &file_gost_proto_msgTypes[0]
if protoimpl.UnsafeEnabled && x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil {
ms.StoreMessageInfo(mi)
}
return ms
}
return mi.MessageOf(x)
}
// Deprecated: Use Chunk.ProtoReflect.Descriptor instead.
func (*Chunk) Descriptor() ([]byte, []int) {
return file_gost_proto_rawDescGZIP(), []int{0}
}
func (x *Chunk) GetData() []byte {
if x != nil {
return x.Data
}
return nil
}
var File_gost_proto protoreflect.FileDescriptor
var file_gost_proto_rawDesc = []byte{
0x0a, 0x0a, 0x67, 0x6f, 0x73, 0x74, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x22, 0x1b, 0x0a, 0x05,
0x43, 0x68, 0x75, 0x6e, 0x6b, 0x12, 0x12, 0x0a, 0x04, 0x64, 0x61, 0x74, 0x61, 0x18, 0x01, 0x20,
0x01, 0x28, 0x0c, 0x52, 0x04, 0x64, 0x61, 0x74, 0x61, 0x32, 0x29, 0x0a, 0x09, 0x47, 0x6f, 0x73,
0x74, 0x54, 0x75, 0x6e, 0x65, 0x6c, 0x12, 0x1c, 0x0a, 0x06, 0x54, 0x75, 0x6e, 0x6e, 0x65, 0x6c,
0x12, 0x06, 0x2e, 0x43, 0x68, 0x75, 0x6e, 0x6b, 0x1a, 0x06, 0x2e, 0x43, 0x68, 0x75, 0x6e, 0x6b,
0x28, 0x01, 0x30, 0x01, 0x42, 0x34, 0x5a, 0x32, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63,
0x6f, 0x6d, 0x2f, 0x67, 0x6f, 0x2d, 0x67, 0x6f, 0x73, 0x74, 0x2f, 0x67, 0x6f, 0x73, 0x74, 0x2f,
0x70, 0x6b, 0x67, 0x2f, 0x63, 0x6f, 0x6d, 0x6d, 0x6f, 0x6e, 0x2f, 0x75, 0x74, 0x69, 0x6c, 0x2f,
0x67, 0x72, 0x70, 0x63, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74,
0x6f, 0x33,
}
var (
file_gost_proto_rawDescOnce sync.Once
file_gost_proto_rawDescData = file_gost_proto_rawDesc
)
func file_gost_proto_rawDescGZIP() []byte {
file_gost_proto_rawDescOnce.Do(func() {
file_gost_proto_rawDescData = protoimpl.X.CompressGZIP(file_gost_proto_rawDescData)
})
return file_gost_proto_rawDescData
}
var file_gost_proto_msgTypes = make([]protoimpl.MessageInfo, 1)
var file_gost_proto_goTypes = []any{
(*Chunk)(nil), // 0: Chunk
}
var file_gost_proto_depIdxs = []int32{
0, // 0: GostTunel.Tunnel:input_type -> Chunk
0, // 1: GostTunel.Tunnel:output_type -> Chunk
1, // [1:2] is the sub-list for method output_type
0, // [0:1] 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_gost_proto_init() }
func file_gost_proto_init() {
if File_gost_proto != nil {
return
}
if !protoimpl.UnsafeEnabled {
file_gost_proto_msgTypes[0].Exporter = func(v any, i int) any {
switch v := v.(*Chunk); i {
case 0:
return &v.state
case 1:
return &v.sizeCache
case 2:
return &v.unknownFields
default:
return nil
}
}
}
type x struct{}
out := protoimpl.TypeBuilder{
File: protoimpl.DescBuilder{
GoPackagePath: reflect.TypeOf(x{}).PkgPath(),
RawDescriptor: file_gost_proto_rawDesc,
NumEnums: 0,
NumMessages: 1,
NumExtensions: 0,
NumServices: 1,
},
GoTypes: file_gost_proto_goTypes,
DependencyIndexes: file_gost_proto_depIdxs,
MessageInfos: file_gost_proto_msgTypes,
}.Build()
File_gost_proto = out.File
file_gost_proto_rawDesc = nil
file_gost_proto_goTypes = nil
file_gost_proto_depIdxs = nil
}

View File

@ -0,0 +1,10 @@
syntax = "proto3";
option go_package = "github.com/go-gost/gost/v3/pkg/common/util/grpc/proto";
message Chunk {
bytes data = 1;
}
service GostTunel {
rpc Tunnel (stream Chunk) returns (stream Chunk);
}

View File

@ -0,0 +1,133 @@
// Code generated by protoc-gen-go-grpc. DO NOT EDIT.
package proto
import (
context "context"
grpc "google.golang.org/grpc"
codes "google.golang.org/grpc/codes"
status "google.golang.org/grpc/status"
)
// This is a compile-time assertion to ensure that this generated file
// is compatible with the grpc package it is being compiled against.
// Requires gRPC-Go v1.32.0 or later.
const _ = grpc.SupportPackageIsVersion7
// GostTunelClient is the client API for GostTunel service.
//
// For semantics around ctx use and closing/ending streaming RPCs, please refer to https://pkg.go.dev/google.golang.org/grpc/?tab=doc#ClientConn.NewStream.
type GostTunelClient interface {
Tunnel(ctx context.Context, opts ...grpc.CallOption) (GostTunel_TunnelClient, error)
}
type gostTunelClient struct {
cc grpc.ClientConnInterface
}
func NewGostTunelClient(cc grpc.ClientConnInterface) GostTunelClient {
return &gostTunelClient{cc}
}
func (c *gostTunelClient) Tunnel(ctx context.Context, opts ...grpc.CallOption) (GostTunel_TunnelClient, error) {
stream, err := c.cc.NewStream(ctx, &GostTunel_ServiceDesc.Streams[0], "/GostTunel/Tunnel", opts...)
if err != nil {
return nil, err
}
x := &gostTunelTunnelClient{stream}
return x, nil
}
type GostTunel_TunnelClient interface {
Send(*Chunk) error
Recv() (*Chunk, error)
grpc.ClientStream
}
type gostTunelTunnelClient struct {
grpc.ClientStream
}
func (x *gostTunelTunnelClient) Send(m *Chunk) error {
return x.ClientStream.SendMsg(m)
}
func (x *gostTunelTunnelClient) Recv() (*Chunk, error) {
m := new(Chunk)
if err := x.ClientStream.RecvMsg(m); err != nil {
return nil, err
}
return m, nil
}
// GostTunelServer is the server API for GostTunel service.
// All implementations must embed UnimplementedGostTunelServer
// for forward compatibility
type GostTunelServer interface {
Tunnel(GostTunel_TunnelServer) error
mustEmbedUnimplementedGostTunelServer()
}
// UnimplementedGostTunelServer must be embedded to have forward compatible implementations.
type UnimplementedGostTunelServer struct {
}
func (UnimplementedGostTunelServer) Tunnel(GostTunel_TunnelServer) error {
return status.Errorf(codes.Unimplemented, "method Tunnel not implemented")
}
func (UnimplementedGostTunelServer) mustEmbedUnimplementedGostTunelServer() {}
// UnsafeGostTunelServer may be embedded to opt out of forward compatibility for this service.
// Use of this interface is not recommended, as added methods to GostTunelServer will
// result in compilation errors.
type UnsafeGostTunelServer interface {
mustEmbedUnimplementedGostTunelServer()
}
func RegisterGostTunelServer(s grpc.ServiceRegistrar, srv GostTunelServer) {
s.RegisterService(&GostTunel_ServiceDesc, srv)
}
func _GostTunel_Tunnel_Handler(srv any, stream grpc.ServerStream) error {
return srv.(GostTunelServer).Tunnel(&gostTunelTunnelServer{stream})
}
type GostTunel_TunnelServer interface {
Send(*Chunk) error
Recv() (*Chunk, error)
grpc.ServerStream
}
type gostTunelTunnelServer struct {
grpc.ServerStream
}
func (x *gostTunelTunnelServer) Send(m *Chunk) error {
return x.ServerStream.SendMsg(m)
}
func (x *gostTunelTunnelServer) Recv() (*Chunk, error) {
m := new(Chunk)
if err := x.ServerStream.RecvMsg(m); err != nil {
return nil, err
}
return m, nil
}
// GostTunel_ServiceDesc is the grpc.ServiceDesc for GostTunel service.
// It's only intended for direct use with grpc.RegisterService,
// and not to be introspected or modified (even as a copy)
var GostTunel_ServiceDesc = grpc.ServiceDesc{
ServiceName: "GostTunel",
HandlerType: (*GostTunelServer)(nil),
Methods: []grpc.MethodDesc{},
Streams: []grpc.StreamDesc{
{
StreamName: "Tunnel",
Handler: _GostTunel_Tunnel_Handler,
ServerStreams: true,
ClientStreams: true,
},
},
Metadata: "gost.proto",
}

View File

@ -0,0 +1,3 @@
protoc --go_out=. --go_opt=paths=source_relative \
--go-grpc_out=. --go-grpc_opt=paths=source_relative \
gost.proto

132
internal/util/http2/conn.go Normal file
View File

@ -0,0 +1,132 @@
package http2
import (
"errors"
"net"
"net/http"
"time"
)
// a dummy HTTP2 client conn used by HTTP2 client connector
type ClientConn struct {
localAddr net.Addr
remoteAddr net.Addr
client *http.Client
onClose func()
}
func NewClientConn(localAddr, remoteAddr net.Addr, client *http.Client, onClose func()) net.Conn {
return &ClientConn{
localAddr: localAddr,
remoteAddr: remoteAddr,
client: client,
onClose: onClose,
}
}
func (c *ClientConn) Client() *http.Client {
return c.client
}
func (c *ClientConn) Close() error {
if c.onClose != nil {
c.onClose()
}
return nil
}
func (c *ClientConn) Read(b []byte) (n int, err error) {
return 0, &net.OpError{Op: "read", Net: "nop", Source: nil, Addr: nil, Err: errors.New("read not supported")}
}
func (c *ClientConn) Write(b []byte) (n int, err error) {
return 0, &net.OpError{Op: "write", Net: "nop", Source: nil, Addr: nil, Err: errors.New("write not supported")}
}
func (c *ClientConn) LocalAddr() net.Addr {
return c.localAddr
}
func (c *ClientConn) RemoteAddr() net.Addr {
return c.remoteAddr
}
func (c *ClientConn) SetDeadline(t time.Time) error {
return &net.OpError{Op: "set", Net: "nop", Source: nil, Addr: nil, Err: errors.New("deadline not supported")}
}
func (c *ClientConn) SetReadDeadline(t time.Time) error {
return &net.OpError{Op: "set", Net: "nop", Source: nil, Addr: nil, Err: errors.New("deadline not supported")}
}
func (c *ClientConn) SetWriteDeadline(t time.Time) error {
return &net.OpError{Op: "set", Net: "nop", Source: nil, Addr: nil, Err: errors.New("deadline not supported")}
}
// a dummy HTTP2 server conn used by HTTP2 handler
type ServerConn struct {
r *http.Request
w http.ResponseWriter
localAddr net.Addr
remoteAddr net.Addr
closed chan struct{}
}
func NewServerConn(w http.ResponseWriter, r *http.Request, localAddr, remoteAddr net.Addr) *ServerConn {
return &ServerConn{
r: r,
w: w,
localAddr: localAddr,
remoteAddr: remoteAddr,
closed: make(chan struct{}),
}
}
func (c *ServerConn) Done() <-chan struct{} {
return c.closed
}
func (c *ServerConn) Request() *http.Request {
return c.r
}
func (c *ServerConn) Writer() http.ResponseWriter {
return c.w
}
func (c *ServerConn) Read(b []byte) (n int, err error) {
return 0, &net.OpError{Op: "read", Net: "http2", Source: nil, Addr: nil, Err: errors.New("read not supported")}
}
func (c *ServerConn) Write(b []byte) (n int, err error) {
return 0, &net.OpError{Op: "write", Net: "http2", Source: nil, Addr: nil, Err: errors.New("write not supported")}
}
func (c *ServerConn) Close() error {
select {
case <-c.closed:
default:
close(c.closed)
}
return nil
}
func (c *ServerConn) LocalAddr() net.Addr {
return c.localAddr
}
func (c *ServerConn) RemoteAddr() net.Addr {
return c.remoteAddr
}
func (c *ServerConn) SetDeadline(t time.Time) error {
return &net.OpError{Op: "set", Net: "http2", Source: nil, Addr: nil, Err: errors.New("deadline not supported")}
}
func (c *ServerConn) SetReadDeadline(t time.Time) error {
return &net.OpError{Op: "set", Net: "http2", Source: nil, Addr: nil, Err: errors.New("deadline not supported")}
}
func (c *ServerConn) SetWriteDeadline(t time.Time) error {
return &net.OpError{Op: "set", Net: "http2", Source: nil, Addr: nil, Err: errors.New("deadline not supported")}
}

285
internal/util/icmp/conn.go Normal file
View File

@ -0,0 +1,285 @@
package icmp
import (
"encoding/binary"
"errors"
"fmt"
"math"
"net"
"sync/atomic"
"github.com/go-gost/gost/v3/pkg/common/bufpool"
"github.com/go-gost/gost/v3/pkg/logger"
"golang.org/x/net/icmp"
"golang.org/x/net/ipv4"
)
const (
readBufferSize = 1500
writeBufferSize = 1500
magicNumber = 0x474F5354
)
const (
messageHeaderLen = 10
)
const (
FlagAck = 1
)
var (
ErrInvalidPacket = errors.New("icmp: invalid packet")
ErrInvalidType = errors.New("icmp: invalid type")
ErrShortBuffer = errors.New("icmp: short buffer")
)
type message struct {
// magic uint32 // magic number
flags uint16 // flags
// rsv uint16 // reserved field
// len uint16 // length of data
data []byte
}
func (m *message) Encode(b []byte) (n int, err error) {
if len(b) < messageHeaderLen+len(m.data) {
err = ErrShortBuffer
return
}
binary.BigEndian.PutUint32(b[:4], magicNumber) // magic number
binary.BigEndian.PutUint16(b[4:6], m.flags) // flags
binary.BigEndian.PutUint16(b[6:8], 0) // reserved
binary.BigEndian.PutUint16(b[8:10], uint16(len(m.data)))
copy(b[messageHeaderLen:], m.data)
n = messageHeaderLen + len(m.data)
return
}
func (m *message) Decode(b []byte) (n int, err error) {
if len(b) < messageHeaderLen {
err = ErrShortBuffer
return
}
if binary.BigEndian.Uint32(b[:4]) != magicNumber {
err = ErrInvalidPacket
return
}
m.flags = binary.BigEndian.Uint16(b[4:6])
length := binary.BigEndian.Uint16(b[8:10])
if len(b[messageHeaderLen:]) < int(length) {
err = ErrShortBuffer
return
}
m.data = b[messageHeaderLen : messageHeaderLen+length]
n = messageHeaderLen + int(length)
return
}
type clientConn struct {
net.PacketConn
id int
seq uint32
}
func ClientConn(conn net.PacketConn, id int) net.PacketConn {
return &clientConn{
PacketConn: conn,
id: id,
}
}
func (c *clientConn) ReadFrom(b []byte) (n int, addr net.Addr, err error) {
buf := bufpool.Get(readBufferSize)
defer bufpool.Put(buf)
for {
n, addr, err = c.PacketConn.ReadFrom(*buf)
if err != nil {
return
}
m, err := icmp.ParseMessage(1, (*buf)[:n])
if err != nil {
// logger.Default().Error("icmp: parse message %v", err)
return 0, addr, err
}
echo, ok := m.Body.(*icmp.Echo)
if !ok || m.Type != ipv4.ICMPTypeEchoReply {
// logger.Default().Warnf("icmp: invalid type %s (discarded)", m.Type)
continue // discard
}
if echo.ID != c.id {
// logger.Default().Warnf("icmp: id mismatch got %d, should be %d (discarded)", echo.ID, c.id)
continue
}
msg := message{}
if _, err := msg.Decode(echo.Data); err != nil {
logger.Default().Warn(err)
continue
}
if msg.flags&FlagAck == 0 {
// logger.Default().Warn("icmp: invalid message (discarded)")
continue
}
n = copy(b, msg.data)
break
}
if v, ok := addr.(*net.IPAddr); ok {
addr = &net.UDPAddr{
IP: v.IP,
Port: c.id,
}
}
// logger.Default().Infof("icmp: read from: %v %d", addr, n)
return
}
func (c *clientConn) WriteTo(b []byte, addr net.Addr) (n int, err error) {
// logger.Default().Infof("icmp: write to: %v %d", addr, len(b))
switch v := addr.(type) {
case *net.UDPAddr:
addr = &net.IPAddr{IP: v.IP}
}
buf := bufpool.Get(writeBufferSize)
defer bufpool.Put(buf)
msg := message{
data: b,
}
nn, err := msg.Encode(*buf)
if err != nil {
return
}
echo := icmp.Echo{
ID: c.id,
Seq: int(atomic.AddUint32(&c.seq, 1)),
Data: (*buf)[:nn],
}
m := icmp.Message{
Type: ipv4.ICMPTypeEcho,
Code: 0,
Body: &echo,
}
wb, err := m.Marshal(nil)
if err != nil {
return 0, err
}
_, err = c.PacketConn.WriteTo(wb, addr)
n = len(b)
return
}
type serverConn struct {
net.PacketConn
seqs [65535]uint32
}
func ServerConn(conn net.PacketConn) net.PacketConn {
return &serverConn{
PacketConn: conn,
}
}
func (c *serverConn) ReadFrom(b []byte) (n int, addr net.Addr, err error) {
buf := bufpool.Get(readBufferSize)
defer bufpool.Put(buf)
for {
n, addr, err = c.PacketConn.ReadFrom(*buf)
if err != nil {
return
}
m, err := icmp.ParseMessage(1, (*buf)[:n])
if err != nil {
// logger.Default().Error("icmp: parse message %v", err)
return 0, addr, err
}
echo, ok := m.Body.(*icmp.Echo)
if !ok || m.Type != ipv4.ICMPTypeEcho || echo.ID <= 0 {
// logger.Default().Warnf("icmp: invalid type %s (discarded)", m.Type)
continue
}
atomic.StoreUint32(&c.seqs[uint16(echo.ID-1)], uint32(echo.Seq))
msg := message{}
if _, err := msg.Decode(echo.Data); err != nil {
continue
}
if msg.flags&FlagAck > 0 {
continue
}
n = copy(b, msg.data)
if v, ok := addr.(*net.IPAddr); ok {
addr = &net.UDPAddr{
IP: v.IP,
Port: echo.ID,
}
}
break
}
// logger.Default().Infof("icmp: read from: %v %d", addr, n)
return
}
func (c *serverConn) WriteTo(b []byte, addr net.Addr) (n int, err error) {
// logger.Default().Infof("icmp: write to: %v %d", addr, len(b))
var id int
switch v := addr.(type) {
case *net.UDPAddr:
addr = &net.IPAddr{IP: v.IP}
id = v.Port
}
if id <= 0 || id > math.MaxUint16 {
err = fmt.Errorf("icmp: invalid message id %v", addr)
return
}
buf := bufpool.Get(writeBufferSize)
defer bufpool.Put(buf)
msg := message{
flags: FlagAck,
data: b,
}
nn, err := msg.Encode(*buf)
if err != nil {
return
}
echo := icmp.Echo{
ID: id,
Seq: int(atomic.LoadUint32(&c.seqs[id-1])),
Data: (*buf)[:nn],
}
m := icmp.Message{
Type: ipv4.ICMPTypeEchoReply,
Code: 0,
Body: &echo,
}
wb, err := m.Marshal(nil)
if err != nil {
return 0, err
}
_, err = c.PacketConn.WriteTo(wb, addr)
n = len(b)
return
}

115
internal/util/kcp/config.go Normal file
View File

@ -0,0 +1,115 @@
package kcp
import (
"crypto/sha1"
"github.com/xtaci/kcp-go/v5"
"golang.org/x/crypto/pbkdf2"
)
var (
// DefaultSalt is the default salt for KCP cipher.
DefaultSalt = "kcp-go"
)
var (
// DefaultKCPConfig is the default KCP config.
DefaultConfig = &Config{
Key: "it's a secrect",
Crypt: "aes",
Mode: "fast",
MTU: 1350,
SndWnd: 1024,
RcvWnd: 1024,
DataShard: 10,
ParityShard: 3,
DSCP: 0,
NoComp: false,
AckNodelay: false,
NoDelay: 0,
Interval: 50,
Resend: 0,
NoCongestion: 0,
SockBuf: 4194304,
KeepAlive: 10,
SnmpLog: "",
SnmpPeriod: 60,
Signal: false,
TCP: false,
}
)
// KCPConfig describes the config for KCP.
type Config struct {
Key string `json:"key"`
Crypt string `json:"crypt"`
Mode string `json:"mode"`
MTU int `json:"mtu"`
SndWnd int `json:"sndwnd"`
RcvWnd int `json:"rcvwnd"`
DataShard int `json:"datashard"`
ParityShard int `json:"parityshard"`
DSCP int `json:"dscp"`
NoComp bool `json:"nocomp"`
AckNodelay bool `json:"acknodelay"`
NoDelay int `json:"nodelay"`
Interval int `json:"interval"`
Resend int `json:"resend"`
NoCongestion int `json:"nc"`
SockBuf int `json:"sockbuf"`
KeepAlive int `json:"keepalive"`
SnmpLog string `json:"snmplog"`
SnmpPeriod int `json:"snmpperiod"`
Signal bool `json:"signal"` // Signal enables the signal SIGUSR1 feature.
TCP bool `json:"tcp"`
}
// Init initializes the KCP config.
func (c *Config) Init() {
switch c.Mode {
case "normal":
c.NoDelay, c.Interval, c.Resend, c.NoCongestion = 0, 40, 2, 1
case "fast":
c.NoDelay, c.Interval, c.Resend, c.NoCongestion = 0, 30, 2, 1
case "fast2":
c.NoDelay, c.Interval, c.Resend, c.NoCongestion = 1, 20, 2, 1
case "fast3":
c.NoDelay, c.Interval, c.Resend, c.NoCongestion = 1, 10, 2, 1
}
}
func BlockCrypt(key, crypt, salt string) (block kcp.BlockCrypt) {
pass := pbkdf2.Key([]byte(key), []byte(salt), 4096, 32, sha1.New)
switch crypt {
case "sm4":
block, _ = kcp.NewSM4BlockCrypt(pass[:16])
case "tea":
block, _ = kcp.NewTEABlockCrypt(pass[:16])
case "xor":
block, _ = kcp.NewSimpleXORBlockCrypt(pass)
case "none":
block, _ = kcp.NewNoneBlockCrypt(pass)
case "aes-128":
block, _ = kcp.NewAESBlockCrypt(pass[:16])
case "aes-192":
block, _ = kcp.NewAESBlockCrypt(pass[:24])
case "blowfish":
block, _ = kcp.NewBlowfishBlockCrypt(pass)
case "twofish":
block, _ = kcp.NewTwofishBlockCrypt(pass)
case "cast5":
block, _ = kcp.NewCast5BlockCrypt(pass[:16])
case "3des":
block, _ = kcp.NewTripleDESBlockCrypt(pass[:24])
case "xtea":
block, _ = kcp.NewXTEABlockCrypt(pass[:16])
case "salsa20":
block, _ = kcp.NewSalsa20BlockCrypt(pass)
case "aes":
fallthrough
default: // aes
block, _ = kcp.NewAESBlockCrypt(pass)
}
return
}

34
internal/util/kcp/kcp.go Normal file
View File

@ -0,0 +1,34 @@
package kcp
import (
"net"
"github.com/golang/snappy"
)
type kcpCompStreamConn struct {
net.Conn
w *snappy.Writer
r *snappy.Reader
}
func CompStreamConn(conn net.Conn) net.Conn {
return &kcpCompStreamConn{
Conn: conn,
w: snappy.NewBufferedWriter(conn),
r: snappy.NewReader(conn),
}
}
func (c *kcpCompStreamConn) Read(b []byte) (n int, err error) {
return c.r.Read(b)
}
func (c *kcpCompStreamConn) Write(b []byte) (n int, err error) {
n, err = c.w.Write(b)
if err != nil {
return
}
err = c.w.Flush()
return n, err
}

85
internal/util/mux/mux.go Normal file
View File

@ -0,0 +1,85 @@
package mux
import (
"net"
smux "github.com/xtaci/smux"
)
type Session struct {
conn net.Conn
session *smux.Session
}
func ClientSession(conn net.Conn) (*Session, error) {
s, err := smux.Client(conn, smux.DefaultConfig())
if err != nil {
return nil, err
}
return &Session{
conn: conn,
session: s,
}, nil
}
func ServerSession(conn net.Conn) (*Session, error) {
s, err := smux.Server(conn, smux.DefaultConfig())
if err != nil {
return nil, err
}
return &Session{
conn: conn,
session: s,
}, nil
}
func (session *Session) GetConn() (net.Conn, error) {
stream, err := session.session.OpenStream()
if err != nil {
return nil, err
}
return &streamConn{Conn: session.conn, stream: stream}, nil
}
func (session *Session) Accept() (net.Conn, error) {
stream, err := session.session.AcceptStream()
if err != nil {
return nil, err
}
return &streamConn{Conn: session.conn, stream: stream}, nil
}
func (session *Session) Close() error {
if session.session == nil {
return nil
}
return session.session.Close()
}
func (session *Session) IsClosed() bool {
if session.session == nil {
return true
}
return session.session.IsClosed()
}
func (session *Session) NumStreams() int {
return session.session.NumStreams()
}
type streamConn struct {
net.Conn
stream *smux.Stream
}
func (c *streamConn) Read(b []byte) (n int, err error) {
return c.stream.Read(b)
}
func (c *streamConn) Write(b []byte) (n int, err error) {
return c.stream.Write(b)
}
func (c *streamConn) Close() error {
return c.stream.Close()
}

105
internal/util/pht/client.go Normal file
View File

@ -0,0 +1,105 @@
package pht
import (
"context"
"errors"
"fmt"
"io"
"net"
"net/http"
"net/http/httputil"
"strconv"
"strings"
"github.com/go-gost/gost/v3/pkg/logger"
)
type Client struct {
Host string
Client *http.Client
AuthorizePath string
PushPath string
PullPath string
TLSEnabled bool
Logger logger.Logger
}
func (c *Client) Dial(ctx context.Context, addr string) (net.Conn, error) {
raddr, err := net.ResolveTCPAddr("tcp", addr)
if err != nil {
c.Logger.Error(err)
return nil, err
}
if c.Host != "" {
addr = net.JoinHostPort(c.Host, strconv.Itoa(raddr.Port))
}
token, err := c.authorize(ctx, addr)
if err != nil {
c.Logger.Error(err)
return nil, err
}
cn := &clientConn{
client: c.Client,
rxc: make(chan []byte, 128),
closed: make(chan struct{}),
localAddr: &net.TCPAddr{},
remoteAddr: raddr,
logger: c.Logger,
}
scheme := "http"
if c.TLSEnabled {
scheme = "https"
}
cn.pushURL = fmt.Sprintf("%s://%s%s?token=%s", scheme, addr, c.PushPath, token)
cn.pullURL = fmt.Sprintf("%s://%s%s?token=%s", scheme, addr, c.PullPath, token)
go cn.readLoop()
return cn, nil
}
func (c *Client) authorize(ctx context.Context, addr string) (token string, err error) {
var url string
if c.TLSEnabled {
url = fmt.Sprintf("https://%s%s", addr, c.AuthorizePath)
} else {
url = fmt.Sprintf("http://%s%s", addr, c.AuthorizePath)
}
r, err := http.NewRequestWithContext(ctx, http.MethodGet, url, nil)
if err != nil {
return
}
if c.Logger.IsLevelEnabled(logger.DebugLevel) {
dump, _ := httputil.DumpRequest(r, false)
c.Logger.Debug(string(dump))
}
resp, err := c.Client.Do(r)
if err != nil {
return
}
defer resp.Body.Close()
if c.Logger.IsLevelEnabled(logger.DebugLevel) {
dump, _ := httputil.DumpResponse(resp, false)
c.Logger.Debug(string(dump))
}
data, err := io.ReadAll(resp.Body)
if err != nil {
return
}
if strings.HasPrefix(string(data), "token=") {
token = strings.TrimPrefix(string(data), "token=")
}
if token == "" {
err = errors.New("authorize failed")
}
return
}

176
internal/util/pht/conn.go Normal file
View File

@ -0,0 +1,176 @@
package pht
import (
"bufio"
"bytes"
"encoding/base64"
"errors"
"net"
"net/http"
"net/http/httputil"
"time"
"github.com/go-gost/gost/v3/pkg/logger"
)
type clientConn struct {
client *http.Client
pushURL string
pullURL string
buf []byte
rxc chan []byte
closed chan struct{}
localAddr net.Addr
remoteAddr net.Addr
logger logger.Logger
}
func (c *clientConn) Read(b []byte) (n int, err error) {
if len(c.buf) == 0 {
select {
case c.buf = <-c.rxc:
case <-c.closed:
err = net.ErrClosed
return
}
}
n = copy(b, c.buf)
c.buf = c.buf[n:]
return
}
func (c *clientConn) Write(b []byte) (n int, err error) {
if len(b) == 0 {
return
}
buf := bytes.NewBufferString(base64.StdEncoding.EncodeToString(b))
buf.WriteByte('\n')
r, err := http.NewRequest(http.MethodPost, c.pushURL, buf)
if err != nil {
return
}
if c.logger.IsLevelEnabled(logger.DebugLevel) {
dump, _ := httputil.DumpRequest(r, false)
c.logger.Debug(string(dump))
}
resp, err := c.client.Do(r)
if err != nil {
return
}
defer resp.Body.Close()
if c.logger.IsLevelEnabled(logger.DebugLevel) {
dump, _ := httputil.DumpResponse(resp, false)
c.logger.Debug(string(dump))
}
if resp.StatusCode != http.StatusOK {
err = errors.New(resp.Status)
return
}
n = len(b)
return
}
func (c *clientConn) readLoop() {
defer c.Close()
for {
err := func() error {
r, err := http.NewRequest(http.MethodGet, c.pullURL, nil)
if err != nil {
return err
}
if c.logger.IsLevelEnabled(logger.DebugLevel) {
dump, _ := httputil.DumpRequest(r, false)
c.logger.Debug(string(dump))
}
resp, err := c.client.Do(r)
if err != nil {
return err
}
defer resp.Body.Close()
if c.logger.IsLevelEnabled(logger.DebugLevel) {
dump, _ := httputil.DumpResponse(resp, false)
c.logger.Debug(string(dump))
}
if resp.StatusCode != http.StatusOK {
return errors.New(resp.Status)
}
scanner := bufio.NewScanner(resp.Body)
for scanner.Scan() {
b, err := base64.StdEncoding.DecodeString(scanner.Text())
if err != nil {
return err
}
select {
case c.rxc <- b:
case <-c.closed:
return net.ErrClosed
}
}
return scanner.Err()
}()
if err != nil {
c.logger.Error(err)
return
}
}
}
func (c *clientConn) LocalAddr() net.Addr {
return c.localAddr
}
func (c *clientConn) RemoteAddr() net.Addr {
return c.remoteAddr
}
func (c *clientConn) Close() error {
select {
case <-c.closed:
default:
close(c.closed)
}
return nil
}
func (c *clientConn) SetReadDeadline(t time.Time) error {
return nil
}
func (c *clientConn) SetWriteDeadline(t time.Time) error {
return nil
}
func (c *clientConn) SetDeadline(t time.Time) error {
return nil
}
type serverConn struct {
net.Conn
remoteAddr net.Addr
localAddr net.Addr
}
func (c *serverConn) LocalAddr() net.Addr {
return c.localAddr
}
func (c *serverConn) RemoteAddr() net.Addr {
return c.remoteAddr
}

344
internal/util/pht/server.go Normal file
View File

@ -0,0 +1,344 @@
package pht
import (
"bufio"
"crypto/tls"
"encoding/base64"
"errors"
"fmt"
"net"
"net/http"
"net/http/httputil"
"os"
"strings"
"sync"
"time"
"github.com/go-gost/gost/v3/pkg/common/bufpool"
"github.com/go-gost/gost/v3/pkg/logger"
"github.com/lucas-clemente/quic-go"
"github.com/lucas-clemente/quic-go/http3"
"github.com/rs/xid"
)
const (
defaultBacklog = 128
)
type serverOptions struct {
authorizePath string
pushPath string
pullPath string
backlog int
tlsEnabled bool
tlsConfig *tls.Config
logger logger.Logger
}
type ServerOption func(opts *serverOptions)
func PathServerOption(authorizePath, pushPath, pullPath string) ServerOption {
return func(opts *serverOptions) {
opts.authorizePath = authorizePath
opts.pullPath = pullPath
opts.pushPath = pushPath
}
}
func BacklogServerOption(backlog int) ServerOption {
return func(opts *serverOptions) {
opts.backlog = backlog
}
}
func TLSConfigServerOption(tlsConfig *tls.Config) ServerOption {
return func(opts *serverOptions) {
opts.tlsConfig = tlsConfig
}
}
func EnableTLSServerOption(enable bool) ServerOption {
return func(opts *serverOptions) {
opts.tlsEnabled = enable
}
}
func LoggerServerOption(logger logger.Logger) ServerOption {
return func(opts *serverOptions) {
opts.logger = logger
}
}
// TODO: remove stale clients from conns
type Server struct {
addr net.Addr
httpServer *http.Server
http3Server *http3.Server
cqueue chan net.Conn
conns sync.Map
closed chan struct{}
options serverOptions
}
func NewServer(addr string, opts ...ServerOption) *Server {
var options serverOptions
for _, opt := range opts {
opt(&options)
}
if options.backlog <= 0 {
options.backlog = defaultBacklog
}
s := &Server{
httpServer: &http.Server{
Addr: addr,
ReadHeaderTimeout: 30 * time.Second,
},
cqueue: make(chan net.Conn, options.backlog),
closed: make(chan struct{}),
options: options,
}
mux := http.NewServeMux()
mux.HandleFunc(options.authorizePath, s.handleAuthorize)
mux.HandleFunc(options.pushPath, s.handlePush)
mux.HandleFunc(options.pullPath, s.handlePull)
s.httpServer.Handler = mux
return s
}
func NewHTTP3Server(addr string, quicConfig *quic.Config, opts ...ServerOption) *Server {
var options serverOptions
for _, opt := range opts {
opt(&options)
}
if options.backlog <= 0 {
options.backlog = defaultBacklog
}
s := &Server{
http3Server: &http3.Server{
Server: &http.Server{
Addr: addr,
TLSConfig: options.tlsConfig,
ReadHeaderTimeout: 30 * time.Second,
},
QuicConfig: quicConfig,
},
cqueue: make(chan net.Conn, options.backlog),
closed: make(chan struct{}),
options: options,
}
mux := http.NewServeMux()
mux.HandleFunc(options.authorizePath, s.handleAuthorize)
mux.HandleFunc(options.pushPath, s.handlePush)
mux.HandleFunc(options.pullPath, s.handlePull)
s.http3Server.Handler = mux
return s
}
func (s *Server) ListenAndServe() error {
if s.http3Server != nil {
addr, err := net.ResolveUDPAddr("udp", s.http3Server.Addr)
if err != nil {
return err
}
s.addr = addr
return s.http3Server.ListenAndServe()
}
ln, err := net.Listen("tcp", s.httpServer.Addr)
if err != nil {
s.options.logger.Error(err)
return err
}
s.addr = ln.Addr()
if s.options.tlsEnabled {
s.httpServer.TLSConfig = s.options.tlsConfig
ln = tls.NewListener(ln, s.options.tlsConfig)
}
return s.httpServer.Serve(ln)
}
func (s *Server) Accept() (conn net.Conn, err error) {
select {
case conn = <-s.cqueue:
case <-s.closed:
err = http.ErrServerClosed
}
return
}
func (s *Server) Close() error {
select {
case <-s.closed:
return http.ErrServerClosed
default:
close(s.closed)
if s.http3Server != nil {
return s.http3Server.Close()
}
return s.httpServer.Close()
}
}
func (s *Server) handleAuthorize(w http.ResponseWriter, r *http.Request) {
if s.options.logger.IsLevelEnabled(logger.DebugLevel) {
dump, _ := httputil.DumpRequest(r, false)
s.options.logger.Debug(string(dump))
}
raddr, _ := net.ResolveTCPAddr("tcp", r.RemoteAddr)
if raddr == nil {
raddr = &net.TCPAddr{}
}
// connection id
cid := xid.New().String()
c1, c2 := net.Pipe()
c := &serverConn{
Conn: c1,
localAddr: s.addr,
remoteAddr: raddr,
}
select {
case s.cqueue <- c:
default:
c.Close()
s.options.logger.Warnf("connection queue is full, client %s discarded", r.RemoteAddr)
w.WriteHeader(http.StatusTooManyRequests)
return
}
w.Write([]byte(fmt.Sprintf("token=%s", cid)))
s.conns.Store(cid, c2)
}
func (s *Server) handlePush(w http.ResponseWriter, r *http.Request) {
if s.options.logger.IsLevelEnabled(logger.DebugLevel) {
dump, _ := httputil.DumpRequest(r, false)
s.options.logger.Debug(string(dump))
}
if r.Method != http.MethodPost {
w.WriteHeader(http.StatusBadRequest)
return
}
if err := r.ParseForm(); err != nil {
w.WriteHeader(http.StatusBadRequest)
return
}
cid := r.Form.Get("token")
v, ok := s.conns.Load(cid)
if !ok {
w.WriteHeader(http.StatusForbidden)
return
}
conn := v.(net.Conn)
br := bufio.NewReader(r.Body)
data, err := br.ReadString('\n')
if err != nil {
s.options.logger.Error(err)
conn.Close()
s.conns.Delete(cid)
w.WriteHeader(http.StatusBadRequest)
return
}
data = strings.TrimSuffix(data, "\n")
if len(data) == 0 {
return
}
b, err := base64.StdEncoding.DecodeString(data)
if err != nil {
s.options.logger.Error(err)
s.conns.Delete(cid)
conn.Close()
w.WriteHeader(http.StatusBadRequest)
return
}
conn.SetWriteDeadline(time.Now().Add(30 * time.Second))
defer conn.SetWriteDeadline(time.Time{})
if _, err := conn.Write(b); err != nil {
s.options.logger.Error(err)
s.conns.Delete(cid)
conn.Close()
w.WriteHeader(http.StatusGone)
}
}
func (s *Server) handlePull(w http.ResponseWriter, r *http.Request) {
if s.options.logger.IsLevelEnabled(logger.DebugLevel) {
dump, _ := httputil.DumpRequest(r, false)
s.options.logger.Debug(string(dump))
}
if r.Method != http.MethodGet {
w.WriteHeader(http.StatusBadRequest)
return
}
if err := r.ParseForm(); err != nil {
w.WriteHeader(http.StatusBadRequest)
return
}
cid := r.Form.Get("token")
v, ok := s.conns.Load(cid)
if !ok {
w.WriteHeader(http.StatusForbidden)
return
}
conn := v.(net.Conn)
w.WriteHeader(http.StatusOK)
if fw, ok := w.(http.Flusher); ok {
fw.Flush()
}
b := bufpool.Get(4096)
defer bufpool.Put(b)
for {
conn.SetReadDeadline(time.Now().Add(10 * time.Second))
n, err := conn.Read(*b)
if err != nil {
if !errors.Is(err, os.ErrDeadlineExceeded) {
s.options.logger.Error(err)
s.conns.Delete(cid)
conn.Close()
} else {
(*b)[0] = '\n'
w.Write((*b)[:1])
}
return
}
bw := bufio.NewWriter(w)
bw.WriteString(base64.StdEncoding.EncodeToString((*b)[:n]))
bw.WriteString("\n")
if err := bw.Flush(); err != nil {
return
}
if fw, ok := w.(http.Flusher); ok {
fw.Flush()
}
}
}

View File

@ -0,0 +1,90 @@
package quic
import (
"crypto/aes"
"crypto/cipher"
"crypto/rand"
"errors"
"io"
"net"
)
type cipherConn struct {
net.PacketConn
key []byte
}
func CipherPacketConn(conn net.PacketConn, key []byte) net.PacketConn {
return &cipherConn{
PacketConn: conn,
key: key,
}
}
func (conn *cipherConn) ReadFrom(data []byte) (n int, addr net.Addr, err error) {
n, addr, err = conn.PacketConn.ReadFrom(data)
if err != nil {
return
}
b, err := conn.decrypt(data[:n])
if err != nil {
return
}
copy(data, b)
return len(b), addr, nil
}
func (conn *cipherConn) WriteTo(data []byte, addr net.Addr) (n int, err error) {
b, err := conn.encrypt(data)
if err != nil {
return
}
_, err = conn.PacketConn.WriteTo(b, addr)
if err != nil {
return
}
return len(b), nil
}
func (conn *cipherConn) encrypt(data []byte) ([]byte, error) {
c, err := aes.NewCipher(conn.key)
if err != nil {
return nil, err
}
gcm, err := cipher.NewGCM(c)
if err != nil {
return nil, err
}
nonce := make([]byte, gcm.NonceSize())
if _, err = io.ReadFull(rand.Reader, nonce); err != nil {
return nil, err
}
return gcm.Seal(nonce, nonce, data, nil), nil
}
func (conn *cipherConn) decrypt(data []byte) ([]byte, error) {
c, err := aes.NewCipher(conn.key)
if err != nil {
return nil, err
}
gcm, err := cipher.NewGCM(c)
if err != nil {
return nil, err
}
nonceSize := gcm.NonceSize()
if len(data) < nonceSize {
return nil, errors.New("ciphertext too short")
}
nonce, ciphertext := data[:nonceSize], data[nonceSize:]
return gcm.Open(nil, nonce, ciphertext, nil)
}

172
internal/util/relay/conn.go Normal file
View File

@ -0,0 +1,172 @@
package relay
import (
"bytes"
"net"
"github.com/go-gost/gosocks5"
"github.com/go-gost/gost/v3/pkg/common/bufpool"
)
type udpTunConn struct {
net.Conn
taddr net.Addr
}
func UDPTunClientConn(c net.Conn, targetAddr net.Addr) net.Conn {
return &udpTunConn{
Conn: c,
taddr: targetAddr,
}
}
func UDPTunClientPacketConn(c net.Conn) net.PacketConn {
return &udpTunConn{
Conn: c,
}
}
func UDPTunServerConn(c net.Conn) net.PacketConn {
return &udpTunConn{
Conn: c,
}
}
func (c *udpTunConn) ReadFrom(b []byte) (n int, addr net.Addr, err error) {
socksAddr := gosocks5.Addr{}
header := gosocks5.UDPHeader{
Addr: &socksAddr,
}
dgram := gosocks5.UDPDatagram{
Header: &header,
Data: b,
}
_, err = dgram.ReadFrom(c.Conn)
if err != nil {
return
}
n = len(dgram.Data)
if n > len(b) {
n = copy(b, dgram.Data)
}
addr, err = net.ResolveUDPAddr("udp", socksAddr.String())
return
}
func (c *udpTunConn) Read(b []byte) (n int, err error) {
n, _, err = c.ReadFrom(b)
return
}
func (c *udpTunConn) WriteTo(b []byte, addr net.Addr) (n int, err error) {
socksAddr := gosocks5.Addr{}
if err = socksAddr.ParseFrom(addr.String()); err != nil {
return
}
header := gosocks5.UDPHeader{
Addr: &socksAddr,
}
dgram := gosocks5.UDPDatagram{
Header: &header,
Data: b,
}
dgram.Header.Rsv = uint16(len(dgram.Data))
dgram.Header.Frag = 0xff // UDP tun relay flag, used by shadowsocks
_, err = dgram.WriteTo(c.Conn)
n = len(b)
return
}
func (c *udpTunConn) Write(b []byte) (n int, err error) {
return c.WriteTo(b, c.taddr)
}
var (
DefaultBufferSize = 4096
)
type udpConn struct {
net.PacketConn
raddr net.Addr
taddr net.Addr
bufferSize int
}
func UDPConn(c net.PacketConn, bufferSize int) net.PacketConn {
return &udpConn{
PacketConn: c,
bufferSize: bufferSize,
}
}
// ReadFrom reads an UDP datagram.
// NOTE: for server side,
// the returned addr is the target address the client want to relay to.
func (c *udpConn) ReadFrom(b []byte) (n int, addr net.Addr, err error) {
rbuf := bufpool.Get(c.bufferSize)
defer bufpool.Put(rbuf)
n, c.raddr, err = c.PacketConn.ReadFrom(*rbuf)
if err != nil {
return
}
socksAddr := gosocks5.Addr{}
header := gosocks5.UDPHeader{
Addr: &socksAddr,
}
hlen, err := header.ReadFrom(bytes.NewReader((*rbuf)[:n]))
if err != nil {
return
}
n = copy(b, (*rbuf)[hlen:n])
addr, err = net.ResolveUDPAddr("udp", socksAddr.String())
return
}
func (c *udpConn) Read(b []byte) (n int, err error) {
n, _, err = c.ReadFrom(b)
return
}
func (c *udpConn) WriteTo(b []byte, addr net.Addr) (n int, err error) {
wbuf := bufpool.Get(c.bufferSize)
defer bufpool.Put(wbuf)
socksAddr := gosocks5.Addr{}
if err = socksAddr.ParseFrom(addr.String()); err != nil {
return
}
header := gosocks5.UDPHeader{
Addr: &socksAddr,
}
dgram := gosocks5.UDPDatagram{
Header: &header,
Data: b,
}
buf := bytes.NewBuffer((*wbuf)[:0])
_, err = dgram.WriteTo(buf)
if err != nil {
return
}
_, err = c.PacketConn.WriteTo(buf.Bytes(), c.raddr)
n = len(b)
return
}
func (c *udpConn) Write(b []byte) (n int, err error) {
return c.WriteTo(b, c.taddr)
}
func (c *udpConn) RemoteAddr() net.Addr {
return c.raddr
}

96
internal/util/ss/conn.go Normal file
View File

@ -0,0 +1,96 @@
package ss
import (
"bytes"
"net"
"github.com/go-gost/gosocks5"
"github.com/go-gost/gost/v3/pkg/common/bufpool"
)
var (
DefaultBufferSize = 4096
)
var (
_ net.PacketConn = (*UDPConn)(nil)
_ net.Conn = (*UDPConn)(nil)
)
type UDPConn struct {
net.PacketConn
raddr net.Addr
taddr net.Addr
bufferSize int
}
func UDPClientConn(c net.PacketConn, remoteAddr, targetAddr net.Addr, bufferSize int) *UDPConn {
return &UDPConn{
PacketConn: c,
raddr: remoteAddr,
taddr: targetAddr,
bufferSize: bufferSize,
}
}
func UDPServerConn(c net.PacketConn, remoteAddr net.Addr, bufferSize int) *UDPConn {
return &UDPConn{
PacketConn: c,
raddr: remoteAddr,
bufferSize: bufferSize,
}
}
func (c *UDPConn) ReadFrom(b []byte) (n int, addr net.Addr, err error) {
rbuf := bufpool.Get(c.bufferSize)
defer bufpool.Put(rbuf)
n, _, err = c.PacketConn.ReadFrom(*rbuf)
if err != nil {
return
}
saddr := gosocks5.Addr{}
addrLen, err := saddr.ReadFrom(bytes.NewReader((*rbuf)[:n]))
if err != nil {
return
}
n = copy(b, (*rbuf)[addrLen:n])
addr, err = net.ResolveUDPAddr("udp", saddr.String())
return
}
func (c *UDPConn) Read(b []byte) (n int, err error) {
n, _, err = c.ReadFrom(b)
return
}
func (c *UDPConn) WriteTo(b []byte, addr net.Addr) (n int, err error) {
wbuf := bufpool.Get(c.bufferSize)
defer bufpool.Put(wbuf)
socksAddr := gosocks5.Addr{}
if err = socksAddr.ParseFrom(addr.String()); err != nil {
return
}
addrLen, err := socksAddr.Encode(*wbuf)
if err != nil {
return
}
n = copy((*wbuf)[addrLen:], b)
_, err = c.PacketConn.WriteTo((*wbuf)[:addrLen+n], c.raddr)
return
}
func (c *UDPConn) Write(b []byte) (n int, err error) {
return c.WriteTo(b, c.taddr)
}
func (c *UDPConn) RemoteAddr() net.Addr {
return c.raddr
}

60
internal/util/ss/ss.go Normal file
View File

@ -0,0 +1,60 @@
package ss
import (
"bytes"
"net"
"github.com/shadowsocks/go-shadowsocks2/core"
ss "github.com/shadowsocks/shadowsocks-go/shadowsocks"
)
type shadowCipher struct {
cipher *ss.Cipher
}
func (c *shadowCipher) StreamConn(conn net.Conn) net.Conn {
return ss.NewConn(conn, c.cipher.Copy())
}
func (c *shadowCipher) PacketConn(conn net.PacketConn) net.PacketConn {
return ss.NewSecurePacketConn(conn, c.cipher.Copy())
}
func ShadowCipher(method, password string, key string) (core.Cipher, error) {
if method == "" && password == "" {
return nil, nil
}
c, _ := ss.NewCipher(method, password)
if c != nil {
return &shadowCipher{cipher: c}, nil
}
return core.PickCipher(method, []byte(key), password)
}
// Due to in/out byte length is inconsistent of the shadowsocks.Conn.Write,
// we wrap around it to make io.Copy happy.
type shadowConn struct {
net.Conn
wbuf *bytes.Buffer
}
func ShadowConn(conn net.Conn, header []byte) net.Conn {
return &shadowConn{
Conn: conn,
wbuf: bytes.NewBuffer(header),
}
}
func (c *shadowConn) Write(b []byte) (n int, err error) {
n = len(b) // force byte length consistent
if c.wbuf.Len() > 0 {
c.wbuf.Write(b) // append the data to the cached header
_, err = c.Conn.Write(c.wbuf.Bytes())
c.wbuf.Reset()
return
}
_, err = c.Conn.Write(b)
return
}

48
internal/util/ssh/conn.go Normal file
View File

@ -0,0 +1,48 @@
package ssh
import (
"net"
"golang.org/x/crypto/ssh"
)
// a dummy ssh client conn used by client connector
type ClientConn struct {
net.Conn
client *ssh.Client
}
func NewClientConn(conn net.Conn, client *ssh.Client) net.Conn {
return &ClientConn{
Conn: conn,
client: client,
}
}
func (c *ClientConn) Client() *ssh.Client {
return c.client
}
type sshConn struct {
channel ssh.Channel
net.Conn
}
func NewConn(conn net.Conn, channel ssh.Channel) net.Conn {
return &sshConn{
Conn: conn,
channel: channel,
}
}
func (c *sshConn) Read(b []byte) (n int, err error) {
return c.channel.Read(b)
}
func (c *sshConn) Write(b []byte) (n int, err error) {
return c.channel.Write(b)
}
func (c *sshConn) Close() error {
return c.channel.Close()
}

75
internal/util/ssh/ssh.go Normal file
View File

@ -0,0 +1,75 @@
package ssh
import (
"errors"
"fmt"
"io/ioutil"
"github.com/go-gost/gost/v3/pkg/auth"
"golang.org/x/crypto/ssh"
)
const (
GostSSHTunnelRequest = "gost-tunnel" // extended request type for ssh tunnel
)
var (
ErrSessionDead = errors.New("session is dead")
)
// PasswordCallbackFunc is a callback function used by SSH server.
// It authenticates user using a password.
type PasswordCallbackFunc func(conn ssh.ConnMetadata, password []byte) (*ssh.Permissions, error)
func PasswordCallback(au auth.Authenticator) PasswordCallbackFunc {
if au == nil {
return nil
}
return func(conn ssh.ConnMetadata, password []byte) (*ssh.Permissions, error) {
if au.Authenticate(conn.User(), string(password)) {
return nil, nil
}
return nil, fmt.Errorf("password rejected for %s", conn.User())
}
}
// PublicKeyCallbackFunc is a callback function used by SSH server.
// It offers a public key for authentication.
type PublicKeyCallbackFunc func(c ssh.ConnMetadata, pubKey ssh.PublicKey) (*ssh.Permissions, error)
func PublicKeyCallback(keys map[string]bool) PublicKeyCallbackFunc {
if len(keys) == 0 {
return nil
}
return func(c ssh.ConnMetadata, pubKey ssh.PublicKey) (*ssh.Permissions, error) {
if keys[string(pubKey.Marshal())] {
return &ssh.Permissions{
// Record the public key used for authentication.
Extensions: map[string]string{
"pubkey-fp": ssh.FingerprintSHA256(pubKey),
},
}, nil
}
return nil, fmt.Errorf("unknown public key for %q", c.User())
}
}
// ParseSSHAuthorizedKeysFile parses ssh authorized keys file.
func ParseAuthorizedKeysFile(name string) (map[string]bool, error) {
authorizedKeysBytes, err := ioutil.ReadFile(name)
if err != nil {
return nil, err
}
authorizedKeysMap := make(map[string]bool)
for len(authorizedKeysBytes) > 0 {
pubKey, _, _, rest, err := ssh.ParseAuthorizedKey(authorizedKeysBytes)
if err != nil {
return nil, err
}
authorizedKeysMap[string(pubKey.Marshal())] = true
authorizedKeysBytes = rest
}
return authorizedKeysMap, nil
}

118
internal/util/sshd/conn.go Normal file
View File

@ -0,0 +1,118 @@
package sshd
import (
"context"
"errors"
"net"
"time"
"golang.org/x/crypto/ssh"
)
type DirectForwardConn struct {
conn ssh.Conn
channel ssh.Channel
dstAddr string
}
func NewDirectForwardConn(conn ssh.Conn, channel ssh.Channel, dstAddr string) net.Conn {
return &DirectForwardConn{
conn: conn,
channel: channel,
dstAddr: dstAddr,
}
}
func (c *DirectForwardConn) Read(b []byte) (n int, err error) {
return c.channel.Read(b)
}
func (c *DirectForwardConn) Write(b []byte) (n int, err error) {
return c.channel.Write(b)
}
func (c *DirectForwardConn) Close() error {
return c.channel.Close()
}
func (c *DirectForwardConn) LocalAddr() net.Addr {
return c.conn.LocalAddr()
}
func (c *DirectForwardConn) RemoteAddr() net.Addr {
return c.conn.RemoteAddr()
}
func (c *DirectForwardConn) SetDeadline(t time.Time) error {
return &net.OpError{Op: "set", Net: "nop", Source: nil, Addr: nil, Err: errors.New("deadline not supported")}
}
func (c *DirectForwardConn) SetReadDeadline(t time.Time) error {
return &net.OpError{Op: "set", Net: "nop", Source: nil, Addr: nil, Err: errors.New("deadline not supported")}
}
func (c *DirectForwardConn) SetWriteDeadline(t time.Time) error {
return &net.OpError{Op: "set", Net: "nop", Source: nil, Addr: nil, Err: errors.New("deadline not supported")}
}
func (c *DirectForwardConn) DstAddr() string {
return c.dstAddr
}
type RemoteForwardConn struct {
ctx context.Context
conn ssh.Conn
req *ssh.Request
}
func NewRemoteForwardConn(ctx context.Context, conn ssh.Conn, req *ssh.Request) net.Conn {
return &RemoteForwardConn{
ctx: ctx,
conn: conn,
req: req,
}
}
func (c *RemoteForwardConn) Conn() ssh.Conn {
return c.conn
}
func (c *RemoteForwardConn) Request() *ssh.Request {
return c.req
}
func (c *RemoteForwardConn) Read(b []byte) (n int, err error) {
return 0, &net.OpError{Op: "read", Net: "nop", Source: nil, Addr: nil, Err: errors.New("read not supported")}
}
func (c *RemoteForwardConn) Write(b []byte) (n int, err error) {
return 0, &net.OpError{Op: "write", Net: "nop", Source: nil, Addr: nil, Err: errors.New("write not supported")}
}
func (c *RemoteForwardConn) Close() error {
return &net.OpError{Op: "close", Net: "nop", Source: nil, Addr: nil, Err: errors.New("close not supported")}
}
func (c *RemoteForwardConn) LocalAddr() net.Addr {
return c.conn.LocalAddr()
}
func (c *RemoteForwardConn) RemoteAddr() net.Addr {
return c.conn.RemoteAddr()
}
func (c *RemoteForwardConn) SetDeadline(t time.Time) error {
return &net.OpError{Op: "set", Net: "nop", Source: nil, Addr: nil, Err: errors.New("deadline not supported")}
}
func (c *RemoteForwardConn) SetReadDeadline(t time.Time) error {
return &net.OpError{Op: "set", Net: "nop", Source: nil, Addr: nil, Err: errors.New("deadline not supported")}
}
func (c *RemoteForwardConn) SetWriteDeadline(t time.Time) error {
return &net.OpError{Op: "set", Net: "nop", Source: nil, Addr: nil, Err: errors.New("deadline not supported")}
}
func (c *RemoteForwardConn) Done() <-chan struct{} {
return c.ctx.Done()
}

View File

@ -0,0 +1,9 @@
package tap
type Config struct {
Name string
Net string
MTU int
Routes []string
Gateway string
}

61
internal/util/tap/conn.go Normal file
View File

@ -0,0 +1,61 @@
package tap
import (
"errors"
"net"
"time"
"github.com/songgao/water"
)
type Conn struct {
config *Config
ifce *water.Interface
laddr net.Addr
raddr net.Addr
}
func NewConn(config *Config, ifce *water.Interface, laddr, raddr net.Addr) *Conn {
return &Conn{
config: config,
ifce: ifce,
laddr: laddr,
raddr: raddr,
}
}
func (c *Conn) Config() *Config {
return c.config
}
func (c *Conn) Read(b []byte) (n int, err error) {
return c.ifce.Read(b)
}
func (c *Conn) Write(b []byte) (n int, err error) {
return c.ifce.Write(b)
}
func (c *Conn) Close() (err error) {
return c.ifce.Close()
}
func (c *Conn) LocalAddr() net.Addr {
return c.laddr
}
func (c *Conn) RemoteAddr() net.Addr {
return c.raddr
}
func (c *Conn) SetDeadline(t time.Time) error {
return &net.OpError{Op: "set", Net: "tuntap", Source: nil, Addr: nil, Err: errors.New("deadline not supported")}
}
func (c *Conn) SetReadDeadline(t time.Time) error {
return &net.OpError{Op: "set", Net: "tuntap", Source: nil, Addr: nil, Err: errors.New("deadline not supported")}
}
func (c *Conn) SetWriteDeadline(t time.Time) error {
return &net.OpError{Op: "set", Net: "tuntap", Source: nil, Addr: nil, Err: errors.New("deadline not supported")}
}

View File

@ -0,0 +1,19 @@
package tun
import "net"
// Route is an IP routing entry
type Route struct {
Net net.IPNet
Gateway net.IP
}
type Config struct {
Name string
Net string
// peer addr of point-to-point on MacOS
Peer string
MTU int
Gateway string
Routes []Route
}

61
internal/util/tun/conn.go Normal file
View File

@ -0,0 +1,61 @@
package tun
import (
"errors"
"net"
"time"
"github.com/songgao/water"
)
type Conn struct {
config *Config
ifce *water.Interface
laddr net.Addr
raddr net.Addr
}
func NewConn(config *Config, ifce *water.Interface, laddr, raddr net.Addr) *Conn {
return &Conn{
config: config,
ifce: ifce,
laddr: laddr,
raddr: raddr,
}
}
func (c *Conn) Config() *Config {
return c.config
}
func (c *Conn) Read(b []byte) (n int, err error) {
return c.ifce.Read(b)
}
func (c *Conn) Write(b []byte) (n int, err error) {
return c.ifce.Write(b)
}
func (c *Conn) Close() (err error) {
return c.ifce.Close()
}
func (c *Conn) LocalAddr() net.Addr {
return c.laddr
}
func (c *Conn) RemoteAddr() net.Addr {
return c.raddr
}
func (c *Conn) SetDeadline(t time.Time) error {
return &net.OpError{Op: "set", Net: "tuntap", Source: nil, Addr: nil, Err: errors.New("deadline not supported")}
}
func (c *Conn) SetReadDeadline(t time.Time) error {
return &net.OpError{Op: "set", Net: "tuntap", Source: nil, Addr: nil, Err: errors.New("deadline not supported")}
}
func (c *Conn) SetWriteDeadline(t time.Time) error {
return &net.OpError{Op: "set", Net: "tuntap", Source: nil, Addr: nil, Err: errors.New("deadline not supported")}
}

56
internal/util/ws/ws.go Normal file
View File

@ -0,0 +1,56 @@
package ws
import (
"net"
"sync"
"time"
"github.com/gorilla/websocket"
)
type WebsocketConn interface {
net.Conn
WriteMessage(int, []byte) error
ReadMessage() (int, []byte, error)
}
type websocketConn struct {
*websocket.Conn
rb []byte
mux sync.Mutex
}
func Conn(conn *websocket.Conn) WebsocketConn {
return &websocketConn{
Conn: conn,
}
}
func (c *websocketConn) Read(b []byte) (n int, err error) {
if len(c.rb) == 0 {
_, c.rb, err = c.Conn.ReadMessage()
}
n = copy(b, c.rb)
c.rb = c.rb[n:]
return
}
func (c *websocketConn) Write(b []byte) (n int, err error) {
err = c.WriteMessage(websocket.BinaryMessage, b)
n = len(b)
return
}
func (c *websocketConn) WriteMessage(messageType int, data []byte) error {
c.mux.Lock()
defer c.mux.Unlock()
return c.Conn.WriteMessage(messageType, data)
}
func (c *websocketConn) SetDeadline(t time.Time) error {
if err := c.SetReadDeadline(t); err != nil {
return err
}
return c.SetWriteDeadline(t)
}