func TestNodeTableInsertRemove(t *testing.T) { b := big.NewInt(int64(0)) var id [20]byte copy(id[:], b.Bytes()[:]) selfid := utils.NewNodeID(namespace, id) n := newNodeTable(50, selfid) ary := make([]utils.NodeID, 100) for i := 0; i < len(ary); i++ { b.Add(b, big.NewInt(int64(1))) var id [20]byte copy(id[:], b.Bytes()[:]) node := utils.NewNodeID(namespace, id) ary[i] = node } for _, id := range ary { n.insert(utils.NodeInfo{ID: id, Addr: nil}) } for _, id := range ary { if n.find(id) == nil { t.Errorf("%s not found", id.String()) } } for _, id := range ary { n.remove(id) if n.find(id) != nil { t.Errorf("%s should be removed", id.String()) } } }
func TestRouterMessageExchange(t *testing.T) { logger := log.NewLogger() msg := "The quick brown fox jumps over the lazy dog" key1 := utils.GeneratePrivateKey() key2 := utils.GeneratePrivateKey() router1, err := NewRouter(key1, logger, utils.DefaultConfig) if err != nil { t.Fatal(err) } router1.Discover(utils.DefaultConfig.Bootstrap()) router2, err := NewRouter(key2, logger, utils.DefaultConfig) if err != nil { t.Fatal(err) } router2.Discover(utils.DefaultConfig.Bootstrap()) time.Sleep(100 * time.Millisecond) router1.SendMessage(utils.NewNodeID(namespace, key2.Digest()), []byte(msg)) m, err := router2.RecvMessage() if err != nil { t.Errorf("router2: recvMessage() returns error") } if m.Node.Digest.Cmp(router1.key.Digest()) != 0 { t.Errorf("router2: wrong source id") } if string(m.Payload) != msg { t.Errorf("router2: wrong message body") } router2.SendMessage(utils.NewNodeID(namespace, router1.key.Digest()), []byte(msg)) m, err = router1.RecvMessage() if err != nil { t.Errorf("router1: recvMessage() returns error") } if m.Node.Digest.Cmp(router2.key.Digest()) != 0 { t.Errorf("router1: wrong source id") } if string(m.Payload) != msg { t.Errorf("router1: wrong message body") } router1.Close() router2.Close() }
func NewRouter(key *utils.PrivateKey, logger *log.Logger, config utils.Config) (*Router, error) { exit := make(chan int) listener, err := getOpenPortConn(config) if err != nil { return nil, err } logger.Info("Node ID: %s", key.Digest().String()) logger.Info("Node Socket: %v", listener.Addr()) ns := utils.GlobalNamespace id := utils.NewNodeID(ns, key.Digest()) r := Router{ id: id, listener: listener, key: key, sessions: make(map[utils.NodeID]*session), mainDht: dht.NewDHT(10, id, id, listener.RawConn, logger), groupDht: make(map[utils.NodeID]*dht.DHT), receivedPackets: make(map[[20]byte]int), logger: logger, recv: make(chan Message, 100), send: make(chan internal.Packet, 100), exit: exit, } go r.run() return &r, nil }
func (p *DHT) StoreNodes(key string, nodes []utils.NodeInfo) { hash := sha1.Sum([]byte(key)) b, err := msgpack.Marshal(nodes) if err != nil { return } c := p.newRPCCommand("store-node", map[string]interface{}{ "key": key, "value": string(b), }) for _, n := range p.FindNearestNode(utils.NewNodeID(p.id.NS, hash)) { p.sendPacket(n.ID, c) } t := newNodeTable(p.k, p.id) for _, n := range nodes { t.insert(n) } p.kvsMutex.RLock() msgpack.Unmarshal([]byte(p.kvs[key]), &nodes) p.kvsMutex.RUnlock() for _, n := range nodes { t.insert(n) } b, err = msgpack.Marshal(t.nodes()) if err == nil { p.kvsMutex.Lock() p.kvs[key] = string(b) p.kvsMutex.Unlock() } }
func (p *DHT) StoreValue(key string, value string) { hash := sha1.Sum([]byte(key)) c := p.newRPCCommand("store", map[string]interface{}{ "key": key, "value": value, }) for _, n := range p.FindNearestNode(utils.NewNodeID(p.id.NS, hash)) { p.sendPacket(n.ID, c) } }
func TestRouterRouteExchange(t *testing.T) { logger := log.NewLogger() msg := "The quick brown fox jumps over the lazy dog" key1 := utils.GeneratePrivateKey() key2 := utils.GeneratePrivateKey() key3 := utils.GeneratePrivateKey() router1, err := NewRouter(key1, logger, utils.DefaultConfig) if err != nil { t.Fatal(err) } defer router1.Close() router1.Discover(utils.DefaultConfig.Bootstrap()) router2, err := NewRouter(key2, logger, utils.DefaultConfig) if err != nil { t.Fatal(err) } defer router2.Close() router2.Discover(utils.DefaultConfig.Bootstrap()) time.Sleep(100 * time.Millisecond) router3, err := NewRouter(key3, logger, utils.DefaultConfig) if err != nil { t.Fatal(err) } defer router3.Close() addr, _ := net.ResolveUDPAddr("udp", router1.listener.Addr().String()) router3.Discover([]net.UDPAddr{net.UDPAddr{Port: addr.Port, IP: net.ParseIP("127.0.0.1")}}) time.Sleep(100 * time.Millisecond) router3.SendMessage(utils.NewNodeID(namespace, key1.Digest()), []byte(msg)) m, err := router1.RecvMessage() if err != nil { t.Errorf("router1: recvMessage() returns error") } if m.Node.Digest.Cmp(router3.key.Digest()) != 0 { t.Errorf("router1: wrong source id") } if string(m.Payload) != msg { t.Errorf("router1: wrong message body") } }
func (s *session) sendPubkey() error { data, err := msgpack.Marshal(s.lkey.PublicKey) if err != nil { return err } pkt := internal.Packet{ Src: utils.NewNodeID(utils.GlobalNamespace, s.lkey.Digest()), Type: "pubkey", Payload: data, } err = s.Write(pkt) if err != nil { return err } return nil }
// NewClient generates a Client with the given PrivateKey. func NewClient(key *utils.PrivateKey, config utils.Config) (*Client, error) { logger := log.NewLogger() r, err := router.NewRouter(key, logger, config) if err != nil { return nil, err } c := &Client{ router: r, readch: make(chan router.Message), mbuf: newMessageBuffer(128), id: utils.NewNodeID(utils.GlobalNamespace, key.Digest()), config: config, Logger: logger, } return c, nil }
func (s *session) sendCommonKey() ([]byte, error) { var key [32]byte _, err := rand.Read(key[:]) if err != nil { return nil, err } pkt := internal.Packet{ Src: utils.NewNodeID(utils.GlobalNamespace, s.lkey.Digest()), Type: "key", Payload: key[:], } err = s.Write(pkt) if err != nil { return nil, err } return key[:], nil }
func (s *session) verifyPubkey() error { s.conn.SetReadDeadline(time.Now().Add(time.Second * 2)) defer s.conn.SetReadDeadline(time.Time{}) r := msgpack.NewDecoder(s.r) var packet internal.Packet err := r.Decode(&packet) if err != nil { return err } if packet.Type == "pubkey" { var key utils.PublicKey err := msgpack.Unmarshal(packet.Payload, &key) if err == nil { id := utils.NewNodeID(utils.GlobalNamespace, key.Digest()) if id.Digest.Cmp(packet.Src.Digest) != 0 { return errors.New("receive wrong public key") } s.rkey = &key } } else { return errors.New("receive wrong packet") } return nil }
func TestRouterGroup(t *testing.T) { var config = utils.Config{ P: "9200-9300", B: []string{ "localhost:9200-9300", }, } logger := log.NewLogger() gkey1 := utils.GeneratePrivateKey() gkey2 := utils.GeneratePrivateKey() router1, err := NewRouter(utils.GeneratePrivateKey(), logger, config) if err != nil { t.Fatal(err) } router1.Join(utils.NewNodeID(utils.GroupNamespace, gkey1.Digest())) defer router1.Close() router2, err := NewRouter(utils.GeneratePrivateKey(), logger, config) if err != nil { t.Fatal(err) } router2.Join(utils.NewNodeID(utils.GroupNamespace, gkey1.Digest())) defer router2.Close() router3, err := NewRouter(utils.GeneratePrivateKey(), logger, config) if err != nil { t.Fatal(err) } router3.Join(utils.NewNodeID(utils.GroupNamespace, gkey1.Digest())) defer router3.Close() router4, err := NewRouter(utils.GeneratePrivateKey(), logger, config) if err != nil { t.Fatal(err) } router4.Join(utils.NewNodeID(utils.GroupNamespace, gkey2.Digest())) defer router4.Close() router5, err := NewRouter(utils.GeneratePrivateKey(), logger, config) if err != nil { t.Fatal(err) } router5.Join(utils.NewNodeID(utils.GroupNamespace, gkey2.Digest())) defer router5.Close() router1.Discover(utils.DefaultConfig.Bootstrap()) router2.Discover(utils.DefaultConfig.Bootstrap()) router3.Discover(utils.DefaultConfig.Bootstrap()) router4.Discover(utils.DefaultConfig.Bootstrap()) router5.Discover(utils.DefaultConfig.Bootstrap()) time.Sleep(100 * time.Millisecond) msg := "The quick brown fox jumps over the lazy dog" router3.SendMessage(utils.NewNodeID(utils.GroupNamespace, gkey1.Digest()), []byte(msg)) { m, err := router1.RecvMessage() if err != nil { t.Errorf("router1: recvMessage() returns error") } if m.Node.Digest.Cmp(router3.key.Digest()) != 0 { t.Errorf("router1: wrong source id") } if string(m.Payload) != msg { t.Errorf("router1: wrong message body") } } { m, err := router2.RecvMessage() if err != nil { t.Errorf("router2: recvMessage() returns error") } if m.Node.Digest.Cmp(router3.key.Digest()) != 0 { t.Errorf("router2: wrong source id") } if string(m.Payload) != msg { t.Errorf("router2: wrong message body") } } }
func main() { path := os.Getenv("TANGORPATH") if path == "" { path = os.Getenv("HOME") + "/.tangor" } config := getConfig(path) keyfile := flag.String("i", path+"/id_dsa", "Identity file") bootstrap := flag.String("b", "", "Additional bootstrap node") web := flag.Bool("web", false, "Open web browser") flag.Parse() color.Print("\n@{Gk} @{Yk} tangor @{Gk} @{|}\n\n") if len(*bootstrap) > 0 { config.B = append(config.B, *bootstrap) } key, err := getKey(*keyfile) if err != nil { color.Printf(" -> @{Rk}ERROR:@{|} %v\n", err) os.Exit(-1) } id := utils.NewNodeID(utils.GlobalNamespace, key.Digest()) color.Printf("Your ID: @{Wk} %s @{|}\n\n", id.String()) client, err := murcott.NewClient(key, config) if err != nil { panic(err) } defer client.Close() filename := filepath.Join(path, id.Digest.String()+".dat") data, err := ioutil.ReadFile(filename) if err == nil { client.UnmarshalBinary(data) } if *web { go webui() open.Run("http://localhost:3000") } exit := make(chan int) s := Session{cli: client} go func() { for { select { case <-exit: return case <-time.After(time.Minute): s.save(filename) } } }() s.bootstrap() s.commandLoop() s.save(filename) close(exit) }
func (s *Session) commandLoop() { var chatID *utils.NodeID go func() { for { m, src, err := s.cli.Read() if err != nil { return } if msg, ok := m.(murcott.ChatMessage); ok { /* if chatID == nil { chatID = &src color.Printf("\n -> Start a chat with @{Wk} %s @{|}\n\n", src.String()) } */ str := src.String() color.Printf("\r* @{Wk}%s@{|} %s\n", str[len(str)-8:], msg.Text()) fmt.Print("* ") } } }() bio := bufio.NewReader(os.Stdin) for { if chatID == nil { fmt.Print("> ") } else { fmt.Print("* ") } line, _, err := bio.ReadLine() if err != nil { return } c := strings.Split(string(line), " ") if len(c) == 0 || c[0] == "" { continue } switch c[0] { case "/chat": if len(c) != 2 { color.Printf(" -> @{Rk}ERROR:@{|} /chat takes 1 argument\n") } else { nid, err := utils.NewNodeIDFromString(c[1]) if err != nil { color.Printf(" -> @{Rk}ERROR:@{|} invalid ID\n") } else { s.cli.Join(nid) chatID = &nid color.Printf(" -> Start a chat with @{Wk} %s @{|}\n\n", nid.String()) } } case "/add": if len(c) != 2 { color.Printf(" -> @{Rk}ERROR:@{|} /add takes 1 argument\n") } else { nid, err := utils.NewNodeIDFromString(c[1]) if err != nil { color.Printf(" -> @{Rk}ERROR:@{|} invalid ID\n") } else { s.cli.Roster.Set(nid, murcott.UserProfile{}) } } case "/mkg": key := utils.GeneratePrivateKey() id := utils.NewNodeID(utils.GroupNamespace, key.Digest()) color.Printf("Group ID: @{Wk} %s @{|}\n\n", id.String()) case "/stat": color.Printf(" * NumGoroutine() = %d\n", runtime.NumGoroutine()) nodes := s.cli.ActiveSessions() color.Printf(" * active sessions (%d) *\n", len(nodes)) for _, n := range nodes { color.Printf(" %v\n", n) } list := s.cli.Roster.List() color.Printf(" * Roster (%d) *\n", len(list)) for _, n := range list { color.Printf(" %v %s \n", n, s.cli.Roster.Get(n).Nickname) } case "/end": if chatID != nil { color.Printf(" -> End current chat\n") s.cli.Leave(*chatID) chatID = nil } case "/exit", "/quit": color.Printf(" -> See you@{Kg}.@{Kr}.@{Ky}.@{|}\n") return case "/help": showHelp() default: if chatID == nil { color.Printf(" -> @{Rk}ERROR:@{|} unknown command\n") showHelp() } else { s.cli.SendMessage(*chatID, murcott.NewPlainChatMessage(string(line))) } } } }
func (s *session) ID() utils.NodeID { return utils.NewNodeID(utils.GlobalNamespace, s.rkey.Digest()) }
func (p *DHT) ProcessPacket(b []byte, addr net.Addr) { var c dhtRPCCommand err := msgpack.Unmarshal(b, &c) if err != nil { p.logger.Error("%v", err) return } ns := utils.GlobalNamespace if !bytes.Equal(p.net.NS[:], ns[:]) && p.net.Digest.Cmp(c.Net.Digest) != 0 { return } if p.id.Match(c.Src) { return } p.table.insert(utils.NodeInfo{ID: c.Src, Addr: addr}) switch c.Method { case "ping": p.logger.Info("%s: Receive DHT Ping from %s %v", p.id.String(), c.Src.String(), addr) p.sendPacket(c.Src, p.newRPCReturnCommand(c.ID, nil)) case "find-node": p.logger.Info("%s: Receive DHT Find-Node from %s", p.net.String(), c.Src.String()) if id, ok := c.Args["id"].(string); ok { args := map[string]interface{}{} nid, err := utils.NewNodeIDFromBytes([]byte(id)) if err != nil { p.logger.Error("find-node: %v", err) } else { nodes := append(p.table.nearestNodes(nid), p.groupTable.nearestNodes(nid)...) args["nodes"] = nodes p.sendPacket(c.Src, p.newRPCReturnCommand(c.ID, args)) } } case "store": p.logger.Info("%s: Receive DHT Store from %s", p.id.String(), c.Src.String()) if key, ok := c.Args["key"].(string); ok { if val, ok := c.Args["value"].(string); ok { p.kvsMutex.Lock() p.kvs[key] = val p.kvsMutex.Unlock() } } case "store-node": p.logger.Info("%s: Receive DHT Store-node from %s", p.id.String(), c.Src.String()) if key, ok := c.Args["key"].(string); ok { if val, ok := c.Args["value"].(string); ok { var nodes []utils.NodeInfo t := newNodeTable(p.k, p.id) p.kvsMutex.RLock() msgpack.Unmarshal([]byte(p.kvs[key]), &nodes) p.kvsMutex.RUnlock() for _, n := range nodes { t.insert(n) } msgpack.Unmarshal([]byte(val), &nodes) for _, n := range nodes { host, port, _ := net.SplitHostPort(n.Addr.String()) if !net.ParseIP(host).IsGlobalUnicast() { host, _, _ := net.SplitHostPort(addr.String()) global, _ := net.ResolveUDPAddr(n.Addr.Network(), net.JoinHostPort(host, port)) n.Addr = global } t.insert(n) } b, err := msgpack.Marshal(t.nodes()) if err == nil { p.kvsMutex.Lock() p.kvs[key] = string(b) p.kvsMutex.Unlock() } } } case "find-value": p.logger.Info("%s: Receive DHT Find-Value from %s", p.id.String(), c.Src.String()) if key, ok := c.Args["key"].(string); ok { args := map[string]interface{}{} p.kvsMutex.RLock() if val, ok := p.kvs[key]; ok { args["value"] = val } else { hash := sha1.Sum([]byte(key)) n := p.table.nearestNodes(utils.NewNodeID(c.Src.NS, hash)) args["nodes"] = n } p.kvsMutex.RUnlock() p.sendPacket(c.Src, p.newRPCReturnCommand(c.ID, args)) } case "": // callback id := string(c.ID) p.chmapMutex.Lock() defer p.chmapMutex.Unlock() if ch, ok := p.chmap[id]; ok { delete(p.chmap, id) ch <- dhtRPCReturn{command: c, addr: addr} } } }
func (p *DHT) LoadValue(key string) *string { p.kvsMutex.RLock() if v, ok := p.kvs[key]; ok { p.kvsMutex.RUnlock() return &v } p.kvsMutex.RUnlock() hash := sha1.Sum([]byte(key)) keyid := utils.NewNodeID(p.id.NS, hash) retch := make(chan *string, 2) reqch := make(chan utils.NodeID, 100) endch := make(chan struct{}, 100) nodes := p.table.nearestNodes(keyid) f := func(id utils.NodeID, keyid utils.NodeID, command dhtRPCCommand) { ret, err := p.sendAndWaitPacket(id, command) if err == nil { if val, ok := ret.command.Args["value"].(string); ok { retch <- &val } else if _, ok := ret.command.Args["nodes"]; ok { var nodes []utils.NodeInfo ret.command.getArgs("nodes", &nodes) dist := id.Digest.Xor(keyid.Digest) for _, n := range nodes { p.table.insert(n) if dist.Cmp(n.ID.Digest.Xor(keyid.Digest)) == 1 { reqch <- n.ID } } } } endch <- struct{}{} } if len(nodes) == 0 { return nil } for _, n := range nodes { reqch <- n.ID } count := 0 requested := make(map[utils.NodeID]struct{}) for { select { case id := <-reqch: if _, ok := requested[id]; !ok { requested[id] = struct{}{} c := p.newRPCCommand("find-value", map[string]interface{}{ "key": key, }) go f(id, keyid, c) count++ } case <-endch: count-- if count == 0 { select { case data := <-retch: return data default: return nil } } case data := <-retch: return data default: } } }