mirror of
https://github.com/snltty/linker.git
synced 2025-10-06 17:46:59 +08:00
一些常规优化
This commit is contained in:
8
.github/workflows/docker.yml
vendored
8
.github/workflows/docker.yml
vendored
@@ -57,9 +57,9 @@ jobs:
|
|||||||
docker tag snltty/linker-musl-x64:latest snltty/linker-musl:amd64 && \
|
docker tag snltty/linker-musl-x64:latest snltty/linker-musl:amd64 && \
|
||||||
docker push snltty/linker-musl:amd64 && \
|
docker push snltty/linker-musl:amd64 && \
|
||||||
docker manifest create snltty/linker-musl:latest snltty/linker-musl:amd64 snltty/linker-musl:arm64 snltty/linker-musl:arm && \
|
docker manifest create snltty/linker-musl:latest snltty/linker-musl:amd64 snltty/linker-musl:arm64 snltty/linker-musl:arm && \
|
||||||
docker manifest create snltty/linker-musl:v1.5.9 snltty/linker-musl:amd64 snltty/linker-musl:arm64 snltty/linker-musl:arm && \
|
docker manifest create snltty/linker-musl:v1.6.0 snltty/linker-musl:amd64 snltty/linker-musl:arm64 snltty/linker-musl:arm && \
|
||||||
docker manifest push snltty/linker-musl:latest && \
|
docker manifest push snltty/linker-musl:latest && \
|
||||||
docker manifest push snltty/linker-musl:v1.5.9 && \
|
docker manifest push snltty/linker-musl:v1.6.0 && \
|
||||||
docker pull --platform linux/arm/v7 snltty/linker-debian-arm:latest && \
|
docker pull --platform linux/arm/v7 snltty/linker-debian-arm:latest && \
|
||||||
docker tag snltty/linker-debian-arm:latest snltty/linker-debian:arm && \
|
docker tag snltty/linker-debian-arm:latest snltty/linker-debian:arm && \
|
||||||
docker push snltty/linker-debian:arm && \
|
docker push snltty/linker-debian:arm && \
|
||||||
@@ -70,6 +70,6 @@ jobs:
|
|||||||
docker tag snltty/linker-debian-x64:latest snltty/linker-debian:amd64 && \
|
docker tag snltty/linker-debian-x64:latest snltty/linker-debian:amd64 && \
|
||||||
docker push snltty/linker-debian:amd64 && \
|
docker push snltty/linker-debian:amd64 && \
|
||||||
docker manifest create snltty/linker-debian:latest snltty/linker-debian:amd64 snltty/linker-debian:arm64 snltty/linker-debian:arm && \
|
docker manifest create snltty/linker-debian:latest snltty/linker-debian:amd64 snltty/linker-debian:arm64 snltty/linker-debian:arm && \
|
||||||
docker manifest create snltty/linker-debian:v1.5.9 snltty/linker-debian:amd64 snltty/linker-debian:arm64 snltty/linker-debian:arm && \
|
docker manifest create snltty/linker-debian:v1.6.0 snltty/linker-debian:amd64 snltty/linker-debian:arm64 snltty/linker-debian:arm && \
|
||||||
docker manifest push snltty/linker-debian:latest && \
|
docker manifest push snltty/linker-debian:latest && \
|
||||||
docker manifest push snltty/linker-debian:v1.5.9
|
docker manifest push snltty/linker-debian:v1.6.0
|
30
.github/workflows/dotnet.yml
vendored
30
.github/workflows/dotnet.yml
vendored
@@ -33,11 +33,11 @@ jobs:
|
|||||||
env:
|
env:
|
||||||
GITHUB_TOKEN: ${{ secrets.ACTIONS_TOKEN }}
|
GITHUB_TOKEN: ${{ secrets.ACTIONS_TOKEN }}
|
||||||
with:
|
with:
|
||||||
tag_name: v1.5.9
|
tag_name: v1.6.0
|
||||||
release_name: v1.5.9.${{ steps.date.outputs.today }}
|
release_name: v1.6.0.${{ steps.date.outputs.today }}
|
||||||
draft: false
|
draft: false
|
||||||
prerelease: false
|
prerelease: false
|
||||||
body: "1. 优化UI\r\n2. 优化网卡\r\n3. 修复网卡禁用自动重启\r\n4. 增加一键安装脚本"
|
body: "1. 优化websocket(某些代理环境下可能请求头key名称不规范)\r\n2. 尝试解决UDP获取外网IP端口失败的问题\r\n3. 测试中"
|
||||||
- name: upload-win-x86-oss
|
- name: upload-win-x86-oss
|
||||||
id: upload-win-x86-oss
|
id: upload-win-x86-oss
|
||||||
uses: tvrcgo/oss-action@v0.1.1
|
uses: tvrcgo/oss-action@v0.1.1
|
||||||
@@ -47,7 +47,7 @@ jobs:
|
|||||||
key-secret: ${{ secrets.ALIYUN_OSS_SECRET }}
|
key-secret: ${{ secrets.ALIYUN_OSS_SECRET }}
|
||||||
bucket: ide-qbcode
|
bucket: ide-qbcode
|
||||||
asset-path: ./public/publish-zip/linker-win-x86.zip
|
asset-path: ./public/publish-zip/linker-win-x86.zip
|
||||||
target-path: /downloads/linker/v1.5.9/linker-win-x86.zip
|
target-path: /downloads/linker/v1.6.0/linker-win-x86.zip
|
||||||
- name: upload-win-x86
|
- name: upload-win-x86
|
||||||
id: upload-win-x86
|
id: upload-win-x86
|
||||||
uses: actions/upload-release-asset@master
|
uses: actions/upload-release-asset@master
|
||||||
@@ -67,7 +67,7 @@ jobs:
|
|||||||
key-secret: ${{ secrets.ALIYUN_OSS_SECRET }}
|
key-secret: ${{ secrets.ALIYUN_OSS_SECRET }}
|
||||||
bucket: ide-qbcode
|
bucket: ide-qbcode
|
||||||
asset-path: ./public/publish-zip/linker-win-x64.zip
|
asset-path: ./public/publish-zip/linker-win-x64.zip
|
||||||
target-path: /downloads/linker/v1.5.9/linker-win-x64.zip
|
target-path: /downloads/linker/v1.6.0/linker-win-x64.zip
|
||||||
- name: upload-win-x64
|
- name: upload-win-x64
|
||||||
id: upload-win-x64
|
id: upload-win-x64
|
||||||
uses: actions/upload-release-asset@master
|
uses: actions/upload-release-asset@master
|
||||||
@@ -87,7 +87,7 @@ jobs:
|
|||||||
key-secret: ${{ secrets.ALIYUN_OSS_SECRET }}
|
key-secret: ${{ secrets.ALIYUN_OSS_SECRET }}
|
||||||
bucket: ide-qbcode
|
bucket: ide-qbcode
|
||||||
asset-path: ./public/publish-zip/linker-win-arm64.zip
|
asset-path: ./public/publish-zip/linker-win-arm64.zip
|
||||||
target-path: /downloads/linker/v1.5.9/linker-win-arm64.zip
|
target-path: /downloads/linker/v1.6.0/linker-win-arm64.zip
|
||||||
- name: upload-win-arm64
|
- name: upload-win-arm64
|
||||||
id: upload-win-arm64
|
id: upload-win-arm64
|
||||||
uses: actions/upload-release-asset@master
|
uses: actions/upload-release-asset@master
|
||||||
@@ -107,7 +107,7 @@ jobs:
|
|||||||
key-secret: ${{ secrets.ALIYUN_OSS_SECRET }}
|
key-secret: ${{ secrets.ALIYUN_OSS_SECRET }}
|
||||||
bucket: ide-qbcode
|
bucket: ide-qbcode
|
||||||
asset-path: ./public/publish-zip/linker-linux-x64.zip
|
asset-path: ./public/publish-zip/linker-linux-x64.zip
|
||||||
target-path: /downloads/linker/v1.5.9/linker-linux-x64.zip
|
target-path: /downloads/linker/v1.6.0/linker-linux-x64.zip
|
||||||
- name: upload-linux-x64
|
- name: upload-linux-x64
|
||||||
id: upload-linux-x64
|
id: upload-linux-x64
|
||||||
uses: actions/upload-release-asset@master
|
uses: actions/upload-release-asset@master
|
||||||
@@ -127,7 +127,7 @@ jobs:
|
|||||||
key-secret: ${{ secrets.ALIYUN_OSS_SECRET }}
|
key-secret: ${{ secrets.ALIYUN_OSS_SECRET }}
|
||||||
bucket: ide-qbcode
|
bucket: ide-qbcode
|
||||||
asset-path: ./public/publish-zip/linker-linux-arm.zip
|
asset-path: ./public/publish-zip/linker-linux-arm.zip
|
||||||
target-path: /downloads/linker/v1.5.9/linker-linux-arm.zip
|
target-path: /downloads/linker/v1.6.0/linker-linux-arm.zip
|
||||||
- name: upload-linux-arm
|
- name: upload-linux-arm
|
||||||
id: upload-linux-arm
|
id: upload-linux-arm
|
||||||
uses: actions/upload-release-asset@master
|
uses: actions/upload-release-asset@master
|
||||||
@@ -147,7 +147,7 @@ jobs:
|
|||||||
key-secret: ${{ secrets.ALIYUN_OSS_SECRET }}
|
key-secret: ${{ secrets.ALIYUN_OSS_SECRET }}
|
||||||
bucket: ide-qbcode
|
bucket: ide-qbcode
|
||||||
asset-path: ./public/publish-zip/linker-linux-arm64.zip
|
asset-path: ./public/publish-zip/linker-linux-arm64.zip
|
||||||
target-path: /downloads/linker/v1.5.9/linker-linux-arm64.zip
|
target-path: /downloads/linker/v1.6.0/linker-linux-arm64.zip
|
||||||
- name: upload-linux-arm64
|
- name: upload-linux-arm64
|
||||||
id: upload-linux-arm64
|
id: upload-linux-arm64
|
||||||
uses: actions/upload-release-asset@master
|
uses: actions/upload-release-asset@master
|
||||||
@@ -167,7 +167,7 @@ jobs:
|
|||||||
key-secret: ${{ secrets.ALIYUN_OSS_SECRET }}
|
key-secret: ${{ secrets.ALIYUN_OSS_SECRET }}
|
||||||
bucket: ide-qbcode
|
bucket: ide-qbcode
|
||||||
asset-path: ./public/publish-zip/linker-linux-musl-x64.zip
|
asset-path: ./public/publish-zip/linker-linux-musl-x64.zip
|
||||||
target-path: /downloads/linker/v1.5.9/linker-linux-musl-x64.zip
|
target-path: /downloads/linker/v1.6.0/linker-linux-musl-x64.zip
|
||||||
- name: upload-linux-musl-x64
|
- name: upload-linux-musl-x64
|
||||||
id: upload-linux-musl-x64
|
id: upload-linux-musl-x64
|
||||||
uses: actions/upload-release-asset@master
|
uses: actions/upload-release-asset@master
|
||||||
@@ -187,7 +187,7 @@ jobs:
|
|||||||
key-secret: ${{ secrets.ALIYUN_OSS_SECRET }}
|
key-secret: ${{ secrets.ALIYUN_OSS_SECRET }}
|
||||||
bucket: ide-qbcode
|
bucket: ide-qbcode
|
||||||
asset-path: ./public/publish-zip/linker-linux-musl-arm.zip
|
asset-path: ./public/publish-zip/linker-linux-musl-arm.zip
|
||||||
target-path: /downloads/linker/v1.5.9/linker-linux-musl-arm.zip
|
target-path: /downloads/linker/v1.6.0/linker-linux-musl-arm.zip
|
||||||
- name: upload-linux-musl-arm
|
- name: upload-linux-musl-arm
|
||||||
id: upload-linux-musl-arm
|
id: upload-linux-musl-arm
|
||||||
uses: actions/upload-release-asset@master
|
uses: actions/upload-release-asset@master
|
||||||
@@ -207,7 +207,7 @@ jobs:
|
|||||||
key-secret: ${{ secrets.ALIYUN_OSS_SECRET }}
|
key-secret: ${{ secrets.ALIYUN_OSS_SECRET }}
|
||||||
bucket: ide-qbcode
|
bucket: ide-qbcode
|
||||||
asset-path: ./public/publish-zip/linker-linux-musl-arm64.zip
|
asset-path: ./public/publish-zip/linker-linux-musl-arm64.zip
|
||||||
target-path: /downloads/linker/v1.5.9/linker-linux-musl-arm64.zip
|
target-path: /downloads/linker/v1.6.0/linker-linux-musl-arm64.zip
|
||||||
- name: upload-linux-musl-arm64
|
- name: upload-linux-musl-arm64
|
||||||
id: upload-linux-musl-arm64
|
id: upload-linux-musl-arm64
|
||||||
uses: actions/upload-release-asset@master
|
uses: actions/upload-release-asset@master
|
||||||
@@ -227,7 +227,7 @@ jobs:
|
|||||||
key-secret: ${{ secrets.ALIYUN_OSS_SECRET }}
|
key-secret: ${{ secrets.ALIYUN_OSS_SECRET }}
|
||||||
bucket: ide-qbcode
|
bucket: ide-qbcode
|
||||||
asset-path: ./public/publish-zip/linker-osx-x64.zip
|
asset-path: ./public/publish-zip/linker-osx-x64.zip
|
||||||
target-path: /downloads/linker/v1.5.9/linker-osx-x64.zip
|
target-path: /downloads/linker/v1.6.0/linker-osx-x64.zip
|
||||||
- name: upload-osx-x64
|
- name: upload-osx-x64
|
||||||
id: upload-osx-x64
|
id: upload-osx-x64
|
||||||
uses: actions/upload-release-asset@master
|
uses: actions/upload-release-asset@master
|
||||||
@@ -247,7 +247,7 @@ jobs:
|
|||||||
key-secret: ${{ secrets.ALIYUN_OSS_SECRET }}
|
key-secret: ${{ secrets.ALIYUN_OSS_SECRET }}
|
||||||
bucket: ide-qbcode
|
bucket: ide-qbcode
|
||||||
asset-path: ./public/publish-zip/linker-osx-arm64.zip
|
asset-path: ./public/publish-zip/linker-osx-arm64.zip
|
||||||
target-path: /downloads/linker/v1.5.9/linker-osx-arm64.zip
|
target-path: /downloads/linker/v1.6.0/linker-osx-arm64.zip
|
||||||
- name: upload-osx-arm64
|
- name: upload-osx-arm64
|
||||||
id: upload-osx-arm64
|
id: upload-osx-arm64
|
||||||
uses: actions/upload-release-asset@master
|
uses: actions/upload-release-asset@master
|
||||||
@@ -277,7 +277,7 @@ jobs:
|
|||||||
key-secret: ${{ secrets.ALIYUN_OSS_SECRET }}
|
key-secret: ${{ secrets.ALIYUN_OSS_SECRET }}
|
||||||
bucket: ide-qbcode
|
bucket: ide-qbcode
|
||||||
asset-path: ./public/publish-zip/linker-windows-route.zip
|
asset-path: ./public/publish-zip/linker-windows-route.zip
|
||||||
target-path: /downloads/linker/v1.5.9/linker-windows-route.zip
|
target-path: /downloads/linker/v1.6.0/linker-windows-route.zip
|
||||||
- name: upload-version-oss
|
- name: upload-version-oss
|
||||||
id: upload-version-oss
|
id: upload-version-oss
|
||||||
uses: tvrcgo/oss-action@v0.1.1
|
uses: tvrcgo/oss-action@v0.1.1
|
||||||
|
6
.github/workflows/nuget.yml
vendored
6
.github/workflows/nuget.yml
vendored
@@ -38,6 +38,6 @@ jobs:
|
|||||||
|
|
||||||
- name: Push
|
- name: Push
|
||||||
run: |
|
run: |
|
||||||
nuget push ./linker.tunnel/bin/release/linker.tunnel.1.5.9.nupkg -Source https://api.nuget.org/v3/index.json -SkipDuplicate -ApiKey ${{ secrets.NUGET_KEY }} -NoSymbol
|
nuget push ./linker.tunnel/bin/release/linker.tunnel.1.6.0.nupkg -Source https://api.nuget.org/v3/index.json -SkipDuplicate -ApiKey ${{ secrets.NUGET_KEY }} -NoSymbol
|
||||||
nuget push ./linker.libs/bin/release/linker.libs.1.5.9.nupkg -Source https://api.nuget.org/v3/index.json -SkipDuplicate -ApiKey ${{ secrets.NUGET_KEY }} -NoSymbol
|
nuget push ./linker.libs/bin/release/linker.libs.1.6.0.nupkg -Source https://api.nuget.org/v3/index.json -SkipDuplicate -ApiKey ${{ secrets.NUGET_KEY }} -NoSymbol
|
||||||
nuget push ./linker.tun/bin/release/linker.tun.1.5.9.nupkg -Source https://api.nuget.org/v3/index.json -SkipDuplicate -ApiKey ${{ secrets.NUGET_KEY }} -NoSymbol
|
nuget push ./linker.tun/bin/release/linker.tun.1.6.0.nupkg -Source https://api.nuget.org/v3/index.json -SkipDuplicate -ApiKey ${{ secrets.NUGET_KEY }} -NoSymbol
|
||||||
|
@@ -3,6 +3,10 @@ sidebar_position: 1
|
|||||||
---
|
---
|
||||||
|
|
||||||
# 1、首页
|
# 1、首页
|
||||||
|
:::danger[特别声明]
|
||||||
|
1. 因为没有签名,又需要较高的权限,所以可能存在报毒的情况,请手动在安全软件中添加白名单
|
||||||
|
2. 如有疑问,可右上角移步软件源码,公开透明
|
||||||
|
:::
|
||||||
|
|
||||||
:::tip[说明]
|
:::tip[说明]
|
||||||
|
|
||||||
|
@@ -15,14 +15,15 @@ sidebar_position: 1
|
|||||||
下载安装脚本
|
下载安装脚本
|
||||||
```
|
```
|
||||||
curl -fsSL https://linker-doc.snltty.com/linker-install.sh -o linker-install.sh
|
curl -fsSL https://linker-doc.snltty.com/linker-install.sh -o linker-install.sh
|
||||||
|
chmod +x linker-install.sh
|
||||||
```
|
```
|
||||||
默认安装位置
|
默认安装位置
|
||||||
```
|
```
|
||||||
sudo sh linker-install.sh
|
./linker-install.sh
|
||||||
```
|
```
|
||||||
指定安装位置
|
指定安装位置
|
||||||
```
|
```
|
||||||
sudo sh linker-install.sh /usr/local/bin
|
./linker-install.sh /usr/local/bin
|
||||||
```
|
```
|
||||||
:::
|
:::
|
||||||
|
|
||||||
|
@@ -17,15 +17,11 @@ plain='\033[0m'
|
|||||||
export PATH=$PATH:/usr/local/bin
|
export PATH=$PATH:/usr/local/bin
|
||||||
|
|
||||||
|
|
||||||
install_base() {
|
|
||||||
(command -v git >/dev/null 2>&1 && command -v curl >/dev/null 2>&1 && command -v wget >/dev/null 2>&1 && command -v unzip >/dev/null 2>&1 && command -v getenforce >/dev/null 2>&1) ||
|
|
||||||
(install_soft curl wget git unzip)
|
|
||||||
}
|
|
||||||
install_soft() {
|
install_soft() {
|
||||||
(command -v yum >/dev/null 2>&1 && yum makecache >/dev/null 2>&1 && yum install $* iproute2 dmidecode net-tools curl traceroute iptables ca-certificates -y >/dev/null 2>&1) ||
|
(command -v yum >/dev/null 2>&1 && yum makecache >/dev/null 2>&1 && yum install curl wget git unzip iproute dmidecode net-tools curl traceroute iptables ca-certificates -y >/dev/null 2>&1) ||
|
||||||
(command -v apt >/dev/null 2>&1 && apt update >/dev/null 2>&1 && apt install $* iproute2 dmidecode net-tools curl traceroute iptables ca-certificates -y >/dev/null 2>&1) ||
|
(command -v apt >/dev/null 2>&1 && apt update >/dev/null 2>&1 && apt install curl wget git unzip iproute2 dmidecode net-tools curl traceroute iptables ca-certificates -y >/dev/null 2>&1) ||
|
||||||
(command -v pacman >/dev/null 2>&1 && pacman -Syu $* base-devel --noconfirm && install_arch) ||
|
(command -v pacman >/dev/null 2>&1 && pacman -Syu curl wget git unzip base-devel --noconfirm && install_arch) ||
|
||||||
(command -v apt-get >/dev/null 2>&1 && apt-get update >/dev/null 2>&1 && apt-get install $* iproute2 dmidecode net-tools curl traceroute iptables ca-certificates -y >/dev/null 2>&1) ||
|
(command -v apt-get >/dev/null 2>&1 && apt-get update >/dev/null 2>&1 && apt-get install curl wget git unzip iproute2 dmidecode net-tools curl traceroute iptables ca-certificates -y >/dev/null 2>&1) ||
|
||||||
(command -v apk >/dev/null 2>&1 && apk update >/dev/null 2>&1 && apk add --no-cache net-tools iproute2 numactl-dev iputils iptables dmidecode -f >/dev/null 2>&1)
|
(command -v apk >/dev/null 2>&1 && apk update >/dev/null 2>&1 && apk add --no-cache net-tools iproute2 numactl-dev iputils iptables dmidecode -f >/dev/null 2>&1)
|
||||||
}
|
}
|
||||||
install_systemd() {
|
install_systemd() {
|
||||||
@@ -131,14 +127,20 @@ install_docker() {
|
|||||||
os_alpine="0"
|
os_alpine="0"
|
||||||
[ -e /etc/os-release ] && cat /etc/os-release | grep -i "PRETTY_NAME" | grep -qi "alpine" && os_alpine='1'
|
[ -e /etc/os-release ] && cat /etc/os-release | grep -i "PRETTY_NAME" | grep -qi "alpine" && os_alpine='1'
|
||||||
command -v docker >/dev/null 2>&1
|
command -v docker >/dev/null 2>&1
|
||||||
if [[ $? != 0 ]]; then
|
if docker --version >/dev/null 2>&1; then
|
||||||
echo -e "${yellow}===================================================${plain}\n正在安装 Docker"
|
echo -e "${green}已安装docker${plain}"
|
||||||
|
else
|
||||||
|
echo -e "${yellow}===================================================${plain}\n正在安装 docker"
|
||||||
if [ "$os_alpine" != 1 ]; then
|
if [ "$os_alpine" != 1 ]; then
|
||||||
|
echo -e "下载docker脚本..."
|
||||||
bash <(curl -sL https://get.docker.com -o get-docker.sh) >/dev/null 2>&1
|
bash <(curl -sL https://get.docker.com -o get-docker.sh) >/dev/null 2>&1
|
||||||
if [[ $? != 0 ]]; then
|
if [[ $? != 0 ]]; then
|
||||||
echo -e "${red}下载脚本失败,请检查本机能否连接 https://get.docker.com${plain}"
|
echo -e "${red}下载脚本失败,请检查本机能否连接 https://get.docker.com${plain}"
|
||||||
return 0
|
return 0
|
||||||
fi
|
fi
|
||||||
|
echo -e "执行docker脚本..."
|
||||||
|
chmod +x get-docker.sh
|
||||||
|
./get-docker.sh
|
||||||
systemctl enable docker.service >/dev/null 2>&1
|
systemctl enable docker.service >/dev/null 2>&1
|
||||||
systemctl start docker.service >/dev/null 2>&1
|
systemctl start docker.service >/dev/null 2>&1
|
||||||
else
|
else
|
||||||
@@ -146,7 +148,11 @@ install_docker() {
|
|||||||
rc-update add docker
|
rc-update add docker
|
||||||
rc-service docker start
|
rc-service docker start
|
||||||
fi
|
fi
|
||||||
echo -e "${green}Docker 安装成功${plain}"
|
if docker --version >/dev/null 2>&1; then
|
||||||
|
echo -e "${green}docker 安装成功${plain}"
|
||||||
|
else
|
||||||
|
echo -e "${red}docker 安装失败,可以多试几次${plain}" && exit 1
|
||||||
|
fi
|
||||||
fi
|
fi
|
||||||
|
|
||||||
LINKER_IMAGES=$(docker ps | grep -w "linker")
|
LINKER_IMAGES=$(docker ps | grep -w "linker")
|
||||||
@@ -198,7 +204,7 @@ install_docker() {
|
|||||||
|
|
||||||
select_version() {
|
select_version() {
|
||||||
if [[ -z $LINKER_IS_DOCKER ]]; then
|
if [[ -z $LINKER_IS_DOCKER ]]; then
|
||||||
echo -e "${yellow}===================================================\n${plain}请自行选择您的安装方式:${yellow}\n1. Docker\n2. 独立安装${plain}"
|
echo -e "${yellow}===================================================\n${plain}请自行选择您的安装方式:${yellow}\n1. docker\n2. 独立安装${plain}"
|
||||||
while true; do
|
while true; do
|
||||||
read -e -r -p "请输入选择 [1-2]:" option
|
read -e -r -p "请输入选择 [1-2]:" option
|
||||||
case "${option}" in
|
case "${option}" in
|
||||||
@@ -218,7 +224,7 @@ select_version() {
|
|||||||
fi
|
fi
|
||||||
}
|
}
|
||||||
|
|
||||||
install_base
|
install_soft
|
||||||
select_version
|
select_version
|
||||||
|
|
||||||
|
|
||||||
|
@@ -4,6 +4,8 @@ using System.Linq;
|
|||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using Microsoft.CodeAnalysis.Text;
|
using Microsoft.CodeAnalysis.Text;
|
||||||
using System.Text;
|
using System.Text;
|
||||||
|
using System;
|
||||||
|
using System.Diagnostics;
|
||||||
|
|
||||||
namespace linker.gen
|
namespace linker.gen
|
||||||
{
|
{
|
||||||
@@ -31,6 +33,8 @@ namespace linker.gen
|
|||||||
|
|
||||||
context.RegisterSourceOutput(compilations, (sourceProductionContext, compilation) =>
|
context.RegisterSourceOutput(compilations, (sourceProductionContext, compilation) =>
|
||||||
{
|
{
|
||||||
|
|
||||||
|
|
||||||
foreach (GeneratorInfo info in generators)
|
foreach (GeneratorInfo info in generators)
|
||||||
{
|
{
|
||||||
var iFlowSymbol = compilation.GetTypeByMetadataName(info.InterfaceName);
|
var iFlowSymbol = compilation.GetTypeByMetadataName(info.InterfaceName);
|
||||||
|
@@ -21,7 +21,7 @@ namespace linker.libs.api
|
|||||||
protected readonly Dictionary<string, PluginPathCacheInfo> plugins = new();
|
protected readonly Dictionary<string, PluginPathCacheInfo> plugins = new();
|
||||||
protected readonly ConcurrentDictionary<uint, ConnectionTimeInfo> connectionTimes = new();
|
protected readonly ConcurrentDictionary<uint, ConnectionTimeInfo> connectionTimes = new();
|
||||||
public uint OnlineNum = 0;
|
public uint OnlineNum = 0;
|
||||||
private Memory<byte> password = Helper.EmptyArray;
|
private string password = string.Empty;
|
||||||
|
|
||||||
private WebSocketServer server;
|
private WebSocketServer server;
|
||||||
|
|
||||||
@@ -34,7 +34,7 @@ namespace linker.libs.api
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
public void Websocket(int port, string password = "")
|
public void Websocket(int port, string password = "")
|
||||||
{
|
{
|
||||||
this.password = Encoding.UTF8.GetBytes(password);
|
this.password = password;
|
||||||
server = new WebSocketServer();
|
server = new WebSocketServer();
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
@@ -46,10 +46,10 @@ namespace linker.libs.api
|
|||||||
}
|
}
|
||||||
server.OnConnecting = (connection, header) =>
|
server.OnConnecting = (connection, header) =>
|
||||||
{
|
{
|
||||||
bool res = this.password.Length == 0 || this.password.Span.SequenceEqual(header.SecWebSocketProtocol.Span);
|
bool res = string.IsNullOrWhiteSpace(this.password) || (header.TryGetHeaderValue(WebsocketHeaderKey.SecWebSocketProtocol, out string _password) && _password.Contains(this.password));
|
||||||
if (res)
|
if (res)
|
||||||
{
|
{
|
||||||
header.SecWebSocketExtensions = Helper.EmptyArray;
|
header.SetHeaderValue(WebsocketHeaderKey.SecWebSocketExtensions, string.Empty);
|
||||||
}
|
}
|
||||||
return res;
|
return res;
|
||||||
};
|
};
|
||||||
|
@@ -1,5 +1,6 @@
|
|||||||
using linker.libs.jsonConverters;
|
using linker.libs.jsonConverters;
|
||||||
using System.Text.Json;
|
using System.Text.Json;
|
||||||
|
using System.Text.Json.Serialization;
|
||||||
using System.Text.Unicode;
|
using System.Text.Unicode;
|
||||||
|
|
||||||
namespace linker.libs.extends
|
namespace linker.libs.extends
|
||||||
@@ -23,15 +24,15 @@ namespace linker.libs.extends
|
|||||||
WriteIndented = true,
|
WriteIndented = true,
|
||||||
Converters = { new IPAddressJsonConverter(), new IPEndpointJsonConverter(), new DateTimeConverter() }
|
Converters = { new IPAddressJsonConverter(), new IPEndpointJsonConverter(), new DateTimeConverter() }
|
||||||
};
|
};
|
||||||
private static JsonSerializerOptions jsonSerializerOptionsIndented = new JsonSerializerOptions
|
public static void AddAOT(JsonSerializerContext[] contexts)
|
||||||
{
|
{
|
||||||
Encoder = System.Text.Encodings.Web.JavaScriptEncoder.Create(UnicodeRanges.All),
|
foreach (var context in contexts)
|
||||||
AllowTrailingCommas = true,
|
{
|
||||||
ReadCommentHandling = JsonCommentHandling.Skip,
|
jsonSerializerOptions1.TypeInfoResolverChain.Insert(0, context);
|
||||||
PropertyNameCaseInsensitive = true,
|
jsonSerializerOptions.TypeInfoResolverChain.Insert(0, context);
|
||||||
WriteIndented = true,
|
}
|
||||||
Converters = { new IPAddressJsonConverter(), new IPEndpointJsonConverter(), new DateTimeConverter() }
|
}
|
||||||
};
|
|
||||||
public static string ToJson(this object obj)
|
public static string ToJson(this object obj)
|
||||||
{
|
{
|
||||||
return JsonSerializer.Serialize(obj, jsonSerializerOptions1);
|
return JsonSerializer.Serialize(obj, jsonSerializerOptions1);
|
||||||
@@ -45,4 +46,6 @@ namespace linker.libs.extends
|
|||||||
return JsonSerializer.Deserialize<T>(json, options: jsonSerializerOptions);
|
return JsonSerializer.Deserialize<T>(json, options: jsonSerializerOptions);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@@ -14,9 +14,9 @@
|
|||||||
<Copyright>snltty</Copyright>
|
<Copyright>snltty</Copyright>
|
||||||
<PackageProjectUrl>https://github.com/snltty/linker</PackageProjectUrl>
|
<PackageProjectUrl>https://github.com/snltty/linker</PackageProjectUrl>
|
||||||
<RepositoryUrl>https://github.com/snltty/linker</RepositoryUrl>
|
<RepositoryUrl>https://github.com/snltty/linker</RepositoryUrl>
|
||||||
<Version>1.5.9</Version>
|
<Version>1.6.0</Version>
|
||||||
<AssemblyVersion>1.5.9</AssemblyVersion>
|
<AssemblyVersion>1.6.0</AssemblyVersion>
|
||||||
<FileVersion>1.5.9</FileVersion>
|
<FileVersion>1.6.0</FileVersion>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|AnyCPU'">
|
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|AnyCPU'">
|
||||||
<DebugType>full</DebugType>
|
<DebugType>full</DebugType>
|
||||||
|
@@ -140,10 +140,9 @@ namespace linker.libs.websocket
|
|||||||
{
|
{
|
||||||
|
|
||||||
token.SecWebSocketKey = WebSocketParser.BuildSecWebSocketKey();
|
token.SecWebSocketKey = WebSocketParser.BuildSecWebSocketKey();
|
||||||
byte[] connectData = WebSocketParser.BuildConnectData(new WebsocketHeaderInfo
|
WebsocketHeaderInfo header = new WebsocketHeaderInfo();
|
||||||
{
|
header.SetHeaderValue(WebsocketHeaderKey.SecWebSocketKey, token.SecWebSocketKey);
|
||||||
SecWebSocketKey = token.SecWebSocketKey,
|
byte[] connectData = WebSocketParser.BuildConnectData(header);
|
||||||
});
|
|
||||||
|
|
||||||
token.TargetSocket.Send(connectData, SocketFlags.None);
|
token.TargetSocket.Send(connectData, SocketFlags.None);
|
||||||
|
|
||||||
@@ -366,7 +365,7 @@ namespace linker.libs.websocket
|
|||||||
private void HandleConnect(AsyncServerUserToken token, Memory<byte> data)
|
private void HandleConnect(AsyncServerUserToken token, Memory<byte> data)
|
||||||
{
|
{
|
||||||
WebsocketHeaderInfo header = WebsocketHeaderInfo.Parse(data);
|
WebsocketHeaderInfo header = WebsocketHeaderInfo.Parse(data);
|
||||||
if (!WebSocketParser.VerifySecWebSocketAccept(token.SecWebSocketKey, header.SecWebSocketAccept))
|
if (header.TryGetHeaderValue(WebsocketHeaderKey.SecWebSocketAccept, out string accept) == false || WebSocketParser.VerifySecWebSocketAccept(token.SecWebSocketKey, accept) == false)
|
||||||
{
|
{
|
||||||
OnConnectFail("Sec-WebSocket-Accept Invalid");
|
OnConnectFail("Sec-WebSocket-Accept Invalid");
|
||||||
CloseClientSocket();
|
CloseClientSocket();
|
||||||
@@ -471,7 +470,7 @@ namespace linker.libs.websocket
|
|||||||
/// 当前帧的数据类型
|
/// 当前帧的数据类型
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public WebSocketFrameInfo.EnumOpcode Opcode { get; set; }
|
public WebSocketFrameInfo.EnumOpcode Opcode { get; set; }
|
||||||
public Memory<byte> SecWebSocketKey { get; set; }
|
public string SecWebSocketKey { get; set; }
|
||||||
public byte[] PoolBuffer { get; set; }
|
public byte[] PoolBuffer { get; set; }
|
||||||
|
|
||||||
public void Clear()
|
public void Clear()
|
||||||
|
@@ -1,6 +1,7 @@
|
|||||||
using System;
|
using System;
|
||||||
using System.Buffers;
|
using System.Buffers;
|
||||||
using System.Buffers.Binary;
|
using System.Buffers.Binary;
|
||||||
|
using System.Collections.Generic;
|
||||||
using System.Net;
|
using System.Net;
|
||||||
using System.Security.Cryptography;
|
using System.Security.Cryptography;
|
||||||
using System.Text;
|
using System.Text;
|
||||||
@@ -13,7 +14,7 @@ namespace linker.libs.websocket
|
|||||||
public static class WebSocketParser
|
public static class WebSocketParser
|
||||||
{
|
{
|
||||||
private readonly static SHA1 sha1 = SHA1.Create();
|
private readonly static SHA1 sha1 = SHA1.Create();
|
||||||
private readonly static Memory<byte> magicCode = Encoding.ASCII.GetBytes("258EAFA5-E914-47DA-95CA-C5AB0DC85B11");
|
private readonly static Memory<byte> magicCode = Encoding.UTF8.GetBytes("258EAFA5-E914-47DA-95CA-C5AB0DC85B11");
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 构建连接数据
|
/// 构建连接数据
|
||||||
/// </summary>
|
/// </summary>
|
||||||
@@ -21,21 +22,23 @@ namespace linker.libs.websocket
|
|||||||
/// <returns></returns>
|
/// <returns></returns>
|
||||||
public static byte[] BuildConnectData(WebsocketHeaderInfo header)
|
public static byte[] BuildConnectData(WebsocketHeaderInfo header)
|
||||||
{
|
{
|
||||||
string path = header.Path.Length == 0 ? "/" : Encoding.UTF8.GetString(header.Path.Span);
|
string path = header.Path.Length == 0 ? "/" : header.Path;
|
||||||
|
|
||||||
|
header.TryGetHeaderValue(WebsocketHeaderKey.SecWebSocketKey,out string key);
|
||||||
|
|
||||||
StringBuilder sb = new StringBuilder(10);
|
StringBuilder sb = new StringBuilder(10);
|
||||||
sb.Append($"GET {path} HTTP/1.1\r\n");
|
sb.Append($"GET {path} HTTP/1.1\r\n");
|
||||||
sb.Append($"Upgrade: websocket\r\n");
|
sb.Append($"Upgrade: websocket\r\n");
|
||||||
sb.Append($"Connection: Upgrade\r\n");
|
sb.Append($"Connection: Upgrade\r\n");
|
||||||
sb.Append($"Sec-WebSocket-Version: 13\r\n");
|
sb.Append($"Sec-WebSocket-Version: 13\r\n");
|
||||||
sb.Append($"Sec-WebSocket-Key: {Encoding.UTF8.GetString(header.SecWebSocketKey.Span)}\r\n");
|
sb.Append($"Sec-WebSocket-Key: {key}\r\n");
|
||||||
if (header.SecWebSocketProtocol.Length > 0)
|
if (header.TryGetHeaderValue(WebsocketHeaderKey.SecWebSocketProtocol, out string protocol))
|
||||||
{
|
{
|
||||||
sb.Append($"Sec-WebSocket-Protocol: {Encoding.UTF8.GetString(header.SecWebSocketProtocol.Span)}\r\n");
|
sb.Append($"Sec-WebSocket-Protocol: {protocol}\r\n");
|
||||||
}
|
}
|
||||||
if (header.SecWebSocketExtensions.Length > 0)
|
if (header.TryGetHeaderValue(WebsocketHeaderKey.SecWebSocketExtensions, out string extensions))
|
||||||
{
|
{
|
||||||
sb.Append($"Sec-WebSocket-Extensions: {Encoding.UTF8.GetString(header.SecWebSocketExtensions.Span)}\r\n");
|
sb.Append($"Sec-WebSocket-Extensions: {extensions}\r\n");
|
||||||
}
|
}
|
||||||
sb.Append("\r\n");
|
sb.Append("\r\n");
|
||||||
|
|
||||||
@@ -48,30 +51,31 @@ namespace linker.libs.websocket
|
|||||||
/// <returns></returns>
|
/// <returns></returns>
|
||||||
public static byte[] BuildConnectResponseData(WebsocketHeaderInfo header)
|
public static byte[] BuildConnectResponseData(WebsocketHeaderInfo header)
|
||||||
{
|
{
|
||||||
string acceptStr = BuildSecWebSocketAccept(header.SecWebSocketKey);
|
header.TryGetHeaderValue(WebsocketHeaderKey.SecWebSocketKey,out string key);
|
||||||
|
string acceptStr = BuildSecWebSocketAccept(key);
|
||||||
|
|
||||||
StringBuilder sb = new StringBuilder(10);
|
StringBuilder sb = new StringBuilder(10);
|
||||||
sb.Append($"HTTP/1.1 {(int)header.StatusCode} {AddSpace(header.StatusCode)}\r\n");
|
sb.Append($"HTTP/1.1 {(int)header.StatusCode} {AddSpace(header.StatusCode)}\r\n");
|
||||||
sb.Append($"Sec-WebSocket-Accept: {acceptStr}\r\n");
|
sb.Append($"Sec-WebSocket-Accept: {acceptStr}\r\n");
|
||||||
if (header.Connection.Length > 0)
|
if (header.TryGetHeaderValue(WebsocketHeaderKey.Connection, out string str1))
|
||||||
{
|
{
|
||||||
sb.Append($"Connection: {Encoding.UTF8.GetString(header.Connection.Span)}\r\n");
|
sb.Append($"Connection: {str1}\r\n");
|
||||||
}
|
}
|
||||||
if (header.Upgrade.Length > 0)
|
if (header.TryGetHeaderValue(WebsocketHeaderKey.Upgrade, out str1))
|
||||||
{
|
{
|
||||||
sb.Append($"Upgrade: {Encoding.UTF8.GetString(header.Upgrade.Span)}\r\n");
|
sb.Append($"Upgrade: {str1}\r\n");
|
||||||
}
|
}
|
||||||
if (header.SecWebSocketVersion.Length > 0)
|
if (header.TryGetHeaderValue(WebsocketHeaderKey.SecWebSocketVersion, out str1))
|
||||||
{
|
{
|
||||||
sb.Append($"Sec-WebSocket-Version: {Encoding.UTF8.GetString(header.SecWebSocketVersion.Span)}\r\n");
|
sb.Append($"Sec-Websocket-Version: {str1}\r\n");
|
||||||
}
|
}
|
||||||
if (header.SecWebSocketProtocol.Length > 0)
|
if (header.TryGetHeaderValue(WebsocketHeaderKey.SecWebSocketProtocol, out str1))
|
||||||
{
|
{
|
||||||
sb.Append($"Sec-WebSocket-Protocol: {Encoding.UTF8.GetString(header.SecWebSocketProtocol.Span)}\r\n");
|
sb.Append($"Sec-WebSocket-Protocol: {str1}\r\n");
|
||||||
}
|
}
|
||||||
if (header.SecWebSocketExtensions.Length > 0)
|
if (header.TryGetHeaderValue(WebsocketHeaderKey.SecWebSocketExtensions, out str1))
|
||||||
{
|
{
|
||||||
sb.Append($"Sec-WebSocket-Extensions: {Encoding.UTF8.GetString(header.SecWebSocketExtensions.Span)}\r\n");
|
sb.Append($"Sec-WebSocket-Extensions: {str1}\r\n");
|
||||||
}
|
}
|
||||||
sb.Append("\r\n");
|
sb.Append("\r\n");
|
||||||
|
|
||||||
@@ -81,7 +85,7 @@ namespace linker.libs.websocket
|
|||||||
/// 生成随机key
|
/// 生成随机key
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <returns></returns>
|
/// <returns></returns>
|
||||||
public static byte[] BuildSecWebSocketKey()
|
public static string BuildSecWebSocketKey()
|
||||||
{
|
{
|
||||||
byte[] bytes = new byte[16];
|
byte[] bytes = new byte[16];
|
||||||
Random random = new Random(DateTime.Now.Ticks.GetHashCode());
|
Random random = new Random(DateTime.Now.Ticks.GetHashCode());
|
||||||
@@ -89,8 +93,7 @@ namespace linker.libs.websocket
|
|||||||
{
|
{
|
||||||
bytes[i] = (byte)random.Next(0, 255);
|
bytes[i] = (byte)random.Next(0, 255);
|
||||||
}
|
}
|
||||||
byte[] res = Encoding.UTF8.GetBytes(Convert.ToBase64String(bytes));
|
return Convert.ToBase64String(bytes);
|
||||||
return res;
|
|
||||||
}
|
}
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 构建mask数据
|
/// 构建mask数据
|
||||||
@@ -113,12 +116,12 @@ namespace linker.libs.websocket
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="key"></param>
|
/// <param name="key"></param>
|
||||||
/// <returns></returns>
|
/// <returns></returns>
|
||||||
private static string BuildSecWebSocketAccept(Memory<byte> key)
|
private static string BuildSecWebSocketAccept(string key)
|
||||||
{
|
{
|
||||||
int keyLength = key.Length + magicCode.Length;
|
int keyLength = key.Length + magicCode.Length;
|
||||||
byte[] acceptBytes = new byte[keyLength];
|
byte[] acceptBytes = new byte[keyLength];
|
||||||
|
|
||||||
key.CopyTo(acceptBytes);
|
Encoding.UTF8.GetBytes(key).AsMemory().CopyTo(acceptBytes);
|
||||||
magicCode.CopyTo(acceptBytes.AsMemory(key.Length));
|
magicCode.CopyTo(acceptBytes.AsMemory(key.Length));
|
||||||
|
|
||||||
string acceptStr = Convert.ToBase64String(sha1.ComputeHash(acceptBytes, 0, keyLength));
|
string acceptStr = Convert.ToBase64String(sha1.ComputeHash(acceptBytes, 0, keyLength));
|
||||||
@@ -131,10 +134,10 @@ namespace linker.libs.websocket
|
|||||||
/// <param name="key"></param>
|
/// <param name="key"></param>
|
||||||
/// <param name="accept"></param>
|
/// <param name="accept"></param>
|
||||||
/// <returns></returns>
|
/// <returns></returns>
|
||||||
public static bool VerifySecWebSocketAccept(Memory<byte> key, Memory<byte> accept)
|
public static bool VerifySecWebSocketAccept(string key, string accept)
|
||||||
{
|
{
|
||||||
string acceptStr = BuildSecWebSocketAccept(key);
|
string acceptStr = BuildSecWebSocketAccept(key);
|
||||||
return acceptStr == Encoding.UTF8.GetString(accept.Span);
|
return acceptStr == accept;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@@ -536,113 +539,108 @@ namespace linker.libs.websocket
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
public sealed class WebsocketHeaderInfo
|
public sealed class WebsocketHeaderInfo
|
||||||
{
|
{
|
||||||
static byte[][] bytes = new byte[][] {
|
|
||||||
Encoding.ASCII.GetBytes("Connection: "),
|
|
||||||
Encoding.ASCII.GetBytes("Upgrade: "),
|
|
||||||
Encoding.ASCII.GetBytes("Origin: "),
|
|
||||||
Encoding.ASCII.GetBytes("Sec-WebSocket-Version: "),
|
|
||||||
Encoding.ASCII.GetBytes("Sec-WebSocket-Key: "),
|
|
||||||
Encoding.ASCII.GetBytes("Sec-WebSocket-Extensions: "),
|
|
||||||
Encoding.ASCII.GetBytes("Sec-WebSocket-Protocol: "),
|
|
||||||
Encoding.ASCII.GetBytes("Sec-WebSocket-Accept: ")
|
|
||||||
};
|
|
||||||
static byte[] httpBytes = Encoding.UTF8.GetBytes("HTTP/");
|
static byte[] httpBytes = Encoding.UTF8.GetBytes("HTTP/");
|
||||||
|
static byte[] endBytes = Encoding.UTF8.GetBytes("\r\n");
|
||||||
|
static byte[] splitBytes = Encoding.UTF8.GetBytes(": ");
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 状态码
|
||||||
|
/// </summary>
|
||||||
public HttpStatusCode StatusCode { get; set; } = HttpStatusCode.SwitchingProtocols;
|
public HttpStatusCode StatusCode { get; set; } = HttpStatusCode.SwitchingProtocols;
|
||||||
public Memory<byte> Method { get; private set; }
|
/// <summary>
|
||||||
|
/// 方法
|
||||||
|
/// </summary>
|
||||||
|
public string Method { get; private set; }
|
||||||
|
/// <summary>
|
||||||
|
/// 路径
|
||||||
|
/// </summary>
|
||||||
|
public string Path { get; set; }
|
||||||
|
/// <summary>
|
||||||
|
/// 请求头
|
||||||
|
/// </summary>
|
||||||
|
public Dictionary<string, string> Headers { get; private set; } = new Dictionary<string, string>();
|
||||||
|
|
||||||
private string _pathSet { get; set; }
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 用这个设置path值
|
/// 获取请求头
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public string PathSet
|
/// <param name="key"></param>
|
||||||
|
/// <param name="value"></param>
|
||||||
|
/// <returns></returns>
|
||||||
|
public bool TryGetHeaderValue(string key, out string value)
|
||||||
{
|
{
|
||||||
get
|
return Headers.TryGetValue(key, out value) && string.IsNullOrWhiteSpace(value) == false;
|
||||||
{
|
|
||||||
return _pathSet;
|
|
||||||
}
|
|
||||||
set
|
|
||||||
{
|
|
||||||
_pathSet = value;
|
|
||||||
Path = Encoding.UTF8.GetBytes(_pathSet);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 如果 仅1个字符,那就是 /
|
/// 设置请求头
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public Memory<byte> Path { get; private set; }
|
/// <param name="key"></param>
|
||||||
public Memory<byte> Connection { get; private set; }
|
/// <param name="value"></param>
|
||||||
public Memory<byte> Upgrade { get; private set; }
|
public void SetHeaderValue(string key, string value)
|
||||||
public Memory<byte> Origin { get; private set; }
|
{
|
||||||
public Memory<byte> SecWebSocketVersion { get; private set; }
|
Headers[key] = value;
|
||||||
public Memory<byte> SecWebSocketKey { get; set; }
|
}
|
||||||
public Memory<byte> SecWebSocketExtensions { get; set; }
|
|
||||||
public Memory<byte> SecWebSocketProtocol { get; set; }
|
/// <summary>
|
||||||
public Memory<byte> SecWebSocketAccept { get; set; }
|
/// 解析websocket请求头
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="header"></param>
|
||||||
|
/// <returns></returns>
|
||||||
public static WebsocketHeaderInfo Parse(Memory<byte> header)
|
public static WebsocketHeaderInfo Parse(Memory<byte> header)
|
||||||
{
|
{
|
||||||
Span<byte> span = header.Span;
|
Span<byte> span = header.Span;
|
||||||
int flag = 0xff;
|
|
||||||
int bit = 0x01;
|
|
||||||
|
|
||||||
ulong[] res = new ulong[bytes.Length];
|
Span<byte> temp = span;
|
||||||
|
WebsocketHeaderInfo headerInfo = new WebsocketHeaderInfo();
|
||||||
|
|
||||||
for (int i = 0, len = span.Length; i < len; i++)
|
//跳过头
|
||||||
|
temp = temp.Slice(temp.IndexOf(endBytes) + 2);
|
||||||
|
int splitIndex = 0;
|
||||||
|
//还有分割线
|
||||||
|
while ((splitIndex = temp.IndexOf(splitBytes)) >= 0)
|
||||||
{
|
{
|
||||||
if (span[i] == 13 && span[i + 1] == 10 && span[i + 2] == 13 && span[i + 3] == 10)
|
//取到key
|
||||||
{
|
string key = Encoding.UTF8.GetString(temp.Slice(0, splitIndex)).ToLowerInvariant();
|
||||||
break;
|
//跳过key
|
||||||
}
|
temp = temp.Slice(splitIndex + 2);
|
||||||
if (span[i] == 13 && span[i + 1] == 10)
|
|
||||||
{
|
|
||||||
int startIndex = i + 2;
|
|
||||||
for (int k = 0; k < bytes.Length; k++)
|
|
||||||
{
|
|
||||||
if ((flag >> k & 1) == 1 && span[startIndex] == bytes[k][0])
|
|
||||||
{
|
|
||||||
if (span.Slice(startIndex, bytes[k].Length).SequenceEqual(bytes[k]))
|
|
||||||
{
|
|
||||||
int index = span.Slice(startIndex).IndexOf((byte)13);
|
|
||||||
flag &= ~(bit << k);
|
|
||||||
|
|
||||||
#pragma warning disable CS0675 // 对进行了带符号扩展的操作数使用了按位或运算符
|
//取到value
|
||||||
res[k] = (ulong)(startIndex + bytes[k].Length) << 32 | (ulong)(index - bytes[k].Length);
|
int endIndex = temp.IndexOf(endBytes);
|
||||||
#pragma warning restore CS0675 // 对进行了带符号扩展的操作数使用了按位或运算符
|
string value = Encoding.UTF8.GetString(temp.Slice(0, endIndex));
|
||||||
|
//跳过value
|
||||||
|
temp = temp.Slice(endIndex + 2);
|
||||||
|
|
||||||
i += index + 1;
|
headerInfo.Headers[key] = value;
|
||||||
break;
|
|
||||||
}
|
}
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
WebsocketHeaderInfo headerInfo = new WebsocketHeaderInfo
|
|
||||||
{
|
|
||||||
Connection = header.Slice((int)(res[0] >> 32), (int)(res[0] & 0xffffffff)),
|
|
||||||
Upgrade = header.Slice((int)(res[1] >> 32), (int)(res[1] & 0xffffffff)),
|
|
||||||
Origin = header.Slice((int)(res[2] >> 32), (int)(res[2] & 0xffffffff)),
|
|
||||||
SecWebSocketVersion = header.Slice((int)(res[3] >> 32), (int)(res[3] & 0xffffffff)),
|
|
||||||
SecWebSocketKey = header.Slice((int)(res[4] >> 32), (int)(res[4] & 0xffffffff)),
|
|
||||||
SecWebSocketExtensions = header.Slice((int)(res[5] >> 32), (int)(res[5] & 0xffffffff)),
|
|
||||||
SecWebSocketProtocol = header.Slice((int)(res[6] >> 32), (int)(res[6] & 0xffffffff)),
|
|
||||||
SecWebSocketAccept = header.Slice((int)(res[7] >> 32), (int)(res[7] & 0xffffffff)),
|
|
||||||
};
|
|
||||||
|
|
||||||
int pathIndex = span.IndexOf((byte)32);
|
int pathIndex = span.IndexOf((byte)32);
|
||||||
int pathIndex1 = span.Slice(pathIndex + 1).IndexOf((byte)32);
|
int pathIndex1 = span.Slice(pathIndex + 1).IndexOf((byte)32);
|
||||||
|
//响应的,获取状态码
|
||||||
if (header.Slice(0, httpBytes.Length).Span.SequenceEqual(httpBytes))
|
if (header.Slice(0, httpBytes.Length).Span.SequenceEqual(httpBytes))
|
||||||
{
|
{
|
||||||
int code = int.Parse(Encoding.UTF8.GetString(header.Slice(pathIndex + 1, pathIndex1).Span));
|
int code = int.Parse(Encoding.UTF8.GetString(header.Slice(pathIndex + 1, pathIndex1).Span));
|
||||||
headerInfo.StatusCode = (HttpStatusCode)code;
|
headerInfo.StatusCode = (HttpStatusCode)code;
|
||||||
}
|
}
|
||||||
|
//请求的,获取路径和方法
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
headerInfo.Path = header.Slice(pathIndex + 1, pathIndex1);
|
headerInfo.Path = Encoding.UTF8.GetString(span.Slice(pathIndex + 1, pathIndex1));
|
||||||
headerInfo.Method = header.Slice(0, pathIndex);
|
headerInfo.Method = Encoding.UTF8.GetString(span.Slice(0, pathIndex));
|
||||||
}
|
}
|
||||||
|
|
||||||
return headerInfo;
|
return headerInfo;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
public sealed class WebsocketHeaderKey
|
||||||
|
{
|
||||||
|
public static string Connection = "connection";
|
||||||
|
public static string Upgrade = "upgrade";
|
||||||
|
public static string Origin = "origin";
|
||||||
|
public static string SecWebSocketVersion = "sec-websocket-version";
|
||||||
|
public static string SecWebSocketKey = "sec-websocket-key";
|
||||||
|
public static string SecWebSocketExtensions = "sec-websocket-extensions";
|
||||||
|
public static string SecWebSocketProtocol = "sec-websocket-protocol";
|
||||||
|
public static string SecWebSocketAccept = "sec-websocket-accept";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -25,7 +25,7 @@ namespace linker.libs.websocket
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
public Func<WebsocketConnection, WebsocketHeaderInfo, bool> OnConnecting = (connection, header) =>
|
public Func<WebsocketConnection, WebsocketHeaderInfo, bool> OnConnecting = (connection, header) =>
|
||||||
{
|
{
|
||||||
header.SecWebSocketExtensions = Helper.EmptyArray; return true;
|
header.SetHeaderValue(WebsocketHeaderKey.SecWebSocketExtensions, string.Empty); return true;
|
||||||
};
|
};
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 已断开连接,没有收到关闭帧
|
/// 已断开连接,没有收到关闭帧
|
||||||
@@ -351,7 +351,7 @@ namespace linker.libs.websocket
|
|||||||
private void HandleConnect(AsyncUserToken token, Memory<byte> data)
|
private void HandleConnect(AsyncUserToken token, Memory<byte> data)
|
||||||
{
|
{
|
||||||
WebsocketHeaderInfo header = WebsocketHeaderInfo.Parse(data);
|
WebsocketHeaderInfo header = WebsocketHeaderInfo.Parse(data);
|
||||||
if (header.SecWebSocketKey.Length == 0)
|
if (header.TryGetHeaderValue(WebsocketHeaderKey.SecWebSocketKey,out string key) == false)
|
||||||
{
|
{
|
||||||
header.StatusCode = HttpStatusCode.MethodNotAllowed;
|
header.StatusCode = HttpStatusCode.MethodNotAllowed;
|
||||||
token.Connectrion.ConnectResponse(header);
|
token.Connectrion.ConnectResponse(header);
|
||||||
|
@@ -1,5 +1,5 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?>
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
<project ver="10" name="linker.tray.win" libEmbed="true" icon="..\linker\favicon.ico" ui="win" output="linker.tray.win.exe" CompanyName="snltty" FileDescription="linker.tray.win" LegalCopyright="Copyright (C) snltty 2024" ProductName="linker.tray.win" InternalName="linker.install.win" FileVersion="0.0.0.175" ProductVersion="0.0.0.175" publishDir="/dist/" dstrip="false" local="false" ignored="false">
|
<project ver="10" name="linker.tray.win" libEmbed="true" icon="..\linker\favicon.ico" ui="win" output="linker.tray.win.exe" CompanyName="snltty" FileDescription="linker.tray.win" LegalCopyright="Copyright (C) snltty 2024" ProductName="linker.tray.win" InternalName="linker.install.win" FileVersion="0.0.0.178" ProductVersion="0.0.0.178" publishDir="/dist/" dstrip="false" local="false" ignored="false">
|
||||||
<file name="main.aardio" path="main.aardio" comment="main.aardio"/>
|
<file name="main.aardio" path="main.aardio" comment="main.aardio"/>
|
||||||
<folder name="资源文件" path="res" embed="true" local="false" ignored="false">
|
<folder name="资源文件" path="res" embed="true" local="false" ignored="false">
|
||||||
<file name="favicon.ico" path="res\favicon.ico" comment="res\favicon.ico"/>
|
<file name="favicon.ico" path="res\favicon.ico" comment="res\favicon.ico"/>
|
||||||
|
BIN
linker.tray.win/dist/linker.tray.win.exe
vendored
BIN
linker.tray.win/dist/linker.tray.win.exe
vendored
Binary file not shown.
@@ -16,9 +16,9 @@
|
|||||||
<PackageProjectUrl>https://github.com/snltty/linker</PackageProjectUrl>
|
<PackageProjectUrl>https://github.com/snltty/linker</PackageProjectUrl>
|
||||||
<RepositoryUrl>https://github.com/snltty/linker</RepositoryUrl>
|
<RepositoryUrl>https://github.com/snltty/linker</RepositoryUrl>
|
||||||
<PackageReleaseNotes>linker tun</PackageReleaseNotes>
|
<PackageReleaseNotes>linker tun</PackageReleaseNotes>
|
||||||
<Version>1.5.9</Version>
|
<Version>1.6.0</Version>
|
||||||
<AssemblyVersion>1.5.9</AssemblyVersion>
|
<AssemblyVersion>1.6.0</AssemblyVersion>
|
||||||
<FileVersion>1.5.9</FileVersion>
|
<FileVersion>1.6.0</FileVersion>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
|
|
||||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|AnyCPU'">
|
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|AnyCPU'">
|
||||||
|
@@ -16,9 +16,9 @@
|
|||||||
<PackageProjectUrl>https://github.com/snltty/linker</PackageProjectUrl>
|
<PackageProjectUrl>https://github.com/snltty/linker</PackageProjectUrl>
|
||||||
<RepositoryUrl>https://github.com/snltty/linker</RepositoryUrl>
|
<RepositoryUrl>https://github.com/snltty/linker</RepositoryUrl>
|
||||||
<PackageReleaseNotes>linker tunnel</PackageReleaseNotes>
|
<PackageReleaseNotes>linker tunnel</PackageReleaseNotes>
|
||||||
<Version>1.5.9</Version>
|
<Version>1.6.0</Version>
|
||||||
<AssemblyVersion>1.5.9</AssemblyVersion>
|
<AssemblyVersion>1.6.0</AssemblyVersion>
|
||||||
<FileVersion>1.5.9</FileVersion>
|
<FileVersion>1.6.0</FileVersion>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
|
|
||||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|AnyCPU'">
|
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|AnyCPU'">
|
||||||
|
@@ -1,4 +1,6 @@
|
|||||||
using System.Net;
|
using linker.libs.extends;
|
||||||
|
using System.Net;
|
||||||
|
using System.Text;
|
||||||
|
|
||||||
namespace linker.tunnel.wanport
|
namespace linker.tunnel.wanport
|
||||||
{
|
{
|
||||||
@@ -16,6 +18,7 @@ namespace linker.tunnel.wanport
|
|||||||
/// <param name="server">服务器</param>
|
/// <param name="server">服务器</param>
|
||||||
/// <returns></returns>
|
/// <returns></returns>
|
||||||
public Task<TunnelWanPortEndPoint> GetAsync(IPAddress inter, IPEndPoint server);
|
public Task<TunnelWanPortEndPoint> GetAsync(IPAddress inter, IPEndPoint server);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public sealed class TunnelWanPortEndPoint
|
public sealed class TunnelWanPortEndPoint
|
||||||
|
@@ -3,6 +3,7 @@ using linker.libs.extends;
|
|||||||
using System.Buffers;
|
using System.Buffers;
|
||||||
using System.Net;
|
using System.Net;
|
||||||
using System.Net.Sockets;
|
using System.Net.Sockets;
|
||||||
|
using System.Text;
|
||||||
|
|
||||||
namespace linker.tunnel.wanport
|
namespace linker.tunnel.wanport
|
||||||
{
|
{
|
||||||
@@ -23,13 +24,19 @@ namespace linker.tunnel.wanport
|
|||||||
udpClient.Client.ReuseBind(new IPEndPoint(localIP, 0));
|
udpClient.Client.ReuseBind(new IPEndPoint(localIP, 0));
|
||||||
udpClient.Client.WindowsUdpBug();
|
udpClient.Client.WindowsUdpBug();
|
||||||
|
|
||||||
|
byte[] buffer = ArrayPool<byte>.Shared.Rent(1024);
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
for (int i = 0; i < 5; i++)
|
await udpClient.SendAsync(BuildSendData(buffer, 0), server).ConfigureAwait(false);
|
||||||
|
TimerHelper.Async(async () =>
|
||||||
{
|
{
|
||||||
try
|
for (byte i = 1; i < 10; i++)
|
||||||
{
|
{
|
||||||
await udpClient.SendAsync(new byte[1] { 0 }, server).ConfigureAwait(false);
|
await Task.Delay(15);
|
||||||
|
await udpClient.SendAsync(BuildSendData(buffer, i), server).ConfigureAwait(false);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
UdpReceiveResult result = await udpClient.ReceiveAsync().WaitAsync(TimeSpan.FromMilliseconds(2000)).ConfigureAwait(false);
|
UdpReceiveResult result = await udpClient.ReceiveAsync().WaitAsync(TimeSpan.FromMilliseconds(2000)).ConfigureAwait(false);
|
||||||
if (result.Buffer.Length == 0)
|
if (result.Buffer.Length == 0)
|
||||||
{
|
{
|
||||||
@@ -49,11 +56,6 @@ namespace linker.tunnel.wanport
|
|||||||
|
|
||||||
return new TunnelWanPortEndPoint { Local = udpClient.Client.LocalEndPoint as IPEndPoint, Remote = remoteEP };
|
return new TunnelWanPortEndPoint { Local = udpClient.Client.LocalEndPoint as IPEndPoint, Remote = remoteEP };
|
||||||
}
|
}
|
||||||
catch (Exception)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
catch (Exception ex)
|
catch (Exception ex)
|
||||||
{
|
{
|
||||||
if (LoggerHelper.Instance.LoggerLevel <= LoggerTypes.DEBUG)
|
if (LoggerHelper.Instance.LoggerLevel <= LoggerTypes.DEBUG)
|
||||||
@@ -61,11 +63,21 @@ namespace linker.tunnel.wanport
|
|||||||
}
|
}
|
||||||
finally
|
finally
|
||||||
{
|
{
|
||||||
|
ArrayPool<byte>.Shared.Return(buffer);
|
||||||
udpClient.Close();
|
udpClient.Close();
|
||||||
}
|
}
|
||||||
|
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
private Memory<byte> BuildSendData(byte[] buffer, byte i)
|
||||||
|
{
|
||||||
|
byte[] temp = Encoding.UTF8.GetBytes(Environment.TickCount64.ToString().Md5().SubStr(0, new Random().Next(16, 32)));
|
||||||
|
temp.AsMemory().CopyTo(buffer);
|
||||||
|
buffer[0] = 0;
|
||||||
|
buffer[1] = i;
|
||||||
|
|
||||||
|
return buffer.AsMemory(0, temp.Length);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@@ -82,13 +94,15 @@ namespace linker.tunnel.wanport
|
|||||||
|
|
||||||
public async Task<TunnelWanPortEndPoint> GetAsync(IPAddress localIP, IPEndPoint server)
|
public async Task<TunnelWanPortEndPoint> GetAsync(IPAddress localIP, IPEndPoint server)
|
||||||
{
|
{
|
||||||
byte[] buffer = ArrayPool<byte>.Shared.Rent(20);
|
byte[] buffer = ArrayPool<byte>.Shared.Rent(1024);
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
Socket socket = new Socket(server.AddressFamily, SocketType.Stream, System.Net.Sockets.ProtocolType.Tcp);
|
Socket socket = new Socket(server.AddressFamily, SocketType.Stream, System.Net.Sockets.ProtocolType.Tcp);
|
||||||
socket.ReuseBind(new IPEndPoint(localIP, 0));
|
socket.ReuseBind(new IPEndPoint(localIP, 0));
|
||||||
await socket.ConnectAsync(server).ConfigureAwait(false);
|
await socket.ConnectAsync(server).ConfigureAwait(false);
|
||||||
await socket.SendAsync(new byte[] { 0 });
|
|
||||||
|
await socket.SendAsync(BuildSendData(buffer, (byte)new Random().Next(0, 255)));
|
||||||
|
|
||||||
int length = await socket.ReceiveAsync(buffer.AsMemory(), SocketFlags.None).ConfigureAwait(false);
|
int length = await socket.ReceiveAsync(buffer.AsMemory(), SocketFlags.None).ConfigureAwait(false);
|
||||||
for (int j = 0; j < length; j++)
|
for (int j = 0; j < length; j++)
|
||||||
{
|
{
|
||||||
@@ -118,5 +132,15 @@ namespace linker.tunnel.wanport
|
|||||||
|
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private Memory<byte> BuildSendData(byte[] buffer, byte i)
|
||||||
|
{
|
||||||
|
byte[] temp = Encoding.UTF8.GetBytes(Environment.TickCount64.ToString().Md5().SubStr(0, new Random().Next(16, 32)));
|
||||||
|
temp.AsMemory().CopyTo(buffer);
|
||||||
|
buffer[0] = 0;
|
||||||
|
buffer[1] = i;
|
||||||
|
|
||||||
|
return buffer.AsMemory(0, temp.Length);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -4,7 +4,6 @@ using linker.startup;
|
|||||||
using linker.config;
|
using linker.config;
|
||||||
using System.ServiceProcess;
|
using System.ServiceProcess;
|
||||||
using System.Diagnostics;
|
using System.Diagnostics;
|
||||||
using linker.libs.extends;
|
|
||||||
|
|
||||||
namespace linker
|
namespace linker
|
||||||
{
|
{
|
||||||
|
@@ -174,6 +174,7 @@ namespace linker.config
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[JsonAotAttribute]
|
||||||
public sealed partial class ConfigCommonInfo : IConfig
|
public sealed partial class ConfigCommonInfo : IConfig
|
||||||
{
|
{
|
||||||
public ConfigCommonInfo() { }
|
public ConfigCommonInfo() { }
|
||||||
@@ -223,4 +224,8 @@ namespace linker.config
|
|||||||
return text.DeJson<ConfigCommonInfo>();
|
return text.DeJson<ConfigCommonInfo>();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
[AttributeUsage(AttributeTargets.Class)]
|
||||||
|
public class JsonAotAttribute : Attribute { }
|
||||||
}
|
}
|
||||||
|
@@ -21,17 +21,16 @@
|
|||||||
<Title>linker</Title>
|
<Title>linker</Title>
|
||||||
<Authors>snltty</Authors>
|
<Authors>snltty</Authors>
|
||||||
<Company>snltty</Company>
|
<Company>snltty</Company>
|
||||||
<Description>1. 优化UI
|
<Description>1. 优化websocket(某些代理环境下可能请求头key名称不规范)
|
||||||
2. 优化网卡
|
2. 尝试解决UDP获取外网IP端口失败的问题
|
||||||
3. 修复网卡禁用自动重启
|
3. 测试中</Description>
|
||||||
4. 增加一键安装脚本</Description>
|
|
||||||
<Copyright>snltty</Copyright>
|
<Copyright>snltty</Copyright>
|
||||||
<PackageProjectUrl>https://github.com/snltty/linker</PackageProjectUrl>
|
<PackageProjectUrl>https://github.com/snltty/linker</PackageProjectUrl>
|
||||||
<RepositoryUrl>https://github.com/snltty/linker</RepositoryUrl>
|
<RepositoryUrl>https://github.com/snltty/linker</RepositoryUrl>
|
||||||
<PackageReleaseNotes>linker</PackageReleaseNotes>
|
<PackageReleaseNotes>linker</PackageReleaseNotes>
|
||||||
<Version>1.5.9</Version>
|
<Version>1.6.0</Version>
|
||||||
<AssemblyVersion>1.5.9</AssemblyVersion>
|
<AssemblyVersion>1.6.0</AssemblyVersion>
|
||||||
<FileVersion>1.5.9</FileVersion>
|
<FileVersion>1.6.0</FileVersion>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
|
|
||||||
|
|
||||||
@@ -76,4 +75,7 @@
|
|||||||
<PackageReference Include="Microsoft.Extensions.DependencyInjection" Version="8.0.1" />
|
<PackageReference Include="Microsoft.Extensions.DependencyInjection" Version="8.0.1" />
|
||||||
<PackageReference Include="System.ServiceProcess.ServiceController" Version="8.0.1" />
|
<PackageReference Include="System.ServiceProcess.ServiceController" Version="8.0.1" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
<ItemGroup>
|
||||||
|
<Folder Include="plugins\pcp\" />
|
||||||
|
</ItemGroup>
|
||||||
</Project>
|
</Project>
|
||||||
|
@@ -7,6 +7,7 @@ using linker.plugins.client;
|
|||||||
using linker.plugins.tunnel;
|
using linker.plugins.tunnel;
|
||||||
using linker.plugins.messenger;
|
using linker.plugins.messenger;
|
||||||
using linker.plugins.relay.client;
|
using linker.plugins.relay.client;
|
||||||
|
using linker.client.config;
|
||||||
|
|
||||||
namespace linker.plugins.forward.proxy
|
namespace linker.plugins.forward.proxy
|
||||||
{
|
{
|
||||||
@@ -18,8 +19,8 @@ namespace linker.plugins.forward.proxy
|
|||||||
|
|
||||||
protected override string TransactionId => "forward";
|
protected override string TransactionId => "forward";
|
||||||
|
|
||||||
public ForwardProxy(FileConfig config, TunnelTransfer tunnelTransfer, RelayTransfer relayTransfer, ClientSignInTransfer clientSignInTransfer, ClientSignInState clientSignInState)
|
public ForwardProxy(FileConfig config, TunnelTransfer tunnelTransfer, RelayTransfer relayTransfer, ClientSignInTransfer clientSignInTransfer, ClientSignInState clientSignInState, RunningConfig runningConfig)
|
||||||
: base(config, tunnelTransfer, relayTransfer, clientSignInTransfer, clientSignInState)
|
: base(config, tunnelTransfer, relayTransfer, clientSignInTransfer, clientSignInState, runningConfig)
|
||||||
{
|
{
|
||||||
TaskUdp();
|
TaskUdp();
|
||||||
}
|
}
|
||||||
|
@@ -52,6 +52,8 @@ namespace linker.plugins.relay.client.transport
|
|||||||
relayInfo.FlowingId = relayAskResultInfo.FlowingId;
|
relayInfo.FlowingId = relayAskResultInfo.FlowingId;
|
||||||
if (relayInfo.FlowingId == 0 || relayAskResultInfo.Nodes.Count == 0)
|
if (relayInfo.FlowingId == 0 || relayAskResultInfo.Nodes.Count == 0)
|
||||||
{
|
{
|
||||||
|
if (LoggerHelper.Instance.LoggerLevel <= LoggerTypes.DEBUG)
|
||||||
|
LoggerHelper.Instance.Error($"relay ask fail,flowid:{relayInfo.FlowingId},nodes:{relayAskResultInfo.Nodes.Count}");
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -65,12 +67,16 @@ namespace linker.plugins.relay.client.transport
|
|||||||
Socket socket = await ConnectNodeServer(relayInfo, relayAskResultInfo.Nodes);
|
Socket socket = await ConnectNodeServer(relayInfo, relayAskResultInfo.Nodes);
|
||||||
if (socket == null)
|
if (socket == null)
|
||||||
{
|
{
|
||||||
|
if (LoggerHelper.Instance.LoggerLevel <= LoggerTypes.DEBUG)
|
||||||
|
LoggerHelper.Instance.Error($"relay connect server fail,flowid:{relayInfo.FlowingId},nodes:{relayAskResultInfo.Nodes.Count}");
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
//让对方确认中继
|
//让对方确认中继
|
||||||
if (await RelayConfirm(relayInfo) == false)
|
if (await RelayConfirm(relayInfo) == false)
|
||||||
{
|
{
|
||||||
|
if (LoggerHelper.Instance.LoggerLevel <= LoggerTypes.DEBUG)
|
||||||
|
LoggerHelper.Instance.Error($"relay confirm fail,flowid:{relayInfo.FlowingId},nodes:{relayAskResultInfo.Nodes.Count}");
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -212,9 +218,8 @@ namespace linker.plugins.relay.client.transport
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (LoggerHelper.Instance.LoggerLevel <= LoggerTypes.DEBUG)
|
if (LoggerHelper.Instance.LoggerLevel <= LoggerTypes.DEBUG)
|
||||||
{
|
|
||||||
LoggerHelper.Instance.Debug($"connect relay server {ep}");
|
LoggerHelper.Instance.Debug($"connect relay server {ep}");
|
||||||
}
|
|
||||||
//连接中继服务器
|
//连接中继服务器
|
||||||
Socket socket = new Socket(ep.AddressFamily, SocketType.Stream, System.Net.Sockets.ProtocolType.Tcp);
|
Socket socket = new Socket(ep.AddressFamily, SocketType.Stream, System.Net.Sockets.ProtocolType.Tcp);
|
||||||
socket.KeepAlive();
|
socket.KeepAlive();
|
||||||
@@ -232,8 +237,13 @@ namespace linker.plugins.relay.client.transport
|
|||||||
await socket.SendAsync(new byte[] { (byte)ResolverType.Relay });
|
await socket.SendAsync(new byte[] { (byte)ResolverType.Relay });
|
||||||
await socket.SendAsync(MemoryPackSerializer.Serialize(relayMessage));
|
await socket.SendAsync(MemoryPackSerializer.Serialize(relayMessage));
|
||||||
|
|
||||||
|
if (LoggerHelper.Instance.LoggerLevel <= LoggerTypes.DEBUG) LoggerHelper.Instance.Debug($"relay connected {ep}");
|
||||||
|
|
||||||
//是否允许连接
|
//是否允许连接
|
||||||
int length = await socket.ReceiveAsync(buffer);
|
int length = await socket.ReceiveAsync(buffer.AsMemory(0, 1));
|
||||||
|
|
||||||
|
if (LoggerHelper.Instance.LoggerLevel <= LoggerTypes.DEBUG)
|
||||||
|
LoggerHelper.Instance.Debug($"relay connected {ep}->{buffer[0]}");
|
||||||
if (buffer[0] == 0)
|
if (buffer[0] == 0)
|
||||||
{
|
{
|
||||||
relayInfo.Server = node.EndPoint;
|
relayInfo.Server = node.EndPoint;
|
||||||
|
@@ -5,6 +5,8 @@ using System.Collections.Concurrent;
|
|||||||
using linker.plugins.resolver;
|
using linker.plugins.resolver;
|
||||||
using System.Net;
|
using System.Net;
|
||||||
using MemoryPack;
|
using MemoryPack;
|
||||||
|
using linker.config;
|
||||||
|
using linker.libs;
|
||||||
|
|
||||||
namespace linker.plugins.relay.server
|
namespace linker.plugins.relay.server
|
||||||
{
|
{
|
||||||
@@ -51,10 +53,12 @@ namespace linker.plugins.relay.server
|
|||||||
int length = await socket.ReceiveAsync(buffer.AsMemory(), SocketFlags.None).ConfigureAwait(false);
|
int length = await socket.ReceiveAsync(buffer.AsMemory(), SocketFlags.None).ConfigureAwait(false);
|
||||||
RelayMessage relayMessage = MemoryPackSerializer.Deserialize<RelayMessage>(buffer.AsMemory(0, length).Span);
|
RelayMessage relayMessage = MemoryPackSerializer.Deserialize<RelayMessage>(buffer.AsMemory(0, length).Span);
|
||||||
|
|
||||||
if (relayMessage.Type == RelayMessengerType.Ask)
|
if (relayMessage.Type == RelayMessengerType.Ask && relayMessage.NodeId != RelayNodeInfo.MASTER_NODE_ID)
|
||||||
{
|
{
|
||||||
if (relayServerNodeTransfer.Invalid())
|
if (relayServerNodeTransfer.Validate() == false)
|
||||||
{
|
{
|
||||||
|
if (LoggerHelper.Instance.LoggerLevel <= LoggerTypes.DEBUG)
|
||||||
|
LoggerHelper.Instance.Debug($"relay Validate false,flowid:{relayMessage.FlowId}");
|
||||||
await socket.SendAsync(new byte[] { 1 });
|
await socket.SendAsync(new byte[] { 1 });
|
||||||
socket.SafeClose();
|
socket.SafeClose();
|
||||||
return;
|
return;
|
||||||
|
@@ -75,9 +75,9 @@ namespace linker.plugins.relay.server
|
|||||||
/// 无效请求
|
/// 无效请求
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <returns></returns>
|
/// <returns></returns>
|
||||||
public bool Invalid()
|
public bool Validate()
|
||||||
{
|
{
|
||||||
return ValidateConnection() == false || ValidateBytes() == false;
|
return ValidateConnection() && ValidateBytes();
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@@ -100,7 +100,11 @@ namespace linker.plugins.relay.server
|
|||||||
/// <returns></returns>
|
/// <returns></returns>
|
||||||
public bool ValidateConnection()
|
public bool ValidateConnection()
|
||||||
{
|
{
|
||||||
return fileConfig.Data.Server.Relay.Distributed.Node.MaxConnection == 0 || fileConfig.Data.Server.Relay.Distributed.Node.MaxConnection * 2 > connectionNum;
|
bool res = fileConfig.Data.Server.Relay.Distributed.Node.MaxConnection == 0 || fileConfig.Data.Server.Relay.Distributed.Node.MaxConnection * 2 > connectionNum;
|
||||||
|
if (res == false && LoggerHelper.Instance.LoggerLevel <= LoggerTypes.DEBUG)
|
||||||
|
LoggerHelper.Instance.Debug($"relay ValidateConnection false,{connectionNum}/{fileConfig.Data.Server.Relay.Distributed.Node.MaxConnection*2}");
|
||||||
|
|
||||||
|
return res;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@@ -109,8 +113,13 @@ namespace linker.plugins.relay.server
|
|||||||
/// <returns></returns>
|
/// <returns></returns>
|
||||||
public bool ValidateBytes()
|
public bool ValidateBytes()
|
||||||
{
|
{
|
||||||
return fileConfig.Data.Server.Relay.Distributed.Node.MaxGbTotal == 0
|
bool res= fileConfig.Data.Server.Relay.Distributed.Node.MaxGbTotal == 0
|
||||||
|| (fileConfig.Data.Server.Relay.Distributed.Node.MaxGbTotal > 0 && fileConfig.Data.Server.Relay.Distributed.Node.MaxGbTotalLastBytes > 0);
|
|| (fileConfig.Data.Server.Relay.Distributed.Node.MaxGbTotal > 0 && fileConfig.Data.Server.Relay.Distributed.Node.MaxGbTotalLastBytes > 0);
|
||||||
|
|
||||||
|
if (res == false && LoggerHelper.Instance.LoggerLevel <= LoggerTypes.DEBUG)
|
||||||
|
LoggerHelper.Instance.Debug($"relay ValidateBytes false,{fileConfig.Data.Server.Relay.Distributed.Node.MaxGbTotalLastBytes}bytes/{fileConfig.Data.Server.Relay.Distributed.Node.MaxGbTotal}gb");
|
||||||
|
|
||||||
|
return res;
|
||||||
}
|
}
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 添加流量
|
/// 添加流量
|
||||||
|
@@ -13,6 +13,7 @@ using linker.plugins.socks5.config;
|
|||||||
using System.Text;
|
using System.Text;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using linker.plugins.relay.client;
|
using linker.plugins.relay.client;
|
||||||
|
using linker.client.config;
|
||||||
|
|
||||||
namespace linker.plugins.socks5
|
namespace linker.plugins.socks5
|
||||||
{
|
{
|
||||||
@@ -25,8 +26,8 @@ namespace linker.plugins.socks5
|
|||||||
|
|
||||||
protected override string TransactionId => "socks5";
|
protected override string TransactionId => "socks5";
|
||||||
|
|
||||||
public TunnelProxy(FileConfig config, TunnelTransfer tunnelTransfer, RelayTransfer relayTransfer, ClientSignInTransfer clientSignInTransfer, ClientSignInState clientSignInState)
|
public TunnelProxy(FileConfig config, TunnelTransfer tunnelTransfer, RelayTransfer relayTransfer, ClientSignInTransfer clientSignInTransfer, ClientSignInState clientSignInState, RunningConfig runningConfig)
|
||||||
: base(config, tunnelTransfer, relayTransfer, clientSignInTransfer, clientSignInState)
|
: base(config, tunnelTransfer, relayTransfer, clientSignInTransfer, clientSignInState, runningConfig)
|
||||||
{
|
{
|
||||||
TaskUdp();
|
TaskUdp();
|
||||||
}
|
}
|
||||||
|
@@ -3,6 +3,9 @@ using System.Net;
|
|||||||
using System.Buffers;
|
using System.Buffers;
|
||||||
using linker.libs.extends;
|
using linker.libs.extends;
|
||||||
using linker.plugins.resolver;
|
using linker.plugins.resolver;
|
||||||
|
using linker.libs;
|
||||||
|
using System.Text;
|
||||||
|
using System;
|
||||||
|
|
||||||
namespace linker.plugins.tunnel
|
namespace linker.plugins.tunnel
|
||||||
{
|
{
|
||||||
@@ -28,6 +31,8 @@ namespace linker.plugins.tunnel
|
|||||||
/// <returns></returns>
|
/// <returns></returns>
|
||||||
public async Task Resolve(Socket socket, IPEndPoint ep, Memory<byte> memory)
|
public async Task Resolve(Socket socket, IPEndPoint ep, Memory<byte> memory)
|
||||||
{
|
{
|
||||||
|
if (LoggerHelper.Instance.LoggerLevel <= LoggerTypes.DEBUG) LoggerHelper.Instance.Debug($"{ep} get udp external port");
|
||||||
|
|
||||||
AddReceive((ulong)memory.Length);
|
AddReceive((ulong)memory.Length);
|
||||||
byte[] sendData = ArrayPool<byte>.Shared.Rent(20);
|
byte[] sendData = ArrayPool<byte>.Shared.Rent(20);
|
||||||
try
|
try
|
||||||
@@ -36,8 +41,9 @@ namespace linker.plugins.tunnel
|
|||||||
AddSendt((ulong)send.Length);
|
AddSendt((ulong)send.Length);
|
||||||
await socket.SendToAsync(send, SocketFlags.None, ep).ConfigureAwait(false);
|
await socket.SendToAsync(send, SocketFlags.None, ep).ConfigureAwait(false);
|
||||||
}
|
}
|
||||||
catch (Exception)
|
catch (Exception ex)
|
||||||
{
|
{
|
||||||
|
if (LoggerHelper.Instance.LoggerLevel <= LoggerTypes.DEBUG) LoggerHelper.Instance.Error(ex);
|
||||||
}
|
}
|
||||||
finally
|
finally
|
||||||
{
|
{
|
||||||
@@ -51,15 +57,17 @@ namespace linker.plugins.tunnel
|
|||||||
/// <returns></returns>
|
/// <returns></returns>
|
||||||
public async Task Resolve(Socket socket, Memory<byte> memory)
|
public async Task Resolve(Socket socket, Memory<byte> memory)
|
||||||
{
|
{
|
||||||
byte[] sendData = ArrayPool<byte>.Shared.Rent(20);
|
if (LoggerHelper.Instance.LoggerLevel <= LoggerTypes.DEBUG) LoggerHelper.Instance.Debug($"{socket.RemoteEndPoint} get tcp external port");
|
||||||
|
byte[] sendData = ArrayPool<byte>.Shared.Rent(1024);
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
memory = BuildSendData(sendData, socket.RemoteEndPoint as IPEndPoint);
|
memory = BuildSendData(sendData, socket.RemoteEndPoint as IPEndPoint);
|
||||||
AddSendt((ulong)memory.Length);
|
AddSendt((ulong)memory.Length);
|
||||||
await socket.SendAsync(memory, SocketFlags.None).ConfigureAwait(false);
|
await socket.SendAsync(memory, SocketFlags.None).ConfigureAwait(false);
|
||||||
}
|
}
|
||||||
catch (Exception)
|
catch (Exception ex)
|
||||||
{
|
{
|
||||||
|
if (LoggerHelper.Instance.LoggerLevel <= LoggerTypes.DEBUG) LoggerHelper.Instance.Error(ex);
|
||||||
}
|
}
|
||||||
finally
|
finally
|
||||||
{
|
{
|
||||||
@@ -78,7 +86,11 @@ namespace linker.plugins.tunnel
|
|||||||
{
|
{
|
||||||
data[i] = (byte)(data[i] ^ byte.MaxValue);
|
data[i] = (byte)(data[i] ^ byte.MaxValue);
|
||||||
}
|
}
|
||||||
return data.AsMemory(0, 1 + length + 2);
|
|
||||||
|
byte[] temp = Encoding.UTF8.GetBytes(Environment.TickCount64.ToString().Md5().SubStr(0, new Random().Next(16, 32)));
|
||||||
|
temp.AsMemory().CopyTo(data.AsMemory(1 + length + 2));
|
||||||
|
|
||||||
|
return data.AsMemory(0, 1 + length + 2 + temp.Length);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -6,6 +6,7 @@ using linker.tunnel;
|
|||||||
using linker.tunnel.connection;
|
using linker.tunnel.connection;
|
||||||
using System.Collections.Concurrent;
|
using System.Collections.Concurrent;
|
||||||
using linker.plugins.relay.client;
|
using linker.plugins.relay.client;
|
||||||
|
using linker.client.config;
|
||||||
|
|
||||||
namespace linker.plugins.tunnel
|
namespace linker.plugins.tunnel
|
||||||
{
|
{
|
||||||
@@ -21,16 +22,18 @@ namespace linker.plugins.tunnel
|
|||||||
private readonly RelayTransfer relayTransfer;
|
private readonly RelayTransfer relayTransfer;
|
||||||
private readonly ClientSignInTransfer clientSignInTransfer;
|
private readonly ClientSignInTransfer clientSignInTransfer;
|
||||||
private readonly ClientSignInState clientSignInState;
|
private readonly ClientSignInState clientSignInState;
|
||||||
|
private readonly RunningConfig runningConfig;
|
||||||
|
|
||||||
private uint maxTimes = 3;
|
private uint maxTimes = 3;
|
||||||
|
|
||||||
public TunnelBase(FileConfig config, TunnelTransfer tunnelTransfer, RelayTransfer relayTransfer, ClientSignInTransfer clientSignInTransfer, ClientSignInState clientSignInState)
|
public TunnelBase(FileConfig config, TunnelTransfer tunnelTransfer, RelayTransfer relayTransfer, ClientSignInTransfer clientSignInTransfer, ClientSignInState clientSignInState, RunningConfig runningConfig)
|
||||||
{
|
{
|
||||||
this.config = config;
|
this.config = config;
|
||||||
this.tunnelTransfer = tunnelTransfer;
|
this.tunnelTransfer = tunnelTransfer;
|
||||||
this.relayTransfer = relayTransfer;
|
this.relayTransfer = relayTransfer;
|
||||||
this.clientSignInTransfer = clientSignInTransfer;
|
this.clientSignInTransfer = clientSignInTransfer;
|
||||||
this.clientSignInState = clientSignInState;
|
this.clientSignInState = clientSignInState;
|
||||||
|
this.runningConfig = runningConfig;
|
||||||
|
|
||||||
//监听打洞成功
|
//监听打洞成功
|
||||||
tunnelTransfer.SetConnectedCallback(TransactionId, OnConnected);
|
tunnelTransfer.SetConnectedCallback(TransactionId, OnConnected);
|
||||||
|
@@ -4,7 +4,6 @@ using linker.libs;
|
|||||||
using linker.plugins.client;
|
using linker.plugins.client;
|
||||||
using linker.plugins.decenter;
|
using linker.plugins.decenter;
|
||||||
using linker.plugins.messenger;
|
using linker.plugins.messenger;
|
||||||
using linker.plugins.tunnel.messenger;
|
|
||||||
using linker.tunnel;
|
using linker.tunnel;
|
||||||
using linker.tunnel.adapter;
|
using linker.tunnel.adapter;
|
||||||
using MemoryPack;
|
using MemoryPack;
|
||||||
|
@@ -29,6 +29,7 @@ namespace linker.client.config
|
|||||||
|
|
||||||
public int PortMapWan { get; set; }
|
public int PortMapWan { get; set; }
|
||||||
public int PortMapLan { get; set; }
|
public int PortMapLan { get; set; }
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
[MemoryPackable]
|
[MemoryPackable]
|
||||||
|
@@ -32,7 +32,7 @@ namespace linker.plugins.tuntap
|
|||||||
private readonly ClientSignInState clientSignInState;
|
private readonly ClientSignInState clientSignInState;
|
||||||
|
|
||||||
public TuntapProxy(FileConfig config, RunningConfig runningConfig, TunnelTransfer tunnelTransfer, RelayTransfer relayTransfer, ClientSignInTransfer clientSignInTransfer, LinkerTunDeviceAdapter linkerTunDeviceAdapter, ClientSignInState clientSignInState)
|
public TuntapProxy(FileConfig config, RunningConfig runningConfig, TunnelTransfer tunnelTransfer, RelayTransfer relayTransfer, ClientSignInTransfer clientSignInTransfer, LinkerTunDeviceAdapter linkerTunDeviceAdapter, ClientSignInState clientSignInState)
|
||||||
: base(config, tunnelTransfer, relayTransfer, clientSignInTransfer, clientSignInState)
|
: base(config, tunnelTransfer, relayTransfer, clientSignInTransfer, clientSignInState, runningConfig)
|
||||||
{
|
{
|
||||||
this.config = config;
|
this.config = config;
|
||||||
this.runningConfig = runningConfig;
|
this.runningConfig = runningConfig;
|
||||||
|
@@ -33,16 +33,16 @@ do
|
|||||||
fi
|
fi
|
||||||
done
|
done
|
||||||
cd public/publish/docker/linux-${p}-x64/${f}
|
cd public/publish/docker/linux-${p}-x64/${f}
|
||||||
docker buildx build -f ${target}/public/publish/docker/linux-${p}-x64/${f}/Dockerfile-${p} --platform="linux/x86_64" --force-rm -t "${image}-${p}-x64:latest" -t "${image}-${p}-x64:v1.5.9" . --push
|
docker buildx build -f ${target}/public/publish/docker/linux-${p}-x64/${f}/Dockerfile-${p} --platform="linux/x86_64" --force-rm -t "${image}-${p}-x64:latest" -t "${image}-${p}-x64:v1.6.0" . --push
|
||||||
cd ../../../../../
|
cd ../../../../../
|
||||||
|
|
||||||
|
|
||||||
cd public/publish/docker/linux-${p}-arm64/${f}
|
cd public/publish/docker/linux-${p}-arm64/${f}
|
||||||
docker buildx build -f ${target}/public/publish/docker/linux-${p}-arm64/${f}/Dockerfile-${p} --platform="linux/arm64" --force-rm -t "${image}-${p}-arm64:latest" -t "${image}-${p}-arm64:v1.5.9" . --push
|
docker buildx build -f ${target}/public/publish/docker/linux-${p}-arm64/${f}/Dockerfile-${p} --platform="linux/arm64" --force-rm -t "${image}-${p}-arm64:latest" -t "${image}-${p}-arm64:v1.6.0" . --push
|
||||||
cd ../../../../../
|
cd ../../../../../
|
||||||
|
|
||||||
cd public/publish/docker/linux-${p}-arm/${f}
|
cd public/publish/docker/linux-${p}-arm/${f}
|
||||||
docker buildx build -f ${target}/public/publish/docker/linux-${p}-arm/${f}/Dockerfile-${p} --platform="linux/arm/v7" --force-rm -t "${image}-${p}-arm:latest" -t "${image}-${p}-arm:v1.5.9" . --push
|
docker buildx build -f ${target}/public/publish/docker/linux-${p}-arm/${f}/Dockerfile-${p} --platform="linux/arm/v7" --force-rm -t "${image}-${p}-arm:latest" -t "${image}-${p}-arm:v1.6.0" . --push
|
||||||
cd ../../../../../
|
cd ../../../../../
|
||||||
done
|
done
|
||||||
done
|
done
|
11
version.txt
11
version.txt
@@ -1,6 +1,5 @@
|
|||||||
v1.5.9
|
v1.6.0
|
||||||
2024-11-22 17:23:21
|
2024-11-29 00:49:22
|
||||||
1. 优化UI
|
1. 优化websocket(某些代理环境下可能请求头key名称不规范)
|
||||||
2. 优化网卡
|
2. 尝试解决UDP获取外网IP端口失败的问题
|
||||||
3. 修复网卡禁用自动重启
|
3. 测试中
|
||||||
4. 增加一键安装脚本
|
|
Reference in New Issue
Block a user