libzypp  17.31.8
curlhelper.cc
Go to the documentation of this file.
1 /*---------------------------------------------------------------------\
2 | ____ _ __ __ ___ |
3 | |__ / \ / / . \ . \ |
4 | / / \ V /| _/ _/ |
5 | / /__ | | | | | | |
6 | /_____||_| |_| |_| |
7 | |
8 \---------------------------------------------------------------------*/
12 #include "private/curlhelper_p.h"
13 
14 #include <zypp/APIConfig.h>
15 
16 #include <zypp-core/fs/PathInfo.h>
17 #include <zypp-core/Pathname.h>
18 #include <zypp-core/base/Logger.h>
19 #include <zypp-core/base/String.h>
20 #include <zypp-curl/ProxyInfo>
21 #include <zypp-curl/auth/CurlAuthData>
22 #include <zypp-media/MediaException>
23 #include <list>
24 #include <string>
25 
26 using std::endl;
27 using namespace zypp;
28 
29 namespace zypp
30 {
31  namespace env
32  {
33  namespace
34  {
35  inline int getZYPP_MEDIA_CURL_IPRESOLVE()
36  {
37  int ret = 0;
38  if ( const char * envp = getenv( "ZYPP_MEDIA_CURL_IPRESOLVE" ) )
39  {
40  WAR << "env set: $ZYPP_MEDIA_CURL_IPRESOLVE='" << envp << "'" << std::endl;
41  if ( strcmp( envp, "4" ) == 0 ) ret = 4;
42  else if ( strcmp( envp, "6" ) == 0 ) ret = 6;
43  }
44  return ret;
45  }
46  } //namespace
47 
49  {
50  static int _v = getZYPP_MEDIA_CURL_IPRESOLVE();
51  return _v;
52  }
53  } // namespace env
54 } // namespace zypp
55 
56 namespace internal
57 {
58 
60 {
61  // function-level static <=> std::call_once
62  static bool once __attribute__ ((__unused__)) = ( [] {
63  if ( curl_global_init( CURL_GLOBAL_ALL ) != 0 )
64  WAR << "curl global init failed" << std::endl;
65  } (), true );
66 }
67 
68 int log_curl(CURL *, curl_infotype info,
69  char *ptr, size_t len, void *max_lvl)
70 {
71  if ( max_lvl == nullptr )
72  return 0;
73 
74  long maxlvl = *((long *)max_lvl);
75 
76  char pfx = ' ';
77  switch( info )
78  {
79  case CURLINFO_TEXT: if ( maxlvl < 1 ) return 0; pfx = '*'; break;
80  case CURLINFO_HEADER_IN: if ( maxlvl < 2 ) return 0; pfx = '<'; break;
81  case CURLINFO_HEADER_OUT: if ( maxlvl < 2 ) return 0; pfx = '>'; break;
82  default:
83  return 0;
84  }
85 
86  std::vector<std::string> lines;
87  str::split( std::string(ptr,len), std::back_inserter(lines), "\r\n" );
88  for( const auto & line : lines )
89  {
90  if ( str::startsWith( line, "Authorization:" ) ) {
91  std::string::size_type pos { line.find( " ", 15 ) }; // Authorization: <type> <credentials>
92  if ( pos == std::string::npos )
93  pos = 15;
94  DBG << pfx << " " << line.substr( 0, pos ) << " <credentials removed>" << std::endl;
95  }
96  else
97  DBG << pfx << " " << line << std::endl;
98  }
99  return 0;
100 }
101 
102 size_t log_redirects_curl( char *ptr, size_t size, size_t nmemb, void *userdata)
103 {
104  //INT << "got header: " << std::string(ptr, ptr + size*nmemb) << endl;
105 
106  char * lstart = ptr, * lend = ptr;
107  size_t pos = 0;
108  size_t max = size * nmemb;
109  while (pos + 1 < max)
110  {
111  // get line
112  for (lstart = lend; *lend != '\n' && pos < max; ++lend, ++pos);
113 
114  // look for "Location"
115  if ( strncasecmp( lstart, "Location:", 9 ) == 0 )
116  {
117  std::string line { lstart, *(lend-1)=='\r' ? lend-1 : lend };
118  DBG << "redirecting to " << line << std::endl;
119  if ( userdata ) {
120  *reinterpret_cast<std::string *>( userdata ) = line;
121  }
122  return max;
123  }
124 
125  // continue with the next line
126  if (pos + 1 < max)
127  {
128  ++lend;
129  ++pos;
130  }
131  else
132  break;
133  }
134 
135  return max;
136 }
137 
143 {
144  {
145  const std::string & param { url.getQueryParam("timeout") };
146  if( ! param.empty() )
147  {
148  long num = str::strtonum<long>(param);
149  if( num >= 0 && num <= TRANSFER_TIMEOUT_MAX )
150  s.setTimeout( num );
151  }
152  }
153  {
154  std::string param { url.getUsername() };
155  if ( ! param.empty() )
156  {
157  s.setUsername( std::move(param) );
158  param = url.getPassword();
159  if ( ! param.empty() )
160  s.setPassword( std::move(param) );
161  }
162  else
163  {
164  // if there is no username, set anonymous auth
165  if ( ( url.getScheme() == "ftp" || url.getScheme() == "tftp" ) && s.username().empty() )
166  s.setAnonymousAuth();
167  }
168  }
169  if ( url.getScheme() == "https" )
170  {
171  s.setVerifyPeerEnabled( false );
172  s.setVerifyHostEnabled( false );
173 
174  const std::string & verify { url.getQueryParam("ssl_verify") };
175  if( verify.empty() || verify == "yes" )
176  {
177  s.setVerifyPeerEnabled( true );
178  s.setVerifyHostEnabled( true );
179  }
180  else if ( verify == "no" )
181  {
182  s.setVerifyPeerEnabled( false );
183  s.setVerifyHostEnabled( false );
184  }
185  else
186  {
187  std::vector<std::string> flags;
188  str::split( verify, std::back_inserter(flags), "," );
189  for ( const auto & flag : flags )
190  {
191  if ( flag == "host" )
192  s.setVerifyHostEnabled( true );
193  else if ( flag == "peer" )
194  s.setVerifyPeerEnabled( true );
195  else
196  ZYPP_THROW( media::MediaBadUrlException(url, "Unknown ssl_verify flag "+flag) );
197  }
198  }
199  }
200  {
201  Pathname ca_path { url.getQueryParam("ssl_capath") };
202  if( ! ca_path.empty() )
203  {
204  if( ! PathInfo(ca_path).isDir() || ! ca_path.absolute() )
205  ZYPP_THROW(media::MediaBadUrlException(url, "Invalid ssl_capath path"));
206  else
207  s.setCertificateAuthoritiesPath( std::move(ca_path) );
208  }
209  }
210  {
211  Pathname client_cert { url.getQueryParam("ssl_clientcert") };
212  if( ! client_cert.empty() )
213  {
214  if( ! PathInfo(client_cert).isFile() || ! client_cert.absolute() )
215  ZYPP_THROW(media::MediaBadUrlException(url, "Invalid ssl_clientcert file"));
216  else
217  s.setClientCertificatePath( std::move(client_cert) );
218  }
219  }
220  {
221  Pathname client_key { url.getQueryParam("ssl_clientkey") };
222  if( ! client_key.empty() )
223  {
224  if( ! PathInfo(client_key).isFile() || ! client_key.absolute() )
225  ZYPP_THROW(media::MediaBadUrlException(url, "Invalid ssl_clientkey file"));
226  else
227  s.setClientKeyPath( std::move(client_key) );
228  }
229  }
230  {
231  std::string param { url.getQueryParam( "proxy" ) };
232  if ( ! param.empty() )
233  {
234  if ( param == EXPLICITLY_NO_PROXY ) {
235  // Workaround TransferSettings shortcoming: With an
236  // empty proxy string, code will continue to look for
237  // valid proxy settings. So set proxy to some non-empty
238  // string, to indicate it has been explicitly disabled.
240  s.setProxyEnabled(false);
241  }
242  else {
243  const std::string & proxyport { url.getQueryParam( "proxyport" ) };
244  if ( ! proxyport.empty() ) {
245  param += ":";
246  param += proxyport;
247  }
248  s.setProxy( std::move(param) );
249  s.setProxyEnabled( true );
250  }
251  }
252  }
253  {
254  std::string param { url.getQueryParam( "proxyuser" ) };
255  if ( ! param.empty() )
256  {
257  s.setProxyUsername( std::move(param) );
258  s.setProxyPassword( url.getQueryParam( "proxypass" ) );
259  }
260  }
261  {
262  // HTTP authentication type
263  std::string param { url.getQueryParam("auth") };
264  if ( ! param.empty() && (url.getScheme() == "http" || url.getScheme() == "https") )
265  {
266  try
267  {
268  media::CurlAuthData::auth_type_str2long (param ); // check if we know it
269  }
270  catch ( const media::MediaException & ex_r )
271  {
272  DBG << "Rethrowing as MediaUnauthorizedException.";
273  ZYPP_THROW(media::MediaUnauthorizedException(url, ex_r.msg(), "", ""));
274  }
275  s.setAuthType( std::move(param) );
276  }
277  }
278  {
279  // workarounds
280  const std::string & param { url.getQueryParam("head_requests") };
281  if( ! param.empty() && param == "no" )
282  s.setHeadRequestsAllowed( false );
283  }
284 }
285 
291 {
292  media::ProxyInfo proxy_info;
293  if ( proxy_info.useProxyFor( url ) )
294  {
295  // We must extract any 'user:pass' from the proxy url
296  // otherwise they won't make it into curl (.curlrc wins).
297  try {
298  Url u( proxy_info.proxy( url ) );
300  // don't overwrite explicit auth settings
301  if ( s.proxyUsername().empty() )
302  {
303  s.setProxyUsername( u.getUsername( url::E_ENCODED ) );
304  s.setProxyPassword( u.getPassword( url::E_ENCODED ) );
305  }
306  s.setProxyEnabled( true );
307  }
308  catch (...) {} // no proxy if URL is malformed
309  }
310 }
311 
312 void curlEscape( std::string & str_r,
313  const char char_r, const std::string & escaped_r ) {
314  for ( std::string::size_type pos = str_r.find( char_r );
315  pos != std::string::npos; pos = str_r.find( char_r, pos ) ) {
316  str_r.replace( pos, 1, escaped_r );
317  }
318 }
319 
320 std::string curlEscapedPath( std::string path_r ) {
321  curlEscape( path_r, ' ', "%20" );
322  return path_r;
323 }
324 
325 std::string curlUnEscape( std::string text_r ) {
326  char * tmp = curl_unescape( text_r.c_str(), 0 );
327  std::string ret( tmp );
328  curl_free( tmp );
329  return ret;
330 }
331 
333 {
334  Url curlUrl (url);
335  curlUrl.setUsername( "" );
336  curlUrl.setPassword( "" );
337  curlUrl.setPathParams( "" );
338  curlUrl.setFragment( "" );
339  curlUrl.delQueryParam("cookies");
340  curlUrl.delQueryParam("proxy");
341  curlUrl.delQueryParam("proxyport");
342  curlUrl.delQueryParam("proxyuser");
343  curlUrl.delQueryParam("proxypass");
344  curlUrl.delQueryParam("ssl_capath");
345  curlUrl.delQueryParam("ssl_verify");
346  curlUrl.delQueryParam("ssl_clientcert");
347  curlUrl.delQueryParam("timeout");
348  curlUrl.delQueryParam("auth");
349  curlUrl.delQueryParam("username");
350  curlUrl.delQueryParam("password");
351  curlUrl.delQueryParam("mediahandler");
352  curlUrl.delQueryParam("credentials");
353  curlUrl.delQueryParam("head_requests");
354  return curlUrl;
355 }
356 
357 // bsc#933839: propagate proxy settings passed in the repo URL
358 zypp::Url propagateQueryParams( zypp::Url url_r, const zypp::Url & template_r )
359 {
360  using namespace std::literals::string_literals;
361  for ( const std::string &param : { "proxy"s, "proxyport"s, "proxyuser"s, "proxypass"s} )
362  {
363  const std::string & value( template_r.getQueryParam( param ) );
364  if ( ! value.empty() )
365  url_r.setQueryParam( param, value );
366  }
367  return url_r;
368 }
369 
370 }
std::string getScheme() const
Returns the scheme name of the URL.
Definition: Url.cc:533
void setPassword(const std::string &pass, EEncoding eflag=zypp::url::E_DECODED)
Set the password in the URL authority.
Definition: Url.cc:739
void globalInitCurlOnce()
Definition: curlhelper.cc:59
size_t log_redirects_curl(char *ptr, size_t size, size_t nmemb, void *userdata)
Definition: curlhelper.cc:102
void setQueryParam(const std::string &param, const std::string &value)
Set or add value for the specified query parameter.
Definition: Url.cc:838
#define ZYPP_THROW(EXCPT)
Drops a logline and throws the Exception.
Definition: Exception.h:428
zypp::Url propagateQueryParams(zypp::Url url_r, const zypp::Url &template_r)
Definition: curlhelper.cc:358
Flag to request encoded string(s).
Definition: UrlUtils.h:53
void setPassword(const std::string &val_r)
sets the auth password
int log_curl(CURL *, curl_infotype info, char *ptr, size_t len, void *max_lvl)
Definition: curlhelper.cc:68
Holds transfer setting.
const std::string & proxyUsername() const
proxy auth username
Url clearQueryString(const Url &url)
Definition: curlhelper.cc:332
bool useProxyFor(const Url &url_r) const
Return true if enabled and url_r does not match noProxy.
Definition: proxyinfo.cc:55
static const ViewOption WITH_SCHEME
Option to include scheme name in the URL string.
Definition: UrlBase.h:51
static const ViewOption WITH_HOST
Option to include hostname in the URL string.
Definition: UrlBase.h:74
void setPathParams(const std::string &params)
Set the path parameters.
Definition: Url.cc:791
void setUsername(const std::string &val_r)
sets the auth username
std::string curlEscapedPath(std::string path_r)
Definition: curlhelper.cc:320
void setHeadRequestsAllowed(bool allowed)
set whether HEAD requests are allowed
void setUsername(const std::string &user, EEncoding eflag=zypp::url::E_DECODED)
Set the username in the URL authority.
Definition: Url.cc:730
void setFragment(const std::string &fragment, EEncoding eflag=zypp::url::E_DECODED)
Set the fragment string in the URL.
Definition: Url.cc:722
const std::string & username() const
auth username
void setAnonymousAuth()
sets anonymous authentication (ie: for ftp)
int ZYPP_MEDIA_CURL_IPRESOLVE()
4/6 to force IPv4/v6
Definition: curlhelper.cc:48
void curlEscape(std::string &str_r, const char char_r, const std::string &escaped_r)
Definition: curlhelper.cc:312
unsigned split(const C_Str &line_r, TOutputIterator result_r, const C_Str &sepchars_r=" \, const Trim trim_r=NO_TRIM)
Split line_r into words.
Definition: String.h:531
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:660
void setAuthType(const std::string &val_r)
set the allowed authentication types
void setProxy(const std::string &val_r)
proxy to use if it is enabled
Just inherits Exception to separate media exceptions.
#define WAR
Definition: Logger.h:97
bool startsWith(const C_Str &str_r, const C_Str &prefix_r)
alias for hasPrefix
Definition: String.h:1085
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:142
void setTimeout(long t)
set the transfer timeout
std::string proxy(const Url &url) const
Definition: proxyinfo.cc:43
SolvableIdType size_type
Definition: PoolMember.h:126
struct zypp::media::MediaBlock __attribute__
void setProxyPassword(const std::string &val_r)
sets the proxy password
std::string curlUnEscape(std::string text_r)
Definition: curlhelper.cc:325
static long auth_type_str2long(std::string &auth_type_str)
Converts a string of comma separated list of authetication type names into a long of ORed CURLAUTH_* ...
Definition: curlauthdata.cc:50
Wrapper class for ::stat/::lstat.
Definition: PathInfo.h:220
void setClientCertificatePath(const Pathname &val_r)
Sets the SSL client certificate file.
void fillSettingsSystemProxy(const Url &url, media::TransferSettings &s)
Reads the system proxy configuration and fills the settings structure proxy information.
Definition: curlhelper.cc:290
void setProxyUsername(const std::string &val_r)
sets the proxy user
void setCertificateAuthoritiesPath(const Pathname &val_r)
Sets the SSL certificate authorities path.
#define EXPLICITLY_NO_PROXY
Definition: curlhelper_p.h:25
static const ViewOption WITH_PORT
Option to include port number in the URL string.
Definition: UrlBase.h:81
Easy-to use interface to the ZYPP dependency resolver.
Definition: CodePitfalls.doc:1
#define TRANSFER_TIMEOUT_MAX
Definition: curlhelper_p.h:22
void setVerifyPeerEnabled(bool enabled)
Sets whether to verify host for ssl.
void setClientKeyPath(const Pathname &val_r)
Sets the SSL client key file.
std::string getPassword(EEncoding eflag=zypp::url::E_DECODED) const
Returns the password from the URL authority.
Definition: Url.cc:580
void setVerifyHostEnabled(bool enabled)
Sets whether to verify host for ssl.
Url manipulation class.
Definition: Url.h:91
void setProxyEnabled(bool enabled)
whether the proxy is used or not
#define DBG
Definition: Logger.h:95
void delQueryParam(const std::string &param)
remove the specified query parameter.
Definition: Url.cc:845
std::string getUsername(EEncoding eflag=zypp::url::E_DECODED) const
Returns the username from the URL authority.
Definition: Url.cc:572
const std::string & msg() const
Return the message string provided to the ctor.
Definition: Exception.h:195