@@ -37,7 +37,7 @@ use serde::{Deserialize, Serialize};
3737use std:: net:: { IpAddr , Ipv4Addr , Ipv6Addr } ;
3838use tokio:: net:: lookup_host;
3939use tracing:: { info, warn} ;
40- use url:: Url ;
40+ use url:: { Host , Url } ;
4141use urlencoding:: encode;
4242use webpage:: HTML ;
4343
@@ -64,24 +64,7 @@ pub async fn fetch_link_metadata(
6464 return Err ( LemmyErrorType :: InvalidUrl . into ( ) ) ;
6565 }
6666
67- // Resolve the domain and throw an error if it points to any internal IP,
68- // using logic from nightly IpAddr::is_global.
69- if !cfg ! ( debug_assertions) {
70- // TODO: Replace with IpAddr::is_global() once stabilized
71- // https://doc.rust-lang.org/std/net/enum.IpAddr.html#method.is_global
72- let domain = url. domain ( ) . ok_or ( LemmyErrorType :: UrlWithoutDomain ) ?;
73- let invalid_ip =
74- lookup_host ( ( domain. to_owned ( ) , 80 ) )
75- . await ?
76- . any ( |addr| match addr. ip ( ) . to_canonical ( ) {
77- IpAddr :: V4 ( addr) => v4_is_invalid ( addr) ,
78- IpAddr :: V6 ( addr) => v6_is_invalid ( addr) ,
79- } ) ;
80- if invalid_ip {
81- return Err ( LemmyErrorType :: InvalidUrl . into ( ) ) ;
82- }
83- }
84-
67+ validate_link_ip ( url) . await ?;
8568 info ! ( "Fetching site metadata for url: {}" , url) ;
8669 // We only fetch the first MB of data in order to not waste bandwidth especially for large
8770 // binary files. This high limit is particularly needed for youtube, which includes a lot of
@@ -161,6 +144,37 @@ pub async fn fetch_link_metadata(
161144 } )
162145}
163146
147+ /// Resolve the domain and throw an error if it points to any internal IP.
148+ pub async fn validate_link_ip ( url : & Url ) -> LemmyResult < ( ) > {
149+ if cfg ! ( debug_assertions) {
150+ return Ok ( ( ) ) ;
151+ }
152+ // Resolve the domain and throw an error if it points to any internal IP,
153+ // using logic from nightly IpAddr::is_global.
154+
155+ // TODO: Replace with IpAddr::is_global() once stabilized
156+ // https://doc.rust-lang.org/std/net/enum.IpAddr.html#method.is_global
157+ let mut ip = vec ! [ ] ;
158+ match url. host ( ) . ok_or ( LemmyErrorType :: UrlWithoutDomain ) ? {
159+ Host :: Domain ( domain) => ip. extend (
160+ lookup_host ( ( domain. to_owned ( ) , 80 ) )
161+ . await ?
162+ . map ( |s| s. ip ( ) . to_canonical ( ) ) ,
163+ ) ,
164+ Host :: Ipv4 ( ipv4) => ip. push ( ipv4. into ( ) ) ,
165+ Host :: Ipv6 ( ipv6) => ip. push ( ipv6. into ( ) ) ,
166+ } ;
167+
168+ let invalid_ip = ip. into_iter ( ) . any ( |addr| match addr {
169+ IpAddr :: V4 ( addr) => v4_is_invalid ( addr) ,
170+ IpAddr :: V6 ( addr) => v6_is_invalid ( addr) ,
171+ } ) ;
172+ if invalid_ip {
173+ return Err ( LemmyErrorType :: InvalidUrl . into ( ) ) ;
174+ }
175+ Ok ( ( ) )
176+ }
177+
164178fn v4_is_invalid ( v4 : Ipv4Addr ) -> bool {
165179 v4. is_private ( )
166180 || v4. is_loopback ( )
0 commit comments