|
|
|
@@ -38,6 +38,14 @@ var defaultBlockAllRule *FinalRule
|
|
|
|
|
func init() {
|
|
|
|
|
common.Must(common.RegisterConfig((*Config)(nil), func(ctx context.Context, config interface{}) (interface{}, error) {
|
|
|
|
|
h := new(Handler)
|
|
|
|
|
if handler, ok := session.FullHandlerFromContext(ctx).(handlerWithSocketSettings); ok {
|
|
|
|
|
if sockopt := handler.SocketSettings(); sockopt != nil {
|
|
|
|
|
h.socketStrategy = sockopt.DomainStrategy
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
if handler, ok := session.FullHandlerFromContext(ctx).(handlerWithProxySettings); ok {
|
|
|
|
|
h.usesProxySettings = handler.UsesProxySettings()
|
|
|
|
|
}
|
|
|
|
|
if err := core.RequireFeatures(ctx, func(pm policy.Manager) error {
|
|
|
|
|
return h.Init(config.(*Config), pm)
|
|
|
|
|
}); err != nil {
|
|
|
|
@@ -88,6 +96,14 @@ func init() {
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
type handlerWithSocketSettings interface {
|
|
|
|
|
SocketSettings() *internet.SocketConfig
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
type handlerWithProxySettings interface {
|
|
|
|
|
UsesProxySettings() bool
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
type FinalRule struct {
|
|
|
|
|
action RuleAction
|
|
|
|
|
network [8]bool
|
|
|
|
@@ -98,9 +114,11 @@ type FinalRule struct {
|
|
|
|
|
|
|
|
|
|
// Handler handles Freedom connections.
|
|
|
|
|
type Handler struct {
|
|
|
|
|
policyManager policy.Manager
|
|
|
|
|
config *Config
|
|
|
|
|
finalRules []*FinalRule
|
|
|
|
|
policyManager policy.Manager
|
|
|
|
|
config *Config
|
|
|
|
|
finalRules []*FinalRule
|
|
|
|
|
socketStrategy internet.DomainStrategy
|
|
|
|
|
usesProxySettings bool
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func buildFinalRule(config *FinalRuleConfig) (*FinalRule, error) {
|
|
|
|
@@ -177,22 +195,6 @@ func getDefaultFinalRule(inbound *session.Inbound) *FinalRule {
|
|
|
|
|
return nil
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func (h *Handler) shouldResolveDomainBeforeFinalRules(dialDest net.Destination, defaultRule *FinalRule) bool {
|
|
|
|
|
if !dialDest.Address.Family().IsDomain() {
|
|
|
|
|
return false
|
|
|
|
|
}
|
|
|
|
|
if len(h.finalRules) > 0 {
|
|
|
|
|
rule := h.finalRules[0]
|
|
|
|
|
if rule.action == RuleAction_Allow && rule.network[dialDest.Network] && len(rule.port) == 0 && rule.ip == nil {
|
|
|
|
|
return false
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
if defaultRule != nil || len(h.finalRules) > 0 {
|
|
|
|
|
return true
|
|
|
|
|
}
|
|
|
|
|
return false
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func (h *Handler) matchFinalRule(network net.Network, address net.Address, port net.Port, defaultRule *FinalRule) *FinalRule {
|
|
|
|
|
for _, rule := range h.finalRules {
|
|
|
|
|
if rule.Apply(network, address, port) {
|
|
|
|
@@ -205,13 +207,6 @@ func (h *Handler) matchFinalRule(network net.Network, address net.Address, port
|
|
|
|
|
return nil
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func (h *Handler) applyFinalRules(network net.Network, address net.Address, port net.Port, defaultRule *FinalRule) RuleAction {
|
|
|
|
|
if rule := h.matchFinalRule(network, address, port, defaultRule); rule != nil {
|
|
|
|
|
return rule.action
|
|
|
|
|
}
|
|
|
|
|
return RuleAction_Allow
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Init initializes the Handler with necessary parameters.
|
|
|
|
|
func (h *Handler) Init(config *Config, pm policy.Manager) error {
|
|
|
|
|
h.config = config
|
|
|
|
@@ -239,11 +234,32 @@ func (h *Handler) blockDelay(rule *FinalRule) time.Duration {
|
|
|
|
|
min = rule.blockDelay.Min
|
|
|
|
|
max = rule.blockDelay.Max
|
|
|
|
|
}
|
|
|
|
|
abs := max - min
|
|
|
|
|
span := max - min
|
|
|
|
|
if max < min {
|
|
|
|
|
abs = min - max
|
|
|
|
|
span = min - max
|
|
|
|
|
}
|
|
|
|
|
return time.Duration(min+uint64(dice.Roll(int(abs+1)))) * time.Second
|
|
|
|
|
return time.Duration(min+uint64(dice.Roll(int(span+1)))) * time.Second
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func (h *Handler) blackhole(ctx context.Context, input buf.Reader, output buf.Writer, rule *FinalRule, dest *net.Destination) error {
|
|
|
|
|
delay := h.blockDelay(rule)
|
|
|
|
|
errors.LogInfo(ctx, "blocked target: ", *dest, ", blackholing connection for ", delay)
|
|
|
|
|
timer := time.AfterFunc(delay, func() {
|
|
|
|
|
common.Interrupt(input)
|
|
|
|
|
common.Interrupt(output)
|
|
|
|
|
errors.LogInfo(ctx, "closed blackholed connection to blocked target: ", *dest)
|
|
|
|
|
})
|
|
|
|
|
defer timer.Stop()
|
|
|
|
|
defer common.Close(output)
|
|
|
|
|
_ = buf.Copy(input, buf.Discard)
|
|
|
|
|
return nil
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func (h *Handler) udpDomainStrategy() internet.DomainStrategy {
|
|
|
|
|
if h.config.DomainStrategy.HasStrategy() {
|
|
|
|
|
return h.config.DomainStrategy
|
|
|
|
|
}
|
|
|
|
|
return h.socketStrategy
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func isValidAddress(addr *net.IPOrDomain) bool {
|
|
|
|
@@ -295,40 +311,73 @@ func (h *Handler) Process(ctx context.Context, link *transport.Link, dialer inte
|
|
|
|
|
var blockedRule *FinalRule
|
|
|
|
|
err := retry.ExponentialBackoff(5, 100).On(func() error {
|
|
|
|
|
dialDest := destination
|
|
|
|
|
if h.config.DomainStrategy.HasStrategy() && dialDest.Address.Family().IsDomain() {
|
|
|
|
|
strategy := h.config.DomainStrategy
|
|
|
|
|
if destination.Network == net.Network_UDP && origTargetAddr != nil && outGateway == nil {
|
|
|
|
|
strategy = strategy.GetDynamicStrategy(origTargetAddr.Family())
|
|
|
|
|
}
|
|
|
|
|
ips, err := internet.LookupForIP(dialDest.Address.Domain(), strategy, outGateway)
|
|
|
|
|
if err != nil {
|
|
|
|
|
errors.LogInfoInner(ctx, err, "failed to get IP address for domain ", dialDest.Address.Domain())
|
|
|
|
|
if h.config.DomainStrategy.ForceIP() {
|
|
|
|
|
return err
|
|
|
|
|
|
|
|
|
|
if dialDest.Address.Family().IsDomain() {
|
|
|
|
|
if strategy := h.config.DomainStrategy; strategy.HasStrategy() {
|
|
|
|
|
if destination.Network == net.Network_UDP && origTargetAddr != nil && outGateway == nil {
|
|
|
|
|
strategy = strategy.GetDynamicStrategy(origTargetAddr.Family())
|
|
|
|
|
}
|
|
|
|
|
} else {
|
|
|
|
|
dialDest = net.Destination{
|
|
|
|
|
Network: dialDest.Network,
|
|
|
|
|
Address: net.IPAddress(ips[dice.Roll(len(ips))]),
|
|
|
|
|
Port: dialDest.Port,
|
|
|
|
|
}
|
|
|
|
|
errors.LogInfo(ctx, "dialing to ", dialDest)
|
|
|
|
|
}
|
|
|
|
|
} else if h.shouldResolveDomainBeforeFinalRules(dialDest, defaultRule) { // asis + domain + hasrules
|
|
|
|
|
addrs, err := net.DefaultResolver.LookupIPAddr(ctx, dialDest.Address.Domain())
|
|
|
|
|
if err != nil {
|
|
|
|
|
errors.LogInfoInner(ctx, err, "failed to get IP address for domain ", dialDest.Address.Domain())
|
|
|
|
|
} else if len(addrs) > 0 {
|
|
|
|
|
if addr := net.IPAddress(addrs[dice.Roll(len(addrs))].IP); addr != nil {
|
|
|
|
|
dialDest.Address = addr
|
|
|
|
|
ips, err := internet.LookupForIP(dialDest.Address.Domain(), strategy, outGateway)
|
|
|
|
|
if err != nil { // SRV/TXT
|
|
|
|
|
errors.LogInfoInner(ctx, err, "failed to get IP address for domain ", dialDest.Address.Domain())
|
|
|
|
|
if h.config.DomainStrategy.ForceIP() || defaultRule != nil || len(h.finalRules) > 0 {
|
|
|
|
|
return err // retry
|
|
|
|
|
}
|
|
|
|
|
} else { // to ip
|
|
|
|
|
dialDest = net.Destination{
|
|
|
|
|
Network: dialDest.Network,
|
|
|
|
|
Address: net.IPAddress(ips[dice.Roll(len(ips))]),
|
|
|
|
|
Port: dialDest.Port,
|
|
|
|
|
}
|
|
|
|
|
errors.LogInfo(ctx, "dialing to ", dialDest)
|
|
|
|
|
if rule := h.matchFinalRule(dialDest.Network, dialDest.Address, dialDest.Port, defaultRule); rule != nil && rule.action == RuleAction_Block {
|
|
|
|
|
blockedDest = &dialDest
|
|
|
|
|
blockedRule = rule
|
|
|
|
|
return nil
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
} else if defaultRule != nil || len(h.finalRules) > 0 { // freedom asis + hasrules
|
|
|
|
|
if strategy := h.socketStrategy; strategy.HasStrategy() {
|
|
|
|
|
ips, err := internet.LookupForIP(dialDest.Address.Domain(), strategy, outGateway)
|
|
|
|
|
if err != nil { // SRV/TXT
|
|
|
|
|
errors.LogInfoInner(ctx, err, "failed to get IP address for domain ", dialDest.Address.Domain())
|
|
|
|
|
if strategy.ForceIP() {
|
|
|
|
|
return err // retry
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
for _, ip := range ips {
|
|
|
|
|
if addr := net.IPAddress(ip); addr != nil {
|
|
|
|
|
if rule := h.matchFinalRule(dialDest.Network, addr, dialDest.Port, defaultRule); rule != nil && rule.action == RuleAction_Block {
|
|
|
|
|
blockedDest = &dialDest
|
|
|
|
|
blockedDest.Address = addr
|
|
|
|
|
blockedRule = rule
|
|
|
|
|
return nil
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
} else { // sockopt asis
|
|
|
|
|
addrs, err := net.DefaultResolver.LookupIPAddr(ctx, dialDest.Address.Domain())
|
|
|
|
|
if err != nil { // SRV/TXT
|
|
|
|
|
errors.LogInfoInner(ctx, err, "failed to get IP address for domain ", dialDest.Address.Domain())
|
|
|
|
|
}
|
|
|
|
|
for _, addr := range addrs {
|
|
|
|
|
if ipAddr := net.IPAddress(addr.IP); ipAddr != nil {
|
|
|
|
|
if rule := h.matchFinalRule(dialDest.Network, ipAddr, dialDest.Port, defaultRule); rule != nil && rule.action == RuleAction_Block {
|
|
|
|
|
blockedDest = &dialDest
|
|
|
|
|
blockedDest.Address = ipAddr
|
|
|
|
|
blockedRule = rule
|
|
|
|
|
return nil
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
if rule := h.matchFinalRule(dialDest.Network, dialDest.Address, dialDest.Port, defaultRule); rule != nil && rule.action == RuleAction_Block {
|
|
|
|
|
blockedDest = &dialDest
|
|
|
|
|
blockedRule = rule
|
|
|
|
|
return nil
|
|
|
|
|
} else {
|
|
|
|
|
if rule := h.matchFinalRule(dialDest.Network, dialDest.Address, dialDest.Port, defaultRule); rule != nil && rule.action == RuleAction_Block {
|
|
|
|
|
blockedDest = &dialDest
|
|
|
|
|
blockedRule = rule
|
|
|
|
|
return nil
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
rawConn, err := dialer.Dial(ctx, dialDest)
|
|
|
|
@@ -343,25 +392,21 @@ func (h *Handler) Process(ctx context.Context, link *transport.Link, dialer inte
|
|
|
|
|
return errors.New("failed to open connection to ", destination).Base(err)
|
|
|
|
|
}
|
|
|
|
|
if blockedDest != nil {
|
|
|
|
|
delay := h.blockDelay(blockedRule)
|
|
|
|
|
errors.LogInfo(ctx, "blocked target: ", *blockedDest, ", blackholing connection for ", delay)
|
|
|
|
|
timer := time.AfterFunc(delay, func() {
|
|
|
|
|
common.Interrupt(input)
|
|
|
|
|
common.Interrupt(output)
|
|
|
|
|
errors.LogInfo(ctx, "closed blackholed connection to blocked target: ", *blockedDest)
|
|
|
|
|
})
|
|
|
|
|
defer timer.Stop()
|
|
|
|
|
defer common.Close(output)
|
|
|
|
|
if err := buf.Copy(input, buf.Discard); err != nil {
|
|
|
|
|
return nil
|
|
|
|
|
}
|
|
|
|
|
return nil
|
|
|
|
|
return h.blackhole(ctx, input, output, blockedRule, blockedDest)
|
|
|
|
|
}
|
|
|
|
|
// TODO: SRV/TXT
|
|
|
|
|
// if remoteDest := net.DestinationFromAddr(conn.RemoteAddr()); h.applyFinalRules(remoteDest.Network, remoteDest.Address, remoteDest.Port, defaultRule) == RuleAction_Block {
|
|
|
|
|
// conn.Close()
|
|
|
|
|
// return blackhole(remoteDest)
|
|
|
|
|
// }
|
|
|
|
|
if defaultRule != nil || len(h.finalRules) > 0 {
|
|
|
|
|
if h.usesProxySettings {
|
|
|
|
|
errors.LogInfo(ctx, "skipping final rule check for proxied remote endpoint, original target: ", destination)
|
|
|
|
|
} else {
|
|
|
|
|
// SRV/TXT, lookup failed
|
|
|
|
|
remoteDest := net.DestinationFromAddr(conn.RemoteAddr())
|
|
|
|
|
if rule := h.matchFinalRule(remoteDest.Network, remoteDest.Address, remoteDest.Port, defaultRule); rule != nil && rule.action == RuleAction_Block {
|
|
|
|
|
conn.Close()
|
|
|
|
|
return h.blackhole(ctx, input, output, rule, &remoteDest)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if h.config.ProxyProtocol > 0 && h.config.ProxyProtocol <= 2 {
|
|
|
|
|
version := byte(h.config.ProxyProtocol)
|
|
|
|
|
srcAddr := inbound.Source.RawNetAddr()
|
|
|
|
@@ -406,7 +451,7 @@ func (h *Handler) Process(ctx context.Context, link *transport.Link, dialer inte
|
|
|
|
|
writer = buf.NewWriter(conn)
|
|
|
|
|
}
|
|
|
|
|
} else {
|
|
|
|
|
writer = NewPacketWriter(conn, h, defaultRule, UDPOverride, destination)
|
|
|
|
|
writer = NewPacketWriter(conn, h, defaultRule, UDPOverride, destination, outGateway)
|
|
|
|
|
if h.config.Noises != nil {
|
|
|
|
|
errors.LogDebug(ctx, "NOISE", h.config.Noises)
|
|
|
|
|
writer = &NoisePacketWriter{
|
|
|
|
@@ -510,7 +555,7 @@ func (r *PacketReader) ReadMultiBuffer() (buf.MultiBuffer, error) {
|
|
|
|
|
}
|
|
|
|
|
udpAddr := d.(*net.UDPAddr)
|
|
|
|
|
sourceAddr := net.IPAddress(udpAddr.IP)
|
|
|
|
|
if r.Handler.applyFinalRules(net.Network_UDP, sourceAddr, net.Port(udpAddr.Port), r.DefaultRule) == RuleAction_Block {
|
|
|
|
|
if rule := r.Handler.matchFinalRule(net.Network_UDP, sourceAddr, net.Port(udpAddr.Port), r.DefaultRule); rule != nil && rule.action == RuleAction_Block {
|
|
|
|
|
continue
|
|
|
|
|
}
|
|
|
|
|
b.Resize(0, int32(n))
|
|
|
|
@@ -535,7 +580,7 @@ func (r *PacketReader) ReadMultiBuffer() (buf.MultiBuffer, error) {
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// DialDest means the dial target used in the dialer when creating conn
|
|
|
|
|
func NewPacketWriter(conn net.Conn, h *Handler, defaultRule *FinalRule, UDPOverride net.Destination, DialDest net.Destination) buf.Writer {
|
|
|
|
|
func NewPacketWriter(conn net.Conn, h *Handler, defaultRule *FinalRule, UDPOverride net.Destination, DialDest net.Destination, outGateway net.Address) buf.Writer {
|
|
|
|
|
iConn := conn
|
|
|
|
|
statConn, ok := iConn.(*stat.CounterConnection)
|
|
|
|
|
if ok {
|
|
|
|
@@ -559,7 +604,7 @@ func NewPacketWriter(conn net.Conn, h *Handler, defaultRule *FinalRule, UDPOverr
|
|
|
|
|
DefaultRule: defaultRule,
|
|
|
|
|
UDPOverride: UDPOverride,
|
|
|
|
|
ResolvedUDPAddr: resolvedUDPAddr,
|
|
|
|
|
LocalAddr: net.DestinationFromAddr(conn.LocalAddr()).Address,
|
|
|
|
|
OutGateway: outGateway,
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
@@ -578,7 +623,7 @@ type PacketWriter struct {
|
|
|
|
|
// Resulting in these packets being sent to many different IPs randomly
|
|
|
|
|
// So, cache and keep the resolve result
|
|
|
|
|
ResolvedUDPAddr *utils.TypedSyncMap[string, net.Address]
|
|
|
|
|
LocalAddr net.Address
|
|
|
|
|
OutGateway net.Address
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func (w *PacketWriter) WriteMultiBuffer(mb buf.MultiBuffer) error {
|
|
|
|
@@ -601,21 +646,21 @@ func (w *PacketWriter) WriteMultiBuffer(mb buf.MultiBuffer) error {
|
|
|
|
|
if ip, ok := w.ResolvedUDPAddr.Load(b.UDP.Address.Domain()); ok {
|
|
|
|
|
b.UDP.Address = ip
|
|
|
|
|
} else {
|
|
|
|
|
ShouldUseSystemResolver := true
|
|
|
|
|
if w.Handler.config.DomainStrategy.HasStrategy() {
|
|
|
|
|
ips, err := internet.LookupForIP(b.UDP.Address.Domain(), w.Handler.config.DomainStrategy, w.LocalAddr)
|
|
|
|
|
shouldUseSystemResolver := true
|
|
|
|
|
if resolveStrategy := w.Handler.udpDomainStrategy(); resolveStrategy.HasStrategy() {
|
|
|
|
|
ips, err := internet.LookupForIP(b.UDP.Address.Domain(), resolveStrategy, w.OutGateway)
|
|
|
|
|
if err != nil {
|
|
|
|
|
// drop packet if resolve failed when forceIP
|
|
|
|
|
if w.Handler.config.DomainStrategy.ForceIP() {
|
|
|
|
|
if resolveStrategy.ForceIP() {
|
|
|
|
|
b.Release()
|
|
|
|
|
continue
|
|
|
|
|
}
|
|
|
|
|
} else {
|
|
|
|
|
ip = net.IPAddress(ips[dice.Roll(len(ips))])
|
|
|
|
|
ShouldUseSystemResolver = false
|
|
|
|
|
shouldUseSystemResolver = false
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
if ShouldUseSystemResolver {
|
|
|
|
|
if shouldUseSystemResolver {
|
|
|
|
|
udpAddr, err := net.ResolveUDPAddr("udp", b.UDP.NetAddr())
|
|
|
|
|
if err != nil {
|
|
|
|
|
b.Release()
|
|
|
|
@@ -629,7 +674,7 @@ func (w *PacketWriter) WriteMultiBuffer(mb buf.MultiBuffer) error {
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
if w.applyFinalRules(net.Network_UDP, b.UDP.Address, b.UDP.Port, w.DefaultRule) == RuleAction_Block {
|
|
|
|
|
if rule := w.matchFinalRule(net.Network_UDP, b.UDP.Address, b.UDP.Port, w.DefaultRule); rule != nil && rule.action == RuleAction_Block {
|
|
|
|
|
b.Release()
|
|
|
|
|
continue
|
|
|
|
|
}
|
|
|
|
|