rmmagent/agent/utils.go

168 lines
3.5 KiB
Go

package agent
import (
"fmt"
"math"
"math/rand"
"net"
"os"
"path/filepath"
"runtime"
"strings"
"time"
ps "github.com/elastic/go-sysinfo"
"github.com/go-resty/resty/v2"
"github.com/shirou/gopsutil/process"
)
var client = resty.New()
// APIRequest struct
type APIRequest struct {
URL string
Method string
Payload interface{}
Headers map[string]string
Timeout time.Duration
LocalCert string
Debug bool
}
// MakeRequest creates an api request to the RMM
func (r *APIRequest) MakeRequest() (*resty.Response, error) {
client.SetCloseConnection(true)
client.SetHeaders(r.Headers)
client.SetTimeout(r.Timeout * time.Second)
client.SetDebug(r.Debug)
if len(r.LocalCert) > 0 {
client.SetRootCertificate(r.LocalCert)
}
var resp *resty.Response
var err error
switch r.Method {
case "GET":
resp, err = client.R().Get(r.URL)
case "POST":
resp, err = client.R().SetBody(r.Payload).Post(r.URL)
case "PATCH":
resp, err = client.R().SetBody(r.Payload).Patch(r.URL)
case "PUT":
resp, err = client.R().SetBody(r.Payload).Put(r.URL)
}
if err != nil {
return &resty.Response{}, err
}
return resp, nil
}
// PublicIP returns the agent's public ip
// Tries 2 times before giving up
func PublicIP() string {
client.SetCloseConnection(true)
client.SetTimeout(7 * time.Second)
urls := []string{"https://ifconfig.co/ip", "https://icanhazip.com"}
for _, url := range urls {
r, err := client.R().Get(url)
if err != nil {
continue
}
ip := StripAll(r.String())
if !IsValidIP(ip) {
continue
}
return ip
}
return "error"
}
// GenerateAgentID creates and returns a unique agent id
func GenerateAgentID(hostname string) string {
rand.Seed(time.Now().UnixNano())
letters := []rune("abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ")
b := make([]rune, 35)
for i := range b {
b[i] = letters[rand.Intn(len(letters))]
}
return string(b) + "-" + hostname
}
// ShowVersionInfo prints basic debugging info
func ShowVersionInfo(ver string) {
fmt.Println("Tactical RMM Agent:", ver)
fmt.Println("Arch:", runtime.GOARCH)
fmt.Println("Go version:", runtime.Version())
if runtime.GOOS == "windows" {
fmt.Println("Program Directory:", filepath.Join(os.Getenv("ProgramFiles"), "TacticalAgent"))
}
}
// FileExists checks whether a file exists
func FileExists(path string) bool {
if _, err := os.Stat(path); err != nil {
if os.IsNotExist(err) {
return false
}
}
return true
}
// TotalRAM returns total RAM in GB
func TotalRAM() float64 {
host, _ := ps.Host()
mem, _ := host.Memory()
return math.Ceil(float64(mem.Total) / 1073741824.0)
}
// BootTime returns system boot time as a unix timestamp
func BootTime() int64 {
host, _ := ps.Host()
info := host.Info()
return info.BootTime.Unix()
}
// IsValidIP checks for a valid ipv4 or ipv6
func IsValidIP(ip string) bool {
return net.ParseIP(ip) != nil
}
// StripAll strips all whitespace and newline chars
func StripAll(s string) string {
s = strings.TrimSpace(s)
s = strings.Trim(s, "\n")
s = strings.Trim(s, "\r")
return s
}
// KillProc kills a process and its children
func KillProc(pid int32) error {
p, err := process.NewProcess(pid)
if err != nil {
return err
}
children, err := p.Children()
if err == nil {
for _, child := range children {
if err := child.Kill(); err != nil {
continue
}
}
}
if err := p.Kill(); err != nil {
return err
}
return nil
}
// DjangoStringResp removes double quotes from django rest api resp
func DjangoStringResp(resp string) string {
return strings.Trim(resp, `"`)
}