github.com/klaytn/klaytn@v1.12.1/networks/p2p/dial.go (about) 1 // Modifications Copyright 2018 The klaytn Authors 2 // Copyright 2015 The go-ethereum Authors 3 // This file is part of the go-ethereum library. 4 // 5 // The go-ethereum library is free software: you can redistribute it and/or modify 6 // it under the terms of the GNU Lesser General Public License as published by 7 // the Free Software Foundation, either version 3 of the License, or 8 // (at your option) any later version. 9 // 10 // The go-ethereum library is distributed in the hope that it will be useful, 11 // but WITHOUT ANY WARRANTY; without even the implied warranty of 12 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 // GNU Lesser General Public License for more details. 14 // 15 // You should have received a copy of the GNU Lesser General Public License 16 // along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>. 17 // 18 // This file is derived from p2p/dial.go (2018/06/04). 19 // Modified and improved for the klaytn development. 20 21 package p2p 22 23 import ( 24 "container/heap" 25 "crypto/ecdsa" 26 "crypto/rand" 27 "errors" 28 "fmt" 29 "net" 30 "time" 31 32 "github.com/klaytn/klaytn/common/math" 33 "github.com/klaytn/klaytn/networks/p2p/discover" 34 "github.com/klaytn/klaytn/networks/p2p/netutil" 35 ) 36 37 const ( 38 // This is the amount of time spent waiting in between 39 // redialing a certain node. 40 dialHistoryExpiration = 30 * time.Second 41 42 // Discovery lookups are throttled and can only run 43 // once every few seconds. 44 lookupInterval = 4 * time.Second 45 46 // If no peers are found for this amount of time, the initial bootnodes are 47 // attempted to be connected. 48 fallbackInterval = 20 * time.Second 49 50 // Endpoint resolution is throttled with bounded backoff. 51 initialResolveDelay = 60 * time.Second 52 maxResolveDelay = time.Hour 53 ) 54 55 // NodeDialer is used to connect to nodes in the network, typically by using 56 // an underlying net.Dialer but also using net.Pipe in tests. 57 type NodeDialer interface { 58 Dial(*discover.Node) (net.Conn, error) 59 DialMulti(*discover.Node) ([]net.Conn, error) 60 } 61 62 // TCPDialer implements the NodeDialer interface by using a net.Dialer to 63 // create TCP connections to nodes in the network. 64 type TCPDialer struct { 65 *net.Dialer 66 } 67 68 // Dial creates a TCP connection to the node. 69 func (t TCPDialer) Dial(dest *discover.Node) (net.Conn, error) { 70 addr := &net.TCPAddr{IP: dest.IP, Port: int(dest.TCP)} 71 return t.Dialer.Dial("tcp", addr.String()) 72 } 73 74 // DialMulti creates TCP connections to the node. 75 func (t TCPDialer) DialMulti(dest *discover.Node) ([]net.Conn, error) { 76 var conns []net.Conn 77 if dest.TCPs != nil || len(dest.TCPs) != 0 { 78 conns = make([]net.Conn, 0, len(dest.TCPs)) 79 for _, tcp := range dest.TCPs { 80 addr := &net.TCPAddr{IP: dest.IP, Port: int(tcp)} 81 conn, err := t.Dialer.Dial("tcp", addr.String()) 82 conns = append(conns, conn) 83 if err != nil { 84 return nil, err 85 } 86 } 87 } 88 return conns, nil 89 } 90 91 // dialstate schedules dials and discovery lookups. 92 // it get's a chance to compute new tasks on every iteration 93 // of the main loop in Server.run. 94 type dialstate struct { 95 maxDynDials int 96 ntab discover.Discovery 97 netrestrict *netutil.Netlist 98 99 lookupRunning bool 100 typedLookupRunning map[dialType]bool 101 dialing map[discover.NodeID]connFlag 102 lookupBuf []*discover.Node // current discovery lookup results 103 randomNodes []*discover.Node // filled from Table 104 static map[discover.NodeID]*dialTask 105 hist *dialHistory 106 107 start time.Time // time when the dialer was first used 108 bootnodes []*discover.Node // default dials when there are no peers 109 110 tsMap map[dialType]typedStatic // tsMap holds typedStaticDial per dialType(discovery name) 111 } 112 113 // the dial history remembers recent dials. 114 type dialHistory []pastDial 115 116 // pastDial is an entry in the dial history. 117 type pastDial struct { 118 id discover.NodeID 119 exp time.Time 120 } 121 122 type task interface { 123 Do(Server) 124 } 125 126 // A dialTask is generated for each node that is dialed. Its 127 // fields cannot be accessed while the task is running. 128 type dialTask struct { 129 flags connFlag 130 dest *discover.Node 131 lastResolved time.Time 132 resolveDelay time.Duration 133 failedTry int 134 dialType dialType 135 } 136 137 // discoverTask runs discovery table operations. 138 // Only one discoverTask is active at any time. 139 // discoverTask.Do performs a random lookup. 140 type discoverTask struct { 141 results []*discover.Node 142 } 143 144 // A waitExpireTask is generated if there are no other tasks 145 // to keep the loop in Server.run ticking. 146 type waitExpireTask struct { 147 time.Duration 148 } 149 150 type typedStatic struct { 151 maxNodeCount int 152 maxTry int 153 } 154 155 type discoverTypedStaticTask struct { 156 name dialType 157 max int 158 results []*discover.Node 159 } 160 161 func (t *discoverTypedStaticTask) Do(srv Server) { 162 // newTasks generates a lookup task whenever typed static dials are 163 // necessary. Lookups need to take some time, otherwise the 164 // event loop spins too fast. 165 next := srv.AddLastLookup() 166 if now := time.Now(); now.Before(next) { 167 time.Sleep(next.Sub(now)) 168 } 169 srv.SetLastLookupToNow() 170 t.results = srv.GetNodes(convertDialT2NodeT(t.name), t.max) 171 logger.Trace("discoverTypedStaticTask", "result", len(t.results)) 172 } 173 174 func (t *discoverTypedStaticTask) String() string { 175 s := fmt.Sprintf("discover TypedStaticTask: max: %d", t.max) 176 if len(t.results) > 0 { 177 s += fmt.Sprintf(" (%d results)", len(t.results)) 178 } 179 return s 180 } 181 182 const ( 183 DT_UNLIMITED = dialType("DIAL_TYPE_UNLIMITED") 184 DT_CN = dialType("CN") 185 DT_PN = dialType("PN") 186 DT_EN = dialType("EN") 187 ) 188 189 type dialType string 190 191 func convertDialT2NodeT(dt dialType) discover.NodeType { 192 switch dt { 193 case DT_CN: 194 return discover.NodeTypeCN 195 case DT_PN: 196 return discover.NodeTypePN 197 default: 198 logger.Crit("Support only CN, PN for typed static dial", "DialType", dt) 199 } 200 return discover.NodeTypeUnknown 201 } 202 203 func newDialState(static []*discover.Node, bootnodes []*discover.Node, ntab discover.Discovery, maxdyn int, 204 netrestrict *netutil.Netlist, privateKey *ecdsa.PrivateKey, tsMap map[dialType]typedStatic, 205 ) *dialstate { 206 if tsMap == nil { 207 tsMap = make(map[dialType]typedStatic) 208 } 209 tsMap[DT_UNLIMITED] = typedStatic{maxTry: math.MaxInt64, maxNodeCount: math.MaxInt64} 210 211 s := &dialstate{ 212 maxDynDials: maxdyn, 213 ntab: ntab, 214 netrestrict: netrestrict, 215 static: make(map[discover.NodeID]*dialTask), 216 dialing: make(map[discover.NodeID]connFlag), 217 bootnodes: make([]*discover.Node, len(bootnodes)), 218 randomNodes: make([]*discover.Node, maxdyn/2), 219 hist: new(dialHistory), 220 tsMap: tsMap, 221 typedLookupRunning: make(map[dialType]bool), 222 } 223 copy(s.bootnodes, bootnodes) 224 225 if privateKey != nil { 226 selfNodeID := discover.PubkeyID(&privateKey.PublicKey) 227 228 for _, n := range static { 229 if selfNodeID != n.ID { 230 s.addStatic(n) 231 } else { 232 logger.Debug("[Dial] Ignored static node which has same id with myself", "mySelfID", selfNodeID) 233 } 234 } 235 return s 236 } 237 238 for _, n := range static { 239 s.addStatic(n) 240 } 241 return s 242 } 243 244 func (s *dialstate) addStatic(n *discover.Node) { 245 s.addTypedStatic(n, DT_UNLIMITED) 246 } 247 248 func (s *dialstate) addTypedStatic(n *discover.Node, dType dialType) { 249 // This overwrites the task instead of updating an existing 250 // entry, giving users the opportunity to force a resolve operation. 251 if s.static[n.ID] == nil { 252 logger.Trace("[Dial] Add TypedStatic", "node", n, "dialType", dType) 253 if dType != DT_UNLIMITED { 254 s.static[n.ID] = &dialTask{flags: staticDialedConn | trustedConn, dest: n, dialType: dType} 255 } else { 256 s.static[n.ID] = &dialTask{flags: staticDialedConn, dest: n, dialType: dType} 257 } 258 259 } 260 } 261 262 func (s *dialstate) removeStatic(n *discover.Node) { 263 // This removes a task so future attempts to connect will not be made. 264 delete(s.static, n.ID) 265 // This removes a previous dial timestamp so that application 266 // can force a server to reconnect with chosen peer immediately. 267 s.hist.remove(n.ID) 268 } 269 270 func (s *dialstate) newTasks(nRunning int, peers map[discover.NodeID]*Peer, now time.Time) []task { 271 if s.start.IsZero() { 272 s.start = now 273 } 274 275 var newtasks []task 276 addDialTask := func(flag connFlag, n *discover.Node) bool { 277 logger.Trace("[Dial] Try to add dialTask", "connFlag", flag, "node", n) 278 if err := s.checkDial(n, peers); err != nil { 279 logger.Trace("[Dial] Skipping dial candidate from discovery nodes", "id", n.ID, 280 "addr", &net.TCPAddr{IP: n.IP, Port: int(n.TCP)}, "err", err) 281 return false 282 } 283 s.dialing[n.ID] = flag 284 logger.Debug("[Dial] Add dial candidate from discovery nodes", "id", n.ID, "addr", 285 &net.TCPAddr{IP: n.IP, Port: int(n.TCP)}) 286 newtasks = append(newtasks, &dialTask{flags: flag, dest: n}) 287 return true 288 } 289 290 var needDynDials int 291 calcNeedDynDials := func() { 292 needDynDials = s.maxDynDials 293 for _, p := range peers { 294 if p.rws[ConnDefault].is(dynDialedConn) { 295 needDynDials-- 296 } 297 } 298 for _, flag := range s.dialing { 299 if flag&dynDialedConn != 0 { 300 needDynDials-- 301 } 302 } 303 } 304 305 addStaticDialTasks := func() { 306 cnt := make(map[dialType]int) 307 for _, t := range s.static { 308 cnt[t.dialType]++ 309 } 310 311 checkStaticDial := func(dt *dialTask, peers map[discover.NodeID]*Peer) error { 312 err := s.checkDial(dt.dest, peers) 313 if err != nil { 314 return err 315 } 316 317 sd := s.static[dt.dest.ID] 318 if sd.flags&staticDialedConn == 0 { 319 err := fmt.Errorf("dialer: can't check conntype except staticconn [connType : %d]", sd.flags) 320 logger.Error("[Dial] ", "err", err) 321 return err 322 } 323 324 typeSpec, ok := s.tsMap[dt.dialType] 325 if !ok { 326 err := fmt.Errorf("dialer: no data for typespec [%s]", dt.dialType) 327 logger.Error("[Dial] ", "err", err) 328 return err 329 } 330 331 if dt.failedTry > typeSpec.maxTry { 332 return errExpired 333 } 334 335 if cnt[dt.dialType] > s.tsMap[dt.dialType].maxNodeCount { 336 return errExceedMaxTypedDial 337 } 338 return nil 339 } 340 341 for id, t := range s.static { 342 err := checkStaticDial(t, peers) 343 switch err { 344 case errNotWhitelisted, errSelf: 345 logger.Info("[Dial] Removing static dial candidate from static nodes", "id", 346 t.dest.ID, "addr", &net.TCPAddr{IP: t.dest.IP, Port: int(t.dest.TCP)}, "err", err) 347 delete(s.static, t.dest.ID) 348 cnt[t.dialType]-- 349 case errExpired: 350 logger.Info("[Dial] Removing expired dial candidate from static nodes", "id", 351 t.dest.ID, "addr", &net.TCPAddr{IP: t.dest.IP, Port: int(t.dest.TCP)}, "dialType", t.dialType, 352 "dialCount", cnt[t.dialType], "err", err) 353 delete(s.static, t.dest.ID) 354 cnt[t.dialType]-- 355 case errExceedMaxTypedDial: 356 logger.Info("[Dial] Removing exceeded dial candidate from static nodes", "id", 357 t.dest.ID, "addr", &net.TCPAddr{IP: t.dest.IP, Port: int(t.dest.TCP)}, "dialType", t.dialType, 358 "dialCount", cnt[t.dialType], "err", err, 359 ) 360 delete(s.static, t.dest.ID) 361 cnt[t.dialType]-- 362 case nil: 363 s.dialing[id] = t.flags 364 newtasks = append(newtasks, t) 365 logger.Info("[Dial] Add dial candidate from static nodes", "id", t.dest.ID, 366 "NodeType", t.dest.NType, "ip", t.dest.IP, "mainPort", t.dest.TCP, "port", t.dest.TCPs) 367 default: 368 logger.Trace("[Dial] Skipped addStaticDial", "reason", err, "to", t.dest) 369 } 370 } 371 // 2. add typedStaticDiscoverTask 372 if s.ntab != nil { // Run DiscoveryTasks when only Discovery Mode 373 for k, ts := range s.tsMap { 374 if k != DT_UNLIMITED && !s.typedLookupRunning[k] && cnt[k] < ts.maxNodeCount { 375 logger.Debug("[Dial] Add new discoverTypedStaticTask", "name", k) 376 s.typedLookupRunning[k] = true 377 maxDiscover := ts.maxNodeCount - cnt[k] 378 newtasks = append(newtasks, &discoverTypedStaticTask{name: k, max: maxDiscover}) 379 } 380 } 381 } 382 } 383 384 // Compute number of dynamic dials necessary at this point. 385 calcNeedDynDials() 386 logger.Trace("[Dial] Dynamic Dials Remained Capacity", "needDynDials", needDynDials, "maxDynDials", s.maxDynDials) 387 388 // Expire the dial history on every invocation. 389 s.hist.expire(now) 390 391 // Create dials for static nodes if they are not connected. 392 addStaticDialTasks() 393 394 // Use random nodes from the table for half of the necessary 395 // dynamic dials. 396 randomCandidates := needDynDials / 2 397 if randomCandidates > 0 { 398 n := s.ntab.ReadRandomNodes(s.randomNodes, discover.NodeTypeEN) 399 for i := 0; i < randomCandidates && i < n; i++ { 400 if addDialTask(dynDialedConn, s.randomNodes[i]) { 401 needDynDials-- 402 } 403 } 404 } 405 // Create dynamic dials from random lookup results, removing tried 406 // items from the result buffer. 407 i := 0 408 for ; i < len(s.lookupBuf) && needDynDials > 0; i++ { 409 if addDialTask(dynDialedConn, s.lookupBuf[i]) { 410 needDynDials-- 411 } 412 } 413 s.lookupBuf = s.lookupBuf[:copy(s.lookupBuf, s.lookupBuf[i:])] 414 // Launch a discovery lookup if more candidates are needed. 415 if len(s.lookupBuf) < needDynDials && !s.lookupRunning { 416 s.lookupRunning = true 417 newtasks = append(newtasks, &discoverTask{}) 418 } 419 420 // Launch a timer to wait for the next node to expire if all 421 // candidates have been tried and no task is currently active. 422 // This should prevent cases where the dialer logic is not ticked 423 // because there are no pending events. 424 if nRunning == 0 && len(newtasks) == 0 && s.hist.Len() > 0 { 425 t := &waitExpireTask{s.hist.min().exp.Sub(now)} 426 newtasks = append(newtasks, t) 427 } 428 return newtasks 429 } 430 431 var ( 432 errSelf = errors.New("is self") 433 errAlreadyDialing = errors.New("already dialing") 434 errAlreadyConnected = errors.New("already connected") 435 errRecentlyDialed = errors.New("recently dialed") 436 errNotWhitelisted = errors.New("not contained in netrestrict whitelist") 437 errExpired = errors.New("is expired") 438 errExceedMaxTypedDial = errors.New("exceeded max typed dial") 439 errUpdateDial = errors.New("updated to be multichannel peer") 440 ) 441 442 func (s *dialstate) checkDial(n *discover.Node, peers map[discover.NodeID]*Peer) error { 443 _, dialing := s.dialing[n.ID] 444 switch { 445 case dialing: 446 return errAlreadyDialing 447 case peers[n.ID] != nil: 448 return errAlreadyConnected 449 case s.ntab != nil && n.ID == s.ntab.Self().ID: 450 return errSelf 451 case s.netrestrict != nil && !s.netrestrict.Contains(n.IP): 452 return errNotWhitelisted 453 case s.hist.contains(n.ID): 454 return errRecentlyDialed 455 } 456 return nil 457 } 458 459 func (s *dialstate) taskDone(t task, now time.Time) { 460 switch t := t.(type) { 461 case *dialTask: 462 s.hist.add(t.dest.ID, now.Add(dialHistoryExpiration)) 463 delete(s.dialing, t.dest.ID) 464 case *discoverTask: 465 s.lookupRunning = false 466 s.lookupBuf = append(s.lookupBuf, t.results...) 467 case *discoverTypedStaticTask: 468 logger.Trace("[Dial] discoverTypedStaticTask - done", "t.name", t.name, 469 "result count", len(t.results)) 470 s.typedLookupRunning[t.name] = false 471 for _, r := range t.results { 472 s.addTypedStatic(r, t.name) 473 } 474 } 475 } 476 477 func (t *dialTask) Do(srv Server) { 478 if t.dest.Incomplete() { 479 if !t.resolve(srv, t.dest.NType) { 480 return 481 } 482 } 483 var err error 484 if len(t.dest.TCPs) > 1 { 485 err = t.dialMulti(srv, t.dest) 486 } else { 487 err = t.dial(srv, t.dest) 488 } 489 490 if err != nil { 491 logger.Debug("[Dial] Failed dialing", "task", t, "err", err) 492 // Try resolving the ID of static nodes if dialing failed. 493 if _, ok := err.(*dialError); ok && t.flags&staticDialedConn != 0 && t.resolve(srv, t.dest.NType) { 494 if len(t.dest.TCPs) > 1 { 495 err = t.dialMulti(srv, t.dest) 496 } else { 497 err = t.dial(srv, t.dest) 498 } 499 } 500 501 // redial with updated connection 502 if err == errUpdateDial { 503 err = t.dialMulti(srv, t.dest) 504 } 505 506 if err != nil { 507 t.failedTry++ 508 } 509 } 510 } 511 512 // resolve attempts to find the current endpoint for the destination 513 // using discovery. 514 // 515 // Resolve operations are throttled with backoff to avoid flooding the 516 // discovery network with useless queries for nodes that don't exist. 517 // The backoff delay resets when the node is found. 518 func (t *dialTask) resolve(srv Server, nType discover.NodeType) bool { 519 if srv.CheckNilNetworkTable() { 520 logger.Debug("Can't resolve node", "id", t.dest.ID, "NodeType", nType, 521 "err", "discovery is disabled") 522 return false 523 } 524 if t.resolveDelay == 0 { 525 t.resolveDelay = initialResolveDelay 526 } 527 if time.Since(t.lastResolved) < t.resolveDelay { 528 return false 529 } 530 resolved := srv.Resolve(t.dest.ID, nType) 531 t.lastResolved = time.Now() 532 if resolved == nil { 533 t.resolveDelay *= 2 534 if t.resolveDelay > maxResolveDelay { 535 t.resolveDelay = maxResolveDelay 536 } 537 logger.Debug("Resolving node failed", "id", t.dest.ID, "newdelay", t.resolveDelay) 538 return false 539 } 540 // The node was found. 541 t.resolveDelay = initialResolveDelay 542 t.dest = resolved 543 logger.Debug("Resolved node", "id", t.dest.ID, "addr", &net.TCPAddr{IP: t.dest.IP, Port: int(t.dest.TCP)}) 544 return true 545 } 546 547 type dialError struct { 548 error 549 } 550 551 // dial performs the actual connection attempt. 552 func (t *dialTask) dial(srv Server, dest *discover.Node) error { 553 dialTryCounter.Inc(1) 554 logger.Debug("[Dial] Dialing node", "id", dest.ID, "addr", &net.TCPAddr{IP: dest.IP, Port: int(dest.TCP)}) 555 556 fd, err := srv.Dial(dest) 557 if err != nil { 558 dialFailCounter.Inc(1) 559 return &dialError{err} 560 } 561 mfd := newMeteredConn(fd, false) 562 return srv.SetupConn(mfd, t.flags, dest) 563 } 564 565 // dialMulti performs the actual connection attempt. 566 func (t *dialTask) dialMulti(srv Server, dest *discover.Node) error { 567 dialTryCounter.Inc(1) 568 addresses := make([]*net.TCPAddr, 0, len(dest.TCPs)) 569 for _, tcp := range dest.TCPs { 570 addresses = append(addresses, &net.TCPAddr{IP: dest.IP, Port: int(tcp)}) 571 } 572 logger.Debug("[Dial] Dialing node", "id", dest.ID, "addresses", addresses) 573 574 fds, err := srv.DialMulti(dest) 575 if err != nil { 576 dialFailCounter.Inc(1) 577 return &dialError{err} 578 } 579 580 var errorBackup error 581 for portOrder, fd := range fds { 582 mfd := newMeteredConn(fd, false) 583 dest.PortOrder = uint16(portOrder) 584 err := srv.SetupConn(mfd, t.flags, dest) 585 if err != nil { 586 errorBackup = err 587 } 588 } 589 if errorBackup != nil { 590 for _, fd := range fds { 591 fd.Close() 592 } 593 } 594 return errorBackup 595 } 596 597 func (t *dialTask) String() string { 598 return fmt.Sprintf("%v %x %v:%d", t.flags, t.dest.ID[:8], t.dest.IP, t.dest.TCP) 599 } 600 601 func (t *discoverTask) Do(srv Server) { 602 // newTasks generates a lookup task whenever dynamic dials are 603 // necessary. Lookups need to take some time, otherwise the 604 // event loop spins too fast. 605 next := srv.AddLastLookup() 606 if now := time.Now(); now.Before(next) { 607 logger.Trace("discoverTask sleep", "period", next.Sub(now)) 608 time.Sleep(next.Sub(now)) 609 } 610 logger.Trace("discoverTask wakeup") 611 srv.SetLastLookupToNow() 612 var target discover.NodeID 613 rand.Read(target[:]) 614 t.results = srv.Lookup(target, discover.NodeTypeEN) // TODO-Klaytn Supposed dynamicDial discover only en, but type have to get from argument. 615 } 616 617 func (t *discoverTask) String() string { 618 s := "discovery lookup" 619 if len(t.results) > 0 { 620 s += fmt.Sprintf(" (%d results)", len(t.results)) 621 } 622 return s 623 } 624 625 func (t waitExpireTask) Do(Server) { 626 time.Sleep(t.Duration) 627 } 628 629 func (t waitExpireTask) String() string { 630 return fmt.Sprintf("wait for dial hist expire (%v)", t.Duration) 631 } 632 633 // Use only these methods to access or modify dialHistory. 634 func (h dialHistory) min() pastDial { 635 return h[0] 636 } 637 638 func (h *dialHistory) add(id discover.NodeID, exp time.Time) { 639 heap.Push(h, pastDial{id, exp}) 640 } 641 642 func (h *dialHistory) remove(id discover.NodeID) bool { 643 for i, v := range *h { 644 if v.id == id { 645 heap.Remove(h, i) 646 return true 647 } 648 } 649 return false 650 } 651 652 func (h dialHistory) contains(id discover.NodeID) bool { 653 for _, v := range h { 654 if v.id == id { 655 return true 656 } 657 } 658 return false 659 } 660 661 func (h *dialHistory) expire(now time.Time) { 662 for h.Len() > 0 && h.min().exp.Before(now) { 663 heap.Pop(h) 664 } 665 } 666 667 // heap.Interface boilerplate 668 func (h dialHistory) Len() int { return len(h) } 669 func (h dialHistory) Less(i, j int) bool { return h[i].exp.Before(h[j].exp) } 670 func (h dialHistory) Swap(i, j int) { h[i], h[j] = h[j], h[i] } 671 func (h *dialHistory) Push(x interface{}) { 672 *h = append(*h, x.(pastDial)) 673 } 674 675 func (h *dialHistory) Pop() interface{} { 676 old := *h 677 n := len(old) 678 x := old[n-1] 679 *h = old[0 : n-1] 680 return x 681 }