package logic import ( "errors" "fmt" "os" "os/exec" "regexp" "sshd-tool/utils" "strconv" "strings" ) var ( // 需要的二进制文件 NeedExec = []string{"clamscan", "clamdscan"} ReInfectedFile = regexp.MustCompile(`(?i)^infected\s+files:\s+([0-9]+)\s*$`) ErrNoLineMatch = errors.New("no line match") ) func CheckExec() error { for _, v := range NeedExec { p := utils.FindCmd(v) if p == nil { return fmt.Errorf("%s not found", v) } } return nil } // todo 添加对tar、tgz等压缩文件的优化 // 压缩文件大小在500MB内,解压出流,让clamdscan扫描流 // ScanFile 扫描文件,返回是否有毒,如果有毒会删除 func ScanFile(f string) (bool, error) { finfo, err := os.Stat(f) if err != nil { return false, err } if finfo.IsDir() { return false, errors.New("error: dest is directory") } sf := fmt.Sprintf("%s.scanning", f) err = os.Rename(f, sf) if err != nil { return false, err } cmd := exec.Command("clamdscan", "-i", sf) output, err := cmd.CombinedOutput() if err != nil { if cmd.ProcessState.ExitCode() != 1 { return false, fmt.Errorf("run clamdscan failed, output: %s", string(output)) } } if cmd.ProcessState.ExitCode() == 1 { // 返回值不为0,有异常 str := string(output) num, err := parseClamOutput(str) if err != nil { return false, fmt.Errorf("parse clamdscan output error, output: %s", str) } if num > 0 { // todo:可以在这里插入日志 os.Remove(sf) } return true, nil } else { // 无异常,直接返回 err = os.Rename(sf, f) return false, err } } // parseClamOutput 解析clamdscan输出 func parseClamOutput(str string) (int, error) { for _, line := range strings.Split(strings.Trim(str, "\n"), "\n") { if ReInfectedFile.MatchString(line) { f := ReInfectedFile.FindStringSubmatch(line) return strconv.Atoi(f[1]) } } return 0, ErrNoLineMatch }