123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229 |
- package daemon
- import (
- "bytes"
- _ "embed"
- "fmt"
- "io"
- "log"
- "net"
- "os"
- "os/signal"
- "syscall"
- "git.aetherial.dev/aeth/yosai/pkg/config"
- daemonproto "git.aetherial.dev/aeth/yosai/pkg/daemon-proto"
- "git.aetherial.dev/aeth/yosai/pkg/keytags"
- "git.aetherial.dev/aeth/yosai/pkg/secrets/keyring"
- )
- type Context struct {
- conn net.Listener
- keyring *keyring.ApiKeyRing
- Keytags keytags.Keytagger
- routes map[string]Router
- sockPath string
- Config *config.Configuration
- servers []config.VpnServer
- rwBuffer bytes.Buffer
- stream io.Writer
- }
- /*
- Show all of the route information for the context
- :param msg: a message to parse from the daemon socket
- */
- func (c *Context) ShowRoutesHandler(msg daemonproto.SockMessage) daemonproto.SockMessage {
- var data string
- for k, v := range c.routes {
- data = data + k + "\n"
- routes := v.Routes()
- for i := range routes {
- data = data + "\u0009" + string(i) + "\n"
- }
- data = data + "\n"
- }
- return *daemonproto.NewSockMessage(daemonproto.MsgResponse, daemonproto.REQUEST_OK, []byte(data))
- }
- /*
- Context router
- */
- type ContextRouter struct {
- routes map[daemonproto.Method]func(daemonproto.SockMessage) daemonproto.SockMessage
- }
- func (c *ContextRouter) Register(method daemonproto.Method, callable func(daemonproto.SockMessage) daemonproto.SockMessage) {
- c.routes[method] = callable
- }
- func (c *ContextRouter) Routes() map[daemonproto.Method]func(daemonproto.SockMessage) daemonproto.SockMessage {
- return c.routes
- }
- func NewContextRouter() *ContextRouter {
- return &ContextRouter{routes: map[daemonproto.Method]func(daemonproto.SockMessage) daemonproto.SockMessage{}}
- }
- /*
- Write a message back to the caller
- */
- func (c *Context) Respond(conn net.Conn) {
- conn.Write(c.rwBuffer.Bytes())
- }
- /*
- Log wrapper for the context struct
- :param data: string arguments to send to the logger
- */
- func (c *Context) Log(msg ...string) {
- dMsg := []string{"daemon.Context:"}
- dMsg = append(dMsg, msg...)
- c.Config.Log(dMsg...)
- }
- func (c *Context) Handle(conn net.Conn) {
- defer conn.Close()
- b := make([]byte, 2048)
- nr, err := conn.Read(b)
- if err != nil {
- c.Log(err.Error())
- return
- }
- req := c.parseRequest(b[0:nr])
- out := c.resolveRoute(req)
- _, err = conn.Write(daemonproto.Marshal(out))
- if err != nil {
- c.Log(err.Error())
- return
- }
- }
- /*
- spawns subroutines to listen for different syscalls
- */
- func (c *Context) handleSyscalls() {
- // Cleanup the sockfile.
- chanSig := make(chan os.Signal, 1)
- signal.Notify(chanSig, os.Interrupt, syscall.SIGTERM)
- go func() {
- <-chanSig
- os.Remove(c.sockPath)
- os.Exit(1)
- }()
- }
- /*
- Open a daemon context pointer
- */
- func NewContext(path string, rdr io.Writer, apiKeyring *keyring.ApiKeyRing, conf *config.Configuration) *Context {
- sock, err := net.Listen("unix", path)
- if err != nil {
- log.Fatal(err)
- }
- routes := map[string]Router{}
- buf := make([]byte, 1024)
- return &Context{conn: sock, sockPath: path, rwBuffer: *bytes.NewBuffer(buf), stream: rdr, keyring: apiKeyring,
- routes: routes, Config: conf, Keytags: keytags.ConstKeytag{}}
- }
- /*
- Register a function to the daemons function router
- :param name: the name to map the function to. This will dictate how the CLI will resolve a keyword to the target function
- :param callable: the callable that will be executed when the keyword from 'name' is passed to the CLI
- */
- func (c *Context) Register(name string, router Router) {
- c.routes[name] = router
- }
- /*
- Hold the execution context open and listen for input
- */
- func (c *Context) ListenAndServe() {
- c.handleSyscalls()
- for {
- conn, err := c.conn.Accept()
- if err != nil {
- log.Fatal(err)
- }
- go c.Handle(conn)
- }
- }
- /*
- Validate and parse a stream from the unix socket and return an Action
- :param msg: a byte array with the action and arguments
- */
- func (c *Context) parseRequest(msg []byte) daemonproto.SockMessage {
- c.Log("Recieved request to parse action. ", string(msg))
- return daemonproto.Unmarshal(msg)
- }
- /*
- Resolve an action to a function
- :param action: a parsed action from the sock stream
- */
- func (c *Context) resolveRoute(req daemonproto.SockMessage) daemonproto.SockMessage {
- router, ok := c.routes[req.Target]
- if !ok {
- err := InvalidAction{Msg: "Invalid Action", Action: req.Target}
- c.Log("Error finding a router for target: ", req.Target)
- return daemonproto.SockMessage{StatusMsg: daemonproto.UNRESOLVEABLE, Body: []byte(err.Error())}
- }
- method, err := daemonproto.MethodCheck(req.Method)
- if err != nil {
- c.Log("Error parsing request: ", string(daemonproto.Marshal(req)), err.Error())
- }
- handlerFunc, ok := router.Routes()[method]
- if !ok {
- err := InvalidAction{Msg: "Unimplemented method", Action: req.Method}
- c.Log("Error invoking the method: ", req.Method, "on the target: ", req.Target)
- return daemonproto.SockMessage{StatusMsg: daemonproto.UNRESOLVEABLE, Body: []byte(err.Error())}
- }
- return handlerFunc(req)
- }
- /*
- ###########################################
- ################ ERRORS ###################
- ###########################################
- */
- type InvalidAction struct {
- Msg string
- Action string
- }
- func (i *InvalidAction) Error() string {
- return fmt.Sprintf("Invalid action: '%s' parsed. Error: %s", i.Action, i.Msg)
- }
- type DaemonIoError struct {
- Msg []byte
- Action string
- }
- func (d *DaemonIoError) Error() string {
- return fmt.Sprintf("There was an error %s. Message: %s", d.Action, string(d.Msg))
- }
|