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