github.com/netdata/go.d.plugin@v0.58.1/modules/openvpn_status_log/parser.go (about)

     1  // SPDX-License-Identifier: GPL-3.0-or-later
     2  
     3  package openvpn_status_log
     4  
     5  import (
     6  	"bufio"
     7  	"fmt"
     8  	"os"
     9  	"strconv"
    10  	"strings"
    11  	"time"
    12  )
    13  
    14  type clientInfo struct {
    15  	commonName     string
    16  	bytesReceived  int64
    17  	bytesSent      int64
    18  	connectedSince int64
    19  }
    20  
    21  func parse(path string) ([]clientInfo, error) {
    22  	f, err := os.Open(path)
    23  	if err != nil {
    24  		return nil, err
    25  	}
    26  	defer func() { _ = f.Close() }()
    27  
    28  	sc := bufio.NewScanner(f)
    29  	_ = sc.Scan()
    30  	line := sc.Text()
    31  
    32  	if line == "OpenVPN CLIENT LIST" {
    33  		return parseV1(sc), nil
    34  	}
    35  	if strings.HasPrefix(line, "TITLE,OpenVPN") || strings.HasPrefix(line, "TITLE\tOpenVPN") {
    36  		return parseV2V3(sc), nil
    37  	}
    38  	if line == "OpenVPN STATISTICS" {
    39  		return parseStaticKey(sc), nil
    40  	}
    41  	return nil, fmt.Errorf("the status log file is invalid (%s)", path)
    42  }
    43  
    44  func parseV1(sc *bufio.Scanner) []clientInfo {
    45  	// https://github.com/OpenVPN/openvpn/blob/d5315a5d7400a26f1113bbc44766d49dd0c3688f/src/openvpn/multi.c#L836
    46  	var clients []clientInfo
    47  
    48  	for sc.Scan() {
    49  		if !strings.HasPrefix(sc.Text(), "Common Name") {
    50  			continue
    51  		}
    52  		for sc.Scan() && !strings.HasPrefix(sc.Text(), "ROUTING TABLE") {
    53  			parts := strings.Split(sc.Text(), ",")
    54  			if len(parts) != 5 {
    55  				continue
    56  			}
    57  
    58  			name := parts[0]
    59  			bytesRx, _ := strconv.ParseInt(parts[2], 10, 64)
    60  			bytesTx, _ := strconv.ParseInt(parts[3], 10, 64)
    61  			connSince, _ := time.Parse("Mon Jan 2 15:04:05 2006", parts[4])
    62  
    63  			clients = append(clients, clientInfo{
    64  				commonName:     name,
    65  				bytesReceived:  bytesRx,
    66  				bytesSent:      bytesTx,
    67  				connectedSince: connSince.Unix(),
    68  			})
    69  		}
    70  		break
    71  	}
    72  	return clients
    73  }
    74  
    75  func parseV2V3(sc *bufio.Scanner) []clientInfo {
    76  	// https://github.com/OpenVPN/openvpn/blob/d5315a5d7400a26f1113bbc44766d49dd0c3688f/src/openvpn/multi.c#L901
    77  	var clients []clientInfo
    78  	var sep string
    79  	if strings.IndexByte(sc.Text(), '\t') != -1 {
    80  		sep = "\t"
    81  	} else {
    82  		sep = ","
    83  	}
    84  
    85  	for sc.Scan() {
    86  		line := sc.Text()
    87  		if !strings.HasPrefix(line, "CLIENT_LIST") {
    88  			continue
    89  		}
    90  		parts := strings.Split(line, sep)
    91  		if len(parts) != 13 {
    92  			continue
    93  		}
    94  
    95  		name := parts[1]
    96  		bytesRx, _ := strconv.ParseInt(parts[5], 10, 64)
    97  		bytesTx, _ := strconv.ParseInt(parts[6], 10, 64)
    98  		connSince, _ := strconv.ParseInt(parts[8], 10, 64)
    99  
   100  		clients = append(clients, clientInfo{
   101  			commonName:     name,
   102  			bytesReceived:  bytesRx,
   103  			bytesSent:      bytesTx,
   104  			connectedSince: connSince,
   105  		})
   106  	}
   107  	return clients
   108  }
   109  
   110  func parseStaticKey(sc *bufio.Scanner) []clientInfo {
   111  	// https://github.com/OpenVPN/openvpn/blob/d5315a5d7400a26f1113bbc44766d49dd0c3688f/src/openvpn/sig.c#L283
   112  	var info clientInfo
   113  	for sc.Scan() {
   114  		line := sc.Text()
   115  		if !strings.HasPrefix(line, "TCP/UDP") {
   116  			continue
   117  		}
   118  		i := strings.IndexByte(line, ',')
   119  		if i == -1 || len(line) == i {
   120  			continue
   121  		}
   122  		bytes, _ := strconv.ParseInt(line[i+1:], 10, 64)
   123  		switch line[:i] {
   124  		case "TCP/UDP read bytes":
   125  			info.bytesReceived += bytes
   126  		case "TCP/UDP write bytes":
   127  			info.bytesSent += bytes
   128  		}
   129  	}
   130  	return []clientInfo{info}
   131  }