Example #1
0
    def doLog(self, message):
        """
        Process a log line from cjdns to see if it indicates that a peer we are
        responsible for is unresponsive.

        """

        logging.debug(message)

        # Short-circuit messages that start with the wrong l;etter and can't
        # possibly match.
        if message[0] != 'P': return

        # Test plausible messages against the regex
        p = IS_UNRESPONSIVE.match(message)

        # If they don't match, ignore them.
        if not p: return

        # Otherwise, get the key of the unresponsive node from the regex match
        # group.
        downKey = p.group(1)

        # And get the nodfe for that key
        node = self.nodes.get(downKey, None)

        if not node:
            # Complain we aren't responsible for that node.
            logging.debug("Unmonitored neighbor {} is down".format(
                PublicToIp6_convert(downKey)))
            return

        # Otherwise, announce we are doing our job.
        logging.warning("Monitored neighbor {} is down.".format(
            PublicToIp6_convert(downKey)))

        # If we are responsible for it, register it as unresponsive.
        self.unresponsive[downKey] = node

        # Go get its address and try reconnecting.
        self.lookup(node)
Example #2
0
def makeGraph():
    import cjdnsadmin.adminTools as admin
    import networkx as nx
    from cjdnsadmin.publicToIp6 import PublicToIp6_convert
    from collections import deque

    cjdns = admin.connect()
    root = admin.whoami(cjdns)
    rootIP = root['IP']

    G = nx.Graph()
    G.add_node(rootIP[-4:], ip=rootIP)

    nodes = deque()
    nodes.append(rootIP)
    while len(nodes) != 0:
        parentIP = nodes.popleft()
        resp = cjdns.NodeStore_nodeForAddr(parentIP)
        numLinks = 0
        if b'result' in resp:
            link = resp[b'result']
            if b'linkCount' in link:
                numLinks = int(resp[b'result'][b'linkCount'])
                G.nodes[parentIP[-4:]]['version'] = resp[b'result'][
                    b'protocolVersion']

        for i in range(0, numLinks):
            resp = cjdns.NodeStore_getLink(i, parent=parentIP)
            childLink = resp[b'result']
            if not childLink: continue
            childAddr = admin.parseAddr(childLink[b'child'])
            childIP = PublicToIp6_convert(childAddr[b'publicKey'])
            # Check to see if its one hop away from parent node
            if childLink[b'isOneHop'] != 1:
                continue
            # If its a new node then we want to follow it
            if not childIP[-4:] in G.nodes():
                G.add_node(childIP[-4:], ip=childIP)
                G.nodes[childIP[-4:]]['version'] = 0
                nodes.append(childIP)
            # If there is not a link between the nodes we should put one there
            if (not childIP[-4:] in G[parentIP[-4:]]):
                G.add_edge(parentIP[-4:], childIP[-4:])

    return G
Example #3
0
    def lookup(self, node):
        """
        Look up the current IP address for the given Node object, and tell the
        cjdns router to try to connect to it.

        """

        try:

            # Use AF_INET here to make sure we don't get an IPv6 address and try
            # to connect to it when the cjdns UDPInterface is using only IPv4.
            # TODO: Make cjdns bind its UDPInterface to IPv6 as well as IPv4.
            for info in socket.getaddrinfo(node.host, node.port,
                                           socket.AF_INET, socket.SOCK_DGRAM):

                # For every IP address the node has in DNS, with the port we
                # wanted attached...

                # Save the address we get in a field in the node.
                sockaddr = info[-1]
                node.lastAddr = sockaddr

                # Grab the IP:port string
                sockaddr = sockaddr[0] + ":" + str(sockaddr[1])

                # Announce we are going to connect
                logging.info("Connecting to {} at {}".format(
                    PublicToIp6_convert(node.key), sockaddr))

                # Tell CJDNS to begin a UDPInterface connection to the given
                # IP:port, with the given public key and password. Always use
                # the 0th UDPInterface, which is the default.
                reply = self.cjdns.UDPInterface_beginConnection(
                    password=node.password,
                    publicKey=node.key,
                    address=sockaddr)

                if reply["error"] != "none":
                    # The router didn't like our request. Complain.
                    logging.error(
                        "Router refuses to connect to remote peer. {}".format(
                            reply["error"]))

                    # Maybe try the next address?
                    break

                # Mark this node as no longer unresponsive
                try:
                    del self.unresponsive[node.key]
                except KeyError:
                    pass

                # Mark this node as no longer unresolved
                try:
                    del self.unresolved[node.key]
                except KeyError:
                    pass

                # Don't try any more addresses. Stop after the first.
                return

        except socket.gaierror as e:
            if IS_TRANSIENT_HOSTNAME.match(node.host):
                logging.info(
                    "Transient name {} is currently not resolvable.".format(
                        node.host))
                self.unresolved[node.key] = node
                # return and try this node later again
                return
            else:
                # The lookup failed at the OS level. Did we put in a bad hostname?
                logging.error("Could not resolve DNS name {}: {}".format(
                    node.host, e))

        # If we get here, we found no addresses that worked.
        logging.error("No working addresses found for node {}".format(
            PublicToIp6_convert(node.key)))