1/* 2 Written by Christopher E. Miller 3 $(LINK2 http://www.boost.org/LICENSE_1_0.txt, Boost License 1.0). 4*/ 5 6 7module core.sys.windows.winsock2; 8version (Windows): 9@system: 10 11pragma(lib, "ws2_32"); 12 13extern(Windows): 14nothrow: 15 16alias SOCKET = size_t; 17alias socklen_t = int; 18 19enum SOCKET INVALID_SOCKET = cast(SOCKET)~0; 20enum int SOCKET_ERROR = -1; 21 22enum WSADESCRIPTION_LEN = 256; 23enum WSASYS_STATUS_LEN = 128; 24 25struct WSADATA 26{ 27 ushort wVersion; 28 ushort wHighVersion; 29 char[WSADESCRIPTION_LEN + 1] szDescription = 0; 30 char[WSASYS_STATUS_LEN + 1] szSystemStatus = 0; 31 ushort iMaxSockets; 32 ushort iMaxUdpDg; 33 char* lpVendorInfo; 34} 35alias LPWSADATA = WSADATA*; 36 37 38enum int IOCPARM_MASK = 0x7F; 39enum int IOC_IN = cast(int)0x80000000; 40enum int FIONBIO = cast(int)(IOC_IN | ((uint.sizeof & IOCPARM_MASK) << 16) | (102 << 8) | 126); 41 42enum NI_MAXHOST = 1025; 43enum NI_MAXSERV = 32; 44 45@nogc 46{ 47int WSAStartup(ushort wVersionRequested, LPWSADATA lpWSAData); 48int WSACleanup(); 49SOCKET socket(int af, int type, int protocol); 50int ioctlsocket(SOCKET s, int cmd, uint* argp); 51int bind(SOCKET s, const(sockaddr)* name, socklen_t namelen); 52int connect(SOCKET s, const(sockaddr)* name, socklen_t namelen); 53int listen(SOCKET s, int backlog); 54SOCKET accept(SOCKET s, sockaddr* addr, socklen_t* addrlen); 55int closesocket(SOCKET s); 56int shutdown(SOCKET s, int how); 57int getpeername(SOCKET s, sockaddr* name, socklen_t* namelen); 58int getsockname(SOCKET s, sockaddr* name, socklen_t* namelen); 59int send(SOCKET s, const(void)* buf, int len, int flags); 60int sendto(SOCKET s, const(void)* buf, int len, int flags, const(sockaddr)* to, socklen_t tolen); 61int recv(SOCKET s, void* buf, int len, int flags); 62int recvfrom(SOCKET s, void* buf, int len, int flags, sockaddr* from, socklen_t* fromlen); 63int getsockopt(SOCKET s, int level, int optname, void* optval, socklen_t* optlen); 64int setsockopt(SOCKET s, int level, int optname, const(void)* optval, socklen_t optlen); 65uint inet_addr(const char* cp); 66int select(int nfds, fd_set* readfds, fd_set* writefds, fd_set* errorfds, const(timeval)* timeout); 67char* inet_ntoa(in_addr ina); 68hostent* gethostbyname(const char* name); 69hostent* gethostbyaddr(const(void)* addr, int len, int type); 70protoent* getprotobyname(const char* name); 71protoent* getprotobynumber(int number); 72servent* getservbyname(const char* name, const char* proto); 73servent* getservbyport(int port, const char* proto); 74} 75 76enum: int 77{ 78 NI_NOFQDN = 0x01, 79 NI_NUMERICHOST = 0x02, 80 NI_NAMEREQD = 0x04, 81 NI_NUMERICSERV = 0x08, 82 NI_DGRAM = 0x10, 83} 84 85@nogc 86{ 87int gethostname(const char* name, int namelen); 88int getaddrinfo(const(char)* nodename, const(char)* servname, const(addrinfo)* hints, addrinfo** res); 89void freeaddrinfo(addrinfo* ai); 90int getnameinfo(const(sockaddr)* sa, socklen_t salen, char* host, uint hostlen, char* serv, uint servlen, int flags); 91} 92 93enum WSABASEERR = 10000; 94 95enum: int 96{ 97 /* 98 * Windows Sockets definitions of regular Microsoft C error constants 99 */ 100 WSAEINTR = (WSABASEERR+4), 101 WSAEBADF = (WSABASEERR+9), 102 WSAEACCES = (WSABASEERR+13), 103 WSAEFAULT = (WSABASEERR+14), 104 WSAEINVAL = (WSABASEERR+22), 105 WSAEMFILE = (WSABASEERR+24), 106 107 /* 108 * Windows Sockets definitions of regular Berkeley error constants 109 */ 110 WSAEWOULDBLOCK = (WSABASEERR+35), 111 WSAEINPROGRESS = (WSABASEERR+36), 112 WSAEALREADY = (WSABASEERR+37), 113 WSAENOTSOCK = (WSABASEERR+38), 114 WSAEDESTADDRREQ = (WSABASEERR+39), 115 WSAEMSGSIZE = (WSABASEERR+40), 116 WSAEPROTOTYPE = (WSABASEERR+41), 117 WSAENOPROTOOPT = (WSABASEERR+42), 118 WSAEPROTONOSUPPORT = (WSABASEERR+43), 119 WSAESOCKTNOSUPPORT = (WSABASEERR+44), 120 WSAEOPNOTSUPP = (WSABASEERR+45), 121 WSAEPFNOSUPPORT = (WSABASEERR+46), 122 WSAEAFNOSUPPORT = (WSABASEERR+47), 123 WSAEADDRINUSE = (WSABASEERR+48), 124 WSAEADDRNOTAVAIL = (WSABASEERR+49), 125 WSAENETDOWN = (WSABASEERR+50), 126 WSAENETUNREACH = (WSABASEERR+51), 127 WSAENETRESET = (WSABASEERR+52), 128 WSAECONNABORTED = (WSABASEERR+53), 129 WSAECONNRESET = (WSABASEERR+54), 130 WSAENOBUFS = (WSABASEERR+55), 131 WSAEISCONN = (WSABASEERR+56), 132 WSAENOTCONN = (WSABASEERR+57), 133 WSAESHUTDOWN = (WSABASEERR+58), 134 WSAETOOMANYREFS = (WSABASEERR+59), 135 WSAETIMEDOUT = (WSABASEERR+60), 136 WSAECONNREFUSED = (WSABASEERR+61), 137 WSAELOOP = (WSABASEERR+62), 138 WSAENAMETOOLONG = (WSABASEERR+63), 139 WSAEHOSTDOWN = (WSABASEERR+64), 140 WSAEHOSTUNREACH = (WSABASEERR+65), 141 WSAENOTEMPTY = (WSABASEERR+66), 142 WSAEPROCLIM = (WSABASEERR+67), 143 WSAEUSERS = (WSABASEERR+68), 144 WSAEDQUOT = (WSABASEERR+69), 145 WSAESTALE = (WSABASEERR+70), 146 WSAEREMOTE = (WSABASEERR+71), 147 148 /* 149 * Extended Windows Sockets error constant definitions 150 */ 151 WSASYSNOTREADY = (WSABASEERR+91), 152 WSAVERNOTSUPPORTED = (WSABASEERR+92), 153 WSANOTINITIALISED = (WSABASEERR+93), 154 155 /* Authoritative Answer: Host not found */ 156 WSAHOST_NOT_FOUND = (WSABASEERR+1001), 157 HOST_NOT_FOUND = WSAHOST_NOT_FOUND, 158 159 /* Non-Authoritative: Host not found, or SERVERFAIL */ 160 WSATRY_AGAIN = (WSABASEERR+1002), 161 TRY_AGAIN = WSATRY_AGAIN, 162 163 /* Non recoverable errors, FORMERR, REFUSED, NOTIMP */ 164 WSANO_RECOVERY = (WSABASEERR+1003), 165 NO_RECOVERY = WSANO_RECOVERY, 166 167 /* Valid name, no data record of requested type */ 168 WSANO_DATA = (WSABASEERR+1004), 169 NO_DATA = WSANO_DATA, 170 171 /* no address, look for MX record */ 172 WSANO_ADDRESS = WSANO_DATA, 173 NO_ADDRESS = WSANO_ADDRESS 174} 175 176/* 177 * Windows Sockets errors redefined as regular Berkeley error constants 178 */ 179enum: int 180{ 181 EWOULDBLOCK = WSAEWOULDBLOCK, 182 EINPROGRESS = WSAEINPROGRESS, 183 EALREADY = WSAEALREADY, 184 ENOTSOCK = WSAENOTSOCK, 185 EDESTADDRREQ = WSAEDESTADDRREQ, 186 EMSGSIZE = WSAEMSGSIZE, 187 EPROTOTYPE = WSAEPROTOTYPE, 188 ENOPROTOOPT = WSAENOPROTOOPT, 189 EPROTONOSUPPORT = WSAEPROTONOSUPPORT, 190 ESOCKTNOSUPPORT = WSAESOCKTNOSUPPORT, 191 EOPNOTSUPP = WSAEOPNOTSUPP, 192 EPFNOSUPPORT = WSAEPFNOSUPPORT, 193 EAFNOSUPPORT = WSAEAFNOSUPPORT, 194 EADDRINUSE = WSAEADDRINUSE, 195 EADDRNOTAVAIL = WSAEADDRNOTAVAIL, 196 ENETDOWN = WSAENETDOWN, 197 ENETUNREACH = WSAENETUNREACH, 198 ENETRESET = WSAENETRESET, 199 ECONNABORTED = WSAECONNABORTED, 200 ECONNRESET = WSAECONNRESET, 201 ENOBUFS = WSAENOBUFS, 202 EISCONN = WSAEISCONN, 203 ENOTCONN = WSAENOTCONN, 204 ESHUTDOWN = WSAESHUTDOWN, 205 ETOOMANYREFS = WSAETOOMANYREFS, 206 ETIMEDOUT = WSAETIMEDOUT, 207 ECONNREFUSED = WSAECONNREFUSED, 208 ELOOP = WSAELOOP, 209 ENAMETOOLONG = WSAENAMETOOLONG, 210 EHOSTDOWN = WSAEHOSTDOWN, 211 EHOSTUNREACH = WSAEHOSTUNREACH, 212 ENOTEMPTY = WSAENOTEMPTY, 213 EPROCLIM = WSAEPROCLIM, 214 EUSERS = WSAEUSERS, 215 EDQUOT = WSAEDQUOT, 216 ESTALE = WSAESTALE, 217 EREMOTE = WSAEREMOTE 218} 219 220enum: int 221{ 222 EAI_NONAME = WSAHOST_NOT_FOUND, 223} 224 225int WSAGetLastError() @trusted @nogc; 226 227 228enum: int 229{ 230 AF_UNSPEC = 0, 231 232 AF_UNIX = 1, 233 AF_INET = 2, 234 AF_IMPLINK = 3, 235 AF_PUP = 4, 236 AF_CHAOS = 5, 237 AF_NS = 6, 238 AF_IPX = AF_NS, 239 AF_ISO = 7, 240 AF_OSI = AF_ISO, 241 AF_ECMA = 8, 242 AF_DATAKIT = 9, 243 AF_CCITT = 10, 244 AF_SNA = 11, 245 AF_DECnet = 12, 246 AF_DLI = 13, 247 AF_LAT = 14, 248 AF_HYLINK = 15, 249 AF_APPLETALK = 16, 250 AF_NETBIOS = 17, 251 AF_VOICEVIEW = 18, 252 AF_FIREFOX = 19, 253 AF_UNKNOWN1 = 20, 254 AF_BAN = 21, 255 AF_ATM = 22, 256 AF_INET6 = 23, 257 AF_CLUSTER = 24, 258 AF_12844 = 25, 259 AF_IRDA = 26, 260 AF_NETDES = 28, 261 262 AF_MAX = 29, 263 264 265 PF_UNSPEC = AF_UNSPEC, 266 267 PF_UNIX = AF_UNIX, 268 PF_INET = AF_INET, 269 PF_IMPLINK = AF_IMPLINK, 270 PF_PUP = AF_PUP, 271 PF_CHAOS = AF_CHAOS, 272 PF_NS = AF_NS, 273 PF_IPX = AF_IPX, 274 PF_ISO = AF_ISO, 275 PF_OSI = AF_OSI, 276 PF_ECMA = AF_ECMA, 277 PF_DATAKIT = AF_DATAKIT, 278 PF_CCITT = AF_CCITT, 279 PF_SNA = AF_SNA, 280 PF_DECnet = AF_DECnet, 281 PF_DLI = AF_DLI, 282 PF_LAT = AF_LAT, 283 PF_HYLINK = AF_HYLINK, 284 PF_APPLETALK = AF_APPLETALK, 285 PF_VOICEVIEW = AF_VOICEVIEW, 286 PF_FIREFOX = AF_FIREFOX, 287 PF_UNKNOWN1 = AF_UNKNOWN1, 288 PF_BAN = AF_BAN, 289 PF_INET6 = AF_INET6, 290 291 PF_MAX = AF_MAX, 292} 293 294 295enum: int 296{ 297 SOL_SOCKET = 0xFFFF, 298} 299 300 301enum: int 302{ 303 SO_DEBUG = 0x0001, 304 SO_ACCEPTCONN = 0x0002, 305 SO_REUSEADDR = 0x0004, 306 SO_KEEPALIVE = 0x0008, 307 SO_DONTROUTE = 0x0010, 308 SO_BROADCAST = 0x0020, 309 SO_USELOOPBACK = 0x0040, 310 SO_LINGER = 0x0080, 311 SO_DONTLINGER = ~SO_LINGER, 312 SO_OOBINLINE = 0x0100, 313 SO_SNDBUF = 0x1001, 314 SO_RCVBUF = 0x1002, 315 SO_SNDLOWAT = 0x1003, 316 SO_RCVLOWAT = 0x1004, 317 SO_SNDTIMEO = 0x1005, 318 SO_RCVTIMEO = 0x1006, 319 SO_ERROR = 0x1007, 320 SO_TYPE = 0x1008, 321 SO_EXCLUSIVEADDRUSE = ~SO_REUSEADDR, 322 323 TCP_NODELAY = 1, 324 325 IP_OPTIONS = 1, 326 327 IP_HDRINCL = 2, 328 IP_TOS = 3, 329 IP_TTL = 4, 330 IP_MULTICAST_IF = 9, 331 IP_MULTICAST_TTL = 10, 332 IP_MULTICAST_LOOP = 11, 333 IP_ADD_MEMBERSHIP = 12, 334 IP_DROP_MEMBERSHIP = 13, 335 IP_DONTFRAGMENT = 14, 336 IP_ADD_SOURCE_MEMBERSHIP = 15, 337 IP_DROP_SOURCE_MEMBERSHIP = 16, 338 IP_BLOCK_SOURCE = 17, 339 IP_UNBLOCK_SOURCE = 18, 340 IP_PKTINFO = 19, 341 342 IPV6_UNICAST_HOPS = 4, 343 IPV6_MULTICAST_IF = 9, 344 IPV6_MULTICAST_HOPS = 10, 345 IPV6_MULTICAST_LOOP = 11, 346 IPV6_ADD_MEMBERSHIP = 12, 347 IPV6_DROP_MEMBERSHIP = 13, 348 IPV6_JOIN_GROUP = IPV6_ADD_MEMBERSHIP, 349 IPV6_LEAVE_GROUP = IPV6_DROP_MEMBERSHIP, 350 IPV6_V6ONLY = 27, 351} 352 353 354/// Default FD_SETSIZE value. 355/// In C/C++, it is redefinable by #define-ing the macro before #include-ing 356/// winsock.h. In D, use the $(D FD_CREATE) function to allocate a $(D fd_set) 357/// of an arbitrary size. 358enum int FD_SETSIZE = 64; 359 360 361struct fd_set_custom(uint SETSIZE) 362{ 363 uint fd_count; 364 SOCKET[SETSIZE] fd_array; 365} 366 367alias fd_set = fd_set_custom!FD_SETSIZE; 368 369// Removes. 370void FD_CLR(SOCKET fd, fd_set* set) pure @nogc 371{ 372 uint c = set.fd_count; 373 SOCKET* start = set.fd_array.ptr; 374 SOCKET* stop = start + c; 375 376 for (; start != stop; start++) 377 { 378 if (*start == fd) 379 goto found; 380 } 381 return; //not found 382 383 found: 384 for (++start; start != stop; start++) 385 { 386 *(start - 1) = *start; 387 } 388 389 set.fd_count = c - 1; 390} 391 392 393// Tests. 394int FD_ISSET(SOCKET fd, const(fd_set)* set) pure @nogc 395{ 396const(SOCKET)* start = set.fd_array.ptr; 397const(SOCKET)* stop = start + set.fd_count; 398 399 for (; start != stop; start++) 400 { 401 if (*start == fd) 402 return true; 403 } 404 return false; 405} 406 407 408// Adds. 409void FD_SET(SOCKET fd, fd_set* set) pure @nogc 410{ 411 uint c = set.fd_count; 412 set.fd_array.ptr[c] = fd; 413 set.fd_count = c + 1; 414} 415 416 417// Resets to zero. 418void FD_ZERO(fd_set* set) pure @nogc 419{ 420 set.fd_count = 0; 421} 422 423 424/// Creates a new $(D fd_set) with the specified capacity. 425fd_set* FD_CREATE(uint capacity) pure 426{ 427 // Take into account alignment (SOCKET may be 64-bit and require 64-bit alignment on 64-bit systems) 428 size_t size = (fd_set_custom!1).sizeof - SOCKET.sizeof + (SOCKET.sizeof * capacity); 429 auto data = new ubyte[size]; 430 auto set = cast(fd_set*)data.ptr; 431 FD_ZERO(set); 432 return set; 433} 434 435struct linger 436{ 437 ushort l_onoff; 438 ushort l_linger; 439} 440 441 442struct protoent 443{ 444 char* p_name; 445 char** p_aliases; 446 short p_proto; 447} 448 449 450struct servent 451{ 452 char* s_name; 453 char** s_aliases; 454 455 version (Win64) 456 { 457 char* s_proto; 458 short s_port; 459 } 460 else version (Win32) 461 { 462 short s_port; 463 char* s_proto; 464 } 465} 466 467 468/+ 469union in6_addr 470{ 471 private union _u_t 472 { 473 ubyte[16] Byte; 474 ushort[8] Word; 475 } 476 _u_t u; 477} 478 479 480struct in_addr6 481{ 482 ubyte[16] s6_addr; 483} 484+/ 485 486@safe pure @nogc 487{ 488 ushort htons(ushort x); 489 uint htonl(uint x); 490 ushort ntohs(ushort x); 491 uint ntohl(uint x); 492} 493 494 495enum: int 496{ 497 SOCK_STREAM = 1, 498 SOCK_DGRAM = 2, 499 SOCK_RAW = 3, 500 SOCK_RDM = 4, 501 SOCK_SEQPACKET = 5, 502} 503 504 505enum: int 506{ 507 IPPROTO_IP = 0, 508 IPPROTO_ICMP = 1, 509 IPPROTO_IGMP = 2, 510 IPPROTO_GGP = 3, 511 IPPROTO_TCP = 6, 512 IPPROTO_PUP = 12, 513 IPPROTO_UDP = 17, 514 IPPROTO_IDP = 22, 515 IPPROTO_IPV6 = 41, 516 IPPROTO_ND = 77, 517 IPPROTO_RAW = 255, 518 519 IPPROTO_MAX = 256, 520} 521 522 523enum: int 524{ 525 MSG_OOB = 0x1, 526 MSG_PEEK = 0x2, 527 MSG_DONTROUTE = 0x4 528} 529 530 531enum: int 532{ 533 SD_RECEIVE = 0, 534 SD_SEND = 1, 535 SD_BOTH = 2, 536} 537 538 539enum: uint 540{ 541 INADDR_ANY = 0, 542 INADDR_LOOPBACK = 0x7F000001, 543 INADDR_BROADCAST = 0xFFFFFFFF, 544 INADDR_NONE = 0xFFFFFFFF, 545 ADDR_ANY = INADDR_ANY, 546} 547 548 549enum: int 550{ 551 AI_PASSIVE = 0x1, 552 AI_CANONNAME = 0x2, 553 AI_NUMERICHOST = 0x4, 554 AI_ADDRCONFIG = 0x0400, 555 AI_NON_AUTHORITATIVE = 0x04000, 556 AI_SECURE = 0x08000, 557 AI_RETURN_PREFERRED_NAMES = 0x010000, 558} 559 560 561struct timeval 562{ 563 int tv_sec; 564 int tv_usec; 565} 566 567 568union in_addr 569{ 570 private union _S_un_t 571 { 572 private struct _S_un_b_t 573 { 574 ubyte s_b1, s_b2, s_b3, s_b4; 575 } 576 _S_un_b_t S_un_b; 577 578 private struct _S_un_w_t 579 { 580 ushort s_w1, s_w2; 581 } 582 _S_un_w_t S_un_w; 583 584 uint S_addr; 585 } 586 _S_un_t S_un; 587 588 uint s_addr; 589 590 struct 591 { 592 ubyte s_net, s_host; 593 594 union 595 { 596 ushort s_imp; 597 598 struct 599 { 600 ubyte s_lh, s_impno; 601 } 602 } 603 } 604} 605 606 607union in6_addr 608{ 609 private union _in6_u_t 610 { 611 ubyte[16] u6_addr8; 612 ushort[8] u6_addr16; 613 uint[4] u6_addr32; 614 } 615 _in6_u_t in6_u; 616 617 ubyte[16] s6_addr8; 618 ushort[8] s6_addr16; 619 uint[4] s6_addr32; 620 621 alias s6_addr = s6_addr8; 622} 623 624 625enum in6_addr IN6ADDR_ANY = { s6_addr8: [0] }; 626enum in6_addr IN6ADDR_LOOPBACK = { s6_addr8: [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1] }; 627//alias IN6ADDR_ANY_INIT = IN6ADDR_ANY; 628//alias IN6ADDR_LOOPBACK_INIT = IN6ADDR_LOOPBACK; 629 630enum int INET_ADDRSTRLEN = 16; 631enum int INET6_ADDRSTRLEN = 46; 632 633 634 635 636struct sockaddr 637{ 638 short sa_family; 639 ubyte[14] sa_data; 640} 641alias sockaddr SOCKADDR; 642alias SOCKADDR* PSOCKADDR, LPSOCKADDR; 643 644struct sockaddr_storage 645{ 646 short ss_family; 647 char[6] __ss_pad1 = void; 648 long __ss_align; 649 char[112] __ss_pad2 = void; 650} 651alias sockaddr_storage SOCKADDR_STORAGE; 652alias SOCKADDR_STORAGE* PSOCKADDR_STORAGE; 653 654struct sockaddr_in 655{ 656 short sin_family = AF_INET; 657 ushort sin_port; 658 in_addr sin_addr; 659 ubyte[8] sin_zero; 660} 661alias sockaddr_in SOCKADDR_IN; 662alias SOCKADDR_IN* PSOCKADDR_IN, LPSOCKADDR_IN; 663 664 665struct sockaddr_in6 666{ 667 short sin6_family = AF_INET6; 668 ushort sin6_port; 669 uint sin6_flowinfo; 670 in6_addr sin6_addr; 671 uint sin6_scope_id; 672} 673 674 675struct addrinfo 676{ 677 int ai_flags; 678 int ai_family; 679 int ai_socktype; 680 int ai_protocol; 681 size_t ai_addrlen; 682 char* ai_canonname; 683 sockaddr* ai_addr; 684 addrinfo* ai_next; 685} 686 687 688struct hostent 689{ 690 char* h_name; 691 char** h_aliases; 692 short h_addrtype; 693 short h_length; 694 char** h_addr_list; 695 696 697 char* h_addr() @safe pure nothrow @nogc 698 { 699 return h_addr_list[0]; 700 } 701} 702 703// Note: These are Winsock2!! 704struct WSAOVERLAPPED; 705alias LPWSAOVERLAPPED = WSAOVERLAPPED*; 706alias LPWSAOVERLAPPED_COMPLETION_ROUTINE = void function(uint, uint, LPWSAOVERLAPPED, uint); 707int WSAIoctl(SOCKET s, uint dwIoControlCode, 708 void* lpvInBuffer, uint cbInBuffer, 709 void* lpvOutBuffer, uint cbOutBuffer, 710 uint* lpcbBytesReturned, 711 LPWSAOVERLAPPED lpOverlapped, LPWSAOVERLAPPED_COMPLETION_ROUTINE lpCompletionRoutine); 712 713 714enum IOC_VENDOR = 0x18000000; 715enum SIO_KEEPALIVE_VALS = IOC_IN | IOC_VENDOR | 4; 716 717/* Argument structure for SIO_KEEPALIVE_VALS */ 718struct tcp_keepalive 719{ 720 uint onoff; 721 uint keepalivetime; 722 uint keepaliveinterval; 723} 724