-
Notifications
You must be signed in to change notification settings - Fork 0
/
rules.go
116 lines (98 loc) · 2.78 KB
/
rules.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
package main
// Copyright (c) 2013-2020, Sapphire Cat <https://github.com/sapphirecat>. All
// rights reserved. See the accompanying LICENSE file for license terms.
import (
"log"
"net"
"regexp"
"strconv"
"strings"
)
const (
// RuleForHTTP indicates an interception of a plain-text HTTP connection.
RuleForHTTP = iota
// RuleForTLS indicates an interception of a TLS-secured HTTPS connection.
RuleForTLS
// NoOp represents "no modification" for a request, e.g. for the HTTPS
// branch of an HTTP-only action.
NoOp = ""
)
// Rule represents a single host[:port] interception and action to take.
type Rule struct {
MatchHost *regexp.Regexp
MatchPort *regexp.Regexp
DebugRule bool
SendTo Server
}
// Server represents a destination to forward intercepted traffic to.
type Server struct {
Address string `toml:"address"`
HTTPPort int `toml:"http_port" default:"80"`
HTTPSPort int `toml:"https_port" default:"443"`
}
// Mode is either RuleForHttp (plain text) or RuleForTls (TLS)
type Mode int
// Ruleset contains the list of interception Rules.
type Ruleset struct {
Verbose bool
items []Rule
}
// Add adds a Rule to a Ruleset.
func (r *Ruleset) Add(a Rule) {
r.items = append(r.items, a)
}
// Length returns the number of Rules that were added to the Ruleset.
func (r *Ruleset) Length() int {
return len(r.items)
}
// NewRuleset creates a new Ruleset. The capacity can be pre-set.
func NewRuleset(capacity int) Ruleset {
return Ruleset{
items: make([]Rule, 0, capacity),
}
}
func getTarget(rules Ruleset, hostname string, mode Mode) string {
host, port, err := net.SplitHostPort(hostname)
if err != nil {
// Parse the error string, and hope it's never changed/localized....
if strings.Contains(err.Error(), "missing port") {
host = hostname
port = "80"
} else {
log.Fatalf("Bad hostname: %s: %s", err.Error(), hostname)
}
}
for i := range rules.items {
// take a pointer to prevent range from copying the rule struct
rule := &rules.items[i]
if !rule.MatchHost.MatchString(host) {
if rule.DebugRule {
text := rule.MatchHost.String()
log.Println("!match: host", host, "with", text)
}
continue
}
if rule.MatchPort != nil && !rule.MatchPort.MatchString(port) {
if rule.DebugRule {
text := rule.MatchPort.String()
log.Println("!match: port", port, "with", text)
}
continue
}
destination := rule.SendTo.Address
var destPort int
if mode == RuleForHTTP {
destPort = rule.SendTo.HTTPPort
} else {
destPort = rule.SendTo.HTTPSPort
}
destHostPort := net.JoinHostPort(destination, strconv.Itoa(destPort))
if rule.DebugRule {
srcHostPort := net.JoinHostPort(host, port)
log.Printf("match for %s -> %s\n", srcHostPort, destHostPort)
}
return destHostPort
}
// no rules specified an operation
return NoOp
}