1 /// Various networking support tools 2 module mecca.lib.net; 3 4 // Licensed under the Boost license. Full copyright information in the AUTHORS file 5 6 import core.stdc.errno : errno, EINVAL; 7 import core.sys.posix.sys.socket; 8 public import core.sys.posix.sys.socket: AF_INET, AF_UNIX, AF_INET6, AF_UNSPEC; 9 import core.sys.posix.netinet.in_; 10 import core.sys.posix.sys.un; 11 import core.sys.posix.netdb; 12 import std.conv; 13 14 import mecca.containers.arrays: FixedString, setStringzLength; 15 import mecca.lib.exception; 16 import mecca.lib..string; 17 import mecca.log; 18 19 /// An IPv4 address 20 @FMT("{bytes[0]}.{bytes[1]}.{bytes[2]}.{bytes[3]}") 21 struct IPv4 { 22 enum IPv4 loopback = IPv4(cast(ubyte[])[127, 0, 0, 1]); /// Lookpack address 23 enum IPv4 none = IPv4(cast(ubyte[])[255, 255, 255, 255]); /// No address 24 enum IPv4 broadcast = IPv4(cast(ubyte[])[255, 255, 255, 255]); /// Broadcast address 25 enum IPv4 any = IPv4(cast(ubyte[])[0, 0, 0, 0]); /// "Any" address (for local binding) 26 27 union { 28 in_addr inaddr = in_addr(0xffffffff); /// Address as an `in_addr` 29 ubyte[4] bytes; /// Address as array of bytes 30 } 31 32 /// Construct an IPv4 address 33 this(uint netOrder) pure nothrow @safe @nogc { 34 inaddr.s_addr = netOrder; 35 } 36 /// ditto 37 this(ubyte[4] bytes) pure nothrow @safe @nogc { 38 this.bytes = bytes; 39 } 40 /// ditto 41 this(in_addr ia) pure nothrow @safe @nogc { 42 inaddr = ia; 43 } 44 /// ditto 45 this(string dottedString) @safe @nogc { 46 opAssign(dottedString); 47 } 48 49 /// Assignment 50 ref typeof(this) opAssign(uint netOrder) return pure nothrow @safe @nogc { 51 inaddr.s_addr = netOrder; 52 return this; 53 } 54 /// ditto 55 ref typeof(this) opAssign(ubyte[4] bytes) return pure nothrow @safe @nogc { 56 this.bytes = bytes; 57 return this; 58 } 59 /// ditto 60 ref typeof(this) opAssign(in_addr ia) return pure nothrow @safe @nogc { 61 inaddr = ia; 62 return this; 63 } 64 /// ditto 65 ref typeof(this) opAssign(string dottedString) return @trusted @nogc { 66 int res = inet_pton(AF_INET, ToStringz!INET_ADDRSTRLEN(dottedString), &inaddr); 67 68 DBG_ASSERT!"Invalid call to inet_pton"(res>=0); 69 if( res!=1 ) { 70 errno = EINVAL; 71 throw mkEx!ErrnoException("Invalid IPv4 address to SockAddrIPv4 constructor"); 72 } 73 74 return this; 75 } 76 77 /// Return the address in network order 78 @property uint netOrder() const pure nothrow @safe @nogc { 79 return inaddr.s_addr; 80 } 81 /// Return the address in host order 82 @property uint hostOrder() const pure nothrow @safe @nogc { 83 return ntohl(inaddr.s_addr); 84 } 85 /// Is the address a valid one 86 /// 87 /// The broadcast address is not considered valid. 88 @property bool isValid() const pure nothrow @safe @nogc { 89 return inaddr.s_addr != 0 && inaddr.s_addr != 0xffffffff; 90 } 91 92 /// Return a GC allocated string for the address (dot notation) 93 string toString() const nothrow @trusted { 94 char[INET_ADDRSTRLEN] buffer; 95 ASSERT!"Address translation failed"(inet_ntop(AF_INET, &inaddr, buffer.ptr, INET_ADDRSTRLEN) !is null); 96 97 return to!string(buffer.ptr); 98 } 99 100 // 101 // netmasks 102 // 103 /// Return a mask corresponding to a network of 2^^bits addresses 104 static IPv4 mask(ubyte bits) nothrow @safe @nogc { 105 return IPv4((1 << bits) - 1); 106 } 107 108 /// Return how many bits in the current mask 109 /// 110 /// Object must be initialized to a valid mask address 111 ubyte maskBits() pure nothrow @safe @nogc { 112 if (netOrder == 0) { 113 return 0; 114 } 115 import core.bitop: bsf; 116 assert(isMask, "not a mask"); 117 int leastSignificantSetBit = bsf(cast(uint)hostOrder); 118 auto maskBits = (leastSignificantSetBit.sizeof * 8) - leastSignificantSetBit; 119 return cast(byte)maskBits; 120 } 121 122 /// Return whether current address is a valid mask address 123 public bool isMask() pure const nothrow @safe @nogc { 124 return (~hostOrder & (~hostOrder + 1)) == 0; 125 } 126 127 /// Return the intersection of the two addresses 128 /// 129 /// This is useful to extract network name and client name from an address 130 IPv4 opBinary(string op: "&")(IPv4 rhs) const nothrow @safe @nogc { 131 return IPv4(inaddr.s_addr & rhs.inaddr.s_addr); 132 } 133 134 /// Returns whether our IP and host are in the same network 135 bool isInSubnet(IPv4 host, IPv4 mask) const nothrow @safe @nogc { 136 assert(mask.isMask, "mask argument is not valid"); 137 return (host & mask) == (this & mask); 138 } 139 140 static assert (this.sizeof == in_addr.sizeof); 141 } 142 143 144 unittest { 145 import std.stdio; 146 assert(IPv4.loopback.toString() == "127.0.0.1"); 147 auto ip = IPv4("1.2.3.4"); 148 assert(ip.toString() == "1.2.3.4"); 149 assert(ip.netOrder == 0x04030201); 150 assert(ip.hostOrder == 0x01020304); 151 152 auto m = ip.mask(24); 153 assert(m.toString == "255.255.255.0"); 154 assert((ip & m) == IPv4("1.2.3.0")); 155 ip = "172.16.0.195"; 156 assert(ip.toString == "172.16.0.195"); 157 ip = IPv4.loopback; 158 assert(ip.toString == "127.0.0.1"); 159 assert(ip.bytes[0] == 127); 160 } 161 162 /// A D representation of the `sockaddr_in` struct 163 struct SockAddrIPv4 { 164 /// Unspecified port constant 165 enum PORT_ANY = 0; 166 167 /// the underlying `sockaddr_in` struct 168 sockaddr_in sa; 169 170 /// Construct a SockAddrIPv4 171 this(in_addr addr, ushort port = PORT_ANY) nothrow @safe @nogc { 172 sa.sin_family = AF_INET; 173 sa.sin_addr = addr; 174 this.port = port; 175 } 176 177 /// ditto 178 this(string addr, ushort port = PORT_ANY) @trusted @nogc { 179 int res = inet_pton(AF_INET, toStringzNGC(addr), &sa.sin_addr); 180 181 DBG_ASSERT!"Invalid call to inet_pton(%s)"(res>=0, addr); 182 if( res!=1 ) { 183 errno = EINVAL; 184 throw mkEx!ErrnoException("Invalid IPv4 address to SockAddrIPv4 constructor"); 185 } 186 187 sa.sin_family = AF_INET; 188 this.port = port; 189 } 190 191 /// ditto 192 this(const sockaddr* sa, socklen_t length) nothrow @trusted @nogc { 193 ASSERT!"Wrong address family for IPv4. %s instead of %s"(sa.sa_family == AF_INET, sa.sa_family, AF_INET); 194 ASSERT!"IPv4 sockaddr too short. %s<%s"(length >= sockaddr_in.sizeof, length, sockaddr_in.sizeof); 195 this.sa = *cast(sockaddr_in*)sa; 196 } 197 198 /// ditto 199 this(IPv4 addr, ushort port = PORT_ANY) nothrow @safe @nogc { 200 this(addr.inaddr, port); 201 } 202 203 /// Get/set the sa's port in $(B host) byte order 204 @property void port(ushort newPort) nothrow @safe @nogc { 205 sa.sin_port = htons(newPort); 206 } 207 208 /// ditto 209 @property ushort port() const pure nothrow @safe @nogc { 210 return ntohs(sa.sin_port); 211 } 212 213 /// Get/set the sa's addr 214 @property void addr(IPv4 address) nothrow @safe @nogc { 215 sa.sin_addr = address.inaddr; 216 } 217 218 /// ditto 219 @property IPv4 addr() const pure nothrow @safe @nogc { 220 return IPv4(sa.sin_addr); 221 } 222 223 /// Convert the address to GC allocated string in the format addr:port 224 string toString() const nothrow @safe { 225 return toStringAddr() ~ ":" ~ toStringPort(); 226 } 227 228 /// Convert just the address part to a GC allocated string 229 string toStringAddr() const nothrow @safe { 230 if( sa.sin_family != AF_INET ) 231 return "<Invalid IPv4 address>"; 232 233 return IPv4(sa.sin_addr).toString(); 234 } 235 236 auto toFixedStringAddr() const nothrow @trusted @nogc { 237 ASSERT!"Address family is %s, not IPv4"( sa.sin_family == AF_INET, sa.sin_family ); 238 239 FixedString!INET_ADDRSTRLEN buf; 240 buf.length = buf.capacity; 241 ASSERT!"Address translation failed"(inet_ntop(AF_INET, &sa.sin_addr, buf.ptr, buf.len) !is null); 242 setStringzLength(buf); 243 244 return buf; 245 } 246 247 /// Convert just the port part to a GC allocated string 248 string toStringPort() const nothrow @safe { 249 if( port!=PORT_ANY ) 250 return to!string(port); 251 else 252 return "*"; 253 } 254 255 /// Construct a loopback sockaddr for the given port 256 static SockAddrIPv4 loopback(ushort port = PORT_ANY) nothrow @safe @nogc { 257 return SockAddrIPv4(IPv4.loopback, port); 258 } 259 260 /// Construct an any sockaddr for the given port 261 static SockAddrIPv4 any(ushort port = PORT_ANY) nothrow @safe @nogc { 262 return SockAddrIPv4(IPv4.any, port); 263 } 264 265 /// Construct a broadcast sockaddr for the given port 266 static SockAddrIPv4 broadcast(ushort port = PORT_ANY) nothrow @safe @nogc { 267 return SockAddrIPv4(IPv4.broadcast, port); 268 } 269 } 270 271 unittest { 272 SockAddrIPv4 s1 = SockAddrIPv4.loopback(1234); 273 SockAddrIPv4 s2 = SockAddrIPv4(in_addr(htonl(0x0a0b0c0d))); 274 275 assertEQ(s1.toString(), "127.0.0.1:1234"); 276 assertEQ(s1.toStringAddr(), "127.0.0.1"); 277 assertEQ(s1.toFixedStringAddr(), "127.0.0.1"); 278 assertEQ(s1.toStringPort(), "1234"); 279 //assertEQ(s1.addrFixedString(), "127.0.0.1"); 280 assertEQ(s2.toString(), "10.11.12.13:*"); 281 //assertEQ(s2.addrFixedString(), "10.11.12.13"); 282 } 283 284 /// An IPv6 address 285 struct IPv6 { 286 enum any = IPv6(cast(ubyte[ADDR_LEN])[0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0]); /// "Any" address (for local binding) 287 enum loopback = IPv6(cast(ubyte[ADDR_LEN])[0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1]); /// Loopback address 288 289 enum ADDR_LEN = 16; /// Length of an IPv6 address 290 291 union { 292 in6_addr inaddr; /// Address as `in6_addr` 293 ubyte[ADDR_LEN] bytes; /// Address as array of bytes 294 } 295 296 /// Construct an IPv6 address 297 this(ref const in6_addr ia) nothrow @safe @nogc { 298 this.inaddr = ia; 299 } 300 301 /// ditto 302 this(ubyte[ADDR_LEN] bytes) nothrow @safe @nogc { 303 this.bytes = bytes; 304 } 305 306 /// ditto 307 this(string dottedString) @safe @nogc { 308 opAssign(dottedString); 309 } 310 /// Assignment 311 ref auto opAssign(ref const in6_addr ia) nothrow @safe @nogc { 312 this.inaddr = ia; 313 return this; 314 } 315 /// ditto 316 ref auto opAssign(ubyte[16] bytes) nothrow @safe @nogc { 317 this.bytes = bytes; 318 return this; 319 } 320 /// ditto 321 ref auto opAssign(string dottedString) @trusted @nogc { 322 int res = inet_pton(AF_INET6, ToStringz!INET6_ADDRSTRLEN(dottedString), &inaddr); 323 324 DBG_ASSERT!"Invalid call to inet_pton"(res>=0); 325 if( res!=1 ) { 326 errno = EINVAL; 327 throw mkEx!ErrnoException("Invalid IPv6 address to SockAddrIPv6 constructor"); 328 } 329 330 return this; 331 } 332 333 /// Return a GC allocated string for the address (colon notation) 334 string toString() const @trusted { 335 char[INET6_ADDRSTRLEN] buf; 336 errnoEnforceNGC(inet_ntop(AF_INET6, bytes.ptr, buf.ptr, buf.length) !is null, "inet_ntop failed"); 337 return to!string(buf.ptr); 338 } 339 340 /// Returns true if this is the unspecified address 341 @property bool isUnspecified() pure const @trusted @nogc { 342 // DMDBUG the "macro" is incorrectly defined, which means we have to cast away constness here 343 return IN6_IS_ADDR_UNSPECIFIED(cast(in6_addr*)&inaddr) != 0; 344 } 345 /// Returns true if this is the loopback address 346 @property bool isLoopback() pure const @trusted @nogc { 347 // DMDBUG the "macro" is incorrectly defined, which means we have to cast away constness here 348 return IN6_IS_ADDR_LOOPBACK(cast(in6_addr*)&inaddr) != 0; 349 } 350 /// Returns true if this is a multicast address 351 @property bool isMulticast() pure const @trusted @nogc { 352 // DMDBUG the "macro" is incorrectly defined, which means we have to cast away constness here 353 return IN6_IS_ADDR_MULTICAST(cast(in6_addr*)&inaddr) != 0; 354 } 355 /// Returns true if this is a link local address 356 @property bool isLinkLocal() pure const @trusted @nogc { 357 // DMDBUG the "macro" is incorrectly defined, which means we have to cast away constness here 358 return IN6_IS_ADDR_LINKLOCAL(cast(in6_addr*)&inaddr) != 0; 359 } 360 /// Returns true if this is a site local address 361 @property bool isSiteLocal() pure const @trusted @nogc { 362 // DMDBUG the "macro" is incorrectly defined, which means we have to cast away constness here 363 return IN6_IS_ADDR_SITELOCAL(cast(in6_addr*)&inaddr) != 0; 364 } 365 @property bool isV4Mapped() pure const @trusted @nogc { 366 // DMDBUG the "macro" is incorrectly defined, which means we have to cast away constness here 367 return IN6_IS_ADDR_V4MAPPED(cast(in6_addr*)&inaddr) != 0; 368 } 369 /// Returns true if this address is in the IPv4 range 370 @property bool isV4Compat() pure const @trusted @nogc { 371 // DMDBUG the "macro" is incorrectly defined, which means we have to cast away constness here 372 return IN6_IS_ADDR_V4COMPAT(cast(in6_addr*)&inaddr) != 0; 373 } 374 375 static assert (this.sizeof == in6_addr.sizeof, "Incorrect size of struct"); 376 } 377 378 unittest { 379 assert(IPv6.loopback.toString() == "::1"); 380 assert(IPv6.loopback.isLoopback); 381 } 382 383 /// A D representation of the `sockaddr_in6` struct 384 struct SockAddrIPv6 { 385 /// Unspecified port constant 386 enum PORT_ANY = 0; 387 388 /// the underlying `sockaddr_in6` struct 389 sockaddr_in6 sa; 390 391 /// Construct a SockAddrIPv6 392 this(in6_addr addr, ushort port = PORT_ANY) nothrow @safe @nogc { 393 sa.sin6_family = AF_INET6; 394 sa.sin6_addr = addr; 395 sa.sin6_flowinfo = 0; 396 this.port = port; 397 } 398 399 /// ditto 400 this(string addr, ushort port = PORT_ANY) @trusted @nogc { 401 int res = inet_pton(AF_INET6, toStringzNGC(addr), &sa.sin6_addr); 402 403 DBG_ASSERT!"Invalid call to inet_pton"(res>=0); 404 if( res!=1 ) { 405 errno = EINVAL; 406 throw mkEx!ErrnoException("Invalid IPv6 address to SockAddrIPv6 constructor"); 407 } 408 409 sa.sin6_family = AF_INET6; 410 this.port = port; 411 } 412 413 /// ditto 414 this(const sockaddr* sa, socklen_t length) nothrow @trusted @nogc { 415 ASSERT!"Wrong address family for IPv6. %s instead of %s"(sa.sa_family == AF_INET6, sa.sa_family, AF_INET6); 416 ASSERT!"IPv4 sockaddr too short. %s<%s"(length < sockaddr_in6.sizeof, length, sockaddr_in6.sizeof); 417 this.sa = *cast(sockaddr_in6*)sa; 418 } 419 420 /// ditto 421 this(IPv6 addr, ushort port = PORT_ANY) nothrow @safe @nogc { 422 this(addr.inaddr, port); 423 } 424 425 /// Get/set the sa's port in $(B host) byte order 426 @property void port(ushort newPort) nothrow @safe @nogc { 427 sa.sin6_port = htons(newPort); 428 } 429 430 /// ditto 431 @property ushort port() const pure nothrow @safe @nogc { 432 return ntohs(sa.sin6_port); 433 } 434 435 /// Convert the address to GC allocated string in the format addr:port 436 string toString() const nothrow @safe { 437 return toStringAddr() ~ ":" ~ toStringPort(); 438 } 439 440 /// Convert just the address part to a GC allocated string 441 string toStringAddr() const nothrow @trusted { 442 if( sa.sin6_family != AF_INET6 ) 443 return "<Invalid IPv6 address>"; 444 445 char[INET6_ADDRSTRLEN] buffer; 446 ASSERT!"Address translation failed"(inet_ntop(AF_INET6, &sa.sin6_addr, buffer.ptr, INET6_ADDRSTRLEN) !is null); 447 448 return to!string(buffer.ptr); 449 } 450 451 auto toFixedStringAddr() const nothrow @trusted @nogc { 452 ASSERT!"Address family is %s, not IPv6"( sa.sin6_family == AF_INET6, sa.sin6_family ); 453 454 FixedString!INET6_ADDRSTRLEN buf; 455 buf.length = buf.capacity; 456 ASSERT!"Address translation failed"(inet_ntop(AF_INET6, &sa.sin6_addr, buf.ptr, buf.len) !is null); 457 setStringzLength(buf); 458 459 return buf; 460 } 461 462 /// Convert just the port part to a GC allocated string 463 string toStringPort() const nothrow @safe { 464 if( port!=PORT_ANY ) 465 return to!string(port); 466 else 467 return "*"; 468 } 469 470 /// Construct a loopback sockaddr for the given port 471 static SockAddrIPv6 loopback(ushort port = PORT_ANY) nothrow @safe @nogc { 472 return SockAddrIPv6(IPv6.loopback, port); 473 } 474 475 /// Construct an any sockaddr for the given port 476 static SockAddrIPv6 any(ushort port = PORT_ANY) nothrow @safe @nogc { 477 return SockAddrIPv6(IPv6.any, port); 478 } 479 } 480 481 unittest { 482 // String translation tests 483 in6_addr test_ip6 = in6_addr(['\x11','\x11','\x11','\x11', 484 '\x11','\x11','\x11','\x11', 485 '\x11','\x11','\x11','\x11', 486 '\x11','\x11','\x11','\x11']); 487 488 SockAddrIPv6 s1 = SockAddrIPv6.loopback(1234); 489 SockAddrIPv6 s2 = SockAddrIPv6(test_ip6, 1234); 490 491 assertEQ(s1.toString(), "::1:1234"); 492 assertEQ(s1.toFixedStringAddr(), "::1"); 493 assertEQ(s2.toString(), "1111:1111:1111:1111:1111:1111:1111:1111:1234"); 494 assertEQ(s2.toFixedStringAddr(), "1111:1111:1111:1111:1111:1111:1111:1111"); 495 } 496 497 /// A D representation of the `sockaddr_un` struct 498 struct SockAddrUnix { 499 /// the underlying `sockaddr_in` struct 500 sockaddr_un unix = void; 501 502 /// Construct a SockAddrUnix 503 this(const sockaddr* sa, socklen_t length) nothrow @trusted @nogc { 504 ASSERT!"Wrong address family for Unix domain sockets. %s instead of %s"(sa.sa_family == AF_UNIX, sa.sa_family, AF_UNIX); 505 ASSERT!"Unix domain sockaddr too short. %s<%s"(length < sockaddr_un.sizeof, length, sockaddr_un.sizeof); 506 this.unix = *cast(sockaddr_un*)sa; 507 } 508 509 /// ditto 510 this(string path) nothrow @trusted @nogc { 511 unix.sun_family = AF_UNIX; 512 unix.sun_path[0..path.length][] = cast(immutable(byte)[])path[]; 513 if( path.length < unix.sun_path.length ) 514 unix.sun_path[path.length] = '\0'; 515 } 516 517 /// Convert the address to GC allocated string 518 string toString() @trusted { 519 import std..string; 520 521 if( unix.sun_family != AF_UNIX ) 522 return "<Invalid Unix domain address>"; 523 524 if( unix.sun_path[0] == '\0' ) 525 return "<Anonymous Unix domain address>"; 526 527 auto idx = indexOf(cast(char[])(unix.sun_path[]), cast(byte)'\0'); 528 if( idx<0 ) 529 idx = unix.sun_path.length; 530 531 return to!string(unix.sun_path[0..idx]); 532 } 533 } 534 535 /// A D representation of a `sockaddr` struct 536 /// 537 /// This is how `sockaddr` might have looked like had C supported inheritence 538 struct SockAddr { 539 align(1): 540 union { 541 sockaddr base = sockaddr(AF_UNSPEC); /// SockAddr as a `sockaddr` 542 SockAddrIPv4 ipv4; /// SockAddr as a SockAddrIPv4 543 SockAddrIPv6 ipv6; /// SockAddr as a SockAddrIPv6 544 SockAddrUnix unix; /// SockAddr as a SockAddrUnix 545 } 546 547 /// Construct a SockAddr 548 this(const sockaddr* sa, socklen_t length) nothrow @safe @nogc { 549 switch(sa.sa_family) { 550 case AF_INET: 551 this.ipv4 = SockAddrIPv4(sa, length); 552 break; 553 case AF_INET6: 554 this.ipv6 = SockAddrIPv6(sa, length); 555 break; 556 case AF_UNIX: 557 this.unix = SockAddrUnix(sa, length); 558 break; 559 default: 560 ASSERT!"SockAddr constructor called with invalid family %s"(false, sa.sa_family); 561 } 562 } 563 564 /// ditto 565 this(SockAddrIPv4 sa) nothrow @safe @nogc { 566 ipv4 = sa; 567 ASSERT!"Called with mismatching address family. Expected IPv4(%s), got %s"( AF_INET == family, AF_INET, family ); 568 } 569 570 /// ditto 571 this(SockAddrIPv6 sa) nothrow @safe @nogc { 572 ipv6 = sa; 573 ASSERT!"Called with mismatching address family. Expected IPv6(%s), got %s"( AF_INET6 == family, AF_INET6, family ); 574 } 575 576 /// ditto 577 this(SockAddrUnix sa) nothrow @safe @nogc { 578 unix = sa; 579 ASSERT!"Called with mismatching address family. Expected Unix domain(%s), got %s"( AF_UNIX == family, AF_UNIX, family ); 580 } 581 582 /// Return the address family 583 @property sa_family_t family() const pure nothrow @safe @nogc { 584 return base.sa_family; 585 } 586 587 /// Return a GC allocated string representing the address 588 string toString() @safe { 589 switch( family ) { 590 case AF_UNSPEC: 591 return "<Uninitialized socket address>"; 592 case AF_INET: 593 return ipv4.toString(); 594 case AF_INET6: 595 return ipv6.toString(); 596 case AF_UNIX: 597 return unix.toString(); 598 599 default: 600 return "<Unsupported socket address family>"; 601 } 602 } 603 604 /// Convert just the address part to a GC allocated string 605 string toStringAddr() @safe { 606 switch( family ) { 607 case AF_UNSPEC: 608 return "<Uninitialized socket address>"; 609 case AF_INET: 610 return ipv4.toStringAddr(); 611 case AF_INET6: 612 return ipv6.toStringAddr(); 613 case AF_UNIX: 614 return unix.toString(); 615 616 default: 617 return "<Unsupported socket address family>"; 618 } 619 } 620 621 /// Convert just the port part to a GC allocated string 622 string toStringPort() @safe { 623 switch( family ) { 624 case AF_UNSPEC: 625 return "<Uninitialized socket address>"; 626 case AF_INET: 627 return ipv4.toStringPort(); 628 case AF_INET6: 629 return ipv6.toStringPort(); 630 case AF_UNIX: 631 return "<Portless address family AF_UNIX>"; 632 633 default: 634 return "<Unsupported socket address family>"; 635 } 636 } 637 638 /// Perform a name resolution on the given string 639 static SockAddr resolve(string hostname, string service = null, ushort family = AF_INET, int sockType = 0) @trusted 640 { 641 ASSERT!"Invalid family %s"(family == AF_INET || family == AF_INET6, family); 642 643 addrinfo* res = null; 644 addrinfo hint; 645 hint.ai_family = family; 646 hint.ai_socktype = sockType; 647 648 auto rc = getaddrinfo(hostname.toStringzNGC, ToStringz!512(service), &hint, &res); 649 if( rc!=0 ) { 650 throw mkExFmt!Exception("Lookup failed for %s:%s: %s", hostname, service, to!string(gai_strerror(rc))); 651 } 652 if( res is null ) { 653 throw mkExFmt!Exception("Lookup for %s:%s returned no results", hostname, service); 654 } 655 scope(exit) freeaddrinfo(res); 656 657 return SockAddr(res.ai_addr, res.ai_addrlen); 658 } 659 660 unittest { 661 auto addr = SockAddr.resolve("localhost", "ssh"); 662 assertEQ( addr.toString(), "127.0.0.1:22" ); 663 addr = SockAddr.resolve("localhost"); 664 assertEQ( addr.toString(), "127.0.0.1:*" ); 665 } 666 667 /// Returns the length of the data in the struct 668 /// 669 /// This is needed for underlying system calls that accept `sockaddr` 670 @property uint len() const pure nothrow @safe @nogc { 671 switch(family) { 672 case AF_UNSPEC: 673 return SockAddr.sizeof; 674 case AF_INET: 675 return SockAddrIPv4.sizeof; 676 case AF_INET6: 677 return SockAddrIPv6.sizeof; 678 case AF_UNIX: 679 return SockAddrUnix.sizeof; 680 default: 681 assert(false, "Unknown family"); 682 } 683 } 684 }