mirror of
https://github.com/snltty/linker.git
synced 2025-10-18 23:14:53 +08:00
中继有BUG
This commit is contained in:
@@ -49,11 +49,29 @@ systemctl enable linker
|
||||
1. 服务端端口 `1802` TCP+UDP
|
||||
2. 客户端端口 `1804`、`1803` TCP
|
||||
3. 配置文件夹 `./configs`
|
||||
4. 插件文件夹 `./plugins`
|
||||
5. 网页文件夹 `./web`
|
||||
6. 日志文件夹 `./logs`
|
||||
4. 日志文件夹 `./logs`
|
||||
5. x64 `snltty/linker-debian-x64`
|
||||
6. arm64 `snltty/linker-debian-arm64`
|
||||
|
||||
#### 客户端
|
||||
```
|
||||
snltty/linker-musl-x64
|
||||
snltty/linker-musl-arm64
|
||||
docker run -it -d --name linker \
|
||||
-p 1804:1804/tcp -p 1803:1803/tcp \
|
||||
-v /usr/local/linker-docker/configs:/app/configs \
|
||||
-v /usr/local/linker-docker/logs:/app/logs \
|
||||
--device /dev/net/tun \
|
||||
--restart=always \
|
||||
--privileged=true \
|
||||
snltty/linker-debian-x64
|
||||
```
|
||||
|
||||
#### 服务端
|
||||
```
|
||||
docker run -it -d --name linker \
|
||||
-p 1802:1802/tcp -p 1802:1802/udp \
|
||||
-v /usr/local/linker-docker/configs:/app/configs \
|
||||
-v /usr/local/linker-docker/logs:/app/logs \
|
||||
--restart=always \
|
||||
--privileged=true \
|
||||
snltty/linker-debian-x64
|
||||
```
|
@@ -4,8 +4,8 @@ import { sendWebsocketMsg } from './request'
|
||||
export const getUpdater = () => {
|
||||
return sendWebsocketMsg('updaterclient/get');
|
||||
}
|
||||
export const confirm = (machineId, version) => {
|
||||
return sendWebsocketMsg('updaterclient/confirm', { machineId, version });
|
||||
export const confirm = (data) => {
|
||||
return sendWebsocketMsg('updaterclient/confirm', data);
|
||||
}
|
||||
export const exit = (machineId) => {
|
||||
return sendWebsocketMsg('updaterclient/exit', machineId);
|
||||
|
@@ -130,9 +130,13 @@ export default {
|
||||
if(updateInfo.Status == 2){
|
||||
|
||||
const selectedValue = ref(updaterVersion.value);
|
||||
const selectOptions = [h(ElOption, { label: `${updaterVersion.value} - 最新版本`, value: updaterVersion.value })];
|
||||
const selectOptions = [
|
||||
h(ElOption, { label: `仅[${row.MachineName}] -> ${updaterVersion.value}(最新版本)`, value: updaterVersion.value }),
|
||||
h(ElOption, { label: `[所有] -> ${updaterVersion.value}(最新版本)`, value: `all->${updaterVersion.value}` }),
|
||||
];
|
||||
if(row.Version != serverVersion.value && updaterVersion.value != serverVersion.value){
|
||||
selectOptions.push(h(ElOption, { label: `${serverVersion.value} - 服务器版本`, value: serverVersion.value }));
|
||||
selectOptions.push(h(ElOption, { label: `仅[${row.MachineName}] -> ${serverVersion.value}(服务器版本)`, value: serverVersion.value }));
|
||||
selectOptions.push(h(ElOption, { label: `[所有] -> ${serverVersion.value}(服务器版本)`, value: `all->${serverVersion.value}` }));
|
||||
}
|
||||
|
||||
ElMessageBox({
|
||||
@@ -148,7 +152,15 @@ export default {
|
||||
confirmButtonText: '确定',
|
||||
cancelButtonText: '取消'
|
||||
}).then(() => {
|
||||
confirm(row.MachineId,selectedValue.value);
|
||||
const data = {
|
||||
MachineId:row.MachineId,
|
||||
Version:selectedValue.value.replace('all->',''),
|
||||
All:selectedValue.value.indexOf('all->') >= 0
|
||||
};
|
||||
if(data.All){
|
||||
data.MachineId = '';
|
||||
}
|
||||
confirm(data);
|
||||
}).catch(() => {});
|
||||
}
|
||||
}
|
||||
|
@@ -14,7 +14,6 @@ export const provideUpdater = () => {
|
||||
const _getUpdater = () => {
|
||||
if (globalData.value.api.connected) {
|
||||
getUpdater().then((res) => {
|
||||
console.log(res);
|
||||
const self = Object.values(res).filter(c => !!c.Version)[0];
|
||||
if (self) {
|
||||
updater.value.current.DateTime = self.DateTime;
|
||||
|
@@ -1,5 +1,12 @@
|
||||
<template>
|
||||
<Version ckey="relayServers"/>
|
||||
<Version ckey="relayServers">
|
||||
<div>
|
||||
<el-radio-group v-model="state.byRelay" size="small" style="vertical-align:bottom" @change="handleByRelayChange">
|
||||
<el-radio-button label="延迟优先" :value="true" />
|
||||
<el-radio-button label="顺序优先" :value="false" />
|
||||
</el-radio-group>
|
||||
</div>
|
||||
</Version>
|
||||
<el-table :data="state.list" border size="small" width="100%" :height="`${state.height}px`" @cell-dblclick="handleCellClick">
|
||||
<el-table-column prop="Name" label="名称" width="100">
|
||||
<template #default="scope">
|
||||
@@ -94,12 +101,14 @@ export default {
|
||||
const globalData = injectGlobalData();
|
||||
const state = reactive({
|
||||
list:globalData.value.config.Running.Relay.Servers.sort((a,b)=>a.Disabled - b.Disabled),
|
||||
byRelay:globalData.value.config.Running.Relay.ByRelay,
|
||||
types:[],
|
||||
height: computed(()=>globalData.value.height-127)
|
||||
});
|
||||
watch(()=>globalData.value.config.Running.Relay.Servers,()=>{
|
||||
if(state.list.filter(c=>c['__editing']).length == 0){
|
||||
state.list = globalData.value.config.Running.Relay.Servers.sort((a,b)=>a.Disabled - b.Disabled);
|
||||
state.byRelay = globalData.value.config.Running.Relay.ByRelay;
|
||||
}
|
||||
})
|
||||
|
||||
@@ -151,9 +160,15 @@ export default {
|
||||
handleSave(state.list);
|
||||
}
|
||||
|
||||
const handleByRelayChange = ()=>{
|
||||
handleSave();
|
||||
}
|
||||
const handleSave = ()=>{
|
||||
state.list = state.list.slice().sort((a,b)=>a.Disabled - b.Disabled);
|
||||
setRelayServers(state.list).then(()=>{
|
||||
setRelayServers({
|
||||
servers:state.list,
|
||||
byRelay:state.byRelay
|
||||
}).then(()=>{
|
||||
ElMessage.success('已操作');
|
||||
}).catch(()=>{
|
||||
ElMessage.success('操作失败');
|
||||
@@ -164,7 +179,7 @@ export default {
|
||||
_getRelayTypes();
|
||||
});
|
||||
|
||||
return {state,handleCellClick,handleEditBlur,handleDel,handleAdd,handleSort}
|
||||
return {state,handleCellClick,handleEditBlur,handleDel,handleAdd,handleSort,handleByRelayChange}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
@@ -1,8 +1,10 @@
|
||||
<template>
|
||||
<div class="running-version-wrap">
|
||||
<div class="running-version-wrap flex">
|
||||
<span>配置版本 : {{version || 1}}</span>
|
||||
<el-button size="small" @click=handleEdit>手动修改版本</el-button>
|
||||
<span>高版本一端自动同步到低版本一端</span>
|
||||
<span class="flex-1"></span>
|
||||
<slot></slot>
|
||||
</div>
|
||||
</template>
|
||||
<script>
|
||||
|
@@ -27,5 +27,6 @@ COPY . .
|
||||
RUN chmod +x ./linker \
|
||||
&& chmod +x ./plugins/tuntap/tun2socks
|
||||
|
||||
ENV LINKER_IS_DOCKER="linker"
|
||||
|
||||
ENTRYPOINT ["./linker"]
|
@@ -2,11 +2,10 @@ FROM alpine:latest
|
||||
|
||||
ENV TZ=Asia/Shanghai DOTNET_SYSTEM_GLOBALIZATION_INVARIANT=1
|
||||
RUN echo "https://mirrors.ustc.edu.cn/alpine/latest-stable/main/" > /etc/apk/repositories \
|
||||
&& apk add --no-cache libstdc++ libintl tzdata zeromq bash \
|
||||
&& apk add --no-cache libstdc++ libintl tzdata zeromq bash net-tools iproute2 \
|
||||
&& ln -snf /usr/share/zoneinfo/$clTZ /etc/localtime \
|
||||
&& echo $TZ > /etc/timezone
|
||||
|
||||
|
||||
EXPOSE 1802/tcp
|
||||
EXPOSE 1802/udp
|
||||
EXPOSE 1803/tcp
|
||||
@@ -20,5 +19,7 @@ COPY . .
|
||||
RUN chmod +x ./linker \
|
||||
&& chmod +x ./plugins/tuntap/tun2socks
|
||||
|
||||
RUN mkdir -p /dev/net && mknod /dev/net/tun c 10 200 && chmod 600 /dev/net/tun
|
||||
|
||||
|
||||
ENTRYPOINT ["./linker"]
|
@@ -6,6 +6,7 @@ using MemoryPack;
|
||||
using linker.plugins.client;
|
||||
using linker.plugins.capi;
|
||||
using linker.plugins.messenger;
|
||||
using linker.client.config;
|
||||
|
||||
namespace linker.plugins.relay
|
||||
{
|
||||
@@ -42,7 +43,7 @@ namespace linker.plugins.relay
|
||||
/// <returns></returns>
|
||||
public bool SetServers(ApiControllerParamsInfo param)
|
||||
{
|
||||
RelayServerInfo[] info = param.Content.DeJson<RelayServerInfo[]>();
|
||||
RelayRunningSyncInfo info = param.Content.DeJson<RelayRunningSyncInfo>();
|
||||
relayTransfer.OnServers(info);
|
||||
return true;
|
||||
}
|
||||
|
@@ -84,9 +84,10 @@ namespace linker.plugins.relay
|
||||
/// 收到中继协议列表
|
||||
/// </summary>
|
||||
/// <param name="servers"></param>
|
||||
public void OnServers(RelayServerInfo[] servers)
|
||||
public void OnServers(RelayRunningSyncInfo info)
|
||||
{
|
||||
running.Data.Relay.Servers = servers;
|
||||
running.Data.Relay.Servers = info.Servers;
|
||||
running.Data.Relay.ByRelay = info.ByRelay;
|
||||
running.Data.Update();
|
||||
runningConfigTransfer.IncrementVersion(configKey);
|
||||
SyncServers();
|
||||
@@ -94,13 +95,19 @@ namespace linker.plugins.relay
|
||||
}
|
||||
private void SetServers(Memory<byte> data)
|
||||
{
|
||||
running.Data.Relay.Servers = MemoryPackSerializer.Deserialize<RelayServerInfo[]>(data.Span);
|
||||
RelayRunningSyncInfo relayRunningSyncInfo = MemoryPackSerializer.Deserialize<RelayRunningSyncInfo>(data.Span);
|
||||
running.Data.Relay.Servers = relayRunningSyncInfo.Servers;
|
||||
running.Data.Relay.ByRelay = relayRunningSyncInfo.ByRelay;
|
||||
running.Data.Update();
|
||||
_ = TaskRelay();
|
||||
}
|
||||
private void SyncServers()
|
||||
{
|
||||
runningConfigTransfer.Sync(configKey, MemoryPackSerializer.Serialize(running.Data.Relay.Servers));
|
||||
runningConfigTransfer.Sync(configKey, MemoryPackSerializer.Serialize(new RelayRunningSyncInfo
|
||||
{
|
||||
Servers = running.Data.Relay.Servers,
|
||||
ByRelay = running.Data.Relay.ByRelay,
|
||||
}));
|
||||
}
|
||||
|
||||
|
||||
@@ -145,10 +152,13 @@ namespace linker.plugins.relay
|
||||
}
|
||||
try
|
||||
{
|
||||
IEnumerable<ITransport> _transports = transports.OrderBy(c => c.Type);
|
||||
foreach (RelayServerInfo item in running.Data.Relay.Servers.Where(c => c.Disabled == false && string.IsNullOrWhiteSpace(c.Host) == false))
|
||||
var servers = running.Data.Relay.Servers
|
||||
.Where(c => c.Disabled == false)
|
||||
.Where(c => string.IsNullOrWhiteSpace(c.Host) == false)
|
||||
.Where(c => c.Delay >= 0).OrderBy(c => c.Delay);
|
||||
foreach (RelayServerInfo item in servers)
|
||||
{
|
||||
ITransport transport = _transports.FirstOrDefault(c => c.Type == item.RelayType);
|
||||
ITransport transport = transports.FirstOrDefault(c => c.Type == item.RelayType);
|
||||
if (transport == null)
|
||||
{
|
||||
continue;
|
||||
@@ -218,6 +228,10 @@ namespace linker.plugins.relay
|
||||
LoggerHelper.Instance.Debug($"relay from {relayInfo.RemoteMachineId}->{relayInfo.RemoteMachineName} success,{relayInfo.ToJson()}");
|
||||
ConnectedCallback(relayInfo, connection);
|
||||
}
|
||||
else
|
||||
{
|
||||
LoggerHelper.Instance.Error($"relay from {relayInfo.RemoteMachineId}->{relayInfo.RemoteMachineName} error,{relayInfo.ToJson()}");
|
||||
}
|
||||
}).ConfigureAwait(false);
|
||||
return true;
|
||||
}
|
||||
|
@@ -21,6 +21,19 @@ namespace linker.client.config
|
||||
/// 中继服务器列表
|
||||
/// </summary>
|
||||
public RelayServerInfo[] Servers { get; set; } = Array.Empty<RelayServerInfo>();
|
||||
|
||||
public bool ByRelay { get; set; }
|
||||
}
|
||||
|
||||
[MemoryPackable]
|
||||
public sealed partial class RelayRunningSyncInfo
|
||||
{
|
||||
/// <summary>
|
||||
/// 中继服务器列表
|
||||
/// </summary>
|
||||
public RelayServerInfo[] Servers { get; set; } = Array.Empty<RelayServerInfo>();
|
||||
|
||||
public bool ByRelay { get; set; }
|
||||
}
|
||||
}
|
||||
|
||||
|
@@ -71,7 +71,6 @@ namespace linker.plugins.relay.transport
|
||||
return null;
|
||||
}
|
||||
connection.Cancel();
|
||||
ClearSocket(socket);
|
||||
|
||||
//通知对方,确认中继
|
||||
resp = await messengerSender.SendReply(new MessageRequestWrap
|
||||
@@ -85,6 +84,7 @@ namespace linker.plugins.relay.transport
|
||||
connection.Disponse(7);
|
||||
return null;
|
||||
}
|
||||
ClearSocket(socket);
|
||||
|
||||
SslStream sslStream = null;
|
||||
if (relayInfo.SSL)
|
||||
@@ -133,7 +133,6 @@ namespace linker.plugins.relay.transport
|
||||
Socket socket = new Socket(relayInfo.Server.AddressFamily, SocketType.Stream, System.Net.Sockets.ProtocolType.Tcp);
|
||||
socket.KeepAlive();
|
||||
await socket.ConnectAsync(relayInfo.Server).WaitAsync(TimeSpan.FromMilliseconds(500)).ConfigureAwait(false);
|
||||
|
||||
IConnection connection = await messengerResolver.BeginReceiveClient(socket);
|
||||
await messengerSender.SendOnly(new MessageRequestWrap
|
||||
{
|
||||
@@ -141,10 +140,9 @@ namespace linker.plugins.relay.transport
|
||||
MessengerId = (ushort)RelayMessengerIds.RelayConfirm,
|
||||
Payload = MemoryPackSerializer.Serialize(relayInfo)
|
||||
}).ConfigureAwait(false);
|
||||
|
||||
connection.Cancel();
|
||||
await Task.Delay(30).ConfigureAwait(false);
|
||||
ClearSocket(socket);
|
||||
|
||||
_ = WaitSSL(connection, socket, relayInfo).ContinueWith((result) =>
|
||||
{
|
||||
callback(result.Result);
|
||||
@@ -164,33 +162,46 @@ namespace linker.plugins.relay.transport
|
||||
|
||||
private async Task<TunnelConnectionTcp> WaitSSL(IConnection connection, Socket socket, RelayInfo relayInfo)
|
||||
{
|
||||
SslStream sslStream = null;
|
||||
if (relayInfo.SSL)
|
||||
try
|
||||
{
|
||||
if (certificate == null)
|
||||
SslStream sslStream = null;
|
||||
if (relayInfo.SSL)
|
||||
{
|
||||
connection.Disponse(8);
|
||||
return null;
|
||||
if (certificate == null)
|
||||
{
|
||||
connection.Disponse(8);
|
||||
return null;
|
||||
}
|
||||
sslStream = new SslStream(connection.SourceNetworkStream, false);
|
||||
Console.WriteLine(socket.Available);
|
||||
await sslStream.AuthenticateAsServerAsync(certificate, false, SslProtocols.Tls | SslProtocols.Tls11 | SslProtocols.Tls12 | SslProtocols.Tls13, false).ConfigureAwait(false);
|
||||
}
|
||||
sslStream = new SslStream(connection.SourceNetworkStream, false);
|
||||
await sslStream.AuthenticateAsServerAsync(certificate, false, SslProtocols.Tls | SslProtocols.Tls11 | SslProtocols.Tls12 | SslProtocols.Tls13, false).ConfigureAwait(false);
|
||||
return new TunnelConnectionTcp
|
||||
{
|
||||
Direction = TunnelDirection.Reverse,
|
||||
ProtocolType = TunnelProtocolType.Tcp,
|
||||
RemoteMachineId = relayInfo.RemoteMachineId,
|
||||
RemoteMachineName = relayInfo.RemoteMachineName,
|
||||
Stream = sslStream,
|
||||
Socket = socket,
|
||||
Mode = TunnelMode.Server,
|
||||
IPEndPoint = socket.RemoteEndPoint as IPEndPoint,
|
||||
TransactionId = relayInfo.TransactionId,
|
||||
TransportName = Name,
|
||||
Type = TunnelType.Relay,
|
||||
SSL = relayInfo.SSL,
|
||||
BufferSize = 3,
|
||||
};
|
||||
}
|
||||
return new TunnelConnectionTcp
|
||||
catch (Exception ex)
|
||||
{
|
||||
Direction = TunnelDirection.Reverse,
|
||||
ProtocolType = TunnelProtocolType.Tcp,
|
||||
RemoteMachineId = relayInfo.RemoteMachineId,
|
||||
RemoteMachineName = relayInfo.RemoteMachineName,
|
||||
Stream = sslStream,
|
||||
Socket = socket,
|
||||
Mode = TunnelMode.Server,
|
||||
IPEndPoint = socket.RemoteEndPoint as IPEndPoint,
|
||||
TransactionId = relayInfo.TransactionId,
|
||||
TransportName = Name,
|
||||
Type = TunnelType.Relay,
|
||||
SSL = relayInfo.SSL,
|
||||
BufferSize = 3,
|
||||
};
|
||||
if (LoggerHelper.Instance.LoggerLevel <= LoggerTypes.DEBUG)
|
||||
{
|
||||
LoggerHelper.Instance.Error(ex);
|
||||
}
|
||||
connection?.Disponse();
|
||||
}
|
||||
return null;
|
||||
}
|
||||
private void ClearSocket(Socket socket)
|
||||
{
|
||||
|
@@ -43,7 +43,7 @@ namespace linker.plugins.updater
|
||||
public UpdateInfo GetCurrent(ApiControllerParamsInfo param)
|
||||
{
|
||||
var updaters = updaterTransfer.Get();
|
||||
if(updaters.TryGetValue(config.Data.Client.Id,out UpdateInfo info))
|
||||
if (updaters.TryGetValue(config.Data.Client.Id, out UpdateInfo info))
|
||||
{
|
||||
return info;
|
||||
}
|
||||
@@ -90,11 +90,7 @@ namespace linker.plugins.updater
|
||||
{
|
||||
UpdaterConfirmInfo confirm = param.Content.DeJson<UpdaterConfirmInfo>();
|
||||
|
||||
if (string.IsNullOrWhiteSpace(confirm.MachineId) || confirm.MachineId == config.Data.Client.Id)
|
||||
{
|
||||
updaterTransfer.Confirm(confirm.Version);
|
||||
}
|
||||
else
|
||||
if (confirm.All)
|
||||
{
|
||||
await messengerSender.SendOnly(new MessageRequestWrap
|
||||
{
|
||||
@@ -102,6 +98,11 @@ namespace linker.plugins.updater
|
||||
MessengerId = (ushort)UpdaterMessengerIds.ConfirmForward,
|
||||
Payload = MemoryPackSerializer.Serialize(confirm)
|
||||
});
|
||||
updaterTransfer.Confirm(confirm.Version);
|
||||
}
|
||||
else if (confirm.MachineId == config.Data.Client.Id)
|
||||
{
|
||||
updaterTransfer.Confirm(confirm.Version);
|
||||
}
|
||||
}
|
||||
public async Task Exit(ApiControllerParamsInfo param)
|
||||
|
@@ -21,6 +21,7 @@ namespace linker.plugins.updater.config
|
||||
{
|
||||
public string MachineId { get; set; }
|
||||
public string Version { get; set; }
|
||||
public bool All { get; set; }
|
||||
}
|
||||
|
||||
[MemoryPackable]
|
||||
|
@@ -79,7 +79,7 @@ namespace linker.plugins.updater.messenger
|
||||
public void ConfirmServer(IConnection connection)
|
||||
{
|
||||
UpdaterConfirmServerInfo confirm = MemoryPackSerializer.Deserialize<UpdaterConfirmServerInfo>(connection.ReceiveRequestWrap.Payload.Span);
|
||||
if(fileConfig.Data.Server.Updater.SecretKey == confirm.SecretKey)
|
||||
if (fileConfig.Data.Server.Updater.SecretKey == confirm.SecretKey)
|
||||
{
|
||||
updaterServerTransfer.Confirm(confirm.Version);
|
||||
}
|
||||
@@ -108,7 +108,26 @@ namespace linker.plugins.updater.messenger
|
||||
public async Task ConfirmForward(IConnection connection)
|
||||
{
|
||||
UpdaterConfirmInfo confirm = MemoryPackSerializer.Deserialize<UpdaterConfirmInfo>(connection.ReceiveRequestWrap.Payload.Span);
|
||||
if (signCaching.TryGet(connection.Id, out SignCacheInfo cache) && signCaching.TryGet(confirm.MachineId, out SignCacheInfo cache1) && cache.GroupId == cache1.GroupId)
|
||||
if (signCaching.TryGet(connection.Id, out SignCacheInfo cache) == false)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (confirm.All)
|
||||
{
|
||||
var tasks = signCaching.Get(cache.GroupId).Where(c => c.MachineId != connection.Id).Select(c =>
|
||||
{
|
||||
return messengerSender.SendOnly(new MessageRequestWrap
|
||||
{
|
||||
Connection = c.Connection,
|
||||
MessengerId = (ushort)UpdaterMessengerIds.Confirm,
|
||||
Payload = connection.ReceiveRequestWrap.Payload
|
||||
});
|
||||
});
|
||||
|
||||
await Task.WhenAll(tasks);
|
||||
}
|
||||
else if (signCaching.TryGet(confirm.MachineId, out SignCacheInfo cache1) && cache.GroupId == cache1.GroupId)
|
||||
{
|
||||
await messengerSender.SendOnly(new MessageRequestWrap
|
||||
{
|
||||
|
Reference in New Issue
Block a user