code.gitea.io/gitea@v1.22.3/modules/hostmatcher/http.go (about) 1 // Copyright 2021 The Gitea Authors. All rights reserved. 2 // SPDX-License-Identifier: MIT 3 4 package hostmatcher 5 6 import ( 7 "context" 8 "fmt" 9 "net" 10 "net/url" 11 "syscall" 12 "time" 13 ) 14 15 // NewDialContext returns a DialContext for Transport, the DialContext will do allow/block list check 16 func NewDialContext(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 30 Control: func(network, ipAddr string, c syscall.RawConn) error { 31 host, port, err := net.SplitHostPort(addrOrHost) 32 if err != nil { 33 return err 34 } 35 if proxy != nil { 36 // Always allow the host of the proxy, but only on the specified port. 37 if host == proxy.Hostname() && port == proxy.Port() { 38 return nil 39 } 40 } 41 42 // in Control func, the addr was already resolved to IP:PORT format, there is no cost to do ResolveTCPAddr here 43 tcpAddr, err := net.ResolveTCPAddr(network, ipAddr) 44 if err != nil { 45 return fmt.Errorf("%s can only call HTTP servers via TCP, deny '%s(%s:%s)', err=%w", usage, host, network, ipAddr, err) 46 } 47 48 var blockedError error 49 if blockList.MatchHostOrIP(host, tcpAddr.IP) { 50 blockedError = fmt.Errorf("%s can not call blocked HTTP servers (check your %s setting), deny '%s(%s)'", usage, blockList.SettingKeyHint, host, ipAddr) 51 } 52 53 // if we have an allow-list, check the allow-list first 54 if !allowList.IsEmpty() { 55 if !allowList.MatchHostOrIP(host, tcpAddr.IP) { 56 return fmt.Errorf("%s can only call allowed HTTP servers (check your %s setting), deny '%s(%s)'", usage, allowList.SettingKeyHint, host, ipAddr) 57 } 58 } 59 // otherwise, we always follow the blocked list 60 return blockedError 61 }, 62 } 63 return dialer.DialContext(ctx, network, addrOrHost) 64 } 65 }