From 0aead76a2357fa6fbcead86f777c24592e069a99 Mon Sep 17 00:00:00 2001 From: gVisor bot Date: Fri, 4 Feb 2022 06:11:24 +0800 Subject: [PATCH] [Feat] update gvisor Chore: use "-m mark --mark" instead of "-m owner --uid-owner" --- component/script/build_local.go | 9 - component/script/build_xgo.go | 25 - component/script/clash_module.c | 735 ------------------------ component/script/clash_module.go | 337 ----------- component/script/clash_module.h | 62 -- component/script/clash_module_export.go | 136 ----- component/script/thread.go | 52 -- go.mod | 2 - hub/executor/executor.go | 7 +- listener/listener.go | 38 +- listener/tun/ipstack/gvisor/tun.go | 4 +- listener/tun/ipstack/lwip/dns.go | 87 --- listener/tun/ipstack/lwip/tcp.go | 61 -- listener/tun/ipstack/lwip/tun.go | 121 ---- listener/tun/ipstack/lwip/udp.go | 74 --- rule/common/base.go | 15 + rule/geosite.go | 70 --- rule/port.go | 125 ---- rule/script.go | 73 --- 19 files changed, 52 insertions(+), 1981 deletions(-) delete mode 100644 component/script/build_local.go delete mode 100644 component/script/build_xgo.go delete mode 100644 component/script/clash_module.c delete mode 100644 component/script/clash_module.go delete mode 100644 component/script/clash_module.h delete mode 100644 component/script/clash_module_export.go delete mode 100644 component/script/thread.go delete mode 100644 listener/tun/ipstack/lwip/dns.go delete mode 100644 listener/tun/ipstack/lwip/tcp.go delete mode 100644 listener/tun/ipstack/lwip/tun.go delete mode 100644 listener/tun/ipstack/lwip/udp.go delete mode 100644 rule/geosite.go delete mode 100644 rule/port.go delete mode 100644 rule/script.go diff --git a/component/script/build_local.go b/component/script/build_local.go deleted file mode 100644 index b3e5640e..00000000 --- a/component/script/build_local.go +++ /dev/null @@ -1,9 +0,0 @@ -//go:build build_local -// +build build_local - -package script - -/* -#cgo pkg-config: python3-embed -*/ -import "C" diff --git a/component/script/build_xgo.go b/component/script/build_xgo.go deleted file mode 100644 index 4a066c90..00000000 --- a/component/script/build_xgo.go +++ /dev/null @@ -1,25 +0,0 @@ -//go:build !build_local && cgo -// +build !build_local,cgo - -package script - -/* -#cgo linux,amd64 pkg-config: python3-embed - -#cgo darwin,amd64 CFLAGS: -I/build/python/python-3.9.7-darwin-amd64/include/python3.9 -#cgo darwin,arm64 CFLAGS: -I/build/python/python-3.9.7-darwin-arm64/include/python3.9 -#cgo windows,amd64 CFLAGS: -I/build/python/python-3.9.7-windows-amd64/include -DMS_WIN64 -#cgo windows,386 CFLAGS: -I/build/python/python-3.9.7-windows-386/include -//#cgo linux,amd64 CFLAGS: -I/home/runner/work/clash/clash/bin/python/python-3.9.7-linux-amd64/include/python3.9 -//#cgo linux,arm64 CFLAGS: -I/build/python/python-3.9.7-linux-arm64/include/python3.9 -//#cgo linux,386 CFLAGS: -I/build/python/python-3.9.7-linux-386/include/python3.9 - -#cgo darwin,amd64 LDFLAGS: -L/build/python/python-3.9.7-darwin-amd64/lib -lpython3.9 -ldl -framework CoreFoundation -#cgo darwin,arm64 LDFLAGS: -L/build/python/python-3.9.7-darwin-arm64/lib -lpython3.9 -ldl -framework CoreFoundation -#cgo windows,amd64 LDFLAGS: -L/build/python/python-3.9.7-windows-amd64/lib -lpython39 -lpthread -lm -#cgo windows,386 LDFLAGS: -L/build/python/python-3.9.7-windows-386/lib -lpython39 -lpthread -lm -//#cgo linux,amd64 LDFLAGS: -L/home/runner/work/clash/clash/bin/python/python-3.9.7-linux-amd64/lib -lpython3.9 -lpthread -ldl -lutil -lm -//#cgo linux,arm64 LDFLAGS: -L/build/python/python-3.9.7-linux-arm64/lib -lpython3.9 -lpthread -ldl -lutil -lm -//#cgo linux,386 LDFLAGS: -L/build/python/python-3.9.7-linux-386/lib -lpython3.9 -lpthread -ldl -lutil -lm -*/ -import "C" diff --git a/component/script/clash_module.c b/component/script/clash_module.c deleted file mode 100644 index f15f0191..00000000 --- a/component/script/clash_module.c +++ /dev/null @@ -1,735 +0,0 @@ -#define PY_SSIZE_T_CLEAN - -#include "clash_module.h" -#include - -PyObject *clash_module; -PyObject *main_fn; -PyObject *clash_context; - -// init_python -void init_python(const char *program, const char *path) { - -// Py_NoSiteFlag = 1; -// Py_FrozenFlag = 1; -// Py_IgnoreEnvironmentFlag = 1; -// Py_IsolatedFlag = 1; - - append_inittab(); - - wchar_t *programName = Py_DecodeLocale(program, NULL); - if (programName != NULL) { - Py_SetProgramName(programName); - PyMem_RawFree(programName); - } - -// wchar_t *newPath = Py_DecodeLocale(path, NULL); -// if (newPath != NULL) { -// Py_SetPath(newPath); -// PyMem_RawFree(newPath); -// } - -// Py_Initialize(); - Py_InitializeEx(0); - - char *pathPrefix = "import sys; sys.path.append('"; - char *pathSuffix = "')"; - char *newPath = (char *) malloc(strlen(pathPrefix) + strlen(path) + strlen(pathSuffix)); - sprintf(newPath, "%s%s%s", pathPrefix, path, pathSuffix); - - PyRun_SimpleString(newPath); - free(newPath); - - /* Optionally import the module; alternatively, - import can be deferred until the embedded script - imports it. */ - clash_module = PyImport_ImportModule("clash"); -} - -// Load function, same as "import module_name.func_name as obj" in Python -// Returns the function object or NULL if not found -PyObject *load_func(const char *module_name, char *func_name) { - // Import the module - PyObject *py_mod_name = PyUnicode_FromString(module_name); - if (py_mod_name == NULL) { - return NULL; - } - - PyObject *module = PyImport_Import(py_mod_name); - Py_DECREF(py_mod_name); - if (module == NULL) { - return NULL; - } - - // Get function, same as "getattr(module, func_name)" in Python - PyObject *func = PyObject_GetAttrString(module, func_name); - Py_DECREF(module); - return func; -} - -// Return last error as char *, NULL if there was no error -const char *py_last_error() { - PyObject *err = PyErr_Occurred(); - if (err == NULL) { - return NULL; - } - - PyObject *type, *value, *traceback; - PyErr_Fetch(&type, &value, &traceback); - - if (value == NULL) { - return NULL; - } - - PyObject *str = PyObject_Str(value); - const char *utf8 = PyUnicode_AsUTF8(str); - Py_DECREF(str); - PyErr_Clear(); - return utf8; -} - -void py_clear(PyObject *obj) { - Py_CLEAR(obj); -} - -void load_main_func() { - main_fn = load_func(CLASH_SCRIPT_MODULE_NAME, "main"); -} - -/** callback function, that call go function by python3 script. **/ - -resolve_ip_callback resolve_ip_callback_fn; - -geoip_callback geoip_callback_fn; - -rule_provider_callback rule_provider_callback_fn; - -log_callback log_callback_fn; - -void -set_resolve_ip_callback(resolve_ip_callback cb) -{ - resolve_ip_callback_fn = cb; -} - -void -set_geoip_callback(geoip_callback cb) -{ - geoip_callback_fn = cb; -} - -void -set_rule_provider_callback(rule_provider_callback cb) -{ - rule_provider_callback_fn = cb; -} - -void -set_log_callback(log_callback cb) -{ - log_callback_fn = cb; -} - -/** end callback function **/ - -/* --------------------------------------------------------------------- */ - -/* RuleProvider objects */ - -typedef struct { - PyObject_HEAD - PyObject *name; /* rule provider name */ -} RuleProviderObject; - -static int -RuleProvider_traverse(RuleProviderObject *self, visitproc visit, void *arg) -{ - Py_VISIT(self->name); - return 0; -} - -static int -RuleProvider_clear(RuleProviderObject *self) -{ - Py_CLEAR(self->name); - return 0; -} - -static void -RuleProvider_dealloc(RuleProviderObject *self) -{ - PyObject_GC_UnTrack(self); - RuleProvider_clear(self); - Py_TYPE(self)->tp_free((PyObject *) self); -} - -static PyObject * -RuleProvider_new(PyTypeObject *type, PyObject *args, PyObject *kwds) -{ - RuleProviderObject *self; - self = (RuleProviderObject *) type->tp_alloc(type, 0); - if (self != NULL) { - self->name = PyUnicode_FromString(""); - if (self->name == NULL) { - Py_DECREF(self); - return NULL; - } - } - return (PyObject *) self; -} - -static int -RuleProvider_init(RuleProviderObject *self, PyObject *args, PyObject *kwds) -{ - static char *kwlist[] = {"name", NULL}; - PyObject *name = NULL, *tmp; - - if (!PyArg_ParseTupleAndKeywords(args, kwds, "|Us", kwlist, &name)) - return -1; - - if (name) { - tmp = self->name; - Py_INCREF(name); - self->name = name; - Py_DECREF(tmp); - } - return 0; -} - -//static PyMemberDef RuleProvider_members[] = { -// {"adapter_type", T_STRING, offsetof(RuleProviderObject, adapter_type), 0, -// "adapter type"}, -// {NULL} /* Sentinel */ -//}; - -static PyObject * -RuleProvider_getname(RuleProviderObject *self, void *closure) -{ - Py_INCREF(self->name); - return self->name; -} - -static int -RuleProvider_setname(RuleProviderObject *self, PyObject *value, void *closure) -{ - if (value == NULL) { - PyErr_SetString(PyExc_TypeError, "Cannot delete the name attribute"); - return -1; - } - if (!PyUnicode_Check(value)) { - PyErr_SetString(PyExc_TypeError, - "The name attribute value must be a string"); - return -1; - } - Py_INCREF(value); - Py_CLEAR(self->name); - self->name = value; - return 0; -} - -static PyGetSetDef RuleProvider_getsetters[] = { - {"name", (getter) RuleProvider_getname, (setter) RuleProvider_setname, - "name", NULL}, - {NULL} /* Sentinel */ -}; - -static PyObject * -RuleProvider_name(RuleProviderObject *self, PyObject *Py_UNUSED(ignored)) -{ - Py_INCREF(self->name); - return self->name; -} - -static PyObject * -RuleProvider_match(RuleProviderObject *self, PyObject *args) -{ - PyObject *result; - PyObject *tmp; - const char *provider_name; - - if (!PyArg_ParseTuple(args, "O!", &PyDict_Type, &tmp)) //Format "O","O!","O&": Borrowed reference. - return NULL; - - if (tmp == NULL) - Py_RETURN_FALSE; - - Py_INCREF(tmp); -// PyObject *py_src_port = PyDict_GetItemString(tmp, "src_port"); //Return value: Borrowed reference. -// PyObject *py_dst_port = PyDict_GetItemString(tmp, "dst_port"); //Return value: Borrowed reference. -// Py_INCREF(py_src_port); -// Py_INCREF(py_dst_port); -// char *c_src_port = (char *) malloc(PyLong_AsSize_t(py_src_port)); -// char *c_dst_port = (char *) malloc(PyLong_AsSize_t(py_dst_port)); -// sprintf(c_src_port, "%ld", PyLong_AsLong(py_src_port)); -// sprintf(c_dst_port, "%ld", PyLong_AsLong(py_dst_port)); - - struct Metadata metadata = { - .type = PyUnicode_AsUTF8(PyDict_GetItemString(tmp, "type")), // PyDict_GetItemString() Return value: Borrowed reference. - .network = PyUnicode_AsUTF8(PyDict_GetItemString(tmp, "network")), - .process_name = PyUnicode_AsUTF8(PyDict_GetItemString(tmp, "process_name")), - .host = PyUnicode_AsUTF8(PyDict_GetItemString(tmp, "host")), - .src_ip = PyUnicode_AsUTF8(PyDict_GetItemString(tmp, "src_ip")), - .src_port = (unsigned short)PyLong_AsUnsignedLong(PyDict_GetItemString(tmp, "src_port")), - .dst_ip = PyUnicode_AsUTF8(PyDict_GetItemString(tmp, "dst_ip")), - .dst_port = (unsigned short)PyLong_AsUnsignedLong(PyDict_GetItemString(tmp, "dst_port")) - }; - -// Py_DECREF(py_src_port); -// Py_DECREF(py_dst_port); - - Py_INCREF(self->name); - provider_name = PyUnicode_AsUTF8(self->name); - Py_DECREF(self->name); - Py_DECREF(tmp); - - int rs = rule_provider_callback_fn(provider_name, &metadata); - - result = (rs == 1) ? Py_True : Py_False; - Py_INCREF(result); - return result; -} - -static PyMethodDef RuleProvider_methods[] = { - {"name", (PyCFunction) RuleProvider_name, METH_NOARGS, - "Return the RuleProvider name" - }, - {"match", (PyCFunction) RuleProvider_match, METH_VARARGS, - "Match the rule by the RuleProvider, match(metadata) -> boolean" - }, - {NULL} /* Sentinel */ -}; - -static PyTypeObject RuleProviderType = { - PyVarObject_HEAD_INIT(NULL, 0) - .tp_name = "clash.RuleProvider", - .tp_doc = "Clash RuleProvider objects", - .tp_basicsize = sizeof(RuleProviderObject), - .tp_itemsize = 0, - .tp_flags = Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE | Py_TPFLAGS_HAVE_GC, - .tp_new = RuleProvider_new, - .tp_init = (initproc) RuleProvider_init, - .tp_dealloc = (destructor) RuleProvider_dealloc, - .tp_traverse = (traverseproc) RuleProvider_traverse, - .tp_clear = (inquiry) RuleProvider_clear, -// .tp_members = RuleProvider_members, - .tp_methods = RuleProvider_methods, - .tp_getset = RuleProvider_getsetters, -}; - -/* end RuleProvider objects */ -/* --------------------------------------------------------------------- */ - -/* Context objects */ - -typedef struct { - PyObject_HEAD - PyObject *rule_providers; /* Dict */ -} ContextObject; - -static int -Context_traverse(ContextObject *self, visitproc visit, void *arg) -{ - Py_VISIT(self->rule_providers); - return 0; -} - -static int -Context_clear(ContextObject *self) -{ - Py_CLEAR(self->rule_providers); - return 0; -} - -static void -Context_dealloc(ContextObject *self) -{ - PyObject_GC_UnTrack(self); - Context_clear(self); - Py_TYPE(self)->tp_free((PyObject *) self); -} - -static PyObject * -Context_new(PyTypeObject *type, PyObject *args, PyObject *kwds) -{ - ContextObject *self; - self = (ContextObject *) type->tp_alloc(type, 0); - if (self != NULL) { - self->rule_providers = PyDict_New(); - if (self->rule_providers == NULL) { - Py_DECREF(self); - return NULL; - } - } - return (PyObject *) self; -} - -static int -Context_init(ContextObject *self, PyObject *args, PyObject *kwds) -{ - static char *kwlist[] = {"rule_providers", NULL}; - PyObject *rule_providers = NULL, *tmp; - - if (!PyArg_ParseTupleAndKeywords(args, kwds, "|O", kwlist, - &rule_providers)) - return -1; - - if (rule_providers) { - tmp = self->rule_providers; - Py_INCREF(rule_providers); - self->rule_providers = rule_providers; - Py_DECREF(tmp); - } - return 0; -} - -static PyObject * -Context_getrule_providers(ContextObject *self, void *closure) -{ - Py_INCREF(self->rule_providers); - return self->rule_providers; -} - -static int -Context_setrule_providers(ContextObject *self, PyObject *value, void *closure) -{ - if (value == NULL) { - PyErr_SetString(PyExc_TypeError, "Cannot delete the rule_providers attribute"); - return -1; - } - if (!PyDict_Check(value)) { - PyErr_SetString(PyExc_TypeError, - "The rule_providers attribute value must be a dict"); - return -1; - } - Py_INCREF(value); - Py_CLEAR(self->rule_providers); - self->rule_providers = value; - return 0; -} - -static PyGetSetDef Context_getsetters[] = { - {"rule_providers", (getter) Context_getrule_providers, (setter) Context_setrule_providers, - "rule_providers", NULL}, - {NULL} /* Sentinel */ -}; - -static PyObject * -Context_resolve_ip(PyObject *self, PyObject *args) -{ - const char *host; - const char *ip; - - if (!PyArg_ParseTuple(args, "s", &host)) - return NULL; - - if (host == NULL) - return PyUnicode_FromString(""); - - ip = resolve_ip_callback_fn(host); - - return PyUnicode_FromString(ip); -} - -static PyObject * -Context_geoip(PyObject *self, PyObject *args) -{ - const char *ip; - const char *countryCode; - - if (!PyArg_ParseTuple(args, "s", &ip)) - return NULL; - - if (ip == NULL) - return PyUnicode_FromString(""); - - countryCode = geoip_callback_fn(ip); - - return PyUnicode_FromString(countryCode); -} - -static PyObject * -Context_log(PyObject *self, PyObject *args) -{ - const char *msg; - - if (!PyArg_ParseTuple(args, "s", &msg)) - return NULL; - - log_callback_fn(msg); - - Py_RETURN_NONE; -} - -static PyMethodDef Context_methods[] = { - {"resolve_ip", (PyCFunction) Context_resolve_ip, METH_VARARGS, - "resolve_ip(host) -> string" - }, - {"geoip", (PyCFunction) Context_geoip, METH_VARARGS, - "geoip(ip) -> string" - }, - {"log", (PyCFunction) Context_log, METH_VARARGS, - "log(msg) -> void" - }, - {NULL} /* Sentinel */ -}; - -static PyTypeObject ContextType = { - PyVarObject_HEAD_INIT(NULL, 0) - .tp_name = "clash.Context", - .tp_doc = "Clash Context objects", - .tp_basicsize = sizeof(ContextObject), - .tp_itemsize = 0, - .tp_flags = Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE | Py_TPFLAGS_HAVE_GC, - .tp_new = Context_new, - .tp_init = (initproc) Context_init, - .tp_dealloc = (destructor) Context_dealloc, - .tp_traverse = (traverseproc) Context_traverse, - .tp_clear = (inquiry) Context_clear, - .tp_methods = Context_methods, - .tp_getset = Context_getsetters, -}; - -static PyModuleDef clashmodule = { - PyModuleDef_HEAD_INIT, - .m_name = "clash", - .m_doc = "Clash module that creates an extension module for python3.", - .m_size = -1, -}; - -PyMODINIT_FUNC -PyInit_clash(void) -{ - PyObject *m; - - m = PyModule_Create(&clashmodule); - if (m == NULL) - return NULL; - - if (PyType_Ready(&RuleProviderType) < 0) - return NULL; - - Py_INCREF(&RuleProviderType); - if (PyModule_AddObject(m, "RuleProvider", (PyObject *) &RuleProviderType) < 0) { - Py_DECREF(&RuleProviderType); - Py_DECREF(m); - return NULL; - } - - if (PyType_Ready(&ContextType) < 0) - return NULL; - - Py_INCREF(&ContextType); - if (PyModule_AddObject(m, "Context", (PyObject *) &ContextType) < 0) { - Py_DECREF(&ContextType); - Py_DECREF(m); - return NULL; - } - - return m; -} - -/* end Context objects */ - -/* --------------------------------------------------------------------- */ - -void -append_inittab() -{ - /* Add a built-in module, before Py_Initialize */ - PyImport_AppendInittab("clash", PyInit_clash); -} - -int new_clash_py_context(const char *provider_name_arr[], int size) { - PyObject *dict = PyDict_New(); //Return value: New reference. - if (dict == NULL) { - PyErr_SetString(PyExc_TypeError, - "PyDict_New failure"); - return 0; - } - - for (int i = 0; i < size; i++) { - PyObject *rule_provider = RuleProvider_new(&RuleProviderType, NULL, NULL); - if (rule_provider == NULL) { - Py_DECREF(dict); - PyErr_SetString(PyExc_TypeError, - "RuleProvider_new failure"); - return 0; - } - - RuleProviderObject *providerObj = (RuleProviderObject *) rule_provider; - - PyObject *py_name = PyUnicode_FromString(provider_name_arr[i]); //Return value: New reference. - RuleProvider_setname(providerObj, py_name, NULL); - Py_DECREF(py_name); - - PyDict_SetItemString(dict, provider_name_arr[i], rule_provider); //Parameter value: New reference. - Py_DECREF(rule_provider); - } - - clash_context = Context_new(&ContextType, NULL, NULL); - - if (clash_context == NULL) { - Py_DECREF(dict); - PyErr_SetString(PyExc_TypeError, - "Context_new failure"); - return 0; - } - - Context_setrule_providers((ContextObject *) clash_context, dict, NULL); - Py_DECREF(dict); - return 1; -} - -const char *call_main( - const char *type, - const char *network, - const char *process_name, - const char *host, - const char *src_ip, - unsigned short src_port, - const char *dst_ip, - unsigned short dst_port) { - - PyObject *metadataDict; - PyObject *tupleArgs; - PyObject *result; - - metadataDict = PyDict_New(); //Return value: New reference. - - if (metadataDict == NULL) { - PyErr_SetString(PyExc_TypeError, - "PyDict_New failure"); - return "-1"; - } - - PyObject *p_type = PyUnicode_FromString(type); //Return value: New reference. - PyObject *p_network = PyUnicode_FromString(network); //Return value: New reference. - PyObject *p_process_name = PyUnicode_FromString(process_name); //Return value: New reference. - PyObject *p_host = PyUnicode_FromString(host); //Return value: New reference. - PyObject *p_src_ip = PyUnicode_FromString(src_ip); //Return value: New reference. - PyObject *p_src_port = PyLong_FromUnsignedLong((unsigned long)src_port); //Return value: New reference. - PyObject *p_dst_ip = PyUnicode_FromString(dst_ip); //Return value: New reference. - PyObject *p_dst_port = PyLong_FromUnsignedLong((unsigned long)dst_port); //Return value: New reference. - - PyDict_SetItemString(metadataDict, "type", p_type); //Parameter value: New reference. - PyDict_SetItemString(metadataDict, "network", p_network); //Parameter value: New reference. - PyDict_SetItemString(metadataDict, "process_name", p_process_name); //Parameter value: New reference. - PyDict_SetItemString(metadataDict, "host", p_host); //Parameter value: New reference. - PyDict_SetItemString(metadataDict, "src_ip", p_src_ip); //Parameter value: New reference. - PyDict_SetItemString(metadataDict, "src_port", p_src_port); //Parameter value: New reference. - PyDict_SetItemString(metadataDict, "dst_ip", p_dst_ip); //Parameter value: New reference. - PyDict_SetItemString(metadataDict, "dst_port", p_dst_port); //Parameter value: New reference. - - Py_DECREF(p_type); - Py_DECREF(p_network); - Py_DECREF(p_process_name); - Py_DECREF(p_host); - Py_DECREF(p_src_ip); - Py_DECREF(p_src_port); - Py_DECREF(p_dst_ip); - Py_DECREF(p_dst_port); - - tupleArgs = PyTuple_New(2); //Return value: New reference. - if (tupleArgs == NULL) { - Py_DECREF(metadataDict); - PyErr_SetString(PyExc_TypeError, - "PyTuple_New failure"); - return "-1"; - } - - Py_INCREF(clash_context); - PyTuple_SetItem(tupleArgs, 0, clash_context); //clash_context Parameter value: Stolen reference. - PyTuple_SetItem(tupleArgs, 1, metadataDict); //metadataDict Parameter value: Stolen reference. - - Py_INCREF(main_fn); - result = PyObject_CallObject(main_fn, tupleArgs); //Return value: New reference. - Py_DECREF(main_fn); - Py_DECREF(tupleArgs); - - if (result == NULL) { - return "-1"; - } - - if (!PyUnicode_Check(result)) { - Py_DECREF(result); - PyErr_SetString(PyExc_TypeError, - "script main function return value must be a string"); - return "-1"; - } - - const char *adapter = PyUnicode_AsUTF8(result); - - Py_DECREF(result); - - return adapter; -} - -int call_shortcut(PyObject *shortcut_fn, - const char *type, - const char *network, - const char *process_name, - const char *host, - const char *src_ip, - unsigned short src_port, - const char *dst_ip, - unsigned short dst_port) { - - PyObject *args; - PyObject *result; - - args = Py_BuildValue("{s:O, s:s, s:s, s:s, s:s, s:H, s:s, s:H}", - "ctx", clash_context, - "network", network, - "process_name", process_name, - "host", host, - "src_ip", src_ip, - "src_port", src_port, - "dst_ip", dst_ip, - "dst_port", dst_port); //Return value: New reference. - - if (args == NULL) { - PyErr_SetString(PyExc_TypeError, - "Py_BuildValue failure"); - return -1; - } - - PyObject *tupleArgs = PyTuple_New(0); //Return value: New reference. - - Py_INCREF(clash_context); - Py_INCREF(shortcut_fn); - result = PyObject_Call(shortcut_fn, tupleArgs, args); //Return value: New reference. - Py_DECREF(shortcut_fn); - Py_DECREF(clash_context); - Py_DECREF(tupleArgs); - Py_DECREF(args); - - if (result == NULL) { - return -1; - } - - if (!PyBool_Check(result)) { - Py_DECREF(result); - PyErr_SetString(PyExc_TypeError, - "script shortcut return value must be as boolean"); - return -1; - } - - int rs = (result == Py_True) ? 1 : 0; - - Py_DECREF(result); - - return rs; -} - -void finalize_Python() { - Py_CLEAR(main_fn); - Py_CLEAR(clash_context); - Py_CLEAR(clash_module); - Py_FinalizeEx(); - -// clash_module = NULL; -// main_fn = NULL; -// clash_context = NULL; -} - -/* --------------------------------------------------------------------- */ \ No newline at end of file diff --git a/component/script/clash_module.go b/component/script/clash_module.go deleted file mode 100644 index 5fccb4c1..00000000 --- a/component/script/clash_module.go +++ /dev/null @@ -1,337 +0,0 @@ -package script - -/* -#include "clash_module.h" - -extern const char *resolveIPCallbackFn(const char *host); - -void -go_set_resolve_ip_callback() { - set_resolve_ip_callback(resolveIPCallbackFn); -} - -extern const char *geoipCallbackFn(const char *ip); - -void -go_set_geoip_callback() { - set_geoip_callback(geoipCallbackFn); -} - -extern const int ruleProviderCallbackFn(const char *provider_name, struct Metadata *metadata); - -void -go_set_rule_provider_callback() { - set_rule_provider_callback(ruleProviderCallbackFn); -} - -extern void logCallbackFn(const char *msg); - -void -go_set_log_callback() { - set_log_callback(logCallbackFn); -} -*/ -import "C" -import ( - "errors" - "fmt" - "os" - "runtime" - "strconv" - "strings" - "sync" - "syscall" - "unsafe" - - "github.com/Dreamacro/clash/constant" - "github.com/Dreamacro/clash/log" -) - -const ClashScriptModuleName = C.CLASH_SCRIPT_MODULE_NAME - -var lock sync.Mutex - -type PyObject C.PyObject - -func togo(cobject *C.PyObject) *PyObject { - return (*PyObject)(cobject) -} - -func toc(object *PyObject) *C.PyObject { - return (*C.PyObject)(object) -} - -func (pyObject *PyObject) IncRef() { - C.Py_IncRef(toc(pyObject)) -} - -func (pyObject *PyObject) DecRef() { - C.Py_DecRef(toc(pyObject)) -} - -func (pyObject *PyObject) Clear() { - C.py_clear(toc(pyObject)) -} - -// Py_Initialize initialize Python3 -func Py_Initialize(program string, path string) error { - lock.Lock() - defer lock.Unlock() - - if C.Py_IsInitialized() != 0 { - if pyThreadState != nil { - PyEval_RestoreThread(pyThreadState) - } - C.finalize_Python() - } - - path = strings.ReplaceAll(path, "\\", "/") - cPath := C.CString(path) - //defer C.free(unsafe.Pointer(cPath)) - - C.init_python(C.CString(program), cPath) - err := PyLastError() - - if err != nil { - if C.Py_IsInitialized() != 0 { - C.finalize_Python() - _ = os.RemoveAll(constant.Path.ScriptDir()) - } - return err - } else if C.Py_IsInitialized() == 0 { - err = errors.New("initialized script module failure") - return err - } - - initPython3Callback() - return nil -} - -func Py_IsInitialized() bool { - lock.Lock() - defer lock.Unlock() - - return C.Py_IsInitialized() != 0 -} - -func Py_Finalize() { - lock.Lock() - defer lock.Unlock() - - if C.Py_IsInitialized() != 0 { - if pyThreadState != nil { - PyEval_RestoreThread(pyThreadState) - } - C.finalize_Python() - _ = os.RemoveAll(constant.Path.ScriptDir()) - log.Warnln("Clash clean up script mode.") - } -} - -//Py_GetVersion get -func Py_GetVersion() string { - cversion := C.Py_GetVersion() - return strings.Split(C.GoString(cversion), "\n")[0] -} - -// loadPyFunc loads a Python function by module and function name -func loadPyFunc(moduleName, funcName string) (*C.PyObject, error) { - // Convert names to C char* - cMod := C.CString(moduleName) - cFunc := C.CString(funcName) - - // Free memory allocated by C.CString - defer func() { - C.free(unsafe.Pointer(cMod)) - C.free(unsafe.Pointer(cFunc)) - }() - - fnc := C.load_func(cMod, cFunc) - if fnc == nil { - return nil, PyLastError() - } - - return fnc, nil -} - -//PyLastError python last error -func PyLastError() error { - cp := C.py_last_error() - if cp == nil { - return nil - } - - return errors.New(C.GoString(cp)) -} - -func LoadShortcutFunction(shortcut string) (*PyObject, error) { - fnc, err := loadPyFunc(ClashScriptModuleName, shortcut) - if err != nil { - return nil, err - } - return togo(fnc), nil -} - -func LoadMainFunction() error { - C.load_main_func() - err := PyLastError() - if err != nil { - return err - } - return nil -} - -//CallPyMainFunction call python script main function -//return the proxy adapter name. -func CallPyMainFunction(mtd *constant.Metadata) (string, error) { - _type := C.CString(mtd.Type.String()) - network := C.CString(mtd.NetWork.String()) - processName := C.CString(mtd.Process) - host := C.CString(mtd.Host) - - srcPortGo, _ := strconv.ParseUint(mtd.SrcPort, 10, 16) - dstPortGo, _ := strconv.ParseUint(mtd.DstPort, 10, 16) - srcPort := C.ushort(srcPortGo) - dstPort := C.ushort(dstPortGo) - - dstIpGo := "" - srcIpGo := "" - if mtd.SrcIP != nil { - srcIpGo = mtd.SrcIP.String() - } - if mtd.DstIP != nil { - dstIpGo = mtd.DstIP.String() - } - srcIp := C.CString(srcIpGo) - dstIp := C.CString(dstIpGo) - - defer func() { - C.free(unsafe.Pointer(_type)) - C.free(unsafe.Pointer(network)) - C.free(unsafe.Pointer(processName)) - C.free(unsafe.Pointer(host)) - C.free(unsafe.Pointer(srcIp)) - C.free(unsafe.Pointer(dstIp)) - }() - - runtime.LockOSThread() - gilState := PyGILState_Ensure() - defer PyGILState_Release(gilState) - - cRs := C.call_main(_type, network, processName, host, srcIp, srcPort, dstIp, dstPort) - - rs := C.GoString(cRs) - if rs == "-1" { - err := PyLastError() - if err != nil { - log.Errorln("[Script] script code error: %s", err.Error()) - killSelf() - return "", fmt.Errorf("script code error: %w", err) - } else { - return "", fmt.Errorf("script code error, result: %v", rs) - } - } - - return rs, nil -} - -//CallPyShortcut call python script shortcuts function -//param: shortcut name -//return the match result. -func CallPyShortcut(fn *PyObject, mtd *constant.Metadata) (bool, error) { - _type := C.CString(mtd.Type.String()) - network := C.CString(mtd.NetWork.String()) - processName := C.CString(mtd.Process) - host := C.CString(mtd.Host) - - srcPortGo, _ := strconv.ParseUint(mtd.SrcPort, 10, 16) - dstPortGo, _ := strconv.ParseUint(mtd.DstPort, 10, 16) - srcPort := C.ushort(srcPortGo) - dstPort := C.ushort(dstPortGo) - - dstIpGo := "" - srcIpGo := "" - if mtd.SrcIP != nil { - srcIpGo = mtd.SrcIP.String() - } - if mtd.DstIP != nil { - dstIpGo = mtd.DstIP.String() - } - srcIp := C.CString(srcIpGo) - dstIp := C.CString(dstIpGo) - - defer func() { - C.free(unsafe.Pointer(_type)) - C.free(unsafe.Pointer(network)) - C.free(unsafe.Pointer(processName)) - C.free(unsafe.Pointer(host)) - C.free(unsafe.Pointer(srcIp)) - C.free(unsafe.Pointer(dstIp)) - }() - - runtime.LockOSThread() - gilState := PyGILState_Ensure() - defer PyGILState_Release(gilState) - - cRs := C.call_shortcut(toc(fn), _type, network, processName, host, srcIp, srcPort, dstIp, dstPort) - - rs := int(cRs) - if rs == -1 { - err := PyLastError() - if err != nil { - log.Errorln("[Script] script shortcut code error: %s", err.Error()) - killSelf() - return false, fmt.Errorf("script shortcut code error: %w", err) - } else { - return false, fmt.Errorf("script shortcut code error: result: %d", rs) - } - } - - if rs == 1 { - return true, nil - } else { - return false, nil - } -} - -func initPython3Callback() { - C.go_set_resolve_ip_callback() - C.go_set_geoip_callback() - C.go_set_rule_provider_callback() - C.go_set_log_callback() -} - -//NewClashPyContext new clash context for python -func NewClashPyContext(ruleProvidersName []string) error { - length := len(ruleProvidersName) - cStringArr := make([]*C.char, length) - for i, v := range ruleProvidersName { - cStringArr[i] = C.CString(v) - defer C.free(unsafe.Pointer(cStringArr[i])) - } - - cArrPointer := unsafe.Pointer(nil) - if length > 0 { - cArrPointer = unsafe.Pointer(&cStringArr[0]) - } - - rs := C.new_clash_py_context((**C.char)(cArrPointer), C.int(length)) - - if int(rs) == 0 { - err := PyLastError() - return fmt.Errorf("new script module context failure: %s", err.Error()) - } - - return nil -} - -func killSelf() { - p, err := os.FindProcess(os.Getpid()) - - if err != nil { - os.Exit(int(syscall.SIGINT)) - return - } - - _ = p.Signal(syscall.SIGINT) -} diff --git a/component/script/clash_module.h b/component/script/clash_module.h deleted file mode 100644 index 3e03e16d..00000000 --- a/component/script/clash_module.h +++ /dev/null @@ -1,62 +0,0 @@ -#ifndef CLASH_CALLBACK_MODULE_H__ -#define CLASH_CALLBACK_MODULE_H__ - -#include - -#define CLASH_SCRIPT_MODULE_NAME "clash_script" - -struct Metadata { - const char *type; /* type socks5/http */ - const char *network; /* network tcp/udp */ - const char *process_name; - const char *host; - const char *src_ip; - unsigned short src_port; - const char *dst_ip; - unsigned short dst_port; -}; - -/** callback function, that call go function by python3 script. **/ -typedef const char *(*resolve_ip_callback)(const char *host); -typedef const char *(*geoip_callback)(const char *ip); -typedef const int (*rule_provider_callback)(const char *provider_name, struct Metadata *metadata); -typedef void (*log_callback)(const char *msg); - -void set_resolve_ip_callback(resolve_ip_callback cb); -void set_geoip_callback(geoip_callback cb); -void set_rule_provider_callback(rule_provider_callback cb); -void set_log_callback(log_callback cb); -/*---------------------------------------------------------------*/ - -void append_inittab(); -void init_python(const char *program, const char *path); -void load_main_func(); -void finalize_Python(); -void py_clear(PyObject *obj); -const char *py_last_error(); - -PyObject *load_func(const char *module_name, char *func_name); - -int new_clash_py_context(const char *provider_name_arr[], int size); - -const char *call_main( - const char *type, - const char *network, - const char *process_name, - const char *host, - const char *src_ip, - unsigned short src_port, - const char *dst_ip, - unsigned short dst_port); - -int call_shortcut(PyObject *shortcut_fn, - const char *type, - const char *network, - const char *process_name, - const char *host, - const char *src_ip, - unsigned short src_port, - const char *dst_ip, - unsigned short dst_port); - -#endif // CLASH_CALLBACK_MODULE_H__ \ No newline at end of file diff --git a/component/script/clash_module_export.go b/component/script/clash_module_export.go deleted file mode 100644 index b3f5355b..00000000 --- a/component/script/clash_module_export.go +++ /dev/null @@ -1,136 +0,0 @@ -package script - -/* -#include "clash_module.h" -*/ -import "C" -import ( - "net" - "strconv" - "strings" - "unsafe" - - "github.com/Dreamacro/clash/component/mmdb" - "github.com/Dreamacro/clash/component/resolver" - "github.com/Dreamacro/clash/constant" - "github.com/Dreamacro/clash/log" -) - -var ( - ruleProviders = map[string]constant.Rule{} - pyThreadState *PyThreadState -) - -func UpdateRuleProviders(rpd map[string]constant.Rule) { - ruleProviders = rpd - if Py_IsInitialized() { - pyThreadState = PyEval_SaveThread() - } -} - -//export resolveIPCallbackFn -func resolveIPCallbackFn(cHost *C.char) *C.char { - host := C.GoString(cHost) - if len(host) == 0 { - cip := C.CString("") - defer C.free(unsafe.Pointer(cip)) - return cip - } - if ip, err := resolver.ResolveIP(host); err == nil { - cip := C.CString(ip.String()) - defer C.free(unsafe.Pointer(cip)) - return cip - } else { - log.Errorln("[Script] resolve ip error: %s", err.Error()) - cip := C.CString("") - defer C.free(unsafe.Pointer(cip)) - return cip - } -} - -//export geoipCallbackFn -func geoipCallbackFn(cIP *C.char) *C.char { - dstIP := net.ParseIP(C.GoString(cIP)) - - if dstIP == nil { - emptyC := C.CString("") - defer C.free(unsafe.Pointer(emptyC)) - - return emptyC - } - - if dstIP.IsPrivate() || constant.TunBroadcastAddr.Equal(dstIP) { - lanC := C.CString("LAN") - defer C.free(unsafe.Pointer(lanC)) - - return lanC - } - - record, _ := mmdb.Instance().Country(dstIP) - - rc := C.CString(strings.ToUpper(record.Country.IsoCode)) - defer C.free(unsafe.Pointer(rc)) - - return rc -} - -//export ruleProviderCallbackFn -func ruleProviderCallbackFn(cProviderName *C.char, cMetadata *C.struct_Metadata) C.int { - //_type := C.GoString(cMetadata._type) - //network := C.GoString(cMetadata.network) - processName := C.GoString(cMetadata.process_name) - host := C.GoString(cMetadata.host) - srcIp := C.GoString(cMetadata.src_ip) - srcPort := strconv.Itoa(int(cMetadata.src_port)) - dstIp := C.GoString(cMetadata.dst_ip) - dstPort := strconv.Itoa(int(cMetadata.dst_port)) - - dst := net.ParseIP(dstIp) - addrType := constant.AtypDomainName - - if dst != nil { - if dst.To4() != nil { - addrType = constant.AtypIPv4 - } else { - addrType = constant.AtypIPv6 - } - } - - metadata := &constant.Metadata{ - Process: processName, - SrcIP: net.ParseIP(srcIp), - DstIP: dst, - SrcPort: srcPort, - DstPort: dstPort, - AddrType: addrType, - Host: host, - } - - providerName := C.GoString(cProviderName) - - rule, ok := ruleProviders[providerName] - if !ok { - log.Warnln("[Script] rule provider [%s] not found", providerName) - return C.int(0) - } - - if strings.HasPrefix(providerName, "geosite:") { - if len(host) == 0 { - return C.int(0) - } - metadata.AddrType = constant.AtypDomainName - } - - rs := rule.Match(metadata) - - if rs { - return C.int(1) - } - return C.int(0) -} - -//export logCallbackFn -func logCallbackFn(msg *C.char) { - - log.Infoln(C.GoString(msg)) -} diff --git a/component/script/thread.go b/component/script/thread.go deleted file mode 100644 index 8b7735c0..00000000 --- a/component/script/thread.go +++ /dev/null @@ -1,52 +0,0 @@ -package script - -/* -#include "Python.h" -*/ -import "C" - -//PyThreadState : https://docs.python.org/3/c-api/init.html#c.PyThreadState -type PyThreadState C.PyThreadState - -//PyGILState is an opaque “handle” to the thread state when PyGILState_Ensure() was called, and must be passed to PyGILState_Release() to ensure Python is left in the same state -type PyGILState C.PyGILState_STATE - -//PyEval_SaveThread : https://docs.python.org/3/c-api/init.html#c.PyEval_SaveThread -func PyEval_SaveThread() *PyThreadState { - return (*PyThreadState)(C.PyEval_SaveThread()) -} - -//PyEval_RestoreThread : https://docs.python.org/3/c-api/init.html#c.PyEval_RestoreThread -func PyEval_RestoreThread(tstate *PyThreadState) { - C.PyEval_RestoreThread((*C.PyThreadState)(tstate)) -} - -//PyThreadState_Get : https://docs.python.org/3/c-api/init.html#c.PyThreadState_Get -func PyThreadState_Get() *PyThreadState { - return (*PyThreadState)(C.PyThreadState_Get()) -} - -//PyThreadState_Swap : https://docs.python.org/3/c-api/init.html#c.PyThreadState_Swap -func PyThreadState_Swap(tstate *PyThreadState) *PyThreadState { - return (*PyThreadState)(C.PyThreadState_Swap((*C.PyThreadState)(tstate))) -} - -//PyGILState_Ensure : https://docs.python.org/3/c-api/init.html#c.PyGILState_Ensure -func PyGILState_Ensure() PyGILState { - return PyGILState(C.PyGILState_Ensure()) -} - -//PyGILState_Release : https://docs.python.org/3/c-api/init.html#c.PyGILState_Release -func PyGILState_Release(state PyGILState) { - C.PyGILState_Release(C.PyGILState_STATE(state)) -} - -//PyGILState_GetThisThreadState : https://docs.python.org/3/c-api/init.html#c.PyGILState_GetThisThreadState -func PyGILState_GetThisThreadState() *PyThreadState { - return (*PyThreadState)(C.PyGILState_GetThisThreadState()) -} - -//PyGILState_Check : https://docs.python.org/3/c-api/init.html#c.PyGILState_Check -func PyGILState_Check() bool { - return C.PyGILState_Check() == 1 -} diff --git a/go.mod b/go.mod index fdd14c9e..64f5b329 100644 --- a/go.mod +++ b/go.mod @@ -41,8 +41,6 @@ require ( github.com/marten-seemann/qtls-go1-18 v0.1.0-beta.1 // indirect github.com/nxadm/tail v1.4.8 // indirect github.com/onsi/ginkgo v1.16.4 // indirect - github.com/google/btree v1.0.1 // indirect - github.com/kr/pretty v0.2.1 // indirect github.com/pmezard/go-difflib v1.0.0 // indirect github.com/u-root/uio v0.0.0-20210528114334-82958018845c // indirect golang.org/x/mod v0.4.2 // indirect diff --git a/hub/executor/executor.go b/hub/executor/executor.go index f8e34ea8..7f37a833 100644 --- a/hub/executor/executor.go +++ b/hub/executor/executor.go @@ -261,14 +261,10 @@ func updateTun(Tun *config.Tun) { if Tun == nil { return } - tcpIn := tunnel.TCPIn() udpIn := tunnel.UDPIn() - if err := P.ReCreateTun(*Tun, tcpIn, udpIn); err != nil { - log.Errorln("Start Tun interface error: %s", err.Error()) - os.Exit(2) - } + P.ReCreateTun(*Tun, tcpIn, udpIn) } func updateUsers(users []auth.AuthUser) { @@ -331,6 +327,7 @@ func updateIPTables(dns *config.DNS, general *config.General, tun *config.Tun) { } tproxy.CleanUpTProxyLinuxIPTables() + dialer.DefaultRoutingMark.Store(2158) err = tproxy.SetTProxyLinuxIPTables(general.Interface, general.TProxyPort, dnsPort) diff --git a/listener/listener.go b/listener/listener.go index 675abb5d..34f79e2b 100644 --- a/listener/listener.go +++ b/listener/listener.go @@ -4,6 +4,7 @@ import ( "fmt" "github.com/Dreamacro/clash/listener/inner" "net" + "os" "runtime" "strconv" "sync" @@ -322,23 +323,50 @@ func ReCreateMixed(port int, tcpIn chan<- C.ConnContext, udpIn chan<- *inbound.P log.Infoln("Mixed(http+socks) proxy listening at: %s", mixedListener.Address()) } -func ReCreateTun(conf config.Tun, tcpIn chan<- C.ConnContext, udpIn chan<- *inbound.PacketAdapter) error { +//func ReCreateTun(conf config.Tun, tcpIn chan<- C.ConnContext, udpIn chan<- *inbound.PacketAdapter) error { +// tunMux.Lock() +// defer tunMux.Unlock() +// +// if tunAdapter != nil { +// tunAdapter.Close() +// tunAdapter = nil +// } +// +// if !conf.Enable { +// return nil +// } +// +// var err error +// tunAdapter, err = tun.New(conf, tcpIn, udpIn) +// +// return err +//} + +func ReCreateTun(conf config.Tun, tcpIn chan<- C.ConnContext, udpIn chan<- *inbound.PacketAdapter) { tunMux.Lock() defer tunMux.Unlock() + var err error + defer func() { + if err != nil { + log.Errorln("Start TUN interface error: %s", err.Error()) + os.Exit(2) + } + }() + if tunAdapter != nil { tunAdapter.Close() tunAdapter = nil } if !conf.Enable { - return nil + return } - var err error tunAdapter, err = tun.New(conf, tcpIn, udpIn) - - return err + if err != nil { + log.Warnln("Failed to start TUN interface: %s", err.Error()) + } } // GetPorts return the ports of proxy servers diff --git a/listener/tun/ipstack/gvisor/tun.go b/listener/tun/ipstack/gvisor/tun.go index 77cd780f..528bf16a 100644 --- a/listener/tun/ipstack/gvisor/tun.go +++ b/listener/tun/ipstack/gvisor/tun.go @@ -54,7 +54,7 @@ func NewAdapter(device dev.TunDevice, conf config.Tun, tcpIn chan<- C.ConnContex NetworkProtocols: []stack.NetworkProtocolFactory{ipv4.NewProtocol, ipv6.NewProtocol}, TransportProtocols: []stack.TransportProtocolFactory{tcp.NewProtocol, udp.NewProtocol}, }) - + adapter := &gvisorAdapter{ device: device, ipstack: ipstack, udpIn: udpIn, @@ -116,7 +116,7 @@ func NewAdapter(device dev.TunDevice, conf config.Tun, tcpIn chan<- C.ConnContex ipstack.SetTransportProtocolHandler(udp.ProtocolNumber, adapter.udpHandlePacket) if resolver.DefaultResolver != nil { - err = adapter.ReCreateDNSServer(resolver.DefaultResolver.(*dns.Resolver), resolver.DefaultHostMapper.(*dns.ResolverEnhancer), conf.DNSListen) + err = adapter.ReCreateDNSServer(resolver.DefaultResolver.(*dns.Resolver), resolver.DefaultHostMapper.(*dns.ResolverEnhancer), conf.DnsHijack) if err != nil { return nil, err } diff --git a/listener/tun/ipstack/lwip/dns.go b/listener/tun/ipstack/lwip/dns.go deleted file mode 100644 index 6a314c08..00000000 --- a/listener/tun/ipstack/lwip/dns.go +++ /dev/null @@ -1,87 +0,0 @@ -package lwip - -import ( - "encoding/binary" - "io" - "net" - "time" - - "github.com/Dreamacro/clash/component/resolver" - D "github.com/Dreamacro/clash/listener/tun/ipstack/commons" - "github.com/Dreamacro/clash/log" - "github.com/yaling888/go-lwip" -) - -const defaultDnsReadTimeout = time.Second * 8 - -func shouldHijackDns(dnsIP net.IP, targetIp net.IP, targetPort int) bool { - if targetPort != 53 { - return false - } - - return dnsIP.Equal(net.IPv4zero) || dnsIP.Equal(targetIp) -} - -func hijackUDPDns(conn golwip.UDPConn, pkt []byte, addr *net.UDPAddr) { - go func() { - defer func(conn golwip.UDPConn) { - _ = conn.Close() - }(conn) - - answer, err := D.RelayDnsPacket(pkt) - if err != nil { - return - } - _, _ = conn.WriteFrom(answer, addr) - }() -} - -func hijackTCPDns(conn net.Conn) { - go func() { - defer func(conn net.Conn) { - _ = conn.Close() - }(conn) - - if err := conn.SetDeadline(time.Now().Add(defaultDnsReadTimeout)); err != nil { - return - } - - for { - var length uint16 - if binary.Read(conn, binary.BigEndian, &length) != nil { - return - } - - data := make([]byte, length) - - _, err := io.ReadFull(conn, data) - if err != nil { - return - } - - rb, err := D.RelayDnsPacket(data) - if err != nil { - continue - } - - if binary.Write(conn, binary.BigEndian, uint16(len(rb))) != nil { - return - } - - if _, err = conn.Write(rb); err != nil { - return - } - } - }() -} - -type dnsHandler struct{} - -func newDnsHandler() golwip.DnsHandler { - return &dnsHandler{} -} - -func (d dnsHandler) ResolveIP(host string) (net.IP, error) { - log.Debugln("[TUN] lwip resolve ip for host: %s", host) - return resolver.ResolveIP(host) -} diff --git a/listener/tun/ipstack/lwip/tcp.go b/listener/tun/ipstack/lwip/tcp.go deleted file mode 100644 index c62a6beb..00000000 --- a/listener/tun/ipstack/lwip/tcp.go +++ /dev/null @@ -1,61 +0,0 @@ -package lwip - -import ( - "net" - "strconv" - - C "github.com/Dreamacro/clash/constant" - "github.com/Dreamacro/clash/context" - "github.com/Dreamacro/clash/log" - "github.com/yaling888/go-lwip" -) - -type tcpHandler struct { - dnsIP net.IP - tcpIn chan<- C.ConnContext -} - -func newTCPHandler(dnsIP net.IP, tcpIn chan<- C.ConnContext) golwip.TCPConnHandler { - return &tcpHandler{dnsIP, tcpIn} -} - -func (h *tcpHandler) Handle(conn net.Conn, target *net.TCPAddr) error { - if shouldHijackDns(h.dnsIP, target.IP, target.Port) { - hijackTCPDns(conn) - log.Debugln("[TUN] hijack dns tcp: %s:%d", target.IP.String(), target.Port) - return nil - } - - if conn.RemoteAddr() == nil { - _ = conn.Close() - return nil - } - - src, _ := conn.LocalAddr().(*net.TCPAddr) - dst, _ := conn.RemoteAddr().(*net.TCPAddr) - - addrType := C.AtypIPv4 - if dst.IP.To4() == nil { - addrType = C.AtypIPv6 - } - - metadata := &C.Metadata{ - NetWork: C.TCP, - Type: C.TUN, - SrcIP: src.IP, - DstIP: dst.IP, - SrcPort: strconv.Itoa(src.Port), - DstPort: strconv.Itoa(dst.Port), - AddrType: addrType, - Host: "", - } - - go func(conn net.Conn, metadata *C.Metadata) { - //if c, ok := conn.(*net.TCPConn); ok { - // c.SetKeepAlive(true) - //} - h.tcpIn <- context.NewConnContext(conn, metadata) - }(conn, metadata) - - return nil -} diff --git a/listener/tun/ipstack/lwip/tun.go b/listener/tun/ipstack/lwip/tun.go deleted file mode 100644 index 9037be6e..00000000 --- a/listener/tun/ipstack/lwip/tun.go +++ /dev/null @@ -1,121 +0,0 @@ -package lwip - -import ( - "io" - "net" - "sync" - - "github.com/Dreamacro/clash/adapter/inbound" - "github.com/Dreamacro/clash/common/pool" - "github.com/Dreamacro/clash/config" - C "github.com/Dreamacro/clash/constant" - "github.com/Dreamacro/clash/listener/tun/dev" - "github.com/Dreamacro/clash/listener/tun/ipstack" - "github.com/Dreamacro/clash/log" - "github.com/yaling888/go-lwip" -) - -type lwipAdapter struct { - device dev.TunDevice - lwipStack golwip.LWIPStack - lock sync.Mutex - mtu int - stackName string - dnsListen string - autoRoute bool -} - -func NewAdapter(device dev.TunDevice, conf config.Tun, mtu int, tcpIn chan<- C.ConnContext, udpIn chan<- *inbound.PacketAdapter) (ipstack.TunAdapter, error) { - adapter := &lwipAdapter{ - device: device, - mtu: mtu, - stackName: conf.Stack, - dnsListen: conf.DNSListen, - autoRoute: conf.AutoRoute, - } - - adapter.lock.Lock() - defer adapter.lock.Unlock() - - dnsHost, _, err := net.SplitHostPort(conf.DNSListen) - if err != nil { - return nil, err - } - - dnsIP := net.ParseIP(dnsHost) - - // Register output function, write packets from lwip stack to tun device - golwip.RegisterOutputFn(func(data []byte) (int, error) { - return device.Write(data) - }) - - // Set custom buffer pool - golwip.SetPoolAllocator(newLWIPPool()) - - // Setup TCP/IP stack. - lwipStack, err := golwip.NewLWIPStack(mtu) - if err != nil { - return nil, err - } - adapter.lwipStack = lwipStack - - golwip.RegisterDnsHandler(newDnsHandler()) - golwip.RegisterTCPConnHandler(newTCPHandler(dnsIP, tcpIn)) - golwip.RegisterUDPConnHandler(newUDPHandler(dnsIP, udpIn)) - - // Copy packets from tun device to lwip stack, it's the loop. - go func(lwipStack golwip.LWIPStack, device dev.TunDevice, mtu int) { - _, err := io.CopyBuffer(lwipStack.(io.Writer), device, make([]byte, mtu)) - if err != nil { - log.Debugln("copying data failed: %v", err) - } - }(lwipStack, device, mtu) - - return adapter, nil -} - -func (l *lwipAdapter) Stack() string { - return l.stackName -} - -func (l *lwipAdapter) AutoRoute() bool { - return l.autoRoute -} - -func (l *lwipAdapter) DNSListen() string { - return l.dnsListen -} - -func (l *lwipAdapter) Close() { - l.lock.Lock() - defer l.lock.Unlock() - - l.stopLocked() -} - -func (l *lwipAdapter) stopLocked() { - if l.lwipStack != nil { - _ = l.lwipStack.Close() - } - - if l.device != nil { - _ = l.device.Close() - } - - l.lwipStack = nil - l.device = nil -} - -type lwipPool struct{} - -func (p lwipPool) Get(size int) []byte { - return pool.Get(size) -} - -func (p lwipPool) Put(buf []byte) error { - return pool.Put(buf) -} - -func newLWIPPool() golwip.LWIPPool { - return &lwipPool{} -} diff --git a/listener/tun/ipstack/lwip/udp.go b/listener/tun/ipstack/lwip/udp.go deleted file mode 100644 index 747796bf..00000000 --- a/listener/tun/ipstack/lwip/udp.go +++ /dev/null @@ -1,74 +0,0 @@ -package lwip - -import ( - "io" - "net" - - "github.com/Dreamacro/clash/adapter/inbound" - C "github.com/Dreamacro/clash/constant" - "github.com/Dreamacro/clash/log" - "github.com/Dreamacro/clash/transport/socks5" - "github.com/yaling888/go-lwip" -) - -type udpPacket struct { - source *net.UDPAddr - payload []byte - sender golwip.UDPConn -} - -func (u *udpPacket) Data() []byte { - return u.payload -} - -func (u *udpPacket) WriteBack(b []byte, addr net.Addr) (n int, err error) { - _, ok := addr.(*net.UDPAddr) - if !ok { - return 0, io.ErrClosedPipe - } - - return u.sender.WriteFrom(b, u.source) -} - -func (u *udpPacket) Drop() { -} - -func (u *udpPacket) LocalAddr() net.Addr { - return u.source -} - -type udpHandler struct { - dnsIP net.IP - udpIn chan<- *inbound.PacketAdapter -} - -func newUDPHandler(dnsIP net.IP, udpIn chan<- *inbound.PacketAdapter) golwip.UDPConnHandler { - return &udpHandler{dnsIP, udpIn} -} - -func (h *udpHandler) Connect(golwip.UDPConn, *net.UDPAddr) error { - return nil -} - -func (h *udpHandler) ReceiveTo(conn golwip.UDPConn, data []byte, addr *net.UDPAddr) error { - if shouldHijackDns(h.dnsIP, addr.IP, addr.Port) { - hijackUDPDns(conn, data, addr) - log.Debugln("[TUN] hijack dns udp: %s:%d", addr.IP.String(), addr.Port) - return nil - } - - packet := &udpPacket{ - source: conn.LocalAddr(), - payload: data, - sender: conn, - } - - go func(addr *net.UDPAddr, packet *udpPacket) { - select { - case h.udpIn <- inbound.NewPacket(socks5.ParseAddrToSocksAddr(addr), packet, C.TUN): - default: - } - }(addr, packet) - - return nil -} diff --git a/rule/common/base.go b/rule/common/base.go index 3e27ac24..118153d4 100644 --- a/rule/common/base.go +++ b/rule/common/base.go @@ -3,6 +3,7 @@ package common import ( "errors" "net" + "strings" C "github.com/Dreamacro/clash/constant" ) @@ -51,3 +52,17 @@ func FindSourceIPs(params []string) []*net.IPNet { } return nil } + +func FindProcessName(params []string) []string { + var processNames []string + for _, p := range params { + if strings.HasPrefix(p, "P:") { + processNames = append(processNames, strings.TrimPrefix(p, "P:")) + } + } + + if len(processNames) > 0 { + return processNames + } + return nil +} diff --git a/rule/geosite.go b/rule/geosite.go deleted file mode 100644 index 875320bd..00000000 --- a/rule/geosite.go +++ /dev/null @@ -1,70 +0,0 @@ -package rules - -import ( - "fmt" - - "github.com/Dreamacro/clash/component/geodata" - "github.com/Dreamacro/clash/component/geodata/router" - C "github.com/Dreamacro/clash/constant" - "github.com/Dreamacro/clash/log" - - _ "github.com/Dreamacro/clash/component/geodata/standard" -) - -type GEOSITE struct { - country string - adapter string - ruleExtra *C.RuleExtra - matcher *router.DomainMatcher -} - -func (gs *GEOSITE) RuleType() C.RuleType { - return C.GEOSITE -} - -func (gs *GEOSITE) Match(metadata *C.Metadata) bool { - if metadata.AddrType != C.AtypDomainName { - return false - } - - domain := metadata.Host - return gs.matcher.ApplyDomain(domain) -} - -func (gs *GEOSITE) Adapter() string { - return gs.adapter -} - -func (gs *GEOSITE) Payload() string { - return gs.country -} - -func (gs *GEOSITE) ShouldResolveIP() bool { - return false -} - -func (gs *GEOSITE) RuleExtra() *C.RuleExtra { - return gs.ruleExtra -} - -func (gs *GEOSITE) GetDomainMatcher() *router.DomainMatcher { - return gs.matcher -} - -func NewGEOSITE(country string, adapter string, ruleExtra *C.RuleExtra) (*GEOSITE, error) { - matcher, recordsCount, err := geodata.LoadGeoSiteMatcher(country) - if err != nil { - return nil, fmt.Errorf("load GeoSite data error, %s", err.Error()) - } - - log.Infoln("Start initial GeoSite rule %s => %s, records: %d", country, adapter, recordsCount) - - geoSite := &GEOSITE{ - country: country, - adapter: adapter, - ruleExtra: ruleExtra, - matcher: matcher, - } - - return geoSite, nil -} diff --git a/rule/port.go b/rule/port.go deleted file mode 100644 index e978e28d..00000000 --- a/rule/port.go +++ /dev/null @@ -1,125 +0,0 @@ -package rules - -import ( - "fmt" - "strconv" - "strings" - - C "github.com/Dreamacro/clash/constant" -) - -type portReal struct { - portStart int - portEnd int -} - -type Port struct { - adapter string - port string - isSource bool - portList []portReal - ruleExtra *C.RuleExtra -} - -func (p *Port) RuleType() C.RuleType { - if p.isSource { - return C.SrcPort - } - return C.DstPort -} - -func (p *Port) Match(metadata *C.Metadata) bool { - if p.isSource { - return p.matchPortReal(metadata.SrcPort) - } - return p.matchPortReal(metadata.DstPort) -} - -func (p *Port) Adapter() string { - return p.adapter -} - -func (p *Port) Payload() string { - return p.port -} - -func (p *Port) ShouldResolveIP() bool { - return false -} - -func (p *Port) RuleExtra() *C.RuleExtra { - return p.ruleExtra -} - -func (p *Port) matchPortReal(portRef string) bool { - port, err := strconv.Atoi(portRef) - if err != nil { - return false - } - - var rs bool - for _, pr := range p.portList { - if pr.portEnd == -1 { - rs = port == pr.portStart - } else { - rs = port >= pr.portStart && port <= pr.portEnd - } - if rs { - return true - } - } - return false -} - -func NewPort(port string, adapter string, isSource bool, ruleExtra *C.RuleExtra) (*Port, error) { - ports := strings.Split(port, "/") - if len(ports) > 28 { - return nil, fmt.Errorf("%s, too many ports to use, maximum support 28 ports", errPayload.Error()) - } - - var portList []portReal - for _, p := range ports { - if p == "" { - continue - } - - subPorts := strings.Split(p, "-") - subPortsLen := len(subPorts) - if subPortsLen > 2 { - return nil, errPayload - } - - portStart, err := strconv.Atoi(strings.Trim(subPorts[0], "[ ]")) - if err != nil || portStart < 0 || portStart > 65535 { - return nil, errPayload - } - - if subPortsLen == 1 { - portList = append(portList, portReal{portStart, -1}) - } else if subPortsLen == 2 { - portEnd, err1 := strconv.Atoi(strings.Trim(subPorts[1], "[ ]")) - if err1 != nil || portEnd < 0 || portEnd > 65535 { - return nil, errPayload - } - - shouldReverse := portStart > portEnd - if shouldReverse { - portList = append(portList, portReal{portEnd, portStart}) - } else { - portList = append(portList, portReal{portStart, portEnd}) - } - } - } - - if len(portList) == 0 { - return nil, errPayload - } - - return &Port{ - adapter: adapter, - port: port, - isSource: isSource, - portList: portList, - ruleExtra: ruleExtra, - }, nil -} diff --git a/rule/script.go b/rule/script.go deleted file mode 100644 index b1ccb5fb..00000000 --- a/rule/script.go +++ /dev/null @@ -1,73 +0,0 @@ -package rules - -import ( - "fmt" - "runtime" - "strings" - - S "github.com/Dreamacro/clash/component/script" - C "github.com/Dreamacro/clash/constant" - "github.com/Dreamacro/clash/log" -) - -type Script struct { - shortcut string - adapter string - shortcutFunction *S.PyObject -} - -func (s *Script) RuleType() C.RuleType { - return C.Script -} - -func (s *Script) Match(metadata *C.Metadata) bool { - rs, err := S.CallPyShortcut(s.shortcutFunction, metadata) - if err != nil { - log.Errorln("[Script] match rule error: %s", err.Error()) - return false - } - - return rs -} - -func (s *Script) Adapter() string { - return s.adapter -} - -func (s *Script) Payload() string { - return s.shortcut -} - -func (s *Script) ShouldResolveIP() bool { - return false -} - -func (s *Script) RuleExtra() *C.RuleExtra { - return nil -} - -func NewScript(shortcut string, adapter string) (*Script, error) { - shortcut = strings.ToLower(shortcut) - if !S.Py_IsInitialized() { - return nil, fmt.Errorf("load script shortcut [%s] failure, can't find any shortcuts in the config file", shortcut) - } - - shortcutFunction, err := S.LoadShortcutFunction(shortcut) - if err != nil { - return nil, fmt.Errorf("can't find script shortcut [%s] in the config file", shortcut) - } - - obj := &Script{ - shortcut: shortcut, - adapter: adapter, - shortcutFunction: shortcutFunction, - } - - runtime.SetFinalizer(obj, func(s *Script) { - s.shortcutFunction.Clear() - }) - - log.Infoln("Start initial script shortcut rule %s => %s", shortcut, adapter) - - return obj, nil -}