66use crate :: config:: ConfigFile ;
77use crate :: errors:: * ;
88use crate :: manifest:: {
9- ImageIndexEntry , OciImageIndex , OciImageManifest , OciManifest , Versioned ,
9+ ImageIndexEntry , OciDescriptor , OciImageIndex , OciImageManifest , OciManifest , Versioned ,
1010 IMAGE_CONFIG_MEDIA_TYPE , IMAGE_LAYER_GZIP_MEDIA_TYPE , IMAGE_LAYER_MEDIA_TYPE ,
1111 IMAGE_MANIFEST_LIST_MEDIA_TYPE , IMAGE_MANIFEST_MEDIA_TYPE , OCI_IMAGE_INDEX_MEDIA_TYPE ,
1212 OCI_IMAGE_MEDIA_TYPE ,
@@ -361,7 +361,7 @@ impl Client {
361361 async move {
362362 let mut out: Vec < u8 > = Vec :: new ( ) ;
363363 debug ! ( "Pulling image layer" ) ;
364- this. pull_blob ( image, & layer. digest , & mut out) . await ?;
364+ this. pull_blob ( image, layer, & mut out) . await ?;
365365 Ok :: < _ , OciDistributionError > ( ImageLayer :: new (
366366 out,
367367 layer. media_type . clone ( ) ,
@@ -840,8 +840,7 @@ impl Client {
840840
841841 let mut out: Vec < u8 > = Vec :: new ( ) ;
842842 debug ! ( "Pulling config layer" ) ;
843- self . pull_blob ( image, & manifest. config . digest , & mut out)
844- . await ?;
843+ self . pull_blob ( image, & manifest. config , & mut out) . await ?;
845844 let media_type = manifest. config . media_type . clone ( ) ;
846845 let annotations = manifest. annotations . clone ( ) ;
847846 Ok ( ( manifest, digest, Config :: new ( out, media_type, annotations) ) )
@@ -864,26 +863,50 @@ impl Client {
864863 /// Pull a single layer from an OCI registry.
865864 ///
866865 /// This pulls the layer for a particular image that is identified by
867- /// the given digest . The image reference is used to find the
866+ /// the given layer descriptor . The image reference is used to find the
868867 /// repository and the registry, but it is not used to verify that
869868 /// the digest is a layer inside of the image. (The manifest is
870869 /// used for that.)
871870 pub async fn pull_blob < T : AsyncWrite + Unpin > (
872871 & self ,
873872 image : & Reference ,
874- digest : & str ,
873+ layer : & OciDescriptor ,
875874 mut out : T ,
876875 ) -> Result < ( ) > {
877- let url = self . to_v2_blob_url ( image. resolve_registry ( ) , image. repository ( ) , digest) ;
878- let mut stream = RequestBuilderWrapper :: from_client ( self , |client| client. get ( & url) )
876+ let url = self . to_v2_blob_url ( image. resolve_registry ( ) , image. repository ( ) , & layer. digest ) ;
877+
878+ let mut response = RequestBuilderWrapper :: from_client ( self , |client| client. get ( & url) )
879879 . apply_accept ( MIME_TYPES_DISTRIBUTION_MANIFEST ) ?
880880 . apply_auth ( image, RegistryOperation :: Pull )
881881 . await ?
882882 . into_request_builder ( )
883883 . send ( )
884- . await ?
885- . error_for_status ( ) ?
886- . bytes_stream ( ) ;
884+ . await ?;
885+
886+ if let Some ( urls) = & layer. urls {
887+ for url in urls {
888+ if response. error_for_status_ref ( ) . is_ok ( ) {
889+ break ;
890+ }
891+
892+ let url = Url :: parse ( url)
893+ . map_err ( |e| OciDistributionError :: UrlParseError ( e. to_string ( ) ) ) ?;
894+
895+ if url. scheme ( ) == "http" || url. scheme ( ) == "https" {
896+ // NOTE: we must not authenticate on additional URLs as those
897+ // can be abused to leak credentials or tokens. Please
898+ // refer to CVE-2020-15157 for more information.
899+ response =
900+ RequestBuilderWrapper :: from_client ( self , |client| client. get ( url. clone ( ) ) )
901+ . apply_accept ( MIME_TYPES_DISTRIBUTION_MANIFEST ) ?
902+ . into_request_builder ( )
903+ . send ( )
904+ . await ?
905+ }
906+ }
907+ }
908+
909+ let mut stream = response. error_for_status ( ) ?. bytes_stream ( ) ;
887910
888911 while let Some ( bytes) = stream. next ( ) . await {
889912 out. write_all ( & bytes?) . await ?;
@@ -899,16 +922,43 @@ impl Client {
899922 pub async fn pull_blob_stream (
900923 & self ,
901924 image : & Reference ,
902- digest : & str ,
925+ layer : & OciDescriptor ,
903926 ) -> Result < impl Stream < Item = std:: result:: Result < bytes:: Bytes , std:: io:: Error > > > {
904- let url = self . to_v2_blob_url ( image. resolve_registry ( ) , image. repository ( ) , digest) ;
905- let stream = RequestBuilderWrapper :: from_client ( self , |client| client. get ( & url) )
927+ let url = self . to_v2_blob_url ( image. resolve_registry ( ) , image. repository ( ) , & layer. digest ) ;
928+
929+ let mut response = RequestBuilderWrapper :: from_client ( self , |client| client. get ( & url) )
906930 . apply_accept ( MIME_TYPES_DISTRIBUTION_MANIFEST ) ?
907931 . apply_auth ( image, RegistryOperation :: Pull )
908932 . await ?
909933 . into_request_builder ( )
910934 . send ( )
911- . await ?
935+ . await ?;
936+
937+ if let Some ( urls) = & layer. urls {
938+ for url in urls {
939+ if response. error_for_status_ref ( ) . is_ok ( ) {
940+ break ;
941+ }
942+
943+ let url = Url :: parse ( url)
944+ . map_err ( |e| OciDistributionError :: UrlParseError ( e. to_string ( ) ) ) ?;
945+
946+ if url. scheme ( ) == "http" || url. scheme ( ) == "https" {
947+ // NOTE: we must not authenticate on additional URLs as those
948+ // can be abused to leak credentials or tokens. Please
949+ // refer to CVE-2020-15157 for more information.
950+ response =
951+ RequestBuilderWrapper :: from_client ( self , |client| client. get ( url. clone ( ) ) )
952+ . apply_accept ( MIME_TYPES_DISTRIBUTION_MANIFEST ) ?
953+ . into_request_builder ( )
954+ . send ( )
955+ . await ?
956+ }
957+ }
958+ }
959+
960+ let stream = response
961+ . error_for_status ( ) ?
912962 . bytes_stream ( )
913963 . map_err ( |e| std:: io:: Error :: new ( std:: io:: ErrorKind :: Other , e) ) ;
914964
@@ -1496,6 +1546,18 @@ pub fn linux_amd64_resolver(manifests: &[ImageIndexEntry]) -> Option<String> {
14961546 . map ( |entry| entry. digest . clone ( ) )
14971547}
14981548
1549+ /// A platform resolver that chooses the first windows/amd64 variant, if present
1550+ pub fn windows_amd64_resolver ( manifests : & [ ImageIndexEntry ] ) -> Option < String > {
1551+ manifests
1552+ . iter ( )
1553+ . find ( |entry| {
1554+ entry. platform . as_ref ( ) . map_or ( false , |platform| {
1555+ platform. os == "windows" && platform. architecture == "amd64"
1556+ } )
1557+ } )
1558+ . map ( |entry| entry. digest . clone ( ) )
1559+ }
1560+
14991561const MACOS : & str = "macos" ;
15001562const DARWIN : & str = "darwin" ;
15011563
@@ -2259,7 +2321,7 @@ mod test {
22592321 // This call likes to flake, so we try it at least 5 times
22602322 let mut last_error = None ;
22612323 for i in 1 ..6 {
2262- if let Err ( e) = c. pull_blob ( & reference, & layer0. digest , & mut file) . await {
2324+ if let Err ( e) = c. pull_blob ( & reference, & layer0, & mut file) . await {
22632325 println ! (
22642326 "Got error on pull_blob call attempt {}. Will retry in 1s: {:?}" ,
22652327 i, e
@@ -2304,7 +2366,7 @@ mod test {
23042366 let layer0 = & manifest. layers [ 0 ] ;
23052367
23062368 let layer_stream = c
2307- . pull_blob_stream ( & reference, & layer0. digest )
2369+ . pull_blob_stream ( & reference, & layer0)
23082370 . await
23092371 . expect ( "failed to pull blob stream" ) ;
23102372
@@ -2615,22 +2677,25 @@ mod test {
26152677 . parse ( )
26162678 . unwrap ( ) ;
26172679 let layer_data = vec ! [ 1u8 , 2 , 3 , 4 ] ;
2618- let layer_digest = sha256_digest ( & layer_data) ;
2619- c. push_blob ( & layer_reference, & [ 1 , 2 , 3 , 4 ] , & layer_digest)
2680+ let layer = OciDescriptor {
2681+ digest : sha256_digest ( & layer_data) ,
2682+ ..Default :: default ( )
2683+ } ;
2684+ c. push_blob ( & layer_reference, & [ 1 , 2 , 3 , 4 ] , & layer. digest )
26202685 . await
26212686 . expect ( "Failed to push" ) ;
26222687
26232688 // Mount the layer at `image-repository`
26242689 let image_reference: Reference = format ! ( "localhost:{}/image-repository" , port)
26252690 . parse ( )
26262691 . unwrap ( ) ;
2627- c. mount_blob ( & image_reference, & layer_reference, & layer_digest )
2692+ c. mount_blob ( & image_reference, & layer_reference, & layer . digest )
26282693 . await
26292694 . expect ( "Failed to mount" ) ;
26302695
26312696 // Pull the layer from `image-repository`
26322697 let mut buf = Vec :: new ( ) ;
2633- c. pull_blob ( & image_reference, & layer_digest , & mut buf)
2698+ c. pull_blob ( & image_reference, & layer , & mut buf)
26342699 . await
26352700 . expect ( "Failed to pull" ) ;
26362701
0 commit comments