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  }