XRootD
Loading...
Searching...
No Matches
XrdNetAddrInfo.cc
Go to the documentation of this file.
1/******************************************************************************/
2/* */
3/* X r d N e t A d d r I n f o . c c */
4/* */
5/* (c) 2013 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 <cerrno>
33#include <netdb.h>
34#include <cstdio>
35#include <arpa/inet.h>
36#include <sys/types.h>
37
39#include "XrdNet/XrdNetCache.hh"
40
41/******************************************************************************/
42/* P l a t f o r m D e p e n d e n c i e s */
43/******************************************************************************/
44
45// Linux defines s6_addr32 but MacOS does not and Solaris defines it only when
46// compiling the kernel. This is really standard stuff that should be here.
47//
48#ifndef s6_addr32
49#if defined(__solaris__)
50#define s6_addr32 _S6_un._S6_u32
51#elif defined(__APPLE__) || defined(__FreeBSD__)
52#define s6_addr32 __u6_addr.__u6_addr32
53#endif
54#endif
55
56// The following tests for Unique Local Addresses (ULA) which Linux does not
57// provide. The SITELOCAL macro only tests for the now deprecated non-routable
58// addresses (RFC 3879). So, we need to implement the ULA test ourselves.
59// Technically, only addresses starting with prefix 0xfd are ULA useable but
60// RFC 4193 doesn't explicitly prohibit ULA's that start with 0xfc which may
61// be used for registered ULA's in the future. So we test for both.
62//
63#define IN6_IS_ADDR_UNIQLOCAL(a) \
64 ( ((const uint8_t *)(a))[0] == 0xfc || ((const uint8_t *)(a))[0] == 0xfd )
65
66/******************************************************************************/
67/* S t a t i c M e m b e r s */
68/******************************************************************************/
69
71
72namespace
73{
74 static const char lbVal[13] ={0,0,0,0,0,0,0,0,0,0,0,0,0x7f};
75};
76
77/******************************************************************************/
78/* F o r m a t */
79/******************************************************************************/
80
81int XrdNetAddrInfo::Format(char *bAddr, int bLen, fmtUse theFmt, int fmtOpts)
82{
83 const char *pFmt = "]:%d";
84 int totLen, n, pNum, addBrak = 0;
85 int omitP = (fmtOpts & (noPort|noPortRaw));
86 int ipRaw = (fmtOpts & noPortRaw);
87 int ipOld = fmtOpts & (old6Map4 | prefipv4);
88
89// Handle the degenerative case first
90//
91 if (IP.Addr.sa_family == AF_UNIX)
92 {n = (omitP ? snprintf(bAddr, bLen, "localhost")
93 : snprintf(bAddr, bLen, "localhost:%s", unixPipe->sun_path));
94 return (n < bLen ? n : QFill(bAddr, bLen));
95 }
96
97// Grab the potr. The port number is the same position and same size regardless
98// of address type.
99//
100 pNum = ntohs(IP.v4.sin_port);
101
102// Resolve address if need be and return result if possible
103//
104 if (theFmt == fmtName || theFmt == fmtAuto)
105 {if (!hostName && dnsCache && !(hostName = dnsCache->Find(this))
106 && theFmt == fmtName) Resolve();
107 if (hostName)
108 {n = (omitP ? snprintf(bAddr, bLen, "%s", hostName)
109 : snprintf(bAddr, bLen, "%s:%d", hostName, pNum));
110 return (n < bLen ? n : QFill(bAddr, bLen));
111 }
112 theFmt = fmtAddr;
113 }
114
115// Check if we can now produce an address format quickly. We used assume that
116// the hostname was an address if it started with a non-alpha. But this does
117// not correspond to RFC 1178 and RFC 3696, among others. We now only use
118// the optimization path if the name is actually an IPV6/mapped4 address.
119//
120 if (hostName && *hostName == '[' && !ipOld)
121 {n = (omitP ? snprintf(bAddr, bLen, "%s", hostName)
122 : snprintf(bAddr, bLen, "%s:%d", hostName, pNum));
123 return (n < bLen ? n : QFill(bAddr, bLen));
124 }
125
126// Format address
127//
128 if (IP.Addr.sa_family == AF_INET6)
129 {if (bLen < (INET6_ADDRSTRLEN+2)) return QFill(bAddr, bLen);
130 if (ipOld && IN6_IS_ADDR_V4MAPPED(&IP.v6.sin6_addr))
131 { if (fmtOpts & prefipv4) {n = 0; pFmt = ":%d";}
132 else if (ipRaw) {strcpy(bAddr, "::"); n = 2;}
133 else {strcpy(bAddr, "[::"); n = 3; addBrak=1;}
134 if (!inet_ntop(AF_INET, &IP.v6.sin6_addr.s6_addr32[3],
135 bAddr+n, bLen-n)) return QFill(bAddr, bLen);
136 } else {
137 if (!ipRaw) {*bAddr = '['; n = 1; addBrak = 1;}
138 else n = 0;
139 if (!inet_ntop(AF_INET6,&(IP.v6.sin6_addr),bAddr+n,bLen-n))
140 return QFill(bAddr, bLen);
141 }
142 }
143 else if (IP.Addr.sa_family == AF_INET)
144 {if (theFmt != fmtAdv6) {n = 0; pFmt = ":%d";}
145 else {if (bLen < (INET_ADDRSTRLEN+9)) return QFill(bAddr, bLen);
146 if (fmtOpts & old6Map4) {strcpy(bAddr, "[::"); n = 3;}
147 else {strcpy(bAddr, "[::ffff:"); n = 8;}
148 if (ipRaw) {strcpy(bAddr, bAddr+1); n--;}
149 addBrak = 1;
150 }
151 if (!inet_ntop(AF_INET, &(IP.v4.sin_addr),bAddr+n,bLen-n))
152 return QFill(bAddr, bLen);
153 }
154 else return QFill(bAddr, bLen);
155
156// Recalculate buffer position and length
157//
158 totLen = strlen(bAddr); bAddr += totLen; bLen -= totLen;
159
160// Process when no port number wanted
161//
162 if (omitP)
163 {if (addBrak)
164 {if (bLen < 2) return QFill(bAddr, bLen);
165 *bAddr++ = ']'; *bAddr = 0; totLen++;
166 }
167 return totLen;
168 }
169
170// Add the port number and return result
171//
172 if ((n = snprintf(bAddr, bLen, pFmt, pNum)) >= bLen)
173 return QFill(bAddr, bLen);
174 return totLen+n;
175}
176
177/******************************************************************************/
178/* i s L o c a l */
179/******************************************************************************/
180
182{
183 unsigned char *ipV4 = nullptr;
184
185// For IPv6, return true for link-local (fe80::/10) and loopback (::1)
186//
187 if (IP.Addr.sa_family == AF_INET6)
188 {if ((IN6_IS_ADDR_V4MAPPED(&IP.v6.sin6_addr)))
189 ipV4 = (unsigned char *)&IP.v6.sin6_addr.s6_addr32[3];
190 else {if ((IN6_IS_ADDR_LINKLOCAL(&IP.v6.sin6_addr))
191 || (IN6_IS_ADDR_LOOPBACK (&IP.v6.sin6_addr))) return true;
192 return false;
193 }
194 }
195
196// If this is not an IPv4 address, consider it local (e.g. AF_UNIX)
197//
198 if (!ipV4)
199 {if (IP.Addr.sa_family != AF_INET) return true;
200 ipV4 = (unsigned char *)&IP.v4.sin_addr.s_addr;
201 }
202
203// For IPV4, return true for link-local (169.254.0.0/16) and loopback (127.0.0.0/8)
204//
205 if ((ipV4[0] == 169 && ipV4[1] == 254) || ipV4[0] == 127)
206 return true;
207
208// Not a local address
209//
210 return false;
211}
212
213/******************************************************************************/
214/* i s L o o p b a c k */
215/******************************************************************************/
216
218{
219
220// Check for loopback address
221//
222 if (IP.Addr.sa_family == AF_INET)
223 return !memcmp(&IP.v4.sin_addr.s_addr, &lbVal[12], 1);
224
225 if (IP.Addr.sa_family == AF_INET6)
226 return !memcmp(&IP.v6.sin6_addr, &in6addr_loopback, sizeof(in6_addr))
227 || !memcmp(&IP.v6.sin6_addr, lbVal, sizeof(lbVal));
228
229 return false;
230}
231
232/******************************************************************************/
233/* i s P r i v a t e */
234/******************************************************************************/
235
237{
238 unsigned char *ipV4 = 0;
239
240// For IPV6 addresses we will use the macro unless it is mapped
241//
242 if (IP.Addr.sa_family == AF_INET6)
243 {if ((IN6_IS_ADDR_V4MAPPED(&IP.v6.sin6_addr)))
244 ipV4 = (unsigned char *)&IP.v6.sin6_addr.s6_addr32[3];
245 else {if ((IN6_IS_ADDR_LINKLOCAL(&IP.v6.sin6_addr))
246 || (IN6_IS_ADDR_SITELOCAL(&IP.v6.sin6_addr))
247 || (IN6_IS_ADDR_UNIQLOCAL(&IP.v6.sin6_addr))
248 || (IN6_IS_ADDR_LOOPBACK (&IP.v6.sin6_addr))) return true;
249 return false;
250 }
251 }
252
253// If this is not an IPV4 address then we will consider it private
254//
255 if (!ipV4)
256 {if (IP.Addr.sa_family != AF_INET) return true;
257 ipV4 = (unsigned char *)&IP.v4.sin_addr.s_addr;
258 }
259
260// For IPV4 we use the RFC definition of private. Note that this includes
261// mapped addresses which, as odd as it is, we could get.
262//
263 if (ipV4[0] == 10
264 || (ipV4[0] == 172 && ipV4[1] >= 16 && ipV4[1] <= 31)
265 || (ipV4[0] == 192 && ipV4[1] == 168)
266 || (ipV4[0] == 169 && ipV4[1] == 254)
267 || ipV4[0] == 127) return true;
268
269// Not a local address
270//
271 return false;
272}
273
274/******************************************************************************/
275/* i s R e g i s t e r e d */
276/******************************************************************************/
277
279{
280 const char *hName;
281
282// Simply see if we can resolve this name
283//
284 if (!(hName = Name())) return false;
285 return isHostName(hName);
286}
287
288/******************************************************************************/
289/* i s U s i n g T L S */
290/******************************************************************************/
291
293{
294 return (protFlgs & isTLS) != 0;
295}
296
297/******************************************************************************/
298/* Private: L o w C a s e */
299/******************************************************************************/
300
302{
303 unsigned char *sp = (unsigned char*)str;
304
305 while(*sp) {if (isupper((int)*sp)) *sp = (char)tolower((int)*sp); sp++;}
306
307 return str;
308}
309
310/******************************************************************************/
311/* i s H o s t N a m e */
312/******************************************************************************/
313
314bool XrdNetAddrInfo::isHostName(const char *name)
315{
316 const char *dot;
317 int dnum;
318
319// First check for Iv6 format or hostname
320//
321 if (*name == '[') return false;
322 if (!isdigit(*name)) return true;
323
324// The IPv4 case is more complicated. The fastest way here is this is a
325// host name if there are no dots or if the last component is not a digit
326// according to the RFC spec.
327//
328 if (!(dot = rindex(name, '.')) || !isdigit(*(dot+1))) return true;
329
330// We are not out of the woods yet. Now we need to do a full check.
331//
332 name++; dnum = 0;
333 while(*name)
334 {if (*name == '.') dnum++;
335 else if (!isdigit(*name)) return true;
336 name++;
337 }
338 return (dnum == 3 ? false : true);
339}
340
341/******************************************************************************/
342/* N a m e */
343/******************************************************************************/
344
345const char *XrdNetAddrInfo::Name(const char *eName, const char **eText)
346{
347 int rc;
348
349// Preset errtxt to zero
350//
351 if (eText) *eText = 0;
352
353// Check for unix family which is equal to localhost.
354//
355 if (IP.Addr.sa_family == AF_UNIX) return "localhost";
356
357// If we already translated this name, just return the translation
358//
359 if (hostName || (dnsCache && (hostName = dnsCache->Find(this))))
360 return hostName;
361
362// Try to resolve this address
363//
364 if (!(rc = Resolve())) return hostName;
365
366// We failed resolving this address
367//
368 if (eText) *eText = gai_strerror(rc);
369 return eName;
370}
371
372/******************************************************************************/
373/* P o r t */
374/******************************************************************************/
375
377{
378// Make sure we have a proper address family here
379//
380 if (IP.Addr.sa_family != AF_INET && IP.Addr.sa_family != AF_INET6)
381 return -1;
382
383// Return port number
384//
385 return ntohs(IP.v6.sin6_port);
386}
387
388/******************************************************************************/
389/* Private: Q F i l l */
390/******************************************************************************/
391
392int XrdNetAddrInfo::QFill(char *bAddr, int bLen)
393{
394 static const char quests[] = "????????";
395
396// Insert up to 8 question marks
397//
398 if (bLen)
399 {strncpy(bAddr, quests, bLen);
400 bAddr[bLen-1] = 0;
401 }
402 return 0;
403}
404
405/******************************************************************************/
406/* Private: R e s o l v e */
407/******************************************************************************/
408
410{
411 char hBuff[NI_MAXHOST];
412 int n, rc;
413
414// Free up hostname here
415//
416 if (hostName) {free(hostName); hostName = 0;}
417
418// Determine the actual size of the address structure
419//
420 if (IP.Addr.sa_family == AF_INET ) n = sizeof(IP.v4);
421 else if (IP.Addr.sa_family == AF_INET6) n = sizeof(IP.v6);
422 else if (IP.Addr.sa_family == AF_UNIX )
423 {hostName = strdup("localhost");
424 return 0;
425 }
426 else return EAI_FAMILY;
427
428// Do lookup of canonical name. If an error is returned we simply assume that
429// the name is not resolvable and return the address as the host name.
430//
431 if ((rc = getnameinfo(&IP.Addr, n, hBuff+1, sizeof(hBuff)-2, 0, 0, 0)))
432 {int ec = errno;
433 if (Format(hBuff, sizeof(hBuff), fmtAddr, noPort))
434 {hostName = strdup(hBuff); return 0;}
435 errno = ec;
436 return rc;
437 }
438
439// Handle the case when the mapping returned an actual name or an address
440// We always want numeric ipv6 addresses surrounded by brackets. Additionally,
441// some implementations of getnameinfo() return the scopeid when a numeric
442// address is returned. We check and remove it.
443//
444 if (!index(hBuff+1, ':')) hostName = strdup(LowCase(hBuff+1));
445 else {char *perCent = index(hBuff+1, '%');
446 if (perCent) *perCent = 0;
447 n = strlen(hBuff+1);
448 hBuff[0] = '['; hBuff[n+1] = ']'; hBuff[n+2] = 0;
449 hostName = strdup(hBuff);
450 }
451
452// Add the entry to the cache and return success
453//
454 if (dnsCache) dnsCache->Add(this, hostName);
455 return 0;
456}
457
458/******************************************************************************/
459/* S a m e */
460/******************************************************************************/
461
462#define IS_INET(x) (x == AF_INET || x == AF_INET6)
463
464#define MY_FAMILY IP.Addr.sa_family
465
466#define UR_FAMILY ipAddr->IP.Addr.sa_family
467
468int XrdNetAddrInfo::Same(const XrdNetAddrInfo *ipAddr, bool plusPort)
469{
470 static const int ipv4ASZ = sizeof(IP.v4.sin_addr);
471
472 bool isINet = IS_INET(MY_FAMILY) && IS_INET(UR_FAMILY);
473
474// Do port comparison if requested and makes sense to do it
475//
476 if (plusPort && isINet)
477 {in_port_t port1, port2;
478 port1 = (MY_FAMILY == AF_INET ? IP.v4.sin_port : IP.v6.sin6_port);
479 port2 = (UR_FAMILY == AF_INET ? ipAddr->IP.v4.sin_port
480 : ipAddr->IP.v6.sin6_port);
481 if (port1 != port2) return 0;
482 }
483
484// If address families do not match, they are the same if the hostnames match.
485// If we don't have a hostname and the addresses are convertable, we can
486// compare the actual addresses.
487//
488 if (MY_FAMILY != UR_FAMILY)
489 {if (!isINet) return 0;
490 if (hostName && ipAddr->hostName)
491 return !strcmp(hostName,ipAddr->hostName);
492 if (MY_FAMILY == AF_INET && ipAddr->isMapped())
493 return !memcmp(&IP.v4.sin_addr,
494 &(ipAddr->IP.v6.sin6_addr.s6_addr32[3]), ipv4ASZ);
495 if (isMapped() && UR_FAMILY == AF_INET)
496 return !memcmp(&IP.v6.sin6_addr.s6_addr32[3],
497 &(ipAddr->IP.v4.sin_addr), ipv4ASZ);
498 return 0;
499 }
500
501// Now process to do the match
502//
503 if (MY_FAMILY == AF_INET)
504 return !memcmp(&IP.v4.sin_addr, &(ipAddr->IP.v4.sin_addr), ipv4ASZ);
505 else if (MY_FAMILY == AF_INET6)
506 return !memcmp(&IP.v6.sin6_addr, &(ipAddr->IP.v6.sin6_addr),
507 sizeof(IP.v6.sin6_addr));
508 else if (MY_FAMILY == AF_UNIX)
509 return !strcmp(unixPipe->sun_path, ipAddr->unixPipe->sun_path);
510
511 return 0;
512}
#define UR_FAMILY
#define IS_INET(x)
#define MY_FAMILY
#define IN6_IS_ADDR_UNIQLOCAL(a)
struct sockaddr_in6 v6
struct sockaddr_in v4
static XrdNetCache * dnsCache
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)
unsigned char protFlgs
XrdNetSockAddr IP
static const int noPortRaw
Use raw address format (no port)
int Same(const XrdNetAddrInfo *ipAddr, bool plusPort=false)
static const int prefipv4
Use if mapped IPV4 actual format.
int Format(char *bAddr, int bLen, fmtUse fmtType=fmtAuto, int fmtOpts=0)
XrdNetAddrInfo(XrdNetAddrInfo const &oP)
Copy constructor.
char * LowCase(char *str)
int QFill(char *bAddr, int bLen)
@ fmtAddr
Address using suitable ipv4 or ipv6 format.
@ fmtName
Hostname if it is resolvable o/w use fmtAddr.
@ fmtAuto
Hostname if already resolved o/w use fmtAddr.
static const char isTLS
Location using TLS.
const char * Name(const char *eName=0, const char **eText=0)