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 }