mirror of
https://github.com/emanuele-f/PCAPdroid.git
synced 2026-06-28 21:00:51 +08:00
Merge pull request #513 from emanuele-f/issue-509-tcp-exporter
Implement TCP Exporter (pcap-over-ip)
This commit is contained in:
commit
b098d65515
@ -79,6 +79,7 @@ import com.emanuelef.remote_capture.model.CaptureStats;
|
||||
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.TCPDumper;
|
||||
import com.emanuelef.remote_capture.pcap_dump.UDPDumper;
|
||||
import com.pcapdroid.mitm.MitmAPI;
|
||||
|
||||
@ -385,22 +386,24 @@ public class CaptureService extends VpnService implements Runnable {
|
||||
}
|
||||
|
||||
mDumper = new UDPDumper(new InetSocketAddress(addr, mSettings.collector_port), mSettings.pcapng_format);
|
||||
}
|
||||
|
||||
if(mDumper != null) {
|
||||
// Max memory usage = (JAVA_PCAP_BUFFER_SIZE * 64) = 32 MB
|
||||
mDumpQueue = new LinkedBlockingDeque<>(64);
|
||||
} else if(mSettings.dump_mode == Prefs.DumpMode.TCP_EXPORTER) {
|
||||
InetAddress addr;
|
||||
|
||||
try {
|
||||
mDumper.startDumper();
|
||||
} catch (IOException | SecurityException e) {
|
||||
addr = InetAddress.getByName(mSettings.collector_address);
|
||||
} catch (UnknownHostException e) {
|
||||
reportError(e.getLocalizedMessage());
|
||||
e.printStackTrace();
|
||||
mDumper = null;
|
||||
return abortStart();
|
||||
}
|
||||
|
||||
mDumper = new TCPDumper(new InetSocketAddress(addr, mSettings.collector_port), mSettings.pcapng_format);
|
||||
}
|
||||
|
||||
if(mDumper != null)
|
||||
// Max memory usage = (JAVA_PCAP_BUFFER_SIZE * 64) = 32 MB
|
||||
mDumpQueue = new LinkedBlockingDeque<>(64);
|
||||
|
||||
mSocks5Address = "";
|
||||
mSocks5Enabled = mSettings.socks5_enabled || mSettings.tls_decryption;
|
||||
if(mSocks5Enabled) {
|
||||
@ -1230,6 +1233,19 @@ public class CaptureService extends VpnService implements Runnable {
|
||||
}
|
||||
|
||||
private void dumpWork() {
|
||||
Log.d(TAG, "Starting the dumper");
|
||||
|
||||
try {
|
||||
mDumper.startDumper();
|
||||
} catch (IOException | SecurityException e) {
|
||||
e.printStackTrace();
|
||||
reportError(e.getLocalizedMessage());
|
||||
mHandler.post(CaptureService::stopPacketLoop);
|
||||
return;
|
||||
}
|
||||
|
||||
Log.d(TAG, "Dumper running");
|
||||
|
||||
while(true) {
|
||||
byte[] data;
|
||||
try {
|
||||
|
||||
@ -220,7 +220,10 @@ public class CaptureCtrl extends AppCompatActivity {
|
||||
private String checkRemoteServerNotAllowed(CaptureSettings settings) {
|
||||
SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(this);
|
||||
|
||||
if((settings.dump_mode == Prefs.DumpMode.UDP_EXPORTER) &&
|
||||
boolean exporterEnabled = (settings.dump_mode == Prefs.DumpMode.UDP_EXPORTER) ||
|
||||
(settings.dump_mode == Prefs.DumpMode.TCP_EXPORTER);
|
||||
|
||||
if(exporterEnabled &&
|
||||
!Utils.isLocalNetworkAddress(settings.collector_address) &&
|
||||
!Prefs.getCollectorIp(prefs).equals(settings.collector_address))
|
||||
return settings.collector_address;
|
||||
|
||||
@ -824,7 +824,10 @@ public class MainActivity extends BaseActivity implements NavigationView.OnNavig
|
||||
if(mPrefs.getBoolean(Prefs.PREF_REMOTE_COLLECTOR_ACK, false))
|
||||
return false; // already acknowledged
|
||||
|
||||
if(((Prefs.getDumpMode(mPrefs) == Prefs.DumpMode.UDP_EXPORTER) && !Utils.isLocalNetworkAddress(Prefs.getCollectorIp(mPrefs))) ||
|
||||
boolean exporterEnabled = (Prefs.getDumpMode(mPrefs) == Prefs.DumpMode.UDP_EXPORTER) ||
|
||||
(Prefs.getDumpMode(mPrefs) == Prefs.DumpMode.TCP_EXPORTER);
|
||||
|
||||
if((exporterEnabled && !Utils.isLocalNetworkAddress(Prefs.getCollectorIp(mPrefs))) ||
|
||||
(Prefs.getSocks5Enabled(mPrefs) && !Utils.isLocalNetworkAddress(Prefs.getSocks5ProxyHost(mPrefs)))) {
|
||||
Log.i(TAG, "Showing possible scan notice");
|
||||
|
||||
|
||||
@ -202,7 +202,7 @@ public class SettingsActivity extends BaseActivity implements PreferenceFragment
|
||||
setPreferencesFromResource(R.xml.root_preferences, rootKey);
|
||||
mIab = Billing.newInstance(requireContext());
|
||||
|
||||
setupUdpExporterPrefs();
|
||||
setupExporterPrefs();
|
||||
setupHttpServerPrefs();
|
||||
setupTrafficInspectionPrefs();
|
||||
setupCapturePrefs();
|
||||
@ -254,7 +254,7 @@ public class SettingsActivity extends BaseActivity implements PreferenceFragment
|
||||
}
|
||||
|
||||
@SuppressWarnings("deprecation")
|
||||
private void setupUdpExporterPrefs() {
|
||||
private void setupExporterPrefs() {
|
||||
/* Collector IP validation */
|
||||
EditTextPreference mRemoteCollectorIp = requirePreference(Prefs.PREF_COLLECTOR_IP_KEY);
|
||||
mRemoteCollectorIp.setOnPreferenceChangeListener((preference, newValue) -> Utils.validateIpAddress(newValue.toString()));
|
||||
|
||||
@ -290,6 +290,10 @@ public class StatusFragment extends Fragment implements AppStateListener, MenuPr
|
||||
info = String.format(getResources().getString(R.string.collector_info),
|
||||
CaptureService.getCollectorAddress(), CaptureService.getCollectorPort());
|
||||
break;
|
||||
case TCP_EXPORTER:
|
||||
info = String.format(getResources().getString(R.string.tcp_collector_info),
|
||||
CaptureService.getCollectorAddress(), CaptureService.getCollectorPort());
|
||||
break;
|
||||
}
|
||||
|
||||
mCollectorInfoText.setText(info);
|
||||
|
||||
@ -38,6 +38,7 @@ public class Prefs {
|
||||
public static final String DUMP_NONE = "none";
|
||||
public static final String DUMP_HTTP_SERVER = "http_server";
|
||||
public static final String DUMP_UDP_EXPORTER = "udp_exporter";
|
||||
public static final String DUMP_TCP_EXPORTER = "tcp_exporter";
|
||||
public static final String DUMP_PCAP_FILE = "pcap_file";
|
||||
public static final String DEFAULT_DUMP_MODE = DUMP_NONE;
|
||||
|
||||
@ -114,7 +115,8 @@ public class Prefs {
|
||||
NONE,
|
||||
HTTP_SERVER,
|
||||
PCAP_FILE,
|
||||
UDP_EXPORTER
|
||||
UDP_EXPORTER,
|
||||
TCP_EXPORTER
|
||||
}
|
||||
|
||||
public enum IpMode {
|
||||
@ -140,6 +142,7 @@ public class Prefs {
|
||||
case DUMP_HTTP_SERVER: return DumpMode.HTTP_SERVER;
|
||||
case DUMP_PCAP_FILE: return DumpMode.PCAP_FILE;
|
||||
case DUMP_UDP_EXPORTER: return DumpMode.UDP_EXPORTER;
|
||||
case DUMP_TCP_EXPORTER: return DumpMode.TCP_EXPORTER;
|
||||
default: return DumpMode.NONE;
|
||||
}
|
||||
}
|
||||
|
||||
@ -0,0 +1,95 @@
|
||||
/*
|
||||
* This file is part of PCAPdroid.
|
||||
*
|
||||
* PCAPdroid is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* PCAPdroid is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with PCAPdroid. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
* Copyright 2020-25 - Emanuele Faranda
|
||||
*/
|
||||
|
||||
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.DataOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.net.InetSocketAddress;
|
||||
import java.net.Socket;
|
||||
import java.util.Iterator;
|
||||
|
||||
public class TCPDumper implements PcapDumper {
|
||||
private static final String TAG = "TCPDumper";
|
||||
private final InetSocketAddress mServer;
|
||||
private final boolean mPcapngFormat;
|
||||
private boolean mSendHeader;
|
||||
private Socket mSocket;
|
||||
private DataOutputStream mDataOut;
|
||||
|
||||
public TCPDumper(InetSocketAddress server, boolean pcapngFormat) {
|
||||
mServer = server;
|
||||
mSendHeader = true;
|
||||
mPcapngFormat = pcapngFormat;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void startDumper() throws IOException {
|
||||
mSocket = new Socket();
|
||||
boolean ok = false;
|
||||
|
||||
try {
|
||||
mSocket.connect(mServer, 1000);
|
||||
mDataOut = new DataOutputStream(mSocket.getOutputStream());
|
||||
ok = true;
|
||||
} finally {
|
||||
if (!ok)
|
||||
mSocket.close();
|
||||
}
|
||||
|
||||
CaptureService.requireInstance().protect(mSocket);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void stopDumper() throws IOException {
|
||||
try {
|
||||
mDataOut.close();
|
||||
} finally {
|
||||
mSocket.close();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getBpf() {
|
||||
return "not (host " + mServer.getAddress().getHostAddress() + " and tcp port " + mServer.getPort() + ")";
|
||||
}
|
||||
|
||||
@Override
|
||||
public void dumpData(byte[] data) throws IOException {
|
||||
if(mSendHeader) {
|
||||
mSendHeader = false;
|
||||
|
||||
byte[] hdr = CaptureService.getPcapHeader();
|
||||
mDataOut.write(hdr);
|
||||
}
|
||||
|
||||
Iterator<Integer> it = Utils.iterPcapRecords(data, mPcapngFormat);
|
||||
int pos = 0;
|
||||
|
||||
while(it.hasNext()) {
|
||||
int rec_len = it.next();
|
||||
mDataOut.write(data, pos, rec_len);
|
||||
pos += rec_len;
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -1,3 +1,22 @@
|
||||
/*
|
||||
* This file is part of PCAPdroid.
|
||||
*
|
||||
* PCAPdroid is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* PCAPdroid is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with PCAPdroid. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
* Copyright 2020-25 - Emanuele Faranda
|
||||
*/
|
||||
|
||||
package com.emanuelef.remote_capture.pcap_dump;
|
||||
|
||||
import com.emanuelef.remote_capture.CaptureService;
|
||||
|
||||
@ -4,6 +4,7 @@
|
||||
<string name="stop_button">停止</string>
|
||||
<string name="title_activity_settings">设置</string>
|
||||
<string name="collector_info">UDP 收集器: %1$s:%2$d</string>
|
||||
<string name="tcp_collector_info">TCP 收集器: %1$s:%2$d</string>
|
||||
<string name="http_server_status">HTTP 服务器: http://%1$s:%2$d</string>
|
||||
<string name="rcvd_and_sent">%1$s 已接收 — %2$s 已发送</string>
|
||||
<string name="query">查询</string>
|
||||
@ -22,10 +23,13 @@
|
||||
<string name="duration">持续时间</string>
|
||||
<string name="http_server">HTTP 服务器</string>
|
||||
<string name="udp_exporter">UDP 导出器</string>
|
||||
<string name="tcp_exporter">TCP 导出器</string>
|
||||
<string name="tcp_udp_exporter">TCP/UDP 导出器</string>
|
||||
<string name="no_dump">无转储</string>
|
||||
<string name="no_dump_info">不会转储流量</string>
|
||||
<string name="http_server_info">启动一个 HTTP 服务器下载 PCAP</string>
|
||||
<string name="udp_exporter_info">发送 PCAP 到远程 UDP 接收器</string>
|
||||
<string name="tcp_exporter_info">发送 PCAP 到远程 TCP 接收器(pcap-over-ip)</string>
|
||||
<string name="http_server_port">HTTP 服务器端口</string>
|
||||
<string name="receiver_ip_address">收集器 IP 地址</string>
|
||||
<string name="receiver_port">收集器端口</string>
|
||||
|
||||
@ -5,18 +5,21 @@
|
||||
<item>http_server</item>
|
||||
<item>pcap_file</item>
|
||||
<item>udp_exporter</item>
|
||||
<item>tcp_exporter</item>
|
||||
</string-array>
|
||||
<string-array name="pcap_dump_modes_labels">
|
||||
<item>@string/no_dump</item>
|
||||
<item>@string/http_server</item>
|
||||
<item>@string/pcap_file</item>
|
||||
<item>@string/udp_exporter</item>
|
||||
<item>@string/tcp_exporter</item>
|
||||
</string-array>
|
||||
<string-array name="pcap_dump_modes_descriptions">
|
||||
<item>@string/no_dump_info</item>
|
||||
<item>@string/http_server_info</item>
|
||||
<item>@string/pcap_file_info</item>
|
||||
<item>@string/udp_exporter_info</item>
|
||||
<item>@string/tcp_exporter_info</item>
|
||||
</string-array>
|
||||
|
||||
<string-array name="app_languages">
|
||||
|
||||
@ -51,6 +51,7 @@
|
||||
<string name="stop_button">Stop</string>
|
||||
<string name="title_activity_settings">Settings</string>
|
||||
<string name="collector_info">UDP collector: %1$s:%2$d</string>
|
||||
<string name="tcp_collector_info">TCP collector: %1$s:%2$d</string>
|
||||
<string name="http_server_status">HTTP server: http://%1$s:%2$d</string>
|
||||
<string name="rcvd_and_sent">%1$s received — %2$s sent</string>
|
||||
<string name="query">Query</string>
|
||||
@ -69,10 +70,13 @@
|
||||
<string name="duration">Duration</string>
|
||||
<string name="http_server">HTTP server</string>
|
||||
<string name="udp_exporter">UDP exporter</string>
|
||||
<string name="tcp_exporter">TCP exporter</string>
|
||||
<string name="tcp_udp_exporter">TCP/UDP exporter</string>
|
||||
<string name="no_dump">No dump</string>
|
||||
<string name="no_dump_info">Traffic will not be dumped</string>
|
||||
<string name="http_server_info">Start an HTTP server for the PCAP download</string>
|
||||
<string name="udp_exporter_info">Sends the PCAP to a remote UDP receiver</string>
|
||||
<string name="udp_exporter_info">Send the PCAP to a remote UDP receiver</string>
|
||||
<string name="tcp_exporter_info">Send the PCAP to a remote TCP receiver (pcap-over-ip)</string>
|
||||
<string name="http_server_port">HTTP server port</string>
|
||||
<string name="receiver_ip_address">Collector IP address</string>
|
||||
<string name="receiver_port">Collector port</string>
|
||||
|
||||
@ -28,7 +28,7 @@
|
||||
app:useSimpleSummaryProvider="true" />
|
||||
</PreferenceCategory>
|
||||
|
||||
<PreferenceCategory app:title="@string/udp_exporter" app:iconSpaceReserved="false">
|
||||
<PreferenceCategory app:title="@string/tcp_udp_exporter" app:iconSpaceReserved="false">
|
||||
<EditTextPreference
|
||||
app:key="collector_ip_address"
|
||||
app:title="@string/receiver_ip_address"
|
||||
|
||||
@ -78,10 +78,10 @@ As shown above, the capture settings can be specified by using intent extras. Th
|
||||
|
||||
| Parameter | Type | Ver | Mode | Value |
|
||||
|-------------------------|--------|-----|------|--------------------------------------------------------------------|
|
||||
| pcap_dump_mode | string | | | none \| http_server \| udp_exporter \| pcap_file |
|
||||
| pcap_dump_mode | string | | | none \| http_server \| udp_exporter \| tcp_exporter \| pcap_file |
|
||||
| app_filter | string | | | package name of the app(s) to capture (73+: comma separated list) |
|
||||
| collector_ip_address | string | | | the IP address of the collector in udp_exporter mode |
|
||||
| collector_port | int | | | the UDP port of the collector in udp_exporter mode |
|
||||
| collector_ip_address | string | | | the IP address of the collector in tcp/udp_exporter mode |
|
||||
| collector_port | int | | | the UDP port of the collector in tcp/udp_exporter mode |
|
||||
| http_server_port | int | | | the HTTP server port in http_server mode |
|
||||
| pcap_uri | string | | | the URI for the PCAP dump in pcap_file mode (overrides pcap_name) |
|
||||
| socks5_enabled | bool | | vpn | true to redirect the TCP connections to a SOCKS5 proxy |
|
||||
|
||||
Loading…
Reference in New Issue
Block a user