Wolfgang Thallergcc16 merge934c3885 months agoHistory
3925 lines · 114.8 KB · D
RawDownload
// Written in the D programming language // NOTE: When working on this module, be sure to run tests with -debug=std_socket// E.g.: dmd -version=StdUnittest -debug=std_socket -unittest -main -run socket// This will enable some tests which are too slow or flaky to run as part of CI. /*        Copyright (C) 2004-2011 Christopher E. Miller         socket.d 1.4        Jan 2011         Thanks to Benjamin Herr for his assistance. */ /** * Socket primitives. * Example: See [listener.d](https://github.com/dlang/undeaD/blob/master/dmdsamples/listener.d) and [htmlget.d](https://github.com/dlang/undeaD/blob/master/dmdsamples/htmlget.d) * License: $(HTTP www.boost.org/LICENSE_1_0.txt, Boost License 1.0). * Authors: Christopher E. Miller, $(HTTP klickverbot.at, David Nadlinger), *      $(HTTP thecybershadow.net, Vladimir Panteleev) * Source:  $(PHOBOSSRC std/socket.d) */ module std.socket; import core.stdc.stdint, core.stdc.stdlib, core.stdc.string, std.conv, std.string; import core.stdc.config;import core.time : dur, Duration;import std.exception; import std.internal.cstring; version (iOS)    version = iOSDerived;else version (TVOS)    version = iOSDerived;else version (WatchOS)    version = iOSDerived; @safe: version (Windows){    pragma (lib, "ws2_32.lib");    pragma (lib, "wsock32.lib");     import core.sys.windows.winbase, std.windows.syserror;    public import core.sys.windows.winsock2;    private alias _ctimeval = core.sys.windows.winsock2.timeval;    private alias _clinger = core.sys.windows.winsock2.linger;     enum socket_t : SOCKET { INVALID_SOCKET }    private const int _SOCKET_ERROR = SOCKET_ERROR;     /**     * On Windows, there is no `SO_REUSEPORT`.     * However, `SO_REUSEADDR` is equivalent to `SO_REUSEPORT` there.     * $(LINK https://learn.microsoft.com/en-us/windows/win32/winsock/using-so-reuseaddr-and-so-exclusiveaddruse)     */    private enum SO_REUSEPORT = SO_REUSEADDR;     private int _lasterr() nothrow @nogc    {        return WSAGetLastError();    }}else version (Posix){    version (linux)    {        enum : int        {            TCP_KEEPIDLE  = 4,            TCP_KEEPINTVL = 5        }    }     public import core.sys.posix.netinet.in_;    import core.sys.posix.arpa.inet;    import core.sys.posix.fcntl;    import core.sys.posix.netdb;    import core.sys.posix.netinet.tcp;    import core.sys.posix.sys.select;    import core.sys.posix.sys.socket;    import core.sys.posix.sys.time;    import core.sys.posix.sys.un : sockaddr_un;    import core.sys.posix.unistd;    private alias _ctimeval = core.sys.posix.sys.time.timeval;    private alias _clinger = core.sys.posix.sys.socket.linger;     import core.stdc.errno;     enum socket_t : int32_t { _init = -1 }    private const int _SOCKET_ERROR = -1;     private enum : int    {        SD_RECEIVE = SHUT_RD,        SD_SEND    = SHUT_WR,        SD_BOTH    = SHUT_RDWR    }     private int _lasterr() nothrow @nogc    {        return errno;    }}else{    static assert(0, "No socket support for this platform yet.");} version (StdUnittest){    // Print a message on exception instead of failing the unittest.    private void softUnittest(void delegate() @safe test, int line = __LINE__) @trusted    {        debug (std_socket)            test();        else        {            import std.stdio : writefln;            try                test();            catch (Throwable e)                writefln("Ignoring std.socket(%d) test failure (likely caused by flaky environment): %s", line, e.msg);        }    }     // Without debug=std_socket, still compile the slow tests, just don't run them.    debug (std_socket)        private enum runSlowTests = true;    else        private enum runSlowTests = false;} /// Base exception thrown by `std.socket`.class SocketException: Exception{    mixin basicExceptionCtors;} version (CRuntime_Glibc) version = GNU_STRERROR;version (CRuntime_UClibc) version = GNU_STRERROR; /* * Needs to be public so that SocketOSException can be thrown outside of * std.socket (since it uses it as a default argument), but it probably doesn't * need to actually show up in the docs, since there's not really any public * need for it outside of being a default argument. */string formatSocketError(int err) @trusted{    version (Posix)    {        char[80] buf;        const(char)* cs;        version (GNU_STRERROR)        {            cs = strerror_r(err, buf.ptr, buf.length);        }        else        {            auto errs = strerror_r(err, buf.ptr, buf.length);            if (errs == 0)                cs = buf.ptr;            else                return "Socket error " ~ to!string(err);        }         auto len = strlen(cs);         if (cs[len - 1] == '\n')            len--;        if (cs[len - 1] == '\r')            len--;        return cs[0 .. len].idup;    }    else    version (Windows)    {        return generateSysErrorMsg(err);    }    else        return "Socket error " ~ to!string(err);} /// Returns the error message of the most recently encountered network error.@property string lastSocketError(){    return formatSocketError(_lasterr());} /// Socket exception representing network errors reported by the operating system.class SocketOSException: SocketException{    int errorCode;     /// Platform-specific error code.     ///    this(string msg,         string file = __FILE__,         size_t line = __LINE__,         Throwable next = null,         int err = _lasterr(),         string function(int) @trusted errorFormatter = &formatSocketError)    {        errorCode = err;         if (msg.length)            super(msg ~ ": " ~ errorFormatter(err), file, line, next);        else            super(errorFormatter(err), file, line, next);    }     ///    this(string msg,         Throwable next,         string file = __FILE__,         size_t line = __LINE__,         int err = _lasterr(),         string function(int) @trusted errorFormatter = &formatSocketError)    {        this(msg, file, line, next, err, errorFormatter);    }     ///    this(string msg,         int err,         string function(int) @trusted errorFormatter = &formatSocketError,         string file = __FILE__,         size_t line = __LINE__,         Throwable next = null)    {        this(msg, file, line, next, err, errorFormatter);    }} /// Socket exception representing invalid parameters specified by user code.class SocketParameterException: SocketException{    mixin basicExceptionCtors;} /** * Socket exception representing attempts to use network capabilities not * available on the current system. */class SocketFeatureException: SocketException{    mixin basicExceptionCtors;}  /** * Returns: * `true` if the last socket operation failed because the socket * was in non-blocking mode and the operation would have blocked, * or if the socket is in blocking mode and set a `SNDTIMEO` or `RCVTIMEO`, * and the operation timed out. */bool wouldHaveBlocked() nothrow @nogc{    version (Windows)        return _lasterr() == WSAEWOULDBLOCK || _lasterr() == WSAETIMEDOUT;    else version (Posix)        return _lasterr() == EAGAIN;    else        static assert(0, "No socket support for this platform yet.");} @safe unittest{    auto sockets = socketPair();    auto s = sockets[0];    s.setOption(SocketOptionLevel.SOCKET, SocketOption.RCVTIMEO, dur!"msecs"(10));    ubyte[] buffer = new ubyte[](16);    auto rec = s.receive(buffer);    assert(rec == -1 && wouldHaveBlocked());}  private immutable{    typeof(&getnameinfo) getnameinfoPointer;    typeof(&getaddrinfo) getaddrinfoPointer;    typeof(&freeaddrinfo) freeaddrinfoPointer;} shared static this() @system{    version (Windows)    {        WSADATA wd;         // Winsock will still load if an older version is present.        // The version is just a request.        int val;        val = WSAStartup(0x2020, &wd);        if (val)         // Request Winsock 2.2 for IPv6.            throw new SocketOSException("Unable to initialize socket library", val);         // These functions may not be present on older Windows versions.        // See the comment in InternetAddress.toHostNameString() for details.        auto ws2Lib = GetModuleHandleA("ws2_32.dll");        if (ws2Lib)        {            getnameinfoPointer = cast(typeof(getnameinfoPointer))                                 GetProcAddress(ws2Lib, "getnameinfo");            getaddrinfoPointer = cast(typeof(getaddrinfoPointer))                                 GetProcAddress(ws2Lib, "getaddrinfo");            freeaddrinfoPointer = cast(typeof(freeaddrinfoPointer))                                 GetProcAddress(ws2Lib, "freeaddrinfo");        }    }    else version (Posix)    {        getnameinfoPointer = &getnameinfo;        getaddrinfoPointer = &getaddrinfo;        freeaddrinfoPointer = &freeaddrinfo;    }}  shared static ~this() @system nothrow @nogc{    version (Windows)    {        WSACleanup();    }} /** * The communication domain used to resolve an address. */enum AddressFamily: ushort{    UNSPEC =     AF_UNSPEC,     /// Unspecified address family    UNIX =       AF_UNIX,       /// Local communication (Unix socket)    INET =       AF_INET,       /// Internet Protocol version 4    IPX =        AF_IPX,        /// Novell IPX    APPLETALK =  AF_APPLETALK,  /// AppleTalk    INET6 =      AF_INET6,      /// Internet Protocol version 6}  /** * Communication semantics */enum SocketType: int{    STREAM =     SOCK_STREAM,           /// Sequenced, reliable, two-way communication-based byte streams    DGRAM =      SOCK_DGRAM,            /// Connectionless, unreliable datagrams with a fixed maximum length; data may be lost or arrive out of order    RAW =        SOCK_RAW,              /// Raw protocol access    RDM =        SOCK_RDM,              /// Reliably-delivered message datagrams    SEQPACKET =  SOCK_SEQPACKET,        /// Sequenced, reliable, two-way connection-based datagrams with a fixed maximum length}  /** * Protocol */enum ProtocolType: int{    IP =    IPPROTO_IP,         /// Internet Protocol version 4    ICMP =  IPPROTO_ICMP,       /// Internet Control Message Protocol    IGMP =  IPPROTO_IGMP,       /// Internet Group Management Protocol    GGP =   IPPROTO_GGP,        /// Gateway to Gateway Protocol    TCP =   IPPROTO_TCP,        /// Transmission Control Protocol    PUP =   IPPROTO_PUP,        /// PARC Universal Packet Protocol    UDP =   IPPROTO_UDP,        /// User Datagram Protocol    IDP =   IPPROTO_IDP,        /// Xerox NS protocol    RAW =   IPPROTO_RAW,        /// Raw IP packets    IPV6 =  IPPROTO_IPV6,       /// Internet Protocol version 6}  /** * Class for retrieving protocol information. * * Example: * --- * auto proto = new Protocol; * writeln("About protocol TCP:"); * if (proto.getProtocolByType(ProtocolType.TCP)) * { *     writefln("  Name: %s", proto.name); *     foreach (string s; proto.aliases) *          writefln("  Alias: %s", s); * } * else *     writeln("  No information found"); * --- */class Protocol{    /// These members are populated when one of the following functions are called successfully:    ProtocolType type;    string name;                /// ditto    string[] aliases;           /// ditto      void populate(protoent* proto) @system pure nothrow    {        type = cast(ProtocolType) proto.p_proto;        name = to!string(proto.p_name);         int i;        for (i = 0;; i++)        {            if (!proto.p_aliases[i])                break;        }         if (i)        {            aliases = new string[i];            for (i = 0; i != aliases.length; i++)            {                aliases[i] =                    to!string(proto.p_aliases[i]);            }        }        else        {            aliases = null;        }    }     /** Returns: false on failure */    bool getProtocolByName(scope const(char)[] name) @trusted nothrow    {        protoent* proto;        proto = getprotobyname(name.tempCString());        if (!proto)            return false;        populate(proto);        return true;    }      /** Returns: false on failure */    // Same as getprotobynumber().    bool getProtocolByType(ProtocolType type) @trusted nothrow    {        protoent* proto;        proto = getprotobynumber(type);        if (!proto)            return false;        populate(proto);        return true;    }}  // Skip this test on Android because getprotobyname/number are// unimplemented in bionic.version (CRuntime_Bionic) {} else@safe unittest{    // import std.stdio : writefln;    softUnittest({        Protocol proto = new Protocol;        assert(proto.getProtocolByType(ProtocolType.TCP));        //writeln("About protocol TCP:");        //writefln("\tName: %s", proto.name);        // foreach (string s; proto.aliases)        // {        //      writefln("\tAlias: %s", s);        // }        assert(proto.name == "tcp");        assert(proto.aliases.length == 1 && proto.aliases[0] == "TCP");    });}  /** * Class for retrieving service information. * * Example: * --- * auto serv = new Service; * writeln("About service epmap:"); * if (serv.getServiceByName("epmap", "tcp")) * { *     writefln("  Service: %s", serv.name); *     writefln("  Port: %d", serv.port); *     writefln("  Protocol: %s", serv.protocolName); *     foreach (string s; serv.aliases) *          writefln("  Alias: %s", s); * } * else *     writefln("  No service for epmap."); * --- */class Service{    /// These members are populated when one of the following functions are called successfully:    string name;

Lines 1–500 of 3925.

Keyboard shortcuts

General

Show keyboard shortcuts
?
Focus the current search
/

Issues and pull requests

Open filters
f

Issues, pull requests and Inbox

Select next item
j↓
Select previous item
k↑
Open selected item
Enter
Dismiss filters or clear selection
Esc

Repository code

Go to file
t