spray/internal/runner.go

157 lines
2.9 KiB
Go

package internal
import (
"fmt"
"github.com/chainreactors/logs"
"github.com/chainreactors/spray/pkg"
"io/ioutil"
"net/http"
"os"
"strings"
"sync"
)
var BlackStatus = []int{404, 410}
var FuzzyStatus = []int{403, 500, 501, 503}
type Runner struct {
URL string
URLFile string
URLList []string
WordFile string
Wordlist []string
Headers http.Header
OutputFile string
Offset int
Limit int
Threads int
PoolSize int
Pools map[string]*Pool
Deadline int // todo 总的超时时间,适配云函数的deadline
Debug bool
Mod string
OutputCh chan *baseline
}
func (r *Runner) Prepare() error {
if r.Debug {
logs.Log.Level = logs.Debug
}
var file *os.File
var err error
urlfrom := r.URLFile
if r.URL != "" {
r.URLList = append(r.URLList, r.URL)
urlfrom = "cmd"
} else if r.URLFile != "" {
file, err = os.Open(r.URLFile)
if err != nil {
return err
}
} else if pkg.HasStdin() {
file = os.Stdin
urlfrom = "stdin"
}
if file != nil {
content, err := ioutil.ReadAll(file)
if err != nil {
return err
}
r.URLList = strings.Split(string(content), "\n")
}
// todo url formatter
for i, u := range r.URLList {
r.URLList[i] = strings.TrimSpace(u)
}
logs.Log.Importantf("load %d urls from %s", len(r.URLList), urlfrom)
if r.WordFile != "" {
content, err := ioutil.ReadFile(r.WordFile)
if err != nil {
return err
}
r.Wordlist = strings.Split(string(content), "\n")
} else {
return fmt.Errorf("not special wordlist")
}
if r.Wordlist != nil && len(r.Wordlist) > 0 {
// todo suffix/prefix/trim/generator
for i, word := range r.Wordlist {
r.Wordlist[i] = strings.TrimSpace(word)
}
logs.Log.Importantf("load %d word from %s", len(r.Wordlist), r.WordFile)
} else {
return fmt.Errorf("no wordlist")
}
CheckStatusCode = func(status int) bool {
for _, black := range BlackStatus {
if black == status {
return false
}
}
return true
}
r.OutputCh = make(chan *baseline, 100)
r.Pools = make(map[string]*Pool)
go r.Outputting()
return nil
}
func (r *Runner) Run() {
// todo pool 结束与并发控制
var wg sync.WaitGroup
for _, u := range r.URLList {
wg.Add(1)
u := u
go func() {
config := &pkg.Config{
BaseURL: u,
Wordlist: r.Wordlist,
Thread: r.Threads,
Timeout: 2,
Headers: r.Headers,
}
pool, err := NewPool(config, r.OutputCh)
if err != nil {
logs.Log.Error(err.Error())
return
}
err = pool.Init()
if err != nil {
logs.Log.Error(err.Error())
return
}
r.Pools[u] = pool
// todo pool 总超时时间
pool.Run()
wg.Done()
}()
}
wg.Wait()
for {
if len(r.OutputCh) == 0 {
close(r.OutputCh)
return
}
}
}
func (r *Runner) Outputting() {
for {
select {
case bl := <-r.OutputCh:
if bl.IsValid {
logs.Log.Console(bl.String() + "\n")
} else {
logs.Log.Debug(bl.String())
}
}
}
}