XRootD
XrdHttpTpcConfigure.cc
Go to the documentation of this file.
1 
2 #include "XrdHttpTpcTPC.hh"
3 
4 #include <climits>
5 #include <dlfcn.h>
6 #include <fcntl.h>
7 
8 #include "XrdOuc/XrdOuca2x.hh"
9 #include "XrdOuc/XrdOucEnv.hh"
10 #include "XrdOuc/XrdOucStream.hh"
11 #include "XrdOuc/XrdOucPinPath.hh"
14 #include "XrdOuc/XrdOucTUtils.hh"
15 
16 using namespace TPC;
17 
18 
19 bool TPCHandler::Configure(const char *configfn, XrdOucEnv *myEnv)
20 {
21  XrdOucEnv cfgEnv;
22  XrdOucStream Config(&m_log, getenv("XRDINSTANCE"), &cfgEnv, "=====> ");
23 
25 
26  // test if XrdEC is used
27  usingEC = getenv("XRDCL_EC")? true : false;
28  // Test if the CRL checking is enabled
29  allowMissingCRL = (bool) myEnv->GetInt("http.allowmissingcrl");
30  std::string authLib;
31  std::string authLibParms;
32  int cfgFD = open(configfn, O_RDONLY, 0);
33  if (cfgFD < 0) {
34  m_log.Emsg("Config", errno, "open config file", configfn);
35  return false;
36  }
37  Config.Attach(cfgFD);
38  static const char *cvec[] = { "*** http tpc plugin config:", 0 };
39  Config.Capture(cvec);
40  const char *val;
41  while ((val = Config.GetMyFirstWord())) {
42  if (!strcmp("http.desthttps", val)) {
43  if (!(val = Config.GetWord())) {
44  Config.Close();
45  m_log.Emsg("Config", "http.desthttps value not specified");
46  return false;
47  }
48  if (!strcmp("1", val) || !strcasecmp("yes", val) || !strcasecmp("true", val)) {
49  m_desthttps = true;
50  } else if (!strcmp("0", val) || !strcasecmp("no", val) || !strcasecmp("false", val)) {
51  m_desthttps = false;
52  } else {
53  Config.Close();
54  m_log.Emsg("Config", "https.desthttps value is invalid", val);
55  return false;
56  }
57  } else if (!strcmp("tpc.allow", val)) {
58  if (!(val = Config.GetWord())) {
59  Config.Close();
60  m_log.Emsg("Config", "tpc.allow value not specified");
61  return false;
62  }
63  if (strcmp(val, "local") == 0) {
64  m_allow_local = true;
65  } else if (strcmp(val, "private") == 0) {
66  m_allow_private = true;
67  } else {
68  Config.Close();
69  m_log.Emsg("Config", "tpc.allow value is invalid", val);
70  return false;
71  }
72  } else if (!strcmp("tpc.deny", val)) {
73  if (!(val = Config.GetWord())) {
74  Config.Close();
75  m_log.Emsg("Config", "tpc.deny value not specified");
76  return false;
77  }
78  if (strcmp(val, "local") == 0) {
79  m_allow_local = false;
80  } else if (strcmp(val, "private") == 0) {
81  m_allow_private = false;
82  } else {
83  Config.Close();
84  m_log.Emsg("Config", "tpc.deny value is invalid", val);
85  return false;
86  }
87  } else if (!strcmp("tpc.trace", val)) {
88  if (!ConfigureLogger(Config)) {
89  Config.Close();
90  return false;
91  }
92  } else if (!strcmp("tpc.fixed_route", val)) {
93  if (!(val = Config.GetWord())) {
94  Config.Close();
95  m_log.Emsg("Config", "tpc.fixed_route value not specified");
96  return false;
97  }
98  if (!strcmp("1", val) || !strcasecmp("yes", val) || !strcasecmp("true", val)) {
99  m_fixed_route= true;
100  } else if (!strcmp("0", val) || !strcasecmp("no", val) || !strcasecmp("false", val)) {
101  m_fixed_route= false;
102  } else {
103  Config.Close();
104  m_log.Emsg("Config", "tpc.fixed_route value is invalid", val);
105  return false;
106  }
107  } else if (!strcmp("tpc.header2cgi",val)) {
108  // header2cgi parsing
109  if(XrdHttpProtocol::parseHeader2CGI(Config,m_log,hdr2cgimap)){
110  Config.Close();
111  return false;
112  }
113  // remove authorization header2cgi parsing as it will anyway be added to the CGI before the file open
114  // by the HTTP/TPC logic
115  auto authHdr = XrdOucTUtils::caseInsensitiveFind(hdr2cgimap,"authorization");
116  if(authHdr != hdr2cgimap.end()) {
117  hdr2cgimap.erase(authHdr);
118  }
119  } else if (!strcmp("tpc.low_speed", val)) {
120  if (!(val = Config.GetWord())) {
121  Config.Close();
122  m_log.Emsg("Config", "tpc.low_speed rate not specified.");
123  return false;
124  }
125 
126  long long low_speed_limit;
127  if (XrdOuca2x::a2sz(m_log, "low speed rate", val, &low_speed_limit, 0, LONG_MAX)) {
128  return false;
129  }
130  m_low_speed_limit = static_cast<long>(low_speed_limit);
131 
132  if ((val = Config.GetWord())) {
133  int low_speed_time;
134  if (XrdOuca2x::a2tm(m_log, "low speed time", val, &low_speed_time, 1)) {
135  return false;
136  }
137  m_low_speed_time = low_speed_time;
138  }
139  } else if (!strcmp("tpc.timeout", val)) {
140  if (!(val = Config.GetWord())) {
141  Config.Close();
142  m_log.Emsg("Config","tpc.timeout value not specified."); return false;
143  }
144  if (XrdOuca2x::a2tm(m_log, "timeout value", val, &m_timeout, 0)) return false;
145  // First byte timeout can be set separately from the continuous timeout.
146  if ((val = Config.GetWord())) {
147  if (XrdOuca2x::a2tm(m_log, "first byte timeout value", val, &m_first_timeout, 0)) return false;
148  } else {
149  m_first_timeout = 2*m_timeout;
150  }
151  }
152  }
153  Config.Close();
154 
155  // Internal override: allow xrdtpc to use a different ca dir from the one prepared by the xrootd
156  // framework. meant for exceptional situations where the site might need a specially-prepared set
157  // of cas only for tpc (such as trying out various workarounds for libnss). Explicitly disables
158  // the NSS hack below.
159  auto env_cadir = getenv("XRDTPC_CADIR");
160  if (env_cadir) m_cadir = env_cadir;
161 
162  // Sharing a single pre-parsed CA/CRL store between transfers relies on
163  // CURLOPT_SSL_CTX_FUNCTION, which only libcurl's OpenSSL, mbedTLS and wolfSSL
164  // backends implement; the others reject it with CURLE_NOT_BUILT_IN. libcurl
165  // checks this against the backend selected at run time rather than at build
166  // time, so probe an actual handle, and do it here so that an unsupported build
167  // is reported at startup instead of silently costing memory per transfer.
168  {
169  ManagedCurlHandle probe(curl_easy_init());
170  m_sslctx_supported = probe &&
171  curl_easy_setopt(probe.get(), CURLOPT_SSL_CTX_FUNCTION,
172  ssl_ctx_callback) == CURLE_OK;
173  if (!m_sslctx_supported) {
174  m_log.Emsg("Config", "libcurl does not support CURLOPT_SSL_CTX_FUNCTION; "
175  "each transfer will parse the CA and CRL bundles for itself, "
176  "which costs significant memory per concurrent transfer.");
177  }
178  }
179 
180  const char *cadir = nullptr, *cafile = nullptr;
181  if ((cadir = env_cadir ? env_cadir : myEnv->Get("http.cadir"))) {
182  m_cadir = cadir;
183  if (!env_cadir) {
184  // Only ask for the pre-parsed store when we can actually install it;
185  // maintaining one costs tens of MB that the fallback path never reads.
186  m_ca_file.reset(new XrdTlsTempCA(&m_log, m_cadir, m_sslctx_supported));
187  if (!m_ca_file->IsValid()) {
188  m_log.Emsg("Config", "CAs / CRL generation for libcurl failed.");
189  return false;
190  }
191  }
192  }
193  if ((cafile = myEnv->Get("http.cafile"))) {
194  m_cafile = cafile;
195  }
196 
197  if (!cadir && !cafile) {
198  // We do not necessary need TLS to perform HTTP TPC transfers, just log that these values were not specified
199  m_log.Emsg("Config", "neither xrd.tls cadir nor certfile value specified; is TLS enabled?");
200  }
201 
202  void *sfs_raw_ptr;
203  if ((sfs_raw_ptr = myEnv->GetPtr("XrdSfsFileSystem*"))) {
204  m_sfs = static_cast<XrdSfsFileSystem*>(sfs_raw_ptr);
205  m_log.Emsg("Config", "Using filesystem object from the framework.");
206  return true;
207  } else {
208  m_log.Emsg("Config", "No filesystem object available to HTTP-TPC subsystem. Internal error.");
209  return false;
210  }
211  return true;
212 }
213 
214 bool TPCHandler::ConfigureLogger(XrdOucStream &config_obj)
215 {
216  char *val = config_obj.GetWord();
217  if (!val || !val[0])
218  {
219  m_log.Emsg("Config", "tpc.trace requires at least one directive [all | error | warning | info | debug | none]");
220  return false;
221  }
222  // If the config option is given, reset the log mask.
223  m_log.setMsgMask(0);
224 
225  do {
226  if (!strcasecmp(val, "all"))
227  {
228  m_log.setMsgMask(m_log.getMsgMask() | LogMask::All);
229  }
230  else if (!strcasecmp(val, "error"))
231  {
232  m_log.setMsgMask(m_log.getMsgMask() | LogMask::Error);
233  }
234  else if (!strcasecmp(val, "warning"))
235  {
236  m_log.setMsgMask(m_log.getMsgMask() | LogMask::Warning);
237  }
238  else if (!strcasecmp(val, "info"))
239  {
240  m_log.setMsgMask(m_log.getMsgMask() | LogMask::Info);
241  }
242  else if (!strcasecmp(val, "debug"))
243  {
244  m_log.setMsgMask(m_log.getMsgMask() | LogMask::Debug);
245  }
246  else if (!strcasecmp(val, "none"))
247  {
248  m_log.setMsgMask(0);
249  }
250  else
251  {
252  m_log.Emsg("Config", "tpc.trace encountered an unknown directive (valid values: [all | error | warning | info | debug | none]):", val);
253  return false;
254  }
255  val = config_obj.GetWord();
256  } while (val);
257 
258  return true;
259 }
A pragmatic implementation of the HTTP/DAV protocol for the Xrd framework.
#define open
Definition: XrdPosix.hh:76
bool Debug
@ Error
static int parseHeader2CGI(XrdOucStream &Config, XrdSysError &err, std::map< std::string, std::string > &header2cgi)
Use this function to parse header2cgi configurations.
long GetInt(const char *varname)
Definition: XrdOucEnv.cc:253
void * GetPtr(const char *varname)
Definition: XrdOucEnv.cc:281
char * Get(const char *varname)
Definition: XrdOucEnv.hh:69
char * GetWord(int lowcase=0)
static std::map< std::string, T >::const_iterator caseInsensitiveFind(const std::map< std::string, T > &m, const std::string &lowerCaseSearchKey)
Definition: XrdOucTUtils.hh:79
static int a2sz(XrdSysError &, const char *emsg, const char *item, long long *val, long long minv=-1, long long maxv=-1)
Definition: XrdOuca2x.cc:257
static int a2tm(XrdSysError &, const char *emsg, const char *item, int *val, int minv=-1, int maxv=-1)
Definition: XrdOuca2x.cc:288
int Emsg(const char *esfx, int ecode, const char *text1, const char *text2=0)
Definition: XrdSysError.cc:95
void setMsgMask(int mask)
Definition: XrdSysError.hh:154
int getMsgMask()
Definition: XrdSysError.hh:156
std::unique_ptr< CURL, CurlDeleter > ManagedCurlHandle
@ Warning
XrdCmsConfig Config