XRootD
Loading...
Searching...
No Matches
XrdNetUtils.cc
Go to the documentation of this file.
1/******************************************************************************/
2/* */
3/* X r d N e t U t i l s . c c */
4/* */
5/* (c) 2025 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 <cinttypes>
33#include <netdb.h>
34#include <cstring>
35#include <unistd.h>
36
37#include <netinet/in.h>
38#include <sys/socket.h>
39#include <sys/types.h>
40#include <fcntl.h>
41#include <poll.h>
42
43#include "XrdNet/XrdNetAddr.hh"
45#include "XrdNet/XrdNetIF.hh"
47#include "XrdNet/XrdNetUtils.hh"
48#include "XrdOuc/XrdOucTList.hh"
49#include "XrdOuc/XrdOucUtils.hh"
50#include "XrdSys/XrdSysE2T.hh"
52#ifndef HAVE_PROTOR
54#endif
55
56/******************************************************************************/
57/* S t a t i c M e m b e r s */
58/******************************************************************************/
59
60int XrdNetUtils::autoFamily;
61
62// The following also sets autoFamily!
63//
64int XrdNetUtils::autoHints = XrdNetUtils::SetAuto(XrdNetUtils::prefAuto);
65
66/******************************************************************************/
67/* C o m p a r e */
68/******************************************************************************/
69
71 XrdNetSockAddr& ip2, bool* psame)
72{
73// The family code is always in the same place so trivially check equivalence
74//
75 if (ip1.Addr.sa_family != ip2.Addr.sa_family) return IPDFam;
76
77// Since the mailies are te same we need to check if he actual ip addess is
78// same, What we check depends on the address family. Start with IPv6.
79//
80 if (ip1.Addr.sa_family == AF_INET6)
81 {if (memcmp(&ip1.v6.sin6_addr,&ip2.v6.sin6_addr,sizeof(struct in6_addr)))
82 return IPDiff;
83 if (psame) *psame = (ip1.v6.sin6_port == ip2.v6.sin6_port);
84 return IPSame;
85 }
86
87// Try IPv4
88//
89 if (ip1.Addr.sa_family == AF_INET)
90 {if (memcmp(&ip1.v4.sin_addr,&ip2.v4.sin_addr, sizeof(struct in_addr)))
91 return IPDiff;
92 if (psame) *psame = (ip1.v4.sin_port == ip2.v4.sin_port);
93 return IPSame;
94 }
95
96// We don't support the address family
97//
98 return IPNSup;
99}
100
101/******************************************************************************/
102/* D e c o d e */
103/******************************************************************************/
104
105int XrdNetUtils::Decode(XrdNetSockAddr *sadr, const char *buff, int blen)
106{
107 static const int ipv4Sz = sizeof(struct in_addr)*2+4;
108 static const int ipv6Sz = sizeof(struct in6_addr)*2+4;
109 char bval[sizeof(struct in6_addr)+2];
110 int isv6, n, i = 0, Odd = 0;
111
112// Determine if this will be IPV4 or IPV6 (only ones allowed)
113//
114 if (blen == ipv6Sz) isv6 = 1;
115 else if (blen == ipv4Sz) isv6 = 0;
116 else return -1;
117
118// Convert the whole string to a temporary
119//
120 while(blen--)
121 { if (*buff >= '0' && *buff <= '9') n = *buff-48;
122 else if (*buff >= 'a' && *buff <= 'f') n = *buff-87;
123 else if (*buff >= 'A' && *buff <= 'F') n = *buff-55;
124 else return -1;
125 if (Odd) bval[i++] |= n;
126 else bval[i ] = n << 4;
127 buff++; Odd = ~Odd;
128 }
129
130// Clear the address
131//
132 memset(sadr, 0, sizeof(XrdNetSockAddr));
133
134// Copy out the data, as needed
135//
136 if (isv6)
137 {sadr->v6.sin6_family = AF_INET6;
138 memcpy(&(sadr->v6.sin6_port), bval, 2);
139 memcpy(&(sadr->v6.sin6_addr), &bval[2], sizeof(struct in6_addr));
140 } else {
141 sadr->v4.sin_family = AF_INET;
142 memcpy(&(sadr->v4.sin_port), bval, 2);
143 memcpy(&(sadr->v4.sin_addr), &bval[2], sizeof(struct in_addr));
144 }
145
146// Return the converted port (it's the same for v4/v6)
147//
148 return static_cast<int>(ntohs(sadr->v6.sin6_port));
149}
150
151/******************************************************************************/
152/* E n c o d e */
153/******************************************************************************/
154
155int XrdNetUtils::Encode(const XrdNetSockAddr *sadr, char *buff, int blen,
156 int port)
157{
158 static const char *hv = "0123456789abcdef";
159 char *src, bval[sizeof(struct in6_addr)+2];
160 int asz, i, j = 0;
161
162// Compute the size we need for the buffer (note we only support IP4/6)
163//
164 if (sadr->Addr.sa_family == AF_INET6)
165 {src = (char *)&(sadr->v6.sin6_addr); asz = sizeof(struct in6_addr);}
166 else if (sadr->Addr.sa_family == AF_INET)
167 {src = (char *)&(sadr->v4.sin_addr); asz = sizeof(struct in_addr); }
168 else return 0;
169 if (blen < (asz*2)+5) return -((asz*2)+5);
170
171// Get the port value in the first two bytes followed by the address.
172//
173 if (port < 0) memcpy(bval, &(sadr->v6.sin6_port), 2);
174 else {short sPort = htons(static_cast<short>(port));
175 memcpy(bval, &sPort, 2);
176 }
177 memcpy(&bval[2], src, asz);
178 asz += 2;
179
180// Now convert to hex
181//
182 for (i = 0; i < asz; i++)
183 {buff[j++] = hv[(bval[i] >> 4) & 0x0f];
184 buff[j++] = hv[ bval[i] & 0x0f];
185 }
186 buff[j] = '\0';
187
188// All done
189//
190 return asz*2;
191}
192
193/******************************************************************************/
194/* Private: F i l l A d d r s */
195/******************************************************************************/
196
197#define OrderXX (XrdNetUtils::order46 | XrdNetUtils::order64)
198
199#define SIN_PORT(x) ((struct sockaddr_in *)(x->ai_addr))->sin_port
200namespace XrdNetSpace
201{
202struct hpSpec
203 {const char *ipAddr;
204 addrinfo hints;
205 addrinfo *aiP4;
206 int aNum4;
207 int aNum6;
208 addrinfo *aiP6;
209 int port;
210 bool map426;
214 char ipMap[7]; // ::ffff: (length = 7)
215 char ipAdr[256];
216
218 : aiP4(0), aNum4(0), aNum6(0), aiP6(0), map426(false),
219 noOrder((opts & OrderXX) == 0),
220 order46((opts & XrdNetUtils::order46) != 0),
221 onlyUDP((opts & XrdNetUtils::onlyUDP) != 0) {}
222
223 ~hpSpec() {if (aiP4) freeaddrinfo(aiP4);
224 if (aiP6) freeaddrinfo(aiP6);
225 }
226 };
227}
228using namespace XrdNetSpace;
229
230void XrdNetUtils::FillAddr(XrdNetSpace::hpSpec &aInfo, XrdNetAddr *aVec,
231 int *ordn, unsigned int rotNum)
232{
233 struct addrinfo *aP, *nP[2];
234 int aN[2];
235
236// Establish ordering
237//
238 if (aInfo.order46)
239 {nP[0] = aInfo.aiP4; aN[0] = aInfo.aNum4;
240 nP[1] = aInfo.aiP6; aN[1] = aInfo.aNum6;
241 } else {
242 nP[0] = aInfo.aiP6; aN[0] = aInfo.aNum6;
243 nP[1] = aInfo.aiP4; aN[1] = aInfo.aNum4;
244 }
245
246 for (int k = 0; k < 2; k++)
247 {int sz = aN[k];
248 if (sz && (aP = nP[k]))
249 {int iBeg = rotNum % sz, iEnd = sz;
250 do {for (int i = iBeg; i < iEnd && aP; i++)
251 {int pNum = int(SIN_PORT(aP)) & 0x0000ffff;
252 aVec[i].Set(aP, pNum, aInfo.map426);
253 aP = aP->ai_next;
254 }
255 iEnd = iBeg; iBeg = 0;
256 } while(aP);
257 aVec = &aVec[sz];
258 }
259 }
260
261// Supply the ordinal if it is wanted
262//
263 if (ordn)
264 {if (aInfo.noOrder) *ordn = aInfo.aNum4 + aInfo.aNum6;
265 else if (aInfo.order46) *ordn = aInfo.aNum4;
266 else *ordn = aInfo.aNum6;
267 }
268}
269
270/******************************************************************************/
271/* G e t A d d r s */
272/******************************************************************************/
273
274const char *XrdNetUtils::GetAddrs(const char *hSpec,
275 XrdNetAddr *aVec[], int &aVsz,
276 XrdNetUtils::AddrOpts opts, int pNum)
277{
278 const char *eText;
279 hpSpec aInfo(opts);
280
281// Prep the returned fields
282//
283 *aVec = 0;
284 aVsz = 0;
285
286// Parse the options
287//
288 GetHints(aInfo, opts);
289
290// Parse the host specification and get addresses
291//
292 if ((eText = GetHostPort(aInfo, hSpec, pNum))
293 || (eText = GetAInfo(aInfo))) return eText;
294
295// If we have any addresses, resize the vector with that many netaddr objects
296// and then initialze each one of them.
297//
298 if (aInfo.aNum4 || aInfo.aNum6)
299 {aVsz = aInfo.aNum4 + aInfo.aNum6;
300 *aVec = new XrdNetAddr[(unsigned int)aVsz];
301 FillAddr(aInfo, *aVec);
302 }
303
304// All done
305//
306 return 0;
307}
308
309/******************************************************************************/
310
311const char *XrdNetUtils::GetAddrs(const std::string &hSpec,
312 std::vector<XrdNetAddr> &aVec,
313 int *ordn, AddrOpts opts, int pNum)
314{
315// If this references a registered name, process it as such.
316//
317 if (*(hSpec.c_str()) == XrdNetRegistry::pfx)
318 return XrdNetRegistry::GetAddrs(hSpec, aVec, ordn, opts, pNum);
319
320// Start up!
321//
322 const char *eText;
323 hpSpec aInfo(opts);
324
325// Clear the result vector
326//
327 aVec.clear();
328 if (ordn) *ordn = 0;
329
330// Parse the options
331//
332 GetHints(aInfo, opts);
333
334// Parse the host specification and get address info
335//
336 if ((eText = GetHostPort(aInfo, hSpec.c_str(), pNum))
337 || (eText = GetAInfo(aInfo))) return eText;
338
339// If we have any addresses, resize the vector with that many netaddr objects
340// and then initialze each one of them.
341//
342 if (aInfo.aNum4 || aInfo.aNum6)
343 {aVec.resize(aInfo.aNum4 + aInfo.aNum6);
344 FillAddr(aInfo, aVec.data(), ordn);
345 }
346
347// All done
348//
349 return 0;
350}
351
352/******************************************************************************/
353
354const char *XrdNetUtils::GetAddrs(std::vector<std::string> &hSVec,
355 std::vector<XrdNetAddr> &aVec, int *ordn,
356 AddrOpts opts, unsigned int rotNum,
357 bool force)
358{
359 const char *eText;
360 hpSpec aInfo(opts);
361
362// Clear the result vector and make sure we have something to do
363//
364 aVec.clear();
365 if (ordn) *ordn = 0;
366 if (!hSVec.size()) return 0;
367
368// Parse the options
369//
370 GetHints(aInfo, opts);
371
372// Process each specification
373//
374 for (int i = 0; i < (int)hSVec.size(); i++)
375 {if (((eText = GetHostPort(aInfo, hSVec[i].c_str(), PortInSpec))
376 || (eText = GetAInfo(aInfo))) && !force) return eText;
377 }
378
379// Size the vector and fill it in
380//
381 if (aInfo.aNum4 || aInfo.aNum6)
382 {aVec.resize(aInfo.aNum4 + aInfo.aNum6);
383 FillAddr(aInfo, aVec.data(), ordn, rotNum);
384 }
385
386// All done
387//
388 return 0;
389}
390
391/******************************************************************************/
392/* Private: G e t A I n f o */
393/******************************************************************************/
394
395const char *XrdNetUtils::GetAInfo(XrdNetSpace::hpSpec &aInfo)
396{
397 struct addrinfo *last4 = 0, *last6 = 0, *xP = 0, *rP = 0, *nP;
398 unsigned short pNum = static_cast<unsigned short>(aInfo.port);
399
400// Get all of the addresses
401//
402 int rc = getaddrinfo(aInfo.ipAddr, 0, &aInfo.hints, &rP);
403 if (rc || !rP)
404 {if (rP) freeaddrinfo(rP);
405 return (rc ? gai_strerror(rc) : "host not found");
406 }
407
408// Count the number of entries we will return and chain the entries. We will
409// never return link local addresses which may be returned (shouldn't but does)
410//
411 do {nP = rP->ai_next;
412 if (rP->ai_family == AF_INET6 || rP->ai_family == AF_INET)
413 {SIN_PORT(rP) = pNum;
414 bool v4mapped = false;
415 if (rP->ai_family == AF_INET6)
416 {struct sockaddr_in6 *ipv6 = (struct sockaddr_in6 *)rP->ai_addr;
417 if (IN6_IS_ADDR_LINKLOCAL(&ipv6->sin6_addr))
418 {rP->ai_next = xP; xP = rP; continue;}
419 v4mapped = IN6_IS_ADDR_V4MAPPED(&ipv6->sin6_addr);
420 }
421 if (aInfo.noOrder || rP->ai_family == AF_INET || v4mapped)
422 {if (last4) last4->ai_next = rP;
423 else aInfo.aiP4 = rP;
424 last4 = rP;
425 aInfo.aNum4++;
426 } else {
427 if (last6) last6->ai_next = rP;
428 else aInfo.aiP6 = rP;
429 last6 = rP;
430 aInfo.aNum6++;
431 }
432 rP->ai_next = 0;
433 } else {rP->ai_next = xP; xP = rP;}
434 } while((rP = nP));
435
436// Free any entries that we were not interested in and return
437//
438 if (xP) freeaddrinfo(xP);
439 return 0;
440}
441
442/******************************************************************************/
443/* Private: G e t H i n t s */
444/******************************************************************************/
445
446void XrdNetUtils::GetHints(XrdNetSpace::hpSpec &aInfo,
448{
449 struct addrinfo &hints = aInfo.hints;
450
451// Setup the hints
452//
453 memset(&hints, 0, sizeof(hints));
454 hints.ai_socktype = (aInfo.onlyUDP ? SOCK_DGRAM : SOCK_STREAM);
455 opts = opts & ~(onlyUDP | order46 | order64);
456 switch(opts)
457 {case allIPMap: hints.ai_family = AF_INET6;
458 hints.ai_flags = AI_V4MAPPED | AI_ALL;
459 break;
460 case allIPv64: hints.ai_family = AF_UNSPEC;
461 break;
462 case allV4Map: hints.ai_family = AF_INET;
463 aInfo.map426 = true;
464 break;
465 case onlyIPv6: hints.ai_family = AF_INET6;
466 break;
467 case onlyIPv4: hints.ai_family = AF_INET;
468 break;
469 case prefIPv6: hints.ai_family = AF_INET6;
470 hints.ai_flags = AI_V4MAPPED;
471 break;
472 case prefAuto: hints.ai_family = autoFamily;
473 hints.ai_flags = autoHints;
474 break;
475 default: hints.ai_family = AF_INET6;
476 hints.ai_flags = AI_V4MAPPED | AI_ALL;
477 break;
478 }
479}
480
481/******************************************************************************/
482/* Private: G e t H o s t P o r t */
483/******************************************************************************/
484
485const char *XrdNetUtils::GetHostPort(XrdNetSpace::hpSpec &aInfo,
486 const char *hSpec, int pNum)
487{
488 static const char *badHS = "invalid host specification";
489 const char *hnBeg, *hnEnd, *pnBeg, *pnEnd;
490
491// Copy the host specification
492//
493 if (!hSpec) return badHS;
494 strlcpy(aInfo.ipAdr, hSpec, sizeof(aInfo.ipAdr));
495
496// Parse the host specification
497//
498 if (pNum == NoPortRaw)
499 {hnBeg = aInfo.ipAdr;
500 aInfo.port = 0;
501 } else {
502 if (!Parse(aInfo.ipAdr, &hnBeg, &hnEnd, &pnBeg, &pnEnd)) return badHS;
503 aInfo.ipAdr[hnEnd-aInfo.ipAdr] = 0;
504 if (pnBeg == hnEnd)
505 {if (pNum == PortInSpec) return "port not specified";
506 aInfo.port = abs(pNum);
507 } else {
508 const char *eText;
509 aInfo.ipAdr[pnEnd-aInfo.ipAdr] = 0;
510 int n = ServPort(pnBeg, aInfo.onlyUDP, &eText);
511 if (!n) return eText;
512 if (pNum < 0) aInfo.port = n;
513 }
514 }
515
516// Check if we need to convert an ipv4 address to an ipv6 one
517//
518 if (aInfo.hints.ai_family == AF_INET6 && aInfo.ipAdr[0] != '['
520 {memcpy(aInfo.ipMap, "::ffff:", 7);
521 aInfo.ipAddr = aInfo.ipMap;
522 } else aInfo.ipAddr = hnBeg;
523
524// All done
525//
526 return 0;
527}
528
529/******************************************************************************/
530/* g e t S o k I n f o */
531/******************************************************************************/
532
533int XrdNetUtils::GetSokInfo(int fd, char *theAddr, int theALen, char &theType)
534{
535 XrdNetSockAddr theIP;
536 XrdNetAddr ipAddr;
537 static const int fmtopts = XrdNetAddrInfo::noPortRaw
539 SOCKLEN_t addrSize = sizeof(theIP);
540 int rc;
541 unsigned short thePort;
542
543// The the address wanted
544//
545 rc = (fd > 0 ? getpeername( fd, &theIP.Addr, &addrSize)
546 : getsockname(-fd, &theIP.Addr, &addrSize));
547 if (rc) return -errno;
548
549// Set the address
550//
551 if (ipAddr.Set(&theIP.Addr)) return -EAFNOSUPPORT;
552
553// Establis the type of address we have
554//
555 if (ipAddr.isIPType(XrdNetAddrInfo::IPv4) || ipAddr.isMapped()) theType='4';
556 else theType = '6';
557
558// Now format the address
559//
560 if (theAddr && theALen > 0
561 && !ipAddr.Format(theAddr, theALen, XrdNetAddrInfo::fmtAddr, fmtopts))
562 return -EINVAL;
563
564// Get the port number and return it.
565//
566 thePort = htons((theIP.Addr.sa_family == AF_INET
567 ? theIP.v4.sin_port : theIP.v6.sin6_port));
568 return static_cast<int>(thePort);
569}
570
571
572/******************************************************************************/
573/* H o s t s */
574/******************************************************************************/
575
576XrdOucTList *XrdNetUtils::Hosts(const char *hSpec, int hPort, int hWant,
577 int *sPort, const char **eText)
578{
579 static const int hMax = 8;
580 XrdNetAddr myAddr(0), aList[hMax];
581 XrdOucTList *tList = 0;
582 const char *etext, *hName;
583 int numIP, i, k;
584
585// Check if the port must be in the spec and set maximum
586//
587 if (hPort < 0) hPort = XrdNetAddr::PortInSpec;
588 if (hWant > hMax) hWant = hMax;
589 else if (hWant < 1) hWant = 1;
590
591// Initialze the list of addresses
592//
593 if ((etext = aList[0].Set(hSpec, numIP, hWant, hPort)))
594 {if (eText) *eText = etext;
595 return 0;
596 }
597
598// Create the tlist object list without duplicates. We may have duplicates as
599// this may be a multi-homed node and we don't want to show that here.
600//
601 for (i = 0; i < numIP; i++)
602 {if (sPort && myAddr.Same(&aList[i]))
603 {*sPort = aList[i].Port(); sPort = 0;}
604 hName = aList[i].Name("");
605 for (k = 0; k < i; k++) {if (!strcmp(hName, aList[k].Name(""))) break;}
606 if (k >= i) tList = new XrdOucTList(hName, aList[i].Port(), tList);
607 }
608
609// All done, return the result
610//
611 if (eText) *eText = (tList ? 0 : "unknown processing error");
612 return tList;
613}
614
615/******************************************************************************/
616/* I P F o r m a t */
617/******************************************************************************/
618
619int XrdNetUtils::IPFormat(const struct sockaddr *sAddr,
620 char *bP, int bL, int opts)
621{
622 XrdNetAddr theAddr;
623 int fmtopts = (opts & oldFmt ? XrdNetAddrInfo::old6Map4 : 0);
624
625// Set the address
626//
627 if (theAddr.Set(sAddr)) return 0;
628
629// Now format the address
630//
631 if (opts & noPort) fmtopts |= XrdNetAddrInfo::noPort;
632 return theAddr.Format(bP, bL, XrdNetAddrInfo::fmtAdv6, fmtopts);
633}
634
635/******************************************************************************/
636
637int XrdNetUtils::IPFormat(int fd, char *bP, int bL, int opts)
638{
639 XrdNetSockAddr theIP;
640 SOCKLEN_t addrSize = sizeof(theIP);
641 int rc;
642
643// The the address wanted
644//
645 rc = (fd > 0 ? getpeername( fd, &theIP.Addr, &addrSize)
646 : getsockname(-fd, &theIP.Addr, &addrSize));
647 if (rc) return 0;
648
649// Now format it
650//
651 return IPFormat(&theIP.Addr, bP, bL, opts);
652}
653
654/******************************************************************************/
655/* M a t c h */
656/******************************************************************************/
657
658bool XrdNetUtils::Match(const char *HostName, const char *HostPat)
659{
660 static const int maxIP = 16;
661 const char *mval;
662 int i, j, k;
663
664// First check if this will match right away
665//
666 if (!strcmp(HostPat, HostName)) return true;
667
668// Check for an asterisk do prefix/suffix match
669//
670 if ((mval = index(HostPat, (int)'*')))
671 { i = mval - HostPat; mval++;
672 k = strlen(HostName); j = strlen(mval);
673 if ((i+j) > k
674 || strncmp(HostName, HostPat,i)
675 || strncmp((HostName+k-j),mval,j)) return false;
676 return 1;
677 }
678
679// Now check for host expansion
680//
681 i = strlen(HostPat);
682 if (i && HostPat[i-1] == '+')
683 {XrdNetAddr InetAddr[maxIP];
684 char hBuff[264];
685 if (i >= (int)sizeof(hBuff)) return false;
686 memcpy(hBuff, HostPat, i-1);
687 hBuff[i-1] = 0;
688 if (InetAddr[0].Set(hBuff, i, maxIP, 0)) return false;
689 while(i--) if ((mval = InetAddr[i].Name()) && !strcmp(mval, HostName))
690 return true;
691 }
692
693// No matches
694//
695 return false;
696}
697
698/******************************************************************************/
699/* M y H o s t N a m e */
700/******************************************************************************/
701
702char *XrdNetUtils::MyHostName(const char *eName, const char **eText)
703{
704 const char *fqn = XrdNetIdentity::FQN(eText);
705
706// Return the appropriate result
707//
708 if (!fqn) fqn = eName;
709 return (fqn ? strdup(fqn) : 0);
710}
711
712/******************************************************************************/
713/* N e t C o n f i g */
714/******************************************************************************/
715
717 const char **eText)
718{
719 XrdNetAddr *myAddrs;
720 const char *eMsg;
721 char buff[1024];
722 NetProt hasProt = hasNone;
723 int aCnt, ifType;
724
725// Make sure we support this query
726//
727 if (netquery != qryINET && netquery != qryINIF)
728 {if (eText) *eText = "unsupported NetType query";
729 return hasNone;
730 }
731
732// We base the nonfig of the interface addresses unless we can't query the if's
733//
734 if (netquery == qryINIF && (ifType = XrdNetIF::GetIF((XrdOucTList **)0,0)))
735 {if (ifType & XrdNetIF::haveIPv4) hasProt = NetProt(hasProt | hasIPv4);
736 if (ifType & XrdNetIF::haveIPv6) hasProt = NetProt(hasProt | hasIPv6);
737 if (ifType & XrdNetIF::havePub4) hasProt = NetProt(hasProt | hasPub4);
738 if (ifType & XrdNetIF::havePub6) hasProt = NetProt(hasProt | hasPub6);
739 return hasProt;
740 }
741
742// Get our host name and initialize this object with it
743//
744 gethostname(buff, sizeof(buff));
746
747// Now get all of the addresses associated with this hostname
748//
749 if ((eMsg = GetAddrs(buff, &myAddrs, aCnt, allIPv64, NoPortRaw)))
750 {if (eText) *eText = eMsg;
751 return hasNone;
752 }
753
754// Now run through all of the addresses to see what we have
755//
756 for (int i = 0; i < aCnt && hasProt != hasIP64; i++)
757 { if (myAddrs[i].isIPType(XrdNetAddrInfo::IPv6))
758 {hasProt = NetProt(hasProt | hasIPv6);
759 if (!myAddrs[i].isPrivate())
760 hasProt = NetProt(hasProt | hasPub6);
761 }
762 else if (myAddrs[i].isIPType(XrdNetAddrInfo::IPv4))
763 {hasProt = NetProt(hasProt | hasIPv4);
764 if (!myAddrs[i].isPrivate())
765 hasProt = NetProt(hasProt | hasPub4);
766 }
767 }
768
769// Delete the array and return what we have
770//
771 delete [] myAddrs;
772 if (hasProt == hasNone && eText) *eText = "";
773 return hasProt;
774}
775
776/******************************************************************************/
777/* P a r s e */
778/******************************************************************************/
779
780bool XrdNetUtils::Parse(const char *hSpec,
781 const char **hName, const char **hNend,
782 const char **hPort, const char **hPend)
783{
784 const char *asep = 0;
785
786// Parse the specification
787//
788 if (*hSpec == '[')
789 {if (!(*hNend = index(hSpec+1, ']'))) return false;
790 *hName = hSpec+1; asep = (*hNend)+1;
791 } else {
792 *hName = hSpec;
793 if (!(*hNend = index(hSpec, ':'))) *hNend = hSpec + strlen(hSpec);
794 else asep = *hNend;
795 }
796
797// See if we have a port to parse. We stop on a non-alphameric.
798//
799 if (asep && *asep == ':')
800 {*hPort = ++asep;
801 while(isalnum(*asep)) asep++;
802 if (*hPort == asep) return false;
803 *hPend = asep;
804 } else *hPort = *hPend = *hNend;
805
806// All done
807//
808 return true;
809}
810
811/******************************************************************************/
812/* P o r t */
813/******************************************************************************/
814
815int XrdNetUtils::Port(int fd, const char **eText)
816{
817 XrdNetSockAddr Inet;
818 SOCKLEN_t slen = (socklen_t)sizeof(Inet);
819 int rc;
820
821 if ((rc = getsockname(fd, &Inet.Addr, &slen)))
822 {rc = errno;
823 if (eText) setET(eText, errno);
824 return -rc;
825 }
826
827 return static_cast<int>(ntohs(Inet.v6.sin6_port));
828}
829
830/******************************************************************************/
831/* P r o t o I D */
832/******************************************************************************/
833
834#ifndef IPPROTO_TCP
835#define IPPROTO_TCP 6
836#endif
837
838int XrdNetUtils::ProtoID(const char *pname)
839{
840#ifdef HAVE_PROTOR
841 struct protoent pp;
842 char buff[1024];
843#else
844 static XrdSysMutex protomutex;
845 struct protoent *pp;
846 int protoid;
847#endif
848
849// Note that POSIX did include getprotobyname_r() in the last minute. Many
850// platforms do not document this variant but just about all include it.
851//
852#ifdef __solaris__
853 if (!getprotobyname_r(pname, &pp, buff, sizeof(buff)))
854 return IPPROTO_TCP;
855 return pp.p_proto;
856#elif !defined(HAVE_PROTOR)
857 protomutex.Lock();
858 if (!(pp = getprotobyname(pname))) protoid = IPPROTO_TCP;
859 else protoid = pp->p_proto;
860 protomutex.UnLock();
861 return protoid;
862#else
863 struct protoent *ppp;
864 if (getprotobyname_r(pname, &pp, buff, sizeof(buff), &ppp))
865 return IPPROTO_TCP;
866 return pp.p_proto;
867#endif
868}
869
870/******************************************************************************/
871/* S e r v P o r t */
872/******************************************************************************/
873
874int XrdNetUtils::ServPort(const char *sName, bool isUDP, const char **eText)
875{
876 struct addrinfo *rP = 0, myHints;
877 int rc, portnum = 0;
878
879// First check if this is a plain number
880//
881 if (isdigit(*sName))
882 {char *send;
883 portnum = strtol(sName, &send, 10);
884 if (portnum > 0 && portnum < 65536 && *send == 0) return portnum;
885 if (eText) *eText = "invalid port number";
886 return 0;
887 }
888
889// Fill out the hints
890//
891 memset(&myHints, 0, sizeof(myHints));
892 myHints.ai_socktype = (isUDP ? SOCK_DGRAM : SOCK_STREAM);
893
894// Try to find the port number
895//
896 rc = getaddrinfo(0, sName, &myHints, &rP);
897 if (rc || !rP)
898 {if (eText) *eText = (rc ? gai_strerror(rc) : "service not found");
899 if (rP) freeaddrinfo(rP);
900 return 0;
901 }
902
903// Return the port number
904//
905 portnum = int(ntohs(SIN_PORT(rP))) & 0x0000ffff;
906 freeaddrinfo(rP);
907 if (!portnum && eText) *eText = "service has no port";
908 return portnum;
909}
910
911/******************************************************************************/
912/* S e t A u t o */
913/******************************************************************************/
914
916{
917
918// If a specific family is not specified, then determine which families to use
919//
920 if (aOpts != onlyIPv4 && aOpts != allIPMap)
921 {int ifTypes = XrdNetIF::GetIF(0);
922 if (ifTypes & XrdNetIF::haveIPv6) aOpts = allIPMap;
923 else if (ifTypes & XrdNetIF::haveIPv4) aOpts = onlyIPv4;
924 else {autoFamily = AF_UNSPEC; autoHints = AI_V4MAPPED | AI_ADDRCONFIG;
925 return AI_V4MAPPED | AI_ADDRCONFIG;
926 }
927 }
928
929// If this is forced IPv4 then we know how to set the hints
930//
931 if (aOpts == onlyIPv4)
932 {autoFamily = AF_INET; autoHints = 0; return 0;}
933
934// So, this is IPv6. Be as flexible as possible.
935//
936 autoFamily = AF_INET6;
937 autoHints = AI_V4MAPPED | AI_ALL;
938 return AI_V4MAPPED | AI_ALL;
939}
940
941/******************************************************************************/
942/* Private: s e t E T */
943/******************************************************************************/
944
945int XrdNetUtils::setET(const char **errtxt, int rc)
946{
947 if (rc) *errtxt = XrdSysE2T(rc);
948 else *errtxt = "unexpected error";
949 return 0;
950}
951
952/******************************************************************************/
953/* S i n g l e t o n */
954/******************************************************************************/
955
956bool XrdNetUtils::Singleton(const char *hSpec, const char **eText)
957{
958 XrdOucTList *hList, *hNow;
959 bool isSingle;
960
961// Obtain a list of unique hostnames associated with this host
962//
963 hList = Hosts(hSpec, 1234, 2, 0, eText);
964
965// If this is none or only one then this is a singleton
966//
967 isSingle = !hList || hList->next == 0;
968
969// Free up the list of hosts
970//
971 while((hNow = hList))
972 {hList = hList->next;
973 delete hNow;
974 };
975
976// All done
977//
978 return isSingle;
979}
980
981bool XrdNetUtils::ConnectWithTimeout(int sockfd, const struct sockaddr* clientAddr,size_t clientAddrLen, uint32_t timeout_sec, std::stringstream &errMsg) {
982
983 if (!SetSockBlocking(sockfd, false, errMsg)) { // Set to non-blocking
984 close(sockfd);
985 return false;
986 }
987
988 int result = connect(sockfd, clientAddr, clientAddrLen);
989 if (result == 0) {
990 //We've managed to connect immediately
991 // Set the socket back to blocking
992 if(!SetSockBlocking(sockfd, true, errMsg)) {
993 ::close(sockfd);
994 return false;
995 }
996 return true;
997 } else if (errno != EINPROGRESS) {
998 errMsg << "Connection failed: " << strerror(errno);
999 ::close(sockfd);
1000 return false;
1001 }
1002
1003 struct pollfd fds;
1004 fds.fd = sockfd;
1005 fds.events = POLLOUT; // Wait for the socket to be ready to write
1006
1007 result = poll(&fds, 1, timeout_sec * 1000); //Timeout in milliseconds
1008
1009 if (result < 0) {
1010 errMsg << "Poll error: " << strerror(errno);
1011 ::close(sockfd);
1012 return false;
1013 } else if (result == 0) {
1014 errMsg << "Connection timed out";
1015 ::close(sockfd);
1016 return false;
1017 }
1018 // Polling succeeded
1019 if (!(fds.revents & POLLOUT)) {
1020 // We should normally not reach this code
1021 errMsg << "Poll returned without error but the corresponding socket (" << sockfd << ") is not ready to write";
1022 ::close(sockfd);
1023 return false;
1024 }
1025 // Check for potential errors on the socket after polling
1026 int so_error;
1027 socklen_t len = sizeof(so_error);
1028 getsockopt(sockfd, SOL_SOCKET, SO_ERROR, &so_error, &len);
1029 if (so_error != 0) {
1030 errMsg << "Connection failed after poll: " << strerror(so_error);
1031 ::close(sockfd);
1032 return false;
1033 }
1034 // Everything succeeded, set the socket back to blocking
1035 if(!SetSockBlocking(sockfd, true, errMsg)) {
1036 ::close(sockfd);
1037 return false;
1038 }
1039 return true;
1040}
1041
1042bool XrdNetUtils::SetSockBlocking(int sockfd, bool blocking, std::stringstream & errMsg) {
1043 int flags = fcntl(sockfd, F_GETFL, 0);
1044 if (flags == -1) {
1045 errMsg << "Failed to get socket flags " << strerror(errno);
1046 return false;
1047 }
1048
1049 flags = blocking ? (flags & ~O_NONBLOCK) : (flags | O_NONBLOCK);
1050
1051 if (fcntl(sockfd, F_SETFL, flags) == -1) {
1052 errMsg << "Failed to set socket blocking/non-blocking " << strerror(errno);
1053 return false;
1054 }
1055
1056 return true;
1057}
struct sockaddr_in6 v6
struct sockaddr Addr
struct sockaddr_in v4
#define SIN_PORT(x)
#define IPPROTO_TCP
#define OrderXX
#define close(a)
Definition XrdPosix.hh:48
#define eMsg(x)
struct myOpts opts
const char * XrdSysE2T(int errcode)
Definition XrdSysE2T.cc:104
size_t strlcpy(char *dst, const char *src, size_t sz)
#define SOCKLEN_t
static const int noPort
Do not add port number.
static const int old6Map4
Use deprecated IPV6 mapped format.
bool isMapped() const
static bool isHostName(const char *name)
static const int noPortRaw
Use raw address format (no port)
static const int prefipv4
Use if mapped IPV4 actual format.
bool isIPType(IPType ipType) const
int Format(char *bAddr, int bLen, fmtUse fmtType=fmtAuto, int fmtOpts=0)
@ fmtAddr
Address using suitable ipv4 or ipv6 format.
const char * Name(const char *eName=0, const char **eText=0)
int Port(int pNum=-1)
static const int PortInSpec
const char * Set(const char *hSpec, int pNum=PortInSpec)
static const int haveIPv4
ifList == 0 && non-local ipv4 i/f found (or'd)
Definition XrdNetIF.hh:150
static int GetIF(XrdOucTList **ifList, const char **eText=0)
Definition XrdNetIF.cc:413
static const int havePub6
ifList == 0 && public ipv6 i/f found (or'd)
Definition XrdNetIF.hh:160
static const int havePub4
ifList == 0 && public ipv4 i/f found (or'd)
Definition XrdNetIF.hh:158
static const int haveIPv6
ifList == 0 && non-local ipv6 i/f found (or'd)
Definition XrdNetIF.hh:152
static const char * FQN(const char **etext=0)
static const char * GetAddrs(const std::string &hSpec, std::vector< XrdNetAddr > &aVec, int *ordn=0, XrdNetUtils::AddrOpts opts=XrdNetUtils::allIPMap, int pNum=XrdNetUtils::PortInSpec)
static const char pfx
Registry names must start with this character.
static const int NoPortRaw
static bool Match(const char *hName, const char *pattern)
static IPComp Compare(XrdNetSockAddr &ip1, XrdNetSockAddr &ip2, bool *psame=0)
@ qryINET
Only consider internet protocols via DNS.
@ qryINIF
Only consider internet protocols via ifconfig.
static char * MyHostName(const char *eName="*unknown*", const char **eText=0)
static int GetSokInfo(int fd, char *theAddr, int theALen, char &theType)
static int Encode(const XrdNetSockAddr *sadr, char *buff, int blen, int port=-1)
@ hasNone
Unable to determine available protocols.
static int IPFormat(const struct sockaddr *sAddr, char *bP, int bL, int opts=0)
static bool Singleton(const char *hSpec, const char **eText=0)
static const char * GetAddrs(const char *hSpec, XrdNetAddr *aListP[], int &aListN, AddrOpts opts=allIPMap, int pNum=PortInSpec)
static int Decode(XrdNetSockAddr *sadr, const char *buff, int blen)
static NetProt NetConfig(NetType netquery=qryINET, const char **eText=0)
static const int PortInSpec
static bool ConnectWithTimeout(int sockfd, const struct sockaddr *clientAddr, size_t clientAddrLen, uint32_t timeout_sec, std::stringstream &errMsg)
static int Port(int fd, const char **eText=0)
static const int oldFmt
static int ProtoID(const char *pName)
static int ServPort(const char *sName, bool isUDP=false, const char **eText=0)
static XrdOucTList * Hosts(const char *hSpec, int hPort=-1, int hWant=8, int *sPort=0, const char **eText=0)
static const int noPort
static bool Parse(const char *hSpec, const char **hName, const char **hNend, const char **hPort, const char **hPend)
static int SetAuto(AddrOpts aOpts=allIPMap)
XrdOucTList * next
static void toLower(char *str)
const char * ipAddr
hpSpec(XrdNetUtils::AddrOpts opts)