Native PCAP export, fixes

This commit is contained in:
M66B 2016-01-17 10:42:21 +01:00
parent 38dfc4aa36
commit 7a41f35e74
4 changed files with 318 additions and 99 deletions

View File

@ -58,6 +58,8 @@ import org.xml.sax.XMLReader;
import org.xml.sax.helpers.DefaultHandler;
import org.xmlpull.v1.XmlSerializer;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
@ -80,9 +82,11 @@ public class ActivitySettings extends AppCompatActivity implements SharedPrefere
private static final int REQUEST_EXPORT = 1;
private static final int REQUEST_IMPORT = 2;
private static final int REQUEST_METERED = 3;
private static final int REQUEST_ROAMING_NATIONAL = 4;
private static final int REQUEST_ROAMING_INTERNATIONAL = 5;
private static final int REQUEST_PCAP = 3;
private static final int REQUEST_METERED = 4;
private static final int REQUEST_ROAMING_NATIONAL = 5;
private static final int REQUEST_ROAMING_INTERNATIONAL = 6;
private static final Intent INTENT_VPN_SETTINGS = new Intent("android.net.vpn.SETTINGS");
protected void onCreate(Bundle savedInstanceState) {
@ -168,6 +172,17 @@ public class ActivitySettings extends AppCompatActivity implements SharedPrefere
}
});
// Handle pcap export
Preference pref_pcap = screen.findPreference("pcap");
pref_pcap.setEnabled(getIntentPCAPDocument().resolveActivity(getPackageManager()) != null);
pref_pcap.setOnPreferenceClickListener(new Preference.OnPreferenceClickListener() {
@Override
public boolean onPreferenceClick(Preference preference) {
startActivityForResult(getIntentPCAPDocument(), ActivitySettings.REQUEST_PCAP);
return true;
}
});
// Handle technical info
Preference.OnPreferenceClickListener listener = new Preference.OnPreferenceClickListener() {
@Override
@ -214,6 +229,12 @@ public class ActivitySettings extends AppCompatActivity implements SharedPrefere
options.removePreference(screen.findPreference("unmetered_4g"));
options.removePreference(screen.findPreference("national_roaming"));
}
if (!Util.isDebuggable(this)) {
PreferenceCategory backup = (PreferenceCategory) screen.findPreference("category_backup");
backup.removePreference(screen.findPreference("pcap"));
}
}
@Override
@ -478,20 +499,47 @@ public class ActivitySettings extends AppCompatActivity implements SharedPrefere
if (resultCode == RESULT_OK && data != null)
handleImport(data);
} else if (requestCode == REQUEST_PCAP) {
if (resultCode == RESULT_OK && data != null)
handleExportPCAP(data);
} else {
Log.w(TAG, "Unknown activity result request=" + requestCode);
super.onActivityResult(requestCode, resultCode, data);
}
}
private static Intent getIntentCreateDocument() {
Intent intent = new Intent(Intent.ACTION_CREATE_DOCUMENT);
intent.addCategory(Intent.CATEGORY_OPENABLE);
intent.setType("text/xml");
intent.putExtra(Intent.EXTRA_TITLE, "netguard_" + new SimpleDateFormat("yyyyMMdd").format(new Date().getTime()) + ".xml");
return intent;
}
private static Intent getIntentOpenDocument() {
Intent intent = new Intent(Intent.ACTION_OPEN_DOCUMENT);
intent.addCategory(Intent.CATEGORY_OPENABLE);
intent.setType("text/xml");
return intent;
}
private static Intent getIntentPCAPDocument() {
Intent intent = new Intent(Intent.ACTION_CREATE_DOCUMENT);
intent.addCategory(Intent.CATEGORY_OPENABLE);
intent.setType("application/octet-stream");
intent.putExtra(Intent.EXTRA_TITLE, "netguard_" + new SimpleDateFormat("yyyyMMdd").format(new Date().getTime()) + ".pcap");
return intent;
}
private void handleExport(final Intent data) {
new AsyncTask<Object, Object, Throwable>() {
@Override
protected Throwable doInBackground(Object... objects) {
OutputStream out = null;
try {
out = getContentResolver().openOutputStream(data.getData());
Log.i(TAG, "Writing URI=" + data.getData());
out = getContentResolver().openOutputStream(data.getData());
xmlExport(out);
return null;
} catch (Throwable ex) {
@ -518,29 +566,14 @@ public class ActivitySettings extends AppCompatActivity implements SharedPrefere
}.execute();
}
private static Intent getIntentCreateDocument() {
Intent intent = new Intent(Intent.ACTION_CREATE_DOCUMENT);
intent.addCategory(Intent.CATEGORY_OPENABLE);
intent.setType("text/xml");
intent.putExtra(Intent.EXTRA_TITLE, "netguard_" + new SimpleDateFormat("yyyyMMdd").format(new Date().getTime()) + ".xml");
return intent;
}
private static Intent getIntentOpenDocument() {
Intent intent = new Intent(Intent.ACTION_OPEN_DOCUMENT);
intent.addCategory(Intent.CATEGORY_OPENABLE);
intent.setType("text/xml");
return intent;
}
private void handleImport(final Intent data) {
new AsyncTask<Object, Object, Throwable>() {
@Override
protected Throwable doInBackground(Object... objects) {
InputStream in = null;
try {
in = getContentResolver().openInputStream(data.getData());
Log.i(TAG, "Reading URI=" + data.getData());
in = getContentResolver().openInputStream(data.getData());
xmlImport(in);
return null;
} catch (Throwable ex) {
@ -568,6 +601,55 @@ public class ActivitySettings extends AppCompatActivity implements SharedPrefere
}.execute();
}
private void handleExportPCAP(final Intent data) {
new AsyncTask<Object, Object, Throwable>() {
@Override
protected Throwable doInBackground(Object... objects) {
OutputStream out = null;
InputStream in = null;
try {
Log.i(TAG, "Export PCAP URI=" + data.getData());
out = getContentResolver().openOutputStream(data.getData());
File pcap = new File(getCacheDir(), "netguard.pcap");
in = new FileInputStream(pcap);
byte[] buf = new byte[4096];
int len;
while ((len = in.read(buf)) > 0)
out.write(buf, 0, len);
return null;
} catch (Throwable ex) {
Log.e(TAG, ex.toString() + "\n" + Log.getStackTraceString(ex));
Util.sendCrashReport(ex, ActivitySettings.this);
return ex;
} finally {
if (out != null)
try {
out.close();
} catch (IOException ex) {
Log.e(TAG, ex.toString() + "\n" + Log.getStackTraceString(ex));
}
if (in != null)
try {
in.close();
} catch (IOException ex) {
Log.e(TAG, ex.toString() + "\n" + Log.getStackTraceString(ex));
}
}
}
@Override
protected void onPostExecute(Throwable ex) {
if (ex == null)
Toast.makeText(ActivitySettings.this, R.string.msg_completed, Toast.LENGTH_LONG).show();
else
Toast.makeText(ActivitySettings.this, ex.toString(), Toast.LENGTH_LONG).show();
}
}.execute();
}
private void xmlExport(OutputStream out) throws IOException {
XmlSerializer serializer = Xml.newSerializer();
serializer.setOutput(out, "UTF-8");

View File

@ -61,6 +61,7 @@ import android.util.Log;
import android.util.TypedValue;
import android.widget.RemoteViews;
import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Comparator;
@ -111,12 +112,14 @@ public class SinkholeService extends VpnService implements SharedPreferences.OnS
private static final String ACTION_SCREEN_OFF_DELAYED = "eu.faircode.netguard.SCREEN_OFF_DELAYED";
private native void jni_init();
private native void jni_init(boolean debug, String pcap);
private native void jni_start(int tun);
private native void jni_stop(int tun, boolean clear);
private native void jni_done();
static {
System.loadLibrary("netguard");
}
@ -852,7 +855,11 @@ public class SinkholeService extends VpnService implements SharedPreferences.OnS
public void onCreate() {
Log.i(TAG, "Create");
jni_init();
boolean debug = Util.isDebuggable(this);
File pcap = new File(getCacheDir(), "netguard.pcap");
if (pcap.exists())
pcap.delete();
jni_init(debug, pcap.getAbsolutePath());
SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(this);
prefs.registerOnSharedPreferenceChangeListener(this);
@ -997,6 +1004,7 @@ public class SinkholeService extends VpnService implements SharedPreferences.OnS
stopVPN(vpn);
vpn = null;
}
jni_done();
SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(this);
prefs.unregisterOnSharedPreferenceChangeListener(this);

View File

@ -32,6 +32,7 @@
#define TCPTTL 64
#define TCPWINDOW 32768
#define UIDDELAY 10 // milliseconds
#define MAXPCAP 80
struct arguments {
jobject instance;
@ -55,6 +56,34 @@ struct session {
struct session *next;
};
// https://wiki.wireshark.org/Development/LibpcapFileFormat
typedef unsigned short guint16_t;
typedef unsigned int guint32_t;
typedef signed int gint32_t;
typedef struct pcap_hdr_s {
guint32_t magic_number;
guint16_t version_major;
guint16_t version_minor;
gint32_t thiszone;
guint32_t sigfigs;
guint32_t snaplen;
guint32_t network;
} pcap_hdr_t;
typedef struct pcaprec_hdr_s {
guint32_t ts_sec;
guint32_t ts_usec;
guint32_t incl_len;
guint32_t orig_len;
} pcaprec_hdr_t;
#define LINKTYPE_RAW 101
void pcap_write(const void *, size_t);
void *handle_events(void *);
void handle_ip(JNIEnv *, jobject, const struct arguments *, const uint8_t *, const uint16_t);
@ -84,13 +113,54 @@ static JavaVM *jvm;
pthread_t thread_id;
int signaled = 0;
struct session *session = NULL;
char *pcap_fn = NULL;
// JNI
JNIEXPORT void JNICALL
Java_eu_faircode_netguard_SinkholeService_jni_1init(JNIEnv *env, jobject instance) {
__android_log_print(ANDROID_LOG_DEBUG, TAG, "Init");
session = NULL;
Java_eu_faircode_netguard_SinkholeService_jni_1init(JNIEnv *env, jobject instance,
jboolean debug, jstring pcap_) {
const char *pcap = (*env)->GetStringUTFChars(env, pcap_, 0);
if (pcap == NULL)
pcap_fn = NULL;
else {
pcap_fn = malloc(strlen(pcap) + 1);
strcpy(pcap_fn, pcap);
__android_log_print(ANDROID_LOG_DEBUG, TAG, "PCAP %s", pcap_fn);
session = NULL;
struct pcap_hdr_s pcap_hdr;
pcap_hdr.magic_number = 0xa1b2c3d4;
pcap_hdr.version_major = 2;
pcap_hdr.version_minor = 4;
pcap_hdr.thiszone = 0;
pcap_hdr.sigfigs = 0;
pcap_hdr.snaplen = MAXPCAP;
pcap_hdr.network = LINKTYPE_RAW;
pcap_write(&pcap_hdr, sizeof(struct pcap_hdr_s));
}
(*env)->ReleaseStringUTFChars(env, pcap_, pcap);
}
void pcap_write(const void *ptr, size_t len) {
FILE *fd = fopen(pcap_fn, "ab");
if (fd == NULL)
__android_log_print(ANDROID_LOG_ERROR, TAG, "fopen %s error %d: %s",
pcap_fn, errno, strerror(errno));
else {
if (fwrite(ptr, len, 1, fd) < 1)
__android_log_print(ANDROID_LOG_ERROR, TAG, "fwrite %s error %d: %s",
pcap_fn, errno, strerror(errno));
else
__android_log_print(ANDROID_LOG_DEBUG, TAG, "PCAP write %d", len);
if (fclose(fd))
__android_log_print(ANDROID_LOG_ERROR, TAG, "fclose %s error %d: %s",
pcap_fn, errno, strerror(errno));
}
}
JNIEXPORT void JNICALL
@ -146,6 +216,12 @@ Java_eu_faircode_netguard_SinkholeService_jni_1stop(JNIEnv *env, jobject instanc
__android_log_print(ANDROID_LOG_WARN, TAG, "Not running");
}
JNIEXPORT void JNICALL
Java_eu_faircode_netguard_SinkholeService_jni_1done(JNIEnv *env, jobject instance) {
__android_log_print(ANDROID_LOG_DEBUG, TAG, "Done");
free(pcap_fn);
}
// Private functions
void sig_handler(int sig, siginfo_t *info, void *context) {
@ -244,8 +320,6 @@ void *handle_events(void *a) {
if (close(cur->socket))
__android_log_print(ANDROID_LOG_ERROR, TAG, "close error %d: %s",
errno, strerror(errno));
else
cur->state = TCP_CLOSE;
if (last == NULL)
session = cur->next;
@ -328,8 +402,32 @@ void *handle_events(void *a) {
else
break;
}
if (length > 0)
else if (length > 0) {
if (pcap_fn != NULL) {
struct timespec ts;
if (clock_gettime(CLOCK_REALTIME, &ts))
__android_log_print(ANDROID_LOG_ERROR, TAG,
"clock_gettime error %d: %s",
errno, strerror(errno));
int plen = (length < MAXPCAP ? length : MAXPCAP);
struct pcaprec_hdr_s *pcap_rec = malloc(
sizeof(struct pcaprec_hdr_s) + plen);
pcap_rec->ts_sec = ts.tv_sec;
pcap_rec->ts_usec = ts.tv_nsec / 1000;
pcap_rec->incl_len = plen;
pcap_rec->orig_len = length;
memcpy(((uint8_t *) pcap_rec) + sizeof(struct pcaprec_hdr_s), buffer, plen);
pcap_write(pcap_rec, sizeof(struct pcaprec_hdr_s) + plen);
free(pcap_rec);
}
handle_ip(env, args->instance, args, buffer, length);
}
else {
__android_log_print(ANDROID_LOG_ERROR, TAG, "tun empty read");
break;
@ -409,8 +507,9 @@ void *handle_events(void *a) {
}
}
else
__android_log_print(ANDROID_LOG_DEBUG, TAG, "recv lport %u empty",
cur->lport);
__android_log_print(ANDROID_LOG_DEBUG, TAG,
"recv empty lport %u state %s",
cur->lport, strstate(cur->state));
// TODO can write
if (writeTCP(cur, NULL, 0, 0, 0, 1, 0, args->tun) < 0) // FIN
@ -418,18 +517,18 @@ void *handle_events(void *a) {
"write FIN lport %u error %d: %s",
cur->lport, errno, strerror((errno)));
else {
__android_log_print(ANDROID_LOG_DEBUG, TAG,
"Half close initiated");
cur->local_seq++; // local FIN
if (cur->state == TCP_SYN_RECV || cur->state == TCP_ESTABLISHED)
cur->state = TCP_FIN_WAIT1;
else // close wait
cur->state = TCP_LAST_ACK;
__android_log_print(ANDROID_LOG_DEBUG, TAG, "Half close state %s",
strstate(cur->state));
}
} else {
__android_log_print(ANDROID_LOG_DEBUG, TAG,
"recv lport %u bytes %d",
cur->lport, bytes);
"recv lport %u bytes %d state %s",
cur->lport, bytes, strstate(cur->state));
// TODO can write
if (writeTCP(cur, buffer, bytes, 0, 0, 0, 0, args->tun) < 0) // ACK
__android_log_print(ANDROID_LOG_ERROR, TAG,
@ -535,7 +634,7 @@ void handle_ip(JNIEnv *env, jobject instance, const struct arguments *args,
flags[flen++] = 'P';
if (tcp->fin)
flags[flen++] = 'F';
if (tcp->fin)
if (tcp->rst)
flags[flen++] = 'R';
// TODO checksum
@ -640,7 +739,8 @@ void handle_tcp(JNIEnv *env, jobject instance, const struct arguments *args,
inet_ntop(AF_INET, &(iphdr->daddr), dest, sizeof(dest));
__android_log_print(ANDROID_LOG_DEBUG, TAG, "Received %s/%u seq %u ack %u window %u data %d",
dest, ntohs(tcphdr->dest),
ntohl(tcphdr->seq), ntohl(tcphdr->ack_seq),
ntohl(tcphdr->seq) - (cur == NULL ? 0 : cur->remote_start),
ntohl(tcphdr->ack_seq) - (cur == NULL ? 0 : cur->local_start),
ntohs(tcphdr->window), datalen);
if (cur == NULL) {
@ -691,24 +791,18 @@ void handle_tcp(JNIEnv *env, jobject instance, const struct arguments *args,
}
else {
__android_log_print(ANDROID_LOG_WARN, TAG, "Unknown session");
struct session *rst = malloc(sizeof(struct session));
rst->time = time(NULL);
rst->remote_seq = ntohl(tcphdr->seq);
rst->local_seq = 0;
rst->saddr = iphdr->saddr;
rst->source = tcphdr->source;
rst->daddr = iphdr->daddr;
rst->dest = tcphdr->dest;
rst->state = TCP_TIME_WAIT;
rst->next = NULL;
struct session rst;
memset(&rst, 0, sizeof(struct session));
rst.saddr = iphdr->saddr;
rst.source = tcphdr->source;
rst.daddr = iphdr->daddr;
rst.dest = tcphdr->dest;
// TODO can write
int confirm = (tcphdr->syn || tcphdr->fin ? 1 : 0) + datalen;
if (writeTCP(rst, NULL, 0, confirm, 0, 0, 1, args->tun) < 0)
if (writeTCP(&rst, NULL, 0, 0, 0, 0, 1, args->tun) < 0)
__android_log_print(ANDROID_LOG_ERROR, TAG,
"write RST error %d: %s",
errno, strerror((errno)));
free(rst);
}
}
else {
@ -726,16 +820,12 @@ void handle_tcp(JNIEnv *env, jobject instance, const struct arguments *args,
__android_log_print(ANDROID_LOG_DEBUG, TAG, "Ignoring repeated SYN");
else if (tcphdr->ack && !tcphdr->fin) {
// TODO proper wrap around
if (ntohl(tcphdr->seq) + 1 == cur->remote_seq) {
if (((uint32_t) ntohl(tcphdr->seq) + 1) == cur->remote_seq) {
// TODO respond to keep alive?
__android_log_print(ANDROID_LOG_DEBUG, TAG, "Keep alive");
cur->time = time(NULL);
} else if (ntohl(tcphdr->ack_seq) < cur->local_seq ||
ntohl(tcphdr->seq) < cur->remote_seq)
__android_log_print(ANDROID_LOG_WARN, TAG, "Old ack");
else if (ntohl(tcphdr->ack_seq) == cur->local_seq &&
ntohl(tcphdr->seq) == cur->remote_seq) {
} else if (ntohl(tcphdr->ack_seq) == cur->local_seq &&
ntohl(tcphdr->seq) == cur->remote_seq) {
cur->time = time(NULL);
if (cur->state == TCP_SYN_RECV) {
@ -776,67 +866,86 @@ void handle_tcp(JNIEnv *env, jobject instance, const struct arguments *args,
}
else {
// TODO check old seq/ack
__android_log_print(ANDROID_LOG_WARN, TAG, "Invalid seq/ack");
__android_log_print(ANDROID_LOG_WARN, TAG, "Invalid seq %u/%u ack %u/%u",
ntohl(tcphdr->seq) - cur->remote_start,
cur->remote_seq - cur->remote_start,
ntohl(tcphdr->ack_seq) - cur->local_start,
cur->local_seq - cur->local_start);
}
}
else if (tcphdr->fin /* ack */) {
else if (tcphdr->fin /* ACK */) {
if (ntohl(tcphdr->ack_seq) == cur->local_seq &&
ntohl(tcphdr->seq) == cur->remote_seq) {
cur->time = time(NULL);
__android_log_print(ANDROID_LOG_DEBUG, TAG, "socket RD shutdown");
if (shutdown(cur->socket, SHUT_RD)) {
__android_log_print(ANDROID_LOG_ERROR, TAG, "shutdown error %d: %s",
__android_log_print(ANDROID_LOG_ERROR, TAG, "shutdown RD error %d: %s",
errno, strerror(errno));
// Remote will retry
// Socket could be closed by remote
}
else {
__android_log_print(ANDROID_LOG_DEBUG, TAG, "Shutdown socket");
int ok = 1;
if (tcphdr->ack && datalen) {
__android_log_print(ANDROID_LOG_DEBUG, TAG, "send socket data %u",
datalen);
// TODO non blocking
if (send(cur->socket, buffer + dataoff, datalen, 0) < 0) {
__android_log_print(ANDROID_LOG_ERROR, TAG, "send error %d: %s",
int ok = 1;
if (tcphdr->ack && datalen) {
__android_log_print(ANDROID_LOG_DEBUG, TAG, "send socket data %u",
datalen);
// TODO non blocking
if (send(cur->socket, buffer + dataoff, datalen, 0) < 0) {
__android_log_print(ANDROID_LOG_ERROR, TAG, "send error %d: %s",
errno, strerror(errno));
ok = 0;
}
else {
if (shutdown(cur->socket, SHUT_WR)) {
__android_log_print(ANDROID_LOG_ERROR, TAG,
"shutdown WR error %d: %s",
errno, strerror(errno));
ok = 0;
// Data might be lost
}
else
__android_log_print(ANDROID_LOG_DEBUG, TAG, "socket WR shutdown");
}
}
if (ok) {
// TODO can write
if (writeTCP(cur, NULL, 0, 1 + datalen, 0, 0, 0, args->tun) < 0) // ACK
__android_log_print(ANDROID_LOG_ERROR, TAG,
"write ACK error %d: %s",
errno, strerror((errno)));
else {
cur->remote_seq += 1 + datalen; // FIN + received from tun
// TODO check ACK !TCP_FIN_WAIT1
if (cur->state == TCP_ESTABLISHED)
cur->state = TCP_CLOSE_WAIT;
else if (cur->state == TCP_FIN_WAIT1) {
if (tcphdr->ack)
cur->state = TCP_TIME_WAIT;
else
cur->state = TCP_CLOSING;
} else if (cur->state == TCP_FIN_WAIT2)
if (ok) {
// TODO can write
if (writeTCP(cur, NULL, 0, 1 + datalen, 0, 0, 0, args->tun) < 0) // ACK
__android_log_print(ANDROID_LOG_ERROR, TAG,
"write ACK error %d: %s",
errno, strerror((errno)));
else {
cur->remote_seq += (1 + datalen); // FIN + received from tun
// TODO check ACK !TCP_FIN_WAIT1
if (cur->state == TCP_ESTABLISHED)
cur->state = TCP_CLOSE_WAIT;
else if (cur->state == TCP_FIN_WAIT1) {
if (tcphdr->ack)
cur->state = TCP_TIME_WAIT;
else
__android_log_print(ANDROID_LOG_ERROR, TAG, "Invalid FIN");
}
cur->state = TCP_CLOSING;
} else if (cur->state == TCP_FIN_WAIT2)
cur->state = TCP_TIME_WAIT;
else
__android_log_print(ANDROID_LOG_ERROR, TAG, "Invalid FIN");
}
}
}
else {
// TODO check old seq/ack
__android_log_print(ANDROID_LOG_WARN, TAG, "Invalid seq/ack");
__android_log_print(ANDROID_LOG_WARN, TAG, "Invalid seq %u/%u ack %u/%u",
ntohl(tcphdr->seq) - cur->remote_start,
cur->remote_seq - cur->remote_start,
ntohl(tcphdr->ack_seq) - cur->local_start,
cur->local_seq - cur->local_start);
}
}
else if (tcphdr->rst)
else if (tcphdr->rst) {
cur->time = time(NULL);
cur->state = TCP_TIME_WAIT; // will close socket
}
else
__android_log_print(ANDROID_LOG_ERROR, TAG, "Unknown packet");
@ -955,15 +1064,17 @@ int writeTCP(const struct session *cur,
tcp->source = cur->dest;
tcp->dest = cur->source;
tcp->seq = htonl(cur->local_seq);
tcp->ack_seq = htonl(cur->remote_seq + confirm); // TODO proper wrap around
tcp->ack_seq = htonl((uint32_t) (cur->remote_seq + confirm));
tcp->doff = sizeof(struct tcphdr) >> 2;
tcp->syn = syn;
// TODO why does a FIN need an ACK?
tcp->ack = (datalen > 0 || confirm > 0 || syn || fin);
tcp->ack = (datalen > 0 || confirm > 0 || syn);
tcp->fin = fin;
tcp->rst = rst;
tcp->window = htons(TCPWINDOW);
if (!tcp->ack)
tcp->ack_seq = 0;
// Calculate TCP checksum
// TODO optimize memory usage
uint16_t clen = sizeof(struct ippseudo) + sizeof(struct tcphdr) + datalen;
@ -998,13 +1109,29 @@ int writeTCP(const struct session *cur,
ntohl(tcp->seq) - cur->local_start,
ntohl(tcp->ack_seq) - cur->remote_start,
datalen, confirm);
//if (tcp->fin || tcp->rst) {
// char *h = hex(buffer, len);
// __android_log_print(ANDROID_LOG_DEBUG, TAG, "%s", h);
// free(h);
//}
int res = write(tun, buffer, len);
if (pcap_fn != NULL) {
struct timespec ts;
if (clock_gettime(CLOCK_REALTIME, &ts))
__android_log_print(ANDROID_LOG_ERROR, TAG,
"clock_gettime error %d: %s",
errno, strerror(errno));
int plen = (len < MAXPCAP ? len : MAXPCAP);
struct pcaprec_hdr_s *pcap_rec = malloc(sizeof(struct pcaprec_hdr_s) + plen);
pcap_rec->ts_sec = ts.tv_sec;
pcap_rec->ts_usec = ts.tv_nsec / 1000;
pcap_rec->incl_len = plen;
pcap_rec->orig_len = len;
memcpy(((uint8_t *) pcap_rec) + sizeof(struct pcaprec_hdr_s), buffer, plen);
pcap_write(pcap_rec, sizeof(struct pcaprec_hdr_s) + plen);
free(pcap_rec);
}
free(buffer);
return res;
@ -1140,4 +1267,3 @@ char *hex(const u_int8_t *data, const u_int16_t len) {
}
return hexout;
}

View File

@ -124,6 +124,9 @@
<Preference
android:key="import"
android:title="@string/setting_import" />
<Preference
android:key="pcap"
android:title="Export PCAP" />
</PreferenceCategory>
<PreferenceCategory
android:key="category_technical"