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

Add method "DumpState" to D-Bus API #121

Merged
merged 1 commit into from
Sep 5, 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
4 changes: 4 additions & 0 deletions configs/dbus/com.telekom_mms.oc_daemon.Daemon.conf
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,10 @@
<allow send_destination="com.telekom_mms.oc_daemon.Daemon"
send_interface="com.telekom_mms.oc_daemon.Daemon"
send_member="Disconnect"/>

<allow send_destination="com.telekom_mms.oc_daemon.Daemon"
send_interface="com.telekom_mms.oc_daemon.Daemon"
send_member="DumpState"/>
</policy>

<policy context="default">
Expand Down
18 changes: 18 additions & 0 deletions internal/client/client.go
Original file line number Diff line number Diff line change
Expand Up @@ -221,6 +221,24 @@ func getStatus() error {
return printStatus(status)
}

// dumpState dumps the internal state of the daemon.
func dumpState() error {
// create client
c, err := clientNewClient(config)
if err != nil {
return fmt.Errorf("error creating client: %w", err)
}
defer func() { _ = c.Close() }()

// get status
state, err := c.DumpState()
if err != nil {
return fmt.Errorf("error getting status: %w", err)
}
fmt.Println(state)
return nil
}

// monitor subscribes to VPN status updates from the daemon and displays them.
func monitor() error {
// create client
Expand Down
35 changes: 35 additions & 0 deletions internal/client/client_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,8 @@ import (
type testClient struct {
querErr error
status *vpnstatus.Status
dumpErr error
dumpSta string
authErr error
connErr error
discErr error
Expand All @@ -33,6 +35,7 @@ func (t *testClient) Subscribe() (chan *vpnstatus.Status, error) { return t.subs
func (t *testClient) Authenticate() error { return t.authErr }
func (t *testClient) Connect() error { return t.connErr }
func (t *testClient) Disconnect() error { return t.discErr }
func (t *testClient) DumpState() (string, error) { return t.dumpSta, t.dumpErr }
func (t *testClient) Close() error { return nil }

// TestListServers tests listServers.
Expand Down Expand Up @@ -205,6 +208,38 @@ func TestGetStatus(t *testing.T) {
}
}

// TestDumpState tests dumpState.
func TestDumpState(t *testing.T) {
defer func() { clientNewClient = client.NewClient }()

// test with client error
clientNewClient = func(*client.Config) (client.Client, error) {
return nil, errors.New("test error")
}

if err := dumpState(); err == nil {
t.Error("client error should return error")
}

// test with dump state error
clientNewClient = func(*client.Config) (client.Client, error) {
return &testClient{dumpErr: errors.New("test error")}, nil
}

if err := dumpState(); err == nil {
t.Error("dump state error should return error")
}

// test without error
clientNewClient = func(*client.Config) (client.Client, error) {
return &testClient{dumpSta: "test state"}, nil
}

if err := dumpState(); err != nil {
t.Error("dump state should not return error")
}
}

// TestMonitor tests monitor.
func TestMonitor(t *testing.T) {
defer func() { clientNewClient = client.NewClient }()
Expand Down
2 changes: 2 additions & 0 deletions internal/client/cmd.go
Original file line number Diff line number Diff line change
Expand Up @@ -243,6 +243,8 @@ func run(args []string) error {
return reconnectVPN()
case "status":
return getStatus()
case "dumpstate":
return dumpState()
case "monitor":
return monitor()
case "save":
Expand Down
1 change: 1 addition & 0 deletions internal/client/cmd_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -126,6 +126,7 @@ func TestRun(t *testing.T) {
"reconnect",
"status",
"monitor",
"dumpstate",
} {
if err := run([]string{"test",
"-cert", "cert-file",
Expand Down
34 changes: 34 additions & 0 deletions internal/daemon/daemon.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ package daemon

import (
"context"
"encoding/json"
"fmt"
"net"
"net/netip"
Expand Down Expand Up @@ -473,6 +474,33 @@ func (d *Daemon) handleClientRequest(request *api.Request) {
}
}

// dumpState returns the internal daemon state as json string.
func (d *Daemon) dumpState() string {
// define state type
type State struct {
TrafficPolicing *trafpol.State
VPNSetup *vpnsetup.State
}

// collect internal state
state := State{}
if d.trafpol != nil {
state.TrafficPolicing = d.trafpol.GetState()
}
if d.vpnsetup != nil {
state.VPNSetup = d.vpnsetup.GetState()
}

// convert to json
b, err := json.Marshal(state)
if err != nil {
log.WithError(err).Error("Daemon could not convert internal state to JSON")
return ""
}

return string(b)
}

// handleDBusRequest handles a D-Bus API client request.
func (d *Daemon) handleDBusRequest(request *dbusapi.Request) {
defer request.Close()
Expand Down Expand Up @@ -505,6 +533,12 @@ func (d *Daemon) handleDBusRequest(request *dbusapi.Request) {
// diconnect VPN
log.Info("Daemon got disconnect request from client")
d.disconnectVPN()

case dbusapi.RequestDumpState:
// dump state
state := d.dumpState()
log.WithField("state", state).Info("Daemon got dump state request from client")
request.Results = []any{state}
}
}

Expand Down
41 changes: 33 additions & 8 deletions internal/dbusapi/service.go
Original file line number Diff line number Diff line change
Expand Up @@ -130,12 +130,14 @@ const (
const (
MethodConnect = Interface + ".Connect"
MethodDisconnect = Interface + ".Disconnect"
MethodDumpState = Interface + ".DumpState"
)

// Request Names.
const (
RequestConnect = "Connect"
RequestDisconnect = "Disconnect"
RequestDumpState = "DumpState"
)

// Request is a D-Bus client request.
Expand Down Expand Up @@ -212,6 +214,27 @@ func (d daemon) Disconnect(sender dbus.Sender) *dbus.Error {
return nil
}

// DumpState is the "DumpState" method of the D-Bus interface.
func (d daemon) DumpState(sender dbus.Sender) (string, *dbus.Error) {
log.WithField("sender", sender).Debug("Received D-Bus DumpState() call")
request := &Request{
Name: RequestDumpState,
wait: make(chan struct{}),
done: d.done,
}
select {
case d.requests <- request:
case <-d.done:
return "", dbus.NewError(Interface+".DumpStateAborted", []any{"DumpState aborted"})
}

request.Wait()
if request.Error != nil {
return "", dbus.NewError(Interface+".DumpStateAborted", []any{request.Error.Error()})
}
return request.Results[0].(string), nil
}

// propertyUpdate is an update of a property.
type propertyUpdate struct {
name string
Expand Down Expand Up @@ -431,16 +454,18 @@ func (s *Service) Start() error {
// set names of method arguments
introMeths := introspect.Methods(meths)
for _, m := range introMeths {
if m.Name != "Connect" {
continue
if m.Name == "Connect" {
m.Args[0].Name = "server"
m.Args[1].Name = "cookie"
m.Args[2].Name = "host"
m.Args[3].Name = "connect_url"
m.Args[4].Name = "fingerprint"
m.Args[5].Name = "resolve"
}
m.Args[0].Name = "server"
m.Args[1].Name = "cookie"
m.Args[2].Name = "host"
m.Args[3].Name = "connect_url"
m.Args[4].Name = "fingerprint"
m.Args[5].Name = "resolve"

if m.Name == "DumpState" {
m.Args[0].Name = "state"
}
}
// set peer interface
peerData := introspect.Interface{
Expand Down
67 changes: 67 additions & 0 deletions internal/dbusapi/service_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -167,6 +167,73 @@ func TestDaemonDisconnect(t *testing.T) {
}
}

// TestDaemonDumpStateErrors tests DumpState of daemon, errors.
func TestDaemonDumpStateErrors(t *testing.T) {
// create daemon
requests := make(chan *Request)
done := make(chan struct{})
daemon := daemon{
requests: requests,
done: done,
}

// error when handling request
go func() {
r := <-requests
r.Error = errors.New("test error")
r.Close()
}()
if _, err := daemon.DumpState(""); err == nil {
t.Error("should return error")
}

// closed daemon
close(done)
if _, err := daemon.DumpState(""); err == nil {
t.Error("should return error")
}
}

// TestDaemonDumpState tests DumpState of daemon.
func TestDaemonDumpState(t *testing.T) {
// create daemon
requests := make(chan *Request)
done := make(chan struct{})
daemon := daemon{
requests: requests,
done: done,
}

// run disconnect and get results
want := &Request{
Name: RequestDumpState,
Results: []any{"test state"},
done: done,
}
got := &Request{}
go func() {
r := <-requests
r.Results = append(r.Results, "test state")
got = r
r.Close()
}()
state, err := daemon.DumpState("sender")
if err != nil {
t.Error(err)
}

// check results
if got.Name != want.Name ||
!reflect.DeepEqual(got.Parameters, want.Parameters) ||
!reflect.DeepEqual(got.Results, want.Results) ||
got.Error != want.Error ||
got.done != want.done ||
state != "test state" {
// not equal
t.Errorf("got %v, want %v", got, want)
}
}

// testConn implements the dbusConn interface for testing.
type testConn struct {
reqNameReply dbus.RequestNameReply
Expand Down
19 changes: 19 additions & 0 deletions internal/dnsproxy/proxy.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,14 @@ import (
log "github.com/sirupsen/logrus"
)

// State is the internal state of the DNS Proxy.
type State struct {
Config *Config
Remotes map[string][]string
Watches []string
TempWatches []string
}

// Proxy is a DNS proxy.
type Proxy struct {
config *Config
Expand Down Expand Up @@ -247,6 +255,17 @@ func (p *Proxy) SetWatches(watches []string) {
}
}

// GetState returns the internal state of the DNS Proxy.
func (p *Proxy) GetState() *State {
watches, tempWatches := p.watches.List()
return &State{
Config: p.config,
Remotes: p.remotes.List(),
Watches: watches,
TempWatches: tempWatches,
}
}

// NewProxy returns a new Proxy that listens on address.
func NewProxy(config *Config) *Proxy {
var udp *dns.Server
Expand Down
29 changes: 29 additions & 0 deletions internal/dnsproxy/proxy_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import (
"errors"
"net"
"net/netip"
"reflect"
"testing"

"github.com/miekg/dns"
Expand Down Expand Up @@ -272,6 +273,34 @@ func TestProxySetWatches(_ *testing.T) {
p.SetWatches(watches)
}

// TestProxyGetState tests GetState of Proxy.
func TestProxyGetState(t *testing.T) {
p := NewProxy(getTestConfig())

// set remotes
getRemotes := func() map[string][]string {
return map[string][]string{".": {"192.168.1.1"}}
}
p.SetRemotes(getRemotes())

// set watches
p.watches.Add("example.com.")
p.watches.AddTempCNAME("cname.example.com.", 300)
p.watches.AddTempDNAME("dname.example.com.", 300)

// check state
want := &State{
Config: getTestConfig(),
Remotes: getRemotes(),
Watches: []string{"example.com."},
TempWatches: []string{"cname.example.com.", "dname.example.com."},
}
got := p.GetState()
if !reflect.DeepEqual(got, want) {
t.Errorf("got %v, want %v", got, want)
}
}

// TestNewProxy tests NewProxy.
func TestNewProxy(t *testing.T) {
p := NewProxy(getTestConfig())
Expand Down
Loading
Loading