1 /**
2 Contains a platform independent socket library.
3 
4 Use SocketHandle to represent a handle to a socket.
5 
6 This file should contain the public platform independent interface to the socket library.
7 If you want platform dependent definitions, you can import more.net.os.<platform>.sock.
8 
9 Anything that can be exposed as platform-independent should be defined in this module.
10 */
11 module more.net.sock;
12 
13 static import core.stdc.string;
14 
15 import std.format : format, formattedWrite;
16 import std.bitmanip : nativeToBigEndian;
17 import std.typecons : Flag, Yes, No;
18 import bitmanip = std.bitmanip;
19 
20 import more.types : passfail;
21 import more.c : cint, cuint;
22 import more.format : StringSink;
23 version(Windows)
24 {
25     import platform_sock = more.os.windows.sock;
26     public import more.os.windows.core : lastError;
27 }
28 else version(Posix)
29 {
30     import platform_sock = more.os.posix.sock;
31     import platform_core = more.os.posix.core;
32     public import more.os.posix.core : lastError;
33 }
34 else static assert(0);
35 
36 enum AddressFamily : ushort
37 {
38     unspec    = platform_sock.AF_UNSPEC,
39     unix      = platform_sock.AF_UNIX,
40     inet      = platform_sock.AF_INET,
41     inet6     = platform_sock.AF_INET6,
42     ipx       = platform_sock.AF_IPX,
43     appleTalk = platform_sock.AF_APPLETALK,
44 }
45 enum SocketType : int
46 {
47     stream    = platform_sock.SOCK_STREAM,
48     dgram     = platform_sock.SOCK_DGRAM,
49     raw       = platform_sock.SOCK_RAW,
50     rdm       = platform_sock.SOCK_RDM,
51     seqPacket = platform_sock.SOCK_SEQPACKET,
52 }
53 enum Protocol : int
54 {
55     raw  = platform_sock.IPPROTO_RAW,
56     udp  = platform_sock.IPPROTO_UDP,
57     tcp  = platform_sock.IPPROTO_TCP,
58     ip   = platform_sock.IPPROTO_IP,
59     ipv6 = platform_sock.IPPROTO_IPV6,
60     icmp = platform_sock.IPPROTO_ICMP,
61     igmp = platform_sock.IPPROTO_IGMP,
62     ggp  = platform_sock.IPPROTO_GGP,
63     pup  = platform_sock.IPPROTO_PUP,
64     idp  = platform_sock.IPPROTO_IDP,
65 }
66 
67 T ntohs(T)(T value) if(T.sizeof == 2)
68 {
69     auto result = bitmanip.nativeToBigEndian(value.toUshort);
70     return *(cast(T*)&result);
71 }
72 T htons(T)(T value) if(T.sizeof == 2)
73 {
74     auto result = bitmanip.nativeToBigEndian(value.toUshort);
75     return *(cast(T*)&result);
76 }
77 ushort htons(ushort value)
78 {
79     auto result = bitmanip.nativeToBigEndian(value);
80     return *(cast(ushort*)&result);
81 }
82 T ntohl(T)(T value) if(T.sizeof == 4)
83 {
84     auto result = bitmanip.nativeToBigEndian(value);
85     return *(cast(T*)&result);
86 }
87 T htonl(T)(T value) if(T.sizeof == 4)
88 {
89     auto result = bitmanip.nativeToBigEndian(value);
90     return *(cast(T*)&result);
91 }
92 
93 
94 struct in_addr
95 {
96     @property static in_addr any() { return in_addr(0); }
97     uint s_addr;
98 }
99 struct in6_addr
100 {
101     @property static in6_addr any() { return in6_addr(); }
102     ubyte[16] s6_addr;
103 }
104 
105 union inet_addr
106 {
107     in_addr ipv4;
108     in6_addr ipv6;
109 }
110 
111 struct sockaddr
112 {
113     AddressFamily sa_family;
114     char[14] sa_data;
115     /*
116     static void assign(sockaddr* dst, sockaddr* src)
117     {
118         auto size = sockaddrsize(src.sa_family);
119         (cast(ubyte*)dst)[0..size] == (cast(ubyte*)src)[0..size];
120     }
121     */
122 }
123 
124 struct Port
125 {
126     private ushort value;
127     ushort toUshort() const { return value; }
128     void toString(StringSink sink) const
129     {
130         formattedWrite(sink, "%s", toUshort);
131     }
132 }
133 pragma(inline) ushort toUshort(ushort value) { return value; }
134 
135 struct sockaddr_in
136 {
137     AddressFamily sin_family;
138     Port sin_port;
139     in_addr sin_addr;
140     version(Windows)
141     {
142         char[] sin_zero;
143     }
144     bool equals(ref const(sockaddr_in) other) const
145     {
146         return sin_port == other.sin_port &&
147             sin_addr.s_addr == other.sin_addr.s_addr;
148     }
149     void toString(scope void delegate(const(char)[]) sink) const
150     {
151         assert(sin_family == AddressFamily.inet);
152         auto addr = ntohl(sin_addr.s_addr);
153         formattedWrite(sink, "%s.%s.%s.%s:%s",
154                       (addr >> 24),
155                       (addr >> 16) & 0xFF,
156                       (addr >>  8) & 0xFF,
157                       (addr >>  0) & 0xFF,
158                       ntohs(sin_port));
159     }
160 }
161 struct sockaddr_in6
162 {
163     AddressFamily sin6_family;
164     Port   sin6_port;
165     uint        sin6_flowinfo;
166     in6_addr    sin6_addr;
167     uint        sin6_scope_id;
168 }
169 
170 
171 private enum INET6_ADDRSTRLEN = 46;
172 
173 // a sockaddr meant to hold either an ipv4 or ipv6 socket address.
174 union inet_sockaddr
175 {
176     struct
177     {
178         AddressFamily family;
179         Port in_port;
180     }
181     sockaddr     sa;
182     sockaddr_in  ipv4;
183     sockaddr_in6 ipv6;
184     this(const Port sin_port, in_addr sin_addr)
185     {
186         ipv4.sin_family = AddressFamily.inet;
187         ipv4.sin_port   = sin_port;
188         ipv4.sin_addr   = sin_addr;
189     }
190     this(const Port sin6_port, in6_addr sin6_addr)
191     {
192         ipv4.sin_family = AddressFamily.inet6;
193         ipv6.sin6_port   = sin6_port;
194         ipv6.sin6_addr   = sin6_addr;
195     }
196     void toString(scope void delegate(const(char)[]) sink) const
197     {
198         if(family == AddressFamily.inet)
199         {
200             ipv4.toString(sink);
201         }
202         else if(family == AddressFamily.inet6)
203         {
204             char[INET6_ADDRSTRLEN] str;
205             version(Windows)
206             {
207                 assert(0, "inet_sockaddr ipv6 toString not implemented");
208             }
209             else
210             {
211                 assert(platform_sock.inet_ntop(AddressFamily.inet6, &ipv6.sin6_addr, str.ptr, str.length),
212                     format("inet_ntop failed (e=%s)", lastError()));
213             }
214             formattedWrite(sink, "[%s]:%s", str.ptr[0..core.stdc..string.strlen(str.ptr)], ntohs(in_port));
215         } else {
216           formattedWrite(sink, "<unknown_family:%s>", family);
217         }
218     }
219     bool equals(ref const(inet_sockaddr) other) const
220     {
221         if(family != other.family || in_port != other.in_port)
222             return false;
223         if(family == AddressFamily.inet) {
224             return ipv4.sin_addr.s_addr == other.ipv4.sin_addr.s_addr;
225         } else if(family == AddressFamily.inet6) {
226             assert(0, "not implemented");
227         } else {
228             assert(0, "not currently handled");
229         }
230     }
231 }
232 
233 version (Windows)
234 {
235     struct SocketHandle
236     {
237         @property static SocketHandle invalidValue()
238         {
239             return SocketHandle(size_t.max);
240         }
241 
242         private size_t value;
243         @property bool isInvalid() const
244         {
245             return value == invalidValue.value;
246         }
247     }
248 }
249 else version(Posix)
250 {
251     alias SocketHandle = platform_core.FileHandle;
252 }
253 
254 struct SockResult
255 {
256     static SockResult pass() { return SockResult(0); }
257 
258     private cint value;
259     // NOTE: we could check that value == SOCKET_ERROR, however, if
260     //       a function returned neither 0 or SOCKET_ERROR, then we should
261     //       treat it as a failure.
262     @property bool failed() const { return value != 0; }
263     @property bool passed() const { return value == 0; }
264 }
265 struct SockLengthResult
266 {
267     private cint value;
268     /**
269     NOTE: will return failed if return value is not positive
270     */
271     @property bool failed() const { return value < 0; }
272     @property cuint length() in { assert(!failed); } do
273     {
274         return cast(cuint)value;
275     }
276 }
277 struct SendResult
278 {
279     private SockLengthResult result;
280     private cuint expected;
281     @property bool failed() const { return result.failed; }
282     /**
283     NOTE: will return failed if return value is not positive
284     */
285     @property bool sentAll() const { return result.value == expected; }
286     @property auto sent() const { return result.value; }
287 }
288 
289 SocketHandle createsocket(AddressFamily family, SocketType type, Protocol protocol)
290 {
291     return platform_sock.socket(family, type, protocol);
292 }
293 
294 version (Windows)
295 {
296     extern(Windows) nothrow @nogc
297     {
298         SockResult closesocket(SocketHandle);
299     }
300 }
301 else version (Posix)
302 {
303     pragma(inline) int closesocket(SocketHandle handle) nothrow @nogc
304     {
305         return platform_core.close(handle);
306     }
307 }
308 else
309 {
310     static assert(0, "closesocket not declared for this OS");
311 }
312 
313 pragma(inline)
314 auto bind(T)(SocketHandle sock, const(T)* addr)
315     if( is(T == inet_sockaddr) || is(T == sockaddr_in) /* add more types */ )
316 {
317     return platform_sock.bind(sock, cast(const(sockaddr)*)addr, T.sizeof);
318 }
319 pragma(inline)
320 auto connect(T)(SocketHandle sock, const(T)* addr)
321     if( is(T == inet_sockaddr) || is(T == sockaddr_in) /* add more types */ )
322 {
323     return platform_sock.connect(sock, cast(sockaddr*)addr, T.sizeof);
324 }
325 pragma(inline)
326 auto accept(T)(SocketHandle sock, T* addr)
327     if( is(T == inet_sockaddr) || is(T == sockaddr_in) /* add more types */ )
328 {
329     socklen_t fromlen = T.sizeof;
330     return platform_sock.accept(sock, cast(sockaddr*)addr, &fromlen);
331 }
332 pragma(inline)
333 auto send(T)(SocketHandle sock, const(T)* buffer, size_t len, uint flags = 0)
334     if(T.sizeof == 1 && !is(T == ubyte))
335 {
336     return platform_sock.send(sock, cast(const(ubyte)*)buffer, len, flags);
337 }
338 pragma(inline)
339 auto send(T)(SocketHandle sock, const(T)[] buffer, uint flags = 0)
340     if(T.sizeof == 1)
341 {
342     return platform_sock.send(sock, cast(const(ubyte)*)buffer, buffer.length, flags);
343 }
344 pragma(inline)
345 auto recv(T)(SocketHandle sock, T* buffer, uint len, uint flags = 0)
346     if(T.sizeof == 1 && !is(T == ubyte))
347 {
348     return platform_sock.recv(sock, cast(ubyte*)buffer, len, flags);
349 }
350 pragma(inline)
351 auto recv(T)(SocketHandle sock, T[] buffer, uint flags = 0)
352     if(T.sizeof == 1)
353 {
354     return platform_sock.recv(sock, cast(ubyte*)buffer.ptr, buffer.length, flags);
355 }
356 pragma(inline)
357 auto recvfrom(T,U)(SocketHandle sock, T[] buffer, uint flags, U* from)
358     if(T.sizeof == 1 && is(U == inet_sockaddr) || is(U == sockaddr_in) /* add more types */ )
359 {
360     uint fromlen = U.sizeof;
361     return platform_sock.recvfrom(sock, cast(ubyte*)buffer.ptr, buffer.length, flags, cast(sockaddr*)from, &fromlen);
362 }
363 pragma(inline)
364 auto sendto(T,U)(SocketHandle sock, const(T)[] buffer, uint flags, const(U)* to)
365     if(T.sizeof == 1 && is(U == inet_sockaddr) || is(U == sockaddr_in) /* add more types */ )
366 {
367     auto sent = platform_sock.sendto(sock, cast(const(ubyte)*)buffer.ptr, buffer.length, flags, cast(const(sockaddr)*)to, U.sizeof);
368     return SendResult(sent, buffer.length);
369 }
370 
371 struct fd_set
372 {
373     uint fd_count;
374     union
375     {
376         SocketHandle[0] fd_array_0;
377         SocketHandle fd_array_first;
378     }
379     inout(SocketHandle)* fd_array() inout
380     {
381         return &fd_array_first;
382     }
383 }
384 struct fd_set_storage(size_t size)
385 {
386     uint fd_count;
387     SocketHandle[size] fd_array;
388     @property fd_set* ptr()
389     {
390         return cast(fd_set*)&this;
391     }
392     void addNoCheck(SocketHandle sock)
393     {
394         fd_array[fd_count++] = sock;
395     }
396 }
397 version (Windows)
398 {
399     extern(Windows) nothrow @nogc
400     {
401         // TODO: return value should by typed
402         int select(int ignore, fd_set* readfds, fd_set* writefds, fd_set* exceptfds, platform_sock.timeval* timeout);
403     }
404 }
405 
406 
407 
408 alias Blocking = Flag!"blocking";
409 
410 passfail setMode(SocketHandle sock, Blocking blocking)
411 {
412     version (Windows)
413     {
414         uint ioctlArg = blocking ? 0 : 0xFFFFFFFF;
415         return platform_sock.ioctlsocket(sock, platform_sock.FIONBIO, &ioctlArg).passed ? passfail.pass : passfail.fail;
416     }
417     else
418     {
419         auto flags = platform_core.fcntlGetFlags(sock);
420         if (flags.isInvalid)
421             return passfail.fail;
422         return platform_core.fcntlSetFlags(sock, flags | platform_core.O_NONBLOCK);
423     }
424 }