关于独占端口
作者:Star
2000.6.12
欢迎转载,请注明出处.
--------------------------------------------------------------------------------
不知道各位有没有安装新的for 2K的PlatformSDK啊,如果安装后,不妨比较一下一些头文件和VS98原有的头文件的异同,会发现不少有趣的东东喔!有些内容其实MS早就在之前的平台上就提供了支持,只不过一直没有写入它的头文件里去,下面就是在WinSock2.h里发现的一个例子:
在新版的WinSock2.h里,多了这么一段定义:
#define SO_EXCLUSIVEADDRUSE ((int)(~SO_REUSEADDR)) /* disallow local address reuse */
从MS的注释也可以看出来了,这个东东是为了防止本地端口被重用的,我们知道,本来我们是可以向同一个端口绑定两个Socket的,只要使用上面被“反”掉的那个选项SO_REUSEADDR就可以了,想想看这会发生什么事?我们可以在一个程序打开POP3端口(服务器端缺省是110,客户端应该设置为随机选择)收信的同时,运行自己的程序,绑定自己的Socket到同样的端口上,这时侯你就可以在该程序收信的同时,窃听到信件的内容,包括账户密码!看起来非常简单,不是吗?可是现实中很多软件并没有采取任何有效的策略来对付这一手,比如国内著名的某电邮客户端软件F**Mail.
那么我们该怎么防止这种情形在自己的程序运行当中出现?直接的想法是实时监控本机连接,看看都有些什么端口被绑定了,如果发现窃听程序存在立刻断开。听起来不错,可是真等你断开连接时可能密码已经泄漏了,要确保在密码传输前切断连接,需要大量的实时处理工作,就算抛开这些困难不谈,在Windows平台下去访问一大堆的注册表内容来确定目前活动的端口就有够烦了。还好,现在有了上面的这个socket选项,一切就变得简单了,请看如下演示程序:
//Test.cpp : Test exclusive with sockets
//if it's ok then written by Star, else i don't know^_^
//
#include
#include
//if have no the new PlatformSDK, then u need to add the
//define statement to your sourcecode, else remove it
//
#define SO_EXCLUSIVEADDRUSE ((int)(~SO_REUSEADDR))
const u_short EXCLUSIVE_PORT = 110;//or others
void main(int argc, char* argv[])
{
SOCKET sock1, sock2;
int ret;
BOOL val;
WSADATA ws;
struct sockaddr_in in;
//need WinSock 2.0!
if (WSAStartup (2, &ws))
return;
if (LOBYTE (ws.wVersion) - 2)
return;
//make two sockets
sock1 = socket (AF_INET, SOCK_STREAM, IPPROTO_TCP);
sock2 = socket (AF_INET, SOCK_STREAM, IPPROTO_TCP);
val = TRUE;
ret = setsockopt (sock1, SOL_SOCKET, SO_EXCLUSIVEADDRUSE, //set to exclusive
(LPCSTR)&val, 4);
if (ret)
{
//failed....
//do something to clear the error....
return;
}
ZeroMemory (&in, sizeof(in));
in.sin_family = AF_INET;
in.sin_port = htons (EXCLUSIVE_PORT);
in.sin_addr.s_addr = INADDR_ANY;
printf ("Now to bind the first socket to port %d with SO_EXCLUSIVEADDRUSE\n", EXCLUSIVE_PORT);
if (bind (sock1, (const struct sockaddr*)&in, sizeof(in)))
{
//failed...
//do something to clear the error...
closesocket (sock1);
return;
}
printf ("The first socket has been bound to port %d\n", EXCLUSIVE_PORT);
ret = setsockopt (sock2, SOL_SOCKET, SO_REUSEADDR,//try to reuse
(LPCSTR)&val, 4);
if (ret)
{
//failed....
//do something to clear the error....
return;
}
ZeroMemory (&in, sizeof(in));
in.sin_family = AF_INET;
in.sin_port = htons (EXCLUSIVE_PORT);
in.sin_addr.s_addr = INADDR_ANY;
printf ("\nNow to bind the second socket to port %d\n", EXCLUSIVE_PORT);
if (bind (sock2, (const struct sockaddr*)&in, sizeof(in)))
{
if (WSAEADDRINUSE == GetLastError ())
printf ("bind failed, the SO_EXCLUSIVEADDRUSE take effect!\n");
else
{
//failed...
//do something to clear the error...
}
}
else
printf ("What??? I see nothing!!!\n");
closesocket (sock1);
closesocket (sock2);
return;
}
上面的代码在我的PNT4SP6下编译运行通过,注意除了几个显眼处,很多地方都缺少应有的错误检测,需要用的朋友自己加上吧。9X下的情况我就不很清楚了,毕竟没用已经一年了,快忘光了:-)知道的朋友还请告诉我:-)我会补在后面的:-)
--------------------------------------------------------------------------------
灌水之作,敬请笑纳^_^
关于Internet Programming我也是新手,这篇东东在lu0兄的多次追问下写出来的,所以内容比较垃圾一些...现在我还记得lu0兄问我写了什么东西时的语气:“当时lu0兄的火气距离化成言辞冲出他的喉咙只差0.01公分,但是一天零一夜之后它将永远的憋在里面,因为我决定灌一篇水到这个版面上来。虽然本人生平灌过无数篇水,但是这篇,我认为是最纯净的。”