• <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
            <2008年12月>
            30123456
            78910111213
            14151617181920
            21222324252627
            28293031123
            45678910

            常用鏈接

            留言簿(34)

            隨筆分類

            隨筆檔案

            文章檔案

            相冊

            收藏夾

            搜索

            •  

            最新評論

            閱讀排行榜

            評論排行榜

            SOCKS5

            Socks5版本的協(xié)議說明參考 RFC1928,RFC1929
            下面簡單地羅列程序?qū)崿F(xiàn),不涉及詳細的協(xié)議規(guī)范。首先對于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: 連接代理服務器成功后,馬上開始和代理協(xié)商,協(xié)商報文如下,詢問服務器,版本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: 代理服務器將返回兩個字節(jié)的協(xié)商結(jié)果,接收協(xié)商結(jié)果
            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]發(fā)送來的數(shù)據(jù)
            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: 根據(jù)協(xié)商結(jié)果判斷是否需要驗證用戶,如果是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: 協(xié)商完成,開始發(fā)送連接遠程服務器請求,請求報文格式如下:
              +----+-----+-------+------+----------+----------+
              |VER | CMD | RSV | ATYP | DST.ADDR | DST.PORT |
              +----+-----+-------+------+----------+----------+
              | 1 |  1 | 0x00  |  1 | Variable |   2   |
              +----+-----+-------+------+----------+----------+
            // CMD==0x01 表示連接, ATYP==0x01表示采用IPV4格式地址,DST.ADDR是遠程服務器地址,DST.PORT是遠程服務器端口
            // 如果需要接受外來連接,則需要在連接完成之后,發(fā)送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;
            }

            // 至此,連接已經(jīng)建立。在此套接字上可進行數(shù)據(jù)的收發(fā)。
            }

            2、UDP

            SOCKS V5提供了對UDP的支持,它通過在代理服務器和內(nèi)網(wǎng)主機之間建立一個UDP中繼的TCP連接,來輔助進行UDP數(shù)據(jù)包的收發(fā)。此連接有一個有效期,在此有效生命周期內(nèi),必須往代理發(fā)送UDP數(shù)據(jù)報確認連接有效,來維持此連接的有效性,否則,代理服務器超時,將會自動釋放此連接的資源。
            下面簡單地羅列程序?qū)崿F(xiàn),不涉及詳細的協(xié)議規(guī)范。

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

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

            // 連接成功,開始和代理服務器協(xié)商,首先發(fā)送版本標志,方法選擇報文
            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;
            }

            // 接收協(xié)商的響應
            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;
            // 密碼驗證通過了
            }

            // 開始發(fā)送向目標服務器的連接請求,其中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,一般代理服務器都是多宿主機器,因此這個綁定地址是局域網(wǎng)地址,代理服務器綁定udp端口BND.PORT
            // m_ulProxyUDPAddr 代理綁定地址
            // m_usUDPAssociatePort 代理綁定端口
            memmove(&m_ulProxyUDPAddr,&szRecvBuf[4],4);
            memmove(&m_usUDPAssociatePort,&szRecvBuf[8],2);

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


            // 客戶端收發(fā)實例
            // 首先創(chuàng)建數(shù)據(jù)報套接字,綁定套接字,得到本地數(shù)據(jù)報套接字地址,端口
            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套接字地址,地址+端口由系統(tǒng)決定
            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;

            // 按照格式,發(fā)送數(shù)據(jù)
            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));

            // 接收數(shù)據(jù)
            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;
            }
            // 客戶端收發(fā)結(jié)束


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

            // 服務器創(chuàng)建數(shù)據(jù)報套接字
            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

            // 目標機器的地址端口,透過代理后發(fā)向這里
            *(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));
            // 服務器端發(fā)送結(jié)束
            posted on 2007-08-17 13:14 聶文龍 閱讀(817) 評論(0)  編輯 收藏 引用 所屬分類: net work
            99久久精品这里只有精品| 麻豆精品久久久久久久99蜜桃| 色欲av伊人久久大香线蕉影院| 亚洲第一极品精品无码久久| 7777久久亚洲中文字幕| 久久精品国产亚洲av瑜伽| 伊人久久精品无码av一区| 亚洲精品国产成人99久久| 无码国内精品久久综合88| 久久综合丁香激情久久| 亚洲乱码日产精品a级毛片久久 | 9191精品国产免费久久| 亚洲精品NV久久久久久久久久 | 青青青国产成人久久111网站| 久久久久这里只有精品| 久久久精品2019免费观看 | 色综合久久无码五十路人妻| 久久99热这里只有精品国产| 久久久久99精品成人片欧美| 日日狠狠久久偷偷色综合0| 精品乱码久久久久久久| 精品国产乱码久久久久久呢| 精品久久久久中文字幕一区| 国产高潮国产高潮久久久| 亚洲欧美另类日本久久国产真实乱对白| 久久精品亚洲一区二区三区浴池| 免费精品国产日韩热久久| 激情综合色综合久久综合| 国产精品99精品久久免费| 久久99精品久久只有精品| 久久亚洲AV无码精品色午夜麻豆| 无码国内精品久久人妻麻豆按摩| 青青草国产精品久久久久| 伊人久久综在合线亚洲2019| 国内精品九九久久久精品| 久久亚洲精品国产精品| 丰满少妇高潮惨叫久久久| 狠狠色婷婷综合天天久久丁香| 久久成人精品视频| 久久亚洲国产精品五月天婷| 欧美午夜A∨大片久久|