一下就是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