mirror of
https://github.com/dovecoteescapee/ByeDPIAndroid.git
synced 2024-12-22 06:15:44 +00:00
Merge pull request #74 from dovecoteescapee/dev
Add command line style settings and refactor settings
This commit is contained in:
commit
18fcf9262c
@ -19,8 +19,7 @@
|
|||||||
tools:targetApi="34">
|
tools:targetApi="34">
|
||||||
<activity
|
<activity
|
||||||
android:name=".activities.MainActivity"
|
android:name=".activities.MainActivity"
|
||||||
android:exported="true"
|
android:exported="true">
|
||||||
android:launchMode="singleInstance">
|
|
||||||
<intent-filter>
|
<intent-filter>
|
||||||
<action android:name="android.intent.action.MAIN" />
|
<action android:name="android.intent.action.MAIN" />
|
||||||
<action android:name="android.service.quicksettings.action.QS_TILE_PREFERENCES" />
|
<action android:name="android.service.quicksettings.action.QS_TILE_PREFERENCES" />
|
||||||
|
@ -10,3 +10,9 @@ int get_addr(const char *str, struct sockaddr_ina *addr);
|
|||||||
void *add(void **root, int *n, size_t ss);
|
void *add(void **root, int *n, size_t ss);
|
||||||
|
|
||||||
void clear_params(void);
|
void clear_params(void);
|
||||||
|
|
||||||
|
char *ftob(const char *str, ssize_t *sl);
|
||||||
|
|
||||||
|
struct mphdr *parse_hosts(char *buffer, size_t size);
|
||||||
|
|
||||||
|
int parse_offset(struct part *part, const char *str);
|
||||||
|
@ -19,12 +19,43 @@ const enum demode DESYNC_METHODS[] = {
|
|||||||
DESYNC_OOB,
|
DESYNC_OOB,
|
||||||
};
|
};
|
||||||
|
|
||||||
JNIEXPORT jint JNI_OnLoad(JavaVM *vm, __attribute__((unused)) void *reserved) {
|
JNIEXPORT jint JNI_OnLoad(
|
||||||
oob_data.data = NULL;
|
__attribute__((unused)) JavaVM *vm,
|
||||||
|
__attribute__((unused)) void *reserved) {
|
||||||
default_params = params;
|
default_params = params;
|
||||||
return JNI_VERSION_1_6;
|
return JNI_VERSION_1_6;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
JNIEXPORT jint JNICALL
|
||||||
|
Java_io_github_dovecoteescapee_byedpi_core_ByeDpiProxy_jniCreateSocketWithCommandLine(
|
||||||
|
JNIEnv *env,
|
||||||
|
__attribute__((unused)) jobject thiz,
|
||||||
|
jobjectArray args) {
|
||||||
|
int argc = (*env)->GetArrayLength(env, args);
|
||||||
|
char *argv[argc];
|
||||||
|
for (int i = 0; i < argc; i++) {
|
||||||
|
jstring arg = (jstring) (*env)->GetObjectArrayElement(env, args, i);
|
||||||
|
const char *arg_str = (*env)->GetStringUTFChars(env, arg, 0);
|
||||||
|
argv[i] = strdup(arg_str);
|
||||||
|
(*env)->ReleaseStringUTFChars(env, arg, arg_str);
|
||||||
|
}
|
||||||
|
|
||||||
|
int res = parse_args(argc, argv);
|
||||||
|
if (res < 0) {
|
||||||
|
uniperror("parse_args");
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
int fd = listen_socket((struct sockaddr_ina *)¶ms.laddr);
|
||||||
|
if (fd < 0) {
|
||||||
|
uniperror("listen_socket");
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
LOG(LOG_S, "listen_socket, fd: %d", fd);
|
||||||
|
|
||||||
|
return fd;
|
||||||
|
}
|
||||||
|
|
||||||
JNIEXPORT jint JNICALL
|
JNIEXPORT jint JNICALL
|
||||||
Java_io_github_dovecoteescapee_byedpi_core_ByeDpiProxy_jniCreateSocket(
|
Java_io_github_dovecoteescapee_byedpi_core_ByeDpiProxy_jniCreateSocket(
|
||||||
JNIEnv *env,
|
JNIEnv *env,
|
||||||
@ -36,6 +67,9 @@ Java_io_github_dovecoteescapee_byedpi_core_ByeDpiProxy_jniCreateSocket(
|
|||||||
jint default_ttl,
|
jint default_ttl,
|
||||||
jboolean custom_ttl,
|
jboolean custom_ttl,
|
||||||
jboolean no_domain,
|
jboolean no_domain,
|
||||||
|
jboolean desync_http,
|
||||||
|
jboolean desync_https,
|
||||||
|
jboolean desync_udp,
|
||||||
jint desync_method,
|
jint desync_method,
|
||||||
jint split_position,
|
jint split_position,
|
||||||
jboolean split_at_host,
|
jboolean split_at_host,
|
||||||
@ -90,6 +124,10 @@ Java_io_github_dovecoteescapee_byedpi_core_ByeDpiProxy_jniCreateSocket(
|
|||||||
}
|
}
|
||||||
|
|
||||||
dp->ttl = fake_ttl;
|
dp->ttl = fake_ttl;
|
||||||
|
dp->proto =
|
||||||
|
IS_HTTP * desync_http |
|
||||||
|
IS_HTTPS * desync_https |
|
||||||
|
IS_UDP * desync_udp;
|
||||||
dp->mod_http =
|
dp->mod_http =
|
||||||
MH_HMIX * host_mixed_case |
|
MH_HMIX * host_mixed_case |
|
||||||
MH_DMIX * domain_mixed_case |
|
MH_DMIX * domain_mixed_case |
|
||||||
@ -108,7 +146,9 @@ Java_io_github_dovecoteescapee_byedpi_core_ByeDpiProxy_jniCreateSocket(
|
|||||||
|
|
||||||
enum demode mode = DESYNC_METHODS[desync_method];
|
enum demode mode = DESYNC_METHODS[desync_method];
|
||||||
|
|
||||||
part->flag = split_at_host ? OFFSET_SNI : 0;
|
int offset_flag = dp->proto || desync_https ? OFFSET_SNI : OFFSET_HOST;
|
||||||
|
|
||||||
|
part->flag = split_at_host ? offset_flag : 0;
|
||||||
part->pos = split_position;
|
part->pos = split_position;
|
||||||
part->m = mode;
|
part->m = mode;
|
||||||
|
|
||||||
@ -125,7 +165,7 @@ Java_io_github_dovecoteescapee_byedpi_core_ByeDpiProxy_jniCreateSocket(
|
|||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
tlsrec_part->flag = tls_record_split_at_sni ? OFFSET_SNI : 0;
|
tlsrec_part->flag = tls_record_split_at_sni ? offset_flag : 0;
|
||||||
tlsrec_part->pos = tls_record_split_position;
|
tlsrec_part->pos = tls_record_split_position;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -154,6 +194,16 @@ Java_io_github_dovecoteescapee_byedpi_core_ByeDpiProxy_jniCreateSocket(
|
|||||||
(*env)->ReleaseStringUTFChars(env, custom_oob_data, oob);
|
(*env)->ReleaseStringUTFChars(env, custom_oob_data, oob);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (dp->proto) {
|
||||||
|
dp = add((void *)¶ms.dp,
|
||||||
|
¶ms.dp_count, sizeof(struct desync_params));
|
||||||
|
if (!dp) {
|
||||||
|
uniperror("add");
|
||||||
|
clear_params();
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
params.mempool = mem_pool(0);
|
params.mempool = mem_pool(0);
|
||||||
if (!params.mempool) {
|
if (!params.mempool) {
|
||||||
uniperror("mem_pool");
|
uniperror("mem_pool");
|
||||||
@ -200,4 +250,4 @@ Java_io_github_dovecoteescapee_byedpi_core_ByeDpiProxy_jniStopProxy(
|
|||||||
return get_e();
|
return get_e();
|
||||||
}
|
}
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
@ -1,6 +1,12 @@
|
|||||||
#include "utils.h"
|
#include <getopt.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <string.h>
|
||||||
|
|
||||||
#include "byedpi/params.h"
|
#include "byedpi/params.h"
|
||||||
|
#include "error.h"
|
||||||
#include "main.h"
|
#include "main.h"
|
||||||
|
#include "packets.h"
|
||||||
|
#include "utils.h"
|
||||||
|
|
||||||
struct params default_params;
|
struct params default_params;
|
||||||
|
|
||||||
@ -8,3 +14,413 @@ void reset_params(void) {
|
|||||||
clear_params();
|
clear_params();
|
||||||
params = default_params;
|
params = default_params;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
extern const struct option options[35];
|
||||||
|
|
||||||
|
int parse_args(int argc, char **argv) {
|
||||||
|
int optc = sizeof(options)/sizeof(*options);
|
||||||
|
for (int i = 0, e = optc; i < e; i++)
|
||||||
|
optc += options[i].has_arg;
|
||||||
|
|
||||||
|
char opt[optc + 1];
|
||||||
|
opt[optc] = 0;
|
||||||
|
|
||||||
|
for (int i = 0, o = 0; o < optc; i++, o++) {
|
||||||
|
opt[o] = options[i].val;
|
||||||
|
for (int c = options[i].has_arg; c; c--) {
|
||||||
|
o++;
|
||||||
|
opt[o] = ':';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
params.laddr.sin6_port = htons(1080);
|
||||||
|
|
||||||
|
int rez;
|
||||||
|
int invalid = 0;
|
||||||
|
|
||||||
|
long val;
|
||||||
|
char *end = 0;
|
||||||
|
|
||||||
|
struct desync_params *dp = add((void *)¶ms.dp,
|
||||||
|
¶ms.dp_count, sizeof(struct desync_params));
|
||||||
|
if (!dp) {
|
||||||
|
reset_params();
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
optind = optreset = 1;
|
||||||
|
|
||||||
|
while (!invalid && (rez = getopt_long_only(
|
||||||
|
argc, argv, opt, options, 0)) != -1) {
|
||||||
|
switch (rez) {
|
||||||
|
|
||||||
|
case 'N':
|
||||||
|
params.resolve = 0;
|
||||||
|
break;
|
||||||
|
case 'X':
|
||||||
|
params.ipv6 = 0;
|
||||||
|
break;
|
||||||
|
case 'U':
|
||||||
|
params.udp = 0;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 'i':
|
||||||
|
if (get_addr(optarg,
|
||||||
|
(struct sockaddr_ina *)¶ms.laddr) < 0)
|
||||||
|
invalid = 1;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 'p':
|
||||||
|
val = strtol(optarg, &end, 0);
|
||||||
|
if (val <= 0 || val > 0xffff || *end)
|
||||||
|
invalid = 1;
|
||||||
|
else
|
||||||
|
params.laddr.sin6_port = htons(val);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 'I':
|
||||||
|
if (get_addr(optarg,
|
||||||
|
(struct sockaddr_ina *)¶ms.baddr) < 0)
|
||||||
|
invalid = 1;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 'b':
|
||||||
|
val = strtol(optarg, &end, 0);
|
||||||
|
if (val <= 0 || val > INT_MAX/4 || *end)
|
||||||
|
invalid = 1;
|
||||||
|
else
|
||||||
|
params.bfsize = val;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 'c':
|
||||||
|
val = strtol(optarg, &end, 0);
|
||||||
|
if (val <= 0 || val >= (0xffff/2) || *end)
|
||||||
|
invalid = 1;
|
||||||
|
else
|
||||||
|
params.max_open = val;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 'x': //
|
||||||
|
params.debug = strtol(optarg, 0, 0);
|
||||||
|
if (params.debug < 0)
|
||||||
|
invalid = 1;
|
||||||
|
break;
|
||||||
|
|
||||||
|
// desync options
|
||||||
|
|
||||||
|
case 'F':
|
||||||
|
params.tfo = 1;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 'A':
|
||||||
|
dp = add((void *)¶ms.dp, ¶ms.dp_count,
|
||||||
|
sizeof(struct desync_params));
|
||||||
|
if (!dp) {
|
||||||
|
reset_params();
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
if (!optarg) {
|
||||||
|
dp->detect |= DETECT_TORST;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
end = optarg;
|
||||||
|
while (end && !invalid) {
|
||||||
|
switch (*end) {
|
||||||
|
case 't':
|
||||||
|
dp->detect |= DETECT_TORST;
|
||||||
|
break;
|
||||||
|
case 'r':
|
||||||
|
dp->detect |= DETECT_HTTP_LOCAT;
|
||||||
|
break;
|
||||||
|
case 'c':
|
||||||
|
dp->detect |= DETECT_HTTP_CLERR;
|
||||||
|
break;
|
||||||
|
case 's':
|
||||||
|
dp->detect |= DETECT_TLS_INVSID;
|
||||||
|
break;
|
||||||
|
case 'a':
|
||||||
|
dp->detect |= DETECT_TLS_ALERT;
|
||||||
|
break;
|
||||||
|
case 'n':
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
invalid = 1;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
end = strchr(end, ',');
|
||||||
|
if (end) end++;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 'u':
|
||||||
|
val = strtol(optarg, &end, 0);
|
||||||
|
if (val <= 0 || *end)
|
||||||
|
invalid = 1;
|
||||||
|
else
|
||||||
|
params.cache_ttl = val;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 'T':;
|
||||||
|
float f = strtof(optarg, &end);
|
||||||
|
val = (long)(f * 1000);
|
||||||
|
if (val <= 0 || val > UINT_MAX || *end)
|
||||||
|
invalid = 1;
|
||||||
|
else
|
||||||
|
params.timeout = val;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 'K':
|
||||||
|
end = optarg;
|
||||||
|
while (end && !invalid) {
|
||||||
|
switch (*end) {
|
||||||
|
case 't':
|
||||||
|
dp->proto |= IS_HTTPS;
|
||||||
|
break;
|
||||||
|
case 'h':
|
||||||
|
dp->proto |= IS_HTTP;
|
||||||
|
break;
|
||||||
|
case 'u':
|
||||||
|
dp->proto |= IS_UDP;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
invalid = 1;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
end = strchr(end, ',');
|
||||||
|
if (end) end++;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 'H':;
|
||||||
|
if (dp->file_ptr) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
dp->file_ptr = ftob(optarg, &dp->file_size);
|
||||||
|
if (!dp->file_ptr) {
|
||||||
|
uniperror("read/parse");
|
||||||
|
invalid = 1;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
dp->hosts = parse_hosts(dp->file_ptr, dp->file_size);
|
||||||
|
if (!dp->hosts) {
|
||||||
|
perror("parse_hosts");
|
||||||
|
reset_params();
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 's':
|
||||||
|
case 'd':
|
||||||
|
case 'o':
|
||||||
|
case 'f':
|
||||||
|
;
|
||||||
|
struct part *part = add((void *)&dp->parts,
|
||||||
|
&dp->parts_n, sizeof(struct part));
|
||||||
|
if (!part) {
|
||||||
|
reset_params();
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
if (parse_offset(part, optarg)) {
|
||||||
|
invalid = 1;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
switch (rez) {
|
||||||
|
case 's': part->m = DESYNC_SPLIT;
|
||||||
|
break;
|
||||||
|
case 'd': part->m = DESYNC_DISORDER;
|
||||||
|
break;
|
||||||
|
case 'o': part->m = DESYNC_OOB;
|
||||||
|
break;
|
||||||
|
case 'f': part->m = DESYNC_FAKE;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 't':
|
||||||
|
val = strtol(optarg, &end, 0);
|
||||||
|
if (val <= 0 || val > 255 || *end)
|
||||||
|
invalid = 1;
|
||||||
|
else
|
||||||
|
dp->ttl = val;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 'k':
|
||||||
|
if (dp->ip_options) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (optarg)
|
||||||
|
dp->ip_options = ftob(optarg, &dp->ip_options_len);
|
||||||
|
else {
|
||||||
|
dp->ip_options = ip_option;
|
||||||
|
dp->ip_options_len = sizeof(ip_option);
|
||||||
|
}
|
||||||
|
if (!dp->ip_options) {
|
||||||
|
uniperror("read/parse");
|
||||||
|
invalid = 1;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 'S':
|
||||||
|
dp->md5sig = 1;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 'n':
|
||||||
|
if (change_tls_sni(optarg, fake_tls.data, fake_tls.size)) {
|
||||||
|
fprintf(stderr, "error chsni\n");
|
||||||
|
reset_params();
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
printf("sni: %s\n", optarg);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 'l':
|
||||||
|
if (dp->fake_data.data) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
dp->fake_data.data = ftob(optarg, &dp->fake_data.size);
|
||||||
|
if (!dp->fake_data.data) {
|
||||||
|
uniperror("read/parse");
|
||||||
|
invalid = 1;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 'e':
|
||||||
|
if (oob_data.data != oob_char) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
oob_data.data = ftob(optarg, &oob_data.size);
|
||||||
|
if (!oob_data.data) {
|
||||||
|
uniperror("read/parse");
|
||||||
|
invalid = 1;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 'M':
|
||||||
|
end = optarg;
|
||||||
|
while (end && !invalid) {
|
||||||
|
switch (*end) {
|
||||||
|
case 'r':
|
||||||
|
dp->mod_http |= MH_SPACE;
|
||||||
|
break;
|
||||||
|
case 'h':
|
||||||
|
dp->mod_http |= MH_HMIX;
|
||||||
|
break;
|
||||||
|
case 'd':
|
||||||
|
dp->mod_http |= MH_DMIX;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
invalid = 1;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
end = strchr(end, ',');
|
||||||
|
if (end) end++;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 'r':
|
||||||
|
part = add((void *)&dp->tlsrec,
|
||||||
|
&dp->tlsrec_n, sizeof(struct part));
|
||||||
|
if (!part) {
|
||||||
|
reset_params();
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
if (parse_offset(part, optarg)
|
||||||
|
|| part->pos > 0xffff) {
|
||||||
|
invalid = 1;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 'a':
|
||||||
|
val = strtol(optarg, &end, 0);
|
||||||
|
if (val < 0 || val > INT_MAX || *end)
|
||||||
|
invalid = 1;
|
||||||
|
else
|
||||||
|
dp->udp_fake_count = val;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 'V':
|
||||||
|
val = strtol(optarg, &end, 0);
|
||||||
|
if (val <= 0 || val > USHRT_MAX)
|
||||||
|
invalid = 1;
|
||||||
|
else {
|
||||||
|
dp->pf[0] = htons(val);
|
||||||
|
if (*end == '-') {
|
||||||
|
val = strtol(end + 1, &end, 0);
|
||||||
|
if (val <= 0 || val > USHRT_MAX)
|
||||||
|
invalid = 1;
|
||||||
|
}
|
||||||
|
if (*end)
|
||||||
|
invalid = 1;
|
||||||
|
else
|
||||||
|
dp->pf[1] = htons(val);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 'g':
|
||||||
|
val = strtol(optarg, &end, 0);
|
||||||
|
if (val <= 0 || val > 255 || *end)
|
||||||
|
invalid = 1;
|
||||||
|
else {
|
||||||
|
params.def_ttl = val;
|
||||||
|
params.custom_ttl = 1;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 'w': //
|
||||||
|
params.sfdelay = strtol(optarg, &end, 0);
|
||||||
|
if (params.sfdelay < 0 || optarg == end
|
||||||
|
|| params.sfdelay >= 1000 || *end)
|
||||||
|
invalid = 1;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 'W':
|
||||||
|
params.wait_send = 0;
|
||||||
|
break;
|
||||||
|
case 'P':
|
||||||
|
params.protect_path = optarg;
|
||||||
|
break;
|
||||||
|
case 0:
|
||||||
|
break;
|
||||||
|
|
||||||
|
case '?':
|
||||||
|
reset_params();
|
||||||
|
return -1;
|
||||||
|
|
||||||
|
default:
|
||||||
|
printf("?: %c\n", rez);
|
||||||
|
reset_params();
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (invalid) {
|
||||||
|
fprintf(stderr, "invalid value: -%c %s\n", rez, optarg);
|
||||||
|
reset_params();
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
if (dp->hosts || dp->proto || dp->pf[0]) {
|
||||||
|
dp = add((void *)¶ms.dp,
|
||||||
|
¶ms.dp_count, sizeof(struct desync_params));
|
||||||
|
if (!dp) {
|
||||||
|
reset_params();
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (params.baddr.sin6_family != AF_INET6) {
|
||||||
|
params.ipv6 = 0;
|
||||||
|
}
|
||||||
|
if (!params.def_ttl) {
|
||||||
|
if ((params.def_ttl = get_default_ttl()) < 1) {
|
||||||
|
reset_params();
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
params.mempool = mem_pool(0);
|
||||||
|
if (!params.mempool) {
|
||||||
|
uniperror("mem_pool");
|
||||||
|
reset_params();
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
@ -1,3 +1,4 @@
|
|||||||
extern struct params default_params;
|
extern struct params default_params;
|
||||||
|
|
||||||
void reset_params(void);
|
void reset_params(void);
|
||||||
|
int parse_args(int argc, char **argv);
|
||||||
|
@ -19,19 +19,12 @@ import androidx.appcompat.app.AppCompatActivity
|
|||||||
import androidx.core.content.ContextCompat
|
import androidx.core.content.ContextCompat
|
||||||
import androidx.lifecycle.lifecycleScope
|
import androidx.lifecycle.lifecycleScope
|
||||||
import io.github.dovecoteescapee.byedpi.R
|
import io.github.dovecoteescapee.byedpi.R
|
||||||
import io.github.dovecoteescapee.byedpi.data.AppStatus
|
import io.github.dovecoteescapee.byedpi.data.*
|
||||||
import io.github.dovecoteescapee.byedpi.data.FAILED_BROADCAST
|
import io.github.dovecoteescapee.byedpi.fragments.MainSettingsFragment
|
||||||
import io.github.dovecoteescapee.byedpi.data.Mode
|
|
||||||
import io.github.dovecoteescapee.byedpi.data.SENDER
|
|
||||||
import io.github.dovecoteescapee.byedpi.data.STARTED_BROADCAST
|
|
||||||
import io.github.dovecoteescapee.byedpi.data.STOPPED_BROADCAST
|
|
||||||
import io.github.dovecoteescapee.byedpi.data.Sender
|
|
||||||
import io.github.dovecoteescapee.byedpi.fragments.SettingsFragment
|
|
||||||
import io.github.dovecoteescapee.byedpi.databinding.ActivityMainBinding
|
import io.github.dovecoteescapee.byedpi.databinding.ActivityMainBinding
|
||||||
import io.github.dovecoteescapee.byedpi.services.ServiceManager
|
import io.github.dovecoteescapee.byedpi.services.ServiceManager
|
||||||
import io.github.dovecoteescapee.byedpi.services.appStatus
|
import io.github.dovecoteescapee.byedpi.services.appStatus
|
||||||
import io.github.dovecoteescapee.byedpi.utility.getPreferences
|
import io.github.dovecoteescapee.byedpi.utility.*
|
||||||
import io.github.dovecoteescapee.byedpi.utility.mode
|
|
||||||
import kotlinx.coroutines.Dispatchers
|
import kotlinx.coroutines.Dispatchers
|
||||||
import kotlinx.coroutines.launch
|
import kotlinx.coroutines.launch
|
||||||
import java.io.IOException
|
import java.io.IOException
|
||||||
@ -154,9 +147,9 @@ class MainActivity : AppCompatActivity() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
val theme = getPreferences(this)
|
val theme = getPreferences()
|
||||||
.getString("app_theme", null)
|
.getString("app_theme", null)
|
||||||
SettingsFragment.setTheme(theme ?: "system")
|
MainSettingsFragment.setTheme(theme ?: "system")
|
||||||
|
|
||||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU &&
|
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU &&
|
||||||
ContextCompat.checkSelfPermission(
|
ContextCompat.checkSelfPermission(
|
||||||
@ -215,7 +208,7 @@ class MainActivity : AppCompatActivity() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private fun start() {
|
private fun start() {
|
||||||
when (getPreferences(this).mode()) {
|
when (getPreferences().mode()) {
|
||||||
Mode.VPN -> {
|
Mode.VPN -> {
|
||||||
val intentPrepare = VpnService.prepare(this)
|
val intentPrepare = VpnService.prepare(this)
|
||||||
if (intentPrepare != null) {
|
if (intentPrepare != null) {
|
||||||
@ -238,9 +231,9 @@ class MainActivity : AppCompatActivity() {
|
|||||||
|
|
||||||
Log.i(TAG, "Updating status: $status, $mode")
|
Log.i(TAG, "Updating status: $status, $mode")
|
||||||
|
|
||||||
val preferences = getPreferences(this)
|
val preferences = getPreferences()
|
||||||
val proxyIp = preferences.getString("byedpi_proxy_ip", null) ?: "127.0.0.1"
|
val proxyIp = preferences.getStringNotNull("byedpi_proxy_ip", "127.0.0.1")
|
||||||
val proxyPort = preferences.getString("byedpi_proxy_port", null) ?: "1080"
|
val proxyPort = preferences.getStringNotNull("byedpi_proxy_port", "1080")
|
||||||
binding.proxyAddress.text = getString(R.string.proxy_address, proxyIp, proxyPort)
|
binding.proxyAddress.text = getString(R.string.proxy_address, proxyIp, proxyPort)
|
||||||
|
|
||||||
when (status) {
|
when (status) {
|
||||||
|
@ -5,7 +5,7 @@ import android.view.Menu
|
|||||||
import android.view.MenuItem
|
import android.view.MenuItem
|
||||||
import androidx.appcompat.app.AppCompatActivity
|
import androidx.appcompat.app.AppCompatActivity
|
||||||
import io.github.dovecoteescapee.byedpi.R
|
import io.github.dovecoteescapee.byedpi.R
|
||||||
import io.github.dovecoteescapee.byedpi.fragments.SettingsFragment
|
import io.github.dovecoteescapee.byedpi.fragments.MainSettingsFragment
|
||||||
import io.github.dovecoteescapee.byedpi.utility.getPreferences
|
import io.github.dovecoteescapee.byedpi.utility.getPreferences
|
||||||
|
|
||||||
class SettingsActivity : AppCompatActivity() {
|
class SettingsActivity : AppCompatActivity() {
|
||||||
@ -15,7 +15,7 @@ class SettingsActivity : AppCompatActivity() {
|
|||||||
|
|
||||||
supportFragmentManager
|
supportFragmentManager
|
||||||
.beginTransaction()
|
.beginTransaction()
|
||||||
.replace(R.id.settings, SettingsFragment())
|
.replace(R.id.settings, MainSettingsFragment())
|
||||||
.commit()
|
.commit()
|
||||||
|
|
||||||
supportActionBar?.setDisplayHomeAsUpEnabled(true)
|
supportActionBar?.setDisplayHomeAsUpEnabled(true)
|
||||||
@ -33,14 +33,11 @@ class SettingsActivity : AppCompatActivity() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
R.id.action_reset_settings -> {
|
R.id.action_reset_settings -> {
|
||||||
getPreferences(this)
|
getPreferences().edit().clear().apply()
|
||||||
.edit()
|
|
||||||
.clear()
|
|
||||||
.apply()
|
|
||||||
|
|
||||||
supportFragmentManager
|
supportFragmentManager
|
||||||
.beginTransaction()
|
.beginTransaction()
|
||||||
.replace(R.id.settings, SettingsFragment())
|
.replace(R.id.settings, MainSettingsFragment())
|
||||||
.commit()
|
.commit()
|
||||||
true
|
true
|
||||||
}
|
}
|
||||||
|
@ -41,7 +41,19 @@ class ByeDpiProxy {
|
|||||||
throw IllegalStateException("Proxy is already running")
|
throw IllegalStateException("Proxy is already running")
|
||||||
}
|
}
|
||||||
|
|
||||||
val fd = jniCreateSocket(
|
val fd = createSocketFromPreferences(preferences)
|
||||||
|
if (fd < 0) {
|
||||||
|
return -1
|
||||||
|
}
|
||||||
|
this.fd = fd
|
||||||
|
fd
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun createSocketFromPreferences(preferences: ByeDpiProxyPreferences) =
|
||||||
|
when (preferences) {
|
||||||
|
is ByeDpiProxyCmdPreferences -> jniCreateSocketWithCommandLine(preferences.args)
|
||||||
|
|
||||||
|
is ByeDpiProxyUIPreferences -> jniCreateSocket(
|
||||||
ip = preferences.ip,
|
ip = preferences.ip,
|
||||||
port = preferences.port,
|
port = preferences.port,
|
||||||
maxConnections = preferences.maxConnections,
|
maxConnections = preferences.maxConnections,
|
||||||
@ -49,6 +61,9 @@ class ByeDpiProxy {
|
|||||||
defaultTtl = preferences.defaultTtl,
|
defaultTtl = preferences.defaultTtl,
|
||||||
customTtl = preferences.customTtl,
|
customTtl = preferences.customTtl,
|
||||||
noDomain = preferences.noDomain,
|
noDomain = preferences.noDomain,
|
||||||
|
desyncHttp = preferences.desyncHttp,
|
||||||
|
desyncHttps = preferences.desyncHttps,
|
||||||
|
desyncUdp = preferences.desyncUdp,
|
||||||
desyncMethod = preferences.desyncMethod.ordinal,
|
desyncMethod = preferences.desyncMethod.ordinal,
|
||||||
splitPosition = preferences.splitPosition,
|
splitPosition = preferences.splitPosition,
|
||||||
splitAtHost = preferences.splitAtHost,
|
splitAtHost = preferences.splitAtHost,
|
||||||
@ -62,15 +77,10 @@ class ByeDpiProxy {
|
|||||||
tlsRecordSplitPosition = preferences.tlsRecordSplitPosition,
|
tlsRecordSplitPosition = preferences.tlsRecordSplitPosition,
|
||||||
tlsRecordSplitAtSni = preferences.tlsRecordSplitAtSni,
|
tlsRecordSplitAtSni = preferences.tlsRecordSplitAtSni,
|
||||||
)
|
)
|
||||||
|
|
||||||
if (fd < 0) {
|
|
||||||
return -1
|
|
||||||
}
|
|
||||||
|
|
||||||
this.fd = fd
|
|
||||||
fd
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private external fun jniCreateSocketWithCommandLine(args: Array<String>): Int
|
||||||
|
|
||||||
private external fun jniCreateSocket(
|
private external fun jniCreateSocket(
|
||||||
ip: String,
|
ip: String,
|
||||||
port: Int,
|
port: Int,
|
||||||
@ -79,6 +89,9 @@ class ByeDpiProxy {
|
|||||||
defaultTtl: Int,
|
defaultTtl: Int,
|
||||||
customTtl: Boolean,
|
customTtl: Boolean,
|
||||||
noDomain: Boolean,
|
noDomain: Boolean,
|
||||||
|
desyncHttp: Boolean,
|
||||||
|
desyncHttps: Boolean,
|
||||||
|
desyncUdp: Boolean,
|
||||||
desyncMethod: Int,
|
desyncMethod: Int,
|
||||||
splitPosition: Int,
|
splitPosition: Int,
|
||||||
splitAtHost: Boolean,
|
splitAtHost: Boolean,
|
||||||
|
@ -1,14 +1,48 @@
|
|||||||
package io.github.dovecoteescapee.byedpi.core
|
package io.github.dovecoteescapee.byedpi.core
|
||||||
|
|
||||||
import android.content.SharedPreferences
|
import android.content.SharedPreferences
|
||||||
|
import io.github.dovecoteescapee.byedpi.utility.getStringNotNull
|
||||||
|
import io.github.dovecoteescapee.byedpi.utility.shellSplit
|
||||||
|
|
||||||
class ByeDpiProxyPreferences(
|
sealed interface ByeDpiProxyPreferences {
|
||||||
|
companion object {
|
||||||
|
fun fromSharedPreferences(preferences: SharedPreferences): ByeDpiProxyPreferences =
|
||||||
|
when (preferences.getBoolean("byedpi_enable_cmd_settings", false)) {
|
||||||
|
true -> ByeDpiProxyCmdPreferences(preferences)
|
||||||
|
false -> ByeDpiProxyUIPreferences(preferences)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class ByeDpiProxyCmdPreferences(val args: Array<String>) : ByeDpiProxyPreferences {
|
||||||
|
constructor(cmd: String) : this(cmdToArgs(cmd))
|
||||||
|
|
||||||
|
constructor(preferences: SharedPreferences) : this(
|
||||||
|
preferences.getStringNotNull(
|
||||||
|
"byedpi_cmd_args",
|
||||||
|
""
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
companion object {
|
||||||
|
private fun cmdToArgs(cmd: String): Array<String> {
|
||||||
|
val firstArgIndex = cmd.indexOf("-")
|
||||||
|
val argsStr = (if (firstArgIndex > 0) cmd.substring(firstArgIndex) else cmd).trim()
|
||||||
|
return arrayOf("ciadpi") + shellSplit(argsStr)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class ByeDpiProxyUIPreferences(
|
||||||
ip: String? = null,
|
ip: String? = null,
|
||||||
port: Int? = null,
|
port: Int? = null,
|
||||||
maxConnections: Int? = null,
|
maxConnections: Int? = null,
|
||||||
bufferSize: Int? = null,
|
bufferSize: Int? = null,
|
||||||
defaultTtl: Int? = null,
|
defaultTtl: Int? = null,
|
||||||
noDomain: Boolean? = null,
|
noDomain: Boolean? = null,
|
||||||
|
desyncHttp: Boolean? = null,
|
||||||
|
desyncHttps: Boolean? = null,
|
||||||
|
desyncUdp: Boolean? = null,
|
||||||
desyncMethod: DesyncMethod? = null,
|
desyncMethod: DesyncMethod? = null,
|
||||||
splitPosition: Int? = null,
|
splitPosition: Int? = null,
|
||||||
splitAtHost: Boolean? = null,
|
splitAtHost: Boolean? = null,
|
||||||
@ -21,7 +55,7 @@ class ByeDpiProxyPreferences(
|
|||||||
tlsRecordSplit: Boolean? = null,
|
tlsRecordSplit: Boolean? = null,
|
||||||
tlsRecordSplitPosition: Int? = null,
|
tlsRecordSplitPosition: Int? = null,
|
||||||
tlsRecordSplitAtSni: Boolean? = null,
|
tlsRecordSplitAtSni: Boolean? = null,
|
||||||
) {
|
) : ByeDpiProxyPreferences {
|
||||||
val ip: String = ip ?: "127.0.0.1"
|
val ip: String = ip ?: "127.0.0.1"
|
||||||
val port: Int = port ?: 1080
|
val port: Int = port ?: 1080
|
||||||
val maxConnections: Int = maxConnections ?: 512
|
val maxConnections: Int = maxConnections ?: 512
|
||||||
@ -29,6 +63,9 @@ class ByeDpiProxyPreferences(
|
|||||||
val defaultTtl: Int = defaultTtl ?: 0
|
val defaultTtl: Int = defaultTtl ?: 0
|
||||||
val customTtl: Boolean = defaultTtl != null
|
val customTtl: Boolean = defaultTtl != null
|
||||||
val noDomain: Boolean = noDomain ?: false
|
val noDomain: Boolean = noDomain ?: false
|
||||||
|
val desyncHttp: Boolean = desyncHttp ?: true
|
||||||
|
val desyncHttps: Boolean = desyncHttps ?: true
|
||||||
|
val desyncUdp: Boolean = desyncUdp ?: false
|
||||||
val desyncMethod: DesyncMethod = desyncMethod ?: DesyncMethod.Disorder
|
val desyncMethod: DesyncMethod = desyncMethod ?: DesyncMethod.Disorder
|
||||||
val splitPosition: Int = splitPosition ?: 3
|
val splitPosition: Int = splitPosition ?: 3
|
||||||
val splitAtHost: Boolean = splitAtHost ?: false
|
val splitAtHost: Boolean = splitAtHost ?: false
|
||||||
@ -49,6 +86,9 @@ class ByeDpiProxyPreferences(
|
|||||||
bufferSize = preferences.getString("byedpi_buffer_size", null)?.toIntOrNull(),
|
bufferSize = preferences.getString("byedpi_buffer_size", null)?.toIntOrNull(),
|
||||||
defaultTtl = preferences.getString("byedpi_default_ttl", null)?.toIntOrNull(),
|
defaultTtl = preferences.getString("byedpi_default_ttl", null)?.toIntOrNull(),
|
||||||
noDomain = preferences.getBoolean("byedpi_no_domain", false),
|
noDomain = preferences.getBoolean("byedpi_no_domain", false),
|
||||||
|
desyncHttp = preferences.getBoolean("byedpi_desync_http", true),
|
||||||
|
desyncHttps = preferences.getBoolean("byedpi_desync_https", true),
|
||||||
|
desyncUdp = preferences.getBoolean("byedpi_desync_udp", false),
|
||||||
desyncMethod = preferences.getString("byedpi_desync_method", null)
|
desyncMethod = preferences.getString("byedpi_desync_method", null)
|
||||||
?.let { DesyncMethod.fromName(it) },
|
?.let { DesyncMethod.fromName(it) },
|
||||||
splitPosition = preferences.getString("byedpi_split_position", null)?.toIntOrNull(),
|
splitPosition = preferences.getString("byedpi_split_position", null)?.toIntOrNull(),
|
||||||
@ -60,9 +100,10 @@ class ByeDpiProxyPreferences(
|
|||||||
domainMixedCase = preferences.getBoolean("byedpi_domain_mixed_case", false),
|
domainMixedCase = preferences.getBoolean("byedpi_domain_mixed_case", false),
|
||||||
hostRemoveSpaces = preferences.getBoolean("byedpi_host_remove_spaces", false),
|
hostRemoveSpaces = preferences.getBoolean("byedpi_host_remove_spaces", false),
|
||||||
tlsRecordSplit = preferences.getBoolean("byedpi_tlsrec_enabled", false),
|
tlsRecordSplit = preferences.getBoolean("byedpi_tlsrec_enabled", false),
|
||||||
tlsRecordSplitPosition = preferences.getString("byedpi_tlsrec_position", null)?.toIntOrNull(),
|
tlsRecordSplitPosition = preferences.getString("byedpi_tlsrec_position", null)
|
||||||
|
?.toIntOrNull(),
|
||||||
tlsRecordSplitAtSni = preferences.getBoolean("byedpi_tlsrec_at_sni", false),
|
tlsRecordSplitAtSni = preferences.getBoolean("byedpi_tlsrec_at_sni", false),
|
||||||
)
|
)
|
||||||
|
|
||||||
enum class DesyncMethod {
|
enum class DesyncMethod {
|
||||||
None,
|
None,
|
||||||
|
@ -0,0 +1,11 @@
|
|||||||
|
package io.github.dovecoteescapee.byedpi.fragments
|
||||||
|
|
||||||
|
import android.os.Bundle
|
||||||
|
import androidx.preference.PreferenceFragmentCompat
|
||||||
|
import io.github.dovecoteescapee.byedpi.R
|
||||||
|
|
||||||
|
class ByeDpiCommandLineSettingsFragment : PreferenceFragmentCompat() {
|
||||||
|
override fun onCreatePreferences(savedInstanceState: Bundle?, rootKey: String?) {
|
||||||
|
setPreferencesFromResource(R.xml.byedpi_cmd_settings, rootKey)
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,146 @@
|
|||||||
|
package io.github.dovecoteescapee.byedpi.fragments
|
||||||
|
|
||||||
|
import android.content.SharedPreferences
|
||||||
|
import android.os.Bundle
|
||||||
|
import androidx.preference.*
|
||||||
|
import io.github.dovecoteescapee.byedpi.R
|
||||||
|
import io.github.dovecoteescapee.byedpi.core.ByeDpiProxyUIPreferences
|
||||||
|
import io.github.dovecoteescapee.byedpi.core.ByeDpiProxyUIPreferences.DesyncMethod.*
|
||||||
|
import io.github.dovecoteescapee.byedpi.utility.*
|
||||||
|
|
||||||
|
class ByeDpiUISettingsFragment : PreferenceFragmentCompat() {
|
||||||
|
|
||||||
|
private val preferenceListener =
|
||||||
|
SharedPreferences.OnSharedPreferenceChangeListener { _, _ ->
|
||||||
|
updatePreferences()
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onCreatePreferences(savedInstanceState: Bundle?, rootKey: String?) {
|
||||||
|
setPreferencesFromResource(R.xml.byedpi_ui_settings, rootKey)
|
||||||
|
|
||||||
|
setEditTextPreferenceListener("byedpi_proxy_ip") { checkIp(it) }
|
||||||
|
setEditTestPreferenceListenerPort("byedpi_proxy_port")
|
||||||
|
setEditTestPreferenceListenerInt(
|
||||||
|
"byedpi_max_connections",
|
||||||
|
1,
|
||||||
|
Short.MAX_VALUE.toInt()
|
||||||
|
)
|
||||||
|
setEditTestPreferenceListenerInt(
|
||||||
|
"byedpi_buffer_size",
|
||||||
|
1,
|
||||||
|
Int.MAX_VALUE / 4
|
||||||
|
)
|
||||||
|
setEditTestPreferenceListenerInt("byedpi_default_ttl", 0, 255)
|
||||||
|
setEditTestPreferenceListenerInt(
|
||||||
|
"byedpi_split_position",
|
||||||
|
Int.MIN_VALUE,
|
||||||
|
Int.MAX_VALUE
|
||||||
|
)
|
||||||
|
setEditTestPreferenceListenerInt("byedpi_fake_ttl", 1, 255)
|
||||||
|
setEditTestPreferenceListenerInt(
|
||||||
|
"byedpi_tlsrec_position",
|
||||||
|
2 * Short.MIN_VALUE,
|
||||||
|
2 * Short.MAX_VALUE,
|
||||||
|
)
|
||||||
|
|
||||||
|
findPreferenceNotNull<EditTextPreference>("byedpi_oob_data")
|
||||||
|
.setOnBindEditTextListener {
|
||||||
|
it.filters = arrayOf(android.text.InputFilter.LengthFilter(1))
|
||||||
|
}
|
||||||
|
|
||||||
|
updatePreferences()
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onResume() {
|
||||||
|
super.onResume()
|
||||||
|
sharedPreferences?.registerOnSharedPreferenceChangeListener(preferenceListener)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onPause() {
|
||||||
|
super.onPause()
|
||||||
|
sharedPreferences?.unregisterOnSharedPreferenceChangeListener(preferenceListener)
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun updatePreferences() {
|
||||||
|
val desyncMethod =
|
||||||
|
findPreferenceNotNull<ListPreference>("byedpi_desync_method")
|
||||||
|
.value.let { ByeDpiProxyUIPreferences.DesyncMethod.fromName(it) }
|
||||||
|
|
||||||
|
val desyncHttp = findPreferenceNotNull<CheckBoxPreference>("byedpi_desync_http")
|
||||||
|
val desyncHttps = findPreferenceNotNull<CheckBoxPreference>("byedpi_desync_https")
|
||||||
|
val desyncUdp = findPreferenceNotNull<CheckBoxPreference>("byedpi_desync_udp")
|
||||||
|
val splitPosition = findPreferenceNotNull<EditTextPreference>("byedpi_split_position")
|
||||||
|
val splitAtHost = findPreferenceNotNull<CheckBoxPreference>("byedpi_split_at_host")
|
||||||
|
val ttlFake = findPreferenceNotNull<EditTextPreference>("byedpi_fake_ttl")
|
||||||
|
val fakeSni = findPreferenceNotNull<EditTextPreference>("byedpi_fake_sni")
|
||||||
|
val oobData = findPreferenceNotNull<EditTextPreference>("byedpi_oob_data")
|
||||||
|
val hostMixedCase = findPreferenceNotNull<CheckBoxPreference>("byedpi_host_mixed_case")
|
||||||
|
val domainMixedCase = findPreferenceNotNull<CheckBoxPreference>("byedpi_domain_mixed_case")
|
||||||
|
val hostRemoveSpaces =
|
||||||
|
findPreferenceNotNull<CheckBoxPreference>("byedpi_host_remove_spaces")
|
||||||
|
val splitTlsRec = findPreferenceNotNull<CheckBoxPreference>("byedpi_tlsrec_enabled")
|
||||||
|
val splitTlsRecPosition =
|
||||||
|
findPreferenceNotNull<EditTextPreference>("byedpi_tlsrec_position")
|
||||||
|
val splitTlsRecAtSni = findPreferenceNotNull<CheckBoxPreference>("byedpi_tlsrec_at_sni")
|
||||||
|
|
||||||
|
when (desyncMethod) {
|
||||||
|
None -> {
|
||||||
|
desyncHttp.isVisible = false
|
||||||
|
desyncHttps.isVisible = false
|
||||||
|
desyncUdp.isVisible = false
|
||||||
|
splitPosition.isVisible = false
|
||||||
|
splitAtHost.isVisible = false
|
||||||
|
ttlFake.isVisible = false
|
||||||
|
fakeSni.isVisible = false
|
||||||
|
oobData.isVisible = false
|
||||||
|
hostMixedCase.isVisible = false
|
||||||
|
domainMixedCase.isVisible = false
|
||||||
|
hostRemoveSpaces.isVisible = false
|
||||||
|
}
|
||||||
|
|
||||||
|
else -> {
|
||||||
|
desyncHttp.isVisible = true
|
||||||
|
desyncHttps.isVisible = true
|
||||||
|
desyncUdp.isVisible = true
|
||||||
|
splitPosition.isVisible = true
|
||||||
|
splitAtHost.isVisible = true
|
||||||
|
|
||||||
|
val desyncAllProtocols =
|
||||||
|
!desyncHttp.isChecked && !desyncHttps.isChecked && !desyncUdp.isChecked
|
||||||
|
|
||||||
|
if (desyncAllProtocols || desyncHttp.isChecked) {
|
||||||
|
hostMixedCase.isVisible = true
|
||||||
|
domainMixedCase.isVisible = true
|
||||||
|
hostRemoveSpaces.isVisible = true
|
||||||
|
} else {
|
||||||
|
hostMixedCase.isVisible = false
|
||||||
|
domainMixedCase.isVisible = false
|
||||||
|
hostRemoveSpaces.isVisible = false
|
||||||
|
}
|
||||||
|
|
||||||
|
when (desyncMethod) {
|
||||||
|
Fake -> {
|
||||||
|
ttlFake.isVisible = true
|
||||||
|
fakeSni.isVisible = true
|
||||||
|
oobData.isVisible = false
|
||||||
|
}
|
||||||
|
|
||||||
|
OOB -> {
|
||||||
|
ttlFake.isVisible = false
|
||||||
|
fakeSni.isVisible = false
|
||||||
|
oobData.isVisible = true
|
||||||
|
}
|
||||||
|
|
||||||
|
else -> {
|
||||||
|
ttlFake.isVisible = false
|
||||||
|
fakeSni.isVisible = false
|
||||||
|
oobData.isVisible = false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
splitTlsRecPosition.isVisible = splitTlsRec.isChecked
|
||||||
|
splitTlsRecAtSni.isVisible = splitTlsRec.isChecked
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,92 @@
|
|||||||
|
package io.github.dovecoteescapee.byedpi.fragments
|
||||||
|
|
||||||
|
import android.content.SharedPreferences
|
||||||
|
import android.os.Bundle
|
||||||
|
import android.util.Log
|
||||||
|
import androidx.appcompat.app.AppCompatDelegate
|
||||||
|
import androidx.preference.*
|
||||||
|
import io.github.dovecoteescapee.byedpi.BuildConfig
|
||||||
|
import io.github.dovecoteescapee.byedpi.R
|
||||||
|
import io.github.dovecoteescapee.byedpi.data.Mode
|
||||||
|
import io.github.dovecoteescapee.byedpi.utility.*
|
||||||
|
|
||||||
|
class MainSettingsFragment : PreferenceFragmentCompat() {
|
||||||
|
companion object {
|
||||||
|
private val TAG: String = MainSettingsFragment::class.java.simpleName
|
||||||
|
|
||||||
|
fun setTheme(name: String) =
|
||||||
|
themeByName(name)?.let {
|
||||||
|
AppCompatDelegate.setDefaultNightMode(it)
|
||||||
|
} ?: throw IllegalStateException("Invalid value for app_theme: $name")
|
||||||
|
|
||||||
|
private fun themeByName(name: String): Int? = when (name) {
|
||||||
|
"system" -> AppCompatDelegate.MODE_NIGHT_FOLLOW_SYSTEM
|
||||||
|
"light" -> AppCompatDelegate.MODE_NIGHT_NO
|
||||||
|
"dark" -> AppCompatDelegate.MODE_NIGHT_YES
|
||||||
|
else -> {
|
||||||
|
Log.w(TAG, "Invalid value for app_theme: $name")
|
||||||
|
null
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private val preferenceListener =
|
||||||
|
SharedPreferences.OnSharedPreferenceChangeListener { _, _ ->
|
||||||
|
updatePreferences()
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onCreatePreferences(savedInstanceState: Bundle?, rootKey: String?) {
|
||||||
|
setPreferencesFromResource(R.xml.main_settings, rootKey)
|
||||||
|
|
||||||
|
setEditTextPreferenceListener("dns_ip") { checkIp(it) }
|
||||||
|
|
||||||
|
findPreferenceNotNull<DropDownPreference>("app_theme")
|
||||||
|
.setOnPreferenceChangeListener { _, newValue ->
|
||||||
|
setTheme(newValue as String)
|
||||||
|
true
|
||||||
|
}
|
||||||
|
|
||||||
|
val switchCommandLineSettings = findPreferenceNotNull<SwitchPreference>(
|
||||||
|
"byedpi_enable_cmd_settings"
|
||||||
|
)
|
||||||
|
val uiSettings = findPreferenceNotNull<Preference>("byedpi_ui_settings")
|
||||||
|
val cmdSettings = findPreferenceNotNull<Preference>("byedpi_cmd_settings")
|
||||||
|
|
||||||
|
val setByeDpiSettingsMode = { enable: Boolean ->
|
||||||
|
uiSettings.isEnabled = !enable
|
||||||
|
cmdSettings.isEnabled = enable
|
||||||
|
}
|
||||||
|
|
||||||
|
setByeDpiSettingsMode(switchCommandLineSettings.isChecked)
|
||||||
|
|
||||||
|
switchCommandLineSettings.setOnPreferenceChangeListener { _, newValue ->
|
||||||
|
setByeDpiSettingsMode(newValue as Boolean)
|
||||||
|
true
|
||||||
|
}
|
||||||
|
|
||||||
|
findPreferenceNotNull<Preference>("version").summary = BuildConfig.VERSION_NAME
|
||||||
|
|
||||||
|
updatePreferences()
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onResume() {
|
||||||
|
super.onResume()
|
||||||
|
sharedPreferences?.registerOnSharedPreferenceChangeListener(preferenceListener)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onPause() {
|
||||||
|
super.onPause()
|
||||||
|
sharedPreferences?.unregisterOnSharedPreferenceChangeListener(preferenceListener)
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun updatePreferences() {
|
||||||
|
val mode = findPreferenceNotNull<ListPreference>("byedpi_mode")
|
||||||
|
.value.let { Mode.fromString(it) }
|
||||||
|
val dns = findPreferenceNotNull<EditTextPreference>("dns_ip")
|
||||||
|
|
||||||
|
when (mode) {
|
||||||
|
Mode.VPN -> dns.isVisible = true
|
||||||
|
Mode.Proxy -> dns.isVisible = false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -1,131 +0,0 @@
|
|||||||
package io.github.dovecoteescapee.byedpi.fragments
|
|
||||||
|
|
||||||
import android.net.InetAddresses
|
|
||||||
import android.os.Build
|
|
||||||
import android.os.Bundle
|
|
||||||
import android.util.Log
|
|
||||||
import android.util.Patterns
|
|
||||||
import android.widget.Toast
|
|
||||||
import androidx.appcompat.app.AppCompatDelegate
|
|
||||||
import androidx.preference.DropDownPreference
|
|
||||||
import androidx.preference.EditTextPreference
|
|
||||||
import androidx.preference.Preference
|
|
||||||
import androidx.preference.PreferenceFragmentCompat
|
|
||||||
import io.github.dovecoteescapee.byedpi.BuildConfig
|
|
||||||
import io.github.dovecoteescapee.byedpi.R
|
|
||||||
|
|
||||||
class SettingsFragment : PreferenceFragmentCompat() {
|
|
||||||
companion object {
|
|
||||||
private val TAG: String = SettingsFragment::class.java.simpleName
|
|
||||||
|
|
||||||
fun setTheme(name: String): Boolean = when (val theme = themeByName(name)) {
|
|
||||||
null -> {
|
|
||||||
Log.w(TAG, "Invalid value for app_theme: $name")
|
|
||||||
false
|
|
||||||
}
|
|
||||||
|
|
||||||
else -> {
|
|
||||||
AppCompatDelegate.setDefaultNightMode(theme)
|
|
||||||
true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun themeByName(name: String): Int? = when (name) {
|
|
||||||
"system" -> AppCompatDelegate.MODE_NIGHT_FOLLOW_SYSTEM
|
|
||||||
"light" -> AppCompatDelegate.MODE_NIGHT_NO
|
|
||||||
"dark" -> AppCompatDelegate.MODE_NIGHT_YES
|
|
||||||
else -> {
|
|
||||||
Log.w(TAG, "Invalid value for app_theme: $name")
|
|
||||||
null
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun checkIp(ip: String): Boolean =
|
|
||||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
|
|
||||||
InetAddresses.isNumericAddress(ip)
|
|
||||||
} else {
|
|
||||||
// This pattern doesn't not support IPv6
|
|
||||||
Patterns.IP_ADDRESS.matcher(ip).matches()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun onCreatePreferences(savedInstanceState: Bundle?, rootKey: String?) {
|
|
||||||
setPreferencesFromResource(R.xml.settings, rootKey)
|
|
||||||
|
|
||||||
findPreference<DropDownPreference>("app_theme")
|
|
||||||
?.setOnPreferenceChangeListener { preference, newValue ->
|
|
||||||
setTheme(newValue as String)
|
|
||||||
}
|
|
||||||
|
|
||||||
setEditTextPreferenceListener("dns_ip") { checkIp(it) }
|
|
||||||
setEditTextPreferenceListener("byedpi_proxy_ip") { checkIp(it) }
|
|
||||||
setEditTestPreferenceListenerPort("byedpi_proxy_port")
|
|
||||||
setEditTestPreferenceListenerInt(
|
|
||||||
"byedpi_max_connections",
|
|
||||||
1,
|
|
||||||
Short.MAX_VALUE.toInt()
|
|
||||||
)
|
|
||||||
setEditTestPreferenceListenerInt(
|
|
||||||
"byedpi_buffer_size",
|
|
||||||
1,
|
|
||||||
Int.MAX_VALUE / 4
|
|
||||||
)
|
|
||||||
setEditTestPreferenceListenerInt("byedpi_default_ttl", 0, 255)
|
|
||||||
setEditTestPreferenceListenerInt(
|
|
||||||
"byedpi_split_position",
|
|
||||||
Int.MIN_VALUE,
|
|
||||||
Int.MAX_VALUE
|
|
||||||
)
|
|
||||||
setEditTestPreferenceListenerInt("byedpi_fake_ttl", 1, 255)
|
|
||||||
setEditTestPreferenceListenerInt(
|
|
||||||
"byedpi_tlsrec_position",
|
|
||||||
2 * Short.MIN_VALUE,
|
|
||||||
2 * Short.MAX_VALUE,
|
|
||||||
)
|
|
||||||
|
|
||||||
findPreference<Preference>("version")?.summary =
|
|
||||||
BuildConfig.VERSION_NAME
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun setEditTestPreferenceListenerPort(key: String) {
|
|
||||||
setEditTestPreferenceListenerInt(key, 1, 65535)
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun setEditTestPreferenceListenerInt(
|
|
||||||
key: String,
|
|
||||||
min: Int = Int.MIN_VALUE,
|
|
||||||
max: Int = Int.MAX_VALUE
|
|
||||||
) {
|
|
||||||
setEditTextPreferenceListener(key) { value ->
|
|
||||||
value.toIntOrNull()?.let { it in min..max } ?: false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun setEditTextPreferenceListener(key: String, check: (String) -> Boolean) {
|
|
||||||
findPreference<EditTextPreference>(key)
|
|
||||||
?.setOnPreferenceChangeListener { preference, newValue ->
|
|
||||||
when (newValue) {
|
|
||||||
is String -> {
|
|
||||||
val valid = check(newValue)
|
|
||||||
if (!valid) {
|
|
||||||
Toast.makeText(
|
|
||||||
requireContext(),
|
|
||||||
"Invalid value for ${preference.title}: $newValue",
|
|
||||||
Toast.LENGTH_SHORT
|
|
||||||
).show()
|
|
||||||
}
|
|
||||||
valid
|
|
||||||
}
|
|
||||||
|
|
||||||
else -> {
|
|
||||||
Log.w(
|
|
||||||
TAG,
|
|
||||||
"Invalid type for ${preference.key}: " +
|
|
||||||
"$newValue has type ${newValue::class.java}"
|
|
||||||
)
|
|
||||||
false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -10,19 +10,8 @@ import androidx.lifecycle.lifecycleScope
|
|||||||
import io.github.dovecoteescapee.byedpi.R
|
import io.github.dovecoteescapee.byedpi.R
|
||||||
import io.github.dovecoteescapee.byedpi.core.ByeDpiProxy
|
import io.github.dovecoteescapee.byedpi.core.ByeDpiProxy
|
||||||
import io.github.dovecoteescapee.byedpi.core.ByeDpiProxyPreferences
|
import io.github.dovecoteescapee.byedpi.core.ByeDpiProxyPreferences
|
||||||
import io.github.dovecoteescapee.byedpi.data.AppStatus
|
import io.github.dovecoteescapee.byedpi.data.*
|
||||||
import io.github.dovecoteescapee.byedpi.data.START_ACTION
|
import io.github.dovecoteescapee.byedpi.utility.*
|
||||||
import io.github.dovecoteescapee.byedpi.data.STOP_ACTION
|
|
||||||
import io.github.dovecoteescapee.byedpi.data.FAILED_BROADCAST
|
|
||||||
import io.github.dovecoteescapee.byedpi.data.Mode
|
|
||||||
import io.github.dovecoteescapee.byedpi.data.SENDER
|
|
||||||
import io.github.dovecoteescapee.byedpi.data.STARTED_BROADCAST
|
|
||||||
import io.github.dovecoteescapee.byedpi.data.STOPPED_BROADCAST
|
|
||||||
import io.github.dovecoteescapee.byedpi.data.Sender
|
|
||||||
import io.github.dovecoteescapee.byedpi.data.ServiceStatus
|
|
||||||
import io.github.dovecoteescapee.byedpi.utility.createConnectionNotification
|
|
||||||
import io.github.dovecoteescapee.byedpi.utility.getPreferences
|
|
||||||
import io.github.dovecoteescapee.byedpi.utility.registerNotificationChannel
|
|
||||||
import kotlinx.coroutines.Dispatchers
|
import kotlinx.coroutines.Dispatchers
|
||||||
import kotlinx.coroutines.Job
|
import kotlinx.coroutines.Job
|
||||||
import kotlinx.coroutines.launch
|
import kotlinx.coroutines.launch
|
||||||
@ -159,7 +148,7 @@ class ByeDpiProxyService : LifecycleService() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private fun getByeDpiPreferences(): ByeDpiProxyPreferences =
|
private fun getByeDpiPreferences(): ByeDpiProxyPreferences =
|
||||||
ByeDpiProxyPreferences(getPreferences(this))
|
ByeDpiProxyPreferences.fromSharedPreferences(getPreferences())
|
||||||
|
|
||||||
private fun updateStatus(newStatus: ServiceStatus) {
|
private fun updateStatus(newStatus: ServiceStatus) {
|
||||||
Log.d(TAG, "Proxy status changed from $status to $newStatus")
|
Log.d(TAG, "Proxy status changed from $status to $newStatus")
|
||||||
|
@ -15,19 +15,8 @@ import io.github.dovecoteescapee.byedpi.R
|
|||||||
import io.github.dovecoteescapee.byedpi.activities.MainActivity
|
import io.github.dovecoteescapee.byedpi.activities.MainActivity
|
||||||
import io.github.dovecoteescapee.byedpi.core.ByeDpiProxy
|
import io.github.dovecoteescapee.byedpi.core.ByeDpiProxy
|
||||||
import io.github.dovecoteescapee.byedpi.core.ByeDpiProxyPreferences
|
import io.github.dovecoteescapee.byedpi.core.ByeDpiProxyPreferences
|
||||||
import io.github.dovecoteescapee.byedpi.data.AppStatus
|
import io.github.dovecoteescapee.byedpi.data.*
|
||||||
import io.github.dovecoteescapee.byedpi.data.START_ACTION
|
import io.github.dovecoteescapee.byedpi.utility.*
|
||||||
import io.github.dovecoteescapee.byedpi.data.STOP_ACTION
|
|
||||||
import io.github.dovecoteescapee.byedpi.data.FAILED_BROADCAST
|
|
||||||
import io.github.dovecoteescapee.byedpi.data.Mode
|
|
||||||
import io.github.dovecoteescapee.byedpi.data.SENDER
|
|
||||||
import io.github.dovecoteescapee.byedpi.data.STARTED_BROADCAST
|
|
||||||
import io.github.dovecoteescapee.byedpi.data.STOPPED_BROADCAST
|
|
||||||
import io.github.dovecoteescapee.byedpi.data.Sender
|
|
||||||
import io.github.dovecoteescapee.byedpi.data.ServiceStatus
|
|
||||||
import io.github.dovecoteescapee.byedpi.utility.createConnectionNotification
|
|
||||||
import io.github.dovecoteescapee.byedpi.utility.getPreferences
|
|
||||||
import io.github.dovecoteescapee.byedpi.utility.registerNotificationChannel
|
|
||||||
import kotlinx.coroutines.Dispatchers
|
import kotlinx.coroutines.Dispatchers
|
||||||
import kotlinx.coroutines.Job
|
import kotlinx.coroutines.Job
|
||||||
import kotlinx.coroutines.launch
|
import kotlinx.coroutines.launch
|
||||||
@ -189,9 +178,9 @@ class ByeDpiVpnService : LifecycleVpnService() {
|
|||||||
throw IllegalStateException("VPN field not null")
|
throw IllegalStateException("VPN field not null")
|
||||||
}
|
}
|
||||||
|
|
||||||
val sharedPreferences = getPreferences(this)
|
val sharedPreferences = getPreferences()
|
||||||
val port = sharedPreferences.getString("byedpi_proxy_port", null)?.toInt() ?: 1080
|
val port = sharedPreferences.getString("byedpi_proxy_port", null)?.toInt() ?: 1080
|
||||||
val dns = sharedPreferences.getString("dns_ip", null) ?: "1.1.1.1"
|
val dns = sharedPreferences.getStringNotNull("dns_ip", "1.1.1.1")
|
||||||
|
|
||||||
val vpn = createBuilder(dns).establish()
|
val vpn = createBuilder(dns).establish()
|
||||||
?: throw IllegalStateException("VPN connection failed")
|
?: throw IllegalStateException("VPN connection failed")
|
||||||
@ -213,7 +202,7 @@ class ByeDpiVpnService : LifecycleVpnService() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private fun getByeDpiPreferences(): ByeDpiProxyPreferences =
|
private fun getByeDpiPreferences(): ByeDpiProxyPreferences =
|
||||||
ByeDpiProxyPreferences(getPreferences(this))
|
ByeDpiProxyPreferences.fromSharedPreferences(getPreferences())
|
||||||
|
|
||||||
private fun updateStatus(newStatus: ServiceStatus) {
|
private fun updateStatus(newStatus: ServiceStatus) {
|
||||||
Log.d(TAG, "VPN status changed from $status to $newStatus")
|
Log.d(TAG, "VPN status changed from $status to $newStatus")
|
||||||
|
@ -8,7 +8,11 @@ import androidx.lifecycle.Lifecycle
|
|||||||
import androidx.lifecycle.LifecycleOwner
|
import androidx.lifecycle.LifecycleOwner
|
||||||
import androidx.lifecycle.ServiceLifecycleDispatcher
|
import androidx.lifecycle.ServiceLifecycleDispatcher
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Based on @link [androidx.lifecycle.LifecycleService]
|
||||||
|
*/
|
||||||
open class LifecycleVpnService : VpnService(), LifecycleOwner {
|
open class LifecycleVpnService : VpnService(), LifecycleOwner {
|
||||||
|
@Suppress("LeakingThis")
|
||||||
private val dispatcher = ServiceLifecycleDispatcher(this)
|
private val dispatcher = ServiceLifecycleDispatcher(this)
|
||||||
|
|
||||||
@CallSuper
|
@CallSuper
|
||||||
@ -48,4 +52,4 @@ open class LifecycleVpnService : VpnService(), LifecycleOwner {
|
|||||||
|
|
||||||
override val lifecycle: Lifecycle
|
override val lifecycle: Lifecycle
|
||||||
get() = dispatcher.lifecycle
|
get() = dispatcher.lifecycle
|
||||||
}
|
}
|
||||||
|
@ -17,13 +17,7 @@ import androidx.core.service.quicksettings.PendingIntentActivityWrapper
|
|||||||
import androidx.core.service.quicksettings.TileServiceCompat
|
import androidx.core.service.quicksettings.TileServiceCompat
|
||||||
import io.github.dovecoteescapee.byedpi.R
|
import io.github.dovecoteescapee.byedpi.R
|
||||||
import io.github.dovecoteescapee.byedpi.activities.MainActivity
|
import io.github.dovecoteescapee.byedpi.activities.MainActivity
|
||||||
import io.github.dovecoteescapee.byedpi.data.AppStatus
|
import io.github.dovecoteescapee.byedpi.data.*
|
||||||
import io.github.dovecoteescapee.byedpi.data.FAILED_BROADCAST
|
|
||||||
import io.github.dovecoteescapee.byedpi.data.Mode
|
|
||||||
import io.github.dovecoteescapee.byedpi.data.SENDER
|
|
||||||
import io.github.dovecoteescapee.byedpi.data.STARTED_BROADCAST
|
|
||||||
import io.github.dovecoteescapee.byedpi.data.STOPPED_BROADCAST
|
|
||||||
import io.github.dovecoteescapee.byedpi.data.Sender
|
|
||||||
import io.github.dovecoteescapee.byedpi.utility.getPreferences
|
import io.github.dovecoteescapee.byedpi.utility.getPreferences
|
||||||
import io.github.dovecoteescapee.byedpi.utility.mode
|
import io.github.dovecoteescapee.byedpi.utility.mode
|
||||||
|
|
||||||
@ -116,7 +110,7 @@ class QuickTileService : TileService() {
|
|||||||
val (status) = appStatus
|
val (status) = appStatus
|
||||||
when (status) {
|
when (status) {
|
||||||
AppStatus.Halted -> {
|
AppStatus.Halted -> {
|
||||||
val mode = getPreferences(this).mode()
|
val mode = getPreferences().mode()
|
||||||
|
|
||||||
if (mode == Mode.VPN && VpnService.prepare(this) != null) {
|
if (mode == Mode.VPN && VpnService.prepare(this) != null) {
|
||||||
updateStatus()
|
updateStatus()
|
||||||
@ -130,4 +124,4 @@ class QuickTileService : TileService() {
|
|||||||
AppStatus.Running -> ServiceManager.stop(this)
|
AppStatus.Running -> ServiceManager.stop(this)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -47,4 +47,4 @@ object ServiceManager {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -0,0 +1,41 @@
|
|||||||
|
package io.github.dovecoteescapee.byedpi.utility
|
||||||
|
|
||||||
|
// Based on https://gist.github.com/raymyers/8077031
|
||||||
|
fun shellSplit(string: CharSequence): List<String> {
|
||||||
|
val tokens: MutableList<String> = ArrayList()
|
||||||
|
var escaping = false
|
||||||
|
var quoteChar = ' '
|
||||||
|
var quoting = false
|
||||||
|
var lastCloseQuoteIndex = Int.MIN_VALUE
|
||||||
|
var current = StringBuilder()
|
||||||
|
|
||||||
|
for (i in string.indices) {
|
||||||
|
val c = string[i]
|
||||||
|
|
||||||
|
if (escaping) {
|
||||||
|
current.append(c)
|
||||||
|
escaping = false
|
||||||
|
} else if (c == '\\' && !(quoting && quoteChar == '\'')) {
|
||||||
|
escaping = true
|
||||||
|
} else if (quoting && c == quoteChar) {
|
||||||
|
quoting = false
|
||||||
|
lastCloseQuoteIndex = i
|
||||||
|
} else if (!quoting && (c == '\'' || c == '"')) {
|
||||||
|
quoting = true
|
||||||
|
quoteChar = c
|
||||||
|
} else if (!quoting && Character.isWhitespace(c)) {
|
||||||
|
if (current.isNotEmpty() || lastCloseQuoteIndex == i - 1) {
|
||||||
|
tokens.add(current.toString())
|
||||||
|
current = StringBuilder()
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
current.append(c)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (current.isNotEmpty() || lastCloseQuoteIndex == string.length - 1) {
|
||||||
|
tokens.add(current.toString())
|
||||||
|
}
|
||||||
|
|
||||||
|
return tokens
|
||||||
|
}
|
@ -2,14 +2,22 @@ package io.github.dovecoteescapee.byedpi.utility
|
|||||||
|
|
||||||
import android.content.Context
|
import android.content.Context
|
||||||
import android.content.SharedPreferences
|
import android.content.SharedPreferences
|
||||||
|
import androidx.preference.Preference
|
||||||
|
import androidx.preference.PreferenceFragmentCompat
|
||||||
import androidx.preference.PreferenceManager
|
import androidx.preference.PreferenceManager
|
||||||
import io.github.dovecoteescapee.byedpi.data.Mode
|
import io.github.dovecoteescapee.byedpi.data.Mode
|
||||||
|
|
||||||
fun getPreferences(context: Context): SharedPreferences =
|
val PreferenceFragmentCompat.sharedPreferences
|
||||||
PreferenceManager.getDefaultSharedPreferences(context)
|
get() = preferenceScreen.sharedPreferences
|
||||||
|
|
||||||
|
fun Context.getPreferences(): SharedPreferences =
|
||||||
|
PreferenceManager.getDefaultSharedPreferences(this)
|
||||||
|
|
||||||
fun SharedPreferences.getStringNotNull(key: String, defValue: String): String =
|
fun SharedPreferences.getStringNotNull(key: String, defValue: String): String =
|
||||||
getString(key, defValue) ?: defValue
|
getString(key, defValue) ?: defValue
|
||||||
|
|
||||||
fun SharedPreferences.mode(): Mode =
|
fun SharedPreferences.mode(): Mode =
|
||||||
Mode.fromString(getStringNotNull("byedpi_mode", "vpn"))
|
Mode.fromString(getStringNotNull("byedpi_mode", "vpn"))
|
||||||
|
|
||||||
|
fun <T : Preference> PreferenceFragmentCompat.findPreferenceNotNull(key: CharSequence): T =
|
||||||
|
findPreference(key) ?: throw IllegalStateException("Preference $key not found")
|
||||||
|
@ -0,0 +1,65 @@
|
|||||||
|
package io.github.dovecoteescapee.byedpi.utility
|
||||||
|
|
||||||
|
import android.net.InetAddresses
|
||||||
|
import android.os.Build
|
||||||
|
import android.util.Log
|
||||||
|
import android.util.Patterns
|
||||||
|
import android.widget.Toast
|
||||||
|
import androidx.preference.EditTextPreference
|
||||||
|
import androidx.preference.PreferenceFragmentCompat
|
||||||
|
|
||||||
|
private const val TAG = "ValidateUtils"
|
||||||
|
|
||||||
|
fun checkIp(ip: String): Boolean =
|
||||||
|
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
|
||||||
|
InetAddresses.isNumericAddress(ip)
|
||||||
|
} else {
|
||||||
|
// This pattern doesn't not support IPv6
|
||||||
|
@Suppress("DEPRECATION")
|
||||||
|
Patterns.IP_ADDRESS.matcher(ip).matches()
|
||||||
|
}
|
||||||
|
|
||||||
|
fun PreferenceFragmentCompat.setEditTestPreferenceListenerPort(key: String) {
|
||||||
|
setEditTestPreferenceListenerInt(key, 1, 65535)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun PreferenceFragmentCompat.setEditTestPreferenceListenerInt(
|
||||||
|
key: String,
|
||||||
|
min: Int = Int.MIN_VALUE,
|
||||||
|
max: Int = Int.MAX_VALUE
|
||||||
|
) {
|
||||||
|
setEditTextPreferenceListener(key) { value ->
|
||||||
|
value.toIntOrNull()?.let { it in min..max } ?: false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun PreferenceFragmentCompat.setEditTextPreferenceListener(
|
||||||
|
key: String,
|
||||||
|
check: (String) -> Boolean
|
||||||
|
) {
|
||||||
|
findPreferenceNotNull<EditTextPreference>(key)
|
||||||
|
.setOnPreferenceChangeListener { preference, newValue ->
|
||||||
|
when (newValue) {
|
||||||
|
is String -> {
|
||||||
|
val valid = check(newValue)
|
||||||
|
if (!valid) {
|
||||||
|
Toast.makeText(
|
||||||
|
requireContext(),
|
||||||
|
"Invalid value for ${preference.title}: $newValue",
|
||||||
|
Toast.LENGTH_SHORT
|
||||||
|
).show()
|
||||||
|
}
|
||||||
|
valid
|
||||||
|
}
|
||||||
|
|
||||||
|
else -> {
|
||||||
|
Log.w(
|
||||||
|
TAG,
|
||||||
|
"Invalid type for ${preference.key}: " +
|
||||||
|
"$newValue has type ${newValue::class.java}"
|
||||||
|
)
|
||||||
|
false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -47,4 +47,14 @@
|
|||||||
<string name="general_category">General</string>
|
<string name="general_category">General</string>
|
||||||
<string name="byedpi_category">ByeDPI</string>
|
<string name="byedpi_category">ByeDPI</string>
|
||||||
<string name="about_category">About</string>
|
<string name="about_category">About</string>
|
||||||
|
<string name="command_line_editor">Command line editor</string>
|
||||||
|
<string name="ui_editor">UI editor</string>
|
||||||
|
<string name="use_command_line_settings">Use command line settings</string>
|
||||||
|
<string name="desync_http">Desync HTTP</string>
|
||||||
|
<string name="desync_https">Desync HTTPS</string>
|
||||||
|
<string name="desync_udp">Desync UDP</string>
|
||||||
|
<string name="oob_data">OOB Data</string>
|
||||||
|
<string name="sni_of_fake_packet">SNI of fake packet</string>
|
||||||
|
<string name="byedpi_proxy">Proxy</string>
|
||||||
|
<string name="byedpi_desync">Desync</string>
|
||||||
</resources>
|
</resources>
|
25
app/src/main/res/xml/byedpi_cmd_settings.xml
Normal file
25
app/src/main/res/xml/byedpi_cmd_settings.xml
Normal file
@ -0,0 +1,25 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<PreferenceScreen xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
android:tag="byedpi_cmd_settings">
|
||||||
|
|
||||||
|
<Preference
|
||||||
|
android:key="byedpi_readme"
|
||||||
|
android:title="Documentation"
|
||||||
|
android:textColor="?attr/textFillColor"
|
||||||
|
android:icon="@drawable/ic_github_36">
|
||||||
|
<intent
|
||||||
|
android:action="android.intent.action.VIEW"
|
||||||
|
android:data="https://github.com/hufrea/byedpi/tree/main#readme-ov-file" />
|
||||||
|
</Preference>
|
||||||
|
|
||||||
|
<androidx.preference.PreferenceCategory>
|
||||||
|
|
||||||
|
<com.takisoft.preferencex.EditTextPreference
|
||||||
|
android:key="byedpi_cmd_args"
|
||||||
|
android:title="Command line arguments"
|
||||||
|
android:dialogTitle="Command line arguments"
|
||||||
|
android:inputType="textMultiLine" />
|
||||||
|
|
||||||
|
</androidx.preference.PreferenceCategory>
|
||||||
|
|
||||||
|
</PreferenceScreen>
|
@ -1,48 +1,20 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?>
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
<PreferenceScreen xmlns:android="http://schemas.android.com/apk/res/android"
|
<PreferenceScreen xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||||
xmlns:tools="http://schemas.android.com/tools"
|
android:tag="byedpi_ui_settings">
|
||||||
android:tag="settings_screen">
|
|
||||||
|
<Preference
|
||||||
|
android:key="byedpi_readme"
|
||||||
|
android:title="@string/byedpi_readme_link"
|
||||||
|
android:textColor="?attr/textFillColor"
|
||||||
|
android:icon="@drawable/ic_github_36">
|
||||||
|
<intent
|
||||||
|
android:action="android.intent.action.VIEW"
|
||||||
|
android:data="https://github.com/hufrea/byedpi/tree/main#readme-ov-file" />
|
||||||
|
</Preference>
|
||||||
|
|
||||||
<androidx.preference.PreferenceCategory
|
<androidx.preference.PreferenceCategory
|
||||||
android:title="@string/general_category">
|
android:title="@string/byedpi_proxy">
|
||||||
|
|
||||||
<DropDownPreference
|
|
||||||
android:key="app_theme"
|
|
||||||
android:title="@string/theme_settings"
|
|
||||||
android:entries="@array/themes"
|
|
||||||
android:entryValues="@array/themes_entries"
|
|
||||||
android:defaultValue="system"
|
|
||||||
app:useSimpleSummaryProvider="true" />
|
|
||||||
|
|
||||||
<DropDownPreference
|
|
||||||
android:key="byedpi_mode"
|
|
||||||
android:title="@string/mode_setting"
|
|
||||||
android:entries="@array/byedpi_modes"
|
|
||||||
android:entryValues="@array/byedpi_modes_entries"
|
|
||||||
android:defaultValue="vpn"
|
|
||||||
app:useSimpleSummaryProvider="true" />
|
|
||||||
|
|
||||||
</androidx.preference.PreferenceCategory>
|
|
||||||
|
|
||||||
<androidx.preference.PreferenceCategory
|
|
||||||
android:title="@string/byedpi_category">
|
|
||||||
|
|
||||||
<Preference
|
|
||||||
android:key="byedpi_readme"
|
|
||||||
android:title="@string/byedpi_readme_link"
|
|
||||||
android:textColor="?attr/textFillColor"
|
|
||||||
android:icon="@drawable/ic_github_36">
|
|
||||||
<intent
|
|
||||||
android:action="android.intent.action.VIEW"
|
|
||||||
android:data="https://github.com/hufrea/byedpi/tree/main#readme-ov-file" />
|
|
||||||
</Preference>
|
|
||||||
|
|
||||||
<com.takisoft.preferencex.EditTextPreference
|
|
||||||
android:key="dns_ip"
|
|
||||||
android:title="@string/dbs_ip_setting"
|
|
||||||
android:defaultValue="1.1.1.1"
|
|
||||||
app:useSimpleSummaryProvider="true"/>
|
|
||||||
|
|
||||||
<com.takisoft.preferencex.EditTextPreference
|
<com.takisoft.preferencex.EditTextPreference
|
||||||
android:key="byedpi_proxy_ip"
|
android:key="byedpi_proxy_ip"
|
||||||
@ -71,6 +43,16 @@
|
|||||||
android:defaultValue="16384"
|
android:defaultValue="16384"
|
||||||
app:useSimpleSummaryProvider="true" />
|
app:useSimpleSummaryProvider="true" />
|
||||||
|
|
||||||
|
<CheckBoxPreference
|
||||||
|
android:key="byedpi_no_domain"
|
||||||
|
android:title="@string/byedpi_no_domain_setting"
|
||||||
|
android:defaultValue="false" />
|
||||||
|
|
||||||
|
</androidx.preference.PreferenceCategory>
|
||||||
|
|
||||||
|
<androidx.preference.PreferenceCategory
|
||||||
|
android:title="@string/byedpi_desync">
|
||||||
|
|
||||||
<com.takisoft.preferencex.EditTextPreference
|
<com.takisoft.preferencex.EditTextPreference
|
||||||
android:key="byedpi_default_ttl"
|
android:key="byedpi_default_ttl"
|
||||||
android:title="@string/byedpi_default_ttl_setting"
|
android:title="@string/byedpi_default_ttl_setting"
|
||||||
@ -78,11 +60,6 @@
|
|||||||
android:defaultValue="0"
|
android:defaultValue="0"
|
||||||
app:useSimpleSummaryProvider="true" />
|
app:useSimpleSummaryProvider="true" />
|
||||||
|
|
||||||
<CheckBoxPreference
|
|
||||||
android:key="byedpi_no_domain"
|
|
||||||
android:title="@string/byedpi_no_domain_setting"
|
|
||||||
android:defaultValue="false"/>
|
|
||||||
|
|
||||||
<DropDownPreference
|
<DropDownPreference
|
||||||
android:key="byedpi_desync_method"
|
android:key="byedpi_desync_method"
|
||||||
android:title="@string/byedpi_desync_method_setting"
|
android:title="@string/byedpi_desync_method_setting"
|
||||||
@ -91,6 +68,21 @@
|
|||||||
android:defaultValue="disorder"
|
android:defaultValue="disorder"
|
||||||
app:useSimpleSummaryProvider="true" />
|
app:useSimpleSummaryProvider="true" />
|
||||||
|
|
||||||
|
<CheckBoxPreference
|
||||||
|
android:key="byedpi_desync_http"
|
||||||
|
android:title="@string/desync_http"
|
||||||
|
android:defaultValue="true" />
|
||||||
|
|
||||||
|
<CheckBoxPreference
|
||||||
|
android:key="byedpi_desync_https"
|
||||||
|
android:title="@string/desync_https"
|
||||||
|
android:defaultValue="true" />
|
||||||
|
|
||||||
|
<CheckBoxPreference
|
||||||
|
android:key="byedpi_desync_udp"
|
||||||
|
android:title="@string/desync_udp"
|
||||||
|
android:defaultValue="false" />
|
||||||
|
|
||||||
<com.takisoft.preferencex.EditTextPreference
|
<com.takisoft.preferencex.EditTextPreference
|
||||||
android:key="byedpi_split_position"
|
android:key="byedpi_split_position"
|
||||||
android:title="@string/byedpi_split_position_setting"
|
android:title="@string/byedpi_split_position_setting"
|
||||||
@ -112,13 +104,13 @@
|
|||||||
|
|
||||||
<com.takisoft.preferencex.EditTextPreference
|
<com.takisoft.preferencex.EditTextPreference
|
||||||
android:key="byedpi_fake_sni"
|
android:key="byedpi_fake_sni"
|
||||||
android:title="SNI of fake packet"
|
android:title="@string/sni_of_fake_packet"
|
||||||
android:defaultValue="www.iana.org"
|
android:defaultValue="www.iana.org"
|
||||||
app:useSimpleSummaryProvider="true" />
|
app:useSimpleSummaryProvider="true" />
|
||||||
|
|
||||||
<com.takisoft.preferencex.EditTextPreference
|
<com.takisoft.preferencex.EditTextPreference
|
||||||
android:key="byedpi_oob_data"
|
android:key="byedpi_oob_data"
|
||||||
android:title="OOB Data"
|
android:title="@string/oob_data"
|
||||||
android:defaultValue="a"
|
android:defaultValue="a"
|
||||||
app:useSimpleSummaryProvider="true" />
|
app:useSimpleSummaryProvider="true" />
|
||||||
|
|
||||||
@ -156,24 +148,4 @@
|
|||||||
|
|
||||||
</androidx.preference.PreferenceCategory>
|
</androidx.preference.PreferenceCategory>
|
||||||
|
|
||||||
<androidx.preference.PreferenceCategory
|
|
||||||
android:title="@string/about_category">
|
|
||||||
|
|
||||||
<Preference
|
|
||||||
android:key="version"
|
|
||||||
android:title="@string/version"
|
|
||||||
app:useSimpleSummaryProvider="true"
|
|
||||||
tools:summary="1.0.0" />
|
|
||||||
|
|
||||||
<Preference
|
|
||||||
android:key="source_code"
|
|
||||||
android:title="@string/source_code_link"
|
|
||||||
android:icon="@drawable/ic_github_36">
|
|
||||||
<intent
|
|
||||||
android:action="android.intent.action.VIEW"
|
|
||||||
android:data="https://github.com/dovecoteescapee/ByeDPIAndroid" />
|
|
||||||
</Preference>
|
|
||||||
|
|
||||||
</androidx.preference.PreferenceCategory>
|
|
||||||
|
|
||||||
</PreferenceScreen>
|
</PreferenceScreen>
|
76
app/src/main/res/xml/main_settings.xml
Normal file
76
app/src/main/res/xml/main_settings.xml
Normal file
@ -0,0 +1,76 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<PreferenceScreen xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||||
|
xmlns:tools="http://schemas.android.com/tools"
|
||||||
|
android:tag="main_settings">
|
||||||
|
|
||||||
|
<androidx.preference.PreferenceCategory
|
||||||
|
android:title="@string/general_category">
|
||||||
|
|
||||||
|
<DropDownPreference
|
||||||
|
android:key="app_theme"
|
||||||
|
android:title="@string/theme_settings"
|
||||||
|
android:entries="@array/themes"
|
||||||
|
android:entryValues="@array/themes_entries"
|
||||||
|
android:defaultValue="system"
|
||||||
|
app:useSimpleSummaryProvider="true" />
|
||||||
|
|
||||||
|
<DropDownPreference
|
||||||
|
android:key="byedpi_mode"
|
||||||
|
android:title="@string/mode_setting"
|
||||||
|
android:entries="@array/byedpi_modes"
|
||||||
|
android:entryValues="@array/byedpi_modes_entries"
|
||||||
|
android:defaultValue="vpn"
|
||||||
|
app:useSimpleSummaryProvider="true" />
|
||||||
|
|
||||||
|
<com.takisoft.preferencex.EditTextPreference
|
||||||
|
android:key="dns_ip"
|
||||||
|
android:title="@string/dbs_ip_setting"
|
||||||
|
android:defaultValue="1.1.1.1"
|
||||||
|
app:useSimpleSummaryProvider="true"/>
|
||||||
|
|
||||||
|
</androidx.preference.PreferenceCategory>
|
||||||
|
|
||||||
|
<androidx.preference.PreferenceCategory
|
||||||
|
android:title="@string/byedpi_category">
|
||||||
|
|
||||||
|
<SwitchPreference
|
||||||
|
android:key="byedpi_enable_cmd_settings"
|
||||||
|
android:title="@string/use_command_line_settings"
|
||||||
|
android:defaultValue="false" />
|
||||||
|
|
||||||
|
<Preference
|
||||||
|
android:key="byedpi_ui_settings"
|
||||||
|
android:title="@string/ui_editor"
|
||||||
|
app:useSimpleSummaryProvider="true"
|
||||||
|
app:fragment="io.github.dovecoteescapee.byedpi.fragments.ByeDpiUISettingsFragment" />
|
||||||
|
|
||||||
|
<Preference
|
||||||
|
android:key="byedpi_cmd_settings"
|
||||||
|
android:title="@string/command_line_editor"
|
||||||
|
app:useSimpleSummaryProvider="true"
|
||||||
|
app:fragment="io.github.dovecoteescapee.byedpi.fragments.ByeDpiCommandLineSettingsFragment" />
|
||||||
|
|
||||||
|
</androidx.preference.PreferenceCategory>
|
||||||
|
|
||||||
|
<androidx.preference.PreferenceCategory
|
||||||
|
android:title="@string/about_category">
|
||||||
|
|
||||||
|
<Preference
|
||||||
|
android:key="version"
|
||||||
|
android:title="@string/version"
|
||||||
|
app:useSimpleSummaryProvider="true"
|
||||||
|
tools:summary="1.0.0" />
|
||||||
|
|
||||||
|
<Preference
|
||||||
|
android:key="source_code"
|
||||||
|
android:title="@string/source_code_link"
|
||||||
|
android:icon="@drawable/ic_github_36">
|
||||||
|
<intent
|
||||||
|
android:action="android.intent.action.VIEW"
|
||||||
|
android:data="https://github.com/dovecoteescapee/ByeDPIAndroid" />
|
||||||
|
</Preference>
|
||||||
|
|
||||||
|
</androidx.preference.PreferenceCategory>
|
||||||
|
|
||||||
|
</PreferenceScreen>
|
Loading…
Reference in New Issue
Block a user