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

213 lines
5.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 (
2019-04-13 12:48:26 +02:00
"bytes"
2018-05-18 22:20:56 +02:00
"context"
"crypto/tls"
"fmt"
2018-06-27 22:23:18 +02:00
"io"
2018-05-18 22:20:56 +02:00
"io/ioutil"
"net/http"
"net/url"
"strings"
2018-12-11 21:41:42 +01:00
"time"
2018-05-18 22:20:56 +02:00
"unicode/utf8"
)
2018-12-11 21:41:42 +01:00
// HTTPClient represents a http object
type HTTPClient struct {
2018-05-18 22:20:56 +02:00
client *http.Client
context context.Context
userAgent string
username string
password string
includeLength bool
}
2018-12-11 21:41:42 +01:00
// HTTPOptions provides options to the http client
type HTTPOptions struct {
Proxy string
Username string
Password string
UserAgent string
Timeout time.Duration
FollowRedirect bool
InsecureSSL bool
IncludeLength bool
}
2018-05-18 22:20:56 +02:00
// NewHTTPClient returns a new HTTPClient
2018-12-11 21:41:42 +01:00
func NewHTTPClient(c context.Context, 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 {
2018-05-20 22:37:33 +02:00
return nil, fmt.Errorf("proxy URL is invalid (%v)", 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{
Proxy: proxyURLFunc,
TLSClientConfig: &tls.Config{
InsecureSkipVerify: opt.InsecureSSL,
},
}}
client.context = c
client.username = opt.Username
client.password = opt.Password
client.includeLength = opt.IncludeLength
2018-05-18 22:31:49 +02:00
client.userAgent = opt.UserAgent
2018-05-18 22:20:56 +02:00
return &client, nil
}
2019-04-13 12:48:26 +02:00
// Get gets an URL and returns the status, the length and an error
2018-12-11 21:41:42 +01:00
func (client *HTTPClient) Get(fullURL, host, cookie string) (*int, *int64, error) {
2019-04-13 12:48:26 +02:00
return client.requestWithoutBody(http.MethodGet, fullURL, host, cookie, "")
}
// Post posts to an URL and returns the status, the length and an error
func (client *HTTPClient) Post(fullURL, host, cookie, data string) (*int, *int64, error) {
return client.requestWithoutBody(http.MethodPost, fullURL, host, cookie, data)
}
// GetWithBody gets an URL and returns the status and the body
func (client *HTTPClient) GetWithBody(fullURL, host, cookie string) (*int, *string, error) {
return client.requestWithBody(http.MethodGet, fullURL, host, cookie, "")
}
// PostWithBody gets an URL and returns the status and the body
func (client *HTTPClient) PostWithBody(fullURL, host, cookie, data string) (*int, *string, error) {
return client.requestWithBody(http.MethodPost, fullURL, host, cookie, data)
}
// requestWithoutBody makes an http request and returns the status, the length and an error
func (client *HTTPClient) requestWithoutBody(method, fullURL, host, cookie, data string) (*int, *int64, error) {
resp, err := client.makeRequest(method, fullURL, host, cookie, data)
2018-12-11 21:41:42 +01:00
if err != nil {
2019-04-11 21:54:54 +02:00
// ignore context canceled errors
if client.context.Err() == context.Canceled {
return nil, nil, nil
}
2018-12-11 21:41:42 +01:00
return nil, nil, err
}
defer resp.Body.Close()
var length *int64
if client.includeLength {
length = new(int64)
if resp.ContentLength <= 0 {
body, err2 := ioutil.ReadAll(resp.Body)
if err2 == nil {
*length = int64(utf8.RuneCountInString(string(body)))
}
} else {
*length = resp.ContentLength
}
} else {
// DO NOT REMOVE!
// absolutely needed so golang will reuse connections!
_, err := io.Copy(ioutil.Discard, resp.Body)
if err != nil {
return nil, nil, err
}
}
return &resp.StatusCode, length, nil
}
2018-05-18 22:20:56 +02:00
2019-04-13 12:48:26 +02:00
// requestWithBody makes an http request and returns the status and the body
func (client *HTTPClient) requestWithBody(method, fullURL, host, cookie, data string) (*int, *string, error) {
resp, err := client.makeRequest(method, fullURL, host, cookie, data)
2018-05-18 22:20:56 +02:00
if err != nil {
2019-04-11 21:54:54 +02:00
// ignore context canceled errors
if client.context.Err() == context.Canceled {
return nil, nil, nil
}
2018-05-18 22:20:56 +02:00
return nil, nil, err
}
2018-12-11 21:41:42 +01:00
defer resp.Body.Close()
body, err := ioutil.ReadAll(resp.Body)
if err != nil {
return nil, nil, fmt.Errorf("could not read body: %v", err)
}
bodyString := string(body)
return &resp.StatusCode, &bodyString, nil
}
2019-04-13 12:48:26 +02:00
func (client *HTTPClient) makeRequest(method, fullURL, host, cookie, data string) (*http.Response, error) {
var req *http.Request
var err error
switch method {
case http.MethodGet:
req, err = http.NewRequest(http.MethodGet, fullURL, nil)
if err != nil {
return nil, err
}
case http.MethodPost:
buf := bytes.NewBufferString(data)
req, err = http.NewRequest(http.MethodPost, fullURL, buf)
if err != nil {
return nil, err
}
default:
return nil, fmt.Errorf("invalid method %s", method)
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
req = req.WithContext(client.context)
if cookie != "" {
req.Header.Set("Cookie", cookie)
}
2018-12-11 21:41:42 +01:00
if host != "" {
req.Host = host
}
ua := DefaultUserAgent()
2018-05-18 22:20:56 +02:00
if client.userAgent != "" {
ua = client.userAgent
}
req.Header.Set("User-Agent", ua)
if client.username != "" {
req.SetBasicAuth(client.username, client.password)
}
resp, err := client.client.Do(req)
if err != nil {
if ue, ok := err.(*url.Error); ok {
if strings.HasPrefix(ue.Err.Error(), "x509") {
2018-12-11 21:41:42 +01:00
return nil, fmt.Errorf("Invalid certificate: %v", 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
}