1// Copyright 2013 The Chromium Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5#include "net/quic/crypto/quic_crypto_server_config.h"
6
7#include <stdarg.h>
8
9#include "base/stl_util.h"
10#include "net/quic/crypto/aes_128_gcm_12_encrypter.h"
11#include "net/quic/crypto/crypto_handshake_message.h"
12#include "net/quic/crypto/crypto_secret_boxer.h"
13#include "net/quic/crypto/crypto_server_config_protobuf.h"
14#include "net/quic/crypto/quic_random.h"
15#include "net/quic/crypto/strike_register_client.h"
16#include "net/quic/quic_time.h"
17#include "net/quic/test_tools/mock_clock.h"
18#include "net/quic/test_tools/quic_test_utils.h"
19#include "testing/gmock/include/gmock/gmock.h"
20#include "testing/gtest/include/gtest/gtest.h"
21
22using base::StringPiece;
23using std::make_pair;
24using std::map;
25using std::pair;
26using std::string;
27using std::vector;
28
29namespace net {
30namespace test {
31
32class QuicCryptoServerConfigPeer {
33 public:
34  explicit QuicCryptoServerConfigPeer(QuicCryptoServerConfig* server_config)
35      : server_config_(server_config) {}
36
37  scoped_refptr<QuicCryptoServerConfig::Config> GetConfig(string config_id) {
38    base::AutoLock locked(server_config_->configs_lock_);
39    if (config_id == "<primary>") {
40      return scoped_refptr<QuicCryptoServerConfig::Config>(
41          server_config_->primary_config_);
42    } else {
43      return server_config_->GetConfigWithScid(config_id);
44    }
45  }
46
47  bool ConfigHasDefaultSourceAddressTokenBoxer(string config_id) {
48    scoped_refptr<QuicCryptoServerConfig::Config> config = GetConfig(config_id);
49    return config->source_address_token_boxer ==
50        &(server_config_->default_source_address_token_boxer_);
51  }
52
53  string NewSourceAddressToken(
54      string config_id,
55      IPEndPoint ip,
56      QuicRandom* rand,
57      QuicWallTime now) {
58    return server_config_->NewSourceAddressToken(
59        *GetConfig(config_id), ip, rand, now);
60  }
61
62  HandshakeFailureReason ValidateSourceAddressToken(string config_id,
63                                                    StringPiece srct,
64                                                    IPEndPoint ip,
65                                                    QuicWallTime now) {
66    return server_config_->ValidateSourceAddressToken(
67        *GetConfig(config_id), srct, ip, now);
68  }
69
70  string NewServerNonce(QuicRandom* rand, QuicWallTime now) const {
71    return server_config_->NewServerNonce(rand, now);
72  }
73
74  HandshakeFailureReason ValidateServerNonce(StringPiece token,
75                                             QuicWallTime now) {
76    return server_config_->ValidateServerNonce(token, now);
77  }
78
79  base::Lock* GetStrikeRegisterClientLock() {
80    return &server_config_->strike_register_client_lock_;
81  }
82
83  // CheckConfigs compares the state of the Configs in |server_config_| to the
84  // description given as arguments. The arguments are given as NULL-terminated
85  // pairs. The first of each pair is the server config ID of a Config. The
86  // second is a boolean describing whether the config is the primary. For
87  // example:
88  //   CheckConfigs(NULL);  // checks that no Configs are loaded.
89  //
90  //   // Checks that exactly three Configs are loaded with the given IDs and
91  //   // status.
92  //   CheckConfigs(
93  //     "id1", false,
94  //     "id2", true,
95  //     "id3", false,
96  //     NULL);
97  void CheckConfigs(const char* server_config_id1, ...) {
98    va_list ap;
99    va_start(ap, server_config_id1);
100
101    vector<pair<ServerConfigID, bool> > expected;
102    bool first = true;
103    for (;;) {
104      const char* server_config_id;
105      if (first) {
106        server_config_id = server_config_id1;
107        first = false;
108      } else {
109        server_config_id = va_arg(ap, const char*);
110      }
111
112      if (!server_config_id) {
113        break;
114      }
115
116      // varargs will promote the value to an int so we have to read that from
117      // the stack and cast down.
118      const bool is_primary = static_cast<bool>(va_arg(ap, int));
119      expected.push_back(make_pair(server_config_id, is_primary));
120    }
121
122    va_end(ap);
123
124    base::AutoLock locked(server_config_->configs_lock_);
125
126    ASSERT_EQ(expected.size(), server_config_->configs_.size())
127        << ConfigsDebug();
128
129    for (QuicCryptoServerConfig::ConfigMap::const_iterator
130             i = server_config_->configs_.begin();
131         i != server_config_->configs_.end(); ++i) {
132      bool found = false;
133      for (vector<pair<ServerConfigID, bool> >::iterator j = expected.begin();
134           j != expected.end(); ++j) {
135        if (i->first == j->first && i->second->is_primary == j->second) {
136          found = true;
137          j->first.clear();
138          break;
139        }
140      }
141
142      ASSERT_TRUE(found) << "Failed to find match for " << i->first
143                         << " in configs:\n" << ConfigsDebug();
144    }
145  }
146
147  // ConfigsDebug returns a string that contains debugging information about
148  // the set of Configs loaded in |server_config_| and their status.
149  // ConfigsDebug() should be called after acquiring
150  // server_config_->configs_lock_.
151  string ConfigsDebug() {
152    if (server_config_->configs_.empty()) {
153      return "No Configs in QuicCryptoServerConfig";
154    }
155
156    string s;
157
158    for (QuicCryptoServerConfig::ConfigMap::const_iterator
159             i = server_config_->configs_.begin();
160         i != server_config_->configs_.end(); ++i) {
161      const scoped_refptr<QuicCryptoServerConfig::Config> config = i->second;
162      if (config->is_primary) {
163        s += "(primary) ";
164      } else {
165        s += "          ";
166      }
167      s += config->id;
168      s += "\n";
169    }
170
171    return s;
172  }
173
174  void SelectNewPrimaryConfig(int seconds) {
175    base::AutoLock locked(server_config_->configs_lock_);
176    server_config_->SelectNewPrimaryConfig(
177        QuicWallTime::FromUNIXSeconds(seconds));
178  }
179
180 private:
181  const QuicCryptoServerConfig* server_config_;
182};
183
184class TestStrikeRegisterClient : public StrikeRegisterClient {
185 public:
186  explicit TestStrikeRegisterClient(QuicCryptoServerConfig* config)
187      : config_(config),
188        is_known_orbit_called_(false) {
189  }
190
191  virtual bool IsKnownOrbit(StringPiece orbit) const OVERRIDE {
192    // Ensure that the strike register client lock is not held.
193    QuicCryptoServerConfigPeer peer(config_);
194    base::Lock* m = peer.GetStrikeRegisterClientLock();
195    // In Chromium, we will dead lock if the lock is held by the current thread.
196    // Chromium doesn't have AssertNotHeld API call.
197    // m->AssertNotHeld();
198    base::AutoLock lock(*m);
199
200    is_known_orbit_called_ = true;
201    return true;
202  }
203
204  virtual void VerifyNonceIsValidAndUnique(
205      StringPiece nonce,
206      QuicWallTime now,
207      ResultCallback* cb) OVERRIDE {
208    LOG(FATAL) << "Not implemented";
209  }
210
211  bool is_known_orbit_called() { return is_known_orbit_called_; }
212
213 private:
214  QuicCryptoServerConfig* config_;
215  mutable bool is_known_orbit_called_;
216};
217
218TEST(QuicCryptoServerConfigTest, ServerConfig) {
219  QuicRandom* rand = QuicRandom::GetInstance();
220  QuicCryptoServerConfig server(QuicCryptoServerConfig::TESTING, rand);
221  MockClock clock;
222
223  scoped_ptr<CryptoHandshakeMessage>(
224      server.AddDefaultConfig(rand, &clock,
225                              QuicCryptoServerConfig::ConfigOptions()));
226}
227
228TEST(QuicCryptoServerConfigTest, GetOrbitIsCalledWithoutTheStrikeRegisterLock) {
229  QuicRandom* rand = QuicRandom::GetInstance();
230  QuicCryptoServerConfig server(QuicCryptoServerConfig::TESTING, rand);
231  MockClock clock;
232
233  TestStrikeRegisterClient* strike_register =
234      new TestStrikeRegisterClient(&server);
235  server.SetStrikeRegisterClient(strike_register);
236
237  QuicCryptoServerConfig::ConfigOptions options;
238  scoped_ptr<CryptoHandshakeMessage>(
239      server.AddDefaultConfig(rand, &clock, options));
240  EXPECT_TRUE(strike_register->is_known_orbit_called());
241}
242
243TEST(QuicCryptoServerConfigTest, SourceAddressTokens) {
244  const string kPrimary = "<primary>";
245  const string kOverride = "Config with custom source address token key";
246
247  MockClock clock;
248  clock.AdvanceTime(QuicTime::Delta::FromSeconds(1000000));
249
250  QuicWallTime now = clock.WallNow();
251  const QuicWallTime original_time = now;
252
253  QuicRandom* rand = QuicRandom::GetInstance();
254  QuicCryptoServerConfig server(QuicCryptoServerConfig::TESTING, rand);
255  QuicCryptoServerConfigPeer peer(&server);
256
257  scoped_ptr<CryptoHandshakeMessage>(
258      server.AddDefaultConfig(rand, &clock,
259                              QuicCryptoServerConfig::ConfigOptions()));
260
261  // Add a config that overrides the default boxer.
262  QuicCryptoServerConfig::ConfigOptions options;
263  options.id = kOverride;
264  scoped_ptr<QuicServerConfigProtobuf> protobuf(
265      QuicCryptoServerConfig::GenerateConfig(rand, &clock, options));
266  protobuf->set_source_address_token_secret_override("a secret key");
267  // Lower priority than the default config.
268  protobuf->set_priority(1);
269  scoped_ptr<CryptoHandshakeMessage>(
270      server.AddConfig(protobuf.get(), now));
271
272  EXPECT_TRUE(peer.ConfigHasDefaultSourceAddressTokenBoxer(kPrimary));
273  EXPECT_FALSE(peer.ConfigHasDefaultSourceAddressTokenBoxer(kOverride));
274
275  IPEndPoint ip4 = IPEndPoint(Loopback4(), 1);
276  IPEndPoint ip4d = IPEndPoint(ConvertIPv4NumberToIPv6Number(ip4.address()), 1);
277  IPEndPoint ip6 = IPEndPoint(Loopback6(), 2);
278
279  // Primary config generates configs that validate successfully.
280  const string token4 = peer.NewSourceAddressToken(kPrimary, ip4, rand, now);
281  const string token4d = peer.NewSourceAddressToken(kPrimary, ip4d, rand, now);
282  const string token6 = peer.NewSourceAddressToken(kPrimary, ip6, rand, now);
283  EXPECT_EQ(HANDSHAKE_OK, peer.ValidateSourceAddressToken(
284      kPrimary, token4, ip4, now));
285  DCHECK_EQ(HANDSHAKE_OK, peer.ValidateSourceAddressToken(
286      kPrimary, token4, ip4d, now));
287  DCHECK_EQ(SOURCE_ADDRESS_TOKEN_DIFFERENT_IP_ADDRESS_FAILURE,
288            peer.ValidateSourceAddressToken(kPrimary, token4, ip6, now));
289  DCHECK_EQ(HANDSHAKE_OK, peer.ValidateSourceAddressToken(
290      kPrimary, token4d, ip4, now));
291  DCHECK_EQ(HANDSHAKE_OK, peer.ValidateSourceAddressToken(
292      kPrimary, token4d, ip4d, now));
293  DCHECK_EQ(SOURCE_ADDRESS_TOKEN_DIFFERENT_IP_ADDRESS_FAILURE,
294            peer.ValidateSourceAddressToken(kPrimary, token4d, ip6, now));
295  DCHECK_EQ(HANDSHAKE_OK, peer.ValidateSourceAddressToken(
296      kPrimary, token6, ip6, now));
297
298  // Override config generates configs that validate successfully.
299  const string override_token4 = peer.NewSourceAddressToken(
300      kOverride, ip4, rand, now);
301  const string override_token6 = peer.NewSourceAddressToken(
302      kOverride, ip6, rand, now);
303  DCHECK_EQ(HANDSHAKE_OK, peer.ValidateSourceAddressToken(
304      kOverride, override_token4, ip4, now));
305  DCHECK_EQ(SOURCE_ADDRESS_TOKEN_DIFFERENT_IP_ADDRESS_FAILURE,
306            peer.ValidateSourceAddressToken(kOverride, override_token4, ip6,
307                                            now));
308  DCHECK_EQ(HANDSHAKE_OK, peer.ValidateSourceAddressToken(
309      kOverride, override_token6, ip6, now));
310
311  // Tokens generated by the primary config do not validate
312  // successfully against the override config, and vice versa.
313  DCHECK_EQ(SOURCE_ADDRESS_TOKEN_DECRYPTION_FAILURE,
314            peer.ValidateSourceAddressToken(kOverride, token4, ip4, now));
315  DCHECK_EQ(SOURCE_ADDRESS_TOKEN_DECRYPTION_FAILURE,
316            peer.ValidateSourceAddressToken(kOverride, token6, ip6, now));
317  DCHECK_EQ(SOURCE_ADDRESS_TOKEN_DECRYPTION_FAILURE,
318            peer.ValidateSourceAddressToken(kPrimary, override_token4, ip4,
319                                            now));
320  DCHECK_EQ(SOURCE_ADDRESS_TOKEN_DECRYPTION_FAILURE,
321            peer.ValidateSourceAddressToken(kPrimary, override_token6, ip6,
322                                            now));
323
324  // Validation fails after tokens expire.
325  now = original_time.Add(QuicTime::Delta::FromSeconds(86400 * 7));
326  DCHECK_EQ(SOURCE_ADDRESS_TOKEN_EXPIRED_FAILURE,
327            peer.ValidateSourceAddressToken(kPrimary, token4, ip4, now));
328
329  now = original_time.Subtract(QuicTime::Delta::FromSeconds(3600 * 2));
330  DCHECK_EQ(SOURCE_ADDRESS_TOKEN_CLOCK_SKEW_FAILURE,
331            peer.ValidateSourceAddressToken(kPrimary, token4, ip4, now));
332}
333
334TEST(QuicCryptoServerConfigTest, ValidateServerNonce) {
335  QuicRandom* rand = QuicRandom::GetInstance();
336  QuicCryptoServerConfig server(QuicCryptoServerConfig::TESTING, rand);
337  QuicCryptoServerConfigPeer peer(&server);
338
339  StringPiece message("hello world");
340  const size_t key_size = CryptoSecretBoxer::GetKeySize();
341  scoped_ptr<uint8[]> key(new uint8[key_size]);
342  memset(key.get(), 0x11, key_size);
343
344  CryptoSecretBoxer boxer;
345  boxer.SetKey(StringPiece(reinterpret_cast<char*>(key.get()), key_size));
346  const string box = boxer.Box(rand, message);
347  MockClock clock;
348  QuicWallTime now = clock.WallNow();
349  const QuicWallTime original_time = now;
350  EXPECT_EQ(SERVER_NONCE_DECRYPTION_FAILURE,
351            peer.ValidateServerNonce(box, now));
352
353  string server_nonce = peer.NewServerNonce(rand, now);
354  EXPECT_EQ(HANDSHAKE_OK, peer.ValidateServerNonce(server_nonce, now));
355  EXPECT_EQ(SERVER_NONCE_NOT_UNIQUE_FAILURE,
356            peer.ValidateServerNonce(server_nonce, now));
357
358  now = original_time.Add(QuicTime::Delta::FromSeconds(1000 * 7));
359  server_nonce = peer.NewServerNonce(rand, now);
360  EXPECT_EQ(HANDSHAKE_OK, peer.ValidateServerNonce(server_nonce, now));
361}
362
363class CryptoServerConfigsTest : public ::testing::Test {
364 public:
365  CryptoServerConfigsTest()
366      : rand_(QuicRandom::GetInstance()),
367        config_(QuicCryptoServerConfig::TESTING, rand_),
368        test_peer_(&config_) {}
369
370  virtual void SetUp() {
371    clock_.AdvanceTime(QuicTime::Delta::FromSeconds(1000));
372  }
373
374  // SetConfigs constructs suitable config protobufs and calls SetConfigs on
375  // |config_|. The arguments are given as NULL-terminated pairs. The first of
376  // each pair is the server config ID of a Config. The second is the
377  // |primary_time| of that Config, given in epoch seconds. (Although note
378  // that, in these tests, time is set to 1000 seconds since the epoch.) For
379  // example:
380  //   SetConfigs(NULL);  // calls |config_.SetConfigs| with no protobufs.
381  //
382  //   // Calls |config_.SetConfigs| with two protobufs: one for a Config with
383  //   // a |primary_time| of 900 and priority 1, and another with
384  //   // a |primary_time| of 1000 and priority 2.
385
386  //   CheckConfigs(
387  //     "id1", 900, 1,
388  //     "id2", 1000, 2,
389  //     NULL);
390  //
391  // If the server config id starts with "INVALID" then the generated protobuf
392  // will be invalid.
393  void SetConfigs(const char* server_config_id1, ...) {
394    const char kOrbit[] = "12345678";
395
396    va_list ap;
397    va_start(ap, server_config_id1);
398    bool has_invalid = false;
399    bool is_empty = true;
400
401    vector<QuicServerConfigProtobuf*> protobufs;
402    bool first = true;
403    for (;;) {
404      const char* server_config_id;
405      if (first) {
406        server_config_id = server_config_id1;
407        first = false;
408      } else {
409        server_config_id = va_arg(ap, const char*);
410      }
411
412      if (!server_config_id) {
413        break;
414      }
415
416      is_empty = false;
417      int primary_time = va_arg(ap, int);
418      int priority = va_arg(ap, int);
419
420      QuicCryptoServerConfig::ConfigOptions options;
421      options.id = server_config_id;
422      options.orbit = kOrbit;
423      QuicServerConfigProtobuf* protobuf(
424          QuicCryptoServerConfig::GenerateConfig(rand_, &clock_, options));
425      protobuf->set_primary_time(primary_time);
426      protobuf->set_priority(priority);
427      if (string(server_config_id).find("INVALID") == 0) {
428        protobuf->clear_key();
429        has_invalid = true;
430      }
431      protobufs.push_back(protobuf);
432    }
433
434    ASSERT_EQ(!has_invalid && !is_empty,
435              config_.SetConfigs(protobufs, clock_.WallNow()));
436    STLDeleteElements(&protobufs);
437  }
438
439 protected:
440  QuicRandom* const rand_;
441  MockClock clock_;
442  QuicCryptoServerConfig config_;
443  QuicCryptoServerConfigPeer test_peer_;
444};
445
446TEST_F(CryptoServerConfigsTest, NoConfigs) {
447  test_peer_.CheckConfigs(NULL);
448}
449
450TEST_F(CryptoServerConfigsTest, MakePrimaryFirst) {
451  // Make sure that "b" is primary even though "a" comes first.
452  SetConfigs("a", 1100, 1,
453             "b", 900, 1,
454             NULL);
455  test_peer_.CheckConfigs(
456      "a", false,
457      "b", true,
458      NULL);
459}
460
461TEST_F(CryptoServerConfigsTest, MakePrimarySecond) {
462  // Make sure that a remains primary after b is added.
463  SetConfigs("a", 900, 1,
464             "b", 1100, 1,
465             NULL);
466  test_peer_.CheckConfigs(
467      "a", true,
468      "b", false,
469      NULL);
470}
471
472TEST_F(CryptoServerConfigsTest, Delete) {
473  // Ensure that configs get deleted when removed.
474  SetConfigs("a", 800, 1,
475             "b", 900, 1,
476             "c", 1100, 1,
477             NULL);
478  test_peer_.CheckConfigs(
479      "a", false,
480      "b", true,
481      "c", false,
482      NULL);
483  SetConfigs("b", 900, 1,
484             "c", 1100, 1,
485             NULL);
486  test_peer_.CheckConfigs(
487      "b", true,
488      "c", false,
489      NULL);
490}
491
492TEST_F(CryptoServerConfigsTest, DeletePrimary) {
493  // Ensure that deleting the primary config works.
494  SetConfigs("a", 800, 1,
495             "b", 900, 1,
496             "c", 1100, 1,
497             NULL);
498  test_peer_.CheckConfigs(
499      "a", false,
500      "b", true,
501      "c", false,
502      NULL);
503  SetConfigs("a", 800, 1,
504             "c", 1100, 1,
505             NULL);
506  test_peer_.CheckConfigs(
507      "a", true,
508      "c", false,
509      NULL);
510}
511
512TEST_F(CryptoServerConfigsTest, FailIfDeletingAllConfigs) {
513  // Ensure that configs get deleted when removed.
514  SetConfigs("a", 800, 1,
515             "b", 900, 1,
516             NULL);
517  test_peer_.CheckConfigs(
518      "a", false,
519      "b", true,
520      NULL);
521  SetConfigs(NULL);
522  // Config change is rejected, still using old configs.
523  test_peer_.CheckConfigs(
524      "a", false,
525      "b", true,
526      NULL);
527}
528
529TEST_F(CryptoServerConfigsTest, ChangePrimaryTime) {
530  // Check that updates to primary time get picked up.
531  SetConfigs("a", 400, 1,
532             "b", 800, 1,
533             "c", 1200, 1,
534             NULL);
535  test_peer_.SelectNewPrimaryConfig(500);
536  test_peer_.CheckConfigs(
537      "a", true,
538      "b", false,
539      "c", false,
540      NULL);
541  SetConfigs("a", 1200, 1,
542             "b", 800, 1,
543             "c", 400, 1,
544             NULL);
545  test_peer_.SelectNewPrimaryConfig(500);
546  test_peer_.CheckConfigs(
547      "a", false,
548      "b", false,
549      "c", true,
550      NULL);
551}
552
553TEST_F(CryptoServerConfigsTest, AllConfigsInThePast) {
554  // Check that the most recent config is selected.
555  SetConfigs("a", 400, 1,
556             "b", 800, 1,
557             "c", 1200, 1,
558             NULL);
559  test_peer_.SelectNewPrimaryConfig(1500);
560  test_peer_.CheckConfigs(
561      "a", false,
562      "b", false,
563      "c", true,
564      NULL);
565}
566
567TEST_F(CryptoServerConfigsTest, AllConfigsInTheFuture) {
568  // Check that the first config is selected.
569  SetConfigs("a", 400, 1,
570             "b", 800, 1,
571             "c", 1200, 1,
572             NULL);
573  test_peer_.SelectNewPrimaryConfig(100);
574  test_peer_.CheckConfigs(
575      "a", true,
576      "b", false,
577      "c", false,
578      NULL);
579}
580
581TEST_F(CryptoServerConfigsTest, SortByPriority) {
582  // Check that priority is used to decide on a primary config when
583  // configs have the same primary time.
584  SetConfigs("a", 900, 1,
585             "b", 900, 2,
586             "c", 900, 3,
587             NULL);
588  test_peer_.CheckConfigs(
589      "a", true,
590      "b", false,
591      "c", false,
592      NULL);
593  test_peer_.SelectNewPrimaryConfig(800);
594  test_peer_.CheckConfigs(
595      "a", true,
596      "b", false,
597      "c", false,
598      NULL);
599  test_peer_.SelectNewPrimaryConfig(1000);
600  test_peer_.CheckConfigs(
601      "a", true,
602      "b", false,
603      "c", false,
604      NULL);
605
606  // Change priorities and expect sort order to change.
607  SetConfigs("a", 900, 2,
608             "b", 900, 1,
609             "c", 900, 0,
610             NULL);
611  test_peer_.CheckConfigs(
612      "a", false,
613      "b", false,
614      "c", true,
615      NULL);
616  test_peer_.SelectNewPrimaryConfig(800);
617  test_peer_.CheckConfigs(
618      "a", false,
619      "b", false,
620      "c", true,
621      NULL);
622  test_peer_.SelectNewPrimaryConfig(1000);
623  test_peer_.CheckConfigs(
624      "a", false,
625      "b", false,
626      "c", true,
627      NULL);
628}
629
630TEST_F(CryptoServerConfigsTest, AdvancePrimary) {
631  // Check that a new primary config is enabled at the right time.
632  SetConfigs("a", 900, 1,
633             "b", 1100, 1,
634             NULL);
635  test_peer_.SelectNewPrimaryConfig(1000);
636  test_peer_.CheckConfigs(
637      "a", true,
638      "b", false,
639      NULL);
640  test_peer_.SelectNewPrimaryConfig(1101);
641  test_peer_.CheckConfigs(
642      "a", false,
643      "b", true,
644      NULL);
645}
646
647TEST_F(CryptoServerConfigsTest, InvalidConfigs) {
648  // Ensure that invalid configs don't change anything.
649  SetConfigs("a", 800, 1,
650             "b", 900, 1,
651             "c", 1100, 1,
652             NULL);
653  test_peer_.CheckConfigs(
654      "a", false,
655      "b", true,
656      "c", false,
657      NULL);
658  SetConfigs("a", 800, 1,
659             "c", 1100, 1,
660             "INVALID1", 1000, 1,
661             NULL);
662  test_peer_.CheckConfigs(
663      "a", false,
664      "b", true,
665      "c", false,
666      NULL);
667}
668
669}  // namespace test
670}  // namespace net
671