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