subfinder/v2/pkg/runner/outputter.go

237 lines
5.3 KiB
Go

package runner
import (
"bufio"
"errors"
"io"
"os"
"path/filepath"
"strings"
jsoniter "github.com/json-iterator/go"
"github.com/projectdiscovery/subfinder/v2/pkg/resolve"
)
// OutPutter outputs content to writers.
type OutPutter struct {
JSON bool
}
type jsonSourceResult struct {
Host string `json:"host"`
Input string `json:"input"`
Source string `json:"source"`
}
type jsonSourceIPResult struct {
Host string `json:"host"`
IP string `json:"ip"`
Input string `json:"input"`
Source string `json:"source"`
}
type jsonSourcesResult struct {
Host string `json:"host"`
Input string `json:"input"`
Sources []string `json:"sources"`
}
// NewOutputter creates a new Outputter
func NewOutputter(json bool) *OutPutter {
return &OutPutter{JSON: json}
}
func (o *OutPutter) createFile(filename string, appendtoFile bool) (*os.File, error) {
if filename == "" {
return nil, errors.New("empty filename")
}
dir := filepath.Dir(filename)
if dir != "" {
if _, err := os.Stat(dir); os.IsNotExist(err) {
err := os.MkdirAll(dir, os.ModePerm)
if err != nil {
return nil, err
}
}
}
var file *os.File
var err error
if appendtoFile {
file, err = os.OpenFile(filename, os.O_APPEND|os.O_CREATE|os.O_WRONLY, 0644)
} else {
file, err = os.Create(filename)
}
if err != nil {
return nil, err
}
return file, nil
}
// WriteHostIP writes the output list of subdomain to an io.Writer
func (o *OutPutter) WriteHostIP(input string, results map[string]resolve.Result, writer io.Writer) error {
var err error
if o.JSON {
err = writeJSONHostIP(input, results, writer)
} else {
err = writePlainHostIP(input, results, writer)
}
return err
}
func writePlainHostIP(_ string, results map[string]resolve.Result, writer io.Writer) error {
bufwriter := bufio.NewWriter(writer)
sb := &strings.Builder{}
for _, result := range results {
sb.WriteString(result.Host)
sb.WriteString(",")
sb.WriteString(result.IP)
sb.WriteString(",")
sb.WriteString(result.Source)
sb.WriteString("\n")
_, err := bufwriter.WriteString(sb.String())
if err != nil {
bufwriter.Flush()
return err
}
sb.Reset()
}
return bufwriter.Flush()
}
func writeJSONHostIP(input string, results map[string]resolve.Result, writer io.Writer) error {
encoder := jsoniter.NewEncoder(writer)
var data jsonSourceIPResult
for _, result := range results {
data.Host = result.Host
data.IP = result.IP
data.Input = input
data.Source = result.Source
err := encoder.Encode(&data)
if err != nil {
return err
}
}
return nil
}
// WriteHostNoWildcard writes the output list of subdomain with nW flag to an io.Writer
func (o *OutPutter) WriteHostNoWildcard(input string, results map[string]resolve.Result, writer io.Writer) error {
hosts := make(map[string]resolve.HostEntry)
for host, result := range results {
hosts[host] = resolve.HostEntry{Host: result.Host, Source: result.Source}
}
return o.WriteHost(input, hosts, writer)
}
// WriteHost writes the output list of subdomain to an io.Writer
func (o *OutPutter) WriteHost(input string, results map[string]resolve.HostEntry, writer io.Writer) error {
var err error
if o.JSON {
err = writeJSONHost(input, results, writer)
} else {
err = writePlainHost(input, results, writer)
}
return err
}
func writePlainHost(_ string, results map[string]resolve.HostEntry, writer io.Writer) error {
bufwriter := bufio.NewWriter(writer)
sb := &strings.Builder{}
for _, result := range results {
sb.WriteString(result.Host)
sb.WriteString("\n")
_, err := bufwriter.WriteString(sb.String())
if err != nil {
bufwriter.Flush()
return err
}
sb.Reset()
}
return bufwriter.Flush()
}
func writeJSONHost(input string, results map[string]resolve.HostEntry, writer io.Writer) error {
encoder := jsoniter.NewEncoder(writer)
var data jsonSourceResult
for _, result := range results {
data.Host = result.Host
data.Input = input
data.Source = result.Source
err := encoder.Encode(data)
if err != nil {
return err
}
}
return nil
}
// WriteSourceHost writes the output list of subdomain to an io.Writer
func (o *OutPutter) WriteSourceHost(input string, sourceMap map[string]map[string]struct{}, writer io.Writer) error {
var err error
if o.JSON {
err = writeSourceJSONHost(input, sourceMap, writer)
} else {
err = writeSourcePlainHost(input, sourceMap, writer)
}
return err
}
func writeSourceJSONHost(input string, sourceMap map[string]map[string]struct{}, writer io.Writer) error {
encoder := jsoniter.NewEncoder(writer)
var data jsonSourcesResult
for host, sources := range sourceMap {
data.Host = host
data.Input = input
keys := make([]string, 0, len(sources))
for source := range sources {
keys = append(keys, source)
}
data.Sources = keys
err := encoder.Encode(&data)
if err != nil {
return err
}
}
return nil
}
func writeSourcePlainHost(_ string, sourceMap map[string]map[string]struct{}, writer io.Writer) error {
bufwriter := bufio.NewWriter(writer)
sb := &strings.Builder{}
for host, sources := range sourceMap {
sb.WriteString(host)
sb.WriteString(",[")
sourcesString := ""
for source := range sources {
sourcesString += source + ","
}
sb.WriteString(strings.Trim(sourcesString, ", "))
sb.WriteString("]\n")
_, err := bufwriter.WriteString(sb.String())
if err != nil {
bufwriter.Flush()
return err
}
sb.Reset()
}
return bufwriter.Flush()
}