如何区分套接字句柄和文件句柄
How to differentiate between socket handle and file handle
我需要检查 windows 中进程创建事件的某些行为,我需要实施一个规则来检查传递给 createprocess api 调用的 startupinfo 结构并提取 std input/std 输出处理创建过程的值。
然后我必须检查这个句柄是否属于 tcp 套接字。
是否有任何 api 函数可以帮助我获取有关我拥有的句柄号(无论是文件句柄还是套接字句柄)的任何信息?
使用GetFileType()
函数
Retrieves the file type of the specified file.
Syntax
DWORD WINAPI GetFileType( _In_ HANDLE hFile );
Parameters
hFile [in]
A handle to the file.
Return value
The function returns one of the following values.
FILE_TYPE_CHAR
The specified file is a character file, typically an LPT device or a console.
FILE_TYPE_DISK
The specified file is a disk file.
FILE_TYPE_PIPE
The specified file is a socket, a named pipe, or an anonymous pipe.
FILE_TYPE_REMOTE
Unused.
FILE_TYPE_UNKNOWN
Either the type of the specified file is unknown, or the function failed
基于@RemyLebeau 的回答,我想看看我是否能找到一种可靠的方法来区分套接字和管道(GetFileType()
做不到),我想出了以下方法,似乎有效并且没有明显的缺点。
它的要点是如果传递任何不是 SOCKET 的东西,getsockopt()
将 return WSAENOTSOCK
(= 10038)。因此,这个测试本身就足够了,只要 传递给它的任何句柄是 SOCKET 或文件或管道 HANDLE。不要只传递任何旧的 HANDLE(有各种各样的),否则它可能会根据下面@HansPassant 的第一条评论感到困惑。
示例代码:
#include <winsock2.h> // * before* windows.h (!)
#include <windows.h>
#include <assert.h>
#include <iostream>
int main ()
{
WSADATA wsa_data;
int err = WSAStartup (2, &wsa_data);
assert (err == 0);
// Pipe
HANDLE hReadPipe, hWritePipe;
BOOL ok = CreatePipe (&hReadPipe, &hWritePipe, NULL, 2048);
assert (ok);
int opt;
int optlen = sizeof (opt);
err = getsockopt ((SOCKET) hReadPipe, IPPROTO_TCP, TCP_NODELAY, (char *) &opt, &optlen);
if (err)
{
DWORD dwErr = GetLastError ();
std::cout << "Pipe: " << dwErr << std::endl;
}
else
std::cout << "Pipe: OK" << std::endl;
CloseHandle (hReadPipe);
CloseHandle (hWritePipe);
// Socket
SOCKET skt = WSASocket (AF_INET, SOCK_STREAM, 0, NULL, 0, 0);
assert (skt != INVALID_SOCKET);
optlen = sizeof (opt);
err = getsockopt (skt, IPPROTO_TCP, TCP_NODELAY, (char *) &opt, &optlen);
if (err)
{
DWORD dwErr = GetLastError ();
std::cout << "Socket: " << dwErr << std::endl;
}
else
std::cout << "Socket: OK" << std::endl;
closesocket (skt);
return 0;
}
输出:
Pipe: 10038
Socket: OK
编辑: 如果您阅读下面的评论,您会看到有一些关于此代码是否可以导致相信文件或管道 HANDLE 实际上是 SOCKET 的讨论.好吧,它不能。我们可以知道这一点,因为像 ReadFile()
和 WriteFile()
这样的函数在文件/管道 HANDLE 和 SOCKET 上工作得同样好,如果有任何可能将一个误认为另一个,那将不会工作。
因此,此代码 (a) 安全,(b) 简单,并且 (c) 在所有情况下都有效(包括重定向输出,Remy 的代码会将其视为套接字)。因此我推荐它。请确保在执行任何其他操作之前调用 WSAStartup()
。
感谢@HansPassant 和@eryksun 对此做出的重要贡献post。
使用 GetNamedPipeInfo(s, NULL, NULL, NULL, NULL) 区分管道和套接字。
bool is_socket(LPVOID s)
{
if (GetFileType(s) != FILE_TYPE_PIPE) return false;
return !GetNamedPipeInfo(s, NULL, NULL, NULL, NULL);
}
我需要检查 windows 中进程创建事件的某些行为,我需要实施一个规则来检查传递给 createprocess api 调用的 startupinfo 结构并提取 std input/std 输出处理创建过程的值。 然后我必须检查这个句柄是否属于 tcp 套接字。 是否有任何 api 函数可以帮助我获取有关我拥有的句柄号(无论是文件句柄还是套接字句柄)的任何信息?
使用GetFileType()
函数
Retrieves the file type of the specified file.
Syntax
DWORD WINAPI GetFileType( _In_ HANDLE hFile );
Parameters
hFile [in]
A handle to the file.
Return value
The function returns one of the following values.
FILE_TYPE_CHAR
The specified file is a character file, typically an LPT device or a console.
FILE_TYPE_DISK
The specified file is a disk file.
FILE_TYPE_PIPE
The specified file is a socket, a named pipe, or an anonymous pipe.
FILE_TYPE_REMOTE
Unused.
FILE_TYPE_UNKNOWN
Either the type of the specified file is unknown, or the function failed
基于@RemyLebeau 的回答,我想看看我是否能找到一种可靠的方法来区分套接字和管道(GetFileType()
做不到),我想出了以下方法,似乎有效并且没有明显的缺点。
它的要点是如果传递任何不是 SOCKET 的东西,getsockopt()
将 return WSAENOTSOCK
(= 10038)。因此,这个测试本身就足够了,只要 传递给它的任何句柄是 SOCKET 或文件或管道 HANDLE。不要只传递任何旧的 HANDLE(有各种各样的),否则它可能会根据下面@HansPassant 的第一条评论感到困惑。
示例代码:
#include <winsock2.h> // * before* windows.h (!)
#include <windows.h>
#include <assert.h>
#include <iostream>
int main ()
{
WSADATA wsa_data;
int err = WSAStartup (2, &wsa_data);
assert (err == 0);
// Pipe
HANDLE hReadPipe, hWritePipe;
BOOL ok = CreatePipe (&hReadPipe, &hWritePipe, NULL, 2048);
assert (ok);
int opt;
int optlen = sizeof (opt);
err = getsockopt ((SOCKET) hReadPipe, IPPROTO_TCP, TCP_NODELAY, (char *) &opt, &optlen);
if (err)
{
DWORD dwErr = GetLastError ();
std::cout << "Pipe: " << dwErr << std::endl;
}
else
std::cout << "Pipe: OK" << std::endl;
CloseHandle (hReadPipe);
CloseHandle (hWritePipe);
// Socket
SOCKET skt = WSASocket (AF_INET, SOCK_STREAM, 0, NULL, 0, 0);
assert (skt != INVALID_SOCKET);
optlen = sizeof (opt);
err = getsockopt (skt, IPPROTO_TCP, TCP_NODELAY, (char *) &opt, &optlen);
if (err)
{
DWORD dwErr = GetLastError ();
std::cout << "Socket: " << dwErr << std::endl;
}
else
std::cout << "Socket: OK" << std::endl;
closesocket (skt);
return 0;
}
输出:
Pipe: 10038
Socket: OK
编辑: 如果您阅读下面的评论,您会看到有一些关于此代码是否可以导致相信文件或管道 HANDLE 实际上是 SOCKET 的讨论.好吧,它不能。我们可以知道这一点,因为像 ReadFile()
和 WriteFile()
这样的函数在文件/管道 HANDLE 和 SOCKET 上工作得同样好,如果有任何可能将一个误认为另一个,那将不会工作。
因此,此代码 (a) 安全,(b) 简单,并且 (c) 在所有情况下都有效(包括重定向输出,Remy 的代码会将其视为套接字)。因此我推荐它。请确保在执行任何其他操作之前调用 WSAStartup()
。
感谢@HansPassant 和@eryksun 对此做出的重要贡献post。
使用 GetNamedPipeInfo(s, NULL, NULL, NULL, NULL) 区分管道和套接字。
bool is_socket(LPVOID s)
{
if (GetFileType(s) != FILE_TYPE_PIPE) return false;
return !GetNamedPipeInfo(s, NULL, NULL, NULL, NULL);
}