mirror of https://github.com/M66B/NetGuard.git
Native PCAP export, fixes
This commit is contained in:
parent
38dfc4aa36
commit
7a41f35e74
|
@ -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");
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
|
@ -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"
|
||||
|
|
Loading…
Reference in New Issue