java实现一个socks5代理 <一>了解socks5协议

本文打算用java 实现一个socks5代理,写之前在网上搜索发现资料也比较少,决定做个总结

1.首先我们讲解什么是代理


用一幅形象的图解释什么事代理,我们的请求过来后 代理将请求转达到真正的网络,再将结果转发回来。我们的浏览器看起来就像和真正的网站之间进行通信一样,我们要做的就是对请求和响应的转发工作


2.要想接受浏览器的请求,必须先和应用程序之间进行握手连接,只有应用程序同意了,才能将数据包发给我们,我们才能将返回的结果发给应用程序,下面讲讲握手过程

socks5的文档,英语好的可以看看 ,下面我翻译一下握手过程

第一步,程序将发送给代理请求握手的信号

VER
NMETHODS 
METHODS  
1 1 1~255

ver 代表协议版本占用一个字节 这里肯定是 : 5

nmethods 代表下一个字段专用的字节数量 这里不确定

methods 代表客户端拥有的加密方式占用1~255个字节都有可能,比如说0 代表不加密 1代表加密 2代表 另一种加密等


代理收到后返回

VER METHOD
1 1

method代表代理选择了一种握手方式

eg. 

client -->  5  2   0   2  代表着 版本5 有两种握手方式 加密的和不加密的供代理选择

proxy--> 5   0           代表着版本5  选择了不加密的协议方式

或者是

client--> 5 1 0 只有一种不加密的握手方式

proxy--> 5 0 选择了不加密的握手方式


上面的过程就叫做第一步握手连接,client 和proxy之间互相选择连接方式,如果client的所有握手方式proxy都不满足,则直接断开连接就好了



经过上一步的握手 client将发送如下信息到proxy

VER
CMD 
RSV  
ATYP 
DST.ADDR
DST.PORT
1 1 1 1 variable 2


cmd 字段,占用一个字节 1代表 想要tcp连接 3 代表想要udp连接

rsv 保留字,默认0

atyp 1 ip4

3 域名

4ip6

dst.addr  如果上一个字段是1 则这个字段是4位ip4地址

如果是3 则这个字段第一个字节代表域名长度,紧跟其后的是域名

如果是6 则这个字段16位ip6地址

dst.port 两个字节代表目的地端口


proxy返回

ver rep rsv atyp bnd.addr bnd.port
1 1 1 1   2

rep 代表proxy告诉应用程序处理的情况,0代表处理成功,否则可以直接断开连接了

所以有可能是这样的情况

eg.

client-->  5 1  0 1  123 123 123 123 0 80  表示tcp连接 到123.123.123.123 的80 端口

proxy-->5  0  0 1   0 0 0 0 0 0  代表着已经连接上了,并且将atype置1代表后面的是ip和端口 ,由于大多客户端的实现都会忽略后面的ip和端口,我们直接全写0

这样握手就完成了。


然后我们已经拿到了程序想要访问的地址和端口,我们新建一个socket 自己连接到ip和端口,然后一边接受应用程序发的数据,转发给网站,一遍接受网站返回的数据,转发给应用程序,这样达到了让他们俩通信的目的


注意

这里解释一点,为什么java用两个byte表示端口,用四个byte表示ip地址,byte的范围是-128~127 而ip地址每一位的范围是0 ~255 之间从数量上一个byte刚好有256个数足够表示一位ip地址,但是当ip地址超过127时自动变成-128 ,也就是说当某位ip地址超过127 比如说是129,那么当他超过127时自动从-128开始计算,也就是说129==--127

我们拿到某位ip是负的时,我们只需要用256加上这位负的地址,就能求出真正的地址

两位地址表示端口,计算机上有65535个端口,一位byte只能表示256个数是远远不够的,这时当端口超过127 就从-128开始计算,当端口超过256 就向前进一位,从0开始计算,比如说端口时 1 60,并不是代表60端口,而是1*256+60=316 端口, 某端口时 1  -69 ,代表着1*256+(256-69)==443端口,如果你收到的端口时1  -69 则代表443端口(https用的端口),

公式就是 如果端口是正数   实际端口=256*前面一位+后面一位。如果端口是负的   实际端口=256*前面一位+(256+后面一位) ps(因为后面一位是负的,所以加上后面一位代表减去那个数的绝对值)。

这就是处理端口和ip的方法

附上根据一个bytebuffer 和长度 解析出ip/域名  和端口的方法

//解析地址
			public String getHost(ByteBuffer a,int len){
				if(len<8){
					return null;
				}
				StringBuffer sb=new StringBuffer();
				if(a.get(3)==3){
					//说明是网址地址
					int size=a.get(4); //网址长度
					for(int i=5;i<(5+size);i++){
						sb.append((char)a.get(i));
					}
					
				}else if(a.get(3)==1){
					//说明是ip地址
					for(int i=4;i<=7;i++){
						int A=a.get(i);
						if(A<0) A=256+A;
						sb.append(A);
						sb.append(".");
					}
					sb.deleteCharAt(sb.length()-1);
				}
				return sb.toString();
			}
			
			//解析端口
			public int getPort(ByteBuffer a,int len){
				if(len<4){
					return 0;
				}
				int port = a.get(len-1);
				int thod=a.get(len-2);
				if(port>0){
					return 256*thod+port;
				}else{
					return 256*thod +(256+port);
				}
			}
 

猜你喜欢

转载自blog.csdn.net/woaiqianzhige/article/details/78733314