mirror of
https://github.com/Waujito/youtubeUnblock.git
synced 2026-02-01 15:10:34 +03:00
510 lines
12 KiB
C
510 lines
12 KiB
C
/*
|
|
youtubeUnblock - https://github.com/Waujito/youtubeUnblock
|
|
|
|
Copyright (C) 2024-2025 Vadim Vetrov <vetrovvd@gmail.com>
|
|
|
|
This program 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.
|
|
|
|
This program 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 this program. If not, see <https://www.gnu.org/licenses/>.
|
|
*/
|
|
|
|
/**
|
|
* dpi.c - Inspects packets for blocked patterns.
|
|
* "If you want to bypass the DPI, you should became the DPI"
|
|
*/
|
|
|
|
#define _GNU_SOURCE
|
|
#include "types.h" // IWYU pragma: keep
|
|
#ifndef KERNEL_SPACE
|
|
#include <stdlib.h>
|
|
#else
|
|
#include "linux/inet.h"
|
|
#endif
|
|
|
|
#include "dpi.h"
|
|
#include "config.h"
|
|
#include "utils.h"
|
|
#include "quic.h"
|
|
#include "logging.h"
|
|
#include "tls.h"
|
|
|
|
#include "mangle.h"
|
|
|
|
int log_packet(const struct parsed_packet *pkt);
|
|
|
|
#define MAX_FRAGMENTATION_PTS 16
|
|
|
|
struct fragmentation_points {
|
|
size_t payload_points[16];
|
|
int used_points;
|
|
};
|
|
|
|
int process_packet(const struct config_t *config, const struct packet_data *pd) {
|
|
assert (config);
|
|
assert (pd);
|
|
|
|
struct parsed_packet pkt = {0};
|
|
int ret = 0;
|
|
|
|
pkt.yct = pd->yct;
|
|
|
|
lgtrace_start();
|
|
|
|
pkt.raw_payload = pd->payload;
|
|
pkt.raw_payload_len = pd->payload_len;
|
|
|
|
if (pkt.raw_payload_len > MAX_PACKET_SIZE) {
|
|
return PKT_ACCEPT;
|
|
}
|
|
|
|
pkt.ipver = netproto_version(pkt.raw_payload, pkt.raw_payload_len);
|
|
|
|
lgtrace_wr("IPv%d ", pkt.ipver);
|
|
|
|
pkt.transport_proto = -1;
|
|
if (pkt.ipver == IP4VERSION) {
|
|
ret = ip4_payload_split((uint8_t *)pkt.raw_payload, pkt.raw_payload_len,
|
|
(struct iphdr **)&pkt.iph, &pkt.iph_len,
|
|
(uint8_t **)&pkt.ip_payload, &pkt.ip_payload_len);
|
|
|
|
if (ret < 0)
|
|
goto accept;
|
|
|
|
pkt.transport_proto = pkt.iph->protocol;
|
|
}
|
|
#ifndef NO_IPV6
|
|
else if (pkt.ipver == IP6VERSION && config->use_ipv6) {
|
|
ret = ip6_payload_split((uint8_t *)pkt.raw_payload, pkt.raw_payload_len,
|
|
(struct ip6_hdr **)&pkt.ip6h, &pkt.iph_len,
|
|
(uint8_t **)&pkt.ip_payload, &pkt.ip_payload_len);
|
|
|
|
|
|
if (ret < 0)
|
|
goto accept;
|
|
|
|
pkt.transport_proto = pkt.ip6h->ip6_nxt;
|
|
}
|
|
#endif
|
|
|
|
if (pkt.transport_proto == IPPROTO_TCP) {
|
|
int ret = tcp_payload_split((uint8_t *)pkt.raw_payload, pkt.raw_payload_len,
|
|
NULL, NULL,
|
|
(struct tcphdr **)&pkt.tcph, &pkt.tcph_len,
|
|
(uint8_t **)&pkt.transport_payload, &pkt.transport_payload_len);
|
|
if (ret < 0)
|
|
goto accept;
|
|
} else if (pkt.transport_proto == IPPROTO_UDP) {
|
|
int ret = udp_payload_split((uint8_t *)pkt.raw_payload, pkt.raw_payload_len,
|
|
NULL, NULL,
|
|
(struct udphdr **)&pkt.udph,
|
|
(uint8_t **)&pkt.transport_payload, &pkt.transport_payload_len);
|
|
|
|
if (ret < 0)
|
|
goto accept;
|
|
}
|
|
|
|
if (LOG_LEVEL >= VERBOSE_TRACE) {
|
|
log_packet(&pkt);
|
|
}
|
|
|
|
int verdict = PKT_CONTINUE;
|
|
|
|
ITER_CONFIG_SECTIONS(config, section) {
|
|
lgtrace_wr("Section #%d: ", CONFIG_SECTION_NUMBER(section));
|
|
|
|
switch (pkt.transport_proto) {
|
|
case IPPROTO_TCP:
|
|
verdict = process_tcp_packet(section, &pkt);
|
|
break;
|
|
case IPPROTO_UDP:
|
|
verdict = process_udp_packet(section, &pkt);
|
|
break;
|
|
}
|
|
|
|
if (verdict == PKT_CONTINUE) {
|
|
lgtrace_wr("continue_flow");
|
|
lgtrace_write();
|
|
continue;
|
|
}
|
|
|
|
lgtrace_write();
|
|
goto ret_verdict;
|
|
}
|
|
|
|
accept:
|
|
verdict = PKT_ACCEPT;
|
|
|
|
ret_verdict:
|
|
|
|
switch (verdict) {
|
|
case PKT_ACCEPT:
|
|
lgtrace_wr("accept");
|
|
break;
|
|
case PKT_DROP:
|
|
lgtrace_wr("drop");
|
|
break;
|
|
default:
|
|
lgtrace_wr("unknown verdict: %d", verdict);
|
|
}
|
|
lgtrace_end();
|
|
|
|
return verdict;
|
|
}
|
|
|
|
enum tls_proc_verdict {
|
|
TLS_NOT_MATCHED,
|
|
TLS_ERROR,
|
|
TLS_MATCHED,
|
|
};
|
|
|
|
enum tls_proc_verdict process_tls_packet(const struct section_config_t *section,
|
|
const struct parsed_packet *pkt,
|
|
struct fragmentation_points *frag_pts);
|
|
|
|
|
|
int perform_attack(const struct section_config_t *section,
|
|
const struct parsed_packet *pkt, const struct fragmentation_points *frag_pts);
|
|
|
|
int process_tcp_packet(const struct section_config_t *section, const struct parsed_packet *pkt) {
|
|
assert (section);
|
|
assert (pkt);
|
|
|
|
assert (pkt->transport_proto == IPPROTO_TCP);
|
|
|
|
uint16_t dport = ntohs(pkt->tcph->dest);
|
|
|
|
if (section->tcp_dport_range_len) {
|
|
int is_dport_matched = 0;
|
|
|
|
for (int i = 0; i < section->tcp_dport_range_len; i++) {
|
|
struct dport_range crange = section->tcp_dport_range[i];
|
|
if (dport >= crange.start && dport <= crange.end) {
|
|
lgtrace_addp("matched to %d-%d", crange.start, crange.end);
|
|
is_dport_matched = 1;
|
|
}
|
|
}
|
|
|
|
if (!is_dport_matched) {
|
|
return PKT_CONTINUE;
|
|
}
|
|
} else if (section->dport_filter && dport != 443) {
|
|
return PKT_CONTINUE;
|
|
}
|
|
|
|
if (pkt->tcph->syn && section->synfake) {
|
|
return send_synfake(section, pkt);
|
|
}
|
|
|
|
if (pkt->tcph->syn)
|
|
return PKT_CONTINUE;
|
|
|
|
int is_matched = 0;
|
|
struct fragmentation_points frag_pts = {0};
|
|
|
|
if (!is_matched && section->tls_enabled) {
|
|
enum tls_proc_verdict vrd = process_tls_packet(section, pkt, &frag_pts);
|
|
|
|
if (vrd == TLS_ERROR) {
|
|
return PKT_ACCEPT;
|
|
}
|
|
|
|
if (vrd == TLS_MATCHED) {
|
|
is_matched = 1;
|
|
}
|
|
}
|
|
|
|
if (!is_matched && section->tcp_match_connpkts && pkt->yct.orig_packets) {
|
|
if (pkt->yct.orig_packets <= section->tcp_match_connpkts) {
|
|
lgtrace_addp("connpackets match: %lu <= %d",
|
|
pkt->yct.orig_packets, section->tcp_match_connpkts);
|
|
is_matched = 1;
|
|
|
|
frag_pts.used_points = 0;
|
|
if (section->frag_sni_pos &&
|
|
pkt->transport_payload_len > section->frag_sni_pos) {
|
|
frag_pts.payload_points[frag_pts.used_points++] =
|
|
section->frag_sni_pos;
|
|
lgtrace_addp("frag set to %d", section->frag_sni_pos);
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
if (is_matched) {
|
|
return perform_attack(section, pkt, &frag_pts);
|
|
}
|
|
|
|
return PKT_CONTINUE;
|
|
}
|
|
|
|
static void bubblesort(size_t arr[], size_t n){
|
|
for (int i = 0; i < n - 1; i++) {
|
|
for (int j = 0; j < n - 1 - i; j++) {
|
|
if (arr[j] > arr[j + 1]) {
|
|
int temp = arr[j];
|
|
arr[j] = arr[j + 1];
|
|
arr[j + 1] = temp;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
enum tls_proc_verdict process_tls_packet(const struct section_config_t *section,
|
|
const struct parsed_packet *pkt,
|
|
struct fragmentation_points *frag_pts) {
|
|
assert (section);
|
|
assert (pkt);
|
|
|
|
|
|
struct tls_verdict vrd = analyze_tls_data(section,
|
|
pkt->transport_payload, pkt->transport_payload_len);
|
|
lgtrace_addp("TLS analyzed");
|
|
|
|
if (vrd.sni_len != 0) {
|
|
lgtrace_addp("SNI detected: %.*s", vrd.sni_len, vrd.sni_ptr);
|
|
}
|
|
|
|
if (vrd.target_sni) {
|
|
lgdebug("Target SNI detected: %.*s", vrd.sni_len, vrd.sni_ptr);
|
|
size_t target_sni_offset = vrd.target_sni_ptr - pkt->transport_payload;
|
|
|
|
size_t ipd_offset = target_sni_offset;
|
|
size_t mid_offset = ipd_offset + vrd.target_sni_len / 2;
|
|
|
|
// hardcode googlevideo.com split
|
|
// googlevideo domains are very long, so
|
|
// it is possible for the entire domain to not be
|
|
// splitted (split goes for subdomain)
|
|
if (vrd.target_sni_len > 30) {
|
|
mid_offset = ipd_offset +
|
|
vrd.target_sni_len - 12;
|
|
}
|
|
|
|
frag_pts->used_points = 0;
|
|
|
|
if (section->frag_sni_pos && pkt->transport_payload_len > section->frag_sni_pos) {
|
|
frag_pts->payload_points[frag_pts->used_points++] = section->frag_sni_pos;
|
|
}
|
|
|
|
if (section->frag_middle_sni) {
|
|
frag_pts->payload_points[frag_pts->used_points++] = mid_offset;
|
|
}
|
|
|
|
bubblesort(frag_pts->payload_points, frag_pts->used_points);
|
|
|
|
return TLS_MATCHED;
|
|
}
|
|
|
|
return TLS_NOT_MATCHED;
|
|
}
|
|
|
|
int perform_attack(const struct section_config_t *section,
|
|
const struct parsed_packet *pkt, const struct fragmentation_points *frag_pts) {
|
|
assert (section);
|
|
assert (pkt);
|
|
assert (frag_pts);
|
|
|
|
int ret = 0;
|
|
|
|
size_t payload_len = pkt->raw_payload_len;
|
|
uint8_t *payload = malloc(pkt->raw_payload_len);
|
|
if (payload == NULL) {
|
|
lgerror(-ENOMEM, "Allocation error");
|
|
return PKT_ACCEPT;
|
|
}
|
|
|
|
memcpy(payload, pkt->raw_payload, pkt->raw_payload_len);
|
|
|
|
if (pkt->transport_payload_len > AVAILABLE_MTU) {
|
|
lgdebug("WARNING! Tartget packet is too big and may cause issues!");
|
|
}
|
|
|
|
if (section->fake_sni) {
|
|
void *iph;
|
|
size_t iph_len;
|
|
struct tcphdr *tcph;
|
|
size_t tcph_len;
|
|
uint8_t *data;
|
|
size_t dlen;
|
|
|
|
int ret = tcp_payload_split(payload, payload_len,
|
|
&iph, &iph_len, &tcph, &tcph_len,
|
|
&data, &dlen);
|
|
|
|
if (ret < 0) {
|
|
lgerror(ret, "tcp_payload_split in targ_sni");
|
|
goto accept_lc;
|
|
}
|
|
|
|
if (section->fk_winsize) {
|
|
tcph->window = htons(section->fk_winsize);
|
|
set_tcp_checksum(tcph, iph, iph_len);
|
|
}
|
|
|
|
|
|
struct fake_type f_type = args_default_fake_type(section);
|
|
post_fake_sni(f_type, iph, iph_len, tcph, tcph_len);
|
|
}
|
|
|
|
|
|
if (frag_pts->used_points > 0) {
|
|
if (section->fragmentation_strategy == FRAG_STRAT_TCP) {
|
|
ret = send_tcp_frags(section, payload, payload_len, frag_pts->payload_points,
|
|
frag_pts->used_points, 0);
|
|
if (ret < 0) {
|
|
lgerror(ret, "tcp4 send frags");
|
|
goto accept_lc;
|
|
}
|
|
|
|
goto drop_lc;
|
|
} else if (section->fragmentation_strategy == FRAG_STRAT_IP && pkt->ipver == IP4VERSION) {
|
|
ret = send_ip4_frags(section, payload, payload_len, frag_pts->payload_points,
|
|
frag_pts->used_points, 0);
|
|
if (ret < 0) {
|
|
lgerror(ret, "tcp4 send frags");
|
|
goto accept_lc;
|
|
}
|
|
|
|
goto drop_lc;
|
|
|
|
} else if (section->fragmentation_strategy == FRAG_STRAT_IP && pkt->ipver != IP4VERSION) {
|
|
lginfo("WARNING: IP fragmentation is supported only for IPv4");
|
|
goto accept_lc;
|
|
}
|
|
}
|
|
|
|
|
|
accept_lc:
|
|
free(payload);
|
|
return PKT_ACCEPT;
|
|
drop_lc:
|
|
free(payload);
|
|
return PKT_DROP;
|
|
}
|
|
|
|
int process_udp_packet(const struct section_config_t *section, const struct parsed_packet *pkt) {
|
|
assert (section);
|
|
assert (pkt);
|
|
|
|
assert (pkt->transport_proto == IPPROTO_UDP);
|
|
|
|
int ret = 0;
|
|
|
|
if (!detect_udp_filtered(section, pkt->raw_payload, pkt->raw_payload_len))
|
|
goto continue_flow;
|
|
|
|
if (section->udp_mode == UDP_MODE_DROP)
|
|
goto drop;
|
|
else if (section->udp_mode == UDP_MODE_FAKE) {
|
|
for (int i = 0; i < section->udp_fake_seq_len; i++) {
|
|
uint8_t *fake_udp = NULL;
|
|
size_t fake_udp_len = 0;
|
|
|
|
struct udp_fake_type fake_type = {
|
|
.fake_len = section->udp_fake_len,
|
|
.strategy = {
|
|
.strategy = section->udp_faking_strategy,
|
|
.faking_ttl = section->faking_ttl,
|
|
},
|
|
};
|
|
ret = gen_fake_udp(fake_type, pkt->iph, pkt->iph_len, pkt->udph,
|
|
&fake_udp, &fake_udp_len);
|
|
if (ret < 0) {
|
|
lgerror(ret, "gen_fake_udp");
|
|
goto erret;
|
|
}
|
|
|
|
lgtrace_addp("post fake udp #%d", i + 1);
|
|
|
|
ret = instance_config.send_raw_packet(fake_udp, fake_udp_len);
|
|
if (ret < 0) {
|
|
lgerror(ret, "send fake udp");
|
|
goto erret_lc;
|
|
}
|
|
|
|
free(fake_udp);
|
|
continue;
|
|
erret_lc:
|
|
free(fake_udp);
|
|
erret:
|
|
goto accept;
|
|
}
|
|
|
|
|
|
// requeue
|
|
ret = instance_config.send_raw_packet(pkt->raw_payload, pkt->raw_payload_len);
|
|
goto drop;
|
|
}
|
|
|
|
continue_flow:
|
|
return PKT_CONTINUE;
|
|
accept:
|
|
return PKT_ACCEPT;
|
|
drop:
|
|
return PKT_DROP;
|
|
}
|
|
|
|
int log_packet(const struct parsed_packet *pkt) {
|
|
int ret = 0;
|
|
|
|
const char *bpt = inet_ntop(
|
|
pkt->ipver == IP4VERSION ? AF_INET : AF_INET6,
|
|
pkt->ipver == IP4VERSION ? (void *)(&pkt->iph->saddr) :
|
|
(void *)(&pkt->ip6h->ip6_src),
|
|
ylgh_curptr, ylgh_leftbuf);
|
|
if (bpt != NULL) {
|
|
ret = strnlen(bpt, ylgh_leftbuf);
|
|
ylgh_leftbuf -= ret;
|
|
ylgh_curptr += ret;
|
|
}
|
|
|
|
lgtrace_wr(" => ");
|
|
|
|
bpt = inet_ntop(
|
|
pkt->ipver == IP4VERSION ? AF_INET : AF_INET6,
|
|
pkt->ipver == IP4VERSION ? (void *)(&pkt->iph->daddr) :
|
|
(void *)(&pkt->ip6h->ip6_dst),
|
|
ylgh_curptr, ylgh_leftbuf);
|
|
if (bpt != NULL) {
|
|
ret = strnlen(bpt, ylgh_leftbuf);
|
|
ylgh_leftbuf -= ret;
|
|
ylgh_curptr += ret;
|
|
|
|
}
|
|
|
|
lgtrace_wr(" ");
|
|
int sport = -1, dport = -1;
|
|
|
|
if (pkt->transport_proto == IPPROTO_TCP) {
|
|
lgtrace_wr("TCP ");
|
|
|
|
sport = ntohs(pkt->tcph->source);
|
|
dport = ntohs(pkt->tcph->dest);
|
|
|
|
} else if (pkt->transport_proto == IPPROTO_UDP) {
|
|
lgtrace_wr("UDP ");
|
|
|
|
sport = ntohs(pkt->udph->source);
|
|
dport = ntohs(pkt->udph->dest);
|
|
}
|
|
|
|
lgtrace_wr("%d => %d ", sport, dport);
|
|
lgtrace_write();
|
|
|
|
lgtrace_wr("Transport payload: [ ");
|
|
for (int i = 0; i < min((int)16, (int)pkt->transport_payload_len); i++) {
|
|
lgtrace_wr("%02x ", pkt->transport_payload[i]);
|
|
}
|
|
lgtrace_wr("]");
|
|
lgtrace_write();
|
|
}
|