o
    bhC                     @   s  d Z ddlZddlZddlmZ ddlmZmZmZm	Z	m
Z
mZmZmZ ddlmZ ddlmZ ddlmZ ddlmZ dd	lmZ dd
lmZ ddlmZmZmZmZ ddlm Z  dZ!dd Z"dZ#dd Z$dd Z%dd Z&G dd dZ'dd Z(eeG dd dZ)G dd dZ*dS )zD
Tools for automated testing of L{twisted.pair}-based applications.
    N)deque)EAGAINEBADFEINTREINVALENOBUFSENOSYSEPERMEWOULDBLOCKwraps)implementer)DatagramProtocol)EthernetProtocol)
IPProtocol)RawUDPProtocol)	_IFNAMSIZ
_TUNSETIFFTunnelFlags_IInputOutputSystem)nativeString   c                 C   s   t d| S )z
    Pack an integer into a network-order two-byte string.

    @param n: The integer to pack.  Only values that fit into 16 bits are
        supported.

    @return: The packed representation of the integer.
    @rtype: L{bytes}
    z>H)structpack)n r   6/usr/lib/python3/dist-packages/twisted/pair/testing.py_H   s   
r      c                 C   s   ||  t | | S )a  
    Construct an ethernet frame.

    @param src: The source ethernet address, encoded.
    @type src: L{bytes}

    @param dst: The destination ethernet address, encoded.
    @type dst: L{bytes}

    @param protocol: The protocol number of the payload of this datagram.
    @type protocol: L{int}

    @param payload: The content of the ethernet frame (such as an IP datagram).
    @type payload: L{bytes}

    @return: The full ethernet frame.
    @rtype: L{bytes}
    )r   srcdstprotocolpayloadr   r   r   	_ethernet.   s   r$   c                 C   s   dt dt|  d t d ttjt|  ttjt| }ttd|}|d? }|d@ | }|dA }|dd	 t	d
| |dd  }|| S )a  
    Construct an IP datagram with the given source, destination, and
    application payload.

    @param src: The source IPv4 address as a dotted-quad string.
    @type src: L{bytes}

    @param dst: The destination IPv4 address as a dotted-quad string.
    @type dst: L{bytes}

    @param payload: The content of the IP datagram (such as a UDP datagram).
    @type payload: L{bytes}

    @return: An IP datagram header and payload.
    @rtype: L{bytes}
    s   E    s      @r   z!10H   i  N
   z!H   )
r   lensocket	inet_ptonAF_INETr   sumr   unpackr   )r    r!   r#   ipHeaderchecksumStep1carrychecksumStep2checksumStep3r   r   r   _ipD   s$   	$r4   c                 C   s0   t | t | t t|d  t d }|| S )a~  
    Construct a UDP datagram with the given source, destination, and
    application payload.

    @param src: The source port number.
    @type src: L{int}

    @param dst: The destination port number.
    @type dst: L{int}

    @param payload: The content of the UDP datagram.
    @type payload: L{bytes}

    @return: A UDP datagram header and payload.
    @rtype: L{bytes}
       r   )r   r)   )r    r!   r#   	udpHeaderr   r   r   _udpv   s   
r7   c                   @   sr   e Zd ZdZdZeedZee	dZ
eedZeZdZdd Zed	d
 Zedd Zdd Zdd Zdd ZdS )Tunnelz
    An in-memory implementation of a tun or tap device.

    @cvar _DEVICE_NAME: A string representing the conventional filesystem entry
        for the tunnel factory character special device.
    @type _DEVICE_NAME: C{bytes}
    s   /dev/net/tunz Resource temporarily unavailablezOperation would blockzInterrupted function calli   c                 C   s:   || _ || _d| _d| _d| _t | _t | _t | _dS )a  
        @param system: An L{_IInputOutputSystem} provider to use to perform I/O.

        @param openFlags: Any flags to apply when opening the tunnel device.
            See C{os.O_*}.

        @type openFlags: L{int}

        @param fileMode: ignored
        N)	system	openFlags
tunnelModerequestedNamenamer   
readBufferwriteBufferpendingSignals)selfr9   r:   fileModer   r   r   __init__   s   zTunnel.__init__c                 C   s   | j | jj@  S )zx
        If the file descriptor for this tunnel is open in blocking mode,
        C{True}.  C{False} otherwise.
        )r:   r9   
O_NONBLOCKrA   r   r   r   blocking   s   zTunnel.blockingc                 C   s   t | j| jj@ S )zz
        If the file descriptor for this tunnel is marked as close-on-exec,
        C{True}.  C{False} otherwise.
        )boolr:   r9   	O_CLOEXECrE   r   r   r   closeOnExec   s   zTunnel.closeOnExecc                 C   s.   | j tjj@ rtddt|d}| j| dS )aI  
        Deliver a datagram to this tunnel's read buffer.  This makes it
        available to be read later using the C{read} method.

        @param datagram: The IPv4 datagram to deliver.  If the mode of this
            tunnel is TAP then ethernet framing will be added automatically.
        @type datagram: L{bytes}
        s         s   r   N)r;   r   IFF_TAPvaluer$   _IPv4r>   appendrA   datagramr   r   r   addToReadBuffer   s
   
zTunnel.addToReadBufferc                 C   sR   | j r | jtjj@ rd}ndt }|d8 }|| j  d|  S | jr&t | j	)a  
        Read a datagram out of this tunnel.

        @param limit: The maximum number of bytes from the datagram to return.
            If the next datagram is larger than this, extra bytes are dropped
            and lost forever.
        @type limit: L{int}

        @raise OSError: Any of the usual I/O problems can result in this
            exception being raised with some particular error number set.

        @raise IOError: Any of the usual I/O problems can result in this
            exception being raised with some particular error number set.

        @return: The datagram which was read from the tunnel.  If the tunnel
            mode does not include L{TunnelFlags.IFF_NO_PI} then the datagram is
            prefixed with a 4 byte PI header.
        @rtype: L{bytes}
                r   N)
r>   r;   r   	IFF_NO_PIrK   _PI_SIZEpopleftrF   NotImplementedErrornonBlockingExceptionStyle)rA   limitheaderr   r   r   read   s   zTunnel.readc                 C   sF   | j r| j   ttdt|| jkrttd| j| t|S )a{  
        Write a datagram into this tunnel.

        @param datagram: The datagram to write.
        @type datagram: L{bytes}

        @raise IOError: Any of the usual I/O problems can result in this
            exception being raised with some particular error number set.

        @return: The number of bytes of the datagram which were written.
        @rtype: L{int}
        zInterrupted system callzNo buffer space available)	r@   rU   OSErrorr   r)   SEND_BUFFER_SIZEr   r?   rM   rN   r   r   r   write  s   


zTunnel.writeN)__name__
__module____qualname____doc___DEVICE_NAMEIOErrorr   EAGAIN_STYLEr[   r
   EWOULDBLOCK_STYLEr   EINTR_STYLErW   r\   rC   propertyrF   rI   rP   rZ   r]   r   r   r   r   r8      s     




#r8   c                    s   t   fdd}|S )a|  
    Wrap a L{MemoryIOSystem} method with permission-checking logic.  The
    returned function will check C{self.permissions} and raise L{IOError} with
    L{errno.EPERM} if the function name is not listed as an available
    permission.

    @param original: The L{MemoryIOSystem} instance to wrap.

    @return: A wrapper around C{original} that applies permission checks.
    c                    s,    j | jvrttd | g|R i |S )NzOperation not permitted)r^   permissionsr[   r	   )rA   argskwargsoriginalr   r   permissionChecker+  s   
z&_privileged.<locals>.permissionCheckerr   )rl   rm   r   rk   r   _privileged  s   rn   c                   @   sz   e Zd ZdZdZdZdZdZdd Zdd	 Z	d
d Z
edddZdd Zdd Zdd Zedd Zdd Zdd ZdS )MemoryIOSystemz
    An in-memory implementation of basic I/O primitives, useful in the context
    of unit testing as a drop-in replacement for parts of the C{os} module.

    @ivar _devices:
    @ivar _openFiles:
    @ivar permissions:

    @ivar _counter:
    i          r   c                 C   s   i | _ i | _ddh| _d S )Nopenioctl)_devices
_openFilesrh   rE   r   r   r   rC   G  s   zMemoryIOSystem.__init__c                 C   s   | j |  S )aX  
        Get the L{Tunnel} object associated with the given L{TuntapPort}.

        @param port: A L{TuntapPort} previously initialized using this
            L{MemoryIOSystem}.

        @return: The tunnel object created by a prior use of C{open} on this
            object on the tunnel special device file.
        @rtype: L{Tunnel}
        )ru   fileno)rA   portr   r   r   	getTunnelL     zMemoryIOSystem.getTunnelc                 C   s   || j |< dS )a1  
        Specify a class which will be used to handle I/O to a device of a
        particular name.

        @param name: The filesystem path name of the device.
        @type name: L{bytes}

        @param cls: A class (like L{Tunnel}) to instantiated whenever this
            device is opened.
        N)rt   )rA   r=   clsr   r   r   registerSpecialDeviceY  ry   z$MemoryIOSystem.registerSpecialDeviceNc                 C   sD   || j v r| j}|  jd7  _| j | | ||| j|< |S ttd)a  
        A replacement for C{os.open}.  This initializes state in this
        L{MemoryIOSystem} which will be reflected in the behavior of the other
        file descriptor-related methods (eg L{MemoryIOSystem.read},
        L{MemoryIOSystem.write}, etc).

        @param name: A string giving the name of the file to open.
        @type name: C{bytes}

        @param flags: The flags with which to open the file.
        @type flags: C{int}

        @param mode: The mode with which to open the file.
        @type mode: C{int}

        @raise OSError: With C{ENOSYS} if the file is not a recognized special
            device file.

        @return: A file descriptor associated with the newly opened file
            description.
        @rtype: L{int}
        rp   zFunction not implemented)rt   _counterru   r[   r   )rA   r=   flagsmodefdr   r   r   rr   f  s   

zMemoryIOSystem.openc                 C   ,   z	| j | |W S  ty   ttdw )z
        Try to read some bytes out of one of the in-memory buffers which may
        previously have been populated by C{write}.

        @see: L{os.read}
        Bad file descriptor)ru   rZ   KeyErrorr[   r   )rA   r   rX   r   r   r   rZ     
   
zMemoryIOSystem.readc                 C   r   )z
        Try to add some bytes to one of the in-memory buffers to be accessed by
        a later C{read} call.

        @see: L{os.write}
        r   )ru   r]   r   r[   r   )rA   r   datar   r   r   r]     r   zMemoryIOSystem.writec                 C   s(   z| j |= W dS  ty   ttdw )z
        Discard the in-memory buffer and other in-memory state for the given
        file descriptor.

        @see: L{os.close}
        r   N)ru   r   r[   r   )rA   r   r   r   r   close  s
   
zMemoryIOSystem.closec                 C   s   z| j | }W n ty   ttdw |tkrttdtdtf |\}}||_	||_
|dtd  d |_tdtf |j|S )z
        Perform some configuration change to the in-memory state for the given
        file descriptor.

        @see: L{fcntl.ioctl}
        r   zRequest or args is not valid.z%dsHN   s   123)ru   r   r[   r   r   r   r   r.   r   r;   r<   r=   r   )rA   r   requestri   tunnelr=   r~   r   r   r   rs     s   

zMemoryIOSystem.ioctlc                 C   sL   d}d}t ||d t||d |dd}t| j }|d | ||fS )ah  
        Write an ethernet frame containing an ip datagram containing a udp
        datagram containing the given payload, addressed to the given address,
        to a tunnel device previously opened on this I/O system.

        @param datagram: A UDP datagram payload to send.
        @type datagram: L{bytes}

        @param address: The destination to which to send the datagram.
        @type address: L{tuple} of (L{bytes}, L{int})

        @return: A two-tuple giving the address from which gives the address
            from which the datagram was sent.
        @rtype: L{tuple} of (L{bytes}, L{int})
        z10.1.2.3iaS  r   rp   )r    r!   r#   )r4   r7   listru   valuesrP   )rA   rO   addresssrcIPsrcPort
serialized	openFilesr   r   r   sendUDP  s   zMemoryIOSystem.sendUDPc                 C   s
   t | |S )aa  
        Get a socket-like object which can be used to receive a datagram sent
        from the given address.

        @param fileno: A file descriptor representing a tunnel device which the
            datagram will be received via.
        @type fileno: L{int}

        @param host: The IPv4 address to which the datagram was sent.
        @type host: L{bytes}

        @param port: The UDP port number to which the datagram was sent.
            received.
        @type port: L{int}

        @return: A L{socket.socket}-like object which can be used to receive
            the specified datagram.
        )	_FakePort)rA   rv   hostrw   r   r   r   
receiveUDP  s   
zMemoryIOSystem.receiveUDPN)r^   r_   r`   ra   r|   O_RDWRrD   rH   rC   rx   r{   rn   rr   rZ   r]   r   rs   r   r   r   r   r   r   ro   4  s$    
ro   c                   @   s    e Zd ZdZdd Zdd ZdS )r   z
    A socket-like object which can be used to read UDP datagrams from
    tunnel-like file descriptors managed by a L{MemoryIOSystem}.
    c                 C   s   || _ || _d S r   )_system_fileno)rA   r9   rv   r   r   r   rC     s   
z_FakePort.__init__c           
         s   | j j| j j }g  t } fdd}||_t }|d| t	 d| | j j| j j
}|tjj@ rEt }|d |j}nfdd}|tjj@  }	|	rZ|td }||  d	 d| S )
a_  
        Receive a datagram sent to this port using the L{MemoryIOSystem} which
        created this object.

        This behaves like L{socket.socket.recv} but the data being I{sent} and
        I{received} only passes through various memory buffers managed by this
        object and L{MemoryIOSystem}.

        @see: L{socket.socket.recv}
        c                    s     |  d S r   )rM   )rO   r   )	datagramsr   r   capture  s   z_FakePort.recv.<locals>.capturei90     r   c                    s     | d d d d S r   )datagramReceived)r   )ipr   r   <lambda>   s    
z _FakePort.recv.<locals>.<lambda>Nr   )r   ru   r   r?   rU   r   r   r   addProtor   r;   r   rJ   rK   r   rS   rT   )
rA   nbytesr   receiverr   udpr~   etherr   	dataHasPIr   )r   r   r   recv  s(   z_FakePort.recvN)r^   r_   r`   ra   rC   r   r   r   r   r   r     s    r   )+ra   r*   r   collectionsr   errnor   r   r   r   r   r   r	   r
   	functoolsr   zope.interfacer   twisted.internet.protocolr   twisted.pair.ethernetr   twisted.pair.ipr   twisted.pair.rawudpr   twisted.pair.tuntapr   r   r   r   twisted.python.compatr   rT   r   rL   r$   r4   r7   r8   rn   ro   r   r   r   r   r   <module>   s4   (2  A