// newTargetServerEntryIterator is a helper for initializing the TargetServerEntry case func newTargetServerEntryIterator(config *Config) (iterator *ServerEntryIterator, err error) { serverEntry, err := protocol.DecodeServerEntry( config.TargetServerEntry, common.GetCurrentTimestamp(), protocol.SERVER_ENTRY_SOURCE_TARGET) if err != nil { return nil, err } if config.EgressRegion != "" && serverEntry.Region != config.EgressRegion { return nil, errors.New("TargetServerEntry does not support EgressRegion") } if config.TunnelProtocol != "" { // Note: same capability/protocol mapping as in StoreServerEntry requiredCapability := strings.TrimSuffix(config.TunnelProtocol, "-OSSH") if !common.Contains(serverEntry.Capabilities, requiredCapability) { return nil, errors.New("TargetServerEntry does not support TunnelProtocol") } } iterator = &ServerEntryIterator{ isTargetServerEntryIterator: true, hasNextTargetServerEntry: true, targetServerEntry: serverEntry, } NoticeInfo("using TargetServerEntry: %s", serverEntry.IpAddress) return iterator, 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( protocol.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 protocol.HandshakeResponse err := json.Unmarshal(response, &handshakeResponse) if err != nil { return common.ContextError(err) } serverContext.clientRegion = handshakeResponse.ClientRegion NoticeClientRegion(serverContext.clientRegion) var decodedServerEntries []*protocol.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 := protocol.DecodeServerEntry( encodedServerEntry, common.TruncateTimestampToHour(handshakeResponse.ServerTimestamp), protocol.SERVER_ENTRY_SOURCE_DISCOVERY) if err != nil { return common.ContextError(err) } err = protocol.ValidateServerEntry(serverEntry) if err != nil { // Skip this entry and continue with the next one NoticeAlert("invalid server entry: %s", err) 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 }