1 /*******************************************************************************
2 3 IP socket that memorises its address
4 5 TODO: Add an AddressIPSocket subclass with host resolution.
6 7 Copyright:
8 Copyright (c) 2009-2016 dunnhumby Germany GmbH.
9 All rights reserved.
10 11 License:
12 Boost Software License Version 1.0. See LICENSE_BOOST.txt for details.
13 Alternatively, this file may be distributed under the terms of the Tango
14 3-Clause BSD License (see LICENSE_BSD.txt for details).
15 16 *******************************************************************************/17 18 moduleocean.sys.socket.AddressIPSocket;
19 20 importocean.sys.socket.model.IAddressIPSocketInfo;
21 22 importocean.sys.socket.IPSocket,
23 ocean.sys.socket.InetAddress,
24 ocean.sys.socket.AddrInfo;
25 26 importocean.meta.types.Qualifiers;
27 28 importocean.io.device.Conduit: ISelectable;
29 30 importcore.stdc.string: strlen;
31 32 33 /******************************************************************************
34 35 IP socket class, memorises the address most recently passed to bind() or
36 connect() or obtained by accept().
37 38 Params:
39 IPv6 = true: use IPv6, false: use IPv4
40 41 ******************************************************************************/42 43 classAddressIPSocket ( boolIPv6 = false ) : IPSocket!(IPv6), IAddressIPSocketInfo44 {
45 /**************************************************************************
46 47 Internet address
48 49 **************************************************************************/50 51 privateInetAddressin_address;
52 53 /**************************************************************************
54 55 Internet address string buffer, passed to inet_pton()/inet_ntop().
56 57 **************************************************************************/58 59 privatechar[in_address.addrstrlen] ip_address_;
60 61 /**************************************************************************
62 63 Number of valid characters in address_.
64 65 **************************************************************************/66 67 privatesize_tip_address_len = 0;
68 69 /**************************************************************************
70 71 Obtains the IP address most recently passed to bind() or connect() or
72 obtained by accept().
73 74 Returns:
75 the current IP address.
76 77 **************************************************************************/78 79 publiccstringaddress ( )
80 {
81 returnthis.ip_address_[0 .. this.ip_address_len];
82 }
83 84 /***************************************************************************
85 86 Asks the operating system what address & port this socket is actually
87 bound to and updates the internal buffers accordingly.
88 89 This can be useful when you bind to port 0 which means that the OS will
90 choose a random port for you and you want to find out which one was
91 chosen.
92 93 Returns:
94 0 on success, -1 on failure
95 96 ***************************************************************************/97 98 publicintupdateAddress ( )
99 {
100 returnthis.getsockname(this.in_address.addr);
101 }
102 103 /**************************************************************************
104 105 Obtains the port number most recently passed to bind() or connect() or
106 obtained by accept().
107 108 Returns:
109 the current port number.
110 111 **************************************************************************/112 113 publicushortport ( )
114 {
115 returnthis.in_address.port;
116 }
117 118 /***************************************************************************
119 120 Returns:
121 true if a client connection is currently established or false if
122 not.
123 124 ***************************************************************************/125 126 publicboolconnected ( )
127 {
128 returnthis.fileHandle >= 0;
129 }
130 131 /***************************************************************************
132 133 IAddressIPSocketInfo interface method. Wrapper for method implemented by
134 super class.
135 136 Returns:
137 I/O device instance (file descriptor under linux)
138 139 ***************************************************************************/140 141 publicoverrideHandlefileHandle ( )
142 {
143 returnsuper.fileHandle();
144 }
145 146 /**************************************************************************
147 148 Obtains the address most recently passed to bind() or connect() or
149 obtained by accept().
150 151 Returns:
152 the current address.
153 154 **************************************************************************/155 156 publicInAddrin_addr ( )
157 {
158 returnthis.in_address.addr;
159 }
160 161 /**************************************************************************
162 163 Assigns a local address and optionally a port to this socket and
164 memorises address and port. This socket needs to have been created by
165 socket().
166 167 Params:
168 local_ip_address = local IP address
169 local_port = local port or 0 to use the wildcard "any" port
170 171 Returns:
172 0 on success or -1 on failure. On failure errno is set
173 appropriately.
174 175 Errors:
176 as the overridden method but also sets errno to EINVAL if the
177 address does not contain a valid IP address string.
178 179 **************************************************************************/180 181 publicoverrideintbind ( cstringlocal_ip_address, ushortlocal_port = 0 )
182 {
183 if (autoa = this.in_address(local_ip_address, local_port))
184 {
185 scope (exit) this.setAddress();
186 returnsuper.bind(a);
187 }
188 else189 {
190 return -1;
191 }
192 }
193 194 /**************************************************************************
195 196 Assigns the wildcard "any" local address and optionally a port to this
197 socket and memorises address and port. This socket needs to have been
198 created by socket().
199 200 Params:
201 local_port = local port or 0 to use the wildcard "any" port
202 203 Returns:
204 0 on success or -1 on failure. On failure errno is set
205 appropriately.
206 207 **************************************************************************/208 209 publicoverrideintbind ( ushortlocal_port )
210 {
211 scope (exit) this.setAddress();
212 213 returnsuper.bind(this.in_address(local_port));
214 }
215 216 /**************************************************************************
217 218 Connects this socket the specified address and port and memorises
219 address and port. This socket needs to have been created by socket().
220 221 Params:
222 remote_ip_address = remote IP address
223 remote_port = remote port
224 225 Returns:
226 0 on success or -1 on failure. On failure errno is set
227 appropriately.
228 229 Errors:
230 as the overridden method but also sets errno to EINVAL if the
231 address does not contain a valid IP address string.
232 233 **************************************************************************/234 235 publicoverrideintconnect ( cstringremote_ip_address, ushortremote_port )
236 {
237 if (autoa = this.in_address(remote_ip_address, remote_port))
238 {
239 scope (exit) this.setAddress();
240 returnsuper.connect(a);
241 }
242 else243 {
244 return -1;
245 }
246 }
247 248 /**************************************************************************
249 250 Connects this socket the specified address memorises and it. This socket
251 needs to have been created by socket().
252 253 Params:
254 remote_address = remote address
255 256 Returns:
257 0 on success or -1 on failure. On failure errno is set
258 appropriately.
259 260 Errors:
261 as above.
262 263 **************************************************************************/264 265 publicoverrideintconnect ( InAddrremote_address )
266 {
267 scope (exit) this.setAddress();
268 269 returnsuper.connect(this.in_address = remote_address);
270 }
271 272 /**************************************************************************
273 274 Calls accept() to accept a connection from a listening socket, sets
275 this.fd to the accepted socket file descriptor and memorises the remote
276 address and port.
277 278 Params:
279 listening_socket = the listening socket to accept the new connection
280 from
281 remote_address = filled in with the address of the peer socket, as
282 known to the communications layer
283 flags = socket flags, see description above
284 285 Returns:
286 the file descriptor of the accepted socket on success or -1 on
287 failure. On failure errno is set appropriately.
288 289 **************************************************************************/290 291 publicoverrideintaccept ( ISelectablelistening_socket,
292 refInAddrremote_address,
293 SocketFlagsflags = SocketFlags.None )
294 {
295 scope (exit) this.setAddress(remote_address);
296 297 returnsuper.accept(listening_socket, remote_address, flags);
298 }
299 300 /**************************************************************************
301 302 Calls accept() to accept a connection from a listening socket, sets
303 this.fd to the accepted socket file descriptor and memorises the remote
304 address and port.
305 306 Params:
307 listening_socket = the listening socket to accept the new connection
308 from
309 remote_address = filled in with the address of the peer socket, as
310 known to the communications layer
311 nonblocking = true: make the accepted socket nonblocking
312 false: leave it blocking
313 314 Returns:
315 the file descriptor of the accepted socket on success or -1 on
316 failure. On failure errno is set appropriately.
317 318 **************************************************************************/319 320 publicoverrideintaccept ( ISelectablelistening_socket,
321 refInAddrremote_address, boolnonblocking )
322 {
323 scope (exit) this.setAddress(remote_address);
324 325 returnsuper.accept(listening_socket, remote_address, nonblocking);
326 }
327 328 329 /**************************************************************************
330 331 Calls accept() to accept a connection from a listening socket, sets
332 this.fd to the accepted socket file descriptor and memorises the remote
333 address and port.
334 335 Params:
336 listening_socket = the listening socket to accept the new connection
337 from
338 flags = socket flags, see description above
339 340 Returns:
341 the file descriptor of the accepted socket on success or -1 on
342 failure. On failure errno is set appropriately.
343 344 **************************************************************************/345 346 publicoverrideintaccept ( ISelectablelistening_socket,
347 SocketFlagsflags = SocketFlags.init )
348 {
349 scope (exit) this.setAddress();
350 351 returnsuper.accept(listening_socket, this.in_address.addr, flags);
352 }
353 354 /**************************************************************************
355 356 Calls accept() to accept a connection from a listening socket, sets
357 this.fd to the accepted socket file descriptor and memorises the remote
358 address and port.
359 360 Params:
361 listening_socket = the listening socket to accept the new connection
362 from
363 nonblocking = true: make the accepted socket nonblocking,
364 false: leave it blocking
365 366 Returns:
367 the file descriptor of the accepted socket on success or -1 on
368 failure. On failure errno is set appropriately.
369 370 **************************************************************************/371 372 373 publicoverrideintaccept ( ISelectablelistening_socket,
374 boolnonblocking )
375 {
376 scope (exit) this.setAddress();
377 378 returnsuper.accept(listening_socket,
379 this.in_address.addr, nonblocking);
380 }
381 382 /**************************************************************************
383 384 Sets this.in_address to address and updates this.ip_address_.
385 386 Params:
387 address = input address
388 389 **************************************************************************/390 391 privatevoidsetAddress ( InAddraddress )
392 {
393 this.in_address = address;
394 this.setAddress();
395 }
396 397 /**************************************************************************
398 399 Updates this.ip_address_.
400 401 **************************************************************************/402 403 privatevoidsetAddress ( )
404 {
405 this.ip_address_len = this.in_address.inet_ntop(this.ip_address_).length;
406 }
407 }
408 409 // Test that methods that accept IP addresses in presentation notation410 // correctly accept valid and reject invalid addresses.411 412 version (unittest)
413 {
414 importocean.core.Test;
415 importcore.stdc.errno: errno, EINVAL, EBADF;
416 }
417 418 unittest419 {
420 {
421 scopes = newAddressIPSocket!();
422 // With s.fd == -1 the connect(2) call should fail with EBADF if the423 // inet_pton(3) call succeeds.424 test(s.fd == -1);
425 426 errno = 0;
427 test!("==")(s.bind("127.0.0.1", 12345), -1);
428 test!("==")(errno, EBADF);
429 test!("==")(s.address, "127.0.0.1");
430 test!("==")(s.port, 12345);
431 432 errno = 0;
433 test!("==")(s.bind("Hello World!", 6789), -1);
434 test!("==")(errno, EINVAL);
435 test!("==")(s.address, "127.0.0.1");
436 test!("==")(s.port, 12345);
437 438 errno = 0;
439 test!("==")(s.connect("127.0.0.2", 9876), -1);
440 test!("==")(errno, EBADF);
441 test!("==")(s.address, "127.0.0.2");
442 test!("==")(s.port, 9876);
443 444 errno = 0;
445 test!("==")(s.connect("Hello World!", 54321), -1);
446 test!("==")(errno, EINVAL);
447 test!("==")(s.address, "127.0.0.2");
448 test!("==")(s.port, 9876);
449 }
450 451 {
452 scopes = newAddressIPSocket!(true);
453 test(s.fd == -1);
454 455 errno = 0;
456 // IP address taken from the Linux manual page for inet_pton(3).457 test!("==")(s.bind("::ffff:204.152.189.116", 12345), -1);
458 test!("==")(errno, EBADF);
459 test!("==")(s.address, "::ffff:204.152.189.116");
460 test!("==")(s.port, 12345);
461 462 errno = 0;
463 test!("==")(s.bind("Hello World!", 6789), -1);
464 test!("==")(errno, EINVAL);
465 test!("==")(s.address, "::ffff:204.152.189.116");
466 test!("==")(s.port, 12345);
467 468 errno = 0;
469 test!("==")(s.connect("::ffff:204.152.189.117", 9876), -1);
470 test!("==")(errno, EBADF);
471 test!("==")(s.address, "::ffff:204.152.189.117");
472 test!("==")(s.port, 9876);
473 474 errno = 0;
475 test!("==")(s.connect("Hello World!", 54321), -1);
476 test!("==")(errno, EINVAL);
477 test!("==")(s.address, "::ffff:204.152.189.117");
478 test!("==")(s.port, 9876);
479 }
480 }