c++ - 获取远程桌面客户端的公共 IP 地址
问题描述
当用户启动与 RDP 的远程连接时,我正在开发一个在远程服务器(Windows Server 2016-2019、Windows 10)上运行的应用程序。我正在使用 C++ 和 Win API。
我正在尝试获取远程桌面客户端的公共 IP地址。我使用了WTSQuerySessionInformationW方法,其中 WTSInfoClass 设置为 WTSClientAddress。不幸的是,看起来这个函数返回客户端计算机的本地 IP,例如 192.168.1.10 而不是公共 IP。
该场景是客户端正在从世界任何地方访问远程桌面(因此不仅来自本地网络)。在应用程序和服务日志 -> Microsoft -> Windows -> TerminalServices-LocalSessionManager 下的窗口事件查看器中,我可以看到公共 IP 地址(源网络地址)。
我可以使用哪个函数或机制来获取此 IP 地址?
解决方案
当我们阅读WTS_CLIENT_ADDRESS
结构时
客户端网络地址由 RDP 客户端在连接到服务器时自行报告。这可能与 实际连接到服务器的地址不同。例如,假设客户端和服务器之间有一个 NAT。客户端可以上报自己的IP地址,但实际连接服务器的IP地址是NAT地址。对于 VPN 连接,客户端可能无法发现 IP 地址。如果无法发现,客户端可以报告它拥有的唯一 IP 地址,这可能是 ISP 分配的地址。由于该地址可能不是实际的网络地址,因此不应将其用作客户端身份验证的一种形式。
为了从服务器视图中获取实际的网络地址,我们可以使用WinStationQueryInformationW
-it WinStationRemoteAddress
return WINSTATIONREMOTEADDRESS
。
您可以复制粘贴此声明或所有winsta.h(由于未知原因未包含在 sdk 中)
乍一看,我们可以通过布局确定它与(和)WINSTATIONREMOTEADDRESS
相同。我们可以重新解释从to的转换指针。SOCKADDR
SOCKADDR_IN
SOCKADDR_IN6
WINSTATIONREMOTEADDRESS
SOCKADDR
但这是严重错误。结构有不同的对齐方式!
C_ASSERT(FIELD_OFFSET(SOCKADDR_IN, sin_port) == 2);
C_ASSERT(FIELD_OFFSET(WINSTATIONREMOTEADDRESS, ipv4.sin_port) == 4);
C_ASSERT(FIELD_OFFSET(SOCKADDR_IN, sin_addr) == 4);
C_ASSERT(FIELD_OFFSET(WINSTATIONREMOTEADDRESS, ipv4.in_addr) == 8);
使用WinStationQueryInformationW
需要与winsta.lib链接或从winsta.dll在运行时获取它
所以最终代码可以是下一个:
typedef enum _WINSTATIONINFOCLASS {
// ...
WinStationRemoteAddress = 29,
// ...
} WINSTATIONINFOCLASS;
#define LOGONID_CURRENT ((ULONG)-1)
typedef struct {
unsigned short sin_family;
union {
struct {
USHORT sin_port;
ULONG in_addr;
UCHAR sin_zero[8];
} ipv4;
struct {
USHORT sin6_port;
ULONG sin6_flowinfo;
USHORT sin6_addr[8];
ULONG sin6_scope_id;
} ipv6;
};
} WINSTATIONREMOTEADDRESS,
*PWINSTATIONREMOTEADDRESS;
EXTERN_C
DECLSPEC_IMPORT
BOOLEAN
WINAPI
WinStationQueryInformationW(
_In_opt_ HANDLE hServer,
_In_ ULONG SessionId,
_In_ WINSTATIONINFOCLASS WinStationInformationClass,
_Out_writes_bytes_(WinStationInformationLength) PVOID pWinStationInformation,
_In_ ULONG WinStationInformationLength,
_Out_ PULONG pReturnLength
);
ULONG GetRdpClientAddressFromServerView()
{
ULONG dwError = NOERROR;
ULONG cb;
union {
SOCKADDR sa;
SOCKADDR_IN sa4;
SOCKADDR_IN6 sa6;
};
WINSTATIONREMOTEADDRESS ra;
if (WinStationQueryInformationW(0, LOGONID_CURRENT, WinStationRemoteAddress, &ra, sizeof(ra), &cb))
{
switch (sa.sa_family = ra.sin_family)
{
case AF_INET:
sa4.sin_port = ra.ipv4.sin_port;
sa4.sin_addr.S_un.S_addr = ra.ipv4.in_addr;
RtlZeroMemory(sa4.sin_zero, sizeof(sa4.sin_zero));
cb = sizeof(SOCKADDR_IN);
break;
case AF_INET6:
sa6.sin6_port = ra.ipv6.sin6_port;
sa6.sin6_flowinfo = ra.ipv6.sin6_flowinfo;
memcpy(&sa6.sin6_addr, &ra.ipv6.sin6_addr, sizeof(in6_addr));
sa6.sin6_scope_id = ra.ipv6.sin6_scope_id;
cb = sizeof(SOCKADDR_IN6);
break;
default:
dwError = ERROR_GEN_FAILURE;
}
if (dwError == NOERROR)
{
// assume that WSAStartup already called
// WSADATA wd;
// WSAStartup(WINSOCK_VERSION, &wd);
char AddressString[64];
ULONG dwAddressStringLength = _countof(AddressString);
if (WSAAddressToStringA(&sa, cb, 0, AddressString, &dwAddressStringLength) == NOERROR)
{
DbgPrint("client ip is %s\n", AddressString);
}
else
{
dwError = WSAGetLastError();
}
}
}
else
{
dwError = GetLastError();
}
return dwError;
}
推荐阅读
- angular - AG 网格在组索引处获取选定的行
- c# - 我在哪里可以找到 TabbedPage 的 ToolbarPlacement 属性?
- asp.net-mvc - asp.net/MVC 自定义模型验证属性不起作用
- r - 如何在ggplot中映射光栅正确投影?
- java - 在哪里可以找到 SimpleJPARepository 实现?
- ios - Flutter unique_identified 库问题?
- java - Rhapsody API 重载项目
- python - Scikit:如何限制每个样本的预测最大值
- typescript - 将逗号替换为分号更漂亮
- javascript - 执行字符串逗号字符','作为代码