// doHandshakeRequest performs the handshake API request. The handshake // returns upgrade info, newly discovered server entries -- which are // stored -- and sponsor info (home pages, stat regexes). func (session *Session) doHandshakeRequest() error { extraParams := make([]*ExtraParam, 0) serverEntryIpAddresses, err := GetServerEntryIpAddresses() if err != nil { return ContextError(err) } // Submit a list of known servers -- this will be used for // discovery statistics. for _, ipAddress := range serverEntryIpAddresses { extraParams = append(extraParams, &ExtraParam{"known_server", ipAddress}) } url := session.buildRequestUrl("handshake", extraParams...) responseBody, err := session.doGetRequest(url) if err != nil { return ContextError(err) } // Skip legacy format lines and just parse the JSON config line configLinePrefix := []byte("Config: ") var configLine []byte for _, line := range bytes.Split(responseBody, []byte("\n")) { if bytes.HasPrefix(line, configLinePrefix) { configLine = line[len(configLinePrefix):] break } } if len(configLine) == 0 { return ContextError(errors.New("no config line found")) } // Note: // - 'preemptive_reconnect_lifetime_milliseconds' is currently unused // - 'ssh_session_id' is ignored; client session ID is used instead var handshakeConfig struct { Homepages []string `json:"homepages"` UpgradeClientVersion string `json:"upgrade_client_version"` PageViewRegexes []map[string]string `json:"page_view_regexes"` HttpsRequestRegexes []map[string]string `json:"https_request_regexes"` EncodedServerList []string `json:"encoded_server_list"` ClientRegion string `json:"client_region"` } err = json.Unmarshal(configLine, &handshakeConfig) if err != nil { return ContextError(err) } session.clientRegion = handshakeConfig.ClientRegion var decodedServerEntries []*ServerEntry // Store discovered server entries for _, encodedServerEntry := range handshakeConfig.EncodedServerList { serverEntry, err := DecodeServerEntry(encodedServerEntry) if err != nil { return ContextError(err) } err = ValidateServerEntry(serverEntry) if err != nil { // Skip this entry and continue with the next one continue } decodedServerEntries = append(decodedServerEntries, serverEntry) } // The reason we are storing the entire array of server entries at once rather // than one at a time is that some desirable side-effects get triggered by // StoreServerEntries that don't get triggered by StoreServerEntry. err = StoreServerEntries(decodedServerEntries, true) if err != nil { return ContextError(err) } // TODO: formally communicate the sponsor and upgrade info to an // outer client via some control interface. for _, homepage := range handshakeConfig.Homepages { NoticeHomepage(homepage) } if handshakeConfig.UpgradeClientVersion != "" { NoticeClientUpgradeAvailable(handshakeConfig.UpgradeClientVersion) } var regexpsNotices []string session.statsRegexps, regexpsNotices = transferstats.MakeRegexps( handshakeConfig.PageViewRegexes, handshakeConfig.HttpsRequestRegexes) for _, notice := range regexpsNotices { NoticeAlert(notice) } return nil }
// doHandshakeRequest performs the "handshake" API request. The handshake // returns upgrade info, newly discovered server entries -- which are // stored -- and sponsor info (home pages, stat regexes). func (serverContext *ServerContext) doHandshakeRequest() error { params := serverContext.getBaseParams() // *TODO*: this is obsolete? /* serverEntryIpAddresses, err := GetServerEntryIpAddresses() if err != nil { return common.ContextError(err) } // Submit a list of known servers -- this will be used for // discovery statistics. for _, ipAddress := range serverEntryIpAddresses { params = append(params, requestParam{"known_server", ipAddress}) } */ var response []byte if serverContext.psiphonHttpsClient == nil { request, err := makeSSHAPIRequestPayload(params) if err != nil { return common.ContextError(err) } response, err = serverContext.tunnel.SendAPIRequest( common.PSIPHON_API_HANDSHAKE_REQUEST_NAME, request) if err != nil { return common.ContextError(err) } } else { // Legacy web service API request responseBody, err := serverContext.doGetRequest( makeRequestUrl(serverContext.tunnel, "", "handshake", params)) if err != nil { return common.ContextError(err) } // Skip legacy format lines and just parse the JSON config line configLinePrefix := []byte("Config: ") for _, line := range bytes.Split(responseBody, []byte("\n")) { if bytes.HasPrefix(line, configLinePrefix) { response = line[len(configLinePrefix):] break } } if len(response) == 0 { return common.ContextError(errors.New("no config line found")) } } // Legacy fields: // - 'preemptive_reconnect_lifetime_milliseconds' is unused and ignored // - 'ssh_session_id' is ignored; client session ID is used instead var handshakeResponse common.HandshakeResponse err := json.Unmarshal(response, &handshakeResponse) if err != nil { return common.ContextError(err) } serverContext.clientRegion = handshakeResponse.ClientRegion NoticeClientRegion(serverContext.clientRegion) var decodedServerEntries []*ServerEntry // Store discovered server entries // We use the server's time, as it's available here, for the server entry // timestamp since this is more reliable than the client time. for _, encodedServerEntry := range handshakeResponse.EncodedServerList { serverEntry, err := DecodeServerEntry( encodedServerEntry, common.TruncateTimestampToHour(handshakeResponse.ServerTimestamp), common.SERVER_ENTRY_SOURCE_DISCOVERY) if err != nil { return common.ContextError(err) } err = ValidateServerEntry(serverEntry) if err != nil { // Skip this entry and continue with the next one continue } decodedServerEntries = append(decodedServerEntries, serverEntry) } // The reason we are storing the entire array of server entries at once rather // than one at a time is that some desirable side-effects get triggered by // StoreServerEntries that don't get triggered by StoreServerEntry. err = StoreServerEntries(decodedServerEntries, true) if err != nil { return common.ContextError(err) } // TODO: formally communicate the sponsor and upgrade info to an // outer client via some control interface. for _, homepage := range handshakeResponse.Homepages { NoticeHomepage(homepage) } serverContext.clientUpgradeVersion = handshakeResponse.UpgradeClientVersion if handshakeResponse.UpgradeClientVersion != "" { NoticeClientUpgradeAvailable(handshakeResponse.UpgradeClientVersion) } else { NoticeClientIsLatestVersion("") } var regexpsNotices []string serverContext.statsRegexps, regexpsNotices = transferstats.MakeRegexps( handshakeResponse.PageViewRegexes, handshakeResponse.HttpsRequestRegexes) for _, notice := range regexpsNotices { NoticeAlert(notice) } serverContext.serverHandshakeTimestamp = handshakeResponse.ServerTimestamp return nil }