
    \dhC                     P   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 Z"dZ#d Z$d Z%d Z& G d d          Z'd Z( ee           G d d                      Z) G 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                 ,    t          j        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)ns    4lib/python3.11/site-packages/twisted/pair/testing.py_Hr      s     ;tQ       c                 2    || z   t          |          z   |z   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payloads       r   	_ethernetr'   .   s    & 9r(||#g--r   c                    dt          dt          |          z             z   dz   t          d          z   t          j        t          j        t          |                     z   t          j        t          j        t          |                    z   }t          t          j        d|                    }|dz	  }|dz  |z   }|dz  }|dd	         t          j	        d
|          z   |dd         z   }||z   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checksumStep3s           r   _ipr8   D   s    &	 R#g,,

		 
 &	& Q%%	 
6><+<+<
=
=		> 
6><+<+<
=
=	> " fh7788MRE"V+u4M!F*M
 }v{4???(233-OHgr   c                     t          |           t          |          z   t          t          |          dz             z   t          d          z   }||z   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&   	udpHeaders       r   _udpr<   v   sZ    & 	3
S''	 S\\A

		 Q%%	  wr   c                       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 Zed             Zed	             Zd
 Zd Z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                     || _         || _        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)selfr@   rA   fileModes       r   __init__zTunnel.__init__   sS      #!	'' 77#ggr   c                 ,    | j         | j        j        z   S )zx
        If the file descriptor for this tunnel is open in blocking mode,
        C{True}.  C{False} otherwise.
        )rA   r@   
O_NONBLOCKrH   s    r   blockingzTunnel.blocking   s     NT[%;;<<r   c                 D    t          | j        | j        j        z            S )zz
        If the file descriptor for this tunnel is marked as close-on-exec,
        C{True}.  C{False} otherwise.
        )boolrA   r@   	O_CLOEXECrM   s    r   closeOnExeczTunnel.closeOnExec   s     DNT[%::;;;r   c                     | j         t          j        j        z  rt	          ddt
          |          }| 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)rB   r   IFF_TAPvaluer'   _IPv4rE   appendrH   datagrams     r   addToReadBufferzTunnel.addToReadBuffer   sT     ?[066 	 [5(  H 	x(((((r   c                     | j         rO| j        t          j        j        z  rd}ndt
          z  }|dz  }|| j                                         d|         z   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       r   N)
rE   rB   r   	IFF_NO_PIrU   _PI_SIZEpopleftrN   NotImplementedErrornonBlockingExceptionStyle)rH   limitheaders      r   readzTunnel.read   s    ( ? 	1!6!<< 
 !8+
DO3355fuf===] 	1%'''00r   c                    | 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)	rG   r_   OSErrorr   r-   SEND_BUFFER_SIZEr   rF   rW   rX   s     r   writezTunnel.write  s}      	<'')))%!:;;;x==4000'#>???)))8}}r   N)__name__
__module____qualname____doc___DEVICE_NAMEIOErrorr   EAGAIN_STYLErf   r   EWOULDBLOCK_STYLEr   EINTR_STYLEra   rg   rJ   propertyrN   rR   rZ   rd   rh    r   r   r>   r>      s          #L 76#EFFL-DEE '%!<==K ,& & &. = = X= < < X<) ) )"!1 !1 !1F    r   r>   c                 <     t                      f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                 b    j         | j        vrt          t          d           | g|R i |S )NzOperation not permitted)ri   permissionsrf   r
   )rH   argskwargsoriginals      r   permissionCheckerz&_privileged.<locals>.permissionChecker+  sE    D$444%!:;;;x.t...v...r   r   )ry   rz   s   ` r   _privilegedr{     s6     8__/ / / / _/
 r   c                       e Zd ZdZdZdZdZdZd Zd Z	d Z
edd
            Zd Zd Zd Zed             Zd Z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                 4    i | _         i | _        ddh| _        d S )Nopenioctl)_devices
_openFilesrv   rM   s    r   rJ   zMemoryIOSystem.__init__G  s#    "G,r   c                 @    | 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}
        )r   fileno)rH   ports     r   	getTunnelzMemoryIOSystem.getTunnelL  s     t{{}}--r   c                     || 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)r   )rH   rD   clss      r   registerSpecialDevicez$MemoryIOSystem.registerSpecialDeviceY  s     "dr   Nc                     || j         v r9| j        }| xj        dz  c_         | 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}
        r~   zFunction not implemented)r   _counterr   rf   r	   )rH   rD   flagsmodefds        r   r   zMemoryIOSystem.openf  s_    0 4=  BMMQMM"5$-"5dE4"H"HDOBIf8999r   c                     	 | j         |                             |          S # t          $ r t          t          d          w xY 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)r   rd   KeyErrorrf   r   )rH   r   rb   s      r   rd   zMemoryIOSystem.read  sN    	8?2&++E222 	8 	8 	8%!6777	8	   "  Ac                     	 | j         |                             |          S # t          $ r t          t          d          w xY w)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   )r   rh   r   rf   r   )rH   r   datas      r   rh   zMemoryIOSystem.write  sN    	8?2&,,T222 	8 	8 	8%!6777	8r   c                 ^    	 | j         |= dS # t          $ r t          t          d          w xY w)z
        Discard the in-memory buffer and other in-memory state for the given
        file descriptor.

        @see: L{os.close}
        r   N)r   r   rf   r   )rH   r   s     r   closezMemoryIOSystem.close  sC    	8### 	8 	8 	8%!6777	8s     ,c                    	 | j         |         }n## t          $ r t          t          d          w xY w|t          k    rt          t
          d          t          j        dt          fz  |          \  }}||_	        ||_
        |dt          dz
           dz   |_        t          j        dt          fz  |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)r   r   rf   r   r   r   r   r2   r   rB   rC   rD   r   )rH   r   requestrw   tunnelrD   r   s          r   r   zMemoryIOSystem.ioctl  s    	8_R(FF 	8 	8 	8%!6777	8 j  &"ABBB]6YL#8$??
d #?Y]?+f4{6YL0&+tDDDs     0c           	          d}d}t          ||d         t          ||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   r~   )r#   r$   r&   )r8   r<   listr   valuesrZ   )rH   rY   addresssrcIPsrcPort
serialized	openFiless          r   sendUDPzMemoryIOSystem.sendUDP  s    " 
W'!*hGGG
 
 

 //1122	!$$Z000wr   c                 "    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)rH   r   hostr   s       r   
receiveUDPzMemoryIOSystem.receiveUDP  s    & v&&&r   N)ri   rj   rk   rl   r   O_RDWRrL   rQ   rJ   r   r   r{   r   rd   rh   r   r   r   r   rs   r   r   r}   r}   4  s        	 	 HFJI- - -
. . ." " " : : : [:<
8 
8 
8
8 
8 
8
8 
8 
8 E E [E,     >' ' ' ' 'r   r}   c                       e Zd ZdZd Z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                 "    || _         || _        d S r   )_system_fileno)rH   r@   r   s      r   rJ   z_FakePort.__init__  s    r   c                 n  
 | j         j        | j                 j                                        }g 
t                      }
fd}||_        t                      }|                    d|           t                                          d|           | j         j        | j                 j
        }|t          j        j        z  r,t                      }|                    d           |j        }nfd}|t          j        j        z   }	|	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                 2                         |            d S r   )rW   )rY   r   	datagramss     r   capturez_FakePort.recv.<locals>.capture  s    X&&&&&r   i90     r    c                 6                         | d d d d           S r   )datagramReceived)r   ips    r   <lambda>z _FakePort.recv.<locals>.<lambda>   s"    B,?,?dD$- - r   Nr   )r   r   r   rF   r_   r   r   r   addProtor   rB   r   rT   rU   r   r]   r^   )rH   nbytesr   receiverr   udpr   etherr   	dataHasPIr   r   s             @@r   recvz_FakePort.recv  sL    |&t|4@HHJJ	#%%	' 	' 	' 	' 	' %,!UH%%%\\
B|&t|4?+%++ 	$&&ENN5"%%%$5         5 ;;<	 	#		?D|GVG$$r   N)ri   rj   rk   rl   rJ   r   rs   r   r   r   r     s<         
  ,% ,% ,% ,% ,%r   r   )+rl   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   r^   r   rV   r'   r8   r<   r>   r{   r}   r   rs   r   r   <module>r      s            S S S S S S S S S S S S S S S S S S S S       & & & & & & 6 6 6 6 6 6 2 2 2 2 2 2 & & & & & & . . . . . . W W W W W W W W W W W W . . . . . . 
  
  
  	. . .,/ / /d  <H H H H H H H HV  *  !!}' }' }' }' }' }' }' "!}'@6% 6% 6% 6% 6% 6% 6% 6% 6% 6%r   