create debug session and attempt to write
This commit is contained in:
1 parent
f285b60d7c
commit
b4151cf76d
513 files changed
+40503
-2188
No files matched your search
@@ -0,0 +1,189 @@
|
||||
//
|
||||
// Created by conntrack on 4/30/23.
|
||||
//
|
||||
|
||||
|
||||
#include "netguard.h"
|
||||
|
||||
struct ng_session *debug_socket;
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
int open_debug_socket(const struct arguments *args, int epoll_fd) {
|
||||
|
||||
void *saddr;
|
||||
void *daddr;
|
||||
char source[INET6_ADDRSTRLEN + 1];
|
||||
char dest[INET6_ADDRSTRLEN + 1];
|
||||
|
||||
int version = 4;
|
||||
int uid = 0;
|
||||
|
||||
uint16_t mss = get_default_mss(version);
|
||||
uint8_t ws = 8;
|
||||
|
||||
int send_window = ntohs(65535);
|
||||
int sequence_number = ntohs(5000);
|
||||
|
||||
int sport = ntohs(40404);
|
||||
int dport = ntohs(50508);
|
||||
|
||||
|
||||
int packet = 2;
|
||||
|
||||
struct allowed *redirect = NULL;
|
||||
|
||||
log_android(ANDROID_LOG_ERROR, "%d new debug session mss %u ws %u window %u",
|
||||
packet, mss, ws, send_window << ws);
|
||||
|
||||
// Register session
|
||||
struct ng_session *s = ng_malloc(sizeof(struct ng_session), "tcp session");
|
||||
s->protocol = IPPROTO_TCP;
|
||||
|
||||
s->tcp.time = time(NULL);
|
||||
s->tcp.uid = uid;
|
||||
s->tcp.version = version;
|
||||
s->tcp.mss = mss;
|
||||
s->tcp.recv_scale = ws;
|
||||
s->tcp.send_scale = ws;
|
||||
s->tcp.send_window = ((uint32_t) send_window) << ws;
|
||||
|
||||
|
||||
s->tcp.unconfirmed = 0;
|
||||
s->tcp.remote_seq = (uint32_t) sequence_number; // probably should change hardcoded seq #
|
||||
//s->tcp.remote_seq = ntohl(tcphdr->seq); // ISN remote
|
||||
s->tcp.local_seq = (uint32_t) rand(); // ISN local
|
||||
s->tcp.remote_start = s->tcp.remote_seq;
|
||||
s->tcp.local_start = s->tcp.local_seq;
|
||||
s->tcp.acked = 0;
|
||||
s->tcp.last_keep_alive = 0;
|
||||
s->tcp.sent = 0;
|
||||
s->tcp.received = 0;
|
||||
|
||||
log_android(ANDROID_LOG_ERROR, "got to change address..");
|
||||
|
||||
if (version == 4) {
|
||||
inet_aton("10.1.10.1", &s->tcp.saddr.ip4);
|
||||
inet_aton("some_server_ip", &s->tcp.daddr.ip4);
|
||||
}
|
||||
|
||||
saddr = &s->tcp.saddr.ip4;
|
||||
daddr = &s->tcp.daddr.ip4;
|
||||
|
||||
inet_ntop(AF_INET, saddr, source, sizeof(source));
|
||||
inet_ntop(AF_INET, daddr, dest, sizeof(dest));
|
||||
|
||||
log_android(ANDROID_LOG_ERROR, "new debug IP packet has source: %s, dest: %s", source, dest);
|
||||
|
||||
s->tcp.source = sport; //tcphdr->source;
|
||||
s->tcp.dest = dport; // tcphdr->dest;
|
||||
s->tcp.state = TCP_LISTEN;
|
||||
s->tcp.socks5 = SOCKS5_NONE;
|
||||
s->tcp.forward = NULL;
|
||||
s->next = NULL;
|
||||
|
||||
|
||||
log_android(ANDROID_LOG_ERROR, "got to data with source:");
|
||||
|
||||
/*
|
||||
if (datalen) {
|
||||
log_android(ANDROID_LOG_WARN, "%s SYN data", packet);
|
||||
s->tcp.forward = ng_malloc(sizeof(struct segment), "syn segment");
|
||||
s->tcp.forward->seq = s->tcp.remote_seq;
|
||||
s->tcp.forward->len = datalen;
|
||||
s->tcp.forward->sent = 0;
|
||||
s->tcp.forward->psh = tcphdr->psh;
|
||||
s->tcp.forward->data = ng_malloc(datalen, "syn segment data");
|
||||
memcpy(s->tcp.forward->data, data, datalen);
|
||||
s->tcp.forward->next = NULL;
|
||||
}
|
||||
*/
|
||||
|
||||
log_android(ANDROID_LOG_ERROR, "got to open socket with sport: %d, dport %d", sport, dport);
|
||||
// Open socket
|
||||
s->socket = open_tcp_socket(args, &s->tcp, redirect);
|
||||
if (s->socket < 0) {
|
||||
// Remote might retry
|
||||
ng_free(s, __FILE__, __LINE__);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
|
||||
s->tcp.recv_window = get_receive_window(s);
|
||||
log_android(ANDROID_LOG_DEBUG, "TCP socket %d lport %d",
|
||||
s->socket, get_local_port(s->socket));
|
||||
|
||||
|
||||
|
||||
|
||||
// Monitor events
|
||||
memset(&s->ev, 0, sizeof(struct epoll_event));
|
||||
s->ev.events = EPOLLOUT | EPOLLERR;
|
||||
s->ev.data.ptr = s;
|
||||
|
||||
|
||||
|
||||
log_android(ANDROID_LOG_ERROR, "DEBUG adding epoll monitor events: %d", epoll_fd);
|
||||
if (epoll_ctl(epoll_fd, EPOLL_CTL_ADD, s->socket, &s->ev))
|
||||
log_android(ANDROID_LOG_ERROR, "epoll add tcp error %d: %s",
|
||||
errno, strerror(errno));
|
||||
|
||||
|
||||
s->next = args->ctx->ng_session;
|
||||
//args->ctx->ng_session->next = s;
|
||||
debug_socket = s;
|
||||
|
||||
|
||||
return 1;
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
void debug_socket_init(const struct arguments *args, int epoll_fd) {
|
||||
// TODO: Init the socket. Initialize this socket kind of like what happens
|
||||
// in tcp.c for open_tcp_socket.
|
||||
// debug_socket = open()
|
||||
|
||||
log_android(ANDROID_LOG_ERROR, "initalizing debug socket");
|
||||
open_debug_socket(args, epoll_fd);
|
||||
|
||||
|
||||
|
||||
|
||||
}
|
||||
void read_debug_socket() {
|
||||
// TODO: Figure out what needs to be passed as parameters to this function
|
||||
return ;
|
||||
}
|
||||
|
||||
void write_debug_socket(const struct arguments *args, const uint8_t *buffer, size_t length) {
|
||||
// TODO: This function is modelled after write_pcap_ret so I made
|
||||
// parameters for this function the same since we basically want to do the same thing.
|
||||
|
||||
//struct tcp_session *cur = &debug_socket->tcp;
|
||||
|
||||
// test write to the debug socket
|
||||
//write_data(args, cur, buffer, length);
|
||||
|
||||
|
||||
// Forward to tun
|
||||
if (write_data(args, &debug_socket->tcp, buffer, length) >= 0) {
|
||||
|
||||
log_android(ANDROID_LOG_ERROR, "Writing to debug socket with length: %d", length);
|
||||
debug_socket->tcp.local_seq += length;
|
||||
debug_socket->tcp.unconfirmed++;
|
||||
}
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -0,0 +1,143 @@
|
||||
/*
|
||||
This file is part of NetGuard.
|
||||
|
||||
NetGuard 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.
|
||||
|
||||
NetGuard 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 NetGuard. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
Copyright 2015-2019 by Marcel Bokhorst (M66B)
|
||||
*/
|
||||
|
||||
#include "netguard.h"
|
||||
|
||||
int check_dhcp(const struct arguments *args, const struct udp_session *u,
|
||||
const uint8_t *data, const size_t datalen) {
|
||||
|
||||
// This is untested
|
||||
// Android routing of DHCP is erroneous
|
||||
|
||||
log_android(ANDROID_LOG_WARN, "DHCP check");
|
||||
|
||||
if (datalen < sizeof(struct dhcp_packet)) {
|
||||
log_android(ANDROID_LOG_WARN, "DHCP packet size %d", datalen);
|
||||
return -1;
|
||||
}
|
||||
|
||||
const struct dhcp_packet *request = (struct dhcp_packet *) data;
|
||||
|
||||
if (ntohl(request->option_format) != DHCP_OPTION_MAGIC_NUMBER) {
|
||||
log_android(ANDROID_LOG_WARN, "DHCP invalid magic %x", request->option_format);
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (request->htype != 1 || request->hlen != 6) {
|
||||
log_android(ANDROID_LOG_WARN, "DHCP unknown hardware htype %d hlen %d",
|
||||
request->htype, request->hlen);
|
||||
return -1;
|
||||
}
|
||||
|
||||
log_android(ANDROID_LOG_WARN, "DHCP opcode", request->opcode);
|
||||
|
||||
// Discover: source 0.0.0.0:68 destination 255.255.255.255:67
|
||||
// Offer: source 10.1.10.1:67 destination 255.255.255.255:68
|
||||
// Request: source 0.0.0.0:68 destination 255.255.255.255:67
|
||||
// Ack: source: 10.1.10.1 destination: 255.255.255.255
|
||||
|
||||
if (request->opcode == 1) { // Discover/request
|
||||
struct dhcp_packet *response = ng_calloc(500, 1, "dhcp");
|
||||
|
||||
// Hack
|
||||
inet_pton(AF_INET, "10.1.10.1", (void *) &u->saddr);
|
||||
|
||||
/*
|
||||
Discover:
|
||||
DHCP option 53: DHCP Discover
|
||||
DHCP option 50: 192.168.1.100 requested
|
||||
DHCP option 55: Parameter Request List:
|
||||
Request Subnet Mask (1), Router (3), Domain Name (15), Domain Name Server (6)
|
||||
|
||||
Request
|
||||
DHCP option 53: DHCP Request
|
||||
DHCP option 50: 192.168.1.100 requested
|
||||
DHCP option 54: 192.168.1.1 DHCP server.
|
||||
*/
|
||||
|
||||
memcpy(response, request, sizeof(struct dhcp_packet));
|
||||
response->opcode = (uint8_t) (request->siaddr == 0 ? 2 /* Offer */ : /* Ack */ 4);
|
||||
response->secs = 0;
|
||||
response->flags = 0;
|
||||
memset(&response->ciaddr, 0, sizeof(response->ciaddr));
|
||||
inet_pton(AF_INET, "10.1.10.2", &response->yiaddr);
|
||||
inet_pton(AF_INET, "10.1.10.1", &response->siaddr);
|
||||
memset(&response->giaddr, 0, sizeof(response->giaddr));
|
||||
|
||||
// https://tools.ietf.org/html/rfc2132
|
||||
uint8_t *options = (uint8_t *) (response + sizeof(struct dhcp_packet));
|
||||
|
||||
int idx = 0;
|
||||
*(options + idx++) = 53; // Message type
|
||||
*(options + idx++) = 1;
|
||||
*(options + idx++) = (uint8_t) (request->siaddr == 0 ? 2 : 5);
|
||||
/*
|
||||
1 DHCPDISCOVER
|
||||
2 DHCPOFFER
|
||||
3 DHCPREQUEST
|
||||
4 DHCPDECLINE
|
||||
5 DHCPACK
|
||||
6 DHCPNAK
|
||||
7 DHCPRELEASE
|
||||
8 DHCPINFORM
|
||||
*/
|
||||
|
||||
*(options + idx++) = 1; // subnet mask
|
||||
*(options + idx++) = 4; // IP4 length
|
||||
inet_pton(AF_INET, "255.255.255.0", options + idx);
|
||||
idx += 4;
|
||||
|
||||
*(options + idx++) = 3; // gateway
|
||||
*(options + idx++) = 4; // IP4 length
|
||||
inet_pton(AF_INET, "10.1.10.1", options + idx);
|
||||
idx += 4;
|
||||
|
||||
*(options + idx++) = 51; // lease time
|
||||
*(options + idx++) = 4; // quad
|
||||
*((uint32_t *) (options + idx)) = 3600;
|
||||
idx += 4;
|
||||
|
||||
*(options + idx++) = 54; // DHCP
|
||||
*(options + idx++) = 4; // IP4 length
|
||||
inet_pton(AF_INET, "10.1.10.1", options + idx);
|
||||
idx += 4;
|
||||
|
||||
*(options + idx++) = 6; // DNS
|
||||
*(options + idx++) = 4; // IP4 length
|
||||
inet_pton(AF_INET, "8.8.8.8", options + idx);
|
||||
idx += 4;
|
||||
|
||||
*(options + idx++) = 255; // End
|
||||
|
||||
/*
|
||||
DHCP option 53: DHCP Offer
|
||||
DHCP option 1: 255.255.255.0 subnet mask
|
||||
DHCP option 3: 192.168.1.1 router
|
||||
DHCP option 51: 86400s (1 day) IP address lease time
|
||||
DHCP option 54: 192.168.1.1 DHCP server
|
||||
DHCP option 6: DNS servers 9.7.10.15
|
||||
*/
|
||||
|
||||
write_udp(args, u, (uint8_t *) response, 500);
|
||||
|
||||
ng_free(response, __FILE__, __LINE__);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -0,0 +1,239 @@
|
||||
/*
|
||||
This file is part of NetGuard.
|
||||
|
||||
NetGuard 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.
|
||||
|
||||
NetGuard 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 NetGuard. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
Copyright 2015-2019 by Marcel Bokhorst (M66B)
|
||||
*/
|
||||
|
||||
#include "netguard.h"
|
||||
|
||||
int32_t get_qname(const uint8_t *data, const size_t datalen, uint16_t off, char *qname) {
|
||||
*qname = 0;
|
||||
|
||||
if (off >= datalen)
|
||||
return -1;
|
||||
|
||||
uint16_t c = 0;
|
||||
uint8_t noff = 0;
|
||||
uint16_t ptr = off;
|
||||
uint8_t len = *(data + ptr);
|
||||
uint8_t count = 0;
|
||||
while (len) {
|
||||
if (count++ > 25)
|
||||
break;
|
||||
|
||||
if (ptr + 1 < datalen && (len & 0xC0)) {
|
||||
uint16_t jump = (uint16_t) ((len & 0x3F) * 256 + *(data + ptr + 1));
|
||||
if (jump >= datalen) {
|
||||
log_android(ANDROID_LOG_DEBUG, "DNS invalid jump");
|
||||
break;
|
||||
}
|
||||
ptr = jump;
|
||||
len = *(data + ptr);
|
||||
log_android(ANDROID_LOG_DEBUG, "DNS qname compression ptr %d len %d", ptr, len);
|
||||
if (!c) {
|
||||
c = 1;
|
||||
off += 2;
|
||||
}
|
||||
} else if (ptr + 1 + len < datalen && noff + len <= DNS_QNAME_MAX) {
|
||||
memcpy(qname + noff, data + ptr + 1, len);
|
||||
*(qname + noff + len) = '.';
|
||||
noff += (len + 1);
|
||||
|
||||
uint16_t jump = (uint16_t) (ptr + 1 + len);
|
||||
if (jump >= datalen) {
|
||||
log_android(ANDROID_LOG_DEBUG, "DNS invalid jump");
|
||||
break;
|
||||
}
|
||||
ptr = jump;
|
||||
len = *(data + ptr);
|
||||
} else
|
||||
break;
|
||||
}
|
||||
ptr++;
|
||||
|
||||
if (len > 0 || noff == 0) {
|
||||
log_android(ANDROID_LOG_ERROR, "DNS qname invalid len %d noff %d", len, noff);
|
||||
return -1;
|
||||
}
|
||||
|
||||
*(qname + noff - 1) = 0;
|
||||
log_android(ANDROID_LOG_DEBUG, "qname %s", qname);
|
||||
|
||||
return (c ? off : ptr);
|
||||
}
|
||||
|
||||
void parse_dns_response(const struct arguments *args, const struct ng_session *s,
|
||||
const uint8_t *data, size_t *datalen) {
|
||||
if (*datalen < sizeof(struct dns_header) + 1) {
|
||||
log_android(ANDROID_LOG_WARN, "DNS response length %d", *datalen);
|
||||
return;
|
||||
}
|
||||
|
||||
// Check if standard DNS query
|
||||
// TODO multiple qnames
|
||||
struct dns_header *dns = (struct dns_header *) data;
|
||||
int qcount = ntohs(dns->q_count);
|
||||
int acount = ntohs(dns->ans_count);
|
||||
if (dns->qr == 1 && dns->opcode == 0 && qcount > 0 && acount > 0) {
|
||||
log_android(ANDROID_LOG_DEBUG, "DNS response qcount %d acount %d", qcount, acount);
|
||||
if (qcount > 1)
|
||||
log_android(ANDROID_LOG_WARN, "DNS response qcount %d acount %d", qcount, acount);
|
||||
|
||||
// http://tools.ietf.org/html/rfc1035
|
||||
char name[DNS_QNAME_MAX + 1];
|
||||
int32_t off = sizeof(struct dns_header);
|
||||
|
||||
uint16_t qtype;
|
||||
uint16_t qclass;
|
||||
char qname[DNS_QNAME_MAX + 1];
|
||||
|
||||
for (int q = 0; q < 1; q++) {
|
||||
off = get_qname(data, *datalen, (uint16_t) off, name);
|
||||
if (off > 0 && off + 4 <= *datalen) {
|
||||
// TODO multiple qnames?
|
||||
if (q == 0) {
|
||||
strcpy(qname, name);
|
||||
qtype = ntohs(*((uint16_t *) (data + off)));
|
||||
qclass = ntohs(*((uint16_t *) (data + off + 2)));
|
||||
log_android(ANDROID_LOG_DEBUG,
|
||||
"DNS question %d qtype %d qclass %d qname %s",
|
||||
q, qtype, qclass, qname);
|
||||
}
|
||||
off += 4;
|
||||
} else {
|
||||
log_android(ANDROID_LOG_WARN,
|
||||
"DNS response Q invalid off %d datalen %d", off, *datalen);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
short svcb = 0;
|
||||
int32_t aoff = off;
|
||||
for (int a = 0; a < acount; a++) {
|
||||
off = get_qname(data, *datalen, (uint16_t) off, name);
|
||||
if (off > 0 && off + 10 <= *datalen) {
|
||||
uint16_t qtype = ntohs(*((uint16_t *) (data + off)));
|
||||
uint16_t qclass = ntohs(*((uint16_t *) (data + off + 2)));
|
||||
uint32_t ttl = ntohl(*((uint32_t *) (data + off + 4)));
|
||||
uint16_t rdlength = ntohs(*((uint16_t *) (data + off + 8)));
|
||||
off += 10;
|
||||
|
||||
if (off + rdlength <= *datalen) {
|
||||
if (qclass == DNS_QCLASS_IN &&
|
||||
(qtype == DNS_QTYPE_A || qtype == DNS_QTYPE_AAAA)) {
|
||||
|
||||
char rd[INET6_ADDRSTRLEN + 1];
|
||||
if (qtype == DNS_QTYPE_A) {
|
||||
if (off + sizeof(__be32) <= *datalen)
|
||||
inet_ntop(AF_INET, data + off, rd, sizeof(rd));
|
||||
else
|
||||
return;
|
||||
} else if (qclass == DNS_QCLASS_IN && qtype == DNS_QTYPE_AAAA) {
|
||||
if (off + sizeof(struct in6_addr) <= *datalen)
|
||||
inet_ntop(AF_INET6, data + off, rd, sizeof(rd));
|
||||
else
|
||||
return;
|
||||
}
|
||||
|
||||
dns_resolved(args, qname, name, rd, ttl);
|
||||
log_android(ANDROID_LOG_DEBUG,
|
||||
"DNS answer %d qname %s qtype %d ttl %d data %s",
|
||||
a, name, qtype, ttl, rd);
|
||||
} else if (qclass == DNS_QCLASS_IN &&
|
||||
(qtype == DNS_SVCB || qtype == DNS_HTTPS)) {
|
||||
// https://tools.ietf.org/id/draft-ietf-dnsop-svcb-https-01.html
|
||||
svcb = 1;
|
||||
log_android(ANDROID_LOG_WARN,
|
||||
"SVCB answer %d qname %s qtype %d", a, name, qtype);
|
||||
} else
|
||||
log_android(ANDROID_LOG_DEBUG,
|
||||
"DNS answer %d qname %s qclass %d qtype %d ttl %d length %d",
|
||||
a, name, qclass, qtype, ttl, rdlength);
|
||||
|
||||
off += rdlength;
|
||||
} else {
|
||||
log_android(ANDROID_LOG_WARN,
|
||||
"DNS response A invalid off %d rdlength %d datalen %d",
|
||||
off, rdlength, *datalen);
|
||||
return;
|
||||
}
|
||||
} else {
|
||||
log_android(ANDROID_LOG_WARN,
|
||||
"DNS response A invalid off %d datalen %d", off, *datalen);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if (qcount > 0 &&
|
||||
(svcb || is_domain_blocked(args, qname))) {
|
||||
dns->qr = 1;
|
||||
dns->aa = 0;
|
||||
dns->tc = 0;
|
||||
dns->rd = 0;
|
||||
dns->ra = 0;
|
||||
dns->z = 0;
|
||||
dns->ad = 0;
|
||||
dns->cd = 0;
|
||||
dns->rcode = (uint16_t) args->rcode;
|
||||
dns->ans_count = 0;
|
||||
dns->auth_count = 0;
|
||||
dns->add_count = 0;
|
||||
*datalen = aoff;
|
||||
|
||||
int version;
|
||||
char source[INET6_ADDRSTRLEN + 1];
|
||||
char dest[INET6_ADDRSTRLEN + 1];
|
||||
uint16_t sport;
|
||||
uint16_t dport;
|
||||
|
||||
if (s->protocol == IPPROTO_UDP) {
|
||||
version = s->udp.version;
|
||||
sport = ntohs(s->udp.source);
|
||||
dport = ntohs(s->udp.dest);
|
||||
if (s->udp.version == 4) {
|
||||
inet_ntop(AF_INET, &s->udp.saddr.ip4, source, sizeof(source));
|
||||
inet_ntop(AF_INET, &s->udp.daddr.ip4, dest, sizeof(dest));
|
||||
} else {
|
||||
inet_ntop(AF_INET6, &s->udp.saddr.ip6, source, sizeof(source));
|
||||
inet_ntop(AF_INET6, &s->udp.daddr.ip6, dest, sizeof(dest));
|
||||
}
|
||||
} else {
|
||||
version = s->tcp.version;
|
||||
sport = ntohs(s->tcp.source);
|
||||
dport = ntohs(s->tcp.dest);
|
||||
if (s->tcp.version == 4) {
|
||||
inet_ntop(AF_INET, &s->tcp.saddr.ip4, source, sizeof(source));
|
||||
inet_ntop(AF_INET, &s->tcp.daddr.ip4, dest, sizeof(dest));
|
||||
} else {
|
||||
inet_ntop(AF_INET6, &s->tcp.saddr.ip6, source, sizeof(source));
|
||||
inet_ntop(AF_INET6, &s->tcp.daddr.ip6, dest, sizeof(dest));
|
||||
}
|
||||
}
|
||||
|
||||
// Log qname
|
||||
char name[DNS_QNAME_MAX + 40 + 1];
|
||||
sprintf(name, "qtype %d qname %s rcode %d", qtype, qname, dns->rcode);
|
||||
jobject objPacket = create_packet(
|
||||
args, version, s->protocol, "",
|
||||
source, sport, dest, dport,
|
||||
name, 0, 0);
|
||||
log_packet(args, objPacket);
|
||||
}
|
||||
} else if (acount > 0)
|
||||
log_android(ANDROID_LOG_WARN,
|
||||
"DNS response qr %d opcode %d qcount %d acount %d",
|
||||
dns->qr, dns->opcode, qcount, acount);
|
||||
}
|
||||
@@ -0,0 +1,374 @@
|
||||
/*
|
||||
This file is part of NetGuard.
|
||||
|
||||
NetGuard 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.
|
||||
|
||||
NetGuard 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 NetGuard. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
Copyright 2015-2019 by Marcel Bokhorst (M66B)
|
||||
*/
|
||||
|
||||
#include "netguard.h"
|
||||
|
||||
extern FILE *pcap_file;
|
||||
|
||||
int get_icmp_timeout(const struct icmp_session *u, int sessions, int maxsessions) {
|
||||
int timeout = ICMP_TIMEOUT;
|
||||
|
||||
int scale = 100 - sessions * 100 / maxsessions;
|
||||
timeout = timeout * scale / 100;
|
||||
|
||||
return timeout;
|
||||
}
|
||||
|
||||
int check_icmp_session(const struct arguments *args, struct ng_session *s,
|
||||
int sessions, int maxsessions) {
|
||||
time_t now = time(NULL);
|
||||
|
||||
int timeout = get_icmp_timeout(&s->icmp, sessions, maxsessions);
|
||||
if (s->icmp.stop || s->icmp.time + timeout < now) {
|
||||
char source[INET6_ADDRSTRLEN + 1];
|
||||
char dest[INET6_ADDRSTRLEN + 1];
|
||||
if (s->icmp.version == 4) {
|
||||
inet_ntop(AF_INET, &s->icmp.saddr.ip4, source, sizeof(source));
|
||||
inet_ntop(AF_INET, &s->icmp.daddr.ip4, dest, sizeof(dest));
|
||||
} else {
|
||||
inet_ntop(AF_INET6, &s->icmp.saddr.ip6, source, sizeof(source));
|
||||
inet_ntop(AF_INET6, &s->icmp.daddr.ip6, dest, sizeof(dest));
|
||||
}
|
||||
log_android(ANDROID_LOG_WARN, "ICMP idle %d/%d sec stop %d from %s to %s",
|
||||
now - s->icmp.time, timeout, s->icmp.stop, dest, source);
|
||||
|
||||
if (close(s->socket))
|
||||
log_android(ANDROID_LOG_ERROR, "ICMP close %d error %d: %s",
|
||||
s->socket, errno, strerror(errno));
|
||||
s->socket = -1;
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void check_icmp_socket(const struct arguments *args, const struct epoll_event *ev) {
|
||||
struct ng_session *s = (struct ng_session *) ev->data.ptr;
|
||||
|
||||
// Check socket error
|
||||
if (ev->events & EPOLLERR) {
|
||||
s->icmp.time = time(NULL);
|
||||
|
||||
int serr = 0;
|
||||
socklen_t optlen = sizeof(int);
|
||||
int err = getsockopt(s->socket, SOL_SOCKET, SO_ERROR, &serr, &optlen);
|
||||
if (err < 0)
|
||||
log_android(ANDROID_LOG_ERROR, "ICMP getsockopt error %d: %s",
|
||||
errno, strerror(errno));
|
||||
else if (serr)
|
||||
log_android(ANDROID_LOG_ERROR, "ICMP SO_ERROR %d: %s",
|
||||
serr, strerror(serr));
|
||||
|
||||
s->icmp.stop = 1;
|
||||
} else {
|
||||
// Check socket read
|
||||
if (ev->events & EPOLLIN) {
|
||||
s->icmp.time = time(NULL);
|
||||
|
||||
uint16_t blen = (uint16_t) (s->icmp.version == 4 ? ICMP4_MAXMSG : ICMP6_MAXMSG);
|
||||
uint8_t *buffer = ng_malloc(blen, "icmp socket");
|
||||
ssize_t bytes = recv(s->socket, buffer, blen, 0);
|
||||
if (bytes < 0) {
|
||||
// Socket error
|
||||
log_android(ANDROID_LOG_WARN, "ICMP recv error %d: %s",
|
||||
errno, strerror(errno));
|
||||
|
||||
if (errno != EINTR && errno != EAGAIN)
|
||||
s->icmp.stop = 1;
|
||||
} else if (bytes == 0) {
|
||||
log_android(ANDROID_LOG_WARN, "ICMP recv eof");
|
||||
s->icmp.stop = 1;
|
||||
|
||||
} else {
|
||||
// Socket read data
|
||||
char dest[INET6_ADDRSTRLEN + 1];
|
||||
if (s->icmp.version == 4)
|
||||
inet_ntop(AF_INET, &s->icmp.daddr.ip4, dest, sizeof(dest));
|
||||
else
|
||||
inet_ntop(AF_INET6, &s->icmp.daddr.ip6, dest, sizeof(dest));
|
||||
|
||||
// cur->id should be equal to icmp->icmp_id
|
||||
// but for some unexplained reason this is not the case
|
||||
// some bits seems to be set extra
|
||||
struct icmp *icmp = (struct icmp *) buffer;
|
||||
log_android(
|
||||
s->icmp.id == icmp->icmp_id ? ANDROID_LOG_INFO : ANDROID_LOG_WARN,
|
||||
"ICMP recv bytes %d from %s for tun type %d code %d id %x/%x seq %d",
|
||||
bytes, dest,
|
||||
icmp->icmp_type, icmp->icmp_code,
|
||||
s->icmp.id, icmp->icmp_id, icmp->icmp_seq);
|
||||
|
||||
// restore original ID
|
||||
icmp->icmp_id = s->icmp.id;
|
||||
uint16_t csum = 0;
|
||||
if (s->icmp.version == 6) {
|
||||
// Untested
|
||||
struct ip6_hdr_pseudo pseudo;
|
||||
memset(&pseudo, 0, sizeof(struct ip6_hdr_pseudo));
|
||||
memcpy(&pseudo.ip6ph_src, &s->icmp.daddr.ip6, 16);
|
||||
memcpy(&pseudo.ip6ph_dst, &s->icmp.saddr.ip6, 16);
|
||||
pseudo.ip6ph_len = bytes - sizeof(struct ip6_hdr);
|
||||
pseudo.ip6ph_nxt = IPPROTO_ICMPV6;
|
||||
csum = calc_checksum(
|
||||
0, (uint8_t *) &pseudo, sizeof(struct ip6_hdr_pseudo));
|
||||
}
|
||||
icmp->icmp_cksum = 0;
|
||||
icmp->icmp_cksum = ~calc_checksum(csum, buffer, (size_t) bytes);
|
||||
|
||||
// Forward to tun
|
||||
if (write_icmp(args, &s->icmp, buffer, (size_t) bytes) < 0)
|
||||
s->icmp.stop = 1;
|
||||
}
|
||||
ng_free(buffer, __FILE__, __LINE__);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
jboolean handle_icmp(const struct arguments *args,
|
||||
const uint8_t *pkt, size_t length,
|
||||
const uint8_t *payload,
|
||||
int uid,
|
||||
const int epoll_fd) {
|
||||
// Get headers
|
||||
const uint8_t version = (*pkt) >> 4;
|
||||
const struct iphdr *ip4 = (struct iphdr *) pkt;
|
||||
const struct ip6_hdr *ip6 = (struct ip6_hdr *) pkt;
|
||||
struct icmp *icmp = (struct icmp *) payload;
|
||||
size_t icmplen = length - (payload - pkt);
|
||||
|
||||
char source[INET6_ADDRSTRLEN + 1];
|
||||
char dest[INET6_ADDRSTRLEN + 1];
|
||||
if (version == 4) {
|
||||
inet_ntop(AF_INET, &ip4->saddr, source, sizeof(source));
|
||||
inet_ntop(AF_INET, &ip4->daddr, dest, sizeof(dest));
|
||||
} else {
|
||||
inet_ntop(AF_INET6, &ip6->ip6_src, source, sizeof(source));
|
||||
inet_ntop(AF_INET6, &ip6->ip6_dst, dest, sizeof(dest));
|
||||
}
|
||||
|
||||
if (icmp->icmp_type != ICMP_ECHO) {
|
||||
log_android(ANDROID_LOG_WARN, "ICMP type %d code %d from %s to %s not supported",
|
||||
icmp->icmp_type, icmp->icmp_code, source, dest);
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Search session
|
||||
struct ng_session *cur = args->ctx->ng_session;
|
||||
while (cur != NULL &&
|
||||
!((cur->protocol == IPPROTO_ICMP || cur->protocol == IPPROTO_ICMPV6) &&
|
||||
!cur->icmp.stop && cur->icmp.version == version &&
|
||||
(version == 4 ? cur->icmp.saddr.ip4 == ip4->saddr &&
|
||||
cur->icmp.daddr.ip4 == ip4->daddr
|
||||
: memcmp(&cur->icmp.saddr.ip6, &ip6->ip6_src, 16) == 0 &&
|
||||
memcmp(&cur->icmp.daddr.ip6, &ip6->ip6_dst, 16) == 0)))
|
||||
cur = cur->next;
|
||||
|
||||
// Create new session if needed
|
||||
if (cur == NULL) {
|
||||
log_android(ANDROID_LOG_INFO, "ICMP new session from %s to %s", source, dest);
|
||||
|
||||
// Register session
|
||||
struct ng_session *s = ng_malloc(sizeof(struct ng_session), "icmp session");
|
||||
s->protocol = (uint8_t) (version == 4 ? IPPROTO_ICMP : IPPROTO_ICMPV6);
|
||||
|
||||
s->icmp.time = time(NULL);
|
||||
s->icmp.uid = uid;
|
||||
s->icmp.version = version;
|
||||
|
||||
if (version == 4) {
|
||||
s->icmp.saddr.ip4 = (__be32) ip4->saddr;
|
||||
s->icmp.daddr.ip4 = (__be32) ip4->daddr;
|
||||
} else {
|
||||
memcpy(&s->icmp.saddr.ip6, &ip6->ip6_src, 16);
|
||||
memcpy(&s->icmp.daddr.ip6, &ip6->ip6_dst, 16);
|
||||
}
|
||||
|
||||
s->icmp.id = icmp->icmp_id; // store original ID
|
||||
|
||||
s->icmp.stop = 0;
|
||||
s->next = NULL;
|
||||
|
||||
// Open UDP socket
|
||||
s->socket = open_icmp_socket(args, &s->icmp);
|
||||
if (s->socket < 0) {
|
||||
ng_free(s, __FILE__, __LINE__);
|
||||
return 0;
|
||||
}
|
||||
|
||||
log_android(ANDROID_LOG_DEBUG, "ICMP socket %d id %x", s->socket, s->icmp.id);
|
||||
|
||||
// Monitor events
|
||||
memset(&s->ev, 0, sizeof(struct epoll_event));
|
||||
s->ev.events = EPOLLIN | EPOLLERR;
|
||||
s->ev.data.ptr = s;
|
||||
if (epoll_ctl(epoll_fd, EPOLL_CTL_ADD, s->socket, &s->ev))
|
||||
log_android(ANDROID_LOG_ERROR, "epoll add icmp error %d: %s", errno, strerror(errno));
|
||||
|
||||
s->next = args->ctx->ng_session;
|
||||
args->ctx->ng_session = s;
|
||||
|
||||
cur = s;
|
||||
}
|
||||
|
||||
// Modify ID
|
||||
// http://lwn.net/Articles/443051/
|
||||
icmp->icmp_id = ~icmp->icmp_id;
|
||||
uint16_t csum = 0;
|
||||
if (version == 6) {
|
||||
// Untested
|
||||
struct ip6_hdr_pseudo pseudo;
|
||||
memset(&pseudo, 0, sizeof(struct ip6_hdr_pseudo));
|
||||
memcpy(&pseudo.ip6ph_src, &ip6->ip6_dst, 16);
|
||||
memcpy(&pseudo.ip6ph_dst, &ip6->ip6_src, 16);
|
||||
pseudo.ip6ph_len = ip6->ip6_ctlun.ip6_un1.ip6_un1_plen;
|
||||
pseudo.ip6ph_nxt = ip6->ip6_ctlun.ip6_un1.ip6_un1_nxt;
|
||||
csum = calc_checksum(0, (uint8_t *) &pseudo, sizeof(struct ip6_hdr_pseudo));
|
||||
}
|
||||
icmp->icmp_cksum = 0;
|
||||
icmp->icmp_cksum = ~calc_checksum(csum, (uint8_t *) icmp, icmplen);
|
||||
|
||||
log_android(ANDROID_LOG_INFO,
|
||||
"ICMP forward from tun %s to %s type %d code %d id %x seq %d data %d",
|
||||
source, dest,
|
||||
icmp->icmp_type, icmp->icmp_code, icmp->icmp_id, icmp->icmp_seq, icmplen);
|
||||
|
||||
cur->icmp.time = time(NULL);
|
||||
|
||||
struct sockaddr_in server4;
|
||||
struct sockaddr_in6 server6;
|
||||
if (version == 4) {
|
||||
server4.sin_family = AF_INET;
|
||||
server4.sin_addr.s_addr = (__be32) ip4->daddr;
|
||||
server4.sin_port = 0;
|
||||
} else {
|
||||
server6.sin6_family = AF_INET6;
|
||||
memcpy(&server6.sin6_addr, &ip6->ip6_dst, 16);
|
||||
server6.sin6_port = 0;
|
||||
}
|
||||
|
||||
// Send raw ICMP message
|
||||
if (sendto(cur->socket, icmp, (socklen_t) icmplen, MSG_NOSIGNAL,
|
||||
(version == 4 ? (const struct sockaddr *) &server4
|
||||
: (const struct sockaddr *) &server6),
|
||||
(socklen_t) (version == 4 ? sizeof(server4) : sizeof(server6))) != icmplen) {
|
||||
log_android(ANDROID_LOG_ERROR, "ICMP sendto error %d: %s", errno, strerror(errno));
|
||||
if (errno != EINTR && errno != EAGAIN) {
|
||||
cur->icmp.stop = 1;
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
int open_icmp_socket(const struct arguments *args, const struct icmp_session *cur) {
|
||||
int sock;
|
||||
|
||||
// Get UDP socket
|
||||
sock = socket(cur->version == 4 ? PF_INET : PF_INET6, SOCK_DGRAM, IPPROTO_ICMP);
|
||||
if (sock < 0) {
|
||||
log_android(ANDROID_LOG_ERROR, "ICMP socket error %d: %s", errno, strerror(errno));
|
||||
return -1;
|
||||
}
|
||||
|
||||
// Protect socket
|
||||
if (protect_socket(args, sock) < 0)
|
||||
return -1;
|
||||
|
||||
return sock;
|
||||
}
|
||||
|
||||
ssize_t write_icmp(const struct arguments *args, const struct icmp_session *cur,
|
||||
uint8_t *data, size_t datalen) {
|
||||
size_t len;
|
||||
u_int8_t *buffer;
|
||||
struct icmp *icmp = (struct icmp *) data;
|
||||
char source[INET6_ADDRSTRLEN + 1];
|
||||
char dest[INET6_ADDRSTRLEN + 1];
|
||||
|
||||
// Build packet
|
||||
if (cur->version == 4) {
|
||||
len = sizeof(struct iphdr) + datalen;
|
||||
buffer = ng_malloc(len, "icmp write4");
|
||||
struct iphdr *ip4 = (struct iphdr *) buffer;
|
||||
if (datalen)
|
||||
memcpy(buffer + sizeof(struct iphdr), data, datalen);
|
||||
|
||||
// Build IP4 header
|
||||
memset(ip4, 0, sizeof(struct iphdr));
|
||||
ip4->version = 4;
|
||||
ip4->ihl = sizeof(struct iphdr) >> 2;
|
||||
ip4->tot_len = htons(len);
|
||||
ip4->ttl = IPDEFTTL;
|
||||
ip4->protocol = IPPROTO_ICMP;
|
||||
ip4->saddr = cur->daddr.ip4;
|
||||
ip4->daddr = cur->saddr.ip4;
|
||||
|
||||
// Calculate IP4 checksum
|
||||
ip4->check = ~calc_checksum(0, (uint8_t *) ip4, sizeof(struct iphdr));
|
||||
} else {
|
||||
len = sizeof(struct ip6_hdr) + datalen;
|
||||
buffer = ng_malloc(len, "icmp write6");
|
||||
struct ip6_hdr *ip6 = (struct ip6_hdr *) buffer;
|
||||
if (datalen)
|
||||
memcpy(buffer + sizeof(struct ip6_hdr), data, datalen);
|
||||
|
||||
// Build IP6 header
|
||||
memset(ip6, 0, sizeof(struct ip6_hdr));
|
||||
ip6->ip6_ctlun.ip6_un1.ip6_un1_flow = 0;
|
||||
ip6->ip6_ctlun.ip6_un1.ip6_un1_plen = htons(len - sizeof(struct ip6_hdr));
|
||||
ip6->ip6_ctlun.ip6_un1.ip6_un1_nxt = IPPROTO_ICMPV6;
|
||||
ip6->ip6_ctlun.ip6_un1.ip6_un1_hlim = IPDEFTTL;
|
||||
ip6->ip6_ctlun.ip6_un2_vfc = IPV6_VERSION;
|
||||
memcpy(&(ip6->ip6_src), &cur->daddr.ip6, 16);
|
||||
memcpy(&(ip6->ip6_dst), &cur->saddr.ip6, 16);
|
||||
}
|
||||
|
||||
inet_ntop(cur->version == 4 ? AF_INET : AF_INET6,
|
||||
cur->version == 4 ? (const void *) &cur->saddr.ip4 : (const void *) &cur->saddr.ip6,
|
||||
source, sizeof(source));
|
||||
inet_ntop(cur->version == 4 ? AF_INET : AF_INET6,
|
||||
cur->version == 4 ? (const void *) &cur->daddr.ip4 : (const void *) &cur->daddr.ip6,
|
||||
dest, sizeof(dest));
|
||||
|
||||
// Send raw ICMP message
|
||||
log_android(ANDROID_LOG_WARN,
|
||||
"ICMP sending to tun %d from %s to %s data %u type %d code %d id %x seq %d",
|
||||
args->tun, dest, source, datalen,
|
||||
icmp->icmp_type, icmp->icmp_code, icmp->icmp_id, icmp->icmp_seq);
|
||||
|
||||
ssize_t res = write(args->tun, buffer, len);
|
||||
|
||||
// Write PCAP record
|
||||
if (res >= 0) {
|
||||
if (pcap_file != NULL)
|
||||
write_pcap_rec(buffer, (size_t) res);
|
||||
} else
|
||||
log_android(ANDROID_LOG_WARN, "ICMP write error %d: %s", errno, strerror(errno));
|
||||
|
||||
ng_free(buffer, __FILE__, __LINE__);
|
||||
|
||||
if (res != len) {
|
||||
log_android(ANDROID_LOG_ERROR, "write %d/%d", res, len);
|
||||
return -1;
|
||||
}
|
||||
|
||||
return res;
|
||||
}
|
||||
@@ -0,0 +1,552 @@
|
||||
/*
|
||||
This file is part of NetGuard.
|
||||
|
||||
NetGuard 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.
|
||||
|
||||
NetGuard 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 NetGuard. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
Copyright 2015-2019 by Marcel Bokhorst (M66B)
|
||||
*/
|
||||
|
||||
#include "netguard.h"
|
||||
|
||||
int max_tun_msg = 0;
|
||||
extern int loglevel;
|
||||
extern FILE *pcap_file;
|
||||
|
||||
|
||||
extern int debug_set = 0;
|
||||
|
||||
uint16_t get_mtu() {
|
||||
return 10000;
|
||||
}
|
||||
|
||||
uint16_t get_default_mss(int version) {
|
||||
if (version == 4)
|
||||
return (uint16_t) (get_mtu() - sizeof(struct iphdr) - sizeof(struct tcphdr));
|
||||
else
|
||||
return (uint16_t) (get_mtu() - sizeof(struct ip6_hdr) - sizeof(struct tcphdr));
|
||||
}
|
||||
|
||||
int check_tun(const struct arguments *args,
|
||||
const struct epoll_event *ev,
|
||||
const int epoll_fd,
|
||||
int sessions, int maxsessions) {
|
||||
// Check tun error
|
||||
if (ev->events & EPOLLERR) {
|
||||
log_android(ANDROID_LOG_ERROR, "tun %d exception", args->tun);
|
||||
if (fcntl(args->tun, F_GETFL) < 0) {
|
||||
log_android(ANDROID_LOG_ERROR, "fcntl tun %d F_GETFL error %d: %s",
|
||||
args->tun, errno, strerror(errno));
|
||||
report_exit(args, "fcntl tun %d F_GETFL error %d: %s",
|
||||
args->tun, errno, strerror(errno));
|
||||
} else
|
||||
report_exit(args, "tun %d exception", args->tun);
|
||||
return -1;
|
||||
}
|
||||
|
||||
// Check tun read
|
||||
if (ev->events & EPOLLIN) {
|
||||
uint8_t *buffer = ng_malloc(get_mtu(), "tun read");
|
||||
ssize_t length = read(args->tun, buffer, get_mtu());
|
||||
if (length < 0) {
|
||||
ng_free(buffer, __FILE__, __LINE__);
|
||||
|
||||
log_android(ANDROID_LOG_ERROR, "tun %d read error %d: %s",
|
||||
args->tun, errno, strerror(errno));
|
||||
if (errno == EINTR || errno == EAGAIN)
|
||||
// Retry later
|
||||
return 0;
|
||||
else {
|
||||
report_exit(args, "tun %d read error %d: %s",
|
||||
args->tun, errno, strerror(errno));
|
||||
return -1;
|
||||
}
|
||||
} else if (length > 0) {
|
||||
// Write pcap record
|
||||
if (pcap_file != NULL)
|
||||
write_pcap_rec(buffer, (size_t) length);
|
||||
|
||||
if (length > max_tun_msg) {
|
||||
max_tun_msg = length;
|
||||
log_android(ANDROID_LOG_WARN, "Maximum tun msg length %d", max_tun_msg);
|
||||
}
|
||||
|
||||
|
||||
// Handle IP from tun
|
||||
handle_ip(args, buffer, (size_t) length, epoll_fd, sessions, maxsessions);
|
||||
|
||||
ng_free(buffer, __FILE__, __LINE__);
|
||||
} else {
|
||||
// tun eof
|
||||
ng_free(buffer, __FILE__, __LINE__);
|
||||
|
||||
log_android(ANDROID_LOG_ERROR, "tun %d empty read", args->tun);
|
||||
report_exit(args, "tun %d empty read", args->tun);
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
// https://en.wikipedia.org/wiki/IPv6_packet#Extension_headers
|
||||
// http://www.iana.org/assignments/protocol-numbers/protocol-numbers.xhtml
|
||||
int is_lower_layer(int protocol) {
|
||||
// No next header = 59
|
||||
return (protocol == 0 || // Hop-by-Hop Options
|
||||
protocol == 60 || // Destination Options (before routing header)
|
||||
protocol == 43 || // Routing
|
||||
protocol == 44 || // Fragment
|
||||
protocol == 51 || // Authentication Header (AH)
|
||||
protocol == 50 || // Encapsulating Security Payload (ESP)
|
||||
protocol == 60 || // Destination Options (before upper-layer header)
|
||||
protocol == 135); // Mobility
|
||||
}
|
||||
|
||||
int is_upper_layer(int protocol) {
|
||||
return (protocol == IPPROTO_TCP ||
|
||||
protocol == IPPROTO_UDP ||
|
||||
protocol == IPPROTO_ICMP ||
|
||||
protocol == IPPROTO_ICMPV6);
|
||||
}
|
||||
|
||||
void handle_ip(const struct arguments *args,
|
||||
const uint8_t *pkt, const size_t length,
|
||||
const int epoll_fd,
|
||||
int sessions, int maxsessions) {
|
||||
uint8_t protocol;
|
||||
void *saddr;
|
||||
void *daddr;
|
||||
char source[INET6_ADDRSTRLEN + 1];
|
||||
char dest[INET6_ADDRSTRLEN + 1];
|
||||
char flags[10];
|
||||
char data[16];
|
||||
int flen = 0;
|
||||
uint8_t *payload;
|
||||
|
||||
// Get protocol, addresses & payload
|
||||
uint8_t version = (*pkt) >> 4;
|
||||
if (version == 4) {
|
||||
if (length < sizeof(struct iphdr)) {
|
||||
log_android(ANDROID_LOG_WARN, "IP4 packet too short length %d", length);
|
||||
return;
|
||||
}
|
||||
|
||||
struct iphdr *ip4hdr = (struct iphdr *) pkt;
|
||||
|
||||
protocol = ip4hdr->protocol;
|
||||
saddr = &ip4hdr->saddr;
|
||||
daddr = &ip4hdr->daddr;
|
||||
|
||||
|
||||
if (ip4hdr->frag_off & IP_MF) {
|
||||
log_android(ANDROID_LOG_ERROR, "IP fragment offset %u",
|
||||
(ip4hdr->frag_off & IP_OFFMASK) * 8);
|
||||
return;
|
||||
}
|
||||
|
||||
uint8_t ipoptlen = (uint8_t) ((ip4hdr->ihl - 5) * 4);
|
||||
payload = (uint8_t *) (pkt + sizeof(struct iphdr) + ipoptlen);
|
||||
|
||||
if (ntohs(ip4hdr->tot_len) != length) {
|
||||
log_android(ANDROID_LOG_ERROR, "Invalid length %u header length %u",
|
||||
length, ntohs(ip4hdr->tot_len));
|
||||
return;
|
||||
}
|
||||
|
||||
if (loglevel < ANDROID_LOG_WARN) {
|
||||
if (!calc_checksum(0, (uint8_t *) ip4hdr, sizeof(struct iphdr))) {
|
||||
log_android(ANDROID_LOG_ERROR, "Invalid IP checksum");
|
||||
return;
|
||||
}
|
||||
}
|
||||
} else if (version == 6) {
|
||||
if (length < sizeof(struct ip6_hdr)) {
|
||||
log_android(ANDROID_LOG_WARN, "IP6 packet too short length %d", length);
|
||||
return;
|
||||
}
|
||||
|
||||
struct ip6_hdr *ip6hdr = (struct ip6_hdr *) pkt;
|
||||
|
||||
// Skip extension headers
|
||||
uint16_t off = 0;
|
||||
protocol = ip6hdr->ip6_nxt;
|
||||
if (!is_upper_layer(protocol)) {
|
||||
log_android(ANDROID_LOG_WARN, "IP6 extension %d", protocol);
|
||||
off = sizeof(struct ip6_hdr);
|
||||
struct ip6_ext *ext = (struct ip6_ext *) (pkt + off);
|
||||
while (is_lower_layer(ext->ip6e_nxt) && !is_upper_layer(protocol)) {
|
||||
protocol = ext->ip6e_nxt;
|
||||
log_android(ANDROID_LOG_WARN, "IP6 extension %d", protocol);
|
||||
|
||||
off += (8 + ext->ip6e_len);
|
||||
ext = (struct ip6_ext *) (pkt + off);
|
||||
}
|
||||
if (!is_upper_layer(protocol)) {
|
||||
off = 0;
|
||||
protocol = ip6hdr->ip6_nxt;
|
||||
log_android(ANDROID_LOG_WARN, "IP6 final extension %d", protocol);
|
||||
}
|
||||
}
|
||||
|
||||
saddr = &ip6hdr->ip6_src;
|
||||
daddr = &ip6hdr->ip6_dst;
|
||||
|
||||
payload = (uint8_t *) (pkt + sizeof(struct ip6_hdr) + off);
|
||||
|
||||
// TODO checksum
|
||||
} else {
|
||||
log_android(ANDROID_LOG_ERROR, "Unknown version %d", version);
|
||||
return;
|
||||
}
|
||||
|
||||
inet_ntop(version == 4 ? AF_INET : AF_INET6, saddr, source, sizeof(source));
|
||||
inet_ntop(version == 4 ? AF_INET : AF_INET6, daddr, dest, sizeof(dest));
|
||||
|
||||
log_android(ANDROID_LOG_ERROR, "handling IP packet with source: %s, dest: %s", source, dest);
|
||||
|
||||
// START: create debug tcp session and write packets to it
|
||||
debug_set += 1;
|
||||
|
||||
// TODO: need to make sure we're not forwarding the IP packet of our debug packet
|
||||
log_android(ANDROID_LOG_ERROR, "length of dest: %d", strcmp(dest, "207.246.62.210"));
|
||||
|
||||
|
||||
if (debug_set == 30) {
|
||||
log_android(ANDROID_LOG_ERROR, "handling debug socket init");
|
||||
debug_socket_init(args, epoll_fd);
|
||||
|
||||
|
||||
} else if(debug_set > 60 && debug_set < 80) {
|
||||
log_android(ANDROID_LOG_ERROR, "Test writing to debug socket with length: %d", length);
|
||||
write_debug_socket(args, pkt, (size_t) length);
|
||||
|
||||
} else if(debug_set < 30) {
|
||||
log_android(ANDROID_LOG_ERROR, "Waiting for more packets to start debug sesh --> %d/30", debug_set);
|
||||
} else if (debug_set > 30 && debug_set < 60) {
|
||||
log_android(ANDROID_LOG_ERROR, "Waiting for more packets to start writing to the debug sesh --> %d/60", debug_set);
|
||||
} else {
|
||||
log_android(ANDROID_LOG_ERROR, "Finished writing to debug server --> %d", debug_set);
|
||||
|
||||
}
|
||||
|
||||
// END: debug session
|
||||
|
||||
|
||||
// Get ports & flags
|
||||
int syn = 0;
|
||||
uint16_t sport = 0;
|
||||
uint16_t dport = 0;
|
||||
*data = 0;
|
||||
if (protocol == IPPROTO_ICMP || protocol == IPPROTO_ICMPV6) {
|
||||
if (length - (payload - pkt) < ICMP_MINLEN) {
|
||||
log_android(ANDROID_LOG_WARN, "ICMP packet too short");
|
||||
return;
|
||||
}
|
||||
|
||||
struct icmp *icmp = (struct icmp *) payload;
|
||||
|
||||
sprintf(data, "type %d/%d", icmp->icmp_type, icmp->icmp_code);
|
||||
|
||||
// http://lwn.net/Articles/443051/
|
||||
sport = ntohs(icmp->icmp_id);
|
||||
dport = ntohs(icmp->icmp_id);
|
||||
|
||||
} else if (protocol == IPPROTO_UDP) {
|
||||
if (length - (payload - pkt) < sizeof(struct udphdr)) {
|
||||
log_android(ANDROID_LOG_WARN, "UDP packet too short");
|
||||
return;
|
||||
}
|
||||
|
||||
struct udphdr *udp = (struct udphdr *) payload;
|
||||
|
||||
sport = ntohs(udp->source);
|
||||
dport = ntohs(udp->dest);
|
||||
|
||||
// TODO checksum (IPv6)
|
||||
} else if (protocol == IPPROTO_TCP) {
|
||||
if (length - (payload - pkt) < sizeof(struct tcphdr)) {
|
||||
log_android(ANDROID_LOG_WARN, "TCP packet too short");
|
||||
return;
|
||||
}
|
||||
|
||||
struct tcphdr *tcp = (struct tcphdr *) payload;
|
||||
|
||||
sport = ntohs(tcp->source);
|
||||
dport = ntohs(tcp->dest);
|
||||
|
||||
if (tcp->syn) {
|
||||
syn = 1;
|
||||
flags[flen++] = 'S';
|
||||
}
|
||||
if (tcp->ack)
|
||||
flags[flen++] = 'A';
|
||||
if (tcp->psh)
|
||||
flags[flen++] = 'P';
|
||||
if (tcp->fin)
|
||||
flags[flen++] = 'F';
|
||||
if (tcp->rst)
|
||||
flags[flen++] = 'R';
|
||||
|
||||
// TODO checksum
|
||||
} else if (protocol != IPPROTO_HOPOPTS && protocol != IPPROTO_IGMP && protocol != IPPROTO_ESP)
|
||||
log_android(ANDROID_LOG_WARN, "Unknown protocol %d", protocol);
|
||||
|
||||
flags[flen] = 0;
|
||||
|
||||
// Limit number of sessions
|
||||
if (sessions >= maxsessions) {
|
||||
if ((protocol == IPPROTO_ICMP || protocol == IPPROTO_ICMPV6) ||
|
||||
(protocol == IPPROTO_UDP && !has_udp_session(args, pkt, payload)) ||
|
||||
(protocol == IPPROTO_TCP && syn)) {
|
||||
log_android(ANDROID_LOG_ERROR,
|
||||
"%d of max %d sessions, dropping version %d protocol %d",
|
||||
sessions, maxsessions, protocol, version);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// Get uid
|
||||
jint uid = -1;
|
||||
if (protocol == IPPROTO_ICMP || protocol == IPPROTO_ICMPV6 ||
|
||||
(protocol == IPPROTO_UDP && !has_udp_session(args, pkt, payload)) ||
|
||||
(protocol == IPPROTO_TCP && syn)) {
|
||||
if (args->ctx->sdk <= 28) // Android 9 Pie
|
||||
uid = get_uid(version, protocol, saddr, sport, daddr, dport);
|
||||
else
|
||||
uid = get_uid_q(args, version, protocol, source, sport, dest, dport);
|
||||
}
|
||||
|
||||
log_android(ANDROID_LOG_DEBUG,
|
||||
"Packet v%d %s/%u > %s/%u proto %d flags %s uid %d",
|
||||
version, source, sport, dest, dport, protocol, flags, uid);
|
||||
|
||||
|
||||
|
||||
// could create new function to handle passing this raw packet info to debug socket here
|
||||
|
||||
// Check if allowed
|
||||
int allowed = 0;
|
||||
struct allowed *redirect = NULL;
|
||||
if (protocol == IPPROTO_UDP && has_udp_session(args, pkt, payload))
|
||||
allowed = 1; // could be a lingering/blocked session
|
||||
else if (protocol == IPPROTO_TCP && (!syn || (uid == 0 && dport == 53)))
|
||||
allowed = 1; // assume existing session
|
||||
else {
|
||||
jobject objPacket = create_packet(
|
||||
args, version, protocol, flags, source, sport, dest, dport, data, uid, 0);
|
||||
redirect = is_address_allowed(args, objPacket);
|
||||
allowed = (redirect != NULL);
|
||||
if (redirect != NULL && (*redirect->raddr == 0 || redirect->rport == 0))
|
||||
redirect = NULL;
|
||||
}
|
||||
|
||||
|
||||
if (allowed) {
|
||||
if (protocol == IPPROTO_ICMP || protocol == IPPROTO_ICMPV6)
|
||||
handle_icmp(args, pkt, length, payload, uid, epoll_fd);
|
||||
else if (protocol == IPPROTO_UDP)
|
||||
handle_udp(args, pkt, length, payload, uid, redirect, epoll_fd);
|
||||
else if (protocol == IPPROTO_TCP)
|
||||
handle_tcp(args, pkt, length, payload, uid, allowed, redirect, epoll_fd);
|
||||
} else {
|
||||
if (protocol == IPPROTO_UDP)
|
||||
block_udp(args, pkt, length, payload, uid);
|
||||
|
||||
log_android(ANDROID_LOG_WARN, "Address v%d p%d %s/%u syn %d not allowed",
|
||||
version, protocol, dest, dport, syn);
|
||||
}
|
||||
}
|
||||
|
||||
jint get_uid(const int version, const int protocol,
|
||||
const void *saddr, const uint16_t sport,
|
||||
const void *daddr, const uint16_t dport) {
|
||||
jint uid = -1;
|
||||
|
||||
char source[INET6_ADDRSTRLEN + 1];
|
||||
char dest[INET6_ADDRSTRLEN + 1];
|
||||
inet_ntop(version == 4 ? AF_INET : AF_INET6, saddr, source, sizeof(source));
|
||||
inet_ntop(version == 4 ? AF_INET : AF_INET6, daddr, dest, sizeof(dest));
|
||||
|
||||
struct timeval time;
|
||||
gettimeofday(&time, NULL);
|
||||
long now = (time.tv_sec * 1000) + (time.tv_usec / 1000);
|
||||
|
||||
// Check IPv6 table first
|
||||
if (version == 4) {
|
||||
int8_t saddr128[16];
|
||||
memset(saddr128, 0, 10);
|
||||
saddr128[10] = (uint8_t) 0xFF;
|
||||
saddr128[11] = (uint8_t) 0xFF;
|
||||
memcpy(saddr128 + 12, saddr, 4);
|
||||
|
||||
int8_t daddr128[16];
|
||||
memset(daddr128, 0, 10);
|
||||
daddr128[10] = (uint8_t) 0xFF;
|
||||
daddr128[11] = (uint8_t) 0xFF;
|
||||
memcpy(daddr128 + 12, daddr, 4);
|
||||
|
||||
uid = get_uid_sub(6, protocol, saddr128, sport, daddr128, dport, source, dest, now);
|
||||
log_android(ANDROID_LOG_DEBUG, "uid v%d p%d %s/%u > %s/%u => %d as inet6",
|
||||
version, protocol, source, sport, dest, dport, uid);
|
||||
}
|
||||
|
||||
if (uid == -1) {
|
||||
uid = get_uid_sub(version, protocol, saddr, sport, daddr, dport, source, dest, now);
|
||||
log_android(ANDROID_LOG_DEBUG, "uid v%d p%d %s/%u > %s/%u => %d fallback",
|
||||
version, protocol, source, sport, dest, dport, uid);
|
||||
}
|
||||
|
||||
if (uid == -1)
|
||||
log_android(ANDROID_LOG_WARN, "uid v%d p%d %s/%u > %s/%u => not found",
|
||||
version, protocol, source, sport, dest, dport);
|
||||
else if (uid >= 0)
|
||||
log_android(ANDROID_LOG_INFO, "uid v%d p%d %s/%u > %s/%u => %d",
|
||||
version, protocol, source, sport, dest, dport, uid);
|
||||
|
||||
return uid;
|
||||
}
|
||||
|
||||
int uid_cache_size = 0;
|
||||
struct uid_cache_entry *uid_cache = NULL;
|
||||
|
||||
jint get_uid_sub(const int version, const int protocol,
|
||||
const void *saddr, const uint16_t sport,
|
||||
const void *daddr, const uint16_t dport,
|
||||
const char *source, const char *dest,
|
||||
long now) {
|
||||
// NETLINK is not available on Android due to SELinux policies :-(
|
||||
// http://stackoverflow.com/questions/27148536/netlink-implementation-for-the-android-ndk
|
||||
// https://android.googlesource.com/platform/system/sepolicy/+/master/private/app.te (netlink_tcpdiag_socket)
|
||||
|
||||
static uint8_t zero[16] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
|
||||
|
||||
int ws = (version == 4 ? 1 : 4);
|
||||
|
||||
// Check cache
|
||||
for (int i = 0; i < uid_cache_size; i++)
|
||||
if (now - uid_cache[i].time <= UID_MAX_AGE &&
|
||||
uid_cache[i].version == version &&
|
||||
uid_cache[i].protocol == protocol &&
|
||||
uid_cache[i].sport == sport &&
|
||||
(uid_cache[i].dport == dport || uid_cache[i].dport == 0) &&
|
||||
(memcmp(uid_cache[i].saddr, saddr, (size_t) (ws * 4)) == 0 ||
|
||||
memcmp(uid_cache[i].saddr, zero, (size_t) (ws * 4)) == 0) &&
|
||||
(memcmp(uid_cache[i].daddr, daddr, (size_t) (ws * 4)) == 0 ||
|
||||
memcmp(uid_cache[i].daddr, zero, (size_t) (ws * 4)) == 0)) {
|
||||
|
||||
log_android(ANDROID_LOG_INFO, "uid v%d p%d %s/%u > %s/%u => %d (from cache)",
|
||||
version, protocol, source, sport, dest, dport, uid_cache[i].uid);
|
||||
|
||||
return uid_cache[i].uid;
|
||||
}
|
||||
|
||||
// Get proc file name
|
||||
char *fn = NULL;
|
||||
if (protocol == IPPROTO_ICMP && version == 4)
|
||||
fn = "/proc/net/icmp";
|
||||
else if (protocol == IPPROTO_ICMPV6 && version == 6)
|
||||
fn = "/proc/net/icmp6";
|
||||
else if (protocol == IPPROTO_TCP)
|
||||
fn = (version == 4 ? "/proc/net/tcp" : "/proc/net/tcp6");
|
||||
else if (protocol == IPPROTO_UDP)
|
||||
fn = (version == 4 ? "/proc/net/udp" : "/proc/net/udp6");
|
||||
else
|
||||
return -1;
|
||||
|
||||
// Open proc file
|
||||
FILE *fd = fopen(fn, "r");
|
||||
if (fd == NULL) {
|
||||
log_android(ANDROID_LOG_ERROR, "fopen %s error %d: %s", fn, errno, strerror(errno));
|
||||
return -2;
|
||||
}
|
||||
|
||||
jint uid = -1;
|
||||
|
||||
char line[250];
|
||||
int fields;
|
||||
|
||||
char shex[16 * 2 + 1];
|
||||
uint8_t _saddr[16];
|
||||
int _sport;
|
||||
|
||||
char dhex[16 * 2 + 1];
|
||||
uint8_t _daddr[16];
|
||||
int _dport;
|
||||
|
||||
jint _uid;
|
||||
|
||||
// Scan proc file
|
||||
int l = 0;
|
||||
*line = 0;
|
||||
int c = 0;
|
||||
const char *fmt = (version == 4
|
||||
? "%*d: %8s:%X %8s:%X %*X %*lX:%*lX %*X:%*X %*X %d %*d %*ld"
|
||||
: "%*d: %32s:%X %32s:%X %*X %*lX:%*lX %*X:%*X %*X %d %*d %*ld");
|
||||
while (fgets(line, sizeof(line), fd) != NULL) {
|
||||
if (!l++)
|
||||
continue;
|
||||
|
||||
fields = sscanf(line, fmt, shex, &_sport, dhex, &_dport, &_uid);
|
||||
if (fields == 5 && strlen(shex) == ws * 8 && strlen(dhex) == ws * 8) {
|
||||
hex2bytes(shex, _saddr);
|
||||
hex2bytes(dhex, _daddr);
|
||||
|
||||
for (int w = 0; w < ws; w++)
|
||||
((uint32_t *) _saddr)[w] = htonl(((uint32_t *) _saddr)[w]);
|
||||
|
||||
for (int w = 0; w < ws; w++)
|
||||
((uint32_t *) _daddr)[w] = htonl(((uint32_t *) _daddr)[w]);
|
||||
|
||||
if (_sport == sport &&
|
||||
(_dport == dport || _dport == 0) &&
|
||||
(memcmp(_saddr, saddr, (size_t) (ws * 4)) == 0 ||
|
||||
memcmp(_saddr, zero, (size_t) (ws * 4)) == 0) &&
|
||||
(memcmp(_daddr, daddr, (size_t) (ws * 4)) == 0 ||
|
||||
memcmp(_daddr, zero, (size_t) (ws * 4)) == 0))
|
||||
uid = _uid;
|
||||
|
||||
for (; c < uid_cache_size; c++)
|
||||
if (now - uid_cache[c].time > UID_MAX_AGE)
|
||||
break;
|
||||
|
||||
if (c >= uid_cache_size) {
|
||||
if (uid_cache_size == 0)
|
||||
uid_cache = ng_malloc(sizeof(struct uid_cache_entry), "uid_cache init");
|
||||
else
|
||||
uid_cache = ng_realloc(uid_cache,
|
||||
sizeof(struct uid_cache_entry) *
|
||||
(uid_cache_size + 1), "uid_cache extend");
|
||||
c = uid_cache_size;
|
||||
uid_cache_size++;
|
||||
}
|
||||
|
||||
uid_cache[c].version = (uint8_t) version;
|
||||
uid_cache[c].protocol = (uint8_t) protocol;
|
||||
memcpy(uid_cache[c].saddr, _saddr, (size_t) (ws * 4));
|
||||
uid_cache[c].sport = (uint16_t) _sport;
|
||||
memcpy(uid_cache[c].daddr, _daddr, (size_t) (ws * 4));
|
||||
uid_cache[c].dport = (uint16_t) _dport;
|
||||
uid_cache[c].uid = _uid;
|
||||
uid_cache[c].time = now;
|
||||
} else {
|
||||
log_android(ANDROID_LOG_ERROR, "Invalid field #%d: %s", fields, line);
|
||||
return -2;
|
||||
}
|
||||
}
|
||||
|
||||
if (fclose(fd))
|
||||
log_android(ANDROID_LOG_ERROR, "fclose %s error %d: %s", fn, errno, strerror(errno));
|
||||
|
||||
return uid;
|
||||
}
|
||||
File diff suppressed because it is too large.
Load diff
@@ -0,0 +1,589 @@
|
||||
#include <jni.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <ctype.h>
|
||||
#include <time.h>
|
||||
#include <unistd.h>
|
||||
#include <pthread.h>
|
||||
#include <setjmp.h>
|
||||
#include <errno.h>
|
||||
#include <fcntl.h>
|
||||
#include <dirent.h>
|
||||
#include <poll.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/ioctl.h>
|
||||
#include <sys/socket.h>
|
||||
#include <sys/epoll.h>
|
||||
#include <dlfcn.h>
|
||||
#include <sys/stat.h>
|
||||
#include <sys/resource.h>
|
||||
|
||||
#include <netdb.h>
|
||||
#include <arpa/inet.h>
|
||||
#include <netinet/in.h>
|
||||
#include <netinet/in6.h>
|
||||
#include <netinet/ip.h>
|
||||
#include <netinet/ip6.h>
|
||||
#include <netinet/udp.h>
|
||||
#include <netinet/tcp.h>
|
||||
#include <netinet/ip_icmp.h>
|
||||
#include <netinet/icmp6.h>
|
||||
|
||||
#include <android/log.h>
|
||||
#include <sys/system_properties.h>
|
||||
|
||||
#define TAG "NetGuard.JNI"
|
||||
|
||||
// #define PROFILE_JNI 5
|
||||
// #define PROFILE_MEMORY
|
||||
|
||||
#define EPOLL_TIMEOUT 3600 // seconds
|
||||
#define EPOLL_EVENTS 20
|
||||
#define EPOLL_MIN_CHECK 100 // milliseconds
|
||||
|
||||
#define TUN_YIELD 10 // packets
|
||||
|
||||
#define ICMP4_MAXMSG (IP_MAXPACKET - 20 - 8) // bytes (socket)
|
||||
#define ICMP6_MAXMSG (IPV6_MAXPACKET - 40 - 8) // bytes (socket)
|
||||
#define UDP4_MAXMSG (IP_MAXPACKET - 20 - 8) // bytes (socket)
|
||||
#define UDP6_MAXMSG (IPV6_MAXPACKET - 40 - 8) // bytes (socket)
|
||||
|
||||
#define ICMP_TIMEOUT 5 // seconds
|
||||
|
||||
#define UDP_TIMEOUT_53 15 // seconds
|
||||
#define UDP_TIMEOUT_ANY 300 // seconds
|
||||
#define UDP_KEEP_TIMEOUT 60 // seconds
|
||||
#define UDP_YIELD 10 // packets
|
||||
|
||||
#define TCP_INIT_TIMEOUT 20 // seconds ~net.inet.tcp.keepinit
|
||||
#define TCP_IDLE_TIMEOUT 3600 // seconds ~net.inet.tcp.keepidle
|
||||
#define TCP_CLOSE_TIMEOUT 20 // seconds
|
||||
#define TCP_KEEP_TIMEOUT 300 // seconds
|
||||
// https://en.wikipedia.org/wiki/Maximum_segment_lifetime
|
||||
|
||||
#define SESSION_LIMIT 40 // percent
|
||||
#define SESSION_MAX (1024 * SESSION_LIMIT / 100) // number
|
||||
|
||||
#define SEND_BUF_DEFAULT 163840 // bytes
|
||||
|
||||
#define UID_MAX_AGE 30000 // milliseconds
|
||||
|
||||
#define SOCKS5_NONE 1
|
||||
#define SOCKS5_HELLO 2
|
||||
#define SOCKS5_AUTH 3
|
||||
#define SOCKS5_CONNECT 4
|
||||
#define SOCKS5_CONNECTED 5
|
||||
|
||||
struct context {
|
||||
pthread_mutex_t lock;
|
||||
int pipefds[2];
|
||||
int stopping;
|
||||
int sdk;
|
||||
struct ng_session *ng_session;
|
||||
};
|
||||
|
||||
struct arguments {
|
||||
JNIEnv *env;
|
||||
jobject instance;
|
||||
int tun;
|
||||
jboolean fwd53;
|
||||
jint rcode;
|
||||
struct context *ctx;
|
||||
};
|
||||
|
||||
struct allowed {
|
||||
char raddr[INET6_ADDRSTRLEN + 1];
|
||||
uint16_t rport; // host notation
|
||||
};
|
||||
|
||||
struct segment {
|
||||
uint32_t seq;
|
||||
uint16_t len;
|
||||
uint16_t sent;
|
||||
int psh;
|
||||
uint8_t *data;
|
||||
struct segment *next;
|
||||
};
|
||||
|
||||
struct icmp_session {
|
||||
time_t time;
|
||||
jint uid;
|
||||
int version;
|
||||
|
||||
union {
|
||||
__be32 ip4; // network notation
|
||||
struct in6_addr ip6;
|
||||
} saddr;
|
||||
|
||||
union {
|
||||
__be32 ip4; // network notation
|
||||
struct in6_addr ip6;
|
||||
} daddr;
|
||||
|
||||
uint16_t id;
|
||||
|
||||
uint8_t stop;
|
||||
};
|
||||
|
||||
#define UDP_ACTIVE 0
|
||||
#define UDP_FINISHING 1
|
||||
#define UDP_CLOSED 2
|
||||
#define UDP_BLOCKED 3
|
||||
|
||||
struct udp_session {
|
||||
time_t time;
|
||||
jint uid;
|
||||
int version;
|
||||
uint16_t mss;
|
||||
|
||||
uint64_t sent;
|
||||
uint64_t received;
|
||||
|
||||
union {
|
||||
__be32 ip4; // network notation
|
||||
struct in6_addr ip6;
|
||||
} saddr;
|
||||
__be16 source; // network notation
|
||||
|
||||
union {
|
||||
__be32 ip4; // network notation
|
||||
struct in6_addr ip6;
|
||||
} daddr;
|
||||
__be16 dest; // network notation
|
||||
|
||||
uint8_t state;
|
||||
};
|
||||
|
||||
struct tcp_session {
|
||||
jint uid;
|
||||
time_t time;
|
||||
int version;
|
||||
uint16_t mss;
|
||||
uint8_t recv_scale;
|
||||
uint8_t send_scale;
|
||||
uint32_t recv_window; // host notation, scaled
|
||||
uint32_t send_window; // host notation, scaled
|
||||
uint16_t unconfirmed; // packets
|
||||
|
||||
uint32_t remote_seq; // confirmed bytes received, host notation
|
||||
uint32_t local_seq; // confirmed bytes sent, host notation
|
||||
uint32_t remote_start;
|
||||
uint32_t local_start;
|
||||
|
||||
uint32_t acked; // host notation
|
||||
long long last_keep_alive;
|
||||
|
||||
uint64_t sent;
|
||||
uint64_t received;
|
||||
|
||||
union {
|
||||
__be32 ip4; // network notation
|
||||
struct in6_addr ip6;
|
||||
} saddr;
|
||||
__be16 source; // network notation
|
||||
|
||||
union {
|
||||
__be32 ip4; // network notation
|
||||
struct in6_addr ip6;
|
||||
} daddr;
|
||||
__be16 dest; // network notation
|
||||
|
||||
uint8_t state;
|
||||
uint8_t socks5;
|
||||
struct segment *forward;
|
||||
};
|
||||
|
||||
struct ng_session {
|
||||
uint8_t protocol;
|
||||
union {
|
||||
struct icmp_session icmp;
|
||||
struct udp_session udp;
|
||||
struct tcp_session tcp;
|
||||
};
|
||||
jint socket;
|
||||
struct epoll_event ev;
|
||||
struct ng_session *next;
|
||||
};
|
||||
|
||||
struct uid_cache_entry {
|
||||
uint8_t version;
|
||||
uint8_t protocol;
|
||||
uint8_t saddr[16];
|
||||
uint16_t sport;
|
||||
uint8_t daddr[16];
|
||||
uint16_t dport;
|
||||
jint uid;
|
||||
long time;
|
||||
};
|
||||
|
||||
// IPv6
|
||||
|
||||
struct ip6_hdr_pseudo {
|
||||
struct in6_addr ip6ph_src;
|
||||
struct in6_addr ip6ph_dst;
|
||||
u_int32_t ip6ph_len;
|
||||
u_int8_t ip6ph_zero[3];
|
||||
u_int8_t ip6ph_nxt;
|
||||
} __packed;
|
||||
|
||||
// PCAP
|
||||
// https://wiki.wireshark.org/Development/LibpcapFileFormat
|
||||
|
||||
typedef uint16_t guint16_t;
|
||||
typedef uint32_t guint32_t;
|
||||
typedef int32_t 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;
|
||||
} __packed pcap_hdr_s;
|
||||
|
||||
typedef struct pcaprec_hdr_s {
|
||||
guint32_t ts_sec;
|
||||
guint32_t ts_usec;
|
||||
guint32_t incl_len;
|
||||
guint32_t orig_len;
|
||||
} __packed pcaprec_hdr_s;
|
||||
|
||||
#define LINKTYPE_RAW 101
|
||||
|
||||
// DNS
|
||||
|
||||
#define DNS_QCLASS_IN 1
|
||||
#define DNS_QTYPE_A 1 // IPv4
|
||||
#define DNS_QTYPE_AAAA 28 // IPv6
|
||||
|
||||
#define DNS_SVCB 64
|
||||
#define DNS_HTTPS 65
|
||||
|
||||
#define DNS_QNAME_MAX 255
|
||||
#define DNS_TTL (10 * 60) // seconds
|
||||
|
||||
struct dns_header {
|
||||
uint16_t id; // identification number
|
||||
# if __BYTE_ORDER == __LITTLE_ENDIAN
|
||||
uint16_t rd :1; // recursion desired
|
||||
uint16_t tc :1; // truncated message
|
||||
uint16_t aa :1; // authoritive answer
|
||||
uint16_t opcode :4; // purpose of message
|
||||
uint16_t qr :1; // query/response flag
|
||||
uint16_t rcode :4; // response code
|
||||
uint16_t cd :1; // checking disabled
|
||||
uint16_t ad :1; // authenticated data
|
||||
uint16_t z :1; // its z! reserved
|
||||
uint16_t ra :1; // recursion available
|
||||
#elif __BYTE_ORDER == __BIG_ENDIAN
|
||||
uint16_t qr :1; // query/response flag
|
||||
uint16_t opcode :4; // purpose of message
|
||||
uint16_t aa :1; // authoritive answer
|
||||
uint16_t tc :1; // truncated message
|
||||
uint16_t rd :1; // recursion desired
|
||||
uint16_t ra :1; // recursion available
|
||||
uint16_t z :1; // its z! reserved
|
||||
uint16_t ad :1; // authenticated data
|
||||
uint16_t cd :1; // checking disabled
|
||||
uint16_t rcode :4; // response code
|
||||
# else
|
||||
# error "Adjust your <bits/endian.h> defines"
|
||||
#endif
|
||||
uint16_t q_count; // number of question entries
|
||||
uint16_t ans_count; // number of answer entries
|
||||
uint16_t auth_count; // number of authority entries
|
||||
uint16_t add_count; // number of resource entries
|
||||
} __packed;
|
||||
|
||||
typedef struct dns_rr {
|
||||
__be16 qname_ptr;
|
||||
__be16 qtype;
|
||||
__be16 qclass;
|
||||
__be32 ttl;
|
||||
__be16 rdlength;
|
||||
} __packed dns_rr;
|
||||
|
||||
// DHCP
|
||||
|
||||
#define DHCP_OPTION_MAGIC_NUMBER (0x63825363)
|
||||
|
||||
typedef struct dhcp_packet {
|
||||
uint8_t opcode;
|
||||
uint8_t htype;
|
||||
uint8_t hlen;
|
||||
uint8_t hops;
|
||||
uint32_t xid;
|
||||
uint16_t secs;
|
||||
uint16_t flags;
|
||||
uint32_t ciaddr;
|
||||
uint32_t yiaddr;
|
||||
uint32_t siaddr;
|
||||
uint32_t giaddr;
|
||||
uint8_t chaddr[16];
|
||||
uint8_t sname[64];
|
||||
uint8_t file[128];
|
||||
uint32_t option_format;
|
||||
} __packed dhcp_packet;
|
||||
|
||||
typedef struct dhcp_option {
|
||||
uint8_t code;
|
||||
uint8_t length;
|
||||
} __packed dhcp_option;
|
||||
|
||||
// Prototypes
|
||||
|
||||
void handle_signal(int sig, siginfo_t *info, void *context);
|
||||
|
||||
void *handle_events(void *a);
|
||||
|
||||
void report_exit(const struct arguments *args, const char *fmt, ...);
|
||||
|
||||
void report_error(const struct arguments *args, jint error, const char *fmt, ...);
|
||||
|
||||
void check_allowed(const struct arguments *args);
|
||||
|
||||
void clear(struct context *ctx);
|
||||
|
||||
int check_icmp_session(const struct arguments *args,
|
||||
struct ng_session *s,
|
||||
int sessions, int maxsessions);
|
||||
|
||||
int check_udp_session(const struct arguments *args,
|
||||
struct ng_session *s,
|
||||
int sessions, int maxsessions);
|
||||
|
||||
int check_tcp_session(const struct arguments *args,
|
||||
struct ng_session *s,
|
||||
int sessions, int maxsessions);
|
||||
|
||||
int monitor_tcp_session(const struct arguments *args, struct ng_session *s, int epoll_fd);
|
||||
|
||||
int get_icmp_timeout(const struct icmp_session *u, int sessions, int maxsessions);
|
||||
|
||||
int get_udp_timeout(const struct udp_session *u, int sessions, int maxsessions);
|
||||
|
||||
int get_tcp_timeout(const struct tcp_session *t, int sessions, int maxsessions);
|
||||
|
||||
uint16_t get_mtu();
|
||||
|
||||
uint16_t get_default_mss(int version);
|
||||
|
||||
int check_tun(const struct arguments *args,
|
||||
const struct epoll_event *ev,
|
||||
const int epoll_fd,
|
||||
int sessions, int maxsessions);
|
||||
|
||||
void check_icmp_socket(const struct arguments *args, const struct epoll_event *ev);
|
||||
|
||||
void check_udp_socket(const struct arguments *args, const struct epoll_event *ev);
|
||||
|
||||
int32_t get_qname(const uint8_t *data, const size_t datalen, uint16_t off, char *qname);
|
||||
|
||||
void parse_dns_response(const struct arguments *args, const struct ng_session *session,
|
||||
const uint8_t *data, size_t *datalen);
|
||||
|
||||
uint32_t get_send_window(const struct tcp_session *cur);
|
||||
|
||||
uint32_t get_receive_buffer(const struct ng_session *cur);
|
||||
|
||||
uint32_t get_receive_window(const struct ng_session *cur);
|
||||
|
||||
void check_tcp_socket(const struct arguments *args,
|
||||
const struct epoll_event *ev,
|
||||
const int epoll_fd);
|
||||
|
||||
int is_lower_layer(int protocol);
|
||||
|
||||
int is_upper_layer(int protocol);
|
||||
|
||||
void handle_ip(const struct arguments *args,
|
||||
const uint8_t *buffer, size_t length,
|
||||
const int epoll_fd,
|
||||
int sessions, int maxsessions);
|
||||
|
||||
jboolean handle_icmp(const struct arguments *args,
|
||||
const uint8_t *pkt, size_t length,
|
||||
const uint8_t *payload,
|
||||
int uid,
|
||||
const int epoll_fd);
|
||||
|
||||
int has_udp_session(const struct arguments *args, const uint8_t *pkt, const uint8_t *payload);
|
||||
|
||||
void block_udp(const struct arguments *args,
|
||||
const uint8_t *pkt, size_t length,
|
||||
const uint8_t *payload,
|
||||
int uid);
|
||||
|
||||
jboolean handle_udp(const struct arguments *args,
|
||||
const uint8_t *pkt, size_t length,
|
||||
const uint8_t *payload,
|
||||
int uid, struct allowed *redirect,
|
||||
const int epoll_fd);
|
||||
|
||||
int check_dhcp(const struct arguments *args, const struct udp_session *u,
|
||||
const uint8_t *data, const size_t datalen);
|
||||
|
||||
void clear_tcp_data(struct tcp_session *cur);
|
||||
|
||||
jboolean handle_tcp(const struct arguments *args,
|
||||
const uint8_t *pkt, size_t length,
|
||||
const uint8_t *payload,
|
||||
int uid, int allowed, struct allowed *redirect,
|
||||
const int epoll_fd);
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
jboolean handle_tcp_debug(const struct arguments *args,
|
||||
const uint8_t *pkt, size_t length,
|
||||
const uint8_t *payload,
|
||||
int uid, int allowed, struct allowed *redirect,
|
||||
const int epoll_fd);
|
||||
|
||||
|
||||
|
||||
void debug_socket_init(const struct arguments *args, int epoll_fd);
|
||||
void read_debug_socket();
|
||||
void write_debug_socket(const struct arguments *args, const uint8_t *buffer, size_t length);
|
||||
|
||||
|
||||
|
||||
void queue_tcp(const struct arguments *args,
|
||||
const struct tcphdr *tcphdr,
|
||||
const char *session, struct tcp_session *cur,
|
||||
const uint8_t *data, uint16_t datalen);
|
||||
|
||||
int open_icmp_socket(const struct arguments *args, const struct icmp_session *cur);
|
||||
|
||||
int open_udp_socket(const struct arguments *args,
|
||||
const struct udp_session *cur, const struct allowed *redirect);
|
||||
|
||||
int open_tcp_socket(const struct arguments *args,
|
||||
const struct tcp_session *cur, const struct allowed *redirect);
|
||||
|
||||
int32_t get_local_port(const int sock);
|
||||
|
||||
int write_syn_ack(const struct arguments *args, struct tcp_session *cur);
|
||||
|
||||
int write_ack(const struct arguments *args, struct tcp_session *cur);
|
||||
|
||||
int write_data(const struct arguments *args, struct tcp_session *cur,
|
||||
const uint8_t *buffer, size_t length);
|
||||
|
||||
int write_fin_ack(const struct arguments *args, struct tcp_session *cur);
|
||||
|
||||
void write_rst(const struct arguments *args, struct tcp_session *cur);
|
||||
|
||||
void write_rst_ack(const struct arguments *args, struct tcp_session *cur);
|
||||
|
||||
ssize_t write_icmp(const struct arguments *args, const struct icmp_session *cur,
|
||||
uint8_t *data, size_t datalen);
|
||||
|
||||
ssize_t write_udp(const struct arguments *args, const struct udp_session *cur,
|
||||
uint8_t *data, size_t datalen);
|
||||
|
||||
ssize_t write_tcp(const struct arguments *args, const struct tcp_session *cur,
|
||||
const uint8_t *data, size_t datalen,
|
||||
int syn, int ack, int fin, int rst);
|
||||
|
||||
uint8_t char2nible(const char c);
|
||||
|
||||
void hex2bytes(const char *hex, uint8_t *buffer);
|
||||
|
||||
jint get_uid(const int version, const int protocol,
|
||||
const void *saddr, const uint16_t sport,
|
||||
const void *daddr, const uint16_t dport);
|
||||
|
||||
jint get_uid_sub(const int version, const int protocol,
|
||||
const void *saddr, const uint16_t sport,
|
||||
const void *daddr, const uint16_t dport,
|
||||
const char *source, const char *dest,
|
||||
long now);
|
||||
|
||||
int protect_socket(const struct arguments *args, int socket);
|
||||
|
||||
uint16_t calc_checksum(uint16_t start, const uint8_t *buffer, size_t length);
|
||||
|
||||
jobject jniGlobalRef(JNIEnv *env, jobject cls);
|
||||
|
||||
jclass jniFindClass(JNIEnv *env, const char *name);
|
||||
|
||||
jmethodID jniGetMethodID(JNIEnv *env, jclass cls, const char *name, const char *signature);
|
||||
|
||||
jfieldID jniGetFieldID(JNIEnv *env, jclass cls, const char *name, const char *type);
|
||||
|
||||
jobject jniNewObject(JNIEnv *env, jclass cls, jmethodID constructor, const char *name);
|
||||
|
||||
int jniCheckException(JNIEnv *env);
|
||||
|
||||
int sdk_int(JNIEnv *env);
|
||||
|
||||
void log_android(int prio, const char *fmt, ...);
|
||||
|
||||
void log_packet(const struct arguments *args, jobject jpacket);
|
||||
|
||||
void dns_resolved(const struct arguments *args,
|
||||
const char *qname, const char *aname, const char *resource, int ttl);
|
||||
|
||||
jboolean is_domain_blocked(const struct arguments *args, const char *name);
|
||||
|
||||
jint get_uid_q(const struct arguments *args,
|
||||
jint version,
|
||||
jint protocol,
|
||||
const char *source,
|
||||
jint sport,
|
||||
const char *dest,
|
||||
jint dport);
|
||||
|
||||
struct allowed *is_address_allowed(const struct arguments *args, jobject objPacket);
|
||||
|
||||
jobject create_packet(const struct arguments *args,
|
||||
jint version,
|
||||
jint protocol,
|
||||
const char *flags,
|
||||
const char *source,
|
||||
jint sport,
|
||||
const char *dest,
|
||||
jint dport,
|
||||
const char *data,
|
||||
jint uid,
|
||||
jboolean allowed);
|
||||
|
||||
void account_usage(const struct arguments *args, jint version, jint protocol,
|
||||
const char *daddr, jint dport, jint uid, jlong sent, jlong received);
|
||||
|
||||
void write_pcap_hdr();
|
||||
|
||||
void write_pcap_rec(const uint8_t *buffer, size_t len);
|
||||
|
||||
void write_pcap(const void *ptr, size_t len);
|
||||
|
||||
int compare_u32(uint32_t seq1, uint32_t seq2);
|
||||
|
||||
const char *strstate(const int state);
|
||||
|
||||
char *hex(const u_int8_t *data, const size_t len);
|
||||
|
||||
int is_readable(int fd);
|
||||
|
||||
int is_writable(int fd);
|
||||
|
||||
long long get_ms();
|
||||
|
||||
void ng_add_alloc(void *ptr, const char *tag);
|
||||
|
||||
void ng_delete_alloc(void *ptr, const char *file, int line);
|
||||
|
||||
void *ng_malloc(size_t __byte_count, const char *tag);
|
||||
|
||||
void *ng_calloc(size_t __item_count, size_t __item_size, const char *tag);
|
||||
|
||||
void *ng_realloc(void *__ptr, size_t __byte_count, const char *tag);
|
||||
|
||||
void ng_free(void *__ptr, const char *file, int line);
|
||||
|
||||
void ng_dump();
|
||||
@@ -0,0 +1,78 @@
|
||||
/*
|
||||
This file is part of NetGuard.
|
||||
|
||||
NetGuard 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.
|
||||
|
||||
NetGuard 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 NetGuard. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
Copyright 2015-2019 by Marcel Bokhorst (M66B)
|
||||
*/
|
||||
|
||||
#include "netguard.h"
|
||||
|
||||
FILE *pcap_file = NULL;
|
||||
size_t pcap_record_size = 64;
|
||||
long pcap_file_size = 2 * 1024 * 1024;
|
||||
|
||||
void write_pcap_hdr() {
|
||||
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 = pcap_record_size;
|
||||
pcap_hdr.network = LINKTYPE_RAW;
|
||||
write_pcap(&pcap_hdr, sizeof(struct pcap_hdr_s));
|
||||
}
|
||||
|
||||
void write_pcap_rec(const uint8_t *buffer, size_t length) {
|
||||
struct timespec ts;
|
||||
if (clock_gettime(CLOCK_REALTIME, &ts))
|
||||
log_android(ANDROID_LOG_ERROR, "clock_gettime error %d: %s", errno, strerror(errno));
|
||||
|
||||
size_t plen = (length < pcap_record_size ? length : pcap_record_size);
|
||||
size_t rlen = sizeof(struct pcaprec_hdr_s) + plen;
|
||||
struct pcaprec_hdr_s *pcap_rec = ng_malloc(rlen, "pcap");
|
||||
|
||||
pcap_rec->ts_sec = (guint32_t) ts.tv_sec;
|
||||
pcap_rec->ts_usec = (guint32_t) (ts.tv_nsec / 1000);
|
||||
pcap_rec->incl_len = (guint32_t) plen;
|
||||
pcap_rec->orig_len = (guint32_t) length;
|
||||
|
||||
memcpy(((uint8_t *) pcap_rec) + sizeof(struct pcaprec_hdr_s), buffer, plen);
|
||||
|
||||
write_pcap(pcap_rec, rlen);
|
||||
|
||||
ng_free(pcap_rec, __FILE__, __LINE__);
|
||||
}
|
||||
|
||||
void write_pcap(const void *ptr, size_t len) {
|
||||
if (fwrite(ptr, len, 1, pcap_file) < 1)
|
||||
log_android(ANDROID_LOG_ERROR, "PCAP fwrite error %d: %s", errno, strerror(errno));
|
||||
else {
|
||||
long fsize = ftell(pcap_file);
|
||||
log_android(ANDROID_LOG_VERBOSE, "PCAP wrote %d @%ld", len, fsize);
|
||||
|
||||
if (fsize > pcap_file_size) {
|
||||
log_android(ANDROID_LOG_WARN, "PCAP truncate @%ld", fsize);
|
||||
if (ftruncate(fileno(pcap_file), sizeof(struct pcap_hdr_s)))
|
||||
log_android(ANDROID_LOG_ERROR, "PCAP ftruncate error %d: %s",
|
||||
errno, strerror(errno));
|
||||
else {
|
||||
if (!lseek(fileno(pcap_file), sizeof(struct pcap_hdr_s), SEEK_SET))
|
||||
log_android(ANDROID_LOG_ERROR, "PCAP ftruncate error %d: %s",
|
||||
errno, strerror(errno));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,370 @@
|
||||
/*
|
||||
This file is part of NetGuard.
|
||||
|
||||
NetGuard 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.
|
||||
|
||||
NetGuard 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 NetGuard. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
Copyright 2015-2019 by Marcel Bokhorst (M66B)
|
||||
*/
|
||||
|
||||
#include "netguard.h"
|
||||
|
||||
void clear(struct context *ctx) {
|
||||
struct ng_session *s = ctx->ng_session;
|
||||
while (s != NULL) {
|
||||
if (s->socket >= 0 && close(s->socket))
|
||||
log_android(ANDROID_LOG_ERROR, "close %d error %d: %s",
|
||||
s->socket, errno, strerror(errno));
|
||||
if (s->protocol == IPPROTO_TCP)
|
||||
clear_tcp_data(&s->tcp);
|
||||
struct ng_session *p = s;
|
||||
s = s->next;
|
||||
ng_free(p, __FILE__, __LINE__);
|
||||
}
|
||||
ctx->ng_session = NULL;
|
||||
}
|
||||
|
||||
void *handle_events(void *a) {
|
||||
struct arguments *args = (struct arguments *) a;
|
||||
log_android(ANDROID_LOG_ERROR, "Start events tun=%d", args->tun);
|
||||
|
||||
// Get max number of sessions
|
||||
int maxsessions = SESSION_MAX;
|
||||
struct rlimit rlim;
|
||||
if (getrlimit(RLIMIT_NOFILE, &rlim))
|
||||
log_android(ANDROID_LOG_WARN, "getrlimit error %d: %s", errno, strerror(errno));
|
||||
else {
|
||||
maxsessions = (int) (rlim.rlim_cur * SESSION_LIMIT / 100);
|
||||
if (maxsessions > SESSION_MAX)
|
||||
maxsessions = SESSION_MAX;
|
||||
log_android(ANDROID_LOG_WARN, "getrlimit soft %d hard %d max sessions %d",
|
||||
rlim.rlim_cur, rlim.rlim_max, maxsessions);
|
||||
}
|
||||
|
||||
// Terminate existing sessions not allowed anymore
|
||||
check_allowed(args);
|
||||
|
||||
// Open epoll file
|
||||
int epoll_fd = epoll_create(1);
|
||||
if (epoll_fd < 0) {
|
||||
log_android(ANDROID_LOG_ERROR, "epoll create error %d: %s", errno, strerror(errno));
|
||||
report_exit(args, "epoll create error %d: %s", errno, strerror(errno));
|
||||
args->ctx->stopping = 1;
|
||||
}
|
||||
|
||||
// Monitor stop events
|
||||
struct epoll_event ev_pipe;
|
||||
memset(&ev_pipe, 0, sizeof(struct epoll_event));
|
||||
ev_pipe.events = EPOLLIN | EPOLLERR;
|
||||
ev_pipe.data.ptr = &ev_pipe;
|
||||
if (epoll_ctl(epoll_fd, EPOLL_CTL_ADD, args->ctx->pipefds[0], &ev_pipe)) {
|
||||
log_android(ANDROID_LOG_ERROR, "epoll add pipe error %d: %s", errno, strerror(errno));
|
||||
report_exit(args, "epoll add pipe error %d: %s", errno, strerror(errno));
|
||||
args->ctx->stopping = 1;
|
||||
}
|
||||
|
||||
// Monitor tun events
|
||||
struct epoll_event ev_tun;
|
||||
memset(&ev_tun, 0, sizeof(struct epoll_event));
|
||||
ev_tun.events = EPOLLIN | EPOLLERR;
|
||||
ev_tun.data.ptr = NULL;
|
||||
if (epoll_ctl(epoll_fd, EPOLL_CTL_ADD, args->tun, &ev_tun)) {
|
||||
log_android(ANDROID_LOG_ERROR, "epoll add tun error %d: %s", errno, strerror(errno));
|
||||
report_exit(args, "epoll add tun error %d: %s", errno, strerror(errno));
|
||||
args->ctx->stopping = 1;
|
||||
}
|
||||
|
||||
// Loop
|
||||
long long last_check = 0;
|
||||
while (!args->ctx->stopping) {
|
||||
log_android(ANDROID_LOG_DEBUG, "Loop");
|
||||
|
||||
int recheck = 0;
|
||||
int timeout = EPOLL_TIMEOUT;
|
||||
|
||||
// Count sessions
|
||||
int isessions = 0;
|
||||
int usessions = 0;
|
||||
int tsessions = 0;
|
||||
struct ng_session *s = args->ctx->ng_session;
|
||||
while (s != NULL) {
|
||||
if (s->protocol == IPPROTO_ICMP || s->protocol == IPPROTO_ICMPV6) {
|
||||
if (!s->icmp.stop)
|
||||
isessions++;
|
||||
} else if (s->protocol == IPPROTO_UDP) {
|
||||
if (s->udp.state == UDP_ACTIVE)
|
||||
usessions++;
|
||||
} else if (s->protocol == IPPROTO_TCP) {
|
||||
if (s->tcp.state != TCP_CLOSING && s->tcp.state != TCP_CLOSE)
|
||||
tsessions++;
|
||||
if (s->socket >= 0)
|
||||
recheck = recheck | monitor_tcp_session(args, s, epoll_fd);
|
||||
}
|
||||
s = s->next;
|
||||
}
|
||||
int sessions = isessions + usessions + tsessions;
|
||||
|
||||
// Check sessions
|
||||
long long ms = get_ms();
|
||||
if (ms - last_check > EPOLL_MIN_CHECK) {
|
||||
last_check = ms;
|
||||
|
||||
time_t now = time(NULL);
|
||||
struct ng_session *sl = NULL;
|
||||
s = args->ctx->ng_session;
|
||||
while (s != NULL) {
|
||||
int del = 0;
|
||||
if (s->protocol == IPPROTO_ICMP || s->protocol == IPPROTO_ICMPV6) {
|
||||
del = check_icmp_session(args, s, sessions, maxsessions);
|
||||
if (!s->icmp.stop && !del) {
|
||||
int stimeout = s->icmp.time +
|
||||
get_icmp_timeout(&s->icmp, sessions, maxsessions) - now + 1;
|
||||
if (stimeout > 0 && stimeout < timeout)
|
||||
timeout = stimeout;
|
||||
}
|
||||
} else if (s->protocol == IPPROTO_UDP) {
|
||||
del = check_udp_session(args, s, sessions, maxsessions);
|
||||
if (s->udp.state == UDP_ACTIVE && !del) {
|
||||
int stimeout = s->udp.time +
|
||||
get_udp_timeout(&s->udp, sessions, maxsessions) - now + 1;
|
||||
if (stimeout > 0 && stimeout < timeout)
|
||||
timeout = stimeout;
|
||||
}
|
||||
} else if (s->protocol == IPPROTO_TCP) {
|
||||
del = check_tcp_session(args, s, sessions, maxsessions);
|
||||
if (s->tcp.state != TCP_CLOSING && s->tcp.state != TCP_CLOSE && !del) {
|
||||
int stimeout = s->tcp.time +
|
||||
get_tcp_timeout(&s->tcp, sessions, maxsessions) - now + 1;
|
||||
if (stimeout > 0 && stimeout < timeout)
|
||||
timeout = stimeout;
|
||||
}
|
||||
}
|
||||
|
||||
if (del) {
|
||||
if (sl == NULL)
|
||||
args->ctx->ng_session = s->next;
|
||||
else
|
||||
sl->next = s->next;
|
||||
|
||||
struct ng_session *c = s;
|
||||
s = s->next;
|
||||
if (c->protocol == IPPROTO_TCP)
|
||||
clear_tcp_data(&c->tcp);
|
||||
ng_free(c, __FILE__, __LINE__);
|
||||
} else {
|
||||
sl = s;
|
||||
s = s->next;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
recheck = 1;
|
||||
log_android(ANDROID_LOG_DEBUG, "Skipped session checks");
|
||||
}
|
||||
|
||||
log_android(ANDROID_LOG_DEBUG,
|
||||
"sessions ICMP %d UDP %d TCP %d max %d/%d timeout %d recheck %d",
|
||||
isessions, usessions, tsessions, sessions, maxsessions, timeout, recheck);
|
||||
|
||||
// Poll
|
||||
struct epoll_event ev[EPOLL_EVENTS];
|
||||
int ready = epoll_wait(epoll_fd, ev, EPOLL_EVENTS,
|
||||
recheck ? EPOLL_MIN_CHECK : timeout * 1000);
|
||||
|
||||
if (ready < 0) {
|
||||
if (errno == EINTR) {
|
||||
log_android(ANDROID_LOG_DEBUG, "epoll interrupted tun %d", args->tun);
|
||||
continue;
|
||||
} else {
|
||||
log_android(ANDROID_LOG_ERROR,
|
||||
"epoll tun %d error %d: %s",
|
||||
args->tun, errno, strerror(errno));
|
||||
report_exit(args, "epoll tun %d error %d: %s",
|
||||
args->tun, errno, strerror(errno));
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (ready == 0)
|
||||
log_android(ANDROID_LOG_DEBUG, "epoll timeout");
|
||||
else {
|
||||
|
||||
if (pthread_mutex_lock(&args->ctx->lock))
|
||||
log_android(ANDROID_LOG_ERROR, "pthread_mutex_lock failed");
|
||||
|
||||
int error = 0;
|
||||
|
||||
for (int i = 0; i < ready; i++) {
|
||||
if (ev[i].data.ptr == &ev_pipe) {
|
||||
// Check pipe
|
||||
uint8_t buffer[1];
|
||||
if (read(args->ctx->pipefds[0], buffer, 1) < 0)
|
||||
log_android(ANDROID_LOG_WARN, "Read pipe error %d: %s",
|
||||
errno, strerror(errno));
|
||||
else
|
||||
log_android(ANDROID_LOG_WARN, "Read pipe");
|
||||
|
||||
} else if (ev[i].data.ptr == NULL) {
|
||||
// Check upstream
|
||||
log_android(ANDROID_LOG_DEBUG, "epoll ready %d/%d in %d out %d err %d hup %d",
|
||||
i, ready,
|
||||
(ev[i].events & EPOLLIN) != 0,
|
||||
(ev[i].events & EPOLLOUT) != 0,
|
||||
(ev[i].events & EPOLLERR) != 0,
|
||||
(ev[i].events & EPOLLHUP) != 0);
|
||||
|
||||
int count = 0;
|
||||
while (count < TUN_YIELD && !error && !args->ctx->stopping &&
|
||||
is_readable(args->tun)) {
|
||||
count++;
|
||||
if (check_tun(args, &ev[i], epoll_fd, sessions, maxsessions) < 0)
|
||||
error = 1;
|
||||
}
|
||||
|
||||
} else {
|
||||
// Check downstream
|
||||
log_android(ANDROID_LOG_DEBUG,
|
||||
"epoll ready %d/%d in %d out %d err %d hup %d prot %d sock %d",
|
||||
i, ready,
|
||||
(ev[i].events & EPOLLIN) != 0,
|
||||
(ev[i].events & EPOLLOUT) != 0,
|
||||
(ev[i].events & EPOLLERR) != 0,
|
||||
(ev[i].events & EPOLLHUP) != 0,
|
||||
((struct ng_session *) ev[i].data.ptr)->protocol,
|
||||
((struct ng_session *) ev[i].data.ptr)->socket);
|
||||
|
||||
struct ng_session *session = (struct ng_session *) ev[i].data.ptr;
|
||||
if (session->protocol == IPPROTO_ICMP ||
|
||||
session->protocol == IPPROTO_ICMPV6)
|
||||
check_icmp_socket(args, &ev[i]);
|
||||
else if (session->protocol == IPPROTO_UDP) {
|
||||
int count = 0;
|
||||
while (count < UDP_YIELD && !args->ctx->stopping &&
|
||||
!(ev[i].events & EPOLLERR) && (ev[i].events & EPOLLIN) &&
|
||||
is_readable(session->socket)) {
|
||||
count++;
|
||||
check_udp_socket(args, &ev[i]);
|
||||
}
|
||||
} else if (session->protocol == IPPROTO_TCP)
|
||||
check_tcp_socket(args, &ev[i], epoll_fd);
|
||||
}
|
||||
|
||||
if (error)
|
||||
break;
|
||||
}
|
||||
|
||||
if (pthread_mutex_unlock(&args->ctx->lock))
|
||||
log_android(ANDROID_LOG_ERROR, "pthread_mutex_unlock failed");
|
||||
|
||||
if (error)
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// Close epoll file
|
||||
if (epoll_fd >= 0 && close(epoll_fd))
|
||||
log_android(ANDROID_LOG_ERROR,
|
||||
"epoll close error %d: %s", errno, strerror(errno));
|
||||
|
||||
// Cleanup
|
||||
ng_free(args, __FILE__, __LINE__);
|
||||
|
||||
log_android(ANDROID_LOG_WARN, "Stopped events tun=%d", args->tun);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
void check_allowed(const struct arguments *args) {
|
||||
char source[INET6_ADDRSTRLEN + 1];
|
||||
char dest[INET6_ADDRSTRLEN + 1];
|
||||
|
||||
struct ng_session *l = NULL;
|
||||
struct ng_session *s = args->ctx->ng_session;
|
||||
while (s != NULL) {
|
||||
if (s->protocol == IPPROTO_ICMP || s->protocol == IPPROTO_ICMPV6) {
|
||||
if (!s->icmp.stop) {
|
||||
if (s->icmp.version == 4) {
|
||||
inet_ntop(AF_INET, &s->icmp.saddr.ip4, source, sizeof(source));
|
||||
inet_ntop(AF_INET, &s->icmp.daddr.ip4, dest, sizeof(dest));
|
||||
} else {
|
||||
inet_ntop(AF_INET6, &s->icmp.saddr.ip6, source, sizeof(source));
|
||||
inet_ntop(AF_INET6, &s->icmp.daddr.ip6, dest, sizeof(dest));
|
||||
}
|
||||
|
||||
jobject objPacket = create_packet(
|
||||
args, s->icmp.version, IPPROTO_ICMP, "",
|
||||
source, 0, dest, 0, "", s->icmp.uid, 0);
|
||||
if (is_address_allowed(args, objPacket) == NULL) {
|
||||
s->icmp.stop = 1;
|
||||
log_android(ANDROID_LOG_WARN, "ICMP terminate %d uid %d",
|
||||
s->socket, s->icmp.uid);
|
||||
}
|
||||
}
|
||||
|
||||
} else if (s->protocol == IPPROTO_UDP) {
|
||||
if (s->udp.state == UDP_ACTIVE) {
|
||||
if (s->udp.version == 4) {
|
||||
inet_ntop(AF_INET, &s->udp.saddr.ip4, source, sizeof(source));
|
||||
inet_ntop(AF_INET, &s->udp.daddr.ip4, dest, sizeof(dest));
|
||||
} else {
|
||||
inet_ntop(AF_INET6, &s->udp.saddr.ip6, source, sizeof(source));
|
||||
inet_ntop(AF_INET6, &s->udp.daddr.ip6, dest, sizeof(dest));
|
||||
}
|
||||
|
||||
jobject objPacket = create_packet(
|
||||
args, s->udp.version, IPPROTO_UDP, "",
|
||||
source, ntohs(s->udp.source), dest, ntohs(s->udp.dest), "", s->udp.uid, 0);
|
||||
if (is_address_allowed(args, objPacket) == NULL) {
|
||||
s->udp.state = UDP_FINISHING;
|
||||
log_android(ANDROID_LOG_WARN, "UDP terminate session socket %d uid %d",
|
||||
s->socket, s->udp.uid);
|
||||
}
|
||||
} else if (s->udp.state == UDP_BLOCKED) {
|
||||
log_android(ANDROID_LOG_WARN, "UDP remove blocked session uid %d", s->udp.uid);
|
||||
|
||||
if (l == NULL)
|
||||
args->ctx->ng_session = s->next;
|
||||
else
|
||||
l->next = s->next;
|
||||
|
||||
struct ng_session *c = s;
|
||||
s = s->next;
|
||||
ng_free(c, __FILE__, __LINE__);
|
||||
continue;
|
||||
}
|
||||
|
||||
} else if (s->protocol == IPPROTO_TCP) {
|
||||
if (s->tcp.state != TCP_CLOSING && s->tcp.state != TCP_CLOSE) {
|
||||
if (s->tcp.version == 4) {
|
||||
inet_ntop(AF_INET, &s->tcp.saddr.ip4, source, sizeof(source));
|
||||
inet_ntop(AF_INET, &s->tcp.daddr.ip4, dest, sizeof(dest));
|
||||
} else {
|
||||
inet_ntop(AF_INET6, &s->tcp.saddr.ip6, source, sizeof(source));
|
||||
inet_ntop(AF_INET6, &s->tcp.daddr.ip6, dest, sizeof(dest));
|
||||
}
|
||||
|
||||
jobject objPacket = create_packet(
|
||||
args, s->tcp.version, IPPROTO_TCP, "",
|
||||
source, ntohs(s->tcp.source), dest, ntohs(s->tcp.dest), "", s->tcp.uid, 0);
|
||||
if (is_address_allowed(args, objPacket) == NULL) {
|
||||
write_rst(args, &s->tcp);
|
||||
log_android(ANDROID_LOG_WARN, "TCP terminate socket %d uid %d",
|
||||
s->socket, s->tcp.uid);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
l = s;
|
||||
s = s->next;
|
||||
}
|
||||
}
|
||||
|
||||
File diff suppressed because it is too large.
Load diff
@@ -0,0 +1,549 @@
|
||||
/*
|
||||
This file is part of NetGuard.
|
||||
|
||||
NetGuard 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.
|
||||
|
||||
NetGuard 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 NetGuard. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
Copyright 2015-2019 by Marcel Bokhorst (M66B)
|
||||
*/
|
||||
|
||||
#include "netguard.h"
|
||||
|
||||
extern FILE *pcap_file;
|
||||
|
||||
int get_udp_timeout(const struct udp_session *u, int sessions, int maxsessions) {
|
||||
int timeout = (ntohs(u->dest) == 53 ? UDP_TIMEOUT_53 : UDP_TIMEOUT_ANY);
|
||||
|
||||
int scale = 100 - sessions * 100 / maxsessions;
|
||||
timeout = timeout * scale / 100;
|
||||
|
||||
return timeout;
|
||||
}
|
||||
|
||||
int check_udp_session(const struct arguments *args, struct ng_session *s,
|
||||
int sessions, int maxsessions) {
|
||||
time_t now = time(NULL);
|
||||
|
||||
char source[INET6_ADDRSTRLEN + 1];
|
||||
char dest[INET6_ADDRSTRLEN + 1];
|
||||
if (s->udp.version == 4) {
|
||||
inet_ntop(AF_INET, &s->udp.saddr.ip4, source, sizeof(source));
|
||||
inet_ntop(AF_INET, &s->udp.daddr.ip4, dest, sizeof(dest));
|
||||
} else {
|
||||
inet_ntop(AF_INET6, &s->udp.saddr.ip6, source, sizeof(source));
|
||||
inet_ntop(AF_INET6, &s->udp.daddr.ip6, dest, sizeof(dest));
|
||||
}
|
||||
|
||||
// Check session timeout
|
||||
int timeout = get_udp_timeout(&s->udp, sessions, maxsessions);
|
||||
if (s->udp.state == UDP_ACTIVE && s->udp.time + timeout < now) {
|
||||
log_android(ANDROID_LOG_WARN, "UDP idle %d/%d sec state %d from %s/%u to %s/%u",
|
||||
now - s->udp.time, timeout, s->udp.state,
|
||||
source, ntohs(s->udp.source), dest, ntohs(s->udp.dest));
|
||||
s->udp.state = UDP_FINISHING;
|
||||
}
|
||||
|
||||
// Check finished sessions
|
||||
if (s->udp.state == UDP_FINISHING) {
|
||||
log_android(ANDROID_LOG_INFO, "UDP close from %s/%u to %s/%u socket %d",
|
||||
source, ntohs(s->udp.source), dest, ntohs(s->udp.dest), s->socket);
|
||||
|
||||
if (close(s->socket))
|
||||
log_android(ANDROID_LOG_ERROR, "UDP close %d error %d: %s",
|
||||
s->socket, errno, strerror(errno));
|
||||
s->socket = -1;
|
||||
|
||||
s->udp.time = time(NULL);
|
||||
s->udp.state = UDP_CLOSED;
|
||||
}
|
||||
|
||||
if (s->udp.state == UDP_CLOSED && (s->udp.sent || s->udp.received)) {
|
||||
account_usage(args, s->udp.version, IPPROTO_UDP,
|
||||
dest, ntohs(s->udp.dest), s->udp.uid, s->udp.sent, s->udp.received);
|
||||
s->udp.sent = 0;
|
||||
s->udp.received = 0;
|
||||
}
|
||||
|
||||
// Cleanup lingering sessions
|
||||
if ((s->udp.state == UDP_CLOSED || s->udp.state == UDP_BLOCKED) &&
|
||||
s->udp.time + UDP_KEEP_TIMEOUT < now)
|
||||
return 1;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void check_udp_socket(const struct arguments *args, const struct epoll_event *ev) {
|
||||
struct ng_session *s = (struct ng_session *) ev->data.ptr;
|
||||
|
||||
// Check socket error
|
||||
if (ev->events & EPOLLERR) {
|
||||
s->udp.time = time(NULL);
|
||||
|
||||
int serr = 0;
|
||||
socklen_t optlen = sizeof(int);
|
||||
int err = getsockopt(s->socket, SOL_SOCKET, SO_ERROR, &serr, &optlen);
|
||||
if (err < 0)
|
||||
log_android(ANDROID_LOG_ERROR, "UDP getsockopt error %d: %s",
|
||||
errno, strerror(errno));
|
||||
else if (serr)
|
||||
log_android(ANDROID_LOG_ERROR, "UDP SO_ERROR %d: %s", serr, strerror(serr));
|
||||
|
||||
s->udp.state = UDP_FINISHING;
|
||||
} else {
|
||||
// Check socket read
|
||||
if (ev->events & EPOLLIN) {
|
||||
s->udp.time = time(NULL);
|
||||
|
||||
uint8_t *buffer = ng_malloc(s->udp.mss, "udp recv");
|
||||
ssize_t bytes = recv(s->socket, buffer, s->udp.mss, 0);
|
||||
if (bytes < 0) {
|
||||
// Socket error
|
||||
log_android(ANDROID_LOG_WARN, "UDP recv error %d: %s",
|
||||
errno, strerror(errno));
|
||||
|
||||
if (errno != EINTR && errno != EAGAIN)
|
||||
s->udp.state = UDP_FINISHING;
|
||||
} else if (bytes == 0) {
|
||||
log_android(ANDROID_LOG_WARN, "UDP recv eof");
|
||||
s->udp.state = UDP_FINISHING;
|
||||
|
||||
} else {
|
||||
// Socket read data
|
||||
char dest[INET6_ADDRSTRLEN + 1];
|
||||
if (s->udp.version == 4)
|
||||
inet_ntop(AF_INET, &s->udp.daddr.ip4, dest, sizeof(dest));
|
||||
else
|
||||
inet_ntop(AF_INET6, &s->udp.daddr.ip6, dest, sizeof(dest));
|
||||
log_android(ANDROID_LOG_INFO, "UDP recv bytes %d from %s/%u for tun",
|
||||
bytes, dest, ntohs(s->udp.dest));
|
||||
|
||||
s->udp.received += bytes;
|
||||
|
||||
// Process DNS response
|
||||
if (ntohs(s->udp.dest) == 53)
|
||||
parse_dns_response(args, s, buffer, (size_t *) &bytes);
|
||||
|
||||
// Forward to tun
|
||||
if (write_udp(args, &s->udp, buffer, (size_t) bytes) < 0)
|
||||
s->udp.state = UDP_FINISHING;
|
||||
else {
|
||||
// Prevent too many open files
|
||||
if (ntohs(s->udp.dest) == 53)
|
||||
s->udp.state = UDP_FINISHING;
|
||||
}
|
||||
}
|
||||
ng_free(buffer, __FILE__, __LINE__);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
int has_udp_session(const struct arguments *args, const uint8_t *pkt, const uint8_t *payload) {
|
||||
// Get headers
|
||||
const uint8_t version = (*pkt) >> 4;
|
||||
const struct iphdr *ip4 = (struct iphdr *) pkt;
|
||||
const struct ip6_hdr *ip6 = (struct ip6_hdr *) pkt;
|
||||
const struct udphdr *udphdr = (struct udphdr *) payload;
|
||||
|
||||
if (ntohs(udphdr->dest) == 53 && !args->fwd53)
|
||||
return 1;
|
||||
|
||||
// Search session
|
||||
struct ng_session *cur = args->ctx->ng_session;
|
||||
while (cur != NULL &&
|
||||
!(cur->protocol == IPPROTO_UDP &&
|
||||
cur->udp.version == version &&
|
||||
cur->udp.source == udphdr->source && cur->udp.dest == udphdr->dest &&
|
||||
(version == 4 ? cur->udp.saddr.ip4 == ip4->saddr &&
|
||||
cur->udp.daddr.ip4 == ip4->daddr
|
||||
: memcmp(&cur->udp.saddr.ip6, &ip6->ip6_src, 16) == 0 &&
|
||||
memcmp(&cur->udp.daddr.ip6, &ip6->ip6_dst, 16) == 0)))
|
||||
cur = cur->next;
|
||||
|
||||
return (cur != NULL);
|
||||
}
|
||||
|
||||
void block_udp(const struct arguments *args,
|
||||
const uint8_t *pkt, size_t length,
|
||||
const uint8_t *payload,
|
||||
int uid) {
|
||||
// Get headers
|
||||
const uint8_t version = (*pkt) >> 4;
|
||||
const struct iphdr *ip4 = (struct iphdr *) pkt;
|
||||
const struct ip6_hdr *ip6 = (struct ip6_hdr *) pkt;
|
||||
const struct udphdr *udphdr = (struct udphdr *) payload;
|
||||
|
||||
char source[INET6_ADDRSTRLEN + 1];
|
||||
char dest[INET6_ADDRSTRLEN + 1];
|
||||
if (version == 4) {
|
||||
inet_ntop(AF_INET, &ip4->saddr, source, sizeof(source));
|
||||
inet_ntop(AF_INET, &ip4->daddr, dest, sizeof(dest));
|
||||
} else {
|
||||
inet_ntop(AF_INET6, &ip6->ip6_src, source, sizeof(source));
|
||||
inet_ntop(AF_INET6, &ip6->ip6_dst, dest, sizeof(dest));
|
||||
}
|
||||
|
||||
log_android(ANDROID_LOG_INFO, "UDP blocked session from %s/%u to %s/%u",
|
||||
source, ntohs(udphdr->source), dest, ntohs(udphdr->dest));
|
||||
|
||||
// Register session
|
||||
struct ng_session *s = ng_malloc(sizeof(struct ng_session), "udp session block");
|
||||
s->protocol = IPPROTO_UDP;
|
||||
|
||||
s->udp.time = time(NULL);
|
||||
s->udp.uid = uid;
|
||||
s->udp.version = version;
|
||||
|
||||
if (version == 4) {
|
||||
s->udp.saddr.ip4 = (__be32) ip4->saddr;
|
||||
s->udp.daddr.ip4 = (__be32) ip4->daddr;
|
||||
} else {
|
||||
memcpy(&s->udp.saddr.ip6, &ip6->ip6_src, 16);
|
||||
memcpy(&s->udp.daddr.ip6, &ip6->ip6_dst, 16);
|
||||
}
|
||||
|
||||
s->udp.source = udphdr->source;
|
||||
s->udp.dest = udphdr->dest;
|
||||
s->udp.state = UDP_BLOCKED;
|
||||
s->socket = -1;
|
||||
|
||||
s->next = args->ctx->ng_session;
|
||||
args->ctx->ng_session = s;
|
||||
}
|
||||
|
||||
jboolean handle_udp(const struct arguments *args,
|
||||
const uint8_t *pkt, size_t length,
|
||||
const uint8_t *payload,
|
||||
int uid, struct allowed *redirect,
|
||||
const int epoll_fd) {
|
||||
// Get headers
|
||||
const uint8_t version = (*pkt) >> 4;
|
||||
const struct iphdr *ip4 = (struct iphdr *) pkt;
|
||||
const struct ip6_hdr *ip6 = (struct ip6_hdr *) pkt;
|
||||
const struct udphdr *udphdr = (struct udphdr *) payload;
|
||||
const uint8_t *data = payload + sizeof(struct udphdr);
|
||||
const size_t datalen = length - (data - pkt);
|
||||
|
||||
// Search session
|
||||
struct ng_session *cur = args->ctx->ng_session;
|
||||
while (cur != NULL &&
|
||||
!(cur->protocol == IPPROTO_UDP &&
|
||||
cur->udp.version == version &&
|
||||
cur->udp.source == udphdr->source && cur->udp.dest == udphdr->dest &&
|
||||
(version == 4 ? cur->udp.saddr.ip4 == ip4->saddr &&
|
||||
cur->udp.daddr.ip4 == ip4->daddr
|
||||
: memcmp(&cur->udp.saddr.ip6, &ip6->ip6_src, 16) == 0 &&
|
||||
memcmp(&cur->udp.daddr.ip6, &ip6->ip6_dst, 16) == 0)))
|
||||
cur = cur->next;
|
||||
|
||||
char source[INET6_ADDRSTRLEN + 1];
|
||||
char dest[INET6_ADDRSTRLEN + 1];
|
||||
if (version == 4) {
|
||||
inet_ntop(AF_INET, &ip4->saddr, source, sizeof(source));
|
||||
inet_ntop(AF_INET, &ip4->daddr, dest, sizeof(dest));
|
||||
} else {
|
||||
inet_ntop(AF_INET6, &ip6->ip6_src, source, sizeof(source));
|
||||
inet_ntop(AF_INET6, &ip6->ip6_dst, dest, sizeof(dest));
|
||||
}
|
||||
|
||||
if (cur != NULL && cur->udp.state != UDP_ACTIVE) {
|
||||
log_android(ANDROID_LOG_INFO, "UDP ignore session from %s/%u to %s/%u state %d",
|
||||
source, ntohs(udphdr->source), dest, ntohs(udphdr->dest), cur->udp.state);
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Create new session if needed
|
||||
if (cur == NULL) {
|
||||
log_android(ANDROID_LOG_INFO, "UDP new session from %s/%u to %s/%u",
|
||||
source, ntohs(udphdr->source), dest, ntohs(udphdr->dest));
|
||||
|
||||
// Register session
|
||||
struct ng_session *s = ng_malloc(sizeof(struct ng_session), "udp session");
|
||||
s->protocol = IPPROTO_UDP;
|
||||
|
||||
s->udp.time = time(NULL);
|
||||
s->udp.uid = uid;
|
||||
s->udp.version = version;
|
||||
|
||||
int rversion;
|
||||
if (redirect == NULL)
|
||||
rversion = s->udp.version;
|
||||
else
|
||||
rversion = (strstr(redirect->raddr, ":") == NULL ? 4 : 6);
|
||||
s->udp.mss = (uint16_t) (rversion == 4 ? UDP4_MAXMSG : UDP6_MAXMSG);
|
||||
|
||||
s->udp.sent = 0;
|
||||
s->udp.received = 0;
|
||||
|
||||
if (version == 4) {
|
||||
s->udp.saddr.ip4 = (__be32) ip4->saddr;
|
||||
s->udp.daddr.ip4 = (__be32) ip4->daddr;
|
||||
} else {
|
||||
memcpy(&s->udp.saddr.ip6, &ip6->ip6_src, 16);
|
||||
memcpy(&s->udp.daddr.ip6, &ip6->ip6_dst, 16);
|
||||
}
|
||||
|
||||
s->udp.source = udphdr->source;
|
||||
s->udp.dest = udphdr->dest;
|
||||
s->udp.state = UDP_ACTIVE;
|
||||
s->next = NULL;
|
||||
|
||||
// Open UDP socket
|
||||
s->socket = open_udp_socket(args, &s->udp, redirect);
|
||||
if (s->socket < 0) {
|
||||
ng_free(s, __FILE__, __LINE__);
|
||||
return 0;
|
||||
}
|
||||
|
||||
log_android(ANDROID_LOG_DEBUG, "UDP socket %d", s->socket);
|
||||
|
||||
// Monitor events
|
||||
memset(&s->ev, 0, sizeof(struct epoll_event));
|
||||
s->ev.events = EPOLLIN | EPOLLERR;
|
||||
s->ev.data.ptr = s;
|
||||
if (epoll_ctl(epoll_fd, EPOLL_CTL_ADD, s->socket, &s->ev))
|
||||
log_android(ANDROID_LOG_ERROR, "epoll add udp error %d: %s", errno, strerror(errno));
|
||||
|
||||
s->next = args->ctx->ng_session;
|
||||
args->ctx->ng_session = s;
|
||||
|
||||
cur = s;
|
||||
}
|
||||
|
||||
// Check for DHCP (tethering)
|
||||
if (ntohs(udphdr->source) == 68 || ntohs(udphdr->dest) == 67) {
|
||||
if (check_dhcp(args, &cur->udp, data, datalen) >= 0)
|
||||
return 1;
|
||||
}
|
||||
|
||||
log_android(ANDROID_LOG_INFO, "UDP forward from tun %s/%u to %s/%u data %d",
|
||||
source, ntohs(udphdr->source), dest, ntohs(udphdr->dest), datalen);
|
||||
|
||||
cur->udp.time = time(NULL);
|
||||
|
||||
int rversion;
|
||||
struct sockaddr_in addr4;
|
||||
struct sockaddr_in6 addr6;
|
||||
if (redirect == NULL) {
|
||||
rversion = cur->udp.version;
|
||||
if (cur->udp.version == 4) {
|
||||
addr4.sin_family = AF_INET;
|
||||
addr4.sin_addr.s_addr = (__be32) cur->udp.daddr.ip4;
|
||||
addr4.sin_port = cur->udp.dest;
|
||||
} else {
|
||||
addr6.sin6_family = AF_INET6;
|
||||
memcpy(&addr6.sin6_addr, &cur->udp.daddr.ip6, 16);
|
||||
addr6.sin6_port = cur->udp.dest;
|
||||
}
|
||||
} else {
|
||||
rversion = (strstr(redirect->raddr, ":") == NULL ? 4 : 6);
|
||||
log_android(ANDROID_LOG_WARN, "UDP%d redirect to %s/%u",
|
||||
rversion, redirect->raddr, redirect->rport);
|
||||
|
||||
if (rversion == 4) {
|
||||
addr4.sin_family = AF_INET;
|
||||
inet_pton(AF_INET, redirect->raddr, &addr4.sin_addr);
|
||||
addr4.sin_port = htons(redirect->rport);
|
||||
} else {
|
||||
addr6.sin6_family = AF_INET6;
|
||||
inet_pton(AF_INET6, redirect->raddr, &addr6.sin6_addr);
|
||||
addr6.sin6_port = htons(redirect->rport);
|
||||
}
|
||||
}
|
||||
|
||||
if (sendto(cur->socket, data, (socklen_t) datalen, MSG_NOSIGNAL,
|
||||
(rversion == 4 ? (const struct sockaddr *) &addr4
|
||||
: (const struct sockaddr *) &addr6),
|
||||
(socklen_t) (rversion == 4 ? sizeof(addr4) : sizeof(addr6))) != datalen) {
|
||||
log_android(ANDROID_LOG_ERROR, "UDP sendto error %d: %s", errno, strerror(errno));
|
||||
if (errno != EINTR && errno != EAGAIN) {
|
||||
cur->udp.state = UDP_FINISHING;
|
||||
return 0;
|
||||
}
|
||||
} else
|
||||
cur->udp.sent += datalen;
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
int open_udp_socket(const struct arguments *args,
|
||||
const struct udp_session *cur, const struct allowed *redirect) {
|
||||
int sock;
|
||||
int version;
|
||||
if (redirect == NULL)
|
||||
version = cur->version;
|
||||
else
|
||||
version = (strstr(redirect->raddr, ":") == NULL ? 4 : 6);
|
||||
|
||||
// Get UDP socket
|
||||
sock = socket(version == 4 ? PF_INET : PF_INET6, SOCK_DGRAM, IPPROTO_UDP);
|
||||
if (sock < 0) {
|
||||
log_android(ANDROID_LOG_ERROR, "UDP socket error %d: %s", errno, strerror(errno));
|
||||
return -1;
|
||||
}
|
||||
|
||||
// Protect socket
|
||||
if (protect_socket(args, sock) < 0)
|
||||
return -1;
|
||||
|
||||
// Check for broadcast/multicast
|
||||
if (cur->version == 4) {
|
||||
uint32_t broadcast4 = INADDR_BROADCAST;
|
||||
if (memcmp(&cur->daddr.ip4, &broadcast4, sizeof(broadcast4)) == 0) {
|
||||
log_android(ANDROID_LOG_WARN, "UDP4 broadcast");
|
||||
int on = 1;
|
||||
if (setsockopt(sock, SOL_SOCKET, SO_BROADCAST, &on, sizeof(on)))
|
||||
log_android(ANDROID_LOG_ERROR, "UDP setsockopt SO_BROADCAST error %d: %s",
|
||||
errno, strerror(errno));
|
||||
}
|
||||
} else {
|
||||
// http://man7.org/linux/man-pages/man7/ipv6.7.html
|
||||
if (*((uint8_t *) &cur->daddr.ip6) == 0xFF) {
|
||||
log_android(ANDROID_LOG_WARN, "UDP6 broadcast");
|
||||
|
||||
int loop = 1; // true
|
||||
if (setsockopt(sock, IPPROTO_IPV6, IPV6_MULTICAST_LOOP, &loop, sizeof(loop)))
|
||||
log_android(ANDROID_LOG_ERROR,
|
||||
"UDP setsockopt IPV6_MULTICAST_LOOP error %d: %s",
|
||||
errno, strerror(errno));
|
||||
|
||||
int ttl = -1; // route default
|
||||
if (setsockopt(sock, IPPROTO_IPV6, IPV6_MULTICAST_HOPS, &ttl, sizeof(ttl)))
|
||||
log_android(ANDROID_LOG_ERROR,
|
||||
"UDP setsockopt IPV6_MULTICAST_HOPS error %d: %s",
|
||||
errno, strerror(errno));
|
||||
|
||||
struct ipv6_mreq mreq6;
|
||||
memcpy(&mreq6.ipv6mr_multiaddr, &cur->daddr.ip6, sizeof(struct in6_addr));
|
||||
mreq6.ipv6mr_interface = INADDR_ANY;
|
||||
if (setsockopt(sock, IPPROTO_IPV6, IPV6_ADD_MEMBERSHIP, &mreq6, sizeof(mreq6)))
|
||||
log_android(ANDROID_LOG_ERROR,
|
||||
"UDP setsockopt IPV6_ADD_MEMBERSHIP error %d: %s",
|
||||
errno, strerror(errno));
|
||||
}
|
||||
}
|
||||
|
||||
return sock;
|
||||
}
|
||||
|
||||
ssize_t write_udp(const struct arguments *args, const struct udp_session *cur,
|
||||
uint8_t *data, size_t datalen) {
|
||||
size_t len;
|
||||
u_int8_t *buffer;
|
||||
struct udphdr *udp;
|
||||
uint16_t csum;
|
||||
char source[INET6_ADDRSTRLEN + 1];
|
||||
char dest[INET6_ADDRSTRLEN + 1];
|
||||
|
||||
// Build packet
|
||||
if (cur->version == 4) {
|
||||
len = sizeof(struct iphdr) + sizeof(struct udphdr) + datalen;
|
||||
buffer = ng_malloc(len, "udp write4");
|
||||
struct iphdr *ip4 = (struct iphdr *) buffer;
|
||||
udp = (struct udphdr *) (buffer + sizeof(struct iphdr));
|
||||
if (datalen)
|
||||
memcpy(buffer + sizeof(struct iphdr) + sizeof(struct udphdr), data, datalen);
|
||||
|
||||
// Build IP4 header
|
||||
memset(ip4, 0, sizeof(struct iphdr));
|
||||
ip4->version = 4;
|
||||
ip4->ihl = sizeof(struct iphdr) >> 2;
|
||||
ip4->tot_len = htons(len);
|
||||
ip4->ttl = IPDEFTTL;
|
||||
ip4->protocol = IPPROTO_UDP;
|
||||
ip4->saddr = cur->daddr.ip4;
|
||||
ip4->daddr = cur->saddr.ip4;
|
||||
|
||||
// Calculate IP4 checksum
|
||||
ip4->check = ~calc_checksum(0, (uint8_t *) ip4, sizeof(struct iphdr));
|
||||
|
||||
// Calculate UDP4 checksum
|
||||
struct ippseudo pseudo;
|
||||
memset(&pseudo, 0, sizeof(struct ippseudo));
|
||||
pseudo.ippseudo_src.s_addr = (__be32) ip4->saddr;
|
||||
pseudo.ippseudo_dst.s_addr = (__be32) ip4->daddr;
|
||||
pseudo.ippseudo_p = ip4->protocol;
|
||||
pseudo.ippseudo_len = htons(sizeof(struct udphdr) + datalen);
|
||||
|
||||
csum = calc_checksum(0, (uint8_t *) &pseudo, sizeof(struct ippseudo));
|
||||
} else {
|
||||
len = sizeof(struct ip6_hdr) + sizeof(struct udphdr) + datalen;
|
||||
buffer = ng_malloc(len, "udp write6");
|
||||
struct ip6_hdr *ip6 = (struct ip6_hdr *) buffer;
|
||||
udp = (struct udphdr *) (buffer + sizeof(struct ip6_hdr));
|
||||
if (datalen)
|
||||
memcpy(buffer + sizeof(struct ip6_hdr) + sizeof(struct udphdr), data, datalen);
|
||||
|
||||
// Build IP6 header
|
||||
memset(ip6, 0, sizeof(struct ip6_hdr));
|
||||
ip6->ip6_ctlun.ip6_un1.ip6_un1_flow = 0;
|
||||
ip6->ip6_ctlun.ip6_un1.ip6_un1_plen = htons(len - sizeof(struct ip6_hdr));
|
||||
ip6->ip6_ctlun.ip6_un1.ip6_un1_nxt = IPPROTO_UDP;
|
||||
ip6->ip6_ctlun.ip6_un1.ip6_un1_hlim = IPDEFTTL;
|
||||
ip6->ip6_ctlun.ip6_un2_vfc = IPV6_VERSION;
|
||||
memcpy(&(ip6->ip6_src), &cur->daddr.ip6, 16);
|
||||
memcpy(&(ip6->ip6_dst), &cur->saddr.ip6, 16);
|
||||
|
||||
// Calculate UDP6 checksum
|
||||
struct ip6_hdr_pseudo pseudo;
|
||||
memset(&pseudo, 0, sizeof(struct ip6_hdr_pseudo));
|
||||
memcpy(&pseudo.ip6ph_src, &ip6->ip6_dst, 16);
|
||||
memcpy(&pseudo.ip6ph_dst, &ip6->ip6_src, 16);
|
||||
pseudo.ip6ph_len = ip6->ip6_ctlun.ip6_un1.ip6_un1_plen;
|
||||
pseudo.ip6ph_nxt = ip6->ip6_ctlun.ip6_un1.ip6_un1_nxt;
|
||||
|
||||
csum = calc_checksum(0, (uint8_t *) &pseudo, sizeof(struct ip6_hdr_pseudo));
|
||||
}
|
||||
|
||||
// Build UDP header
|
||||
memset(udp, 0, sizeof(struct udphdr));
|
||||
udp->source = cur->dest;
|
||||
udp->dest = cur->source;
|
||||
udp->len = htons(sizeof(struct udphdr) + datalen);
|
||||
|
||||
// Continue checksum
|
||||
csum = calc_checksum(csum, (uint8_t *) udp, sizeof(struct udphdr));
|
||||
csum = calc_checksum(csum, data, datalen);
|
||||
udp->check = ~csum;
|
||||
|
||||
inet_ntop(cur->version == 4 ? AF_INET : AF_INET6,
|
||||
(cur->version == 4 ? (const void *) &cur->saddr.ip4 : (const void *) &cur->saddr.ip6),
|
||||
source,
|
||||
sizeof(source));
|
||||
inet_ntop(cur->version == 4 ? AF_INET : AF_INET6,
|
||||
(cur->version == 4 ? (const void *) &cur->daddr.ip4 : (const void *) &cur->daddr.ip6),
|
||||
dest,
|
||||
sizeof(dest));
|
||||
|
||||
// Send packet
|
||||
log_android(ANDROID_LOG_DEBUG,
|
||||
"UDP sending to tun %d from %s/%u to %s/%u data %u",
|
||||
args->tun, dest, ntohs(cur->dest), source, ntohs(cur->source), len);
|
||||
|
||||
ssize_t res = write(args->tun, buffer, len);
|
||||
|
||||
// Write PCAP record
|
||||
if (res >= 0) {
|
||||
if (pcap_file != NULL)
|
||||
write_pcap_rec(buffer, (size_t) res);
|
||||
} else
|
||||
log_android(ANDROID_LOG_WARN, "UDP write error %d: %s", errno, strerror(errno));
|
||||
|
||||
ng_free(buffer, __FILE__, __LINE__);
|
||||
|
||||
if (res != len) {
|
||||
log_android(ANDROID_LOG_ERROR, "write %d/%d", res, len);
|
||||
return -1;
|
||||
}
|
||||
|
||||
return res;
|
||||
}
|
||||
@@ -0,0 +1,182 @@
|
||||
/*
|
||||
This file is part of NetGuard.
|
||||
|
||||
NetGuard 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.
|
||||
|
||||
NetGuard 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 NetGuard. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
Copyright 2015-2019 by Marcel Bokhorst (M66B)
|
||||
*/
|
||||
|
||||
#include "netguard.h"
|
||||
|
||||
extern int loglevel;
|
||||
|
||||
uint16_t calc_checksum(uint16_t start, const uint8_t *buffer, size_t length) {
|
||||
register uint32_t sum = start;
|
||||
register uint16_t *buf = (uint16_t *) buffer;
|
||||
register size_t len = length;
|
||||
|
||||
while (len > 1) {
|
||||
sum += *buf++;
|
||||
len -= 2;
|
||||
}
|
||||
|
||||
if (len > 0)
|
||||
sum += *((uint8_t *) buf);
|
||||
|
||||
while (sum >> 16)
|
||||
sum = (sum & 0xFFFF) + (sum >> 16);
|
||||
|
||||
return (uint16_t) sum;
|
||||
}
|
||||
|
||||
int compare_u32(uint32_t s1, uint32_t s2) {
|
||||
// https://tools.ietf.org/html/rfc1982
|
||||
if (s1 == s2)
|
||||
return 0;
|
||||
|
||||
uint32_t i1 = s1;
|
||||
uint32_t i2 = s2;
|
||||
if ((i1 < i2 && i2 - i1 < 0x7FFFFFFF) ||
|
||||
(i1 > i2 && i1 - i2 > 0x7FFFFFFF))
|
||||
return -1;
|
||||
else
|
||||
return 1;
|
||||
}
|
||||
|
||||
int sdk_int(JNIEnv *env) {
|
||||
jclass clsVersion = jniFindClass(env, "android/os/Build$VERSION");
|
||||
jfieldID fid = (*env)->GetStaticFieldID(env, clsVersion, "SDK_INT", "I");
|
||||
return (*env)->GetStaticIntField(env, clsVersion, fid);
|
||||
}
|
||||
|
||||
void log_android(int prio, const char *fmt, ...) {
|
||||
if (prio >= loglevel) {
|
||||
char line[1024];
|
||||
va_list argptr;
|
||||
va_start(argptr, fmt);
|
||||
vsprintf(line, fmt, argptr);
|
||||
__android_log_print(prio, TAG, "%s", line);
|
||||
va_end(argptr);
|
||||
}
|
||||
}
|
||||
|
||||
uint8_t char2nible(const char c) {
|
||||
if (c >= '0' && c <= '9') return (uint8_t) (c - '0');
|
||||
if (c >= 'a' && c <= 'f') return (uint8_t) ((c - 'a') + 10);
|
||||
if (c >= 'A' && c <= 'F') return (uint8_t) ((c - 'A') + 10);
|
||||
return 255;
|
||||
}
|
||||
|
||||
void hex2bytes(const char *hex, uint8_t *buffer) {
|
||||
size_t len = strlen(hex);
|
||||
for (int i = 0; i < len; i += 2)
|
||||
buffer[i / 2] = (char2nible(hex[i]) << 4) | char2nible(hex[i + 1]);
|
||||
}
|
||||
|
||||
char *trim(char *str) {
|
||||
while (isspace(*str))
|
||||
str++;
|
||||
if (*str == 0)
|
||||
return str;
|
||||
|
||||
char *end = str + strlen(str) - 1;
|
||||
while (end > str && isspace(*end))
|
||||
end--;
|
||||
*(end + 1) = 0;
|
||||
return str;
|
||||
}
|
||||
|
||||
const char *strstate(const int state) {
|
||||
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_WAIT1";
|
||||
case TCP_FIN_WAIT2:
|
||||
return "FIN_WAIT2";
|
||||
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:
|
||||
return "UNKNOWN";
|
||||
}
|
||||
}
|
||||
|
||||
char *hex(const u_int8_t *data, const size_t len) {
|
||||
char hex_str[] = "0123456789ABCDEF";
|
||||
|
||||
char *hexout;
|
||||
hexout = (char *) ng_malloc(len * 3 + 1, "hex"); // TODO free
|
||||
|
||||
for (size_t i = 0; i < len; i++) {
|
||||
hexout[i * 3 + 0] = hex_str[(data[i] >> 4) & 0x0F];
|
||||
hexout[i * 3 + 1] = hex_str[(data[i]) & 0x0F];
|
||||
hexout[i * 3 + 2] = ' ';
|
||||
}
|
||||
hexout[len * 3] = 0;
|
||||
|
||||
return hexout;
|
||||
}
|
||||
|
||||
int32_t get_local_port(const int sock) {
|
||||
struct sockaddr_in sin;
|
||||
socklen_t len = sizeof(sin);
|
||||
if (getsockname(sock, (struct sockaddr *) &sin, &len) < 0) {
|
||||
log_android(ANDROID_LOG_ERROR, "getsockname error %d: %s", errno, strerror(errno));
|
||||
return -1;
|
||||
} else
|
||||
return ntohs(sin.sin_port);
|
||||
}
|
||||
|
||||
int is_event(int fd, short event) {
|
||||
struct pollfd p;
|
||||
p.fd = fd;
|
||||
p.events = event;
|
||||
p.revents = 0;
|
||||
int r = poll(&p, 1, 0);
|
||||
if (r < 0) {
|
||||
log_android(ANDROID_LOG_ERROR, "poll readable error %d: %s", errno, strerror(errno));
|
||||
return 0;
|
||||
} else if (r == 0)
|
||||
return 0;
|
||||
else
|
||||
return (p.revents & event);
|
||||
}
|
||||
|
||||
int is_readable(int fd) {
|
||||
return is_event(fd, POLLIN);
|
||||
}
|
||||
|
||||
int is_writable(int fd) {
|
||||
return is_event(fd, POLLOUT);
|
||||
}
|
||||
|
||||
long long get_ms() {
|
||||
struct timespec ts;
|
||||
clock_gettime(CLOCK_MONOTONIC, &ts);
|
||||
return ts.tv_sec * 1000LL + ts.tv_nsec / 1e6;
|
||||
}
|
||||
Reference in new issue
Block a user