-- v2: iphlpapi: Try to disambiguate addresses in GetBestRoute2() by probing system assigned ones. iphlpapi/tests: Add tests for best routes. iphlpapi: Reimplement GetBestInterfaceEx() on top of GetBestRoute2(). iphlpapi: Implement GetBestRoute2().
From: Paul Gofman pgofman@codeweavers.com
--- dlls/iphlpapi/iphlpapi_main.c | 195 ++++++++++++++++++++++++++++++++-- include/netioapi.h | 3 + 2 files changed, 187 insertions(+), 11 deletions(-)
diff --git a/dlls/iphlpapi/iphlpapi_main.c b/dlls/iphlpapi/iphlpapi_main.c index 1231b98e656..31c6c03eb8c 100644 --- a/dlls/iphlpapi/iphlpapi_main.c +++ b/dlls/iphlpapi/iphlpapi_main.c @@ -4681,24 +4681,197 @@ DWORD WINAPI GetIpInterfaceEntry( MIB_IPINTERFACE_ROW *row ) return ERROR_SUCCESS; }
+static BOOL match_ip_address_with_prefix( const SOCKADDR_INET *addr, const IP_ADDRESS_PREFIX *pfx ) +{ + const BYTE *p1, *p2; + unsigned int len; + BYTE mask; + + if (addr->si_family != pfx->Prefix.si_family) return FALSE; + if (!(len = pfx->PrefixLength)) return TRUE; + + if (addr->si_family == AF_INET6) + { + if (len > 128) return FALSE; + p1 = (const BYTE *)&addr->Ipv6.sin6_addr; + p2 = (const BYTE *)&pfx->Prefix.Ipv6.sin6_addr; + } + else + { + if (len > 32) return FALSE; + p1 = (const BYTE *)&addr->Ipv4.sin_addr; + p2 = (const BYTE *)&pfx->Prefix.Ipv4.sin_addr; + } + if (memcmp( p1, p2, len / 8 )) return FALSE; + mask = 0xffu << (8 - (len % 8)); + if (!mask) return TRUE; + return (p1[len / 8] & mask) == (p2[len / 8] & mask); +} + +static MIB_UNICASTIPADDRESS_ROW *find_first_matching_unicast_addr( MIB_UNICASTIPADDRESS_TABLE *uni, BOOL v6, + const SOCKADDR_INET *src, const SOCKADDR_INET *dst, + NET_IFINDEX if_index ) +{ + unsigned int i; + + for (i = 0; i < uni->NumEntries; ++i) + { + if (if_index && uni->Table[i].InterfaceIndex != if_index) continue; + if (src) + { + if (v6) + { + if (src->Ipv6.sin6_scope_id && IN6_IS_ADDR_LINKLOCAL(&src->Ipv6.sin6_addr) + && src->Ipv6.sin6_scope_id != uni->Table[i].InterfaceIndex) continue; + if (memcmp( &src->Ipv6.sin6_addr, &uni->Table[i].Address.Ipv6.sin6_addr, sizeof(src->Ipv6.sin6_addr) )) + continue; + } + else if (src->Ipv4.sin_addr.s_addr != uni->Table[i].Address.Ipv4.sin_addr.s_addr) continue; + } + if (v6 && dst) + { + if (IN6_IS_ADDR_LINKLOCAL(&dst->Ipv6.sin6_addr) + != IN6_IS_ADDR_LINKLOCAL(&uni->Table[i].Address.Ipv6.sin6_addr)) + continue; + } + return &uni->Table[i]; + } + return NULL; +} + /****************************************************************** * GetBestRoute2 (IPHLPAPI.@) */ -DWORD WINAPI GetBestRoute2(NET_LUID *luid, NET_IFINDEX index, - const SOCKADDR_INET *source, const SOCKADDR_INET *destination, - ULONG options, PMIB_IPFORWARD_ROW2 bestroute, - SOCKADDR_INET *bestaddress) -{ - static int once; +DWORD WINAPI GetBestRoute2( NET_LUID *luid, NET_IFINDEX index, + const SOCKADDR_INET *src, const SOCKADDR_INET *dst, + ULONG options, PMIB_IPFORWARD_ROW2 bestroute, + SOCKADDR_INET *bestaddress ) +{ + MIB_UNICASTIPADDRESS_ROW *uni_row = NULL; + MIB_UNICASTIPADDRESS_TABLE *uni; + MIB_IPFORWARD_TABLE2 *fwd; + unsigned int i, best_idx; + int max_prefix_len; + DWORD ret; + BOOL v6; + + TRACE( "(%p, %ld, %p, %p, 0x%08lx, %p, %p).\n", luid, index, src, dst, options, bestroute, bestaddress ); + + if (!dst || !bestroute || !bestaddress) + return ERROR_INVALID_PARAMETER; + + memset( bestroute, 0, sizeof(*bestroute) ); + memset( bestaddress, 0, sizeof(*bestaddress) );
- if (!once++) - FIXME("(%p, %ld, %p, %p, 0x%08lx, %p, %p): stub\n", luid, index, source, - destination, options, bestroute, bestaddress); + if (dst->si_family != AF_INET && dst->si_family != AF_INET6) return ERROR_INVALID_PARAMETER; + v6 = dst->si_family == AF_INET6; + if (src) + { + if (src->si_family != dst->si_family) src = NULL; + else if (v6 && IN6_IS_ADDR_UNSPECIFIED(&src->Ipv6.sin6_addr)) src = NULL; + else if (!v6 && !src->Ipv4.sin_addr.s_addr) src = NULL; + }
- if (!destination || !bestroute || !bestaddress) + if (v6 && !IN6_IS_ADDR_LINKLOCAL(&dst->Ipv6.sin6_addr) && dst->Ipv6.sin6_scope_id) + return ERROR_INVALID_PARAMETER; + if (v6 && src && !IN6_IS_ADDR_LINKLOCAL(&src->Ipv6.sin6_addr) && src->Ipv6.sin6_scope_id) return ERROR_INVALID_PARAMETER;
- return ERROR_NOT_SUPPORTED; + if ((ret = GetUnicastIpAddressTable( dst->si_family, &uni ))) return ret; + if ((ret = GetIpForwardTable2( dst->si_family, &fwd ))) + { + FreeMibTable( &uni ); + return ret; + } + + if (!luid && index) + { + for (i = 0; i < uni->NumEntries; ++i) + { + if (uni->Table[i].InterfaceIndex == index) break; + } + if (i == uni->NumEntries) + { + ret = ERROR_FILE_NOT_FOUND; + goto done; + } + luid = &uni->Table[i].InterfaceLuid; + } + if (src && !(uni_row = find_first_matching_unicast_addr( uni, v6, src, NULL, 0 ))) + { + ret = ERROR_NOT_FOUND; + goto done; + } + if (!uni_row && luid && luid->Value) + { + for (i = 0; i < uni->NumEntries; ++i) + { + if (uni->Table[i].InterfaceLuid.Value == luid->Value) break; + } + if (i == uni->NumEntries) + { + ret = ERROR_NOT_FOUND; + goto done; + } + uni_row = &uni->Table[i]; + } + + if (uni_row) index = uni_row->InterfaceIndex; + else index = 0; + + if (v6) + { + if (!index) index = dst->Ipv6.sin6_scope_id; + if (IN6_IS_ADDR_LINKLOCAL(&dst->Ipv6.sin6_addr) && dst->Ipv6.sin6_scope_id + && dst->Ipv6.sin6_scope_id != index) + { + ret = ERROR_INVALID_PARAMETER; + goto done; + } + } + + max_prefix_len = -1; + best_idx = 0; + for (i = 0; i < fwd->NumEntries; ++i) + { + if (index && fwd->Table[i].InterfaceIndex != index) continue; + + if (match_ip_address_with_prefix( dst, &fwd->Table[i].DestinationPrefix ) + && max_prefix_len < fwd->Table[i].DestinationPrefix.PrefixLength ) + { + max_prefix_len = fwd->Table[i].DestinationPrefix.PrefixLength; + best_idx = i; + } + } + + if (max_prefix_len == -1) + { + ret = ERROR_NETWORK_UNREACHABLE; + goto done; + } + index = fwd->Table[best_idx].InterfaceIndex; + if (!fwd->Table[best_idx].Loopback && ((v6 && IN6_IS_ADDR_LOOPBACK(&dst->Ipv6.sin6_addr)) + || (!v6 && dst->Ipv4.sin_addr.S_un.S_un_b.s_b1 == 127))) + { + ret = ERROR_INVALID_PARAMETER; + goto done; + } + + if (!(uni_row = find_first_matching_unicast_addr( uni, v6, src, dst, index ))) + { + WARN( "Could not find source address.\n" ); + ret = ERROR_NOT_FOUND; + goto done; + } + *bestaddress = uni_row->Address; + *bestroute = fwd->Table[best_idx]; + + ret = ERROR_SUCCESS; +done: + FreeMibTable( fwd ); + FreeMibTable( uni ); + TRACE( "-> %lu.\n", ret ); + return ret; }
/****************************************************************** diff --git a/include/netioapi.h b/include/netioapi.h index 09824f946e4..080f16635d2 100644 --- a/include/netioapi.h +++ b/include/netioapi.h @@ -270,6 +270,9 @@ IPHLPAPI_DLL_LINKAGE DWORD WINAPI ConvertLengthToIpv4Mask(ULONG,ULONG*); IPHLPAPI_DLL_LINKAGE void WINAPI FreeMibTable(void*); IPHLPAPI_DLL_LINKAGE DWORD WINAPI GetAnycastIpAddressTable(ADDRESS_FAMILY,MIB_ANYCASTIPADDRESS_TABLE**); IPHLPAPI_DLL_LINKAGE NET_IF_COMPARTMENT_ID WINAPI GetCurrentThreadCompartmentId(void); +IPHLPAPI_DLL_LINKAGE DWORD WINAPI GetBestRoute2(NET_LUID *luid, NET_IFINDEX index, const SOCKADDR_INET *source, + const SOCKADDR_INET *destination, ULONG options, + PMIB_IPFORWARD_ROW2 bestroute, SOCKADDR_INET *bestaddress); IPHLPAPI_DLL_LINKAGE DWORD WINAPI GetIfEntry2(MIB_IF_ROW2*); IPHLPAPI_DLL_LINKAGE DWORD WINAPI GetIfEntry2Ex(MIB_IF_TABLE_LEVEL,MIB_IF_ROW2*); IPHLPAPI_DLL_LINKAGE DWORD WINAPI GetIfTable2(MIB_IF_TABLE2**);
From: Paul Gofman pgofman@codeweavers.com
--- dlls/iphlpapi/iphlpapi_main.c | 30 ++++++++++++------------------ 1 file changed, 12 insertions(+), 18 deletions(-)
diff --git a/dlls/iphlpapi/iphlpapi_main.c b/dlls/iphlpapi/iphlpapi_main.c index 31c6c03eb8c..e860226b5bd 100644 --- a/dlls/iphlpapi/iphlpapi_main.c +++ b/dlls/iphlpapi/iphlpapi_main.c @@ -1389,27 +1389,21 @@ DWORD WINAPI GetBestInterface(IPAddr dwDestAddr, PDWORD pdwBestIfIndex) * Success: NO_ERROR * Failure: error code from winerror.h */ -DWORD WINAPI GetBestInterfaceEx(struct sockaddr *pDestAddr, PDWORD pdwBestIfIndex) +DWORD WINAPI GetBestInterfaceEx( struct sockaddr *pDestAddr, PDWORD pdwBestIfIndex ) { - DWORD ret; + SOCKADDR_INET best_address; + MIB_IPFORWARD_ROW2 row; + DWORD ret;
- TRACE("pDestAddr %p, pdwBestIfIndex %p\n", pDestAddr, pdwBestIfIndex); - if (!pDestAddr || !pdwBestIfIndex) - ret = ERROR_INVALID_PARAMETER; - else { - MIB_IPFORWARDROW ipRow; + TRACE( "pDestAddr %p, pdwBestIfIndex %p\n", pDestAddr, pdwBestIfIndex );
- if (pDestAddr->sa_family == AF_INET) { - ret = GetBestRoute(((struct sockaddr_in *)pDestAddr)->sin_addr.S_un.S_addr, 0, &ipRow); - if (ret == ERROR_SUCCESS) - *pdwBestIfIndex = ipRow.dwForwardIfIndex; - } else { - FIXME("address family %d not supported\n", pDestAddr->sa_family); - ret = ERROR_NOT_SUPPORTED; - } - } - TRACE("returning %ld\n", ret); - return ret; + if (!pDestAddr || !pdwBestIfIndex) return ERROR_INVALID_PARAMETER; + + ret = GetBestRoute2( NULL, 0, NULL, (const SOCKADDR_INET *)pDestAddr, 0, &row, &best_address ); + if (!ret) *pdwBestIfIndex = row.InterfaceIndex; + + TRACE( "returning %ld\n", ret ); + return ret; }
From: Paul Gofman pgofman@codeweavers.com
--- dlls/iphlpapi/tests/iphlpapi.c | 485 +++++++++++++++++++++++++++++++++ 1 file changed, 485 insertions(+)
diff --git a/dlls/iphlpapi/tests/iphlpapi.c b/dlls/iphlpapi/tests/iphlpapi.c index ebd69a37ad8..49c3059b5df 100644 --- a/dlls/iphlpapi/tests/iphlpapi.c +++ b/dlls/iphlpapi/tests/iphlpapi.c @@ -3488,6 +3488,490 @@ static void test_GetIpInterface(void) FreeMibTable( if_table ); }
+static MIB_IPFORWARD_ROW2 *find_ipforward_row( MIB_IPFORWARD_TABLE2 *table, DWORD ifindex ) +{ + unsigned int i; + + for (i = 0; i < table->NumEntries; ++i) + { + if (ifindex == table->Table[i].InterfaceIndex) return &table->Table[i]; + } + return NULL; +} + +static void test_best_routes(void) +{ + static const IN6_ADDR link_local_prefix = {{ IN6ADDR_LINKLOCALPREFIX_INIT }}; + + DWORD ret, index, link_local_index = ~0u, loopback_index = ~0u, default_route_index = ~0u; + struct sockaddr_in6 dst6, src6, best6, link_local_addr6, global_addr6; + struct sockaddr_in dst4, src4, global_addr4; + NET_LUID link_local_luid = { 0 }, luid; + MIB_UNICASTIPADDRESS_ROW uni_row; + MIB_IPFORWARD_ROW2 fwd_row, *r; + MIB_IPFORWARD_TABLE2 *table; + char s[256], s2[256]; + SOCKADDR_INET best4; + unsigned int i; + + memset( &dst6, 0, sizeof(src6) ); + dst6.sin6_family = AF_INET6; + ret = inet_pton( AF_INET6, "::1", &dst6.sin6_addr); + ok(ret, "got error %u.\n", WSAGetLastError()); + ret = GetBestRoute2( NULL, 0, NULL, (SOCKADDR_INET *)&dst6, 0, NULL, (SOCKADDR_INET *)&best6 ); + ok( ret == ERROR_INVALID_PARAMETER, "got error %lu.\n", ret ); + ret = GetBestRoute2( NULL, 0, NULL, NULL, 0, &fwd_row, (SOCKADDR_INET *)&best6 ); + ok( ret == ERROR_INVALID_PARAMETER, "got error %lu.\n", ret ); + ret = GetBestRoute2( NULL, 0, NULL, (SOCKADDR_INET *)&dst6, 0, &fwd_row, NULL ); + ok( ret == ERROR_INVALID_PARAMETER, "got error %lu.\n", ret ); + + ret = GetIpForwardTable2( AF_INET6, &table ); + ok( !ret, "got error %lu.\n", ret ); + + for (i = 0; i < table->NumEntries; ++i) + { + if (IN6_IS_ADDR_LINKLOCAL(&table->Table[i].DestinationPrefix.Prefix.Ipv6.sin6_addr)) + { + if (link_local_index == ~0u) + { + link_local_index = table->Table[i].InterfaceIndex; + link_local_luid = table->Table[i].InterfaceLuid; + } + } + if (IN6_IS_ADDR_LOOPBACK(&table->Table[i].DestinationPrefix.Prefix.Ipv6.sin6_addr)) + loopback_index = table->Table[i].InterfaceIndex; + } + ok( link_local_index != ~0u, "could not find any link local route.\n" ); + ok( loopback_index != ~0u, "could not find loopback route.\n" ); + + /* Test with link local address. */ + memset( &dst6, 0, sizeof(dst6) ); + dst6.sin6_family = AF_INET6; + dst6.sin6_addr = link_local_prefix; + dst6.sin6_scope_id = link_local_index; + dst6.sin6_addr.u.Byte[15] = 1; + ret = GetBestInterfaceEx( (struct sockaddr *)&dst6, &index ); + ok( !ret, "got error %lu.\n", ret ); + ok( index == link_local_index, "got %lu, expected %lu.\n", index, link_local_index ); + + memset( &fwd_row, 0xcc, sizeof(fwd_row) ); + ret = GetBestRoute2( NULL, 0xdeadbeef, NULL, (SOCKADDR_INET *)&dst6, 0, &fwd_row, (SOCKADDR_INET *)&best6 ); + ok( ret == ERROR_FILE_NOT_FOUND, "got error %lu.\n", ret ); + ok( !fwd_row.InterfaceIndex, "got %#lx.\n", fwd_row.InterfaceIndex ); + + memset( &fwd_row, 0xcc, sizeof(fwd_row) ); + dst6.sin6_scope_id = loopback_index; + ret = GetBestRoute2( NULL, link_local_index, NULL, (SOCKADDR_INET *)&dst6, 0, &fwd_row, (SOCKADDR_INET *)&best6 ); + ok( ret == ERROR_INVALID_PARAMETER, "got error %lu.\n", ret ); + ok( !fwd_row.InterfaceIndex, "got %#lx.\n", fwd_row.InterfaceIndex ); + + memset( &fwd_row, 0xcc, sizeof(fwd_row) ); + dst6.sin6_scope_id = link_local_index; + ret = GetBestRoute2( NULL, loopback_index, NULL, (SOCKADDR_INET *)&dst6, 0, &fwd_row, (SOCKADDR_INET *)&best6 ); + ok( ret == ERROR_INVALID_PARAMETER, "got error %lu.\n", ret ); + ok( !fwd_row.InterfaceIndex, "got %#lx.\n", fwd_row.InterfaceIndex ); + + memset( &fwd_row, 0xcc, sizeof(fwd_row) ); + dst6.sin6_scope_id = 0xdeadbeef; + ret = GetBestRoute2( NULL, link_local_index, NULL, (SOCKADDR_INET *)&dst6, 0, &fwd_row, (SOCKADDR_INET *)&best6 ); + ok( ret == ERROR_INVALID_PARAMETER, "got error %lu.\n", ret ); + ok( !fwd_row.InterfaceIndex, "got %#lx.\n", fwd_row.InterfaceIndex ); + + dst6.sin6_scope_id = link_local_index; + + memset( &fwd_row, 0xcc, sizeof(fwd_row) ); + memset( &best6, 0xcc, sizeof(best6) ); + ret = GetBestRoute2( NULL, link_local_index, NULL, (SOCKADDR_INET *)&dst6, 0, &fwd_row, (SOCKADDR_INET *)&best6 ); + ok( !ret, "got error %lu.\n", ret ); + ok( fwd_row.InterfaceIndex == link_local_index, "got %lu, expected %lu.\n", fwd_row.InterfaceIndex, link_local_index ); + ok( !fwd_row.Loopback, "got %d.\n", fwd_row.Loopback ); + ok( IN6_IS_ADDR_LINKLOCAL(&fwd_row.DestinationPrefix.Prefix.Ipv6.sin6_addr), "expected link local prefix.\n" ); + ok( IN6_IS_ADDR_UNSPECIFIED(&fwd_row.NextHop.Ipv6.sin6_addr), "expected unspecifed address.\n" ); + ok( best6.sin6_family == AF_INET6, "got %u.\n", best6.sin6_family ); + ok( best6.sin6_scope_id == link_local_index, "got %lu, expected %lu.\n", best6.sin6_scope_id, link_local_index ); + memset( &uni_row, 0, sizeof(uni_row) ); + *(struct sockaddr_in6 *)&uni_row.Address = best6; + uni_row.InterfaceIndex = fwd_row.InterfaceIndex; + ret = GetUnicastIpAddressEntry( &uni_row ); + ok( !ret, "got error %lu.\n", ret ); + ok( !memcmp( &uni_row.Address, &best6, sizeof(best6) ), "got different address.\n" ); + link_local_addr6 = best6; + + memset( &fwd_row, 0xcc, sizeof(fwd_row) ); + dst6.sin6_scope_id = link_local_index; + ret = GetBestRoute2( &link_local_luid, 0xdeadbeef, NULL, (SOCKADDR_INET *)&dst6, 0, &fwd_row, + (SOCKADDR_INET *)&best6 ); + ok( !ret, "got error %lu.\n", ret ); + ok( fwd_row.InterfaceIndex == link_local_index, "got %lu, expected %lu.\n", fwd_row.InterfaceIndex, link_local_index ); + ok( IN6_IS_ADDR_UNSPECIFIED(&fwd_row.NextHop.Ipv6.sin6_addr), "expected unspecifed address.\n" ); + + dst6.sin6_scope_id = link_local_index; + memset( &fwd_row, 0xcc, sizeof(fwd_row) ); + ret = GetBestRoute2( NULL, 0, NULL, (SOCKADDR_INET *)&dst6, 0, &fwd_row, (SOCKADDR_INET *)&best6 ); + ok( !ret, "got error %lu.\n", ret ); + ok( fwd_row.InterfaceIndex == link_local_index, "got %lu, expected %lu.\n", fwd_row.InterfaceIndex, link_local_index ); + ok( !fwd_row.Loopback, "got %d.\n", fwd_row.Loopback ); + + dst6.sin6_scope_id = 0; + memset( &fwd_row, 0xcc, sizeof(fwd_row) ); + ret = GetBestRoute2( NULL, link_local_index, NULL, (SOCKADDR_INET *)&dst6, 0, &fwd_row, (SOCKADDR_INET *)&best6 ); + ok( !ret, "got error %lu.\n", ret ); + ok( fwd_row.InterfaceIndex == link_local_index, "got %lu, expected %lu.\n", fwd_row.InterfaceIndex, link_local_index ); + ok( !fwd_row.Loopback, "got %d.\n", fwd_row.Loopback ); + ok( best6.sin6_scope_id == link_local_index, "got %lu, expected %lu.\n", best6.sin6_scope_id, link_local_index ); + ok( IN6_IS_ADDR_UNSPECIFIED(&fwd_row.NextHop.Ipv6.sin6_addr), "expected unspecifed address.\n" ); + + /* With both scope_id in destination address and interface unspecified Windows returns a row for a random + * matching route, not even necessarily a link local one. */ + dst6.sin6_scope_id = 0; + memset( &fwd_row, 0xcc, sizeof(fwd_row) ); + ret = GetBestRoute2( NULL, 0, NULL, (SOCKADDR_INET *)&dst6, 0, &fwd_row, (SOCKADDR_INET *)&best6 ); + ok( !ret, "got error %lu.\n", ret ); + r = find_ipforward_row( table, fwd_row.InterfaceIndex ); + ok( !!r, "got NULL.\n" ); + ok( best6.sin6_scope_id == fwd_row.InterfaceIndex, "got %lu, expected %lu.\n", best6.sin6_scope_id, + fwd_row.InterfaceIndex ); + ok( IN6_IS_ADDR_UNSPECIFIED(&fwd_row.NextHop.Ipv6.sin6_addr), "expected unspecifed address.\n" ); + + dst6.sin6_scope_id = 0; + ret = GetBestInterfaceEx( (struct sockaddr *)&dst6, &index ); + ok( !ret, "got error %lu.\n", ret ); + r = find_ipforward_row( table, index ); + ok( !!r, "got NULL.\n" ); + + memset( &src6, 0, sizeof(src6) ); + src6.sin6_family = AF_INET6; + ret = inet_pton( AF_INET6, "dead:beaf:dead:beaf::beaf", &src6.sin6_addr); + ok(ret, "got error %u.\n", WSAGetLastError()); + dst6.sin6_scope_id = 0; + ret = GetBestRoute2( NULL, 0, (SOCKADDR_INET *)&src6, (SOCKADDR_INET *)&dst6, 0, &fwd_row, (SOCKADDR_INET *)&best6 ); + ok( ret == ERROR_NOT_FOUND, "got error %lu.\n", ret ); + + ret = GetBestRoute2( NULL, 0, (SOCKADDR_INET *)&link_local_addr6, (SOCKADDR_INET *)&dst6, 0, &fwd_row, + (SOCKADDR_INET *)&best6 ); + ok( !ret, "got error %lu.\n", ret ); + ok( best6.sin6_scope_id == link_local_index, "got %lu, expected %lu.\n", best6.sin6_scope_id, link_local_index ); + + dst6.sin6_scope_id = 0; + ret = GetBestRoute2( &link_local_luid, 0, (SOCKADDR_INET *)&src6, (SOCKADDR_INET *)&dst6, 0, &fwd_row, + (SOCKADDR_INET *)&best6 ); + ok( ret == ERROR_NOT_FOUND, "got error %lu.\n", ret ); + + dst6.sin6_scope_id = link_local_index; + ret = GetBestRoute2( &link_local_luid, 0, (SOCKADDR_INET *)&link_local_addr6, (SOCKADDR_INET *)&dst6, 0, &fwd_row, + (SOCKADDR_INET *)&best6 ); + ok( !ret, "got error %lu.\n", ret ); + ok( best6.sin6_scope_id == link_local_index, "got %lu, expected %lu.\n", best6.sin6_scope_id, link_local_index ); + + /* src address with non-matching or unknown family is ignored. */ + src6.sin6_family = AF_INET; + ret = GetBestRoute2( NULL, 0, (SOCKADDR_INET *)&src6, (SOCKADDR_INET *)&dst6, 0, &fwd_row, (SOCKADDR_INET *)&best6 ); + ok( !ret, "got error %lu.\n", ret ); + src6.sin6_family = 0xbeed; + ret = GetBestRoute2( NULL, 0, (SOCKADDR_INET *)&src6, (SOCKADDR_INET *)&dst6, 0, &fwd_row, (SOCKADDR_INET *)&best6 ); + ok( !ret, "got error %lu.\n", ret ); + + /* zero src address is ignored. */ + memset( &src6, 0, sizeof(src6) ); + src6.sin6_family = AF_INET6; + ret = GetBestRoute2( NULL, 0, (SOCKADDR_INET *)&src6, (SOCKADDR_INET *)&dst6, 0, &fwd_row, (SOCKADDR_INET *)&best6 ); + ok( !ret, "got error %lu.\n", ret ); + + /* Specified source address not matching destination, result is weird. */ + memset( &src6, 0, sizeof(src6) ); + src6.sin6_family = AF_INET6; + ret = inet_pton( AF_INET6, "::1", &src6.sin6_addr); + ok(ret, "got error %u.\n", WSAGetLastError()); + dst6.sin6_scope_id = 0; + memset( &best6, 0xcc, sizeof(best6) ); + memset( &fwd_row, 0xcc, sizeof(fwd_row) ); + ret = GetBestRoute2( &link_local_luid, 0, (SOCKADDR_INET *)&src6, (SOCKADDR_INET *)&dst6, 0, &fwd_row, + (SOCKADDR_INET *)&best6 ); + todo_wine ok( !ret, "got error %lu.\n", ret ); + todo_wine ok( !memcmp( &src6, &best6, sizeof(best6) ), "got different address %s.\n", + inet_ntop( AF_INET6, &best6.sin6_addr, s, sizeof(s) )); + ok( !fwd_row.InterfaceIndex, "got %lu.\n", fwd_row.InterfaceIndex ); + ok( IN6_IS_ADDR_UNSPECIFIED(&fwd_row.DestinationPrefix.Prefix.Ipv6.sin6_addr), "expected unspecifed address.\n" ); + + /* Specified source address takes precedence over specified interface. */ + dst6.sin6_scope_id = 0; + memset( &best6, 0xcc, sizeof(best6) ); + memset( &fwd_row, 0xcc, sizeof(fwd_row) ); + src6 = link_local_addr6; + src6.sin6_port = 1; + ret = GetBestRoute2( &link_local_luid, loopback_index, (SOCKADDR_INET *)&src6, (SOCKADDR_INET *)&dst6, 0, &fwd_row, + (SOCKADDR_INET *)&best6 ); + ok( !ret, "got error %lu.\n", ret ); + ok( fwd_row.InterfaceIndex == link_local_index, "got %lu, expected %lu.\n", fwd_row.InterfaceIndex, link_local_index ); + ok( !fwd_row.Loopback, "got %d.\n", fwd_row.Loopback ); + ok( best6.sin6_scope_id == link_local_index, "got %lu, expected %lu.\n", best6.sin6_scope_id, link_local_index ); + ok( !memcmp( &best6, &link_local_addr6, sizeof(best6) ), "got different address.\n" ); + + src6.sin6_scope_id = 0; + ret = GetBestRoute2( NULL, loopback_index, (SOCKADDR_INET *)&src6, (SOCKADDR_INET *)&dst6, 0, &fwd_row, + (SOCKADDR_INET *)&best6 ); + ok( !ret, "got error %lu.\n", ret ); + ok( fwd_row.InterfaceIndex == link_local_index, "got %lu, expected %lu.\n", fwd_row.InterfaceIndex, link_local_index ); + + src6.sin6_scope_id = 0xffff; + ret = GetBestRoute2( NULL, loopback_index, (SOCKADDR_INET *)&src6, (SOCKADDR_INET *)&dst6, 0, &fwd_row, + (SOCKADDR_INET *)&best6 ); + ok( ret == ERROR_NOT_FOUND, "got error %lu.\n", ret ); + ok( !fwd_row.InterfaceIndex, "got %lu, expected %lu.\n", fwd_row.InterfaceIndex, link_local_index ); + + src6.sin6_scope_id = link_local_index; + ret = GetBestRoute2( NULL, loopback_index, (SOCKADDR_INET *)&src6, (SOCKADDR_INET *)&dst6, 0, &fwd_row, + (SOCKADDR_INET *)&best6 ); + ok( !ret, "got error %lu.\n", ret ); + ok( fwd_row.InterfaceIndex == link_local_index, "got %lu, expected %lu.\n", fwd_row.InterfaceIndex, link_local_index ); + + luid.Value = 0xdeadbeef; + ret = GetBestRoute2( &luid, 0xdeadbeef, (SOCKADDR_INET *)&link_local_addr6, (SOCKADDR_INET *)&dst6, 0, &fwd_row, + (SOCKADDR_INET *)&best6 ); + ok( !ret, "got error %lu.\n", ret ); + ok( fwd_row.InterfaceIndex == link_local_index, "got %lu, expected %lu.\n", fwd_row.InterfaceIndex, link_local_index ); + + /* ... but if iface index is specified without luid that will still fail for invalid index. */ + ret = GetBestRoute2( NULL, 0xdeadbeef, (SOCKADDR_INET *)&link_local_addr6, (SOCKADDR_INET *)&dst6, 0, &fwd_row, + (SOCKADDR_INET *)&best6 ); + ok( ret == ERROR_FILE_NOT_FOUND, "got error %lu.\n", ret ); + + luid.Value = 0xdeadbeef; + ret = GetBestRoute2( &luid, 0xdeadbeef, NULL, (SOCKADDR_INET *)&dst6, 0, &fwd_row, (SOCKADDR_INET *)&best6 ); + ok( ret == ERROR_NOT_FOUND, "got error %lu.\n", ret ); + + /* Test with global address. */ + ret = inet_pton( AF_INET6, "2ead:beaf:dead:beaf::beaf", &dst6.sin6_addr); + ok(ret, "got error %u.\n", WSAGetLastError()); + + dst6.sin6_scope_id = 0xdeadbeef; + ret = GetBestInterfaceEx( (struct sockaddr *)&dst6, &index ); + ok( ret == ERROR_INVALID_PARAMETER, "got error %lu.\n", ret ); + + dst6.sin6_scope_id = link_local_index; + ret = GetBestInterfaceEx( (struct sockaddr *)&dst6, &index ); + ok( ret == ERROR_INVALID_PARAMETER, "got error %lu.\n", ret ); + + dst6.sin6_scope_id = 0; + ret = GetBestInterfaceEx( (struct sockaddr *)&dst6, &index ); + if (ret == ERROR_NETWORK_UNREACHABLE) + { + skip( "Global IPv6 address is unreachable.\n" ); + goto loopback_ipv6; + } + ok( !ret, "got error %lu.\n", ret ); + r = find_ipforward_row( table, index ); + ok( !!r, "got NULL.\n" ); + default_route_index = index; + dst6.sin6_scope_id = index; + ret = GetBestInterfaceEx( (struct sockaddr *)&dst6, &index ); + ok( ret == ERROR_INVALID_PARAMETER, "got error %lu.\n", ret ); + + ret = GetBestRoute2( NULL, 0, NULL, (SOCKADDR_INET *)&dst6, 0, &fwd_row, (SOCKADDR_INET *)&best6 ); + ok( ret == ERROR_INVALID_PARAMETER, "got error %lu.\n", ret ); + + dst6.sin6_scope_id = 0; + ret = GetBestRoute2( NULL, 0, NULL, (SOCKADDR_INET *)&dst6, 0, &fwd_row, (SOCKADDR_INET *)&best6 ); + ok( !ret, "got error %lu.\n", ret ); + ok( fwd_row.InterfaceIndex == index, "got %lu, %lu.\n", fwd_row.InterfaceIndex, index ); + ok( !IN6_IS_ADDR_UNSPECIFIED(&fwd_row.NextHop.Ipv6.sin6_addr) + || broken( IN6_IS_ADDR_UNSPECIFIED(&fwd_row.NextHop.Ipv6.sin6_addr) /* Win10 1507-1709 */), + "got unspecified address.\n" ); + + ret = GetBestRoute2( NULL, index, NULL, (SOCKADDR_INET *)&dst6, 0, &fwd_row, (SOCKADDR_INET *)&best6 ); + ok( !ret, "got error %lu.\n", ret ); + ok( fwd_row.InterfaceIndex == index, "got %lu, %lu.\n", fwd_row.InterfaceIndex, index ); + global_addr6 = best6; + + memset( &src6, 0, sizeof(src6) ); + src6.sin6_family = AF_INET6; + ret = inet_pton( AF_INET6, "dead:beaf:dead:beaf::beaf", &src6.sin6_addr); + ok(ret, "got error %u.\n", WSAGetLastError()); + + ret = GetBestRoute2( NULL, 0, (SOCKADDR_INET *)&src6, (SOCKADDR_INET *)&dst6, 0, &fwd_row, (SOCKADDR_INET *)&best6 ); + ok( ret == ERROR_NOT_FOUND, "got error %lu.\n", ret ); + + src6 = global_addr6; + src6.sin6_port = 28; + ret = GetBestRoute2( NULL, link_local_index, (SOCKADDR_INET *)&src6, (SOCKADDR_INET *)&dst6, 0, &fwd_row, + (SOCKADDR_INET *)&best6 ); + ok( !ret, "got error %lu.\n", ret ); + ok( fwd_row.InterfaceIndex == index, "got %lu, %lu.\n", fwd_row.InterfaceIndex, index ); + ok( !best6.sin6_scope_id, "got %lu.\n", best6.sin6_scope_id ); + ok( !memcmp( &global_addr6, &best6, sizeof(best6) ), "got different address.\n" ); + ok( !IN6_IS_ADDR_UNSPECIFIED(&fwd_row.NextHop.Ipv6.sin6_addr) + || broken( IN6_IS_ADDR_UNSPECIFIED(&fwd_row.NextHop.Ipv6.sin6_addr) /* Win10 1507-1709 */), + "got unspecified address.\n" ); + + src6.sin6_scope_id = 1; + ret = GetBestRoute2( NULL, link_local_index, (SOCKADDR_INET *)&src6, (SOCKADDR_INET *)&dst6, 0, &fwd_row, + (SOCKADDR_INET *)&best6 ); + ok( ret == ERROR_INVALID_PARAMETER, "got error %lu.\n", ret ); + + memset( &src6, 0, sizeof(src6) ); + src6.sin6_family = AF_INET6; + ret = inet_pton( AF_INET6, "::1", &src6.sin6_addr); + ok(ret, "got error %u.\n", WSAGetLastError()); + memset( &best6, 0xcc, sizeof(best6) ); + memset( &fwd_row, 0xcc, sizeof(fwd_row) ); + ret = GetBestRoute2( NULL, 0, (SOCKADDR_INET *)&src6, (SOCKADDR_INET *)&dst6, 0, &fwd_row, (SOCKADDR_INET *)&best6 ); + ok( ret == ERROR_NETWORK_UNREACHABLE, "got error %lu.\n", ret ); + ok( !fwd_row.InterfaceIndex, "got %lu.\n", fwd_row.InterfaceIndex ); + ok( !fwd_row.InterfaceLuid.Value, "got %#I64x.\n", fwd_row.InterfaceLuid.Value ); + ok( IN6_IS_ADDR_UNSPECIFIED(&best6.sin6_addr), "expected unspecifed address.\n" ); + ok( IN6_IS_ADDR_UNSPECIFIED(&fwd_row.NextHop.Ipv6.sin6_addr), "expected unspecifed address.\n" ); + + luid.Value = 0; + ret = GetBestRoute2( &luid, 0xdeadbeef, NULL, (SOCKADDR_INET *)&dst6, 0, &fwd_row, (SOCKADDR_INET *)&best6 ); + ok( !ret, "got error %lu.\n", ret ); + ok( fwd_row.InterfaceIndex == index, "got %lu, %lu.\n", fwd_row.InterfaceIndex, index ); + ok( !best6.sin6_scope_id, "got %lu.\n", best6.sin6_scope_id ); + memset( &uni_row, 0, sizeof(uni_row) ); + *(struct sockaddr_in6 *)&uni_row.Address = best6; + uni_row.InterfaceIndex = fwd_row.InterfaceIndex; + ret = GetUnicastIpAddressEntry( &uni_row ); + ok( !ret, "got error %lu.\n", ret ); + ok( !memcmp( &uni_row.Address, &best6, sizeof(best6) ), "got different address.\n" ); + ok( !IN6_IS_ADDR_UNSPECIFIED(&fwd_row.NextHop.Ipv6.sin6_addr) + || broken( IN6_IS_ADDR_UNSPECIFIED(&fwd_row.NextHop.Ipv6.sin6_addr) /* Win10 1507-1709 */), + "got unspecified address.\n" ); + + memset( &fwd_row, 0xcc, sizeof(fwd_row) ); + memset( &best6, 0xcc, sizeof(best6) ); + ret = GetBestRoute2( NULL, loopback_index, NULL, (SOCKADDR_INET *)&dst6, 0, &fwd_row, (SOCKADDR_INET *)&best6 ); + ok( ret == ERROR_NETWORK_UNREACHABLE, "got error %lu.\n", ret ); + ok( !fwd_row.InterfaceIndex, "got %#lx.\n", fwd_row.InterfaceIndex ); + ok( !best6.sin6_scope_id, "got %lu.\n", best6.sin6_scope_id ); + ok( IN6_IS_ADDR_UNSPECIFIED(&fwd_row.NextHop.Ipv6.sin6_addr), "expected unspecifed address.\n" ); + + memset( &dst6.sin6_addr, 0, sizeof(dst6.sin6_addr) ); + ret = GetBestRoute2( NULL, 0, NULL, (SOCKADDR_INET *)&dst6, 0, &fwd_row, (SOCKADDR_INET *)&best6 ); + ok( !ret, "got error %lu.\n", ret ); + ok( fwd_row.InterfaceIndex == index, "got %lu, %lu.\n", fwd_row.InterfaceIndex, index ); + ok( !memcmp( &global_addr6, &best6, sizeof(best6) ), "got different address.\n" ); + + /* Test with loopback address. */ +loopback_ipv6: + ret = inet_pton( AF_INET6, "::1", &dst6.sin6_addr); + ok(ret, "got error %u.\n", WSAGetLastError()); + dst6.sin6_scope_id = loopback_index; + ret = GetBestInterfaceEx( (struct sockaddr *)&dst6, &index ); + ok( ret == ERROR_INVALID_PARAMETER, "got error %lu.\n", ret ); + + dst6.sin6_scope_id = 0; + ret = GetBestInterfaceEx( (struct sockaddr *)&dst6, &index ); + ok( !ret, "got error %lu.\n", ret ); + ok( index == loopback_index, "got %lu, expected %lu.\n", index, loopback_index ); + ok( !best6.sin6_scope_id, "got %lu.\n", best6.sin6_scope_id ); + + dst6.sin6_scope_id = loopback_index; + ret = GetBestRoute2( NULL, loopback_index, NULL, (SOCKADDR_INET *)&dst6, 0, &fwd_row, (SOCKADDR_INET *)&best6 ); + ok( ret == ERROR_INVALID_PARAMETER, "got error %lu.\n", ret ); + + dst6.sin6_scope_id = 0; + ret = GetBestRoute2( NULL, loopback_index, NULL, (SOCKADDR_INET *)&dst6, 0, &fwd_row, (SOCKADDR_INET *)&best6 ); + ok( !ret, "got error %lu.\n", ret ); + ok( fwd_row.InterfaceIndex == loopback_index, "got %lu, %lu.\n", fwd_row.InterfaceIndex, loopback_index ); + ret = GetBestRoute2( NULL, 0, NULL, (SOCKADDR_INET *)&dst6, 0, &fwd_row, (SOCKADDR_INET *)&best6 ); + ok( !ret, "got error %lu.\n", ret ); + ok( fwd_row.InterfaceIndex == loopback_index, "got %lu, %lu.\n", fwd_row.InterfaceIndex, loopback_index ); + ok( !best6.sin6_scope_id, "got %lu.\n", best6.sin6_scope_id ); + ok( !memcmp( &dst6, &best6, sizeof(best6) ), "got different address.\n" ); + + memset( &best6, 0xcc, sizeof(best6) ); + if (default_route_index != ~0u) + { + ok( default_route_index != loopback_index, "got same interfaces.\n" ); + ret = GetBestRoute2( NULL, default_route_index, NULL, (SOCKADDR_INET *)&dst6, 0, &fwd_row, + (SOCKADDR_INET *)&best6 ); + ok( ret == ERROR_INVALID_PARAMETER, "got error %lu.\n", ret ); + ok( !fwd_row.InterfaceIndex, "got %#lx.\n", fwd_row.InterfaceIndex ); + } + + /* Test with ipv4 */ + memset( &dst4, 0xcc, sizeof(dst4) ); + dst4.sin_family = AF_INET; + ret = inet_pton( AF_INET, "25.25.0.0", &dst4.sin_addr); + ok(ret, "got error %u.\n", WSAGetLastError()); + + ret = GetBestInterfaceEx( (struct sockaddr *)&dst4, &index ); + if (ret == ERROR_NETWORK_UNREACHABLE) + { + skip( "Global IPv4 address is unreachable.\n" ); + goto done; + } + ok( !ret, "got error %lu.\n", ret ); + default_route_index = index; + + memset( &fwd_row, 0xcc, sizeof(fwd_row) ); + memset( &best4, 0xcc, sizeof(best4) ); + ret = GetBestRoute2( NULL, default_route_index, NULL, (SOCKADDR_INET *)&dst4, 0, &fwd_row, (SOCKADDR_INET *)&best4 ); + ok( !ret, "got error %lu.\n", ret ); + ok( fwd_row.InterfaceIndex == default_route_index, "got %lu, expected %lu.\n", fwd_row.InterfaceIndex, + default_route_index ); + ok( fwd_row.NextHop.Ipv4.sin_addr.s_addr, "expected specified next hop.\n" ); + memset( &uni_row, 0, sizeof(uni_row) ); + *(struct sockaddr_in *)&uni_row.Address = best4.Ipv4; + uni_row.InterfaceIndex = fwd_row.InterfaceIndex; + ret = GetUnicastIpAddressEntry( &uni_row ); + ok( !ret, "got error %lu.\n", ret ); + ok( !memcmp( &uni_row.Address, &best4, sizeof(best4) ), "got different address, %s, %s.\n", + inet_ntop( AF_INET, &best4.Ipv4.sin_addr, s, sizeof(s) ), inet_ntop( AF_INET, + &uni_row.Address.Ipv4.sin_addr, s2, sizeof(s2) ) ); + /* GetBestRoute2 zeroes the whole SOCKADDR_INET, not just IPv4 part. */ + ok( !best4.Ipv6.sin6_addr.u.Word[7], "got %#x.\n", best4.Ipv6.sin6_addr.u.Word[7] ); + global_addr4 = best4.Ipv4; + + src4.sin_family = AF_INET; + ret = inet_pton( AF_INET, "127.0.0.1", &src4.sin_addr ); + ok(ret, "got error %u.\n", WSAGetLastError()); + memset( &best4, 0xcc, sizeof(best4) ); + ret = GetBestRoute2( NULL, 0, (SOCKADDR_INET *)&src4, (SOCKADDR_INET *)&dst4, 0, &fwd_row, (SOCKADDR_INET *)&best4 ); + ok( ret == ERROR_NETWORK_UNREACHABLE, "got error %lu.\n", ret ); + ok( !best4.Ipv4.sin_family, "got %u.\n", best4.Ipv4.sin_family ); + ok( !best4.Ipv6.sin6_addr.u.Word[7], "got %#x.\n", best4.Ipv6.sin6_addr.u.Word[7] ); + + src4 = global_addr4; + ret = GetBestRoute2( NULL, loopback_index, (SOCKADDR_INET *)&src4, (SOCKADDR_INET *)&dst4, 0, &fwd_row, + (SOCKADDR_INET *)&best4 ); + ok( !ret, "got error %lu.\n", ret ); + ok( fwd_row.InterfaceIndex == default_route_index, "got %lu, expected %lu.\n", fwd_row.InterfaceIndex, + link_local_index ); + ok( fwd_row.NextHop.Ipv4.sin_addr.s_addr, "expected specified next hop.\n" ); + ok( !memcmp( &global_addr4, &best4, sizeof(best4.Ipv4) ), "got different address.\n" ); + + memset( &src4, 0xcc, sizeof(src4) ); + memset( &fwd_row, 0xcc, sizeof(fwd_row) ); + ret = GetBestRoute2( NULL, default_route_index, (SOCKADDR_INET *)&src4, (SOCKADDR_INET *)&dst4, 0, &fwd_row, + (SOCKADDR_INET *)&best4 ); + ok( !ret, "got error %lu.\n", ret ); + ok( fwd_row.InterfaceIndex == default_route_index, "got %lu, expected %lu.\n", fwd_row.InterfaceIndex, + link_local_index ); + + src4 = best4.Ipv4; + ret = GetBestRoute2( NULL, default_route_index, (SOCKADDR_INET *)&src4, (SOCKADDR_INET *)&dst4, 0, &fwd_row, + (SOCKADDR_INET *)&best4 ); + ok( !ret, "got error %lu.\n", ret ); + ok( fwd_row.InterfaceIndex == default_route_index, "got %lu, expected %lu.\n", fwd_row.InterfaceIndex, + link_local_index ); + ok( fwd_row.NextHop.Ipv4.sin_addr.s_addr, "expected specified next hop.\n" ); + + ret = GetBestRoute2( NULL, loopback_index, NULL, (SOCKADDR_INET *)&dst4, 0, &fwd_row, (SOCKADDR_INET *)&best4 ); + ok( ret == ERROR_NETWORK_UNREACHABLE, "got error %lu.\n", ret ); + + ret = inet_pton( AF_INET, "127.0.0.1", &dst4.sin_addr); + ok(ret, "got error %u.\n", WSAGetLastError()); + ret = GetBestRoute2( NULL, default_route_index, NULL, (SOCKADDR_INET *)&dst4, 0, &fwd_row, (SOCKADDR_INET *)&best4 ); + ok( ret == ERROR_INVALID_PARAMETER, "got error %lu.\n", ret ); + + ret = GetBestRoute2( NULL, 0, NULL, (SOCKADDR_INET *)&dst4, 0, &fwd_row, (SOCKADDR_INET *)&best4 ); + ok( !ret, "got error %lu.\n", ret ); + ok( !fwd_row.NextHop.Ipv4.sin_addr.s_addr, "expected unspecified next hop.\n" ); + +done: + FreeMibTable( table ); +} + START_TEST(iphlpapi) { WSADATA wsa_data; @@ -3530,6 +4014,7 @@ START_TEST(iphlpapi) test_ConvertGuidToString(); test_compartments(); test_GetIpInterface(); + test_best_routes(); freeIPHlpApi(); }
From: Paul Gofman pgofman@codeweavers.com
--- dlls/iphlpapi/iphlpapi_main.c | 62 ++++++++++++++++++++++++++++++++--- 1 file changed, 58 insertions(+), 4 deletions(-)
diff --git a/dlls/iphlpapi/iphlpapi_main.c b/dlls/iphlpapi/iphlpapi_main.c index e860226b5bd..6b30f637dcc 100644 --- a/dlls/iphlpapi/iphlpapi_main.c +++ b/dlls/iphlpapi/iphlpapi_main.c @@ -4733,6 +4733,13 @@ static MIB_UNICASTIPADDRESS_ROW *find_first_matching_unicast_addr( MIB_UNICASTIP return NULL; }
+static BOOL is_addr_unspecified( const SOCKADDR_INET *addr, BOOL v6 ) +{ + if (v6 && IN6_IS_ADDR_UNSPECIFIED(&addr->Ipv6.sin6_addr)) return TRUE; + if (!v6 && !addr->Ipv4.sin_addr.s_addr) return TRUE; + return FALSE; +} + /****************************************************************** * GetBestRoute2 (IPHLPAPI.@) */ @@ -4761,9 +4768,8 @@ DWORD WINAPI GetBestRoute2( NET_LUID *luid, NET_IFINDEX index, v6 = dst->si_family == AF_INET6; if (src) { - if (src->si_family != dst->si_family) src = NULL; - else if (v6 && IN6_IS_ADDR_UNSPECIFIED(&src->Ipv6.sin6_addr)) src = NULL; - else if (!v6 && !src->Ipv4.sin_addr.s_addr) src = NULL; + if (src->si_family != dst->si_family) src = NULL; + else if (is_addr_unspecified( src, v6 )) src = NULL; }
if (v6 && !IN6_IS_ADDR_LINKLOCAL(&dst->Ipv6.sin6_addr) && dst->Ipv6.sin6_scope_id) @@ -4824,6 +4830,54 @@ DWORD WINAPI GetBestRoute2( NET_LUID *luid, NET_IFINDEX index, } }
+ uni_row = NULL; + if (!src && !(v6 && IN6_IS_ADDR_LINKLOCAL(&dst->Ipv6.sin6_addr))) + { + static const struct in6_addr ipv6_default_addr = {{{ 0x20, 0x01, 0x0d, 0xb8 }}}; + SOCKADDR_INET src_addr, dst_addr; + WORD ver = MAKEWORD (2, 2); + WSADATA data; + SOCKET s; + int len; + + WSAStartup ( ver, &data ); + if ((s = socket( dst->si_family, SOCK_DGRAM, IPPROTO_UDP )) == -1) goto skip_system_route; + dst_addr = *dst; + if (is_addr_unspecified( dst, v6 )) + { + /* GetBestRoute2() should return default route in this case while connect() while unspecified address for + * connect() has different semantics. So use some global IP address instead. */ + if (v6) dst_addr.Ipv6.sin6_addr = ipv6_default_addr; + else dst_addr.Ipv4.sin_addr.s_addr = 0x01010101; + } + if (connect( s, (struct sockaddr *)&dst_addr, sizeof(dst_addr) ) == -1) + { + WARN( "Resolving destination address failed.\n" ); + goto skip_system_route; + } + len = sizeof(src_addr); + if (getsockname( s, (struct sockaddr *)&src_addr, &len )) + { + WARN( "getsockname failed, error %d.\n", WSAGetLastError() ); + goto skip_system_route; + } + if (!(uni_row = find_first_matching_unicast_addr( uni, v6, &src_addr, &dst_addr, index ))) + { + WARN( "Could not find matching unicast addr for system route.\n" ); + goto skip_system_route; + } + if (index && uni_row->InterfaceIndex != index) + { + WARN( "Specified index %lu doesn't match system %lu.\n", index, uni_row->InterfaceIndex ); + uni_row = NULL; + goto skip_system_route; + } + index = uni_row->InterfaceIndex; +skip_system_route: + if (s != -1) closesocket(s); + WSACleanup(); + } + max_prefix_len = -1; best_idx = 0; for (i = 0; i < fwd->NumEntries; ++i) @@ -4851,7 +4905,7 @@ DWORD WINAPI GetBestRoute2( NET_LUID *luid, NET_IFINDEX index, goto done; }
- if (!(uni_row = find_first_matching_unicast_addr( uni, v6, src, dst, index ))) + if (!uni_row && !(uni_row = find_first_matching_unicast_addr( uni, v6, src, dst, index ))) { WARN( "Could not find source address.\n" ); ret = ERROR_NOT_FOUND;
v2: - Insert GetBestRoute2() prototype sorted; - Fix FreeMibTable() calls.