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 o p b a c k */
179/******************************************************************************/
180
182{
183
184// Check for loopback address
185//
186 if (IP.Addr.sa_family == AF_INET)
187 return !memcmp(&IP.v4.sin_addr.s_addr, &lbVal[12], 1);
188
189 if (IP.Addr.sa_family == AF_INET6)
190 return !memcmp(&IP.v6.sin6_addr, &in6addr_loopback, sizeof(in6_addr))
191 || !memcmp(&IP.v6.sin6_addr, lbVal, sizeof(lbVal));
192
193 return false;
194}
195
196/******************************************************************************/
197/* i s P r i v a t e */
198/******************************************************************************/
199
201{
202 unsigned char *ipV4 = 0;
203
204// For IPV6 addresses we will use the macro unless it is mapped
205//
206 if (IP.Addr.sa_family == AF_INET6)
207 {if ((IN6_IS_ADDR_V4MAPPED(&IP.v6.sin6_addr)))
208 ipV4 = (unsigned char *)&IP.v6.sin6_addr.s6_addr32[3];
209 else {if ((IN6_IS_ADDR_LINKLOCAL(&IP.v6.sin6_addr))
210 || (IN6_IS_ADDR_SITELOCAL(&IP.v6.sin6_addr))
211 || (IN6_IS_ADDR_UNIQLOCAL(&IP.v6.sin6_addr))
212 || (IN6_IS_ADDR_LOOPBACK (&IP.v6.sin6_addr))) return true;
213 return false;
214 }
215 }
216
217// If this is not an IPV4 address then we will consider it private
218//
219 if (!ipV4)
220 {if (IP.Addr.sa_family != AF_INET) return true;
221 ipV4 = (unsigned char *)&IP.v4.sin_addr.s_addr;
222 }
223
224// For IPV4 we use the RFC definition of private. Note that this includes
225// mapped addresses which, as odd as it is, we could get.
226//
227 if (ipV4[0] == 10
228 || (ipV4[0] == 172 && ipV4[1] >= 16 && ipV4[1] <= 31)
229 || (ipV4[0] == 192 && ipV4[1] == 168)
230 || (ipV4[0] == 169 && ipV4[1] == 254)
231 || ipV4[0] == 127) return true;
232
233// Not a local address
234//
235 return false;
236}
237
238/******************************************************************************/
239/* i s R e g i s t e r e d */
240/******************************************************************************/
241
243{
244 const char *hName;
245
246// Simply see if we can resolve this name
247//
248 if (!(hName = Name())) return false;
249 return isHostName(hName);
250}
251
252/******************************************************************************/
253/* i s U s i n g T L S */
254/******************************************************************************/
255
257{
258 return (protFlgs & isTLS) != 0;
259}
260
261/******************************************************************************/
262/* Private: L o w C a s e */
263/******************************************************************************/
264
266{
267 unsigned char *sp = (unsigned char*)str;
268
269 while(*sp) {if (isupper((int)*sp)) *sp = (char)tolower((int)*sp); sp++;}
270
271 return str;
272}
273
274/******************************************************************************/
275/* i s H o s t N a m e */
276/******************************************************************************/
277
278bool XrdNetAddrInfo::isHostName(const char *name)
279{
280 const char *dot;
281 int dnum;
282
283// First check for Iv6 format or hostname
284//
285 if (*name == '[') return false;
286 if (!isdigit(*name)) return true;
287
288// The IPv4 case is more complicated. The fastest way here is this is a
289// host name if there are no dots or if the last component is not a digit
290// according to the RFC spec.
291//
292 if (!(dot = rindex(name, '.')) || !isdigit(*(dot+1))) return true;
293
294// We are not out of the woods yet. Now we need to do a full check.
295//
296 name++; dnum = 0;
297 while(*name)
298 {if (*name == '.') dnum++;
299 else if (!isdigit(*name)) return true;
300 name++;
301 }
302 return (dnum == 3 ? false : true);
303}
304
305/******************************************************************************/
306/* N a m e */
307/******************************************************************************/
308
309const char *XrdNetAddrInfo::Name(const char *eName, const char **eText)
310{
311 int rc;
312
313// Preset errtxt to zero
314//
315 if (eText) *eText = 0;
316
317// Check for unix family which is equal to localhost.
318//
319 if (IP.Addr.sa_family == AF_UNIX) return "localhost";
320
321// If we already translated this name, just return the translation
322//
323 if (hostName || (dnsCache && (hostName = dnsCache->Find(this))))
324 return hostName;
325
326// Try to resolve this address
327//
328 if (!(rc = Resolve())) return hostName;
329
330// We failed resolving this address
331//
332 if (eText) *eText = gai_strerror(rc);
333 return eName;
334}
335
336/******************************************************************************/
337/* P o r t */
338/******************************************************************************/
339
341{
342// Make sure we have a proper address family here
343//
344 if (IP.Addr.sa_family != AF_INET && IP.Addr.sa_family != AF_INET6)
345 return -1;
346
347// Return port number
348//
349 return ntohs(IP.v6.sin6_port);
350}
351
352/******************************************************************************/
353/* Private: Q F i l l */
354/******************************************************************************/
355
356int XrdNetAddrInfo::QFill(char *bAddr, int bLen)
357{
358 static const char quests[] = "????????";
359
360// Insert up to 8 question marks
361//
362 if (bLen)
363 {strncpy(bAddr, quests, bLen);
364 bAddr[bLen-1] = 0;
365 }
366 return 0;
367}
368
369/******************************************************************************/
370/* Private: R e s o l v e */
371/******************************************************************************/
372
374{
375 char hBuff[NI_MAXHOST];
376 int n, rc;
377
378// Free up hostname here
379//
380 if (hostName) {free(hostName); hostName = 0;}
381
382// Determine the actual size of the address structure
383//
384 if (IP.Addr.sa_family == AF_INET ) n = sizeof(IP.v4);
385 else if (IP.Addr.sa_family == AF_INET6) n = sizeof(IP.v6);
386 else if (IP.Addr.sa_family == AF_UNIX )
387 {hostName = strdup("localhost");
388 return 0;
389 }
390 else return EAI_FAMILY;
391
392// Do lookup of canonical name. If an error is returned we simply assume that
393// the name is not resolvable and return the address as the host name.
394//
395 if ((rc = getnameinfo(&IP.Addr, n, hBuff+1, sizeof(hBuff)-2, 0, 0, 0)))
396 {int ec = errno;
397 if (Format(hBuff, sizeof(hBuff), fmtAddr, noPort))
398 {hostName = strdup(hBuff); return 0;}
399 errno = ec;
400 return rc;
401 }
402
403// Handle the case when the mapping returned an actual name or an address
404// We always want numeric ipv6 addresses surrounded by brackets. Additionally,
405// some implementations of getnameinfo() return the scopeid when a numeric
406// address is returned. We check and remove it.
407//
408 if (!index(hBuff+1, ':')) hostName = strdup(LowCase(hBuff+1));
409 else {char *perCent = index(hBuff+1, '%');
410 if (perCent) *perCent = 0;
411 n = strlen(hBuff+1);
412 hBuff[0] = '['; hBuff[n+1] = ']'; hBuff[n+2] = 0;
413 hostName = strdup(hBuff);
414 }
415
416// Add the entry to the cache and return success
417//
418 if (dnsCache) dnsCache->Add(this, hostName);
419 return 0;
420}
421
422/******************************************************************************/
423/* S a m e */
424/******************************************************************************/
425
426#define IS_INET(x) (x == AF_INET || x == AF_INET6)
427
428#define MY_FAMILY IP.Addr.sa_family
429
430#define UR_FAMILY ipAddr->IP.Addr.sa_family
431
432int XrdNetAddrInfo::Same(const XrdNetAddrInfo *ipAddr, bool plusPort)
433{
434 static const int ipv4ASZ = sizeof(IP.v4.sin_addr);
435
436 bool isINet = IS_INET(MY_FAMILY) && IS_INET(UR_FAMILY);
437
438// Do port comparison if requested and makes sense to do it
439//
440 if (plusPort && isINet)
441 {in_port_t port1, port2;
442 port1 = (MY_FAMILY == AF_INET ? IP.v4.sin_port : IP.v6.sin6_port);
443 port2 = (UR_FAMILY == AF_INET ? ipAddr->IP.v4.sin_port
444 : ipAddr->IP.v6.sin6_port);
445 if (port1 != port2) return 0;
446 }
447
448// If address families do not match, they are the same if the hostnames match.
449// If we don't have a hostname and the addresses are convertable, we can
450// compare the actual addresses.
451//
452 if (MY_FAMILY != UR_FAMILY)
453 {if (!isINet) return 0;
454 if (hostName && ipAddr->hostName)
455 return !strcmp(hostName,ipAddr->hostName);
456 if (MY_FAMILY == AF_INET && ipAddr->isMapped())
457 return !memcmp(&IP.v4.sin_addr,
458 &(ipAddr->IP.v6.sin6_addr.s6_addr32[3]), ipv4ASZ);
459 if (isMapped() && UR_FAMILY == AF_INET)
460 return !memcmp(&IP.v6.sin6_addr.s6_addr32[3],
461 &(ipAddr->IP.v4.sin_addr), ipv4ASZ);
462 return 0;
463 }
464
465// Now process to do the match
466//
467 if (MY_FAMILY == AF_INET)
468 return !memcmp(&IP.v4.sin_addr, &(ipAddr->IP.v4.sin_addr), ipv4ASZ);
469 else if (MY_FAMILY == AF_INET6)
470 return !memcmp(&IP.v6.sin6_addr, &(ipAddr->IP.v6.sin6_addr),
471 sizeof(IP.v6.sin6_addr));
472 else if (MY_FAMILY == AF_UNIX)
473 return !strcmp(unixPipe->sun_path, ipAddr->unixPipe->sun_path);
474
475 return 0;
476}
#define UR_FAMILY
#define IS_INET(x)
#define MY_FAMILY
#define IN6_IS_ADDR_UNIQLOCAL(a)
struct sockaddr_in6 v6
struct sockaddr Addr
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)
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)
void Add(XrdNetAddrInfo *hAddr, const char *hName)
char * Find(XrdNetAddrInfo *hAddr)