Skip to content
This repository was archived by the owner on Nov 6, 2022. It is now read-only.

app:service: convert to dep injection #5

Merged
merged 3 commits into from
Dec 16, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
18 changes: 9 additions & 9 deletions app/src/main/java/network/grape/service/GrapeVpnService.java
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,10 @@
import java.net.Socket;
import java.net.UnknownHostException;
import java.nio.ByteBuffer;
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
import network.grape.lib.PacketHeaderException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
Expand Down Expand Up @@ -69,12 +73,6 @@ public int onStartCommand(Intent intent, int flags, int startId) {
public void run() {
logger.info("running vpn service");

// map this class into the socket protector implementation so that other classes like the
// SessionHandler can protect sockets later on (protect is provided by the VpnService which
// this class inherits).
SocketProtector protector = SocketProtector.getInstance();
protector.setProtector(this);

try {
if (startVpnService()) {
logger.info("VPN Service started");
Expand Down Expand Up @@ -134,11 +132,13 @@ void startTrafficHandler() throws IOException {
// Allocate the buffer for a single packet.
ByteBuffer packet = ByteBuffer.allocate(MAX_PACKET_LEN);

SessionHandler handler = SessionHandler.getInstance();
handler.setOutputStream(clientWriter);
SessionManager sessionManager = new SessionManager();
SessionHandler handler = new SessionHandler(sessionManager, new SocketProtector(this));

// background thread for writing output to the vpn outputstream
vpnWriter = new VpnWriter(clientWriter);
final BlockingQueue<Runnable> taskQueue = new LinkedBlockingQueue<>();
ThreadPoolExecutor executor = new ThreadPoolExecutor(8, 100, 10, TimeUnit.SECONDS, taskQueue);
vpnWriter = new VpnWriter(clientWriter, sessionManager, executor);
vpnWriterThread = new Thread(vpnWriter);
vpnWriterThread.start();

Expand Down
33 changes: 13 additions & 20 deletions app/src/main/java/network/grape/service/SessionHandler.java
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@
import static network.grape.lib.network.ip.IpHeader.IP4_VERSION;
import static network.grape.lib.network.ip.IpHeader.IP6_VERSION;

import java.io.FileOutputStream;
import java.io.IOException;
import java.net.Inet4Address;
import java.net.InetSocketAddress;
Expand All @@ -30,20 +29,14 @@
*/
public class SessionHandler {
private final Logger logger = LoggerFactory.getLogger(SessionHandler.class);
private static final SessionHandler handler = new SessionHandler();
private SocketProtector protector = SocketProtector.getInstance();
private Selector selector = SessionManager.INSTANCE.getSelector();
private FileOutputStream outputStream;

public static SessionHandler getInstance() {
return handler;
}

private SessionHandler() {
}

void setOutputStream(FileOutputStream outputStream) {
this.outputStream = outputStream;
private final SocketProtector protector;
private final Selector selector;
private final SessionManager sessionManager;

public SessionHandler(SessionManager sessionManager, SocketProtector protector) {
this.sessionManager = sessionManager;
this.selector = sessionManager.getSelector();
this.protector = protector;
}

/**
Expand Down Expand Up @@ -92,10 +85,10 @@ private void handleTcpPacket(ByteBuffer payload, IpHeader ipHeader, TcpHeader tc

private void handleUdpPacket(ByteBuffer payload, IpHeader ipHeader, UdpHeader udpHeader) {
// try to find an existing session
Session session = SessionManager.INSTANCE
.getSession(ipHeader.getSourceAddress(), udpHeader.getSourcePort(),
ipHeader.getDestinationAddress(),
udpHeader.getDestinationPort(), TransportHeader.UDP_PROTOCOL);
Session session = sessionManager.getSession(ipHeader.getSourceAddress(),
udpHeader.getSourcePort(),
ipHeader.getDestinationAddress(),
udpHeader.getDestinationPort(), TransportHeader.UDP_PROTOCOL);

// otherwise create a new one
if (session == null) {
Expand Down Expand Up @@ -151,7 +144,7 @@ private void handleUdpPacket(ByteBuffer payload, IpHeader ipHeader, UdpHeader ud
}
session.setChannel(channel);

if (!SessionManager.INSTANCE.putSession(session)) {
if (!sessionManager.putSession(session)) {
// just in case we fail to add it (we should hopefully never get here)
logger.error("Unable to create a new session in the session manager for " + session);
return;
Expand Down
12 changes: 6 additions & 6 deletions app/src/main/java/network/grape/service/SessionManager.java
Original file line number Diff line number Diff line change
Expand Up @@ -19,15 +19,15 @@
* You can think of this thing as a sort of a local NAT within the phone because it has to keep
* track of all of the outbound <-> inbound mappings of ports and IPs like a NAT table does.
*/
public enum SessionManager {
INSTANCE;
public class SessionManager {

private final Logger logger = LoggerFactory.getLogger(SessionManager.class);
private final Map<String, Session> table = new ConcurrentHashMap<>();
@Getter
private Selector selector;
private final Logger logger;
private final Map<String, Session> table;
@Getter private Selector selector;

SessionManager() {
logger = LoggerFactory.getLogger(SessionManager.class);
table = new ConcurrentHashMap<>();
try {
selector = Selector.open();
} catch (IOException ex) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,18 +23,21 @@
* terminates.
*/
public class SocketDataReaderWorker implements Runnable {
private final Logger logger = LoggerFactory.getLogger(SocketDataReaderWorker.class);
private final Logger logger;
private final SessionManager sessionManager;
private FileOutputStream outputStream;
private String sessionKey;

SocketDataReaderWorker(FileOutputStream outputStream, String sessionKey) {
SocketDataReaderWorker(FileOutputStream outputStream, String sessionKey, SessionManager sessionManager) {
this.logger = LoggerFactory.getLogger(SocketDataReaderWorker.class);
this.outputStream = outputStream;
this.sessionKey = sessionKey;
this.sessionManager = sessionManager;
}

@Override
public void run() {
Session session = SessionManager.INSTANCE.getSessionByKey(sessionKey);
Session session = sessionManager.getSessionByKey(sessionKey);
if (session == null) {
logger.error("Session NOT FOUND: " + sessionKey);
return;
Expand Down Expand Up @@ -82,7 +85,7 @@ public void run() {
return;
}

SessionManager.INSTANCE.closeSession(session);
sessionManager.closeSession(session);
} else {
session.setBusyRead(false);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,18 +16,22 @@
* TCP connection, writes an RST to the VPN stream to reset the connection.
*/
public class SocketDataWriterWorker implements Runnable {
private final Logger logger = LoggerFactory.getLogger(SocketDataWriterWorker.class);
private final Logger logger;
private FileOutputStream outputStream; // really only used for sending RST packet on TCP
private String sessionKey;
private SessionManager sessionManager;

SocketDataWriterWorker(FileOutputStream outputStream, String sessionKey) {
SocketDataWriterWorker(FileOutputStream outputStream, String sessionKey,
SessionManager sessionManager) {
this.logger = LoggerFactory.getLogger(SocketDataWriterWorker.class);
this.outputStream = outputStream;
this.sessionKey = sessionKey;
this.sessionManager = sessionManager;
}

@Override
public void run() {
final Session session = SessionManager.INSTANCE.getSessionByKey(sessionKey);
final Session session = sessionManager.getSessionByKey(sessionKey);
if (session == null) {
logger.error("No session related to " + sessionKey + "for write");
return;
Expand Down Expand Up @@ -73,7 +77,7 @@ public void run() {
return;
}

SessionManager.INSTANCE.closeSession(session);
sessionManager.closeSession(session);
}
}

Expand Down
35 changes: 5 additions & 30 deletions app/src/main/java/network/grape/service/SocketProtector.java
Original file line number Diff line number Diff line change
Expand Up @@ -4,39 +4,14 @@
import java.net.Socket;

/**
* Singleton class which gives access to socket protection from the GrapeVpnService more widely. The
* GrapeVpnService sets itself as the socket protector, and then every class using this singleton
* can protect sockets.
* Gives access to socket protection from the GrapeVpnService more widely. The GrapeVpnService sets
* itself as the socket protector, and then every class using this singleton can protect sockets.
*/
public class SocketProtector {
private static final Object synObject = new Object();
private static volatile SocketProtector instance = null;
private ProtectSocket protector = null;
private final ProtectSocket protector;

/**
* Provides a socket protector to any class which requires one.
* @return a singleton instance of the socket protector
*/
public static SocketProtector getInstance() {
if (instance == null) {
synchronized (synObject) {
if (instance == null) {
instance = new SocketProtector();
}
}
}
return instance;
}

/**
* set class that implement IProtectSocket if only if it was never set before.
*
* @param protector ProtectSocket
*/
public void setProtector(ProtectSocket protector) {
if (this.protector == null) {
this.protector = protector;
}
public SocketProtector(ProtectSocket protector) {
this.protector = protector;
}

public void protect(Socket socket) {
Expand Down
47 changes: 25 additions & 22 deletions app/src/main/java/network/grape/service/VpnWriter.java
Original file line number Diff line number Diff line change
Expand Up @@ -26,43 +26,46 @@
*/
public class VpnWriter implements Runnable {

private final Logger logger = LoggerFactory.getLogger(VpnWriter.class);
private final Logger logger;
public static final Object syncSelector = new Object();
public static final Object syncSelector2 = new Object();
private FileOutputStream outputStream;
private final FileOutputStream outputStream;
private final SessionManager sessionManager;
private Selector selector;
//create thread pool for reading/writing data to socket
private ThreadPoolExecutor workerPool;
private volatile boolean running;

/**
* Construct a new VpnWriter.
* @param outputStream the stream to write back into the VPN interface.
*/
public VpnWriter(FileOutputStream outputStream) {
this.outputStream = outputStream;
final BlockingQueue<Runnable> taskQueue = new LinkedBlockingQueue<>();
workerPool = new ThreadPoolExecutor(8, 100, 10, TimeUnit.SECONDS, taskQueue);
}

/**
* Construct a new VpnWriter with the workerpool provided.
*
* @param outputStream the stream to write back into the VPN interface.
* @param workerPool the worker pool to execute reader and writer threads in.
* @param workerPool the worker pool to execute reader and writer threads in.
*/
public VpnWriter(FileOutputStream outputStream, ThreadPoolExecutor workerPool) {
public VpnWriter(FileOutputStream outputStream, SessionManager sessionManager,
ThreadPoolExecutor workerPool) {
this.logger = LoggerFactory.getLogger(VpnWriter.class);
this.outputStream = outputStream;
this.sessionManager = sessionManager;
this.workerPool = workerPool;
}

boolean isRunning() {
return running;
}

boolean notRunning() {
return !running;
}

/**
* Main thread for the VpnWriter.
*/
public void run() {
logger.info("VpnWriter starting in the background");
selector = SessionManager.INSTANCE.getSelector();
selector = sessionManager.getSelector();
running = true;
while (running) {
while (isRunning()) {

// first just try to wait for a socket to be ready for a connect, read, etc
try {
Expand All @@ -79,7 +82,7 @@ public void run() {
continue;
}

if (!running) {
if (notRunning()) {
break;
}

Expand All @@ -95,7 +98,7 @@ public void run() {
processUdpSelectionKey(key);
}
iterator.remove();
if (!running) {
if (notRunning()) {
break;
}
}
Expand All @@ -109,7 +112,7 @@ protected void processUdpSelectionKey(SelectionKey key) {
return;
}
DatagramChannel channel = (DatagramChannel) key.channel();
Session session = SessionManager.INSTANCE.getSessionByChannel(channel);
Session session = sessionManager.getSessionByChannel(channel);
String keyString = channel.socket().getLocalAddress().toString() + ":"
+ channel.socket().getLocalPort() + ","
+ channel.socket().getInetAddress().toString() + ":"
Expand Down Expand Up @@ -153,21 +156,21 @@ protected void processUdpSelectionKey(SelectionKey key) {
* Generic selector handling for both TCP and UDP sessions.
*
* @param selectionKey the key in the selection set which is marked for reading or writing.
* @param session the session associated with the selection key.
* @param session the session associated with the selection key.
*/
protected void processSelector(SelectionKey selectionKey, Session session) {
// tcp has PSH flag when data is ready for sending, UDP does not have this
if (selectionKey.isValid() && selectionKey.isWritable() && !session.isBusyWrite()
&& session.hasDataToSend() && session.isDataForSendingReady()) {
session.setBusyWrite(true);
final SocketDataWriterWorker worker =
new SocketDataWriterWorker(outputStream, session.getKey());
new SocketDataWriterWorker(outputStream, session.getKey(), sessionManager);
workerPool.execute(worker);
}
if (selectionKey.isValid() && selectionKey.isReadable() && !session.isBusyRead()) {
session.setBusyRead(true);
final SocketDataReaderWorker worker =
new SocketDataReaderWorker(outputStream, session.getKey());
new SocketDataReaderWorker(outputStream, session.getKey(), sessionManager);
workerPool.execute(worker);
}
}
Expand Down
Loading