This commit is contained in:
snltty
2024-10-25 17:12:40 +08:00
parent 50ad5f518e
commit 1b4deae2c1
56 changed files with 2190 additions and 780 deletions

View File

@@ -37,7 +37,7 @@ jobs:
release_name: v1.5.1.${{ steps.date.outputs.today }}
draft: false
prerelease: false
body: "1. 优化点对网和网对网的局域网IP包括禁用IP和冲突检测\r\n2. 一些UI优化\r\n3. 测试中测试中测试中不要更新用v1.4.9"
body: "1. 优化点对网和网对网的局域网IP包括禁用IP和冲突检测\r\n2. 一些UI优化\r\n3. 新增socks5代理\r\n4. 优化端口转发和内网穿透配置\r\n5. 优化网卡的一些东西\r\n6. 测试中测试中测试中不要更新用v1.4.9"
- name: upload-win-x86-oss
id: upload-win-x86-oss
uses: tvrcgo/oss-action@v0.1.1

View File

@@ -22,6 +22,7 @@ namespace linker.gen
new GeneratorInfo{ ClassName="MessengerResolverTypesLoader", ClassNameSpace="linker.plugins.messenger", InterfaceName="linker.plugins.messenger.IMessenger"},
new GeneratorInfo{ ClassName="ApiClientTypesLoader", ClassNameSpace="linker.plugins.capi", InterfaceName="linker.plugins.capi.IApiClientController"},
new GeneratorInfo{ ClassName="ConfigSyncTypesLoader", ClassNameSpace="linker.plugins.config", InterfaceName="linker.plugins.config.IConfigSync"},
new GeneratorInfo{ ClassName="DecenterTypesLoader", ClassNameSpace="linker.plugins.decenter", InterfaceName="linker.plugins.decenter.IDecenter"},
};
public void Initialize(IncrementalGeneratorInitializationContext context)

View File

@@ -15,10 +15,15 @@ namespace linker.libs
public void Add()
{
if(Interlocked.Increment(ref version) > ulong.MaxValue - ushort.MaxValue)
if (Interlocked.Increment(ref version) > ulong.MaxValue - ushort.MaxValue)
{
Interlocked.Exchange(ref version, 0);
Interlocked.Exchange(ref version, 1);
}
}
public bool Reset()
{
return Interlocked.Exchange(ref version, 0) > 0;
}
}
}

View File

@@ -0,0 +1,8 @@
import { sendWebsocketMsg } from './request'
export const getAccesss = (machineid) => {
return sendWebsocketMsg('access/GetAccesss', machineid);
}
export const setAccess = (data) => {
return sendWebsocketMsg('access/SetAccess', data);
}

View File

@@ -11,12 +11,6 @@ export const exportConfig = (data) => {
return sendWebsocketMsg('configclient/export', data);
}
export const getAccesss = (machineid) => {
return sendWebsocketMsg('configclient/GetAccesss', machineid);
}
export const setAccess = (data) => {
return sendWebsocketMsg('configclient/SetAccess', data);
}
export const getSyncNames = () => {
return sendWebsocketMsg('configclient/SyncNames');
}

View File

@@ -0,0 +1,25 @@
import { sendWebsocketMsg } from './request'
export const getSocks5Connections = (hashcode = '0') => {
return sendWebsocketMsg('socks5client/connections', hashcode);
}
export const removeSocks5Connection = (id) => {
return sendWebsocketMsg('socks5client/removeconnection', id);
}
export const getSocks5Info = (hashcode = '0') => {
return sendWebsocketMsg('socks5client/get', hashcode);
}
export const runSocks5 = (name) => {
return sendWebsocketMsg('socks5client/run', name);
}
export const stopSocks5 = (name) => {
return sendWebsocketMsg('socks5client/stop', name);
}
export const updateSocks5 = (name) => {
return sendWebsocketMsg('socks5client/update', name);
}
export const refreshSocks5 = () => {
return sendWebsocketMsg('socks5client/refresh');
}

View File

@@ -10,7 +10,7 @@
</el-dialog>
</template>
<script>
import { setAccess } from '@/apis/config';
import { setAccess } from '@/apis/access';
import { ElMessage } from 'element-plus';
import { reactive, ref, watch } from 'vue';
import Access from '@/views/full/devices/Access.vue'

View File

@@ -53,7 +53,7 @@
<script>
import { reactive, watch,computed, onMounted, onUnmounted } from 'vue';
import { ElMessage } from 'element-plus';
import { useConnections, useForwardConnections, useTuntapConnections } from './connections';
import { useConnections, useForwardConnections, useSocks5Connections, useTuntapConnections } from './connections';
import { Delete } from '@element-plus/icons-vue';
import { injectGlobalData } from '@/provide';
export default {
@@ -68,6 +68,7 @@ export default {
const connections = useConnections();
const forwardConnections = useForwardConnections();
const tuntapConnections = useTuntapConnections();
const socks5Connections = useSocks5Connections();
const state = reactive({
show: true,
protocolTypes:{1:'tcp',2:'udp',4:'msquic'},
@@ -78,6 +79,7 @@ export default {
return [
forwardConnections.value.list[connections.value.current],
tuntapConnections.value.list[connections.value.current],
socks5Connections.value.list[connections.value.current],
].filter(c=>!!c);
}),
});

View File

@@ -5,6 +5,7 @@
<el-table-column prop="Version" label="版本" width="110" sortable="custom"></el-table-column>
<el-table-column prop="tunnel" label="网关" width="90" sortable="custom"></el-table-column>
<el-table-column prop="tuntap" label="网卡IP" width="160" sortable="custom"></el-table-column>
<el-table-column prop="socks5" label="socks5" width="160" sortable="custom"></el-table-column>
<el-table-column prop="forward" label=""></el-table-column>
<el-table-column label="" width="74" fixed="right"></el-table-column>
</el-table>
@@ -12,7 +13,7 @@
<Device @edit="handleDeviceEdit" @refresh="handlePageRefresh"></Device>
<Tunnel @edit="handleTunnelEdit" @refresh="handleTunnelRefresh" @connections="handleTunnelConnections"></Tunnel>
<Tuntap @edit="handleTuntapEdit" @refresh="handleTuntapRefresh"></Tuntap>
<Socks5 @edit="_handleForwardEdit" @sedit="handleSForwardEdit"></Socks5>
<Socks5 @edit="handleSocks5Edit" @refresh="handleSocks5Refresh"></Socks5>
<Forward @edit="_handleForwardEdit" @sedit="handleSForwardEdit"></Forward>
<Oper @refresh="handlePageRefresh" @access="handleAccessEdit"></Oper>
</el-table>
@@ -28,6 +29,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>
<Socks5Edit v-if="socks5.showEdit" v-model="socks5.showEdit" @change="handleSocks5Refresh"></Socks5Edit>
<TuntapLease v-if="tuntap.showLease" v-model="tuntap.showLease" @change="handleTuntapRefresh"></TuntapLease>
<ForwardEdit v-if="forward.showEdit" v-model="forward.showEdit" ></ForwardEdit>
<ForwardCopy v-if="forward.showCopy" v-model="forward.showCopy" ></ForwardCopy>
@@ -38,33 +40,54 @@
<script>
import { injectGlobalData } from '@/provide.js'
import { reactive, onMounted, onUnmounted, computed } from 'vue'
import { ElMessage } from 'element-plus'
import Oper from './Oper.vue'
import Device from './Device.vue'
import DeviceEdit from './DeviceEdit.vue'
import { provideDevices } from './devices'
import AccessEdit from './AccessEdit.vue'
import { provideAccess } from './access'
import Tuntap from './Tuntap.vue'
import TuntapEdit from './TuntapEdit.vue'
import TuntapLease from './TuntapLease.vue'
import { provideTuntap } from './tuntap'
import Socks5 from './Socks5.vue'
import Socks5Edit from './Socks5Edit.vue'
import { provideSocks5 } from './socks5'
import Tunnel from './Tunnel.vue'
import TunnelEdit from './TunnelEdit.vue'
import Socks5 from './Socks5.vue'
import { provideTunnel } from './tunnel'
import Forward from './Forward.vue'
import ForwardEdit from './ForwardEdit.vue'
import ForwardCopy from './ForwardCopy.vue'
import { provideForward } from './forward'
import SForwardEdit from './SForwardEdit.vue'
import SForwardCopy from './SForwardCopy.vue'
import ConnectionsEdit from './ConnectionsEdit.vue'
import { ElMessage } from 'element-plus'
import { provideTuntap } from './tuntap'
import { provideTunnel } from './tunnel'
import { provideForward } from './forward'
import { provideConnections } from './connections'
import { provideSforward } from './sforward'
import { provideDevices } from './devices'
import ConnectionsEdit from './ConnectionsEdit.vue'
import { provideConnections } from './connections'
import { provideUpdater } from './updater'
import { provideAccess } from './access'
export default {
components: {Oper,Device,DeviceEdit,AccessEdit,Tunnel,TunnelEdit,ConnectionsEdit, Tuntap,TuntapEdit,TuntapLease, Socks5, Forward,ForwardEdit,ForwardCopy,SForwardEdit,SForwardCopy },
components: {Oper,
Device,DeviceEdit,
AccessEdit,
Tunnel,TunnelEdit,
ConnectionsEdit,
Tuntap,TuntapEdit,TuntapLease,
Socks5, Socks5Edit,
Forward,ForwardEdit,ForwardCopy,
SForwardEdit,SForwardCopy
},
setup(props) {
const globalData = injectGlobalData();
@@ -76,12 +99,14 @@ export default {
handleDeviceEdit,handleAccessEdit, handlePageChange, handlePageSizeChange, handleDel,clearDevicesTimeout,setSort} = provideDevices();
const {tuntap,_getTuntapInfo,handleTuntapEdit,handleTuntapRefresh,clearTuntapTimeout,getTuntapMachines,sortTuntapIP} = provideTuntap();
const {socks5,_getSocks5Info,handleSocks5Edit,handleSocks5Refresh,clearSocks5Timeout,getSocks5Machines,sortSocks5} = provideSocks5();
const {tunnel,_getTunnelInfo,handleTunnelEdit,handleTunnelRefresh,clearTunnelTimeout,sortTunnel} = provideTunnel();
const {forward,_getForwardInfo,handleForwardEdit,_testTargetForwardInfo,clearForwardTimeout,getForwardMachines} = provideForward();
const {sforward,_getSForwardInfo,handleSForwardEdit,_testLocalSForwardInfo,clearSForwardTimeout,getSForwardMachines} = provideSforward();
const {connections,
forwardConnections,_getForwardConnections,
tuntapConnections,_getTuntapConnections,
socks5Connections,_getSocks5Connections,
handleTunnelConnections,clearConnectionsTimeout
} = provideConnections();
@@ -107,6 +132,11 @@ export default {
if(ids .length > 0){
fn = setSort(ids);
}
}else if(row.prop == 'socks5'){
const ids = sortSocks5(devices.page.Request.Asc);
if(ids .length > 0){
fn = setSort(ids);
}
}
fn.then(()=>{
handlePageChange();
@@ -139,12 +169,14 @@ export default {
handlePageChange();
handleTunnelRefresh();
handleTuntapRefresh();
handleSocks5Refresh();
ElMessage.success({message:'刷新成功',grouping:true});
}
const handlePageSearch = ()=>{
handlePageChange();
handleTunnelRefresh();
handleTuntapRefresh();
handleSocks5Refresh();
ElMessage.success({message:'刷新成功',grouping:true});
}
@@ -155,9 +187,11 @@ export default {
_getSignList();
_getSignList1();
_getTuntapInfo();
_getSocks5Info();
_getTunnelInfo();
_getForwardConnections();
_getTuntapConnections();
_getSocks5Connections();
_getForwardInfo();
_getSForwardInfo();
@@ -173,6 +207,7 @@ export default {
clearDevicesTimeout();
clearConnectionsTimeout();
clearTuntapTimeout();
clearSocks5Timeout();
clearTunnelTimeout();
clearForwardTimeout();
clearSForwardTimeout();
@@ -186,6 +221,7 @@ export default {
state,devices, machineId,handleSortChange,
handleDeviceEdit,handleAccessEdit,handlePageRefresh,handlePageSearch, handlePageChange,handlePageSizeChange, handleDel,
tuntap, handleTuntapEdit, handleTuntapRefresh,
socks5, handleSocks5Edit, handleSocks5Refresh,
tunnel,connections, handleTunnelEdit, handleTunnelRefresh,handleTunnelConnections,
forward,_handleForwardEdit,
sforward,handleSForwardEdit

View File

@@ -1,39 +1,35 @@
<template>
<el-table-column prop="socks5" label="socks5代理" width="160">
<el-table-column prop="socks5" label="socks5" width="160">
<template #default="scope">
<div>
<div class="flex">
<div class="flex-1">
<a href="javascript:;" class="a-line" title="socks5代理">
<strong class="gateway">socks5://*:1805</strong>
</a>
</div>
<el-switch size="small" inline-prompt active-text="😀" inactive-text="😣"/>
</div>
<div>1111</div>
<div v-if="socks5.list[scope.row.MachineId]">
<Socks5Show :config="true" :item="scope.row" @edit="handleSocks5" @refresh="handleSocks5Refresh"></Socks5Show>
</div>
</template>
</el-table-column>
</template>
<script>
import { injectGlobalData } from '@/provide';
import { useSocks5 } from './socks5';
import Socks5Show from './Socks5Show.vue';
export default {
emits: ['edit','sedit'],
emits: ['edit','refresh'],
components:{Socks5Show},
setup(props, { emit }) {
const globalData = injectGlobalData();
return {}
const socks5 = useSocks5();
const handleSocks5 = (_socks5) => {
emit('edit',_socks5);
}
const handleSocks5Refresh = ()=>{
emit('refresh');
}
return {
socks5, handleSocks5,handleSocks5Refresh
}
}
}
</script>
<style lang="stylus" scoped>
.gateway{
background:linear-gradient(90deg, #c5b260, #858585, #c5b260, #858585);
-webkit-background-clip:text;
-webkit-text-fill-color:hsla(0,0%,100%,0);
&.green{
background:linear-gradient(90deg, #e4bb10, #008000, #e4bb10, #008000);
-webkit-background-clip:text;
-webkit-text-fill-color:hsla(0,0%,100%,0);
}
}
</style>

View File

@@ -0,0 +1,123 @@
<template>
<el-dialog v-model="state.show" :close-on-click-modal="false" append-to=".app-wrap" :title="`设置[${state.machineName}]代理`" top="1vh" width="600">
<div>
<el-form ref="ruleFormRef" :model="state.ruleForm" :rules="state.rules" label-width="140">
<el-form-item prop="gateway" style="margin-bottom:0">
配置代理通过代理访问其它设备
</el-form-item>
<el-form-item label="代理端口" prop="Port">
<el-input v-model="state.ruleForm.Port" style="width:14rem" />
</el-form-item>
<div class="upgrade-wrap">
<el-form-item label="此设备局域网IP" prop="LanIP" class="lan-item">
<template v-for="(item, index) in state.ruleForm.Lans" :key="index">
<div class="flex" style="margin-bottom:.6rem">
<div class="flex-1">
<el-input v-model="item.IP" style="width:14rem" />
<span>/</span>
<el-input @change="handleMaskChange(index)" v-model="item.PrefixLength" style="width:4rem" />
</div>
<div class="pdl-10">
<el-checkbox v-model="item.Disabled" label="禁用记录" size="large" />
</div>
<div class="pdl-10">
<el-button type="danger" @click="handleDel(index)"><el-icon><Delete /></el-icon></el-button>
<el-button type="primary" @click="handleAdd(index)"><el-icon><Plus /></el-icon></el-button>
</div>
</div>
</template>
</el-form-item>
</div>
<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 {updateSocks5 } from '@/apis/socks5';
import { injectGlobalData } from '@/provide';
import { ElMessage } from 'element-plus';
import { reactive, ref, watch } from 'vue';
import { useSocks5 } from './socks5';
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 socks5 = useSocks5();
const ruleFormRef = ref(null);
const state = reactive({
show: true,
machineName:socks5.value.current.device.MachineName,
bufferSize:globalData.value.bufferSize,
ruleForm: {
Port: socks5.value.current.Port,
Lans: socks5.value.current.Lans.slice(0)
},
rules: {}
});
if (state.ruleForm.Lans.length == 0) {
state.ruleForm.Lans.push({IP:'0.0.0.0',PrefixLength:24});
}
watch(() => state.show, (val) => {
if (!val) {
setTimeout(() => {
emit('update:modelValue', val);
}, 300);
}
});
const handleMaskChange = (index)=>{
var value = +state.ruleForm.Lans[index].PrefixLength;
if(value>32 || value<16 || isNaN(value)){
value = 24;
}
state.ruleForm.Lans[index].PrefixLength = value;
}
const handleDel = (index) => {
state.ruleForm.Lans.splice(index, 1);
if (state.ruleForm.Lans.length == 0){
handleAdd(0);
}
}
const handleAdd = (index) => {
state.ruleForm.Lans.splice(index + 1, 0, {IP:'0.0.0.0',PrefixLength:24});
}
const handleSave = () => {
const json = JSON.parse(JSON.stringify(socks5.value.current));
json.Port = +(state.ruleForm.Port || '1805');
json.Lans = state.ruleForm.Lans.map(c=>{ c.PrefixLength=+c.PrefixLength;return c; });
updateSocks5(json).then(() => {
state.show = false;
ElMessage.success('已操作!');
emit('change')
}).catch(() => {
ElMessage.error('操作失败!');
});
}
return {
state, ruleFormRef,handleMaskChange, handleDel, handleAdd, handleSave
}
}
}
</script>
<style lang="stylus" scoped>
.el-switch.is-disabled{opacity :1;}
.upgrade-wrap{
border:1px solid #ddd;
margin-bottom:2rem
padding:1rem 0 1rem 0;
}
.lan-item{
margin-bottom:0;
}
</style>

View File

@@ -0,0 +1,149 @@
<template>
<div>
<div class="flex">
<div class="flex-1">
<a href="javascript:;" class="a-line" @click="handleSocks5Port(socks5.list[item.MachineId])" title="此设备的socks5代理">
<template v-if="socks5.list[item.MachineId].SetupError">
<strong class="red" :title="socks5.list[item.MachineId].SetupError">
socks5://*:{{ socks5.list[item.MachineId].Port }}
</strong>
</template>
<template v-else>
<template v-if="socks5.list[item.MachineId].running">
<strong class="green gateway">socks5://*:{{ socks5.list[item.MachineId].Port }}</strong>
</template>
<template v-else>
<strong>socks5://*:{{ socks5.list[item.MachineId].Port }}</strong>
</template>
</template>
</a>
</div>
<template v-if="socks5.list[item.MachineId].loading">
<div>
<el-icon size="14" class="loading"><Loading /></el-icon>
</div>
</template>
<template v-else>
<el-switch v-model="socks5.list[item.MachineId].running" :loading="socks5.list[item.MachineId].loading" disabled @click="handleSocks5(socks5.list[item.MachineId])" size="small" inline-prompt active-text="😀" inactive-text="😣" >
</el-switch>
</template>
</div>
<div>
<div>
<template v-for="(item1,index) in socks5.list[item.MachineId].Lans" :key="index">
<template v-if="item1.Disabled">
<div class="flex yellow" title="已禁用">{{ item1.IP }} / {{ item1.PrefixLength }}</div>
</template>
<template v-else-if="item1.Exists">
<div class="flex red" title="与其它设备填写IP、或本机局域网IP有冲突">{{ item1.IP }} / {{ item1.PrefixLength }}</div>
</template>
<template v-else>
<div class="flex" title="正常使用" :class="{green:socks5.list[item.MachineId].running}">{{ item1.IP }} / {{ item1.PrefixLength }}</div>
</template>
</template>
</div>
</div>
</div>
</template>
<script>
import { stopSocks5, runSocks5 } from '@/apis/socks5';
import { ElMessage } from 'element-plus';
import { useSocks5 } from './socks5';
import {Loading} from '@element-plus/icons-vue'
import { injectGlobalData } from '@/provide';
import { computed } from 'vue';
export default {
props:['item','config'],
emits: ['edit','refresh'],
components:{Loading},
setup (props,{emit}) {
const socks5 = useSocks5();
const globalData = injectGlobalData();
const machineId = computed(() => globalData.value.config.Client.Id);
const hasSocks5ChangeSelf = computed(()=>globalData.value.hasAccess('Socks5ChangeSelf'));
const hasSocks5ChangeOther = computed(()=>globalData.value.hasAccess('Socks5ChangeOther'));
const hasSocks5StatusSelf = computed(()=>globalData.value.hasAccess('Socks5StatusSelf'));
const hasSocks5StatusOther = computed(()=>globalData.value.hasAccess('Socks5StatusOther'));
const handleSocks5 = (socks5) => {
if(!props.config){
return;
}
if(machineId.value === socks5.MachineId){
if(!hasSocks5StatusSelf.value){
return;
}
}else{
if(!hasSocks5StatusOther.value){
return;
}
}
const fn = socks5.running ? stopSocks5 (socks5.MachineId) : runSocks5(socks5.MachineId);
socks5.loading = true;
fn.then(() => {
ElMessage.success('操作成功!');
}).catch(() => {
ElMessage.error('操作失败!');
})
}
const handleSocks5Port = (socks5) => {
if(!props.config && machineId.value != socks5.MachineId){
return;
}
if(machineId.value === socks5.MachineId){
if(!hasSocks5ChangeSelf.value){
return;
}
}else{
if(!hasSocks5ChangeOther.value){
return;
}
}
socks5.device = props.item;
emit('edit',socks5);
}
const handleSocks5Refresh = ()=>{
emit('refresh');
}
return {
item:computed(()=>props.item),socks5, handleSocks5, handleSocks5Port,handleSocks5Refresh
}
}
}
</script>
<style lang="stylus" scoped>
@keyframes loading {
from{transform:rotate(0deg)}
to{transform:rotate(360deg)}
}
.el-icon.loading,a.loading{
vertical-align:middle;font-weight:bold;
animation:loading 1s linear infinite;
}
.el-switch.is-disabled{opacity :1;}
.el-input{
width:8rem;
}
.gateway{
background:linear-gradient(90deg, #c5b260, #858585, #c5b260, #858585);
-webkit-background-clip:text;
-webkit-text-fill-color:hsla(0,0%,100%,0);
&.green{
background:linear-gradient(90deg, #e4bb10, #008000, #e4bb10, #008000);
-webkit-background-clip:text;
-webkit-text-fill-color:hsla(0,0%,100%,0);
}
}
.switch-btn{
font-size:1.5rem;
}
</style>

View File

@@ -19,7 +19,7 @@
</template>
<script>
import { useTunnel } from './tunnel';
import { useConnections,useForwardConnections,useTuntapConnections } from './connections';
import { useConnections,useForwardConnections,useSocks5Connections,useTuntapConnections } from './connections';
import { injectGlobalData } from '@/provide';
import { computed } from 'vue';
@@ -36,11 +36,13 @@ export default {
const connections = useConnections();
const forwardConnections = useForwardConnections();
const tuntapConnections = useTuntapConnections();
const socks5Connections = useSocks5Connections();
const connectionCount = (machineId)=>{
return [
forwardConnections.value.list[machineId],
tuntapConnections.value.list[machineId],
socks5Connections.value.list[machineId],
].filter(c=>!!c && c.Connected).length;
};

View File

@@ -1,4 +1,4 @@
import { getAccesss } from "@/apis/config";
import { getAccesss } from "@/apis/access";
import { inject, provide, ref } from "vue";
const accessSymbol = Symbol();

View File

@@ -1,10 +1,12 @@
import { getForwardConnections, removeForwardConnection } from "@/apis/forward";
import { getTuntapConnections, removeTuntapConnection } from "@/apis/tuntap";
import { getSocks5Connections, removeSocks5Connection } from "@/apis/socks5";
import { inject, provide, ref } from "vue";
const connectionsSymbol = Symbol();
const forwardConnectionsSymbol = Symbol();
const tuntapConnectionsSymbol = Symbol();
const socks5ConnectionsSymbol = Symbol();
export const provideConnections = () => {
const connections = ref({
showEdit: false,
@@ -28,8 +30,6 @@ export const provideConnections = () => {
list: {},
});
provide(forwardConnectionsSymbol, forwardConnections);
const _getForwardConnections = () => {
getForwardConnections(connections.value.hashcode.toString()).then((res) => {
if (connections.value._updateRealTime == false)
@@ -44,6 +44,8 @@ export const provideConnections = () => {
forwardConnections.value.timer = setTimeout(_getForwardConnections, 1000);
})
}
const tuntapConnections = ref({
timer: 0,
list: {},
@@ -63,6 +65,28 @@ export const provideConnections = () => {
tuntapConnections.value.timer = setTimeout(_getTuntapConnections, 1000);
})
}
const socks5Connections = ref({
timer: 0,
list: {},
});
provide(socks5ConnectionsSymbol, socks5Connections);
const _getSocks5Connections = () => {
getSocks5Connections(connections.value.hashcode1.toString()).then((res) => {
if (connections.value._updateRealTime == false)
connections.value.hashcode1 = res.HashCode;
if (res.List) {
parseConnections(res.List, removeSocks5Connection);
socks5Connections.value.list = res.List;
}
socks5Connections.value.timer = setTimeout(_getSocks5Connections, 1000);
}).catch((e) => {
socks5Connections.value.timer = setTimeout(_getSocks5Connections, 1000);
})
}
const parseConnections = (_connections, removeFunc) => {
const caches = connections.value.speedCache;
for (let machineId in _connections) {
@@ -93,14 +117,18 @@ export const provideConnections = () => {
connections.value.currentName = device.MachineName;
connections.value.showEdit = true;
}
const clearConnectionsTimeout = () => {
clearTimeout(forwardConnections.value.timer);
clearTimeout(tuntapConnections.value.timer);
clearTimeout(socks5Connections.value.timer);
}
return {
connections,
forwardConnections, _getForwardConnections,
tuntapConnections, _getTuntapConnections,
socks5Connections, _getSocks5Connections,
handleTunnelConnections, clearConnectionsTimeout
}
}
@@ -113,3 +141,6 @@ export const useForwardConnections = () => {
export const useTuntapConnections = () => {
return inject(tuntapConnectionsSymbol);
}
export const useSocks5Connections = () => {
return inject(socks5ConnectionsSymbol);
}

View File

@@ -0,0 +1,63 @@
import { inject, provide, ref } from "vue"
import { getSocks5Info, refreshSocks5 } from "@/apis/socks5";
const socks5Symbol = Symbol();
export const provideSocks5 = () => {
const socks5 = ref({
timer: 0,
showEdit: false,
current: null,
list: {},
hashcode: 0,
});
provide(socks5Symbol, socks5);
const _getSocks5Info = () => {
clearTimeout(socks5.value.timer);
getSocks5Info(socks5.value.hashcode.toString()).then((res) => {
socks5.value.hashcode = res.HashCode;
if (res.List) {
for (let j in res.List) {
Object.assign(res.List[j], {
running: res.List[j].Status == 2,
loading: res.List[j].Status == 1
});
}
socks5.value.list = res.List;
}
socks5.value.timer = setTimeout(_getSocks5Info, 1100);
}).catch((e) => {
socks5.value.timer = setTimeout(_getSocks5Info, 1100);
});
}
const handleSocks5Edit = (_socks5) => {
socks5.value.current = _socks5;
socks5.value.showEdit = true;
}
const handleSocks5Refresh = () => {
refreshSocks5();
}
const clearSocks5Timeout = () => {
clearTimeout(socks5.value.timer);
socks5.value.timer = 0;
}
const getSocks5Machines = (name) => {
return Object.values(socks5.value.list)
.filter(c => c.IP.indexOf(name) >= 0 || (c.Lans.filter(d => d.IP.indexOf(name) >= 0).length > 0))
.map(c => c.MachineId);
}
const sortSocks5 = (asc) => {
const sort = Object.values(socks5.value.list).sort((a, b) => a.Port - b.Port);
return sort.map(c => c.MachineId);
}
return {
socks5, _getSocks5Info, handleSocks5Edit, handleSocks5Refresh, clearSocks5Timeout, getSocks5Machines, sortSocks5
}
}
export const useSocks5 = () => {
return inject(socks5Symbol);
}

View File

@@ -1,11 +1,8 @@
import { injectGlobalData } from "@/provide";
import { ElMessage } from "element-plus";
import { inject, provide, ref } from "vue"
import { getTuntapInfo, refreshTuntap, subscribePing } from "@/apis/tuntap";
const tuntapSymbol = Symbol();
export const provideTuntap = () => {
const globalData = injectGlobalData();
const tuntap = ref({
timer: 0,
showEdit: false,

View File

@@ -122,7 +122,7 @@ namespace linker
TimerHelper.SetInterval(() =>
{
string[] files = Directory.GetFiles("logs").OrderBy(c => c).ToArray();
for (int i = 0; i < files.Length - 7; i++)
for (int i = 0; i < files.Length - 180; i++)
{
try
{

View File

@@ -23,7 +23,10 @@
<Company>snltty</Company>
<Description>1. 优化点对网和网对网的局域网IP包括禁用IP和冲突检测
2. 一些UI优化
3. 测试中测试中测试中不要更新用v1.4.9</Description>
3. 新增socks5代理
4. 优化端口转发和内网穿透配置
5. 优化网卡的一些东西
6. 测试中测试中测试中不要更新用v1.4.9</Description>
<Copyright>snltty</Copyright>
<PackageProjectUrl>https://github.com/snltty/linker</PackageProjectUrl>
<RepositoryUrl>https://github.com/snltty/linker</RepositoryUrl>

View File

@@ -0,0 +1,69 @@
using linker.config;
using linker.libs.api;
using linker.libs.extends;
using linker.plugins.capi;
using linker.libs;
using linker.plugins.client;
using linker.plugins.messenger;
using MemoryPack;
using linker.plugins.access.messenger;
namespace linker.plugins.access
{
public sealed class AccessApiController : IApiClientController
{
private readonly FileConfig config;
private readonly IMessengerSender sender;
private readonly ClientSignInState clientSignInState;
private readonly AccessTransfer accessTransfer;
public AccessApiController(FileConfig config, IMessengerSender sender, ClientSignInState clientSignInState, AccessTransfer accessTransfer)
{
this.config = config;
this.sender = sender;
this.clientSignInState = clientSignInState;
this.accessTransfer = accessTransfer;
}
public AccessListInfo GetAccesss(ApiControllerParamsInfo param)
{
ulong hashCode = ulong.Parse(param.Content);
if (accessTransfer.Version.Eq(hashCode, out ulong version) == false)
{
return new AccessListInfo
{
HashCode = version,
List = accessTransfer.GetAccesss()
};
}
return new AccessListInfo { HashCode = version };
}
[ClientApiAccessAttribute(ClientApiAccess.Access)]
public async Task<bool> SetAccess(ApiControllerParamsInfo param)
{
ConfigUpdateAccessInfo configUpdateAccessInfo = param.Content.DeJson<ConfigUpdateAccessInfo>();
if (configUpdateAccessInfo.ToMachineId == config.Data.Client.Id)
{
return false;
}
configUpdateAccessInfo.FromMachineId = config.Data.Client.Id;
MessageResponeInfo resp = await sender.SendReply(new MessageRequestWrap
{
Connection = clientSignInState.Connection,
MessengerId = (ushort)AccessMessengerIds.AccessUpdateForward,
Payload = MemoryPackSerializer.Serialize(configUpdateAccessInfo)
});
return resp.Code == MessageResponeCodes.OK && resp.Data.Span.SequenceEqual(Helper.TrueArray);
}
}
public sealed class AccessListInfo
{
public Dictionary<string, ClientApiAccess> List { get; set; }
public ulong HashCode { get; set; }
}
}

View File

@@ -0,0 +1,41 @@
using linker.config;
using linker.plugins.access.messenger;
using linker.startup;
using Microsoft.Extensions.DependencyInjection;
namespace linker.plugins.access
{
public sealed class AccessStartup : IStartup
{
public string Name => "access";
public bool Required => true;
public StartupLevel Level => StartupLevel.Top;
public string[] Dependent => new string[] { "messenger", "signin", "serialize" };
public StartupLoadType LoadType => StartupLoadType.Normal;
public void AddClient(ServiceCollection serviceCollection, FileConfig config)
{
serviceCollection.AddSingleton<AccessApiController>();
serviceCollection.AddSingleton<AccessClientMessenger>();
serviceCollection.AddSingleton<AccessTransfer>();
}
public void AddServer(ServiceCollection serviceCollection, FileConfig config)
{
serviceCollection.AddSingleton<AccessServerMessenger>();
}
public void UseClient(ServiceProvider serviceProvider, FileConfig config)
{
}
public void UseServer(ServiceProvider serviceProvider, FileConfig config)
{
}
}
}

View File

@@ -1,15 +1,19 @@
using linker.config;
using linker.libs;
using linker.plugins.client;
using linker.plugins.config.messenger;
using linker.plugins.decenter;
using linker.plugins.messenger;
using MemoryPack;
namespace linker.plugins.config
namespace linker.plugins.access
{
public sealed class AccessTransfer
public sealed class AccessTransfer : IDecenter
{
public string Name => "access";
public VersionManager DataVersion { get; } = new VersionManager();
public VersionManager Version { get; } = new VersionManager();
private Dictionary<string, ClientApiAccess> accesss = new Dictionary<string, ClientApiAccess>();
private readonly FileConfig fileConfig;
@@ -21,32 +25,43 @@ namespace linker.plugins.config
this.sender = sender;
this.clientSignInState = clientSignInState;
clientSignInState.NetworkEnabledHandle += (times) => Sync();
clientSignInState.NetworkEnabledHandle += (times) => DataVersion.Add();
}
public Memory<byte> GetData()
{
ConfigAccessInfo info = new ConfigAccessInfo { MachineId = fileConfig.Data.Client.Id, Access = fileConfig.Data.Client.Access };
accesss[info.MachineId] = info.Access;
Version.Add();
return MemoryPackSerializer.Serialize(info);
}
public void SetData(Memory<byte> data)
{
ConfigAccessInfo access = MemoryPackSerializer.Deserialize<ConfigAccessInfo>(data.Span);
accesss[access.MachineId] = access.Access;
Version.Add();
}
public void SetData(List<ReadOnlyMemory<byte>> data)
{
List<ConfigAccessInfo> list = data.Select(c => MemoryPackSerializer.Deserialize<ConfigAccessInfo>(c.Span)).ToList();
accesss = list.ToDictionary(c => c.MachineId, d => d.Access);
accesss[fileConfig.Data.Client.Id] = fileConfig.Data.Client.Access;
Version.Add();
}
public Dictionary<string, ClientApiAccess> GetAccesss()
{
return accesss;
}
public ConfigAccessInfo GetAccess()
{
return new ConfigAccessInfo { MachineId = fileConfig.Data.Client.Id, Access = fileConfig.Data.Client.Access }; ;
}
public void SetAccess(ConfigAccessInfo access)
{
accesss[access.MachineId] = access.Access;
Version.Add();
}
public void SetAccess(ConfigUpdateAccessInfo info)
{
//我的权限删掉它的权限==0说明它至少拥有我的全部权限我是它的子集它有权管我
if (accesss.TryGetValue(info.FromMachineId, out ClientApiAccess access) && ((~access) & fileConfig.Data.Client.Access) == 0)
if (accesss.TryGetValue(info.FromMachineId, out ClientApiAccess access) && (~access & fileConfig.Data.Client.Access) == 0)
{
fileConfig.Data.Client.Access = (ClientApiAccess)info.Access;
fileConfig.Data.Update();
Version.Add();
Sync();
GetData();
DataVersion.Add();
}
}
public ClientApiAccess AssignAccess(ClientApiAccess access)
@@ -54,25 +69,5 @@ namespace linker.plugins.config
return fileConfig.Data.Client.Access & access;
}
private void Sync()
{
ConfigAccessInfo access = GetAccess();
sender.SendReply(new MessageRequestWrap
{
Connection = clientSignInState.Connection,
MessengerId = (ushort)ConfigMessengerIds.AccessForward,
Timeout = 10000,
Payload = MemoryPackSerializer.Serialize(access)
}).ContinueWith((result) =>
{
if (result.Result.Code == MessageResponeCodes.OK)
{
List<ConfigAccessInfo> list = MemoryPackSerializer.Deserialize<List<ConfigAccessInfo>>(result.Result.Data.Span);
accesss = list.ToDictionary(c => c.MachineId, d => d.Access);
accesss[access.MachineId] = access.Access;
Version.Add();
}
});
}
}
}

View File

@@ -0,0 +1,215 @@
using MemoryPack;
using System.Reflection;
namespace linker.config
{
public sealed partial class ConfigClientInfo
{
private Dictionary<string, ClientApiAccessInfo> accesss;
public Dictionary<string, ClientApiAccessInfo> Accesss
{
get
{
if (accesss == null)
{
accesss = new Dictionary<string, ClientApiAccessInfo>();
Type enumType = typeof(ClientApiAccess);
// 获取所有字段值
foreach (var value in Enum.GetValues(enumType))
{
// 获取字段信息
FieldInfo fieldInfo = enumType.GetField(value.ToString());
var attribute = fieldInfo.GetCustomAttribute<ClientAccessDisplayAttribute>(false);
if (attribute != null)
{
accesss.TryAdd(fieldInfo.Name, new ClientApiAccessInfo { Text = attribute.Value, Value = (ulong)value });
}
}
}
return accesss;
}
}
/// <summary>
/// 管理权限
/// </summary>
public ClientApiAccess Access { get; set; } = ClientApiAccess.Full;
/// <summary>
/// 是否拥有某项权限
/// </summary>
/// <param name="clientManagerAccess"></param>
/// <returns></returns>
public bool HasAccess(ClientApiAccess clientManagerAccess)
{
return (Access & clientManagerAccess) == clientManagerAccess;
}
}
[AttributeUsage(AttributeTargets.Method, Inherited = false, AllowMultiple = false)]
public sealed class ClientApiAccessAttribute : Attribute
{
public ClientApiAccess Value { get; set; }
public ClientApiAccessAttribute(ClientApiAccess value)
{
Value = value;
}
}
[AttributeUsage(AttributeTargets.Field, Inherited = false, AllowMultiple = false)]
public sealed class ClientAccessDisplayAttribute : Attribute
{
public string Value { get; set; }
public ClientAccessDisplayAttribute(string value)
{
Value = value;
}
}
[Flags]
public enum ClientApiAccess : ulong
{
None = 0,
[ClientAccessDisplayAttribute("简单管理")]
NetManager = 1,
[ClientAccessDisplayAttribute("专业管理")]
FullManager = 1 << 1,
[ClientAccessDisplayAttribute("服务器配置")]
Config = 1 << 2,
[ClientAccessDisplayAttribute("日志列表")]
LoggerShow = 1 << 3,
[ClientAccessDisplayAttribute("日志配置")]
LoggerLevel = 1 << 4,
[ClientAccessDisplayAttribute("修改本机设备名")]
RenameSelf = 1 << 5,
[ClientAccessDisplayAttribute("修改所有设备名")]
RenameOther = 1 << 6,
[ClientAccessDisplayAttribute("显示公网信息")]
ExternalShow = 1 << 7,
[ClientAccessDisplayAttribute("升级本机")]
UpdateSelf = 1 << 8,
[ClientAccessDisplayAttribute("升级所有设备")]
UpdateOther = 1 << 9,
[ClientAccessDisplayAttribute("升级服务器")]
UpdateServer = 1 << 10,
[ClientAccessDisplayAttribute("开关本机网卡")]
TuntapStatusSelf = 1 << 11,
[ClientAccessDisplayAttribute("开关所有网卡")]
TuntapStatusOther = 1 << 12,
[ClientAccessDisplayAttribute("修改本机网卡")]
TuntapChangeSelf = 1 << 13,
[ClientAccessDisplayAttribute("修改所有网卡")]
TuntapChangeOther = 1 << 14,
[ClientAccessDisplayAttribute("显示本机端口转发")]
ForwardShowSelf = 1 << 15,
[ClientAccessDisplayAttribute("显示所有设备端口转发")]
ForwardShowOther = 1 << 16,
[ClientAccessDisplayAttribute("配置本机端口转发")]
ForwardSelf = 1 << 17,
[ClientAccessDisplayAttribute("配置所有设备端口转发")]
ForwardOther = 1 << 18,
[ClientAccessDisplayAttribute("重启其它设备")]
Reboot = 1 << 19,
[ClientAccessDisplayAttribute("删除其它设备")]
Remove = 1 << 20,
[ClientAccessDisplayAttribute("修改本机网关")]
TunnelChangeSelf = 1 << 21,
[ClientAccessDisplayAttribute("修改所有设备网关")]
TunnelChangeOther = 1 << 22,
[ClientAccessDisplayAttribute("删除隧道连接")]
TunnelRemove = 1 << 23,
[ClientAccessDisplayAttribute("开启管理API")]
Api = 1 << 24,
[ClientAccessDisplayAttribute("开启管理网页")]
Web = 1 << 25,
[ClientAccessDisplayAttribute("导出配置")]
Export = 1 << 26,
[ClientAccessDisplayAttribute("修改权限")]
Access = 1 << 27,
[ClientAccessDisplayAttribute("修改打洞协议")]
Transport = 1 << 28,
[ClientAccessDisplayAttribute("修改验证参数")]
Action = 1 << 29,
[ClientAccessDisplayAttribute("查看内网穿透流量")]
SForwardFlow = 1 << 30,
[ClientAccessDisplayAttribute("查看中继流量")]
RelayFlow = ((ulong)1 << 31),
[ClientAccessDisplayAttribute("查看信标流量")]
SigninFlow = ((ulong)1 << 32),
[ClientAccessDisplayAttribute("查看流量")]
Flow = ((ulong)1 << 33),
[ClientAccessDisplayAttribute("同步配置")]
Sync = ((ulong)1 << 34),
[ClientAccessDisplayAttribute("配置组网网络")]
Lease = ((ulong)1 << 35),
[ClientAccessDisplayAttribute("开关本机socks5")]
Socks5StatusSelf = 1 << 36,
[ClientAccessDisplayAttribute("开关所有socks5")]
Socks5StatusOther = 1 << 37,
[ClientAccessDisplayAttribute("修改本机socks5")]
Socks5ChangeSelf = 1 << 38,
[ClientAccessDisplayAttribute("修改所有socks5")]
Socks5ChangeOther = 1 << 39,
Full = ulong.MaxValue >> (64 - 52),
}
public sealed class ClientApiAccessInfo
{
public ulong Value { get; set; }
public string Text { get; set; }
}
[MemoryPackable]
public sealed partial class ConfigUpdateAccessInfo
{
/// <summary>
/// 设备
/// </summary>
public string FromMachineId { get; set; }
/// <summary>
/// 设备
/// </summary>
public string ToMachineId { get; set; }
/// <summary>
/// 权限
/// </summary>
public ulong Access { get; set; }
}
[MemoryPackable]
public sealed partial class ConfigAccessInfo
{
/// <summary>
/// 设备
/// </summary>
public string MachineId { get; set; }
/// <summary>
/// 权限
/// </summary>
public ClientApiAccess Access { get; set; }
}
}

View File

@@ -0,0 +1,70 @@
using linker.config;
using linker.libs;
using linker.plugins.client;
using linker.plugins.messenger;
using linker.plugins.signin.messenger;
using MemoryPack;
namespace linker.plugins.access.messenger
{
public sealed class AccessServerMessenger : IMessenger
{
private readonly IMessengerSender sender;
private readonly SignCaching signCaching;
public AccessServerMessenger(IMessengerSender sender, SignCaching signCaching)
{
this.sender = sender;
this.signCaching = signCaching;
}
[MessengerId((ushort)AccessMessengerIds.AccessUpdateForward)]
public void AccessUpdateForward(IConnection connection)
{
ConfigUpdateAccessInfo info = MemoryPackSerializer.Deserialize<ConfigUpdateAccessInfo>(connection.ReceiveRequestWrap.Payload.Span);
info.FromMachineId = connection.Id;
if (signCaching.TryGet(connection.Id, out SignCacheInfo cache) && signCaching.TryGet(info.ToMachineId, out SignCacheInfo cache1) && cache1.GroupId == cache.GroupId)
{
uint requiestid = connection.ReceiveRequestWrap.RequestId;
sender.SendReply(new MessageRequestWrap
{
Connection = cache1.Connection,
MessengerId = (ushort)AccessMessengerIds.AccessUpdate,
Payload = MemoryPackSerializer.Serialize(info),
Timeout = 3000,
}).ContinueWith(async (result) =>
{
await sender.ReplyOnly(new MessageResponseWrap
{
RequestId = requiestid,
Connection = connection,
Payload = result.Result.Data
}, (ushort)AccessMessengerIds.AccessUpdateForward).ConfigureAwait(false);
});
}
}
}
public sealed class AccessClientMessenger : IMessenger
{
private readonly AccessTransfer accessTransfer;
private readonly FileConfig fileConfig;
private readonly ClientSignInTransfer clientSignInTransfer;
public AccessClientMessenger(AccessTransfer accessTransfer, FileConfig fileConfig, ClientSignInTransfer clientSignInTransfer)
{
this.accessTransfer = accessTransfer;
this.fileConfig = fileConfig;
this.clientSignInTransfer = clientSignInTransfer;
}
[MessengerId((ushort)AccessMessengerIds.AccessUpdate)]
public void AccessUpdate(IConnection connection)
{
ConfigUpdateAccessInfo info = MemoryPackSerializer.Deserialize<ConfigUpdateAccessInfo>(connection.ReceiveRequestWrap.Payload.Span);
accessTransfer.SetAccess(info);
connection.Write(Helper.TrueArray);
}
}
}

View File

@@ -0,0 +1,15 @@
namespace linker.plugins.access.messenger
{
public enum AccessMessengerIds : ushort
{
Min = 2600,
Access = 2603,
AccessForward = 2604,
AccessUpdate = 2605,
AccessUpdateForward = 2606,
Max = 2699
}
}

View File

@@ -1,7 +1,5 @@
using linker.config;
using linker.libs;
using linker.libs;
using linker.libs.extends;
using LiteDB;
using MemoryPack;
using System.Net;
using System.Reflection;
@@ -24,45 +22,6 @@ namespace linker.config
accesss?.Clear();
}
private Dictionary<string, ClientApiAccessInfo> accesss;
public Dictionary<string, ClientApiAccessInfo> Accesss
{
get
{
if (accesss == null)
{
accesss = new Dictionary<string, ClientApiAccessInfo>();
Type enumType = typeof(ClientApiAccess);
// 获取所有字段值
foreach (var value in Enum.GetValues(enumType))
{
// 获取字段信息
FieldInfo fieldInfo = enumType.GetField(value.ToString());
var attribute = fieldInfo.GetCustomAttribute<ClientAccessDisplayAttribute>(false);
if (attribute != null)
{
accesss.TryAdd(fieldInfo.Name, new ClientApiAccessInfo { Text = attribute.Value, Value = (ulong)value });
}
}
}
return accesss;
}
}
/// <summary>
/// 管理权限
/// </summary>
public ClientApiAccess Access { get; set; } = ClientApiAccess.Full;
/// <summary>
/// 是否拥有某项权限
/// </summary>
/// <param name="clientManagerAccess"></param>
/// <returns></returns>
public bool HasAccess(ClientApiAccess clientManagerAccess)
{
return (Access & clientManagerAccess) == clientManagerAccess;
}
public bool OnlyNode { get; set; }
@@ -138,10 +97,6 @@ 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]
@@ -194,162 +149,4 @@ namespace linker.config
}
[AttributeUsage(AttributeTargets.Method, Inherited = false, AllowMultiple = false)]
public sealed class ClientApiAccessAttribute : Attribute
{
public ClientApiAccess Value { get; set; }
public ClientApiAccessAttribute(ClientApiAccess value)
{
Value = value;
}
}
[AttributeUsage(AttributeTargets.Field, Inherited = false, AllowMultiple = false)]
public sealed class ClientAccessDisplayAttribute : Attribute
{
public string Value { get; set; }
public ClientAccessDisplayAttribute(string value)
{
Value = value;
}
}
[Flags]
public enum ClientApiAccess : ulong
{
None = 0,
[ClientAccessDisplayAttribute("简单管理")]
NetManager = 1,
[ClientAccessDisplayAttribute("专业管理")]
FullManager = 1 << 1,
[ClientAccessDisplayAttribute("服务器配置")]
Config = 1 << 2,
[ClientAccessDisplayAttribute("日志列表")]
LoggerShow = 1 << 3,
[ClientAccessDisplayAttribute("日志配置")]
LoggerLevel = 1 << 4,
[ClientAccessDisplayAttribute("修改本机设备名")]
RenameSelf = 1 << 5,
[ClientAccessDisplayAttribute("修改所有设备名")]
RenameOther = 1 << 6,
[ClientAccessDisplayAttribute("显示公网信息")]
ExternalShow = 1 << 7,
[ClientAccessDisplayAttribute("升级本机")]
UpdateSelf = 1 << 8,
[ClientAccessDisplayAttribute("升级所有设备")]
UpdateOther = 1 << 9,
[ClientAccessDisplayAttribute("升级服务器")]
UpdateServer = 1 << 10,
[ClientAccessDisplayAttribute("开关本机网卡")]
TuntapStatusSelf = 1 << 11,
[ClientAccessDisplayAttribute("开关所有网卡")]
TuntapStatusOther = 1 << 12,
[ClientAccessDisplayAttribute("修改本机网卡")]
TuntapChangeSelf = 1 << 13,
[ClientAccessDisplayAttribute("修改所有网卡")]
TuntapChangeOther = 1 << 14,
[ClientAccessDisplayAttribute("显示本机端口转发")]
ForwardShowSelf = 1 << 15,
[ClientAccessDisplayAttribute("显示所有设备端口转发")]
ForwardShowOther = 1 << 16,
[ClientAccessDisplayAttribute("配置本机端口转发")]
ForwardSelf = 1 << 17,
[ClientAccessDisplayAttribute("配置所有设备端口转发")]
ForwardOther = 1 << 18,
[ClientAccessDisplayAttribute("重启其它设备")]
Reboot = 1 << 19,
[ClientAccessDisplayAttribute("删除其它设备")]
Remove = 1 << 20,
[ClientAccessDisplayAttribute("修改本机网关")]
TunnelChangeSelf = 1 << 21,
[ClientAccessDisplayAttribute("修改所有设备网关")]
TunnelChangeOther = 1 << 22,
[ClientAccessDisplayAttribute("删除隧道连接")]
TunnelRemove = 1 << 23,
[ClientAccessDisplayAttribute("开启管理API")]
Api = 1 << 24,
[ClientAccessDisplayAttribute("开启管理网页")]
Web = 1 << 25,
[ClientAccessDisplayAttribute("导出配置")]
Export = 1 << 26,
[ClientAccessDisplayAttribute("修改权限")]
Access = 1 << 27,
[ClientAccessDisplayAttribute("修改打洞协议")]
Transport = 1 << 28,
[ClientAccessDisplayAttribute("修改验证参数")]
Action = 1 << 29,
[ClientAccessDisplayAttribute("查看内网穿透流量")]
SForwardFlow = 1 << 30,
[ClientAccessDisplayAttribute("查看中继流量")]
RelayFlow = ((ulong)1 << 31),
[ClientAccessDisplayAttribute("查看信标流量")]
SigninFlow = ((ulong)1 << 32),
[ClientAccessDisplayAttribute("查看流量")]
Flow = ((ulong)1 << 33),
[ClientAccessDisplayAttribute("同步配置")]
Sync = ((ulong)1 << 34),
[ClientAccessDisplayAttribute("配置组网网络")]
Lease = ((ulong)1 << 35),
Full = ulong.MaxValue >> (64 - 52),
}
public sealed class ClientApiAccessInfo
{
public ulong Value { get; set; }
public string Text { get; set; }
}
[MemoryPackable]
public sealed partial class ConfigUpdateAccessInfo
{
/// <summary>
/// 设备
/// </summary>
public string FromMachineId { get; set; }
/// <summary>
/// 设备
/// </summary>
public string ToMachineId { get; set; }
/// <summary>
/// 权限
/// </summary>
public ulong Access { get; set; }
}
[MemoryPackable]
public sealed partial class ConfigAccessInfo
{
/// <summary>
/// 设备
/// </summary>
public string MachineId { get; set; }
/// <summary>
/// 权限
/// </summary>
public ClientApiAccess Access { get; set; }
}
}

View File

@@ -7,9 +7,7 @@ using System.IO.Compression;
using linker.libs;
using linker.plugins.client;
using linker.plugins.messenger;
using linker.plugins.config.messenger;
using MemoryPack;
using linker.plugins.access;
namespace linker.plugins.config
{
public sealed class ConfigClientApiController : IApiClientController
@@ -19,18 +17,18 @@ namespace linker.plugins.config
private readonly ClientSignInTransfer clientSignInTransfer;
private readonly IMessengerSender sender;
private readonly ClientSignInState clientSignInState;
private readonly AccessTransfer accessTransfer;
private readonly ConfigSyncTreansfer configSyncTreansfer;
private readonly AccessTransfer accessTransfer;
public ConfigClientApiController(RunningConfig runningConfig, FileConfig config, ClientSignInTransfer clientSignInTransfer, IMessengerSender sender, ClientSignInState clientSignInState, AccessTransfer accessTransfer, ConfigSyncTreansfer configSyncTreansfer)
public ConfigClientApiController(RunningConfig runningConfig, FileConfig config, ClientSignInTransfer clientSignInTransfer, IMessengerSender sender, ClientSignInState clientSignInState, ConfigSyncTreansfer configSyncTreansfer, AccessTransfer accessTransfer)
{
this.runningConfig = runningConfig;
this.config = config;
this.clientSignInTransfer = clientSignInTransfer;
this.sender = sender;
this.clientSignInState = clientSignInState;
this.accessTransfer = accessTransfer;
this.configSyncTreansfer = configSyncTreansfer;
this.accessTransfer = accessTransfer;
}
public object Get(ApiControllerParamsInfo param)
@@ -87,40 +85,6 @@ namespace linker.plugins.config
}
public AccessListInfo GetAccesss(ApiControllerParamsInfo param)
{
ulong hashCode = ulong.Parse(param.Content);
if (accessTransfer.Version.Eq(hashCode, out ulong version) == false)
{
return new AccessListInfo
{
HashCode = version,
List = accessTransfer.GetAccesss()
};
}
return new AccessListInfo { HashCode = version };
}
[ClientApiAccessAttribute(ClientApiAccess.Access)]
public async Task<bool> SetAccess(ApiControllerParamsInfo param)
{
ConfigUpdateAccessInfo configUpdateAccessInfo = param.Content.DeJson<ConfigUpdateAccessInfo>();
if (configUpdateAccessInfo.ToMachineId == config.Data.Client.Id)
{
return false;
}
configUpdateAccessInfo.FromMachineId = config.Data.Client.Id;
MessageResponeInfo resp = await sender.SendReply(new MessageRequestWrap
{
Connection = clientSignInState.Connection,
MessengerId = (ushort)ConfigMessengerIds.AccessUpdateForward,
Payload = MemoryPackSerializer.Serialize(configUpdateAccessInfo)
});
return resp.Code == MessageResponeCodes.OK && resp.Data.Span.SequenceEqual(Helper.TrueArray);
}
[ClientApiAccessAttribute(ClientApiAccess.Export)]
public async Task<bool> Export(ApiControllerParamsInfo param)
{
@@ -148,15 +112,18 @@ namespace linker.plugins.config
Directory.CreateDirectory(configPath);
ConfigClientInfo client = config.Data.Client.ToJson().DeJson<ConfigClientInfo>();
client.Id = string.Empty;
client.Name = string.Empty;
if (configExportInfo.Single || client.OnlyNode)
{
client.Id = await clientSignInTransfer.GetNewId();
client.Name = configExportInfo.Name;
}
if (client.OnlyNode == false)
{
client.CApi.ApiPassword = configExportInfo.ApiPassword;
}
client.Name = configExportInfo.Name;
client.Access = accessTransfer.AssignAccess((ClientApiAccess)configExportInfo.Access);
client.OnlyNode = true;
client.Action.Args.Clear();

View File

@@ -26,9 +26,6 @@ namespace linker.plugins.config
serviceCollection.AddSingleton<RunningConfig>();
serviceCollection.AddSingleton<AccessTransfer>();
serviceCollection.AddSingleton<ConfigSyncTreansfer>();
serviceCollection.AddSingleton<ConfigSyncTypesLoader>();

View File

@@ -1,7 +1,4 @@
using linker.config;
using linker.libs;
using linker.plugins.client;
using linker.plugins.messenger;
using linker.plugins.messenger;
using linker.plugins.signin.messenger;
using MemoryPack;
@@ -18,69 +15,6 @@ namespace linker.plugins.config.messenger
this.signCaching = signCaching;
}
[MessengerId((ushort)ConfigMessengerIds.AccessForward)]
public void AccessForward(IConnection connection)
{
ConfigAccessInfo info = MemoryPackSerializer.Deserialize<ConfigAccessInfo>(connection.ReceiveRequestWrap.Payload.Span);
if (signCaching.TryGet(connection.Id, out SignCacheInfo cache))
{
uint requiestid = connection.ReceiveRequestWrap.RequestId;
List<SignCacheInfo> caches = signCaching.Get(cache.GroupId);
List<Task<MessageResponeInfo>> tasks = new List<Task<MessageResponeInfo>>();
foreach (SignCacheInfo item in caches.Where(c => c.MachineId != connection.Id && c.Connected))
{
tasks.Add(sender.SendReply(new MessageRequestWrap
{
Connection = item.Connection,
MessengerId = (ushort)ConfigMessengerIds.Access,
Payload = connection.ReceiveRequestWrap.Payload,
Timeout = 1000,
}));
}
Task.WhenAll(tasks).ContinueWith(async (result) =>
{
List<ConfigAccessInfo> results = tasks.Where(c => c.Result.Code == MessageResponeCodes.OK)
.Select(c => MemoryPackSerializer.Deserialize<ConfigAccessInfo>(c.Result.Data.Span)).ToList();
await sender.ReplyOnly(new MessageResponseWrap
{
RequestId = requiestid,
Connection = connection,
Payload = MemoryPackSerializer.Serialize(results)
},(ushort)ConfigMessengerIds.AccessForward).ConfigureAwait(false);
});
}
}
[MessengerId((ushort)ConfigMessengerIds.AccessUpdateForward)]
public void AccessUpdateForward(IConnection connection)
{
ConfigUpdateAccessInfo info = MemoryPackSerializer.Deserialize<ConfigUpdateAccessInfo>(connection.ReceiveRequestWrap.Payload.Span);
info.FromMachineId = connection.Id;
if (signCaching.TryGet(connection.Id, out SignCacheInfo cache) && signCaching.TryGet(info.ToMachineId, out SignCacheInfo cache1) && cache1.GroupId == cache.GroupId)
{
uint requiestid = connection.ReceiveRequestWrap.RequestId;
sender.SendReply(new MessageRequestWrap
{
Connection = cache1.Connection,
MessengerId = (ushort)ConfigMessengerIds.AccessUpdate,
Payload = MemoryPackSerializer.Serialize(info),
Timeout = 3000,
}).ContinueWith(async (result) =>
{
await sender.ReplyOnly(new MessageResponseWrap
{
RequestId = requiestid,
Connection = connection,
Payload = result.Result.Data
}, (ushort)ConfigMessengerIds.AccessUpdateForward).ConfigureAwait(false);
});
}
}
[MessengerId((ushort)ConfigMessengerIds.SyncForward)]
public async Task SyncForward(IConnection connection)
@@ -108,35 +42,13 @@ namespace linker.plugins.config.messenger
public sealed class ConfigClientMessenger : IMessenger
{
private readonly AccessTransfer accessTransfer;
private readonly FileConfig fileConfig;
private readonly ClientSignInTransfer clientSignInTransfer;
private readonly ConfigSyncTreansfer syncTreansfer;
public ConfigClientMessenger(AccessTransfer accessTransfer, FileConfig fileConfig, ClientSignInTransfer clientSignInTransfer, ConfigSyncTreansfer syncTreansfer)
public ConfigClientMessenger(ConfigSyncTreansfer syncTreansfer)
{
this.accessTransfer = accessTransfer;
this.fileConfig = fileConfig;
this.clientSignInTransfer = clientSignInTransfer;
this.syncTreansfer = syncTreansfer;
}
[MessengerId((ushort)ConfigMessengerIds.Access)]
public void Access(IConnection connection)
{
ConfigAccessInfo info = MemoryPackSerializer.Deserialize<ConfigAccessInfo>(connection.ReceiveRequestWrap.Payload.Span);
accessTransfer.SetAccess(info);
connection.Write(MemoryPackSerializer.Serialize((accessTransfer.GetAccess())));
}
[MessengerId((ushort)ConfigMessengerIds.AccessUpdate)]
public void AccessUpdate(IConnection connection)
{
ConfigUpdateAccessInfo info = MemoryPackSerializer.Deserialize<ConfigUpdateAccessInfo>(connection.ReceiveRequestWrap.Payload.Span);
accessTransfer.SetAccess(info);
connection.Write(Helper.TrueArray);
}
[MessengerId((ushort)ConfigMessengerIds.Sync)]
public void Sync(IConnection connection)
{

View File

@@ -4,15 +4,6 @@
{
Min = 2500,
//Update = 2501,
//UpdateForward = 2502,
Access = 2503,
AccessForward = 2504,
AccessUpdate = 2505,
AccessUpdateForward = 2506,
Sync = 2507,
SyncForward = 2508,

View File

@@ -0,0 +1,43 @@
using linker.config;
using linker.plugins.config.messenger;
using linker.startup;
using Microsoft.Extensions.DependencyInjection;
namespace linker.plugins.decenter
{
public sealed class DecenterStartup : IStartup
{
public string Name => "decenter";
public bool Required => true;
public StartupLevel Level => StartupLevel.Top;
public string[] Dependent => new string[] { "messenger", "signin", "serialize" };
public StartupLoadType LoadType => StartupLoadType.Normal;
public void AddClient(ServiceCollection serviceCollection, FileConfig config)
{
serviceCollection.AddSingleton<DecenterTransfer>();
serviceCollection.AddSingleton<DecenterTypesLoader>();
}
public void AddServer(ServiceCollection serviceCollection, FileConfig config)
{
serviceCollection.AddSingleton<ConfigServerMessenger>();
}
public void UseClient(ServiceProvider serviceProvider, FileConfig config)
{
DecenterTransfer decenterTransfer = serviceProvider.GetService<DecenterTransfer>();
DecenterTypesLoader decenterTypesLoader = serviceProvider.GetService<DecenterTypesLoader>();
}
public void UseServer(ServiceProvider serviceProvider, FileConfig config)
{
}
}
}

View File

@@ -0,0 +1,83 @@
using linker.plugins.client;
using linker.plugins.messenger;
using MemoryPack;
using linker.libs;
using linker.plugins.decenter.messenger;
namespace linker.plugins.decenter
{
[MemoryPackable]
public sealed partial class DecenterSyncInfo
{
public DecenterSyncInfo() { }
public string Name { get; set; }
public Memory<byte> Data { get; set; }
}
public sealed class DecenterTransfer
{
private List<IDecenter> syncs = new List<IDecenter>();
private readonly IMessengerSender messengerSender;
private readonly ClientSignInState clientSignInState;
public DecenterTransfer(IMessengerSender messengerSender, ClientSignInState clientSignInState)
{
this.messengerSender = messengerSender;
this.clientSignInState = clientSignInState;
SyncTask();
}
public void LoadDecenters(List<IDecenter> list)
{
syncs = list;
}
public Memory<byte> Sync(DecenterSyncInfo decenterSyncInfo)
{
IDecenter sync = syncs.FirstOrDefault(c => c.Name == decenterSyncInfo.Name);
if (sync != null)
{
sync.SetData(decenterSyncInfo.Data);
return sync.GetData();
}
return Helper.EmptyArray;
}
private void SyncTask()
{
TimerHelper.SetInterval(async () =>
{
try
{
foreach (var sync in syncs)
{
if (sync.DataVersion.Reset())
{
MessageResponeInfo resp = await messengerSender.SendReply(new MessageRequestWrap
{
Connection = clientSignInState.Connection,
MessengerId = (ushort)DecenterMessengerIds.SyncForward,
Payload = MemoryPackSerializer.Serialize(new DecenterSyncInfo { Name = sync.Name, Data = sync.GetData() })
});
if (resp.Code == MessageResponeCodes.OK)
{
sync.SetData(MemoryPackSerializer.Deserialize<List<ReadOnlyMemory<byte>>>(resp.Data.Span));
}
}
}
}
catch (Exception ex)
{
if (LoggerHelper.Instance.LoggerLevel <= LoggerTypes.DEBUG)
{
LoggerHelper.Instance.Error(ex);
}
}
return true;
}, () => 3000);
}
}
}

View File

@@ -0,0 +1,17 @@
using linker.libs;
using Microsoft.Extensions.DependencyInjection;
namespace linker.plugins.decenter
{
public sealed partial class DecenterTypesLoader
{
public DecenterTypesLoader(DecenterTransfer decenterTransfer, ServiceProvider serviceProvider)
{
var types = GetSourceGeneratorTypes();
var syncs = types.Select(c => (IDecenter)serviceProvider.GetService(c)).Where(c => c != null).ToList();
decenterTransfer.LoadDecenters(syncs);
LoggerHelper.Instance.Info($"load decenter transport:{string.Join(",", syncs.Select(c => c.GetType().Name))}");
}
}
}

View File

@@ -0,0 +1,13 @@
using linker.libs;
namespace linker.plugins.decenter
{
public interface IDecenter
{
public string Name { get; }
public VersionManager DataVersion { get; }
public Memory<byte> GetData();
public void SetData(Memory<byte> data);
public void SetData(List<ReadOnlyMemory<byte>> data);
}
}

View File

@@ -0,0 +1,74 @@
using linker.plugins.messenger;
using linker.plugins.signin.messenger;
using MemoryPack;
namespace linker.plugins.decenter.messenger
{
public sealed class DecenterServerMessenger : IMessenger
{
private readonly IMessengerSender sender;
private readonly SignCaching signCaching;
public DecenterServerMessenger(IMessengerSender sender, SignCaching signCaching)
{
this.sender = sender;
this.signCaching = signCaching;
}
[MessengerId((ushort)DecenterMessengerIds.SyncForward)]
public void SyncForward(IConnection connection)
{
DecenterSyncInfo info = MemoryPackSerializer.Deserialize<DecenterSyncInfo>(connection.ReceiveRequestWrap.Payload.Span);
if (signCaching.TryGet(connection.Id, out SignCacheInfo cache))
{
uint requiestid = connection.ReceiveRequestWrap.RequestId;
List<SignCacheInfo> caches = signCaching.Get(cache.GroupId);
List<Task<MessageResponeInfo>> tasks = new List<Task<MessageResponeInfo>>();
foreach (SignCacheInfo item in caches.Where(c => c.MachineId != connection.Id && c.Connected))
{
tasks.Add(sender.SendReply(new MessageRequestWrap
{
Connection = item.Connection,
MessengerId = (ushort)DecenterMessengerIds.Sync,
Payload = connection.ReceiveRequestWrap.Payload,
Timeout = 1000,
}));
}
Task.WhenAll(tasks).ContinueWith(async (result) =>
{
List<ReadOnlyMemory<byte>> results = tasks.Where(c => c.Result.Code == MessageResponeCodes.OK)
.Select(c => c.Result.Data).ToList();
await sender.ReplyOnly(new MessageResponseWrap
{
RequestId = requiestid,
Connection = connection,
Payload = MemoryPackSerializer.Serialize(results)
}, (ushort)DecenterMessengerIds.SyncForward).ConfigureAwait(false);
});
}
}
}
public sealed class DecenterClientMessenger : IMessenger
{
private readonly DecenterTransfer syncTreansfer;
public DecenterClientMessenger(DecenterTransfer syncTreansfer)
{
this.syncTreansfer = syncTreansfer;
}
[MessengerId((ushort)DecenterMessengerIds.Sync)]
public void Sync(IConnection connection)
{
DecenterSyncInfo info = MemoryPackSerializer.Deserialize<DecenterSyncInfo>(connection.ReceiveRequestWrap.Payload.Span);
syncTreansfer.Sync(info);
}
}
}

View File

@@ -0,0 +1,12 @@
namespace linker.plugins.decenter.messenger
{
public enum DecenterMessengerIds : ushort
{
Min = 2800,
Sync = 2807,
SyncForward = 2808,
Max = 2899
}
}

View File

@@ -3,6 +3,7 @@ using System.Net.Sockets;
using linker.libs.extends;
using System.Buffers;
using System.Net;
using linker.plugins.messenger;
namespace linker.plugins.resolver
{
@@ -31,6 +32,7 @@ namespace linker.plugins.resolver
{
return;
}
LoggerHelper.Instance.Info($"tcp connect from {socket.RemoteEndPoint}");
socket.KeepAlive();
int length = await socket.ReceiveAsync(buffer.AsMemory(0, 1), SocketFlags.None).ConfigureAwait(false);

View File

@@ -67,7 +67,7 @@ namespace linker.plugins.signIn.args
//之前的登录有唯一编号的,则验证,唯一编号不一样,不允许登录
if (string.IsNullOrWhiteSpace(keyOld) == false && keyNew != keyOld)
{
return "your machine key is already online";
return $"your id 【{signInfo.MachineId}】 is already online, online machineName {cache.MachineName}";
}
}
await Task.CompletedTask;

View File

@@ -42,6 +42,8 @@ namespace linker.plugins.signin.messenger
public async Task SignIn(IConnection connection)
{
SignInfo info = MemoryPackSerializer.Deserialize<SignInfo>(connection.ReceiveRequestWrap.Payload.Span);
LoggerHelper.Instance.Info($"sign in from {connection.Address}->{info.ToJson()}");
info.Connection = connection;
SignInResponseInfo resp = new SignInResponseInfo();

View File

@@ -0,0 +1,176 @@
using linker.libs.api;
using MemoryPack;
using linker.libs.extends;
using System.Collections.Concurrent;
using linker.config;
using linker.tunnel.connection;
using linker.plugins.client;
using linker.plugins.capi;
using linker.plugins.messenger;
using linker.client.config;
using linker.plugins.socks5.config;
using linker.plugins.socks5.messenger;
namespace linker.plugins.socks5
{
public sealed class Socks5ClientApiController : IApiClientController
{
private readonly IMessengerSender messengerSender;
private readonly Socks5ConfigTransfer socks5ConfigTransfer;
private readonly ClientSignInState clientSignInState;
private readonly FileConfig config;
private readonly TunnelProxy tunnelProxy;
private readonly RunningConfig runningConfig;
public Socks5ClientApiController(IMessengerSender messengerSender, Socks5ConfigTransfer socks5ConfigTransfer, ClientSignInState clientSignInState, FileConfig config, TunnelProxy tunnelProxy, RunningConfig runningConfig, Socks5ConfigTransfer Socks5ConfigTransfer)
{
this.messengerSender = messengerSender;
this.socks5ConfigTransfer = socks5ConfigTransfer;
this.clientSignInState = clientSignInState;
this.config = config;
this.tunnelProxy = tunnelProxy;
this.runningConfig = runningConfig;
}
public ConnectionListInfo Connections(ApiControllerParamsInfo param)
{
ulong hashCode = ulong.Parse(param.Content);
if (tunnelProxy.Version.Eq(hashCode, out ulong version) == false)
{
return new ConnectionListInfo
{
List = tunnelProxy.GetConnections(),
HashCode = version
};
}
return new ConnectionListInfo { HashCode = version };
}
[ClientApiAccess(ClientApiAccess.TunnelRemove)]
public void RemoveConnection(ApiControllerParamsInfo param)
{
tunnelProxy.RemoveConnection(param.Content);
}
/// <summary>
/// 获取所有客户端的信息
/// </summary>
/// <param name="param"></param>
/// <returns></returns>
public Socks5ListInfo Get(ApiControllerParamsInfo param)
{
ulong hashCode = ulong.Parse(param.Content);
if (socks5ConfigTransfer.Version.Eq(hashCode, out ulong version) == false)
{
return new Socks5ListInfo
{
List = socks5ConfigTransfer.Infos,
HashCode = version
};
}
return new Socks5ListInfo { HashCode = version };
}
/// <summary>
/// 刷新信息
/// </summary>
/// <param name="param"></param>
public void Refresh(ApiControllerParamsInfo param)
{
socks5ConfigTransfer.RefreshConfig();
}
/// <summary>
/// 运行网卡
/// </summary>
/// <param name="param"></param>
/// <returns></returns>
public async Task<bool> Run(ApiControllerParamsInfo param)
{
//运行自己的
if (param.Content == config.Data.Client.Id)
{
if (config.Data.Client.HasAccess(ClientApiAccess.Socks5StatusSelf) == false) return false;
socks5ConfigTransfer.Retstart();
}
else
{
if (config.Data.Client.HasAccess(ClientApiAccess.Socks5StatusOther) == false) return false;
//运行别人的
await messengerSender.SendOnly(new MessageRequestWrap
{
Connection = clientSignInState.Connection,
MessengerId = (ushort)Socks5MessengerIds.RunForward,
Payload = MemoryPackSerializer.Serialize(param.Content)
}).ConfigureAwait(false);
}
return true;
}
/// <summary>
/// 停止网卡
/// </summary>
/// <param name="param"></param>
/// <returns></returns>
public async Task<bool> Stop(ApiControllerParamsInfo param)
{
//停止自己的
if (param.Content == config.Data.Client.Id)
{
if (config.Data.Client.HasAccess(ClientApiAccess.Socks5StatusSelf) == false) return false;
socks5ConfigTransfer.Stop();
}
else
{
if (config.Data.Client.HasAccess(ClientApiAccess.Socks5StatusOther) == false) return false;
//停止别人的
await messengerSender.SendOnly(new MessageRequestWrap
{
Connection = clientSignInState.Connection,
MessengerId = (ushort)Socks5MessengerIds.StopForward,
Payload = MemoryPackSerializer.Serialize(param.Content)
}).ConfigureAwait(false);
}
return true;
}
/// <summary>
/// 更新网卡信息
/// </summary>
/// <param name="param"></param>
/// <returns></returns>
public async Task<bool> Update(ApiControllerParamsInfo param)
{
Socks5Info info = param.Content.DeJson<Socks5Info>();
//更新自己的
if (info.MachineId == config.Data.Client.Id)
{
if (config.Data.Client.HasAccess(ClientApiAccess.Socks5ChangeSelf) == false) return false;
socks5ConfigTransfer.UpdateConfig(info);
}
else
{
if (config.Data.Client.HasAccess(ClientApiAccess.Socks5ChangeOther) == false) return false;
//更新别人的
await messengerSender.SendOnly(new MessageRequestWrap
{
Connection = clientSignInState.Connection,
MessengerId = (ushort)Socks5MessengerIds.UpdateForward,
Payload = MemoryPackSerializer.Serialize(info)
}).ConfigureAwait(false);
}
return true;
}
}
public sealed class Socks5ListInfo
{
public ConcurrentDictionary<string, Socks5Info> List { get; set; }
public ulong HashCode { get; set; }
}
public sealed class ConnectionListInfo
{
public ConcurrentDictionary<string, ITunnelConnection> List { get; set; }
public ulong HashCode { get; set; }
}
}

View File

@@ -0,0 +1,248 @@
using linker.client.config;
using linker.config;
using linker.libs;
using MemoryPack;
using System.Collections.Concurrent;
using System.Net;
using linker.plugins.client;
using linker.plugins.messenger;
using linker.plugins.socks5.config;
using linker.plugins.decenter;
namespace linker.plugins.socks5
{
public sealed class Socks5ConfigTransfer : IDecenter
{
public string Name => "socks5";
public VersionManager DataVersion { get; } = new VersionManager();
private readonly IMessengerSender messengerSender;
private readonly ClientSignInState clientSignInState;
private readonly FileConfig config;
private readonly RunningConfig runningConfig;
private readonly TunnelProxy tunnelProxy;
public VersionManager Version { get; } = new VersionManager();
private readonly ConcurrentDictionary<string, Socks5Info> socks5Infos = new ConcurrentDictionary<string, Socks5Info>();
public ConcurrentDictionary<string, Socks5Info> Infos => socks5Infos;
private readonly SemaphoreSlim slim = new SemaphoreSlim(1);
public Socks5ConfigTransfer(IMessengerSender messengerSender, ClientSignInState clientSignInState, FileConfig config, RunningConfig runningConfig, TunnelProxy tunnelProxy)
{
this.messengerSender = messengerSender;
this.clientSignInState = clientSignInState;
this.config = config;
this.runningConfig = runningConfig;
this.tunnelProxy = tunnelProxy;
clientSignInState.NetworkEnabledHandle += (times) => DataVersion.Add();
}
public Memory<byte> GetData()
{
Socks5Info info = new Socks5Info
{
Lans = runningConfig.Data.Socks5.Lans.Select(c => { c.Exists = false; return c; }).ToList(),
MachineId = config.Data.Client.Id,
Status = tunnelProxy.Running ? Socks5Status.Running : Socks5Status.Normal,
Port = runningConfig.Data.Socks5.Port,
SetupError = tunnelProxy.Error
};
socks5Infos.AddOrUpdate(info.MachineId, info, (a, b) => info);
Version.Add();
return MemoryPackSerializer.Serialize(info);
}
public void SetData(Memory<byte> data)
{
Socks5Info info = MemoryPackSerializer.Deserialize<Socks5Info>(data.Span);
TimerHelper.Async(async () =>
{
await slim.WaitAsync();
try
{
socks5Infos.AddOrUpdate(info.MachineId, info, (a, b) => info);
Version.Add();
AddRoute();
}
catch (Exception ex)
{
if (LoggerHelper.Instance.LoggerLevel <= LoggerTypes.DEBUG)
{
LoggerHelper.Instance.Error(ex);
}
}
slim.Release();
});
}
public void SetData(List<ReadOnlyMemory<byte>> data)
{
List<Socks5Info> list = data.Select(c => MemoryPackSerializer.Deserialize<Socks5Info>(c.Span)).ToList();
TimerHelper.Async(async () =>
{
await slim.WaitAsync();
try
{
foreach (var item in list)
{
socks5Infos.AddOrUpdate(item.MachineId, item, (a, b) => item);
item.LastTicks.Update();
}
var removes = socks5Infos.Keys.Except(list.Select(c => c.MachineId)).ToList();
foreach (var item in removes)
{
if (socks5Infos.TryGetValue(item, out Socks5Info socks5Info))
{
socks5Info.Status = Socks5Status.Normal;
socks5Info.LastTicks.Clear();
}
}
Version.Add();
AddRoute();
}
catch (Exception ex)
{
if (LoggerHelper.Instance.LoggerLevel <= LoggerTypes.DEBUG)
{
LoggerHelper.Instance.Error(ex);
}
}
finally
{
slim.Release();
}
});
}
/// <summary>
/// 重启
/// </summary>
/// <returns></returns>
public void Retstart()
{
tunnelProxy.Start(runningConfig.Data.Socks5.Port);
}
/// <summary>
/// 网卡
/// </summary>
public void Stop()
{
tunnelProxy.Stop();
}
/// <summary>
/// 刷新信息,把自己的配置发给别人,顺便把别人的信息带回来
/// </summary>
public void RefreshConfig()
{
DataVersion.Add();
}
/// <summary>
/// 更新本机信息
/// </summary>
/// <param name="info"></param>
public void UpdateConfig(Socks5Info info)
{
TimerHelper.Async(() =>
{
int port = runningConfig.Data.Socks5.Port;
runningConfig.Data.Socks5.Port = info.Port;
runningConfig.Data.Socks5.Lans = info.Lans;
runningConfig.Data.Update();
bool needReboot = (port != runningConfig.Data.Socks5.Port && runningConfig.Data.Socks5.Running)
|| (runningConfig.Data.Socks5.Running && tunnelProxy.Running == false);
if (needReboot)
{
Retstart();
}
GetData();
DataVersion.Add();
});
}
/// <summary>
/// 添加路由
/// </summary>
private void AddRoute()
{
List<Socks5LanIPAddressList> ipsList = ParseIPs(socks5Infos.Values.ToList());
Socks5LanIPAddress[] ips = ipsList.SelectMany(c => c.IPS).ToArray();
tunnelProxy.SetIPs(ips);
Version.Add();
}
private List<Socks5LanIPAddressList> ParseIPs(List<Socks5Info> infos)
{
//排除的IP
uint[] excludeIps =//本机局域网IP
config.Data.Client.Tunnel.LocalIPs.Where(c => c.AddressFamily == System.Net.Sockets.AddressFamily.InterNetwork)
//路由上的IP
.Concat(config.Data.Client.Tunnel.RouteIPs)
//网卡IP 服务器IP
.Concat(new IPAddress[] { runningConfig.Data.Tuntap.IP, clientSignInState.Connection.Address.Address })
//网卡配置的局域网IP
.Concat(runningConfig.Data.Socks5.Lans.Select(c => c.IP))
.Select(NetworkHelper.IP2Value)
.ToArray();
HashSet<uint> hashSet = new HashSet<uint>();
return infos
.Where(c => c.MachineId != config.Data.Client.Id)
.OrderByDescending(c => c.Status)
.OrderByDescending(c => c.LastTicks.Value)
.Select(c =>
{
var lans = c.Lans.Where(c => c.Disabled == false && c.IP.Equals(IPAddress.Any) == false);
foreach (var lan in lans)
{
uint ipInt = NetworkHelper.IP2Value(lan.IP);
uint maskValue = NetworkHelper.PrefixLength2Value(lan.PrefixLength);
lan.Exists = excludeIps.Count(d => (d & maskValue) == (ipInt & maskValue)) > 0 || hashSet.Contains(ipInt & maskValue);
hashSet.Add(ipInt & maskValue);
}
return new Socks5LanIPAddressList
{
MachineId = c.MachineId,
IPS = ParseIPs(lans.Where(c => c.Disabled == false && c.Exists == false).ToList(), c.MachineId)
.Where(c => excludeIps.Select(d => d & c.MaskValue).Contains(c.NetWork) == false).ToList(),
};
}).ToList();
}
private List<Socks5LanIPAddress> ParseIPs(List<Socks5LanInfo> lans, string machineid)
{
return lans.Where(c => c.IP.Equals(IPAddress.Any) == false && c != null).Select((c, index) =>
{
return ParseIPAddress(c.IP, c.PrefixLength, machineid);
}).ToList();
}
private Socks5LanIPAddress ParseIPAddress(IPAddress ip, byte prefixLength, string machineid)
{
uint ipInt = NetworkHelper.IP2Value(ip);
//掩码十进制
uint maskValue = NetworkHelper.PrefixLength2Value(prefixLength);
return new Socks5LanIPAddress
{
IPAddress = ipInt,
PrefixLength = prefixLength,
MaskValue = maskValue,
NetWork = ipInt & maskValue,
Broadcast = ipInt | ~maskValue,
OriginIPAddress = ip,
MachineId = machineid
};
}
}
}

View File

@@ -10,6 +10,7 @@ using System.Net;
using System.Net.Sockets;
using linker.plugins.tunnel;
using linker.plugins.client;
using linker.plugins.socks5.config;
namespace linker.plugins.socks5
{
@@ -18,7 +19,9 @@ namespace linker.plugins.socks5
private IPEndPoint proxyEP;
private uint[] maskValues = Array.Empty<uint>();
private readonly ConcurrentDictionary<uint, string> dic = new ConcurrentDictionary<uint, string>();
private readonly ConcurrentDictionary<uint, List<string>> ip2MachineDic = new ConcurrentDictionary<uint, List<string>>();
protected override string TransactionId => "socks5";
public TunnelProxy(FileConfig config, TunnelTransfer tunnelTransfer, RelayTransfer relayTransfer, ClientSignInTransfer clientSignInTransfer, ClientSignInState clientSignInState)
: base(config, tunnelTransfer, relayTransfer, clientSignInTransfer, clientSignInState)
@@ -26,13 +29,44 @@ namespace linker.plugins.socks5
TaskUdp();
}
public void Start()
/// <summary>
/// 设置IP等下有连接进来用IP匹配才能知道这个连接是要连谁
/// </summary>
/// <param name="ips"></param>
public void SetIPs(Socks5LanIPAddress[] ips)
{
var dic = ips.GroupBy(c => c.NetWork).ToDictionary(c => c.Key, d => d.Select(e => e.MachineId).ToList());
foreach (var item in dic)
{
ip2MachineDic.AddOrUpdate(item.Key, item.Value, (a, b) => item.Value);
}
maskValues = ips.Select(c => c.MaskValue).Distinct().OrderBy(c => c).ToArray();
}
/// <summary>
/// 设置IP等下有连接进来用IP匹配才能知道这个连接是要连谁
/// </summary>
/// <param name="machineId"></param>
/// <param name="ip"></param>
public void SetIP(string machineId, uint ip)
{
if (ip2MachineDic.TryGetValue(ip, out List<string> list) == false)
{
list = new List<string>();
ip2MachineDic.AddOrUpdate(ip, list, (a, b) => list);
}
list.Add(machineId);
}
public void Start(int port)
{
if (proxyEP != null)
{
Stop(proxyEP.Port);
}
Start(new IPEndPoint(IPAddress.Any, 0), 3);
Start(new IPEndPoint(IPAddress.Any, port), 3);
proxyEP = new IPEndPoint(IPAddress.Any, LocalEndpoint.Port);
}
@@ -175,9 +209,9 @@ namespace linker.plugins.socks5
for (int i = 0; i < maskValues.Length; i++)
{
uint network = ip & maskValues[i];
if (dic.TryGetValue(network, out string machineId))
if (ip2MachineDic.TryGetValue(network, out List<string> machineId))
{
return await ConnectTunnel(machineId, TunnelProtocolType.Udp).ConfigureAwait(false);
return await ConnectTunnel(machineId[0], TunnelProtocolType.Udp).ConfigureAwait(false);
}
}

View File

@@ -0,0 +1,50 @@
using linker.config;
using linker.plugins.socks5;
using linker.plugins.socks5.messenger;
using linker.startup;
using linker.tun;
using Microsoft.Extensions.DependencyInjection;
namespace linker.plugins.Socks5
{
public sealed class Socks5Startup : IStartup
{
public StartupLevel Level => StartupLevel.Normal;
public string Name => "socks5";
public bool Required => false;
public string[] Dependent => new string[] { "messenger", "relay", "tunnel", "signin", "serialize", "config" };
public StartupLoadType LoadType => StartupLoadType.Normal;
public void AddClient(ServiceCollection serviceCollection, FileConfig config)
{
serviceCollection.AddSingleton<Socks5ClientApiController>();
serviceCollection.AddSingleton<LinkerTunDeviceAdapter>();
serviceCollection.AddSingleton<TunnelProxy>();
serviceCollection.AddSingleton<Socks5ClientMessenger>();
serviceCollection.AddSingleton<Socks5ConfigTransfer>();
}
public void AddServer(ServiceCollection serviceCollection, FileConfig config)
{
serviceCollection.AddSingleton<Socks5ServerMessenger>();
}
public void UseClient(ServiceProvider serviceProvider, FileConfig config)
{
TunnelProxy socks5Proxy = serviceProvider.GetService<TunnelProxy>();
Socks5ConfigTransfer socks5ConfigTransfer = serviceProvider.GetService<Socks5ConfigTransfer>();
}
public void UseServer(ServiceProvider serviceProvider, FileConfig config)
{
}
}
}

View File

@@ -15,12 +15,16 @@ namespace linker.plugins.socks5
private Socket socket;
public IPEndPoint LocalEndpoint { get; private set; }
public bool Running { get; private set; }
public string Error { get; private set; }
/// <summary>
/// 监听一个端口
/// </summary>
/// <param name="port"></param>
private void StartTcp(IPEndPoint ep, byte bufferSize)
{
Error = string.Empty;
IPEndPoint _localEndPoint = ep;
socket = new Socket(_localEndPoint.AddressFamily, SocketType.Stream, ProtocolType.Tcp);
socket.IPv6Only(_localEndPoint.AddressFamily, false);
@@ -41,8 +45,10 @@ namespace linker.plugins.socks5
}
catch (Exception ex)
{
Error = ex.Message;
LoggerHelper.Instance.Error(ex);
}
Running = true;
}
/// <summary>
/// 接收连接
@@ -63,6 +69,7 @@ namespace linker.plugins.socks5
LoggerHelper.Instance.Error(ex);
token.Clear();
}
Running = false;
}
private void ProcessAccept(AsyncUserToken acceptToken, Socket socket)

View File

@@ -0,0 +1,123 @@
using linker.libs;
using linker.plugins.socks5.config;
using MemoryPack;
using System.Net;
namespace linker.plugins.socks5.config
{
[MemoryPackable]
public sealed partial class Socks5ConfigInfo
{
public Socks5ConfigInfo() { }
public int Port { get; set; } = 1805;
/// <summary>
/// 局域网配置列表
/// </summary>
public List<Socks5LanInfo> Lans { get; set; } = new List<Socks5LanInfo>();
/// <summary>
/// 是否在运行中
/// </summary>
public bool Running { get; set; }
}
[MemoryPackable]
public sealed partial class Socks5LanIPAddress
{
/// <summary>
/// ip存小端
/// </summary>
public uint IPAddress { get; set; }
public byte PrefixLength { get; set; }
public uint MaskValue { get; set; }
public uint NetWork { get; set; }
public uint Broadcast { get; set; }
[MemoryPackIgnore]
public string MachineId { get; set; }
[MemoryPackIgnore]
public IPAddress OriginIPAddress { get; set; }
}
[MemoryPackable]
public sealed partial class Socks5LanIPAddressList
{
public string MachineId { get; set; }
public List<Socks5LanIPAddress> IPS { get; set; }
}
[MemoryPackable]
public sealed partial class Socks5Info
{
/// <summary>
/// 设备id
/// </summary>
public string MachineId { get; set; }
/// <summary>
/// 状态
/// </summary>
public Socks5Status Status { get; set; }
/// <summary>
/// 前缀长度
/// </summary>
public int Port { get; set; } = 1804;
/// <summary>
/// 局域网IP列表
/// </summary>
public List<Socks5LanInfo> Lans { get; set; } = new List<Socks5LanInfo>();
/// <summary>
/// 安装错误
/// </summary>
public string SetupError { get; set; }
[MemoryPackIgnore]
public LastTicksManager LastTicks { get; set; } = new LastTicksManager();
}
[MemoryPackable]
public sealed partial class Socks5LanInfo
{
[MemoryPackAllowSerialize]
public IPAddress IP { get; set; }
public byte PrefixLength { get; set; } = 24;
public bool Disabled { get; set; }
public bool Exists { get; set; }
public string Error { get; set; } = string.Empty;
}
public enum Socks5Status : byte
{
/// <summary>
/// 无
/// </summary>
Normal = 0,
/// <summary>
/// 操作中
/// </summary>
Operating = 1,
/// <summary>
/// 运行中
/// </summary>
Running = 2
}
}
namespace linker.client.config
{
public sealed partial class RunningConfigInfo
{
/// <summary>
/// 虚拟网卡配置
/// </summary>
public Socks5ConfigInfo Socks5 { get; set; } = new Socks5ConfigInfo();
}
}

View File

@@ -0,0 +1,127 @@
using linker.config;
using linker.plugins.messenger;
using linker.plugins.signin.messenger;
using linker.plugins.socks5.config;
using MemoryPack;
namespace linker.plugins.socks5.messenger
{
public sealed class Socks5ClientMessenger : IMessenger
{
private readonly Socks5ConfigTransfer socks5ConfigTransfer;
private readonly TunnelProxy socks5Proxy;
public Socks5ClientMessenger(Socks5ConfigTransfer socks5ConfigTransfer, TunnelProxy socks5Proxy)
{
this.socks5ConfigTransfer = socks5ConfigTransfer;
this.socks5Proxy = socks5Proxy;
}
/// <summary>
/// 运行
/// </summary>
/// <param name="connection"></param>
[MessengerId((ushort)Socks5MessengerIds.Run)]
public void Run(IConnection connection)
{
socks5ConfigTransfer.Retstart();
}
/// <summary>
/// 停止
/// </summary>
/// <param name="connection"></param>
[MessengerId((ushort)Socks5MessengerIds.Stop)]
public void Stop(IConnection connection)
{
socks5ConfigTransfer.Stop();
}
/// <summary>
/// 更新信息
/// </summary>
/// <param name="connection"></param>
[MessengerId((ushort)Socks5MessengerIds.Update)]
public void Update(IConnection connection)
{
Socks5Info info = MemoryPackSerializer.Deserialize<Socks5Info>(connection.ReceiveRequestWrap.Payload.Span);
socks5ConfigTransfer.UpdateConfig(info);
}
}
public sealed class Socks5ServerMessenger : IMessenger
{
private readonly IMessengerSender messengerSender;
private readonly SignCaching signCaching;
private readonly FileConfig config;
public Socks5ServerMessenger(IMessengerSender messengerSender, SignCaching signCaching, FileConfig config)
{
this.messengerSender = messengerSender;
this.signCaching = signCaching;
this.config = config;
}
/// <summary>
/// 转发运行命令
/// </summary>
/// <param name="connection"></param>
/// <returns></returns>
[MessengerId((ushort)Socks5MessengerIds.RunForward)]
public async Task RunForward(IConnection connection)
{
string name = MemoryPackSerializer.Deserialize<string>(connection.ReceiveRequestWrap.Payload.Span);
if (signCaching.TryGet(name, out SignCacheInfo cache) && signCaching.TryGet(connection.Id, out SignCacheInfo cache1) && cache.GroupId == cache1.GroupId)
{
await messengerSender.SendOnly(new MessageRequestWrap
{
Connection = cache.Connection,
Timeout = 3000,
MessengerId = (ushort)Socks5MessengerIds.Run
}).ConfigureAwait(false);
}
}
/// <summary>
/// 转发停止命令
/// </summary>
/// <param name="connection"></param>
/// <returns></returns>
[MessengerId((ushort)Socks5MessengerIds.StopForward)]
public async Task StopForward(IConnection connection)
{
string name = MemoryPackSerializer.Deserialize<string>(connection.ReceiveRequestWrap.Payload.Span);
if (signCaching.TryGet(name, out SignCacheInfo cache) && signCaching.TryGet(connection.Id, out SignCacheInfo cache1) && cache.GroupId == cache1.GroupId)
{
await messengerSender.SendOnly(new MessageRequestWrap
{
Connection = cache.Connection,
Timeout = 3000,
MessengerId = (ushort)Socks5MessengerIds.Stop
}).ConfigureAwait(false);
}
}
/// <summary>
/// 转发更新信息命令
/// </summary>
/// <param name="connection"></param>
/// <returns></returns>
[MessengerId((ushort)Socks5MessengerIds.UpdateForward)]
public async Task UpdateForward(IConnection connection)
{
Socks5Info info = MemoryPackSerializer.Deserialize<Socks5Info>(connection.ReceiveRequestWrap.Payload.Span);
if (signCaching.TryGet(info.MachineId, out SignCacheInfo cache) && signCaching.TryGet(connection.Id, out SignCacheInfo cache1) && cache.GroupId == cache1.GroupId)
{
await messengerSender.SendOnly(new MessageRequestWrap
{
Connection = cache.Connection,
Timeout = 3000,
MessengerId = (ushort)Socks5MessengerIds.Update,
Payload = connection.ReceiveRequestWrap.Payload
}).ConfigureAwait(false);
}
}
}
}

View File

@@ -0,0 +1,17 @@
namespace linker.plugins.socks5.messenger
{
public enum Socks5MessengerIds : ushort
{
Run = 2900,
RunForward = 2901,
Stop = 2902,
StopForward = 2903,
Update = 2904,
UpdateForward = 2905,
None = 2999
}
}

View File

@@ -2,11 +2,11 @@
using linker.config;
using linker.libs;
using linker.plugins.client;
using linker.plugins.decenter;
using linker.plugins.messenger;
using linker.plugins.tunnel.messenger;
using linker.tunnel;
using linker.tunnel.adapter;
using linker.tunnel.wanport;
using MemoryPack;
using System.Collections.Concurrent;
using System.Net;
@@ -14,8 +14,12 @@ using System.Net.Quic;
namespace linker.plugins.tunnel
{
public sealed class TunnelConfigTransfer
public sealed class TunnelConfigTransfer:IDecenter
{
public string Name => "tunnel";
public VersionManager DataVersion { get; } = new VersionManager();
private readonly FileConfig config;
private readonly RunningConfig running;
private readonly ClientSignInState clientSignInState;
@@ -26,6 +30,8 @@ namespace linker.plugins.tunnel
public VersionManager Version { get; } = new VersionManager();
public ConcurrentDictionary<string, TunnelTransportRouteLevelInfo> Config { get; } = new ConcurrentDictionary<string, TunnelTransportRouteLevelInfo>();
public TunnelConfigTransfer(FileConfig config, RunningConfig running, ClientSignInState clientSignInState, IMessengerSender messengerSender, ITunnelAdapter tunnelAdapter, TunnelUpnpTransfer upnpTransfer)
{
this.config = config;
@@ -38,12 +44,40 @@ namespace linker.plugins.tunnel
clientSignInState.NetworkEnabledHandle += (times) =>
{
RefreshRouteLevel();
GetRemoteRouteLevel();
DataVersion.Add();
RefreshPortMap();
};
TestQuic();
}
public Memory<byte> GetData()
{
TunnelTransportRouteLevelInfo tunnelTransportRouteLevelInfo = GetLocalRouteLevel();
Config.AddOrUpdate(tunnelTransportRouteLevelInfo.MachineId, tunnelTransportRouteLevelInfo, (a, b) => tunnelTransportRouteLevelInfo);
Version.Add();
return MemoryPackSerializer.Serialize(tunnelTransportRouteLevelInfo);
}
public void SetData(Memory<byte> data)
{
TunnelTransportRouteLevelInfo tunnelTransportRouteLevelInfo = MemoryPackSerializer.Deserialize<TunnelTransportRouteLevelInfo>(data.Span);
Config.AddOrUpdate(tunnelTransportRouteLevelInfo.MachineId, tunnelTransportRouteLevelInfo, (a, b) => tunnelTransportRouteLevelInfo);
Version.Add();
}
public void SetData(List<ReadOnlyMemory<byte>> data)
{
List<TunnelTransportRouteLevelInfo> list = data.Select(c => MemoryPackSerializer.Deserialize<TunnelTransportRouteLevelInfo>(c.Span)).ToList();
foreach (var item in list)
{
Config.AddOrUpdate(item.MachineId, item, (a, b) => item);
}
TunnelTransportRouteLevelInfo config = GetLocalRouteLevel();
Config.AddOrUpdate(config.MachineId, config, (a, b) => config);
Version.Add();
}
/// <summary>
/// 刷新网关等级数据
/// </summary>
private void RefreshRouteLevel()
{
TimerHelper.Async(() =>
@@ -59,7 +93,7 @@ namespace linker.plugins.tunnel
/// </summary>
public void RefreshConfig()
{
GetRemoteRouteLevel();
DataVersion.Add();
}
/// <summary>
/// 修改自己的网关层级信息
@@ -71,42 +105,8 @@ namespace linker.plugins.tunnel
running.Data.Tunnel.PortMapWan = tunnelTransportFileConfigInfo.PortMapWan;
running.Data.Tunnel.PortMapLan = tunnelTransportFileConfigInfo.PortMapLan;
running.Data.Update();
GetRemoteRouteLevel();
}
/// <summary>
/// 收到别人的信息
/// </summary>
/// <param name="tunnelTransportFileConfigInfo"></param>
/// <returns></returns>
public TunnelTransportRouteLevelInfo OnRemoteRouteLevel(TunnelTransportRouteLevelInfo tunnelTransportFileConfigInfo)
{
Config.AddOrUpdate(tunnelTransportFileConfigInfo.MachineId, tunnelTransportFileConfigInfo, (a, b) => tunnelTransportFileConfigInfo);
Version.Add();
return GetLocalRouteLevel();
}
private void GetRemoteRouteLevel()
{
TunnelTransportRouteLevelInfo config = GetLocalRouteLevel();
messengerSender.SendReply(new MessageRequestWrap
{
Connection = clientSignInState.Connection,
MessengerId = (ushort)TunnelMessengerIds.ConfigForward,
Timeout = 10000,
Payload = MemoryPackSerializer.Serialize(config)
}).ContinueWith((result) =>
{
if (result.Result.Code == MessageResponeCodes.OK)
{
List<TunnelTransportRouteLevelInfo> list = MemoryPackSerializer.Deserialize<List<TunnelTransportRouteLevelInfo>>(result.Result.Data.Span);
foreach (var item in list)
{
Config.AddOrUpdate(item.MachineId, item, (a, b) => item);
}
TunnelTransportRouteLevelInfo config = GetLocalRouteLevel();
Config.AddOrUpdate(config.MachineId, config, (a, b) => config);
Version.Add();
}
});
GetData();
DataVersion.Add();
}
private TunnelTransportRouteLevelInfo GetLocalRouteLevel()
{
@@ -173,5 +173,7 @@ namespace linker.plugins.tunnel
}
}
}
}

View File

@@ -95,15 +95,6 @@ namespace linker.plugins.tunnel.messenger
tunnelConfigTransfer.OnLocalRouteLevel(tunnelTransportFileConfigInfo);
}
[MessengerId((ushort)TunnelMessengerIds.Config)]
public void Config(IConnection connection)
{
TunnelTransportRouteLevelInfo tunnelTransportFileConfigInfo = MemoryPackSerializer.Deserialize<TunnelTransportRouteLevelInfo>(connection.ReceiveRequestWrap.Payload.Span);
TunnelTransportRouteLevelInfo result = tunnelConfigTransfer.OnRemoteRouteLevel(tunnelTransportFileConfigInfo);
connection.Write(MemoryPackSerializer.Serialize(result));
}
}
public sealed class TunnelServerMessenger : IMessenger
@@ -219,40 +210,6 @@ namespace linker.plugins.tunnel.messenger
}
[MessengerId((ushort)TunnelMessengerIds.ConfigForward)]
public void ConfigForward(IConnection connection)
{
TunnelTransportRouteLevelInfo tunnelTransportRouteLevelInfo = MemoryPackSerializer.Deserialize<TunnelTransportRouteLevelInfo>(connection.ReceiveRequestWrap.Payload.Span);
if (signCaching.TryGet(connection.Id, out SignCacheInfo cache))
{
uint requestid = connection.ReceiveRequestWrap.RequestId;
List<SignCacheInfo> caches = signCaching.Get(cache.GroupId);
List<Task<MessageResponeInfo>> tasks = new List<Task<MessageResponeInfo>>();
foreach (SignCacheInfo item in caches.Where(c => c.MachineId != connection.Id && c.Connected))
{
tasks.Add(messengerSender.SendReply(new MessageRequestWrap
{
Connection = item.Connection,
MessengerId = (ushort)TunnelMessengerIds.Config,
Timeout = 3000,
Payload = connection.ReceiveRequestWrap.Payload
}));
}
Task.WhenAll(tasks).ContinueWith(async (result) =>
{
List<TunnelTransportRouteLevelInfo> results = tasks.Where(c => c.Result.Code == MessageResponeCodes.OK).Select(c => MemoryPackSerializer.Deserialize<TunnelTransportRouteLevelInfo>(c.Result.Data.Span)).ToList();
await messengerSender.ReplyOnly(new MessageResponseWrap
{
Connection = connection,
Payload = MemoryPackSerializer.Serialize(results),
RequestId = requestid,
}, (ushort)TunnelMessengerIds.ConfigForward).ConfigureAwait(false);
});
}
}
}
[MemoryPackable]

View File

@@ -16,9 +16,6 @@
Success = 2007,
SuccessForward = 2008,
Config = 2009,
ConfigForward = 2010,
RouteLevel = 2011,
RouteLevelForward = 2012,

View File

@@ -10,11 +10,16 @@ using linker.plugins.messenger;
using linker.plugins.tuntap.config;
using linker.tun;
using linker.plugins.tuntap.lease;
using linker.plugins.decenter;
namespace linker.plugins.tuntap.client
{
public sealed class TuntapConfigTransfer
public sealed class TuntapConfigTransfer : IDecenter
{
public string Name => "tuntap";
public VersionManager DataVersion { get; } = new VersionManager();
private readonly IMessengerSender messengerSender;
private readonly ClientSignInState clientSignInState;
private readonly FileConfig config;
@@ -29,6 +34,8 @@ namespace linker.plugins.tuntap.client
private readonly ConcurrentDictionary<string, TuntapInfo> tuntapInfos = new ConcurrentDictionary<string, TuntapInfo>();
public ConcurrentDictionary<string, TuntapInfo> Infos => tuntapInfos;
private readonly SemaphoreSlim slim = new SemaphoreSlim(1);
public TuntapConfigTransfer(IMessengerSender messengerSender, ClientSignInState clientSignInState, FileConfig config, TuntapProxy tuntapProxy, RunningConfig runningConfig, TuntapTransfer tuntapTransfer, LeaseClientTreansfer leaseClientTreansfer)
{
@@ -43,13 +50,13 @@ namespace linker.plugins.tuntap.client
clientSignInState.NetworkEnabledHandle += (times) => RefreshIP();
tuntapTransfer.OnSetupBefore += () => { NotifyConfig(); };
tuntapTransfer.OnSetupAfter += () => { NotifyConfig(); };
tuntapTransfer.OnSetupSuccess += () => { NotifyConfig(); runningConfig.Data.Tuntap.Running = true; runningConfig.Data.Update(); };
tuntapTransfer.OnSetupBefore += () => { DataVersion.Add(); };
tuntapTransfer.OnSetupAfter += () => { DataVersion.Add(); };
tuntapTransfer.OnSetupSuccess += () => { DataVersion.Add(); runningConfig.Data.Tuntap.Running = true; runningConfig.Data.Update(); };
tuntapTransfer.OnShutdownBefore += () => { NotifyConfig(); };
tuntapTransfer.OnShutdownAfter += () => { NotifyConfig(); };
tuntapTransfer.OnShutdownSuccess += () => { NotifyConfig(); DeleteForward(); DelRoute(); runningConfig.Data.Tuntap.Running = false; runningConfig.Data.Update(); };
tuntapTransfer.OnShutdownBefore += () => { DataVersion.Add(); };
tuntapTransfer.OnShutdownAfter += () => { DataVersion.Add(); };
tuntapTransfer.OnShutdownSuccess += () => { DataVersion.Add(); DeleteForward(); DelRoute(); runningConfig.Data.Tuntap.Running = false; runningConfig.Data.Update(); DataVersion.Add(); };
InitConfig();
@@ -61,6 +68,138 @@ namespace linker.plugins.tuntap.client
runningConfig.Data.Tuntap.Lans = runningConfig.Data.Tuntap.LanIPs.Select((a, b) => new TuntapLanInfo { IP = a, PrefixLength = (byte)runningConfig.Data.Tuntap.Masks[b] }).ToList();
}
}
/// <summary>
/// 更新本机网卡信息
/// </summary>
/// <param name="info"></param>
public void UpdateConfig(TuntapInfo info)
{
TimerHelper.Async(async () =>
{
DeleteForward();
IPAddress oldIP = runningConfig.Data.Tuntap.IP;
byte prefixLength = runningConfig.Data.Tuntap.PrefixLength;
runningConfig.Data.Tuntap.IP = info.IP;
runningConfig.Data.Tuntap.Lans = info.Lans;
runningConfig.Data.Tuntap.PrefixLength = info.PrefixLength;
runningConfig.Data.Tuntap.Switch = info.Switch;
runningConfig.Data.Tuntap.Forwards = info.Forwards;
runningConfig.Data.Update();
TuntapGroup2IPInfo tuntapGroup2IPInfo = new TuntapGroup2IPInfo { IP = runningConfig.Data.Tuntap.IP, PrefixLength = runningConfig.Data.Tuntap.PrefixLength };
runningConfig.Data.Tuntap.Group2IP.AddOrUpdate(config.Data.Client.Group.Id, tuntapGroup2IPInfo, (a, b) => tuntapGroup2IPInfo);
await LeaseIP();
bool needReboot = ((oldIP.Equals(runningConfig.Data.Tuntap.IP) == false || prefixLength != runningConfig.Data.Tuntap.PrefixLength) && runningConfig.Data.Tuntap.Running)
|| (runningConfig.Data.Tuntap.Running && tuntapTransfer.Status != TuntapStatus.Running);
if (needReboot)
{
await RetstartDevice();
}
else
{
AddForward();
}
GetData();
DataVersion.Add();
});
}
public Memory<byte> GetData()
{
TuntapInfo info = new TuntapInfo
{
IP = runningConfig.Data.Tuntap.IP,
Lans = runningConfig.Data.Tuntap.Lans.Select(c => { c.Exists = false; return c; }).ToList(),
PrefixLength = runningConfig.Data.Tuntap.PrefixLength,
MachineId = config.Data.Client.Id,
Status = tuntapTransfer.Status,
SetupError = tuntapTransfer.SetupError,
NatError = tuntapTransfer.NatError,
SystemInfo = $"{System.Runtime.InteropServices.RuntimeInformation.OSDescription} {(string.IsNullOrWhiteSpace(Environment.GetEnvironmentVariable("SNLTTY_LINKER_IS_DOCKER")) == false ? "Docker" : "")}",
Forwards = runningConfig.Data.Tuntap.Forwards,
Switch = runningConfig.Data.Tuntap.Switch
};
tuntapInfos.AddOrUpdate(info.MachineId, info, (a, b) => info);
Version.Add();
return MemoryPackSerializer.Serialize(info);
}
public void SetData(Memory<byte> data)
{
TuntapInfo info = MemoryPackSerializer.Deserialize<TuntapInfo>(data.Span);
if (LoggerHelper.Instance.LoggerLevel <= LoggerTypes.DEBUG)
{
LoggerHelper.Instance.Debug($"tuntap got {info.IP}");
}
TimerHelper.Async(async () =>
{
await slim.WaitAsync();
try
{
DelRoute();
tuntapInfos.AddOrUpdate(info.MachineId, info, (a, b) => info);
Version.Add();
AddRoute();
}
catch (Exception ex)
{
if (LoggerHelper.Instance.LoggerLevel <= LoggerTypes.DEBUG)
{
LoggerHelper.Instance.Error(ex);
}
}
slim.Release();
});
}
public void SetData(List<ReadOnlyMemory<byte>> data)
{
List<TuntapInfo> list = data.Select(c => MemoryPackSerializer.Deserialize<TuntapInfo>(c.Span)).ToList();
TimerHelper.Async(async () =>
{
await slim.WaitAsync();
try
{
DelRoute();
foreach (var item in list)
{
tuntapInfos.AddOrUpdate(item.MachineId, item, (a, b) => item);
item.LastTicks.Update();
}
var removes = tuntapInfos.Keys.Except(list.Select(c => c.MachineId)).ToList();
foreach (var item in removes)
{
if (tuntapInfos.TryGetValue(item, out TuntapInfo tuntapInfo))
{
tuntapInfo.Status = TuntapStatus.Normal;
tuntapInfo.LastTicks.Clear();
}
}
Version.Add();
AddRoute();
}
catch (Exception ex)
{
if (LoggerHelper.Instance.LoggerLevel <= LoggerTypes.DEBUG)
{
LoggerHelper.Instance.Error(ex);
}
}
finally
{
slim.Release();
}
});
}
/// <summary>
/// 刷新IP
@@ -86,7 +225,7 @@ namespace linker.plugins.tuntap.client
tuntapTransfer.Shutdown();
tuntapTransfer.Setup(runningConfig.Data.Tuntap.IP, runningConfig.Data.Tuntap.PrefixLength);
}
NotifyConfig();
DataVersion.Add();
});
}
@@ -121,13 +260,11 @@ namespace linker.plugins.tuntap.client
runningConfig.Data.Tuntap.PrefixLength = tuntapGroup2IPInfo.PrefixLength;
}
}
if (runningConfig.Data.Tuntap.Running || runningConfig.Data.Tuntap.IP.Equals(IPAddress.Any) == false)
{
LeaseInfo leaseInfo = await leaseClientTreansfer.LeaseIp(runningConfig.Data.Tuntap.IP, runningConfig.Data.Tuntap.PrefixLength);
runningConfig.Data.Tuntap.IP = leaseInfo.IP;
runningConfig.Data.Tuntap.PrefixLength = leaseInfo.PrefixLength;
runningConfig.Data.Update();
}
tuntapGroup2IPInfo = new TuntapGroup2IPInfo { IP = runningConfig.Data.Tuntap.IP, PrefixLength = runningConfig.Data.Tuntap.PrefixLength };
runningConfig.Data.Tuntap.Group2IP.AddOrUpdate(config.Data.Client.Group.Id, tuntapGroup2IPInfo, (a, b) => tuntapGroup2IPInfo);
}
@@ -137,175 +274,7 @@ namespace linker.plugins.tuntap.client
/// </summary>
public void RefreshConfig()
{
NotifyConfig();
}
/// <summary>
/// 更新本机网卡信息
/// </summary>
/// <param name="info"></param>
public void UpdateConfig(TuntapInfo info)
{
TimerHelper.Async(() =>
{
DeleteForward();
bool needReboot = info.IP.Equals(runningConfig.Data.Tuntap.IP) == false || info.PrefixLength != runningConfig.Data.Tuntap.PrefixLength;
runningConfig.Data.Tuntap.IP = info.IP;
runningConfig.Data.Tuntap.Lans = info.Lans;
runningConfig.Data.Tuntap.PrefixLength = info.PrefixLength;
runningConfig.Data.Tuntap.Switch = info.Switch;
runningConfig.Data.Tuntap.Forwards = info.Forwards;
TuntapGroup2IPInfo tuntapGroup2IPInfo = new TuntapGroup2IPInfo { IP = info.IP, PrefixLength = info.PrefixLength };
runningConfig.Data.Tuntap.Group2IP.AddOrUpdate(config.Data.Client.Group.Id, tuntapGroup2IPInfo, (a, b) => tuntapGroup2IPInfo);
runningConfig.Data.Update();
if (tuntapTransfer.Status == TuntapStatus.Running && needReboot)
{
tuntapTransfer.Shutdown();
tuntapTransfer.Setup(runningConfig.Data.Tuntap.IP, runningConfig.Data.Tuntap.PrefixLength);
}
else
{
AddForward();
NotifyConfig();
}
});
}
/// <summary>
/// 收到别的客户端的网卡信息
/// </summary>
/// <param name="info"></param>
/// <returns></returns>
public TuntapInfo OnConfig(TuntapInfo info)
{
if (LoggerHelper.Instance.LoggerLevel <= LoggerTypes.DEBUG)
{
LoggerHelper.Instance.Debug($"tuntap got {info.IP}");
}
TimerHelper.Async(async () =>
{
await slim.WaitAsync();
try
{
DelRoute();
tuntapInfos.AddOrUpdate(info.MachineId, info, (a, b) => info);
Version.Add();
AddRoute();
}
catch (Exception ex)
{
if (LoggerHelper.Instance.LoggerLevel <= LoggerTypes.DEBUG)
{
LoggerHelper.Instance.Error(ex);
}
}
slim.Release();
});
return GetLocalInfo();
}
/// <summary>
/// 信息有变化,刷新信息,把自己的网卡配置发给别人,顺便把别人的网卡信息带回来
/// </summary>
private void NotifyConfig()
{
TimerHelper.Async(async () =>
{
await slim.WaitAsync();
try
{
for (int i = 0; i < 5; i++)
{
List<TuntapInfo> list = await GetRemoteInfo().ConfigureAwait(false);
if (list != null)
{
DelRoute();
foreach (var item in list)
{
tuntapInfos.AddOrUpdate(item.MachineId, item, (a, b) => item);
item.LastTicks.Update();
}
var removes = tuntapInfos.Keys.Except(list.Select(c => c.MachineId)).ToList();
foreach (var item in removes)
{
if (tuntapInfos.TryGetValue(item, out TuntapInfo tuntapInfo))
{
tuntapInfo.Status = TuntapStatus.Normal;
tuntapInfo.LastTicks.Clear();
}
}
Version.Add();
AddRoute();
break;
}
await Task.Delay(1000);
}
}
catch (Exception ex)
{
if (LoggerHelper.Instance.LoggerLevel <= LoggerTypes.DEBUG)
{
LoggerHelper.Instance.Error(ex);
}
}
slim.Release();
});
}
/// <summary>
/// 获取自己的网卡信息
/// </summary>
/// <returns></returns>
private TuntapInfo GetLocalInfo()
{
TuntapInfo info = new TuntapInfo
{
IP = runningConfig.Data.Tuntap.IP,
Lans = runningConfig.Data.Tuntap.Lans.Where(c => c.Disabled == false && c.IP.Equals(IPAddress.Any) == false).Select(c => { c.Exists = false;return c; }).ToList(),
PrefixLength = runningConfig.Data.Tuntap.PrefixLength,
MachineId = config.Data.Client.Id,
Status = tuntapTransfer.Status,
SetupError = tuntapTransfer.SetupError,
NatError = tuntapTransfer.NatError,
SystemInfo = $"{System.Runtime.InteropServices.RuntimeInformation.OSDescription} {(string.IsNullOrWhiteSpace(Environment.GetEnvironmentVariable("SNLTTY_LINKER_IS_DOCKER")) == false ? "Docker" : "")}",
Forwards = runningConfig.Data.Tuntap.Forwards,
Switch = runningConfig.Data.Tuntap.Switch
};
return info;
}
/// <summary>
/// 获取别人的网卡信息
/// </summary>
/// <returns></returns>
private async Task<List<TuntapInfo>> GetRemoteInfo()
{
if (LoggerHelper.Instance.LoggerLevel <= LoggerTypes.DEBUG)
{
LoggerHelper.Instance.Debug($"tuntap sync");
}
TuntapInfo info = GetLocalInfo();
tuntapInfos.AddOrUpdate(info.MachineId, info, (a, b) => info);
MessageResponeInfo resp = await messengerSender.SendReply(new MessageRequestWrap
{
Connection = clientSignInState.Connection,
MessengerId = (ushort)TuntapMessengerIds.ConfigForward,
Payload = MemoryPackSerializer.Serialize(info),
Timeout = 3000
}).ConfigureAwait(false);
if (resp.Code != MessageResponeCodes.OK)
{
return null;
}
List<TuntapInfo> infos = MemoryPackSerializer.Deserialize<List<TuntapInfo>>(resp.Data.Span);
infos.Add(info);
return infos;
DataVersion.Add();
}
// <summary>
@@ -377,8 +346,9 @@ namespace linker.plugins.tuntap.client
.Select(c =>
{
Console.WriteLine($"{c.MachineId}->{config.Data.Client.Id}");
foreach (var lan in c.Lans)
var lans = c.Lans.Where(c => c.Disabled == false && c.IP.Equals(IPAddress.Any) == false);
foreach (var lan in lans)
{
uint ipInt = NetworkHelper.IP2Value(lan.IP);
uint maskValue = NetworkHelper.PrefixLength2Value(lan.PrefixLength);
@@ -389,7 +359,7 @@ namespace linker.plugins.tuntap.client
return new TuntapVeaLanIPAddressList
{
MachineId = c.MachineId,
IPS = ParseIPs(c.Lans.Where(c => c.Disabled == false && c.Exists == false).ToList(), c.MachineId)
IPS = ParseIPs(lans.Where(c => c.Disabled == false && c.Exists == false).ToList(), c.MachineId)
.Where(c => excludeIps.Select(d => d & c.MaskValue).Contains(c.NetWork) == false).ToList(),
};
}).ToList();
@@ -419,5 +389,6 @@ namespace linker.plugins.tuntap.client
};
}
}
}

View File

@@ -54,10 +54,12 @@ namespace linker.plugins.tuntap.client
}
protected override void OffLine(string machineId)
{
/*
foreach (var item in ip2MachineDic.Values)
{
item.Remove(machineId);
}
*/
}
/// <summary>

View File

@@ -101,9 +101,12 @@ namespace linker.plugins.tuntap.lease
cache.LastTime = DateTime.Now;
info.PrefixLength = NetworkHelper.PrefixValue2Length(cache.PrefixValue);
LeaseCacheUserInfo self = cache.Users.FirstOrDefault(c => c.Id == userId);
if (self != null)
//已有的
if (self != null && (info.IP.Equals(NetworkHelper.Value2IP(self.IP)) || info.IP.Equals(IPAddress.Any)))
{
self.LastTime = DateTime.Now;
info.IP = NetworkHelper.Value2IP(self.IP);
return info;
}

View File

@@ -54,18 +54,6 @@ namespace linker.plugins.tuntap.messenger
tuntapConfigTransfer.UpdateConfig(info);
}
/// <summary>
/// 收到别人的网卡信息
/// </summary>
/// <param name="connection"></param>
[MessengerId((ushort)TuntapMessengerIds.Config)]
public void Config(IConnection connection)
{
TuntapInfo info = MemoryPackSerializer.Deserialize<TuntapInfo>(connection.ReceiveRequestWrap.Payload.Span);
TuntapInfo _info = tuntapConfigTransfer.OnConfig(info);
connection.Write(MemoryPackSerializer.Serialize(_info));
}
/// <summary>
/// 重新租赁
/// </summary>
@@ -154,48 +142,6 @@ namespace linker.plugins.tuntap.messenger
}
}
/// <summary>
/// 广播网卡信息命令,把自己的发给所有人,然后拿回所有人的信息
/// </summary>
/// <param name="connection"></param>
[MessengerId((ushort)TuntapMessengerIds.ConfigForward)]
public void ConfigForward(IConnection connection)
{
TuntapInfo tuntapInfo = MemoryPackSerializer.Deserialize<TuntapInfo>(connection.ReceiveRequestWrap.Payload.Span);
if (signCaching.TryGet(connection.Id, out SignCacheInfo cache))
{
uint requiestid = connection.ReceiveRequestWrap.RequestId;
List<SignCacheInfo> caches = signCaching.Get(cache.GroupId);
List<Task<MessageResponeInfo>> tasks = new List<Task<MessageResponeInfo>>();
foreach (SignCacheInfo item in caches.Where(c => c.MachineId != connection.Id && c.Connected))
{
tasks.Add(messengerSender.SendReply(new MessageRequestWrap
{
Connection = item.Connection,
MessengerId = (ushort)TuntapMessengerIds.Config,
Payload = connection.ReceiveRequestWrap.Payload,
Timeout = 1000,
}));
}
Task.WhenAll(tasks).ContinueWith(async (result) =>
{
List<TuntapInfo> results = tasks.Where(c => c.Result.Code == MessageResponeCodes.OK)
.Select(c => MemoryPackSerializer.Deserialize<TuntapInfo>(c.Result.Data.Span)).ToList();
await messengerSender.ReplyOnly(new MessageResponseWrap
{
RequestId = requiestid,
Connection = connection,
Payload = MemoryPackSerializer.Serialize(results)
}, (ushort)TuntapMessengerIds.ConfigForward).ConfigureAwait(false);
});
}
}
/// <summary>
/// 添加网络配置
/// </summary>

View File

@@ -11,9 +11,6 @@
Update = 2204,
UpdateForward = 2205,
Config = 2206,
ConfigForward = 2207,
LeaseAddNetwork = 2208,
LeaseGetNetwork = 2209,

View File

@@ -1,5 +1,8 @@
v1.5.1
2024-10-24 17:27:23
2024-10-25 17:12:39
1. 优化点对网和网对网的局域网IP包括禁用IP和冲突检测
2. 一些UI优化
3. 测试中测试中测试中不要更新用v1.4.9
3. 新增socks5代理
4. 优化端口转发和内网穿透配置
5. 优化网卡的一些东西
6. 测试中测试中测试中不要更新用v1.4.9