2023-01-23 18:54:21 +07:00
/ *
* SPDX - License - Identifier : AGPL - 3.0 - only
2023-03-14 14:01:55 +07:00
* Copyright ( c ) 2022 - 2023 , daeuniverse Organization < dae @ v2raya . org >
2023-01-23 18:54:21 +07:00
* /
package outbound
import (
2023-06-29 21:30:33 +07:00
"errors"
2023-01-23 18:54:21 +07:00
"fmt"
2023-04-23 12:27:29 +07:00
"time"
2023-03-14 14:01:55 +07:00
"github.com/daeuniverse/dae/common/consts"
"github.com/daeuniverse/dae/component/outbound/dialer"
2023-03-23 14:34:56 +07:00
"github.com/mzz2017/softwind/netproxy"
"github.com/sirupsen/logrus"
2023-01-23 18:54:21 +07:00
)
2023-02-12 14:39:00 +07:00
var NoAliveDialerError = fmt . Errorf ( "no alive dialer" )
2023-01-23 18:54:21 +07:00
type DialerGroup struct {
2023-02-17 23:49:35 +07:00
netproxy . Dialer
2023-01-23 18:54:21 +07:00
log * logrus . Logger
Name string
Dialers [ ] * dialer . Dialer
2023-02-12 14:39:00 +07:00
aliveDialerSets [ 6 ] * dialer . AliveDialerSet
2023-01-23 18:54:21 +07:00
selectionPolicy * DialerSelectionPolicy
}
2023-03-02 20:28:30 +07:00
func NewDialerGroup ( option * dialer . GlobalOption , name string , dialers [ ] * dialer . Dialer , p DialerSelectionPolicy , aliveChangeCallback func ( alive bool , networkType * dialer . NetworkType , isInit bool ) ) * DialerGroup {
2023-01-28 14:47:43 +07:00
log := option . Log
2023-02-12 14:39:00 +07:00
var aliveDnsTcp4DialerSet * dialer . AliveDialerSet
var aliveDnsTcp6DialerSet * dialer . AliveDialerSet
var aliveTcp4DialerSet * dialer . AliveDialerSet
var aliveTcp6DialerSet * dialer . AliveDialerSet
var aliveDnsUdp4DialerSet * dialer . AliveDialerSet
var aliveDnsUdp6DialerSet * dialer . AliveDialerSet
2023-01-23 18:54:21 +07:00
2023-03-23 14:34:56 +07:00
var needAliveState bool
2023-01-23 18:54:21 +07:00
switch p . Policy {
case consts . DialerSelectionPolicy_Random ,
consts . DialerSelectionPolicy_MinLastLatency ,
2023-02-19 00:49:36 +07:00
consts . DialerSelectionPolicy_MinAverage10Latencies ,
consts . DialerSelectionPolicy_MinMovingAverageLatencies :
2023-01-23 18:54:21 +07:00
// Need to know the alive state or latency.
2023-03-23 14:34:56 +07:00
needAliveState = true
case consts . DialerSelectionPolicy_Fixed :
// No need to know if the dialer is alive.
needAliveState = false
default :
log . Panicf ( "Unexpected dialer selection policy: %v" , p . Policy )
}
networkType := & dialer . NetworkType {
L4Proto : consts . L4ProtoStr_TCP ,
IpVersion : consts . IpVersionStr_4 ,
IsDns : false ,
}
if needAliveState {
2023-02-12 14:39:00 +07:00
aliveTcp4DialerSet = dialer . NewAliveDialerSet (
log , name , networkType , option . CheckTolerance , p . Policy , dialers ,
func ( networkType * dialer . NetworkType ) func ( alive bool ) {
// Use the trick to copy a pointer of *dialer.NetworkType.
2023-03-02 20:28:30 +07:00
return func ( alive bool ) { aliveChangeCallback ( alive , networkType , false ) }
2023-02-12 14:39:00 +07:00
} ( networkType ) , true )
2023-03-23 14:34:56 +07:00
}
aliveChangeCallback ( true , networkType , true )
2023-02-12 14:39:00 +07:00
2023-03-23 14:34:56 +07:00
networkType = & dialer . NetworkType {
L4Proto : consts . L4ProtoStr_TCP ,
IpVersion : consts . IpVersionStr_6 ,
IsDns : false ,
}
if needAliveState {
2023-02-12 14:39:00 +07:00
aliveTcp6DialerSet = dialer . NewAliveDialerSet (
log , name , networkType , option . CheckTolerance , p . Policy , dialers ,
func ( networkType * dialer . NetworkType ) func ( alive bool ) {
// Use the trick to copy a pointer of *dialer.NetworkType.
2023-03-02 20:28:30 +07:00
return func ( alive bool ) { aliveChangeCallback ( alive , networkType , false ) }
2023-02-12 14:39:00 +07:00
} ( networkType ) , true )
2023-03-23 14:34:56 +07:00
}
aliveChangeCallback ( true , networkType , true )
2023-02-12 14:39:00 +07:00
2023-03-23 14:34:56 +07:00
networkType = & dialer . NetworkType {
L4Proto : consts . L4ProtoStr_UDP ,
IpVersion : consts . IpVersionStr_4 ,
IsDns : true ,
}
if needAliveState {
2023-02-12 14:39:00 +07:00
aliveDnsUdp4DialerSet = dialer . NewAliveDialerSet (
log , name , networkType , option . CheckTolerance , p . Policy , dialers ,
func ( networkType * dialer . NetworkType ) func ( alive bool ) {
// Use the trick to copy a pointer of *dialer.NetworkType.
2023-03-02 20:28:30 +07:00
return func ( alive bool ) { aliveChangeCallback ( alive , networkType , false ) }
2023-02-12 14:39:00 +07:00
} ( networkType ) , true )
2023-03-23 14:34:56 +07:00
}
aliveChangeCallback ( true , networkType , true )
2023-02-12 14:39:00 +07:00
2023-03-23 14:34:56 +07:00
networkType = & dialer . NetworkType {
L4Proto : consts . L4ProtoStr_UDP ,
IpVersion : consts . IpVersionStr_6 ,
IsDns : true ,
}
if needAliveState {
2023-02-12 14:39:00 +07:00
aliveDnsUdp6DialerSet = dialer . NewAliveDialerSet (
log , name , networkType , option . CheckTolerance , p . Policy , dialers ,
func ( networkType * dialer . NetworkType ) func ( alive bool ) {
// Use the trick to copy a pointer of *dialer.NetworkType.
2023-03-02 20:28:30 +07:00
return func ( alive bool ) { aliveChangeCallback ( alive , networkType , false ) }
2023-02-12 14:39:00 +07:00
} ( networkType ) , true )
2023-03-23 14:34:56 +07:00
}
aliveChangeCallback ( true , networkType , true )
2023-01-23 18:54:21 +07:00
2023-03-23 14:34:56 +07:00
if option . CheckDnsTcp && needAliveState {
aliveDnsTcp4DialerSet = dialer . NewAliveDialerSet ( log , name , & dialer . NetworkType {
L4Proto : consts . L4ProtoStr_TCP ,
IpVersion : consts . IpVersionStr_4 ,
IsDns : true ,
} , option . CheckTolerance , p . Policy , dialers , func ( alive bool ) { } , true )
2023-01-23 18:54:21 +07:00
2023-03-23 14:34:56 +07:00
aliveDnsTcp6DialerSet = dialer . NewAliveDialerSet ( log , name , & dialer . NetworkType {
L4Proto : consts . L4ProtoStr_TCP ,
IpVersion : consts . IpVersionStr_6 ,
IsDns : true ,
} , option . CheckTolerance , p . Policy , dialers , func ( alive bool ) { } , true )
2023-01-23 18:54:21 +07:00
}
2023-02-12 14:39:00 +07:00
for _ , d := range dialers {
d . RegisterAliveDialerSet ( aliveTcp4DialerSet )
d . RegisterAliveDialerSet ( aliveTcp6DialerSet )
d . RegisterAliveDialerSet ( aliveDnsTcp4DialerSet )
d . RegisterAliveDialerSet ( aliveDnsTcp6DialerSet )
d . RegisterAliveDialerSet ( aliveDnsUdp4DialerSet )
d . RegisterAliveDialerSet ( aliveDnsUdp6DialerSet )
}
2023-01-23 18:54:21 +07:00
return & DialerGroup {
2023-02-09 12:27:59 +07:00
log : log ,
Name : name ,
Dialers : dialers ,
2023-02-12 14:39:00 +07:00
aliveDialerSets : [ 6 ] * dialer . AliveDialerSet {
aliveDnsTcp4DialerSet ,
aliveDnsTcp6DialerSet ,
aliveDnsUdp4DialerSet ,
aliveDnsUdp6DialerSet ,
aliveTcp4DialerSet ,
aliveTcp6DialerSet ,
} ,
selectionPolicy : & p ,
2023-01-23 18:54:21 +07:00
}
}
func ( g * DialerGroup ) Close ( ) error {
2023-02-12 14:39:00 +07:00
for _ , d := range g . Dialers {
for _ , a := range g . aliveDialerSets {
d . UnregisterAliveDialerSet ( a )
2023-01-23 18:54:21 +07:00
}
}
return nil
}
func ( g * DialerGroup ) SetSelectionPolicy ( policy DialerSelectionPolicy ) {
// TODO:
g . selectionPolicy = & policy
}
2023-02-13 13:59:18 +07:00
func ( g * DialerGroup ) GetSelectionPolicy ( ) ( policy consts . DialerSelectionPolicy ) {
return g . selectionPolicy . Policy
}
2023-02-12 14:39:00 +07:00
func ( d * DialerGroup ) MustGetAliveDialerSet ( typ * dialer . NetworkType ) * dialer . AliveDialerSet {
if typ . IsDns {
switch typ . L4Proto {
case consts . L4ProtoStr_TCP :
switch typ . IpVersion {
case consts . IpVersionStr_4 :
return d . aliveDialerSets [ 0 ]
case consts . IpVersionStr_6 :
return d . aliveDialerSets [ 1 ]
}
case consts . L4ProtoStr_UDP :
switch typ . IpVersion {
case consts . IpVersionStr_4 :
return d . aliveDialerSets [ 2 ]
case consts . IpVersionStr_6 :
return d . aliveDialerSets [ 3 ]
}
}
} else {
switch typ . L4Proto {
case consts . L4ProtoStr_TCP :
switch typ . IpVersion {
case consts . IpVersionStr_4 :
return d . aliveDialerSets [ 4 ]
case consts . IpVersionStr_6 :
return d . aliveDialerSets [ 5 ]
}
case consts . L4ProtoStr_UDP :
}
}
panic ( "invalid param" )
}
2023-06-29 21:30:33 +07:00
// Select selects a dialer from group according to selectionPolicy. If 'strictIpVersion' is false and no alive dialer, it will fallback to another ipversion.
func ( g * DialerGroup ) Select ( networkType * dialer . NetworkType , strictIpVersion bool ) ( d * dialer . Dialer , latency time . Duration , err error ) {
d , latency , err = g . _select ( networkType )
if ! strictIpVersion && errors . Is ( err , NoAliveDialerError ) {
networkType . IpVersion = ( consts . IpVersion_X - networkType . IpVersion . ToIpVersionType ( ) ) . ToIpVersionStr ( )
return g . _select ( networkType )
}
return d , latency , err
}
func ( g * DialerGroup ) _select ( networkType * dialer . NetworkType ) ( d * dialer . Dialer , latency time . Duration , err error ) {
2023-01-23 18:54:21 +07:00
if len ( g . Dialers ) == 0 {
2023-02-09 10:40:34 +07:00
return nil , 0 , fmt . Errorf ( "no dialer in this group" )
2023-01-23 18:54:21 +07:00
}
2023-02-12 14:39:00 +07:00
a := g . MustGetAliveDialerSet ( networkType )
2023-01-23 18:54:21 +07:00
switch g . selectionPolicy . Policy {
case consts . DialerSelectionPolicy_Random :
2023-02-08 19:15:24 +07:00
d := a . GetRand ( )
2023-01-23 18:54:21 +07:00
if d == nil {
// No alive dialer.
2023-02-12 14:39:00 +07:00
return nil , time . Hour , NoAliveDialerError
2023-01-23 18:54:21 +07:00
}
2023-02-09 10:40:34 +07:00
return d , 0 , nil
2023-01-23 18:54:21 +07:00
case consts . DialerSelectionPolicy_Fixed :
if g . selectionPolicy . FixedIndex < 0 || g . selectionPolicy . FixedIndex >= len ( g . Dialers ) {
2023-02-09 10:40:34 +07:00
return nil , 0 , fmt . Errorf ( "selected dialer index is out of range" )
2023-01-23 18:54:21 +07:00
}
2023-02-09 10:40:34 +07:00
return g . Dialers [ g . selectionPolicy . FixedIndex ] , 0 , nil
2023-01-23 18:54:21 +07:00
2023-02-19 00:49:36 +07:00
case consts . DialerSelectionPolicy_MinLastLatency ,
consts . DialerSelectionPolicy_MinAverage10Latencies ,
consts . DialerSelectionPolicy_MinMovingAverageLatencies :
2023-02-09 10:40:34 +07:00
d , latency := a . GetMinLatency ( )
2023-01-23 18:54:21 +07:00
if d == nil {
// No alive dialer.
2023-02-12 14:39:00 +07:00
return nil , time . Hour , NoAliveDialerError
2023-01-23 18:54:21 +07:00
}
2023-02-09 10:40:34 +07:00
return d , latency , nil
2023-01-23 18:54:21 +07:00
default :
2023-02-09 10:40:34 +07:00
return nil , 0 , fmt . Errorf ( "unsupported DialerSelectionPolicy: %v" , g . selectionPolicy )
2023-01-23 18:54:21 +07:00
}
}