1
1
Fork 0
mirror of https://github.com/OJ/gobuster.git synced 2024-06-08 08:36:01 +02:00
gobuster/libgobuster/libgobuster.go
Christian Mehlmauer 9b6813f4c4
reduce code reuse
2019-04-29 09:29:57 +02:00

185 lines
4.2 KiB
Go

package libgobuster
import (
"bufio"
"context"
"fmt"
"os"
"strings"
"sync"
)
// SetupFunc is the "setup" function prototype for implementations
type SetupFunc func(*Gobuster) error
// ProcessFunc is the "process" function prototype for implementations
type ProcessFunc func(*Gobuster, string) ([]Result, error)
// ResultToStringFunc is the "to string" function prototype for implementations
type ResultToStringFunc func(*Gobuster, *Result) (*string, error)
// Gobuster is the main object when creating a new run
type Gobuster struct {
Opts *Options
context context.Context
requestsExpected int
requestsIssued int
mu *sync.RWMutex
plugin GobusterPlugin
resultChan chan Result
errorChan chan error
}
// NewGobuster returns a new Gobuster object
func NewGobuster(c context.Context, opts *Options, plugin GobusterPlugin) (*Gobuster, error) {
var g Gobuster
g.Opts = opts
g.plugin = plugin
g.mu = new(sync.RWMutex)
g.context = c
g.resultChan = make(chan Result)
g.errorChan = make(chan error)
return &g, nil
}
// Results returns a channel of Results
func (g *Gobuster) Results() <-chan Result {
return g.resultChan
}
// Errors returns a channel of errors
func (g *Gobuster) Errors() <-chan error {
return g.errorChan
}
func (g *Gobuster) incrementRequests() {
g.mu.Lock()
g.requestsIssued++
g.mu.Unlock()
}
// PrintProgress outputs the current wordlist progress to stderr
func (g *Gobuster) PrintProgress() {
if !g.Opts.Quiet && !g.Opts.NoProgress {
g.mu.RLock()
if g.Opts.Wordlist == "-" {
fmt.Fprintf(os.Stderr, "\rProgress: %d", g.requestsIssued)
// only print status if we already read in the wordlist
} else if g.requestsExpected > 0 {
fmt.Fprintf(os.Stderr, "\rProgress: %d / %d (%3.2f%%)", g.requestsIssued, g.requestsExpected, float32(g.requestsIssued)*100.0/float32(g.requestsExpected))
}
g.mu.RUnlock()
}
}
// ClearProgress removes the last status line from stderr
func (g *Gobuster) ClearProgress() {
fmt.Fprint(os.Stderr, resetTerminal())
}
func (g *Gobuster) worker(wordChan <-chan string, wg *sync.WaitGroup) {
defer wg.Done()
for {
select {
case <-g.context.Done():
return
case word, ok := <-wordChan:
// worker finished
if !ok {
return
}
g.incrementRequests()
wordCleaned := strings.TrimSpace(word)
// Skip "comment" (starts with #), as well as empty lines
if strings.HasPrefix(wordCleaned, "#") || len(wordCleaned) == 0 {
break
}
// Mode-specific processing
res, err := g.plugin.Run(wordCleaned)
if err != nil {
// do not exit and continue
g.errorChan <- err
continue
} else {
for _, r := range res {
g.resultChan <- r
}
}
}
}
}
func (g *Gobuster) getWordlist() (*bufio.Scanner, error) {
if g.Opts.Wordlist == "-" {
// Read directly from stdin
return bufio.NewScanner(os.Stdin), nil
}
// Pull content from the wordlist
wordlist, err := os.Open(g.Opts.Wordlist)
if err != nil {
return nil, fmt.Errorf("failed to open wordlist: %v", err)
}
lines, err := lineCounter(wordlist)
if err != nil {
return nil, fmt.Errorf("failed to get number of lines: %v", err)
}
g.requestsExpected = lines
g.requestsIssued = 0
// rewind wordlist
_, err = wordlist.Seek(0, 0)
if err != nil {
return nil, fmt.Errorf("failed to rewind wordlist: %v", err)
}
return bufio.NewScanner(wordlist), nil
}
// Start the busting of the website with the given
// set of settings from the command line.
func (g *Gobuster) Start() error {
defer close(g.resultChan)
defer close(g.errorChan)
if err := g.plugin.PreRun(); err != nil {
return err
}
var workerGroup sync.WaitGroup
workerGroup.Add(g.Opts.Threads)
wordChan := make(chan string, g.Opts.Threads)
// Create goroutines for each of the number of threads
// specified.
for i := 0; i < g.Opts.Threads; i++ {
go g.worker(wordChan, &workerGroup)
}
scanner, err := g.getWordlist()
if err != nil {
return err
}
Scan:
for scanner.Scan() {
select {
case <-g.context.Done():
break Scan
case wordChan <- scanner.Text():
}
}
close(wordChan)
workerGroup.Wait()
return nil
}
// GetConfigString returns the current config as a printable string
func (g *Gobuster) GetConfigString() (string, error) {
return g.plugin.GetConfigString()
}