145
|
1 // Written in the D programming language
|
|
2
|
|
3 /*
|
|
4 Copyright (C) 2004-2011 Christopher E. Miller
|
|
5
|
|
6 Boost Software License - Version 1.0 - August 17th, 2003
|
|
7
|
|
8 Permission is hereby granted, free of charge, to any person or organization
|
|
9 obtaining a copy of the software and accompanying documentation covered by
|
|
10 this license (the "Software") to use, reproduce, display, distribute,
|
|
11 execute, and transmit the Software, and to prepare derivative works of the
|
|
12 Software, and to permit third-parties to whom the Software is furnished to
|
|
13 do so, all subject to the following:
|
|
14
|
|
15 The copyright notices in the Software and this entire statement, including
|
|
16 the above license grant, this restriction and the following disclaimer,
|
|
17 must be included in all copies of the Software, in whole or in part, and
|
|
18 all derivative works of the Software, unless such copies or derivative
|
|
19 works are solely in the form of machine-executable object code generated by
|
|
20 a source language processor.
|
|
21
|
|
22 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
23 IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
24 FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT
|
|
25 SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE
|
|
26 FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE,
|
|
27 ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
|
|
28 DEALINGS IN THE SOFTWARE.
|
|
29
|
|
30 socket.d 1.4
|
|
31 Jan 2011
|
|
32
|
|
33 Thanks to Benjamin Herr for his assistance.
|
|
34 */
|
|
35
|
|
36 /**
|
|
37 * Socket primitives.
|
|
38 * Example: See $(SAMPLESRC listener.d) and $(SAMPLESRC htmlget.d)
|
|
39 * License: $(HTTP www.boost.org/LICENSE_1_0.txt, Boost License 1.0).
|
|
40 * Authors: Christopher E. Miller, $(HTTP klickverbot.at, David Nadlinger),
|
|
41 * $(HTTP thecybershadow.net, Vladimir Panteleev)
|
|
42 * Source: $(PHOBOSSRC std/_socket.d)
|
|
43 */
|
|
44
|
|
45 module std.socket;
|
|
46
|
|
47 import core.stdc.stdint, core.stdc.stdlib, core.stdc.string, std.conv, std.string;
|
|
48
|
|
49 import core.stdc.config;
|
|
50 import core.time : dur, Duration;
|
|
51 import std.exception;
|
|
52
|
|
53 import std.internal.cstring;
|
|
54
|
|
55
|
|
56 @safe:
|
|
57
|
|
58 version (Windows)
|
|
59 {
|
|
60 pragma (lib, "ws2_32.lib");
|
|
61 pragma (lib, "wsock32.lib");
|
|
62
|
|
63 import core.sys.windows.windows, std.windows.syserror;
|
|
64 public import core.sys.windows.winsock2;
|
|
65 private alias _ctimeval = core.sys.windows.winsock2.timeval;
|
|
66 private alias _clinger = core.sys.windows.winsock2.linger;
|
|
67
|
|
68 enum socket_t : SOCKET { INVALID_SOCKET }
|
|
69 private const int _SOCKET_ERROR = SOCKET_ERROR;
|
|
70
|
|
71
|
|
72 private int _lasterr() nothrow @nogc
|
|
73 {
|
|
74 return WSAGetLastError();
|
|
75 }
|
|
76 }
|
|
77 else version (Posix)
|
|
78 {
|
|
79 version (linux)
|
|
80 {
|
|
81 enum : int
|
|
82 {
|
|
83 TCP_KEEPIDLE = 4,
|
|
84 TCP_KEEPINTVL = 5
|
|
85 }
|
|
86 }
|
|
87
|
|
88 import core.sys.posix.arpa.inet;
|
|
89 import core.sys.posix.fcntl;
|
|
90 import core.sys.posix.netdb;
|
|
91 import core.sys.posix.netinet.in_;
|
|
92 import core.sys.posix.netinet.tcp;
|
|
93 import core.sys.posix.sys.select;
|
|
94 import core.sys.posix.sys.socket;
|
|
95 import core.sys.posix.sys.time;
|
|
96 import core.sys.posix.sys.un : sockaddr_un;
|
|
97 import core.sys.posix.unistd;
|
|
98 private alias _ctimeval = core.sys.posix.sys.time.timeval;
|
|
99 private alias _clinger = core.sys.posix.sys.socket.linger;
|
|
100
|
|
101 import core.stdc.errno;
|
|
102
|
|
103 enum socket_t : int32_t { init = -1 }
|
|
104 private const int _SOCKET_ERROR = -1;
|
|
105
|
|
106 private enum : int
|
|
107 {
|
|
108 SD_RECEIVE = SHUT_RD,
|
|
109 SD_SEND = SHUT_WR,
|
|
110 SD_BOTH = SHUT_RDWR
|
|
111 }
|
|
112
|
|
113 private int _lasterr() nothrow @nogc
|
|
114 {
|
|
115 return errno;
|
|
116 }
|
|
117 }
|
|
118 else
|
|
119 {
|
|
120 static assert(0); // No socket support yet.
|
|
121 }
|
|
122
|
|
123 version (unittest)
|
|
124 {
|
|
125 static assert(is(uint32_t == uint));
|
|
126 static assert(is(uint16_t == ushort));
|
|
127
|
|
128 import std.stdio : writefln;
|
|
129
|
|
130 // Print a message on exception instead of failing the unittest.
|
|
131 private void softUnittest(void delegate() @safe test, int line = __LINE__) @trusted
|
|
132 {
|
|
133 try
|
|
134 test();
|
|
135 catch (Throwable e)
|
|
136 {
|
|
137 writefln(" --- std.socket(%d) test fails depending on environment ---", line);
|
|
138 writefln(" (%s)", e);
|
|
139 }
|
|
140 }
|
|
141 }
|
|
142
|
|
143 /// Base exception thrown by $(D std.socket).
|
|
144 class SocketException: Exception
|
|
145 {
|
|
146 mixin basicExceptionCtors;
|
|
147 }
|
|
148
|
|
149
|
|
150 /*
|
|
151 * Needs to be public so that SocketOSException can be thrown outside of
|
|
152 * std.socket (since it uses it as a default argument), but it probably doesn't
|
|
153 * need to actually show up in the docs, since there's not really any public
|
|
154 * need for it outside of being a default argument.
|
|
155 */
|
|
156 string formatSocketError(int err) @trusted
|
|
157 {
|
|
158 version (Posix)
|
|
159 {
|
|
160 char[80] buf;
|
|
161 const(char)* cs;
|
|
162 version (CRuntime_Glibc)
|
|
163 {
|
|
164 cs = strerror_r(err, buf.ptr, buf.length);
|
|
165 }
|
|
166 else
|
|
167 {
|
|
168 auto errs = strerror_r(err, buf.ptr, buf.length);
|
|
169 if (errs == 0)
|
|
170 cs = buf.ptr;
|
|
171 else
|
|
172 return "Socket error " ~ to!string(err);
|
|
173 }
|
|
174
|
|
175 auto len = strlen(cs);
|
|
176
|
|
177 if (cs[len - 1] == '\n')
|
|
178 len--;
|
|
179 if (cs[len - 1] == '\r')
|
|
180 len--;
|
|
181 return cs[0 .. len].idup;
|
|
182 }
|
|
183 else
|
|
184 version (Windows)
|
|
185 {
|
|
186 return sysErrorString(err);
|
|
187 }
|
|
188 else
|
|
189 return "Socket error " ~ to!string(err);
|
|
190 }
|
|
191
|
|
192 /// Retrieve the error message for the most recently encountered network error.
|
|
193 @property string lastSocketError()
|
|
194 {
|
|
195 return formatSocketError(_lasterr());
|
|
196 }
|
|
197
|
|
198 /**
|
|
199 * Socket exceptions representing network errors reported by the operating
|
|
200 * system.
|
|
201 */
|
|
202 class SocketOSException: SocketException
|
|
203 {
|
|
204 int errorCode; /// Platform-specific error code.
|
|
205
|
|
206 ///
|
|
207 this(string msg,
|
|
208 string file = __FILE__,
|
|
209 size_t line = __LINE__,
|
|
210 Throwable next = null,
|
|
211 int err = _lasterr(),
|
|
212 string function(int) @trusted errorFormatter = &formatSocketError)
|
|
213 {
|
|
214 errorCode = err;
|
|
215
|
|
216 if (msg.length)
|
|
217 super(msg ~ ": " ~ errorFormatter(err), file, line, next);
|
|
218 else
|
|
219 super(errorFormatter(err), file, line, next);
|
|
220 }
|
|
221
|
|
222 ///
|
|
223 this(string msg,
|
|
224 Throwable next,
|
|
225 string file = __FILE__,
|
|
226 size_t line = __LINE__,
|
|
227 int err = _lasterr(),
|
|
228 string function(int) @trusted errorFormatter = &formatSocketError)
|
|
229 {
|
|
230 this(msg, file, line, next, err, errorFormatter);
|
|
231 }
|
|
232
|
|
233 ///
|
|
234 this(string msg,
|
|
235 int err,
|
|
236 string function(int) @trusted errorFormatter = &formatSocketError,
|
|
237 string file = __FILE__,
|
|
238 size_t line = __LINE__,
|
|
239 Throwable next = null)
|
|
240 {
|
|
241 this(msg, file, line, next, err, errorFormatter);
|
|
242 }
|
|
243 }
|
|
244
|
|
245 /// Socket exceptions representing invalid parameters specified by user code.
|
|
246 class SocketParameterException: SocketException
|
|
247 {
|
|
248 mixin basicExceptionCtors;
|
|
249 }
|
|
250
|
|
251 /**
|
|
252 * Socket exceptions representing attempts to use network capabilities not
|
|
253 * available on the current system.
|
|
254 */
|
|
255 class SocketFeatureException: SocketException
|
|
256 {
|
|
257 mixin basicExceptionCtors;
|
|
258 }
|
|
259
|
|
260
|
|
261 /**
|
|
262 * Returns:
|
|
263 * $(D true) if the last socket operation failed because the socket
|
|
264 * was in non-blocking mode and the operation would have blocked.
|
|
265 */
|
|
266 bool wouldHaveBlocked() nothrow @nogc
|
|
267 {
|
|
268 version (Windows)
|
|
269 return _lasterr() == WSAEWOULDBLOCK;
|
|
270 else version (Posix)
|
|
271 return _lasterr() == EAGAIN;
|
|
272 else
|
|
273 static assert(0);
|
|
274 }
|
|
275
|
|
276
|
|
277 private immutable
|
|
278 {
|
|
279 typeof(&getnameinfo) getnameinfoPointer;
|
|
280 typeof(&getaddrinfo) getaddrinfoPointer;
|
|
281 typeof(&freeaddrinfo) freeaddrinfoPointer;
|
|
282 }
|
|
283
|
|
284 shared static this() @system
|
|
285 {
|
|
286 version (Windows)
|
|
287 {
|
|
288 WSADATA wd;
|
|
289
|
|
290 // Winsock will still load if an older version is present.
|
|
291 // The version is just a request.
|
|
292 int val;
|
|
293 val = WSAStartup(0x2020, &wd);
|
|
294 if (val) // Request Winsock 2.2 for IPv6.
|
|
295 throw new SocketOSException("Unable to initialize socket library", val);
|
|
296
|
|
297 // These functions may not be present on older Windows versions.
|
|
298 // See the comment in InternetAddress.toHostNameString() for details.
|
|
299 auto ws2Lib = GetModuleHandleA("ws2_32.dll");
|
|
300 if (ws2Lib)
|
|
301 {
|
|
302 getnameinfoPointer = cast(typeof(getnameinfoPointer))
|
|
303 GetProcAddress(ws2Lib, "getnameinfo");
|
|
304 getaddrinfoPointer = cast(typeof(getaddrinfoPointer))
|
|
305 GetProcAddress(ws2Lib, "getaddrinfo");
|
|
306 freeaddrinfoPointer = cast(typeof(freeaddrinfoPointer))
|
|
307 GetProcAddress(ws2Lib, "freeaddrinfo");
|
|
308 }
|
|
309 }
|
|
310 else version (Posix)
|
|
311 {
|
|
312 getnameinfoPointer = &getnameinfo;
|
|
313 getaddrinfoPointer = &getaddrinfo;
|
|
314 freeaddrinfoPointer = &freeaddrinfo;
|
|
315 }
|
|
316 }
|
|
317
|
|
318
|
|
319 shared static ~this() @system nothrow @nogc
|
|
320 {
|
|
321 version (Windows)
|
|
322 {
|
|
323 WSACleanup();
|
|
324 }
|
|
325 }
|
|
326
|
|
327 /**
|
|
328 * The communication domain used to resolve an address.
|
|
329 */
|
|
330 enum AddressFamily: int
|
|
331 {
|
|
332 UNSPEC = AF_UNSPEC, /// Unspecified address family
|
|
333 UNIX = AF_UNIX, /// Local communication
|
|
334 INET = AF_INET, /// Internet Protocol version 4
|
|
335 IPX = AF_IPX, /// Novell IPX
|
|
336 APPLETALK = AF_APPLETALK, /// AppleTalk
|
|
337 INET6 = AF_INET6, /// Internet Protocol version 6
|
|
338 }
|
|
339
|
|
340
|
|
341 /**
|
|
342 * Communication semantics
|
|
343 */
|
|
344 enum SocketType: int
|
|
345 {
|
|
346 STREAM = SOCK_STREAM, /// Sequenced, reliable, two-way communication-based byte streams
|
|
347 DGRAM = SOCK_DGRAM, /// Connectionless, unreliable datagrams with a fixed maximum length; data may be lost or arrive out of order
|
|
348 RAW = SOCK_RAW, /// Raw protocol access
|
|
349 RDM = SOCK_RDM, /// Reliably-delivered message datagrams
|
|
350 SEQPACKET = SOCK_SEQPACKET, /// Sequenced, reliable, two-way connection-based datagrams with a fixed maximum length
|
|
351 }
|
|
352
|
|
353
|
|
354 /**
|
|
355 * Protocol
|
|
356 */
|
|
357 enum ProtocolType: int
|
|
358 {
|
|
359 IP = IPPROTO_IP, /// Internet Protocol version 4
|
|
360 ICMP = IPPROTO_ICMP, /// Internet Control Message Protocol
|
|
361 IGMP = IPPROTO_IGMP, /// Internet Group Management Protocol
|
|
362 GGP = IPPROTO_GGP, /// Gateway to Gateway Protocol
|
|
363 TCP = IPPROTO_TCP, /// Transmission Control Protocol
|
|
364 PUP = IPPROTO_PUP, /// PARC Universal Packet Protocol
|
|
365 UDP = IPPROTO_UDP, /// User Datagram Protocol
|
|
366 IDP = IPPROTO_IDP, /// Xerox NS protocol
|
|
367 RAW = IPPROTO_RAW, /// Raw IP packets
|
|
368 IPV6 = IPPROTO_IPV6, /// Internet Protocol version 6
|
|
369 }
|
|
370
|
|
371
|
|
372 /**
|
|
373 * $(D Protocol) is a class for retrieving protocol information.
|
|
374 *
|
|
375 * Example:
|
|
376 * ---
|
|
377 * auto proto = new Protocol;
|
|
378 * writeln("About protocol TCP:");
|
|
379 * if (proto.getProtocolByType(ProtocolType.TCP))
|
|
380 * {
|
|
381 * writefln(" Name: %s", proto.name);
|
|
382 * foreach (string s; proto.aliases)
|
|
383 * writefln(" Alias: %s", s);
|
|
384 * }
|
|
385 * else
|
|
386 * writeln(" No information found");
|
|
387 * ---
|
|
388 */
|
|
389 class Protocol
|
|
390 {
|
|
391 /// These members are populated when one of the following functions are called successfully:
|
|
392 ProtocolType type;
|
|
393 string name; /// ditto
|
|
394 string[] aliases; /// ditto
|
|
395
|
|
396
|
|
397 void populate(protoent* proto) @system pure nothrow
|
|
398 {
|
|
399 type = cast(ProtocolType) proto.p_proto;
|
|
400 name = to!string(proto.p_name);
|
|
401
|
|
402 int i;
|
|
403 for (i = 0;; i++)
|
|
404 {
|
|
405 if (!proto.p_aliases[i])
|
|
406 break;
|
|
407 }
|
|
408
|
|
409 if (i)
|
|
410 {
|
|
411 aliases = new string[i];
|
|
412 for (i = 0; i != aliases.length; i++)
|
|
413 {
|
|
414 aliases[i] =
|
|
415 to!string(proto.p_aliases[i]);
|
|
416 }
|
|
417 }
|
|
418 else
|
|
419 {
|
|
420 aliases = null;
|
|
421 }
|
|
422 }
|
|
423
|
|
424 /** Returns: false on failure */
|
|
425 bool getProtocolByName(in char[] name) @trusted nothrow
|
|
426 {
|
|
427 protoent* proto;
|
|
428 proto = getprotobyname(name.tempCString());
|
|
429 if (!proto)
|
|
430 return false;
|
|
431 populate(proto);
|
|
432 return true;
|
|
433 }
|
|
434
|
|
435
|
|
436 /** Returns: false on failure */
|
|
437 // Same as getprotobynumber().
|
|
438 bool getProtocolByType(ProtocolType type) @trusted nothrow
|
|
439 {
|
|
440 protoent* proto;
|
|
441 proto = getprotobynumber(type);
|
|
442 if (!proto)
|
|
443 return false;
|
|
444 populate(proto);
|
|
445 return true;
|
|
446 }
|
|
447 }
|
|
448
|
|
449
|
|
450 // Skip this test on Android because getprotobyname/number are
|
|
451 // unimplemented in bionic.
|
|
452 version (CRuntime_Bionic) {} else
|
|
453 @safe unittest
|
|
454 {
|
|
455 softUnittest({
|
|
456 Protocol proto = new Protocol;
|
|
457 assert(proto.getProtocolByType(ProtocolType.TCP));
|
|
458 //writeln("About protocol TCP:");
|
|
459 //writefln("\tName: %s", proto.name);
|
|
460 // foreach (string s; proto.aliases)
|
|
461 // {
|
|
462 // writefln("\tAlias: %s", s);
|
|
463 // }
|
|
464 assert(proto.name == "tcp");
|
|
465 assert(proto.aliases.length == 1 && proto.aliases[0] == "TCP");
|
|
466 });
|
|
467 }
|
|
468
|
|
469
|
|
470 /**
|
|
471 * $(D Service) is a class for retrieving service information.
|
|
472 *
|
|
473 * Example:
|
|
474 * ---
|
|
475 * auto serv = new Service;
|
|
476 * writeln("About service epmap:");
|
|
477 * if (serv.getServiceByName("epmap", "tcp"))
|
|
478 * {
|
|
479 * writefln(" Service: %s", serv.name);
|
|
480 * writefln(" Port: %d", serv.port);
|
|
481 * writefln(" Protocol: %s", serv.protocolName);
|
|
482 * foreach (string s; serv.aliases)
|
|
483 * writefln(" Alias: %s", s);
|
|
484 * }
|
|
485 * else
|
|
486 * writefln(" No service for epmap.");
|
|
487 * ---
|
|
488 */
|
|
489 class Service
|
|
490 {
|
|
491 /// These members are populated when one of the following functions are called successfully:
|
|
492 string name;
|
|
493 string[] aliases; /// ditto
|
|
494 ushort port; /// ditto
|
|
495 string protocolName; /// ditto
|
|
496
|
|
497
|
|
498 void populate(servent* serv) @system pure nothrow
|
|
499 {
|
|
500 name = to!string(serv.s_name);
|
|
501 port = ntohs(cast(ushort) serv.s_port);
|
|
502 protocolName = to!string(serv.s_proto);
|
|
503
|
|
504 int i;
|
|
505 for (i = 0;; i++)
|
|
506 {
|
|
507 if (!serv.s_aliases[i])
|
|
508 break;
|
|
509 }
|
|
510
|
|
511 if (i)
|
|
512 {
|
|
513 aliases = new string[i];
|
|
514 for (i = 0; i != aliases.length; i++)
|
|
515 {
|
|
516 aliases[i] =
|
|
517 to!string(serv.s_aliases[i]);
|
|
518 }
|
|
519 }
|
|
520 else
|
|
521 {
|
|
522 aliases = null;
|
|
523 }
|
|
524 }
|
|
525
|
|
526 /**
|
|
527 * If a protocol name is omitted, any protocol will be matched.
|
|
528 * Returns: false on failure.
|
|
529 */
|
|
530 bool getServiceByName(in char[] name, in char[] protocolName = null) @trusted nothrow
|
|
531 {
|
|
532 servent* serv;
|
|
533 serv = getservbyname(name.tempCString(), protocolName.tempCString());
|
|
534 if (!serv)
|
|
535 return false;
|
|
536 populate(serv);
|
|
537 return true;
|
|
538 }
|
|
539
|
|
540
|
|
541 /// ditto
|
|
542 bool getServiceByPort(ushort port, in char[] protocolName = null) @trusted nothrow
|
|
543 {
|
|
544 servent* serv;
|
|
545 serv = getservbyport(port, protocolName.tempCString());
|
|
546 if (!serv)
|
|
547 return false;
|
|
548 populate(serv);
|
|
549 return true;
|
|
550 }
|
|
551 }
|
|
552
|
|
553
|
|
554 @safe unittest
|
|
555 {
|
|
556 softUnittest({
|
|
557 Service serv = new Service;
|
|
558 if (serv.getServiceByName("epmap", "tcp"))
|
|
559 {
|
|
560 // writefln("About service epmap:");
|
|
561 // writefln("\tService: %s", serv.name);
|
|
562 // writefln("\tPort: %d", serv.port);
|
|
563 // writefln("\tProtocol: %s", serv.protocolName);
|
|
564 // foreach (string s; serv.aliases)
|
|
565 // {
|
|
566 // writefln("\tAlias: %s", s);
|
|
567 // }
|
|
568 // For reasons unknown this is loc-srv on Wine and epmap on Windows
|
|
569 assert(serv.name == "loc-srv" || serv.name == "epmap", serv.name);
|
|
570 assert(serv.port == 135);
|
|
571 assert(serv.protocolName == "tcp");
|
|
572 }
|
|
573 else
|
|
574 {
|
|
575 writefln("No service for epmap.");
|
|
576 }
|
|
577 });
|
|
578 }
|
|
579
|
|
580
|
|
581 private mixin template socketOSExceptionCtors()
|
|
582 {
|
|
583 ///
|
|
584 this(string msg, string file = __FILE__, size_t line = __LINE__,
|
|
585 Throwable next = null, int err = _lasterr())
|
|
586 {
|
|
587 super(msg, file, line, next, err);
|
|
588 }
|
|
589
|
|
590 ///
|
|
591 this(string msg, Throwable next, string file = __FILE__,
|
|
592 size_t line = __LINE__, int err = _lasterr())
|
|
593 {
|
|
594 super(msg, next, file, line, err);
|
|
595 }
|
|
596
|
|
597 ///
|
|
598 this(string msg, int err, string file = __FILE__, size_t line = __LINE__,
|
|
599 Throwable next = null)
|
|
600 {
|
|
601 super(msg, next, file, line, err);
|
|
602 }
|
|
603 }
|
|
604
|
|
605
|
|
606 /**
|
|
607 * Class for exceptions thrown from an `InternetHost`.
|
|
608 */
|
|
609 class HostException: SocketOSException
|
|
610 {
|
|
611 mixin socketOSExceptionCtors;
|
|
612 }
|
|
613
|
|
614 /**
|
|
615 * `InternetHost` is a class for resolving IPv4 addresses.
|
|
616 *
|
|
617 * Consider using `getAddress`, `parseAddress` and `Address` methods
|
|
618 * instead of using this class directly.
|
|
619 */
|
|
620 class InternetHost
|
|
621 {
|
|
622 /// These members are populated when one of the following functions are called successfully:
|
|
623 string name;
|
|
624 string[] aliases; /// ditto
|
|
625 uint[] addrList; /// ditto
|
|
626
|
|
627
|
|
628 void validHostent(in hostent* he)
|
|
629 {
|
|
630 if (he.h_addrtype != cast(int) AddressFamily.INET || he.h_length != 4)
|
|
631 throw new HostException("Address family mismatch");
|
|
632 }
|
|
633
|
|
634
|
|
635 void populate(hostent* he) @system pure nothrow
|
|
636 {
|
|
637 int i;
|
|
638 char* p;
|
|
639
|
|
640 name = to!string(he.h_name);
|
|
641
|
|
642 for (i = 0;; i++)
|
|
643 {
|
|
644 p = he.h_aliases[i];
|
|
645 if (!p)
|
|
646 break;
|
|
647 }
|
|
648
|
|
649 if (i)
|
|
650 {
|
|
651 aliases = new string[i];
|
|
652 for (i = 0; i != aliases.length; i++)
|
|
653 {
|
|
654 aliases[i] =
|
|
655 to!string(he.h_aliases[i]);
|
|
656 }
|
|
657 }
|
|
658 else
|
|
659 {
|
|
660 aliases = null;
|
|
661 }
|
|
662
|
|
663 for (i = 0;; i++)
|
|
664 {
|
|
665 p = he.h_addr_list[i];
|
|
666 if (!p)
|
|
667 break;
|
|
668 }
|
|
669
|
|
670 if (i)
|
|
671 {
|
|
672 addrList = new uint[i];
|
|
673 for (i = 0; i != addrList.length; i++)
|
|
674 {
|
|
675 addrList[i] = ntohl(*(cast(uint*) he.h_addr_list[i]));
|
|
676 }
|
|
677 }
|
|
678 else
|
|
679 {
|
|
680 addrList = null;
|
|
681 }
|
|
682 }
|
|
683
|
|
684 private bool getHostNoSync(string opMixin, T)(T param) @system
|
|
685 {
|
|
686 mixin(opMixin);
|
|
687 if (!he)
|
|
688 return false;
|
|
689 validHostent(he);
|
|
690 populate(he);
|
|
691 return true;
|
|
692 }
|
|
693
|
|
694 version (Windows)
|
|
695 alias getHost = getHostNoSync;
|
|
696 else
|
|
697 {
|
|
698 // posix systems use global state for return value, so we
|
|
699 // must synchronize across all threads
|
|
700 private bool getHost(string opMixin, T)(T param) @system
|
|
701 {
|
|
702 synchronized(this.classinfo)
|
|
703 return getHostNoSync!(opMixin, T)(param);
|
|
704 }
|
|
705 }
|
|
706
|
|
707 /**
|
|
708 * Resolve host name.
|
|
709 * Returns: false if unable to resolve.
|
|
710 */
|
|
711 bool getHostByName(in char[] name) @trusted
|
|
712 {
|
|
713 static if (is(typeof(gethostbyname_r)))
|
|
714 {
|
|
715 return getHostNoSync!q{
|
|
716 hostent he_v;
|
|
717 hostent* he;
|
|
718 ubyte[256] buffer_v = void;
|
|
719 auto buffer = buffer_v[];
|
|
720 auto param_zTmp = param.tempCString();
|
|
721 while (true)
|
|
722 {
|
|
723 he = &he_v;
|
|
724 int errno;
|
|
725 if (gethostbyname_r(param_zTmp, he, buffer.ptr, buffer.length, &he, &errno) == ERANGE)
|
|
726 buffer.length = buffer.length * 2;
|
|
727 else
|
|
728 break;
|
|
729 }
|
|
730 }(name);
|
|
731 }
|
|
732 else
|
|
733 {
|
|
734 return getHost!q{
|
|
735 auto he = gethostbyname(param.tempCString());
|
|
736 }(name);
|
|
737 }
|
|
738 }
|
|
739
|
|
740 /**
|
|
741 * Resolve IPv4 address number.
|
|
742 *
|
|
743 * Params:
|
|
744 * addr = The IPv4 address to resolve, in host byte order.
|
|
745 * Returns:
|
|
746 * false if unable to resolve.
|
|
747 */
|
|
748 bool getHostByAddr(uint addr) @trusted
|
|
749 {
|
|
750 return getHost!q{
|
|
751 auto x = htonl(param);
|
|
752 auto he = gethostbyaddr(&x, 4, cast(int) AddressFamily.INET);
|
|
753 }(addr);
|
|
754 }
|
|
755
|
|
756 /**
|
|
757 * Same as previous, but addr is an IPv4 address string in the
|
|
758 * dotted-decimal form $(I a.b.c.d).
|
|
759 * Returns: false if unable to resolve.
|
|
760 */
|
|
761 bool getHostByAddr(in char[] addr) @trusted
|
|
762 {
|
|
763 return getHost!q{
|
|
764 auto x = inet_addr(param.tempCString());
|
|
765 enforce(x != INADDR_NONE,
|
|
766 new SocketParameterException("Invalid IPv4 address"));
|
|
767 auto he = gethostbyaddr(&x, 4, cast(int) AddressFamily.INET);
|
|
768 }(addr);
|
|
769 }
|
|
770 }
|
|
771
|
|
772 ///
|
|
773 @safe unittest
|
|
774 {
|
|
775 InternetHost ih = new InternetHost;
|
|
776
|
|
777 ih.getHostByAddr(0x7F_00_00_01);
|
|
778 assert(ih.addrList[0] == 0x7F_00_00_01);
|
|
779 ih.getHostByAddr("127.0.0.1");
|
|
780 assert(ih.addrList[0] == 0x7F_00_00_01);
|
|
781
|
|
782 if (!ih.getHostByName("www.digitalmars.com"))
|
|
783 return; // don't fail if not connected to internet
|
|
784
|
|
785 assert(ih.addrList.length);
|
|
786 InternetAddress ia = new InternetAddress(ih.addrList[0], InternetAddress.PORT_ANY);
|
|
787 assert(ih.name == "www.digitalmars.com" || ih.name == "digitalmars.com",
|
|
788 ih.name);
|
|
789
|
|
790 assert(ih.getHostByAddr(ih.addrList[0]));
|
|
791 string getHostNameFromInt = ih.name.dup;
|
|
792
|
|
793 assert(ih.getHostByAddr(ia.toAddrString()));
|
|
794 string getHostNameFromStr = ih.name.dup;
|
|
795
|
|
796 assert(getHostNameFromInt == getHostNameFromStr);
|
|
797 }
|
|
798
|
|
799
|
|
800 /// Holds information about a socket _address retrieved by $(D getAddressInfo).
|
|
801 struct AddressInfo
|
|
802 {
|
|
803 AddressFamily family; /// Address _family
|
|
804 SocketType type; /// Socket _type
|
|
805 ProtocolType protocol; /// Protocol
|
|
806 Address address; /// Socket _address
|
|
807 string canonicalName; /// Canonical name, when $(D AddressInfoFlags.CANONNAME) is used.
|
|
808 }
|
|
809
|
|
810 /**
|
|
811 * A subset of flags supported on all platforms with getaddrinfo.
|
|
812 * Specifies option flags for $(D getAddressInfo).
|
|
813 */
|
|
814 enum AddressInfoFlags: int
|
|
815 {
|
|
816 /// The resulting addresses will be used in a call to $(D Socket.bind).
|
|
817 PASSIVE = AI_PASSIVE,
|
|
818
|
|
819 /// The canonical name is returned in $(D canonicalName) member in the first $(D AddressInfo).
|
|
820 CANONNAME = AI_CANONNAME,
|
|
821
|
|
822 /**
|
|
823 * The $(D node) parameter passed to $(D getAddressInfo) must be a numeric string.
|
|
824 * This will suppress any potentially lengthy network host address lookups.
|
|
825 */
|
|
826 NUMERICHOST = AI_NUMERICHOST,
|
|
827 }
|
|
828
|
|
829
|
|
830 /**
|
|
831 * On POSIX, getaddrinfo uses its own error codes, and thus has its own
|
|
832 * formatting function.
|
|
833 */
|
|
834 private string formatGaiError(int err) @trusted
|
|
835 {
|
|
836 version (Windows)
|
|
837 {
|
|
838 return sysErrorString(err);
|
|
839 }
|
|
840 else
|
|
841 {
|
|
842 synchronized
|
|
843 return to!string(gai_strerror(err));
|
|
844 }
|
|
845 }
|
|
846
|
|
847 /**
|
|
848 * Provides _protocol-independent translation from host names to socket
|
|
849 * addresses. If advanced functionality is not required, consider using
|
|
850 * $(D getAddress) for compatibility with older systems.
|
|
851 *
|
|
852 * Returns: Array with one $(D AddressInfo) per socket address.
|
|
853 *
|
|
854 * Throws: $(D SocketOSException) on failure, or $(D SocketFeatureException)
|
|
855 * if this functionality is not available on the current system.
|
|
856 *
|
|
857 * Params:
|
|
858 * node = string containing host name or numeric address
|
|
859 * options = optional additional parameters, identified by type:
|
|
860 * $(UL $(LI $(D string) - service name or port number)
|
|
861 * $(LI $(D AddressInfoFlags) - option flags)
|
|
862 * $(LI $(D AddressFamily) - address family to filter by)
|
|
863 * $(LI $(D SocketType) - socket type to filter by)
|
|
864 * $(LI $(D ProtocolType) - protocol to filter by))
|
|
865 *
|
|
866 * Example:
|
|
867 * ---
|
|
868 * // Roundtrip DNS resolution
|
|
869 * auto results = getAddressInfo("www.digitalmars.com");
|
|
870 * assert(results[0].address.toHostNameString() ==
|
|
871 * "digitalmars.com");
|
|
872 *
|
|
873 * // Canonical name
|
|
874 * results = getAddressInfo("www.digitalmars.com",
|
|
875 * AddressInfoFlags.CANONNAME);
|
|
876 * assert(results[0].canonicalName == "digitalmars.com");
|
|
877 *
|
|
878 * // IPv6 resolution
|
|
879 * results = getAddressInfo("ipv6.google.com");
|
|
880 * assert(results[0].family == AddressFamily.INET6);
|
|
881 *
|
|
882 * // Multihomed resolution
|
|
883 * results = getAddressInfo("google.com");
|
|
884 * assert(results.length > 1);
|
|
885 *
|
|
886 * // Parsing IPv4
|
|
887 * results = getAddressInfo("127.0.0.1",
|
|
888 * AddressInfoFlags.NUMERICHOST);
|
|
889 * assert(results.length && results[0].family ==
|
|
890 * AddressFamily.INET);
|
|
891 *
|
|
892 * // Parsing IPv6
|
|
893 * results = getAddressInfo("::1",
|
|
894 * AddressInfoFlags.NUMERICHOST);
|
|
895 * assert(results.length && results[0].family ==
|
|
896 * AddressFamily.INET6);
|
|
897 * ---
|
|
898 */
|
|
899 AddressInfo[] getAddressInfo(T...)(in char[] node, T options)
|
|
900 {
|
|
901 const(char)[] service = null;
|
|
902 addrinfo hints;
|
|
903 hints.ai_family = AF_UNSPEC;
|
|
904
|
|
905 foreach (option; options)
|
|
906 {
|
|
907 static if (is(typeof(option) : const(char)[]))
|
|
908 service = option;
|
|
909 else
|
|
910 static if (is(typeof(option) == AddressInfoFlags))
|
|
911 hints.ai_flags |= option;
|
|
912 else
|
|
913 static if (is(typeof(option) == AddressFamily))
|
|
914 hints.ai_family = option;
|
|
915 else
|
|
916 static if (is(typeof(option) == SocketType))
|
|
917 hints.ai_socktype = option;
|
|
918 else
|
|
919 static if (is(typeof(option) == ProtocolType))
|
|
920 hints.ai_protocol = option;
|
|
921 else
|
|
922 static assert(0, "Unknown getAddressInfo option type: " ~ typeof(option).stringof);
|
|
923 }
|
|
924
|
|
925 return () @trusted { return getAddressInfoImpl(node, service, &hints); }();
|
|
926 }
|
|
927
|
|
928 @system unittest
|
|
929 {
|
|
930 struct Oops
|
|
931 {
|
|
932 const(char[]) breakSafety()
|
|
933 {
|
|
934 *cast(int*) 0xcafebabe = 0xdeadbeef;
|
|
935 return null;
|
|
936 }
|
|
937 alias breakSafety this;
|
|
938 }
|
|
939 assert(!__traits(compiles, () {
|
|
940 getAddressInfo("", Oops.init);
|
|
941 }), "getAddressInfo breaks @safe");
|
|
942 }
|
|
943
|
|
944 private AddressInfo[] getAddressInfoImpl(in char[] node, in char[] service, addrinfo* hints) @system
|
|
945 {
|
|
946 import std.array : appender;
|
|
947
|
|
948 if (getaddrinfoPointer && freeaddrinfoPointer)
|
|
949 {
|
|
950 addrinfo* ai_res;
|
|
951
|
|
952 int ret = getaddrinfoPointer(
|
|
953 node.tempCString(),
|
|
954 service.tempCString(),
|
|
955 hints, &ai_res);
|
|
956 enforce(ret == 0, new SocketOSException("getaddrinfo error", ret, &formatGaiError));
|
|
957 scope(exit) freeaddrinfoPointer(ai_res);
|
|
958
|
|
959 auto result = appender!(AddressInfo[])();
|
|
960
|
|
961 // Use const to force UnknownAddressReference to copy the sockaddr.
|
|
962 for (const(addrinfo)* ai = ai_res; ai; ai = ai.ai_next)
|
|
963 result ~= AddressInfo(
|
|
964 cast(AddressFamily) ai.ai_family,
|
|
965 cast(SocketType ) ai.ai_socktype,
|
|
966 cast(ProtocolType ) ai.ai_protocol,
|
|
967 new UnknownAddressReference(ai.ai_addr, cast(socklen_t) ai.ai_addrlen),
|
|
968 ai.ai_canonname ? to!string(ai.ai_canonname) : null);
|
|
969
|
|
970 assert(result.data.length > 0);
|
|
971 return result.data;
|
|
972 }
|
|
973
|
|
974 throw new SocketFeatureException("Address info lookup is not available " ~
|
|
975 "on this system.");
|
|
976 }
|
|
977
|
|
978
|
|
979 @safe unittest
|
|
980 {
|
|
981 softUnittest({
|
|
982 if (getaddrinfoPointer)
|
|
983 {
|
|
984 // Roundtrip DNS resolution
|
|
985 auto results = getAddressInfo("www.digitalmars.com");
|
|
986 assert(results[0].address.toHostNameString() == "digitalmars.com");
|
|
987
|
|
988 // Canonical name
|
|
989 results = getAddressInfo("www.digitalmars.com",
|
|
990 AddressInfoFlags.CANONNAME);
|
|
991 assert(results[0].canonicalName == "digitalmars.com");
|
|
992
|
|
993 // IPv6 resolution
|
|
994 //results = getAddressInfo("ipv6.google.com");
|
|
995 //assert(results[0].family == AddressFamily.INET6);
|
|
996
|
|
997 // Multihomed resolution
|
|
998 //results = getAddressInfo("google.com");
|
|
999 //assert(results.length > 1);
|
|
1000
|
|
1001 // Parsing IPv4
|
|
1002 results = getAddressInfo("127.0.0.1", AddressInfoFlags.NUMERICHOST);
|
|
1003 assert(results.length && results[0].family == AddressFamily.INET);
|
|
1004
|
|
1005 // Parsing IPv6
|
|
1006 results = getAddressInfo("::1", AddressInfoFlags.NUMERICHOST);
|
|
1007 assert(results.length && results[0].family == AddressFamily.INET6);
|
|
1008 }
|
|
1009 });
|
|
1010
|
|
1011 if (getaddrinfoPointer)
|
|
1012 {
|
|
1013 auto results = getAddressInfo(null, "1234", AddressInfoFlags.PASSIVE,
|
|
1014 SocketType.STREAM, ProtocolType.TCP, AddressFamily.INET);
|
|
1015 assert(results.length == 1 && results[0].address.toString() == "0.0.0.0:1234");
|
|
1016 }
|
|
1017 }
|
|
1018
|
|
1019
|
|
1020 private ushort serviceToPort(in char[] service)
|
|
1021 {
|
|
1022 if (service == "")
|
|
1023 return InternetAddress.PORT_ANY;
|
|
1024 else
|
|
1025 if (isNumeric(service))
|
|
1026 return to!ushort(service);
|
|
1027 else
|
|
1028 {
|
|
1029 auto s = new Service();
|
|
1030 s.getServiceByName(service);
|
|
1031 return s.port;
|
|
1032 }
|
|
1033 }
|
|
1034
|
|
1035 /**
|
|
1036 * Provides _protocol-independent translation from host names to socket
|
|
1037 * addresses. Uses $(D getAddressInfo) if the current system supports it,
|
|
1038 * and $(D InternetHost) otherwise.
|
|
1039 *
|
|
1040 * Returns: Array with one $(D Address) instance per socket address.
|
|
1041 *
|
|
1042 * Throws: $(D SocketOSException) on failure.
|
|
1043 *
|
|
1044 * Example:
|
|
1045 * ---
|
|
1046 * writeln("Resolving www.digitalmars.com:");
|
|
1047 * try
|
|
1048 * {
|
|
1049 * auto addresses = getAddress("www.digitalmars.com");
|
|
1050 * foreach (address; addresses)
|
|
1051 * writefln(" IP: %s", address.toAddrString());
|
|
1052 * }
|
|
1053 * catch (SocketException e)
|
|
1054 * writefln(" Lookup failed: %s", e.msg);
|
|
1055 * ---
|
|
1056 */
|
|
1057 Address[] getAddress(in char[] hostname, in char[] service = null)
|
|
1058 {
|
|
1059 if (getaddrinfoPointer && freeaddrinfoPointer)
|
|
1060 {
|
|
1061 // use getAddressInfo
|
|
1062 auto infos = getAddressInfo(hostname, service);
|
|
1063 Address[] results;
|
|
1064 results.length = infos.length;
|
|
1065 foreach (i, ref result; results)
|
|
1066 result = infos[i].address;
|
|
1067 return results;
|
|
1068 }
|
|
1069 else
|
|
1070 return getAddress(hostname, serviceToPort(service));
|
|
1071 }
|
|
1072
|
|
1073 /// ditto
|
|
1074 Address[] getAddress(in char[] hostname, ushort port)
|
|
1075 {
|
|
1076 if (getaddrinfoPointer && freeaddrinfoPointer)
|
|
1077 return getAddress(hostname, to!string(port));
|
|
1078 else
|
|
1079 {
|
|
1080 // use getHostByName
|
|
1081 auto ih = new InternetHost;
|
|
1082 if (!ih.getHostByName(hostname))
|
|
1083 throw new AddressException(
|
|
1084 text("Unable to resolve host '", hostname, "'"));
|
|
1085
|
|
1086 Address[] results;
|
|
1087 foreach (uint addr; ih.addrList)
|
|
1088 results ~= new InternetAddress(addr, port);
|
|
1089 return results;
|
|
1090 }
|
|
1091 }
|
|
1092
|
|
1093
|
|
1094 @safe unittest
|
|
1095 {
|
|
1096 softUnittest({
|
|
1097 auto addresses = getAddress("63.105.9.61");
|
|
1098 assert(addresses.length && addresses[0].toAddrString() == "63.105.9.61");
|
|
1099
|
|
1100 if (getaddrinfoPointer)
|
|
1101 {
|
|
1102 // test via gethostbyname
|
|
1103 auto getaddrinfoPointerBackup = getaddrinfoPointer;
|
|
1104 cast() getaddrinfoPointer = null;
|
|
1105 scope(exit) cast() getaddrinfoPointer = getaddrinfoPointerBackup;
|
|
1106
|
|
1107 addresses = getAddress("63.105.9.61");
|
|
1108 assert(addresses.length && addresses[0].toAddrString() == "63.105.9.61");
|
|
1109 }
|
|
1110 });
|
|
1111 }
|
|
1112
|
|
1113
|
|
1114 /**
|
|
1115 * Provides _protocol-independent parsing of network addresses. Does not
|
|
1116 * attempt name resolution. Uses $(D getAddressInfo) with
|
|
1117 * $(D AddressInfoFlags.NUMERICHOST) if the current system supports it, and
|
|
1118 * $(D InternetAddress) otherwise.
|
|
1119 *
|
|
1120 * Returns: An $(D Address) instance representing specified address.
|
|
1121 *
|
|
1122 * Throws: $(D SocketException) on failure.
|
|
1123 *
|
|
1124 * Example:
|
|
1125 * ---
|
|
1126 * writeln("Enter IP address:");
|
|
1127 * string ip = readln().chomp();
|
|
1128 * try
|
|
1129 * {
|
|
1130 * Address address = parseAddress(ip);
|
|
1131 * writefln("Looking up reverse of %s:",
|
|
1132 * address.toAddrString());
|
|
1133 * try
|
|
1134 * {
|
|
1135 * string reverse = address.toHostNameString();
|
|
1136 * if (reverse)
|
|
1137 * writefln(" Reverse name: %s", reverse);
|
|
1138 * else
|
|
1139 * writeln(" Reverse hostname not found.");
|
|
1140 * }
|
|
1141 * catch (SocketException e)
|
|
1142 * writefln(" Lookup error: %s", e.msg);
|
|
1143 * }
|
|
1144 * catch (SocketException e)
|
|
1145 * {
|
|
1146 * writefln(" %s is not a valid IP address: %s",
|
|
1147 * ip, e.msg);
|
|
1148 * }
|
|
1149 * ---
|
|
1150 */
|
|
1151 Address parseAddress(in char[] hostaddr, in char[] service = null)
|
|
1152 {
|
|
1153 if (getaddrinfoPointer && freeaddrinfoPointer)
|
|
1154 return getAddressInfo(hostaddr, service, AddressInfoFlags.NUMERICHOST)[0].address;
|
|
1155 else
|
|
1156 return parseAddress(hostaddr, serviceToPort(service));
|
|
1157 }
|
|
1158
|
|
1159 /// ditto
|
|
1160 Address parseAddress(in char[] hostaddr, ushort port)
|
|
1161 {
|
|
1162 if (getaddrinfoPointer && freeaddrinfoPointer)
|
|
1163 return parseAddress(hostaddr, to!string(port));
|
|
1164 else
|
|
1165 {
|
|
1166 auto in4_addr = InternetAddress.parse(hostaddr);
|
|
1167 enforce(in4_addr != InternetAddress.ADDR_NONE,
|
|
1168 new SocketParameterException("Invalid IP address"));
|
|
1169 return new InternetAddress(in4_addr, port);
|
|
1170 }
|
|
1171 }
|
|
1172
|
|
1173
|
|
1174 @safe unittest
|
|
1175 {
|
|
1176 softUnittest({
|
|
1177 auto address = parseAddress("63.105.9.61");
|
|
1178 assert(address.toAddrString() == "63.105.9.61");
|
|
1179
|
|
1180 if (getaddrinfoPointer)
|
|
1181 {
|
|
1182 // test via inet_addr
|
|
1183 auto getaddrinfoPointerBackup = getaddrinfoPointer;
|
|
1184 cast() getaddrinfoPointer = null;
|
|
1185 scope(exit) cast() getaddrinfoPointer = getaddrinfoPointerBackup;
|
|
1186
|
|
1187 address = parseAddress("63.105.9.61");
|
|
1188 assert(address.toAddrString() == "63.105.9.61");
|
|
1189 }
|
|
1190
|
|
1191 assert(collectException!SocketException(parseAddress("Invalid IP address")));
|
|
1192 });
|
|
1193 }
|
|
1194
|
|
1195
|
|
1196 /**
|
|
1197 * Class for exceptions thrown from an $(D Address).
|
|
1198 */
|
|
1199 class AddressException: SocketOSException
|
|
1200 {
|
|
1201 mixin socketOSExceptionCtors;
|
|
1202 }
|
|
1203
|
|
1204
|
|
1205 /**
|
|
1206 * $(D Address) is an abstract class for representing a socket addresses.
|
|
1207 *
|
|
1208 * Example:
|
|
1209 * ---
|
|
1210 * writeln("About www.google.com port 80:");
|
|
1211 * try
|
|
1212 * {
|
|
1213 * Address[] addresses = getAddress("www.google.com", 80);
|
|
1214 * writefln(" %d addresses found.", addresses.length);
|
|
1215 * foreach (int i, Address a; addresses)
|
|
1216 * {
|
|
1217 * writefln(" Address %d:", i+1);
|
|
1218 * writefln(" IP address: %s", a.toAddrString());
|
|
1219 * writefln(" Hostname: %s", a.toHostNameString());
|
|
1220 * writefln(" Port: %s", a.toPortString());
|
|
1221 * writefln(" Service name: %s",
|
|
1222 * a.toServiceNameString());
|
|
1223 * }
|
|
1224 * }
|
|
1225 * catch (SocketException e)
|
|
1226 * writefln(" Lookup error: %s", e.msg);
|
|
1227 * ---
|
|
1228 */
|
|
1229 abstract class Address
|
|
1230 {
|
|
1231 /// Returns pointer to underlying $(D sockaddr) structure.
|
|
1232 abstract @property sockaddr* name() pure nothrow @nogc;
|
|
1233 abstract @property const(sockaddr)* name() const pure nothrow @nogc; /// ditto
|
|
1234
|
|
1235 /// Returns actual size of underlying $(D sockaddr) structure.
|
|
1236 abstract @property socklen_t nameLen() const pure nothrow @nogc;
|
|
1237
|
|
1238 // Socket.remoteAddress, Socket.localAddress, and Socket.receiveFrom
|
|
1239 // use setNameLen to set the actual size of the address as returned by
|
|
1240 // getsockname, getpeername, and recvfrom, respectively.
|
|
1241 // The following implementation is sufficient for fixed-length addresses,
|
|
1242 // and ensures that the length is not changed.
|
|
1243 // Must be overridden for variable-length addresses.
|
|
1244 protected void setNameLen(socklen_t len)
|
|
1245 {
|
|
1246 if (len != this.nameLen)
|
|
1247 throw new AddressException(
|
|
1248 format("%s expects address of length %d, not %d", typeid(this),
|
|
1249 this.nameLen, len), 0);
|
|
1250 }
|
|
1251
|
|
1252 /// Family of this address.
|
|
1253 @property AddressFamily addressFamily() const pure nothrow @nogc
|
|
1254 {
|
|
1255 return cast(AddressFamily) name.sa_family;
|
|
1256 }
|
|
1257
|
|
1258 // Common code for toAddrString and toHostNameString
|
|
1259 private string toHostString(bool numeric) @trusted const
|
|
1260 {
|
|
1261 // getnameinfo() is the recommended way to perform a reverse (name)
|
|
1262 // lookup on both Posix and Windows. However, it is only available
|
|
1263 // on Windows XP and above, and not included with the WinSock import
|
|
1264 // libraries shipped with DMD. Thus, we check for getnameinfo at
|
|
1265 // runtime in the shared module constructor, and use it if it's
|
|
1266 // available in the base class method. Classes for specific network
|
|
1267 // families (e.g. InternetHost) override this method and use a
|
|
1268 // deprecated, albeit commonly-available method when getnameinfo()
|
|
1269 // is not available.
|
|
1270 // http://technet.microsoft.com/en-us/library/aa450403.aspx
|
|
1271 if (getnameinfoPointer)
|
|
1272 {
|
|
1273 auto buf = new char[NI_MAXHOST];
|
|
1274 auto ret = getnameinfoPointer(
|
|
1275 name, nameLen,
|
|
1276 buf.ptr, cast(uint) buf.length,
|
|
1277 null, 0,
|
|
1278 numeric ? NI_NUMERICHOST : NI_NAMEREQD);
|
|
1279
|
|
1280 if (!numeric)
|
|
1281 {
|
|
1282 if (ret == EAI_NONAME)
|
|
1283 return null;
|
|
1284 version (Windows)
|
|
1285 if (ret == WSANO_DATA)
|
|
1286 return null;
|
|
1287 }
|
|
1288
|
|
1289 enforce(ret == 0, new AddressException("Could not get " ~
|
|
1290 (numeric ? "host address" : "host name")));
|
|
1291 return assumeUnique(buf[0 .. strlen(buf.ptr)]);
|
|
1292 }
|
|
1293
|
|
1294 throw new SocketFeatureException((numeric ? "Host address" : "Host name") ~
|
|
1295 " lookup for this address family is not available on this system.");
|
|
1296 }
|
|
1297
|
|
1298 // Common code for toPortString and toServiceNameString
|
|
1299 private string toServiceString(bool numeric) @trusted const
|
|
1300 {
|
|
1301 // See toHostNameString() for details about getnameinfo().
|
|
1302 if (getnameinfoPointer)
|
|
1303 {
|
|
1304 auto buf = new char[NI_MAXSERV];
|
|
1305 enforce(getnameinfoPointer(
|
|
1306 name, nameLen,
|
|
1307 null, 0,
|
|
1308 buf.ptr, cast(uint) buf.length,
|
|
1309 numeric ? NI_NUMERICSERV : NI_NAMEREQD
|
|
1310 ) == 0, new AddressException("Could not get " ~
|
|
1311 (numeric ? "port number" : "service name")));
|
|
1312 return assumeUnique(buf[0 .. strlen(buf.ptr)]);
|
|
1313 }
|
|
1314
|
|
1315 throw new SocketFeatureException((numeric ? "Port number" : "Service name") ~
|
|
1316 " lookup for this address family is not available on this system.");
|
|
1317 }
|
|
1318
|
|
1319 /**
|
|
1320 * Attempts to retrieve the host address as a human-readable string.
|
|
1321 *
|
|
1322 * Throws: $(D AddressException) on failure, or $(D SocketFeatureException)
|
|
1323 * if address retrieval for this address family is not available on the
|
|
1324 * current system.
|
|
1325 */
|
|
1326 string toAddrString() const
|
|
1327 {
|
|
1328 return toHostString(true);
|
|
1329 }
|
|
1330
|
|
1331 /**
|
|
1332 * Attempts to retrieve the host name as a fully qualified domain name.
|
|
1333 *
|
|
1334 * Returns: The FQDN corresponding to this $(D Address), or $(D null) if
|
|
1335 * the host name did not resolve.
|
|
1336 *
|
|
1337 * Throws: $(D AddressException) on error, or $(D SocketFeatureException)
|
|
1338 * if host name lookup for this address family is not available on the
|
|
1339 * current system.
|
|
1340 */
|
|
1341 string toHostNameString() const
|
|
1342 {
|
|
1343 return toHostString(false);
|
|
1344 }
|
|
1345
|
|
1346 /**
|
|
1347 * Attempts to retrieve the numeric port number as a string.
|
|
1348 *
|
|
1349 * Throws: $(D AddressException) on failure, or $(D SocketFeatureException)
|
|
1350 * if port number retrieval for this address family is not available on the
|
|
1351 * current system.
|
|
1352 */
|
|
1353 string toPortString() const
|
|
1354 {
|
|
1355 return toServiceString(true);
|
|
1356 }
|
|
1357
|
|
1358 /**
|
|
1359 * Attempts to retrieve the service name as a string.
|
|
1360 *
|
|
1361 * Throws: $(D AddressException) on failure, or $(D SocketFeatureException)
|
|
1362 * if service name lookup for this address family is not available on the
|
|
1363 * current system.
|
|
1364 */
|
|
1365 string toServiceNameString() const
|
|
1366 {
|
|
1367 return toServiceString(false);
|
|
1368 }
|
|
1369
|
|
1370 /// Human readable string representing this address.
|
|
1371 override string toString() const
|
|
1372 {
|
|
1373 try
|
|
1374 {
|
|
1375 string host = toAddrString();
|
|
1376 string port = toPortString();
|
|
1377 if (host.indexOf(':') >= 0)
|
|
1378 return "[" ~ host ~ "]:" ~ port;
|
|
1379 else
|
|
1380 return host ~ ":" ~ port;
|
|
1381 }
|
|
1382 catch (SocketException)
|
|
1383 return "Unknown";
|
|
1384 }
|
|
1385 }
|
|
1386
|
|
1387 /**
|
|
1388 * $(D UnknownAddress) encapsulates an unknown socket address.
|
|
1389 */
|
|
1390 class UnknownAddress: Address
|
|
1391 {
|
|
1392 protected:
|
|
1393 sockaddr sa;
|
|
1394
|
|
1395
|
|
1396 public:
|
|
1397 override @property sockaddr* name()
|
|
1398 {
|
|
1399 return &sa;
|
|
1400 }
|
|
1401
|
|
1402 override @property const(sockaddr)* name() const
|
|
1403 {
|
|
1404 return &sa;
|
|
1405 }
|
|
1406
|
|
1407
|
|
1408 override @property socklen_t nameLen() const
|
|
1409 {
|
|
1410 return cast(socklen_t) sa.sizeof;
|
|
1411 }
|
|
1412
|
|
1413 }
|
|
1414
|
|
1415
|
|
1416 /**
|
|
1417 * $(D UnknownAddressReference) encapsulates a reference to an arbitrary
|
|
1418 * socket address.
|
|
1419 */
|
|
1420 class UnknownAddressReference: Address
|
|
1421 {
|
|
1422 protected:
|
|
1423 sockaddr* sa;
|
|
1424 socklen_t len;
|
|
1425
|
|
1426 public:
|
|
1427 /// Constructs an $(D Address) with a reference to the specified $(D sockaddr).
|
|
1428 this(sockaddr* sa, socklen_t len) pure nothrow @nogc
|
|
1429 {
|
|
1430 this.sa = sa;
|
|
1431 this.len = len;
|
|
1432 }
|
|
1433
|
|
1434 /// Constructs an $(D Address) with a copy of the specified $(D sockaddr).
|
|
1435 this(const(sockaddr)* sa, socklen_t len) @system pure nothrow
|
|
1436 {
|
|
1437 this.sa = cast(sockaddr*) (cast(ubyte*) sa)[0 .. len].dup.ptr;
|
|
1438 this.len = len;
|
|
1439 }
|
|
1440
|
|
1441 override @property sockaddr* name()
|
|
1442 {
|
|
1443 return sa;
|
|
1444 }
|
|
1445
|
|
1446 override @property const(sockaddr)* name() const
|
|
1447 {
|
|
1448 return sa;
|
|
1449 }
|
|
1450
|
|
1451
|
|
1452 override @property socklen_t nameLen() const
|
|
1453 {
|
|
1454 return cast(socklen_t) len;
|
|
1455 }
|
|
1456 }
|
|
1457
|
|
1458
|
|
1459 /**
|
|
1460 * $(D InternetAddress) encapsulates an IPv4 (Internet Protocol version 4)
|
|
1461 * socket address.
|
|
1462 *
|
|
1463 * Consider using $(D getAddress), $(D parseAddress) and $(D Address) methods
|
|
1464 * instead of using this class directly.
|
|
1465 */
|
|
1466 class InternetAddress: Address
|
|
1467 {
|
|
1468 protected:
|
|
1469 sockaddr_in sin;
|
|
1470
|
|
1471
|
|
1472 this() pure nothrow @nogc
|
|
1473 {
|
|
1474 }
|
|
1475
|
|
1476
|
|
1477 public:
|
|
1478 override @property sockaddr* name()
|
|
1479 {
|
|
1480 return cast(sockaddr*)&sin;
|
|
1481 }
|
|
1482
|
|
1483 override @property const(sockaddr)* name() const
|
|
1484 {
|
|
1485 return cast(const(sockaddr)*)&sin;
|
|
1486 }
|
|
1487
|
|
1488
|
|
1489 override @property socklen_t nameLen() const
|
|
1490 {
|
|
1491 return cast(socklen_t) sin.sizeof;
|
|
1492 }
|
|
1493
|
|
1494
|
|
1495 enum uint ADDR_ANY = INADDR_ANY; /// Any IPv4 host address.
|
|
1496 enum uint ADDR_NONE = INADDR_NONE; /// An invalid IPv4 host address.
|
|
1497 enum ushort PORT_ANY = 0; /// Any IPv4 port number.
|
|
1498
|
|
1499 /// Returns the IPv4 _port number (in host byte order).
|
|
1500 @property ushort port() const pure nothrow @nogc
|
|
1501 {
|
|
1502 return ntohs(sin.sin_port);
|
|
1503 }
|
|
1504
|
|
1505 /// Returns the IPv4 address number (in host byte order).
|
|
1506 @property uint addr() const pure nothrow @nogc
|
|
1507 {
|
|
1508 return ntohl(sin.sin_addr.s_addr);
|
|
1509 }
|
|
1510
|
|
1511 /**
|
|
1512 * Construct a new $(D InternetAddress).
|
|
1513 * Params:
|
|
1514 * addr = an IPv4 address string in the dotted-decimal form a.b.c.d,
|
|
1515 * or a host name which will be resolved using an $(D InternetHost)
|
|
1516 * object.
|
|
1517 * port = port number, may be $(D PORT_ANY).
|
|
1518 */
|
|
1519 this(in char[] addr, ushort port)
|
|
1520 {
|
|
1521 uint uiaddr = parse(addr);
|
|
1522 if (ADDR_NONE == uiaddr)
|
|
1523 {
|
|
1524 InternetHost ih = new InternetHost;
|
|
1525 if (!ih.getHostByName(addr))
|
|
1526 //throw new AddressException("Invalid internet address");
|
|
1527 throw new AddressException(
|
|
1528 text("Unable to resolve host '", addr, "'"));
|
|
1529 uiaddr = ih.addrList[0];
|
|
1530 }
|
|
1531 sin.sin_family = AddressFamily.INET;
|
|
1532 sin.sin_addr.s_addr = htonl(uiaddr);
|
|
1533 sin.sin_port = htons(port);
|
|
1534 }
|
|
1535
|
|
1536 /**
|
|
1537 * Construct a new $(D InternetAddress).
|
|
1538 * Params:
|
|
1539 * addr = (optional) an IPv4 address in host byte order, may be $(D ADDR_ANY).
|
|
1540 * port = port number, may be $(D PORT_ANY).
|
|
1541 */
|
|
1542 this(uint addr, ushort port) pure nothrow @nogc
|
|
1543 {
|
|
1544 sin.sin_family = AddressFamily.INET;
|
|
1545 sin.sin_addr.s_addr = htonl(addr);
|
|
1546 sin.sin_port = htons(port);
|
|
1547 }
|
|
1548
|
|
1549 /// ditto
|
|
1550 this(ushort port) pure nothrow @nogc
|
|
1551 {
|
|
1552 sin.sin_family = AddressFamily.INET;
|
|
1553 sin.sin_addr.s_addr = ADDR_ANY;
|
|
1554 sin.sin_port = htons(port);
|
|
1555 }
|
|
1556
|
|
1557 /**
|
|
1558 * Construct a new $(D InternetAddress).
|
|
1559 * Params:
|
|
1560 * addr = A sockaddr_in as obtained from lower-level API calls such as getifaddrs.
|
|
1561 */
|
|
1562 this(sockaddr_in addr) pure nothrow @nogc
|
|
1563 {
|
|
1564 assert(addr.sin_family == AddressFamily.INET);
|
|
1565 sin = addr;
|
|
1566 }
|
|
1567
|
|
1568 /// Human readable string representing the IPv4 address in dotted-decimal form.
|
|
1569 override string toAddrString() @trusted const
|
|
1570 {
|
|
1571 return to!string(inet_ntoa(sin.sin_addr));
|
|
1572 }
|
|
1573
|
|
1574 /// Human readable string representing the IPv4 port.
|
|
1575 override string toPortString() const
|
|
1576 {
|
|
1577 return std.conv.to!string(port);
|
|
1578 }
|
|
1579
|
|
1580 /**
|
|
1581 * Attempts to retrieve the host name as a fully qualified domain name.
|
|
1582 *
|
|
1583 * Returns: The FQDN corresponding to this $(D InternetAddress), or
|
|
1584 * $(D null) if the host name did not resolve.
|
|
1585 *
|
|
1586 * Throws: $(D AddressException) on error.
|
|
1587 */
|
|
1588 override string toHostNameString() const
|
|
1589 {
|
|
1590 // getnameinfo() is the recommended way to perform a reverse (name)
|
|
1591 // lookup on both Posix and Windows. However, it is only available
|
|
1592 // on Windows XP and above, and not included with the WinSock import
|
|
1593 // libraries shipped with DMD. Thus, we check for getnameinfo at
|
|
1594 // runtime in the shared module constructor, and fall back to the
|
|
1595 // deprecated getHostByAddr() if it could not be found. See also:
|
|
1596 // http://technet.microsoft.com/en-us/library/aa450403.aspx
|
|
1597
|
|
1598 if (getnameinfoPointer)
|
|
1599 return super.toHostNameString();
|
|
1600 else
|
|
1601 {
|
|
1602 auto host = new InternetHost();
|
|
1603 if (!host.getHostByAddr(ntohl(sin.sin_addr.s_addr)))
|
|
1604 return null;
|
|
1605 return host.name;
|
|
1606 }
|
|
1607 }
|
|
1608
|
|
1609 /**
|
|
1610 * Compares with another InternetAddress of same type for equality
|
|
1611 * Returns: true if the InternetAddresses share the same address and
|
|
1612 * port number.
|
|
1613 */
|
|
1614 override bool opEquals(Object o) const
|
|
1615 {
|
|
1616 auto other = cast(InternetAddress) o;
|
|
1617 return other && this.sin.sin_addr.s_addr == other.sin.sin_addr.s_addr &&
|
|
1618 this.sin.sin_port == other.sin.sin_port;
|
|
1619 }
|
|
1620
|
|
1621 ///
|
|
1622 @system unittest
|
|
1623 {
|
|
1624 auto addr1 = new InternetAddress("127.0.0.1", 80);
|
|
1625 auto addr2 = new InternetAddress("127.0.0.2", 80);
|
|
1626
|
|
1627 assert(addr1 == addr1);
|
|
1628 assert(addr1 != addr2);
|
|
1629 }
|
|
1630
|
|
1631 /**
|
|
1632 * Parse an IPv4 address string in the dotted-decimal form $(I a.b.c.d)
|
|
1633 * and return the number.
|
|
1634 * Returns: If the string is not a legitimate IPv4 address,
|
|
1635 * $(D ADDR_NONE) is returned.
|
|
1636 */
|
|
1637 static uint parse(in char[] addr) @trusted nothrow
|
|
1638 {
|
|
1639 return ntohl(inet_addr(addr.tempCString()));
|
|
1640 }
|
|
1641
|
|
1642 /**
|
|
1643 * Convert an IPv4 address number in host byte order to a human readable
|
|
1644 * string representing the IPv4 address in dotted-decimal form.
|
|
1645 */
|
|
1646 static string addrToString(uint addr) @trusted nothrow
|
|
1647 {
|
|
1648 in_addr sin_addr;
|
|
1649 sin_addr.s_addr = htonl(addr);
|
|
1650 return to!string(inet_ntoa(sin_addr));
|
|
1651 }
|
|
1652 }
|
|
1653
|
|
1654
|
|
1655 @safe unittest
|
|
1656 {
|
|
1657 softUnittest({
|
|
1658 const InternetAddress ia = new InternetAddress("63.105.9.61", 80);
|
|
1659 assert(ia.toString() == "63.105.9.61:80");
|
|
1660 });
|
|
1661
|
|
1662 softUnittest({
|
|
1663 // test construction from a sockaddr_in
|
|
1664 sockaddr_in sin;
|
|
1665
|
|
1666 sin.sin_addr.s_addr = htonl(0x7F_00_00_01); // 127.0.0.1
|
|
1667 sin.sin_family = AddressFamily.INET;
|
|
1668 sin.sin_port = htons(80);
|
|
1669
|
|
1670 const InternetAddress ia = new InternetAddress(sin);
|
|
1671 assert(ia.toString() == "127.0.0.1:80");
|
|
1672 });
|
|
1673
|
|
1674 softUnittest({
|
|
1675 // test reverse lookup
|
|
1676 auto ih = new InternetHost;
|
|
1677 if (ih.getHostByName("digitalmars.com"))
|
|
1678 {
|
|
1679 const ia = new InternetAddress(ih.addrList[0], 80);
|
|
1680 assert(ia.toHostNameString() == "digitalmars.com");
|
|
1681
|
|
1682 if (getnameinfoPointer)
|
|
1683 {
|
|
1684 // test reverse lookup, via gethostbyaddr
|
|
1685 auto getnameinfoPointerBackup = getnameinfoPointer;
|
|
1686 cast() getnameinfoPointer = null;
|
|
1687 scope(exit) cast() getnameinfoPointer = getnameinfoPointerBackup;
|
|
1688
|
|
1689 assert(ia.toHostNameString() == "digitalmars.com");
|
|
1690 }
|
|
1691 }
|
|
1692 });
|
|
1693
|
|
1694 version (SlowTests)
|
|
1695 softUnittest({
|
|
1696 // test failing reverse lookup
|
|
1697 const InternetAddress ia = new InternetAddress("127.114.111.120", 80);
|
|
1698 assert(ia.toHostNameString() is null);
|
|
1699
|
|
1700 if (getnameinfoPointer)
|
|
1701 {
|
|
1702 // test failing reverse lookup, via gethostbyaddr
|
|
1703 auto getnameinfoPointerBackup = getnameinfoPointer;
|
|
1704 getnameinfoPointer = null;
|
|
1705 scope(exit) getnameinfoPointer = getnameinfoPointerBackup;
|
|
1706
|
|
1707 assert(ia.toHostNameString() is null);
|
|
1708 }
|
|
1709 });
|
|
1710 }
|
|
1711
|
|
1712
|
|
1713 /**
|
|
1714 * $(D Internet6Address) encapsulates an IPv6 (Internet Protocol version 6)
|
|
1715 * socket address.
|
|
1716 *
|
|
1717 * Consider using $(D getAddress), $(D parseAddress) and $(D Address) methods
|
|
1718 * instead of using this class directly.
|
|
1719 */
|
|
1720 class Internet6Address: Address
|
|
1721 {
|
|
1722 protected:
|
|
1723 sockaddr_in6 sin6;
|
|
1724
|
|
1725
|
|
1726 this() pure nothrow @nogc
|
|
1727 {
|
|
1728 }
|
|
1729
|
|
1730
|
|
1731 public:
|
|
1732 override @property sockaddr* name()
|
|
1733 {
|
|
1734 return cast(sockaddr*)&sin6;
|
|
1735 }
|
|
1736
|
|
1737 override @property const(sockaddr)* name() const
|
|
1738 {
|
|
1739 return cast(const(sockaddr)*)&sin6;
|
|
1740 }
|
|
1741
|
|
1742
|
|
1743 override @property socklen_t nameLen() const
|
|
1744 {
|
|
1745 return cast(socklen_t) sin6.sizeof;
|
|
1746 }
|
|
1747
|
|
1748
|
|
1749 /// Any IPv6 host address.
|
|
1750 static @property ref const(ubyte)[16] ADDR_ANY() pure nothrow @nogc
|
|
1751 {
|
|
1752 const(ubyte)[16]* addr;
|
|
1753 static if (is(typeof(IN6ADDR_ANY)))
|
|
1754 {
|
|
1755 addr = &IN6ADDR_ANY.s6_addr;
|
|
1756 return *addr;
|
|
1757 }
|
|
1758 else static if (is(typeof(in6addr_any)))
|
|
1759 {
|
|
1760 addr = &in6addr_any.s6_addr;
|
|
1761 return *addr;
|
|
1762 }
|
|
1763 else
|
|
1764 static assert(0);
|
|
1765 }
|
|
1766
|
|
1767 /// Any IPv6 port number.
|
|
1768 enum ushort PORT_ANY = 0;
|
|
1769
|
|
1770 /// Returns the IPv6 port number.
|
|
1771 @property ushort port() const pure nothrow @nogc
|
|
1772 {
|
|
1773 return ntohs(sin6.sin6_port);
|
|
1774 }
|
|
1775
|
|
1776 /// Returns the IPv6 address.
|
|
1777 @property ubyte[16] addr() const pure nothrow @nogc
|
|
1778 {
|
|
1779 return sin6.sin6_addr.s6_addr;
|
|
1780 }
|
|
1781
|
|
1782 /**
|
|
1783 * Construct a new $(D Internet6Address).
|
|
1784 * Params:
|
|
1785 * addr = an IPv6 host address string in the form described in RFC 2373,
|
|
1786 * or a host name which will be resolved using $(D getAddressInfo).
|
|
1787 * service = (optional) service name.
|
|
1788 */
|
|
1789 this(in char[] addr, in char[] service = null) @trusted
|
|
1790 {
|
|
1791 auto results = getAddressInfo(addr, service, AddressFamily.INET6);
|
|
1792 assert(results.length && results[0].family == AddressFamily.INET6);
|
|
1793 sin6 = *cast(sockaddr_in6*) results[0].address.name;
|
|
1794 }
|
|
1795
|
|
1796 /**
|
|
1797 * Construct a new $(D Internet6Address).
|
|
1798 * Params:
|
|
1799 * addr = an IPv6 host address string in the form described in RFC 2373,
|
|
1800 * or a host name which will be resolved using $(D getAddressInfo).
|
|
1801 * port = port number, may be $(D PORT_ANY).
|
|
1802 */
|
|
1803 this(in char[] addr, ushort port)
|
|
1804 {
|
|
1805 if (port == PORT_ANY)
|
|
1806 this(addr);
|
|
1807 else
|
|
1808 this(addr, to!string(port));
|
|
1809 }
|
|
1810
|
|
1811 /**
|
|
1812 * Construct a new $(D Internet6Address).
|
|
1813 * Params:
|
|
1814 * addr = (optional) an IPv6 host address in host byte order, or
|
|
1815 * $(D ADDR_ANY).
|
|
1816 * port = port number, may be $(D PORT_ANY).
|
|
1817 */
|
|
1818 this(ubyte[16] addr, ushort port) pure nothrow @nogc
|
|
1819 {
|
|
1820 sin6.sin6_family = AddressFamily.INET6;
|
|
1821 sin6.sin6_addr.s6_addr = addr;
|
|
1822 sin6.sin6_port = htons(port);
|
|
1823 }
|
|
1824
|
|
1825 /// ditto
|
|
1826 this(ushort port) pure nothrow @nogc
|
|
1827 {
|
|
1828 sin6.sin6_family = AddressFamily.INET6;
|
|
1829 sin6.sin6_addr.s6_addr = ADDR_ANY;
|
|
1830 sin6.sin6_port = htons(port);
|
|
1831 }
|
|
1832
|
|
1833 /**
|
|
1834 * Construct a new $(D Internet6Address).
|
|
1835 * Params:
|
|
1836 * addr = A sockaddr_in6 as obtained from lower-level API calls such as getifaddrs.
|
|
1837 */
|
|
1838 this(sockaddr_in6 addr) pure nothrow @nogc
|
|
1839 {
|
|
1840 assert(addr.sin6_family == AddressFamily.INET6);
|
|
1841 sin6 = addr;
|
|
1842 }
|
|
1843
|
|
1844 /**
|
|
1845 * Parse an IPv6 host address string as described in RFC 2373, and return the
|
|
1846 * address.
|
|
1847 * Throws: $(D SocketException) on error.
|
|
1848 */
|
|
1849 static ubyte[16] parse(in char[] addr) @trusted
|
|
1850 {
|
|
1851 // Although we could use inet_pton here, it's only available on Windows
|
|
1852 // versions starting with Vista, so use getAddressInfo with NUMERICHOST
|
|
1853 // instead.
|
|
1854 auto results = getAddressInfo(addr, AddressInfoFlags.NUMERICHOST);
|
|
1855 if (results.length && results[0].family == AddressFamily.INET6)
|
|
1856 return (cast(sockaddr_in6*) results[0].address.name).sin6_addr.s6_addr;
|
|
1857 throw new AddressException("Not an IPv6 address", 0);
|
|
1858 }
|
|
1859 }
|
|
1860
|
|
1861
|
|
1862 @safe unittest
|
|
1863 {
|
|
1864 softUnittest({
|
|
1865 const Internet6Address ia = new Internet6Address("::1", 80);
|
|
1866 assert(ia.toString() == "[::1]:80");
|
|
1867 });
|
|
1868
|
|
1869 softUnittest({
|
|
1870 // test construction from a sockaddr_in6
|
|
1871 sockaddr_in6 sin;
|
|
1872
|
|
1873 sin.sin6_addr.s6_addr = [ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1]; // [::1]
|
|
1874 sin.sin6_family = AddressFamily.INET6;
|
|
1875 sin.sin6_port = htons(80);
|
|
1876
|
|
1877 const Internet6Address ia = new Internet6Address(sin);
|
|
1878 assert(ia.toString() == "[::1]:80");
|
|
1879 });
|
|
1880 }
|
|
1881
|
|
1882
|
|
1883 version (StdDdoc)
|
|
1884 {
|
|
1885 static if (!is(sockaddr_un))
|
|
1886 {
|
|
1887 // This exists only to allow the constructor taking
|
|
1888 // a sockaddr_un to be compilable for documentation
|
|
1889 // on platforms that don't supply a sockaddr_un.
|
|
1890 struct sockaddr_un
|
|
1891 {
|
|
1892 }
|
|
1893 }
|
|
1894
|
|
1895 /**
|
|
1896 * $(D UnixAddress) encapsulates an address for a Unix domain socket
|
|
1897 * ($(D AF_UNIX)), i.e. a socket bound to a path name in the file system.
|
|
1898 * Available only on supported systems.
|
|
1899 *
|
|
1900 * Linux also supports an abstract address namespace, in which addresses
|
|
1901 * are independent of the file system. A socket address is abstract
|
|
1902 * iff `path` starts with a _null byte (`'\0'`). Null bytes in other
|
|
1903 * positions of an abstract address are allowed and have no special
|
|
1904 * meaning.
|
|
1905 *
|
|
1906 * Example:
|
|
1907 * ---
|
|
1908 * auto addr = new UnixAddress("/var/run/dbus/system_bus_socket");
|
|
1909 * auto abstractAddr = new UnixAddress("\0/tmp/dbus-OtHLWmCLPR");
|
|
1910 * ---
|
|
1911 *
|
|
1912 * See_Also: $(HTTP http://man7.org/linux/man-pages/man7/unix.7.html, UNIX(7))
|
|
1913 */
|
|
1914 class UnixAddress: Address
|
|
1915 {
|
|
1916 private this() pure nothrow @nogc {}
|
|
1917
|
|
1918 /// Construct a new $(D UnixAddress) from the specified path.
|
|
1919 this(in char[] path) { }
|
|
1920
|
|
1921 /**
|
|
1922 * Construct a new $(D UnixAddress).
|
|
1923 * Params:
|
|
1924 * addr = A sockaddr_un as obtained from lower-level API calls.
|
|
1925 */
|
|
1926 this(sockaddr_un addr) pure nothrow @nogc { }
|
|
1927
|
|
1928 /// Get the underlying _path.
|
|
1929 @property string path() const { return null; }
|
|
1930
|
|
1931 /// ditto
|
|
1932 override string toString() const { return null; }
|
|
1933
|
|
1934 override @property sockaddr* name() { return null; }
|
|
1935 override @property const(sockaddr)* name() const { return null; }
|
|
1936 override @property socklen_t nameLen() const { return 0; }
|
|
1937 }
|
|
1938 }
|
|
1939 else
|
|
1940 static if (is(sockaddr_un))
|
|
1941 {
|
|
1942 class UnixAddress: Address
|
|
1943 {
|
|
1944 protected:
|
|
1945 socklen_t _nameLen;
|
|
1946
|
|
1947 struct
|
|
1948 {
|
|
1949 align (1):
|
|
1950 sockaddr_un sun;
|
|
1951 char unused = '\0'; // placeholder for a terminating '\0'
|
|
1952 }
|
|
1953
|
|
1954 this() pure nothrow @nogc
|
|
1955 {
|
|
1956 sun.sun_family = AddressFamily.UNIX;
|
|
1957 sun.sun_path = '?';
|
|
1958 _nameLen = sun.sizeof;
|
|
1959 }
|
|
1960
|
|
1961 override void setNameLen(socklen_t len) @trusted
|
|
1962 {
|
|
1963 if (len > sun.sizeof)
|
|
1964 throw new SocketParameterException("Not enough socket address storage");
|
|
1965 _nameLen = len;
|
|
1966 }
|
|
1967
|
|
1968 public:
|
|
1969 override @property sockaddr* name()
|
|
1970 {
|
|
1971 return cast(sockaddr*)&sun;
|
|
1972 }
|
|
1973
|
|
1974 override @property const(sockaddr)* name() const
|
|
1975 {
|
|
1976 return cast(const(sockaddr)*)&sun;
|
|
1977 }
|
|
1978
|
|
1979 override @property socklen_t nameLen() @trusted const
|
|
1980 {
|
|
1981 return _nameLen;
|
|
1982 }
|
|
1983
|
|
1984 this(in char[] path) @trusted pure
|
|
1985 {
|
|
1986 enforce(path.length <= sun.sun_path.sizeof, new SocketParameterException("Path too long"));
|
|
1987 sun.sun_family = AddressFamily.UNIX;
|
|
1988 sun.sun_path.ptr[0 .. path.length] = (cast(byte[]) path)[];
|
|
1989 _nameLen = cast(socklen_t)
|
|
1990 {
|
|
1991 auto len = sockaddr_un.init.sun_path.offsetof + path.length;
|
|
1992 // Pathname socket address must be terminated with '\0'
|
|
1993 // which must be included in the address length.
|
|
1994 if (sun.sun_path.ptr[0])
|
|
1995 {
|
|
1996 sun.sun_path.ptr[path.length] = 0;
|
|
1997 ++len;
|
|
1998 }
|
|
1999 return len;
|
|
2000 }();
|
|
2001 }
|
|
2002
|
|
2003 this(sockaddr_un addr) pure nothrow @nogc
|
|
2004 {
|
|
2005 assert(addr.sun_family == AddressFamily.UNIX);
|
|
2006 sun = addr;
|
|
2007 }
|
|
2008
|
|
2009 @property string path() @trusted const pure
|
|
2010 {
|
|
2011 auto len = _nameLen - sockaddr_un.init.sun_path.offsetof;
|
|
2012 // For pathname socket address we need to strip off the terminating '\0'
|
|
2013 if (sun.sun_path.ptr[0])
|
|
2014 --len;
|
|
2015 return (cast(const(char)*) sun.sun_path.ptr)[0 .. len].idup;
|
|
2016 }
|
|
2017
|
|
2018 override string toString() const pure
|
|
2019 {
|
|
2020 return path;
|
|
2021 }
|
|
2022 }
|
|
2023
|
|
2024 @safe unittest
|
|
2025 {
|
|
2026 import core.stdc.stdio : remove;
|
|
2027 import std.file : deleteme;
|
|
2028
|
|
2029 immutable ubyte[] data = [1, 2, 3, 4];
|
|
2030 Socket[2] pair;
|
|
2031
|
|
2032 auto names = [ deleteme ~ "-unix-socket" ];
|
|
2033 version (linux)
|
|
2034 names ~= "\0" ~ deleteme ~ "-abstract\0unix\0socket";
|
|
2035 foreach (name; names)
|
|
2036 {
|
|
2037 auto address = new UnixAddress(name);
|
|
2038
|
|
2039 auto listener = new Socket(AddressFamily.UNIX, SocketType.STREAM);
|
|
2040 scope(exit) listener.close();
|
|
2041 listener.bind(address);
|
|
2042 scope(exit) () @trusted { if (name[0]) remove(name.tempCString()); } ();
|
|
2043 assert(listener.localAddress.toString == name);
|
|
2044
|
|
2045 listener.listen(1);
|
|
2046
|
|
2047 pair[0] = new Socket(AddressFamily.UNIX, SocketType.STREAM);
|
|
2048 scope(exit) listener.close();
|
|
2049
|
|
2050 pair[0].connect(address);
|
|
2051 scope(exit) pair[0].close();
|
|
2052
|
|
2053 pair[1] = listener.accept();
|
|
2054 scope(exit) pair[1].close();
|
|
2055
|
|
2056 pair[0].send(data);
|
|
2057
|
|
2058 auto buf = new ubyte[data.length];
|
|
2059 pair[1].receive(buf);
|
|
2060 assert(buf == data);
|
|
2061 }
|
|
2062 }
|
|
2063 }
|
|
2064
|
|
2065
|
|
2066 /**
|
|
2067 * Class for exceptions thrown by $(D Socket.accept).
|
|
2068 */
|
|
2069 class SocketAcceptException: SocketOSException
|
|
2070 {
|
|
2071 mixin socketOSExceptionCtors;
|
|
2072 }
|
|
2073
|
|
2074 /// How a socket is shutdown:
|
|
2075 enum SocketShutdown: int
|
|
2076 {
|
|
2077 RECEIVE = SD_RECEIVE, /// socket receives are disallowed
|
|
2078 SEND = SD_SEND, /// socket sends are disallowed
|
|
2079 BOTH = SD_BOTH, /// both RECEIVE and SEND
|
|
2080 }
|
|
2081
|
|
2082
|
|
2083 /// Flags may be OR'ed together:
|
|
2084 enum SocketFlags: int
|
|
2085 {
|
|
2086 NONE = 0, /// no flags specified
|
|
2087
|
|
2088 OOB = MSG_OOB, /// out-of-band stream data
|
|
2089 PEEK = MSG_PEEK, /// peek at incoming data without removing it from the queue, only for receiving
|
|
2090 DONTROUTE = MSG_DONTROUTE, /// data should not be subject to routing; this flag may be ignored. Only for sending
|
|
2091 }
|
|
2092
|
|
2093
|
|
2094 private mixin template FieldProxy(string target, string field)
|
|
2095 {
|
|
2096 mixin(`
|
|
2097 @property typeof(`~target~`) `~field~`() const pure nothrow @nogc
|
|
2098 {
|
|
2099 return `~target~`;
|
|
2100 }
|
|
2101
|
|
2102 /// ditto
|
|
2103 @property typeof(`~target~`) `~field~`(typeof(`~target~`) value) pure nothrow @nogc
|
|
2104 {
|
|
2105 return `~target~` = value;
|
|
2106 }
|
|
2107 `);
|
|
2108 }
|
|
2109
|
|
2110
|
|
2111 /// Duration timeout value.
|
|
2112 struct TimeVal
|
|
2113 {
|
|
2114 _ctimeval ctimeval;
|
|
2115 alias tv_sec_t = typeof(ctimeval.tv_sec);
|
|
2116 alias tv_usec_t = typeof(ctimeval.tv_usec);
|
|
2117
|
|
2118 version (StdDdoc) // no DDoc for string mixins, can't forward individual fields
|
|
2119 {
|
|
2120 tv_sec_t seconds; /// Number of _seconds.
|
|
2121 tv_usec_t microseconds; /// Number of additional _microseconds.
|
|
2122 }
|
|
2123 else
|
|
2124 {
|
|
2125 // D interface
|
|
2126 mixin FieldProxy!(`ctimeval.tv_sec`, `seconds`);
|
|
2127 mixin FieldProxy!(`ctimeval.tv_usec`, `microseconds`);
|
|
2128 }
|
|
2129 }
|
|
2130
|
|
2131
|
|
2132 /**
|
|
2133 * A collection of sockets for use with $(D Socket.select).
|
|
2134 *
|
|
2135 * $(D SocketSet) wraps the platform $(D fd_set) type. However, unlike
|
|
2136 * $(D fd_set), $(D SocketSet) is not statically limited to $(D FD_SETSIZE)
|
|
2137 * or any other limit, and grows as needed.
|
|
2138 */
|
|
2139 class SocketSet
|
|
2140 {
|
|
2141 private:
|
|
2142 version (Windows)
|
|
2143 {
|
|
2144 // On Windows, fd_set is an array of socket handles,
|
|
2145 // following a word containing the fd_set instance size.
|
|
2146 // We use one dynamic array for everything, and use its first
|
|
2147 // element(s) for the count.
|
|
2148
|
|
2149 alias fd_set_count_type = typeof(fd_set.init.fd_count);
|
|
2150 alias fd_set_type = typeof(fd_set.init.fd_array[0]);
|
|
2151 static assert(fd_set_type.sizeof == socket_t.sizeof);
|
|
2152
|
|
2153 // Number of fd_set_type elements at the start of our array that are
|
|
2154 // used for the socket count and alignment
|
|
2155
|
|
2156 enum FD_SET_OFFSET = fd_set.fd_array.offsetof / fd_set_type.sizeof;
|
|
2157 static assert(FD_SET_OFFSET);
|
|
2158 static assert(fd_set.fd_count.offsetof % fd_set_type.sizeof == 0);
|
|
2159
|
|
2160 fd_set_type[] set;
|
|
2161
|
|
2162 void resize(size_t size) pure nothrow
|
|
2163 {
|
|
2164 set.length = FD_SET_OFFSET + size;
|
|
2165 }
|
|
2166
|
|
2167 ref inout(fd_set_count_type) count() @trusted @property inout pure nothrow @nogc
|
|
2168 {
|
|
2169 assert(set.length);
|
|
2170 return *cast(inout(fd_set_count_type)*)set.ptr;
|
|
2171 }
|
|
2172
|
|
2173 size_t capacity() @property const pure nothrow @nogc
|
|
2174 {
|
|
2175 return set.length - FD_SET_OFFSET;
|
|
2176 }
|
|
2177
|
|
2178 inout(socket_t)[] fds() @trusted inout @property pure nothrow @nogc
|
|
2179 {
|
|
2180 return cast(inout(socket_t)[])set[FD_SET_OFFSET .. FD_SET_OFFSET+count];
|
|
2181 }
|
|
2182 }
|
|
2183 else
|
|
2184 version (Posix)
|
|
2185 {
|
|
2186 // On Posix, fd_set is a bit array. We assume that the fd_set
|
|
2187 // type (declared in core.sys.posix.sys.select) is a structure
|
|
2188 // containing a single field, a static array.
|
|
2189
|
|
2190 static assert(fd_set.tupleof.length == 1);
|
|
2191
|
|
2192 // This is the type used in the fd_set array.
|
|
2193 // Using the type of the correct size is important for big-endian
|
|
2194 // architectures.
|
|
2195
|
|
2196 alias fd_set_type = typeof(fd_set.init.tupleof[0][0]);
|
|
2197
|
|
2198 // Number of file descriptors represented by one fd_set_type
|
|
2199
|
|
2200 enum FD_NFDBITS = 8 * fd_set_type.sizeof;
|
|
2201
|
|
2202 static fd_set_type mask(uint n) pure nothrow @nogc
|
|
2203 {
|
|
2204 return (cast(fd_set_type) 1) << (n % FD_NFDBITS);
|
|
2205 }
|
|
2206
|
|
2207 // Array size to fit that many sockets
|
|
2208
|
|
2209 static size_t lengthFor(size_t size) pure nothrow @nogc
|
|
2210 {
|
|
2211 return (size + (FD_NFDBITS-1)) / FD_NFDBITS;
|
|
2212 }
|
|
2213
|
|
2214 fd_set_type[] set;
|
|
2215
|
|
2216 void resize(size_t size) pure nothrow
|
|
2217 {
|
|
2218 set.length = lengthFor(size);
|
|
2219 }
|
|
2220
|
|
2221 // Make sure we can fit that many sockets
|
|
2222
|
|
2223 void setMinCapacity(size_t size) pure nothrow
|
|
2224 {
|
|
2225 auto length = lengthFor(size);
|
|
2226 if (set.length < length)
|
|
2227 set.length = length;
|
|
2228 }
|
|
2229
|
|
2230 size_t capacity() @property const pure nothrow @nogc
|
|
2231 {
|
|
2232 return set.length * FD_NFDBITS;
|
|
2233 }
|
|
2234
|
|
2235 int maxfd;
|
|
2236 }
|
|
2237 else
|
|
2238 static assert(false, "Unknown platform");
|
|
2239
|
|
2240 public:
|
|
2241
|
|
2242 /**
|
|
2243 * Create a SocketSet with a specific initial capacity (defaults to
|
|
2244 * $(D FD_SETSIZE), the system's default capacity).
|
|
2245 */
|
|
2246 this(size_t size = FD_SETSIZE) pure nothrow
|
|
2247 {
|
|
2248 resize(size);
|
|
2249 reset();
|
|
2250 }
|
|
2251
|
|
2252 /// Reset the $(D SocketSet) so that there are 0 $(D Socket)s in the collection.
|
|
2253 void reset() pure nothrow @nogc
|
|
2254 {
|
|
2255 version (Windows)
|
|
2256 count = 0;
|
|
2257 else
|
|
2258 {
|
|
2259 set[] = 0;
|
|
2260 maxfd = -1;
|
|
2261 }
|
|
2262 }
|
|
2263
|
|
2264
|
|
2265 void add(socket_t s) @trusted pure nothrow
|
|
2266 {
|
|
2267 version (Windows)
|
|
2268 {
|
|
2269 if (count == capacity)
|
|
2270 {
|
|
2271 set.length *= 2;
|
|
2272 set.length = set.capacity;
|
|
2273 }
|
|
2274 ++count;
|
|
2275 fds[$-1] = s;
|
|
2276 }
|
|
2277 else
|
|
2278 {
|
|
2279 auto index = s / FD_NFDBITS;
|
|
2280 auto length = set.length;
|
|
2281 if (index >= length)
|
|
2282 {
|
|
2283 while (index >= length)
|
|
2284 length *= 2;
|
|
2285 set.length = length;
|
|
2286 set.length = set.capacity;
|
|
2287 }
|
|
2288 set[index] |= mask(s);
|
|
2289 if (maxfd < s)
|
|
2290 maxfd = s;
|
|
2291 }
|
|
2292 }
|
|
2293
|
|
2294 /**
|
|
2295 * Add a $(D Socket) to the collection.
|
|
2296 * The socket must not already be in the collection.
|
|
2297 */
|
|
2298 void add(Socket s) pure nothrow
|
|
2299 {
|
|
2300 add(s.sock);
|
|
2301 }
|
|
2302
|
|
2303 void remove(socket_t s) pure nothrow
|
|
2304 {
|
|
2305 version (Windows)
|
|
2306 {
|
|
2307 import std.algorithm.searching : countUntil;
|
|
2308 auto fds = fds;
|
|
2309 auto p = fds.countUntil(s);
|
|
2310 if (p >= 0)
|
|
2311 fds[p] = fds[--count];
|
|
2312 }
|
|
2313 else
|
|
2314 {
|
|
2315 auto index = s / FD_NFDBITS;
|
|
2316 if (index >= set.length)
|
|
2317 return;
|
|
2318 set[index] &= ~mask(s);
|
|
2319 // note: adjusting maxfd would require scanning the set, not worth it
|
|
2320 }
|
|
2321 }
|
|
2322
|
|
2323
|
|
2324 /**
|
|
2325 * Remove this $(D Socket) from the collection.
|
|
2326 * Does nothing if the socket is not in the collection already.
|
|
2327 */
|
|
2328 void remove(Socket s) pure nothrow
|
|
2329 {
|
|
2330 remove(s.sock);
|
|
2331 }
|
|
2332
|
|
2333 int isSet(socket_t s) const pure nothrow @nogc
|
|
2334 {
|
|
2335 version (Windows)
|
|
2336 {
|
|
2337 import std.algorithm.searching : canFind;
|
|
2338 return fds.canFind(s) ? 1 : 0;
|
|
2339 }
|
|
2340 else
|
|
2341 {
|
|
2342 if (s > maxfd)
|
|
2343 return 0;
|
|
2344 auto index = s / FD_NFDBITS;
|
|
2345 return (set[index] & mask(s)) ? 1 : 0;
|
|
2346 }
|
|
2347 }
|
|
2348
|
|
2349
|
|
2350 /// Return nonzero if this $(D Socket) is in the collection.
|
|
2351 int isSet(Socket s) const pure nothrow @nogc
|
|
2352 {
|
|
2353 return isSet(s.sock);
|
|
2354 }
|
|
2355
|
|
2356
|
|
2357 /**
|
|
2358 * Returns:
|
|
2359 * The current capacity of this $(D SocketSet). The exact
|
|
2360 * meaning of the return value varies from platform to platform.
|
|
2361 *
|
|
2362 * Note:
|
|
2363 * Since D 2.065, this value does not indicate a
|
|
2364 * restriction, and $(D SocketSet) will grow its capacity as
|
|
2365 * needed automatically.
|
|
2366 */
|
|
2367 @property uint max() const pure nothrow @nogc
|
|
2368 {
|
|
2369 return cast(uint) capacity;
|
|
2370 }
|
|
2371
|
|
2372
|
|
2373 fd_set* toFd_set() @trusted pure nothrow @nogc
|
|
2374 {
|
|
2375 return cast(fd_set*) set.ptr;
|
|
2376 }
|
|
2377
|
|
2378
|
|
2379 int selectn() const pure nothrow @nogc
|
|
2380 {
|
|
2381 version (Windows)
|
|
2382 {
|
|
2383 return count;
|
|
2384 }
|
|
2385 else version (Posix)
|
|
2386 {
|
|
2387 return maxfd + 1;
|
|
2388 }
|
|
2389 }
|
|
2390 }
|
|
2391
|
|
2392 @safe unittest
|
|
2393 {
|
|
2394 auto fds = cast(socket_t[])
|
|
2395 [cast(socket_t) 1, 2, 0, 1024, 17, 42, 1234, 77, 77+32, 77+64];
|
|
2396 auto set = new SocketSet();
|
|
2397 foreach (fd; fds) assert(!set.isSet(fd));
|
|
2398 foreach (fd; fds) set.add(fd);
|
|
2399 foreach (fd; fds) assert(set.isSet(fd));
|
|
2400
|
|
2401 // Make sure SocketSet reimplements fd_set correctly
|
|
2402 auto fdset = set.toFd_set();
|
|
2403 foreach (fd; fds[0]..cast(socket_t)(fds[$-1]+1))
|
|
2404 assert(cast(bool) set.isSet(fd) == cast(bool)(() @trusted => FD_ISSET(fd, fdset))());
|
|
2405
|
|
2406 foreach (fd; fds)
|
|
2407 {
|
|
2408 assert(set.isSet(fd));
|
|
2409 set.remove(fd);
|
|
2410 assert(!set.isSet(fd));
|
|
2411 }
|
|
2412 }
|
|
2413
|
|
2414 @safe unittest
|
|
2415 {
|
|
2416 softUnittest({
|
|
2417 enum PAIRS = 768;
|
|
2418 version (Posix)
|
|
2419 () @trusted
|
|
2420 {
|
|
2421 enum LIMIT = 2048;
|
|
2422 static assert(LIMIT > PAIRS*2);
|
|
2423 import core.sys.posix.sys.resource;
|
|
2424 rlimit fileLimit;
|
|
2425 getrlimit(RLIMIT_NOFILE, &fileLimit);
|
|
2426 assert(fileLimit.rlim_max > LIMIT, "Open file hard limit too low");
|
|
2427 fileLimit.rlim_cur = LIMIT;
|
|
2428 setrlimit(RLIMIT_NOFILE, &fileLimit);
|
|
2429 } ();
|
|
2430
|
|
2431 Socket[2][PAIRS] pairs;
|
|
2432 foreach (ref pair; pairs)
|
|
2433 pair = socketPair();
|
|
2434 scope(exit)
|
|
2435 {
|
|
2436 foreach (pair; pairs)
|
|
2437 {
|
|
2438 pair[0].close();
|
|
2439 pair[1].close();
|
|
2440 }
|
|
2441 }
|
|
2442
|
|
2443 import std.random;
|
|
2444 auto rng = Xorshift(42);
|
|
2445 pairs[].randomShuffle(rng);
|
|
2446
|
|
2447 auto readSet = new SocketSet();
|
|
2448 auto writeSet = new SocketSet();
|
|
2449 auto errorSet = new SocketSet();
|
|
2450
|
|
2451 foreach (testPair; pairs)
|
|
2452 {
|
|
2453 void fillSets()
|
|
2454 {
|
|
2455 readSet.reset();
|
|
2456 writeSet.reset();
|
|
2457 errorSet.reset();
|
|
2458 foreach (ref pair; pairs)
|
|
2459 foreach (s; pair[])
|
|
2460 {
|
|
2461 readSet.add(s);
|
|
2462 writeSet.add(s);
|
|
2463 errorSet.add(s);
|
|
2464 }
|
|
2465 }
|
|
2466
|
|
2467 fillSets();
|
|
2468 auto n = Socket.select(readSet, writeSet, errorSet);
|
|
2469 assert(n == PAIRS*2); // All in writeSet
|
|
2470 assert(writeSet.isSet(testPair[0]));
|
|
2471 assert(writeSet.isSet(testPair[1]));
|
|
2472 assert(!readSet.isSet(testPair[0]));
|
|
2473 assert(!readSet.isSet(testPair[1]));
|
|
2474 assert(!errorSet.isSet(testPair[0]));
|
|
2475 assert(!errorSet.isSet(testPair[1]));
|
|
2476
|
|
2477 ubyte[1] b;
|
|
2478 testPair[0].send(b[]);
|
|
2479 fillSets();
|
|
2480 n = Socket.select(readSet, null, null);
|
|
2481 assert(n == 1); // testPair[1]
|
|
2482 assert(readSet.isSet(testPair[1]));
|
|
2483 assert(!readSet.isSet(testPair[0]));
|
|
2484 testPair[1].receive(b[]);
|
|
2485 }
|
|
2486 });
|
|
2487 }
|
|
2488
|
|
2489 @safe unittest // Issue 14012, 14013
|
|
2490 {
|
|
2491 auto set = new SocketSet(1);
|
|
2492 assert(set.max >= 0);
|
|
2493
|
|
2494 enum LIMIT = 4096;
|
|
2495 foreach (n; 0 .. LIMIT)
|
|
2496 set.add(cast(socket_t) n);
|
|
2497 assert(set.max >= LIMIT);
|
|
2498 }
|
|
2499
|
|
2500 /// The level at which a socket option is defined:
|
|
2501 enum SocketOptionLevel: int
|
|
2502 {
|
|
2503 SOCKET = SOL_SOCKET, /// Socket level
|
|
2504 IP = ProtocolType.IP, /// Internet Protocol version 4 level
|
|
2505 ICMP = ProtocolType.ICMP, /// Internet Control Message Protocol level
|
|
2506 IGMP = ProtocolType.IGMP, /// Internet Group Management Protocol level
|
|
2507 GGP = ProtocolType.GGP, /// Gateway to Gateway Protocol level
|
|
2508 TCP = ProtocolType.TCP, /// Transmission Control Protocol level
|
|
2509 PUP = ProtocolType.PUP, /// PARC Universal Packet Protocol level
|
|
2510 UDP = ProtocolType.UDP, /// User Datagram Protocol level
|
|
2511 IDP = ProtocolType.IDP, /// Xerox NS protocol level
|
|
2512 RAW = ProtocolType.RAW, /// Raw IP packet level
|
|
2513 IPV6 = ProtocolType.IPV6, /// Internet Protocol version 6 level
|
|
2514 }
|
|
2515
|
|
2516 /// _Linger information for use with SocketOption.LINGER.
|
|
2517 struct Linger
|
|
2518 {
|
|
2519 _clinger clinger;
|
|
2520
|
|
2521 version (StdDdoc) // no DDoc for string mixins, can't forward individual fields
|
|
2522 {
|
|
2523 private alias l_onoff_t = typeof(_clinger.init.l_onoff );
|
|
2524 private alias l_linger_t = typeof(_clinger.init.l_linger);
|
|
2525 l_onoff_t on; /// Nonzero for _on.
|
|
2526 l_linger_t time; /// Linger _time.
|
|
2527 }
|
|
2528 else
|
|
2529 {
|
|
2530 // D interface
|
|
2531 mixin FieldProxy!(`clinger.l_onoff`, `on`);
|
|
2532 mixin FieldProxy!(`clinger.l_linger`, `time`);
|
|
2533 }
|
|
2534 }
|
|
2535
|
|
2536 /// Specifies a socket option:
|
|
2537 enum SocketOption: int
|
|
2538 {
|
|
2539 DEBUG = SO_DEBUG, /// Record debugging information
|
|
2540 BROADCAST = SO_BROADCAST, /// Allow transmission of broadcast messages
|
|
2541 REUSEADDR = SO_REUSEADDR, /// Allow local reuse of address
|
|
2542 LINGER = SO_LINGER, /// Linger on close if unsent data is present
|
|
2543 OOBINLINE = SO_OOBINLINE, /// Receive out-of-band data in band
|
|
2544 SNDBUF = SO_SNDBUF, /// Send buffer size
|
|
2545 RCVBUF = SO_RCVBUF, /// Receive buffer size
|
|
2546 DONTROUTE = SO_DONTROUTE, /// Do not route
|
|
2547 SNDTIMEO = SO_SNDTIMEO, /// Send timeout
|
|
2548 RCVTIMEO = SO_RCVTIMEO, /// Receive timeout
|
|
2549 ERROR = SO_ERROR, /// Retrieve and clear error status
|
|
2550 KEEPALIVE = SO_KEEPALIVE, /// Enable keep-alive packets
|
|
2551 ACCEPTCONN = SO_ACCEPTCONN, /// Listen
|
|
2552 RCVLOWAT = SO_RCVLOWAT, /// Minimum number of input bytes to process
|
|
2553 SNDLOWAT = SO_SNDLOWAT, /// Minimum number of output bytes to process
|
|
2554 TYPE = SO_TYPE, /// Socket type
|
|
2555
|
|
2556 // SocketOptionLevel.TCP:
|
|
2557 TCP_NODELAY = .TCP_NODELAY, /// Disable the Nagle algorithm for send coalescing
|
|
2558
|
|
2559 // SocketOptionLevel.IPV6:
|
|
2560 IPV6_UNICAST_HOPS = .IPV6_UNICAST_HOPS, /// IP unicast hop limit
|
|
2561 IPV6_MULTICAST_IF = .IPV6_MULTICAST_IF, /// IP multicast interface
|
|
2562 IPV6_MULTICAST_LOOP = .IPV6_MULTICAST_LOOP, /// IP multicast loopback
|
|
2563 IPV6_MULTICAST_HOPS = .IPV6_MULTICAST_HOPS, /// IP multicast hops
|
|
2564 IPV6_JOIN_GROUP = .IPV6_JOIN_GROUP, /// Add an IP group membership
|
|
2565 IPV6_LEAVE_GROUP = .IPV6_LEAVE_GROUP, /// Drop an IP group membership
|
|
2566 IPV6_V6ONLY = .IPV6_V6ONLY, /// Treat wildcard bind as AF_INET6-only
|
|
2567 }
|
|
2568
|
|
2569
|
|
2570 /**
|
|
2571 * $(D Socket) is a class that creates a network communication endpoint using
|
|
2572 * the Berkeley sockets interface.
|
|
2573 */
|
|
2574 class Socket
|
|
2575 {
|
|
2576 private:
|
|
2577 socket_t sock;
|
|
2578 AddressFamily _family;
|
|
2579
|
|
2580 version (Windows)
|
|
2581 bool _blocking = false; /// Property to get or set whether the socket is blocking or nonblocking.
|
|
2582
|
|
2583 // The WinSock timeouts seem to be effectively skewed by a constant
|
|
2584 // offset of about half a second (value in milliseconds). This has
|
|
2585 // been confirmed on updated (as of Jun 2011) Windows XP, Windows 7
|
|
2586 // and Windows Server 2008 R2 boxes. The unittest below tests this
|
|
2587 // behavior.
|
|
2588 enum WINSOCK_TIMEOUT_SKEW = 500;
|
|
2589
|
|
2590 @safe unittest
|
|
2591 {
|
|
2592 version (SlowTests)
|
|
2593 softUnittest({
|
|
2594 import std.datetime;
|
|
2595 import std.typecons;
|
|
2596
|
|
2597 enum msecs = 1000;
|
|
2598 auto pair = socketPair();
|
|
2599 auto sock = pair[0];
|
|
2600 sock.setOption(SocketOptionLevel.SOCKET,
|
|
2601 SocketOption.RCVTIMEO, dur!"msecs"(msecs));
|
|
2602
|
|
2603 auto sw = StopWatch(Yes.autoStart);
|
|
2604 ubyte[1] buf;
|
|
2605 sock.receive(buf);
|
|
2606 sw.stop();
|
|
2607
|
|
2608 Duration readBack = void;
|
|
2609 sock.getOption(SocketOptionLevel.SOCKET, SocketOption.RCVTIMEO, readBack);
|
|
2610
|
|
2611 assert(readBack.total!"msecs" == msecs);
|
|
2612 assert(sw.peek().msecs > msecs-100 && sw.peek().msecs < msecs+100);
|
|
2613 });
|
|
2614 }
|
|
2615
|
|
2616 void setSock(socket_t handle)
|
|
2617 {
|
|
2618 assert(handle != socket_t.init);
|
|
2619 sock = handle;
|
|
2620
|
|
2621 // Set the option to disable SIGPIPE on send() if the platform
|
|
2622 // has it (e.g. on OS X).
|
|
2623 static if (is(typeof(SO_NOSIGPIPE)))
|
|
2624 {
|
|
2625 setOption(SocketOptionLevel.SOCKET, cast(SocketOption) SO_NOSIGPIPE, true);
|
|
2626 }
|
|
2627 }
|
|
2628
|
|
2629
|
|
2630 // For use with accepting().
|
|
2631 protected this() pure nothrow @nogc
|
|
2632 {
|
|
2633 }
|
|
2634
|
|
2635
|
|
2636 public:
|
|
2637
|
|
2638 /**
|
|
2639 * Create a blocking socket. If a single protocol type exists to support
|
|
2640 * this socket type within the address family, the $(D ProtocolType) may be
|
|
2641 * omitted.
|
|
2642 */
|
|
2643 this(AddressFamily af, SocketType type, ProtocolType protocol) @trusted
|
|
2644 {
|
|
2645 _family = af;
|
|
2646 auto handle = cast(socket_t) socket(af, type, protocol);
|
|
2647 if (handle == socket_t.init)
|
|
2648 throw new SocketOSException("Unable to create socket");
|
|
2649 setSock(handle);
|
|
2650 }
|
|
2651
|
|
2652 /// ditto
|
|
2653 this(AddressFamily af, SocketType type)
|
|
2654 {
|
|
2655 /* A single protocol exists to support this socket type within the
|
|
2656 * protocol family, so the ProtocolType is assumed.
|
|
2657 */
|
|
2658 this(af, type, cast(ProtocolType) 0); // Pseudo protocol number.
|
|
2659 }
|
|
2660
|
|
2661
|
|
2662 /// ditto
|
|
2663 this(AddressFamily af, SocketType type, in char[] protocolName) @trusted
|
|
2664 {
|
|
2665 protoent* proto;
|
|
2666 proto = getprotobyname(protocolName.tempCString());
|
|
2667 if (!proto)
|
|
2668 throw new SocketOSException("Unable to find the protocol");
|
|
2669 this(af, type, cast(ProtocolType) proto.p_proto);
|
|
2670 }
|
|
2671
|
|
2672
|
|
2673 /**
|
|
2674 * Create a blocking socket using the parameters from the specified
|
|
2675 * $(D AddressInfo) structure.
|
|
2676 */
|
|
2677 this(in AddressInfo info)
|
|
2678 {
|
|
2679 this(info.family, info.type, info.protocol);
|
|
2680 }
|
|
2681
|
|
2682 /// Use an existing socket handle.
|
|
2683 this(socket_t sock, AddressFamily af) pure nothrow @nogc
|
|
2684 {
|
|
2685 assert(sock != socket_t.init);
|
|
2686 this.sock = sock;
|
|
2687 this._family = af;
|
|
2688 }
|
|
2689
|
|
2690
|
|
2691 ~this() nothrow @nogc
|
|
2692 {
|
|
2693 close();
|
|
2694 }
|
|
2695
|
|
2696
|
|
2697 /// Get underlying socket handle.
|
|
2698 @property socket_t handle() const pure nothrow @nogc
|
|
2699 {
|
|
2700 return sock;
|
|
2701 }
|
|
2702
|
|
2703 /**
|
|
2704 * Get/set socket's blocking flag.
|
|
2705 *
|
|
2706 * When a socket is blocking, calls to receive(), accept(), and send()
|
|
2707 * will block and wait for data/action.
|
|
2708 * A non-blocking socket will immediately return instead of blocking.
|
|
2709 */
|
|
2710 @property bool blocking() @trusted const nothrow @nogc
|
|
2711 {
|
|
2712 version (Windows)
|
|
2713 {
|
|
2714 return _blocking;
|
|
2715 }
|
|
2716 else version (Posix)
|
|
2717 {
|
|
2718 return !(fcntl(handle, F_GETFL, 0) & O_NONBLOCK);
|
|
2719 }
|
|
2720 }
|
|
2721
|
|
2722 /// ditto
|
|
2723 @property void blocking(bool byes) @trusted
|
|
2724 {
|
|
2725 version (Windows)
|
|
2726 {
|
|
2727 uint num = !byes;
|
|
2728 if (_SOCKET_ERROR == ioctlsocket(sock, FIONBIO, &num))
|
|
2729 goto err;
|
|
2730 _blocking = byes;
|
|
2731 }
|
|
2732 else version (Posix)
|
|
2733 {
|
|
2734 int x = fcntl(sock, F_GETFL, 0);
|
|
2735 if (-1 == x)
|
|
2736 goto err;
|
|
2737 if (byes)
|
|
2738 x &= ~O_NONBLOCK;
|
|
2739 else
|
|
2740 x |= O_NONBLOCK;
|
|
2741 if (-1 == fcntl(sock, F_SETFL, x))
|
|
2742 goto err;
|
|
2743 }
|
|
2744 return; // Success.
|
|
2745
|
|
2746 err:
|
|
2747 throw new SocketOSException("Unable to set socket blocking");
|
|
2748 }
|
|
2749
|
|
2750
|
|
2751 /// Get the socket's address family.
|
|
2752 @property AddressFamily addressFamily()
|
|
2753 {
|
|
2754 return _family;
|
|
2755 }
|
|
2756
|
|
2757 /// Property that indicates if this is a valid, alive socket.
|
|
2758 @property bool isAlive() @trusted const
|
|
2759 {
|
|
2760 int type;
|
|
2761 socklen_t typesize = cast(socklen_t) type.sizeof;
|
|
2762 return !getsockopt(sock, SOL_SOCKET, SO_TYPE, cast(char*)&type, &typesize);
|
|
2763 }
|
|
2764
|
|
2765 /// Associate a local address with this socket.
|
|
2766 void bind(Address addr) @trusted
|
|
2767 {
|
|
2768 if (_SOCKET_ERROR == .bind(sock, addr.name, addr.nameLen))
|
|
2769 throw new SocketOSException("Unable to bind socket");
|
|
2770 }
|
|
2771
|
|
2772 /**
|
|
2773 * Establish a connection. If the socket is blocking, connect waits for
|
|
2774 * the connection to be made. If the socket is nonblocking, connect
|
|
2775 * returns immediately and the connection attempt is still in progress.
|
|
2776 */
|
|
2777 void connect(Address to) @trusted
|
|
2778 {
|
|
2779 if (_SOCKET_ERROR == .connect(sock, to.name, to.nameLen))
|
|
2780 {
|
|
2781 int err;
|
|
2782 err = _lasterr();
|
|
2783
|
|
2784 if (!blocking)
|
|
2785 {
|
|
2786 version (Windows)
|
|
2787 {
|
|
2788 if (WSAEWOULDBLOCK == err)
|
|
2789 return;
|
|
2790 }
|
|
2791 else version (Posix)
|
|
2792 {
|
|
2793 if (EINPROGRESS == err)
|
|
2794 return;
|
|
2795 }
|
|
2796 else
|
|
2797 {
|
|
2798 static assert(0);
|
|
2799 }
|
|
2800 }
|
|
2801 throw new SocketOSException("Unable to connect socket", err);
|
|
2802 }
|
|
2803 }
|
|
2804
|
|
2805 /**
|
|
2806 * Listen for an incoming connection. $(D bind) must be called before you
|
|
2807 * can $(D listen). The $(D backlog) is a request of how many pending
|
|
2808 * incoming connections are queued until $(D accept)ed.
|
|
2809 */
|
|
2810 void listen(int backlog) @trusted
|
|
2811 {
|
|
2812 if (_SOCKET_ERROR == .listen(sock, backlog))
|
|
2813 throw new SocketOSException("Unable to listen on socket");
|
|
2814 }
|
|
2815
|
|
2816 /**
|
|
2817 * Called by $(D accept) when a new $(D Socket) must be created for a new
|
|
2818 * connection. To use a derived class, override this method and return an
|
|
2819 * instance of your class. The returned $(D Socket)'s handle must not be
|
|
2820 * set; $(D Socket) has a protected constructor $(D this()) to use in this
|
|
2821 * situation.
|
|
2822 *
|
|
2823 * Override to use a derived class.
|
|
2824 * The returned socket's handle must not be set.
|
|
2825 */
|
|
2826 protected Socket accepting() pure nothrow
|
|
2827 {
|
|
2828 return new Socket;
|
|
2829 }
|
|
2830
|
|
2831 /**
|
|
2832 * Accept an incoming connection. If the socket is blocking, $(D accept)
|
|
2833 * waits for a connection request. Throws $(D SocketAcceptException) if
|
|
2834 * unable to _accept. See $(D accepting) for use with derived classes.
|
|
2835 */
|
|
2836 Socket accept() @trusted
|
|
2837 {
|
|
2838 auto newsock = cast(socket_t).accept(sock, null, null);
|
|
2839 if (socket_t.init == newsock)
|
|
2840 throw new SocketAcceptException("Unable to accept socket connection");
|
|
2841
|
|
2842 Socket newSocket;
|
|
2843 try
|
|
2844 {
|
|
2845 newSocket = accepting();
|
|
2846 assert(newSocket.sock == socket_t.init);
|
|
2847
|
|
2848 newSocket.setSock(newsock);
|
|
2849 version (Windows)
|
|
2850 newSocket._blocking = _blocking; //inherits blocking mode
|
|
2851 newSocket._family = _family; //same family
|
|
2852 }
|
|
2853 catch (Throwable o)
|
|
2854 {
|
|
2855 _close(newsock);
|
|
2856 throw o;
|
|
2857 }
|
|
2858
|
|
2859 return newSocket;
|
|
2860 }
|
|
2861
|
|
2862 /// Disables sends and/or receives.
|
|
2863 void shutdown(SocketShutdown how) @trusted nothrow @nogc
|
|
2864 {
|
|
2865 .shutdown(sock, cast(int) how);
|
|
2866 }
|
|
2867
|
|
2868
|
|
2869 private static void _close(socket_t sock) @system nothrow @nogc
|
|
2870 {
|
|
2871 version (Windows)
|
|
2872 {
|
|
2873 .closesocket(sock);
|
|
2874 }
|
|
2875 else version (Posix)
|
|
2876 {
|
|
2877 .close(sock);
|
|
2878 }
|
|
2879 }
|
|
2880
|
|
2881
|
|
2882 /**
|
|
2883 * Immediately drop any connections and release socket resources.
|
|
2884 * Calling $(D shutdown) before $(D close) is recommended for
|
|
2885 * connection-oriented sockets. The $(D Socket) object is no longer
|
|
2886 * usable after $(D close).
|
|
2887 * Calling shutdown() before this is recommended
|
|
2888 * for connection-oriented sockets.
|
|
2889 */
|
|
2890 void close() @trusted nothrow @nogc
|
|
2891 {
|
|
2892 _close(sock);
|
|
2893 sock = socket_t.init;
|
|
2894 }
|
|
2895
|
|
2896
|
|
2897 /**
|
|
2898 * Returns: the local machine's host name
|
|
2899 */
|
|
2900 static @property string hostName() @trusted // getter
|
|
2901 {
|
|
2902 char[256] result; // Host names are limited to 255 chars.
|
|
2903 if (_SOCKET_ERROR == .gethostname(result.ptr, result.length))
|
|
2904 throw new SocketOSException("Unable to obtain host name");
|
|
2905 return to!string(result.ptr);
|
|
2906 }
|
|
2907
|
|
2908 /// Remote endpoint $(D Address).
|
|
2909 @property Address remoteAddress() @trusted
|
|
2910 {
|
|
2911 Address addr = createAddress();
|
|
2912 socklen_t nameLen = addr.nameLen;
|
|
2913 if (_SOCKET_ERROR == .getpeername(sock, addr.name, &nameLen))
|
|
2914 throw new SocketOSException("Unable to obtain remote socket address");
|
|
2915 addr.setNameLen(nameLen);
|
|
2916 assert(addr.addressFamily == _family);
|
|
2917 return addr;
|
|
2918 }
|
|
2919
|
|
2920 /// Local endpoint $(D Address).
|
|
2921 @property Address localAddress() @trusted
|
|
2922 {
|
|
2923 Address addr = createAddress();
|
|
2924 socklen_t nameLen = addr.nameLen;
|
|
2925 if (_SOCKET_ERROR == .getsockname(sock, addr.name, &nameLen))
|
|
2926 throw new SocketOSException("Unable to obtain local socket address");
|
|
2927 addr.setNameLen(nameLen);
|
|
2928 assert(addr.addressFamily == _family);
|
|
2929 return addr;
|
|
2930 }
|
|
2931
|
|
2932 /**
|
|
2933 * Send or receive error code. See $(D wouldHaveBlocked),
|
|
2934 * $(D lastSocketError) and $(D Socket.getErrorText) for obtaining more
|
|
2935 * information about the error.
|
|
2936 */
|
|
2937 enum int ERROR = _SOCKET_ERROR;
|
|
2938
|
|
2939 private static int capToInt(size_t size) nothrow @nogc
|
|
2940 {
|
|
2941 // Windows uses int instead of size_t for length arguments.
|
|
2942 // Luckily, the send/recv functions make no guarantee that
|
|
2943 // all the data is sent, so we use that to send at most
|
|
2944 // int.max bytes.
|
|
2945 return size > size_t(int.max) ? int.max : cast(int) size;
|
|
2946 }
|
|
2947
|
|
2948 /**
|
|
2949 * Send data on the connection. If the socket is blocking and there is no
|
|
2950 * buffer space left, $(D send) waits.
|
|
2951 * Returns: The number of bytes actually sent, or $(D Socket.ERROR) on
|
|
2952 * failure.
|
|
2953 */
|
|
2954 ptrdiff_t send(const(void)[] buf, SocketFlags flags) @trusted
|
|
2955 {
|
|
2956 static if (is(typeof(MSG_NOSIGNAL)))
|
|
2957 {
|
|
2958 flags = cast(SocketFlags)(flags | MSG_NOSIGNAL);
|
|
2959 }
|
|
2960 version (Windows)
|
|
2961 auto sent = .send(sock, buf.ptr, capToInt(buf.length), cast(int) flags);
|
|
2962 else
|
|
2963 auto sent = .send(sock, buf.ptr, buf.length, cast(int) flags);
|
|
2964 return sent;
|
|
2965 }
|
|
2966
|
|
2967 /// ditto
|
|
2968 ptrdiff_t send(const(void)[] buf)
|
|
2969 {
|
|
2970 return send(buf, SocketFlags.NONE);
|
|
2971 }
|
|
2972
|
|
2973 /**
|
|
2974 * Send data to a specific destination Address. If the destination address is
|
|
2975 * not specified, a connection must have been made and that address is used.
|
|
2976 * If the socket is blocking and there is no buffer space left, $(D sendTo) waits.
|
|
2977 * Returns: The number of bytes actually sent, or $(D Socket.ERROR) on
|
|
2978 * failure.
|
|
2979 */
|
|
2980 ptrdiff_t sendTo(const(void)[] buf, SocketFlags flags, Address to) @trusted
|
|
2981 {
|
|
2982 static if (is(typeof(MSG_NOSIGNAL)))
|
|
2983 {
|
|
2984 flags = cast(SocketFlags)(flags | MSG_NOSIGNAL);
|
|
2985 }
|
|
2986 version (Windows)
|
|
2987 return .sendto(
|
|
2988 sock, buf.ptr, capToInt(buf.length),
|
|
2989 cast(int) flags, to.name, to.nameLen
|
|
2990 );
|
|
2991 else
|
|
2992 return .sendto(sock, buf.ptr, buf.length, cast(int) flags, to.name, to.nameLen);
|
|
2993 }
|
|
2994
|
|
2995 /// ditto
|
|
2996 ptrdiff_t sendTo(const(void)[] buf, Address to)
|
|
2997 {
|
|
2998 return sendTo(buf, SocketFlags.NONE, to);
|
|
2999 }
|
|
3000
|
|
3001
|
|
3002 //assumes you connect()ed
|
|
3003 /// ditto
|
|
3004 ptrdiff_t sendTo(const(void)[] buf, SocketFlags flags) @trusted
|
|
3005 {
|
|
3006 static if (is(typeof(MSG_NOSIGNAL)))
|
|
3007 {
|
|
3008 flags = cast(SocketFlags)(flags | MSG_NOSIGNAL);
|
|
3009 }
|
|
3010 version (Windows)
|
|
3011 return .sendto(sock, buf.ptr, capToInt(buf.length), cast(int) flags, null, 0);
|
|
3012 else
|
|
3013 return .sendto(sock, buf.ptr, buf.length, cast(int) flags, null, 0);
|
|
3014 }
|
|
3015
|
|
3016
|
|
3017 //assumes you connect()ed
|
|
3018 /// ditto
|
|
3019 ptrdiff_t sendTo(const(void)[] buf)
|
|
3020 {
|
|
3021 return sendTo(buf, SocketFlags.NONE);
|
|
3022 }
|
|
3023
|
|
3024
|
|
3025 /**
|
|
3026 * Receive data on the connection. If the socket is blocking, $(D receive)
|
|
3027 * waits until there is data to be received.
|
|
3028 * Returns: The number of bytes actually received, $(D 0) if the remote side
|
|
3029 * has closed the connection, or $(D Socket.ERROR) on failure.
|
|
3030 */
|
|
3031 ptrdiff_t receive(void[] buf, SocketFlags flags) @trusted
|
|
3032 {
|
|
3033 version (Windows) // Does not use size_t
|
|
3034 {
|
|
3035 return buf.length
|
|
3036 ? .recv(sock, buf.ptr, capToInt(buf.length), cast(int) flags)
|
|
3037 : 0;
|
|
3038 }
|
|
3039 else
|
|
3040 {
|
|
3041 return buf.length
|
|
3042 ? .recv(sock, buf.ptr, buf.length, cast(int) flags)
|
|
3043 : 0;
|
|
3044 }
|
|
3045 }
|
|
3046
|
|
3047 /// ditto
|
|
3048 ptrdiff_t receive(void[] buf)
|
|
3049 {
|
|
3050 return receive(buf, SocketFlags.NONE);
|
|
3051 }
|
|
3052
|
|
3053 /**
|
|
3054 * Receive data and get the remote endpoint $(D Address).
|
|
3055 * If the socket is blocking, $(D receiveFrom) waits until there is data to
|
|
3056 * be received.
|
|
3057 * Returns: The number of bytes actually received, $(D 0) if the remote side
|
|
3058 * has closed the connection, or $(D Socket.ERROR) on failure.
|
|
3059 */
|
|
3060 ptrdiff_t receiveFrom(void[] buf, SocketFlags flags, ref Address from) @trusted
|
|
3061 {
|
|
3062 if (!buf.length) //return 0 and don't think the connection closed
|
|
3063 return 0;
|
|
3064 if (from is null || from.addressFamily != _family)
|
|
3065 from = createAddress();
|
|
3066 socklen_t nameLen = from.nameLen;
|
|
3067 version (Windows)
|
|
3068 {
|
|
3069 auto read = .recvfrom(sock, buf.ptr, capToInt(buf.length), cast(int) flags, from.name, &nameLen);
|
|
3070 from.setNameLen(nameLen);
|
|
3071 assert(from.addressFamily == _family);
|
|
3072 // if (!read) //connection closed
|
|
3073 return read;
|
|
3074 }
|
|
3075 else
|
|
3076 {
|
|
3077 auto read = .recvfrom(sock, buf.ptr, buf.length, cast(int) flags, from.name, &nameLen);
|
|
3078 from.setNameLen(nameLen);
|
|
3079 assert(from.addressFamily == _family);
|
|
3080 // if (!read) //connection closed
|
|
3081 return read;
|
|
3082 }
|
|
3083 }
|
|
3084
|
|
3085
|
|
3086 /// ditto
|
|
3087 ptrdiff_t receiveFrom(void[] buf, ref Address from)
|
|
3088 {
|
|
3089 return receiveFrom(buf, SocketFlags.NONE, from);
|
|
3090 }
|
|
3091
|
|
3092
|
|
3093 //assumes you connect()ed
|
|
3094 /// ditto
|
|
3095 ptrdiff_t receiveFrom(void[] buf, SocketFlags flags) @trusted
|
|
3096 {
|
|
3097 if (!buf.length) //return 0 and don't think the connection closed
|
|
3098 return 0;
|
|
3099 version (Windows)
|
|
3100 {
|
|
3101 auto read = .recvfrom(sock, buf.ptr, capToInt(buf.length), cast(int) flags, null, null);
|
|
3102 // if (!read) //connection closed
|
|
3103 return read;
|
|
3104 }
|
|
3105 else
|
|
3106 {
|
|
3107 auto read = .recvfrom(sock, buf.ptr, buf.length, cast(int) flags, null, null);
|
|
3108 // if (!read) //connection closed
|
|
3109 return read;
|
|
3110 }
|
|
3111 }
|
|
3112
|
|
3113
|
|
3114 //assumes you connect()ed
|
|
3115 /// ditto
|
|
3116 ptrdiff_t receiveFrom(void[] buf)
|
|
3117 {
|
|
3118 return receiveFrom(buf, SocketFlags.NONE);
|
|
3119 }
|
|
3120
|
|
3121
|
|
3122 /**
|
|
3123 * Get a socket option.
|
|
3124 * Returns: The number of bytes written to $(D result).
|
|
3125 * The length, in bytes, of the actual result - very different from getsockopt()
|
|
3126 */
|
|
3127 int getOption(SocketOptionLevel level, SocketOption option, void[] result) @trusted
|
|
3128 {
|
|
3129 socklen_t len = cast(socklen_t) result.length;
|
|
3130 if (_SOCKET_ERROR == .getsockopt(sock, cast(int) level, cast(int) option, result.ptr, &len))
|
|
3131 throw new SocketOSException("Unable to get socket option");
|
|
3132 return len;
|
|
3133 }
|
|
3134
|
|
3135
|
|
3136 /// Common case of getting integer and boolean options.
|
|
3137 int getOption(SocketOptionLevel level, SocketOption option, out int32_t result) @trusted
|
|
3138 {
|
|
3139 return getOption(level, option, (&result)[0 .. 1]);
|
|
3140 }
|
|
3141
|
|
3142
|
|
3143 /// Get the linger option.
|
|
3144 int getOption(SocketOptionLevel level, SocketOption option, out Linger result) @trusted
|
|
3145 {
|
|
3146 //return getOption(cast(SocketOptionLevel) SocketOptionLevel.SOCKET, SocketOption.LINGER, (&result)[0 .. 1]);
|
|
3147 return getOption(level, option, (&result.clinger)[0 .. 1]);
|
|
3148 }
|
|
3149
|
|
3150 /// Get a timeout (duration) option.
|
|
3151 void getOption(SocketOptionLevel level, SocketOption option, out Duration result) @trusted
|
|
3152 {
|
|
3153 enforce(option == SocketOption.SNDTIMEO || option == SocketOption.RCVTIMEO,
|
|
3154 new SocketParameterException("Not a valid timeout option: " ~ to!string(option)));
|
|
3155 // WinSock returns the timeout values as a milliseconds DWORD,
|
|
3156 // while Linux and BSD return a timeval struct.
|
|
3157 version (Windows)
|
|
3158 {
|
|
3159 int msecs;
|
|
3160 getOption(level, option, (&msecs)[0 .. 1]);
|
|
3161 if (option == SocketOption.RCVTIMEO)
|
|
3162 msecs += WINSOCK_TIMEOUT_SKEW;
|
|
3163 result = dur!"msecs"(msecs);
|
|
3164 }
|
|
3165 else version (Posix)
|
|
3166 {
|
|
3167 TimeVal tv;
|
|
3168 getOption(level, option, (&tv.ctimeval)[0 .. 1]);
|
|
3169 result = dur!"seconds"(tv.seconds) + dur!"usecs"(tv.microseconds);
|
|
3170 }
|
|
3171 else static assert(false);
|
|
3172 }
|
|
3173
|
|
3174 /// Set a socket option.
|
|
3175 void setOption(SocketOptionLevel level, SocketOption option, void[] value) @trusted
|
|
3176 {
|
|
3177 if (_SOCKET_ERROR == .setsockopt(sock, cast(int) level,
|
|
3178 cast(int) option, value.ptr, cast(uint) value.length))
|
|
3179 throw new SocketOSException("Unable to set socket option");
|
|
3180 }
|
|
3181
|
|
3182
|
|
3183 /// Common case for setting integer and boolean options.
|
|
3184 void setOption(SocketOptionLevel level, SocketOption option, int32_t value) @trusted
|
|
3185 {
|
|
3186 setOption(level, option, (&value)[0 .. 1]);
|
|
3187 }
|
|
3188
|
|
3189
|
|
3190 /// Set the linger option.
|
|
3191 void setOption(SocketOptionLevel level, SocketOption option, Linger value) @trusted
|
|
3192 {
|
|
3193 //setOption(cast(SocketOptionLevel) SocketOptionLevel.SOCKET, SocketOption.LINGER, (&value)[0 .. 1]);
|
|
3194 setOption(level, option, (&value.clinger)[0 .. 1]);
|
|
3195 }
|
|
3196
|
|
3197 /**
|
|
3198 * Sets a timeout (duration) option, i.e. $(D SocketOption.SNDTIMEO) or
|
|
3199 * $(D RCVTIMEO). Zero indicates no timeout.
|
|
3200 *
|
|
3201 * In a typical application, you might also want to consider using
|
|
3202 * a non-blocking socket instead of setting a timeout on a blocking one.
|
|
3203 *
|
|
3204 * Note: While the receive timeout setting is generally quite accurate
|
|
3205 * on *nix systems even for smaller durations, there are two issues to
|
|
3206 * be aware of on Windows: First, although undocumented, the effective
|
|
3207 * timeout duration seems to be the one set on the socket plus half
|
|
3208 * a second. $(D setOption()) tries to compensate for that, but still,
|
|
3209 * timeouts under 500ms are not possible on Windows. Second, be aware
|
|
3210 * that the actual amount of time spent until a blocking call returns
|
|
3211 * randomly varies on the order of 10ms.
|
|
3212 *
|
|
3213 * Params:
|
|
3214 * level = The level at which a socket option is defined.
|
|
3215 * option = Either $(D SocketOption.SNDTIMEO) or $(D SocketOption.RCVTIMEO).
|
|
3216 * value = The timeout duration to set. Must not be negative.
|
|
3217 *
|
|
3218 * Throws: $(D SocketException) if setting the options fails.
|
|
3219 *
|
|
3220 * Example:
|
|
3221 * ---
|
|
3222 * import std.datetime;
|
|
3223 * import std.typecons;
|
|
3224 * auto pair = socketPair();
|
|
3225 * scope(exit) foreach (s; pair) s.close();
|
|
3226 *
|
|
3227 * // Set a receive timeout, and then wait at one end of
|
|
3228 * // the socket pair, knowing that no data will arrive.
|
|
3229 * pair[0].setOption(SocketOptionLevel.SOCKET,
|
|
3230 * SocketOption.RCVTIMEO, dur!"seconds"(1));
|
|
3231 *
|
|
3232 * auto sw = StopWatch(Yes.autoStart);
|
|
3233 * ubyte[1] buffer;
|
|
3234 * pair[0].receive(buffer);
|
|
3235 * writefln("Waited %s ms until the socket timed out.",
|
|
3236 * sw.peek.msecs);
|
|
3237 * ---
|
|
3238 */
|
|
3239 void setOption(SocketOptionLevel level, SocketOption option, Duration value) @trusted
|
|
3240 {
|
|
3241 enforce(option == SocketOption.SNDTIMEO || option == SocketOption.RCVTIMEO,
|
|
3242 new SocketParameterException("Not a valid timeout option: " ~ to!string(option)));
|
|
3243
|
|
3244 enforce(value >= dur!"hnsecs"(0), new SocketParameterException(
|
|
3245 "Timeout duration must not be negative."));
|
|
3246
|
|
3247 version (Windows)
|
|
3248 {
|
|
3249 import std.algorithm.comparison : max;
|
|
3250
|
|
3251 auto msecs = to!int(value.total!"msecs");
|
|
3252 if (msecs != 0 && option == SocketOption.RCVTIMEO)
|
|
3253 msecs = max(1, msecs - WINSOCK_TIMEOUT_SKEW);
|
|
3254 setOption(level, option, msecs);
|
|
3255 }
|
|
3256 else version (Posix)
|
|
3257 {
|
|
3258 _ctimeval tv;
|
|
3259 value.split!("seconds", "usecs")(tv.tv_sec, tv.tv_usec);
|
|
3260 setOption(level, option, (&tv)[0 .. 1]);
|
|
3261 }
|
|
3262 else static assert(false);
|
|
3263 }
|
|
3264
|
|
3265 /**
|
|
3266 * Get a text description of this socket's error status, and clear the
|
|
3267 * socket's error status.
|
|
3268 */
|
|
3269 string getErrorText()
|
|
3270 {
|
|
3271 int32_t error;
|
|
3272 getOption(SocketOptionLevel.SOCKET, SocketOption.ERROR, error);
|
|
3273 return formatSocketError(error);
|
|
3274 }
|
|
3275
|
|
3276 /**
|
|
3277 * Enables TCP keep-alive with the specified parameters.
|
|
3278 *
|
|
3279 * Params:
|
|
3280 * time = Number of seconds with no activity until the first
|
|
3281 * keep-alive packet is sent.
|
|
3282 * interval = Number of seconds between when successive keep-alive
|
|
3283 * packets are sent if no acknowledgement is received.
|
|
3284 *
|
|
3285 * Throws: $(D SocketOSException) if setting the options fails, or
|
|
3286 * $(D SocketFeatureException) if setting keep-alive parameters is
|
|
3287 * unsupported on the current platform.
|
|
3288 */
|
|
3289 void setKeepAlive(int time, int interval) @trusted
|
|
3290 {
|
|
3291 version (Windows)
|
|
3292 {
|
|
3293 tcp_keepalive options;
|
|
3294 options.onoff = 1;
|
|
3295 options.keepalivetime = time * 1000;
|
|
3296 options.keepaliveinterval = interval * 1000;
|
|
3297 uint cbBytesReturned;
|
|
3298 enforce(WSAIoctl(sock, SIO_KEEPALIVE_VALS,
|
|
3299 &options, options.sizeof,
|
|
3300 null, 0,
|
|
3301 &cbBytesReturned, null, null) == 0,
|
|
3302 new SocketOSException("Error setting keep-alive"));
|
|
3303 }
|
|
3304 else
|
|
3305 static if (is(typeof(TCP_KEEPIDLE)) && is(typeof(TCP_KEEPINTVL)))
|
|
3306 {
|
|
3307 setOption(SocketOptionLevel.TCP, cast(SocketOption) TCP_KEEPIDLE, time);
|
|
3308 setOption(SocketOptionLevel.TCP, cast(SocketOption) TCP_KEEPINTVL, interval);
|
|
3309 setOption(SocketOptionLevel.SOCKET, SocketOption.KEEPALIVE, true);
|
|
3310 }
|
|
3311 else
|
|
3312 throw new SocketFeatureException("Setting keep-alive options " ~
|
|
3313 "is not supported on this platform");
|
|
3314 }
|
|
3315
|
|
3316 /**
|
|
3317 * Wait for a socket to change status. A wait timeout of $(REF Duration, core, time) or
|
|
3318 * $(D TimeVal), may be specified; if a timeout is not specified or the
|
|
3319 * $(D TimeVal) is $(D null), the maximum timeout is used. The $(D TimeVal)
|
|
3320 * timeout has an unspecified value when $(D select) returns.
|
|
3321 * Returns: The number of sockets with status changes, $(D 0) on timeout,
|
|
3322 * or $(D -1) on interruption. If the return value is greater than $(D 0),
|
|
3323 * the $(D SocketSets) are updated to only contain the sockets having status
|
|
3324 * changes. For a connecting socket, a write status change means the
|
|
3325 * connection is established and it's able to send. For a listening socket,
|
|
3326 * a read status change means there is an incoming connection request and
|
|
3327 * it's able to accept.
|
|
3328 *
|
|
3329 * `SocketSet`'s updated to include only those sockets which an event occured.
|
|
3330 * For a `connect()`ing socket, writeability means connected.
|
|
3331 * For a `listen()`ing socket, readability means listening
|
|
3332 * `Winsock`; possibly internally limited to 64 sockets per set.
|
|
3333 *
|
|
3334 * Returns:
|
|
3335 * the number of events, 0 on timeout, or -1 on interruption
|
|
3336 */
|
|
3337 static int select(SocketSet checkRead, SocketSet checkWrite, SocketSet checkError, Duration timeout) @trusted
|
|
3338 {
|
|
3339 auto vals = timeout.split!("seconds", "usecs")();
|
|
3340 TimeVal tv;
|
|
3341 tv.seconds = cast(tv.tv_sec_t ) vals.seconds;
|
|
3342 tv.microseconds = cast(tv.tv_usec_t) vals.usecs;
|
|
3343 return select(checkRead, checkWrite, checkError, &tv);
|
|
3344 }
|
|
3345
|
|
3346 /// ditto
|
|
3347 //maximum timeout
|
|
3348 static int select(SocketSet checkRead, SocketSet checkWrite, SocketSet checkError)
|
|
3349 {
|
|
3350 return select(checkRead, checkWrite, checkError, null);
|
|
3351 }
|
|
3352
|
|
3353 /// Ditto
|
|
3354 static int select(SocketSet checkRead, SocketSet checkWrite, SocketSet checkError, TimeVal* timeout) @trusted
|
|
3355 in
|
|
3356 {
|
|
3357 //make sure none of the SocketSet's are the same object
|
|
3358 if (checkRead)
|
|
3359 {
|
|
3360 assert(checkRead !is checkWrite);
|
|
3361 assert(checkRead !is checkError);
|
|
3362 }
|
|
3363 if (checkWrite)
|
|
3364 {
|
|
3365 assert(checkWrite !is checkError);
|
|
3366 }
|
|
3367 }
|
|
3368 body
|
|
3369 {
|
|
3370 fd_set* fr, fw, fe;
|
|
3371 int n = 0;
|
|
3372
|
|
3373 version (Windows)
|
|
3374 {
|
|
3375 // Windows has a problem with empty fd_set`s that aren't null.
|
|
3376 fr = checkRead && checkRead.count ? checkRead.toFd_set() : null;
|
|
3377 fw = checkWrite && checkWrite.count ? checkWrite.toFd_set() : null;
|
|
3378 fe = checkError && checkError.count ? checkError.toFd_set() : null;
|
|
3379 }
|
|
3380 else
|
|
3381 {
|
|
3382 if (checkRead)
|
|
3383 {
|
|
3384 fr = checkRead.toFd_set();
|
|
3385 n = checkRead.selectn();
|
|
3386 }
|
|
3387 else
|
|
3388 {
|
|
3389 fr = null;
|
|
3390 }
|
|
3391
|
|
3392 if (checkWrite)
|
|
3393 {
|
|
3394 fw = checkWrite.toFd_set();
|
|
3395 int _n;
|
|
3396 _n = checkWrite.selectn();
|
|
3397 if (_n > n)
|
|
3398 n = _n;
|
|
3399 }
|
|
3400 else
|
|
3401 {
|
|
3402 fw = null;
|
|
3403 }
|
|
3404
|
|
3405 if (checkError)
|
|
3406 {
|
|
3407 fe = checkError.toFd_set();
|
|
3408 int _n;
|
|
3409 _n = checkError.selectn();
|
|
3410 if (_n > n)
|
|
3411 n = _n;
|
|
3412 }
|
|
3413 else
|
|
3414 {
|
|
3415 fe = null;
|
|
3416 }
|
|
3417
|
|
3418 // Make sure the sets' capacity matches, to avoid select reading
|
|
3419 // out of bounds just because one set was bigger than another
|
|
3420 if (checkRead ) checkRead .setMinCapacity(n);
|
|
3421 if (checkWrite) checkWrite.setMinCapacity(n);
|
|
3422 if (checkError) checkError.setMinCapacity(n);
|
|
3423 }
|
|
3424
|
|
3425 int result = .select(n, fr, fw, fe, &timeout.ctimeval);
|
|
3426
|
|
3427 version (Windows)
|
|
3428 {
|
|
3429 if (_SOCKET_ERROR == result && WSAGetLastError() == WSAEINTR)
|
|
3430 return -1;
|
|
3431 }
|
|
3432 else version (Posix)
|
|
3433 {
|
|
3434 if (_SOCKET_ERROR == result && errno == EINTR)
|
|
3435 return -1;
|
|
3436 }
|
|
3437 else
|
|
3438 {
|
|
3439 static assert(0);
|
|
3440 }
|
|
3441
|
|
3442 if (_SOCKET_ERROR == result)
|
|
3443 throw new SocketOSException("Socket select error");
|
|
3444
|
|
3445 return result;
|
|
3446 }
|
|
3447
|
|
3448
|
|
3449 /**
|
|
3450 * Can be overridden to support other addresses.
|
|
3451 * Returns: a new `Address` object for the current address family.
|
|
3452 */
|
|
3453 protected Address createAddress() pure nothrow
|
|
3454 {
|
|
3455 Address result;
|
|
3456 switch (_family)
|
|
3457 {
|
|
3458 static if (is(sockaddr_un))
|
|
3459 {
|
|
3460 case AddressFamily.UNIX:
|
|
3461 result = new UnixAddress;
|
|
3462 break;
|
|
3463 }
|
|
3464
|
|
3465 case AddressFamily.INET:
|
|
3466 result = new InternetAddress;
|
|
3467 break;
|
|
3468
|
|
3469 case AddressFamily.INET6:
|
|
3470 result = new Internet6Address;
|
|
3471 break;
|
|
3472
|
|
3473 default:
|
|
3474 result = new UnknownAddress;
|
|
3475 }
|
|
3476 return result;
|
|
3477 }
|
|
3478
|
|
3479 }
|
|
3480
|
|
3481
|
|
3482 /// $(D TcpSocket) is a shortcut class for a TCP Socket.
|
|
3483 class TcpSocket: Socket
|
|
3484 {
|
|
3485 /// Constructs a blocking TCP Socket.
|
|
3486 this(AddressFamily family)
|
|
3487 {
|
|
3488 super(family, SocketType.STREAM, ProtocolType.TCP);
|
|
3489 }
|
|
3490
|
|
3491 /// Constructs a blocking IPv4 TCP Socket.
|
|
3492 this()
|
|
3493 {
|
|
3494 this(AddressFamily.INET);
|
|
3495 }
|
|
3496
|
|
3497
|
|
3498 //shortcut
|
|
3499 /// Constructs a blocking TCP Socket and connects to an $(D Address).
|
|
3500 this(Address connectTo)
|
|
3501 {
|
|
3502 this(connectTo.addressFamily);
|
|
3503 connect(connectTo);
|
|
3504 }
|
|
3505 }
|
|
3506
|
|
3507
|
|
3508 /// $(D UdpSocket) is a shortcut class for a UDP Socket.
|
|
3509 class UdpSocket: Socket
|
|
3510 {
|
|
3511 /// Constructs a blocking UDP Socket.
|
|
3512 this(AddressFamily family)
|
|
3513 {
|
|
3514 super(family, SocketType.DGRAM, ProtocolType.UDP);
|
|
3515 }
|
|
3516
|
|
3517
|
|
3518 /// Constructs a blocking IPv4 UDP Socket.
|
|
3519 this()
|
|
3520 {
|
|
3521 this(AddressFamily.INET);
|
|
3522 }
|
|
3523 }
|
|
3524
|
|
3525 // Issue 16514
|
|
3526 @safe unittest
|
|
3527 {
|
|
3528 class TestSocket : Socket
|
|
3529 {
|
|
3530 override
|
|
3531 {
|
|
3532 const pure nothrow @nogc @property @safe socket_t handle() { assert(0); }
|
|
3533 const nothrow @nogc @property @trusted bool blocking() { assert(0); }
|
|
3534 @property @trusted void blocking(bool byes) { assert(0); }
|
|
3535 @property @safe AddressFamily addressFamily() { assert(0); }
|
|
3536 const @property @trusted bool isAlive() { assert(0); }
|
|
3537 @trusted void bind(Address addr) { assert(0); }
|
|
3538 @trusted void connect(Address to) { assert(0); }
|
|
3539 @trusted void listen(int backlog) { assert(0); }
|
|
3540 protected pure nothrow @safe Socket accepting() { assert(0); }
|
|
3541 @trusted Socket accept() { assert(0); }
|
|
3542 nothrow @nogc @trusted void shutdown(SocketShutdown how) { assert(0); }
|
|
3543 nothrow @nogc @trusted void close() { assert(0); }
|
|
3544 @property @trusted Address remoteAddress() { assert(0); }
|
|
3545 @property @trusted Address localAddress() { assert(0); }
|
|
3546 @trusted ptrdiff_t send(const(void)[] buf, SocketFlags flags) { assert(0); }
|
|
3547 @safe ptrdiff_t send(const(void)[] buf) { assert(0); }
|
|
3548 @trusted ptrdiff_t sendTo(const(void)[] buf, SocketFlags flags, Address to) { assert(0); }
|
|
3549 @safe ptrdiff_t sendTo(const(void)[] buf, Address to) { assert(0); }
|
|
3550 @trusted ptrdiff_t sendTo(const(void)[] buf, SocketFlags flags) { assert(0); }
|
|
3551 @safe ptrdiff_t sendTo(const(void)[] buf) { assert(0); }
|
|
3552 @trusted ptrdiff_t receive(void[] buf, SocketFlags flags) { assert(0); }
|
|
3553 @safe ptrdiff_t receive(void[] buf) { assert(0); }
|
|
3554 @trusted ptrdiff_t receiveFrom(void[] buf, SocketFlags flags, ref Address from) { assert(0); }
|
|
3555 @safe ptrdiff_t receiveFrom(void[] buf, ref Address from) { assert(0); }
|
|
3556 @trusted ptrdiff_t receiveFrom(void[] buf, SocketFlags flags) { assert(0); }
|
|
3557 @safe ptrdiff_t receiveFrom(void[] buf) { assert(0); }
|
|
3558 @trusted int getOption(SocketOptionLevel level, SocketOption option, void[] result) { assert(0); }
|
|
3559 @trusted int getOption(SocketOptionLevel level, SocketOption option, out int32_t result) { assert(0); }
|
|
3560 @trusted int getOption(SocketOptionLevel level, SocketOption option, out Linger result) { assert(0); }
|
|
3561 @trusted void getOption(SocketOptionLevel level, SocketOption option, out Duration result) { assert(0); }
|
|
3562 @trusted void setOption(SocketOptionLevel level, SocketOption option, void[] value) { assert(0); }
|
|
3563 @trusted void setOption(SocketOptionLevel level, SocketOption option, int32_t value) { assert(0); }
|
|
3564 @trusted void setOption(SocketOptionLevel level, SocketOption option, Linger value) { assert(0); }
|
|
3565 @trusted void setOption(SocketOptionLevel level, SocketOption option, Duration value) { assert(0); }
|
|
3566 @safe string getErrorText() { assert(0); }
|
|
3567 @trusted void setKeepAlive(int time, int interval) { assert(0); }
|
|
3568 protected pure nothrow @safe Address createAddress() { assert(0); }
|
|
3569 }
|
|
3570 }
|
|
3571 }
|
|
3572
|
|
3573 /**
|
|
3574 * Creates a pair of connected sockets.
|
|
3575 *
|
|
3576 * The two sockets are indistinguishable.
|
|
3577 *
|
|
3578 * Throws: $(D SocketException) if creation of the sockets fails.
|
|
3579 */
|
|
3580 Socket[2] socketPair() @trusted
|
|
3581 {
|
|
3582 version (Posix)
|
|
3583 {
|
|
3584 int[2] socks;
|
|
3585 if (socketpair(AF_UNIX, SOCK_STREAM, 0, socks) == -1)
|
|
3586 throw new SocketOSException("Unable to create socket pair");
|
|
3587
|
|
3588 Socket toSocket(size_t id)
|
|
3589 {
|
|
3590 auto s = new Socket;
|
|
3591 s.setSock(cast(socket_t) socks[id]);
|
|
3592 s._family = AddressFamily.UNIX;
|
|
3593 return s;
|
|
3594 }
|
|
3595
|
|
3596 return [toSocket(0), toSocket(1)];
|
|
3597 }
|
|
3598 else version (Windows)
|
|
3599 {
|
|
3600 // We do not have socketpair() on Windows, just manually create a
|
|
3601 // pair of sockets connected over some localhost port.
|
|
3602 Socket[2] result;
|
|
3603
|
|
3604 auto listener = new TcpSocket();
|
|
3605 listener.setOption(SocketOptionLevel.SOCKET, SocketOption.REUSEADDR, true);
|
|
3606 listener.bind(new InternetAddress(INADDR_LOOPBACK, InternetAddress.PORT_ANY));
|
|
3607 auto addr = listener.localAddress;
|
|
3608 listener.listen(1);
|
|
3609
|
|
3610 result[0] = new TcpSocket(addr);
|
|
3611 result[1] = listener.accept();
|
|
3612
|
|
3613 listener.close();
|
|
3614 return result;
|
|
3615 }
|
|
3616 else
|
|
3617 static assert(false);
|
|
3618 }
|
|
3619
|
|
3620 ///
|
|
3621 @safe unittest
|
|
3622 {
|
|
3623 immutable ubyte[] data = [1, 2, 3, 4];
|
|
3624 auto pair = socketPair();
|
|
3625 scope(exit) foreach (s; pair) s.close();
|
|
3626
|
|
3627 pair[0].send(data);
|
|
3628
|
|
3629 auto buf = new ubyte[data.length];
|
|
3630 pair[1].receive(buf);
|
|
3631 assert(buf == data);
|
|
3632 }
|