github.com/keltia/go-ipfs@v0.3.8-0.20150909044612-210793031c63/p2p/discovery/mdns.go (about) 1 package discovery 2 3 import ( 4 "errors" 5 "io" 6 "io/ioutil" 7 golog "log" 8 "net" 9 "sync" 10 "time" 11 12 "github.com/ipfs/go-ipfs/Godeps/_workspace/src/github.com/cryptix/mdns" 13 ma "github.com/ipfs/go-ipfs/Godeps/_workspace/src/github.com/jbenet/go-multiaddr" 14 manet "github.com/ipfs/go-ipfs/Godeps/_workspace/src/github.com/jbenet/go-multiaddr-net" 15 16 "github.com/ipfs/go-ipfs/p2p/host" 17 "github.com/ipfs/go-ipfs/p2p/peer" 18 u "github.com/ipfs/go-ipfs/util" 19 ) 20 21 var log = u.Logger("mdns") 22 23 const ServiceTag = "discovery.ipfs.io" 24 25 type Service interface { 26 io.Closer 27 RegisterNotifee(Notifee) 28 UnregisterNotifee(Notifee) 29 } 30 31 type Notifee interface { 32 HandlePeerFound(peer.PeerInfo) 33 } 34 35 type mdnsService struct { 36 server *mdns.Server 37 service *mdns.MDNSService 38 host host.Host 39 40 lk sync.Mutex 41 notifees []Notifee 42 interval time.Duration 43 } 44 45 func getDialableListenAddrs(ph host.Host) ([]*net.TCPAddr, error) { 46 var out []*net.TCPAddr 47 for _, addr := range ph.Addrs() { 48 na, err := manet.ToNetAddr(addr) 49 if err != nil { 50 continue 51 } 52 tcp, ok := na.(*net.TCPAddr) 53 if ok { 54 out = append(out, tcp) 55 } 56 } 57 if len(out) == 0 { 58 return nil, errors.New("failed to find good external addr from peerhost") 59 } 60 return out, nil 61 } 62 63 func NewMdnsService(peerhost host.Host, interval time.Duration) (Service, error) { 64 65 // TODO: dont let mdns use logging... 66 golog.SetOutput(ioutil.Discard) 67 68 var ipaddrs []net.IP 69 port := 4001 70 71 addrs, err := getDialableListenAddrs(peerhost) 72 if err != nil { 73 log.Warning(err) 74 } else { 75 port = addrs[0].Port 76 for _, a := range addrs { 77 ipaddrs = append(ipaddrs, a.IP) 78 } 79 } 80 81 myid := peerhost.ID().Pretty() 82 83 info := []string{myid} 84 service, err := mdns.NewMDNSService(myid, ServiceTag, "", "", port, ipaddrs, info) 85 if err != nil { 86 return nil, err 87 } 88 89 // Create the mDNS server, defer shutdown 90 server, err := mdns.NewServer(&mdns.Config{Zone: service}) 91 if err != nil { 92 return nil, err 93 } 94 95 s := &mdnsService{ 96 server: server, 97 service: service, 98 host: peerhost, 99 interval: interval, 100 } 101 102 go s.pollForEntries() 103 104 return s, nil 105 } 106 107 func (m *mdnsService) Close() error { 108 return m.server.Shutdown() 109 } 110 111 func (m *mdnsService) pollForEntries() { 112 ticker := time.NewTicker(m.interval) 113 for { 114 select { 115 case <-ticker.C: 116 entriesCh := make(chan *mdns.ServiceEntry, 16) 117 go func() { 118 for entry := range entriesCh { 119 m.handleEntry(entry) 120 } 121 }() 122 123 qp := mdns.QueryParam{} 124 qp.Domain = "local" 125 qp.Entries = entriesCh 126 qp.Service = ServiceTag 127 qp.Timeout = time.Second * 5 128 129 err := mdns.Query(&qp) 130 if err != nil { 131 log.Error("mdns lookup error: ", err) 132 } 133 close(entriesCh) 134 } 135 } 136 } 137 138 func (m *mdnsService) handleEntry(e *mdns.ServiceEntry) { 139 mpeer, err := peer.IDB58Decode(e.Info) 140 if err != nil { 141 log.Warning("Error parsing peer ID from mdns entry: ", err) 142 return 143 } 144 145 if mpeer == m.host.ID() { 146 return 147 } 148 149 maddr, err := manet.FromNetAddr(&net.TCPAddr{ 150 IP: e.AddrV4, 151 Port: e.Port, 152 }) 153 if err != nil { 154 log.Warning("Error parsing multiaddr from mdns entry: ", err) 155 return 156 } 157 158 pi := peer.PeerInfo{ 159 ID: mpeer, 160 Addrs: []ma.Multiaddr{maddr}, 161 } 162 163 m.lk.Lock() 164 for _, n := range m.notifees { 165 n.HandlePeerFound(pi) 166 } 167 m.lk.Unlock() 168 } 169 170 func (m *mdnsService) RegisterNotifee(n Notifee) { 171 m.lk.Lock() 172 m.notifees = append(m.notifees, n) 173 m.lk.Unlock() 174 } 175 176 func (m *mdnsService) UnregisterNotifee(n Notifee) { 177 m.lk.Lock() 178 found := -1 179 for i, notif := range m.notifees { 180 if notif == n { 181 found = i 182 break 183 } 184 } 185 if found != -1 { 186 m.notifees = append(m.notifees[:found], m.notifees[found+1:]...) 187 } 188 m.lk.Unlock() 189 }