123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869 |
- // Copyright 2021 The Gitea Authors. All rights reserved.
- // SPDX-License-Identifier: MIT
-
- package hostmatcher
-
- import (
- "context"
- "fmt"
- "net"
- "net/url"
- "syscall"
- "time"
- )
-
- // NewDialContext returns a DialContext for Transport, the DialContext will do allow/block list check
- func NewDialContext(usage string, allowList, blockList *HostMatchList) func(ctx context.Context, network, addr string) (net.Conn, error) {
- return NewDialContextWithProxy(usage, allowList, blockList, nil)
- }
-
- func NewDialContextWithProxy(usage string, allowList, blockList *HostMatchList, proxy *url.URL) func(ctx context.Context, network, addr string) (net.Conn, error) {
- // How Go HTTP Client works with redirection:
- // transport.RoundTrip URL=http://domain.com, Host=domain.com
- // transport.DialContext addrOrHost=domain.com:80
- // dialer.Control tcp4:11.22.33.44:80
- // transport.RoundTrip URL=http://www.domain.com/, Host=(empty here, in the direction, HTTP client doesn't fill the Host field)
- // transport.DialContext addrOrHost=domain.com:80
- // dialer.Control tcp4:11.22.33.44:80
- return func(ctx context.Context, network, addrOrHost string) (net.Conn, error) {
- dialer := net.Dialer{
- // default values comes from http.DefaultTransport
- Timeout: 30 * time.Second,
- KeepAlive: 30 * time.Second,
-
- Control: func(network, ipAddr string, c syscall.RawConn) error {
- host, port, err := net.SplitHostPort(addrOrHost)
- if err != nil {
- return err
- }
- if proxy != nil {
- // Always allow the host of the proxy, but only on the specified port.
- if host == proxy.Hostname() && port == proxy.Port() {
- return nil
- }
- }
-
- // in Control func, the addr was already resolved to IP:PORT format, there is no cost to do ResolveTCPAddr here
- tcpAddr, err := net.ResolveTCPAddr(network, ipAddr)
- if err != nil {
- return fmt.Errorf("%s can only call HTTP servers via TCP, deny '%s(%s:%s)', err=%w", usage, host, network, ipAddr, err)
- }
-
- var blockedError error
- if blockList.MatchHostOrIP(host, tcpAddr.IP) {
- blockedError = fmt.Errorf("%s can not call blocked HTTP servers (check your %s setting), deny '%s(%s)'", usage, blockList.SettingKeyHint, host, ipAddr)
- }
-
- // if we have an allow-list, check the allow-list first
- if !allowList.IsEmpty() {
- if !allowList.MatchHostOrIP(host, tcpAddr.IP) {
- return fmt.Errorf("%s can only call allowed HTTP servers (check your %s setting), deny '%s(%s)'", usage, allowList.SettingKeyHint, host, ipAddr)
- }
- }
- // otherwise, we always follow the blocked list
- return blockedError
- },
- }
- return dialer.DialContext(ctx, network, addrOrHost)
- }
- }
|