Advertisement
Not a member of Pastebin yet?
Sign Up,
it unlocks many cool features!
- import std.socket;
- import std.c.unix.unix; // sockaddr
- import std.c.string; // strlen, memset
- import std.c.stdlib; // getError() and maybe other stuff for LLSocket
- import std.stdint; // int32_t
- import std.string; // toStrinz
- static import std.gc; // malloc() is used once..
- // some C functions we need that are not contained in std.c.* or are implemented by me in mischelper.c
- extern(C) {
- /* ****************************************************************************************
- * The following two functions are implemented in C, because std.c.linux.socket currently *
- * lacks sendmsg(), recvmsg() and related structs and macros/functions *
- * (the cmsg-stuff like struct msghdr, struct iovec, struct cmsghdr and all those *
- * macros to modify those structs like CMSG_LEN, CMSG_FIRSTHDR, CMSG_DATA etc) *
- * These functions however are crucial to pass file descriptors between processes. *
- * ****************************************************************************************/
- /**
- * Sends file/socket-descriptor fd_to_send to other process via socket.
- * The Socket must be of AddressFamily UNIX (AF_UNIX)!
- *
- * Returns: The amount of bytes written, -1 on error
- */
- // this is not a standard function - you have to compile the C-code and link it yourself
- int send_fd(int socket, int fd_to_send);
- /**
- * Receives a file/socket-descriptor from another process via socket.
- * The Socket must be of AddressFamily UNIX (AF_UNIX)!
- *
- * Returns: The received file/socket-descriptor, -1 on error
- */
- // this is not a standard function - you have to compile the C-code and link it yourself
- int recv_fd(int socket);
- /**
- * creates a pair of locally connected sockets
- *
- * domain: must be AF_UNIX
- * type: STREAM, DGRAM or something like that (I use SOCK_STREAM)
- * protocol: optional (0 should work)
- * sv[2]: sv[0] will contain first socket-fd, sv[1] the second
- */
- // this is a standard POSIX function that isn't represented in std.c.*
- int socketpair(int domain, int type, int protocol, int sv[2]);
- }
- /* ********************
- * Socket-Stuff *
- * ********************/
- // this struct normally resides in <sys/un.h> (for C)
- /// a sockaddr for local connections (AF_UNIX)
- struct sockaddr_un {
- ushort sun_family = AF_UNIX; // addr family
- char sun_path[108]; // pathname
- };
- /// an Address (derived from std.socket.Address) with public getters...
- abstract class PubAddress : Address {
- // add public accessors for name and namelen
- public sockaddr* getName() { return name(); }
- public int getNameLen() { return nameLen(); }
- }
- /// an Address (derived from std.socket.Address) with public getters for local addresses (AF_UNIX)
- class LocalAddress : PubAddress {
- sockaddr_un addr;
- this() {
- addr.sun_family = AF_UNIX;
- }
- this(char[] path) {
- // see man 7 unix for further information about the path/address types
- if(path && path.length>0) {
- // unnamed type
- if(path[0] == '\0' && path[1] == '\0')
- return;
- // abstract type
- if(path[0] == '\0'){
- if(path.length > 108)
- throw new Exception("path to long - must be at most 108 chars");
- addr.sun_path[0..path.length] = path[];
- }
- // pathname type
- path ~= '\0'; // just to be sure
- int len = cast(int)strlen(cast(char*)addr.sun_path);
- if(len > 107) // nullterminated path doesn't fit into sun_path
- throw new Exception("path to long - must be at most 108 chars");
- addr.sun_path[0..len] = path[0..len];
- }
- }
- this(sockaddr_un *sadr){
- if(sadr.sun_family != AF_UNIX) {
- throw new Exception("this was not a AF_UNIX socketaddr");
- }
- addr = *sadr;
- }
- protected sockaddr* name() { return cast(sockaddr*) &addr; }
- protected int nameLen() {
- // if the address is the unnamed type
- if(addr.sun_path[0] == '\0' && addr.sun_path[1] == '\0')
- return cast(int)ushort.sizeof;
- // if the address is the abstract type
- if(addr.sun_path[0] == '\0')
- return cast(int)addr.sizeof;
- // at this point it has to be the pathname type
- assert(addr.sun_path[107] == '\0');
- int len = cast(int)strlen(cast(char*)addr.sun_path) + 1; // + terminating \0
- return cast(int)(ushort.sizeof + len);
- }
- AddressFamily addressFamily() { return AddressFamily.UNIX; }
- string toString() { return "local:"~getPath(); }
- char[] getPath() {
- // if the address is the unnamed type
- if(addr.sun_path[0] == '\0' && addr.sun_path[1] == '\0')
- return "\0\0";
- // if the address is the abstract type
- if(addr.sun_path[0] == '\0')
- return addr.sun_path;
- // at this point it has to be the pathname type
- assert(addr.sun_path[107] == '\0');
- int len = cast(int)strlen(cast(char*)addr.sun_path)+1; // + terminating \0
- return addr.sun_path[0..len];
- }
- }
- version = BsdSockets;
- /**
- * "Low Level Socket" - Like an ordinary std.socket.Socket, but contains 1000% more hatred and also
- * a constructor that accepts an existing socket_t (like one might get from c functions, e.g. socketpair()).
- */
- class LLSocket : Socket
- {
- /*
- * yeah, this mess is basically copied&pasted from the original std.socket.Socket,
- * a constructor accepting a socket_t was added and then hacks were added to work around
- * package protection and such in std.socket
- */
- protected: // don't make the same mistake (making this private) again ...
- socket_t sock;
- AddressFamily _family;
- // to be used with the constructor getting a socket_t
- bool closeOnExit=true;
- // ####################################################################################
- // ########## workarounds all this private crap in std.socket #########################
- // this might be specific for BsdSockets, i.e. doesn't work in windows - see std.socket
- private const int _SOCKET_ERROR = -1;
- static private int _lasterr()
- {
- return getErrno();
- }
- /**
- * wraps an Address into a PubAddress - currently only works for InternetAddress and PubAddress
- * (because, apart from UnknownAddress, no other Addresses are currently specified by std.socket)
- */
- class WrapAddress : PubAddress {
- Address _addr;
- AddressFamily fam;
- sockaddr *sad;
- int len;
- this(Address addr) {
- _addr = addr;
- fam = addr.addressFamily();
- // addr is an InternetAddress
- if( is(addr : InternetAddress) ) {
- InternetAddress iad = cast(InternetAddress)addr;
- sockaddr_in *tmp = cast(sockaddr_in *)std.gc.malloc(sockaddr_in.sizeof);
- memset(tmp, 0, sockaddr_in.sizeof); // should be nulled
- tmp.sin_family = AF_INET;
- // set address
- tmp.sin_addr.s_addr = htonl(iad.addr());
- // set port
- tmp.sin_port = htons(iad.port());
- sad = cast(sockaddr *)tmp;
- } else if( is(addr : PubAddress) ) {
- PubAddress pad = cast(PubAddress) addr;
- sad = pad.getName();
- len = pad.getNameLen();
- }
- }
- protected sockaddr* name() {
- return sad;
- }
- protected int nameLen() {
- return len;
- }
- AddressFamily addressFamily() { return fam; }
- string toString() { return _addr.toString(); }
- }
- // ################ end of sucky workarounds ##########################################
- // ####################################################################################
- version(Win32)
- bool _blocking = false; /// Property to get or set whether the socket is blocking or nonblocking.
- // For use with accepting().
- protected this()
- {
- }
- public:
- /**
- * Create a Socket from a given socket handle (which might have been obtained
- * from a C-function like socketpair() or might even have been passed from
- * another process via ancillary messages - see man cmsg or man 7 unix).
- * The AddressFamily has to be specified.
- * Per default the socket will be closed when this Socket object is
- * destructed - set closeOnExit=false to prevent this.
- */
- this(socket_t socket, AddressFamily fam, bool closeOnExit=true) {
- this.sock = socket;
- this._family = fam;
- this.closeOnExit = closeOnExit;
- }
- /**
- * Create a blocking socket. If a single protocol type exists to support
- * this socket type within the address family, the ProtocolType may be
- * omitted.
- */
- this(AddressFamily af, SocketType type, ProtocolType protocol)
- {
- sock = cast(socket_t)socket(af, type, protocol);
- if(sock == socket_t.init)
- throw new SocketException("Unable to create socket", _lasterr());
- _family = af;
- }
- // A single protocol exists to support this socket type within the
- // protocol family, so the ProtocolType is assumed.
- /// ditto
- this(AddressFamily af, SocketType type)
- {
- this(af, type, cast(ProtocolType)0); // Pseudo protocol number.
- }
- /// ditto
- this(AddressFamily af, SocketType type, string protocolName)
- {
- protoent* proto;
- proto = getprotobyname(toStringz(protocolName));
- if(!proto)
- throw new SocketException("Unable to find the protocol", _lasterr());
- this(af, type, cast(ProtocolType)proto.p_proto);
- }
- ~this()
- {
- if(closeOnExit)
- close();
- }
- /// Get underlying socket handle.
- socket_t handle()
- {
- return sock;
- }
- /**
- * Get/set socket's blocking flag.
- *
- * When a socket is blocking, calls to receive(), accept(), and send()
- * will block and wait for data/action.
- * A non-blocking socket will immediately return instead of blocking.
- */
- bool blocking()
- {
- version(Win32)
- {
- return _blocking;
- }
- else version(BsdSockets)
- {
- return !(fcntl(handle, F_GETFL, 0) & O_NONBLOCK);
- }
- }
- /// ditto
- void blocking(bool byes)
- {
- version(Win32)
- {
- uint num = !byes;
- if(_SOCKET_ERROR == ioctlsocket(sock, FIONBIO, &num))
- goto err;
- _blocking = byes;
- }
- else version(BsdSockets)
- {
- int x = fcntl(sock, F_GETFL, 0);
- if(-1 == x)
- goto err;
- if(byes)
- x &= ~O_NONBLOCK;
- else
- x |= O_NONBLOCK;
- if(-1 == fcntl(sock, F_SETFL, x))
- goto err;
- }
- return; // Success.
- err:
- throw new SocketException("Unable to set socket blocking", _lasterr());
- }
- /// Get the socket's address family.
- AddressFamily addressFamily() // getter
- {
- return _family;
- }
- /// Property that indicates if this is a valid, alive socket.
- bool isAlive() // getter
- {
- int type, typesize = type.sizeof;
- return !getsockopt(sock, SOL_SOCKET, SO_TYPE, cast(char*)&type, &typesize);
- }
- /// Associate a local address with this socket.
- void bind(Address addr)
- {
- PubAddress wad;
- if(is(addr : PubAddress))
- wad = cast(PubAddress)addr;
- else
- wad = new WrapAddress(addr);
- if(_SOCKET_ERROR == .bind(sock, wad.getName(), wad.getNameLen()))
- throw new SocketException("Unable to bind socket", _lasterr());
- }
- /**
- * Establish a connection. If the socket is blocking, connect waits for
- * the connection to be made. If the socket is nonblocking, connect
- * returns immediately and the connection attempt is still in progress.
- */
- void connect(Address to)
- {
- PubAddress wad;
- if(is(to : PubAddress))
- wad = cast(PubAddress)to;
- else
- wad = new WrapAddress(to);
- if(_SOCKET_ERROR == .connect(sock, wad.getName(), wad.getNameLen()))
- {
- int err;
- err = _lasterr();
- if(!blocking)
- {
- version(Win32)
- {
- if(WSAEWOULDBLOCK == err)
- return;
- }
- else version(Unix)
- {
- if(EINPROGRESS == err)
- return;
- }
- else
- {
- static assert(0);
- }
- }
- throw new SocketException("Unable to connect socket", err);
- }
- }
- /**
- * Listen for an incoming connection. bind must be called before you can
- * listen. The backlog is a request of how many pending incoming
- * connections are queued until accept'ed.
- */
- void listen(int backlog)
- {
- if(_SOCKET_ERROR == .listen(sock, backlog))
- throw new SocketException("Unable to listen on socket", _lasterr());
- }
- /**
- * Called by accept when a new Socket must be created for a new
- * connection. To use a derived class, override this method and return an
- * instance of your class. The returned Socket's handle must not be set;
- * Socket has a protected constructor this() to use in this situation.
- */
- // Override to use a derived class.
- // The returned socket's handle must not be set.
- protected Socket accepting()
- {
- return new LLSocket;
- }
- /**
- * Accept an incoming connection. If the socket is blocking, accept
- * waits for a connection request. Throws SocketAcceptException if unable
- * to accept. See accepting for use with derived classes.
- */
- Socket accept()
- {
- socket_t newsock;
- //newsock = cast(socket_t).accept(sock, null, null); // DMD 0.101 error: found '(' when expecting ';' following 'statement
- alias .accept topaccept;
- newsock = cast(socket_t)topaccept(sock, null, null);
- if(socket_t.init == newsock)
- throw new SocketAcceptException("Unable to accept socket connection", _lasterr());
- LLSocket newSocket;
- try
- {
- // this cast shouldn't do any harm - accepting returns an LLSocket and any derived class
- // will also be castable to LLSocket
- newSocket = cast(LLSocket)accepting();
- assert(newSocket.sock == socket_t.init);
- newSocket.sock = newsock;
- version(Win32)
- newSocket._blocking = _blocking; //inherits blocking mode
- newSocket._family = _family; //same family
- }
- catch(Object o)
- {
- _close(newsock);
- throw o;
- }
- return newSocket;
- }
- /// Disables sends and/or receives.
- void shutdown(SocketShutdown how)
- {
- .shutdown(sock, cast(int)how);
- }
- private static void _close(socket_t sock)
- {
- version(Win32)
- {
- .closesocket(sock);
- }
- else version(BsdSockets)
- {
- .close(sock);
- }
- }
- /**
- * Immediately drop any connections and release socket resources.
- * Calling shutdown before close is recommended for connection-oriented
- * sockets. The Socket object is no longer usable after close.
- */
- //calling shutdown() before this is recommended
- //for connection-oriented sockets
- void close()
- {
- _close(sock);
- sock = socket_t.init;
- }
- private Address newFamilyObject()
- {
- Address result;
- switch(_family)
- {
- case cast(AddressFamily)AddressFamily.INET:
- // port 0 seems to be default so this shouldn't do any harm
- result = new InternetAddress(cast(ushort)0);
- break;
- default:
- result = new UnknownAddress;
- }
- return result;
- }
- /// Returns the local machine's host name. Idea from mango.
- static string hostName() // getter
- {
- char[256] result; // Host names are limited to 255 chars.
- if(_SOCKET_ERROR == .gethostname(result.ptr, result.length))
- throw new SocketException("Unable to obtain host name", _lasterr());
- return std.string.toString(cast(char*)result).dup;
- }
- /// Remote endpoint Address.
- Address remoteAddress()
- {
- Address addr = newFamilyObject();
- PubAddress wad;
- if(is(addr : PubAddress))
- wad = cast(PubAddress)addr;
- else
- wad = new WrapAddress(addr);
- int nameLen = wad.getNameLen();
- if(_SOCKET_ERROR == .getpeername(sock, wad.getName(), &nameLen))
- throw new SocketException("Unable to obtain remote socket address", _lasterr());
- assert(addr.addressFamily() == _family);
- return addr;
- }
- /// Local endpoint Address.
- Address localAddress()
- {
- Address addr = newFamilyObject();
- PubAddress wad;
- if(is(addr : PubAddress))
- wad = cast(PubAddress)addr;
- else
- wad = new WrapAddress(addr);
- int nameLen = wad.getNameLen();
- if(_SOCKET_ERROR == .getsockname(sock, wad.getName(), &nameLen))
- throw new SocketException("Unable to obtain local socket address", _lasterr());
- assert(addr.addressFamily() == _family);
- return addr;
- }
- /// Send or receive error code.
- const int ERROR = _SOCKET_ERROR;
- /**
- * Send data on the connection. Returns the number of bytes actually
- * sent, or ERROR on failure. If the socket is blocking and there is no
- * buffer space left, send waits.
- */
- //returns number of bytes actually sent, or -1 on error
- int send(void[] buf, SocketFlags flags)
- {
- flags |= SocketFlags.NOSIGNAL;
- int sent = .send(sock, buf.ptr, buf.length, cast(int)flags);
- return sent;
- }
- /// ditto
- int send(void[] buf)
- {
- return send(buf, SocketFlags.NOSIGNAL);
- }
- /**
- * Send data to a specific destination Address. If the destination address is not specified, a connection must have been made and that address is used. If the socket is blocking and there is no buffer space left, sendTo waits.
- */
- int sendTo(void[] buf, SocketFlags flags, Address to)
- {
- flags |= SocketFlags.NOSIGNAL;
- PubAddress wad;
- if(is(to : PubAddress))
- wad = cast(PubAddress)to;
- else
- wad = new WrapAddress(to);
- int sent = .sendto(sock, buf.ptr, buf.length, cast(int)flags, wad.getName(), wad.getNameLen());
- return sent;
- }
- /// ditto
- int sendTo(void[] buf, Address to)
- {
- return sendTo(buf, SocketFlags.NONE, to);
- }
- //assumes you connect()ed
- /// ditto
- int sendTo(void[] buf, SocketFlags flags)
- {
- flags |= SocketFlags.NOSIGNAL;
- int sent = .sendto(sock, buf.ptr, buf.length, cast(int)flags, null, 0);
- return sent;
- }
- //assumes you connect()ed
- /// ditto
- int sendTo(void[] buf)
- {
- return sendTo(buf, SocketFlags.NONE);
- }
- /**
- * Receive data on the connection. Returns the number of bytes actually
- * received, 0 if the remote side has closed the connection, or ERROR on
- * failure. If the socket is blocking, receive waits until there is data
- * to be received.
- */
- //returns number of bytes actually received, 0 on connection closure, or -1 on error
- int receive(void[] buf, SocketFlags flags)
- {
- if(!buf.length) //return 0 and don't think the connection closed
- return 0;
- int read = .recv(sock, buf.ptr, buf.length, cast(int)flags);
- // if(!read) //connection closed
- return read;
- }
- /// ditto
- int receive(void[] buf)
- {
- return receive(buf, SocketFlags.NONE);
- }
- /**
- * Receive data and get the remote endpoint Address.
- * If the socket is blocking, receiveFrom waits until there is data to
- * be received.
- * Returns: the number of bytes actually received,
- * 0 if the remote side has closed the connection, or ERROR on failure.
- */
- int receiveFrom(void[] buf, SocketFlags flags, out Address from)
- {
- if(!buf.length) //return 0 and don't think the connection closed
- return 0;
- from = newFamilyObject();
- PubAddress wad;
- if(is(from : PubAddress))
- wad = cast(PubAddress)from;
- else
- wad = new WrapAddress(from);
- int nameLen = wad.getNameLen();
- int read = .recvfrom(sock, buf.ptr, buf.length, cast(int)flags, wad.getName(), &nameLen);
- assert(from.addressFamily() == _family);
- // if(!read) //connection closed
- return read;
- }
- /// ditto
- int receiveFrom(void[] buf, out Address from)
- {
- return receiveFrom(buf, SocketFlags.NONE, from);
- }
- //assumes you connect()ed
- /// ditto
- int receiveFrom(void[] buf, SocketFlags flags)
- {
- if(!buf.length) //return 0 and don't think the connection closed
- return 0;
- int read = .recvfrom(sock, buf.ptr, buf.length, cast(int)flags, null, null);
- // if(!read) //connection closed
- return read;
- }
- //assumes you connect()ed
- /// ditto
- int receiveFrom(void[] buf)
- {
- return receiveFrom(buf, SocketFlags.NONE);
- }
- /// Get a socket option. Returns the number of bytes written to result.
- //returns the length, in bytes, of the actual result - very different from getsockopt()
- int getOption(SocketOptionLevel level, SocketOption option, void[] result)
- {
- int len = result.length;
- if(_SOCKET_ERROR == .getsockopt(sock, cast(int)level, cast(int)option, result.ptr, &len))
- throw new SocketException("Unable to get socket option", _lasterr());
- return len;
- }
- /// Common case of getting integer and boolean options.
- int getOption(SocketOptionLevel level, SocketOption option, out int32_t result)
- {
- return getOption(level, option, (&result)[0 .. 1]);
- }
- /// Get the linger option.
- int getOption(SocketOptionLevel level, SocketOption option, out std.socket.linger result)
- {
- //return getOption(cast(SocketOptionLevel)SocketOptionLevel.SOCKET, SocketOption.LINGER, (&result)[0 .. 1]);
- return getOption(level, option, (&result)[0 .. 1]);
- }
- // Set a socket option.
- void setOption(SocketOptionLevel level, SocketOption option, void[] value)
- {
- if(_SOCKET_ERROR == .setsockopt(sock, cast(int)level, cast(int)option, value.ptr, value.length))
- throw new SocketException("Unable to set socket option", _lasterr());
- }
- /// Common case for setting integer and boolean options.
- void setOption(SocketOptionLevel level, SocketOption option, int32_t value)
- {
- setOption(level, option, (&value)[0 .. 1]);
- }
- /// Set the linger option.
- void setOption(SocketOptionLevel level, SocketOption option, std.socket.linger value)
- {
- //setOption(cast(SocketOptionLevel)SocketOptionLevel.SOCKET, SocketOption.LINGER, (&value)[0 .. 1]);
- setOption(level, option, (&value)[0 .. 1]);
- }
- /**
- * Wait for a socket to change status. A wait timeout timeval or int microseconds may be specified; if a timeout is not specified or the timeval is null, the maximum timeout is used. The timeval timeout has an unspecified value when select returns. Returns the number of sockets with status changes, 0 on timeout, or -1 on interruption. If the return value is greater than 0, the SocketSets are updated to only contain the sockets having status changes. For a connecting socket, a write status change means the connection is established and it's able to send. For a listening socket, a read status change means there is an incoming connection request and it's able to accept.
- */
- //SocketSet's updated to include only those sockets which an event occured
- //returns the number of events, 0 on timeout, or -1 on interruption
- //for a connect()ing socket, writeability means connected
- //for a listen()ing socket, readability means listening
- //Winsock: possibly internally limited to 64 sockets per set
- static int select(SocketSet checkRead, SocketSet checkWrite, SocketSet checkError, std.socket.timeval* tv)
- in
- {
- //make sure none of the SocketSet's are the same object
- if(checkRead)
- {
- assert(checkRead !is checkWrite);
- assert(checkRead !is checkError);
- }
- if(checkWrite)
- {
- assert(checkWrite !is checkError);
- }
- }
- body
- {
- fd_set* fr, fw, fe;
- int n = 0;
- version(Win32)
- {
- // Windows has a problem with empty fd_set`s that aren't null.
- fr = (checkRead && checkRead.count()) ? checkRead.toFd_set() : null;
- fw = (checkWrite && checkWrite.count()) ? checkWrite.toFd_set() : null;
- fe = (checkError && checkError.count()) ? checkError.toFd_set() : null;
- }
- else
- {
- if(checkRead)
- {
- fr = checkRead.toFd_set();
- n = checkRead.selectn();
- }
- else
- {
- fr = null;
- }
- if(checkWrite)
- {
- fw = checkWrite.toFd_set();
- int _n;
- _n = checkWrite.selectn();
- if(_n > n)
- n = _n;
- }
- else
- {
- fw = null;
- }
- if(checkError)
- {
- fe = checkError.toFd_set();
- int _n;
- _n = checkError.selectn();
- if(_n > n)
- n = _n;
- }
- else
- {
- fe = null;
- }
- }
- int result = .select(n, fr, fw, fe, cast(_ctimeval*)tv);
- version(Win32)
- {
- if(_SOCKET_ERROR == result && WSAGetLastError() == WSAEINTR)
- return -1;
- }
- else version(Unix)
- {
- if(_SOCKET_ERROR == result && getErrno() == EINTR)
- return -1;
- }
- else
- {
- static assert(0);
- }
- if(_SOCKET_ERROR == result)
- throw new SocketException("Socket select error", _lasterr());
- return result;
- }
- /// ditto
- static int select(SocketSet checkRead, SocketSet checkWrite, SocketSet checkError, int microseconds)
- {
- std.socket.timeval tv;
- tv.seconds = 0;
- tv.microseconds = microseconds;
- return select(checkRead, checkWrite, checkError, &tv);
- }
- /// ditto
- //maximum timeout
- static int select(SocketSet checkRead, SocketSet checkWrite, SocketSet checkError)
- {
- return select(checkRead, checkWrite, checkError, null);
- }
- }
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement