191 lines
4.1 KiB
Go
191 lines
4.1 KiB
Go
// util.go v1.0
|
|
package sandbox
|
|
|
|
import (
|
|
"fmt"
|
|
"io"
|
|
"net"
|
|
"os"
|
|
"path/filepath"
|
|
"strconv"
|
|
"strings"
|
|
"sync"
|
|
"time"
|
|
|
|
"github.com/ssgo/log"
|
|
"github.com/ssgo/u"
|
|
)
|
|
|
|
var currentIds = map[string]bool{}
|
|
var idLock sync.Mutex
|
|
|
|
func getId() string {
|
|
id := u.Id8()
|
|
idLock.Lock()
|
|
defer idLock.Unlock()
|
|
for currentIds[id] {
|
|
id = u.Id8()
|
|
}
|
|
currentIds[id] = true
|
|
return id
|
|
}
|
|
|
|
func releaseId(id string) {
|
|
idLock.Lock()
|
|
defer idLock.Unlock()
|
|
delete(currentIds, id)
|
|
}
|
|
|
|
func (s *Sandbox) assertLog(title string, err error, extra ...any) {
|
|
if s.config.NoLog {
|
|
return
|
|
}
|
|
if err != nil {
|
|
extra = append(extra, "err", err.Error())
|
|
log.DefaultLogger.Info("[Sandbox] "+title+" failed", extra...)
|
|
} else {
|
|
log.DefaultLogger.Info("[Sandbox] "+title+" success", extra...)
|
|
}
|
|
}
|
|
|
|
func (s *Sandbox) log(title string, extra ...any) {
|
|
if s.config.NoLog {
|
|
return
|
|
}
|
|
log.DefaultLogger.Info("[Sandbox] "+title, extra...)
|
|
}
|
|
|
|
type Volumes struct {
|
|
volumes []Volume
|
|
indexByTarget map[string]int
|
|
}
|
|
|
|
func (v *Volumes) Add(vv ...Volume) {
|
|
for _, v1 := range vv {
|
|
if v1.Target == "" {
|
|
v1.Target = v1.Source
|
|
}
|
|
if v1.Target == "/proc" || v1.Target == "/sys" {
|
|
continue
|
|
}
|
|
if _, ok := v.indexByTarget[v1.Target]; ok {
|
|
v.volumes[v.indexByTarget[v1.Target]] = v1
|
|
} else {
|
|
v.volumes = append(v.volumes, v1)
|
|
v.indexByTarget[v1.Target] = len(v.volumes) - 1
|
|
}
|
|
}
|
|
}
|
|
|
|
func (v *Volumes) Get() []Volume {
|
|
return v.volumes
|
|
}
|
|
|
|
func NewVolumes() *Volumes {
|
|
return &Volumes{
|
|
volumes: []Volume{},
|
|
indexByTarget: map[string]int{},
|
|
}
|
|
}
|
|
|
|
func copyLog(workDir string, startTime int64) (int, error) {
|
|
logDate := time.Unix(startTime, 0).Format("20060102")
|
|
lastLogFile := filepath.Join(workDir, "stdout.log")
|
|
errorLogFile := filepath.Join(workDir, "stderr.log")
|
|
lastLogDest := filepath.Join(workDir, "logs", "stdout_"+logDate+".log")
|
|
errorLogDest := filepath.Join(workDir, "logs", "stderr_"+logDate+".log")
|
|
ok := 0
|
|
var err1 error
|
|
if !u.FileExists(lastLogDest) {
|
|
if err := u.CopyFile(lastLogFile, lastLogDest); err == nil {
|
|
ok++
|
|
} else {
|
|
err1 = err
|
|
}
|
|
} else {
|
|
if to, err := os.OpenFile(lastLogDest, os.O_CREATE|os.O_WRONLY|os.O_APPEND, 0644); err == nil {
|
|
defer to.Close()
|
|
if from, err := os.Open(lastLogFile); err == nil {
|
|
defer from.Close()
|
|
to.WriteString("\n")
|
|
if _, err := io.Copy(to, from); err == nil {
|
|
ok++
|
|
} else {
|
|
err1 = err
|
|
}
|
|
}
|
|
}
|
|
}
|
|
if !u.FileExists(errorLogDest) {
|
|
if err := u.CopyFile(errorLogFile, errorLogDest); err == nil {
|
|
ok++
|
|
} else {
|
|
err1 = err
|
|
}
|
|
} else {
|
|
if to, err := os.OpenFile(errorLogDest, os.O_CREATE|os.O_WRONLY|os.O_APPEND, 0644); err == nil {
|
|
defer to.Close()
|
|
if from, err := os.Open(errorLogFile); err == nil {
|
|
defer from.Close()
|
|
to.WriteString("\n")
|
|
if _, err := io.Copy(to, from); err == nil {
|
|
ok++
|
|
} else {
|
|
err1 = err
|
|
}
|
|
}
|
|
}
|
|
}
|
|
return ok, err1
|
|
}
|
|
|
|
type NetRule struct {
|
|
IP [16]byte // 统一 IPv6 长度
|
|
Mask int8 // -1 代表不校验掩码 (单IP), 0-128 代表掩码位
|
|
Port uint16 // 0 代表所有端口
|
|
IsV6 uint8 // 0: v4, 1: v6
|
|
}
|
|
|
|
func ParseNetRule(s string) (*NetRule, error) {
|
|
rule := &NetRule{Mask: -1}
|
|
hostStr := s
|
|
|
|
// 1. 处理端口 (从后往前找最后一个冒号,且排除 IPv6 的冒号)
|
|
if lastColon := strings.LastIndex(s, ":"); lastColon != -1 && !strings.HasSuffix(s, "]") {
|
|
// 检查冒号后面是否是纯数字(端口),如果不是,说明是 IPv6 地址的一部分
|
|
if port, err := strconv.Atoi(s[lastColon+1:]); err == nil {
|
|
rule.Port = uint16(port)
|
|
hostStr = s[:lastColon]
|
|
}
|
|
}
|
|
|
|
// 2. 处理 CIDR
|
|
ipStr := hostStr
|
|
if strings.Contains(hostStr, "/") {
|
|
_, ipNet, err := net.ParseCIDR(hostStr)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
ones, _ := ipNet.Mask.Size()
|
|
rule.Mask = int8(ones)
|
|
ipStr = ipNet.IP.String()
|
|
}
|
|
|
|
// 3. 处理 IP (去掉 IPv6 的方括号)
|
|
ipStr = strings.Trim(ipStr, "[]")
|
|
ip := net.ParseIP(ipStr)
|
|
if ip == nil {
|
|
return nil, fmt.Errorf("invalid ip: %s", ipStr)
|
|
}
|
|
|
|
if ip4 := ip.To4(); ip4 != nil {
|
|
copy(rule.IP[:4], ip4)
|
|
rule.IsV6 = 0
|
|
} else {
|
|
copy(rule.IP[:], ip.To16())
|
|
rule.IsV6 = 1
|
|
}
|
|
|
|
return rule, nil
|
|
}
|