- Remove the remaining __USE_W32API, deprecated for ages.
[reactos.git] / reactos / dll / win32 / ws2_32 / misc / ns.c
1 /*
2 * COPYRIGHT: See COPYING in the top level directory
3 * PROJECT: ReactOS WinSock 2 DLL
4 * FILE: misc/ns.c
5 * PURPOSE: Namespace APIs
6 * PROGRAMMERS: Casper S. Hornstrup (chorns@users.sourceforge.net)
7 * REVISIONS:
8 * CSH 01/09-2000 Created
9 */
10 #include <ctype.h>
11 #include <ws2_32.h>
12 #include <winbase.h>
13
14 #ifndef BUFSIZ
15 #define BUFSIZ 1024
16 #endif/*BUFSIZ*/
17
18 #ifndef MAX_HOSTNAME_LEN
19 #define MAX_HOSTNAME_LEN 256
20 #endif
21
22 /* Name resolution APIs */
23
24 /*
25 * @unimplemented
26 */
27 INT
28 EXPORT
29 WSAAddressToStringA(IN LPSOCKADDR lpsaAddress,
30 IN DWORD dwAddressLength,
31 IN LPWSAPROTOCOL_INFOA lpProtocolInfo,
32 OUT LPSTR lpszAddressString,
33 IN OUT LPDWORD lpdwAddressStringLength)
34 {
35 UNIMPLEMENTED
36
37 WSASetLastError(WSASYSCALLFAILURE);
38 return SOCKET_ERROR;
39 }
40
41
42 /*
43 * @unimplemented
44 */
45 INT
46 EXPORT
47 WSAAddressToStringW(IN LPSOCKADDR lpsaAddress,
48 IN DWORD dwAddressLength,
49 IN LPWSAPROTOCOL_INFOW lpProtocolInfo,
50 OUT LPWSTR lpszAddressString,
51 IN OUT LPDWORD lpdwAddressStringLength)
52 {
53 UNIMPLEMENTED
54
55 WSASetLastError(WSASYSCALLFAILURE);
56 return SOCKET_ERROR;
57 }
58
59
60 /*
61 * @unimplemented
62 */
63 INT
64 EXPORT
65 WSAEnumNameSpaceProvidersA(IN OUT LPDWORD lpdwBufferLength,
66 OUT LPWSANAMESPACE_INFOA lpnspBuffer)
67 {
68 UNIMPLEMENTED
69
70 WSASetLastError(WSASYSCALLFAILURE);
71 return SOCKET_ERROR;
72 }
73
74
75 /*
76 * @unimplemented
77 */
78 INT
79 EXPORT
80 WSAEnumNameSpaceProvidersW(IN OUT LPDWORD lpdwBufferLength,
81 OUT LPWSANAMESPACE_INFOW lpnspBuffer)
82 {
83 UNIMPLEMENTED
84
85 WSASetLastError(WSASYSCALLFAILURE);
86 return SOCKET_ERROR;
87 }
88
89
90 /*
91 * @unimplemented
92 */
93 INT
94 EXPORT
95 WSAGetServiceClassInfoA(IN LPGUID lpProviderId,
96 IN LPGUID lpServiceClassId,
97 IN OUT LPDWORD lpdwBufferLength,
98 OUT LPWSASERVICECLASSINFOA lpServiceClassInfo)
99 {
100 UNIMPLEMENTED
101
102 WSASetLastError(WSASYSCALLFAILURE);
103 return SOCKET_ERROR;
104 }
105
106
107 /*
108 * @unimplemented
109 */
110 INT
111 EXPORT
112 WSAGetServiceClassInfoW(IN LPGUID lpProviderId,
113 IN LPGUID lpServiceClassId,
114 IN OUT LPDWORD lpdwBufferLength,
115 OUT LPWSASERVICECLASSINFOW lpServiceClassInfo)
116 {
117 UNIMPLEMENTED
118
119 WSASetLastError(WSASYSCALLFAILURE);
120 return SOCKET_ERROR;
121 }
122
123
124 /*
125 * @unimplemented
126 */
127 INT
128 EXPORT
129 WSAGetServiceClassNameByClassIdA(IN LPGUID lpServiceClassId,
130 OUT LPSTR lpszServiceClassName,
131 IN OUT LPDWORD lpdwBufferLength)
132 {
133 UNIMPLEMENTED
134
135 WSASetLastError(WSASYSCALLFAILURE);
136 return SOCKET_ERROR;
137 }
138
139
140 /*
141 * @unimplemented
142 */
143 INT
144 EXPORT
145 WSAGetServiceClassNameByClassIdW(IN LPGUID lpServiceClassId,
146 OUT LPWSTR lpszServiceClassName,
147 IN OUT LPDWORD lpdwBufferLength)
148 {
149 UNIMPLEMENTED
150
151 WSASetLastError(WSASYSCALLFAILURE);
152 return SOCKET_ERROR;
153 }
154
155
156 /*
157 * @unimplemented
158 */
159 INT
160 EXPORT
161 WSAInstallServiceClassA(IN LPWSASERVICECLASSINFOA lpServiceClassInfo)
162 {
163 UNIMPLEMENTED
164
165 WSASetLastError(WSASYSCALLFAILURE);
166 return SOCKET_ERROR;
167 }
168
169
170 /*
171 * @unimplemented
172 */
173 INT
174 EXPORT
175 WSAInstallServiceClassW(IN LPWSASERVICECLASSINFOW lpServiceClassInfo)
176 {
177 UNIMPLEMENTED
178
179 WSASetLastError(WSASYSCALLFAILURE);
180 return SOCKET_ERROR;
181 }
182
183
184 /*
185 * @unimplemented
186 */
187 INT
188 EXPORT
189 WSALookupServiceBeginA(IN LPWSAQUERYSETA lpqsRestrictions,
190 IN DWORD dwControlFlags,
191 OUT LPHANDLE lphLookup)
192 {
193 UNIMPLEMENTED
194
195 WSASetLastError(WSASYSCALLFAILURE);
196 return SOCKET_ERROR;
197 }
198
199
200 /*
201 * @unimplemented
202 */
203 INT
204 EXPORT
205 WSALookupServiceBeginW(IN LPWSAQUERYSETW lpqsRestrictions,
206 IN DWORD dwControlFlags,
207 OUT LPHANDLE lphLookup)
208 {
209 UNIMPLEMENTED
210
211 WSASetLastError(WSASYSCALLFAILURE);
212 return SOCKET_ERROR;
213 }
214
215
216 /*
217 * @unimplemented
218 */
219 INT
220 EXPORT
221 WSALookupServiceEnd(IN HANDLE hLookup)
222 {
223 UNIMPLEMENTED
224
225 WSASetLastError(WSASYSCALLFAILURE);
226 return SOCKET_ERROR;
227 }
228
229
230 /*
231 * @unimplemented
232 */
233 INT
234 EXPORT
235 WSALookupServiceNextA(IN HANDLE hLookup,
236 IN DWORD dwControlFlags,
237 IN OUT LPDWORD lpdwBufferLength,
238 OUT LPWSAQUERYSETA lpqsResults)
239 {
240 UNIMPLEMENTED
241
242 WSASetLastError(WSASYSCALLFAILURE);
243 return SOCKET_ERROR;
244 }
245
246
247 /*
248 * @unimplemented
249 */
250 INT
251 EXPORT
252 WSALookupServiceNextW(IN HANDLE hLookup,
253 IN DWORD dwControlFlags,
254 IN OUT LPDWORD lpdwBufferLength,
255 OUT LPWSAQUERYSETW lpqsResults)
256 {
257 UNIMPLEMENTED
258
259 WSASetLastError(WSASYSCALLFAILURE);
260 return SOCKET_ERROR;
261 }
262
263
264 /*
265 * @unimplemented
266 */
267 INT
268 EXPORT
269 WSARemoveServiceClass(IN LPGUID lpServiceClassId)
270 {
271 UNIMPLEMENTED
272
273 WSASetLastError(WSASYSCALLFAILURE);
274 return SOCKET_ERROR;
275 }
276
277
278 /*
279 * @unimplemented
280 */
281 INT
282 EXPORT
283 WSASetServiceA(IN LPWSAQUERYSETA lpqsRegInfo,
284 IN WSAESETSERVICEOP essOperation,
285 IN DWORD dwControlFlags)
286 {
287 UNIMPLEMENTED
288
289 WSASetLastError(WSASYSCALLFAILURE);
290 return SOCKET_ERROR;
291 }
292
293
294 /*
295 * @unimplemented
296 */
297 INT
298 EXPORT
299 WSASetServiceW(IN LPWSAQUERYSETW lpqsRegInfo,
300 IN WSAESETSERVICEOP essOperation,
301 IN DWORD dwControlFlags)
302 {
303 UNIMPLEMENTED
304
305 WSASetLastError(WSASYSCALLFAILURE);
306 return SOCKET_ERROR;
307 }
308
309
310 /*
311 * @unimplemented
312 */
313 INT
314 EXPORT
315 WSAStringToAddressA(IN LPSTR AddressString,
316 IN INT AddressFamily,
317 IN LPWSAPROTOCOL_INFOA lpProtocolInfo,
318 OUT LPSOCKADDR lpAddress,
319 IN OUT LPINT lpAddressLength)
320 {
321 INT ret, len;
322 LPWSTR szTemp;
323 LPWSAPROTOCOL_INFOW lpProtoInfoW = NULL;
324
325 len = MultiByteToWideChar(CP_ACP,
326 0,
327 AddressString,
328 -1,
329 NULL,
330 0);
331
332 szTemp = HeapAlloc(GetProcessHeap(),
333 0,
334 len * sizeof(WCHAR));
335
336 MultiByteToWideChar(CP_ACP,
337 0,
338 AddressString,
339 -1,
340 szTemp,
341 len);
342
343 if (lpProtocolInfo)
344 {
345 len = WSAPROTOCOL_LEN+1;
346 lpProtoInfoW = HeapAlloc(GetProcessHeap(),
347 0,
348 len * sizeof(WCHAR) );
349
350 memcpy(lpProtoInfoW,
351 lpProtocolInfo,
352 sizeof(LPWSAPROTOCOL_INFOA));
353
354 MultiByteToWideChar(CP_ACP,
355 0,
356 lpProtocolInfo->szProtocol,
357 -1,
358 lpProtoInfoW->szProtocol,
359 len);
360 }
361
362 ret = WSAStringToAddressW(szTemp,
363 AddressFamily,
364 lpProtoInfoW,
365 lpAddress,
366 lpAddressLength);
367
368 HeapFree(GetProcessHeap(),
369 0,
370 szTemp );
371
372 if (lpProtocolInfo)
373 HeapFree(GetProcessHeap(),
374 0,
375 lpProtoInfoW);
376
377 WSASetLastError(ret);
378 return ret;
379 }
380
381
382
383 /*
384 * @implemented
385 */
386 INT
387 EXPORT
388 WSAStringToAddressW(IN LPWSTR AddressString,
389 IN INT AddressFamily,
390 IN LPWSAPROTOCOL_INFOW lpProtocolInfo,
391 OUT LPSOCKADDR lpAddress,
392 IN OUT LPINT lpAddressLength)
393 {
394 int pos=0;
395 int res=0;
396 LONG inetaddr = 0;
397 LPWSTR *bp=NULL;
398 SOCKADDR_IN *sockaddr;
399
400 if (!lpAddressLength || !lpAddress || !AddressString)
401 {
402 WSASetLastError(WSAEINVAL);
403 return SOCKET_ERROR;
404 }
405
406 sockaddr = (SOCKADDR_IN *) lpAddress;
407
408 /* Set right adress family */
409 if (lpProtocolInfo!=NULL)
410 sockaddr->sin_family = lpProtocolInfo->iAddressFamily;
411
412 else sockaddr->sin_family = AddressFamily;
413
414 /* Report size */
415 if (AddressFamily == AF_INET)
416 {
417 if (*lpAddressLength < (INT)sizeof(SOCKADDR_IN))
418 {
419 *lpAddressLength = sizeof(SOCKADDR_IN);
420 res = WSAEFAULT;
421 }
422 else
423 {
424 // translate ip string to ip
425
426 /* rest sockaddr.sin_addr.s_addr
427 for we need to be sure it is zero when we come to while */
428 memset(lpAddress,0,sizeof(SOCKADDR_IN));
429
430 /* Set right adress family */
431 sockaddr->sin_family = AF_INET;
432
433 /* Get port number */
434 pos = wcscspn(AddressString,L":") + 1;
435
436 if (pos < (int)wcslen(AddressString))
437 sockaddr->sin_port = wcstol(&AddressString[pos],
438 bp,
439 10);
440
441 else
442 sockaddr->sin_port = 0;
443
444 /* Get ip number */
445 pos=0;
446 inetaddr=0;
447
448 while (pos < (int)wcslen(AddressString))
449 {
450 inetaddr = (inetaddr<<8) + ((UCHAR)wcstol(&AddressString[pos],
451 bp,
452 10));
453 pos += wcscspn( &AddressString[pos],L".") +1 ;
454 }
455
456 res = 0;
457 sockaddr->sin_addr.s_addr = inetaddr;
458
459 }
460 }
461
462 WSASetLastError(res);
463 if (!res) return 0;
464 return SOCKET_ERROR;
465 }
466
467 void check_hostent(struct hostent **he)
468 {
469 struct hostent *new_he;
470
471 WS_DbgPrint(MID_TRACE,("*he: %x\n",*he));
472
473 if(!*he)
474 {
475 new_he = HeapAlloc(GlobalHeap,
476 0,
477 sizeof(struct hostent) + MAX_HOSTNAME_LEN + 1);
478
479 new_he->h_name = (PCHAR)(new_he + 1);
480 new_he->h_aliases = 0;
481 new_he->h_addrtype = 0; // AF_INET
482 new_he->h_length = 0; // sizeof(in_addr)
483 new_he->h_addr_list = HeapAlloc(GlobalHeap,
484 0,
485 sizeof(char *) * 2);
486
487 RtlZeroMemory(new_he->h_addr_list,
488 sizeof(char *) * 2);
489 *he = new_he;
490 }
491 }
492
493 void populate_hostent(struct hostent *he, char* name, DNS_A_DATA addr)
494 {
495 ASSERT(he);
496
497 //he = HeapAlloc(GlobalHeap, 0, sizeof(struct hostent));
498 //he->h_name = HeapAlloc(GlobalHeap, 0, MAX_HOSTNAME_LEN+1);
499
500 strncpy(he->h_name,
501 name,
502 MAX_HOSTNAME_LEN);
503
504 if( !he->h_aliases ) {
505 he->h_aliases = HeapAlloc(GlobalHeap, 0, sizeof(char *));
506 he->h_aliases[0] = 0;
507 }
508 he->h_addrtype = AF_INET;
509 he->h_length = sizeof(IN_ADDR); //sizeof(struct in_addr);
510
511 if( he->h_addr_list[0] )
512 {
513 HeapFree(GlobalHeap,
514 0,
515 he->h_addr_list[0]);
516 }
517
518 he->h_addr_list[0] = HeapAlloc(GlobalHeap,
519 0,
520 MAX_HOSTNAME_LEN + 1);
521
522 WS_DbgPrint(MID_TRACE,("he->h_addr_list[0] %x\n", he->h_addr_list[0]));
523
524 RtlCopyMemory(he->h_addr_list[0],
525 (char*)&addr.IpAddress,
526 sizeof(addr.IpAddress));
527
528 he->h_addr_list[1] = 0;
529 }
530
531
532 #define HFREE(x) if(x) { HeapFree(GlobalHeap, 0, (x)); x=0; }
533 void free_hostent(struct hostent *he)
534 {
535 if(he)
536 {
537 char *next = 0;
538 HFREE(he->h_name);
539 if(he->h_aliases)
540 {
541 next = he->h_aliases[0];
542 while(next) { HFREE(next); next++; }
543 }
544 if(he->h_addr_list)
545 {
546 next = he->h_addr_list[0];
547 while(next) { HFREE(next); next++; }
548 }
549 HFREE(he->h_addr_list);
550 HFREE(he->h_aliases);
551 HFREE(he);
552 }
553 }
554
555 /* WinSock 1.1 compatible name resolution APIs */
556
557 /*
558 * @unimplemented
559 */
560 LPHOSTENT
561 EXPORT
562 gethostbyaddr(IN CONST CHAR FAR* addr,
563 IN INT len,
564 IN INT type)
565 {
566 UNIMPLEMENTED
567
568 return (LPHOSTENT)NULL;
569 }
570
571 /*
572 Assumes rfc 1123 - adam *
573 addr[1] = 0;
574 addr[0] = inet_addr(name);
575 strcpy( hostname, name );
576 if(addr[0] == 0xffffffff) return NULL;
577 he.h_addr_list = (void *)addr;
578 he.h_name = hostname;
579 he.h_aliases = NULL;
580 he.h_addrtype = AF_INET;
581 he.h_length = sizeof(addr);
582 return &he;
583
584 <RANT>
585 From the MSDN Platform SDK: Windows Sockets 2
586 "The gethostbyname function cannot resolve IP address strings passed to it.
587 Such a request is treated exactly as if an unknown host name were passed."
588 </RANT>
589
590 Defferring to the the documented behaviour, rather than the unix behaviour
591 What if the hostname is in the HOSTS file? see getservbyname
592
593 * @implemented
594 */
595
596 /* DnsQuery -- lib/dnsapi/dnsapi/query.c */
597 /* see ws2_32.h, winsock2.h*/
598 /*getnetworkparameters - iphlp api */
599 /*
600 REFERENCES
601
602 servent -- w32api/include/winsock2.h
603 PWINSOCK_THREAD_BLOCK -- ws2_32.h
604 dllmain.c -- threadlocal memory allocation / deallocation
605 lib/dnsapi
606
607
608 */
609 /* lib/adns/src/adns.h XXX */
610
611
612 /*
613 struct hostent {
614 char *h_name;
615 char **h_aliases;
616 short h_addrtype;
617 short h_length;
618 char **h_addr_list;
619 #define h_addr h_addr_list[0]
620 };
621 struct servent {
622 char *s_name;
623 char **s_aliases;
624 short s_port;
625 char *s_proto;
626 };
627
628
629 struct hostent defined in w32api/include/winsock2.h
630 */
631
632 void free_servent(struct servent* s)
633 {
634 char* next;
635 HFREE(s->s_name);
636 next = s->s_aliases[0];
637 while(next) { HFREE(next); next++; }
638 s->s_port = 0;
639 HFREE(s->s_proto);
640 HFREE(s);
641 }
642
643
644
645 LPHOSTENT
646 EXPORT
647 gethostbyname(IN CONST CHAR FAR* name)
648 {
649 enum addr_type
650 {
651 GH_INVALID,
652 GH_IPV6,
653 GH_IPV4,
654 GH_RFC1123_DNS
655 };
656 typedef enum addr_type addr_type;
657 addr_type addr;
658 int ret = 0;
659 char* found = 0;
660 DNS_STATUS dns_status = {0};
661 /* include/WinDNS.h -- look up DNS_RECORD on MSDN */
662 PDNS_RECORD dp = 0;
663 PWINSOCK_THREAD_BLOCK p;
664
665 addr = GH_INVALID;
666
667 p = NtCurrentTeb()->WinSockData;
668
669 if( !p )
670 {
671 WSASetLastError( WSANOTINITIALISED );
672 return NULL;
673 }
674
675 check_hostent(&p->Hostent); /*XXX alloc_hostent*/
676
677 /* Hostname NULL - behave like gethostname */
678 if(name == NULL)
679 {
680 ret = gethostname(p->Hostent->h_name, MAX_HOSTNAME_LEN);
681 return p->Hostent;
682 }
683
684 if(ret)
685 {
686 WSASetLastError( WSAHOST_NOT_FOUND ); //WSANO_DATA ??
687 return NULL;
688 }
689
690 /* Is it an IPv6 address? */
691 found = strstr(name, ":");
692 if( found != NULL )
693 {
694 addr = GH_IPV6;
695 goto act;
696 }
697
698 /* Is it an IPv4 address? */
699 if (!isalpha(name[0]))
700 {
701 addr = GH_IPV4;
702 goto act;
703 }
704
705 addr = GH_RFC1123_DNS;
706
707 /* Broken out in case we want to get fancy later */
708 act:
709 switch(addr)
710 {
711 case GH_IPV6:
712 WSASetLastError(STATUS_NOT_IMPLEMENTED);
713 return NULL;
714 break;
715
716 case GH_INVALID:
717 WSASetLastError(WSAEFAULT);
718 return NULL;
719 break;
720
721 /* Note: If passed an IP address, MSDN says that gethostbyname()
722 treats it as an unknown host.
723 This is different from the unix implementation. Use inet_addr()
724 */
725 case GH_IPV4:
726 case GH_RFC1123_DNS:
727 /* DNS_TYPE_A: include/WinDNS.h */
728 /* DnsQuery -- lib/dnsapi/dnsapi/query.c */
729 dns_status = DnsQuery_A(name,
730 DNS_TYPE_A,
731 DNS_QUERY_STANDARD,
732 0,
733 /* extra dns servers */ &dp,
734 0);
735
736 if(dns_status == 0)
737 {
738 //ASSERT(dp->wType == DNS_TYPE_A);
739 //ASSERT(dp->wDataLength == sizeof(DNS_A_DATA));
740 PDNS_RECORD curr;
741 for(curr=dp;
742 curr != NULL && curr->wType != DNS_TYPE_A;
743 curr = curr->pNext )
744 {
745 WS_DbgPrint(MID_TRACE,("wType: %i\n", curr->wType));
746 /*empty */
747 }
748
749 if(curr)
750 {
751 WS_DbgPrint(MID_TRACE,("populating hostent\n"));
752 WS_DbgPrint(MID_TRACE,("pName is (%s)\n", curr->pName));
753 populate_hostent(p->Hostent, (PCHAR)curr->pName, curr->Data.A);
754 DnsRecordListFree(dp, DnsFreeRecordList);
755 return p->Hostent;
756 }
757 else
758 {
759 DnsRecordListFree(dp, DnsFreeRecordList);
760 }
761 }
762
763 WS_DbgPrint(MID_TRACE,("Called DnsQuery, but host not found. Err: %i\n",
764 dns_status));
765 WSASetLastError(WSAHOST_NOT_FOUND);
766 return NULL;
767
768 break;
769
770 default:
771 WSASetLastError(WSANO_RECOVERY);
772 return NULL;
773 break;
774 }
775
776 WSASetLastError(WSANO_RECOVERY);
777 return NULL;
778 }
779
780 /*
781 * @implemented
782 */
783 INT
784 EXPORT
785 gethostname(OUT CHAR FAR* name,
786 IN INT namelen)
787 {
788 DWORD size = namelen;
789
790 int ret = GetComputerNameExA(ComputerNameDnsHostname,
791 name,
792 &size);
793 if(ret == 0)
794 {
795 WSASetLastError(WSAEFAULT);
796 return SOCKET_ERROR;
797 }
798 else
799 {
800 name[namelen-1] = '\0';
801 return 0;
802 }
803 }
804
805
806 /*
807 * XXX arty -- Partial implementation pending a better one. This one will
808 * do for normal purposes.#include <ws2_32.h>
809 *
810 * Return the address of a static LPPROTOENT corresponding to the named
811 * protocol. These structs aren't very interesting, so I'm not too ashamed
812 * to have this function work on builtins for now.
813 *
814 * @unimplemented
815 */
816
817 static CHAR *no_aliases = 0;
818 static PROTOENT protocols[] =
819 {
820 {"icmp",&no_aliases, IPPROTO_ICMP},
821 {"tcp", &no_aliases, IPPROTO_TCP},
822 {"udp", &no_aliases, IPPROTO_UDP},
823 {NULL, NULL, 0}
824 };
825
826 LPPROTOENT
827 EXPORT
828 getprotobyname(IN CONST CHAR FAR* name)
829 {
830 UINT i;
831 for (i = 0; protocols[i].p_name; i++)
832 {
833 if (_stricmp(protocols[i].p_name, name) == 0)
834 return &protocols[i];
835 }
836 return NULL;
837 }
838
839 /*
840 * @unimplemented
841 */
842 LPPROTOENT
843 EXPORT
844 getprotobynumber(IN INT number)
845 {
846 UINT i;
847 for (i = 0; protocols[i].p_name; i++)
848 {
849 if (protocols[i].p_proto == number)
850 return &protocols[i];
851 }
852 return NULL;
853 }
854
855 #define SKIPWS(ptr,act) \
856 {while(*ptr && isspace(*ptr)) ptr++; if(!*ptr) act;}
857 #define SKIPANDMARKSTR(ptr,act) \
858 {while(*ptr && !isspace(*ptr)) ptr++; \
859 if(!*ptr) {act;} else { *ptr = 0; ptr++; }}
860
861
862 static BOOL
863 DecodeServEntFromString(IN PCHAR ServiceString,
864 OUT PCHAR *ServiceName,
865 OUT PCHAR *PortNumberStr,
866 OUT PCHAR *ProtocolStr,
867 IN PCHAR *Aliases,
868 IN DWORD MaxAlias)
869 {
870 UINT NAliases = 0;
871
872 WS_DbgPrint(MAX_TRACE, ("Parsing service ent [%s]\n", ServiceString));
873
874 SKIPWS(ServiceString, return FALSE);
875 *ServiceName = ServiceString;
876 SKIPANDMARKSTR(ServiceString, return FALSE);
877 SKIPWS(ServiceString, return FALSE);
878 *PortNumberStr = ServiceString;
879 SKIPANDMARKSTR(ServiceString, ;);
880
881 while( *ServiceString && NAliases < MaxAlias - 1 )
882 {
883 SKIPWS(ServiceString, break);
884 if( *ServiceString )
885 {
886 SKIPANDMARKSTR(ServiceString, ;);
887 if( strlen(ServiceString) )
888 {
889 WS_DbgPrint(MAX_TRACE, ("Alias: %s\n", ServiceString));
890 *Aliases++ = ServiceString;
891 NAliases++;
892 }
893 }
894 }
895 *Aliases = NULL;
896
897 *ProtocolStr = strchr(*PortNumberStr,'/');
898 if( !*ProtocolStr ) return FALSE;
899 **ProtocolStr = 0; (*ProtocolStr)++;
900
901 WS_DbgPrint(MAX_TRACE, ("Parsing done: %s %s %s %d\n",
902 *ServiceName, *ProtocolStr, *PortNumberStr,
903 NAliases));
904
905 return TRUE;
906 }
907
908 #define ADJ_PTR(p,b1,b2) p = (p - b1) + b2
909
910 /*
911 * @implemented
912 */
913 LPSERVENT
914 EXPORT
915 getservbyname(IN CONST CHAR FAR* name,
916 IN CONST CHAR FAR* proto)
917 {
918 BOOL Found = FALSE;
919 HANDLE ServicesFile;
920 CHAR ServiceDBData[BUFSIZ] = { 0 };
921 PCHAR SystemDirectory = ServiceDBData; /* Reuse this stack space */
922 PCHAR ServicesFileLocation = "\\drivers\\etc\\services";
923 PCHAR ThisLine = 0, NextLine = 0, ServiceName = 0, PortNumberStr = 0,
924 ProtocolStr = 0, Comment = 0;
925 PCHAR Aliases[WS2_INTERNAL_MAX_ALIAS] = { 0 };
926 UINT i,SizeNeeded = 0,
927 SystemDirSize = sizeof(ServiceDBData) - 1;
928 DWORD ReadSize = 0, ValidData = 0;
929 PWINSOCK_THREAD_BLOCK p = NtCurrentTeb()->WinSockData;
930
931 if( !p )
932 {
933 WSASetLastError( WSANOTINITIALISED );
934 return NULL;
935 }
936
937 if( !name )
938 {
939 WSASetLastError( WSANO_RECOVERY );
940 return NULL;
941 }
942
943 if( !GetSystemDirectoryA( SystemDirectory, SystemDirSize ) )
944 {
945 WSASetLastError( WSANO_RECOVERY );
946 WS_DbgPrint(MIN_TRACE, ("Could not get windows system directory.\n"));
947 return NULL; /* Can't get system directory */
948 }
949
950 strncat(SystemDirectory,
951 ServicesFileLocation,
952 SystemDirSize );
953
954 ServicesFile = CreateFileA(SystemDirectory,
955 GENERIC_READ,
956 FILE_SHARE_READ,
957 NULL,
958 OPEN_EXISTING,
959 FILE_ATTRIBUTE_NORMAL | FILE_FLAG_SEQUENTIAL_SCAN,
960 NULL );
961
962 if( ServicesFile == INVALID_HANDLE_VALUE )
963 {
964 WSASetLastError( WSANO_RECOVERY );
965 return NULL;
966 }
967
968 /* Scan the services file ...
969 *
970 * We will read up to BUFSIZ bytes per pass, until the buffer does not
971 * contain a full line, then we will try to read more.
972 *
973 * We fall from the loop if the buffer does not have a line terminator.
974 */
975
976 /* Initial Read */
977 while(!Found &&
978 ReadFile(ServicesFile,
979 ServiceDBData + ValidData,
980 sizeof( ServiceDBData ) - ValidData,
981 &ReadSize,
982 NULL))
983 {
984 ValidData += ReadSize;
985 ReadSize = 0;
986 NextLine = ThisLine = ServiceDBData;
987
988 /* Find the beginning of the next line */
989 while(NextLine < ServiceDBData + ValidData &&
990 *NextLine != '\r' && *NextLine != '\n' )
991 {
992 NextLine++;
993 }
994
995 /* Zero and skip, so we can treat what we have as a string */
996 if( NextLine >= ServiceDBData + ValidData )
997 break;
998
999 *NextLine = 0; NextLine++;
1000
1001 Comment = strchr( ThisLine, '#' );
1002 if( Comment ) *Comment = 0; /* Terminate at comment start */
1003
1004 if(DecodeServEntFromString(ThisLine,
1005 &ServiceName,
1006 &PortNumberStr,
1007 &ProtocolStr,
1008 Aliases,
1009 WS2_INTERNAL_MAX_ALIAS) &&
1010 !strcmp( ServiceName, name ) &&
1011 (proto ? !strcmp( ProtocolStr, proto ) : TRUE) )
1012 {
1013
1014 WS_DbgPrint(MAX_TRACE,("Found the service entry.\n"));
1015 Found = TRUE;
1016 SizeNeeded = sizeof(WINSOCK_GETSERVBYNAME_CACHE) +
1017 (NextLine - ThisLine);
1018 break;
1019 }
1020
1021 /* Get rid of everything we read so far */
1022 while( NextLine <= ServiceDBData + ValidData &&
1023 isspace( *NextLine ) )
1024 {
1025 NextLine++;
1026 }
1027
1028 WS_DbgPrint(MAX_TRACE,("About to move %d chars\n",
1029 ServiceDBData + ValidData - NextLine));
1030
1031 memmove(ServiceDBData,
1032 NextLine,
1033 ServiceDBData + ValidData - NextLine );
1034 ValidData -= NextLine - ServiceDBData;
1035 WS_DbgPrint(MAX_TRACE,("Valid bytes: %d\n", ValidData));
1036 }
1037
1038 /* This we'll do no matter what */
1039 CloseHandle( ServicesFile );
1040
1041 if( !Found )
1042 {
1043 WS_DbgPrint(MAX_TRACE,("Not found\n"));
1044 WSASetLastError( WSANO_DATA );
1045 return NULL;
1046 }
1047
1048 if( !p->Getservbyname || p->Getservbyname->Size < SizeNeeded )
1049 {
1050 /* Free previous getservbyname buffer, allocate bigger */
1051 if( p->Getservbyname )
1052 HeapFree(GlobalHeap, 0, p->Getservbyname);
1053 p->Getservbyname = HeapAlloc(GlobalHeap, 0, SizeNeeded);
1054 if( !p->Getservbyname )
1055 {
1056 WS_DbgPrint(MIN_TRACE,("Couldn't allocate %d bytes\n",
1057 SizeNeeded));
1058 WSASetLastError( WSATRY_AGAIN );
1059 return NULL;
1060 }
1061 p->Getservbyname->Size = SizeNeeded;
1062 }
1063
1064 /* Copy the data */
1065 memmove(p->Getservbyname->Data,
1066 ThisLine,
1067 NextLine - ThisLine );
1068
1069 ADJ_PTR(ServiceName,ThisLine,p->Getservbyname->Data);
1070 ADJ_PTR(ProtocolStr,ThisLine,p->Getservbyname->Data);
1071 WS_DbgPrint(MAX_TRACE, ("ServiceName: %s, Protocol: %s\n",
1072 ServiceName,
1073 ProtocolStr));
1074
1075 for( i = 0; Aliases[i]; i++ )
1076 {
1077 ADJ_PTR(Aliases[i],ThisLine,p->Getservbyname->Data);
1078 WS_DbgPrint(MAX_TRACE,("Aliase %d: %s\n", i, Aliases[i]));
1079 }
1080
1081 memcpy(p->Getservbyname,
1082 Aliases,
1083 sizeof(Aliases));
1084
1085 /* Create the struct proper */
1086 p->Getservbyname->ServerEntry.s_name = ServiceName;
1087 p->Getservbyname->ServerEntry.s_aliases = p->Getservbyname->Aliases;
1088 p->Getservbyname->ServerEntry.s_port = htons(atoi(PortNumberStr));
1089 p->Getservbyname->ServerEntry.s_proto = ProtocolStr;
1090
1091 return &p->Getservbyname->ServerEntry;
1092 }
1093
1094
1095 /*
1096 * @implemented
1097 */
1098 LPSERVENT
1099 EXPORT
1100 getservbyport(IN INT port,
1101 IN CONST CHAR FAR* proto)
1102 {
1103 BOOL Found = FALSE;
1104 HANDLE ServicesFile;
1105 CHAR ServiceDBData[BUFSIZ] = { 0 };
1106 PCHAR SystemDirectory = ServiceDBData; /* Reuse this stack space */
1107 PCHAR ServicesFileLocation = "\\drivers\\etc\\services";
1108 PCHAR ThisLine = 0, NextLine = 0, ServiceName = 0, PortNumberStr = 0,
1109 ProtocolStr = 0, Comment = 0;
1110 PCHAR Aliases[WS2_INTERNAL_MAX_ALIAS] = { 0 };
1111 UINT i,SizeNeeded = 0,
1112 SystemDirSize = sizeof(ServiceDBData) - 1;
1113 DWORD ReadSize = 0, ValidData = 0;
1114 PWINSOCK_THREAD_BLOCK p = NtCurrentTeb()->WinSockData;
1115
1116 if( !p )
1117 {
1118 WSASetLastError( WSANOTINITIALISED );
1119 return NULL;
1120 }
1121
1122 if ( !port )
1123 {
1124 WSASetLastError( WSANO_RECOVERY );
1125 return NULL;
1126 }
1127
1128 if( !GetSystemDirectoryA( SystemDirectory, SystemDirSize ) )
1129 {
1130 WSASetLastError( WSANO_RECOVERY );
1131 WS_DbgPrint(MIN_TRACE, ("Could not get windows system directory.\n"));
1132 return NULL; /* Can't get system directory */
1133 }
1134
1135 strncat(SystemDirectory,
1136 ServicesFileLocation,
1137 SystemDirSize );
1138
1139 ServicesFile = CreateFileA(SystemDirectory,
1140 GENERIC_READ,
1141 FILE_SHARE_READ,
1142 NULL,
1143 OPEN_EXISTING,
1144 FILE_ATTRIBUTE_NORMAL | FILE_FLAG_SEQUENTIAL_SCAN,
1145 NULL );
1146
1147 if( ServicesFile == INVALID_HANDLE_VALUE )
1148 {
1149 WSASetLastError( WSANO_RECOVERY );
1150 return NULL;
1151 }
1152
1153 /* Scan the services file ...
1154 *
1155 * We will read up to BUFSIZ bytes per pass, until the buffer does not
1156 * contain a full line, then we will try to read more.
1157 *
1158 * We fall from the loop if the buffer does not have a line terminator.
1159 */
1160
1161 /* Initial Read */
1162 while(!Found &&
1163 ReadFile(ServicesFile,
1164 ServiceDBData + ValidData,
1165 sizeof( ServiceDBData ) - ValidData,
1166 &ReadSize, NULL ) )
1167 {
1168 ValidData += ReadSize;
1169 ReadSize = 0;
1170 NextLine = ThisLine = ServiceDBData;
1171
1172 /* Find the beginning of the next line */
1173 while( NextLine < ServiceDBData + ValidData &&
1174 *NextLine != '\r' && *NextLine != '\n' ) NextLine++;
1175
1176 /* Zero and skip, so we can treat what we have as a string */
1177 if( NextLine >= ServiceDBData + ValidData )
1178 break;
1179
1180 *NextLine = 0; NextLine++;
1181
1182 Comment = strchr( ThisLine, '#' );
1183 if( Comment ) *Comment = 0; /* Terminate at comment start */
1184
1185 if(DecodeServEntFromString(ThisLine,
1186 &ServiceName,
1187 &PortNumberStr,
1188 &ProtocolStr,
1189 Aliases,
1190 WS2_INTERNAL_MAX_ALIAS ) &&
1191 (htons(atoi( PortNumberStr )) == port ) &&
1192 (proto ? !strcmp( ProtocolStr, proto ) : TRUE) )
1193 {
1194
1195 WS_DbgPrint(MAX_TRACE,("Found the port entry.\n"));
1196
1197 Found = TRUE;
1198 SizeNeeded = sizeof(WINSOCK_GETSERVBYPORT_CACHE) +
1199 (NextLine - ThisLine);
1200 break;
1201 }
1202
1203 /* Get rid of everything we read so far */
1204 while( NextLine <= ServiceDBData + ValidData &&
1205 isspace( *NextLine ) )
1206 {
1207 NextLine++;
1208 }
1209
1210 WS_DbgPrint(MAX_TRACE,("About to move %d chars\n",
1211 ServiceDBData + ValidData - NextLine));
1212
1213 memmove(ServiceDBData,
1214 NextLine,
1215 ServiceDBData + ValidData - NextLine );
1216 ValidData -= NextLine - ServiceDBData;
1217 WS_DbgPrint(MAX_TRACE,("Valid bytes: %d\n", ValidData));
1218 }
1219
1220 /* This we'll do no matter what */
1221 CloseHandle( ServicesFile );
1222
1223 if( !Found )
1224 {
1225 WS_DbgPrint(MAX_TRACE,("Not found\n"));
1226 WSASetLastError( WSANO_DATA );
1227 return NULL;
1228 }
1229
1230 if( !p->Getservbyport || p->Getservbyport->Size < SizeNeeded )
1231 {
1232 /* Free previous getservbyport buffer, allocate bigger */
1233 if( p->Getservbyport )
1234 HeapFree(GlobalHeap, 0, p->Getservbyport);
1235 p->Getservbyport = HeapAlloc(GlobalHeap,
1236 0,
1237 SizeNeeded);
1238 if( !p->Getservbyport )
1239 {
1240 WS_DbgPrint(MIN_TRACE,("Couldn't allocate %d bytes\n",
1241 SizeNeeded));
1242 WSASetLastError( WSATRY_AGAIN );
1243 return NULL;
1244 }
1245 p->Getservbyport->Size = SizeNeeded;
1246 }
1247 /* Copy the data */
1248 memmove(p->Getservbyport->Data,
1249 ThisLine,
1250 NextLine - ThisLine );
1251
1252 ADJ_PTR(PortNumberStr,ThisLine,p->Getservbyport->Data);
1253 ADJ_PTR(ProtocolStr,ThisLine,p->Getservbyport->Data);
1254 WS_DbgPrint(MAX_TRACE, ("Port Number: %s, Protocol: %s\n",
1255 PortNumberStr, ProtocolStr));
1256
1257 for( i = 0; Aliases[i]; i++ )
1258 {
1259 ADJ_PTR(Aliases[i],ThisLine,p->Getservbyport->Data);
1260 WS_DbgPrint(MAX_TRACE,("Aliases %d: %s\n", i, Aliases[i]));
1261 }
1262
1263 memcpy(p->Getservbyport,Aliases,sizeof(Aliases));
1264
1265 /* Create the struct proper */
1266 p->Getservbyport->ServerEntry.s_name = ServiceName;
1267 p->Getservbyport->ServerEntry.s_aliases = p->Getservbyport->Aliases;
1268 p->Getservbyport->ServerEntry.s_port = port;
1269 p->Getservbyport->ServerEntry.s_proto = ProtocolStr;
1270
1271 WS_DbgPrint(MID_TRACE,("s_name: %s\n", ServiceName));
1272
1273 return &p->Getservbyport->ServerEntry;
1274
1275 }
1276
1277
1278 /*
1279 * @implemented
1280 */
1281 ULONG
1282 EXPORT
1283 inet_addr(IN CONST CHAR FAR* cp)
1284 /*
1285 * FUNCTION: Converts a string containing an IPv4 address to an unsigned long
1286 * ARGUMENTS:
1287 * cp = Pointer to string with address to convert
1288 * RETURNS:
1289 * Binary representation of IPv4 address, or INADDR_NONE
1290 */
1291 {
1292 UINT i;
1293 PCHAR p;
1294 ULONG u = 0;
1295
1296 p = (PCHAR)cp;
1297
1298 if (!p)
1299 {
1300 WSASetLastError(WSAEFAULT);
1301 return INADDR_NONE;
1302 }
1303
1304 if (strlen(p) == 0)
1305 return INADDR_NONE;
1306
1307 if (strcmp(p, " ") == 0)
1308 return 0;
1309
1310 for (i = 0; i <= 3; i++)
1311 {
1312 u += (strtoul(p, &p, 0) << (i * 8));
1313
1314 if (strlen(p) == 0)
1315 return u;
1316
1317 if (p[0] != '.')
1318 return INADDR_NONE;
1319
1320 p++;
1321 }
1322
1323 return u;
1324 }
1325
1326
1327 /*
1328 * @implemented
1329 */
1330 CHAR FAR*
1331 EXPORT
1332 inet_ntoa(IN IN_ADDR in)
1333 {
1334 CHAR b[10];
1335 PCHAR p;
1336
1337 p = ((PWINSOCK_THREAD_BLOCK)NtCurrentTeb()->WinSockData)->Intoa;
1338 _itoa(in.S_un.S_addr & 0xFF, b, 10);
1339 strcpy(p, b);
1340 _itoa((in.S_un.S_addr >> 8) & 0xFF, b, 10);
1341 strcat(p, ".");
1342 strcat(p, b);
1343 _itoa((in.S_un.S_addr >> 16) & 0xFF, b, 10);
1344 strcat(p, ".");
1345 strcat(p, b);
1346 _itoa((in.S_un.S_addr >> 24) & 0xFF, b, 10);
1347 strcat(p, ".");
1348 strcat(p, b);
1349
1350 return (CHAR FAR*)p;
1351 }
1352
1353
1354 /*
1355 * @implemented
1356 */
1357 VOID
1358 EXPORT
1359 freeaddrinfo(struct addrinfo *pAddrInfo)
1360 {
1361 struct addrinfo *next, *cur;
1362 cur = pAddrInfo;
1363 while (cur)
1364 {
1365 next = cur->ai_next;
1366 if (cur->ai_addr)
1367 HeapFree(GetProcessHeap(), 0, cur->ai_addr);
1368 if (cur->ai_canonname)
1369 HeapFree(GetProcessHeap(), 0, cur->ai_canonname);
1370 HeapFree(GetProcessHeap(), 0, cur);
1371 cur = next;
1372 }
1373 }
1374
1375
1376 struct addrinfo *
1377 new_addrinfo(struct addrinfo *prev)
1378 {
1379 struct addrinfo *ret = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(struct addrinfo));
1380 if (prev)
1381 prev->ai_next = ret;
1382 return ret;
1383 }
1384
1385 /*
1386 * @implemented
1387 */
1388 INT
1389 EXPORT
1390 getaddrinfo(const char FAR * nodename,
1391 const char FAR * servname,
1392 const struct addrinfo FAR * hints,
1393 struct addrinfo FAR * FAR * res)
1394 {
1395 struct addrinfo *ret = NULL, *ai;
1396 ULONG addr;
1397 USHORT port;
1398 struct servent *se;
1399 char *proto;
1400 LPPROTOENT pent;
1401 DNS_STATUS dns_status;
1402 PDNS_RECORD dp, currdns;
1403 struct sockaddr_in *sin;
1404
1405 if (res == NULL)
1406 return WSAEINVAL;
1407 if (nodename == NULL && servname == NULL)
1408 return WSAHOST_NOT_FOUND;
1409
1410 if (!WSAINITIALIZED)
1411 return WSANOTINITIALISED;
1412
1413 if (servname)
1414 {
1415 /* converting port number */
1416 port = strtoul(servname, NULL, 10);
1417 /* service name was specified? */
1418 if (port == 0)
1419 {
1420 /* protocol was specified? */
1421 if (hints && hints->ai_protocol)
1422 {
1423 pent = getprotobynumber(hints->ai_protocol);
1424 if (pent == NULL)
1425 return WSAEINVAL;
1426 proto = pent->p_name;
1427 }
1428 else
1429 proto = NULL;
1430 se = getservbyname(servname, proto);
1431 if (se == NULL)
1432 return WSATYPE_NOT_FOUND;
1433 port = se->s_port;
1434 }
1435 else
1436 port = htons(port);
1437 }
1438 else
1439 port = 0;
1440
1441 if (nodename)
1442 {
1443 /* Is it an IPv6 address? */
1444 if (strstr(nodename, ":"))
1445 return WSAHOST_NOT_FOUND;
1446
1447 /* Is it an IPv4 address? */
1448 addr = inet_addr(nodename);
1449 if (addr != INADDR_NONE)
1450 {
1451 ai = new_addrinfo(NULL);
1452 ai->ai_family = PF_INET;
1453 ai->ai_addrlen = sizeof(struct sockaddr_in);
1454 ai->ai_addr = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, ai->ai_addrlen);
1455 sin = (struct sockaddr_in *)ai->ai_addr;
1456 sin->sin_family = AF_INET;
1457 sin->sin_port = port;
1458 RtlCopyMemory(&sin->sin_addr, &addr, sizeof(sin->sin_addr));
1459 if (hints)
1460 {
1461 if (ai->ai_socktype == 0)
1462 ai->ai_socktype = hints->ai_socktype;
1463 if (ai->ai_protocol == 0)
1464 ai->ai_protocol = hints->ai_protocol;
1465 }
1466 ret = ai;
1467 }
1468 else
1469 {
1470 /* resolving host name */
1471 dns_status = DnsQuery_A(nodename,
1472 DNS_TYPE_A,
1473 DNS_QUERY_STANDARD,
1474 0,
1475 /* extra dns servers */ &dp,
1476 0);
1477
1478 if (dns_status == 0)
1479 {
1480 ai = NULL;
1481 for (currdns = dp; currdns; currdns = currdns->pNext )
1482 {
1483 /* accept only A records */
1484 if (currdns->wType != DNS_TYPE_A) continue;
1485
1486 ai = new_addrinfo(ai);
1487 if (ret == NULL)
1488 ret = ai;
1489 ai->ai_family = PF_INET;
1490 ai->ai_addrlen = sizeof(struct sockaddr_in);
1491 ai->ai_addr = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, ai->ai_addrlen);
1492 sin = (struct sockaddr_in *)ret->ai_addr;
1493 sin->sin_family = AF_INET;
1494 sin->sin_port = port;
1495 RtlCopyMemory(&sin->sin_addr, &currdns->Data.A.IpAddress, sizeof(sin->sin_addr));
1496 if (hints)
1497 {
1498 if (ai->ai_socktype == 0)
1499 ai->ai_socktype = hints->ai_socktype;
1500 if (ai->ai_protocol == 0)
1501 ai->ai_protocol = hints->ai_protocol;
1502 }
1503 }
1504 DnsRecordListFree(dp, DnsFreeRecordList);
1505 }
1506 }
1507 }
1508 else
1509 {
1510 ai = new_addrinfo(NULL);
1511 ai->ai_family = PF_INET;
1512 ai->ai_addrlen = sizeof(struct sockaddr_in);
1513 ai->ai_addr = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, ai->ai_addrlen);
1514 sin = (struct sockaddr_in *)ai->ai_addr;
1515 sin->sin_family = AF_INET;
1516 sin->sin_port = port;
1517 if (hints)
1518 {
1519 if (!(hints->ai_flags & AI_PASSIVE))
1520 {
1521 sin->sin_addr.S_un.S_un_b.s_b1 = 127;
1522 sin->sin_addr.S_un.S_un_b.s_b2 = 0;
1523 sin->sin_addr.S_un.S_un_b.s_b3 = 0;
1524 sin->sin_addr.S_un.S_un_b.s_b4 = 1;
1525 }
1526 if (ai->ai_socktype == 0)
1527 ai->ai_socktype = hints->ai_socktype;
1528 if (ai->ai_protocol == 0)
1529 ai->ai_protocol = hints->ai_protocol;
1530 }
1531 ret = ai;
1532 }
1533
1534 if (ret == NULL)
1535 return WSAHOST_NOT_FOUND;
1536
1537 if (hints && hints->ai_family != PF_UNSPEC && hints->ai_family != PF_INET)
1538 {
1539 freeaddrinfo(ret);
1540 return WSAEAFNOSUPPORT;
1541 }
1542
1543 *res = ret;
1544 return 0;
1545 }
1546
1547 /* EOF */