sock5代理及隧道
1 引出
https://www.cnblogs.com/du-better/p/6519502.html
java发送邮件功能支持代理,但是不支持http代理,支持sock代理(支持SOCKS V4或V5协议)并且连接代理不需要用户名和密码。
使用的是JDK版本需要1.5或者更高,javaMail包必须是1.4.5或者更新,可以用mail.smtp.socks.host和mail.smtp.socks.port
public Properties getProperties() { Properties props = new Properties(); props.setProperty("proxySet", "true"); props.setProperty("mail.smtp.socks.host","代理IP"); props.setProperty("mail.smtp.socks.port","代理端口"); props.put("mail.smtp.auth", "true"); props.put("mail.smtp.host", "smtp.163.com"); return props; }
如果JavaMail包很旧(1.4.5之前的),这种方法不建议使用。
public Properties getProerties() { Properties props = System.getProperties(); props.setProperty("proxySet", "true"); props.setProperty("socksProxyHost","代理IP"); props.setProperty("socksProxyPort","代理端口"); props.put("mail.smtp.auth", "true"); props.put("mail.smtp.host", "smtp.163.com"); return props; }
这样也可以实现通过代理发邮件,但是必须使用System.getProperties(),这是系统级属性,会将TCP套接字指向SOCKS代理,例如数据库连接。
实现发邮件功能:
public void sendMessage(String msg) throws Exception { Properties props = getPro(); // 获得邮件会话对象 Session session = Session.getInstance(props, new SmtpAuthenticator( "邮件用户名", "邮件密码")); MimeMessage mimeMessage = new MimeMessage(session); mimeMessage.setFrom(new InternetAddress("test1@163.com"));// 发件人 // 一个人 mimeMessage.setRecipient(Message.RecipientType.TO, new InternetAddress("test2@163.com"));// 收件人 mimeMessage.setSubject("认证接口欠费或异常"); mimeMessage.setSentDate(new Date());// 发送日期 Multipart mp = new MimeMultipart("related");// 发送HTML格式的邮箱 BodyPart bodyPart = new MimeBodyPart();// 正文 bodyPart.setDataHandler(new DataHandler(msg, "text/html;charset=GBK"));// 网页格式 mp.addBodyPart(bodyPart); mimeMessage.setContent(mp);// 设置邮件内容对象 Transport.send(mimeMessage);// 发送邮件 }
SmtpAuthenticator.java
class SmtpAuthenticator extends Authenticator { String username = null; String password = null; // SMTP身份验证 public SmtpAuthenticator(String username, String password) { this.username = username; this.password = password; } public PasswordAuthentication getPasswordAuthentication() { return new PasswordAuthentication(this.username, this.password); } }
下面是官方文档解释:
问:如何将JavaMail配置为通过我的代理服务器工作?
A: JavaMail当前不支持通过Web代理服务器访问邮件服务器。 使用代理服务器的主要原因之一是允许来自公司网络的HTTP请求通过公司防火墙。 防火墙通常会阻止对Internet的大多数访问,但将允许来自代理服务器的请求通过。 此外,公司网络内的邮件服务器将对电子邮件执行类似的功能,通过SMTP接受邮件并将其转发到Internet上的最终目的地,并接受传入邮件并将其发送到相应的内部邮件服务器。
如果您的代理服务器支持SOCKS V4或V5协议( http://www.socks.nec.com/aboutsocks.html,RFC1928 )并允许匿名连接,并且您使用的是JDK 1.5或更高版本和JavaMail 1.4.5或较新的,您可以通过设置com.sun.mail.smtp软件包的javadocs中所述的“mail.smtp.socks.host”属性,在每个会话,每个协议的基础上配置SOCKS代理。 类似的属性存在于“imap”和“pop3”协议。
如果您使用的是旧版本的JDK或JavaMail,则可以让Java运行时将所有TCP套接字连接指向SOCKS服务器。 有关socksProxyHost
和socksProxyPort
属性的最新文档,请参阅网络属性指南 。 这些是系统级属性,而不是JavaMail会话属性。 它们可以在调用应用程序时从命令行进行设置,例如:java -DsocksProxyHost=myproxy ...
此工具可用于将SMTP,IMAP和POP3通信从JavaMail指向SOCKS代理服务器。 请注意,设置这些属性会将所有 TCP套接字指向SOCKS代理,这可能会对应用程序的其他方面产生负面影响。
没有这样的SOCKS服务器,如果要使用JavaMail间接访问防火墙外的邮件服务器,您可以使用诸如Corkscrew之类的程序或通过HTTP代理服务器连接 TCP 连接 。 JavaMail不支持通过HTTP代理Web服务器的直接访问。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 | package com.jds.test.httpproxy.miniserver; import io.netty.bootstrap.Bootstrap; import io.netty.bootstrap.ServerBootstrap; import io.netty.channel.*; import io.netty.channel.nio.NioEventLoopGroup; import io.netty.channel.socket.SocketChannel; import io.netty.channel.socket.nio.NioServerSocketChannel; import io.netty.channel.socket.nio.NioSocketChannel; import io.netty.handler.codec.socksx.SocksVersion; import io.netty.handler.codec.socksx.v5.*; import io.netty.handler.logging.LoggingHandler; // curl -x socks5h://localhost:11080 https://baidu.com /** * curl -x localhost:11080 https://baidu.com * 不是ss5协议 */ public class ProxySocks5 { private static void println(String tmp) { System. out .println(tmp); } private EventLoopGroup bossGroup= new NioEventLoopGroup(); private int port; private ProxySocks5( int port) { this .port = port; } public static ProxySocks5 create( int port) { return new ProxySocks5(port); } public void start() throws Exception { EventLoopGroup boss = new NioEventLoopGroup(2); EventLoopGroup worker = new NioEventLoopGroup(); try { ServerBootstrap bootstrap = new ServerBootstrap(); bootstrap. group (boss, worker) .channel(NioServerSocketChannel. class ) .option(ChannelOption.SO_BACKLOG, 1024) .option(ChannelOption.CONNECT_TIMEOUT_MILLIS, 1000) .childHandler( new ChannelInitializer<SocketChannel>() { @Override protected void initChannel(SocketChannel ch) throws Exception { if ( false ) { ch.pipeline().addLast( new LoggingHandler()); } ch.pipeline().addLast(Socks5ServerEncoder.DEFAULT); ch.pipeline().addLast( new Socks5InitialRequestDecoder()); ch.pipeline().addLast( new Socks5InitialRequestHandler()); if ( false ) { ch.pipeline().addLast( new Socks5PasswordAuthRequestDecoder()); ch.pipeline().addLast( new Socks5PasswordAuthRequestHandler()); } ch.pipeline().addLast( new Socks5CommandRequestDecoder()); ch.pipeline().addLast( new Socks5CommandRequestHandler()); } }); ChannelFuture future = bootstrap.bind(port).sync(); println( "bind port : " + port); future.channel().closeFuture().sync(); } finally { boss.shutdownGracefully(); worker.shutdownGracefully(); } } public static void main(String[] args) throws Exception { int port = 11080; ProxySocks5.create(port).start(); } class Socks5InitialRequestHandler extends SimpleChannelInboundHandler<DefaultSocks5InitialRequest> { @Override protected void channelRead0(ChannelHandlerContext ctx, DefaultSocks5InitialRequest msg) throws Exception { if (msg.decoderResult().isFailure()) { println( "not ss5" ); ctx.fireChannelRead(msg); } else { if (msg.version(). equals (SocksVersion.SOCKS5)) { if ( false ) { Socks5InitialResponse initialResponse = new DefaultSocks5InitialResponse(Socks5AuthMethod.PASSWORD); ctx.writeAndFlush(initialResponse); } else { Socks5InitialResponse initialResponse = new DefaultSocks5InitialResponse(Socks5AuthMethod.NO_AUTH); ctx.writeAndFlush(initialResponse); } } } } } public class Socks5PasswordAuthRequestHandler extends SimpleChannelInboundHandler<DefaultSocks5PasswordAuthRequest> { @Override protected void channelRead0(ChannelHandlerContext ctx, DefaultSocks5PasswordAuthRequest msg) throws Exception { println( "auth : " + msg.username() + "," + msg.password()); if ( true ) { Socks5PasswordAuthResponse passwordAuthResponse = new DefaultSocks5PasswordAuthResponse(Socks5PasswordAuthStatus.SUCCESS); ctx.writeAndFlush(passwordAuthResponse); } else { Socks5PasswordAuthResponse passwordAuthResponse = new DefaultSocks5PasswordAuthResponse(Socks5PasswordAuthStatus.FAILURE); //发送鉴权失败消息,完成后关闭channel ctx.writeAndFlush(passwordAuthResponse).addListener(ChannelFutureListener.CLOSE); } } } private class Socks5CommandRequestHandler extends SimpleChannelInboundHandler<DefaultSocks5CommandRequest> { @Override protected void channelRead0(final ChannelHandlerContext clientChannelContext, DefaultSocks5CommandRequest msg) throws Exception { println( "target : " + msg.type() + "," + msg.dstAddr() + "," + msg.dstPort()); if (msg.type(). equals (Socks5CommandType.CONNECT)) { Bootstrap bootstrap = new Bootstrap(); bootstrap. group (bossGroup) .channel(NioSocketChannel. class ) .option(ChannelOption.TCP_NODELAY, true ) .handler( new ChannelInitializer<SocketChannel>() { @Override protected void initChannel(SocketChannel ch) throws Exception { //ch.pipeline().addLast(new LoggingHandler());//in out ch.pipeline().addLast( new Dest2ClientHandler(clientChannelContext)); } }); ChannelFuture future = bootstrap.connect(msg.dstAddr(), msg.dstPort()); future.addListener( new ChannelFutureListener() { public void operationComplete(final ChannelFuture future) throws Exception { if (future.isSuccess()) { clientChannelContext.pipeline().addLast( new Client2DestHandler(future)); Socks5CommandResponse commandResponse = new DefaultSocks5CommandResponse(Socks5CommandStatus.SUCCESS, Socks5AddressType.IPv4); clientChannelContext.writeAndFlush(commandResponse); } else { Socks5CommandResponse commandResponse = new DefaultSocks5CommandResponse(Socks5CommandStatus.FAILURE, Socks5AddressType.IPv4); clientChannelContext.writeAndFlush(commandResponse); } } }); } else { clientChannelContext.fireChannelRead(msg); } } private class Dest2ClientHandler extends ChannelInboundHandlerAdapter { private ChannelHandlerContext clientChannelContext; public Dest2ClientHandler(ChannelHandlerContext clientChannelContext) { this .clientChannelContext = clientChannelContext; } @Override public void channelRead(ChannelHandlerContext ctx2, Object destMsg) throws Exception { clientChannelContext.writeAndFlush(destMsg); } @Override public void channelInactive(ChannelHandlerContext ctx2) throws Exception { println( "inactive target" ); clientChannelContext.channel().close(); } } private class Client2DestHandler extends ChannelInboundHandlerAdapter { private ChannelFuture destChannelFuture; public Client2DestHandler(ChannelFuture destChannelFuture) { this .destChannelFuture = destChannelFuture; } @Override public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception { destChannelFuture.channel().writeAndFlush(msg); } @Override public void channelInactive(ChannelHandlerContext ctx) throws Exception { println( "inactive client" ); destChannelFuture.channel().close(); } } } } |
2 使用socks 5 代理作为系统代理(2025年1月)
1 )腾讯云东京
浏览器一塌糊涂(https透明代理也是),curl偶尔成功
不勾选后,确实传递到代理的是ip
而safiri和chrome使用mac代理时,后端永远是域名,所以系统代理默认就托管dns到代理了,结合https透明代理也有问题,这样看东京那个服务器有问题
2 )阿里云杭州
三大浏览器都可
优酷不行,意味着优酷里面有协议没支持socks
所以还是要VPN,专门建立通道,连接直达对方
https://www.cnblogs.com/cc11001100/p/9949729.html
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 | package cc11001100.proxyServerDev.socks5; 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.net.UnknownHostException; import java.nio.ByteBuffer; import java.util.ArrayList; import java.util.Arrays; import java.util.List; import java.util.UUID; import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicInteger; /** * socks5代理服务器简单实现 * * <a>https://www.ietf.org/rfc/rfc1928.txt</a> * <p> * <p> * 使用socks5代理的坑,域名在本地解析还是在代理服务器端解析,有些比如google.com就必须在代理服务器端解析 * <a>https://blog.emacsos.com/use-socks5-proxy-in-curl.html</a> * * @author CC11001100 */ public class Socks5ProxyServer { // 服务监听在哪个端口上 private static final Integer SERVICE_LISTENER_PORT = 10086; // 能够允许的最大客户端数量 private static final Integer MAX_CLIENT_NUM = 100; // 用于统计客户端的数量 private static AtomicInteger clientNumCount = new AtomicInteger(); // socks协议的版本,固定为5 private static final byte VERSION = 0X05; // RSV,必须为0 private static final byte RSV = 0X00; private static String SERVER_IP_ADDRESS; static { try { SERVER_IP_ADDRESS = InetAddress.getLocalHost().getHostAddress(); } catch (UnknownHostException e) { e.printStackTrace(); } } public static class ClientHandler implements Runnable { private Socket clientSocket; private String clientIp; private int clientPort; public ClientHandler(Socket clientSocket) { this .clientSocket = clientSocket; this .clientIp = clientSocket.getInetAddress().getHostAddress(); this .clientPort = clientSocket.getPort(); } @Override public void run() { try { // 协商认证方法 negotiationCertificationMethod(); // 开始处理客户端的命令 handleClientCommand(); } catch (Exception e) { handleLog( "exception, " + e.getMessage()); } finally { close(clientSocket); handleLog( "client dead, current client count=%s" , clientNumCount.decrementAndGet()); } } // 协商与客户端的认证方法 private void negotiationCertificationMethod() throws IOException { InputStream is = clientSocket.getInputStream(); OutputStream os = clientSocket.getOutputStream(); byte [] buff = new byte [255]; // 接收客户端的支持的方法 is .read(buff, 0, 2); int version = buff[0]; int methodNum = buff[1]; if (version != VERSION) { throw new RuntimeException( "version must 0X05" ); } else if (methodNum < 1) { throw new RuntimeException( "method num must gt 0" ); } is .read(buff, 0, methodNum); List<METHOD> clientSupportMethodList = METHOD.convertToMethod(Arrays.copyOfRange(buff, 0, methodNum)); handleLog( "version=%s, methodNum=%s, clientSupportMethodList=%s" , version, methodNum, clientSupportMethodList); // 向客户端发送回应,这里不进行认证 buff[0] = VERSION; buff[1] = METHOD.NO_AUTHENTICATION_REQUIRED.rangeStart; os.write(buff, 0, 2); os.flush(); } // 认证通过,开始处理客户端发送过来的指令 private void handleClientCommand() throws IOException { InputStream is = clientSocket.getInputStream(); OutputStream os = clientSocket.getOutputStream(); byte [] buff = new byte [255]; // 接收客户端命令 is .read(buff, 0, 4); int version = buff[0]; COMMAND command = COMMAND.convertToCmd(buff[1]); int rsv = buff[2]; ADDRESS_TYPE addressType = ADDRESS_TYPE.convertToAddressType(buff[3]); if (rsv != RSV) { throw new RuntimeException( "RSV must 0X05" ); } else if (version != VERSION) { throw new RuntimeException( "VERSION must 0X05" ); } else if (command == null ) { // 不支持的命令 sendCommandResponse(COMMAND_STATUS.COMMAND_NOT_SUPPORTED); handleLog( "not supported command" ); return ; } else if (addressType == null ) { // 不支持的地址类型 sendCommandResponse(COMMAND_STATUS.ADDRESS_TYPE_NOT_SUPPORTED); handleLog( "address type not supported" ); return ; } String targetAddress = "" ; switch (addressType) { case DOMAIN: // 如果是域名的话第一个字节表示域名的长度为n,紧接着n个字节表示域名 is .read(buff, 0, 1); int domainLength = buff[0]; is .read(buff, 0, domainLength); targetAddress = new String(Arrays.copyOfRange(buff, 0, domainLength)); break ; case IPV4: // 如果是ipv4的话使用固定的4个字节表示地址 is .read(buff, 0, 4); targetAddress = ipAddressBytesToString(buff); break ; case IPV6: throw new RuntimeException( "not support ipv6." ); } is .read(buff, 0, 2); int targetPort = ((buff[0] & 0XFF) << 8) | (buff[1] & 0XFF); StringBuilder msg = new StringBuilder(); msg.append( "version=" ).append(version).append( ", cmd=" ).append(command.name()) .append( ", addressType=" ).append(addressType.name()) .append( ", domain=" ).append(targetAddress).append( ", port=" ).append(targetPort); handleLog(msg.toString()); // 响应客户端发送的命令,暂时只实现CONNECT命令 switch (command) { case CONNECT: handleConnectCommand(targetAddress, targetPort); case BIND: throw new RuntimeException( "not support command BIND" ); case UDP_ASSOCIATE: throw new RuntimeException( "not support command UDP_ASSOCIATE" ); } } // convert ip address from 4 byte to string private String ipAddressBytesToString( byte [] ipAddressBytes) { // first convert to int avoid negative return (ipAddressBytes[0] & 0XFF) + "." + (ipAddressBytes[1] & 0XFF) + "." + (ipAddressBytes[2] & 0XFF) + "." + (ipAddressBytes[3] & 0XFF); } // 处理CONNECT命令 private void handleConnectCommand(String targetAddress, int targetPort) throws IOException { Socket targetSocket = null ; try { targetSocket = new Socket(targetAddress, targetPort); } catch (IOException e) { sendCommandResponse(COMMAND_STATUS.GENERAL_SOCKS_SERVER_FAILURE); return ; } sendCommandResponse(COMMAND_STATUS.SUCCEEDED); new SocketForwarding(clientSocket, targetSocket).start(); } private void sendCommandResponse(COMMAND_STATUS commandStatus) throws IOException { OutputStream os = clientSocket.getOutputStream(); os.write(buildCommandResponse(commandStatus.rangeStart)); os.flush(); } private byte [] buildCommandResponse( byte commandStatusCode) { ByteBuffer payload = ByteBuffer.allocate(100); payload.put(VERSION); payload.put(commandStatusCode); payload.put(RSV); // payload.put(ADDRESS_TYPE.IPV4.value); // payload.put(SERVER_IP_ADDRESS.getBytes()); payload.put(ADDRESS_TYPE.DOMAIN.value); byte [] addressBytes = SERVER_IP_ADDRESS.getBytes(); payload.put(( byte ) addressBytes.length); payload.put(addressBytes); payload.put(( byte ) (((SERVICE_LISTENER_PORT & 0XFF00) >> 8))); payload.put(( byte ) (SERVICE_LISTENER_PORT & 0XFF)); byte [] payloadBytes = new byte [payload.position()]; payload.flip(); payload. get (payloadBytes); return payloadBytes; } private void handleLog(String format, Object... args) { log( "handle, clientIp=" + clientIp + ", port=" + clientPort + ", " + format, args); } } // 用来连接客户端和目标服务器转发流量 public static class SocketForwarding { // 客户端socket private Socket clientSocket; private String clientIp; // 目标地址socket private Socket targetSocket; private String targetAddress; private int targetPort; public SocketForwarding(Socket clientSocket, Socket targetSocket) { this .clientSocket = clientSocket; this .clientIp = clientSocket.getInetAddress().getHostAddress(); this .targetSocket = targetSocket; this .targetAddress = targetSocket.getInetAddress().getHostAddress(); this .targetPort = targetSocket.getPort(); } public void start() { OutputStream clientOs = null ; InputStream clientIs = null ; InputStream targetIs = null ; OutputStream targetOs = null ; long start = System.currentTimeMillis(); try { clientOs = clientSocket.getOutputStream(); clientIs = clientSocket.getInputStream(); targetOs = targetSocket.getOutputStream(); targetIs = targetSocket.getInputStream(); // 512K,因为会有很多个线程同时申请buff空间,所以不要太大以以防OOM byte [] buff = new byte [1024 * 512]; while ( true ) { boolean needSleep = true ; while (clientIs.available() != 0) { int n = clientIs.read(buff); targetOs.write(buff, 0, n); transientLog( "client to remote, bytes=%d" , n); needSleep = false ; } while (targetIs.available() != 0) { int n = targetIs.read(buff); clientOs.write(buff, 0, n); transientLog( "remote to client, bytes=%d" , n); needSleep = false ; } if (clientSocket.isClosed()) { transientLog( "client closed" ); break ; } // 会话最多30秒超时,防止有人占着线程老不释放 if (System.currentTimeMillis() - start > 30_000) { transientLog( "time out" ); break ; } // 如果本次循环没有数据传输,说明管道现在不繁忙,应该休息一下把资源让给别的线程 if (needSleep) { try { TimeUnit.MILLISECONDS.sleep(10); } catch (InterruptedException e) { e.printStackTrace(); } } } } catch (IOException e) { transientLog( "conn exception" + e.getMessage()); } finally { close(clientIs); close(clientOs); close(targetIs); close(targetOs); close(clientSocket); close(targetSocket); } transientLog( "done." ); } private void transientLog(String format, Object... args) { log( "forwarding, clientIp=" + clientIp + ", targetAddress=" + targetAddress + ", port=" + targetPort + ", " + format, args); } } // 客户端认证方法 public static enum METHOD { NO_AUTHENTICATION_REQUIRED(( byte ) 0X00, ( byte ) 0X00, "NO AUTHENTICATION REQUIRED" ), GSSAPI(( byte ) 0X01, ( byte ) 0X01, "GSSAPI" ), USERNAME_PASSWORD(( byte ) 0X02, ( byte ) 0X02, " USERNAME/PASSWORD" ), IANA_ASSIGNED(( byte ) 0X03, ( byte ) 0X07, "IANA ASSIGNED" ), RESERVED_FOR_PRIVATE_METHODS(( byte ) 0X80, ( byte ) 0XFE, "RESERVED FOR PRIVATE METHODS" ), NO_ACCEPTABLE_METHODS(( byte ) 0XFF, ( byte ) 0XFF, "NO ACCEPTABLE METHODS" ); private byte rangeStart; private byte rangeEnd; private String description; METHOD( byte rangeStart, byte rangeEnd, String description) { this .rangeStart = rangeStart; this .rangeEnd = rangeEnd; this .description = description; } public boolean isMe( byte value) { return value >= rangeStart && value <= rangeEnd; } public static List<METHOD> convertToMethod( byte [] methodValues) { List<METHOD> methodList = new ArrayList<>(); for ( byte b : methodValues) { for (METHOD method : METHOD.values()) { if (method.isMe(b)) { methodList.add(method); break ; } } } return methodList; } } // 客户端命令 public static enum COMMAND { CONNECT(( byte ) 0X01, "CONNECT" ), BIND(( byte ) 0X02, "BIND" ), UDP_ASSOCIATE(( byte ) 0X03, "UDP ASSOCIATE" ); byte value; String description; COMMAND( byte value, String description) { this .value = value; this .description = description; } public static COMMAND convertToCmd( byte value) { for (COMMAND cmd : COMMAND.values()) { if (cmd.value == value) { return cmd; } } return null ; } } // 要请求的地址类型 public static enum ADDRESS_TYPE { IPV4(( byte ) 0X01, "the address is a version-4 IP address, with a length of 4 octets" ), DOMAIN(( byte ) 0X03, "the address field contains a fully-qualified domain name. The first\n" + " octet of the address field contains the number of octets of name that\n" + " follow, there is no terminating NUL octet." ), IPV6(( byte ) 0X04, "the address is a version-6 IP address, with a length of 16 octets." ); byte value; String description; ADDRESS_TYPE( byte value, String description) { this .value = value; this .description = description; } public static ADDRESS_TYPE convertToAddressType( byte value) { for (ADDRESS_TYPE addressType : ADDRESS_TYPE.values()) { if (addressType.value == value) { return addressType; } } return null ; } } // 对于命令的处理结果 public static enum COMMAND_STATUS { SUCCEEDED(( byte ) 0X00, ( byte ) 0X00, "succeeded" ), GENERAL_SOCKS_SERVER_FAILURE(( byte ) 0X01, ( byte ) 0X01, "general SOCKS server failure" ), CONNECTION_NOT_ALLOWED_BY_RULESET(( byte ) 0X02, ( byte ) 0X02, "connection not allowed by ruleset" ), NETWORK_UNREACHABLE(( byte ) 0X03, ( byte ) 0X03, "Network unreachable" ), HOST_UNREACHABLE(( byte ) 0X04, ( byte ) 0X04, "Host unreachable" ), CONNECTION_REFUSED(( byte ) 0X05, ( byte ) 0X05, "Connection refused" ), TTL_EXPIRED(( byte ) 0X06, ( byte ) 0X06, "TTL expired" ), COMMAND_NOT_SUPPORTED(( byte ) 0X07, ( byte ) 0X07, "Command not supported" ), ADDRESS_TYPE_NOT_SUPPORTED(( byte ) 0X08, ( byte ) 0X08, "Address type not supported" ), UNASSIGNED(( byte ) 0X09, ( byte ) 0XFF, "unassigned" ); private byte rangeStart; private byte rangeEnd; private String description; COMMAND_STATUS( byte rangeStart, byte rangeEnd, String description) { this .rangeStart = rangeStart; this .rangeEnd = rangeEnd; this .description = description; } } private synchronized static void log(String format, Object... args) { System. out .println(String.format(format, args)); } private static void close(Closeable closeable) { if (closeable != null ) { try { closeable.close(); } catch (IOException e) { e.printStackTrace(); } } } public static void main(String[] args) throws IOException { ServerSocket serverSocket = new ServerSocket(SERVICE_LISTENER_PORT); while ( true ) { Socket socket = serverSocket.accept(); if (clientNumCount. get () >= MAX_CLIENT_NUM) { log( "client num run out." ); continue ; } log( "new client, ip=%s:%d, current client count=%s" , socket.getInetAddress(), socket.getPort(), clientNumCount. get ()); clientNumCount.incrementAndGet(); new Thread( new ClientHandler(socket), "client-handler-" + UUID.randomUUID().toString()).start(); } } } |
在客户端访问域名的时候,涉及到一个问题,这个域名是应该是客户端解析完告诉代理服务器ip还是应该把域名交给代理服务器去解析?
一般客户端的默认行为是域名在客户端解析,然后再将解析出来的ip拿给代理服务器去处理,但是对于一些网站来说通过ip访问是不成功的,比如google的dns解析,国内机器解析到的ip可能已经被污染,笔者实验发现对于在阿里云服务器访问google解析到的ip 74.86.151.162:443,通过ip访问是不能成功的,而在微软云机器上解析到的ip 172.217.161.164:443,通过ip可以访问成功,将域名交给代理服务器,代理服务器在美国,解析到的ip是能够访问成功的,所以在使用socks5的时候最好能够指明域名是在本地解析还是在代理服务器解析:
默认是在本地解析完将ip传给代理服务器:
1
|
curl --socks5 "23.225.xxx.xxx:10086" https://www.google.com |
通过socks5-hostname指定域名交给代理服务器解析
1
|
curl --socks5-hostname "23.225.xxx.xxx:10086" "https://www.google.com" |
curl中关于这部分的说明:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
|
--socks5-hostname <host[:port]> Use the specified SOCKS5 proxy (and let the proxy resolve the host name). If the port number is not specified, it is assumed at port 1080. (Added in 7.18.0) This option overrides any previous use of -x, --proxy, as they are mutually exclusive. Since 7.21.7, this option is superfluous since you can specify a socks5 hostname proxy with -x, --proxy using a socks5h:// protocol prefix. If this option is used several times, the last one will be used. (This option was previously wrongly documented and used as --socks without the number appended.) --socks5 <host[:port]> Use the specified SOCKS5 proxy - but resolve the host name locally. If the port number is not specified, it is assumed at port 1080. This option overrides any previous use of -x, --proxy, as they are mutually exclusive. Since 7.21.7, this option is superfluous since you can specify a socks5 proxy with -x, --proxy using a socks5:// protocol prefix. If this option is used several times, the last one will be used. (This option was previously wrongly documented and used as --socks without the number appended.) This option (as well as --socks4) does not work with IPV6, FTPS or LDAP. |
Chrome浏览器的SwitchyOmega的socks5代理是会将域名传给代理服务器解析。
https://blog.csdn.net/penetrate/article/details/52353613
在linux上运行了socks5的代理客户端之后,发现我的iphone在局域网的详细信息里竟然无法设置socks代理,只有http代理,而我的代理客户端又不支持http代理,所以找了下iphone上设置socks代理的方法,记录如下:
HTTP Prxoy有3种模式: 关闭Off/手动Manual/自动Auto
用自动配置文件,就可以支持socks代理
-
function FindProxyForURL(url, host)
-
{
-
return "SOCKS proxy_host:proxy_port";
-
}
将上面的proxy_host和proxy_port换成socks服务器实际的IP地址和端口
然后保存为 proxy.pac
放到 http://zhiwei.li/proxy.pac
在 iPhone设备中,添加自动配置 URL 为上面的地址,就可以使用socks代理了。
https://www.zhihu.com/tardis/bd/ans/3374886575
下载Proxifier
https://blog.csdn.net/logocool/article/details/139295444
socks5 如何让dns不被污染
问题
发现firefox浏览器代理设置成socks5后,查看ip是成功了,但是谷歌等海外的还是无法正常访问。
原因
主要原因是socks5连接虽然是成功了,但是dns还是走国内的,国内的dns解析都被污染了导致没法正常访问
解决
把设置里的 使用SOCKS v5 时代理DNS查询
既然是dns被污染了,dns 的ip是设置在我们电脑,为什么这个勾选上就知道走socks5服务器的dns呢?
翻了下sokcs5的协议有个 AdreesType 为0x03的类型,实际上就是用来请求域名的。 所以实际不是把 dns的请求包发给Socks5服务器,而是发了个特殊的命令。
我们抓包看下socks5的连接请求
DNS代理请求后面会带了域名
对比下没有DNS代理的包, 看后面跟着是IP +端口, 由于dns污染返回的是错的ip。 socks5去访问这个 错的ip当然是不通了
https://yr7ywq.smartapps.baidu.com/?_chatQuery=windows%2Bsocket5代理&searchid=8246449664258517682&_chatParams=%7B%22agent_id%22%3A%22c816%22%2C%22chat_no_login%22%3Atrue%2C%22content_build_id%22%3A%221342df2%22%2C%22from%22%3A%22q2c%22%2C%22token%22%3A%22YN3Ckf9%2F0q5prgS9HHUq8IOtfzF7SNNWmrnCmjLyN1CnkSGrIi1tMmrsbA0bg8%2BrW5lY%2B%2F3aWmeC8RCKphFPws12gdwMh5aNEP9E43wr3ntDL8Dyr5Minuw%3D%22%7D&tplname=ai_agent_distribute&srcid=1599&lid=8246449664258517682&order=3&_swebScene=3711000610001000
常用的Windows Socket5代理软件或工具
- Goproxy:
- 一个由Golang实现的高性能HTTP、HTTPS、WebSocket、TCP、SOCKS5代理服务器,支持多种功能和跨平台运行。
- Dante:
- 一个免费的SOCKS代理服务器,提供安全性和TCP/IP终止、资源控制、日志记录和身份验证等功能。
- Gost:
- 一个用Go语言实现的安全隧道,支持多端口监听、可设置转发代理,同时支持包括SOCKS5在内的多种代理协议。
- Hysteria:
- 一个功能丰富的代理加速工具,专为恶劣网络环境进行优化,基于修改版的QUIC协议。
- WARP:
- Cloudflare推出的一款服务,可以用来保护使用者隐私,基于WireGuard协议,可以额外提供SOCKS5代理支持。
3 隧道(20250218)
3.1 https://blog.csdn.net/2401_86436868/article/details/142168849
TUN/TAP 是操作系统内核中的虚拟网络设备,由软件进行实现,向操作系统和应用程序提供与硬件网络设备完全相同的功能。其中 TAP 是以太网设备(二层设备),操作和封装以太网数据帧,TUN 则是网络层设备(三层设备),操作和封装网络层数据帧。
当应用程序发出报文后,报文将通过操作系统协议栈处理,到达网络设备,硬件网络设备将收到的报文转化为电信号发出,而虚拟网络设备(TUN/TAP)不具备实际的物理功能,报文需要上层应用进行处理
3.2 https://blog.csdn.net/Lid_23/article/details/132110210
TUN/TAP是一对虚拟网络设备接口,用于在用户空间程序和内核之间进行数据传输。TUN代表"网络隧道",而TAP代表"以太网适配器"。
TUN/TAP是一对虚拟网络设备接口,它们是Linux内核中的一部分。TUN设备被用于处理IP协议数据包,而TAP设备则处理以太网帧。使用TUN/TAP接口,用户空间程序可以像操作物理网络设备一样操作这些虚拟设备,从而实现对网络数据包的捕获、修改和注入等操作。TUN/TAP设备的实现基于Linux内核的网络栈,并通过设备文件的形式暴露给用户空间
3.3 https://blog.csdn.net/weixin_30270889/article/details/99323785
若要实现在 Linux 下的代理程序,方法有很多,比如看着 RFC 1928 来实现一个 socks5 代理并自行设置程序经过 socks5 代理等方式,下文是使用 Linux 提供的 tun/tap
设备来实现 UDP 代理隧道的大体思路和过程讲解。
tun / tap 是由 Linux (可能还有其他 *NIX 系统提供支持)提供的,可以用来实现用户态的网络路由等处理的虚拟网络接口。也就是说,它们允许用户态的程序直接管理这个网络接口,而不是让内核协议栈来处理网络包。
TUN 和 TAP 分别是虚拟的三层和二层网络设备,也就是说,我们可以从 TUN 拿到的就是 IP 层的网络数据包了,而 TAP 则是二层网络包,比如以太网包。因为我只打算对 IP 层的包进行处理
TUN 设备可以由程序创建和销毁(这种情况下即便程序没有主动的销毁创建的 TUN 设备,程序退出时 TUN 设备也会自行销毁),也可以使用 cli 工具创建和销毁,比如 ip tuntap
,tunctl
,或是 openvpn --mktun
。在我们用程序实现之前,我们先使用 ip tuntap
来创建一个 TUN 设备来进行简易的测试。
建立一个 tun 设备(网络接口),然后设置路由表把数据包路由到 tun 设备里。
由于 tun 设备需要我们编写用户态的程序来操作数据包,所以需要写个东西来处理包数据的 IO 。tun 是三层设备,故能拿到的都是三层( IP 层)的包了。
当开始处理包时,我们就可以在 wireshark 或其他类似软件中看到流经该 tun 的网络包了。
作为最简单的实践,我们可以写一个无脑的程序去伪装远程端响应我们网络设备中出现的 ICMP 包,首先本地拦截 123.123.123.123
到我们的 TUN 网络设备,然后我们在 ping 的时候,就可以从 TUN 中 read
到 ICMP 包了,那么接下来,我们只需要互换 IP 包的源地址和目的地址,并修改 ICMP 包中的标志位为 ECHO_REPLY
,(别忘了重算包的checksum)然后写回 TUN 设备,就可以让 ping 程序认为远程端服务器正确的做了响应了。
那么服务端怎么把客户端发来的包写到网卡中,又怎么把远程端服务器返回的数据包捕获到程序中呢?
为了让我们的数据包发出去后远程端返回的数据包依然能回到我们的代理隧道服务端程序所处的服务器上,我们自然要对数据包进行一次 sNAT。而假如我们把源地址改成了服务器的 IP,返回的数据包就会进入服务器的默认网络接口。一旦数据包进入由内核控制的网络接口,内核协议栈就会处理 SYN 包并自动做出响应,如果我们隧道中的 TCP 数据包发出去,结果远程端服务器与我们的服务器建立的连接,这就很糟糕了,于是我们需要让我们的数据包不经过协议栈处理而由我们控制。
我们能控制什么?当然是 TUN 设备啦!还记得我们可以给 TUN 设备指定网络地址吗?我们可以在服务端也建立一个 TUN 设备,并指定一个内网网络地址(我假设指定的是192.168.61.123
),我们把要发的数据包写入该 TUN ,数据包就发出去了。接下来呢?我们当然是写一条 iptables 规则来把数据包导到我们的 TUN 设备了。大概是这样的:
3.4 https://blog.nsfocus.net/linux-tun-tap/
在linux的tcp/ip协议栈中tap/tun设备所处的地位以及这种技术的使用和实验讲解。本文只讨论以太网的物理网卡,并且以linux 3.x内核的一个UDP包的发送过程作为示例
3.5 https://www.zhihu.com/tardis/bd/art/601733049?source_id=1001
Tap(网络接口层)
与 Tun 原理和用途类似,只不过 Tap 工作在网络接口层,Tun 处理 IP 报文,而 Tap 处理以太帧。
3.6 https://www.zhihu.com/question/580303670?utm_id=0
我们知道不管是SOCKS还是HTTP代理,都是应用层的代理。所以即使是在系统设置了全局代理,总有一些程序倔强,不按系统设置走代理。于是就有了TUN/TAP这类虚拟网卡。
虽然数据在内核和用户态之间来来回回,但对现在的电脑硬件来讲,也不算什么,至少日常看看视频上网都没问题。
TUN/TAP在windows上的实现
Tap Windows Adapter V9
openvpn默认的虚拟网卡,支持二层和三层转发,效率不如wintun。
Wintun
WireGuard默认的虚拟网卡,仅仅支持三层转发,本次实验采用wintun。
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 分享一个免费、快速、无限量使用的满血 DeepSeek R1 模型,支持深度思考和联网搜索!
· 基于 Docker 搭建 FRP 内网穿透开源项目(很简单哒)
· ollama系列01:轻松3步本地部署deepseek,普通电脑可用
· 25岁的心里话
· 按钮权限的设计及实现
2019-10-10 ymm (yet)
2017-10-10 spring-data-redis序列化实践笔记