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.

http.go 2.6KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869
  1. // Copyright 2021 The Gitea Authors. All rights reserved.
  2. // SPDX-License-Identifier: MIT
  3. package hostmatcher
  4. import (
  5. "context"
  6. "fmt"
  7. "net"
  8. "net/url"
  9. "syscall"
  10. "time"
  11. )
  12. // NewDialContext returns a DialContext for Transport, the DialContext will do allow/block list check
  13. func NewDialContext(usage string, allowList, blockList *HostMatchList) func(ctx context.Context, network, addr string) (net.Conn, error) {
  14. return NewDialContextWithProxy(usage, allowList, blockList, nil)
  15. }
  16. func NewDialContextWithProxy(usage string, allowList, blockList *HostMatchList, proxy *url.URL) func(ctx context.Context, network, addr string) (net.Conn, error) {
  17. // How Go HTTP Client works with redirection:
  18. // transport.RoundTrip URL=http://domain.com, Host=domain.com
  19. // transport.DialContext addrOrHost=domain.com:80
  20. // dialer.Control tcp4:11.22.33.44:80
  21. // transport.RoundTrip URL=http://www.domain.com/, Host=(empty here, in the direction, HTTP client doesn't fill the Host field)
  22. // transport.DialContext addrOrHost=domain.com:80
  23. // dialer.Control tcp4:11.22.33.44:80
  24. return func(ctx context.Context, network, addrOrHost string) (net.Conn, error) {
  25. dialer := net.Dialer{
  26. // default values comes from http.DefaultTransport
  27. Timeout: 30 * time.Second,
  28. KeepAlive: 30 * time.Second,
  29. Control: func(network, ipAddr string, c syscall.RawConn) error {
  30. host, port, err := net.SplitHostPort(addrOrHost)
  31. if err != nil {
  32. return err
  33. }
  34. if proxy != nil {
  35. // Always allow the host of the proxy, but only on the specified port.
  36. if host == proxy.Hostname() && port == proxy.Port() {
  37. return nil
  38. }
  39. }
  40. // in Control func, the addr was already resolved to IP:PORT format, there is no cost to do ResolveTCPAddr here
  41. tcpAddr, err := net.ResolveTCPAddr(network, ipAddr)
  42. if err != nil {
  43. return fmt.Errorf("%s can only call HTTP servers via TCP, deny '%s(%s:%s)', err=%w", usage, host, network, ipAddr, err)
  44. }
  45. var blockedError error
  46. if blockList.MatchHostOrIP(host, tcpAddr.IP) {
  47. blockedError = fmt.Errorf("%s can not call blocked HTTP servers (check your %s setting), deny '%s(%s)'", usage, blockList.SettingKeyHint, host, ipAddr)
  48. }
  49. // if we have an allow-list, check the allow-list first
  50. if !allowList.IsEmpty() {
  51. if !allowList.MatchHostOrIP(host, tcpAddr.IP) {
  52. return fmt.Errorf("%s can only call allowed HTTP servers (check your %s setting), deny '%s(%s)'", usage, allowList.SettingKeyHint, host, ipAddr)
  53. }
  54. }
  55. // otherwise, we always follow the blocked list
  56. return blockedError
  57. },
  58. }
  59. return dialer.DialContext(ctx, network, addrOrHost)
  60. }
  61. }