main.go 5.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209
  1. package main
  2. import (
  3. "bytes"
  4. "encoding/base64"
  5. "encoding/hex"
  6. "fmt"
  7. "github.com/ethereum/go-ethereum/crypto"
  8. "github.com/ethereum/go-ethereum/p2p/discover"
  9. "github.com/ethereum/go-ethereum/p2p/enode"
  10. "github.com/ethereum/go-ethereum/p2p/enr"
  11. "github.com/ethereum/go-ethereum/params"
  12. "github.com/ethereum/go-ethereum/rlp"
  13. "gopkg.in/urfave/cli.v1"
  14. "net"
  15. "os"
  16. "path/filepath"
  17. "sort"
  18. "strings"
  19. )
  20. var app = &cli.App{
  21. Name: filepath.Base(os.Args[0]),
  22. Usage: "p2p tool",
  23. Writer: os.Stdout,
  24. }
  25. var (
  26. bootnodesFlag = cli.StringFlag{
  27. Name: "bootnodes",
  28. Usage: "Comma separated nodes used for bootstrapping",
  29. }
  30. nodekeyFlag = cli.StringFlag{
  31. Name: "nodekey",
  32. Usage: "Hex-encoded node key",
  33. }
  34. nodedbFlag = cli.StringFlag{
  35. Name: "nodedb",
  36. Usage: "Nodes database location",
  37. }
  38. listenAddrFlag = cli.StringFlag{
  39. Name: "addr",
  40. Usage: "Listening address",
  41. }
  42. )
  43. func main() {
  44. app.CommandNotFound = func(ctx *cli.Context, cmd string) {
  45. fmt.Fprintf(os.Stderr, "No such command: %s\n", cmd)
  46. os.Exit(1)
  47. }
  48. app.Commands = []cli.Command{
  49. onePeerCommand,
  50. }
  51. exit(app.Run(os.Args))
  52. }
  53. func exit(err interface{}) {
  54. if err == nil {
  55. os.Exit(0)
  56. }
  57. fmt.Fprintln(os.Stderr, err)
  58. os.Exit(1)
  59. }
  60. // commandHasFlag returns true if the current command supports the given flag.
  61. func commandHasFlag(ctx *cli.Context, flag cli.Flag) bool {
  62. flags := ctx.FlagNames()
  63. sort.Strings(flags)
  64. i := sort.SearchStrings(flags, flag.GetName())
  65. return i != len(flags) && flags[i] == flag.GetName()
  66. }
  67. // startV4 starts an ephemeral discovery V4 node.
  68. func startV4(ctx *cli.Context) *discover.UDPv4 {
  69. ln, config := makeDiscoveryConfig(ctx)
  70. socket := listen(ln, ctx.String(listenAddrFlag.Name))
  71. disc, err := discover.ListenV4(socket, ln, config)
  72. if err != nil {
  73. exit(err)
  74. }
  75. return disc
  76. }
  77. func makeDiscoveryConfig(ctx *cli.Context) (*enode.LocalNode, discover.Config) {
  78. var cfg discover.Config
  79. if ctx.IsSet(nodekeyFlag.Name) {
  80. key, err := crypto.HexToECDSA(ctx.String(nodekeyFlag.Name))
  81. if err != nil {
  82. exit(fmt.Errorf("-%s: %v", nodekeyFlag.Name, err))
  83. }
  84. cfg.PrivateKey = key
  85. } else {
  86. cfg.PrivateKey, _ = crypto.GenerateKey()
  87. }
  88. if commandHasFlag(ctx, bootnodesFlag) {
  89. bn, err := parseBootnodes(ctx)
  90. if err != nil {
  91. exit(err)
  92. }
  93. cfg.Bootnodes = bn
  94. }
  95. dbpath := ctx.String(nodedbFlag.Name)
  96. db, err := enode.OpenDB(dbpath)
  97. if err != nil {
  98. exit(err)
  99. }
  100. ln := enode.NewLocalNode(db, cfg.PrivateKey)
  101. return ln, cfg
  102. }
  103. func listen(ln *enode.LocalNode, addr string) *net.UDPConn {
  104. if addr == "" {
  105. addr = "0.0.0.0:0"
  106. }
  107. socket, err := net.ListenPacket("udp4", addr)
  108. if err != nil {
  109. exit(err)
  110. }
  111. usocket := socket.(*net.UDPConn)
  112. uaddr := socket.LocalAddr().(*net.UDPAddr)
  113. if uaddr.IP.IsUnspecified() {
  114. ln.SetFallbackIP(net.IP{127, 0, 0, 1})
  115. } else {
  116. ln.SetFallbackIP(uaddr.IP)
  117. }
  118. ln.SetFallbackUDP(uaddr.Port)
  119. return usocket
  120. }
  121. func parseBootnodes(ctx *cli.Context) ([]*enode.Node, error) {
  122. s := params.MainnetBootnodes
  123. if ctx.IsSet(bootnodesFlag.Name) {
  124. input := ctx.String(bootnodesFlag.Name)
  125. if input == "" {
  126. return nil, nil
  127. }
  128. s = strings.Split(input, ",")
  129. }
  130. nodes := make([]*enode.Node, len(s))
  131. var err error
  132. for i, record := range s {
  133. nodes[i], err = parseNode(record)
  134. if err != nil {
  135. return nil, fmt.Errorf("invalid bootstrap node: %v", err)
  136. }
  137. }
  138. return nodes, nil
  139. }
  140. // parseNode parses a node record and verifies its signature.
  141. func parseNode(source string) (*enode.Node, error) {
  142. if strings.HasPrefix(source, "enode://") {
  143. return enode.ParseV4(source)
  144. }
  145. r, err := parseRecord(source)
  146. if err != nil {
  147. return nil, err
  148. }
  149. return enode.New(enode.ValidSchemes, r)
  150. }
  151. // parseRecord parses a node record from hex, base64, or raw binary input.
  152. func parseRecord(source string) (*enr.Record, error) {
  153. bin := []byte(source)
  154. if d, ok := decodeRecordHex(bytes.TrimSpace(bin)); ok {
  155. bin = d
  156. } else if d, ok := decodeRecordBase64(bytes.TrimSpace(bin)); ok {
  157. bin = d
  158. }
  159. var r enr.Record
  160. err := rlp.DecodeBytes(bin, &r)
  161. return &r, err
  162. }
  163. func decodeRecordHex(b []byte) ([]byte, bool) {
  164. if bytes.HasPrefix(b, []byte("0x")) {
  165. b = b[2:]
  166. }
  167. dec := make([]byte, hex.DecodedLen(len(b)))
  168. _, err := hex.Decode(dec, b)
  169. return dec, err == nil
  170. }
  171. func decodeRecordBase64(b []byte) ([]byte, bool) {
  172. if bytes.HasPrefix(b, []byte("enr:")) {
  173. b = b[4:]
  174. }
  175. dec := make([]byte, base64.RawURLEncoding.DecodedLen(len(b)))
  176. n, err := base64.RawURLEncoding.Decode(dec, b)
  177. return dec[:n], err == nil
  178. }
  179. // getNodeArg handles the common case of a single node descriptor argument.
  180. func getNodeArg(ctx *cli.Context) *enode.Node {
  181. if ctx.NArg() < 1 {
  182. exit("missing node as command-line argument")
  183. }
  184. n, err := parseNode(ctx.Args()[0])
  185. if err != nil {
  186. exit(err)
  187. }
  188. return n
  189. }