// 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. Load more
// 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;