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 }