This commit is contained in:
snltty
2024-10-16 17:33:41 +08:00
parent 6200632744
commit c5a12185e7
48 changed files with 681 additions and 275 deletions

View File

@@ -12,6 +12,8 @@ sidebar_position: 3
4.`powershell`尝试运行`2`命令,如果没有报错,运行`3`命令删除你可以使用NetNat 4.`powershell`尝试运行`2`命令,如果没有报错,运行`3`命令删除你可以使用NetNat
5. 如果报错,那你需要打开`Hyper-V`功能, `控制面板\程序\启用或关闭 Windows 功能` 5. 如果报错,那你需要打开`Hyper-V`功能, `控制面板\程序\启用或关闭 Windows 功能`
6. 确认支持即可剩下的交给linker 6. 确认支持即可剩下的交给linker
:::
![Docusaurus Plushie](./img/hyper-v.png) ![Docusaurus Plushie](./img/hyper-v.png)
:::

View File

@@ -6,9 +6,10 @@ sidebar_position: 2
:::danger[重要] :::danger[重要]
1. 你要确定你知道你在搞什么如果只是简单的P2P通信上面的内容已经够了以下的东西不要看不要看不要看 1. 你要确定你知道你在搞什么如果只是简单的P2P通信上面的内容已经够了以下的东西不要看不要看不要看
2. 确定要搞,那么请看示意图 2. 确定要搞,那么请看示意图(仅示例,以下绿色内容与此图无关)
![Docusaurus Plushie](./img/tuntap12n.jpg) ![Docusaurus Plushie](./img/tuntap12n.jpg)
::: :::
:::tip[1、情况1你的设备支持NAT转发时] :::tip[1、情况1你的设备支持NAT转发时]
@@ -28,7 +29,7 @@ nat on en0 from 10.18.18.0/24 to any -> (en0)
sudo pfctl -f /etc/pf.conf -e sudo pfctl -f /etc/pf.conf -e
``` ```
这里我`公司-linux`的局域网IP为`192.168.1.18`配置完成后,即可访问其所在局域网`192.168.1.18/24`的所有设备 这里我`公司-linux`的局域网IP为`192.168.1.18`如图配置,`其它客户端`即可通过`192.168.1.18/24`访问`公司-linux`的所有设备
![Docusaurus Plushie](./img/tuntap12n-1.png) ![Docusaurus Plushie](./img/tuntap12n-1.png)
![Docusaurus Plushie](./img/tuntap12n-2.png) ![Docusaurus Plushie](./img/tuntap12n-2.png)

View File

@@ -21,6 +21,7 @@ namespace linker.gen
new GeneratorInfo{ ClassName="StartupTransfer", ClassNameSpace="linker.startup", InterfaceName="linker.startup.IStartup", Instance=true }, new GeneratorInfo{ ClassName="StartupTransfer", ClassNameSpace="linker.startup", InterfaceName="linker.startup.IStartup", Instance=true },
new GeneratorInfo{ ClassName="MessengerResolverTypes", ClassNameSpace="linker.plugins.messenger", InterfaceName="linker.plugins.messenger.IMessenger"}, new GeneratorInfo{ ClassName="MessengerResolverTypes", ClassNameSpace="linker.plugins.messenger", InterfaceName="linker.plugins.messenger.IMessenger"},
new GeneratorInfo{ ClassName="ApiClientServer", ClassNameSpace="linker.plugins.capi", InterfaceName="linker.plugins.capi.IApiClientController"}, new GeneratorInfo{ ClassName="ApiClientServer", ClassNameSpace="linker.plugins.capi", InterfaceName="linker.plugins.capi.IApiClientController"},
new GeneratorInfo{ ClassName="ConfigSyncTreansfer", ClassNameSpace="linker.plugins.config", InterfaceName="linker.plugins.config.IConfigSync"},
}; };
public void Initialize(IncrementalGeneratorInitializationContext context) public void Initialize(IncrementalGeneratorInitializationContext context)

View File

@@ -21,13 +21,6 @@ namespace linker.libs
await Task.Delay(-1, cancellationTokenSource.Token).ConfigureAwait(false); await Task.Delay(-1, cancellationTokenSource.Token).ConfigureAwait(false);
} }
static DateTime startTime = new DateTime(1970, 1, 1);
public static long Timestamp()
{
return (long)(DateTime.UtcNow.Subtract(startTime)).TotalMilliseconds;
}
private delegate bool ConsoleCtrlDelegate(int ctrlType); private delegate bool ConsoleCtrlDelegate(int ctrlType);
private static ConsoleCtrlDelegate handler; private static ConsoleCtrlDelegate handler;
[DllImport("Kernel32")] [DllImport("Kernel32")]

View File

@@ -107,7 +107,7 @@ namespace linker.libs
for (ushort i = 1; i <= 5; i++) for (ushort i = 1; i <= 5; i++)
{ {
using Ping pinger = new(); using Ping pinger = new();
PingReply reply = pinger.Send(target, 100, Encoding.ASCII.GetBytes("snltty"), new PingOptions { Ttl = i, DontFragment = true }); PingReply reply = pinger.Send(target, 100, Encoding.ASCII.GetBytes(Helper.GlobalString), new PingOptions { Ttl = i, DontFragment = true });
if (starts.Any(c => reply.Address.ToString().StartsWith(c))) if (starts.Any(c => reply.Address.ToString().StartsWith(c)))
{ {

View File

@@ -4,6 +4,7 @@ import { sendWebsocketMsg } from './request'
export const setSignIn = (data) => { export const setSignIn = (data) => {
return sendWebsocketMsg('signInclient/set', data); return sendWebsocketMsg('signInclient/set', data);
} }
export const setSignInServers = (servers) => { export const setSignInServers = (servers) => {
return sendWebsocketMsg('signInclient/setservers', servers); return sendWebsocketMsg('signInclient/setservers', servers);
} }
@@ -27,3 +28,6 @@ export const signInDel = (machineId) => {
export const setSignInName = (data) => { export const setSignInName = (data) => {
return sendWebsocketMsg('signInclient/setname', data); return sendWebsocketMsg('signInclient/setname', data);
} }
export const setSignInGroups = (data) => {
return sendWebsocketMsg('signInclient/SetGroups', data);
}

View File

@@ -27,6 +27,7 @@
<TunnelEdit v-if="tunnel.showEdit" v-model="tunnel.showEdit" @change="handleTunnelRefresh"></TunnelEdit> <TunnelEdit v-if="tunnel.showEdit" v-model="tunnel.showEdit" @change="handleTunnelRefresh"></TunnelEdit>
<ConnectionsEdit v-if="connections.showEdit" v-model="connections.showEdit" ></ConnectionsEdit> <ConnectionsEdit v-if="connections.showEdit" v-model="connections.showEdit" ></ConnectionsEdit>
<TuntapEdit v-if="tuntap.showEdit" v-model="tuntap.showEdit" @change="handleTuntapRefresh"></TuntapEdit> <TuntapEdit v-if="tuntap.showEdit" v-model="tuntap.showEdit" @change="handleTuntapRefresh"></TuntapEdit>
<TuntapDHCP v-if="tuntap.showDHCP" v-model="tuntap.showDHCP" @change="handleTuntapRefresh"></TuntapDHCP>
<ForwardEdit v-if="forward.showEdit" v-model="forward.showEdit" ></ForwardEdit> <ForwardEdit v-if="forward.showEdit" v-model="forward.showEdit" ></ForwardEdit>
<ForwardCopy v-if="forward.showCopy" v-model="forward.showCopy" ></ForwardCopy> <ForwardCopy v-if="forward.showCopy" v-model="forward.showCopy" ></ForwardCopy>
<SForwardEdit v-if="sforward.showEdit" v-model="sforward.showEdit" ></SForwardEdit> <SForwardEdit v-if="sforward.showEdit" v-model="sforward.showEdit" ></SForwardEdit>
@@ -42,6 +43,7 @@ import DeviceEdit from './DeviceEdit.vue'
import AccessEdit from './AccessEdit.vue' import AccessEdit from './AccessEdit.vue'
import Tuntap from './Tuntap.vue' import Tuntap from './Tuntap.vue'
import TuntapEdit from './TuntapEdit.vue' import TuntapEdit from './TuntapEdit.vue'
import TuntapDHCP from './TuntapDHCP.vue'
import Tunnel from './Tunnel.vue' import Tunnel from './Tunnel.vue'
import TunnelEdit from './TunnelEdit.vue' import TunnelEdit from './TunnelEdit.vue'
import Forward from './Forward.vue' import Forward from './Forward.vue'
@@ -60,7 +62,7 @@ import { provideDevices } from './devices'
import { provideUpdater } from './updater' import { provideUpdater } from './updater'
import { provideAccess } from './access' import { provideAccess } from './access'
export default { export default {
components: {Oper,Device,DeviceEdit,AccessEdit,Tunnel,TunnelEdit,ConnectionsEdit, Tuntap,TuntapEdit, Forward,ForwardEdit,ForwardCopy,SForwardEdit,SForwardCopy }, components: {Oper,Device,DeviceEdit,AccessEdit,Tunnel,TunnelEdit,ConnectionsEdit, Tuntap,TuntapEdit,TuntapDHCP, Forward,ForwardEdit,ForwardCopy,SForwardEdit,SForwardCopy },
setup(props) { setup(props) {
const globalData = injectGlobalData(); const globalData = injectGlobalData();

View File

@@ -1,5 +1,8 @@
<template> <template>
<el-table-column prop="tuntap" label="虚拟网卡" width="160"> <el-table-column prop="tuntap" label="虚拟网卡" width="160">
<template #header>
<a href="javascript:;" class="a-line">虚拟网卡</a>
</template>
<template #default="scope"> <template #default="scope">
<div v-if="tuntap.list[scope.row.MachineId]"> <div v-if="tuntap.list[scope.row.MachineId]">
<TuntapShow :config="true" :item="scope.row" @edit="handleTuntapIP" @refresh="handleTuntapRefresh"></TuntapShow> <TuntapShow :config="true" :item="scope.row" @edit="handleTuntapIP" @refresh="handleTuntapRefresh"></TuntapShow>

View File

@@ -0,0 +1,71 @@
<template>
<el-dialog v-model="state.show" :close-on-click-modal="false" append-to=".app-wrap" title="配置本组的网络" top="1vh" width="700">
<div>
<el-form ref="ruleFormRef" :model="state.ruleForm" :rules="state.rules" label-width="140">
<el-form-item prop="gateway" style="margin-bottom:0">
赐予此设备IP其它设备可通过此IP访问
</el-form-item>
<el-form-item label="此设备的虚拟网卡IP" prop="IP">
</el-form-item>
<el-form-item label="" prop="Btns">
<div>
<el-button @click="state.show = false">取消</el-button>
<el-button type="primary" @click="handleSave">确认</el-button>
</div>
</el-form-item>
</el-form>
</div>
</el-dialog>
</template>
<script>
import {updateTuntap } from '@/apis/tuntap';
import { injectGlobalData } from '@/provide';
import { ElMessage } from 'element-plus';
import { reactive, ref, watch } from 'vue';
import { useTuntap } from './tuntap';
import { Delete, Plus } from '@element-plus/icons-vue'
export default {
props: ['modelValue'],
emits: ['change','update:modelValue'],
components: {Delete,Plus},
setup(props, { emit }) {
const globalData = injectGlobalData();
const tuntap = useTuntap();
const ruleFormRef = ref(null);
const state = reactive({
show: true,
ruleForm: {
},
rules: {}
});
watch(() => state.show, (val) => {
if (!val) {
setTimeout(() => {
emit('update:modelValue', val);
}, 300);
}
});
const handleSave = () => {
const json = JSON.parse(JSON.stringify(tuntap.value.current));
updateTuntap(json).then(() => {
state.show = false;
ElMessage.success('已操作!');
emit('change')
}).catch(() => {
ElMessage.error('操作失败!');
});
}
return {
state, ruleFormRef, handleSave
}
}
}
</script>
<style lang="stylus" scoped>
.el-switch.is-disabled{opacity :1;}
</style>

View File

@@ -11,7 +11,9 @@ export const provideTuntap = () => {
showEdit: false, showEdit: false,
current: null, current: null,
list: {}, list: {},
hashcode: 0 hashcode: 0,
showDHCP: false,
}); });
provide(tuntapSymbol, tuntap); provide(tuntapSymbol, tuntap);

View File

@@ -14,8 +14,8 @@
</el-form-item> </el-form-item>
</el-col> </el-col>
<el-col :span="12"> <el-col :span="12">
<el-form-item label="分组名" prop="groupid"> <el-form-item label="网页端口" prop="web">
<el-input v-model="state.form.groupid" maxlength="36" show-word-limit /> <el-input style="width:44.5rem;" v-model="state.form.web" />
</el-form-item> </el-form-item>
</el-col> </el-col>
</el-row> </el-row>
@@ -28,8 +28,8 @@
</el-form-item> </el-form-item>
</el-col> </el-col>
<el-col :span="12"> <el-col :span="12">
<el-form-item label="网页端口" prop="web"> <el-form-item label="接口密码" prop="password">
<el-input v-model="state.form.web" /> <el-input type="password" v-model="state.form.password" show-password maxlength="36" show-word-limit/>
</el-form-item> </el-form-item>
</el-col> </el-col>
</el-row> </el-row>
@@ -37,13 +37,22 @@
<el-form-item label="" label-width="0"> <el-form-item label="" label-width="0">
<el-row> <el-row>
<el-col :span="12"> <el-col :span="12">
<el-form-item label="接口密码" prop="password"> <el-form-item label="分组名" prop="groupid">
<el-input style="width:42rem" type="password" v-model="state.form.password" show-password maxlength="36" show-word-limit/> <el-input v-model="state.form.groupid" maxlength="36" show-word-limit />
</el-form-item> </el-form-item>
</el-col> </el-col>
<el-col :span="12"> <el-col :span="12">
<el-form-item label="分组密码" prop="groupPassword">
<el-input v-model="state.form.groupPassword" type="password" show-password maxlength="36" show-word-limit />
</el-form-item>
</el-col>
</el-row>
</el-form-item>
<el-form-item label="" label-width="0">
<el-row>
<el-col :span="24">
<el-form-item label-width="8rem" prop="hasServer"> <el-form-item label-width="8rem" prop="hasServer">
<el-checkbox v-model="state.form.hasServer" label="我有服务器" size="large" /> <el-checkbox v-model="state.form.hasServer" label="我有服务器(私有部署服务端,使用自己的服务器)" size="large" />
</el-form-item> </el-form-item>
</el-col> </el-col>
</el-row> </el-row>
@@ -107,7 +116,8 @@ export default {
const state = reactive({ const state = reactive({
form: { form: {
name:step.value.form.client.name || globalData.value.config.Client.Name, name:step.value.form.client.name || globalData.value.config.Client.Name,
groupid: step.value.form.client.groupid ||globalData.value.config.Client.GroupId, groupid: step.value.form.client.groupid ||globalData.value.config.Client.Group.Id,
groupPassword: step.value.form.client.groupPassword ||globalData.value.config.Client.GroupPassword,
api: step.value.form.client.api ||globalData.value.config.Client.CApi.ApiPort, api: step.value.form.client.api ||globalData.value.config.Client.CApi.ApiPort,
web: step.value.form.client.web ||globalData.value.config.Client.CApi.WebPort, web: step.value.form.client.web ||globalData.value.config.Client.CApi.WebPort,
password:step.value.form.client.password || globalData.value.config.Client.CApi.ApiPassword, password:step.value.form.client.password || globalData.value.config.Client.CApi.ApiPassword,
@@ -122,6 +132,7 @@ export default {
rules: { rules: {
name: [{ required: true, message: "必填", trigger: "blur" }], name: [{ required: true, message: "必填", trigger: "blur" }],
groupid: [{ required: true, message: "必填", trigger: "blur" }], groupid: [{ required: true, message: "必填", trigger: "blur" }],
groupPassword: [{ required: true, message: "必填", trigger: "blur" }],
password: [{ required: true, message: "必填", trigger: "blur" }], password: [{ required: true, message: "必填", trigger: "blur" }],
api: [ api: [
{ required: true, message: "必填", trigger: "blur" }, { required: true, message: "必填", trigger: "blur" },
@@ -161,6 +172,7 @@ export default {
Client:{ Client:{
name: state.form.name, name: state.form.name,
groupid: state.form.groupid, groupid: state.form.groupid,
groupPassword: state.form.groupPassword,
api: +state.form.api, api: +state.form.api,
web: +state.form.web, web: +state.form.web,
password: state.form.password, password: state.form.password,

View File

@@ -32,15 +32,6 @@
</el-col> </el-col>
</el-row> </el-row>
</el-form-item> </el-form-item>
<el-form-item label="" label-width="0">
<el-row>
<el-col :span="24">
<el-form-item label="信标密钥" prop="signinSecretKey">
<el-input v-model="state.form.signinSecretKey" maxlength="36" show-word-limit />
</el-form-item>
</el-col>
</el-row>
</el-form-item>
<el-form-item label="" label-width="0"> <el-form-item label="" label-width="0">
<el-row> <el-row>
<el-col :span="12"> <el-col :span="12">
@@ -57,7 +48,12 @@
</el-form-item> </el-form-item>
<el-form-item label="" label-width="0"> <el-form-item label="" label-width="0">
<el-row> <el-row>
<el-col :span="24"> <el-col :span="12">
<el-form-item label="信标密钥" prop="signinSecretKey">
<el-input v-model="state.form.signinSecretKey" maxlength="36" show-word-limit />
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="更新密钥" prop="updaterSecretKey"> <el-form-item label="更新密钥" prop="updaterSecretKey">
<el-input v-model="state.form.updaterSecretKey" maxlength="36" show-word-limit /> <el-input v-model="state.form.updaterSecretKey" maxlength="36" show-word-limit />
</el-form-item> </el-form-item>

View File

@@ -0,0 +1,118 @@
<template>
<el-table :data="state.list" border size="small" width="100%" :height="`${state.height}px`" @cell-dblclick="handleCellClick">
<el-table-column prop="Name" label="名称" width="100">
<template #default="scope">
<template v-if="scope.row.NameEditing">
<el-input autofocus size="small" v-model="scope.row.Name"
@blur="handleEditBlur(scope.row, 'Name')"></el-input>
</template>
<template v-else>
{{ scope.row.Name }}
</template>
</template>
</el-table-column>
<el-table-column prop="Id" label="Id" >
<template #default="scope">
<template v-if="scope.row.IdEditing">
<el-input autofocus size="small" v-model="scope.row.Id"
@blur="handleEditBlur(scope.row, 'Id')"></el-input>
</template>
<template v-else>
{{ scope.row.Id }}
</template>
</template>
</el-table-column>
<el-table-column prop="Password" label="密码" >
<template #default="scope">
<template v-if="scope.row.PasswordEditing">
<el-input type="password" show-password size="small" v-model="scope.row.Password" @blur="handleEditBlur(scope.row, 'Password')"></el-input>
</template>
<template v-else></template>
</template>
</el-table-column>
<el-table-column prop="Oper" label="操作" width="110">
<template #default="scope">
<div>
<el-popconfirm title="删除不可逆,是否确认?" @confirm="handleDel(scope.$index)">
<template #reference>
<el-button type="danger" size="small">
<el-icon><Delete /></el-icon>
</el-button>
</template>
</el-popconfirm>
<el-button type="primary" size="small" @click="handleAdd(scope.$index)">
<el-icon><Plus /></el-icon>
</el-button>
</div>
</template>
</el-table-column>
</el-table>
</template>
<script>
import { setSignInGroups } from '@/apis/signin';
import { injectGlobalData } from '@/provide';
import { ElMessage } from 'element-plus';
import { computed, reactive, watch } from 'vue'
import { Delete,Plus,Select } from '@element-plus/icons-vue';
export default {
label:'分组',
name:'signInGroups',
order:2,
components:{Delete,Plus,Select },
setup(props) {
const globalData = injectGlobalData();
const state = reactive({
list:globalData.value.config.Client.Groups || [],
height: computed(()=>globalData.value.height-90),
});
watch(()=>globalData.value.config.Client.Groups,()=>{
if(state.list.filter(c=>c['__editing']).length == 0){
state.list = globalData.value.config.Client.Groups;
}
})
const handleCellClick = (row, column) => {
handleEdit(row, column.property);
}
const handleEdit = (row, p) => {
state.list.forEach(c => {
c[`NameEditing`] = false;
c[`IdEditing`] = false;
c[`PasswordEditing`] = false;
})
row[`${p}Editing`] = true;
row[`__editing`] = true;
}
const handleEditBlur = (row, p) => {
row[`${p}Editing`] = false;
row[`__editing`] = false;
handleSave();
}
const handleDel = (index)=>{
state.list.splice(index,1);
handleSave();
}
const handleAdd = (index)=>{
if(state.list.filter(c=>c.Id == '' || c.Name == '').length > 0){
return;
}
state.list.splice(index+1,0,{Name:'',Id:'',Password:''});
handleSave();
}
const handleSave = ()=>{
setSignInGroups(state.list).then(()=>{
ElMessage.success('已操作');
}).catch(()=>{
ElMessage.error('操作失败');
});
}
return {state,handleCellClick,handleEditBlur,handleDel,handleAdd}
}
}
</script>
<style lang="stylus" scoped>
</style>

View File

@@ -16,12 +16,19 @@ export default {
components:{}, components:{},
setup(props) { setup(props) {
const excludes = ['./Index.vue','./Version.vue','./TunnelServers.vue'] const globalData = injectGlobalData();
const hasConfig = computed(()=>globalData.value.hasAccess('Config'))
const hasSync = computed(()=>globalData.value.hasAccess('Sync'));
const excludes = ['./Index.vue','./Version.vue','./TunnelServers.vue'];
if(hasSync.value == false){
excludes.push('./Async.vue');
}
const files = require.context('./', true, /.+\.vue/); const files = require.context('./', true, /.+\.vue/);
const settingComponents = files.keys().filter(c=>excludes.includes(c)==false).map(c => files(c).default).sort((a,b)=>a.order-b.order); const settingComponents = files.keys().filter(c=>excludes.includes(c)==false).map(c => files(c).default).sort((a,b)=>a.order-b.order);
const globalData = injectGlobalData();
const hasConfig = computed(()=>globalData.value.hasAccess('Config'))
const state = reactive({ const state = reactive({
tab:settingComponents[0].name tab:settingComponents[0].name
}); });

View File

@@ -86,7 +86,7 @@ import { Delete,Plus,Top,Bottom } from '@element-plus/icons-vue';
export default { export default {
label:'中继服务器', label:'中继服务器',
name:'relayServers', name:'relayServers',
order:4, order:2,
components:{Delete,Plus,Top,Bottom}, components:{Delete,Plus,Top,Bottom},
setup(props) { setup(props) {
const globalData = injectGlobalData(); const globalData = injectGlobalData();

View File

@@ -16,7 +16,7 @@ import {onMounted, reactive } from 'vue'
export default { export default {
label:'服务器穿透', label:'服务器穿透',
name:'sforward', name:'sforward',
order:5, order:3,
setup(props) { setup(props) {
const globalData = injectGlobalData(); const globalData = injectGlobalData();
const state = reactive({ const state = reactive({

View File

@@ -16,7 +16,7 @@ import { computed, inject, onMounted, reactive } from 'vue'
export default { export default {
label:'服务器更新', label:'服务器更新',
name:'updater', name:'updater',
order:6, order:4,
setup(props) { setup(props) {
const globalData = injectGlobalData(); const globalData = injectGlobalData();
const state = reactive({ const state = reactive({

View File

@@ -9,7 +9,9 @@
<el-input v-model="state.form.name" maxlength="12" show-word-limit /> <el-input v-model="state.form.name" maxlength="12" show-word-limit />
</el-form-item> </el-form-item>
<el-form-item label="分组名" prop="groupid"> <el-form-item label="分组名" prop="groupid">
<el-input v-model="state.form.groupid" type="password" show-password maxlength="36" show-word-limit /> <el-select v-model="state.groupid" @change="handleGroupChange">
<el-option v-for="item in state.form.groups" :key="item.Id" :label="item.Name" :value="item.Id"/>
</el-select>
</el-form-item> </el-form-item>
</el-form> </el-form>
</div> </div>
@@ -26,9 +28,9 @@ import { setSignIn } from '@/apis/signin';
import { injectGlobalData } from '@/provide'; import { injectGlobalData } from '@/provide';
import { ElMessage } from 'element-plus'; import { ElMessage } from 'element-plus';
import { computed, reactive, ref } from 'vue'; import { computed, reactive, ref } from 'vue';
import {Promotion} from '@element-plus/icons-vue' import {Promotion,CirclePlus} from '@element-plus/icons-vue'
export default { export default {
components:{Promotion}, components:{Promotion,CirclePlus},
props:['config'], props:['config'],
setup(props) { setup(props) {
@@ -38,9 +40,10 @@ export default {
show: false, show: false,
loading: false, loading: false,
connected: computed(() => globalData.value.signin.Connected), connected: computed(() => globalData.value.signin.Connected),
groupid: globalData.value.config.Client.Group.Id,
form: { form: {
name: globalData.value.config.Client.Name, name: globalData.value.config.Client.Name,
groupid: globalData.value.config.Client.GroupId, groups: globalData.value.config.Client.Groups,
}, },
rules: {}, rules: {},
}); });
@@ -50,9 +53,22 @@ export default {
return; return;
} }
state.form.name = globalData.value.config.Client.Name; state.form.name = globalData.value.config.Client.Name;
state.form.groupid = globalData.value.config.Client.GroupId; state.form.groups = globalData.value.config.Client.Groups;
state.groupid = globalData.value.config.Client.Group.Id;
state.show = true; state.show = true;
} }
const handleGroupChange = (value)=>{
const index = state.form.groups.map((item,index)=>{
item.$index = index;
return item;
}).filter(c=>c.Id == value)[0].$index;
const temp = state.form.groups[index];
state.form.groups[index] = state.form.groups[0];
state.form.groups[0] = temp;
console.log(state.form.groups);
}
const handleSave = () => { const handleSave = () => {
state.loading = true; state.loading = true;
setSignIn(state.form).then(() => { setSignIn(state.form).then(() => {
@@ -68,7 +84,7 @@ export default {
}); });
} }
return { return {
config:props.config, state, handleConfig, handleSave config:props.config, state, handleConfig, handleSave,handleGroupChange
} }
} }
} }

View File

@@ -1,9 +1,9 @@
<template> <template>
<a v-if="config" href="javascript:;" title="linker服务端网速点击查看详细信息" @click="handleShow"> <div class="flow-wrap" v-if="config">
<p>在线 {{state.overallOnline}}{{ state.serverOnline }}</p> <p>在线 <a href="javascript:;" :title="`本服务器\r\n在线数/7天内上线数`">{{state.overallOnline}}</a><a href="javascript:;" :title="`所有服务器\r\n在线数/7天内上线数/服务端数`">{{ state.serverOnline }}</a></p>
<p>上传 {{state.overallSendtSpeed}}/s</p> <p>上传 <a href="javascript:;" :title="`本服务器\r\n发送速率`" @click="handleShow">{{state.overallSendtSpeed}}/s</a></p>
<p>下载 {{state.overallReceiveSpeed}}/s</p> <p>下载 <a href="javascript:;" :title="`本服务器\r\n接收速率`" @click="handleShow">{{state.overallReceiveSpeed}}/s</a></p>
</a> </div>
<el-dialog :title="state.time" destroy-on-close v-model="state.show" width="540"> <el-dialog :title="state.time" destroy-on-close v-model="state.show" width="540">
<div> <div>
<el-table :data="state.list" border size="small" width="100%"> <el-table :data="state.list" border size="small" width="100%">
@@ -63,7 +63,7 @@ export default {
overallSendtSpeed: '0000.00KB', overallSendtSpeed: '0000.00KB',
overallReceiveSpeed: '0000.00KB', overallReceiveSpeed: '0000.00KB',
overallOnline: '0/0', overallOnline: '0/0',
serverOnline: '0/0/0', serverOnline: '',
time:'', time:'',
list:[], list:[],
old:null, old:null,
@@ -93,11 +93,11 @@ export default {
state.overallOnline = `${res.Items['_'].SendtBytes}/${res.Items['_'].ReceiveBytes}`; state.overallOnline = `${res.Items['_'].SendtBytes}/${res.Items['_'].ReceiveBytes}`;
delete res.Items['_']; delete res.Items['_'];
} }
if(res.Items['flow']){ if(res.Items['flow'] && res.Items['flow'].ReceiveBytes>0){
const online = (BigInt(res.Items['flow'].ReceiveBytes) >> BigInt(32)).toString(); const online = (BigInt(res.Items['flow'].ReceiveBytes) >> BigInt(32)).toString();
const total = (BigInt(res.Items['flow'].ReceiveBytes) & BigInt(0xffffffff)).toString(); const total = (BigInt(res.Items['flow'].ReceiveBytes) & BigInt(0xffffffff)).toString();
const server = res.Items['flow'].SendtBytes; const server = res.Items['flow'].SendtBytes;
state.serverOnline = `${online}/${total}/${server}`; state.serverOnline = `${online}/${total}/${server}`;
delete res.Items['flow']; delete res.Items['flow'];
} }
@@ -171,14 +171,16 @@ export default {
</script> </script>
<style lang="stylus" scoped> <style lang="stylus" scoped>
a{ .flow-wrap{
padding:.4rem;
font-weight:bold;position:absolute;right:1rem;bottom:80%; font-weight:bold;position:absolute;right:1rem;bottom:80%;
border:1px solid #ddd; border:1px solid #ddd;
background-color:#fff; background-color:#fff;
z-index :9 z-index :9
p{ &>a,&>p{
line-height:normal; line-height:normal;
white-space: nowrap; white-space: nowrap;
display:block;
} }
} }
</style> </style>

View File

@@ -112,7 +112,7 @@ namespace linker.config
Data.Updated--; Data.Updated--;
} }
return true; return true;
}, 1000); }, 3000);
} }
} }

View File

@@ -23,9 +23,6 @@ namespace linker.plugins.signin
public bool SetArgs(ApiControllerParamsInfo param) public bool SetArgs(ApiControllerParamsInfo param)
{ {
actionTransfer.SetActionArg(param.Content); actionTransfer.SetActionArg(param.Content);
clientSignInTransfer.SignOut();
_ = clientSignInTransfer.SignIn();
return true; return true;
} }
@@ -33,8 +30,6 @@ namespace linker.plugins.signin
public bool SetServerArgs(ApiControllerParamsInfo param) public bool SetServerArgs(ApiControllerParamsInfo param)
{ {
actionTransfer.SetActionArgs(param.Content.DeJson<Dictionary<string, string>>()); actionTransfer.SetActionArgs(param.Content.DeJson<Dictionary<string, string>>());
clientSignInTransfer.SignOut();
_ = clientSignInTransfer.SignIn();
return true; return true;
} }
} }

View File

@@ -8,6 +8,7 @@ using System.Net;
using System.Net.Sockets; using System.Net.Sockets;
using linker.plugins.messenger; using linker.plugins.messenger;
using linker.plugins.signIn.args; using linker.plugins.signIn.args;
using linker.plugins.signin;
namespace linker.plugins.client namespace linker.plugins.client
{ {
@@ -62,7 +63,7 @@ namespace linker.plugins.client
/// <returns></returns> /// <returns></returns>
public async Task SignIn() public async Task SignIn()
{ {
if (string.IsNullOrWhiteSpace(config.Data.Client.GroupId)) if (string.IsNullOrWhiteSpace(config.Data.Client.Group.Id))
{ {
LoggerHelper.Instance.Error($"please configure group id"); LoggerHelper.Instance.Error($"please configure group id");
return; return;
@@ -149,7 +150,7 @@ namespace linker.plugins.client
MachineId = config.Data.Client.Id, MachineId = config.Data.Client.Id,
Version = config.Data.Version, Version = config.Data.Version,
Args = args, Args = args,
GroupId = config.Data.Client.GroupId, GroupId = config.Data.Client.Group.Id,
}) })
}).ConfigureAwait(false); }).ConfigureAwait(false);
if (resp.Code != MessageResponeCodes.OK) if (resp.Code != MessageResponeCodes.OK)
@@ -207,39 +208,30 @@ namespace linker.plugins.client
/// 修改客户端名称 /// 修改客户端名称
/// </summary> /// </summary>
/// <param name="newName"></param> /// <param name="newName"></param>
public void SetName(string newName) public void Set(string newName)
{ {
string name = config.Data.Client.Name; config.Data.Client.Name = newName;
config.Data.Update();
if (name != newName)
{
config.Data.Client.Name = newName;
config.Data.Update();
SignOut();
_ = SignIn();
}
} }
/// <summary> /// <summary>
/// 修改客户端名称和分组编号 /// 修改客户端名称和分组编号
/// </summary> /// </summary>
/// <param name="newName"></param> /// <param name="newName"></param>
/// <param name="newGroupid"></param> /// <param name="groups"></param>
public void Set(string newName, string newGroupid) public void Set(string newName, ClientGroupInfo[] groups)
{ {
string name = config.Data.Client.Name; config.Data.Client.Name = newName;
string gid = config.Data.Client.GroupId; config.Data.Client.Groups = groups.DistinctBy(c => c.Name).ToArray();
config.Data.Update();
if (name != newName || gid != newGroupid) }
{ /// <summary>
config.Data.Client.Name = newName; /// 设置分组编号
config.Data.Client.GroupId = newGroupid; /// </summary>
config.Data.Update(); /// <param name="groups"></param>
SignOut(); public void Set(ClientGroupInfo[] groups)
_ = SignIn(); {
} config.Data.Client.Groups = groups.DistinctBy(c=>c.Name).ToArray();
config.Data.Update();
} }
@@ -265,7 +257,6 @@ namespace linker.plugins.client
return resp.Code == MessageResponeCodes.OK && resp.Data.Span.SequenceEqual(Helper.TrueArray); return resp.Code == MessageResponeCodes.OK && resp.Data.Span.SequenceEqual(Helper.TrueArray);
} }
/// <summary> /// <summary>
/// 获取一个新的id /// 获取一个新的id
/// </summary> /// </summary>
@@ -290,22 +281,14 @@ namespace linker.plugins.client
/// 修改信标服务器列表 /// 修改信标服务器列表
/// </summary> /// </summary>
/// <param name="servers"></param> /// <param name="servers"></param>
public async Task SetServers(ClientServerInfo[] servers) public void SetServers(ClientServerInfo[] servers)
{ {
await SetServersReSignin(servers); SetServersReSignin(servers);
} }
private async Task SetServersReSignin(ClientServerInfo[] servers) private void SetServersReSignin(ClientServerInfo[] servers)
{ {
string str = config.Data.Client.ServerInfo.ToStr();
config.Data.Client.Servers = servers; config.Data.Client.Servers = servers;
config.Data.Update(); config.Data.Update();
if (str != config.Data.Client.ServerInfo.ToStr())
{
SignOut();
await SignIn();
}
} }
} }
} }

View File

@@ -24,11 +24,16 @@ namespace linker.plugins.client
{ {
config.Data.Client.Name = Dns.GetHostName().SubStr(0, 12); config.Data.Client.Name = Dns.GetHostName().SubStr(0, 12);
} }
if (config.Data.Client.Groups.Length == 0)
{
config.Data.Client.Groups = new ClientGroupInfo[] { new ClientGroupInfo { Id = config.Data.Client.GroupId, Name= config.Data.Client.GroupId, Password = string.Empty } };
}
serviceCollection.AddSingleton<ClientSignInState>(); serviceCollection.AddSingleton<ClientSignInState>();
serviceCollection.AddSingleton<ClientSignInTransfer>(); serviceCollection.AddSingleton<ClientSignInTransfer>();
serviceCollection.AddSingleton<SignInArgsSecretKeyClient>(); serviceCollection.AddSingleton<SignInArgsSecretKeyClient>();
serviceCollection.AddSingleton<SignInArgsGroupPasswordClient>();
} }
@@ -45,6 +50,7 @@ namespace linker.plugins.client
public void AddServer(ServiceCollection serviceCollection, FileConfig config) public void AddServer(ServiceCollection serviceCollection, FileConfig config)
{ {
serviceCollection.AddSingleton<SignInArgsSecretKeyServer>(); serviceCollection.AddSingleton<SignInArgsSecretKeyServer>();
serviceCollection.AddSingleton<SignInArgsGroupPasswordServer>();
} }
public void UseServer(ServiceProvider serviceProvider, FileConfig config) public void UseServer(ServiceProvider serviceProvider, FileConfig config)
{ {

View File

@@ -0,0 +1,68 @@
using linker.plugins.signIn.args;
using linker.plugins.signin.messenger;
using linker.config;
namespace linker.plugins.client
{
/// <summary>
/// 添加分组密码
/// </summary>
public sealed class SignInArgsGroupPasswordClient : ISignInArgs
{
private readonly FileConfig fileConfig;
public SignInArgsGroupPasswordClient(FileConfig fileConfig)
{
this.fileConfig = fileConfig;
}
public async Task<string> Invoke(string host, Dictionary<string, string> args)
{
args.TryAdd("signin-gpwd", fileConfig.Data.Client.Group.Password);
await Task.CompletedTask;
return string.Empty;
}
public async Task<string> Validate(SignInfo signInfo, SignCacheInfo cache)
{
await Task.CompletedTask;
return string.Empty;
}
}
/// <summary>
/// 验证分组密码
/// </summary>
public sealed class SignInArgsGroupPasswordServer : ISignInArgs
{
private readonly FileConfig fileConfig;
public SignInArgsGroupPasswordServer(FileConfig fileConfig)
{
this.fileConfig = fileConfig;
}
public async Task<string> Invoke(string host, Dictionary<string, string> args)
{
await Task.CompletedTask;
return string.Empty;
}
/// <summary>
/// 验证参数
/// </summary>
/// <param name="signInfo">新登录参数</param>
/// <param name="cache">之前的登录信息</param>
/// <returns></returns>
public async Task<string> Validate(SignInfo signInfo, SignCacheInfo cache)
{
if (string.IsNullOrWhiteSpace(fileConfig.Data.Server.SignIn.SecretKey) == false)
{
if (signInfo.Args.TryGetValue("signin-gpwd", out string gpwd) && string.IsNullOrWhiteSpace(gpwd) == false)
{
signInfo.GroupId = $"{signInfo.GroupId}->{gpwd}";
}
}
await Task.CompletedTask;
return string.Empty;
}
}
}

View File

@@ -6,6 +6,7 @@ using MemoryPack;
using System.Net; using System.Net;
using System.Reflection; using System.Reflection;
using System.Text; using System.Text;
using System.Text.Json.Serialization;
namespace linker.client.config namespace linker.client.config
{ {
@@ -91,10 +92,7 @@ namespace linker.config
}; };
public ClientServerInfo[] Servers public ClientServerInfo[] Servers
{ {
get => servers; set get => servers; set { servers = value; }
{
servers = value;
}
} }
public ClientServerInfo ServerInfo => servers[0]; public ClientServerInfo ServerInfo => servers[0];
@@ -108,6 +106,7 @@ namespace linker.config
} }
} }
private string name = Dns.GetHostName().SubStr(0, 12); private string name = Dns.GetHostName().SubStr(0, 12);
public string Name public string Name
{ {
@@ -118,7 +117,7 @@ namespace linker.config
} }
#if DEBUG #if DEBUG
private string groupid = "snltty"; private string groupid = Helper.GlobalString;
#else #else
private string groupid = string.Empty; private string groupid = string.Empty;
#endif #endif
@@ -130,8 +129,14 @@ namespace linker.config
} }
} }
public string Certificate { get; set; } = "./snltty.pfx";
public string Password { get; set; } = "oeq9tw1o"; public ClientGroupInfo Group => Groups.Length == 0 ? new ClientGroupInfo { } : Groups[0];
public ClientGroupInfo[] Groups { get; set; } = Array.Empty<ClientGroupInfo>();
/// <summary>
/// 加密证书
/// </summary>
public ClientCertificateInfo SSL { get; set; } = new ClientCertificateInfo();
public string Serialize(object obj) public string Serialize(object obj)
{ {
@@ -141,7 +146,6 @@ namespace linker.config
return Convert.ToBase64String(crypto.Encode(Encoding.UTF8.GetBytes(obj.ToJson()))); return Convert.ToBase64String(crypto.Encode(Encoding.UTF8.GetBytes(obj.ToJson())));
#endif #endif
} }
public object Deserialize(string text) public object Deserialize(string text)
{ {
if (text.Contains("ApiPassword")) if (text.Contains("ApiPassword"))
@@ -150,8 +154,53 @@ namespace linker.config
} }
return Encoding.UTF8.GetString(crypto.Decode(Convert.FromBase64String(text)).ToArray()).DeJson<ConfigClientInfo>(); return Encoding.UTF8.GetString(crypto.Decode(Convert.FromBase64String(text)).ToArray()).DeJson<ConfigClientInfo>();
} }
public string ToGroupString()
{
return $"{name}->{Group.Id}->{Group.Password}";
}
} }
[MemoryPackable]
public sealed partial class ClientGroupInfo
{
public ClientGroupInfo() { }
public string Name { get; set; } = string.Empty;
#if DEBUG
private string id = Helper.GlobalString;
#else
private string id = string.Empty;
#endif
public string Id
{
get => id; set
{
id = value.SubStr(0, 36);
}
}
private string passord = string.Empty;
public string Password
{
get => passord; set
{
passord = value.SubStr(0, 36);
}
}
}
[MemoryPackable]
public sealed partial class ClientCertificateInfo
{
public ClientCertificateInfo() { }
public string File { get; set; } = "./snltty.pfx";
public string Password { get; set; } = "oeq9tw1o";
}
[MemoryPackable] [MemoryPackable]
public sealed partial class ClientServerInfo public sealed partial class ClientServerInfo
{ {
@@ -166,6 +215,7 @@ namespace linker.config
} }
} }
[AttributeUsage(AttributeTargets.Method, Inherited = false, AllowMultiple = false)] [AttributeUsage(AttributeTargets.Method, Inherited = false, AllowMultiple = false)]
public sealed class ClientApiAccessAttribute : Attribute public sealed class ClientApiAccessAttribute : Attribute
{ {
@@ -279,6 +329,9 @@ namespace linker.config
[ClientAccessDisplayAttribute("查看流量")] [ClientAccessDisplayAttribute("查看流量")]
Flow = ((ulong)1 << 33), Flow = ((ulong)1 << 33),
[ClientAccessDisplayAttribute("同步配置")]
Sync = ((ulong)1 << 34),
Full = ulong.MaxValue >> (64 - 52), Full = ulong.MaxValue >> (64 - 52),
} }

View File

@@ -20,8 +20,9 @@ namespace linker.plugins.config
private readonly MessengerSender sender; private readonly MessengerSender sender;
private readonly ClientSignInState clientSignInState; private readonly ClientSignInState clientSignInState;
private readonly AccessTransfer accessTransfer; private readonly AccessTransfer accessTransfer;
private readonly ConfigSyncTreansfer configSyncTreansfer;
public ConfigClientApiController(RunningConfig runningConfig, FileConfig config, ClientSignInTransfer clientSignInTransfer, MessengerSender sender, ClientSignInState clientSignInState, AccessTransfer accessTransfer) public ConfigClientApiController(RunningConfig runningConfig, FileConfig config, ClientSignInTransfer clientSignInTransfer, MessengerSender sender, ClientSignInState clientSignInState, AccessTransfer accessTransfer, ConfigSyncTreansfer configSyncTreansfer)
{ {
this.runningConfig = runningConfig; this.runningConfig = runningConfig;
this.config = config; this.config = config;
@@ -29,6 +30,7 @@ namespace linker.plugins.config
this.sender = sender; this.sender = sender;
this.clientSignInState = clientSignInState; this.clientSignInState = clientSignInState;
this.accessTransfer = accessTransfer; this.accessTransfer = accessTransfer;
this.configSyncTreansfer = configSyncTreansfer;
} }
public object Get(ApiControllerParamsInfo param) public object Get(ApiControllerParamsInfo param)
@@ -42,7 +44,7 @@ namespace linker.plugins.config
if (info.Common.Modes.Contains("client")) if (info.Common.Modes.Contains("client"))
{ {
config.Data.Client.Name = info.Client.Name; config.Data.Client.Name = info.Client.Name;
config.Data.Client.GroupId = info.Client.GroupId; config.Data.Client.Groups = new ClientGroupInfo[] { new ClientGroupInfo { Id = info.Client.GroupId, Name = info.Client.GroupId, Password = info.Client.GroupPassword } };
config.Data.Client.CApi.WebPort = info.Client.Web; config.Data.Client.CApi.WebPort = info.Client.Web;
config.Data.Client.CApi.ApiPort = info.Client.Api; config.Data.Client.CApi.ApiPort = info.Client.Api;
config.Data.Client.CApi.ApiPassword = info.Client.Password; config.Data.Client.CApi.ApiPassword = info.Client.Password;
@@ -163,6 +165,8 @@ namespace linker.plugins.config
client.Access = accessTransfer.AssignAccess((ClientApiAccess)configExportInfo.Access); client.Access = accessTransfer.AssignAccess((ClientApiAccess)configExportInfo.Access);
client.OnlyNode = true; client.OnlyNode = true;
client.Action.Args.Clear(); client.Action.Args.Clear();
client.Groups = new ClientGroupInfo[] { client.Group };
File.WriteAllText(Path.Combine(configPath, $"client.json"), client.Serialize(client)); File.WriteAllText(Path.Combine(configPath, $"client.json"), client.Serialize(client));
ConfigCommonInfo common = config.Data.Common.ToJson().DeJson<ConfigCommonInfo>(); ConfigCommonInfo common = config.Data.Common.ToJson().DeJson<ConfigCommonInfo>();
@@ -221,31 +225,17 @@ namespace linker.plugins.config
} }
public async Task<bool> SecretKeyAsync(ApiControllerParamsInfo param) public List<ConfigSyncNameInfo> SyncNames(ApiControllerParamsInfo param)
{ {
SecretKeyAsyncInfo info = param.Content.DeJson<SecretKeyAsyncInfo>(); return configSyncTreansfer.GetNames();
await sender.SendOnly(new MessageRequestWrap }
{ [ClientApiAccessAttribute(ClientApiAccess.Sync)]
Connection = clientSignInState.Connection, public bool Sync(ApiControllerParamsInfo param)
MessengerId = (ushort)ConfigMessengerIds.SecretKeyAsyncForward, {
Payload = MemoryPackSerializer.Serialize(info) string[] names = param.Content.DeJson<string[]>();
}); configSyncTreansfer.Sync(names);
return true; return true;
} }
public async Task<bool> ServerAsync(ApiControllerParamsInfo param)
{
ServerAsyncInfo info = param.Content.DeJson<ServerAsyncInfo>();
await sender.SendOnly(new MessageRequestWrap
{
Connection = clientSignInState.Connection,
MessengerId = (ushort)ConfigMessengerIds.ServerAsyncForward,
Payload = MemoryPackSerializer.Serialize(info)
});
return true;
}
} }
public sealed class ConfigInstallInfo public sealed class ConfigInstallInfo
@@ -258,6 +248,8 @@ namespace linker.plugins.config
{ {
public string Name { get; set; } public string Name { get; set; }
public string GroupId { get; set; } public string GroupId { get; set; }
public string GroupPassword { get; set; }
public int Api { get; set; } public int Api { get; set; }
public int Web { get; set; } public int Web { get; set; }
public string Password { get; set; } public string Password { get; set; }
@@ -302,7 +294,6 @@ namespace linker.plugins.config
public string[] Modes { get; set; } public string[] Modes { get; set; }
} }
public sealed class ConfigExportInfo public sealed class ConfigExportInfo
{ {
public string Name { get; set; } public string Name { get; set; }

View File

@@ -28,6 +28,10 @@ namespace linker.plugins.config
serviceCollection.AddSingleton<AccessTransfer>(); serviceCollection.AddSingleton<AccessTransfer>();
serviceCollection.AddSingleton<ConfigSyncTreansfer>();
} }
public void AddServer(ServiceCollection serviceCollection, FileConfig config) public void AddServer(ServiceCollection serviceCollection, FileConfig config)
@@ -38,6 +42,8 @@ namespace linker.plugins.config
public void UseClient(ServiceProvider serviceProvider, FileConfig config) public void UseClient(ServiceProvider serviceProvider, FileConfig config)
{ {
RunningConfig runningConfig = serviceProvider.GetService<RunningConfig>(); RunningConfig runningConfig = serviceProvider.GetService<RunningConfig>();
ConfigSyncTreansfer configSyncTreansfer = serviceProvider.GetService<ConfigSyncTreansfer>();
} }
public void UseServer(ServiceProvider serviceProvider, FileConfig config) public void UseServer(ServiceProvider serviceProvider, FileConfig config)

View File

@@ -0,0 +1,87 @@
using linker.libs;
using linker.plugins.client;
using linker.plugins.config.messenger;
using linker.plugins.messenger;
using MemoryPack;
using Microsoft.Extensions.DependencyInjection;
namespace linker.plugins.config
{
public interface IConfigSync
{
public string Name { get; }
public string Label { get; }
public Memory<byte> GetData();
public void SetData(Memory<byte> data);
}
[MemoryPackable]
public sealed partial class ConfigAsyncInfo
{
public ConfigAsyncInfo() { }
public string Name { get; set; }
public Memory<byte> Data { get; set; }
}
public sealed class ConfigSyncNameInfo
{
public string Name { get; set; } = string.Empty;
public string Label { get; set; } = string.Empty;
}
public sealed partial class ConfigSyncTreansfer
{
private readonly SemaphoreSlim slim = new SemaphoreSlim(1);
private List<IConfigSync> syncs = new List<IConfigSync>();
private readonly MessengerSender messengerSender;
private readonly ClientSignInState clientSignInState;
public ConfigSyncTreansfer(ServiceProvider serviceProvider, MessengerSender messengerSender, ClientSignInState clientSignInState)
{
this.messengerSender = messengerSender;
this.clientSignInState = clientSignInState;
IEnumerable<Type> types = GetSourceGeneratorTypes();
syncs = types.Select(c => (IConfigSync)serviceProvider.GetService(c)).Where(c => c != null).Where(c => string.IsNullOrWhiteSpace(c.Name) == false).ToList();
LoggerHelper.Instance.Info($"load config sync transport:{string.Join(",", syncs.Select(c => c.Name))}");
}
public List<ConfigSyncNameInfo> GetNames()
{
return syncs.Select(c => new ConfigSyncNameInfo { Label = c.Label, Name = c.Name }).ToList();
}
public void Sync(string[] names)
{
TimerHelper.Async(async () =>
{
await slim.WaitAsync();
try
{
var tasks = syncs.Where(c => names.Contains(c.Name)).Select(c =>
{
return messengerSender.SendOnly(new MessageRequestWrap
{
Connection = clientSignInState.Connection,
MessengerId = (ushort)ConfigMessengerIds.SyncForward,
Payload = c.GetData(),
});
});
await Task.WhenAll(tasks);
}
catch (Exception)
{
}
slim.Release();
});
}
public void Sync(ConfigAsyncInfo info)
{
var sync = syncs.FirstOrDefault(c => c.Name == info.Name);
if (sync != null)
{
sync.SetData(info.Data);
}
}
}
}

View File

@@ -3,9 +3,6 @@ using linker.libs;
using LiteDB; using LiteDB;
using System.Text.Json.Serialization; using System.Text.Json.Serialization;
using linker.config; using linker.config;
using MemoryPack;
using linker.libs.extends;
using linker.tunnel.wanport;
namespace linker.client.config namespace linker.client.config
{ {
@@ -61,7 +58,7 @@ namespace linker.client.config
Data.Updated--; Data.Updated--;
} }
return true; return true;
}, 1000); }, 3000);
} }
private void Save() private void Save()
{ {
@@ -163,21 +160,5 @@ namespace linker.client.config
} }
[MemoryPackable]
public sealed partial class SecretKeyAsyncInfo
{
public SecretKeyAsyncInfo() { }
public string SignSecretKey { get; set; }
public string RelaySecretKey { get; set; }
public string SForwardSecretKey { get; set; }
}
[MemoryPackable]
public sealed partial class ServerAsyncInfo
{
public ServerAsyncInfo() { }
public ClientServerInfo[] SignServers { get; set; }
public RelayServerInfo[] RelayServers { get; set; }
public TunnelWanPortInfo[] TunnelServers { get; set; }
}
} }

View File

@@ -83,10 +83,10 @@ namespace linker.plugins.config.messenger
} }
[MessengerId((ushort)ConfigMessengerIds.SecretKeyAsyncForward)] [MessengerId((ushort)ConfigMessengerIds.Sync)]
public async Task SecretKeyAsyncForward(IConnection connection) public async Task SecretKeyAsyncForward(IConnection connection)
{ {
SecretKeyAsyncInfo info = MemoryPackSerializer.Deserialize<SecretKeyAsyncInfo>(connection.ReceiveRequestWrap.Payload.Span); ConfigAsyncInfo info = MemoryPackSerializer.Deserialize<ConfigAsyncInfo>(connection.ReceiveRequestWrap.Payload.Span);
if (signCaching.TryGet(connection.Id, out SignCacheInfo cache)) if (signCaching.TryGet(connection.Id, out SignCacheInfo cache))
{ {
List<SignCacheInfo> caches = signCaching.Get(cache.GroupId); List<SignCacheInfo> caches = signCaching.Get(cache.GroupId);
@@ -96,29 +96,7 @@ namespace linker.plugins.config.messenger
tasks.Add(sender.SendOnly(new MessageRequestWrap tasks.Add(sender.SendOnly(new MessageRequestWrap
{ {
Connection = item.Connection, Connection = item.Connection,
MessengerId = (ushort)ConfigMessengerIds.SecretKeyAsync, MessengerId = (ushort)ConfigMessengerIds.Sync,
Payload = connection.ReceiveRequestWrap.Payload,
Timeout = 1000,
}));
}
await Task.WhenAll(tasks);
}
}
[MessengerId((ushort)ConfigMessengerIds.ServerAsyncForward)]
public async Task ServerAsyncForward(IConnection connection)
{
ServerAsyncInfo info = MemoryPackSerializer.Deserialize<ServerAsyncInfo>(connection.ReceiveRequestWrap.Payload.Span);
if (signCaching.TryGet(connection.Id, out SignCacheInfo cache))
{
List<SignCacheInfo> caches = signCaching.Get(cache.GroupId);
List<Task> tasks = new List<Task>();
foreach (SignCacheInfo item in caches.Where(c => c.MachineId != connection.Id && c.Connected))
{
tasks.Add(sender.SendOnly(new MessageRequestWrap
{
Connection = item.Connection,
MessengerId = (ushort)ConfigMessengerIds.ServerAsync,
Payload = connection.ReceiveRequestWrap.Payload, Payload = connection.ReceiveRequestWrap.Payload,
Timeout = 1000, Timeout = 1000,
})); }));
@@ -134,12 +112,14 @@ namespace linker.plugins.config.messenger
private readonly AccessTransfer accessTransfer; private readonly AccessTransfer accessTransfer;
private readonly FileConfig fileConfig; private readonly FileConfig fileConfig;
private readonly ClientSignInTransfer clientSignInTransfer; private readonly ClientSignInTransfer clientSignInTransfer;
private readonly ConfigSyncTreansfer syncTreansfer;
public ConfigClientMessenger(AccessTransfer accessTransfer, FileConfig fileConfig, ClientSignInTransfer clientSignInTransfer) public ConfigClientMessenger(AccessTransfer accessTransfer, FileConfig fileConfig, ClientSignInTransfer clientSignInTransfer, ConfigSyncTreansfer syncTreansfer)
{ {
this.accessTransfer = accessTransfer; this.accessTransfer = accessTransfer;
this.fileConfig = fileConfig; this.fileConfig = fileConfig;
this.clientSignInTransfer = clientSignInTransfer; this.clientSignInTransfer = clientSignInTransfer;
this.syncTreansfer = syncTreansfer;
} }
[MessengerId((ushort)ConfigMessengerIds.Access)] [MessengerId((ushort)ConfigMessengerIds.Access)]
@@ -158,38 +138,12 @@ namespace linker.plugins.config.messenger
connection.Write(Helper.TrueArray); connection.Write(Helper.TrueArray);
} }
[MessengerId((ushort)ConfigMessengerIds.SecretKeyAsync)] [MessengerId((ushort)ConfigMessengerIds.Sync)]
public void SecretKeyAsync(IConnection connection) public void Sync(IConnection connection)
{ {
SecretKeyAsyncInfo info = MemoryPackSerializer.Deserialize<SecretKeyAsyncInfo>(connection.ReceiveRequestWrap.Payload.Span); ConfigAsyncInfo info = MemoryPackSerializer.Deserialize<ConfigAsyncInfo>(connection.ReceiveRequestWrap.Payload.Span);
syncTreansfer.Sync(info);
foreach (var item in fileConfig.Data.Client.Servers)
{
item.SecretKey = info.SignSecretKey;
}
foreach (var item in fileConfig.Data.Client.Relay.Servers)
{
item.SecretKey = info.RelaySecretKey;
}
fileConfig.Data.Client.SForward.SecretKey = info.SForwardSecretKey;
fileConfig.Data.Update();
clientSignInTransfer.SignOut();
_ = clientSignInTransfer.SignIn();
} }
[MessengerId((ushort)ConfigMessengerIds.ServerAsync)]
public void ServerAsync(IConnection connection)
{
ServerAsyncInfo info = MemoryPackSerializer.Deserialize<ServerAsyncInfo>(connection.ReceiveRequestWrap.Payload.Span);
fileConfig.Data.Client.Servers = info.SignServers;
fileConfig.Data.Client.Relay.Servers = info.RelayServers;
fileConfig.Data.Client.Tunnel.Servers = info.TunnelServers.ToList();
fileConfig.Data.Update();
clientSignInTransfer.SignOut();
_ = clientSignInTransfer.SignIn();
}
} }
} }

View File

@@ -13,11 +13,8 @@
AccessUpdate = 2505, AccessUpdate = 2505,
AccessUpdateForward = 2506, AccessUpdateForward = 2506,
SecretKeyAsync = 2507, Sync = 2507,
SecretKeyAsyncForward = 2508, SyncForward = 2508,
ServerAsync = 2509,
ServerAsyncForward = 2510,
Max = 2599 Max = 2599
} }

View File

@@ -272,20 +272,24 @@ namespace linker.plugins.forward.proxy
{ {
TimerHelper.SetInterval(() => TimerHelper.SetInterval(() =>
{ {
var connections = udpConnections.Where(c => c.Value.Timeout).Select(c => c.Key).ToList(); if(udpConnections.Count > 0)
foreach (var item in connections)
{ {
if (udpConnections.TryRemove(item, out AsyncUserUdpTokenTarget token)) var connections = udpConnections.Where(c => c.Value.Timeout).Select(c => c.Key).ToList();
foreach (var item in connections)
{ {
try if (udpConnections.TryRemove(item, out AsyncUserUdpTokenTarget token))
{
token.Clear();
}
catch (Exception)
{ {
try
{
token.Clear();
}
catch (Exception)
{
}
} }
} }
} }
return true; return true;
}, 5000); }, 5000);
} }

View File

@@ -41,7 +41,7 @@ namespace linker.plugins.messenger
MessengerResolver messengerResolver = serviceProvider.GetService<MessengerResolver>(); MessengerResolver messengerResolver = serviceProvider.GetService<MessengerResolver>();
messengerResolver.LoadMessenger(); messengerResolver.LoadMessenger();
messengerResolver.Init(config.Data.Client.Certificate, config.Data.Client.Password); messengerResolver.Init(config.Data.Client.SSL.File, config.Data.Client.SSL.Password);
} }
} }
@@ -54,7 +54,7 @@ namespace linker.plugins.messenger
MessengerResolver messengerResolver = serviceProvider.GetService<MessengerResolver>(); MessengerResolver messengerResolver = serviceProvider.GetService<MessengerResolver>();
messengerResolver.LoadMessenger(); messengerResolver.LoadMessenger();
messengerResolver.Init(config.Data.Server.Certificate, config.Data.Server.Password); messengerResolver.Init(config.Data.Server.SSL.File, config.Data.Server.SSL.Password);
} }
} }

View File

@@ -293,7 +293,7 @@ namespace linker.plugins.relay
await TaskRelay(); await TaskRelay();
} }
return true; return true;
}, 1000); }, 3000);
} }
sealed class TestInfo sealed class TestInfo
{ {

View File

@@ -1,5 +1,6 @@
using linker.client.config; using linker.client.config;
using linker.config; using linker.config;
using linker.libs;
using LiteDB; using LiteDB;
using MemoryPack; using MemoryPack;
using System.Diagnostics.CodeAnalysis; using System.Diagnostics.CodeAnalysis;
@@ -55,10 +56,11 @@ namespace linker.config
} }
public sealed class RelayConfigServerInfo public sealed class RelayConfigServerInfo
{ {
/// <summary> #if DEBUG
/// 中继密钥 public string SecretKey { get; set; } = Helper.GlobalString;
/// </summary> #else
public string SecretKey { get; set; } = Guid.NewGuid().ToString().ToUpper(); public string SecretKey { get; set; } = Guid.NewGuid().ToString().ToUpper();
#endif
/// <summary> /// <summary>
/// 缓冲区 /// 缓冲区
/// </summary> /// </summary>
@@ -83,7 +85,7 @@ namespace linker.config
/// <summary> /// <summary>
/// 密钥 /// 密钥
/// </summary> /// </summary>
public string SecretKey { get; set; } = "snltty"; public string SecretKey { get; set; } = Helper.GlobalString;
/// <summary> /// <summary>
/// 服务器地址 /// 服务器地址
/// </summary> /// </summary>

View File

@@ -34,10 +34,10 @@ namespace linker.plugins.relay.transport
this.messengerSender = messengerSender; this.messengerSender = messengerSender;
this.clientSignInState = clientSignInState; this.clientSignInState = clientSignInState;
string path = Path.GetFullPath(config.Data.Client.Certificate); string path = Path.GetFullPath(config.Data.Client.SSL.File);
if (File.Exists(path)) if (File.Exists(path))
{ {
certificate = new X509Certificate2(path, config.Data.Client.Password, X509KeyStorageFlags.Exportable); certificate = new X509Certificate2(path, config.Data.Client.SSL.Password, X509KeyStorageFlags.Exportable);
} }
} }

View File

@@ -10,8 +10,7 @@ namespace linker.config
public ConfigServerInfo() { } public ConfigServerInfo() { }
public int ServicePort { get; set; } = 1802; public int ServicePort { get; set; } = 1802;
public string Certificate { get; set; } = "./snltty.pfx"; public ServerCertificateInfo SSL { get; set; } = new ServerCertificateInfo();
public string Password { get; set; } = "oeq9tw1o";
public object Deserialize(string text) public object Deserialize(string text)
{ {
@@ -22,4 +21,11 @@ namespace linker.config
return obj.ToJsonFormat(); return obj.ToJsonFormat();
} }
} }
public sealed partial class ServerCertificateInfo
{
public ServerCertificateInfo() { }
public string File { get; set; } = "./snltty.pfx";
public string Password { get; set; } = "oeq9tw1o";
}
} }

View File

@@ -1,4 +1,5 @@
using LiteDB; using linker.libs;
using LiteDB;
using MemoryPack; using MemoryPack;
using System.Net; using System.Net;
using System.Text.Json.Serialization; using System.Text.Json.Serialization;
@@ -11,7 +12,7 @@ namespace linker.client.config
/// <summary> /// <summary>
/// 服务器穿透密钥 /// 服务器穿透密钥
/// </summary> /// </summary>
public string SForwardSecretKey { get; set; } = "snltty"; public string SForwardSecretKey { get; set; } = Helper.GlobalString;
/// <summary> /// <summary>
/// 服务器穿透列表 /// 服务器穿透列表
/// </summary> /// </summary>
@@ -81,7 +82,7 @@ namespace linker.config
} }
public sealed class SForwardConfigInfo public sealed class SForwardConfigInfo
{ {
public string SecretKey { get; set; } = "snltty"; public string SecretKey { get; set; } = Helper.GlobalString;
} }
@@ -97,7 +98,11 @@ namespace linker.config
/// <summary> /// <summary>
/// 密钥 /// 密钥
/// </summary> /// </summary>
#if DEBUG
public string SecretKey { get; set; } = Helper.GlobalString;
#else
public string SecretKey { get; set; } = Guid.NewGuid().ToString().ToUpper(); public string SecretKey { get; set; } = Guid.NewGuid().ToString().ToUpper();
#endif
/// <summary> /// <summary>
/// 缓冲区 /// 缓冲区
/// </summary> /// </summary>

View File

@@ -8,7 +8,7 @@ namespace linker.plugins.sforward.proxy
{ {
private readonly NumberSpace ns = new NumberSpace(); private readonly NumberSpace ns = new NumberSpace();
private byte[] flagBytes = Encoding.UTF8.GetBytes($"snltty.sforward"); private byte[] flagBytes = Encoding.UTF8.GetBytes($"{Helper.GlobalString}.sforward");
private readonly SForwardFlow sForwardFlow; private readonly SForwardFlow sForwardFlow;
public SForwardProxy(SForwardFlow sForwardFlow) public SForwardProxy(SForwardFlow sForwardFlow)

View File

@@ -27,7 +27,7 @@ namespace linker.plugins.signin
public void Set(ApiControllerParamsInfo param) public void Set(ApiControllerParamsInfo param)
{ {
ConfigSetInfo info = param.Content.DeJson<ConfigSetInfo>(); ConfigSetInfo info = param.Content.DeJson<ConfigSetInfo>();
clientSignInTransfer.Set(info.Name, info.GroupId); clientSignInTransfer.Set(info.Name, info.Groups);
} }
public async Task<bool> SetName(ApiControllerParamsInfo param) public async Task<bool> SetName(ApiControllerParamsInfo param)
@@ -38,7 +38,7 @@ namespace linker.plugins.signin
{ {
if (config.Data.Client.HasAccess(ClientApiAccess.RenameSelf) == false) return false; if (config.Data.Client.HasAccess(ClientApiAccess.RenameSelf) == false) return false;
clientSignInTransfer.SetName(info.NewName); clientSignInTransfer.Set(info.NewName);
} }
else else
{ {
@@ -53,12 +53,17 @@ namespace linker.plugins.signin
} }
return true; return true;
} }
public void SetGroups(ApiControllerParamsInfo param)
{
ClientGroupInfo[] info = param.Content.DeJson<ClientGroupInfo[]>();
clientSignInTransfer.Set(info);
}
[ClientApiAccessAttribute(ClientApiAccess.Config)] [ClientApiAccessAttribute(ClientApiAccess.Config)]
public async Task<bool> SetServers(ApiControllerParamsInfo param) public bool SetServers(ApiControllerParamsInfo param)
{ {
ClientServerInfo[] servers = param.Content.DeJson<ClientServerInfo[]>(); ClientServerInfo[] servers = param.Content.DeJson<ClientServerInfo[]>();
await clientSignInTransfer.SetServers(servers); clientSignInTransfer.SetServers(servers);
return true; return true;
} }
@@ -117,7 +122,6 @@ namespace linker.plugins.signin
return new SignInIdsResponseInfo { }; return new SignInIdsResponseInfo { };
} }
} }
[MemoryPackable] [MemoryPackable]
@@ -130,7 +134,7 @@ namespace linker.plugins.signin
public sealed class ConfigSetInfo public sealed class ConfigSetInfo
{ {
public string Name { get; set; } public string Name { get; set; }
public string GroupId { get; set; } public ClientGroupInfo[] Groups { get; set; }
} }
} }

View File

@@ -20,7 +20,7 @@ namespace linker.plugins.signin.messenger
public void Name(IConnection connection) public void Name(IConnection connection)
{ {
ConfigSetNameInfo info = MemoryPackSerializer.Deserialize<ConfigSetNameInfo>(connection.ReceiveRequestWrap.Payload.Span); ConfigSetNameInfo info = MemoryPackSerializer.Deserialize<ConfigSetNameInfo>(connection.ReceiveRequestWrap.Payload.Span);
clientSignInTransfer.SetName(info.NewName); clientSignInTransfer.Set(info.NewName);
} }
} }

View File

@@ -43,10 +43,10 @@ namespace linker.plugins.tunnel
this.excludeIPTransfer = excludeIPTransfer; this.excludeIPTransfer = excludeIPTransfer;
this.upnpTransfer = upnpTransfer; this.upnpTransfer = upnpTransfer;
string path = Path.GetFullPath(config.Data.Client.Certificate); string path = Path.GetFullPath(config.Data.Client.SSL.File);
if (File.Exists(path)) if (File.Exists(path))
{ {
Certificate = new X509Certificate2(path, config.Data.Client.Password, X509KeyStorageFlags.Exportable); Certificate = new X509Certificate2(path, config.Data.Client.SSL.Password, X509KeyStorageFlags.Exportable);
} }
} }

View File

@@ -0,0 +1,22 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Text;
using System.Threading.Tasks;
namespace linker.plugins.tuntap
{
public sealed class DHCPTreansfer
{
public void AddNetwork()
{
}
public IPAddress GetIP()
{
return IPAddress.Any;
}
}
}

View File

@@ -25,6 +25,7 @@ namespace linker.plugins.tuntap.client
private readonly LinkerTunDeviceAdapter linkerTunDeviceAdapter; private readonly LinkerTunDeviceAdapter linkerTunDeviceAdapter;
private string groupid = string.Empty; private string groupid = string.Empty;
private readonly FileConfig config; private readonly FileConfig config;
private readonly RunningConfig runningConfig; private readonly RunningConfig runningConfig;
private readonly ClientSignInState clientSignInState; private readonly ClientSignInState clientSignInState;
@@ -36,6 +37,7 @@ namespace linker.plugins.tuntap.client
this.runningConfig = runningConfig; this.runningConfig = runningConfig;
this.linkerTunDeviceAdapter = linkerTunDeviceAdapter; this.linkerTunDeviceAdapter = linkerTunDeviceAdapter;
this.clientSignInState = clientSignInState; this.clientSignInState = clientSignInState;
clientSignInState.NetworkEnabledHandle += (times) => ClearIPs(); clientSignInState.NetworkEnabledHandle += (times) => ClearIPs();
} }
@@ -153,16 +155,6 @@ namespace linker.plugins.tuntap.client
list.Add(machineId); list.Add(machineId);
} }
private void ClearIPs()
{
if (groupid != config.Data.Client.GroupId)
{
ip2MachineDic.Clear();
ipConnections.Clear();
}
groupid = config.Data.Client.GroupId;
}
/// <summary> /// <summary>
/// 打洞或者中继 /// 打洞或者中继
/// </summary> /// </summary>
@@ -190,5 +182,16 @@ namespace linker.plugins.tuntap.client
} }
private void ClearIPs()
{
if (groupid != config.Data.Client.Group.Id)
{
ip2MachineDic.Clear();
ipConnections.Clear();
}
groupid = config.Data.Client.Group.Id;
}
} }
} }

View File

@@ -56,7 +56,7 @@ namespace linker.plugins.tuntap.client
} }
private void NetworkEanble(int times) private void NetworkEanble(int times)
{ {
if (runningConfig.Data.Tuntap.Group2IP.TryGetValue(config.Data.Client.GroupId, out TuntapGroup2IPInfo tuntapGroup2IPInfo)) if (runningConfig.Data.Tuntap.Group2IP.TryGetValue(config.Data.Client.Group.Id, out TuntapGroup2IPInfo tuntapGroup2IPInfo))
{ {
if (tuntapGroup2IPInfo.IP.Equals(runningConfig.Data.Tuntap.IP) == false || tuntapGroup2IPInfo.PrefixLength != runningConfig.Data.Tuntap.PrefixLength) if (tuntapGroup2IPInfo.IP.Equals(runningConfig.Data.Tuntap.IP) == false || tuntapGroup2IPInfo.PrefixLength != runningConfig.Data.Tuntap.PrefixLength)
{ {
@@ -222,7 +222,7 @@ namespace linker.plugins.tuntap.client
runningConfig.Data.Tuntap.Forwards = info.Forwards; runningConfig.Data.Tuntap.Forwards = info.Forwards;
TuntapGroup2IPInfo tuntapGroup2IPInfo = new TuntapGroup2IPInfo { IP = info.IP, PrefixLength = info.PrefixLength }; TuntapGroup2IPInfo tuntapGroup2IPInfo = new TuntapGroup2IPInfo { IP = info.IP, PrefixLength = info.PrefixLength };
runningConfig.Data.Tuntap.Group2IP.AddOrUpdate(config.Data.Client.GroupId, tuntapGroup2IPInfo, (a, b) => tuntapGroup2IPInfo); runningConfig.Data.Tuntap.Group2IP.AddOrUpdate(config.Data.Client.Group.Id, tuntapGroup2IPInfo, (a, b) => tuntapGroup2IPInfo);
runningConfig.Data.Update(); runningConfig.Data.Update();
if (Status == TuntapStatus.Running && needReboot) if (Status == TuntapStatus.Running && needReboot)
@@ -434,16 +434,14 @@ namespace linker.plugins.tuntap.client
/// <param name="infos"></param> /// <param name="infos"></param>
private void CheckLanIPs() private void CheckLanIPs()
{ {
uint[] localIps = config.Data.Client.Tunnel.LocalIPs.Where(c => c.AddressFamily == System.Net.Sockets.AddressFamily.InterNetwork).Concat(config.Data.Client.Tunnel.RouteIPs) uint[] excludeIps = ExcludeIps();
.Select(c => BinaryPrimitives.ReadUInt32BigEndian(c.GetAddressBytes()))
.ToArray();
var ips = tuntapInfos.Values.Where(c => c.MachineId != config.Data.Client.Id).Select(c => var ips = tuntapInfos.Values.Where(c => c.MachineId != config.Data.Client.Id).Select(c =>
{ {
return new TuntapVeaLanIPAddressList return new TuntapVeaLanIPAddressList
{ {
MachineId = c.MachineId, MachineId = c.MachineId,
IPS = ParseIPs(c.LanIPs, c.Masks, c.MachineId).Where(c => localIps.Select(d => d & c.MaskValue).Contains(c.NetWork)).ToList(), IPS = ParseIPs(c.LanIPs, c.Masks, c.MachineId).Where(c => excludeIps.Select(d => d & c.MaskValue).Contains(c.NetWork)).ToList(),
}; };
}).ToList(); }).ToList();
@@ -456,16 +454,23 @@ namespace linker.plugins.tuntap.client
Version.Add(); Version.Add();
} }
private uint[] ExcludeIps()
private List<TuntapVeaLanIPAddressList> ParseIPs(List<TuntapInfo> infos)
{ {
uint[] localIps = config.Data.Client.Tunnel.LocalIPs.Where(c => c.AddressFamily == System.Net.Sockets.AddressFamily.InterNetwork) return //本机局域网IP
.Concat(new IPAddress[] { runningConfig.Data.Tuntap.IP }) config.Data.Client.Tunnel.LocalIPs.Where(c => c.AddressFamily == System.Net.Sockets.AddressFamily.InterNetwork)
//网卡IP 服务器IP
.Concat(new IPAddress[] { runningConfig.Data.Tuntap.IP, clientSignInState.Connection.Address.Address })
//网卡配置的局域网IP
.Concat(runningConfig.Data.Tuntap.LanIPs.Where(c => c != null)) .Concat(runningConfig.Data.Tuntap.LanIPs.Where(c => c != null))
//路由上的IP
.Concat(config.Data.Client.Tunnel.RouteIPs) .Concat(config.Data.Client.Tunnel.RouteIPs)
.Select(c => BinaryPrimitives.ReadUInt32BigEndian(c.GetAddressBytes())) .Select(c => BinaryPrimitives.ReadUInt32BigEndian(c.GetAddressBytes()))
.ToArray(); .ToArray();
}
private List<TuntapVeaLanIPAddressList> ParseIPs(List<TuntapInfo> infos)
{
//排除的IP
uint[] excludeIps = ExcludeIps();
return infos return infos
//自己的ip不要 //自己的ip不要
.Where(c => c.IP.Equals(runningConfig.Data.Tuntap.IP) == false && c.LastTicks.HasValue()) .Where(c => c.IP.Equals(runningConfig.Data.Tuntap.IP) == false && c.LastTicks.HasValue())
@@ -476,8 +481,7 @@ namespace linker.plugins.tuntap.client
{ {
MachineId = c.MachineId, MachineId = c.MachineId,
IPS = ParseIPs(c.LanIPs, c.Masks, c.MachineId) IPS = ParseIPs(c.LanIPs, c.Masks, c.MachineId)
//这边的局域网IP也不要为了防止将本机局域网IP路由到别的地方 .Where(c => excludeIps.Select(d => d & c.MaskValue).Contains(c.NetWork) == false).ToList(),
.Where(c => localIps.Select(d => d & c.MaskValue).Contains(c.NetWork) == false).ToList(),
}; };
}).ToList(); }).ToList();
} }

View File

@@ -60,7 +60,7 @@ namespace linker.config
/// <summary> /// <summary>
/// 密钥 /// 密钥
/// </summary> /// </summary>
public string SecretKey { get; set; } = "snltty"; public string SecretKey { get; set; } = Helper.GlobalString;
} }
public partial class ConfigServerInfo public partial class ConfigServerInfo
@@ -76,7 +76,11 @@ namespace linker.config
/// <summary> /// <summary>
/// 密钥 /// 密钥
/// </summary> /// </summary>
#if DEBUG
public string SecretKey { get; set; } = Helper.GlobalString;
#else
public string SecretKey { get; set; } = Guid.NewGuid().ToString().ToUpper(); public string SecretKey { get; set; } = Guid.NewGuid().ToString().ToUpper();
#endif
} }

View File

@@ -1,4 +1,5 @@
using linker.libs.extends; using linker.libs;
using linker.libs.extends;
using LiteDB; using LiteDB;
using LiteDB.Engine; using LiteDB.Engine;
using System.Net; using System.Net;
@@ -22,12 +23,12 @@ namespace linker.store
if (Encoded() == false) if (Encoded() == false)
{ {
database = new LiteDatabase(@"./configs/db.db", bsonMapper); database = new LiteDatabase(@"./configs/db.db", bsonMapper);
var rebuildOptions = new RebuildOptions { Password = "snltty" }; var rebuildOptions = new RebuildOptions { Password = Helper.GlobalString };
database.Rebuild(rebuildOptions); database.Rebuild(rebuildOptions);
} }
else else
{ {
database = new LiteDatabase(new ConnectionString("Filename=./configs/db.db; Password=snltty"), bsonMapper); database = new LiteDatabase(new ConnectionString($"Filename=./configs/db.db; Password={Helper.GlobalString}"), bsonMapper);
} }
} }

View File

@@ -1,5 +1,5 @@
v1.4.9 v1.4.9
2024-10-14 17:04:07 2024-10-16 17:33:40
1. 使用原生成器替代反射 1. 使用原生成器替代反射
2. 统计所有服务端,客户端数量 2. 统计所有服务端,客户端数量
3. 创建docker镜像清单自动识别平台 3. 创建docker镜像清单自动识别平台