文档章节

java 实现socks代理,包含sock4 sock5代理

林小宝
 林小宝
发布于 2018/06/07 16:27
字数 1496
阅读 69
收藏 0

说明

使用方法,直接编译后
USing port openSock4 openSock5 user pwd 
端口号
是否开通sock4
是否开通sock5
user 不为空时表示 sock5需要使用账号登录 。
示例  java SocksServerOneThread 1080

代码如下


import java.io.ByteArrayOutputStream;
import java.io.Closeable;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.InetAddress;
import java.net.ServerSocket;
import java.net.Socket;
import java.nio.ByteBuffer;
import java.util.Arrays;
import java.util.Date;
import java.util.concurrent.CountDownLatch;

/**
 * 标准的socks代理服务器,支持sock4与sock4代理
 * 
 * @author Administrator
 * 
 */
public class SocksServerOneThread implements Runnable {

	/**
	 * 来源的代理socket
	 */
	private final Socket socket;
	/**
	 * 是否开启socks4代理
	 */
	private final boolean openSock4;
	/**
	 * 是否开启socks5代理
	 */
	private final boolean openSock5;
	/**
	 * socks5代理的登录用户名,如果 不为空表示需要登录验证
	 */
	private final String user;
	/**
	 * socks5代理的登录密码,
	 */
	private final String pwd;
	/**
	 * socks是否需要进行登录验证
	 */
	private final boolean socksNeekLogin;

	/**
	 * @param socket
	 *            来源的代理socket
	 * @param openSock4
	 *            是否开启socks4代理
	 * @param openSock5
	 *            是否开启socks5代理
	 * @param user
	 *            socks5代理的登录用户名,如果 不为空表示需要登录验证
	 * @param pwd
	 *            socks5代理的登录密码,
	 */
	protected SocksServerOneThread(Socket socket, boolean openSock4, boolean openSock5, String user, String pwd) {
		this.socket = socket;
		this.openSock4 = openSock4;
		this.openSock5 = openSock5;
		this.user = user;
		this.pwd = pwd;
		this.socksNeekLogin = null != user;
	}

	public void run() {
		// 获取来源的地址用于日志打印使用
		String addr = socket.getRemoteSocketAddress().toString();
		log("process one socket : %s", addr);
		// 声明流
		InputStream a_in = null, b_in = null;
		OutputStream a_out = null, b_out = null;
		Socket proxy_socket = null;
		ByteArrayOutputStream cache = null;
		try {
			a_in = socket.getInputStream();
			a_out = socket.getOutputStream();

			// 获取协议头。取代理的类型,只有 4,5。
			byte[] tmp = new byte[1];
			int n = a_in.read(tmp);
			if (n == 1) {
				byte protocol = tmp[0];
				if ((openSock4 && 0x04 == protocol)) {// 如果开启代理4,并以socks4协议请求
					proxy_socket = sock4_check(a_in, a_out);
				} else if ((openSock5 && 0x05 == protocol)) {// 如果开启代理5,并以socks5协议请求
					proxy_socket = sock5_check(a_in, a_out);
				} else {// 非socks 4 ,5 协议的请求
					log("not socks proxy : %s  openSock4[] openSock5[]", tmp[0], openSock4, openSock5);
				}
				if (null != proxy_socket) {
					CountDownLatch latch = new CountDownLatch(1);
					b_in = proxy_socket.getInputStream();
					b_out = proxy_socket.getOutputStream();
					// 交换流数据
					if (80 == proxy_socket.getPort()) {
						cache = new ByteArrayOutputStream();
					}
					transfer(latch, a_in, b_out, cache);
					transfer(latch, b_in, a_out, cache);
					try {
						latch.await();
					} catch (Exception e) {
					}
				}
			} else {
				log("socks error : %s", Arrays.toString(tmp));
			}
		} catch (Exception e) {
			log("exception : %s %s", e.getClass(), e.getLocalizedMessage());
			e.printStackTrace();
		} finally {
			log("close socket, system cleanning ...  %s ", addr);
			closeIo(a_in);
			closeIo(b_in);
			closeIo(b_out);
			closeIo(a_out);
			closeIo(socket);
			closeIo(proxy_socket);
			if (null != cache) {
				cache2Local(cache);
			}
		}
	}

	private void cache2Local(ByteArrayOutputStream cache) {
		// OutputStream result = null;
		// try {
		// String fileName = System.currentTimeMillis() + "_"
		// + Thread.currentThread().getId();
		// result = new FileOutputStream("E:/cache/" + fileName + ".info");
		// result.write(cache.toByteArray());
		// } catch (Exception e) {
		// e.printStackTrace();
		// } finally {
		// closeIo(result);
		// }
	}

	/**
	 * sock5代理头处理
	 * 
	 * @param in
	 * @param out
	 * @return
	 * @throws IOException
	 */
	private Socket sock5_check(InputStream in, OutputStream out) throws IOException {
		byte[] tmp = new byte[2];
		in.read(tmp);
		boolean isLogin = false;
		byte method = tmp[1];
		if (0x02 == tmp[0]) {
			method = 0x00;
			in.read();
		}
		if (socksNeekLogin) {
			method = 0x02;
		}
		tmp = new byte[] { 0x05, method };
		out.write(tmp);
		out.flush();
		// Socket result = null;
		Object resultTmp = null;
		if (0x02 == method) {// 处理登录.
			int b = in.read();
			String user = null;
			String pwd = null;
			if (0x01 == b) {
				b = in.read();
				tmp = new byte[b];
				in.read(tmp);
				user = new String(tmp);
				b = in.read();
				tmp = new byte[b];
				in.read(tmp);
				pwd = new String(tmp);
				if (null != user && user.trim().equals(this.user) && null != pwd && pwd.trim().equals(this.pwd)) {// 权限过滤
					isLogin = true;
					tmp = new byte[] { 0x05, 0x00 };// 登录成功
					out.write(tmp);
					out.flush();
					log("%s login success !", user);
				} else {
					log("%s login faild !", user);
				}
			}
		}
		byte cmd = 0;
		if (!socksNeekLogin || isLogin) {// 验证是否需要登录
			tmp = new byte[4];
			in.read(tmp);
			log("proxy header >>  %s", Arrays.toString(tmp));
			cmd = tmp[1];
			String host = getHost(tmp[3], in);
			tmp = new byte[2];
			in.read(tmp);
			int port = ByteBuffer.wrap(tmp).asShortBuffer().get() & 0xFFFF;
			log("connect %s:%s", host, port);
			ByteBuffer rsv = ByteBuffer.allocate(10);
			rsv.put((byte) 0x05);
			try {
				if (0x01 == cmd) {
					resultTmp = new Socket(host, port);
					rsv.put((byte) 0x00);
				} else if (0x02 == cmd) {
					resultTmp = new ServerSocket(port);
					rsv.put((byte) 0x00);
				} else {
					rsv.put((byte) 0x05);
					resultTmp = null;
				}
			} catch (Exception e) {
				rsv.put((byte) 0x05);
				resultTmp = null;
			}
			rsv.put((byte) 0x00);
			rsv.put((byte) 0x01);
			rsv.put(socket.getLocalAddress().getAddress());
			Short localPort = (short) ((socket.getLocalPort()) & 0xFFFF);
			rsv.putShort(localPort);
			tmp = rsv.array();
		} else {
			tmp = new byte[] { 0x05, 0x01 };// 登录失败
			log("socks server need login,but no login info .");
		}
		out.write(tmp);
		out.flush();
		if (null != resultTmp && 0x02 == cmd) {
			ServerSocket ss = (ServerSocket) resultTmp;
			try {
				resultTmp = ss.accept();
			} catch (Exception e) {
			} finally {
				closeIo(ss);
			}
		}
		return (Socket) resultTmp;
	}

	/**
	 * sock4代理的头处理
	 * 
	 * @param in
	 * @param out
	 * @return
	 * @throws IOException
	 */
	private Socket sock4_check(InputStream in, OutputStream out) throws IOException {
		Socket proxy_socket = null;
		byte[] tmp = new byte[3];
		in.read(tmp);
		// 请求协议|VN1|CD1|DSTPORT2|DSTIP4|NULL1|
		int port = ByteBuffer.wrap(tmp, 1, 2).asShortBuffer().get() & 0xFFFF;
		String host = getHost((byte) 0x01, in);
		in.read();
		byte[] rsv = new byte[8];// 返回一个8位的响应协议
		// |VN1|CD1|DSTPORT2|DSTIP 4|
		try {
			proxy_socket = new Socket(host, port);
			log("connect [%s] %s:%s", tmp[1], host, port);
			rsv[1] = 90;// 代理成功
		} catch (Exception e) {
			log("connect exception  %s:%s", host, port);
			rsv[1] = 91;// 代理失败.
		}
		out.write(rsv);
		out.flush();
		return proxy_socket;
	}

	/**
	 * 获取目标的服务器地址
	 * 
	 * @createTime 2014年12月14日 下午8:32:15
	 * @param type
	 * @param in
	 * @return
	 * @throws IOException
	 */
	private String getHost(byte type, InputStream in) throws IOException {
		String host = null;
		byte[] tmp = null;
		switch (type) {
		case 0x01:// IPV4协议
			tmp = new byte[4];
			in.read(tmp);
			host = InetAddress.getByAddress(tmp).getHostAddress();
			break;
		case 0x03:// 使用域名
			int l = in.read();
			tmp = new byte[l];
			in.read(tmp);
			host = new String(tmp);
			break;
		case 0x04:// 使用IPV6
			tmp = new byte[16];
			in.read(tmp);
			host = InetAddress.getByAddress(tmp).getHostAddress();
			break;
		default:
			break;
		}
		return host;
	}

	/**
	 * IO操作中共同的关闭方法
	 * 
	 * @createTime 2014年12月14日 下午7:50:56
	 * @param socket
	 */
	protected static final void closeIo(Socket closeable) {
		if (null != closeable) {
			try {
				closeable.close();
			} catch (IOException e) {
			}
		}
	}

	/**
	 * IO操作中共同的关闭方法
	 * 
	 * @createTime 2014年12月14日 下午7:50:56
	 * @param socket
	 */
	protected static final void closeIo(Closeable closeable) {
		if (null != closeable) {
			try {
				closeable.close();
			} catch (IOException e) {
			}
		}
	}

	/**
	 * 数据交换.主要用于tcp协议的交换
	 * 
	 * @createTime 2014年12月13日 下午11:06:47
	 * @param lock
	 *            锁
	 * @param in
	 *            输入流
	 * @param out
	 *            输出流
	 */
	protected static final void transfer(final CountDownLatch latch, final InputStream in, final OutputStream out,
			final OutputStream cache) {
		new Thread() {
			public void run() {
				byte[] bytes = new byte[1024];
				int n = 0;
				try {
					while ((n = in.read(bytes)) > 0) {
						out.write(bytes, 0, n);
						out.flush();
						if (null != cache) {
							synchronized (cache) {
								cache.write(bytes, 0, n);
							}
						}
					}
				} catch (Exception e) {
				}
				if (null != latch) {
					latch.countDown();
				}
			};
		}.start();
	}

	private final static void log(String message, Object... args) {
		Date dat = new Date();
		String msg = String.format("%1$tF %1$tT %2$-5s %3$s%n", dat, Thread.currentThread().getId(),
				String.format(message, args));
		System.out.print(msg);
	}

	public static void startServer(int port, boolean openSock4, boolean openSock5, String user, String pwd)
			throws IOException {
		log("config >> port[%s] openSock4[%s] openSock5[%s] user[%s] pwd[%s]", port, openSock4, openSock5, user, pwd);
		ServerSocket ss = new ServerSocket(port);
		Socket socket = null;
		log("Socks server port : %s listenning...", port);
		while (null != (socket = ss.accept())) {
			new Thread(new SocksServerOneThread(socket, openSock4, openSock5, user, pwd)).start();
		}
		ss.close();
	}

	public static void main(String[] args) throws IOException {
		java.security.Security.setProperty("networkaddress.cache.ttl", "86400");
		log("\n\tUSing port openSock4 openSock5 user pwd");
		int port = 1080;
		boolean openSock4 = true;
		boolean openSock5 = true;
		String user = null, pwd = null;
		// user = "user";
		pwd = "123456";
		int i = 0;
		if (args.length > i && null != args[i++]) {
			port = Integer.valueOf(args[i].trim());
		}
		if (args.length > i && null != args[i++]) {
			openSock4 = Boolean.valueOf(args[i].trim());
		}
		if (args.length > i && null != args[i++]) {
			openSock5 = Boolean.valueOf(args[i].trim());
		}
		if (args.length > i && null != args[i++]) {
			user = args[i].trim();
		}
		if (args.length > i && null != args[i++]) {
			pwd = args[i].trim();
		}
		SocksServerOneThread.startServer(port, openSock4, openSock5, user, pwd);
	}
}

© 著作权归作者所有

林小宝
粉丝 5
博文 30
码字总数 11167
作品 2
深圳
私信 提问
socks代理服务器协议的说明

socks代理里面,有socks4 socks4a socks5,也有叫sock4 sock4a sock5的,都是一回事。 socks4 socks4a和socks5的tcp部分极其简单。 这又出现一个怪现象了,网文中除了一个人说道udp比tcp复杂...

李秀杰
2014/02/19
0
0
使用javamail通过代理发送邮件

通过socks网关来访问外网的email服务器,前提是你安装了一个socks服务器。完整的使用代理发送邮件代码如下: package mail import javax.mail.internet.{InternetAddress, MimeMessage}impo...

mj4738
2012/05/29
0
2
http,socks4,socks5代理的区别

HTTP代理:能够代理客户机的HTTP访问,主要是代理浏览器访问网页,它的端口一般为80、8080、3128等; SOCKS代理:SOCKS代理与其他类型的代理不同,它只是简单地传递数据包,而并不关心是何种...

mj4738
2013/05/03
0
0
java的System.getProperty()方法可以获取的值

java.version Java 运行时环境版本 java.vendor Java 运行时环境供应商 java.vendor.url Java 供应商的 URL java.home Java 安装目录 java.vm.specification.version Java 虚拟机规范版本 ja...

cRaZy_JaZa
2013/10/16
0
0
monkeysocks开发日志--动机

monkeysocks的目标是为开发以及测试提供一个稳定的环境。 前几天听说公司的测试团队在鼓捣数据固化的东西,说白了就是在测试启动时构建一个临时性的数据库,操作完之后再销毁,这样的好处是不...

黄亿华
2013/07/06
0
2

没有更多内容

加载失败,请刷新页面

加载更多

OSChina 周日乱弹 —— 请务必让我分担他们的痛苦!

Osc乱弹歌单(2019)请戳(这里) 【今日歌曲】 @clouddyy :分享石元丈晴的单曲《Why》: 《Why》- 石元丈晴 手机党少年们想听歌,请使劲儿戳(这里) @一代码哥 :当他妈狗屁的程序员,天天...

小小编辑
22分钟前
47
2
php 遇到 No input file specified的解决方法

(一)IIS Noinput file specified 方法一:改PHP.ini中的doc_root行,打开ini文件注释掉此行,然后重启IIS 方法二: 请修改php.ini 找到 ; cgi.force_redirect = 1 去掉前面分号,把后面的1...

chenhongjiang
今天
7
0
MySQL 基础

一、常用命令 在命令行中,配置好环境变量后,通过cmd可以直接进入mysql命令行模式,同时列举几种常用命令 # 进入mysql数据库,密码可以先不写,打完-p后再输入,防止被别人看到mysql -u账...

华山猛男
今天
6
0
简单的博客系统(四)Django请求HTML页面视图信息--基于函数的视图

1. 编写用于查询数据的功能函数 应用目录 下的 views.py 文件通常用于保存响应各种请求的函数或类 from django.shortcuts import renderfrom .models import BlogArticles# Create your ...

ZeroBit
今天
5
0
用脚本将本地照片库批量导入到Day One中

因为目前iCloud 空间已经不足,其中95%都是照片,之前入手了DayOne,且空间没有限制,订阅费一年也不少,再加上DayOne作为一款日记App 也比较有名,功能方面最大的就是地理视图与照片视图,尤...

在山的那边
昨天
23
0

没有更多内容

加载失败,请刷新页面

加载更多

返回顶部
顶部