U
    �AcjhC  ã                   @   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   ú8/tmp/pip-unpacked-wheel-nrvc_22y/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#   ZipHeaderZchecksumStep1ZcarryZchecksumStep2ZchecksumStep3r   r   r   Ú_ipD   s$    üûù	÷õþ$r/   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#   Z	udpHeaderr   r   r   Ú_udpv   s    þüúþ
r1   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)Úselfr3   r4   Z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.
        )r4   r3   Ú
O_NONBLOCK©r;   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.
        )Úboolr4   r3   Ú	O_CLOEXECr>   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)r5   r   ÚIFF_TAPÚvaluer$   Ú_IPv4r8   Úappend©r;   Údatagramr   r   r   ÚaddToReadBufferÓ   s    
   ÿzTunnel.addToReadBufferc                 C   sX   | j r@| jtjj@ rd}ndt }|d8 }|| j  ¡ d|…  S | jrNtƒ ‚n| j	‚dS )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)
r8   r5   r   Ú	IFF_NO_PIrD   Ú_PI_SIZEÚpopleftr?   ÚNotImplementedErrorÚnonBlockingExceptionStyle)r;   ÚlimitÚheaderr   r   r   Úreadä   s    zTunnel.readc                 C   sF   | j r| j  ¡  ttdƒ‚t|ƒ| jkr2t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:   rN   ÚOSErrorr   r)   ÚSEND_BUFFER_SIZEr   r9   rF   rG   r   r   r   Úwrite  s    


zTunnel.writeN)Ú__name__Ú
__module__Ú__qualname__Ú__doc__Z_DEVICE_NAMEÚIOErrorr   ZEAGAIN_STYLErT   r
   ZEWOULDBLOCK_STYLEr   ZEINTR_STYLErP   rU   r<   Úpropertyr?   rB   rI   rS   rV   r   r   r   r   r2   ”   s   




#r2   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krttdƒ‚ˆ | f|ž|ŽS )NzOperation not permitted)rW   ÚpermissionsrT   r	   )r;   ÚargsÚkwargs©Úoriginalr   r   ÚpermissionChecker+  s    
z&_privileged.<locals>.permissionCheckerr   )ra   rb   r   r`   r   Ú_privileged  s    rc   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Ú
_openFilesr]   r>   r   r   r   r<   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}
        )rj   Úfileno)r;   Úportr   r   r   Ú	getTunnelL  s    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)ri   )r;   r7   Úclsr   r   r   ÚregisterSpecialDeviceY  s    z$MemoryIOSystem.registerSpecialDeviceNc                 C   sH   || j kr:| j}|  jd7  _| j | | ||ƒ| j|< |S ttdƒ‚dS )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}
        re   zFunction not implementedN)ri   Ú_counterrj   rT   r   )r;   r7   ÚflagsÚmodeÚfdr   r   r   rg   f  s    
zMemoryIOSystem.openc                 C   s6   z| j |  |¡W S  tk
r0   ttdƒ‚Y nX dS )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 descriptorN)rj   rS   ÚKeyErrorrT   r   )r;   rs   rQ   r   r   r   rS   …  s    zMemoryIOSystem.readc                 C   s6   z| j |  |¡W S  tk
r0   ttdƒ‚Y nX dS )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}
        rt   N)rj   rV   ru   rT   r   )r;   rs   Údatar   r   r   rV   ‘  s    zMemoryIOSystem.writec                 C   s0   z| j |= W n tk
r*   ttdƒ‚Y nX dS )zŠ
        Discard the in-memory buffer and other in-memory state for the given
        file descriptor.

        @see: L{os.close}
        rt   N)rj   ru   rT   r   )r;   rs   r   r   r   Úclose�  s    zMemoryIOSystem.closec                 C   sŽ   z| j | }W n tk
r,   ttdƒ‚Y nX |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}
        rt   zRequest or args is not valid.z%dsHNé   s   123)rj   ru   rT   r   r   r   r   r.   r   r5   r6   r7   r   )r;   rs   Úrequestr^   Ztunnelr7   rr   r   r   r   rh   ©  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   re   )r    r!   r#   )r/   r1   Úlistrj   ÚvaluesrI   )r;   rH   ÚaddressZsrcIPZsrcPortZ
serializedZ	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)r;   rk   Úhostrl   r   r   r   Ú
receiveUDPß  s    zMemoryIOSystem.receiveUDP)N)rW   rX   rY   rZ   rp   ÚO_RDWRr=   rA   r<   rm   ro   rc   rg   rS   rV   rw   rh   r}   r€   r   r   r   r   rd   4  s"   
rd   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 ©N)Ú_systemÚ_fileno)r;   r3   rk   r   r   r   r<   û  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@ rŠtƒ }| dˆ¡ |j}n‡fdd„}|tjj@  }	|	r´|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‚   )rF   )rH   r|   )Ú	datagramsr   r   Úcapture  s    z_FakePort.recv.<locals>.capturei90  é   r   c                    s   ˆ   | d d d d ¡S r‚   )ÚdatagramReceived)rv   )Úipr   r   Ú<lambda>   s       ÿz _FakePort.recv.<locals>.<lambda>Nr   )rƒ   rj   r„   r9   rN   r   rˆ   r   ZaddProtor   r5   r   rC   rD   r   rL   rM   )
r;   Únbytesrv   Zreceiverr†   Zudprr   Zetherrˆ   Z	dataHasPIr   )r…   r‰   r   Úrecvÿ  s(    z_FakePort.recvN)rW   rX   rY   rZ   r<   rŒ   r   r   r   r   r~   õ  s   r~   )+rZ   r*   r   Úcollectionsr   Úerrnor   r   r   r   r   r   r	   r
   Ú	functoolsr   Zzope.interfacer   Ztwisted.internet.protocolr   Ztwisted.pair.ethernetr   Ztwisted.pair.ipr   Ztwisted.pair.rawudpr   Ztwisted.pair.tuntapr   r   r   r   Ztwisted.python.compatr   rM   r   rE   r$   r/   r1   r2   rc   rd   r~   r   r   r   r   Ú<module>   s2   (2  A