parselog.go 2.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118
  1. package ovpm
  2. import (
  3. "bufio"
  4. "io"
  5. "strconv"
  6. "strings"
  7. "time"
  8. "github.com/sirupsen/logrus"
  9. )
  10. // clEntry reprsents a parsed entry that is present on OpenVPN
  11. // log section CLIENT LIST.
  12. type clEntry struct {
  13. CommonName string `json:"common_name"`
  14. RealAddress string `json:"real_address"`
  15. BytesReceived uint64 `json:"bytes_received"`
  16. BytesSent uint64 `json:"bytes_sent"`
  17. ConnectedSince time.Time `json:"connected_since"`
  18. }
  19. // rtEntry reprsents a parsed entry that is present on OpenVPN
  20. // log section ROUTING TABLE.
  21. type rtEntry struct {
  22. VirtualAddress string `json:"virtual_address"`
  23. CommonName string `json:"common_name"`
  24. RealAddress string `json:"real_address"`
  25. LastRef time.Time `json:"last_ref"`
  26. }
  27. // parseStatusLog parses the received OpenVPN status log file.
  28. // And then returns the parsed client information.
  29. func parseStatusLog(f io.Reader) ([]clEntry, []rtEntry) {
  30. // Recover any panics
  31. defer func() {
  32. if r := recover(); r != nil {
  33. logrus.WithField("panic", r).Error("OpenVPN log file is corrupt")
  34. }
  35. }()
  36. // Parsing stages.
  37. const stageCL int = 0
  38. const stageRT int = 1
  39. const stageFin int = 2
  40. // Parsing variables.
  41. var currStage int
  42. var skipFor int
  43. var cl []clEntry
  44. var rt []rtEntry
  45. // Scan and parse the file by dividing it into chunks.
  46. scanner, skipFor := bufio.NewScanner(f), 3
  47. for lc := 0; scanner.Scan(); lc++ {
  48. if skipFor > 0 {
  49. skipFor--
  50. continue
  51. }
  52. txt := scanner.Text()
  53. switch currStage {
  54. case stageCL:
  55. if strings.Contains(txt, "ROUTING TABLE") {
  56. currStage = stageRT
  57. skipFor = 1
  58. continue
  59. }
  60. dat := strings.Split(txt, ",")
  61. cl = append(cl, clEntry{
  62. CommonName: trim(dat[0]),
  63. RealAddress: trim(dat[1]),
  64. BytesReceived: stoui64(trim(dat[2])),
  65. BytesSent: stoui64(trim(dat[3])),
  66. ConnectedSince: stodt(trim(dat[4])),
  67. })
  68. case stageRT:
  69. if strings.Contains(txt, "GLOBAL STATS") {
  70. currStage = stageFin
  71. break
  72. }
  73. dat := strings.Split(txt, ",")
  74. rt = append(rt, rtEntry{
  75. VirtualAddress: trim(dat[0]),
  76. CommonName: trim(dat[1]),
  77. RealAddress: trim(dat[2]),
  78. LastRef: stodt(trim(dat[3])),
  79. })
  80. }
  81. }
  82. if err := scanner.Err(); err != nil {
  83. panic(err)
  84. }
  85. return cl, rt
  86. }
  87. // stoi64 converts string to uint64.
  88. func stoui64(s string) uint64 {
  89. i, err := strconv.ParseInt(s, 0, 64)
  90. if err != nil {
  91. panic(err)
  92. }
  93. return uint64(i)
  94. }
  95. // stodt converts string to date time.
  96. func stodt(s string) time.Time {
  97. t, err := time.ParseInLocation(time.ANSIC, s, time.Local)
  98. if err != nil {
  99. panic(err)
  100. }
  101. return t
  102. }
  103. // trim will trim all leading and trailing whitespace from the s.
  104. func trim(s string) string {
  105. return strings.TrimSpace(s)
  106. }