mirror of
https://github.com/chainreactors/spray.git
synced 2025-05-06 18:51:22 +00:00
235 lines
4.8 KiB
Go
235 lines
4.8 KiB
Go
package internal
|
|
|
|
import (
|
|
"bytes"
|
|
"github.com/chainreactors/spray/pkg"
|
|
"github.com/chainreactors/words/mask"
|
|
"github.com/chainreactors/words/rule"
|
|
"io/ioutil"
|
|
"net/url"
|
|
"path"
|
|
"strconv"
|
|
"strings"
|
|
)
|
|
|
|
func parseExtension(s string) string {
|
|
if i := strings.Index(s, "."); i != -1 {
|
|
return s[i+1:]
|
|
}
|
|
return ""
|
|
}
|
|
|
|
func parseStatus(preset []int, changed string) []int {
|
|
if changed == "" {
|
|
return preset
|
|
}
|
|
if strings.HasPrefix(changed, "+") {
|
|
for _, s := range strings.Split(changed[1:], ",") {
|
|
if t, err := strconv.Atoi(s); err != nil {
|
|
continue
|
|
} else {
|
|
preset = append(preset, t)
|
|
}
|
|
}
|
|
} else if strings.HasPrefix(changed, "!") {
|
|
for _, s := range strings.Split(changed[1:], ",") {
|
|
for i, status := range preset {
|
|
if t, err := strconv.Atoi(s); err != nil {
|
|
break
|
|
} else if t == status {
|
|
preset = append(preset[:i], preset[i+1:]...)
|
|
break
|
|
}
|
|
}
|
|
}
|
|
} else {
|
|
preset = []int{}
|
|
for _, s := range strings.Split(changed, ",") {
|
|
if t, err := strconv.Atoi(s); err != nil {
|
|
continue
|
|
} else {
|
|
preset = append(preset, t)
|
|
}
|
|
}
|
|
}
|
|
return preset
|
|
}
|
|
|
|
func loadFileToSlice(filename string) ([]string, error) {
|
|
var ss []string
|
|
content, err := ioutil.ReadFile(filename)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
ss = strings.Split(strings.TrimSpace(string(content)), "\n")
|
|
|
|
// 统一windows与linux的回车换行差异
|
|
for i, word := range ss {
|
|
ss[i] = strings.TrimSpace(word)
|
|
}
|
|
|
|
return ss, nil
|
|
}
|
|
|
|
func loadFileAndCombine(filename []string) (string, error) {
|
|
var bs bytes.Buffer
|
|
for _, f := range filename {
|
|
if data, ok := pkg.Rules[f]; ok {
|
|
bs.WriteString(strings.TrimSpace(data))
|
|
bs.WriteString("\n")
|
|
} else {
|
|
content, err := ioutil.ReadFile(f)
|
|
if err != nil {
|
|
return "", err
|
|
}
|
|
bs.Write(bytes.TrimSpace(content))
|
|
bs.WriteString("\n")
|
|
}
|
|
}
|
|
return bs.String(), nil
|
|
}
|
|
|
|
func loadFileWithCache(filename string) ([]string, error) {
|
|
if dict, ok := dictCache[filename]; ok {
|
|
return dict, nil
|
|
}
|
|
dict, err := loadFileToSlice(filename)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
dictCache[filename] = dict
|
|
return dict, nil
|
|
}
|
|
|
|
func loadDictionaries(filenames []string) ([][]string, error) {
|
|
dicts := make([][]string, len(filenames))
|
|
for i, name := range filenames {
|
|
dict, err := loadFileWithCache(name)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
dicts[i] = dict
|
|
}
|
|
return dicts, nil
|
|
}
|
|
|
|
func loadWordlist(word string, dictNames []string) ([]string, error) {
|
|
if wl, ok := wordlistCache[word+strings.Join(dictNames, ",")]; ok {
|
|
return wl, nil
|
|
}
|
|
dicts, err := loadDictionaries(dictNames)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
wl, err := mask.Run(word, dicts, nil)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
wordlistCache[word] = wl
|
|
return wl, nil
|
|
}
|
|
|
|
func loadRuleWithFiles(ruleFiles []string, filter string) ([]rule.Expression, error) {
|
|
if rules, ok := ruleCache[strings.Join(ruleFiles, ",")]; ok {
|
|
return rules, nil
|
|
}
|
|
var rules bytes.Buffer
|
|
for _, filename := range ruleFiles {
|
|
content, err := ioutil.ReadFile(filename)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
rules.Write(content)
|
|
rules.WriteString("\n")
|
|
}
|
|
return rule.Compile(rules.String(), filter).Expressions, nil
|
|
}
|
|
|
|
func relaPath(base, u string) string {
|
|
// 拼接相对目录, 不使用path.join的原因是, 如果存在"////"这样的情况, 可能真的是有意义的路由, 不能随意去掉.
|
|
// "" /a /a
|
|
// "" a /a
|
|
// / "" /
|
|
// /a/ b /a/b
|
|
// /a/ /b /a/b
|
|
// /a b /b
|
|
// /a /b /b
|
|
|
|
if u == "" {
|
|
return base
|
|
}
|
|
|
|
pathSlash := strings.HasPrefix(u, "/")
|
|
if base == "" {
|
|
if pathSlash {
|
|
return u[1:]
|
|
} else {
|
|
return "/" + u
|
|
}
|
|
} else if strings.HasSuffix(base, "/") {
|
|
if pathSlash {
|
|
return base + u[1:]
|
|
} else {
|
|
return base + u
|
|
}
|
|
} else {
|
|
if pathSlash {
|
|
return Dir(base) + u[1:]
|
|
} else {
|
|
return Dir(base) + u
|
|
}
|
|
}
|
|
}
|
|
|
|
func Dir(u string) string {
|
|
// 安全的获取目录, 不会额外处理多个"//", 并非用来获取上级目录
|
|
// /a /
|
|
// /a/ /a/
|
|
// a/ a/
|
|
// aaa /
|
|
|
|
if strings.HasSuffix(u, "/") {
|
|
return u
|
|
} else if i := strings.LastIndex(u, "/"); i == -1 {
|
|
return "/"
|
|
} else {
|
|
return u[:i+1]
|
|
}
|
|
}
|
|
|
|
func FormatURL(base, u string) string {
|
|
if strings.HasPrefix(u, "http") {
|
|
parsed, err := url.Parse(u)
|
|
if err != nil {
|
|
return ""
|
|
}
|
|
if len(parsed.Path) <= 1 {
|
|
return ""
|
|
}
|
|
return parsed.Path
|
|
} else if strings.HasPrefix(u, "//") {
|
|
parsed, err := url.Parse(u)
|
|
if err != nil {
|
|
return ""
|
|
}
|
|
if len(parsed.Path) <= 1 {
|
|
// 跳过"/"与空目录
|
|
return ""
|
|
}
|
|
return parsed.Path
|
|
} else if strings.HasPrefix(u, "/") {
|
|
// 绝对目录拼接
|
|
// 不需要进行处理, 用来跳过下面的判断
|
|
return u
|
|
} else if strings.HasPrefix(u, "./") {
|
|
// "./"相对目录拼接
|
|
return relaPath(base, u[2:])
|
|
} else if strings.HasPrefix(u, "../") {
|
|
return path.Join(Dir(base), u)
|
|
} else {
|
|
// 相对目录拼接
|
|
return relaPath(base, u)
|
|
}
|
|
}
|