summaryrefslogtreecommitdiffstats
path: root/src/go/collectors/go.d.plugin/modules/openvpn/client/client.go
diff options
context:
space:
mode:
Diffstat (limited to '')
-rw-r--r--src/go/collectors/go.d.plugin/modules/openvpn/client/client.go153
1 files changed, 153 insertions, 0 deletions
diff --git a/src/go/collectors/go.d.plugin/modules/openvpn/client/client.go b/src/go/collectors/go.d.plugin/modules/openvpn/client/client.go
new file mode 100644
index 000000000..ddbfdeafb
--- /dev/null
+++ b/src/go/collectors/go.d.plugin/modules/openvpn/client/client.go
@@ -0,0 +1,153 @@
+// SPDX-License-Identifier: GPL-3.0-or-later
+
+package client
+
+import (
+ "fmt"
+ "regexp"
+ "strconv"
+ "strings"
+
+ "github.com/netdata/netdata/go/go.d.plugin/pkg/socket"
+)
+
+var (
+ reLoadStats = regexp.MustCompile(`^SUCCESS: nclients=([0-9]+),bytesin=([0-9]+),bytesout=([0-9]+)`)
+ reVersion = regexp.MustCompile(`^OpenVPN Version: OpenVPN ([0-9]+)\.([0-9]+)\.([0-9]+) .+Management Version: ([0-9])`)
+)
+
+const maxLinesToRead = 500
+
+// New creates new OpenVPN client.
+func New(config socket.Config) *Client {
+ return &Client{Client: socket.New(config)}
+}
+
+// Client represents OpenVPN client.
+type Client struct {
+ socket.Client
+}
+
+// Users Users.
+func (c *Client) Users() (Users, error) {
+ lines, err := c.get(commandStatus3, readUntilEND)
+ if err != nil {
+ return nil, err
+ }
+ return decodeUsers(lines)
+}
+
+// LoadStats LoadStats.
+func (c *Client) LoadStats() (*LoadStats, error) {
+ lines, err := c.get(commandLoadStats, readOneLine)
+ if err != nil {
+ return nil, err
+ }
+ return decodeLoadStats(lines)
+}
+
+// Version Version.
+func (c *Client) Version() (*Version, error) {
+ lines, err := c.get(commandVersion, readUntilEND)
+ if err != nil {
+ return nil, err
+ }
+ return decodeVersion(lines)
+}
+
+func (c *Client) get(command string, stopRead stopReadFunc) (output []string, err error) {
+ var num int
+ var maxLinesErr error
+ err = c.Command(command, func(bytes []byte) bool {
+ line := string(bytes)
+ num++
+ if num > maxLinesToRead {
+ maxLinesErr = fmt.Errorf("read line limit exceeded (%d)", maxLinesToRead)
+ return false
+ }
+
+ // skip real-time messages
+ if strings.HasPrefix(line, ">") {
+ return true
+ }
+
+ line = strings.Trim(line, "\r\n ")
+ output = append(output, line)
+ if stopRead != nil && stopRead(line) {
+ return false
+ }
+ return true
+ })
+ if maxLinesErr != nil {
+ return nil, maxLinesErr
+ }
+ return output, err
+}
+
+type stopReadFunc func(string) bool
+
+func readOneLine(_ string) bool { return true }
+
+func readUntilEND(s string) bool { return strings.HasSuffix(s, "END") }
+
+func decodeLoadStats(src []string) (*LoadStats, error) {
+ m := reLoadStats.FindStringSubmatch(strings.Join(src, " "))
+ if len(m) == 0 {
+ return nil, fmt.Errorf("parse failed : %v", src)
+ }
+ return &LoadStats{
+ NumOfClients: mustParseInt(m[1]),
+ BytesIn: mustParseInt(m[2]),
+ BytesOut: mustParseInt(m[3]),
+ }, nil
+}
+
+func decodeVersion(src []string) (*Version, error) {
+ m := reVersion.FindStringSubmatch(strings.Join(src, " "))
+ if len(m) == 0 {
+ return nil, fmt.Errorf("parse failed : %v", src)
+ }
+ return &Version{
+ Major: mustParseInt(m[1]),
+ Minor: mustParseInt(m[2]),
+ Patch: mustParseInt(m[3]),
+ Management: mustParseInt(m[4]),
+ }, nil
+}
+
+// works only for `status 3\n`
+func decodeUsers(src []string) (Users, error) {
+ var users Users
+
+ // [CLIENT_LIST common_name 178.66.34.194:54200 10.9.0.5 9319 8978 Thu May 9 05:01:44 2019 1557345704 username]
+ for _, v := range src {
+ if !strings.HasPrefix(v, "CLIENT_LIST") {
+ continue
+ }
+ parts := strings.Fields(v)
+ // Right after the connection there are no virtual ip, and both common name and username UNDEF
+ // CLIENT_LIST UNDEF 178.70.95.93:39324 1411 3474 Fri May 10 07:41:54 2019 1557441714 UNDEF
+ if len(parts) != 13 {
+ continue
+ }
+ u := User{
+ CommonName: parts[1],
+ RealAddress: parts[2],
+ VirtualAddress: parts[3],
+ BytesReceived: mustParseInt(parts[4]),
+ BytesSent: mustParseInt(parts[5]),
+ ConnectedSince: mustParseInt(parts[11]),
+ Username: parts[12],
+ }
+ users = append(users, u)
+ }
+ return users, nil
+}
+
+func mustParseInt(str string) int64 {
+ v, err := strconv.ParseInt(str, 10, 64)
+ if err != nil {
+ panic(err)
+ }
+ return v
+}