2 * COPYRIGHT: See COPYING in the top level directory
3 * PROJECT: ReactOS WinSock 2 API
5 * PURPOSE: Protocol-Independent Address Resolution
6 * PROGRAMMER: Alex Ionescu (alex@relsoft.net)
9 /* INCLUDES ******************************************************************/
15 /* DEFINES *******************************************************************/
17 #define Swap(a, b, c) { (c) = (a); (a) = (b); (b) = (c); }
18 #define FreeAddrInfoW(a) freeaddrinfo((LPADDRINFO)a)
20 /* FUNCTIONS *****************************************************************/
22 /* FIXME: put into dnslib */
25 Dns_Ip4AddressToReverseName_W(IN LPWSTR AddressBuffer
,
28 /* Convert the address into IPv4 format */
29 wsprintfW(AddressBuffer
, L
"%u.%u.%u.%u.in-addr.arpa.",
30 Address
.S_un
.S_un_b
.s_b4
,
31 Address
.S_un
.S_un_b
.s_b3
,
32 Address
.S_un
.S_un_b
.s_b2
,
33 Address
.S_un
.S_un_b
.s_b1
);
38 Dns_SplitHostFromDomainNameW(IN LPWSTR DomainName
)
46 ConvertAddrinfoFromUnicodeToAnsi(IN PADDRINFOW Addrinfo
)
51 /* Make sure we have a valid pointer */
57 UnicodeName
= &Addrinfo
->ai_canonname
;
59 /* Check if it exists */
63 AnsiName
= AnsiDupFromUnicode(*UnicodeName
);
66 /* Free the old one */
67 HeapFree(WsSockHeap
, 0, *UnicodeName
);
70 *UnicodeName
= (LPWSTR
)AnsiName
;
74 return GetLastError();
77 } while ((Addrinfo
= Addrinfo
->ai_next
));
87 ParseV4Address(IN PCWSTR AddressString
,
93 /* Do the conversion, don't accept wildcard */
94 RtlIpv4StringToAddressW((LPWSTR
)AddressString
, 0, &Ip
, (IN_ADDR
*)&Address
);
96 /* Return the address and success */
104 NewAddrInfo(IN INT SocketType
,
110 PSOCKADDR_IN SockAddress
;
112 /* Allocate a structure */
113 AddrInfo
= HeapAlloc(WsSockHeap
, 0, sizeof(ADDRINFOW
));
114 if (!AddrInfo
) return NULL
;
116 /* Allocate a sockaddr */
117 SockAddress
= HeapAlloc(WsSockHeap
, 0, sizeof(SOCKADDR_IN
));
120 /* Free the addrinfo and fail */
121 HeapFree(WsSockHeap
, 0, AddrInfo
);
125 /* Write data for socket address */
126 SockAddress
->sin_family
= AF_INET
;
127 SockAddress
->sin_port
= Port
;
128 SockAddress
->sin_addr
.s_addr
= Address
;
130 /* Fill out the addrinfo */
131 AddrInfo
->ai_family
= PF_INET
;
132 AddrInfo
->ai_socktype
= SocketType
;
133 AddrInfo
->ai_protocol
= Protocol
;
134 AddrInfo
->ai_flags
= 0;
135 AddrInfo
->ai_next
= 0;
136 AddrInfo
->ai_canonname
= NULL
;
137 AddrInfo
->ai_addrlen
= sizeof(SOCKADDR_IN
);
138 AddrInfo
->ai_addr
= (PSOCKADDR
)SockAddress
;
147 CloneAddrInfo(IN WORD Port
,
148 IN PADDRINFOW ptResult
)
150 PADDRINFOW Next
= NULL
;
151 PADDRINFOW New
= NULL
;
154 for (Next
= ptResult
; Next
;)
156 /* Create a new structure */
157 New
= NewAddrInfo(SOCK_DGRAM
,
160 ((PSOCKADDR_IN
)Next
->ai_addr
)->sin_addr
.s_addr
);
164 New
->ai_next
= Next
->ai_next
;
169 /* Check if we ran out of memory */
170 if (Next
) return EAI_MEMORY
;
179 QueryDNS(IN LPCSTR NodeName
,
183 OUT CHAR Alias
[NI_MAXHOST
],
184 OUT PADDRINFOW
*pptResult
)
186 PADDRINFOW
*Next
= pptResult
;
187 PHOSTENT Hostent
= NULL
;
190 /* Assume nothing found */
194 /* Get the hostent */
195 Hostent
= gethostbyname(NodeName
);
198 /* Check for valid addresses */
199 if ((Hostent
->h_addrtype
== AF_INET
) &&
200 (Hostent
->h_length
== sizeof(IN_ADDR
)))
202 /* Loop every address in it */
203 for (Addresses
= Hostent
->h_addr_list
; *Addresses
; Addresses
++)
205 /* Create an addrinfo structure for it*/
206 *Next
= NewAddrInfo(SocketType
,
209 ((PIN_ADDR
)*Addresses
)->s_addr
);
210 if (!*Next
) return EAI_MEMORY
;
212 /* Move to the next entry */
213 Next
= &((*Next
)->ai_next
);
217 /* Copy the canonical name */
218 strcpy(Alias
, Hostent
->h_name
);
224 /* Find out what the error was */
225 switch (GetLastError())
227 /* Convert the Winsock Error to an EAI error */
228 case WSAHOST_NOT_FOUND
: return EAI_NONAME
;
229 case WSATRY_AGAIN
: return EAI_AGAIN
;
230 case WSANO_RECOVERY
: return EAI_FAIL
;
231 case WSANO_DATA
: return EAI_NODATA
;
232 default: return EAI_NONAME
;
239 LookupNodeByAddr(IN LPWSTR pNodeBuffer
,
240 IN DWORD NodeBufferSize
,
241 IN BOOLEAN OnlyNodeName
,
244 IN INT AddressFamily
)
246 GUID LookupGuid
= SVCID_DNS_TYPE_PTR
;
247 PIN_ADDR Ip4Addr
= Addr
;
248 WCHAR ReverseBuffer
[76];
249 WSAQUERYSETW Restrictions
, Reply
;
254 /* Validate the address */
255 if (!Addr
) return WSAEFAULT
;
257 /* Make sure the family and address size match */
258 if (AddressFamily
== AF_INET6
)
260 /* Check the address size for this type */
261 if (AddrSize
!= sizeof(IN6_ADDR
)) return WSAEFAULT
;
262 Ip4Addr
= (PIN_ADDR
)&((PIN6_ADDR
)Addr
)->u
.Byte
[12];
264 else if (AddressFamily
== AF_INET
)
266 /* Check the address size for this type */
267 if (AddrSize
!= sizeof(IN_ADDR
)) return WSAEFAULT
;
271 /* Address family not supported */
272 return WSAEAFNOSUPPORT
;
275 /* Check if this is a mapped V4 IPv6 or pure IPv4 */
276 if (((AddressFamily
== AF_INET6
) && (IN6_IS_ADDR_V4MAPPED(Addr
))) ||
277 (AddressFamily
== AF_INET
))
279 /* Get the reverse name */
280 Dns_Ip4AddressToReverseName_W(ReverseBuffer
, *Ip4Addr
);
282 /* FIXME: Not implemented for now
285 /* By this point we have the Reverse Name, so prepare for lookup */
286 RtlZeroMemory(&Restrictions
, sizeof(Restrictions
));
287 Restrictions
.dwSize
= sizeof(Restrictions
);
288 Restrictions
.lpszServiceInstanceName
= ReverseBuffer
;
289 Restrictions
.lpServiceClassId
= &LookupGuid
;
290 Restrictions
.dwNameSpace
= NS_DNS
;
292 /* Now do the lookup */
293 ErrorCode
= WSALookupServiceBeginW(&Restrictions
,
296 if (ErrorCode
== ERROR_SUCCESS
)
298 /* Lookup succesfull, now get the data */
299 BufferLength
= (NI_MAXHOST
- 1) * sizeof(WCHAR
) + sizeof(Restrictions
);
300 ErrorCode
= WSALookupServiceNextW(LookupHandle
,
304 if (ErrorCode
== ERROR_SUCCESS
)
306 /* Now check if we have a name back */
307 Reply
= Restrictions
;
308 if (!Reply
.lpszServiceInstanceName
)
311 ErrorCode
= WSAHOST_NOT_FOUND
;
315 /* Check if the caller only wants the node name */
318 /* Split it and get only the partial host name */
319 Dns_SplitHostFromDomainNameW(Reply
.lpszServiceInstanceName
);
322 /* Check the length and see if it's within our limit */
323 if (wcslen(Reply
.lpszServiceInstanceName
) + 1 >
326 /* It's not, so fail */
327 ErrorCode
= WSAEFAULT
;
331 /* It will fit, let's copy it*/
332 wcscpy(pNodeBuffer
, Reply
.lpszServiceInstanceName
);
337 else if (ErrorCode
== WSASERVICE_NOT_FOUND
)
339 /* Normalize the error code */
340 ErrorCode
= WSAHOST_NOT_FOUND
;
343 /* Finish the lookup if one was in progress */
344 if (LookupHandle
) WSALookupServiceEnd(LookupHandle
);
346 /* Return the error code */
353 GetServiceNameForPort(IN LPWSTR pServiceBuffer
,
354 IN DWORD ServiceBufferSize
,
358 return ERROR_SUCCESS
;
364 LookupAddressForName(IN LPCSTR NodeName
,
368 IN BOOL bAI_CANONNAME
,
369 OUT PADDRINFOW
*pptResult
)
373 CHAR szFQDN1
[NI_MAXHOST
] = "";
374 CHAR szFQDN2
[NI_MAXHOST
] = "";
375 PCHAR Name
= szFQDN1
;
376 PCHAR Alias
= szFQDN2
;
377 PCHAR Scratch
= NULL
;
379 /* Make a copy of the name */
380 strcpy(Name
, NodeName
);
385 /* Do a DNS Query for the name */
386 iError
= QueryDNS(NodeName
,
394 /* Exit if we have a result */
395 if (*pptResult
) break;
397 /* Don't loop continously if this is a DNS misconfiguration */
398 if ((!strlen(Alias
)) || (!strcmp(Name
, Alias
)) || (++AliasCount
== 16))
400 /* Exit the loop with a failure */
405 /* Restart loopup if we got a CNAME */
406 Swap(Name
, Alias
, Scratch
);
409 /* Check if we suceeded and the canonical name is requested */
410 if (!iError
&& bAI_CANONNAME
)
412 /* Allocate memory for a copy */
413 (*pptResult
)->ai_canonname
= HeapAlloc(WsSockHeap
, 0, 512);
415 /* Check if we had enough memory */
416 if (!(*pptResult
)->ai_canonname
)
418 /* Set failure code */
423 /* Convert the alias to UNICODE */
424 MultiByteToWideChar(CP_ACP
,
428 (*pptResult
)->ai_canonname
,
433 /* Return to caller */
442 GetAddrInfoW(IN PCWSTR pszNodeName
,
443 IN PCWSTR pszServiceName
,
444 IN
const ADDRINFOW
*ptHints
,
445 OUT PADDRINFOW
*pptResult
)
449 INT iFamily
= PF_UNSPEC
;
454 PSERVENT ptService
= NULL
;
459 WCHAR CanonicalName
[0x42];
460 CHAR AnsiServiceName
[256];
461 CHAR AnsiNodeName
[256];
462 DPRINT("GetAddrInfoW: %S, %S, %p, %p\n", pszNodeName
, pszServiceName
, ptHints
, pptResult
);
467 /* We must have at least one name to work with */
468 if (!(pszNodeName
) && !(pszServiceName
))
471 SetLastError(EAI_NONAME
);
475 /* Check if we got hints */
478 /* Make sure these are empty */
479 if ((ptHints
->ai_addrlen
) ||
480 (ptHints
->ai_canonname
) ||
481 (ptHints
->ai_addr
) ||
484 /* Fail if they aren't */
485 SetLastError(EAI_FAIL
);
489 /* Save the flags and validate them */
490 iFlags
= ptHints
->ai_flags
;
491 if ((iFlags
& AI_CANONNAME
) && !pszNodeName
)
496 /* Save family and validate it */
497 iFamily
= ptHints
->ai_family
;
498 if ((iFamily
!= PF_UNSPEC
) && (iFamily
!= PF_INET
))
503 /* Save socket type and validate it */
504 iSocketType
= ptHints
->ai_socktype
;
505 if ((iSocketType
!= 0) &&
506 (iSocketType
!= SOCK_STREAM
) &&
507 (iSocketType
!= SOCK_DGRAM
) &&
508 (iSocketType
!= SOCK_RAW
))
513 /* Save the protocol */
514 iProtocol
= ptHints
->ai_protocol
;
517 /* Check if we have a service name */
520 /* We need to convert it to ANSI */
521 WideCharToMultiByte(CP_ACP
,
531 wPort
= (WORD
)strtoul(AnsiServiceName
, &pc
, 10);
533 /* Check if the port string is numeric */
536 /* Get the port directly */
537 wPort
= wTcpPort
= wUdpPort
= htons(wPort
);
539 /* Check if this is both TCP and UDP */
540 if (iSocketType
== 0)
542 /* Set it to TCP for now, but remember to clone */
544 iSocketType
= SOCK_STREAM
;
549 /* The port name was a string. Check if this is a UDP socket */
550 if ((iSocketType
== 0) || (iSocketType
== SOCK_DGRAM
))
552 /* It's UDP, do a getservbyname */
553 ptService
= getservbyname(AnsiServiceName
, "udp");
555 /* If we got a servent, return the port from it */
556 if (ptService
) wPort
= wUdpPort
= ptService
->s_port
;
559 /* Check if this is a TCP socket */
560 if ((iSocketType
== 0) || (iSocketType
== SOCK_STREAM
))
562 /* It's TCP, do a getserbyname */
563 ptService
= getservbyname(AnsiServiceName
, "tcp");
565 /* Return the port from the servent */
566 if (ptService
) wPort
= wTcpPort
= ptService
->s_port
;
569 /* If we got 0, then fail */
572 return iSocketType
? EAI_SERVICE
: EAI_NONAME
;
575 /* Check if this was for both */
576 if (iSocketType
== 0)
578 /* Do the TCP case right now */
579 iSocketType
= (wTcpPort
) ? SOCK_STREAM
: SOCK_DGRAM
;
580 bClone
= (wTcpPort
&& wUdpPort
);
585 /* Check if no node was given or if this is is a valid IPv4 address */
586 if ((!pszNodeName
) || (ParseV4Address(pszNodeName
, &dwAddress
)))
588 /* Check if we don't have a node name */
591 /* Make one up based on the flags */
592 dwAddress
= htonl((iFlags
& AI_PASSIVE
) ?
593 INADDR_ANY
: INADDR_LOOPBACK
);
596 /* Create the Addr Info */
597 *pptResult
= NewAddrInfo(iSocketType
, iProtocol
, wPort
, dwAddress
);
599 /* If we didn't get one back, assume out of memory */
600 if (!(*pptResult
)) iError
= EAI_MEMORY
;
602 /* Check if we have success and a nodename */
603 if (!iError
&& pszNodeName
)
605 /* Set AI_NUMERICHOST since this is a numeric string */
606 (*pptResult
)->ai_flags
|= AI_NUMERICHOST
;
608 /* Check if the canonical name was requestd */
609 if (iFlags
& AI_CANONNAME
)
611 /* Get the canonical name */
612 GetNameInfoW((*pptResult
)->ai_addr
,
613 (socklen_t
)(*pptResult
)->ai_addrlen
,
620 /* Allocate memory for a copy */
621 (*pptResult
)->ai_canonname
= HeapAlloc(WsSockHeap
,
623 wcslen(CanonicalName
));
625 if (!(*pptResult
)->ai_canonname
)
627 /* No memory for the copy */
632 /* Duplicate the string */
633 RtlMoveMemory((*pptResult
)->ai_canonname
,
635 wcslen(CanonicalName
));
640 else if (iFlags
& AI_NUMERICHOST
)
642 /* No name for this request (we have a non-numeric name) */
647 /* We need to convert it to ANSI */
648 WideCharToMultiByte(CP_ACP
,
657 /* Non-numeric name, do DNS lookup */
658 iError
= LookupAddressForName(AnsiNodeName
,
662 (iFlags
& AI_CANONNAME
),
666 /* If all was good and the caller requested UDP and TCP */
667 if (!iError
&& bClone
)
669 /* Process UDP now, we already did TCP */
670 iError
= CloneAddrInfo(wUdpPort
, *pptResult
);
673 /* If we've hit an error till here */
676 /* Free the address info and return nothing */
677 FreeAddrInfoW(*pptResult
);
681 /* Return to caller */
691 freeaddrinfo(PADDRINFOA AddrInfo
)
695 /* Loop the chain of structures */
696 for (NextInfo
= AddrInfo
; NextInfo
; NextInfo
= AddrInfo
)
698 /* Check if there is a canonical name */
699 if (NextInfo
->ai_canonname
)
702 HeapFree(WsSockHeap
, 0, NextInfo
->ai_canonname
);
705 /* Check if there is an address */
706 if (NextInfo
->ai_addr
)
709 HeapFree(WsSockHeap
, 0, NextInfo
->ai_addr
);
712 /* Move to the next entry */
713 AddrInfo
= NextInfo
->ai_next
;
715 /* Free this entry */
716 HeapFree(WsSockHeap
, 0, NextInfo
);
726 getaddrinfo(const char FAR
*nodename
,
727 const char FAR
*servname
,
728 const struct addrinfo FAR
*hints
,
729 struct addrinfo FAR
* FAR
*res
)
732 LPWSTR UnicodeNodeName
;
733 LPWSTR UnicodeServName
= NULL
;
734 DPRINT("getaddrinfo: %s, %s, %p, %p\n", nodename
, servname
, hints
, res
);
736 /* Check for WSAStartup */
737 if ((ErrorCode
= WsQuickProlog()) != ERROR_SUCCESS
) return ErrorCode
;
742 /* Convert the node name */
743 UnicodeNodeName
= UnicodeDupFromAnsi((LPSTR
)nodename
);
744 if (!UnicodeNodeName
)
746 /* Prepare to fail */
747 ErrorCode
= GetLastError();
751 /* Convert the servname too, if we have one */
754 UnicodeServName
= UnicodeDupFromAnsi((LPSTR
)servname
);
755 if (!UnicodeServName
)
757 /* Prepare to fail */
758 ErrorCode
= GetLastError();
763 /* Now call the unicode function */
764 ErrorCode
= GetAddrInfoW(UnicodeNodeName
,
769 /* Convert it to ANSI if we suceeded */
770 if (ErrorCode
== ERROR_SUCCESS
) ConvertAddrinfoFromUnicodeToAnsi((PADDRINFOW
)*res
);
773 /* Check if we have a unicode node name and serv name */
774 if (UnicodeNodeName
) HeapFree(WsSockHeap
, 0, UnicodeNodeName
);
775 if (UnicodeServName
) HeapFree(WsSockHeap
, 0, UnicodeServName
);
777 /* Check if we are in error */
778 if (ErrorCode
!= ERROR_SUCCESS
)
780 /* Free the structure and return nothing */
785 /* Set the last error and return */
786 SetLastError(ErrorCode
);
795 GetNameInfoW(IN CONST SOCKADDR
*pSockaddr
,
796 IN socklen_t SockaddrLength
,
797 OUT PWCHAR pNodeBuffer
,
798 IN DWORD NodeBufferSize
,
799 OUT PWCHAR pServiceBuffer
,
800 IN DWORD ServiceBufferSize
,
803 DWORD AddressLength
, AddrSize
;
806 INT ErrorCode
= ERROR_SUCCESS
;
808 /* Check for valid socket */
809 if (!pSockaddr
) return EAI_FAIL
;
811 /* Check which family this is */
812 if (pSockaddr
->sa_family
== AF_INET
)
815 AddressLength
= sizeof(SOCKADDR_IN
);
816 Addr
= &((PSOCKADDR_IN
)pSockaddr
)->sin_addr
;
817 AddrSize
= sizeof(IN_ADDR
);
819 else if (pSockaddr
->sa_family
== AF_INET6
)
822 AddressLength
= sizeof(SOCKADDR_IN6
);
823 Addr
= &((PSOCKADDR_IN6
)pSockaddr
)->sin6_addr
;
824 AddrSize
= sizeof(IN6_ADDR
);
828 /* Unsupported family */
832 /* Check for valid socket adress length */
833 if ((DWORD
)SockaddrLength
< AddressLength
) return EAI_FAIL
;
835 /* Check if we have a node name */
838 /* Check if only the numeric host is wanted */
839 if (!(Flags
& NI_NUMERICHOST
))
841 /* Do the lookup by addr */
842 ErrorCode
= LookupNodeByAddr(pNodeBuffer
,
847 pSockaddr
->sa_family
);
848 /* Check if we failed */
849 if (ErrorCode
!= ERROR_SUCCESS
)
851 /* Fail if the caller REALLY wants the NAME itself? */
852 if (Flags
& NI_NAMEREQD
) goto quickie
;
856 /* We suceeded, no need to get the numeric address */
861 /* Copy the address */
862 RtlMoveMemory(&Address
, pSockaddr
, AddressLength
);
864 /* Get the numeric address */
865 Address
.sin_port
= 0;
866 ErrorCode
= WSAAddressToStringW((LPSOCKADDR
)&Address
,
871 if (ErrorCode
== SOCKET_ERROR
)
873 /* Get the error code and exit with it */
874 ErrorCode
= GetLastError();
880 /* Check if we got a service name */
883 /* Handle this request */
884 ErrorCode
= GetServiceNameForPort(pServiceBuffer
,
886 ((PSOCKADDR_IN
)pSockaddr
)->sin_port
,
890 /* Set the error and return it (or success) */
892 SetLastError(ErrorCode
);
902 getnameinfo(const struct sockaddr FAR
*sa
,
912 WCHAR ServiceBuffer
[17];
913 DWORD HostLength
= 0, ServLength
= 0;
914 PWCHAR ServiceString
= NULL
, HostString
= NULL
;
915 DPRINT("getaddrinfo: %p, %p, %p, %lx\n", host
, serv
, sa
, salen
);
917 /* Check for WSAStartup */
918 if ((ErrorCode
= WsQuickProlog()) != ERROR_SUCCESS
) return ErrorCode
;
920 /* Check if we have a host pointer */
923 /* Setup the data for it */
925 HostLength
= sizeof(Buffer
) / sizeof(WCHAR
);
928 /* Check if we have a service pointer */
931 /* Setup the data for it */
932 ServiceString
= ServiceBuffer
;
933 ServLength
= sizeof(ServiceBuffer
) - 1;
936 /* Now call the unicode function */
937 ErrorCode
= GetNameInfoW(sa
,
945 /* Check for success */
946 if (ErrorCode
== ERROR_SUCCESS
)
948 /* Check if we had a host pointer */
951 /* Convert it back to ANSI */
952 ErrorCode
= WideCharToMultiByte(CP_ACP
,
960 if (!ErrorCode
) goto Quickie
;
963 /* Check if we have a service pointer */
966 /* Convert it back to ANSI */
967 ErrorCode
= WideCharToMultiByte(CP_ACP
,
975 if (!ErrorCode
) goto Quickie
;
979 return ERROR_SUCCESS
;
982 /* Set the last error and return */
984 SetLastError(ErrorCode
);