github.com/tommi2day/gomodules@v1.13.2-0.20240423190010-b7d55d252a27/dblib/tns_dns.go (about)

     1  package dblib
     2  
     3  import (
     4  	"fmt"
     5  	"net"
     6  	"strings"
     7  
     8  	log "github.com/sirupsen/logrus"
     9  	"github.com/tommi2day/gomodules/netlib"
    10  	"gopkg.in/ini.v1"
    11  )
    12  
    13  // ServiceEntryType holds  host/ip/port of a tns address section
    14  type ServiceEntryType struct {
    15  	Host    string
    16  	IP      string
    17  	Port    string
    18  	Address string
    19  }
    20  
    21  // ServiceEntries List of Map of service entries
    22  type ServiceEntries []ServiceEntryType
    23  
    24  // IgnoreDNSLookup if true, no oracle-dns lookup is done
    25  var IgnoreDNSLookup = false
    26  
    27  // IPv4Only if true, only IPv4 addresses are returned
    28  var IPv4Only = true
    29  
    30  // DNSConfig holds the netlib DNS object
    31  var DNSConfig *netlib.DNSconfig
    32  
    33  // GetRacAdresses reads racinfo.ini or DNS SRV and returns all IP addresses for given rac
    34  func GetRacAdresses(rachost string, racini string) (services ServiceEntries) {
    35  	if racini != "" {
    36  		services = getRACAddressesFromRacInfo(rachost, racini)
    37  		log.Debugf("add %d rac addresses for %s from file %s", len(services), rachost, racini)
    38  	}
    39  	if len(services) == 0 {
    40  		services = getRACAddressesFromDNSSrv(rachost)
    41  		log.Debugf("add %d rac addresses for %s from DNS SRV records", len(services), rachost)
    42  	}
    43  	return
    44  }
    45  
    46  // getRACAddressesFromDNSSrv returns a list of RAC IP addresses for given tnshost using DNS SRV lookup
    47  // SRV Entry format for rachost=rac.example.com:
    48  // _rac._tcp.example.com 10 5 80 racscan.example.com.
    49  // _rac._tcp.example.com 10 5 80 rac-vip1.example.com.
    50  // _rac._tcp.example.com 10 5 80 rac-vip2.example.com.
    51  func getRACAddressesFromDNSSrv(rachost string) (services ServiceEntries) {
    52  	if IgnoreDNSLookup {
    53  		log.Infof("DNSSrv: Skip SRV, Ignore DNS is set")
    54  		return
    55  	}
    56  	// check if host contains only digits
    57  	if netlib.IsValidIP(rachost) {
    58  		log.Debugf("DNSSrv: %s is an IP address, skip", rachost)
    59  		return
    60  	}
    61  
    62  	// configure resolver
    63  	if DNSConfig == nil {
    64  		// set default resolver
    65  		DNSConfig = netlib.NewResolver("", 0, false)
    66  	}
    67  	// split host and domain
    68  	domain := ""
    69  	parts := strings.Split(rachost, ".")
    70  	host := parts[0]
    71  	if len(parts) > 1 {
    72  		domain = strings.Join(parts[1:], ".")
    73  	}
    74  	// create SRV record query
    75  	srvList, err := DNSConfig.LookupSrv(host, domain)
    76  	if err != nil {
    77  		log.Warnf("DNSSrv: cannot resolve %s:%s", host, err)
    78  		return
    79  	}
    80  
    81  	// process returned addresses
    82  	for _, srv := range srvList {
    83  		host = srv.Target
    84  		// delete trailing dot
    85  		host = strings.TrimSuffix(host, ".")
    86  		port := srv.Port
    87  		services = append(services, getServiceList(host, fmt.Sprintf("%v", port))...)
    88  	}
    89  	log.Debugf("DNSSrv: Rac %s Add %d adresses", rachost, len(services))
    90  	return
    91  }
    92  
    93  // getRACAddressesFromRacInfo returns a list of RAC IP addresses from inifile (default: racinfo.ini)
    94  func getRACAddressesFromRacInfo(rachost string, filename string) (services ServiceEntries) {
    95  	cfg, err := ini.InsensitiveLoad(filename)
    96  	if err != nil {
    97  		log.Debugf("RacInfo: cannot Read %s:%s", filename, err)
    98  		return
    99  	}
   100  	// all keys are lowwer case
   101  	entries := cfg.Section(strings.ToLower(rachost)).Keys()
   102  	if len(entries) == 0 {
   103  		log.Debugf("RacInfo: no entries for %s in %s", rachost, filename)
   104  		return
   105  	}
   106  	for _, e := range entries {
   107  		if strings.HasPrefix(strings.ToLower(e.Name()), "vip") || strings.HasPrefix(strings.ToLower(e.Name()), "scan") {
   108  			a := e.Value()
   109  			host, port, err := net.SplitHostPort(a)
   110  			if err != nil {
   111  				log.Warnf("RacInfo: cannot parse %s:%s", a, err)
   112  				continue
   113  			}
   114  			services = append(services, getServiceList(host, fmt.Sprintf("%v", port))...)
   115  		}
   116  	}
   117  	log.Debugf("RacInfo: Rac %s Add %d adresses", rachost, len(services))
   118  	return
   119  }
   120  
   121  // getServiceList returns a list of IP addresses for given tnshost and port
   122  func getServiceList(host string, port string) (services ServiceEntries) {
   123  	// configure resolver
   124  	// configure resolver
   125  	if DNSConfig == nil {
   126  		// set default resolver
   127  		DNSConfig = netlib.NewResolver("", 0, false)
   128  	}
   129  
   130  	// set resolver network ip =ipv4+ipv6 or ip4 only
   131  	if IPv4Only {
   132  		DNSConfig.IPv4Only = true
   133  	}
   134  
   135  	ips, err := DNSConfig.LookupIP(host)
   136  	if err != nil || len(ips) == 0 {
   137  		if IgnoreDNSLookup {
   138  			log.Debugf("getServiceList: cannot resolve %s", host)
   139  			service := ServiceEntryType{Host: host, Port: port, IP: "", Address: fmt.Sprintf("%s:%v", host, port)}
   140  			services = append(services, service)
   141  			return
   142  		}
   143  		log.Warnf("cannot resolve %s, skipped", host)
   144  		return
   145  	}
   146  	for _, ip := range ips {
   147  		hostip := ip.String()
   148  		if IPv4Only && !netlib.IsIPv4(hostip) {
   149  			log.Debugf("getServiceList: skip non ipv4 %s", hostip)
   150  			continue
   151  		}
   152  		service := ServiceEntryType{Host: host, Port: port}
   153  		if len(hostip) > 0 {
   154  			service.IP = hostip
   155  			service.Address = fmt.Sprintf("%s:%v", hostip, port)
   156  		} else {
   157  			service.IP = ""
   158  			service.Address = fmt.Sprintf("%s:%v", host, port)
   159  		}
   160  		services = append(services, service)
   161  	}
   162  	log.Debugf("add %d Services for %s:%s", len(services), host, port)
   163  	return
   164  }