hysteria/pkg/acl/entry.go
2020-04-25 18:58:27 -07:00

111 lines
2.3 KiB
Go

package acl
import (
"errors"
"fmt"
"net"
"strings"
)
type Action byte
const (
ActionDirect = Action(iota)
ActionProxy
ActionBlock
ActionHijack
)
type Entry struct {
Net *net.IPNet
Domain string
Suffix bool
All bool
Action Action
ActionArg string
}
// Format: action cond_type cond arg
// Examples:
// proxy domain-suffix google.com
// block ip 8.8.8.8
// hijack cidr 192.168.1.1/24 127.0.0.1
func ParseEntry(s string) (Entry, error) {
fields := strings.Fields(s)
if len(fields) < 2 {
return Entry{}, fmt.Errorf("expecting at least 2 fields, got %d", len(fields))
}
args := fields[1:]
if len(args) == 1 {
// Make sure there are at least 2 args
args = append(args, "")
}
ipNet, domain, suffix, all, err := parseCond(args[0], args[1])
if err != nil {
return Entry{}, err
}
e := Entry{
Net: ipNet,
Domain: domain,
Suffix: suffix,
All: all,
}
switch strings.ToLower(fields[0]) {
case "direct":
e.Action = ActionDirect
case "proxy":
e.Action = ActionProxy
case "block":
e.Action = ActionBlock
case "hijack":
if len(args) < 3 {
return Entry{}, fmt.Errorf("no hijack destination for %s %s", args[0], args[1])
}
e.Action = ActionHijack
e.ActionArg = args[2]
default:
return Entry{}, fmt.Errorf("invalid action %s", fields[0])
}
return e, nil
}
func parseCond(typ, cond string) (*net.IPNet, string, bool, bool, error) {
switch strings.ToLower(typ) {
case "domain":
if len(cond) == 0 {
return nil, "", false, false, errors.New("empty domain")
}
return nil, cond, false, false, nil
case "domain-suffix":
if len(cond) == 0 {
return nil, "", false, false, errors.New("empty domain suffix")
}
return nil, cond, true, false, nil
case "cidr":
_, ipNet, err := net.ParseCIDR(cond)
if err != nil {
return nil, "", false, false, err
}
return ipNet, "", false, false, nil
case "ip":
ip := net.ParseIP(cond)
if ip == nil {
return nil, "", false, false, fmt.Errorf("invalid ip %s", cond)
}
if ip.To4() != nil {
return &net.IPNet{
IP: ip,
Mask: net.CIDRMask(32, 32),
}, "", false, false, nil
} else {
return &net.IPNet{
IP: ip,
Mask: net.CIDRMask(128, 128),
}, "", false, false, nil
}
case "all":
return nil, "", false, true, nil
default:
return nil, "", false, false, fmt.Errorf("invalid condition type %s", typ)
}
}