mirror of
https://gitee.com/samwaf/SamWaf.git
synced 2025-12-06 06:58:54 +08:00
427 lines
10 KiB
Go
427 lines
10 KiB
Go
package utils
|
||
|
||
import (
|
||
"SamWaf/common/zlog"
|
||
"SamWaf/global"
|
||
"SamWaf/model"
|
||
"crypto/tls"
|
||
"errors"
|
||
"fmt"
|
||
"github.com/lionsoul2014/ip2region/binding/golang/xdb"
|
||
"github.com/oschwald/geoip2-golang"
|
||
"io/ioutil"
|
||
"net"
|
||
"net/http"
|
||
"os"
|
||
"path/filepath"
|
||
"regexp"
|
||
"strconv"
|
||
"strings"
|
||
"time"
|
||
)
|
||
|
||
func GetExternalIp() string {
|
||
resp, err := http.Get("http://myexternalip.com/raw")
|
||
if err != nil {
|
||
return ""
|
||
}
|
||
defer resp.Body.Close()
|
||
//io.Copy(os.Stdout, resp.Body)
|
||
body, _ := ioutil.ReadAll(resp.Body)
|
||
clientIP := fmt.Sprintf("%s", string(body))
|
||
return clientIP
|
||
}
|
||
|
||
func GetCurrentDir() string {
|
||
// 检测环境变量是否存在
|
||
envVar := "SamWafIDE"
|
||
if _, exists := os.LookupEnv(envVar); exists {
|
||
return "."
|
||
}
|
||
|
||
exePath, err := os.Executable()
|
||
if err != nil {
|
||
fmt.Println(fmt.Sprintf("Failed to get executable path:%v", err))
|
||
return ""
|
||
}
|
||
exeDir := filepath.Dir(exePath)
|
||
return exeDir
|
||
}
|
||
|
||
// CheckDebugEnvInfo 检测是否打印debug信息
|
||
func CheckDebugEnvInfo() bool {
|
||
// 检测环境变量是否存在
|
||
envVar := "SamWafIDEDebugLog"
|
||
if _, exists := os.LookupEnv(envVar); exists {
|
||
return true
|
||
}
|
||
|
||
return false
|
||
}
|
||
func GetServerByHosts(hosts model.Hosts) string {
|
||
if hosts.Ssl == 1 {
|
||
return "https"
|
||
} else {
|
||
return "http"
|
||
}
|
||
}
|
||
|
||
/*
|
||
*
|
||
|
||
计算两个自然天数的间隔数
|
||
*/
|
||
func DiffNatureDays(t1, t2 int64) int {
|
||
var SecondsOfDay int64 = 86400
|
||
if t1 == t2 {
|
||
return -1
|
||
}
|
||
if t1 > t2 {
|
||
t1, t2 = t2, t1
|
||
}
|
||
|
||
diffDays := 0
|
||
secDiff := t2 - t1
|
||
if secDiff > SecondsOfDay {
|
||
tmpDays := int(secDiff / SecondsOfDay)
|
||
t1 += int64(tmpDays) * SecondsOfDay
|
||
diffDays += tmpDays
|
||
}
|
||
|
||
st := time.Unix(t1, 0)
|
||
et := time.Unix(t2, 0)
|
||
dateFormatTpl := "20060102"
|
||
if st.Format(dateFormatTpl) != et.Format(dateFormatTpl) {
|
||
diffDays += 1
|
||
}
|
||
|
||
return diffDays
|
||
}
|
||
|
||
/**字符串->时间对象*/
|
||
func Str2Time(formatTimeStr string) time.Time {
|
||
timeLayout := "20060102"
|
||
loc, _ := time.LoadLocation("Local")
|
||
theTime, _ := time.ParseInLocation(timeLayout, formatTimeStr, loc) //使用模板在对应时区转化为time.time类型
|
||
|
||
return theTime
|
||
}
|
||
|
||
/*
|
||
*
|
||
时间转int天
|
||
*/
|
||
func TimeToDayInt(t time.Time) int {
|
||
day, _ := strconv.Atoi(t.Format("20060102"))
|
||
return day
|
||
}
|
||
func GetPublicIP() string {
|
||
conn, err := net.Dial("udp", "8.8.8.8:80")
|
||
if err != nil {
|
||
return "localhost"
|
||
// log.Fatal(err)
|
||
}
|
||
defer conn.Close()
|
||
localAddr := conn.LocalAddr().String()
|
||
idx := strings.LastIndex(localAddr, ":")
|
||
return localAddr[0:idx]
|
||
}
|
||
|
||
func GetCountry(ip string) []string {
|
||
if IsValidIPv6(ip) {
|
||
a := "ipv6|ipv6|ipv6|ipv6|ipv6"
|
||
if global.GCACHE_IPV6_SEARCHER == nil {
|
||
db, err := geoip2.FromBytes(global.GCACHE_IP_V6_COUNTRY_CBUFF)
|
||
if err != nil {
|
||
zlog.Error("Failed to open GeoLite2-Country.mmdb:", err)
|
||
return strings.Split(a, "|")
|
||
}
|
||
global.GCACHE_IPV6_SEARCHER = db
|
||
}
|
||
ipv6 := net.ParseIP(ip)
|
||
record, err := global.GCACHE_IPV6_SEARCHER.Country(ipv6)
|
||
if err != nil {
|
||
zlog.Error("Failed to Search GeoLite2-Country.mmdb:", err)
|
||
return strings.Split(a, "|")
|
||
}
|
||
if record.Country.Names == nil {
|
||
a = "内网" + "||||"
|
||
} else {
|
||
a = record.Country.Names["zh-CN"] + "||||"
|
||
}
|
||
|
||
return strings.Split(a, "|")
|
||
}
|
||
|
||
//IPV4得查询逻辑
|
||
if global.GCACHE_IPV4_SEARCHER == nil {
|
||
// 2、用全局的 cBuff 创建完全基于内存的查询对象。
|
||
searcher, err := xdb.NewWithBuffer(global.GCACHE_IP_CBUFF)
|
||
if err != nil {
|
||
fmt.Printf("failed to create searcher with content: %s\n", err)
|
||
|
||
}
|
||
global.GCACHE_IPV4_SEARCHER = searcher
|
||
}
|
||
|
||
// do the search
|
||
var tStart = time.Now()
|
||
|
||
// 备注:并发使用,每个 goroutine 需要创建一个独立的 searcher 对象。
|
||
region, err := global.GCACHE_IPV4_SEARCHER.SearchByStr(ip)
|
||
if err != nil {
|
||
fmt.Printf("failed to SearchIP(%s): %s\n", ip, err)
|
||
return []string{"无", "无"}
|
||
}
|
||
|
||
zlog.Debug("{region: %s, took: %s}\n", region, time.Since(tStart))
|
||
regions := strings.Split(region, "|")
|
||
//如果是内网IP情况下显示内网的内容
|
||
if regions[4] == "内网IP" {
|
||
regions[0] = "内网"
|
||
regions[1] = "内网"
|
||
regions[2] = "内网"
|
||
}
|
||
return regions
|
||
}
|
||
|
||
// CloseIPDatabase 关闭IP数据库
|
||
func CloseIPDatabase() {
|
||
if global.GCACHE_IPV4_SEARCHER != nil {
|
||
global.GCACHE_IPV4_SEARCHER.Close()
|
||
}
|
||
if global.GCACHE_IPV6_SEARCHER != nil {
|
||
err := global.GCACHE_IPV6_SEARCHER.Close()
|
||
if err != nil {
|
||
return
|
||
}
|
||
}
|
||
}
|
||
|
||
/*
|
||
*
|
||
通过ip段(CIDR)来查询是否在很多CIDR段内
|
||
*/
|
||
func CheckIPInRanges(ip string, ipRanges []string) bool {
|
||
parsedIP := net.ParseIP(ip)
|
||
if parsedIP == nil {
|
||
return false // 如果IP地址解析失败,则返回false
|
||
}
|
||
|
||
for _, ipRange := range ipRanges {
|
||
_, ipNet, err := net.ParseCIDR(ipRange)
|
||
if err != nil {
|
||
return false // 如果CIDR格式解析失败,则返回false
|
||
}
|
||
|
||
if ipNet.Contains(parsedIP) {
|
||
return true // 如果IP地址在指定的CIDR范围内,则返回true
|
||
}
|
||
}
|
||
|
||
return false // 如果IP地址不在任何一个CIDR范围内,则返回false
|
||
}
|
||
|
||
/*
|
||
*
|
||
通过ip段(CIDR)来查询是否在这个段内
|
||
*/
|
||
func CheckIPInCIDR(ip string, ipRange string) bool {
|
||
|
||
parts := strings.Split(ipRange, "/")
|
||
if len(parts) != 2 {
|
||
// 如果是普通ip
|
||
if ip == ipRange {
|
||
return true
|
||
} else {
|
||
return false
|
||
}
|
||
} else {
|
||
//如果是网段IP
|
||
parsedIP := net.ParseIP(ip)
|
||
if parsedIP == nil {
|
||
return false // 如果IP地址解析失败,则返回false
|
||
}
|
||
_, ipNet, err := net.ParseCIDR(ipRange)
|
||
if err != nil {
|
||
return false // 如果CIDR格式解析失败,则返回false
|
||
}
|
||
if ipNet.Contains(parsedIP) {
|
||
return true // 如果IP地址在指定的CIDR范围内,则返回true
|
||
}
|
||
return false // 如果IP地址不在任何一个CIDR范围内,则返回false
|
||
}
|
||
}
|
||
|
||
// IsValidIPOrNetwork 检查给定的字符串是否为有效的 IP 地址或 IP 段(CIDR)
|
||
func IsValidIPOrNetwork(input string) (bool, string) {
|
||
// 尝试解析为 IP 地址
|
||
if ip := net.ParseIP(input); ip != nil {
|
||
return true, "valid IP address"
|
||
}
|
||
|
||
// 尝试解析为 IP 段(CIDR)
|
||
_, _, err := net.ParseCIDR(input)
|
||
if err == nil {
|
||
return true, "valid IP network (CIDR)"
|
||
}
|
||
|
||
return false, "not a valid IP or IP network (CIDR)"
|
||
}
|
||
|
||
// 检查 JSON 字符串是否为有效的 UTF-8 编码
|
||
func CheckJSONValidity(jsonStr []byte) bool {
|
||
for i := 0; i < len(jsonStr); {
|
||
r, size := rune(jsonStr[i]), 1
|
||
switch {
|
||
case r >= 0xF0:
|
||
r, size = rune(jsonStr[i+3]), 4
|
||
case r >= 0xE0:
|
||
r, size = rune(jsonStr[i+2]), 3
|
||
case r >= 0xC0:
|
||
r, size = rune(jsonStr[i+1]), 2
|
||
default:
|
||
}
|
||
if r > 0xFFFF {
|
||
return false
|
||
}
|
||
i += size
|
||
}
|
||
return true
|
||
}
|
||
|
||
/*
|
||
*
|
||
删除老旧数据
|
||
*/
|
||
func DeleteOldFiles(dir string, duration time.Duration) error {
|
||
// 遍历目录下的文件
|
||
return filepath.Walk(dir, func(path string, info os.FileInfo, err error) error {
|
||
if err != nil {
|
||
return err
|
||
}
|
||
|
||
// 如果是文件并且文件创建时间超过指定的时间
|
||
if !info.IsDir() {
|
||
// 获取文件的修改时间(可以近似认为是创建时间)
|
||
fileModTime := info.ModTime()
|
||
|
||
// 判断文件是否超过指定的时间(比如30分钟)
|
||
if time.Since(fileModTime) > duration {
|
||
// 删除文件
|
||
zlog.Info("删除陈旧文件:", path)
|
||
err := os.Remove(path)
|
||
if err != nil {
|
||
return err
|
||
}
|
||
}
|
||
}
|
||
return nil
|
||
})
|
||
}
|
||
|
||
// 检测是否属于ipv4
|
||
func IsValidIPv4(ip string) bool {
|
||
parsedIP := net.ParseIP(ip)
|
||
return parsedIP != nil && parsedIP.To4() != nil
|
||
}
|
||
|
||
// 验证IPv6地址是否有效
|
||
func IsValidIPv6(ip string) bool {
|
||
parsedIP := net.ParseIP(ip)
|
||
// 如果解析出的 IP 类型是 IPv6 且不是 IPv4 映射地址(IPv4-mapped IPv6 addresses)
|
||
return parsedIP != nil && strings.Contains(ip, ":") && parsedIP.To4() == nil
|
||
}
|
||
|
||
// 获取纯域名
|
||
|
||
func GetPureDomain(host string) string {
|
||
// 检查是否包含端口号
|
||
if strings.Contains(host, ":") {
|
||
host, _, _ = net.SplitHostPort(host)
|
||
}
|
||
return host
|
||
}
|
||
|
||
// UpdateFileIsHasNewInfo 检查文件内容并更新
|
||
func UpdateFileIsHasNewInfo(filePath, newContent string) error {
|
||
// 检查文件是否存在
|
||
if _, err := os.Stat(filePath); os.IsNotExist(err) {
|
||
// 文件不存在,写入新内容
|
||
if err := ioutil.WriteFile(filePath, []byte(newContent), 0644); err != nil {
|
||
return fmt.Errorf("failed to write file %s: %v", filePath, err)
|
||
}
|
||
} else {
|
||
// 文件存在,读取当前内容
|
||
currentContent, err := ioutil.ReadFile(filePath)
|
||
if err != nil {
|
||
return fmt.Errorf("failed to read file %s: %v", filePath, err)
|
||
}
|
||
|
||
// 比较内容是否一致
|
||
if string(currentContent) != newContent {
|
||
// 内容不一致,更新文件
|
||
if err := ioutil.WriteFile(filePath, []byte(newContent), 0644); err != nil {
|
||
return fmt.Errorf("failed to update file %s: %v", filePath, err)
|
||
}
|
||
}
|
||
}
|
||
return nil
|
||
}
|
||
|
||
// IsValidChallengeFile 检查challenge文件是否合法
|
||
func IsValidChallengeFile(challengeFile string) bool {
|
||
// 定义一个正则表达式:允许字母、数字、连字符、下划线,且长度在 1 到 255 之间
|
||
// 这里假设合法的文件名不含有特殊字符和空格
|
||
re := regexp.MustCompile(`^[a-zA-Z0-9_-]{1,255}$`)
|
||
|
||
// 判断文件名是否符合正则表达式
|
||
return re.MatchString(challengeFile)
|
||
}
|
||
|
||
// CheckPathAndCreate 检测路径如果不存在则创建
|
||
func CheckPathAndCreate(path string) error {
|
||
// 检测目录是否存在
|
||
_, err := os.Stat(path)
|
||
if os.IsNotExist(err) {
|
||
// 目录不存在,创建目录
|
||
err := os.MkdirAll(path, os.ModePerm)
|
||
if err != nil {
|
||
return errors.New("创建目录失败" + err.Error())
|
||
}
|
||
return nil
|
||
} else if err != nil {
|
||
// 发生其他错误
|
||
return errors.New("检查目录失败" + err.Error())
|
||
} else {
|
||
return nil
|
||
}
|
||
}
|
||
|
||
// CheckSSLCertificateExpiry 检查指定主机的 SSL 证书到期情况
|
||
// host: 需要检查的主机地址,格式为 "ssl.samwaf.com:443"
|
||
func CheckSSLCertificateExpiry(host string) (time.Time, error) {
|
||
conn, err := tls.Dial("tcp", host, nil)
|
||
if err != nil {
|
||
return time.Time{}, fmt.Errorf("无法连接到主机 %s: %v", host, err)
|
||
}
|
||
defer conn.Close()
|
||
|
||
// 获取 SSL 证书的到期时间
|
||
cert := conn.ConnectionState().PeerCertificates[0]
|
||
expiryDate := cert.NotAfter
|
||
// 返回到期时间
|
||
return expiryDate, nil
|
||
}
|
||
|
||
// GetSourceClientIP 获取原始IP
|
||
func GetSourceClientIP(ipAndPort string) string {
|
||
ip, _, err := net.SplitHostPort(ipAndPort)
|
||
if err != nil {
|
||
return ""
|
||
} else {
|
||
return ip
|
||
}
|
||
|
||
}
|