def testPromiscuousConnection(tdir, keysAndNames):
    # Simulating node to client connection
    alphaSighex, alphaPrikey, alphaVerhex, alphaPubkey, alphaName, betaSighex, \
    betaPrikey, betaVerhex, betaPubkey, betaName = keysAndNames
    alpha = RoadStack(name=alphaName,
                      ha=genHa(),
                      sigkey=alphaSighex,
                      prikey=hexlify(alphaPrikey),
                      auto=AutoMode.always,
                      basedirpath=tdir)

    beta = RoadStack(name=betaName,
                     ha=genHa(),
                     main=True,
                     sigkey=betaSighex,
                     prikey=hexlify(betaPrikey),
                     auto=AutoMode.always,
                     basedirpath=tdir)

    try:
        betaRemote = RemoteEstate(stack=alpha, ha=beta.ha)
        alpha.addRemote(betaRemote)

        alpha.join(uid=betaRemote.uid, cascade=True)

        handshake(alpha, beta)

        sendMsgs(alpha, beta, betaRemote)
    finally:
        cleanup(alpha, beta)
def testPromiscuousConnection(tdir, keysAndNames):
    # Simulating node to client connection
    alphaSighex, alphaPrikey, alphaVerhex, alphaPubkey, alphaName, betaSighex, \
    betaPrikey, betaVerhex, betaPubkey, betaName = keysAndNames
    alpha = RoadStack(name=alphaName,
                      ha=genHa(),
                      sigkey=alphaSighex,
                      prikey=hexlify(alphaPrikey),
                      auto=AutoMode.always,
                      basedirpath=tdir)

    beta = RoadStack(name=betaName,
                     ha=genHa(),
                     main=True,
                     sigkey=betaSighex,
                     prikey=hexlify(betaPrikey),
                     auto=AutoMode.always,
                     basedirpath=tdir)

    try:
        betaRemote = RemoteEstate(stack=alpha, ha=beta.ha)
        alpha.addRemote(betaRemote)

        alpha.join(uid=betaRemote.uid, cascade=True)

        handshake(alpha, beta)

        sendMsgs(alpha, beta, betaRemote)
    finally:
        cleanup(alpha, beta)
Esempio n. 3
0
def testPromiscuousConnection(tdir):
    alpha = RoadStack(name='alpha',
                      ha=genHa(),
                      auto=AutoMode.always,
                      basedirpath=tdir)

    beta = RoadStack(name='beta',
                     ha=genHa(),
                     main=True,
                     auto=AutoMode.always,
                     basedirpath=tdir)

    try:
        betaRemote = RemoteEstate(stack=alpha, ha=beta.ha)
        alpha.addRemote(betaRemote)

        alpha.join(uid=betaRemote.uid, cascade=True)

        handshake(alpha, beta)

        sendMsgs(alpha, beta, betaRemote)
    finally:
        cleanup(alpha, beta)
Esempio n. 4
0
def testPromiscuousConnection(tdir):
    alpha = RoadStack(name='alpha',
                      ha=genHa(),
                      auto=AutoMode.always,
                      basedirpath=tdir)

    beta = RoadStack(name='beta',
                     ha=genHa(),
                     main=True,
                     auto=AutoMode.always,
                     basedirpath=tdir)

    try:
        betaRemote = RemoteEstate(stack=alpha, ha=beta.ha)
        alpha.addRemote(betaRemote)

        alpha.join(uid=betaRemote.uid, cascade=True)

        handshake(alpha, beta)

        sendMsgs(alpha, beta, betaRemote)
    finally:
        cleanup(alpha, beta)
Esempio n. 5
0
class RStack(NetworkInterface):
    def __init__(self, *args, **kwargs):
        checkPortAvailable(kwargs['ha'])
        basedirpath = kwargs.get('basedirpath')

        authMode = kwargs.pop('auth_mode', None)
        kwargs['auto'] = self._getAuto(authMode)

        keep = RoadKeep(basedirpath=basedirpath,
                        stackname=kwargs['name'],
                        auto=kwargs.get('auto'),
                        baseroledirpath=basedirpath)  # type: RoadKeep
        kwargs['keep'] = keep
        localRoleData = keep.loadLocalRoleData()

        sighex = kwargs.pop('sighex', None) or localRoleData['sighex']
        if not sighex:
            (sighex, _), (prihex, _) = getEd25519AndCurve25519Keys()
        else:
            prihex = ed25519SkToCurve25519(sighex, toHex=True)
        kwargs['sigkey'] = sighex
        kwargs['prikey'] = prihex

        self.msgHandler = kwargs.pop('msgHandler', None)  # type: Callable
        # if no timeout is set then message will never timeout
        self.messageTimeout = kwargs.pop('messageTimeout', 0)

        self.raetStack = RoadStack(*args, **kwargs)

        if self.ha[1] != kwargs['ha'].port:
            error("the stack port number has changed, likely due to "
                  "information in the keep. {} passed {}, actual {}".format(
                      kwargs['name'], kwargs['ha'].port, self.ha[1]))
        self._created = time.perf_counter()
        self.coro = None

        self._conns = set()  # type: Set[str]

    def _getAuto(self, authMode):
        if authMode == AuthMode.ALLOW_ANY.value:
            return AutoMode.always
        if authMode == AuthMode.RESTRICTED.value:
            return AutoMode.never
        return None

    def __repr__(self):
        return self.name

    @property
    def name(self):
        return self.raetStack.name

    @property
    def remotes(self):
        return self.raetStack.remotes

    @property
    def created(self):
        return self._created

    @property
    def rxMsgs(self):
        return self.raetStack.rxMsgs

    @staticmethod
    def isRemoteConnected(r) -> bool:
        """
        A node is considered to be connected if it is joined, allowed and alived.

        :param r: the remote to check
        """
        return r.joined and r.allowed and r.alived

    @staticmethod
    def initLocalKeys(name, baseDir, sigseed, override=False):
        """
        Initialize RAET local keep. Write local role data to file.

        :param name: name of the node
        :param baseDir: base directory
        :param pkseed: seed to generate public and private key pair
        :param sigseed: seed to generate signing and verification key pair
        :param override: overwrite the local role.json file if already exists
        :return: tuple(public key, verification key)
        """
        rolePath = os.path.join(baseDir, name, "role", "local", "role.json")
        if os.path.isfile(rolePath):
            if not override:
                raise FileExistsError(
                    "Keys exists for local role {}".format(name))

        if sigseed and not isinstance(sigseed, bytes):
            sigseed = sigseed.encode()

        signer = Signer(sigseed)
        keep = RoadKeep(stackname=name, baseroledirpath=baseDir)
        sigkey, verkey = signer.keyhex, signer.verhex
        prikey, pubkey = ed25519SkToCurve25519(sigkey, toHex=True), \
                         ed25519PkToCurve25519(verkey, toHex=True)
        data = OrderedDict([("role", name), ("prihex", prikey),
                            ("sighex", sigkey)])
        keep.dumpLocalRoleData(data)
        return pubkey.decode(), verkey.decode()

    @staticmethod
    def initRemoteKeys(name, remoteName, baseDir, verkey, override=False):
        """
        Initialize RAET remote keep

        :param name: name of the node
        :param remoteName: name of the remote to store keys for
        :param baseDir: base directory
        :param pubkey: public key of the remote
        :param verkey: private key of the remote
        :param override: overwrite the role.remoteName.json file if it already
        exists.
        """
        rolePath = os.path.join(baseDir, name, "role", "remote",
                                "role.{}.json".format(remoteName))
        if os.path.isfile(rolePath):
            if not override:
                raise FileExistsError(
                    "Keys exists for remote role {}".format(remoteName))

        keep = RoadKeep(stackname=name, baseroledirpath=baseDir)
        data = OrderedDict([('role', remoteName), ('acceptance', 1),
                            ('pubhex', ed25519PkToCurve25519(verkey,
                                                             toHex=True)),
                            ('verhex', verkey)])
        keep.dumpRemoteRoleData(data, role=remoteName)

    def onHostAddressChanged(self):
        logger.debug(
            "{} clearing local data in keep as host address changed".format(
                self.name))
        self.raetStack.keep.clearLocalData()

    @staticmethod
    def areKeysSetup(name, baseDir):
        """
        Check that the local RAET keep has the values of role, sighex and prihex
        populated for the given node

        :param name: the name of the node to check the keys for
        :param baseDir: base directory of Plenum
        :return: whether the keys are setup
        """
        localRoleData = getLocalKeep(name=name, baseDir=baseDir)

        for key in ['role', 'sighex', 'prihex']:
            if localRoleData.get(key) is None:
                return False
        return True

    @staticmethod
    def learnKeysFromOthers(baseDir, name, others):
        pass

    @staticmethod
    def getHaFromLocal(name, basedirpath):
        localEstate = getLocalEstateData(name, basedirpath)
        if localEstate:
            return localEstate.get("ha")

    def tellKeysToOthers(self, others):
        pass

    def getRemote(self, name: str = None, ha: HA = None):
        """
        Find the remote by name or ha.

        :param name: the name of the remote to find
        :param ha: host address pair the remote to find
        :raises: RemoteNotFound
        """
        return self.findInRemotesByHA(ha) if ha else \
            self.findInRemotesByName(name)

    def connect(self,
                name=None,
                remoteId=None,
                ha=None,
                verKeyRaw=None,
                publicKeyRaw=None):
        """
        Connect to the node specified by name.

        :param name: name of the node to connect to
        :type name: str or (HA, tuple)
        :return: the uid of the remote estate, or None if a connect is not
            attempted
        """
        # if not self.isKeySharing:
        #     logger.debug("{} skipping join with {} because not key sharing".
        #                   format(self, name))
        #     return None
        if not remoteId and not ha:
            raise ValueError(
                'Either Host Address or Remote ID must be provided to connect to a node in Raet stack'
            )

        if remoteId:
            remote = self.remotes[remoteId]
            return self._doConnectRemote(remote, name)
        else:
            return self._doConnectByHA(ha, name)

    def _doConnectByHA(self, ha, name=None):
        remote = RemoteEstate(stack=self.raetStack, ha=ha)
        self.raetStack.addRemote(remote)
        return self._doConnectRemote(remote, name)

    def _doConnectRemote(self, remote, name=None):
        # updates the store time so the join timer is accurate
        self.updateStamp()
        self.raetStack.join(uid=remote.uid, cascade=True, timeout=30)
        logger.info("{} looking for {} at {}:{}".format(
            self, name or remote.name, *remote.ha),
                    extra={
                        "cli": "PLAIN",
                        "tags": ["node-looking"]
                    })
        return remote.uid

    def removeRemote(self, r):
        self.raetStack.removeRemote(r)

    def transmit(self, msg, uid, timeout=None):
        self.raetStack.transmit(msg, uid, timeout=timeout)

    @property
    def ha(self):
        return self.raetStack.ha

    def start(self):
        if not self.opened:
            self.open()
        logger.info("stack {} starting at {} in {} mode".format(
            self, self.ha, self.raetStack.keep.auto),
                    extra={"cli": False})
        # self.coro = self._raetcoro()
        self.coro = self._raetcoro

    def stop(self):
        if self.opened:
            self.close()
        self.coro = None
        logger.info("stack {} stopped".format(self.name), extra={"cli": False})

    async def service(self, limit=None) -> int:
        """
        Service `limit` number of received messages in this stack.

        :param limit: the maximum number of messages to be processed. If None,
        processes all of the messages in rxMsgs.
        :return: the number of messages processed.
        """
        pracLimit = limit if limit else sys.maxsize
        if self.coro:
            # x = next(self.coro)
            x = await self.coro()
            if x > 0:
                for x in range(pracLimit):
                    try:
                        self.msgHandler(self.raetStack.rxMsgs.popleft())
                    except IndexError:
                        break
            return x
        else:
            logger.debug("{} is stopped".format(self))
            return 0

    # def _raetcoro(self):
    #     """
    #     Generator to service all messages.
    #     Yields the length of rxMsgs queue of this stack.
    #     """
    #     while True:
    #         try:
    #             self._serviceStack(self.age)
    #             l = len(self.rxMsgs)
    #         except Exception as ex:
    #             if isinstance(ex, OSError) and \
    #                     len(ex.args) > 0 and \
    #                     ex.args[0] == 22:
    #                 logger.error("Error servicing stack {}: {}. This could be "
    #                              "due to binding to an internal network "
    #                              "and trying to route to an external one.".
    #                              format(self.name, ex), extra={'cli': 'WARNING'})
    #             else:
    #                 logger.error("Error servicing stack {}: {} {}".
    #                              format(self.name, ex, ex.args),
    #                              extra={'cli': 'WARNING'})
    #
    #             l = 0
    #         yield l

    async def _raetcoro(self):
        try:
            await self._serviceStack(self.age)
            l = len(self.raetStack.rxMsgs)
        except Exception as ex:
            if isinstance(ex, OSError) and \
                    len(ex.args) > 0 and \
                    ex.args[0] == 22:
                logger.error("Error servicing stack {}: {}. This could be "
                             "due to binding to an internal network "
                             "and trying to route to an external one.".format(
                                 self.name, ex),
                             extra={'cli': 'WARNING'})
            else:
                logger.error("Error servicing stack {}: {} {}".format(
                    self.name, ex, ex.args),
                             extra={'cli': 'WARNING'})

            l = 0
        return l

    async def _serviceStack(self, age):
        """
        Update stacks clock and service all tx and rx messages.

        :param age: update timestamp of this RoadStack to this value
        """
        self.updateStamp(age)
        self.raetStack.serviceAll()

    def updateStamp(self, age=None):
        """
        Change the timestamp of this stack's test store.

        :param age: the timestamp will be set to this value
        """
        self.raetStack.store.changeStamp(age if age else self.age)

    @property
    def opened(self):
        return self.raetStack.server.opened

    def open(self):
        """
        Open the UDP socket of this stack's server.
        """
        self.raetStack.server.open()  # close the UDP socket

    def close(self):
        """
        Close the UDP socket of this stack's server.
        """
        self.raetStack.server.close()  # close the UDP socket

    @property
    def isKeySharing(self):
        return self.raetStack.keep.auto != AutoMode.never

    @property
    def verhex(self):
        return self.raetStack.local.signer.verhex

    @property
    def keyhex(self):
        return self.raetStack.local.signer.keyhex

    @property
    def pubhex(self):
        return self.raetStack.local.priver.pubhex

    @property
    def prihex(self):
        return self.raetStack.local.priver.keyhex

    def send(self, msg: Any, remoteName: str, ha=None):
        """
        Transmit the specified message to the remote specified by `remoteName`.

        :param msg: a message
        :param remoteName: the name of the remote
        """
        rid = self.getRemote(remoteName, ha).uid
        # Setting timeout to never expire
        self.raetStack.transmit(msg, rid, timeout=self.messageTimeout)