diff --git a/app/src/main/java/com/emanuelef/remote_capture/CaptureService.java b/app/src/main/java/com/emanuelef/remote_capture/CaptureService.java index 97f7391b..db1dbda6 100644 --- a/app/src/main/java/com/emanuelef/remote_capture/CaptureService.java +++ b/app/src/main/java/com/emanuelef/remote_capture/CaptureService.java @@ -58,12 +58,15 @@ import com.emanuelef.remote_capture.model.ConnectionDescriptor; import com.emanuelef.remote_capture.model.ConnectionUpdate; import com.emanuelef.remote_capture.model.Prefs; import com.emanuelef.remote_capture.model.VPNStats; +import com.emanuelef.remote_capture.pcap_dump.FileDumper; +import com.emanuelef.remote_capture.pcap_dump.HTTPServer; +import com.emanuelef.remote_capture.interfaces.PcapDumper; +import com.emanuelef.remote_capture.pcap_dump.UDPDumper; -import java.io.FileNotFoundException; import java.io.IOException; -import java.io.OutputStream; import java.net.InetAddress; import java.net.InetSocketAddress; +import java.net.Socket; import java.net.UnknownHostException; public class CaptureService extends VpnService implements Runnable { @@ -91,11 +94,9 @@ public class CaptureService extends VpnService implements Runnable { private static CaptureService INSTANCE; private String app_filter; private int app_filter_uid; - private HTTPServer mHttpServer; - private OutputStream mOutputStream; + private PcapDumper mDumper; private ConnectionsRegister conn_reg; private Uri mPcapUri; - private boolean mFirstStreamWrite; private NotificationCompat.Builder mNotificationBuilder; private long mMonitoredNetwork; private ConnectivityManager.NetworkCallback mNetworkCallback; @@ -139,15 +140,23 @@ public class CaptureService extends VpnService implements Runnable { super.onCreate(); } + private int abortStart() { + // NOTE: startForeground must be called before stopSelf, otherwise an exception will occur + setupNotifications(); + startForeground(NOTIFY_ID_VPNSERVICE, getNotification()); + + stopSelf(); + sendServiceStatus(SERVICE_STATUS_STOPPED); + return START_STICKY; + } + @Override public int onStartCommand(Intent intent, int flags, int startId) { - Context app_ctx = getApplicationContext(); - mHandler = new Handler(Looper.getMainLooper()); if (intent == null) { Log.d(CaptureService.TAG, "NULL intent onStartCommand"); - return super.onStartCommand(null, flags, startId); + return abortStart(); } Log.d(CaptureService.TAG, "onStartCommand"); @@ -157,7 +166,7 @@ public class CaptureService extends VpnService implements Runnable { if (settings == null) { Log.e(CaptureService.TAG, "NULL settings"); - return super.onStartCommand(null, flags, startId); + return abortStart(); } // Retrieve DNS server @@ -198,40 +207,41 @@ public class CaptureService extends VpnService implements Runnable { last_connections = 0; root_capture = Prefs.isRootCaptureEnabled(prefs); conn_reg = new ConnectionsRegister(CONNECTIONS_LOG_SIZE); - - if(dump_mode != Prefs.DumpMode.HTTP_SERVER) - mHttpServer = null; - mOutputStream = null; mPcapUri = null; + mDumper = null; - if(dump_mode == Prefs.DumpMode.HTTP_SERVER) { - if (mHttpServer == null) - mHttpServer = new HTTPServer(app_ctx, http_server_port); - - try { - mHttpServer.startConnections(); - } catch (IOException e) { - Log.e(CaptureService.TAG, "Could not start the HTTP server"); - e.printStackTrace(); - } - } else if(dump_mode == Prefs.DumpMode.PCAP_FILE) { + // Possibly allocate the dumper + if(dump_mode == Prefs.DumpMode.HTTP_SERVER) + mDumper = new HTTPServer(this, http_server_port); + else if(dump_mode == Prefs.DumpMode.PCAP_FILE) { String path = settings.getString(Prefs.PREF_PCAP_URI); if(path != null) { mPcapUri = Uri.parse(path); + mDumper = new FileDumper(this, mPcapUri); + } + } else if(dump_mode == Prefs.DumpMode.UDP_EXPORTER) { + InetAddress addr; - try { - Log.d(TAG, "PCAP URI: " + mPcapUri); - mOutputStream = getContentResolver().openOutputStream(mPcapUri); - mFirstStreamWrite = true; - } catch (FileNotFoundException e) { - e.printStackTrace(); - } + try { + addr = InetAddress.getByName(Prefs.getCollectorIp(prefs)); + } catch (UnknownHostException e) { + reportError(e.getLocalizedMessage()); + e.printStackTrace(); + return abortStart(); } - if(mOutputStream == null) { - Utils.showToast(this, R.string.cannot_write_pcap_file); - return super.onStartCommand(intent, flags, startId); + mDumper = new UDPDumper(new InetSocketAddress(addr, Prefs.getCollectorPort(prefs))); + } + + if(mDumper != null) { + try { + mDumper.startDumper(); + } catch (IOException e) { + reportError(e.getLocalizedMessage()); + e.printStackTrace(); + mDumper = null; + return abortStart(); } } @@ -281,7 +291,7 @@ public class CaptureService extends VpnService implements Runnable { } catch (PackageManager.NameNotFoundException e) { String msg = String.format(getResources().getString(R.string.app_not_found), app_filter); Toast.makeText(this, msg, Toast.LENGTH_SHORT).show(); - return super.onStartCommand(intent, flags, startId); + return abortStart(); } } @@ -289,7 +299,7 @@ public class CaptureService extends VpnService implements Runnable { mParcelFileDescriptor = builder.setSession(CaptureService.VpnSessionName).establish(); } catch (IllegalArgumentException | IllegalStateException e) { Utils.showToast(this, R.string.vpn_setup_failed); - return super.onStartCommand(intent, flags, startId); + return abortStart(); } } @@ -304,9 +314,7 @@ public class CaptureService extends VpnService implements Runnable { setupNotifications(); startForeground(NOTIFY_ID_VPNSERVICE, getNotification()); - return START_STICKY; - //return super.onStartCommand(intent, flags, startId); } @Override @@ -326,8 +334,14 @@ public class CaptureService extends VpnService implements Runnable { if(mThread != null) { mThread.interrupt(); } - if(mHttpServer != null) - mHttpServer.stop(); + if(mDumper != null) { + try { + mDumper.stopDumper(); + } catch (IOException e) { + e.printStackTrace(); + } + mDumper = null; + } super.onDestroy(); } @@ -453,16 +467,13 @@ public class CaptureService extends VpnService implements Runnable { mParcelFileDescriptor = null; } - if(mHttpServer != null) - mHttpServer.endConnections(); - // NOTE: do not destroy the mHttpServer, let it terminate the active connections - - if(mOutputStream != null) { + if(mDumper != null) { try { - mOutputStream.close(); + mDumper.stopDumper(); } catch (IOException e) { e.printStackTrace(); } + mDumper = null; } mPcapUri = null; @@ -520,6 +531,12 @@ public class CaptureService extends VpnService implements Runnable { INSTANCE.stop(); } + public static @NonNull CaptureService requireInstance() { + CaptureService inst = INSTANCE; + assert(inst != null); + return(inst); + } + public static @Nullable ConnectionsRegister getConnsRegister() { return((INSTANCE != null) ? INSTANCE.conn_reg : null); } @@ -574,14 +591,6 @@ public class CaptureService extends VpnService implements Runnable { public String getIpv6DnsServer() { return(IPV6_DNS_SERVER); } - public String getPcapCollectorAddress() { - return(collector_address); - } - - public int getPcapCollectorPort() { - return(collector_port); - } - public String getSocks5ProxyAddress() { return(socks5_proxy_address); } public int getSocks5Enabled() { return socks5_enabled ? 1 : 0; } @@ -604,12 +613,17 @@ public class CaptureService extends VpnService implements Runnable { } // returns 1 if dumpPcapData should be called - public int dumpPcapToJava() { - return(((dump_mode == Prefs.DumpMode.HTTP_SERVER) || (dump_mode == Prefs.DumpMode.PCAP_FILE)) ? 1 : 0); + public int pcapDumpEnabled() { + return((dump_mode != Prefs.DumpMode.NONE) ? 1 : 0); } - public int dumpPcapToUdp() { - return((dump_mode == Prefs.DumpMode.UDP_EXPORTER) ? 1 : 0); + @Override + public boolean protect(int socket) { + // Do not call protect in root mode + if(root_capture) + return true; + + return super.protect(socket); } // from NetGuard @@ -668,16 +682,9 @@ public class CaptureService extends VpnService implements Runnable { /* Exports a PCAP data chunk */ public void dumpPcapData(byte[] data) { - if(mHttpServer != null) - mHttpServer.pushData(data); - else if(mOutputStream != null) { + if(mDumper != null) { try { - if(mFirstStreamWrite) { - mOutputStream.write(Utils.hexStringToByteArray(Utils.PCAP_HEADER)); - mFirstStreamWrite = false; - } - - mOutputStream.write(data); + mDumper.dumpData(data); } catch (IOException e) { e.printStackTrace(); reportError(e.getLocalizedMessage()); diff --git a/app/src/main/java/com/emanuelef/remote_capture/Utils.java b/app/src/main/java/com/emanuelef/remote_capture/Utils.java index 15619a4c..b767ce7e 100644 --- a/app/src/main/java/com/emanuelef/remote_capture/Utils.java +++ b/app/src/main/java/com/emanuelef/remote_capture/Utils.java @@ -75,17 +75,20 @@ import java.net.Inet4Address; import java.net.InetAddress; import java.net.NetworkInterface; import java.net.UnknownHostException; +import java.nio.ByteBuffer; import java.nio.ByteOrder; import java.text.DateFormat; import java.text.SimpleDateFormat; +import java.util.Arrays; import java.util.Calendar; import java.util.Collections; import java.util.Date; +import java.util.Iterator; import java.util.List; import java.util.Locale; public class Utils { - public static final String PCAP_HEADER = "d4c3b2a1020004000000000000000000ffff000065000000"; + public static final byte[] PCAP_HEADER = Utils.hexStringToByteArray("d4c3b2a1020004000000000000000000ffff000065000000"); public static final int UID_UNKNOWN = -1; public static final int UID_NO_FILTER = -2; private static Boolean rootAvailable = null; @@ -318,6 +321,28 @@ public class Utils { return data; } + // Splits the provided data into individual PCAP records. Intended to be used with data received + // via CaptureService::dumpPcapData + public static Iterator iterPcapRecords(byte[] data) { + final ByteBuffer buf = ByteBuffer.wrap(data); + buf.order(ByteOrder.nativeOrder()); + + return new Iterator() { + @Override + public boolean hasNext() { + // 16: sizeof(pcaprec_hdr_s) + return(buf.remaining() > 16); + } + + @Override + public Integer next() { + int rec_len = buf.getInt(buf.position() + 8) + 16; + buf.position(buf.position() + rec_len); + return rec_len; + } + }; + } + public static boolean hasVPNRunning(Context context) { ConnectivityManager cm = (ConnectivityManager) context.getSystemService(Context.CONNECTIVITY_SERVICE); diff --git a/app/src/main/java/com/emanuelef/remote_capture/interfaces/PcapDumper.java b/app/src/main/java/com/emanuelef/remote_capture/interfaces/PcapDumper.java new file mode 100644 index 00000000..0f1832c7 --- /dev/null +++ b/app/src/main/java/com/emanuelef/remote_capture/interfaces/PcapDumper.java @@ -0,0 +1,29 @@ +package com.emanuelef.remote_capture.interfaces; + +import java.io.IOException; + +/** A dumper implements the ability to dump PCAP data. + * It has the following lifecycle: + * + * startDumper -> ... dumpData ... -> stopDumper + */ +public interface PcapDumper { + /** + * Starts the dumper. + * @throws IOException + */ + void startDumper() throws IOException; + + /** + * Terminates the dumper. + * @throws IOException + */ + void stopDumper() throws IOException; + + /** + * Dump an unspecified number of PCAP records. The dumper must check if this is the first data + * sent, in which case it should send the Utils.PCAP_HEADER bofore the PCAP records data. + * @throws IOException + */ + void dumpData(byte[] data) throws IOException; +} diff --git a/app/src/main/java/com/emanuelef/remote_capture/ChunkedInputStream.java b/app/src/main/java/com/emanuelef/remote_capture/pcap_dump/ChunkedInputStream.java similarity index 94% rename from app/src/main/java/com/emanuelef/remote_capture/ChunkedInputStream.java rename to app/src/main/java/com/emanuelef/remote_capture/pcap_dump/ChunkedInputStream.java index 3fa1cc40..fe2f3325 100644 --- a/app/src/main/java/com/emanuelef/remote_capture/ChunkedInputStream.java +++ b/app/src/main/java/com/emanuelef/remote_capture/pcap_dump/ChunkedInputStream.java @@ -17,7 +17,9 @@ * Copyright 2020-21 - Emanuele Faranda */ -package com.emanuelef.remote_capture; +package com.emanuelef.remote_capture.pcap_dump; + +import com.emanuelef.remote_capture.Utils; import java.io.InputStream; import java.util.ArrayList; @@ -29,17 +31,16 @@ import java.util.concurrent.locks.ReentrantLock; asynchronously via produceData(). bytes[] chunks are used instead of a single bytes[] in order to avoid excessive data copies. */ -class ChunkedInputStream extends InputStream { - private static final byte[] pcapHeader = Utils.hexStringToByteArray(Utils.PCAP_HEADER); +public class ChunkedInputStream extends InputStream { final Lock mLock = new ReentrantLock(); final Condition newData = mLock.newCondition(); ArrayList mChunks = new ArrayList(); int mCurChunkIndex = 0; boolean hasFinished = false; - ChunkedInputStream() { + public ChunkedInputStream() { // Send the PCAP header as the first chunk - mChunks.add(pcapHeader); + mChunks.add(Utils.PCAP_HEADER); } /* Mark the termination of stream */ diff --git a/app/src/main/java/com/emanuelef/remote_capture/pcap_dump/FileDumper.java b/app/src/main/java/com/emanuelef/remote_capture/pcap_dump/FileDumper.java new file mode 100644 index 00000000..78eb95b5 --- /dev/null +++ b/app/src/main/java/com/emanuelef/remote_capture/pcap_dump/FileDumper.java @@ -0,0 +1,46 @@ +package com.emanuelef.remote_capture.pcap_dump; + +import android.content.Context; +import android.net.Uri; +import android.util.Log; + +import com.emanuelef.remote_capture.Utils; +import com.emanuelef.remote_capture.interfaces.PcapDumper; + +import java.io.IOException; +import java.io.OutputStream; + +public class FileDumper implements PcapDumper { + public static final String TAG = "FileDumper"; + private final Context mContext; + private final Uri mPcapUri; + private boolean mSendHeader; + private OutputStream mOutputStream; + + public FileDumper(Context ctx, Uri pcap_uri) { + mContext = ctx; + mPcapUri = pcap_uri; + mSendHeader = true; + } + + @Override + public void startDumper() throws IOException { + Log.d(TAG, "PCAP URI: " + mPcapUri); + mOutputStream = mContext.getContentResolver().openOutputStream(mPcapUri); + } + + @Override + public void stopDumper() throws IOException { + mOutputStream.close(); + } + + @Override + public void dumpData(byte[] data) throws IOException { + if(mSendHeader) { + mSendHeader = false; + mOutputStream.write(Utils.PCAP_HEADER); + } + + mOutputStream.write(data); + } +} diff --git a/app/src/main/java/com/emanuelef/remote_capture/HTTPServer.java b/app/src/main/java/com/emanuelef/remote_capture/pcap_dump/HTTPServer.java similarity index 62% rename from app/src/main/java/com/emanuelef/remote_capture/HTTPServer.java rename to app/src/main/java/com/emanuelef/remote_capture/pcap_dump/HTTPServer.java index 960ad82d..ac18b734 100644 --- a/app/src/main/java/com/emanuelef/remote_capture/HTTPServer.java +++ b/app/src/main/java/com/emanuelef/remote_capture/pcap_dump/HTTPServer.java @@ -17,19 +17,22 @@ * Copyright 2020-21 - Emanuele Faranda */ -package com.emanuelef.remote_capture; +package com.emanuelef.remote_capture.pcap_dump; import android.content.Context; +import com.emanuelef.remote_capture.R; +import com.emanuelef.remote_capture.Utils; +import com.emanuelef.remote_capture.interfaces.PcapDumper; + import java.io.IOException; import java.util.ArrayList; import fi.iki.elonen.NanoHTTPD; import fi.iki.elonen.NanoHTTPD.Response.Status; -public class HTTPServer extends NanoHTTPD { +public class HTTPServer extends NanoHTTPD implements PcapDumper { private static final String PCAP_MIME = "application/vnd.tcpdump.pcap"; - private boolean firstStart = true; private boolean mAcceptConnections = false; private final Context mContext; @@ -58,54 +61,6 @@ public class HTTPServer extends NanoHTTPD { return res; } - @Override - public void stop() { - super.stop(); - firstStart = true; - } - - public void startConnections() throws IOException { - mAcceptConnections = true; - - if(firstStart) { - start(); - firstStart = false; - } - } - - /* Marks data end on all the active connections */ - public synchronized void endConnections() { - for(int i=mActiveResponses.size()-1; i >= 0; i--) { - Response res = mActiveResponses.get(i); - - if(res.isCloseConnection()) { - /* Cleanup closed connections */ - mActiveResponses.remove(i); - continue; - } - - ((ChunkedInputStream) res.getData()).stop(); - } - - mActiveResponses.clear(); - mAcceptConnections = false; - } - - /* Dispatch PCAP data to the active connections */ - public synchronized void pushData(byte[] data) { - for(int i=mActiveResponses.size()-1; i >= 0; i--) { - Response res = mActiveResponses.get(i); - - if(res.isCloseConnection()) { - /* Cleanup closed connections */ - mActiveResponses.remove(i); - continue; - } - - ((ChunkedInputStream) res.getData()).produceData(data); - } - } - @Override public Response serve(IHTTPSession session) { if(!mAcceptConnections) @@ -119,4 +74,50 @@ public class HTTPServer extends NanoHTTPD { return newPcapStream(); } + + @Override + public void startDumper() throws IOException { + mAcceptConnections = true; + start(); + } + + @Override + public void stopDumper() throws IOException { + synchronized (this) { + for (int i = mActiveResponses.size() - 1; i >= 0; i--) { + Response res = mActiveResponses.get(i); + + if (res.isCloseConnection()) { + /* Cleanup closed connections */ + mActiveResponses.remove(i); + continue; + } + + ((ChunkedInputStream) res.getData()).stop(); + } + + mActiveResponses.clear(); + mAcceptConnections = false; + } + + stop(); + } + + @Override + public void dumpData(byte[] data) throws IOException { + synchronized (this) { + /* Dispatch PCAP data to the active connections */ + for (int i = mActiveResponses.size() - 1; i >= 0; i--) { + Response res = mActiveResponses.get(i); + + if (res.isCloseConnection()) { + /* Cleanup closed connections */ + mActiveResponses.remove(i); + continue; + } + + ((ChunkedInputStream) res.getData()).produceData(data); + } + } + } } diff --git a/app/src/main/java/com/emanuelef/remote_capture/pcap_dump/UDPDumper.java b/app/src/main/java/com/emanuelef/remote_capture/pcap_dump/UDPDumper.java new file mode 100644 index 00000000..ce784323 --- /dev/null +++ b/app/src/main/java/com/emanuelef/remote_capture/pcap_dump/UDPDumper.java @@ -0,0 +1,56 @@ +package com.emanuelef.remote_capture.pcap_dump; + +import com.emanuelef.remote_capture.CaptureService; +import com.emanuelef.remote_capture.Utils; +import com.emanuelef.remote_capture.interfaces.PcapDumper; + +import java.io.IOException; +import java.net.DatagramPacket; +import java.net.DatagramSocket; +import java.net.SocketAddress; +import java.util.Iterator; + +public class UDPDumper implements PcapDumper { + public static final String TAG = "UDPDumper"; + private final SocketAddress mServer; + private boolean mSendHeader; + private DatagramSocket mSocket; + + public UDPDumper(SocketAddress server) { + mServer = server; + mSendHeader = true; + } + + @Override + public void startDumper() throws IOException { + mSocket = new DatagramSocket(); + CaptureService.requireInstance().protect(mSocket); + } + + @Override + public void stopDumper() throws IOException { + mSocket.close(); + } + + private void sendDatagram(byte[] data, int offset, int len) throws IOException { + DatagramPacket request = new DatagramPacket(data, offset, len, mServer); + mSocket.send(request); + } + + @Override + public void dumpData(byte[] data) throws IOException { + if(mSendHeader) { + mSendHeader = false; + sendDatagram(Utils.PCAP_HEADER, 0, Utils.PCAP_HEADER.length); + } + + Iterator it = Utils.iterPcapRecords(data); + int pos = 0; + + while(it.hasNext()) { + int rec_len = it.next(); + sendDatagram(data, pos, rec_len); + pos += rec_len; + } + } +} diff --git a/app/src/main/jni/vpnproxy-jni/pcap_utils.c b/app/src/main/jni/vpnproxy-jni/pcap_utils.c index 3eeaa918..c54ed1a0 100644 --- a/app/src/main/jni/vpnproxy-jni/pcap_utils.c +++ b/app/src/main/jni/vpnproxy-jni/pcap_utils.c @@ -83,20 +83,6 @@ void write_pcap_hdr(int fd, const struct sockaddr *srv, int srv_size) { /* ******************************************************* */ -void write_pcap_rec(int fd, const struct sockaddr *srv, int srv_size, const uint8_t *buffer, int length) { - struct pcaprec_hdr_s *pcap_rec = (struct pcaprec_hdr_s *) pcap_buffer; - - size_t incl_len = init_pcap_rec_hdr(pcap_rec, length); - size_t tot_len = sizeof(struct pcaprec_hdr_s) + incl_len; - - // NOTE: use incl_size as the packet may be cut due to the SNAPLEN - memcpy(pcap_buffer + sizeof(struct pcaprec_hdr_s), buffer, incl_len); - - write_pcap(fd, srv, srv_size, pcap_rec, tot_len); -} - -/* ******************************************************* */ - size_t dump_pcap_rec(u_char *buffer, const u_char *pkt, int pkt_len) { struct pcaprec_hdr_s *pcap_rec = (pcaprec_hdr_s*) buffer; diff --git a/app/src/main/jni/vpnproxy-jni/pcap_utils.h b/app/src/main/jni/vpnproxy-jni/pcap_utils.h index d7438a43..657eb42d 100644 --- a/app/src/main/jni/vpnproxy-jni/pcap_utils.h +++ b/app/src/main/jni/vpnproxy-jni/pcap_utils.h @@ -42,7 +42,6 @@ typedef struct pcaprec_hdr_s { } __packed pcaprec_hdr_s; void write_pcap_hdr(int fd, const struct sockaddr *srv, int srv_size); -void write_pcap_rec(int fd, const struct sockaddr *srv, int srv_size, const uint8_t *buffer, int length); size_t dump_pcap_rec(u_char *buffer, const u_char *pkt, int pkt_len); #endif // __MY_PCAP_H__ diff --git a/app/src/main/jni/vpnproxy-jni/vpnproxy.c b/app/src/main/jni/vpnproxy-jni/vpnproxy.c index 8733a258..836c6c16 100644 --- a/app/src/main/jni/vpnproxy-jni/vpnproxy.c +++ b/app/src/main/jni/vpnproxy-jni/vpnproxy.c @@ -36,8 +36,6 @@ static ndpi_protocol_bitmask_struct_t masterProtos; /* ******************************************************* */ /* NOTE: these must be reset during each run, as android may reuse the service */ -static int dumper_socket; -static bool send_header; static int netd_resolve_waiting; static u_int64_t last_connections_dump; static u_int64_t next_connections_dump; @@ -561,18 +559,18 @@ static void process_ndpi_packet(conn_data_t *data, vpnproxy_data_t *proxy, static void javaPcapDump(vpnproxy_data_t *proxy) { JNIEnv *env = proxy->env; - log_d("Exporting a %d B PCAP buffer", proxy->java_dump.buffer_idx); + log_d("Exporting a %d B PCAP buffer", proxy->pcap_dump.buffer_idx); - jbyteArray barray = (*env)->NewByteArray(env, proxy->java_dump.buffer_idx); + jbyteArray barray = (*env)->NewByteArray(env, proxy->pcap_dump.buffer_idx); if(jniCheckException(env)) return; - (*env)->SetByteArrayRegion(env, barray, 0, proxy->java_dump.buffer_idx, proxy->java_dump.buffer); + (*env)->SetByteArrayRegion(env, barray, 0, proxy->pcap_dump.buffer_idx, proxy->pcap_dump.buffer); (*env)->CallVoidMethod(env, proxy->vpn_service, mids.dumpPcapData, barray); jniCheckException(env); - proxy->java_dump.buffer_idx = 0; - proxy->java_dump.last_dump_ms = proxy->now_ms; + proxy->pcap_dump.buffer_idx = 0; + proxy->pcap_dump.last_dump_ms = proxy->now_ms; (*env)->DeleteLocalRef(env, barray); } @@ -840,36 +838,6 @@ void vpn_protect_socket(vpnproxy_data_t *proxy, socket_t sock) { /* ******************************************************* */ -static int connect_dumper(vpnproxy_data_t *proxy) { - if(proxy->pcap_dump.enabled) { - dumper_socket = socket(AF_INET, proxy->pcap_dump.tcp_socket ? SOCK_STREAM : SOCK_DGRAM, 0); - - if (!dumper_socket) { - log_f("could not open UDP pcap dump socket [%d]: %s", errno, strerror(errno)); - return(-1); - } - - vpn_protect_socket(proxy, dumper_socket); - - if(proxy->pcap_dump.tcp_socket) { - struct sockaddr_in servaddr = {0}; - servaddr.sin_family = AF_INET; - servaddr.sin_port = proxy->pcap_dump.collector_port; - servaddr.sin_addr.s_addr = proxy->pcap_dump.collector_addr; - - if(connect(dumper_socket, (struct sockaddr*)&servaddr, sizeof(servaddr)) < 0) { - log_f("connection to the PCAP receiver failed [%d]: %s", errno, - strerror(errno)); - return(-2); - } - } - } - - return(0); -} - -/* ******************************************************* */ - void run_housekeeping(vpnproxy_data_t *proxy) { if(proxy->capture_stats.new_stats && ((proxy->now_ms - proxy->capture_stats.last_update_ms) >= CAPTURE_STATS_UPDATE_FREQUENCY_MS) || @@ -888,8 +856,8 @@ void run_housekeeping(vpnproxy_data_t *proxy) { last_connections_dump = proxy->now_ms; next_connections_dump = proxy->now_ms + CONNECTION_DUMP_UPDATE_FREQUENCY_MS; netd_resolve_waiting = 0; - } else if ((proxy->java_dump.buffer_idx > 0) - && (proxy->now_ms - proxy->java_dump.last_dump_ms) >= MAX_JAVA_DUMP_DELAY_MS) { + } else if ((proxy->pcap_dump.buffer_idx > 0) + && (proxy->now_ms - proxy->pcap_dump.last_dump_ms) >= MAX_JAVA_DUMP_DELAY_MS) { javaPcapDump(proxy); } } @@ -970,37 +938,22 @@ void account_packet(vpnproxy_data_t *proxy, const zdtun_pkt_t *pkt, uint8_t from data->update_type |= CONN_UPDATE_STATS; notify_connection(&proxy->conns_updates, conn_tuple, data); - if (proxy->java_dump.buffer) { + if (proxy->pcap_dump.buffer) { int tot_size = pkt->len + (int) sizeof(pcaprec_hdr_s); - if ((JAVA_PCAP_BUFFER_SIZE - proxy->java_dump.buffer_idx) <= tot_size) { + if ((JAVA_PCAP_BUFFER_SIZE - proxy->pcap_dump.buffer_idx) <= tot_size) { // Flush the buffer javaPcapDump(proxy); } - if ((JAVA_PCAP_BUFFER_SIZE - proxy->java_dump.buffer_idx) <= tot_size) + if ((JAVA_PCAP_BUFFER_SIZE - proxy->pcap_dump.buffer_idx) <= tot_size) log_e("Invalid buffer size [size=%d, idx=%d, tot_size=%d]", - JAVA_PCAP_BUFFER_SIZE, proxy->java_dump.buffer_idx, tot_size); + JAVA_PCAP_BUFFER_SIZE, proxy->pcap_dump.buffer_idx, tot_size); else - proxy->java_dump.buffer_idx += dump_pcap_rec( - (u_char *) proxy->java_dump.buffer + proxy->java_dump.buffer_idx, + proxy->pcap_dump.buffer_idx += dump_pcap_rec( + (u_char *) proxy->pcap_dump.buffer + proxy->pcap_dump.buffer_idx, (u_char *) pkt->buf, pkt->len); } - - if (dumper_socket > 0) { - struct sockaddr_in servaddr = {0}; - servaddr.sin_family = AF_INET; - servaddr.sin_port = proxy->pcap_dump.collector_port; - servaddr.sin_addr.s_addr = proxy->pcap_dump.collector_addr; - - if(send_header) { - write_pcap_hdr(dumper_socket, (struct sockaddr *) &servaddr, sizeof(servaddr)); - send_header = false; - } - - write_pcap_rec(dumper_socket, (struct sockaddr *) &servaddr, sizeof(servaddr), - (u_int8_t *) pkt->buf, pkt->len); - } } /* ******************************************************* */ @@ -1048,14 +1001,8 @@ static int run_tun(JNIEnv *env, jclass vpn, int tunfd, jint sdk) { .app_filter = getIntPref(env, vpn, "getAppFilterUid"), .root_capture = (bool) getIntPref(env, vpn, "isRootCapture"), .incr_id = 0, - .java_dump = { - .enabled = (bool) getIntPref(env, vpn, "dumpPcapToJava"), - }, .pcap_dump = { - .collector_addr = getIPv4Pref(env, vpn, "getPcapCollectorAddress"), - .collector_port = htons(getIntPref(env, vpn, "getPcapCollectorPort")), - .tcp_socket = false, - .enabled = (bool) getIntPref(env, vpn, "dumpPcapToUdp"), + .enabled = (bool) getIntPref(env, vpn, "pcapDumpEnabled"), }, .socks5 = { .enabled = (bool) getIntPref(env, vpn, "getSocks5Enabled"), @@ -1069,8 +1016,6 @@ static int run_tun(JNIEnv *env, jclass vpn, int tunfd, jint sdk) { }; /* Important: init global state every time. Android may reuse the service. */ - dumper_socket = -1; - send_header = true; running = true; logcallback = log_callback; @@ -1088,16 +1033,11 @@ static int run_tun(JNIEnv *env, jclass vpn, int tunfd, jint sdk) { signal(SIGPIPE, SIG_IGN); if(proxy.pcap_dump.enabled) { - if(connect_dumper(&proxy) < 0) - running = false; - } + proxy.pcap_dump.buffer = malloc(JAVA_PCAP_BUFFER_SIZE); + proxy.pcap_dump.buffer_idx = 0; - if(proxy.java_dump.enabled) { - proxy.java_dump.buffer = malloc(JAVA_PCAP_BUFFER_SIZE); - proxy.java_dump.buffer_idx = 0; - - if(!proxy.java_dump.buffer) { - log_f("malloc(java_dump.buffer) failed with code %d/%s", + if(!proxy.pcap_dump.buffer) { + log_f("malloc(pcap_dump.buffer) failed with code %d/%s", errno, strerror(errno)); running = false; } @@ -1117,17 +1057,12 @@ static int run_tun(JNIEnv *env, jclass vpn, int tunfd, jint sdk) { ndpi_exit_detection_module(proxy.ndpi); - if(dumper_socket > 0) { - close(dumper_socket); - dumper_socket = -1; - } - - if(proxy.java_dump.buffer) { - if(proxy.java_dump.buffer_idx > 0) + if(proxy.pcap_dump.buffer) { + if(proxy.pcap_dump.buffer_idx > 0) javaPcapDump(&proxy); - free(proxy.java_dump.buffer); - proxy.java_dump.buffer = NULL; + free(proxy.pcap_dump.buffer); + proxy.pcap_dump.buffer = NULL; } notifyServiceStatus(&proxy, "stopped"); diff --git a/app/src/main/jni/vpnproxy-jni/vpnproxy.h b/app/src/main/jni/vpnproxy-jni/vpnproxy.h index 019673a5..5a9986f6 100644 --- a/app/src/main/jni/vpnproxy-jni/vpnproxy.h +++ b/app/src/main/jni/vpnproxy-jni/vpnproxy.h @@ -116,19 +116,12 @@ typedef struct vpnproxy_data { bool root_capture; zdtun_statistics_t stats; - struct { - u_int32_t collector_addr; - u_int16_t collector_port; - bool tcp_socket; - bool enabled; - } pcap_dump; - struct { bool enabled; jbyte *buffer; int buffer_idx; u_int64_t last_dump_ms; - } java_dump; + } pcap_dump; struct { bool enabled;