diff options
Diffstat (limited to 'internal/ctlsock')
-rw-r--r-- | internal/ctlsock/ctlsock_serve.go | 137 |
1 files changed, 137 insertions, 0 deletions
diff --git a/internal/ctlsock/ctlsock_serve.go b/internal/ctlsock/ctlsock_serve.go new file mode 100644 index 0000000..571260d --- /dev/null +++ b/internal/ctlsock/ctlsock_serve.go @@ -0,0 +1,137 @@ +// Package ctlsock implementes the control socket interface that can be +// activated by passing "-ctlsock" on the command line. +package ctlsock + +import ( + "encoding/json" + "errors" + "io" + "net" + "os" + "syscall" + + "github.com/rfjakob/gocryptfs/internal/tlog" +) + +// Interface should be implemented by fusefrontend[_reverse] +type Interface interface { + EncryptPath(string) (string, error) + DecryptPath(string) (string, error) +} + +// RequestStruct is sent by a client +type RequestStruct struct { + EncryptPath string + DecryptPath string +} + +// ResponseStruct is sent by us as response to a request +type ResponseStruct struct { + // Result is the resulting decrypted or encrypted path. Empty on error. + Result string + // ErrNo is the error number as defined in errno.h. + // 0 means success and -1 means that the error number is not known + // (look at ErrText in this case). + ErrNo int32 + // ErrText is a detailed error message. + ErrText string +} + +type ctlSockHandler struct { + fs Interface + socket *net.UnixListener +} + +// CreateAndServe creates an unix socket at "path" and serves incoming +// connections in a new goroutine. +func CreateAndServe(path string, fs Interface) error { + sock, err := net.Listen("unix", path) + if err != nil { + return err + } + handler := ctlSockHandler{ + fs: fs, + socket: sock.(*net.UnixListener), + } + go handler.acceptLoop() + return nil +} + +func (ch *ctlSockHandler) acceptLoop() { + for { + conn, err := ch.socket.Accept() + if err != nil { + tlog.Warn.Printf("ctlsock: Accept error: %v", err) + break + } + go ch.handleConnection(conn.(*net.UnixConn)) + } +} + +func (ch *ctlSockHandler) handleConnection(conn *net.UnixConn) { + // 2*PATH_MAX is definitely big enough for requests to decrypt or + // encrypt paths. + buf := make([]byte, 2*syscall.PathMax) + for { + n, err := conn.Read(buf) + if err == io.EOF { + conn.Close() + return + } else if err != nil { + tlog.Warn.Printf("ctlsock: Read error: %#v", err) + conn.Close() + return + } + buf = buf[:n] + var in RequestStruct + err = json.Unmarshal(buf, &in) + if err != nil { + tlog.Warn.Printf("ctlsock: Unmarshal error: %#v", err) + errorMsg := ResponseStruct{ + ErrNo: int32(syscall.EINVAL), + ErrText: err.Error(), + } + sendResponse(&errorMsg, conn) + } + ch.handleRequest(&in, conn) + // Restore original size. + buf = buf[:cap(buf)] + } +} + +func (ch *ctlSockHandler) handleRequest(in *RequestStruct, conn *net.UnixConn) { + var err error + var out ResponseStruct + if in.DecryptPath != "" && in.EncryptPath != "" { + err = errors.New("Ambigous") + } else if in.DecryptPath == "" && in.EncryptPath == "" { + err = errors.New("No operation") + } else if in.DecryptPath != "" { + out.Result, err = ch.fs.DecryptPath(in.DecryptPath) + } else if in.EncryptPath != "" { + out.Result, err = ch.fs.EncryptPath(in.EncryptPath) + } + if err != nil { + out.ErrText = err.Error() + out.ErrNo = -1 + // Try to extract the actual error number + if pe, ok := err.(*os.PathError); ok { + if se, ok := pe.Err.(syscall.Errno); ok { + out.ErrNo = int32(se) + } + } + } + sendResponse(&out, conn) +} + +func sendResponse(msg *ResponseStruct, conn *net.UnixConn) { + jsonMsg, err := json.Marshal(msg) + if err != nil { + tlog.Warn.Printf("ctlsock: Marshal failed: %v", err) + return + } + _, err = conn.Write(jsonMsg) + if err != nil { + tlog.Warn.Printf("ctlsock: Write failed: %v", err) + } +} |