server_test.go 17 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621
  1. // Copyright 2014 The go-ethereum Authors
  2. // This file is part of the go-ethereum library.
  3. //
  4. // The go-ethereum library is free software: you can redistribute it and/or modify
  5. // it under the terms of the GNU Lesser General Public License as published by
  6. // the Free Software Foundation, either version 3 of the License, or
  7. // (at your option) any later version.
  8. //
  9. // The go-ethereum library is distributed in the hope that it will be useful,
  10. // but WITHOUT ANY WARRANTY; without even the implied warranty of
  11. // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  12. // GNU Lesser General Public License for more details.
  13. //
  14. // You should have received a copy of the GNU Lesser General Public License
  15. // along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
  16. package p2p
  17. import (
  18. "crypto/ecdsa"
  19. "crypto/sha256"
  20. "errors"
  21. "io"
  22. "math/rand"
  23. "net"
  24. "reflect"
  25. "testing"
  26. "time"
  27. "github.com/ethereum/go-ethereum/crypto"
  28. "github.com/ethereum/go-ethereum/internal/testlog"
  29. "github.com/ethereum/go-ethereum/log"
  30. "github.com/ethereum/go-ethereum/p2p/enode"
  31. "github.com/ethereum/go-ethereum/p2p/enr"
  32. "github.com/ethereum/go-ethereum/p2p/rlpx"
  33. )
  34. type testTransport struct {
  35. *rlpxTransport
  36. rpub *ecdsa.PublicKey
  37. closeErr error
  38. }
  39. func newTestTransport(rpub *ecdsa.PublicKey, fd net.Conn, dialDest *ecdsa.PublicKey) transport {
  40. wrapped := newRLPX(fd, dialDest).(*rlpxTransport)
  41. wrapped.conn.InitWithSecrets(rlpx.Secrets{
  42. AES: make([]byte, 16),
  43. MAC: make([]byte, 16),
  44. EgressMAC: sha256.New(),
  45. IngressMAC: sha256.New(),
  46. })
  47. return &testTransport{rpub: rpub, rlpxTransport: wrapped}
  48. }
  49. func (c *testTransport) doEncHandshake(prv *ecdsa.PrivateKey) (*ecdsa.PublicKey, error) {
  50. return c.rpub, nil
  51. }
  52. func (c *testTransport) doProtoHandshake(our *protoHandshake) (*protoHandshake, error) {
  53. pubkey := crypto.FromECDSAPub(c.rpub)[1:]
  54. return &protoHandshake{ID: pubkey, Name: "test"}, nil
  55. }
  56. func (c *testTransport) close(err error) {
  57. c.conn.Close()
  58. c.closeErr = err
  59. }
  60. func startTestServer(t *testing.T, remoteKey *ecdsa.PublicKey, pf func(*Peer)) *Server {
  61. config := Config{
  62. Name: "test",
  63. MaxPeers: 10,
  64. ListenAddr: "127.0.0.1:0",
  65. NoDiscovery: true,
  66. PrivateKey: newkey(),
  67. Logger: testlog.Logger(t, log.LvlTrace),
  68. }
  69. server := &Server{
  70. Config: config,
  71. newPeerHook: pf,
  72. newTransport: func(fd net.Conn, dialDest *ecdsa.PublicKey) transport {
  73. return newTestTransport(remoteKey, fd, dialDest)
  74. },
  75. }
  76. if err := server.Start(); err != nil {
  77. t.Fatalf("Could not start server: %v", err)
  78. }
  79. return server
  80. }
  81. func TestServerListen(t *testing.T) {
  82. // start the test server
  83. connected := make(chan *Peer)
  84. remid := &newkey().PublicKey
  85. srv := startTestServer(t, remid, func(p *Peer) {
  86. if p.ID() != enode.PubkeyToIDV4(remid) {
  87. t.Error("peer func called with wrong node id")
  88. }
  89. connected <- p
  90. })
  91. defer close(connected)
  92. defer srv.Stop()
  93. // dial the test server
  94. conn, err := net.DialTimeout("tcp", srv.ListenAddr, 5*time.Second)
  95. if err != nil {
  96. t.Fatalf("could not dial: %v", err)
  97. }
  98. defer conn.Close()
  99. select {
  100. case peer := <-connected:
  101. if peer.LocalAddr().String() != conn.RemoteAddr().String() {
  102. t.Errorf("peer started with wrong conn: got %v, want %v",
  103. peer.LocalAddr(), conn.RemoteAddr())
  104. }
  105. peers := srv.Peers()
  106. if !reflect.DeepEqual(peers, []*Peer{peer}) {
  107. t.Errorf("Peers mismatch: got %v, want %v", peers, []*Peer{peer})
  108. }
  109. case <-time.After(1 * time.Second):
  110. t.Error("server did not accept within one second")
  111. }
  112. }
  113. func TestServerDial(t *testing.T) {
  114. // run a one-shot TCP server to handle the connection.
  115. listener, err := net.Listen("tcp", "127.0.0.1:0")
  116. if err != nil {
  117. t.Fatalf("could not setup listener: %v", err)
  118. }
  119. defer listener.Close()
  120. accepted := make(chan net.Conn, 1)
  121. go func() {
  122. conn, err := listener.Accept()
  123. if err != nil {
  124. return
  125. }
  126. accepted <- conn
  127. }()
  128. // start the server
  129. connected := make(chan *Peer)
  130. remid := &newkey().PublicKey
  131. srv := startTestServer(t, remid, func(p *Peer) { connected <- p })
  132. defer close(connected)
  133. defer srv.Stop()
  134. // tell the server to connect
  135. tcpAddr := listener.Addr().(*net.TCPAddr)
  136. node := enode.NewV4(remid, tcpAddr.IP, tcpAddr.Port, 0)
  137. srv.AddPeer(node)
  138. select {
  139. case conn := <-accepted:
  140. defer conn.Close()
  141. select {
  142. case peer := <-connected:
  143. if peer.ID() != enode.PubkeyToIDV4(remid) {
  144. t.Errorf("peer has wrong id")
  145. }
  146. if peer.Name() != "test" {
  147. t.Errorf("peer has wrong name")
  148. }
  149. if peer.RemoteAddr().String() != conn.LocalAddr().String() {
  150. t.Errorf("peer started with wrong conn: got %v, want %v",
  151. peer.RemoteAddr(), conn.LocalAddr())
  152. }
  153. peers := srv.Peers()
  154. if !reflect.DeepEqual(peers, []*Peer{peer}) {
  155. t.Errorf("Peers mismatch: got %v, want %v", peers, []*Peer{peer})
  156. }
  157. // Test AddTrustedPeer/RemoveTrustedPeer and changing Trusted flags
  158. // Particularly for race conditions on changing the flag state.
  159. if peer := srv.Peers()[0]; peer.Info().Network.Trusted {
  160. t.Errorf("peer is trusted prematurely: %v", peer)
  161. }
  162. done := make(chan bool)
  163. go func() {
  164. srv.AddTrustedPeer(node)
  165. if peer := srv.Peers()[0]; !peer.Info().Network.Trusted {
  166. t.Errorf("peer is not trusted after AddTrustedPeer: %v", peer)
  167. }
  168. srv.RemoveTrustedPeer(node)
  169. if peer := srv.Peers()[0]; peer.Info().Network.Trusted {
  170. t.Errorf("peer is trusted after RemoveTrustedPeer: %v", peer)
  171. }
  172. done <- true
  173. }()
  174. // Trigger potential race conditions
  175. peer = srv.Peers()[0]
  176. _ = peer.Inbound()
  177. _ = peer.Info()
  178. <-done
  179. case <-time.After(1 * time.Second):
  180. t.Error("server did not launch peer within one second")
  181. }
  182. case <-time.After(1 * time.Second):
  183. t.Error("server did not connect within one second")
  184. }
  185. }
  186. // This test checks that RemovePeer disconnects the peer if it is connected.
  187. func TestServerRemovePeerDisconnect(t *testing.T) {
  188. srv1 := &Server{Config: Config{
  189. PrivateKey: newkey(),
  190. MaxPeers: 1,
  191. NoDiscovery: true,
  192. Logger: testlog.Logger(t, log.LvlTrace).New("server", "1"),
  193. }}
  194. srv2 := &Server{Config: Config{
  195. PrivateKey: newkey(),
  196. MaxPeers: 1,
  197. NoDiscovery: true,
  198. NoDial: true,
  199. ListenAddr: "127.0.0.1:0",
  200. Logger: testlog.Logger(t, log.LvlTrace).New("server", "2"),
  201. }}
  202. srv1.Start()
  203. defer srv1.Stop()
  204. srv2.Start()
  205. defer srv2.Stop()
  206. if !syncAddPeer(srv1, srv2.Self()) {
  207. t.Fatal("peer not connected")
  208. }
  209. srv1.RemovePeer(srv2.Self())
  210. if srv1.PeerCount() > 0 {
  211. t.Fatal("removed peer still connected")
  212. }
  213. }
  214. // This test checks that connections are disconnected just after the encryption handshake
  215. // when the server is at capacity. Trusted connections should still be accepted.
  216. func TestServerAtCap(t *testing.T) {
  217. trustedNode := newkey()
  218. trustedID := enode.PubkeyToIDV4(&trustedNode.PublicKey)
  219. srv := &Server{
  220. Config: Config{
  221. PrivateKey: newkey(),
  222. MaxPeers: 10,
  223. NoDial: true,
  224. NoDiscovery: true,
  225. TrustedNodes: []*enode.Node{newNode(trustedID, "")},
  226. Logger: testlog.Logger(t, log.LvlTrace),
  227. },
  228. }
  229. if err := srv.Start(); err != nil {
  230. t.Fatalf("could not start: %v", err)
  231. }
  232. defer srv.Stop()
  233. newconn := func(id enode.ID) *conn {
  234. fd, _ := net.Pipe()
  235. tx := newTestTransport(&trustedNode.PublicKey, fd, nil)
  236. node := enode.SignNull(new(enr.Record), id)
  237. return &conn{fd: fd, transport: tx, flags: inboundConn, node: node, cont: make(chan error)}
  238. }
  239. // Inject a few connections to fill up the peer set.
  240. for i := 0; i < 10; i++ {
  241. c := newconn(randomID())
  242. if err := srv.checkpoint(c, srv.checkpointAddPeer); err != nil {
  243. t.Fatalf("could not add conn %d: %v", i, err)
  244. }
  245. }
  246. // Try inserting a non-trusted connection.
  247. anotherID := randomID()
  248. c := newconn(anotherID)
  249. if err := srv.checkpoint(c, srv.checkpointPostHandshake); err != DiscTooManyPeers {
  250. t.Error("wrong error for insert:", err)
  251. }
  252. // Try inserting a trusted connection.
  253. c = newconn(trustedID)
  254. if err := srv.checkpoint(c, srv.checkpointPostHandshake); err != nil {
  255. t.Error("unexpected error for trusted conn @posthandshake:", err)
  256. }
  257. if !c.is(trustedConn) {
  258. t.Error("Server did not set trusted flag")
  259. }
  260. // Remove from trusted set and try again
  261. srv.RemoveTrustedPeer(newNode(trustedID, ""))
  262. c = newconn(trustedID)
  263. if err := srv.checkpoint(c, srv.checkpointPostHandshake); err != DiscTooManyPeers {
  264. t.Error("wrong error for insert:", err)
  265. }
  266. // Add anotherID to trusted set and try again
  267. srv.AddTrustedPeer(newNode(anotherID, ""))
  268. c = newconn(anotherID)
  269. if err := srv.checkpoint(c, srv.checkpointPostHandshake); err != nil {
  270. t.Error("unexpected error for trusted conn @posthandshake:", err)
  271. }
  272. if !c.is(trustedConn) {
  273. t.Error("Server did not set trusted flag")
  274. }
  275. }
  276. func TestServerPeerLimits(t *testing.T) {
  277. srvkey := newkey()
  278. clientkey := newkey()
  279. clientnode := enode.NewV4(&clientkey.PublicKey, nil, 0, 0)
  280. var tp = &setupTransport{
  281. pubkey: &clientkey.PublicKey,
  282. phs: protoHandshake{
  283. ID: crypto.FromECDSAPub(&clientkey.PublicKey)[1:],
  284. // Force "DiscUselessPeer" due to unmatching caps
  285. // Caps: []Cap{discard.cap()},
  286. },
  287. }
  288. srv := &Server{
  289. Config: Config{
  290. PrivateKey: srvkey,
  291. MaxPeers: 0,
  292. NoDial: true,
  293. NoDiscovery: true,
  294. Protocols: []Protocol{discard},
  295. Logger: testlog.Logger(t, log.LvlTrace),
  296. },
  297. newTransport: func(fd net.Conn, dialDest *ecdsa.PublicKey) transport { return tp },
  298. }
  299. if err := srv.Start(); err != nil {
  300. t.Fatalf("couldn't start server: %v", err)
  301. }
  302. defer srv.Stop()
  303. // Check that server is full (MaxPeers=0)
  304. flags := dynDialedConn
  305. dialDest := clientnode
  306. conn, _ := net.Pipe()
  307. srv.SetupConn(conn, flags, dialDest)
  308. if tp.closeErr != DiscTooManyPeers {
  309. t.Errorf("unexpected close error: %q", tp.closeErr)
  310. }
  311. conn.Close()
  312. srv.AddTrustedPeer(clientnode)
  313. // Check that server allows a trusted peer despite being full.
  314. conn, _ = net.Pipe()
  315. srv.SetupConn(conn, flags, dialDest)
  316. if tp.closeErr == DiscTooManyPeers {
  317. t.Errorf("failed to bypass MaxPeers with trusted node: %q", tp.closeErr)
  318. }
  319. if tp.closeErr != DiscUselessPeer {
  320. t.Errorf("unexpected close error: %q", tp.closeErr)
  321. }
  322. conn.Close()
  323. srv.RemoveTrustedPeer(clientnode)
  324. // Check that server is full again.
  325. conn, _ = net.Pipe()
  326. srv.SetupConn(conn, flags, dialDest)
  327. if tp.closeErr != DiscTooManyPeers {
  328. t.Errorf("unexpected close error: %q", tp.closeErr)
  329. }
  330. conn.Close()
  331. }
  332. func TestServerSetupConn(t *testing.T) {
  333. var (
  334. clientkey, srvkey = newkey(), newkey()
  335. clientpub = &clientkey.PublicKey
  336. srvpub = &srvkey.PublicKey
  337. )
  338. tests := []struct {
  339. dontstart bool
  340. tt *setupTransport
  341. flags connFlag
  342. dialDest *enode.Node
  343. wantCloseErr error
  344. wantCalls string
  345. }{
  346. {
  347. dontstart: true,
  348. tt: &setupTransport{pubkey: clientpub},
  349. wantCalls: "close,",
  350. wantCloseErr: errServerStopped,
  351. },
  352. {
  353. tt: &setupTransport{pubkey: clientpub, encHandshakeErr: errors.New("read error")},
  354. flags: inboundConn,
  355. wantCalls: "doEncHandshake,close,",
  356. wantCloseErr: errors.New("read error"),
  357. },
  358. {
  359. tt: &setupTransport{pubkey: clientpub, phs: protoHandshake{ID: randomID().Bytes()}},
  360. dialDest: enode.NewV4(clientpub, nil, 0, 0),
  361. flags: dynDialedConn,
  362. wantCalls: "doEncHandshake,doProtoHandshake,close,",
  363. wantCloseErr: DiscUnexpectedIdentity,
  364. },
  365. {
  366. tt: &setupTransport{pubkey: clientpub, protoHandshakeErr: errors.New("foo")},
  367. dialDest: enode.NewV4(clientpub, nil, 0, 0),
  368. flags: dynDialedConn,
  369. wantCalls: "doEncHandshake,doProtoHandshake,close,",
  370. wantCloseErr: errors.New("foo"),
  371. },
  372. {
  373. tt: &setupTransport{pubkey: srvpub, phs: protoHandshake{ID: crypto.FromECDSAPub(srvpub)[1:]}},
  374. flags: inboundConn,
  375. wantCalls: "doEncHandshake,close,",
  376. wantCloseErr: DiscSelf,
  377. },
  378. {
  379. tt: &setupTransport{pubkey: clientpub, phs: protoHandshake{ID: crypto.FromECDSAPub(clientpub)[1:]}},
  380. flags: inboundConn,
  381. wantCalls: "doEncHandshake,doProtoHandshake,close,",
  382. wantCloseErr: DiscUselessPeer,
  383. },
  384. }
  385. for i, test := range tests {
  386. t.Run(test.wantCalls, func(t *testing.T) {
  387. cfg := Config{
  388. PrivateKey: srvkey,
  389. MaxPeers: 10,
  390. NoDial: true,
  391. NoDiscovery: true,
  392. Protocols: []Protocol{discard},
  393. Logger: testlog.Logger(t, log.LvlTrace),
  394. }
  395. srv := &Server{
  396. Config: cfg,
  397. newTransport: func(fd net.Conn, dialDest *ecdsa.PublicKey) transport { return test.tt },
  398. log: cfg.Logger,
  399. }
  400. if !test.dontstart {
  401. if err := srv.Start(); err != nil {
  402. t.Fatalf("couldn't start server: %v", err)
  403. }
  404. defer srv.Stop()
  405. }
  406. p1, _ := net.Pipe()
  407. srv.SetupConn(p1, test.flags, test.dialDest)
  408. if !reflect.DeepEqual(test.tt.closeErr, test.wantCloseErr) {
  409. t.Errorf("test %d: close error mismatch: got %q, want %q", i, test.tt.closeErr, test.wantCloseErr)
  410. }
  411. if test.tt.calls != test.wantCalls {
  412. t.Errorf("test %d: calls mismatch: got %q, want %q", i, test.tt.calls, test.wantCalls)
  413. }
  414. })
  415. }
  416. }
  417. type setupTransport struct {
  418. pubkey *ecdsa.PublicKey
  419. encHandshakeErr error
  420. phs protoHandshake
  421. protoHandshakeErr error
  422. calls string
  423. closeErr error
  424. }
  425. func (c *setupTransport) doEncHandshake(prv *ecdsa.PrivateKey) (*ecdsa.PublicKey, error) {
  426. c.calls += "doEncHandshake,"
  427. return c.pubkey, c.encHandshakeErr
  428. }
  429. func (c *setupTransport) doProtoHandshake(our *protoHandshake) (*protoHandshake, error) {
  430. c.calls += "doProtoHandshake,"
  431. if c.protoHandshakeErr != nil {
  432. return nil, c.protoHandshakeErr
  433. }
  434. return &c.phs, nil
  435. }
  436. func (c *setupTransport) close(err error) {
  437. c.calls += "close,"
  438. c.closeErr = err
  439. }
  440. // setupConn shouldn't write to/read from the connection.
  441. func (c *setupTransport) WriteMsg(Msg) error {
  442. panic("WriteMsg called on setupTransport")
  443. }
  444. func (c *setupTransport) ReadMsg() (Msg, error) {
  445. panic("ReadMsg called on setupTransport")
  446. }
  447. func newkey() *ecdsa.PrivateKey {
  448. key, err := crypto.GenerateKey()
  449. if err != nil {
  450. panic("couldn't generate key: " + err.Error())
  451. }
  452. return key
  453. }
  454. func randomID() (id enode.ID) {
  455. for i := range id {
  456. id[i] = byte(rand.Intn(255))
  457. }
  458. return id
  459. }
  460. // This test checks that inbound connections are throttled by IP.
  461. func TestServerInboundThrottle(t *testing.T) {
  462. const timeout = 5 * time.Second
  463. newTransportCalled := make(chan struct{})
  464. srv := &Server{
  465. Config: Config{
  466. PrivateKey: newkey(),
  467. ListenAddr: "127.0.0.1:0",
  468. MaxPeers: 10,
  469. NoDial: true,
  470. NoDiscovery: true,
  471. Protocols: []Protocol{discard},
  472. Logger: testlog.Logger(t, log.LvlTrace),
  473. },
  474. newTransport: func(fd net.Conn, dialDest *ecdsa.PublicKey) transport {
  475. newTransportCalled <- struct{}{}
  476. return newRLPX(fd, dialDest)
  477. },
  478. listenFunc: func(network, laddr string) (net.Listener, error) {
  479. fakeAddr := &net.TCPAddr{IP: net.IP{95, 33, 21, 2}, Port: 4444}
  480. return listenFakeAddr(network, laddr, fakeAddr)
  481. },
  482. }
  483. if err := srv.Start(); err != nil {
  484. t.Fatal("can't start: ", err)
  485. }
  486. defer srv.Stop()
  487. // Dial the test server.
  488. conn, err := net.DialTimeout("tcp", srv.ListenAddr, timeout)
  489. if err != nil {
  490. t.Fatalf("could not dial: %v", err)
  491. }
  492. select {
  493. case <-newTransportCalled:
  494. // OK
  495. case <-time.After(timeout):
  496. t.Error("newTransport not called")
  497. }
  498. conn.Close()
  499. // Dial again. This time the server should close the connection immediately.
  500. connClosed := make(chan struct{}, 1)
  501. conn, err = net.DialTimeout("tcp", srv.ListenAddr, timeout)
  502. if err != nil {
  503. t.Fatalf("could not dial: %v", err)
  504. }
  505. defer conn.Close()
  506. go func() {
  507. conn.SetDeadline(time.Now().Add(timeout))
  508. buf := make([]byte, 10)
  509. if n, err := conn.Read(buf); err != io.EOF || n != 0 {
  510. t.Errorf("expected io.EOF and n == 0, got error %q and n == %d", err, n)
  511. }
  512. connClosed <- struct{}{}
  513. }()
  514. select {
  515. case <-connClosed:
  516. // OK
  517. case <-newTransportCalled:
  518. t.Error("newTransport called for second attempt")
  519. case <-time.After(timeout):
  520. t.Error("connection not closed within timeout")
  521. }
  522. }
  523. func listenFakeAddr(network, laddr string, remoteAddr net.Addr) (net.Listener, error) {
  524. l, err := net.Listen(network, laddr)
  525. if err == nil {
  526. l = &fakeAddrListener{l, remoteAddr}
  527. }
  528. return l, err
  529. }
  530. // fakeAddrListener is a listener that creates connections with a mocked remote address.
  531. type fakeAddrListener struct {
  532. net.Listener
  533. remoteAddr net.Addr
  534. }
  535. type fakeAddrConn struct {
  536. net.Conn
  537. remoteAddr net.Addr
  538. }
  539. func (l *fakeAddrListener) Accept() (net.Conn, error) {
  540. c, err := l.Listener.Accept()
  541. if err != nil {
  542. return nil, err
  543. }
  544. return &fakeAddrConn{c, l.remoteAddr}, nil
  545. }
  546. func (c *fakeAddrConn) RemoteAddr() net.Addr {
  547. return c.remoteAddr
  548. }
  549. func syncAddPeer(srv *Server, node *enode.Node) bool {
  550. var (
  551. ch = make(chan *PeerEvent)
  552. sub = srv.SubscribeEvents(ch)
  553. timeout = time.After(2 * time.Second)
  554. )
  555. defer sub.Unsubscribe()
  556. srv.AddPeer(node)
  557. for {
  558. select {
  559. case ev := <-ch:
  560. if ev.Type == PeerEventTypeAdd && ev.Peer == node.ID() {
  561. return true
  562. }
  563. case <-timeout:
  564. return false
  565. }
  566. }
  567. }