libzypp  17.38.7
MediaNetworkCommonHandler.cc
Go to the documentation of this file.
1 /*---------------------------------------------------------------------\
2 | ____ _ __ __ ___ |
3 | |__ / \ / / . \ . \ |
4 | / / \ V /| _/ _/ |
5 | / /__ | | | | | | |
6 | /_____||_| |_| |_| |
7 | |
8 \---------------------------------------------------------------------*/
15 
16 #include <zypp/ZConfig.h>
17 #include <zypp-core/fs/PathInfo.h>
18 #include <zypp-core/base/Regex.h>
19 #include <zypp-core/base/Logger.h>
20 #include <zypp-core/base/Gettext.h>
21 #include <zypp/Target.h>
23 #include <zypp-media/auth/CredentialManager>
24 #include <zypp-curl/auth/CurlAuthData>
26 
27 #include <zypp/ZYppCallbacks.h>
28 
29 #include <fstream>
30 #include <curl/curl.h>
31 
32 using std::endl;
33 
34 namespace zypp::media
35 {
36  MediaNetworkCommonHandler::MediaNetworkCommonHandler( const MirroredOrigin &origin_r, const Pathname &attach_point_r, const Pathname &urlpath_below_attachpoint_r, const bool does_download_r)
37  : MediaHandler( origin_r, attach_point_r, urlpath_below_attachpoint_r, does_download_r )
38  {
39  _redirTargets.clear ();
40  std::transform ( _origin.begin(), _origin.end(), std::back_inserter(_redirTargets), []( const OriginEndpoint &url_r ) { return findGeoIPRedirect(url_r.url()); } );
41 
42  // initialize default mirror order: mirrors first, then authorities
43  _mirrOrder.reserve( _origin.endpointCount() );
44  uint authCount = _origin.authorityCount();
45  for( unsigned i = authCount; i < _origin.endpointCount () ; i++ ) { _mirrOrder.push_back(i); }
46  for( unsigned i = 0; i < authCount ; i++ ) { _mirrOrder.push_back(i); }
47  }
48 
50  {
51  if ( next )
53 
54  if( !isUseableAttachPoint( attachPoint() ) ) {
56  }
57 
58  disconnectFrom(); // clean state if needed
59 
60  // here : setup TransferSettings
62 
63  // FIXME: need a derived class to propelly compare url's
64  MediaSourceRef media( new MediaSource( url().getScheme(), url().asString()) );
65  setMediaSource(media);
66  }
67 
69  {
70  return MediaHandler::checkAttachPoint( apoint, true, true);
71  }
72 
74  {
76  }
77 
79  {
80  try {
81  const auto &conf = ZConfig::instance();
82  if ( !conf.geoipEnabled() ) {
83  MIL << "GeoIp rewrites disabled via ZConfig." << std::endl;
84  return Url();
85  }
86 
87  if ( !( url.getQueryParam("COUNTRY").empty() && url.getQueryParam("AVOID_COUNTRY").empty() )) {
88  MIL << "GeoIp rewrites disabled since the baseurl " << url << " uses an explicit country setting." << std::endl;
89  return Url();
90  }
91 
92  const auto &hostname = url.getHost();
93  auto geoipFile = conf.geoipCachePath() / hostname ;
94  if ( PathInfo( geoipFile ).isFile() ) {
95 
96  MIL << "Found GeoIP file for host: " << hostname << std::endl;
97 
98  std::ifstream in( geoipFile.asString() );
99  if (!in.is_open()) {
100  MIL << "Failed to open GeoIP for host: " << hostname << std::endl;
101  return Url();
102  }
103 
104  try {
105  std::string newHost;
106  in >> newHost;
107 
108  Url newUrl = url;
109  newUrl.setHost( newHost );
110 
111  MIL << "Found GeoIP rewrite: " << hostname << " -> " << newHost << std::endl;
112 
113  return newUrl;
114 
115  } catch ( const zypp::Exception &e ) {
116  ZYPP_CAUGHT(e);
117  MIL << "No valid GeoIP rewrite target found for " << url << std::endl;
118  }
119  }
120  } catch ( const zypp::Exception &e ) {
121  ZYPP_CAUGHT(e);
122  MIL << "Failed to query GeoIP data, url rewriting disabled." << std::endl;
123  }
124 
125  // no rewrite
126  return Url();
127  }
128 
130 
132  {
133  // Use absolute file name to prevent access of files outside of the
134  // hierarchy below the attach point.
135  getFileCopy( file, localPath(file.filename()).absolutename() );
136  }
137 
138  void MediaNetworkCommonHandler::getDir( const Pathname & dirname, bool recurse_r ) const
139  {
140  filesystem::DirContent content;
141  getDirInfo( content, dirname, /*dots*/false );
142 
143  for ( filesystem::DirContent::const_iterator it = content.begin(); it != content.end(); ++it ) {
144  Pathname filename = dirname + it->name;
145  int res = 0;
146 
147  switch ( it->type ) {
148  case filesystem::FT_NOT_AVAIL: // old directory.yast contains no typeinfo at all
149  case filesystem::FT_FILE:
150  getFile( OnMediaLocation( filename ) );
151  break;
152  case filesystem::FT_DIR: // newer directory.yast contain at least directory info
153  if ( recurse_r ) {
154  getDir( filename, recurse_r );
155  } else {
156  res = assert_dir( localPath( filename ) );
157  if ( res ) {
158  WAR << "Ignore error (" << res << ") on creating local directory '" << localPath( filename ) << "'" << endl;
159  }
160  }
161  break;
162  default:
163  // don't provide devices, sockets, etc.
164  break;
165  }
166  }
167  }
168 
169  void MediaNetworkCommonHandler::getDirInfo( std::list<std::string> & retlist,
170  const Pathname & dirname, bool dots ) const
171  {
172  getDirectoryYast( retlist, dirname, dots );
173  }
174 
176  const Pathname & dirname, bool dots ) const
177  {
178  getDirectoryYast( retlist, dirname, dots );
179  }
180 
182  {
183  // we need to add the release and identifier to the
184  // agent string.
185  // The target could be not initialized, and then this information
186  // is guessed.
187  // bsc#1212187: HTTP/2 RFC 9113 forbids fields ending with a space
188  static const std::string _value( str::trim( str::form(
189  "X-ZYpp-AnonymousId: %s",
190  Target::anonymousUniqueId( Pathname()/*guess root*/ ).c_str()
191  )));
192  return _value.c_str();
193  }
194 
196  {
197  // we need to add the release and identifier to the
198  // agent string.
199  // The target could be not initialized, and then this information
200  // is guessed.
201  // bsc#1212187: HTTP/2 RFC 9113 forbids fields ending with a space
202  static const std::string _value( str::trim( str::form(
203  "X-ZYpp-DistributionFlavor: %s",
204  Target::distributionFlavor( Pathname()/*guess root*/ ).c_str()
205  )));
206  return _value.c_str();
207  }
208 
209  Url MediaNetworkCommonHandler::getFileUrl( int mirrorIdx, const Pathname & filename_r ) const
210  {
211  static const zypp::str::regex invalidRewrites("^.*\\/repomd.xml(.asc|.key)?$|^\\/geoip$");
212 
213  if ( mirrorIdx < 0 || mirrorIdx > _redirTargets.size() )
214  return {};
215 
216  const bool canRedir = _redirTargets[mirrorIdx].isValid() && !invalidRewrites.matches(filename_r.asString());
217  const auto &baseUrl = ( canRedir ) ? _redirTargets[mirrorIdx] : _origin[mirrorIdx].url();
218 
219  if ( canRedir )
220  MIL << "Redirecting " << filename_r << " request to geoip location." << std::endl;
221 
222  // Simply extend the URLs pathname:
223  Url newurl { baseUrl };
224  newurl.appendPathName( filename_r );
225  return newurl;
226  }
227 
229  // we need to add the release and identifier to the
230  // agent string.
231  // The target could be not initialized, and then this information
232  // is guessed.
233  // bsc#1212187: HTTP/2 RFC 9113 forbids fields ending with a space
234  static const std::string _value(str::trim(str::form(
235  "ZYpp " LIBZYPP_VERSION_STRING " (curl %s) %s",
236  curl_version_info(CURLVERSION_NOW)->version,
237  Target::targetDistribution(Pathname() /*guess root*/).c_str())));
238  return _value.c_str();
239  }
240 
242  {
243  // fill some settings from url query parameters
244  try
245  {
247 
248  CredentialManager cm(CredManagerOptions(ZConfig::instance().repoManagerRoot()));
249  for( OriginEndpoint &u : _origin ) {
250 
251  u.setConfig( MIRR_SETTINGS_KEY.data() , std::make_any<TransferSettings>()); // init or reset to default
252 
253  if ( !u.isValid() )
255 
256  TransferSettings &set = u.getConfig<TransferSettings>( MIRR_SETTINGS_KEY.data() );
257 
258  checkProtocol(u.url());
259 
260  set.setUserAgentString(agentString());
261 
262  // apply MediaUrl settings
263  if ( u.hasConfig ("http-headers") ) {
264  // Set up the handler
265  for ( const auto & el : u.getConfig<UrlResolverPlugin::HeaderList>("http-headers") ) {
266  std::string header { el.first };
267  header += ": ";
268  header += el.second;
269  MIL << "Added custom header -> " << header << std::endl;
270  set.addHeader( std::move(header) );
271  }
272  }
273 
274  // add custom headers for download.opensuse.org (bsc#955801)
275  if ( u.url().getHost() == "download.opensuse.org" )
276  {
277  set.addHeader(anonymousIdHeader());
278  set.addHeader(distributionFlavorHeader());
279  }
280  set.addHeader("Pragma:");
281 
282  // apply legacy Url encoded settings
283  ::internal::fillSettingsFromUrl( u.url(), set);
284 
285  // if the proxy was not set (or explicitly unset) by url, then look...
286  if ( set.proxy().empty() )
287  {
288  // ...at the system proxy settings
290  }
291 
292  /* Fixes bsc#1174011 "auth=basic ignored in some cases"
293  * We should proactively add the password to the request if basic auth is configured
294  * and a password is available in the credentials but not in the URL.
295  *
296  * We will be a bit paranoid here and require that the URL has a user embedded, otherwise we go the default route
297  * and ask the server first about the auth method
298  */
299  if ( set.authType() == "basic" && not set.username().empty() && set.password().empty() ) {
300  const auto cred = cm.getCred( u.url() );
301  if ( cred && cred->valid() ) {
302  set.setPassword(cred->password());
303  }
304  }
305  }
306  }
307  catch ( const MediaException &e )
308  {
309  disconnectFrom();
310  ZYPP_RETHROW(e);
311  }
312  }
313 
315  {
316  for( OriginEndpoint &u : _origin ) {
317  u.eraseConfigValue ( MIRR_SETTINGS_KEY.data() );
318  }
319  }
320 
321 
322  bool MediaNetworkCommonHandler::authenticate(const Url &url, TransferSettings &settings, const std::string & availAuthTypes, bool firstTry)
323  {
325  CredentialManager cm(CredManagerOptions(ZConfig::instance().repoManagerRoot()));
326  return authenticate( url, cm, settings, availAuthTypes, firstTry );
327  }
328 
329  std::vector<unsigned int> MediaNetworkCommonHandler::mirrorOrder(const OnMediaLocation &loc) const
330  {
331  uint authCount = _origin.authorityCount();
332 
333  if ( !loc.mirrorsAllowed () ) {
334  MIL << "Fetching file " << loc << " from authorities only ( " << authCount << " ): " << _origin << std::endl;
335  std::vector<unsigned> authOrder;
336  authOrder.reserve( authCount );
337  // Filter _mirrOrder to include only authority indices
338  for ( unsigned idx : _mirrOrder ) {
339  if ( idx < authCount )
340  authOrder.push_back( idx );
341  }
342  return authOrder;
343  }
344 
345  return _mirrOrder;
346  }
347 
349  {
350  auto it = std::find( _mirrOrder.begin(), _mirrOrder.end(), mirr );
351  if ( it != _mirrOrder.end() && _mirrOrder.size() > 1 )
352  {
353  // move found index to the end
354  std::rotate( it, it + 1, _mirrOrder.end() );
355  }
356  }
357 
358  bool MediaNetworkCommonHandler::authenticate( const Url &url, CredentialManager &cm, TransferSettings &settings, const std::string & availAuthTypes, bool firstTry )
359  {
360  CurlAuthData_Ptr credentials;
361 
362  // get stored credentials
363  AuthData_Ptr cmcred = cm.getCred(url);
364 
365  if (cmcred && firstTry)
366  {
367  credentials.reset(new CurlAuthData(*cmcred));
368  DBG << "got stored credentials:" << endl << *credentials << endl;
369  }
370  // if not found, ask user
371  else
372  {
373 
374  CurlAuthData_Ptr curlcred;
375  curlcred.reset(new CurlAuthData());
377 
378  // preset the username if present in current url
379  if (!url.getUsername().empty() && firstTry)
380  curlcred->setUsername(url.getUsername());
381  // if CM has found some credentials, preset the username from there
382  else if (cmcred)
383  curlcred->setUsername(cmcred->username());
384 
385  // indicate we have no good credentials from CM
386  cmcred.reset();
387 
388  std::string prompt_msg = str::Format(_("Authentication required for '%s'")) % url.asString();
389 
390  // set available authentication types from the exception
391  // might be needed in prompt
392  curlcred->setAuthType(availAuthTypes);
393 
394  // ask user
395  if (auth_report->prompt(url, prompt_msg, *curlcred))
396  {
397  DBG << "callback answer: retry" << endl
398  << "CurlAuthData: " << *curlcred << endl;
399 
400  if (curlcred->valid())
401  {
402  credentials = curlcred;
403  // if (credentials->username() != _url.url().getUsername())
404  // _url.url().setUsername(credentials->username());
412  }
413  }
414  else
415  {
416  DBG << "callback answer: cancel" << endl;
417  }
418  }
419 
420  // set username and password
421  if (credentials)
422  {
423  settings.setUsername(credentials->username());
424  settings.setPassword(credentials->password());
425 
426  // set available authentication types from the exception
427  if (credentials->authType() == CURLAUTH_NONE)
428  credentials->setAuthType(availAuthTypes);
429 
430  // set auth type (seems this must be set _after_ setting the userpwd)
431  if (credentials->authType() != CURLAUTH_NONE) {
432  settings.setAuthType(credentials->authTypeAsString());
433  }
434 
435  if (!cmcred)
436  {
437  credentials->setUrl(url);
438  cm.addCred(*credentials);
439  cm.save();
440  }
441 
442  return true;
443  }
444 
445  return false;
446  }
447 
448 
449 } // namespace zypp::media
std::string targetDistribution() const
This is register.target attribute of the installed base product.
Definition: Target.cc:102
#define MIL
Definition: Logger.h:103
std::vector< unsigned > mirrorOrder(const OnMediaLocation &loc) const
int assert_dir(const Pathname &path, unsigned mode)
Like &#39;mkdir -p&#39;.
Definition: PathInfo.cc:338
#define ZYPP_THROW(EXCPT)
Drops a logline and throws the Exception.
Definition: Exception.h:459
Describes a resource file located on a medium.
Regular expression.
Definition: Regex.h:94
static ZConfig & instance()
Singleton ctor.
Definition: ZConfig.cc:756
void setPassword(const std::string &val_r)
sets the auth password
std::string asString(const Patch::Category &obj)
relates: Patch::Category string representation.
Definition: Patch.cc:122
virtual bool checkAttachPoint(const Pathname &apoint) const
Verify if the specified directory as attach point (root) as requires by the particular media handler ...
void appendPathName(const Pathname &path_r, EEncoding eflag_r=zypp::url::E_DECODED)
Extend the path name.
Definition: Url.cc:813
static constexpr std::string_view MIRR_SETTINGS_KEY
Holds transfer setting.
void save()
Saves any unsaved credentials added via addUserCred() or addGlobalCred() methods. ...
Url clearQueryString(const Url &url)
Definition: curlhelper.cc:401
void setAttachPoint(const Pathname &path, bool temp)
Set a new attach point.
virtual void disconnectFrom()
Call concrete handler to disconnect media.
Definition: MediaHandler.h:321
bool isUseableAttachPoint(const Pathname &path, bool mtab=true) const
Ask media manager, if the specified path is already used as attach point or if there are another atta...
void setUsername(const std::string &val_r)
sets the auth username
AuthData_Ptr getCred(const Url &url)
Get credentials for the specified url.
void setHost(const std::string &host)
Set the hostname or IP in the URL authority.
Definition: Url.cc:775
Convenient building of std::string with boost::format.
Definition: String.h:253
std::string form(const char *format,...) __attribute__((format(printf
Printf style construction of std::string.
Definition: String.cc:39
uint authorityCount() const
Edition * _value
Definition: SysContent.cc:311
bool authenticate(const Url &url, TransferSettings &settings, const std::string &availAuthTypes, bool firstTry)
bool mirrorsAllowed() const
The requested file is allowed to be fetched via mirrors ( defaults to true )
endpoint_iterator end()
std::list< DirEntry > DirContent
Returned by readdir.
Definition: PathInfo.h:534
Pathname localPath(const Pathname &pathname) const
Files provided will be available at &#39;localPath(filename)&#39;.
virtual void getFileCopy(const OnMediaLocation &file, const Pathname &targetFilename) const
Call concrete handler to provide a file under a different place in the file system (usually not under...
void attachTo(bool next) override
Call concrete handler to attach the media.
bool checkAttachPoint(const Pathname &apoint) const override
Verify if the specified directory as attach point (root) as requires by the particular media handler ...
#define ZYPP_RETHROW(EXCPT)
Drops a logline and rethrows, updating the CodeLocation.
Definition: Exception.h:479
std::string asString() const
Returns a default string representation of the Url object.
Definition: Url.cc:524
std::string getQueryParam(const std::string &param, EEncoding eflag=zypp::url::E_DECODED) const
Return the value for the specified query parameter.
Definition: Url.cc:687
Abstract base class for &#39;physical&#39; MediaHandler like MediaCD, etc.
Definition: MediaHandler.h:50
MirroredOrigin _origin
Contains the authority URL and mirrors.
Definition: MediaHandler.h:112
void setAuthType(const std::string &val_r)
set the allowed authentication types
std::string trim(const std::string &s, const Trim trim_r)
Definition: String.cc:226
Manages a data source characterized by an authoritative URL and a list of mirror URLs.
void setMediaSource(const MediaSourceRef &ref)
Set new media source reference.
const std::string & asString() const
String representation.
Definition: Pathname.h:94
Just inherits Exception to separate media exceptions.
#define WAR
Definition: Logger.h:104
shared_ptr< AuthData > AuthData_Ptr
Definition: authdata.h:81
void getDirInfo(std::list< std::string > &retlist, const Pathname &dirname, bool dots=true) const override
Call concrete handler to provide a content list of directory on media via retlist.
void getDirectoryYast(std::list< std::string > &retlist, const Pathname &dirname, bool dots=true) const
Retrieve and if available scan dirname/directory.yast.
void fillSettingsFromUrl(const Url &url, media::TransferSettings &s)
Fills the settings structure using options passed on the url for example ?timeout=x&proxy=foo.
Definition: curlhelper.cc:196
bool matches(const char *s, str::smatch &matches, int flags=none) const
Definition: Regex.cc:57
#define _(MSG)
Definition: Gettext.h:39
MediaNetworkCommonHandler(const MirroredOrigin &origin_r, const Pathname &attach_point_r, const Pathname &urlpath_below_attachpoint_r, const bool does_download_r)
const Pathname & filename() const
The path to the resource on the medium.
Media source internally used by MediaManager and MediaHandler.
Definition: MediaSource.h:37
#define ZYPP_CAUGHT(EXCPT)
Drops a logline telling the Exception was caught (in order to handle it).
Definition: Exception.h:475
Base class for Exception.
Definition: Exception.h:152
Pathname attachPoint() const
Return the currently used attach point.
Url url() const
Primary Url used.
Definition: MediaHandler.h:507
Url getFileUrl(int mirrorIdx, const Pathname &filename) const
concatenate the attach url and the filename to a complete download url
shared_ptr< CurlAuthData > CurlAuthData_Ptr
Definition: curlauthdata.h:102
std::string getHost(EEncoding eflag=zypp::url::E_DECODED) const
Returns the hostname or IP from the URL authority.
Definition: Url.cc:615
virtual void checkProtocol(const Url &url) const =0
check the url is supported by the curl library
void deprioritizeMirror(unsigned mirr) const
Move mirror index mirr to the end of the attempt list.
std::multimap< std::string, std::string > HeaderList
Wrapper class for ::stat/::lstat.
Definition: PathInfo.h:225
uint endpointCount() const
void fillSettingsSystemProxy(const Url &url, media::TransferSettings &s)
Reads the system proxy configuration and fills the settings structure proxy information.
Definition: curlhelper.cc:359
std::string anonymousUniqueId() const
anonymous unique id
Definition: Target.cc:132
void setupTransferSettings()
initializes the curl easy handle with the data from the url
void getDir(const Pathname &dirname, bool recurse_r) const override
Call concrete handler to provide directory content (not recursive!) below attach point.
static zypp::Url findGeoIPRedirect(const zypp::Url &url)
Rewrites the baseURL to the geoIP target if one is found in the metadata cache, otherwise simply retu...
Pathname createAttachPoint() const
Try to create a default / temporary attach point.
void addCred(const AuthData &cred)
Add new credentials with user callbacks.
Curl HTTP authentication data.
Definition: curlauthdata.h:22
std::string distributionFlavor() const
This is flavor attribute of the installed base product but does not require the target to be loaded a...
Definition: Target.cc:127
void getFile(const OnMediaLocation &file) const override
Call concrete handler to provide file below attach point.
Represents a single, configurable network endpoint, combining a URL with specific access settings...
endpoint_iterator begin()
Url manipulation class.
Definition: Url.h:92
#define DBG
Definition: Logger.h:102
auto transform(Container< Msg, CArgs... > &&val, Transformation &&transformation)
Definition: transform.h:64
std::string getUsername(EEncoding eflag=zypp::url::E_DECODED) const
Returns the username from the URL authority.
Definition: Url.cc:599