• <ins id="pjuwb"></ins>
    <blockquote id="pjuwb"><pre id="pjuwb"></pre></blockquote>
    <noscript id="pjuwb"></noscript>
          <sup id="pjuwb"><pre id="pjuwb"></pre></sup>
            <dd id="pjuwb"></dd>
            <abbr id="pjuwb"></abbr>
            隨筆 - 298  文章 - 377  trackbacks - 0
            <2007年8月>
            2930311234
            567891011
            12131415161718
            19202122232425
            2627282930311
            2345678

            常用鏈接

            留言簿(34)

            隨筆分類

            隨筆檔案

            文章檔案

            相冊

            收藏夾

            搜索

            •  

            最新評論

            閱讀排行榜

            評論排行榜

            SOCKS5

            Socks5版本的協議說明參考 RFC1928,RFC1929
            下面簡單地羅列程序實現,不涉及詳細的協議規范。首先對于TCP連接,然后再討論UDP傳輸。至于通過socks5的多播通訊,有興趣可以參考D. Chouinard的文章。

            1、TCP:

            // 建立流套接字 
            SOCKET m_socTCP=socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);

            // 連接到代理服務器
            int nRet = connect(m_socTCP,(SOCKADDR*)&m_saiProxy,sizeof(m_saiProxy));

            // Step 1: 連接代理服務器成功后,馬上開始和代理協商,協商報文如下,詢問服務器,版本5,是需要驗證(0x02)還是不需要驗證(0x00)
              +------+-------------------+------------+
              |VER   | Number of METHODS | METHODS    |
              +------+-------------------+------------+
              | 0x05 | 0x02 (有兩個方法)  | 0x00 | 0x02|
              +------+-------------------+------------+
            const char reqNego[4]={(char)0x05,(char)0x02,(char)0x00,(char)0x02};
            nRet = send(m_socTCP,reqNego,4,0);

            // Setp 2: 代理服務器將返回兩個字節的協商結果,接收協商結果
            fd_set fdread;FD_ZERO(&fdread);
            FD_SET(m_socTCP,&fdread);
            // Last param set to NULL for blocking operation. (struct timeval*)
            if((nRet=select(0,&fdread,NULL,NULL,NULL))==SOCKET_ERROR){return NC_E_PROXY_SELECT_READ|WSAGetLastError();}
            char resNego[2]={'\0'};
            int nRcvd=0,nCount=0;
            while(1)
            {
            if(FD_ISSET(m_socTCP,&fdread))
            {
            //接收sock[0]發送來的數據
            do{
            nRet = recv(m_socTCP, (char*)resNego+nRcvd, 2-nRcvd,0);
            if(nRet==SOCKET_ERROR){return NC_E_PROXY_RECEIVE|WSAGetLastError();}
            nRcvd += nRet;
            }
            while((nRcvd!=2)&&(++nCount<1000));
            if(nRcvd==2) break;
            }
            if(nCount++>=2000){return NC_E_PROXY_RECEIVE|WSAGetLastError();}
            }
            if(resNego[0]!=0x05 || (resNego[1]!=0x00 && resNego[1]!=0x02)){return NC_E_PROXY_PROTOCOL_VERSION|WSAGetLastError();};

            // Step 3: 根據協商結果判斷是否需要驗證用戶,如果是0x02,則需要提供驗證,驗證部分參考RFC1929
            if(resNego[1]==0x02)
            {
            // 需要密碼驗證
            char reqAuth[513]={'\0'};
            BYTE byLenUser = (BYTE)strlen(m_szProxyUserName);
            BYTE byLenPswd = (BYTE)strlen(m_szProxyPassword);
            reqAuth[0]=0x01;
            reqAuth[1]=byLenUser;
            sprintf(&reqAuth[2],"%s",m_szProxyUserName);
            reqAuth[2+byLenUser]=byLenPswd;
            sprintf(&reqAuth[3+byLenUser],"%s",m_szProxyPassword);
            //Send authentication info
            int len = (int)byLenUser + (int)byLenPswd + 3;
            nRet=send(m_socTCP,(const char*)reqAuth,len,0);
            if (nRet==SOCKET_ERROR){return NC_E_PROXY_SEND|WSAGetLastError();}
            //Now : Response to the auth request
            char resAuth[2]={'\0'};
            int nRcvd=0,nCount=0;
            do{
            nRet = recv(m_socTCP,resAuth+nRcvd,2-nRcvd,0);
            if(nRet==SOCKET_ERROR){return NC_E_PROXY_RECEIVE|WSAGetLastError();}
            nRcvd += nRet;
            }
            while((nRcvd!=2)&&(++nCount<1000));
            if(nCount>=1000){return NC_E_PROXY_RECEIVE|WSAGetLastError();}
            if (resAuth[1]!=0) return NC_E_PROXY_AUTHORIZE;
            // 密碼驗證通過了
            }

            // Step 4: 協商完成,開始發送連接遠程服務器請求,請求報文格式如下:
              +----+-----+-------+------+----------+----------+
              |VER | CMD | RSV | ATYP | DST.ADDR | DST.PORT |
              +----+-----+-------+------+----------+----------+
              | 1 |  1 | 0x00  |  1 | Variable |   2   |
              +----+-----+-------+------+----------+----------+
            // CMD==0x01 表示連接, ATYP==0x01表示采用IPV4格式地址,DST.ADDR是遠程服務器地址,DST.PORT是遠程服務器端口
            // 如果需要接受外來連接,則需要在連接完成之后,發送CMD==0x02綁定請求,代理將為此請求綁定一個套接字接受外部連接
            char reqSubNego[10]={(char)0x05,(char)0x01,(char)0x00,(char)0x01,(char)0x00,(char)0x00,(char)0x00,(char)0x00,(char)0x00,(char)0x00};
            *(unsigned long*)&reqSubNego[4] =m_saiServerTCP.sin_addr.S_un.S_addr;
            *(unsigned short*)&reqSubNego[8]=m_saiServerTCP.sin_port;
            nRet=send(m_socTCP,(const char*)reqSubNego,10,0);
            if (nRet==SOCKET_ERROR){return NC_E_PROXY_SEND|WSAGetLastError();}

            // Step 5: 接收對請求的響應,響應包格式如下
              +----+-----+-------+------+----------+----------+
              |VER | REP | RSV | ATYP | BND.ADDR | BND.PORT |
              +----+-----+-------+------+----------+----------+
              | 1 | 1 | 0x00  | 1   | Variable |   2   |
              +----+-----+-------+------+----------+----------+
            // VER 必須是0x05, REP==0x00表示成功,ATYP==0x01表示地址是IPV4地址,BND.ADDR 是代理為連接遠程服務器綁定的地址,BND.PORT是這個套接字的端口
            char resSubNego1[5]={'\0'};
            if(FD_ISSET(m_socTCP,&fdread))
            {
            int nRcvd=0,nCount=0;
            do{
            nRet = recv(m_socTCP,resSubNego1+nRcvd,5-nRcvd,0);
            if(nRet==SOCKET_ERROR){return NC_E_PROXY_RECEIVE|WSAGetLastError();}
            nRcvd += nRet;
            }
            while((nRcvd!=5)&&(++nCount<1000));
            if(nCount>=1000){return NC_E_PROXY_RECEIVE|WSAGetLastError();}
            if(resSubNego1[0]!=0x05||resSubNego1[1]!=0x00){return NC_E_PROXY_PROTOCOL_VERSION_SUB|WSAGetLastError();};

            switch(resSubNego1[3])
            {
            case 0x01:
            {
            // IP V4
            char resSubNego2[6]={resSubNego1[4],0};
            int nRet=-1;
            if(FD_ISSET(m_socTCP,&fdread))
            {
            int nRcvd=0,nCount=0;
            do{
            int nRet = recv(m_socTCP,&resSubNego2[1]+nRcvd,5-nRcvd,0);
            if(nRet==SOCKET_ERROR){return NC_E_PROXY_RECEIVE|WSAGetLastError();}
            nRcvd += nRet;
            }
            while((nRcvd!=5)&&(++nCount<1000));
            if(nCount>=1000){return NC_E_PROXY_RECEIVE|WSAGetLastError();}
            }
            // 得到代理綁定地址
            unsigned long  ulBINDAddr = *(unsigned long*)&resSubNego2; // SOCKS BIND ADDR
            unsigned short usBINDPort = *(unsigned short*)&resSubNego2[4]; // SOCKS BIND PORT
            m_saiProxyBindTCP.sin_addr.S_un.S_addr=ulBINDAddr;
            m_saiProxyBindTCP.sin_port=usBINDPort;
            // 得到本機綁定地址
            int len = sizeof(m_saiHostTCP);
            getsockname(m_socTCP,(SOCKADDR*)&m_saiHostTCP,&len);
            }
            break;
            case 0x03:
            {
            // Domain name
            int nLen = resSubNego1[4]+2;
            char* presSubNego2 = new char[nLen];
            if(FD_ISSET(m_socTCP,&fdread))
            {
            int nRet=0,nRcvd=0,nCount=0;
            do{
            nRet = recv(m_socTCP,presSubNego2+nRcvd,nLen-nRcvd,0);
            if(nRet==SOCKET_ERROR){return NC_E_PROXY_RECEIVE|WSAGetLastError();}
            nRcvd += nRet;
            }
            while((nRcvd!=nLen)&&(++nCount<1000));
            if(nCount>=1000){return NC_E_PROXY_RECEIVE|WSAGetLastError();}
            }
            unsigned short usBINDPort = *(unsigned short*)(presSubNego2+nLen-2); // BIND PORT;
            // 此時得到的是遠程主機的Domain Name
            delete[] presSubNego2; presSubNego2=NULL;
            }
            break;
            case 0x04:
            {
            // IPV6
            AfxMessageBox("該版本不支持IPV6";
            }
            break;
            default:
            break;
            }

            // 至此,連接已經建立。在此套接字上可進行數據的收發。
            }

            2、UDP

            SOCKS V5提供了對UDP的支持,它通過在代理服務器和內網主機之間建立一個UDP中繼的TCP連接,來輔助進行UDP數據包的收發。此連接有一個有效期,在此有效生命周期內,必須往代理發送UDP數據報確認連接有效,來維持此連接的有效性,否則,代理服務器超時,將會自動釋放此連接的資源。
            下面簡單地羅列程序實現,不涉及詳細的協議規范。

            // 客戶端為UDP中繼建立一個相關的流套接字
            SOCKET m_socClientTCP_UdpAssociate = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);

            // 連接代理服務器
            int nRet=connect(m_socClientTCP_UdpAssociate,(SOCKADDR*)&saiProxy,sizeof(saiProxy));

            // 連接成功,開始和代理服務器協商,首先發送版本標志,方法選擇報文
            const char reqNego[4]={(char)0x05,(char)0x02,(char)0x00,(char)0x02};
            nRet = send(m_socClientTCP_UdpAssociate,reqNego,4,0);
            if( nRet==SOCKET_ERROR 
            {
            DWORD dwError = WSAGetLastError();
            if (dwError!=WSAEWOULDBLOCK) return NCM_E_WM_CREATE_PROXYREQUESTFAILED;
            }

            // 接收協商的響應
            fd_set fdread; FD_ZERO(&fdread);
            FD_SET(m_socClientTCP_UdpAssociate,&fdread);
            if((nRet=select(0,&fdread,NULL,NULL,NULL))==SOCKET_ERROR) return NCM_E_WM_CREATE_PROXYCONNECTFAILED;
            char resNego[2]={0};
            int nRcvd=0,nCount=0;
            if(FD_ISSET(m_socClientTCP_UdpAssociate,&fdread))
            {
            nRcvd = recv(m_socClientTCP_UdpAssociate, (char*)resNego+nRcvd, 2,0);
            if(nRcvd==SOCKET_ERROR) return NCM_E_WM_CREATE_PROXYCONNECTFAILED;
            }
            if(resNego[0]!=0x05 || (resNego[1]!=0x00 && resNego[1]!=0x02)) return NCM_E_WM_CREATE_PROXYCONNECTFAILED;

            // 看是否需要密碼驗證
            if(resNego[1]==0x02)
            {
            // 需要密碼驗證
            char reqAuth[513]; memset(reqAuth,0,513);
            BYTE byLenUser = (BYTE)strlen(m_szProxyUserName);
            BYTE byLenPswd = (BYTE)strlen(m_szProxyPassword);
            reqAuth[0]=0x01;
            reqAuth[1]=byLenUser;
            sprintf(&reqAuth[2],"%s",m_szProxyUserName);
            reqAuth[2+byLenUser]=byLenPswd;
            sprintf(&reqAuth[3+byLenUser],"%s",m_szProxyPassword);
            //Send authentication info
            int len = (int)byLenUser + (int)byLenPswd + 3;
            int ret=send(m_socClientTCP_UdpAssociate,(const char*)reqAuth,len,0);
            if (ret==SOCKET_ERROR) if (GetLastError()!=WSAEWOULDBLOCK) return NCM_E_WM_CREATE_PROXYREQUESTFAILED;

            //Now : Response to the auth request
            char resAuth[2]={'\0'};
            int nRcvd=0,nCount=0;
            do{
            ret = recv(m_socClientTCP_UdpAssociate,resAuth+nRcvd,2-nRcvd,0);
            if(ret==SOCKET_ERROR){return NC_E_PROXY_RECEIVE|WSAGetLastError();}
            nRcvd += nRet;
            }
            while((nRcvd!=2)&&(++nCount<1000));
            if(nCount>=1000){return NC_E_PROXY_RECEIVE}
            if (resAuth[1]!=0)     return NEM_E_WM_CREATE_PROXYAUTHFAILED;
            // 密碼驗證通過了
            }

            // 開始發送向目標服務器的連接請求,其中DST.ADDR是目標服務器的地址,DST.PORT是目標服務器的UDP端口
            char reqSubNego[10]={(char)0x05,(char)0x03,(char)0x00,(char)0x01,(char)0x00,(char)0x00,(char)0x00,(char)0x00,(char)0x00,(char)0x00};
            *(unsigned long*)&reqSubNego[4] =saiServerUDP.sin_addr.S_un.S_addr; // cmd: DEST.addr
            *(unsigned short*)&reqSubNego[8]=saiServerUDP.sin_port; // cmd: DEST.port in network octet order
            nRet=send(m_socClientTCP_UdpAssociate,(const char*)reqSubNego,10,0);
            if (nRet==SOCKET_ERROR) return NEM_E_WM_CREATE_PROXYREQFAILED;

            // 接收響應信息
            int nRecvCount = 0;
            int nRecvBufferLen = 10;
            char szRecvBuf[10];
            nRet = 0;
            if(FD_ISSET(m_socClientTCP_UdpAssociate,&fdread))
            {
            int nRcvd=0,nCount=0;
            do{
            nRet = recv(m_socClientTCP_UdpAssociate,(char*)szRecvBuf+nRcvd,10-nRcvd,0);
            if(nRet==SOCKET_ERROR){return NC_E_PROXY_RECEIVE|WSAGetLastError();}
            nRcvd += nRet;
            }
            while((nRcvd!=10)&&(++nCount<1000));
            if(nCount>=1000){return NC_E_PROXY_RECEIVE;}
            if (szRecvBuf[0]!=0x05||szRecvBuf[1]!=0x00){return NC_E_PROXY_PROTOCOL_VERSION_SUB;}
            }
            else
            {
            return NCM_E_WM_CREATE_PROXYREQUESTFAILED;
            }

            // 代理服務器綁定udp地址BND.ADR,一般代理服務器都是多宿主機器,因此這個綁定地址是局域網地址,代理服務器綁定udp端口BND.PORT
            // m_ulProxyUDPAddr 代理綁定地址
            // m_usUDPAssociatePort 代理綁定端口
            memmove(&m_ulProxyUDPAddr,&szRecvBuf[4],4);
            memmove(&m_usUDPAssociatePort,&szRecvBuf[8],2);

            m_bUDPAssociated = TRUE;
            // 至此,得到了代理綁定的地址和端口,客戶端就可以通過它來收發UDP數據報了


            // 客戶端收發實例
            // 首先創建數據報套接字,綁定套接字,得到本地數據報套接字地址,端口
            SOCKET m_socClientUDP=WSASocket(AF_INET,SOCK_DGRAM,0,NULL,0,WSA_FLAG_OVERLAPPED);
            SOCKADDR_IN saiLocal;
            memset(&saiLocal,0,sizeof(saiLocal));
            saiLocal.sin_family = AF_INET;
            saiLocal.sin_port = 0;
            saiLocal.sin_addr.S_un.S_addr = INADDR_ANY;
            // 綁定本地udp套接字地址,地址+端口由系統決定
            if (bind(m_socClientUDP, (SOCKADDR *)&saiLocal, sizeof(saiLocal)  == SOCKET_ERROR)
            getsockname(m_socClientUDP, (sockaddr*)&saiLocal, &len);
            // m_ulHostAddr 本機綁定地址
            // m_usHostPortUdp 本機綁定端口
            m_ulHostAddr = saiLocal.sin_addr.S_un.S_addr;
            m_usHostPortUdp = saiLocal.sin_port;

            // 按照格式,發送數據
            SOCKADDR_IN saiProxy;
            memset(&saiProxy,0,sizeof(saiProxy));
            saiProxy.sin_family = AF_INET;
            saiProxy.sin_addr.S_un.S_addr = m_ulProxyUDPAddr; // 代理綁定的udp地址
            saiProxy.sin_port = m_usUDPAssociatePort; // 代理綁定的udp端口

            // 每個UDP包必須攜帶如下所述的頭:
                  +----+------+------+----------+----------+----------+
                  |RSV | FRAG | ATYP | DST.ADDR | DST.PORT |   DATA   |
                  +----+------+------+----------+----------+----------+
                  | 2  |  1   |  1   | Variable |    2     | Variable |
                  +----+------+------+----------+----------+----------+
            // 其中 ATYP==0x01 表示采用 IPV4 地址,那么頭長度就是10,DST.ADDR并不是服務器地址,而是本機綁定地址,DST.PORT是本機綁定端口
            char buf[10+2]={'0x00','0x00','0x00','0x01','0xff'};
            memmove(&buf[4],&m_ulHostAddr,4);
            *(unsigned short*)&buf[8]=m_usHostPortUDP;
            int nRet = sendto(m_socClientUDP,buf,12,0,(SOCKADDR*)&saiProxy,sizeof(saiProxy));

            // 接收數據
            int nLen = sizeof(saiProxy);
            char buf=new char[MAX_UDPLEN];
            nRet = recvfrom(m_socClientUDP,buf,MAX_UDPLEN,0,(SOCKADDR *)&saiProxy,&nLen);
            if(nRet==SOCKET_ERROR) return SOCKET_ERROR;
            BYTE flag=0xff;
            if(memcmp(&buf[0],&flag,1)==0) return SOCKET_ERROR;
            BYTE byProxyHead[20];
            byProxyHead[0]=byProxyHead[1]=byProxyHead[2]=0;
            byProxyHead[3]=1;
            *(unsigned long*)&byProxyHead[4] = saiServerUDP.sin_addr.S_un.S_addr;
            *(unsigned short*)&byProxyHead[8] = saiServerUDP.sin_port;
            byProxyHead[10]=byProxyHead[11]=byProxyHead[12]=0;
            byProxyHead[13]=1;
            *(unsigned long*)&byProxyHead[14] = m_ulHostAddr;
            *(unsigned short*)&byProxyHead[18] = m_usHostPortUdp;

            int i=0;
            BOOL bIsForMe=FALSE;
            if(memcmp(&byProxyHead[0],&buf[i],4)==0)
            {
            unsigned long ulRetAddr = *(unsigned long*)&buf[i+4];
            if(ulRetAddr==m_ulHostAddr)
            {
            unsigned short usRetPort = ntohs((unsigned short)(*(unsigned short*)&buf[i+8]));
            if(usRetPort==m_usHostPortUdp)
            {
            bIsForMe=TRUE;
            }
            }
            i+=10;
            }
            // 客戶端收發結束


            // 服務器端發送實例
            // m_ulProxyUDPAddr 代理綁定地址
            // m_usUDPAssociatePort 代理綁定端口
            // m_ulHostAddr 本機綁定地址
            // m_usHostPortUdp 本機綁定端口
            // 客戶端必須把上面的m_ulProxyUDPAddr,m_usUDPAssociatePort,m_ulHostAddr,m_usHostPortUdp告知服務器,服務器通過這兩個套接字進行數據的收發

            // 服務器創建數據報套接字
            SOCKET m_socUDP=WSASocket(AF_INET,SOCK_DGRAM,0,NULL,0,WSA_FLAG_OVERLAPPED);

            // 代理的udp中繼地址
            SOCKADDR_IN saiClient;
            saiClient.sin_family = AF_INET;
            saiClient.sin_addr.S_un.S_addr = m_ulProxyUDPAddr;
            saiClient.sin_port = m_usUDPAssociatePort;
            // 如果遠程目標在代理服務器后面,透過代理,指定遠程目標的socks信息,參照RFC1928
            char buffer[10]={'\0'};
            buffer[0]=buffer[1]=buffer[2]=0;
            buffer[3]=1; // No Fragment, It's a dependent udp, no need for socks server to arrange udp fragments

            // 目標機器的地址端口,透過代理后發向這里
            *(int*)&buffer[4]=m_ulHostAddr;
            *(unsigned short*)&buffer[8]=m_usHostPortUdp;
            BYTE buf[DATA_BUFSIZE]={'\0'};
            memmove(&buf[10],&szSendbuf[0],dwLength);
            memmove(&buf[0],&buffer[0],10);
            int nSent = sendto(m_socUDP, (const char*)&buf[0], dwLength+10, 0, (SOCKADDR*)&saiClient,sizeof(saiClient));
            // 服務器端發送結束
            posted on 2007-08-17 13:14 聶文龍 閱讀(817) 評論(0)  編輯 收藏 引用 所屬分類: net work
            www.久久热| 成人综合久久精品色婷婷| 精品人妻久久久久久888| 国产精品美女久久久m| 国产69精品久久久久9999| 久久无码一区二区三区少妇 | yellow中文字幕久久网| 人人狠狠综合久久亚洲高清| 久久久久se色偷偷亚洲精品av| 99久久综合狠狠综合久久止| 亚洲天堂久久久| 久久久久久久精品妇女99| 久久综合给久久狠狠97色| 久久久久国产一区二区三区| 婷婷五月深深久久精品| 国产精品99久久不卡| 亚洲AV日韩AV天堂久久| 久久精品无码av| 国产精品久久国产精麻豆99网站| 亚洲美日韩Av中文字幕无码久久久妻妇| 亚洲午夜久久久久妓女影院| 久久夜色精品国产| 亚洲国产成人久久精品动漫| 久久亚洲精品国产精品| 久久久久久午夜精品| 国产午夜精品久久久久九九| 狠狠狠色丁香婷婷综合久久俺| 狠狠色狠狠色综合久久| 日本加勒比久久精品| 国产一区二区精品久久岳| 久久成人精品视频| 韩国三级大全久久网站| 91精品国产9l久久久久| 国内精品久久久久伊人av| 人妻无码αv中文字幕久久| 亚洲精品蜜桃久久久久久| 伊人久久大香线蕉亚洲| 亚洲AV乱码久久精品蜜桃| 亚洲欧美日韩中文久久| 熟妇人妻久久中文字幕| 久久久久无码精品国产|