feat: implement port hopping (#1064)

* implement port hopping using sing and sing-quic

* 更新quic-go

* 更新sing

* Update go.sum

---------

Co-authored-by: wwqgtxx <wwqgtxx@gmail.com>
This commit is contained in:
keakon
2024-03-08 22:38:41 +08:00
committed by wwqgtxx
parent 7754b46dc4
commit feedc9ec66
3 changed files with 69 additions and 6 deletions

View File

@@ -5,15 +5,19 @@ import (
"crypto/tls"
"errors"
"fmt"
"math/rand"
"net"
"runtime"
"strconv"
"strings"
"time"
CN "github.com/metacubex/mihomo/common/net"
"github.com/metacubex/mihomo/component/ca"
"github.com/metacubex/mihomo/component/dialer"
"github.com/metacubex/mihomo/component/proxydialer"
C "github.com/metacubex/mihomo/constant"
"github.com/metacubex/mihomo/log"
tuicCommon "github.com/metacubex/mihomo/transport/tuic/common"
"github.com/metacubex/sing-quic/hysteria2"
@@ -25,6 +29,9 @@ func init() {
hysteria2.SetCongestionController = tuicCommon.SetCongestionController
}
const minHopInterval = 5
const defaultHopInterval = 30
type Hysteria2 struct {
*Base
@@ -38,6 +45,8 @@ type Hysteria2Option struct {
Name string `proxy:"name"`
Server string `proxy:"server"`
Port int `proxy:"port"`
Ports string `proxy:"ports,omitempty"`
HopInterval int `proxy:"hop-interval,omitempty"`
Up string `proxy:"up,omitempty"`
Down string `proxy:"down,omitempty"`
Password string `proxy:"password,omitempty"`
@@ -82,6 +91,41 @@ func closeHysteria2(h *Hysteria2) {
}
}
func parsePorts(portStr string) (ports []uint16) {
portStrs := strings.Split(portStr, ",")
for _, portStr := range portStrs {
if strings.Contains(portStr, "-") {
// Port range
portRange := strings.Split(portStr, "-")
if len(portRange) != 2 {
return nil
}
start, err := strconv.ParseUint(portRange[0], 10, 16)
if err != nil {
return nil
}
end, err := strconv.ParseUint(portRange[1], 10, 16)
if err != nil {
return nil
}
if start > end {
start, end = end, start
}
for i := start; i <= end; i++ {
ports = append(ports, uint16(i))
}
} else {
// Single port
port, err := strconv.ParseUint(portStr, 10, 16)
if err != nil {
return nil
}
ports = append(ports, uint16(port))
}
}
return ports
}
func NewHysteria2(option Hysteria2Option) (*Hysteria2, error) {
addr := net.JoinHostPort(option.Server, strconv.Itoa(option.Port))
var salamanderPassword string
@@ -129,6 +173,7 @@ func NewHysteria2(option Hysteria2Option) (*Hysteria2, error) {
clientOptions := hysteria2.ClientOptions{
Context: context.TODO(),
Dialer: singDialer,
Logger: log.SingLogger,
ServerAddress: M.ParseSocksaddrHostPort(option.Server, uint16(option.Port)),
SendBPS: StringToBps(option.Up),
ReceiveBPS: StringToBps(option.Down),
@@ -140,6 +185,23 @@ func NewHysteria2(option Hysteria2Option) (*Hysteria2, error) {
UdpMTU: option.UdpMTU,
}
if option.Ports != "" {
ports := parsePorts(option.Ports)
if len(ports) > 0 {
for _, port := range ports {
clientOptions.ServerAddresses = append(clientOptions.ServerAddresses, M.ParseSocksaddrHostPort(option.Server, port))
}
clientOptions.ServerAddress = clientOptions.ServerAddresses[rand.Intn(len(clientOptions.ServerAddresses))]
if option.HopInterval == 0 {
option.HopInterval = defaultHopInterval
} else if option.HopInterval < minHopInterval {
option.HopInterval = minHopInterval
}
clientOptions.HopInterval = time.Duration(option.HopInterval) * time.Second
}
}
client, err := hysteria2.NewClient(clientOptions)
if err != nil {
return nil, err