-
Notifications
You must be signed in to change notification settings - Fork 7
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge branch 'main' into refactor/binary
- Loading branch information
Showing
33 changed files
with
667 additions
and
218 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,96 @@ | ||
package model | ||
|
||
import ( | ||
"net" | ||
|
||
"github.com/apex/log" | ||
"github.com/ooni/minivpn/internal/runtimex" | ||
) | ||
|
||
// Config contains options to initialize the OpenVPN tunnel. | ||
type Config struct { | ||
// openVPNOptions contains options related to openvpn. | ||
openvpnOptions *OpenVPNOptions | ||
|
||
// logger will be used to log events. | ||
logger Logger | ||
|
||
// if a tracer is provided, it will be used to trace the openvpn handshake. | ||
tracer HandshakeTracer | ||
} | ||
|
||
// NewConfig returns a Config ready to intialize a vpn tunnel. | ||
func NewConfig(options ...Option) *Config { | ||
cfg := &Config{ | ||
openvpnOptions: &OpenVPNOptions{}, | ||
logger: log.Log, | ||
tracer: &dummyTracer{}, | ||
} | ||
for _, opt := range options { | ||
opt(cfg) | ||
} | ||
return cfg | ||
} | ||
|
||
// Option is an option you can pass to initialize minivpn. | ||
type Option func(config *Config) | ||
|
||
// WithConfigFile configures OpenVPNOptions parsed from the given file. | ||
func WithConfigFile(configPath string) Option { | ||
return func(config *Config) { | ||
openvpnOpts, err := ReadConfigFile(configPath) | ||
runtimex.PanicOnError(err, "cannot parse config file") | ||
runtimex.PanicIfFalse(openvpnOpts.HasAuthInfo(), "missing auth info") | ||
config.openvpnOptions = openvpnOpts | ||
} | ||
} | ||
|
||
// WithLogger configures the passed [Logger]. | ||
func WithLogger(logger Logger) Option { | ||
return func(config *Config) { | ||
config.logger = logger | ||
} | ||
} | ||
|
||
// WithHandshakeTracer configures the passed [HandshakeTracer]. | ||
func WithHandshakeTracer(tracer HandshakeTracer) Option { | ||
return func(config *Config) { | ||
config.tracer = tracer | ||
} | ||
} | ||
|
||
// Logger returns the configured logger. | ||
func (c *Config) Logger() Logger { | ||
return c.logger | ||
} | ||
|
||
// Tracer returns the handshake tracer. | ||
func (c *Config) Tracer() HandshakeTracer { | ||
return c.tracer | ||
} | ||
|
||
// OpenVPNOptions returns the configured openvpn options. | ||
func (c *Config) OpenVPNOptions() *OpenVPNOptions { | ||
return c.openvpnOptions | ||
} | ||
|
||
// Remote returns the OpenVPN remote. | ||
func (c *Config) Remote() *Remote { | ||
return &Remote{ | ||
IPAddr: c.openvpnOptions.Remote, | ||
Endpoint: net.JoinHostPort(c.openvpnOptions.Remote, c.openvpnOptions.Port), | ||
Protocol: c.openvpnOptions.Proto.String(), | ||
} | ||
} | ||
|
||
// Remote has info about the OpenVPN remote. | ||
type Remote struct { | ||
// IPAddr is the IP Address for the remote. | ||
IPAddr string | ||
|
||
// Endpoint is in the form ip:port. | ||
Endpoint string | ||
|
||
// Protocol is either "tcp" or "udp" | ||
Protocol string | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,59 @@ | ||
package model | ||
|
||
// NegotiationState is the state of the session negotiation. | ||
type NegotiationState int | ||
|
||
const ( | ||
// S_ERROR means there was some form of protocol error. | ||
S_ERROR = NegotiationState(iota) - 1 | ||
|
||
// S_UNDER is the undefined state. | ||
S_UNDEF | ||
|
||
// S_INITIAL means we're ready to begin the three-way handshake. | ||
S_INITIAL | ||
|
||
// S_PRE_START means we're waiting for acknowledgment from the remote. | ||
S_PRE_START | ||
|
||
// S_START means we've done the three-way handshake. | ||
S_START | ||
|
||
// S_SENT_KEY means we have sent the local part of the key_source2 random material. | ||
S_SENT_KEY | ||
|
||
// S_GOT_KEY means we have got the remote part of key_source2. | ||
S_GOT_KEY | ||
|
||
// S_ACTIVE means the control channel was established. | ||
S_ACTIVE | ||
|
||
// S_GENERATED_KEYS means the data channel keys have been generated. | ||
S_GENERATED_KEYS | ||
) | ||
|
||
// String maps a [SessionNegotiationState] to a string. | ||
func (sns NegotiationState) String() string { | ||
switch sns { | ||
case S_UNDEF: | ||
return "S_UNDEF" | ||
case S_INITIAL: | ||
return "S_INITIAL" | ||
case S_PRE_START: | ||
return "S_PRE_START" | ||
case S_START: | ||
return "S_START" | ||
case S_SENT_KEY: | ||
return "S_SENT_KEY" | ||
case S_GOT_KEY: | ||
return "S_GOT_KEY" | ||
case S_ACTIVE: | ||
return "S_ACTIVE" | ||
case S_GENERATED_KEYS: | ||
return "S_GENERATED_KEYS" | ||
case S_ERROR: | ||
return "S_ERROR" | ||
default: | ||
return "S_INVALID" | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,72 @@ | ||
package model | ||
|
||
import ( | ||
"fmt" | ||
"time" | ||
) | ||
|
||
// HandshakeTracer allows to collect traces for a given OpenVPN handshake. A HandshakeTracer can be optionally | ||
// added to the top-level TUN constructor, and it will be propagated to any layer that needs to register an event. | ||
type HandshakeTracer interface { | ||
// TimeNow allows to inject time for deterministic tests. | ||
TimeNow() time.Time | ||
|
||
// OnStateChange is called for each transition in the state machine. | ||
OnStateChange(state NegotiationState) | ||
|
||
// OnIncomingPacket is called when a packet is received. | ||
OnIncomingPacket(packet *Packet, stage NegotiationState) | ||
|
||
// OnOutgoingPacket is called when a packet is about to be sent. | ||
OnOutgoingPacket(packet *Packet, stage NegotiationState, retries int) | ||
|
||
// OnDroppedPacket is called whenever a packet is dropped (in/out) | ||
OnDroppedPacket(direction Direction, stage NegotiationState, packet *Packet) | ||
} | ||
|
||
// Direction is one of two directions on a packet. | ||
type Direction int | ||
|
||
const ( | ||
// DirectionIncoming marks received packets. | ||
DirectionIncoming = Direction(iota) | ||
|
||
// DirectionOutgoing marks packets to be sent. | ||
DirectionOutgoing | ||
) | ||
|
||
var _ fmt.Stringer = Direction(0) | ||
|
||
// String implements fmt.Stringer | ||
func (d Direction) String() string { | ||
switch d { | ||
case DirectionIncoming: | ||
return "read" | ||
case DirectionOutgoing: | ||
return "write" | ||
default: | ||
return "undefined" | ||
} | ||
} | ||
|
||
// dummyTracer is a no-op implementation of [model.HandshakeTracer] that does nothing | ||
// but can be safely passed as a default implementation. | ||
type dummyTracer struct{} | ||
|
||
// TimeNow allows to manipulate time for deterministic tests. | ||
func (dt *dummyTracer) TimeNow() time.Time { return time.Now() } | ||
|
||
// OnStateChange is called for each transition in the state machine. | ||
func (dt *dummyTracer) OnStateChange(NegotiationState) {} | ||
|
||
// OnIncomingPacket is called when a packet is received. | ||
func (dt *dummyTracer) OnIncomingPacket(*Packet, NegotiationState) {} | ||
|
||
// OnOutgoingPacket is called when a packet is about to be sent. | ||
func (dt *dummyTracer) OnOutgoingPacket(*Packet, NegotiationState, int) {} | ||
|
||
// OnDroppedPacket is called whenever a packet is dropped (in/out) | ||
func (dt *dummyTracer) OnDroppedPacket(Direction, NegotiationState, *Packet) {} | ||
|
||
// Assert that dummyTracer implements [model.HandshakeTracer]. | ||
var _ HandshakeTracer = &dummyTracer{} |
Oops, something went wrong.