XRootD
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 
38 #include "XrdNet/XrdNetAddrInfo.hh"
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 
72 namespace
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 
81 int 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 
265 char *XrdNetAddrInfo::LowCase(char *str)
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 
278 bool 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 
309 const 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 
356 int 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 
432 int 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
if(Avsz)
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)
Definition: XrdNetCache.cc:63
char * Find(XrdNetAddrInfo *hAddr)
Definition: XrdNetCache.cc:148