master
go 150 lines 3.56 KB
Raw
1 // SPDX-License-Identifier: GPL-3.0-or-later
2
3 package client
4
5 import (
6 "fmt"
7 "regexp"
8 "strconv"
9 "strings"
10
11 "github.com/netdata/netdata/go/plugins/plugin/go.d/pkg/socket"
12 )
13
14 var (
15 reLoadStats = regexp.MustCompile(`^SUCCESS: nclients=([0-9]+),bytesin=([0-9]+),bytesout=([0-9]+)`)
16 reVersion = regexp.MustCompile(`^OpenVPN Version: OpenVPN ([0-9]+)\.([0-9]+)\.([0-9]+) .+Management Version: ([0-9])`)
17 )
18
19 const maxLinesToRead = 500
20
21 // New creates new OpenVPN client.
22 func New(config socket.Config) *Client {
23 return &Client{Client: socket.New(config)}
24 }
25
26 // Client represents OpenVPN client.
27 type Client struct {
28 socket.Client
29 }
30
31 // Users Users.
32 func (c *Client) Users() (Users, error) {
33 lines, err := c.get(commandStatus3, readUntilEND)
34 if err != nil {
35 return nil, err
36 }
37 return decodeUsers(lines)
38 }
39
40 // LoadStats LoadStats.
41 func (c *Client) LoadStats() (*LoadStats, error) {
42 lines, err := c.get(commandLoadStats, readOneLine)
43 if err != nil {
44 return nil, err
45 }
46 return decodeLoadStats(lines)
47 }
48
49 // Version Version.
50 func (c *Client) Version() (*Version, error) {
51 lines, err := c.get(commandVersion, readUntilEND)
52 if err != nil {
53 return nil, err
54 }
55 return decodeVersion(lines)
56 }
57
58 func (c *Client) get(command string, stopRead stopReadFunc) (output []string, err error) {
59 var num int
60 if err := c.Command(command, func(bytes []byte) (bool, error) {
61 line := string(bytes)
62 num++
63 if num > maxLinesToRead {
64 return false, fmt.Errorf("read line limit exceeded (%d)", maxLinesToRead)
65 }
66
67 // skip real-time messages
68 if strings.HasPrefix(line, ">") {
69 return true, nil
70 }
71
72 line = strings.Trim(line, "\r\n ")
73 output = append(output, line)
74 if stopRead != nil && stopRead(line) {
75 return false, nil
76 }
77 return true, nil
78 }); err != nil {
79 return nil, err
80 }
81 return output, err
82 }
83
84 type stopReadFunc func(string) bool
85
86 func readOneLine(_ string) bool { return true }
87
88 func readUntilEND(s string) bool { return strings.HasSuffix(s, "END") }
89
90 func decodeLoadStats(src []string) (*LoadStats, error) {
91 m := reLoadStats.FindStringSubmatch(strings.Join(src, " "))
92 if len(m) == 0 {
93 return nil, fmt.Errorf("parse failed : %v", src)
94 }
95 return &LoadStats{
96 NumOfClients: mustParseInt(m[1]),
97 BytesIn: mustParseInt(m[2]),
98 BytesOut: mustParseInt(m[3]),
99 }, nil
100 }
101
102 func decodeVersion(src []string) (*Version, error) {
103 m := reVersion.FindStringSubmatch(strings.Join(src, " "))
104 if len(m) == 0 {
105 return nil, fmt.Errorf("parse failed : %v", src)
106 }
107 return &Version{
108 Major: mustParseInt(m[1]),
109 Minor: mustParseInt(m[2]),
110 Patch: mustParseInt(m[3]),
111 Management: mustParseInt(m[4]),
112 }, nil
113 }
114
115 // works only for `status 3\n`
116 func decodeUsers(src []string) (Users, error) {
117 var users Users
118
119 // [CLIENT_LIST common_name 178.66.34.194:54200 10.9.0.5 9319 8978 Thu May 9 05:01:44 2019 1557345704 username]
120 for _, v := range src {
121 if !strings.HasPrefix(v, "CLIENT_LIST") {
122 continue
123 }
124 parts := strings.Fields(v)
125 // Right after the connection there are no virtual ip, and both common name and username UNDEF
126 // CLIENT_LIST UNDEF 178.70.95.93:39324 1411 3474 Fri May 10 07:41:54 2019 1557441714 UNDEF
127 if len(parts) != 13 {
128 continue
129 }
130 u := User{
131 CommonName: parts[1],
132 RealAddress: parts[2],
133 VirtualAddress: parts[3],
134 BytesReceived: mustParseInt(parts[4]),
135 BytesSent: mustParseInt(parts[5]),
136 ConnectedSince: mustParseInt(parts[11]),
137 Username: parts[12],
138 }
139 users = append(users, u)
140 }
141 return users, nil
142 }
143
144 func mustParseInt(str string) int64 {
145 v, err := strconv.ParseInt(str, 10, 64)
146 if err != nil {
147 panic(err)
148 }
149 return v
150 }