srdusr
aboutsummaryrefslogtreecommitdiffstats
path: root/internal/ipc/ipc.go
diff options
context:
space:
mode:
Diffstat (limited to 'internal/ipc/ipc.go')
-rw-r--r--internal/ipc/ipc.go86
1 files changed, 83 insertions, 3 deletions
diff --git a/internal/ipc/ipc.go b/internal/ipc/ipc.go
index 19c9c23..03a4bc7 100644
--- a/internal/ipc/ipc.go
+++ b/internal/ipc/ipc.go
@@ -10,6 +10,7 @@ import (
"fmt"
"net"
"sync"
+ "time"
"mitmux/internal/rules"
"mitmux/internal/store"
@@ -17,7 +18,7 @@ import (
// Request is sent by a client to the daemon.
type Request struct {
- Type string `json:"type"` // "list", "get", "subscribe", "repeat", "rules_list", "rules_save", "rules_delete", or "rules_toggle"
+ Type string `json:"type"` // "list", "get", "subscribe", "repeat", "intrude", "rules_list", "rules_save", "rules_delete", or "rules_toggle"
Limit int `json:"limit,omitempty"`
BeforeID int64 `json:"before_id,omitempty"`
ID int64 `json:"id,omitempty"`
@@ -28,10 +29,15 @@ type Request struct {
Query string `json:"query,omitempty"`
// For "repeat": send Raw to scheme://host exactly as given.
+ // For "intrude": Raw is the §marked§ template - see proxy.Intrude.
Scheme string `json:"scheme,omitempty"`
Host string `json:"host,omitempty"`
Raw []byte `json:"raw,omitempty"`
+ // For "intrude": the payload set, applied to each marked position in
+ // turn (Sniper-style - see proxy.Intrude).
+ Payloads []string `json:"payloads,omitempty"`
+
// For "rules_save": add (Rule.ID == 0) or update (Rule.ID != 0) a
// match-and-replace rule. For "rules_delete"/"rules_toggle": RuleID
// (and RuleEnabled for toggle) identify the target.
@@ -42,12 +48,27 @@ type Request struct {
// Response is sent by the daemon to a client.
type Response struct {
- Type string `json:"type"` // "list", "get", "new", "repeat", "rules", or "error"
+ Type string `json:"type"` // "list", "get", "new", "repeat", "rules", "intrude_result", "intrude_done", or "error"
Entries []store.Summary `json:"entries,omitempty"` // for "list"
Detail *EntryDetail `json:"detail,omitempty"` // for "get" and "repeat"
New *store.Summary `json:"new,omitempty"` // for "new" (subscribe push)
Rules []rules.Rule `json:"rules,omitempty"` // for "rules"
- Error string `json:"error,omitempty"`
+
+ // For "intrude_result": one completed attack request.
+ IntrudeResult *IntrudeResultMsg `json:"intrude_result,omitempty"`
+
+ Error string `json:"error,omitempty"`
+}
+
+// IntrudeResultMsg is one completed Intruder attack request.
+type IntrudeResultMsg struct {
+ Position int `json:"position"`
+ Payload string `json:"payload"`
+ EntryID int64 `json:"entry_id"`
+ StatusCode int `json:"status_code"`
+ RespSize int `json:"resp_size"`
+ Duration time.Duration `json:"duration"`
+ Error string `json:"error,omitempty"`
}
// EntryDetail is a full history entry, raw bytes included.
@@ -237,3 +258,62 @@ func Subscribe(path string) (<-chan store.Summary, func() error, error) {
}()
return ch, conn.Close, nil
}
+
+// Intrude starts a Sniper attack (see proxy.Intrude): template must
+// contain at least one §marked§ position, fuzzed in turn through
+// payloads. Unlike Subscribe's live feed, no result is ever dropped for
+// a slow consumer - each one is the attack's actual data, not a
+// notification with the real thing recoverable elsewhere. A setup error
+// (bad markers, empty payload set, too many requests) is returned
+// directly rather than through the channel. The returned channel closes
+// when the attack finishes or the connection is closed early.
+func Intrude(path, scheme, host string, template []byte, payloads []string) (<-chan IntrudeResultMsg, func() error, error) {
+ conn, err := net.Dial("unix", path)
+ if err != nil {
+ return nil, nil, fmt.Errorf("dial %s: %w", path, err)
+ }
+ if err := json.NewEncoder(conn).Encode(Request{Type: "intrude", Scheme: scheme, Host: host, Raw: template, Payloads: payloads}); err != nil {
+ conn.Close()
+ return nil, nil, err
+ }
+
+ dec := json.NewDecoder(conn)
+ var first Response
+ if err := dec.Decode(&first); err != nil {
+ conn.Close()
+ return nil, nil, err
+ }
+ if first.Type == "error" {
+ conn.Close()
+ return nil, nil, errors.New(first.Error)
+ }
+
+ ch := make(chan IntrudeResultMsg)
+ go func() {
+ defer close(ch)
+ deliver := func(resp Response) bool {
+ switch resp.Type {
+ case "intrude_result":
+ if resp.IntrudeResult != nil {
+ ch <- *resp.IntrudeResult
+ }
+ return true
+ default: // "intrude_done", or anything else - stop
+ return false
+ }
+ }
+ if !deliver(first) {
+ return
+ }
+ for {
+ var resp Response
+ if err := dec.Decode(&resp); err != nil {
+ return
+ }
+ if !deliver(resp) {
+ return
+ }
+ }
+ }()
+ return ch, conn.Close, nil
+}