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 ******************************************************************/
18 /* DEFINES *******************************************************************/
20 #define Swap(a, b, c) { (c) = (a); (a) = (b); (b) = (c); }
21 #define FreeAddrInfoW(a) freeaddrinfo((LPADDRINFO)a)
23 /* FUNCTIONS *****************************************************************/
25 /* FIXME: put into dnslib */
28 Dns_Ip4AddressToReverseName_W(IN LPWSTR AddressBuffer
,
31 /* Convert the address into IPv4 format */
32 wsprintfW(AddressBuffer
, L
"%u.%u.%u.%u.in-addr.arpa.",
33 Address
.S_un
.S_un_b
.s_b4
,
34 Address
.S_un
.S_un_b
.s_b3
,
35 Address
.S_un
.S_un_b
.s_b2
,
36 Address
.S_un
.S_un_b
.s_b1
);
41 Dns_SplitHostFromDomainNameW(IN LPWSTR DomainName
)
49 ConvertAddrinfoFromUnicodeToAnsi(IN PADDRINFOW Addrinfo
)
54 /* Make sure we have a valid pointer */
60 UnicodeName
= &Addrinfo
->ai_canonname
;
62 /* Check if it exists */
66 AnsiName
= AnsiDupFromUnicode(*UnicodeName
);
69 /* Free the old one */
70 HeapFree(WsSockHeap
, 0, *UnicodeName
);
73 *UnicodeName
= (LPWSTR
)AnsiName
;
77 return GetLastError();
80 } while ((Addrinfo
= Addrinfo
->ai_next
));
90 ParseV4Address(IN PCWSTR AddressString
,
98 Status
= RtlIpv4StringToAddressW(AddressString
, FALSE
, &Terminator
, &Address
);
100 if (!NT_SUCCESS(Status
))
103 *pAddress
= Address
.S_un
.S_addr
;
110 NewAddrInfo(IN INT SocketType
,
116 PSOCKADDR_IN SockAddress
;
118 /* Allocate a structure */
119 AddrInfo
= HeapAlloc(WsSockHeap
, 0, sizeof(ADDRINFOW
));
120 if (!AddrInfo
) return NULL
;
122 /* Allocate a sockaddr */
123 SockAddress
= HeapAlloc(WsSockHeap
, 0, sizeof(SOCKADDR_IN
));
126 /* Free the addrinfo and fail */
127 HeapFree(WsSockHeap
, 0, AddrInfo
);
131 /* Write data for socket address */
132 SockAddress
->sin_family
= AF_INET
;
133 SockAddress
->sin_port
= Port
;
134 SockAddress
->sin_addr
.s_addr
= Address
;
136 /* Fill out the addrinfo */
137 AddrInfo
->ai_family
= PF_INET
;
138 AddrInfo
->ai_socktype
= SocketType
;
139 AddrInfo
->ai_protocol
= Protocol
;
140 AddrInfo
->ai_flags
= 0;
141 AddrInfo
->ai_next
= 0;
142 AddrInfo
->ai_canonname
= NULL
;
143 AddrInfo
->ai_addrlen
= sizeof(SOCKADDR_IN
);
144 AddrInfo
->ai_addr
= (PSOCKADDR
)SockAddress
;
153 CloneAddrInfo(IN WORD Port
,
154 IN PADDRINFOW ptResult
)
156 PADDRINFOW Next
= NULL
;
157 PADDRINFOW New
= NULL
;
160 for (Next
= ptResult
; Next
;)
162 /* Create a new structure */
163 New
= NewAddrInfo(SOCK_DGRAM
,
166 ((PSOCKADDR_IN
)Next
->ai_addr
)->sin_addr
.s_addr
);
170 New
->ai_next
= Next
->ai_next
;
175 /* Check if we ran out of memory */
176 if (Next
) return EAI_MEMORY
;
185 QueryDNS(IN LPCSTR NodeName
,
189 OUT CHAR Alias
[NI_MAXHOST
],
190 OUT PADDRINFOW
*pptResult
)
192 PADDRINFOW
*Next
= pptResult
;
193 PHOSTENT Hostent
= NULL
;
196 /* Assume nothing found */
200 /* Get the hostent */
201 Hostent
= gethostbyname(NodeName
);
204 /* Check for valid addresses */
205 if ((Hostent
->h_addrtype
== AF_INET
) &&
206 (Hostent
->h_length
== sizeof(IN_ADDR
)))
208 /* Loop every address in it */
209 for (Addresses
= Hostent
->h_addr_list
; *Addresses
; Addresses
++)
211 /* Create an addrinfo structure for it*/
212 *Next
= NewAddrInfo(SocketType
,
215 ((PIN_ADDR
)*Addresses
)->s_addr
);
216 if (!*Next
) return EAI_MEMORY
;
218 /* Move to the next entry */
219 Next
= &((*Next
)->ai_next
);
223 /* Copy the canonical name */
224 strcpy(Alias
, Hostent
->h_name
);
230 /* Find out what the error was */
231 switch (GetLastError())
233 /* Convert the Winsock Error to an EAI error */
234 case WSAHOST_NOT_FOUND
: return EAI_NONAME
;
235 case WSATRY_AGAIN
: return EAI_AGAIN
;
236 case WSANO_RECOVERY
: return EAI_FAIL
;
237 case WSANO_DATA
: return EAI_NODATA
;
238 default: return EAI_NONAME
;
245 LookupNodeByAddr(IN LPWSTR pNodeBuffer
,
246 IN DWORD NodeBufferSize
,
247 IN BOOLEAN OnlyNodeName
,
250 IN INT AddressFamily
)
252 GUID LookupGuid
= SVCID_DNS_TYPE_PTR
;
253 PIN_ADDR Ip4Addr
= Addr
;
254 WCHAR ReverseBuffer
[76];
255 WSAQUERYSETW Restrictions
, Reply
;
260 /* Validate the address */
261 if (!Addr
) return WSAEFAULT
;
263 /* Make sure the family and address size match */
264 if (AddressFamily
== AF_INET6
)
266 /* Check the address size for this type */
267 if (AddrSize
!= sizeof(IN6_ADDR
)) return WSAEFAULT
;
268 Ip4Addr
= (PIN_ADDR
)&((PIN6_ADDR
)Addr
)->u
.Byte
[12];
270 else if (AddressFamily
== AF_INET
)
272 /* Check the address size for this type */
273 if (AddrSize
!= sizeof(IN_ADDR
)) return WSAEFAULT
;
277 /* Address family not supported */
278 return WSAEAFNOSUPPORT
;
281 /* Check if this is a mapped V4 IPv6 or pure IPv4 */
282 if (((AddressFamily
== AF_INET6
) && (IN6_IS_ADDR_V4MAPPED(Addr
))) ||
283 (AddressFamily
== AF_INET
))
285 /* Get the reverse name */
286 Dns_Ip4AddressToReverseName_W(ReverseBuffer
, *Ip4Addr
);
288 /* FIXME: Not implemented for now
291 /* By this point we have the Reverse Name, so prepare for lookup */
292 RtlZeroMemory(&Restrictions
, sizeof(Restrictions
));
293 Restrictions
.dwSize
= sizeof(Restrictions
);
294 Restrictions
.lpszServiceInstanceName
= ReverseBuffer
;
295 Restrictions
.lpServiceClassId
= &LookupGuid
;
296 Restrictions
.dwNameSpace
= NS_DNS
;
298 /* Now do the lookup */
299 ErrorCode
= WSALookupServiceBeginW(&Restrictions
,
302 if (ErrorCode
== ERROR_SUCCESS
)
304 /* Lookup succesfull, now get the data */
305 BufferLength
= (NI_MAXHOST
- 1) * sizeof(WCHAR
) + sizeof(Restrictions
);
306 ErrorCode
= WSALookupServiceNextW(LookupHandle
,
310 if (ErrorCode
== ERROR_SUCCESS
)
312 /* Now check if we have a name back */
313 Reply
= Restrictions
;
314 if (!Reply
.lpszServiceInstanceName
)
317 ErrorCode
= WSAHOST_NOT_FOUND
;
321 /* Check if the caller only wants the node name */
324 /* Split it and get only the partial host name */
325 Dns_SplitHostFromDomainNameW(Reply
.lpszServiceInstanceName
);
328 /* Check the length and see if it's within our limit */
329 if (wcslen(Reply
.lpszServiceInstanceName
) + 1 >
332 /* It's not, so fail */
333 ErrorCode
= WSAEFAULT
;
337 /* It will fit, let's copy it*/
338 wcscpy(pNodeBuffer
, Reply
.lpszServiceInstanceName
);
343 else if (ErrorCode
== WSASERVICE_NOT_FOUND
)
345 /* Normalize the error code */
346 ErrorCode
= WSAHOST_NOT_FOUND
;
349 /* Finish the lookup if one was in progress */
350 if (LookupHandle
) WSALookupServiceEnd(LookupHandle
);
352 /* Return the error code */
359 GetServiceNameForPort(IN LPWSTR pServiceBuffer
,
360 IN DWORD ServiceBufferSize
,
364 return ERROR_SUCCESS
;
370 LookupAddressForName(IN LPCSTR NodeName
,
374 IN BOOL bAI_CANONNAME
,
375 OUT PADDRINFOW
*pptResult
)
379 CHAR szFQDN1
[NI_MAXHOST
] = "";
380 CHAR szFQDN2
[NI_MAXHOST
] = "";
381 PCHAR Name
= szFQDN1
;
382 PCHAR Alias
= szFQDN2
;
383 PCHAR Scratch
= NULL
;
385 /* Make a copy of the name */
386 strcpy(Name
, NodeName
);
391 /* Do a DNS Query for the name */
392 iError
= QueryDNS(NodeName
,
400 /* Exit if we have a result */
401 if (*pptResult
) break;
403 /* Don't loop continously if this is a DNS misconfiguration */
404 if ((!strlen(Alias
)) || (!strcmp(Name
, Alias
)) || (++AliasCount
== 16))
406 /* Exit the loop with a failure */
411 /* Restart loopup if we got a CNAME */
412 Swap(Name
, Alias
, Scratch
);
415 /* Check if we suceeded and the canonical name is requested */
416 if (!iError
&& bAI_CANONNAME
)
418 /* Allocate memory for a copy */
419 (*pptResult
)->ai_canonname
= HeapAlloc(WsSockHeap
, 0, 512);
421 /* Check if we had enough memory */
422 if (!(*pptResult
)->ai_canonname
)
424 /* Set failure code */
429 /* Convert the alias to UNICODE */
430 MultiByteToWideChar(CP_ACP
,
434 (*pptResult
)->ai_canonname
,
439 /* Return to caller */
448 GetAddrInfoW(IN PCWSTR pszNodeName
,
449 IN PCWSTR pszServiceName
,
450 IN
const ADDRINFOW
*ptHints
,
451 OUT PADDRINFOW
*pptResult
)
455 INT iFamily
= PF_UNSPEC
;
460 PSERVENT ptService
= NULL
;
465 WCHAR CanonicalName
[0x42];
466 CHAR AnsiServiceName
[256];
467 CHAR AnsiNodeName
[256];
468 DPRINT("GetAddrInfoW: %S, %S, %p, %p\n", pszNodeName
, pszServiceName
, ptHints
, pptResult
);
473 /* We must have at least one name to work with */
474 if (!(pszNodeName
) && !(pszServiceName
))
477 SetLastError(EAI_NONAME
);
481 /* Check if we got hints */
484 /* Make sure these are empty */
485 if ((ptHints
->ai_addrlen
) ||
486 (ptHints
->ai_canonname
) ||
487 (ptHints
->ai_addr
) ||
490 /* Fail if they aren't */
491 SetLastError(EAI_FAIL
);
495 /* Save the flags and validate them */
496 iFlags
= ptHints
->ai_flags
;
497 if ((iFlags
& AI_CANONNAME
) && !pszNodeName
)
502 /* Save family and validate it */
503 iFamily
= ptHints
->ai_family
;
504 if ((iFamily
!= PF_UNSPEC
) && (iFamily
!= PF_INET
))
509 /* Save socket type and validate it */
510 iSocketType
= ptHints
->ai_socktype
;
511 if ((iSocketType
!= 0) &&
512 (iSocketType
!= SOCK_STREAM
) &&
513 (iSocketType
!= SOCK_DGRAM
) &&
514 (iSocketType
!= SOCK_RAW
))
519 /* Save the protocol */
520 iProtocol
= ptHints
->ai_protocol
;
523 /* Check if we have a service name */
526 /* We need to convert it to ANSI */
527 WideCharToMultiByte(CP_ACP
,
537 wPort
= (WORD
)strtoul(AnsiServiceName
, &pc
, 10);
539 /* Check if the port string is numeric */
542 /* Get the port directly */
543 wPort
= wTcpPort
= wUdpPort
= htons(wPort
);
545 /* Check if this is both TCP and UDP */
546 if (iSocketType
== 0)
548 /* Set it to TCP for now, but remember to clone */
550 iSocketType
= SOCK_STREAM
;
555 /* The port name was a string. Check if this is a UDP socket */
556 if ((iSocketType
== 0) || (iSocketType
== SOCK_DGRAM
))
558 /* It's UDP, do a getservbyname */
559 ptService
= getservbyname(AnsiServiceName
, "udp");
561 /* If we got a servent, return the port from it */
562 if (ptService
) wPort
= wUdpPort
= ptService
->s_port
;
565 /* Check if this is a TCP socket */
566 if ((iSocketType
== 0) || (iSocketType
== SOCK_STREAM
))
568 /* It's TCP, do a getserbyname */
569 ptService
= getservbyname(AnsiServiceName
, "tcp");
571 /* Return the port from the servent */
572 if (ptService
) wPort
= wTcpPort
= ptService
->s_port
;
575 /* If we got 0, then fail */
578 return iSocketType
? EAI_SERVICE
: EAI_NONAME
;
581 /* Check if this was for both */
582 if (iSocketType
== 0)
584 /* Do the TCP case right now */
585 iSocketType
= (wTcpPort
) ? SOCK_STREAM
: SOCK_DGRAM
;
586 bClone
= (wTcpPort
&& wUdpPort
);
591 /* Check if no node was given or if this is is a valid IPv4 address */
592 if ((!pszNodeName
) || (ParseV4Address(pszNodeName
, &dwAddress
)))
594 /* Check if we don't have a node name */
597 /* Make one up based on the flags */
598 dwAddress
= htonl((iFlags
& AI_PASSIVE
) ?
599 INADDR_ANY
: INADDR_LOOPBACK
);
602 /* Create the Addr Info */
603 *pptResult
= NewAddrInfo(iSocketType
, iProtocol
, wPort
, dwAddress
);
605 /* If we didn't get one back, assume out of memory */
606 if (!(*pptResult
)) iError
= EAI_MEMORY
;
608 /* Check if we have success and a nodename */
609 if (!iError
&& pszNodeName
)
611 /* Set AI_NUMERICHOST since this is a numeric string */
612 (*pptResult
)->ai_flags
|= AI_NUMERICHOST
;
614 /* Check if the canonical name was requestd */
615 if (iFlags
& AI_CANONNAME
)
617 /* Get the canonical name */
618 GetNameInfoW((*pptResult
)->ai_addr
,
619 (socklen_t
)(*pptResult
)->ai_addrlen
,
626 /* Allocate memory for a copy */
627 (*pptResult
)->ai_canonname
= HeapAlloc(WsSockHeap
,
629 wcslen(CanonicalName
));
631 if (!(*pptResult
)->ai_canonname
)
633 /* No memory for the copy */
638 /* Duplicate the string */
639 RtlMoveMemory((*pptResult
)->ai_canonname
,
641 wcslen(CanonicalName
));
646 else if (iFlags
& AI_NUMERICHOST
)
648 /* No name for this request (we have a non-numeric name) */
653 /* We need to convert it to ANSI */
654 WideCharToMultiByte(CP_ACP
,
663 /* Non-numeric name, do DNS lookup */
664 iError
= LookupAddressForName(AnsiNodeName
,
668 (iFlags
& AI_CANONNAME
),
672 /* If all was good and the caller requested UDP and TCP */
673 if (!iError
&& bClone
)
675 /* Process UDP now, we already did TCP */
676 iError
= CloneAddrInfo(wUdpPort
, *pptResult
);
679 /* If we've hit an error till here */
682 /* Free the address info and return nothing */
683 FreeAddrInfoW(*pptResult
);
687 /* Return to caller */
697 freeaddrinfo(PADDRINFOA AddrInfo
)
701 /* Loop the chain of structures */
702 for (NextInfo
= AddrInfo
; NextInfo
; NextInfo
= AddrInfo
)
704 /* Check if there is a canonical name */
705 if (NextInfo
->ai_canonname
)
708 HeapFree(WsSockHeap
, 0, NextInfo
->ai_canonname
);
711 /* Check if there is an address */
712 if (NextInfo
->ai_addr
)
715 HeapFree(WsSockHeap
, 0, NextInfo
->ai_addr
);
718 /* Move to the next entry */
719 AddrInfo
= NextInfo
->ai_next
;
721 /* Free this entry */
722 HeapFree(WsSockHeap
, 0, NextInfo
);
732 getaddrinfo(const char FAR
*nodename
,
733 const char FAR
*servname
,
734 const struct addrinfo FAR
*hints
,
735 struct addrinfo FAR
* FAR
*res
)
738 LPWSTR UnicodeNodeName
;
739 LPWSTR UnicodeServName
= NULL
;
740 DPRINT("getaddrinfo: %s, %s, %p, %p\n", nodename
, servname
, hints
, res
);
742 /* Check for WSAStartup */
743 if ((ErrorCode
= WsQuickProlog()) != ERROR_SUCCESS
) return ErrorCode
;
748 /* Convert the node name */
749 UnicodeNodeName
= UnicodeDupFromAnsi((LPSTR
)nodename
);
750 if (!UnicodeNodeName
)
752 /* Prepare to fail */
753 ErrorCode
= GetLastError();
757 /* Convert the servname too, if we have one */
760 UnicodeServName
= UnicodeDupFromAnsi((LPSTR
)servname
);
761 if (!UnicodeServName
)
763 /* Prepare to fail */
764 ErrorCode
= GetLastError();
769 /* Now call the unicode function */
770 ErrorCode
= GetAddrInfoW(UnicodeNodeName
,
775 /* Convert it to ANSI if we suceeded */
776 if (ErrorCode
== ERROR_SUCCESS
) ConvertAddrinfoFromUnicodeToAnsi((PADDRINFOW
)*res
);
779 /* Check if we have a unicode node name and serv name */
780 if (UnicodeNodeName
) HeapFree(WsSockHeap
, 0, UnicodeNodeName
);
781 if (UnicodeServName
) HeapFree(WsSockHeap
, 0, UnicodeServName
);
783 /* Check if we are in error */
784 if (ErrorCode
!= ERROR_SUCCESS
)
786 /* Free the structure and return nothing */
791 /* Set the last error and return */
792 SetLastError(ErrorCode
);
801 GetNameInfoW(IN CONST SOCKADDR
*pSockaddr
,
802 IN socklen_t SockaddrLength
,
803 OUT PWCHAR pNodeBuffer
,
804 IN DWORD NodeBufferSize
,
805 OUT PWCHAR pServiceBuffer
,
806 IN DWORD ServiceBufferSize
,
809 DWORD AddressLength
, AddrSize
;
812 INT ErrorCode
= ERROR_SUCCESS
;
814 /* Check for valid socket */
815 if (!pSockaddr
) return EAI_FAIL
;
817 /* Check which family this is */
818 if (pSockaddr
->sa_family
== AF_INET
)
821 AddressLength
= sizeof(SOCKADDR_IN
);
822 Addr
= &((PSOCKADDR_IN
)pSockaddr
)->sin_addr
;
823 AddrSize
= sizeof(IN_ADDR
);
825 else if (pSockaddr
->sa_family
== AF_INET6
)
828 AddressLength
= sizeof(SOCKADDR_IN6
);
829 Addr
= &((PSOCKADDR_IN6
)pSockaddr
)->sin6_addr
;
830 AddrSize
= sizeof(IN6_ADDR
);
834 /* Unsupported family */
838 /* Check for valid socket adress length */
839 if ((DWORD
)SockaddrLength
< AddressLength
) return EAI_FAIL
;
841 /* Check if we have a node name */
844 /* Check if only the numeric host is wanted */
845 if (!(Flags
& NI_NUMERICHOST
))
847 /* Do the lookup by addr */
848 ErrorCode
= LookupNodeByAddr(pNodeBuffer
,
853 pSockaddr
->sa_family
);
854 /* Check if we failed */
855 if (ErrorCode
!= ERROR_SUCCESS
)
857 /* Fail if the caller REALLY wants the NAME itself? */
858 if (Flags
& NI_NAMEREQD
) goto quickie
;
862 /* We suceeded, no need to get the numeric address */
867 /* Copy the address */
868 RtlMoveMemory(&Address
, pSockaddr
, AddressLength
);
870 /* Get the numeric address */
871 Address
.sin_port
= 0;
872 ErrorCode
= WSAAddressToStringW((LPSOCKADDR
)&Address
,
877 if (ErrorCode
== SOCKET_ERROR
)
879 /* Get the error code and exit with it */
880 ErrorCode
= GetLastError();
886 /* Check if we got a service name */
889 /* Handle this request */
890 ErrorCode
= GetServiceNameForPort(pServiceBuffer
,
892 ((PSOCKADDR_IN
)pSockaddr
)->sin_port
,
896 /* Set the error and return it (or success) */
898 SetLastError(ErrorCode
);
908 getnameinfo(const struct sockaddr FAR
*sa
,
918 WCHAR ServiceBuffer
[17];
919 DWORD HostLength
= 0, ServLength
= 0;
920 PWCHAR ServiceString
= NULL
, HostString
= NULL
;
921 DPRINT("getnameinfo: %p, %p, %p, %lx\n", host
, serv
, sa
, salen
);
923 /* Check for WSAStartup */
924 if ((ErrorCode
= WsQuickProlog()) != ERROR_SUCCESS
) return ErrorCode
;
926 /* Check if we have a host pointer */
929 /* Setup the data for it */
931 HostLength
= sizeof(Buffer
) / sizeof(WCHAR
);
934 /* Check if we have a service pointer */
937 /* Setup the data for it */
938 ServiceString
= ServiceBuffer
;
939 ServLength
= sizeof(ServiceBuffer
) - 1;
942 /* Now call the unicode function */
943 ErrorCode
= GetNameInfoW(sa
,
951 /* Check for success */
952 if (ErrorCode
== ERROR_SUCCESS
)
954 /* Check if we had a host pointer */
957 /* Convert it back to ANSI */
958 ErrorCode
= WideCharToMultiByte(CP_ACP
,
966 if (!ErrorCode
) goto Quickie
;
969 /* Check if we have a service pointer */
972 /* Convert it back to ANSI */
973 ErrorCode
= WideCharToMultiByte(CP_ACP
,
981 if (!ErrorCode
) goto Quickie
;
985 return ERROR_SUCCESS
;
988 /* Set the last error and return */
990 SetLastError(ErrorCode
);