重复监听端口的实现与防止.

SO_REUSEADDR 和 SO_EXCLUSIVEADDRUSE 参数
 
前者允许两个socket绑到同一个端口上,后者禁止.
 
MSDN对两者的解释:
 

Using SO_REUSEADDR

The SO_REUSEADDR socket option allows a socket to forcibly bind to a port in use by another socket. The second socket calls setsockopt with the optname parameter set to SO_REUSEADDR and the optval parameter set to a boolean value of TRUE before calling bind on the same port as the original socket. Once the second sockey has successfully bound, the behvaior for all sockets bound to that port is indeterminate. For example, if all of the sockets on the same port provide TCP service, any incoming TCP connection requests over the port cannot be guaranteed to be handled by the correct socket — the behavior is non-deterministic. A malicious program use SO_REUSEADDR to forcibly bind sockets already in use for standard network protocol services in order to deny access to those service. No special privileges are required to use this option.

The exception to this non-deterministic behavior is multicast sockets. If two sockets are bound to the same interface and port and are members of the same multicast group, data will be delivered to both sockets, rather than an arbitrarily chosen one.

 

大意是 setsockopt函数中,第一个socket使用了 SO_REUSEADDR后,第2个也同样设置了SO_REUSEADDR的socket就可以绑到第一个socket的端口上去偷听啦.

 

Using SO_EXCLUSIVEADDRUSE

Before the SO_EXCLUSIVEADDRUSE socket option was introduced, there was very little a network application developer could do to prevent a malicious program from binding to the port on which the network application had its own sockets bound. In order to address this security issue, Windows Sockets introduced the SO_EXCLUSIVEADDRUSE socket option, which became available with Windows NT 4.0 SP4 and later operating systems. Note that this option is only available to members of the Administrators security group in all relevant OS versions previous to Windows Server 2003 (the reasons for which are discussed later in the article).

The SO_EXCLUSIVEADDRUSE option is set by calling the setsockopt function with the optname parameter set to SO_EXCLUSIVEADDRUSE and the optval parameter set to a boolean value of TRUE before the socket is bound. After the option is set, the behavior of subsequent bind calls differs depending on the network address specified in each bind call.

大意是以前没这个参数的,所以黑客们很爽.现在有了这个参数,就麻烦多了.

哦,这个参数是禁止第2个socket绑到同一个端口上.

 

Enhanced Socket Security

Enhanced socket security was added with the relase of Windows Server 2003. In previous Microsoft server operating system releases, the default socket security easily allowed processes to hijack ports from unsuspecting applications. In Windows Server 2003, sockets are not in a sharable state by default. Therefore, if an application wants to allow other processes to reuse a port on which a socket is already bound, it must specifically enable it. If that is the case, the first socket to call bind on the port must have SO_REUSEADDR set on it. The only exception to this case occurs when the second bind call is performed by the same user account that made the original call to bind. This exception exists solely to provide backwards compatibility.

有了第2个参数后,还是有菜鸟网管不知道默认是第一个,不会改.所以win2003 server里头就把第2个参数当作默认地.

 

例程:

//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;
}     

Advertisements

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s

%d bloggers like this: