mirror of
https://github.com/Waujito/youtubeUnblock.git
synced 2024-12-21 22:05:56 +00:00
Massive update of argparse system
This is required for furhter maintance of kernel module. Aims to provide common interface for both
This commit is contained in:
parent
9b5c8a729d
commit
457a7a7f04
2
Kbuild
2
Kbuild
@ -1,3 +1,3 @@
|
||||
obj-m := kyoutubeUnblock.o
|
||||
kyoutubeUnblock-objs := kytunblock.o mangle.o quic.o utils.o kargs.o tls.o
|
||||
kyoutubeUnblock-objs := kytunblock.o mangle.o quic.o utils.o kargs.o tls.o getopt.o args.o
|
||||
ccflags-y := -std=gnu99 -DKERNEL_SPACE -Wno-error -Wno-declaration-after-statement
|
||||
|
771
args.c
771
args.c
@ -1,33 +1,219 @@
|
||||
#include "config.h"
|
||||
#include <stdbool.h>
|
||||
#include <errno.h>
|
||||
#include <stdio.h>
|
||||
#include <unistd.h>
|
||||
#include <getopt.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include "types.h"
|
||||
|
||||
#include "types.h"
|
||||
#include "args.h"
|
||||
#include "logging.h"
|
||||
#include "getopt.h"
|
||||
#include "raw_replacements.h"
|
||||
|
||||
static char custom_fake_buf[MAX_FAKE_SIZE];
|
||||
#ifdef KERNEL_SPACE
|
||||
static int errno = 0;
|
||||
#define strtol kstrtol
|
||||
#endif
|
||||
|
||||
struct config_t config = {
|
||||
.threads = THREADS_NUM,
|
||||
.queue_start_num = DEFAULT_QUEUE_NUM,
|
||||
.mark = DEFAULT_RAWSOCKET_MARK,
|
||||
.use_ipv6 = 1,
|
||||
struct config_t config = default_config_set;
|
||||
|
||||
.verbose = VERBOSE_DEBUG,
|
||||
.use_gso = true,
|
||||
static int parse_sni_domains(struct domains_list **dlist, const char *domains_str, size_t domains_strlen) {
|
||||
// Empty and shouldn't be used
|
||||
struct domains_list ndomain = {0};
|
||||
struct domains_list *cdomain = &ndomain;
|
||||
|
||||
.default_config = default_section_config,
|
||||
.custom_configs_len = 0,
|
||||
unsigned int j = 0;
|
||||
for (unsigned int i = 0; i <= domains_strlen; i++) {
|
||||
if (( i == domains_strlen ||
|
||||
domains_str[i] == '\0' ||
|
||||
domains_str[i] == ',' ||
|
||||
domains_str[i] == '\n' )) {
|
||||
|
||||
.daemonize = 0,
|
||||
.noclose = 0,
|
||||
.syslog = 0,
|
||||
if (i == j) {
|
||||
j++;
|
||||
continue;
|
||||
}
|
||||
|
||||
unsigned int domain_len = (i - j);
|
||||
const char *domain_startp = domains_str + j;
|
||||
struct domains_list *edomain = malloc(sizeof(struct domains_list));
|
||||
*edomain = (struct domains_list){0};
|
||||
if (edomain == NULL) {
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
edomain->domain_len = domain_len;
|
||||
edomain->domain_name = malloc(domain_len + 1);
|
||||
if (edomain->domain_name == NULL) {
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
strncpy(edomain->domain_name, domain_startp, domain_len);
|
||||
edomain->domain_name[domain_len] = '\0';
|
||||
cdomain->next = edomain;
|
||||
cdomain = edomain;
|
||||
|
||||
j = i + 1;
|
||||
}
|
||||
}
|
||||
|
||||
*dlist = ndomain.next;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void free_sni_domains(struct domains_list *dlist) {
|
||||
for (struct domains_list *ldl = dlist; ldl != NULL;) {
|
||||
struct domains_list *ndl = ldl->next;
|
||||
printf("freeing domains\n");
|
||||
SFREE(ldl->domain_name);
|
||||
SFREE(ldl);
|
||||
ldl = ndl;
|
||||
}
|
||||
}
|
||||
|
||||
static long parse_numeric_option(const char* value) {
|
||||
errno = 0;
|
||||
|
||||
if (*value == '\0') {
|
||||
errno = EINVAL;
|
||||
return 0;
|
||||
}
|
||||
|
||||
long result;
|
||||
int len;
|
||||
sscanf(value, "%ld%n", &result, &len);
|
||||
if (*(value + len) != '\0') {
|
||||
errno = EINVAL;
|
||||
return 0;
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
static int parse_udp_dport_range(char *str, struct udp_dport_range **udpr, int *udpr_len) {
|
||||
int ret = 0;
|
||||
int seclen = 1;
|
||||
const char *p = str;
|
||||
while (*p != '\0') {
|
||||
if (*p == ',')
|
||||
seclen++;
|
||||
p++;
|
||||
}
|
||||
|
||||
#ifdef KERNEL_SPACE
|
||||
struct udp_dport_range *udp_dport_ranges = kmalloc(
|
||||
seclen * sizeof(struct udp_dport_range), GFP_KERNEL);
|
||||
|
||||
#else
|
||||
struct udp_dport_range *udp_dport_ranges = malloc(
|
||||
seclen * sizeof(struct udp_dport_range));
|
||||
#endif
|
||||
if (udp_dport_ranges == NULL) {
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
int i = 0;
|
||||
|
||||
|
||||
p = str;
|
||||
const char *ep = p;
|
||||
while (1) {
|
||||
if (*ep == '\0' || *ep == ',') {
|
||||
if (ep == p) {
|
||||
if (*ep == '\0')
|
||||
break;
|
||||
|
||||
p++, ep++;
|
||||
continue;
|
||||
}
|
||||
|
||||
const char *endp;
|
||||
long num1;
|
||||
int len;
|
||||
sscanf(p, "%ld%n", &num1, &len);
|
||||
endp = p + len;
|
||||
long num2 = num1;
|
||||
|
||||
if (endp != ep) {
|
||||
if (*endp == '-') {
|
||||
endp++;
|
||||
int len;
|
||||
sscanf(endp, "%ld%n", &num2, &len);
|
||||
endp = endp + len;
|
||||
|
||||
if (endp != ep)
|
||||
goto erret;
|
||||
} else {
|
||||
goto erret;
|
||||
}
|
||||
}
|
||||
|
||||
if (
|
||||
!(num1 > 0 && num1 < (1 << 16)) ||
|
||||
!(num2 > 0 && num2 < (1 << 16)) ||
|
||||
num2 < num1
|
||||
)
|
||||
goto erret;
|
||||
|
||||
udp_dport_ranges[i] = (struct udp_dport_range){
|
||||
.start = num1,
|
||||
.end = num2
|
||||
};
|
||||
i++;
|
||||
|
||||
if (*ep == '\0') {
|
||||
break;
|
||||
} else {
|
||||
p = ep + 1;
|
||||
ep = p;
|
||||
}
|
||||
} else {
|
||||
ep++;
|
||||
}
|
||||
}
|
||||
|
||||
if (i == 0) {
|
||||
free(udp_dport_ranges);
|
||||
}
|
||||
|
||||
*udpr = udp_dport_ranges;
|
||||
*udpr_len = i;
|
||||
return 0;
|
||||
|
||||
erret:
|
||||
free(udp_dport_ranges);
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
// Allocates and fills custom fake buffer
|
||||
static int parse_fake_custom_payload(
|
||||
const char *custom_hex_fake,
|
||||
char **custom_fake_buf, unsigned int *custom_fake_len) {
|
||||
int ret;
|
||||
|
||||
size_t custom_hlen = strlen(custom_hex_fake);
|
||||
if ((custom_hlen & 1) == 1) {
|
||||
printf("Custom fake hex should be divisible by two\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
size_t custom_len = custom_hlen >> 1;
|
||||
if (custom_len > MAX_FAKE_SIZE) {
|
||||
printf("Custom fake is too large\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
unsigned char *custom_buf = malloc(custom_len);
|
||||
|
||||
for (int i = 0; i < custom_len; i++) {
|
||||
ret = sscanf(custom_hex_fake + (i << 1), "%2hhx", custom_buf + i);
|
||||
if (ret != 1) {
|
||||
free(custom_buf);
|
||||
return -EINVAL;
|
||||
}
|
||||
}
|
||||
|
||||
*custom_fake_buf = (char *)custom_buf;
|
||||
*custom_fake_len = custom_len;
|
||||
return 0;
|
||||
}
|
||||
|
||||
enum {
|
||||
OPT_SNI_DOMAINS,
|
||||
@ -69,11 +255,14 @@ enum {
|
||||
OPT_UDP_DPORT_FILTER,
|
||||
OPT_UDP_FILTER_QUIC,
|
||||
OPT_TLS_ENABLED,
|
||||
OPT_CLS,
|
||||
OPT_HELP,
|
||||
OPT_VERSION,
|
||||
};
|
||||
|
||||
static struct option long_opt[] = {
|
||||
{"help", 0, 0, 'h'},
|
||||
{"version", 0, 0, 'v'},
|
||||
{"help", 0, 0, OPT_HELP},
|
||||
{"version", 0, 0, OPT_VERSION},
|
||||
{"sni-domains", 1, 0, OPT_SNI_DOMAINS},
|
||||
{"exclude-domains", 1, 0, OPT_EXCLUDE_DOMAINS},
|
||||
{"fake-sni", 1, 0, OPT_FAKE_SNI},
|
||||
@ -113,27 +302,10 @@ static struct option long_opt[] = {
|
||||
{"packet-mark", 1, 0, OPT_PACKET_MARK},
|
||||
{"fbegin", 0, 0, OPT_START_SECTION},
|
||||
{"fend", 0, 0, OPT_END_SECTION},
|
||||
{0,0,0,0}
|
||||
{"cls", 0, 0, OPT_CLS},
|
||||
{0, 0, 0, 0},
|
||||
};
|
||||
|
||||
static long parse_numeric_option(const char* value) {
|
||||
errno = 0;
|
||||
|
||||
if (*value == '\0') {
|
||||
errno = EINVAL;
|
||||
return 0;
|
||||
}
|
||||
|
||||
char* end;
|
||||
long result = strtol(value, &end, 10);
|
||||
if (*end != '\0') {
|
||||
errno = EINVAL;
|
||||
return 0;
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
void print_version(void) {
|
||||
printf("youtubeUnblock"
|
||||
#if defined(PKG_VERSION)
|
||||
@ -192,93 +364,22 @@ void print_usage(const char *argv0) {
|
||||
printf("\n");
|
||||
}
|
||||
|
||||
int parse_udp_dport_range(char *str, struct udp_dport_range **udpr, int *udpr_len) {
|
||||
int ret = 0;
|
||||
int seclen = 1;
|
||||
int strlen = 0;
|
||||
const char *p = optarg;
|
||||
while (*p != '\0') {
|
||||
if (*p == ',')
|
||||
seclen++;
|
||||
p++;
|
||||
}
|
||||
strlen = p - optarg;
|
||||
|
||||
struct udp_dport_range *udp_dport_ranges = malloc(
|
||||
seclen * sizeof(struct udp_dport_range));
|
||||
|
||||
int i = 0;
|
||||
|
||||
|
||||
p = optarg;
|
||||
const char *ep = p;
|
||||
while (1) {
|
||||
if (*ep == '\0' || *ep == ',') {
|
||||
if (ep == p) {
|
||||
if (*ep == '\0')
|
||||
break;
|
||||
|
||||
p++, ep++;
|
||||
continue;
|
||||
}
|
||||
|
||||
char *endp;
|
||||
long num1 = strtol(p, &endp, 10);
|
||||
long num2 = num1;
|
||||
if (errno)
|
||||
goto erret;
|
||||
|
||||
if (endp != ep) {
|
||||
if (*endp == '-') {
|
||||
endp++;
|
||||
num2 = strtol(endp, &endp, 10);
|
||||
|
||||
if (endp != ep || errno)
|
||||
goto erret;
|
||||
} else {
|
||||
goto erret;
|
||||
}
|
||||
}
|
||||
|
||||
if (
|
||||
!(num1 > 0 && num1 < (1 << 16)) ||
|
||||
!(num2 > 0 && num2 < (1 << 16)) ||
|
||||
num2 < num1
|
||||
)
|
||||
goto erret;
|
||||
|
||||
udp_dport_ranges[i] = (struct udp_dport_range){
|
||||
.start = num1,
|
||||
.end = num2
|
||||
};
|
||||
i++;
|
||||
|
||||
if (*ep == '\0') {
|
||||
break;
|
||||
} else {
|
||||
p = ep + 1;
|
||||
ep = p;
|
||||
}
|
||||
} else {
|
||||
ep++;
|
||||
}
|
||||
}
|
||||
|
||||
*udpr = udp_dport_ranges;
|
||||
*udpr_len = seclen;
|
||||
return 0;
|
||||
|
||||
erret:
|
||||
free(udp_dport_ranges);
|
||||
return -1;
|
||||
}
|
||||
|
||||
int parse_args(int argc, char *argv[]) {
|
||||
int yparse_args(int argc, char *argv[]) {
|
||||
int opt;
|
||||
int optIdx = 0;
|
||||
optind=1, opterr=1, optreset=0;
|
||||
long num;
|
||||
int ret;
|
||||
|
||||
struct section_config_t *sect_config = &config.default_config;
|
||||
struct config_t rep_config;
|
||||
ret = init_config(&rep_config);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
struct section_config_t *default_section = rep_config.last_section;
|
||||
|
||||
struct section_config_t *sect_config = rep_config.last_section;
|
||||
int sect_i = 0;
|
||||
sect_config->id = sect_i++;
|
||||
|
||||
#define SECT_ITER_DEFAULT 1
|
||||
#define SECT_ITER_INSIDE 2
|
||||
@ -286,86 +387,95 @@ int parse_args(int argc, char *argv[]) {
|
||||
|
||||
int section_iter = SECT_ITER_DEFAULT;
|
||||
|
||||
while ((opt = getopt_long(argc, argv, "hv", long_opt, &optIdx)) != -1) {
|
||||
while ((opt = getopt_long(argc, argv, "", long_opt, &optIdx)) != -1) {
|
||||
switch (opt) {
|
||||
case OPT_CLS:
|
||||
free_config(rep_config);
|
||||
ret = init_config(&rep_config);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
default_section = rep_config.last_section;
|
||||
|
||||
sect_config = rep_config.last_section;
|
||||
sect_i = 0;
|
||||
sect_config->id = sect_i++;
|
||||
section_iter = SECT_ITER_DEFAULT;
|
||||
|
||||
break;
|
||||
|
||||
/* config_t scoped configs */
|
||||
case 'h':
|
||||
case OPT_HELP:
|
||||
print_usage(argv[0]);
|
||||
#ifndef KERNEL_SPACE
|
||||
goto stop_exec;
|
||||
case 'v':
|
||||
#else
|
||||
break;
|
||||
#endif
|
||||
case OPT_VERSION:
|
||||
print_version();
|
||||
#ifndef KERNEL_SPACE
|
||||
goto stop_exec;
|
||||
#else
|
||||
break;
|
||||
#endif
|
||||
case OPT_TRACE:
|
||||
if (section_iter != SECT_ITER_DEFAULT)
|
||||
goto invalid_opt;
|
||||
config.verbose = 2;
|
||||
rep_config.verbose = 2;
|
||||
break;
|
||||
case OPT_SILENT:
|
||||
if (section_iter != SECT_ITER_DEFAULT)
|
||||
goto invalid_opt;
|
||||
|
||||
config.verbose = 0;
|
||||
rep_config.verbose = 0;
|
||||
break;
|
||||
case OPT_NO_GSO:
|
||||
if (section_iter != SECT_ITER_DEFAULT)
|
||||
goto invalid_opt;
|
||||
|
||||
config.use_gso = 0;
|
||||
rep_config.use_gso = 0;
|
||||
break;
|
||||
case OPT_NO_IPV6:
|
||||
if (section_iter != SECT_ITER_DEFAULT)
|
||||
goto invalid_opt;
|
||||
|
||||
config.use_ipv6 = 0;
|
||||
rep_config.use_ipv6 = 0;
|
||||
break;
|
||||
case OPT_DAEMONIZE:
|
||||
config.daemonize = 1;
|
||||
rep_config.daemonize = 1;
|
||||
break;
|
||||
case OPT_NOCLOSE:
|
||||
config.noclose = 1;
|
||||
rep_config.noclose = 1;
|
||||
break;
|
||||
case OPT_SYSLOG:
|
||||
config.syslog = 1;
|
||||
rep_config.syslog = 1;
|
||||
break;
|
||||
case OPT_THREADS:
|
||||
if (section_iter != SECT_ITER_DEFAULT)
|
||||
goto invalid_opt;
|
||||
|
||||
num = parse_numeric_option(optarg);
|
||||
if (errno != 0 || num < 0 || num > MAX_THREADS) {
|
||||
goto invalid_opt;
|
||||
}
|
||||
|
||||
config.threads = num;
|
||||
rep_config.threads = num;
|
||||
break;
|
||||
case OPT_QUEUE_NUM:
|
||||
if (section_iter != SECT_ITER_DEFAULT)
|
||||
goto invalid_opt;
|
||||
|
||||
num = parse_numeric_option(optarg);
|
||||
if (errno != 0 || num < 0) {
|
||||
goto invalid_opt;
|
||||
}
|
||||
|
||||
config.queue_start_num = num;
|
||||
rep_config.queue_start_num = num;
|
||||
break;
|
||||
case OPT_PACKET_MARK:
|
||||
if (section_iter != SECT_ITER_DEFAULT)
|
||||
goto invalid_opt;
|
||||
|
||||
num = parse_numeric_option(optarg);
|
||||
if (errno != 0 || num < 0) {
|
||||
goto invalid_opt;
|
||||
}
|
||||
|
||||
config.mark = num;
|
||||
rep_config.mark = num;
|
||||
break;
|
||||
case OPT_START_SECTION:
|
||||
if (section_iter != SECT_ITER_DEFAULT && section_iter != SECT_ITER_OUTSIDE)
|
||||
goto invalid_opt;
|
||||
|
||||
sect_config = &config.custom_configs[config.custom_configs_len++];
|
||||
*sect_config = (struct section_config_t)default_section_config;
|
||||
struct section_config_t *nsect;
|
||||
ret = init_section_config(&nsect, rep_config.last_section);
|
||||
if (ret < 0) {
|
||||
goto error;
|
||||
}
|
||||
rep_config.last_section->next = nsect;
|
||||
rep_config.last_section = nsect;
|
||||
sect_config = nsect;
|
||||
sect_config->id = sect_i++;
|
||||
section_iter = SECT_ITER_INSIDE;
|
||||
|
||||
break;
|
||||
@ -374,7 +484,7 @@ int parse_args(int argc, char *argv[]) {
|
||||
goto invalid_opt;
|
||||
|
||||
section_iter = SECT_ITER_OUTSIDE;
|
||||
sect_config = &config.default_config;
|
||||
sect_config = default_section;
|
||||
break;
|
||||
|
||||
/* section_config_t scoped configs */
|
||||
@ -389,16 +499,20 @@ int parse_args(int argc, char *argv[]) {
|
||||
|
||||
break;
|
||||
case OPT_SNI_DOMAINS:
|
||||
sect_config->all_domains = 0;
|
||||
if (!strcmp(optarg, "all")) {
|
||||
sect_config->all_domains = 1;
|
||||
}
|
||||
|
||||
sect_config->domains_str = optarg;
|
||||
sect_config->domains_strlen = strlen(sect_config->domains_str);
|
||||
ret = parse_sni_domains(§_config->sni_domains, optarg, strlen(optarg));
|
||||
if (ret < 0)
|
||||
goto error;
|
||||
break;
|
||||
case OPT_EXCLUDE_DOMAINS:
|
||||
sect_config->exclude_domains_str = optarg;
|
||||
sect_config->exclude_domains_strlen = strlen(sect_config->exclude_domains_str);
|
||||
ret = parse_sni_domains(§_config->exclude_sni_domains, optarg, strlen(optarg));
|
||||
if (ret < 0)
|
||||
goto error;
|
||||
|
||||
break;
|
||||
case OPT_FRAG:
|
||||
if (strcmp(optarg, "tcp") == 0) {
|
||||
@ -512,30 +626,16 @@ int parse_args(int argc, char *argv[]) {
|
||||
}
|
||||
|
||||
break;
|
||||
case OPT_FAKE_CUSTOM_PAYLOAD: {
|
||||
uint8_t *const custom_buf = (uint8_t *)custom_fake_buf;
|
||||
case OPT_FAKE_CUSTOM_PAYLOAD:
|
||||
SFREE(sect_config->udp_dport_range);
|
||||
|
||||
const char *custom_hex_fake = optarg;
|
||||
size_t custom_hlen = strlen(custom_hex_fake);
|
||||
if ((custom_hlen & 1) == 1) {
|
||||
printf("Custom fake hex should be divisible by two\n");
|
||||
ret = parse_fake_custom_payload(optarg, §_config->fake_custom_pkt, §_config->fake_custom_pkt_sz);
|
||||
if (ret == -EINVAL) {
|
||||
goto invalid_opt;
|
||||
} else if (ret < 0) {
|
||||
goto error;
|
||||
}
|
||||
|
||||
|
||||
size_t custom_len = custom_hlen >> 1;
|
||||
if (custom_len > MAX_FAKE_SIZE) {
|
||||
printf("Custom fake is too large\n");
|
||||
goto invalid_opt;
|
||||
}
|
||||
|
||||
for (int i = 0; i < custom_len; i++) {
|
||||
sscanf(custom_hex_fake + (i << 1), "%2hhx", custom_buf + i);
|
||||
}
|
||||
|
||||
sect_config->fake_custom_pkt_sz = custom_len;
|
||||
sect_config->fake_custom_pkt = (char *)custom_buf;
|
||||
}
|
||||
break;
|
||||
case OPT_FK_WINSIZE:
|
||||
num = parse_numeric_option(optarg);
|
||||
@ -623,13 +723,10 @@ int parse_args(int argc, char *argv[]) {
|
||||
break;
|
||||
case OPT_UDP_DPORT_FILTER:
|
||||
{
|
||||
struct udp_dport_range *udp_dport_range;
|
||||
int udp_range_len = 0;
|
||||
if (parse_udp_dport_range(optarg, &udp_dport_range, &udp_range_len) < 0) {
|
||||
SFREE(sect_config->udp_dport_range);
|
||||
if (parse_udp_dport_range(optarg, §_config->udp_dport_range, §_config->udp_dport_range_len) < 0) {
|
||||
goto invalid_opt;
|
||||
}
|
||||
sect_config->udp_dport_range = udp_dport_range;
|
||||
sect_config->udp_dport_range_len = udp_range_len;
|
||||
break;
|
||||
}
|
||||
case OPT_UDP_FILTER_QUIC:
|
||||
@ -648,22 +745,248 @@ int parse_args(int argc, char *argv[]) {
|
||||
|
||||
}
|
||||
|
||||
struct config_t old_config = config;
|
||||
config = rep_config;
|
||||
free_config(old_config);
|
||||
|
||||
errno = 0;
|
||||
return 0;
|
||||
|
||||
stop_exec:
|
||||
free_config(rep_config);
|
||||
errno = 0;
|
||||
return 1;
|
||||
|
||||
invalid_opt:
|
||||
printf("Invalid option %s\n", long_opt[optIdx].name);
|
||||
ret = -EINVAL;
|
||||
error:
|
||||
#ifndef KERNEL_SPACE
|
||||
print_usage(argv[0]);
|
||||
errno = EINVAL;
|
||||
#endif
|
||||
if (ret != -EINVAL) {
|
||||
lgerror(ret, "Error thrown in %s\n", long_opt[optIdx].name);
|
||||
}
|
||||
|
||||
errno = -ret;
|
||||
free_config(rep_config);
|
||||
return -errno;
|
||||
}
|
||||
|
||||
#define print_cnf_raw(fmt, ...) do { \
|
||||
sz = snprintf(buf_ptr, buf_sz, fmt, ##__VA_ARGS__); \
|
||||
if (sz > buf_sz) { buf_sz = 0; } \
|
||||
else { buf_sz -= sz; } \
|
||||
buf_ptr += sz; \
|
||||
} while(0)
|
||||
|
||||
#define print_cnf_buf(fmt, ...) print_cnf_raw(fmt " ", ##__VA_ARGS__)
|
||||
// Returns written buffer size
|
||||
static size_t print_config_section(const struct section_config_t *section, char *buffer, size_t buffer_size) {
|
||||
char *buf_ptr = buffer;
|
||||
size_t buf_sz = buffer_size;
|
||||
size_t sz;
|
||||
|
||||
if (section->tls_enabled) {
|
||||
print_cnf_buf("--tls=enabled");
|
||||
if (section->sni_domains != NULL) {
|
||||
print_cnf_raw("--sni-domains=");
|
||||
for (struct domains_list *sne = section->sni_domains; sne != NULL; sne = sne->next) {
|
||||
print_cnf_raw("%s,", sne->domain_name);
|
||||
}
|
||||
print_cnf_raw(" ");
|
||||
}
|
||||
if (section->exclude_sni_domains != NULL) {
|
||||
print_cnf_raw("--exclude-domains=");
|
||||
for (struct domains_list *sne = section->exclude_sni_domains; sne != NULL; sne = sne->next) {
|
||||
print_cnf_raw("%s,", sne->domain_name);
|
||||
}
|
||||
print_cnf_raw(" ");
|
||||
}
|
||||
|
||||
switch(section->fragmentation_strategy) {
|
||||
case FRAG_STRAT_IP:
|
||||
print_cnf_buf("--frag=ip");
|
||||
break;
|
||||
case FRAG_STRAT_TCP:
|
||||
print_cnf_buf("--frag=tcp");
|
||||
break;
|
||||
case FRAG_STRAT_NONE:
|
||||
print_cnf_buf("--frag=none");
|
||||
break;
|
||||
}
|
||||
|
||||
print_cnf_buf("frag-sni-reverse=%d", section->frag_sni_reverse);
|
||||
print_cnf_buf("frag-sni-faked=%d", section->frag_sni_faked);
|
||||
print_cnf_buf("frag-middle-sni=%d", section->frag_middle_sni);
|
||||
print_cnf_buf("frag-sni-pos=%d", section->frag_sni_pos);
|
||||
print_cnf_buf("fk-winsize=%d", section->fk_winsize);
|
||||
|
||||
if (section->fake_sni) {
|
||||
print_cnf_buf("--fake-sni=1");
|
||||
print_cnf_buf("--fake-sni-seq-len=%d", section->fake_sni_seq_len);
|
||||
switch(section->fake_sni_type) {
|
||||
case FAKE_PAYLOAD_CUSTOM:
|
||||
print_cnf_buf("--fake-sni-type=custom");
|
||||
print_cnf_buf("--fake-custom-payload=<hidden>");
|
||||
break;
|
||||
case FAKE_PAYLOAD_RANDOM:
|
||||
print_cnf_buf("--fake-sni-type=random");
|
||||
break;
|
||||
case FAKE_PAYLOAD_DEFAULT:
|
||||
print_cnf_buf("--fake-sni-type=default");
|
||||
break;
|
||||
}
|
||||
|
||||
switch(section->faking_strategy) {
|
||||
case FAKE_STRAT_TTL:
|
||||
print_cnf_buf("--faking-strategy=ttl");
|
||||
print_cnf_buf("--faking-ttl=%d", section->faking_ttl);
|
||||
break;
|
||||
case FAKE_STRAT_RAND_SEQ:
|
||||
print_cnf_buf("--faking-strategy=randseq");
|
||||
break;
|
||||
case FAKE_STRAT_TCP_CHECK:
|
||||
print_cnf_buf("--faking-strategy=tcp_check");
|
||||
break;
|
||||
case FAKE_STRAT_TCP_MD5SUM:
|
||||
print_cnf_buf("--faking-strategy=md5sum");
|
||||
break;
|
||||
case FAKE_STRAT_PAST_SEQ:
|
||||
print_cnf_buf("--faking-strategy=pastseq");
|
||||
print_cnf_buf("--fake-seq-offset=%d", section->fakeseq_offset);
|
||||
break;
|
||||
|
||||
}
|
||||
|
||||
switch(section->sni_detection) {
|
||||
case SNI_DETECTION_BRUTE:
|
||||
print_cnf_buf("--sni_detection=brute");
|
||||
break;
|
||||
case SNI_DETECTION_PARSE:
|
||||
print_cnf_buf("--sni_detection=parse");
|
||||
break;
|
||||
|
||||
}
|
||||
|
||||
print_cnf_buf("--seg2delay=%d", section->seg2_delay);
|
||||
}
|
||||
} else {
|
||||
print_cnf_buf("--tls=disabled");
|
||||
}
|
||||
|
||||
if (section->synfake) {
|
||||
print_cnf_buf("--synfake=1");
|
||||
print_cnf_buf("--synfake-len=%d", section->synfake_len);
|
||||
} else {
|
||||
print_cnf_buf("--synfake=0");
|
||||
}
|
||||
|
||||
|
||||
if (section->udp_filter_quic == UDP_FILTER_QUIC_ALL && section->udp_mode == UDP_MODE_DROP) {
|
||||
print_cnf_buf("--drop-quic");
|
||||
}
|
||||
|
||||
switch(section->udp_filter_quic) {
|
||||
case UDP_FILTER_QUIC_ALL:
|
||||
print_cnf_buf("--udp-filter-quic=all");
|
||||
break;
|
||||
case UDP_FILTER_QUIC_DISABLED:
|
||||
print_cnf_buf("--udp-filter-quic=disabled");
|
||||
break;
|
||||
}
|
||||
|
||||
if (section->udp_dport_range_len != 0)
|
||||
print_cnf_raw("--udp-dport-filter=");
|
||||
for (int i = 0; i < section->udp_dport_range_len; i++) {
|
||||
struct udp_dport_range range = section->udp_dport_range[i];
|
||||
print_cnf_raw("%d-%d,", range.start, range.end);
|
||||
}
|
||||
print_cnf_raw(" ");
|
||||
|
||||
|
||||
if (section->udp_filter_quic != UDP_FILTER_QUIC_DISABLED || section->udp_dport_range_len != 0) {
|
||||
switch(section->udp_mode) {
|
||||
case UDP_MODE_DROP:
|
||||
print_cnf_buf("--udp-mode=drop");
|
||||
break;
|
||||
case UDP_MODE_FAKE:
|
||||
print_cnf_buf("--udp-mode=fake");
|
||||
print_cnf_buf("--udp-fake-seq-len=%d", section->udp_fake_seq_len);
|
||||
{
|
||||
switch(section->udp_faking_strategy) {
|
||||
case FAKE_STRAT_UDP_CHECK:
|
||||
print_cnf_buf("--udp-faking-strategy=checksum");
|
||||
break;
|
||||
case FAKE_STRAT_TTL:
|
||||
print_cnf_buf("--udp-faking-strategy=ttl");
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return buffer_size - buf_sz;
|
||||
}
|
||||
// Returns written buffer length
|
||||
size_t print_config(char *buffer, size_t buffer_size) {
|
||||
char *buf_ptr = buffer;
|
||||
size_t buf_sz = buffer_size;
|
||||
size_t sz;
|
||||
|
||||
#ifndef KERNEL_SPACE
|
||||
print_cnf_buf("--queue-num=%d", config.queue_start_num);
|
||||
print_cnf_buf("--threads=%d", config.threads);
|
||||
#endif
|
||||
print_cnf_buf("--mark=%d", config.mark);
|
||||
|
||||
#ifndef KERNEL_SPACE
|
||||
if (config.daemonize) {
|
||||
print_cnf_buf("--daemonize");
|
||||
}
|
||||
if (config.syslog) {
|
||||
print_cnf_buf("--syslog");
|
||||
}
|
||||
if (config.noclose) {
|
||||
print_cnf_buf("--noclose");
|
||||
}
|
||||
if (!config.use_gso) {
|
||||
print_cnf_buf("--no-gso");
|
||||
}
|
||||
#endif
|
||||
if (!config.use_ipv6) {
|
||||
print_cnf_buf("--no-ipv6");
|
||||
}
|
||||
if (config.verbose == VERBOSE_TRACE) {
|
||||
print_cnf_buf("--trace");
|
||||
}
|
||||
if (config.verbose == VERBOSE_INFO) {
|
||||
print_cnf_buf("--silent");
|
||||
}
|
||||
|
||||
size_t wbuf_len = print_config_section(config.first_section, buf_ptr, buf_sz);
|
||||
buf_ptr += wbuf_len;
|
||||
buf_sz -= wbuf_len;
|
||||
|
||||
for (struct section_config_t *section = config.first_section->next;
|
||||
section != NULL; section = section->next) {
|
||||
print_cnf_buf("--fbegin");
|
||||
wbuf_len = print_config_section(section, buf_ptr, buf_sz);
|
||||
buf_ptr += wbuf_len;
|
||||
buf_sz -= wbuf_len;
|
||||
print_cnf_buf("--fend");
|
||||
}
|
||||
|
||||
return buffer_size - buf_sz;
|
||||
}
|
||||
|
||||
void print_welcome(void) {
|
||||
char welcome_message[4000];
|
||||
|
||||
size_t sz = print_config(welcome_message, 4000);
|
||||
printf("Running with flags: %.*s\n", (int)sz, welcome_message);
|
||||
return;
|
||||
/**
|
||||
if (config.syslog) {
|
||||
printf("Logging to system log\n");
|
||||
}
|
||||
@ -762,5 +1085,73 @@ void print_welcome(void) {
|
||||
lginfo("Target sni domains: %s\n", section->domains_str);
|
||||
}
|
||||
}
|
||||
*/
|
||||
}
|
||||
|
||||
int init_section_config(struct section_config_t **section, struct section_config_t *prev) {
|
||||
struct section_config_t *def_section = NULL;
|
||||
int ret;
|
||||
#ifdef KERNEL_SPACE
|
||||
def_section = kmalloc(sizeof(struct section_config_t), GFP_KERNEL);
|
||||
#else
|
||||
def_section = malloc(sizeof(struct section_config_t));
|
||||
#endif
|
||||
*def_section = (struct section_config_t)default_section_config;
|
||||
def_section->prev = prev;
|
||||
|
||||
if (def_section == NULL)
|
||||
return -ENOMEM;
|
||||
|
||||
ret = parse_sni_domains(&def_section->sni_domains, default_snistr, sizeof(default_snistr));
|
||||
if (ret < 0) {
|
||||
free(def_section);
|
||||
return ret;
|
||||
}
|
||||
|
||||
def_section->fake_sni_pkt = fake_sni_old;
|
||||
def_section->fake_sni_pkt_sz = sizeof(fake_sni_old) - 1;
|
||||
|
||||
*section = def_section;
|
||||
return 0;
|
||||
}
|
||||
|
||||
int init_config(struct config_t *config) {
|
||||
struct config_t def_config = default_config_set;
|
||||
int ret = 0;
|
||||
struct section_config_t *def_section = NULL;
|
||||
ret = init_section_config(&def_section, NULL);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
def_config.last_section = def_section;
|
||||
def_config.first_section = def_section;
|
||||
|
||||
*config = def_config;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void free_config_section(struct section_config_t *section) {
|
||||
lginfo("freeing %d\n", section->id);
|
||||
if (section->udp_dport_range_len != 0) {
|
||||
SFREE(section->udp_dport_range);
|
||||
}
|
||||
|
||||
free_sni_domains(section->sni_domains);
|
||||
section->sni_domains = NULL;
|
||||
free_sni_domains(section->exclude_sni_domains);
|
||||
section->exclude_sni_domains = NULL;
|
||||
|
||||
section->fake_custom_pkt_sz = 0;
|
||||
SFREE(section->fake_custom_pkt);
|
||||
|
||||
free(section);
|
||||
}
|
||||
|
||||
void free_config(struct config_t config) {
|
||||
lginfo("freeing config\n");
|
||||
for (struct section_config_t *sct = config.last_section; sct != NULL;) {
|
||||
struct section_config_t *psct = sct->prev;
|
||||
free_config_section(sct);
|
||||
sct = psct;
|
||||
}
|
||||
}
|
||||
|
14
args.h
14
args.h
@ -1,9 +1,21 @@
|
||||
#ifndef ARGS_H
|
||||
#define ARGS_H
|
||||
#include "types.h"
|
||||
#include "config.h"
|
||||
|
||||
void print_version(void);
|
||||
void print_usage(const char *argv0);
|
||||
int parse_args(int argc, char *argv[]);
|
||||
int yparse_args(int argc, char *argv[]);
|
||||
size_t print_config(char *buffer, size_t buffer_size);
|
||||
|
||||
// Initializes configuration storage.
|
||||
int init_config(struct config_t *config);
|
||||
// Allocates and initializes configuration section.
|
||||
int init_section_config(struct section_config_t **section, struct section_config_t *prev);
|
||||
// Frees configuration section
|
||||
void free_config_section(struct section_config_t *config);
|
||||
// Frees sections under config
|
||||
void free_config(struct config_t config);
|
||||
|
||||
/* Prints starting messages */
|
||||
void print_welcome(void);
|
||||
|
77
config.h
77
config.h
@ -5,7 +5,6 @@
|
||||
#define USER_SPACE
|
||||
#endif
|
||||
|
||||
#include "raw_replacements.h"
|
||||
#include "types.h"
|
||||
|
||||
typedef int (*raw_send_t)(const unsigned char *data, unsigned int data_len);
|
||||
@ -26,9 +25,21 @@ struct udp_dport_range {
|
||||
uint16_t end;
|
||||
};
|
||||
|
||||
struct domains_list {
|
||||
char *domain_name;
|
||||
uint16_t domain_len;
|
||||
|
||||
struct domains_list *next;
|
||||
};
|
||||
|
||||
struct section_config_t {
|
||||
const char *domains_str;
|
||||
unsigned int domains_strlen;
|
||||
int id;
|
||||
struct section_config_t *next;
|
||||
struct section_config_t *prev;
|
||||
|
||||
struct domains_list *sni_domains;
|
||||
struct domains_list *exclude_sni_domains;
|
||||
unsigned int all_domains;
|
||||
|
||||
int tls_enabled;
|
||||
|
||||
@ -53,14 +64,10 @@ struct section_config_t {
|
||||
int synfake;
|
||||
unsigned int synfake_len;
|
||||
|
||||
const char *exclude_domains_str;
|
||||
unsigned int exclude_domains_strlen;
|
||||
unsigned int all_domains;
|
||||
|
||||
const char *fake_sni_pkt;
|
||||
unsigned int fake_sni_pkt_sz;
|
||||
|
||||
const char *fake_custom_pkt;
|
||||
char *fake_custom_pkt;
|
||||
unsigned int fake_custom_pkt_sz;
|
||||
|
||||
unsigned int fk_winsize;
|
||||
@ -98,17 +105,16 @@ struct config_t {
|
||||
#define VERBOSE_TRACE 2
|
||||
int verbose;
|
||||
|
||||
struct section_config_t default_config;
|
||||
struct section_config_t custom_configs[MAX_CONFIGLIST_LEN];
|
||||
int custom_configs_len;
|
||||
struct section_config_t *first_section;
|
||||
struct section_config_t *last_section;
|
||||
};
|
||||
|
||||
extern struct config_t config;
|
||||
|
||||
#define ITER_CONFIG_SECTIONS(section) \
|
||||
for (struct section_config_t *section = &config.default_config + config.custom_configs_len; section >= &config.default_config; section--)
|
||||
#define ITER_CONFIG_SECTIONS(config, section) \
|
||||
for (struct section_config_t *section = (config)->last_section; section != NULL; section = section->prev)
|
||||
|
||||
#define CONFIG_SECTION_NUMBER(section) (int)((section) - &config.default_config)
|
||||
#define CONFIG_SECTION_NUMBER(section) ((section)->id)
|
||||
|
||||
#define MAX_THREADS 16
|
||||
|
||||
@ -180,7 +186,7 @@ if ((fake_bitmask) & strategy)
|
||||
|
||||
#define DEFAULT_SNISTR "googlevideo.com,ggpht.com,ytimg.com,youtube.com,play.google.com,youtu.be,googleapis.com,googleusercontent.com,gstatic.com,l.google.com"
|
||||
|
||||
static const char defaul_snistr[] = DEFAULT_SNISTR;
|
||||
static const char default_snistr[] = DEFAULT_SNISTR;
|
||||
|
||||
enum {
|
||||
UDP_MODE_DROP,
|
||||
@ -193,6 +199,9 @@ enum {
|
||||
};
|
||||
|
||||
#define default_section_config { \
|
||||
.sni_domains = NULL, \
|
||||
.exclude_sni_domains = NULL, \
|
||||
.all_domains = 0, \
|
||||
.tls_enabled = 1, \
|
||||
.frag_sni_reverse = 1, \
|
||||
.frag_sni_faked = 0, \
|
||||
@ -202,6 +211,8 @@ enum {
|
||||
.fake_sni = 1, \
|
||||
.fake_sni_seq_len = 1, \
|
||||
.fake_sni_type = FAKE_PAYLOAD_DEFAULT, \
|
||||
.fake_custom_pkt = NULL, \
|
||||
.fake_custom_pkt_sz = 0, \
|
||||
.frag_middle_sni = 1, \
|
||||
.frag_sni_pos = 1, \
|
||||
.fakeseq_offset = 10000, \
|
||||
@ -210,16 +221,6 @@ enum {
|
||||
\
|
||||
.seg2_delay = 0, \
|
||||
\
|
||||
.domains_str = defaul_snistr, \
|
||||
.domains_strlen = sizeof(defaul_snistr), \
|
||||
\
|
||||
.exclude_domains_str = "", \
|
||||
.exclude_domains_strlen = 0, \
|
||||
\
|
||||
.fake_sni_pkt = fake_sni_old, \
|
||||
.fake_sni_pkt_sz = sizeof(fake_sni_old) - 1, \
|
||||
.fake_custom_pkt = custom_fake_buf, \
|
||||
.fake_custom_pkt_sz = 0, \
|
||||
.sni_detection = SNI_DETECTION_PARSE, \
|
||||
\
|
||||
.udp_mode = UDP_MODE_FAKE, \
|
||||
@ -229,6 +230,32 @@ enum {
|
||||
.udp_dport_range = NULL, \
|
||||
.udp_dport_range_len = 0, \
|
||||
.udp_filter_quic = UDP_FILTER_QUIC_DISABLED, \
|
||||
\
|
||||
.prev = NULL, \
|
||||
.next = NULL, \
|
||||
.id = 0, \
|
||||
}
|
||||
|
||||
#define default_config_set { \
|
||||
.threads = THREADS_NUM, \
|
||||
.queue_start_num = DEFAULT_QUEUE_NUM, \
|
||||
.mark = DEFAULT_RAWSOCKET_MARK, \
|
||||
.use_ipv6 = 1, \
|
||||
\
|
||||
.verbose = VERBOSE_DEBUG, \
|
||||
.use_gso = 1, \
|
||||
\
|
||||
.first_section = NULL, \
|
||||
.last_section = NULL, \
|
||||
\
|
||||
.daemonize = 0, \
|
||||
.noclose = 0, \
|
||||
.syslog = 0, \
|
||||
}
|
||||
|
||||
#define CONFIG_SET(config) \
|
||||
struct config_t config = default_config_set; \
|
||||
config->last_section = &(config.default_config) \
|
||||
|
||||
|
||||
#endif /* YTB_CONFIG_H */
|
||||
|
204
getopt.c
Normal file
204
getopt.c
Normal file
@ -0,0 +1,204 @@
|
||||
#include "types.h"
|
||||
#include "logging.h"
|
||||
#include "getopt.h"
|
||||
|
||||
char *optarg;
|
||||
int optind=1, opterr=1, optopt, __optpos, optreset=0;
|
||||
|
||||
#define optpos __optpos
|
||||
|
||||
static void __getopt_msg(const char *b, const char *c, size_t l)
|
||||
{
|
||||
lgerr("%s %.*s\n", b, (int)l, c);
|
||||
}
|
||||
|
||||
int getopt(int argc, char * const argv[], const char *optstring)
|
||||
{
|
||||
int i, c, d;
|
||||
int k, l;
|
||||
char *optchar;
|
||||
|
||||
if (!optind || optreset) {
|
||||
optreset = 0;
|
||||
__optpos = 0;
|
||||
optind = 1;
|
||||
}
|
||||
|
||||
if (optind >= argc || !argv[optind])
|
||||
return -1;
|
||||
|
||||
if (argv[optind][0] != '-') {
|
||||
if (optstring[0] == '-') {
|
||||
optarg = argv[optind++];
|
||||
return 1;
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (!argv[optind][1])
|
||||
return -1;
|
||||
|
||||
if (argv[optind][1] == '-' && !argv[optind][2])
|
||||
return optind++, -1;
|
||||
|
||||
if (!optpos) optpos++;
|
||||
c = argv[optind][optpos], k = 1;
|
||||
optchar = argv[optind]+optpos;
|
||||
optopt = c;
|
||||
optpos += k;
|
||||
|
||||
if (!argv[optind][optpos]) {
|
||||
optind++;
|
||||
optpos = 0;
|
||||
}
|
||||
|
||||
if (optstring[0] == '-' || optstring[0] == '+')
|
||||
optstring++;
|
||||
|
||||
i = 0;
|
||||
d = 0;
|
||||
do {
|
||||
d = optstring[i], l = 1;
|
||||
if (l>0) i+=l; else i++;
|
||||
} while (l && d != c);
|
||||
|
||||
if (d != c) {
|
||||
if (optstring[0] != ':' && opterr)
|
||||
__getopt_msg("Unrecognized option: ", optchar, k);
|
||||
return '?';
|
||||
}
|
||||
if (optstring[i] == ':') {
|
||||
if (optstring[i+1] == ':') optarg = 0;
|
||||
else if (optind >= argc) {
|
||||
if (optstring[0] == ':') return ':';
|
||||
if (opterr) __getopt_msg("Option requires an argument: ",
|
||||
optchar, k);
|
||||
return '?';
|
||||
}
|
||||
if (optstring[i+1] != ':' || optpos) {
|
||||
optarg = argv[optind++] + optpos;
|
||||
optpos = 0;
|
||||
}
|
||||
}
|
||||
return c;
|
||||
}
|
||||
|
||||
static void permute(char *const *argv, int dest, int src)
|
||||
{
|
||||
char **av = (char **)argv;
|
||||
char *tmp = av[src];
|
||||
int i;
|
||||
for (i=src; i>dest; i--)
|
||||
av[i] = av[i-1];
|
||||
av[dest] = tmp;
|
||||
}
|
||||
|
||||
static int __getopt_long_core(int argc, char *const *argv, const char *optstring, const struct option *longopts, int *idx, int longonly)
|
||||
{
|
||||
optarg = 0;
|
||||
if (longopts && argv[optind][0] == '-' &&
|
||||
((longonly && argv[optind][1] && argv[optind][1] != '-') ||
|
||||
(argv[optind][1] == '-' && argv[optind][2])))
|
||||
{
|
||||
int colon = optstring[optstring[0]=='+'||optstring[0]=='-']==':';
|
||||
int i, cnt, match = -1;
|
||||
char *opt;
|
||||
for (cnt=i=0; longopts[i].name; i++) {
|
||||
const char *name = longopts[i].name;
|
||||
opt = argv[optind]+1;
|
||||
if (*opt == '-') opt++;
|
||||
for (; *name && *name == *opt; name++, opt++);
|
||||
if (*opt && *opt != '=') continue;
|
||||
match = i;
|
||||
if (!*name) {
|
||||
cnt = 1;
|
||||
break;
|
||||
}
|
||||
cnt++;
|
||||
}
|
||||
if (cnt==1) {
|
||||
i = match;
|
||||
optind++;
|
||||
optopt = longopts[i].val;
|
||||
if (*opt == '=') {
|
||||
if (!longopts[i].has_arg) {
|
||||
if (colon || !opterr)
|
||||
return '?';
|
||||
__getopt_msg(
|
||||
"Option does not take an argument: ",
|
||||
longopts[i].name,
|
||||
strlen(longopts[i].name));
|
||||
return '?';
|
||||
}
|
||||
optarg = opt+1;
|
||||
} else if (longopts[i].has_arg == required_argument) {
|
||||
if (!(optarg = argv[optind])) {
|
||||
if (colon) return ':';
|
||||
if (!opterr) return '?';
|
||||
__getopt_msg(
|
||||
"Option requires an argument: ",
|
||||
longopts[i].name,
|
||||
strlen(longopts[i].name));
|
||||
return '?';
|
||||
}
|
||||
optind++;
|
||||
}
|
||||
if (idx) *idx = i;
|
||||
if (longopts[i].flag) {
|
||||
*longopts[i].flag = longopts[i].val;
|
||||
return 0;
|
||||
}
|
||||
return longopts[i].val;
|
||||
}
|
||||
if (argv[optind][1] == '-') {
|
||||
if (!colon && opterr)
|
||||
__getopt_msg(cnt ?
|
||||
"Option is ambiguous: " :
|
||||
"Unrecognized option: ",
|
||||
argv[optind]+2,
|
||||
strlen(argv[optind]+2));
|
||||
optind++;
|
||||
return '?';
|
||||
}
|
||||
}
|
||||
return getopt(argc, argv, optstring);
|
||||
}
|
||||
|
||||
static int __getopt_long(int argc, char *const *argv, const char *optstring, const struct option *longopts, int *idx, int longonly)
|
||||
{
|
||||
int ret, skipped, resumed;
|
||||
if (!optind || optreset) {
|
||||
optreset = 0;
|
||||
__optpos = 0;
|
||||
optind = 1;
|
||||
}
|
||||
if (optind >= argc || !argv[optind]) return -1;
|
||||
skipped = optind;
|
||||
if (optstring[0] != '+' && optstring[0] != '-') {
|
||||
int i;
|
||||
for (i=optind; ; i++) {
|
||||
if (i >= argc || !argv[i]) return -1;
|
||||
if (argv[i][0] == '-' && argv[i][1]) break;
|
||||
}
|
||||
optind = i;
|
||||
}
|
||||
resumed = optind;
|
||||
ret = __getopt_long_core(argc, argv, optstring, longopts, idx, longonly);
|
||||
if (resumed > skipped) {
|
||||
int i, cnt = optind-resumed;
|
||||
for (i=0; i<cnt; i++)
|
||||
permute(argv, skipped, optind-1);
|
||||
optind = skipped + cnt;
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
int getopt_long(int argc, char *const *argv, const char *optstring, const struct option *longopts, int *idx)
|
||||
{
|
||||
return __getopt_long(argc, argv, optstring, longopts, idx, 0);
|
||||
}
|
||||
|
||||
int getopt_long_only(int argc, char *const *argv, const char *optstring, const struct option *longopts, int *idx)
|
||||
{
|
||||
return __getopt_long(argc, argv, optstring, longopts, idx, 1);
|
||||
}
|
45
getopt.h
Normal file
45
getopt.h
Normal file
@ -0,0 +1,45 @@
|
||||
/*
|
||||
Copyright 2005-2014 Rich Felker, et al.
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining
|
||||
a copy of this software and associated documentation files (the
|
||||
"Software"), to deal in the Software without restriction, including
|
||||
without limitation the rights to use, copy, modify, merge, publish,
|
||||
distribute, sublicense, and/or sell copies of the Software, and to
|
||||
permit persons to whom the Software is furnished to do so, subject to
|
||||
the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be
|
||||
included in all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
|
||||
IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
|
||||
CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
|
||||
TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
|
||||
SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
*/
|
||||
|
||||
#ifndef _GETOPT_H
|
||||
#define _GETOPT_H
|
||||
|
||||
int getopt(int, char * const [], const char *);
|
||||
extern char *optarg;
|
||||
extern int optind, opterr, optopt, optreset;
|
||||
|
||||
struct option {
|
||||
const char *name;
|
||||
int has_arg;
|
||||
int *flag;
|
||||
int val;
|
||||
};
|
||||
|
||||
int getopt_long(int, char *const *, const char *, const struct option *, int *);
|
||||
int getopt_long_only(int, char *const *, const char *, const struct option *, int *);
|
||||
|
||||
#define no_argument 0
|
||||
#define required_argument 1
|
||||
#define optional_argument 2
|
||||
|
||||
#endif
|
@ -20,6 +20,7 @@
|
||||
#include "config.h"
|
||||
#include "utils.h"
|
||||
#include "logging.h"
|
||||
#include "args.h"
|
||||
|
||||
#if defined(PKG_VERSION)
|
||||
MODULE_VERSION(PKG_VERSION);
|
||||
@ -206,9 +207,9 @@ erret_lc:
|
||||
int ipvx = netproto_version(pkt, pktlen);
|
||||
|
||||
if (ipvx == IP4VERSION) {
|
||||
return send_raw_ipv4(pkt, pktlen);
|
||||
ret = send_raw_ipv4(pkt, pktlen);
|
||||
} else if (ipvx == IP6VERSION) {
|
||||
return send_raw_ipv6(pkt, pktlen);
|
||||
ret = send_raw_ipv6(pkt, pktlen);
|
||||
} else {
|
||||
printf("proto version %d is unsupported\n", ipvx);
|
||||
return -EINVAL;
|
||||
@ -346,6 +347,8 @@ static struct nf_hook_ops ykb6_nf_reg __read_mostly = {
|
||||
|
||||
static int __init ykb_init(void) {
|
||||
int ret = 0;
|
||||
ret = init_config(&config);
|
||||
if (ret < 0) goto err;
|
||||
|
||||
ret = open_raw_socket();
|
||||
if (ret < 0) goto err;
|
||||
@ -420,6 +423,8 @@ static void __exit ykb_destroy(void) {
|
||||
#endif
|
||||
|
||||
close_raw_socket();
|
||||
|
||||
free_config(config);
|
||||
lginfo("youtubeUnblock kernel module destroyed.\n");
|
||||
}
|
||||
|
||||
|
2
mangle.c
2
mangle.c
@ -62,7 +62,7 @@ int process_packet(const uint8_t *raw_payload, uint32_t raw_payload_len) {
|
||||
lgtrace_addp("UDP");
|
||||
|
||||
|
||||
ITER_CONFIG_SECTIONS(section) {
|
||||
ITER_CONFIG_SECTIONS(&config, section) {
|
||||
lgtrace_addp("Section #%d", CONFIG_SECTION_NUMBER(section));
|
||||
|
||||
switch (transport_proto) {
|
||||
|
251
tls.c
251
tls.c
@ -9,6 +9,114 @@
|
||||
#include <unistd.h>
|
||||
#endif
|
||||
|
||||
static int bruteforce_analyze_sni_str(
|
||||
const struct section_config_t *section,
|
||||
const uint8_t *data, size_t dlen,
|
||||
struct tls_verdict *vrd
|
||||
) {
|
||||
if (section->all_domains) {
|
||||
vrd->target_sni = 1;
|
||||
vrd->sni_len = 0;
|
||||
vrd->sni_offset = dlen / 2;
|
||||
return 0;
|
||||
}
|
||||
|
||||
for (struct domains_list *sne = section->sni_domains; sne != NULL; sne = sne->next) {
|
||||
const char *domain_startp = sne->domain_name;
|
||||
int domain_len = sne->domain_len;
|
||||
|
||||
if (sne->domain_len + dlen + 1 > MAX_PACKET_SIZE) {
|
||||
continue;
|
||||
}
|
||||
|
||||
NETBUF_ALLOC(buf, MAX_PACKET_SIZE);
|
||||
if (!NETBUF_CHECK(buf)) {
|
||||
lgerror(-ENOMEM, "Allocation error");
|
||||
return -ENOMEM;
|
||||
}
|
||||
NETBUF_ALLOC(nzbuf, MAX_PACKET_SIZE * sizeof(int));
|
||||
if (!NETBUF_CHECK(nzbuf)) {
|
||||
lgerror(-ENOMEM, "Allocation error");
|
||||
NETBUF_FREE(buf);
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
int *zbuf = (void *)nzbuf;
|
||||
|
||||
memcpy(buf, domain_startp, domain_len);
|
||||
memcpy(buf + domain_len, "#", 1);
|
||||
memcpy(buf + domain_len + 1, data, dlen);
|
||||
|
||||
z_function((char *)buf, zbuf, domain_len + 1 + dlen);
|
||||
|
||||
for (unsigned int k = 0; k < dlen; k++) {
|
||||
if (zbuf[k] == domain_len) {
|
||||
vrd->target_sni = 1;
|
||||
vrd->sni_len = domain_len;
|
||||
vrd->sni_offset = (k - domain_len - 1);
|
||||
vrd->sni_target_offset = vrd->sni_offset;
|
||||
vrd->sni_target_len = vrd->sni_len;
|
||||
NETBUF_FREE(buf);
|
||||
NETBUF_FREE(nzbuf);
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
NETBUF_FREE(buf);
|
||||
NETBUF_FREE(nzbuf);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
static int analyze_sni_str(
|
||||
const struct section_config_t *section,
|
||||
const char *sni_name, int sni_len, const uint8_t *data,
|
||||
struct tls_verdict *vrd
|
||||
) {
|
||||
if (section->all_domains) {
|
||||
vrd->target_sni = 1;
|
||||
goto check_domain;
|
||||
}
|
||||
|
||||
for (struct domains_list *sne = section->sni_domains; sne != NULL; sne = sne->next) {
|
||||
const char *sni_startp = sni_name + sni_len - sne->domain_len;
|
||||
const char *domain_startp = sne->domain_name;
|
||||
|
||||
if (sni_len >= sne->domain_len &&
|
||||
sni_len < 128 &&
|
||||
!strncmp(sni_startp,
|
||||
domain_startp,
|
||||
sne->domain_len)) {
|
||||
vrd->target_sni = 1;
|
||||
vrd->sni_target_offset = (const uint8_t *)sni_startp - data;
|
||||
vrd->sni_target_len = sne->domain_len;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
check_domain:
|
||||
if (vrd->target_sni == 1) {
|
||||
for (struct domains_list *sne = section->exclude_sni_domains; sne != NULL; sne = sne->next) {
|
||||
const char *sni_startp = sni_name + sni_len - sne->domain_len;
|
||||
const char *domain_startp = sne->domain_name;
|
||||
|
||||
if (sni_len >= sne->domain_len &&
|
||||
sni_len < 128 &&
|
||||
!strncmp(sni_startp,
|
||||
domain_startp,
|
||||
sne->domain_len)) {
|
||||
vrd->target_sni = 0;
|
||||
lgdebugmsg("Excluded SNI: %.*s",
|
||||
vrd->sni_len, data + vrd->sni_offset);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
#define TLS_CONTENT_TYPE_HANDSHAKE 0x16
|
||||
#define TLS_HANDSHAKE_TYPE_CLIENT_HELLO 0x01
|
||||
#define TLS_EXTENSION_SNI 0x0000
|
||||
@ -26,10 +134,16 @@ struct tls_verdict analyze_tls_data(
|
||||
uint32_t dlen)
|
||||
{
|
||||
struct tls_verdict vrd = {0};
|
||||
int ret;
|
||||
|
||||
size_t i = 0;
|
||||
const uint8_t *data_end = data + dlen;
|
||||
|
||||
if (section->sni_detection == SNI_DETECTION_BRUTE) {
|
||||
ret = bruteforce_analyze_sni_str(section, data, dlen, &vrd);
|
||||
goto out;
|
||||
}
|
||||
|
||||
while (i + 4 < dlen) {
|
||||
const uint8_t *msgData = data + i;
|
||||
|
||||
@ -44,10 +158,6 @@ struct tls_verdict analyze_tls_data(
|
||||
if (tls_content_type != TLS_CONTENT_TYPE_HANDSHAKE)
|
||||
goto nextMessage;
|
||||
|
||||
if (section->sni_detection == SNI_DETECTION_BRUTE) {
|
||||
goto brute;
|
||||
}
|
||||
|
||||
const uint8_t *handshakeProto = msgData + 5;
|
||||
|
||||
if (handshakeProto + 1 >= data_end) break;
|
||||
@ -120,76 +230,14 @@ struct tls_verdict analyze_tls_data(
|
||||
|
||||
if (sni_ext_ptr + sni_len > sni_ext_end) break;
|
||||
|
||||
char *sni_name = (char *)sni_ext_ptr;
|
||||
const char *sni_name = (char *)sni_ext_ptr;
|
||||
|
||||
vrd.sni_offset = (uint8_t *)sni_name - data;
|
||||
vrd.sni_target_offset = vrd.sni_offset;
|
||||
vrd.sni_len = sni_len;
|
||||
vrd.sni_target_len = vrd.sni_len;
|
||||
|
||||
if (section->all_domains) {
|
||||
vrd.target_sni = 1;
|
||||
goto check_domain;
|
||||
}
|
||||
|
||||
unsigned int j = 0;
|
||||
for (unsigned int i = 0; i <= section->domains_strlen; i++) {
|
||||
if ( i > j &&
|
||||
(i == section->domains_strlen ||
|
||||
section->domains_str[i] == '\0' ||
|
||||
section->domains_str[i] == ',' ||
|
||||
section->domains_str[i] == '\n' )) {
|
||||
|
||||
unsigned int domain_len = (i - j);
|
||||
const char *sni_startp = sni_name + sni_len - domain_len;
|
||||
const char *domain_startp = section->domains_str + j;
|
||||
|
||||
if (sni_len >= domain_len &&
|
||||
sni_len < 128 &&
|
||||
!strncmp(sni_startp,
|
||||
domain_startp,
|
||||
domain_len)) {
|
||||
vrd.target_sni = 1;
|
||||
vrd.sni_target_offset = (const uint8_t *)sni_startp - data;
|
||||
vrd.sni_target_len = domain_len;
|
||||
goto check_domain;
|
||||
}
|
||||
|
||||
j = i + 1;
|
||||
}
|
||||
}
|
||||
|
||||
check_domain:
|
||||
if (vrd.target_sni == 1 && section->exclude_domains_strlen != 0) {
|
||||
unsigned int j = 0;
|
||||
for (unsigned int i = 0; i <= section->exclude_domains_strlen; i++) {
|
||||
if ( i > j &&
|
||||
(i == section->exclude_domains_strlen ||
|
||||
section->exclude_domains_str[i] == '\0' ||
|
||||
section->exclude_domains_str[i] == ',' ||
|
||||
section->exclude_domains_str[i] == '\n' )) {
|
||||
|
||||
unsigned int domain_len = (i - j);
|
||||
const char *sni_startp = sni_name + sni_len - domain_len;
|
||||
const char *domain_startp = section->exclude_domains_str + j;
|
||||
|
||||
if (sni_len >= domain_len &&
|
||||
sni_len < 128 &&
|
||||
!strncmp(sni_startp,
|
||||
domain_startp,
|
||||
domain_len)) {
|
||||
|
||||
vrd.target_sni = 0;
|
||||
lgdebugmsg("Excluded SNI: %.*s",
|
||||
vrd.sni_len, data + vrd.sni_offset);
|
||||
goto out;
|
||||
}
|
||||
|
||||
j = i + 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ret = analyze_sni_str(section, sni_name, sni_len, data, &vrd);
|
||||
goto out;
|
||||
|
||||
nextExtension:
|
||||
@ -201,73 +249,6 @@ nextMessage:
|
||||
|
||||
out:
|
||||
return vrd;
|
||||
|
||||
|
||||
brute:
|
||||
if (section->all_domains) {
|
||||
vrd.target_sni = 1;
|
||||
vrd.sni_len = 0;
|
||||
vrd.sni_offset = dlen / 2;
|
||||
goto out;
|
||||
}
|
||||
|
||||
unsigned int j = 0;
|
||||
for (unsigned int i = 0; i <= section->domains_strlen; i++) {
|
||||
if ( i > j &&
|
||||
(i == section->domains_strlen ||
|
||||
section->domains_str[i] == '\0' ||
|
||||
section->domains_str[i] == ',' ||
|
||||
section->domains_str[i] == '\n' )) {
|
||||
|
||||
unsigned int domain_len = (i - j);
|
||||
const char *domain_startp = section->domains_str + j;
|
||||
|
||||
if (domain_len + dlen + 1> MAX_PACKET_SIZE) {
|
||||
continue;
|
||||
}
|
||||
|
||||
NETBUF_ALLOC(buf, MAX_PACKET_SIZE);
|
||||
if (!NETBUF_CHECK(buf)) {
|
||||
lgerror(-ENOMEM, "Allocation error");
|
||||
goto out;
|
||||
}
|
||||
NETBUF_ALLOC(nzbuf, MAX_PACKET_SIZE * sizeof(int));
|
||||
if (!NETBUF_CHECK(nzbuf)) {
|
||||
lgerror(-ENOMEM, "Allocation error");
|
||||
NETBUF_FREE(buf);
|
||||
goto out;
|
||||
}
|
||||
|
||||
int *zbuf = (void *)nzbuf;
|
||||
|
||||
memcpy(buf, domain_startp, domain_len);
|
||||
memcpy(buf + domain_len, "#", 1);
|
||||
memcpy(buf + domain_len + 1, data, dlen);
|
||||
|
||||
z_function((char *)buf, zbuf, domain_len + 1 + dlen);
|
||||
|
||||
for (unsigned int k = 0; k < dlen; k++) {
|
||||
if (zbuf[k] == domain_len) {
|
||||
vrd.target_sni = 1;
|
||||
vrd.sni_len = domain_len;
|
||||
vrd.sni_offset = (k - domain_len - 1);
|
||||
vrd.sni_target_offset = vrd.sni_offset;
|
||||
vrd.sni_target_len = vrd.sni_len;
|
||||
NETBUF_FREE(buf);
|
||||
NETBUF_FREE(nzbuf);
|
||||
goto out;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
j = i + 1;
|
||||
|
||||
NETBUF_FREE(buf);
|
||||
NETBUF_FREE(nzbuf);
|
||||
}
|
||||
}
|
||||
|
||||
goto out;
|
||||
}
|
||||
|
||||
int gen_fake_sni(struct fake_type type,
|
||||
|
8
types.h
8
types.h
@ -34,6 +34,9 @@
|
||||
#include <linux/tcp.h> // IWYU pragma: export
|
||||
#include <linux/version.h>
|
||||
|
||||
#define free kfree
|
||||
#define malloc(size) kmalloc((size), GFP_KERNEL)
|
||||
|
||||
#define ip6_hdr ipv6hdr
|
||||
|
||||
/* from <netinet/ip.h> */
|
||||
@ -67,6 +70,11 @@
|
||||
#include <netinet/udp.h> // IWYU pragma: export
|
||||
#endif
|
||||
|
||||
#define SFREE(item) do { \
|
||||
free((item)); \
|
||||
(item) = NULL; \
|
||||
} while (0)
|
||||
|
||||
#ifndef KERNEL_SPACE
|
||||
|
||||
#define max(a,b)__extension__\
|
||||
|
@ -31,7 +31,7 @@ export CC CCLD LD CFLAGS LDFLAGS LIBNFNETLINK_CFLAGS LIBNFNETLINK_LIBS LIBMNL_CF
|
||||
|
||||
APP:=$(BUILD_DIR)/youtubeUnblock
|
||||
|
||||
SRCS := youtubeUnblock.c mangle.c args.c utils.c quic.c tls.c
|
||||
SRCS := youtubeUnblock.c mangle.c args.c utils.c quic.c tls.c getopt.c
|
||||
OBJS := $(SRCS:%.c=$(BUILD_DIR)/%.o)
|
||||
|
||||
LIBNFNETLINK := $(DEPSDIR)/lib/libnfnetlink.la
|
||||
|
@ -612,7 +612,7 @@ struct instance_config_t instance_config = {
|
||||
|
||||
int main(int argc, char *argv[]) {
|
||||
int ret;
|
||||
if ((ret = parse_args(argc, argv)) != 0) {
|
||||
if ((ret = yparse_args(argc, argv)) != 0) {
|
||||
if (ret < 0) {
|
||||
lgerror(-errno, "Unable to parse args");
|
||||
exit(EXIT_FAILURE);
|
||||
|
Loading…
Reference in New Issue
Block a user