├── Dockerfile ├── HFP.go ├── Makefile ├── README.md ├── go.mod ├── go.sum ├── hep.go └── prometheus.go /Dockerfile: -------------------------------------------------------------------------------- 1 | FROM golang:alpine as builder 2 | 3 | RUN apk update && apk add --no-cache git build-base 4 | RUN git clone https://github.com/ivlovric/HFP /HFP 5 | WORKDIR /HFP 6 | RUN go build -ldflags "-s -w" -o HFP *.go 7 | 8 | FROM alpine:latest 9 | RUN apk --no-cache add ca-certificates 10 | WORKDIR /root/ 11 | COPY --from=builder /HFP/HFP . 12 | 13 | ENTRYPOINT ["/root/HFP"] 14 | -------------------------------------------------------------------------------- /HFP.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | //"bufio" 5 | "flag" 6 | "fmt" 7 | "io/ioutil" 8 | "log" 9 | "net" 10 | "os" 11 | "strings" 12 | "sync" 13 | "time" 14 | 15 | "github.com/guumaster/logsymbols" 16 | ) 17 | 18 | const AppVersion = "1.0" 19 | 20 | 21 | var localAddr *string = flag.String("l", ":9060", "Local HEP listening address") 22 | var remoteAddr *string = flag.String("r", "192.168.2.2:9060", "Remote HEP address") 23 | var IPfilter *string = flag.String("ipf", "", "IP filter address from HEP SRC or DST chunks. Option can use multiple IP as comma sepeated values. Default is no filter without processing HEP acting as high performance HEP proxy") 24 | var IPfilterAction *string = flag.String("ipfa", "pass", "IP filter Action. Options are pass or reject") 25 | var Debug *string = flag.String("d", "off", "Debug options are off or on") 26 | var PrometheusPort *string = flag.String("prom", "8090", "Prometheus metrics port") 27 | 28 | var ( 29 | AppLogger *log.Logger 30 | filterIPs []string 31 | HFPlog string = "HFP.log" 32 | HEPsavefile string = "HEP/HEP-saved.arch" 33 | ) 34 | 35 | func initLoopbackConn(wg *sync.WaitGroup) { 36 | 37 | //Connect loopback in 38 | outnet, err := net.Dial("tcp4", *localAddr) 39 | 40 | if err != nil { 41 | log.Println("c==>", logsymbols.Error, "|| INITIAL Loopback IN", err) 42 | AppLogger.Println("c==>", logsymbols.Error, "|| INITIAL Loopback IN error", err) 43 | 44 | } else { 45 | _, err := outnet.Write([]byte("HELLO HFP")) 46 | if err != nil { 47 | log.Println("HELLO HFP c==>", logsymbols.Error, "|| Send HELLO HFP error", err) 48 | AppLogger.Println("HELLO HFP c==>", logsymbols.Error, "|| Send HELLO HFP error", err) 49 | } else { 50 | log.Println("HELLO HFP c==>", logsymbols.Success, "|| INITIAL Dial LOOPBACK IN success") 51 | AppLogger.Println("HELLO HFP c==>", logsymbols.Success, "|| INITIAL Dial LOOPBACK IN success") 52 | } 53 | 54 | } 55 | 56 | wg.Add(1) 57 | wg.Done() 58 | 59 | } 60 | 61 | func connectToHEPBackend(dst string) net.Conn { 62 | 63 | 64 | for { 65 | conn, err := net.Dial("tcp", dst) 66 | if err != nil { 67 | log.Println("Unable to connect to server: ", err) 68 | connectionStatus.Set(0) 69 | time.Sleep(time.Second * 5) // wait for 5 seconds before reconnecting 70 | 71 | } else { 72 | log.Println("Connected to server successfully ", conn) 73 | connectionStatus.Set(1) 74 | copyHEPFileOut(conn) 75 | return conn 76 | } 77 | 78 | } 79 | } 80 | 81 | func handleConnection(clientConn net.Conn, destAddr string) { 82 | var destConn net.Conn 83 | //var err error 84 | 85 | // use a buffer to transfer data between connections 86 | buf := make([]byte, 65535) 87 | 88 | // for { 89 | // destConn, err = net.Dial("tcp", destAddr) 90 | // if err != nil { 91 | // log.Println("||-->", logsymbols.Error, "Dial OUT reconnect failure - retrying", err) 92 | // AppLogger.Println("||-->", logsymbols.Error, " Dial OUT reconnect failure - retrying") 93 | // copyHEPbufftoFile(buf[:n2], HEPsavefile) 94 | // 95 | // //log.Println(err) 96 | // time.Sleep(time.Second * 5) // wait for 5 seconds before reconnecting 97 | // continue 98 | // } 99 | // break 100 | // } 101 | //defer destConn.Close() 102 | 103 | go func() { 104 | destConn = connectToHEPBackend(destAddr) 105 | }() 106 | 107 | //reader := bufio.NewReader(clientConn) 108 | for { 109 | //n, err := reader.Read(buf) 110 | n, err := clientConn.Read(buf) 111 | if err != nil { 112 | log.Println(err) 113 | return 114 | } 115 | 116 | if *Debug == "on" { 117 | log.Println("-->|| Got", n, "bytes on wire -- Total buffer size:", len(buf)) 118 | } 119 | 120 | //Prometheus timestamp metric of incoming packet to detect lack of inbound HEP traffic 121 | clientLastMetricTimestamp.SetToCurrentTime() 122 | 123 | if destConn != nil { 124 | 125 | // 126 | if *IPfilter != "" && *IPfilterAction == "pass" { 127 | hepPkt, err := DecodeHEP(buf[:n]) 128 | if err != nil { 129 | log.Println("Error decoding HEP", err) 130 | } 131 | 132 | if *Debug == "on" { 133 | //log.Println("HEP decoded ", hepPkt) 134 | log.Println("HEP decoded SRC IP", hepPkt.SrcIP) 135 | log.Println("HEP decoded DST IP", hepPkt.DstIP) 136 | } 137 | 138 | var accepted bool = false 139 | for _, ipf := range filterIPs { 140 | if hepPkt.SrcIP == string(ipf) || hepPkt.DstIP == string(ipf) || string(buf[:n]) == "HELLO HFP" { 141 | 142 | //Send HEP out to backend 143 | if _, err_HEPout := destConn.Write(buf[:n]); err_HEPout != nil { 144 | log.Println("||-->", logsymbols.Error, " Sending HEP OUT error:", err_HEPout) 145 | // rb := bytes.NewReader(buf[:data]) 146 | connectionStatus.Set(0) 147 | copyHEPbufftoFile(buf[:n], HEPsavefile) 148 | accepted = true 149 | 150 | for { 151 | destConn, err = net.Dial("tcp4", destAddr) 152 | if err != nil { 153 | log.Println("||-->", logsymbols.Error, " Dial OUT reconnect failure - retrying", err) 154 | AppLogger.Println("||-->", logsymbols.Error, " Dial OUT reconnect failure - retrying") 155 | time.Sleep(time.Second * 5) // wait for 5 seconds before reconnecting 156 | continue 157 | } else { 158 | connectionStatus.Set(1) 159 | copyHEPFileOut(destConn) 160 | } 161 | break 162 | } 163 | continue 164 | 165 | } else { 166 | if *Debug == "on" { 167 | if string(buf[:n]) == "HELLO HFP" { 168 | log.Println("||--> Sending init HELLO HFP successful with filter for", string(ipf), "to", destConn.RemoteAddr()) 169 | } else { 170 | log.Println("||--> Sending HEP OUT successful with filter for", string(ipf), "to", destConn.RemoteAddr()) 171 | } 172 | } 173 | accepted = true 174 | 175 | } 176 | } 177 | } 178 | 179 | if !accepted { 180 | if *Debug == "on" { 181 | log.Println("-->", logsymbols.Error, "|| HEP filter not matched with source or destination IP in HEP packet", hepPkt.SrcIP, "or", hepPkt.DstIP) 182 | } 183 | } 184 | 185 | } else if *IPfilter != "" && *IPfilterAction == "reject" { 186 | hepPkt, err := DecodeHEP(buf[:n]) 187 | if err != nil { 188 | log.Println("Error decoding HEP", err) 189 | } 190 | 191 | if *Debug == "on" { 192 | //log.Println("HEP decoded ", hepPkt) 193 | log.Println("HEP decoded SRC IP", hepPkt.SrcIP) 194 | log.Println("HEP decoded DST IP", hepPkt.DstIP) 195 | } 196 | 197 | var rejected bool = false 198 | for _, ipf := range filterIPs { 199 | if hepPkt.SrcIP == string(ipf) || hepPkt.DstIP == string(ipf) { 200 | clientConn.Write([]byte("Rejecting IP")) 201 | if *Debug == "on" { 202 | log.Printf("-->|| Rejecting IP:%q", ipf) 203 | } 204 | rejected = true 205 | break 206 | } 207 | } 208 | 209 | if !rejected { 210 | //Send HEP out to backend 211 | if _, err_HEPout := destConn.Write(buf[:n]); err_HEPout != nil { 212 | log.Println("||-->", logsymbols.Error, " Sending HEP OUT error:", err_HEPout) 213 | //rb := bytes.NewReader(buf[:data]) 214 | connectionStatus.Set(0) 215 | copyHEPbufftoFile(buf[:n], HEPsavefile) 216 | 217 | for { 218 | destConn, err = net.Dial("tcp4", destAddr) 219 | if err != nil { 220 | log.Println("||-->", logsymbols.Error, " Dial OUT reconnect failure - retrying", err) 221 | AppLogger.Println("||-->", logsymbols.Error, " Dial OUT reconnect failure - retrying") 222 | time.Sleep(time.Second * 5) // wait for 5 seconds before reconnecting 223 | continue 224 | } else { 225 | connectionStatus.Set(1) 226 | copyHEPFileOut(destConn) 227 | } 228 | break 229 | } 230 | continue 231 | 232 | //return 233 | } else { 234 | if *Debug == "on" { 235 | log.Println("||-->", logsymbols.Success, " Sending HEP OUT successful with filter to", destConn.RemoteAddr()) 236 | } 237 | } 238 | } 239 | 240 | } else { 241 | //Send HEP out to backend 242 | _, err_HEPout := destConn.Write(buf[:n]) 243 | if err_HEPout != nil { 244 | log.Println("||-->", logsymbols.Error, " Sending HEP OUT error:", err_HEPout) 245 | // rb := bytes.NewReader(buf[:data]) 246 | connectionStatus.Set(0) 247 | copyHEPbufftoFile(buf[:n], HEPsavefile) 248 | 249 | for { 250 | destConn, err = net.Dial("tcp4", destAddr) 251 | if err != nil { 252 | log.Println("||-->", logsymbols.Error, " Dial OUT reconnect failure - retrying", err) 253 | AppLogger.Println("||-->", logsymbols.Error, " Dial OUT reconnect failure - retrying") 254 | time.Sleep(time.Second * 5) // wait for 5 seconds before reconnecting 255 | continue 256 | } else { 257 | connectionStatus.Set(1) 258 | copyHEPFileOut(destConn) 259 | } 260 | break 261 | } 262 | continue 263 | 264 | // return 265 | } else { 266 | if *Debug == "on" { 267 | if string(buf[:n]) == "HELLO HFP" { 268 | log.Println("||-->", logsymbols.Success, " Sending init HELLO HFP successful without filters to", destConn.RemoteAddr()) 269 | } else { 270 | log.Println("||-->", logsymbols.Success, " Sending HEP OUT successful without filters to", destConn.RemoteAddr()) 271 | } 272 | } 273 | } 274 | } 275 | 276 | // 277 | //_, err = destConn.Write(buf[:n]) 278 | // if err != nil { 279 | // log.Println(logsymbols.Error, err) 280 | // destConn.Close() 281 | // copyHEPbufftoFile(buf[:n], HEPsavefile) 282 | // for { 283 | // destConn, err = net.Dial("tcp4", destAddr) 284 | // if err != nil { 285 | // log.Println("||-->X Dial OUT reconnect failure - retrying", err) 286 | // AppLogger.Println("||-->X Dial OUT reconnect failure - retrying") 287 | // time.Sleep(time.Second * 5) // wait for 5 seconds before reconnecting 288 | // continue 289 | // } else { 290 | // copyHEPFileOut(destConn) 291 | // } 292 | // break 293 | // } 294 | // continue 295 | // } 296 | } else { 297 | 298 | hepPkt, err := DecodeHEP(buf[:n]) 299 | if err != nil { 300 | log.Println("Error decoding HEP", err) 301 | } 302 | 303 | if *Debug == "on" { 304 | //log.Println("HEP decoded ", hepPkt) 305 | log.Println("HEP decoded SRC IP", hepPkt.SrcIP) 306 | log.Println("HEP decoded DST IP", hepPkt.DstIP) 307 | } 308 | 309 | for _, ipf := range filterIPs { 310 | if ((hepPkt.SrcIP == string(ipf) || hepPkt.DstIP == string(ipf) || string(buf[:n]) == "HELLO HFP") && *IPfilterAction == "pass") || ((hepPkt.SrcIP != string(ipf) || hepPkt.DstIP != string(ipf)) && *IPfilterAction == "reject") { 311 | copyHEPbufftoFile(buf[:n], HEPsavefile) 312 | } else { 313 | log.Println("Not logging filtered HEP traffic") 314 | } 315 | 316 | } 317 | } 318 | 319 | } 320 | } 321 | 322 | func copyHEPbufftoFile(inbytes []byte, file string) (int64, error) { 323 | 324 | destination, err := os.OpenFile(file, os.O_APPEND|os.O_CREATE|os.O_WRONLY, 0666) 325 | if err != nil { 326 | fmt.Println("Open HEP file error", err) 327 | } 328 | 329 | defer destination.Close() 330 | nBytes, err := destination.Write(inbytes) 331 | 332 | if err != nil { 333 | log.Println("||-->", logsymbols.Error, " File Send HEP from buffer to file error", err) 334 | AppLogger.Println("||-->", logsymbols.Error, " File Send HEP from buffer to file error", err) 335 | 336 | } else { 337 | log.Println("||-->", logsymbols.Success, " File Send HEP from buffer to file OK") 338 | AppLogger.Println("||-->", logsymbols.Success, "File Send HEP from buffer to file OK") 339 | 340 | go hepBytesInFile.Add(float64(nBytes)) 341 | 342 | } 343 | 344 | return int64(nBytes), err 345 | 346 | } 347 | 348 | func copyHEPFileOut(outnet net.Conn) (int, error) { 349 | 350 | HEPFileData, HEPFileDataerr := ioutil.ReadFile(HEPsavefile) 351 | if HEPFileDataerr != nil { 352 | fmt.Println("Read HEP file error", HEPFileDataerr) 353 | } 354 | 355 | //Send Logged HEP upon reconnect out to backend 356 | hl, err := outnet.Write(HEPFileData) 357 | if err != nil { 358 | log.Println("||-->X Send HEP from LOG error", err) 359 | AppLogger.Println("||-->X Send HEP from LOG error", err) 360 | hepFileFlushesError.Inc() 361 | } else { 362 | fi, err := os.Stat(HEPsavefile) 363 | if err != nil { 364 | log.Println("Cannot stat HEP log file", err) 365 | AppLogger.Println("Cannot stat HEP log file", err) 366 | } 367 | 368 | if fi.Size() > 0 { 369 | log.Println("||-->", logsymbols.Success, " Send HEP from LOG OK -", hl, "bytes") 370 | log.Println("Clearing HEP file") 371 | AppLogger.Println("||-->", logsymbols.Success, " Send HEP from LOG OK -", hl, "bytes") 372 | AppLogger.Println("Clearing HEP file") 373 | //Recreate file, thus cleaning the content 374 | os.Create(HEPsavefile) 375 | hepFileFlushesSuccess.Inc() 376 | } 377 | } 378 | 379 | return hl, err 380 | } 381 | 382 | func main() { 383 | 384 | var wg sync.WaitGroup 385 | logsymbols.ForceColors() 386 | 387 | version := flag.Bool("v", false, "Prints current HFP version") 388 | flag.Parse() 389 | 390 | if *version { 391 | fmt.Println(AppVersion) 392 | os.Exit(0) 393 | } 394 | 395 | filterIPs = strings.Split(*IPfilter, ",") 396 | 397 | errmkdir := os.Mkdir("HEP", 0755) 398 | if errmkdir != nil { 399 | log.Println(errmkdir) 400 | } 401 | 402 | if _, errhfexist := os.Stat(HEPsavefile); errhfexist != nil { 403 | if os.IsNotExist(errhfexist) { 404 | fmt.Println("HEP File doesnt exists - Creating", errhfexist) 405 | _, errhfcreate := os.Create(HEPsavefile) 406 | fmt.Println(logsymbols.Info, "-->|| Creating HEP file") 407 | if errhfcreate != nil { 408 | fmt.Println("Create file error", errhfcreate) 409 | return 410 | } 411 | } 412 | } 413 | 414 | applog, err := os.OpenFile(HFPlog, os.O_APPEND|os.O_CREATE|os.O_WRONLY, 0666) 415 | if err != nil { 416 | log.Fatal(err) 417 | } 418 | AppLogger = log.New(applog, "INFO: ", log.Ldate|log.Ltime|log.Lshortfile) 419 | 420 | fi, err := os.Stat(HEPsavefile) 421 | if err != nil { 422 | log.Println(logsymbols.Error, err) 423 | } 424 | fmt.Println(logsymbols.Info, "Saved HEP file is ", fi.Size(), "bytes\n") 425 | 426 | fmt.Printf("Listening for HEP on: %v\nProxying HEP to: %v\nIPFilter: %v\nIPFilterAction: %v\nPrometheus metrics: %v\n\n", *localAddr, *remoteAddr, *IPfilter, *IPfilterAction, *PrometheusPort) 427 | AppLogger.Println("Listening for HEP on:", *localAddr, "\n", "Proxying HEP to:", *remoteAddr, "\n", "IPFilter:", *IPfilter, "\n", "IPFilterAction:", *IPfilterAction, "\n", "Prometheus metrics:", *PrometheusPort, "\n") 428 | 429 | if *IPfilter == "" { 430 | fmt.Println(logsymbols.Success, "HFP starting in proxy high performance mode\n__________________________________________\n") 431 | AppLogger.Println(logsymbols.Success, "HFP starting in proxy high performance mode\n__________________________________________\n") 432 | } else { 433 | fmt.Println(logsymbols.Success, "HFP starting in proxy processing mode\n_____________________________________\n") 434 | AppLogger.Println(logsymbols.Success, "HFP starting in proxy processing mode\n_____________________________________\n") 435 | } 436 | 437 | addr, err := net.ResolveTCPAddr("tcp", *localAddr) 438 | if err != nil { 439 | log.Println(logsymbols.Error, err) 440 | return 441 | } 442 | listener, err := net.ListenTCP("tcp4", addr) 443 | if err != nil { 444 | fmt.Println(logsymbols.Error, "|| HFP starting error", err) 445 | os.Exit(1) 446 | // } else { 447 | // fmt.Println(logsymbols.Success, "|| HFP listener started") 448 | // 449 | // } 450 | } 451 | defer listener.Close() 452 | 453 | go startMetrics(&wg) 454 | go initLoopbackConn(&wg) 455 | 456 | wg.Wait() 457 | 458 | for { 459 | clientConn, err := listener.AcceptTCP() 460 | log.Println(logsymbols.Success, "-->|| New connection from", clientConn.RemoteAddr()) 461 | AppLogger.Println(logsymbols.Success, "-->|| New connection from", clientConn.RemoteAddr()) 462 | connectedClients.Inc() 463 | 464 | if err != nil { 465 | log.Println(logsymbols.Error, err) 466 | return 467 | } 468 | 469 | for i := 1; i <= 2; i++ { 470 | go handleConnection(clientConn, *remoteAddr) 471 | } 472 | } 473 | 474 | } 475 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | all: 2 | go build -ldflags "-s -w" -o HFP *.go 3 | 4 | debug: 5 | go build -o HFP *.go 6 | 7 | .PHONY: clean 8 | clean: 9 | rm -fr HFP 10 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # ![image](https://user-images.githubusercontent.com/1423657/147674071-15879110-4d36-40d4-9605-651b21301866.png) 2 | 3 | #### HEP Fidelity Proxy 4 | 5 | Reliable way of relaying all your [HEP](http://hep.sipcapture.org) to any HEP remote server that is behind unreliable networks. 6 | 7 | It is buffered TCP proxy with option of storing HEP locally in cases of backend HEP server unavailability and replaying of that HEP after HEP server becomes reachable again. It can be beneficial in highly distributed voice networks to reliably deliver your HEP to its destination without additional infrastructure. 8 | It can be deployed locally to every HEP generating node within one premesis/DC/location acting as addon (1:1) approach or HEP generating nodes can connect to one HFP that will reliably proxy HEP generated within one premesis/DC/location (N:1) 9 | 10 | From version 0.2 two modes of operation are supported. 11 | - Strict reliable HEP proxy mode without processing for high-performance 12 | - Reliable processing HEP proxy mode for filtering purposes to filter HEP by IP whether it is from HEP source or destination fields. It is engaged when "ipf" switch is configured 13 | 14 | 15 | ### Usage 16 | ``` 17 | ./HFP -l :9060 -r (HEP TCP server we want to reliably proxy HEP) 18 | 19 | Options: 20 | -l string 21 | Local HEP listening address (default ":9060") 22 | -r string 23 | Remote HEP address (default "192.168.2.2:9060") 24 | -ipf string 25 | IP filter address from HEP SRC or DST chunks. Option can use multiple IP as comma sepeated values. Default is no filter without processing HEP acting as high performance HEP proxy 26 | -ipfa string 27 | IP filter Action. Options are pass or reject (default "pass") 28 | -d string 29 | Debug options are off or on (default "off") 30 | -prom string 31 | Prometheus metrics port (default "8090") 32 | -v 33 | Prints current HFP version 34 | ``` 35 | 36 | ### Build 37 | ##### Manual 38 | Building HFP requires go 1.15+ 39 | ``` 40 | make 41 | ``` 42 | ###### Docker 43 | ``` 44 | docker build -t sipcapture/HFP . 45 | docker run -ti --rm sipcapture/HFP -l :9062 -r 1.2.3.4:9062 (optional: -ipf -ipfa -d -prom ) 46 | ``` 47 | 48 | ### Run 49 | image 50 | 51 | ### Flow Diagrams 52 | 53 | 54 | image 55 | 56 | 57 | ### Metrics 58 | Metrics are accessable on port 8090 unless port is changed by option flag. Example: http://HFP:8090/metrics 59 | 60 | Prometheus metrics in grafana 61 | 62 | Screenshot 2021-09-08 at 12 42 45 63 | Screenshot 2021-09-08 at 12 44 29 64 | 65 | 66 | 67 | ### Note 68 | 69 | HEP parser and decoder used from https://github.com/sipcapture/heplify-server Heplify-server project 70 | -------------------------------------------------------------------------------- /go.mod: -------------------------------------------------------------------------------- 1 | module github.com/ivlovric/HFP 2 | 3 | go 1.15 4 | 5 | require ( 6 | github.com/guumaster/logsymbols v0.3.1 // indirect 7 | github.com/prometheus/client_golang v1.11.0 8 | ) 9 | -------------------------------------------------------------------------------- /go.sum: -------------------------------------------------------------------------------- 1 | cloud.google.com/go v0.34.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= 2 | github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc= 3 | github.com/alecthomas/template v0.0.0-20190718012654-fb15b899a751/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc= 4 | github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0= 5 | github.com/alecthomas/units v0.0.0-20190717042225-c3de453c63f4/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0= 6 | github.com/alecthomas/units v0.0.0-20190924025748-f65c72e2690d/go.mod h1:rBZYJk541a8SKzHPHnH3zbiI+7dagKZ0cgpgrD7Fyho= 7 | github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q= 8 | github.com/beorn7/perks v1.0.0/go.mod h1:KWe93zE9D1o94FZ5RNwFwVgaQK1VOXiVxmqh+CedLV8= 9 | github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM= 10 | github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw= 11 | github.com/cespare/xxhash/v2 v2.1.1 h1:6MnRN8NT7+YBpUIWxHtefFZOKTAPgGjpQSxqLNn0+qY= 12 | github.com/cespare/xxhash/v2 v2.1.1/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= 13 | github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= 14 | github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= 15 | github.com/go-kit/kit v0.8.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as= 16 | github.com/go-kit/kit v0.9.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as= 17 | github.com/go-kit/log v0.1.0/go.mod h1:zbhenjAZHb184qTLMA9ZjW7ThYL0H2mk7Q6pNt4vbaY= 18 | github.com/go-logfmt/logfmt v0.3.0/go.mod h1:Qt1PoO58o5twSAckw1HlFXLmHsOX5/0LbT9GBnD5lWE= 19 | github.com/go-logfmt/logfmt v0.4.0/go.mod h1:3RMwSq7FuexP4Kalkev3ejPJsZTpXXBr9+V4qmtdjCk= 20 | github.com/go-logfmt/logfmt v0.5.0/go.mod h1:wCYkCAKZfumFQihp8CzCvQ3paCTfi41vtzG1KdI/P7A= 21 | github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY= 22 | github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ= 23 | github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= 24 | github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= 25 | github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= 26 | github.com/golang/protobuf v1.4.0-rc.1/go.mod h1:ceaxUfeHdC40wWswd/P6IGgMaK3YpKi5j83Wpe3EHw8= 27 | github.com/golang/protobuf v1.4.0-rc.1.0.20200221234624-67d41d38c208/go.mod h1:xKAWHe0F5eneWXFV3EuXVDTCmh+JuBKY0li0aMyXATA= 28 | github.com/golang/protobuf v1.4.0-rc.2/go.mod h1:LlEzMj4AhA7rCAGe4KMBDvJI+AwstrUpVNzEA03Pprs= 29 | github.com/golang/protobuf v1.4.0-rc.4.0.20200313231945-b860323f09d0/go.mod h1:WU3c8KckQ9AFe+yFwt9sWVRKCVIyN9cPHBJSNnbL67w= 30 | github.com/golang/protobuf v1.4.0/go.mod h1:jodUvKwWbYaEsadDk5Fwe5c77LiNKVO9IDvqG2KuDX0= 31 | github.com/golang/protobuf v1.4.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI= 32 | github.com/golang/protobuf v1.4.3 h1:JjCZWpVbqXDqFVmTfYWEVTMIYrL/NPdPSCHPJ0T/raM= 33 | github.com/golang/protobuf v1.4.3/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI= 34 | github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= 35 | github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= 36 | github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= 37 | github.com/google/go-cmp v0.5.4/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= 38 | github.com/google/go-cmp v0.5.5 h1:Khx7svrCpmxxtHBq5j2mp/xVjsi8hQMfNLvJFAlrGgU= 39 | github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= 40 | github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= 41 | github.com/jpillora/backoff v1.0.0/go.mod h1:J/6gKK9jxlEcS3zixgDgUAsiuZ7yrSoa/FX5e0EB2j4= 42 | github.com/json-iterator/go v1.1.6/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU= 43 | github.com/json-iterator/go v1.1.10/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= 44 | github.com/json-iterator/go v1.1.11/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= 45 | github.com/julienschmidt/httprouter v1.2.0/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7VTCxuUUipMqKk8s4w= 46 | github.com/julienschmidt/httprouter v1.3.0/go.mod h1:JR6WtHb+2LUe8TCKY3cZOxFyyO8IZAc4RVcycCCAKdM= 47 | github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= 48 | github.com/konsorten/go-windows-terminal-sequences v1.0.3/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= 49 | github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515/go.mod h1:+0opPa2QZZtGFBFZlji/RkVcI2GknAs/DXo4wKdlNEc= 50 | github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= 51 | github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= 52 | github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= 53 | github.com/matttproud/golang_protobuf_extensions v1.0.1 h1:4hp9jkHxhMHkqkrB3Ix0jegS5sx/RkqARlsWZ6pIwiU= 54 | github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0= 55 | github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= 56 | github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= 57 | github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0= 58 | github.com/modern-go/reflect2 v1.0.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0= 59 | github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U= 60 | github.com/mwitkow/go-conntrack v0.0.0-20190716064945-2f068394615f/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U= 61 | github.com/negbie/logp v0.0.0-20190313141056-04cebff7f846 h1:PAr5hcOgvc2m71W4SlbUsAbUnea5lNjB5/DfIHW9f8Q= 62 | github.com/negbie/logp v0.0.0-20190313141056-04cebff7f846/go.mod h1:xTKf9aKLuVL0r9wxWouR+/4ctK2ywm0rTPFY2klrnOg= 63 | github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= 64 | github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= 65 | github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= 66 | github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= 67 | github.com/prometheus/client_golang v0.9.1/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw= 68 | github.com/prometheus/client_golang v1.0.0/go.mod h1:db9x61etRT2tGnBNRi70OPL5FsnadC4Ky3P0J6CfImo= 69 | github.com/prometheus/client_golang v1.7.1/go.mod h1:PY5Wy2awLA44sXw4AOSfFBetzPP4j5+D6mVACh+pe2M= 70 | github.com/prometheus/client_golang v1.11.0 h1:HNkLOAEQMIDv/K+04rukrLx6ch7msSRwf3/SASFAGtQ= 71 | github.com/prometheus/client_golang v1.11.0/go.mod h1:Z6t4BnS23TR94PD6BsDNk8yVqroYurpAkEiz0P2BEV0= 72 | github.com/prometheus/client_model v0.0.0-20180712105110-5c3871d89910/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo= 73 | github.com/prometheus/client_model v0.0.0-20190129233127-fd36f4220a90/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= 74 | github.com/prometheus/client_model v0.2.0 h1:uq5h0d+GuxiXLJLNABMgp2qUWDPiLvgCzz2dUR+/W/M= 75 | github.com/prometheus/client_model v0.2.0/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= 76 | github.com/prometheus/common v0.4.1/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4= 77 | github.com/prometheus/common v0.10.0/go.mod h1:Tlit/dnDKsSWFlCLTWaA1cyBgKHSMdTB80sz/V91rCo= 78 | github.com/prometheus/common v0.26.0 h1:iMAkS2TDoNWnKM+Kopnx/8tnEStIfpYA0ur0xQzzhMQ= 79 | github.com/prometheus/common v0.26.0/go.mod h1:M7rCNAaPfAosfx8veZJCuw84e35h3Cfd9VFqTh1DIvc= 80 | github.com/prometheus/procfs v0.0.0-20181005140218-185b4288413d/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk= 81 | github.com/prometheus/procfs v0.0.2/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA= 82 | github.com/prometheus/procfs v0.1.3/go.mod h1:lV6e/gmhEcM9IjHGsFOCxxuZ+z1YqCvr4OA4YeYWdaU= 83 | github.com/prometheus/procfs v0.6.0 h1:mxy4L2jP6qMonqmq+aTtOx1ifVWUgG/TAmntgbh3xv4= 84 | github.com/prometheus/procfs v0.6.0/go.mod h1:cz+aTbrPOrUb4q7XlbU9ygM+/jj0fzG6c1xBZuNvfVA= 85 | github.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo= 86 | github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE= 87 | github.com/sirupsen/logrus v1.6.0/go.mod h1:7uNnSEd1DgxDLC74fIahvMZmmYsHGZGEOFrfsX/uA88= 88 | github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= 89 | github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= 90 | github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= 91 | github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= 92 | github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= 93 | golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= 94 | golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= 95 | golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= 96 | golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= 97 | golang.org/x/net v0.0.0-20181114220301-adae6a3d119a/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= 98 | golang.org/x/net v0.0.0-20190108225652-1e06a53dbb7e/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= 99 | golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= 100 | golang.org/x/net v0.0.0-20190613194153-d28f0bde5980/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= 101 | golang.org/x/net v0.0.0-20200625001655-4c5254603344/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= 102 | golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= 103 | golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= 104 | golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= 105 | golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= 106 | golang.org/x/sync v0.0.0-20201207232520-09787c993a3a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= 107 | golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= 108 | golang.org/x/sys v0.0.0-20181116152217-5ac8a444bdc5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= 109 | golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= 110 | golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 111 | golang.org/x/sys v0.0.0-20190422165155-953cdadca894/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 112 | golang.org/x/sys v0.0.0-20200106162015-b016eb3dc98e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 113 | golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 114 | golang.org/x/sys v0.0.0-20200615200032-f1bc736245b1/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 115 | golang.org/x/sys v0.0.0-20200625212154-ddb9806d33ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 116 | golang.org/x/sys v0.0.0-20210124154548-22da62e12c0c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 117 | golang.org/x/sys v0.0.0-20210603081109-ebe580a85c40 h1:JWgyZ1qgdTaF3N3oxC+MdTV7qvEEgHo3otj+HB5CM7Q= 118 | golang.org/x/sys v0.0.0-20210603081109-ebe580a85c40/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= 119 | golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= 120 | golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= 121 | golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= 122 | golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543 h1:E7g+9GITq07hpfrRu66IVDexMakfv52eLZ2CXBWiKr4= 123 | golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= 124 | google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= 125 | google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8= 126 | google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0= 127 | google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM= 128 | google.golang.org/protobuf v1.20.1-0.20200309200217-e05f789c0967/go.mod h1:A+miEFZTKqfCUM6K7xSMQL9OKL/b6hQv+e19PK+JZNE= 129 | google.golang.org/protobuf v1.21.0/go.mod h1:47Nbq4nVaFHyn7ilMalzfO3qCViNmqZ2kzikPIcrTAo= 130 | google.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= 131 | google.golang.org/protobuf v1.26.0-rc.1 h1:7QnIQpGRHE5RnLKnESfDoxm2dTapTZua5a0kS0A+VXQ= 132 | google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw= 133 | gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw= 134 | gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= 135 | gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= 136 | gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= 137 | gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= 138 | gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= 139 | gopkg.in/yaml.v2 v2.2.5/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= 140 | gopkg.in/yaml.v2 v2.3.0/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= 141 | -------------------------------------------------------------------------------- /hep.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "bytes" 5 | "encoding/binary" 6 | "fmt" 7 | "net" 8 | "strconv" 9 | "time" 10 | "log" 11 | ) 12 | 13 | 14 | // HEP chuncks 15 | const ( 16 | Version = 1 // Chunk 0x0001 IP protocol family (0x02=IPv4, 0x0a=IPv6) 17 | Protocol = 2 // Chunk 0x0002 IP protocol ID (0x06=TCP, 0x11=UDP) 18 | IP4SrcIP = 3 // Chunk 0x0003 IPv4 source address 19 | IP4DstIP = 4 // Chunk 0x0004 IPv4 destination address 20 | IP6SrcIP = 5 // Chunk 0x0005 IPv6 source address 21 | IP6DstIP = 6 // Chunk 0x0006 IPv6 destination address 22 | SrcPort = 7 // Chunk 0x0007 Protocol source port 23 | DstPort = 8 // Chunk 0x0008 Protocol destination port 24 | Tsec = 9 // Chunk 0x0009 Unix timestamp, seconds 25 | Tmsec = 10 // Chunk 0x000a Unix timestamp, microseconds 26 | ProtoType = 11 // Chunk 0x000b Protocol type (DNS, LOG, RTCP, SIP) 27 | NodeID = 12 // Chunk 0x000c Capture client ID 28 | NodePW = 14 // Chunk 0x000e Authentication key (plain text / TLS connection) 29 | Payload = 15 // Chunk 0x000f Captured packet payload 30 | CID = 17 // Chunk 0x0011 Correlation ID 31 | Vlan = 18 // Chunk 0x0012 VLAN 32 | NodeName = 19 // Chunk 0x0013 NodeName 33 | ) 34 | 35 | // HEP represents HEP packet 36 | type HEP struct { 37 | Version uint32 `protobuf:"varint,1,req,name=Version" json:"Version"` 38 | Protocol uint32 `protobuf:"varint,2,req,name=Protocol" json:"Protocol"` 39 | SrcIP string 40 | DstIP string 41 | SrcPort uint32 `protobuf:"varint,5,req,name=SrcPort" json:"SrcPort"` 42 | DstPort uint32 `protobuf:"varint,6,req,name=DstPort" json:"DstPort"` 43 | Tsec uint32 `protobuf:"varint,7,req,name=Tsec" json:"Tsec"` 44 | Tmsec uint32 `protobuf:"varint,8,req,name=Tmsec" json:"Tmsec"` 45 | ProtoType uint32 `protobuf:"varint,9,req,name=ProtoType" json:"ProtoType"` 46 | NodeID uint32 `protobuf:"varint,10,req,name=NodeID" json:"NodeID"` 47 | NodePW string `protobuf:"bytes,11,req,name=NodePW" json:"NodePW"` 48 | Payload string `protobuf:"bytes,12,req,name=Payload" json:"Payload"` 49 | CID string `protobuf:"bytes,13,req,name=CID" json:"CID"` 50 | Vlan uint32 `protobuf:"varint,14,req,name=Vlan" json:"Vlan"` 51 | ProtoString string 52 | Timestamp time.Time 53 | NodeName string 54 | TargetName string 55 | SID string 56 | } 57 | 58 | // DecodeHEP returns a parsed HEP message 59 | func DecodeHEP(packet []byte) (*HEP, error) { 60 | hep := &HEP{} 61 | err := hep.parse(packet) 62 | if err != nil { 63 | return nil, err 64 | } 65 | // log.Printf("HEP decoded ", hep) 66 | return hep, nil 67 | } 68 | 69 | func (h *HEP) parse(packet []byte) error { 70 | var err error 71 | if bytes.HasPrefix(packet, []byte{0x48, 0x45, 0x50, 0x33}) { 72 | err = h.parseHEP(packet) 73 | if err != nil { 74 | log.Println("Warning>", err) 75 | return err 76 | } 77 | } else { 78 | //err = h.Unmarshal(packet) 79 | //if err != nil { 80 | // log.Println("malformed packet with length %d which is neither hep nor protobuf encapsulated", len(packet)) 81 | return err 82 | // } 83 | } 84 | 85 | h.Timestamp = time.Unix(int64(h.Tsec), int64(h.Tmsec*1000)) 86 | if h.Tsec == 0 && h.Tmsec == 0 { 87 | log.Println("Debug> got null timestamp from nodeID %d", h.NodeID) 88 | h.Timestamp = time.Now() 89 | } 90 | 91 | 92 | 93 | if h.NodeName == "" { 94 | h.NodeName = strconv.FormatUint(uint64(h.NodeID), 10) 95 | } 96 | 97 | //log.Println("Debug> %+v\n\n", h) 98 | return nil 99 | } 100 | 101 | //-------------------------- 102 | 103 | func (h *HEP) parseHEP(packet []byte) error { 104 | length := binary.BigEndian.Uint16(packet[4:6]) 105 | // if int(length) != len(packet) { 106 | // return fmt.Errorf("HEP packet length is %d but should be %d", len(packet), length) 107 | // } 108 | currentByte := uint16(6) 109 | 110 | for currentByte < length { 111 | hepChunk := packet[currentByte:] 112 | if len(hepChunk) < 6 { 113 | return fmt.Errorf("HEP chunk must be >= 6 byte long but is %d", len(hepChunk)) 114 | } 115 | //chunkVendorId := binary.BigEndian.Uint16(hepChunk[:2]) 116 | chunkType := binary.BigEndian.Uint16(hepChunk[2:4]) 117 | chunkLength := binary.BigEndian.Uint16(hepChunk[4:6]) 118 | if len(hepChunk) < int(chunkLength) || int(chunkLength) < 6 { 119 | return fmt.Errorf("HEP chunk with %d byte < chunkLength %d or chunkLength < 6", len(hepChunk), chunkLength) 120 | } 121 | chunkBody := hepChunk[6:chunkLength] 122 | 123 | switch chunkType { 124 | case Version, Protocol, ProtoType: 125 | if len(chunkBody) != 1 { 126 | return fmt.Errorf("HEP chunkType %d should be 1 byte long but is %d", chunkType, len(chunkBody)) 127 | } 128 | case SrcPort, DstPort, Vlan: 129 | if len(chunkBody) != 2 { 130 | return fmt.Errorf("HEP chunkType %d should be 2 byte long but is %d", chunkType, len(chunkBody)) 131 | } 132 | case IP4SrcIP, IP4DstIP, Tsec, Tmsec, NodeID: 133 | if len(chunkBody) != 4 { 134 | return fmt.Errorf("HEP chunkType %d should be 4 byte long but is %d", chunkType, len(chunkBody)) 135 | } 136 | case IP6SrcIP, IP6DstIP: 137 | if len(chunkBody) != 16 { 138 | return fmt.Errorf("HEP chunkType %d should be 16 byte long but is %d", chunkType, len(chunkBody)) 139 | } 140 | } 141 | 142 | switch chunkType { 143 | case Version: 144 | h.Version = uint32(chunkBody[0]) 145 | case Protocol: 146 | h.Protocol = uint32(chunkBody[0]) 147 | case IP4SrcIP: 148 | h.SrcIP = net.IP(chunkBody).To4().String() 149 | case IP4DstIP: 150 | h.DstIP = net.IP(chunkBody).To4().String() 151 | case IP6SrcIP: 152 | h.SrcIP = net.IP(chunkBody).To16().String() 153 | case IP6DstIP: 154 | h.DstIP = net.IP(chunkBody).To16().String() 155 | case SrcPort: 156 | h.SrcPort = uint32(binary.BigEndian.Uint16(chunkBody)) 157 | case DstPort: 158 | h.DstPort = uint32(binary.BigEndian.Uint16(chunkBody)) 159 | case Tsec: 160 | h.Tsec = binary.BigEndian.Uint32(chunkBody) 161 | case Tmsec: 162 | h.Tmsec = binary.BigEndian.Uint32(chunkBody) 163 | case ProtoType: 164 | h.ProtoType = uint32(chunkBody[0]) 165 | switch h.ProtoType { 166 | case 1: 167 | h.ProtoString = "sip" 168 | case 5: 169 | h.ProtoString = "rtcp" 170 | case 34: 171 | h.ProtoString = "rtpagent" 172 | case 35: 173 | h.ProtoString = "rtcpxr" 174 | case 38: 175 | h.ProtoString = "horaclifix" 176 | case 53: 177 | h.ProtoString = "dns" 178 | case 100: 179 | h.ProtoString = "log" 180 | default: 181 | h.ProtoString = strconv.Itoa(int(h.ProtoType)) 182 | } 183 | case NodeID: 184 | h.NodeID = binary.BigEndian.Uint32(chunkBody) 185 | case NodePW: 186 | h.NodePW = string(chunkBody) 187 | case Payload: 188 | h.Payload = string(chunkBody) 189 | case CID: 190 | h.CID = string(chunkBody) 191 | case Vlan: 192 | h.Vlan = uint32(binary.BigEndian.Uint16(chunkBody)) 193 | case NodeName: 194 | h.NodeName = string(chunkBody) 195 | default: 196 | } 197 | currentByte += chunkLength 198 | } 199 | return nil 200 | } 201 | -------------------------------------------------------------------------------- /prometheus.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "net/http" 5 | "sync" 6 | 7 | "github.com/prometheus/client_golang/prometheus" 8 | "github.com/prometheus/client_golang/prometheus/promhttp" 9 | ) 10 | 11 | var connectedClients = prometheus.NewCounter( 12 | prometheus.CounterOpts{ 13 | Name: "hfp_client_connects_in", 14 | Help: "No of inbound client connects", 15 | }, 16 | ) 17 | 18 | var connectionStatus = prometheus.NewGauge( 19 | prometheus.GaugeOpts{ 20 | Name: "hfp_connection_status_out", 21 | Help: "Connection status OUT - 1 is connected, 0 is disconnected", 22 | }, 23 | ) 24 | 25 | var hepBytesInFile = prometheus.NewGauge( 26 | prometheus.GaugeOpts{ 27 | Name: "hfp_hep_bytes_in_file", 28 | Help: "No of HEP bytes in file", 29 | }, 30 | ) 31 | 32 | var hepFileFlushesSuccess = prometheus.NewCounter( 33 | prometheus.CounterOpts{ 34 | Name: "hfp_hep_file_flushes_success", 35 | Help: "No of times HEP pakets from file have been successfully sent over network to backend HEP server", 36 | }, 37 | ) 38 | 39 | var hepFileFlushesError = prometheus.NewCounter( 40 | prometheus.CounterOpts{ 41 | Name: "hfp_hep_file_flushes_error", 42 | Help: "No of times HEP pakets from file failed sending over network to backend HEP server", 43 | }, 44 | ) 45 | 46 | var clientLastMetricTimestamp = prometheus.NewGauge( 47 | prometheus.GaugeOpts{ 48 | Name: "hfp_client_in_last_metric_timestamp", 49 | Help: "Inbound client's last metric arrival", 50 | }, 51 | ) 52 | 53 | func startMetrics(wg *sync.WaitGroup) { 54 | wg.Add(1) 55 | prometheus.MustRegister(connectedClients) 56 | prometheus.MustRegister(connectionStatus) 57 | prometheus.MustRegister(hepBytesInFile) 58 | prometheus.MustRegister(hepFileFlushesSuccess) 59 | prometheus.MustRegister(hepFileFlushesError) 60 | prometheus.MustRegister(clientLastMetricTimestamp) 61 | 62 | http.Handle("/metrics", promhttp.Handler()) 63 | http.ListenAndServe(":"+*PrometheusPort, nil) 64 | wg.Done() 65 | 66 | } 67 | --------------------------------------------------------------------------------