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 auto val() const { return cast(size_t) value; }
244         @property bool isInvalid() const
245         {
246             return value == invalidValue.value;
247         }
248     }
249 }
250 else version(Posix)
251 {
252     alias SocketHandle = platform_core.FileHandle;
253 }
254 
255 struct SockResult
256 {
257     static SockResult pass() { return SockResult(0); }
258 
259     private cint value;
260     // NOTE: we could check that value == SOCKET_ERROR, however, if
261     //       a function returned neither 0 or SOCKET_ERROR, then we should
262     //       treat it as a failure.
263     @property bool failed() const { return value != 0; }
264     @property bool passed() const { return value == 0; }
265 }
266 struct SockLengthResult
267 {
268     private cint value;
269     /**
270     NOTE: will return failed if return value is not positive
271     */
272     @property cint val() const { return cast(cint)value; }
273     @property bool failed() const { return value < 0; }
274     @property cuint length() in { assert(!failed); } do
275     {
276         return cast(cuint)value;
277     }
278 }
279 struct SendResult
280 {
281     private SockLengthResult result;
282     @property auto val() const { return result.val; }
283     @property bool failed() const { return result.failed; }
284 }
285 
286 SocketHandle createsocket(AddressFamily family, SocketType type, Protocol protocol)
287 {
288     return platform_sock.socket(family, type, protocol);
289 }
290 
291 version (Windows)
292 {
293     extern(Windows) nothrow @nogc
294     {
295         SockResult closesocket(SocketHandle);
296     }
297 }
298 else version (Posix)
299 {
300     pragma(inline) int closesocket(SocketHandle handle) nothrow @nogc
301     {
302         return platform_core.close(handle);
303     }
304 }
305 else
306 {
307     static assert(0, "closesocket not declared for this OS");
308 }
309 
310 pragma(inline)
311 auto bind(T)(SocketHandle sock, const(T)* addr)
312     if( is(T == inet_sockaddr) || is(T == sockaddr_in) /* add more types */ )
313 {
314     return platform_sock.bind(sock, cast(const(sockaddr)*)addr, T.sizeof);
315 }
316 pragma(inline)
317 auto connect(T)(SocketHandle sock, const(T)* addr)
318     if( is(T == inet_sockaddr) || is(T == sockaddr_in) /* add more types */ )
319 {
320     return platform_sock.connect(sock, cast(sockaddr*)addr, T.sizeof);
321 }
322 pragma(inline)
323 auto accept(T)(SocketHandle sock, T* addr)
324     if( is(T == inet_sockaddr) || is(T == sockaddr_in) /* add more types */ )
325 {
326     socklen_t fromlen = T.sizeof;
327     return platform_sock.accept(sock, cast(sockaddr*)addr, &fromlen);
328 }
329 pragma(inline)
330 auto send(T)(SocketHandle sock, const(T)* buffer, size_t len, uint flags = 0)
331     if(T.sizeof == 1 && !is(T == ubyte))
332 {
333     return platform_sock.send(sock, cast(const(ubyte)*)buffer, len, flags);
334 }
335 pragma(inline)
336 auto send(T)(SocketHandle sock, const(T)[] buffer, uint flags = 0)
337     if(T.sizeof == 1)
338 {
339     return platform_sock.send(sock, cast(const(ubyte)*)buffer, buffer.length, flags);
340 }
341 pragma(inline)
342 auto recv(T)(SocketHandle sock, T* buffer, uint len, uint flags = 0)
343     if(T.sizeof == 1 && !is(T == ubyte))
344 {
345     return platform_sock.recv(sock, cast(ubyte*)buffer, len, flags);
346 }
347 pragma(inline)
348 auto recv(T)(SocketHandle sock, T[] buffer, uint flags = 0)
349     if(T.sizeof == 1)
350 {
351     return platform_sock.recv(sock, cast(ubyte*)buffer.ptr, buffer.length, flags);
352 }
353 pragma(inline)
354 auto recvfrom(T,U)(SocketHandle sock, T[] buffer, uint flags, U* from)
355     if(T.sizeof == 1 && is(U == inet_sockaddr) || is(U == sockaddr_in) /* add more types */ )
356 {
357     uint fromlen = U.sizeof;
358     return platform_sock.recvfrom(sock, cast(ubyte*)buffer.ptr, buffer.length, flags, cast(sockaddr*)from, &fromlen);
359 }
360 pragma(inline)
361 auto sendto(T,U)(SocketHandle sock, const(T)[] buffer, uint flags, const(U)* to)
362     if(T.sizeof == 1 && is(U == inet_sockaddr) || is(U == sockaddr_in) /* add more types */ )
363 {
364     auto sent = platform_sock.sendto(sock, cast(const(ubyte)*)buffer.ptr, buffer.length, flags, cast(const(sockaddr)*)to, U.sizeof);
365     return SendResult(sent);
366 }
367 
368 struct fd_set
369 {
370     uint fd_count;
371     union
372     {
373         SocketHandle[0] fd_array_0;
374         SocketHandle fd_array_first;
375     }
376     inout(SocketHandle)* fd_array() inout
377     {
378         return &fd_array_first;
379     }
380 }
381 struct fd_set_storage(size_t size)
382 {
383     uint fd_count;
384     SocketHandle[size] fd_array;
385     @property fd_set* ptr()
386     {
387         return cast(fd_set*)&this;
388     }
389     void addNoCheck(SocketHandle sock)
390     {
391         fd_array[fd_count++] = sock;
392     }
393 }
394 version (Windows)
395 {
396     extern(Windows) nothrow @nogc
397     {
398         // TODO: return value should by typed
399         int select(int ignore, fd_set* readfds, fd_set* writefds, fd_set* exceptfds, platform_sock.timeval* timeout);
400     }
401 }
402 
403 
404 
405 alias Blocking = Flag!"blocking";
406 
407 passfail setMode(SocketHandle sock, Blocking blocking)
408 {
409     version (Windows)
410     {
411         uint ioctlArg = blocking ? 0 : 0xFFFFFFFF;
412         return platform_sock.ioctlsocket(sock, platform_sock.FIONBIO, &ioctlArg).passed ? passfail.pass : passfail.fail;
413     }
414     else
415     {
416         auto flags = platform_core.fcntlGetFlags(sock);
417         if (flags.isInvalid)
418             return passfail.fail;
419         return platform_core.fcntlSetFlags(sock, flags | platform_core.O_NONBLOCK);
420     }
421 }