dispatcher.go 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326
  1. package goproxy
  2. import (
  3. "bytes"
  4. "io/ioutil"
  5. "net"
  6. "net/http"
  7. "regexp"
  8. "strings"
  9. )
  10. // ReqCondition.HandleReq will decide whether or not to use the ReqHandler on an HTTP request
  11. // before sending it to the remote server
  12. type ReqCondition interface {
  13. RespCondition
  14. HandleReq(req *http.Request, ctx *ProxyCtx) bool
  15. }
  16. // RespCondition.HandleReq will decide whether or not to use the RespHandler on an HTTP response
  17. // before sending it to the proxy client. Note that resp might be nil, in case there was an
  18. // error sending the request.
  19. type RespCondition interface {
  20. HandleResp(resp *http.Response, ctx *ProxyCtx) bool
  21. }
  22. // ReqConditionFunc.HandleReq(req,ctx) <=> ReqConditionFunc(req,ctx)
  23. type ReqConditionFunc func(req *http.Request, ctx *ProxyCtx) bool
  24. // RespConditionFunc.HandleResp(resp,ctx) <=> RespConditionFunc(resp,ctx)
  25. type RespConditionFunc func(resp *http.Response, ctx *ProxyCtx) bool
  26. func (c ReqConditionFunc) HandleReq(req *http.Request, ctx *ProxyCtx) bool {
  27. return c(req, ctx)
  28. }
  29. // ReqConditionFunc cannot test responses. It only satisfies RespCondition interface so that
  30. // to be usable as RespCondition.
  31. func (c ReqConditionFunc) HandleResp(resp *http.Response, ctx *ProxyCtx) bool {
  32. return c(ctx.Req, ctx)
  33. }
  34. func (c RespConditionFunc) HandleResp(resp *http.Response, ctx *ProxyCtx) bool {
  35. return c(resp, ctx)
  36. }
  37. // UrlHasPrefix returns a ReqCondition checking wether the destination URL the proxy client has requested
  38. // has the given prefix, with or without the host.
  39. // For example UrlHasPrefix("host/x") will match requests of the form 'GET host/x', and will match
  40. // requests to url 'http://host/x'
  41. func UrlHasPrefix(prefix string) ReqConditionFunc {
  42. return func(req *http.Request, ctx *ProxyCtx) bool {
  43. return strings.HasPrefix(req.URL.Path, prefix) ||
  44. strings.HasPrefix(req.URL.Host+req.URL.Path, prefix) ||
  45. strings.HasPrefix(req.URL.Scheme+req.URL.Host+req.URL.Path, prefix)
  46. }
  47. }
  48. // UrlIs returns a ReqCondition, testing whether or not the request URL is one of the given strings
  49. // with or without the host prefix.
  50. // UrlIs("google.com/","foo") will match requests 'GET /' to 'google.com', requests `'GET google.com/' to
  51. // any host, and requests of the form 'GET foo'.
  52. func UrlIs(urls ...string) ReqConditionFunc {
  53. urlSet := make(map[string]bool)
  54. for _, u := range urls {
  55. urlSet[u] = true
  56. }
  57. return func(req *http.Request, ctx *ProxyCtx) bool {
  58. _, pathOk := urlSet[req.URL.Path]
  59. _, hostAndOk := urlSet[req.URL.Host+req.URL.Path]
  60. return pathOk || hostAndOk
  61. }
  62. }
  63. // ReqHostMatches returns a ReqCondition, testing whether the host to which the request was directed to matches
  64. // any of the given regular expressions.
  65. func ReqHostMatches(regexps ...*regexp.Regexp) ReqConditionFunc {
  66. return func(req *http.Request, ctx *ProxyCtx) bool {
  67. for _, re := range regexps {
  68. if re.MatchString(req.Host) {
  69. return true
  70. }
  71. }
  72. return false
  73. }
  74. }
  75. // ReqHostIs returns a ReqCondition, testing whether the host to which the request is directed to equal
  76. // to one of the given strings
  77. func ReqHostIs(hosts ...string) ReqConditionFunc {
  78. hostSet := make(map[string]bool)
  79. for _, h := range hosts {
  80. hostSet[h] = true
  81. }
  82. return func(req *http.Request, ctx *ProxyCtx) bool {
  83. _, ok := hostSet[req.URL.Host]
  84. return ok
  85. }
  86. }
  87. var localHostIpv4 = regexp.MustCompile(`127\.0\.0\.\d+`)
  88. // IsLocalHost checks whether the destination host is explicitly local host
  89. // (buggy, there can be IPv6 addresses it doesn't catch)
  90. var IsLocalHost ReqConditionFunc = func(req *http.Request, ctx *ProxyCtx) bool {
  91. return req.URL.Host == "::1" ||
  92. req.URL.Host == "0:0:0:0:0:0:0:1" ||
  93. localHostIpv4.MatchString(req.URL.Host) ||
  94. req.URL.Host == "localhost"
  95. }
  96. // UrlMatches returns a ReqCondition testing whether the destination URL
  97. // of the request matches the given regexp, with or without prefix
  98. func UrlMatches(re *regexp.Regexp) ReqConditionFunc {
  99. return func(req *http.Request, ctx *ProxyCtx) bool {
  100. return re.MatchString(req.URL.Path) ||
  101. re.MatchString(req.URL.Host+req.URL.Path)
  102. }
  103. }
  104. // DstHostIs returns a ReqCondition testing wether the host in the request url is the given string
  105. func DstHostIs(host string) ReqConditionFunc {
  106. return func(req *http.Request, ctx *ProxyCtx) bool {
  107. return req.URL.Host == host
  108. }
  109. }
  110. // SrcIpIs returns a ReqCondition testing whether the source IP of the request is one of the given strings
  111. func SrcIpIs(ips ...string) ReqCondition {
  112. return ReqConditionFunc(func(req *http.Request, ctx *ProxyCtx) bool {
  113. for _, ip := range ips {
  114. if strings.HasPrefix(req.RemoteAddr, ip+":") {
  115. return true
  116. }
  117. }
  118. return false
  119. })
  120. }
  121. // Not returns a ReqCondition negating the given ReqCondition
  122. func Not(r ReqCondition) ReqConditionFunc {
  123. return func(req *http.Request, ctx *ProxyCtx) bool {
  124. return !r.HandleReq(req, ctx)
  125. }
  126. }
  127. // ContentTypeIs returns a RespCondition testing whether the HTTP response has Content-Type header equal
  128. // to one of the given strings.
  129. func ContentTypeIs(typ string, types ...string) RespCondition {
  130. types = append(types, typ)
  131. return RespConditionFunc(func(resp *http.Response, ctx *ProxyCtx) bool {
  132. if resp == nil {
  133. return false
  134. }
  135. contentType := resp.Header.Get("Content-Type")
  136. for _, typ := range types {
  137. if contentType == typ || strings.HasPrefix(contentType, typ+";") {
  138. return true
  139. }
  140. }
  141. return false
  142. })
  143. }
  144. // ProxyHttpServer.OnRequest Will return a temporary ReqProxyConds struct, aggregating the given condtions.
  145. // You will use the ReqProxyConds struct to register a ReqHandler, that would filter
  146. // the request, only if all the given ReqCondition matched.
  147. // Typical usage:
  148. // proxy.OnRequest(UrlIs("example.com/foo"),UrlMatches(regexp.MustParse(`.*\.exampl.\com\./.*`)).Do(...)
  149. func (proxy *ProxyHttpServer) OnRequest(conds ...ReqCondition) *ReqProxyConds {
  150. return &ReqProxyConds{proxy, conds}
  151. }
  152. // ReqProxyConds aggregate ReqConditions for a ProxyHttpServer. Upon calling Do, it will register a ReqHandler that would
  153. // handle the request if all conditions on the HTTP request are met.
  154. type ReqProxyConds struct {
  155. proxy *ProxyHttpServer
  156. reqConds []ReqCondition
  157. }
  158. // DoFunc is equivalent to proxy.OnRequest().Do(FuncReqHandler(f))
  159. func (pcond *ReqProxyConds) DoFunc(f func(req *http.Request, ctx *ProxyCtx) (*http.Request, *http.Response)) {
  160. pcond.Do(FuncReqHandler(f))
  161. }
  162. // ReqProxyConds.Do will register the ReqHandler on the proxy,
  163. // the ReqHandler will handle the HTTP request if all the conditions
  164. // aggregated in the ReqProxyConds are met. Typical usage:
  165. // proxy.OnRequest().Do(handler) // will call handler.Handle(req,ctx) on every request to the proxy
  166. // proxy.OnRequest(cond1,cond2).Do(handler)
  167. // // given request to the proxy, will test if cond1.HandleReq(req,ctx) && cond2.HandleReq(req,ctx) are true
  168. // // if they are, will call handler.Handle(req,ctx)
  169. func (pcond *ReqProxyConds) Do(h ReqHandler) {
  170. pcond.proxy.reqHandlers = append(pcond.proxy.reqHandlers,
  171. FuncReqHandler(func(r *http.Request, ctx *ProxyCtx) (*http.Request, *http.Response) {
  172. for _, cond := range pcond.reqConds {
  173. if !cond.HandleReq(r, ctx) {
  174. return r, nil
  175. }
  176. }
  177. return h.Handle(r, ctx)
  178. }))
  179. }
  180. // HandleConnect is used when proxy receives an HTTP CONNECT request,
  181. // it'll then use the HttpsHandler to determine what should it
  182. // do with this request. The handler returns a ConnectAction struct, the Action field in the ConnectAction
  183. // struct returned will determine what to do with this request. ConnectAccept will simply accept the request
  184. // forwarding all bytes from the client to the remote host, ConnectReject will close the connection with the
  185. // client, and ConnectMitm, will assume the underlying connection is an HTTPS connection, and will use Man
  186. // in the Middle attack to eavesdrop the connection. All regular handler will be active on this eavesdropped
  187. // connection.
  188. // The ConnectAction struct contains possible tlsConfig that will be used for eavesdropping. If nil, the proxy
  189. // will use the default tls configuration.
  190. // proxy.OnRequest().HandleConnect(goproxy.AlwaysReject) // rejects all CONNECT requests
  191. func (pcond *ReqProxyConds) HandleConnect(h HttpsHandler) {
  192. pcond.proxy.httpsHandlers = append(pcond.proxy.httpsHandlers,
  193. FuncHttpsHandler(func(host string, ctx *ProxyCtx) (*ConnectAction, string) {
  194. for _, cond := range pcond.reqConds {
  195. if !cond.HandleReq(ctx.Req, ctx) {
  196. return nil, ""
  197. }
  198. }
  199. return h.HandleConnect(host, ctx)
  200. }))
  201. }
  202. // HandleConnectFunc is equivalent to HandleConnect,
  203. // for example, accepting CONNECT request if they contain a password in header
  204. // io.WriteString(h,password)
  205. // passHash := h.Sum(nil)
  206. // proxy.OnRequest().HandleConnectFunc(func(host string, ctx *ProxyCtx) (*ConnectAction, string) {
  207. // c := sha1.New()
  208. // io.WriteString(c,ctx.Req.Header.Get("X-GoProxy-Auth"))
  209. // if c.Sum(nil) == passHash {
  210. // return OkConnect, host
  211. // }
  212. // return RejectConnect, host
  213. // })
  214. func (pcond *ReqProxyConds) HandleConnectFunc(f func(host string, ctx *ProxyCtx) (*ConnectAction, string)) {
  215. pcond.HandleConnect(FuncHttpsHandler(f))
  216. }
  217. func (pcond *ReqProxyConds) HijackConnect(f func(req *http.Request, client net.Conn, ctx *ProxyCtx)) {
  218. pcond.proxy.httpsHandlers = append(pcond.proxy.httpsHandlers,
  219. FuncHttpsHandler(func(host string, ctx *ProxyCtx) (*ConnectAction, string) {
  220. for _, cond := range pcond.reqConds {
  221. if !cond.HandleReq(ctx.Req, ctx) {
  222. return nil, ""
  223. }
  224. }
  225. return &ConnectAction{Action: ConnectHijack, Hijack: f}, host
  226. }))
  227. }
  228. // ProxyConds is used to aggregate RespConditions for a ProxyHttpServer.
  229. // Upon calling ProxyConds.Do, it will register a RespHandler that would
  230. // handle the HTTP response from remote server if all conditions on the HTTP response are met.
  231. type ProxyConds struct {
  232. proxy *ProxyHttpServer
  233. reqConds []ReqCondition
  234. respCond []RespCondition
  235. }
  236. // ProxyConds.DoFunc is equivalent to proxy.OnResponse().Do(FuncRespHandler(f))
  237. func (pcond *ProxyConds) DoFunc(f func(resp *http.Response, ctx *ProxyCtx) *http.Response) {
  238. pcond.Do(FuncRespHandler(f))
  239. }
  240. // ProxyConds.Do will register the RespHandler on the proxy, h.Handle(resp,ctx) will be called on every
  241. // request that matches the conditions aggregated in pcond.
  242. func (pcond *ProxyConds) Do(h RespHandler) {
  243. pcond.proxy.respHandlers = append(pcond.proxy.respHandlers,
  244. FuncRespHandler(func(resp *http.Response, ctx *ProxyCtx) *http.Response {
  245. for _, cond := range pcond.reqConds {
  246. if !cond.HandleReq(ctx.Req, ctx) {
  247. return resp
  248. }
  249. }
  250. for _, cond := range pcond.respCond {
  251. if !cond.HandleResp(resp, ctx) {
  252. return resp
  253. }
  254. }
  255. return h.Handle(resp, ctx)
  256. }))
  257. }
  258. // OnResponse is used when adding a response-filter to the HTTP proxy, usual pattern is
  259. // proxy.OnResponse(cond1,cond2).Do(handler) // handler.Handle(resp,ctx) will be used
  260. // // if cond1.HandleResp(resp) && cond2.HandleResp(resp)
  261. func (proxy *ProxyHttpServer) OnResponse(conds ...RespCondition) *ProxyConds {
  262. return &ProxyConds{proxy, make([]ReqCondition, 0), conds}
  263. }
  264. // AlwaysMitm is a HttpsHandler that always eavesdrop https connections, for example to
  265. // eavesdrop all https connections to www.google.com, we can use
  266. // proxy.OnRequest(goproxy.ReqHostIs("www.google.com")).HandleConnect(goproxy.AlwaysMitm)
  267. var AlwaysMitm FuncHttpsHandler = func(host string, ctx *ProxyCtx) (*ConnectAction, string) {
  268. return MitmConnect, host
  269. }
  270. // AlwaysReject is a HttpsHandler that drops any CONNECT request, for example, this code will disallow
  271. // connections to hosts on any other port than 443
  272. // proxy.OnRequest(goproxy.Not(goproxy.ReqHostMatches(regexp.MustCompile(":443$"))).
  273. // HandleConnect(goproxy.AlwaysReject)
  274. var AlwaysReject FuncHttpsHandler = func(host string, ctx *ProxyCtx) (*ConnectAction, string) {
  275. return RejectConnect, host
  276. }
  277. // HandleBytes will return a RespHandler that read the entire body of the request
  278. // to a byte array in memory, would run the user supplied f function on the byte arra,
  279. // and will replace the body of the original response with the resulting byte array.
  280. func HandleBytes(f func(b []byte, ctx *ProxyCtx) []byte) RespHandler {
  281. return FuncRespHandler(func(resp *http.Response, ctx *ProxyCtx) *http.Response {
  282. b, err := ioutil.ReadAll(resp.Body)
  283. if err != nil {
  284. ctx.Warnf("Cannot read response %s", err)
  285. return resp
  286. }
  287. resp.Body.Close()
  288. resp.Body = ioutil.NopCloser(bytes.NewBuffer(f(b, ctx)))
  289. return resp
  290. })
  291. }