XRootD
Loading...
Searching...
No Matches
XrdNetAddr.cc
Go to the documentation of this file.
1/******************************************************************************/
2/* */
3/* X r d N e t A d d r . 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 <netdb.h>
33#include <cstdio>
34#include <unistd.h>
35#include <arpa/inet.h>
36#include <sys/types.h>
37
38#include "XrdNet/XrdNetAddr.hh"
39#include "XrdNet/XrdNetCache.hh"
41#include "XrdNet/XrdNetUtils.hh"
42#include "XrdSys/XrdSysE2T.hh"
43
44/******************************************************************************/
45/* P l a t f o r m D e p e n d e n c i e s */
46/******************************************************************************/
47
48// Linux defines s6_addr32 but MacOS does not and Solaris defines it only when
49// compiling the kernel. This is really standard stuff that should be here.
50//
51#ifndef s6_addr32
52#if defined(__solaris__)
53#define s6_addr32 _S6_un._S6_u32
54#elif defined(__APPLE__) || defined(__FreeBSD__)
55#define s6_addr32 __u6_addr.__u6_addr32
56#endif
57#endif
58
59/******************************************************************************/
60/* S t a t i c M e m b e r s */
61/******************************************************************************/
62
63namespace
64{
65bool OnlyIPV4()
66{
67 int fd;
68
69// Detect badly configured or non-extent IPv6 stacks and revert to IPv4 is so.
70//
71 if ((fd = socket(AF_INET6, SOCK_STREAM, 0)) >= 0) close(fd);
72 else if (errno == EAFNOSUPPORT)
74 return true;
75 }
76 return false;
77}
78}
79
80struct addrinfo *XrdNetAddr::hostHints = XrdNetAddr::Hints(0, 0);
81
82struct addrinfo *XrdNetAddr::huntHintsTCP = XrdNetAddr::Hints(1, SOCK_STREAM);
83
84struct addrinfo *XrdNetAddr::huntHintsUDP = XrdNetAddr::Hints(2, SOCK_DGRAM);
85
86// The following must be initialzed after all of the hint structures!
87//
88bool XrdNetAddr::useIPV4 = OnlyIPV4();
89bool XrdNetAddr::dynDNS = false;
90
91/******************************************************************************/
92/* C o n s t r u c t o r */
93/******************************************************************************/
94
96{
97 const char *fqn = XrdNetIdentity::FQN();
98
99// The host name might not be resolvable. If we have an fqn then either the
100// name was manually set or we are using an IP address because reverse
101// lookup did not work. If we have an fqn, then use it as the name. Otherwise,
102// use localhost as that is always a safe fallback.
103//
104 if (!fqn || Set(fqn, port))
105 {Set("localhost", port);
106 if (fqn)
107 {if (hostName) free(hostName);
108 hostName = strdup(fqn);
109 }
110 }
111}
112
113/******************************************************************************/
114/* Private: H i n t s */
115/******************************************************************************/
116
117struct addrinfo *XrdNetAddr::Hints(int htype, int stype)
118{
119 static struct addrinfo theHints[3];
120
121// Return properly initialized hint structure. We need to do this dynamically
122// in a static constructor since the addrinfo layout differs by OS-type.
123//
124 memset(&theHints[htype], 0, sizeof(struct addrinfo));;
125 if (htype) theHints[htype].ai_flags = AI_V4MAPPED | AI_ADDRCONFIG;
126 else theHints[htype].ai_flags = AI_V4MAPPED | AI_CANONNAME;
127 theHints[htype].ai_family = AF_UNSPEC;
128 theHints[htype].ai_socktype = stype;
129 return &theHints[htype];
130}
131
132/******************************************************************************/
133/* Private: M a p 6 4 */
134/******************************************************************************/
135
136bool XrdNetAddr::Map64()
137{
138
139// The address must be a mapped IPV4 address
140//
141 if (!IN6_IS_ADDR_V4MAPPED(&IP.v6.sin6_addr)) return false;
142
143// Now convert this down to an IPv4 address
144//
145 IP.v4.sin_addr.s_addr = IP.v6.sin6_addr.s6_addr32[3];
146 IP.v4.sin_family = AF_INET;
147 protType = PF_INET;
148 addrSize = sizeof(sockaddr_in);
149 return true;
150}
151
152/******************************************************************************/
153/* P o r t */
154/******************************************************************************/
155
156int XrdNetAddr::Port(int pNum)
157{
158// Make sure we have a proper address family here
159//
160 if (IP.Addr.sa_family != AF_INET && IP.Addr.sa_family != AF_INET6)
161 return -1;
162
163// Return port number if so wanted. The port location is the same regardless of
164// the address family.
165//
166 if (pNum < 0) return ntohs(IP.v6.sin6_port);
167
168// Set port number if we have a valid address. The location of the port
169// is the same regardless of address family.
170//
171 if (pNum > 0xffff) return -1;
172 IP.v6.sin6_port = htons(static_cast<short>(pNum));
173 return pNum;
174}
175
176/******************************************************************************/
177/* R e g i s t e r */
178/******************************************************************************/
179
180bool XrdNetAddr::Register(const char *hName)
181{
182 XrdNetAddr *aListVec = 0;
183 int i, aListNum;
184
185// Step one is to make sure the incoming name is not an address
186//
187 if (!isHostName(hName)) return false;
188
189// The next step is to get all of the IP addresses registered for this name
190//
191 if (XrdNetUtils::GetAddrs(hName, &aListVec, aListNum,
193 return false;
194
195// In order to use the given name, one of the IP addresses in the list must
196// match our address. This is about as secure we can get.
197//
198 for (i = 0; i < aListNum; i++) {if (Same(&aListVec[i])) break;}
199 delete [] aListVec;
200
201// If we didn't find a match, report it
202//
203 if (i >= aListNum) return false;
204
205// Replace current hostname with the wanted one
206//
207 if (hostName) free(hostName);
208 hostName = strdup(hName);
209 return true;
210}
211
212/******************************************************************************/
213/* S e t */
214/******************************************************************************/
215
216const char *XrdNetAddr::Set(const char *hSpec, int pNum)
217{
218 static const char *badIPv4 = "invalid IPv4 address";
219 static const char *badIPv6 = "invalid IPv6 address";
220 static const char *badIP64 = "IPv6 address not IPv4 representable";
221 static const char *badName = "invalid host name";
222 static const int map46ID = htonl(0x0000ffff);
223
224 const char *Colon, *iP;
225 char aBuff[NI_MAXHOST+INET6_ADDRSTRLEN];
226 int aLen, n;
227 bool mapIt;
228
229// Clear translation if set (note unixPipe & sockAddr are the same).
230//
231 if (hostName) {free(hostName); hostName = 0;}
232 if (sockAddr != &IP.Addr) {delete unixPipe; sockAddr = &IP.Addr;}
233 memset(&IP, 0, sizeof(IP));
234 addrSize = sizeof(sockaddr_in6);
235
236// Check for any address setting
237//
238 if (!hSpec)
239 {if (useIPV4)
240 {IP.v4.sin_family = AF_INET;
241 IP.v4.sin_addr.s_addr = INADDR_ANY;
242 protType = PF_INET;
243 addrSize = sizeof(sockaddr_in);
244 } else {
245 IP.v6.sin6_family = AF_INET6;
246 IP.v6.sin6_addr = in6addr_any;
247 protType = PF_INET6;
248 }
249 if (pNum < 0) pNum= -pNum;
250 IP.v6.sin6_port = htons(static_cast<short>(pNum));
251 return 0;
252 }
253
254// Check for Unix type address here
255//
256 if (*hSpec == '/')
257 {if (strlen(hSpec) >= sizeof(unixPipe->sun_path)) return "path too long";
258 unixPipe = new sockaddr_un;
259 strcpy(unixPipe->sun_path, hSpec);
260 unixPipe->sun_family = IP.Addr.sa_family = AF_UNIX;
261 addrSize = sizeof(sockaddr_un);
262 protType = PF_UNIX;
263 return 0;
264 }
265
266// Do length check to see if we can fit the host name in our buffer.
267//
268 aLen = strlen(hSpec);
269 if (aLen >= (int)sizeof(aBuff)) return "host id too long";
270
271// Convert the address as appropriate. Note that we do accept RFC5156 deprecated
272// IPV4 mapped IPV6 addresses(i.e. [::a.b.c.d]. This is historical.
273//
274 if (*hSpec == '[')
275 {const char *Brak = index(hSpec+1, ']');
276 if (!Brak) return badIPv6;
277 Colon = Brak+1;
278 if (!(*Colon)) Colon = 0;
279 else if (*Colon != ':') return badIPv6;
280 aLen = Brak - (hSpec+1);
281 if (aLen >= INET6_ADDRSTRLEN) return badIPv6;
282 mapIt = (*(hSpec+1) == ':' && *(hSpec+2) == ':'
283 && *(hSpec+3) >= '0' && *(hSpec+3) <= '9'
284 && (iP = index(hSpec+4, '.')) && iP < Brak);
285 strncpy(aBuff, hSpec+1, aLen); aBuff[aLen] = 0;
286 if (inet_pton(AF_INET6,aBuff,&IP.v6.sin6_addr) != 1) return badIPv6;
287 if (mapIt) IP.v6.sin6_addr.s6_addr32[2] = map46ID;
288 IP.v6.sin6_family = AF_INET6;
289 protType = PF_INET6;
290 if (useIPV4 && !Map64()) return badIP64;
291 }
292 else if (!isHostName(hSpec))
293 {if ((Colon = index(hSpec, ':')))
294 {aLen = Colon - hSpec;
295 if (aLen >= INET_ADDRSTRLEN) return badIPv4;
296 strncpy(aBuff, hSpec, aLen); aBuff[aLen] = 0; iP = aBuff;
297 } else iP = hSpec;
298 if (inet_pton(AF_INET ,iP, &IP.v6.sin6_addr.s6_addr32[3]) != 1)
299 return badIPv4;
300 IP.v6.sin6_addr.s6_addr32[2] = map46ID;
301 IP.v6.sin6_family = AF_INET6;
302 protType = PF_INET6;
303 if (useIPV4 && !Map64()) return badIPv4;
304 }
305 else if (*hSpec == 0) return badName;
306
307 else {struct addrinfo *rP = 0;
308 if ((Colon = index(hSpec, ':')))
309 {aLen = Colon - hSpec;
310 if (aLen > MAXHOSTNAMELEN) return badName;
311 strncpy(aBuff, hSpec, aLen); aBuff[aLen] = 0; iP = aBuff;
312 } else iP = hSpec;
313 n = getaddrinfo(iP, 0, hostHints, &rP);
314 if (n || !rP)
315 {if (rP) freeaddrinfo(rP);
316 if (n == EAI_NONAME && dynDNS)
317 return "Dynamic name or service not yet registered";
318 return (n ? gai_strerror(n) : "host not found");
319 }
320 memcpy(&IP.Addr, rP->ai_addr, rP->ai_addrlen);
321 protType = (IP.v6.sin6_family == AF_INET6 ? PF_INET6 : PF_INET);
322 if (rP->ai_canonname) hostName = LowCase(strdup(rP->ai_canonname));
323 freeaddrinfo(rP);
324 }
325
326// Now set the port number as needed (v4 & v6 port locations are the same)
327//
328 if (pNum == PortInSpec && !Colon) return "port not specified";
329 if (pNum <= 0 && Colon)
330 {char *eP;
331 pNum = strtol(Colon+1, &eP, 10);
332 if (pNum < 0 || pNum > 0xffff || *eP) return "invalid port number";
333 } else if (pNum < 0) pNum = -pNum;
334 IP.v6.sin6_port = htons(static_cast<short>(pNum));
335
336// All done
337//
338 return 0;
339}
340
341/******************************************************************************/
342
343const char *XrdNetAddr::Set(const char *hSpec, int &numIP, int maxIP,
344 int pNum, bool optUDP)
345{
346 struct addrinfo *hP, *rP = 0, *pP, *nP;
347 XrdNetAddr *aVec = this;
348 const char *hnBeg, *hnEnd, *pnBeg, *pnEnd;
349 char hBuff[MAXHOSTNAMELEN+8];
350 int hLen, n;
351
352// If only one address can be returned, just revert to standard processing
353//
354 if (!hSpec || !isalpha(*hSpec) || maxIP < 2)
355 {const char *eMsg = Set(hSpec, pNum);
356 numIP = (eMsg ? 0 : 1);
357 return eMsg;
358 }
359
360// Extract out host name
361//
362 if (!XrdNetUtils::Parse(hSpec, &hnBeg, &hnEnd, &pnBeg, &pnEnd))
363 return "invalid host specification";
364 hLen = hnEnd - hnBeg;
365 if (hLen > MAXHOSTNAMELEN) return "host name too long";
366 strncpy(hBuff, hSpec, hLen); hBuff[hLen] = 0;
367
368// Get the port number we will be setting
369//
370 if (pnBeg == hnEnd)
371 {if (pNum == PortInSpec) return "port not specified";
372 if (pNum < 0) pNum = -pNum;
373 } else {
374 if (*pnEnd || !(n = XrdNetUtils::ServPort(pnBeg, optUDP)))
375 return "invalid port";
376 if (pNum < 0) pNum = n;
377 }
378
379// Get all of the addresses
380//
381 hP = (optUDP ? huntHintsUDP : huntHintsTCP);
382 n = getaddrinfo(hBuff, 0, hP, &rP);
383 if (n || !rP)
384 {if (rP) freeaddrinfo(rP);
385 return (n ? gai_strerror(n) : "host not found");
386 }
387
388// Now self-referentially fill out ourselves with no duplicates
389//
390 n = 0; pP = 0; nP = rP;
391 do {if (!pP || pP->ai_addrlen != nP->ai_addrlen
392 || memcmp((const void *)pP->ai_addr, (const void *)nP->ai_addr,
393 nP->ai_addrlen)) {aVec[n].Set(nP, pNum); n++;}
394 pP = nP; nP = nP->ai_next;
395 } while(n < maxIP && nP);
396
397// All done
398//
399 numIP = n;
400 if (rP) freeaddrinfo(rP);
401 return 0;
402}
403
404/******************************************************************************/
405
406const char *XrdNetAddr::Set(const struct sockaddr *sockP, int sockFD)
407{
408// Clear translation if set
409//
410 if (hostName) {free(hostName); hostName = 0;}
411 if (sockAddr != &IP.Addr) {delete unixPipe; sockAddr = &IP.Addr;}
412 sockNum = sockFD;
413
414// Copy the address based on address family
415//
416 if (sockP->sa_family == AF_INET6) {addrSize = sizeof(IP.v6);
417 protType = PF_INET6;
418 }
419 else if (sockP->sa_family == AF_INET) {addrSize = sizeof(IP.v4);
420 protType = PF_INET;
421 }
422 else if (sockP->sa_family == AF_UNIX)
423 {unixPipe = new sockaddr_un;
424 memcpy(unixPipe, sockP, sizeof(struct sockaddr_un));
425 unixPipe->sun_path[sizeof(unixPipe->sun_path)-1] = 0;
426 addrSize = sizeof(sockaddr_un);
427 memset(&IP, 0, sizeof(IP));
428 IP.Addr.sa_family = AF_UNIX;
429 protType = PF_UNIX;
430 return 0;
431 }
432 else return "invalid address family";
433
434// Copy the address and return
435//
436 memcpy(&IP, sockP, addrSize);
437 return 0;
438}
439
440/******************************************************************************/
441
442const char *XrdNetAddr::Set(int sockFD, bool peer)
443{
444 SOCKLEN_t aSize = static_cast<SOCKLEN_t>(sizeof(IP));
445 int rc;
446
447// Clear translation if set
448//
449 if (hostName) {free(hostName); hostName = 0;}
450 if (sockAddr != &IP.Addr) {delete unixPipe; sockAddr = &IP.Addr;}
451 sockNum = sockFD;
452
453// Get the address on the appropriate side of this socket
454//
455 if (peer) rc = getpeername(sockFD, &IP.Addr, &aSize);
456 else rc = getsockname(sockFD, &IP.Addr, &aSize);
457 if (rc < 0)
458 {addrSize = 0;
459 return XrdSysE2T(errno);
460 }
461
462// Set the correct address size and protocol family
463//
464 addrSize = aSize;
465 protType = (IP.Addr.sa_family == AF_INET ? PF_INET : PF_INET6);
466
467// All done
468//
469 return 0;
470}
471
472/******************************************************************************/
473
474const char *XrdNetAddr::Set(struct addrinfo *rP, int Port, bool mapit)
475{
476 static const int map46ID = htonl(0x0000ffff);
477
478// See if we need to convert this address otherwise just copy it
479//
480 if (mapit && rP->ai_family == AF_INET)
481 {memset(&IP.Addr, 0, sizeof(IP.Addr));
482 IP.v6.sin6_family = AF_INET6;
483 memcpy(&IP.v6.sin6_addr.s6_addr32[3], (rP->ai_addr->sa_data)+2, 4);
484 IP.v6.sin6_addr.s6_addr32[2] = map46ID;
485 addrSize = sizeof(IP.v6);
486 protType = PF_INET6;
487 } else {
488 memcpy(&IP.Addr, rP->ai_addr, rP->ai_addrlen);
489 addrSize = rP->ai_addrlen;
490 protType = rP->ai_protocol;
491 }
492
493// Cleanup pre-existing information
494//
495 if (hostName) free(hostName);
496 hostName = (rP->ai_canonname ? LowCase(strdup(rP->ai_canonname)) : 0);
497 if (sockAddr != &IP.Addr) {delete unixPipe; sockAddr = &IP.Addr;}
498 IP.v6.sin6_port = htons(static_cast<short>(Port));
499 sockNum = 0;
500 return 0;
501}
502
503/******************************************************************************/
504/* S e t C a c h e */
505/******************************************************************************/
506
507void XrdNetAddr::SetCache(int keeptime)
508{
509 static XrdNetCache theCache;
510
511// Set the cache keep time
512//
513 theCache.SetKT(keeptime);
514 dnsCache = (keeptime > 0 ? &theCache : 0);
515}
516
517/******************************************************************************/
518/* S e t D y n D N S */
519/******************************************************************************/
520
521void XrdNetAddr::SetDynDNS(bool onoff) {dynDNS = onoff;}
522
523/******************************************************************************/
524/* S e t I P V 4 */
525/******************************************************************************/
526
528{
529
530// To force IPV4 mode we merely change the hints structure and set the IPV4
531// mode flag to reject IPV6 address unless they are mapped.
532//
533 hostHints->ai_flags = AI_CANONNAME;
534 hostHints->ai_family = AF_INET;
535
536 huntHintsTCP->ai_flags = AI_ADDRCONFIG;
537 huntHintsTCP->ai_family = AF_INET;
538
539 huntHintsUDP->ai_flags = AI_ADDRCONFIG;
540 huntHintsUDP->ai_family = AF_INET;
541
542 useIPV4 = true;
543
544// Inform NetUtils that we changed mode
545//
547}
548
549/******************************************************************************/
550/* S e t I P V 6 */
551/******************************************************************************/
552
554{
555
556// To force IPV6 mode we merely change the hints structure and set the IPV4
557// mode flag to accept IPV6 address.
558//
559 hostHints->ai_flags = AI_V4MAPPED | AI_CANONNAME;
560 hostHints->ai_family = AF_INET6;
561
562 huntHintsTCP->ai_flags = AI_V4MAPPED | AI_ALL;
563 huntHintsTCP->ai_family = AF_INET6;
564
565 huntHintsUDP->ai_flags = AI_V4MAPPED | AI_ALL;
566 huntHintsUDP->ai_family = AF_INET6;
567
568 useIPV4 = false;
569
570// Inform NetUtils that we changed mode
571//
573}
574
575/******************************************************************************/
576/* S e t L o c a t i o n */
577/******************************************************************************/
578
580{
581// Copy in the new location information but preserve the flags
582//
583 addrLoc = loc;
584}
585
586/******************************************************************************/
587/* S e t T L S */
588/******************************************************************************/
589
590void XrdNetAddr::SetTLS(bool val)
591{
592 if (val) protFlgs |= isTLS;
593 else protFlgs &= ~isTLS;
594}
struct sockaddr_in6 v6
struct sockaddr Addr
struct sockaddr_in v4
#define close(a)
Definition XrdPosix.hh:48
#define eMsg(x)
const char * XrdSysE2T(int errcode)
Definition XrdSysE2T.cc:104
#define SOCKLEN_t
static XrdNetCache * dnsCache
static bool isHostName(const char *name)
unsigned char protFlgs
XrdNetSockAddr IP
int Same(const XrdNetAddrInfo *ipAddr, bool plusPort=false)
unsigned char protType
char * LowCase(char *str)
static const char isTLS
Location using TLS.
unsigned short addrSize
XrdNetAddr()
Assignment operator and copy constructor are inherited, no need to define.
static void SetIPV4()
bool Register(const char *hName)
static void SetCache(int keeptime)
static void SetIPV6()
void SetLocation(XrdNetAddrInfo::LocInfo &loc)
static void SetDynDNS(bool onoff)
void SetTLS(bool val)
static const int PortInSpec
const char * Set(const char *hSpec, int pNum=PortInSpec)
static const char * FQN(const char **etext=0)
static const int NoPortRaw
static const char * GetAddrs(const char *hSpec, XrdNetAddr *aListP[], int &aListN, AddrOpts opts=allIPMap, int pNum=PortInSpec)
static int ServPort(const char *sName, bool isUDP=false, const char **eText=0)
static bool Parse(const char *hSpec, const char **hName, const char **hNend, const char **hPort, const char **hPend)
static int SetAuto(AddrOpts aOpts=allIPMap)