github.com/netdata/go.d.plugin@v0.58.1/modules/openvpn/client/client.go (about) 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/go.d.plugin/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 var maxLinesErr error 61 err = c.Command(command, func(bytes []byte) bool { 62 line := string(bytes) 63 num++ 64 if num > maxLinesToRead { 65 maxLinesErr = fmt.Errorf("read line limit exceeded (%d)", maxLinesToRead) 66 return false 67 } 68 69 // skip real-time messages 70 if strings.HasPrefix(line, ">") { 71 return true 72 } 73 74 line = strings.Trim(line, "\r\n ") 75 output = append(output, line) 76 if stopRead != nil && stopRead(line) { 77 return false 78 } 79 return true 80 }) 81 if maxLinesErr != nil { 82 return nil, maxLinesErr 83 } 84 return output, err 85 } 86 87 type stopReadFunc func(string) bool 88 89 func readOneLine(_ string) bool { return true } 90 91 func readUntilEND(s string) bool { return strings.HasSuffix(s, "END") } 92 93 func decodeLoadStats(src []string) (*LoadStats, error) { 94 m := reLoadStats.FindStringSubmatch(strings.Join(src, " ")) 95 if len(m) == 0 { 96 return nil, fmt.Errorf("parse failed : %v", src) 97 } 98 return &LoadStats{ 99 NumOfClients: mustParseInt(m[1]), 100 BytesIn: mustParseInt(m[2]), 101 BytesOut: mustParseInt(m[3]), 102 }, nil 103 } 104 105 func decodeVersion(src []string) (*Version, error) { 106 m := reVersion.FindStringSubmatch(strings.Join(src, " ")) 107 if len(m) == 0 { 108 return nil, fmt.Errorf("parse failed : %v", src) 109 } 110 return &Version{ 111 Major: mustParseInt(m[1]), 112 Minor: mustParseInt(m[2]), 113 Patch: mustParseInt(m[3]), 114 Management: mustParseInt(m[4]), 115 }, nil 116 } 117 118 // works only for `status 3\n` 119 func decodeUsers(src []string) (Users, error) { 120 var users Users 121 122 // [CLIENT_LIST common_name 178.66.34.194:54200 10.9.0.5 9319 8978 Thu May 9 05:01:44 2019 1557345704 username] 123 for _, v := range src { 124 if !strings.HasPrefix(v, "CLIENT_LIST") { 125 continue 126 } 127 parts := strings.Fields(v) 128 // Right after the connection there are no virtual ip, and both common name and username UNDEF 129 // CLIENT_LIST UNDEF 178.70.95.93:39324 1411 3474 Fri May 10 07:41:54 2019 1557441714 UNDEF 130 if len(parts) != 13 { 131 continue 132 } 133 u := User{ 134 CommonName: parts[1], 135 RealAddress: parts[2], 136 VirtualAddress: parts[3], 137 BytesReceived: mustParseInt(parts[4]), 138 BytesSent: mustParseInt(parts[5]), 139 ConnectedSince: mustParseInt(parts[11]), 140 Username: parts[12], 141 } 142 users = append(users, u) 143 } 144 return users, nil 145 } 146 147 func mustParseInt(str string) int64 { 148 v, err := strconv.ParseInt(str, 10, 64) 149 if err != nil { 150 panic(err) 151 } 152 return v 153 }