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 }