)zXdZddlZddlZddlZddlZddlZddlZddlZ ddl m Z m Z ddl mZddlmZmZmZmZerddlZeeZdZdedefd ZGd d e ZGd d ZGddeZGddeZGddejj Z!Gddejj"Z#Gdde j$j%Z&Gdde j$j'Z(GddZ)dS)avNetworking transport helpers for urllib. This module provides a small abstraction on top of urllib.request so that callers can keep using urllib.request.Request, but routing of connections can be customized: - hostname resolution is handled in user code; - selected IP may be randomized or chosen using any complex logic; - for HTTPS: connects to a chosen IP but keeps correct SNI and certificate hostname validation for the original hostname (NOT the IP). Examples: Default behavior (plain urllib): from defence360agent.utils.net_transport import UrlTransport transport = UrlTransport() req = urllib.request.Request( "https://files.imunify360.com/static/sigs/v1/description.json" ) with transport.open(req, timeout=10) as resp: body = resp.read() Randomize target IP on each connection (A/AAAA -> random choice): from defence360agent.utils.net_transport import UrlTransport, RandomIpChooser chooser = RandomIpChooser() transport = UrlTransport(ip_chooser=chooser) req = urllib.request.Request( "https://files.imunify360.com/static/sigs/v1/description.json" ) with transport.open(req, timeout=10) as resp: body = resp.read() Notes: - HTTPS: connects to the chosen IP but keeps SNI/cert checks against original hostname. - HTTP: Host header stays original hostname because urllib builds it from the URL. N)ABCabstractmethod) getLogger)DictOptionalTuple TYPE_CHECKINGgr@ipreturnc~ ttj|tjS#t$rYdSwxYw)z~Return True if *ip* is an IPv4 address string. Implementation relies solely on ipaddress.ip_address for correctness. F) isinstance ipaddress ip_address IPv4Address ValueError)r s X/opt/imunify360/venv/lib/python3.11/site-packages/defence360agent/utils/net_transport.py_is_ipv4rCsG ).r22I4IJJJ uus +. <<cJeZdZdZedededefdZdededefdZdS) IpChooserzSelect an IP address to connect to for a given hostname and port. Implementations may be stateful and can keep caches/metrics inside. hostnameportr ct)z6Return an IP address (v4 or v6) for *hostname*:*port*.)NotImplementedErrorselfrrs rchoosezIpChooser.chooseTs "!c.|||SN)rrs r__call__zIpChooser.__call__Ys{{8T***rN) __name__ __module__ __qualname____doc__rstrintrr rrrrNs| "s"#"#"""^"++C+C++++++rrcXeZdZdZejeddedefdZ de dede e d ffd Z d S) DnsCacheResolverzDNS cache for socket.getaddrinfo() results. It caches per (hostname, port, family). This is intentionally small and local: it is meant only to avoid excessive getaddrinfo() calls. )family ttl_secondsr*r+c`||_||_i|_tj|_dSr)_family _ttl_seconds_cache threadingLock_lock)rr*r+s r__init__zDnsCacheResolver.__init__ds4  '  ^%% rrrr .c,|||jf}tj}|j5|j|}|;|\}}||kr0t d|||j|cdddSdddn #1swxYwYt d|||jtj|||jtj }g}|D])\} } } } } | d} | |vr| | *|s#td ||t|} |j5||jz| f|j|<dddn #1swxYwYt d|||j| | S)Nz0DnsCacheResolver cache hit for %s:%s (family=%s)z9DnsCacheResolver cache miss/expired for %s:%s (family=%s)rzNo IPs resolved for {}:{}z1DnsCacheResolver resolved %s:%s (family=%s) to %s)r-timer2r/getloggerdebugsocket getaddrinfo SOCK_STREAMappendOSErrorformattupler.) rrrkeynowcached expires_atipsinfos_sockaddrr ips_ts rget_ipszDnsCacheResolver.get_ipsqsnt|,ikk Z  [__S))F!"( C##LLJ                            G   L    "   L     $)   Aq!Q!B}} 2 N5<z7RandomIpChooserWithIPv6Toggle.choose..s-::Xb\\:::::::rzNo IPv4 IPs resolved for {}:{}zKRandomIpChooserWithIPv6Toggle selected IPv4 IP %s for %s:%s (IPv6 disabled)) rSrIrWrVchoicerXr7r8r?r=r>)rrrrDchosenipv4_ipss rrz$RandomIpChooserWithIPv6Toggle.choosesn$$Xt44   Y%%c**F"DM LL!    M::c::::: 077$GG !!(++           rr N)r!r"r#r$rr)rTrUrer3r\r^r`r%rbrfr&rr'rrrNrNs04'+! , , ,+, ,fm $ ,  , , , ,""""####"""""#>$>>>>s##rrNc`eZdZdZddddeedeejfdZde de d e fd Z dS) RandomIpChooserzAResolve hostname and select a random IP from resolved candidates.N)rOrPrOrPcd|p t|_|ptj|_dSr)r)rSrTrUrV)rrOrPs rr3zRandomIpChooser.__init__s- "7%5%7%7*6=?? rrrr c|j||}|j|}td||||S)Nz(RandomIpChooser selected IP %s for %s:%s)rSrIrVrkr7r8)rrrrDrls rrzRandomIpChooser.choosesWn$$Xt44!!#&& 6        r) r!r"r#r$rr)rTrUr3r%r&rr'rrrprpsKK 04'+ ++++,+fm $ ++++ s # #      rrpcZeZdZdZ d ejdddedeede ffdZ d d Z xZ S) ForcedIPHTTPConnectionzHTTPConnection that connects to a chosen IP. Important: urllib builds the request URL with the original hostname, therefore the Host header stays correct. Ntimeoutsource_addressrr ip_chooserc`t||||||_dS)N)rrvrwsuperr3 _ip_chooser)rrrrxrvrw __class__s rr3zForcedIPHTTPConnection.__init__ sB  )    &rr c|jpd}|j|j|}td|||jt j||f|j|j |_ dS)NPz:ForcedIPHTTPConnection connecting to %s:%s for hostname %s) rr|rhostr7r8r9create_connectionrvrwsock)rrr s rconnectzForcedIPHTTPConnection.connectswyB   $ $TY 5 5 H   I    , J L     rrrn r!r"r#r$r9_GLOBAL_DEFAULT_TIMEOUTr%rr&rr3r __classcell__r}s@rrtrts#& .&&&&sm&  &&&&&&"        rrtc ^eZdZdZ d ejdddedeede ddffd Z d d Z xZ S)ForcedIPHTTPSConnectionzHTTPSConnection that connects to a chosen IP. TLS details: - Uses original hostname for SNI (server_hostname in wrap_socket) - Certificate hostname validation is performed for the original hostname Nrurrrxcontextssl.SSLContextcbt|||||||_dS)N)rrrvrwrz)rrrrxrrvrwr}s rr3z ForcedIPHTTPSConnection.__init__3sE  )    &rr c~|jpd}|j|j|}td|||jt j||f|j|j }|j r"||_ | |j }|j ||j|_ dS)Niz;ForcedIPHTTPSConnection connecting to %s:%s for hostname %s)server_hostname)rr|rrr7r8r9rrvrw _tunnel_hostr_tunnel_context wrap_socket)rrr raw_socks rrzForcedIPHTTPSConnection.connectFsyC   $ $TY 5 5 I   I    + J L       ! DI LLNNNyHM--  I.   rrrnrrs@rrr+s#&.&&&&sm&  & " &&&&&&&        rrcHeZdZdZdeffd ZdejjfdZ xZ S)ForcedIPHTTPHandlerz3urllib handler that creates ForcedIPHTTPConnection.rxcVt||_dSrrz)rrxr}s rr3zForcedIPHTTPHandler.__init__ds' %rr c:fd}||S)NcXt|j|dS)Nrv)rxrv)rtr|r6rkwargsrs rfactoryz.ForcedIPHTTPHandler.http_open..factoryis2)+ 9-- rdo_openrreqrs` r http_openzForcedIPHTTPHandler.http_openhs2     ||GS)))r) r!r"r#r$rr3httpclient HTTPResponserrrs@rrrasj==&i&&&&&&* 8********rrcLeZdZdZdeddffd ZdejjfdZ xZ S)ForcedIPHTTPSHandlerz4urllib handler that creates ForcedIPHTTPSConnection.rxrrcht|||_||_dS)N)r)r{r3r|r)rrxrr}s rr3zForcedIPHTTPSHandler.__init__vs1 )))% rr c:fd}||S)Ncdt|jj|dS)Nrv)rxrrv)rr|rr6rs rrz0ForcedIPHTTPSHandler.https_open..factory|s7*+  9--  rrrs` r https_openzForcedIPHTTPSHandler.https_open{s2     ||GS)))r) r!r"r#r$rr3rrrrrrs@rrrssr>> i :J      *!9 * * * * * * * *rrceZdZdZddddeededfdZddd ejj d ee d e j j fd ZdS) UrlTransportaSingle entrypoint for opening urllib requests. If *ip_chooser* is provided, the transport will connect to the selected IP address, while keeping correct Host/SNI/cert validation for the original hostname. If *ip_chooser* is not provided, it behaves like plain urllib. N)rx ssl_contextrxrrc ddl}|p||_|%tj|_dStjt|t||j|_dS)Nr)rx)rxr) sslcreate_default_context _ssl_contexturllibrequest build_opener_openerrr)rrxr_ssls rr3zUrlTransport.__init__s 'H4+F+F+H+H  !>6688DLLL!>66#z:::$) -DLLLrrvrrvr cr||j|S|j||S)Nr)ropen)rrrvs rrzUrlTransport.opens: ?<$$S)) )|  g 666r)r!r"r#r$rrr3rrRequestrLrrrrr'rrrrs+/26 Y'./ 2$( 777 ^ #7% 7  ! 777777rr)*r$ http.clientrrrTr9r0r5urllib.requestrabcrrloggingrtypingrrrr rr!r7rKr%rerrr)rNrprHTTPConnectionrtHTTPSConnectionrr HTTPHandlerr HTTPSHandlerrrr'rrrs,,\  ########777777777777JJJ 8  ! + + + + + + + +GGGGGGGGT@@@@@I@@@Fi0& & & & & T[7& & & R3 3 3 3 3 dk93 3 3 l*****&.4***$*****6>6***(%7%7%7%7%7%7%7%7%7%7r