XRootD
Loading...
Searching...
No Matches
XrdOucUtils.cc
Go to the documentation of this file.
1/******************************************************************************/
2/* */
3/* X r d O u c U t i l s . c c */
4/* */
5/* (c) 2005 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 <cctype>
32#include <grp.h>
33#include <cstdio>
34#include <list>
35#include <vector>
36#include <unordered_set>
37#include <algorithm>
38#include <charconv>
39
40#include <regex.h>
41
42#ifdef WIN32
43#include <direct.h>
44#include "XrdSys/XrdWin32.hh"
45#else
46#include <fcntl.h>
47#include <math.h>
48#include <pwd.h>
49#include <sys/stat.h>
50#include <sys/types.h>
51#endif
52#include <map>
53#include <iomanip>
54#include "XrdNet/XrdNetUtils.hh"
55#include "XrdOuc/XrdOucCRC.hh"
56#include "XrdOuc/XrdOucEnv.hh"
57#include "XrdOuc/XrdOucSHA3.hh"
60#include "XrdOuc/XrdOucUtils.hh"
62#include "XrdSys/XrdSysE2T.hh"
63#include "XrdSys/XrdSysError.hh"
66
67#ifndef ENODATA
68#define ENODATA ENOATTR
69#endif
70
71/******************************************************************************/
72/* L o c a l M e t h o d s */
73/******************************************************************************/
74
75namespace
76{
77struct idInfo
78{ time_t Expr;
79 char *Name;
80
81 idInfo(const char *name, time_t keep)
82 : Expr(time(0)+keep), Name(strdup(name)) {}
83 ~idInfo() {free(Name);}
84};
85
86typedef std::map<unsigned int, struct idInfo*> idMap_t;
87
88idMap_t gidMap;
89idMap_t uidMap;
90XrdSysMutex idMutex;
91
92void AddID(idMap_t &idMap, unsigned int id, const char *name, time_t keepT)
93{
94 std::pair<idMap_t::iterator,bool> ret;
95 idInfo *infoP = new idInfo(name, keepT);
96
97 idMutex.Lock();
98 ret = idMap.insert(std::pair<unsigned int, struct idInfo*>(id, infoP));
99 if (ret.second == false) delete infoP;
100 idMutex.UnLock();
101}
102
103int LookUp(idMap_t &idMap, unsigned int id, char *buff, int blen)
104{
105 idMap_t::iterator it;
106 int luRet = 0;
107
108 idMutex.Lock();
109 it = idMap.find(id);
110 if (it != idMap.end())
111 {if (it->second->Expr <= time(0))
112 {delete it->second;
113 idMap.erase(it);
114 } else {
115 if (blen > 0) luRet = snprintf(buff, blen, "%s", it->second->Name);
116 }
117 }
118 idMutex.UnLock();
119 return luRet;
120}
121}
122
123/******************************************************************************/
124/* a r g L i s t */
125/******************************************************************************/
126
127int XrdOucUtils::argList(char *args, char **argV, int argC)
128{
129 char *aP = args;
130 int j;
131
132// Construct the argv array based on passed command line.
133//
134for (j = 0; j < argC; j++)
135 {while(*aP == ' ') aP++;
136 if (!(*aP)) break;
137
138 if (*aP == '"' || *aP == '\'')
139 {argV[j] = aP+1;
140 aP = index(aP+1, *aP);
141 if (!aP || (*(aP+1) != ' ' && *(aP+1)))
142 {if (!j) argV[0] = 0; return -EINVAL;}
143 *aP++ = '\0';
144 } else {
145 argV[j] = aP;
146 if ((aP = index(aP+1, ' '))) *aP++ = '\0';
147 else {j++; break;}
148 }
149
150 }
151
152// Make sure we did not overflow the vector
153//
154 if (j > argC-1) return -E2BIG;
155
156// End list with a null pointer and return the actual number of arguments
157//
158 argV[j] = 0;
159 return j;
160}
161
162/******************************************************************************/
163/* b i n 2 h e x */
164/******************************************************************************/
165
166char *XrdOucUtils::bin2hex(char *inbuff, int dlen, char *buff, int blen,
167 bool sep)
168{
169 static const char hv[] = "0123456789abcdef";
170 char *outbuff = buff;
171 for (int i = 0; i < dlen && blen > 2; i++) {
172 *outbuff++ = hv[(inbuff[i] >> 4) & 0x0f];
173 *outbuff++ = hv[ inbuff[i] & 0x0f];
174 blen -= 2;
175 if (sep && blen > 1 && ((i & 0x03) == 0x03 || i+1 == dlen))
176 {*outbuff++ = ' '; blen--;}
177 }
178 *outbuff = '\0';
179 return buff;
180}
181
182/******************************************************************************/
183/* h e x 2 b i n */
184/******************************************************************************/
185
186int XrdOucUtils::hex2bin(const char *hex, char *bin, int size)
187{
188 int len = 0, lo = -1, hi = -1;
189
190 while (int c = *hex++) {
191 if (c == ' ' || c == '\n')
192 break;
193
194 switch (c) {
195 case '0' ... '9':
196 c -= '0';
197 break;
198 case 'a' ... 'f':
199 c -= 'a' - 10;
200 break;
201 case 'A' ... 'F':
202 c -= 'A' - 10;
203 break;
204 default:
205 return -EINVAL;
206 }
207
208 if (hi == -1)
209 hi = c;
210 else
211 lo = c;
212
213 if (lo == -1)
214 continue;
215
216 if (len >= size)
217 return -EOVERFLOW;
218
219 bin[len++] = (hi << 4) | lo;
220 hi = lo = -1;
221 }
222 if (hi >= 0 || lo >= 0 || len == 0)
223 return -EINVAL;
224 return len;
225}
226
227/******************************************************************************/
228/* e n d s W i t h */
229/******************************************************************************/
230
231bool XrdOucUtils::endsWith(const char *text, const char *ending, int endlen)
232{
233 int tlen = strlen(text);
234
235 return (tlen >= endlen && !strcmp(text+(tlen-endlen), ending));
236}
237
238/******************************************************************************/
239/* e T e x t */
240/******************************************************************************/
241
242// eText() returns the text associated with the error.
243// The text buffer pointer is returned.
244
245char *XrdOucUtils::eText(int rc, char *eBuff, int eBlen)
246{
247 const char *etP;
248
249// Get error text
250//
251 etP = XrdSysE2T(rc);
252
253// Copy the text and lower case the first letter
254//
255 strlcpy(eBuff, etP, eBlen);
256
257// All done
258//
259 return eBuff;
260}
261
262/******************************************************************************/
263/* d o I f */
264/******************************************************************************/
265
266// doIf() parses "if [<hostlist>] [<conds>]"
267// conds: <cond1> | <cond2> | <cond3>
268// cond1: defined <varlist> [&& <conds>]
269// cond2: exec <pgmlist> [&& <cond3>]
270// cond3: named <namelist>
271
272// Returning 1 if true (i.e., this machine is one of the named hosts in hostlist
273// and is running one of the programs pgmlist and named by one of the names in
274// namelist).
275// Return -1 (negative truth) if an error occurred.
276// Otherwise, returns false (0). Some combination of hostlist, pgm, and
277// namelist, must be specified.
278
280 const char *what, const char *hname,
281 const char *nname, const char *pname)
282{
283 static const char *brk[] = {"defined", "exec", "named", 0};
284 XrdOucEnv *theEnv = 0;
285 char *val;
286 int hostok, isDef;
287
288// Make sure that at least one thing appears after the if
289//
290 if (!(val = Config.GetWord()))
291 {if (eDest) eDest->Emsg("Config","Host name missing after 'if' in", what);
292 return -1;
293 }
294
295// Check if we are one of the listed hosts
296//
297 if (!is1of(val, brk))
298 {do {hostok = XrdNetUtils::Match(hname, val);
299 val = Config.GetWord();
300 } while(!hostok && val && !is1of(val, brk));
301 if (hostok)
302 { while(val && !is1of(val, brk)) val = Config.GetWord();
303 // No more directives
304 if (!val) return 1;
305 } else return 0;
306 }
307
308// Check if this is a defined test
309//
310 while(!strcmp(val, "defined"))
311 {if (!(val = Config.GetWord()) || *val != '?')
312 {if (eDest)
313 {eDest->Emsg("Config","'?var' missing after 'defined' in",what);}
314 return -1;
315 }
316 // Get environment if we have none
317 //
318 if (!theEnv && (theEnv = Config.SetEnv(0))) Config.SetEnv(theEnv);
319 if (!theEnv && *(val+1) != '~') return 0;
320
321 // Check if any listed variable is defined.
322 //
323 isDef = 0;
324 while(val && *val == '?')
325 {if (*(val+1) == '~' ? getenv(val+2) : theEnv->Get(val+1)) isDef=1;
326 val = Config.GetWord();
327 }
328 if (!val || !isDef) return isDef;
329 if (strcmp(val, "&&"))
330 {if (eDest)
331 {eDest->Emsg("Config",val,"is invalid for defined test in",what);}
332 return -1;
333 } else {
334 if (!(val = Config.GetWord()))
335 {if (eDest)
336 {eDest->Emsg("Config","missing keyword after '&&' in",what);}
337 return -1;
338 }
339 }
340 if (!is1of(val, brk))
341 {if (eDest)
342 {eDest->Emsg("Config",val,"is invalid after '&&' in",what);}
343 return -1;
344 }
345 }
346
347// Check if we need to compare program names (we are here only if we either
348// passed the hostlist test or there was no hostlist present)
349//
350 if (!strcmp(val, "exec"))
351 {if (!(val = Config.GetWord()) || !strcmp(val, "&&"))
352 {if (eDest)
353 {eDest->Emsg("Config","Program name missing after 'if exec' in",what);}
354 return -1;
355 }
356
357 // Check if we are one of the programs.
358 //
359 if (!pname) return 0;
360 while(val && strcmp(val, pname))
361 if (!strcmp(val, "&&")) return 0;
362 else val = Config.GetWord();
363 if (!val) return 0;
364 while(val && strcmp(val, "&&")) val = Config.GetWord();
365 if (!val) return 1;
366
367 if (!(val = Config.GetWord()))
368 {if (eDest)
369 {eDest->Emsg("Config","Keyword missing after '&&' in",what);}
370 return -1;
371 }
372 if (strcmp(val, "named"))
373 {if (eDest)
374 {eDest->Emsg("Config",val,"is invalid after '&&' in",what);}
375 return -1;
376 }
377 }
378
379// Check if we need to compare net names (we are here only if we either
380// passed the hostlist test or there was no hostlist present)
381//
382 if (!(val = Config.GetWord()))
383 {if (eDest)
384 {eDest->Emsg("Config","Instance name missing after 'if named' in", what);}
385 return -1;
386 }
387
388// Check if we are one of the names
389//
390 if (!nname) return 0;
391 while(val && strcmp(val, nname)) val = Config.GetWord();
392
393// All done
394//
395 return (val != 0);
396}
397
398/******************************************************************************/
399/* f i n d P g m */
400/******************************************************************************/
401
402bool XrdOucUtils::findPgm(const char *pgm, XrdOucString& path)
403{
404 struct stat Stat;
405
406// Check if only executable bit needs to be checked
407//
408 if (*pgm == '/')
409 {if (stat(pgm, &Stat) || !(Stat.st_mode & S_IXOTH)) return false;
410 path = pgm;
411 return true;
412 }
413
414// Make sure we have the paths to check
415//
416 const char *pEnv = getenv("PATH");
417 if (!pEnv) return false;
418
419// Setup to find th executable
420//
421 XrdOucString prog, pList(pEnv);
422 int from = 0;;
423 prog += '/'; prog += pgm;
424
425// Find it!
426//
427 while((from = pList.tokenize(path, from, ':')) != -1)
428 {path += prog;
429 if (!stat(path.c_str(), &Stat) && Stat.st_mode & S_IXOTH) return true;
430 }
431 return false;
432}
433
434/******************************************************************************/
435/* f m t B y t e s */
436/******************************************************************************/
437
438int XrdOucUtils::fmtBytes(long long val, char *buff, int bsz)
439{
440 static const long long Kval = 1024LL;
441 static const long long Mval = 1024LL*1024LL;
442 static const long long Gval = 1024LL*1024LL*1024LL;
443 static const long long Tval = 1024LL*1024LL*1024LL*1024LL;
444 char sName = ' ';
445 int resid;
446
447// Get correct scaling
448//
449 if (val < 1024) return snprintf(buff, bsz, "%lld", val);
450 if (val < Mval) {val = val*10/Kval; sName = 'K';}
451 else if (val < Gval) {val = val*10/Mval; sName = 'M';}
452 else if (val < Tval) {val = val*10/Gval; sName = 'G';}
453 else {val = val*10/Tval; sName = 'T';}
454 resid = val%10LL; val = val/10LL;
455
456// Format it
457//
458 return snprintf(buff, bsz, "%lld.%d%c", val, resid, sName);
459}
460
461std::string XrdOucUtils::genHumanSize(size_t size, uint64_t base) {
462 static const char* units[] = {"", "K", "M", "G", "T", "P", "E"};
463 const int maxUnit = 6;
464
465 double value = static_cast<double>(size);
466 int unitIndex = 0;
467
468 while (value >= static_cast<double>(base) && unitIndex < maxUnit) {
469 value /= static_cast<double>(base);
470 ++unitIndex;
471 }
472
473 auto ceil_to = [](double x, int decimals) {
474 if (x == 0.0) return 0.0; // avoid creating a -0 in the output
475 const double p = std::pow(10.0, decimals);
476 // small epsilon to avoid 1.0 becoming 1.1 due to floating noise
477 return std::ceil(x * p - 1e-12) / p;
478 };
479
480 auto decimals_for = [](double x, int u) {
481 return (u > 0 && x < 10.0) ? 1 : 0; // e.g: 1.0G for exact powers
482 };
483
484 int prec = decimals_for(value, unitIndex);
485 double shown = ceil_to(value, prec);
486
487 // If rounding pushed us to the next unit (e.g. 1024.0K), promote
488 if (shown >= static_cast<double>(base) && unitIndex < maxUnit) {
489 shown /= static_cast<double>(base);
490 ++unitIndex;
491
492 prec = decimals_for(shown, unitIndex);
493 shown = ceil_to(shown, prec);
494 }
495
496 std::ostringstream out;
497 out << std::fixed << std::setprecision(prec) << shown << units[unitIndex];
498 return out.str();
499}
500
501/******************************************************************************/
502/* g e n P a t h */
503/******************************************************************************/
504
505char *XrdOucUtils::genPath(const char *p_path, const char *inst,
506 const char *s_path)
507{
508 char buff[2048];
509 int i = strlcpy(buff, p_path, sizeof(buff));
510
511 if (buff[i-1] != '/') {buff[i++] = '/'; buff[i] = '\0';}
512 if (inst) {strcpy(buff+i, inst); strcat(buff, "/");}
513 if (s_path) strcat(buff, s_path);
514
515 i = strlen(buff);
516 if (buff[i-1] != '/') {buff[i++] = '/'; buff[i] = '\0';}
517
518 return strdup(buff);
519}
520
521/******************************************************************************/
522
523int XrdOucUtils::genPath(char *buff, int blen, const char *path, const char *psfx)
524{
525 int i, j;
526
527 i = strlen(path);
528 j = (psfx ? strlen(psfx) : 0);
529 if (i+j+3 > blen) return -ENAMETOOLONG;
530
531 strcpy(buff, path);
532 if (psfx)
533 {if (buff[i-1] != '/') buff[i++] = '/';
534 strcpy(&buff[i], psfx);
535 if (psfx[j-1] != '/') strcat(buff, "/");
536 }
537 return 0;
538}
539
540/******************************************************************************/
541/* g e t F i l e */
542/******************************************************************************/
543
544char *XrdOucUtils::getFile(const char *path, int &rc, int maxsz, bool notempty)
545{
546 struct stat Stat;
547 struct fdHelper
548 {int fd = -1;
549 fdHelper() {}
550 ~fdHelper() {if (fd >= 0) close(fd);}
551 } file;
552 char *buff;
553 int flen;
554
555// Preset RC
556//
557 rc = 0;
558
559// Open the file in read mode
560//
561 if ((file.fd = open(path, O_RDONLY)) < 0) {rc = errno; return 0;}
562
563// Get the size of the file
564//
565 if (fstat(file.fd, &Stat)) {rc = errno; return 0;}
566
567// Check if the size exceeds the maximum allowed
568//
569 if (Stat.st_size > maxsz) {rc = EFBIG; return 0;}
570
571// Make sure the file is not empty if empty files are disallowed
572//
573 if (Stat.st_size == 0 && notempty) {rc = ENODATA; return 0;}
574
575// Allocate a buffer
576//
577 if ((buff = (char *)malloc(Stat.st_size+1)) == 0)
578 {rc = errno; return 0;}
579
580// Read the contents of the file into the buffer
581//
582 if (Stat.st_size)
583 {if ((flen = read(file.fd, buff, Stat.st_size)) < 0)
584 {rc = errno; free(buff); return 0;}
585 } else flen = 0;
586
587// Add null byte. recall the buffer is bigger by one byte
588//
589 buff[flen] = 0;
590
591// Return the size aand the buffer
592//
593 rc = flen;
594 return buff;
595}
596
597/******************************************************************************/
598/* g e t G I D */
599/******************************************************************************/
600
601bool XrdOucUtils::getGID(const char *gName, gid_t &gID)
602{
603 struct group Grp, *result;
604 char buff[65536];
605
606 getgrnam_r(gName, &Grp, buff, sizeof(buff), &result);
607 if (!result) return false;
608
609 gID = Grp.gr_gid;
610 return true;
611}
612
613/******************************************************************************/
614/* g e t U I D */
615/******************************************************************************/
616
617bool XrdOucUtils::getUID(const char *uName, uid_t &uID, gid_t *gID)
618{
619 struct passwd pwd, *result;
620 char buff[16384];
621
622 getpwnam_r(uName, &pwd, buff, sizeof(buff), &result);
623 if (!result) return false;
624
625 uID = pwd.pw_uid;
626 if (gID) *gID = pwd.pw_gid;
627
628 return true;
629}
630
631/******************************************************************************/
632/* G i d N a m e */
633/******************************************************************************/
634
635int XrdOucUtils::GidName(gid_t gID, char *gName, int gNsz, time_t keepT)
636{
637 static const int maxgBsz = 256*1024;
638 static const int addGsz = 4096;
639 struct group *gEnt, gStruct;
640 char gBuff[1024], *gBp = gBuff;
641 int glen = 0, gBsz = sizeof(gBuff), aOK = 1;
642 int n, retVal = 0;
643
644// Get ID from cache, if allowed
645//
646 if (keepT)
647 {int n = LookUp(gidMap, static_cast<unsigned int>(gID),gName,gNsz);
648 if (n > 0) return (n < gNsz ? n : 0);
649 }
650
651// Get the the group struct. If we don't have a large enough buffer, get a
652// larger one and try again up to the maximum buffer we will tolerate.
653//
654 while(( retVal = getgrgid_r(gID, &gStruct, gBp, gBsz, &gEnt) ) == ERANGE)
655 {if (gBsz >= maxgBsz) {aOK = 0; break;}
656 if (gBsz > addGsz) free(gBp);
657 gBsz += addGsz;
658 if (!(gBp = (char *)malloc(gBsz))) {aOK = 0; break;}
659 }
660
661// Return a group name if all went well
662//
663 if (aOK && retVal == 0 && gEnt != NULL)
664 {if (keepT)
665 AddID(gidMap, static_cast<unsigned int>(gID), gEnt->gr_name, keepT);
666 glen = strlen(gEnt->gr_name);
667 if (glen >= gNsz) glen = 0;
668 else strcpy(gName, gEnt->gr_name);
669 } else {
670 n = snprintf(gName, gNsz, "%ud", static_cast<unsigned int>(gID));
671 if (n >= gNsz) glen = 0;
672 }
673
674// Free any allocated buffer and return result
675//
676 if (gBsz > addGsz && gBp) free(gBp);
677 return glen;
678}
679
680/******************************************************************************/
681/* G r o u p N a m e */
682/******************************************************************************/
683
684int XrdOucUtils::GroupName(gid_t gID, char *gName, int gNsz)
685{
686 static const int maxgBsz = 256*1024;
687 static const int addGsz = 4096;
688 struct group *gEnt, gStruct;
689 char gBuff[1024], *gBp = gBuff;
690 int glen, gBsz = sizeof(gBuff), aOK = 1;
691 int retVal = 0;
692
693// Get the the group struct. If we don't have a large enough buffer, get a
694// larger one and try again up to the maximum buffer we will tolerate.
695//
696 while(( retVal = getgrgid_r(gID, &gStruct, gBp, gBsz, &gEnt) ) == ERANGE)
697 {if (gBsz >= maxgBsz) {aOK = 0; break;}
698 if (gBsz > addGsz) free(gBp);
699 gBsz += addGsz;
700 if (!(gBp = (char *)malloc(gBsz))) {aOK = 0; break;}
701 }
702
703// Return a group name if all went well
704//
705 if (aOK && retVal == 0 && gEnt != NULL)
706 {glen = strlen(gEnt->gr_name);
707 if (glen >= gNsz) glen = 0;
708 else strcpy(gName, gEnt->gr_name);
709 } else glen = 0;
710
711// Free any allocated buffer and return result
712//
713 if (gBsz > addGsz && gBp) free(gBp);
714 return glen;
715}
716
717/******************************************************************************/
718/* H S i z e */
719/******************************************************************************/
720
721const char *XrdOucUtils::HSize(size_t bytes, char* buff, int bsz)
722{
723
724// Do fast conversion of the quantity is less than 1K
725//
726 if (bytes < 1024)
727 {snprintf(buff, bsz, "%zu", bytes);
728 return buff;
729 }
730
731// Scale this down
732//
733 const char *suffix = " KMGTPEYZ";
734 double dBytes = static_cast<double>(bytes);
735
736do{dBytes /= 1024.0; suffix++;
737 } while(dBytes >= 1024.0 && *(suffix+1));
738
739
740// Format and return result. Include fractions only if they meaningfully exist.
741//
742 double whole, frac = modf(dBytes, &whole);
743 if (frac >= .005) snprintf(buff, bsz, "%.02lf%c", dBytes, *suffix);
744 else snprintf(buff, bsz, "%g%c", whole, *suffix);
745 return buff;
746}
747
748/******************************************************************************/
749/* i 2 b s t r */
750/******************************************************************************/
751
752const char *XrdOucUtils::i2bstr(char *buff, int blen, int val, bool pad)
753{
754 char zo[2] = {'0', '1'};
755
756 if (blen < 2) return "";
757
758 buff[--blen] = 0;
759 if (!val) buff[blen--] = '0';
760 else while(val && blen >= 0)
761 {buff[blen--] = zo[val & 0x01];
762 val >>= 1;
763 }
764
765 if (blen >= 0 && pad) while(blen >= 0) buff[blen--] = '0';
766
767 return &buff[blen+1];
768}
769
770/******************************************************************************/
771/* I d e n t */
772/******************************************************************************/
773
774namespace
775{
776long long genSID(char *&urSID, const char *iHost, int iPort,
777 const char *iName, const char *iProg)
778{
779 static const XrdOucSHA3::MDLen mdLen = XrdOucSHA3::SHA3_512;
780 static const uint32_t fpOffs = 2, fpSize = 6; // 48 bit finger print
781
782 const char *iSite = getenv("XRDSITE");
783 unsigned char mDigest[mdLen];
784 XrdOucString myID;
785 union {uint64_t mdLL; unsigned char mdUC[8];}; // Works for fpSize only!
786
787// Construct our unique identification
788//
789 if (iSite) myID = iSite;
790 myID += iHost;
791 myID += iPort;
792 if (iName) myID += iName;
793 myID += iProg;
794
795// Generate a SHA3 digest of this string.
796//
797 memset(mDigest, 0, sizeof(mDigest));
798 XrdOucSHA3::Calc(myID.c_str(), myID.length(), mDigest, mdLen);
799
800// Generate a CRC32C of the same string
801//
802 uint32_t crc32c = XrdOucCRC::Calc32C(myID.c_str(), myID.length());
803
804// We need a 48-bit fingerprint that has a very low probability of collision.
805// We accomplish this by convoluting the CRC32C checksum with the SHA3 checksum.
806//
807 uint64_t fpPos = crc32c % (((uint32_t)mdLen) - fpSize);
808 mdLL = 0;
809 memcpy(mdUC+fpOffs, mDigest+fpPos, fpSize);
810 long long fpVal = static_cast<long long>(ntohll(mdLL));
811
812// Generate the character version of our fingerprint and return the binary one.
813//
814 char fpBuff[64];
815 snprintf(fpBuff, sizeof(fpBuff), "%lld", fpVal);
816 urSID = strdup(fpBuff);
817 return fpVal;
818}
819}
820
821char *XrdOucUtils::Ident(long long &mySID, char *iBuff, int iBlen,
822 const char *iHost, const char *iProg,
823 const char *iName, int iPort)
824{
825 static char *theSIN;
826 static long long theSID = genSID(theSIN, iHost, iPort, iName, iProg);
827 const char *sP = getenv("XRDSITE");
828 char uName[256];
829 int myPid = static_cast<int>(getpid());
830
831// Get our username
832//
833 if (UserName(getuid(), uName, sizeof(uName)))
834 sprintf(uName, "%d", static_cast<int>(getuid()));
835
836// Create identification record
837//
838 snprintf(iBuff,iBlen,"%s.%d:%s@%s\n&site=%s&port=%d&inst=%s&pgm=%s",
839 uName, myPid, theSIN, iHost, (sP ? sP : ""), iPort, iName, iProg);
840
841// Return a copy of the sid key
842//
843 h2nll(theSID, mySID);
844 return strdup(theSIN);
845}
846
847/******************************************************************************/
848/* I n s t N a m e */
849/******************************************************************************/
850
851const char *XrdOucUtils::InstName(int TranOpt)
852{
853 const char *iName = getenv("XRDNAME");
854
855// If tran is zero, return what we have
856//
857 if (!TranOpt) return iName;
858
859// If trans is positive then make sure iName has a value. Otherwise, make sure
860// iName has no value if it's actually "anon".
861//
862 if (TranOpt > 0) {if (!iName || !*iName) iName = "anon";}
863 else if (iName && !strcmp(iName, "anon")) iName = 0;
864 return iName;
865}
866/******************************************************************************/
867
868const char *XrdOucUtils::InstName(const char *name, int Fillit)
869{ return (Fillit ? name && *name ? name : "anon"
870 : name && strcmp(name,"anon") && *name ? name : 0);
871}
872
873/******************************************************************************/
874/* i s 1 o f */
875/******************************************************************************/
876
877int XrdOucUtils::is1of(char *val, const char **clist)
878{
879 int i = 0;
880 while(clist[i]) if (!strcmp(val, clist[i])) return 1;
881 else i++;
882 return 0;
883}
884
885/******************************************************************************/
886/* i s F W D */
887/******************************************************************************/
888
889int XrdOucUtils::isFWD(const char *path, int *port, char *hBuff, int hBLen,
890 bool pTrim)
891{
892 const char *hName, *hNend, *hPort, *hPend, *hP = path;
893 char *eP;
894 int n;
895
896 if (*path == '/') hP++; // Note: It's assumed an objectid if no slash
897 if (*hP == 'x') hP++;
898 if (strncmp("root:/", hP, 6)) return 0;
899 if (hBuff == 0 || hBLen <= 0) return (hP - path) + 6;
900 hP += 6;
901
902 if (!XrdNetUtils::Parse(hP, &hName, &hNend, &hPort, &hPend)) return 0;
903 if (*hNend == ']') hNend++;
904 else {if (!(*hNend) && !(hNend = index(hName, '/'))) return 0;
905 if (!(*hPend)) hPend = hNend;
906 }
907
908 if (pTrim || !(*hPort)) n = hNend - hP;
909 else n = hPend - hP;
910 if (n >= hBLen) return 0;
911 strncpy(hBuff, hP, n);
912 hBuff[n] = 0;
913
914 if (port)
915 {if (*hNend != ':') *port = 0;
916 else {*port = strtol(hPort, &eP, 10);
917 if (*port < 0 || *port > 65535 || eP != hPend) return 0;
918 }
919 }
920
921 return hPend-path;
922}
923
924/******************************************************************************/
925/* L o g 2 */
926/******************************************************************************/
927
928// Based on an algorithm produced by Todd Lehman. However, this one returns 0
929// when passed 0 (which is invalid). The caller must check the validity of
930// the input prior to calling Log2(). Essentially, the algorithm subtracts
931// away progressively smaller squares in the sequence
932// { 0 <= k <= 5: 2^(2^k) } = { 2**32, 2**16, 2**8 2**4 2**2, 2**1 } =
933// = { 4294967296, 65536, 256, 16, 4, 2 }
934// and sums the exponents k of the subtracted values. It is generally the
935// fastest way to compute log2 for a wide range of possible input values.
936
937int XrdOucUtils::Log2(unsigned long long n)
938{
939 int i = 0;
940
941 #define SHFT(k) if (n >= (1ULL << k)) { i += k; n >>= k; }
942
943 SHFT(32); SHFT(16); SHFT(8); SHFT(4); SHFT(2); SHFT(1); return i;
944
945 #undef SHFT
946}
947
948/******************************************************************************/
949/* L o g 1 0 */
950/******************************************************************************/
951
952int XrdOucUtils::Log10(unsigned long long n)
953{
954 int i = 0;
955
956 #define SHFT(k, m) if (n >= m) { i += k; n /= m; }
957
958 SHFT(16,10000000000000000ULL); SHFT(8,100000000ULL);
959 SHFT(4,10000ULL); SHFT(2,100ULL); SHFT(1,10ULL);
960 return i;
961
962 #undef SHFT
963}
964
965/******************************************************************************/
966/* m a k e H o m e */
967/******************************************************************************/
968
970{
971 char buff[2048];
972
973 if (!inst || !getcwd(buff, sizeof(buff))) return;
974
975 strcat(buff, "/"); strcat(buff, inst);
976 if (MAKEDIR(buff, pathMode) && errno != EEXIST)
977 {eDest.Emsg("Config", errno, "create home directory", buff);
978 return;
979 }
980
981 if (chdir(buff) < 0)
982 eDest.Emsg("Config", errno, "chdir to home directory", buff);
983}
984
985/******************************************************************************/
986
988 const char *path, mode_t mode)
989{
990 char cwDir[2048];
991 const char *slash = "", *slash2 = "";
992 int n, rc;
993
994// Provide backward compatibility for instance name qualification
995//
996
997 if (!path || !(n = strlen(path)))
998 {if (inst) makeHome(eDest, inst);
999 return true;
1000 }
1001
1002// Augment the path with instance name, if need be
1003//
1004 if (path[n-1] != '/') slash = "/";
1005 if (!inst || !(n = strlen(inst))) inst = "";
1006 else slash2 = "/";
1007 n = snprintf(cwDir, sizeof(cwDir), "%s%s%s%s", path, slash, inst, slash2);
1008 if (n >= (int)sizeof(cwDir))
1009 {eDest.Emsg("Config", ENAMETOOLONG, "create home directory", cwDir);
1010 return false;
1011 }
1012
1013// Create the path if it doesn't exist
1014//
1015 if ((rc = makePath(cwDir, mode, true)))
1016 {eDest.Emsg("Config", rc, "create home directory", cwDir);
1017 return false;
1018 }
1019
1020// Switch to this directory
1021//
1022 if (chdir(cwDir) < 0)
1023 {eDest.Emsg("Config", errno, "chdir to home directory", cwDir);
1024 return false;
1025 }
1026
1027// All done
1028//
1029 return true;
1030}
1031
1032/******************************************************************************/
1033/* m a k e P a t h */
1034/******************************************************************************/
1035
1036int XrdOucUtils::makePath(char *path, mode_t mode, bool reset)
1037{
1038 char *next_path = path+1;
1039 struct stat buf;
1040 bool dochmod = false; // The 1st component stays as is
1041
1042// Typically, the path exists. So, do a quick check before launching into it
1043//
1044 if (!reset && !stat(path, &buf)) return 0;
1045
1046// Start creating directories starting with the root
1047//
1048 while((next_path = index(next_path, int('/'))))
1049 {*next_path = '\0';
1050 if (MAKEDIR(path, mode))
1051 if (errno != EEXIST) return -errno;
1052 if (dochmod) CHMOD(path, mode);
1053 dochmod = reset;
1054 *next_path = '/';
1055 next_path = next_path+1;
1056 }
1057
1058// All done
1059//
1060 return 0;
1061}
1062
1063/******************************************************************************/
1064/* m o d e 2 m a s k */
1065/******************************************************************************/
1066
1067bool XrdOucUtils::mode2mask(const char *mode, mode_t &mask)
1068{
1069 mode_t mval[3] = {0}, mbit[3] = {0x04, 0x02, 0x01};
1070 const char *mok = "rwx";
1071 char mlet;
1072
1073// Accept octal mode
1074//
1075 if (isdigit(*mode))
1076 {char *eP;
1077 mask = strtol(mode, &eP, 8);
1078 return *eP == 0;
1079 }
1080
1081// Make sure we have the correct number of characters
1082//
1083 int n = strlen(mode);
1084 if (!n || n > 9 || n/3*3 != n) return false;
1085
1086// Convert groups of three
1087//
1088 int k = 0;
1089 do {for (int i = 0; i < 3; i++)
1090 {mlet = *mode++;
1091 if (mlet != '-')
1092 {if (mlet != mok[i]) return false;
1093 mval[k] |= mbit[i];
1094 }
1095 }
1096 } while(++k < 3 && *mode);
1097
1098// Combine the modes and return success
1099//
1100 mask = mval[0]<<6 | mval[1]<<3 | mval[2];
1101 return true;
1102}
1103
1104/******************************************************************************/
1105/* p a r s e L i b */
1106/******************************************************************************/
1107
1109 const char *libName, char *&libPath, char **libParm)
1110{
1111 char *val, parms[2048];
1112
1113// Get the next token
1114//
1115 val = Config.GetWord();
1116
1117// We do not support stacking as the caller does not support stacking
1118//
1119 if (val && !strcmp("++", val))
1120 {eDest.Say("Config warning: stacked plugins are not supported in "
1121 "this context; directive ignored!");
1122 return true;
1123 }
1124
1125// Now skip over any options
1126//
1127 while(val && *val && *val == '+') val = Config.GetWord();
1128
1129// Check if we actually have a path
1130//
1131 if (!val || !val[0])
1132 {eDest.Emsg("Config", libName, "not specified"); return false;}
1133
1134// Record the path
1135//
1136 if (libPath) free(libPath);
1137 libPath = strdup(val);
1138
1139// Handle optional parameter
1140//
1141 if (!libParm) return true;
1142 if (*libParm) free(*libParm);
1143 *libParm = 0;
1144
1145// Record any parms
1146//
1147 *parms = 0;
1148 if (!Config.GetRest(parms, sizeof(parms)))
1149 {eDest.Emsg("Config", libName, "parameters too long"); return false;}
1150 if (*parms) *libParm = strdup(parms);
1151 return true;
1152}
1153
1154/******************************************************************************/
1155/* p a r s e H o m e */
1156/******************************************************************************/
1157
1159{
1160 char *pval, *val, *HomePath = 0;
1161
1162// Get the path
1163//
1164 pval = Config.GetWord();
1165 if (!pval || !pval[0])
1166 {eDest.Emsg("Config", "home path not specified"); return 0;}
1167
1168// Make sure it's an absolute path
1169//
1170 if (*pval != '/')
1171 {eDest.Emsg("Config", "home path not absolute"); return 0;}
1172
1173// Record the path
1174//
1175 HomePath = strdup(pval);
1176
1177// Get the optional access rights
1178//
1179 mode = S_IRWXU;
1180 if ((val = Config.GetWord()) && val[0])
1181 {if (!strcmp("group", val)) mode |= (S_IRGRP | S_IXGRP);
1182 else {eDest.Emsg("Config", "invalid home path modifier -", val);
1183 free(HomePath);
1184 return 0;
1185 }
1186 }
1187 return HomePath;
1188}
1189
1190/******************************************************************************/
1191/* R e L i n k */
1192/******************************************************************************/
1193
1194int XrdOucUtils::ReLink(const char *path, const char *target, mode_t mode)
1195{
1196 const mode_t AMode = S_IRWXU; // Only us as a default
1197 char pbuff[MAXPATHLEN+64];
1198 int n;
1199
1200// Copy the path
1201//
1202 n = strlen(path);
1203 if (n >= (int)sizeof(pbuff)) return ENAMETOOLONG;
1204 strcpy(pbuff, path);
1205
1206// Unlink the target, make the path, and create the symlink
1207//
1208 unlink(path);
1209 makePath(pbuff, (mode ? mode : AMode));
1210 if (symlink(target, path)) return errno;
1211 return 0;
1212}
1213
1214/******************************************************************************/
1215/* S a n i t i z e */
1216/******************************************************************************/
1217
1218void XrdOucUtils::Sanitize(char *str, char subc)
1219{
1220
1221// Sanitize string according to POSIX.1-2008 stanadard using only the
1222// Portable Filename Character Set: a-z A-Z 0-9 ._- with 1st char not being -
1223//
1224 if (*str)
1225 {if (*str == '-') *str = subc;
1226 else if (*str == ' ') *str = subc;
1227 char *blank = rindex(str, ' ');
1228 if (blank) while(*blank == ' ') *blank-- = 0;
1229 while(*str)
1230 {if (!isalnum(*str) && index("_-.", *str) == 0) *str = subc;
1231 str++;
1232 }
1233 }
1234}
1235
1236/******************************************************************************/
1237/* s u b L o g f n */
1238/******************************************************************************/
1239
1240char *XrdOucUtils::subLogfn(XrdSysError &eDest, const char *inst, char *logfn)
1241{
1242 const mode_t lfm = S_IRWXU|S_IRWXG|S_IROTH|S_IXOTH;
1243 char buff[2048], *sp;
1244 int rc;
1245
1246 if (!inst || !*inst) return logfn;
1247 if (!(sp = rindex(logfn, '/'))) strcpy(buff, "./");
1248 else {*sp = '\0'; strcpy(buff, logfn); strcat(buff, "/");}
1249
1250 strcat(buff, inst); strcat(buff, "/");
1251
1252 if ((rc = XrdOucUtils::makePath(buff, lfm)))
1253 {eDest.Emsg("Config", rc, "create log file path", buff);
1254 return 0;
1255 }
1256
1257 if (sp) {*sp = '/'; strcat(buff, sp+1);}
1258 else strcat(buff, logfn);
1259
1260 free(logfn);
1261 return strdup(buff);
1262}
1263
1264/******************************************************************************/
1265/* t o L o w e r */
1266/******************************************************************************/
1267
1269{
1270 unsigned char* ustr = (unsigned char*)str; // Avoid undefined behaviour
1271
1272// Change each character to lower case
1273//
1274 while(*ustr) {*ustr = tolower(*ustr); ustr++;}
1275}
1276
1277/******************************************************************************/
1278/* T o k e n */
1279/******************************************************************************/
1280
1281int XrdOucUtils::Token(const char **str, char delim, char *buff, int bsz)
1282{
1283 const char *eP, *bP = *str;
1284 int aLen, mLen;
1285
1286// Trim off the delimeters. Return zero if nothing left.
1287//
1288 while(*bP && *bP == delim) bP++;
1289 if (*bP == 0) {*buff = 0; return 0;}
1290
1291// Find the next delimiter
1292//
1293 eP = bP;
1294 while(*eP && *eP != delim) eP++;
1295
1296// If we ended at a null, make sure next call will return zero
1297//
1298 if (*eP == 0) *str = eP;
1299 else *str = eP+1;
1300
1301// Calculate length and make sure we don't overrun the buffer
1302//
1303 aLen = eP-bP;
1304 if (aLen >= bsz) mLen = bsz-1;
1305 else mLen = aLen;
1306
1307// Copy token into buffer and end with null byte
1308//
1309 strncpy(buff, bP, mLen);
1310 buff[mLen] = 0;
1311
1312// Return actual length
1313//
1314 return aLen;
1315}
1316
1317/******************************************************************************/
1318/* U n d e r c o v e r */
1319/******************************************************************************/
1320#ifdef WIN32
1321void XrdOucUtils::Undercover(XrdSysError &, int, int *)
1322{
1323}
1324#else
1325void XrdOucUtils::Undercover(XrdSysError &eDest, int noLog, int *pipeFD)
1326{
1327 static const int maxFiles = 256;
1328 pid_t mypid;
1329 int myfd, logFD = eDest.baseFD();
1330
1331// Issue warning if there is no logfile attached
1332//
1333 if (noLog) eDest.Emsg("Config", "Warning! No log file specified; "
1334 "backgrounding disables all logging!");
1335
1336// Fork so that we are not tied to a shell
1337//
1338 if ((mypid = fork()) < 0)
1339 {eDest.Emsg("Config", errno, "fork process 1 for backgrounding");
1340 return;
1341 }
1342 else if (mypid)
1343 {
1344 // we have been given a pair of pipe descriptors to be able to read the
1345 // status of the child process
1346 if( pipeFD )
1347 {
1348 int status = 1;
1349 close( pipeFD[1] );
1350 // read will wait untill the status is communicated by the
1351 // child process, if the child process dies before being able
1352 // to comunicate the status then read will see EOF
1353 if( read( pipeFD[0], &status, sizeof(status) ) != sizeof(status) )
1354 _exit(1);
1355 _exit(status);
1356 }
1357 // no pipes given, return success
1358 else _exit(0);
1359 }
1360
1361 if( pipeFD )
1362 close( pipeFD[0] );
1363
1364// Become the process group leader
1365//
1366 if (setsid() < 0)
1367 {eDest.Emsg("Config", errno, "doing setsid() for backgrounding");
1368 return;
1369 }
1370
1371// Fork to that we are cannot get a controlling terminal
1372//
1373 if ((mypid = fork()) < 0)
1374 {eDest.Emsg("Config", errno, "fork process 2 for backgrounding");
1375 return;
1376 }
1377 else if (mypid) _exit(0);
1378
1379// Switch stdin, stdout, and stderr to /dev/null (we can't use /dev/console
1380// unless we are root which is unlikely).
1381//
1382 if ((myfd = open("/dev/null", O_RDWR)) < 0)
1383 {eDest.Emsg("Config", errno, "open /dev/null for backgrounding");
1384 return;
1385 }
1386 dup2(myfd, 0); dup2(myfd, 1); dup2(myfd, 2); dup2(myfd, logFD);
1387
1388// Close any open file descriptors left open by the parent process
1389// but the communication pipe and the logger's shadow file descriptor.
1390//
1391 for (myfd = 3; myfd < maxFiles; myfd++)
1392 if( (!pipeFD || myfd != pipeFD[1]) && myfd != logFD ) close(myfd);
1393}
1394
1395/******************************************************************************/
1396/* U i d N a m e */
1397/******************************************************************************/
1398
1399int XrdOucUtils::UidName(uid_t uID, char *uName, int uNsz, time_t keepT)
1400{
1401 struct passwd *pEnt, pStruct;
1402 char pBuff[1024];
1403 int n, rc;
1404
1405// Get ID from cache, if allowed
1406//
1407 if (keepT)
1408 {int n = LookUp(uidMap, static_cast<unsigned int>(uID),uName,uNsz);
1409 if (n > 0) return (n < uNsz ? n : 0);
1410 }
1411
1412// Try to obtain the username. We use this form to make sure we are using
1413// the standards conforming version (compilation error otherwise).
1414//
1415 rc = getpwuid_r(uID, &pStruct, pBuff, sizeof(pBuff), &pEnt);
1416 if (rc || !pEnt)
1417 {n = snprintf(uName, uNsz, "%ud", static_cast<unsigned int>(uID));
1418 return (n >= uNsz ? 0 : n);
1419 }
1420
1421// Add entry to the cache if need be
1422//
1423 if (keepT)
1424 AddID(uidMap, static_cast<unsigned int>(uID), pEnt->pw_name, keepT);
1425
1426// Return length of username or zero if it is too big
1427//
1428 n = strlen(pEnt->pw_name);
1429 if (uNsz <= (int)strlen(pEnt->pw_name)) return 0;
1430 strcpy(uName, pEnt->pw_name);
1431 return n;
1432}
1433
1434/******************************************************************************/
1435/* U s e r N a m e */
1436/******************************************************************************/
1437
1438int XrdOucUtils::UserName(uid_t uID, char *uName, int uNsz)
1439{
1440 struct passwd *pEnt, pStruct;
1441 char pBuff[1024];
1442 int rc;
1443
1444// Try to obtain the username. We use this form to make sure we are using
1445// the standards conforming version (compilation error otherwise).
1446//
1447 rc = getpwuid_r(uID, &pStruct, pBuff, sizeof(pBuff), &pEnt);
1448 if (rc) return rc;
1449 if (!pEnt) return ESRCH;
1450
1451// Return length of username or zero if it is too big
1452//
1453 if (uNsz <= (int)strlen(pEnt->pw_name)) return ENAMETOOLONG;
1454 strcpy(uName, pEnt->pw_name);
1455 return 0;
1456}
1457
1458/******************************************************************************/
1459/* V a l P a t h */
1460/******************************************************************************/
1461
1462const char *XrdOucUtils::ValPath(const char *path, mode_t allow, bool isdir)
1463{
1464 static const mode_t mMask = S_IRWXU | S_IRWXG | S_IRWXO;
1465 struct stat buf;
1466
1467// Check if this really exists
1468//
1469 if (stat(path, &buf))
1470 {if (errno == ENOENT) return "does not exist.";
1471 return XrdSysE2T(errno);
1472 }
1473
1474// Verify that this is the correct type of file
1475//
1476 if (isdir)
1477 {if (!S_ISDIR(buf.st_mode)) return "is not a directory.";
1478 } else {
1479 if (!S_ISREG(buf.st_mode)) return "is not a file.";
1480 }
1481
1482// Verify that the does not have excessive privileges
1483//
1484 if ((buf.st_mode & mMask) & ~allow) return "has excessive access rights.";
1485
1486// All went well
1487//
1488 return 0;
1489}
1490
1491/******************************************************************************/
1492/* P i d F i l e */
1493/******************************************************************************/
1494
1496{
1497 char buff[32];
1498 int fd;
1499
1500 if( (fd = open( path, O_WRONLY|O_CREAT|O_TRUNC, 0644 )) < 0 )
1501 {
1502 eDest.Emsg( "Config", errno, "create pidfile" );
1503 return false;
1504 }
1505
1506 if( write( fd, buff, snprintf( buff, sizeof(buff), "%d",
1507 static_cast<int>(getpid()) ) ) < 0 )
1508 {
1509 eDest.Emsg( "Config", errno, "write to pidfile" );
1510 close(fd);
1511 return false;
1512 }
1513
1514 close(fd);
1515 return true;
1516}
1517/******************************************************************************/
1518/* getModificationTime */
1519/******************************************************************************/
1520int XrdOucUtils::getModificationTime(const char *path, time_t &modificationTime) {
1521 struct stat buf;
1522 int statRet = ::stat(path,&buf);
1523 if(!statRet) {
1524 modificationTime = buf.st_mtime;
1525 }
1526 return statRet;
1527}
1528
1529void XrdOucUtils::trim(std::string &str) {
1530 // Trim leading non-letters
1531 while( str.size() && !isgraph(str[0]) ) str.erase(str.begin());
1532
1533 // Trim trailing non-letters
1534
1535 while( str.size() && !isgraph(str[str.size()-1]) )
1536 str.resize (str.size () - 1);
1537}
1538
1539void XrdOucUtils::trim(std::string_view & sv) {
1540 const auto toTrim = [](char c) { return !isgraph(c); };
1541 size_t start = 0;
1542 size_t end = sv.size();
1543
1544 while (start < end && toTrim(sv[start])) ++start;
1545 while (end > start && toTrim(sv[end - 1])) --end;
1546
1547 sv = sv.substr(start, end - start);
1548}
1549
1550uint8_t XrdOucUtils::touint8_t(const std::string_view sv) {
1551 unsigned int temp; // wider type for parsing
1552 auto [ptr, ec] = std::from_chars(sv.data(), sv.data() + sv.size(), temp);
1553
1554 if (ec == std::errc::invalid_argument) {
1555 throw std::invalid_argument("Invalid number format");
1556 }
1557 if (ec == std::errc::result_out_of_range || temp > std::numeric_limits<uint8_t>::max()) {
1558 throw std::out_of_range("Value out of range for unsigned short");
1559 }
1560
1561 return static_cast<unsigned short>(temp);
1562}
1563
1568
1569static bool is_token_character(int c)
1570{
1571 if (isalnum(c))
1572 return true;
1573
1574 static constexpr char token_chars[] = "-._~+/=:%";
1575
1576 for (char ch : token_chars)
1577 if (c == ch)
1578 return true;
1579
1580 return false;
1581}
1582
1590
1591std::string obfuscateAuth(const std::string& input)
1592{
1593 static const regex_t auth_regex = []() {
1594 constexpr char re[] =
1595 "(authz=|(transferheader)?(www-|proxy-)?auth(orization|enticate)[[:space:]]*:[[:space:]]*)"
1596 "(Bearer([[:space:]]|%20)?(token([[:space:]]|%20)?)?)?";
1597
1598 regex_t regex;
1599
1600 if (regcomp(&regex, re, REG_EXTENDED | REG_ICASE) != 0)
1601 throw std::runtime_error("Failed to compile regular expression");
1602
1603 return regex;
1604 }();
1605
1606 regmatch_t match;
1607 size_t offset = 0;
1608 std::string redacted;
1609 const char *const text = input.c_str();
1610
1611 while (regexec(&auth_regex, text + offset, 1, &match, 0) == 0) {
1612 redacted.append(text + offset, match.rm_eo).append("REDACTED");
1613
1614 offset += match.rm_eo;
1615
1616 while (offset < input.size() && is_token_character(input[offset]))
1617 ++offset;
1618 }
1619
1620 return redacted.append(text + offset);
1621}
1622
1623static bool is_rfc3986_unreserved(unsigned char c)
1624{
1625 return std::isalnum(c) || c == '-' || c == '_' || c == '.' || c == '~';
1626}
1627
1635std::string XrdOucUtils::UrlEncode(const std::string &input)
1636{
1637 static const char hex[] = "0123456789ABCDEF";
1638
1639 std::string out;
1640 out.reserve(input.size() * 3);
1641
1642 for (unsigned char c: input) {
1643 if (is_rfc3986_unreserved(c)) {
1644 out.push_back(c);
1645 } else {
1646 out.push_back('%');
1647 out.push_back(hex[c >> 4]);
1648 out.push_back(hex[c & 0x0f]);
1649 }
1650 }
1651 return out;
1652}
1653
1654static int from_hex(char c)
1655{
1656 if (c >= '0' && c <= '9') return c - '0';
1657 if (c >= 'A' && c <= 'F') return c - 'A' + 10;
1658 if (c >= 'a' && c <= 'f') return c - 'a' + 10;
1659 return -1;
1660}
1661
1669std::string XrdOucUtils::UrlDecode(const std::string &input)
1670{
1671 std::string out;
1672 out.reserve(input.size());
1673
1674 for (size_t i = 0; i < input.size(); ++i) {
1675 if (input[i] == '%' && i + 2 < input.size() &&
1676 std::isxdigit(input[i + 1]) &&
1677 std::isxdigit(input[i + 2])) {
1678 const int hi = from_hex(input[i + 1]);
1679 const int lo = from_hex(input[i + 2]);
1680 out.push_back(static_cast<char>((hi << 4) | lo));
1681 i += 2;
1682 } else if (input[i] == '+') {
1683 out.push_back(' ');
1684 } else {
1685 out.push_back(input[i]);
1686 }
1687 }
1688 return out;
1689}
1690
1697
1698void stripCgi(std::string& url, const std::unordered_set<std::string> &cgiKeys)
1699{
1700 for (const auto &key : cgiKeys) {
1701 if (key.empty())
1702 continue;
1703
1704 const std::string needle = key + "=";
1705 size_t spos = 0, epos = 0;
1706
1707 while ((spos = url.find(needle, spos)) != std::string::npos) {
1708 epos = spos;
1709 while (epos < url.size() && is_token_character(url[epos]))
1710 ++epos;
1711 url.erase(spos, epos - spos);
1712 }
1713 }
1714
1715 // If a stripped CGI was the first element, remove the extra &
1716 size_t spos = 0;
1717 if ((spos = url.find("?&")) != std::string::npos)
1718 url.erase(spos + 1, 1);
1719
1720 // If stripping removed the only query parameter, remove the dangling ?
1721 if (!url.empty() && url.back() == '?')
1722 url.pop_back();
1723}
1724
1725void stripCgi(XrdOucString& url, const std::unordered_set<std::string> &cgiKeys)
1726{
1727 std::string tmp = url.c_str();
1728 stripCgi(tmp, cgiKeys);
1729 url = tmp.c_str();
1730}
1731
1732#endif
struct stat Stat
Definition XrdCks.cc:49
static XrdSysError eDest(0,"crypto_")
#define ENODATA
uint32_t crc32c(uint32_t crc, void const *buf, size_t len)
#define SHFT(k)
void stripCgi(std::string &url, const std::unordered_set< std::string > &cgiKeys)
static bool is_token_character(int c)
static int from_hex(char c)
static bool is_rfc3986_unreserved(unsigned char c)
std::string obfuscateAuth(const std::string &input)
#define close(a)
Definition XrdPosix.hh:48
#define fstat(a, b)
Definition XrdPosix.hh:62
#define write(a, b, c)
Definition XrdPosix.hh:115
#define open
Definition XrdPosix.hh:76
#define chdir(a)
Definition XrdPosix.hh:46
#define unlink(a)
Definition XrdPosix.hh:113
#define stat(a, b)
Definition XrdPosix.hh:101
#define read(a, b, c)
Definition XrdPosix.hh:82
const char * XrdSysE2T(int errcode)
Definition XrdSysE2T.cc:104
size_t strlcpy(char *dst, const char *src, size_t sz)
#define CHMOD(path, mode)
#define MAKEDIR(path, mode)
static bool Match(const char *hName, const char *pattern)
static bool Parse(const char *hSpec, const char **hName, const char **hNend, const char **hPort, const char **hPend)
static uint32_t Calc32C(const void *data, size_t count, uint32_t prevcs=0)
Definition XrdOucCRC.cc:190
char * Get(const char *varname)
Definition XrdOucEnv.hh:69
MDLen
SHA3 digest lengths (bits to bytes).
Definition XrdOucSHA3.hh:56
static void * Calc(const void *in, size_t inlen, void *md, MDLen mdlen)
int length() const
int tokenize(XrdOucString &tok, int from, char del=':')
const char * c_str() const
static char * parseHome(XrdSysError &eDest, XrdOucStream &Config, int &mode)
static void Sanitize(char *instr, char subc='_')
static bool getGID(const char *gName, gid_t &gID)
static const mode_t pathMode
static const char * HSize(size_t bytes, char *buff, int bsz)
static int isFWD(const char *path, int *port=0, char *hBuff=0, int hBLen=0, bool pTrim=false)
static int UserName(uid_t uID, char *uName, int uNsz)
static char * Ident(long long &mySID, char *iBuff, int iBlen, const char *iHost, const char *iProg, const char *iName, int Port)
static bool getUID(const char *uName, uid_t &uID, gid_t *gID=0)
static int getModificationTime(const char *path, time_t &modificationTime)
static const char * ValPath(const char *path, mode_t allow, bool isdir)
static char * genPath(const char *path, const char *inst, const char *psfx=0)
static uint8_t touint8_t(const std::string_view sv)
static int Token(const char **str, char delim, char *buff, int bsz)
static int ReLink(const char *path, const char *target, mode_t mode=0)
static bool parseLib(XrdSysError &eDest, XrdOucStream &Config, const char *libName, char *&path, char **libparm)
static int hex2bin(const char *hex, char *bin, int size)
static int is1of(char *val, const char **clist)
static std::string UrlDecode(const std::string &input)
static const char * InstName(int TranOpt=0)
static std::string UrlEncode(const std::string &input)
static char * eText(int rc, char *eBuff, int eBlen)
static int argList(char *args, char **argV, int argC)
static bool mode2mask(const char *mode, mode_t &mask)
static std::string genHumanSize(size_t size, uint64_t base)
static int GidName(gid_t gID, char *gName, int gNsz, time_t keepT=0)
static int UidName(uid_t uID, char *uName, int uNsz, time_t keepT=0)
static char * bin2hex(char *inbuff, int dlen, char *buff, int blen, bool sep=true)
static int Log10(unsigned long long n)
static int doIf(XrdSysError *eDest, XrdOucStream &Config, const char *what, const char *hname, const char *nname, const char *pname)
static int makePath(char *path, mode_t mode, bool reset=false)
static int GroupName(gid_t gID, char *gName, int gNsz)
static void trim(std::string &str)
static bool findPgm(const char *pgm, XrdOucString &path)
static bool PidFile(XrdSysError &eDest, const char *path)
static const char * i2bstr(char *buff, int blen, int val, bool pad=false)
static void toLower(char *str)
static int fmtBytes(long long val, char *buff, int bsz)
static int Log2(unsigned long long n)
static void makeHome(XrdSysError &eDest, const char *inst)
static bool endsWith(const char *text, const char *ending, int endlen)
static void Undercover(XrdSysError &eDest, int noLog, int *pipeFD=0)
static char * getFile(const char *path, int &rc, int maxsz=10240, bool notempty=true)
static char * subLogfn(XrdSysError &eDest, const char *inst, char *logfn)