§
    AxÅj\8  ã                   ó<  — d Z ddlZddlZddlZddlmZ ddlmZmZm	Z	 ddl
mZmZ ddlmZ ddlmZ ddlmZmZ dd	lmZ d
Z ed¦  «        ZdZdZdZ ej        d¦  «        gZdZ G d„ de ¦  «        Z!de"fd„Z#de$de"fd„Z%de$de&dee$         fd„Z'de&fd„Z(de$ddfd„Z)de$de$fd„Z* G d„ de¦  «        Z+d e$defd!„Z,d"eddfd#„Z-d"eddfd$„Z.d%e	e&         ddfd&„Z/d'e&d%e	e&         dee0         fd(„Z1de$fd)„Z2d*d+dded,œde$d-e"d.e&d/e	e3         d%e	e&         d0e&fd1„Z4dS )2a’  
PodWorker | serverless | utils | rp_ssrf.py

SSRF-safe fetching of user-supplied (job-input) URLs.

Job input carries arbitrary URLs that the worker downloads. Without restriction
a job can point the worker at cloud instance-metadata (169.254.169.254) or other
non-public addresses (CWE-918). This module blocks any destination that is not a
globally-routable IP, pins connections to a pre-validated address to defeat DNS
rebinding, re-validates redirect hops, and caps download size.

Pinning requires this process to open the socket, so a URL that the environment
routes through a proxy is refused rather than fetched unprotected; see
_refuse_if_proxied.
é    N)Úsuppress)ÚIteratorÚListÚOptional)ÚurljoinÚurlparse)ÚConnectionError)ÚHTTPAdapter)Úget_environ_proxiesÚselect_proxy)ÚSyncClientSession)ÚhttpÚhttps)i-  i.  i/  i3  i4  é   ÚRUNPOD_MAX_DOWNLOAD_BYTESl        z100.64.0.0/10Ú"RUNPOD_ALLOW_PRIVATE_DOWNLOAD_URLSc                   ó   — e Zd ZdZdS )Ú	SSRFErrora�  
    Raised when a URL is rejected as unsafe (non-public destination, blocked
    scheme, or oversized body).

    Subclasses ValueError and deliberately NOT requests.RequestException so it
    bypasses the download path's backoff retry and `except RequestException`
    handler: a blocked internal URL must fail loudly, not be retried or silently
    reported as an ordinary failed download.
    N)Ú__name__Ú
__module__Ú__qualname__Ú__doc__© ó    úPC:\Projects\Historical\venv\Lib\site-packages\runpod/serverless/utils/rp_ssrf.pyr   r   0   s   € € € € € ðð ð ð r   r   Úreturnc                  ó˜   — t           j                             t          d¦  «                             ¦   «                              ¦   «         dvS )NÚ )Ú1ÚtrueÚyes)ÚosÚenvironÚgetÚ_ALLOW_PRIVATE_ENVÚstripÚlowerr   r   r   Ú_ssrf_protection_enabledr(   <   s>   € ÝŒ:�>Š>Õ,¨bÑ1Ô1×7Ò7Ñ9Ô9×?Ò?ÑAÔAð Jð ð r   Úipc                 ó&  ‡— 	 t          j        | ¦  «        Šn# t          $ r Y dS w xY wt          ‰dd¦  «        }|�|Št	          ˆfd„t
          D ¦   «         ¦  «        rdS ‰j        s#‰j        s‰j        s‰j	        s‰j
        s‰j        rdS ‰j        S )a.  
    Return True only for a globally-routable unicast IP address.

    Blocks loopback, link-local (incl. 169.254.169.254 metadata), RFC1918,
    CGNAT, reserved, multicast, unspecified, IPv6 unique-local/link-local, and
    IPv4-mapped IPv6 forms of any of the above. Unparseable input is unsafe.
    FÚipv4_mappedNc              3   ó    •K  — | ]}‰|v V — Œ	d S ©Nr   )Ú.0ÚnetworkÚaddresss     €r   ú	<genexpr>z"is_safe_address.<locals>.<genexpr>V   s(   øè è € Ð
EÐ
E 'ˆ7�gÐÐ
EÐ
EÐ
EÐ
EÐ
EÐ
Er   )Ú	ipaddressÚ
ip_addressÚ
ValueErrorÚgetattrÚanyÚ_EXTRA_BLOCKED_NETWORKSÚ
is_privateÚis_loopbackÚis_link_localÚis_reservedÚis_multicastÚis_unspecifiedÚ	is_global)r)   Úmappedr0   s     @r   Úis_safe_addressr@   D   sá   ø€ ðÝÔ& rÑ*Ô*ˆˆøÝð ð ð Øˆuˆuðøøøõ �W˜m¨TÑ2Ô2€FØÐØˆå
Ð
EÐ
EÐ
EÐ
EÕ-DÐ
EÑ
EÔ
EÑEÔEð Øˆuð 	ÔðàÔðð Ô ðð Ôð	ð
 Ôðð Ô!ðð ˆuàÔÐs   ƒ ˜
&¥&ÚhostÚportc                 ó˜  — 	 t          j        | |t           j        ¬¦  «        }n-# t           j        $ r}t	          d| ›d|› �¦  «        |‚d}~ww xY wg }|D ])}|d         d         }||vr|                     |¦  «         Œ*|st	          d| ›�¦  «        ‚t          ¦   «         r)|D ]&}t          |¦  «        st	          d| ›d	|› �¦  «        ‚Œ'|S )
a‡  
    Resolve `host` and return its IPs, failing closed on any unsafe address.

    Raises SSRFError if the host cannot be resolved, resolves to nothing, or
    resolves to any non-global address (a host resolving to both a public and a
    private IP is treated as hostile). Validation is skipped when the escape
    hatch env var is set, but resolution still occurs so callers can pin.
    )Útypezcould not resolve host ú: Né   r   zno addresses resolved for host zhost z  resolves to non-public address )ÚsocketÚgetaddrinfoÚSOCK_STREAMÚgaierrorr   Úappendr(   r@   )rA   rB   ÚinfosÚerrÚipsÚinfor)   s          r   Úresolve_and_validaterP   f   s$  € ðLÝÔ" 4¨µFÔ4FÐGÑGÔGˆˆøÝŒ?ð Lð Lð LÝÐA°$ÐAÐA¸CÐAÐAÑBÔBÈÐKøøøøðLøøøð €CØð ð ˆØ�!ŒW�QŒZˆØ�Sˆ=ˆ=Ø�JŠJ�r‰NŒNˆNøàð DÝÐB¸$ÐBÐBÑCÔCÐCåÑ!Ô!ð Øð 	ð 	ˆBÝ" 2Ñ&Ô&ð ÝØH˜DÐHÐHÀBÐHÐHñô ð ðð
 €Js   ‚!$ ¤A³A	Á	Ac                  óÊ   — t           j                             t          ¦  «        } | €t          S 	 t          | ¦  «        }n# t          $ r
 t          cY S w xY w|dk    r|nt          S )z4Configured per-download byte cap; defaults to 5 GiB.Nr   )r"   r#   r$   Ú_MAX_BYTES_ENVÚ_DEFAULT_MAX_BYTESÚintr4   )ÚrawÚvalues     r   Úmax_download_bytesrW   ‡   sl   € å
Œ*�.Š.�Ñ
(Ô
(€CØ
€{Ý!Ð!ð"Ý�C‘”ˆˆøÝð "ð "ð "Ý!Ð!Ð!Ð!ð"øøøà˜A’I�Iˆ5ˆ5Õ#5Ð5s   ¯? ¿AÁAÚurlc           	      ó    — t          ¦   «         sdS t          | t          | ¦  «        ¦  «        }|rt          d| › d|› dt          › d�¦  «        ‚dS )ab  
    Reject `url` when the environment routes it through a proxy.

    Pinning only holds if this process opens the socket: through a proxy the
    request carries the hostname and the proxy resolves it, so the validated IP
    is not what gets dialed and the rebind protection silently stops applying.
    Failing here turns that into a loud, actionable error instead of a false
    sense of protection.

    NO_PROXY exclusions are honored (such a host is fetched directly, so pinning
    still holds), and the escape hatch env var allows the proxied fetch for
    deployments that accept the trade-off.
    Nzrefusing to fetch z through proxy z1: IP pinning cannot be enforced via a proxy; set z=true to allow it)r(   r   r   r   r%   )rX   Úproxys     r   Ú_refuse_if_proxiedr[   “   sŠ   € õ $Ñ%Ô%ð Øˆå˜Õ1°#Ñ6Ô6Ñ7Ô7€EØð 
ÝðO ð Oð O°Uð Oð OÝ);ðOð Oð Oñ
ô 
ð 	
ð
ð 
r   c                 óp   — t          | ¦  «        }|j        pd}d|v rd|› d�}|j        �|› d|j        › �S |S )a�  
    Build the Host header authority for `url`.

    The pinned adapter dials an IP but must present the original authority, so
    this reconstructs it from the URL rather than the socket. urlparse strips
    the brackets from an IPv6 literal, and RFC 7230 requires them in Host, so
    they are restored: without this an IPv6-literal URL yields a malformed
    header such as "2606::1:443".
    r   ú:ú[ú])r   ÚhostnamerB   )rX   ÚparsedrA   s      r   Ú_host_header_forrb   ¬   sT   € õ �c‰]Œ]€FØŒ?Ð ˜b€DØ
ˆd€{€{Ø�4ˆ{ˆ{ˆ{ˆØ„{ÐØÐ&Ð&˜œÐ&Ð&Ð&Ø€Kr   c                   óV   ‡ — e Zd ZdZdefˆ fd„Zˆ fd„Zdefd„Zd
ˆ fd„	Zdˆ fd	„	Z	ˆ xZ
S )ÚPinnedIPAdaptera«  
    Requests adapter that dials a pre-validated IP instead of re-resolving the
    URL host, defeating DNS rebinding.

    The superclass builds the connection pool with its normal TLS/verification
    configuration; this adapter only redirects the socket to the pinned IP and
    keeps the TLS SNI, certificate hostname check, and Host header bound to the
    original URL host. It never alters certificate verification.
    Ú	pinned_ipc                 óH   •— || _          t          ¦   «         j        di |¤Ž d S )Nr   )Ú
_pinned_ipÚsuperÚ__init__)Úselfre   ÚkwargsÚ	__class__s      €r   ri   zPinnedIPAdapter.__init__Ê   s-   ø€ Ø#ˆŒØ�‰ŒÔÐ"Ð"˜6Ð"Ð"Ð"Ð"Ð"r   c                 óp   •— t          |j        ¦  «        |j        d<    t          ¦   «         j        |fi |¤ŽS )NÚHost)rb   rX   Úheadersrh   Úsend)rj   Úrequestrk   rl   s      €r   rp   zPinnedIPAdapter.sendÎ   s7   ø€ Ý"2°7´;Ñ"?Ô"?ˆŒ˜ÑØ�u‰wŒwŒ|˜GÐ.Ð. vÐ.Ð.Ð.r   rX   c                 ó¾   — t          |¦  «        }|j        dk    r6|j        |_        t	          |j        pi ¦  «        |_        |j        |j        d<   | j        |_        |S )Nr   Úserver_hostname)r   Úschemer`   Úassert_hostnameÚdictÚconn_kwrg   rA   )rj   ÚpoolrX   ra   s       r   Ú_pinzPinnedIPAdapter._pinÒ   sW   € Ý˜#‘”ˆØŒ=˜GÒ#Ð#Ø#)¤?ˆDÔ Ý ¤Ð 2°Ñ3Ô3ˆDŒLØ.4¬oˆDŒLÐ*Ñ+Ø”OˆŒ	Øˆr   Nc                 ó„   •— t          ¦   «                              ||||¬¦  «        }|                      ||j        ¦  «        S )N)ÚproxiesÚcert)rh   Úget_connection_with_tls_contextry   rX   )rj   rq   Úverifyr{   r|   rx   rl   s         €r   r}   z/PinnedIPAdapter.get_connection_with_tls_contextÛ   sB   ø€ Ý‰wŒw×6Ò6Ø�V W°4ð 7ñ 
ô 
ˆð �yŠy˜˜wœ{Ñ+Ô+Ð+r   c                 óv   •— t          ¦   «                              ||¬¦  «        }|                      ||¦  «        S )N)r{   )rh   Úget_connectionry   )rj   rX   r{   rx   rl   s       €r   r€   zPinnedIPAdapter.get_connectioná   s3   ø€ Ý‰wŒw×%Ò% c°7Ð%Ñ;Ô;ˆØ�yŠy˜˜sÑ#Ô#Ð#r   )NNr-   )r   r   r   r   Ústrri   rp   ry   r}   r€   Ú__classcell__)rl   s   @r   rd   rd   ¿   s¾   ø€ € € € € ðð ð# #ð #ð #ð #ð #ð #ð #ð/ð /ð /ð /ð /ð˜cð ð ð ð ð,ð ,ð ,ð ,ð ,ð ,ð$ð $ð $ð $ð $ð $ð $ð $ð $ð $r   rd   re   c                 ó˜   — t          ¦   «         }t          | ¦  «        }|                     d|¦  «         |                     d|¦  «         |S )z:A session whose http(s) connections dial only `pinned_ip`.zhttp://zhttps://)r   rd   Úmount)re   ÚsessionÚadapters      r   Ú_build_pinned_sessionr‡   æ   sG   € åÑ!Ô!€GÝ˜iÑ(Ô(€GØ‡M‚M�)˜WÑ%Ô%Ð%Ø‡M‚M�*˜gÑ&Ô&Ð&Ø€Nr   r…   c                 óˆ   — t          t          ¦  «        5  |                      ¦   «          ddd¦  «         dS # 1 swxY w Y   dS )a½  
    Close a pinned session best-effort, tolerating teardown errors.

    This runs only on cleanup paths (a failed request, a closing response) where
    the caller's original exception is the meaningful one. A failure while
    closing the session's connection pools must not mask that exception, so it is
    suppressed rather than raised. Session.close() is idempotent and does no
    actionable I/O, so nothing worth surfacing is lost.
    N)r   Ú	ExceptionÚclose)r…   s    r   Ú_close_session_quietlyr‹   ï   sƒ   € õ 
•)Ñ	Ô	ð ð Ø�Š‰Œˆðð ð ñ ô ð ð ð ð ð ð ð øøøð ð ð ð ð ð s   •7·;¾;c                 ó2   ‡‡— | j         Šˆˆfd„}|| _         dS )aÙ  
    Tie `session` cleanup to `response.close()`.

    Each safe_get() call owns a single-use session; the caller only holds the
    response. Closing a requests.Response releases its connection but not the
    session (its adapters and connection pools), so without this the session
    leaks one set of pooled sockets/FDs per download in a long-lived worker.
    Wrapping close() makes `with safe_get(...) as r:` (and any direct
    response.close()) tear down both.
    c                  ó`   •— 	  ‰| i |¤Ž t          ‰¦  «         d S # t          ‰¦  «         w xY wr-   )r‹   )Úargsrk   Úoriginal_closer…   s     €€r   Ú
close_bothz-_bind_session_to_response.<locals>.close_both
  sF   ø€ ð	,ØˆN˜DÐ+ FÐ+Ð+Ð+å" 7Ñ+Ô+Ð+Ð+Ð+øÕ" 7Ñ+Ô+Ð+Ð+øøøs   ƒ œ-N)rŠ   )Úresponser…   r�   r�   s    ` @r   Ú_bind_session_to_responser’   ý   s8   øø€ ð ”^€Nð,ð ,ð ,ð ,ð ,ð ,ð  €H„N€N€Nr   Ú	max_bytesc                 óî   — |€d S | j                              d¦  «        }|€d S 	 t          |¦  «        }n# t          $ r Y d S w xY w||k    r*|                      ¦   «          t          d|› d|› d�¦  «        ‚d S )NzContent-Lengthzdownload exceeds size cap: z > ú bytes)ro   r$   rT   r4   rŠ   r   )r‘   r“   ÚdeclaredÚsizes       r   Ú_enforce_content_lengthr˜     s¨   € ØÐØˆØÔ×#Ò#Ð$4Ñ5Ô5€HØÐØˆðÝ�8‰}Œ}ˆˆøÝð ð ð ØˆˆðøøøàˆiÒÐØ�ŠÑÔÐÝÐP°dÐPÐP¸yÐPÐPÐPÑQÔQÐQð Ðs   ¤4 ´
AÁAÚ
chunk_sizec              #   óÐ   K  — d}|                       |¬¦  «        D ]J}|sŒ|t          |¦  «        z  }|�-||k    r'|                      ¦   «          t          d|› d�¦  «        ‚|V — ŒKdS )z 
    Yield the response body in chunks, aborting with SSRFError if the running
    total exceeds `max_bytes`. Guards against a lying/absent Content-Length.
    r   )r™   Nzdownload exceeds size cap: >r•   )Úiter_contentÚlenrŠ   r   )r‘   r™   r“   ÚtotalÚchunks        r   Úiter_content_cappedrŸ   "  s—   è è € ð
 €EØ×&Ò&°*Ð&Ñ=Ô=ð ð ˆØð 	ØØ•�U‘”ÑˆØÐ  U¨YÒ%6Ð%6Ø�NŠNÑÔÐÝÐL¸9ÐLÐLÐLÑMÔMÐMØˆˆˆˆðð r   c                ó  — d}| D ]s}t          |¦  «        }	 |                     ||d||¬¦  «        }nA# t          $ r}	t          |¦  «         |	}Y d}	~	ŒMd}	~	wt          $ r t          |¦  «         ‚ w xY w||fc S |€t          d|› �¦  «        ‚|‚)uú  
    GET `url` pinned to each validated IP in turn, returning the first that connects.

    A hostname commonly resolves to several addresses (dual-stack AAAA + A), and
    the first one may be unroutable from the worker â€” e.g. an IPv6 address on a
    pod with no IPv6 route. Plain requests walks the address list; pinning to a
    single IP would turn that into a hard download failure, so this preserves
    the fallback while keeping every candidate pre-validated.

    Only connection-level errors advance to the next address; an HTTP response
    (any status) or any other exception is returned/raised immediately. Returns
    the (session, response) pair so the caller owns teardown. Raises the last
    connection error if no address is reachable.
    NF)ro   Úallow_redirectsÚstreamÚtimeoutz&no candidate addresses to connect to: )r‡   r$   ÚRequestsConnectionErrorr‹   ÚBaseExceptionr   )
rN   rX   ro   r¢   r£   Ú
last_errorr)   r…   r‘   rM   s
             r   Ú_get_via_first_reachable_ipr§   2  së   € ð €JØð !ð !ˆÝ'¨Ñ+Ô+ˆð	Ø—{’{ØØØ %ØØð #ñ ô ˆHˆHøõ 'ð 	ð 	ð 	Ý" 7Ñ+Ô+Ð+ØˆJØˆHˆHˆHˆHøøøøÝð 	ð 	ð 	Ý" 7Ñ+Ô+Ð+Øð	øøøð ˜Ð Ð Ð Ð àÐÝÐFÀÐFÐFÑGÔGÐGØ
Ðs   —2²
A0¼AÁA0Té   )r¢   r£   ro   r“   Úmax_redirectsr¢   r£   ro   r©   c                ó  — |€t          ¦   «         }| }t          |dz   ¦  «        D �]M}t          |¦  «        }|j        t          vrt          d|j        ›d|› �¦  «        ‚|j        st          d|› �¦  «        ‚t          |¦  «         |j        p|j        dk    rdnd}	t          |j        |	¦  «        }
t          |
||||¬	¦  «        \  }}|j        t          v ry|j                             d
¦  «        }	 |                     ¦   «          t!          |¦  «         n# t!          |¦  «         w xY w|st          d|› �¦  «        ‚t#          ||¦  «        }�Œ+t%          ||¦  «         t'          ||¦  «         |c S t          d|› d| › �¦  «        ‚)a¿  
    Fetch a user-supplied URL with SSRF protections.

    Enforces an http(s) scheme allowlist, resolves and validates the host to a
    public IP, pins the connection to that IP, and re-validates every redirect
    hop. Rejects a body whose declared size exceeds the cap. Returns the
    (streamed) requests.Response for the caller to consume; use
    iter_content_capped to enforce the cap while reading. Raises SSRFError on
    any block.
    Né   zblocked URL scheme rE   zURL has no host: r   i»  éP   )ro   r¢   r£   ÚLocationz"redirect without Location header: zexceeded maximum redirects (z): )rW   Úranger   rt   Ú_ALLOWED_SCHEMESr   r`   r[   rB   rP   r§   Ústatus_codeÚ_REDIRECT_STATUSro   r$   rŠ   r‹   r   r’   r˜   )rX   r¢   r£   ro   r“   r©   ÚcurrentÚ_ra   rB   rN   r…   r‘   Úlocations                 r   Úsafe_getrµ   Z  sÐ  € ð& ÐÝ&Ñ(Ô(ˆ	à€GÝ�= 1Ñ$Ñ%Ô%ð ñ ˆÝ˜'Ñ"Ô"ˆØŒ=Õ 0Ð0Ð0ÝÐN°&´-ÐNÐNÀWÐNÐNÑOÔOÐOØŒð 	;ÝÐ9°Ð9Ð9Ñ:Ô:Ð:Ý˜7Ñ#Ô#Ð#àŒ{ÐG f¤m°wÒ&>Ð&>˜s˜sÀBˆÝ" 6¤?°DÑ9Ô9ˆå7Ø� '°&À'ð
ñ 
ô 
Ñˆ�ð ÔÕ#3Ð3Ð3ØÔ'×+Ò+¨JÑ7Ô7ˆHð0Ø—’Ñ Ô Ð å& wÑ/Ô/Ð/Ð/øÕ& wÑ/Ô/Ð/Ð/øøøØð PÝÐ NÀWÐ NÐ NÑOÔOÐOÝ˜g xÑ0Ô0ˆGÙõ 	" (¨GÑ4Ô4Ð4Ý ¨)Ñ4Ô4Ð4Øˆˆˆå
ÐJ°=ÐJÐJÀSÐJÐJÑ
KÔ
KÐKs   Ã2DÄD')5r   r2   r"   rG   Ú
contextlibr   Útypingr   r   r   Úurllib.parser   r   Úrequestsr	   r¤   Úrequests.adaptersr
   Úrequests.utilsr   r   Úrunpod.http_clientr   r¯   Ú	frozensetr±   Ú_DEFAULT_MAX_REDIRECTSrR   rS   Ú
ip_networkr7   r%   r4   r   Úboolr(   r�   r@   rT   rP   rW   r[   rb   rd   r‡   r‹   r’   r˜   ÚbytesrŸ   r§   rv   rµ   r   r   r   ú<module>rÂ      s�  ððð ð  Ð Ð Ð Ø 	€	€	€	Ø €€€Ø Ð Ð Ð Ð Ð Ø +Ð +Ð +Ð +Ð +Ð +Ð +Ð +Ð +Ð +Ø *Ð *Ð *Ð *Ð *Ð *Ð *Ð *à ?Ð ?Ð ?Ð ?Ð ?Ð ?Ø )Ð )Ð )Ð )Ð )Ð )Ø <Ð <Ð <Ð <Ð <Ð <Ð <Ð <à 0Ð 0Ð 0Ð 0Ð 0Ð 0à$Ð Ø�9Ð6Ñ7Ô7Ð ØÐ ð -€Ø"Ð ð 0˜9Ô/°Ñ@Ô@ÐAÐ ð :Ð ð	ð 	ð 	ð 	ð 	�
ñ 	ô 	ð 	ð $ð ð ð ð ð˜ð  ð ð ð ð ðD˜sð ¨#ð °$°s´)ð ð ð ð ðB	6˜Cð 	6ð 	6ð 	6ð 	6ð
˜Cð 
 Dð 
ð 
ð 
ð 
ð2˜#ð  #ð ð ð ð ð&$$ð $$ð $$ð $$ð $$�kñ $$ô $$ð $$ðN Sð Ð->ð ð ð ð ðÐ$5ð ¸$ð ð ð ð ð Ð1Bð  Àtð  ð  ð  ð  ð,R°¸#´ð RÀ4ð Rð Rð Rð Rð¨cð ¸hÀs¼mð ÐPXÐY^ÔP_ð ð ð ð ð %¨#ð %ð %ð %ð %ðV ØØ"Ø#Ø/ð7Lð 7Lð 7LØ	ð7Lð ð7Lð ð	7Lð
 �dŒ^ð7Lð ˜Œ}ð7Lð ð7Lð 7Lð 7Lð 7Lð 7Lð 7Lr   