Path: blob/dev/pkg/protocols/common/protocolstate/state.go
2072 views
package protocolstate12import (3"context"4"fmt"5"net"6"net/url"78"github.com/go-sql-driver/mysql"9"github.com/pkg/errors"10"golang.org/x/net/proxy"1112"github.com/projectdiscovery/fastdialer/fastdialer"13"github.com/projectdiscovery/mapcidr/asn"14"github.com/projectdiscovery/networkpolicy"15"github.com/projectdiscovery/nuclei/v3/pkg/types"16"github.com/projectdiscovery/nuclei/v3/pkg/utils/expand"17"github.com/projectdiscovery/retryablehttp-go"18mapsutil "github.com/projectdiscovery/utils/maps"19)2021var (22dialers *mapsutil.SyncLockMap[string, *Dialers]23)2425func init() {26dialers = mapsutil.NewSyncLockMap[string, *Dialers]()27}2829func GetDialers(ctx context.Context) *Dialers {30executionContext := GetExecutionContext(ctx)31dialers, ok := dialers.Get(executionContext.ExecutionID)32if !ok {33return nil34}35return dialers36}3738func GetDialersWithId(id string) *Dialers {39dialers, ok := dialers.Get(id)40if !ok {41return nil42}43return dialers44}4546func ShouldInit(id string) bool {47dialer, ok := dialers.Get(id)48if !ok {49return true50}51return dialer == nil52}5354// Init creates the Dialers instance based on user configuration55func Init(options *types.Options) error {56if GetDialersWithId(options.ExecutionId) != nil {57return nil58}5960return initDialers(options)61}6263// initDialers is the internal implementation of Init64func initDialers(options *types.Options) error {65opts := fastdialer.DefaultOptions66opts.DialerTimeout = options.GetTimeouts().DialTimeout67if options.DialerKeepAlive > 0 {68opts.DialerKeepAlive = options.DialerKeepAlive69}7071var expandedDenyList []string72for _, excludeTarget := range options.ExcludeTargets {73switch {74case asn.IsASN(excludeTarget):75expandedDenyList = append(expandedDenyList, expand.ASN(excludeTarget)...)76default:77expandedDenyList = append(expandedDenyList, excludeTarget)78}79}8081if options.RestrictLocalNetworkAccess {82expandedDenyList = append(expandedDenyList, networkpolicy.DefaultIPv4DenylistRanges...)83expandedDenyList = append(expandedDenyList, networkpolicy.DefaultIPv6DenylistRanges...)84}85npOptions := &networkpolicy.Options{86DenyList: expandedDenyList,87}88opts.WithNetworkPolicyOptions = npOptions8990switch {91case options.SourceIP != "" && options.Interface != "":92isAssociated, err := isIpAssociatedWithInterface(options.SourceIP, options.Interface)93if err != nil {94return err95}96if isAssociated {97opts.Dialer = &net.Dialer{98LocalAddr: &net.TCPAddr{99IP: net.ParseIP(options.SourceIP),100},101}102} else {103return fmt.Errorf("source ip (%s) is not associated with the interface (%s)", options.SourceIP, options.Interface)104}105case options.SourceIP != "":106isAssociated, err := isIpAssociatedWithInterface(options.SourceIP, "any")107if err != nil {108return err109}110if isAssociated {111opts.Dialer = &net.Dialer{112LocalAddr: &net.TCPAddr{113IP: net.ParseIP(options.SourceIP),114},115}116} else {117return fmt.Errorf("source ip (%s) is not associated with any network interface", options.SourceIP)118}119case options.Interface != "":120ifadrr, err := interfaceAddress(options.Interface)121if err != nil {122return err123}124opts.Dialer = &net.Dialer{125LocalAddr: &net.TCPAddr{126IP: ifadrr,127},128}129}130if options.AliveSocksProxy != "" {131proxyURL, err := url.Parse(options.AliveSocksProxy)132if err != nil {133return err134}135var forward *net.Dialer136if opts.Dialer != nil {137forward = opts.Dialer138} else {139forward = &net.Dialer{140Timeout: opts.DialerTimeout,141KeepAlive: opts.DialerKeepAlive,142DualStack: true,143}144}145dialer, err := proxy.FromURL(proxyURL, forward)146if err != nil {147return err148}149opts.ProxyDialer = &dialer150}151152if options.SystemResolvers {153opts.ResolversFile = true154opts.EnableFallback = true155}156if len(options.InternalResolversList) > 0 {157opts.BaseResolvers = options.InternalResolversList158}159160opts.Deny = append(opts.Deny, expandedDenyList...)161162opts.WithDialerHistory = true163opts.SNIName = options.SNI164// this instance is used in javascript protocol libraries and165// dial history is required to get dialed ip of a host166opts.WithDialerHistory = true167168// fastdialer now by default fallbacks to ztls when there are tls related errors169dialer, err := fastdialer.NewDialer(opts)170if err != nil {171return errors.Wrap(err, "could not create dialer")172}173174networkPolicy, _ := networkpolicy.New(*npOptions)175176dialersInstance := &Dialers{177Fastdialer: dialer,178NetworkPolicy: networkPolicy,179HTTPClientPool: mapsutil.NewSyncLockMap[string, *retryablehttp.Client](),180LocalFileAccessAllowed: options.AllowLocalFileAccess,181}182183_ = dialers.Set(options.ExecutionId, dialersInstance)184185// Set a custom dialer for the "nucleitcp" protocol. This is just plain TCP, but it's registered186// with a different name so that we do not clobber the "tcp" dialer in the event that nuclei is187// being included as a package in another application.188mysql.RegisterDialContext("nucleitcp", func(ctx context.Context, addr string) (net.Conn, error) {189// Because we're not using the default TCP workflow, quietly add the default port190// number if no port number was specified.191if _, _, err := net.SplitHostPort(addr); err != nil {192addr += ":3306"193}194195executionId := ctx.Value("executionId").(string)196dialer := GetDialersWithId(executionId)197return dialer.Fastdialer.Dial(ctx, "tcp", addr)198})199200StartActiveMemGuardian(context.Background())201202SetLfaAllowed(options)203204return nil205}206207// isIpAssociatedWithInterface checks if the given IP is associated with the given interface.208func isIpAssociatedWithInterface(sourceIP, interfaceName string) (bool, error) {209addrs, err := interfaceAddresses(interfaceName)210if err != nil {211return false, err212}213for _, addr := range addrs {214if ipnet, ok := addr.(*net.IPNet); ok {215if ipnet.IP.String() == sourceIP {216return true, nil217}218}219}220return false, nil221}222223// interfaceAddress returns the first IPv4 address of the given interface.224func interfaceAddress(interfaceName string) (net.IP, error) {225addrs, err := interfaceAddresses(interfaceName)226if err != nil {227return nil, err228}229var address net.IP230for _, addr := range addrs {231if ipnet, ok := addr.(*net.IPNet); ok && !ipnet.IP.IsLoopback() {232if ipnet.IP.To4() != nil {233address = ipnet.IP234break235}236}237}238if address == nil {239return nil, fmt.Errorf("no suitable address found for interface: `%s`", interfaceName)240}241return address, nil242}243244// interfaceAddresses returns all interface addresses.245func interfaceAddresses(interfaceName string) ([]net.Addr, error) {246if interfaceName == "any" {247return net.InterfaceAddrs()248}249ief, err := net.InterfaceByName(interfaceName)250if err != nil {251return nil, errors.Wrapf(err, "failed to get interface: `%s`", interfaceName)252}253addrs, err := ief.Addrs()254if err != nil {255return nil, errors.Wrapf(err, "failed to get interface addresses for: `%s`", interfaceName)256}257return addrs, nil258}259260// Close closes the global shared fastdialer261func Close(executionId string) {262dialersInstance, ok := dialers.Get(executionId)263if !ok {264return265}266267if dialersInstance != nil {268dialersInstance.Fastdialer.Close()269}270271dialers.Delete(executionId)272273if dialers.IsEmpty() {274StopActiveMemGuardian()275}276}277278279