PageRenderTime 145ms CodeModel.GetById 19ms app.highlight 111ms RepoModel.GetById 2ms app.codeStats 0ms

/std/socket.d

http://github.com/jcd/phobos
D | 3276 lines | 2021 code | 414 blank | 841 comment | 255 complexity | 02a5746ae79a3ff545995366b2eb4268 MD5 | raw file

Large files files are truncated, but you can click here to view the full file

   1// Written in the D programming language
   2
   3/*
   4        Copyright (C) 2004-2011 Christopher E. Miller
   5
   6        Boost Software License - Version 1.0 - August 17th, 2003
   7
   8        Permission is hereby granted, free of charge, to any person or organization
   9        obtaining a copy of the software and accompanying documentation covered by
  10        this license (the "Software") to use, reproduce, display, distribute,
  11        execute, and transmit the Software, and to prepare derivative works of the
  12        Software, and to permit third-parties to whom the Software is furnished to
  13        do so, all subject to the following:
  14
  15        The copyright notices in the Software and this entire statement, including
  16        the above license grant, this restriction and the following disclaimer,
  17        must be included in all copies of the Software, in whole or in part, and
  18        all derivative works of the Software, unless such copies or derivative
  19        works are solely in the form of machine-executable object code generated by
  20        a source language processor.
  21
  22        THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
  23        IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
  24        FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT
  25        SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE
  26        FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE,
  27        ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
  28        DEALINGS IN THE SOFTWARE.
  29
  30        socket.d 1.4
  31        Jan 2011
  32
  33        Thanks to Benjamin Herr for his assistance.
  34 */
  35
  36/**
  37 * Example: See $(SAMPLESRC listener.d) and $(SAMPLESRC htmlget.d)
  38 * License: <a href="http://www.boost.org/LICENSE_1_0.txt">Boost License 1.0</a>.
  39 * Authors: Christopher E. Miller, $(WEB klickverbot.at, David Nadlinger),
  40 *      $(WEB thecybershadow.net, Vladimir Panteleev)
  41 * Source:  $(PHOBOSSRC std/_socket.d)
  42 * Macros:
  43 *      WIKI=Phobos/StdSocket
  44 */
  45
  46module std.socket;
  47
  48import core.stdc.stdint, std.string, std.c.string, std.c.stdlib, std.conv;
  49
  50import core.stdc.config;
  51import core.time : dur, Duration;
  52import std.algorithm : max;
  53import std.exception : assumeUnique, enforce, collectException;
  54
  55version(Windows)
  56{
  57    pragma (lib, "ws2_32.lib");
  58    pragma (lib, "wsock32.lib");
  59
  60    private import std.c.windows.windows, std.c.windows.winsock, std.windows.syserror;
  61    private alias std.c.windows.winsock.timeval _ctimeval;
  62    private alias std.c.windows.winsock.linger _clinger;
  63
  64    enum socket_t : SOCKET { INVALID_SOCKET }
  65    private const int _SOCKET_ERROR = SOCKET_ERROR;
  66
  67
  68    private int _lasterr()
  69    {
  70        return WSAGetLastError();
  71    }
  72}
  73else version(Posix)
  74{
  75    version(linux)
  76        import std.c.linux.socket : AF_IPX, AF_APPLETALK, SOCK_RDM,
  77               IPPROTO_IGMP, IPPROTO_GGP, IPPROTO_PUP, IPPROTO_IDP,
  78               SD_RECEIVE, SD_SEND, SD_BOTH, MSG_NOSIGNAL, INADDR_NONE,
  79               TCP_KEEPIDLE, TCP_KEEPINTVL;
  80    else version(OSX)
  81        import std.c.osx.socket : AF_IPX, AF_APPLETALK, SOCK_RDM,
  82               IPPROTO_IGMP, IPPROTO_GGP, IPPROTO_PUP, IPPROTO_IDP,
  83               SD_RECEIVE, SD_SEND, SD_BOTH, INADDR_NONE;
  84    else version(FreeBSD)
  85    {
  86        import core.sys.posix.sys.socket;
  87        import core.sys.posix.sys.select;
  88        import std.c.freebsd.socket;
  89        private enum SD_RECEIVE = SHUT_RD;
  90        private enum SD_SEND    = SHUT_WR;
  91        private enum SD_BOTH    = SHUT_RDWR;
  92    }
  93    else
  94        static assert(false);
  95
  96    import core.sys.posix.netdb;
  97    import core.sys.posix.sys.un : sockaddr_un;
  98    private import core.sys.posix.fcntl;
  99    private import core.sys.posix.unistd;
 100    private import core.sys.posix.arpa.inet;
 101    private import core.sys.posix.netinet.tcp;
 102    private import core.sys.posix.netinet.in_;
 103    private import core.sys.posix.sys.time;
 104    //private import core.sys.posix.sys.select;
 105    private import core.sys.posix.sys.socket;
 106    private alias core.sys.posix.sys.time.timeval _ctimeval;
 107    private alias core.sys.posix.sys.socket.linger _clinger;
 108
 109    private import core.stdc.errno;
 110
 111    enum socket_t : int32_t { init = -1 }
 112    private const int _SOCKET_ERROR = -1;
 113
 114
 115    private int _lasterr()
 116    {
 117        return errno;
 118    }
 119}
 120else
 121{
 122    static assert(0);     // No socket support yet.
 123}
 124
 125version(unittest)
 126{
 127    static assert(is(uint32_t == uint));
 128    static assert(is(uint16_t == ushort));
 129
 130    private import std.stdio : writefln;
 131
 132    // Print a message on exception instead of failing the unittest.
 133    private void softUnittest(void delegate() test, int line = __LINE__)
 134    {
 135        try
 136            test();
 137        catch (Throwable e)
 138        {
 139            writefln(" --- std.socket(%d) test fails depending on environment ---", line);
 140            writefln(" (%s)", e);
 141        }
 142    }
 143}
 144
 145/// Base exception thrown by $(D std.socket).
 146class SocketException: Exception
 147{
 148    ///
 149    this(string msg, string file = __FILE__, size_t line = __LINE__, Throwable next = null)
 150    {
 151        super(msg, file, line, next);
 152    }
 153
 154    ///
 155    this(string msg, Throwable next, string file = __FILE__, size_t line = __LINE__)
 156    {
 157        super(msg, next, file, line);
 158    }
 159}
 160
 161
 162// Needs to be public so that SocketOSException can be thrown outside of
 163// std.socket (since it uses it as a default argument), but it probably doesn't
 164// need to actually show up in the docs, since there's not really any public
 165// need for it outside of being a default argument.
 166string formatSocketError(int err)
 167{
 168    version(Posix)
 169    {
 170        char[80] buf;
 171        const(char)* cs;
 172        version (linux)
 173        {
 174            cs = strerror_r(err, buf.ptr, buf.length);
 175        }
 176        else version (OSX)
 177        {
 178            auto errs = strerror_r(err, buf.ptr, buf.length);
 179            if (errs == 0)
 180                cs = buf.ptr;
 181            else
 182                return "Socket error " ~ to!string(err);
 183        }
 184        else version (FreeBSD)
 185        {
 186            auto errs = strerror_r(err, buf.ptr, buf.length);
 187            if (errs == 0)
 188                cs = buf.ptr;
 189            else
 190                return "Socket error " ~ to!string(err);
 191        }
 192        else
 193            static assert(0);
 194
 195        auto len = strlen(cs);
 196
 197        if(cs[len - 1] == '\n')
 198            len--;
 199        if(cs[len - 1] == '\r')
 200            len--;
 201        return cs[0 .. len].idup;
 202    }
 203    else
 204    version(Windows)
 205    {
 206        return sysErrorString(err);
 207    }
 208    else
 209        return "Socket error " ~ to!string(err);
 210}
 211
 212/// Retrieve the error message for the most recently encountered network error.
 213@property string lastSocketError()
 214{
 215    return formatSocketError(_lasterr());
 216}
 217
 218/// Socket exceptions representing network errors reported by the operating
 219/// system.
 220class SocketOSException: SocketException
 221{
 222    int errorCode;     /// Platform-specific error code.
 223
 224    ///
 225    this(string msg,
 226         string file = __FILE__,
 227         size_t line = __LINE__,
 228         Throwable next = null,
 229         int err = _lasterr(),
 230         string function(int) errorFormatter = &formatSocketError)
 231    {
 232        errorCode = err;
 233
 234        if (msg.length)
 235            super(msg ~ ": " ~ errorFormatter(err), file, line, next);
 236        else
 237            super(errorFormatter(err), file, line, next);
 238    }
 239
 240    ///
 241    this(string msg,
 242         Throwable next,
 243         string file = __FILE__,
 244         size_t line = __LINE__,
 245         int err = _lasterr(),
 246         string function(int) errorFormatter = &formatSocketError)
 247    {
 248        this(msg, file, line, next, err, errorFormatter);
 249    }
 250
 251    ///
 252    this(string msg,
 253         int err,
 254         string function(int) errorFormatter = &formatSocketError,
 255         string file = __FILE__,
 256         size_t line = __LINE__,
 257         Throwable next = null)
 258    {
 259        this(msg, file, line, next, err, errorFormatter);
 260    }
 261}
 262
 263/// Socket exceptions representing invalid parameters specified by user code.
 264class SocketParameterException: SocketException
 265{
 266    ///
 267    this(string msg, string file = __FILE__, size_t line = __LINE__, Throwable next = null)
 268    {
 269        super(msg, file, line, next);
 270    }
 271
 272    ///
 273    this(string msg, Throwable next, string file = __FILE__, size_t line = __LINE__)
 274    {
 275        super(msg, next, file, line);
 276    }
 277}
 278
 279/// Socket exceptions representing attempts to use network capabilities not
 280/// available on the current system.
 281class SocketFeatureException: SocketException
 282{
 283    ///
 284    this(string msg, string file = __FILE__, size_t line = __LINE__, Throwable next = null)
 285    {
 286        super(msg, file, line, next);
 287    }
 288
 289    ///
 290    this(string msg, Throwable next, string file = __FILE__, size_t line = __LINE__)
 291    {
 292        super(msg, next, file, line);
 293    }
 294}
 295
 296
 297/// Return $(D true) if the last socket operation failed because the socket
 298/// was in non-blocking mode and the operation would have blocked.
 299bool wouldHaveBlocked()
 300{
 301    version(Windows)
 302        return _lasterr() == WSAEWOULDBLOCK;
 303    else version(Posix)
 304        return _lasterr() == EAGAIN;
 305    else
 306        static assert(0);
 307}
 308
 309private __gshared typeof(&getnameinfo) getnameinfoPointer;
 310private __gshared typeof(&getaddrinfo) getaddrinfoPointer;
 311private __gshared typeof(&freeaddrinfo) freeaddrinfoPointer;
 312
 313shared static this()
 314{
 315    version(Windows)
 316    {
 317        WSADATA wd;
 318
 319        // Winsock will still load if an older version is present.
 320        // The version is just a request.
 321        int val;
 322        val = WSAStartup(0x2020, &wd);
 323        if(val)         // Request Winsock 2.2 for IPv6.
 324            throw new SocketOSException("Unable to initialize socket library", val);
 325
 326        // These functions may not be present on older Windows versions.
 327        // See the comment in InternetAddress.toHostNameString() for details.
 328        auto ws2Lib = GetModuleHandleA("ws2_32.dll");
 329        if (ws2Lib)
 330        {
 331            getnameinfoPointer = cast(typeof(getnameinfoPointer))
 332                                 GetProcAddress(ws2Lib, "getnameinfo");
 333            getaddrinfoPointer = cast(typeof(getaddrinfoPointer))
 334                                 GetProcAddress(ws2Lib, "getaddrinfo");
 335            freeaddrinfoPointer = cast(typeof(freeaddrinfoPointer))
 336                                 GetProcAddress(ws2Lib, "freeaddrinfo");
 337        }
 338    }
 339    else version(Posix)
 340    {
 341        getnameinfoPointer = &getnameinfo;
 342        getaddrinfoPointer = &getaddrinfo;
 343        freeaddrinfoPointer = &freeaddrinfo;
 344    }
 345}
 346
 347
 348shared static ~this()
 349{
 350    version(Windows)
 351    {
 352        WSACleanup();
 353    }
 354}
 355
 356/**
 357 * The communication domain used to resolve an address.
 358 */
 359enum AddressFamily: int
 360{
 361    UNSPEC =     AF_UNSPEC,     /// Unspecified address family
 362    UNIX =       AF_UNIX,       /// Local communication
 363    INET =       AF_INET,       /// Internet Protocol version 4
 364    IPX =        AF_IPX,        /// Novell IPX
 365    APPLETALK =  AF_APPLETALK,  /// AppleTalk
 366    INET6 =      AF_INET6,      /// Internet Protocol version 6
 367}
 368
 369
 370/**
 371 * Communication semantics
 372 */
 373enum SocketType: int
 374{
 375    STREAM =     SOCK_STREAM,           /// Sequenced, reliable, two-way communication-based byte streams
 376    DGRAM =      SOCK_DGRAM,            /// Connectionless, unreliable datagrams with a fixed maximum length; data may be lost or arrive out of order
 377    RAW =        SOCK_RAW,              /// Raw protocol access
 378    RDM =        SOCK_RDM,              /// Reliably-delivered message datagrams
 379    SEQPACKET =  SOCK_SEQPACKET,        /// Sequenced, reliable, two-way connection-based datagrams with a fixed maximum length
 380}
 381
 382
 383/**
 384 * Protocol
 385 */
 386enum ProtocolType: int
 387{
 388    IP =    IPPROTO_IP,         /// Internet Protocol version 4
 389    ICMP =  IPPROTO_ICMP,       /// Internet Control Message Protocol
 390    IGMP =  IPPROTO_IGMP,       /// Internet Group Management Protocol
 391    GGP =   IPPROTO_GGP,        /// Gateway to Gateway Protocol
 392    TCP =   IPPROTO_TCP,        /// Transmission Control Protocol
 393    PUP =   IPPROTO_PUP,        /// PARC Universal Packet Protocol
 394    UDP =   IPPROTO_UDP,        /// User Datagram Protocol
 395    IDP =   IPPROTO_IDP,        /// Xerox NS protocol
 396    RAW =   IPPROTO_RAW,        /// Raw IP packets
 397    IPV6 =  IPPROTO_IPV6,       /// Internet Protocol version 6
 398}
 399
 400
 401/**
 402 * $(D Protocol) is a class for retrieving protocol information.
 403 *
 404 * Example:
 405 * ---
 406 * auto proto = new Protocol;
 407 * writeln("About protocol TCP:");
 408 * if (proto.getProtocolByType(ProtocolType.TCP))
 409 * {
 410 *     writefln("  Name: %s", proto.name);
 411 *     foreach(string s; proto.aliases)
 412 *          writefln("  Alias: %s", s);
 413 * }
 414 * else
 415 *     writeln("  No information found");
 416 * ---
 417 */
 418class Protocol
 419{
 420    /// These members are populated when one of the following functions are called successfully:
 421    ProtocolType type;
 422    string name;                /// ditto
 423    string[] aliases;           /// ditto
 424
 425
 426    void populate(protoent* proto)
 427    {
 428        type = cast(ProtocolType)proto.p_proto;
 429        name = to!string(proto.p_name);
 430
 431        int i;
 432        for(i = 0;; i++)
 433        {
 434            if(!proto.p_aliases[i])
 435                break;
 436        }
 437
 438        if(i)
 439        {
 440            aliases = new string[i];
 441            for(i = 0; i != aliases.length; i++)
 442            {
 443                aliases[i] =
 444                    to!string(proto.p_aliases[i]);
 445            }
 446        }
 447        else
 448        {
 449            aliases = null;
 450        }
 451    }
 452
 453    /** Returns: false on failure */
 454    bool getProtocolByName(in char[] name)
 455    {
 456        protoent* proto;
 457        proto = getprotobyname(toStringz(name));
 458        if(!proto)
 459            return false;
 460        populate(proto);
 461        return true;
 462    }
 463
 464
 465    /** Returns: false on failure */
 466    // Same as getprotobynumber().
 467    bool getProtocolByType(ProtocolType type)
 468    {
 469        protoent* proto;
 470        proto = getprotobynumber(type);
 471        if(!proto)
 472            return false;
 473        populate(proto);
 474        return true;
 475    }
 476}
 477
 478
 479unittest
 480{
 481    softUnittest({
 482        Protocol proto = new Protocol;
 483        assert(proto.getProtocolByType(ProtocolType.TCP));
 484        //writeln("About protocol TCP:");
 485        //writefln("\tName: %s", proto.name);
 486        // foreach(string s; proto.aliases)
 487        // {
 488        //      writefln("\tAlias: %s", s);
 489        // }
 490        assert(proto.name == "tcp");
 491        assert(proto.aliases.length == 1 && proto.aliases[0] == "TCP");
 492    });
 493}
 494
 495
 496/**
 497 * $(D Service) is a class for retrieving service information.
 498 *
 499 * Example:
 500 * ---
 501 * auto serv = new Service;
 502 * writeln("About service epmap:");
 503 * if (serv.getServiceByName("epmap", "tcp"))
 504 * {
 505 *     writefln("  Service: %s", serv.name);
 506 *     writefln("  Port: %d", serv.port);
 507 *     writefln("  Protocol: %s", serv.protocolName);
 508 *     foreach (string s; serv.aliases)
 509 *          writefln("  Alias: %s", s);
 510 * }
 511 * else
 512 *     writefln("  No service for epmap.");
 513 * ---
 514 */
 515class Service
 516{
 517    /// These members are populated when one of the following functions are called successfully:
 518    string name;
 519    string[] aliases;           /// ditto
 520    ushort port;                /// ditto
 521    string protocolName;        /// ditto
 522
 523
 524    void populate(servent* serv)
 525    {
 526        name = to!string(serv.s_name);
 527        port = ntohs(cast(ushort)serv.s_port);
 528        protocolName = to!string(serv.s_proto);
 529
 530        int i;
 531        for(i = 0;; i++)
 532        {
 533            if(!serv.s_aliases[i])
 534                break;
 535        }
 536
 537        if(i)
 538        {
 539            aliases = new string[i];
 540            for(i = 0; i != aliases.length; i++)
 541            {
 542                aliases[i] =
 543                    to!string(serv.s_aliases[i]);
 544            }
 545        }
 546        else
 547        {
 548            aliases = null;
 549        }
 550    }
 551
 552    /**
 553     * If a protocol name is omitted, any protocol will be matched.
 554     * Returns: false on failure.
 555     */
 556    bool getServiceByName(in char[] name, in char[] protocolName = null)
 557    {
 558        servent* serv;
 559        serv = getservbyname(toStringz(name), protocolName !is null ? toStringz(protocolName) : null);
 560        if(!serv)
 561            return false;
 562        populate(serv);
 563        return true;
 564    }
 565
 566
 567    /// ditto
 568    bool getServiceByPort(ushort port, in char[] protocolName = null)
 569    {
 570        servent* serv;
 571        serv = getservbyport(port, protocolName !is null ? toStringz(protocolName) : null);
 572        if(!serv)
 573            return false;
 574        populate(serv);
 575        return true;
 576    }
 577}
 578
 579
 580unittest
 581{
 582    softUnittest({
 583        Service serv = new Service;
 584        if(serv.getServiceByName("epmap", "tcp"))
 585        {
 586            // writefln("About service epmap:");
 587            // writefln("\tService: %s", serv.name);
 588            // writefln("\tPort: %d", serv.port);
 589            // writefln("\tProtocol: %s", serv.protocolName);
 590            // foreach(string s; serv.aliases)
 591            // {
 592            //      writefln("\tAlias: %s", s);
 593            // }
 594            // For reasons unknown this is loc-srv on Wine and epmap on Windows
 595            assert(serv.name == "loc-srv" || serv.name == "epmap", serv.name);
 596            assert(serv.port == 135);
 597            assert(serv.protocolName == "tcp");
 598        }
 599        else
 600        {
 601            writefln("No service for epmap.");
 602        }
 603    });
 604}
 605
 606
 607/**
 608 * Class for exceptions thrown from an $(D InternetHost).
 609 */
 610class HostException: SocketOSException
 611{
 612    ///
 613    this(string msg, string file = __FILE__, size_t line = __LINE__, Throwable next = null, int err = _lasterr())
 614    {
 615        super(msg, file, line, next, err);
 616    }
 617
 618    ///
 619    this(string msg, Throwable next, string file = __FILE__, size_t line = __LINE__, int err = _lasterr())
 620    {
 621        super(msg, next, file, line, err);
 622    }
 623
 624    ///
 625    this(string msg, int err, string file = __FILE__, size_t line = __LINE__, Throwable next = null)
 626    {
 627        super(msg, next, file, line, err);
 628    }
 629}
 630
 631/**
 632 * $(D InternetHost) is a class for resolving IPv4 addresses.
 633 *
 634 * Consider using $(D getAddress), $(D parseAddress) and $(D Address) methods
 635 * instead of using this class directly.
 636 *
 637 * Example:
 638 * ---
 639 * auto ih = new InternetHost;
 640 *
 641 * // Forward lookup
 642 * writeln("About www.digitalmars.com:");
 643 * if (ih.getHostByName("www.digitalmars.com"))
 644 * {
 645 *     writefln("  Name: %s", ih.name);
 646 *     auto ip = InternetAddress.addrToString(ih.addrList[0]);
 647 *     writefln("  IP address: %s", ip);
 648 *     foreach (string s; ih.aliases)
 649 *          writefln("  Alias: %s", s);
 650 *     writeln("---");
 651 *
 652 *     // Reverse lookup
 653 *     writefln("About IP %s:", ip);
 654 *     if (ih.getHostByAddr(ih.addrList[0]))
 655 *     {
 656 *         writefln("  Name: %s", ih.name);
 657 *         foreach (string s; ih.aliases)
 658 *              writefln("  Alias: %s", s);
 659 *     }
 660 *     else
 661 *         writeln("  Reverse lookup failed");
 662 * }
 663 * else
 664 *     writeln("  Can't resolve www.digitalmars.com");
 665 * ---
 666 */
 667class InternetHost
 668{
 669    /// These members are populated when one of the following functions are called successfully:
 670    string name;
 671    string[] aliases;           /// ditto
 672    uint[] addrList;            /// ditto
 673
 674
 675    void validHostent(hostent* he)
 676    {
 677        if(he.h_addrtype != cast(int)AddressFamily.INET || he.h_length != 4)
 678            throw new HostException("Address family mismatch");
 679    }
 680
 681
 682    void populate(hostent* he)
 683    {
 684        int i;
 685        char* p;
 686
 687        name = to!string(he.h_name);
 688
 689        for(i = 0;; i++)
 690        {
 691            p = he.h_aliases[i];
 692            if(!p)
 693                break;
 694        }
 695
 696        if(i)
 697        {
 698            aliases = new string[i];
 699            for(i = 0; i != aliases.length; i++)
 700            {
 701                aliases[i] =
 702                    to!string(he.h_aliases[i]);
 703            }
 704        }
 705        else
 706        {
 707            aliases = null;
 708        }
 709
 710        for(i = 0;; i++)
 711        {
 712            p = he.h_addr_list[i];
 713            if(!p)
 714                break;
 715        }
 716
 717        if(i)
 718        {
 719            addrList = new uint[i];
 720            for(i = 0; i != addrList.length; i++)
 721            {
 722                addrList[i] = ntohl(*(cast(uint*)he.h_addr_list[i]));
 723            }
 724        }
 725        else
 726        {
 727            addrList = null;
 728        }
 729    }
 730
 731    private bool getHostNoSync(string opMixin, T)(T param)
 732    {
 733        mixin(opMixin);
 734        if (!he)
 735            return false;
 736        validHostent(he);
 737        populate(he);
 738        return true;
 739    }
 740
 741    version(Windows)
 742        alias getHostNoSync getHost;
 743    else
 744    {
 745        // posix systems use global state for return value, so we
 746        // must synchronize across all threads
 747        private bool getHost(string opMixin, T)(T param)
 748        {
 749            synchronized(this.classinfo)
 750                return getHostNoSync!(opMixin, T)(param);
 751        }
 752    }
 753
 754    /**
 755     * Resolve host name.
 756     * Returns: false if unable to resolve.
 757     */
 758    bool getHostByName(in char[] name)
 759    {
 760        static if (is(typeof(gethostbyname_r)))
 761        {
 762            return getHostNoSync!q{
 763                hostent he_v;
 764                hostent* he;
 765                ubyte[256] buffer_v = void;
 766                auto buffer = buffer_v[];
 767                auto param_z = std.string.toStringz(param);
 768                while (true)
 769                {
 770                    he = &he_v;
 771                    int errno;
 772                    if (gethostbyname_r(param_z, he, buffer.ptr, buffer.length, &he, &errno) == ERANGE)
 773                        buffer.length = buffer.length * 2;
 774                    else
 775                        break;
 776                }
 777            }(name);
 778        }
 779        else
 780        {
 781            return getHost!q{
 782                auto he = gethostbyname(toStringz(param));
 783            }(name);
 784        }
 785    }
 786
 787    /**
 788     * Resolve IPv4 address number.
 789     *
 790     * Params:
 791     *   addr = The IPv4 address to resolve, in host byte order.
 792     * Returns:
 793     *   false if unable to resolve.
 794     */
 795    bool getHostByAddr(uint addr)
 796    {
 797        return getHost!q{
 798            auto x = htonl(param);
 799            auto he = gethostbyaddr(&x, 4, cast(int)AddressFamily.INET);
 800        }(addr);
 801    }
 802
 803    /**
 804     * Same as previous, but addr is an IPv4 address string in the
 805     * dotted-decimal form $(I a.b.c.d).
 806     * Returns: false if unable to resolve.
 807     */
 808    bool getHostByAddr(in char[] addr)
 809    {
 810        return getHost!q{
 811            auto x = inet_addr(std.string.toStringz(param));
 812            enforce(x != INADDR_NONE,
 813                new SocketParameterException("Invalid IPv4 address"));
 814            auto he = gethostbyaddr(&x, 4, cast(int)AddressFamily.INET);
 815        }(addr);
 816    }
 817}
 818
 819
 820unittest
 821{
 822    InternetHost ih = new InternetHost;
 823
 824    ih.getHostByAddr(0x7F_00_00_01);
 825    assert(ih.addrList[0] == 0x7F_00_00_01);
 826    ih.getHostByAddr("127.0.0.1");
 827    assert(ih.addrList[0] == 0x7F_00_00_01);
 828
 829    softUnittest({
 830        if (!ih.getHostByName("www.digitalmars.com"))
 831            return;             // don't fail if not connected to internet
 832        //writefln("addrList.length = %d", ih.addrList.length);
 833        assert(ih.addrList.length);
 834        InternetAddress ia = new InternetAddress(ih.addrList[0], InternetAddress.PORT_ANY);
 835        assert(ih.name == "www.digitalmars.com" || ih.name == "digitalmars.com",
 836               ih.name);
 837        // writefln("IP address = %s", ia.toAddrString());
 838        // writefln("name = %s", ih.name);
 839        // foreach(int i, string s; ih.aliases)
 840        // {
 841        //      writefln("aliases[%d] = %s", i, s);
 842        // }
 843        // writefln("---");
 844
 845        //assert(ih.getHostByAddr(ih.addrList[0]));
 846        // writefln("name = %s", ih.name);
 847        // foreach(int i, string s; ih.aliases)
 848        // {
 849        //      writefln("aliases[%d] = %s", i, s);
 850        // }
 851    });
 852}
 853
 854
 855/// Holds information about a socket _address retrieved by $(D getAddressInfo).
 856struct AddressInfo
 857{
 858    AddressFamily family;   /// Address _family
 859    SocketType type;        /// Socket _type
 860    ProtocolType protocol;  /// Protocol
 861    Address address;        /// Socket _address
 862    string canonicalName;   /// Canonical name, when $(D AddressInfoFlags.CANONNAME) is used.
 863}
 864
 865// A subset of flags supported on all platforms with getaddrinfo.
 866/// Specifies option flags for $(D getAddressInfo).
 867enum AddressInfoFlags: int
 868{
 869    /// The resulting addresses will be used in a call to $(D Socket.bind).
 870    PASSIVE = AI_PASSIVE,
 871
 872    /// The canonical name is returned in $(D canonicalName) member in the first $(D AddressInfo).
 873    CANONNAME = AI_CANONNAME,
 874
 875    /// The $(D node) parameter passed to $(D getAddressInfo) must be a numeric string.
 876    /// This will suppress any potentially lengthy network host address lookups.
 877    NUMERICHOST = AI_NUMERICHOST,
 878}
 879
 880
 881/// On POSIX, getaddrinfo uses its own error codes, and thus has its own
 882/// formatting function.
 883private string formatGaiError(int err)
 884{
 885    version(Windows)
 886    {
 887        return sysErrorString(err);
 888    }
 889    else
 890    {
 891        synchronized
 892            return to!string(gai_strerror(err));
 893    }
 894}
 895
 896/**
 897 * Provides _protocol-independent translation from host names to socket
 898 * addresses. If advanced functionality is not required, consider using
 899 * $(D getAddress) for compatibility with older systems.
 900 *
 901 * Returns: Array with one $(D AddressInfo) per socket address.
 902 *
 903 * Throws: $(D SocketOSException) on failure, or $(D SocketFeatureException)
 904 * if this functionality is not available on the current system.
 905 *
 906 * Params:
 907 *  node     = string containing host name or numeric address
 908 *  options  = optional additional parameters, identified by type:
 909 *             $(UL $(LI $(D string) - service name or port number)
 910 *                  $(LI $(D AddressInfoFlags) - option flags)
 911 *                  $(LI $(D AddressFamily) - address family to filter by)
 912 *                  $(LI $(D SocketType) - socket type to filter by)
 913 *                  $(LI $(D ProtocolType) - protocol to filter by))
 914 *
 915 * Example:
 916 * ---
 917 * // Roundtrip DNS resolution
 918 * auto results = getAddressInfo("www.digitalmars.com");
 919 * assert(results[0].address.toHostNameString() ==
 920 *     "digitalmars.com");
 921 *
 922 * // Canonical name
 923 * results = getAddressInfo("www.digitalmars.com",
 924 *     AddressInfoFlags.CANONNAME);
 925 * assert(results[0].canonicalName == "digitalmars.com");
 926 *
 927 * // IPv6 resolution
 928 * results = getAddressInfo("ipv6.google.com");
 929 * assert(results[0].family == AddressFamily.INET6);
 930 *
 931 * // Multihomed resolution
 932 * results = getAddressInfo("google.com");
 933 * assert(results.length > 1);
 934 *
 935 * // Parsing IPv4
 936 * results = getAddressInfo("127.0.0.1",
 937 *     AddressInfoFlags.NUMERICHOST);
 938 * assert(results.length && results[0].family ==
 939 *     AddressFamily.INET);
 940 *
 941 * // Parsing IPv6
 942 * results = getAddressInfo("::1",
 943 *     AddressInfoFlags.NUMERICHOST);
 944 * assert(results.length && results[0].family ==
 945 *     AddressFamily.INET6);
 946 * ---
 947 */
 948AddressInfo[] getAddressInfo(T...)(in char[] node, T options)
 949{
 950    const(char)[] service = null;
 951    addrinfo hints;
 952    hints.ai_family = AF_UNSPEC;
 953
 954    foreach (option; options)
 955    {
 956        static if (is(typeof(option) : const(char)[]))
 957            service = option;
 958        else
 959        static if (is(typeof(option) == AddressInfoFlags))
 960            hints.ai_flags |= option;
 961        else
 962        static if (is(typeof(option) == AddressFamily))
 963            hints.ai_family = option;
 964        else
 965        static if (is(typeof(option) == SocketType))
 966            hints.ai_socktype = option;
 967        else
 968        static if (is(typeof(option) == ProtocolType))
 969            hints.ai_protocol = option;
 970        else
 971            static assert(0, "Unknown getAddressInfo option type: " ~ typeof(option).stringof);
 972    }
 973
 974    return getAddressInfoImpl(node, service, &hints);
 975}
 976
 977private AddressInfo[] getAddressInfoImpl(in char[] node, in char[] service, addrinfo* hints)
 978{
 979    if (getaddrinfoPointer && freeaddrinfoPointer)
 980    {
 981        addrinfo* ai_res;
 982
 983        int ret = getaddrinfoPointer(
 984            node is null ? null : std.string.toStringz(node),
 985            service is null ? null : std.string.toStringz(service),
 986            hints, &ai_res);
 987        scope(exit) if (ai_res) freeaddrinfoPointer(ai_res);
 988        enforce(ret == 0, new SocketOSException("getaddrinfo error", ret, &formatGaiError));
 989
 990        AddressInfo[] result;
 991        // Use const to force UnknownAddressReference to copy the sockaddr.
 992        for (const(addrinfo)* ai = ai_res; ai; ai = ai.ai_next)
 993            result ~= AddressInfo(
 994                cast(AddressFamily) ai.ai_family,
 995                cast(SocketType   ) ai.ai_socktype,
 996                cast(ProtocolType ) ai.ai_protocol,
 997                new UnknownAddressReference(ai.ai_addr, cast(socklen_t) ai.ai_addrlen),
 998                ai.ai_canonname ? to!string(ai.ai_canonname) : null);
 999
1000        assert(result.length > 0);
1001        return result;
1002    }
1003
1004    throw new SocketFeatureException("Address info lookup is not available " ~
1005        "on this system.");
1006}
1007
1008
1009unittest
1010{
1011    softUnittest({
1012        if (getaddrinfoPointer)
1013        {
1014            // Roundtrip DNS resolution
1015            auto results = getAddressInfo("www.digitalmars.com");
1016            assert(results[0].address.toHostNameString() == "digitalmars.com");
1017
1018            // Canonical name
1019            results = getAddressInfo("www.digitalmars.com",
1020                AddressInfoFlags.CANONNAME);
1021            assert(results[0].canonicalName == "digitalmars.com");
1022
1023            // IPv6 resolution
1024            //results = getAddressInfo("ipv6.google.com");
1025            //assert(results[0].family == AddressFamily.INET6);
1026
1027            // Multihomed resolution
1028            //results = getAddressInfo("google.com");
1029            //assert(results.length > 1);
1030
1031            // Parsing IPv4
1032            results = getAddressInfo("127.0.0.1", AddressInfoFlags.NUMERICHOST);
1033            assert(results.length && results[0].family == AddressFamily.INET);
1034
1035            // Parsing IPv6
1036            results = getAddressInfo("::1", AddressInfoFlags.NUMERICHOST);
1037            assert(results.length && results[0].family == AddressFamily.INET6);
1038        }
1039    });
1040}
1041
1042
1043private ushort serviceToPort(in char[] service)
1044{
1045    if (service == "")
1046        return InternetAddress.PORT_ANY;
1047    else
1048    if (isNumeric(service))
1049        return to!ushort(service);
1050    else
1051    {
1052        auto s = new Service();
1053        s.getServiceByName(service);
1054        return s.port;
1055    }
1056}
1057
1058/**
1059 * Provides _protocol-independent translation from host names to socket
1060 * addresses. Uses $(D getAddressInfo) if the current system supports it,
1061 * and $(D InternetHost) otherwise.
1062 *
1063 * Returns: Array with one $(D Address) instance per socket address.
1064 *
1065 * Throws: $(D SocketOSException) on failure.
1066 *
1067 * Example:
1068 * ---
1069 * writeln("Resolving www.digitalmars.com:");
1070 * try
1071 * {
1072 *     auto addresses = getAddress("www.digitalmars.com");
1073 *     foreach (address; addresses)
1074 *         writefln("  IP: %s", address.toAddrString());
1075 * }
1076 * catch (SocketException e)
1077 *     writefln("  Lookup failed: %s", e.msg);
1078 * ---
1079 */
1080Address[] getAddress(in char[] hostname, in char[] service = null)
1081{
1082    if (getaddrinfoPointer && freeaddrinfoPointer)
1083    {
1084        // use getAddressInfo
1085        Address[] results;
1086        auto infos = getAddressInfo(hostname, service);
1087        foreach (ref info; infos)
1088            results ~= info.address;
1089        return results;
1090    }
1091    else
1092        return getAddress(hostname, serviceToPort(service));
1093}
1094
1095/// ditto
1096Address[] getAddress(in char[] hostname, ushort port)
1097{
1098    if (getaddrinfoPointer && freeaddrinfoPointer)
1099        return getAddress(hostname, to!string(port));
1100    else
1101    {
1102        // use getHostByName
1103        auto ih = new InternetHost;
1104        if (!ih.getHostByName(hostname))
1105            throw new AddressException(
1106                        text("Unable to resolve host '", hostname, "'"));
1107
1108        Address[] results;
1109        foreach (uint addr; ih.addrList)
1110            results ~= new InternetAddress(addr, port);
1111        return results;
1112    }
1113}
1114
1115
1116unittest
1117{
1118    softUnittest({
1119        auto addresses = getAddress("63.105.9.61");
1120        assert(addresses.length && addresses[0].toAddrString() == "63.105.9.61");
1121
1122        if (getaddrinfoPointer)
1123        {
1124            // test via gethostbyname
1125            auto getaddrinfoPointerBackup = getaddrinfoPointer;
1126            getaddrinfoPointer = null;
1127            scope(exit) getaddrinfoPointer = getaddrinfoPointerBackup;
1128
1129            addresses = getAddress("63.105.9.61");
1130            assert(addresses.length && addresses[0].toAddrString() == "63.105.9.61");
1131        }
1132    });
1133}
1134
1135
1136/**
1137 * Provides _protocol-independent parsing of network addresses. Does not
1138 * attempt name resolution. Uses $(D getAddressInfo) with
1139 * $(D AddressInfoFlags.NUMERICHOST) if the current system supports it, and
1140 * $(D InternetAddress) otherwise.
1141 *
1142 * Returns: An $(D Address) instance representing specified address.
1143 *
1144 * Throws: $(D SocketException) on failure.
1145 *
1146 * Example:
1147 * ---
1148 * writeln("Enter IP address:");
1149 * string ip = readln().chomp();
1150 * try
1151 * {
1152 *     Address address = parseAddress(ip);
1153 *     writefln("Looking up reverse of %s:",
1154 *         address.toAddrString());
1155 *     try
1156 *     {
1157 *         string reverse = address.toHostNameString();
1158 *         if (reverse)
1159 *             writefln("  Reverse name: %s", reverse);
1160 *         else
1161 *             writeln("  Reverse hostname not found.");
1162 *     }
1163 *     catch (SocketException e)
1164 *         writefln("  Lookup error: %s", e.msg);
1165 * }
1166 * catch (SocketException e)
1167 * {
1168 *     writefln("  %s is not a valid IP address: %s",
1169 *         ip, e.msg);
1170 * }
1171 * ---
1172 */
1173Address parseAddress(in char[] hostaddr, in char[] service = null)
1174{
1175    if (getaddrinfoPointer && freeaddrinfoPointer)
1176        return getAddressInfo(hostaddr, service, AddressInfoFlags.NUMERICHOST)[0].address;
1177    else
1178        return parseAddress(hostaddr, serviceToPort(service));
1179}
1180
1181/// ditto
1182Address parseAddress(in char[] hostaddr, ushort port)
1183{
1184    if (getaddrinfoPointer && freeaddrinfoPointer)
1185        return parseAddress(hostaddr, to!string(port));
1186    else
1187    {
1188        auto in4_addr = InternetAddress.parse(hostaddr);
1189        enforce(in4_addr != InternetAddress.ADDR_NONE,
1190            new SocketParameterException("Invalid IP address"));
1191        return new InternetAddress(in4_addr, port);
1192    }
1193}
1194
1195
1196unittest
1197{
1198    softUnittest({
1199        auto address = parseAddress("63.105.9.61");
1200        assert(address.toAddrString() == "63.105.9.61");
1201
1202        if (getaddrinfoPointer)
1203        {
1204            // test via inet_addr
1205            auto getaddrinfoPointerBackup = getaddrinfoPointer;
1206            getaddrinfoPointer = null;
1207            scope(exit) getaddrinfoPointer = getaddrinfoPointerBackup;
1208
1209            address = parseAddress("63.105.9.61");
1210            assert(address.toAddrString() == "63.105.9.61");
1211        }
1212
1213        assert(collectException!SocketException(parseAddress("Invalid IP address")));
1214    });
1215}
1216
1217
1218/**
1219 * Class for exceptions thrown from an $(D Address).
1220 */
1221class AddressException: SocketOSException
1222{
1223    ///
1224    this(string msg, string file = __FILE__, size_t line = __LINE__, Throwable next = null, int err = _lasterr())
1225    {
1226        super(msg, file, line, next, err);
1227    }
1228
1229    ///
1230    this(string msg, Throwable next, string file = __FILE__, size_t line = __LINE__, int err = _lasterr())
1231    {
1232        super(msg, next, file, line, err);
1233    }
1234
1235    ///
1236    this(string msg, int err, string file = __FILE__, size_t line = __LINE__, Throwable next = null)
1237    {
1238        super(msg, next, file, line, err);
1239    }
1240}
1241
1242
1243/**
1244 * $(D Address) is an abstract class for representing a socket addresses.
1245 *
1246 * Example:
1247 * ---
1248 * writeln("About www.google.com port 80:");
1249 * try
1250 * {
1251 *     Address[] addresses = getAddress("www.google.com", 80);
1252 *     writefln("  %d addresses found.", addresses.length);
1253 *     foreach (int i, Address a; addresses)
1254 *     {
1255 *         writefln("  Address %d:", i+1);
1256 *         writefln("    IP address: %s", a.toAddrString());
1257 *         writefln("    Hostname: %s", a.toHostNameString());
1258 *         writefln("    Port: %s", a.toPortString());
1259 *         writefln("    Service name: %s",
1260 *             a.toServiceNameString());
1261 *     }
1262 * }
1263 * catch (SocketException e)
1264 *     writefln("  Lookup error: %s", e.msg);
1265 * ---
1266 */
1267abstract class Address
1268{
1269    /// Returns pointer to underlying $(D sockaddr) structure.
1270    abstract @property sockaddr* name();
1271    abstract @property const(sockaddr)* name() const; /// ditto
1272
1273    /// Returns actual size of underlying $(D sockaddr) structure.
1274    abstract @property socklen_t nameLen() const;
1275
1276    /// Family of this address.
1277    @property AddressFamily addressFamily() const
1278    {
1279        return cast(AddressFamily) name.sa_family;
1280    }
1281
1282    // Common code for toAddrString and toHostNameString
1283    private final string toHostString(bool numeric) const
1284    {
1285        // getnameinfo() is the recommended way to perform a reverse (name)
1286        // lookup on both Posix and Windows. However, it is only available
1287        // on Windows XP and above, and not included with the WinSock import
1288        // libraries shipped with DMD. Thus, we check for getnameinfo at
1289        // runtime in the shared module constructor, and use it if it's
1290        // available in the base class method. Classes for specific network
1291        // families (e.g. InternetHost) override this method and use a
1292        // deprecated, albeit commonly-available method when getnameinfo()
1293        // is not available.
1294        // http://technet.microsoft.com/en-us/library/aa450403.aspx
1295        if (getnameinfoPointer)
1296        {
1297            auto buf = new char[NI_MAXHOST];
1298            auto ret = getnameinfoPointer(
1299                        name, nameLen,
1300                        buf.ptr, cast(uint)buf.length,
1301                        null, 0,
1302                        numeric ? NI_NUMERICHOST : NI_NAMEREQD);
1303
1304            if (!numeric)
1305            {
1306                if (ret==EAI_NONAME)
1307                    return null;
1308                version(Windows)
1309                    if (ret==WSANO_DATA)
1310                        return null;
1311            }
1312
1313            enforce(ret == 0, new AddressException("Could not get " ~
1314                        (numeric ? "host address" : "host name")));
1315            return assumeUnique(buf[0 .. strlen(buf.ptr)]);
1316        }
1317
1318        throw new SocketFeatureException((numeric ? "Host address" : "Host name") ~
1319            " lookup for this address family is not available on this system.");
1320    }
1321
1322    // Common code for toPortString and toServiceNameString
1323    private final string toServiceString(bool numeric) const
1324    {
1325        // See toHostNameString() for details about getnameinfo().
1326        if (getnameinfoPointer)
1327        {
1328            auto buf = new char[NI_MAXSERV];
1329            enforce(getnameinfoPointer(
1330                        name, nameLen,
1331                        null, 0,
1332                        buf.ptr, cast(uint)buf.length,
1333                        numeric ? NI_NUMERICSERV : NI_NAMEREQD
1334                    ) == 0, new AddressException("Could not get " ~
1335                        (numeric ? "port number" : "service name")));
1336            return assumeUnique(buf[0 .. strlen(buf.ptr)]);
1337        }
1338
1339        throw new SocketFeatureException((numeric ? "Port number" : "Service name") ~
1340            " lookup for this address family is not available on this system.");
1341    }
1342
1343    /**
1344     * Attempts to retrieve the host address as a human-readable string.
1345     *
1346     * Throws: $(D AddressException) on failure, or $(D SocketFeatureException)
1347     * if address retrieval for this address family is not available on the
1348     * current system.
1349     */
1350    string toAddrString() const
1351    {
1352        return toHostString(true);
1353    }
1354
1355    /**
1356     * Attempts to retrieve the host name as a fully qualified domain name.
1357     *
1358     * Returns: The FQDN corresponding to this $(D Address), or $(D null) if
1359     * the host name did not resolve.
1360     *
1361     * Throws: $(D AddressException) on error, or $(D SocketFeatureException)
1362     * if host name lookup for this address family is not available on the
1363     * current system.
1364     */
1365    string toHostNameString() const
1366    {
1367        return toHostString(false);
1368    }
1369
1370    /**
1371     * Attempts to retrieve the numeric port number as a string.
1372     *
1373     * Throws: $(D AddressException) on failure, or $(D SocketFeatureException)
1374     * if port number retrieval for this address family is not available on the
1375     * current system.
1376     */
1377    string toPortString() const
1378    {
1379        return toServiceString(true);
1380    }
1381
1382    /**
1383     * Attempts to retrieve the service name as a string.
1384     *
1385     * Throws: $(D AddressException) on failure, or $(D SocketFeatureException)
1386     * if service name lookup for this address family is not available on the
1387     * current system.
1388     */
1389    string toServiceNameString() const
1390    {
1391        return toServiceString(false);
1392    }
1393
1394    /// Human readable string representing this address.
1395    override string toString() const
1396    {
1397        try
1398        {
1399            string host = toAddrString();
1400            string port = toPortString();
1401            if (host.indexOf(':') >= 0)
1402                return "[" ~ host ~ "]:" ~ port;
1403            else
1404                return host ~ ":" ~ port;
1405        }
1406        catch (SocketException)
1407            return "Unknown";
1408    }
1409}
1410
1411/**
1412 * $(D UnknownAddress) encapsulates an unknown socket address.
1413 */
1414class UnknownAddress: Address
1415{
1416protected:
1417    sockaddr sa;
1418
1419
1420public:
1421    override @property sockaddr* name()
1422    {
1423        return &sa;
1424    }
1425
1426    override @property const(sockaddr)* name() const
1427    {
1428        return &sa;
1429    }
1430
1431
1432    override @property socklen_t nameLen() const
1433    {
1434        return cast(socklen_t) sa.sizeof;
1435    }
1436
1437}
1438
1439
1440/**
1441 * $(D UnknownAddressReference) encapsulates a reference to an arbitrary
1442 * socket address.
1443 */
1444class UnknownAddressReference: Address
1445{
1446protected:
1447    sockaddr* sa;
1448    socklen_t len;
1449
1450public:
1451    /// Constructs an $(D Address) with a reference to the specified $(D sockaddr).
1452    this(sockaddr* sa, socklen_t len)
1453    {
1454        this.sa  = sa;
1455        this.len = len;
1456    }
1457
1458    /// Constructs an $(D Address) with a copy of the specified $(D sockaddr).
1459    this(const(sockaddr)* sa, socklen_t len)
1460    {
1461        this.sa = cast(sockaddr*) (cast(ubyte*)sa)[0..len].dup.ptr;
1462        this.len = len;
1463    }
1464
1465    override @property sockaddr* name()
1466    {
1467        return sa;
1468    }
1469
1470    override @property const(sockaddr)* name() const
1471    {
1472        return sa;
1473    }
1474
1475
1476    override @property socklen_t nameLen() const
1477    {
1478        return cast(socklen_t) len;
1479    }
1480}
1481
1482
1483/**
1484 * $(D InternetAddress) encapsulates an IPv4 (Internet Protocol version 4)
1485 * socket address.
1486 *
1487 * Consider using $(D getAddress), $(D parseAddress) and $(D Address) methods
1488 * instead of using this class directly.
1489 */
1490class InternetAddress: Address
1491{
1492protected:
1493    sockaddr_in sin;
1494
1495
1496    this()
1497    {
1498    }
1499
1500
1501public:
1502    override @property sockaddr* name()
1503    {
1504        return cast(sockaddr*)&sin;
1505    }
1506
1507    override @property const(sockaddr)* name() const
1508    {
1509        return cast(const(sockaddr)*)&sin;
1510    }
1511
1512
1513    override @property socklen_t nameLen() const
1514    {
1515        return cast(socklen_t) sin.sizeof;
1516    }
1517
1518
1519    enum uint ADDR_ANY = INADDR_ANY;         /// Any IPv4 host address.
1520    enum uint ADDR_NONE = INADDR_NONE;       /// An invalid IPv4 host address.
1521    enum ushort PORT_ANY = 0;                /// Any IPv4 port number.
1522
1523    /// Returns the IPv4 _port number (in host byte order).
1524    @property ushort port() const
1525    {
1526        return ntohs(sin.sin_port);
1527    }
1528
1529    /// Returns the IPv4 address number (in host byte order).
1530    @property uint addr() const
1531    {
1532        return ntohl(sin.sin_addr.s_addr);
1533    }
1534
1535    /**
1536     * Construct a new $(D InternetAddress).
1537     * Params:
1538     *   addr = an IPv4 address string in the dotted-decimal form a.b.c.d,
1539     *          or a host name which will be resolved using an $(D InternetHost)
1540     *          object.
1541     *   port = port number, may be $(D PORT_ANY).
1542     */
1543    this(in char[] addr, ushort port)
1544    {
1545        uint uiaddr = parse(addr);
1546        if(ADDR_NONE == uiaddr)
1547        {
1548            InternetHost ih = new InternetHost;
1549            if(!ih.getHostByName(addr))
1550                //throw new AddressException("Invalid internet address");
1551                throw new AddressException(
1552                          text("Unable to resolve host '", addr, "'"));
1553            uiaddr = ih.addrList[0];
1554        }
1555        sin.sin_family = AddressFamily.INET;
1556        sin.sin_addr.s_addr = htonl(uiaddr);
1557        sin.sin_port = htons(port);
1558    }
1559
1560    /**
1561     * Construct a new $(D InternetAddress).
1562     * Params:
1563     *   addr = (optional) an IPv4 address in host byte order, may be $(D ADDR_ANY).
1564     *   port = port number, may be $(D PORT_ANY).
1565     */
1566    this(uint addr, ushort port)
1567    {
1568        sin.sin_family = AddressFamily.INET;
1569        sin.sin_addr.s_addr = htonl(addr);
1570        sin.sin_port = htons(port);
1571    }
1572
1573    /// ditto
1574    this(ushort port)
1575    {
1576        sin.sin_family = AddressFamily.INET;
1577        sin.sin_addr.s_addr = ADDR_ANY;
1578        sin.sin_port = htons(port);
1579    }
1580
1581    /// Human readable string representing the IPv4 address in dotted-decimal form.
1582    override string toAddrString() const
1583    {
1584        return to!string(inet_ntoa(sin.sin_addr));
1585    }
1586
1587    /// Human readable string representing the IPv4 port.
1588    override string toPortString() const
1589    {
1590        return std.conv.to!string(port);
1591    }
1592
1593    /**
1594     * Attempts to retrieve the host name as a fully qualified domain name.
1595     *
1596     * Returns: The FQDN corresponding to this $(D InternetAddress), or
1597     * $(D null) if the host name did not resolve.
1598     *
1599     * Throws: $(D AddressException) on error.
1600     */
1601    override string toHostNameString() const
1602    {
1603        // getnameinfo() is the recommended way to perform a reverse (name)
1604        // lookup on both Posix and Windows. However, it is only available
1605        // on Windows XP and above, and not included with the WinSock import
1606        // libraries shipped with DMD. Thus, we check for getnameinfo at
1607        // runtime in the shared module constructor, and fall back to the
1608        // deprecated getHostByAddr() if it could not be found. See also:
1609        // http://technet.microsoft.com/en-us/library/aa450403.aspx
1610
1611        if (getnameinfoPointer)
1612            return super.toHostNameString();
1613        else
1614        {
1615            auto host = new InternetHost();
1616            if (!host.getHostByAddr(ntohl(sin.sin_addr.s_addr)))
1617                return null;
1618            return host.name;
1619        }
1620    }
1621
1622    /**
1623     * Parse an IPv4 address string in the dotted-decimal form $(I a.b.c.d)
1624     * and return the number.
1625     * Returns: If the string is not a legitimate IPv4 address,
1626     * $(D ADDR_NONE) is returned.
1627     */
1628    static uint parse(in char[] addr)
1629    {
1630        return ntohl(inet_addr(std.string.toStringz(addr)));
1631    }
1632
1633    /**
1634     * Convert an IPv4 address number in host byte order to a human readable
1635     * string representing the IPv4 address in dotted-decimal form.
1636     */
1637    static string addrToString(uint addr)
1638    {
1639        in_addr sin_addr;
1640        sin_addr.s_addr = htonl(addr);
1641        return to!string(inet_ntoa(sin_addr));
1642    }
1643}
1644
1645
1646unittest
1647{
1648    softUnittest({
1649        const InternetAddress ia = new InternetAddress("63.105.9.61", 80);
1650        assert(ia.toString() == "63.105.9.61:80");
1651    });
1652
1653    softUnittest({
1654        // test reverse lookup
1655        auto ih = new InternetHost;
1656        if (ih.getHostByName("digitalmars.com"))
1657        {
1658            const ia = new InternetAddress(ih.addrList[0], 80);
1659            assert(ia.toHostNameString() == "digitalmars.com");
1660
1661            if (getnameinfoPointer)
1662            {
1663                // test reverse lookup, via gethostbyaddr
1664                auto getnameinfoPointerBackup = getnameinfoPointer;
1665                getnameinfoPointer = null;
1666                scope(exit) getnameinfoPointer = getnameinfoPointerBackup;
1667
1668                assert(ia.toHostNameString() == "digitalmars.com");
1669            }
1670        }
1671    });
1672
1673    version (SlowTests)
1674    softUnittest({
1675        // test failing reverse lookup
1676        const InternetAddress ia = new InternetAddress("127.114.111.120", 80);
1677        assert(ia.toHostNameString() is null);
1678
1679        if (getnameinfoPointer)
1680        {
1681            // test failing reverse lookup, via gethostbyaddr
1682            auto getnameinfoPointerBackup = getnameinfoPointer;
1683            getnameinfoPointer = null;
1684            scope(exit) getnameinfoPointer = getnameinfoPointerBackup;
1685
1686            assert(ia.toHostNameString() is null);
1687        }
1688    });
1689}
1690
1691
1692/**
1693 * $(D Internet6Address) encapsulates an IPv6 (Internet Protocol version 6)
1694 * socket address.
1695 *
1696 * Consider using $(D getAddress), $(D parseAddress) and $(D Address) methods
1697 * instead of using this class directly.
1698 */
1699class Internet6Address: Address
1700{
1701protected:
1702    sockaddr_in6 sin6;
1703
1704
1705    this()
1706    {
1707    }
1708
1709
1710public:
1711    override @property sockaddr* name()
1712    {
1713        return cast(sockaddr*)&sin6;
1714    }
1715
1716    override @property const(sockaddr)* name() const
1717    {
1718        return cast(const(sockaddr)*)&sin6;
1719    }
1720
1721
1722    override @property socklen_t nameLen() const
1723    {
1724        return cast(socklen_t) sin6.sizeof;
1725    }
1726
1727
1728    /// Any IPv6 host address.
1729    static @property ref const(ubyte)[16] ADDR_ANY()
1730    {
1731        const(ubyte)[16]* addr;
1732        static if (is(typeof(IN6ADDR_ANY)))
1733            return addr = &IN6ADDR_ANY.s6_addr, *addr;
1734        else
1735        static if (is(typeof(in6addr_any)))
1736            return addr = &i

Large files files are truncated, but you can click here to view the full file