Signed-off-by: Jinoh Kang jinoh.kang.kr@gmail.com --- include/netioapi.h | 1 + 1 file changed, 1 insertion(+)
diff --git a/include/netioapi.h b/include/netioapi.h index 6005e58e216..832f350b861 100644 --- a/include/netioapi.h +++ b/include/netioapi.h @@ -250,6 +250,7 @@ DWORD WINAPI ConvertInterfaceNameToLuidA(const char*,NET_LUID*); DWORD WINAPI ConvertInterfaceNameToLuidW(const WCHAR*,NET_LUID*); DWORD WINAPI ConvertLengthToIpv4Mask(ULONG,ULONG*); void WINAPI FreeMibTable(void*); +DWORD WINAPI GetBestRoute2(NET_LUID*,NET_IFINDEX,const SOCKADDR_INET*,const SOCKADDR_INET*,ULONG,PMIB_IPFORWARD_ROW2,SOCKADDR_INET*); DWORD WINAPI GetIfEntry2(MIB_IF_ROW2*); DWORD WINAPI GetIfEntry2Ex(MIB_IF_TABLE_LEVEL,MIB_IF_ROW2*); DWORD WINAPI GetIfTable2(MIB_IF_TABLE2**);
Signed-off-by: Jinoh Kang jinoh.kang.kr@gmail.com --- dlls/iphlpapi/tests/iphlpapi.c | 225 +++++++++++++++++++++++++++++++++ 1 file changed, 225 insertions(+)
diff --git a/dlls/iphlpapi/tests/iphlpapi.c b/dlls/iphlpapi/tests/iphlpapi.c index be50cc0b476..09049b2af0f 100644 --- a/dlls/iphlpapi/tests/iphlpapi.c +++ b/dlls/iphlpapi/tests/iphlpapi.c @@ -2391,6 +2391,230 @@ static void test_GetUnicastIpAddressTable(void) FreeMibTable( table ); }
+static void set_sockaddr_ipv4( SOCKADDR_INET *sockaddr, ULONG in_addr ) +{ + memset( sockaddr, 0, sizeof(*sockaddr) ); + sockaddr->Ipv4.sin_family = AF_INET; + sockaddr->Ipv4.sin_addr.S_un.S_addr = in_addr; +} + +static void test_GetBestRoute2(void) +{ + DWORD apiReturn; + SOCKADDR_INET source, destination, bestaddress, bestaddress_memory; + MIB_IPFORWARD_ROW2 bestroute, bestroute_memory; + int validmemflags = 0; + static const MIB_IPFORWARD_ROW2 route_zero; + static const NET_LUID zero_luid, ones_luid = { -1 }; + static const SOCKADDR_INET unspecaddr; + static const SOCKADDR invalidaddr = { -1 }; + static const SOCKADDR_IN in4any = { AF_INET }; + static const SOCKADDR_IN in4loopback = { AF_INET, 0, {{{ 127, 0, 0, 1 }}} }; + static const SOCKADDR_IN in4broadcast = { AF_INET, 0, {{{ 255, 255, 255, 255 }}} }; + static const SOCKADDR_IN6 in6any = { AF_INET6 }; + static const SOCKADDR_IN6 in6loopback = { AF_INET6, 0, 0, {{{ + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, + }}} }; + static const SOCKADDR_IN6 in6broadcast = { AF_INET6, 0, 0, {{{ + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + }}} }; + struct sockaddr_ptrlen + { + const struct sockaddr *ptr; + size_t len; + }; + static const struct getbestroute_test + { + int line; + const NET_LUID *luid; + NET_IFINDEX ifindex; + struct sockaddr_ptrlen source; + struct sockaddr_ptrlen destination; + ULONG options; + DWORD result; + BOOL todo; + int memload; + int memstore; + } getbestroute_tests[] = { +#define SA_PL(x) { ((struct sockaddr *)&(x)), sizeof(x) } + { __LINE__, &zero_luid, 0, { NULL }, SA_PL(in4loopback), 0, NO_ERROR }, + { __LINE__, &zero_luid, -1, { NULL }, SA_PL(in4loopback), 0, NO_ERROR }, + { __LINE__, &ones_luid, 0, { NULL }, SA_PL(in4loopback), 0, ERROR_NOT_FOUND }, + { __LINE__, NULL, -1, { NULL }, SA_PL(in4loopback), 0, ERROR_FILE_NOT_FOUND }, + { __LINE__, &zero_luid, 0, SA_PL(unspecaddr), SA_PL(in4loopback), 0, NO_ERROR }, + { __LINE__, &ones_luid, 0, SA_PL(unspecaddr), SA_PL(in4loopback), 0, ERROR_NOT_FOUND }, + { __LINE__, NULL, 0, SA_PL(unspecaddr), SA_PL(in4loopback), 0, NO_ERROR }, + { __LINE__, NULL, 0, SA_PL(in4any), SA_PL(in4loopback), 0, NO_ERROR }, + { __LINE__, NULL, 0, SA_PL(in4loopback), SA_PL(in4loopback), 0, NO_ERROR }, + { __LINE__, NULL, 0, SA_PL(in4broadcast),SA_PL(in4loopback), 0, ERROR_INVALID_PARAMETER, TRUE }, + { __LINE__, NULL, 0, SA_PL(in6any), SA_PL(in4loopback), 0, NO_ERROR }, + { __LINE__, NULL, 0, SA_PL(in6loopback), SA_PL(in4loopback), 0, NO_ERROR }, + { __LINE__, NULL, 0, SA_PL(in6broadcast),SA_PL(in4loopback), 0, NO_ERROR }, + { __LINE__, NULL, 0, { NULL }, SA_PL(in4loopback), 0, NO_ERROR, FALSE, 0, 0xc7 }, + { __LINE__, NULL, 0, { NULL }, SA_PL(in4loopback), 0, NO_ERROR, FALSE, 0xc0 }, + { __LINE__, NULL, 0, { NULL }, SA_PL(in4loopback), 0, NO_ERROR, FALSE, 0xc2 }, + { __LINE__, NULL, 0, { NULL }, SA_PL(in4loopback), 0, NO_ERROR, FALSE, 0xc4 }, + { __LINE__, NULL, 0, { NULL }, SA_PL(in4loopback), 0, NO_ERROR, FALSE, 0xc6 }, +#if 0 /* not reliable (fails spuriously) on Wine */ + { __LINE__, NULL, 0, { NULL }, SA_PL(in4loopback), 0, NO_ERROR, FALSE, 0xc1 }, + { __LINE__, NULL, 0, { NULL }, SA_PL(in4loopback), 0, NO_ERROR, FALSE, 0xc3 }, + { __LINE__, NULL, 0, { NULL }, SA_PL(in4loopback), 0, NO_ERROR, FALSE, 0xc5 }, + { __LINE__, NULL, 0, { NULL }, SA_PL(in4loopback), 0, NO_ERROR, FALSE, 0xc7 }, +#endif +#undef SA_PL + }; + UINT i; + + set_sockaddr_ipv4( &destination, INADDR_ANY ); + apiReturn = GetBestRoute2( NULL, 0, NULL, &destination, 0, &bestroute, &bestaddress ); + trace( "GetBestRoute2(NULL, 0, NULL, [...], 0, [...], [...]) = %d\n", apiReturn ); + if (apiReturn == ERROR_NOT_SUPPORTED) + { + skip("GetBestRoute2 not supported\n"); + return; + } + + apiReturn = GetBestRoute2( NULL, 0, NULL, NULL, 0, NULL, NULL ); + ok( apiReturn == ERROR_INVALID_PARAMETER, + "GetBestRoute2(NULL, 0, NULL, NULL, 0, NULL, NULL) returned %u, expected %u\n", + apiReturn, ERROR_INVALID_PARAMETER ); + + apiReturn = GetBestRoute2( NULL, 0, NULL, NULL, 0, &bestroute, NULL ); + ok( apiReturn == ERROR_INVALID_PARAMETER, + "GetBestRoute2(NULL, 0, NULL, NULL, 0, %p, NULL) returned %u, expected %u\n", + &bestroute, apiReturn, ERROR_INVALID_PARAMETER ); + + apiReturn = GetBestRoute2( NULL, 0, NULL, NULL, 0, NULL, &bestaddress ); + memcpy( &bestaddress, &invalidaddr, sizeof(invalidaddr) ); + ok( apiReturn == ERROR_INVALID_PARAMETER, + "GetBestRoute2(NULL, 0, NULL, NULL, 0, NULL, %p) returned %u, expected %u\n", + &bestaddress, apiReturn, ERROR_INVALID_PARAMETER ); + ok( memcmp(&bestaddress, &invalidaddr, sizeof(invalidaddr)) == 0, "bestaddress(.si_family = %u) has changed\n", bestaddress.si_family ); + + apiReturn = GetBestRoute2( NULL, 0, NULL, NULL, 0, &bestroute, &bestaddress ); + memcpy( &bestaddress, &invalidaddr, sizeof(invalidaddr) ); + ok( apiReturn == ERROR_INVALID_PARAMETER, + "GetBestRoute2(NULL, 0, NULL, NULL, 0, %p, %p) returned %u, expected %u\n", + &bestroute, &bestaddress, apiReturn, ERROR_INVALID_PARAMETER ); + ok( memcmp(&bestaddress, &invalidaddr, sizeof(invalidaddr)) == 0, "bestaddress(.si_family = %u) has changed\n", bestaddress.si_family ); + + set_sockaddr_ipv4( &destination, INADDR_LOOPBACK ); + memcpy( &bestaddress, &invalidaddr, sizeof(invalidaddr) ); + apiReturn = GetBestRoute2( NULL, 0, NULL, &destination, 0, &bestroute, NULL ); + ok( apiReturn == ERROR_INVALID_PARAMETER, + "GetBestRoute2(NULL, 0, NULL, [127.0.0.1], 0, %p, NULL) returned %u, expected %u\n", + &bestroute, apiReturn, ERROR_INVALID_PARAMETER ); + ok( memcmp(&bestaddress, &invalidaddr, sizeof(invalidaddr)) == 0, "bestaddress(.si_family = %u) has changed\n", bestaddress.si_family ); + + set_sockaddr_ipv4( &destination, INADDR_LOOPBACK ); + memcpy( &bestaddress, &invalidaddr, sizeof(invalidaddr) ); + apiReturn = GetBestRoute2( NULL, 0, NULL, &destination, 0, NULL, &bestaddress ); + ok( apiReturn == ERROR_INVALID_PARAMETER, + "GetBestRoute2(NULL, 0, NULL, [127.0.0.1], 0, NULL, %p) returned %u, expected %u\n", + &bestaddress, apiReturn, ERROR_INVALID_PARAMETER ); + ok( memcmp(&bestaddress, &invalidaddr, sizeof(invalidaddr)) == 0, "bestaddress(.si_family = %u) has changed\n", bestaddress.si_family ); + + memset( &destination, 0, sizeof(destination) ); + memcpy( &bestaddress, &invalidaddr, sizeof(invalidaddr) ); + apiReturn = GetBestRoute2( NULL, 0, NULL, &destination, 0, NULL, &bestaddress ); + ok( apiReturn == ERROR_INVALID_PARAMETER, + "GetBestRoute2(NULL, 0, NULL, <AF_UNSPEC>, 0, NULL, %p) returned %u, expected %u\n", + &bestaddress, apiReturn, ERROR_INVALID_PARAMETER ); + ok( memcmp(&bestaddress, &invalidaddr, sizeof(invalidaddr)) == 0, "bestaddress(.si_family = %u) has changed\n", bestaddress.si_family ); + + for (i = 0; i < ARRAY_SIZE(getbestroute_tests); i++) + { + const struct getbestroute_test *item = &getbestroute_tests[i]; + int validflags = item->memload & validmemflags; + NET_LUID luid; + NET_IFINDEX ifindex = 0; + + winetest_push_context("Subtest #%u (decl at line %d)", i, item->line); + + ok( (item->memload & ~validmemflags) == 0, + "current subtest may be incomplete due to previous test failure (memload = %d, validmemflags = %d)\n", + item->memload, validmemflags ); + + if (item->luid) + { + memcpy(&luid, item->luid, sizeof(luid)); + validflags |= 1; + } + else if (validflags & 1) + { + memcpy(&luid, &bestroute_memory.InterfaceLuid, sizeof(luid)); + } + + if (item->ifindex) + { + ifindex = item->ifindex; + validflags |= 2; + } + else if (validflags & 2) + { + ifindex = bestroute_memory.InterfaceIndex; + } + + if (item->source.len > sizeof(source)) abort(); /* prevent memory corruption */ + if (item->source.ptr) + { + memset( &source, 0, sizeof(source) ); + memcpy( &source, item->source.ptr, item->source.len ); + validflags |= 4; + } + else if (validflags & 4) + { + memcpy( &source, &bestaddress_memory, sizeof(source) ); + } + + if (item->destination.len > sizeof(destination)) abort(); /* prevent memory corruption */ + memset( &destination, 0, sizeof(destination) ); + memcpy( &destination, item->destination.ptr, item->destination.len ); + + apiReturn = GetBestRoute2( (validflags & 1) ? &luid : NULL, + (validflags & 2) ? ifindex : 0, + (validflags & 4) ? &source : NULL, + &destination, + item->options, + &bestroute, &bestaddress ); + todo_wine_if( item->todo ) + ok( apiReturn == item->result, + "GetBestRoute2 returned %u, expected %u\n", apiReturn, item->result ); + if (apiReturn == NO_ERROR) + { + ok( bestaddress.si_family == destination.si_family, + "bestaddress.si_family (%u) shall equal destination.si_family (%u)\n", + bestaddress.si_family, destination.si_family ); + + if (validflags & 0x40) + { + ok( memcmp( &bestroute_memory, &bestroute, sizeof(bestroute) ) == 0, + "returned bestroute does not match last cached value\n" ); + } + + if (validflags & 0x80) + { + ok( memcmp( &bestaddress_memory, &bestaddress, sizeof(bestaddress) ) == 0, + "returned bestaddress does not match last cached value\n" ); + } + + if (item->memstore) + { + memcpy( &bestroute_memory, &bestroute, sizeof(bestroute_memory) ); + memcpy( &bestaddress_memory, &bestaddress, sizeof(bestaddress_memory) ); + validmemflags = item->memstore; + } + } + else + { + ok( memcmp( &bestaddress, &unspecaddr, sizeof(bestaddress) ) == 0, "bestaddress shall be zeroed by GetBestRoute2\n" ); + ok( memcmp( &bestroute, &route_zero, sizeof(bestroute) ) == 0, "bestroute shall be zeroed by GetBestRoute2\n" ); + } + + winetest_pop_context(); + } +} + static void test_ConvertLengthToIpv4Mask(void) { DWORD ret; @@ -2703,6 +2927,7 @@ START_TEST(iphlpapi) test_GetIfTable2Ex(); test_GetUnicastIpAddressEntry(); test_GetUnicastIpAddressTable(); + test_GetBestRoute2(); test_ConvertLengthToIpv4Mask(); test_GetTcp6Table(); test_GetUdp6Table();
Signed-off-by: Jinoh Kang jinoh.kang.kr@gmail.com ---
Notes: A major problem with this patch is that the function is agnostic to the source argument. Even after some tinkering I still haven't figured out how exactly Windows uses this argument.
Also, possibly move GetBestRoute2 implementation to nsiproxy.sys if applicable?
dlls/iphlpapi/iphlpapi_main.c | 137 +++++++++++++++++++++++++++++++++- 1 file changed, 134 insertions(+), 3 deletions(-)
diff --git a/dlls/iphlpapi/iphlpapi_main.c b/dlls/iphlpapi/iphlpapi_main.c index f96a5dc5fd1..22e1f71639f 100644 --- a/dlls/iphlpapi/iphlpapi_main.c +++ b/dlls/iphlpapi/iphlpapi_main.c @@ -4416,16 +4416,147 @@ DWORD WINAPI GetBestRoute2(NET_LUID *luid, NET_IFINDEX index, ULONG options, PMIB_IPFORWARD_ROW2 bestroute, SOCKADDR_INET *bestaddress) { - static int once; + MIB_IPFORWARD_TABLE2 *table; + MIB_UNICASTIPADDRESS_TABLE *table_ip; + const MIB_IPFORWARD_ROW2 *matched; + NET_LUID luid_match; + DWORD ndx, ndx_ip; + SOCKADDR_INET dest, matched_src; + UINT addr_bytes; + const BYTE *dest_prefix; + DWORD ret; + static unsigned int once;
if (!once++) - FIXME("(%p, %d, %p, %p, 0x%08x, %p, %p): stub\n", luid, index, source, + FIXME("(%p, %d, %p, %p, 0x%08x, %p, %p): partial stub\n", luid, index, source, destination, options, bestroute, bestaddress);
if (!destination || !bestroute || !bestaddress) return ERROR_INVALID_PARAMETER;
- return ERROR_NOT_SUPPORTED; + memcpy( &dest, destination, sizeof(dest) ); + switch (dest.si_family) + { + case AF_INET: + addr_bytes = sizeof(dest.Ipv4.sin_addr); + dest_prefix = (const BYTE *)&dest.Ipv4.sin_addr; + break; + case AF_INET6: + addr_bytes = sizeof(dest.Ipv6.sin6_addr); + dest_prefix = (const BYTE *)&dest.Ipv6.sin6_addr; + break; + default: + return ERROR_INVALID_PARAMETER; + } + + if (luid) + { + NET_IFINDEX unused; + memcpy( &luid_match, luid, sizeof(luid_match) ); + if (luid_match.Value) + { + /* check LUID existence */ + ret = ConvertInterfaceLuidToIndex( &luid_match, &unused ); + if (ret) + { + if (ret == ERROR_FILE_NOT_FOUND) ret = ERROR_NOT_FOUND; + goto fail; + } + } + } + else if (index) + { + ret = ConvertInterfaceIndexToLuid( index, &luid_match ); + if (ret) goto fail; + } + else + { + memset( &luid_match, 0, sizeof(luid_match) ); + } + + ret = GetIpForwardTable2( dest.si_family, &table ); + if (ret) goto fail; + + ret = GetUnicastIpAddressTable( dest.si_family, &table_ip ); + if (ret) goto free_ipforwardtable2; + + for (ndx = 0, matched = NULL; ndx < table->NumEntries; ndx++) + { + const MIB_IPFORWARD_ROW2 *row = &table->Table[ndx]; + const BYTE *row_prefix; + const SOCKADDR_INET *forward_src; + UINT prefix_bytes, prefix_lastshr; + + if (luid_match.Value && row->InterfaceLuid.Value != luid_match.Value) continue; + if (row->DestinationPrefix.Prefix.si_family != dest.si_family) continue; + + if (row->DestinationPrefix.PrefixLength > addr_bytes * 8) continue; + switch (row->DestinationPrefix.Prefix.si_family) + { + case AF_INET: + row_prefix = (const BYTE *)&row->DestinationPrefix.Prefix.Ipv4.sin_addr; + break; + case AF_INET6: + row_prefix = (const BYTE *)&row->DestinationPrefix.Prefix.Ipv6.sin6_addr; + break; + default: + continue; + } + + prefix_bytes = row->DestinationPrefix.PrefixLength >> 3; + prefix_lastshr = -row->DestinationPrefix.PrefixLength & 7; + if (memcmp(dest_prefix, row_prefix, prefix_bytes) != 0 || + (prefix_lastshr && ((dest_prefix[prefix_bytes] ^ row_prefix[prefix_bytes]) >> + prefix_lastshr) != 0)) continue; + + forward_src = NULL; + for (ndx_ip = 0; ndx_ip < table_ip->NumEntries; ndx_ip++) + { + if (row->InterfaceLuid.Value == table_ip->Table[ndx_ip].InterfaceLuid.Value) + { + forward_src = &table_ip->Table[ndx_ip].Address; + break; + } + } + if (!forward_src) continue; + + if (!matched || row->DestinationPrefix.PrefixLength > matched->DestinationPrefix.PrefixLength || + (row->DestinationPrefix.PrefixLength == matched->DestinationPrefix.PrefixLength && + row->Metric < matched->Metric)) + { + matched = row; + memcpy( &matched_src, forward_src, sizeof(matched_src) ); + } + } + + if (matched) + { + ret = NO_ERROR; + } + else + { + /* No route matches, which can happen if there's no default route. */ + ret = ERROR_HOST_UNREACHABLE; + } + + heap_free( table_ip ); + +free_ipforwardtable2: + heap_free( table ); + +fail: + if (ret == NO_ERROR && matched) + { + memcpy( bestroute, matched, sizeof(*bestroute) ); + memcpy( bestaddress, &matched_src, sizeof(*bestaddress) ); + } + else + { + memset( bestroute, 0, sizeof(*bestroute) ); + memset( bestaddress, 0, sizeof(*bestaddress) ); + } + TRACE("returning %d\n", ret); + return ret; }
/******************************************************************