123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566156715681569157015711572157315741575157615771578157915801581158215831584158515861587158815891590159115921593159415951596159715981599160016011602160316041605160616071608160916101611161216131614161516161617161816191620162116221623162416251626162716281629163016311632163316341635163616371638163916401641164216431644164516461647164816491650165116521653165416551656165716581659166016611662166316641665166616671668166916701671167216731674167516761677167816791680168116821683168416851686168716881689169016911692169316941695169616971698169917001701170217031704170517061707170817091710171117121713171417151716171717181719172017211722172317241725172617271728172917301731173217331734173517361737173817391740174117421743174417451746174717481749175017511752175317541755175617571758175917601761176217631764176517661767176817691770177117721773177417751776177717781779178017811782178317841785178617871788178917901791179217931794179517961797179817991800180118021803180418051806180718081809181018111812181318141815181618171818181918201821182218231824182518261827182818291830183118321833183418351836183718381839184018411842 |
|
module tango.net.device.Berkeley;
private import tango.sys.Common;
private import tango.core.Exception;
import consts=tango.sys.consts.socket;
/*******************************************************************************
*******************************************************************************/
private extern(C) int strlen(char*);
/*******************************************************************************
*******************************************************************************/
enum {SOCKET_ERROR = consts.SOCKET_ERROR}
/*******************************************************************************
*******************************************************************************/
enum SocketOption
{
DEBUG = consts.SO_DEBUG , /* turn on debugging info recording */
BROADCAST = consts.SO_BROADCAST , /* permit sending of broadcast msgs */
REUSEADDR = consts.SO_REUSEADDR , /* allow local address reuse */
LINGER = consts.SO_LINGER , /* linger on close if data present */
DONTLINGER = ~(consts.SO_LINGER),
OOBINLINE = consts.SO_OOBINLINE , /* leave received OOB data in line */
ACCEPTCONN = consts.SO_ACCEPTCONN, /* socket has had listen() */
KEEPALIVE = consts.SO_KEEPALIVE , /* keep connections alive */
DONTROUTE = consts.SO_DONTROUTE , /* just use interface addresses */
TYPE = consts.SO_TYPE , /* get socket type */
/*
* Additional options, not kept in so_options.
*/
SNDBUF = consts.SO_SNDBUF, /* send buffer size */
RCVBUF = consts.SO_RCVBUF, /* receive buffer size */
ERROR = consts.SO_ERROR , /* get error status and clear */
// OptionLevel.IP settings
MULTICAST_TTL = consts.IP_MULTICAST_TTL ,
MULTICAST_LOOP = consts.IP_MULTICAST_LOOP ,
ADD_MEMBERSHIP = consts.IP_ADD_MEMBERSHIP ,
DROP_MEMBERSHIP = consts.IP_DROP_MEMBERSHIP,
// OptionLevel.TCP settings
TCP_NODELAY = consts.TCP_NODELAY ,
// Windows specifics
WIN_UPDATE_ACCEPT_CONTEXT = 0x700B,
WIN_CONNECT_TIME = 0x700C,
WIN_UPDATE_CONNECT_CONTEXT = 0x7010,
}
/*******************************************************************************
*******************************************************************************/
enum SocketOptionLevel
{
SOCKET = consts.SOL_SOCKET ,
IP = consts.IPPROTO_IP ,
TCP = consts.IPPROTO_TCP ,
UDP = consts.IPPROTO_UDP ,
}
/*******************************************************************************
*******************************************************************************/
enum SocketType
{
STREAM = consts.SOCK_STREAM , /++ sequential, reliable +/
DGRAM = consts.SOCK_DGRAM , /++ connectionless unreliable, max length +/
SEQPACKET = consts.SOCK_SEQPACKET, /++ sequential, reliable, max length +/
}
/*******************************************************************************
*******************************************************************************/
enum ProtocolType
{
IP = consts.IPPROTO_IP , /// default internet protocol (probably 4 for compatibility)
IPV4 = consts.IPPROTO_IP , /// internet protocol version 4
IPV6 = consts.IPPROTO_IPV6 , /// internet protocol version 6
ICMP = consts.IPPROTO_ICMP , /// internet control message protocol
IGMP = consts.IPPROTO_IGMP , /// internet group management protocol
TCP = consts.IPPROTO_TCP , /// transmission control protocol
PUP = consts.IPPROTO_PUP , /// PARC universal packet protocol
UDP = consts.IPPROTO_UDP , /// user datagram protocol
IDP = consts.IPPROTO_IDP , /// Xerox NS protocol
}
/*******************************************************************************
*******************************************************************************/
enum AddressFamily
{
UNSPEC = consts.AF_UNSPEC ,
UNIX = consts.AF_UNIX ,
INET = consts.AF_INET ,
IPX = consts.AF_IPX ,
APPLETALK = consts.AF_APPLETALK,
INET6 = consts.AF_INET6 ,
}
/*******************************************************************************
*******************************************************************************/
enum SocketShutdown
{
RECEIVE = consts.SHUT_RD,
SEND = consts.SHUT_WR,
BOTH = consts.SHUT_RDWR,
}
/*******************************************************************************
*******************************************************************************/
enum SocketFlags
{
NONE = 0,
OOB = consts.MSG_OOB, /// out of band
PEEK = consts.MSG_PEEK, /// only for receiving
DONTROUTE = consts.MSG_DONTROUTE, /// only for sending
NOSIGNAL = 0x4000, /// inhibit signals
}
/*******************************************************************************
conversions for network byte-order
*******************************************************************************/
version(BigEndian)
{
private ushort htons (ushort x)
{
return x;
}
private uint htonl (uint x)
{
return x;
}
}
else
{
private import tango.core.BitManip;
private ushort htons (ushort x)
{
return cast(ushort) ((x >> 8) | (x << 8));
}
private uint htonl (uint x)
{
return bswap(x);
}
}
/*******************************************************************************
*******************************************************************************/
version (Win32)
{
pragma (lib, "ws2_32.lib");
private import tango.sys.win32.WsaSock;
private typedef int socket_t = ~0;
package extern (Windows)
{
alias closesocket close;
socket_t socket(int af, int type, int protocol);
int ioctlsocket(socket_t s, int cmd, uint* argp);
uint inet_addr(char* cp);
int bind(socket_t s, Address.sockaddr* name, int namelen);
int connect(socket_t s, Address.sockaddr* name, int namelen);
int listen(socket_t s, int backlog);
socket_t accept(socket_t s, Address.sockaddr* addr, int* addrlen);
int closesocket(socket_t s);
int shutdown(socket_t s, int how);
int getpeername(socket_t s, Address.sockaddr* name, int* namelen);
int getsockname(socket_t s, Address.sockaddr* name, int* namelen);
int send(socket_t s, void* buf, int len, int flags);
int sendto(socket_t s, void* buf, int len, int flags, Address.sockaddr* to, int tolen);
int recv(socket_t s, void* buf, int len, int flags);
int recvfrom(socket_t s, void* buf, int len, int flags, Address.sockaddr* from, int* fromlen);
int select(int nfds, SocketSet.fd* readfds, SocketSet.fd* writefds, SocketSet.fd* errorfds, SocketSet.timeval* timeout);
int getsockopt(socket_t s, int level, int optname, void* optval, int* optlen);
int setsockopt(socket_t s, int level, int optname, void* optval, int optlen);
int gethostname(void* namebuffer, int buflen);
char* inet_ntoa(uint ina);
NetHost.hostent* gethostbyname(char* name);
NetHost.hostent* gethostbyaddr(void* addr, int len, int type);
}
package extern (Windows)
{
bool function (socket_t, uint, void*, DWORD, DWORD, DWORD, DWORD*, OVERLAPPED*) AcceptEx;
bool function (socket_t, HANDLE, DWORD, DWORD, OVERLAPPED*, void*, DWORD) TransmitFile;
bool function (socket_t, void*, int, void*, DWORD, DWORD*, OVERLAPPED*) ConnectEx;
}
static this()
{
WSADATA wd = void;
if (WSAStartup (0x0202, &wd))
throw new SocketException("version of socket library is too old");
DWORD result;
Guid acceptG = {0xb5367df1, 0xcbac, 0x11cf, [0x95,0xca,0x00,0x80,0x5f,0x48,0xa1,0x92]};
Guid connectG = {0x25a207b9, 0xddf3, 0x4660, [0x8e,0xe9,0x76,0xe5,0x8c,0x74,0x06,0x3e]};
Guid transmitG = {0xb5367df0, 0xcbac, 0x11cf, [0x95,0xca,0x00,0x80,0x5f,0x48,0xa1,0x92]};
auto s = cast(HANDLE) socket (AddressFamily.INET, SocketType.STREAM, ProtocolType.TCP);
assert (s != cast(HANDLE) -1);
WSAIoctl (s, SIO_GET_EXTENSION_FUNCTION_POINTER,
&connectG, connectG.sizeof, &ConnectEx,
ConnectEx.sizeof, &result, null, null);
WSAIoctl (s, SIO_GET_EXTENSION_FUNCTION_POINTER,
&acceptG, acceptG.sizeof, &AcceptEx,
AcceptEx.sizeof, &result, null, null);
WSAIoctl (s, SIO_GET_EXTENSION_FUNCTION_POINTER,
&transmitG, transmitG.sizeof, &TransmitFile,
TransmitFile.sizeof, &result, null, null);
closesocket (cast(socket_t) s);
}
static ~this()
{
WSACleanup();
}
}
else
{
private import tango.stdc.errno;
private typedef int socket_t = -1;
package extern (C)
{
socket_t socket(int af, int type, int protocol);
int fcntl(socket_t s, int f, ...);
uint inet_addr(char* cp);
int bind(socket_t s, Address.sockaddr* name, int namelen);
int connect(socket_t s, Address.sockaddr* name, int namelen);
int listen(socket_t s, int backlog);
socket_t accept(socket_t s, Address.sockaddr* addr, int* addrlen);
int close(socket_t s);
int shutdown(socket_t s, int how);
int getpeername(socket_t s, Address.sockaddr* name, int* namelen);
int getsockname(socket_t s, Address.sockaddr* name, int* namelen);
int send(socket_t s, void* buf, int len, int flags);
int sendto(socket_t s, void* buf, int len, int flags, Address.sockaddr* to, int tolen);
int recv(socket_t s, void* buf, int len, int flags);
int recvfrom(socket_t s, void* buf, int len, int flags, Address.sockaddr* from, int* fromlen);
int select(int nfds, SocketSet.fd* readfds, SocketSet.fd* writefds, SocketSet.fd* errorfds, SocketSet.timeval* timeout);
int getsockopt(socket_t s, int level, int optname, void* optval, int* optlen);
int setsockopt(socket_t s, int level, int optname, void* optval, int optlen);
int gethostname(void* namebuffer, int buflen);
char* inet_ntoa(uint ina);
NetHost.hostent* gethostbyname(char* name);
NetHost.hostent* gethostbyaddr(void* addr, int len, int type);
}
}
/*******************************************************************************
*******************************************************************************/
public struct Berkeley
{
socket_t sock;
SocketType type;
AddressFamily family;
ProtocolType protocol;
version (Windows)
bool synchronous;
enum : socket_t
{
INVALID_SOCKET = socket_t.init
}
enum
{
Error = -1
}
alias Error ERROR; // backward compatibility
alias noDelay setNoDelay; // backward compatibility
alias addressReuse setAddressReuse; // backward compatibility
/***********************************************************************
Configure this instance
***********************************************************************/
void open (AddressFamily family, SocketType type, ProtocolType protocol, bool create=true)
{
this.type = type;
this.family = family;
this.protocol = protocol;
if (create)
reopen;
}
/***********************************************************************
Open/reopen a native socket for this instance
***********************************************************************/
void reopen (socket_t sock = sock.init)
{
if (this.sock != sock.init)
this.detach;
if (sock is sock.init)
{
sock = cast(socket_t) socket (family, type, protocol);
if (sock is sock.init)
exception ("Unable to create socket: ");
}
this.sock = sock;
}
/***********************************************************************
calling shutdown() before this is recommended for connection-
oriented sockets
***********************************************************************/
void detach ()
{
if (sock != sock.init)
.close (sock);
sock = sock.init;
}
/***********************************************************************
Return the underlying OS handle of this Conduit
***********************************************************************/
socket_t handle ()
{
return sock;
}
/***********************************************************************
Return the last error
***********************************************************************/
static int lastError ()
{
version (Win32)
return WSAGetLastError();
else
return errno;
}
/***********************************************************************
Is this socket still alive? A closed socket is considered to
be dead, but a shutdown socket is still alive.
***********************************************************************/
bool isAlive ()
{
int type, typesize = type.sizeof;
return getsockopt (sock, SocketOptionLevel.SOCKET,
SocketOption.TYPE, cast(char*) &type,
&typesize) != Error;
}
/***********************************************************************
***********************************************************************/
AddressFamily addressFamily ()
{
return family;
}
/***********************************************************************
***********************************************************************/
Berkeley* bind (Address addr)
{
if(Error == .bind (sock, addr.name, addr.nameLen))
exception ("Unable to bind socket: ");
return this;
}
/***********************************************************************
***********************************************************************/
Berkeley* connect (Address to)
{
if (Error == .connect (sock, to.name, to.nameLen))
{
if (! blocking)
{
auto err = lastError;
version(Windows)
{
if(err is WSAEWOULDBLOCK)
return this;
}
else
{
if (err is EINPROGRESS)
return this;
}
}
exception ("Unable to connect socket: ");
}
return this;
}
/***********************************************************************
need to bind() first
***********************************************************************/
Berkeley* listen (int backlog)
{
if (Error == .listen (sock, backlog))
exception ("Unable to listen on socket: ");
return this;
}
/***********************************************************************
need to bind() first
***********************************************************************/
void accept (ref Berkeley target)
{
auto newsock = .accept (sock, null, null);
if (socket_t.init is newsock)
exception ("Unable to accept socket connection: ");
target.reopen (newsock);
target.protocol = protocol; //same protocol
target.family = family; //same family
target.type = type; //same type
}
/***********************************************************************
The shutdown function shuts down the connection of the socket.
Depending on the argument value, it will:
- stop receiving data for this socket. If further data
arrives, it is rejected.
- stop trying to transmit data from this socket. Also
discards any data waiting to be sent. Stop looking for
acknowledgement of data already sent; don't retransmit
if any data is lost.
***********************************************************************/
Berkeley* shutdown (SocketShutdown how)
{
.shutdown (sock, how);
return this;
}
/***********************************************************************
set linger timeout
***********************************************************************/
Berkeley* linger (int period)
{
version (Win32)
alias ushort attr;
else
alias uint attr;
union linger
{
struct {
attr l_onoff; // option on/off
attr l_linger; // linger time
};
attr[2] array; // combined
}
linger l;
l.l_onoff = 1; // option on/off
l.l_linger = cast(ushort) period; // linger time
return setOption (SocketOptionLevel.SOCKET, SocketOption.LINGER, l.array);
}
/***********************************************************************
enable/disable address reuse
***********************************************************************/
Berkeley* addressReuse (bool enabled)
{
int[1] x = enabled;
return setOption (SocketOptionLevel.SOCKET, SocketOption.REUSEADDR, x);
}
/***********************************************************************
enable/disable noDelay option (nagle)
***********************************************************************/
Berkeley* noDelay (bool enabled)
{
int[1] x = enabled;
return setOption (SocketOptionLevel.TCP, SocketOption.TCP_NODELAY, x);
}
/***********************************************************************
Helper function to handle the adding and dropping of group
membership.
***********************************************************************/
void joinGroup (IPv4Address address, bool onOff)
{
assert (address, "Socket.joinGroup :: invalid null address");
struct ip_mreq
{
uint imr_multiaddr; /* IP multicast address of group */
uint imr_interface; /* local IP address of interface */
};
ip_mreq mrq;
auto option = (onOff) ? SocketOption.ADD_MEMBERSHIP : SocketOption.DROP_MEMBERSHIP;
mrq.imr_interface = 0;
mrq.imr_multiaddr = address.sin.sin_addr;
if (.setsockopt(sock, SocketOptionLevel.IP, option, &mrq, mrq.sizeof) == Error)
exception ("Unable to perform multicast join: ");
}
/***********************************************************************
***********************************************************************/
Address newFamilyObject ()
{
return (family is AddressFamily.INET) ? new IPv4Address : new UnknownAddress;
}
/***********************************************************************
return the hostname
***********************************************************************/
static char[] hostName ()
{
char[64] name;
if(Error == .gethostname (name.ptr, name.length))
exception ("Unable to obtain host name: ");
return name [0 .. strlen(name.ptr)].dup;
}
/***********************************************************************
return the default host address (IPv4)
***********************************************************************/
static uint hostAddress ()
{
auto ih = new NetHost;
ih.getHostByName (hostName);
assert (ih.addrList.length);
return ih.addrList[0];
}
/***********************************************************************
return the remote address of the current connection (IPv4)
***********************************************************************/
Address remoteAddress ()
{
auto addr = newFamilyObject;
auto nameLen = addr.nameLen;
if(Error == .getpeername (sock, addr.name, &nameLen))
exception ("Unable to obtain remote socket address: ");
assert (addr.addressFamily is family);
return addr;
}
/***********************************************************************
return the local address of the current connection (IPv4)
***********************************************************************/
Address localAddress ()
{
auto addr = newFamilyObject;
auto nameLen = addr.nameLen;
if(Error == .getsockname (sock, addr.name, &nameLen))
exception ("Unable to obtain local socket address: ");
assert (addr.addressFamily is family);
return addr;
}
/***********************************************************************
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=SocketFlags.NONE)
{
if (buf.length is 0)
return 0;
version (Posix)
{
auto ret = .send (sock, buf.ptr, buf.length,
SocketFlags.NOSIGNAL + cast(int) flags);
if (errno is EPIPE)
ret = -1;
return ret;
}
else
return .send (sock, buf.ptr, buf.length, cast(int) flags);
}
/***********************************************************************
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)
{
return sendTo (buf, cast(int) flags, to.name, to.nameLen);
}
/***********************************************************************
ditto
***********************************************************************/
int sendTo (void[] buf, Address to)
{
return sendTo (buf, SocketFlags.NONE, to);
}
/***********************************************************************
ditto - assumes you connect()ed
***********************************************************************/
int sendTo (void[] buf, SocketFlags flags=SocketFlags.NONE)
{
return sendTo (buf, cast(int) flags, null, 0);
}
/***********************************************************************
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.
***********************************************************************/
private int sendTo (void[] buf, int flags, Address.sockaddr* to, int len)
{
if (buf.length is 0)
return 0;
version (Posix)
{
auto ret = .sendto (sock, buf.ptr, buf.length,
flags | SocketFlags.NOSIGNAL, to, len);
if (errno is EPIPE)
ret = -1;
return ret;
}
else
return .sendto (sock, buf.ptr, buf.length, flags, to, len);
}
/***********************************************************************
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=SocketFlags.NONE)
{
if (!buf.length)
badArg ("Socket.receive :: target buffer has 0 length");
return .recv(sock, buf.ptr, buf.length, cast(int)flags);
}
/***********************************************************************
Receive data and get the remote endpoint Address. 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, receiveFrom waits until there is data to be
received.
***********************************************************************/
int receiveFrom (void[] buf, SocketFlags flags, Address from)
{
if (!buf.length)
badArg ("Socket.receiveFrom :: target buffer has 0 length");
assert(from.addressFamily() == family);
int nameLen = from.nameLen();
return .recvfrom(sock, buf.ptr, buf.length, cast(int)flags, from.name(), &nameLen);
}
/***********************************************************************
ditto
***********************************************************************/
int receiveFrom (void[] buf, Address from)
{
return receiveFrom(buf, SocketFlags.NONE, from);
}
/***********************************************************************
ditto - assumes you connect()ed
***********************************************************************/
int receiveFrom (void[] buf, SocketFlags flags = SocketFlags.NONE)
{
if (!buf.length)
badArg ("Socket.receiveFrom :: target buffer has 0 length");
return .recvfrom(sock, buf.ptr, buf.length, cast(int)flags, null, null);
}
/***********************************************************************
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(Error == .getsockopt (sock, cast(int)level, cast(int)option, result.ptr, &len))
exception ("Unable to get socket option: ");
return len;
}
/***********************************************************************
***********************************************************************/
Berkeley* setOption (SocketOptionLevel level, SocketOption option, void[] value)
{
if(Error == .setsockopt (sock, cast(int)level, cast(int)option, value.ptr, value.length))
exception ("Unable to set socket option: ");
return this;
}
/***********************************************************************
getter
***********************************************************************/
bool blocking()
{
version(Windows)
{
return synchronous;
}
else
{
return !(fcntl(sock, F_GETFL, 0) & O_NONBLOCK);
}
}
/***********************************************************************
setter
***********************************************************************/
void blocking(bool yes)
{
version(Windows)
{
uint num = !yes;
if(ioctlsocket(sock, consts.FIONBIO, &num) is ERROR)
exception("Unable to set socket blocking: ");
synchronous = yes;
}
else
{
int x = fcntl(sock, F_GETFL, 0);
if(yes)
x &= ~O_NONBLOCK;
else
x |= O_NONBLOCK;
if(fcntl(sock, F_SETFL, x) is ERROR)
exception("Unable to set socket blocking: ");
}
return;
}
/***********************************************************************
***********************************************************************/
static void exception (char[] msg)
{
throw new SocketException (msg ~ SysError.lookup(lastError));
}
/***********************************************************************
***********************************************************************/
protected static void badArg (char[] msg)
{
throw new IllegalArgumentException (msg);
}
}
/*******************************************************************************
*******************************************************************************/
public abstract class Address
{
public struct sockaddr
{
ushort sa_family;
char[14] sa_data = 0;
}
abstract sockaddr* name();
abstract int nameLen();
abstract char[] toString();
abstract AddressFamily addressFamily();
/***********************************************************************
Internal usage
***********************************************************************/
private static ushort ntohs (ushort x)
{
return htons(x);
}
/***********************************************************************
Internal usage
***********************************************************************/
private static uint ntohl (uint x)
{
return htonl(x);
}
/***********************************************************************
Internal usage
***********************************************************************/
private static char[] convert2D (char* s)
{
return s ? s[0 .. strlen(s)] : cast(char[])null;
}
/***********************************************************************
Internal usage
***********************************************************************/
private static char* convert2C (char[] input, char[] output)
{
output [0 .. input.length] = input;
output [input.length] = 0;
return output.ptr;
}
/***********************************************************************
Internal usage
***********************************************************************/
private static char[] fromInt (char[] tmp, int i)
{
int j = tmp.length;
do {
tmp[--j] = cast(char)(i % 10 + '0');
} while (i /= 10);
return tmp [j .. $];
}
/***********************************************************************
Tango: added this common function
***********************************************************************/
static void exception (char[] msg)
{
throw new SocketException (msg);
}
}
/*******************************************************************************
*******************************************************************************/
public class UnknownAddress : Address
{
sockaddr sa;
/***********************************************************************
***********************************************************************/
sockaddr* name()
{
return &sa;
}
/***********************************************************************
***********************************************************************/
int nameLen()
{
return sa.sizeof;
}
/***********************************************************************
***********************************************************************/
AddressFamily addressFamily()
{
return cast(AddressFamily) sa.sa_family;
}
/***********************************************************************
***********************************************************************/
char[] toString()
{
return "Unknown";
}
}
/*******************************************************************************
*******************************************************************************/
public class IPv4Address : Address
{
/***********************************************************************
***********************************************************************/
enum
{
ADDR_ANY = 0,
ADDR_NONE = cast(uint)-1,
PORT_ANY = 0
}
/***********************************************************************
***********************************************************************/
struct sockaddr_in
{
version ( freebsd ){
ubyte sin_len;
ubyte sinfamily = AddressFamily.INET;
} else {
ushort sinfamily = AddressFamily.INET;
}
ushort sin_port;
uint sin_addr; //in_addr
char[8] sin_zero = 0;
}
static assert(sockaddr_in.sizeof is 16);
private char[8] _port;
private sockaddr_in sin;
/***********************************************************************
***********************************************************************/
package this ()
{
}
/***********************************************************************
***********************************************************************/
this (ushort port)
{
sin.sin_addr = 0; //any, "0.0.0.0"
sin.sin_port = htons(port);
}
/***********************************************************************
***********************************************************************/
this (uint addr, ushort port)
{
sin.sin_addr = htonl(addr);
sin.sin_port = htons(port);
}
/***********************************************************************
-port- can be PORT_ANY
-addr- is an IP address or host name
***********************************************************************/
this (char[] addr, int port = PORT_ANY)
{
uint uiaddr = parse(addr);
if(ADDR_NONE == uiaddr)
{
auto ih = new NetHost;
if(!ih.getHostByName(addr))
{
char[16] tmp = void;
exception ("Unable to resolve "~addr~":"~fromInt(tmp, port));
}
uiaddr = ih.addrList[0];
}
sin.sin_addr = htonl(uiaddr);
sin.sin_port = htons(cast(ushort) port);
}
/***********************************************************************
***********************************************************************/
sockaddr* name()
{
return cast(sockaddr*)&sin;
}
/***********************************************************************
***********************************************************************/
int nameLen()
{
return sin.sizeof;
}
/***********************************************************************
***********************************************************************/
AddressFamily addressFamily()
{
return AddressFamily.INET;
}
/***********************************************************************
***********************************************************************/
ushort port()
{
return ntohs(sin.sin_port);
}
/***********************************************************************
***********************************************************************/
uint addr()
{
return ntohl(sin.sin_addr);
}
/***********************************************************************
***********************************************************************/
synchronized char[] toAddrString()
{
return convert2D(inet_ntoa(sin.sin_addr)).dup;
}
/***********************************************************************
***********************************************************************/
char[] toPortString()
{
return fromInt (_port, port());
}
/***********************************************************************
***********************************************************************/
char[] toString()
{
return toAddrString() ~ ":" ~ toPortString();
}
/***********************************************************************
-addr- is an IP address in the format "a.b.c.d"
returns ADDR_NONE on failure
***********************************************************************/
static uint parse(char[] addr)
{
char[64] tmp;
synchronized (IPv4Address.classinfo)
return ntohl(inet_addr(convert2C (addr, tmp)));
}
}
debug(Unittest)
{
unittest
{
IPv4Address ia = new IPv4Address("63.105.9.61", 80);
assert(ia.toString() == "63.105.9.61:80");
}
}
/*******************************************************************************
*******************************************************************************/
public class NetHost
{
char[] name;
char[][] aliases;
uint[] addrList;
/***********************************************************************
***********************************************************************/
struct hostent
{
char* h_name;
char** h_aliases;
version (Win32)
{
short h_addrtype;
short h_length;
}
else
{
int h_addrtype;
int h_length;
}
char** h_addr_list;
char* h_addr()
{
return h_addr_list[0];
}
}
/***********************************************************************
***********************************************************************/
protected void validHostent(hostent* he)
{
if (he.h_addrtype != AddressFamily.INET || he.h_length != 4)
throw new SocketException("Address family mismatch.");
}
/***********************************************************************
***********************************************************************/
void populate(hostent* he)
{
int i;
char* p;
name = Address.convert2D (he.h_name);
for(i = 0;; i++)
{
p = he.h_aliases[i];
if(!p)
break;
}
if(i)
{
aliases = new char[][i];
for(i = 0; i != aliases.length; i++)
{
aliases[i] = Address.convert2D(he.h_aliases[i]);
}
}
else
{
aliases = null;
}
for(i = 0;; i++)
{
p = he.h_addr_list[i];
if(!p)
break;
}
if(i)
{
addrList = new uint[i];
for(i = 0; i != addrList.length; i++)
{
addrList[i] = Address.ntohl(*(cast(uint*)he.h_addr_list[i]));
}
}
else
{
addrList = null;
}
}
/***********************************************************************
***********************************************************************/
bool getHostByName(char[] name)
{
char[1024] tmp;
synchronized (NetHost.classinfo)
{
auto he = gethostbyname(Address.convert2C (name, tmp));
if(!he)
return false;
validHostent(he);
populate(he);
}
return true;
}
/***********************************************************************
***********************************************************************/
bool getHostByAddr(uint addr)
{
uint x = htonl(addr);
synchronized (NetHost.classinfo)
{
auto he = gethostbyaddr(&x, 4, cast(int)AddressFamily.INET);
if(!he)
return false;
validHostent(he);
populate(he);
}
return true;
}
/***********************************************************************
***********************************************************************/
//shortcut
bool getHostByAddr(char[] addr)
{
char[64] tmp;
synchronized (NetHost.classinfo)
{
uint x = inet_addr(Address.convert2C (addr, tmp));
auto he = gethostbyaddr(&x, 4, cast(int)AddressFamily.INET);
if(!he)
return false;
validHostent(he);
populate(he);
}
return true;
}
}
debug (UnitTest)
{
extern (C) int printf(char*, ...);
unittest
{
try
{
NetHost ih = new NetHost;
ih.getHostByName(Berkeley.hostName());
assert(ih.addrList.length > 0);
IPv4Address ia = new IPv4Address(ih.addrList[0], IPv4Address.PORT_ANY);
printf("IP address = %.*s\nname = %.*s\n", ia.toAddrString(), ih.name);
foreach(int i, char[] s; ih.aliases)
{
printf("aliases[%d] = %.*s\n", i, s);
}
printf("---\n");
assert(ih.getHostByAddr(ih.addrList[0]));
printf("name = %.*s\n", ih.name);
foreach(int i, char[] s; ih.aliases)
{
printf("aliases[%d] = %.*s\n", i, s);
}
}
catch( Object o )
{
assert( false );
}
}
}
/*******************************************************************************
a set of sockets for Berkeley.select()
*******************************************************************************/
public class SocketSet
{
private uint nbytes; //Win32: excludes uint.size "count"
private byte* buf;
struct fd {}
struct timeval
{
int seconds;
int microseconds;
}
version(Win32)
{
uint count()
{
return *(cast(uint*)buf);
}
void count(int setter)
{
*(cast(uint*)buf) = setter;
}
socket_t* first()
{
return cast(socket_t*)(buf + uint.sizeof);
}
}
else version (Posix)
{
import tango.core.BitManip;
uint nfdbits;
socket_t _maxfd = 0;
uint fdelt(socket_t s)
{
return cast(uint)s / nfdbits;
}
uint fdmask(socket_t s)
{
return 1 << cast(uint)s % nfdbits;
}
uint* first()
{
return cast(uint*)buf;
}
public socket_t maxfd()
{
return _maxfd;
}
}
public:
this (uint max)
{
version(Win32)
{
nbytes = max * socket_t.sizeof;
buf = (new byte[nbytes + uint.sizeof]).ptr;
count = 0;
}
else version (Posix)
{
if (max <= 32)
nbytes = 32 * uint.sizeof;
else
nbytes = max * uint.sizeof;
buf = (new byte[nbytes]).ptr;
nfdbits = nbytes * 8;
//clear(); //new initializes to 0
}
else
{
static assert(0);
}
}
this (SocketSet o)
{
nbytes = o.nbytes;
auto size = nbytes;
version (Win32)
size += uint.sizeof;
version (Posix)
{
nfdbits = o.nfdbits;
_maxfd = o._maxfd;
}
auto b = new byte[size];
b[] = o.buf[0..size];
buf = b.ptr;
}
this()
{
version(Win32)
{
this(64);
}
else version (Posix)
{
this(32);
}
else
{
static assert(0);
}
}
SocketSet dup()
{
return new SocketSet (this);
}
SocketSet reset()
{
version(Win32)
{
count = 0;
}
else version (Posix)
{
buf[0 .. nbytes] = 0;
_maxfd = 0;
}
else
{
static assert(0);
}
return this;
}
void add(socket_t s)
in
{
version(Win32)
{
assert(count < max); //added too many sockets; specify a higher max in the constructor
}
}
body
{
version(Win32)
{
uint c = count;
first[c] = s;
count = c + 1;
}
else version (Posix)
{
if (s > _maxfd)
_maxfd = s;
bts(cast(uint*)&first[fdelt(s)], cast(uint)s % nfdbits);
}
else
{
static assert(0);
}
}
void add(Berkeley* s)
{
add(s.handle);
}
void remove(socket_t s)
{
version(Win32)
{
uint c = count;
socket_t* start = first;
socket_t* stop = start + c;
for(; start != stop; start++)
{
if(*start == s)
goto found;
}
return; //not found
found:
for(++start; start != stop; start++)
{
*(start - 1) = *start;
}
count = c - 1;
}
else version (Posix)
{
btr(cast(uint*)&first[fdelt(s)], cast(uint)s % nfdbits);
// If we're removing the biggest file descriptor we've
// entered so far we need to recalculate this value
// for the socket set.
if (s == _maxfd)
{
while (--_maxfd >= 0)
{
if (isSet(_maxfd))
{
break;
}
}
}
}
else
{
static assert(0);
}
}
void remove(Berkeley* s)
{
remove(s.handle);
}
int isSet(socket_t s)
{
version(Win32)
{
socket_t* start = first;
socket_t* stop = start + count;
for(; start != stop; start++)
{
if(*start == s)
return true;
}
return false;
}
else version (Posix)
{
//return bt(cast(uint*)&first[fdelt(s)], cast(uint)s % nfdbits);
int index = cast(uint)s % nfdbits;
return (cast(uint*)&first[fdelt(s)])[index / (uint.sizeof*8)] & (1 << (index & ((uint.sizeof*8) - 1)));
}
else
{
static assert(0);
}
}
int isSet(Berkeley* s)
{
return isSet(s.handle);
}
uint max()
{
return nbytes / socket_t.sizeof;
}
fd* toFd_set()
{
return cast(fd*)buf;
}
/***********************************************************************
SocketSet's are updated to include only those sockets which an
event occured.
Returns the number of events, 0 on timeout, or -1 on error
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, timeval* tv)
{
fd* fr, fw, fe;
//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);
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
{
fr = checkRead ? checkRead.toFd_set() : null;
fw = checkWrite ? checkWrite.toFd_set() : null;
fe = checkError ? checkError.toFd_set() : null;
}
int result;
version(Win32)
{
while ((result = .select (socket_t.max - 1, fr, fw, fe, tv)) == -1)
{
if(WSAGetLastError() != WSAEINTR)
break;
}
}
else version (Posix)
{
socket_t maxfd = 0;
if (checkRead)
maxfd = checkRead.maxfd;
if (checkWrite && checkWrite.maxfd > maxfd)
maxfd = checkWrite.maxfd;
if (checkError && checkError.maxfd > maxfd)
maxfd = checkError.maxfd;
while ((result = .select (maxfd + 1, fr, fw, fe, tv)) == -1)
{
if(errno() != EINTR)
break;
}
}
else
{
static assert(0);
}
return result;
}
/***********************************************************************
select with specified timeout
***********************************************************************/
static int select (SocketSet checkRead, SocketSet checkWrite, SocketSet checkError, long microseconds)
{
timeval tv = {
cast(int)(microseconds / 1000000),
cast(int)(microseconds % 1000000)
};
return select (checkRead, checkWrite, checkError, &tv);
}
/***********************************************************************
select with maximum timeout
***********************************************************************/
static int select (SocketSet checkRead, SocketSet checkWrite, SocketSet checkError)
{
return select (checkRead, checkWrite, checkError, null);
}
}
|