opsi soket SO_EXCLUSIVEADDRUSE

Opsi soket SO_EXCLUSIVEADDRUSE mencegah soket lain terikat secara paksa ke alamat dan port yang sama.

Sintaks

Opsi SO_EXCLUSIVEADDRUSE mencegah soket lain terikat secara paksa ke alamat dan port yang sama, praktik yang diaktifkan oleh opsi soket SO_REUSEADDR. Penggunaan kembali tersebut dapat dijalankan oleh aplikasi berbahaya untuk mengganggu aplikasi. Opsi SO_EXCLUSIVEADDRUSE sangat berguna untuk layanan sistem yang membutuhkan ketersediaan tinggi.

Contoh kode berikut mengilustrasikan pengaturan opsi ini.

#ifndef UNICODE
#define UNICODE
#endif

#ifndef WIN32_LEAN_AND_MEAN
#define WIN32_LEAN_AND_MEAN
#endif

#include <windows.h>
#include <winsock2.h>
#include <ws2tcpip.h>
#include <stdio.h>
#include <stdlib.h>             // Needed for _wtoi

#pragma comment(lib, "Ws2_32.lib")

int __cdecl wmain(int argc, wchar_t ** argv)
{
    WSADATA wsaData;
    int iResult = 0;
    int iError = 0;

    SOCKET s = INVALID_SOCKET;
    SOCKADDR_IN saLocal;
    int iOptval = 0;

    int iFamily = AF_UNSPEC;
    int iType = 0;
    int iProtocol = 0;

    int iPort = 0;

    // Validate the parameters
    if (argc != 5) {
        wprintf(L"usage: %ws <addressfamily> <type> <protocol> <port>\n", argv[0]);
        wprintf(L"    opens a socket for the specified family, type, & protocol\n");
        wprintf(L"    sets the SO_EXCLUSIVEADDRUSE socket option on the socket\n");
        wprintf(L"    then tries to bind the port specified on the command-line\n");
        wprintf(L"%ws example usage\n", argv[0]);
        wprintf(L"   %ws 0 2 17 5150\n", argv[0]);
        wprintf(L"   where AF_UNSPEC=0 SOCK_DGRAM=2 IPPROTO_UDP=17  PORT=5150\n",
                argv[0]);
        wprintf(L"   %ws 2 1 17 5150\n", argv[0]);
        wprintf(L"   where AF_INET=2 SOCK_STREAM=1 IPPROTO_TCP=6  PORT=5150\n", argv[0]);
        wprintf(L"   See the documentation for the socket function for other values\n");
        return 1;
    }

    iFamily = _wtoi(argv[1]);
    iType = _wtoi(argv[2]);
    iProtocol = _wtoi(argv[3]);

    iPort = _wtoi(argv[4]);

    if (iFamily != AF_INET && iFamily != AF_INET6) {
        wprintf(L"Address family must be either AF_INET (2) or AF_INET6 (23)\n");
        return 1;
    }

    if (iPort <= 0 || iPort >= 65535) {
        wprintf(L"Port specified must be greater than 0 and less than 65535\n");
        return 1;
    }
    // Initialize Winsock
    iResult = WSAStartup(MAKEWORD(2, 2), &wsaData);
    if (iResult != 0) {
        wprintf(L"WSAStartup failed with error: %d\n", iResult);
        return 1;
    }
    // Create the socket
    s = socket(iFamily, iType, iProtocol);
    if (s == INVALID_SOCKET) {
        wprintf(L"socket failed with error: %ld\n", WSAGetLastError());
        WSACleanup();
        return 1;
    }
    // Set the exclusive address option
    iOptval = 1;
    iResult = setsockopt(s, SOL_SOCKET, SO_EXCLUSIVEADDRUSE,
                         (char *) &iOptval, sizeof (iOptval));
    if (iResult == SOCKET_ERROR) {
        wprintf(L"setsockopt for SO_EXCLUSIVEADDRUSE failed with error: %ld\n",
                WSAGetLastError());
    }

    saLocal.sin_family = (ADDRESS_FAMILY) iFamily;
    saLocal.sin_port = htons( (u_short) iPort);
    saLocal.sin_addr.s_addr = htonl(INADDR_ANY);

    // Bind the socket
    iResult = bind(s, (SOCKADDR *) & saLocal, sizeof (saLocal));
    if (iResult == SOCKET_ERROR) {

        // Most errors related to setting SO_EXCLUSIVEADDRUSE
        //    will occur at bind.
        iError = WSAGetLastError();
        if (iError == WSAEACCES)
            wprintf(L"bind failed with WSAEACCES (access denied)\n");
        else
            wprintf(L"bind failed with error: %ld\n", iError);

    } else {
        wprintf(L"bind on socket with SO_EXCLUSIVEADDRUSE succeeded to port: %ld\n",
                iPort);
    }

    // cleanup
    closesocket(s);
    WSACleanup();

    return 0;
}

Agar berpengaruh, opsi SO_EXCLUSIVADDRUSE harus diatur sebelum fungsi ikatan dipanggil (ini juga berlaku untuk opsi SO_REUSEADDR). Tabel 1 mencantumkan efek pengaturan opsi SO_EXCLUSIVEADDRUSE. Wildcard menunjukkan pengikatan ke alamat wildcard, seperti 0.0.0.0 untuk IPv4 dan :: untuk IPv6. Spesifik menunjukkan pengikatan ke antarmuka tertentu, seperti mengikat alamat IP yang ditetapkan ke adaptor. Spesifik2 menunjukkan pengikatan ke alamat tertentu selain alamat yang terikat dalam kasus Tertentu.

Catatan

Kasus Spesifik2 hanya berlaku ketika ikatan pertama dilakukan dengan alamat tertentu; untuk kasus di mana soket pertama terikat ke wildcard, entri untuk Spesifik mencakup semua kasus alamat tertentu.

 

Misalnya, pertimbangkan komputer dengan dua antarmuka IP: 10.0.0.1 dan 10.99.99.99. Jika ikatan pertama adalah ke 10.0.0.1 dan port 5150 dengan opsi SO_EXCLUSIVEADDRUSE diatur, maka pengikatan kedua ke 10.99.99.99 dan port 5150 dengan set opsi apa pun atau tidak ada yang berhasil. Namun, jika soket pertama terikat ke alamat wildcard (0.0.0.0) dan port 5150 dengan set SO_EXCLUSIVEADDRUSE, setiap pengikatan berikutnya ke port yang sama—terlepas dari alamat IP—akan gagal dengan WSAEADDRINUSE (10048) atau WSAEACCESS (10013), tergantung opsi mana yang diatur pada soket ikatan kedua.

Perilaku Ikatan dengan Berbagai Opsi Diatur

Ikatan kedua

Ikatan pertama

SO_EXCLUSIVEADDRUSE

Tidak ada opsi atau SO_REUSEADDR

Kartubebas

Tertentu

Kartubebas

Tertentu

${ROWSPAN3}$SO_EXCLUSIVEADDRUSE${REMOVE}$

Kartubebas

Gagal (10048)

Gagal (10048)

Gagal (10048)

Gagal (10048)

Tertentu

Gagal (10048)

Gagal (10048)

Gagal (10048)

Gagal (10048)

Spesifik2

n/a

Berhasil

n/a

Berhasil

${ROWSPAN3}$Tidak ada opsi${REMOVE}$

Kartubebas

Gagal (10048)

Gagal (10048)

Gagal (10048)

Gagal (10048)

Tertentu

Gagal (10048)

Gagal (10048)

Gagal (10048)

Gagal (10048)

Spesifik2

n/a

Berhasil

n/a

Berhasil

${ROWSPAN3}$SO_REUSEADDR${REMOVE}$

Kartubebas

Gagal (10013)

Gagal (10013)

Sukses*

Berhasil

Tertentu

Gagal (10013)

Gagal (10013)

Berhasil

Sukses*

Spesifik2

n/a

Berhasil

n/a

Berhasil

* Perilaku tidak terdefinisi untuk soket mana yang akan menerima paket.

 

Dalam kasus di mana ikatan pertama tidak menetapkan opsi atau SO_REUSEADDR, dan ikatan kedua melakukan SO_REUSEADDR, soket kedua telah menyalip port dan perilaku mengenai soket mana yang akan menerima paket yang tidak ditentukan. SO_EXCLUSIVEADDRUSE diperkenalkan untuk mengatasi situasi ini.

Soket dengan set SO_EXCLUSIVEADDRUSE tidak selalu dapat digunakan kembali segera setelah penutupan soket. Misalnya, jika soket mendengarkan dengan set bendera eksklusif menerima koneksi setelah soket mendengarkan ditutup, soket lain tidak dapat mengikat port yang sama dengan soket mendengarkan pertama dengan bendera eksklusif sampai koneksi yang diterima tidak lagi aktif.

Situasi ini bisa sangat rumit; meskipun soket telah ditutup, transportasi yang mendasar mungkin tidak mengakhiri koneksinya. Bahkan setelah soket ditutup, sistem harus mengirim semua data yang di-buffer, mengirimkan pemutusan sambungan yang anggun ke serekan, dan menunggu pemutusan anggun dari serekan. Oleh karena itu ada kemungkinan bahwa transportasi yang mendasar mungkin tidak pernah melepaskan koneksi, seperti ketika serekan mengiklankan jendela ukuran nol, atau serangan lainnya. Dalam contoh sebelumnya, soket mendengarkan ditutup setelah koneksi klien diterima. Sekarang bahkan jika koneksi klien ditutup, port mungkin masih tidak digunakan kembali jika koneksi klien tetap dalam keadaan aktif karena data yang tidak diakui, dan sebagainya.

Untuk menghindari situasi ini, aplikasi harus memastikan matikan dengan baik : matikan panggilan dengan bendera SD_SEND, lalu tunggu dalam perulangan recv hingga nol byte dikembalikan. Melakukannya menghindari masalah yang terkait dengan penggunaan kembali port, menjamin semua data diterima oleh serekan, dan meyakinkan serekan bahwa semua datanya berhasil diterima.

Opsi SO_LINGER dapat diatur pada soket untuk mencegah port masuk ke salah satu status tunggu aktif; namun, melakukannya tidak disarankan karena dapat menyebabkan efek yang tidak diinginkan, karena dapat menyebabkan koneksi diatur ulang. Misalnya, jika data telah diterima tetapi belum diakui oleh serekan, dan komputer lokal menutup soket dengan set SO_LINGER, koneksi diatur ulang dan serekan membuang data yang tidak diakui. Juga, memilih waktu yang sesuai untuk berlama-lama sulit; nilai yang terlalu kecil mengakibatkan banyak koneksi yang dibatalkan, sementara batas waktu yang besar dapat membuat sistem rentan terhadap penolakan serangan layanan dengan membuat banyak koneksi, dan dengan demikian mengulur-ulur banyak utas aplikasi.

Catatan

Soket yang menggunakan opsi SO_EXCLUSIVEADDRUSE harus dimatikan dengan benar sebelum menutupnya. Kegagalan untuk melakukannya dapat menyebabkan penolakan serangan layanan jika layanan terkait perlu dimulai ulang.

 

Persyaratan

Persyaratan Nilai
Klien minimum yang didukung
Windows 2000 Professional [hanya aplikasi desktop]
Server minimum yang didukung
Windows 2000 Server [hanya aplikasi desktop]
Header
Winsock2.h