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
5. 如果报错,那你需要打开`Hyper-V`功能, `控制面板\程序\启用或关闭 Windows 功能`
6. 确认支持即可剩下的交给linker
:::
![Docusaurus Plushie](./img/hyper-v.png)
:::

View File

@@ -6,9 +6,10 @@ sidebar_position: 2
:::danger[重要]
1. 你要确定你知道你在搞什么如果只是简单的P2P通信上面的内容已经够了以下的东西不要看不要看不要看
2. 确定要搞,那么请看示意图
2. 确定要搞,那么请看示意图(仅示例,以下绿色内容与此图无关)
![Docusaurus Plushie](./img/tuntap12n.jpg)
:::
:::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
```
这里我`公司-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-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="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="ConfigSyncTreansfer", ClassNameSpace="linker.plugins.config", InterfaceName="linker.plugins.config.IConfigSync"},
};
public void Initialize(IncrementalGeneratorInitializationContext context)

View File

@@ -21,13 +21,6 @@ namespace linker.libs
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 static ConsoleCtrlDelegate handler;
[DllImport("Kernel32")]

View File

@@ -107,7 +107,7 @@ namespace linker.libs
for (ushort i = 1; i <= 5; i++)
{
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)))
{

View File

@@ -4,6 +4,7 @@ import { sendWebsocketMsg } from './request'
export const setSignIn = (data) => {
return sendWebsocketMsg('signInclient/set', data);
}
export const setSignInServers = (servers) => {
return sendWebsocketMsg('signInclient/setservers', servers);
}
@@ -27,3 +28,6 @@ export const signInDel = (machineId) => {
export const setSignInName = (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>
<ConnectionsEdit v-if="connections.showEdit" v-model="connections.showEdit" ></ConnectionsEdit>
<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>
<ForwardCopy v-if="forward.showCopy" v-model="forward.showCopy" ></ForwardCopy>
<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 Tuntap from './Tuntap.vue'
import TuntapEdit from './TuntapEdit.vue'
import TuntapDHCP from './TuntapDHCP.vue'
import Tunnel from './Tunnel.vue'
import TunnelEdit from './TunnelEdit.vue'
import Forward from './Forward.vue'
@@ -60,7 +62,7 @@ import { provideDevices } from './devices'
import { provideUpdater } from './updater'
import { provideAccess } from './access'
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) {
const globalData = injectGlobalData();

View File

@@ -1,5 +1,8 @@
<template>
<el-table-column prop="tuntap" label="虚拟网卡" width="160">
<template #header>
<a href="javascript:;" class="a-line">虚拟网卡</a>
</template>
<template #default="scope">
<div v-if="tuntap.list[scope.row.MachineId]">
<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,
current: null,
list: {},
hashcode: 0
hashcode: 0,
showDHCP: false,
});
provide(tuntapSymbol, tuntap);

View File

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

View File

@@ -32,15 +32,6 @@
</el-col>
</el-row>
</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-row>
<el-col :span="12">
@@ -57,7 +48,12 @@
</el-form-item>
<el-form-item label="" label-width="0">
<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-input v-model="state.form.updaterSecretKey" maxlength="36" show-word-limit />
</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:{},
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 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({
tab:settingComponents[0].name
});

View File

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

View File

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

View File

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

View File

@@ -9,7 +9,9 @@
<el-input v-model="state.form.name" maxlength="12" show-word-limit />
</el-form-item>
<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>
</div>
@@ -26,9 +28,9 @@ import { setSignIn } from '@/apis/signin';
import { injectGlobalData } from '@/provide';
import { ElMessage } from 'element-plus';
import { computed, reactive, ref } from 'vue';
import {Promotion} from '@element-plus/icons-vue'
import {Promotion,CirclePlus} from '@element-plus/icons-vue'
export default {
components:{Promotion},
components:{Promotion,CirclePlus},
props:['config'],
setup(props) {
@@ -38,9 +40,10 @@ export default {
show: false,
loading: false,
connected: computed(() => globalData.value.signin.Connected),
groupid: globalData.value.config.Client.Group.Id,
form: {
name: globalData.value.config.Client.Name,
groupid: globalData.value.config.Client.GroupId,
groups: globalData.value.config.Client.Groups,
},
rules: {},
});
@@ -50,9 +53,22 @@ export default {
return;
}
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;
}
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 = () => {
state.loading = true;
setSignIn(state.form).then(() => {
@@ -68,7 +84,7 @@ export default {
});
}
return {
config:props.config, state, handleConfig, handleSave
config:props.config, state, handleConfig, handleSave,handleGroupChange
}
}
}

View File

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

View File

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

View File

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

View File

@@ -8,6 +8,7 @@ using System.Net;
using System.Net.Sockets;
using linker.plugins.messenger;
using linker.plugins.signIn.args;
using linker.plugins.signin;
namespace linker.plugins.client
{
@@ -62,7 +63,7 @@ namespace linker.plugins.client
/// <returns></returns>
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");
return;
@@ -149,7 +150,7 @@ namespace linker.plugins.client
MachineId = config.Data.Client.Id,
Version = config.Data.Version,
Args = args,
GroupId = config.Data.Client.GroupId,
GroupId = config.Data.Client.Group.Id,
})
}).ConfigureAwait(false);
if (resp.Code != MessageResponeCodes.OK)
@@ -207,39 +208,30 @@ namespace linker.plugins.client
/// 修改客户端名称
/// </summary>
/// <param name="newName"></param>
public void SetName(string newName)
{
string name = config.Data.Client.Name;
if (name != newName)
public void Set(string newName)
{
config.Data.Client.Name = newName;
config.Data.Update();
SignOut();
_ = SignIn();
}
}
/// <summary>
/// 修改客户端名称和分组编号
/// </summary>
/// <param name="newName"></param>
/// <param name="newGroupid"></param>
public void Set(string newName, string newGroupid)
{
string name = config.Data.Client.Name;
string gid = config.Data.Client.GroupId;
if (name != newName || gid != newGroupid)
/// <param name="groups"></param>
public void Set(string newName, ClientGroupInfo[] groups)
{
config.Data.Client.Name = newName;
config.Data.Client.GroupId = newGroupid;
config.Data.Client.Groups = groups.DistinctBy(c => c.Name).ToArray();
config.Data.Update();
SignOut();
_ = SignIn();
}
/// <summary>
/// 设置分组编号
/// </summary>
/// <param name="groups"></param>
public void Set(ClientGroupInfo[] groups)
{
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);
}
/// <summary>
/// 获取一个新的id
/// </summary>
@@ -290,22 +281,14 @@ namespace linker.plugins.client
/// 修改信标服务器列表
/// </summary>
/// <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.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);
}
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<ClientSignInTransfer>();
serviceCollection.AddSingleton<SignInArgsSecretKeyClient>();
serviceCollection.AddSingleton<SignInArgsGroupPasswordClient>();
}
@@ -45,6 +50,7 @@ namespace linker.plugins.client
public void AddServer(ServiceCollection serviceCollection, FileConfig config)
{
serviceCollection.AddSingleton<SignInArgsSecretKeyServer>();
serviceCollection.AddSingleton<SignInArgsGroupPasswordServer>();
}
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.Reflection;
using System.Text;
using System.Text.Json.Serialization;
namespace linker.client.config
{
@@ -91,10 +92,7 @@ namespace linker.config
};
public ClientServerInfo[] Servers
{
get => servers; set
{
servers = value;
}
get => servers; set { servers = value; }
}
public ClientServerInfo ServerInfo => servers[0];
@@ -108,6 +106,7 @@ namespace linker.config
}
}
private string name = Dns.GetHostName().SubStr(0, 12);
public string Name
{
@@ -118,7 +117,7 @@ namespace linker.config
}
#if DEBUG
private string groupid = "snltty";
private string groupid = Helper.GlobalString;
#else
private string groupid = string.Empty;
#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)
{
@@ -141,7 +146,6 @@ namespace linker.config
return Convert.ToBase64String(crypto.Encode(Encoding.UTF8.GetBytes(obj.ToJson())));
#endif
}
public object Deserialize(string text)
{
if (text.Contains("ApiPassword"))
@@ -150,7 +154,52 @@ namespace linker.config
}
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]
public sealed partial class ClientServerInfo
@@ -166,6 +215,7 @@ namespace linker.config
}
}
[AttributeUsage(AttributeTargets.Method, Inherited = false, AllowMultiple = false)]
public sealed class ClientApiAccessAttribute : Attribute
{
@@ -279,6 +329,9 @@ namespace linker.config
[ClientAccessDisplayAttribute("查看流量")]
Flow = ((ulong)1 << 33),
[ClientAccessDisplayAttribute("同步配置")]
Sync = ((ulong)1 << 34),
Full = ulong.MaxValue >> (64 - 52),
}

View File

@@ -20,8 +20,9 @@ namespace linker.plugins.config
private readonly MessengerSender sender;
private readonly ClientSignInState clientSignInState;
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.config = config;
@@ -29,6 +30,7 @@ namespace linker.plugins.config
this.sender = sender;
this.clientSignInState = clientSignInState;
this.accessTransfer = accessTransfer;
this.configSyncTreansfer = configSyncTreansfer;
}
public object Get(ApiControllerParamsInfo param)
@@ -42,7 +44,7 @@ namespace linker.plugins.config
if (info.Common.Modes.Contains("client"))
{
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.ApiPort = info.Client.Api;
config.Data.Client.CApi.ApiPassword = info.Client.Password;
@@ -163,6 +165,8 @@ namespace linker.plugins.config
client.Access = accessTransfer.AssignAccess((ClientApiAccess)configExportInfo.Access);
client.OnlyNode = true;
client.Action.Args.Clear();
client.Groups = new ClientGroupInfo[] { client.Group };
File.WriteAllText(Path.Combine(configPath, $"client.json"), client.Serialize(client));
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>();
await sender.SendOnly(new MessageRequestWrap
return configSyncTreansfer.GetNames();
}
[ClientApiAccessAttribute(ClientApiAccess.Sync)]
public bool Sync(ApiControllerParamsInfo param)
{
Connection = clientSignInState.Connection,
MessengerId = (ushort)ConfigMessengerIds.SecretKeyAsyncForward,
Payload = MemoryPackSerializer.Serialize(info)
});
string[] names = param.Content.DeJson<string[]>();
configSyncTreansfer.Sync(names);
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
@@ -258,6 +248,8 @@ namespace linker.plugins.config
{
public string Name { get; set; }
public string GroupId { get; set; }
public string GroupPassword { get; set; }
public int Api { get; set; }
public int Web { get; set; }
public string Password { get; set; }
@@ -302,7 +294,6 @@ namespace linker.plugins.config
public string[] Modes { get; set; }
}
public sealed class ConfigExportInfo
{
public string Name { get; set; }

View File

@@ -28,6 +28,10 @@ namespace linker.plugins.config
serviceCollection.AddSingleton<AccessTransfer>();
serviceCollection.AddSingleton<ConfigSyncTreansfer>();
}
public void AddServer(ServiceCollection serviceCollection, FileConfig config)
@@ -38,6 +42,8 @@ namespace linker.plugins.config
public void UseClient(ServiceProvider serviceProvider, FileConfig config)
{
RunningConfig runningConfig = serviceProvider.GetService<RunningConfig>();
ConfigSyncTreansfer configSyncTreansfer = serviceProvider.GetService<ConfigSyncTreansfer>();
}
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 System.Text.Json.Serialization;
using linker.config;
using MemoryPack;
using linker.libs.extends;
using linker.tunnel.wanport;
namespace linker.client.config
{
@@ -61,7 +58,7 @@ namespace linker.client.config
Data.Updated--;
}
return true;
}, 1000);
}, 3000);
}
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)
{
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))
{
List<SignCacheInfo> caches = signCaching.Get(cache.GroupId);
@@ -96,29 +96,7 @@ namespace linker.plugins.config.messenger
tasks.Add(sender.SendOnly(new MessageRequestWrap
{
Connection = item.Connection,
MessengerId = (ushort)ConfigMessengerIds.SecretKeyAsync,
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,
MessengerId = (ushort)ConfigMessengerIds.Sync,
Payload = connection.ReceiveRequestWrap.Payload,
Timeout = 1000,
}));
@@ -134,12 +112,14 @@ namespace linker.plugins.config.messenger
private readonly AccessTransfer accessTransfer;
private readonly FileConfig fileConfig;
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.fileConfig = fileConfig;
this.clientSignInTransfer = clientSignInTransfer;
this.syncTreansfer = syncTreansfer;
}
[MessengerId((ushort)ConfigMessengerIds.Access)]
@@ -158,38 +138,12 @@ namespace linker.plugins.config.messenger
connection.Write(Helper.TrueArray);
}
[MessengerId((ushort)ConfigMessengerIds.SecretKeyAsync)]
public void SecretKeyAsync(IConnection connection)
[MessengerId((ushort)ConfigMessengerIds.Sync)]
public void Sync(IConnection connection)
{
SecretKeyAsyncInfo info = MemoryPackSerializer.Deserialize<SecretKeyAsyncInfo>(connection.ReceiveRequestWrap.Payload.Span);
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();
ConfigAsyncInfo info = MemoryPackSerializer.Deserialize<ConfigAsyncInfo>(connection.ReceiveRequestWrap.Payload.Span);
syncTreansfer.Sync(info);
}
[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,
AccessUpdateForward = 2506,
SecretKeyAsync = 2507,
SecretKeyAsyncForward = 2508,
ServerAsync = 2509,
ServerAsyncForward = 2510,
Sync = 2507,
SyncForward = 2508,
Max = 2599
}

View File

@@ -271,6 +271,8 @@ namespace linker.plugins.forward.proxy
private void TaskUdp()
{
TimerHelper.SetInterval(() =>
{
if(udpConnections.Count > 0)
{
var connections = udpConnections.Where(c => c.Value.Timeout).Select(c => c.Key).ToList();
foreach (var item in connections)
@@ -286,6 +288,8 @@ namespace linker.plugins.forward.proxy
}
}
}
}
return true;
}, 5000);
}

View File

@@ -41,7 +41,7 @@ namespace linker.plugins.messenger
MessengerResolver messengerResolver = serviceProvider.GetService<MessengerResolver>();
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.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();
}
return true;
}, 1000);
}, 3000);
}
sealed class TestInfo
{

View File

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

View File

@@ -34,10 +34,10 @@ namespace linker.plugins.relay.transport
this.messengerSender = messengerSender;
this.clientSignInState = clientSignInState;
string path = Path.GetFullPath(config.Data.Client.Certificate);
string path = Path.GetFullPath(config.Data.Client.SSL.File);
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 int ServicePort { get; set; } = 1802;
public string Certificate { get; set; } = "./snltty.pfx";
public string Password { get; set; } = "oeq9tw1o";
public ServerCertificateInfo SSL { get; set; } = new ServerCertificateInfo();
public object Deserialize(string text)
{
@@ -22,4 +21,11 @@ namespace linker.config
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 System.Net;
using System.Text.Json.Serialization;
@@ -11,7 +12,7 @@ namespace linker.client.config
/// <summary>
/// 服务器穿透密钥
/// </summary>
public string SForwardSecretKey { get; set; } = "snltty";
public string SForwardSecretKey { get; set; } = Helper.GlobalString;
/// <summary>
/// 服务器穿透列表
/// </summary>
@@ -81,7 +82,7 @@ namespace linker.config
}
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>
#if DEBUG
public string SecretKey { get; set; } = Helper.GlobalString;
#else
public string SecretKey { get; set; } = Guid.NewGuid().ToString().ToUpper();
#endif
/// <summary>
/// 缓冲区
/// </summary>

View File

@@ -8,7 +8,7 @@ namespace linker.plugins.sforward.proxy
{
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;
public SForwardProxy(SForwardFlow sForwardFlow)

View File

@@ -27,7 +27,7 @@ namespace linker.plugins.signin
public void Set(ApiControllerParamsInfo param)
{
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)
@@ -38,7 +38,7 @@ namespace linker.plugins.signin
{
if (config.Data.Client.HasAccess(ClientApiAccess.RenameSelf) == false) return false;
clientSignInTransfer.SetName(info.NewName);
clientSignInTransfer.Set(info.NewName);
}
else
{
@@ -53,12 +53,17 @@ namespace linker.plugins.signin
}
return true;
}
public void SetGroups(ApiControllerParamsInfo param)
{
ClientGroupInfo[] info = param.Content.DeJson<ClientGroupInfo[]>();
clientSignInTransfer.Set(info);
}
[ClientApiAccessAttribute(ClientApiAccess.Config)]
public async Task<bool> SetServers(ApiControllerParamsInfo param)
public bool SetServers(ApiControllerParamsInfo param)
{
ClientServerInfo[] servers = param.Content.DeJson<ClientServerInfo[]>();
await clientSignInTransfer.SetServers(servers);
clientSignInTransfer.SetServers(servers);
return true;
}
@@ -117,7 +122,6 @@ namespace linker.plugins.signin
return new SignInIdsResponseInfo { };
}
}
[MemoryPackable]
@@ -130,7 +134,7 @@ namespace linker.plugins.signin
public sealed class ConfigSetInfo
{
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)
{
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.upnpTransfer = upnpTransfer;
string path = Path.GetFullPath(config.Data.Client.Certificate);
string path = Path.GetFullPath(config.Data.Client.SSL.File);
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 string groupid = string.Empty;
private readonly FileConfig config;
private readonly RunningConfig runningConfig;
private readonly ClientSignInState clientSignInState;
@@ -36,6 +37,7 @@ namespace linker.plugins.tuntap.client
this.runningConfig = runningConfig;
this.linkerTunDeviceAdapter = linkerTunDeviceAdapter;
this.clientSignInState = clientSignInState;
clientSignInState.NetworkEnabledHandle += (times) => ClearIPs();
}
@@ -153,16 +155,6 @@ namespace linker.plugins.tuntap.client
list.Add(machineId);
}
private void ClearIPs()
{
if (groupid != config.Data.Client.GroupId)
{
ip2MachineDic.Clear();
ipConnections.Clear();
}
groupid = config.Data.Client.GroupId;
}
/// <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)
{
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)
{
@@ -222,7 +222,7 @@ namespace linker.plugins.tuntap.client
runningConfig.Data.Tuntap.Forwards = info.Forwards;
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();
if (Status == TuntapStatus.Running && needReboot)
@@ -434,16 +434,14 @@ namespace linker.plugins.tuntap.client
/// <param name="infos"></param>
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)
.Select(c => BinaryPrimitives.ReadUInt32BigEndian(c.GetAddressBytes()))
.ToArray();
uint[] excludeIps = ExcludeIps();
var ips = tuntapInfos.Values.Where(c => c.MachineId != config.Data.Client.Id).Select(c =>
{
return new TuntapVeaLanIPAddressList
{
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();
@@ -456,16 +454,23 @@ namespace linker.plugins.tuntap.client
Version.Add();
}
private List<TuntapVeaLanIPAddressList> ParseIPs(List<TuntapInfo> infos)
private uint[] ExcludeIps()
{
uint[] localIps = config.Data.Client.Tunnel.LocalIPs.Where(c => c.AddressFamily == System.Net.Sockets.AddressFamily.InterNetwork)
.Concat(new IPAddress[] { runningConfig.Data.Tuntap.IP })
return //本机局域网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))
//路由上的IP
.Concat(config.Data.Client.Tunnel.RouteIPs)
.Select(c => BinaryPrimitives.ReadUInt32BigEndian(c.GetAddressBytes()))
.ToArray();
}
private List<TuntapVeaLanIPAddressList> ParseIPs(List<TuntapInfo> infos)
{
//排除的IP
uint[] excludeIps = ExcludeIps();
return infos
//自己的ip不要
.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,
IPS = ParseIPs(c.LanIPs, c.Masks, c.MachineId)
//这边的局域网IP也不要为了防止将本机局域网IP路由到别的地方
.Where(c => localIps.Select(d => d & c.MaskValue).Contains(c.NetWork) == false).ToList(),
.Where(c => excludeIps.Select(d => d & c.MaskValue).Contains(c.NetWork) == false).ToList(),
};
}).ToList();
}

View File

@@ -60,7 +60,7 @@ namespace linker.config
/// <summary>
/// 密钥
/// </summary>
public string SecretKey { get; set; } = "snltty";
public string SecretKey { get; set; } = Helper.GlobalString;
}
public partial class ConfigServerInfo
@@ -76,7 +76,11 @@ namespace linker.config
/// <summary>
/// 密钥
/// </summary>
#if DEBUG
public string SecretKey { get; set; } = Helper.GlobalString;
#else
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.Engine;
using System.Net;
@@ -22,12 +23,12 @@ namespace linker.store
if (Encoded() == false)
{
database = new LiteDatabase(@"./configs/db.db", bsonMapper);
var rebuildOptions = new RebuildOptions { Password = "snltty" };
var rebuildOptions = new RebuildOptions { Password = Helper.GlobalString };
database.Rebuild(rebuildOptions);
}
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
2024-10-14 17:04:07
2024-10-16 17:33:40
1. 使用原生成器替代反射
2. 统计所有服务端,客户端数量
3. 创建docker镜像清单自动识别平台