1
1
Fork 0
mirror of https://github.com/OJ/gobuster.git synced 2024-05-19 02:36:02 +02:00
gobuster/libgobuster/http.go

179 lines
4.3 KiB
Go
Raw Normal View History

2018-12-11 21:41:42 +01:00
package libgobuster
2018-05-18 22:20:56 +02:00
import (
"context"
"crypto/tls"
2020-05-08 10:59:45 +02:00
"errors"
2018-05-18 22:20:56 +02:00
"fmt"
2018-06-27 22:23:18 +02:00
"io"
2018-05-18 22:20:56 +02:00
"net/http"
"net/url"
"strings"
)
2019-05-21 21:04:22 +02:00
// HTTPHeader holds a single key value pair of a HTTP header
type HTTPHeader struct {
Name string
Value string
}
2018-12-11 21:41:42 +01:00
// HTTPClient represents a http object
type HTTPClient struct {
2019-05-17 17:23:20 +02:00
client *http.Client
userAgent string
defaultUserAgent string
username string
password string
headers []HTTPHeader
2020-05-08 10:59:45 +02:00
cookies string
method string
2021-01-04 23:48:36 +01:00
host string
2018-05-18 22:20:56 +02:00
}
2020-05-08 10:59:45 +02:00
// RequestOptions is used to pass options to a single individual request
type RequestOptions struct {
Host string
Body io.Reader
ReturnBody bool
2018-12-11 21:41:42 +01:00
}
2018-05-18 22:20:56 +02:00
// NewHTTPClient returns a new HTTPClient
2021-02-25 00:35:51 +01:00
func NewHTTPClient(opt *HTTPOptions) (*HTTPClient, error) {
2018-05-18 22:20:56 +02:00
var proxyURLFunc func(*http.Request) (*url.URL, error)
2018-12-11 21:41:42 +01:00
var client HTTPClient
2018-05-18 22:20:56 +02:00
proxyURLFunc = http.ProxyFromEnvironment
2018-07-09 19:36:51 +02:00
if opt == nil {
return nil, fmt.Errorf("options is nil")
}
2018-05-18 22:20:56 +02:00
if opt.Proxy != "" {
proxyURL, err := url.Parse(opt.Proxy)
if err != nil {
2020-05-08 10:59:45 +02:00
return nil, fmt.Errorf("proxy URL is invalid (%w)", err)
2018-05-18 22:20:56 +02:00
}
proxyURLFunc = http.ProxyURL(proxyURL)
}
var redirectFunc func(req *http.Request, via []*http.Request) error
if !opt.FollowRedirect {
redirectFunc = func(req *http.Request, via []*http.Request) error {
return http.ErrUseLastResponse
}
} else {
redirectFunc = nil
}
client.client = &http.Client{
Timeout: opt.Timeout,
CheckRedirect: redirectFunc,
Transport: &http.Transport{
2019-04-17 21:17:29 +02:00
Proxy: proxyURLFunc,
MaxIdleConns: 100,
MaxIdleConnsPerHost: 100,
2018-05-18 22:20:56 +02:00
TLSClientConfig: &tls.Config{
2020-06-20 14:58:27 +02:00
InsecureSkipVerify: opt.NoTLSValidation,
2018-05-18 22:20:56 +02:00
},
}}
client.username = opt.Username
client.password = opt.Password
2018-05-18 22:31:49 +02:00
client.userAgent = opt.UserAgent
2019-05-17 17:23:20 +02:00
client.defaultUserAgent = DefaultUserAgent()
client.headers = opt.Headers
2020-05-08 10:59:45 +02:00
client.cookies = opt.Cookies
client.method = opt.Method
if client.method == "" {
client.method = http.MethodGet
}
2021-01-04 23:48:36 +01:00
// Host header needs to be set separately
for _, h := range opt.Headers {
if h.Name == "Host" {
client.host = h.Value
break
}
}
2018-05-18 22:20:56 +02:00
return &client, nil
}
// Request makes an http request and returns the status, the content length, the headers, the body and an error
// if you want the body returned set the corresponding property inside RequestOptions
func (client *HTTPClient) Request(ctx context.Context, fullURL string, opts RequestOptions) (int, int64, http.Header, []byte, error) {
2021-02-25 00:35:51 +01:00
resp, err := client.makeRequest(ctx, fullURL, opts.Host, opts.Body)
2018-12-11 21:41:42 +01:00
if err != nil {
2019-04-11 21:54:54 +02:00
// ignore context canceled errors
2021-02-25 00:35:51 +01:00
if errors.Is(ctx.Err(), context.Canceled) {
return 0, 0, nil, nil, nil
2019-04-11 21:54:54 +02:00
}
return 0, 0, nil, nil, err
2018-12-11 21:41:42 +01:00
}
defer resp.Body.Close()
2020-05-08 10:59:45 +02:00
var body []byte
var length int64
if opts.ReturnBody {
2021-02-25 00:35:51 +01:00
body, err = io.ReadAll(resp.Body)
2020-05-08 10:59:45 +02:00
if err != nil {
return 0, 0, nil, nil, fmt.Errorf("could not read body %w", err)
2018-12-11 21:41:42 +01:00
}
2020-05-08 10:59:45 +02:00
length = int64(len(body))
2018-12-11 21:41:42 +01:00
} else {
// DO NOT REMOVE!
// absolutely needed so golang will reuse connections!
2021-02-25 00:35:51 +01:00
length, err = io.Copy(io.Discard, resp.Body)
2018-12-11 21:41:42 +01:00
if err != nil {
return 0, 0, nil, nil, err
2018-12-11 21:41:42 +01:00
}
}
return resp.StatusCode, length, resp.Header, body, nil
2018-12-11 21:41:42 +01:00
}
2018-05-18 22:20:56 +02:00
2021-02-25 00:35:51 +01:00
func (client *HTTPClient) makeRequest(ctx context.Context, fullURL, host string, data io.Reader) (*http.Response, error) {
2020-05-08 10:59:45 +02:00
req, err := http.NewRequest(client.method, fullURL, data)
2018-12-11 21:41:42 +01:00
if err != nil {
2020-05-08 10:59:45 +02:00
return nil, err
2018-12-11 21:41:42 +01:00
}
2018-05-18 22:20:56 +02:00
// add the context so we can easily cancel out
2021-02-25 00:35:51 +01:00
req = req.WithContext(ctx)
2018-05-18 22:20:56 +02:00
2020-05-08 10:59:45 +02:00
if client.cookies != "" {
req.Header.Set("Cookie", client.cookies)
2018-05-18 22:20:56 +02:00
}
2021-01-04 23:48:36 +01:00
// Use host for VHOST mode on a per request basis, otherwise the one provided from headers
2018-12-11 21:41:42 +01:00
if host != "" {
req.Host = host
2021-01-04 23:48:36 +01:00
} else if client.host != "" {
req.Host = client.host
2018-12-11 21:41:42 +01:00
}
2018-05-18 22:20:56 +02:00
if client.userAgent != "" {
2019-04-16 23:44:58 +02:00
req.Header.Set("User-Agent", client.userAgent)
} else {
2019-05-17 17:23:20 +02:00
req.Header.Set("User-Agent", client.defaultUserAgent)
2018-05-18 22:20:56 +02:00
}
// add custom headers
for _, h := range client.headers {
req.Header.Set(h.Name, h.Value)
}
2018-05-18 22:20:56 +02:00
if client.username != "" {
req.SetBasicAuth(client.username, client.password)
}
resp, err := client.client.Do(req)
if err != nil {
2021-01-05 00:01:48 +01:00
var ue *url.Error
if errors.As(err, &ue) {
2018-05-18 22:20:56 +02:00
if strings.HasPrefix(ue.Err.Error(), "x509") {
2020-05-08 10:59:45 +02:00
return nil, fmt.Errorf("invalid certificate: %w", ue.Err)
2018-05-18 22:20:56 +02:00
}
2018-06-28 08:24:18 +02:00
}
2018-12-11 21:41:42 +01:00
return nil, err
2018-05-18 22:20:56 +02:00
}
2019-04-13 12:48:26 +02:00
2018-12-11 21:41:42 +01:00
return resp, nil
2018-05-18 22:20:56 +02:00
}