XRootD
XrdCmsBlackList.cc
Go to the documentation of this file.
1 /******************************************************************************/
2 /* */
3 /* X r d C m s B l a c k L i s t . c c */
4 /* */
5 /* (c) 2014 by the Board of Trustees of the Leland Stanford, Jr., University */
6 /* All Rights Reserved */
7 /* Produced by Andrew Hanushevsky for Stanford University under contract */
8 /* DE-AC02-76-SFO0515 with the Department of Energy */
9 /* */
10 /* This file is part of the XRootD software suite. */
11 /* */
12 /* XRootD is free software: you can redistribute it and/or modify it under */
13 /* the terms of the GNU Lesser General Public License as published by the */
14 /* Free Software Foundation, either version 3 of the License, or (at your */
15 /* option) any later version. */
16 /* */
17 /* XRootD is distributed in the hope that it will be useful, but WITHOUT */
18 /* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or */
19 /* FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public */
20 /* License for more details. */
21 /* */
22 /* You should have received a copy of the GNU Lesser General Public License */
23 /* along with XRootD in a file called COPYING.LESSER (LGPL license) and file */
24 /* COPYING (GPL license). If not, see <http://www.gnu.org/licenses/>. */
25 /* */
26 /* The copyright holder's institutional names and contributor's names may not */
27 /* be used to endorse or promote products derived from this software without */
28 /* specific prior written permission of the institution or contributor. */
29 /******************************************************************************/
30 
31 #include <cstdio>
32 #include <sys/types.h>
33 #include <sys/stat.h>
34 #include <fcntl.h>
35 
36 #include "Xrd/XrdScheduler.hh"
37 
39 #include "XrdCms/XrdCmsCluster.hh"
40 #include "XrdCms/XrdCmsTrace.hh"
41 #include "XrdCms/XrdCmsUtils.hh"
42 
43 #include "XrdNet/XrdNetAddr.hh"
44 
45 #include "XrdOuc/XrdOucEnv.hh"
46 #include "XrdOuc/XrdOucTList.hh"
47 #include "XrdOuc/XrdOucStream.hh"
49 
50 #include "XrdSys/XrdSysError.hh"
51 #include "XrdSys/XrdSysLogger.hh"
52 #include "XrdSys/XrdSysPthread.hh"
53 
54 /******************************************************************************/
55 /* L o c a l C l a s s e s */
56 /******************************************************************************/
57 
58 class BL_Grip
59  {public:
60  void Add(XrdOucTList *tP)
61  {if (last) last->next = tP;
62  else first = tP;
63  last = tP;
64  }
65 
66  XrdOucTList **Array(int &anum)
67  {XrdOucTList *tP = first;
68  anum = Count();
69  if (!anum) return 0;
70  XrdOucTList **vec = new XrdOucTList *[anum];
71  for (int i = 0; i < anum; i++) {vec[i] = tP; tP = tP->next;}
72  first = last = 0;
73  return vec;
74  }
75 
76  int Count()
77  {XrdOucTList *tP = first;
78  int n = 0;
79  while(tP) {tP=tP->next; n++;}
80  return n;
81  }
82 
84  {XrdOucTList *tP = first;
85  first = last = 0;
86  return tP;
87  }
88 
89  bool Include(const char *item, int &i)
90  {XrdOucTList *tP = first;
91  i = 0;
92  while(tP && strcmp(item,tP->text)) {tP=tP->next; i++;}
93  if (!tP) {Add(new XrdOucTList(item)); return false;}
94  return true;
95  }
96 
97  BL_Grip() : first(0), last(0) {}
98 
100  while((tP = first)) {first = tP->next; delete tP;}
101  last = 0;
102  }
103 
104  private:
105  XrdOucTList *first;
106  XrdOucTList *last;
107  };
108 
109 union BL_Info
110  {long long info;
111  struct {short flags;
112  short pfxLen;
113  short sfxLen;
114  short totLen;
115  } v;
116  enum {exact = 0x8000,
117  redir = 0x4000,
118  rmask = 0x00ff
119  };
120  };
121 
122 /******************************************************************************/
123 /* G l o b a l O b j e c t s */
124 /******************************************************************************/
125 
126 namespace XrdCms
127 {
129 
131 
133  {public:
134  void Ring();
137  };
139 
141 
143 
146 
147  char *blFN;
148 
149  time_t blTime = 0;
150 
151  int blChk;
152  int blRcnt = 0;
153  bool isWList=false;
154 
155  extern XrdSysError Say;
156 };
157 
158 using namespace XrdCms;
159 
160 /******************************************************************************/
161 /* Private: A d d B L */
162 /******************************************************************************/
163 
164 bool XrdCmsBlackList::AddBL(BL_Grip &bAnchor, char *hSpec,
165  BL_Grip *rAnchor, char *rSpec)
166 {
167  const char *bwTag = (isWList ? "whitelist '" : "blacklist '");
168  XrdNetAddr blAddr;
169  XrdOucTList *bP;
170  BL_Info Hdr;
171  const char *eText;
172  char *Ast, blBuff[512];
173 
174 // Initialize the header
175 //
176  Hdr.info = 0;
177 
178 // Check if we are processing a redirect
179 //
180  if (rSpec)
181  {int i = AddRD(rAnchor, rSpec, hSpec);
182  if (i < 0) return false;
183  Hdr.v.flags |= BL_Info::redir | i;
184  }
185 
186 // Get the full name if this is not generic
187 //
188  if (!(Ast = index(hSpec, '*')))
189  {if ((eText = blAddr.Set(hSpec,0)))
190  {snprintf(blBuff, sizeof(blBuff), "'; %s", eText);
191  Say.Say("Config ", "Unable to ", bwTag, hSpec, blBuff);
192  return false;
193  }
194  blAddr.Format(blBuff, sizeof(blBuff), XrdNetAddrInfo::fmtName,
196  hSpec = blBuff;
197  Hdr.v.flags |= BL_Info::exact;
198  } else {
199  Hdr.v.pfxLen = Ast - hSpec;
200  Hdr.v.sfxLen = strlen(hSpec + Hdr.v.pfxLen + 1);
201  Hdr.v.totLen = Hdr.v.pfxLen + Hdr.v.sfxLen;
202  }
203 
204 // Add specification to the list
205 //
206  bP = new XrdOucTList(hSpec, &Hdr.info);
207  bAnchor.Add(bP);
208  return true;
209 }
210 
211 /******************************************************************************/
212 /* A d d R D */
213 /******************************************************************************/
214 
215 int XrdCmsBlackList::AddRD(BL_Grip *rAnchor, char *rSpec, char *hSpec)
216 {
217  XrdOucTList *rP, *rList = 0;
218  char *rTarg;
219  int ival;
220  bool aOK = true;
221 
222 // First see if we have this entry already
223 //
224  if (rAnchor[0].Include(rSpec, ival)) return ival;
225 
226 // Make sure we did not exceed the maximum number of redirect entries
227 //
228  if (ival > BL_Info::rmask)
229  {Say.Say("Config ", "Too many different redirects at ", hSpec,
230  "redirect", rSpec);
231  return -1;
232  }
233 
234 // We now ned to tokenize the specification
235 //
236  XrdOucTokenizer rToks(rSpec);
237  rToks.GetLine();
238 
239 // Process each item
240 //
241  while((rTarg = rToks.GetToken()) && *rTarg) aOK &= AddRD(&rList,rTarg,hSpec);
242  if (!aOK) return -1;
243 
244 // Flatten the list and add it to out list of redirect targets
245 //
246  rP = Flatten(rList, rList->val);
247  rAnchor[1].Add(rP);
248 
249 // Delete the rlist
250 //
251  while((rP = rList)) {rList = rList->next; delete rP;}
252 
253 // All done
254 //
255  return ival;
256 }
257 
258 /******************************************************************************/
259 
260 bool XrdCmsBlackList::AddRD(XrdOucTList **rList, char *rSpec, char *hSpec)
261 {
262  char *rPort;
263 
264 // Screen out IPV6 specifications
265 //
266  if (*rSpec == '[')
267  {if (!(rPort = index(rSpec, ']')))
268  {Say.Say("Config ","Invalid ",hSpec," redirect specification - ",rSpec);
269  return -1;
270  }
271  } else rPort = rSpec;
272 
273 // Grab the port number
274 //
275  if ((rPort = index(rPort, ':')))
276  {if (!(*(rPort+1))) rPort = 0;
277  else *rPort++ = '\0';
278  }
279 
280 // We should have a port specification now
281 //
282  if (!rPort) {Say.Say("Config ", "redirect port not specified for ", hSpec);
283  return -1;
284  }
285 
286 // Convert this to a list of redirect targets
287 //
288  return XrdCmsUtils::ParseMan(&Say, rList, rSpec, rPort, 0, true);
289 }
290 
291 /******************************************************************************/
292 /* D o I t */
293 /******************************************************************************/
294 
296 {
297  struct stat Stat;
298  XrdOucTList **blOldRedr = 0, **blNewRedr = 0, *blNewReal = 0, *tP = 0, *nP;
299  int rc, blOldRcnt = 0, blNewRcnt;
300  bool doUpdt = false, doPrt = false;
301 
302 // Check if the black list file was modified
303 //
304  rc = stat(blFN, &Stat);
305  if ((!rc && blTime != Stat.st_mtime) || (rc && blTime && errno == ENOENT))
306  {blTime = (rc ? 0 : Stat.st_mtime);
307  if (GetBL(blNewReal, blNewRedr, blNewRcnt))
308  {blMutex.Lock();
309  tP = blReal; blReal = blNewReal;
310  blOldRedr = blRedr; blRedr = blNewRedr;
311  blOldRcnt = blRcnt; blRcnt = blNewRcnt;
312  blMutex.UnLock();
313  if (!blReal && tP) doPrt = !isWList;
314  else blMN.Ring();
315  doUpdt = true;
316  }
317  }
318 
319 // Delete any list we need to release
320 //
321  while(tP)
322  {if (doPrt) Say.Say("Config ", tP->text, " removed from blacklist.");
323  nP = tP->next; delete tP; tP = nP;
324  }
325 
326 // Delete the old redirect array
327 //
328  if (blOldRedr)
329  {for (int i = 0; i < blOldRcnt; i++) delete blOldRedr[i];
330  delete [] blOldRedr;
331  }
332 
333 // Do real-time update if need be
334 //
335  if (doUpdt) blCluster->BlackList(blReal);
336 
337 // Reschedule this to check any modifications
338 //
339  blSched->Schedule((XrdJob *)&BlackList, time(0) + blChk);
340 }
341 
342 /******************************************************************************/
343 /* Private: F l a t t e n */
344 /******************************************************************************/
345 
346 XrdOucTList *XrdCmsBlackList::Flatten(XrdOucTList *tList, int tPort)
347 {
348  XrdOucTList *tP = tList;
349  char buff[4096], bPort[8], *bP = buff;
350  int n, pLen, bleft = sizeof(buff);
351  short xdata[4] = {0};
352 
353 // Convert port to a suffix
354 //
355  pLen = sprintf(bPort, ":%d", tPort);
356  *buff = 0;
357 
358 // Fill the buffer and truncate as necessary
359 //
360  while(tP)
361  {n = strlen(tP->text)+pLen+2;
362  if (n >= bleft) break;
363  n = sprintf(bP, " %s%s", tP->text, bPort);
364  bP += n; bleft -= n;
365  tP = tP->next;
366  }
367 
368 // Get actual length including null byte
369 //
370  xdata[0] = strlen(buff+1) + 1;
371  xdata[1] = xdata[0] + sizeof(short);
372  xdata[2] = htons(xdata[0]);
373 
374 // Create a new tlist item
375 //
376  tP = new XrdOucTList(buff+1, xdata);
377  return tP;
378 }
379 
380 /******************************************************************************/
381 /* Private: G e t B L */
382 /******************************************************************************/
383 
384 bool XrdCmsBlackList::GetBL(XrdOucTList *&bList,
385  XrdOucTList **&rList, int &rcnt, bool isInit)
386 {
387  static int msgCnt = 0;
388  XrdOucEnv myEnv;
389  XrdOucStream blFile(&Say, getenv("XRDINSTANCE"), &myEnv, "=====> ");
390  BL_Grip bAnchor, rAnchor[2];
391  const char *fType, *oEmsg, *rEmsg;
392  char *hsp, *rsp, hspBuff[512], rSpec[4096];
393  int blFD, retc;
394  bool aOK = true;
395 
396 // Setup message plugins
397 //
398  if (isWList)
399  {oEmsg = "open whitelist file";
400  rEmsg = "read whitelist file";
401  fType = "whitelist";
402  } else {
403  oEmsg = "open blacklist file";
404  rEmsg = "read blacklist file";
405  fType = "blacklist";
406  }
407 
408 // Open the config file
409 //
410  if ( (blFD = open(blFN, O_RDONLY, 0)) < 0)
411  {if (errno == ENOENT) return true;
412  if (!(msgCnt & 0x03)) Say.Emsg("GetBL", errno, oEmsg, blFN);
413  return false;
414  }
415  blFile.Attach(blFD, 4096);
416 
417 // Trace this now
418 //
419  Say.Say("Config processing ", fType, " file ", blFN);
420 
421 // Start reading the black list
422 //
423  while((hsp = blFile.GetMyFirstWord()))
424  {if (strlen(hsp) >= sizeof(hspBuff))
425  {Say.Say("Config ", hsp, " is too long."); aOK = false; continue;}
426  strcpy(hspBuff, hsp); hsp = hspBuff;
427  if ( (rsp = blFile.GetWord()) && *rsp)
428  {if (strcmp("redirect", rsp))
429  {Say.Say("Config ", rsp, " is an invalid modifier for ", hsp);
430  aOK = false;
431  continue;
432  }
433  *rSpec = 0; rsp = rSpec;
434  if (!blFile.GetRest(rSpec, sizeof(rSpec)))
435  {Say.Say("Config ", "redirect target too long ", hsp);
436  aOK = false;
437  continue;
438  }
439  if (!(*rSpec))
440  {Say.Say("Config ", "redirect target missing for ", hsp);
441  aOK = false;
442  continue;
443  }
444  } else rsp = 0;
445  blFile.noEcho();
446  if (!AddBL(bAnchor, hsp, rAnchor, rsp)) aOK = false;
447  }
448 
449 // Now check if any errors occurred during file i/o
450 //
451  if ((retc = blFile.LastError()))
452  {Say.Emsg("GetBL", retc, rEmsg, blFN); aOK = false;}
453  else if (!aOK) Say.Emsg("GetBL", "Error(s) encountered in",fType,"file!");
454 
455 // Return ending status
456 //
457  blFile.Close();
458  bList = ((aOK || isInit) ? bAnchor.Export() : nullptr);
459  rList = rAnchor[1].Array(rcnt);
460  return aOK;
461 }
462 
463 /******************************************************************************/
464 /* I n i t */
465 /******************************************************************************/
466 
468  const char *blfn, int chkt)
469 {
470  struct stat Stat;
471  const char *cfn;
472 
473 // Copy out the scheduler and cluster pointers
474 //
475  blSched = sP;
476  blCluster = cP;
477 
478 // Determine if this is a black or white list
479 //
480  if (chkt < 0) {isWList = true; chkt = -chkt;}
481 
482 // Copy the file path (this is a one time call during config)
483 //
484  if (blfn) blFN = strdup(blfn);
485  else if (!(cfn = getenv("XRDCONFIGFN"))) return;
486  else {char pBuff[2048], *Slash;
487  strcpy(pBuff, cfn);
488  if (!(Slash = rindex(pBuff, '/'))) return;
489  strcpy(Slash+1, (isWList ? "cms.whitelist" : "cms.blacklist"));
490  blFN = strdup(pBuff);
491  }
492 
493 // Check if the black list file exists, it might not. If it does, process it
494 //
495  if (!stat(blFN, &Stat))
496  {blTime = Stat.st_mtime;
497  GetBL(blReal, blRedr, blRcnt, /* isInit = */ true);
498  if (blReal) blMN.Ring();
499  }
500 
501 // Schedule this to recheck any modifications
502 //
503  blChk = chkt;
504  blSched->Schedule((XrdJob *)&BlackList, time(0) + chkt);
505 
506 // Add ourselves to the midnight run list
507 //
508  Say.logger()->AtMidnight(&blMN);
509 }
510 
511 /******************************************************************************/
512 /* P r e s e n t */
513 /******************************************************************************/
514 
515 int XrdCmsBlackList::Present(const char *hName, XrdOucTList *bList,
516  char *rBuff, int rBLen)
517 {
518  BL_Info Hdr;
519  int hLen, retval;
520  bool doUnLk;
521 
522 // Check if we really have a name here
523 //
524  if (!hName || !blSched) return 0;
525 
526 // Check if we need to supply our list
527 //
528  if (bList) doUnLk = false;
529  else {doUnLk = true;
530  blMutex.Lock();
531  bList = blReal;
532  }
533 
534 // By definition, if there is no list at all then everybody is allowed
535 //
536  if (!bList)
537  {if (doUnLk) blMutex.UnLock();
538  return 0;
539  }
540 
541 // Run through the list and try to compare
542 //
543  hLen = strlen(hName);
544  while(bList)
545  {Hdr.info = bList->dval;
546  if (Hdr.v.flags & BL_Info::exact)
547  {if (!strcmp(hName, bList->text)) break;}
548  else if (hLen >= Hdr.v.totLen)
549  {if (!Hdr.v.pfxLen
550  || !strncmp(bList->text, hName, Hdr.v.pfxLen))
551  {if (!Hdr.v.sfxLen
552  || !strncmp(bList->text+Hdr.v.pfxLen+1,
553  hName + (hLen - Hdr.v.sfxLen),
554  Hdr.v.sfxLen)) break;
555  }
556  }
557  bList = bList->next;
558  }
559 
560 // If we have a black list check if we should redirect
561 //
562  if (bList)
563  {if (!(Hdr.v.flags & BL_Info::redir)) retval = (isWList ? 0 : -1);
564  else {XrdOucTList *rP = blRedr[Hdr.v.flags & BL_Info::rmask];
565  if (rP)
566  {retval = rP->sval[1];
567  if (!rBuff || retval > rBLen) retval = -retval;
568  else {memcpy(rBuff, &(rP->sval[2]), sizeof(short));
569  memcpy(rBuff+sizeof(short), rP->text, rP->sval[0]);
570  }
571  } else retval = -1;
572  }
573  } else retval = (isWList ? -1 : 0);
574 
575 // Unlock ourselves if need be and return result
576 //
577  if (doUnLk) blMutex.UnLock();
578  return retval;
579 }
580 
581 /******************************************************************************/
582 /* R i n g */
583 /******************************************************************************/
584 
586 {
587  BL_Info Hdr;
588  XrdOucTList *tP;
589  const char *bwTag = (isWList ? "Whitelisting " : "Blacklisting ");
590 
591 // Get the list lock
592 //
593  blMutex.Lock();
594  tP = blReal;
595 
596 // Print the list
597 //
598  while(tP)
599  {Hdr.info = tP->dval;
600  if (!(Hdr.v.flags & BL_Info::redir))
601  Say.Say("Config ", bwTag, tP->text);
602  else {XrdOucTList *rP = blRedr[Hdr.v.flags & BL_Info::rmask];
603  Say.Say("Config Blacklisting ",tP->text," redirect ",rP->text);
604  }
605  tP = tP->next;
606  }
607 
608 // All done
609 //
610  blMutex.UnLock();
611 }
struct stat Stat
Definition: XrdCks.cc:49
int stat(const char *path, struct stat *buf)
int open(const char *path, int oflag,...)
XrdOucTList * Export()
void Add(XrdOucTList *tP)
XrdOucTList ** Array(int &anum)
bool Include(const char *item, int &i)
void DoIt()
Time driven method for checking black list file.
static int Present(const char *hName, XrdOucTList *bList=0, char *rbuff=0, int rblen=0)
static void Init(XrdScheduler *sP, XrdCmsCluster *cP, const char *blfn, int chkt=600)
virtual void BlackList(XrdOucTList *blP)
static bool ParseMan(XrdSysError *eDest, XrdOucTList **oldMans, char *hSpec, char *hPort, int *sPort=0, bool hush=false)
Definition: XrdCmsUtils.cc:123
void Ring()
This method gets called at midnight.
Definition: XrdJob.hh:43
static const int noPort
Do not add port number.
int Format(char *bAddr, int bLen, fmtUse fmtType=fmtAuto, int fmtOpts=0)
@ fmtName
Hostname if it is resolvable o/w use fmtAddr.
const char * Set(const char *hSpec, int pNum=PortInSpec)
Definition: XrdNetAddr.cc:216
XrdOucTList * next
Definition: XrdOucTList.hh:45
char * text
Definition: XrdOucTList.hh:46
void Schedule(XrdJob *jp)
int Emsg(const char *esfx, int ecode, const char *text1, const char *text2=0)
Definition: XrdSysError.cc:95
void Say(const char *text1, const char *text2=0, const char *txt3=0, const char *text4=0, const char *text5=0, const char *txt6=0)
Definition: XrdSysError.cc:141
XrdSysLogger * logger(XrdSysLogger *lp=0)
Definition: XrdSysError.hh:141
void AtMidnight(Task *mnTask)
XrdCmsBlackList BlackList
char * blFN
XrdSysError Say
XrdCmsCluster * blCluster
XrdOucTList * blReal
XrdOucTList ** blRedr
time_t blTime
XrdSysMutex blMutex
MidNightTask blMN
XrdScheduler * blSched
struct BL_Info::@78 v
long long info