diff --git a/daemon/procmon/details.go b/daemon/procmon/details.go
new file mode 100644
index 00000000..d099ccf3
--- /dev/null
+++ b/daemon/procmon/details.go
@@ -0,0 +1,164 @@
+package procmon
+
+import (
+ "bufio"
+ "fmt"
+ "io/ioutil"
+ "os"
+ "strconv"
+ "strings"
+
+ "github.com/gustavo-iniguez-goya/opensnitch/daemon/core"
+)
+
+// GetInfo collects information of a process.
+func (p *Process) GetInfo() error {
+ if err := p.readPath(); err != nil {
+ return err
+ }
+ p.readCwd()
+ p.readCmdline()
+ p.readEnv()
+ p.readDescriptors()
+ p.readIOStats()
+ p.readStatus()
+ p.cleanPath()
+
+ return nil
+}
+
+func (p *Process) setCwd(cwd string) {
+ p.CWD = cwd
+}
+
+func (p *Process) readCwd() {
+ if link, err := os.Readlink(fmt.Sprintf("/proc/%d/cwd", p.ID)); err == nil {
+ p.CWD = link
+ }
+}
+
+// read and parse environment variables of a process.
+func (p *Process) readEnv() {
+ if data, err := ioutil.ReadFile(fmt.Sprintf("/proc/%d/environ", p.ID)); err == nil {
+ for _, s := range strings.Split(string(data), "\x00") {
+ parts := strings.SplitN(core.Trim(s), "=", 2)
+ if parts != nil && len(parts) == 2 {
+ key := core.Trim(parts[0])
+ val := core.Trim(parts[1])
+ p.Env[key] = val
+ }
+ }
+ }
+}
+
+func (p *Process) readPath() error {
+ linkName := fmt.Sprint("/proc/", p.ID, "/exe")
+ if _, err := os.Lstat(linkName); err != nil {
+ return err
+ }
+
+ if link, err := os.Readlink(linkName); err == nil {
+ p.Path = link
+ }
+
+ return nil
+}
+
+func (p *Process) readCmdline() {
+ if data, err := ioutil.ReadFile(fmt.Sprintf("/proc/%d/cmdline", p.ID)); err == nil {
+ for i, b := range data {
+ if b == 0x00 {
+ data[i] = byte(' ')
+ }
+ }
+
+ p.Args = make([]string, 0)
+
+ args := strings.Split(string(data), " ")
+ for _, arg := range args {
+ arg = core.Trim(arg)
+ if arg != "" {
+ p.Args = append(p.Args, arg)
+ }
+ }
+ }
+}
+
+func (p *Process) readDescriptors() {
+ f, err := os.Open(fmt.Sprint("/proc/", p.ID, "/fd/"))
+ if err != nil {
+ return
+ }
+ fDesc, err := f.Readdir(-1)
+ f.Close()
+ p.Descriptors = nil
+
+ for _, fd := range fDesc {
+ tempFd := &procDescriptors{
+ Name: fd.Name(),
+ }
+ if link, err := os.Readlink(fmt.Sprint("/proc/", p.ID, "/fd/", fd.Name())); err == nil {
+ tempFd.SymLink = link
+ if linkInfo, err := os.Lstat(link); err == nil {
+ tempFd.Size = linkInfo.Size()
+ tempFd.ModTime = linkInfo.ModTime()
+ }
+ }
+ p.Descriptors = append(p.Descriptors, tempFd)
+ }
+}
+
+func (p *Process) readIOStats() {
+ f, err := os.Open(fmt.Sprint("/proc/", p.ID, "/io"))
+ if err != nil {
+ return
+ }
+ defer f.Close()
+
+ p.IOStats = &procIOstats{}
+
+ scanner := bufio.NewScanner(f)
+ for scanner.Scan() {
+ s := strings.Split(scanner.Text(), " ")
+ switch s[0] {
+ case "rchar:":
+ p.IOStats.RChar, _ = strconv.ParseInt(s[1], 10, 64)
+ case "wchar:":
+ p.IOStats.WChar, _ = strconv.ParseInt(s[1], 10, 64)
+ case "syscr:":
+ p.IOStats.SyscallRead, _ = strconv.ParseInt(s[1], 10, 64)
+ case "syscw:":
+ p.IOStats.SyscallWrite, _ = strconv.ParseInt(s[1], 10, 64)
+ case "read_bytes:":
+ p.IOStats.ReadBytes, _ = strconv.ParseInt(s[1], 10, 64)
+ case "write_bytes:":
+ p.IOStats.WriteBytes, _ = strconv.ParseInt(s[1], 10, 64)
+ }
+ }
+}
+
+func (p *Process) readStatus() {
+ if data, err := ioutil.ReadFile(fmt.Sprint("/proc/", p.ID, "/status")); err == nil {
+ p.Status = string(data)
+ }
+ if data, err := ioutil.ReadFile(fmt.Sprint("/proc/", p.ID, "/stat")); err == nil {
+ p.Stat = string(data)
+ }
+ if data, err := ioutil.ReadFile(fmt.Sprint("/proc/", p.ID, "/stack")); err == nil {
+ p.Stack = string(data)
+ }
+ if data, err := ioutil.ReadFile(fmt.Sprint("/proc/", p.ID, "/maps")); err == nil {
+ p.Maps = string(data)
+ }
+ if data, err := ioutil.ReadFile(fmt.Sprint("/proc/", p.ID, "/statm")); err == nil {
+ p.Statm = &procStatm{}
+ fmt.Sscanf(string(data), "%d %d %d %d %d %d %d", &p.Statm.Size, &p.Statm.Resident, &p.Statm.Shared, &p.Statm.Text, &p.Statm.Lib, &p.Statm.Data, &p.Statm.Dt)
+ }
+}
+
+func (p *Process) cleanPath() {
+ pathLen := len(p.Path)
+ if pathLen >= 10 && p.Path[pathLen-10:] == " (deleted)" {
+ p.Path = p.Path[:len(p.Path)-10]
+ }
+}
diff --git a/daemon/procmon/parse.go b/daemon/procmon/parse.go
index f948ead2..45c49188 100644
--- a/daemon/procmon/parse.go
+++ b/daemon/procmon/parse.go
@@ -2,12 +2,10 @@ package procmon
import (
"fmt"
- "io/ioutil"
"os"
"strings"
"time"
- "github.com/gustavo-iniguez-goya/opensnitch/daemon/core"
"github.com/gustavo-iniguez-goya/opensnitch/daemon/log"
"github.com/gustavo-iniguez-goya/opensnitch/daemon/procmon/audit"
)
@@ -85,50 +83,6 @@ func GetPIDFromINode(inode int, inodeKey string) int {
return found
}
-func cleanPath(proc *Process) {
- pathLen := len(proc.Path)
- if pathLen >= 10 && proc.Path[pathLen-10:] == " (deleted)" {
- proc.Path = proc.Path[:len(proc.Path)-10]
- }
-}
-
-func parseCmdLine(proc *Process) {
- if data, err := ioutil.ReadFile(fmt.Sprintf("/proc/%d/cmdline", proc.ID)); err == nil {
- for i, b := range data {
- if b == 0x00 {
- data[i] = byte(' ')
- }
- }
-
- args := strings.Split(string(data), " ")
- for _, arg := range args {
- arg = core.Trim(arg)
- if arg != "" {
- proc.Args = append(proc.Args, arg)
- }
- }
- }
-}
-
-func parseCWD(proc *Process) {
- if link, err := os.Readlink(fmt.Sprintf("/proc/%d/cwd", proc.ID)); err == nil {
- proc.CWD = link
- }
-}
-
-func parseEnv(proc *Process) {
- if data, err := ioutil.ReadFile(fmt.Sprintf("/proc/%d/environ", proc.ID)); err == nil {
- for _, s := range strings.Split(string(data), "\x00") {
- parts := strings.SplitN(core.Trim(s), "=", 2)
- if parts != nil && len(parts) == 2 {
- key := core.Trim(parts[0])
- val := core.Trim(parts[1])
- proc.Env[key] = val
- }
- }
- }
-}
-
// FindProcess checks if a process exists given a PID.
// If it exists in /proc, a new Process{} object is returned with the details
// to identify a process (cmdline, name, environment variables, etc).
@@ -141,14 +95,14 @@ func FindProcess(pid int, interceptUnknown bool) *Process {
audit.Lock.RLock()
proc := NewProcess(pid, aevent.ProcPath)
proc.Args = strings.Split(strings.Replace(aevent.ProcCmdLine, "\x00", " ", -1), " ")
- proc.CWD = aevent.ProcDir
+ proc.setCwd(aevent.ProcDir)
audit.Lock.RUnlock()
// if the proc dir contains non alhpa-numeric chars the field is empty
if proc.CWD == "" {
- parseCWD(proc)
+ proc.readCwd()
}
- parseEnv(proc)
- cleanPath(proc)
+ proc.readEnv()
+ proc.cleanPath()
return proc
}
@@ -162,10 +116,10 @@ func FindProcess(pid int, interceptUnknown bool) *Process {
if link, err := os.Readlink(linkName); err == nil {
proc := NewProcess(pid, link)
- parseCmdLine(proc)
- parseCWD(proc)
- parseEnv(proc)
- cleanPath(proc)
+ proc.readCmdline()
+ proc.readCwd()
+ proc.readEnv()
+ proc.cleanPath()
return proc
}
diff --git a/daemon/procmon/process.go b/daemon/procmon/process.go
index 574d672f..fafd4dd5 100644
--- a/daemon/procmon/process.go
+++ b/daemon/procmon/process.go
@@ -1,17 +1,53 @@
package procmon
import (
+ "time"
+
"github.com/gustavo-iniguez-goya/opensnitch/daemon/log"
"github.com/gustavo-iniguez-goya/opensnitch/daemon/procmon/audit"
)
-// Process holds the information of a process.
+// man 5 proc; man procfs
+type procIOstats struct {
+ RChar int64
+ WChar int64
+ SyscallRead int64
+ SyscallWrite int64
+ ReadBytes int64
+ WriteBytes int64
+}
+
+type procDescriptors struct {
+ Name string
+ SymLink string
+ Size int64
+ ModTime time.Time
+}
+
+type procStatm struct {
+ Size int64
+ Resident int64
+ Shared int64
+ Text int64
+ Lib int64
+ Data int64 // data + stack
+ Dt int
+}
+
+// Process holds the details of a process.
type Process struct {
- ID int
- Path string
- Args []string
- Env map[string]string
- CWD string
+ ID int
+ Path string
+ Args []string
+ Env map[string]string
+ CWD string
+ Descriptors []*procDescriptors
+ IOStats *procIOstats
+ Status string
+ Stat string
+ Statm *procStatm
+ Stack string
+ Maps string
}
// NewProcess returns a new Process structure.
diff --git a/daemon/ui/notifications.go b/daemon/ui/notifications.go
index 416012d3..9af18bcb 100644
--- a/daemon/ui/notifications.go
+++ b/daemon/ui/notifications.go
@@ -1,9 +1,11 @@
package ui
import (
+ "encoding/json"
"fmt"
"io"
"io/ioutil"
+ "strconv"
"strings"
"time"
@@ -16,6 +18,8 @@ import (
"golang.org/x/net/context"
)
+var stopMonitoringProcess = make(chan int)
+
// NewReply constructs a new protocol notification reply
func NewReply(rID uint64, replyCode protocol.NotificationReplyCode, data string) *protocol.NotificationReply {
return &protocol.NotificationReply{
@@ -48,13 +52,42 @@ func (c *Client) getClientConfig() *protocol.ClientConfig {
}
}
+func (c *Client) monitorProcessDetails(pid int, stream protocol.UI_NotificationsClient, notification *protocol.Notification) {
+ p := procmon.NewProcess(pid, "")
+ ticker := time.NewTicker(2 * time.Second)
+
+ for {
+ select {
+ case _pid := <-stopMonitoringProcess:
+ if _pid != pid {
+ continue
+ }
+ goto Exit
+ case <-ticker.C:
+ if err := p.GetInfo(); err != nil {
+ c.sendNotificationReply(stream, notification.Id, notification.Data, err)
+ goto Exit
+ }
+
+ pJSON, err := json.Marshal(p)
+ notification.Data = string(pJSON)
+ if errs := c.sendNotificationReply(stream, notification.Id, notification.Data, err); errs != nil {
+ goto Exit
+ }
+ }
+ }
+
+Exit:
+ ticker.Stop()
+}
+
func (c *Client) handleActionChangeConfig(stream protocol.UI_NotificationsClient, notification *protocol.Notification) {
log.Info("[notification] Reloading configuration")
// Parse received configuration first, to get the new proc monitor method.
newConf, err := c.parseConf(notification.Data)
if err != nil {
log.Warning("[notification] error parsing received config: %v", notification.Data)
- c.sendNotificationReply(stream, notification.Id, err)
+ c.sendNotificationReply(stream, notification.Id, "", err)
return
}
@@ -73,7 +106,7 @@ func (c *Client) handleActionChangeConfig(stream protocol.UI_NotificationsClient
procmon.Init()
}
- c.sendNotificationReply(stream, notification.Id, err)
+ c.sendNotificationReply(stream, notification.Id, "", err)
}
func (c *Client) handleActionEnableRule(stream protocol.UI_NotificationsClient, notification *protocol.Notification) {
@@ -86,7 +119,7 @@ func (c *Client) handleActionEnableRule(stream protocol.UI_NotificationsClient,
// save to disk only if the duration is rule.Always
err = c.rules.Replace(r, r.Duration == rule.Always)
}
- c.sendNotificationReply(stream, notification.Id, err)
+ c.sendNotificationReply(stream, notification.Id, "", err)
}
func (c *Client) handleActionDisableRule(stream protocol.UI_NotificationsClient, notification *protocol.Notification) {
@@ -97,7 +130,7 @@ func (c *Client) handleActionDisableRule(stream protocol.UI_NotificationsClient,
r.Enabled = false
err = c.rules.Replace(r, r.Duration == rule.Always)
}
- c.sendNotificationReply(stream, notification.Id, err)
+ c.sendNotificationReply(stream, notification.Id, "", err)
}
func (c *Client) handleActionChangeRule(stream protocol.UI_NotificationsClient, notification *protocol.Notification) {
@@ -114,7 +147,7 @@ func (c *Client) handleActionChangeRule(stream protocol.UI_NotificationsClient,
rErr = err
}
}
- c.sendNotificationReply(stream, notification.Id, rErr)
+ c.sendNotificationReply(stream, notification.Id, "", rErr)
}
func (c *Client) handleActionDeleteRule(stream protocol.UI_NotificationsClient, notification *protocol.Notification) {
@@ -126,23 +159,53 @@ func (c *Client) handleActionDeleteRule(stream protocol.UI_NotificationsClient,
log.Error("[notification] Error deleting rule: ", err, rul)
}
}
- c.sendNotificationReply(stream, notification.Id, err)
+ c.sendNotificationReply(stream, notification.Id, "", err)
+}
+
+func (c *Client) handleActionMonitorProcess(stream protocol.UI_NotificationsClient, notification *protocol.Notification) {
+ pid, err := strconv.Atoi(notification.Data)
+ if err != nil {
+ log.Error("parsing PID to monitor")
+ return
+ }
+ if !core.Exists(fmt.Sprint("/proc/", pid)) {
+ c.sendNotificationReply(stream, notification.Id, "", fmt.Errorf("The process is no longer running"))
+ return
+ }
+ go c.monitorProcessDetails(pid, stream, notification)
+}
+
+func (c *Client) handleActionStopMonitorProcess(stream protocol.UI_NotificationsClient, notification *protocol.Notification) {
+ pid, err := strconv.Atoi(notification.Data)
+ if err != nil {
+ log.Error("parsing PID to stop monitor")
+ c.sendNotificationReply(stream, notification.Id, "", fmt.Errorf("Error stopping monitor: ", notification.Data))
+ return
+ }
+ stopMonitoringProcess <- pid
+ c.sendNotificationReply(stream, notification.Id, "", nil)
}
func (c *Client) handleNotification(stream protocol.UI_NotificationsClient, notification *protocol.Notification) {
switch {
+ case notification.Type == protocol.Action_MONITOR_PROCESS:
+ c.handleActionMonitorProcess(stream, notification)
+
+ case notification.Type == protocol.Action_STOP_MONITOR_PROCESS:
+ c.handleActionStopMonitorProcess(stream, notification)
+
case notification.Type == protocol.Action_CHANGE_CONFIG:
c.handleActionChangeConfig(stream, notification)
case notification.Type == protocol.Action_LOAD_FIREWALL:
log.Info("[notification] starting firewall")
firewall.Init(nil)
- c.sendNotificationReply(stream, notification.Id, nil)
+ c.sendNotificationReply(stream, notification.Id, "", nil)
case notification.Type == protocol.Action_UNLOAD_FIREWALL:
log.Info("[notification] stopping firewall")
firewall.Stop(nil)
- c.sendNotificationReply(stream, notification.Id, nil)
+ c.sendNotificationReply(stream, notification.Id, "", nil)
// ENABLE_RULE just replaces the rule on disk
case notification.Type == protocol.Action_ENABLE_RULE:
@@ -160,15 +223,18 @@ func (c *Client) handleNotification(stream protocol.UI_NotificationsClient, noti
}
}
-func (c *Client) sendNotificationReply(stream protocol.UI_NotificationsClient, nID uint64, err error) {
- reply := NewReply(nID, protocol.NotificationReplyCode_OK, "")
+func (c *Client) sendNotificationReply(stream protocol.UI_NotificationsClient, nID uint64, data string, err error) error {
+ reply := NewReply(nID, protocol.NotificationReplyCode_OK, data)
if err != nil {
reply.Code = protocol.NotificationReplyCode_ERROR
reply.Data = fmt.Sprint(err)
}
if err := stream.Send(reply); err != nil {
log.Error("Error replying to notification:", err, reply.Id)
+ return err
}
+
+ return nil
}
// Subscribe opens a connection with the server (UI), to start
diff --git a/daemon/ui/protocol/ui.pb.go b/daemon/ui/protocol/ui.pb.go
index a2ea75ee..6e4f6e8e 100644
--- a/daemon/ui/protocol/ui.pb.go
+++ b/daemon/ui/protocol/ui.pb.go
@@ -27,42 +27,48 @@ const _ = proto.ProtoPackageIsVersion2 // please upgrade the proto package
type Action int32
const (
- Action_NONE Action = 0
- Action_LOAD_FIREWALL Action = 1
- Action_UNLOAD_FIREWALL Action = 2
- Action_CHANGE_CONFIG Action = 3
- Action_ENABLE_RULE Action = 4
- Action_DISABLE_RULE Action = 5
- Action_DELETE_RULE Action = 6
- Action_CHANGE_RULE Action = 7
- Action_LOG_LEVEL Action = 8
- Action_STOP Action = 9
+ Action_NONE Action = 0
+ Action_LOAD_FIREWALL Action = 1
+ Action_UNLOAD_FIREWALL Action = 2
+ Action_CHANGE_CONFIG Action = 3
+ Action_ENABLE_RULE Action = 4
+ Action_DISABLE_RULE Action = 5
+ Action_DELETE_RULE Action = 6
+ Action_CHANGE_RULE Action = 7
+ Action_LOG_LEVEL Action = 8
+ Action_STOP Action = 9
+ Action_MONITOR_PROCESS Action = 10
+ Action_STOP_MONITOR_PROCESS Action = 11
)
var Action_name = map[int32]string{
- 0: "NONE",
- 1: "LOAD_FIREWALL",
- 2: "UNLOAD_FIREWALL",
- 3: "CHANGE_CONFIG",
- 4: "ENABLE_RULE",
- 5: "DISABLE_RULE",
- 6: "DELETE_RULE",
- 7: "CHANGE_RULE",
- 8: "LOG_LEVEL",
- 9: "STOP",
+ 0: "NONE",
+ 1: "LOAD_FIREWALL",
+ 2: "UNLOAD_FIREWALL",
+ 3: "CHANGE_CONFIG",
+ 4: "ENABLE_RULE",
+ 5: "DISABLE_RULE",
+ 6: "DELETE_RULE",
+ 7: "CHANGE_RULE",
+ 8: "LOG_LEVEL",
+ 9: "STOP",
+ 10: "MONITOR_PROCESS",
+ 11: "STOP_MONITOR_PROCESS",
}
var Action_value = map[string]int32{
- "NONE": 0,
- "LOAD_FIREWALL": 1,
- "UNLOAD_FIREWALL": 2,
- "CHANGE_CONFIG": 3,
- "ENABLE_RULE": 4,
- "DISABLE_RULE": 5,
- "DELETE_RULE": 6,
- "CHANGE_RULE": 7,
- "LOG_LEVEL": 8,
- "STOP": 9,
+ "NONE": 0,
+ "LOAD_FIREWALL": 1,
+ "UNLOAD_FIREWALL": 2,
+ "CHANGE_CONFIG": 3,
+ "ENABLE_RULE": 4,
+ "DISABLE_RULE": 5,
+ "DELETE_RULE": 6,
+ "CHANGE_RULE": 7,
+ "LOG_LEVEL": 8,
+ "STOP": 9,
+ "MONITOR_PROCESS": 10,
+ "STOP_MONITOR_PROCESS": 11,
}
func (x Action) String() string {
@@ -711,88 +717,90 @@ func init() {
}
var fileDescriptor_63867a62624c1283 = []byte{
- // 1292 bytes of a gzipped FileDescriptorProto
- 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x94, 0x56, 0xdf, 0x73, 0xd3, 0xc6,
- 0x13, 0x8f, 0x1d, 0x5b, 0x96, 0xd6, 0x76, 0xe2, 0x1c, 0x84, 0xaf, 0xbe, 0xa6, 0x85, 0x60, 0x68,
- 0x9b, 0xc9, 0x74, 0x32, 0x6d, 0x60, 0x3a, 0xc0, 0xc0, 0x74, 0x8c, 0x11, 0xe0, 0xe2, 0xda, 0x99,
- 0x0b, 0xa1, 0x8f, 0x1a, 0x49, 0x77, 0x38, 0x57, 0x84, 0xa4, 0xea, 0x4e, 0xa6, 0xfe, 0x9b, 0xfa,
- 0x0f, 0xf4, 0xa9, 0xaf, 0x7d, 0xe9, 0x63, 0xff, 0x98, 0x3e, 0x76, 0xee, 0x4e, 0xb2, 0x94, 0x5f,
- 0x74, 0xf2, 0x64, 0xed, 0x67, 0xf7, 0xb3, 0xb7, 0xbb, 0xb7, 0xb7, 0x6b, 0x30, 0x33, 0xb6, 0x9f,
- 0xa4, 0xb1, 0x88, 0x91, 0xa9, 0x7e, 0x82, 0x38, 0x1c, 0x64, 0xd0, 0x74, 0x16, 0x34, 0x12, 0x08,
- 0x41, 0x43, 0xb0, 0x0f, 0xd4, 0xae, 0xed, 0xd4, 0x76, 0x2d, 0xac, 0xbe, 0xd1, 0x03, 0x80, 0x20,
- 0x8e, 0x22, 0x1a, 0x08, 0x16, 0x47, 0x76, 0x7d, 0xa7, 0xb6, 0xdb, 0x3e, 0xb8, 0xbe, 0x5f, 0x70,
- 0xf7, 0x47, 0x2b, 0x1d, 0xae, 0xd8, 0xa1, 0x01, 0x34, 0xd2, 0x2c, 0xa4, 0xf6, 0xba, 0xb2, 0xdf,
- 0x28, 0xed, 0x71, 0x16, 0x52, 0xac, 0x74, 0x83, 0x3f, 0x4d, 0x80, 0x23, 0xe1, 0x09, 0xc6, 0x05,
- 0x0b, 0x38, 0xfa, 0x02, 0x36, 0x88, 0x47, 0x3f, 0xc4, 0x91, 0xbb, 0xa0, 0x29, 0x97, 0x87, 0xe9,
- 0x30, 0xba, 0x1a, 0x7d, 0xab, 0x41, 0x74, 0x1d, 0x9a, 0x92, 0xcd, 0x55, 0x28, 0x0d, 0xac, 0x05,
- 0x74, 0x03, 0x8c, 0x2c, 0x51, 0xb1, 0xaf, 0x2b, 0x38, 0x97, 0xd0, 0x5d, 0xe8, 0x92, 0x88, 0xbb,
- 0x29, 0xe5, 0x49, 0x1c, 0x71, 0xca, 0xed, 0x86, 0x52, 0x77, 0x48, 0xc4, 0x71, 0x81, 0xa1, 0x1d,
- 0x68, 0x97, 0xa1, 0x73, 0xbb, 0xa9, 0x4c, 0xaa, 0x10, 0xb2, 0xa1, 0xc5, 0xe6, 0x51, 0x9c, 0x52,
- 0x62, 0x1b, 0x4a, 0x5b, 0x88, 0xa8, 0x0f, 0xa6, 0x17, 0x04, 0x34, 0x11, 0x94, 0xd8, 0x2d, 0xa5,
- 0x5a, 0xc9, 0x92, 0x45, 0xd2, 0x38, 0x49, 0x28, 0xb1, 0x4d, 0xcd, 0xca, 0x45, 0x74, 0x13, 0x2c,
- 0x19, 0xb7, 0x7b, 0xc2, 0x04, 0xb7, 0x2d, 0x4d, 0x93, 0xc0, 0x2b, 0x26, 0x38, 0xba, 0x0d, 0x6d,
- 0xa5, 0xfc, 0xc0, 0xb8, 0x8c, 0x18, 0x94, 0x1a, 0x24, 0xf4, 0xa3, 0x42, 0xd0, 0x13, 0x30, 0xfd,
- 0xa5, 0xab, 0x4a, 0x6a, 0xb7, 0x77, 0xd6, 0x77, 0xdb, 0x07, 0x77, 0xca, 0x02, 0x97, 0x15, 0xdd,
- 0x7f, 0xb6, 0x3c, 0x94, 0xa8, 0x13, 0x89, 0x74, 0x89, 0x5b, 0xbe, 0x96, 0xd0, 0x33, 0x00, 0x7f,
- 0xe9, 0x7a, 0x84, 0xa4, 0x94, 0x73, 0xbb, 0xa3, 0xf8, 0x77, 0x2f, 0xe1, 0x0f, 0xb5, 0x95, 0xf6,
- 0x60, 0xf9, 0x85, 0x8c, 0x1e, 0x41, 0xcb, 0x5f, 0xba, 0x27, 0x31, 0x17, 0x76, 0x57, 0x39, 0xd8,
- 0xb9, 0xc4, 0xc1, 0xab, 0x98, 0x0b, 0xcd, 0x36, 0x7c, 0x25, 0xe4, 0xd4, 0x24, 0x4e, 0x85, 0xbd,
- 0xf1, 0x49, 0xea, 0x61, 0x9c, 0x96, 0x54, 0x29, 0xa0, 0xef, 0xc0, 0xf0, 0x97, 0x6e, 0xc6, 0x88,
- 0xbd, 0xa9, 0x98, 0xb7, 0x2f, 0x61, 0x1e, 0x33, 0xa2, 0x89, 0x4d, 0x5f, 0x7e, 0xa3, 0xd7, 0xd0,
- 0xf5, 0x97, 0x2e, 0xfd, 0x95, 0x06, 0x99, 0xf0, 0xfc, 0x90, 0xda, 0x3d, 0x45, 0xff, 0xf2, 0x12,
- 0xba, 0xb3, 0x32, 0xd4, 0x5e, 0x3a, 0x7e, 0x05, 0x42, 0x5f, 0x81, 0x41, 0xe5, 0x63, 0xe1, 0xf6,
- 0x96, 0xf2, 0xb2, 0x59, 0x7a, 0x51, 0x8f, 0x08, 0xe7, 0xea, 0xfe, 0x63, 0xe8, 0x54, 0x2f, 0x00,
- 0xf5, 0x60, 0xfd, 0x3d, 0x5d, 0xe6, 0x4d, 0x2d, 0x3f, 0x65, 0x2b, 0x2f, 0xbc, 0x30, 0xa3, 0x45,
- 0x2b, 0x2b, 0xe1, 0x71, 0xfd, 0x61, 0xad, 0xff, 0x04, 0x36, 0x4e, 0x17, 0xff, 0x4a, 0xec, 0x47,
- 0xd0, 0xae, 0x54, 0xfe, 0xea, 0xd4, 0x55, 0xe5, 0xaf, 0x44, 0x7d, 0x08, 0x50, 0x96, 0xfe, 0x4a,
- 0xcc, 0xef, 0x61, 0xeb, 0x5c, 0xd5, 0xaf, 0xe2, 0x60, 0x30, 0x86, 0xf6, 0x21, 0x8b, 0xe6, 0x98,
- 0xfe, 0x92, 0x51, 0x2e, 0xd0, 0x06, 0xd4, 0x19, 0x51, 0xcc, 0x06, 0xae, 0x33, 0x82, 0xf6, 0xa0,
- 0xc9, 0x85, 0x27, 0xf8, 0xf9, 0xe9, 0x55, 0xde, 0x3b, 0xd6, 0x26, 0x83, 0x9b, 0x60, 0x69, 0x57,
- 0x49, 0xb8, 0x3c, 0xeb, 0x68, 0xf0, 0xd7, 0x3a, 0x40, 0x39, 0xf0, 0xe4, 0xdb, 0x2f, 0x3c, 0xe5,
- 0x71, 0xae, 0x64, 0xb4, 0x0d, 0x06, 0x4f, 0x03, 0x97, 0x25, 0xea, 0x50, 0x0b, 0x37, 0x79, 0x1a,
- 0x8c, 0x13, 0xf4, 0x7f, 0x30, 0x25, 0xac, 0xda, 0x5f, 0x4e, 0xaa, 0x2e, 0x6e, 0xf1, 0x34, 0x50,
- 0xdd, 0xbd, 0x0d, 0x06, 0xe1, 0x42, 0x32, 0x1a, 0x9a, 0x41, 0xb8, 0xd0, 0x0c, 0x09, 0xab, 0xb7,
- 0xd6, 0x54, 0x8a, 0x16, 0xe1, 0x42, 0x3d, 0xa5, 0x5c, 0xa5, 0x9c, 0x19, 0xda, 0x19, 0xe1, 0x42,
- 0x39, 0xfb, 0x1f, 0xb4, 0x32, 0x4e, 0x53, 0x97, 0xe9, 0xa9, 0xd4, 0xc5, 0x86, 0x14, 0xc7, 0x04,
- 0x7d, 0x0e, 0x90, 0xa4, 0x71, 0x40, 0x39, 0x97, 0x3a, 0x53, 0xe9, 0xac, 0x1c, 0x19, 0x13, 0x74,
- 0x07, 0x3a, 0x85, 0x3a, 0xf1, 0xc4, 0x89, 0x9a, 0x4d, 0x16, 0x6e, 0xe7, 0xd8, 0xa1, 0x27, 0x4e,
- 0xe4, 0x78, 0x2a, 0x4c, 0x82, 0x8f, 0x44, 0x8d, 0x27, 0x0b, 0x17, 0x4e, 0x47, 0x1f, 0x4f, 0xf9,
- 0xf0, 0xd2, 0x39, 0x57, 0x23, 0xaa, 0xf4, 0x31, 0x4c, 0xe7, 0x1c, 0x39, 0xa5, 0x0f, 0x1a, 0x2d,
- 0xf2, 0x21, 0x74, 0xef, 0xa2, 0xad, 0xb2, 0x7f, 0xa8, 0xed, 0x9c, 0x68, 0xa1, 0x5f, 0x63, 0x71,
- 0x92, 0x13, 0x2d, 0xfa, 0x4f, 0x61, 0xf3, 0x8c, 0xfa, 0xbf, 0xda, 0xc6, 0xaa, 0xb6, 0xcd, 0xcf,
- 0x60, 0xce, 0x12, 0x9a, 0x7a, 0x22, 0x4e, 0xd5, 0xea, 0x5b, 0x26, 0xe5, 0xea, 0x5b, 0x26, 0x54,
- 0xce, 0xef, 0x58, 0xea, 0x23, 0x92, 0x73, 0x0b, 0x51, 0x5a, 0x13, 0x4f, 0x78, 0xea, 0x0a, 0x2d,
- 0xac, 0xbe, 0xd1, 0x67, 0x60, 0x71, 0x1a, 0x71, 0x26, 0xd8, 0x82, 0xaa, 0x2b, 0x34, 0x71, 0x09,
- 0x0c, 0x7e, 0xaf, 0x41, 0x43, 0xee, 0x3e, 0x49, 0x8d, 0xbc, 0x72, 0xc7, 0xca, 0x6f, 0x79, 0x10,
- 0x8d, 0x64, 0xeb, 0xeb, 0x83, 0x4c, 0x5c, 0x88, 0xe8, 0x96, 0xbc, 0x2e, 0x1a, 0x50, 0x42, 0xa3,
- 0x40, 0xef, 0x36, 0x13, 0x57, 0x10, 0xb9, 0xf7, 0x3c, 0xbd, 0x99, 0x75, 0xd3, 0xe4, 0x92, 0x6c,
- 0x4d, 0x92, 0xa5, 0x9e, 0xd2, 0xe8, 0xae, 0x59, 0xc9, 0x68, 0x1f, 0xcc, 0x38, 0x4f, 0x5b, 0xb5,
- 0x4d, 0xfb, 0x00, 0x95, 0x95, 0x2f, 0x0a, 0x82, 0x57, 0x36, 0x83, 0xbf, 0x6b, 0xd0, 0x19, 0x85,
- 0x8c, 0x46, 0x62, 0x14, 0x47, 0xef, 0xd8, 0xfc, 0xdc, 0xfb, 0x2a, 0x52, 0xaa, 0x9f, 0x4e, 0xa9,
- 0x58, 0xe3, 0xba, 0x48, 0x85, 0x88, 0xbe, 0x86, 0x2d, 0xc6, 0x5f, 0xb0, 0x94, 0x7e, 0xf4, 0xc2,
- 0x10, 0x67, 0x51, 0xc4, 0xa2, 0x79, 0x5e, 0xaf, 0xf3, 0x0a, 0x99, 0x60, 0xa0, 0x4e, 0xcd, 0xd3,
- 0xc8, 0x25, 0x99, 0x60, 0x18, 0xcf, 0x27, 0x74, 0x41, 0xc3, 0xbc, 0xf7, 0x57, 0x32, 0xba, 0x57,
- 0xfc, 0x45, 0x68, 0xa9, 0xbe, 0x3a, 0xfb, 0xef, 0x43, 0x2b, 0x07, 0x7f, 0xd4, 0xa0, 0x33, 0x8d,
- 0x05, 0x7b, 0xc7, 0x02, 0x5d, 0x97, 0xb3, 0x69, 0xdd, 0x02, 0x08, 0x54, 0xda, 0xd3, 0x32, 0xb9,
- 0x0a, 0x22, 0xf5, 0x9c, 0xa6, 0x0b, 0x9a, 0x2a, 0xbd, 0xce, 0xb2, 0x82, 0xa0, 0x7b, 0x79, 0x4b,
- 0xc9, 0xdc, 0x36, 0x0e, 0x7a, 0x65, 0x14, 0x43, 0xfd, 0x7f, 0x49, 0x37, 0x59, 0xd1, 0x4a, 0xcd,
- 0x4a, 0x2b, 0xad, 0x12, 0x30, 0x3e, 0x95, 0x40, 0x08, 0x5b, 0xd5, 0xf8, 0x2f, 0x1c, 0x59, 0xe8,
- 0x3e, 0x34, 0x82, 0x98, 0xe8, 0xf0, 0x37, 0xaa, 0x1b, 0xf3, 0x1c, 0x75, 0x14, 0x13, 0x8a, 0x95,
- 0xf1, 0x45, 0xed, 0xbd, 0xf7, 0x5b, 0x0d, 0x0c, 0x1d, 0x38, 0x32, 0xa1, 0x31, 0x9d, 0x4d, 0x9d,
- 0xde, 0x1a, 0xda, 0x82, 0xee, 0x64, 0x36, 0x7c, 0xee, 0xbe, 0x18, 0x63, 0xe7, 0xa7, 0xe1, 0x64,
- 0xd2, 0xab, 0xa1, 0x6b, 0xb0, 0x79, 0x3c, 0x3d, 0x0d, 0xd6, 0xa5, 0xdd, 0xe8, 0xd5, 0x70, 0xfa,
- 0xd2, 0x71, 0x47, 0xb3, 0xe9, 0x8b, 0xf1, 0xcb, 0xde, 0x3a, 0xda, 0x84, 0xb6, 0x33, 0x1d, 0x3e,
- 0x9b, 0x38, 0x2e, 0x3e, 0x9e, 0x38, 0xbd, 0x06, 0xea, 0x41, 0xe7, 0xf9, 0xf8, 0xa8, 0x44, 0x9a,
- 0xd2, 0xe4, 0xb9, 0x33, 0x71, 0xde, 0xe4, 0x80, 0x21, 0x81, 0xdc, 0x8d, 0x02, 0x5a, 0xa8, 0x0b,
- 0xd6, 0x64, 0xf6, 0xd2, 0x9d, 0x38, 0x6f, 0x9d, 0x49, 0xcf, 0x94, 0x81, 0x1d, 0xbd, 0x99, 0x1d,
- 0xf6, 0xac, 0xbd, 0x3d, 0xd8, 0xbe, 0x30, 0x41, 0x64, 0x40, 0x7d, 0xf6, 0xba, 0xb7, 0x86, 0x2c,
- 0x68, 0x3a, 0x18, 0xcf, 0x70, 0xaf, 0x76, 0xf0, 0x4f, 0x0d, 0xea, 0xc7, 0x63, 0xf4, 0x00, 0x1a,
- 0x72, 0xf2, 0xa3, 0xed, 0xb2, 0x46, 0x95, 0xa5, 0xd2, 0xbf, 0x76, 0x16, 0x4e, 0xc2, 0xe5, 0x60,
- 0x0d, 0x7d, 0x0b, 0xad, 0x21, 0x7f, 0xaf, 0x5e, 0xf6, 0x85, 0xff, 0x8a, 0xfb, 0x67, 0x2e, 0x6f,
- 0xb0, 0x86, 0x9e, 0x82, 0x75, 0x94, 0xf9, 0x3c, 0x48, 0x99, 0x4f, 0xd1, 0x8d, 0x0a, 0xa9, 0xf2,
- 0xc6, 0xfa, 0x97, 0xe0, 0x83, 0x35, 0xf4, 0x03, 0x74, 0xab, 0xa9, 0x71, 0x74, 0xf3, 0x13, 0x97,
- 0x5a, 0xf5, 0x53, 0x55, 0x0e, 0xd6, 0x76, 0x6b, 0xdf, 0xd4, 0x7c, 0x43, 0x29, 0xef, 0xff, 0x1b,
- 0x00, 0x00, 0xff, 0xff, 0x75, 0xf5, 0x8c, 0x39, 0x16, 0x0c, 0x00, 0x00,
+ // 1315 bytes of a gzipped FileDescriptorProto
+ 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x94, 0x56, 0xdd, 0x73, 0xd3, 0x46,
+ 0x10, 0x8f, 0x1d, 0x5b, 0x96, 0xd6, 0x1f, 0x71, 0x16, 0x42, 0x55, 0xd3, 0x42, 0x30, 0xb4, 0xcd,
+ 0x64, 0x3a, 0x99, 0x36, 0x30, 0x1d, 0x60, 0x60, 0x3a, 0xc6, 0x08, 0x70, 0x31, 0xb6, 0xe7, 0x42,
+ 0xe8, 0xa3, 0x46, 0x1f, 0x87, 0xa3, 0x62, 0x24, 0x55, 0x77, 0x36, 0xf5, 0x7f, 0xd6, 0xa7, 0xbe,
+ 0xf6, 0xa5, 0x8f, 0x7d, 0xea, 0x5f, 0xd2, 0xc7, 0xce, 0xdd, 0x49, 0x96, 0xe2, 0x7c, 0x74, 0xf2,
+ 0x64, 0xed, 0xef, 0xb7, 0xbf, 0xd5, 0xee, 0x6a, 0xef, 0xd6, 0xa0, 0xcf, 0x83, 0x83, 0x38, 0x89,
+ 0x78, 0x84, 0xba, 0xfc, 0xf1, 0xa2, 0x59, 0x77, 0x0e, 0x55, 0x6b, 0x41, 0x43, 0x8e, 0x08, 0x15,
+ 0x1e, 0x7c, 0xa4, 0x66, 0x69, 0xb7, 0xb4, 0x67, 0x10, 0xf9, 0x8c, 0x0f, 0x00, 0xbc, 0x28, 0x0c,
+ 0xa9, 0xc7, 0x83, 0x28, 0x34, 0xcb, 0xbb, 0xa5, 0xbd, 0xfa, 0xe1, 0xf5, 0x83, 0x4c, 0x7b, 0xd0,
+ 0x5f, 0x71, 0xa4, 0xe0, 0x87, 0x5d, 0xa8, 0x24, 0xf3, 0x19, 0x35, 0x37, 0xa5, 0x7f, 0x2b, 0xf7,
+ 0x27, 0xf3, 0x19, 0x25, 0x92, 0xeb, 0xfe, 0xa9, 0x03, 0x1c, 0x71, 0x87, 0x07, 0x8c, 0x07, 0x1e,
+ 0xc3, 0xaf, 0xa0, 0xe5, 0x3b, 0xf4, 0x63, 0x14, 0xda, 0x0b, 0x9a, 0x30, 0xf1, 0x32, 0x95, 0x46,
+ 0x53, 0xa1, 0xef, 0x14, 0x88, 0xd7, 0xa1, 0x2a, 0xd4, 0x4c, 0xa6, 0x52, 0x21, 0xca, 0xc0, 0x1b,
+ 0xa0, 0xcd, 0x63, 0x99, 0xfb, 0xa6, 0x84, 0x53, 0x0b, 0xef, 0x42, 0xd3, 0x0f, 0x99, 0x9d, 0x50,
+ 0x16, 0x47, 0x21, 0xa3, 0xcc, 0xac, 0x48, 0xba, 0xe1, 0x87, 0x8c, 0x64, 0x18, 0xee, 0x42, 0x3d,
+ 0x4f, 0x9d, 0x99, 0x55, 0xe9, 0x52, 0x84, 0xd0, 0x84, 0x5a, 0x30, 0x0d, 0xa3, 0x84, 0xfa, 0xa6,
+ 0x26, 0xd9, 0xcc, 0xc4, 0x0e, 0xe8, 0x8e, 0xe7, 0xd1, 0x98, 0x53, 0xdf, 0xac, 0x49, 0x6a, 0x65,
+ 0x0b, 0x95, 0x9f, 0x44, 0x71, 0x4c, 0x7d, 0x53, 0x57, 0xaa, 0xd4, 0xc4, 0x9b, 0x60, 0x88, 0xbc,
+ 0xed, 0x93, 0x80, 0x33, 0xd3, 0x50, 0x32, 0x01, 0xbc, 0x0a, 0x38, 0xc3, 0xdb, 0x50, 0x97, 0xe4,
+ 0xc7, 0x80, 0x89, 0x8c, 0x41, 0xd2, 0x20, 0xa0, 0x37, 0x12, 0xc1, 0x27, 0xa0, 0xbb, 0x4b, 0x5b,
+ 0xb6, 0xd4, 0xac, 0xef, 0x6e, 0xee, 0xd5, 0x0f, 0xef, 0xe4, 0x0d, 0xce, 0x3b, 0x7a, 0xf0, 0x6c,
+ 0x39, 0x11, 0xa8, 0x15, 0xf2, 0x64, 0x49, 0x6a, 0xae, 0xb2, 0xf0, 0x19, 0x80, 0xbb, 0xb4, 0x1d,
+ 0xdf, 0x4f, 0x28, 0x63, 0x66, 0x43, 0xea, 0xef, 0x5e, 0xa0, 0xef, 0x29, 0x2f, 0x15, 0xc1, 0x70,
+ 0x33, 0x1b, 0x1f, 0x41, 0xcd, 0x5d, 0xda, 0x27, 0x11, 0xe3, 0x66, 0x53, 0x06, 0xd8, 0xbd, 0x20,
+ 0xc0, 0xab, 0x88, 0x71, 0xa5, 0xd6, 0x5c, 0x69, 0xa4, 0xd2, 0x38, 0x4a, 0xb8, 0xd9, 0xba, 0x54,
+ 0x3a, 0x89, 0x92, 0x5c, 0x2a, 0x0c, 0xfc, 0x01, 0x34, 0x77, 0x69, 0xcf, 0x03, 0xdf, 0xdc, 0x92,
+ 0xca, 0xdb, 0x17, 0x28, 0x8f, 0x03, 0x5f, 0x09, 0xab, 0xae, 0x78, 0xc6, 0xd7, 0xd0, 0x74, 0x97,
+ 0x36, 0xfd, 0x8d, 0x7a, 0x73, 0xee, 0xb8, 0x33, 0x6a, 0xb6, 0xa5, 0xfc, 0xeb, 0x0b, 0xe4, 0xd6,
+ 0xca, 0x51, 0x45, 0x69, 0xb8, 0x05, 0x08, 0xbf, 0x01, 0x8d, 0x8a, 0xc3, 0xc2, 0xcc, 0x6d, 0x19,
+ 0x65, 0x2b, 0x8f, 0x22, 0x0f, 0x11, 0x49, 0xe9, 0xce, 0x63, 0x68, 0x14, 0x3f, 0x00, 0xb6, 0x61,
+ 0xf3, 0x03, 0x5d, 0xa6, 0x43, 0x2d, 0x1e, 0xc5, 0x28, 0x2f, 0x9c, 0xd9, 0x9c, 0x66, 0xa3, 0x2c,
+ 0x8d, 0xc7, 0xe5, 0x87, 0xa5, 0xce, 0x13, 0x68, 0x9d, 0x6e, 0xfe, 0x95, 0xd4, 0x8f, 0xa0, 0x5e,
+ 0xe8, 0xfc, 0xd5, 0xa5, 0xab, 0xce, 0x5f, 0x49, 0xfa, 0x10, 0x20, 0x6f, 0xfd, 0x95, 0x94, 0x3f,
+ 0xc2, 0xf6, 0x99, 0xae, 0x5f, 0x25, 0x40, 0x77, 0x00, 0xf5, 0x49, 0x10, 0x4e, 0x09, 0xfd, 0x75,
+ 0x4e, 0x19, 0xc7, 0x16, 0x94, 0x03, 0x5f, 0x2a, 0x2b, 0xa4, 0x1c, 0xf8, 0xb8, 0x0f, 0x55, 0xc6,
+ 0x1d, 0xce, 0xce, 0xde, 0x5e, 0xf9, 0x77, 0x27, 0xca, 0xa5, 0x7b, 0x13, 0x0c, 0x15, 0x2a, 0x9e,
+ 0x2d, 0xd7, 0x03, 0x75, 0xff, 0xda, 0x04, 0xc8, 0x2f, 0x3c, 0x71, 0xf6, 0xb3, 0x48, 0x69, 0x9e,
+ 0x2b, 0x1b, 0x77, 0x40, 0x63, 0x89, 0x67, 0x07, 0xb1, 0x7c, 0xa9, 0x41, 0xaa, 0x2c, 0xf1, 0x06,
+ 0x31, 0x7e, 0x0e, 0xba, 0x80, 0xe5, 0xf8, 0x8b, 0x9b, 0xaa, 0x49, 0x6a, 0x2c, 0xf1, 0xe4, 0x74,
+ 0xef, 0x80, 0xe6, 0x33, 0x2e, 0x14, 0x15, 0xa5, 0xf0, 0x19, 0x57, 0x0a, 0x01, 0xcb, 0xb3, 0x56,
+ 0x95, 0x44, 0xcd, 0x67, 0x5c, 0x1e, 0xa5, 0x94, 0x92, 0xc1, 0x34, 0x15, 0xcc, 0x67, 0x5c, 0x06,
+ 0xfb, 0x0c, 0x6a, 0x73, 0x46, 0x13, 0x3b, 0x50, 0xb7, 0x52, 0x93, 0x68, 0xc2, 0x1c, 0xf8, 0xf8,
+ 0x25, 0x40, 0x9c, 0x44, 0x1e, 0x65, 0x4c, 0x70, 0xba, 0xe4, 0x8c, 0x14, 0x19, 0xf8, 0x78, 0x07,
+ 0x1a, 0x19, 0x1d, 0x3b, 0xfc, 0x44, 0xde, 0x4d, 0x06, 0xa9, 0xa7, 0xd8, 0xc4, 0xe1, 0x27, 0xe2,
+ 0x7a, 0xca, 0x5c, 0xbc, 0x4f, 0xbe, 0xbc, 0x9e, 0x0c, 0x92, 0x05, 0xed, 0x7f, 0x3a, 0x15, 0xc3,
+ 0x49, 0xa6, 0x4c, 0x5e, 0x51, 0x79, 0x8c, 0x5e, 0x32, 0x65, 0x68, 0xe5, 0x31, 0x68, 0xb8, 0x48,
+ 0x2f, 0xa1, 0x7b, 0xe7, 0x6d, 0x95, 0x83, 0x89, 0xf2, 0xb3, 0xc2, 0x85, 0x3a, 0x8d, 0xd9, 0x9b,
+ 0xac, 0x70, 0xd1, 0x79, 0x0a, 0x5b, 0x6b, 0xf4, 0xff, 0x8d, 0x8d, 0x51, 0x1c, 0x9b, 0x5f, 0x40,
+ 0x1f, 0xc7, 0x34, 0x71, 0x78, 0x94, 0xc8, 0xd5, 0xb7, 0x8c, 0xf3, 0xd5, 0xb7, 0x8c, 0xa9, 0xb8,
+ 0xbf, 0x23, 0xc1, 0x87, 0x7e, 0xaa, 0xcd, 0x4c, 0xe1, 0xed, 0x3b, 0xdc, 0x91, 0x9f, 0xd0, 0x20,
+ 0xf2, 0x19, 0xbf, 0x00, 0x83, 0xd1, 0x90, 0x05, 0x3c, 0x58, 0x50, 0xf9, 0x09, 0x75, 0x92, 0x03,
+ 0xdd, 0xdf, 0x4b, 0x50, 0x11, 0xbb, 0x4f, 0x48, 0x43, 0x27, 0xdf, 0xb1, 0xe2, 0x59, 0xbc, 0x88,
+ 0x86, 0x62, 0xf4, 0xd5, 0x8b, 0x74, 0x92, 0x99, 0x78, 0x4b, 0x7c, 0x2e, 0xea, 0x51, 0x9f, 0x86,
+ 0x9e, 0xda, 0x6d, 0x3a, 0x29, 0x20, 0x62, 0xef, 0x39, 0x6a, 0x33, 0xab, 0xa1, 0x49, 0x2d, 0x31,
+ 0x9a, 0xfe, 0x3c, 0x71, 0x24, 0xa3, 0xa6, 0x66, 0x65, 0xe3, 0x01, 0xe8, 0x51, 0x5a, 0xb6, 0x1c,
+ 0x9b, 0xfa, 0x21, 0xe6, 0x9d, 0xcf, 0x1a, 0x42, 0x56, 0x3e, 0xdd, 0xbf, 0x4b, 0xd0, 0xe8, 0xcf,
+ 0x02, 0x1a, 0xf2, 0x7e, 0x14, 0xbe, 0x0f, 0xa6, 0x67, 0xce, 0x57, 0x56, 0x52, 0xf9, 0x74, 0x49,
+ 0xd9, 0x1a, 0x57, 0x4d, 0xca, 0x4c, 0xfc, 0x16, 0xb6, 0x03, 0xf6, 0x22, 0x48, 0xe8, 0x27, 0x67,
+ 0x36, 0x23, 0xf3, 0x30, 0x0c, 0xc2, 0x69, 0xda, 0xaf, 0xb3, 0x84, 0x28, 0xd0, 0x93, 0x6f, 0x4d,
+ 0xcb, 0x48, 0x2d, 0x51, 0xe0, 0x2c, 0x9a, 0x0e, 0xe9, 0x82, 0xce, 0xd2, 0xd9, 0x5f, 0xd9, 0x78,
+ 0x2f, 0xfb, 0x8b, 0x50, 0x93, 0x73, 0xb5, 0xfe, 0xef, 0x43, 0x91, 0xdd, 0x3f, 0x4a, 0xd0, 0x18,
+ 0x45, 0x3c, 0x78, 0x1f, 0x78, 0xaa, 0x2f, 0xeb, 0x65, 0xdd, 0x02, 0xf0, 0x64, 0xd9, 0xa3, 0xbc,
+ 0xb8, 0x02, 0x22, 0x78, 0x46, 0x93, 0x05, 0x4d, 0x24, 0xaf, 0xaa, 0x2c, 0x20, 0x78, 0x2f, 0x1d,
+ 0x29, 0x51, 0x5b, 0xeb, 0xb0, 0x9d, 0x67, 0xd1, 0x53, 0xff, 0x97, 0xd4, 0x90, 0x65, 0xa3, 0x54,
+ 0x2d, 0x8c, 0xd2, 0xaa, 0x00, 0xed, 0xb2, 0x02, 0x66, 0xb0, 0x5d, 0xcc, 0xff, 0xdc, 0x2b, 0x0b,
+ 0xef, 0x43, 0xc5, 0x8b, 0x7c, 0x95, 0x7e, 0xab, 0xb8, 0x31, 0xcf, 0x48, 0xfb, 0x91, 0x4f, 0x89,
+ 0x74, 0x3e, 0x6f, 0xbc, 0xf7, 0xff, 0x29, 0x81, 0xa6, 0x12, 0x47, 0x1d, 0x2a, 0xa3, 0xf1, 0xc8,
+ 0x6a, 0x6f, 0xe0, 0x36, 0x34, 0x87, 0xe3, 0xde, 0x73, 0xfb, 0xc5, 0x80, 0x58, 0x3f, 0xf7, 0x86,
+ 0xc3, 0x76, 0x09, 0xaf, 0xc1, 0xd6, 0xf1, 0xe8, 0x34, 0x58, 0x16, 0x7e, 0xfd, 0x57, 0xbd, 0xd1,
+ 0x4b, 0xcb, 0xee, 0x8f, 0x47, 0x2f, 0x06, 0x2f, 0xdb, 0x9b, 0xb8, 0x05, 0x75, 0x6b, 0xd4, 0x7b,
+ 0x36, 0xb4, 0x6c, 0x72, 0x3c, 0xb4, 0xda, 0x15, 0x6c, 0x43, 0xe3, 0xf9, 0xe0, 0x28, 0x47, 0xaa,
+ 0xc2, 0xe5, 0xb9, 0x35, 0xb4, 0xde, 0xa6, 0x80, 0x26, 0x80, 0x34, 0x8c, 0x04, 0x6a, 0xd8, 0x04,
+ 0x63, 0x38, 0x7e, 0x69, 0x0f, 0xad, 0x77, 0xd6, 0xb0, 0xad, 0x8b, 0xc4, 0x8e, 0xde, 0x8e, 0x27,
+ 0x6d, 0x43, 0x64, 0xf1, 0x66, 0x3c, 0x1a, 0xbc, 0x1d, 0x13, 0x7b, 0x42, 0xc6, 0x7d, 0xeb, 0xe8,
+ 0xa8, 0x0d, 0x68, 0xc2, 0x75, 0x41, 0xdb, 0xeb, 0x4c, 0x7d, 0x7f, 0x1f, 0x76, 0xce, 0xed, 0x07,
+ 0x6a, 0x50, 0x1e, 0xbf, 0x6e, 0x6f, 0xa0, 0x01, 0x55, 0x8b, 0x90, 0x31, 0x69, 0x97, 0x0e, 0xff,
+ 0x2d, 0x41, 0xf9, 0x78, 0x80, 0x0f, 0xa0, 0x22, 0x16, 0x05, 0xee, 0xe4, 0x2d, 0x2d, 0xec, 0xa0,
+ 0xce, 0xb5, 0x75, 0x38, 0x9e, 0x2d, 0xbb, 0x1b, 0xf8, 0x3d, 0xd4, 0x7a, 0xec, 0x83, 0xbc, 0x08,
+ 0xce, 0xfd, 0x13, 0xdd, 0x59, 0xfb, 0xd6, 0xdd, 0x0d, 0x7c, 0x0a, 0xc6, 0xd1, 0xdc, 0x65, 0x5e,
+ 0x12, 0xb8, 0x14, 0x6f, 0x14, 0x44, 0x85, 0x23, 0xd9, 0xb9, 0x00, 0xef, 0x6e, 0xe0, 0x4f, 0xd0,
+ 0x2c, 0x96, 0xc6, 0xf0, 0xe6, 0x25, 0x33, 0x50, 0x8c, 0x53, 0x24, 0xbb, 0x1b, 0x7b, 0xa5, 0xef,
+ 0x4a, 0xae, 0x26, 0xc9, 0xfb, 0xff, 0x05, 0x00, 0x00, 0xff, 0xff, 0xcb, 0xf1, 0x95, 0x55, 0x45,
+ 0x0c, 0x00, 0x00,
}
// Reference imports to suppress errors if they are not otherwise used.
diff --git a/proto/ui.proto b/proto/ui.proto
index 41a2e830..26487e3c 100644
--- a/proto/ui.proto
+++ b/proto/ui.proto
@@ -86,6 +86,8 @@ enum Action {
CHANGE_RULE = 7;
LOG_LEVEL = 8;
STOP = 9;
+ MONITOR_PROCESS = 10;
+ STOP_MONITOR_PROCESS = 11;
}
// client configuration sent on Subscribe()
diff --git a/ui/opensnitch/desktop_parser.py b/ui/opensnitch/desktop_parser.py
index d854b01b..de6d5d9c 100644
--- a/ui/opensnitch/desktop_parser.py
+++ b/ui/opensnitch/desktop_parser.py
@@ -19,6 +19,7 @@ class LinuxDesktopParser(threading.Thread):
self.daemon = True
self.running = False
self.apps = {}
+ self.apps_by_name = {}
# some things are just weird
# (not really, i don't want to keep track of parent pids
# just because of icons tho, this hack is way easier)
@@ -55,18 +56,45 @@ class LinuxDesktopParser(threading.Thread):
return cmd
+ def _discover_app_icon(self, app_name):
+ # more hacks
+ # normally qt will find icons if the system if configured properly.
+ # if it's not, qt won't be able to find the icon by using QIcon().fromTheme(""),
+ # so we fallback to try to determine if the icon exist in some well known system paths.
+ icon_dirs = ("/usr/share/icons/gnome/48x48/apps/", "/usr/share/pixmaps/", "/usr/share/icons/hicolor/48x48/apps/")
+ icon_exts = (".png", ".xpm", ".svg")
+
+ for idir in icon_dirs:
+ for iext in icon_exts:
+ iconPath = idir + app_name + iext
+ if os.path.exists(iconPath):
+ print("found on last chance: ", iconPath)
+ return iconPath
+
def _parse_desktop_file(self, desktop_path):
parser = configparser.ConfigParser(strict=False) # Allow duplicate config entries
try:
+ basename = os.path.basename(desktop_path)[:-8]
parser.read(desktop_path, 'utf8')
cmd = parser.get('Desktop Entry', 'exec', raw=True, fallback=None)
+ if cmd == None:
+ cmd = parser.get('Desktop Entry', 'Exec', raw=True, fallback=None)
if cmd is not None:
cmd = self._parse_exec(cmd)
icon = parser.get('Desktop Entry', 'Icon', raw=True, fallback=None)
name = parser.get('Desktop Entry', 'Name', raw=True, fallback=None)
+ if icon == None:
+ # Some .desktop files doesn't have the Icon entry
+ # FIXME: even if we return an icon, if the DE is not properly configured,
+ # it won't be loaded/displayed.
+ icon = self._discover_app_icon(basename)
+
with self.lock:
+ # The Exec entry may have an absolute path to a binary or just the binary with parameters.
+ # /path/binary or binary, so save both
self.apps[cmd] = (name, icon, desktop_path)
+ self.apps[basename] = (name, icon, desktop_path)
# if the command is a symlink, add the real binary too
if os.path.islink(cmd):
link_to = os.path.realpath(cmd)
@@ -82,8 +110,16 @@ class LinuxDesktopParser(threading.Thread):
path = to
break
+ app_name = self.apps.get(path)
+ if app_name == None:
+ return self.apps.get(def_name, (def_name, default_icon, None))
+
return self.apps.get(path, (def_name, default_icon, None))
+ def get_info_by_binname(self, name, default_icon):
+ def_name = os.path.basename(name)
+ return self.apps.get(def_name, (def_name, default_icon, None))
+
def run(self):
self.running = True
wm = pyinotify.WatchManager()
diff --git a/ui/opensnitch/dialogs/stats.py b/ui/opensnitch/dialogs/stats.py
index 2b729bb8..216c5aad 100644
--- a/ui/opensnitch/dialogs/stats.py
+++ b/ui/opensnitch/dialogs/stats.py
@@ -6,6 +6,7 @@ import sys
import os
import csv
import time
+import json
from PyQt5 import Qt, QtCore, QtGui, uic, QtWidgets
from PyQt5.QtSql import QSqlDatabase, QSqlDatabase, QSqlQueryModel, QSqlQuery, QSqlTableModel
@@ -17,6 +18,7 @@ from version import version
from nodes import Nodes
from dialogs.preferences import PreferencesDialog
from dialogs.ruleseditor import RulesEditorDialog
+from dialogs.processdetails import ProcessDetailsDialog
from customwidgets import ColorizedDelegate
DIALOG_UI_PATH = "%s/../res/stats.ui" % os.path.dirname(sys.modules[__name__].__file__)
@@ -228,6 +230,9 @@ class StatsDialog(QtWidgets.QDialog, uic.loadUiType(DIALOG_UI_PATH)[0]):
self._cfg = Config.get()
self._nodes = Nodes.instance()
+ # TODO: allow to display multiples dialogs
+ self._proc_details_dialog = ProcessDetailsDialog()
+
self.daemon_connected = False
# skip table updates if a context menu is active
self._context_menu_active = False
@@ -265,6 +270,7 @@ class StatsDialog(QtWidgets.QDialog, uic.loadUiType(DIALOG_UI_PATH)[0]):
self.delRuleButton.setVisible(False)
self.editRuleButton.setVisible(False)
self.nodeRuleLabel.setVisible(False)
+ self.cmdProcDetails.clicked.connect(self._cb_proc_details_clicked)
self.TABLES[self.TAB_MAIN]['view'] = self._setup_table(QtWidgets.QTreeView, self.eventsTable, "connections",
self.TABLES[self.TAB_MAIN]['display_fields'],
@@ -463,21 +469,38 @@ class StatsDialog(QtWidgets.QDialog, uic.loadUiType(DIALOG_UI_PATH)[0]):
self._db.remove("DELETE FROM rules WHERE name='%s' AND node='%s'" % (rule.name, node_addr))
self._refresh_active_table()
+ def _cb_proc_details_clicked(self):
+ table = self._tables[self.tabWidget.currentIndex()]
+ nrows = table.model().rowCount()
+ pids = {}
+ for row in range(0, nrows):
+ pid = table.model().index(row, 6).data()
+ node = table.model().index(row, 1).data()
+ if pid not in pids:
+ pids[pid] = node
+
+ self._proc_details_dialog.monitor(pids)
+
@QtCore.pyqtSlot(ui_pb2.NotificationReply)
def _cb_notification_callback(self, reply):
#print("[stats dialog] notification reply: ", reply.id, reply.code)
if reply.id in self._notifications_sent:
- #print("[stats] not received: ", self._notifications_sent[reply.id].type)
if reply.code == ui_pb2.ERROR:
msgBox = QtWidgets.QMessageBox()
msgBox.setText(reply.data)
msgBox.setIcon(QtWidgets.QMessageBox.Warning)
msgBox.setStandardButtons(QtWidgets.QMessageBox.Ok)
+ else:
+ print("[stats] unknown notification received: ", self._notifications_sent[reply.id].type)
+
def _cb_tab_changed(self, index):
if index == self.TAB_MAIN:
self._set_events_query()
else:
+ if index == self.TAB_PROCS:
+ self.cmdProcDetails.setVisible(False)
+
self._refresh_active_table()
def _cb_table_context_menu(self, pos):
@@ -571,6 +594,9 @@ class StatsDialog(QtWidgets.QDialog, uic.loadUiType(DIALOG_UI_PATH)[0]):
self.editRuleButton.setVisible(False)
self.nodeRuleLabel.setText("")
self.rulesFilterLine.setVisible(True)
+ elif cur_idx == StatsDialog.TAB_PROCS:
+ self.cmdProcDetails.setVisible(False)
+
model = self._get_active_table().model()
self.setQuery(model, self._db.get_query(self.TABLES[cur_idx]['name'], self.TABLES[cur_idx]['display_fields']) + self._get_order())
@@ -586,7 +612,7 @@ class StatsDialog(QtWidgets.QDialog, uic.loadUiType(DIALOG_UI_PATH)[0]):
elif idx == StatsDialog.COL_PROCS:
cur_idx = 4
self.tabWidget.setCurrentIndex(cur_idx)
- self._set_process_query(data)
+ self._set_process_tab_active(data)
elif idx == StatsDialog.COL_RULES:
cur_idx = 2
self._set_rules_tab_active(row, cur_idx)
@@ -715,6 +741,10 @@ class StatsDialog(QtWidgets.QDialog, uic.loadUiType(DIALOG_UI_PATH)[0]):
if self.TABLES[cur_idx].get('cmdCleanStats') != None:
self.TABLES[cur_idx]['cmdCleanStats'].setVisible(not state)
+ def _set_proc_tab_active(self, data):
+ self.cmdProcDetails.setVisible(False)
+ self._set_process_query(data)
+
def _set_rules_tab_active(self, row, cur_idx):
data = row.data()
self.delRuleButton.setVisible(True)
@@ -837,6 +867,9 @@ class StatsDialog(QtWidgets.QDialog, uic.loadUiType(DIALOG_UI_PATH)[0]):
"FROM procs as p, connections as c " \
"WHERE p.what = '%s' AND p.what = c.process GROUP BY c.dst_ip, c.dst_host, c.dst_port, UserID, Action, Node %s" % (data, self._get_order()))
+ nrows = self._get_active_table().model().rowCount()
+ self.cmdProcDetails.setVisible(nrows != 0)
+
def _set_addrs_query(self, data):
model = self._get_active_table().model()
self.setQuery(model, "SELECT " \
diff --git a/ui/opensnitch/nodes.py b/ui/opensnitch/nodes.py
index d4293715..30f86a6c 100644
--- a/ui/opensnitch/nodes.py
+++ b/ui/opensnitch/nodes.py
@@ -3,6 +3,7 @@ from datetime import datetime
import time
import json
+import ui_pb2
from database import Database
class Nodes():
@@ -155,7 +156,10 @@ class Nodes():
try:
notification.id = int(str(time.time()).replace(".", ""))
self._nodes[addr]['notifications'].put(notification)
- self._notifications_sent[notification.id] = callback_signal
+ self._notifications_sent[notification.id] = {
+ 'callback': callback_signal,
+ 'type': notification.type
+ }
except Exception as e:
print(self.LOG_TAG + " exception sending notification: ", e, addr, notification)
@@ -170,20 +174,28 @@ class Nodes():
notification.id = int(str(time.time()).replace(".", ""))
for c in self._nodes:
self._nodes[c]['notifications'].put(notification)
- self._notifications_sent[notification.id] = callback_signal
+ self._notifications_sent[notification.id] = {
+ 'callback': callback_signal,
+ 'type': notification.type
+ }
except Exception as e:
print(self.LOG_TAG + " exception sending notifications: ", e, notification)
return notification.id
- def reply_notification(self, reply):
+ def reply_notification(self, addr, reply):
if reply == None:
print(self.LOG_TAG, " reply notification None")
return
if reply.id in self._notifications_sent:
if self._notifications_sent[reply.id] != None:
- self._notifications_sent[reply.id].emit(reply)
- del self._notifications_sent[reply.id]
+ self._notifications_sent[reply.id]['callback'].emit(reply)
+
+ # delete only one-time notifications
+ # we need the ID of streaming notifications from the server
+ # (monitor_process for example) to keep track of the data sent to us.
+ if self._notifications_sent[reply.id]['type'] != ui_pb2.MONITOR_PROCESS:
+ del self._notifications_sent[reply.id]
def update(self, proto, addr, status=ONLINE):
try:
diff --git a/ui/opensnitch/res/process_details.ui b/ui/opensnitch/res/process_details.ui
new file mode 100644
index 00000000..36c36745
--- /dev/null
+++ b/ui/opensnitch/res/process_details.ui
@@ -0,0 +1,259 @@
+
+
+ ProcessDetailsDialog
+
+
+
+ 0
+ 0
+ 731
+ 478
+
+
+
+ Process details
+
+
+ -
+
+
-
+
+
-
+
+
+
+ 48
+ 48
+
+
+
+
+ 64
+ 64
+
+
+
+
+
+
+
+ -
+
+
-
+
+
+ QFrame::NoFrame
+
+
+ loading...
+
+
+ Qt::AlignLeading|Qt::AlignLeft|Qt::AlignTop
+
+
+ true
+
+
+
+ -
+
+
+ loading...
+
+
+ Qt::PlainText
+
+
+ Qt::AlignLeading|Qt::AlignLeft|Qt::AlignTop
+
+
+ true
+
+
+
+
+
+
+
+ -
+
+
+ CWD: loading...
+
+
+
+ -
+
+
-
+
+
+ mem stats: loading...
+
+
+
+
+
+
+
+ -
+
+
+ Qt::Horizontal
+
+
+
+ -
+
+
+ QTabWidget::South
+
+
+ 0
+
+
+ true
+
+
+
+ Status
+
+
+
-
+
+
+ false
+
+
+
+
+
+
+
+ Open files
+
+
+ -
+
+
+ false
+
+
+
+
+
+
+
+ I/O Statistics
+
+
+ -
+
+
+ false
+
+
+
+
+
+
+
+ Memory mapped files
+
+
+ -
+
+
+ false
+
+
+
+
+
+
+
+ Stack
+
+
+ -
+
+
+ false
+
+
+
+
+
+
+
+ Environment variables
+
+
+ -
+
+
+ false
+
+
+
+
+
+
+
+ -
+
+
-
+
+
+ Application pids
+
+
+
+ -
+
+
+ -
+
+
+ Qt::Horizontal
+
+
+
+ 40
+ 20
+
+
+
+
+ -
+
+
+ Start or stop monitoring this process
+
+
+
+
+
+
+
+
+ true
+
+
+
+ -
+
+
+ Close
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/ui/opensnitch/res/stats.ui b/ui/opensnitch/res/stats.ui
index a1eac6f5..dcf90885 100644
--- a/ui/opensnitch/res/stats.ui
+++ b/ui/opensnitch/res/stats.ui
@@ -72,7 +72,7 @@
- ../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../.designer/backup../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../.designer/backup
+ ../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../.designer/backup../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../.designer/backup
-
@@ -81,7 +81,7 @@
- ../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../.designer/backup../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../.designer/backup
+ ../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../.designer/backup../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../.designer/backup
@@ -153,7 +153,8 @@
-
+
+ ../../../../../../../../../../../../../../../../../../../../.designer/backup../../../../../../../../../../../../../../../../../../../../.designer/backup
@@ -191,7 +192,7 @@
- ..
+ ../../../../../../../../../../../../../../../../../../../../.designer/backup../../../../../../../../../../../../../../../../../../../../.designer/backup
@@ -210,7 +211,7 @@
-
-
+
Qt::Horizontal
@@ -260,7 +261,7 @@
- ..
+ ../../../../../../../../../../../../../../../../../../../../.designer/backup../../../../../../../../../../../../../../../../../../../../.designer/backup
@@ -278,7 +279,7 @@
- ..
+ ../../../../../../../../../../../../../../../../../../../../.designer/backup../../../../../../../../../../../../../../../../../../../../.designer/backup
@@ -289,7 +290,7 @@
- ../../../../../../../../.designer/backup../../../../../../../../.designer/backup
+ ../../../../../../../../../../../../../../../../../../../../../../../../../../.designer/backup../../../../../../../../../../../../../../../../../../../../../../../../../../.designer/backup
@@ -384,7 +385,7 @@
- ..
+ ../../../../../../../../../../../../../../../../../../../../.designer/backup../../../../../../../../../../../../../../../../../../../../.designer/backup
@@ -431,7 +432,8 @@
-
+
+ ../../../../../../../../../../../../../../../../../../../../.designer/backup../../../../../../../../../../../../../../../../../../../../.designer/backup
@@ -472,7 +474,17 @@
- ..
+ ../../../../../../../../../../../../../../../../../../../../.designer/backup../../../../../../../../../../../../../../../../../../../../.designer/backup
+
+
+
+ -
+
+
+
+
+
+
@@ -519,7 +531,8 @@
-
+
+ ../../../../../../../../../../../../../../../../../../../../.designer/backup../../../../../../../../../../../../../../../../../../../../.designer/backup
@@ -557,7 +570,7 @@
- ..
+ ../../../../../../../../../../../../../../../../../../../../.designer/backup../../../../../../../../../../../../../../../../../../../../.designer/backup
@@ -601,7 +614,8 @@
-
+
+ ../../../../../../../../../../../../../../../../../../../../.designer/backup../../../../../../../../../../../../../../../../../../../../.designer/backup
@@ -639,7 +653,7 @@
- ..
+ ../../../../../../../../../../../../../../../../../../../../.designer/backup../../../../../../../../../../../../../../../../../../../../.designer/backup
@@ -683,7 +697,8 @@
-
+
+ ../../../../../../../../../../../../../../../../../../../../.designer/backup../../../../../../../../../../../../../../../../../../../../.designer/backup
@@ -721,7 +736,7 @@
- ..
+ ../../../../../../../../../../../../../../../../../../../../.designer/backup../../../../../../../../../../../../../../../../../../../../.designer/backup
@@ -775,7 +790,8 @@
-
+
+ ../../../../../../../../../../../../../../../../../../../../.designer/backup../../../../../../../../../../../../../../../../../../../../.designer/backup
@@ -1105,7 +1121,7 @@
- ../../../../../../../../.designer/backup../../../../../../../../.designer/backup
+ ../../../../../../../../../../../../../../../../../../../../../../../../../../.designer/backup../../../../../../../../../../../../../../../../../../../../../../../../../../.designer/backup
Ctrl+S
@@ -1131,7 +1147,7 @@
- ../../../../../../../../.designer/backup../../../../../../../../.designer/backup
+ ../../../../../../../../../../../../../../../../../../../../../../../../../../.designer/backup../../../../../../../../../../../../../../../../../../../../../../../../../../.designer/backup
@@ -1153,8 +1169,7 @@
-
- ..
+
@@ -1171,7 +1186,7 @@
- ../../../../../../../../.designer/backup../../../../../../../../.designer/backup
+ ../../../../../../../../../../../../../../../../../../../../../../../../../../.designer/backup../../../../../../../../../../../../../../../../../../../../../../../../../../.designer/backup
true
diff --git a/ui/opensnitch/service.py b/ui/opensnitch/service.py
index 1890a511..a3188711 100644
--- a/ui/opensnitch/service.py
+++ b/ui/opensnitch/service.py
@@ -509,7 +509,7 @@ class UIService(ui_pb2_grpc.UIServicer, QtWidgets.QGraphicsObject):
in_message = next(node_iter)
if in_message == None:
continue
- self._nodes.reply_notification(in_message)
+ self._nodes.reply_notification(addr, in_message)
except StopIteration as e:
print("[Notifications] Node exited")
except Exception as e:
diff --git a/ui/opensnitch/ui_pb2.py b/ui/opensnitch/ui_pb2.py
index aef9b549..5a6006ea 100644
--- a/ui/opensnitch/ui_pb2.py
+++ b/ui/opensnitch/ui_pb2.py
@@ -20,7 +20,7 @@ DESCRIPTOR = _descriptor.FileDescriptor(
name='ui.proto',
package='protocol',
syntax='proto3',
- serialized_pb=_b('\n\x08ui.proto\x12\x08protocol\"]\n\x05\x45vent\x12\x0c\n\x04time\x18\x01 \x01(\t\x12(\n\nconnection\x18\x02 \x01(\x0b\x32\x14.protocol.Connection\x12\x1c\n\x04rule\x18\x03 \x01(\x0b\x32\x0e.protocol.Rule\"\xd3\x06\n\nStatistics\x12\x16\n\x0e\x64\x61\x65mon_version\x18\x01 \x01(\t\x12\r\n\x05rules\x18\x02 \x01(\x04\x12\x0e\n\x06uptime\x18\x03 \x01(\x04\x12\x15\n\rdns_responses\x18\x04 \x01(\x04\x12\x13\n\x0b\x63onnections\x18\x05 \x01(\x04\x12\x0f\n\x07ignored\x18\x06 \x01(\x04\x12\x10\n\x08\x61\x63\x63\x65pted\x18\x07 \x01(\x04\x12\x0f\n\x07\x64ropped\x18\x08 \x01(\x04\x12\x11\n\trule_hits\x18\t \x01(\x04\x12\x13\n\x0brule_misses\x18\n \x01(\x04\x12\x33\n\x08\x62y_proto\x18\x0b \x03(\x0b\x32!.protocol.Statistics.ByProtoEntry\x12\x37\n\nby_address\x18\x0c \x03(\x0b\x32#.protocol.Statistics.ByAddressEntry\x12\x31\n\x07\x62y_host\x18\r \x03(\x0b\x32 .protocol.Statistics.ByHostEntry\x12\x31\n\x07\x62y_port\x18\x0e \x03(\x0b\x32 .protocol.Statistics.ByPortEntry\x12/\n\x06\x62y_uid\x18\x0f \x03(\x0b\x32\x1f.protocol.Statistics.ByUidEntry\x12=\n\rby_executable\x18\x10 \x03(\x0b\x32&.protocol.Statistics.ByExecutableEntry\x12\x1f\n\x06\x65vents\x18\x11 \x03(\x0b\x32\x0f.protocol.Event\x1a.\n\x0c\x42yProtoEntry\x12\x0b\n\x03key\x18\x01 \x01(\t\x12\r\n\x05value\x18\x02 \x01(\x04:\x02\x38\x01\x1a\x30\n\x0e\x42yAddressEntry\x12\x0b\n\x03key\x18\x01 \x01(\t\x12\r\n\x05value\x18\x02 \x01(\x04:\x02\x38\x01\x1a-\n\x0b\x42yHostEntry\x12\x0b\n\x03key\x18\x01 \x01(\t\x12\r\n\x05value\x18\x02 \x01(\x04:\x02\x38\x01\x1a-\n\x0b\x42yPortEntry\x12\x0b\n\x03key\x18\x01 \x01(\t\x12\r\n\x05value\x18\x02 \x01(\x04:\x02\x38\x01\x1a,\n\nByUidEntry\x12\x0b\n\x03key\x18\x01 \x01(\t\x12\r\n\x05value\x18\x02 \x01(\x04:\x02\x38\x01\x1a\x33\n\x11\x42yExecutableEntry\x12\x0b\n\x03key\x18\x01 \x01(\t\x12\r\n\x05value\x18\x02 \x01(\x04:\x02\x38\x01\">\n\x0bPingRequest\x12\n\n\x02id\x18\x01 \x01(\x04\x12#\n\x05stats\x18\x02 \x01(\x0b\x32\x14.protocol.Statistics\"\x17\n\tPingReply\x12\n\n\x02id\x18\x01 \x01(\x04\"\xc8\x02\n\nConnection\x12\x10\n\x08protocol\x18\x01 \x01(\t\x12\x0e\n\x06src_ip\x18\x02 \x01(\t\x12\x10\n\x08src_port\x18\x03 \x01(\r\x12\x0e\n\x06\x64st_ip\x18\x04 \x01(\t\x12\x10\n\x08\x64st_host\x18\x05 \x01(\t\x12\x10\n\x08\x64st_port\x18\x06 \x01(\r\x12\x0f\n\x07user_id\x18\x07 \x01(\r\x12\x12\n\nprocess_id\x18\x08 \x01(\r\x12\x14\n\x0cprocess_path\x18\t \x01(\t\x12\x13\n\x0bprocess_cwd\x18\n \x01(\t\x12\x14\n\x0cprocess_args\x18\x0b \x03(\t\x12\x39\n\x0bprocess_env\x18\x0c \x03(\x0b\x32$.protocol.Connection.ProcessEnvEntry\x1a\x31\n\x0fProcessEnvEntry\x12\x0b\n\x03key\x18\x01 \x01(\t\x12\r\n\x05value\x18\x02 \x01(\t:\x02\x38\x01\"J\n\x08Operator\x12\x0c\n\x04type\x18\x01 \x01(\t\x12\x0f\n\x07operand\x18\x02 \x01(\t\x12\x0c\n\x04\x64\x61ta\x18\x03 \x01(\t\x12\x11\n\tsensitive\x18\x04 \x01(\x08\"\x81\x01\n\x04Rule\x12\x0c\n\x04name\x18\x01 \x01(\t\x12\x0f\n\x07\x65nabled\x18\x02 \x01(\x08\x12\x12\n\nprecedence\x18\x03 \x01(\x08\x12\x0e\n\x06\x61\x63tion\x18\x04 \x01(\t\x12\x10\n\x08\x64uration\x18\x05 \x01(\t\x12$\n\x08operator\x18\x06 \x01(\x0b\x32\x12.protocol.Operator\"\x95\x01\n\x0c\x43lientConfig\x12\n\n\x02id\x18\x01 \x01(\x04\x12\x0c\n\x04name\x18\x02 \x01(\t\x12\x0f\n\x07version\x18\x03 \x01(\t\x12\x19\n\x11isFirewallRunning\x18\x04 \x01(\x08\x12\x0e\n\x06\x63onfig\x18\x05 \x01(\t\x12\x10\n\x08logLevel\x18\x06 \x01(\r\x12\x1d\n\x05rules\x18\x07 \x03(\x0b\x32\x0e.protocol.Rule\"\x8f\x01\n\x0cNotification\x12\n\n\x02id\x18\x01 \x01(\x04\x12\x12\n\nclientName\x18\x02 \x01(\t\x12\x12\n\nserverName\x18\x03 \x01(\t\x12\x1e\n\x04type\x18\x04 \x01(\x0e\x32\x10.protocol.Action\x12\x0c\n\x04\x64\x61ta\x18\x05 \x01(\t\x12\x1d\n\x05rules\x18\x06 \x03(\x0b\x32\x0e.protocol.Rule\"\\\n\x11NotificationReply\x12\n\n\x02id\x18\x01 \x01(\x04\x12-\n\x04\x63ode\x18\x02 \x01(\x0e\x32\x1f.protocol.NotificationReplyCode\x12\x0c\n\x04\x64\x61ta\x18\x03 \x01(\t*\xab\x01\n\x06\x41\x63tion\x12\x08\n\x04NONE\x10\x00\x12\x11\n\rLOAD_FIREWALL\x10\x01\x12\x13\n\x0fUNLOAD_FIREWALL\x10\x02\x12\x11\n\rCHANGE_CONFIG\x10\x03\x12\x0f\n\x0b\x45NABLE_RULE\x10\x04\x12\x10\n\x0c\x44ISABLE_RULE\x10\x05\x12\x0f\n\x0b\x44\x45LETE_RULE\x10\x06\x12\x0f\n\x0b\x43HANGE_RULE\x10\x07\x12\r\n\tLOG_LEVEL\x10\x08\x12\x08\n\x04STOP\x10\t**\n\x15NotificationReplyCode\x12\x06\n\x02OK\x10\x00\x12\t\n\x05\x45RROR\x10\x01\x32\xf8\x01\n\x02UI\x12\x34\n\x04Ping\x12\x15.protocol.PingRequest\x1a\x13.protocol.PingReply\"\x00\x12\x31\n\x07\x41skRule\x12\x14.protocol.Connection\x1a\x0e.protocol.Rule\"\x00\x12=\n\tSubscribe\x12\x16.protocol.ClientConfig\x1a\x16.protocol.ClientConfig\"\x00\x12J\n\rNotifications\x12\x1b.protocol.NotificationReply\x1a\x16.protocol.Notification\"\x00(\x01\x30\x01\x62\x06proto3')
+ serialized_pb=_b('\n\x08ui.proto\x12\x08protocol\"]\n\x05\x45vent\x12\x0c\n\x04time\x18\x01 \x01(\t\x12(\n\nconnection\x18\x02 \x01(\x0b\x32\x14.protocol.Connection\x12\x1c\n\x04rule\x18\x03 \x01(\x0b\x32\x0e.protocol.Rule\"\xd3\x06\n\nStatistics\x12\x16\n\x0e\x64\x61\x65mon_version\x18\x01 \x01(\t\x12\r\n\x05rules\x18\x02 \x01(\x04\x12\x0e\n\x06uptime\x18\x03 \x01(\x04\x12\x15\n\rdns_responses\x18\x04 \x01(\x04\x12\x13\n\x0b\x63onnections\x18\x05 \x01(\x04\x12\x0f\n\x07ignored\x18\x06 \x01(\x04\x12\x10\n\x08\x61\x63\x63\x65pted\x18\x07 \x01(\x04\x12\x0f\n\x07\x64ropped\x18\x08 \x01(\x04\x12\x11\n\trule_hits\x18\t \x01(\x04\x12\x13\n\x0brule_misses\x18\n \x01(\x04\x12\x33\n\x08\x62y_proto\x18\x0b \x03(\x0b\x32!.protocol.Statistics.ByProtoEntry\x12\x37\n\nby_address\x18\x0c \x03(\x0b\x32#.protocol.Statistics.ByAddressEntry\x12\x31\n\x07\x62y_host\x18\r \x03(\x0b\x32 .protocol.Statistics.ByHostEntry\x12\x31\n\x07\x62y_port\x18\x0e \x03(\x0b\x32 .protocol.Statistics.ByPortEntry\x12/\n\x06\x62y_uid\x18\x0f \x03(\x0b\x32\x1f.protocol.Statistics.ByUidEntry\x12=\n\rby_executable\x18\x10 \x03(\x0b\x32&.protocol.Statistics.ByExecutableEntry\x12\x1f\n\x06\x65vents\x18\x11 \x03(\x0b\x32\x0f.protocol.Event\x1a.\n\x0c\x42yProtoEntry\x12\x0b\n\x03key\x18\x01 \x01(\t\x12\r\n\x05value\x18\x02 \x01(\x04:\x02\x38\x01\x1a\x30\n\x0e\x42yAddressEntry\x12\x0b\n\x03key\x18\x01 \x01(\t\x12\r\n\x05value\x18\x02 \x01(\x04:\x02\x38\x01\x1a-\n\x0b\x42yHostEntry\x12\x0b\n\x03key\x18\x01 \x01(\t\x12\r\n\x05value\x18\x02 \x01(\x04:\x02\x38\x01\x1a-\n\x0b\x42yPortEntry\x12\x0b\n\x03key\x18\x01 \x01(\t\x12\r\n\x05value\x18\x02 \x01(\x04:\x02\x38\x01\x1a,\n\nByUidEntry\x12\x0b\n\x03key\x18\x01 \x01(\t\x12\r\n\x05value\x18\x02 \x01(\x04:\x02\x38\x01\x1a\x33\n\x11\x42yExecutableEntry\x12\x0b\n\x03key\x18\x01 \x01(\t\x12\r\n\x05value\x18\x02 \x01(\x04:\x02\x38\x01\">\n\x0bPingRequest\x12\n\n\x02id\x18\x01 \x01(\x04\x12#\n\x05stats\x18\x02 \x01(\x0b\x32\x14.protocol.Statistics\"\x17\n\tPingReply\x12\n\n\x02id\x18\x01 \x01(\x04\"\xc8\x02\n\nConnection\x12\x10\n\x08protocol\x18\x01 \x01(\t\x12\x0e\n\x06src_ip\x18\x02 \x01(\t\x12\x10\n\x08src_port\x18\x03 \x01(\r\x12\x0e\n\x06\x64st_ip\x18\x04 \x01(\t\x12\x10\n\x08\x64st_host\x18\x05 \x01(\t\x12\x10\n\x08\x64st_port\x18\x06 \x01(\r\x12\x0f\n\x07user_id\x18\x07 \x01(\r\x12\x12\n\nprocess_id\x18\x08 \x01(\r\x12\x14\n\x0cprocess_path\x18\t \x01(\t\x12\x13\n\x0bprocess_cwd\x18\n \x01(\t\x12\x14\n\x0cprocess_args\x18\x0b \x03(\t\x12\x39\n\x0bprocess_env\x18\x0c \x03(\x0b\x32$.protocol.Connection.ProcessEnvEntry\x1a\x31\n\x0fProcessEnvEntry\x12\x0b\n\x03key\x18\x01 \x01(\t\x12\r\n\x05value\x18\x02 \x01(\t:\x02\x38\x01\"J\n\x08Operator\x12\x0c\n\x04type\x18\x01 \x01(\t\x12\x0f\n\x07operand\x18\x02 \x01(\t\x12\x0c\n\x04\x64\x61ta\x18\x03 \x01(\t\x12\x11\n\tsensitive\x18\x04 \x01(\x08\"\x81\x01\n\x04Rule\x12\x0c\n\x04name\x18\x01 \x01(\t\x12\x0f\n\x07\x65nabled\x18\x02 \x01(\x08\x12\x12\n\nprecedence\x18\x03 \x01(\x08\x12\x0e\n\x06\x61\x63tion\x18\x04 \x01(\t\x12\x10\n\x08\x64uration\x18\x05 \x01(\t\x12$\n\x08operator\x18\x06 \x01(\x0b\x32\x12.protocol.Operator\"\x95\x01\n\x0c\x43lientConfig\x12\n\n\x02id\x18\x01 \x01(\x04\x12\x0c\n\x04name\x18\x02 \x01(\t\x12\x0f\n\x07version\x18\x03 \x01(\t\x12\x19\n\x11isFirewallRunning\x18\x04 \x01(\x08\x12\x0e\n\x06\x63onfig\x18\x05 \x01(\t\x12\x10\n\x08logLevel\x18\x06 \x01(\r\x12\x1d\n\x05rules\x18\x07 \x03(\x0b\x32\x0e.protocol.Rule\"\x8f\x01\n\x0cNotification\x12\n\n\x02id\x18\x01 \x01(\x04\x12\x12\n\nclientName\x18\x02 \x01(\t\x12\x12\n\nserverName\x18\x03 \x01(\t\x12\x1e\n\x04type\x18\x04 \x01(\x0e\x32\x10.protocol.Action\x12\x0c\n\x04\x64\x61ta\x18\x05 \x01(\t\x12\x1d\n\x05rules\x18\x06 \x03(\x0b\x32\x0e.protocol.Rule\"\\\n\x11NotificationReply\x12\n\n\x02id\x18\x01 \x01(\x04\x12-\n\x04\x63ode\x18\x02 \x01(\x0e\x32\x1f.protocol.NotificationReplyCode\x12\x0c\n\x04\x64\x61ta\x18\x03 \x01(\t*\xda\x01\n\x06\x41\x63tion\x12\x08\n\x04NONE\x10\x00\x12\x11\n\rLOAD_FIREWALL\x10\x01\x12\x13\n\x0fUNLOAD_FIREWALL\x10\x02\x12\x11\n\rCHANGE_CONFIG\x10\x03\x12\x0f\n\x0b\x45NABLE_RULE\x10\x04\x12\x10\n\x0c\x44ISABLE_RULE\x10\x05\x12\x0f\n\x0b\x44\x45LETE_RULE\x10\x06\x12\x0f\n\x0b\x43HANGE_RULE\x10\x07\x12\r\n\tLOG_LEVEL\x10\x08\x12\x08\n\x04STOP\x10\t\x12\x13\n\x0fMONITOR_PROCESS\x10\n\x12\x18\n\x14STOP_MONITOR_PROCESS\x10\x0b**\n\x15NotificationReplyCode\x12\x06\n\x02OK\x10\x00\x12\t\n\x05\x45RROR\x10\x01\x32\xf8\x01\n\x02UI\x12\x34\n\x04Ping\x12\x15.protocol.PingRequest\x1a\x13.protocol.PingReply\"\x00\x12\x31\n\x07\x41skRule\x12\x14.protocol.Connection\x1a\x0e.protocol.Rule\"\x00\x12=\n\tSubscribe\x12\x16.protocol.ClientConfig\x1a\x16.protocol.ClientConfig\"\x00\x12J\n\rNotifications\x12\x1b.protocol.NotificationReply\x1a\x16.protocol.Notification\"\x00(\x01\x30\x01\x62\x06proto3')
)
_ACTION = _descriptor.EnumDescriptor(
@@ -69,11 +69,19 @@ _ACTION = _descriptor.EnumDescriptor(
name='STOP', index=9, number=9,
options=None,
type=None),
+ _descriptor.EnumValueDescriptor(
+ name='MONITOR_PROCESS', index=10, number=10,
+ options=None,
+ type=None),
+ _descriptor.EnumValueDescriptor(
+ name='STOP_MONITOR_PROCESS', index=11, number=11,
+ options=None,
+ type=None),
],
containing_type=None,
options=None,
serialized_start=1992,
- serialized_end=2163,
+ serialized_end=2210,
)
_sym_db.RegisterEnumDescriptor(_ACTION)
@@ -95,8 +103,8 @@ _NOTIFICATIONREPLYCODE = _descriptor.EnumDescriptor(
],
containing_type=None,
options=None,
- serialized_start=2165,
- serialized_end=2207,
+ serialized_start=2212,
+ serialized_end=2254,
)
_sym_db.RegisterEnumDescriptor(_NOTIFICATIONREPLYCODE)
@@ -111,6 +119,8 @@ DELETE_RULE = 6
CHANGE_RULE = 7
LOG_LEVEL = 8
STOP = 9
+MONITOR_PROCESS = 10
+STOP_MONITOR_PROCESS = 11
OK = 0
ERROR = 1
@@ -1226,8 +1236,8 @@ _UI = _descriptor.ServiceDescriptor(
file=DESCRIPTOR,
index=0,
options=None,
- serialized_start=2210,
- serialized_end=2458,
+ serialized_start=2257,
+ serialized_end=2505,
methods=[
_descriptor.MethodDescriptor(
name='Ping',
diff --git a/ui/opensnitch/version.py b/ui/opensnitch/version.py
index ee965d3a..1447bd8e 100644
--- a/ui/opensnitch/version.py
+++ b/ui/opensnitch/version.py
@@ -1 +1 @@
-version = '1.2.0'
+version = '1.3.0rc1'