一下就是C#使用Socks5代理髮送UDP數據包的詳細源碼: using System; using System.Net; using System.Net.Sockets; using System.Text; //Socks 5 RFC is available at http://www.faqs.org/rfcs/rfc1928.html. namespace CSProxy { /// <summary> /// Provides sock5 functionality to clients (Connect only). 協議規定客戶端採用TCP方式聯繫代理服務器 /// </summary> public class SocksProxy { private SocksProxy(){} #region ErrorMessages private static string[] errorMsgs= { "Operation completed successfully.",//操作成功完成 "General SOCKS server failure.",//常規服務器失敗 "Connection not allowed by ruleset.",//連接不被允許 "Network unreachable.",//網絡不能到達 "Host unreachable.",//主機不能到達 "Connection refused.",//連接被拒絕 "TTL expired.",//TTL期滿 "Command not supported.",//不支持的命令 "Address type not supported.",//不被支持的地址類型 "Unknown error."//未名的錯誤 }; #endregion /// <summary> /// 連接到socks5代理 /// </summary> /// <param name="proxyAdress">代理服務期地址</param> /// <param name="proxyPort">代理服務器端口</param> /// <param name="destAddress">目標地址 Destination: 目的地,UDP命令時是本機的地址</param> /// <param name="destPort">目標端口,UDP命令時是本機的UDP端口</param> /// <param name="userName">用戶名</param> /// <param name="password">密碼</param> /// <returns>用於TCP連接的SOCKET</returns> public static Socket ConnectToSocks5Proxy(string proxyAdress, ushort proxyPort, string destAddress, ushort destPort,string userName, string password) { IPAddress destIP = null; IPAddress proxyIP = null; byte[] request = new byte[257]; //請求 byte[] response = new byte[257];//應答 ushort nIndex; try { proxyIP = IPAddress.Parse(proxyAdress); } catch(FormatException) { proxyIP = Dns.GetHostByAddress(proxyAdress).AddressList[0]; } // 解析 destAddress (要求是類似 "212.116.65.112" 的string),否則是類似 "www.microsoft.com"的地址 try { destIP = IPAddress.Parse(destAddress); } catch(FormatException) { } IPEndPoint proxyEndPoint = new IPEndPoint(proxyIP,proxyPort); Socket s = new Socket(AddressFamily.InterNetwork,SocketType.Stream,ProtocolType.Tcp); s.Connect(proxyEndPoint);//客戶端連到服務器後,然後就發送請求來協商版本和認證方法: nIndex = 0; request[nIndex++]=0x05; // V 5. [版本] request[nIndex++]=0x02; // 2種驗證方式 [方法的數目] request[nIndex++]=0x00; // X'00' 不需要認證 [方法1] request[nIndex++]=0x02; // X'02' 用戶名/密碼[方法2] s.Send(request,nIndex,SocketFlags.None); // 收到2個字節的應答,填充到response中,如果不是兩個字節,則拋出異常 int nGot = s.Receive(response,2,SocketFlags.None); if (nGot!=2) throw new ConnectionException("從 proxy server 返回錯誤的應答."); // 當前定義的方法有: // X'00' 不需要認證 // X'01' GSSAPI // X'02' 用戶名/密碼 // X'03' -- X'7F' 由IANA分配 // X'80' -- X'FE' 爲私人方法所保留的 // X'FF' 沒有可以接受的方法 switch(response[1]) { case 0xFF: 沒有可以接受的方法(s); break; case 0x02: 用戶名密碼驗證(s,userName,password); break; } UDP命令(s,proxyIP); // 連接成功 return s; } public static void 沒有可以接受的方法(Socket s) { // 客戶端沒有一中驗證方式能被服務器接受,則關閉該socket. s.Close(); throw new ConnectionException("客戶端沒有一中驗證方式能被代理服務器接受."); } public static bool 用戶名密碼驗證(Socket s,string userName, string password) { byte[] request = new byte[257]; //請求 byte[] response = new byte[257];//應答 ushort nIndex; byte[] rawBytes; nIndex = 0; request[nIndex++]=0x05; // Version 5. 不清楚爲什麼報文的首字節是0x01(按照慣例應當是0x05) // 加入 user name request[nIndex++]=(byte)userName.Length; //一個字節,放UserName的長度 rawBytes = Encoding.Default.GetBytes(userName); rawBytes.CopyTo(request,nIndex); //將userName 加入 nIndex+=(ushort)rawBytes.Length; // 加入 password request[nIndex++]=(byte)password.Length; //一個字節,放PassWord的長度 rawBytes = Encoding.Default.GetBytes(password); rawBytes.CopyTo(request,nIndex); nIndex+=(ushort)rawBytes.Length; // 發送 Username/Password 請求 s.Send(request,nIndex,SocketFlags.None); // 收到2個字節的應答,填充到response中 int nGot = s.Receive(response,2,SocketFlags.None); if (nGot!=2) { throw new ConnectionException("從 proxy server 返回錯誤的應答."); } if (response[1] != 0x00) //返回如下的報文字節序列映像爲:0x01 | 驗證結果標誌-->0x00 驗證通過,其餘均表示有故障 { throw new ConnectionException("錯誤的 Usernaem/Password."); } return true; } public static Socket socket; public static string BndAddr; public static ushort BndPort; public static bool UDP命令(Socket s,IPAddress proxyIP ) { socket = new Socket(AddressFamily.InterNetwork, SocketType.Dgram, ProtocolType.Udp); IPEndPoint localIP = new IPEndPoint(IPAddress.Any,0); socket.Bind(localIP); EndPoint localEP =socket.LocalEndPoint; IPAddress destIP =((IPEndPoint)localEP).Address; ushort destPort =(ushort)((IPEndPoint)localEP).Port; // 這個函數只實現了UDP 命令 // // +----+-----+-------+------+----------+----------+ // |VER | CMD | RSV | ATYP | DST.ADDR | DST.PORT | // +----+-----+-------+------+----------+----------+ // | 1 | 1 | X'00' | 1 | Variable | 2 | // +----+-----+-------+------+----------+----------+ // // CMD 命令 // o CONNECT X'01' // o BIND X'02' // o UDP ASSOCIATE X'03' // // ATYP 地址類型 // o IPv4 : X'01' // o 網址 : X'03' // o IPv6 : X'04' // // DST.ADDR 目標地址 // DST.PORT 目標端口 byte[] request = new byte[257]; //請求 byte[] response = new byte[257];//應答 ushort nIndex; byte[] rawBytes; nIndex = 0; request[nIndex++]=0x05; // version 5. request[nIndex++]=0x03; // command = UDP. request[nIndex++]=0x00; // Reserve = 必須是 0x00 if (destIP != null) { switch(destIP.AddressFamily)//目的地址 { case AddressFamily.InterNetwork://IPV4 request[nIndex++]=0x01;//第4個字節 rawBytes = destIP.GetAddressBytes();//肯定是4個字節的長度 rawBytes.CopyTo(request,nIndex);//第5678個字節 nIndex+=(ushort)rawBytes.Length; break; case AddressFamily.InterNetworkV6://IPV6 request[nIndex++]=0x04; rawBytes = destIP.GetAddressBytes(); rawBytes.CopyTo(request,nIndex); nIndex+=(ushort)rawBytes.Length; break; } } byte[] portBytes2 = BitConverter.GetBytes(IPAddress.HostToNetworkOrder((short)destPort)); //portBytes2.CopyTo(request,nIndex);//這句爲何不行? for (int i=0;i<portBytes2.Length;i++) { request[nIndex++]=portBytes2[i]; } // 發送連接請求 s.Send(request,nIndex,SocketFlags.None); int len=s.Receive(response); //獲得沒有固定長度的應答 // 應答格式 // // +----+-----+-------+------+----------+----------+ // |VER | REP | RSV | ATYP | BND.ADDR | BND.PORT | // +----+-----+-------+------+----------+----------+ // | 1 | 1 | X'00' | 1 | Variable | 2 | // +----+-----+-------+------+----------+----------+ // // o VER 版本: X'05' // o REP // o X'00' 成功 // o X'01' 普通的SOCKS服務器請求失敗 // o X'02' 現有的規則不允許的連接 // o X'03' 網絡不可達 // o X'04' 主機不可達 // o X'05' 連接被拒 // o X'06' TTL超時 // o X'07' 不支持的命令 // o X'08' 不支持的地址類型 // o X'09'到X'FF' 未定義 // o RSV 保留 // o ATYP 地址類型 // o IPv4 : X'01' // o 網址 : X'03' // o IPv6 : X'04' // o BND.ADDR 代理服務器綁定的地址 // o BND.PORT 以網絡字節順序表示的代理服務器綁定的端口 // if (response[1]!=0x00) { throw new ConnectionException(errorMsgs[response[1]]); } else { BndAddr= response[4].ToString()+"."+response[5].ToString()+"."+response[6].ToString()+"."+response[7].ToString(); ushort be=BitConverter.ToUInt16(response,8); BndPort=(ushort)IPAddress.NetworkToHostOrder((short)be); } return true; } // 在傳輸UDP數據時,由於通過代理,所以需要按照一定的格式進行包裝,在需要傳送的數據之前添加一個報頭,具體爲: // 保留2字節的0 | 是否數據報分段重組標誌 | 地址類型 | 將要發到代理外的目標地址 | 遠端目標主機的端口 | 需要通過代理傳送出去的數據 // 這裏的地址是最終接收此UDP數據的代理外的服務器地址 /// <summary> /// 發送登陸信息 /// </summary> public static byte[] sLogin(string UserID,string PassWord,int State) { jy.P2PBLL.Login login=new jy.P2PBLL.Login(); login.Flage =jy.P2PBLL.Messages.Login; login.UserID=UserID; login.PassWord=jy.P2PBLL.Crypt.Encrypt(PassWord); login.State=State; byte[] buffer = jy.P2PBLL.Trans.ToBytes("jy.P2PBLL.Login",login); return buffer; } public static int 發送UDP數據(byte[] buffer) { IPAddress destIP=IPAddress.Parse("192.168.1.168"); ushort destPort=6680; byte[] head= GetUdpDataHead(destIP,destPort); buffer.CopyTo(head,10);//對於IPv4 是從10開始,IPv6 不是 int len=10+buffer.Length; IPEndPoint IPEP=new IPEndPoint(IPAddress.Parse(BndAddr),BndPort); socket.SendTo(head,len,SocketFlags.None,(EndPoint)IPEP); return len; } public static byte[] GetUdpDataHead(IPAddress destIP,ushort destPort) { byte[] request = new byte[1034]; ushort nIndex; byte[] rawBytes; nIndex = 0; request[nIndex++]=0x00; // 保留2字節的0 request[nIndex++]=0x00; // 保留2字節的0 request[nIndex++]=0x00; // 是否數據報分段重組標誌 request[nIndex++]=0x01; // 地址類型 // 將要發到代理外的目標地址 // 遠端目標主機的端口 if (destIP != null) { switch(destIP.AddressFamily)//目的地址 { case AddressFamily.InterNetwork://IPV4 rawBytes = destIP.GetAddressBytes();//肯定是4個字節的長度 rawBytes.CopyTo(request,nIndex);//第5678個字節 nIndex+=(ushort)rawBytes.Length; break; case AddressFamily.InterNetworkV6://IPV6 rawBytes = destIP.GetAddressBytes(); rawBytes.CopyTo(request,nIndex); nIndex+=(ushort)rawBytes.Length; break; } } byte[] portBytes2 = BitConverter.GetBytes(IPAddress.HostToNetworkOrder((short)destPort)); for (int i=0;i<portBytes2.Length;i++) { request[nIndex++]=portBytes2[i]; } return request; } } } #region connect 命令和 Bind 命令 // UdpCmd udpcmd=new UdpCmd(); // udpcmd.VER=0x05; // udpcmd.CMD=0x03; // udpcmd.RSV=0x00; // udpcmd.ATYP=0x01; // udpcmd.DstAddr=destIP.GetAddressBytes(); // udpcmd.DstPort=destPort; //關於字節順序 // 如果一個包,你自己打包發送,然後接收到後,自己進行處理, // 如果你能確定發送端的操作系統和接收端的操作系統,使用同樣的主機字節順序,那麼就不需要管字節的順序 // // 例如一個數據包中只存放一個int型數據,打包時使用byte[] intBytes = BitConverter.GetBytes(1); // 此時得到的intBytes是按主機字節順序(比如說是低字節順序)排列的字節數組 [1 0 0 0] // 將該包發出 // 接收端收到該包,使用 int getInt=BitConverter.ToInt32(data,0);[1 0 0 0] // // 但如果此時,接收端的主機字節順序使用高字節順序,則int getInt=BitConverter.ToInt32(data,0);[1 0 0 0] 得到的值是16777216 // 使用 big-edian 字節順序,已規定網絡爲big-edian 字節順序 /* private static bool Connect命令(Socket s, string destAddress, ushort destPort,IPAddress destIP,IPAddress proxyIP ) { // 這個函數只實現了connect 命令 // // +----+-----+-------+------+----------+----------+ // |VER | CMD | RSV | ATYP | DST.ADDR | DST.PORT | // +----+-----+-------+------+----------+----------+ // | 1 | 1 | X'00' | 1 | Variable | 2 | // +----+-----+-------+------+----------+----------+ // // CMD 命令 // o CONNECT X'01' // o BIND X'02' // o UDP ASSOCIATE X'03' // // ATYP 地址類型 // o IPv4 : X'01' // o 網址 : X'03' // o IPv6 : X'04' // // DST.ADDR 目標地址 // DST.PORT 目標端口 byte[] request = new byte[257]; //請求 byte[] response = new byte[257];//應答 ushort nIndex; byte[] rawBytes; nIndex = 0; request[nIndex++]=0x05; // version 5. request[nIndex++]=0x01; // command = connect. request[nIndex++]=0x00; // Reserve = 必須是 0x00 if (destIP != null) { switch(destIP.AddressFamily)//目的地址 { case AddressFamily.InterNetwork://IPV4 request[nIndex++]=0x01; rawBytes = destIP.GetAddressBytes(); rawBytes.CopyTo(request,nIndex); nIndex+=(ushort)rawBytes.Length; break; case AddressFamily.InterNetworkV6://IPV6 request[nIndex++]=0x04; rawBytes = destIP.GetAddressBytes(); rawBytes.CopyTo(request,nIndex); nIndex+=(ushort)rawBytes.Length; break; } } else { // 目標地址是域名 request[nIndex++]=0x03; // 地址是域名 request[nIndex++]=Convert.ToByte(destAddress.Length); // 該地址的長度 rawBytes = Encoding.Default.GetBytes(destAddress); rawBytes.CopyTo(request,nIndex); nIndex+=(ushort)rawBytes.Length; } // 使用 big-edian 字節順序 byte[] portBytes = BitConverter.GetBytes(destPort); for (int i=portBytes.Length-1;i>=0;i--) { request[nIndex++]=portBytes[i]; } // 發送連接請求 s.Send(request,nIndex,SocketFlags.None); int len=s.Receive(response); //獲得沒有固定長度的應答 // 應答格式 // // +----+-----+-------+------+----------+----------+ // |VER | REP | RSV | ATYP | BND.ADDR | BND.PORT | // +----+-----+-------+------+----------+----------+ // | 1 | 1 | X'00' | 1 | Variable | 2 | // +----+-----+-------+------+----------+----------+ // // o VER 版本: X'05' // o REP // o X'00' 成功 // o X'01' 普通的SOCKS服務器請求失敗 // o X'02' 現有的規則不允許的連接 // o X'03' 網絡不可達 // o X'04' 主機不可達 // o X'05' 連接被拒 // o X'06' TTL超時 // o X'07' 不支持的命令 // o X'08' 不支持的地址類型 // o X'09'到X'FF' 未定義 // o RSV 保留 // o ATYP 地址類型 // o IPv4 : X'01' // o 網址 : X'03' // o IPv6 : X'04' // o BND.ADDR 服務器綁定的地址 // o BND.PORT 以網絡字節順序表示的服務器綁定的端口 // if (response[1]!=0x00) { throw new ConnectionException(errorMsgs[response[1]]); return false; } return true; } private static bool Bind命令(Socket s, string destAddress, ushort destPort,IPAddress destIP,IPAddress proxyIP ) { // 這個函數只實現了Bind命令 // // +----+-----+-------+------+----------+----------+ // |VER | CMD | RSV | ATYP | DST.ADDR | DST.PORT | // +----+-----+-------+------+----------+----------+ // | 1 | 1 | X'00' | 1 | Variable | 2 | // +----+-----+-------+------+----------+----------+ // // CMD 命令 // o CONNECT X'01' // o BIND X'02' // o UDP ASSOCIATE X'03' // // ATYP 地址類型 // o IPv4 : X'01' // o 網址 : X'03' // o IPv6 : X'04' // // DST.ADDR 目標地址 // DST.PORT 目標端口 byte[] request = new byte[257]; //請求 byte[] response = new byte[257];//應答 ushort nIndex; byte[] rawBytes; nIndex = 0; request[nIndex++]=0x05; // version 5. request[nIndex++]=0x02; // command = BIND. request[nIndex++]=0x00; // Reserve = 必須是 0x00 if (destIP != null) { switch(destIP.AddressFamily)//目的地址 { case AddressFamily.InterNetwork://IPV4 request[nIndex++]=0x01; rawBytes = destIP.GetAddressBytes(); rawBytes.CopyTo(request,nIndex); nIndex+=(ushort)rawBytes.Length; break; case AddressFamily.InterNetworkV6://IPV6 request[nIndex++]=0x04; rawBytes = destIP.GetAddressBytes(); rawBytes.CopyTo(request,nIndex); nIndex+=(ushort)rawBytes.Length; break; } } else { // 目標地址是域名 request[nIndex++]=0x03; // 地址是域名 request[nIndex++]=Convert.ToByte(destAddress.Length); // 該地址的長度 rawBytes = Encoding.Default.GetBytes(destAddress); rawBytes.CopyTo(request,nIndex); nIndex+=(ushort)rawBytes.Length; } // 使用 big-edian 字節順序 byte[] portBytes = BitConverter.GetBytes(destPort); for (int i=portBytes.Length-1;i>=0;i--) { request[nIndex++]=portBytes[i]; } // 發送連接請求 s.Send(request,nIndex,SocketFlags.None); int len=s.Receive(response); //獲得沒有固定長度的應答 // 應答格式 // // +----+-----+-------+------+----------+----------+ // |VER | REP | RSV | ATYP | BND.ADDR | BND.PORT | // +----+-----+-------+------+----------+----------+ // | 1 | 1 | X'00' | 1 | Variable | 2 | // +----+-----+-------+------+----------+----------+ // // o VER 版本: X'05' // o REP // o X'00' 成功 // o X'01' 普通的SOCKS服務器請求失敗 // o X'02' 現有的規則不允許的連接 // o X'03' 網絡不可達 // o X'04' 主機不可達 // o X'05' 連接被拒 // o X'06' TTL超時 // o X'07' 不支持的命令 // o X'08' 不支持的地址類型 // o X'09'到X'FF' 未定義 // o RSV 保留 // o ATYP 地址類型 // o IPv4 : X'01' // o 網址 : X'03' // o IPv6 : X'04' // o BND.ADDR 服務器綁定的地址 // o BND.PORT 以網絡字節順序表示的服務器綁定的端口 // if (response[1]!=0x00) { throw new ConnectionException(errorMsgs[response[1]]); return false; } return true; } */ #endregion
C# Socks5 發送UDP數據包 Posted on 2006-03-28 20:22 zilong 閱讀(391) 評論(1) 編輯 收藏 網摘 using System; using System.Net; using System.Net.Sockets; using System.Text; //Socks 5 RFC is available at http://www.faqs.org/rfcs/rfc1928.html. namespace CSProxy { /// <summary> /// Provides sock5 functionality to clients (Connect only). 協議規定客戶端採用TCP方式聯繫代理服務器 /// </summary> public class SocksProxy { private SocksProxy(){} #region ErrorMessages private static string[] errorMsgs= { "Operation completed successfully.",//操作成功完成 "General SOCKS server failure.",//常規服務器失敗 "Connection not allowed by ruleset.",//連接不被允許 "Network unreachable.",//網絡不能到達 "Host unreachable.",//主機不能到達 "Connection refused.",//連接被拒絕 "TTL expired.",//TTL期滿 "Command not supported.",//不支持的命令 "Address type not supported.",//不被支持的地址類型 "Unknown error."//未名的錯誤 }; #endregion /// <summary> /// 連接到socks5代理 /// </summary> /// <param name="proxyAdress">代理服務期地址</param> /// <param name="proxyPort">代理服務器端口</param> /// <param name="destAddress">目標地址 Destination: 目的地,UDP命令時是本機的地址</param> /// <param name="destPort">目標端口,UDP命令時是本機的UDP端口</param> /// <param name="userName">用戶名</param> /// <param name="password">密碼</param> /// <returns>用於TCP連接的SOCKET</returns> public static Socket ConnectToSocks5Proxy(string proxyAdress, ushort proxyPort, string destAddress, ushort destPort,string userName, string password) { IPAddress destIP = null; IPAddress proxyIP = null; byte[] request = new byte[257]; //請求 byte[] response = new byte[257];//應答 ushort nIndex; try { proxyIP = IPAddress.Parse(proxyAdress); } catch(FormatException) { proxyIP = Dns.GetHostByAddress(proxyAdress).AddressList[0]; } // 解析 destAddress (要求是類似 "212.116.65.112" 的string),否則是類似 "www.microsoft.com"的地址 try { destIP = IPAddress.Parse(destAddress); } catch(FormatException) { } IPEndPoint proxyEndPoint = new IPEndPoint(proxyIP,proxyPort); Socket s = new Socket(AddressFamily.InterNetwork,SocketType.Stream,ProtocolType.Tcp); s.Connect(proxyEndPoint);//客戶端連到服務器後,然後就發送請求來協商版本和認證方法: nIndex = 0; request[nIndex++]=0x05; // V 5. [版本] request[nIndex++]=0x02; // 2種驗證方式 [方法的數目] request[nIndex++]=0x00; // X'00' 不需要認證 [方法1] request[nIndex++]=0x02; // X'02' 用戶名/密碼[方法2] s.Send(request,nIndex,SocketFlags.None); // 收到2個字節的應答,填充到response中,如果不是兩個字節,則拋出異常 int nGot = s.Receive(response,2,SocketFlags.None); if (nGot!=2) throw new ConnectionException("從 proxy server 返回錯誤的應答."); // 當前定義的方法有: // X'00' 不需要認證 // X'01' GSSAPI // X'02' 用戶名/密碼 // X'03' -- X'7F' 由IANA分配 // X'80' -- X'FE' 爲私人方法所保留的 // X'FF' 沒有可以接受的方法 switch(response[1]) { case 0xFF: 沒有可以接受的方法(s); break; case 0x02: 用戶名密碼驗證(s,userName,password); break; } UDP命令(s,proxyIP); // 連接成功 return s; } public static void 沒有可以接受的方法(Socket s) { // 客戶端沒有一中驗證方式能被服務器接受,則關閉該socket. s.Close(); throw new ConnectionException("客戶端沒有一中驗證方式能被代理服務器接受."); } public static bool 用戶名密碼驗證(Socket s,string userName, string password) { byte[] request = new byte[257]; //請求 byte[] response = new byte[257];//應答 ushort nIndex; byte[] rawBytes; nIndex = 0; request[nIndex++]=0x05; // Version 5. 不清楚爲什麼報文的首字節是0x01(按照慣例應當是0x05) // 加入 user name request[nIndex++]=(byte)userName.Length; //一個字節,放UserName的長度 rawBytes = Encoding.Default.GetBytes(userName); rawBytes.CopyTo(request,nIndex); //將userName 加入 nIndex+=(ushort)rawBytes.Length; // 加入 password request[nIndex++]=(byte)password.Length; //一個字節,放PassWord的長度 rawBytes = Encoding.Default.GetBytes(password); rawBytes.CopyTo(request,nIndex); nIndex+=(ushort)rawBytes.Length; // 發送 Username/Password 請求 s.Send(request,nIndex,SocketFlags.None); // 收到2個字節的應答,填充到response中 int nGot = s.Receive(response,2,SocketFlags.None); if (nGot!=2) { throw new ConnectionException("從 proxy server 返回錯誤的應答."); } if (response[1] != 0x00) //返回如下的報文字節序列映像爲:0x01 | 驗證結果標誌-->0x00 驗證通過,其餘均表示有故障 { throw new ConnectionException("錯誤的 Usernaem/Password."); } return true; } public static Socket socket; public static string BndAddr; public static ushort BndPort; public static bool UDP命令(Socket s,IPAddress proxyIP ) { socket = new Socket(AddressFamily.InterNetwork, SocketType.Dgram, ProtocolType.Udp); IPEndPoint localIP = new IPEndPoint(IPAddress.Any,0); socket.Bind(localIP); EndPoint localEP =socket.LocalEndPoint; IPAddress destIP =((IPEndPoint)localEP).Address; ushort destPort =(ushort)((IPEndPoint)localEP).Port; // 這個函數只實現了UDP 命令 // // +----+-----+-------+------+----------+----------+ // |VER | CMD | RSV | ATYP | DST.ADDR | DST.PORT | // +----+-----+-------+------+----------+----------+ // | 1 | 1 | X'00' | 1 | Variable | 2 | // +----+-----+-------+------+----------+----------+ // // CMD 命令 // o CONNECT X'01' // o BIND X'02' // o UDP ASSOCIATE X'03' // // ATYP 地址類型 // o IPv4 : X'01' // o 網址 : X'03' // o IPv6 : X'04' // // DST.ADDR 目標地址 // DST.PORT 目標端口 byte[] request = new byte[257]; //請求 byte[] response = new byte[257];//應答 ushort nIndex; byte[] rawBytes; nIndex = 0; request[nIndex++]=0x05; // version 5. request[nIndex++]=0x03; // command = UDP. request[nIndex++]=0x00; // Reserve = 必須是 0x00 if (destIP != null) { switch(destIP.AddressFamily)//目的地址 { case AddressFamily.InterNetwork://IPV4 request[nIndex++]=0x01;//第4個字節 rawBytes = destIP.GetAddressBytes();//肯定是4個字節的長度 rawBytes.CopyTo(request,nIndex);//第5678個字節 nIndex+=(ushort)rawBytes.Length; break; case AddressFamily.InterNetworkV6://IPV6 request[nIndex++]=0x04; rawBytes = destIP.GetAddressBytes(); rawBytes.CopyTo(request,nIndex); nIndex+=(ushort)rawBytes.Length; break; } } byte[] portBytes2 = BitConverter.GetBytes(IPAddress.HostToNetworkOrder((short)destPort)); //portBytes2.CopyTo(request,nIndex);//這句爲何不行? for (int i=0;i<portBytes2.Length;i++) { request[nIndex++]=portBytes2[i]; } // 發送連接請求 s.Send(request,nIndex,SocketFlags.None); int len=s.Receive(response); //獲得沒有固定長度的應答 // 應答格式 // // +----+-----+-------+------+----------+----------+ // |VER | REP | RSV | ATYP | BND.ADDR | BND.PORT | // +----+-----+-------+------+----------+----------+ // | 1 | 1 | X'00' | 1 | Variable | 2 | // +----+-----+-------+------+----------+----------+ // // o VER 版本: X'05' // o REP // o X'00' 成功 // o X'01' 普通的SOCKS服務器請求失敗 // o X'02' 現有的規則不允許的連接 // o X'03' 網絡不可達 // o X'04' 主機不可達 // o X'05' 連接被拒 // o X'06' TTL超時 // o X'07' 不支持的命令 // o X'08' 不支持的地址類型 // o X'09'到X'FF' 未定義 // o RSV 保留 // o ATYP 地址類型 // o IPv4 : X'01' // o 網址 : X'03' // o IPv6 : X'04' // o BND.ADDR 代理服務器綁定的地址 // o BND.PORT 以網絡字節順序表示的代理服務器綁定的端口 // if (response[1]!=0x00) { throw new ConnectionException(errorMsgs[response[1]]); } else { BndAddr= response[4].ToString()+"."+response[5].ToString()+"."+response[6].ToString()+"."+response[7].ToString(); ushort be=BitConverter.ToUInt16(response,8); BndPort=(ushort)IPAddress.NetworkToHostOrder((short)be); } return true; } // 在傳輸UDP數據時,由於通過代理,所以需要按照一定的格式進行包裝,在需要傳送的數據之前添加一個報頭,具體爲: // 保留2字節的0 | 是否數據報分段重組標誌 | 地址類型 | 將要發到代理外的目標地址 | 遠端目標主機的端口 | 需要通過代理傳送出去的數據 // 這裏的地址是最終接收此UDP數據的代理外的服務器地址 /// <summary> /// 發送登陸信息 /// </summary> public static byte[] sLogin(string UserID,string PassWord,int State) { jy.P2PBLL.Login login=new jy.P2PBLL.Login(); login.Flage =jy.P2PBLL.Messages.Login; login.UserID=UserID; login.PassWord=jy.P2PBLL.Crypt.Encrypt(PassWord); login.State=State; byte[] buffer = jy.P2PBLL.Trans.ToBytes("jy.P2PBLL.Login",login); return buffer; } public static int 發送UDP數據(byte[] buffer) { IPAddress destIP=IPAddress.Parse("192.168.1.168"); ushort destPort=6680; byte[] head= GetUdpDataHead(destIP,destPort); buffer.CopyTo(head,10);//對於IPv4 是從10開始,IPv6 不是 int len=10+buffer.Length; IPEndPoint IPEP=new IPEndPoint(IPAddress.Parse(BndAddr),BndPort); socket.SendTo(head,len,SocketFlags.None,(EndPoint)IPEP); return len; } public static byte[] GetUdpDataHead(IPAddress destIP,ushort destPort) { byte[] request = new byte[1034]; ushort nIndex; byte[] rawBytes; nIndex = 0; request[nIndex++]=0x00; // 保留2字節的0 request[nIndex++]=0x00; // 保留2字節的0 request[nIndex++]=0x00; // 是否數據報分段重組標誌 request[nIndex++]=0x01; // 地址類型 // 將要發到代理外的目標地址 // 遠端目標主機的端口 if (destIP != null) { switch(destIP.AddressFamily)//目的地址 { case AddressFamily.InterNetwork://IPV4 rawBytes = destIP.GetAddressBytes();//肯定是4個字節的長度 rawBytes.CopyTo(request,nIndex);//第5678個字節 nIndex+=(ushort)rawBytes.Length; break; case AddressFamily.InterNetworkV6://IPV6 rawBytes = destIP.GetAddressBytes(); rawBytes.CopyTo(request,nIndex); nIndex+=(ushort)rawBytes.Length; break; } } byte[] portBytes2 = BitConverter.GetBytes(IPAddress.HostToNetworkOrder((short)destPort)); for (int i=0;i<portBytes2.Length;i++) { request[nIndex++]=portBytes2[i]; } return request; } } } #region connect 命令和 Bind 命令 // UdpCmd udpcmd=new UdpCmd(); // udpcmd.VER=0x05; // udpcmd.CMD=0x03; // udpcmd.RSV=0x00; // udpcmd.ATYP=0x01; // udpcmd.DstAddr=destIP.GetAddressBytes(); // udpcmd.DstPort=destPort; //關於字節順序 // 如果一個包,你自己打包發送,然後接收到後,自己進行處理, // 如果你能確定發送端的操作系統和接收端的操作系統,使用同樣的主機字節順序,那麼就不需要管字節的順序 // // 例如一個數據包中只存放一個int型數據,打包時使用byte[] intBytes = BitConverter.GetBytes(1); // 此時得到的intBytes是按主機字節順序(比如說是低字節順序)排列的字節數組 [1 0 0 0] // 將該包發出 // 接收端收到該包,使用 int getInt=BitConverter.ToInt32(data,0);[1 0 0 0] // // 但如果此時,接收端的主機字節順序使用高字節順序,則int getInt=BitConverter.ToInt32(data,0);[1 0 0 0] 得到的值是16777216 // 使用 big-edian 字節順序,已規定網絡爲big-edian 字節順序 /* private static bool Connect命令(Socket s, string destAddress, ushort destPort,IPAddress destIP,IPAddress proxyIP ) { // 這個函數只實現了connect 命令 // // +----+-----+-------+------+----------+----------+ // |VER | CMD | RSV | ATYP | DST.ADDR | DST.PORT | // +----+-----+-------+------+----------+----------+ // | 1 | 1 | X'00' | 1 | Variable | 2 | // +----+-----+-------+------+----------+----------+ // // CMD 命令 // o CONNECT X'01' // o BIND X'02' // o UDP ASSOCIATE X'03' // // ATYP 地址類型 // o IPv4 : X'01' // o 網址 : X'03' // o IPv6 : X'04' // // DST.ADDR 目標地址 // DST.PORT 目標端口 byte[] request = new byte[257]; //請求 byte[] response = new byte[257];//應答 ushort nIndex; byte[] rawBytes; nIndex = 0; request[nIndex++]=0x05; // version 5. request[nIndex++]=0x01; // command = connect. request[nIndex++]=0x00; // Reserve = 必須是 0x00 if (destIP != null) { switch(destIP.AddressFamily)//目的地址 { case AddressFamily.InterNetwork://IPV4 request[nIndex++]=0x01; rawBytes = destIP.GetAddressBytes(); rawBytes.CopyTo(request,nIndex); nIndex+=(ushort)rawBytes.Length; break; case AddressFamily.InterNetworkV6://IPV6 request[nIndex++]=0x04; rawBytes = destIP.GetAddressBytes(); rawBytes.CopyTo(request,nIndex); nIndex+=(ushort)rawBytes.Length; break; } } else { // 目標地址是域名 request[nIndex++]=0x03; // 地址是域名 request[nIndex++]=Convert.ToByte(destAddress.Length); // 該地址的長度 rawBytes = Encoding.Default.GetBytes(destAddress); rawBytes.CopyTo(request,nIndex); nIndex+=(ushort)rawBytes.Length; } // 使用 big-edian 字節順序 byte[] portBytes = BitConverter.GetBytes(destPort); for (int i=portBytes.Length-1;i>=0;i--) { request[nIndex++]=portBytes[i]; } // 發送連接請求 s.Send(request,nIndex,SocketFlags.None); int len=s.Receive(response); //獲得沒有固定長度的應答 // 應答格式 // // +----+-----+-------+------+----------+----------+ // |VER | REP | RSV | ATYP | BND.ADDR | BND.PORT | // +----+-----+-------+------+----------+----------+ // | 1 | 1 | X'00' | 1 | Variable | 2 | // +----+-----+-------+------+----------+----------+ // // o VER 版本: X'05' // o REP // o X'00' 成功 // o X'01' 普通的SOCKS服務器請求失敗 // o X'02' 現有的規則不允許的連接 // o X'03' 網絡不可達 // o X'04' 主機不可達 // o X'05' 連接被拒 // o X'06' TTL超時 // o X'07' 不支持的命令 // o X'08' 不支持的地址類型 // o X'09'到X'FF' 未定義 // o RSV 保留 // o ATYP 地址類型 // o IPv4 : X'01' // o 網址 : X'03' // o IPv6 : X'04' // o BND.ADDR 服務器綁定的地址 // o BND.PORT 以網絡字節順序表示的服務器綁定的端口 // if (response[1]!=0x00) { throw new ConnectionException(errorMsgs[response[1]]); return false; } return true; } private static bool Bind命令(Socket s, string destAddress, ushort destPort,IPAddress destIP,IPAddress proxyIP ) { // 這個函數只實現了Bind命令 // // +----+-----+-------+------+----------+----------+ // |VER | CMD | RSV | ATYP | DST.ADDR | DST.PORT | // +----+-----+-------+------+----------+----------+ // | 1 | 1 | X'00' | 1 | Variable | 2 | // +----+-----+-------+------+----------+----------+ // // CMD 命令 // o CONNECT X'01' // o BIND X'02' // o UDP ASSOCIATE X'03' // // ATYP 地址類型 // o IPv4 : X'01' // o 網址 : X'03' // o IPv6 : X'04' // // DST.ADDR 目標地址 // DST.PORT 目標端口 byte[] request = new byte[257]; //請求 byte[] response = new byte[257];//應答 ushort nIndex; byte[] rawBytes; nIndex = 0; request[nIndex++]=0x05; // version 5. request[nIndex++]=0x02; // command = BIND. request[nIndex++]=0x00; // Reserve = 必須是 0x00 if (destIP != null) { switch(destIP.AddressFamily)//目的地址 { case AddressFamily.InterNetwork://IPV4 request[nIndex++]=0x01; rawBytes = destIP.GetAddressBytes(); rawBytes.CopyTo(request,nIndex); nIndex+=(ushort)rawBytes.Length; break; case AddressFamily.InterNetworkV6://IPV6 request[nIndex++]=0x04; rawBytes = destIP.GetAddressBytes(); rawBytes.CopyTo(request,nIndex); nIndex+=(ushort)rawBytes.Length; break; } } else { // 目標地址是域名 request[nIndex++]=0x03; // 地址是域名 request[nIndex++]=Convert.ToByte(destAddress.Length); // 該地址的長度 rawBytes = Encoding.Default.GetBytes(destAddress); rawBytes.CopyTo(request,nIndex); nIndex+=(ushort)rawBytes.Length; } // 使用 big-edian 字節順序 byte[] portBytes = BitConverter.GetBytes(destPort); for (int i=portBytes.Length-1;i>=0;i--) { request[nIndex++]=portBytes[i]; } // 發送連接請求 s.Send(request,nIndex,SocketFlags.None); int len=s.Receive(response); //獲得沒有固定長度的應答 // 應答格式 // // +----+-----+-------+------+----------+----------+ // |VER | REP | RSV | ATYP | BND.ADDR | BND.PORT | // +----+-----+-------+------+----------+----------+ // | 1 | 1 | X'00' | 1 | Variable | 2 | // +----+-----+-------+------+----------+----------+ // // o VER 版本: X'05' // o REP // o X'00' 成功 // o X'01' 普通的SOCKS服務器請求失敗 // o X'02' 現有的規則不允許的連接 // o X'03' 網絡不可達 // o X'04' 主機不可達 // o X'05' 連接被拒 // o X'06' TTL超時 // o X'07' 不支持的命令 // o X'08' 不支持的地址類型 // o X'09'到X'FF' 未定義 // o RSV 保留 // o ATYP 地址類型 // o IPv4 : X'01' // o 網址 : X'03' // o IPv6 : X'04' // o BND.ADDR 服務器綁定的地址 // o BND.PORT 以網絡字節順序表示的服務器綁定的端口 // if (response[1]!=0x00) { throw new ConnectionException(errorMsgs[response[1]]); return false; } return true; } */ #endregion