Refactor PCAP dump

PCAP dump now always occurs in Java
This commit is contained in:
emanuele-f 2021-07-01 10:53:01 +02:00
parent 258930d110
commit 91cc995eff
11 changed files with 312 additions and 234 deletions

View File

@ -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());

View File

@ -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<Integer> iterPcapRecords(byte[] data) {
final ByteBuffer buf = ByteBuffer.wrap(data);
buf.order(ByteOrder.nativeOrder());
return new Iterator<Integer>() {
@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);

View File

@ -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;
}

View File

@ -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<byte[]> mChunks = new ArrayList<byte[]>();
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 */

View File

@ -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);
}
}

View File

@ -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);
}
}
}
}

View File

@ -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<Integer> it = Utils.iterPcapRecords(data);
int pos = 0;
while(it.hasNext()) {
int rec_len = it.next();
sendDatagram(data, pos, rec_len);
pos += rec_len;
}
}
}

View File

@ -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;

View File

@ -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__

View File

@ -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");

View File

@ -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;