| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133 |
- /*
- * Copyright (c) 2015, Psiphon Inc.
- * All rights reserved.
- *
- * This program 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.
- *
- * This program 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 this program. If not, see <http://www.gnu.org/licenses/>.
- *
- */
- package psiphon
- import (
- "compress/zlib"
- "io/ioutil"
- "os"
- "strings"
- "time"
- )
- // FetchRemoteServerList downloads a remote server list JSON record from
- // config.RemoteServerListUrl; validates its digital signature using the
- // public key config.RemoteServerListSignaturePublicKey; and parses the
- // data field into ServerEntry records.
- func FetchRemoteServerList(
- config *Config,
- tunnel *Tunnel,
- untunneledDialConfig *DialConfig) error {
- NoticeInfo("fetching remote server list")
- // Select tunneled or untunneled configuration
- httpClient, requestUrl, err := MakeDownloadHttpClient(
- config,
- tunnel,
- untunneledDialConfig,
- config.RemoteServerListUrl,
- time.Duration(*config.FetchRemoteServerListTimeoutSeconds)*time.Second)
- if err != nil {
- return ContextError(err)
- }
- // Proceed with download
- downloadFilename := config.RemoteServerListDownloadFilename
- if downloadFilename == "" {
- splitPath := strings.Split(config.RemoteServerListUrl, "/")
- downloadFilename = splitPath[len(splitPath)-1]
- }
- lastETag, err := GetUrlETag(config.RemoteServerListUrl)
- if err != nil {
- return ContextError(err)
- }
- n, responseETag, err := ResumeDownload(
- httpClient, requestUrl, downloadFilename, lastETag)
- NoticeRemoteServerListDownloadedBytes(n)
- if err != nil {
- return ContextError(err)
- }
- if responseETag == lastETag {
- // The remote server list is unchanged and no data was downloaded
- return nil
- }
- NoticeRemoteServerListDownloaded(downloadFilename)
- // The downloaded content is a zlib compressed authenticated
- // data package containing a list of encoded server entries.
- downloadContent, err := os.Open(downloadFilename)
- if err != nil {
- return ContextError(err)
- }
- defer downloadContent.Close()
- zlibReader, err := zlib.NewReader(downloadContent)
- if err != nil {
- return ContextError(err)
- }
- dataPackage, err := ioutil.ReadAll(zlibReader)
- zlibReader.Close()
- if err != nil {
- return ContextError(err)
- }
- remoteServerList, err := ReadAuthenticatedDataPackage(
- dataPackage, config.RemoteServerListSignaturePublicKey)
- if err != nil {
- return ContextError(err)
- }
- serverEntries, err := DecodeAndValidateServerEntryList(
- remoteServerList,
- GetCurrentTimestamp(),
- SERVER_ENTRY_SOURCE_REMOTE)
- if err != nil {
- return ContextError(err)
- }
- err = StoreServerEntries(serverEntries, true)
- if err != nil {
- return ContextError(err)
- }
- // Now that the server entries are successfully imported, store the response
- // ETag so we won't re-download this same data again.
- if responseETag != "" {
- err := SetUrlETag(config.RemoteServerListUrl, responseETag)
- if err != nil {
- NoticeAlert("failed to set remote server list ETag: %s", ContextError(err))
- // This fetch is still reported as a success, even if we can't store the etag
- }
- }
- return nil
- }
|