Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

refactor: expose public API #70

Merged
merged 3 commits into from
Mar 6, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ TARGET ?= "1.1.1.1"
COUNT ?= 5
TIMEOUT ?= 10
LOCAL_TARGET := $(shell ip -4 addr show docker0 | grep 'inet ' | awk '{print $$2}' | cut -f 1 -d /)
COVERAGE_THRESHOLD := 75
COVERAGE_THRESHOLD := 70
FLAGS=-ldflags="-w -s -buildid=none -linkmode=external" -buildmode=pie -buildvcs=false

build:
Expand Down
59 changes: 23 additions & 36 deletions cmd/minivpn/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,11 +15,10 @@ import (
"github.com/jackpal/gateway"

"github.com/ooni/minivpn/extras/ping"
"github.com/ooni/minivpn/internal/model"
"github.com/ooni/minivpn/internal/networkio"
"github.com/ooni/minivpn/internal/runtimex"
"github.com/ooni/minivpn/internal/tun"
"github.com/ooni/minivpn/pkg/config"
"github.com/ooni/minivpn/pkg/tracex"
"github.com/ooni/minivpn/pkg/tunnel"
)

func runCmd(binaryPath string, args ...string) {
Expand All @@ -41,7 +40,7 @@ func runRoute(args ...string) {
runCmd("/sbin/route", args...)
}

type config struct {
type cmdConfig struct {
configPath string
doPing bool
doTrace bool
Expand All @@ -52,7 +51,7 @@ type config struct {
func main() {
log.SetLevel(log.DebugLevel)

cfg := &config{}
cfg := &cmdConfig{}
flag.StringVar(&cfg.configPath, "config", "", "config file to load")
flag.BoolVar(&cfg.doPing, "ping", false, "if true, do ping and exit (for testing)")
flag.BoolVar(&cfg.doTrace, "trace", false, "if true, do a trace of the handshake and exit (for testing)")
Expand All @@ -68,17 +67,17 @@ func main() {
log.SetHandler(NewHandler(os.Stderr))
log.SetLevel(log.DebugLevel)

opts := []model.Option{
model.WithConfigFile(cfg.configPath),
model.WithLogger(log.Log),
opts := []config.Option{
config.WithConfigFile(cfg.configPath),
config.WithLogger(log.Log),
}

start := time.Now()

var tracer *tracex.Tracer
if cfg.doTrace {
tracer = tracex.NewTracer(start)
opts = append(opts, model.WithHandshakeTracer(tracer))
opts = append(opts, config.WithHandshakeTracer(tracer))
defer func() {
trace := tracer.Trace()
jsonData, err := json.MarshalIndent(trace, "", " ")
Expand All @@ -89,34 +88,22 @@ func main() {
}()
}

config := model.NewConfig(opts...)

// connect to the server
dialer := networkio.NewDialer(log.Log, &net.Dialer{})
ctx := context.Background()

proto := config.Remote().Protocol
addr := config.Remote().Endpoint

conn, err := dialer.DialContext(ctx, proto, addr)
if err != nil {
log.WithError(err).Error("dialer.DialContext")
return
}

// The TLS will expire in 60 seconds by default, but we can pass
// a shorter timeout.
ctx, cancel := context.WithTimeout(ctx, time.Duration(cfg.timeout)*time.Second)
ctx, cancel := context.WithTimeout(context.Background(), time.Duration(cfg.timeout)*time.Second)
defer cancel()

// create config from the passed options
vpncfg := config.NewConfig(opts...)

// create a vpn tun Device
tunnel, err := tun.StartTUN(ctx, conn, config)
tun, err := tunnel.Start(ctx, &net.Dialer{}, vpncfg)
if err != nil {
log.WithError(err).Error("init error")
return
}
log.Infof("Local IP: %s\n", tunnel.LocalAddr())
log.Infof("Gateway: %s\n", tunnel.RemoteAddr())
log.Infof("Local IP: %s\n", tun.LocalAddr())
log.Infof("Gateway: %s\n", tun.RemoteAddr())

fmt.Println("initialization-sequence-completed")
fmt.Printf("elapsed: %v\n", time.Since(start))
Expand All @@ -126,7 +113,7 @@ func main() {
}

if cfg.doPing {
pinger := ping.New("8.8.8.8", tunnel)
pinger := ping.New("8.8.8.8", tun)
count := 5
pinger.Count = count

Expand All @@ -151,9 +138,9 @@ func main() {
MTU := 1420
iface.SetMTU(MTU)

localAddr := tunnel.LocalAddr().String()
remoteAddr := tunnel.RemoteAddr().String()
netMask := tunnel.NetMask()
localAddr := tun.LocalAddr().String()
remoteAddr := tun.RemoteAddr().String()
netMask := tun.NetMask()

// discover local gateway IP, we need it to add a route to our remote via our network gw
defaultGatewayIP, err := gateway.DiscoverGateway()
Expand All @@ -170,8 +157,8 @@ func main() {
}

if defaultGatewayIP != nil && defaultInterface != nil {
log.Infof("route add %s gw %v dev %s", config.Remote().IPAddr, defaultGatewayIP, defaultInterface.Name)
runRoute("add", config.Remote().IPAddr, "gw", defaultGatewayIP.String(), defaultInterface.Name)
log.Infof("route add %s gw %v dev %s", vpncfg.Remote().IPAddr, defaultGatewayIP, defaultInterface.Name)
runRoute("add", vpncfg.Remote().IPAddr, "gw", defaultGatewayIP.String(), defaultInterface.Name)
}

// we want the network CIDR for setting up the routes
Expand All @@ -194,13 +181,13 @@ func main() {
if err != nil {
log.WithError(err).Fatal("error reading from tun")
}
tunnel.Write(packet[:n])
tun.Write(packet[:n])
}
}()
go func() {
for {
packet := make([]byte, 2000)
n, err := tunnel.Read(packet)
n, err := tun.Read(packet)
if err != nil {
log.WithError(err).Fatal("error reading from tun")
}
Expand Down
3 changes: 2 additions & 1 deletion internal/controlchannel/controlchannel.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import (
"github.com/ooni/minivpn/internal/model"
"github.com/ooni/minivpn/internal/session"
"github.com/ooni/minivpn/internal/workers"
"github.com/ooni/minivpn/pkg/config"
)

var (
Expand Down Expand Up @@ -38,7 +39,7 @@ type Service struct {
//
// [ARCHITECTURE]: https://github.com/ooni/minivpn/blob/main/ARCHITECTURE.md
func (svc *Service) StartWorkers(
config *model.Config,
config *config.Config,
workersManager *workers.Manager,
sessionManager *session.Manager,
) {
Expand Down
7 changes: 4 additions & 3 deletions internal/datachannel/common_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,18 +10,19 @@ import (
"github.com/ooni/minivpn/internal/runtimex"
"github.com/ooni/minivpn/internal/session"
"github.com/ooni/minivpn/internal/vpntest"
"github.com/ooni/minivpn/pkg/config"
)

func makeTestingSession() *session.Manager {
manager, err := session.NewManager(model.NewConfig())
manager, err := session.NewManager(config.NewConfig())
runtimex.PanicOnError(err, "could not get session manager")
manager.SetRemoteSessionID(model.SessionID{0x01})
return manager
}

func makeTestingOptions(t *testing.T, cipher, auth string) *model.OpenVPNOptions {
func makeTestingOptions(t *testing.T, cipher, auth string) *config.OpenVPNOptions {
crt, _ := vpntest.WriteTestingCerts(t.TempDir())
opt := &model.OpenVPNOptions{
opt := &config.OpenVPNOptions{
Cipher: cipher,
Auth: auth,
CertPath: crt.Cert,
Expand Down
5 changes: 3 additions & 2 deletions internal/datachannel/controller.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import (
"github.com/ooni/minivpn/internal/model"
"github.com/ooni/minivpn/internal/runtimex"
"github.com/ooni/minivpn/internal/session"
"github.com/ooni/minivpn/pkg/config"
)

// dataChannelHandler manages the data "channel".
Expand All @@ -25,7 +26,7 @@ type dataChannelHandler interface {
// DataChannel represents the data "channel", that will encrypt and decrypt the tunnel payloads.
// data implements the dataHandler interface.
type DataChannel struct {
options *model.OpenVPNOptions
options *config.OpenVPNOptions
sessionManager *session.Manager
state *dataChannelState
decodeFn func(model.Logger, []byte, *session.Manager, *dataChannelState) (*encryptedData, error)
Expand All @@ -39,7 +40,7 @@ var _ dataChannelHandler = &DataChannel{} // Ensure that we implement dataChanne
// NewDataChannelFromOptions returns a new data object, initialized with the
// options given. it also returns any error raised.
func NewDataChannelFromOptions(logger model.Logger,
opt *model.OpenVPNOptions,
opt *config.OpenVPNOptions,
sessionManager *session.Manager) (*DataChannel, error) {
runtimex.Assert(opt != nil, "openvpn datachannel: opts cannot be nil")
runtimex.Assert(opt != nil, "openvpn datachannel: opts cannot be nil")
Expand Down
23 changes: 12 additions & 11 deletions internal/datachannel/controller_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,14 +9,15 @@ import (
"github.com/google/go-cmp/cmp"
"github.com/ooni/minivpn/internal/model"
"github.com/ooni/minivpn/internal/session"
"github.com/ooni/minivpn/pkg/config"
)

func TestNewDataChannelFromOptions(t *testing.T) {
t.Run("check we can create a data channel", func(t *testing.T) {
opt := &model.OpenVPNOptions{
opt := &config.OpenVPNOptions{
Auth: "SHA256",
Cipher: "AES-128-GCM",
Compress: model.CompressionEmpty,
Compress: config.CompressionEmpty,
}
_, err := NewDataChannelFromOptions(log.Log, opt, makeTestingSession())
if err != nil {
Expand Down Expand Up @@ -77,7 +78,7 @@ func Test_DataChannel_setupKeys(t *testing.T) {

func Test_DataChannel_writePacket(t *testing.T) {
type fields struct {
options *model.OpenVPNOptions
options *config.OpenVPNOptions
// session is only used for NonAEAD encryption
session *session.Manager
state *dataChannelState
Expand All @@ -96,7 +97,7 @@ func Test_DataChannel_writePacket(t *testing.T) {
{
name: "good write with aead encryption should not fail",
fields: fields{
options: &model.OpenVPNOptions{Compress: model.CompressionEmpty},
options: &config.OpenVPNOptions{Compress: config.CompressionEmpty},
session: makeTestingSession(),
state: makeTestingStateAEAD(),
encryptEncodeFn: func(model.Logger, []byte, *session.Manager, *dataChannelState) ([]byte, error) {
Expand All @@ -117,7 +118,7 @@ func Test_DataChannel_writePacket(t *testing.T) {
{
name: "good write with non-aead encryption should not fail",
fields: fields{
options: &model.OpenVPNOptions{Compress: model.CompressionEmpty},
options: &config.OpenVPNOptions{Compress: config.CompressionEmpty},
session: makeTestingSession(),
state: makeTestingStateNonAEAD(),
encryptEncodeFn: func(model.Logger, []byte, *session.Manager, *dataChannelState) ([]byte, error) {
Expand Down Expand Up @@ -172,7 +173,7 @@ func Test_DataChannel_deadPacket(t *testing.T) {
}

type fields struct {
options *model.OpenVPNOptions
options *config.OpenVPNOptions
state *dataChannelState
decodeFn func(model.Logger, []byte, *session.Manager, *dataChannelState) (*encryptedData, error)
decryptFn func([]byte, *encryptedData) ([]byte, error)
Expand Down Expand Up @@ -238,7 +239,7 @@ func Test_Data_decrypt(t *testing.T) {
}

type fields struct {
options *model.OpenVPNOptions
options *config.OpenVPNOptions
session *session.Manager
state *dataChannelState
decryptFn func([]byte, *encryptedData) ([]byte, error)
Expand All @@ -258,7 +259,7 @@ func Test_Data_decrypt(t *testing.T) {
{
name: "empty output in decode does fail",
fields: fields{
options: &model.OpenVPNOptions{},
options: &config.OpenVPNOptions{},
session: makeTestingSession(),
state: makeTestingStateAEAD(),
decodeFn: func(model.Logger, []byte, *session.Manager, *dataChannelState) (*encryptedData, error) {
Expand All @@ -275,7 +276,7 @@ func Test_Data_decrypt(t *testing.T) {
{
name: "empty encrypted input does fail",
fields: fields{
options: &model.OpenVPNOptions{},
options: &config.OpenVPNOptions{},
session: makeTestingSession(),
state: makeTestingStateAEAD(),
decodeFn: func(model.Logger, []byte, *session.Manager, *dataChannelState) (*encryptedData, error) {
Expand All @@ -292,7 +293,7 @@ func Test_Data_decrypt(t *testing.T) {
{
name: "error in decrypt propagates",
fields: fields{
options: &model.OpenVPNOptions{},
options: &config.OpenVPNOptions{},
session: makeTestingSession(),
state: makeTestingStateAEAD(),
decodeFn: func(model.Logger, []byte, *session.Manager, *dataChannelState) (*encryptedData, error) {
Expand All @@ -310,7 +311,7 @@ func Test_Data_decrypt(t *testing.T) {
{
name: "good decrypt returns expected output",
fields: fields{
options: &model.OpenVPNOptions{},
options: &config.OpenVPNOptions{},
session: makeTestingSession(),
state: makeTestingStateAEAD(),
decodeFn: func(model.Logger, []byte, *session.Manager, *dataChannelState) (*encryptedData, error) {
Expand Down
7 changes: 4 additions & 3 deletions internal/datachannel/read.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
"github.com/ooni/minivpn/internal/model"
"github.com/ooni/minivpn/internal/runtimex"
"github.com/ooni/minivpn/internal/session"
"github.com/ooni/minivpn/pkg/config"
)

var (
Expand All @@ -36,7 +37,7 @@
return &encryptedData{}, ErrBadRemoteHMAC
}
remoteHMAC := state.hmacKeyRemote[:8]
packet_id := buf[:4]

Check warning on line 40 in internal/datachannel/read.go

View workflow job for this annotation

GitHub Actions / lint

don't use underscores in Go names; var packet_id should be packetID

headers := &bytes.Buffer{}
headers.WriteByte(opcodeAndKeyHeader(session))
Expand Down Expand Up @@ -104,7 +105,7 @@
// modes are supported at the moment, so no real decompression is done. It
// returns a byte array, and an error if the operation could not be completed
// successfully.
func maybeDecompress(b []byte, st *dataChannelState, opt *model.OpenVPNOptions) ([]byte, error) {
func maybeDecompress(b []byte, st *dataChannelState, opt *config.OpenVPNOptions) ([]byte, error) {
if st == nil || st.dataCipher == nil {
return []byte{}, fmt.Errorf("%w:%s", ErrBadInput, "bad state")
}
Expand All @@ -121,7 +122,7 @@
switch st.dataCipher.isAEAD() {
case true:
switch opt.Compress {
case model.CompressionStub, model.CompressionLZONo:
case config.CompressionStub, config.CompressionLZONo:
// these are deprecated in openvpn 2.5.x
compr = b[0]
payload = b[1:]
Expand All @@ -141,7 +142,7 @@
st.SetRemotePacketID(remotePacketID)

switch opt.Compress {
case model.CompressionStub, model.CompressionLZONo:
case config.CompressionStub, config.CompressionLZONo:
compr = b[4]
payload = b[5:]
default:
Expand Down
3 changes: 2 additions & 1 deletion internal/datachannel/service.go
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ import (
"github.com/ooni/minivpn/internal/model"
"github.com/ooni/minivpn/internal/session"
"github.com/ooni/minivpn/internal/workers"
"github.com/ooni/minivpn/pkg/config"
)

var (
Expand Down Expand Up @@ -50,7 +51,7 @@ type Service struct {
// 3. keyWorker BLOCKS on keyUp to read a dataChannelKey and
// initializes the internal state with the resulting key;
func (s *Service) StartWorkers(
config *model.Config,
config *config.Config,
workersManager *workers.Manager,
sessionManager *session.Manager,
) {
Expand Down
3 changes: 2 additions & 1 deletion internal/datachannel/service_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import (
"github.com/ooni/minivpn/internal/model"
"github.com/ooni/minivpn/internal/session"
"github.com/ooni/minivpn/internal/workers"
"github.com/ooni/minivpn/pkg/config"
)

// test that we can start and stop the workers
Expand All @@ -26,7 +27,7 @@ func TestService_StartWorkers(t *testing.T) {
session := makeTestingSession()

opts := makeTestingOptions(t, "AES-128-GCM", "sha512")
s.StartWorkers(model.NewConfig(model.WithOpenVPNOptions(opts)), workers, session)
s.StartWorkers(config.NewConfig(config.WithOpenVPNOptions(opts)), workers, session)

keyReady <- makeTestingDataChannelKey()
<-session.Ready
Expand Down
Loading
Loading