-
Notifications
You must be signed in to change notification settings - Fork 14
/
listener.go
131 lines (113 loc) · 3.92 KB
/
listener.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
// Copyright (c) 2023 RethinkDNS and its authors.
//
// This Source Code Form is subject to the terms of the Mozilla Public
// License, v. 2.0. If a copy of the MPL was not distributed with this
// file, You can obtain one at http://mozilla.org/MPL/2.0/.
package intra
import (
"errors"
"fmt"
"net/netip"
"time"
"github.com/celzero/firestack/intra/ipn"
)
// SocketSummary reports information about each TCP socket
// or a non-DNS UDP association, or ICMP echo when it is closed.
type SocketSummary struct {
Proto string // tcp, udp, icmp, etc.
ID string // Unique ID for this socket.
PID string // Proxy ID that handled this socket.
UID string // UID of the app that owns this socket (sans ICMP).
Target string // Remote IP, if dialed in.
Rx int64 // Total bytes downloaded (sans ICMP).
Tx int64 // Total bytes uploaded (sans ICMP).
Duration int32 // Duration in seconds.
start time.Time // Tracks start time; unexported.
Rtt int32 // Round-trip time (ms); (sans ICMP).
Msg string // Err or other messages, if any.
Dup bool // True if another active connection to Target exists.
}
type SocketListener interface {
// Flow is called on a new connection; return "proxyid,connid" to forward the connection
// to a pre-registered proxy; "Base" to allow the connection; "Block" to block the connection.
// "connid" is used to uniquely identify a connection across all proxies, and a summary of the
// connection is sent back to a pre-registered listener.
// protocol is 6 for TCP, 17 for UDP, 1 for ICMP.
// uid is -1 in case owner-uid of the connection couldn't be determined.
// dup is true if there's another active connection to dst or origdsts.
// src and dst are string'd representation of net.TCPAddr and net.UDPAddr.
// origdsts is a comma-separated list of original source IPs, this may be same as dst.
// domains is a comma-separated list of domain names associated with origsrcs, if any.
// probableDomains is a comma-separated list of probable domain names associated with origsrcs, if any.
// blocklists is a comma-separated list of blocklist names, if any.
Flow(protocol int32, uid int, dup bool, src, dst, origdsts, domains, probableDomains, blocklists string) *Mark
// OnSocketClosed reports summary after a socket closes.
OnSocketClosed(*SocketSummary)
}
type Mark struct {
PID string // PID of the proxy to forward the socket over.
CID string // CID identifies this socket.
UID string // UID of the app which owns this socket.
}
const (
ProtoTypeUDP = "udp"
ProtoTypeTCP = "tcp"
ProtoTypeICMP = "icmp"
)
var (
optionsBlock = &Mark{PID: ipn.Block}
optionsBase = &Mark{PID: ipn.Base}
errNone = errors.New("no error")
)
func icmpSummary(id, pid string) *SocketSummary {
return &SocketSummary{
Proto: ProtoTypeICMP,
ID: id,
PID: pid,
start: time.Now(),
Msg: errNone.Error(),
}
}
func tcpSummary(id, pid, uid string, dup bool, dst netip.Addr) *SocketSummary {
return &SocketSummary{
Proto: ProtoTypeTCP,
ID: id,
PID: pid,
UID: uid,
Dup: dup,
Target: dst.String(),
start: time.Now(),
Msg: errNone.Error(),
}
}
func udpSummary(id, pid, uid string, dup bool, dst netip.Addr) *SocketSummary {
s := tcpSummary(id, pid, uid, dup, dst)
s.Proto = ProtoTypeUDP
return s
}
func (s *SocketSummary) str() string {
return fmt.Sprintf("socket-summary: id=%s pid=%s uid=%s down=%d up=%d dur=%d synack=%d msg=%s",
s.ID, s.PID, s.UID, s.Rx, s.Tx, s.Duration, s.Rtt, s.Msg)
}
func (s *SocketSummary) elapsed() {
s.Duration = int32(time.Since(s.start).Seconds())
}
func (s *SocketSummary) done(errs ...error) {
defer func() {
if len(s.Msg) <= 0 {
s.Msg = errNone.Error()
}
}()
s.elapsed()
if len(errs) <= 0 {
return
}
err := errors.Join(errs...) // errs may be nil
if err != nil {
if s.Msg == errNone.Error() {
s.Msg = err.Error()
} else {
s.Msg = s.Msg + "; " + err.Error()
}
}
}