// Copyright (c) 2020 Proton Technologies AG // // This file is part of ProtonMail Bridge. // // ProtonMail Bridge is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation, either version 3 of the License, or // (at your option) any later version. // // ProtonMail Bridge is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with ProtonMail Bridge. If not, see . package connection import ( "errors" "fmt" "net/http" "github.com/ProtonMail/proton-bridge/pkg/config" "github.com/ProtonMail/proton-bridge/pkg/pmapi" ) // Errors for possible connection issues var ( ErrNoInternetConnection = errors.New("no internet connection") ErrCanNotReachAPI = errors.New("can not reach PM API") log = config.GetLogEntry("connection") //nolint[gochecknoglobals] ) // CheckInternetConnection does a check of API connection. It checks two of our endpoints in parallel. // One endpoint is part of the protonmail API, while the other is not. // This allows us to determine whether there is a problem with the connection itself or only a problem with our API. // Two errors can be returned, ErrNoInternetConnection or ErrCanNotReachAPI. func CheckInternetConnection() error { client := &http.Client{ Transport: pmapi.NewPMAPIPinning(pmapi.CurrentUserAgent).TransportWithPinning(), } // Do not cumulate timeouts, use goroutines. retStatus := make(chan error) retAPI := make(chan error) // Check protonstatus.com without SSL for performance reasons. vpn_status endpoint is fast and // returns only OK; this endpoint is not known by the public. We check the connection only. go checkConnection(client, "http://protonstatus.com/vpn_status", retStatus) // Check of API reachability also uses a fast endpoint. go checkConnection(client, pmapi.GlobalGetRootURL()+"/tests/ping", retAPI) errStatus := <-retStatus errAPI := <-retAPI if errStatus != nil { if errAPI != nil { log.Error("Checking internet connection failed with ", errStatus, " and ", errAPI) return ErrNoInternetConnection } log.Warning("API OK, but status: ", errStatus) return nil } if errAPI != nil { log.Error("Status OK, but API: ", errAPI) return ErrCanNotReachAPI } return nil } func checkConnection(client *http.Client, url string, errorChannel chan error) { resp, err := client.Get(url) if err != nil { errorChannel <- err return } _ = resp.Body.Close() if resp.StatusCode != 200 { errorChannel <- fmt.Errorf("HTTP status code %d", resp.StatusCode) return } errorChannel <- nil }