package org.zarroboogs.smartzpn.tunnel;
import android.annotation.SuppressLint;
import org.zarroboogs.smartzpn.core.LocalVpnService;
import org.zarroboogs.smartzpn.core.ProxyConfigLoader;
import java.io.IOException;
import java.net.InetSocketAddress;
import java.nio.ByteBuffer;
import java.nio.channels.SelectionKey;
import java.nio.channels.Selector;
import java.nio.channels.SocketChannel;
public abstract class Tunnel {
final static ByteBuffer GL_BUFFER = ByteBuffer.allocate(20000);
public static long SessionCount;
protected abstract void onConnected(ByteBuffer buffer) throws Exception;
protected abstract boolean isTunnelEstablished();
protected abstract void beforeSend(ByteBuffer buffer) throws Exception;
protected abstract void afterReceived(ByteBuffer buffer) throws Exception;
protected abstract void onDispose();
private SocketChannel m_InnerChannel;
private ByteBuffer m_SendRemainBuffer;
private Selector m_Selector;
private Tunnel m_BrotherTunnel;
private boolean m_Disposed;
private InetSocketAddress m_ServerEP;
protected InetSocketAddress m_DestAddress;
public Tunnel(SocketChannel innerChannel, Selector selector) {
this.m_InnerChannel = innerChannel;
this.m_Selector = selector;
SessionCount++;
}
public Tunnel(InetSocketAddress serverAddress, Selector selector) throws IOException {
SocketChannel innerChannel = SocketChannel.open();
innerChannel.configureBlocking(false);
this.m_InnerChannel = innerChannel;
this.m_Selector = selector;
this.m_ServerEP = serverAddress;
SessionCount++;
}
public void setBrotherTunnel(Tunnel brotherTunnel) {
m_BrotherTunnel = brotherTunnel;
}
public void connect(InetSocketAddress destAddress) throws Exception {
if (LocalVpnService.Instance.protect(m_InnerChannel.socket())) {//保护socket不走vpn
m_DestAddress = destAddress;
m_InnerChannel.register(m_Selector, SelectionKey.OP_CONNECT, this);//注册连接事件
m_InnerChannel.connect(m_ServerEP);//连接目标
} else {
throw new Exception("VPN protect socket failed.");
}
}
protected void beginReceive() throws Exception {
if (m_InnerChannel.isBlocking()) {
m_InnerChannel.configureBlocking(false);
}
m_InnerChannel.register(m_Selector, SelectionKey.OP_READ, this);//注册读事件
}
protected boolean write(ByteBuffer buffer, boolean copyRemainData) throws Exception {
int bytesSent;
while (buffer.hasRemaining()) {
bytesSent = m_InnerChannel.write(buffer);
if (bytesSent == 0) {
break;//不能再发送了,终止循环
}
}
if (buffer.hasRemaining()) {//数据没有发送完毕
if (copyRemainData) {//拷贝剩余数据,然后侦听写入事件,待可写入时写入。
//拷贝剩余数据
if (m_SendRemainBuffer == null) {
m_SendRemainBuffer = ByteBuffer.allocate(buffer.capacity());
}
m_SendRemainBuffer.clear();
m_SendRemainBuffer.put(buffer);
m_SendRemainBuffer.flip();
m_InnerChannel.register(m_Selector, SelectionKey.OP_WRITE, this);//注册写事件
}
return false;
} else {//发送完毕了
return true;
}
}
protected void onTunnelEstablished() throws Exception {
this.beginReceive();//开始接收数据
m_BrotherTunnel.beginReceive();//兄弟也开始收数据吧
}
@SuppressLint("DefaultLocale")
public void onConnectable() {
try {
if (m_InnerChannel.finishConnect()) {//连接成功
onConnected(GL_BUFFER);//通知子类TCP已连接,子类可以根据协议实现握手等。
} else {//连接失败
LocalVpnService.Instance.writeLog("Error: connect to %s failed.", m_ServerEP);
this.dispose();
}
} catch (Exception e) {
LocalVpnService.Instance.writeLog("Error: connect to %s failed: %s", m_ServerEP, e);
this.dispose();
}
}
public void onReadable(SelectionKey key) {
try {
ByteBuffer buffer = GL_BUFFER;
buffer.clear();
int bytesRead = m_InnerChannel.read(buffer);
if (bytesRead > 0) {
buffer.flip();
afterReceived(buffer);//先让子类处理,例如解密数据。
if (isTunnelEstablished() && buffer.hasRemaining()) {//将读到的数据,转发给兄弟。
m_BrotherTunnel.beforeSend(buffer);//发送之前,先让子类处理,例如做加密等。
if (!m_BrotherTunnel.write(buffer, true)) {
key.cancel();//兄弟吃不消,就取消读取事件。
if (ProxyConfigLoader.IS_DEBUG)
System.out.printf("%s can not read more.\n", m_ServerEP);
}
}
} else if (bytesRead < 0) {
this.dispose();//连接已关闭,释放资源。
}
} catch (Exception e) {
e.printStackTrace();
this.dispose();
}
}
public void onWritable(SelectionKey key) {
try {
this.beforeSend(m_SendRemainBuffer);//发送之前,先让子类处理,例如做加密等。
if (this.write(m_SendRemainBuffer, false)) {//如果剩余数据已经发送完毕
key.cancel();//取消写事件。
if (isTunnelEstablished()) {
m_BrotherTunnel.beginReceive();//这边数据发送完毕,通知兄弟可以收数据了。
} else {
this.beginReceive();//开始接收代理服务器响应数据
}
}
} catch (Exception e) {
this.dispose();
}
}
public void dispose() {
disposeInternal(true);
}
void disposeInternal(boolean disposeBrother) {
if (m_Disposed) {
return;
} else {
try {
m_InnerChannel.close();
} catch (Exception e) {
}
if (m_BrotherTunnel != null && disposeBrother) {
m_BrotherTunnel.disposeInternal(false);//把兄弟的资源也释放了。
}
m_InnerChannel = null;
m_SendRemainBuffer = null;
m_Selector = null;
m_BrotherTunnel = null;
m_Disposed = true;
SessionCount--;
onDispose();
}
}
}