Native close

This commit is contained in:
M66B 2016-01-16 12:32:55 +01:00
parent 36bb564edb
commit a317fa3f3e
1 changed files with 165 additions and 103 deletions

View File

@ -78,6 +78,8 @@ jint getUid(const int, const int, const void *, const uint16_t);
uint16_t checksum(uint8_t *, uint16_t); uint16_t checksum(uint8_t *, uint16_t);
const char *strstate(const int state);
char *hex(const u_int8_t *, const u_int16_t); char *hex(const u_int8_t *, const u_int16_t);
// Global variables // Global variables
@ -228,12 +230,13 @@ void *handle_events(void *a) {
continue; continue;
} else if (cur->state != TCP_TIME_WAIT) { } else if (cur->state != TCP_TIME_WAIT) {
if (cur->state == TCP_SYN_RECV) { if (cur->state == TCP_LISTEN) {
FD_SET(cur->socket, &wfds); FD_SET(cur->socket, &wfds);
if (cur->socket > max) if (cur->socket > max)
max = cur->socket; max = cur->socket;
} }
else if (cur->state == TCP_ESTABLISHED || else if (cur->state == TCP_ESTABLISHED ||
cur->state == TCP_SYN_RECV ||
cur->state == TCP_CLOSE_WAIT) { cur->state == TCP_CLOSE_WAIT) {
FD_SET(cur->socket, &rfds); FD_SET(cur->socket, &rfds);
if (cur->socket > max) if (cur->socket > max)
@ -281,6 +284,9 @@ void *handle_events(void *a) {
if (length < 0) { if (length < 0) {
__android_log_print(ANDROID_LOG_ERROR, TAG, "tun read error %d: %s", __android_log_print(ANDROID_LOG_ERROR, TAG, "tun read error %d: %s",
errno, strerror(errno)); errno, strerror(errno));
if (errno == EINTR)
continue;
else
break; break;
} }
if (length > 0) if (length > 0)
@ -302,7 +308,7 @@ void *handle_events(void *a) {
__android_log_print(ANDROID_LOG_ERROR, TAG, "getsockopt error %d: %s", __android_log_print(ANDROID_LOG_ERROR, TAG, "getsockopt error %d: %s",
errno, strerror(errno)); errno, strerror(errno));
// TODO initiate finish // TODO initiate finish
cur->state = TCP_TIME_WAIT; cur->state = TCP_TIME_WAIT; // will close socket
cur = cur->next; cur = cur->next;
continue; continue;
} }
@ -310,13 +316,14 @@ void *handle_events(void *a) {
__android_log_print(ANDROID_LOG_ERROR, TAG, "SO_ERROR %d: %s", __android_log_print(ANDROID_LOG_ERROR, TAG, "SO_ERROR %d: %s",
serr, strerror(serr)); serr, strerror(serr));
// TODO initiate FIN // TODO initiate FIN
cur->state = TCP_TIME_WAIT; if (serr != EINTR)
cur->state = TCP_TIME_WAIT; // will close socket
cur = cur->next; cur = cur->next;
continue; continue;
} }
} }
if (cur->state == TCP_SYN_RECV) { if (cur->state == TCP_LISTEN) {
// Check socket connect // Check socket connect
if (FD_ISSET(cur->socket, &wfds) && canWrite(args->tun)) { if (FD_ISSET(cur->socket, &wfds) && canWrite(args->tun)) {
// Log // Log
@ -326,63 +333,58 @@ void *handle_events(void *a) {
dest, ntohs(cur->dest), cur->lport); dest, ntohs(cur->dest), cur->lport);
// TODO can write // TODO can write
if (writeTCP(cur, NULL, 1, 1, 0, 0, args->tun) < 0) { // SYN if (writeTCP(cur, NULL, 1, 1, 0, 0, args->tun) < 0) { // SYN+ACK
__android_log_print(ANDROID_LOG_ERROR, TAG, "write SYN error %d: %s", __android_log_print(ANDROID_LOG_ERROR, TAG,
"write SYN+ACK error %d: %s",
errno, strerror((errno))); errno, strerror((errno)));
cur->state = TCP_TIME_WAIT; // Remote will retry
cur->state = TCP_TIME_WAIT; // will close socket
cur = cur->next; cur = cur->next;
continue; continue;
} else } else {
cur->state = TCP_SYN_SENT; cur->local_seq++;
cur->remote_seq++;
cur->state = TCP_SYN_RECV;
}
} }
} }
else if (cur->state == TCP_ESTABLISHED || else if (cur->state == TCP_SYN_RECV ||
cur->state == TCP_ESTABLISHED ||
cur->state == TCP_CLOSE_WAIT) { cur->state == TCP_CLOSE_WAIT) {
// Check socket read // Check socket read
if (FD_ISSET(cur->socket, &rfds)) { if (FD_ISSET(cur->socket, &rfds)) {
// TODO window size
uint8_t buffer[MAXPKT]; uint8_t buffer[MAXPKT];
ssize_t bytes = recv(cur->socket, buffer, MAXPKT, 0); ssize_t bytes = recv(cur->socket, buffer, MAXPKT, 0);
if (bytes <= 0) {
// Socket remotely closed / error
if (bytes < 0) { if (bytes < 0) {
__android_log_print(ANDROID_LOG_ERROR, TAG, "recv socket error %d: %s", __android_log_print(ANDROID_LOG_ERROR, TAG,
"recv socket error %d: %s",
errno, strerror(errno)); errno, strerror(errno));
if (errno != EINTR) { if (errno == EINTR) {
// TODO initiate FIN
cur->state = TCP_TIME_WAIT;
cur = cur->next; cur = cur->next;
continue; continue;
} }
} }
else if (bytes == 0) { else
// Socket remotely closed __android_log_print(ANDROID_LOG_DEBUG, TAG, "recv socket empty");
if (cur->state == TCP_ESTABLISHED) {
// TODO initiate FIN
__android_log_print(ANDROID_LOG_WARN, TAG, "recv socket empty");
if (writeTCP(cur, NULL, 1, 0, 1, 0, args->tun) < 0) // FIN // TODO can write
if (writeTCP(cur, NULL, 0, 0, 1, 0, args->tun) < 0) // FIN
__android_log_print(ANDROID_LOG_ERROR, TAG, __android_log_print(ANDROID_LOG_ERROR, TAG,
"write FIN error %d: %s", "write FIN error %d: %s",
errno, strerror((errno))); errno, strerror((errno)));
else { else {
__android_log_print(ANDROID_LOG_DEBUG, TAG, __android_log_print(ANDROID_LOG_DEBUG, TAG,
"Half close initiated"); "Half close initiated");
cur->local_seq++;
if (cur->state == TCP_SYN_RECV || cur->state == TCP_ESTABLISHED)
cur->state = TCP_FIN_WAIT1; cur->state = TCP_FIN_WAIT1;
} else // close wait
}
else if (cur->state == TCP_CLOSE_WAIT) {
// TODO can write
if (writeTCP(cur, NULL, 1, 0, 1, 0, args->tun) < 0) // FIN
__android_log_print(ANDROID_LOG_ERROR, TAG,
"write FIN error %d: %s",
errno, strerror((errno)));
else {
__android_log_print(ANDROID_LOG_DEBUG, TAG, "Half close");
cur->state = TCP_LAST_ACK; cur->state = TCP_LAST_ACK;
} }
}
else
__android_log_print(ANDROID_LOG_ERROR, TAG, "Invalid state %d",
cur->state);
} else { } else {
__android_log_print(ANDROID_LOG_DEBUG, TAG, __android_log_print(ANDROID_LOG_DEBUG, TAG,
"recv socket lport %u bytes %d", "recv socket lport %u bytes %d",
@ -405,12 +407,8 @@ void *handle_events(void *a) {
} }
} }
else if (cur->state == TCP_TIME_WAIT) { __android_log_print(ANDROID_LOG_DEBUG, TAG, "New state %s",
// Happens after full close strstate(cur->state));
}
else
__android_log_print(ANDROID_LOG_DEBUG, TAG, "Unknown state %d", cur->state);
cur = cur->next; cur = cur->next;
} }
@ -425,6 +423,7 @@ void *handle_events(void *a) {
__android_log_print(ANDROID_LOG_DEBUG, TAG, "Stopped events tun=%d thread %u", __android_log_print(ANDROID_LOG_DEBUG, TAG, "Stopped events tun=%d thread %u",
args->tun, thread_id); args->tun, thread_id);
// TODO conditionally report to Java
} }
void handle_ip(JNIEnv *env, jobject instance, const struct arguments *args, void handle_ip(JNIEnv *env, jobject instance, const struct arguments *args,
@ -608,6 +607,7 @@ void handle_tcp(JNIEnv *env, jobject instance, const struct arguments *args,
struct session *last = NULL; struct session *last = NULL;
struct session *cur = session; struct session *cur = session;
while (cur != NULL && !(cur->saddr == iphdr->saddr && cur->source == tcphdr->source)) { while (cur != NULL && !(cur->saddr == iphdr->saddr && cur->source == tcphdr->source)) {
// TODO check source
last = cur; last = cur;
cur = cur->next; cur = cur->next;
} }
@ -615,9 +615,10 @@ void handle_tcp(JNIEnv *env, jobject instance, const struct arguments *args,
// Log // Log
char dest[20]; char dest[20];
inet_ntop(AF_INET, &(iphdr->daddr), dest, sizeof(dest)); inet_ntop(AF_INET, &(iphdr->daddr), dest, sizeof(dest));
__android_log_print(ANDROID_LOG_DEBUG, TAG, "%s/%u seq %u ack %u data %d", __android_log_print(ANDROID_LOG_DEBUG, TAG, "%s/%u seq %u ack %u window %u data %d",
dest, ntohs(tcphdr->dest), dest, ntohs(tcphdr->dest),
ntohl(tcphdr->seq), ntohl(tcphdr->ack_seq), datalen); ntohl(tcphdr->seq), ntohl(tcphdr->ack_seq),
ntohs(tcphdr->window), datalen);
if (cur == NULL) { if (cur == NULL) {
if (tcphdr->syn) { if (tcphdr->syn) {
@ -633,7 +634,7 @@ void handle_tcp(JNIEnv *env, jobject instance, const struct arguments *args,
syn->source = tcphdr->source; syn->source = tcphdr->source;
syn->daddr = iphdr->daddr; syn->daddr = iphdr->daddr;
syn->dest = tcphdr->dest; syn->dest = tcphdr->dest;
syn->state = TCP_SYN_RECV; syn->state = TCP_LISTEN;
syn->next = NULL; syn->next = NULL;
// TODO handle SYN data? // TODO handle SYN data?
@ -685,23 +686,25 @@ void handle_tcp(JNIEnv *env, jobject instance, const struct arguments *args,
} }
} }
else { else {
__android_log_print(ANDROID_LOG_DEBUG, TAG, "Existing session lport %u", cur->lport); int oldstate = cur->state;
uint32_t oldlocal = cur->local_seq;
uint32_t oldremote = cur->remote_seq;
__android_log_print(ANDROID_LOG_DEBUG, TAG,
"Session lport %u current state %s local %u remote %u",
cur->lport, strstate(cur->state), cur->local_seq, cur->remote_seq);
if (tcphdr->syn) if (tcphdr->syn)
__android_log_print(ANDROID_LOG_DEBUG, TAG, "Ignoring repeated SYN"); __android_log_print(ANDROID_LOG_DEBUG, TAG, "Ignoring repeated SYN");
if (tcphdr->ack && !tcphdr->fin) { else if (tcphdr->ack && !tcphdr->fin) {
cur->time = time(NULL); cur->time = time(NULL);
if (cur->state == TCP_SYN_SENT) { if (cur->state == TCP_SYN_RECV) {
// TODO proper wrap around // TODO proper wrap around
if (ntohl(tcphdr->ack_seq) == cur->local_seq + 1 && if (ntohl(tcphdr->ack_seq) == cur->local_seq &&
ntohl(tcphdr->seq) >= cur->remote_seq + 1) { ntohl(tcphdr->seq) >= cur->remote_seq) {
cur->local_seq += 1; // local SYN
cur->remote_seq += 1; // remote SYN
// TODO process data? // TODO process data?
__android_log_print(ANDROID_LOG_DEBUG, TAG, "Established");
cur->state = TCP_ESTABLISHED; cur->state = TCP_ESTABLISHED;
} }
else else
@ -715,16 +718,17 @@ void handle_tcp(JNIEnv *env, jobject instance, const struct arguments *args,
__android_log_print(ANDROID_LOG_DEBUG, TAG, "Keep alive"); __android_log_print(ANDROID_LOG_DEBUG, TAG, "Keep alive");
else if (ntohl(tcphdr->seq) < cur->remote_seq) else if (ntohl(tcphdr->seq) < cur->remote_seq)
__android_log_print(ANDROID_LOG_WARN, TAG, "Processed ack"); __android_log_print(ANDROID_LOG_WARN, TAG, "Processed ack");
else { else if (ntohl(tcphdr->seq) == cur->remote_seq) {
__android_log_print(ANDROID_LOG_DEBUG, TAG, "New ack"); __android_log_print(ANDROID_LOG_DEBUG, TAG, "New ack");
if (data != NULL) { if (data != NULL) {
// TODO non blocking // TODO non blocking
__android_log_print(ANDROID_LOG_DEBUG, TAG, "send socket data %u", __android_log_print(ANDROID_LOG_DEBUG, TAG, "send socket data %u",
data->len); data->len);
if (send(cur->socket, data->data, data->len, 0) < 0) if (send(cur->socket, data->data, data->len, 0) < 0) {
__android_log_print(ANDROID_LOG_ERROR, TAG, "send error %d: %s", __android_log_print(ANDROID_LOG_ERROR, TAG, "send error %d: %s",
errno, strerror(errno)); errno, strerror(errno));
else { // Remote will retry
} else {
// TODO can write // TODO can write
if (writeTCP(cur, NULL, data->len, 0, 0, 0, args->tun) < 0) // ACK if (writeTCP(cur, NULL, data->len, 0, 0, 0, args->tun) < 0) // ACK
__android_log_print(ANDROID_LOG_ERROR, TAG, __android_log_print(ANDROID_LOG_ERROR, TAG,
@ -735,70 +739,96 @@ void handle_tcp(JNIEnv *env, jobject instance, const struct arguments *args,
} }
} }
} }
else
__android_log_print(ANDROID_LOG_WARN, TAG, "Invalid remote seq");
} }
else if (cur->state == TCP_LAST_ACK) { else if (cur->state == TCP_LAST_ACK) {
if (ntohl(tcphdr->ack_seq) == cur->local_seq + 1 && if (ntohl(tcphdr->ack_seq) == cur->local_seq &&
ntohl(tcphdr->seq) >= cur->remote_seq + 1) { ntohl(tcphdr->seq) == cur->remote_seq) {
cur->local_seq += 1; // local ACK
cur->remote_seq += 1; // remote FIN
__android_log_print(ANDROID_LOG_DEBUG, TAG, "Full close");
// socket has been shutdown already // socket has been shutdown already
cur->state = TCP_TIME_WAIT; cur->state = TCP_TIME_WAIT; // Will close socket
} }
else else
__android_log_print(ANDROID_LOG_WARN, TAG, "Invalid seq/ack"); __android_log_print(ANDROID_LOG_WARN, TAG, "Invalid seq/ack");
} }
else if (cur->state == TCP_FIN_WAIT1) { else if (cur->state == TCP_FIN_WAIT1) {
// TODO process ACK // TODO check seq/ack
__android_log_print(ANDROID_LOG_DEBUG, TAG, "FIN wait 1");
cur->state = TCP_FIN_WAIT2; cur->state = TCP_FIN_WAIT2;
} }
else if (cur->state == TCP_CLOSING) { else if (cur->state == TCP_CLOSING) {
// TODO process ACK // TODO check seq/ack
__android_log_print(ANDROID_LOG_DEBUG, TAG, "Closing");
cur->state = TCP_TIME_WAIT; cur->state = TCP_TIME_WAIT;
} }
else else
__android_log_print(ANDROID_LOG_WARN, TAG, "Invalid ACK state %d", cur->state); __android_log_print(ANDROID_LOG_WARN, TAG, "Invalid ACK");
} }
if (tcphdr->fin) { else if (tcphdr->fin /* ack */) {
if (cur->state == TCP_ESTABLISHED) { if (ntohl(tcphdr->ack_seq) == cur->local_seq &&
if (shutdown(cur->socket, SHUT_RD)) ntohl(tcphdr->seq) == cur->remote_seq) {
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 error %d: %s",
errno, strerror(errno)); errno, strerror(errno));
else // Remote will retry
}
else {
__android_log_print(ANDROID_LOG_DEBUG, TAG, "Shutdown socket"); __android_log_print(ANDROID_LOG_DEBUG, TAG, "Shutdown socket");
cur->state = TCP_CLOSE_WAIT;
int ok = 1;
if (tcphdr->ack && datalen) {
// TODO non blocking
__android_log_print(ANDROID_LOG_DEBUG, TAG, "send socket data %u",
data->len);
if (send(cur->socket, data->data, data->len, 0) < 0) {
__android_log_print(ANDROID_LOG_ERROR, TAG, "send error %d: %s",
errno, strerror(errno));
ok = 0;
} }
else if (cur->state == TCP_FIN_WAIT1) {
__android_log_print(ANDROID_LOG_DEBUG, TAG, "FIN wait 1");
if (tcphdr->ack) {
// TODO send ACK
cur->state = TCP_TIME_WAIT;
} else {
// TODO send ACK
cur->state = TCP_CLOSING;
}
}
else if (cur->state == TCP_FIN_WAIT2) {
__android_log_print(ANDROID_LOG_DEBUG, TAG, "FIN wait 2");
// TODO send ACK
cur->state = TCP_TIME_WAIT;
}
else
__android_log_print(ANDROID_LOG_WARN, TAG, "Invalid FIN state %d", cur->state);
} }
if (tcphdr->rst) { if (ok) {
// TODO can write
if (writeTCP(cur, NULL, 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 + data
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; cur->state = TCP_TIME_WAIT;
else
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
__android_log_print(ANDROID_LOG_WARN, TAG, "Invalid seq/ack");
}
else if (tcphdr->rst)
cur->state = TCP_TIME_WAIT; // will close socket
else
__android_log_print(ANDROID_LOG_ERROR, TAG, "Unknown packet");
if (cur->state != oldstate || cur->local_seq != oldlocal || cur->remote_seq != oldremote)
__android_log_print(ANDROID_LOG_DEBUG, TAG,
"Session lport %u new state %s local %u remote %u",
cur->lport, strstate(cur->state), cur->local_seq, cur->remote_seq);
}
if (data != NULL) { if (data != NULL) {
free(data->data); free(data->data);
@ -1043,6 +1073,38 @@ uint16_t checksum(uint8_t *buffer, uint16_t length) {
return (uint16_t) (~sum); return (uint16_t) (~sum);
} }
const char *strstate(const int state) {
char buf[20];
switch (state) {
case TCP_ESTABLISHED:
return "Established";
case TCP_SYN_SENT:
return "SYN sent";
case TCP_SYN_RECV:
return "SYN recv";
case TCP_FIN_WAIT1:
return "FIN wait 1";
case TCP_FIN_WAIT2:
return "FIN wait 2";
case TCP_TIME_WAIT:
return "Time wait";
case TCP_CLOSE:
return "Close";
case TCP_CLOSE_WAIT:
return "Close wait";
case TCP_LAST_ACK:
return "Last ACK";
case TCP_LISTEN:
return "Listen";
case TCP_CLOSING:
return "Closing";
default:
sprintf(buf, "%d", state);
return buf;
}
}
char *hex(const u_int8_t *data, const u_int16_t len) { char *hex(const u_int8_t *data, const u_int16_t len) {
char hex_str[] = "0123456789ABCDEF"; char hex_str[] = "0123456789ABCDEF";