github.com/ck00004/CobaltStrikeParser-Go@v1.0.14/lib/http/httputil/dump.go (about)

     1  // Copyright 2009 The Go Authors. All rights reserved.
     2  // Use of this source code is governed by a BSD-style
     3  // license that can be found in the LICENSE file.
     4  
     5  package httputil
     6  
     7  import (
     8  	"bufio"
     9  	"bytes"
    10  	"errors"
    11  	"fmt"
    12  	"io"
    13  	"net"
    14  	"strings"
    15  	"time"
    16  
    17  	"github.com/ck00004/CobaltStrikeParser-Go/lib/url"
    18  
    19  	"github.com/ck00004/CobaltStrikeParser-Go/lib/http"
    20  )
    21  
    22  // drainBody reads all of b to memory and then returns two equivalent
    23  // ReadClosers yielding the same bytes.
    24  //
    25  // It returns an error if the initial slurp of all bytes fails. It does not attempt
    26  // to make the returned ReadClosers have identical error-matching behavior.
    27  func drainBody(b io.ReadCloser) (r1, r2 io.ReadCloser, err error) {
    28  	if b == nil || b == http.NoBody {
    29  		// No copying needed. Preserve the magic sentinel meaning of NoBody.
    30  		return http.NoBody, http.NoBody, nil
    31  	}
    32  	var buf bytes.Buffer
    33  	if _, err = buf.ReadFrom(b); err != nil {
    34  		return nil, b, err
    35  	}
    36  	if err = b.Close(); err != nil {
    37  		return nil, b, err
    38  	}
    39  	return io.NopCloser(&buf), io.NopCloser(bytes.NewReader(buf.Bytes())), nil
    40  }
    41  
    42  // dumpConn is a net.Conn which writes to Writer and reads from Reader
    43  type dumpConn struct {
    44  	io.Writer
    45  	io.Reader
    46  }
    47  
    48  func (c *dumpConn) Close() error                       { return nil }
    49  func (c *dumpConn) LocalAddr() net.Addr                { return nil }
    50  func (c *dumpConn) RemoteAddr() net.Addr               { return nil }
    51  func (c *dumpConn) SetDeadline(t time.Time) error      { return nil }
    52  func (c *dumpConn) SetReadDeadline(t time.Time) error  { return nil }
    53  func (c *dumpConn) SetWriteDeadline(t time.Time) error { return nil }
    54  
    55  type neverEnding byte
    56  
    57  func (b neverEnding) Read(p []byte) (n int, err error) {
    58  	for i := range p {
    59  		p[i] = byte(b)
    60  	}
    61  	return len(p), nil
    62  }
    63  
    64  // outGoingLength is a copy of the unexported
    65  // (*http.Request).outgoingLength method.
    66  func outgoingLength(req *http.Request) int64 {
    67  	if req.Body == nil || req.Body == http.NoBody {
    68  		return 0
    69  	}
    70  	if req.ContentLength != 0 {
    71  		return req.ContentLength
    72  	}
    73  	return -1
    74  }
    75  
    76  // DumpRequestOut is like DumpRequest but for outgoing client requests. It
    77  // includes any headers that the standard http.Transport adds, such as
    78  // User-Agent.
    79  func DumpRequestOut(req *http.Request, body bool) ([]byte, error) {
    80  	save := req.Body
    81  	dummyBody := false
    82  	if !body {
    83  		contentLength := outgoingLength(req)
    84  		if contentLength != 0 {
    85  			req.Body = io.NopCloser(io.LimitReader(neverEnding('x'), contentLength))
    86  			dummyBody = true
    87  		}
    88  	} else {
    89  		var err error
    90  		save, req.Body, err = drainBody(req.Body)
    91  		if err != nil {
    92  			return nil, err
    93  		}
    94  	}
    95  
    96  	// Since we're using the actual Transport code to write the request,
    97  	// switch to http so the Transport doesn't try to do an SSL
    98  	// negotiation with our dumpConn and its bytes.Buffer & pipe.
    99  	// The wire format for https and http are the same, anyway.
   100  	reqSend := req
   101  	if req.URL.Scheme == "https" {
   102  		reqSend = new(http.Request)
   103  		*reqSend = *req
   104  		reqSend.URL = new(url.URL)
   105  		*reqSend.URL = *req.URL
   106  		reqSend.URL.Scheme = "http"
   107  	}
   108  
   109  	// Use the actual Transport code to record what we would send
   110  	// on the wire, but not using TCP.  Use a Transport with a
   111  	// custom dialer that returns a fake net.Conn that waits
   112  	// for the full input (and recording it), and then responds
   113  	// with a dummy response.
   114  	var buf bytes.Buffer // records the output
   115  	pr, pw := io.Pipe()
   116  	defer pr.Close()
   117  	defer pw.Close()
   118  	dr := &delegateReader{c: make(chan io.Reader)}
   119  
   120  	t := &http.Transport{
   121  		Dial: func(net, addr string) (net.Conn, error) {
   122  			return &dumpConn{io.MultiWriter(&buf, pw), dr}, nil
   123  		},
   124  	}
   125  	defer t.CloseIdleConnections()
   126  
   127  	// We need this channel to ensure that the reader
   128  	// goroutine exits if t.RoundTrip returns an error.
   129  	// See golang.org/issue/32571.
   130  	quitReadCh := make(chan struct{})
   131  	// Wait for the request before replying with a dummy response:
   132  	go func() {
   133  		req, err := http.ReadRequest(bufio.NewReader(pr))
   134  		if err == nil {
   135  			// Ensure all the body is read; otherwise
   136  			// we'll get a partial dump.
   137  			io.Copy(io.Discard, req.Body)
   138  			req.Body.Close()
   139  		}
   140  		select {
   141  		case dr.c <- strings.NewReader("HTTP/1.1 204 No Content\r\nConnection: close\r\n\r\n"):
   142  		case <-quitReadCh:
   143  			// Ensure delegateReader.Read doesn't block forever if we get an error.
   144  			close(dr.c)
   145  		}
   146  	}()
   147  
   148  	_, err := t.RoundTrip(reqSend)
   149  
   150  	req.Body = save
   151  	if err != nil {
   152  		pw.Close()
   153  		dr.err = err
   154  		close(quitReadCh)
   155  		return nil, err
   156  	}
   157  	dump := buf.Bytes()
   158  
   159  	// If we used a dummy body above, remove it now.
   160  	// TODO: if the req.ContentLength is large, we allocate memory
   161  	// unnecessarily just to slice it off here. But this is just
   162  	// a debug function, so this is acceptable for now. We could
   163  	// discard the body earlier if this matters.
   164  	if dummyBody {
   165  		if i := bytes.Index(dump, []byte("\r\n\r\n")); i >= 0 {
   166  			dump = dump[:i+4]
   167  		}
   168  	}
   169  	return dump, nil
   170  }
   171  
   172  // delegateReader is a reader that delegates to another reader,
   173  // once it arrives on a channel.
   174  type delegateReader struct {
   175  	c   chan io.Reader
   176  	err error     // only used if r is nil and c is closed.
   177  	r   io.Reader // nil until received from c
   178  }
   179  
   180  func (r *delegateReader) Read(p []byte) (int, error) {
   181  	if r.r == nil {
   182  		var ok bool
   183  		if r.r, ok = <-r.c; !ok {
   184  			return 0, r.err
   185  		}
   186  	}
   187  	return r.r.Read(p)
   188  }
   189  
   190  // Return value if nonempty, def otherwise.
   191  func valueOrDefault(value, def string) string {
   192  	if value != "" {
   193  		return value
   194  	}
   195  	return def
   196  }
   197  
   198  var reqWriteExcludeHeaderDump = map[string]bool{
   199  	"Host":              true, // not in Header map anyway
   200  	"Transfer-Encoding": true,
   201  	"Trailer":           true,
   202  }
   203  
   204  // DumpRequest returns the given request in its HTTP/1.x wire
   205  // representation. It should only be used by servers to debug client
   206  // requests. The returned representation is an approximation only;
   207  // some details of the initial request are lost while parsing it into
   208  // an http.Request. In particular, the order and case of header field
   209  // names are lost. The order of values in multi-valued headers is kept
   210  // intact. HTTP/2 requests are dumped in HTTP/1.x form, not in their
   211  // original binary representations.
   212  //
   213  // If body is true, DumpRequest also returns the body. To do so, it
   214  // consumes req.Body and then replaces it with a new io.ReadCloser
   215  // that yields the same bytes. If DumpRequest returns an error,
   216  // the state of req is undefined.
   217  //
   218  // The documentation for http.Request.Write details which fields
   219  // of req are included in the dump.
   220  func DumpRequest(req *http.Request, body bool) ([]byte, error) {
   221  	var err error
   222  	save := req.Body
   223  	if !body || req.Body == nil {
   224  		req.Body = nil
   225  	} else {
   226  		save, req.Body, err = drainBody(req.Body)
   227  		if err != nil {
   228  			return nil, err
   229  		}
   230  	}
   231  
   232  	var b bytes.Buffer
   233  
   234  	// By default, print out the unmodified req.RequestURI, which
   235  	// is always set for incoming server requests. But because we
   236  	// previously used req.URL.RequestURI and the docs weren't
   237  	// always so clear about when to use DumpRequest vs
   238  	// DumpRequestOut, fall back to the old way if the caller
   239  	// provides a non-server Request.
   240  	reqURI := req.RequestURI
   241  	if reqURI == "" {
   242  		reqURI = req.URL.RequestURI()
   243  	}
   244  
   245  	fmt.Fprintf(&b, "%s %s HTTP/%d.%d\r\n", valueOrDefault(req.Method, "GET"),
   246  		reqURI, req.ProtoMajor, req.ProtoMinor)
   247  
   248  	absRequestURI := strings.HasPrefix(req.RequestURI, "http://") || strings.HasPrefix(req.RequestURI, "https://")
   249  	if !absRequestURI {
   250  		host := req.Host
   251  		if host == "" && req.URL != nil {
   252  			host = req.URL.Host
   253  		}
   254  		if host != "" {
   255  			fmt.Fprintf(&b, "Host: %s\r\n", host)
   256  		}
   257  	}
   258  
   259  	chunked := len(req.TransferEncoding) > 0 && req.TransferEncoding[0] == "chunked"
   260  	if len(req.TransferEncoding) > 0 {
   261  		fmt.Fprintf(&b, "Transfer-Encoding: %s\r\n", strings.Join(req.TransferEncoding, ","))
   262  	}
   263  	if req.Close {
   264  		fmt.Fprintf(&b, "Connection: close\r\n")
   265  	}
   266  
   267  	err = req.Header.WriteSubset(&b, reqWriteExcludeHeaderDump)
   268  	if err != nil {
   269  		return nil, err
   270  	}
   271  
   272  	io.WriteString(&b, "\r\n")
   273  
   274  	if req.Body != nil {
   275  		var dest io.Writer = &b
   276  		if chunked {
   277  			dest = NewChunkedWriter(dest)
   278  		}
   279  		_, err = io.Copy(dest, req.Body)
   280  		if chunked {
   281  			dest.(io.Closer).Close()
   282  			io.WriteString(&b, "\r\n")
   283  		}
   284  	}
   285  
   286  	req.Body = save
   287  	if err != nil {
   288  		return nil, err
   289  	}
   290  	return b.Bytes(), nil
   291  }
   292  
   293  // errNoBody is a sentinel error value used by failureToReadBody so we
   294  // can detect that the lack of body was intentional.
   295  var errNoBody = errors.New("sentinel error value")
   296  
   297  // failureToReadBody is a io.ReadCloser that just returns errNoBody on
   298  // Read. It's swapped in when we don't actually want to consume
   299  // the body, but need a non-nil one, and want to distinguish the
   300  // error from reading the dummy body.
   301  type failureToReadBody struct{}
   302  
   303  func (failureToReadBody) Read([]byte) (int, error) { return 0, errNoBody }
   304  func (failureToReadBody) Close() error             { return nil }
   305  
   306  // emptyBody is an instance of empty reader.
   307  var emptyBody = io.NopCloser(strings.NewReader(""))
   308  
   309  // DumpResponse is like DumpRequest but dumps a response.
   310  func DumpResponse(resp *http.Response, body bool) ([]byte, error) {
   311  	var b bytes.Buffer
   312  	var err error
   313  	save := resp.Body
   314  	savecl := resp.ContentLength
   315  
   316  	if !body {
   317  		// For content length of zero. Make sure the body is an empty
   318  		// reader, instead of returning error through failureToReadBody{}.
   319  		if resp.ContentLength == 0 {
   320  			resp.Body = emptyBody
   321  		} else {
   322  			resp.Body = failureToReadBody{}
   323  		}
   324  	} else if resp.Body == nil {
   325  		resp.Body = emptyBody
   326  	} else {
   327  		save, resp.Body, err = drainBody(resp.Body)
   328  		if err != nil {
   329  			return nil, err
   330  		}
   331  	}
   332  	err = resp.Write(&b)
   333  	if err == errNoBody {
   334  		err = nil
   335  	}
   336  	resp.Body = save
   337  	resp.ContentLength = savecl
   338  	if err != nil {
   339  		return nil, err
   340  	}
   341  	return b.Bytes(), nil
   342  }