func (d *dht) see(key string, x *e3x.Exchange) ([]hashname.H, error) { c, err := x.Open(d.prefix+"see", false) if err != nil { return nil, err } defer c.Close() pkt := &lob.Packet{} pkt.Header().SetString("key", key) pkt.Header().SetBool("end", true) err = c.WritePacket(pkt) if err != nil { return nil, err } pkt, err = c.ReadPacket() if err != nil { return nil, err } v, _ := pkt.Header().Get("see") l, ok := v.([]string) if !ok { return nil, nil } h := make([]hashname.H, len(l)) for i, s := range l { h[i] = hashname.H(s) } return h, nil }
// Gets a list of the vnodes on the box func (t *transport) ListVnodes(hn string) ([]*chord.Vnode, error) { var ( addr *e3x.Addr ch *e3x.Channel res []*completeVnode err error ) addr = t.lookupAddr(hashname.H(hn)) if addr == nil { return nil, e3x.ErrNoAddress } ch, err = t.e.Open(addr, "chord.list", true) if err != nil { return nil, err } defer ch.Close() // ch.SetReadDeadline(time.Now().Add(30*time.Second)) // ch.SetWriteDeadline(time.Now().Add(30*time.Second)) err = ch.WritePacket(&lob.Packet{}) if err != nil { return nil, err } err = json.NewDecoder(newStream(ch)).Decode(&res) if err != nil { return nil, err } return t.internalVnodes(res), nil }
func (rt *RoundTripper) RoundTrip(req *http.Request) (*http.Response, error) { var ( hashname = hashname.H(req.URL.Host) c *e3x.Channel resp *http.Response err error ) x := rt.Endpoint.GetExchange(hashname) if x == nil { return nil, e3x.UnreachableEndpointError(hashname) } c, err = x.Open("thtp", true) if err != nil { c.Close() return nil, err } err = rt.writeRequest(req, c) if err != nil { c.Close() return nil, err } resp, err = rt.readResponse(c) if err != nil { c.Close() return nil, err } resp.Request = req return resp, nil }
func (i hashnameIdentifier) Identify(endpoint *Endpoint) (*Identity, error) { x := endpoint.hashnames[hashname.H(i)] if x == nil { return nil, ErrUnidentifiable } return x.RemoteIdentity(), nil }
func init() { transports.RegisterAddr(&peerAddr{}) transports.RegisterResolver("peer", func(addr string) (net.Addr, error) { hn := hashname.H(addr) if !hn.Valid() { return nil, transports.ErrInvalidAddr } return &peerAddr{hn}, nil }) }
func (a *peerAddr) UnmarshalJSON(p []byte) error { var desc struct { Type string `json:"type"` Hn string `json:"hn"` } err := json.Unmarshal(p, &desc) if err != nil { return err } a.router = hashname.H(desc.Hn) return nil }
func (t *transport) completeVnode(vn *chord.Vnode) *completeVnode { if vn == nil { return nil } id := hex.EncodeToString(vn.Id) t.mtx.Lock() defer t.mtx.Unlock() c := &completeVnode{id, t.addressTable[hashname.H(vn.Host)]} return c }
func (mod *module) handle_peer(ch *e3x.Channel) { defer ch.Kill() log := mod.log.From(ch.RemoteHashname()).To(mod.e.LocalHashname()) // MUST allow router role if mod.config.DisableRouter { log.Println("drop: router disabled") return } pkt, err := ch.ReadPacket() if err != nil { log.Printf("drop: failed to read packet: %s", err) return } peerStr, ok := pkt.Header().GetString("peer") if !ok { log.Printf("drop: no peer in packet") return } peer := hashname.H(peerStr) // MUST have link to either endpoint if mod.e.GetExchange(ch.RemoteHashname()) == nil && mod.e.GetExchange(peer) == nil { log.Printf("drop: no link to either peer") return } // MUST pass firewall if mod.config.AllowPeer != nil && !mod.config.AllowPeer(ch.RemoteHashname(), peer) { log.Printf("drop: blocked by firewall") return } ex := mod.e.GetExchange(peer) if ex == nil { log.Printf("drop: no exchange to target") // resolve? return } token := cipherset.ExtractToken(pkt.Body(nil)) if token != cipherset.ZeroToken { // add bridge back to requester mod.RouteToken(token, ch.Exchange()) } mod.connect(ex, bufpool.New().Set(pkt.Body(nil))) }
// Find a successor func (t *transport) FindSuccessors(vn *chord.Vnode, n int, k []byte) ([]*chord.Vnode, error) { var ( addr *e3x.Addr ch *e3x.Channel stream io.ReadWriteCloser res []*completeVnode err error req = struct { Target string N int K []byte }{vn.String(), n, k} ) // tracef("FindSuccessors(target:Vnode(%q))", vn.String()) addr = t.lookupAddr(hashname.H(vn.Host)) if addr == nil { return nil, e3x.ErrNoAddress } ch, err = t.e.Open(addr, "chord.successors.find", true) if err != nil { return nil, err } defer ch.Close() // ch.SetReadDeadline(time.Now().Add(30*time.Second)) // ch.SetWriteDeadline(time.Now().Add(30*time.Second)) stream = newStream(ch) err = json.NewEncoder(stream).Encode(&req) if err != nil { // tracef("(FindSuccessors) error: %s", err) return nil, err } err = json.NewDecoder(stream).Decode(&res) if err != nil { // tracef("(FindSuccessors) error: %s", err) return nil, err } return t.internalVnodes(res), nil }
func (t *transport) completeVnodes(vn []*chord.Vnode) []*completeVnode { if len(vn) == 0 { return nil } t.mtx.Lock() defer t.mtx.Unlock() c := make([]*completeVnode, len(vn)) for i, a := range vn { if a != nil { b := &completeVnode{hex.EncodeToString(a.Id), t.addressTable[hashname.H(a.Host)]} c[i] = b } } return c }
// Notify our successor of ourselves func (t *transport) Notify(target, self *chord.Vnode) ([]*chord.Vnode, error) { var ( addr *e3x.Addr ch *e3x.Channel stream io.ReadWriteCloser res []*completeVnode err error req = struct { Target string Self *completeVnode }{target.String(), t.completeVnode(self)} ) addr = t.lookupAddr(hashname.H(target.Host)) if addr == nil { return nil, e3x.ErrNoAddress } ch, err = t.e.Open(addr, "chord.notify", true) if err != nil { return nil, err } defer ch.Close() // ch.SetReadDeadline(time.Now().Add(30*time.Second)) // ch.SetWriteDeadline(time.Now().Add(30*time.Second)) stream = newStream(ch) err = json.NewEncoder(stream).Encode(&req) if err != nil { return nil, err } err = json.NewDecoder(stream).Decode(&res) if err != nil { return nil, err } // tracef("Notify(target:Vnode(%q), self:Vnode(%q)) => []Vnode(%v)", target.String(), self.String(), res) return t.internalVnodes(res), nil }
// Request a nodes predecessor func (t *transport) GetPredecessor(vn *chord.Vnode) (*chord.Vnode, error) { var ( addr *e3x.Addr ch *e3x.Channel pkt *lob.Packet res *completeVnode err error ) addr = t.lookupAddr(hashname.H(vn.Host)) if addr == nil { return nil, e3x.ErrNoAddress } ch, err = t.e.Open(addr, "chord.predecessor.get", true) if err != nil { return nil, err } defer ch.Close() // ch.SetReadDeadline(time.Now().Add(30*time.Second)) // ch.SetWriteDeadline(time.Now().Add(30*time.Second)) pkt = &lob.Packet{} pkt.Header().SetString("vn", vn.String()) err = ch.WritePacket(pkt) if err != nil { return nil, err } err = json.NewDecoder(newStream(ch)).Decode(&res) if err != nil { return nil, err } if res != nil { // tracef("GetPredecessor(Vnode(%q)) => Vnode(%q)", vn.String(), res.Id) } return t.internalVnode(res), nil }
// Ping a Vnode, check for liveness func (t *transport) Ping(vn *chord.Vnode) (bool, error) { var ( addr *e3x.Addr ch *e3x.Channel pkt *lob.Packet alive bool err error ) addr = t.lookupAddr(hashname.H(vn.Host)) if addr == nil { return false, e3x.ErrNoAddress } ch, err = t.e.Open(addr, "chord.ping", true) if err != nil { return false, err } defer ch.Close() // ch.SetReadDeadline(time.Now().Add(30*time.Second)) // ch.SetWriteDeadline(time.Now().Add(30*time.Second)) pkt = &lob.Packet{} pkt.Header().SetString("vn", vn.String()) err = ch.WritePacket(pkt) if err != nil { return false, err } pkt, err = ch.ReadPacket() if err != nil { return false, err } alive, _ = pkt.Header().GetBool("alive") // tracef("Ping(Vnode(%q)) => %v", vn.String(), alive) return alive, nil }
// Instructs a node to skip a given successor. Used to leave. func (t *transport) SkipSuccessor(target, self *chord.Vnode) error { var ( addr *e3x.Addr ch *e3x.Channel stream io.ReadWriteCloser err error req = struct { Target string Self *completeVnode }{target.String(), t.completeVnode(self)} ) addr = t.lookupAddr(hashname.H(target.Host)) if addr == nil { return e3x.ErrNoAddress } ch, err = t.e.Open(addr, "chord.successor.skip", true) if err != nil { return err } defer ch.Close() // ch.SetReadDeadline(time.Now().Add(30*time.Second)) // ch.SetWriteDeadline(time.Now().Add(30*time.Second)) stream = newStream(ch) err = json.NewEncoder(stream).Encode(&req) if err != nil { return err } return nil }
func resolveSRV(uri *URI, proto string) (*e3x.Identity, error) { // ignore port host, _, _ := net.SplitHostPort(uri.Canonical) if host == "" { host = uri.Canonical } // normalize if !strings.HasSuffix(host, ".") { host += "." } // ignore .public if strings.HasSuffix(host, ".public.") { return nil, &net.DNSError{Name: host, Err: "cannot resolve .public hostnames using DNS"} } // lookup SRV records _, srvs, err := net.LookupSRV("mesh", proto, host) if err != nil { return nil, err } if len(srvs) > 1 { return nil, &net.DNSError{Name: host, Err: "too many SRV records"} } if len(srvs) == 0 { return nil, &net.DNSError{Name: host, Err: "no SRV records"} } var ( srv = srvs[0] port = srv.Port portStr = strconv.Itoa(int(port)) hn hashname.H keys cipherset.Keys ) { // detect valid target parts := strings.SplitN(srv.Target, ".", 2) if len(parts) != 2 || len(parts[0]) != 52 || len(parts[1]) == 0 { return nil, &net.DNSError{Name: host, Err: "SRV must target a <hashname>.<domain> domain"} } hn = hashname.H(parts[0]) if !hn.Valid() { return nil, &net.DNSError{Name: host, Err: "SRV must target a <hashname>.<domain> domain"} } } // detect CNAMEs (they are not allowed) cname, err := net.LookupCNAME(srv.Target) if err != nil { return nil, err } if cname != "" && cname != srv.Target { return nil, &net.DNSError{Name: host, Err: "CNAME record are not allowed"} } // lookup A AAAA records ips, err := net.LookupIP(srv.Target) if err != nil { return nil, err } if len(ips) == 0 { return nil, &net.DNSError{Name: host, Err: "no A or AAAA records"} } // lookup TXT txts, err := net.LookupTXT(srv.Target) if err != nil { return nil, err } if len(txts) == 0 { return nil, &net.DNSError{Name: host, Err: "no TXT records"} } // make addrs addrs := make([]net.Addr, 0, len(ips)) for _, ip := range ips { var ( addr net.Addr ) switch proto { case "udp": addr, _ = transports.ResolveAddr("udp4", net.JoinHostPort(ip.String(), portStr)) if addr == nil { addr, _ = transports.ResolveAddr("udp6", net.JoinHostPort(ip.String(), portStr)) } case "tcp": addr, _ = transports.ResolveAddr("tcp4", net.JoinHostPort(ip.String(), portStr)) if addr == nil { addr, _ = transports.ResolveAddr("tcp6", net.JoinHostPort(ip.String(), portStr)) } // case "http": // addr, _ = http.NewAddr(ip, port) } if addr != nil { addrs = append(addrs, addr) } } { // parse keys // Sort txts so they form ascending sequences of key parts sort.Strings(txts) keyData := make(map[uint8]string, 10) for len(txts) > 0 { var ( txt = txts[0] parts = strings.Split(txt, "=") ) if len(parts) != 2 { txts = txts[1:] continue } var ( label = parts[0] value = parts[1] csid uint8 ) if len(label) < 2 { txts = txts[1:] continue } // parse the CSID portion of the label i, err := strconv.ParseUint(label[:2], 16, 8) if err != nil { txts = txts[1:] continue } csid = uint8(i) // verify the key-part portion of the label if len(label) > 2 { _, err = strconv.ParseUint(label[2:], 10, 8) if err != nil { txts = txts[1:] continue } } keyData[csid] += value txts = txts[1:] } keys = make(cipherset.Keys, len(keyData)) for csid, str := range keyData { key, err := cipherset.DecodeKey(csid, str, "") if err != nil { continue } keys[csid] = key } } ident, err := e3x.NewIdentity(keys, nil, addrs) if err != nil { return nil, err } if hn != ident.Hashname() { return nil, &net.DNSError{Name: host, Err: "invalid keys"} } return ident, nil }