package connchecker import ( "net" "strings" "sync" "time" "github.com/thrasher-corp/gocryptotrader/log" ) // DefaultCheckInterval is a const that defines the amount of time between // checking if the connection is lost const ( DefaultCheckInterval = time.Second ConnRe = "Internet connectivity re-established" ConnLost = "Internet connectivity lost" ConnFound = "Internet connectivity found" ConnNotFound = "No internet connectivity" ) // Default check lists var ( DefaultDNSList = []string{"8.8.8.8", "8.8.4.4", "1.1.1.1", "1.0.0.1"} DefaultDomainList = []string{"www.google.com", "www.cloudflare.com", "www.facebook.com"} ) // New returns a new connection checker, if no values set it will default it out func New(dnsList, domainList []string, checkInterval time.Duration) (*Checker, error) { c := new(Checker) if len(dnsList) == 0 { c.DNSList = DefaultDNSList } else { c.DNSList = dnsList } if len(domainList) == 0 { c.DomainList = DefaultDomainList } else { c.DomainList = domainList } if checkInterval == 0 { c.CheckInterval = DefaultCheckInterval } else { c.CheckInterval = checkInterval } err := c.initialCheck() if err != nil { return nil, err } if c.connected { log.Debugln(log.Global, ConnFound) } else { log.Warnln(log.Global, ConnNotFound) } c.shutdown = make(chan struct{}, 1) var wg sync.WaitGroup wg.Add(1) go c.Monitor(&wg) wg.Wait() return c, nil } // Checker defines a struct to determine connectivity to the interwebs type Checker struct { DNSList []string DomainList []string CheckInterval time.Duration shutdown chan struct{} wg sync.WaitGroup connected bool sync.Mutex } // Shutdown cleanly shutsdown monitor routine func (c *Checker) Shutdown() { c.connected = false close(c.shutdown) c.wg.Wait() } // Monitor determines internet connectivity via a DNS lookup func (c *Checker) Monitor(wg *sync.WaitGroup) { c.wg.Add(1) tick := time.NewTicker(c.CheckInterval) defer func() { tick.Stop(); c.wg.Done() }() wg.Done() for { select { case <-tick.C: go c.connectionTest() case <-c.shutdown: return } } } // initialCheck starts an initial connection check func (c *Checker) initialCheck() error { var connected bool for i := range c.DNSList { err := c.CheckDNS(c.DNSList[i]) if err != nil { if strings.Contains(err.Error(), "unrecognized address") || strings.Contains(err.Error(), "invalid address") { return err } continue } if !connected { connected = true } } for i := range c.DomainList { err := c.CheckHost(c.DomainList[i]) if err != nil { continue } if !connected { connected = true } } c.connected = connected return nil } // ConnectionTest determines if a connection to the internet is available by // iterating over a set list of dns ip and popular domains func (c *Checker) connectionTest() { for i := range c.DNSList { err := c.CheckDNS(c.DNSList[i]) if err == nil { c.Lock() if !c.connected { log.Debugln(log.Global, ConnRe) c.connected = true } c.Unlock() return } } for i := range c.DomainList { err := c.CheckHost(c.DomainList[i]) if err == nil { c.Lock() if !c.connected { log.Debugln(log.Global, ConnRe) c.connected = true } c.Unlock() return } } c.Lock() if c.connected { log.Warnln(log.Global, ConnLost) c.connected = false } c.Unlock() } // CheckDNS checks current dns for connectivity func (c *Checker) CheckDNS(dns string) error { _, err := net.LookupAddr(dns) return err } // CheckHost checks current host name for connectivity func (c *Checker) CheckHost(host string) error { _, err := net.LookupHost(host) return err } // IsConnected returns if there is internet connectivity func (c *Checker) IsConnected() bool { c.Lock() isConnected := c.connected c.Unlock() return isConnected }