mirror of
https://github.com/chainreactors/spray.git
synced 2025-05-08 03:26:56 +00:00
171 lines
3.6 KiB
Go
171 lines
3.6 KiB
Go
package internal
|
|
|
|
import (
|
|
"context"
|
|
"fmt"
|
|
"github.com/chainreactors/logs"
|
|
"github.com/chainreactors/spray/pkg"
|
|
"github.com/gosuri/uiprogress"
|
|
"io/ioutil"
|
|
"net/http"
|
|
"os"
|
|
"strings"
|
|
"sync"
|
|
)
|
|
|
|
var BlackStatus = []int{400, 404, 410}
|
|
var FuzzyStatus = []int{403, 500, 501, 502, 503}
|
|
|
|
type Runner struct {
|
|
URL string `short:"u" long:"url"`
|
|
URLFile string `short:"l" long:"list"`
|
|
URLList []string
|
|
WordFile string `short:"w" long:"work"`
|
|
Wordlist []string
|
|
Headers http.Header `long:"header"`
|
|
OutputFile string `short:"f"`
|
|
Offset int `long:"offset"`
|
|
Limit int `long:"limit"`
|
|
Threads int `short:"t" long:"thread" default:"20"`
|
|
PoolSize int `short:"p" long:"pool"`
|
|
Pools map[string]*Pool
|
|
Deadline int `long:"deadline" default:"600"` // todo 总的超时时间,适配云函数的deadline
|
|
Debug bool `long:"debug"`
|
|
Quiet bool `short:"q" long:"quiet"`
|
|
Mod string `short:"m" long:"mod" default:"path"`
|
|
OutputCh chan *baseline
|
|
Progress *uiprogress.Progress
|
|
}
|
|
|
|
func (r *Runner) Prepare() error {
|
|
r.Progress = uiprogress.New()
|
|
|
|
if r.Debug {
|
|
logs.Log.Level = logs.Debug
|
|
}
|
|
if !r.Quiet {
|
|
r.Progress.Start()
|
|
logs.Log.Writer = r.Progress.Bypass()
|
|
}
|
|
|
|
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 结束与并发控制
|
|
ctx := context.Background()
|
|
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,
|
|
Mod: pkg.ModMap[r.Mod],
|
|
DeadlineTime: r.Deadline,
|
|
}
|
|
pool, err := NewPool(ctx, config, r.OutputCh)
|
|
if err != nil {
|
|
logs.Log.Error(err.Error())
|
|
return
|
|
}
|
|
pool.bar = pkg.NewBar(u, len(r.Wordlist), r.Progress)
|
|
err = pool.Init()
|
|
if err != nil {
|
|
logs.Log.Error(err.Error())
|
|
return
|
|
}
|
|
r.Pools[u] = pool
|
|
// todo pool 总超时时间
|
|
pool.Run(ctx)
|
|
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())
|
|
} else {
|
|
logs.Log.Debug(bl.String())
|
|
}
|
|
}
|
|
}
|
|
}
|