Skip to content

Commit

Permalink
feat: user readable messages between daemon and cli
Browse files Browse the repository at this point in the history
  • Loading branch information
bilalcaliskan committed Jan 27, 2024
1 parent 35e4ed8 commit 5145665
Show file tree
Hide file tree
Showing 8 changed files with 223 additions and 105 deletions.
2 changes: 1 addition & 1 deletion cmd/cli/add/add.go
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ to quickly create a Cobra application.`,
req := fmt.Sprintf("%s %s", cmd.Name(), arg)
res, err := utils.SendCommandToDaemon(utils.SocketPath, req)
if err != nil {
logger.Error().Str("command", req).Str("response", res).Err(err).Msg("error sending command to daemon")
logger.Error().Str("command", req).Err(err).Msg("error sending command to daemon")
continue
}

Expand Down
6 changes: 1 addition & 5 deletions cmd/cli/list/list.go
Original file line number Diff line number Diff line change
@@ -1,8 +1,6 @@
package list

import (
"strings"

"github.com/bilalcaliskan/split-the-tunnel/cmd/cli/utils"
"github.com/bilalcaliskan/split-the-tunnel/internal/logging"

Expand All @@ -29,9 +27,7 @@ to quickly create a Cobra application.`,
RunE: func(cmd *cobra.Command, args []string) error {
logger := logging.GetLogger()

argsStr := strings.Join(args, " ")

logger.Info().Str("args", argsStr).Msg("list called")
logger.Info().Msg("list called")

req := cmd.Name()
res, err := utils.SendCommandToDaemon(utils.SocketPath, req)
Expand Down
31 changes: 24 additions & 7 deletions cmd/cli/utils/utils.go
Original file line number Diff line number Diff line change
@@ -1,9 +1,10 @@
package utils

import (
"bufio"
"errors"
"encoding/json"
"net"

"github.com/pkg/errors"
)

const SocketPath = "/tmp/mydaemon.sock"
Expand All @@ -13,23 +14,39 @@ var (
ErrTooManyArgs = errors.New("too many arguments provided")
)

type DaemonResponse struct {
Success bool `json:"success"`
Response string `json:"response"`
Error string `json:"error"`
}

func SendCommandToDaemon(socketPath, command string) (string, error) {
conn, err := net.Dial("unix", socketPath)
if err != nil {
return "", err
return "", errors.Wrap(err, "failed to connect to unix domain socket")
}
defer conn.Close()

_, err = conn.Write([]byte(command + "\n"))
if err != nil {
return "", err
return "", errors.Wrap(err, "failed to write to unix domain socket")
}

// Read response from daemon
response, err := bufio.NewReader(conn).ReadString('\n')
buf := make([]byte, 1024)
n, err := conn.Read(buf[:])
if err != nil {
return "", err
}

return response, nil
var response DaemonResponse
if err := json.Unmarshal(buf[:n], &response); err != nil {
return "", err
}

var respErr error
if response.Error != "" {
respErr = errors.New(response.Error)
}

return response.Response, respErr
}
1 change: 1 addition & 0 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ module github.com/bilalcaliskan/split-the-tunnel
go 1.21

require (
github.com/pkg/errors v0.9.1
github.com/rs/zerolog v1.31.0
github.com/spf13/cobra v1.8.0
github.com/stretchr/testify v1.8.4
Expand Down
1 change: 1 addition & 0 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ github.com/mattn/go-colorable v0.1.13/go.mod h1:7S9/ev0klgBDR4GtXTXX8a3vIGJpMovk
github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM=
github.com/mattn/go-isatty v0.0.19 h1:JITubQf0MOLdlGRuRq+jtsDlekdYPia9ZFsB8h/APPA=
github.com/mattn/go-isatty v0.0.19/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y=
github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
Expand Down
207 changes: 144 additions & 63 deletions internal/ipc/ipc.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,16 +2,26 @@ package ipc

import (
"bufio"
"errors"
"encoding/json"
"fmt"
"io"
"net"
"os"
"strings"

"github.com/bilalcaliskan/split-the-tunnel/internal/utils"

"github.com/bilalcaliskan/split-the-tunnel/internal/state"
"github.com/pkg/errors"
"github.com/rs/zerolog"
)

type DaemonResponse struct {
Success bool `json:"success"`
Response string `json:"response"`
Error string `json:"error"`
}

func InitIPC(path string, logger zerolog.Logger) error {
// Check and remove the socket file if it already exists
if _, err := os.Stat(path); err == nil {
Expand Down Expand Up @@ -63,105 +73,176 @@ func handleConnection(conn net.Conn, logger zerolog.Logger) {

st := new(state.State)
if err := st.Read("/tmp/state.json"); err != nil {
logger.Error().Str("path", "/tmp/state.json").Err(err).Msg("failed to read state")
logger.Error().Err(err).Msg("failed to read state")
continue
}

logger.Info().Any("state", st).Msg("read state")

if err := processCommand(command, conn); err != nil {
logger.Error().Str("command", command).Err(err).Msg("error processing command")
// get default gateway
gw, err := utils.GetDefaultNonVPNGateway()
if err != nil {
logger.Error().Err(err).Msg("failed to get default gateway")
continue
}

logger.Info().Str("command", command).Msg("command processed successfully")
processCommand(logger, command, gw, conn, st)
}
}

func processCommand(command string, conn net.Conn) error {
func processCommand(logger zerolog.Logger, command, gateway string, conn net.Conn, st *state.State) {
parts := strings.Fields(command)
if len(parts) == 0 {
return errors.New("empty command received")
logger.Error().Msg("empty command received")
return
}

switch parts[0] {
case "add":
if len(parts) < 2 {
_, err := conn.Write([]byte("'add' command requires at least a domain name\n"))
if err != nil {
return err
}
logger = logger.With().Str("operation", "add").Logger()

//if len(parts) < 2 {
// errMsg := fmt.Sprintf("'%s' command requires at least a domain name", parts[0])
//
// res := &DaemonResponse{
// Success: false,
// Response: "",
// Error: errMsg,
// }
//
// responseJson, err := json.Marshal(res)
// if err != nil {
// logger.Error().Err(err).Msg("failed to marshal response")
// return
// }
//
// if _, err := conn.Write(responseJson); err != nil {
// logger.Error().Err(err).Msg("failed to write response to unix domain socket")
// return
// }
//
// logger.Error().Msg(errMsg)
//}

handleAddCommand(logger, gateway, parts[1:], conn, st)
//case "remove":
// logger = logger.With().Str("operation", "remove").Logger()
//
// handleRemoveCommand(parts[1:], conn)
//case "list":
// logger = logger.With().Str("operation", "remove").Logger()
//
// handleListCommand(conn)
}
}

return errors.New("'add' command requires at least a domain name")
}
func handleAddCommand(logger zerolog.Logger, gw string, domains []string, conn net.Conn, st *state.State) {
logger = logger.With().Str("operation", "add").Logger()

for _, domain := range domains {
response := new(DaemonResponse)

return handleAddCommand(parts[1:], conn)
case "remove":
if len(parts) < 2 {
_, err := conn.Write([]byte("'remove' command requires at least a domain name\n"))
ip, err := utils.ResolveDomain(domain)
if err != nil {
response.Success = false
response.Response = ""
response.Error = errors.Wrap(err, "failed to resolve domain").Error()

responseJson, err := json.Marshal(response)
if err != nil {
return err
logger.Error().
Err(err).
Str("domain", domain).
Msg("failed to marshal response object")
continue
}

if _, err := conn.Write(responseJson); err != nil {
logger.Error().
Err(err).
Str("domain", domain).
Msg("failed to write response to unix domain socket")
continue
}

return errors.New("'remove' command requires at least a domain name")
continue
}

re := &state.RouteEntry{
Domain: domain,
ResolvedIP: ip[0],
Gateway: gw,
}

return handleRemoveCommand(parts[1:], conn)
case "list":
if len(parts) != 1 {
_, err := conn.Write([]byte("'list' command does not accept any arguments\n"))
if err := st.AddEntry(re); err != nil {
response.Success = false
response.Response = ""
response.Error = errors.Wrap(err, "failed to write RouteEntry to state").Error()

responseJson, err := json.Marshal(response)
if err != nil {
return err
logger.Error().
Err(err).
Str("domain", domain).
Msg("failed to marshal response object")
continue
}

return errors.New("'list' command does not accept any arguments")
if _, err := conn.Write(responseJson); err != nil {
logger.Error().
Err(err).
Str("domain", domain).
Msg("failed to write response to unix domain socket")
continue
}
}

return handleListCommand(conn)
default:
_, err := conn.Write([]byte("unknown command received\n"))
return err
}
}

func handleAddCommand(domains []string, conn net.Conn) error {
// Add the domain to the routing table
// ...
response.Success = false
response.Response = fmt.Sprintf("added route for " + domain)
response.Error = ""

for _, domain := range domains {
// Send a response to the client
_, err := conn.Write([]byte("added route for " + domain + "\n"))
responseJson, err := json.Marshal(response)
if err != nil {
return err
logger.Error().
Err(err).
Str("domain", domain).
Msg("failed to marshal response object")
continue
}
}

return nil
}

func handleRemoveCommand(domains []string, conn net.Conn) error {
// Remove the domain from the routing table
// ...

for _, domain := range domains {
// Send a response to the client
_, err := conn.Write([]byte("removed route for " + domain + "\n"))
_, err = conn.Write(responseJson)
if err != nil {
return err
logger.Error().
Err(err).
Str("domain", domain).
Msg("failed to write response to unix domain socket")
continue
}
}

return nil
}

func handleListCommand(conn net.Conn) error {
// List the domains that we manage from the routing table
// ...

// Send a response to the client
_, err := conn.Write([]byte("listing routes\n"))
return err
}
//func handleRemoveCommand(domains []string, conn net.Conn) error {
// // Remove the domain from the routing table
// // ...
//
// for _, domain := range domains {
// // Send a response to the client
// _, err := conn.Write([]byte("removed route for " + domain + "\n"))
// if err != nil {
// return err
// }
// }
//
// return nil
//}

//func handleListCommand(conn net.Conn) error {
// // List the domains that we manage from the routing table
// // ...
//
// // Send a response to the client
// _, err := conn.Write([]byte("listing routes\n"))
// return err
//}

func Cleanup(path string) error {
// Perform any cleanup and shutdown tasks here
Expand Down
Loading

0 comments on commit 5145665

Please sign in to comment.