-
Notifications
You must be signed in to change notification settings - Fork 6k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
16 changed files
with
877 additions
and
1 deletion.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,6 @@ | ||
## All available configuration options, their descriptions and default values: | ||
## https://github.com/netdata/netdata/tree/master/src/go/plugin/go.d/modules/tor#readme | ||
|
||
#jobs: | ||
# - name: local | ||
# address: 127.0.0.1:9051 |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,43 @@ | ||
// SPDX-License-Identifier: GPL-3.0-or-later | ||
|
||
package tor | ||
|
||
import ( | ||
"github.com/netdata/netdata/go/plugins/plugin/go.d/agent/module" | ||
) | ||
|
||
const ( | ||
prioTraffic = module.Priority + iota | ||
prioUptime | ||
) | ||
|
||
var charts = module.Charts{ | ||
trafficChart.Copy(), | ||
uptimeChart.Copy(), | ||
} | ||
|
||
var trafficChart = module.Chart{ | ||
ID: "traffic", | ||
Title: "Tor Traffic", | ||
Units: "KiB/s", | ||
Fam: "traffic", | ||
Ctx: "tor.traffic", | ||
Type: module.Area, | ||
Priority: prioTraffic, | ||
Dims: module.Dims{ | ||
{ID: "traffic/read", Name: "read", Algo: module.Incremental, Div: 1024}, | ||
{ID: "traffic/written", Name: "write", Algo: module.Incremental, Mul: -1, Div: 1024}, | ||
}, | ||
} | ||
|
||
var uptimeChart = module.Chart{ | ||
ID: "uptime", | ||
Title: "Tor Uptime", | ||
Units: "seconds", | ||
Fam: "uptime", | ||
Ctx: "tor.uptime", | ||
Priority: prioUptime, | ||
Dims: module.Dims{ | ||
{ID: "uptime"}, | ||
}, | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,117 @@ | ||
// SPDX-License-Identifier: GPL-3.0-or-later | ||
|
||
package tor | ||
|
||
import ( | ||
"bytes" | ||
"errors" | ||
"fmt" | ||
"strings" | ||
|
||
"github.com/netdata/netdata/go/plugins/plugin/go.d/pkg/socket" | ||
) | ||
|
||
// https://spec.torproject.org/control-spec/index.html | ||
// https://github.com/torproject/stem/blob/master/stem/control.py | ||
|
||
const ( | ||
cmdAuthenticate = "AUTHENTICATE" | ||
cmdQuit = "QUIT" | ||
cmdGetInfo = "GETINFO" | ||
) | ||
|
||
type controlConn interface { | ||
connect() error | ||
disconnect() | ||
|
||
getInfo(...string) ([]byte, error) | ||
} | ||
|
||
func newControlConn(conf Config) controlConn { | ||
return &torControlClient{ | ||
password: conf.Password, | ||
conn: socket.New(socket.Config{ | ||
Address: conf.Address, | ||
ConnectTimeout: conf.Timeout.Duration(), | ||
ReadTimeout: conf.Timeout.Duration(), | ||
WriteTimeout: conf.Timeout.Duration(), | ||
})} | ||
} | ||
|
||
type torControlClient struct { | ||
password string | ||
conn socket.Client | ||
} | ||
|
||
func (c *torControlClient) connect() error { | ||
if err := c.conn.Connect(); err != nil { | ||
return err | ||
} | ||
|
||
return c.authenticate() | ||
} | ||
|
||
func (c *torControlClient) authenticate() error { | ||
// https://spec.torproject.org/control-spec/commands.html#authenticate | ||
|
||
cmd := cmdAuthenticate | ||
if c.password != "" { | ||
cmd = fmt.Sprintf("%s \"%s\"", cmdAuthenticate, c.password) | ||
} | ||
|
||
var s string | ||
err := c.conn.Command(cmd+"\n", func(bs []byte) bool { | ||
s = string(bs) | ||
return false | ||
}) | ||
if err != nil { | ||
return fmt.Errorf("authentication failed: %v", err) | ||
} | ||
if !strings.HasPrefix(s, "250") { | ||
return fmt.Errorf("authentication failed: %s", s) | ||
} | ||
return nil | ||
} | ||
|
||
func (c *torControlClient) disconnect() { | ||
// https://spec.torproject.org/control-spec/commands.html#quit | ||
|
||
_ = c.conn.Command(cmdQuit+"\n", func(bs []byte) bool { return false }) | ||
_ = c.conn.Disconnect() | ||
} | ||
|
||
func (c *torControlClient) getInfo(keywords ...string) ([]byte, error) { | ||
// https://spec.torproject.org/control-spec/commands.html#getinfo | ||
|
||
if len(keywords) == 0 { | ||
return nil, errors.New("no keywords specified") | ||
} | ||
cmd := fmt.Sprintf("%s %s", cmdGetInfo, strings.Join(keywords, " ")) | ||
|
||
var buf bytes.Buffer | ||
var err error | ||
|
||
clientErr := c.conn.Command(cmd+"\n", func(bs []byte) bool { | ||
s := string(bs) | ||
|
||
switch { | ||
case strings.HasPrefix(s, "250-"): | ||
buf.WriteString(strings.TrimPrefix(s, "250-")) | ||
buf.WriteByte('\n') | ||
return true | ||
case strings.HasPrefix(s, "250 "): | ||
return false | ||
default: | ||
err = errors.New(s) | ||
return false | ||
} | ||
}) | ||
if clientErr != nil { | ||
return nil, fmt.Errorf("command '%s' failed: %v", cmd, clientErr) | ||
} | ||
if err != nil { | ||
return nil, fmt.Errorf("command '%s' failed: %v", cmd, err) | ||
} | ||
|
||
return buf.Bytes(), nil | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,65 @@ | ||
// SPDX-License-Identifier: GPL-3.0-or-later | ||
|
||
package tor | ||
|
||
import ( | ||
"bufio" | ||
"bytes" | ||
"fmt" | ||
"strconv" | ||
"strings" | ||
) | ||
|
||
func (t *Tor) collect() (map[string]int64, error) { | ||
if t.conn == nil { | ||
conn, err := t.establishConnection() | ||
if err != nil { | ||
return nil, err | ||
} | ||
t.conn = conn | ||
} | ||
|
||
mx := make(map[string]int64) | ||
if err := t.collectServerInfo(mx); err != nil { | ||
t.Cleanup() | ||
return nil, err | ||
} | ||
|
||
return mx, nil | ||
} | ||
|
||
func (t *Tor) collectServerInfo(mx map[string]int64) error { | ||
resp, err := t.conn.getInfo("traffic/read", "traffic/written", "uptime") | ||
if err != nil { | ||
return err | ||
} | ||
|
||
sc := bufio.NewScanner(bytes.NewReader(resp)) | ||
|
||
for sc.Scan() { | ||
line := sc.Text() | ||
|
||
key, value, ok := strings.Cut(line, "=") | ||
if !ok { | ||
return fmt.Errorf("failed to parse metric: %s", line) | ||
} | ||
|
||
v, err := strconv.ParseInt(value, 10, 64) | ||
if err != nil { | ||
return fmt.Errorf("failed to parse metric %s value: %v", line, err) | ||
} | ||
mx[key] = v | ||
} | ||
|
||
return nil | ||
} | ||
|
||
func (t *Tor) establishConnection() (controlConn, error) { | ||
conn := t.newConn(t.Config) | ||
|
||
if err := conn.connect(); err != nil { | ||
return nil, err | ||
} | ||
|
||
return conn, nil | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,53 @@ | ||
{ | ||
"jsonSchema": { | ||
"$schema": "http://json-schema.org/draft-07/schema#", | ||
"title": "Tor collector configuration.", | ||
"type": "object", | ||
"properties": { | ||
"update_every": { | ||
"title": "Update every", | ||
"description": "Data collection interval, measured in seconds.", | ||
"type": "integer", | ||
"minimum": 1, | ||
"default": 1 | ||
}, | ||
"address": { | ||
"title": "Address", | ||
"description": "The IP address and port where the Tor's Control Port listens for connections.", | ||
"type": "string", | ||
"default": "127.0.0.1:9051" | ||
}, | ||
"timeout": { | ||
"title": "Timeout", | ||
"description": "Timeout for establishing a connection and communication (reading and writing) in seconds.", | ||
"type": "number", | ||
"minimum": 0.5, | ||
"default": 1 | ||
}, | ||
"password": { | ||
"title": "Password", | ||
"description": "The password for authentication.", | ||
"type": "string", | ||
"sensitive": true | ||
} | ||
}, | ||
"required": [ | ||
"address" | ||
], | ||
"additionalProperties": false, | ||
"patternProperties": { | ||
"^name$": {} | ||
} | ||
}, | ||
"uiSchema": { | ||
"uiOptions": { | ||
"fullPage": true | ||
}, | ||
"timeout": { | ||
"ui:help": "Accepts decimals for precise control (e.g., type 1.5 for 1.5 seconds)." | ||
}, | ||
"password": { | ||
"ui:widget": "password" | ||
} | ||
} | ||
} |
Oops, something went wrong.