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