def resolve(uri): '''Resolve a URI using RFC3263 to list of (IP address, port) tuples each with its order, preference, transport and TTL information. The application can supply a list of supported protocols if needed.''' if not isinstance(uri, URI): uri = URI(uri) transport, target = uri.param['transport'] if 'transport' in uri.param else None, uri.param['maddr'] if 'maddr' in uri.param else uri.host numeric, port, result, naptr, srv, result = isIPv4(target), uri.port, None, None, None, None #@implements rfc3263 P6L10-P8L32 if transport: transports = [transport] # only the given transport is used elif numeric or port is not None: transports = [x for x in (_secproto if uri.secure else _unsecproto)] else: naptr = _query((target, dns.T_NAPTR)) if naptr: transports = map(lambda y: _rproto[y[1].lower()], sorted(map(lambda x: (x['RDATA']['ORDER'], x['RDATA']['SERVICE']), naptr), lambda a,b: a[0]-b[0])) if uri.secure: transports = filter(lambda x: x in _secproto, transports) if not transports: transports, naptr = _secproto, None # assume tls if not found; clear the naptr response else: srv = filter(lambda x: x[1], [(p, _query(('%s.%s'%(p, target), dns.T_SRV))) for p in [_xproto[x] for x in (_secproto if uri.secure else _unsecproto)]]) transports = [_rxproto[y[0]] for y in srv] or uri.secure and list(_secproto) or list(_unsecproto) #@implements rfc3263 P8L34-P9L31 if numeric: result = [(target, port or _proto[x][1], x) for x in transports] elif port is None: service = None if naptr: service = sorted(map(lambda x: (x['RDATA']['REPLACEMENT'].lower(), x['RDATA']['ORDER'], x['RDATA']['PREFERENCE'], x['RDATA']['SERVICE'].lower()), naptr), lambda a,b: a[1]-b[1]) elif transport: service = [('%s.%s'%(_xproto[transport], target), 0, 0, _proto[transport][0])] if not srv: srv = filter(lambda y: y[1], [(_rproto[a[3].lower()], _query((a[0], dns.T_SRV))) for a in service]) if service else [] if srv: out = sum([[sorted([(y['RDATA']['DOMAIN'].lower(), y['RDATA']['PRIORITY'], y['RDATA']['WEIGHT'], y['RDATA']['PORT'], x[0])], lambda a,b: a[1]-b[1]) for y in x[1]] for x in srv], []) result = sum([[(y['RDATA'], x[1], x[2]) for y in (_query((x[0], dns.T_A)) or [])] for x in [(x[0], x[3], x[4]) for x in sum(out, [])]], []) return result or [(x[0], port or _proto[x[1]][1], x[1]) for x in sum([[(a, b) for a in [x['RDATA'] for x in _query((target, dns.T_A))] ] for b in transports], [])] # finally do A record on target, if nothing else worked
def bootstrap(self, timeout=5, interval=30): """A generator to perform bootstrap function.""" candidates = self.candidates[:] # a copy of list of candidates while True: if _debug: print self.net.name, "bootstrap server=", self.server, "neighbors=", len( self.neighbors ), "candidates=", len(candidates) if not self.server and not self.neighbors and candidates: # more candidates but no more neighbors node = candidates.pop(0) if _debug: print "bootstrap trying node=", repr(node) if node.type == socket.SOCK_DGRAM and isMulticast(node.ip): yield self.net.send(Message(name="Discover:Request"), node=node) msg = yield self.net.get(lambda x: x.name == "Discover:Response" and x.multicast, timeout=timeout) else: if not isIPv4(node.ip): # is a IP address? node = Node(ip=socket.gethostbyname(node.ip), port=node.port, type=node.type, guid=node.guid) yield self.net.send(Message(name="Discover:Request"), node=node) msg = yield self.net.get( lambda x: x.name == "Discover:Response" and not x.multicast, timeout=timeout ) if msg: added = False for node in msg.neighbors: if ( node.hostport == msg.remote.hostport ): # whether msg.remote exists in msg.neighbors, which means remote is a server and we are already connected. if _debug: print "received neighbor", repr(node) self.neighbors.insert(0, node) # put this as most preferred neighbor. added = True else: if _debug: print "received candidate", repr(node) candidates.append(node) # put this as the next candidate if added: yield self.net.put( Message(name="Discover:Indication", node=self.node, neighbors=self.neighbors) ) # indicate change in client. else: if _debug: print "bootstrap did not receive response." elif not self.server and self.neighbors: # perform neighbor refresh yield dht.randomsleep(timeout) result = yield self.net.send(Message(name="Ping:Request"), node=self.neighbors[0], timeout=timeout) if not result: # no response received, remove the neighbor del self.neighbors[0] yield self.net.put( Message(name="Discover:Indication", node=self.node, neighbors=self.neighbors) ) # indicate change in client. elif not self.server and not self.neighbors and not candidates: candidates = self.candidates[:] yield dht.randomsleep(timeout) else: # just wait before trying again. yield dht.randomsleep(interval)
def resolve(uri, supported=('udp', 'tcp', 'tls'), secproto=('tls',)): '''Resolve a URI using RFC3263 to list of (IP address, port) tuples each with its order, preference, transport and TTL information. The application can supply a list of supported protocols if needed.''' if not isinstance(uri, URI): uri = URI(uri) transport = uri.param['transport'] if 'transport' in uri.param else None target = uri.param['maddr'] if 'maddr' in uri.param else uri.host numeric, port, naptr, srv, result = isIPv4(target), uri.port, None, None, None if uri.secure: supported = secproto # only support secproto for "sips" #@implements rfc3263 P6L10-P8L32 if transport: transports = (transport,) if transport in supported else () # only the given transport is used elif numeric or port is not None: transports = supported else: naptr = _query((target, dns.T_NAPTR)) if naptr: # find the first that is supported ordered = filter(lambda r: r[1] in supported, sorted(map(lambda r: (r['RDATA']['ORDER'], _rproto.get(r['RDATA']['SERVICE'].lower(), ''), r), naptr), lambda a,b: a[0]-b[0])) # filter out unsupported transports if ordered: selected = filter(lambda r: r[0] == ordered[0][0], ordered) # keep only top-ordered values, ignore rest transports, naptr = map(lambda r: r[1], selected), map(lambda r: r[2], selected) # unzip to transports and naptr values else: transports, naptr = supported, None # assume failure if not found; clear the naptr response if not naptr: # do not use "else", because naptr may be cleared in "if" srv = filter(lambda r: r[1], map(lambda p: (_rxproto.get(p, ''), _query(('%s.%s'%(p, target), dns.T_SRV))), map(lambda t: _xproto[t], supported))) if srv: transports = map(lambda s: s[0], srv) else: transports = supported #@implements rfc3263 P8L34-P9L31 if numeric: result = map(lambda t: (target, port or _proto[t][1], t), transports) elif port: result = sum(map(lambda t: map(lambda r: (r['RDATA'], port, t), _query((target, dns.T_A))), transports), []) else: service = None if naptr: service = sorted(map(lambda x: (x['RDATA']['REPLACEMENT'].lower(), x['RDATA']['ORDER'], x['RDATA']['PREFERENCE'], x['RDATA']['SERVICE'].lower()), naptr), lambda a,b: a[1]-b[1]) elif transport: service = [('%s.%s'%(_xproto[transport], target), 0, 0, _proto[transport][0])] if not srv: srv = filter(lambda y: y[1], map(lambda s: (_rproto[s[3].lower()], _query((s[0], dns.T_SRV))), service)) if service else [] if srv: out = list(sorted(sum(map(lambda s: map(lambda r: (r['RDATA']['DOMAIN'].lower(), r['RDATA']['PRIORITY'], r['RDATA']['WEIGHT'], r['RDATA']['PORT'], s[0]), s[1]), srv), []), lambda a,b: a[1]-b[1])) result = sum(map(lambda x: map(lambda y: (y['RDATA'], x[1], x[2]), (_query((x[0], dns.T_A)) or [])), map(lambda r: (r[0], r[3], r[4]), out)), []) return result or map(lambda x: (x[0], port or _proto[x[1]][1], x[1]), sum(map(lambda b: map(lambda a: (a, b), map(lambda x: x['RDATA'], _query((target, dns.T_A)))), transports), [])) # finally do A record on target, if nothing else worked
def bootstrap(self, timeout=5, interval=30): '''A generator to perform bootstrap function.''' candidates = self.candidates[:] # a copy of list of candidates while True: if _debug: print self.net.name, 'bootstrap server=', self.server, 'neighbors=', len( self.neighbors), 'candidates=', len(candidates) if not self.server and not self.neighbors and candidates: # more candidates but no more neighbors node = candidates.pop(0) if _debug: print 'bootstrap trying node=', repr(node) if node.type == socket.SOCK_DGRAM and isMulticast(node.ip): yield self.net.send(Message(name='Discover:Request'), node=node) msg = yield self.net.get( lambda x: x.name == 'Discover:Response' and x. multicast, timeout=timeout) else: if not isIPv4(node.ip): # is a IP address? node = Node(ip=socket.gethostbyname(node.ip), port=node.port, type=node.type, guid=node.guid) yield self.net.send(Message(name='Discover:Request'), node=node) msg = yield self.net.get( lambda x: x.name == 'Discover:Response' and not x. multicast, timeout=timeout) if msg: added = False for node in msg.neighbors: if node.hostport == msg.remote.hostport: # whether msg.remote exists in msg.neighbors, which means remote is a server and we are already connected. if _debug: print 'received neighbor', repr(node) self.neighbors.insert( 0, node) # put this as most preferred neighbor. added = True else: if _debug: print 'received candidate', repr(node) candidates.append( node) # put this as the next candidate if added: yield self.net.put( Message(name='Discover:Indication', node=self.node, neighbors=self.neighbors) ) # indicate change in client. else: if _debug: print 'bootstrap did not receive response.' elif not self.server and self.neighbors: # perform neighbor refresh yield dht.randomsleep(timeout) result = yield self.net.send(Message(name='Ping:Request'), node=self.neighbors[0], timeout=timeout) if not result: # no response received, remove the neighbor del self.neighbors[0] yield self.net.put( Message(name='Discover:Indication', node=self.node, neighbors=self.neighbors) ) # indicate change in client. elif not self.server and not self.neighbors and not candidates: candidates = self.candidates[:] yield dht.randomsleep(timeout) else: # just wait before trying again. yield dht.randomsleep(interval)