This is a fork of: https://github.com/roglew/puppy
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

810 lines
20 KiB

8 years ago
package main
/*
Wrappers around http.Request and http.Response to add helper functions needed by the proxy
*/
import (
"bufio"
"bytes"
"crypto/tls"
"fmt"
"io"
"io/ioutil"
"net"
"net/http"
"net/url"
"reflect"
"strconv"
"strings"
8 years ago
"time"
"github.com/deckarep/golang-set"
"github.com/gorilla/websocket"
"golang.org/x/net/proxy"
8 years ago
)
const (
ToServer = iota
ToClient
)
type NetDialer func(network, addr string) (net.Conn, error)
8 years ago
type ProxyResponse struct {
http.Response
bodyBytes []byte
DbId string // ID used by storage implementation. Blank string = unsaved
8 years ago
Unmangled *ProxyResponse
}
type ProxyRequest struct {
http.Request
// Destination connection info
DestHost string
DestPort int
DestUseTLS bool
// Associated messages
ServerResponse *ProxyResponse
WSMessages []*ProxyWSMessage
Unmangled *ProxyRequest
// Additional data
bodyBytes []byte
DbId string // ID used by storage implementation. Blank string = unsaved
8 years ago
StartDatetime time.Time
EndDatetime time.Time
tags mapset.Set
NetDial NetDialer
8 years ago
}
type WSSession struct {
websocket.Conn
8 years ago
Request *ProxyRequest // Request used for handshake
}
type ProxyWSMessage struct {
Type int
Message []byte
8 years ago
Direction int
Unmangled *ProxyWSMessage
Timestamp time.Time
Request *ProxyRequest
8 years ago
DbId string // ID used by storage implementation. Blank string = unsaved
}
func PerformConnect(conn net.Conn, destHost string, destPort int) error {
connStr := []byte(fmt.Sprintf("CONNECT %s:%d HTTP/1.1\r\nHost: %s\r\nProxy-Connection: Keep-Alive\r\n\r\n", destHost, destPort, destHost))
conn.Write(connStr)
rsp, err := http.ReadResponse(bufio.NewReader(conn), nil)
if err != nil {
return fmt.Errorf("error performing CONNECT handshake: %s", err.Error())
}
if rsp.StatusCode != 200 {
return fmt.Errorf("error performing CONNECT handshake")
}
return nil
}
func NewProxyRequest(r *http.Request, destHost string, destPort int, destUseTLS bool) *ProxyRequest {
8 years ago
var retReq *ProxyRequest
if r != nil {
// Write/reread the request to make sure we get all the extra headers Go adds into req.Header
buf := bytes.NewBuffer(make([]byte, 0))
r.Write(buf)
httpReq2, err := http.ReadRequest(bufio.NewReader(buf))
if err != nil {
panic(err)
}
retReq = &ProxyRequest{
*httpReq2,
destHost,
destPort,
destUseTLS,
nil,
make([]*ProxyWSMessage, 0),
nil,
make([]byte, 0),
"",
time.Unix(0, 0),
time.Unix(0, 0),
mapset.NewSet(),
nil,
8 years ago
}
} else {
newReq, _ := http.NewRequest("GET", "/", nil) // Ignore error since this should be run the same every time and shouldn't error
newReq.Header.Set("User-Agent", "Puppy-Proxy/1.0")
newReq.Host = destHost
retReq = &ProxyRequest{
*newReq,
destHost,
destPort,
destUseTLS,
nil,
make([]*ProxyWSMessage, 0),
nil,
make([]byte, 0),
"",
time.Unix(0, 0),
time.Unix(0, 0),
mapset.NewSet(),
nil,
8 years ago
}
}
// Load the body
bodyBuf, _ := ioutil.ReadAll(retReq.Body)
retReq.SetBodyBytes(bodyBuf)
return retReq
}
func ProxyRequestFromBytes(b []byte, destHost string, destPort int, destUseTLS bool) (*ProxyRequest, error) {
8 years ago
buf := bytes.NewBuffer(b)
httpReq, err := http.ReadRequest(bufio.NewReader(buf))
if err != nil {
return nil, err
}
return NewProxyRequest(httpReq, destHost, destPort, destUseTLS), nil
}
func NewProxyResponse(r *http.Response) *ProxyResponse {
8 years ago
// Write/reread the request to make sure we get all the extra headers Go adds into req.Header
oldClose := r.Close
r.Close = false
buf := bytes.NewBuffer(make([]byte, 0))
r.Write(buf)
r.Close = oldClose
httpRsp2, err := http.ReadResponse(bufio.NewReader(buf), nil)
if err != nil {
panic(err)
}
httpRsp2.Close = false
retRsp := &ProxyResponse{
*httpRsp2,
make([]byte, 0),
"",
nil,
}
bodyBuf, _ := ioutil.ReadAll(retRsp.Body)
retRsp.SetBodyBytes(bodyBuf)
return retRsp
}
func ProxyResponseFromBytes(b []byte) (*ProxyResponse, error) {
buf := bytes.NewBuffer(b)
httpRsp, err := http.ReadResponse(bufio.NewReader(buf), nil)
if err != nil {
return nil, err
}
return NewProxyResponse(httpRsp), nil
}
func NewProxyWSMessage(mtype int, message []byte, direction int) (*ProxyWSMessage, error) {
return &ProxyWSMessage{
Type: mtype,
Message: message,
8 years ago
Direction: direction,
Unmangled: nil,
Timestamp: time.Unix(0, 0),
DbId: "",
8 years ago
}, nil
}
func (req *ProxyRequest) DestScheme() string {
if req.IsWSUpgrade() {
if req.DestUseTLS {
return "wss"
} else {
return "ws"
}
} else {
if req.DestUseTLS {
return "https"
} else {
return "http"
}
}
}
func (req *ProxyRequest) FullURL() *url.URL {
// Same as req.URL but guarantees it will include the scheme, host, and port if necessary
8 years ago
var u url.URL
u = *(req.URL) // Copy the original req.URL
u.Host = req.Host
u.Scheme = req.DestScheme()
return &u
}
func (req *ProxyRequest) DestURL() *url.URL {
// Same as req.FullURL() but uses DestHost and DestPort for the host and port
8 years ago
var u url.URL
u = *(req.URL) // Copy the original req.URL
u.Scheme = req.DestScheme()
if req.DestUseTLS && req.DestPort == 443 ||
!req.DestUseTLS && req.DestPort == 80 {
u.Host = req.DestHost
} else {
u.Host = fmt.Sprintf("%s:%d", req.DestHost, req.DestPort)
}
return &u
}
func (req *ProxyRequest) Submit(conn net.Conn) error {
return req.submit(conn, false, nil)
}
func (req *ProxyRequest) SubmitProxy(conn net.Conn, creds *ProxyCredentials) error {
return req.submit(conn, true, creds)
}
func (req *ProxyRequest) submit(conn net.Conn, forProxy bool, proxyCreds *ProxyCredentials) error {
// Write the request to the connection
req.StartDatetime = time.Now()
if forProxy {
if req.DestUseTLS {
req.URL.Scheme = "https"
} else {
req.URL.Scheme = "http"
}
req.URL.Opaque = ""
if err := req.RepeatableProxyWrite(conn, proxyCreds); err != nil {
8 years ago
return err
}
} else {
if err := req.RepeatableWrite(conn); err != nil {
8 years ago
return err
}
}
// Read a response from the server
httpRsp, err := http.ReadResponse(bufio.NewReader(conn), nil)
if err != nil {
return fmt.Errorf("error reading response: %s", err.Error())
8 years ago
}
req.EndDatetime = time.Now()
prsp := NewProxyResponse(httpRsp)
req.ServerResponse = prsp
return nil
}
func (req *ProxyRequest) WSDial(conn net.Conn) (*WSSession, error) {
8 years ago
if !req.IsWSUpgrade() {
return nil, fmt.Errorf("could not start websocket session: request is not a websocket handshake request")
}
upgradeHeaders := make(http.Header)
for k, v := range req.Header {
for _, vv := range v {
if !(k == "Upgrade" ||
k == "Connection" ||
k == "Sec-Websocket-Key" ||
k == "Sec-Websocket-Version" ||
k == "Sec-Websocket-Extensions" ||
k == "Sec-Websocket-Protocol") {
upgradeHeaders.Add(k, vv)
}
}
}
dialer := &websocket.Dialer{}
dialer.NetDial = func(network, address string) (net.Conn, error) {
return conn, nil
}
wsconn, rsp, err := dialer.Dial(req.DestURL().String(), upgradeHeaders)
8 years ago
if err != nil {
return nil, fmt.Errorf("could not dial WebSocket server: %s", err)
}
req.ServerResponse = NewProxyResponse(rsp)
wsession := &WSSession{
*wsconn,
8 years ago
req,
}
return wsession, nil
}
func WSDial(req *ProxyRequest) (*WSSession, error) {
return wsDial(req, false, "", 0, nil, false)
}
func WSDialProxy(req *ProxyRequest, proxyHost string, proxyPort int, creds *ProxyCredentials) (*WSSession, error) {
return wsDial(req, true, proxyHost, proxyPort, creds, false)
}
func WSDialSOCKSProxy(req *ProxyRequest, proxyHost string, proxyPort int, creds *ProxyCredentials) (*WSSession, error) {
return wsDial(req, true, proxyHost, proxyPort, creds, true)
}
func wsDial(req *ProxyRequest, useProxy bool, proxyHost string, proxyPort int, proxyCreds *ProxyCredentials, proxyIsSOCKS bool) (*WSSession, error) {
var conn net.Conn
var dialer NetDialer
var err error
if req.NetDial != nil {
dialer = req.NetDial
} else {
dialer = net.Dial
}
if useProxy {
if proxyIsSOCKS {
var socksCreds *proxy.Auth
if proxyCreds != nil {
socksCreds = &proxy.Auth{
User: proxyCreds.Username,
Password: proxyCreds.Password,
}
}
socksDialer, err := proxy.SOCKS5("tcp", fmt.Sprintf("%s:%d", proxyHost, proxyPort), socksCreds, proxy.Direct)
if err != nil {
return nil, fmt.Errorf("error creating SOCKS dialer: %s", err.Error())
}
conn, err = socksDialer.Dial("tcp", fmt.Sprintf("%s:%d", req.DestHost, req.DestPort))
if err != nil {
return nil, fmt.Errorf("error dialing host: %s", err.Error())
}
defer conn.Close()
} else {
conn, err = dialer("tcp", fmt.Sprintf("%s:%d", proxyHost, proxyPort))
if err != nil {
return nil, fmt.Errorf("error dialing proxy: %s", err.Error())
}
// always perform a CONNECT for websocket regardless of SSL
if err := PerformConnect(conn, req.DestHost, req.DestPort); err != nil {
return nil, err
}
}
} else {
conn, err = dialer("tcp", fmt.Sprintf("%s:%d", req.DestHost, req.DestPort))
if err != nil {
return nil, fmt.Errorf("error dialing host: %s", err.Error())
}
}
if req.DestUseTLS {
tls_conn := tls.Client(conn, &tls.Config{
InsecureSkipVerify: true,
})
conn = tls_conn
}
return req.WSDial(conn)
}
8 years ago
func (req *ProxyRequest) IsWSUpgrade() bool {
for k, v := range req.Header {
for _, vv := range v {
if strings.ToLower(k) == "upgrade" && strings.Contains(vv, "websocket") {
return true
}
}
}
return false
}
func (req *ProxyRequest) StripProxyHeaders() {
if !req.IsWSUpgrade() {
req.Header.Del("Connection")
}
req.Header.Del("Accept-Encoding")
req.Header.Del("Proxy-Connection")
req.Header.Del("Proxy-Authenticate")
req.Header.Del("Proxy-Authorization")
}
func (req *ProxyRequest) Eq(other *ProxyRequest) bool {
if req.StatusLine() != other.StatusLine() ||
!reflect.DeepEqual(req.Header, other.Header) ||
bytes.Compare(req.BodyBytes(), other.BodyBytes()) != 0 ||
req.DestHost != other.DestHost ||
req.DestPort != other.DestPort ||
req.DestUseTLS != other.DestUseTLS {
return false
}
return true
}
func (req *ProxyRequest) Clone() *ProxyRequest {
8 years ago
buf := bytes.NewBuffer(make([]byte, 0))
req.RepeatableWrite(buf)
newReq, err := ProxyRequestFromBytes(buf.Bytes(), req.DestHost, req.DestPort, req.DestUseTLS)
if err != nil {
panic(err)
}
newReq.DestHost = req.DestHost
newReq.DestPort = req.DestPort
newReq.DestUseTLS = req.DestUseTLS
newReq.Header = CopyHeader(req.Header)
return newReq
}
func (req *ProxyRequest) DeepClone() *ProxyRequest {
8 years ago
// Returns a request with the same request, response, and associated websocket messages
newReq := req.Clone()
newReq.DbId = req.DbId
if req.Unmangled != nil {
newReq.Unmangled = req.Unmangled.DeepClone()
}
8 years ago
if req.ServerResponse != nil {
newReq.ServerResponse = req.ServerResponse.DeepClone()
}
for _, wsm := range req.WSMessages {
newReq.WSMessages = append(newReq.WSMessages, wsm.DeepClone())
}
return newReq
}
func (req *ProxyRequest) resetBodyReader() {
// yes I know this method isn't the most efficient, I'll fix it if it causes problems later
req.Body = ioutil.NopCloser(bytes.NewBuffer(req.BodyBytes()))
}
func (req *ProxyRequest) RepeatableWrite(w io.Writer) error {
defer req.resetBodyReader()
return req.Write(w)
}
func (req *ProxyRequest) RepeatableProxyWrite(w io.Writer, proxyCreds *ProxyCredentials) error {
defer req.resetBodyReader()
if proxyCreds != nil {
authHeader := proxyCreds.SerializeHeader()
req.Header.Set("Proxy-Authorization", authHeader)
defer func() { req.Header.Del("Proxy-Authorization") }()
}
return req.WriteProxy(w)
8 years ago
}
func (req *ProxyRequest) BodyBytes() []byte {
return DuplicateBytes(req.bodyBytes)
}
func (req *ProxyRequest) SetBodyBytes(bs []byte) {
req.bodyBytes = bs
req.resetBodyReader()
// Parse the form if we can, ignore errors
req.ParseMultipartForm(1024 * 1024 * 1024) // 1GB for no good reason
8 years ago
req.ParseForm()
req.resetBodyReader()
req.Header.Set("Content-Length", strconv.Itoa(len(bs)))
}
func (req *ProxyRequest) FullMessage() []byte {
buf := bytes.NewBuffer(make([]byte, 0))
req.RepeatableWrite(buf)
return buf.Bytes()
}
func (req *ProxyRequest) PostParameters() (url.Values, error) {
vals, err := url.ParseQuery(string(req.BodyBytes()))
if err != nil {
return nil, err
}
return vals, nil
}
func (req *ProxyRequest) SetPostParameter(key string, value string) {
req.PostForm.Set(key, value)
req.SetBodyBytes([]byte(req.PostForm.Encode()))
}
func (req *ProxyRequest) AddPostParameter(key string, value string) {
req.PostForm.Add(key, value)
req.SetBodyBytes([]byte(req.PostForm.Encode()))
}
func (req *ProxyRequest) DeletePostParameter(key string, value string) {
req.PostForm.Del(key)
req.SetBodyBytes([]byte(req.PostForm.Encode()))
}
func (req *ProxyRequest) SetURLParameter(key string, value string) {
q := req.URL.Query()
q.Set(key, value)
req.URL.RawQuery = q.Encode()
req.ParseForm()
}
func (req *ProxyRequest) URLParameters() url.Values {
8 years ago
vals := req.URL.Query()
return vals
}
func (req *ProxyRequest) AddURLParameter(key string, value string) {
q := req.URL.Query()
q.Add(key, value)
req.URL.RawQuery = q.Encode()
req.ParseForm()
}
func (req *ProxyRequest) DeleteURLParameter(key string, value string) {
q := req.URL.Query()
q.Del(key)
req.URL.RawQuery = q.Encode()
req.ParseForm()
}
func (req *ProxyRequest) AddTag(tag string) {
req.tags.Add(tag)
}
func (req *ProxyRequest) CheckTag(tag string) bool {
return req.tags.Contains(tag)
}
func (req *ProxyRequest) RemoveTag(tag string) {
req.tags.Remove(tag)
}
func (req *ProxyRequest) ClearTags() {
req.tags.Clear()
}
func (req *ProxyRequest) Tags() []string {
items := req.tags.ToSlice()
retslice := make([]string, 0)
for _, item := range items {
str, ok := item.(string)
if ok {
retslice = append(retslice, str)
}
}
return retslice
}
func (req *ProxyRequest) HTTPPath() string {
// The path used in the http request
u := *req.URL
u.Scheme = ""
u.Host = ""
u.Opaque = ""
u.User = nil
return u.String()
}
func (req *ProxyRequest) StatusLine() string {
return fmt.Sprintf("%s %s %s", req.Method, req.HTTPPath(), req.Proto)
}
func (req *ProxyRequest) HeaderSection() string {
8 years ago
retStr := req.StatusLine()
retStr += "\r\n"
for k, vs := range req.Header {
for _, v := range vs {
retStr += fmt.Sprintf("%s: %s\r\n", k, v)
}
}
return retStr
}
func (rsp *ProxyResponse) resetBodyReader() {
// yes I know this method isn't the most efficient, I'll fix it if it causes problems later
rsp.Body = ioutil.NopCloser(bytes.NewBuffer(rsp.BodyBytes()))
}
func (rsp *ProxyResponse) RepeatableWrite(w io.Writer) error {
defer rsp.resetBodyReader()
return rsp.Write(w)
8 years ago
}
func (rsp *ProxyResponse) BodyBytes() []byte {
return DuplicateBytes(rsp.bodyBytes)
}
func (rsp *ProxyResponse) SetBodyBytes(bs []byte) {
rsp.bodyBytes = bs
rsp.resetBodyReader()
rsp.Header.Set("Content-Length", strconv.Itoa(len(bs)))
}
func (rsp *ProxyResponse) Clone() *ProxyResponse {
8 years ago
buf := bytes.NewBuffer(make([]byte, 0))
rsp.RepeatableWrite(buf)
newRsp, err := ProxyResponseFromBytes(buf.Bytes())
if err != nil {
panic(err)
}
return newRsp
}
func (rsp *ProxyResponse) DeepClone() *ProxyResponse {
8 years ago
newRsp := rsp.Clone()
newRsp.DbId = rsp.DbId
if rsp.Unmangled != nil {
newRsp.Unmangled = rsp.Unmangled.DeepClone()
}
return newRsp
}
func (rsp *ProxyResponse) Eq(other *ProxyResponse) bool {
if rsp.StatusLine() != other.StatusLine() ||
!reflect.DeepEqual(rsp.Header, other.Header) ||
bytes.Compare(rsp.BodyBytes(), other.BodyBytes()) != 0 {
return false
}
return true
}
func (rsp *ProxyResponse) FullMessage() []byte {
buf := bytes.NewBuffer(make([]byte, 0))
rsp.RepeatableWrite(buf)
return buf.Bytes()
}
func (rsp *ProxyResponse) HTTPStatus() string {
// The status text to be used in the http request
text := rsp.Status
if text == "" {
text = http.StatusText(rsp.StatusCode)
if text == "" {
text = "status code " + strconv.Itoa(rsp.StatusCode)
}
} else {
// Just to reduce stutter, if user set rsp.Status to "200 OK" and StatusCode to 200.
// Not important.
text = strings.TrimPrefix(text, strconv.Itoa(rsp.StatusCode)+" ")
}
return text
}
func (rsp *ProxyResponse) StatusLine() string {
// Status line, stolen from net/http/response.go
return fmt.Sprintf("HTTP/%d.%d %03d %s", rsp.ProtoMajor, rsp.ProtoMinor, rsp.StatusCode, rsp.HTTPStatus())
}
func (rsp *ProxyResponse) HeaderSection() string {
8 years ago
retStr := rsp.StatusLine()
retStr += "\r\n"
for k, vs := range rsp.Header {
for _, v := range vs {
retStr += fmt.Sprintf("%s: %s\r\n", k, v)
}
}
return retStr
}
func (msg *ProxyWSMessage) String() string {
var dirStr string
if msg.Direction == ToClient {
dirStr = "ToClient"
} else {
dirStr = "ToServer"
}
return fmt.Sprintf("{WS Message msg=\"%s\", type=%d, dir=%s}", string(msg.Message), msg.Type, dirStr)
}
func (msg *ProxyWSMessage) Clone() *ProxyWSMessage {
8 years ago
var retMsg ProxyWSMessage
retMsg.Type = msg.Type
retMsg.Message = msg.Message
retMsg.Direction = msg.Direction
retMsg.Timestamp = msg.Timestamp
retMsg.Request = msg.Request
return &retMsg
}
func (msg *ProxyWSMessage) DeepClone() *ProxyWSMessage {
8 years ago
retMsg := msg.Clone()
retMsg.DbId = msg.DbId
if msg.Unmangled != nil {
retMsg.Unmangled = msg.Unmangled.DeepClone()
}
return retMsg
}
func (msg *ProxyWSMessage) Eq(other *ProxyWSMessage) bool {
if msg.Type != other.Type ||
msg.Direction != other.Direction ||
bytes.Compare(msg.Message, other.Message) != 0 {
return false
}
return true
}
func CopyHeader(hd http.Header) http.Header {
8 years ago
var ret http.Header = make(http.Header)
for k, vs := range hd {
for _, v := range vs {
ret.Add(k, v)
}
}
return ret
}
func submitRequest(req *ProxyRequest, useProxy bool, proxyHost string,
proxyPort int, proxyCreds *ProxyCredentials, proxyIsSOCKS bool) error {
var dialer NetDialer = req.NetDial
if dialer == nil {
dialer = net.Dial
}
var conn net.Conn
var err error
var proxyFormat bool = false
if useProxy {
if proxyIsSOCKS {
var socksCreds *proxy.Auth
if proxyCreds != nil {
socksCreds = &proxy.Auth{
User: proxyCreds.Username,
Password: proxyCreds.Password,
}
}
socksDialer, err := proxy.SOCKS5("tcp", fmt.Sprintf("%s:%d", proxyHost, proxyPort), socksCreds, proxy.Direct)
if err != nil {
return fmt.Errorf("error creating SOCKS dialer: %s", err.Error())
}
conn, err = socksDialer.Dial("tcp", fmt.Sprintf("%s:%d", req.DestHost, req.DestPort))
if err != nil {
return fmt.Errorf("error dialing host: %s", err.Error())
}
defer conn.Close()
} else {
conn, err = dialer("tcp", fmt.Sprintf("%s:%d", proxyHost, proxyPort))
if err != nil {
return fmt.Errorf("error dialing proxy: %s", err.Error())
}
defer conn.Close()
if req.DestUseTLS {
if err := PerformConnect(conn, req.DestHost, req.DestPort); err != nil {
return err
}
proxyFormat = false
} else {
proxyFormat = true
}
}
} else {
conn, err = dialer("tcp", fmt.Sprintf("%s:%d", req.DestHost, req.DestPort))
if err != nil {
return fmt.Errorf("error dialing host: %s", err.Error())
}
defer conn.Close()
}
if req.DestUseTLS {
tls_conn := tls.Client(conn, &tls.Config{
InsecureSkipVerify: true,
})
conn = tls_conn
}
if proxyFormat {
return req.SubmitProxy(conn, proxyCreds)
} else {
return req.Submit(conn)
}
}
func SubmitRequest(req *ProxyRequest) error {
return submitRequest(req, false, "", 0, nil, false)
}
func SubmitRequestProxy(req *ProxyRequest, proxyHost string, proxyPort int, creds *ProxyCredentials) error {
return submitRequest(req, true, proxyHost, proxyPort, creds, false)
}
func SubmitRequestSOCKSProxy(req *ProxyRequest, proxyHost string, proxyPort int, creds *ProxyCredentials) error {
return submitRequest(req, true, proxyHost, proxyPort, creds, true)
}