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