1 /******************************************************************************* 2 3 Internet address handling and conversion heper. 4 5 Wraps a sockaddr_in or sockaddr_in6 struct instance together with the IP 6 address network <-> presentation and port number network <-> host conversion 7 functions. 8 9 Aliases the following types and constants from <netinet/in.h>: 10 11 Type IPv4 name IPv6 name Alias name 12 13 struct sockaddr_in sockaddr_in6 Addr 14 const int INET_ADDRSTRLEN INET6_ADDRSTRLEN addrstrlen 15 const sa_family_t AF_INET AF_INET6 family 16 17 Uses the following functions from <arpa/inet.h>: 18 - inet_ntop, inet_pton (IP address network <-> presentation conversion), 19 - htons, ntohs, htonl (port number network <-> host conversion). 20 21 For the whole misery see 22 23 http://pubs.opengroup.org/onlinepubs/009604499/basedefs/netinet/in.h.html 24 http://pubs.opengroup.org/onlinepubs/009604499/functions/inet_ntop.html 25 http://pubs.opengroup.org/onlinepubs/009604499/functions/ntohs.html 26 http://www.openisbn.com/isbn/0131411551/ 27 28 Important note: To reinitialise the address, use clear() (do not assign 29 .init). 30 31 Copyright: 32 Copyright (c) 2009-2016 dunnhumby Germany GmbH. 33 All rights reserved. 34 35 License: 36 Boost Software License Version 1.0. See LICENSE_BOOST.txt for details. 37 Alternatively, this file may be distributed under the terms of the Tango 38 3-Clause BSD License (see LICENSE_BSD.txt for details). 39 40 *******************************************************************************/ 41 42 module ocean.sys.socket.InetAddress; 43 44 45 46 import ocean.transition; 47 48 import ocean.stdc.posix.sys.socket: sockaddr; 49 50 import core.sys.posix.netinet.in_ : 51 sockaddr_in, AF_INET, INET_ADDRSTRLEN, INADDR_ANY, 52 sockaddr_in6, AF_INET6, INET6_ADDRSTRLEN; 53 54 import core.sys.posix.netdb; 55 56 import core.sys.posix.arpa.inet: inet_ntop, inet_pton, ntohs, htons, htonl; 57 58 import core.stdc.string: strlen; 59 60 import core.stdc.errno: errno, EINVAL; 61 62 import ocean.core.TypeConvert; 63 64 import ocean.core.Verify; 65 66 /****************************************************************************** 67 68 Flags supported by getnameinfo(). 69 70 ******************************************************************************/ 71 72 public enum GetNameInfoFlags : int 73 { 74 None = 0, 75 NI_NUMERICHOST = 1 << 0, /// Don't try to look up hostname. 76 NI_NUMERICSERV = 1 << 1, /// Don't convert port number to name. 77 NI_NOFQDN = 1 << 2, /// Only return nodename portion. 78 NI_NAMEREQD = 1 << 3, /// Don't return numeric addresses. 79 NI_DGRAM = 1 << 4, /// Look up UDP service rather than TCP. 80 NI_IDN = 1 << 5, /// Convert name from IDN format. 81 NI_IDN_ALLOW_UNASSIGNED = 1 << 6, /// Don't reject unassigned Unicode code points. 82 NI_IDN_USE_STD3_ASCII_RULES = 1 << 7 /// Validate strings according to STD3 rules. 83 } 84 85 /******************************************************************************/ 86 87 struct InetAddress ( bool IPv6 = false ) 88 { 89 /************************************************************************** 90 91 Constants and aliases. 92 93 - Addr aliases the "sin" internet address struct type, sockaddr_in 94 for IPv4 or sockaddr_in6 for IPv6, respectively. 95 - addrstrlen is the maximum length of a presentation address string 96 plus one (for the NUL-terminator). 97 - family is the address family identifier. 98 - addr_init is the initial value of an Addr instance with the family 99 field set appropriately. 100 101 **************************************************************************/ 102 103 static if (IPv6) 104 { 105 alias sockaddr_in6 Addr; 106 107 enum addrstrlen = INET6_ADDRSTRLEN, 108 family = AF_INET6; 109 110 enum Addr addr_init = {sin6_family: family}; 111 } 112 else 113 { 114 alias sockaddr_in Addr; 115 116 enum addrstrlen = INET_ADDRSTRLEN, 117 family = AF_INET; 118 119 enum Addr addr_init = {sin_family: family}; 120 } 121 122 /************************************************************************** 123 124 Internet address struct instance. 125 The address and port fields should be accessed by port() and 126 inet_pton()/inet_ntop() unless they are copied from another instance. 127 128 **************************************************************************/ 129 130 Addr addr = addr_init; 131 132 /************************************************************************** 133 134 Gets the port number from this.addr. 135 136 Returns: 137 the port number. 138 139 **************************************************************************/ 140 141 ushort port ( ) 142 { 143 static if (IPv6) 144 { 145 return .ntohs((&this).addr.sin6_port); 146 } 147 else 148 { 149 return .ntohs((&this).addr.sin_port); 150 } 151 } 152 153 /************************************************************************** 154 155 Sets the port field (sin_port/sin6_port) of this.addr to p. 156 157 Params: 158 p = port 159 160 Returns: 161 p. 162 163 **************************************************************************/ 164 165 ushort port ( ushort p ) 166 { 167 static if (IPv6) 168 { 169 (&this).addr.sin6_port = .htons(p); 170 171 } 172 else 173 { 174 (&this).addr.sin_port = .htons(p); 175 } 176 177 return p; 178 } 179 180 /************************************************************************** 181 182 Sets the address field (sin_addr/sin6_addr) of this.addr to the address 183 represented by the string in src. src is expected to contain a valid IP 184 address. 185 186 Params: 187 ip_address_str = input IP address 188 189 Returns: 190 1 on success or 0 if src does not contain a valid IP address. 191 192 **************************************************************************/ 193 194 int inet_pton ( cstring ip_address_str ) 195 { 196 if (ip_address_str.length < (&this).addrstrlen) 197 { 198 char[(&this).addrstrlen] nultermbuf; 199 200 nultermbuf[0 .. ip_address_str.length] = ip_address_str[]; 201 nultermbuf[ip_address_str.length] = '\0'; 202 203 return (&this).inet_pton(nultermbuf.ptr); 204 } 205 else 206 { 207 return 0; 208 } 209 210 } 211 212 /************************************************************************** 213 214 Sets the address field (sin_addr/sin6_addr) of this.addr to the address 215 in ip_address_str. ip_address_str is expected be a NUL-terminated 216 string. 217 218 Params: 219 ip_address_str = input IP address string 220 221 Returns: 222 1 on success or 0 if src does not contain a valid IP address. 223 224 (Note: The inet_pton() specs say it can return -1 with 225 errno = EAFNOSUPPORT; this is not possible unless there is a bug in this 226 struct template ;) 227 228 **************************************************************************/ 229 230 int inet_pton ( in char* ip_address_str ) 231 { 232 return .inet_pton((&this).family, ip_address_str, (&this).address_n.ptr); 233 } 234 235 /************************************************************************** 236 237 Sets this.addr to the wildcard "any" IP address. 238 239 Returns: 240 241 242 **************************************************************************/ 243 244 sockaddr* setAddressAny ( ) 245 { 246 static if (IPv6) 247 { 248 (&this).addr.sin6_addr = (&this).addr.sin6_addr.init; 249 } 250 else 251 { 252 (&this).addr.sin_addr.s_addr = htonl(INADDR_ANY); 253 } 254 255 return cast (sockaddr*) &(&this).addr; 256 } 257 258 /************************************************************************** 259 260 Renders the current address of this.addr as an IP address string, 261 writing to dst. dst.length is expected to be at least this.addstrlength. 262 263 Params: 264 dst = destination string buffer 265 266 Returns: 267 a slice to valid data in dst on success or null on failure. 268 269 Errors: 270 ENOSPC - dst is too short. 271 272 (Note: The inet_ntop() specs say it can fail with errno = EAFNOSUPPORT; 273 this is not possible unless there is a bug in this struct template ;) 274 275 **************************************************************************/ 276 277 mstring inet_ntop ( mstring dst ) 278 { 279 verify( 280 dst.length >= (&this).addrstrlen, 281 "dst.length expected to be at least addrstrlen" 282 ); 283 284 auto address_p = .inet_ntop((&this).family, (&this).address_n.ptr, dst.ptr, 285 castFrom!(size_t).to!(int)(dst.length)); 286 287 return address_p? dst.ptr[0 .. strlen(dst.ptr)] : null; 288 } 289 290 /************************************************************************** 291 292 Sets this.addr to the IP address in ip_address_str and the port to port. 293 294 Params: 295 ip_address_str = IP address string 296 port = port 297 298 Returns: 299 a sockaddr pointer to this.addr on success. 300 If ip_address_str does not contain a valid IP address, null is 301 returned and errno set to EINVAL (this error is reported by this 302 wrapper, not the underlying POSIX function). 303 304 **************************************************************************/ 305 306 public sockaddr* opCall ( cstring ip_address_str, ushort port = 0 ) 307 { 308 if ((&this).inet_pton(ip_address_str) == 1) 309 { 310 (&this).port = port; 311 return cast (sockaddr*) &(&this).addr; 312 } 313 else 314 { 315 .errno = EINVAL; 316 return null; 317 } 318 } 319 320 /************************************************************************** 321 322 Sets this.addr to the wildcard "any" IP address and the port to port. 323 324 Params: 325 port = input port 326 327 Returns: 328 a sockaddr pointer to this.addr. 329 330 **************************************************************************/ 331 332 public sockaddr* opCall ( ushort port ) 333 { 334 (&this).port = port; 335 336 return (&this).setAddressAny(); 337 } 338 339 /************************************************************************** 340 341 Copies addr to this.addr. 342 343 Params: 344 addr = input address 345 346 Returns: 347 a sockaddr pointer to this.addr. 348 349 **************************************************************************/ 350 351 public sockaddr* opAssign ( Addr addr ) 352 { 353 (&this).addr = addr; 354 355 return cast (sockaddr*) &(&this).addr; 356 } 357 358 /************************************************************************** 359 360 Copies *addr to this.addr. 361 362 Params: 363 addr = input address 364 365 Returns: 366 a sockaddr pointer to this.addr. 367 368 In: 369 addr must not be null. 370 371 **************************************************************************/ 372 373 public sockaddr* opAssign ( Addr* addr ) 374 { 375 verify(addr !is null); 376 (&this).addr = *addr; 377 378 return cast (sockaddr*) &(&this).addr; 379 } 380 381 /************************************************************************** 382 383 Clears this.addr. 384 385 **************************************************************************/ 386 387 public void clear ( ) 388 { 389 (&this).addr = (&this).addr_init; 390 } 391 392 /************************************************************************** 393 394 Obtains a slice to the binary address in from this.addr, that is, 395 for IPv4 the sin_addr or for IPv6 the sin6_addr field, respectively. 396 397 Returns: 398 a slice to the binary address data from this.addr. 399 400 **************************************************************************/ 401 402 public void[] address_n ( ) 403 { 404 with ((&this).addr) static if (IPv6) 405 { 406 return (cast (void*) &sin6_addr)[0 .. sin6_addr.sizeof]; 407 } 408 else 409 { 410 return (cast (void*) &sin_addr)[0 .. sin_addr.sizeof]; 411 } 412 } 413 414 // TODO 415 416 public int getnameinfo(mstring host, mstring serv, 417 GetNameInfoFlags flags = GetNameInfoFlags.None) 418 { 419 return core.sys.posix.netdb.getnameinfo( 420 cast (sockaddr*) &(&this).addr, (&this).addr.sizeof, 421 host.ptr, castFrom!(size_t).to!(int)(host.length), serv.ptr, 422 castFrom!(size_t).to!(int)(serv.length), flags); 423 } 424 }