Skip to content

Commit

Permalink
Merge branch 'MetaCubeX:Alpha' into Alpha
Browse files Browse the repository at this point in the history
  • Loading branch information
xishang0128 authored Mar 13, 2024
2 parents b2d2d3e + b3db113 commit 24754a9
Show file tree
Hide file tree
Showing 8 changed files with 166 additions and 135 deletions.
59 changes: 16 additions & 43 deletions adapter/outbound/hysteria2.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,10 +8,10 @@ import (
"net"
"runtime"
"strconv"
"strings"
"time"

CN "github.com/metacubex/mihomo/common/net"
"github.com/metacubex/mihomo/common/utils"
"github.com/metacubex/mihomo/component/ca"
"github.com/metacubex/mihomo/component/dialer"
"github.com/metacubex/mihomo/component/proxydialer"
Expand Down Expand Up @@ -44,7 +44,7 @@ type Hysteria2Option struct {
BasicOption
Name string `proxy:"name"`
Server string `proxy:"server"`
Port int `proxy:"port"`
Port int `proxy:"port,omitempty"`
Ports string `proxy:"ports,omitempty"`
HopInterval int `proxy:"hop-interval,omitempty"`
Up string `proxy:"up,omitempty"`
Expand Down Expand Up @@ -91,41 +91,6 @@ 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
Expand Down Expand Up @@ -187,13 +152,18 @@ func NewHysteria2(option Hysteria2Option) (*Hysteria2, error) {
},
}

var ranges utils.IntRanges[uint16]
var serverAddress []string
if option.Ports != "" {
ports := parsePorts(option.Ports)
if len(ports) > 0 {
serverAddress := make([]string, len(ports))
for i, port := range ports {
serverAddress[i] = net.JoinHostPort(option.Server, strconv.Itoa(int(port)))
}
ranges, err = utils.NewUnsignedRanges[uint16](option.Ports)
if err != nil {
return nil, err
}
ranges.Range(func(port uint16) bool {
serverAddress = append(serverAddress, net.JoinHostPort(option.Server, strconv.Itoa(int(port))))
return true
})
if len(serverAddress) > 0 {
clientOptions.ServerAddress = func(ctx context.Context) (*net.UDPAddr, error) {
return resolveUDPAddrWithPrefer(ctx, "udp", serverAddress[fastrand.Intn(len(serverAddress))], C.NewDNSPrefer(option.IPVersion))
}
Expand All @@ -206,6 +176,9 @@ func NewHysteria2(option Hysteria2Option) (*Hysteria2, error) {
clientOptions.HopInterval = time.Duration(option.HopInterval) * time.Second
}
}
if option.Port == 0 && len(serverAddress) == 0 {
return nil, errors.New("invalid port")
}

client, err := hysteria2.NewClient(clientOptions)
if err != nil {
Expand Down
75 changes: 55 additions & 20 deletions adapter/outbound/ssh.go
Original file line number Diff line number Diff line change
@@ -1,11 +1,15 @@
package outbound

import (
"bytes"
"context"
"encoding/base64"
"fmt"
"net"
"os"
"runtime"
"strconv"
"strings"
"sync"

N "github.com/metacubex/mihomo/common/net"
Expand All @@ -26,12 +30,15 @@ type Ssh struct {

type SshOption struct {
BasicOption
Name string `proxy:"name"`
Server string `proxy:"server"`
Port int `proxy:"port"`
UserName string `proxy:"username"`
Password string `proxy:"password,omitempty"`
PrivateKey string `proxy:"privateKey,omitempty"`
Name string `proxy:"name"`
Server string `proxy:"server"`
Port int `proxy:"port"`
UserName string `proxy:"username"`
Password string `proxy:"password,omitempty"`
PrivateKey string `proxy:"private-key,omitempty"`
PrivateKeyPassphrase string `proxy:"private-key-passphrase,omitempty"`
HostKey []string `proxy:"host-key,omitempty"`
HostKeyAlgorithms []string `proxy:"host-key-algorithms,omitempty"`
}

func (s *Ssh) DialContext(ctx context.Context, metadata *C.Metadata, opts ...dialer.Option) (_ C.Conn, err error) {
Expand Down Expand Up @@ -119,28 +126,56 @@ func NewSsh(option SshOption) (*Ssh, error) {
addr := net.JoinHostPort(option.Server, strconv.Itoa(option.Port))

config := ssh.ClientConfig{
User: option.UserName,
HostKeyCallback: func(hostname string, remote net.Addr, key ssh.PublicKey) error {
return nil
},
User: option.UserName,
HostKeyCallback: ssh.InsecureIgnoreHostKey(),
HostKeyAlgorithms: option.HostKeyAlgorithms,
}

if option.Password == "" {
b, err := os.ReadFile(option.PrivateKey)
if err != nil {
return nil, err
if option.PrivateKey != "" {
var b []byte
var err error
if strings.Contains(option.PrivateKey, "PRIVATE KEY") {
b = []byte(option.PrivateKey)
} else {
b, err = os.ReadFile(C.Path.Resolve(option.PrivateKey))
if err != nil {
return nil, err
}
}
var pKey ssh.Signer
if option.PrivateKeyPassphrase != "" {
pKey, err = ssh.ParsePrivateKeyWithPassphrase(b, []byte(option.PrivateKeyPassphrase))
} else {
pKey, err = ssh.ParsePrivateKey(b)
}
pKey, err := ssh.ParsePrivateKey(b)
if err != nil {
return nil, err
}

config.Auth = []ssh.AuthMethod{
ssh.PublicKeys(pKey),
config.Auth = append(config.Auth, ssh.PublicKeys(pKey))
}

if option.Password != "" {
config.Auth = append(config.Auth, ssh.Password(option.Password))
}

if len(option.HostKey) != 0 {
keys := make([]ssh.PublicKey, len(option.HostKey))
for i, hostKey := range option.HostKey {
key, _, _, _, err := ssh.ParseAuthorizedKey([]byte(hostKey))
if err != nil {
return nil, fmt.Errorf("parse host key :%s", key)
}
keys[i] = key
}
} else {
config.Auth = []ssh.AuthMethod{
ssh.Password(option.Password),
config.HostKeyCallback = func(hostname string, remote net.Addr, key ssh.PublicKey) error {
serverKey := key.Marshal()
for _, hostKey := range keys {
if bytes.Equal(serverKey, hostKey.Marshal()) {
return nil
}
}
return fmt.Errorf("host key mismatch, server send :%s %s", key.Type(), base64.StdEncoding.EncodeToString(serverKey))
}
}

Expand Down
14 changes: 2 additions & 12 deletions adapter/provider/provider.go
Original file line number Diff line number Diff line change
Expand Up @@ -46,18 +46,13 @@ type proxySetProvider struct {
}

func (pp *proxySetProvider) MarshalJSON() ([]byte, error) {
expectedStatus := "*"
if pp.healthCheck.expectedStatus != nil {
expectedStatus = pp.healthCheck.expectedStatus.ToString()
}

return json.Marshal(map[string]any{
"name": pp.Name(),
"type": pp.Type().String(),
"vehicleType": pp.VehicleType().String(),
"proxies": pp.Proxies(),
"testUrl": pp.healthCheck.url,
"expectedStatus": expectedStatus,
"expectedStatus": pp.healthCheck.expectedStatus.String(),
"updatedAt": pp.UpdatedAt,
"subscriptionInfo": pp.subscriptionInfo,
})
Expand Down Expand Up @@ -221,18 +216,13 @@ type compatibleProvider struct {
}

func (cp *compatibleProvider) MarshalJSON() ([]byte, error) {
expectedStatus := "*"
if cp.healthCheck.expectedStatus != nil {
expectedStatus = cp.healthCheck.expectedStatus.ToString()
}

return json.Marshal(map[string]any{
"name": cp.Name(),
"type": cp.Type().String(),
"vehicleType": cp.VehicleType().String(),
"proxies": cp.Proxies(),
"testUrl": cp.healthCheck.url,
"expectedStatus": expectedStatus,
"expectedStatus": cp.healthCheck.expectedStatus.String(),
})
}

Expand Down
19 changes: 15 additions & 4 deletions common/atomic/value.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,28 +15,39 @@ type TypedValue[T any] struct {
value atomic.Value
}

// tValue is a struct with determined type to resolve atomic.Value usages with interface types
// https://github.com/golang/go/issues/22550
//
// The intention to have an atomic value store for errors. However, running this code panics:
// panic: sync/atomic: store of inconsistently typed value into Value
// This is because atomic.Value requires that the underlying concrete type be the same (which is a reasonable expectation for its implementation).
// When going through the atomic.Value.Store method call, the fact that both these are of the error interface is lost.
type tValue[T any] struct {
value T
}

func (t *TypedValue[T]) Load() T {
value := t.value.Load()
if value == nil {
return DefaultValue[T]()
}
return value.(T)
return value.(tValue[T]).value
}

func (t *TypedValue[T]) Store(value T) {
t.value.Store(value)
t.value.Store(tValue[T]{value})
}

func (t *TypedValue[T]) Swap(new T) T {
old := t.value.Swap(new)
old := t.value.Swap(tValue[T]{new})
if old == nil {
return DefaultValue[T]()
}
return old.(T)
}

func (t *TypedValue[T]) CompareAndSwap(old, new T) bool {
return t.value.CompareAndSwap(old, new)
return t.value.CompareAndSwap(tValue[T]{old}, tValue[T]{new})
}

func (t *TypedValue[T]) MarshalJSON() ([]byte, error) {
Expand Down
22 changes: 19 additions & 3 deletions common/utils/ranges.go
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,8 @@ func newIntRanges[T constraints.Integer](expected string, parseFn func(string) (
return nil, nil
}

// support: 200,302 or 200,204,401-429,501-503
expected = strings.ReplaceAll(expected, ",", "/")
list := strings.Split(expected, "/")
if len(list) > 28 {
return nil, fmt.Errorf("%w, too many ranges to use, maximum support 28 ranges", errIntRanges)
Expand Down Expand Up @@ -47,9 +49,9 @@ func newIntRangesFromList[T constraints.Integer](list []string, parseFn func(str
}

switch statusLen {
case 1:
case 1: // Port range
ranges = append(ranges, NewRange(T(start), T(start)))
case 2:
case 2: // Single port
end, err := parseFn(strings.Trim(status[1], "[ ]"))
if err != nil {
return nil, errIntRanges
Expand Down Expand Up @@ -108,7 +110,7 @@ func (ranges IntRanges[T]) Check(status T) bool {
return false
}

func (ranges IntRanges[T]) ToString() string {
func (ranges IntRanges[T]) String() string {
if len(ranges) == 0 {
return "*"
}
Expand All @@ -130,3 +132,17 @@ func (ranges IntRanges[T]) ToString() string {

return strings.Join(terms, "/")
}

func (ranges IntRanges[T]) Range(f func(t T) bool) {
if len(ranges) == 0 {
return
}

for _, r := range ranges {
for i := r.Start(); i <= r.End(); i++ {
if !f(i) {
return
}
}
}
}
6 changes: 5 additions & 1 deletion component/resolver/host.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@ package resolver
import (
"errors"
"net/netip"
"os"
"strconv"
"strings"
_ "unsafe"

Expand All @@ -11,6 +13,8 @@ import (
"github.com/zhangyunhao116/fastrand"
)

var DisableSystemHosts, _ = strconv.ParseBool(os.Getenv("DISABLE_SYSTEM_HOSTS"))

type Hosts struct {
*trie.DomainTrie[HostValue]
}
Expand Down Expand Up @@ -47,7 +51,7 @@ func (h *Hosts) Search(domain string, isDomain bool) (*HostValue, bool) {

return &hostValue, false
}
if !isDomain {
if !isDomain && !DisableSystemHosts {
addr, _ := lookupStaticHost(domain)
if hostValue, err := NewHostValue(addr); err == nil {
return &hostValue, true
Expand Down
Loading

0 comments on commit 24754a9

Please sign in to comment.