Browse Source

support trojan and more optimization

Tokumeikoi 4 years ago
parent
commit
943304eb02

+ 128 - 0
app/Http/Controllers/Admin/Server/TrojanController.php

@@ -0,0 +1,128 @@
+<?php
+
+namespace App\Http\Controllers\Admin\Server;
+
+use App\Http\Requests\Admin\ServerTrojanSave;
+use App\Http\Requests\Admin\ServerTrojanSort;
+use App\Http\Requests\Admin\ServerTrojanUpdate;
+use App\Utils\CacheKey;
+use Illuminate\Http\Request;
+use App\Http\Controllers\Controller;
+use App\Models\ServerTrojan;
+use Illuminate\Support\Facades\Cache;
+use Illuminate\Support\Facades\DB;
+
+class TrojanController extends Controller
+{
+    public function fetch(Request $request)
+    {
+        $server = ServerTrojan::orderBy('sort', 'ASC')->get();
+        for ($i = 0; $i < count($server); $i++) {
+            if (!empty($server[$i]['tags'])) {
+                $server[$i]['tags'] = json_decode($server[$i]['tags']);
+            }
+            $server[$i]['group_id'] = json_decode($server[$i]['group_id']);
+            $server[$i]['online'] = Cache::get(CacheKey::get('SERVER_ONLINE_USER', $server[$i]['id']));
+        }
+        return response([
+            'data' => $server
+        ]);
+    }
+
+    public function save(ServerTrojanSave $request)
+    {
+        $params = $request->only(array_keys(ServerTrojanSave::RULES));
+        $params['group_id'] = json_encode($params['group_id']);
+        if (isset($params['tags'])) {
+            $params['tags'] = json_encode($params['tags']);
+        }
+
+        if ($request->input('id')) {
+            $server = ServerTrojan::find($request->input('id'));
+            if (!$server) {
+                abort(500, '服务器不存在');
+            }
+            try {
+                $server->update($params);
+            } catch (\Exception $e) {
+                abort(500, '保存失败');
+            }
+            return response([
+                'data' => true
+            ]);
+        }
+
+        if (!ServerTrojan::create($params)) {
+            abort(500, '创建失败');
+        }
+
+        return response([
+            'data' => true
+        ]);
+    }
+
+    public function drop(Request $request)
+    {
+        if ($request->input('id')) {
+            $server = ServerTrojan::find($request->input('id'));
+            if (!$server) {
+                abort(500, '节点ID不存在');
+            }
+        }
+        return response([
+            'data' => $server->delete()
+        ]);
+    }
+
+    public function update(ServerTrojanUpdate $request)
+    {
+        $params = $request->only([
+            'show',
+        ]);
+
+        $server = ServerTrojan::find($request->input('id'));
+
+        if (!$server) {
+            abort(500, '该服务器不存在');
+        }
+        try {
+            $server->update($params);
+        } catch (\Exception $e) {
+            abort(500, '保存失败');
+        }
+
+        return response([
+            'data' => true
+        ]);
+    }
+
+    public function copy(Request $request)
+    {
+        $server = ServerTrojan::find($request->input('id'));
+        if (!$server) {
+            abort(500, '服务器不存在');
+        }
+        if (!ServerTrojan::create($server->toArray())) {
+            abort(500, '复制失败');
+        }
+
+        return response([
+            'data' => true
+        ]);
+    }
+
+    public function sort(ServerTrojanSort $request)
+    {
+        DB::beginTransaction();
+        foreach ($request->input('server_ids') as $k => $v) {
+            if (!ServerTrojan::find($v)->update(['sort' => $k + 1])) {
+                DB::rollBack();
+                abort(500, '保存失败');
+            }
+        }
+        DB::commit();
+        return response([
+            'data' => true
+        ]);
+    }
+}

+ 167 - 0
app/Http/Controllers/Admin/Server/V2rayController.php

@@ -0,0 +1,167 @@
+<?php
+
+namespace App\Http\Controllers\Admin\Server;
+
+use App\Http\Requests\Admin\ServerV2raySave;
+use App\Http\Requests\Admin\ServerV2raySort;
+use App\Http\Requests\Admin\ServerV2rayUpdate;
+use App\Services\ServerService;
+use App\Utils\CacheKey;
+use Illuminate\Http\Request;
+use App\Http\Controllers\Controller;
+use App\Models\Server;
+use Illuminate\Support\Facades\Cache;
+use Illuminate\Support\Facades\DB;
+
+class V2rayController extends Controller
+{
+    public function fetch(Request $request)
+    {
+        $server = Server::orderBy('sort', 'ASC')->get();
+        for ($i = 0; $i < count($server); $i++) {
+            if (!empty($server[$i]['tags'])) {
+                $server[$i]['tags'] = json_decode($server[$i]['tags']);
+            }
+            $server[$i]['group_id'] = json_decode($server[$i]['group_id']);
+            $server[$i]['online'] = Cache::get(CacheKey::get('SERVER_ONLINE_USER', $server[$i]['parent_id'] ? $server[$i]['parent_id'] : $server[$i]['id']));
+            if ($server[$i]['parent_id']) {
+                $server[$i]['last_check_at'] = Cache::get(CacheKey::get('SERVER_LAST_CHECK_AT', $server[$i]['parent_id']));
+            } else {
+                $server[$i]['last_check_at'] = Cache::get(CacheKey::get('SERVER_LAST_CHECK_AT', $server[$i]['id']));
+            }
+        }
+        return response([
+            'data' => $server
+        ]);
+    }
+
+    public function save(ServerV2raySave $request)
+    {
+        $params = $request->only(array_keys(ServerV2raySave::RULES));
+        $params['group_id'] = json_encode($params['group_id']);
+        if (isset($params['tags'])) {
+            $params['tags'] = json_encode($params['tags']);
+        }
+
+        if (isset($params['dnsSettings'])) {
+            if (!is_object(json_decode($params['dnsSettings']))) {
+                abort(500, 'DNS规则配置格式不正确');
+            }
+        }
+
+        if (isset($params['ruleSettings'])) {
+            if (!is_object(json_decode($params['ruleSettings']))) {
+                abort(500, '审计规则配置格式不正确');
+            }
+        }
+
+        if (isset($params['networkSettings'])) {
+            if (!is_object(json_decode($params['networkSettings']))) {
+                abort(500, '传输协议配置格式不正确');
+            }
+        }
+
+        if (isset($params['tlsSettings'])) {
+            if (!is_object(json_decode($params['tlsSettings']))) {
+                abort(500, 'TLS配置格式不正确');
+            }
+        }
+
+        if ($request->input('id')) {
+            $server = Server::find($request->input('id'));
+            if (!$server) {
+                abort(500, '服务器不存在');
+            }
+            try {
+                $server->update($params);
+            } catch (\Exception $e) {
+                abort(500, '保存失败');
+            }
+            return response([
+                'data' => true
+            ]);
+        }
+
+        if (!Server::create($params)) {
+            abort(500, '创建失败');
+        }
+
+        return response([
+            'data' => true
+        ]);
+    }
+
+    public function drop(Request $request)
+    {
+        if ($request->input('id')) {
+            $server = Server::find($request->input('id'));
+            if (!$server) {
+                abort(500, '节点ID不存在');
+            }
+        }
+        return response([
+            'data' => $server->delete()
+        ]);
+    }
+
+    public function update(ServerV2rayUpdate $request)
+    {
+        $params = $request->only([
+            'show',
+        ]);
+
+        $server = Server::find($request->input('id'));
+
+        if (!$server) {
+            abort(500, '该服务器不存在');
+        }
+        try {
+            $server->update($params);
+        } catch (\Exception $e) {
+            abort(500, '保存失败');
+        }
+
+        return response([
+            'data' => true
+        ]);
+    }
+
+    public function copy(Request $request)
+    {
+        $server = Server::find($request->input('id'));
+        if (!$server) {
+            abort(500, '服务器不存在');
+        }
+        if (!Server::create($server->toArray())) {
+            abort(500, '复制失败');
+        }
+
+        return response([
+            'data' => true
+        ]);
+    }
+
+    public function viewConfig(Request $request)
+    {
+        $serverService = new ServerService();
+        $config = $serverService->getConfig($request->input('node_id'), 23333);
+        return response([
+            'data' => $config
+        ]);
+    }
+
+    public function sort(ServerV2raySort $request)
+    {
+        DB::beginTransaction();
+        foreach ($request->input('server_ids') as $k => $v) {
+            if (!Server::find($v)->update(['sort' => $k + 1])) {
+                DB::rollBack();
+                abort(500, '保存失败');
+            }
+        }
+        DB::commit();
+        return response([
+            'data' => true
+        ]);
+    }
+}

+ 7 - 7
app/Http/Controllers/Admin/ServerController.php

@@ -2,9 +2,9 @@
 
 namespace App\Http\Controllers\Admin;
 
-use App\Http\Requests\Admin\ServerSave;
-use App\Http\Requests\Admin\ServerSort;
-use App\Http\Requests\Admin\ServerUpdate;
+use App\Http\Requests\Admin\ServerV2raySave;
+use App\Http\Requests\Admin\ServerV2raySort;
+use App\Http\Requests\Admin\ServerV2rayUpdate;
 use App\Services\ServerService;
 use App\Utils\CacheKey;
 use Illuminate\Http\Request;
@@ -38,9 +38,9 @@ class ServerController extends Controller
         ]);
     }
 
-    public function save(ServerSave $request)
+    public function save(ServerV2raySave $request)
     {
-        $params = $request->only(array_keys(ServerSave::RULES));
+        $params = $request->only(array_keys(ServerV2raySave::RULES));
         $params['group_id'] = json_encode($params['group_id']);
         if (isset($params['tags'])) {
             $params['tags'] = json_encode($params['tags']);
@@ -165,7 +165,7 @@ class ServerController extends Controller
         ]);
     }
 
-    public function update(ServerUpdate $request)
+    public function update(ServerV2rayUpdate $request)
     {
         $params = $request->only([
             'show',
@@ -211,7 +211,7 @@ class ServerController extends Controller
         ]);
     }
 
-    public function sort(ServerSort $request)
+    public function sort(ServerV2raySort $request)
     {
         DB::beginTransaction();
         foreach ($request->input('server_ids') as $k => $v) {

+ 47 - 34
app/Http/Controllers/Client/AppController.php

@@ -3,52 +3,65 @@
 namespace App\Http\Controllers\Client;
 
 use App\Http\Controllers\Controller;
+use App\Services\UserService;
 use Illuminate\Http\Request;
-use App\Models\User;
-use App\Models\Plan;
 use App\Models\Server;
-use App\Models\Notice;
-use App\Utils\Helper;
+use Symfony\Component\Yaml\Yaml;
 
 class AppController extends Controller
 {
-    CONST CLIENT_CONFIG = '{"policy":{"levels":{"0":{"uplinkOnly":0}}},"dns":{"servers":["8.8.8.8","localhost"]},"outboundDetour":[{"protocol":"freedom","tag":"direct","settings":{}}],"inbound":{"listen":"0.0.0.0","port":31211,"protocol":"socks","settings":{"auth":"noauth","udp":true,"ip":"127.0.0.1"}},"inboundDetour":[{"listen":"0.0.0.0","allocate":{"strategy":"always","refresh":5,"concurrency":3},"port":31210,"protocol":"http","tag":"httpDetour","domainOverride":["http","tls"],"streamSettings":{},"settings":{"timeout":0}}],"routing":{"strategy":"rules","settings":{"domainStrategy":"IPIfNonMatch","rules":[{"type":"field","ip":["geoip:cn"],"outboundTag":"direct"},{"type":"field","ip":["0.0.0.0/8","10.0.0.0/8","100.64.0.0/10","127.0.0.0/8","169.254.0.0/16","172.16.0.0/12","192.0.0.0/24","192.0.2.0/24","192.168.0.0/16","198.18.0.0/15","198.51.100.0/24","203.0.113.0/24","::1/128","fc00::/7","fe80::/10"],"outboundTag":"direct"}]}},"outbound":{"tag":"proxy","sendThrough":"0.0.0.0","mux":{"enabled":false,"concurrency":8},"protocol":"vmess","settings":{"vnext":[{"address":"server","port":443,"users":[{"id":"uuid","alterId":2,"security":"auto","level":0}],"remark":"remark"}]},"streamSettings":{"network":"tcp","tcpSettings":{"header":{"type":"none"}},"security":"none","tlsSettings":{"allowInsecure":true,"allowInsecureCiphers":true},"kcpSettings":{"header":{"type":"none"},"mtu":1350,"congestion":false,"tti":20,"uplinkCapacity":5,"writeBufferSize":1,"readBufferSize":1,"downlinkCapacity":20},"wsSettings":{"path":"","headers":{"Host":"server.cc"}}}}}';
-    CONST SOCKS_PORT = 10010;
-    CONST HTTP_PORT = 10011;
-
-    // TODO: 1.1.1 abolish
     public function data(Request $request)
     {
+        $server = [];
         $user = $request->user;
-        $nodes = [];
-        if ($user->plan_id) {
-            $user['plan'] = Plan::find($user->plan_id);
-            if (!$user['plan']) {
-                abort(500, '订阅计划不存在');
+        $userService = new UserService();
+        if ($userService->isAvailable($user)) {
+            $servers = Server::where('show', 1)
+                ->orderBy('sort', 'ASC')
+                ->get();
+            foreach ($servers as $item) {
+                $groupId = json_decode($item['group_id']);
+                if (in_array($user->group_id, $groupId)) {
+                    array_push($server, $item);
+                }
+            }
+        }
+        $config = Yaml::parseFile(base_path() . '/resources/rules/app.clash.yaml');
+        $proxy = [];
+        $proxies = [];
+        foreach ($server as $item) {
+            $array = [];
+            $array['name'] = $item->name;
+            $array['type'] = 'vmess';
+            $array['server'] = $item->host;
+            $array['port'] = $item->port;
+            $array['uuid'] = $user->uuid;
+            $array['alterId'] = $user->v2ray_alter_id;
+            $array['cipher'] = 'auto';
+            if ($item->tls) {
+                $tlsSettings = json_decode($item->tlsSettings);
+                $array['tls'] = true;
+                if (isset($tlsSettings->allowInsecure)) $array['skip-cert-verify'] = ($tlsSettings->allowInsecure ? true : false );
             }
-            if ($user->expired_at > time()) {
-                $servers = Server::where('show', 1)
-                    ->orderBy('name')
-                    ->get();
-                foreach ($servers as $item) {
-                    $groupId = json_decode($item['group_id']);
-                    if (in_array($user->group_id, $groupId)) {
-                        array_push($nodes, $item);
-                    }
+            if ($item->network == 'ws') {
+                $array['network'] = $item->network;
+                if ($item->networkSettings) {
+                    $wsSettings = json_decode($item->networkSettings);
+                    if (isset($wsSettings->path)) $array['ws-path'] = $wsSettings->path;
+                    if (isset($wsSettings->headers->Host)) $array['ws-headers'] = [
+                        'Host' => $wsSettings->headers->Host
+                    ];
                 }
             }
+            array_push($proxy, $array);
+            array_push($proxies, $item->name);
+        }
+
+        $config['Proxy'] = array_merge($config['Proxy'] ? $config['Proxy'] : [], $proxy);
+        foreach ($config['Proxy Group'] as $k => $v) {
+            $config['Proxy Group'][$k]['proxies'] = array_merge($config['Proxy Group'][$k]['proxies'], $proxies);
         }
-        return response([
-            'data' => [
-                'nodes' => $nodes,
-                'u' => $user->u,
-                'd' => $user->d,
-                'transfer_enable' => $user->transfer_enable,
-                'expired_at' => $user->expired_at,
-                'plan' => isset($user['plan']) ? $user['plan'] : false,
-                'notice' => Notice::orderBy('created_at', 'DESC')->first()
-            ]
-        ]);
+        die(Yaml::dump($config));
     }
 
     public function config(Request $request)

+ 3 - 3
app/Http/Controllers/Client/ClientController.php

@@ -206,9 +206,9 @@ class ClientController extends Controller
             $subsURL .= $_SERVER['SERVER_NAME'] . $_SERVER['REQUEST_URI'];
         }
 
-        $config = str_replace('$subs_link',$subsURL,$config);
-        $config = str_replace('$proxies',$proxies,$config);
-        $config = str_replace('$proxy_group',rtrim($proxyGroup, ', '),$config);
+        $config = str_replace('$subs_link', $subsURL, $config);
+        $config = str_replace('$proxies', $proxies, $config);
+        $config = str_replace('$proxy_group', rtrim($proxyGroup, ', '), $config);
         return $config;
     }
 

+ 2 - 1
app/Http/Controllers/Server/DeepbworkController.php

@@ -90,7 +90,8 @@ class DeepbworkController extends Controller
                 $request->input('node_id'),
                 $item['u'],
                 $item['d'],
-                $server->rate
+                $server->rate,
+                'vmess'
             );
         }
 

+ 2 - 1
app/Http/Controllers/Server/PoseidonController.php

@@ -80,7 +80,8 @@ class PoseidonController extends Controller
                 $request->input('node_id'),
                 $item['u'],
                 $item['d'],
-                $server->rate
+                $server->rate,
+                'vmess'
             );
         }
 

+ 118 - 0
app/Http/Controllers/Server/TrojanTidalabController.php

@@ -0,0 +1,118 @@
+<?php
+
+namespace App\Http\Controllers\Server;
+
+use App\Services\ServerService;
+use App\Services\UserService;
+use App\Utils\CacheKey;
+use Illuminate\Http\Request;
+use App\Http\Controllers\Controller;
+use App\Models\User;
+use App\Models\ServerTrojan;
+use App\Models\ServerLog;
+use Illuminate\Support\Facades\DB;
+use Illuminate\Support\Facades\Log;
+use Illuminate\Support\Facades\Cache;
+
+class TrojanTidalabController extends Controller
+{
+    CONST SERVER_CONFIG = '{"api":{"services":["HandlerService","StatsService"],"tag":"api"},"stats":{},"inbound":{"port":443,"protocol":"vmess","settings":{"clients":[]},"sniffing":{"enabled": true,"destOverride": ["http","tls"]},"streamSettings":{"network":"tcp"},"tag":"proxy"},"inboundDetour":[{"listen":"0.0.0.0","port":23333,"protocol":"dokodemo-door","settings":{"address":"0.0.0.0"},"tag":"api"}],"log":{"loglevel":"debug","access":"access.log","error":"error.log"},"outbound":{"protocol":"freedom","settings":{}},"outboundDetour":[{"protocol":"blackhole","settings":{},"tag":"block"}],"routing":{"rules":[{"inboundTag":"api","outboundTag":"api","type":"field"}]},"policy":{"levels":{"0":{"handshake":4,"connIdle":300,"uplinkOnly":5,"downlinkOnly":30,"statsUserUplink":true,"statsUserDownlink":true}}}}';
+
+    public function __construct(Request $request)
+    {
+        $token = $request->input('token');
+        if (empty($token)) {
+            abort(500, 'token is null');
+        }
+        if ($token !== config('v2board.server_token')) {
+            abort(500, 'token is error');
+        }
+    }
+
+    // 后端获取用户
+    public function user(Request $request)
+    {
+        $nodeId = $request->input('node_id');
+        $server = ServerTrojan::find($nodeId);
+        if (!$server) {
+            abort(500, 'fail');
+        }
+        Cache::put(CacheKey::get('SERVER_LAST_CHECK_AT', $server->id), time(), 3600);
+        $serverService = new ServerService();
+        $users = $serverService->getAvailableUsers(json_decode($server->group_id));
+        $result = [];
+        foreach ($users as $user) {
+            $user->trojan_user = [
+                "password" => $user->uuid,
+            ];
+            unset($user['uuid']);
+            unset($user['v2ray_alter_id']);
+            unset($user['v2ray_level']);
+            array_push($result, $user);
+        }
+        return response([
+            'msg' => 'ok',
+            'data' => $result,
+        ]);
+    }
+
+    // 后端提交数据
+    public function submit(Request $request)
+    {
+        // Log::info('serverSubmitData:' . $request->input('node_id') . ':' . file_get_contents('php://input'));
+        $server = ServerTrojan::find($request->input('node_id'));
+        if (!$server) {
+            return response([
+                'ret' => 0,
+                'msg' => 'server is not found'
+            ]);
+        }
+        $data = file_get_contents('php://input');
+        $data = json_decode($data, true);
+        Cache::put(CacheKey::get('SERVER_ONLINE_USER', $server->id), count($data), 3600);
+        $serverService = new ServerService();
+        $userService = new UserService();
+        foreach ($data as $item) {
+            $u = $item['u'] * $server->rate;
+            $d = $item['d'] * $server->rate;
+            if (!$userService->trafficFetch($u, $d, $item['user_id'])) {
+                return response([
+                    'ret' => 0,
+                    'msg' => 'user fetch fail'
+                ]);
+            }
+
+            $serverService->log(
+                $item['user_id'],
+                $request->input('node_id'),
+                $item['u'],
+                $item['d'],
+                $server->rate,
+                'trojan'
+            );
+        }
+
+        return response([
+            'ret' => 1,
+            'msg' => 'ok'
+        ]);
+    }
+
+    // 后端获取配置
+    public function config(Request $request)
+    {
+        $nodeId = $request->input('node_id');
+        $localPort = $request->input('local_port');
+        if (empty($nodeId) || empty($localPort)) {
+            abort(500, '参数错误');
+        }
+        $serverService = new ServerService();
+        try {
+            $json = $serverService->getConfig($nodeId, $localPort);
+        } catch (\Exception $e) {
+            abort(500, $e->getMessage());
+        }
+
+        die(json_encode($json, JSON_UNESCAPED_UNICODE));
+    }
+}

+ 42 - 0
app/Http/Requests/Admin/ServerTrojanSave.php

@@ -0,0 +1,42 @@
+<?php
+
+namespace App\Http\Requests\Admin;
+
+use Illuminate\Foundation\Http\FormRequest;
+
+class ServerTrojanSave extends FormRequest
+{
+    CONST RULES = [
+        'show' => '',
+        'name' => 'required',
+        'group_id' => 'required|array',
+        'host' => 'required|regex:/^(?!:\/\/)(?=.{1,255}$)((.{1,63}\.){1,127}(?![0-9]*$)[a-z0-9-]+\.?)$/i',
+        'port' => 'required',
+        'tags' => 'nullable|array',
+        'rate' => 'required|numeric'
+    ];
+    /**
+     * Get the validation rules that apply to the request.
+     *
+     * @return array
+     */
+    public function rules()
+    {
+        return self::RULES;
+    }
+
+    public function messages()
+    {
+        return [
+            'name.required' => '节点名称不能为空',
+            'group_id.required' => '权限组不能为空',
+            'group_id.array' => '权限组格式不正确',
+            'host.required' => '节点地址不能为空',
+            'host.regex' => '节点地址必须为域名',
+            'port.required' => '连接端口不能为空',
+            'tags.array' => '标签格式不正确',
+            'rate.required' => '倍率不能为空',
+            'rate.numeric' => '倍率格式不正确'
+        ];
+    }
+}

+ 28 - 0
app/Http/Requests/Admin/ServerTrojanSort.php

@@ -0,0 +1,28 @@
+<?php
+
+namespace App\Http\Requests\Admin;
+
+use Illuminate\Foundation\Http\FormRequest;
+
+class ServerTrojanSort extends FormRequest
+{
+    /**
+     * Get the validation rules that apply to the request.
+     *
+     * @return array
+     */
+    public function rules()
+    {
+        return [
+            'server_ids' => 'required|array'
+        ];
+    }
+
+    public function messages()
+    {
+        return [
+            'server_ids.required' => '服务器ID不能为空',
+            'server_ids.array' => '服务器ID格式有误'
+        ];
+    }
+}

+ 28 - 0
app/Http/Requests/Admin/ServerTrojanUpdate.php

@@ -0,0 +1,28 @@
+<?php
+
+namespace App\Http\Requests\Admin;
+
+use Illuminate\Foundation\Http\FormRequest;
+
+class ServerTrojanUpdate extends FormRequest
+{
+    /**
+     * Get the validation rules that apply to the request.
+     *
+     * @return array
+     */
+
+    public function rules()
+    {
+        return [
+            'show' => 'in:0,1'
+        ];
+    }
+
+    public function messages()
+    {
+        return [
+            'show.in' => '显示状态格式不正确'
+        ];
+    }
+}

+ 1 - 1
app/Http/Requests/Admin/ServerSave.php → app/Http/Requests/Admin/ServerV2raySave.php

@@ -4,7 +4,7 @@ namespace App\Http\Requests\Admin;
 
 use Illuminate\Foundation\Http\FormRequest;
 
-class ServerSave extends FormRequest
+class ServerV2raySave extends FormRequest
 {
     CONST RULES = [
         'show' => '',

+ 1 - 1
app/Http/Requests/Admin/ServerSort.php → app/Http/Requests/Admin/ServerV2raySort.php

@@ -4,7 +4,7 @@ namespace App\Http\Requests\Admin;
 
 use Illuminate\Foundation\Http\FormRequest;
 
-class ServerSort extends FormRequest
+class ServerV2raySort extends FormRequest
 {
     /**
      * Get the validation rules that apply to the request.

+ 1 - 1
app/Http/Requests/Admin/ServerUpdate.php → app/Http/Requests/Admin/ServerV2rayUpdate.php

@@ -4,7 +4,7 @@ namespace App\Http\Requests\Admin;
 
 use Illuminate\Foundation\Http\FormRequest;
 
-class ServerUpdate extends FormRequest
+class ServerV2rayUpdate extends FormRequest
 {
     /**
      * Get the validation rules that apply to the request.

+ 21 - 7
app/Http/Routes/AdminRoute.php

@@ -23,16 +23,30 @@ class AdminRoute
             $router->post('/plan/update', 'Admin\\PlanController@update');
             $router->post('/plan/sort', 'Admin\\PlanController@sort');
             // Server
-            $router->get ('/server/fetch', 'Admin\\ServerController@fetch');
-            $router->post('/server/save', 'Admin\\ServerController@save');
             $router->get ('/server/group/fetch', 'Admin\\ServerController@groupFetch');
             $router->post('/server/group/save', 'Admin\\ServerController@groupSave');
             $router->post('/server/group/drop', 'Admin\\ServerController@groupDrop');
-            $router->post('/server/drop', 'Admin\\ServerController@drop');
-            $router->post('/server/update', 'Admin\\ServerController@update');
-            $router->post('/server/copy', 'Admin\\ServerController@copy');
-            $router->post('/server/viewConfig', 'Admin\\ServerController@viewConfig');
-            $router->post('/server/sort', 'Admin\\ServerController@sort');
+            $router->group([
+                'prefix' => 'server/trojan'
+            ], function ($router) {
+                $router->get ('fetch', 'Admin\\Server\\TrojanController@fetch');
+                $router->post('save', 'Admin\\Server\\TrojanController@save');
+                $router->post('drop', 'Admin\\Server\\TrojanController@drop');
+                $router->post('update', 'Admin\\Server\\TrojanController@update');
+                $router->post('copy', 'Admin\\Server\\TrojanController@copy');
+                $router->post('sort', 'Admin\\Server\\TrojanController@sort');
+            });
+            $router->group([
+                'prefix' => 'server/v2ray'
+            ], function ($router) {
+                $router->get ('fetch', 'Admin\\Server\\V2rayController@fetch');
+                $router->post('save', 'Admin\\Server\\V2rayController@save');
+                $router->post('drop', 'Admin\\Server\\V2rayController@drop');
+                $router->post('update', 'Admin\\Server\\V2rayController@update');
+                $router->post('copy', 'Admin\\Server\\V2rayController@copy');
+                $router->post('sort', 'Admin\\Server\\V2rayController@sort');
+                $router->post('viewConfig', 'Admin\\Server\\V2rayController@viewConfig');
+            });
             // Order
             $router->get ('/order/fetch', 'Admin\\OrderController@fetch');
             $router->post('/order/repair', 'Admin\\OrderController@repair');

+ 12 - 0
app/Models/ServerTrojan.php

@@ -0,0 +1,12 @@
+<?php
+
+namespace App\Models;
+
+use Illuminate\Database\Eloquent\Model;
+
+class ServerTrojan extends Model
+{
+    protected $table = 'v2_server_trojan';
+    protected $dateFormat = 'U';
+    protected $guarded = ['id'];
+}

+ 14 - 9
app/Services/OrderService.php

@@ -124,21 +124,26 @@ class OrderService
         $orderModel = Order::where('user_id', $user->id)
             ->where('cycle', '!=', 'reset_price')
             ->where('status', 3);
-        $surplusAmount = 0;
+        $orderSurplusMonth = 0;
+        $orderSurplusAmount = 0;
+        $userSurplusMonth = ($user->expired_at - time()) / 2678400;
         foreach ($orderModel->get() as $item) {
             // 兼容历史余留问题
             if ($item->cycle === 'onetime_price') continue;
-            $surplusMonth = strtotime("+ {$strToMonth[$item->cycle]}month", $item->created_at->format('U'));
-            if (!$surplusMonth) continue;
-            $surplusMonth = ($surplusMonth - time()) / 2678400 / $strToMonth[$item->cycle];
-            if ($surplusMonth > 0) {
-                $surplusAmount = $surplusAmount + ($item['total_amount'] + $item['balance_amount']) * $surplusMonth;
-            }
+            $orderSurplusMonth = $orderSurplusMonth + $strToMonth[$item->cycle];
+            $orderSurplusAmount = $orderSurplusAmount + ($item['total_amount'] + $item['balance_amount']);
+        }
+        $monthUnitPrice = $orderSurplusAmount / $orderSurplusMonth;
+        // 如果用户过期月大于订单过期月
+        if ($userSurplusMonth > $orderSurplusMonth) {
+            $orderSurplusAmount = $orderSurplusMonth * $monthUnitPrice;
+        } else {
+            $orderSurplusAmount = $userSurplusMonth * $monthUnitPrice;
         }
-        if (!$surplusAmount) {
+        if (!$orderSurplusAmount) {
             return;
         }
-        $order->surplus_amount = $surplusAmount > 0 ? $surplusAmount : 0;
+        $order->surplus_amount = $orderSurplusAmount > 0 ? $orderSurplusAmount : 0;
         $order->surplus_order_ids = json_encode(array_map(function ($v) { return $v['id'];}, $orderModel->get()->toArray()));
     }
 

+ 3 - 1
app/Services/ServerService.php

@@ -137,7 +137,7 @@ class ServerService
         }
     }
 
-    public function log(int $userId, int $serverId, int $u, int $d, float $rate)
+    public function log(int $userId, int $serverId, int $u, int $d, float $rate, string $method)
     {
         if (($u + $d) <= 10240) return;
         $timestamp = strtotime(date('Y-m-d H:0'));
@@ -146,6 +146,7 @@ class ServerService
             ->where('server_id', $serverId)
             ->where('user_id', $userId)
             ->where('rate', $rate)
+            ->where('method', $method)
             ->first();
         if ($serverLog) {
             $serverLog->u = $serverLog->u + $u;
@@ -159,6 +160,7 @@ class ServerService
             $serverLog->d = $d;
             $serverLog->rate = $rate;
             $serverLog->log_at = $timestamp;
+            $serverLog->method = $method;
             $serverLog->save();
         }
     }

+ 3 - 1
database/install.sql

@@ -165,6 +165,7 @@ CREATE TABLE `v2_server_log` (
   `u` varchar(255) NOT NULL,
   `d` varchar(255) NOT NULL,
   `rate` decimal(10,2) NOT NULL,
+  `method` varchar(255) NOT NULL,
   `log_at` int(11) NOT NULL,
   `created_at` int(11) NOT NULL,
   `updated_at` int(11) NOT NULL,
@@ -192,6 +193,7 @@ CREATE TABLE `v2_server_trojan` (
   `group_id` varchar(255) NOT NULL,
   `tags` varchar(255) DEFAULT NULL,
   `name` varchar(255) NOT NULL,
+  `rate` varchar(11) NOT NULL,
   `host` varchar(255) NOT NULL,
   `port` int(11) NOT NULL,
   `show` tinyint(1) NOT NULL DEFAULT '0',
@@ -279,4 +281,4 @@ CREATE TABLE `v2_user` (
 ) ENGINE=InnoDB DEFAULT CHARSET=utf8;
 
 
--- 2020-06-07 17:06:55
+-- 2020-06-11 12:38:33

+ 6 - 8
database/update.sql

@@ -118,14 +118,6 @@ CREATE TABLE `v2_tutorial` (
   `updated_at` int(11) NOT NULL
 );
 
-SET NAMES utf8mb4;
-
-INSERT INTO `v2_tutorial` (`id`, `title`, `description`, `icon`, `steps`, `show`, `created_at`, `updated_at`) VALUES
-(1,	'Windows',	'兼容 Windows 7 以上的版本',	'fab fa-2x fa-windows',	'[{\"default_area\":\"<div><div>下载 V2rayN 客户端。</div><div>下载完成后解压,解压完成后运行V2rayN</div><div>运行时请右键,以管理员身份运行</div></div>\",\"download_url\":\"/downloads/V2rayN.zip\"},{\"default_area\":\"<div>点击订阅按钮,选择订阅设置点击添加,输入如下内容后点击确定保存</div>\",\"safe_area\":\"<div>备注:<code onclick=\\\"safeAreaCopy(\'{{$app_name}}\')\\\">{{$app_name}}</code></div>\\n<div>地址(url):<code onclick=\\\"safeAreaCopy(\'{{$subscribe_url}}\')\\\">{{$subscribe_url}}</code></div>\",\"img_url\":\"https://i.loli.net/2019/11/21/UkcHNtERTnjLVS8.jpg\"},{\"default_area\":\"<div>点击订阅后,从服务器列表选择服务器</div>\",\"img_url\":\"https://i.loli.net/2019/11/21/BgPGFQ3kCSuIRjJ.jpg\"},{\"default_area\":\"<div>点击参数设置,找到Http代理,选择PAC模式后按确定保存即启动代理。</div>\",\"img_url\":\"https://i.loli.net/2019/11/21/vnVykKEFT8Lzo3f.jpg\"}]',	1,	1577972408,	1577980882),
-(2,	'Android',	'兼容 Android 6 以上的版本',	'fab fa-2x fa-android',	'[{\"default_area\":\"<div>下载 V2rayNG 客户端。</div>\",\"safe_area\":\"\",\"download_url\":\"/downloads/V2rayNG.apk\"},{\"default_area\":\"<div>打开 V2rayNG 点击左上角的菜单图标打开侧边栏,随后点击 订阅设置,点击右上角的➕按钮新增订阅。</div><div>按照下方内容进行填写,填写完毕后点击右上角的☑️按钮。</div>\",\"safe_area\":\"<div>备注:<code onclick=\\\"safeAreaCopy(\'{{$app_name}}\')\\\">{{$app_name}}</code></div>\\n<div>地址(url):<code onclick=\\\"safeAreaCopy(\'{{$subscribe_url}}\')\\\">{{$subscribe_url}}</code></div>\",\"download_url\":\"\",\"img_url\":\"https://i.loli.net/2019/11/21/ghuVkTe6LBqRxSO.jpg\"},{\"default_area\":\"<div>再次从侧边栏进入 设置 页面,点击 路由模式 将其更改为 \\b绕过局域网及大陆地址。</div>\",\"img_url\":\"https://i.loli.net/2019/11/21/Tf1AGoXZuhJrwOq.jpg\"},{\"default_area\":\"<div>随后从侧边栏回到 配置文件 页面,点击右上角的省略号图标选择更新订阅。</div>\",\"img_url\":\"https://i.loli.net/2019/11/21/UtfPShQXupRmB4L.jpg\"},{\"img_url\":\"https://i.loli.net/2019/11/21/ZkbNsSrAg3m5Dny.jpg\",\"default_area\":\"<div>点击选择您需要的节点,点击右下角的V字按钮即可连接。</div>\"}]',	1,	1577972534,	1577981610),
-(3,	'macOS',	'兼容 Yosemite 以上的版本',	'fab fa-2x fa-apple',	'[{\"default_area\":\"<div>下载 ClashX 客户端,安装后运行。</div>\",\"download_url\":\"/downloads/ClashX.dmg\",\"img_url\":\"https://i.loli.net/2019/11/20/uNGrjl2noCL1f5B.jpg\"},{\"default_area\":\"<div>点击通知栏 ClashX 图标保持选中状态,按快捷键 ⌘+M(订阅快捷键),在弹出的窗口点击添加输入下方信息</div>\",\"safe_area\":\"<div>Url:<code onclick=\\\"safeAreaCopy(\'{{$subscribe_url}}\')\\\">{{$subscribe_url}}</code></div>\\n<div>Config Name:<code onclick=\\\"safeAreaCopy(\'{{$app_name}}\')\\\">{{$app_name}}</code></div>\",\"img_url\":\"https://i.loli.net/2019/11/20/8eB13mRbFuszwxg.jpg\"},{\"default_area\":\"<div>点击通知栏 ClashX 图标保持选中状态,按快捷键 ⌘+S(设置为系统代理快捷键),即连接完成</div>\"}]',	1,	1577979855,	1577981646),
-(4,	'iOS',	'兼容 iOS 9 以上的版本',	'fab fa-2x fa-apple',	'[{\"default_area\":\"<div>iOS上使用请在iOS浏览器中打开本页</div>\"},{\"default_area\":\"<div>在 App Store 登录本站提供的美区 Apple ID 下载客户端。</div><div>为了保护您的隐私,请勿在手机设置里直接登录,仅在 App Store 登录即可。</div><div>登陆完成后点击下方下载会自动唤起下载。</div>\",\"safe_area\":\"<div>Apple ID:<code onclick=\\\"safeAreaCopy(\'{{$apple_id}}\')\\\">{{$apple_id}}</code></div><div>密码:<code onclick=\\\"safeAreaCopy(\'{{$apple_id_password}}\')\\\">点击复制密码</code></div>\",\"download_url\":\"https://apps.apple.com/us/app/shadowrocket/id932747118\",\"img_url\":\"https://i.loli.net/2019/11/21/5idkjJ61stWgREV.jpg\"},{\"default_area\":\"<div>待客户端安装完成后,点击下方一键订阅按钮会自动唤起并进行订阅</div>\",\"safe_area\":\"\",\"img_url\":\"https://i.loli.net/2019/11/21/ZcqlNMb3eg5Uhxd.jpg\",\"download_url\":\"shadowrocket://add/sub://{{$b64_subscribe_url}}?remark={{$app_name}}\"},{\"default_area\":\"<div>选择节点进行链接,首次链接过程授权窗口请一路允许。</div>\",\"img_url\":\"https://i.loli.net/2019/11/21/9Zdxksr7Ey6hjlm.jpg\"}]',	1,	1577982016,	1577983283);
-
 ALTER TABLE `v2_server_log`
 CHANGE `rate` `rate` decimal(10,2) NOT NULL AFTER `d`;
 
@@ -282,3 +274,9 @@ DROP `online`;
 
 ALTER TABLE `v2_user`
 CHANGE `v2ray_uuid` `uuid` varchar(36) COLLATE 'utf8_general_ci' NOT NULL AFTER `last_login_ip`;
+
+ALTER TABLE `v2_server_trojan`
+ADD `rate` varchar(11) COLLATE 'utf8mb4_general_ci' NOT NULL AFTER `name`;
+
+ALTER TABLE `v2_server_log`
+ADD `method` varchar(255) NOT NULL AFTER `rate`;

+ 524 - 0
resources/rules/app.clash.yaml

@@ -0,0 +1,524 @@
+port: 8890
+socks-port: 8891
+allow-lan: false
+mode: Rule
+log-level: info
+external-controller: 127.0.0.1:9091
+experimental:
+  ignore-resolve-fail: true
+dns:
+  enable: true
+  ipv6: false
+  enhanced-mode: redir-host
+  nameserver:
+    - 1.2.4.8
+    - 223.5.5.5
+  fallback:
+    - tls://1.0.0.1:853
+    - tls://dns.google:853
+Proxy:
+
+Proxy Group:
+  - { name: "SELECT", type: select, proxies: ["自动选择", "故障转移"] }
+  - { name: "自动选择", type: url-test, proxies: [], url: "http://www.gstatic.com/generate_204", interval: 86400 }
+  - { name: "故障转移", type: fallback, proxies: [], url: "http://www.gstatic.com/generate_204", interval: 7200 }
+
+Rule:
+  # Apple
+  - DOMAIN,safebrowsing.urlsec.qq.com,DIRECT # 如果您并不信任此服务提供商或防止其下载消耗过多带宽资源,可以进入 Safari 设置,关闭 Fraudulent Website Warning 功能,并使用 REJECT 策略。
+  - DOMAIN,safebrowsing.googleapis.com,DIRECT # 如果您并不信任此服务提供商或防止其下载消耗过多带宽资源,可以进入 Safari 设置,关闭 Fraudulent Website Warning 功能,并使用 REJECT 策略。
+  - DOMAIN,ocsp.apple.com,SELECT
+  - DOMAIN-SUFFIX,digicert.com,SELECT
+  - DOMAIN-SUFFIX,entrust.net,SELECT
+  - DOMAIN,ocsp.verisign.net,SELECT
+  - DOMAIN-SUFFIX,apps.apple.com,SELECT
+  - DOMAIN,itunes.apple.com,SELECT
+  - DOMAIN-SUFFIX,blobstore.apple.com,SELECT
+  - DOMAIN-SUFFIX,music.apple.com,DIRECT
+  - DOMAIN-SUFFIX,mzstatic.com,DIRECT
+  - DOMAIN-SUFFIX,itunes.apple.com,DIRECT
+  - DOMAIN-SUFFIX,icloud.com,DIRECT
+  - DOMAIN-SUFFIX,icloud-content.com,DIRECT
+  - DOMAIN-SUFFIX,me.com,DIRECT
+  - DOMAIN-SUFFIX,mzstatic.com,DIRECT
+  - DOMAIN-SUFFIX,akadns.net,DIRECT
+  - DOMAIN-SUFFIX,aaplimg.com,DIRECT
+  - DOMAIN-SUFFIX,cdn-apple.com,DIRECT
+  - DOMAIN-SUFFIX,apple.com,DIRECT
+  - DOMAIN-SUFFIX,apple-cloudkit.com,DIRECT
+  # - DOMAIN,e.crashlytics.com,REJECT //注释此选项有助于大多数App开发者分析崩溃信息;如果您拒绝一切崩溃数据统计、搜集,请取消 # 注释。
+
+
+  # 自定义规则
+  ## 您可以在此处插入您补充的自定义规则(请注意保持缩进)
+
+  # 国内网站
+  - DOMAIN-SUFFIX,cn,DIRECT
+  - DOMAIN-KEYWORD,-cn,DIRECT
+
+  - DOMAIN-SUFFIX,126.com,DIRECT
+  - DOMAIN-SUFFIX,126.net,DIRECT
+  - DOMAIN-SUFFIX,127.net,DIRECT
+  - DOMAIN-SUFFIX,163.com,DIRECT
+  - DOMAIN-SUFFIX,360buyimg.com,DIRECT
+  - DOMAIN-SUFFIX,36kr.com,DIRECT
+  - DOMAIN-SUFFIX,acfun.tv,DIRECT
+  - DOMAIN-SUFFIX,air-matters.com,DIRECT
+  - DOMAIN-SUFFIX,aixifan.com,DIRECT
+  - DOMAIN-SUFFIX,akamaized.net,DIRECT
+  - DOMAIN-KEYWORD,alicdn,DIRECT
+  - DOMAIN-KEYWORD,alipay,DIRECT
+  - DOMAIN-KEYWORD,taobao,DIRECT
+  - DOMAIN-SUFFIX,amap.com,DIRECT
+  - DOMAIN-SUFFIX,autonavi.com,DIRECT
+  - DOMAIN-KEYWORD,baidu,DIRECT
+  - DOMAIN-SUFFIX,bdimg.com,DIRECT
+  - DOMAIN-SUFFIX,bdstatic.com,DIRECT
+  - DOMAIN-SUFFIX,bilibili.com,DIRECT
+  - DOMAIN-SUFFIX,bilivideo.com,DIRECT
+  - DOMAIN-SUFFIX,caiyunapp.com,DIRECT
+  - DOMAIN-SUFFIX,clouddn.com,DIRECT
+  - DOMAIN-SUFFIX,cnbeta.com,DIRECT
+  - DOMAIN-SUFFIX,cnbetacdn.com,DIRECT
+  - DOMAIN-SUFFIX,cootekservice.com,DIRECT
+  - DOMAIN-SUFFIX,csdn.net,DIRECT
+  - DOMAIN-SUFFIX,ctrip.com,DIRECT
+  - DOMAIN-SUFFIX,dgtle.com,DIRECT
+  - DOMAIN-SUFFIX,dianping.com,DIRECT
+  - DOMAIN-SUFFIX,douban.com,DIRECT
+  - DOMAIN-SUFFIX,doubanio.com,DIRECT
+  - DOMAIN-SUFFIX,duokan.com,DIRECT
+  - DOMAIN-SUFFIX,easou.com,DIRECT
+  - DOMAIN-SUFFIX,ele.me,DIRECT
+  - DOMAIN-SUFFIX,feng.com,DIRECT
+  - DOMAIN-SUFFIX,fir.im,DIRECT
+  - DOMAIN-SUFFIX,frdic.com,DIRECT
+  - DOMAIN-SUFFIX,g-cores.com,DIRECT
+  - DOMAIN-SUFFIX,godic.net,DIRECT
+  - DOMAIN-SUFFIX,gtimg.com,DIRECT
+  - DOMAIN,cdn.hockeyapp.net,DIRECT
+  - DOMAIN-SUFFIX,hdslb.com,DIRECT
+  - DOMAIN-SUFFIX,hongxiu.com,DIRECT
+  - DOMAIN-SUFFIX,hxcdn.net,DIRECT
+  - DOMAIN-SUFFIX,iciba.com,DIRECT
+  - DOMAIN-SUFFIX,ifeng.com,DIRECT
+  - DOMAIN-SUFFIX,ifengimg.com,DIRECT
+  - DOMAIN-SUFFIX,ipip.net,DIRECT
+  - DOMAIN-SUFFIX,iqiyi.com,DIRECT
+  - DOMAIN-SUFFIX,jd.com,DIRECT
+  - DOMAIN-SUFFIX,jianshu.com,DIRECT
+  - DOMAIN-SUFFIX,knewone.com,DIRECT
+  - DOMAIN-SUFFIX,le.com,DIRECT
+  - DOMAIN-SUFFIX,lecloud.com,DIRECT
+  - DOMAIN-SUFFIX,lemicp.com,DIRECT
+  - DOMAIN-SUFFIX,licdn.com,DIRECT
+  - DOMAIN-SUFFIX,linkedin.com,DIRECT
+  - DOMAIN-SUFFIX,luoo.net,DIRECT
+  - DOMAIN-SUFFIX,meituan.com,DIRECT
+  - DOMAIN-SUFFIX,meituan.net,DIRECT
+  - DOMAIN-SUFFIX,mi.com,DIRECT
+  - DOMAIN-SUFFIX,miaopai.com,DIRECT
+  - DOMAIN-SUFFIX,microsoft.com,DIRECT
+  - DOMAIN-SUFFIX,microsoftonline.com,DIRECT
+  - DOMAIN-SUFFIX,miui.com,DIRECT
+  - DOMAIN-SUFFIX,miwifi.com,DIRECT
+  - DOMAIN-SUFFIX,mob.com,DIRECT
+  - DOMAIN-SUFFIX,netease.com,DIRECT
+  - DOMAIN-SUFFIX,office.com,DIRECT
+  - DOMAIN-SUFFIX,office365.com,DIRECT
+  - DOMAIN-KEYWORD,officecdn,DIRECT
+  - DOMAIN-SUFFIX,oschina.net,DIRECT
+  - DOMAIN-SUFFIX,ppsimg.com,DIRECT
+  - DOMAIN-SUFFIX,pstatp.com,DIRECT
+  - DOMAIN-SUFFIX,qcloud.com,DIRECT
+  - DOMAIN-SUFFIX,qdaily.com,DIRECT
+  - DOMAIN-SUFFIX,qdmm.com,DIRECT
+  - DOMAIN-SUFFIX,qhimg.com,DIRECT
+  - DOMAIN-SUFFIX,qhres.com,DIRECT
+  - DOMAIN-SUFFIX,qidian.com,DIRECT
+  - DOMAIN-SUFFIX,qihucdn.com,DIRECT
+  - DOMAIN-SUFFIX,qiniu.com,DIRECT
+  - DOMAIN-SUFFIX,qiniucdn.com,DIRECT
+  - DOMAIN-SUFFIX,qiyipic.com,DIRECT
+  - DOMAIN-SUFFIX,qq.com,DIRECT
+  - DOMAIN-SUFFIX,qqurl.com,DIRECT
+  - DOMAIN-SUFFIX,rarbg.to,DIRECT
+  - DOMAIN-SUFFIX,ruguoapp.com,DIRECT
+  - DOMAIN-SUFFIX,segmentfault.com,DIRECT
+  - DOMAIN-SUFFIX,sinaapp.com,DIRECT
+  - DOMAIN-SUFFIX,smzdm.com,DIRECT
+  - DOMAIN-SUFFIX,snapdrop.net,DIRECT
+  - DOMAIN-SUFFIX,sogou.com,DIRECT
+  - DOMAIN-SUFFIX,sogoucdn.com,DIRECT
+  - DOMAIN-SUFFIX,sohu.com,DIRECT
+  - DOMAIN-SUFFIX,soku.com,DIRECT
+  - DOMAIN-SUFFIX,speedtest.net,DIRECT
+  - DOMAIN-SUFFIX,sspai.com,DIRECT
+  - DOMAIN-SUFFIX,suning.com,DIRECT
+  - DOMAIN-SUFFIX,taobao.com,DIRECT
+  - DOMAIN-SUFFIX,tencent.com,DIRECT
+  - DOMAIN-SUFFIX,tenpay.com,DIRECT
+  - DOMAIN-SUFFIX,tianyancha.com,DIRECT
+  - DOMAIN-SUFFIX,tmall.com,DIRECT
+  - DOMAIN-SUFFIX,tudou.com,DIRECT
+  - DOMAIN-SUFFIX,umetrip.com,DIRECT
+  - DOMAIN-SUFFIX,upaiyun.com,DIRECT
+  - DOMAIN-SUFFIX,upyun.com,DIRECT
+  - DOMAIN-SUFFIX,veryzhun.com,DIRECT
+  - DOMAIN-SUFFIX,weather.com,DIRECT
+  - DOMAIN-SUFFIX,weibo.com,DIRECT
+  - DOMAIN-SUFFIX,xiami.com,DIRECT
+  - DOMAIN-SUFFIX,xiami.net,DIRECT
+  - DOMAIN-SUFFIX,xiaomicp.com,DIRECT
+  - DOMAIN-SUFFIX,ximalaya.com,DIRECT
+  - DOMAIN-SUFFIX,xmcdn.com,DIRECT
+  - DOMAIN-SUFFIX,xunlei.com,DIRECT
+  - DOMAIN-SUFFIX,yhd.com,DIRECT
+  - DOMAIN-SUFFIX,yihaodianimg.com,DIRECT
+  - DOMAIN-SUFFIX,yinxiang.com,DIRECT
+  - DOMAIN-SUFFIX,ykimg.com,DIRECT
+  - DOMAIN-SUFFIX,youdao.com,DIRECT
+  - DOMAIN-SUFFIX,youku.com,DIRECT
+  - DOMAIN-SUFFIX,zealer.com,DIRECT
+  - DOMAIN-SUFFIX,zhihu.com,DIRECT
+  - DOMAIN-SUFFIX,zhimg.com,DIRECT
+  - DOMAIN-SUFFIX,zimuzu.tv,DIRECT
+  - DOMAIN-SUFFIX,zoho.com,DIRECT
+
+  # 抗 DNS 污染
+  - DOMAIN-KEYWORD,amazon,SELECT
+  - DOMAIN-KEYWORD,google,SELECT
+  - DOMAIN-KEYWORD,gmail,SELECT
+  - DOMAIN-KEYWORD,youtube,SELECT
+  - DOMAIN-KEYWORD,facebook,SELECT
+  - DOMAIN-SUFFIX,fb.me,SELECT
+  - DOMAIN-SUFFIX,fbcdn.net,SELECT
+  - DOMAIN-KEYWORD,twitter,SELECT
+  - DOMAIN-KEYWORD,instagram,SELECT
+  - DOMAIN-KEYWORD,dropbox,SELECT
+  - DOMAIN-SUFFIX,twimg.com,SELECT
+  - DOMAIN-KEYWORD,blogspot,SELECT
+  - DOMAIN-SUFFIX,youtu.be,SELECT
+  - DOMAIN-KEYWORD,whatsapp,SELECT
+
+  # 常见广告域名屏蔽
+  - DOMAIN-KEYWORD,admarvel,REJECT
+  - DOMAIN-KEYWORD,admaster,REJECT
+  - DOMAIN-KEYWORD,adsage,REJECT
+  - DOMAIN-KEYWORD,adsmogo,REJECT
+  - DOMAIN-KEYWORD,adsrvmedia,REJECT
+  - DOMAIN-KEYWORD,adwords,REJECT
+  - DOMAIN-KEYWORD,adservice,REJECT
+  - DOMAIN-KEYWORD,domob,REJECT
+  - DOMAIN-KEYWORD,duomeng,REJECT
+  - DOMAIN-KEYWORD,dwtrack,REJECT
+  - DOMAIN-KEYWORD,guanggao,REJECT
+  - DOMAIN-KEYWORD,lianmeng,REJECT
+  - DOMAIN-SUFFIX,mmstat.com,REJECT
+  - DOMAIN-KEYWORD,omgmta,REJECT
+  - DOMAIN-KEYWORD,openx,REJECT
+  - DOMAIN-KEYWORD,partnerad,REJECT
+  - DOMAIN-KEYWORD,pingfore,REJECT
+  - DOMAIN-KEYWORD,supersonicads,REJECT
+  - DOMAIN-KEYWORD,tracking,REJECT
+  - DOMAIN-KEYWORD,uedas,REJECT
+  - DOMAIN-KEYWORD,umeng,REJECT
+  - DOMAIN-KEYWORD,usage,REJECT
+  - DOMAIN-KEYWORD,wlmonitor,REJECT
+  - DOMAIN-KEYWORD,zjtoolbar,REJECT
+
+  # 国外网站
+  - DOMAIN-SUFFIX,9to5mac.com,SELECT
+  - DOMAIN-SUFFIX,abpchina.org,SELECT
+  - DOMAIN-SUFFIX,adblockplus.org,SELECT
+  - DOMAIN-SUFFIX,adobe.com,SELECT
+  - DOMAIN-SUFFIX,alfredapp.com,SELECT
+  - DOMAIN-SUFFIX,amplitude.com,SELECT
+  - DOMAIN-SUFFIX,ampproject.org,SELECT
+  - DOMAIN-SUFFIX,android.com,SELECT
+  - DOMAIN-SUFFIX,angularjs.org,SELECT
+  - DOMAIN-SUFFIX,aolcdn.com,SELECT
+  - DOMAIN-SUFFIX,apkpure.com,SELECT
+  - DOMAIN-SUFFIX,appledaily.com,SELECT
+  - DOMAIN-SUFFIX,appshopper.com,SELECT
+  - DOMAIN-SUFFIX,appspot.com,SELECT
+  - DOMAIN-SUFFIX,arcgis.com,SELECT
+  - DOMAIN-SUFFIX,archive.org,SELECT
+  - DOMAIN-SUFFIX,armorgames.com,SELECT
+  - DOMAIN-SUFFIX,aspnetcdn.com,SELECT
+  - DOMAIN-SUFFIX,att.com,SELECT
+  - DOMAIN-SUFFIX,awsstatic.com,SELECT
+  - DOMAIN-SUFFIX,azureedge.net,SELECT
+  - DOMAIN-SUFFIX,azurewebsites.net,SELECT
+  - DOMAIN-SUFFIX,bing.com,SELECT
+  - DOMAIN-SUFFIX,bintray.com,SELECT
+  - DOMAIN-SUFFIX,bit.com,SELECT
+  - DOMAIN-SUFFIX,bit.ly,SELECT
+  - DOMAIN-SUFFIX,bitbucket.org,SELECT
+  - DOMAIN-SUFFIX,bjango.com,SELECT
+  - DOMAIN-SUFFIX,bkrtx.com,SELECT
+  - DOMAIN-SUFFIX,blog.com,SELECT
+  - DOMAIN-SUFFIX,blogcdn.com,SELECT
+  - DOMAIN-SUFFIX,blogger.com,SELECT
+  - DOMAIN-SUFFIX,blogsmithmedia.com,SELECT
+  - DOMAIN-SUFFIX,blogspot.com,SELECT
+  - DOMAIN-SUFFIX,blogspot.hk,SELECT
+  - DOMAIN-SUFFIX,bloomberg.com,SELECT
+  - DOMAIN-SUFFIX,box.com,SELECT
+  - DOMAIN-SUFFIX,box.net,SELECT
+  - DOMAIN-SUFFIX,cachefly.net,SELECT
+  - DOMAIN-SUFFIX,chromium.org,SELECT
+  - DOMAIN-SUFFIX,cl.ly,SELECT
+  - DOMAIN-SUFFIX,cloudflare.com,SELECT
+  - DOMAIN-SUFFIX,cloudfront.net,SELECT
+  - DOMAIN-SUFFIX,cloudmagic.com,SELECT
+  - DOMAIN-SUFFIX,cmail19.com,SELECT
+  - DOMAIN-SUFFIX,cnet.com,SELECT
+  - DOMAIN-SUFFIX,cocoapods.org,SELECT
+  - DOMAIN-SUFFIX,comodoca.com,SELECT
+  - DOMAIN-SUFFIX,crashlytics.com,SELECT
+  - DOMAIN-SUFFIX,culturedcode.com,SELECT
+  - DOMAIN-SUFFIX,d.pr,SELECT
+  - DOMAIN-SUFFIX,danilo.to,SELECT
+  - DOMAIN-SUFFIX,dayone.me,SELECT
+  - DOMAIN-SUFFIX,db.tt,SELECT
+  - DOMAIN-SUFFIX,deskconnect.com,SELECT
+  - DOMAIN-SUFFIX,disq.us,SELECT
+  - DOMAIN-SUFFIX,disqus.com,SELECT
+  - DOMAIN-SUFFIX,disquscdn.com,SELECT
+  - DOMAIN-SUFFIX,dnsimple.com,SELECT
+  - DOMAIN-SUFFIX,docker.com,SELECT
+  - DOMAIN-SUFFIX,dribbble.com,SELECT
+  - DOMAIN-SUFFIX,droplr.com,SELECT
+  - DOMAIN-SUFFIX,duckduckgo.com,SELECT
+  - DOMAIN-SUFFIX,dueapp.com,SELECT
+  - DOMAIN-SUFFIX,dytt8.net,SELECT
+  - DOMAIN-SUFFIX,edgecastcdn.net,SELECT
+  - DOMAIN-SUFFIX,edgekey.net,SELECT
+  - DOMAIN-SUFFIX,edgesuite.net,SELECT
+  - DOMAIN-SUFFIX,engadget.com,SELECT
+  - DOMAIN-SUFFIX,entrust.net,SELECT
+  - DOMAIN-SUFFIX,eurekavpt.com,SELECT
+  - DOMAIN-SUFFIX,evernote.com,SELECT
+  - DOMAIN-SUFFIX,fabric.io,SELECT
+  - DOMAIN-SUFFIX,fast.com,SELECT
+  - DOMAIN-SUFFIX,fastly.net,SELECT
+  - DOMAIN-SUFFIX,fc2.com,SELECT
+  - DOMAIN-SUFFIX,feedburner.com,SELECT
+  - DOMAIN-SUFFIX,feedly.com,SELECT
+  - DOMAIN-SUFFIX,feedsportal.com,SELECT
+  - DOMAIN-SUFFIX,fiftythree.com,SELECT
+  - DOMAIN-SUFFIX,firebaseio.com,SELECT
+  - DOMAIN-SUFFIX,flexibits.com,SELECT
+  - DOMAIN-SUFFIX,flickr.com,SELECT
+  - DOMAIN-SUFFIX,flipboard.com,SELECT
+  - DOMAIN-SUFFIX,g.co,SELECT
+  - DOMAIN-SUFFIX,gabia.net,SELECT
+  - DOMAIN-SUFFIX,geni.us,SELECT
+  - DOMAIN-SUFFIX,gfx.ms,SELECT
+  - DOMAIN-SUFFIX,ggpht.com,SELECT
+  - DOMAIN-SUFFIX,ghostnoteapp.com,SELECT
+  - DOMAIN-SUFFIX,git.io,SELECT
+  - DOMAIN-KEYWORD,github,SELECT
+  - DOMAIN-SUFFIX,globalsign.com,SELECT
+  - DOMAIN-SUFFIX,gmodules.com,SELECT
+  - DOMAIN-SUFFIX,godaddy.com,SELECT
+  - DOMAIN-SUFFIX,golang.org,SELECT
+  - DOMAIN-SUFFIX,gongm.in,SELECT
+  - DOMAIN-SUFFIX,goo.gl,SELECT
+  - DOMAIN-SUFFIX,goodreaders.com,SELECT
+  - DOMAIN-SUFFIX,goodreads.com,SELECT
+  - DOMAIN-SUFFIX,gravatar.com,SELECT
+  - DOMAIN-SUFFIX,gstatic.com,SELECT
+  - DOMAIN-SUFFIX,gvt0.com,SELECT
+  - DOMAIN-SUFFIX,hockeyapp.net,SELECT
+  - DOMAIN-SUFFIX,hotmail.com,SELECT
+  - DOMAIN-SUFFIX,icons8.com,SELECT
+  - DOMAIN-SUFFIX,ifixit.com,SELECT
+  - DOMAIN-SUFFIX,ift.tt,SELECT
+  - DOMAIN-SUFFIX,ifttt.com,SELECT
+  - DOMAIN-SUFFIX,iherb.com,SELECT
+  - DOMAIN-SUFFIX,imageshack.us,SELECT
+  - DOMAIN-SUFFIX,img.ly,SELECT
+  - DOMAIN-SUFFIX,imgur.com,SELECT
+  - DOMAIN-SUFFIX,imore.com,SELECT
+  - DOMAIN-SUFFIX,instapaper.com,SELECT
+  - DOMAIN-SUFFIX,ipn.li,SELECT
+  - DOMAIN-SUFFIX,is.gd,SELECT
+  - DOMAIN-SUFFIX,issuu.com,SELECT
+  - DOMAIN-SUFFIX,itgonglun.com,SELECT
+  - DOMAIN-SUFFIX,itun.es,SELECT
+  - DOMAIN-SUFFIX,ixquick.com,SELECT
+  - DOMAIN-SUFFIX,j.mp,SELECT
+  - DOMAIN-SUFFIX,js.revsci.net,SELECT
+  - DOMAIN-SUFFIX,jshint.com,SELECT
+  - DOMAIN-SUFFIX,jtvnw.net,SELECT
+  - DOMAIN-SUFFIX,justgetflux.com,SELECT
+  - DOMAIN-SUFFIX,kat.cr,SELECT
+  - DOMAIN-SUFFIX,klip.me,SELECT
+  - DOMAIN-SUFFIX,libsyn.com,SELECT
+  - DOMAIN-SUFFIX,linode.com,SELECT
+  - DOMAIN-SUFFIX,lithium.com,SELECT
+  - DOMAIN-SUFFIX,littlehj.com,SELECT
+  - DOMAIN-SUFFIX,live.com,SELECT
+  - DOMAIN-SUFFIX,live.net,SELECT
+  - DOMAIN-SUFFIX,livefilestore.com,SELECT
+  - DOMAIN-SUFFIX,llnwd.net,SELECT
+  - DOMAIN-SUFFIX,macid.co,SELECT
+  - DOMAIN-SUFFIX,macromedia.com,SELECT
+  - DOMAIN-SUFFIX,macrumors.com,SELECT
+  - DOMAIN-SUFFIX,mashable.com,SELECT
+  - DOMAIN-SUFFIX,mathjax.org,SELECT
+  - DOMAIN-SUFFIX,medium.com,SELECT
+  - DOMAIN-SUFFIX,mega.co.nz,SELECT
+  - DOMAIN-SUFFIX,mega.nz,SELECT
+  - DOMAIN-SUFFIX,megaupload.com,SELECT
+  - DOMAIN-SUFFIX,microsofttranslator.com,SELECT
+  - DOMAIN-SUFFIX,mindnode.com,SELECT
+  - DOMAIN-SUFFIX,mobile01.com,SELECT
+  - DOMAIN-SUFFIX,modmyi.com,SELECT
+  - DOMAIN-SUFFIX,msedge.net,SELECT
+  - DOMAIN-SUFFIX,myfontastic.com,SELECT
+  - DOMAIN-SUFFIX,name.com,SELECT
+  - DOMAIN-SUFFIX,nextmedia.com,SELECT
+  - DOMAIN-SUFFIX,nsstatic.net,SELECT
+  - DOMAIN-SUFFIX,nssurge.com,SELECT
+  - DOMAIN-SUFFIX,nyt.com,SELECT
+  - DOMAIN-SUFFIX,nytimes.com,SELECT
+  - DOMAIN-SUFFIX,omnigroup.com,SELECT
+  - DOMAIN-SUFFIX,onedrive.com,SELECT
+  - DOMAIN-SUFFIX,onenote.com,SELECT
+  - DOMAIN-SUFFIX,ooyala.com,SELECT
+  - DOMAIN-SUFFIX,openvpn.net,SELECT
+  - DOMAIN-SUFFIX,openwrt.org,SELECT
+  - DOMAIN-SUFFIX,orkut.com,SELECT
+  - DOMAIN-SUFFIX,osxdaily.com,SELECT
+  - DOMAIN-SUFFIX,outlook.com,SELECT
+  - DOMAIN-SUFFIX,ow.ly,SELECT
+  - DOMAIN-SUFFIX,paddleapi.com,SELECT
+  - DOMAIN-SUFFIX,parallels.com,SELECT
+  - DOMAIN-SUFFIX,parse.com,SELECT
+  - DOMAIN-SUFFIX,pdfexpert.com,SELECT
+  - DOMAIN-SUFFIX,periscope.tv,SELECT
+  - DOMAIN-SUFFIX,pinboard.in,SELECT
+  - DOMAIN-SUFFIX,pinterest.com,SELECT
+  - DOMAIN-SUFFIX,pixelmator.com,SELECT
+  - DOMAIN-SUFFIX,pixiv.net,SELECT
+  - DOMAIN-SUFFIX,playpcesor.com,SELECT
+  - DOMAIN-SUFFIX,playstation.com,SELECT
+  - DOMAIN-SUFFIX,playstation.com.hk,SELECT
+  - DOMAIN-SUFFIX,playstation.net,SELECT
+  - DOMAIN-SUFFIX,playstationnetwork.com,SELECT
+  - DOMAIN-SUFFIX,pushwoosh.com,SELECT
+  - DOMAIN-SUFFIX,rime.im,SELECT
+  - DOMAIN-SUFFIX,servebom.com,SELECT
+  - DOMAIN-SUFFIX,sfx.ms,SELECT
+  - DOMAIN-SUFFIX,shadowsocks.org,SELECT
+  - DOMAIN-SUFFIX,sharethis.com,SELECT
+  - DOMAIN-SUFFIX,shazam.com,SELECT
+  - DOMAIN-SUFFIX,skype.com,SELECT
+  - DOMAIN-SUFFIX,smartdnsSELECT.com,SELECT
+  - DOMAIN-SUFFIX,smartmailcloud.com,SELECT
+  - DOMAIN-SUFFIX,sndcdn.com,SELECT
+  - DOMAIN-SUFFIX,sony.com,SELECT
+  - DOMAIN-SUFFIX,soundcloud.com,SELECT
+  - DOMAIN-SUFFIX,sourceforge.net,SELECT
+  - DOMAIN-SUFFIX,spotify.com,SELECT
+  - DOMAIN-SUFFIX,squarespace.com,SELECT
+  - DOMAIN-SUFFIX,sstatic.net,SELECT
+  - DOMAIN-SUFFIX,st.luluku.pw,SELECT
+  - DOMAIN-SUFFIX,stackoverflow.com,SELECT
+  - DOMAIN-SUFFIX,startpage.com,SELECT
+  - DOMAIN-SUFFIX,staticflickr.com,SELECT
+  - DOMAIN-SUFFIX,steamcommunity.com,SELECT
+  - DOMAIN-SUFFIX,symauth.com,SELECT
+  - DOMAIN-SUFFIX,symcb.com,SELECT
+  - DOMAIN-SUFFIX,symcd.com,SELECT
+  - DOMAIN-SUFFIX,tapbots.com,SELECT
+  - DOMAIN-SUFFIX,tapbots.net,SELECT
+  - DOMAIN-SUFFIX,tdesktop.com,SELECT
+  - DOMAIN-SUFFIX,techcrunch.com,SELECT
+  - DOMAIN-SUFFIX,techsmith.com,SELECT
+  - DOMAIN-SUFFIX,thepiratebay.org,SELECT
+  - DOMAIN-SUFFIX,theverge.com,SELECT
+  - DOMAIN-SUFFIX,time.com,SELECT
+  - DOMAIN-SUFFIX,timeinc.net,SELECT
+  - DOMAIN-SUFFIX,tiny.cc,SELECT
+  - DOMAIN-SUFFIX,tinypic.com,SELECT
+  - DOMAIN-SUFFIX,tmblr.co,SELECT
+  - DOMAIN-SUFFIX,todoist.com,SELECT
+  - DOMAIN-SUFFIX,trello.com,SELECT
+  - DOMAIN-SUFFIX,trustasiassl.com,SELECT
+  - DOMAIN-SUFFIX,tumblr.co,SELECT
+  - DOMAIN-SUFFIX,tumblr.com,SELECT
+  - DOMAIN-SUFFIX,tweetdeck.com,SELECT
+  - DOMAIN-SUFFIX,tweetmarker.net,SELECT
+  - DOMAIN-SUFFIX,twitch.tv,SELECT
+  - DOMAIN-SUFFIX,txmblr.com,SELECT
+  - DOMAIN-SUFFIX,typekit.net,SELECT
+  - DOMAIN-SUFFIX,ubertags.com,SELECT
+  - DOMAIN-SUFFIX,ublock.org,SELECT
+  - DOMAIN-SUFFIX,ubnt.com,SELECT
+  - DOMAIN-SUFFIX,ulyssesapp.com,SELECT
+  - DOMAIN-SUFFIX,urchin.com,SELECT
+  - DOMAIN-SUFFIX,usertrust.com,SELECT
+  - DOMAIN-SUFFIX,v.gd,SELECT
+  - DOMAIN-SUFFIX,v2ex.com,SELECT
+  - DOMAIN-SUFFIX,vimeo.com,SELECT
+  - DOMAIN-SUFFIX,vimeocdn.com,SELECT
+  - DOMAIN-SUFFIX,vine.co,SELECT
+  - DOMAIN-SUFFIX,vivaldi.com,SELECT
+  - DOMAIN-SUFFIX,vox-cdn.com,SELECT
+  - DOMAIN-SUFFIX,vsco.co,SELECT
+  - DOMAIN-SUFFIX,vultr.com,SELECT
+  - DOMAIN-SUFFIX,w.org,SELECT
+  - DOMAIN-SUFFIX,w3schools.com,SELECT
+  - DOMAIN-SUFFIX,webtype.com,SELECT
+  - DOMAIN-SUFFIX,wikiwand.com,SELECT
+  - DOMAIN-SUFFIX,wikileaks.org,SELECT
+  - DOMAIN-SUFFIX,wikimedia.org,SELECT
+  - DOMAIN-SUFFIX,wikipedia.com,SELECT
+  - DOMAIN-SUFFIX,wikipedia.org,SELECT
+  - DOMAIN-SUFFIX,windows.com,SELECT
+  - DOMAIN-SUFFIX,windows.net,SELECT
+  - DOMAIN-SUFFIX,wire.com,SELECT
+  - DOMAIN-SUFFIX,wordpress.com,SELECT
+  - DOMAIN-SUFFIX,workflowy.com,SELECT
+  - DOMAIN-SUFFIX,wp.com,SELECT
+  - DOMAIN-SUFFIX,wsj.com,SELECT
+  - DOMAIN-SUFFIX,wsj.net,SELECT
+  - DOMAIN-SUFFIX,xda-developers.com,SELECT
+  - DOMAIN-SUFFIX,xeeno.com,SELECT
+  - DOMAIN-SUFFIX,xiti.com,SELECT
+  - DOMAIN-SUFFIX,yahoo.com,SELECT
+  - DOMAIN-SUFFIX,yimg.com,SELECT
+  - DOMAIN-SUFFIX,ying.com,SELECT
+  - DOMAIN-SUFFIX,yoyo.org,SELECT
+  - DOMAIN-SUFFIX,ytimg.com,SELECT
+
+  # Telegram
+  - DOMAIN-SUFFIX,telegra.ph,SELECT
+  - DOMAIN-SUFFIX,telegram.org,SELECT
+
+  - IP-CIDR,91.108.4.0/22,SELECT,no-resolve
+  - IP-CIDR,91.108.8.0/22,SELECT,no-resolve
+  - IP-CIDR,91.108.12.0/22,SELECT,no-resolve
+  - IP-CIDR,91.108.16.0/22,SELECT,no-resolve
+  - IP-CIDR,91.108.56.0/22,SELECT,no-resolve
+  - IP-CIDR,149.154.160.0/22,SELECT,no-resolve
+  - IP-CIDR,149.154.164.0/22,SELECT,no-resolve
+  - IP-CIDR,149.154.168.0/22,SELECT,no-resolve
+  - IP-CIDR,149.154.172.0/22,SELECT,no-resolve
+
+  # LAN
+  - DOMAIN-SUFFIX,local,DIRECT
+  - IP-CIDR,127.0.0.0/8,DIRECT
+  - IP-CIDR,172.16.0.0/12,DIRECT
+  - IP-CIDR,192.168.0.0/16,DIRECT
+  - IP-CIDR,10.0.0.0/8,DIRECT
+  - IP-CIDR,17.0.0.0/8,DIRECT
+  - IP-CIDR,100.64.0.0/10,DIRECT
+
+  # 最终规则
+  - GEOIP,CN,DIRECT
+  - MATCH,SELECT