Ejemplo n.º 1
0
    def handle_udp(self, conn, name, enip_process, **kwds):
        """
        Process UDP packets from multiple clients
        """
        with parser.enip_machine(name=name, context='enip') as machine:
            while not kwds['server']['control']['done'] and not kwds['server']['control']['disable']:
                try:
                    source = cpppo.rememberable()
                    data = cpppo.dotdict()

                    # If no/partial EtherNet/IP header received, parsing will fail with a NonTerminal
                    # Exception (dfa exits in non-terminal state).  Build data.request.enip:
                    begun = cpppo.timer()  # waiting for next transaction
                    addr, stats = None, None
                    with contextlib.closing(machine.run(
                            path='request', source=source, data=data)) as engine:
                        # PyPy compatibility; avoid deferred destruction of generators
                        for mch, sta in engine:
                            if sta is not None:
                                # No more transitions available.  Wait for input.
                                continue
                            assert not addr, "Incomplete UDP request from client %r" % (addr)
                            msg = None
                            while msg is None:
                                # For UDP, we'll allow no input only at the start of a new request parse
                                # (addr is None); anything else will be considered a failed request Back
                                # to the trough for more symbols, after having already received a packet
                                # from a peer?  No go!
                                wait = (kwds['server']['control']['latency']
                                        if source.peek() is None else 0)
                                brx = cpppo.timer()
                                msg, frm = network.recvfrom(conn, timeout=wait)
                                now = cpppo.timer()
                                if not msg:
                                    if kwds['server']['control']['done'] or kwds['server']['control']['disable']:
                                        return
                                (logger.info if msg else logger.debug)(
                                    "Transaction receive after %7.3fs (%5s bytes in %7.3f/%7.3fs): %r",
                                    now - begun, len(msg) if msg is not None else "None",
                                    now - brx, wait, self.stats_for(frm)[0])
                                # If we're at a None (can't proceed), and we haven't yet received input,
                                # then this is where we implement "Blocking"; we just loop for input.

                            # We have received exactly one packet from an identified peer!
                            begun = now
                            addr = frm
                            stats, _ = self.stats_for(addr)
                            # For UDP, we don't ever receive incoming EOF, or set stats['eof'].
                            # However, we can respond to a manual eof (eg. from web interface) by
                            # ignoring the peer's packets.
                            assert stats and not stats.get('eof'), \
                                "Ignoring UDP request from client %r: %r" % (addr, msg)
                            stats['received'] += len(msg)
                            logger.debug("%s recv: %5d: %s", machine.name_centered(), len(msg), cpppo.reprlib.repr(msg))
                            source.chain(msg)

                    # Terminal state and EtherNet/IP header recognized; process and return response
                    assert stats
                    if 'request' in data:
                        stats['requests'] += 1
                    # enip_process must be able to handle no request (empty data), indicating the
                    # clean termination of the session if closed from this end (not required if
                    # enip_process returned False, indicating the connection was terminated by
                    # request.)
                    if enip_process(addr, data=data, **kwds):
                        # Produce an EtherNet/IP response carrying the encapsulated response data.
                        # If no encapsulated data, ensure we also return a non-zero EtherNet/IP
                        # status.  A non-zero status indicates the end of the session.
                        assert 'response.enip' in data, "Expected EtherNet/IP response; none found"
                        if 'input' not in data.response.enip or not data.response.enip.input:
                            logger.warning("Expected EtherNet/IP response encapsulated message; none found")
                            assert data.response.enip.status, "If no/empty response payload, expected non-zero EtherNet/IP status"

                        rpy = parser.enip_encode(data.response.enip)
                        logger.debug("%s send: %5d: %s", machine.name_centered(),
                                       len(rpy), cpppo.reprlib.repr(rpy))
                        conn.sendto(rpy, addr)

                    logger.debug("Transaction complete after %7.3fs", cpppo.timer() - begun)

                    stats['processed'] = source.sent
                except:
                    # Parsing failure.  Suck out some remaining input to give us some context, but don't re-raise
                    if stats:
                        stats['processed'] = source.sent
                    memory = bytes(bytearray(source.memory))
                    pos = len(source.memory)
                    future = bytes(bytearray(b for b in source))
                    where = "at %d total bytes:\n%s\n%s (byte %d)" % (
                        stats.get('processed', 0) if stats else 0,
                        repr(memory + future), '-' * (len(repr(memory)) - 1) + '^', pos)
                    logger.error("Client %r EtherNet/IP error %s\n\nFailed with exception:\n%s\n", addr, where, ''.join(
                        traceback.format_exception(*sys.exc_info())))
Ejemplo n.º 2
0
    def handle_udp(self, conn, name, enip_process, **kwds):
        """
        Process UDP packets from multiple clients
        """
        with parser.enip_machine(name=name, context='enip') as machine:
            while not kwds['server']['control']['done'] and not kwds['server']['control']['disable']:
                try:
                    source = cpppo.rememberable()
                    data = cpppo.dotdict()

                    # If no/partial EtherNet/IP header received, parsing will fail with a NonTerminal
                    # Exception (dfa exits in non-terminal state).  Build data.request.enip:
                    begun = cpppo.timer()  # waiting for next transaction
                    addr, stats = None, None
                    with contextlib.closing(machine.run(
                            path='request', source=source, data=data)) as engine:
                        # PyPy compatibility; avoid deferred destruction of generators
                        for mch, sta in engine:
                            if sta is not None:
                                # No more transitions available.  Wait for input.
                                continue
                            assert not addr, "Incomplete UDP request from client %r" % (addr)
                            msg = None
                            while msg is None:
                                # For UDP, we'll allow no input only at the start of a new request parse
                                # (addr is None); anything else will be considered a failed request Back
                                # to the trough for more symbols, after having already received a packet
                                # from a peer?  No go!
                                wait = (kwds['server']['control']['latency']
                                        if source.peek() is None else 0)
                                brx = cpppo.timer()
                                msg, frm = network.recvfrom(conn, timeout=wait)
                                now = cpppo.timer()
                                (logger.info if msg else logger.debug)(
                                    "Transaction receive after %7.3fs (%5s bytes in %7.3f/%7.3fs): %r",
                                    now - begun, len(msg) if msg is not None else "None",
                                    now - brx, wait, self.stats_for(frm)[0])
                                # If we're at a None (can't proceed), and we haven't yet received input,
                                # then this is where we implement "Blocking"; we just loop for input.

                            # We have received exactly one packet from an identified peer!
                            begun = now
                            addr = frm
                            stats, _ = self.stats_for(addr)
                            # For UDP, we don't ever receive incoming EOF, or set stats['eof'].
                            # However, we can respond to a manual eof (eg. from web interface) by
                            # ignoring the peer's packets.
                            assert stats and not stats.get('eof'), \
                                "Ignoring UDP request from client %r: %r" % (addr, msg)
                            stats['received'] += len(msg)
                            logger.debug("%s recv: %5d: %s", machine.name_centered(), len(msg), cpppo.reprlib.repr(msg))
                            source.chain(msg)

                    # Terminal state and EtherNet/IP header recognized; process and return response
                    assert stats
                    if 'request' in data:
                        stats['requests'] += 1
                    # enip_process must be able to handle no request (empty data), indicating the
                    # clean termination of the session if closed from this end (not required if
                    # enip_process returned False, indicating the connection was terminated by
                    # request.)
                    if enip_process(addr, data=data, **kwds):
                        # Produce an EtherNet/IP response carrying the encapsulated response data.
                        # If no encapsulated data, ensure we also return a non-zero EtherNet/IP
                        # status.  A non-zero status indicates the end of the session.
                        assert 'response.enip' in data, "Expected EtherNet/IP response; none found"
                        if 'input' not in data.response.enip or not data.response.enip.input:
                            logger.warning("Expected EtherNet/IP response encapsulated message; none found")
                            assert data.response.enip.status, "If no/empty response payload, expected non-zero EtherNet/IP status"

                        rpy = parser.enip_encode(data.response.enip)
                        logger.debug("%s send: %5d: %s", machine.name_centered(),
                                       len(rpy), cpppo.reprlib.repr(rpy))
                        conn.sendto(rpy, addr)

                    logger.debug("Transaction complete after %7.3fs", cpppo.timer() - begun)

                    stats['processed'] = source.sent
                except:
                    # Parsing failure.  Suck out some remaining input to give us some context, but don't re-raise
                    if stats:
                        stats['processed'] = source.sent
                    memory = bytes(bytearray(source.memory))
                    pos = len(source.memory)
                    future = bytes(bytearray(b for b in source))
                    where = "at %d total bytes:\n%s\n%s (byte %d)" % (
                        stats.get('processed', 0) if stats else 0,
                        repr(memory + future), '-' * (len(repr(memory)) - 1) + '^', pos)
                    logger.error("Client %r EtherNet/IP error %s\n\nFailed with exception:\n%s\n", addr, where, ''.join(
                        traceback.format_exception(*sys.exc_info())))