-
Notifications
You must be signed in to change notification settings - Fork 4
Expand file tree
/
Copy pathhttpserver.go
More file actions
159 lines (124 loc) · 4.92 KB
/
httpserver.go
File metadata and controls
159 lines (124 loc) · 4.92 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
package main
import (
"bufio"
"context"
"fmt"
"net"
"net/http"
)
type httpHandler struct {
table string
}
func (h httpHandler) String() string {
return fmt.Sprintf("HTTP[%s]", h.table)
}
// connHandle handles the connection of a client on the input HTTP CONNECT listener.
// It parses the CONNECT request, establishes a connection to the requested host through the right chain (found in routingtable table),
// transfers data between the established connecion socket and the clien socket, and finally closes evetything on errors or at the end.
// Takes a context with its cancel function, and calls it before returning (also closes client)
func (h httpHandler) connHandle(client net.Conn, ctx context.Context, cancel context.CancelFunc) {
gMetaLogger.Debugf("Entering httpHandler connHandle for connection (c[%%+v]: %+v, c(%%p): %p &c(%%v): %v) accepted", client, client, &client)
defer func() {
gMetaLogger.Debugf("Leaving httpHandler connHandle for connection (c[%%+v]: %+v, c(%%p): %p &c(%%v): %v) accepted", client, client, &client)
}()
defer client.Close()
defer cancel()
// ***** BEGIN HTTP CONNECT input parsing *****
// Parse CONNECT request to retrieve target host and target port
reader := bufio.NewReader(client)
request, err := http.ReadRequest(reader)
if err != nil {
gMetaLogger.Error(err)
return
}
gMetaLogger.Debug(request)
gMetaLogger.Debugf("METHOD: %v\nURL: %v", request.Method, request.URL.Host)
if request.Method != "CONNECT" {
gMetaLogger.Errorf("only HTTP CONNECT method is supported")
resp := &http.Response{StatusCode: 405, ProtoMajor: request.ProtoMajor, ProtoMinor: request.ProtoMinor}
resp.Write(client)
return
}
if request.Host != request.URL.Host {
gMetaLogger.Error("host and URL do not match")
resp := &http.Response{StatusCode: 400, ProtoMajor: request.ProtoMajor, ProtoMinor: request.ProtoMinor}
resp.Write(client)
return
}
addr := request.Host
// ***** END HTTP CONNECT input parsing *****
// ***** BEGIN Routing decision *****
var chainStr string
if gArgPACPath != "" {
// -pac flag defined, use PAC to find the chain
chainStr, err = getRouteWithPAC(addr)
if err != nil {
gMetaLogger.Errorf("error getting route PAC: %v", err)
resp := &http.Response{StatusCode: 400, ProtoMajor: request.ProtoMajor, ProtoMinor: request.ProtoMinor}
resp.Write(client)
return
}
} else {
// use JSON config to find the chain
gRoutingConf.mu.RLock()
table, ok := gRoutingConf.routing[h.table]
if !ok {
gMetaLogger.Errorf("table %v not defined in routing configuration", table)
resp := &http.Response{StatusCode: 400, ProtoMajor: request.ProtoMajor, ProtoMinor: request.ProtoMinor}
resp.Write(client)
gRoutingConf.mu.RUnlock()
return
}
chainStr, err = table.getRoute(addr)
gRoutingConf.mu.RUnlock()
if err != nil {
gMetaLogger.Errorf("error getting route with JSON conf: %v", err)
resp := &http.Response{StatusCode: 400, ProtoMajor: request.ProtoMajor, ProtoMinor: request.ProtoMinor}
resp.Write(client)
return
}
}
gMetaLogger.Debugf("chain to use for %v: %v\n", addr, chainStr)
if chainStr == "drop" {
gMetaLogger.Debugf("dropping connection to %v", addr)
gMetaLogger.Auditf("| %v\t| DROPPED\t| %v\t| %v\t| %v\n", h, client, chainStr, addr)
resp := &http.Response{StatusCode: 403, ProtoMajor: request.ProtoMajor, ProtoMinor: request.ProtoMinor}
resp.Write(client)
return
}
gChainsConf.mu.RLock()
chain, ok := gChainsConf.proxychains[chainStr]
gChainsConf.mu.RUnlock()
if !ok {
gMetaLogger.Errorf("chain '%v' returned by PAC script is not declared in configuration", chainStr)
resp := &http.Response{StatusCode: 500, ProtoMajor: request.ProtoMajor, ProtoMinor: request.ProtoMinor}
resp.Write(client)
return
}
// ***** END Routing decision *****
// ***** BEGIN Connection to target host *****
//Connect to chain
target, chainRepresentation, err := chain.connect(ctx, addr)
if err != nil {
gMetaLogger.Error(err)
gMetaLogger.Auditf("| %v\t| ERROR\t| %v\t| %v\t| %v\t| %v\n", h, client, chainStr, addr, chainRepresentation)
resp := &http.Response{StatusCode: 502, ProtoMajor: request.ProtoMajor, ProtoMinor: request.ProtoMinor}
resp.Write(client)
return
}
defer target.Close()
gMetaLogger.Debugf("Client %v connected to host %v through chain %v", client, addr, chainStr)
// Create auditing trace for connection opening and defering closing trace
gMetaLogger.Auditf("| %v\t| OPEN\t| %v\t| %v\t| %v\t| %v\n", h, client, chainStr, addr, chainRepresentation)
defer gMetaLogger.Auditf("| %v\t| CLOSE\t| %v\t| %v\t| %v\t| %v\n", h, client, chainStr, addr, chainRepresentation)
// Send HTTP Success with matching protocol version
resp := &http.Response{StatusCode: 200, ProtoMajor: request.ProtoMajor, ProtoMinor: request.ProtoMinor}
err = resp.Write(client)
if err != nil {
gMetaLogger.Error(err)
return
}
gMetaLogger.Debugf("sent HTTP success response")
// ***** END Connection to target host *****
relay(client, target)
}