mirror of
https://github.com/springzfx/cgproxy.git
synced 2026-02-09 21:14:57 +08:00
Compare commits
11 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
1fa0d51e1d | ||
|
|
aedebf4e31 | ||
|
|
051742eef1 | ||
|
|
619fcaae8e | ||
|
|
78c10e3e3e | ||
|
|
99b29195f2 | ||
|
|
53fccbe72d | ||
|
|
db4757316a | ||
|
|
3a4e62b3c2 | ||
|
|
4525d83a53 | ||
|
|
c5ec1027ad |
1
.gitignore
vendored
1
.gitignore
vendored
@@ -1,6 +1,7 @@
|
|||||||
build
|
build
|
||||||
.directory
|
.directory
|
||||||
.vscode
|
.vscode
|
||||||
|
.clangd
|
||||||
v2ray_config/proxy
|
v2ray_config/proxy
|
||||||
v2ray_config/06_outbounds_proxy.json
|
v2ray_config/06_outbounds_proxy.json
|
||||||
aur-*
|
aur-*
|
||||||
|
|||||||
@@ -1,62 +1,27 @@
|
|||||||
cmake_minimum_required(VERSION 3.10)
|
cmake_minimum_required(VERSION 3.10)
|
||||||
set(CMAKE_CXX_STANDARD 17)
|
set(CMAKE_CXX_STANDARD 17)
|
||||||
set(CMAKE_CXX_STANDARD_REQUIRED ON)
|
set(CMAKE_CXX_STANDARD_REQUIRED ON)
|
||||||
set(CMAKE_BUILD_TYPE DEBUG)
|
|
||||||
# set(CMAKE_BUILD_TYPE RELEASE)
|
|
||||||
|
|
||||||
project(cgproxy VERSION 4.0)
|
project(cgproxy VERSION 0.12)
|
||||||
|
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wno-unused-result")
|
||||||
|
|
||||||
find_package(Threads REQUIRED)
|
set(build_tools OFF)
|
||||||
find_package(nlohmann_json REQUIRED)
|
set(build_test OFF)
|
||||||
include_directories(${PROJECT_SOURCE_DIR})
|
|
||||||
|
|
||||||
add_executable(cgattach cgattach.cpp)
|
|
||||||
add_executable(cgproxyd cgproxyd.cpp)
|
|
||||||
add_executable(cgproxy cgproxy.cpp)
|
|
||||||
add_executable(cgnoproxy cgnoproxy.cpp)
|
|
||||||
target_link_libraries(cgproxyd Threads::Threads nlohmann_json::nlohmann_json)
|
|
||||||
target_link_libraries(cgproxy nlohmann_json::nlohmann_json)
|
|
||||||
target_link_libraries(cgnoproxy nlohmann_json::nlohmann_json)
|
|
||||||
|
|
||||||
# add_executable(client_test socket_client_test.cpp)
|
|
||||||
# target_link_libraries(client_test nlohmann_json::nlohmann_json)
|
|
||||||
|
|
||||||
set(basic_permission OWNER_READ OWNER_WRITE OWNER_EXECUTE GROUP_READ GROUP_EXECUTE WORLD_READ WORLD_EXECUTE)
|
set(basic_permission OWNER_READ OWNER_WRITE OWNER_EXECUTE GROUP_READ GROUP_EXECUTE WORLD_READ WORLD_EXECUTE)
|
||||||
|
|
||||||
install(TARGETS cgattach DESTINATION /usr/bin PERMISSIONS ${basic_permission})
|
add_subdirectory(src)
|
||||||
install(TARGETS cgproxyd DESTINATION /usr/bin PERMISSIONS ${basic_permission})
|
add_subdirectory(pack)
|
||||||
install(TARGETS cgproxy DESTINATION /usr/bin PERMISSIONS ${basic_permission})
|
if (build_tools)
|
||||||
install(TARGETS cgnoproxy DESTINATION /usr/bin PERMISSIONS ${basic_permission})
|
add_subdirectory(tools)
|
||||||
|
endif()
|
||||||
|
if (build_test)
|
||||||
|
add_subdirectory(test)
|
||||||
|
endif()
|
||||||
|
|
||||||
|
install(FILES cgproxyd DESTINATION /usr/bin PERMISSIONS ${basic_permission})
|
||||||
|
install(FILES cgnoproxy DESTINATION /usr/bin PERMISSIONS ${basic_permission})
|
||||||
install(FILES cgproxy.service DESTINATION /usr/lib/systemd/system/)
|
install(FILES cgproxy.service DESTINATION /usr/lib/systemd/system/)
|
||||||
install(FILES config.json DESTINATION /etc/cgproxy/)
|
install(FILES config.json DESTINATION /etc/cgproxy/)
|
||||||
install(FILES cgroup-tproxy.sh DESTINATION /usr/share/cgproxy/scripts/)
|
install(FILES cgroup-tproxy.sh DESTINATION /usr/share/cgproxy/scripts/)
|
||||||
install(FILES readme.md DESTINATION /share/doc/cgproxy/)
|
install(FILES readme.md DESTINATION /usr/share/doc/cgproxy/)
|
||||||
|
|
||||||
|
|
||||||
## package for deb and rpm
|
|
||||||
set(CPACK_GENERATOR "DEB;RPM")
|
|
||||||
set(CPACK_PACKAGE_NAME "cgproxy")
|
|
||||||
set(CPACK_PACKAGE_DESCRIPTION_SUMMARY "cgproxy will transparent proxy anything running in specific cgroup.It aslo supports global transparent proxy and gateway proxy")
|
|
||||||
|
|
||||||
## deb pack
|
|
||||||
set(CPACK_DEBIAN_PACKAGE_NAME "cgproxy")
|
|
||||||
set(CPACK_DEBIAN_PACKAGE_ARCHITECTURE "x86_64")
|
|
||||||
set(CPACK_DEBIAN_PACKAGE_DEPENDS "systemd" "nlohmann-json3-dev")
|
|
||||||
set(CPACK_DEBIAN_PACKAGE_SECTION "network")
|
|
||||||
set(CPACK_DEBIAN_PACKAGE_PRIORITY "Optional")
|
|
||||||
set(CPACK_DEBIAN_PACKAGE_HOMEPAGE "https://github.com/springzfx/cgproxy")
|
|
||||||
set(CPACK_DEBIAN_PACKAGE_MAINTAINER "springzfx@gmail.com")
|
|
||||||
set(CONTROL_DIR ${CMAKE_SOURCE_DIR}/control)
|
|
||||||
set(CPACK_DEBIAN_PACKAGE_CONTROL_EXTRA "${CONTROL_DIR}/postinst;${CONTROL_DIR}/prerm")
|
|
||||||
|
|
||||||
## rpm pack
|
|
||||||
set(CPACK_RPM_PACKAGE_ARCHITECTURE, "x86_64")
|
|
||||||
set(CPACK_RPM_PACKAGE_REQUIRES "systemd" "json-devel")
|
|
||||||
set(CPACK_RPM_PACKAGE_GROUP "network")
|
|
||||||
set(CPACK_RPM_PACKAGE_URL "https://github.com/springzfx/cgproxy")
|
|
||||||
set(CONTROL_DIR ${CMAKE_SOURCE_DIR}/control)
|
|
||||||
set(CPACK_RPM_POST_INSTALL_SCRIPT_FILE "${CONTROL_DIR}/postinst")
|
|
||||||
set(CPACK_RPM_PRE_UNINSTALL_SCRIPT_FILE "${CONTROL_DIR}/prerm")
|
|
||||||
|
|
||||||
include(CPack)
|
|
||||||
|
|||||||
137
_clang-format
Normal file
137
_clang-format
Normal file
@@ -0,0 +1,137 @@
|
|||||||
|
---
|
||||||
|
Language: Cpp
|
||||||
|
# BasedOnStyle: LLVM
|
||||||
|
AccessModifierOffset: -2
|
||||||
|
AlignAfterOpenBracket: Align
|
||||||
|
AlignConsecutiveMacros: false
|
||||||
|
AlignConsecutiveAssignments: false
|
||||||
|
AlignConsecutiveDeclarations: false
|
||||||
|
AlignEscapedNewlines: Right
|
||||||
|
AlignOperands: true
|
||||||
|
AlignTrailingComments: true
|
||||||
|
AllowAllArgumentsOnNextLine: true
|
||||||
|
AllowAllConstructorInitializersOnNextLine: true
|
||||||
|
AllowAllParametersOfDeclarationOnNextLine: true
|
||||||
|
AllowShortBlocksOnASingleLine: Always
|
||||||
|
AllowShortCaseLabelsOnASingleLine: true
|
||||||
|
AllowShortFunctionsOnASingleLine: All
|
||||||
|
AllowShortLambdasOnASingleLine: All
|
||||||
|
AllowShortIfStatementsOnASingleLine: Always
|
||||||
|
AllowShortLoopsOnASingleLine: true
|
||||||
|
AlwaysBreakAfterDefinitionReturnType: None
|
||||||
|
AlwaysBreakAfterReturnType: None
|
||||||
|
AlwaysBreakBeforeMultilineStrings: false
|
||||||
|
AlwaysBreakTemplateDeclarations: MultiLine
|
||||||
|
BinPackArguments: true
|
||||||
|
BinPackParameters: true
|
||||||
|
BraceWrapping:
|
||||||
|
AfterCaseLabel: false
|
||||||
|
AfterClass: false
|
||||||
|
AfterControlStatement: false
|
||||||
|
AfterEnum: false
|
||||||
|
AfterFunction: false
|
||||||
|
AfterNamespace: false
|
||||||
|
AfterObjCDeclaration: false
|
||||||
|
AfterStruct: false
|
||||||
|
AfterUnion: false
|
||||||
|
AfterExternBlock: false
|
||||||
|
BeforeCatch: false
|
||||||
|
BeforeElse: false
|
||||||
|
IndentBraces: false
|
||||||
|
SplitEmptyFunction: true
|
||||||
|
SplitEmptyRecord: true
|
||||||
|
SplitEmptyNamespace: true
|
||||||
|
BreakBeforeBinaryOperators: None
|
||||||
|
BreakBeforeBraces: Attach
|
||||||
|
BreakBeforeInheritanceComma: false
|
||||||
|
BreakInheritanceList: BeforeColon
|
||||||
|
BreakBeforeTernaryOperators: true
|
||||||
|
BreakConstructorInitializersBeforeComma: false
|
||||||
|
BreakConstructorInitializers: BeforeColon
|
||||||
|
BreakAfterJavaFieldAnnotations: false
|
||||||
|
BreakStringLiterals: true
|
||||||
|
ColumnLimit: 90
|
||||||
|
CommentPragmas: '^ IWYU pragma:'
|
||||||
|
CompactNamespaces: false
|
||||||
|
ConstructorInitializerAllOnOneLineOrOnePerLine: false
|
||||||
|
ConstructorInitializerIndentWidth: 4
|
||||||
|
ContinuationIndentWidth: 4
|
||||||
|
Cpp11BracedListStyle: true
|
||||||
|
DeriveLineEnding: true
|
||||||
|
DerivePointerAlignment: false
|
||||||
|
DisableFormat: false
|
||||||
|
ExperimentalAutoDetectBinPacking: false
|
||||||
|
FixNamespaceComments: true
|
||||||
|
ForEachMacros:
|
||||||
|
- foreach
|
||||||
|
- Q_FOREACH
|
||||||
|
- BOOST_FOREACH
|
||||||
|
IncludeBlocks: Preserve
|
||||||
|
IncludeCategories:
|
||||||
|
- Regex: '^"(llvm|llvm-c|clang|clang-c)/'
|
||||||
|
Priority: 2
|
||||||
|
SortPriority: 0
|
||||||
|
- Regex: '^(<|"(gtest|gmock|isl|json)/)'
|
||||||
|
Priority: 3
|
||||||
|
SortPriority: 0
|
||||||
|
- Regex: '.*'
|
||||||
|
Priority: 1
|
||||||
|
SortPriority: 0
|
||||||
|
IncludeIsMainRegex: '(Test)?$'
|
||||||
|
IncludeIsMainSourceRegex: ''
|
||||||
|
IndentCaseLabels: false
|
||||||
|
IndentGotoLabels: true
|
||||||
|
IndentPPDirectives: None
|
||||||
|
IndentWidth: 2
|
||||||
|
IndentWrappedFunctionNames: false
|
||||||
|
JavaScriptQuotes: Leave
|
||||||
|
JavaScriptWrapImports: true
|
||||||
|
KeepEmptyLinesAtTheStartOfBlocks: true
|
||||||
|
MacroBlockBegin: ''
|
||||||
|
MacroBlockEnd: ''
|
||||||
|
MaxEmptyLinesToKeep: 1
|
||||||
|
NamespaceIndentation: None
|
||||||
|
ObjCBinPackProtocolList: Auto
|
||||||
|
ObjCBlockIndentWidth: 2
|
||||||
|
ObjCSpaceAfterProperty: false
|
||||||
|
ObjCSpaceBeforeProtocolList: true
|
||||||
|
PenaltyBreakAssignment: 2
|
||||||
|
PenaltyBreakBeforeFirstCallParameter: 19
|
||||||
|
PenaltyBreakComment: 300
|
||||||
|
PenaltyBreakFirstLessLess: 120
|
||||||
|
PenaltyBreakString: 1000
|
||||||
|
PenaltyBreakTemplateDeclaration: 10
|
||||||
|
PenaltyExcessCharacter: 1000000
|
||||||
|
PenaltyReturnTypeOnItsOwnLine: 60
|
||||||
|
PointerAlignment: Right
|
||||||
|
ReflowComments: true
|
||||||
|
SortIncludes: true
|
||||||
|
SortUsingDeclarations: true
|
||||||
|
SpaceAfterCStyleCast: false
|
||||||
|
SpaceAfterLogicalNot: false
|
||||||
|
SpaceAfterTemplateKeyword: true
|
||||||
|
SpaceBeforeAssignmentOperators: true
|
||||||
|
SpaceBeforeCpp11BracedList: false
|
||||||
|
SpaceBeforeCtorInitializerColon: true
|
||||||
|
SpaceBeforeInheritanceColon: true
|
||||||
|
SpaceBeforeParens: ControlStatements
|
||||||
|
SpaceBeforeRangeBasedForLoopColon: true
|
||||||
|
SpaceInEmptyBlock: false
|
||||||
|
SpaceInEmptyParentheses: false
|
||||||
|
SpacesBeforeTrailingComments: 1
|
||||||
|
SpacesInAngles: false
|
||||||
|
SpacesInConditionalStatement: false
|
||||||
|
SpacesInContainerLiterals: true
|
||||||
|
SpacesInCStyleCastParentheses: false
|
||||||
|
SpacesInParentheses: false
|
||||||
|
SpacesInSquareBrackets: false
|
||||||
|
SpaceBeforeSquareBrackets: false
|
||||||
|
Standard: Latest
|
||||||
|
StatementMacros:
|
||||||
|
- Q_UNUSED
|
||||||
|
- QT_REQUIRE_VERSION
|
||||||
|
TabWidth: 8
|
||||||
|
UseCRLF: false
|
||||||
|
UseTab: Never
|
||||||
|
...
|
||||||
|
|
||||||
@@ -1,31 +0,0 @@
|
|||||||
#include <nlohmann/json.hpp>
|
|
||||||
#include "socket_client.hpp"
|
|
||||||
using json = nlohmann::json;
|
|
||||||
using namespace CGPROXY;
|
|
||||||
|
|
||||||
bool attach2cgproxy(){
|
|
||||||
pid_t pid=getpid();
|
|
||||||
json j;
|
|
||||||
j["type"] = MSG_TYPE_NOPROXY_PID;
|
|
||||||
j["data"] = pid;
|
|
||||||
int status;
|
|
||||||
SOCKET::send(j.dump(), status);
|
|
||||||
return status==0;
|
|
||||||
}
|
|
||||||
|
|
||||||
int main(int argc, char *argv[]){
|
|
||||||
int shift=1;
|
|
||||||
if (argc==1){
|
|
||||||
error("usage: cgnoproxy [--debug] <CMD>\nexample: cgnoproxy curl -I https://www.google.com");
|
|
||||||
exit(EXIT_FAILURE);
|
|
||||||
}
|
|
||||||
processArgs(argc,argv,shift);
|
|
||||||
|
|
||||||
if (!attach2cgproxy()){
|
|
||||||
error("attach process failed");
|
|
||||||
exit(EXIT_FAILURE);
|
|
||||||
}
|
|
||||||
|
|
||||||
string s=join2str(argc-shift,argv+shift,' ');
|
|
||||||
return system(s.c_str());
|
|
||||||
}
|
|
||||||
31
cgproxy.cpp
31
cgproxy.cpp
@@ -1,31 +0,0 @@
|
|||||||
#include <nlohmann/json.hpp>
|
|
||||||
#include "socket_client.hpp"
|
|
||||||
using json = nlohmann::json;
|
|
||||||
using namespace CGPROXY;
|
|
||||||
|
|
||||||
bool attach2cgproxy(){
|
|
||||||
pid_t pid=getpid();
|
|
||||||
json j;
|
|
||||||
j["type"] = MSG_TYPE_PROXY_PID;
|
|
||||||
j["data"] = pid;
|
|
||||||
int status;
|
|
||||||
SOCKET::send(j.dump(), status);
|
|
||||||
return status==0;
|
|
||||||
}
|
|
||||||
|
|
||||||
int main(int argc, char *argv[]){
|
|
||||||
int shift=1;
|
|
||||||
if (argc==1){
|
|
||||||
error("usage: cgproxy [--debug] <CMD>\nexample: cgroxy curl -I https://www.google.com");
|
|
||||||
exit(EXIT_FAILURE);
|
|
||||||
}
|
|
||||||
processArgs(argc,argv,shift);
|
|
||||||
|
|
||||||
if (!attach2cgproxy()){
|
|
||||||
error("attach process failed");
|
|
||||||
exit(EXIT_FAILURE);
|
|
||||||
}
|
|
||||||
|
|
||||||
string s=join2str(argc-shift,argv+shift,' ');
|
|
||||||
return system(s.c_str());
|
|
||||||
}
|
|
||||||
140
cgproxyd.cpp
140
cgproxyd.cpp
@@ -1,140 +0,0 @@
|
|||||||
#include "common.hpp"
|
|
||||||
#include "socket_server.hpp"
|
|
||||||
#include <fstream>
|
|
||||||
#include <iostream>
|
|
||||||
#include <libconfig.h++>
|
|
||||||
#include <nlohmann/json.hpp>
|
|
||||||
#include <pthread.h>
|
|
||||||
#include <sstream>
|
|
||||||
#include <stdio.h>
|
|
||||||
#include <stdlib.h>
|
|
||||||
#include <string>
|
|
||||||
#include <csignal>
|
|
||||||
#include "config.hpp"
|
|
||||||
#include "cgroup_attach.hpp"
|
|
||||||
|
|
||||||
using namespace std;
|
|
||||||
using json = nlohmann::json;
|
|
||||||
using namespace CGPROXY::SOCKET;
|
|
||||||
using namespace CGPROXY::CONFIG;
|
|
||||||
using namespace CGPROXY::CGROUP;
|
|
||||||
|
|
||||||
namespace CGPROXY{
|
|
||||||
|
|
||||||
class cgproxyd{
|
|
||||||
thread_arg arg_t;
|
|
||||||
Config config;
|
|
||||||
pthread_t socket_thread_id = -1;
|
|
||||||
|
|
||||||
static cgproxyd* instance;
|
|
||||||
static int handle_msg_static(char* msg){
|
|
||||||
if (!instance) {
|
|
||||||
error("no cgproxyd instance assigned");
|
|
||||||
return ERROR;
|
|
||||||
}
|
|
||||||
return instance->handle_msg(msg);
|
|
||||||
}
|
|
||||||
static void signalHandler( int signum ){
|
|
||||||
debug("Signal %d received.", &signum);
|
|
||||||
if (!instance){ error("no cgproxyd instance assigned");}
|
|
||||||
else { instance->stop(); }
|
|
||||||
exit(signum);
|
|
||||||
}
|
|
||||||
|
|
||||||
int handle_msg(char *msg) {
|
|
||||||
debug("received msg: %s", msg);
|
|
||||||
json j;
|
|
||||||
try{ j = json::parse(msg); }catch(exception& e){debug("msg paser error");return MSG_ERROR;}
|
|
||||||
|
|
||||||
int type, status;
|
|
||||||
int pid, cgroup_target;
|
|
||||||
try {
|
|
||||||
type = j.at("type").get<int>();
|
|
||||||
switch (type)
|
|
||||||
{
|
|
||||||
case MSG_TYPE_JSON:
|
|
||||||
status=config.loadFromJson(j.at("data"));
|
|
||||||
if (status==SUCCESS) status=applyConfig(&config);
|
|
||||||
return status;
|
|
||||||
break;
|
|
||||||
case MSG_TYPE_CONFIG_PATH:
|
|
||||||
status=config.loadFromFile(j.at("data").get<string>());
|
|
||||||
if (status==SUCCESS) status=applyConfig(&config);
|
|
||||||
return status;
|
|
||||||
break;
|
|
||||||
case MSG_TYPE_PROXY_PID:
|
|
||||||
pid=j.at("data").get<int>();
|
|
||||||
status=attach(pid, config.cgroup_proxy_preserved);
|
|
||||||
return status;
|
|
||||||
break;
|
|
||||||
case MSG_TYPE_NOPROXY_PID:
|
|
||||||
pid=j.at("data").get<int>();
|
|
||||||
status=attach(pid, config.cgroup_noproxy_preserved);
|
|
||||||
return status;
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
return MSG_ERROR;
|
|
||||||
break;
|
|
||||||
};
|
|
||||||
} catch (out_of_range &e) {
|
|
||||||
return MSG_ERROR;
|
|
||||||
} catch (exception &e){
|
|
||||||
return ERROR;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pthread_t startSocketListeningThread() {
|
|
||||||
arg_t.handle_msg = &handle_msg_static;
|
|
||||||
pthread_t thread_id;
|
|
||||||
int status =
|
|
||||||
pthread_create(&thread_id, NULL, &SocketServer::startThread, &arg_t);
|
|
||||||
if (status != 0)
|
|
||||||
error("socket thread create failed");
|
|
||||||
return thread_id;
|
|
||||||
}
|
|
||||||
|
|
||||||
void assignStaticInstance(){
|
|
||||||
instance=this;
|
|
||||||
}
|
|
||||||
|
|
||||||
public:
|
|
||||||
int start(int argc, char* argv[]) {
|
|
||||||
signal(SIGINT, &signalHandler);
|
|
||||||
signal(SIGTERM,&signalHandler);
|
|
||||||
signal(SIGHUP,&signalHandler);
|
|
||||||
|
|
||||||
int shift=1;
|
|
||||||
processArgs(argc,argv,shift);
|
|
||||||
|
|
||||||
config.loadFromFile(DEFAULT_CONFIG_FILE);
|
|
||||||
applyConfig(&config);
|
|
||||||
|
|
||||||
assignStaticInstance();
|
|
||||||
socket_thread_id = startSocketListeningThread();
|
|
||||||
pthread_join(socket_thread_id, NULL);
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
int applyConfig(Config *c) {
|
|
||||||
system(TPROXY_IPTABLS_CLEAN);
|
|
||||||
c->toEnv();
|
|
||||||
system(TPROXY_IPTABLS_START);
|
|
||||||
// no need to track running status
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
void stop(){
|
|
||||||
debug("stopping");
|
|
||||||
system(TPROXY_IPTABLS_CLEAN);
|
|
||||||
}
|
|
||||||
~cgproxyd(){
|
|
||||||
stop();
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
cgproxyd* cgproxyd::instance=NULL;
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
int main(int argc, char* argv[]) {
|
|
||||||
CGPROXY::cgproxyd d;
|
|
||||||
return d.start(argc,argv);
|
|
||||||
}
|
|
||||||
107
common.hpp
107
common.hpp
@@ -1,107 +0,0 @@
|
|||||||
#ifndef COMMON_H
|
|
||||||
#define COMMON_H 1
|
|
||||||
|
|
||||||
#define TPROXY_IPTABLS_START "sh /usr/share/cgproxy/scripts/cgroup-tproxy.sh"
|
|
||||||
#define TPROXY_IPTABLS_CLEAN "sh /usr/share/cgproxy/scripts/cgroup-tproxy.sh stop"
|
|
||||||
|
|
||||||
#define SOCKET_PATH "/tmp/cgproxy_unix_socket"
|
|
||||||
#define LISTEN_BACKLOG 64
|
|
||||||
#define DEFAULT_CONFIG_FILE "/etc/cgproxy/config.json"
|
|
||||||
|
|
||||||
#define CGROUP_PROXY_PRESVERED "/proxy.slice"
|
|
||||||
#define CGROUP_NOPROXY_PRESVERED "/noproxy.slice"
|
|
||||||
|
|
||||||
#define MSG_TYPE_JSON 1
|
|
||||||
#define MSG_TYPE_CONFIG_PATH 2
|
|
||||||
#define MSG_TYPE_PROXY_PID 3
|
|
||||||
#define MSG_TYPE_NOPROXY_PID 4
|
|
||||||
|
|
||||||
#define UNKNOWN_ERROR 99
|
|
||||||
#define ERROR -1
|
|
||||||
#define SUCCESS 0
|
|
||||||
#define CONN_ERROR 1
|
|
||||||
#define MSG_ERROR 2
|
|
||||||
#define PARSE_ERROR 3
|
|
||||||
#define PARAM_ERROR 4
|
|
||||||
#define APPLY_ERROR 5
|
|
||||||
#define CGROUP_ERROR 6
|
|
||||||
#define FILE_ERROR 7
|
|
||||||
|
|
||||||
|
|
||||||
#include <iostream>
|
|
||||||
#include <sstream>
|
|
||||||
#include <string>
|
|
||||||
#include <regex>
|
|
||||||
using namespace std;
|
|
||||||
|
|
||||||
static bool enable_debug=false;
|
|
||||||
static bool print_help=false;
|
|
||||||
|
|
||||||
#define error(...) {fprintf(stderr, __VA_ARGS__);fprintf(stderr, "\n");}
|
|
||||||
#define debug(...) if (enable_debug) {fprintf(stdout, __VA_ARGS__);fprintf(stdout, "\n");}
|
|
||||||
#define return_error return -1;
|
|
||||||
#define return_success return 0;
|
|
||||||
|
|
||||||
|
|
||||||
void processArgs(const int argc, char *argv[], int &shift){
|
|
||||||
for (int i=1;i<argc;i++){
|
|
||||||
if (strcmp(argv[i], "--debug")==0){
|
|
||||||
enable_debug=true;
|
|
||||||
}
|
|
||||||
if (strcmp(argv[i], "--help")==0){
|
|
||||||
print_help=true;
|
|
||||||
}
|
|
||||||
if (argv[i][0]!='-') {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
shift+=1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
template <typename... T>
|
|
||||||
string to_str(T... args) {
|
|
||||||
stringstream ss;
|
|
||||||
ss.clear();
|
|
||||||
ss << std::boolalpha;
|
|
||||||
(ss << ... << args);
|
|
||||||
return ss.str();
|
|
||||||
}
|
|
||||||
|
|
||||||
string join2str(const vector<string> t, const char delm=' '){
|
|
||||||
string s;
|
|
||||||
for (const auto &e : t)
|
|
||||||
e!=*(t.end()-1)?s+=e+delm:s+=e;
|
|
||||||
return s;
|
|
||||||
}
|
|
||||||
|
|
||||||
string join2str(const int argc,char** argv, const char delm=' '){
|
|
||||||
string s;
|
|
||||||
for (int i=0;i<argc;i++){
|
|
||||||
s+=argv[i];
|
|
||||||
if (i!=argc-1) s+=delm;
|
|
||||||
}
|
|
||||||
return s;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool validCgroup(const string cgroup){
|
|
||||||
return regex_match(cgroup, regex("^/[a-zA-Z0-9\\-_./@]*$"));
|
|
||||||
}
|
|
||||||
|
|
||||||
bool validCgroup(const vector<string> cgroup){
|
|
||||||
for (auto &e:cgroup){
|
|
||||||
if (!regex_match(e, regex("^/[a-zA-Z0-9\\-_./@]*$"))){
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool validPid(const string pid){
|
|
||||||
return regex_match(pid, regex("^[0-9]+$"));
|
|
||||||
}
|
|
||||||
|
|
||||||
bool validPort(const int port){
|
|
||||||
return port>0;
|
|
||||||
}
|
|
||||||
|
|
||||||
#endif
|
|
||||||
143
config.hpp
143
config.hpp
@@ -1,143 +0,0 @@
|
|||||||
#ifndef CONFIG_H
|
|
||||||
#define CONFIG_H
|
|
||||||
#include "common.hpp"
|
|
||||||
#include "socket_server.hpp"
|
|
||||||
#include <fstream>
|
|
||||||
#include <iostream>
|
|
||||||
#include <nlohmann/json.hpp>
|
|
||||||
#include <pthread.h>
|
|
||||||
#include <sstream>
|
|
||||||
#include <stdio.h>
|
|
||||||
#include <stdlib.h>
|
|
||||||
#include <string>
|
|
||||||
#include <vector>
|
|
||||||
#include <set>
|
|
||||||
#include <iomanip>
|
|
||||||
using namespace std;
|
|
||||||
using json = nlohmann::json;
|
|
||||||
|
|
||||||
namespace CGPROXY::CONFIG{
|
|
||||||
|
|
||||||
struct Config {
|
|
||||||
public:
|
|
||||||
const string cgroup_proxy_preserved=CGROUP_PROXY_PRESVERED;
|
|
||||||
const string cgroup_noproxy_preserved=CGROUP_NOPROXY_PRESVERED;
|
|
||||||
private:
|
|
||||||
vector<string> cgroup_proxy;
|
|
||||||
vector<string> cgroup_noproxy;
|
|
||||||
bool enable_gateway = false;
|
|
||||||
int port = 12345;
|
|
||||||
bool enable_dns = true;
|
|
||||||
bool enable_tcp = true;
|
|
||||||
bool enable_udp = true;
|
|
||||||
bool enable_ipv4 = true;
|
|
||||||
bool enable_ipv6 = true;
|
|
||||||
|
|
||||||
public:
|
|
||||||
void toEnv() {
|
|
||||||
mergeReserved();
|
|
||||||
setenv("cgroup_proxy", join2str(cgroup_proxy,':').c_str(), 1);
|
|
||||||
setenv("cgroup_noproxy", join2str(cgroup_noproxy, ':').c_str(), 1);
|
|
||||||
setenv("enable_gateway", to_str(enable_gateway).c_str(), 1);
|
|
||||||
setenv("port", to_str(port).c_str(), 1);
|
|
||||||
setenv("enable_dns", to_str(enable_dns).c_str(), 1);
|
|
||||||
setenv("enable_tcp", to_str(enable_tcp).c_str(), 1);
|
|
||||||
setenv("enable_udp", to_str(enable_udp).c_str(), 1);
|
|
||||||
setenv("enable_ipv4", to_str(enable_ipv4).c_str(), 1);
|
|
||||||
setenv("enable_ipv6", to_str(enable_ipv6).c_str(), 1);
|
|
||||||
}
|
|
||||||
|
|
||||||
int saveToFile(const string f){
|
|
||||||
ofstream o(f);
|
|
||||||
if (!o.is_open()) return FILE_ERROR;
|
|
||||||
json j=toJson();
|
|
||||||
o << setw(4) << j << endl;
|
|
||||||
o.close();
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
json toJson(){
|
|
||||||
json j;
|
|
||||||
#define add2json(v) j[#v]=v;
|
|
||||||
add2json(cgroup_proxy);
|
|
||||||
add2json(cgroup_noproxy);
|
|
||||||
add2json(enable_gateway);
|
|
||||||
add2json(port);
|
|
||||||
add2json(enable_dns);
|
|
||||||
add2json(enable_tcp);
|
|
||||||
add2json(enable_udp);
|
|
||||||
add2json(enable_ipv4);
|
|
||||||
add2json(enable_ipv6);
|
|
||||||
#undef add2json
|
|
||||||
return j;
|
|
||||||
}
|
|
||||||
|
|
||||||
int loadFromFile(const string f) {
|
|
||||||
debug("loading config: %s", f.c_str());
|
|
||||||
ifstream ifs(f);
|
|
||||||
if (ifs.is_open()){
|
|
||||||
json j;
|
|
||||||
try { ifs >> j; }catch (exception& e){error("parse error: %s", f.c_str());ifs.close();return PARSE_ERROR;}
|
|
||||||
ifs.close();
|
|
||||||
return loadFromJson(j);
|
|
||||||
}else{
|
|
||||||
error("open failed: %s",f.c_str());
|
|
||||||
return FILE_ERROR;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
int loadFromJson(const json &j) {
|
|
||||||
if (!validateJson(j)) {error("json validate fail"); return PARAM_ERROR;}
|
|
||||||
#define tryassign(v) try{j.at(#v).get_to(v);}catch(exception &e){}
|
|
||||||
tryassign(cgroup_proxy);
|
|
||||||
tryassign(cgroup_noproxy);
|
|
||||||
tryassign(enable_gateway);
|
|
||||||
tryassign(port);
|
|
||||||
tryassign(enable_dns);
|
|
||||||
tryassign(enable_tcp);
|
|
||||||
tryassign(enable_udp);
|
|
||||||
tryassign(enable_ipv4);
|
|
||||||
tryassign(enable_ipv6);
|
|
||||||
#undef assign
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
void mergeReserved(){
|
|
||||||
#define merge(v) { \
|
|
||||||
v.erase(std::remove(v.begin(), v.end(), v ## _preserved), v.end()); \
|
|
||||||
v.insert(v.begin(), v ## _preserved); \
|
|
||||||
}
|
|
||||||
merge(cgroup_proxy);
|
|
||||||
merge(cgroup_noproxy);
|
|
||||||
#undef merge
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
bool validateJson(const json &j){
|
|
||||||
bool status=true;
|
|
||||||
const set<string> boolset={"enable_gateway","enable_dns","enable_tcp","enable_udp","enable_ipv4","enable_ipv6"};
|
|
||||||
for (auto& [key, value] : j.items()) {
|
|
||||||
if (key=="cgroup_proxy"||key=="cgroup_noproxy"){
|
|
||||||
if (value.is_string()&&!validCgroup((string)value)) status=false;
|
|
||||||
// TODO what if vector<int> etc.
|
|
||||||
if (value.is_array()&&!validCgroup((vector<string>)value)) status=false;
|
|
||||||
if (!value.is_string()&&!value.is_array()) status=false;
|
|
||||||
}else if (key=="port"){
|
|
||||||
if (!validPort(value)) status=false;
|
|
||||||
}else if (boolset.find(key)!=boolset.end()){
|
|
||||||
if (!value.is_boolean()) status=false;
|
|
||||||
}else{
|
|
||||||
error("unknown key: %s", key.c_str());
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
if (!status) {
|
|
||||||
error("invalid value for key: %s", key.c_str());
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
24
pack/CMakeLists.txt
Normal file
24
pack/CMakeLists.txt
Normal file
@@ -0,0 +1,24 @@
|
|||||||
|
## package for deb and rpm
|
||||||
|
set(CPACK_GENERATOR "DEB;RPM")
|
||||||
|
set(CPACK_PACKAGE_NAME "cgproxy")
|
||||||
|
set(CPACK_PACKAGE_DESCRIPTION_SUMMARY "cgproxy will transparent proxy anything running in specific cgroup.It aslo supports global transparent proxy and gateway proxy")
|
||||||
|
|
||||||
|
## deb pack
|
||||||
|
set(CPACK_DEBIAN_PACKAGE_NAME "cgproxy")
|
||||||
|
set(CPACK_DEBIAN_PACKAGE_ARCHITECTURE "x86_64")
|
||||||
|
set(CPACK_DEBIAN_PACKAGE_DEPENDS "systemd")
|
||||||
|
set(CPACK_DEBIAN_PACKAGE_SECTION "network")
|
||||||
|
set(CPACK_DEBIAN_PACKAGE_PRIORITY "Optional")
|
||||||
|
set(CPACK_DEBIAN_PACKAGE_HOMEPAGE "https://github.com/springzfx/cgproxy")
|
||||||
|
set(CPACK_DEBIAN_PACKAGE_MAINTAINER "springzfx@gmail.com")
|
||||||
|
set(CPACK_DEBIAN_PACKAGE_CONTROL_EXTRA "${CMAKE_CURRENT_SOURCE_DIR}/postinst;${CMAKE_CURRENT_SOURCE_DIR}/prerm")
|
||||||
|
|
||||||
|
## rpm pack
|
||||||
|
set(CPACK_RPM_PACKAGE_ARCHITECTURE, "x86_64")
|
||||||
|
set(CPACK_RPM_PACKAGE_REQUIRES "systemd")
|
||||||
|
set(CPACK_RPM_PACKAGE_GROUP "network")
|
||||||
|
set(CPACK_RPM_PACKAGE_URL "https://github.com/springzfx/cgproxy")
|
||||||
|
set(CPACK_RPM_POST_INSTALL_SCRIPT_FILE "${CMAKE_CURRENT_SOURCE_DIR}/postinst")
|
||||||
|
set(CPACK_RPM_PRE_UNINSTALL_SCRIPT_FILE "${CMAKE_CURRENT_SOURCE_DIR}/prerm")
|
||||||
|
|
||||||
|
include(CPack)
|
||||||
@@ -67,7 +67,7 @@ mkdir build && cd build && cmake .. && make && make install
|
|||||||
- For example, test proxy
|
- For example, test proxy
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
cgproxy curl -I https://www.google.com
|
cgproxy curl -vI https://www.google.com
|
||||||
```
|
```
|
||||||
|
|
||||||
- To completely stop
|
- To completely stop
|
||||||
@@ -153,7 +153,7 @@ sudo systemctl restart cgproxy.service
|
|||||||
|
|
||||||
## NOTES
|
## NOTES
|
||||||
|
|
||||||
- v2ray TPROXY need root or special permission
|
- v2ray TPROXY need root or special permission, use [service](https://github.com/springzfx/cgproxy/blob/v3.x/v2ray_config/v2ray.service) or
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
sudo setcap "cap_net_admin,cap_net_bind_service=ep" /usr/lib/v2ray/v2ray
|
sudo setcap "cap_net_admin,cap_net_bind_service=ep" /usr/lib/v2ray/v2ray
|
||||||
|
|||||||
@@ -1,94 +0,0 @@
|
|||||||
#ifndef SOCKET_SERVER_H
|
|
||||||
#define SOCKET_SERVER_H
|
|
||||||
|
|
||||||
#include <functional>
|
|
||||||
#include <iostream>
|
|
||||||
#include <signal.h>
|
|
||||||
#include <stdio.h>
|
|
||||||
#include <stdlib.h>
|
|
||||||
#include <string>
|
|
||||||
#include <sys/socket.h>
|
|
||||||
#include <sys/types.h>
|
|
||||||
#include <sys/un.h>
|
|
||||||
#include <unistd.h>
|
|
||||||
#include <sys/stat.h>
|
|
||||||
#include <filesystem>
|
|
||||||
#include "common.hpp"
|
|
||||||
using namespace std;
|
|
||||||
namespace fs = std::filesystem;
|
|
||||||
|
|
||||||
namespace CGPROXY::SOCKET{
|
|
||||||
|
|
||||||
#define continue_if_error(flag, msg) \
|
|
||||||
if (flag == -1) { \
|
|
||||||
perror(msg); \
|
|
||||||
continue; \
|
|
||||||
}
|
|
||||||
|
|
||||||
struct thread_arg {
|
|
||||||
function<int(char *)> handle_msg;
|
|
||||||
};
|
|
||||||
|
|
||||||
class SocketServer {
|
|
||||||
public:
|
|
||||||
int sfd = -1, cfd = -1, flag = -1;
|
|
||||||
struct sockaddr_un unix_socket;
|
|
||||||
|
|
||||||
void socketListening(function<int(char *)> callback) {
|
|
||||||
debug("starting socket listening");
|
|
||||||
sfd = socket(AF_UNIX, SOCK_STREAM, 0);
|
|
||||||
|
|
||||||
if (fs::exists(SOCKET_PATH)&&unlink(SOCKET_PATH)==-1){
|
|
||||||
error("%s exist, and can't unlink",SOCKET_PATH);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
memset(&unix_socket, '\0', sizeof(struct sockaddr_un));
|
|
||||||
unix_socket.sun_family = AF_UNIX;
|
|
||||||
strncpy(unix_socket.sun_path, SOCKET_PATH,
|
|
||||||
sizeof(unix_socket.sun_path) - 1);
|
|
||||||
|
|
||||||
bind(sfd, (struct sockaddr *)&unix_socket, sizeof(struct sockaddr_un));
|
|
||||||
|
|
||||||
listen(sfd, LISTEN_BACKLOG);
|
|
||||||
chmod(SOCKET_PATH,S_IRWXU|S_IRWXG|S_IRWXO);
|
|
||||||
|
|
||||||
while (true) {
|
|
||||||
close(cfd);
|
|
||||||
cfd = accept(sfd, NULL, NULL);
|
|
||||||
continue_if_error(cfd, "accept");
|
|
||||||
debug("accept connection: %d", cfd);
|
|
||||||
|
|
||||||
// read length
|
|
||||||
int msg_len;
|
|
||||||
flag = read(cfd, &msg_len, sizeof(int));
|
|
||||||
continue_if_error(flag, "read length");
|
|
||||||
// read msg
|
|
||||||
char msg[msg_len];
|
|
||||||
flag = read(cfd, msg, msg_len * sizeof(char));
|
|
||||||
continue_if_error(flag, "read msg");
|
|
||||||
msg[msg_len]='\0';
|
|
||||||
// handle msg
|
|
||||||
int status = callback(msg);
|
|
||||||
// send back flag
|
|
||||||
flag = write(cfd, &status, sizeof(int));
|
|
||||||
continue_if_error(flag, "write back");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
~SocketServer() {
|
|
||||||
close(sfd);
|
|
||||||
close(cfd);
|
|
||||||
unlink(SOCKET_PATH);
|
|
||||||
}
|
|
||||||
|
|
||||||
static void *startThread(void *arg) {
|
|
||||||
thread_arg *p = (thread_arg *)arg;
|
|
||||||
SocketServer server;
|
|
||||||
server.socketListening(p->handle_msg);
|
|
||||||
return (void *)0;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
#endif
|
|
||||||
14
src/CMakeLists.txt
Normal file
14
src/CMakeLists.txt
Normal file
@@ -0,0 +1,14 @@
|
|||||||
|
find_package(Threads REQUIRED)
|
||||||
|
find_package(nlohmann_json REQUIRED)
|
||||||
|
include_directories(${PROJECT_SOURCE_DIR})
|
||||||
|
include_directories(${CMAKE_CURRENT_SOURCE_DIR})
|
||||||
|
|
||||||
|
add_executable(main main.cpp
|
||||||
|
common.cpp config.cpp cgroup_attach.cpp
|
||||||
|
socket_client.cpp socket_server.cpp)
|
||||||
|
|
||||||
|
target_link_libraries(main nlohmann_json::nlohmann_json Threads::Threads)
|
||||||
|
set_target_properties(main PROPERTIES LINKER_LANGUAGE CXX)
|
||||||
|
set_target_properties(main PROPERTIES OUTPUT_NAME cgproxy)
|
||||||
|
|
||||||
|
install(TARGETS main DESTINATION /usr/bin PERMISSIONS ${basic_permission})
|
||||||
66
src/cgproxy.hpp
Normal file
66
src/cgproxy.hpp
Normal file
@@ -0,0 +1,66 @@
|
|||||||
|
#include "common.h"
|
||||||
|
#include "config.h"
|
||||||
|
#include "socket_client.h"
|
||||||
|
#include <cstdlib>
|
||||||
|
#include <nlohmann/json.hpp>
|
||||||
|
#include <unistd.h>
|
||||||
|
using json = nlohmann::json;
|
||||||
|
using namespace CGPROXY;
|
||||||
|
using namespace CGPROXY::CONFIG;
|
||||||
|
|
||||||
|
namespace CGPROXY::CGPROXY {
|
||||||
|
|
||||||
|
bool print_help = false, proxy = true;
|
||||||
|
inline void print_usage() {
|
||||||
|
if (proxy) {
|
||||||
|
cout << "Run program with proxy" << endl;
|
||||||
|
cout << "Usage: cgproxy [--help] [--debug] <CMD>" << endl;
|
||||||
|
} else {
|
||||||
|
cout << "Run program without proxy" << endl;
|
||||||
|
cout << "Usage: cgpnoroxy [--help] [--debug] <CMD>" << endl;
|
||||||
|
cout << "Alias: cgnoproxy = cgproxy --noproxy" << endl;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void processArgs(const int argc, char *argv[], int &shift) {
|
||||||
|
for (int i = 1; i < argc; i++) {
|
||||||
|
if (strcmp(argv[i], "--noproxy") == 0) { proxy = false; }
|
||||||
|
if (strcmp(argv[i], "--debug") == 0) { enable_debug = true; }
|
||||||
|
if (strcmp(argv[i], "--help") == 0) { print_help = true; }
|
||||||
|
if (argv[i][0] != '-') { break; }
|
||||||
|
shift += 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void send_pid(const pid_t pid, bool proxy, int &status) {
|
||||||
|
json j;
|
||||||
|
j["type"] = proxy ? MSG_TYPE_PROXY_PID : MSG_TYPE_NOPROXY_PID;
|
||||||
|
j["data"] = pid;
|
||||||
|
SOCKET::send(j.dump(), status);
|
||||||
|
}
|
||||||
|
|
||||||
|
int main(int argc, char *argv[]) {
|
||||||
|
int shift = 1;
|
||||||
|
processArgs(argc, argv, shift);
|
||||||
|
|
||||||
|
if (print_help) {
|
||||||
|
print_usage();
|
||||||
|
exit(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (argc == shift) {
|
||||||
|
error("no program specified");
|
||||||
|
exit(EXIT_FAILURE);
|
||||||
|
}
|
||||||
|
|
||||||
|
int status = -1;
|
||||||
|
send_pid(getpid(), proxy, status);
|
||||||
|
if (status != 0) {
|
||||||
|
error("attach process failed");
|
||||||
|
exit(EXIT_FAILURE);
|
||||||
|
}
|
||||||
|
|
||||||
|
string s = join2str(argc - shift, argv + shift, ' ');
|
||||||
|
return system(s.c_str());
|
||||||
|
}
|
||||||
|
} // namespace CGPROXY::CGPROXY
|
||||||
184
src/cgproxyd.hpp
Normal file
184
src/cgproxyd.hpp
Normal file
@@ -0,0 +1,184 @@
|
|||||||
|
#ifndef CGPROXYD_HPP
|
||||||
|
#define CGPROXYD_HPP
|
||||||
|
|
||||||
|
#include "cgroup_attach.h"
|
||||||
|
#include "common.h"
|
||||||
|
#include "config.h"
|
||||||
|
#include "socket_server.h"
|
||||||
|
#include <csignal>
|
||||||
|
#include <fstream>
|
||||||
|
#include <nlohmann/json.hpp>
|
||||||
|
#include <sys/file.h>
|
||||||
|
#include <unistd.h>
|
||||||
|
|
||||||
|
using namespace std;
|
||||||
|
using json = nlohmann::json;
|
||||||
|
using namespace ::CGPROXY::SOCKET;
|
||||||
|
using namespace ::CGPROXY::CONFIG;
|
||||||
|
using namespace ::CGPROXY::CGROUP;
|
||||||
|
|
||||||
|
namespace CGPROXY::CGPROXYD {
|
||||||
|
|
||||||
|
class cgproxyd {
|
||||||
|
thread_arg arg_t;
|
||||||
|
Config config;
|
||||||
|
pthread_t socket_thread_id = -1;
|
||||||
|
|
||||||
|
static cgproxyd *instance;
|
||||||
|
static int handle_msg_static(char *msg) {
|
||||||
|
if (!instance) {
|
||||||
|
error("no cgproxyd instance assigned");
|
||||||
|
return ERROR;
|
||||||
|
}
|
||||||
|
return instance->handle_msg(msg);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void signalHandler(int signum) {
|
||||||
|
debug("Signal %d received.", signum);
|
||||||
|
if (!instance) {
|
||||||
|
error("no cgproxyd instance assigned");
|
||||||
|
} else {
|
||||||
|
instance->stop();
|
||||||
|
}
|
||||||
|
exit(signum);
|
||||||
|
}
|
||||||
|
|
||||||
|
// single process instance
|
||||||
|
int lock_fd;
|
||||||
|
void lock() {
|
||||||
|
lock_fd = open(PID_LOCK_FILE, O_CREAT | O_RDWR, 0666);
|
||||||
|
int rc = flock(lock_fd, LOCK_EX | LOCK_NB);
|
||||||
|
if (rc == -1) {
|
||||||
|
perror(PID_LOCK_FILE);
|
||||||
|
error("maybe another cgproxyd is running");
|
||||||
|
exit(EXIT_FAILURE);
|
||||||
|
} else {
|
||||||
|
ofstream ofs(PID_LOCK_FILE);
|
||||||
|
ofs << getpid() << endl;
|
||||||
|
ofs.close();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
void unlock() {
|
||||||
|
close(lock_fd);
|
||||||
|
unlink(PID_LOCK_FILE);
|
||||||
|
}
|
||||||
|
|
||||||
|
int handle_msg(char *msg) {
|
||||||
|
debug("received msg: %s", msg);
|
||||||
|
json j;
|
||||||
|
try {
|
||||||
|
j = json::parse(msg);
|
||||||
|
} catch (exception &e) {
|
||||||
|
debug("msg paser error");
|
||||||
|
return MSG_ERROR;
|
||||||
|
}
|
||||||
|
|
||||||
|
int type, status;
|
||||||
|
int pid, cgroup_target;
|
||||||
|
try {
|
||||||
|
type = j.at("type").get<int>();
|
||||||
|
switch (type) {
|
||||||
|
case MSG_TYPE_CONFIG_JSON:
|
||||||
|
status = config.loadFromJsonStr(j.at("data").dump());
|
||||||
|
if (status == SUCCESS) status = applyConfig(&config);
|
||||||
|
return status;
|
||||||
|
break;
|
||||||
|
case MSG_TYPE_CONFIG_PATH:
|
||||||
|
status = config.loadFromFile(j.at("data").get<string>());
|
||||||
|
if (status == SUCCESS) status = applyConfig(&config);
|
||||||
|
return status;
|
||||||
|
break;
|
||||||
|
case MSG_TYPE_PROXY_PID:
|
||||||
|
pid = j.at("data").get<int>();
|
||||||
|
status = attach(pid, config.cgroup_proxy_preserved);
|
||||||
|
return status;
|
||||||
|
break;
|
||||||
|
case MSG_TYPE_NOPROXY_PID:
|
||||||
|
pid = j.at("data").get<int>();
|
||||||
|
status = attach(pid, config.cgroup_noproxy_preserved);
|
||||||
|
return status;
|
||||||
|
break;
|
||||||
|
default: return MSG_ERROR; break;
|
||||||
|
};
|
||||||
|
} catch (out_of_range &e) { return MSG_ERROR; } catch (exception &e) {
|
||||||
|
return ERROR;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pthread_t startSocketListeningThread() {
|
||||||
|
arg_t.handle_msg = &handle_msg_static;
|
||||||
|
pthread_t thread_id;
|
||||||
|
int status = pthread_create(&thread_id, NULL, &SocketServer::startThread, &arg_t);
|
||||||
|
if (status != 0) error("socket thread create failed");
|
||||||
|
return thread_id;
|
||||||
|
}
|
||||||
|
|
||||||
|
void assignStaticInstance() { instance = this; }
|
||||||
|
|
||||||
|
public:
|
||||||
|
int start() {
|
||||||
|
lock();
|
||||||
|
signal(SIGINT, &signalHandler);
|
||||||
|
signal(SIGTERM, &signalHandler);
|
||||||
|
signal(SIGHUP, &signalHandler);
|
||||||
|
|
||||||
|
config.loadFromFile(DEFAULT_CONFIG_FILE);
|
||||||
|
applyConfig(&config);
|
||||||
|
|
||||||
|
assignStaticInstance();
|
||||||
|
socket_thread_id = startSocketListeningThread();
|
||||||
|
pthread_join(socket_thread_id, NULL);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
int applyConfig(Config *c) {
|
||||||
|
system(TPROXY_IPTABLS_CLEAN);
|
||||||
|
c->toEnv();
|
||||||
|
system(TPROXY_IPTABLS_START);
|
||||||
|
// no need to track running status
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
void stop() {
|
||||||
|
debug("stopping");
|
||||||
|
system(TPROXY_IPTABLS_CLEAN);
|
||||||
|
unlock();
|
||||||
|
}
|
||||||
|
|
||||||
|
~cgproxyd() { stop(); }
|
||||||
|
};
|
||||||
|
|
||||||
|
cgproxyd *cgproxyd::instance = NULL;
|
||||||
|
|
||||||
|
bool print_help = false;
|
||||||
|
|
||||||
|
void print_usage() {
|
||||||
|
cout << "Start a daemon with unix socket to accept control" << endl;
|
||||||
|
cout << "Usage: cgproxyd [--help] [--debug]" << endl;
|
||||||
|
cout << "Alias: cgproxyd = cgproxy --daemon" << endl;
|
||||||
|
}
|
||||||
|
|
||||||
|
void processArgs(const int argc, char *argv[]) {
|
||||||
|
for (int i = 1; i < argc; i++) {
|
||||||
|
if (strcmp(argv[i], "--debug") == 0) { enable_debug = true; }
|
||||||
|
if (strcmp(argv[i], "--help") == 0) { print_help = true; }
|
||||||
|
if (argv[i][0] != '-') { break; }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
int main(int argc, char *argv[]) {
|
||||||
|
processArgs(argc, argv);
|
||||||
|
if (print_help) {
|
||||||
|
print_usage();
|
||||||
|
exit(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (getuid() != 0) {
|
||||||
|
error("permission denied, need root");
|
||||||
|
exit(EXIT_FAILURE);
|
||||||
|
}
|
||||||
|
|
||||||
|
cgproxyd d;
|
||||||
|
return d.start();
|
||||||
|
}
|
||||||
|
} // namespace CGPROXY::CGPROXYD
|
||||||
|
#endif
|
||||||
@@ -1,66 +1,60 @@
|
|||||||
#ifndef CGPROUP_ATTACH_H
|
#include "cgroup_attach.h"
|
||||||
#define CGPROUP_ATTACH_H
|
#include "common.h"
|
||||||
|
|
||||||
#include <errno.h>
|
#include <errno.h>
|
||||||
#include <fstream>
|
#include <fstream>
|
||||||
#include <iostream>
|
#include <iostream>
|
||||||
#include <regex>
|
#include <regex>
|
||||||
|
#include <stdio.h>
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
#include <string>
|
#include <string>
|
||||||
#include <sys/stat.h>
|
#include <sys/stat.h>
|
||||||
#include <sys/types.h>
|
#include <sys/types.h>
|
||||||
#include <unistd.h>
|
#include <unistd.h>
|
||||||
#include <stdio.h>
|
|
||||||
#include "common.hpp"
|
|
||||||
using namespace std;
|
|
||||||
|
|
||||||
namespace CGPROXY::CGROUP{
|
namespace CGPROXY::CGROUP {
|
||||||
|
|
||||||
bool exist(string path) {
|
bool exist(string path) {
|
||||||
struct stat st;
|
struct stat st;
|
||||||
if (stat(path.c_str(), &st) != -1) {
|
if (stat(path.c_str(), &st) != -1) { return S_ISDIR(st.st_mode); }
|
||||||
return S_ISDIR(st.st_mode);
|
|
||||||
}
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool validate(string pid, string cgroup) {
|
bool validate(string pid, string cgroup) {
|
||||||
bool pid_v = validPid(pid);
|
bool pid_v = validPid(pid);
|
||||||
bool cg_v = validCgroup(cgroup);
|
bool cg_v = validCgroup(cgroup);
|
||||||
if (pid_v && cg_v)
|
if (pid_v && cg_v) return true;
|
||||||
return true;
|
|
||||||
|
|
||||||
error("attach paramater validate error");
|
error("attach paramater validate error");
|
||||||
return_error
|
return_error
|
||||||
}
|
}
|
||||||
|
|
||||||
string get_cgroup2_mount_point(int &status){
|
string get_cgroup2_mount_point(int &status) {
|
||||||
char cgroup2_mount_point[100]="";
|
char cgroup2_mount_point[100] = "";
|
||||||
FILE* fp = popen("findmnt -t cgroup2 -n -o TARGET", "r");
|
FILE *fp = popen("findmnt -t cgroup2 -n -o TARGET", "r");
|
||||||
int count=fscanf(fp,"%s",&cgroup2_mount_point);
|
int count = fscanf(fp, "%s", cgroup2_mount_point);
|
||||||
fclose(fp);
|
fclose(fp);
|
||||||
if (count=0){
|
if (count == 0) {
|
||||||
error("cgroup2 not supported");
|
error("cgroup2 not supported");
|
||||||
status=-1;
|
status = -1;
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
status=0;
|
status = 0;
|
||||||
return cgroup2_mount_point;
|
return cgroup2_mount_point;
|
||||||
}
|
}
|
||||||
|
|
||||||
int attach(const string pid, const string cgroup_target) {
|
int attach(const string pid, const string cgroup_target) {
|
||||||
if (getuid()!=0) {
|
if (getuid() != 0) {
|
||||||
error("need root to attach cgroup");
|
error("need root to attach cgroup");
|
||||||
return_error
|
return_error
|
||||||
}
|
}
|
||||||
|
|
||||||
debug("attaching %s to %s",pid.c_str(),cgroup_target.c_str());
|
debug("attaching %s to %s", pid.c_str(), cgroup_target.c_str());
|
||||||
|
|
||||||
int status;
|
int status;
|
||||||
if (!validate(pid, cgroup_target)) return_error
|
if (!validate(pid, cgroup_target))
|
||||||
string cgroup_mount_point = get_cgroup2_mount_point(status);
|
return_error string cgroup_mount_point = get_cgroup2_mount_point(status);
|
||||||
if (status!=0) return_error
|
if (status != 0)
|
||||||
string cgroup_target_path = cgroup_mount_point + cgroup_target;
|
return_error string cgroup_target_path = cgroup_mount_point + cgroup_target;
|
||||||
string cgroup_target_procs = cgroup_target_path + "/cgroup.procs";
|
string cgroup_target_procs = cgroup_target_path + "/cgroup.procs";
|
||||||
|
|
||||||
// check if exist, we will create it if not exist
|
// check if exist, we will create it if not exist
|
||||||
@@ -87,17 +81,15 @@ int attach(const string pid, const string cgroup_target) {
|
|||||||
|
|
||||||
// maybe there some write error, for example process pid may not exist
|
// maybe there some write error, for example process pid may not exist
|
||||||
if (!procs) {
|
if (!procs) {
|
||||||
error("write %s to %s failed, maybe process %s not exist",
|
error("write %s to %s failed, maybe process %s not exist", pid.c_str(),
|
||||||
pid.c_str(), cgroup_target_procs.c_str(), pid.c_str());
|
cgroup_target_procs.c_str(), pid.c_str());
|
||||||
return_error
|
return_error
|
||||||
}
|
}
|
||||||
return_success
|
return_success
|
||||||
}
|
}
|
||||||
|
|
||||||
int attach(const int pid, const string cgroup_target){
|
int attach(const int pid, const string cgroup_target) {
|
||||||
return attach(to_str(pid), cgroup_target);
|
return attach(to_str(pid), cgroup_target);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
} // namespace CGPROXY::CGROUP
|
||||||
|
|
||||||
#endif
|
|
||||||
18
src/cgroup_attach.h
Normal file
18
src/cgroup_attach.h
Normal file
@@ -0,0 +1,18 @@
|
|||||||
|
#ifndef CGPROUP_ATTACH_H
|
||||||
|
#define CGPROUP_ATTACH_H
|
||||||
|
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <string>
|
||||||
|
using namespace std;
|
||||||
|
|
||||||
|
namespace CGPROXY::CGROUP {
|
||||||
|
|
||||||
|
bool exist(string path);
|
||||||
|
bool validate(string pid, string cgroup);
|
||||||
|
string get_cgroup2_mount_point(int &status);
|
||||||
|
int attach(const string pid, const string cgroup_target);
|
||||||
|
int attach(const int pid, const string cgroup_target);
|
||||||
|
|
||||||
|
} // namespace CGPROXY::CGROUP
|
||||||
|
|
||||||
|
#endif
|
||||||
34
src/common.cpp
Normal file
34
src/common.cpp
Normal file
@@ -0,0 +1,34 @@
|
|||||||
|
#include "common.h"
|
||||||
|
#include <regex>
|
||||||
|
|
||||||
|
bool enable_debug = false;
|
||||||
|
|
||||||
|
string join2str(const vector<string> t, const char delm) {
|
||||||
|
string s;
|
||||||
|
for (const auto &e : t) e != *(t.end() - 1) ? s += e + delm : s += e;
|
||||||
|
return s;
|
||||||
|
}
|
||||||
|
|
||||||
|
string join2str(const int argc, char **argv, const char delm) {
|
||||||
|
string s;
|
||||||
|
for (int i = 0; i < argc; i++) {
|
||||||
|
s += argv[i];
|
||||||
|
if (i != argc - 1) s += delm;
|
||||||
|
}
|
||||||
|
return s;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool validCgroup(const string cgroup) {
|
||||||
|
return regex_match(cgroup, regex("^/[a-zA-Z0-9\\-_./@]*$"));
|
||||||
|
}
|
||||||
|
|
||||||
|
bool validCgroup(const vector<string> cgroup) {
|
||||||
|
for (auto &e : cgroup) {
|
||||||
|
if (!regex_match(e, regex("^/[a-zA-Z0-9\\-_./@]*$"))) { return false; }
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool validPid(const string pid) { return regex_match(pid, regex("^[0-9]+$")); }
|
||||||
|
|
||||||
|
bool validPort(const int port) { return port > 0; }
|
||||||
72
src/common.h
Normal file
72
src/common.h
Normal file
@@ -0,0 +1,72 @@
|
|||||||
|
#ifndef COMMON_H
|
||||||
|
#define COMMON_H 1
|
||||||
|
|
||||||
|
#include <iostream>
|
||||||
|
#include <sstream>
|
||||||
|
#include <string>
|
||||||
|
#include <vector>
|
||||||
|
using namespace std;
|
||||||
|
|
||||||
|
#define TPROXY_IPTABLS_START "sh /usr/share/cgproxy/scripts/cgroup-tproxy.sh"
|
||||||
|
#define TPROXY_IPTABLS_CLEAN "sh /usr/share/cgproxy/scripts/cgroup-tproxy.sh stop"
|
||||||
|
|
||||||
|
#define PID_LOCK_FILE "/var/run/cgproxyd.pid"
|
||||||
|
#define SOCKET_PATH "/tmp/cgproxy_unix_socket"
|
||||||
|
#define LISTEN_BACKLOG 64
|
||||||
|
#define DEFAULT_CONFIG_FILE "/etc/cgproxy/config.json"
|
||||||
|
|
||||||
|
#define CGROUP_PROXY_PRESVERED "/proxy.slice"
|
||||||
|
#define CGROUP_NOPROXY_PRESVERED "/noproxy.slice"
|
||||||
|
|
||||||
|
#define MSG_TYPE_CONFIG_JSON 1
|
||||||
|
#define MSG_TYPE_CONFIG_PATH 2
|
||||||
|
#define MSG_TYPE_PROXY_PID 3
|
||||||
|
#define MSG_TYPE_NOPROXY_PID 4
|
||||||
|
|
||||||
|
#define UNKNOWN_ERROR 99
|
||||||
|
#define ERROR -1
|
||||||
|
#define SUCCESS 0
|
||||||
|
#define CONN_ERROR 1
|
||||||
|
#define MSG_ERROR 2
|
||||||
|
#define PARSE_ERROR 3
|
||||||
|
#define PARAM_ERROR 4
|
||||||
|
#define APPLY_ERROR 5
|
||||||
|
#define CGROUP_ERROR 6
|
||||||
|
#define FILE_ERROR 7
|
||||||
|
|
||||||
|
extern bool enable_debug;
|
||||||
|
|
||||||
|
#define error(...) \
|
||||||
|
{ \
|
||||||
|
fprintf(stderr, "error: "); \
|
||||||
|
fprintf(stderr, __VA_ARGS__); \
|
||||||
|
fprintf(stderr, "\n"); \
|
||||||
|
}
|
||||||
|
|
||||||
|
#define debug(...) \
|
||||||
|
if (enable_debug) { \
|
||||||
|
fprintf(stderr, "debug: "); \
|
||||||
|
fprintf(stdout, __VA_ARGS__); \
|
||||||
|
fprintf(stdout, "\n"); \
|
||||||
|
}
|
||||||
|
|
||||||
|
#define return_error return -1;
|
||||||
|
#define return_success return 0;
|
||||||
|
|
||||||
|
template <typename... T> string to_str(T... args) {
|
||||||
|
stringstream ss;
|
||||||
|
ss.clear();
|
||||||
|
ss << std::boolalpha;
|
||||||
|
(ss << ... << args);
|
||||||
|
return ss.str();
|
||||||
|
}
|
||||||
|
|
||||||
|
string join2str(const vector<string> t, const char delm = ' ');
|
||||||
|
string join2str(const int argc, char **argv, const char delm = ' ');
|
||||||
|
|
||||||
|
bool validCgroup(const string cgroup);
|
||||||
|
bool validCgroup(const vector<string> cgroup);
|
||||||
|
bool validPid(const string pid);
|
||||||
|
bool validPort(const int port);
|
||||||
|
|
||||||
|
#endif
|
||||||
121
src/config.cpp
Normal file
121
src/config.cpp
Normal file
@@ -0,0 +1,121 @@
|
|||||||
|
#include "config.h"
|
||||||
|
#include "common.h"
|
||||||
|
#include <fstream>
|
||||||
|
#include <iomanip>
|
||||||
|
#include <nlohmann/json.hpp>
|
||||||
|
#include <set>
|
||||||
|
using json = nlohmann::json;
|
||||||
|
|
||||||
|
#define add2json(v) j[#v] = v;
|
||||||
|
#define tryassign(v) \
|
||||||
|
try { \
|
||||||
|
j.at(#v).get_to(v); \
|
||||||
|
} catch (exception & e) {}
|
||||||
|
#define merge(v) \
|
||||||
|
{ \
|
||||||
|
v.erase(std::remove(v.begin(), v.end(), v##_preserved), v.end()); \
|
||||||
|
v.insert(v.begin(), v##_preserved); \
|
||||||
|
}
|
||||||
|
|
||||||
|
namespace CGPROXY::CONFIG {
|
||||||
|
|
||||||
|
void Config::toEnv() {
|
||||||
|
mergeReserved();
|
||||||
|
setenv("cgroup_proxy", join2str(cgroup_proxy, ':').c_str(), 1);
|
||||||
|
setenv("cgroup_noproxy", join2str(cgroup_noproxy, ':').c_str(), 1);
|
||||||
|
setenv("enable_gateway", to_str(enable_gateway).c_str(), 1);
|
||||||
|
setenv("port", to_str(port).c_str(), 1);
|
||||||
|
setenv("enable_dns", to_str(enable_dns).c_str(), 1);
|
||||||
|
setenv("enable_tcp", to_str(enable_tcp).c_str(), 1);
|
||||||
|
setenv("enable_udp", to_str(enable_udp).c_str(), 1);
|
||||||
|
setenv("enable_ipv4", to_str(enable_ipv4).c_str(), 1);
|
||||||
|
setenv("enable_ipv6", to_str(enable_ipv6).c_str(), 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
int Config::saveToFile(const string f) {
|
||||||
|
ofstream o(f);
|
||||||
|
if (!o.is_open()) return FILE_ERROR;
|
||||||
|
string js = toJsonStr();
|
||||||
|
o << setw(4) << js << endl;
|
||||||
|
o.close();
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
string Config::toJsonStr() {
|
||||||
|
json j;
|
||||||
|
add2json(cgroup_proxy);
|
||||||
|
add2json(cgroup_noproxy);
|
||||||
|
add2json(enable_gateway);
|
||||||
|
add2json(port);
|
||||||
|
add2json(enable_dns);
|
||||||
|
add2json(enable_tcp);
|
||||||
|
add2json(enable_udp);
|
||||||
|
add2json(enable_ipv4);
|
||||||
|
add2json(enable_ipv6);
|
||||||
|
return j.dump();
|
||||||
|
}
|
||||||
|
|
||||||
|
int Config::loadFromFile(const string f) {
|
||||||
|
debug("loading config: %s", f.c_str());
|
||||||
|
ifstream ifs(f);
|
||||||
|
if (ifs.is_open()) {
|
||||||
|
string js = to_str(ifs.rdbuf());
|
||||||
|
ifs.close();
|
||||||
|
return loadFromJsonStr(js);
|
||||||
|
} else {
|
||||||
|
error("open failed: %s", f.c_str());
|
||||||
|
return FILE_ERROR;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
int Config::loadFromJsonStr(const string js) {
|
||||||
|
if (!validateJsonStr(js)) {
|
||||||
|
error("json validate fail");
|
||||||
|
return PARAM_ERROR;
|
||||||
|
}
|
||||||
|
json j = json::parse(js);
|
||||||
|
tryassign(cgroup_proxy);
|
||||||
|
tryassign(cgroup_noproxy);
|
||||||
|
tryassign(enable_gateway);
|
||||||
|
tryassign(port);
|
||||||
|
tryassign(enable_dns);
|
||||||
|
tryassign(enable_tcp);
|
||||||
|
tryassign(enable_udp);
|
||||||
|
tryassign(enable_ipv4);
|
||||||
|
tryassign(enable_ipv6);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Config::mergeReserved() {
|
||||||
|
merge(cgroup_proxy);
|
||||||
|
merge(cgroup_noproxy);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool Config::validateJsonStr(const string js) {
|
||||||
|
json j = json::parse(js);
|
||||||
|
bool status = true;
|
||||||
|
const set<string> boolset = {"enable_gateway", "enable_dns", "enable_tcp",
|
||||||
|
"enable_udp", "enable_ipv4", "enable_ipv6"};
|
||||||
|
for (auto &[key, value] : j.items()) {
|
||||||
|
if (key == "cgroup_proxy" || key == "cgroup_noproxy") {
|
||||||
|
if (value.is_string() && !validCgroup((string)value)) status = false;
|
||||||
|
// TODO what if vector<int> etc.
|
||||||
|
if (value.is_array() && !validCgroup((vector<string>)value)) status = false;
|
||||||
|
if (!value.is_string() && !value.is_array()) status = false;
|
||||||
|
} else if (key == "port") {
|
||||||
|
if (!validPort(value)) status = false;
|
||||||
|
} else if (boolset.find(key) != boolset.end()) {
|
||||||
|
if (!value.is_boolean()) status = false;
|
||||||
|
} else {
|
||||||
|
error("unknown key: %s", key.c_str());
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (!status) {
|
||||||
|
error("invalid value for key: %s", key.c_str());
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace CGPROXY::CONFIG
|
||||||
38
src/config.h
Normal file
38
src/config.h
Normal file
@@ -0,0 +1,38 @@
|
|||||||
|
#ifndef CONFIG_H
|
||||||
|
#define CONFIG_H
|
||||||
|
#include "common.h"
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <string>
|
||||||
|
#include <vector>
|
||||||
|
using namespace std;
|
||||||
|
|
||||||
|
namespace CGPROXY::CONFIG {
|
||||||
|
|
||||||
|
class Config {
|
||||||
|
public:
|
||||||
|
const string cgroup_proxy_preserved = CGROUP_PROXY_PRESVERED;
|
||||||
|
const string cgroup_noproxy_preserved = CGROUP_NOPROXY_PRESVERED;
|
||||||
|
|
||||||
|
vector<string> cgroup_proxy;
|
||||||
|
vector<string> cgroup_noproxy;
|
||||||
|
bool enable_gateway = false;
|
||||||
|
int port = 12345;
|
||||||
|
bool enable_dns = true;
|
||||||
|
bool enable_tcp = true;
|
||||||
|
bool enable_udp = true;
|
||||||
|
bool enable_ipv4 = true;
|
||||||
|
bool enable_ipv6 = true;
|
||||||
|
|
||||||
|
void toEnv();
|
||||||
|
int saveToFile(const string f);
|
||||||
|
string toJsonStr();
|
||||||
|
int loadFromFile(const string f);
|
||||||
|
int loadFromJsonStr(const string js);
|
||||||
|
|
||||||
|
private:
|
||||||
|
void mergeReserved();
|
||||||
|
bool validateJsonStr(const string js);
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace CGPROXY::CONFIG
|
||||||
|
#endif
|
||||||
17
src/main.cpp
Normal file
17
src/main.cpp
Normal file
@@ -0,0 +1,17 @@
|
|||||||
|
#include "cgproxy.hpp"
|
||||||
|
#include "cgproxyd.hpp"
|
||||||
|
|
||||||
|
bool as_cgproxyd = false;
|
||||||
|
void processArgs(const int argc, char *argv[]) {
|
||||||
|
for (int i = 1; i < argc; i++) {
|
||||||
|
if (strcmp(argv[i], "--daemon") == 0) { as_cgproxyd = true; }
|
||||||
|
if (argv[i][0] != '-') { break; }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
int main(int argc, char *argv[]) {
|
||||||
|
processArgs(argc, argv);
|
||||||
|
if (as_cgproxyd) ::CGPROXY::CGPROXYD::main(argc, argv);
|
||||||
|
else
|
||||||
|
::CGPROXY::CGPROXY::main(argc, argv);
|
||||||
|
}
|
||||||
@@ -1,28 +1,19 @@
|
|||||||
#ifndef SOCKET_CLIENT_H
|
#include "socket_client.h"
|
||||||
#define SOCKET_CLIENT_H
|
#include "common.h"
|
||||||
|
|
||||||
#include <iostream>
|
|
||||||
#include <stdio.h>
|
|
||||||
#include <stdlib.h>
|
|
||||||
#include <string>
|
|
||||||
#include <sys/socket.h>
|
#include <sys/socket.h>
|
||||||
#include <sys/types.h>
|
|
||||||
#include <sys/un.h>
|
#include <sys/un.h>
|
||||||
#include <unistd.h>
|
#include <unistd.h>
|
||||||
#include "common.hpp"
|
|
||||||
|
|
||||||
using namespace std;
|
#define return_if_error(flag, msg) \
|
||||||
|
if (flag == -1) { \
|
||||||
namespace CGPROXY::SOCKET{
|
perror(msg); \
|
||||||
|
status = CONN_ERROR; \
|
||||||
#define return_if_error(flag, msg) \
|
close(sfd); \
|
||||||
if (flag == -1) { \
|
return; \
|
||||||
perror(msg); \
|
|
||||||
status = CONN_ERROR; \
|
|
||||||
close(sfd); \
|
|
||||||
return; \
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
namespace CGPROXY::SOCKET {
|
||||||
|
|
||||||
void send(const char *msg, int &status) {
|
void send(const char *msg, int &status) {
|
||||||
debug("send msg: %s", msg);
|
debug("send msg: %s", msg);
|
||||||
status = UNKNOWN_ERROR;
|
status = UNKNOWN_ERROR;
|
||||||
@@ -35,8 +26,7 @@ void send(const char *msg, int &status) {
|
|||||||
unix_socket.sun_family = AF_UNIX;
|
unix_socket.sun_family = AF_UNIX;
|
||||||
strncpy(unix_socket.sun_path, SOCKET_PATH, sizeof(unix_socket.sun_path) - 1);
|
strncpy(unix_socket.sun_path, SOCKET_PATH, sizeof(unix_socket.sun_path) - 1);
|
||||||
|
|
||||||
flag =
|
flag = connect(sfd, (struct sockaddr *)&unix_socket, sizeof(struct sockaddr_un));
|
||||||
connect(sfd, (struct sockaddr *)&unix_socket, sizeof(struct sockaddr_un));
|
|
||||||
return_if_error(flag, "connect");
|
return_if_error(flag, "connect");
|
||||||
|
|
||||||
int msg_len = strlen(msg);
|
int msg_len = strlen(msg);
|
||||||
@@ -60,5 +50,4 @@ void send(const string msg, int &status) {
|
|||||||
debug("return status: %d", status);
|
debug("return status: %d", status);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
} // namespace CGPROXY::SOCKET
|
||||||
#endif
|
|
||||||
14
src/socket_client.h
Normal file
14
src/socket_client.h
Normal file
@@ -0,0 +1,14 @@
|
|||||||
|
#ifndef SOCKET_CLIENT_H
|
||||||
|
#define SOCKET_CLIENT_H
|
||||||
|
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <string>
|
||||||
|
using namespace std;
|
||||||
|
|
||||||
|
namespace CGPROXY::SOCKET {
|
||||||
|
|
||||||
|
void send(const char *msg, int &status);
|
||||||
|
void send(const string msg, int &status);
|
||||||
|
|
||||||
|
} // namespace CGPROXY::SOCKET
|
||||||
|
#endif
|
||||||
65
src/socket_server.cpp
Normal file
65
src/socket_server.cpp
Normal file
@@ -0,0 +1,65 @@
|
|||||||
|
#include "socket_server.h"
|
||||||
|
#include "common.h"
|
||||||
|
#include <filesystem>
|
||||||
|
#include <sys/socket.h>
|
||||||
|
#include <sys/stat.h>
|
||||||
|
#include <unistd.h>
|
||||||
|
|
||||||
|
namespace fs = std::filesystem;
|
||||||
|
|
||||||
|
namespace CGPROXY::SOCKET {
|
||||||
|
|
||||||
|
void SocketServer::socketListening(function<int(char *)> callback) {
|
||||||
|
debug("starting socket listening");
|
||||||
|
sfd = socket(AF_UNIX, SOCK_STREAM, 0);
|
||||||
|
|
||||||
|
if (fs::exists(SOCKET_PATH) && unlink(SOCKET_PATH) == -1) {
|
||||||
|
error("%s exist, and can't unlink", SOCKET_PATH);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
memset(&unix_socket, '\0', sizeof(struct sockaddr_un));
|
||||||
|
unix_socket.sun_family = AF_UNIX;
|
||||||
|
strncpy(unix_socket.sun_path, SOCKET_PATH, sizeof(unix_socket.sun_path) - 1);
|
||||||
|
|
||||||
|
bind(sfd, (struct sockaddr *)&unix_socket, sizeof(struct sockaddr_un));
|
||||||
|
|
||||||
|
listen(sfd, LISTEN_BACKLOG);
|
||||||
|
chmod(SOCKET_PATH, S_IRWXU | S_IRWXG | S_IRWXO);
|
||||||
|
|
||||||
|
while (true) {
|
||||||
|
close(cfd);
|
||||||
|
cfd = accept(sfd, NULL, NULL);
|
||||||
|
continue_if_error(cfd, "accept");
|
||||||
|
debug("accept connection: %d", cfd);
|
||||||
|
|
||||||
|
// read length
|
||||||
|
int msg_len;
|
||||||
|
flag = read(cfd, &msg_len, sizeof(int));
|
||||||
|
continue_if_error(flag, "read length");
|
||||||
|
// read msg
|
||||||
|
char msg[msg_len];
|
||||||
|
flag = read(cfd, msg, msg_len * sizeof(char));
|
||||||
|
continue_if_error(flag, "read msg");
|
||||||
|
msg[msg_len] = '\0';
|
||||||
|
// handle msg
|
||||||
|
int status = callback(msg);
|
||||||
|
// send back flag
|
||||||
|
flag = write(cfd, &status, sizeof(int));
|
||||||
|
continue_if_error(flag, "write back");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void *SocketServer::startThread(void *arg) {
|
||||||
|
thread_arg *p = (thread_arg *)arg;
|
||||||
|
SocketServer server;
|
||||||
|
server.socketListening(p->handle_msg);
|
||||||
|
return (void *)0;
|
||||||
|
}
|
||||||
|
|
||||||
|
SocketServer::~SocketServer() {
|
||||||
|
close(sfd);
|
||||||
|
close(cfd);
|
||||||
|
unlink(SOCKET_PATH);
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace CGPROXY::SOCKET
|
||||||
33
src/socket_server.h
Normal file
33
src/socket_server.h
Normal file
@@ -0,0 +1,33 @@
|
|||||||
|
#ifndef SOCKET_SERVER_H
|
||||||
|
#define SOCKET_SERVER_H
|
||||||
|
|
||||||
|
#include <functional>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <sys/un.h>
|
||||||
|
using namespace std;
|
||||||
|
|
||||||
|
namespace CGPROXY::SOCKET {
|
||||||
|
|
||||||
|
#define continue_if_error(flag, msg) \
|
||||||
|
if (flag == -1) { \
|
||||||
|
perror(msg); \
|
||||||
|
continue; \
|
||||||
|
}
|
||||||
|
|
||||||
|
struct thread_arg {
|
||||||
|
function<int(char *)> handle_msg;
|
||||||
|
};
|
||||||
|
|
||||||
|
class SocketServer {
|
||||||
|
public:
|
||||||
|
int sfd = -1, cfd = -1, flag = -1;
|
||||||
|
struct sockaddr_un unix_socket;
|
||||||
|
|
||||||
|
void socketListening(function<int(char *)> callback);
|
||||||
|
~SocketServer();
|
||||||
|
static void *startThread(void *arg);
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace CGPROXY::SOCKET
|
||||||
|
|
||||||
|
#endif
|
||||||
7
test/CMakeLists.txt
Normal file
7
test/CMakeLists.txt
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
include_directories(${PROJECT_SOURCE_DIR})
|
||||||
|
include_directories(${PROJECT_SOURCE_DIR}/src)
|
||||||
|
|
||||||
|
find_package(nlohmann_json REQUIRED)
|
||||||
|
add_executable(client_test socket_client_test.cpp
|
||||||
|
../src/socket_client.cpp ../src/common.cpp ../src/config.cpp)
|
||||||
|
target_link_libraries(client_test nlohmann_json::nlohmann_json)
|
||||||
48
test/socket_client_test.cpp
Normal file
48
test/socket_client_test.cpp
Normal file
@@ -0,0 +1,48 @@
|
|||||||
|
#include "common.h"
|
||||||
|
#include "config.h"
|
||||||
|
#include "socket_client.h"
|
||||||
|
#include <nlohmann/json.hpp>
|
||||||
|
|
||||||
|
using namespace std;
|
||||||
|
using json = nlohmann::json;
|
||||||
|
using namespace CGPROXY;
|
||||||
|
using namespace CGPROXY::CONFIG;
|
||||||
|
|
||||||
|
void send_config(Config &config, int &status) {
|
||||||
|
json j;
|
||||||
|
j["type"] = MSG_TYPE_CONFIG_JSON;
|
||||||
|
j["data"] = config.toJsonStr();
|
||||||
|
SOCKET::send(j.dump(), status);
|
||||||
|
}
|
||||||
|
|
||||||
|
void send_config_path(const string s, int &status) {
|
||||||
|
json j;
|
||||||
|
j["type"] = MSG_TYPE_CONFIG_PATH;
|
||||||
|
j["data"] = s;
|
||||||
|
SOCKET::send(j.dump(), status);
|
||||||
|
}
|
||||||
|
|
||||||
|
void send_pid(const pid_t pid, bool proxy, int &status) {
|
||||||
|
json j;
|
||||||
|
j["type"] = proxy ? MSG_TYPE_PROXY_PID : MSG_TYPE_NOPROXY_PID;
|
||||||
|
j["data"] = pid;
|
||||||
|
SOCKET::send(j.dump(), status);
|
||||||
|
}
|
||||||
|
|
||||||
|
void test_config() {
|
||||||
|
Config config;
|
||||||
|
config.cgroup_proxy = {"/"};
|
||||||
|
int status;
|
||||||
|
send_config(config, status);
|
||||||
|
}
|
||||||
|
|
||||||
|
void test_config_path() {
|
||||||
|
string path = "/etc/cgproxy/config.json";
|
||||||
|
int status;
|
||||||
|
send_config_path(path, status);
|
||||||
|
}
|
||||||
|
|
||||||
|
int main() {
|
||||||
|
test_config();
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
4
tools/CMakeLists.txt
Normal file
4
tools/CMakeLists.txt
Normal file
@@ -0,0 +1,4 @@
|
|||||||
|
include_directories(${PROJECT_SOURCE_DIR})
|
||||||
|
include_directories(${PROJECT_SOURCE_DIR}/src)
|
||||||
|
add_executable(cgattach cgattach.cpp ../src/cgroup_attach.cpp ../src/common.cpp)
|
||||||
|
install(TARGETS cgattach DESTINATION /usr/bin PERMISSIONS ${basic_permission})
|
||||||
@@ -1,11 +1,14 @@
|
|||||||
#include "cgroup_attach.hpp"
|
#include "cgroup_attach.h"
|
||||||
|
#include "common.h"
|
||||||
|
#include <cstdlib>
|
||||||
|
#include <unistd.h>
|
||||||
using namespace std;
|
using namespace std;
|
||||||
|
|
||||||
void print_usage() { fprintf(stdout, "usage: cgattach <pid> <cgroup>\n"); }
|
void print_usage() { fprintf(stdout, "usage: cgattach <pid> <cgroup>\n"); }
|
||||||
|
|
||||||
int main(int argc, char *argv[]) {
|
int main(int argc, char *argv[]) {
|
||||||
int flag=setuid(0);
|
int flag = setuid(0);
|
||||||
if (flag!=0) {
|
if (flag != 0) {
|
||||||
perror("cgattach need root");
|
perror("cgattach need root");
|
||||||
exit(EXIT_FAILURE);
|
exit(EXIT_FAILURE);
|
||||||
}
|
}
|
||||||
@@ -19,5 +22,10 @@ int main(int argc, char *argv[]) {
|
|||||||
string pid = string(argv[1]);
|
string pid = string(argv[1]);
|
||||||
string cgroup_target = string(argv[2]);
|
string cgroup_target = string(argv[2]);
|
||||||
|
|
||||||
CGPROXY::CGROUP::attach(pid,cgroup_target);
|
if (validPid(pid) && validCgroup(cgroup_target)) {
|
||||||
|
CGPROXY::CGROUP::attach(pid, cgroup_target);
|
||||||
|
} else {
|
||||||
|
error("param not valid");
|
||||||
|
exit(EXIT_FAILURE);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
Reference in New Issue
Block a user