/
actions.go
138 lines (112 loc) · 3.64 KB
/
actions.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
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
// Package actions describes the actions that can be applied to a given packet.
//
// See the top-level documentation for more details.
package actions
import (
"errors"
"fmt"
"github.com/google/gopacket"
"github.com/getlantern/geneva/internal"
"github.com/getlantern/geneva/internal/scanner"
"github.com/getlantern/geneva/triggers"
// gopacket best practice says import this, too.
_ "github.com/google/gopacket/layers"
)
var ErrInvalidAction = errors.New("invalid action")
// ActionTree represents a Geneva (trigger, action) pair.
//
// Technically, Geneva uses the term "action tree" to refer to the tree of actions in the tuple
// (trigger, action tree). In other words, RootAction here is what they call the "action tree". They
// have no name for the (trigger, action tree) tuple, which this type actually represents.
type ActionTree struct {
// Trigger is the trigger that will fire this action tree if matched.
Trigger triggers.Trigger
// RootAction is the root action of the tree and may have subordinate actions that it calls.
RootAction Action
}
// String returns a string representation of this ActionTree.
func (at *ActionTree) String() string {
return fmt.Sprintf("%s-%s-|", at.Trigger, at.RootAction)
}
// Matches returns whether this action tree's trigger matches the packet.
func (at *ActionTree) Matches(packet gopacket.Packet) (bool, error) {
r, err := at.Trigger.Matches(packet)
if err != nil {
return r, fmt.Errorf("match failed: %w", err)
}
return r, nil
}
// Apply applies this action tree to the packet, returning zero or more potentially-modified
// packets.
func (at *ActionTree) Apply(packet gopacket.Packet) ([]gopacket.Packet, error) {
r, err := at.RootAction.Apply(packet)
if err != nil {
return r, fmt.Errorf("apply failed: %w", err)
}
return r, nil
}
// ParseActionTree attempts to parse an action tree from its input.
func ParseActionTree(s *scanner.Scanner) (*ActionTree, error) {
trigger, err := triggers.ParseTrigger(s)
if err != nil {
return nil, fmt.Errorf("failed to parse action tree: %w", err)
}
at := &ActionTree{trigger, nil}
if _, err := s.Expect("-"); err != nil {
return nil, fmt.Errorf(
"unexpected token in action tree: %w",
internal.EOFUnexpected(err),
)
}
at.RootAction, err = ParseAction(s)
if err != nil {
return nil, fmt.Errorf("failed to parse action: %w", err)
}
if _, err := s.Expect("-|"); err != nil {
return nil, fmt.Errorf(
"unexpected token in action tree: %w",
internal.EOFUnexpected(err),
)
}
return at, nil
}
// Action is implemented by any value that describes a Geneva action.
type Action interface {
// Apply applies the action to the packet, returning zero or more potentially-modified
// packets.
Apply(gopacket.Packet) ([]gopacket.Packet, error)
fmt.Stringer
}
// ParseAction parses a string representation of an action into the actual Action object.
//
// If the string is malformed, an error will be returned instead.
func ParseAction(s *scanner.Scanner) (Action, error) {
if s.FindToken("duplicate", true) {
return ParseDuplicateAction(s)
}
if s.FindToken("fragment", true) {
return ParseFragmentAction(s)
}
if s.FindToken("tamper", true) {
return ParseTamperAction(s)
}
if s.FindToken("drop", true) {
if err := s.Advance(4); err != nil {
return nil, fmt.Errorf(
"failed to parse action: %w",
internal.EOFUnexpected(err),
)
}
return DefaultDropAction, nil
}
if s.FindToken("send", true) {
if err := s.Advance(4); err != nil {
return nil, fmt.Errorf(
"failed to parse action: %w",
internal.EOFUnexpected(err),
)
}
return DefaultSendAction, nil
}
return nil, fmt.Errorf("%w at %d", ErrInvalidAction, s.Pos())
}