github.com/projectdiscovery/nuclei/v2@v2.9.15/pkg/protocols/common/protocolstate/state.go (about)

     1  package protocolstate
     2  
     3  import (
     4  	"fmt"
     5  	"net"
     6  	"net/url"
     7  
     8  	"github.com/pkg/errors"
     9  	"golang.org/x/net/proxy"
    10  
    11  	"github.com/projectdiscovery/fastdialer/fastdialer"
    12  	"github.com/projectdiscovery/networkpolicy"
    13  	"github.com/projectdiscovery/nuclei/v2/pkg/types"
    14  )
    15  
    16  // Dialer is a shared fastdialer instance for host DNS resolution
    17  var Dialer *fastdialer.Dialer
    18  
    19  // Init creates the Dialer instance based on user configuration
    20  func Init(options *types.Options) error {
    21  	if Dialer != nil {
    22  		return nil
    23  	}
    24  	opts := fastdialer.DefaultOptions
    25  	InitHeadless(options.RestrictLocalNetworkAccess, options.AllowLocalFileAccess)
    26  
    27  	switch {
    28  	case options.SourceIP != "" && options.Interface != "":
    29  		isAssociated, err := isIpAssociatedWithInterface(options.SourceIP, options.Interface)
    30  		if err != nil {
    31  			return err
    32  		}
    33  		if isAssociated {
    34  			opts.Dialer = &net.Dialer{
    35  				LocalAddr: &net.TCPAddr{
    36  					IP: net.ParseIP(options.SourceIP),
    37  				},
    38  			}
    39  		} else {
    40  			return fmt.Errorf("source ip (%s) is not associated with the interface (%s)", options.SourceIP, options.Interface)
    41  		}
    42  	case options.SourceIP != "":
    43  		isAssociated, err := isIpAssociatedWithInterface(options.SourceIP, "any")
    44  		if err != nil {
    45  			return err
    46  		}
    47  		if isAssociated {
    48  			opts.Dialer = &net.Dialer{
    49  				LocalAddr: &net.TCPAddr{
    50  					IP: net.ParseIP(options.SourceIP),
    51  				},
    52  			}
    53  		} else {
    54  			return fmt.Errorf("source ip (%s) is not associated with any network interface", options.SourceIP)
    55  		}
    56  	case options.Interface != "":
    57  		ifadrr, err := interfaceAddress(options.Interface)
    58  		if err != nil {
    59  			return err
    60  		}
    61  		opts.Dialer = &net.Dialer{
    62  			LocalAddr: &net.TCPAddr{
    63  				IP: ifadrr,
    64  			},
    65  		}
    66  	}
    67  	if types.ProxySocksURL != "" {
    68  		proxyURL, err := url.Parse(types.ProxySocksURL)
    69  		if err != nil {
    70  			return err
    71  		}
    72  		var forward *net.Dialer
    73  		if opts.Dialer != nil {
    74  			forward = opts.Dialer
    75  		} else {
    76  			forward = &net.Dialer{
    77  				Timeout:   opts.DialerTimeout,
    78  				KeepAlive: opts.DialerKeepAlive,
    79  				DualStack: true,
    80  			}
    81  		}
    82  		dialer, err := proxy.FromURL(proxyURL, forward)
    83  		if err != nil {
    84  			return err
    85  		}
    86  		opts.ProxyDialer = &dialer
    87  	}
    88  
    89  	if options.SystemResolvers {
    90  		opts.EnableFallback = true
    91  	}
    92  	if options.ResolversFile != "" {
    93  		opts.BaseResolvers = options.InternalResolversList
    94  	}
    95  	if options.RestrictLocalNetworkAccess {
    96  		opts.Deny = append(networkpolicy.DefaultIPv4DenylistRanges, networkpolicy.DefaultIPv6DenylistRanges...)
    97  	}
    98  	opts.WithDialerHistory = true
    99  	opts.SNIName = options.SNI
   100  	// fastdialer now by default fallbacks to ztls when there are tls related errors
   101  	dialer, err := fastdialer.NewDialer(opts)
   102  	if err != nil {
   103  		return errors.Wrap(err, "could not create dialer")
   104  	}
   105  	Dialer = dialer
   106  	return nil
   107  }
   108  
   109  // isIpAssociatedWithInterface checks if the given IP is associated with the given interface.
   110  func isIpAssociatedWithInterface(sourceIP, interfaceName string) (bool, error) {
   111  	addrs, err := interfaceAddresses(interfaceName)
   112  	if err != nil {
   113  		return false, err
   114  	}
   115  	for _, addr := range addrs {
   116  		if ipnet, ok := addr.(*net.IPNet); ok {
   117  			if ipnet.IP.String() == sourceIP {
   118  				return true, nil
   119  			}
   120  		}
   121  	}
   122  	return false, nil
   123  }
   124  
   125  // interfaceAddress returns the first IPv4 address of the given interface.
   126  func interfaceAddress(interfaceName string) (net.IP, error) {
   127  	addrs, err := interfaceAddresses(interfaceName)
   128  	if err != nil {
   129  		return nil, err
   130  	}
   131  	var address net.IP
   132  	for _, addr := range addrs {
   133  		if ipnet, ok := addr.(*net.IPNet); ok && !ipnet.IP.IsLoopback() {
   134  			if ipnet.IP.To4() != nil {
   135  				address = ipnet.IP
   136  			}
   137  		}
   138  	}
   139  	if address == nil {
   140  		return nil, fmt.Errorf("no suitable address found for interface: `%s`", interfaceName)
   141  	}
   142  	return address, nil
   143  }
   144  
   145  // interfaceAddresses returns all interface addresses.
   146  func interfaceAddresses(interfaceName string) ([]net.Addr, error) {
   147  	if interfaceName == "any" {
   148  		return net.InterfaceAddrs()
   149  	}
   150  	ief, err := net.InterfaceByName(interfaceName)
   151  	if err != nil {
   152  		return nil, errors.Wrapf(err, "failed to get interface: `%s`", interfaceName)
   153  	}
   154  	addrs, err := ief.Addrs()
   155  	if err != nil {
   156  		return nil, errors.Wrapf(err, "failed to get interface addresses for: `%s`", interfaceName)
   157  	}
   158  	return addrs, nil
   159  }
   160  
   161  // Close closes the global shared fastdialer
   162  func Close() {
   163  	if Dialer != nil {
   164  		Dialer.Close()
   165  	}
   166  }