310 Commits

Author SHA1 Message Date
Senis John
0254c6c557 Update docker.yml 2023-10-09 10:06:46 +08:00
pings
4c52e33adb 对接GoV2Panel面板 (#474)
* Update actions

* up

---------

Co-authored-by: thank243 <thank243@gmail.com>
2023-10-08 12:56:26 +08:00
thank243
b8d40c201b Update actions 2023-10-08 12:06:18 +08:00
Senis John
dbd4a85a6c Update Mergo package and Go version
Replaced the Mergo package from "github.com/imdario/mergo" to "dario.cat/mergo". This change was necessary due to the new release from the Mergo package which includes bug fixes and performance improvements.
Moreover, updated the Go version from 1.20 to 1.21.1, including the toolchain version to stay up to date with the latest language features and bug fixes.
Lastly, other dependencies were updated and cleaned up for maintaining package stability and compatibility.
2023-10-08 09:21:39 +08:00
dependabot[bot]
8f28716b21 build(deps): bump github.com/spf13/viper from 1.16.0 to 1.17.0 (#469)
Bumps [github.com/spf13/viper](https://github.com/spf13/viper) from 1.16.0 to 1.17.0.
- [Release notes](https://github.com/spf13/viper/releases)
- [Commits](https://github.com/spf13/viper/compare/v1.16.0...v1.17.0)

---
updated-dependencies:
- dependency-name: github.com/spf13/viper
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-10-07 13:10:53 +08:00
Senis John
73bc37cb51 Add EnableREALITY field and update REALITYConfig parsing
Enabled the REALITY feature on both model.go and sspanel.go for the api and service assemblies and updated the inboundbuilder to switch the REALITY feature based on the new field and condition statements. This enhancement allows finer control and visibility of the REALITY feature's activation.

In addition, the REALITYConfig in config.go was updated to remove the DisableLocal field and add the DisableLocalREALITYConfig field. The main/config.yml.example file has also been updated to reflect these changes and provide users with a clearer understanding of the config setup.
2023-10-04 23:08:59 +08:00
M1Screw
78f2f88296 feat: update sspanel model (#467) 2023-10-04 20:28:03 +08:00
Senis John
42a2226769 Enhanced ssPanel API by adding panel version check and improving custom config parsing
Added a function to compare the ssPanel version with the baseline version and inform users if their panel version is outdated. Made changes to improve readability and clarity in variable naming and error handling. The handling of the custom config feature has also been significantly optimized, providing more efficient use and easier debugging of parsing issues. The overall changes lead to better compatibility with different ssPanel versions and a more user-friendly custom config feature.
2023-10-04 16:50:52 +08:00
M1Screw
71aba0601e feat: unify trojan transport & add ss2022 related config (#465) 2023-10-04 10:44:45 +08:00
Senis John
5fe18e020d Refactor sspanel and test files in the API
Fixed typos, changed variable naming to follow camel case convention, simplified the error handling, and refactored the function names in sspanel.go for improved readability and consistency. Accordingly, updated the test function names in sspanel_test.go to match the changes made in the main file. Additionally, the if condition in ParseSSPanelNodeInfo was streamlined to improve the function's readability and error handling. The changes will help in better understanding the code while debugging or adding new features.
2023-10-04 09:45:52 +08:00
dependabot[bot]
a0f2730bb2 build(deps): bump github.com/shirou/gopsutil/v3 from 3.23.8 to 3.23.9 (#462)
Bumps [github.com/shirou/gopsutil/v3](https://github.com/shirou/gopsutil) from 3.23.8 to 3.23.9.
- [Release notes](https://github.com/shirou/gopsutil/releases)
- [Commits](https://github.com/shirou/gopsutil/compare/v3.23.8...v3.23.9)

---
updated-dependencies:
- dependency-name: github.com/shirou/gopsutil/v3
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-10-03 08:28:04 +08:00
dependabot[bot]
0c10c59877 build(deps): bump github.com/sagernet/sing from 0.2.11 to 0.2.12 (#461)
Bumps [github.com/sagernet/sing](https://github.com/sagernet/sing) from 0.2.11 to 0.2.12.
- [Commits](https://github.com/sagernet/sing/compare/v0.2.11...v0.2.12)

---
updated-dependencies:
- dependency-name: github.com/sagernet/sing
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-10-03 08:27:55 +08:00
dependabot[bot]
119e4810f2 build(deps): bump github.com/go-resty/resty/v2 from 2.8.0 to 2.9.1 (#463)
Bumps [github.com/go-resty/resty/v2](https://github.com/go-resty/resty) from 2.8.0 to 2.9.1.
- [Release notes](https://github.com/go-resty/resty/releases)
- [Commits](https://github.com/go-resty/resty/compare/v2.8.0...v2.9.1)

---
updated-dependencies:
- dependency-name: github.com/go-resty/resty/v2
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-10-03 08:27:45 +08:00
dependabot[bot]
5dad910488 build(deps): bump github.com/redis/go-redis/v9 from 9.2.0 to 9.2.1 (#460)
Bumps [github.com/redis/go-redis/v9](https://github.com/redis/go-redis) from 9.2.0 to 9.2.1.
- [Release notes](https://github.com/redis/go-redis/releases)
- [Changelog](https://github.com/redis/go-redis/blob/master/CHANGELOG.md)
- [Commits](https://github.com/redis/go-redis/compare/v9.2.0...v9.2.1)

---
updated-dependencies:
- dependency-name: github.com/redis/go-redis/v9
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-10-02 23:29:38 +08:00
dependabot[bot]
8841e55f70 build(deps): bump github.com/sagernet/sing-shadowsocks (#458)
Bumps [github.com/sagernet/sing-shadowsocks](https://github.com/sagernet/sing-shadowsocks) from 0.2.4 to 0.2.5.
- [Commits](https://github.com/sagernet/sing-shadowsocks/compare/v0.2.4...v0.2.5)

---
updated-dependencies:
- dependency-name: github.com/sagernet/sing-shadowsocks
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-09-27 17:36:02 +08:00
dependabot[bot]
5d20732881 build(deps): bump github.com/sagernet/sing from 0.2.9 to 0.2.11 (#455)
Bumps [github.com/sagernet/sing](https://github.com/sagernet/sing) from 0.2.9 to 0.2.11.
- [Commits](https://github.com/sagernet/sing/compare/v0.2.9...v0.2.11)

---
updated-dependencies:
- dependency-name: github.com/sagernet/sing
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-09-21 23:30:29 +08:00
dependabot[bot]
d579933451 build(deps): bump github.com/go-acme/lego/v4 from 4.14.0 to 4.14.2 (#456)
Bumps [github.com/go-acme/lego/v4](https://github.com/go-acme/lego) from 4.14.0 to 4.14.2.
- [Release notes](https://github.com/go-acme/lego/releases)
- [Changelog](https://github.com/go-acme/lego/blob/master/CHANGELOG.md)
- [Commits](https://github.com/go-acme/lego/compare/v4.14.0...v4.14.2)

---
updated-dependencies:
- dependency-name: github.com/go-acme/lego/v4
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-09-21 08:20:31 +08:00
dependabot[bot]
46e836f93b build(deps): bump github.com/redis/go-redis/v9 from 9.1.0 to 9.2.0 (#457)
Bumps [github.com/redis/go-redis/v9](https://github.com/redis/go-redis) from 9.1.0 to 9.2.0.
- [Release notes](https://github.com/redis/go-redis/releases)
- [Changelog](https://github.com/redis/go-redis/blob/master/CHANGELOG.md)
- [Commits](https://github.com/redis/go-redis/compare/v9.1.0...v9.2.0)

---
updated-dependencies:
- dependency-name: github.com/redis/go-redis/v9
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-09-21 08:20:21 +08:00
dependabot[bot]
e27b0c6cd8 build(deps): bump github.com/go-resty/resty/v2 from 2.7.0 to 2.8.0 (#453)
Bumps [github.com/go-resty/resty/v2](https://github.com/go-resty/resty) from 2.7.0 to 2.8.0.
- [Release notes](https://github.com/go-resty/resty/releases)
- [Commits](https://github.com/go-resty/resty/compare/v2.7.0...v2.8.0)

---
updated-dependencies:
- dependency-name: github.com/go-resty/resty/v2
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-09-19 01:53:53 +08:00
Senis John
e34a3b4a94 Update dependencies and replace go-redis with redis library
Replaced go-redis older version with a more updated version of redis library in limiter.go and go.mod files. This change will help to improve the performance and efficiency of the existing code. Updated the versions of 'gocache/store/redis/v4' and 'bsm/gomega' in go.sum and go.mod files to keep the project's dependencies up-to-date and fix potential security vulnerabilities. Removed the unnecessary indirect dependency 'go-redis/redis/v8' from go.sum file. Added 'redis/go-redis/v9' instead, to ensure compatibility with the newer version of Redis.
2023-09-12 17:28:27 +08:00
Senis John
b366171401 Update golang.org/x/exp and golang.org/x/tools versions
Updated versions for golang.org/x/exp and golang.org/x/tools in go.sum and go.mod files. It is crucial to keep these dependencies up-to-date for maintaining the functionality of the project as well as addressing potential security vulnerabilities.
2023-09-12 07:06:49 +08:00
dependabot[bot]
b034a2d48f build(deps): bump github.com/eko/gocache/store/go_cache/v4 (#449)
Bumps [github.com/eko/gocache/store/go_cache/v4](https://github.com/eko/gocache) from 4.2.0 to 4.2.1.
- [Release notes](https://github.com/eko/gocache/releases)
- [Commits](https://github.com/eko/gocache/compare/store/redis/v4.2.0...store/redis/v4.2.1)

---
updated-dependencies:
- dependency-name: github.com/eko/gocache/store/go_cache/v4
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-09-12 06:22:38 +08:00
dependabot[bot]
65b25ed3f6 build(deps): bump github.com/eko/gocache/lib/v4 from 4.1.4 to 4.1.5 (#448)
Bumps [github.com/eko/gocache/lib/v4](https://github.com/eko/gocache) from 4.1.4 to 4.1.5.
- [Release notes](https://github.com/eko/gocache/releases)
- [Commits](https://github.com/eko/gocache/compare/lib/v4.1.4...lib/v4.1.5)

---
updated-dependencies:
- dependency-name: github.com/eko/gocache/lib/v4
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-09-12 05:54:06 +08:00
dependabot[bot]
b65dfd7f92 build(deps): bump golang.org/x/net from 0.14.0 to 0.15.0 (#446)
Bumps [golang.org/x/net](https://github.com/golang/net) from 0.14.0 to 0.15.0.
- [Commits](https://github.com/golang/net/compare/v0.14.0...v0.15.0)

---
updated-dependencies:
- dependency-name: golang.org/x/net
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-09-07 05:00:29 +08:00
Senis John
6569a0bf36 Updated several Go dependencies in go.sum
Upgraded the version of multiple dependencies listed in go.sum to incorporate bug fixes, improvements and deal with any potential security vulnerabilities. Key changes include an update to packages such as github.com/BurntSushi/toml and github.com/aws/aws-sdk-go-v2 among others.
2023-09-04 09:59:14 +08:00
dependabot[bot]
e5e5e4ef92 build(deps): bump github.com/shirou/gopsutil/v3 from 3.23.7 to 3.23.8 (#442)
Bumps [github.com/shirou/gopsutil/v3](https://github.com/shirou/gopsutil) from 3.23.7 to 3.23.8.
- [Release notes](https://github.com/shirou/gopsutil/releases)
- [Commits](https://github.com/shirou/gopsutil/compare/v3.23.7...v3.23.8)

---
updated-dependencies:
- dependency-name: github.com/shirou/gopsutil/v3
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-09-04 09:39:47 +08:00
Senis John
f35a056cc6 Update several dependencies for bug fixes and improvements
This commit includes updates to several dependencies in go.sum file in order to incorporate bug fixes and performance improvements. The updated dependencies include 'cloud.google.com/go/compute', 'github.com/gaukas/godicttls', 'github.com/google/pprof' and 'github.com/xtls/xray-core' among others.

These updates are crucial to ensure the project runs efficiently and securely, with all known issues in these dependencies addressed in their latest versions.
2023-09-04 08:53:08 +08:00
dependabot[bot]
73ffd3f505 build(deps): bump github.com/eko/gocache/lib/v4 from 4.1.3 to 4.1.4 (#433)
Bumps [github.com/eko/gocache/lib/v4](https://github.com/eko/gocache) from 4.1.3 to 4.1.4.
- [Release notes](https://github.com/eko/gocache/releases)
- [Commits](https://github.com/eko/gocache/compare/lib/v4.1.3...lib/v4.1.4)

---
updated-dependencies:
- dependency-name: github.com/eko/gocache/lib/v4
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-08-08 23:37:35 +08:00
dependabot[bot]
73c5e28f41 build(deps): bump github.com/eko/gocache/store/go_cache/v4 (#432)
Bumps [github.com/eko/gocache/store/go_cache/v4](https://github.com/eko/gocache) from 4.1.2 to 4.2.0.
- [Release notes](https://github.com/eko/gocache/releases)
- [Commits](https://github.com/eko/gocache/compare/lib/v4.1.2...store/redis/v4.2.0)

---
updated-dependencies:
- dependency-name: github.com/eko/gocache/store/go_cache/v4
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-08-08 01:17:18 +08:00
dependabot[bot]
f1b45c02f4 build(deps): bump golang.org/x/net from 0.13.0 to 0.14.0 (#426)
Bumps [golang.org/x/net](https://github.com/golang/net) from 0.13.0 to 0.14.0.
- [Commits](https://github.com/golang/net/compare/v0.13.0...v0.14.0)

---
updated-dependencies:
- dependency-name: golang.org/x/net
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-08-05 16:17:56 +08:00
dependabot[bot]
941f256ba5 build(deps): bump golang.org/x/net from 0.12.0 to 0.13.0 (#423)
Bumps [golang.org/x/net](https://github.com/golang/net) from 0.12.0 to 0.13.0.
- [Commits](https://github.com/golang/net/compare/v0.12.0...v0.13.0)

---
updated-dependencies:
- dependency-name: golang.org/x/net
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-08-05 14:00:23 +08:00
dependabot[bot]
2e547afdb7 build(deps): bump github.com/shirou/gopsutil/v3 from 3.23.6 to 3.23.7 (#421)
Bumps [github.com/shirou/gopsutil/v3](https://github.com/shirou/gopsutil) from 3.23.6 to 3.23.7.
- [Release notes](https://github.com/shirou/gopsutil/releases)
- [Commits](https://github.com/shirou/gopsutil/compare/v3.23.6...v3.23.7)

---
updated-dependencies:
- dependency-name: github.com/shirou/gopsutil/v3
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-08-05 14:00:13 +08:00
dependabot[bot]
422ed1a311 build(deps): bump github.com/go-acme/lego/v4 from 4.13.2 to 4.13.3 (#418)
Bumps [github.com/go-acme/lego/v4](https://github.com/go-acme/lego) from 4.13.2 to 4.13.3.
- [Release notes](https://github.com/go-acme/lego/releases)
- [Changelog](https://github.com/go-acme/lego/blob/master/CHANGELOG.md)
- [Commits](https://github.com/go-acme/lego/compare/v4.13.2...v4.13.3)

---
updated-dependencies:
- dependency-name: github.com/go-acme/lego/v4
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-07-27 08:08:09 +08:00
dependabot[bot]
214e412993 build(deps): bump github.com/sagernet/sing-shadowsocks (#415)
Bumps [github.com/sagernet/sing-shadowsocks](https://github.com/sagernet/sing-shadowsocks) from 0.2.2 to 0.2.4.
- [Commits](https://github.com/sagernet/sing-shadowsocks/compare/v0.2.2...v0.2.4)

---
updated-dependencies:
- dependency-name: github.com/sagernet/sing-shadowsocks
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-07-24 22:32:14 +08:00
dependabot[bot]
f85f6b47ee build(deps): bump github.com/go-acme/lego/v4 from 4.13.1 to 4.13.2 (#414)
Bumps [github.com/go-acme/lego/v4](https://github.com/go-acme/lego) from 4.13.1 to 4.13.2.
- [Release notes](https://github.com/go-acme/lego/releases)
- [Changelog](https://github.com/go-acme/lego/blob/master/CHANGELOG.md)
- [Commits](https://github.com/go-acme/lego/compare/v4.13.1...v4.13.2)

---
updated-dependencies:
- dependency-name: github.com/go-acme/lego/v4
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-07-24 22:32:05 +08:00
dependabot[bot]
7e09aef1cc build(deps): bump github.com/go-acme/lego/v4 from 4.12.3 to 4.13.1 (#411)
Bumps [github.com/go-acme/lego/v4](https://github.com/go-acme/lego) from 4.12.3 to 4.13.1.
- [Release notes](https://github.com/go-acme/lego/releases)
- [Changelog](https://github.com/go-acme/lego/blob/master/CHANGELOG.md)
- [Commits](https://github.com/go-acme/lego/compare/v4.12.3...v4.13.1)

---
updated-dependencies:
- dependency-name: github.com/go-acme/lego/v4
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-07-23 21:07:59 +08:00
dependabot[bot]
0b4caba8f6 build(deps): bump golang.org/x/net from 0.11.0 to 0.12.0 (#403)
Bumps [golang.org/x/net](https://github.com/golang/net) from 0.11.0 to 0.12.0.
- [Commits](https://github.com/golang/net/compare/v0.11.0...v0.12.0)

---
updated-dependencies:
- dependency-name: golang.org/x/net
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-07-11 09:58:05 +08:00
dependabot[bot]
b0866011e5 build(deps): bump github.com/shirou/gopsutil/v3 from 3.23.5 to 3.23.6 (#401)
Bumps [github.com/shirou/gopsutil/v3](https://github.com/shirou/gopsutil) from 3.23.5 to 3.23.6.
- [Release notes](https://github.com/shirou/gopsutil/releases)
- [Commits](https://github.com/shirou/gopsutil/compare/v3.23.5...v3.23.6)

---
updated-dependencies:
- dependency-name: github.com/shirou/gopsutil/v3
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-07-06 07:53:23 +08:00
dependabot[bot]
4d1d89b837 build(deps): bump github.com/sagernet/sing from 0.2.6 to 0.2.7 (#398)
Bumps [github.com/sagernet/sing](https://github.com/sagernet/sing) from 0.2.6 to 0.2.7.
- [Commits](https://github.com/sagernet/sing/compare/v0.2.6...v0.2.7)

---
updated-dependencies:
- dependency-name: github.com/sagernet/sing
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-06-27 06:17:09 +08:00
dependabot[bot]
0febf96021 build(deps): bump google.golang.org/protobuf from 1.30.0 to 1.31.0 (#397)
Bumps google.golang.org/protobuf from 1.30.0 to 1.31.0.

---
updated-dependencies:
- dependency-name: google.golang.org/protobuf
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-06-27 06:16:47 +08:00
Senis John
0f10e837e4 fix: typo 2023-06-25 12:05:14 +08:00
Senis John
5ab352f9c9 update: remove old v2board api
update: update xray-core to 1.8.3
alterID is not use anymore
2023-06-25 07:21:27 +08:00
OPPO9008
5d5470a919 修复http伪装配置解析 (#389)
根据sspanel代码,headerType 才是下发订阅的参数
2023-06-21 07:14:20 +08:00
dependabot[bot]
db27722bbc build(deps): bump github.com/go-acme/lego/v4 from 4.12.2 to 4.12.3 (#395)
Bumps [github.com/go-acme/lego/v4](https://github.com/go-acme/lego) from 4.12.2 to 4.12.3.
- [Release notes](https://github.com/go-acme/lego/releases)
- [Changelog](https://github.com/go-acme/lego/blob/master/CHANGELOG.md)
- [Commits](https://github.com/go-acme/lego/compare/v4.12.2...v4.12.3)

---
updated-dependencies:
- dependency-name: github.com/go-acme/lego/v4
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-06-21 07:13:58 +08:00
dependabot[bot]
914510c687 build(deps): bump github.com/sagernet/sing from 0.2.3 to 0.2.6 (#394)
Bumps [github.com/sagernet/sing](https://github.com/sagernet/sing) from 0.2.3 to 0.2.6.
- [Commits](https://github.com/sagernet/sing/compare/v0.2.3...v0.2.6)

---
updated-dependencies:
- dependency-name: github.com/sagernet/sing
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-06-21 07:13:41 +08:00
dependabot[bot]
a8226b01e2 build(deps): bump github.com/go-acme/lego/v4 from 4.12.1 to 4.12.2 (#393)
Bumps [github.com/go-acme/lego/v4](https://github.com/go-acme/lego) from 4.12.1 to 4.12.2.
- [Release notes](https://github.com/go-acme/lego/releases)
- [Changelog](https://github.com/go-acme/lego/blob/master/CHANGELOG.md)
- [Commits](https://github.com/go-acme/lego/compare/v4.12.1...v4.12.2)

---
updated-dependencies:
- dependency-name: github.com/go-acme/lego/v4
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-06-20 08:08:50 +08:00
dependabot[bot]
54a958f39c build(deps): bump github.com/sagernet/sing-shadowsocks (#392)
Bumps [github.com/sagernet/sing-shadowsocks](https://github.com/sagernet/sing-shadowsocks) from 0.2.1 to 0.2.2.
- [Commits](https://github.com/sagernet/sing-shadowsocks/compare/v0.2.1...v0.2.2)

---
updated-dependencies:
- dependency-name: github.com/sagernet/sing-shadowsocks
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-06-20 08:08:37 +08:00
dependabot[bot]
a1e407e18f build(deps): bump golang.org/x/net from 0.10.0 to 0.11.0 (#387)
Bumps [golang.org/x/net](https://github.com/golang/net) from 0.10.0 to 0.11.0.
- [Commits](https://github.com/golang/net/compare/v0.10.0...v0.11.0)

---
updated-dependencies:
- dependency-name: golang.org/x/net
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-06-14 05:38:39 +08:00
Senis John
42da6c155d fix: unexceptional etags handle 2023-06-11 20:07:43 +08:00
dependabot[bot]
89f2342a42 build(deps): bump github.com/go-acme/lego/v4 from 4.12.0 to 4.12.1 (#380)
Bumps [github.com/go-acme/lego/v4](https://github.com/go-acme/lego) from 4.12.0 to 4.12.1.
- [Release notes](https://github.com/go-acme/lego/releases)
- [Changelog](https://github.com/go-acme/lego/blob/master/CHANGELOG.md)
- [Commits](https://github.com/go-acme/lego/compare/v4.12.0...v4.12.1)

---
updated-dependencies:
- dependency-name: github.com/go-acme/lego/v4
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-06-08 00:06:31 +08:00
dependabot[bot]
ee1a606888 build(deps): bump github.com/bitly/go-simplejson from 0.5.0 to 0.5.1 (#379)
Bumps [github.com/bitly/go-simplejson](https://github.com/bitly/go-simplejson) from 0.5.0 to 0.5.1.
- [Release notes](https://github.com/bitly/go-simplejson/releases)
- [Commits](https://github.com/bitly/go-simplejson/compare/v0.5.0...v0.5.1)

---
updated-dependencies:
- dependency-name: github.com/bitly/go-simplejson
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-06-07 09:59:16 +08:00
dependabot[bot]
c183c6492e build(deps): bump github.com/shirou/gopsutil/v3 from 3.23.4 to 3.23.5 (#373)
Bumps [github.com/shirou/gopsutil/v3](https://github.com/shirou/gopsutil) from 3.23.4 to 3.23.5.
- [Release notes](https://github.com/shirou/gopsutil/releases)
- [Commits](https://github.com/shirou/gopsutil/compare/v3.23.4...v3.23.5)

---
updated-dependencies:
- dependency-name: github.com/shirou/gopsutil/v3
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-06-02 18:33:12 +08:00
OPPO9008
a70a0d9a31 初步适配sspanel ETag 功能 (#309)
在最新sspanel 版本 M1Screw 应该找到正确下发Etag的办法了,如果你是旧版本,可以尝试
修改
res.Header().Get("ETag")
为
res.Header().Get("webapi-etag")

Co-authored-by: admin <admin@justcn2.top>
2023-06-01 18:09:08 +08:00
dependabot[bot]
4c651e15fa build(deps): bump github.com/stretchr/testify from 1.8.3 to 1.8.4 (#369)
Bumps [github.com/stretchr/testify](https://github.com/stretchr/testify) from 1.8.3 to 1.8.4.
- [Release notes](https://github.com/stretchr/testify/releases)
- [Commits](https://github.com/stretchr/testify/compare/v1.8.3...v1.8.4)

---
updated-dependencies:
- dependency-name: github.com/stretchr/testify
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-06-01 18:07:41 +08:00
dependabot[bot]
dda00c5dd6 build(deps): bump github.com/spf13/viper from 1.15.0 to 1.16.0 (#368)
Bumps [github.com/spf13/viper](https://github.com/spf13/viper) from 1.15.0 to 1.16.0.
- [Release notes](https://github.com/spf13/viper/releases)
- [Commits](https://github.com/spf13/viper/compare/v1.15.0...v1.16.0)

---
updated-dependencies:
- dependency-name: github.com/spf13/viper
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-05-31 00:27:41 +08:00
dependabot[bot]
28e1b82320 build(deps): bump github.com/imdario/mergo from 0.3.13 to 0.3.16 (#365)
Bumps [github.com/imdario/mergo](https://github.com/imdario/mergo) from 0.3.13 to 0.3.16.
- [Release notes](https://github.com/imdario/mergo/releases)
- [Commits](https://github.com/imdario/mergo/compare/v0.3.13...v0.3.16)

---
updated-dependencies:
- dependency-name: github.com/imdario/mergo
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-05-30 00:47:59 +08:00
dependabot[bot]
f1ab2eac13 build(deps): bump github.com/go-acme/lego/v4 from 4.10.2 to 4.12.0 (#364)
Bumps [github.com/go-acme/lego/v4](https://github.com/go-acme/lego) from 4.10.2 to 4.12.0.
- [Release notes](https://github.com/go-acme/lego/releases)
- [Changelog](https://github.com/go-acme/lego/blob/master/CHANGELOG.md)
- [Commits](https://github.com/go-acme/lego/compare/v4.10.2...v4.12.0)

---
updated-dependencies:
- dependency-name: github.com/go-acme/lego/v4
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-05-30 00:47:50 +08:00
Senis John
4f13aac094 fix: sspanel custom flow config doesn't work 2023-05-25 21:08:56 +08:00
Senis John
4ab196ad29 fix: local vless flow config doesn't work 2023-05-25 20:39:46 +08:00
Senis John
9590697c29 update: switch vless flow on local config 2023-05-25 10:12:16 +08:00
Senis John
0bec6c4fdf fix: vless and trojan flow mode 2023-05-24 20:44:26 +08:00
dependabot[bot]
223589ba14 build(deps): bump github.com/stretchr/testify from 1.8.2 to 1.8.3 (#355)
Bumps [github.com/stretchr/testify](https://github.com/stretchr/testify) from 1.8.2 to 1.8.3.
- [Release notes](https://github.com/stretchr/testify/releases)
- [Commits](https://github.com/stretchr/testify/compare/v1.8.2...v1.8.3)

---
updated-dependencies:
- dependency-name: github.com/stretchr/testify
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-05-20 00:03:43 +08:00
dependabot[bot]
6ba7fe2776 build(deps): bump golang.org/x/net from 0.9.0 to 0.10.0 (#346)
Bumps [golang.org/x/net](https://github.com/golang/net) from 0.9.0 to 0.10.0.
- [Commits](https://github.com/golang/net/compare/v0.9.0...v0.10.0)

---
updated-dependencies:
- dependency-name: golang.org/x/net
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-05-20 00:03:16 +08:00
Senis John
b49798ab16 fix: sspanel version >= 2023.2 will discard node status report.
fix: return error message for null user list
2023-05-18 01:23:46 +08:00
dependabot[bot]
af9224f5bb build(deps): bump github.com/shirou/gopsutil/v3 from 3.23.2 to 3.23.4 (#337)
Bumps [github.com/shirou/gopsutil/v3](https://github.com/shirou/gopsutil) from 3.23.2 to 3.23.4.
- [Release notes](https://github.com/shirou/gopsutil/releases)
- [Commits](https://github.com/shirou/gopsutil/compare/v3.23.2...v3.23.4)

---
updated-dependencies:
- dependency-name: github.com/shirou/gopsutil/v3
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-05-05 05:27:31 +08:00
Senis John
c54650f195 update: add readme for REALITY proto params 2023-04-22 17:49:07 +08:00
Senis John
c73af2309b fix: When server port is 0, the program will not exit normally 2023-04-22 17:05:10 +08:00
thank243
169f742b76 Update core to 1.8.1 and support Reality proto (#326)
* update: Support reality proto

* fix: typo
2023-04-22 16:41:22 +08:00
pocketW
0fa8a45e51 Merge pull request #287 from XrayR-project/dependabot/go_modules/google.golang.org/protobuf-1.29.1
build(deps): bump google.golang.org/protobuf from 1.29.0 to 1.29.1
2023-03-15 10:34:09 +11:00
dependabot[bot]
17c538a5b7 build(deps): bump google.golang.org/protobuf from 1.29.0 to 1.29.1
Bumps [google.golang.org/protobuf](https://github.com/protocolbuffers/protobuf-go) from 1.29.0 to 1.29.1.
- [Release notes](https://github.com/protocolbuffers/protobuf-go/releases)
- [Changelog](https://github.com/protocolbuffers/protobuf-go/blob/master/release.bash)
- [Commits](https://github.com/protocolbuffers/protobuf-go/compare/v1.29.0...v1.29.1)

---
updated-dependencies:
- dependency-name: google.golang.org/protobuf
  dependency-type: direct:production
...

Signed-off-by: dependabot[bot] <support@github.com>
2023-03-14 23:08:17 +00:00
pocketW
5625d570fd Merge pull request #284 from XrayR-project/dependabot/go_modules/github.com/xtls/xray-core-1.8.0
build(deps): bump github.com/xtls/xray-core from 1.7.5 to 1.8.0
2023-03-11 18:27:02 +08:00
pocketW
9b0e55f037 Merge pull request #283 from XrayR-project/dependabot/go_modules/github.com/sagernet/sing-0.1.8
build(deps): bump github.com/sagernet/sing from 0.1.7 to 0.1.8
2023-03-11 18:26:50 +08:00
dependabot[bot]
4597d6ac56 build(deps): bump github.com/xtls/xray-core from 1.7.5 to 1.8.0
Bumps [github.com/xtls/xray-core](https://github.com/xtls/xray-core) from 1.7.5 to 1.8.0.
- [Release notes](https://github.com/xtls/xray-core/releases)
- [Commits](https://github.com/xtls/xray-core/compare/v1.7.5...v1.8.0)

---
updated-dependencies:
- dependency-name: github.com/xtls/xray-core
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2023-03-09 15:15:48 +00:00
dependabot[bot]
9aaad5e8ad build(deps): bump github.com/sagernet/sing from 0.1.7 to 0.1.8
Bumps [github.com/sagernet/sing](https://github.com/sagernet/sing) from 0.1.7 to 0.1.8.
- [Release notes](https://github.com/sagernet/sing/releases)
- [Commits](https://github.com/sagernet/sing/compare/v0.1.7...v0.1.8)

---
updated-dependencies:
- dependency-name: github.com/sagernet/sing
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2023-03-09 15:15:03 +00:00
dependabot[bot]
94d82f33ea build(deps): bump google.golang.org/protobuf from 1.28.1 to 1.29.0 (#282)
Bumps [google.golang.org/protobuf](https://github.com/protocolbuffers/protobuf-go) from 1.28.1 to 1.29.0.
- [Release notes](https://github.com/protocolbuffers/protobuf-go/releases)
- [Changelog](https://github.com/protocolbuffers/protobuf-go/blob/master/release.bash)
- [Commits](https://github.com/protocolbuffers/protobuf-go/compare/v1.28.1...v1.29.0)

---
updated-dependencies:
- dependency-name: google.golang.org/protobuf
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-03-09 02:08:25 +08:00
pocketW
49aa520d67 Merge pull request #280 from XrayR-project/dependabot/go_modules/golang.org/x/crypto-0.7.0
build(deps): bump golang.org/x/crypto from 0.6.0 to 0.7.0
2023-03-08 20:03:21 +08:00
dependabot[bot]
73c3047651 build(deps): bump golang.org/x/crypto from 0.6.0 to 0.7.0
Bumps [golang.org/x/crypto](https://github.com/golang/crypto) from 0.6.0 to 0.7.0.
- [Release notes](https://github.com/golang/crypto/releases)
- [Commits](https://github.com/golang/crypto/compare/v0.6.0...v0.7.0)

---
updated-dependencies:
- dependency-name: golang.org/x/crypto
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2023-03-06 16:12:02 +00:00
dependabot[bot]
613da96543 build(deps): bump github.com/shirou/gopsutil/v3 from 3.23.1 to 3.23.2 (#278)
Bumps [github.com/shirou/gopsutil/v3](https://github.com/shirou/gopsutil) from 3.23.1 to 3.23.2.
- [Release notes](https://github.com/shirou/gopsutil/releases)
- [Commits](https://github.com/shirou/gopsutil/compare/v3.23.1...v3.23.2)

---
updated-dependencies:
- dependency-name: github.com/shirou/gopsutil/v3
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-03-02 00:31:15 +08:00
dependabot[bot]
76127f4757 build(deps): bump github.com/stretchr/testify from 1.8.1 to 1.8.2 (#274)
Bumps [github.com/stretchr/testify](https://github.com/stretchr/testify) from 1.8.1 to 1.8.2.
- [Release notes](https://github.com/stretchr/testify/releases)
- [Commits](https://github.com/stretchr/testify/compare/v1.8.1...v1.8.2)

---
updated-dependencies:
- dependency-name: github.com/stretchr/testify
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-02-28 19:09:42 +08:00
dependabot[bot]
1d5a34cf98 build(deps): bump github.com/go-acme/lego/v4 from 4.10.0 to 4.10.2 (#273)
Bumps [github.com/go-acme/lego/v4](https://github.com/go-acme/lego) from 4.10.0 to 4.10.2.
- [Release notes](https://github.com/go-acme/lego/releases)
- [Changelog](https://github.com/go-acme/lego/blob/master/CHANGELOG.md)
- [Commits](https://github.com/go-acme/lego/compare/v4.10.0...v4.10.2)

---
updated-dependencies:
- dependency-name: github.com/go-acme/lego/v4
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-02-28 19:09:29 +08:00
dependabot[bot]
bf13971502 build(deps): bump github.com/eko/gocache/lib/v4 from 4.1.2 to 4.1.3 (#272)
Bumps [github.com/eko/gocache/lib/v4](https://github.com/eko/gocache) from 4.1.2 to 4.1.3.
- [Release notes](https://github.com/eko/gocache/releases)
- [Commits](https://github.com/eko/gocache/compare/lib/v4.1.2...lib/v4.1.3)

---
updated-dependencies:
- dependency-name: github.com/eko/gocache/lib/v4
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-02-28 19:09:20 +08:00
pocketW
572be38ae4 Merge pull request #267 from XrayR-project/dependabot/go_modules/github.com/sagernet/sing-shadowsocks-0.1.1
build(deps): bump github.com/sagernet/sing-shadowsocks from 0.1.1-0.20230202035033-e3123545f2f7 to 0.1.1
2023-02-24 23:45:09 +11:00
pocketW
34d96c9338 Merge pull request #266 from XrayR-project/dependabot/go_modules/github.com/go-acme/lego/v4-4.10.0
build(deps): bump github.com/go-acme/lego/v4 from 4.9.2-0.20221210101705-6695fcc87344 to 4.10.0
2023-02-24 23:44:55 +11:00
dependabot[bot]
4e69c0656e build(deps): bump github.com/sagernet/sing-shadowsocks
Bumps [github.com/sagernet/sing-shadowsocks](https://github.com/sagernet/sing-shadowsocks) from 0.1.1-0.20230202035033-e3123545f2f7 to 0.1.1.
- [Release notes](https://github.com/sagernet/sing-shadowsocks/releases)
- [Commits](https://github.com/sagernet/sing-shadowsocks/commits/v0.1.1)

---
updated-dependencies:
- dependency-name: github.com/sagernet/sing-shadowsocks
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2023-02-23 15:21:36 +00:00
dependabot[bot]
88f7709fad build(deps): bump github.com/go-acme/lego/v4
Bumps [github.com/go-acme/lego/v4](https://github.com/go-acme/lego) from 4.9.2-0.20221210101705-6695fcc87344 to 4.10.0.
- [Release notes](https://github.com/go-acme/lego/releases)
- [Commits](https://github.com/go-acme/lego/commits/v4.10.0)

---
updated-dependencies:
- dependency-name: github.com/go-acme/lego/v4
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2023-02-23 15:21:30 +00:00
pocketW
f95825395d update to v0.9.0 2023-02-22 22:38:53 +11:00
dependabot[bot]
e279d39d5a build(deps): bump github.com/sagernet/sing from 0.1.6 to 0.1.7 (#263)
Bumps [github.com/sagernet/sing](https://github.com/sagernet/sing) from 0.1.6 to 0.1.7.
- [Release notes](https://github.com/sagernet/sing/releases)
- [Commits](https://github.com/sagernet/sing/compare/v0.1.6...v0.1.7)

---
updated-dependencies:
- dependency-name: github.com/sagernet/sing
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-02-21 22:48:01 +08:00
dependabot[bot]
74038add93 build(deps): bump golang.org/x/net from 0.6.0 to 0.7.0 (#259)
Bumps [golang.org/x/net](https://github.com/golang/net) from 0.6.0 to 0.7.0.
- [Release notes](https://github.com/golang/net/releases)
- [Commits](https://github.com/golang/net/compare/v0.6.0...v0.7.0)

---
updated-dependencies:
- dependency-name: golang.org/x/net
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-02-16 07:34:28 +08:00
dependabot[bot]
0a2b8612dc build(deps): bump golang.org/x/crypto from 0.5.0 to 0.6.0 (#253)
Bumps [golang.org/x/crypto](https://github.com/golang/crypto) from 0.5.0 to 0.6.0.
- [Release notes](https://github.com/golang/crypto/releases)
- [Commits](https://github.com/golang/crypto/compare/v0.5.0...v0.6.0)

---
updated-dependencies:
- dependency-name: golang.org/x/crypto
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-02-10 15:55:37 +08:00
dependabot[bot]
5923018a9d build(deps): bump github.com/xtls/xray-core from 1.7.3 to 1.7.5 (#252)
Bumps [github.com/xtls/xray-core](https://github.com/xtls/xray-core) from 1.7.3 to 1.7.5.
- [Release notes](https://github.com/xtls/xray-core/releases)
- [Commits](https://github.com/xtls/xray-core/compare/v1.7.3...v1.7.5)

---
updated-dependencies:
- dependency-name: github.com/xtls/xray-core
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-02-08 23:49:27 +08:00
pocketW
239a0eea78 fix: compatiable VLESS+TCP+TLS #245 2023-02-03 13:30:38 +11:00
pocketW
456951155d chore: support xtls-rprx-vision for VLESS 2023-02-02 22:05:33 +11:00
pocketW
a7a3d0220d update to v0.8.9 2023-02-02 22:03:09 +11:00
pocketW
b857bfac3e fix docker build 2023-01-21 15:30:25 +11:00
pocketW
f4ea59493f fix docker build 2023-01-21 14:08:46 +11:00
dependabot[bot]
957b439ced build(deps): bump github.com/spf13/viper from 1.14.0 to 1.15.0 (#228)
Bumps [github.com/spf13/viper](https://github.com/spf13/viper) from 1.14.0 to 1.15.0.
- [Release notes](https://github.com/spf13/viper/releases)
- [Commits](https://github.com/spf13/viper/compare/v1.14.0...v1.15.0)

---
updated-dependencies:
- dependency-name: github.com/spf13/viper
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-01-20 23:31:28 +08:00
pocketW
19583503cd chore: keep dispatcher updated to Xray-core 2023-01-20 23:07:36 +11:00
pocketW
d8a3242e22 Merge pull request #227 from Github-Aiko/master
Translate Readme for English(en) users
2023-01-20 21:10:17 +11:00
Aiko
007d9a0717 Merge branch 'master' into master 2023-01-20 16:47:13 +07:00
pocketW
016051892b Update README.md 2023-01-20 20:17:15 +11:00
Aiko
5764c3e610 Update Readme 2023-01-20 16:02:02 +07:00
Aiko
5bacee35aa Add En 2023-01-20 16:00:50 +07:00
Aiko
e6b6abee24 Create README-en.md 2023-01-20 16:00:15 +07:00
pocketW
edddd2f965 Update README.md 2023-01-20 19:33:24 +11:00
pocketW
0cd17f2b55 Merge pull request #226 from Github-Aiko/patch-2
Translate Readme for Vietnamese(vi) users
2023-01-20 19:28:58 +11:00
pocketW
3838b2d333 Merge pull request #224 from DanielBlackBeard/master
translate Readme for Iranian(farsi) users
2023-01-20 19:26:42 +11:00
Aiko
7a90f157b8 Translate Readme for Vietnam(vi) users 2023-01-19 23:46:06 +07:00
Persian Prince
a10efcaea0 README_Fa.md
*Add Persian Translation of Readme
2023-01-19 20:12:43 +03:30
dependabot[bot]
1f85ce3762 build(deps): bump github.com/sagernet/sing from 0.1.5 to 0.1.6 (#216)
Bumps [github.com/sagernet/sing](https://github.com/sagernet/sing) from 0.1.5 to 0.1.6.
- [Release notes](https://github.com/sagernet/sing/releases)
- [Commits](https://github.com/sagernet/sing/compare/v0.1.5...v0.1.6)

---
updated-dependencies:
- dependency-name: github.com/sagernet/sing
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-01-17 01:34:39 +08:00
dependabot[bot]
eed0b8ae32 build(deps): bump github.com/sagernet/sing from 0.1.3 to 0.1.5 (#209)
Bumps [github.com/sagernet/sing](https://github.com/sagernet/sing) from 0.1.3 to 0.1.5.
- [Release notes](https://github.com/sagernet/sing/releases)
- [Commits](https://github.com/sagernet/sing/compare/v0.1.3...v0.1.5)

---
updated-dependencies:
- dependency-name: github.com/sagernet/sing
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-01-13 00:36:02 +08:00
Senis John
ea2f7a64e1 fix: newV2board cannot parse tcp http header
fix: typo
chore: update deps
2023-01-11 13:17:53 +08:00
dependabot[bot]
ad8243fcf5 build(deps): bump github.com/sagernet/sing from 0.1.2 to 0.1.3 (#206)
Bumps [github.com/sagernet/sing](https://github.com/sagernet/sing) from 0.1.2 to 0.1.3.
- [Release notes](https://github.com/sagernet/sing/releases)
- [Commits](https://github.com/sagernet/sing/compare/v0.1.2...v0.1.3)

---
updated-dependencies:
- dependency-name: github.com/sagernet/sing
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-01-09 23:39:18 +08:00
pocketW
66fccfd422 Merge pull request #194 from XrayR-project/dependabot/go_modules/golang.org/x/crypto-0.5.0
build(deps): bump golang.org/x/crypto from 0.4.0 to 0.5.0
2023-01-06 09:11:11 +11:00
dependabot[bot]
448d44ff46 build(deps): bump golang.org/x/crypto from 0.4.0 to 0.5.0
Bumps [golang.org/x/crypto](https://github.com/golang/crypto) from 0.4.0 to 0.5.0.
- [Release notes](https://github.com/golang/crypto/releases)
- [Commits](https://github.com/golang/crypto/compare/v0.4.0...v0.5.0)

---
updated-dependencies:
- dependency-name: golang.org/x/crypto
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2023-01-05 22:11:02 +00:00
pocketW
891068a091 Merge pull request #193 from XrayR-project/dependabot/go_modules/golang.org/x/net-0.5.0
build(deps): bump golang.org/x/net from 0.4.0 to 0.5.0
2023-01-06 09:09:04 +11:00
pocketW
127ccef619 Merge pull request #191 from XrayR-project/dependabot/go_modules/github.com/sagernet/sing-0.1.2
build(deps): bump github.com/sagernet/sing from 0.1.1 to 0.1.2
2023-01-06 09:08:51 +11:00
dependabot[bot]
84bc47ba18 build(deps): bump golang.org/x/net from 0.4.0 to 0.5.0
Bumps [golang.org/x/net](https://github.com/golang/net) from 0.4.0 to 0.5.0.
- [Release notes](https://github.com/golang/net/releases)
- [Commits](https://github.com/golang/net/compare/v0.4.0...v0.5.0)

---
updated-dependencies:
- dependency-name: golang.org/x/net
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2023-01-05 15:25:54 +00:00
dependabot[bot]
a139d48959 build(deps): bump github.com/sagernet/sing from 0.1.1 to 0.1.2
Bumps [github.com/sagernet/sing](https://github.com/sagernet/sing) from 0.1.1 to 0.1.2.
- [Release notes](https://github.com/sagernet/sing/releases)
- [Commits](https://github.com/sagernet/sing/compare/v0.1.1...v0.1.2)

---
updated-dependencies:
- dependency-name: github.com/sagernet/sing
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2023-01-03 15:08:22 +00:00
dependabot[bot]
180a4af7ca build(deps): bump github.com/shirou/gopsutil/v3 from 3.22.11 to 3.22.12 (#190)
Bumps [github.com/shirou/gopsutil/v3](https://github.com/shirou/gopsutil) from 3.22.11 to 3.22.12.
- [Release notes](https://github.com/shirou/gopsutil/releases)
- [Commits](https://github.com/shirou/gopsutil/commits/v3.22.12)

---
updated-dependencies:
- dependency-name: github.com/shirou/gopsutil/v3
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-01-03 00:59:23 +08:00
Senis John
8512c354f5 revert: dns hot load feat
the dispatcher mod cannot reload dns instance. So I revert the DNS feat and will create a new branch to test it.
2023-01-02 21:46:30 +08:00
Senis John
4746bd57d0 fix: reload dns service when need 2023-01-01 02:28:38 +08:00
Senis John
6477ef6c44 fix: dns memory leak
It can be properly working after xray-core accept the patch
2022-12-31 12:35:55 +08:00
Senis John
21e0ebc428 fix: Try to fix dns memory leak
update: release.yml
2022-12-30 22:19:48 +08:00
Senis John
40c65a86fc update: reformat newV2board serverConfig structure 2022-12-28 10:36:29 +08:00
Senis John
b5a8f4464c fix: ss2022 not working as expect
fix: minor bugs
2022-12-27 17:28:56 +08:00
Senis John
451b5a1fd4 fix: typo 2022-12-27 16:10:51 +08:00
Senis John
8ddf257aa4 update: release.yml
fix: typo
2022-12-27 15:10:17 +08:00
Senis John
a8742426b3 update: refactor newV2board parser's structure 2022-12-27 12:00:34 +08:00
Senis John
d44fa11eee update: support v2board v1.7.2 routes structure
After update this version, the v2board version must >= 1.7.2 release
2022-12-26 12:41:18 +08:00
Senis John
0c2d03f3e0 update: add error message for none user 2022-12-25 09:01:26 +08:00
Senis John
9c6ae5b7ab update: speedup docker build 2022-12-22 09:20:16 +08:00
Senis John
4c699d86f2 update: compatible with v2board v1.7.1 route rules 2022-12-21 20:09:09 +08:00
pocketW
be940fdcea add cache to speedup docker build 2022-12-20 11:39:48 +11:00
pocketW
619a455432 refactor: remove unused regexp 2022-12-20 09:19:17 +11:00
pocketW
03b7bf6ed1 fix: only encode userKey with base64 for V2board 2022-12-20 09:05:18 +11:00
dependabot[bot]
224224084e build(deps): bump github.com/sagernet/sing from 0.1.0 to 0.1.1 (#171)
Bumps [github.com/sagernet/sing](https://github.com/sagernet/sing) from 0.1.0 to 0.1.1.
- [Release notes](https://github.com/sagernet/sing/releases)
- [Commits](https://github.com/sagernet/sing/compare/v0.1.0...v0.1.1)

---
updated-dependencies:
- dependency-name: github.com/sagernet/sing
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2022-12-19 23:24:31 +08:00
pocketW
ac460c2f71 Merge branch 'master' of https://github.com/XrayR-project/XrayR 2022-12-19 21:48:29 +11:00
pocketW
e1b512ef5b fix: check userKey for ss2022 2022-12-19 21:48:19 +11:00
Senis John
d5ab69d703 update: support v2board shadowsocks obfs settings
fix: v2board route block bugs
2022-12-19 01:21:27 +08:00
Senis John
d9bbb836b8 fix: minor bugs 2022-12-18 16:08:56 +08:00
Senis John
1de5143fde update: support v2board DNS rules 2022-12-18 15:31:23 +08:00
Senis John
77814acd1a fix: typo and remove redundant conversion 2022-12-18 10:27:59 +08:00
pocketW
3926463c59 feat: add shadowsocks2022 support for v2raysocks 2022-12-18 12:11:55 +11:00
pocketW
05ff6dff1b chore: fix build error 2022-12-18 11:26:38 +11:00
Senis John
1d5d4564e5 chore: update go.mod 2022-12-18 01:51:22 +08:00
Senis John
0777cc8892 update: add ss plugin support ss2022
The error message will have more details
2022-12-16 20:43:38 +08:00
Senis John
b09f371447 fix: typo 2022-12-16 14:43:56 +08:00
pocketW
fc71d76f4d update to v0.8.8 2022-12-16 09:51:34 +11:00
pocketW
443fb959f6 fix: type conversion 2022-12-15 23:00:56 +11:00
Senis John
040e19ee87 fix: Priority local speed limit 2022-12-15 19:51:43 +08:00
pocketW
addde0f8d4 feat: read speed limit from V2board
chore: update dependence
2022-12-15 22:31:35 +11:00
Senis John
9ef39925fd fix: newV2board rule regexp pattern 2022-12-15 09:55:49 +08:00
Senis John
a6dbf757e9 fix: add error to ss2022's length check 2022-12-14 10:12:19 +08:00
Senis John
acd3cce667 fix: trim length of userKey for ss2022 2022-12-13 20:43:02 +08:00
Senis John
eea6c0dea6 fix: typo 2022-12-11 06:51:25 +08:00
Senis John
889160b908 update: xray-core dev v1.6.6 2022-12-09 19:31:19 +08:00
dependabot[bot]
0bea22e145 build(deps): bump golang.org/x/crypto from 0.3.0 to 0.4.0 (#145)
Bumps [golang.org/x/crypto](https://github.com/golang/crypto) from 0.3.0 to 0.4.0.
- [Release notes](https://github.com/golang/crypto/releases)
- [Commits](https://github.com/golang/crypto/compare/v0.3.0...v0.4.0)

---
updated-dependencies:
- dependency-name: golang.org/x/crypto
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: thank243 <thank243@gmail.com>
2022-12-07 23:28:46 +08:00
dependabot[bot]
f25d5993f3 build(deps): bump golang.org/x/net from 0.3.0 to 0.4.0
Bumps [golang.org/x/net](https://github.com/golang/net) from 0.3.0 to 0.4.0.
- [Release notes](https://github.com/golang/net/releases)
- [Commits](https://github.com/golang/net/compare/v0.3.0...v0.4.0)

---
updated-dependencies:
- dependency-name: golang.org/x/net
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2022-12-07 23:27:46 +08:00
Senis John
676365b13b update: Add support NewV2board api block rules 2022-12-07 23:26:09 +08:00
thank243
e774d5c822 Merge branch 'master' into globalLimit 2022-12-07 20:19:43 +08:00
pocketW
be4f9cdac1 reduce clear interval 2022-12-07 21:58:12 +11:00
Senis John
9a06f78653 fix: Add deviceLimit to the prefix of email string
Solve different node and different device limit the less limit node cannot work normally.
2022-12-07 18:49:08 +08:00
Senis John
85d73408c3 update: refactor global limit cache. Use gocache lib for level 2 cache
fix: shadowsocks2022 init feat
2022-12-07 13:29:05 +08:00
dependabot[bot]
c93fdb4cf3 build(deps): bump golang.org/x/net from 0.2.0 to 0.3.0
Bumps [golang.org/x/net](https://github.com/golang/net) from 0.2.0 to 0.3.0.
- [Release notes](https://github.com/golang/net/releases)
- [Commits](https://github.com/golang/net/compare/v0.2.0...v0.3.0)

---
updated-dependencies:
- dependency-name: golang.org/x/net
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2022-12-06 23:25:19 +08:00
dependabot[bot]
0d565b034b build(deps): bump golang.org/x/time from 0.2.0 to 0.3.0
Bumps [golang.org/x/time](https://github.com/golang/time) from 0.2.0 to 0.3.0.
- [Release notes](https://github.com/golang/time/releases)
- [Commits](https://github.com/golang/time/compare/v0.2.0...v0.3.0)

---
updated-dependencies:
- dependency-name: golang.org/x/time
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2022-12-05 23:31:39 +08:00
Senis John
212f0ff135 fix: log prefix display not completed 2022-12-04 08:43:10 +08:00
Senis John
608764a8a0 fix: shadowsocks2022 user manager not working 2022-12-04 00:50:54 +08:00
Senis John
fc16cb0972 fix: shadowsocks2022 user manager not working 2022-12-04 00:42:35 +08:00
pocketW
5397720430 fix: expire time error 2022-12-03 19:17:33 +11:00
pocketW
839b15c22c fix: fix condition of check cert service
fix: check devices on redis first
2022-12-03 14:28:32 +11:00
Senis John
5b45b8ffe8 fix: refactor the InboundInfo struct
Global limit can separate settings for each node
2022-12-03 09:11:43 +08:00
dependabot[bot]
a56cd91b34 build(deps): bump github.com/shirou/gopsutil/v3 from 3.22.10 to 3.22.11 (#132)
Bumps [github.com/shirou/gopsutil/v3](https://github.com/shirou/gopsutil) from 3.22.10 to 3.22.11.
- [Release notes](https://github.com/shirou/gopsutil/releases)
- [Commits](https://github.com/shirou/gopsutil/commits)

---
updated-dependencies:
- dependency-name: github.com/shirou/gopsutil/v3
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2022-12-01 23:18:31 +08:00
Senis John
b6600729b2 update: code clean 2022-11-30 09:13:35 +08:00
Senis John
8d0225bcbb fix: On enable global limit, the online device report feat will failure
update: add pushIP method for limiter
2022-11-29 20:42:09 +08:00
pocketW
ce5fe799f4 Merge pull request #126 from XrayR-project/dependabot/go_modules/github.com/xtls/xray-core-1.6.5
build(deps): bump github.com/xtls/xray-core from 1.6.4 to 1.6.5
2022-11-29 09:51:07 +11:00
pocketW
8763d5960f Merge pull request #125 from XrayR-project/dependabot/go_modules/github.com/go-acme/lego/v4-4.9.1
build(deps): bump github.com/go-acme/lego/v4 from 4.9.0 to 4.9.1
2022-11-29 09:50:55 +11:00
dependabot[bot]
7a452a4a53 build(deps): bump github.com/xtls/xray-core from 1.6.4 to 1.6.5
Bumps [github.com/xtls/xray-core](https://github.com/xtls/xray-core) from 1.6.4 to 1.6.5.
- [Release notes](https://github.com/xtls/xray-core/releases)
- [Commits](https://github.com/xtls/xray-core/compare/v1.6.4...v1.6.5)

---
updated-dependencies:
- dependency-name: github.com/xtls/xray-core
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2022-11-28 15:10:20 +00:00
dependabot[bot]
fac8b62286 build(deps): bump github.com/go-acme/lego/v4 from 4.9.0 to 4.9.1
Bumps [github.com/go-acme/lego/v4](https://github.com/go-acme/lego) from 4.9.0 to 4.9.1.
- [Release notes](https://github.com/go-acme/lego/releases)
- [Changelog](https://github.com/go-acme/lego/blob/master/CHANGELOG.md)
- [Commits](https://github.com/go-acme/lego/compare/v4.9.0...v4.9.1)

---
updated-dependencies:
- dependency-name: github.com/go-acme/lego/v4
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2022-11-28 15:10:08 +00:00
thank243
d320aadb54 fix: global limit will failure on some situation (#124)
fix: typo
2022-11-28 11:16:27 +08:00
thank243
40ae48f507 update: refactor the global limit (#121)
fix: some log prefix

It an enhance device limit. Redis just use to sync local ipMap.
If global limit is enabled. The online report will faliure. All enabled nodes will report the device from redis.
2022-11-27 10:12:30 +08:00
pocketW
d1bc36782b add timeout 2022-11-26 21:44:14 +11:00
AikoCute
c04330d0bf Merge branch 'master' of https://github.com/Github-Aiko/XrayR-Real 2022-11-26 16:14:06 +07:00
AikoCute
872eb12d35 Check Connect Redis Server 2022-11-26 16:13:45 +07:00
pocketW
e1d4428d98 Merge pull request #118 from amin550/master
fix: ssPanel Report Node Status
2022-11-26 17:41:00 +11:00
pocketW
0fef3cf278 update config example 2022-11-26 17:30:20 +11:00
Amin
a4ca37b1de Fix sspanel ReportNodeStatus 2022-11-26 00:52:30 +03:30
Senis Y
74f3a75682 fix: delete unused struct 2022-11-25 00:25:39 +08:00
Senis Y
183b1be519 feature: add support v2board new API
update: add eTag on fetch users for save resources
update: split cert monitor service
update: refactor log format
update: add support shadowsocks_2022

The cert monitor period = UpdatePeriodic * 60

The xray-core official is not implementation of proxy.UserManager feature. So it may change in the future.
2022-11-24 15:11:01 +08:00
pocketW
2f10c3f6b8 revert error format 2022-11-20 14:57:20 +11:00
AikoCute
86324ff1ae Logs for GlobalDeviceLimit 2022-11-20 10:10:03 +07:00
pocketW
1897404c9d Merge pull request #104 from XrayR-project/dependabot/go_modules/golang.org/x/crypto-0.3.0
build(deps): bump golang.org/x/crypto from 0.2.0 to 0.3.0
2022-11-18 17:25:38 +11:00
dependabot[bot]
ee53e746c5 build(deps): bump golang.org/x/crypto from 0.2.0 to 0.3.0
Bumps [golang.org/x/crypto](https://github.com/golang/crypto) from 0.2.0 to 0.3.0.
- [Release notes](https://github.com/golang/crypto/releases)
- [Commits](https://github.com/golang/crypto/compare/v0.2.0...v0.3.0)

---
updated-dependencies:
- dependency-name: golang.org/x/crypto
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2022-11-17 15:15:59 +00:00
pocketW
656df61c17 Merge pull request #101 from thank243/master
fix: http mode not working
fix: use "email|uid" for global limit user identify
update: tls alpn challenge for apply certificate
2022-11-17 12:42:53 +11:00
Senis
e0237f5c54 fix: http mode not working
fix: use "email|uid" for global limit user identify
update: tls alpn challenge for apply certificate
2022-11-16 12:59:44 +08:00
pocketW
e16d94fb4a Merge pull request #97 from XrayR-project/dependabot/go_modules/github.com/xtls/xray-core-1.6.4
build(deps): bump github.com/xtls/xray-core from 1.6.3 to 1.6.4
2022-11-15 11:25:59 +11:00
dependabot[bot]
e357fc438f build(deps): bump github.com/xtls/xray-core from 1.6.3 to 1.6.4
Bumps [github.com/xtls/xray-core](https://github.com/xtls/xray-core) from 1.6.3 to 1.6.4.
- [Release notes](https://github.com/xtls/xray-core/releases)
- [Commits](https://github.com/xtls/xray-core/compare/v1.6.3...v1.6.4)

---
updated-dependencies:
- dependency-name: github.com/xtls/xray-core
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2022-11-14 15:12:52 +00:00
pocketW
cb1638ac21 update to v0.8.6 2022-11-13 13:08:12 +11:00
pocketW
de0da25c21 chore: update dependence 2022-11-12 21:22:13 +11:00
pocketW
faec840c23 Merge pull request #90 from XrayR-project/dependabot/go_modules/golang.org/x/crypto-0.2.0
build(deps): bump golang.org/x/crypto from 0.1.0 to 0.2.0
2022-11-12 02:26:08 +11:00
pocketW
a6a1baf70c Merge pull request #89 from thank243/master
fix: adding the issuers certificate to the new certificate
2022-11-12 02:25:54 +11:00
pocketW
4013f71e4c fix: update global device limit 2022-11-12 02:22:53 +11:00
dependabot[bot]
c8f0981b0e build(deps): bump golang.org/x/crypto from 0.1.0 to 0.2.0
Bumps [golang.org/x/crypto](https://github.com/golang/crypto) from 0.1.0 to 0.2.0.
- [Release notes](https://github.com/golang/crypto/releases)
- [Commits](https://github.com/golang/crypto/compare/v0.1.0...v0.2.0)

---
updated-dependencies:
- dependency-name: golang.org/x/crypto
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2022-11-11 15:16:31 +00:00
Senis
5274edf657 fix: adding the issuers certificate to the new certificate 2022-11-11 22:42:31 +08:00
pocketW
e6232c1852 Merge pull request #88 from XrayR-project/dependabot/go_modules/golang.org/x/net-0.2.0
build(deps): bump golang.org/x/net from 0.1.0 to 0.2.0
2022-11-11 23:59:47 +11:00
dependabot[bot]
e0688fc609 build(deps): bump golang.org/x/net from 0.1.0 to 0.2.0
Bumps [golang.org/x/net](https://github.com/golang/net) from 0.1.0 to 0.2.0.
- [Release notes](https://github.com/golang/net/releases)
- [Commits](https://github.com/golang/net/compare/v0.1.0...v0.2.0)

---
updated-dependencies:
- dependency-name: golang.org/x/net
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2022-11-11 12:58:43 +00:00
pocketW
ffa444f2ab Merge pull request #86 from XrayR-project/dependabot/go_modules/github.com/spf13/viper-1.14.0
build(deps): bump github.com/spf13/viper from 1.13.0 to 1.14.0
2022-11-11 23:57:06 +11:00
dependabot[bot]
81ba4ebb43 build(deps): bump github.com/spf13/viper from 1.13.0 to 1.14.0
Bumps [github.com/spf13/viper](https://github.com/spf13/viper) from 1.13.0 to 1.14.0.
- [Release notes](https://github.com/spf13/viper/releases)
- [Commits](https://github.com/spf13/viper/compare/v1.13.0...v1.14.0)

---
updated-dependencies:
- dependency-name: github.com/spf13/viper
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2022-11-11 12:56:11 +00:00
pocketW
f50c61c782 fix: fix cert storage path 2022-11-11 23:44:43 +11:00
Senis
9c356cd28c Merge remote-tracking branch 'origin/master' 2022-11-08 17:50:53 +08:00
Senis
310353f344 fix: Redundant character escape 2022-11-08 17:50:03 +08:00
thank243
808b5ecc3c Merge branch 'master' into master 2022-11-08 14:48:44 +08:00
Senis
c4ef5bb843 update: xray-core v1.6.3 2022-11-08 14:46:06 +08:00
Senis
ddce3fa86d fix: typo 2022-11-08 11:57:06 +08:00
Senis
209f5a17d6 update: rebuild the implementation of legocmd 2022-11-08 11:13:27 +08:00
pocketW
af3fae9cdb Merge pull request #82 from thank243/master
fix: on some circumstance, periodic will run twice
2022-11-07 23:30:32 +11:00
Senis
3b96b352cb fix: on some circumstance, periodic will run twice 2022-11-07 15:55:30 +08:00
pocketW
398c3133d3 Merge pull request #79 from thank243/master
feat: add global user ip limit
2022-11-06 14:31:22 +11:00
Senis
a397af5d73 fix: init global limit 2022-11-04 14:36:34 +08:00
Senis
b47954ea64 fix: minor bugs 2022-11-04 08:58:41 +08:00
Senis
9a2188cb0c update: Global User IP limit. 2022-11-04 08:26:56 +08:00
pocketW
c7af43fc49 Merge pull request #75 from XrayR-project/dependabot/go_modules/github.com/shirou/gopsutil/v3-3.22.10
build(deps): bump github.com/shirou/gopsutil/v3 from 3.22.9 to 3.22.10
2022-11-03 22:49:11 +11:00
pocketW
87aa855154 Merge pull request #74 from XrayR-project/dependabot/go_modules/github.com/xtls/xray-core-1.6.2
build(deps): bump github.com/xtls/xray-core from 1.6.1 to 1.6.2
2022-11-03 22:48:58 +11:00
dependabot[bot]
37eff6755c build(deps): bump github.com/shirou/gopsutil/v3 from 3.22.9 to 3.22.10
Bumps [github.com/shirou/gopsutil/v3](https://github.com/shirou/gopsutil) from 3.22.9 to 3.22.10.
- [Release notes](https://github.com/shirou/gopsutil/releases)
- [Commits](https://github.com/shirou/gopsutil/compare/v3.22.9...v3.22.10)

---
updated-dependencies:
- dependency-name: github.com/shirou/gopsutil/v3
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2022-11-01 15:53:37 +00:00
dependabot[bot]
5e346ddfee build(deps): bump github.com/xtls/xray-core from 1.6.1 to 1.6.2
Bumps [github.com/xtls/xray-core](https://github.com/xtls/xray-core) from 1.6.1 to 1.6.2.
- [Release notes](https://github.com/xtls/xray-core/releases)
- [Commits](https://github.com/xtls/xray-core/compare/v1.6.1...v1.6.2)

---
updated-dependencies:
- dependency-name: github.com/xtls/xray-core
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2022-10-31 16:00:54 +00:00
Senis
838c667a87 fix: typo 2022-10-27 08:26:23 +08:00
pocketW
057f4156bf Merge pull request #71 from XrayR-project/dependabot/go_modules/github.com/stretchr/testify-1.8.1
build(deps): bump github.com/stretchr/testify from 1.8.0 to 1.8.1
2022-10-26 13:40:42 +11:00
pocketW
1f59a7cd7a Merge pull request #70 from XrayR-project/dependabot/go_modules/github.com/xtls/xray-core-1.6.1
build(deps): bump github.com/xtls/xray-core from 1.6.0 to 1.6.1
2022-10-26 13:40:30 +11:00
dependabot[bot]
2b5fa4feee build(deps): bump github.com/stretchr/testify from 1.8.0 to 1.8.1
Bumps [github.com/stretchr/testify](https://github.com/stretchr/testify) from 1.8.0 to 1.8.1.
- [Release notes](https://github.com/stretchr/testify/releases)
- [Commits](https://github.com/stretchr/testify/compare/v1.8.0...v1.8.1)

---
updated-dependencies:
- dependency-name: github.com/stretchr/testify
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2022-10-24 16:02:42 +00:00
dependabot[bot]
571191a190 build(deps): bump github.com/xtls/xray-core from 1.6.0 to 1.6.1
Bumps [github.com/xtls/xray-core](https://github.com/xtls/xray-core) from 1.6.0 to 1.6.1.
- [Release notes](https://github.com/xtls/xray-core/releases)
- [Commits](https://github.com/xtls/xray-core/compare/v1.6.0...v1.6.1)

---
updated-dependencies:
- dependency-name: github.com/xtls/xray-core
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2022-10-24 16:02:30 +00:00
pocketW
3d5891fef3 update to v0.8.5 2022-10-14 15:19:16 +11:00
pocketW
70a0099f2c Merge pull request #67 from XrayR-project/dependabot/go_modules/github.com/fsnotify/fsnotify-1.6.0
build(deps): bump github.com/fsnotify/fsnotify from 1.5.4 to 1.6.0
2022-10-14 08:44:50 +11:00
dependabot[bot]
af8f24b5b1 build(deps): bump github.com/fsnotify/fsnotify from 1.5.4 to 1.6.0
Bumps [github.com/fsnotify/fsnotify](https://github.com/fsnotify/fsnotify) from 1.5.4 to 1.6.0.
- [Release notes](https://github.com/fsnotify/fsnotify/releases)
- [Changelog](https://github.com/fsnotify/fsnotify/blob/main/CHANGELOG.md)
- [Commits](https://github.com/fsnotify/fsnotify/compare/v1.5.4...v1.6.0)

---
updated-dependencies:
- dependency-name: github.com/fsnotify/fsnotify
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2022-10-13 15:51:51 +00:00
pocketW
79528d3e17 feat: dynamic update speedlimit bucket 2022-10-13 08:37:59 +11:00
pocketW
2f0461ddda chore: update lego to v4.9.0 2022-10-05 10:25:07 +11:00
pocketW
023680fec7 Merge pull request #62 from XrayR-project/dependabot/go_modules/github.com/shirou/gopsutil/v3-3.22.9
build(deps): bump github.com/shirou/gopsutil/v3 from 3.22.8 to 3.22.9
2022-10-04 11:50:38 +11:00
dependabot[bot]
708adf1e43 build(deps): bump github.com/shirou/gopsutil/v3 from 3.22.8 to 3.22.9
Bumps [github.com/shirou/gopsutil/v3](https://github.com/shirou/gopsutil) from 3.22.8 to 3.22.9.
- [Release notes](https://github.com/shirou/gopsutil/releases)
- [Commits](https://github.com/shirou/gopsutil/compare/v3.22.8...v3.22.9)

---
updated-dependencies:
- dependency-name: github.com/shirou/gopsutil/v3
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2022-10-03 15:40:50 +00:00
pocketW
0bbec7ebeb Merge pull request #61 from JoshuaCylinder/master
feat: add dynamic speed limit #39
添加动态限速功能
2022-10-03 13:03:58 +11:00
JoshuaCylinder
a9dfd5404f Add default value for AutoSpeedLimitConfig 2022-10-01 07:11:20 +00:00
JoshuaCylinder
39c1036c4a Add AutoSpeedLimitConfig for some special node (For example: IPLC, IEPL, GameNode) 2022-10-01 06:49:51 +00:00
pocketW
7604e33b03 chore: update go to 1.19 2022-09-22 16:12:37 +10:00
pocketW
a906006015 Merge pull request #54 from XrayR-project/dependabot/go_modules/github.com/xtls/xray-core-1.6.0
build(deps): bump github.com/xtls/xray-core from 1.5.10 to 1.6.0
2022-09-20 17:48:04 +10:00
dependabot[bot]
cb5cc17a82 build(deps): bump github.com/xtls/xray-core from 1.5.10 to 1.6.0
Bumps [github.com/xtls/xray-core](https://github.com/xtls/xray-core) from 1.5.10 to 1.6.0.
- [Release notes](https://github.com/xtls/xray-core/releases)
- [Commits](https://github.com/xtls/xray-core/compare/v1.5.10...v1.6.0)

---
updated-dependencies:
- dependency-name: github.com/xtls/xray-core
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2022-09-19 15:52:56 +00:00
pocketW
1cf8bca79e update v0.8.4 2022-09-18 14:26:48 +10:00
pocketW
cbffafbb4c fix: fix CF dns env error #51 2022-09-18 14:19:55 +10:00
pocketW
b37705b374 update v0.8.3 2022-09-11 00:05:22 +10:00
pocketW
e00d228c3d Merge branch 'master' of https://github.com/XrayR-project/XrayR 2022-09-09 22:43:06 +10:00
pocketW
21847fee7d remove dockerhub support 2022-09-09 22:43:00 +10:00
pocketW
f42e30cc61 Merge pull request #44 from XrayR-project/dependabot/go_modules/github.com/spf13/viper-1.13.0
build(deps): bump github.com/spf13/viper from 1.12.0 to 1.13.0
2022-09-07 11:03:44 +10:00
pocketW
e3c7cf2a5f Merge pull request #43 from XrayR-project/dependabot/go_modules/github.com/urfave/cli-1.22.10
build(deps): bump github.com/urfave/cli from 1.22.9 to 1.22.10
2022-09-07 11:03:31 +10:00
dependabot[bot]
82731d9d9c build(deps): bump github.com/spf13/viper from 1.12.0 to 1.13.0
Bumps [github.com/spf13/viper](https://github.com/spf13/viper) from 1.12.0 to 1.13.0.
- [Release notes](https://github.com/spf13/viper/releases)
- [Commits](https://github.com/spf13/viper/compare/v1.12.0...v1.13.0)

---
updated-dependencies:
- dependency-name: github.com/spf13/viper
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2022-09-06 15:39:39 +00:00
dependabot[bot]
45f4d80a49 build(deps): bump github.com/urfave/cli from 1.22.9 to 1.22.10
Bumps [github.com/urfave/cli](https://github.com/urfave/cli) from 1.22.9 to 1.22.10.
- [Release notes](https://github.com/urfave/cli/releases)
- [Changelog](https://github.com/urfave/cli/blob/main/docs/CHANGELOG.md)
- [Commits](https://github.com/urfave/cli/compare/v1.22.9...v1.22.10)

---
updated-dependencies:
- dependency-name: github.com/urfave/cli
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2022-09-05 16:49:36 +00:00
pocketW
10691e4cf3 panic #42 2022-09-03 20:34:22 +10:00
pocketW
9dbb231a12 Merge pull request #41 from XrayR-project/dependabot/go_modules/github.com/shirou/gopsutil/v3-3.22.8
build(deps): bump github.com/shirou/gopsutil/v3 from 3.22.7 to 3.22.8
2022-09-03 20:32:45 +10:00
dependabot[bot]
8724ece88a build(deps): bump github.com/shirou/gopsutil/v3 from 3.22.7 to 3.22.8
Bumps [github.com/shirou/gopsutil/v3](https://github.com/shirou/gopsutil) from 3.22.7 to 3.22.8.
- [Release notes](https://github.com/shirou/gopsutil/releases)
- [Commits](https://github.com/shirou/gopsutil/compare/v3.22.7...v3.22.8)

---
updated-dependencies:
- dependency-name: github.com/shirou/gopsutil/v3
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2022-09-01 15:46:31 +00:00
pocketW
f0610d09fd feat: not reset traffic when report failed 2022-08-30 00:51:21 +10:00
pocketW
8991284e19 chore: update Xray-core to 1.5.10 2022-08-28 14:15:57 +10:00
pocketW
7a12a806a8 Update README.md 2022-08-27 21:05:18 +10:00
pocketW
fda14882a4 Create stale.yml 2022-08-26 22:29:38 +10:00
pocketW
710f081025 fix: #37 2022-08-26 13:00:07 +10:00
pocketW
c280cca7b3 add github docker package 2022-08-25 20:32:07 +10:00
pocketW
fa51cb5309 fix: fix bug when parsing transport protocol 2022-08-24 09:52:45 +10:00
pocketW
529eb46ea0 chore: add hints for config file
doc: add some badges
2022-08-23 11:20:33 +10:00
pocketW
2a528070bf doc: update doc 2022-08-22 22:41:28 +10:00
pocketW
d9089520db doc: update readme 2022-08-22 22:24:05 +10:00
pocketW
bdbf916a14 doc: add support for v2raysocks 2022-08-22 22:13:58 +10:00
pocketW
1ec1765517 fix: trim regexp: prefix for rule 2022-08-22 22:09:54 +10:00
pocketW
f29d2db235 fix: update url for v2raysocks 2022-08-21 21:55:30 +10:00
pocketW
337f441c32 feat: add support for v2raysocks 2022-08-21 00:36:16 +10:00
pocketW
c9895fa02f feat: add support for customize transport protocol 2022-08-17 11:08:56 +10:00
qiuzi
5d6eeb4f29 增加trojan自定义网络协议与加密方式 2022-08-16 16:03:21 +08:00
pocketW
fc9cb4ac10 fix: fix index out of range #27 2022-08-08 11:19:23 +10:00
pocketW
01ef6fb699 Update codeql-analysis.yml 2022-08-04 10:48:02 +10:00
pocketW
e46dc1d8d7 fix: fix incorrect conversion between integer type 2022-08-02 19:42:29 +10:00
pocketW
84db0453cb chore: remove support for ppc64 2022-08-02 19:25:01 +10:00
pocketW
b934a52875 fix: fix get cpu usage error in openbsd
fix: fix incorrect conversion between integer types
fix: fix test file error
chore: update dependencies
2022-08-02 19:03:53 +10:00
pocketW
246f9374a4 Merge pull request #20 from XrayR-project/dependabot/go_modules/google.golang.org/protobuf-1.28.1
build(deps): bump google.golang.org/protobuf from 1.28.0 to 1.28.1
2022-07-30 23:10:39 +10:00
萌新
4dd827d94d Update README.md 2022-07-29 09:40:09 +08:00
dependabot[bot]
c349d3d9a1 build(deps): bump google.golang.org/protobuf from 1.28.0 to 1.28.1
Bumps [google.golang.org/protobuf](https://github.com/protocolbuffers/protobuf-go) from 1.28.0 to 1.28.1.
- [Release notes](https://github.com/protocolbuffers/protobuf-go/releases)
- [Changelog](https://github.com/protocolbuffers/protobuf-go/blob/master/release.bash)
- [Commits](https://github.com/protocolbuffers/protobuf-go/compare/v1.28.0...v1.28.1)

---
updated-dependencies:
- dependency-name: google.golang.org/protobuf
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2022-07-28 15:41:35 +00:00
pocketW
48ca6d9648 chore: update docker.yml 2022-07-28 21:55:23 +10:00
pocketW
f7d1114fb2 doc: update doc url 2022-07-28 21:50:37 +10:00
萌新
dbf7727bcd Update docker.yml 2022-07-28 17:31:10 +08:00
萌新
09f27ec1f1 Update main.go 2022-07-28 17:15:59 +08:00
pocketW
61b501de3d refactor: require features only once 2022-07-21 00:40:44 +10:00
pocketW
9947d7e4f3 chore: update xray-core to 1.5.9 2022-07-19 23:11:21 +10:00
pocketW
a0e1491bb5 update docker actions 2022-07-19 22:35:44 +10:00
misakano7545
b31bef02da Sync to GitLab 2022-06-16 22:15:23 +08:00
pocketW
25594bf9e2 Merge pull request #10 from XrayR-project/dependabot/go_modules/github.com/stretchr/testify-1.7.2
Bump github.com/stretchr/testify from 1.7.1 to 1.7.2
2022-06-14 10:17:40 +10:00
pocketW
1566ffd937 Merge branch 'master' into dependabot/go_modules/github.com/stretchr/testify-1.7.2 2022-06-14 10:17:34 +10:00
pocketW
5682ab37db Merge pull request #9 from XrayR-project/dependabot/go_modules/github.com/xtls/xray-core-1.5.7
Bump github.com/xtls/xray-core from 1.5.6 to 1.5.7
2022-06-14 10:16:52 +10:00
dependabot[bot]
454afe5285 Bump github.com/stretchr/testify from 1.7.1 to 1.7.2
Bumps [github.com/stretchr/testify](https://github.com/stretchr/testify) from 1.7.1 to 1.7.2.
- [Release notes](https://github.com/stretchr/testify/releases)
- [Commits](https://github.com/stretchr/testify/compare/v1.7.1...v1.7.2)

---
updated-dependencies:
- dependency-name: github.com/stretchr/testify
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2022-06-06 15:39:56 +00:00
dependabot[bot]
807a7182bb Bump github.com/xtls/xray-core from 1.5.6 to 1.5.7
Bumps [github.com/xtls/xray-core](https://github.com/xtls/xray-core) from 1.5.6 to 1.5.7.
- [Release notes](https://github.com/xtls/xray-core/releases)
- [Commits](https://github.com/xtls/xray-core/compare/v1.5.6...v1.5.7)

---
updated-dependencies:
- dependency-name: github.com/xtls/xray-core
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2022-06-06 15:39:49 +00:00
pocketW
911d06bece chore: update Xray-core to 1.5.6 2022-06-02 10:01:28 +10:00
pocketW
7f823007cb Merge pull request #7 from XrayR-project/dependabot/go_modules/github.com/go-acme/lego/v4-4.7.0
Bump github.com/go-acme/lego/v4 from 4.6.0 to 4.7.0
2022-06-02 09:34:28 +10:00
pocketW
943660ee56 Merge pull request #6 from XrayR-project/dependabot/go_modules/github.com/spf13/viper-1.12.0
Bump github.com/spf13/viper from 1.11.0 to 1.12.0
2022-06-02 09:34:13 +10:00
pocketW
8195f72eaa Merge pull request #5 from XrayR-project/dependabot/go_modules/github.com/imdario/mergo-0.3.13
Bump github.com/imdario/mergo from 0.3.12 to 0.3.13
2022-06-02 09:34:01 +10:00
dependabot[bot]
35a6585db1 Bump github.com/go-acme/lego/v4 from 4.6.0 to 4.7.0
Bumps [github.com/go-acme/lego/v4](https://github.com/go-acme/lego) from 4.6.0 to 4.7.0.
- [Release notes](https://github.com/go-acme/lego/releases)
- [Changelog](https://github.com/go-acme/lego/blob/master/CHANGELOG.md)
- [Commits](https://github.com/go-acme/lego/compare/v4.6.0...v4.7.0)

---
updated-dependencies:
- dependency-name: github.com/go-acme/lego/v4
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2022-05-30 15:43:57 +00:00
dependabot[bot]
f0911f6239 Bump github.com/spf13/viper from 1.11.0 to 1.12.0
Bumps [github.com/spf13/viper](https://github.com/spf13/viper) from 1.11.0 to 1.12.0.
- [Release notes](https://github.com/spf13/viper/releases)
- [Commits](https://github.com/spf13/viper/compare/v1.11.0...v1.12.0)

---
updated-dependencies:
- dependency-name: github.com/spf13/viper
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2022-05-27 15:53:34 +00:00
dependabot[bot]
afc11dbb41 Bump github.com/imdario/mergo from 0.3.12 to 0.3.13
Bumps [github.com/imdario/mergo](https://github.com/imdario/mergo) from 0.3.12 to 0.3.13.
- [Release notes](https://github.com/imdario/mergo/releases)
- [Commits](https://github.com/imdario/mergo/compare/0.3.12...v0.3.13)

---
updated-dependencies:
- dependency-name: github.com/imdario/mergo
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2022-05-26 15:27:11 +00:00
Misaka No
e1b0cd3c79 Merge pull request #4 from XrayR-project/dependabot/go_modules/github.com/urfave/cli-1.22.9
Bump github.com/urfave/cli from 1.22.8 to 1.22.9
2022-05-07 14:45:29 +08:00
dependabot[bot]
6ce4d4e7a2 Bump github.com/urfave/cli from 1.22.8 to 1.22.9
Bumps [github.com/urfave/cli](https://github.com/urfave/cli) from 1.22.8 to 1.22.9.
- [Release notes](https://github.com/urfave/cli/releases)
- [Changelog](https://github.com/urfave/cli/blob/main/docs/CHANGELOG.md)
- [Commits](https://github.com/urfave/cli/compare/v1.22.8...v1.22.9)

---
updated-dependencies:
- dependency-name: github.com/urfave/cli
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2022-05-05 15:41:09 +00:00
misakano7545
febeee6b2c Add s390x arch support 2022-05-04 11:52:08 +08:00
misakano7545
fbe57822ce Update geosite.dat and geoip.dat 2022-05-03 22:42:07 +08:00
misakano7545
63697586a2 Optimize config file for support WARP Socks5 proxy mode 2022-05-03 22:41:10 +08:00
misakano7545
6b6e2a03d8 Change get script method to wget 2022-04-30 16:58:49 +08:00
pocketW
674445afae fix: fix Fakedns xUDP destination override 2022-04-30 13:19:08 +10:00
pocketW
bf29bd11c4 Merge pull request #3 from XrayR-project/dependabot/go_modules/github.com/urfave/cli-1.22.8
Bump github.com/urfave/cli from 1.22.7 to 1.22.8
2022-04-29 10:33:32 +10:00
dependabot[bot]
40ba9b202b Bump github.com/urfave/cli from 1.22.7 to 1.22.8
Bumps [github.com/urfave/cli](https://github.com/urfave/cli) from 1.22.7 to 1.22.8.
- [Release notes](https://github.com/urfave/cli/releases)
- [Changelog](https://github.com/urfave/cli/blob/main/docs/CHANGELOG.md)
- [Commits](https://github.com/urfave/cli/compare/v1.22.7...v1.22.8)

---
updated-dependencies:
- dependency-name: github.com/urfave/cli
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2022-04-29 00:19:20 +00:00
pocketW
39a09bea92 fix: fix error of config.proto conflict 2022-04-29 10:15:15 +10:00
pocketW
5c0eea8d44 Fix memory leak caused by requiring component. 2022-04-28 14:26:10 +10:00
pocketW
69b0afcb35 Update release.yml 2022-04-27 14:17:07 +10:00
94 changed files with 40862 additions and 15996 deletions

View File

@@ -1,33 +1,95 @@
{
"android-arm64": { "friendlyName": "android-arm64-v8a" },
"darwin-amd64": { "friendlyName": "macos-64" },
"darwin-arm64": { "friendlyName": "macos-arm64-v8a" },
"dragonfly-amd64": { "friendlyName": "dragonfly-64" },
"freebsd-386": { "friendlyName": "freebsd-32" },
"freebsd-amd64": { "friendlyName": "freebsd-64" },
"freebsd-arm64": { "friendlyName": "freebsd-arm64-v8a" },
"freebsd-arm7": { "friendlyName": "freebsd-arm32-v7a" },
"linux-386": { "friendlyName": "linux-32" },
"linux-amd64": { "friendlyName": "linux-64" },
"linux-arm5": { "friendlyName": "linux-arm32-v5" },
"linux-arm64": { "friendlyName": "linux-arm64-v8a" },
"linux-arm6": { "friendlyName": "linux-arm32-v6" },
"linux-arm7": { "friendlyName": "linux-arm32-v7a" },
"linux-mips64le": { "friendlyName": "linux-mips64le" },
"linux-mips64": { "friendlyName": "linux-mips64" },
"linux-mipslesoftfloat": { "friendlyName": "linux-mips32le-softfloat" },
"linux-mipsle": { "friendlyName": "linux-mips32le" },
"linux-mipssoftfloat": { "friendlyName": "linux-mips32-softfloat" },
"linux-mips": { "friendlyName": "linux-mips32" },
"linux-ppc64le": { "friendlyName": "linux-ppc64le" },
"linux-ppc64": { "friendlyName": "linux-ppc64" },
"linux-riscv64": { "friendlyName": "linux-riscv64" },
"linux-s390x": { "friendlyName": "linux-s390x" },
"openbsd-386": { "friendlyName": "openbsd-32" },
"openbsd-amd64": { "friendlyName": "openbsd-64" },
"openbsd-arm64": { "friendlyName": "openbsd-arm64-v8a" },
"openbsd-arm7": { "friendlyName": "openbsd-arm32-v7a" },
"windows-386": { "friendlyName": "windows-32" },
"windows-amd64": { "friendlyName": "windows-64" },
"windows-arm7": { "friendlyName": "windows-arm32-v7a" }
}
"android-arm64": {
"friendlyName": "android-arm64-v8a"
},
"darwin-amd64": {
"friendlyName": "macos-64"
},
"darwin-arm64": {
"friendlyName": "macos-arm64-v8a"
},
"dragonfly-amd64": {
"friendlyName": "dragonfly-64"
},
"freebsd-386": {
"friendlyName": "freebsd-32"
},
"freebsd-amd64": {
"friendlyName": "freebsd-64"
},
"freebsd-arm64": {
"friendlyName": "freebsd-arm64-v8a"
},
"freebsd-arm7": {
"friendlyName": "freebsd-arm32-v7a"
},
"linux-386": {
"friendlyName": "linux-32"
},
"linux-amd64": {
"friendlyName": "linux-64"
},
"linux-arm5": {
"friendlyName": "linux-arm32-v5"
},
"linux-arm64": {
"friendlyName": "linux-arm64-v8a"
},
"linux-arm6": {
"friendlyName": "linux-arm32-v6"
},
"linux-arm7": {
"friendlyName": "linux-arm32-v7a"
},
"linux-mips64le": {
"friendlyName": "linux-mips64le"
},
"linux-mips64": {
"friendlyName": "linux-mips64"
},
"linux-mipslesoftfloat": {
"friendlyName": "linux-mips32le-softfloat"
},
"linux-mipsle": {
"friendlyName": "linux-mips32le"
},
"linux-mipssoftfloat": {
"friendlyName": "linux-mips32-softfloat"
},
"linux-mips": {
"friendlyName": "linux-mips32"
},
"linux-ppc64le": {
"friendlyName": "linux-ppc64le"
},
"linux-ppc64": {
"friendlyName": "linux-ppc64"
},
"linux-riscv64": {
"friendlyName": "linux-riscv64"
},
"linux-s390x": {
"friendlyName": "linux-s390x"
},
"openbsd-386": {
"friendlyName": "openbsd-32"
},
"openbsd-amd64": {
"friendlyName": "openbsd-64"
},
"openbsd-arm64": {
"friendlyName": "openbsd-arm64-v8a"
},
"openbsd-arm7": {
"friendlyName": "openbsd-arm32-v7a"
},
"windows-386": {
"friendlyName": "windows-32"
},
"windows-amd64": {
"friendlyName": "windows-64"
},
"windows-arm7": {
"friendlyName": "windows-arm32-v7a"
}
}

View File

@@ -34,34 +34,38 @@ jobs:
# https://docs.github.com/en/free-pro-team@latest/github/finding-security-vulnerabilities-and-errors-in-your-code/configuring-code-scanning#changing-the-languages-that-are-analyzed
steps:
- name: Checkout repository
uses: actions/checkout@v2
- name: Checkout repository
uses: actions/checkout@v3
# Initializes the CodeQL tools for scanning.
- name: Initialize CodeQL
uses: github/codeql-action/init@v1
with:
languages: ${{ matrix.language }}
# If you wish to specify custom queries, you can do so here or in a config file.
# By default, queries listed here will override any specified in a config file.
# Prefix the list here with "+" to use these queries and those in the config file.
# queries: ./path/to/local/query, your-org/your-repo/queries@main
# Initializes the CodeQL tools for scanning.
- name: Initialize CodeQL
uses: github/codeql-action/init@v2
with:
languages: ${{ matrix.language }}
# If you wish to specify custom queries, you can do so here or in a config file.
# By default, queries listed here will override any specified in a config file.
# Prefix the list here with "+" to use these queries and those in the config file.
# queries: ./path/to/local/query, your-org/your-repo/queries@main
- name: Install Go
uses: actions/setup-go@v4
with:
go-version-file: go.mod
# Autobuild attempts to build any compiled languages (C/C++, C#, or Java).
# If this step fails, then you should remove it and run the build manually (see below)
- name: Autobuild
uses: github/codeql-action/autobuild@v2
# Autobuild attempts to build any compiled languages (C/C++, C#, or Java).
# If this step fails, then you should remove it and run the build manually (see below)
- name: Autobuild
uses: github/codeql-action/autobuild@v1
# Command-line programs to run using the OS shell.
# 📚 https://git.io/JvXDl
# Command-line programs to run using the OS shell.
# 📚 https://git.io/JvXDl
# If the Autobuild fails above, remove it and uncomment the following three lines
# and modify them (or add more) to build your code if your project
# uses a compiled language
# ✏️ If the Autobuild fails above, remove it and uncomment the following three lines
# and modify them (or add more) to build your code if your project
# uses a compiled language
#- run: |
# make bootstrap
# make release
#- run: |
# make bootstrap
# make release
- name: Perform CodeQL Analysis
uses: github/codeql-action/analyze@v1
- name: Perform CodeQL Analysis
uses: github/codeql-action/analyze@v2

View File

@@ -2,39 +2,104 @@ name: Publish Docker image
on:
workflow_dispatch:
push:
branches:
- master
paths:
- "**/*.go"
- "go.mod"
- "go.sum"
- ".github/workflows/*.yml"
tags:
- 'v*'
pull_request:
branches:
- 'master'
env:
REGISTRY: ghcr.io
IMAGE_NAME: xrayr-project/xrayr
jobs:
push_to_registry:
name: Push Docker image to Docker Hub
build:
runs-on: ubuntu-latest
strategy:
fail-fast: false
matrix:
platform:
- linux/amd64
- linux/arm/v6
- linux/arm/v7
- linux/arm64
- linux/s390x
steps:
- name: Check out the repo
uses: actions/checkout@v2
-
name: Docker meta
id: docker_meta
uses: crazy-max/ghaction-docker-meta@v1
- name: Checkout
uses: actions/checkout@v4
- name: Docker meta
id: meta
uses: docker/metadata-action@v5
with:
images: crackair/xrayr
tag-semver: |
{{version}}
{{major}}.{{minor}}
-
name: Set up Docker Buildx
uses: docker/setup-buildx-action@v1
-
name: Login to DockerHub
uses: docker/login-action@v1
images: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}
- name: Set up QEMU
uses: docker/setup-qemu-action@v3
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v3
- name: Login to GitHub Container Registry
uses: docker/login-action@v3
with:
username: ${{ secrets.DOCKER_USERNAME }}
password: ${{ secrets.DOCKER_PASSWORD }}
-
name: Build and push
uses: docker/build-push-action@v2
registry: ${{ env.REGISTRY }}
username: ${{ github.actor }}
password: ${{ secrets.GITHUB_TOKEN }}
- name: Build and push by digest
id: build
uses: docker/build-push-action@v5
with:
context: .
platforms: linux/arm/v7,linux/arm64,linux/amd64
push: ${{ github.event_name != 'pull_request' }}
tags: ${{ steps.docker_meta.outputs.tags }}
labels: ${{ steps.docker_meta.outputs.labels }}
platforms: ${{ matrix.platform }}
labels: ${{ steps.meta.outputs.labels }}
outputs: type=image,name=${{ env.REGISTRY }}/${{ env.IMAGE_NAME }},push-by-digest=true,name-canonical=true,push=true
- name: Export digest
run: |
mkdir -p /tmp/digests
digest="${{ steps.build.outputs.digest }}"
touch "/tmp/digests/${digest#sha256:}"
- name: Upload digest
uses: actions/upload-artifact@v3
with:
name: digests
path: /tmp/digests/*
if-no-files-found: error
retention-days: 1
merge:
runs-on: ubuntu-latest
needs:
- build
steps:
- name: Download digests
uses: actions/download-artifact@v3
with:
name: digests
path: /tmp/digests
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v3
- name: Docker meta
id: meta
uses: docker/metadata-action@v5
with:
images: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}
- name: Login to GitHub Container Registry
uses: docker/login-action@v3
with:
registry: ${{ env.REGISTRY }}
username: ${{ github.actor }}
password: ${{ secrets.GITHUB_TOKEN }}
- name: Create manifest list and push
working-directory: /tmp/digests
run: |
ls -al
echo docker buildx imagetools create $(jq -cr '.tags | map("-t " + .) | join(" ")' <<< "$DOCKER_METADATA_OUTPUT_JSON") \
$(printf '${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}@sha256:%s ' *)
docker buildx imagetools create $(jq -cr '.tags | map("-t " + .) | join(" ")' <<< "$DOCKER_METADATA_OUTPUT_JSON") \
$(printf '${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}@sha256:%s ' *)
- name: Inspect image
run: |
docker buildx imagetools inspect ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:${{ steps.meta.outputs.version }}

View File

@@ -11,14 +11,14 @@ on:
- "go.sum"
- ".github/workflows/*.yml"
pull_request:
types: [opened, synchronize, reopened]
types: [ opened, synchronize, reopened ]
paths:
- "**/*.go"
- "go.mod"
- "go.sum"
- ".github/workflows/*.yml"
release:
types: [published]
types: [ published ]
jobs:
@@ -26,8 +26,8 @@ jobs:
strategy:
matrix:
# Include amd64 on all platforms.
goos: [windows, freebsd, openbsd, linux, dragonfly, darwin]
goarch: [amd64, 386]
goos: [ windows, freebsd, openbsd, linux, dragonfly, darwin ]
goarch: [ amd64, 386 ]
exclude:
# Exclude i386 on darwin and dragonfly.
- goarch: 386
@@ -74,8 +74,8 @@ jobs:
goarch: mips
# END MIPS
# BEGIN PPC
- goos: linux
goarch: ppc64
# - goos: linux # Removed due to the unsupport of shirou/gopsutil
# goarch: ppc64
- goos: linux
goarch: ppc64le
# END PPC
@@ -92,7 +92,7 @@ jobs:
# END S390X
# END Other architectures
fail-fast: false
runs-on: ubuntu-latest
env:
GOOS: ${{ matrix.goos }}
@@ -101,19 +101,19 @@ jobs:
CGO_ENABLED: 0
steps:
- name: Checkout codebase
uses: actions/checkout@v2
- name: Show workflow information
uses: actions/checkout@v3
- name: Show workflow information
id: get_filename
run: |
export _NAME=$(jq ".[\"$GOOS-$GOARCH$GOARM$GOMIPS\"].friendlyName" -r < .github/build/friendly-filenames.json)
echo "GOOS: $GOOS, GOARCH: $GOARCH, GOARM: $GOARM, GOMIPS: $GOMIPS, RELEASE_NAME: $_NAME"
echo "::set-output name=ASSET_NAME::$_NAME"
echo "ASSET_NAME=$_NAME" >> $GITHUB_OUTPUT
echo "ASSET_NAME=$_NAME" >> $GITHUB_ENV
- name: Set up Go
uses: actions/setup-go@v2
uses: actions/setup-go@v3
with:
go-version: ^1.17.2
go-version: ^1.20
- name: Get project dependencies
run: go mod download
@@ -123,7 +123,7 @@ jobs:
run: |
mkdir -p build_assets
go build -v -o build_assets/XrayR -trimpath -ldflags "-s -w -buildid=" ./main
- name: Build Mips softfloat XrayR
if: matrix.goarch == 'mips' || matrix.goarch == 'mipsle'
run: |
@@ -135,26 +135,32 @@ jobs:
mv XrayR XrayR.exe
- name: Prepare to release
run: |
cp ${GITHUB_WORKSPACE}/README.md ./build_assets/README.md
cp ${GITHUB_WORKSPACE}/LICENSE ./build_assets/LICENSE
cp ${GITHUB_WORKSPACE}/main/dns.json ./build_assets/dns.json
cp ${GITHUB_WORKSPACE}/main/route.json ./build_assets/route.json
cp ${GITHUB_WORKSPACE}/main/custom_outbound.json ./build_assets/custom_outbound.json
cp ${GITHUB_WORKSPACE}/main/rulelist ./build_assets/rulelist
cp ${GITHUB_WORKSPACE}/main/config.yml.example ./build_assets/config.yml
LIST=('geoip geoip geoip' 'domain-list-community dlc geosite')
for i in "${LIST[@]}"
do
INFO=($(echo $i | awk 'BEGIN{FS=" ";OFS=" "} {print $1,$2,$3}'))
LASTEST_TAG="$(curl -sL "https://api.github.com/repos/v2fly/${INFO[0]}/releases" | jq -r ".[0].tag_name" || echo "latest")"
FILE_NAME="${INFO[2]}.dat"
echo -e "Downloading ${FILE_NAME}..."
curl -L "https://github.com/v2fly/${INFO[0]}/releases/download/${LASTEST_TAG}/${INFO[1]}.dat" -o ./build_assets/${FILE_NAME}
echo -e "Verifying HASH key..."
HASH="$(curl -sL "https://github.com/v2fly/${INFO[0]}/releases/download/${LASTEST_TAG}/${INFO[1]}.dat.sha256sum" | awk -F ' ' '{print $1}')"
[ "$(sha256sum "./build_assets/${FILE_NAME}" | awk -F ' ' '{print $1}')" == "${HASH}" ] || { echo -e "The HASH key of ${FILE_NAME} does not match cloud one."; exit 1; }
done
uses: nick-fields/retry@v2
with:
timeout_minutes: 60
retry_wait_seconds: 60
max_attempts: 5
command: |
cp ${GITHUB_WORKSPACE}/README.md ./build_assets/README.md
cp ${GITHUB_WORKSPACE}/LICENSE ./build_assets/LICENSE
cp ${GITHUB_WORKSPACE}/main/dns.json ./build_assets/dns.json
cp ${GITHUB_WORKSPACE}/main/route.json ./build_assets/route.json
cp ${GITHUB_WORKSPACE}/main/custom_outbound.json ./build_assets/custom_outbound.json
cp ${GITHUB_WORKSPACE}/main/custom_inbound.json ./build_assets/custom_inbound.json
cp ${GITHUB_WORKSPACE}/main/rulelist ./build_assets/rulelist
cp ${GITHUB_WORKSPACE}/main/config.yml.example ./build_assets/config.yml
LIST=('geoip geoip geoip' 'domain-list-community dlc geosite')
for i in "${LIST[@]}"
do
INFO=($(echo $i | awk 'BEGIN{FS=" ";OFS=" "} {print $1,$2,$3}'))
DOWNLOAD_URL="https://raw.githubusercontent.com/v2fly/${INFO[0]}/release/${INFO[1]}.dat"
FILE_NAME="${INFO[2]}.dat"
echo -e "Downloading ${DOWNLOAD_URL}..."
curl -L "${DOWNLOAD_URL}" -o ./build_assets/${FILE_NAME}
echo -e "Verifying HASH key..."
HASH="$(curl -sL "${DOWNLOAD_URL}.sha256sum" | awk -F ' ' '{print $1}')"
[ "$(sha256sum "./build_assets/${FILE_NAME}" | awk -F ' ' '{print $1}')" == "${HASH}" ] || { echo -e "The HASH key of ${FILE_NAME} does not match cloud one."; exit 1; }
done
- name: Create ZIP archive
shell: bash
run: |
@@ -172,7 +178,7 @@ jobs:
run: |
mv build_assets XrayR-$ASSET_NAME
- name: Upload files to Artifacts
uses: actions/upload-artifact@v2
uses: actions/upload-artifact@v3
with:
name: XrayR-${{ steps.get_filename.outputs.ASSET_NAME }}
path: |

22
.github/workflows/stale.yml vendored Normal file
View File

@@ -0,0 +1,22 @@
name: Close inactive issues
on:
schedule:
- cron: "30 1 * * *"
jobs:
close-issues:
runs-on: ubuntu-latest
permissions:
issues: write
pull-requests: write
steps:
- uses: actions/stale@v5
with:
days-before-issue-stale: 30
days-before-issue-close: 14
stale-issue-label: "stale"
stale-issue-message: "This issue is stale because it has been open for 30 days with no activity."
close-issue-message: "This issue was closed because it has been inactive for 14 days since being marked as stale."
days-before-pr-stale: -1
days-before-pr-close: -1
repo-token: ${{ secrets.GITHUB_TOKEN }}

5
.gitignore vendored
View File

@@ -12,4 +12,7 @@ main/.lego
main/cert
main/config.yml
./vscode
.idea/*
.idea/*
.DS_Store
*.bak
go.work*

View File

@@ -1,5 +1,5 @@
# Build go
FROM golang:1.17-alpine AS builder
FROM golang:1.21-alpine AS builder
WORKDIR /app
COPY . .
ENV CGO_ENABLED=0

109
README-en.md Normal file
View File

@@ -0,0 +1,109 @@
# XrayR
[![](https://img.shields.io/badge/TgChat-@XrayR讨论-blue.svg)](https://t.me/XrayR_project)
[![](https://img.shields.io/badge/Channel-@XrayR通知-blue.svg)](https://t.me/XrayR_channel)
![](https://img.shields.io/github/stars/XrayR-project/XrayR)
![](https://img.shields.io/github/forks/XrayR-project/XrayR)
![](https://github.com/XrayR-project/XrayR/actions/workflows/release.yml/badge.svg)
![](https://github.com/XrayR-project/XrayR/actions/workflows/docker.yml/badge.svg)
[![Github All Releases](https://img.shields.io/github/downloads/XrayR-project/XrayR/total.svg)]()
[Iranian(farsi) README](https://github.com/XrayR-project/XrayR/blob/master/README_Fa.md), [Vietnamese(vi) README](https://github.com/XrayR-project/XrayR/blob/master/README-vi.md), [English(en) README](https://github.com/XrayR-project/XrayR/blob/master/README-en.md)
A Xray backend framework that can easily support many panels.
A back -end framework based on XRAY supports V2ay, Trojan, Shadowsocks protocols, which are easy to expand and support multi -panel docker.
If you like this project, you can click STAR+WATCH in the upper right corner to continue to pay attention to the progress of this project.
## Guide for use
Tutorial[Detailed tutorial](https://xrayr-project.github.io/XrayR-doc/)
## Disclaimer
This project is just my personal learning and development and maintenance. I do not guarantee any availability and is not responsible for any consequences caused by the use of this software.
## Features
* Permanent open source and free.
* Support V2Ray, Trojan, Shadowsocks multiple protocols.
* Support new features such as Vless and XTLS.
* Support single instance docking multi -panel and multi -node, no need to start repeatedly.
* Support restriction online IP
* Support node port level and user level speed limit.
* The configuration is simple and clear.
* Modify the automatic restart instance.
* Easy to compile and upgrade, you can quickly update the core version and support the new features of XRAY-CORE.
## Function
| Function | v2ray | trojan | shadowsocks |
|-----------|-------|--------|-------------|
| Get node information | √ | √ | √ |
| Get user information | √ | √ | √ |
| User traffic statistics | √ | √ | √ |
| Server information report | √ | √ | √ |
| Automatically apply for a TLS certificate | √ | √ | √ |
| Automatic renewal TLS certificate | √ | √ | √ |
| Number of online people | √ | √ | √ |
| Online user restrictions | √ | √ | √ |
| Audit rules | √ | √ | √ |
| Node port speed limit | √ | √ | √ |
| According to user speed limit | √ | √ | √ |
| Custom DNS | √ | √ | √ |
## Support for panels
| Panel | v2ray | trojan | shadowsocks |
|--------------------------------------------------------|-------|--------|-------------------------|
| sspanel-uim | √ | √ | √ (Single-ended multi-user and V2Ray-Plugin) |
| v2board | √ | √ | √ |
| [PMPanel](https://github.com/ByteInternetHK/PMPanel) | √ | √ | √ |
| [ProxyPanel](https://github.com/ProxyPanel/ProxyPanel) | √ | √ | √ |
| [WHMCS (V2RaySocks)](https://v2raysocks.doxtex.com/) | √ | √ | √ |
## Software Installation
### 1-Click installation
```
wget -N https://raw.githubusercontent.com/XrayR-project/XrayR-release/master/install.sh && bash install.sh
```
### Docker
[Docker deployment tutorial](https://xrayr-project.github.io/XrayR-doc/xrayr-xia-zai-he-an-zhuang/install/docker)
### Manual installation
[Manual installation tutorial](https://xrayr-project.github.io/XrayR-doc/xrayr-xia-zai-he-an-zhuang/install/manual)
## Configuration file and detailed use tutorial
[Detailed tutorial](https://xrayr-project.github.io/XrayR-doc/)
## Thanks
* [Project X](https://github.com/XTLS/)
* [V2Fly](https://github.com/v2fly)
* [VNet-V2ray](https://github.com/ProxyPanel/VNet-V2ray)
* [Air-Universe](https://github.com/crossfw/Air-Universe)
## Licence
[Mozilla Public License Version 2.0](https://github.com/XrayR-project/XrayR/blob/master/LICENSE)
## Telgram
[Xrayr back-end discussion](https://t.me/XrayR_project)
[Xrayr notification](https://t.me/XrayR_channel)
## Stargazers over time
[![Stargazers over time](https://starchart.cc/XrayR-project/XrayR.svg)](https://starchart.cc/XrayR-project/XrayR)

104
README-vi.md Normal file
View File

@@ -0,0 +1,104 @@
# XrayR
[![](https://img.shields.io/badge/TgChat-@XrayR讨论-blue.svg)](https://t.me/XrayR_project)
[![](https://img.shields.io/badge/Channel-@XrayR通知-blue.svg)](https://t.me/XrayR_channel)
![](https://img.shields.io/github/stars/XrayR-project/XrayR)
![](https://img.shields.io/github/forks/XrayR-project/XrayR)
![](https://github.com/XrayR-project/XrayR/actions/workflows/release.yml/badge.svg)
![](https://github.com/XrayR-project/XrayR/actions/workflows/docker.yml/badge.svg)
[![Github All Releases](https://img.shields.io/github/downloads/XrayR-project/XrayR/total.svg)]()
[Iranian(farsi) README](https://github.com/XrayR-project/XrayR/blob/master/README_Fa.md), [Vietnamese(vi) README](https://github.com/XrayR-project/XrayR/blob/master/README-vi.md), [English(en) README](https://github.com/XrayR-project/XrayR/blob/master/README-en.md)
A Xray backend framework that can easily support many panels.
Khung trở lại dựa trên XRay hỗ trợ các giao thức V2ay, Trojan, Shadowsocks, dễ dàng mở rộng và hỗ trợ kết nối nhiều người.
Nếu bạn thích dự án này, bạn có thể nhấp vào Star+Watch ở góc trên bên phải để tiếp tục chú ý đến tiến trình của dự án này.
## Tài liệu
Sử dụng hướng dẫn: [Hướng dẫn chi tiết](https://xrayr-project.github.io/XrayR-doc/) ( Tiếng Trung )
## Tuyên bố miễn trừ
Dự án này chỉ là học tập và phát triển và bảo trì cá nhân của tôi. Tôi không đảm bảo bất kỳ sự sẵn có nào và không chịu trách nhiệm cho bất kỳ hậu quả nào do việc sử dụng phần mềm này.
## Đặt điểm nổi bật
* Nguồn mở vĩnh viễn và miễn phí.
* Hỗ trợ V2Ray, Trojan, Shadowsocks nhiều giao thức.
* Hỗ trợ các tính năng mới như Vless và XTL.
* Hỗ trợ trường hợp đơn lẻ kết nối Multi -Panel và Multi -Node, không cần phải bắt đầu nhiều lần.
* Hỗ trợ hạn chế IP trực tuyến
* Hỗ trợ cấp cổng nút và giới hạn tốc độ cấp người dùng.
* Cấu hình đơn giản và rõ ràng.
* Sửa đổi phiên bản khởi động lại tự động.
* Dễ dàng biên dịch và nâng cấp, bạn có thể nhanh chóng cập nhật phiên bản cốt lõi và hỗ trợ các tính năng mới của Xray-Core.
## Chức năng
| Chức năng | v2ray | trojan | shadowsocks |
|-----------|-------|--------|-------------|
| Nhận thông tin Node | √ | √ | √ |
| Nhận thông tin người dùng | √ | √ | √ |
| Thống kê lưu lượng người dùng | √ | √ | √ |
| Báo cáo thông tin máy chủ | √ | √ | √ |
| Tự động đăng ký chứng chỉ TLS | √ | √ | √ |
| Chứng chỉ TLS gia hạn tự động | √ | √ | √ |
| Số người trực tuyến | √ | √ | √ |
| Hạn chế người dùng trực tuyến | √ | √ | √ |
| Quy tắc kiểm toán | √ | √ | √ |
| Giới hạn tốc độ cổng nút | √ | √ | √ |
| Theo giới hạn tốc độ người dùng | √ | √ | √ |
| DNS tùy chỉnh | √ | √ | √ |
## Hỗ trợ Panel
| Panel | v2ray | trojan | shadowsocks |
|--------------------------------------------------------|-------|--------|-------------------------|
| sspanel-uim | √ | √ | √ (Nhiều người dùng cuối và v2ray-plugin) |
| v2board | √ | √ | √ |
| [PMPanel](https://github.com/ByteInternetHK/PMPanel) | √ | √ | √ |
| [ProxyPanel](https://github.com/ProxyPanel/ProxyPanel) | √ | √ | √ |
| [WHMCS (V2RaySocks)](https://v2raysocks.doxtex.com/) | √ | √ | √ |
## Cài đặt phần mềm
### Một cài đặt chính
```
wget -N https://raw.githubusercontent.com/XrayR-project/XrayR-release/master/install.sh && bash install.sh
```
### Sử dụng phần mềm triển khai Docker
[Hướng dẫn cài đặt thông qua Docker](https://xrayr-project.github.io/XrayR-doc/xrayr-xia-zai-he-an-zhuang/install/docker)
### Hướng dẫn cài đặt
[Hướng dẫn cài đặt thủ công](https://xrayr-project.github.io/XrayR-doc/xrayr-xia-zai-he-an-zhuang/install/manual)
## Tệp cấu hình và hướng dẫn sử dụng chi tiết
[Hướng dẫn chi tiết](https://xrayr-project.github.io/XrayR-doc/)
## Thanks
* [Project X](https://github.com/XTLS/)
* [V2Fly](https://github.com/v2fly)
* [VNet-V2ray](https://github.com/ProxyPanel/VNet-V2ray)
* [Air-Universe](https://github.com/crossfw/Air-Universe)
## Licence
[Mozilla Public License Version 2.0](https://github.com/XrayR-project/XrayR/blob/master/LICENSE)
## Telgram
[Xrayr Back-end Thảo luận](https://t.me/XrayR_project)
[Thông báo Xrayr](https://t.me/XrayR_channel)
## Stargazers over time
[![Stargazers over time](https://starchart.cc/XrayR-project/XrayR.svg)](https://starchart.cc/XrayR-project/XrayR)

View File

@@ -1,6 +1,15 @@
# XrayR
[![](https://img.shields.io/badge/TgChat-@XrayR讨论-blue.svg)](https://t.me/XrayR_project)
[![](https://img.shields.io/badge/Channel-@XrayR通知-blue.svg)](https://t.me/XrayR_channel)
![](https://img.shields.io/github/stars/XrayR-project/XrayR)
![](https://img.shields.io/github/forks/XrayR-project/XrayR)
![](https://github.com/XrayR-project/XrayR/actions/workflows/release.yml/badge.svg)
![](https://github.com/XrayR-project/XrayR/actions/workflows/docker.yml/badge.svg)
[![Github All Releases](https://img.shields.io/github/downloads/XrayR-project/XrayR/total.svg)]()
[English](https://github.com/XrayR-project/XrayR/blob/master/README-en.md)|[Iranian](https://github.com/XrayR-project/XrayR/blob/master/README_Fa.md)|[Vietnamese](https://github.com/XrayR-project/XrayR/blob/master/README-vi.md)
A Xray backend framework that can easily support many panels.
@@ -8,12 +17,15 @@ A Xray backend framework that can easily support many panels.
如果您喜欢本项目可以右上角点个star+watch持续关注本项目的进展。
使用教程:[详细使用教程](https://crackair.gitbook.io/xrayr-project/)
使用教程:[详细使用教程](https://xrayr-project.github.io/XrayR-doc/)
## 免责声明
本项目只是本人个人学习开发并维护,本人不保证任何可用性,也不对使用本软件造成的任何后果负责。
## 特点
* 永久开源且免费。
* 支持V2rayTrojan Shadowsocks多种协议。
* 支持Vless和XTLS等新特性。
@@ -26,43 +38,51 @@ A Xray backend framework that can easily support many panels.
## 功能介绍
| 功能 | v2ray | trojan | shadowsocks |
| --------------- | ----- | ------ | ----------- |
| 功能 | v2ray | trojan | shadowsocks |
|-----------|-------|--------|-------------|
| 获取节点信息 | √ | √ | √ |
| 获取用户信息 | √ | √ | √ |
| 用户流量统计 | √ | √ | √ |
| 服务器信息上报 | √ | √ | √ |
| 服务器信息上报 | √ | √ | √ |
| 自动申请tls证书 | √ | √ | √ |
| 自动续签tls证书 | √ | √ | √ |
| 在线人数统计 | √ | √ | √ |
| 在线用户限制 | √ | √ | √ |
| 审计规则 | √ | √ | √ |
| 审计规则 | √ | √ | √ |
| 节点端口限速 | √ | √ | √ |
| 按照用户限速 | √ | √ | √ |
| 自定义DNS | √ | √ | √ |
| 自定义DNS | √ | √ | √ |
## 支持前端
| 前端 | v2ray | trojan | shadowsocks |
| ------------------------------------------------------ | ----- | ------ | ------------------------------ |
| 前端 | v2ray | trojan | shadowsocks |
|--------------------------------------------------------|-------|--------|-------------------------|
| sspanel-uim | √ | √ | √ (单端口多用户和V2ray-Plugin) |
| v2board | √ | √ | √ |
| [PMPanel](https://github.com/ByteInternetHK/PMPanel) | √ | √ | √ |
| [ProxyPanel](https://github.com/ProxyPanel/ProxyPanel) | √ | √ | √ |
| v2board | √ | √ | √ |
| [PMPanel](https://github.com/ByteInternetHK/PMPanel) | √ | √ | √ |
| [ProxyPanel](https://github.com/ProxyPanel/ProxyPanel) | √ | √ | √ |
| [WHMCS (V2RaySocks)](https://v2raysocks.doxtex.com/) | √ | √ | √ |
| [GoV2Panel](https://github.com/pingProMax/gov2panel) | √ | √ | √ |
## 软件安装
### 一键安装
```
bash <(curl -Ls https://raw.githubusercontent.com/XrayR-project/XrayR-release/master/install.sh)
wget -N https://raw.githubusercontent.com/XrayR-project/XrayR-release/master/install.sh && bash install.sh
```
### 使用Docker部署软件
[Docker部署教程](https://crackair.gitbook.io/xrayr-project/xrayr-xia-zai-he-an-zhuang/install/docker)
[Docker部署教程](https://xrayr-project.github.io/XrayR-doc/xrayr-xia-zai-he-an-zhuang/install/docker)
### 手动安装
[手动安装教程](https://crackair.gitbook.io/xrayr-project/xrayr-xia-zai-he-an-zhuang/install/manual)
[手动安装教程](https://xrayr-project.github.io/XrayR-doc/xrayr-xia-zai-he-an-zhuang/install/manual)
## 配置文件及详细使用教程
[详细使用教程](https://crackair.gitbook.io/xrayr-project/)
[详细使用教程](https://xrayr-project.github.io/XrayR-doc/)
## Thanks
@@ -80,7 +100,9 @@ bash <(curl -Ls https://raw.githubusercontent.com/XrayR-project/XrayR-release/ma
[XrayR后端讨论](https://t.me/XrayR_project)
[XrayR通知](https://t.me/XrayR_channel)
## Stargazers over time
[![Stargazers over time](https://starchart.cc/XrayR-project/XrayR.svg)](https://starchart.cc/XrayR-project/XrayR)

104
README_Fa.md Normal file
View File

@@ -0,0 +1,104 @@
# XrayR
[![](https://img.shields.io/badge/TgChat-@XrayR讨论-blue.svg)](https://t.me/XrayR_project)
[![](https://img.shields.io/badge/Channel-@XrayR通知-blue.svg)](https://t.me/XrayR_channel)
![](https://img.shields.io/github/stars/XrayR-project/XrayR)
![](https://img.shields.io/github/forks/XrayR-project/XrayR)
![](https://github.com/XrayR-project/XrayR/actions/workflows/release.yml/badge.svg)
![](https://github.com/XrayR-project/XrayR/actions/workflows/docker.yml/badge.svg)
[![Github All Releases](https://img.shields.io/github/downloads/XrayR-project/XrayR/total.svg)]()
[Iranian(farsi) README](https://github.com/XrayR-project/XrayR/blob/master/README_Fa.md), [Vietnamese(vi) README](https://github.com/XrayR-project/XrayR/blob/master/README-vi.md), [English(en) README](https://github.com/XrayR-project/XrayR/blob/master/README-en.md)
یک فریمورک بک اند مبتنی بر xray که از چند از پنل پشتیبانی می کند
یک چارچوب بک‌اند مبتنی بر Xray که از پروتکل‌های V2ay، Trojan و Shadowsocks پشتیبانی می‌کند، به راحتی قابل گسترش است و از اتصال چند پنل پشتیبانی می‌کند.
اگر این پروژه را دوست دارید، می توانید با کلیک بر روی ستاره+ساعت در گوشه بالا سمت راست به ادامه روند پیشرفت این پروژه توجه کنید.
آموزش:[اموزش با جزئیات](https://xrayr-project.github.io/XrayR-doc/)
## سلب مسئولیت
این پروژه فقط مطالعه، توسعه و نگهداری شخصی من است. من هیچ گونه قابلیت استفاده را تضمین نمی کنم و مسئولیتی در قبال عواقب ناشی از استفاده از این نرم افزار ندارم.
## امکانات
* منبع باز دائمی و رایگان
* پشتیبانی از چندین پروتکل V2ray، Trojan، Shadowsocks.
* پشتیبانی از ویژگی های جدید مانند Vless و XTLS.
* پشتیبانی از اتصال یک نمونه چند پانل، چند گره، بدون نیاز به شروع مکرر.
* پشتیبانی محدود IP آنلاین
* پشتیبانی از سطح پورت گره، محدودیت سرعت سطح کاربر.
* پیکربندی ساده و سرراست است.
* پیکربندی را تغییر دهید تا نمونه به طور خودکار راه اندازی مجدد شود.
* کامپایل و ارتقاء آن آسان است و می تواند به سرعت نسخه اصلی را به روز کند و از ویژگی های جدید Xray-core پشتیبانی می کند.
## امکانات
| امکانات | v2ray | trojan | shadowsocks |
|-----------|-------|--------|-------------|
| اطلاعات گره را دریافت کنید | √ | √ | √ |
| دریافت اطلاعات کاربر | √ | √ | √ |
| آمار ترافیک کاربران | √ | √ | √ |
| گزارش اطلاعات سرور | √ | √ | √ |
| به طور خودکار برای گواهی tls درخواست دهید | √ | √ | √ |
| تمدید خودکار گواهی tls | √ | √ | √ |
| آمار آنلاین | √ | √ | √ |
| محدودیت کاربر آنلاین | √ | √ | √ |
| قوانین حسابرسی | √ | √ | √ |
| محدودیت سرعت پورت گره | √ | √ | √ |
| محدودیت سرعت بر اساس کاربر | √ | √ | √ |
| DNS سفارشی | √ | √ | √ |
## پشتیبانی از قسمت فرانت
| قسمت فرانت | v2ray | trojan | shadowsocks |
|--------------------------------------------------------|-------|--------|-------------------------|
| sspanel-uim | √ | √ | √ (تک پورت چند کاربره و V2ray-Plugin) |
| v2board | √ | √ | √ |
| [PMPanel](https://github.com/ByteInternetHK/PMPanel) | √ | √ | √ |
| [ProxyPanel](https://github.com/ProxyPanel/ProxyPanel) | √ | √ | √ |
| [WHMCS (V2RaySocks)](https://v2raysocks.doxtex.com/) | √ | √ | √ |
## نصب نرم افزار
### نصب بصورت یکپارچه
```
wget -N https://raw.githubusercontent.com/XrayR-project/XrayR-release/master/install.sh && bash install.sh
```
### استقرار نرم افزار با استفاده از Docker
[آموزش استقرار داکر](https://xrayr-project.github.io/XrayR-doc/xrayr-xia-zai-he-an-zhuang/install/docker)
### نصب دستی
[آموزش نصب دستی](https://xrayr-project.github.io/XrayR-doc/xrayr-xia-zai-he-an-zhuang/install/manual)
## فایل های پیکربندی و آموزش های با جرئیات
[آموزش مفصل](https://xrayr-project.github.io/XrayR-doc/)
## Thanks
* [Project X](https://github.com/XTLS/)
* [V2Fly](https://github.com/v2fly)
* [VNet-V2ray](https://github.com/ProxyPanel/VNet-V2ray)
* [Air-Universe](https://github.com/crossfw/Air-Universe)
## Licence
[Mozilla Public License Version 2.0](https://github.com/XrayR-project/XrayR/blob/master/LICENSE)
## Telgram
[بحث در مورد XrayR Backend](https://t.me/XrayR_project)
[کانال اعلان در مورد XrayR](https://t.me/XrayR_channel)
## Stargazers over time
[![Stargazers over time](https://starchart.cc/XrayR-project/XrayR.svg)](https://starchart.cc/XrayR-project/XrayR)

View File

@@ -3,16 +3,24 @@ package api
import (
"encoding/json"
"regexp"
"github.com/xtls/xray-core/infra/conf"
)
// API config
const (
UserNotModified = "users not modified"
NodeNotModified = "node not modified"
RuleNotModified = "rules not modified"
)
// Config API config
type Config struct {
APIHost string `mapstructure:"ApiHost"`
NodeID int `mapstructure:"NodeID"`
Key string `mapstructure:"ApiKey"`
NodeType string `mapstructure:"NodeType"`
EnableVless bool `mapstructure:"EnableVless"`
EnableXTLS bool `mapstructure:"EnableXTLS"`
VlessFlow string `mapstructure:"VlessFlow"`
Timeout int `mapstructure:"Timeout"`
SpeedLimit float64 `mapstructure:"SpeedLimit"`
DeviceLimit int `mapstructure:"DeviceLimit"`
@@ -20,46 +28,46 @@ type Config struct {
DisableCustomConfig bool `mapstructure:"DisableCustomConfig"`
}
// Node status
// NodeStatus Node status
type NodeStatus struct {
CPU float64
Mem float64
Disk float64
Uptime int
Uptime uint64
}
type NodeInfo struct {
NodeType string // Must be V2ray, Trojan, and Shadowsocks
NodeID int
Port int
Port uint32
SpeedLimit uint64 // Bps
AlterID int
AlterID uint16
TransportProtocol string
FakeType string
Host string
Path string
EnableTLS bool
TLSType string
EnableVless bool
VlessFlow string
CypherMethod string
ServerKey string
ServiceName string
Header json.RawMessage
NameServerConfig []*conf.NameServerConfig
EnableREALITY bool
REALITYConfig *REALITYConfig
}
type UserInfo struct {
UID int
Email string
Passwd string
Port int
Method string
SpeedLimit uint64 // Bps
DeviceLimit int
Protocol string
ProtocolParam string
Obfs string
ObfsParam string
UUID string
AlterID int
UID int
Email string
UUID string
Passwd string
Port uint32
AlterID uint16
Method string
SpeedLimit uint64 // Bps
DeviceLimit int
}
type OnlineUser struct {
@@ -90,3 +98,14 @@ type DetectResult struct {
UID int
RuleID int
}
type REALITYConfig struct {
Dest string
ProxyProtocolVer uint64
ServerNames []string
PrivateKey string
MinClientVer string
MaxClientVer string
MaxTimeDiff uint64
ShortIds []string
}

402
api/gov2panel/gov2panel.go Normal file
View File

@@ -0,0 +1,402 @@
package gov2panel
import (
"bufio"
"encoding/json"
"errors"
"fmt"
"log"
"os"
"regexp"
"strconv"
"strings"
"sync/atomic"
"time"
"github.com/bitly/go-simplejson"
"github.com/go-resty/resty/v2"
"github.com/gogf/gf/v2/util/gconv"
"github.com/xtls/xray-core/common/net"
"github.com/xtls/xray-core/infra/conf"
"github.com/XrayR-project/XrayR/api"
)
// APIClient create an api client to the panel.
type APIClient struct {
client *resty.Client
APIHost string
NodeID int
Key string
NodeType string
EnableVless bool
VlessFlow string
SpeedLimit float64
DeviceLimit int
LocalRuleList []api.DetectRule
resp atomic.Value
eTags map[string]string
}
// New create an api instance
func New(apiConfig *api.Config) *APIClient {
client := resty.New()
client.SetRetryCount(3)
if apiConfig.Timeout > 0 {
client.SetTimeout(time.Duration(apiConfig.Timeout) * time.Second)
} else {
client.SetTimeout(5 * time.Second)
}
client.OnError(func(req *resty.Request, err error) {
if v, ok := err.(*resty.ResponseError); ok {
// v.Response contains the last response from the server
// v.Err contains the original error
log.Print(v.Err)
}
})
client.SetBaseURL(apiConfig.APIHost)
// Create Key for each requests
client.SetQueryParams(map[string]string{
"node_id": strconv.Itoa(apiConfig.NodeID),
"node_type": strings.ToLower(apiConfig.NodeType),
"token": apiConfig.Key,
})
// Read local rule list
localRuleList := readLocalRuleList(apiConfig.RuleListPath)
apiClient := &APIClient{
client: client,
NodeID: apiConfig.NodeID,
Key: apiConfig.Key,
APIHost: apiConfig.APIHost,
NodeType: apiConfig.NodeType,
EnableVless: apiConfig.EnableVless,
VlessFlow: apiConfig.VlessFlow,
SpeedLimit: apiConfig.SpeedLimit,
DeviceLimit: apiConfig.DeviceLimit,
LocalRuleList: localRuleList,
eTags: make(map[string]string),
}
return apiClient
}
// readLocalRuleList reads the local rule list file
func readLocalRuleList(path string) (LocalRuleList []api.DetectRule) {
LocalRuleList = make([]api.DetectRule, 0)
if path != "" {
// open the file
file, err := os.Open(path)
defer file.Close()
// handle errors while opening
if err != nil {
log.Printf("Error when opening file: %s", err)
return LocalRuleList
}
fileScanner := bufio.NewScanner(file)
// read line by line
for fileScanner.Scan() {
LocalRuleList = append(LocalRuleList, api.DetectRule{
ID: -1,
Pattern: regexp.MustCompile(fileScanner.Text()),
})
}
// handle first encountered error while reading
if err := fileScanner.Err(); err != nil {
log.Fatalf("Error while reading file: %s", err)
return
}
}
return LocalRuleList
}
// Describe return a description of the client
func (c *APIClient) Describe() api.ClientInfo {
return api.ClientInfo{APIHost: c.APIHost, NodeID: c.NodeID, Key: c.Key, NodeType: c.NodeType}
}
// Debug set the client debug for client
func (c *APIClient) Debug() {
c.client.SetDebug(true)
}
func (c *APIClient) assembleURL(path string) string {
return c.APIHost + path
}
func (c *APIClient) parseResponse(res *resty.Response, path string, err error) (*simplejson.Json, error) {
if err != nil {
return nil, fmt.Errorf("request %s failed: %v", c.assembleURL(path), err)
}
if res.StatusCode() > 399 {
return nil, fmt.Errorf("request %s failed: %s, %v", c.assembleURL(path), res.String(), err)
}
rtn, err := simplejson.NewJson(res.Body())
if err != nil {
return nil, fmt.Errorf("ret %s invalid", res.String())
}
return rtn, nil
}
// GetNodeInfo will pull NodeInfo Config from panel
func (c *APIClient) GetNodeInfo() (nodeInfo *api.NodeInfo, err error) {
server := new(serverConfig)
path := "/api/server/config"
res, err := c.client.R().
SetHeader("If-None-Match", c.eTags["node"]).
ForceContentType("application/json").
Get(path)
// Etag identifier for a specific version of a resource. StatusCode = 304 means no changed
if res.StatusCode() == 304 {
return nil, errors.New(api.NodeNotModified)
}
// update etag
if res.Header().Get("Etag") != "" && res.Header().Get("Etag") != c.eTags["node"] {
c.eTags["node"] = res.Header().Get("Etag")
}
nodeInfoResp, err := c.parseResponse(res, path, err)
if err != nil {
return nil, err
}
b, _ := nodeInfoResp.Encode()
json.Unmarshal(b, server)
if gconv.Uint32(server.Port) == 0 {
return nil, errors.New("server port must > 0")
}
c.resp.Store(server)
switch c.NodeType {
case "V2ray":
nodeInfo, err = c.parseV2rayNodeResponse(server)
case "Trojan":
nodeInfo, err = c.parseTrojanNodeResponse(server)
case "Shadowsocks":
nodeInfo, err = c.parseSSNodeResponse(server)
default:
return nil, fmt.Errorf("unsupported node type: %s", c.NodeType)
}
if err != nil {
return nil, fmt.Errorf("parse node info failed: %s, \nError: %v", res.String(), err)
}
return nodeInfo, nil
}
// GetUserList will pull user form panel
func (c *APIClient) GetUserList() (UserList *[]api.UserInfo, err error) {
var users []*user
path := "/api/server/user"
switch c.NodeType {
case "V2ray", "Trojan", "Shadowsocks":
break
default:
return nil, fmt.Errorf("unsupported node type: %s", c.NodeType)
}
res, err := c.client.R().
SetHeader("If-None-Match", c.eTags["users"]).
ForceContentType("application/json").
Get(path)
// Etag identifier for a specific version of a resource. StatusCode = 304 means no changed
if res.StatusCode() == 304 {
return nil, errors.New(api.UserNotModified)
}
// update etag
if res.Header().Get("Etag") != "" && res.Header().Get("Etag") != c.eTags["users"] {
c.eTags["users"] = res.Header().Get("Etag")
}
usersResp, err := c.parseResponse(res, path, err)
if err != nil {
return nil, err
}
b, _ := usersResp.Get("users").Encode()
json.Unmarshal(b, &users)
if len(users) == 0 {
return nil, errors.New("users is null")
}
userList := make([]api.UserInfo, len(users))
for i := 0; i < len(users); i++ {
u := api.UserInfo{
UID: users[i].Id,
UUID: users[i].Uuid,
}
// Support 1.7.1 speed limit
if c.SpeedLimit > 0 {
u.SpeedLimit = uint64(c.SpeedLimit * 1000000 / 8)
} else {
u.SpeedLimit = uint64(users[i].SpeedLimit * 1000000 / 8)
}
u.DeviceLimit = c.DeviceLimit // todo waiting v2board send configuration
u.Email = u.UUID + "@gov2panel.user"
if c.NodeType == "Shadowsocks" {
u.Passwd = u.UUID
}
userList[i] = u
}
return &userList, nil
}
// ReportUserTraffic reports the user traffic
func (c *APIClient) ReportUserTraffic(userTraffic *[]api.UserTraffic) error {
path := "/api/server/push"
res, err := c.client.R().SetBody(userTraffic).ForceContentType("application/json").Post(path)
_, err = c.parseResponse(res, path, err)
if err != nil {
return err
}
return nil
}
// GetNodeRule implements the API interface
func (c *APIClient) GetNodeRule() (*[]api.DetectRule, error) {
routes := c.resp.Load().(*serverConfig).Routes
ruleList := c.LocalRuleList
for i := range routes {
if routes[i].Action == "block" {
ruleList = append(ruleList, api.DetectRule{
ID: i,
Pattern: regexp.MustCompile(strings.Join(routes[i].Match, "|")),
})
}
}
return &ruleList, nil
}
// ReportNodeStatus implements the API interface
func (c *APIClient) ReportNodeStatus(nodeStatus *api.NodeStatus) (err error) {
return nil
}
// ReportNodeOnlineUsers implements the API interface
func (c *APIClient) ReportNodeOnlineUsers(onlineUserList *[]api.OnlineUser) error {
return nil
}
// ReportIllegal implements the API interface
func (c *APIClient) ReportIllegal(detectResultList *[]api.DetectResult) error {
return nil
}
// parseTrojanNodeResponse parse the response for the given nodeInfo format
func (c *APIClient) parseTrojanNodeResponse(s *serverConfig) (*api.NodeInfo, error) {
// Create GeneralNodeInfo
nodeInfo := &api.NodeInfo{
NodeType: c.NodeType,
NodeID: c.NodeID,
Port: gconv.Uint32(s.Port),
TransportProtocol: "tcp",
EnableTLS: true,
Host: s.Host,
ServiceName: s.Sni,
NameServerConfig: s.parseDNSConfig(),
}
return nodeInfo, nil
}
// parseSSNodeResponse parse the response for the given nodeInfo format
func (c *APIClient) parseSSNodeResponse(s *serverConfig) (*api.NodeInfo, error) {
var header json.RawMessage
if s.Obfs == "http" {
path := "/"
if p := s.ObfsSettings.Path; p != "" {
if strings.HasPrefix(p, "/") {
path = p
} else {
path += p
}
}
h := simplejson.New()
h.Set("type", "http")
h.SetPath([]string{"request", "path"}, path)
header, _ = h.Encode()
}
// Create GeneralNodeInfo
return &api.NodeInfo{
NodeType: c.NodeType,
NodeID: c.NodeID,
Port: gconv.Uint32(s.Port),
TransportProtocol: "tcp",
CypherMethod: s.Encryption,
ServerKey: s.ServerKey, // shadowsocks2022 share key
NameServerConfig: s.parseDNSConfig(),
Header: header,
}, nil
}
// parseV2rayNodeResponse parse the response for the given nodeInfo format
func (c *APIClient) parseV2rayNodeResponse(s *serverConfig) (*api.NodeInfo, error) {
var (
header json.RawMessage
enableTLS bool
)
switch s.Net {
case "tcp":
if s.Header != nil {
if httpHeader, err := s.Header.MarshalJSON(); err != nil {
return nil, err
} else {
header = httpHeader
}
}
}
if s.TLS == "tls" {
enableTLS = true
}
// Create GeneralNodeInfo
return &api.NodeInfo{
NodeType: c.NodeType,
NodeID: c.NodeID,
Port: gconv.Uint32(s.Port),
AlterID: 0,
TransportProtocol: s.Net,
EnableTLS: enableTLS,
Path: s.Path,
Host: s.Host,
EnableVless: c.EnableVless,
VlessFlow: c.VlessFlow,
ServiceName: s.Sni,
Header: header,
NameServerConfig: s.parseDNSConfig(),
}, nil
}
func (s *serverConfig) parseDNSConfig() (nameServerList []*conf.NameServerConfig) {
for i := range s.Routes {
if s.Routes[i].Action == "dns" {
nameServerList = append(nameServerList, &conf.NameServerConfig{
Address: &conf.Address{Address: net.ParseAddress(s.Routes[i].ActionValue)},
Domains: s.Routes[i].Match,
})
}
}
return
}

View File

@@ -0,0 +1,101 @@
package gov2panel_test
import (
"testing"
"github.com/XrayR-project/XrayR/api"
"github.com/XrayR-project/XrayR/api/gov2panel"
)
func CreateClient() api.API {
apiConfig := &api.Config{
APIHost: "http://localhost:8080",
Key: "123456",
NodeID: 1,
NodeType: "V2ray",
}
client := gov2panel.New(apiConfig)
return client
}
func TestGetV2rayNodeInfo(t *testing.T) {
client := CreateClient()
nodeInfo, err := client.GetNodeInfo()
if err != nil {
t.Error(err)
}
t.Log(nodeInfo)
}
func TestGetSSNodeInfo(t *testing.T) {
apiConfig := &api.Config{
APIHost: "http://127.0.0.1:668",
Key: "qwertyuiopasdfghjkl",
NodeID: 1,
NodeType: "Shadowsocks",
}
client := gov2panel.New(apiConfig)
nodeInfo, err := client.GetNodeInfo()
if err != nil {
t.Error(err)
}
t.Log(nodeInfo)
}
func TestGetTrojanNodeInfo(t *testing.T) {
apiConfig := &api.Config{
APIHost: "http://127.0.0.1:668",
Key: "qwertyuiopasdfghjkl",
NodeID: 1,
NodeType: "Trojan",
}
client := gov2panel.New(apiConfig)
nodeInfo, err := client.GetNodeInfo()
if err != nil {
t.Error(err)
}
t.Log(nodeInfo)
}
func TestGetUserList(t *testing.T) {
client := CreateClient()
userList, err := client.GetUserList()
if err != nil {
t.Error(err)
}
t.Log(userList)
}
func TestReportReportUserTraffic(t *testing.T) {
client := CreateClient()
userList, err := client.GetUserList()
if err != nil {
t.Error(err)
}
generalUserTraffic := make([]api.UserTraffic, len(*userList))
for i, userInfo := range *userList {
generalUserTraffic[i] = api.UserTraffic{
UID: userInfo.UID,
Upload: 1111,
Download: 2222,
}
}
// client.Debug()
err = client.ReportUserTraffic(&generalUserTraffic)
if err != nil {
t.Error(err)
}
}
func TestGetNodeRule(t *testing.T) {
client := CreateClient()
client.Debug()
ruleList, err := client.GetNodeRule()
if err != nil {
t.Error(err)
}
t.Log(ruleList)
}

46
api/gov2panel/model.go Normal file
View File

@@ -0,0 +1,46 @@
package gov2panel
import "encoding/json"
type serverConfig struct {
v2ray
shadowsocks
//---
Routes []route `json:"routes"`
Header *json.RawMessage `json:"header"`
}
type v2ray struct {
Port string `json:"port"`
Scy string `json:"scy"`
Net string `json:"net"`
Type string `json:"type"`
Host string `json:"host"`
Path string `json:"path"`
TLS string `json:"tls"`
Sni string `json:"sni"`
Alpn string `json:"alpn"`
}
type shadowsocks struct {
Encryption string `json:"encryption"`
Obfs string `json:"obfs"`
ObfsSettings struct {
Path string `json:"path"`
Host string `json:"host"`
} `json:"obfs_settings"`
ServerKey string `json:"server_key"`
}
type route struct {
Id int `json:"id"`
Match []string `json:"match"`
Action string `json:"action"`
ActionValue string `json:"action_value"`
}
type user struct {
Id int `json:"id"`
Uuid string `json:"uuid"`
SpeedLimit int `json:"speed_limit"`
}

57
api/newV2board/model.go Normal file
View File

@@ -0,0 +1,57 @@
package newV2board
import (
"encoding/json"
)
type serverConfig struct {
shadowsocks
v2ray
trojan
ServerPort int `json:"server_port"`
BaseConfig struct {
PushInterval int `json:"push_interval"`
PullInterval int `json:"pull_interval"`
} `json:"base_config"`
Routes []route `json:"routes"`
}
type shadowsocks struct {
Cipher string `json:"cipher"`
Obfs string `json:"obfs"`
ObfsSettings struct {
Path string `json:"path"`
Host string `json:"host"`
} `json:"obfs_settings"`
ServerKey string `json:"server_key"`
}
type v2ray struct {
Network string `json:"network"`
NetworkSettings struct {
Path string `json:"path"`
Headers *json.RawMessage `json:"headers"`
ServiceName string `json:"serviceName"`
Header *json.RawMessage `json:"header"`
} `json:"networkSettings"`
Tls int `json:"tls"`
}
type trojan struct {
Host string `json:"host"`
ServerName string `json:"server_name"`
}
type route struct {
Id int `json:"id"`
Match []string `json:"match"`
Action string `json:"action"`
ActionValue string `json:"action_value"`
}
type user struct {
Id int `json:"id"`
Uuid string `json:"uuid"`
SpeedLimit int `json:"speed_limit"`
}

416
api/newV2board/v2board.go Normal file
View File

@@ -0,0 +1,416 @@
package newV2board
import (
"bufio"
"encoding/json"
"errors"
"fmt"
"log"
"os"
"regexp"
"strconv"
"strings"
"sync/atomic"
"time"
"github.com/bitly/go-simplejson"
"github.com/go-resty/resty/v2"
"github.com/xtls/xray-core/common/net"
"github.com/xtls/xray-core/infra/conf"
"github.com/XrayR-project/XrayR/api"
)
// APIClient create an api client to the panel.
type APIClient struct {
client *resty.Client
APIHost string
NodeID int
Key string
NodeType string
EnableVless bool
VlessFlow string
SpeedLimit float64
DeviceLimit int
LocalRuleList []api.DetectRule
resp atomic.Value
eTags map[string]string
}
// New create an api instance
func New(apiConfig *api.Config) *APIClient {
client := resty.New()
client.SetRetryCount(3)
if apiConfig.Timeout > 0 {
client.SetTimeout(time.Duration(apiConfig.Timeout) * time.Second)
} else {
client.SetTimeout(5 * time.Second)
}
client.OnError(func(req *resty.Request, err error) {
if v, ok := err.(*resty.ResponseError); ok {
// v.Response contains the last response from the server
// v.Err contains the original error
log.Print(v.Err)
}
})
client.SetBaseURL(apiConfig.APIHost)
// Create Key for each requests
client.SetQueryParams(map[string]string{
"node_id": strconv.Itoa(apiConfig.NodeID),
"node_type": strings.ToLower(apiConfig.NodeType),
"token": apiConfig.Key,
})
// Read local rule list
localRuleList := readLocalRuleList(apiConfig.RuleListPath)
apiClient := &APIClient{
client: client,
NodeID: apiConfig.NodeID,
Key: apiConfig.Key,
APIHost: apiConfig.APIHost,
NodeType: apiConfig.NodeType,
EnableVless: apiConfig.EnableVless,
VlessFlow: apiConfig.VlessFlow,
SpeedLimit: apiConfig.SpeedLimit,
DeviceLimit: apiConfig.DeviceLimit,
LocalRuleList: localRuleList,
eTags: make(map[string]string),
}
return apiClient
}
// readLocalRuleList reads the local rule list file
func readLocalRuleList(path string) (LocalRuleList []api.DetectRule) {
LocalRuleList = make([]api.DetectRule, 0)
if path != "" {
// open the file
file, err := os.Open(path)
defer file.Close()
// handle errors while opening
if err != nil {
log.Printf("Error when opening file: %s", err)
return LocalRuleList
}
fileScanner := bufio.NewScanner(file)
// read line by line
for fileScanner.Scan() {
LocalRuleList = append(LocalRuleList, api.DetectRule{
ID: -1,
Pattern: regexp.MustCompile(fileScanner.Text()),
})
}
// handle first encountered error while reading
if err := fileScanner.Err(); err != nil {
log.Fatalf("Error while reading file: %s", err)
return
}
}
return LocalRuleList
}
// Describe return a description of the client
func (c *APIClient) Describe() api.ClientInfo {
return api.ClientInfo{APIHost: c.APIHost, NodeID: c.NodeID, Key: c.Key, NodeType: c.NodeType}
}
// Debug set the client debug for client
func (c *APIClient) Debug() {
c.client.SetDebug(true)
}
func (c *APIClient) assembleURL(path string) string {
return c.APIHost + path
}
func (c *APIClient) parseResponse(res *resty.Response, path string, err error) (*simplejson.Json, error) {
if err != nil {
return nil, fmt.Errorf("request %s failed: %v", c.assembleURL(path), err)
}
if res.StatusCode() > 399 {
return nil, fmt.Errorf("request %s failed: %s, %v", c.assembleURL(path), res.String(), err)
}
rtn, err := simplejson.NewJson(res.Body())
if err != nil {
return nil, fmt.Errorf("ret %s invalid", res.String())
}
return rtn, nil
}
// GetNodeInfo will pull NodeInfo Config from panel
func (c *APIClient) GetNodeInfo() (nodeInfo *api.NodeInfo, err error) {
server := new(serverConfig)
path := "/api/v1/server/UniProxy/config"
res, err := c.client.R().
SetHeader("If-None-Match", c.eTags["node"]).
ForceContentType("application/json").
Get(path)
// Etag identifier for a specific version of a resource. StatusCode = 304 means no changed
if res.StatusCode() == 304 {
return nil, errors.New(api.NodeNotModified)
}
// update etag
if res.Header().Get("Etag") != "" && res.Header().Get("Etag") != c.eTags["node"] {
c.eTags["node"] = res.Header().Get("Etag")
}
nodeInfoResp, err := c.parseResponse(res, path, err)
if err != nil {
return nil, err
}
b, _ := nodeInfoResp.Encode()
json.Unmarshal(b, server)
if server.ServerPort == 0 {
return nil, errors.New("server port must > 0")
}
c.resp.Store(server)
switch c.NodeType {
case "V2ray":
nodeInfo, err = c.parseV2rayNodeResponse(server)
case "Trojan":
nodeInfo, err = c.parseTrojanNodeResponse(server)
case "Shadowsocks":
nodeInfo, err = c.parseSSNodeResponse(server)
default:
return nil, fmt.Errorf("unsupported node type: %s", c.NodeType)
}
if err != nil {
return nil, fmt.Errorf("parse node info failed: %s, \nError: %v", res.String(), err)
}
return nodeInfo, nil
}
// GetUserList will pull user form panel
func (c *APIClient) GetUserList() (UserList *[]api.UserInfo, err error) {
var users []*user
path := "/api/v1/server/UniProxy/user"
switch c.NodeType {
case "V2ray", "Trojan", "Shadowsocks":
break
default:
return nil, fmt.Errorf("unsupported node type: %s", c.NodeType)
}
res, err := c.client.R().
SetHeader("If-None-Match", c.eTags["users"]).
ForceContentType("application/json").
Get(path)
// Etag identifier for a specific version of a resource. StatusCode = 304 means no changed
if res.StatusCode() == 304 {
return nil, errors.New(api.UserNotModified)
}
// update etag
if res.Header().Get("Etag") != "" && res.Header().Get("Etag") != c.eTags["users"] {
c.eTags["users"] = res.Header().Get("Etag")
}
usersResp, err := c.parseResponse(res, path, err)
if err != nil {
return nil, err
}
b, _ := usersResp.Get("users").Encode()
json.Unmarshal(b, &users)
if len(users) == 0 {
return nil, errors.New("users is null")
}
userList := make([]api.UserInfo, len(users))
for i := 0; i < len(users); i++ {
u := api.UserInfo{
UID: users[i].Id,
UUID: users[i].Uuid,
}
// Support 1.7.1 speed limit
if c.SpeedLimit > 0 {
u.SpeedLimit = uint64(c.SpeedLimit * 1000000 / 8)
} else {
u.SpeedLimit = uint64(users[i].SpeedLimit * 1000000 / 8)
}
u.DeviceLimit = c.DeviceLimit // todo waiting v2board send configuration
u.Email = u.UUID + "@v2board.user"
if c.NodeType == "Shadowsocks" {
u.Passwd = u.UUID
}
userList[i] = u
}
return &userList, nil
}
// ReportUserTraffic reports the user traffic
func (c *APIClient) ReportUserTraffic(userTraffic *[]api.UserTraffic) error {
path := "/api/v1/server/UniProxy/push"
// json structure: {uid1: [u, d], uid2: [u, d], uid1: [u, d], uid3: [u, d]}
data := make(map[int][]int64, len(*userTraffic))
for _, traffic := range *userTraffic {
data[traffic.UID] = []int64{traffic.Upload, traffic.Download}
}
res, err := c.client.R().SetBody(data).ForceContentType("application/json").Post(path)
_, err = c.parseResponse(res, path, err)
if err != nil {
return err
}
return nil
}
// GetNodeRule implements the API interface
func (c *APIClient) GetNodeRule() (*[]api.DetectRule, error) {
routes := c.resp.Load().(*serverConfig).Routes
ruleList := c.LocalRuleList
for i := range routes {
if routes[i].Action == "block" {
ruleList = append(ruleList, api.DetectRule{
ID: i,
Pattern: regexp.MustCompile(strings.Join(routes[i].Match, "|")),
})
}
}
return &ruleList, nil
}
// ReportNodeStatus implements the API interface
func (c *APIClient) ReportNodeStatus(nodeStatus *api.NodeStatus) (err error) {
return nil
}
// ReportNodeOnlineUsers implements the API interface
func (c *APIClient) ReportNodeOnlineUsers(onlineUserList *[]api.OnlineUser) error {
return nil
}
// ReportIllegal implements the API interface
func (c *APIClient) ReportIllegal(detectResultList *[]api.DetectResult) error {
return nil
}
// parseTrojanNodeResponse parse the response for the given nodeInfo format
func (c *APIClient) parseTrojanNodeResponse(s *serverConfig) (*api.NodeInfo, error) {
// Create GeneralNodeInfo
nodeInfo := &api.NodeInfo{
NodeType: c.NodeType,
NodeID: c.NodeID,
Port: uint32(s.ServerPort),
TransportProtocol: "tcp",
EnableTLS: true,
Host: s.Host,
ServiceName: s.ServerName,
NameServerConfig: s.parseDNSConfig(),
}
return nodeInfo, nil
}
// parseSSNodeResponse parse the response for the given nodeInfo format
func (c *APIClient) parseSSNodeResponse(s *serverConfig) (*api.NodeInfo, error) {
var header json.RawMessage
if s.Obfs == "http" {
path := "/"
if p := s.ObfsSettings.Path; p != "" {
if strings.HasPrefix(p, "/") {
path = p
} else {
path += p
}
}
h := simplejson.New()
h.Set("type", "http")
h.SetPath([]string{"request", "path"}, path)
header, _ = h.Encode()
}
// Create GeneralNodeInfo
return &api.NodeInfo{
NodeType: c.NodeType,
NodeID: c.NodeID,
Port: uint32(s.ServerPort),
TransportProtocol: "tcp",
CypherMethod: s.Cipher,
ServerKey: s.ServerKey, // shadowsocks2022 share key
NameServerConfig: s.parseDNSConfig(),
Header: header,
}, nil
}
// parseV2rayNodeResponse parse the response for the given nodeInfo format
func (c *APIClient) parseV2rayNodeResponse(s *serverConfig) (*api.NodeInfo, error) {
var (
host string
header json.RawMessage
enableTLS bool
)
switch s.Network {
case "ws":
if s.NetworkSettings.Headers != nil {
if httpHeader, err := s.NetworkSettings.Headers.MarshalJSON(); err != nil {
return nil, err
} else {
b, _ := simplejson.NewJson(httpHeader)
host = b.Get("Host").MustString()
}
}
case "tcp":
if s.NetworkSettings.Header != nil {
if httpHeader, err := s.NetworkSettings.Header.MarshalJSON(); err != nil {
return nil, err
} else {
header = httpHeader
}
}
}
if s.Tls == 1 {
enableTLS = true
}
// Create GeneralNodeInfo
return &api.NodeInfo{
NodeType: c.NodeType,
NodeID: c.NodeID,
Port: uint32(s.ServerPort),
AlterID: 0,
TransportProtocol: s.Network,
EnableTLS: enableTLS,
Path: s.NetworkSettings.Path,
Host: host,
EnableVless: c.EnableVless,
VlessFlow: c.VlessFlow,
ServiceName: s.NetworkSettings.ServiceName,
Header: header,
NameServerConfig: s.parseDNSConfig(),
}, nil
}
func (s *serverConfig) parseDNSConfig() (nameServerList []*conf.NameServerConfig) {
for i := range s.Routes {
if s.Routes[i].Action == "dns" {
nameServerList = append(nameServerList, &conf.NameServerConfig{
Address: &conf.Address{Address: net.ParseAddress(s.Routes[i].ActionValue)},
Domains: s.Routes[i].Match,
})
}
}
return
}

View File

@@ -1,10 +1,10 @@
package v2board_test
package newV2board_test
import (
"testing"
"github.com/XrayR-project/XrayR/api"
"github.com/XrayR-project/XrayR/api/v2board"
"github.com/XrayR-project/XrayR/api/newV2board"
)
func CreateClient() api.API {
@@ -14,11 +14,11 @@ func CreateClient() api.API {
NodeID: 1,
NodeType: "V2ray",
}
client := v2board.New(apiConfig)
client := newV2board.New(apiConfig)
return client
}
func TestGetV2rayNodeinfo(t *testing.T) {
func TestGetV2rayNodeInfo(t *testing.T) {
client := CreateClient()
nodeInfo, err := client.GetNodeInfo()
if err != nil {
@@ -27,14 +27,14 @@ func TestGetV2rayNodeinfo(t *testing.T) {
t.Log(nodeInfo)
}
func TestGetSSNodeinfo(t *testing.T) {
func TestGetSSNodeInfo(t *testing.T) {
apiConfig := &api.Config{
APIHost: "http://127.0.0.1:668",
Key: "qwertyuiopasdfghjkl",
NodeID: 1,
NodeType: "Shadowsocks",
}
client := v2board.New(apiConfig)
client := newV2board.New(apiConfig)
nodeInfo, err := client.GetNodeInfo()
if err != nil {
t.Error(err)
@@ -42,14 +42,14 @@ func TestGetSSNodeinfo(t *testing.T) {
t.Log(nodeInfo)
}
func TestGetTrojanNodeinfo(t *testing.T) {
func TestGetTrojanNodeInfo(t *testing.T) {
apiConfig := &api.Config{
APIHost: "http://127.0.0.1:668",
Key: "qwertyuiopasdfghjkl",
NodeID: 1,
NodeType: "Trojan",
}
client := v2board.New(apiConfig)
client := newV2board.New(apiConfig)
nodeInfo, err := client.GetNodeInfo()
if err != nil {
t.Error(err)
@@ -82,7 +82,7 @@ func TestReportReportUserTraffic(t *testing.T) {
Download: 114514,
}
}
//client.Debug()
// client.Debug()
err = client.ReportUserTraffic(&generalUserTraffic)
if err != nil {
t.Error(err)

View File

@@ -9,14 +9,14 @@ type NodeInfoResponse struct {
Method string `json:"method"`
TrafficRate float64 `json:"trafficRate"`
RawServerString string `json:"outServer"`
Port int `json:"outPort"`
AlterId int `json:"alterId"`
Port uint32 `json:"outPort"`
AlterId uint16 `json:"alterId"`
Network string `json:"network"`
Security string `json:"security"`
Host string `json:"host"`
Path string `json:"path"`
Grpc bool `json:"grpc"`
Sni string `json:sni`
Sni string `json:"sni"`
}
// UserResponse is the response of user

View File

@@ -11,8 +11,9 @@ import (
"strconv"
"time"
"github.com/XrayR-project/XrayR/api"
"github.com/go-resty/resty/v2"
"github.com/XrayR-project/XrayR/api"
)
// APIClient create a api client to the panel.
@@ -23,7 +24,7 @@ type APIClient struct {
Key string
NodeType string
EnableVless bool
EnableXTLS bool
VlessFlow string
SpeedLimit float64
DeviceLimit int
LocalRuleList []api.DetectRule
@@ -60,7 +61,7 @@ func New(apiConfig *api.Config) *APIClient {
APIHost: apiConfig.APIHost,
NodeType: apiConfig.NodeType,
EnableVless: apiConfig.EnableVless,
EnableXTLS: apiConfig.EnableXTLS,
VlessFlow: apiConfig.VlessFlow,
SpeedLimit: apiConfig.SpeedLimit,
DeviceLimit: apiConfig.DeviceLimit,
LocalRuleList: localRuleList,
@@ -76,7 +77,7 @@ func readLocalRuleList(path string) (LocalRuleList []api.DetectRule) {
// open the file
file, err := os.Open(path)
//handle errors while opening
// handle errors while opening
if err != nil {
log.Printf("Error when opening file: %s", err)
return LocalRuleList
@@ -94,7 +95,7 @@ func readLocalRuleList(path string) (LocalRuleList []api.DetectRule) {
// handle first encountered error while reading
if err := fileScanner.Err(); err != nil {
log.Fatalf("Error while reading file: %s", err)
return make([]api.DetectRule, 0)
return
}
file.Close()
@@ -130,7 +131,7 @@ func (c *APIClient) parseResponse(res *resty.Response, path string, err error) (
if response.Ret != 200 {
res, _ := json.Marshal(&response)
return nil, fmt.Errorf("Ret %s invalid", string(res))
return nil, fmt.Errorf("ret %s invalid", string(res))
}
return response, nil
}
@@ -167,7 +168,7 @@ func (c *APIClient) GetNodeInfo() (nodeInfo *api.NodeInfo, err error) {
nodeInfoResponse := new(NodeInfoResponse)
if err := json.Unmarshal(response.Data, nodeInfoResponse); err != nil {
return nil, fmt.Errorf("Unmarshal %s failed: %s", reflect.TypeOf(nodeInfoResponse), err)
return nil, fmt.Errorf("unmarshal %s failed: %s", reflect.TypeOf(nodeInfoResponse), err)
}
switch c.NodeType {
case "V2ray":
@@ -177,7 +178,7 @@ func (c *APIClient) GetNodeInfo() (nodeInfo *api.NodeInfo, err error) {
case "Shadowsocks":
nodeInfo, err = c.ParseSSNodeResponse(nodeInfoResponse)
default:
return nil, fmt.Errorf("Unsupported Node type: %s", c.NodeType)
return nil, fmt.Errorf("unsupported Node type: %s", c.NodeType)
}
if err != nil {
@@ -219,12 +220,12 @@ func (c *APIClient) GetUserList() (UserList *[]api.UserInfo, err error) {
var userListResponse *[]UserResponse
if err := json.Unmarshal(response.Data, &userListResponse); err != nil {
return nil, fmt.Errorf("Unmarshal %s failed: %s", reflect.TypeOf(userListResponse), err)
return nil, fmt.Errorf("unmarshal %s failed: %s", reflect.TypeOf(userListResponse), err)
}
userList, err := c.ParseUserListResponse(userListResponse)
if err != nil {
res, _ := json.Marshal(userListResponse)
return nil, fmt.Errorf("Parse user list failed: %s", string(res))
return nil, fmt.Errorf("parse user list failed: %s", string(res))
}
return userList, nil
}
@@ -234,7 +235,7 @@ func (c *APIClient) ReportNodeStatus(nodeStatus *api.NodeStatus) (err error) {
return nil
}
//ReportNodeOnlineUsers reports online user ip
// ReportNodeOnlineUsers reports online user ip
func (c *APIClient) ReportNodeOnlineUsers(onlineUserList *[]api.OnlineUser) error {
var nodeType = ""
switch c.NodeType {
@@ -338,7 +339,7 @@ func (c *APIClient) GetNodeRule() (*[]api.DetectRule, error) {
ruleListResponse := new([]RuleItem)
if err := json.Unmarshal(response.Data, ruleListResponse); err != nil {
return nil, fmt.Errorf("Unmarshal %s failed: %s", reflect.TypeOf(ruleListResponse), err)
return nil, fmt.Errorf("unmarshal %s failed: %s", reflect.TypeOf(ruleListResponse), err)
}
for _, r := range *ruleListResponse {
@@ -358,8 +359,8 @@ func (c *APIClient) ReportIllegal(detectResultList *[]api.DetectResult) error {
// ParseV2rayNodeResponse parse the response for the given nodeinfor format
func (c *APIClient) ParseV2rayNodeResponse(nodeInfoResponse *NodeInfoResponse) (*api.NodeInfo, error) {
var enableTLS bool
var path, host, TLStype, transportProtocol, serviceName string
var speedlimit uint64 = 0
var path, host, transportProtocol, serviceName string
var speedLimit uint64 = 0
port := nodeInfoResponse.Port
alterID := nodeInfoResponse.AlterId
@@ -375,34 +376,29 @@ func (c *APIClient) ParseV2rayNodeResponse(nodeInfoResponse *NodeInfoResponse) (
}
// Compatible with more node types config
switch nodeInfoResponse.Security {
case "tls", "xtls":
if c.EnableXTLS {
TLStype = "xtls"
} else {
TLStype = "tls"
}
case "tls":
enableTLS = true
default:
enableTLS = false
}
if c.SpeedLimit > 0 {
speedlimit = uint64((c.SpeedLimit * 1000000) / 8)
speedLimit = uint64((c.SpeedLimit * 1000000) / 8)
} else {
speedlimit = uint64((nodeInfoResponse.SpeedLimit * 1000000) / 8)
speedLimit = uint64((nodeInfoResponse.SpeedLimit * 1000000) / 8)
}
// Create GeneralNodeInfo
nodeinfo := &api.NodeInfo{
NodeType: c.NodeType,
NodeID: c.NodeID,
Port: port,
SpeedLimit: speedlimit,
SpeedLimit: speedLimit,
AlterID: alterID,
TransportProtocol: transportProtocol,
EnableTLS: enableTLS,
TLSType: TLStype,
Path: path,
Host: host,
EnableVless: c.EnableVless,
VlessFlow: c.VlessFlow,
ServiceName: serviceName,
}
@@ -411,41 +407,33 @@ func (c *APIClient) ParseV2rayNodeResponse(nodeInfoResponse *NodeInfoResponse) (
// ParseSSNodeResponse parse the response for the given nodeinfor format
func (c *APIClient) ParseSSNodeResponse(nodeInfoResponse *NodeInfoResponse) (*api.NodeInfo, error) {
var port int = 0
var speedlimit uint64 = 0
port = nodeInfoResponse.Port
var speedLimit uint64 = 0
if c.SpeedLimit > 0 {
speedlimit = uint64((c.SpeedLimit * 1000000) / 8)
speedLimit = uint64((c.SpeedLimit * 1000000) / 8)
} else {
speedlimit = uint64((nodeInfoResponse.SpeedLimit * 1000000) / 8)
speedLimit = uint64((nodeInfoResponse.SpeedLimit * 1000000) / 8)
}
// Create GeneralNodeInfo
nodeinfo := &api.NodeInfo{
nodeInfo := &api.NodeInfo{
NodeType: c.NodeType,
NodeID: c.NodeID,
Port: port,
SpeedLimit: speedlimit,
Port: nodeInfoResponse.Port,
SpeedLimit: speedLimit,
TransportProtocol: "tcp",
CypherMethod: nodeInfoResponse.Method,
}
return nodeinfo, nil
return nodeInfo, nil
}
// ParseTrojanNodeResponse parse the response for the given nodeinfor format
func (c *APIClient) ParseTrojanNodeResponse(nodeInfoResponse *NodeInfoResponse) (*api.NodeInfo, error) {
// 域名或IP;port=连接端口#偏移端口|host=xx
// gz.aaa.com;port=443#12345|host=hk.aaa.com
var TLSType, host string
var host string
var transportProtocol = "tcp"
var speedlimit uint64 = 0
if c.EnableXTLS {
TLSType = "xtls"
} else {
TLSType = "tls"
}
host = nodeInfoResponse.Host
port := nodeInfoResponse.Port
@@ -458,25 +446,24 @@ func (c *APIClient) ParseTrojanNodeResponse(nodeInfoResponse *NodeInfoResponse)
transportProtocol = "grpc"
}
// Create GeneralNodeInfo
nodeinfo := &api.NodeInfo{
nodeInfo := &api.NodeInfo{
NodeType: c.NodeType,
NodeID: c.NodeID,
Port: port,
SpeedLimit: speedlimit,
TransportProtocol: transportProtocol,
EnableTLS: true,
TLSType: TLSType,
Host: host,
ServiceName: nodeInfoResponse.Sni,
}
return nodeinfo, nil
return nodeInfo, nil
}
// ParseUserListResponse parse the response for the given nodeinfo format
func (c *APIClient) ParseUserListResponse(userInfoResponse *[]UserResponse) (*[]api.UserInfo, error) {
var deviceLimit int = 0
var speedlimit uint64 = 0
var deviceLimit = 0
var speedLimit uint64 = 0
userList := make([]api.UserInfo, len(*userInfoResponse))
for i, user := range *userInfoResponse {
if c.DeviceLimit > 0 {
@@ -485,15 +472,15 @@ func (c *APIClient) ParseUserListResponse(userInfoResponse *[]UserResponse) (*[]
deviceLimit = user.DeviceLimit
}
if c.SpeedLimit > 0 {
speedlimit = uint64((c.SpeedLimit * 1000000) / 8)
speedLimit = uint64((c.SpeedLimit * 1000000) / 8)
} else {
speedlimit = uint64((user.SpeedLimit * 1000000) / 8)
speedLimit = uint64((user.SpeedLimit * 1000000) / 8)
}
userList[i] = api.UserInfo{
UID: user.ID,
Passwd: user.Passwd,
UUID: user.Passwd,
SpeedLimit: speedlimit,
SpeedLimit: speedLimit,
DeviceLimit: deviceLimit,
}
}

View File

@@ -85,7 +85,7 @@ func TestGetUserList(t *testing.T) {
func TestReportNodeStatus(t *testing.T) {
client := CreateClient()
nodeStatus := &api.NodeStatus{
1, 1, 1, 256,
CPU: 1, Mem: 1, Disk: 1, Uptime: 256,
}
err := client.ReportNodeStatus(nodeStatus)
if err != nil {
@@ -107,7 +107,7 @@ func TestReportReportNodeOnlineUsers(t *testing.T) {
IP: fmt.Sprintf("1.1.1.%d", i),
}
}
//client.Debug()
// client.Debug()
err = client.ReportNodeOnlineUsers(&onlineUserList)
if err != nil {
t.Error(err)
@@ -128,7 +128,7 @@ func TestReportReportUserTraffic(t *testing.T) {
Download: 114514,
}
}
//client.Debug()
// client.Debug()
err = client.ReportUserTraffic(&generalUserTraffic)
if err != nil {
t.Error(err)
@@ -150,8 +150,8 @@ func TestReportIllegal(t *testing.T) {
client := CreateClient()
detectResult := []api.DetectResult{
api.DetectResult{1, 2},
api.DetectResult{1, 3},
{1, 2},
{1, 3},
}
client.Debug()
err := client.ReportIllegal(&detectResult)

View File

@@ -19,8 +19,8 @@ type V2rayNodeInfo struct {
Key string `json:"key"`
Cert string `json:"pem"`
V2License string `json:"v2_license"`
V2AlterID int `json:"v2_alter_id"`
V2Port int `json:"v2_port"`
V2AlterID uint16 `json:"v2_alter_id"`
V2Port uint32 `json:"v2_port"`
V2Method string `json:"v2_method"`
V2Net string `json:"v2_net"`
V2Type string `json:"v2_type"`
@@ -37,7 +37,7 @@ type ShadowsocksNodeInfo struct {
SpeedLimit uint64 `json:"speed_limit"`
ClientLimit int `json:"client_limit"`
Method string `json:"method"`
Port int `json:"port"`
Port uint32 `json:"port"`
}
type TrojanNodeInfo struct {
@@ -46,10 +46,10 @@ type TrojanNodeInfo struct {
SpeedLimit uint64 `json:"speed_limit"`
ClientLimit int `json:"client_limit"`
PushPort int `json:"push_port"`
TrojanPort int `json:"trojan_port"`
TrojanPort uint32 `json:"trojan_port"`
}
// Node status report
// NodeStatus Node status report
type NodeStatus struct {
CPU string `json:"cpu"`
Mem string `json:"mem"`
@@ -98,7 +98,6 @@ type NodeRuleItem struct {
Pattern string `json:"pattern"`
}
// IllegalReport
type IllegalReport struct {
UID int `json:"uid"`
RuleID int `json:"rule_id"`

View File

@@ -11,8 +11,9 @@ import (
"strconv"
"time"
"github.com/XrayR-project/XrayR/api"
"github.com/go-resty/resty/v2"
"github.com/XrayR-project/XrayR/api"
)
// APIClient create a api client to the panel.
@@ -23,7 +24,7 @@ type APIClient struct {
Key string
NodeType string
EnableVless bool
EnableXTLS bool
VlessFlow string
SpeedLimit float64
DeviceLimit int
LocalRuleList []api.DetectRule
@@ -56,7 +57,7 @@ func New(apiConfig *api.Config) *APIClient {
APIHost: apiConfig.APIHost,
NodeType: apiConfig.NodeType,
EnableVless: apiConfig.EnableVless,
EnableXTLS: apiConfig.EnableXTLS,
VlessFlow: apiConfig.VlessFlow,
SpeedLimit: apiConfig.SpeedLimit,
DeviceLimit: apiConfig.DeviceLimit,
LocalRuleList: localRuleList,
@@ -72,7 +73,7 @@ func readLocalRuleList(path string) (LocalRuleList []api.DetectRule) {
// open the file
file, err := os.Open(path)
//handle errors while opening
// handle errors while opening
if err != nil {
log.Printf("Error when opening file: %s", err)
return LocalRuleList
@@ -90,7 +91,7 @@ func readLocalRuleList(path string) (LocalRuleList []api.DetectRule) {
// handle first encountered error while reading
if err := fileScanner.Err(); err != nil {
log.Fatalf("Error while reading file: %s", err)
return make([]api.DetectRule, 0)
return
}
file.Close()
@@ -134,7 +135,7 @@ func (c *APIClient) parseResponse(res *resty.Response, path string, err error) (
if response.Status != "success" {
res, _ := json.Marshal(&response)
return nil, fmt.Errorf("Ret %s invalid", string(res))
return nil, fmt.Errorf("ret %s invalid", string(res))
}
return response, nil
}
@@ -150,7 +151,7 @@ func (c *APIClient) GetNodeInfo() (nodeInfo *api.NodeInfo, err error) {
case "Shadowsocks":
path = fmt.Sprintf("/api/ss/v1/node/%d", c.NodeID)
default:
return nil, fmt.Errorf("Unsupported Node type: %s", c.NodeType)
return nil, fmt.Errorf("unsupported Node type: %s", c.NodeType)
}
res, err := c.createCommonRequest().
@@ -171,7 +172,7 @@ func (c *APIClient) GetNodeInfo() (nodeInfo *api.NodeInfo, err error) {
case "Shadowsocks":
nodeInfo, err = c.ParseSSNodeResponse(&response.Data)
default:
return nil, fmt.Errorf("Unsupported Node type: %s", c.NodeType)
return nil, fmt.Errorf("unsupported Node type: %s", c.NodeType)
}
if err != nil {
@@ -193,7 +194,7 @@ func (c *APIClient) GetUserList() (UserList *[]api.UserInfo, err error) {
case "Shadowsocks":
path = fmt.Sprintf("/api/ss/v1/userList/%d", c.NodeID)
default:
return nil, fmt.Errorf("Unsupported Node type: %s", c.NodeType)
return nil, fmt.Errorf("unsupported Node type: %s", c.NodeType)
}
res, err := c.createCommonRequest().
@@ -214,11 +215,11 @@ func (c *APIClient) GetUserList() (UserList *[]api.UserInfo, err error) {
case "Shadowsocks":
userList, err = c.ParseSSUserListResponse(&response.Data)
default:
return nil, fmt.Errorf("Unsupported Node type: %s", c.NodeType)
return nil, fmt.Errorf("unsupported Node type: %s", c.NodeType)
}
if err != nil {
res, _ := json.Marshal(response.Data)
return nil, fmt.Errorf("Parse user list failed: %s", string(res))
return nil, fmt.Errorf("parse user list failed: %s", string(res))
}
return userList, nil
}
@@ -234,11 +235,11 @@ func (c *APIClient) ReportNodeStatus(nodeStatus *api.NodeStatus) (err error) {
case "Shadowsocks":
path = fmt.Sprintf("/api/ss/v1/nodeStatus/%d", c.NodeID)
default:
return fmt.Errorf("Unsupported Node type: %s", c.NodeType)
return fmt.Errorf("unsupported Node type: %s", c.NodeType)
}
systemload := NodeStatus{
Uptime: nodeStatus.Uptime,
Uptime: int(nodeStatus.Uptime),
CPU: fmt.Sprintf("%d%%", int(nodeStatus.CPU)),
Mem: fmt.Sprintf("%d%%", int(nodeStatus.Mem)),
Disk: fmt.Sprintf("%d%%", int(nodeStatus.Disk)),
@@ -258,7 +259,7 @@ func (c *APIClient) ReportNodeStatus(nodeStatus *api.NodeStatus) (err error) {
return nil
}
//ReportNodeOnlineUsers reports online user ip
// ReportNodeOnlineUsers reports online user ip
func (c *APIClient) ReportNodeOnlineUsers(onlineUserList *[]api.OnlineUser) error {
var path string
@@ -270,7 +271,7 @@ func (c *APIClient) ReportNodeOnlineUsers(onlineUserList *[]api.OnlineUser) erro
case "Shadowsocks":
path = fmt.Sprintf("/api/ss/v1/nodeOnline/%d", c.NodeID)
default:
return fmt.Errorf("Unsupported Node type: %s", c.NodeType)
return fmt.Errorf("unsupported Node type: %s", c.NodeType)
}
data := make([]NodeOnline, len(*onlineUserList))
@@ -303,7 +304,7 @@ func (c *APIClient) ReportUserTraffic(userTraffic *[]api.UserTraffic) error {
case "Shadowsocks":
path = fmt.Sprintf("/api/ss/v1/userTraffic/%d", c.NodeID)
default:
return fmt.Errorf("Unsupported Node type: %s", c.NodeType)
return fmt.Errorf("unsupported Node type: %s", c.NodeType)
}
data := make([]UserTraffic, len(*userTraffic))
@@ -338,7 +339,7 @@ func (c *APIClient) GetNodeRule() (*[]api.DetectRule, error) {
case "Shadowsocks":
path = fmt.Sprintf("/api/ss/v1/nodeRule/%d", c.NodeID)
default:
return nil, fmt.Errorf("Unsupported Node type: %s", c.NodeType)
return nil, fmt.Errorf("unsupported Node type: %s", c.NodeType)
}
res, err := c.createCommonRequest().
@@ -354,7 +355,7 @@ func (c *APIClient) GetNodeRule() (*[]api.DetectRule, error) {
ruleListResponse := new(NodeRule)
if err := json.Unmarshal(response.Data, ruleListResponse); err != nil {
return nil, fmt.Errorf("Unmarshal %s failed: %s", reflect.TypeOf(ruleListResponse), err)
return nil, fmt.Errorf("unmarshal %s failed: %s", reflect.TypeOf(ruleListResponse), err)
}
ruleList := c.LocalRuleList
// Only support reject rule type
@@ -386,7 +387,7 @@ func (c *APIClient) ReportIllegal(detectResultList *[]api.DetectResult) error {
case "Shadowsocks":
path = fmt.Sprintf("/api/ss/v1/trigger/%d", c.NodeID)
default:
return fmt.Errorf("Unsupported Node type: %s", c.NodeType)
return fmt.Errorf("unsupported Node type: %s", c.NodeType)
}
for _, r := range *detectResultList {
@@ -411,23 +412,17 @@ func (c *APIClient) ReportIllegal(detectResultList *[]api.DetectResult) error {
// ParseV2rayNodeResponse parse the response for the given nodeinfor format
func (c *APIClient) ParseV2rayNodeResponse(nodeInfoResponse *json.RawMessage) (*api.NodeInfo, error) {
var TLStype string
var speedlimit uint64 = 0
if c.EnableXTLS {
TLStype = "xtls"
} else {
TLStype = "tls"
}
var speedLimit uint64 = 0
v2rayNodeInfo := new(V2rayNodeInfo)
if err := json.Unmarshal(*nodeInfoResponse, v2rayNodeInfo); err != nil {
return nil, fmt.Errorf("Unmarshal %s failed: %s", reflect.TypeOf(*nodeInfoResponse), err)
return nil, fmt.Errorf("unmarshal %s failed: %s", reflect.TypeOf(*nodeInfoResponse), err)
}
if c.SpeedLimit > 0 {
speedlimit = uint64((c.SpeedLimit * 1000000) / 8)
speedLimit = uint64((c.SpeedLimit * 1000000) / 8)
} else {
speedlimit = uint64((v2rayNodeInfo.SpeedLimit * 1000000) / 8)
speedLimit = (v2rayNodeInfo.SpeedLimit * 1000000) / 8
}
if c.DeviceLimit == 0 && v2rayNodeInfo.ClientLimit > 0 {
@@ -435,72 +430,65 @@ func (c *APIClient) ParseV2rayNodeResponse(nodeInfoResponse *json.RawMessage) (*
}
// Create GeneralNodeInfo
nodeinfo := &api.NodeInfo{
nodeInfo := &api.NodeInfo{
NodeType: c.NodeType,
NodeID: c.NodeID,
Port: v2rayNodeInfo.V2Port,
SpeedLimit: speedlimit,
SpeedLimit: speedLimit,
AlterID: v2rayNodeInfo.V2AlterID,
TransportProtocol: v2rayNodeInfo.V2Net,
FakeType: v2rayNodeInfo.V2Type,
EnableTLS: v2rayNodeInfo.V2TLS,
TLSType: TLStype,
Path: v2rayNodeInfo.V2Path,
Host: v2rayNodeInfo.V2Host,
EnableVless: c.EnableVless,
VlessFlow: c.VlessFlow,
}
return nodeinfo, nil
return nodeInfo, nil
}
// ParseSSNodeResponse parse the response for the given nodeinfor format
func (c *APIClient) ParseSSNodeResponse(nodeInfoResponse *json.RawMessage) (*api.NodeInfo, error) {
var speedlimit uint64 = 0
var speedLimit uint64 = 0
shadowsocksNodeInfo := new(ShadowsocksNodeInfo)
if err := json.Unmarshal(*nodeInfoResponse, shadowsocksNodeInfo); err != nil {
return nil, fmt.Errorf("Unmarshal %s failed: %s", reflect.TypeOf(*nodeInfoResponse), err)
return nil, fmt.Errorf("unmarshal %s failed: %s", reflect.TypeOf(*nodeInfoResponse), err)
}
if c.SpeedLimit > 0 {
speedlimit = uint64((c.SpeedLimit * 1000000) / 8)
speedLimit = uint64((c.SpeedLimit * 1000000) / 8)
} else {
speedlimit = uint64((shadowsocksNodeInfo.SpeedLimit * 1000000) / 8)
speedLimit = uint64((shadowsocksNodeInfo.SpeedLimit * 1000000) / 8)
}
if c.DeviceLimit == 0 && shadowsocksNodeInfo.ClientLimit > 0 {
c.DeviceLimit = shadowsocksNodeInfo.ClientLimit
}
// Create GeneralNodeInfo
nodeinfo := &api.NodeInfo{
nodeInfo := &api.NodeInfo{
NodeType: c.NodeType,
NodeID: c.NodeID,
Port: shadowsocksNodeInfo.Port,
SpeedLimit: speedlimit,
SpeedLimit: speedLimit,
TransportProtocol: "tcp",
CypherMethod: shadowsocksNodeInfo.Method,
}
return nodeinfo, nil
return nodeInfo, nil
}
// ParseTrojanNodeResponse parse the response for the given nodeinfor format
func (c *APIClient) ParseTrojanNodeResponse(nodeInfoResponse *json.RawMessage) (*api.NodeInfo, error) {
var TLSType string
var speedlimit uint64 = 0
if c.EnableXTLS {
TLSType = "xtls"
} else {
TLSType = "tls"
}
var speedLimit uint64 = 0
trojanNodeInfo := new(TrojanNodeInfo)
if err := json.Unmarshal(*nodeInfoResponse, trojanNodeInfo); err != nil {
return nil, fmt.Errorf("Unmarshal %s failed: %s", reflect.TypeOf(*nodeInfoResponse), err)
return nil, fmt.Errorf("unmarshal %s failed: %s", reflect.TypeOf(*nodeInfoResponse), err)
}
if c.SpeedLimit > 0 {
speedlimit = uint64((c.SpeedLimit * 1000000) / 8)
speedLimit = uint64((c.SpeedLimit * 1000000) / 8)
} else {
speedlimit = uint64((trojanNodeInfo.SpeedLimit * 1000000) / 8)
speedLimit = (trojanNodeInfo.SpeedLimit * 1000000) / 8
}
if c.DeviceLimit == 0 && trojanNodeInfo.ClientLimit > 0 {
@@ -508,41 +496,40 @@ func (c *APIClient) ParseTrojanNodeResponse(nodeInfoResponse *json.RawMessage) (
}
// Create GeneralNodeInfo
nodeinfo := &api.NodeInfo{
nodeInfo := &api.NodeInfo{
NodeType: c.NodeType,
NodeID: c.NodeID,
Port: trojanNodeInfo.TrojanPort,
SpeedLimit: speedlimit,
SpeedLimit: speedLimit,
TransportProtocol: "tcp",
EnableTLS: true,
TLSType: TLSType,
}
return nodeinfo, nil
return nodeInfo, nil
}
// ParseV2rayUserListResponse parse the response for the given userinfo format
func (c *APIClient) ParseV2rayUserListResponse(userInfoResponse *json.RawMessage) (*[]api.UserInfo, error) {
var speedlimit uint64 = 0
var speedLimit uint64 = 0
vmessUserList := new([]*VMessUser)
if err := json.Unmarshal(*userInfoResponse, vmessUserList); err != nil {
return nil, fmt.Errorf("Unmarshal %s failed: %s", reflect.TypeOf(*userInfoResponse), err)
return nil, fmt.Errorf("unmarshal %s failed: %s", reflect.TypeOf(*userInfoResponse), err)
}
userList := make([]api.UserInfo, len(*vmessUserList))
for i, user := range *vmessUserList {
if c.SpeedLimit > 0 {
speedlimit = uint64((c.SpeedLimit * 1000000) / 8)
speedLimit = uint64((c.SpeedLimit * 1000000) / 8)
} else {
speedlimit = uint64((user.SpeedLimit * 1000000) / 8)
speedLimit = (user.SpeedLimit * 1000000) / 8
}
userList[i] = api.UserInfo{
UID: user.UID,
Email: "",
UUID: user.VmessUID,
DeviceLimit: c.DeviceLimit,
SpeedLimit: speedlimit,
SpeedLimit: speedLimit,
}
}
@@ -551,26 +538,26 @@ func (c *APIClient) ParseV2rayUserListResponse(userInfoResponse *json.RawMessage
// ParseTrojanUserListResponse parse the response for the given userinfo format
func (c *APIClient) ParseTrojanUserListResponse(userInfoResponse *json.RawMessage) (*[]api.UserInfo, error) {
var speedlimit uint64 = 0
var speedLimit uint64 = 0
trojanUserList := new([]*TrojanUser)
if err := json.Unmarshal(*userInfoResponse, trojanUserList); err != nil {
return nil, fmt.Errorf("Unmarshal %s failed: %s", reflect.TypeOf(*userInfoResponse), err)
return nil, fmt.Errorf("unmarshal %s failed: %s", reflect.TypeOf(*userInfoResponse), err)
}
userList := make([]api.UserInfo, len(*trojanUserList))
for i, user := range *trojanUserList {
if c.SpeedLimit > 0 {
speedlimit = uint64((c.SpeedLimit * 1000000) / 8)
speedLimit = uint64((c.SpeedLimit * 1000000) / 8)
} else {
speedlimit = uint64((user.SpeedLimit * 1000000) / 8)
speedLimit = (user.SpeedLimit * 1000000) / 8
}
userList[i] = api.UserInfo{
UID: user.UID,
Email: "",
UUID: user.Password,
DeviceLimit: c.DeviceLimit,
SpeedLimit: speedlimit,
SpeedLimit: speedLimit,
}
}
@@ -579,26 +566,26 @@ func (c *APIClient) ParseTrojanUserListResponse(userInfoResponse *json.RawMessag
// ParseSSUserListResponse parse the response for the given userinfo format
func (c *APIClient) ParseSSUserListResponse(userInfoResponse *json.RawMessage) (*[]api.UserInfo, error) {
var speedlimit uint64 = 0
var speedLimit uint64 = 0
ssUserList := new([]*SSUser)
if err := json.Unmarshal(*userInfoResponse, ssUserList); err != nil {
return nil, fmt.Errorf("Unmarshal %s failed: %s", reflect.TypeOf(*userInfoResponse), err)
return nil, fmt.Errorf("unmarshal %s failed: %s", reflect.TypeOf(*userInfoResponse), err)
}
userList := make([]api.UserInfo, len(*ssUserList))
for i, user := range *ssUserList {
if c.SpeedLimit > 0 {
speedlimit = uint64((c.SpeedLimit * 1000000) / 8)
speedLimit = uint64((c.SpeedLimit * 1000000) / 8)
} else {
speedlimit = uint64((user.SpeedLimit * 1000000) / 8)
speedLimit = uint64(user.SpeedLimit * 1000000 / 8)
}
userList[i] = api.UserInfo{
UID: user.UID,
Email: "",
Passwd: user.Password,
DeviceLimit: c.DeviceLimit,
SpeedLimit: speedlimit,
SpeedLimit: speedLimit,
}
}

View File

@@ -89,7 +89,7 @@ func TestGetUserList(t *testing.T) {
func TestReportNodeStatus(t *testing.T) {
client := CreateClient()
nodeStatus := &api.NodeStatus{
1, 1, 1, 256,
CPU: 1, Mem: 1, Disk: 1, Uptime: 256,
}
err := client.ReportNodeStatus(nodeStatus)
if err != nil {
@@ -111,7 +111,7 @@ func TestReportReportNodeOnlineUsers(t *testing.T) {
IP: fmt.Sprintf("1.1.1.%d", i),
}
}
//client.Debug()
// client.Debug()
err = client.ReportNodeOnlineUsers(&onlineUserList)
if err != nil {
t.Error(err)
@@ -154,8 +154,8 @@ func TestReportIllegal(t *testing.T) {
client := CreateClient()
detectResult := []api.DetectResult{
api.DetectResult{1, 1},
api.DetectResult{1, 2},
{1, 1},
{1, 2},
}
client.Debug()
err := client.ReportIllegal(&detectResult)

View File

@@ -8,7 +8,6 @@ type NodeInfoResponse struct {
Class int `json:"node_class"`
SpeedLimit float64 `json:"node_speedlimit"`
TrafficRate float64 `json:"traffic_rate"`
MuOnly int `json:"mu_only"`
Sort int `json:"sort"`
RawServerString string `json:"server"`
Type string `json:"type"`
@@ -17,51 +16,35 @@ type NodeInfoResponse struct {
}
type CustomConfig struct {
OffsetPortUser string `json:"offset_port_user"`
OffsetPortNode string `json:"offset_port_node"`
ServerSub string `json:"server_sub"`
Host string `json:"host"`
MuPort string `json:"mu_port"`
MuEncryption string `json:"mu_encryption"`
MuProtocol string `json:"mu_protocol"`
MuObfs string `json:"mu_obfs"`
MuSuffix string `json:"mu_suffix"`
V2Port string `json:"v2_port"`
Method string `json:"method"`
TLS string `json:"tls"`
EnableVless string `json:"enable_vless"`
AlterID string `json:"alter_id"`
Network string `json:"network"`
Security string `json:"security"`
Path string `json:"path"`
VerifyCert bool `json:"verify_cert"`
Obfs string `json:"obfs"`
Header json.RawMessage `json:"header"`
TrojanPort string `json:"trojan_port"`
AllowInsecure string `json:"allow_insecure"`
Grpc string `json:"grpc"`
Servicename string `json:"servicename"`
EnableXtls string `json:"enable_xtls"`
Flow string `json:"flow"`
EnableREALITY bool `json:"enable_reality"`
RealityOpts *REALITYConfig `json:"reality-opts"`
}
// UserResponse is the response of user
type UserResponse struct {
ID int `json:"id"`
Email string `json:"email"`
Passwd string `json:"passwd"`
Port int `json:"port"`
Method string `json:"method"`
SpeedLimit float64 `json:"node_speedlimit"`
DeviceLimit int `json:"node_connector"`
Protocol string `json:"protocol"`
ProtocolParam string `json:"protocol_param"`
Obfs string `json:"obfs"`
ObfsParam string `json:"obfs_param"`
ForbiddenIP string `json:"forbidden_ip"`
ForbiddenPort string `json:"forbidden_port"`
UUID string `json:"uuid"`
MultiUser int `json:"is_multi_user"`
AliveIP int `json:"alive_ip"`
ID int `json:"id"`
Passwd string `json:"passwd"`
Port uint32 `json:"port"`
Method string `json:"method"`
SpeedLimit float64 `json:"node_speedlimit"`
DeviceLimit int `json:"node_iplimit"`
UUID string `json:"uuid"`
AliveIP int `json:"alive_ip"`
}
// Response is the common response
@@ -75,7 +58,7 @@ type PostData struct {
Data interface{} `json:"data"`
}
// SystemLoad is the data structure of systemload
// SystemLoad is the data structure of system load
type SystemLoad struct {
Uptime string `json:"uptime"`
Load string `json:"load"`
@@ -103,3 +86,14 @@ type IllegalItem struct {
ID int `json:"list_id"`
UID int `json:"user_id"`
}
type REALITYConfig struct {
Dest string `json:"dest,omitempty"`
ProxyProtocolVer uint64 `json:"proxy_protocol_ver,omitempty"`
ServerNames []string `json:"server_names,omitempty"`
PrivateKey string `json:"private_key,omitempty"`
MinClientVer string `json:"min_client_ver,omitempty"`
MaxClientVer string `json:"max_client_ver,omitempty"`
MaxTimeDiff uint64 `json:"max_time_diff,omitempty"`
ShortIds []string `json:"short_ids,omitempty"`
}

View File

@@ -3,6 +3,7 @@ package sspanel
import (
"bufio"
"encoding/json"
"errors"
"fmt"
"log"
"os"
@@ -13,14 +14,15 @@ import (
"sync"
"time"
"github.com/XrayR-project/XrayR/api"
"github.com/go-resty/resty/v2"
"github.com/XrayR-project/XrayR/api"
)
var (
firstPortRe = regexp.MustCompile(`(?m)port=(?P<outport>\d+)#?`) // First Port
secondPortRe = regexp.MustCompile(`(?m)port=\d+#(\d+)`) // Second Port
hostRe = regexp.MustCompile(`(?m)host=([\w\.]+)\|?`) // Host
hostRe = regexp.MustCompile(`(?m)host=([\w.]+)\|?`) // Host
)
// APIClient create a api client to the panel.
@@ -31,19 +33,21 @@ type APIClient struct {
Key string
NodeType string
EnableVless bool
EnableXTLS bool
VlessFlow string
SpeedLimit float64
DeviceLimit int
DisableCustomConfig bool
LocalRuleList []api.DetectRule
LastReportOnline map[int]int
access sync.Mutex
version string
eTags map[string]string
}
// New creat a api instance
// New create api instance
func New(apiConfig *api.Config) *APIClient {
client := resty.New()
client.SetRetryCount(3)
if apiConfig.Timeout > 0 {
client.SetTimeout(time.Duration(apiConfig.Timeout) * time.Second)
@@ -51,12 +55,14 @@ func New(apiConfig *api.Config) *APIClient {
client.SetTimeout(5 * time.Second)
}
client.OnError(func(req *resty.Request, err error) {
if v, ok := err.(*resty.ResponseError); ok {
var v *resty.ResponseError
if errors.As(err, &v) {
// v.Response contains the last response from the server
// v.Err contains the original error
log.Print(v.Err)
}
})
client.SetBaseURL(apiConfig.APIHost)
// Create Key for each requests
client.SetQueryParam("key", apiConfig.Key)
@@ -72,24 +78,25 @@ func New(apiConfig *api.Config) *APIClient {
APIHost: apiConfig.APIHost,
NodeType: apiConfig.NodeType,
EnableVless: apiConfig.EnableVless,
EnableXTLS: apiConfig.EnableXTLS,
VlessFlow: apiConfig.VlessFlow,
SpeedLimit: apiConfig.SpeedLimit,
DeviceLimit: apiConfig.DeviceLimit,
LocalRuleList: localRuleList,
DisableCustomConfig: apiConfig.DisableCustomConfig,
LastReportOnline: make(map[int]int),
eTags: make(map[string]string),
}
}
// readLocalRuleList reads the local rule list file
func readLocalRuleList(path string) (LocalRuleList []api.DetectRule) {
LocalRuleList = make([]api.DetectRule, 0)
if path != "" {
// open the file
file, err := os.Open(path)
defer file.Close()
//handle errors while opening
// handle errors while opening
if err != nil {
log.Printf("Error when opening file: %s", err)
return LocalRuleList
@@ -107,10 +114,8 @@ func readLocalRuleList(path string) (LocalRuleList []api.DetectRule) {
// handle first encountered error while reading
if err := fileScanner.Err(); err != nil {
log.Fatalf("Error while reading file: %s", err)
return make([]api.DetectRule, 0)
return
}
file.Close()
}
return LocalRuleList
@@ -143,18 +148,27 @@ func (c *APIClient) parseResponse(res *resty.Response, path string, err error) (
if response.Ret != 1 {
res, _ := json.Marshal(&response)
return nil, fmt.Errorf("Ret %s invalid", string(res))
return nil, fmt.Errorf("ret %s invalid", string(res))
}
return response, nil
}
// GetNodeInfo will pull NodeInfo Config from sspanel
// GetNodeInfo will pull NodeInfo Config from ssPanel
func (c *APIClient) GetNodeInfo() (nodeInfo *api.NodeInfo, err error) {
path := fmt.Sprintf("/mod_mu/nodes/%d/info", c.NodeID)
res, err := c.client.R().
SetResult(&Response{}).
SetHeader("If-None-Match", c.eTags["node"]).
ForceContentType("application/json").
Get(path)
// Etag identifier for a specific version of a resource. StatusCode = 304 means no changed
if res.StatusCode() == 304 {
return nil, errors.New(api.NodeNotModified)
}
if res.Header().Get("ETag") != "" && res.Header().Get("ETag") != c.eTags["node"] {
c.eTags["node"] = res.Header().Get("ETag")
}
response, err := c.parseResponse(res, path, err)
if err != nil {
@@ -164,28 +178,21 @@ func (c *APIClient) GetNodeInfo() (nodeInfo *api.NodeInfo, err error) {
nodeInfoResponse := new(NodeInfoResponse)
if err := json.Unmarshal(response.Data, nodeInfoResponse); err != nil {
return nil, fmt.Errorf("Unmarshal %s failed: %s", reflect.TypeOf(nodeInfoResponse), err)
return nil, fmt.Errorf("unmarshal %s failed: %s", reflect.TypeOf(nodeInfoResponse), err)
}
// New sspanel API
disableCustomConfig := c.DisableCustomConfig
if nodeInfoResponse.Version == "2021.11" && !disableCustomConfig {
// Check if custom_config is empty
if configString, err := json.Marshal(nodeInfoResponse.CustomConfig); err != nil || string(configString) == "[]" {
log.Printf("custom_config is empty! take config from address now.")
disableCustomConfig = true
}
} else {
disableCustomConfig = true
// determine ssPanel version, if disable custom config or version < 2021.11, then use old api
c.version = nodeInfoResponse.Version
var isExpired bool
if compareVersion(c.version, "2021.11") == -1 {
isExpired = true
}
if !disableCustomConfig {
nodeInfo, err = c.ParseSSPanelNodeInfo(nodeInfoResponse)
if err != nil {
res, _ := json.Marshal(nodeInfoResponse)
return nil, fmt.Errorf("Parse node info failed: %s, \nError: %s, \nPlease check the doc of custom_config for help: https://crackair.gitbook.io/xrayr-project/dui-jie-sspanel/sspanel/sspanel_custom_config", string(res), err)
if c.DisableCustomConfig || isExpired {
if isExpired {
log.Print("The panel version is expired, it is recommended to update immediately")
}
} else {
switch c.NodeType {
case "V2ray":
nodeInfo, err = c.ParseV2rayNodeResponse(nodeInfoResponse)
@@ -196,7 +203,13 @@ func (c *APIClient) GetNodeInfo() (nodeInfo *api.NodeInfo, err error) {
case "Shadowsocks-Plugin":
nodeInfo, err = c.ParseSSPluginNodeResponse(nodeInfoResponse)
default:
return nil, fmt.Errorf("Unsupported Node type: %s", c.NodeType)
return nil, fmt.Errorf("unsupported Node type: %s", c.NodeType)
}
} else {
nodeInfo, err = c.ParseSSPanelNodeInfo(nodeInfoResponse)
if err != nil {
res, _ := json.Marshal(nodeInfoResponse)
return nil, fmt.Errorf("Parse node info failed: %s, \nError: %s, \nPlease check the doc of custom_config for help: https://xrayr-project.github.io/XrayR-doc/dui-jie-sspanel/sspanel/sspanel_custom_config", string(res), err)
}
}
@@ -208,14 +221,23 @@ func (c *APIClient) GetNodeInfo() (nodeInfo *api.NodeInfo, err error) {
return nodeInfo, nil
}
// GetUserList will pull user form sspanel
// GetUserList will pull user form ssPanel
func (c *APIClient) GetUserList() (UserList *[]api.UserInfo, err error) {
path := "/mod_mu/users"
res, err := c.client.R().
SetQueryParam("node_id", strconv.Itoa(c.NodeID)).
SetHeader("If-None-Match", c.eTags["users"]).
SetResult(&Response{}).
ForceContentType("application/json").
Get(path)
// Etag identifier for a specific version of a resource. StatusCode = 304 means no changed
if res.StatusCode() == 304 {
return nil, errors.New(api.UserNotModified)
}
if res.Header().Get("ETag") != "" && res.Header().Get("ETag") != c.eTags["users"] {
c.eTags["users"] = res.Header().Get("ETag")
}
response, err := c.parseResponse(res, path, err)
if err != nil {
@@ -225,39 +247,41 @@ func (c *APIClient) GetUserList() (UserList *[]api.UserInfo, err error) {
userListResponse := new([]UserResponse)
if err := json.Unmarshal(response.Data, userListResponse); err != nil {
return nil, fmt.Errorf("Unmarshal %s failed: %s", reflect.TypeOf(userListResponse), err)
return nil, fmt.Errorf("unmarshal %s failed: %s", reflect.TypeOf(userListResponse), err)
}
userList, err := c.ParseUserListResponse(userListResponse)
if err != nil {
res, _ := json.Marshal(userListResponse)
return nil, fmt.Errorf("Parse user list failed: %s", string(res))
return nil, fmt.Errorf("parse user list failed: %s", string(res))
}
return userList, nil
}
// ReportNodeStatus reports the node status to the sspanel
// ReportNodeStatus reports the node status to the ssPanel
func (c *APIClient) ReportNodeStatus(nodeStatus *api.NodeStatus) (err error) {
path := fmt.Sprintf("/mod_mu/nodes/%d/info", c.NodeID)
systemload := SystemLoad{
Uptime: strconv.Itoa(nodeStatus.Uptime),
Load: fmt.Sprintf("%.2f %.2f %.2f", nodeStatus.CPU/100, nodeStatus.CPU/100, nodeStatus.CPU/100),
// Determine whether a status report is in need
if compareVersion(c.version, "2023.2") == -1 {
path := fmt.Sprintf("/mod_mu/nodes/%d/info", c.NodeID)
systemLoad := SystemLoad{
Uptime: strconv.FormatUint(nodeStatus.Uptime, 10),
Load: fmt.Sprintf("%.2f %.2f %.2f", nodeStatus.CPU/100, nodeStatus.Mem/100, nodeStatus.Disk/100),
}
res, err := c.client.R().
SetBody(systemLoad).
SetResult(&Response{}).
ForceContentType("application/json").
Post(path)
_, err = c.parseResponse(res, path, err)
if err != nil {
return err
}
}
res, err := c.client.R().
SetBody(systemload).
SetResult(&Response{}).
ForceContentType("application/json").
Post(path)
_, err = c.parseResponse(res, path, err)
if err != nil {
return err
}
return nil
}
//ReportNodeOnlineUsers reports online user ip
// ReportNodeOnlineUsers reports online user ip
func (c *APIClient) ReportNodeOnlineUsers(onlineUserList *[]api.OnlineUser) error {
c.access.Lock()
defer c.access.Unlock()
@@ -317,15 +341,25 @@ func (c *APIClient) ReportUserTraffic(userTraffic *[]api.UserTraffic) error {
return nil
}
// GetNodeRule will pull the audit rule form sspanel
// GetNodeRule will pull the audit rule form ssPanel
func (c *APIClient) GetNodeRule() (*[]api.DetectRule, error) {
ruleList := c.LocalRuleList
path := "/mod_mu/func/detect_rules"
res, err := c.client.R().
SetResult(&Response{}).
SetHeader("If-None-Match", c.eTags["rules"]).
ForceContentType("application/json").
Get(path)
// Etag identifier for a specific version of a resource. StatusCode = 304 means no changed
if res.StatusCode() == 304 {
return nil, errors.New(api.RuleNotModified)
}
if res.Header().Get("ETag") != "" && res.Header().Get("ETag") != c.eTags["rules"] {
c.eTags["rules"] = res.Header().Get("ETag")
}
response, err := c.parseResponse(res, path, err)
if err != nil {
return nil, err
@@ -334,7 +368,7 @@ func (c *APIClient) GetNodeRule() (*[]api.DetectRule, error) {
ruleListResponse := new([]RuleItem)
if err := json.Unmarshal(response.Data, ruleListResponse); err != nil {
return nil, fmt.Errorf("Unmarshal %s failed: %s", reflect.TypeOf(ruleListResponse), err)
return nil, fmt.Errorf("unmarshal %s failed: %s", reflect.TypeOf(ruleListResponse), err)
}
for _, r := range *ruleListResponse {
@@ -371,34 +405,34 @@ func (c *APIClient) ReportIllegal(detectResultList *[]api.DetectResult) error {
return nil
}
// ParseV2rayNodeResponse parse the response for the given nodeinfor format
// ParseV2rayNodeResponse parse the response for the given node info format
func (c *APIClient) ParseV2rayNodeResponse(nodeInfoResponse *NodeInfoResponse) (*api.NodeInfo, error) {
var enableTLS bool
var path, host, TLStype, transportProtocol, serviceName, HeaderType string
var path, host, transportProtocol, serviceName, HeaderType string
var header json.RawMessage
var speedlimit uint64 = 0
var speedLimit uint64 = 0
if nodeInfoResponse.RawServerString == "" {
return nil, fmt.Errorf("No server info in response")
return nil, fmt.Errorf("no server info in response")
}
//nodeInfo.RawServerString = strings.ToLower(nodeInfo.RawServerString)
// nodeInfo.RawServerString = strings.ToLower(nodeInfo.RawServerString)
serverConf := strings.Split(nodeInfoResponse.RawServerString, ";")
port, err := strconv.Atoi(serverConf[1])
parsedPort, err := strconv.ParseInt(serverConf[1], 10, 32)
if err != nil {
return nil, err
}
alterID, err := strconv.Atoi(serverConf[2])
port := uint32(parsedPort)
parsedAlterID, err := strconv.ParseInt(serverConf[2], 10, 16)
if err != nil {
return nil, err
}
alterID := uint16(parsedAlterID)
// Compatible with more node types config
for _, value := range serverConf[3:5] {
switch value {
case "tls", "xtls":
if c.EnableXTLS {
TLStype = "xtls"
} else {
TLStype = "tls"
}
case "tls":
enableTLS = true
default:
if value != "" {
@@ -423,14 +457,14 @@ func (c *APIClient) ParseV2rayNodeResponse(nodeInfoResponse *NodeInfoResponse) (
host = value
case "servicename":
serviceName = value
case "headertype":
case "headerType":
HeaderType = value
}
}
if c.SpeedLimit > 0 {
speedlimit = uint64((c.SpeedLimit * 1000000) / 8)
speedLimit = uint64((c.SpeedLimit * 1000000) / 8)
} else {
speedlimit = uint64((nodeInfoResponse.SpeedLimit * 1000000) / 8)
speedLimit = uint64((nodeInfoResponse.SpeedLimit * 1000000) / 8)
}
if HeaderType != "" {
@@ -439,33 +473,33 @@ func (c *APIClient) ParseV2rayNodeResponse(nodeInfoResponse *NodeInfoResponse) (
}
if err != nil {
return nil, fmt.Errorf("Marshal Header Type %s into config fialed: %s", header, err)
return nil, fmt.Errorf("marshal Header Type %s into config fialed: %s", header, err)
}
// Create GeneralNodeInfo
nodeinfo := &api.NodeInfo{
nodeInfo := &api.NodeInfo{
NodeType: c.NodeType,
NodeID: c.NodeID,
Port: port,
SpeedLimit: speedlimit,
SpeedLimit: speedLimit,
AlterID: alterID,
TransportProtocol: transportProtocol,
EnableTLS: enableTLS,
TLSType: TLStype,
Path: path,
Host: host,
EnableVless: c.EnableVless,
VlessFlow: c.VlessFlow,
ServiceName: serviceName,
Header: header,
}
return nodeinfo, nil
return nodeInfo, nil
}
// ParseSSNodeResponse parse the response for the given nodeinfor format
// ParseSSNodeResponse parse the response for the given node info format
func (c *APIClient) ParseSSNodeResponse(nodeInfoResponse *NodeInfoResponse) (*api.NodeInfo, error) {
var port int = 0
var speedlimit uint64 = 0
var port uint32 = 0
var speedLimit uint64 = 0
var method string
path := "/mod_mu/users"
res, err := c.client.R().
@@ -482,49 +516,39 @@ func (c *APIClient) ParseSSNodeResponse(nodeInfoResponse *NodeInfoResponse) (*ap
userListResponse := new([]UserResponse)
if err := json.Unmarshal(response.Data, userListResponse); err != nil {
return nil, fmt.Errorf("Unmarshal %s failed: %s", reflect.TypeOf(userListResponse), err)
}
// Find the multi-user
for _, u := range *userListResponse {
if u.MultiUser > 0 {
port = u.Port
method = u.Method
break
}
}
if port == 0 || method == "" {
return nil, fmt.Errorf("Cant find the single port multi user")
return nil, fmt.Errorf("unmarshal %s failed: %s", reflect.TypeOf(userListResponse), err)
}
if c.SpeedLimit > 0 {
speedlimit = uint64((c.SpeedLimit * 1000000) / 8)
speedLimit = uint64((c.SpeedLimit * 1000000) / 8)
} else {
speedlimit = uint64((nodeInfoResponse.SpeedLimit * 1000000) / 8)
speedLimit = uint64((nodeInfoResponse.SpeedLimit * 1000000) / 8)
}
// Create GeneralNodeInfo
nodeinfo := &api.NodeInfo{
nodeInfo := &api.NodeInfo{
NodeType: c.NodeType,
NodeID: c.NodeID,
Port: port,
SpeedLimit: speedlimit,
SpeedLimit: speedLimit,
TransportProtocol: "tcp",
CypherMethod: method,
}
return nodeinfo, nil
return nodeInfo, nil
}
// ParseSSPluginNodeResponse parse the response for the given nodeinfor format
// ParseSSPluginNodeResponse parse the response for the given node info format
func (c *APIClient) ParseSSPluginNodeResponse(nodeInfoResponse *NodeInfoResponse) (*api.NodeInfo, error) {
var enableTLS bool
var path, host, TLStype, transportProtocol string
var speedlimit uint64 = 0
var path, host, transportProtocol string
var speedLimit uint64 = 0
serverConf := strings.Split(nodeInfoResponse.RawServerString, ";")
port, err := strconv.Atoi(serverConf[1])
parsedPort, err := strconv.ParseInt(serverConf[1], 10, 32)
if err != nil {
return nil, err
}
port := uint32(parsedPort)
port = port - 1 // Shadowsocks-Plugin requires two ports, one for ss the other for other stream protocol
if port <= 0 {
return nil, fmt.Errorf("Shadowsocks-Plugin listen port must bigger than 1")
@@ -532,12 +556,7 @@ func (c *APIClient) ParseSSPluginNodeResponse(nodeInfoResponse *NodeInfoResponse
// Compatible with more node types config
for _, value := range serverConf[3:5] {
switch value {
case "tls", "xtls":
if c.EnableXTLS {
TLStype = "xtls"
} else {
TLStype = "tls"
}
case "tls":
enableTLS = true
case "ws":
transportProtocol = "ws"
@@ -563,41 +582,35 @@ func (c *APIClient) ParseSSPluginNodeResponse(nodeInfoResponse *NodeInfoResponse
}
}
if c.SpeedLimit > 0 {
speedlimit = uint64((c.SpeedLimit * 1000000) / 8)
speedLimit = uint64((c.SpeedLimit * 1000000) / 8)
} else {
speedlimit = uint64((nodeInfoResponse.SpeedLimit * 1000000) / 8)
speedLimit = uint64((nodeInfoResponse.SpeedLimit * 1000000) / 8)
}
// Create GeneralNodeInfo
nodeinfo := &api.NodeInfo{
nodeInfo := &api.NodeInfo{
NodeType: c.NodeType,
NodeID: c.NodeID,
Port: port,
SpeedLimit: speedlimit,
SpeedLimit: speedLimit,
TransportProtocol: transportProtocol,
EnableTLS: enableTLS,
TLSType: TLStype,
Path: path,
Host: host,
}
return nodeinfo, nil
return nodeInfo, nil
}
// ParseTrojanNodeResponse parse the response for the given nodeinfor format
// ParseTrojanNodeResponse parse the response for the given node info format
func (c *APIClient) ParseTrojanNodeResponse(nodeInfoResponse *NodeInfoResponse) (*api.NodeInfo, error) {
// 域名或IP;port=连接端口#偏移端口|host=xx
// gz.aaa.com;port=443#12345|host=hk.aaa.com
var p, TLSType, host, outsidePort, insidePort, transportProtocol, serviceName string
var speedlimit uint64 = 0
if c.EnableXTLS {
TLSType = "xtls"
} else {
TLSType = "tls"
}
var p, host, outsidePort, insidePort, transportProtocol, serviceName string
var speedLimit uint64 = 0
if nodeInfoResponse.RawServerString == "" {
return nil, fmt.Errorf("No server info in response")
return nil, fmt.Errorf("no server info in response")
}
if result := firstPortRe.FindStringSubmatch(nodeInfoResponse.RawServerString); len(result) > 1 {
outsidePort = result[1]
@@ -615,10 +628,11 @@ func (c *APIClient) ParseTrojanNodeResponse(nodeInfoResponse *NodeInfoResponse)
p = outsidePort
}
port, err := strconv.Atoi(p)
parsedPort, err := strconv.ParseInt(p, 10, 32)
if err != nil {
return nil, err
}
port := uint32(parsedPort)
serverConf := strings.Split(nodeInfoResponse.RawServerString, ";")
extraServerConf := strings.Split(serverConf[1], "|")
@@ -640,27 +654,26 @@ func (c *APIClient) ParseTrojanNodeResponse(nodeInfoResponse *NodeInfoResponse)
}
if c.SpeedLimit > 0 {
speedlimit = uint64((c.SpeedLimit * 1000000) / 8)
speedLimit = uint64((c.SpeedLimit * 1000000) / 8)
} else {
speedlimit = uint64((nodeInfoResponse.SpeedLimit * 1000000) / 8)
speedLimit = uint64((nodeInfoResponse.SpeedLimit * 1000000) / 8)
}
// Create GeneralNodeInfo
nodeinfo := &api.NodeInfo{
nodeInfo := &api.NodeInfo{
NodeType: c.NodeType,
NodeID: c.NodeID,
Port: port,
SpeedLimit: speedlimit,
SpeedLimit: speedLimit,
TransportProtocol: transportProtocol,
EnableTLS: true,
TLSType: TLSType,
Host: host,
ServiceName: serviceName,
}
return nodeinfo, nil
return nodeInfo, nil
}
// ParseUserListResponse parse the response for the given nodeinfo format
// ParseUserListResponse parse the response for the given node info format
func (c *APIClient) ParseUserListResponse(userInfoResponse *[]UserResponse) (*[]api.UserInfo, error) {
c.access.Lock()
// Clear Last report log
@@ -670,8 +683,8 @@ func (c *APIClient) ParseUserListResponse(userInfoResponse *[]UserResponse) (*[]
}()
var deviceLimit, localDeviceLimit int = 0, 0
var speedlimit uint64 = 0
userList := []api.UserInfo{}
var speedLimit uint64 = 0
var userList []api.UserInfo
for _, user := range *userInfoResponse {
if c.DeviceLimit > 0 {
deviceLimit = c.DeviceLimit
@@ -698,101 +711,147 @@ func (c *APIClient) ParseUserListResponse(userInfoResponse *[]UserResponse) (*[]
}
if c.SpeedLimit > 0 {
speedlimit = uint64((c.SpeedLimit * 1000000) / 8)
speedLimit = uint64((c.SpeedLimit * 1000000) / 8)
} else {
speedlimit = uint64((user.SpeedLimit * 1000000) / 8)
speedLimit = uint64((user.SpeedLimit * 1000000) / 8)
}
userList = append(userList, api.UserInfo{
UID: user.ID,
Email: user.Email,
UUID: user.UUID,
Passwd: user.Passwd,
SpeedLimit: speedlimit,
DeviceLimit: deviceLimit,
Port: user.Port,
Method: user.Method,
Protocol: user.Protocol,
ProtocolParam: user.ProtocolParam,
Obfs: user.Obfs,
ObfsParam: user.ObfsParam,
UID: user.ID,
UUID: user.UUID,
Passwd: user.Passwd,
SpeedLimit: speedLimit,
DeviceLimit: deviceLimit,
Port: user.Port,
Method: user.Method,
})
}
return &userList, nil
}
// ParseSSPanelNodeInfo parse the response for the given nodeinfor format
// Only used for SSPanel version >= 2021.11
// ParseSSPanelNodeInfo parse the response for the given node info format
// Only available for SSPanel version >= 2021.11
func (c *APIClient) ParseSSPanelNodeInfo(nodeInfoResponse *NodeInfoResponse) (*api.NodeInfo, error) {
var (
speedLimit uint64 = 0
enableTLS, enableVless bool
alterID uint16 = 0
tlsType, transportProtocol string
)
var speedlimit uint64 = 0
var EnableTLS, EnableVless bool
var AlterID int = 0
var TLSType, transportProtocol string
nodeConfig := new(CustomConfig)
json.Unmarshal(nodeInfoResponse.CustomConfig, nodeConfig)
if c.SpeedLimit > 0 {
speedlimit = uint64((c.SpeedLimit * 1000000) / 8)
} else {
speedlimit = uint64((nodeInfoResponse.SpeedLimit * 1000000) / 8)
// Check if custom_config is null
if len(nodeInfoResponse.CustomConfig) == 0 {
return nil, errors.New("custom_config is empty, disable custom config")
}
port, err := strconv.Atoi(nodeConfig.OffsetPortNode)
nodeConfig := new(CustomConfig)
err := json.Unmarshal(nodeInfoResponse.CustomConfig, nodeConfig)
if err != nil {
return nil, fmt.Errorf("custom_config format error: %v", err)
}
if c.SpeedLimit > 0 {
speedLimit = uint64((c.SpeedLimit * 1000000) / 8)
} else {
speedLimit = uint64((nodeInfoResponse.SpeedLimit * 1000000) / 8)
}
parsedPort, err := strconv.ParseInt(nodeConfig.OffsetPortNode, 10, 32)
if err != nil {
return nil, err
}
if c.NodeType == "Shadowsocks" {
port := uint32(parsedPort)
switch c.NodeType {
case "Shadowsocks":
transportProtocol = "tcp"
}
if c.NodeType == "V2ray" {
case "V2ray":
transportProtocol = nodeConfig.Network
TLSType = nodeConfig.Security
if AlterID, err = strconv.Atoi(nodeConfig.AlterID); err != nil {
return nil, err
}
if TLSType == "tls" || TLSType == "xtls" {
EnableTLS = true
tlsType = nodeConfig.Security
if tlsType == "tls" || tlsType == "xtls" {
enableTLS = true
}
if nodeConfig.EnableVless == "1" {
EnableVless = true
enableVless = true
}
case "Trojan":
enableTLS = true
tlsType = "tls"
transportProtocol = "tcp"
if nodeConfig.Security != "" {
tlsType = nodeConfig.Security // try to read security from config
}
// Select transport protocol
if nodeConfig.Network != "" {
transportProtocol = nodeConfig.Network // try to read transport protocol from config
}
}
if c.NodeType == "Trojan" {
EnableTLS = true
TLSType = "tls"
if nodeConfig.Grpc == "1" {
transportProtocol = "grpc"
} else {
transportProtocol = "tcp"
}
if nodeConfig.EnableXtls == "1" {
TLSType = "xtls"
// parse reality config
realityConfig := new(api.REALITYConfig)
if nodeConfig.RealityOpts != nil {
r := nodeConfig.RealityOpts
realityConfig = &api.REALITYConfig{
Dest: r.Dest,
ProxyProtocolVer: r.ProxyProtocolVer,
ServerNames: r.ServerNames,
PrivateKey: r.PrivateKey,
MinClientVer: r.MinClientVer,
MaxClientVer: r.MaxClientVer,
MaxTimeDiff: r.MaxTimeDiff,
ShortIds: r.ShortIds,
}
}
// Create GeneralNodeInfo
nodeinfo := &api.NodeInfo{
nodeInfo := &api.NodeInfo{
NodeType: c.NodeType,
NodeID: c.NodeID,
Port: port,
SpeedLimit: speedlimit,
AlterID: AlterID,
SpeedLimit: speedLimit,
AlterID: alterID,
TransportProtocol: transportProtocol,
Host: nodeConfig.Host,
Path: nodeConfig.Path,
EnableTLS: EnableTLS,
TLSType: TLSType,
EnableVless: EnableVless,
CypherMethod: nodeConfig.MuEncryption,
EnableTLS: enableTLS,
EnableVless: enableVless,
VlessFlow: nodeConfig.Flow,
CypherMethod: nodeConfig.Method,
ServiceName: nodeConfig.Servicename,
Header: nodeConfig.Header,
EnableREALITY: nodeConfig.EnableREALITY,
REALITYConfig: realityConfig,
}
return nodeinfo, nil
return nodeInfo, nil
}
// compareVersion, version1 > version2 return 1, version1 < version2 return -1, 0 means equal
func compareVersion(version1, version2 string) int {
n, m := len(version1), len(version2)
i, j := 0, 0
for i < n || j < m {
x := 0
for ; i < n && version1[i] != '.'; i++ {
x = x*10 + int(version1[i]-'0')
}
i++ // jump dot
y := 0
for ; j < m && version2[j] != '.'; j++ {
y = y*10 + int(version2[j]-'0')
}
j++ // jump dot
if x > y {
return 1
}
if x < y {
return -1
}
}
return 0
}

View File

@@ -19,7 +19,7 @@ func CreateClient() api.API {
return client
}
func TestGetV2rayNodeinfo(t *testing.T) {
func TestGetV2rayNodeInfo(t *testing.T) {
client := CreateClient()
nodeInfo, err := client.GetNodeInfo()
@@ -29,7 +29,7 @@ func TestGetV2rayNodeinfo(t *testing.T) {
t.Log(nodeInfo)
}
func TestGetSSNodeinfo(t *testing.T) {
func TestGetSSNodeInfo(t *testing.T) {
apiConfig := &api.Config{
APIHost: "http://127.0.0.1:667",
Key: "123",
@@ -44,7 +44,7 @@ func TestGetSSNodeinfo(t *testing.T) {
t.Log(nodeInfo)
}
func TestGetTrojanNodeinfo(t *testing.T) {
func TestGetTrojanNodeInfo(t *testing.T) {
apiConfig := &api.Config{
APIHost: "http://127.0.0.1:667",
Key: "123",
@@ -59,7 +59,7 @@ func TestGetTrojanNodeinfo(t *testing.T) {
t.Log(nodeInfo)
}
func TestGetSSinfo(t *testing.T) {
func TestGetSSInfo(t *testing.T) {
client := CreateClient()
nodeInfo, err := client.GetNodeInfo()
@@ -83,7 +83,7 @@ func TestGetUserList(t *testing.T) {
func TestReportNodeStatus(t *testing.T) {
client := CreateClient()
nodeStatus := &api.NodeStatus{
1, 1, 1, 256,
CPU: 1, Mem: 1, Disk: 1, Uptime: 256,
}
err := client.ReportNodeStatus(nodeStatus)
if err != nil {
@@ -105,7 +105,7 @@ func TestReportReportNodeOnlineUsers(t *testing.T) {
IP: fmt.Sprintf("1.1.1.%d", i),
}
}
//client.Debug()
// client.Debug()
err = client.ReportNodeOnlineUsers(&onlineUserList)
if err != nil {
t.Error(err)
@@ -126,7 +126,7 @@ func TestReportReportUserTraffic(t *testing.T) {
Download: 114514,
}
}
//client.Debug()
// client.Debug()
err = client.ReportUserTraffic(&generalUserTraffic)
if err != nil {
t.Error(err)
@@ -148,8 +148,8 @@ func TestReportIllegal(t *testing.T) {
client := CreateClient()
detectResult := []api.DetectResult{
api.DetectResult{1, 2},
api.DetectResult{1, 3},
{1, 2},
{1, 3},
}
client.Debug()
err := client.ReportIllegal(&detectResult)

View File

@@ -1,7 +0,0 @@
package v2board
type UserTraffic struct {
UID int `json:"user_id"`
Upload int64 `json:"u"`
Download int64 `json:"d"`
}

7
api/v2raysocks/model.go Normal file
View File

@@ -0,0 +1,7 @@
package v2raysocks
type UserTraffic struct {
UID int `json:"user_id"`
Upload int64 `json:"u"`
Download int64 `json:"d"`
}

View File

@@ -1,4 +1,4 @@
package v2board
package v2raysocks
import (
"bufio"
@@ -8,12 +8,16 @@ import (
"os"
"regexp"
"strconv"
"strings"
"sync"
"time"
"github.com/XrayR-project/XrayR/api"
"github.com/bitly/go-simplejson"
"github.com/go-resty/resty/v2"
"github.com/sagernet/sing-shadowsocks/shadowaead_2022"
C "github.com/sagernet/sing/common"
"github.com/XrayR-project/XrayR/api"
)
// APIClient create an api client to the panel.
@@ -24,7 +28,7 @@ type APIClient struct {
Key string
NodeType string
EnableVless bool
EnableXTLS bool
VlessFlow string
SpeedLimit float64
DeviceLimit int
LocalRuleList []api.DetectRule
@@ -49,7 +53,6 @@ func New(apiConfig *api.Config) *APIClient {
log.Print(v.Err)
}
})
client.SetBaseURL(apiConfig.APIHost)
// Create Key for each requests
client.SetQueryParams(map[string]string{
"node_id": strconv.Itoa(apiConfig.NodeID),
@@ -64,7 +67,7 @@ func New(apiConfig *api.Config) *APIClient {
APIHost: apiConfig.APIHost,
NodeType: apiConfig.NodeType,
EnableVless: apiConfig.EnableVless,
EnableXTLS: apiConfig.EnableXTLS,
VlessFlow: apiConfig.VlessFlow,
SpeedLimit: apiConfig.SpeedLimit,
DeviceLimit: apiConfig.DeviceLimit,
LocalRuleList: localRuleList,
@@ -80,7 +83,7 @@ func readLocalRuleList(path string) (LocalRuleList []api.DetectRule) {
// open the file
file, err := os.Open(path)
//handle errors while opening
// handle errors while opening
if err != nil {
log.Printf("Error when opening file: %s", err)
return LocalRuleList
@@ -98,7 +101,7 @@ func readLocalRuleList(path string) (LocalRuleList []api.DetectRule) {
// handle first encountered error while reading
if err := fileScanner.Err(); err != nil {
log.Fatalf("Error while reading file: %s", err)
return make([]api.DetectRule, 0)
return
}
file.Close()
@@ -132,34 +135,29 @@ func (c *APIClient) parseResponse(res *resty.Response, path string, err error) (
}
rtn, err := simplejson.NewJson(res.Body())
if err != nil {
return nil, fmt.Errorf("Ret %s invalid", res.String())
return nil, fmt.Errorf("ret %s invalid", res.String())
}
return rtn, nil
}
// GetNodeInfo will pull NodeInfo Config from sspanel
// GetNodeInfo will pull NodeInfo Config from panel
func (c *APIClient) GetNodeInfo() (nodeInfo *api.NodeInfo, err error) {
var path string
var nodeType string
switch c.NodeType {
case "V2ray":
path = "/api/v1/server/Deepbwork/config"
case "Trojan":
path = "/api/v1/server/TrojanTidalab/config"
case "Shadowsocks":
if nodeInfo, err = c.ParseSSNodeResponse(); err == nil {
return nodeInfo, nil
} else {
return nil, err
}
case "V2ray", "Trojan", "Shadowsocks":
nodeType = strings.ToLower(c.NodeType)
default:
return nil, fmt.Errorf("unsupported Node type: %s", c.NodeType)
}
res, err := c.client.R().
SetQueryParam("local_port", "1").
SetQueryParams(map[string]string{
"act": "config",
"nodetype": nodeType,
}).
ForceContentType("application/json").
Get(path)
Get(c.APIHost)
response, err := c.parseResponse(res, path, err)
response, err := c.parseResponse(res, "", err)
c.access.Lock()
defer c.access.Unlock()
c.ConfigResp = response
@@ -173,7 +171,7 @@ func (c *APIClient) GetNodeInfo() (nodeInfo *api.NodeInfo, err error) {
case "Trojan":
nodeInfo, err = c.ParseTrojanNodeResponse(response)
case "Shadowsocks":
nodeInfo, err = c.ParseSSNodeResponse()
nodeInfo, err = c.ParseSSNodeResponse(response)
default:
return nil, fmt.Errorf("unsupported Node type: %s", c.NodeType)
}
@@ -186,24 +184,24 @@ func (c *APIClient) GetNodeInfo() (nodeInfo *api.NodeInfo, err error) {
return nodeInfo, nil
}
// GetUserList will pull user form sspanel
// GetUserList will pull user form panel
func (c *APIClient) GetUserList() (UserList *[]api.UserInfo, err error) {
var path string
var nodeType string
switch c.NodeType {
case "V2ray":
path = "/api/v1/server/Deepbwork/user"
case "Trojan":
path = "/api/v1/server/TrojanTidalab/user"
case "Shadowsocks":
path = "/api/v1/server/ShadowsocksTidalab/user"
case "V2ray", "Trojan", "Shadowsocks":
nodeType = strings.ToLower(c.NodeType)
default:
return nil, fmt.Errorf("unsupported Node type: %s", c.NodeType)
}
res, err := c.client.R().
SetQueryParams(map[string]string{
"act": "user",
"nodetype": nodeType,
}).
ForceContentType("application/json").
Get(path)
Get(c.APIHost)
response, err := c.parseResponse(res, path, err)
response, err := c.parseResponse(res, "", err)
if err != nil {
return nil, err
}
@@ -212,22 +210,26 @@ func (c *APIClient) GetUserList() (UserList *[]api.UserInfo, err error) {
for i := 0; i < numOfUsers; i++ {
user := api.UserInfo{}
user.UID = response.Get("data").GetIndex(i).Get("id").MustInt()
user.SpeedLimit = uint64(c.SpeedLimit * 1000000 / 8)
user.DeviceLimit = c.DeviceLimit
switch c.NodeType {
case "Shadowsocks":
user.Email = response.Get("data").GetIndex(i).Get("secret").MustString()
user.Passwd = response.Get("data").GetIndex(i).Get("secret").MustString()
user.Method = response.Get("data").GetIndex(i).Get("cipher").MustString()
user.Port = response.Get("data").GetIndex(i).Get("port").MustInt()
user.Email = response.Get("data").GetIndex(i).Get("shadowsocks_user").Get("secret").MustString()
user.Passwd = response.Get("data").GetIndex(i).Get("shadowsocks_user").Get("secret").MustString()
user.Method = response.Get("data").GetIndex(i).Get("shadowsocks_user").Get("cipher").MustString()
user.SpeedLimit = response.Get("data").GetIndex(i).Get("shadowsocks_user").Get("speed_limit").MustUint64() * 1000000 / 8
case "Trojan":
user.UUID = response.Get("data").GetIndex(i).Get("trojan_user").Get("password").MustString()
user.Email = response.Get("data").GetIndex(i).Get("trojan_user").Get("password").MustString()
user.SpeedLimit = response.Get("data").GetIndex(i).Get("trojan_user").Get("speed_limit").MustUint64() * 1000000 / 8
case "V2ray":
user.UUID = response.Get("data").GetIndex(i).Get("v2ray_user").Get("uuid").MustString()
user.Email = response.Get("data").GetIndex(i).Get("v2ray_user").Get("email").MustString()
user.AlterID = response.Get("data").GetIndex(i).Get("v2ray_user").Get("alter_id").MustInt()
user.AlterID = uint16(response.Get("data").GetIndex(i).Get("v2ray_user").Get("alter_id").MustUint64())
user.SpeedLimit = response.Get("data").GetIndex(i).Get("v2ray_user").Get("speed_limit").MustUint64() * 1000000 / 8
}
if c.SpeedLimit > 0 {
user.SpeedLimit = uint64((c.SpeedLimit * 1000000) / 8)
}
user.DeviceLimit = c.DeviceLimit
userList[i] = user
}
return &userList, nil
@@ -235,15 +237,6 @@ func (c *APIClient) GetUserList() (UserList *[]api.UserInfo, err error) {
// ReportUserTraffic reports the user traffic
func (c *APIClient) ReportUserTraffic(userTraffic *[]api.UserTraffic) error {
var path string
switch c.NodeType {
case "V2ray":
path = "/api/v1/server/Deepbwork/submit"
case "Trojan":
path = "/api/v1/server/TrojanTidalab/submit"
case "Shadowsocks":
path = "/api/v1/server/ShadowsocksTidalab/submit"
}
data := make([]UserTraffic, len(*userTraffic))
for i, traffic := range *userTraffic {
@@ -255,10 +248,14 @@ func (c *APIClient) ReportUserTraffic(userTraffic *[]api.UserTraffic) error {
res, err := c.client.R().
SetQueryParam("node_id", strconv.Itoa(c.NodeID)).
SetQueryParams(map[string]string{
"act": "submit",
"nodetype": strings.ToLower(c.NodeType),
}).
SetBody(data).
ForceContentType("application/json").
Post(path)
_, err = c.parseResponse(res, path, err)
Post(c.APIHost)
_, err = c.parseResponse(res, "", err)
if err != nil {
return err
}
@@ -272,12 +269,13 @@ func (c *APIClient) GetNodeRule() (*[]api.DetectRule, error) {
return &ruleList, nil
}
// V2board only support the rule for v2ray
// Only support the rule for v2ray
// fix: reuse config response
c.access.Lock()
defer c.access.Unlock()
ruleListResponse := c.ConfigResp.Get("routing").Get("rules").GetIndex(1).Get("domain").MustStringArray()
for i, rule := range ruleListResponse {
rule = strings.TrimPrefix(rule, "regexp:")
ruleListItem := api.DetectRule{
ID: i,
Pattern: regexp.MustCompile(rule),
@@ -292,7 +290,7 @@ func (c *APIClient) ReportNodeStatus(nodeStatus *api.NodeStatus) (err error) {
return nil
}
//ReportNodeOnlineUsers implements the API interface
// ReportNodeOnlineUsers implements the API interface
func (c *APIClient) ReportNodeOnlineUsers(onlineUserList *[]api.OnlineUser) error {
return nil
}
@@ -302,77 +300,74 @@ func (c *APIClient) ReportIllegal(detectResultList *[]api.DetectResult) error {
return nil
}
// ParseTrojanNodeResponse parse the response for the given nodeinfor format
// ParseTrojanNodeResponse parse the response for the given nodeInfo format
func (c *APIClient) ParseTrojanNodeResponse(nodeInfoResponse *simplejson.Json) (*api.NodeInfo, error) {
var TLSType = "tls"
if c.EnableXTLS {
TLSType = "xtls"
}
port := nodeInfoResponse.Get("local_port").MustInt()
host := nodeInfoResponse.Get("ssl").Get("sni").MustString()
tmpInboundInfo := nodeInfoResponse.Get("inbounds").MustArray()
marshalByte, _ := json.Marshal(tmpInboundInfo[0].(map[string]interface{}))
inboundInfo, _ := simplejson.NewJson(marshalByte)
port := uint32(inboundInfo.Get("port").MustUint64())
host := inboundInfo.Get("streamSettings").Get("tlsSettings").Get("serverName").MustString()
// Create GeneralNodeInfo
nodeinfo := &api.NodeInfo{
nodeInfo := &api.NodeInfo{
NodeType: c.NodeType,
NodeID: c.NodeID,
Port: port,
TransportProtocol: "tcp",
EnableTLS: true,
TLSType: TLSType,
Host: host,
}
return nodeinfo, nil
return nodeInfo, nil
}
// ParseSSNodeResponse parse the response for the given nodeinfor format
func (c *APIClient) ParseSSNodeResponse() (*api.NodeInfo, error) {
var port int
var method string
userInfo, err := c.GetUserList()
if err != nil {
return nil, err
}
if len(*userInfo) > 0 {
port = (*userInfo)[0].Port
method = (*userInfo)[0].Method
// ParseSSNodeResponse parse the response for the given nodeInfo format
func (c *APIClient) ParseSSNodeResponse(nodeInfoResponse *simplejson.Json) (*api.NodeInfo, error) {
var method, serverPsk string
tmpInboundInfo := nodeInfoResponse.Get("inbounds").MustArray()
marshalByte, _ := json.Marshal(tmpInboundInfo[0].(map[string]interface{}))
inboundInfo, _ := simplejson.NewJson(marshalByte)
port := uint32(inboundInfo.Get("port").MustUint64())
method = inboundInfo.Get("settings").Get("method").MustString()
// Shadowsocks 2022
if C.Contains(shadowaead_2022.List, method) {
serverPsk = inboundInfo.Get("settings").Get("password").MustString()
} else {
userInfo, err := c.GetUserList()
if err != nil {
return nil, err
}
if len(*userInfo) > 0 {
method = (*userInfo)[0].Method
}
}
// Create GeneralNodeInfo
nodeinfo := &api.NodeInfo{
nodeInfo := &api.NodeInfo{
NodeType: c.NodeType,
NodeID: c.NodeID,
Port: port,
TransportProtocol: "tcp",
CypherMethod: method,
ServerKey: serverPsk,
}
return nodeinfo, nil
return nodeInfo, nil
}
// ParseV2rayNodeResponse parse the response for the given nodeinfor format
// ParseV2rayNodeResponse parse the response for the given nodeInfo format
func (c *APIClient) ParseV2rayNodeResponse(nodeInfoResponse *simplejson.Json) (*api.NodeInfo, error) {
var TLSType string = "tls"
var path, host, serviceName string
var header json.RawMessage
var enableTLS bool
var alterID int = 0
if c.EnableXTLS {
TLSType = "xtls"
}
var alterID uint16 = 0
inboundInfo := simplejson.New()
if tmpInboundInfo, ok := nodeInfoResponse.CheckGet("inbound"); ok {
inboundInfo = tmpInboundInfo
// Compatible with v2board 1.5.5-dev
} else if tmpInboundInfo, ok := nodeInfoResponse.CheckGet("inbounds"); ok {
tmpInboundInfo := tmpInboundInfo.MustArray()
marshalByte, _ := json.Marshal(tmpInboundInfo[0].(map[string]interface{}))
inboundInfo, _ = simplejson.NewJson(marshalByte)
} else {
return nil, fmt.Errorf("Unable to find inbound(s) in the nodeInfo.")
}
tmpInboundInfo := nodeInfoResponse.Get("inbounds").MustArray()
marshalByte, _ := json.Marshal(tmpInboundInfo[0].(map[string]interface{}))
inboundInfo, _ := simplejson.NewJson(marshalByte)
port := inboundInfo.Get("port").MustInt()
port := uint32(inboundInfo.Get("port").MustUint64())
transportProtocol := inboundInfo.Get("streamSettings").Get("network").MustString()
switch transportProtocol {
@@ -408,10 +403,10 @@ func (c *APIClient) ParseV2rayNodeResponse(nodeInfoResponse *simplejson.Json) (*
AlterID: alterID,
TransportProtocol: transportProtocol,
EnableTLS: enableTLS,
TLSType: TLSType,
Path: path,
Host: host,
EnableVless: c.EnableVless,
VlessFlow: c.VlessFlow,
ServiceName: serviceName,
Header: header,
}

View File

@@ -0,0 +1,102 @@
package v2raysocks_test
import (
"testing"
"github.com/XrayR-project/XrayR/api"
"github.com/XrayR-project/XrayR/api/v2raysocks"
)
func CreateClient() api.API {
apiConfig := &api.Config{
APIHost: "https://127.0.0.1/",
Key: "123456789",
NodeID: 280002,
NodeType: "V2ray",
}
client := v2raysocks.New(apiConfig)
return client
}
func TestGetV2rayNodeinfo(t *testing.T) {
client := CreateClient()
client.Debug()
nodeInfo, err := client.GetNodeInfo()
if err != nil {
t.Error(err)
}
t.Log(nodeInfo)
}
func TestGetSSNodeinfo(t *testing.T) {
apiConfig := &api.Config{
APIHost: "https://127.0.0.1/",
Key: "123456789",
NodeID: 280009,
NodeType: "Shadowsocks",
}
client := v2raysocks.New(apiConfig)
nodeInfo, err := client.GetNodeInfo()
if err != nil {
t.Error(err)
}
t.Log(nodeInfo)
}
func TestGetTrojanNodeinfo(t *testing.T) {
apiConfig := &api.Config{
APIHost: "https://127.0.0.1/",
Key: "123456789",
NodeID: 280008,
NodeType: "Trojan",
}
client := v2raysocks.New(apiConfig)
nodeInfo, err := client.GetNodeInfo()
if err != nil {
t.Error(err)
}
t.Log(nodeInfo)
}
func TestGetUserList(t *testing.T) {
client := CreateClient()
userList, err := client.GetUserList()
if err != nil {
t.Error(err)
}
t.Log(userList)
}
func TestReportReportUserTraffic(t *testing.T) {
client := CreateClient()
userList, err := client.GetUserList()
if err != nil {
t.Error(err)
}
generalUserTraffic := make([]api.UserTraffic, len(*userList))
for i, userInfo := range *userList {
generalUserTraffic[i] = api.UserTraffic{
UID: userInfo.UID,
Upload: 114514,
Download: 114514,
}
}
// client.Debug()
err = client.ReportUserTraffic(&generalUserTraffic)
if err != nil {
t.Error(err)
}
}
func TestGetNodeRule(t *testing.T) {
client := CreateClient()
client.Debug()
ruleList, err := client.GetNodeRule()
if err != nil {
t.Error(err)
}
t.Log(ruleList)
}

View File

@@ -1,8 +1,8 @@
// Code generated by protoc-gen-go. DO NOT EDIT.
// versions:
// protoc-gen-go v1.26.0
// protoc-gen-go v1.28.0
// protoc v3.19.4
// source: config.proto
// source: app/mydispatcher/config.proto
package mydispatcher
@@ -29,7 +29,7 @@ type SessionConfig struct {
func (x *SessionConfig) Reset() {
*x = SessionConfig{}
if protoimpl.UnsafeEnabled {
mi := &file_config_proto_msgTypes[0]
mi := &file_app_mydispatcher_config_proto_msgTypes[0]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
@@ -42,7 +42,7 @@ func (x *SessionConfig) String() string {
func (*SessionConfig) ProtoMessage() {}
func (x *SessionConfig) ProtoReflect() protoreflect.Message {
mi := &file_config_proto_msgTypes[0]
mi := &file_app_mydispatcher_config_proto_msgTypes[0]
if protoimpl.UnsafeEnabled && x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil {
@@ -55,7 +55,7 @@ func (x *SessionConfig) ProtoReflect() protoreflect.Message {
// Deprecated: Use SessionConfig.ProtoReflect.Descriptor instead.
func (*SessionConfig) Descriptor() ([]byte, []int) {
return file_config_proto_rawDescGZIP(), []int{0}
return file_app_mydispatcher_config_proto_rawDescGZIP(), []int{0}
}
type Config struct {
@@ -69,7 +69,7 @@ type Config struct {
func (x *Config) Reset() {
*x = Config{}
if protoimpl.UnsafeEnabled {
mi := &file_config_proto_msgTypes[1]
mi := &file_app_mydispatcher_config_proto_msgTypes[1]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
@@ -82,7 +82,7 @@ func (x *Config) String() string {
func (*Config) ProtoMessage() {}
func (x *Config) ProtoReflect() protoreflect.Message {
mi := &file_config_proto_msgTypes[1]
mi := &file_app_mydispatcher_config_proto_msgTypes[1]
if protoimpl.UnsafeEnabled && x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil {
@@ -95,7 +95,7 @@ func (x *Config) ProtoReflect() protoreflect.Message {
// Deprecated: Use Config.ProtoReflect.Descriptor instead.
func (*Config) Descriptor() ([]byte, []int) {
return file_config_proto_rawDescGZIP(), []int{1}
return file_app_mydispatcher_config_proto_rawDescGZIP(), []int{1}
}
func (x *Config) GetSettings() *SessionConfig {
@@ -105,45 +105,46 @@ func (x *Config) GetSettings() *SessionConfig {
return nil
}
var File_config_proto protoreflect.FileDescriptor
var File_app_mydispatcher_config_proto protoreflect.FileDescriptor
var file_config_proto_rawDesc = []byte{
0x0a, 0x0c, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x12, 0x16,
0x78, 0x72, 0x61, 0x79, 0x72, 0x2e, 0x61, 0x70, 0x70, 0x2e, 0x6d, 0x79, 0x64, 0x69, 0x73, 0x70,
0x61, 0x74, 0x63, 0x68, 0x65, 0x72, 0x22, 0x15, 0x0a, 0x0d, 0x53, 0x65, 0x73, 0x73, 0x69, 0x6f,
0x6e, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x4a, 0x04, 0x08, 0x01, 0x10, 0x02, 0x22, 0x4b, 0x0a,
0x06, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x12, 0x41, 0x0a, 0x08, 0x73, 0x65, 0x74, 0x74, 0x69,
0x6e, 0x67, 0x73, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x25, 0x2e, 0x78, 0x72, 0x61, 0x79,
0x72, 0x2e, 0x61, 0x70, 0x70, 0x2e, 0x6d, 0x79, 0x64, 0x69, 0x73, 0x70, 0x61, 0x74, 0x63, 0x68,
0x65, 0x72, 0x2e, 0x53, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67,
0x52, 0x08, 0x73, 0x65, 0x74, 0x74, 0x69, 0x6e, 0x67, 0x73, 0x42, 0x67, 0x0a, 0x1a, 0x63, 0x6f,
0x6d, 0x2e, 0x78, 0x72, 0x61, 0x79, 0x72, 0x2e, 0x61, 0x70, 0x70, 0x2e, 0x6d, 0x79, 0x64, 0x69,
0x73, 0x70, 0x61, 0x74, 0x63, 0x68, 0x65, 0x72, 0x50, 0x01, 0x5a, 0x2f, 0x67, 0x69, 0x74, 0x68,
0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x58, 0x72, 0x61, 0x79, 0x52, 0x2d, 0x70, 0x72, 0x6f,
0x6a, 0x65, 0x63, 0x74, 0x2f, 0x58, 0x72, 0x61, 0x79, 0x52, 0x2f, 0x61, 0x70, 0x70, 0x2f, 0x6d,
0x79, 0x64, 0x69, 0x73, 0x70, 0x61, 0x74, 0x63, 0x68, 0x65, 0x72, 0xaa, 0x02, 0x15, 0x58, 0x72,
0x61, 0x79, 0x52, 0x2e, 0x41, 0x70, 0x70, 0x2e, 0x4d, 0x79, 0x69, 0x73, 0x70, 0x61, 0x74, 0x63,
0x68, 0x65, 0x72, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33,
var file_app_mydispatcher_config_proto_rawDesc = []byte{
0x0a, 0x1d, 0x61, 0x70, 0x70, 0x2f, 0x6d, 0x79, 0x64, 0x69, 0x73, 0x70, 0x61, 0x74, 0x63, 0x68,
0x65, 0x72, 0x2f, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x12,
0x16, 0x78, 0x72, 0x61, 0x79, 0x72, 0x2e, 0x61, 0x70, 0x70, 0x2e, 0x6d, 0x79, 0x64, 0x69, 0x73,
0x70, 0x61, 0x74, 0x63, 0x68, 0x65, 0x72, 0x22, 0x15, 0x0a, 0x0d, 0x53, 0x65, 0x73, 0x73, 0x69,
0x6f, 0x6e, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x4a, 0x04, 0x08, 0x01, 0x10, 0x02, 0x22, 0x4b,
0x0a, 0x06, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x12, 0x41, 0x0a, 0x08, 0x73, 0x65, 0x74, 0x74,
0x69, 0x6e, 0x67, 0x73, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x25, 0x2e, 0x78, 0x72, 0x61,
0x79, 0x72, 0x2e, 0x61, 0x70, 0x70, 0x2e, 0x6d, 0x79, 0x64, 0x69, 0x73, 0x70, 0x61, 0x74, 0x63,
0x68, 0x65, 0x72, 0x2e, 0x53, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x43, 0x6f, 0x6e, 0x66, 0x69,
0x67, 0x52, 0x08, 0x73, 0x65, 0x74, 0x74, 0x69, 0x6e, 0x67, 0x73, 0x42, 0x67, 0x0a, 0x1a, 0x63,
0x6f, 0x6d, 0x2e, 0x78, 0x72, 0x61, 0x79, 0x72, 0x2e, 0x61, 0x70, 0x70, 0x2e, 0x6d, 0x79, 0x64,
0x69, 0x73, 0x70, 0x61, 0x74, 0x63, 0x68, 0x65, 0x72, 0x50, 0x01, 0x5a, 0x2f, 0x67, 0x69, 0x74,
0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x58, 0x72, 0x61, 0x79, 0x52, 0x2d, 0x70, 0x72,
0x6f, 0x6a, 0x65, 0x63, 0x74, 0x2f, 0x58, 0x72, 0x61, 0x79, 0x52, 0x2f, 0x61, 0x70, 0x70, 0x2f,
0x6d, 0x79, 0x64, 0x69, 0x73, 0x70, 0x61, 0x74, 0x63, 0x68, 0x65, 0x72, 0xaa, 0x02, 0x15, 0x58,
0x72, 0x61, 0x79, 0x52, 0x2e, 0x41, 0x70, 0x70, 0x2e, 0x4d, 0x79, 0x69, 0x73, 0x70, 0x61, 0x74,
0x63, 0x68, 0x65, 0x72, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33,
}
var (
file_config_proto_rawDescOnce sync.Once
file_config_proto_rawDescData = file_config_proto_rawDesc
file_app_mydispatcher_config_proto_rawDescOnce sync.Once
file_app_mydispatcher_config_proto_rawDescData = file_app_mydispatcher_config_proto_rawDesc
)
func file_config_proto_rawDescGZIP() []byte {
file_config_proto_rawDescOnce.Do(func() {
file_config_proto_rawDescData = protoimpl.X.CompressGZIP(file_config_proto_rawDescData)
func file_app_mydispatcher_config_proto_rawDescGZIP() []byte {
file_app_mydispatcher_config_proto_rawDescOnce.Do(func() {
file_app_mydispatcher_config_proto_rawDescData = protoimpl.X.CompressGZIP(file_app_mydispatcher_config_proto_rawDescData)
})
return file_config_proto_rawDescData
return file_app_mydispatcher_config_proto_rawDescData
}
var file_config_proto_msgTypes = make([]protoimpl.MessageInfo, 2)
var file_config_proto_goTypes = []interface{}{
var file_app_mydispatcher_config_proto_msgTypes = make([]protoimpl.MessageInfo, 2)
var file_app_mydispatcher_config_proto_goTypes = []interface{}{
(*SessionConfig)(nil), // 0: xrayr.app.mydispatcher.SessionConfig
(*Config)(nil), // 1: xrayr.app.mydispatcher.Config
}
var file_config_proto_depIdxs = []int32{
var file_app_mydispatcher_config_proto_depIdxs = []int32{
0, // 0: xrayr.app.mydispatcher.Config.settings:type_name -> xrayr.app.mydispatcher.SessionConfig
1, // [1:1] is the sub-list for method output_type
1, // [1:1] is the sub-list for method input_type
@@ -152,13 +153,13 @@ var file_config_proto_depIdxs = []int32{
0, // [0:1] is the sub-list for field type_name
}
func init() { file_config_proto_init() }
func file_config_proto_init() {
if File_config_proto != nil {
func init() { file_app_mydispatcher_config_proto_init() }
func file_app_mydispatcher_config_proto_init() {
if File_app_mydispatcher_config_proto != nil {
return
}
if !protoimpl.UnsafeEnabled {
file_config_proto_msgTypes[0].Exporter = func(v interface{}, i int) interface{} {
file_app_mydispatcher_config_proto_msgTypes[0].Exporter = func(v interface{}, i int) interface{} {
switch v := v.(*SessionConfig); i {
case 0:
return &v.state
@@ -170,7 +171,7 @@ func file_config_proto_init() {
return nil
}
}
file_config_proto_msgTypes[1].Exporter = func(v interface{}, i int) interface{} {
file_app_mydispatcher_config_proto_msgTypes[1].Exporter = func(v interface{}, i int) interface{} {
switch v := v.(*Config); i {
case 0:
return &v.state
@@ -187,18 +188,18 @@ func file_config_proto_init() {
out := protoimpl.TypeBuilder{
File: protoimpl.DescBuilder{
GoPackagePath: reflect.TypeOf(x{}).PkgPath(),
RawDescriptor: file_config_proto_rawDesc,
RawDescriptor: file_app_mydispatcher_config_proto_rawDesc,
NumEnums: 0,
NumMessages: 2,
NumExtensions: 0,
NumServices: 0,
},
GoTypes: file_config_proto_goTypes,
DependencyIndexes: file_config_proto_depIdxs,
MessageInfos: file_config_proto_msgTypes,
GoTypes: file_app_mydispatcher_config_proto_goTypes,
DependencyIndexes: file_app_mydispatcher_config_proto_depIdxs,
MessageInfos: file_app_mydispatcher_config_proto_msgTypes,
}.Build()
File_config_proto = out.File
file_config_proto_rawDesc = nil
file_config_proto_goTypes = nil
file_config_proto_depIdxs = nil
File_app_mydispatcher_config_proto = out.File
file_app_mydispatcher_config_proto_rawDesc = nil
file_app_mydispatcher_config_proto_goTypes = nil
file_app_mydispatcher_config_proto_depIdxs = nil
}

View File

@@ -1,7 +1,7 @@
syntax = "proto3";
package xrayr.app.mydispatcher;
option csharp_namespace = "XrayR.App.Myispatcher";
option csharp_namespace = "XrayR.App.Mydispatcher";
option go_package = "github.com/XrayR-project/XrayR/app/mydispatcher";
option java_package = "com.xrayr.app.mydispatcher";
option java_multiple_files = true;

View File

@@ -9,8 +9,6 @@ import (
"sync"
"time"
"github.com/XrayR-project/XrayR/common/limiter"
"github.com/XrayR-project/XrayR/common/rule"
"github.com/xtls/xray-core/common"
"github.com/xtls/xray-core/common/buf"
"github.com/xtls/xray-core/common/log"
@@ -22,10 +20,13 @@ import (
"github.com/xtls/xray-core/features/outbound"
"github.com/xtls/xray-core/features/policy"
"github.com/xtls/xray-core/features/routing"
routing_session "github.com/xtls/xray-core/features/routing/session"
routingSession "github.com/xtls/xray-core/features/routing/session"
"github.com/xtls/xray-core/features/stats"
"github.com/xtls/xray-core/transport"
"github.com/xtls/xray-core/transport/pipe"
"github.com/XrayR-project/XrayR/common/limiter"
"github.com/XrayR-project/XrayR/common/rule"
)
var errSniffingTimeout = newError("timeout on sniffing")
@@ -95,15 +96,19 @@ type DefaultDispatcher struct {
router routing.Router
policy policy.Manager
stats stats.Manager
hosts dns.HostsLookup
dns dns.Client
fdns dns.FakeDNSEngine
Limiter *limiter.Limiter
RuleManager *rule.RuleManager
RuleManager *rule.Manager
}
func init() {
common.Must(common.RegisterConfig((*Config)(nil), func(ctx context.Context, config interface{}) (interface{}, error) {
d := new(DefaultDispatcher)
if err := core.RequireFeatures(ctx, func(om outbound.Manager, router routing.Router, pm policy.Manager, sm stats.Manager, dc dns.Client) error {
core.RequireFeatures(ctx, func(fdns dns.FakeDNSEngine) {
d.fdns = fdns
})
return d.Init(config.(*Config), om, router, pm, sm, dc)
}); err != nil {
return nil, err
@@ -113,16 +118,14 @@ func init() {
}
// Init initializes DefaultDispatcher.
func (d *DefaultDispatcher) Init(config *Config, om outbound.Manager, router routing.Router, pm policy.Manager, sm stats.Manager, dc dns.Client) error {
func (d *DefaultDispatcher) Init(config *Config, om outbound.Manager, router routing.Router, pm policy.Manager, sm stats.Manager, dns dns.Client) error {
d.ohm = om
d.router = router
d.policy = pm
d.stats = sm
d.Limiter = limiter.New()
d.RuleManager = rule.New()
if hosts, ok := dc.(dns.HostsLookup); ok {
d.hosts = hosts
}
d.dns = dns
return nil
}
@@ -137,9 +140,11 @@ func (*DefaultDispatcher) Start() error {
}
// Close implements common.Closable.
func (*DefaultDispatcher) Close() error { return nil }
func (*DefaultDispatcher) Close() error {
return nil
}
func (d *DefaultDispatcher) getLink(ctx context.Context) (*transport.Link, *transport.Link, error) {
func (d *DefaultDispatcher) getLink(ctx context.Context, network net.Network, sniffing session.SniffingRequest) (*transport.Link, *transport.Link, error) {
opt := pipe.OptionsFromContext(ctx)
uplinkReader, uplinkWriter := pipe.New(opt...)
downlinkReader, downlinkWriter := pipe.New(opt...)
@@ -164,7 +169,7 @@ func (d *DefaultDispatcher) getLink(ctx context.Context) (*transport.Link, *tran
// Speed Limit and Device Limit
bucket, ok, reject := d.Limiter.GetUserBucket(sessionInbound.Tag, user.Email, sessionInbound.Source.Address.IP().String())
if reject {
newError("Devices reach the limit: ", user.Email).AtError().WriteToLog()
newError("Devices reach the limit: ", user.Email).AtWarning().WriteToLog()
common.Close(outboundLink.Writer)
common.Close(inboundLink.Writer)
common.Interrupt(outboundLink.Reader)
@@ -175,6 +180,7 @@ func (d *DefaultDispatcher) getLink(ctx context.Context) (*transport.Link, *tran
inboundLink.Writer = d.Limiter.RateWriter(inboundLink.Writer, bucket)
outboundLink.Writer = d.Limiter.RateWriter(outboundLink.Writer, bucket)
}
p := d.policy.ForLevel(user.Level)
if p.Stats.UserUplink {
name := "user>>>" + user.Email + ">>>traffic>>>uplink"
@@ -199,17 +205,13 @@ func (d *DefaultDispatcher) getLink(ctx context.Context) (*transport.Link, *tran
return inboundLink, outboundLink, nil
}
func shouldOverride(ctx context.Context, result SniffResult, request session.SniffingRequest, destination net.Destination) bool {
func (d *DefaultDispatcher) shouldOverride(ctx context.Context, result SniffResult, request session.SniffingRequest, destination net.Destination) bool {
domain := result.Domain()
for _, d := range request.ExcludeForDomain {
if strings.ToLower(domain) == d {
return false
}
}
var fakeDNSEngine dns.FakeDNSEngine
core.RequireFeatures(ctx, func(fdns dns.FakeDNSEngine) {
fakeDNSEngine = fdns
})
protocolString := result.Protocol()
if resComp, ok := result.(SnifferResultComposite); ok {
protocolString = resComp.ProtocolForDomainResult()
@@ -218,7 +220,7 @@ func shouldOverride(ctx context.Context, result SniffResult, request session.Sni
if strings.HasPrefix(protocolString, p) {
return true
}
if fkr0, ok := fakeDNSEngine.(dns.FakeDNSEngineRev0); ok && protocolString != "bittorrent" && p == "fakedns" &&
if fkr0, ok := d.fdns.(dns.FakeDNSEngineRev0); ok && protocolString != "bittorrent" && p == "fakedns" &&
destination.Address.Family().IsIP() && fkr0.IsIPInIPPool(destination.Address) {
newError("Using sniffer ", protocolString, " since the fake DNS missed").WriteToLog(session.ExportIDToError(ctx))
return true
@@ -242,48 +244,30 @@ func (d *DefaultDispatcher) Dispatch(ctx context.Context, destination net.Destin
Target: destination,
}
ctx = session.ContextWithOutbound(ctx, ob)
inbound, outbound, err := d.getLink(ctx)
if err != nil {
return nil, err
}
content := session.ContentFromContext(ctx)
if content == nil {
content = new(session.Content)
ctx = session.ContextWithContent(ctx, content)
}
sniffingRequest := content.SniffingRequest
switch {
case !sniffingRequest.Enabled:
inbound, outbound, err := d.getLink(ctx, destination.Network, sniffingRequest)
if err != nil {
return nil, err
}
if !sniffingRequest.Enabled {
go d.routedDispatch(ctx, outbound, destination)
case destination.Network != net.Network_TCP:
// Only metadata sniff will be used for non tcp connection
result, err := sniffer(ctx, nil, true)
if err == nil {
content.Protocol = result.Protocol()
if shouldOverride(ctx, result, sniffingRequest, destination) {
domain := result.Domain()
newError("sniffed domain: ", domain).WriteToLog(session.ExportIDToError(ctx))
destination.Address = net.ParseAddress(domain)
if sniffingRequest.RouteOnly && result.Protocol() != "fakedns" {
ob.RouteTarget = destination
} else {
ob.Target = destination
}
}
}
go d.routedDispatch(ctx, outbound, destination)
default:
} else {
go func() {
cReader := &cachedReader{
reader: outbound.Reader.(*pipe.Reader),
}
outbound.Reader = cReader
result, err := sniffer(ctx, cReader, sniffingRequest.MetadataOnly)
result, err := sniffer(ctx, cReader, sniffingRequest.MetadataOnly, destination.Network)
if err == nil {
content.Protocol = result.Protocol()
}
if err == nil && shouldOverride(ctx, result, sniffingRequest, destination) {
if err == nil && d.shouldOverride(ctx, result, sniffingRequest, destination) {
domain := result.Domain()
newError("sniffed domain: ", domain).WriteToLog(session.ExportIDToError(ctx))
destination.Address = net.ParseAddress(domain)
@@ -314,37 +298,19 @@ func (d *DefaultDispatcher) DispatchLink(ctx context.Context, destination net.De
ctx = session.ContextWithContent(ctx, content)
}
sniffingRequest := content.SniffingRequest
switch {
case !sniffingRequest.Enabled:
if !sniffingRequest.Enabled {
go d.routedDispatch(ctx, outbound, destination)
case destination.Network != net.Network_TCP:
// Only metadata sniff will be used for non tcp connection
result, err := sniffer(ctx, nil, true)
if err == nil {
content.Protocol = result.Protocol()
if shouldOverride(ctx, result, sniffingRequest, destination) {
domain := result.Domain()
newError("sniffed domain: ", domain).WriteToLog(session.ExportIDToError(ctx))
destination.Address = net.ParseAddress(domain)
if sniffingRequest.RouteOnly && result.Protocol() != "fakedns" {
ob.RouteTarget = destination
} else {
ob.Target = destination
}
}
}
go d.routedDispatch(ctx, outbound, destination)
default:
} else {
go func() {
cReader := &cachedReader{
reader: outbound.Reader.(*pipe.Reader),
}
outbound.Reader = cReader
result, err := sniffer(ctx, cReader, sniffingRequest.MetadataOnly)
result, err := sniffer(ctx, cReader, sniffingRequest.MetadataOnly, destination.Network)
if err == nil {
content.Protocol = result.Protocol()
}
if err == nil && shouldOverride(ctx, result, sniffingRequest, destination) {
if err == nil && d.shouldOverride(ctx, result, sniffingRequest, destination) {
domain := result.Domain()
newError("sniffed domain: ", domain).WriteToLog(session.ExportIDToError(ctx))
destination.Address = net.ParseAddress(domain)
@@ -357,10 +323,11 @@ func (d *DefaultDispatcher) DispatchLink(ctx context.Context, destination net.De
d.routedDispatch(ctx, outbound, destination)
}()
}
return nil
}
func sniffer(ctx context.Context, cReader *cachedReader, metadataOnly bool) (SniffResult, error) {
func sniffer(ctx context.Context, cReader *cachedReader, metadataOnly bool, network net.Network) (SniffResult, error) {
payload := buf.New()
defer payload.Release()
@@ -386,7 +353,7 @@ func sniffer(ctx context.Context, cReader *cachedReader, metadataOnly bool) (Sni
cReader.Cache(payload)
if !payload.IsEmpty() {
result, err := sniffer.Sniff(ctx, payload.Bytes())
result, err := sniffer.Sniff(ctx, payload.Bytes(), network)
if err != common.ErrNoClue {
return result, err
}
@@ -408,8 +375,8 @@ func sniffer(ctx context.Context, cReader *cachedReader, metadataOnly bool) (Sni
func (d *DefaultDispatcher) routedDispatch(ctx context.Context, link *transport.Link, destination net.Destination) {
ob := session.OutboundFromContext(ctx)
if d.hosts != nil && destination.Address.Family().IsDomain() {
proxied := d.hosts.LookupHosts(ob.Target.String())
if hosts, ok := d.dns.(dns.HostsLookup); ok && destination.Address.Family().IsDomain() {
proxied := hosts.LookupHosts(ob.Target.String())
if proxied != nil {
ro := ob.RouteTarget == destination
destination.Address = *proxied
@@ -436,12 +403,13 @@ func (d *DefaultDispatcher) routedDispatch(ctx context.Context, link *transport.
}
}
routingLink := routing_session.AsRoutingContext(ctx)
routingLink := routingSession.AsRoutingContext(ctx)
inTag := routingLink.GetInboundTag()
isPickRoute := 0
if forcedOutboundTag := session.GetForcedOutboundTagFromContext(ctx); forcedOutboundTag != "" {
ctx = session.SetForcedOutboundTagToContext(ctx, "")
if h := d.ohm.GetHandler(forcedOutboundTag); h != nil {
isPickRoute = 1
newError("taking platform initialized detour [", forcedOutboundTag, "] for [", destination, "]").WriteToLog(session.ExportIDToError(ctx))
handler = h
} else {
@@ -451,13 +419,14 @@ func (d *DefaultDispatcher) routedDispatch(ctx context.Context, link *transport.
return
}
} else if d.router != nil {
if route, err := d.router.PickRoute(routing_session.AsRoutingContext(ctx)); err == nil {
tag := route.GetOutboundTag()
if h := d.ohm.GetHandler(tag); h != nil {
newError("taking detour [", tag, "] for [", destination, "]").WriteToLog(session.ExportIDToError(ctx))
if route, err := d.router.PickRoute(routingLink); err == nil {
outTag := route.GetOutboundTag()
if h := d.ohm.GetHandler(outTag); h != nil {
isPickRoute = 2
newError("taking detour [", outTag, "] for [", destination, "]").WriteToLog(session.ExportIDToError(ctx))
handler = h
} else {
newError("non existing outTag: ", tag).AtWarning().WriteToLog(session.ExportIDToError(ctx))
newError("non existing outTag: ", outTag).AtWarning().WriteToLog(session.ExportIDToError(ctx))
}
} else {
newError("default route for ", destination).WriteToLog(session.ExportIDToError(ctx))
@@ -465,7 +434,7 @@ func (d *DefaultDispatcher) routedDispatch(ctx context.Context, link *transport.
}
if handler == nil {
handler = d.ohm.GetHandler(inTag) // Default outbound hander tag should be as same as the inbound tag
handler = d.ohm.GetHandler(inTag) // Default outbound handler tag should be as same as the inbound tag
}
// If there is no outbound with tag as same as the inbound tag
@@ -482,7 +451,15 @@ func (d *DefaultDispatcher) routedDispatch(ctx context.Context, link *transport.
if accessMessage := log.AccessMessageFromContext(ctx); accessMessage != nil {
if tag := handler.Tag(); tag != "" {
accessMessage.Detour = tag
if inTag == "" {
accessMessage.Detour = tag
} else if isPickRoute == 1 {
accessMessage.Detour = inTag + " ==> " + tag
} else if isPickRoute == 2 {
accessMessage.Detour = inTag + " -> " + tag
} else {
accessMessage.Detour = inTag + " >> " + tag
}
}
log.Record(accessMessage)
}

View File

@@ -1,4 +1,4 @@
// Package dispather implement the rate limiter and the onlie device counter
// Package mydispatcher Package dispatcher implement the rate limiter and the online device counter
package mydispatcher
//go:generate go run github.com/xtls/xray-core/common/errors/errorgen

View File

@@ -2,8 +2,6 @@ package mydispatcher
import "github.com/xtls/xray-core/common/errors"
type errPathObjHolder struct{}
func newError(values ...interface{}) *errors.Error {
return errors.New(values...).WithPathObj(errPathObjHolder{})
return errors.New(values...)
}

View File

@@ -14,12 +14,13 @@ import (
// newFakeDNSSniffer Create a Fake DNS metadata sniffer
func newFakeDNSSniffer(ctx context.Context) (protocolSnifferWithMetadata, error) {
var fakeDNSEngine dns.FakeDNSEngine
err := core.RequireFeatures(ctx, func(fdns dns.FakeDNSEngine) {
fakeDNSEngine = fdns
})
if err != nil {
return protocolSnifferWithMetadata{}, err
{
fakeDNSEngineFeat := core.MustFromContext(ctx).GetFeature((*dns.FakeDNSEngine)(nil))
if fakeDNSEngineFeat != nil {
fakeDNSEngine = fakeDNSEngineFeat.(dns.FakeDNSEngine)
}
}
if fakeDNSEngine == nil {
errNotInit := newError("FakeDNSEngine is not initialized, but such a sniffer is used").AtError()
return protocolSnifferWithMetadata{}, errNotInit

View File

@@ -4,8 +4,10 @@ import (
"context"
"github.com/xtls/xray-core/common"
"github.com/xtls/xray-core/common/net"
"github.com/xtls/xray-core/common/protocol/bittorrent"
"github.com/xtls/xray-core/common/protocol/http"
"github.com/xtls/xray-core/common/protocol/quic"
"github.com/xtls/xray-core/common/protocol/tls"
)
@@ -22,6 +24,7 @@ type protocolSnifferWithMetadata struct {
// for both TCP and UDP connections
// It will not be shown as a traffic type for routing unless there is no other successful sniffing.
metadataSniffer bool
network net.Network
}
type Sniffer struct {
@@ -31,9 +34,11 @@ type Sniffer struct {
func NewSniffer(ctx context.Context) *Sniffer {
ret := &Sniffer{
sniffer: []protocolSnifferWithMetadata{
{func(c context.Context, b []byte) (SniffResult, error) { return http.SniffHTTP(b) }, false},
{func(c context.Context, b []byte) (SniffResult, error) { return tls.SniffTLS(b) }, false},
{func(c context.Context, b []byte) (SniffResult, error) { return bittorrent.SniffBittorrent(b) }, false},
{func(c context.Context, b []byte) (SniffResult, error) { return http.SniffHTTP(b) }, false, net.Network_TCP},
{func(c context.Context, b []byte) (SniffResult, error) { return tls.SniffTLS(b) }, false, net.Network_TCP},
{func(c context.Context, b []byte) (SniffResult, error) { return bittorrent.SniffBittorrent(b) }, false, net.Network_TCP},
{func(c context.Context, b []byte) (SniffResult, error) { return quic.SniffQUIC(b) }, false, net.Network_UDP},
{func(c context.Context, b []byte) (SniffResult, error) { return bittorrent.SniffUTP(b) }, false, net.Network_UDP},
},
}
if sniffer, err := newFakeDNSSniffer(ctx); err == nil {
@@ -49,11 +54,11 @@ func NewSniffer(ctx context.Context) *Sniffer {
var errUnknownContent = newError("unknown content")
func (s *Sniffer) Sniff(c context.Context, payload []byte) (SniffResult, error) {
func (s *Sniffer) Sniff(c context.Context, payload []byte, network net.Network) (SniffResult, error) {
var pendingSniffer []protocolSnifferWithMetadata
for _, si := range s.sniffer {
s := si.protocolSniffer
if si.metadataSniffer {
if si.metadataSniffer || si.network != network {
continue
}
result, err := s(c, payload)

View File

@@ -1,14 +0,0 @@
package cmd
import "github.com/urfave/cli"
// CreateCommands Creates all CLI commands.
func CreateCommands() []cli.Command {
return []cli.Command{
createRun(),
createRevoke(),
createRenew(),
createDNSHelp(),
createList(),
}
}

View File

@@ -1,23 +0,0 @@
package cmd
import (
"github.com/XrayR-project/XrayR/common/legocmd/log"
"github.com/urfave/cli"
)
func Before(ctx *cli.Context) error {
if ctx.GlobalString("path") == "" {
log.Panic("Could not determine current working directory. Please pass --path.")
}
err := createNonExistingFolder(ctx.GlobalString("path"))
if err != nil {
log.Panicf("Could not check/create path: %v", err)
}
if ctx.GlobalString("server") == "" {
log.Panic("Could not determine current working server. Please pass --server.")
}
return nil
}

View File

@@ -1,73 +0,0 @@
package cmd
import (
"fmt"
"io"
"os"
"strings"
"text/tabwriter"
"github.com/urfave/cli"
)
func createDNSHelp() cli.Command {
return cli.Command{
Name: "dnshelp",
Usage: "Shows additional help for the '--dns' global option",
Action: dnsHelp,
Flags: []cli.Flag{
cli.StringFlag{
Name: "code, c",
Usage: fmt.Sprintf("DNS code: %s", allDNSCodes()),
},
},
}
}
func dnsHelp(ctx *cli.Context) error {
code := ctx.String("code")
if code == "" {
w := tabwriter.NewWriter(os.Stdout, 0, 0, 2, ' ', 0)
ew := &errWriter{w: w}
ew.writeln(`Credentials for DNS providers must be passed through environment variables.`)
ew.writeln()
ew.writeln(`To display the documentation for a DNS providers:`)
ew.writeln()
ew.writeln("\t$ lego dnshelp -c code")
ew.writeln()
ew.writeln("All DNS codes:")
ew.writef("\t%s\n", allDNSCodes())
ew.writeln()
ew.writeln("More information: https://go-acme.github.io/lego/dns")
if ew.err != nil {
return ew.err
}
return w.Flush()
}
return displayDNSHelp(strings.ToLower(code))
}
type errWriter struct {
w io.Writer
err error
}
func (ew *errWriter) writeln(a ...interface{}) {
if ew.err != nil {
return
}
_, ew.err = fmt.Fprintln(ew.w, a...)
}
func (ew *errWriter) writef(format string, a ...interface{}) {
if ew.err != nil {
return
}
_, ew.err = fmt.Fprintf(ew.w, format, a...)
}

View File

@@ -1,136 +0,0 @@
package cmd
import (
"encoding/json"
"fmt"
"io/ioutil"
"net/url"
"path/filepath"
"strings"
"github.com/go-acme/lego/v4/certcrypto"
"github.com/urfave/cli"
)
func createList() cli.Command {
return cli.Command{
Name: "list",
Usage: "Display certificates and accounts information.",
Action: list,
Flags: []cli.Flag{
cli.BoolFlag{
Name: "accounts, a",
Usage: "Display accounts.",
},
cli.BoolFlag{
Name: "names, n",
Usage: "Display certificate common names only.",
},
},
}
}
func list(ctx *cli.Context) error {
if ctx.Bool("accounts") && !ctx.Bool("names") {
if err := listAccount(ctx); err != nil {
return err
}
}
return listCertificates(ctx)
}
func listCertificates(ctx *cli.Context) error {
certsStorage := NewCertificatesStorage(ctx)
matches, err := filepath.Glob(filepath.Join(certsStorage.GetRootPath(), "*.crt"))
if err != nil {
return err
}
names := ctx.Bool("names")
if len(matches) == 0 {
if !names {
fmt.Println("No certificates found.")
}
return nil
}
if !names {
fmt.Println("Found the following certs:")
}
for _, filename := range matches {
if strings.HasSuffix(filename, ".issuer.crt") {
continue
}
data, err := ioutil.ReadFile(filename)
if err != nil {
return err
}
pCert, err := certcrypto.ParsePEMCertificate(data)
if err != nil {
return err
}
if names {
fmt.Println(pCert.Subject.CommonName)
} else {
fmt.Println(" Certificate Name:", pCert.Subject.CommonName)
fmt.Println(" Domains:", strings.Join(pCert.DNSNames, ", "))
fmt.Println(" Expiry Date:", pCert.NotAfter)
fmt.Println(" Certificate Path:", filename)
fmt.Println()
}
}
return nil
}
func listAccount(ctx *cli.Context) error {
// fake email, needed by NewAccountsStorage
if err := ctx.GlobalSet("email", "unknown"); err != nil {
return err
}
accountsStorage := NewAccountsStorage(ctx)
matches, err := filepath.Glob(filepath.Join(accountsStorage.GetRootPath(), "*", "*", "*.json"))
if err != nil {
return err
}
if len(matches) == 0 {
fmt.Println("No accounts found.")
return nil
}
fmt.Println("Found the following accounts:")
for _, filename := range matches {
data, err := ioutil.ReadFile(filename)
if err != nil {
return err
}
var account Account
err = json.Unmarshal(data, &account)
if err != nil {
return err
}
uri, err := url.Parse(account.Registration.URI)
if err != nil {
return err
}
fmt.Println(" Email:", account.Email)
fmt.Println(" Server:", uri.Host)
fmt.Println(" Path:", filepath.Dir(filename))
fmt.Println()
}
return nil
}

View File

@@ -1,225 +0,0 @@
package cmd
import (
"crypto"
"crypto/x509"
"time"
"github.com/XrayR-project/XrayR/common/legocmd/log"
"github.com/go-acme/lego/v4/certcrypto"
"github.com/go-acme/lego/v4/certificate"
"github.com/go-acme/lego/v4/lego"
"github.com/urfave/cli"
)
const (
renewEnvAccountEmail = "LEGO_ACCOUNT_EMAIL"
renewEnvCertDomain = "LEGO_CERT_DOMAIN"
renewEnvCertPath = "LEGO_CERT_PATH"
renewEnvCertKeyPath = "LEGO_CERT_KEY_PATH"
)
func createRenew() cli.Command {
return cli.Command{
Name: "renew",
Usage: "Renew a certificate",
Action: renew,
Before: func(ctx *cli.Context) error {
// we require either domains or csr, but not both
hasDomains := len(ctx.GlobalStringSlice("domains")) > 0
hasCsr := len(ctx.GlobalString("csr")) > 0
if hasDomains && hasCsr {
log.Panic("Please specify either --domains/-d or --csr/-c, but not both")
}
if !hasDomains && !hasCsr {
log.Panic("Please specify --domains/-d (or --csr/-c if you already have a CSR)")
}
return nil
},
Flags: []cli.Flag{
cli.IntFlag{
Name: "days",
Value: 30,
Usage: "The number of days left on a certificate to renew it.",
},
cli.BoolFlag{
Name: "reuse-key",
Usage: "Used to indicate you want to reuse your current private key for the new certificate.",
},
cli.BoolFlag{
Name: "no-bundle",
Usage: "Do not create a certificate bundle by adding the issuers certificate to the new certificate.",
},
cli.BoolFlag{
Name: "must-staple",
Usage: "Include the OCSP must staple TLS extension in the CSR and generated certificate. Only works if the CSR is generated by lego.",
},
cli.StringFlag{
Name: "renew-hook",
Usage: "Define a hook. The hook is executed only when the certificates are effectively renewed.",
},
cli.StringFlag{
Name: "preferred-chain",
Usage: "If the CA offers multiple certificate chains, prefer the chain with an issuer matching this Subject Common Name. If no match, the default offered chain will be used.",
},
},
}
}
func renew(ctx *cli.Context) error {
account, client := setup(ctx, NewAccountsStorage(ctx))
setupChallenges(ctx, client)
if account.Registration == nil {
log.Panicf("Account %s is not registered. Use 'run' to register a new account.\n", account.Email)
}
certsStorage := NewCertificatesStorage(ctx)
bundle := !ctx.Bool("no-bundle")
meta := map[string]string{renewEnvAccountEmail: account.Email}
// CSR
if ctx.GlobalIsSet("csr") {
return renewForCSR(ctx, client, certsStorage, bundle, meta)
}
// Domains
return renewForDomains(ctx, client, certsStorage, bundle, meta)
}
func renewForDomains(ctx *cli.Context, client *lego.Client, certsStorage *CertificatesStorage, bundle bool, meta map[string]string) error {
domains := ctx.GlobalStringSlice("domains")
domain := domains[0]
// load the cert resource from files.
// We store the certificate, private key and metadata in different files
// as web servers would not be able to work with a combined file.
certificates, err := certsStorage.ReadCertificate(domain, ".crt")
if err != nil {
log.Panicf("Error while loading the certificate for domain %s\n\t%v", domain, err)
}
cert := certificates[0]
if !needRenewal(cert, domain, ctx.Int("days")) {
return nil
}
// This is just meant to be informal for the user.
timeLeft := cert.NotAfter.Sub(time.Now().UTC())
log.Infof("[%s] acme: Trying renewal with %d hours remaining", domain, int(timeLeft.Hours()))
certDomains := certcrypto.ExtractDomains(cert)
var privateKey crypto.PrivateKey
if ctx.Bool("reuse-key") {
keyBytes, errR := certsStorage.ReadFile(domain, ".key")
if errR != nil {
log.Panicf("Error while loading the private key for domain %s\n\t%v", domain, errR)
}
privateKey, errR = certcrypto.ParsePEMPrivateKey(keyBytes)
if errR != nil {
return errR
}
}
request := certificate.ObtainRequest{
Domains: merge(certDomains, domains),
Bundle: bundle,
PrivateKey: privateKey,
MustStaple: ctx.Bool("must-staple"),
PreferredChain: ctx.String("preferred-chain"),
}
certRes, err := client.Certificate.Obtain(request)
if err != nil {
log.Panic(err)
}
certsStorage.SaveResource(certRes)
meta[renewEnvCertDomain] = domain
meta[renewEnvCertPath] = certsStorage.GetFileName(domain, ".crt")
meta[renewEnvCertKeyPath] = certsStorage.GetFileName(domain, ".key")
return launchHook(ctx.String("renew-hook"), meta)
}
func renewForCSR(ctx *cli.Context, client *lego.Client, certsStorage *CertificatesStorage, bundle bool, meta map[string]string) error {
csr, err := readCSRFile(ctx.GlobalString("csr"))
if err != nil {
log.Panic(err)
}
domain := csr.Subject.CommonName
// load the cert resource from files.
// We store the certificate, private key and metadata in different files
// as web servers would not be able to work with a combined file.
certificates, err := certsStorage.ReadCertificate(domain, ".crt")
if err != nil {
log.Panicf("Error while loading the certificate for domain %s\n\t%v", domain, err)
}
cert := certificates[0]
if !needRenewal(cert, domain, ctx.Int("days")) {
return nil
}
// This is just meant to be informal for the user.
timeLeft := cert.NotAfter.Sub(time.Now().UTC())
log.Infof("[%s] acme: Trying renewal with %d hours remaining", domain, int(timeLeft.Hours()))
certRes, err := client.Certificate.ObtainForCSR(certificate.ObtainForCSRRequest{
CSR: csr,
Bundle: bundle,
PreferredChain: ctx.String("preferred-chain"),
})
if err != nil {
log.Panic(err)
}
certsStorage.SaveResource(certRes)
meta[renewEnvCertDomain] = domain
meta[renewEnvCertPath] = certsStorage.GetFileName(domain, ".crt")
meta[renewEnvCertKeyPath] = certsStorage.GetFileName(domain, ".key")
return launchHook(ctx.String("renew-hook"), meta)
}
func needRenewal(x509Cert *x509.Certificate, domain string, days int) bool {
if x509Cert.IsCA {
log.Panicf("[%s] Certificate bundle starts with a CA certificate", domain)
}
if days >= 0 {
notAfter := int(time.Until(x509Cert.NotAfter).Hours() / 24.0)
if notAfter > days {
log.Printf("[%s] The certificate expires in %d days, the number of days defined to perform the renewal is %d: no renewal.",
domain, notAfter, days)
return false
}
}
return true
}
func merge(prevDomains, nextDomains []string) []string {
for _, next := range nextDomains {
var found bool
for _, prev := range prevDomains {
if prev == next {
found = true
break
}
}
if !found {
prevDomains = append(prevDomains, next)
}
}
return prevDomains
}

View File

@@ -1,62 +0,0 @@
package cmd
import (
"github.com/XrayR-project/XrayR/common/legocmd/log"
"github.com/urfave/cli"
)
func createRevoke() cli.Command {
return cli.Command{
Name: "revoke",
Usage: "Revoke a certificate",
Action: revoke,
Flags: []cli.Flag{
cli.BoolFlag{
Name: "keep, k",
Usage: "Keep the certificates after the revocation instead of archiving them.",
},
},
}
}
func revoke(ctx *cli.Context) error {
acc, client := setup(ctx, NewAccountsStorage(ctx))
if acc.Registration == nil {
log.Panicf("Account %s is not registered. Use 'run' to register a new account.\n", acc.Email)
}
certsStorage := NewCertificatesStorage(ctx)
certsStorage.CreateRootFolder()
for _, domain := range ctx.GlobalStringSlice("domains") {
log.Printf("Trying to revoke certificate for domain %s", domain)
certBytes, err := certsStorage.ReadFile(domain, ".crt")
if err != nil {
log.Panicf("Error while revoking the certificate for domain %s\n\t%v", domain, err)
}
err = client.Certificate.Revoke(certBytes)
if err != nil {
log.Panicf("Error while revoking the certificate for domain %s\n\t%v", domain, err)
}
log.Println("Certificate was revoked.")
if ctx.Bool("keep") {
return nil
}
certsStorage.CreateArchiveFolder()
err = certsStorage.MoveToArchive(domain)
if err != nil {
return err
}
log.Println("Certificate was archived for domain:", domain)
}
return nil
}

View File

@@ -1,186 +0,0 @@
package cmd
import (
"bufio"
"fmt"
"os"
"strings"
"github.com/XrayR-project/XrayR/common/legocmd/log"
"github.com/go-acme/lego/v4/certificate"
"github.com/go-acme/lego/v4/lego"
"github.com/go-acme/lego/v4/registration"
"github.com/urfave/cli"
)
func createRun() cli.Command {
return cli.Command{
Name: "run",
Usage: "Register an account, then create and install a certificate",
Before: func(ctx *cli.Context) error {
// we require either domains or csr, but not both
hasDomains := len(ctx.GlobalStringSlice("domains")) > 0
hasCsr := len(ctx.GlobalString("csr")) > 0
if hasDomains && hasCsr {
log.Panic("Please specify either --domains/-d or --csr/-c, but not both")
}
if !hasDomains && !hasCsr {
log.Panic("Please specify --domains/-d (or --csr/-c if you already have a CSR)")
}
return nil
},
Action: run,
Flags: []cli.Flag{
cli.BoolFlag{
Name: "no-bundle",
Usage: "Do not create a certificate bundle by adding the issuers certificate to the new certificate.",
},
cli.BoolFlag{
Name: "must-staple",
Usage: "Include the OCSP must staple TLS extension in the CSR and generated certificate. Only works if the CSR is generated by lego.",
},
cli.StringFlag{
Name: "run-hook",
Usage: "Define a hook. The hook is executed when the certificates are effectively created.",
},
cli.StringFlag{
Name: "preferred-chain",
Usage: "If the CA offers multiple certificate chains, prefer the chain with an issuer matching this Subject Common Name. If no match, the default offered chain will be used.",
},
},
}
}
const rootPathWarningMessage = `!!!! HEADS UP !!!!
Your account credentials have been saved in your Let's Encrypt
configuration directory at "%s".
You should make a secure backup of this folder now. This
configuration directory will also contain certificates and
private keys obtained from Let's Encrypt so making regular
backups of this folder is ideal.
`
func run(ctx *cli.Context) error {
accountsStorage := NewAccountsStorage(ctx)
account, client := setup(ctx, accountsStorage)
setupChallenges(ctx, client)
if account.Registration == nil {
reg, err := register(ctx, client)
if err != nil {
log.Panicf("Could not complete registration\n\t%v", err)
}
account.Registration = reg
if err = accountsStorage.Save(account); err != nil {
log.Panic(err)
}
fmt.Printf(rootPathWarningMessage, accountsStorage.GetRootPath())
}
certsStorage := NewCertificatesStorage(ctx)
certsStorage.CreateRootFolder()
cert, err := obtainCertificate(ctx, client)
if err != nil {
// Make sure to return a non-zero exit code if ObtainSANCertificate returned at least one error.
// Due to us not returning partial certificate we can just exit here instead of at the end.
log.Panicf("Could not obtain certificates:\n\t%v", err)
}
certsStorage.SaveResource(cert)
meta := map[string]string{
renewEnvAccountEmail: account.Email,
renewEnvCertDomain: cert.Domain,
renewEnvCertPath: certsStorage.GetFileName(cert.Domain, ".crt"),
renewEnvCertKeyPath: certsStorage.GetFileName(cert.Domain, ".key"),
}
return launchHook(ctx.String("run-hook"), meta)
}
func handleTOS(ctx *cli.Context, client *lego.Client) bool {
// Check for a global accept override
if ctx.GlobalBool("accept-tos") {
return true
}
reader := bufio.NewReader(os.Stdin)
log.Printf("Please review the TOS at %s", client.GetToSURL())
for {
fmt.Println("Do you accept the TOS? Y/n")
text, err := reader.ReadString('\n')
if err != nil {
log.Panicf("Could not read from console: %v", err)
}
text = strings.Trim(text, "\r\n")
switch text {
case "", "y", "Y":
return true
case "n", "N":
return false
default:
fmt.Println("Your input was invalid. Please answer with one of Y/y, n/N or by pressing enter.")
}
}
}
func register(ctx *cli.Context, client *lego.Client) (*registration.Resource, error) {
accepted := handleTOS(ctx, client)
if !accepted {
log.Panic("You did not accept the TOS. Unable to proceed.")
}
if ctx.GlobalBool("eab") {
kid := ctx.GlobalString("kid")
hmacEncoded := ctx.GlobalString("hmac")
if kid == "" || hmacEncoded == "" {
log.Panicf("Requires arguments --kid and --hmac.")
}
return client.Registration.RegisterWithExternalAccountBinding(registration.RegisterEABOptions{
TermsOfServiceAgreed: accepted,
Kid: kid,
HmacEncoded: hmacEncoded,
})
}
return client.Registration.Register(registration.RegisterOptions{TermsOfServiceAgreed: true})
}
func obtainCertificate(ctx *cli.Context, client *lego.Client) (*certificate.Resource, error) {
bundle := !ctx.Bool("no-bundle")
domains := ctx.GlobalStringSlice("domains")
if len(domains) > 0 {
// obtain a certificate, generating a new private key
request := certificate.ObtainRequest{
Domains: domains,
Bundle: bundle,
MustStaple: ctx.Bool("must-staple"),
PreferredChain: ctx.String("preferred-chain"),
}
return client.Certificate.Obtain(request)
}
// read the CSR
csr, err := readCSRFile(ctx.GlobalString("csr"))
if err != nil {
return nil, err
}
// obtain a certificate for this CSR
return client.Certificate.ObtainForCSR(certificate.ObtainForCSRRequest{
CSR: csr,
Bundle: bundle,
PreferredChain: ctx.String("preferred-chain"),
})
}

View File

@@ -1,120 +0,0 @@
package cmd
import (
"github.com/go-acme/lego/v4/lego"
"github.com/urfave/cli"
)
func CreateFlags(defaultPath string) []cli.Flag {
return []cli.Flag{
cli.StringSliceFlag{
Name: "domains, d",
Usage: "Add a domain to the process. Can be specified multiple times.",
},
cli.StringFlag{
Name: "server, s",
Usage: "CA hostname (and optionally :port). The server certificate must be trusted in order to avoid further modifications to the client.",
Value: lego.LEDirectoryProduction,
},
cli.BoolFlag{
Name: "accept-tos, a",
Usage: "By setting this flag to true you indicate that you accept the current Let's Encrypt terms of service.",
},
cli.StringFlag{
Name: "email, m",
Usage: "Email used for registration and recovery contact.",
},
cli.StringFlag{
Name: "csr, c",
Usage: "Certificate signing request filename, if an external CSR is to be used.",
},
cli.BoolFlag{
Name: "eab",
Usage: "Use External Account Binding for account registration. Requires --kid and --hmac.",
},
cli.StringFlag{
Name: "kid",
Usage: "Key identifier from External CA. Used for External Account Binding.",
},
cli.StringFlag{
Name: "hmac",
Usage: "MAC key from External CA. Should be in Base64 URL Encoding without padding format. Used for External Account Binding.",
},
cli.StringFlag{
Name: "key-type, k",
Value: "ec256",
Usage: "Key type to use for private keys. Supported: rsa2048, rsa4096, rsa8192, ec256, ec384.",
},
cli.StringFlag{
Name: "filename",
Usage: "(deprecated) Filename of the generated certificate.",
},
cli.StringFlag{
Name: "path",
EnvVar: "LEGO_PATH",
Usage: "Directory to use for storing the data.",
Value: defaultPath,
},
cli.BoolFlag{
Name: "http",
Usage: "Use the HTTP challenge to solve challenges. Can be mixed with other types of challenges.",
},
cli.StringFlag{
Name: "http.port",
Usage: "Set the port and interface to use for HTTP based challenges to listen on.Supported: interface:port or :port.",
Value: ":80",
},
cli.StringFlag{
Name: "http.proxy-header",
Usage: "Validate against this HTTP header when solving HTTP based challenges behind a reverse proxy.",
Value: "Host",
},
cli.StringFlag{
Name: "http.webroot",
Usage: "Set the webroot folder to use for HTTP based challenges to write directly in a file in .well-known/acme-challenge. This disables the built-in server and expects the given directory to be publicly served with access to .well-known/acme-challenge",
},
cli.StringSliceFlag{
Name: "http.memcached-host",
Usage: "Set the memcached host(s) to use for HTTP based challenges. Challenges will be written to all specified hosts.",
},
cli.BoolFlag{
Name: "tls",
Usage: "Use the TLS challenge to solve challenges. Can be mixed with other types of challenges.",
},
cli.StringFlag{
Name: "tls.port",
Usage: "Set the port and interface to use for TLS based challenges to listen on. Supported: interface:port or :port.",
Value: ":443",
},
cli.StringFlag{
Name: "dns",
Usage: "Solve a DNS challenge using the specified provider. Can be mixed with other types of challenges. Run 'lego dnshelp' for help on usage.",
},
cli.BoolFlag{
Name: "dns.disable-cp",
Usage: "By setting this flag to true, disables the need to wait the propagation of the TXT record to all authoritative name servers.",
},
cli.StringSliceFlag{
Name: "dns.resolvers",
Usage: "Set the resolvers to use for performing recursive DNS queries. Supported: host:port. The default is to use the system resolvers, or Google's DNS resolvers if the system's cannot be determined.",
},
cli.IntFlag{
Name: "http-timeout",
Usage: "Set the HTTP timeout value to a specific value in seconds.",
},
cli.IntFlag{
Name: "dns-timeout",
Usage: "Set the DNS timeout value to a specific value in seconds. Used only when performing authoritative name servers queries.",
Value: 10,
},
cli.BoolFlag{
Name: "pem",
Usage: "Generate a .pem file by concatenating the .key and .crt files together.",
},
cli.IntFlag{
Name: "cert.timeout",
Usage: "Set the certificate timeout value to a specific value in seconds. Only used when obtaining certificates.",
Value: 30,
},
}
}

View File

@@ -1,47 +0,0 @@
package cmd
import (
"context"
"errors"
"fmt"
"os"
"os/exec"
"strings"
"time"
)
func launchHook(hook string, meta map[string]string) error {
if hook == "" {
return nil
}
ctxCmd, cancel := context.WithTimeout(context.Background(), 120*time.Second)
defer cancel()
parts := strings.Fields(hook)
cmdCtx := exec.CommandContext(ctxCmd, parts[0], parts[1:]...)
cmdCtx.Env = append(os.Environ(), metaToEnv(meta)...)
output, err := cmdCtx.CombinedOutput()
if len(output) > 0 {
fmt.Println(string(output))
}
if errors.Is(ctxCmd.Err(), context.DeadlineExceeded) {
return errors.New("hook timed out")
}
return err
}
func metaToEnv(meta map[string]string) []string {
var envs []string
for k, v := range meta {
envs = append(envs, k+"="+v)
}
return envs
}

View File

@@ -1,129 +0,0 @@
package cmd
import (
"crypto/x509"
"encoding/pem"
"fmt"
"io/ioutil"
"os"
"strings"
"time"
"github.com/XrayR-project/XrayR/common/legocmd/log"
"github.com/go-acme/lego/v4/certcrypto"
"github.com/go-acme/lego/v4/lego"
"github.com/go-acme/lego/v4/registration"
"github.com/urfave/cli"
)
const filePerm os.FileMode = 0o600
func setup(ctx *cli.Context, accountsStorage *AccountsStorage) (*Account, *lego.Client) {
keyType := getKeyType(ctx)
privateKey := accountsStorage.GetPrivateKey(keyType)
var account *Account
if accountsStorage.ExistsAccountFilePath() {
account = accountsStorage.LoadAccount(privateKey)
} else {
account = &Account{Email: accountsStorage.GetUserID(), key: privateKey}
}
client := newClient(ctx, account, keyType)
return account, client
}
func newClient(ctx *cli.Context, acc registration.User, keyType certcrypto.KeyType) *lego.Client {
config := lego.NewConfig(acc)
config.CADirURL = ctx.GlobalString("server")
config.Certificate = lego.CertificateConfig{
KeyType: keyType,
Timeout: time.Duration(ctx.GlobalInt("cert.timeout")) * time.Second,
}
config.UserAgent = fmt.Sprintf("lego-cli/%s", ctx.App.Version)
if ctx.GlobalIsSet("http-timeout") {
config.HTTPClient.Timeout = time.Duration(ctx.GlobalInt("http-timeout")) * time.Second
}
client, err := lego.NewClient(config)
if err != nil {
log.Panicf("Could not create client: %v", err)
}
if client.GetExternalAccountRequired() && !ctx.GlobalIsSet("eab") {
log.Panic("Server requires External Account Binding. Use --eab with --kid and --hmac.")
}
return client
}
// getKeyType the type from which private keys should be generated.
func getKeyType(ctx *cli.Context) certcrypto.KeyType {
keyType := ctx.GlobalString("key-type")
switch strings.ToUpper(keyType) {
case "RSA2048":
return certcrypto.RSA2048
case "RSA4096":
return certcrypto.RSA4096
case "RSA8192":
return certcrypto.RSA8192
case "EC256":
return certcrypto.EC256
case "EC384":
return certcrypto.EC384
}
log.Panicf("Unsupported KeyType: %s", keyType)
return ""
}
func getEmail(ctx *cli.Context) string {
email := ctx.GlobalString("email")
if email == "" {
log.Panic("You have to pass an account (email address) to the program using --email or -m")
}
return email
}
func createNonExistingFolder(path string) error {
if _, err := os.Stat(path); os.IsNotExist(err) {
return os.MkdirAll(path, 0o700)
} else if err != nil {
return err
}
return nil
}
func readCSRFile(filename string) (*x509.CertificateRequest, error) {
bytes, err := ioutil.ReadFile(filename)
if err != nil {
return nil, err
}
raw := bytes
// see if we can find a PEM-encoded CSR
var p *pem.Block
rest := bytes
for {
// decode a PEM block
p, rest = pem.Decode(rest)
// did we fail?
if p == nil {
break
}
// did we get a CSR?
if p.Type == "CERTIFICATE REQUEST" {
raw = p.Bytes
}
}
// no PEM-encoded CSR
// assume we were given a DER-encoded ASN.1 CSR
// (if this assumption is wrong, parsing these bytes will fail)
return x509.ParseCertificateRequest(raw)
}

View File

@@ -1,126 +0,0 @@
package cmd
import (
"net"
"strings"
"time"
"github.com/XrayR-project/XrayR/common/legocmd/log"
"github.com/go-acme/lego/v4/challenge"
"github.com/go-acme/lego/v4/challenge/dns01"
"github.com/go-acme/lego/v4/challenge/http01"
"github.com/go-acme/lego/v4/challenge/tlsalpn01"
"github.com/go-acme/lego/v4/lego"
"github.com/go-acme/lego/v4/providers/dns"
"github.com/go-acme/lego/v4/providers/http/memcached"
"github.com/go-acme/lego/v4/providers/http/webroot"
"github.com/urfave/cli"
)
func setupChallenges(ctx *cli.Context, client *lego.Client) {
if !ctx.GlobalBool("http") && !ctx.GlobalBool("tls") && !ctx.GlobalIsSet("dns") {
log.Panic("No challenge selected. You must specify at least one challenge: `--http`, `--tls`, `--dns`.")
}
if ctx.GlobalBool("http") {
err := client.Challenge.SetHTTP01Provider(setupHTTPProvider(ctx))
if err != nil {
log.Panic(err)
}
}
if ctx.GlobalBool("tls") {
err := client.Challenge.SetTLSALPN01Provider(setupTLSProvider(ctx))
if err != nil {
log.Panic(err)
}
}
if ctx.GlobalIsSet("dns") {
setupDNS(ctx, client)
}
}
func setupHTTPProvider(ctx *cli.Context) challenge.Provider {
switch {
case ctx.GlobalIsSet("http.webroot"):
ps, err := webroot.NewHTTPProvider(ctx.GlobalString("http.webroot"))
if err != nil {
log.Panic(err)
}
return ps
case ctx.GlobalIsSet("http.memcached-host"):
ps, err := memcached.NewMemcachedProvider(ctx.GlobalStringSlice("http.memcached-host"))
if err != nil {
log.Panic(err)
}
return ps
case ctx.GlobalIsSet("http.port"):
iface := ctx.GlobalString("http.port")
if !strings.Contains(iface, ":") {
log.Panicf("The --http switch only accepts interface:port or :port for its argument.")
}
host, port, err := net.SplitHostPort(iface)
if err != nil {
log.Panic(err)
}
srv := http01.NewProviderServer(host, port)
if header := ctx.GlobalString("http.proxy-header"); header != "" {
srv.SetProxyHeader(header)
}
return srv
case ctx.GlobalBool("http"):
srv := http01.NewProviderServer("", "")
if header := ctx.GlobalString("http.proxy-header"); header != "" {
srv.SetProxyHeader(header)
}
return srv
default:
log.Panic("Invalid HTTP challenge options.")
return nil
}
}
func setupTLSProvider(ctx *cli.Context) challenge.Provider {
switch {
case ctx.GlobalIsSet("tls.port"):
iface := ctx.GlobalString("tls.port")
if !strings.Contains(iface, ":") {
log.Panicf("The --tls switch only accepts interface:port or :port for its argument.")
}
host, port, err := net.SplitHostPort(iface)
if err != nil {
log.Panic(err)
}
return tlsalpn01.NewProviderServer(host, port)
case ctx.GlobalBool("tls"):
return tlsalpn01.NewProviderServer("", "")
default:
log.Panic("Invalid HTTP challenge options.")
return nil
}
}
func setupDNS(ctx *cli.Context, client *lego.Client) {
provider, err := dns.NewDNSChallengeProviderByName(ctx.GlobalString("dns"))
if err != nil {
log.Panic(err)
}
servers := ctx.GlobalStringSlice("dns.resolvers")
err = client.Challenge.SetDNS01Provider(provider,
dns01.CondOption(len(servers) > 0,
dns01.AddRecursiveNameservers(dns01.ParseNameservers(ctx.GlobalStringSlice("dns.resolvers")))),
dns01.CondOption(ctx.GlobalBool("dns.disable-cp"),
dns01.DisableCompletePropagationRequirement()),
dns01.CondOption(ctx.GlobalIsSet("dns-timeout"),
dns01.AddDNSTimeout(time.Duration(ctx.GlobalInt("dns-timeout"))*time.Second)),
)
if err != nil {
log.Panic(err)
}
}

File diff suppressed because it is too large Load Diff

View File

@@ -1,189 +0,0 @@
// Let's Encrypt client to go!
// CLI application for generating Let's Encrypt certificates using the ACME package.
package legocmd
import (
"errors"
"fmt"
"os"
"path"
"path/filepath"
"runtime"
"strings"
"github.com/XrayR-project/XrayR/common/legocmd/cmd"
"github.com/urfave/cli"
)
var version = "dev"
var defaultPath string
type LegoCMD struct {
cmdClient *cli.App
}
func New() (*LegoCMD, error) {
app := cli.NewApp()
app.Name = "lego"
app.HelpName = "lego"
app.Usage = "Let's Encrypt client written in Go"
app.EnableBashCompletion = true
app.Version = version
cli.VersionPrinter = func(c *cli.Context) {
fmt.Printf("lego version %s %s/%s\n", c.App.Version, runtime.GOOS, runtime.GOARCH)
}
// Set default path to configPath/cert
var path string = ""
configPath := os.Getenv("XRAY_LOCATION_CONFIG")
if configPath != "" {
path = configPath
} else if cwd, err := os.Getwd(); err==nil{
path = cwd
} else {
path = "."
}
defaultPath = filepath.Join(path, "cert")
app.Flags = cmd.CreateFlags(defaultPath)
app.Before = cmd.Before
app.Commands = cmd.CreateCommands()
lego := &LegoCMD{
cmdClient: app,
}
return lego, nil
}
// DNSCert cert a domain using DNS API
func (l *LegoCMD) DNSCert(domain, email, provider string, DNSEnv map[string]string) (CertPath string, KeyPath string, err error) {
defer func() (string, string, error) {
// Handle any error
if r := recover(); r != nil {
switch x := r.(type) {
case string:
err = errors.New(x)
case error:
err = x
default:
err = errors.New("unknow panic")
}
return "", "", err
}
return CertPath, KeyPath, nil
}()
// Set Env for DNS configuration
for key, value := range DNSEnv {
os.Setenv(key, value)
}
// First check if the certificate exists
CertPath, KeyPath, err = checkCertfile(domain)
if err == nil {
return CertPath, KeyPath, err
}
argstring := fmt.Sprintf("lego -a -d %s -m %s --dns %s run", domain, email, provider)
err = l.cmdClient.Run(strings.Split(argstring, " "))
if err != nil {
return "", "", err
}
CertPath, KeyPath, err = checkCertfile(domain)
if err != nil {
return "", "", err
}
return CertPath, KeyPath, nil
}
// HTTPCert cert a domain using http methods
func (l *LegoCMD) HTTPCert(domain, email string) (CertPath string, KeyPath string, err error) {
defer func() (string, string, error) {
// Handle any error
if r := recover(); r != nil {
switch x := r.(type) {
case string:
err = errors.New(x)
case error:
err = x
default:
err = errors.New("unknow panic")
}
return "", "", err
}
return CertPath, KeyPath, nil
}()
// First check if the certificate exists
CertPath, KeyPath, err = checkCertfile(domain)
if err == nil {
return CertPath, KeyPath, err
}
argstring := fmt.Sprintf("lego -a -d %s -m %s --http run", domain, email)
err = l.cmdClient.Run(strings.Split(argstring, " "))
if err != nil {
return "", "", err
}
CertPath, KeyPath, err = checkCertfile(domain)
if err != nil {
return "", "", err
}
return CertPath, KeyPath, nil
}
//RenewCert renew a domain cert
func (l *LegoCMD) RenewCert(domain, email, certMode, provider string, DNSEnv map[string]string) (CertPath string, KeyPath string, err error) {
var argstring string
defer func() (string, string, error) {
// Handle any error
if r := recover(); r != nil {
switch x := r.(type) {
case string:
err = errors.New(x)
case error:
err = x
default:
err = errors.New("unknow panic")
}
return "", "", err
}
return CertPath, KeyPath, nil
}()
if certMode == "http" {
argstring = fmt.Sprintf("lego -a -d %s -m %s --http renew --days 30", domain, email)
} else if certMode == "dns" {
// Set Env for DNS configuration
for key, value := range DNSEnv {
os.Setenv(key, value)
}
argstring = fmt.Sprintf("lego -a -d %s -m %s --dns %s renew --days 30", domain, email, provider)
} else {
return "", "", fmt.Errorf("Unsupport cert mode: %s", certMode)
}
err = l.cmdClient.Run(strings.Split(argstring, " "))
if err != nil {
return "", "", err
}
CertPath, KeyPath, err = checkCertfile(domain)
if err != nil {
return "", "", err
}
return CertPath, KeyPath, nil
}
func checkCertfile(domain string) (string, string, error) {
keyPath := path.Join(defaultPath, "certificates", fmt.Sprintf("%s.key", domain))
certPath := path.Join(defaultPath, "certificates", fmt.Sprintf("%s.crt", domain))
if _, err := os.Stat(keyPath); os.IsNotExist(err) {
return "", "", fmt.Errorf("Cert key failed: %s", domain)
}
if _, err := os.Stat(certPath); os.IsNotExist(err) {
return "", "", fmt.Errorf("Cert cert failed: %s", domain)
}
absKeyPath, _ := filepath.Abs(keyPath)
absCertPath, _ := filepath.Abs(certPath)
return absCertPath, absKeyPath, nil
}

View File

@@ -1,82 +0,0 @@
package legocmd_test
import (
"testing"
"github.com/XrayR-project/XrayR/common/legocmd"
)
func TestLegoClient(t *testing.T) {
_, err := legocmd.New()
if err != nil {
t.Error(err)
}
}
func TestLegoDNSCert(t *testing.T) {
lego, err := legocmd.New()
if err != nil {
t.Error(err)
}
var (
domain string = "node1.test.com"
email string = "test@gmail.com"
provider string = "alidns"
DNSEnv map[string]string
)
DNSEnv = make(map[string]string)
DNSEnv["ALICLOUD_ACCESS_KEY"] = "aaa"
DNSEnv["ALICLOUD_SECRET_KEY"] = "bbb"
certPath, keyPath, err := lego.DNSCert(domain, email, provider, DNSEnv)
if err != nil {
t.Error(err)
}
t.Log(certPath)
t.Log(keyPath)
}
func TestLegoHTTPCert(t *testing.T) {
lego, err := legocmd.New()
if err != nil {
t.Error(err)
}
var (
domain string = "node1.test.com"
email string = "test@gmail.com"
)
certPath, keyPath, err := lego.HTTPCert(domain, email)
if err != nil {
t.Error(err)
}
t.Log(certPath)
t.Log(keyPath)
}
func TestLegoRenewCert(t *testing.T) {
lego, err := legocmd.New()
if err != nil {
t.Error(err)
}
var (
domain string = "node1.test.com"
email string = "test@gmail.com"
provider string = "alidns"
DNSEnv map[string]string
)
DNSEnv = make(map[string]string)
DNSEnv["ALICLOUD_ACCESS_KEY"] = "aaa"
DNSEnv["ALICLOUD_SECRET_KEY"] = "bbb"
certPath, keyPath, err := lego.RenewCert(domain, email, "dns", provider, DNSEnv)
if err != nil {
t.Error(err)
}
t.Log(certPath)
t.Log(keyPath)
certPath, keyPath, err = lego.RenewCert(domain, email, "http", provider, DNSEnv)
if err != nil {
t.Error(err)
}
t.Log(certPath)
t.Log(keyPath)
}

View File

@@ -1,60 +0,0 @@
package log
import (
"log"
"os"
)
// Logger is an optional custom logger.
var Logger StdLogger = log.New(os.Stdout, "", log.LstdFlags)
// StdLogger interface for Standard Logger.
type StdLogger interface {
Panic(args ...interface{})
Fatalln(args ...interface{})
Panicf(format string, args ...interface{})
Print(args ...interface{})
Println(args ...interface{})
Printf(format string, args ...interface{})
}
// Panic writes a log entry.
// It uses Logger if not nil, otherwise it uses the default log.Logger.
func Panic(args ...interface{}) {
Logger.Panic(args...)
}
// Panicf writes a log entry.
// It uses Logger if not nil, otherwise it uses the default log.Logger.
func Panicf(format string, args ...interface{}) {
Logger.Panicf(format, args...)
}
// Print writes a log entry.
// It uses Logger if not nil, otherwise it uses the default log.Logger.
func Print(args ...interface{}) {
Logger.Print(args...)
}
// Println writes a log entry.
// It uses Logger if not nil, otherwise it uses the default log.Logger.
func Println(args ...interface{}) {
Logger.Println(args...)
}
// Printf writes a log entry.
// It uses Logger if not nil, otherwise it uses the default log.Logger.
func Printf(format string, args ...interface{}) {
Logger.Printf(format, args...)
}
// Warnf writes a log entry.
func Warnf(format string, args ...interface{}) {
Printf("[WARN] "+format, args...)
}
// Infof writes a log entry.
func Infof(format string, args ...interface{}) {
Printf("[INFO] "+format, args...)
}

View File

@@ -2,8 +2,6 @@ package limiter
import "github.com/xtls/xray-core/common/errors"
type errPathObjHolder struct{}
func newError(values ...interface{}) *errors.Error {
return errors.New(values...).WithPathObj(errPathObjHolder{})
return errors.New(values...)
}

View File

@@ -1,13 +1,24 @@
// Package limiter is to control the links that go into the dispather
// Package limiter is to control the links that go into the dispatcher
package limiter
import (
"context"
"fmt"
"strconv"
"strings"
"sync"
"time"
"github.com/eko/gocache/lib/v4/cache"
"github.com/eko/gocache/lib/v4/marshaler"
"github.com/eko/gocache/lib/v4/store"
goCacheStore "github.com/eko/gocache/store/go_cache/v4"
redisStore "github.com/eko/gocache/store/redis/v4"
goCache "github.com/patrickmn/go-cache"
"github.com/redis/go-redis/v9"
"golang.org/x/time/rate"
"github.com/XrayR-project/XrayR/api"
"github.com/juju/ratelimit"
)
type UserInfo struct {
@@ -20,8 +31,12 @@ type InboundInfo struct {
Tag string
NodeSpeedLimit uint64
UserInfo *sync.Map // Key: Email value: UserInfo
BucketHub *sync.Map // key: Email, value: *ratelimit.Bucket
UserOnlineIP *sync.Map // Key: Email Value: *sync.Map: Key: IP, Value: UID
BucketHub *sync.Map // key: Email, value: *rate.Limiter
UserOnlineIP *sync.Map // Key: Email, value: {Key: IP, value: UID}
GlobalLimit struct {
config *GlobalDeviceLimitConfig
globalOnlineIP *marshaler.Marshaler
}
}
type Limiter struct {
@@ -34,13 +49,37 @@ func New() *Limiter {
}
}
func (l *Limiter) AddInboundLimiter(tag string, nodeSpeedLimit uint64, userList *[]api.UserInfo) error {
func (l *Limiter) AddInboundLimiter(tag string, nodeSpeedLimit uint64, userList *[]api.UserInfo, globalLimit *GlobalDeviceLimitConfig) error {
inboundInfo := &InboundInfo{
Tag: tag,
NodeSpeedLimit: nodeSpeedLimit,
BucketHub: new(sync.Map),
UserOnlineIP: new(sync.Map),
}
if globalLimit != nil && globalLimit.Enable {
inboundInfo.GlobalLimit.config = globalLimit
// init local store
gs := goCacheStore.NewGoCache(goCache.New(time.Duration(globalLimit.Expiry)*time.Second, 1*time.Minute))
// init redis store
rs := redisStore.NewRedis(redis.NewClient(
&redis.Options{
Addr: globalLimit.RedisAddr,
Password: globalLimit.RedisPassword,
DB: globalLimit.RedisDB,
}),
store.WithExpiration(time.Duration(globalLimit.Expiry)*time.Second))
// init chained cache. First use local go-cache, if go-cache is nil, then use redis cache
cacheManager := cache.NewChain[any](
cache.New[any](gs), // go-cache is priority
cache.New[any](rs),
)
inboundInfo.GlobalLimit.globalOnlineIP = marshaler.New(cacheManager)
}
userMap := new(sync.Map)
for _, u := range *userList {
userMap.Store(fmt.Sprintf("%s|%s|%d", tag, u.Email, u.UID), UserInfo{
@@ -55,7 +94,6 @@ func (l *Limiter) AddInboundLimiter(tag string, nodeSpeedLimit uint64, userList
}
func (l *Limiter) UpdateInboundLimiter(tag string, updatedUserList *[]api.UserInfo) error {
if value, ok := l.InboundInfo.Load(tag); ok {
inboundInfo := value.(*InboundInfo)
// Update User info
@@ -65,7 +103,17 @@ func (l *Limiter) UpdateInboundLimiter(tag string, updatedUserList *[]api.UserIn
SpeedLimit: u.SpeedLimit,
DeviceLimit: u.DeviceLimit,
})
inboundInfo.BucketHub.Delete(fmt.Sprintf("%s|%s|%d", tag, u.Email, u.UID)) // Delete old limiter bucket
// Update old limiter bucket
limit := determineRate(inboundInfo.NodeSpeedLimit, u.SpeedLimit)
if limit > 0 {
if bucket, ok := inboundInfo.BucketHub.Load(fmt.Sprintf("%s|%s|%d", tag, u.Email, u.UID)); ok {
limiter := bucket.(*rate.Limiter)
limiter.SetLimit(rate.Limit(limit))
limiter.SetBurst(int(limit))
}
} else {
inboundInfo.BucketHub.Delete(fmt.Sprintf("%s|%s|%d", tag, u.Email, u.UID))
}
}
} else {
return fmt.Errorf("no such inbound in limiter: %s", tag)
@@ -79,7 +127,8 @@ func (l *Limiter) DeleteInboundLimiter(tag string) error {
}
func (l *Limiter) GetOnlineDevice(tag string) (*[]api.OnlineUser, error) {
onlineUser := make([]api.OnlineUser, 0)
var onlineUser []api.OnlineUser
if value, ok := l.InboundInfo.Load(tag); ok {
inboundInfo := value.(*InboundInfo)
// Clear Speed Limiter bucket for users who are not online
@@ -91,43 +140,48 @@ func (l *Limiter) GetOnlineDevice(tag string) (*[]api.OnlineUser, error) {
return true
})
inboundInfo.UserOnlineIP.Range(func(key, value interface{}) bool {
email := key.(string)
ipMap := value.(*sync.Map)
ipMap.Range(func(key, value interface{}) bool {
ip := key.(string)
uid := value.(int)
ip := key.(string)
onlineUser = append(onlineUser, api.OnlineUser{UID: uid, IP: ip})
return true
})
email := key.(string)
inboundInfo.UserOnlineIP.Delete(email) // Reset online device
return true
})
} else {
return nil, fmt.Errorf("no such inbound in limiter: %s", tag)
}
return &onlineUser, nil
}
func (l *Limiter) GetUserBucket(tag string, email string, ip string) (limiter *ratelimit.Bucket, SpeedLimit bool, Reject bool) {
func (l *Limiter) GetUserBucket(tag string, email string, ip string) (limiter *rate.Limiter, SpeedLimit bool, Reject bool) {
if value, ok := l.InboundInfo.Load(tag); ok {
var (
userLimit uint64 = 0
deviceLimit, uid int
)
inboundInfo := value.(*InboundInfo)
nodeLimit := inboundInfo.NodeSpeedLimit
var userLimit uint64 = 0
var deviceLimit int = 0
var uid int = 0
if v, ok := inboundInfo.UserInfo.Load(email); ok {
u := v.(UserInfo)
uid = u.UID
userLimit = u.SpeedLimit
deviceLimit = u.DeviceLimit
}
// Report online device
// Local device limit
ipMap := new(sync.Map)
ipMap.Store(ip, uid)
// If any device is online
if v, ok := inboundInfo.UserOnlineIP.LoadOrStore(email, ipMap); ok {
ipMap := v.(*sync.Map)
// If this ip is a new device
// If this is a new ip
if _, ok := ipMap.LoadOrStore(ip, uid); !ok {
counter := 0
ipMap.Range(func(key, value interface{}) bool {
@@ -140,11 +194,20 @@ func (l *Limiter) GetUserBucket(tag string, email string, ip string) (limiter *r
}
}
}
limit := determineRate(nodeLimit, userLimit) // If need the Speed limit
// GlobalLimit
if inboundInfo.GlobalLimit.config != nil && inboundInfo.GlobalLimit.config.Enable {
if reject := globalLimit(inboundInfo, email, uid, ip, deviceLimit); reject {
return nil, false, true
}
}
// Speed limit
limit := determineRate(nodeLimit, userLimit) // Determine the speed limit rate
if limit > 0 {
limiter := ratelimit.NewBucketWithQuantum(time.Duration(int64(time.Second)), int64(limit), int64(limit)) // Byte/s
limiter := rate.NewLimiter(rate.Limit(limit), int(limit)) // Byte/s
if v, ok := inboundInfo.BucketHub.LoadOrStore(email, limiter); ok {
bucket := v.(*ratelimit.Bucket)
bucket := v.(*rate.Limiter)
return bucket, true, false
} else {
return limiter, true, false
@@ -158,6 +221,51 @@ func (l *Limiter) GetUserBucket(tag string, email string, ip string) (limiter *r
}
}
// Global device limit
func globalLimit(inboundInfo *InboundInfo, email string, uid int, ip string, deviceLimit int) bool {
ctx, cancel := context.WithTimeout(context.Background(), time.Duration(inboundInfo.GlobalLimit.config.Timeout)*time.Second)
defer cancel()
// reformat email for unique key
uniqueKey := strings.Replace(email, inboundInfo.Tag, strconv.Itoa(deviceLimit), 1)
v, err := inboundInfo.GlobalLimit.globalOnlineIP.Get(ctx, uniqueKey, new(map[string]int))
if err != nil {
if _, ok := err.(*store.NotFound); ok {
// If the email is a new device
go pushIP(inboundInfo, uniqueKey, &map[string]int{ip: uid})
} else {
newError("cache service").Base(err).AtError().WriteToLog()
}
return false
}
ipMap := v.(*map[string]int)
// Reject device reach limit directly
if deviceLimit > 0 && len(*ipMap) > deviceLimit {
return true
}
// If the ip is not in cache
if _, ok := (*ipMap)[ip]; !ok {
(*ipMap)[ip] = uid
go pushIP(inboundInfo, uniqueKey, ipMap)
}
return false
}
// push the ip to cache
func pushIP(inboundInfo *InboundInfo, uniqueKey string, ipMap *map[string]int) {
ctx, cancel := context.WithTimeout(context.Background(), time.Duration(inboundInfo.GlobalLimit.config.Timeout)*time.Second)
defer cancel()
if err := inboundInfo.GlobalLimit.globalOnlineIP.Set(ctx, uniqueKey, ipMap); err != nil {
newError("cache service").Base(err).AtError().WriteToLog()
}
}
// determineRate returns the minimum non-zero rate
func determineRate(nodeLimit, userLimit uint64) (limit uint64) {
if nodeLimit == 0 || userLimit == 0 {

10
common/limiter/model.go Normal file
View File

@@ -0,0 +1,10 @@
package limiter
type GlobalDeviceLimitConfig struct {
Enable bool `mapstructure:"Enable"`
RedisAddr string `mapstructure:"RedisAddr"` // host:port
RedisPassword string `mapstructure:"RedisPassword"`
RedisDB int `mapstructure:"RedisDB"`
Timeout int `mapstructure:"Timeout"`
Expiry int `mapstructure:"Expiry"` // second
}

View File

@@ -1,20 +1,21 @@
package limiter
import (
"context"
"io"
"github.com/juju/ratelimit"
"github.com/xtls/xray-core/common"
"github.com/xtls/xray-core/common/buf"
"golang.org/x/time/rate"
)
type Writer struct {
writer buf.Writer
limiter *ratelimit.Bucket
limiter *rate.Limiter
w io.Writer
}
func (l *Limiter) RateWriter(writer buf.Writer, limiter *ratelimit.Bucket) buf.Writer {
func (l *Limiter) RateWriter(writer buf.Writer, limiter *rate.Limiter) buf.Writer {
return &Writer{
writer: writer,
limiter: limiter,
@@ -26,6 +27,7 @@ func (w *Writer) Close() error {
}
func (w *Writer) WriteMultiBuffer(mb buf.MultiBuffer) error {
w.limiter.Wait(int64(mb.Len()))
ctx := context.Background()
w.limiter.WaitN(ctx, int(mb.Len()))
return w.writer.WriteMultiBuffer(mb)
}

View File

@@ -1,4 +1,4 @@
package cmd
package mylego
import (
"crypto"

View File

@@ -1,4 +1,4 @@
package cmd
package mylego
import (
"crypto"
@@ -6,18 +6,16 @@ import (
"encoding/json"
"encoding/pem"
"errors"
"fmt"
"io/ioutil"
"log"
"net/url"
"os"
"path/filepath"
"strings"
"github.com/XrayR-project/XrayR/common/legocmd/log"
"github.com/go-acme/lego/v4/certcrypto"
"github.com/go-acme/lego/v4/lego"
"github.com/go-acme/lego/v4/registration"
"github.com/urfave/cli"
"golang.org/x/crypto/acme"
)
const (
@@ -30,56 +28,53 @@ const (
//
// rootPath:
//
// ./.lego/accounts/
// │ └── root accounts directory
// └── "path" option
// ./.lego/accounts/
// │ └── root accounts directory
// └── "path" option
//
// rootUserPath:
//
// ./.lego/accounts/localhost_14000/hubert@hubert.com/
// │ │ │ └── userID ("email" option)
// │ │ └── CA server ("server" option)
// │ └── root accounts directory
// └── "path" option
// ./.lego/accounts/localhost_14000/hubert@hubert.com/
// │ │ │ └── userID ("email" option)
// │ │ └── CA server ("server" option)
// │ └── root accounts directory
// └── "path" option
//
// keysPath:
//
// ./.lego/accounts/localhost_14000/hubert@hubert.com/keys/
// │ │ │ │ └── root keys directory
// │ │ │ └── userID ("email" option)
// │ │ └── CA server ("server" option)
// │ └── root accounts directory
// └── "path" option
// ./.lego/accounts/localhost_14000/hubert@hubert.com/keys/
// │ │ │ │ └── root keys directory
// │ │ │ └── userID ("email" option)
// │ │ └── CA server ("server" option)
// │ └── root accounts directory
// └── "path" option
//
// accountFilePath:
//
// ./.lego/accounts/localhost_14000/hubert@hubert.com/account.json
// │ │ │ │ └── account file
// │ │ │ └── userID ("email" option)
// │ │ └── CA server ("server" option)
// │ └── root accounts directory
// └── "path" option
//
// ./.lego/accounts/localhost_14000/hubert@hubert.com/account.json
// │ │ │ │ └── account file
// │ │ │ └── userID ("email" option)
// │ │ └── CA server ("server" option)
// │ └── root accounts directory
// └── "path" option
type AccountsStorage struct {
userID string
rootPath string
rootUserPath string
keysPath string
accountFilePath string
ctx *cli.Context
}
// NewAccountsStorage Creates a new AccountsStorage.
func NewAccountsStorage(ctx *cli.Context) *AccountsStorage {
// TODO: move to account struct? Currently MUST pass email.
email := getEmail(ctx)
func NewAccountsStorage(l *LegoCMD) *AccountsStorage {
email := l.C.Email
serverURL, err := url.Parse(ctx.GlobalString("server"))
serverURL, err := url.Parse(acme.LetsEncryptURL)
if err != nil {
log.Panic(err)
}
rootPath := filepath.Join(ctx.GlobalString("path"), baseAccountsRootFolderName)
rootPath := filepath.Join(l.path, baseAccountsRootFolderName)
serverPath := strings.NewReplacer(":", "_", "/", string(os.PathSeparator)).Replace(serverURL.Host)
accountsPath := filepath.Join(rootPath, serverPath)
rootUserPath := filepath.Join(accountsPath, email)
@@ -90,7 +85,6 @@ func NewAccountsStorage(ctx *cli.Context) *AccountsStorage {
rootUserPath: rootUserPath,
keysPath: filepath.Join(rootUserPath, baseKeysFolderName),
accountFilePath: filepath.Join(rootUserPath, accountFileName),
ctx: ctx,
}
}
@@ -122,11 +116,11 @@ func (s *AccountsStorage) Save(account *Account) error {
return err
}
return ioutil.WriteFile(s.accountFilePath, jsonBytes, filePerm)
return os.WriteFile(s.accountFilePath, jsonBytes, filePerm)
}
func (s *AccountsStorage) LoadAccount(privateKey crypto.PrivateKey) *Account {
fileBytes, err := ioutil.ReadFile(s.accountFilePath)
fileBytes, err := os.ReadFile(s.accountFilePath)
if err != nil {
log.Panicf("Could not load file for account %s: %v", s.userID, err)
}
@@ -140,7 +134,7 @@ func (s *AccountsStorage) LoadAccount(privateKey crypto.PrivateKey) *Account {
account.key = privateKey
if account.Registration == nil || account.Registration.Body.Status == "" {
reg, err := tryRecoverRegistration(s.ctx, privateKey)
reg, err := tryRecoverRegistration(privateKey)
if err != nil {
log.Panicf("Could not load account for %s. Registration is nil: %#v", s.userID, err)
}
@@ -207,7 +201,7 @@ func generatePrivateKey(file string, keyType certcrypto.KeyType) (crypto.Private
}
func loadPrivateKey(file string) (crypto.PrivateKey, error) {
keyBytes, err := ioutil.ReadFile(file)
keyBytes, err := os.ReadFile(file)
if err != nil {
return nil, err
}
@@ -224,11 +218,11 @@ func loadPrivateKey(file string) (crypto.PrivateKey, error) {
return nil, errors.New("unknown private key type")
}
func tryRecoverRegistration(ctx *cli.Context, privateKey crypto.PrivateKey) (*registration.Resource, error) {
func tryRecoverRegistration(privateKey crypto.PrivateKey) (*registration.Resource, error) {
// couldn't load account but got a key. Try to look the account up.
config := lego.NewConfig(&Account{key: privateKey})
config.CADirURL = ctx.GlobalString("server")
config.UserAgent = fmt.Sprintf("lego-cli/%s", ctx.App.Version)
config.CADirURL = acme.LetsEncryptURL
config.UserAgent = "lego-cli/dev"
client, err := lego.NewClient(config)
if err != nil {

View File

@@ -1,56 +1,45 @@
package cmd
package mylego
import (
"bytes"
"crypto/x509"
"encoding/json"
"io/ioutil"
"log"
"os"
"path/filepath"
"strconv"
"strings"
"time"
"github.com/XrayR-project/XrayR/common/legocmd/log"
"github.com/go-acme/lego/v4/certcrypto"
"github.com/go-acme/lego/v4/certificate"
"github.com/urfave/cli"
"golang.org/x/net/idna"
)
const (
baseCertificatesFolderName = "certificates"
baseArchivesFolderName = "archives"
)
// CertificatesStorage a certificates storage.
// CertificatesStorage a certificates' storage.
//
// rootPath:
//
// ./.lego/certificates/
// │ └── root certificates directory
// └── "path" option
// ./.lego/certificates/
// │ └── root certificates directory
// └── "path" option
//
// archivePath:
//
// ./.lego/archives/
// │ └── archived certificates directory
// └── "path" option
//
// ./.lego/archives/
// │ └── archived certificates directory
// └── "path" option
type CertificatesStorage struct {
rootPath string
archivePath string
pem bool
filename string // Deprecated
rootPath string
pem bool
}
// NewCertificatesStorage create a new certificates storage.
func NewCertificatesStorage(ctx *cli.Context) *CertificatesStorage {
func NewCertificatesStorage(path string) *CertificatesStorage {
return &CertificatesStorage{
rootPath: filepath.Join(ctx.GlobalString("path"), baseCertificatesFolderName),
archivePath: filepath.Join(ctx.GlobalString("path"), baseArchivesFolderName),
pem: ctx.GlobalBool("pem"),
filename: ctx.GlobalString("filename"),
rootPath: filepath.Join(path, baseCertificatesFolderName),
}
}
@@ -61,13 +50,6 @@ func (s *CertificatesStorage) CreateRootFolder() {
}
}
func (s *CertificatesStorage) CreateArchiveFolder() {
err := createNonExistingFolder(s.archivePath)
if err != nil {
log.Panicf("Could not check/create path: %v", err)
}
}
func (s *CertificatesStorage) GetRootPath() string {
return s.rootPath
}
@@ -144,7 +126,7 @@ func (s *CertificatesStorage) ExistsFile(domain, extension string) bool {
}
func (s *CertificatesStorage) ReadFile(domain, extension string) ([]byte, error) {
return ioutil.ReadFile(s.GetFileName(domain, extension))
return os.ReadFile(s.GetFileName(domain, extension))
}
func (s *CertificatesStorage) GetFileName(domain, extension string) string {
@@ -163,36 +145,11 @@ func (s *CertificatesStorage) ReadCertificate(domain, extension string) ([]*x509
}
func (s *CertificatesStorage) WriteFile(domain, extension string, data []byte) error {
var baseFileName string
if s.filename != "" {
baseFileName = s.filename
} else {
baseFileName = sanitizedDomain(domain)
}
var baseFileName = sanitizedDomain(domain)
filePath := filepath.Join(s.rootPath, baseFileName+extension)
return ioutil.WriteFile(filePath, data, filePerm)
}
func (s *CertificatesStorage) MoveToArchive(domain string) error {
matches, err := filepath.Glob(filepath.Join(s.rootPath, sanitizedDomain(domain)+".*"))
if err != nil {
return err
}
for _, oldFile := range matches {
date := strconv.FormatInt(time.Now().Unix(), 10)
filename := date + "." + filepath.Base(oldFile)
newFile := filepath.Join(s.archivePath, filename)
err = os.Rename(oldFile, newFile)
if err != nil {
return err
}
}
return nil
return os.WriteFile(filePath, data, filePerm)
}
// sanitizedDomain Make sure no funny chars are in the cert names (like wildcards ;)).

View File

@@ -0,0 +1,87 @@
package mylego_test
import (
"testing"
"github.com/XrayR-project/XrayR/common/mylego"
)
func TestLegoClient(t *testing.T) {
_, err := mylego.New(&mylego.CertConfig{})
if err != nil {
t.Error(err)
}
}
func TestLegoDNSCert(t *testing.T) {
lego, err := mylego.New(&mylego.CertConfig{
CertDomain: "node1.test.com",
Provider: "alidns",
Email: "test@gmail.com",
DNSEnv: map[string]string{
"ALICLOUD_ACCESS_KEY": "aaa",
"ALICLOUD_SECRET_KEY": "bbb",
},
},
)
if err != nil {
t.Error(err)
}
certPath, keyPath, err := lego.DNSCert()
if err != nil {
t.Error(err)
}
t.Log(certPath)
t.Log(keyPath)
}
func TestLegoHTTPCert(t *testing.T) {
lego, err := mylego.New(&mylego.CertConfig{
CertMode: "http",
CertDomain: "node1.test.com",
Email: "test@gmail.com",
})
if err != nil {
t.Error(err)
}
certPath, keyPath, err := lego.HTTPCert()
if err != nil {
t.Error(err)
}
t.Log(certPath)
t.Log(keyPath)
}
func TestLegoRenewCert(t *testing.T) {
lego, err := mylego.New(&mylego.CertConfig{
CertDomain: "node1.test.com",
Email: "test@gmail.com",
Provider: "alidns",
DNSEnv: map[string]string{
"ALICLOUD_ACCESS_KEY": "aaa",
"ALICLOUD_SECRET_KEY": "bbb",
},
})
if err != nil {
t.Error(err)
}
lego.C.CertMode = "http"
certPath, keyPath, ok, err := lego.RenewCert()
if err != nil {
t.Error(err)
}
t.Log(certPath)
t.Log(keyPath)
t.Log(ok)
lego.C.CertMode = "dns"
certPath, keyPath, ok, err = lego.RenewCert()
if err != nil {
t.Error(err)
}
t.Log(certPath)
t.Log(keyPath)
t.Log(ok)
}

17
common/mylego/model.go Normal file
View File

@@ -0,0 +1,17 @@
package mylego
type CertConfig struct {
CertMode string `mapstructure:"CertMode"` // none, file, http, dns
CertDomain string `mapstructure:"CertDomain"`
CertFile string `mapstructure:"CertFile"`
KeyFile string `mapstructure:"KeyFile"`
Provider string `mapstructure:"Provider"` // alidns, cloudflare, gandi, godaddy....
Email string `mapstructure:"Email"`
DNSEnv map[string]string `mapstructure:"DNSEnv"`
RejectUnknownSni bool `mapstructure:"RejectUnknownSni"`
}
type LegoCMD struct {
C *CertConfig
path string
}

163
common/mylego/mylego.go Normal file
View File

@@ -0,0 +1,163 @@
package mylego
import (
"errors"
"fmt"
"os"
"path"
"path/filepath"
"strings"
)
var defaultPath string
func New(certConf *CertConfig) (*LegoCMD, error) {
// Set default path to configPath/cert
var p = ""
configPath := os.Getenv("XRAY_LOCATION_CONFIG")
if configPath != "" {
p = configPath
} else if cwd, err := os.Getwd(); err == nil {
p = cwd
} else {
p = "."
}
defaultPath = filepath.Join(p, "cert")
lego := &LegoCMD{
C: certConf,
path: defaultPath,
}
return lego, nil
}
func (l *LegoCMD) getPath() string {
return l.path
}
func (l *LegoCMD) getCertConfig() *CertConfig {
return l.C
}
// DNSCert cert a domain using DNS API
func (l *LegoCMD) DNSCert() (CertPath string, KeyPath string, err error) {
defer func() (string, string, error) {
// Handle any error
if r := recover(); r != nil {
switch x := r.(type) {
case string:
err = errors.New(x)
case error:
err = x
default:
err = errors.New("unknown panic")
}
return "", "", err
}
return CertPath, KeyPath, nil
}()
// Set Env for DNS configuration
for key, value := range l.C.DNSEnv {
os.Setenv(strings.ToUpper(key), value)
}
// First check if the certificate exists
CertPath, KeyPath, err = checkCertFile(l.C.CertDomain)
if err == nil {
return CertPath, KeyPath, err
}
err = l.Run()
if err != nil {
return "", "", err
}
CertPath, KeyPath, err = checkCertFile(l.C.CertDomain)
if err != nil {
return "", "", err
}
return CertPath, KeyPath, nil
}
// HTTPCert cert a domain using http methods
func (l *LegoCMD) HTTPCert() (CertPath string, KeyPath string, err error) {
defer func() (string, string, error) {
// Handle any error
if r := recover(); r != nil {
switch x := r.(type) {
case string:
err = errors.New(x)
case error:
err = x
default:
err = errors.New("unknown panic")
}
return "", "", err
}
return CertPath, KeyPath, nil
}()
// First check if the certificate exists
CertPath, KeyPath, err = checkCertFile(l.C.CertDomain)
if err == nil {
return CertPath, KeyPath, err
}
err = l.Run()
if err != nil {
return "", "", err
}
CertPath, KeyPath, err = checkCertFile(l.C.CertDomain)
if err != nil {
return "", "", err
}
return CertPath, KeyPath, nil
}
// RenewCert renew a domain cert
func (l *LegoCMD) RenewCert() (CertPath string, KeyPath string, ok bool, err error) {
defer func() (string, string, bool, error) {
// Handle any error
if r := recover(); r != nil {
switch x := r.(type) {
case string:
err = errors.New(x)
case error:
err = x
default:
err = errors.New("unknown panic")
}
return "", "", false, err
}
return CertPath, KeyPath, ok, nil
}()
ok, err = l.Renew()
if err != nil {
return
}
CertPath, KeyPath, err = checkCertFile(l.C.CertDomain)
if err != nil {
return
}
return
}
func checkCertFile(domain string) (string, string, error) {
keyPath := path.Join(defaultPath, "certificates", fmt.Sprintf("%s.key", domain))
certPath := path.Join(defaultPath, "certificates", fmt.Sprintf("%s.crt", domain))
if _, err := os.Stat(keyPath); os.IsNotExist(err) {
return "", "", fmt.Errorf("cert key failed: %s", domain)
}
if _, err := os.Stat(certPath); os.IsNotExist(err) {
return "", "", fmt.Errorf("cert cert failed: %s", domain)
}
absKeyPath, _ := filepath.Abs(keyPath)
absCertPath, _ := filepath.Abs(certPath)
return absCertPath, absKeyPath, nil
}

77
common/mylego/renew.go Normal file
View File

@@ -0,0 +1,77 @@
package mylego
import (
"crypto"
"crypto/x509"
"log"
"time"
"github.com/go-acme/lego/v4/certcrypto"
"github.com/go-acme/lego/v4/certificate"
"github.com/go-acme/lego/v4/lego"
)
func (l *LegoCMD) Renew() (bool, error) {
account, client := setup(NewAccountsStorage(l))
setupChallenges(l, client)
if account.Registration == nil {
log.Panicf("Account %s is not registered. Use 'run' to register a new account.\n", account.Email)
}
return renewForDomains(l.C.CertDomain, client, NewCertificatesStorage(l.path))
}
func renewForDomains(domain string, client *lego.Client, certsStorage *CertificatesStorage) (bool, error) {
// load the cert resource from files.
// We store the certificate, private key and metadata in different files
// as web servers would not be able to work with a combined file.
certificates, err := certsStorage.ReadCertificate(domain, ".crt")
if err != nil {
log.Panicf("Error while loading the certificate for domain %s\n\t%v", domain, err)
}
cert := certificates[0]
if !needRenewal(cert, domain, 30) {
return false, nil
}
// This is just meant to be informal for the user.
timeLeft := cert.NotAfter.Sub(time.Now().UTC())
log.Printf("[%s] acme: Trying renewal with %d hours remaining", domain, int(timeLeft.Hours()))
certDomains := certcrypto.ExtractDomains(cert)
var privateKey crypto.PrivateKey
request := certificate.ObtainRequest{
Domains: certDomains,
Bundle: true,
PrivateKey: privateKey,
}
certRes, err := client.Certificate.Obtain(request)
if err != nil {
log.Panic(err)
}
certsStorage.SaveResource(certRes)
return true, nil
}
func needRenewal(x509Cert *x509.Certificate, domain string, days int) bool {
if x509Cert.IsCA {
log.Panicf("[%s] Certificate bundle starts with a CA certificate", domain)
}
if days >= 0 {
notAfter := int(time.Until(x509Cert.NotAfter).Hours() / 24.0)
if notAfter > days {
log.Printf("[%s] The certificate expires in %d days, the number of days defined to perform the renewal is %d: no renewal.",
domain, notAfter, days)
return false
}
}
return true
}

View File

@@ -1,4 +1,4 @@
package cmd
package mylego
import (
"crypto/x509"
@@ -116,3 +116,19 @@ func Test_needRenewal(t *testing.T) {
})
}
}
func merge(prevDomains, nextDomains []string) []string {
for _, next := range nextDomains {
var found bool
for _, prev := range prevDomains {
if prev == next {
found = true
break
}
}
if !found {
prevDomains = append(prevDomains, next)
}
}
return prevDomains
}

68
common/mylego/run.go Normal file
View File

@@ -0,0 +1,68 @@
package mylego
import (
"fmt"
"log"
"github.com/go-acme/lego/v4/certificate"
"github.com/go-acme/lego/v4/lego"
"github.com/go-acme/lego/v4/registration"
)
const rootPathWarningMessage = `!!!! HEADS UP !!!!
Your account credentials have been saved in your Let's Encrypt
configuration directory at "%s".
You should make a secure backup of this folder now. This
configuration directory will also contain certificates and
private keys obtained from Let's Encrypt so making regular
backups of this folder is ideal.
`
func (l *LegoCMD) Run() error {
accountsStorage := NewAccountsStorage(l)
account, client := setup(accountsStorage)
setupChallenges(l, client)
if account.Registration == nil {
reg, err := client.Registration.Register(registration.RegisterOptions{TermsOfServiceAgreed: true})
if err != nil {
log.Panicf("Could not complete registration\n\t%v", err)
}
account.Registration = reg
if err = accountsStorage.Save(account); err != nil {
log.Panic(err)
}
fmt.Printf(rootPathWarningMessage, accountsStorage.GetRootPath())
}
certsStorage := NewCertificatesStorage(l.path)
certsStorage.CreateRootFolder()
cert, err := obtainCertificate([]string{l.C.CertDomain}, client)
if err != nil {
// Make sure to return a non-zero exit code if ObtainSANCertificate returned at least one error.
// Due to us not returning partial certificate we can just exit here instead of at the end.
log.Panicf("Could not obtain certificates:\n\t%v", err)
}
certsStorage.SaveResource(cert)
return nil
}
func obtainCertificate(domains []string, client *lego.Client) (*certificate.Resource, error) {
if len(domains) > 0 {
// obtain a certificate, generating a new private key
request := certificate.ObtainRequest{
Domains: domains,
Bundle: true,
}
return client.Certificate.Obtain(request)
}
return nil, fmt.Errorf("not a valid domain")
}

95
common/mylego/setup.go Normal file
View File

@@ -0,0 +1,95 @@
package mylego
import (
"log"
"os"
"time"
"github.com/go-acme/lego/v4/certcrypto"
"github.com/go-acme/lego/v4/challenge/dns01"
"github.com/go-acme/lego/v4/challenge/http01"
"github.com/go-acme/lego/v4/challenge/tlsalpn01"
"github.com/go-acme/lego/v4/lego"
"github.com/go-acme/lego/v4/providers/dns"
"github.com/go-acme/lego/v4/registration"
"golang.org/x/crypto/acme"
)
const filePerm os.FileMode = 0o600
func setup(accountsStorage *AccountsStorage) (*Account, *lego.Client) {
keyType := certcrypto.EC256
privateKey := accountsStorage.GetPrivateKey(keyType)
var account *Account
if accountsStorage.ExistsAccountFilePath() {
account = accountsStorage.LoadAccount(privateKey)
} else {
account = &Account{Email: accountsStorage.GetUserID(), key: privateKey}
}
client := newClient(account, keyType)
return account, client
}
func newClient(acc registration.User, keyType certcrypto.KeyType) *lego.Client {
config := lego.NewConfig(acc)
config.CADirURL = acme.LetsEncryptURL
config.Certificate = lego.CertificateConfig{
KeyType: keyType,
Timeout: 30 * time.Second,
}
config.UserAgent = "lego-cli/dev"
client, err := lego.NewClient(config)
if err != nil {
log.Panicf("Could not create client: %v", err)
}
return client
}
func createNonExistingFolder(path string) error {
if _, err := os.Stat(path); os.IsNotExist(err) {
return os.MkdirAll(path, 0o700)
} else if err != nil {
return err
}
return nil
}
func setupChallenges(l *LegoCMD, client *lego.Client) {
switch l.C.CertMode {
case "http":
err := client.Challenge.SetHTTP01Provider(http01.NewProviderServer("", ""))
if err != nil {
log.Panic(err)
}
case "tls":
err := client.Challenge.SetTLSALPN01Provider(tlsalpn01.NewProviderServer("", ""))
if err != nil {
log.Panic(err)
}
case "dns":
setupDNS(l.C.Provider, client)
default:
log.Panic("No challenge selected. You must specify at least one challenge: `http`, `tls`, `dns`.")
}
}
func setupDNS(p string, client *lego.Client) {
provider, err := dns.NewDNSChallengeProviderByName(p)
if err != nil {
log.Panic(err)
}
err = client.Challenge.SetDNS01Provider(
provider,
dns01.CondOption(true, dns01.AddDNSTimeout(10*time.Second)),
)
if err != nil {
log.Panic(err)
}
}

View File

@@ -2,8 +2,6 @@ package rule
import "github.com/xtls/xray-core/common/errors"
type errPathObjHolder struct{}
func newError(values ...interface{}) *errors.Error {
return errors.New(values...).WithPathObj(errPathObjHolder{})
return errors.New(values...)
}

View File

@@ -8,23 +8,24 @@ import (
"strings"
"sync"
"github.com/XrayR-project/XrayR/api"
mapset "github.com/deckarep/golang-set"
"github.com/XrayR-project/XrayR/api"
)
type RuleManager struct {
type Manager struct {
InboundRule *sync.Map // Key: Tag, Value: []api.DetectRule
InboundDetectResult *sync.Map // key: Tag, Value: mapset.NewSet []api.DetectResult
}
func New() *RuleManager {
return &RuleManager{
func New() *Manager {
return &Manager{
InboundRule: new(sync.Map),
InboundDetectResult: new(sync.Map),
}
}
func (r *RuleManager) UpdateRule(tag string, newRuleList []api.DetectRule) error {
func (r *Manager) UpdateRule(tag string, newRuleList []api.DetectRule) error {
if value, ok := r.InboundRule.LoadOrStore(tag, newRuleList); ok {
oldRuleList := value.([]api.DetectRule)
if !reflect.DeepEqual(oldRuleList, newRuleList) {
@@ -34,7 +35,7 @@ func (r *RuleManager) UpdateRule(tag string, newRuleList []api.DetectRule) error
return nil
}
func (r *RuleManager) GetDetectResult(tag string) (*[]api.DetectResult, error) {
func (r *Manager) GetDetectResult(tag string) (*[]api.DetectResult, error) {
detectResult := make([]api.DetectResult, 0)
if value, ok := r.InboundDetectResult.LoadAndDelete(tag); ok {
resultSet := value.(mapset.Set)
@@ -46,9 +47,9 @@ func (r *RuleManager) GetDetectResult(tag string) (*[]api.DetectResult, error) {
return &detectResult, nil
}
func (r *RuleManager) Detect(tag string, destination string, email string) (reject bool) {
func (r *Manager) Detect(tag string, destination string, email string) (reject bool) {
reject = false
var hitRuleID int = -1
var hitRuleID = -1
// If we have some rule for this inbound
if value, ok := r.InboundRule.Load(tag); ok {
ruleList := value.([]api.DetectRule)

View File

@@ -3,39 +3,51 @@ package serverstatus
import (
"fmt"
"time"
"github.com/shirou/gopsutil/cpu"
"github.com/shirou/gopsutil/disk"
"github.com/shirou/gopsutil/mem"
"github.com/shirou/gopsutil/v3/cpu"
"github.com/shirou/gopsutil/v3/disk"
"github.com/shirou/gopsutil/v3/host"
"github.com/shirou/gopsutil/v3/mem"
)
// GetSystemInfo get the system info of a given periodic
func GetSystemInfo() (Cpu float64, Mem float64, Disk float64, Uptime int, err error) {
func GetSystemInfo() (Cpu float64, Mem float64, Disk float64, Uptime uint64, err error) {
errorString := ""
upTime := time.Now()
cpuPercent, err := cpu.Percent(0, false)
// Check if cpuPercent is empty
if len(cpuPercent) > 0 {
if len(cpuPercent) > 0 && err == nil {
Cpu = cpuPercent[0]
} else {
Cpu = 0
}
if err != nil {
return 0, 0, 0, 0, fmt.Errorf("get cpu usage failed: %s", err)
errorString += fmt.Sprintf("get cpu usage failed: %s ", err)
}
memUsage, err := mem.VirtualMemory()
if err != nil {
return 0, 0, 0, 0, fmt.Errorf("get mem usage failed: %s", err)
errorString += fmt.Sprintf("get mem usage failed: %s ", err)
} else {
Mem = memUsage.UsedPercent
}
diskUsage, err := disk.Usage("/")
if err != nil {
return 0, 0, 0, 0, fmt.Errorf("et disk usage failed: %s", err)
errorString += fmt.Sprintf("get disk usage failed: %s ", err)
} else {
Disk = diskUsage.UsedPercent
}
Uptime = int(time.Since(upTime).Seconds())
return Cpu, memUsage.UsedPercent, diskUsage.UsedPercent, Uptime, nil
uptime, err := host.Uptime()
if err != nil {
errorString += fmt.Sprintf("get uptime failed: %s ", err)
} else {
Uptime = uptime
}
if errorString != "" {
err = fmt.Errorf(errorString)
}
return Cpu, Mem, Disk, Uptime, err
}

278
go.mod
View File

@@ -1,170 +1,232 @@
module github.com/XrayR-project/XrayR
go 1.17
go 1.21
toolchain go1.21.1
require (
github.com/bitly/go-simplejson v0.5.0
github.com/bmizerany/assert v0.0.0-20160611221934-b7ed37b82869 // indirect
dario.cat/mergo v1.0.0
github.com/bitly/go-simplejson v0.5.1
github.com/deckarep/golang-set v1.8.0
github.com/fsnotify/fsnotify v1.5.4
github.com/go-acme/lego/v4 v4.6.0
github.com/go-ole/go-ole v1.2.6 // indirect
github.com/go-resty/resty/v2 v2.7.0
github.com/golang/protobuf v1.5.2 // indirect
github.com/imdario/mergo v0.3.12
github.com/juju/ratelimit v1.0.1
github.com/eko/gocache/lib/v4 v4.1.5
github.com/eko/gocache/store/go_cache/v4 v4.2.1
github.com/eko/gocache/store/redis/v4 v4.2.1
github.com/fsnotify/fsnotify v1.6.0
github.com/go-acme/lego/v4 v4.14.2
github.com/go-resty/resty/v2 v2.9.1
github.com/gogf/gf/v2 v2.5.4
github.com/patrickmn/go-cache v2.1.0+incompatible
github.com/r3labs/diff/v2 v2.15.1
github.com/shirou/gopsutil v3.21.11+incompatible
github.com/spf13/viper v1.11.0
github.com/stretchr/testify v1.7.1
github.com/tklauser/go-sysconf v0.3.10 // indirect
github.com/urfave/cli v1.22.7
github.com/xtls/xray-core v1.5.5
golang.org/x/net v0.0.0-20220421235706-1d1ef9303861
google.golang.org/appengine v1.6.7 // indirect
google.golang.org/protobuf v1.28.0
github.com/redis/go-redis/v9 v9.2.1
github.com/sagernet/sing v0.2.13
github.com/sagernet/sing-shadowsocks v0.2.5
github.com/shirou/gopsutil/v3 v3.23.9
github.com/spf13/viper v1.17.0
github.com/stretchr/testify v1.8.4
github.com/xtls/xray-core v1.8.4
golang.org/x/crypto v0.14.0
golang.org/x/net v0.16.0
golang.org/x/time v0.3.0
google.golang.org/protobuf v1.31.0
)
require (
cloud.google.com/go/compute v1.5.0 // indirect
github.com/Azure/azure-sdk-for-go v62.0.0+incompatible // indirect
cloud.google.com/go/compute v1.23.0 // indirect
cloud.google.com/go/compute/metadata v0.2.3 // indirect
github.com/AdamSLevy/jsonrpc2/v14 v14.1.0 // indirect
github.com/Azure/azure-sdk-for-go v68.0.0+incompatible // indirect
github.com/Azure/azure-sdk-for-go/sdk/azcore v1.6.0 // indirect
github.com/Azure/azure-sdk-for-go/sdk/azidentity v1.3.0 // indirect
github.com/Azure/azure-sdk-for-go/sdk/internal v1.3.0 // indirect
github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/dns/armdns v1.1.0 // indirect
github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/privatedns/armprivatedns v1.1.0 // indirect
github.com/Azure/go-autorest v14.2.0+incompatible // indirect
github.com/Azure/go-autorest/autorest v0.11.24 // indirect
github.com/Azure/go-autorest/autorest/adal v0.9.18 // indirect
github.com/Azure/go-autorest/autorest/azure/auth v0.5.11 // indirect
github.com/Azure/go-autorest/autorest/azure/auth v0.5.12 // indirect
github.com/Azure/go-autorest/autorest/azure/cli v0.4.5 // indirect
github.com/Azure/go-autorest/autorest/date v0.3.0 // indirect
github.com/Azure/go-autorest/autorest/to v0.4.0 // indirect
github.com/Azure/go-autorest/autorest/validation v0.3.1 // indirect
github.com/Azure/go-autorest/logger v0.2.1 // indirect
github.com/Azure/go-autorest/tracing v0.6.0 // indirect
github.com/AzureAD/microsoft-authentication-library-for-go v1.0.0 // indirect
github.com/OpenDNS/vegadns2client v0.0.0-20180418235048-a3fa4a771d87 // indirect
github.com/akamai/AkamaiOPEN-edgegrid-golang v1.1.1 // indirect
github.com/aliyun/alibaba-cloud-sdk-go v1.61.1505 // indirect
github.com/akamai/AkamaiOPEN-edgegrid-golang v1.2.2 // indirect
github.com/aliyun/alibaba-cloud-sdk-go v1.61.1755 // indirect
github.com/andres-erbsen/clock v0.0.0-20160526145045-9e14626cd129 // indirect
github.com/aws/aws-sdk-go v1.43.12 // indirect
github.com/boombuler/barcode v1.0.1 // indirect
github.com/cenkalti/backoff/v4 v4.1.2 // indirect
github.com/cheekybits/genny v1.0.0 // indirect
github.com/cloudflare/cloudflare-go v0.34.0 // indirect
github.com/andybalholm/brotli v1.0.5 // indirect
github.com/aws/aws-sdk-go-v2 v1.19.0 // indirect
github.com/aws/aws-sdk-go-v2/config v1.18.28 // indirect
github.com/aws/aws-sdk-go-v2/credentials v1.13.27 // indirect
github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.13.5 // indirect
github.com/aws/aws-sdk-go-v2/internal/configsources v1.1.35 // indirect
github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.4.29 // indirect
github.com/aws/aws-sdk-go-v2/internal/ini v1.3.36 // indirect
github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.9.29 // indirect
github.com/aws/aws-sdk-go-v2/service/lightsail v1.27.2 // indirect
github.com/aws/aws-sdk-go-v2/service/route53 v1.28.4 // indirect
github.com/aws/aws-sdk-go-v2/service/sso v1.12.13 // indirect
github.com/aws/aws-sdk-go-v2/service/ssooidc v1.14.13 // indirect
github.com/aws/aws-sdk-go-v2/service/sts v1.19.3 // indirect
github.com/aws/smithy-go v1.13.5 // indirect
github.com/beorn7/perks v1.0.1 // indirect
github.com/boombuler/barcode v1.0.1-0.20190219062509-6c824513bacc // indirect
github.com/cenkalti/backoff/v4 v4.2.1 // indirect
github.com/cespare/xxhash/v2 v2.2.0 // indirect
github.com/civo/civogo v0.3.11 // indirect
github.com/cloudflare/cloudflare-go v0.70.0 // indirect
github.com/cpu/goacmedns v0.1.1 // indirect
github.com/cpuguy83/go-md2man/v2 v2.0.1 // indirect
github.com/davecgh/go-spew v1.1.1 // indirect
github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect
github.com/deepmap/oapi-codegen v1.9.1 // indirect
github.com/dgryski/go-metro v0.0.0-20211217172704-adc40b04c140 // indirect
github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f // indirect
github.com/dimchansky/utfbom v1.1.1 // indirect
github.com/dnsimple/dnsimple-go v0.71.1 // indirect
github.com/exoscale/egoscale v1.19.0 // indirect
github.com/dnsimple/dnsimple-go v1.2.0 // indirect
github.com/exoscale/egoscale v0.100.1 // indirect
github.com/fatih/structs v1.1.0 // indirect
github.com/francoispqt/gojay v1.2.13 // indirect
github.com/ghodss/yaml v1.0.1-0.20190212211648-25d852aebe32 // indirect
github.com/go-errors/errors v1.4.2 // indirect
github.com/go-task/slim-sprig v0.0.0-20210107165309-348f09dbbbc0 // indirect
github.com/gofrs/uuid v4.2.0+incompatible // indirect
github.com/golang-jwt/jwt/v4 v4.3.0 // indirect
github.com/gaukas/godicttls v0.0.4 // indirect
github.com/ghodss/yaml v1.0.1-0.20220118164431-d8423dcdf344 // indirect
github.com/go-errors/errors v1.0.1 // indirect
github.com/go-jose/go-jose/v3 v3.0.0 // indirect
github.com/go-ole/go-ole v1.2.6 // indirect
github.com/go-task/slim-sprig v0.0.0-20230315185526-52ccab3ef572 // indirect
github.com/gofrs/uuid v4.4.0+incompatible // indirect
github.com/golang-jwt/jwt/v4 v4.5.0 // indirect
github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da // indirect
github.com/golang/mock v1.6.0 // indirect
github.com/golang/protobuf v1.5.3 // indirect
github.com/google/btree v1.1.2 // indirect
github.com/google/go-querystring v1.1.0 // indirect
github.com/google/uuid v1.3.0 // indirect
github.com/googleapis/gax-go/v2 v2.3.0 // indirect
github.com/gophercloud/gophercloud v0.24.0 // indirect
github.com/gophercloud/utils v0.0.0-20220209210848-d0ab9f2a8909 // indirect
github.com/google/pprof v0.0.0-20230821062121-407c9e7a662f // indirect
github.com/google/s2a-go v0.1.7 // indirect
github.com/google/uuid v1.3.1 // indirect
github.com/googleapis/enterprise-certificate-proxy v0.3.1 // indirect
github.com/googleapis/gax-go/v2 v2.12.0 // indirect
github.com/gophercloud/gophercloud v1.0.0 // indirect
github.com/gophercloud/utils v0.0.0-20210216074907-f6de111f2eae // indirect
github.com/gorilla/websocket v1.5.0 // indirect
github.com/hashicorp/errwrap v1.0.0 // indirect
github.com/hashicorp/go-cleanhttp v0.5.2 // indirect
github.com/hashicorp/go-retryablehttp v0.7.0 // indirect
github.com/hashicorp/go-multierror v1.1.1 // indirect
github.com/hashicorp/go-retryablehttp v0.7.4 // indirect
github.com/hashicorp/hcl v1.0.0 // indirect
github.com/iij/doapi v0.0.0-20190504054126-0bbf12d6d7df // indirect
github.com/infobloxopen/infoblox-go-client v1.1.1 // indirect
github.com/jarcoal/httpmock v1.1.0 // indirect
github.com/jmespath/go-jmespath v0.4.0 // indirect
github.com/json-iterator/go v1.1.12 // indirect
github.com/k0kubun/go-ansi v0.0.0-20180517002512-3bf9e2903213 // indirect
github.com/kolo/xmlrpc v0.0.0-20201022064351-38db28db192b // indirect
github.com/klauspost/compress v1.17.0 // indirect
github.com/klauspost/cpuid/v2 v2.2.5 // indirect
github.com/kolo/xmlrpc v0.0.0-20220921171641-a4b6fa1dd06b // indirect
github.com/kylelemons/godebug v1.1.0 // indirect
github.com/labbsr0x/bindman-dns-webhook v1.0.2 // indirect
github.com/labbsr0x/goh v1.0.1 // indirect
github.com/linode/linodego v1.3.0 // indirect
github.com/linode/linodego v1.17.2 // indirect
github.com/liquidweb/go-lwApi v0.0.5 // indirect
github.com/liquidweb/liquidweb-cli v0.6.10 // indirect
github.com/liquidweb/liquidweb-cli v0.6.9 // indirect
github.com/liquidweb/liquidweb-go v1.6.3 // indirect
github.com/lucas-clemente/quic-go v0.27.0 // indirect
github.com/magiconair/properties v1.8.6 // indirect
github.com/marten-seemann/qtls-go1-16 v0.1.5 // indirect
github.com/marten-seemann/qtls-go1-17 v0.1.1 // indirect
github.com/marten-seemann/qtls-go1-18 v0.1.1 // indirect
github.com/mattn/go-isatty v0.0.14 // indirect
github.com/miekg/dns v1.1.48 // indirect
github.com/lufia/plan9stats v0.0.0-20211012122336-39d0f177ccd0 // indirect
github.com/magiconair/properties v1.8.7 // indirect
github.com/mattn/go-isatty v0.0.19 // indirect
github.com/matttproud/golang_protobuf_extensions v1.0.1 // indirect
github.com/miekg/dns v1.1.55 // indirect
github.com/mimuret/golang-iij-dpf v0.9.1 // indirect
github.com/mitchellh/go-homedir v1.1.0 // indirect
github.com/mitchellh/mapstructure v1.4.3 // indirect
github.com/mitchellh/mapstructure v1.5.0 // indirect
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect
github.com/modern-go/reflect2 v1.0.2 // indirect
github.com/namedotcom/go v0.0.0-20180403034216-08470befbe04 // indirect
github.com/nrdcg/auroradns v1.0.1 // indirect
github.com/nrdcg/desec v0.6.0 // indirect
github.com/nrdcg/auroradns v1.1.0 // indirect
github.com/nrdcg/bunny-go v0.0.0-20230728143221-c9dda82568d9 // indirect
github.com/nrdcg/desec v0.7.0 // indirect
github.com/nrdcg/dnspod-go v0.4.0 // indirect
github.com/nrdcg/freemyip v0.2.0 // indirect
github.com/nrdcg/goinwx v0.8.1 // indirect
github.com/nrdcg/goinwx v0.8.2 // indirect
github.com/nrdcg/namesilo v0.2.1 // indirect
github.com/nrdcg/porkbun v0.1.1 // indirect
github.com/nxadm/tail v1.4.8 // indirect
github.com/onsi/ginkgo v1.16.5 // indirect
github.com/nrdcg/nodion v0.1.0 // indirect
github.com/nrdcg/porkbun v0.2.0 // indirect
github.com/nzdjb/go-metaname v1.0.0 // indirect
github.com/onsi/ginkgo/v2 v2.12.0 // indirect
github.com/oracle/oci-go-sdk v24.3.0+incompatible // indirect
github.com/ovh/go-ovh v1.1.0 // indirect
github.com/patrickmn/go-cache v2.1.0+incompatible // indirect
github.com/ovh/go-ovh v1.4.2 // indirect
github.com/pelletier/go-toml v1.9.5 // indirect
github.com/pelletier/go-toml/v2 v2.0.0-beta.8 // indirect
github.com/pires/go-proxyproto v0.6.2 // indirect
github.com/pelletier/go-toml/v2 v2.1.0 // indirect
github.com/pires/go-proxyproto v0.7.0 // indirect
github.com/pkg/browser v0.0.0-20210911075715-681adbf594b8 // indirect
github.com/pkg/errors v0.9.1 // indirect
github.com/pmezard/go-difflib v1.0.0 // indirect
github.com/pquerna/otp v1.3.0 // indirect
github.com/rainycape/memcache v0.0.0-20150622160815-1031fa0ce2f2 // indirect
github.com/refraction-networking/utls v1.0.0 // indirect
github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 // indirect
github.com/power-devops/perfstat v0.0.0-20210106213030-5aafc221ea8c // indirect
github.com/pquerna/otp v1.4.0 // indirect
github.com/prometheus/client_golang v1.14.0 // indirect
github.com/prometheus/client_model v0.3.0 // indirect
github.com/prometheus/common v0.37.0 // indirect
github.com/prometheus/procfs v0.8.0 // indirect
github.com/quic-go/qtls-go1-20 v0.3.3 // indirect
github.com/quic-go/quic-go v0.38.1 // indirect
github.com/refraction-networking/utls v1.4.3 // indirect
github.com/riobard/go-bloom v0.0.0-20200614022211-cdc8013cb5b3 // indirect
github.com/rogpeppe/go-internal v1.8.1 // indirect
github.com/russross/blackfriday/v2 v2.1.0 // indirect
github.com/sacloud/libsacloud v1.36.2 // indirect
github.com/scaleway/scaleway-sdk-go v1.0.0-beta.9 // indirect
github.com/sacloud/api-client-go v0.2.8 // indirect
github.com/sacloud/go-http v0.1.6 // indirect
github.com/sacloud/iaas-api-go v1.11.1 // indirect
github.com/sacloud/packages-go v0.0.9 // indirect
github.com/sagernet/wireguard-go v0.0.0-20221116151939-c99467f53f2c // indirect
github.com/sagikazarmark/locafero v0.3.0 // indirect
github.com/sagikazarmark/slog-shim v0.1.0 // indirect
github.com/scaleway/scaleway-sdk-go v1.0.0-beta.17 // indirect
github.com/seiflotfy/cuckoofilter v0.0.0-20220411075957-e3b120b3f5fb // indirect
github.com/shoenig/go-m1cpu v0.1.6 // indirect
github.com/sirupsen/logrus v1.8.1 // indirect
github.com/smartystreets/go-aws-auth v0.0.0-20180515143844-0c1422d1fdb9 // indirect
github.com/softlayer/softlayer-go v1.0.4 // indirect
github.com/softlayer/softlayer-go v1.1.2 // indirect
github.com/softlayer/xmlrpc v0.0.0-20200409220501-5f089df7cb7e // indirect
github.com/spf13/afero v1.8.2 // indirect
github.com/spf13/cast v1.4.1 // indirect
github.com/spf13/jwalterweatherman v1.1.0 // indirect
github.com/sourcegraph/conc v0.3.0 // indirect
github.com/spf13/afero v1.10.0 // indirect
github.com/spf13/cast v1.5.1 // indirect
github.com/spf13/pflag v1.0.5 // indirect
github.com/stretchr/objx v0.3.0 // indirect
github.com/subosito/gotenv v1.2.0 // indirect
github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/common v1.0.360 // indirect
github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/dnspod v1.0.360 // indirect
github.com/tklauser/numcpus v0.4.0 // indirect
github.com/transip/gotransip/v6 v6.14.0 // indirect
github.com/stretchr/objx v0.5.0 // indirect
github.com/subosito/gotenv v1.6.0 // indirect
github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/common v1.0.490 // indirect
github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/dnspod v1.0.490 // indirect
github.com/tklauser/go-sysconf v0.3.12 // indirect
github.com/tklauser/numcpus v0.6.1 // indirect
github.com/transip/gotransip/v6 v6.20.0 // indirect
github.com/ultradns/ultradns-go-sdk v1.5.0-20230427130837-23c9b0c // indirect
github.com/v2fly/ss-bloomring v0.0.0-20210312155135-28617310f63e // indirect
github.com/vinyldns/go-vinyldns v0.9.16 // indirect
github.com/vmihailenco/msgpack v4.0.4+incompatible // indirect
github.com/vultr/govultr/v2 v2.14.1 // indirect
github.com/xtls/go v0.0.0-20210920065950-d4af136d3672 // indirect
github.com/yusufpapurcu/wmi v1.2.2 // indirect
go.opencensus.io v0.23.0 // indirect
go.starlark.net v0.0.0-20220328144851-d1966c6b9fcd // indirect
github.com/vmihailenco/msgpack/v5 v5.3.5 // indirect
github.com/vmihailenco/tagparser/v2 v2.0.0 // indirect
github.com/vultr/govultr/v2 v2.17.2 // indirect
github.com/xtls/reality v0.0.0-20230828171259-e426190d57f6 // indirect
github.com/yandex-cloud/go-genproto v0.0.0-20220805142335-27b56ddae16f // indirect
github.com/yandex-cloud/go-sdk v0.0.0-20220805164847-cf028e604997 // indirect
github.com/yusufpapurcu/wmi v1.2.3 // indirect
go.opencensus.io v0.24.0 // indirect
go.opentelemetry.io/otel v1.14.0 // indirect
go.opentelemetry.io/otel/trace v1.14.0 // indirect
go.uber.org/atomic v1.11.0 // indirect
go.uber.org/multierr v1.9.0 // indirect
go.uber.org/ratelimit v0.2.0 // indirect
golang.org/x/crypto v0.0.0-20220411220226-7b82a4e95df4 // indirect
golang.org/x/mod v0.6.0-dev.0.20220106191415-9b9b3d81d5e3 // indirect
golang.org/x/oauth2 v0.0.0-20220411215720-9780585627b5 // indirect
golang.org/x/sys v0.0.0-20220422013727-9388b58f7150 // indirect
golang.org/x/text v0.3.7 // indirect
golang.org/x/time v0.0.0-20220224211638-0e9765cccd65 // indirect
golang.org/x/tools v0.1.10 // indirect
golang.org/x/xerrors v0.0.0-20220411194840-2f41105eb62f // indirect
google.golang.org/api v0.74.0 // indirect
google.golang.org/genproto v0.0.0-20220422154200-b37d22cd5731 // indirect
google.golang.org/grpc v1.46.0 // indirect
gopkg.in/ini.v1 v1.66.4 // indirect
gopkg.in/ns1/ns1-go.v2 v2.6.3 // indirect
gopkg.in/square/go-jose.v2 v2.6.0 // indirect
gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 // indirect
go4.org/netipx v0.0.0-20230824141953-6213f710f925 // indirect
golang.org/x/exp v0.0.0-20230905200255-921286631fa9 // indirect
golang.org/x/mod v0.12.0 // indirect
golang.org/x/oauth2 v0.12.0 // indirect
golang.org/x/sys v0.13.0 // indirect
golang.org/x/text v0.13.0 // indirect
golang.org/x/tools v0.13.0 // indirect
google.golang.org/api v0.143.0 // indirect
google.golang.org/appengine v1.6.7 // indirect
google.golang.org/genproto v0.0.0-20230913181813-007df8e322eb // indirect
google.golang.org/genproto/googleapis/api v0.0.0-20230913181813-007df8e322eb // indirect
google.golang.org/genproto/googleapis/rpc v0.0.0-20230920204549-e6e6cdab5c13 // indirect
google.golang.org/grpc v1.58.2 // indirect
gopkg.in/ini.v1 v1.67.0 // indirect
gopkg.in/ns1/ns1-go.v2 v2.7.6 // indirect
gopkg.in/yaml.v2 v2.4.0 // indirect
gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b // indirect
gopkg.in/yaml.v3 v3.0.1 // indirect
gvisor.dev/gvisor v0.0.0-20230822212503-5bf4e5f98744 // indirect
lukechampine.com/blake3 v1.2.1 // indirect
)
replace github.com/linode/linodego => github.com/linode/linodego v0.31.1
replace github.com/exoscale/egoscale => github.com/exoscale/egoscale v0.67.0
replace github.com/exoscale/egoscale => github.com/exoscale/egoscale v0.102.0

911
go.sum

File diff suppressed because it is too large Load Diff

View File

@@ -6,15 +6,14 @@ DnsConfigPath: # /etc/XrayR/dns.json # Path to dns config, check https://xtls.gi
RouteConfigPath: # /etc/XrayR/route.json # Path to route config, check https://xtls.github.io/config/routing.html for help
InboundConfigPath: # /etc/XrayR/custom_inbound.json # Path to custom inbound config, check https://xtls.github.io/config/inbound.html for help
OutboundConfigPath: # /etc/XrayR/custom_outbound.json # Path to custom outbound config, check https://xtls.github.io/config/outbound.html for help
ConnetionConfig:
ConnectionConfig:
Handshake: 4 # Handshake time limit, Second
ConnIdle: 30 # Connection idle time limit, Second
UplinkOnly: 2 # Time limit when the connection downstream is closed, Second
DownlinkOnly: 4 # Time limit when the connection is closed after the uplink is closed, Second
BufferSize: 64 # The internal cache size of each connection, kB
BufferSize: 64 # The internal cache size of each connection, kB
Nodes:
-
PanelType: "SSpanel" # Panel type: SSpanel, V2board, PMpanel, , Proxypanel
- PanelType: "SSpanel" # Panel type: SSpanel, NewV2board, PMpanel, Proxypanel, V2RaySocks, GoV2Panel
ApiConfig:
ApiHost: "http://127.0.0.1:667"
ApiKey: "123"
@@ -22,10 +21,11 @@ Nodes:
NodeType: V2ray # Node type: V2ray, Shadowsocks, Trojan, Shadowsocks-Plugin
Timeout: 30 # Timeout for the api request
EnableVless: false # Enable Vless for V2ray Type
EnableXTLS: false # Enable XTLS for V2ray and Trojan
VlessFlow: "xtls-rprx-vision" # Only support vless
SpeedLimit: 0 # Mbps, Local settings will replace remote settings, 0 means disable
DeviceLimit: 0 # Local settings will replace remote settings, 0 means disable
RuleListPath: # /etc/XrayR/rulelist Path to local rulelist file
DisableCustomConfig: false # disable custom config for sspanel
ControllerConfig:
ListenIP: 0.0.0.0 # IP address you want to listen
SendIP: 0.0.0.0 # IP address you want to send pacakage
@@ -33,16 +33,42 @@ Nodes:
EnableDNS: false # Use custom DNS config, Please ensure that you set the dns.json well
DNSType: AsIs # AsIs, UseIP, UseIPv4, UseIPv6, DNS strategy
EnableProxyProtocol: false # Only works for WebSocket and TCP
AutoSpeedLimitConfig:
Limit: 0 # Warned speed. Set to 0 to disable AutoSpeedLimit (mbps)
WarnTimes: 0 # After (WarnTimes) consecutive warnings, the user will be limited. Set to 0 to punish overspeed user immediately.
LimitSpeed: 0 # The speedlimit of a limited user (unit: mbps)
LimitDuration: 0 # How many minutes will the limiting last (unit: minute)
GlobalDeviceLimitConfig:
Enable: false # Enable the global device limit of a user
RedisAddr: 127.0.0.1:6379 # The redis server address
RedisPassword: YOUR PASSWORD # Redis password
RedisDB: 0 # Redis DB
Timeout: 5 # Timeout for redis request
Expiry: 60 # Expiry time (second)
EnableFallback: false # Only support for Trojan and Vless
FallBackConfigs: # Support multiple fallbacks
-
SNI: # TLS SNI(Server Name Indication), Empty for any
- SNI: # TLS SNI(Server Name Indication), Empty for any
Alpn: # Alpn, Empty for any
Path: # HTTP PATH, Empty for any
Dest: 80 # Required, Destination of fallback, check https://xtls.github.io/config/features/fallback.html for details.
ProxyProtocolVer: 0 # Send PROXY protocol version, 0 for dsable
ProxyProtocolVer: 0 # Send PROXY protocol version, 0 for disable
DisableLocalREALITYConfig: false # disable local reality config
EnableREALITY: true # Enable REALITY
REALITYConfigs:
Show: true # Show REALITY debug
Dest: www.smzdm.com:443 # Required, Same as fallback
ProxyProtocolVer: 0 # Send PROXY protocol version, 0 for disable
ServerNames: # Required, list of available serverNames for the client, * wildcard is not supported at the moment.
- www.smzdm.com
PrivateKey: YOUR_PRIVATE_KEY # Required, execute './xray x25519' to generate.
MinClientVer: # Optional, minimum version of Xray client, format is x.y.z.
MaxClientVer: # Optional, maximum version of Xray client, format is x.y.z.
MaxTimeDiff: 0 # Optional, maximum allowed time difference, unit is in milliseconds.
ShortIds: # Required, list of available shortIds for the client, can be used to differentiate between different clients.
- ""
- 0123456789abcdef
CertConfig:
CertMode: dns # Option about how to get certificate: none, file, http, dns. Choose "none" will forcedly disable the tls config.
CertMode: dns # Option about how to get certificate: none, file, http, tls, dns. Choose "none" will forcedly disable the tls config.
CertDomain: "node1.test.com" # Domain to cert
CertFile: /etc/XrayR/cert/node1.test.com.cert # Provided if the CertMode is file
KeyFile: /etc/XrayR/cert/node1.test.com.key
@@ -51,30 +77,66 @@ Nodes:
DNSEnv: # DNS ENV option used by DNS provider
ALICLOUD_ACCESS_KEY: aaa
ALICLOUD_SECRET_KEY: bbb
# -
# PanelType: "V2board" # Panel type: SSpanel, V2board
# ApiConfig:
# ApiHost: "http://127.0.0.1:668"
# ApiKey: "123"
# NodeID: 4
# NodeType: Shadowsocks # Node type: V2ray, Shadowsocks, Trojan
# Timeout: 30 # Timeout for the api request
# EnableVless: false # Enable Vless for V2ray Type
# EnableXTLS: false # Enable XTLS for V2ray and Trojan
# SpeedLimit: 0 # Mbps, Local settings will replace remote settings
# DeviceLimit: 0 # Local settings will replace remote settings
# ControllerConfig:
# ListenIP: 0.0.0.0 # IP address you want to listen
# UpdatePeriodic: 10 # Time to update the nodeinfo, how many sec.
# EnableDNS: false # Use custom DNS config, Please ensure that you set the dns.json well
# CertConfig:
# CertMode: dns # Option about how to get certificate: none, file, http, dns
# CertDomain: "node1.test.com" # Domain to cert
# CertFile: /etc/XrayR/cert/node1.test.com.cert # Provided if the CertMode is file
# KeyFile: /etc/XrayR/cert/node1.test.com.pem
# Provider: alidns # DNS cert provider, Get the full support list here: https://go-acme.github.io/lego/dns/
# Email: test@me.com
# DNSEnv: # DNS ENV option used by DNS provider
# ALICLOUD_ACCESS_KEY: aaa
# ALICLOUD_SECRET_KEY: bbb
# - PanelType: "SSpanel" # Panel type: SSpanel, V2board, NewV2board, PMpanel, Proxypanel, V2RaySocks, GoV2Panel
# ApiConfig:
# ApiHost: "http://127.0.0.1:668"
# ApiKey: "123"
# NodeID: 41
# NodeType: V2ray # Node type: V2ray, Shadowsocks, Trojan, Shadowsocks-Plugin
# Timeout: 30 # Timeout for the api request
# EnableVless: false # Enable Vless for V2ray Type
# VlessFlow: "xtls-rprx-vision" # Only support vless
# SpeedLimit: 0 # Mbps, Local settings will replace remote settings, 0 means disable
# DeviceLimit: 0 # Local settings will replace remote settings, 0 means disable
# RuleListPath: # /etc/XrayR/rulelist Path to local rulelist file
# ControllerConfig:
# ListenIP: 0.0.0.0 # IP address you want to listen
# SendIP: 0.0.0.0 # IP address you want to send pacakage
# UpdatePeriodic: 60 # Time to update the nodeinfo, how many sec.
# EnableDNS: false # Use custom DNS config, Please ensure that you set the dns.json well
# DNSType: AsIs # AsIs, UseIP, UseIPv4, UseIPv6, DNS strategy
# EnableProxyProtocol: false # Only works for WebSocket and TCP
# AutoSpeedLimitConfig:
# Limit: 0 # Warned speed. Set to 0 to disable AutoSpeedLimit (mbps)
# WarnTimes: 0 # After (WarnTimes) consecutive warnings, the user will be limited. Set to 0 to punish overspeed user immediately.
# LimitSpeed: 0 # The speedlimit of a limited user (unit: mbps)
# LimitDuration: 0 # How many minutes will the limiting last (unit: minute)
# GlobalDeviceLimitConfig:
# Enable: false # Enable the global device limit of a user
# RedisAddr: 127.0.0.1:6379 # The redis server address
# RedisPassword: YOUR PASSWORD # Redis password
# RedisDB: 0 # Redis DB
# Timeout: 5 # Timeout for redis request
# Expiry: 60 # Expiry time (second)
# EnableFallback: false # Only support for Trojan and Vless
# FallBackConfigs: # Support multiple fallbacks
# - SNI: # TLS SNI(Server Name Indication), Empty for any
# Alpn: # Alpn, Empty for any
# Path: # HTTP PATH, Empty for any
# Dest: 80 # Required, Destination of fallback, check https://xtls.github.io/config/features/fallback.html for details.
# ProxyProtocolVer: 0 # Send PROXY protocol version, 0 for disable
# EnableREALITY: true # Enable REALITY
# REALITYConfigs:
# Show: true # Show REALITY debug
# Dest: www.smzdm.com:443 # Required, Same as fallback
# ProxyProtocolVer: 0 # Send PROXY protocol version, 0 for disable
# ServerNames: # Required, list of available serverNames for the client, * wildcard is not supported at the moment.
# - www.smzdm.com
# PrivateKey: YOUR_PRIVATE_KEY # Required, execute './xray x25519' to generate.
# MinClientVer: # Optional, minimum version of Xray client, format is x.y.z.
# MaxClientVer: # Optional, maximum version of Xray client, format is x.y.z.
# MaxTimeDiff: 0 # Optional, maximum allowed time difference, unit is in milliseconds.
# ShortIds: # Required, list of available shortIds for the client, can be used to differentiate between different clients.
# - ""
# - 0123456789abcdef
# CertConfig:
# CertMode: dns # Option about how to get certificate: none, file, http, tls, dns. Choose "none" will forcedly disable the tls config.
# CertDomain: "node1.test.com" # Domain to cert
# CertFile: /etc/XrayR/cert/node1.test.com.cert # Provided if the CertMode is file
# KeyFile: /etc/XrayR/cert/node1.test.com.key
# Provider: alidns # DNS cert provider, Get the full support list here: https://go-acme.github.io/lego/dns/
# Email: test@me.com
# DNSEnv: # DNS ENV option used by DNS provider
# ALICLOUD_ACCESS_KEY: aaa
# ALICLOUD_SECRET_KEY: bbb

View File

@@ -1,19 +1,19 @@
[
{
"listen": "0.0.0.0",
"port": 1234,
"protocol": "socks",
"settings": {
"auth": "noauth",
"accounts": [
{
"user": "my-username",
"pass": "my-password"
}
],
"udp": false,
"ip": "127.0.0.1",
"userLevel": 0
{
"listen": "0.0.0.0",
"port": 1234,
"protocol": "socks",
"settings": {
"auth": "noauth",
"accounts": [
{
"user": "my-username",
"pass": "my-password"
}
],
"udp": false,
"ip": "127.0.0.1",
"userLevel": 0
}
}
]

View File

@@ -1,17 +1,30 @@
[
{
"tag": "IPv4_out",
"protocol": "freedom"
},
{
"tag": "IPv6_out",
"protocol": "freedom",
"settings": {
"domainStrategy": "UseIPv6"
}
},
{
"protocol": "blackhole",
"tag": "block"
{
"tag": "IPv4_out",
"protocol": "freedom",
"settings": {}
},
{
"tag": "IPv6_out",
"protocol": "freedom",
"settings": {
"domainStrategy": "UseIPv6"
}
},
{
"tag": "socks5-warp",
"protocol": "socks",
"settings": {
"servers": [
{
"address": "127.0.0.1",
"port": 1080
}
]
}
},
{
"protocol": "blackhole",
"tag": "block"
}
]

View File

@@ -3,11 +3,13 @@ package all
import (
// The following are necessary as they register handlers in their init functions.
// Required features. Can't remove unless there is replacements.
// _ "github.com/xtls/xray-core/app/dispatcher"
_ "github.com/xtls/xray-core/app/proxyman/inbound"
_ "github.com/xtls/xray-core/app/proxyman/outbound"
// Required features. Can't remove unless there is replacements.
// _ "github.com/xtls/xray-core/app/dispatcher"
_ "github.com/XrayR-project/XrayR/app/mydispatcher"
// Default commander and all its services. This is an optional feature.
_ "github.com/xtls/xray-core/app/commander"
_ "github.com/xtls/xray-core/app/log/command"
@@ -17,6 +19,7 @@ import (
// Other optional features.
_ "github.com/xtls/xray-core/app/dns"
_ "github.com/xtls/xray-core/app/log"
_ "github.com/xtls/xray-core/app/metrics"
_ "github.com/xtls/xray-core/app/policy"
_ "github.com/xtls/xray-core/app/reverse"
_ "github.com/xtls/xray-core/app/router"
@@ -28,7 +31,6 @@ import (
_ "github.com/xtls/xray-core/proxy/dokodemo"
_ "github.com/xtls/xray-core/proxy/freedom"
_ "github.com/xtls/xray-core/proxy/http"
_ "github.com/xtls/xray-core/proxy/mtproto"
_ "github.com/xtls/xray-core/proxy/shadowsocks"
_ "github.com/xtls/xray-core/proxy/socks"
_ "github.com/xtls/xray-core/proxy/trojan"
@@ -42,11 +44,11 @@ import (
_ "github.com/xtls/xray-core/transport/internet/http"
_ "github.com/xtls/xray-core/transport/internet/kcp"
_ "github.com/xtls/xray-core/transport/internet/quic"
_ "github.com/xtls/xray-core/transport/internet/reality"
_ "github.com/xtls/xray-core/transport/internet/tcp"
_ "github.com/xtls/xray-core/transport/internet/tls"
_ "github.com/xtls/xray-core/transport/internet/udp"
_ "github.com/xtls/xray-core/transport/internet/websocket"
_ "github.com/xtls/xray-core/transport/internet/xtls"
// Transport headers
_ "github.com/xtls/xray-core/transport/internet/headers/http"

View File

@@ -1,8 +1,8 @@
{
"servers": [
"1.1.1.1",
"8.8.8.8",
"localhost"
],
"tag": "dns_inbound"
"servers": [
"1.1.1.1",
"8.8.8.8",
"localhost"
],
"tag": "dns_inbound"
}

Binary file not shown.

File diff suppressed because one or more lines are too long

View File

@@ -12,9 +12,10 @@ import (
"syscall"
"time"
"github.com/XrayR-project/XrayR/panel"
"github.com/fsnotify/fsnotify"
"github.com/spf13/viper"
"github.com/XrayR-project/XrayR/panel"
)
var (
@@ -23,7 +24,7 @@ var (
)
var (
version = "0.8.0"
version = "0.9.1"
codename = "XrayR"
intro = "A Xray backend that supports many panels"
)
@@ -56,7 +57,7 @@ func getConfig() *viper.Viper {
}
if err := config.ReadInConfig(); err != nil {
log.Panicf("Fatal error config file: %s \n", err)
log.Panicf("Config file error: %s \n", err)
}
config.WatchConfig() // Watch the config
@@ -73,7 +74,9 @@ func main() {
config := getConfig()
panelConfig := &panel.Config{}
config.Unmarshal(panelConfig)
if err := config.Unmarshal(panelConfig); err != nil {
log.Panicf("Parse config file %v failed: %s \n", configFile, err)
}
p := panel.New(panelConfig)
lastTime := time.Now()
config.OnConfigChange(func(e fsnotify.Event) {
@@ -84,7 +87,9 @@ func main() {
p.Close()
// Delete old instance and trigger GC
runtime.GC()
config.Unmarshal(panelConfig)
if err := config.Unmarshal(panelConfig); err != nil {
log.Panicf("Parse config file %v failed: %s \n", configFile, err)
}
p.Start()
lastTime = time.Now()
}
@@ -92,7 +97,7 @@ func main() {
p.Start()
defer p.Close()
//Explicitly triggering GC to remove garbage from config loading.
// Explicitly triggering GC to remove garbage from config loading.
runtime.GC()
// Running backend
{

View File

@@ -1,26 +1,36 @@
{
"domainStrategy": "IPOnDemand",
"rules": [
{
"type": "field",
"outboundTag": "block",
"ip": [
"geoip:private"
]
},
{
"type": "field",
"outboundTag": "block",
"protocol": [
"bittorrent"
]
},
{
"type": "field",
"outboundTag": "IPv6_out",
"domain": [
"geosite:netflix"
]
}
]
"domainStrategy": "IPOnDemand",
"rules": [
{
"type": "field",
"outboundTag": "block",
"ip": [
"geoip:private"
]
},
{
"type": "field",
"outboundTag": "block",
"protocol": [
"bittorrent"
]
},
{
"type": "field",
"outboundTag": "socks5-warp",
"domain": []
},
{
"type": "field",
"outboundTag": "IPv6_out",
"domain": [
"geosite:netflix"
]
},
{
"type": "field",
"outboundTag": "IPv4_out",
"network": "udp,tcp"
}
]
}

View File

@@ -6,13 +6,13 @@ import (
)
type Config struct {
LogConfig *LogConfig `mapstructure:"Log"`
DnsConfigPath string `mapstructure:"DnsConfigPath"`
InboundConfigPath string `mapstructure:"InboundConfigPath"`
OutboundConfigPath string `mapstructure:"OutboundConfigPath"`
RouteConfigPath string `mapstructure:"RouteConfigPath"`
ConnetionConfig *ConnetionConfig `mapstructure:"ConnetionConfig"`
NodesConfig []*NodesConfig `mapstructure:"Nodes"`
LogConfig *LogConfig `mapstructure:"Log"`
DnsConfigPath string `mapstructure:"DnsConfigPath"`
InboundConfigPath string `mapstructure:"InboundConfigPath"`
OutboundConfigPath string `mapstructure:"OutboundConfigPath"`
RouteConfigPath string `mapstructure:"RouteConfigPath"`
ConnectionConfig *ConnectionConfig `mapstructure:"ConnectionConfig"`
NodesConfig []*NodesConfig `mapstructure:"Nodes"`
}
type NodesConfig struct {
@@ -27,7 +27,7 @@ type LogConfig struct {
ErrorPath string `mapstructure:"ErrorPath"`
}
type ConnetionConfig struct {
type ConnectionConfig struct {
Handshake uint32 `mapstructure:"handshake"`
ConnIdle uint32 `mapstructure:"connIdle"`
UplinkOnly uint32 `mapstructure:"uplinkOnly"`

View File

@@ -10,8 +10,8 @@ func getDefaultLogConfig() *LogConfig {
}
}
func getDefaultConnetionConfig() *ConnetionConfig {
return &ConnetionConfig{
func getDefaultConnectionConfig() *ConnectionConfig {
return &ConnectionConfig{
Handshake: 4,
ConnIdle: 30,
UplinkOnly: 2,

View File

@@ -2,26 +2,30 @@ package panel
import (
"encoding/json"
io "io/ioutil"
"log"
"os"
"sync"
"github.com/XrayR-project/XrayR/api"
"github.com/XrayR-project/XrayR/api/pmpanel"
"github.com/XrayR-project/XrayR/api/proxypanel"
"github.com/XrayR-project/XrayR/api/sspanel"
"github.com/XrayR-project/XrayR/api/v2board"
"github.com/XrayR-project/XrayR/api/gov2panel"
"github.com/XrayR-project/XrayR/api/newV2board"
"github.com/XrayR-project/XrayR/app/mydispatcher"
_ "github.com/XrayR-project/XrayR/main/distro/all"
"github.com/XrayR-project/XrayR/service"
"github.com/XrayR-project/XrayR/service/controller"
"github.com/imdario/mergo"
"dario.cat/mergo"
"github.com/r3labs/diff/v2"
"github.com/xtls/xray-core/app/proxyman"
"github.com/xtls/xray-core/app/stats"
"github.com/xtls/xray-core/common/serial"
"github.com/xtls/xray-core/core"
"github.com/xtls/xray-core/infra/conf"
"github.com/XrayR-project/XrayR/api"
"github.com/XrayR-project/XrayR/api/pmpanel"
"github.com/XrayR-project/XrayR/api/proxypanel"
"github.com/XrayR-project/XrayR/api/sspanel"
"github.com/XrayR-project/XrayR/api/v2raysocks"
_ "github.com/XrayR-project/XrayR/main/distro/all"
"github.com/XrayR-project/XrayR/service"
"github.com/XrayR-project/XrayR/service/controller"
)
// Panel Structure
@@ -54,7 +58,7 @@ func (p *Panel) loadCore(panelConfig *Config) *core.Instance {
// DNS config
coreDnsConfig := &conf.DNSConfig{}
if panelConfig.DnsConfigPath != "" {
if data, err := io.ReadFile(panelConfig.DnsConfigPath); err != nil {
if data, err := os.ReadFile(panelConfig.DnsConfigPath); err != nil {
log.Panicf("Failed to read DNS config file at: %s", panelConfig.DnsConfigPath)
} else {
if err = json.Unmarshal(data, coreDnsConfig); err != nil {
@@ -62,14 +66,21 @@ func (p *Panel) loadCore(panelConfig *Config) *core.Instance {
}
}
}
// init controller's DNS config
// for _, config := range p.panelConfig.NodesConfig {
// config.ControllerConfig.DNSConfig = coreDnsConfig
// }
dnsConfig, err := coreDnsConfig.Build()
if err != nil {
log.Panicf("Failed to understand DNS config, Please check: https://xtls.github.io/config/dns.html for help: %s", err)
}
// Routing config
coreRouterConfig := &conf.RouterConfig{}
if panelConfig.RouteConfigPath != "" {
if data, err := io.ReadFile(panelConfig.RouteConfigPath); err != nil {
if data, err := os.ReadFile(panelConfig.RouteConfigPath); err != nil {
log.Panicf("Failed to read Routing config file at: %s", panelConfig.RouteConfigPath)
} else {
if err = json.Unmarshal(data, coreRouterConfig); err != nil {
@@ -82,9 +93,9 @@ func (p *Panel) loadCore(panelConfig *Config) *core.Instance {
log.Panicf("Failed to understand Routing config Please check: https://xtls.github.io/config/routing.html for help: %s", err)
}
// Custom Inbound config
coreCustomInboundConfig := []conf.InboundDetourConfig{}
var coreCustomInboundConfig []conf.InboundDetourConfig
if panelConfig.InboundConfigPath != "" {
if data, err := io.ReadFile(panelConfig.InboundConfigPath); err != nil {
if data, err := os.ReadFile(panelConfig.InboundConfigPath); err != nil {
log.Panicf("Failed to read Custom Inbound config file at: %s", panelConfig.OutboundConfigPath)
} else {
if err = json.Unmarshal(data, &coreCustomInboundConfig); err != nil {
@@ -92,7 +103,7 @@ func (p *Panel) loadCore(panelConfig *Config) *core.Instance {
}
}
}
inBoundConfig := []*core.InboundHandlerConfig{}
var inBoundConfig []*core.InboundHandlerConfig
for _, config := range coreCustomInboundConfig {
oc, err := config.Build()
if err != nil {
@@ -101,9 +112,9 @@ func (p *Panel) loadCore(panelConfig *Config) *core.Instance {
inBoundConfig = append(inBoundConfig, oc)
}
// Custom Outbound config
coreCustomOutboundConfig := []conf.OutboundDetourConfig{}
var coreCustomOutboundConfig []conf.OutboundDetourConfig
if panelConfig.OutboundConfigPath != "" {
if data, err := io.ReadFile(panelConfig.OutboundConfigPath); err != nil {
if data, err := os.ReadFile(panelConfig.OutboundConfigPath); err != nil {
log.Panicf("Failed to read Custom Outbound config file at: %s", panelConfig.OutboundConfigPath)
} else {
if err = json.Unmarshal(data, &coreCustomOutboundConfig); err != nil {
@@ -111,7 +122,7 @@ func (p *Panel) loadCore(panelConfig *Config) *core.Instance {
}
}
}
outBoundConfig := []*core.OutboundHandlerConfig{}
var outBoundConfig []*core.OutboundHandlerConfig
for _, config := range coreCustomOutboundConfig {
oc, err := config.Build()
if err != nil {
@@ -120,7 +131,7 @@ func (p *Panel) loadCore(panelConfig *Config) *core.Instance {
outBoundConfig = append(outBoundConfig, oc)
}
// Policy config
levelPolicyConfig := parseConnectionConfig(panelConfig.ConnetionConfig)
levelPolicyConfig := parseConnectionConfig(panelConfig.ConnectionConfig)
corePolicyConfig := &conf.PolicyConfig{}
corePolicyConfig.Levels = map[uint32]*conf.Policy{0: levelPolicyConfig}
policyConfig, _ := corePolicyConfig.Build()
@@ -148,7 +159,7 @@ func (p *Panel) loadCore(panelConfig *Config) *core.Instance {
return server
}
// Start Start the panel
// Start the panel
func (p *Panel) Start() {
p.access.Lock()
defer p.access.Unlock()
@@ -159,18 +170,23 @@ func (p *Panel) Start() {
log.Panicf("Failed to start instance: %s", err)
}
p.Server = server
// Load Nodes config
for _, nodeConfig := range p.panelConfig.NodesConfig {
var apiClient api.API
switch nodeConfig.PanelType {
case "SSpanel":
apiClient = sspanel.New(nodeConfig.ApiConfig)
case "V2board":
apiClient = v2board.New(nodeConfig.ApiConfig)
case "NewV2board":
apiClient = newV2board.New(nodeConfig.ApiConfig)
case "PMpanel":
apiClient = pmpanel.New(nodeConfig.ApiConfig)
case "Proxypanel":
apiClient = proxypanel.New(nodeConfig.ApiConfig)
case "V2RaySocks":
apiClient = v2raysocks.New(nodeConfig.ApiConfig)
case "GoV2Panel":
apiClient = gov2panel.New(nodeConfig.ApiConfig)
default:
log.Panicf("Unsupport panel type: %s", nodeConfig.PanelType)
}
@@ -198,7 +214,7 @@ func (p *Panel) Start() {
return
}
// Close Close the panel
// Close the panel
func (p *Panel) Close() {
p.access.Lock()
defer p.access.Unlock()
@@ -214,21 +230,21 @@ func (p *Panel) Close() {
return
}
func parseConnectionConfig(c *ConnetionConfig) (policy *conf.Policy) {
connetionConfig := getDefaultConnetionConfig()
func parseConnectionConfig(c *ConnectionConfig) (policy *conf.Policy) {
connectionConfig := getDefaultConnectionConfig()
if c != nil {
if _, err := diff.Merge(connetionConfig, c, connetionConfig); err != nil {
log.Panicf("Read ConnetionConfig failed: %s", err)
if _, err := diff.Merge(connectionConfig, c, connectionConfig); err != nil {
log.Panicf("Read ConnectionConfig failed: %s", err)
}
}
policy = &conf.Policy{
StatsUserUplink: true,
StatsUserDownlink: true,
Handshake: &connetionConfig.Handshake,
ConnectionIdle: &connetionConfig.ConnIdle,
UplinkOnly: &connetionConfig.UplinkOnly,
DownlinkOnly: &connetionConfig.DownlinkOnly,
BufferSize: &connetionConfig.BufferSize,
Handshake: &connectionConfig.Handshake,
ConnectionIdle: &connectionConfig.ConnIdle,
UplinkOnly: &connectionConfig.UplinkOnly,
DownlinkOnly: &connectionConfig.DownlinkOnly,
BufferSize: &connectionConfig.BufferSize,
}
return

View File

@@ -1,30 +1,36 @@
package controller
import (
"github.com/XrayR-project/XrayR/common/limiter"
"github.com/XrayR-project/XrayR/common/mylego"
)
type Config struct {
ListenIP string `mapstructure:"ListenIP"`
SendIP string `mapstructure:"SendIP"`
UpdatePeriodic int `mapstructure:"UpdatePeriodic"`
CertConfig *CertConfig `mapstructure:"CertConfig"`
EnableDNS bool `mapstructure:"EnableDNS"`
DNSType string `mapstructure:"DNSType"`
DisableUploadTraffic bool `mapstructure:"DisableUploadTraffic"`
DisableGetRule bool `mapstructure:"DisableGetRule"`
EnableProxyProtocol bool `mapstructure:"EnableProxyProtocol"`
EnableFallback bool `mapstructure:"EnableFallback"`
DisableIVCheck bool `mapstructure:"DisableIVCheck"`
DisableSniffing bool `mapstructure:"DisableSniffing"`
FallBackConfigs []*FallBackConfig `mapstructure:"FallBackConfigs"`
ListenIP string `mapstructure:"ListenIP"`
SendIP string `mapstructure:"SendIP"`
UpdatePeriodic int `mapstructure:"UpdatePeriodic"`
CertConfig *mylego.CertConfig `mapstructure:"CertConfig"`
EnableDNS bool `mapstructure:"EnableDNS"`
DNSType string `mapstructure:"DNSType"`
DisableUploadTraffic bool `mapstructure:"DisableUploadTraffic"`
DisableGetRule bool `mapstructure:"DisableGetRule"`
EnableProxyProtocol bool `mapstructure:"EnableProxyProtocol"`
EnableFallback bool `mapstructure:"EnableFallback"`
DisableIVCheck bool `mapstructure:"DisableIVCheck"`
DisableSniffing bool `mapstructure:"DisableSniffing"`
AutoSpeedLimitConfig *AutoSpeedLimitConfig `mapstructure:"AutoSpeedLimitConfig"`
GlobalDeviceLimitConfig *limiter.GlobalDeviceLimitConfig `mapstructure:"GlobalDeviceLimitConfig"`
FallBackConfigs []*FallBackConfig `mapstructure:"FallBackConfigs"`
DisableLocalREALITYConfig bool `mapstructure:"DisableLocalREALITYConfig"`
EnableREALITY bool `mapstructure:"EnableREALITY"`
REALITYConfigs *REALITYConfig `mapstructure:"REALITYConfigs"`
}
type CertConfig struct {
CertMode string `mapstructure:"CertMode"` // none, file, http, dns
RejectUnknownSni bool `mapstructure:"RejectUnknownSni"`
CertDomain string `mapstructure:"CertDomain"`
CertFile string `mapstructure:"CertFile"`
KeyFile string `mapstructure:"KeyFile"`
Provider string `mapstructure:"Provider"` // alidns, cloudflare, gandi, godaddy....
Email string `mapstructure:"Email"`
DNSEnv map[string]string `mapstructure:"DNSEnv"`
type AutoSpeedLimitConfig struct {
Limit int `mapstructure:"Limit"` // mbps
WarnTimes int `mapstructure:"WarnTimes"`
LimitSpeed int `mapstructure:"LimitSpeed"` // mbps
LimitDuration int `mapstructure:"LimitDuration"` // minute
}
type FallBackConfig struct {
@@ -34,3 +40,15 @@ type FallBackConfig struct {
Dest string `mapstructure:"Dest"`
ProxyProtocolVer uint64 `mapstructure:"ProxyProtocolVer"`
}
type REALITYConfig struct {
Show bool `mapstructure:"Show"`
Dest string `mapstructure:"Dest"`
ProxyProtocolVer uint64 `mapstructure:"ProxyProtocolVer"`
ServerNames []string `mapstructure:"ServerNames"`
PrivateKey string `mapstructure:"PrivateKey"`
MinClientVer string `mapstructure:"MinClientVer"`
MaxClientVer string `mapstructure:"MaxClientVer"`
MaxTimeDiff uint64 `mapstructure:"MaxTimeDiff"`
ShortIds []string `mapstructure:"ShortIds"`
}

View File

@@ -4,31 +4,28 @@ import (
"context"
"fmt"
"github.com/XrayR-project/XrayR/api"
"github.com/XrayR-project/XrayR/app/mydispatcher"
"github.com/xtls/xray-core/common/protocol"
"github.com/xtls/xray-core/core"
"github.com/xtls/xray-core/features/inbound"
"github.com/xtls/xray-core/features/outbound"
"github.com/xtls/xray-core/features/routing"
"github.com/xtls/xray-core/features/stats"
"github.com/xtls/xray-core/proxy"
"github.com/XrayR-project/XrayR/api"
"github.com/XrayR-project/XrayR/common/limiter"
)
func (c *Controller) removeInbound(tag string) error {
inboundManager := c.server.GetFeature(inbound.ManagerType()).(inbound.Manager)
err := inboundManager.RemoveHandler(context.Background(), tag)
err := c.ibm.RemoveHandler(context.Background(), tag)
return err
}
func (c *Controller) removeOutbound(tag string) error {
outboundManager := c.server.GetFeature(outbound.ManagerType()).(outbound.Manager)
err := outboundManager.RemoveHandler(context.Background(), tag)
err := c.obm.RemoveHandler(context.Background(), tag)
return err
}
func (c *Controller) addInbound(config *core.InboundHandlerConfig) error {
inboundManager := c.server.GetFeature(inbound.ManagerType()).(inbound.Manager)
rawHandler, err := core.CreateObject(c.server, config)
if err != nil {
return err
@@ -37,14 +34,13 @@ func (c *Controller) addInbound(config *core.InboundHandlerConfig) error {
if !ok {
return fmt.Errorf("not an InboundHandler: %s", err)
}
if err := inboundManager.AddHandler(context.Background(), handler); err != nil {
if err := c.ibm.AddHandler(context.Background(), handler); err != nil {
return err
}
return nil
}
func (c *Controller) addOutbound(config *core.OutboundHandlerConfig) error {
outboundManager := c.server.GetFeature(outbound.ManagerType()).(outbound.Manager)
rawHandler, err := core.CreateObject(c.server, config)
if err != nil {
return err
@@ -53,26 +49,25 @@ func (c *Controller) addOutbound(config *core.OutboundHandlerConfig) error {
if !ok {
return fmt.Errorf("not an InboundHandler: %s", err)
}
if err := outboundManager.AddHandler(context.Background(), handler); err != nil {
if err := c.obm.AddHandler(context.Background(), handler); err != nil {
return err
}
return nil
}
func (c *Controller) addUsers(users []*protocol.User, tag string) error {
inboundManager := c.server.GetFeature(inbound.ManagerType()).(inbound.Manager)
handler, err := inboundManager.GetHandler(context.Background(), tag)
handler, err := c.ibm.GetHandler(context.Background(), tag)
if err != nil {
return fmt.Errorf("No such inbound tag: %s", err)
return fmt.Errorf("no such inbound tag: %s", err)
}
inboundInstance, ok := handler.(proxy.GetInbound)
if !ok {
return fmt.Errorf("handler %s is not implement proxy.GetInbound", tag)
return fmt.Errorf("handler %s has not implemented proxy.GetInbound", tag)
}
userManager, ok := inboundInstance.GetInbound().(proxy.UserManager)
if !ok {
return fmt.Errorf("handler %s is not implement proxy.UserManager", err)
return fmt.Errorf("handler %s has not implemented proxy.UserManager", tag)
}
for _, item := range users {
mUser, err := item.ToMemoryUser()
@@ -88,10 +83,9 @@ func (c *Controller) addUsers(users []*protocol.User, tag string) error {
}
func (c *Controller) removeUsers(users []string, tag string) error {
inboundManager := c.server.GetFeature(inbound.ManagerType()).(inbound.Manager)
handler, err := inboundManager.GetHandler(context.Background(), tag)
handler, err := c.ibm.GetHandler(context.Background(), tag)
if err != nil {
return fmt.Errorf("No such inbound tag: %s", err)
return fmt.Errorf("no such inbound tag: %s", err)
}
inboundInstance, ok := handler.(proxy.GetInbound)
if !ok {
@@ -111,54 +105,57 @@ func (c *Controller) removeUsers(users []string, tag string) error {
return nil
}
func (c *Controller) getTraffic(email string) (up int64, down int64) {
func (c *Controller) getTraffic(email string) (up int64, down int64, upCounter stats.Counter, downCounter stats.Counter) {
upName := "user>>>" + email + ">>>traffic>>>uplink"
downName := "user>>>" + email + ">>>traffic>>>downlink"
statsManager := c.server.GetFeature(stats.ManagerType()).(stats.Manager)
upCounter := statsManager.GetCounter(upName)
downCounter := statsManager.GetCounter(downName)
if upCounter != nil {
upCounter = c.stm.GetCounter(upName)
downCounter = c.stm.GetCounter(downName)
if upCounter != nil && upCounter.Value() != 0 {
up = upCounter.Value()
upCounter.Set(0)
} else {
upCounter = nil
}
if downCounter != nil {
if downCounter != nil && downCounter.Value() != 0 {
down = downCounter.Value()
downCounter.Set(0)
} else {
downCounter = nil
}
return up, down
return up, down, upCounter, downCounter
}
func (c *Controller) AddInboundLimiter(tag string, nodeSpeedLimit uint64, userList *[]api.UserInfo) error {
dispather := c.server.GetFeature(routing.DispatcherType()).(*mydispatcher.DefaultDispatcher)
err := dispather.Limiter.AddInboundLimiter(tag, nodeSpeedLimit, userList)
func (c *Controller) resetTraffic(upCounterList *[]stats.Counter, downCounterList *[]stats.Counter) {
for _, upCounter := range *upCounterList {
upCounter.Set(0)
}
for _, downCounter := range *downCounterList {
downCounter.Set(0)
}
}
func (c *Controller) AddInboundLimiter(tag string, nodeSpeedLimit uint64, userList *[]api.UserInfo, globalDeviceLimitConfig *limiter.GlobalDeviceLimitConfig) error {
err := c.dispatcher.Limiter.AddInboundLimiter(tag, nodeSpeedLimit, userList, globalDeviceLimitConfig)
return err
}
func (c *Controller) UpdateInboundLimiter(tag string, updatedUserList *[]api.UserInfo) error {
dispather := c.server.GetFeature(routing.DispatcherType()).(*mydispatcher.DefaultDispatcher)
err := dispather.Limiter.UpdateInboundLimiter(tag, updatedUserList)
err := c.dispatcher.Limiter.UpdateInboundLimiter(tag, updatedUserList)
return err
}
func (c *Controller) DeleteInboundLimiter(tag string) error {
dispather := c.server.GetFeature(routing.DispatcherType()).(*mydispatcher.DefaultDispatcher)
err := dispather.Limiter.DeleteInboundLimiter(tag)
err := c.dispatcher.Limiter.DeleteInboundLimiter(tag)
return err
}
func (c *Controller) GetOnlineDevice(tag string) (*[]api.OnlineUser, error) {
dispather := c.server.GetFeature(routing.DispatcherType()).(*mydispatcher.DefaultDispatcher)
return dispather.Limiter.GetOnlineDevice(tag)
return c.dispatcher.Limiter.GetOnlineDevice(tag)
}
func (c *Controller) UpdateRule(tag string, newRuleList []api.DetectRule) error {
dispather := c.server.GetFeature(routing.DispatcherType()).(*mydispatcher.DefaultDispatcher)
err := dispather.RuleManager.UpdateRule(tag, newRuleList)
err := c.dispatcher.RuleManager.UpdateRule(tag, newRuleList)
return err
}
func (c *Controller) GetDetectResult(tag string) (*[]api.DetectResult, error) {
dispather := c.server.GetFeature(routing.DispatcherType()).(*mydispatcher.DefaultDispatcher)
return dispather.RuleManager.GetDetectResult(tag)
return c.dispatcher.RuleManager.GetDetectResult(tag)
}

View File

@@ -1,41 +1,70 @@
package controller
import (
"errors"
"fmt"
"log"
"math"
"reflect"
"time"
"github.com/XrayR-project/XrayR/api"
"github.com/XrayR-project/XrayR/common/legocmd"
"github.com/XrayR-project/XrayR/common/serverstatus"
"github.com/xtls/xray-core/common/protocol"
"github.com/xtls/xray-core/common/task"
"github.com/xtls/xray-core/core"
"github.com/xtls/xray-core/features/inbound"
"github.com/xtls/xray-core/features/outbound"
"github.com/xtls/xray-core/features/routing"
"github.com/xtls/xray-core/features/stats"
"github.com/XrayR-project/XrayR/api"
"github.com/XrayR-project/XrayR/app/mydispatcher"
"github.com/XrayR-project/XrayR/common/mylego"
"github.com/XrayR-project/XrayR/common/serverstatus"
)
type LimitInfo struct {
end int64
currentSpeedLimit int
originSpeedLimit uint64
}
type Controller struct {
server *core.Instance
config *Config
clientInfo api.ClientInfo
apiClient api.API
nodeInfo *api.NodeInfo
Tag string
userList *[]api.UserInfo
nodeInfoMonitorPeriodic *task.Periodic
userReportPeriodic *task.Periodic
panelType string
server *core.Instance
config *Config
clientInfo api.ClientInfo
apiClient api.API
nodeInfo *api.NodeInfo
Tag string
userList *[]api.UserInfo
tasks []periodicTask
limitedUsers map[api.UserInfo]LimitInfo
warnedUsers map[api.UserInfo]int
panelType string
ibm inbound.Manager
obm outbound.Manager
stm stats.Manager
dispatcher *mydispatcher.DefaultDispatcher
startAt time.Time
}
type periodicTask struct {
tag string
*task.Periodic
}
// New return a Controller service with default parameters.
func New(server *core.Instance, api api.API, config *Config, panelType string) *Controller {
controller := &Controller{
server: server,
config: config,
apiClient: api,
panelType: panelType,
server: server,
config: config,
apiClient: api,
panelType: panelType,
ibm: server.GetFeature(inbound.ManagerType()).(inbound.Manager),
obm: server.GetFeature(outbound.ManagerType()).(outbound.Manager),
stm: server.GetFeature(stats.ManagerType()).(stats.Manager),
dispatcher: server.GetFeature(routing.DispatcherType()).(*mydispatcher.DefaultDispatcher),
startAt: time.Now(),
}
return controller
}
@@ -47,8 +76,12 @@ func (c *Controller) Start() error {
if err != nil {
return err
}
if newNodeInfo.Port == 0 {
return errors.New("server port must > 0")
}
c.nodeInfo = newNodeInfo
c.Tag = c.buildNodeTag()
// Add new tag
err = c.addNewTag(newNodeInfo)
if err != nil {
@@ -61,17 +94,19 @@ func (c *Controller) Start() error {
return err
}
// sync controller userList
c.userList = userInfo
err = c.addNewUser(userInfo, newNodeInfo)
if err != nil {
return err
}
//sync controller userList
c.userList = userInfo
// Add Limiter
if err := c.AddInboundLimiter(c.Tag, newNodeInfo.SpeedLimit, userInfo); err != nil {
if err := c.AddInboundLimiter(c.Tag, newNodeInfo.SpeedLimit, userInfo, c.config.GlobalDeviceLimitConfig); err != nil {
log.Print(err)
}
// Add Rule Manager
if !c.config.DisableGetRule {
if ruleList, err := c.apiClient.GetNodeRule(); err != nil {
@@ -82,100 +117,141 @@ func (c *Controller) Start() error {
}
}
}
c.nodeInfoMonitorPeriodic = &task.Periodic{
Interval: time.Duration(c.config.UpdatePeriodic) * time.Second,
Execute: c.nodeInfoMonitor,
}
c.userReportPeriodic = &task.Periodic{
Interval: time.Duration(c.config.UpdatePeriodic) * time.Second,
Execute: c.userInfoMonitor,
}
log.Printf("[%s: %d] Start monitor node status", c.nodeInfo.NodeType, c.nodeInfo.NodeID)
// delay to start nodeInfoMonitor
go func() {
time.Sleep(time.Duration(c.config.UpdatePeriodic) * time.Second)
_ = c.nodeInfoMonitorPeriodic.Start()
}()
log.Printf("[%s: %d] Start report node status", c.nodeInfo.NodeType, c.nodeInfo.NodeID)
// delay to start userReport
go func() {
time.Sleep(time.Duration(c.config.UpdatePeriodic) * time.Second)
_ = c.userReportPeriodic.Start()
}()
// Init AutoSpeedLimitConfig
if c.config.AutoSpeedLimitConfig == nil {
c.config.AutoSpeedLimitConfig = &AutoSpeedLimitConfig{0, 0, 0, 0}
}
if c.config.AutoSpeedLimitConfig.Limit > 0 {
c.limitedUsers = make(map[api.UserInfo]LimitInfo)
c.warnedUsers = make(map[api.UserInfo]int)
}
// Add periodic tasks
c.tasks = append(c.tasks,
periodicTask{
tag: "node monitor",
Periodic: &task.Periodic{
Interval: time.Duration(c.config.UpdatePeriodic) * time.Second,
Execute: c.nodeInfoMonitor,
}},
periodicTask{
tag: "user monitor",
Periodic: &task.Periodic{
Interval: time.Duration(c.config.UpdatePeriodic) * time.Second,
Execute: c.userInfoMonitor,
}},
)
// Check cert service in need
if c.nodeInfo.EnableTLS && c.config.EnableREALITY == false {
c.tasks = append(c.tasks, periodicTask{
tag: "cert monitor",
Periodic: &task.Periodic{
Interval: time.Duration(c.config.UpdatePeriodic) * time.Second * 60,
Execute: c.certMonitor,
}})
}
// Start periodic tasks
for i := range c.tasks {
log.Printf("%s Start %s periodic task", c.logPrefix(), c.tasks[i].tag)
go c.tasks[i].Start()
}
return nil
}
// Close implement the Close() function of the service interface
func (c *Controller) Close() error {
if c.nodeInfoMonitorPeriodic != nil {
err := c.nodeInfoMonitorPeriodic.Close()
if err != nil {
log.Panicf("node info periodic close failed: %s", err)
for i := range c.tasks {
if c.tasks[i].Periodic != nil {
if err := c.tasks[i].Periodic.Close(); err != nil {
log.Panicf("%s %s periodic task close failed: %s", c.logPrefix(), c.tasks[i].tag, err)
}
}
}
if c.nodeInfoMonitorPeriodic != nil {
err := c.userReportPeriodic.Close()
if err != nil {
log.Panicf("user report periodic close failed: %s", err)
}
}
return nil
}
func (c *Controller) nodeInfoMonitor() (err error) {
// delay to start
if time.Since(c.startAt) < time.Duration(c.config.UpdatePeriodic)*time.Second {
return nil
}
// First fetch Node Info
var nodeInfoChanged = true
newNodeInfo, err := c.apiClient.GetNodeInfo()
if err != nil {
log.Print(err)
return nil
if err.Error() == api.NodeNotModified {
nodeInfoChanged = false
newNodeInfo = c.nodeInfo
} else {
log.Print(err)
return nil
}
}
if newNodeInfo.Port == 0 {
return errors.New("server port must > 0")
}
// Update User
var usersChanged = true
newUserInfo, err := c.apiClient.GetUserList()
if err != nil {
log.Print(err)
return nil
if err.Error() == api.UserNotModified {
usersChanged = false
newUserInfo = c.userList
} else {
log.Print(err)
return nil
}
}
var nodeInfoChanged = false
// If nodeInfo changed
if !reflect.DeepEqual(c.nodeInfo, newNodeInfo) {
// Remove old tag
oldtag := c.Tag
err := c.removeOldTag(oldtag)
if err != nil {
log.Print(err)
return nil
}
if c.nodeInfo.NodeType == "Shadowsocks-Plugin" {
err = c.removeOldTag(fmt.Sprintf("dokodemo-door_%s+1", c.Tag))
}
if err != nil {
log.Print(err)
return nil
}
// Add new tag
c.nodeInfo = newNodeInfo
c.Tag = c.buildNodeTag()
err = c.addNewTag(newNodeInfo)
if err != nil {
log.Print(err)
return nil
}
nodeInfoChanged = true
// Remove Old limiter
if err = c.DeleteInboundLimiter(oldtag); err != nil {
log.Print(err)
return nil
if nodeInfoChanged {
if !reflect.DeepEqual(c.nodeInfo, newNodeInfo) {
// Remove old tag
oldTag := c.Tag
err := c.removeOldTag(oldTag)
if err != nil {
log.Print(err)
return nil
}
if c.nodeInfo.NodeType == "Shadowsocks-Plugin" {
err = c.removeOldTag(fmt.Sprintf("dokodemo-door_%s+1", c.Tag))
}
if err != nil {
log.Print(err)
return nil
}
// Add new tag
c.nodeInfo = newNodeInfo
c.Tag = c.buildNodeTag()
err = c.addNewTag(newNodeInfo)
if err != nil {
log.Print(err)
return nil
}
nodeInfoChanged = true
// Remove Old limiter
if err = c.DeleteInboundLimiter(oldTag); err != nil {
log.Print(err)
return nil
}
} else {
nodeInfoChanged = false
}
}
// Check Rule
if !c.config.DisableGetRule {
if ruleList, err := c.apiClient.GetNodeRule(); err != nil {
log.Printf("Get rule list filed: %s", err)
if err.Error() != api.RuleNotModified {
log.Printf("Get rule list filed: %s", err)
}
} else if len(*ruleList) > 0 {
if err := c.UpdateRule(c.Tag, *ruleList); err != nil {
log.Print(err)
@@ -183,64 +259,56 @@ func (c *Controller) nodeInfoMonitor() (err error) {
}
}
// Check Cert
if c.nodeInfo.EnableTLS && (c.config.CertConfig.CertMode == "dns" || c.config.CertConfig.CertMode == "http") {
lego, err := legocmd.New()
if err != nil {
log.Print(err)
}
// Xray-core supports the OcspStapling certification hot renew
_, _, err = lego.RenewCert(c.config.CertConfig.CertDomain, c.config.CertConfig.Email, c.config.CertConfig.CertMode, c.config.CertConfig.Provider, c.config.CertConfig.DNSEnv)
if err != nil {
log.Print(err)
}
}
if nodeInfoChanged {
err = c.addNewUser(newUserInfo, newNodeInfo)
if err != nil {
log.Print(err)
return nil
}
// Add Limiter
if err := c.AddInboundLimiter(c.Tag, newNodeInfo.SpeedLimit, newUserInfo); err != nil {
if err := c.AddInboundLimiter(c.Tag, newNodeInfo.SpeedLimit, newUserInfo, c.config.GlobalDeviceLimitConfig); err != nil {
log.Print(err)
return nil
}
} else {
deleted, added := compareUserList(c.userList, newUserInfo)
if len(deleted) > 0 {
deletedEmail := make([]string, len(deleted))
for i, u := range deleted {
deletedEmail[i] = fmt.Sprintf("%s|%s|%d", c.Tag, u.Email, u.UID)
var deleted, added []api.UserInfo
if usersChanged {
deleted, added = compareUserList(c.userList, newUserInfo)
if len(deleted) > 0 {
deletedEmail := make([]string, len(deleted))
for i, u := range deleted {
deletedEmail[i] = fmt.Sprintf("%s|%s|%d", c.Tag, u.Email, u.UID)
}
err := c.removeUsers(deletedEmail, c.Tag)
if err != nil {
log.Print(err)
}
}
err := c.removeUsers(deletedEmail, c.Tag)
if err != nil {
log.Print(err)
if len(added) > 0 {
err = c.addNewUser(&added, c.nodeInfo)
if err != nil {
log.Print(err)
}
// Update Limiter
if err := c.UpdateInboundLimiter(c.Tag, &added); err != nil {
log.Print(err)
}
}
}
if len(added) > 0 {
err = c.addNewUser(&added, c.nodeInfo)
if err != nil {
log.Print(err)
}
// Update Limiter
if err := c.UpdateInboundLimiter(c.Tag, &added); err != nil {
log.Print(err)
}
}
log.Printf("[%s: %d] %d user deleted, %d user added", c.nodeInfo.NodeType, c.nodeInfo.NodeID, len(deleted), len(added))
log.Printf("%s %d user deleted, %d user added", c.logPrefix(), len(deleted), len(added))
}
c.userList = newUserInfo
return nil
}
func (c *Controller) removeOldTag(oldtag string) (err error) {
err = c.removeInbound(oldtag)
func (c *Controller) removeOldTag(oldTag string) (err error) {
err = c.removeInbound(oldTag)
if err != nil {
return err
}
err = c.removeOutbound(oldtag)
err = c.removeOutbound(oldTag)
if err != nil {
return err
}
@@ -276,7 +344,7 @@ func (c *Controller) addNewTag(newNodeInfo *api.NodeInfo) (err error) {
}
func (c *Controller) addInboundForSSPlugin(newNodeInfo api.NodeInfo) (err error) {
// Shadowsocks-Plugin require a seaperate inbound for other TransportProtocol likes: ws, grpc
// Shadowsocks-Plugin require a separate inbound for other TransportProtocol likes: ws, grpc
fakeNodeInfo := newNodeInfo
fakeNodeInfo.TransportProtocol = "tcp"
fakeNodeInfo.EnableTLS = false
@@ -329,69 +397,59 @@ func (c *Controller) addInboundForSSPlugin(newNodeInfo api.NodeInfo) (err error)
func (c *Controller) addNewUser(userInfo *[]api.UserInfo, nodeInfo *api.NodeInfo) (err error) {
users := make([]*protocol.User, 0)
if nodeInfo.NodeType == "V2ray" {
switch nodeInfo.NodeType {
case "V2ray":
if nodeInfo.EnableVless {
users = c.buildVlessUser(userInfo)
} else {
alterID := 0
if c.panelType == "V2board" {
// use latest userInfo
alterID = (*userInfo)[0].AlterID
} else {
alterID = nodeInfo.AlterID
}
if alterID >= 0 && alterID < math.MaxUint16 {
users = c.buildVmessUser(userInfo, uint16(alterID))
} else {
users = c.buildVmessUser(userInfo, 0)
return fmt.Errorf("AlterID should between 0 to 1<<16 - 1, set it to 0 for now")
}
users = c.buildVmessUser(userInfo)
}
} else if nodeInfo.NodeType == "Trojan" {
case "Trojan":
users = c.buildTrojanUser(userInfo)
} else if nodeInfo.NodeType == "Shadowsocks" {
case "Shadowsocks":
users = c.buildSSUser(userInfo, nodeInfo.CypherMethod)
} else if nodeInfo.NodeType == "Shadowsocks-Plugin" {
case "Shadowsocks-Plugin":
users = c.buildSSPluginUser(userInfo)
} else {
default:
return fmt.Errorf("unsupported node type: %s", nodeInfo.NodeType)
}
err = c.addUsers(users, c.Tag)
if err != nil {
return err
}
log.Printf("[%s: %d] Added %d new users", c.nodeInfo.NodeType, c.nodeInfo.NodeID, len(*userInfo))
log.Printf("%s Added %d new users", c.logPrefix(), len(*userInfo))
return nil
}
func compareUserList(old, new *[]api.UserInfo) (deleted, added []api.UserInfo) {
msrc := make(map[api.UserInfo]byte) //按源数组建索引
mall := make(map[api.UserInfo]byte) //源+目所有元素建索引
mSrc := make(map[api.UserInfo]byte) // 按源数组建索引
mAll := make(map[api.UserInfo]byte) // 源+目所有元素建索引
var set []api.UserInfo //交集
var set []api.UserInfo // 交集
//1.源数组建立map
// 1.源数组建立map
for _, v := range *old {
msrc[v] = 0
mall[v] = 0
mSrc[v] = 0
mAll[v] = 0
}
//2.目数组中,存不进去,即重复元素,所有存不进去的集合就是并集
// 2.目数组中,存不进去,即重复元素,所有存不进去的集合就是并集
for _, v := range *new {
l := len(mall)
mall[v] = 1
if l != len(mall) { //长度变化,即可以存
l = len(mall)
} else { //存不了,进并集
l := len(mAll)
mAll[v] = 1
if l != len(mAll) { // 长度变化,即可以存
l = len(mAll)
} else { // 存不了,进并集
set = append(set, v)
}
}
//3.遍历交集,在并集中找,找到就从并集中删,删完后就是补集(即并-交=所有变化的元素)
// 3.遍历交集,在并集中找,找到就从并集中删,删完后就是补集(即并-交=所有变化的元素)
for _, v := range set {
delete(mall, v)
delete(mAll, v)
}
//4.此时mall是补集所有元素去源中找找到就是删除的找不到的必定能在目数组中找到即新加的
for v := range mall {
_, exist := msrc[v]
// 4.此时mall是补集所有元素去源中找找到就是删除的找不到的必定能在目数组中找到即新加的
for v := range mAll {
_, exist := mSrc[v]
if exist {
deleted = append(deleted, v)
} else {
@@ -402,7 +460,23 @@ func compareUserList(old, new *[]api.UserInfo) (deleted, added []api.UserInfo) {
return deleted, added
}
func limitUser(c *Controller, user api.UserInfo, silentUsers *[]api.UserInfo) {
c.limitedUsers[user] = LimitInfo{
end: time.Now().Unix() + int64(c.config.AutoSpeedLimitConfig.LimitDuration*60),
currentSpeedLimit: c.config.AutoSpeedLimitConfig.LimitSpeed,
originSpeedLimit: user.SpeedLimit,
}
log.Printf("Limit User: %s Speed: %d End: %s", c.buildUserTag(&user), c.config.AutoSpeedLimitConfig.LimitSpeed, time.Unix(c.limitedUsers[user].end, 0).Format("01-02 15:04:05"))
user.SpeedLimit = uint64((c.config.AutoSpeedLimitConfig.LimitSpeed * 1000000) / 8)
*silentUsers = append(*silentUsers, user)
}
func (c *Controller) userInfoMonitor() (err error) {
// delay to start
if time.Since(c.startAt) < time.Duration(c.config.UpdatePeriodic)*time.Second {
return nil
}
// Get server status
CPU, Mem, Disk, Uptime, err := serverstatus.GetSystemInfo()
if err != nil {
@@ -418,23 +492,86 @@ func (c *Controller) userInfoMonitor() (err error) {
if err != nil {
log.Print(err)
}
// Unlock users
if c.config.AutoSpeedLimitConfig.Limit > 0 && len(c.limitedUsers) > 0 {
log.Printf("%s Limited users:", c.logPrefix())
toReleaseUsers := make([]api.UserInfo, 0)
for user, limitInfo := range c.limitedUsers {
if time.Now().Unix() > limitInfo.end {
user.SpeedLimit = limitInfo.originSpeedLimit
toReleaseUsers = append(toReleaseUsers, user)
log.Printf("User: %s Speed: %d End: nil (Unlimit)", c.buildUserTag(&user), user.SpeedLimit)
delete(c.limitedUsers, user)
} else {
log.Printf("User: %s Speed: %d End: %s", c.buildUserTag(&user), limitInfo.currentSpeedLimit, time.Unix(c.limitedUsers[user].end, 0).Format("01-02 15:04:05"))
}
}
if len(toReleaseUsers) > 0 {
if err := c.UpdateInboundLimiter(c.Tag, &toReleaseUsers); err != nil {
log.Print(err)
}
}
}
// Get User traffic
userTraffic := make([]api.UserTraffic, 0)
var userTraffic []api.UserTraffic
var upCounterList []stats.Counter
var downCounterList []stats.Counter
AutoSpeedLimit := int64(c.config.AutoSpeedLimitConfig.Limit)
UpdatePeriodic := int64(c.config.UpdatePeriodic)
limitedUsers := make([]api.UserInfo, 0)
for _, user := range *c.userList {
up, down := c.getTraffic(c.buildUserTag(&user))
up, down, upCounter, downCounter := c.getTraffic(c.buildUserTag(&user))
if up > 0 || down > 0 {
// Over speed users
if AutoSpeedLimit > 0 {
if down > AutoSpeedLimit*1000000*UpdatePeriodic/8 || up > AutoSpeedLimit*1000000*UpdatePeriodic/8 {
if _, ok := c.limitedUsers[user]; !ok {
if c.config.AutoSpeedLimitConfig.WarnTimes == 0 {
limitUser(c, user, &limitedUsers)
} else {
c.warnedUsers[user] += 1
if c.warnedUsers[user] > c.config.AutoSpeedLimitConfig.WarnTimes {
limitUser(c, user, &limitedUsers)
delete(c.warnedUsers, user)
}
}
}
} else {
delete(c.warnedUsers, user)
}
}
userTraffic = append(userTraffic, api.UserTraffic{
UID: user.UID,
Email: user.Email,
Upload: up,
Download: down})
if upCounter != nil {
upCounterList = append(upCounterList, upCounter)
}
if downCounter != nil {
downCounterList = append(downCounterList, downCounter)
}
} else {
delete(c.warnedUsers, user)
}
}
if len(userTraffic) > 0 && !c.config.DisableUploadTraffic {
err = c.apiClient.ReportUserTraffic(&userTraffic)
if len(limitedUsers) > 0 {
if err := c.UpdateInboundLimiter(c.Tag, &limitedUsers); err != nil {
log.Print(err)
}
}
if len(userTraffic) > 0 {
var err error // Define an empty error
if !c.config.DisableUploadTraffic {
err = c.apiClient.ReportUserTraffic(&userTraffic)
}
// If report traffic error, not clear the traffic
if err != nil {
log.Print(err)
} else {
c.resetTraffic(&upCounterList, &downCounterList)
}
}
@@ -445,9 +582,10 @@ func (c *Controller) userInfoMonitor() (err error) {
if err = c.apiClient.ReportNodeOnlineUsers(onlineDevice); err != nil {
log.Print(err)
} else {
log.Printf("[%s: %d] Report %d online users", c.nodeInfo.NodeType, c.nodeInfo.NodeID, len(*onlineDevice))
log.Printf("%s Report %d online users", c.logPrefix(), len(*onlineDevice))
}
}
// Report Illegal user
if detectResult, err := c.GetDetectResult(c.Tag); err != nil {
log.Print(err)
@@ -455,7 +593,7 @@ func (c *Controller) userInfoMonitor() (err error) {
if err = c.apiClient.ReportIllegal(detectResult); err != nil {
log.Print(err)
} else {
log.Printf("[%s: %d] Report %d illegal behaviors", c.nodeInfo.NodeType, c.nodeInfo.NodeID, len(*detectResult))
log.Printf("%s Report %d illegal behaviors", c.logPrefix(), len(*detectResult))
}
}
@@ -465,3 +603,26 @@ func (c *Controller) userInfoMonitor() (err error) {
func (c *Controller) buildNodeTag() string {
return fmt.Sprintf("%s_%s_%d", c.nodeInfo.NodeType, c.config.ListenIP, c.nodeInfo.Port)
}
func (c *Controller) logPrefix() string {
return fmt.Sprintf("[%s] %s(ID=%d)", c.clientInfo.APIHost, c.nodeInfo.NodeType, c.nodeInfo.NodeID)
}
// Check Cert
func (c *Controller) certMonitor() error {
if c.nodeInfo.EnableTLS && c.config.EnableREALITY == false {
switch c.config.CertConfig.CertMode {
case "dns", "http", "tls":
lego, err := mylego.New(c.config.CertConfig)
if err != nil {
log.Print(err)
}
// Xray-core supports the OcspStapling certification hot renew
_, _, _, err = lego.RenewCert()
if err != nil {
log.Print(err)
}
}
}
return nil
}

View File

@@ -8,12 +8,14 @@ import (
"syscall"
"testing"
"github.com/XrayR-project/XrayR/api"
"github.com/XrayR-project/XrayR/api/sspanel"
_ "github.com/XrayR-project/XrayR/main/distro/all"
. "github.com/XrayR-project/XrayR/service/controller"
"github.com/xtls/xray-core/core"
"github.com/xtls/xray-core/infra/conf"
"github.com/XrayR-project/XrayR/api"
"github.com/XrayR-project/XrayR/api/sspanel"
"github.com/XrayR-project/XrayR/common/mylego"
_ "github.com/XrayR-project/XrayR/main/distro/all"
. "github.com/XrayR-project/XrayR/service/controller"
)
func TestController(t *testing.T) {
@@ -22,7 +24,7 @@ func TestController(t *testing.T) {
LogConfig: &conf.LogConfig{LogLevel: "debug"},
}
policyConfig := &conf.PolicyConfig{}
policyConfig.Levels = map[uint32]*conf.Policy{0: &conf.Policy{
policyConfig.Levels = map[uint32]*conf.Policy{0: {
StatsUserUplink: true,
StatsUserDownlink: true,
}}
@@ -45,13 +47,13 @@ func TestController(t *testing.T) {
if err = server.Start(); err != nil {
t.Errorf("Failed to start instance: %s", err)
}
certConfig := &CertConfig{
certConfig := &mylego.CertConfig{
CertMode: "http",
CertDomain: "test.ss.tk",
Provider: "alidns",
Email: "ss@ss.com",
}
controlerconfig := &Config{
controlerConfig := &Config{
UpdatePeriodic: 5,
CertConfig: certConfig,
}
@@ -61,14 +63,14 @@ func TestController(t *testing.T) {
NodeID: 41,
NodeType: "V2ray",
}
apiclient := sspanel.New(apiConfig)
c := New(server, apiclient, controlerconfig)
apiClient := sspanel.New(apiConfig)
c := New(server, apiClient, controlerConfig, "SSpanel")
fmt.Println("Sleep 1s")
err = c.Start()
if err != nil {
t.Error(err)
}
//Explicitly triggering GC to remove garbage from config loading.
// Explicitly triggering GC to remove garbage from config loading.
runtime.GC()
{

View File

@@ -0,0 +1,7 @@
package controller
import "github.com/xtls/xray-core/common/errors"
func newError(values ...interface{}) *errors.Error {
return errors.New(values...)
}

View File

@@ -1,33 +1,39 @@
//Package generate the InbounderConfig used by add inbound
// Package controller Package generate the InboundConfig used by add inbound
package controller
import (
"crypto/rand"
"encoding/base64"
"encoding/hex"
"encoding/json"
"fmt"
"strings"
"github.com/XrayR-project/XrayR/api"
"github.com/XrayR-project/XrayR/common/legocmd"
"github.com/sagernet/sing-shadowsocks/shadowaead_2022"
C "github.com/sagernet/sing/common"
"github.com/xtls/xray-core/common/net"
"github.com/xtls/xray-core/common/uuid"
"github.com/xtls/xray-core/core"
"github.com/xtls/xray-core/infra/conf"
"github.com/XrayR-project/XrayR/api"
"github.com/XrayR-project/XrayR/common/mylego"
)
//InboundBuilder build Inbound config for different protocol
// InboundBuilder build Inbound config for different protocol
func InboundBuilder(config *Config, nodeInfo *api.NodeInfo, tag string) (*core.InboundHandlerConfig, error) {
inboundDetourConfig := &conf.InboundDetourConfig{}
// Build Listen IP address
if nodeInfo.NodeType == "Shadowsocks-Plugin" {
// Shdowsocks listen in 127.0.0.1 for safety
inboundDetourConfig.ListenOn = &conf.Address{net.ParseAddress("127.0.0.1")}
inboundDetourConfig.ListenOn = &conf.Address{Address: net.ParseAddress("127.0.0.1")}
} else if config.ListenIP != "" {
ipAddress := net.ParseAddress(config.ListenIP)
inboundDetourConfig.ListenOn = &conf.Address{ipAddress}
inboundDetourConfig.ListenOn = &conf.Address{Address: ipAddress}
}
// Build Port
portList := &conf.PortList{
Range: []conf.PortRange{{From: uint32(nodeInfo.Port), To: uint32(nodeInfo.Port)}},
Range: []conf.PortRange{{From: nodeInfo.Port, To: nodeInfo.Port}},
}
inboundDetourConfig.PortList = portList
// Build Tag
@@ -48,9 +54,10 @@ func InboundBuilder(config *Config, nodeInfo *api.NodeInfo, tag string) (*core.I
setting json.RawMessage
)
var proxySetting interface{}
var proxySetting any
// Build Protocol and Protocol setting
if nodeInfo.NodeType == "V2ray" {
switch nodeInfo.NodeType {
case "V2ray":
if nodeInfo.EnableVless {
protocol = "vless"
// Enable fallback
@@ -73,7 +80,7 @@ func InboundBuilder(config *Config, nodeInfo *api.NodeInfo, tag string) (*core.I
protocol = "vmess"
proxySetting = &conf.VMessInboundConfig{}
}
} else if nodeInfo.NodeType == "Trojan" {
case "Trojan":
protocol = "trojan"
// Enable fallback
if config.EnableFallback {
@@ -88,23 +95,36 @@ func InboundBuilder(config *Config, nodeInfo *api.NodeInfo, tag string) (*core.I
} else {
proxySetting = &conf.TrojanServerConfig{}
}
} else if nodeInfo.NodeType == "Shadowsocks" || nodeInfo.NodeType == "Shadowsocks-Plugin" {
case "Shadowsocks", "Shadowsocks-Plugin":
protocol = "shadowsocks"
proxySetting = &conf.ShadowsocksServerConfig{}
randomPasswd := uuid.New()
defaultSSuser := &conf.ShadowsocksUserConfig{
Cipher: "aes-128-gcm",
Password: randomPasswd.String(),
cipher := strings.ToLower(nodeInfo.CypherMethod)
proxySetting = &conf.ShadowsocksServerConfig{
Cipher: cipher,
Password: nodeInfo.ServerKey, // shadowsocks2022 shareKey
}
proxySetting, _ := proxySetting.(*conf.ShadowsocksServerConfig)
proxySetting.Users = append(proxySetting.Users, defaultSSuser)
// shadowsocks must have a random password
// shadowsocks2022's password == user PSK, thus should a length of string >= 32 and base64 encoder
b := make([]byte, 32)
rand.Read(b)
randPasswd := hex.EncodeToString(b)
if C.Contains(shadowaead_2022.List, cipher) {
proxySetting.Users = append(proxySetting.Users, &conf.ShadowsocksUserConfig{
Password: base64.StdEncoding.EncodeToString(b),
})
} else {
proxySetting.Password = randPasswd
}
proxySetting.NetworkList = &conf.NetworkList{"tcp", "udp"}
proxySetting.IVCheck = true
if config.DisableIVCheck {
proxySetting.IVCheck = false
}
} else if nodeInfo.NodeType == "dokodemo-door" {
case "dokodemo-door":
protocol = "dokodemo-door"
proxySetting = struct {
Host string `json:"address"`
@@ -113,14 +133,16 @@ func InboundBuilder(config *Config, nodeInfo *api.NodeInfo, tag string) (*core.I
Host: "v1.mux.cool",
NetworkList: []string{"tcp", "udp"},
}
} else {
return nil, fmt.Errorf("Unsupported node type: %s, Only support: V2ray, Trojan, Shadowsocks, and Shadowsocks-Plugin", nodeInfo.NodeType)
default:
return nil, fmt.Errorf("unsupported node type: %s, Only support: V2ray, Trojan, Shadowsocks, and Shadowsocks-Plugin", nodeInfo.NodeType)
}
setting, err := json.Marshal(proxySetting)
if err != nil {
return nil, fmt.Errorf("Marshal proxy %s config fialed: %s", nodeInfo.NodeType, err)
return nil, fmt.Errorf("marshal proxy %s config fialed: %s", nodeInfo.NodeType, err)
}
inboundDetourConfig.Protocol = protocol
inboundDetourConfig.Settings = &setting
// Build streamSettings
streamSetting = new(conf.StreamConfig)
@@ -129,13 +151,15 @@ func InboundBuilder(config *Config, nodeInfo *api.NodeInfo, tag string) (*core.I
if err != nil {
return nil, fmt.Errorf("convert TransportProtocol failed: %s", err)
}
if networkType == "tcp" {
switch networkType {
case "tcp":
tcpSetting := &conf.TCPConfig{
AcceptProxyProtocol: config.EnableProxyProtocol,
HeaderConfig: nodeInfo.Header,
}
streamSetting.TCPSettings = tcpSetting
} else if networkType == "websocket" {
case "websocket":
headers := make(map[string]string)
headers["Host"] = nodeInfo.Host
wsSettings := &conf.WebSocketConfig{
@@ -144,14 +168,14 @@ func InboundBuilder(config *Config, nodeInfo *api.NodeInfo, tag string) (*core.I
Headers: headers,
}
streamSetting.WSSettings = wsSettings
} else if networkType == "http" {
case "http":
hosts := conf.StringList{nodeInfo.Host}
httpSettings := &conf.HTTPConfig{
Host: &hosts,
Path: nodeInfo.Path,
}
streamSetting.HTTPSettings = httpSettings
} else if networkType == "grpc" {
case "grpc":
grpcSettings := &conf.GRPCConfig{
ServiceName: nodeInfo.ServiceName,
}
@@ -159,28 +183,57 @@ func InboundBuilder(config *Config, nodeInfo *api.NodeInfo, tag string) (*core.I
}
streamSetting.Network = &transportProtocol
// Build TLS and XTLS settings
if nodeInfo.EnableTLS && config.CertConfig.CertMode != "none" {
streamSetting.Security = nodeInfo.TLSType
// Build TLS and REALITY settings
var isREALITY bool
if config.DisableLocalREALITYConfig {
if nodeInfo.REALITYConfig != nil && nodeInfo.EnableREALITY {
isREALITY = true
streamSetting.Security = "reality"
r := nodeInfo.REALITYConfig
streamSetting.REALITYSettings = &conf.REALITYConfig{
Show: config.REALITYConfigs.Show,
Dest: []byte(`"` + r.Dest + `"`),
Xver: r.ProxyProtocolVer,
ServerNames: r.ServerNames,
PrivateKey: r.PrivateKey,
MinClientVer: r.MinClientVer,
MaxClientVer: r.MaxClientVer,
MaxTimeDiff: r.MaxTimeDiff,
ShortIds: r.ShortIds,
}
}
} else if config.EnableREALITY && config.REALITYConfigs != nil {
isREALITY = true
streamSetting.Security = "reality"
streamSetting.REALITYSettings = &conf.REALITYConfig{
Show: config.REALITYConfigs.Show,
Dest: []byte(`"` + config.REALITYConfigs.Dest + `"`),
Xver: config.REALITYConfigs.ProxyProtocolVer,
ServerNames: config.REALITYConfigs.ServerNames,
PrivateKey: config.REALITYConfigs.PrivateKey,
MinClientVer: config.REALITYConfigs.MinClientVer,
MaxClientVer: config.REALITYConfigs.MaxClientVer,
MaxTimeDiff: config.REALITYConfigs.MaxTimeDiff,
ShortIds: config.REALITYConfigs.ShortIds,
}
}
if !isREALITY && nodeInfo.EnableTLS && config.CertConfig.CertMode != "none" {
streamSetting.Security = "tls"
certFile, keyFile, err := getCertFile(config.CertConfig)
if err != nil {
return nil, err
}
if nodeInfo.TLSType == "tls" {
tlsSettings := &conf.TLSConfig{
RejectUnknownSNI: config.CertConfig.RejectUnknownSni,
}
tlsSettings.Certs = append(tlsSettings.Certs, &conf.TLSCertConfig{CertFile: certFile, KeyFile: keyFile, OcspStapling: 3600})
streamSetting.TLSSettings = tlsSettings
} else if nodeInfo.TLSType == "xtls" {
xtlsSettings := &conf.XTLSConfig{
RejectUnknownSNI: config.CertConfig.RejectUnknownSni,
}
xtlsSettings.Certs = append(xtlsSettings.Certs, &conf.XTLSCertConfig{CertFile: certFile, KeyFile: keyFile, OcspStapling: 3600})
streamSetting.XTLSSettings = xtlsSettings
tlsSettings := &conf.TLSConfig{
RejectUnknownSNI: config.CertConfig.RejectUnknownSni,
}
tlsSettings.Certs = append(tlsSettings.Certs, &conf.TLSCertConfig{CertFile: certFile, KeyFile: keyFile, OcspStapling: 3600})
streamSetting.TLSSettings = tlsSettings
}
// Support ProxyProtocol for any transport protocol
if networkType != "tcp" && networkType != "ws" && config.EnableProxyProtocol {
sockoptConfig := &conf.SocketConfig{
@@ -188,60 +241,59 @@ func InboundBuilder(config *Config, nodeInfo *api.NodeInfo, tag string) (*core.I
}
streamSetting.SocketSettings = sockoptConfig
}
inboundDetourConfig.Protocol = protocol
inboundDetourConfig.StreamSetting = streamSetting
inboundDetourConfig.Settings = &setting
return inboundDetourConfig.Build()
}
func getCertFile(certConfig *CertConfig) (certFile string, keyFile string, err error) {
if certConfig.CertMode == "file" {
func getCertFile(certConfig *mylego.CertConfig) (certFile string, keyFile string, err error) {
switch certConfig.CertMode {
case "file":
if certConfig.CertFile == "" || certConfig.KeyFile == "" {
return "", "", fmt.Errorf("Cert file path or key file path not exist")
return "", "", fmt.Errorf("cert file path or key file path not exist")
}
return certConfig.CertFile, certConfig.KeyFile, nil
} else if certConfig.CertMode == "dns" {
lego, err := legocmd.New()
case "dns":
lego, err := mylego.New(certConfig)
if err != nil {
return "", "", err
}
certPath, keyPath, err := lego.DNSCert(certConfig.CertDomain, certConfig.Email, certConfig.Provider, certConfig.DNSEnv)
certPath, keyPath, err := lego.DNSCert()
if err != nil {
return "", "", err
}
return certPath, keyPath, err
} else if certConfig.CertMode == "http" {
lego, err := legocmd.New()
case "http", "tls":
lego, err := mylego.New(certConfig)
if err != nil {
return "", "", err
}
certPath, keyPath, err := lego.HTTPCert(certConfig.CertDomain, certConfig.Email)
certPath, keyPath, err := lego.HTTPCert()
if err != nil {
return "", "", err
}
return certPath, keyPath, err
default:
return "", "", fmt.Errorf("unsupported certmode: %s", certConfig.CertMode)
}
return "", "", fmt.Errorf("Unsupported certmode: %s", certConfig.CertMode)
}
func buildVlessFallbacks(fallbackConfigs []*FallBackConfig) ([]*conf.VLessInboundFallback, error) {
if fallbackConfigs == nil {
return nil, fmt.Errorf("You must provide FallBackConfigs")
return nil, fmt.Errorf("you must provide FallBackConfigs")
}
vlessFallBacks := make([]*conf.VLessInboundFallback, len(fallbackConfigs))
for i, c := range fallbackConfigs {
if c.Dest == "" {
return nil, fmt.Errorf("Dest is required for fallback fialed")
return nil, fmt.Errorf("dest is required for fallback fialed")
}
var dest json.RawMessage
dest, err := json.Marshal(c.Dest)
if err != nil {
return nil, fmt.Errorf("Marshal dest %s config fialed: %s", dest, err)
return nil, fmt.Errorf("marshal dest %s config fialed: %s", dest, err)
}
vlessFallBacks[i] = &conf.VLessInboundFallback{
Name: c.SNI,
@@ -256,20 +308,20 @@ func buildVlessFallbacks(fallbackConfigs []*FallBackConfig) ([]*conf.VLessInboun
func buildTrojanFallbacks(fallbackConfigs []*FallBackConfig) ([]*conf.TrojanInboundFallback, error) {
if fallbackConfigs == nil {
return nil, fmt.Errorf("You must provide FallBackConfigs")
return nil, fmt.Errorf("you must provide FallBackConfigs")
}
trojanFallBacks := make([]*conf.TrojanInboundFallback, len(fallbackConfigs))
for i, c := range fallbackConfigs {
if c.Dest == "" {
return nil, fmt.Errorf("Dest is required for fallback fialed")
return nil, fmt.Errorf("dest is required for fallback fialed")
}
var dest json.RawMessage
dest, err := json.Marshal(c.Dest)
if err != nil {
return nil, fmt.Errorf("Marshal dest %s config fialed: %s", dest, err)
return nil, fmt.Errorf("marshal dest %s config fialed: %s", dest, err)
}
trojanFallBacks[i] = &conf.TrojanInboundFallback{
Name: c.SNI,

View File

@@ -4,6 +4,7 @@ import (
"testing"
"github.com/XrayR-project/XrayR/api"
"github.com/XrayR-project/XrayR/common/mylego"
. "github.com/XrayR-project/XrayR/service/controller"
)
@@ -18,9 +19,8 @@ func TestBuildV2ray(t *testing.T) {
Host: "test.test.tk",
Path: "v2ray",
EnableTLS: false,
TLSType: "tls",
}
certConfig := &CertConfig{
certConfig := &mylego.CertConfig{
CertMode: "http",
CertDomain: "test.test.tk",
Provider: "alidns",
@@ -29,7 +29,7 @@ func TestBuildV2ray(t *testing.T) {
config := &Config{
CertConfig: certConfig,
}
_, err := InboundBuilder(config, nodeInfo)
_, err := InboundBuilder(config, nodeInfo, "test_tag")
if err != nil {
t.Error(err)
}
@@ -46,12 +46,11 @@ func TestBuildTrojan(t *testing.T) {
Host: "trojan.test.tk",
Path: "v2ray",
EnableTLS: false,
TLSType: "tls",
}
DNSEnv := make(map[string]string)
DNSEnv["ALICLOUD_ACCESS_KEY"] = "aaa"
DNSEnv["ALICLOUD_SECRET_KEY"] = "bbb"
certConfig := &CertConfig{
certConfig := &mylego.CertConfig{
CertMode: "dns",
CertDomain: "trojan.test.tk",
Provider: "alidns",
@@ -61,7 +60,7 @@ func TestBuildTrojan(t *testing.T) {
config := &Config{
CertConfig: certConfig,
}
_, err := InboundBuilder(config, nodeInfo)
_, err := InboundBuilder(config, nodeInfo, "test_tag")
if err != nil {
t.Error(err)
}
@@ -78,12 +77,11 @@ func TestBuildSS(t *testing.T) {
Host: "test.test.tk",
Path: "v2ray",
EnableTLS: false,
TLSType: "tls",
}
DNSEnv := make(map[string]string)
DNSEnv["ALICLOUD_ACCESS_KEY"] = "aaa"
DNSEnv["ALICLOUD_SECRET_KEY"] = "bbb"
certConfig := &CertConfig{
certConfig := &mylego.CertConfig{
CertMode: "dns",
CertDomain: "trojan.test.tk",
Provider: "alidns",
@@ -93,7 +91,7 @@ func TestBuildSS(t *testing.T) {
config := &Config{
CertConfig: certConfig,
}
_, err := InboundBuilder(config, nodeInfo)
_, err := InboundBuilder(config, nodeInfo, "test_tag")
if err != nil {
t.Error(err)
}

View File

@@ -4,13 +4,14 @@ import (
"encoding/json"
"fmt"
"github.com/XrayR-project/XrayR/api"
"github.com/xtls/xray-core/common/net"
"github.com/xtls/xray-core/core"
"github.com/xtls/xray-core/infra/conf"
"github.com/XrayR-project/XrayR/api"
)
//OutboundBuilder build freedom outbund config for addoutbound
// OutboundBuilder build freedom outbound config for addOutbound
func OutboundBuilder(config *Config, nodeInfo *api.NodeInfo, tag string) (*core.OutboundHandlerConfig, error) {
outboundDetourConfig := &conf.OutboundDetourConfig{}
outboundDetourConfig.Protocol = "freedom"
@@ -19,11 +20,11 @@ func OutboundBuilder(config *Config, nodeInfo *api.NodeInfo, tag string) (*core.
// Build Send IP address
if config.SendIP != "" {
ipAddress := net.ParseAddress(config.SendIP)
outboundDetourConfig.SendThrough = &conf.Address{ipAddress}
outboundDetourConfig.SendThrough = &conf.Address{Address: ipAddress}
}
// Freedom Protocol setting
var domainStrategy string = "Asis"
var domainStrategy = "Asis"
if config.EnableDNS {
if config.DNSType != "" {
domainStrategy = config.DNSType
@@ -41,7 +42,7 @@ func OutboundBuilder(config *Config, nodeInfo *api.NodeInfo, tag string) (*core.
var setting json.RawMessage
setting, err := json.Marshal(proxySetting)
if err != nil {
return nil, fmt.Errorf("Marshal proxy %s config fialed: %s", nodeInfo.NodeType, err)
return nil, fmt.Errorf("marshal proxy %s config fialed: %s", nodeInfo.NodeType, err)
}
outboundDetourConfig.Settings = &setting
return outboundDetourConfig.Build()

View File

@@ -1,26 +1,35 @@
package controller
import (
"encoding/base64"
"fmt"
"strings"
"github.com/XrayR-project/XrayR/api"
"github.com/sagernet/sing-shadowsocks/shadowaead_2022"
C "github.com/sagernet/sing/common"
"github.com/xtls/xray-core/common/protocol"
"github.com/xtls/xray-core/common/serial"
"github.com/xtls/xray-core/infra/conf"
"github.com/xtls/xray-core/proxy/shadowsocks"
"github.com/xtls/xray-core/proxy/shadowsocks_2022"
"github.com/xtls/xray-core/proxy/trojan"
"github.com/xtls/xray-core/proxy/vless"
"github.com/XrayR-project/XrayR/api"
)
var AEADMethod = []shadowsocks.CipherType{shadowsocks.CipherType_AES_128_GCM, shadowsocks.CipherType_AES_256_GCM, shadowsocks.CipherType_CHACHA20_POLY1305, shadowsocks.CipherType_XCHACHA20_POLY1305}
var AEADMethod = map[shadowsocks.CipherType]uint8{
shadowsocks.CipherType_AES_128_GCM: 0,
shadowsocks.CipherType_AES_256_GCM: 0,
shadowsocks.CipherType_CHACHA20_POLY1305: 0,
shadowsocks.CipherType_XCHACHA20_POLY1305: 0,
}
func (c *Controller) buildVmessUser(userInfo *[]api.UserInfo, serverAlterID uint16) (users []*protocol.User) {
func (c *Controller) buildVmessUser(userInfo *[]api.UserInfo) (users []*protocol.User) {
users = make([]*protocol.User, len(*userInfo))
for i, user := range *userInfo {
vmessAccount := &conf.VMessAccount{
ID: user.UUID,
AlterIds: serverAlterID,
Security: "auto",
}
users[i] = &protocol.User{
@@ -37,7 +46,7 @@ func (c *Controller) buildVlessUser(userInfo *[]api.UserInfo) (users []*protocol
for i, user := range *userInfo {
vlessAccount := &vless.Account{
Id: user.UUID,
Flow: "xtls-rprx-direct",
Flow: c.nodeInfo.VlessFlow,
}
users[i] = &protocol.User{
Level: 0,
@@ -53,7 +62,6 @@ func (c *Controller) buildTrojanUser(userInfo *[]api.UserInfo) (users []*protoco
for i, user := range *userInfo {
trojanAccount := &trojan.Account{
Password: user.UUID,
Flow: "xtls-rprx-direct",
}
users[i] = &protocol.User{
Level: 0,
@@ -65,43 +73,75 @@ func (c *Controller) buildTrojanUser(userInfo *[]api.UserInfo) (users []*protoco
}
func (c *Controller) buildSSUser(userInfo *[]api.UserInfo, method string) (users []*protocol.User) {
users = make([]*protocol.User, 0)
users = make([]*protocol.User, len(*userInfo))
cypherMethod := cipherFromString(method)
for _, user := range *userInfo {
ssAccount := &shadowsocks.Account{
Password: user.Passwd,
CipherType: cypherMethod,
for i, user := range *userInfo {
// shadowsocks2022 Key = "openssl rand -base64 32" and multi users needn't cipher method
if C.Contains(shadowaead_2022.List, strings.ToLower(method)) {
e := c.buildUserTag(&user)
userKey, err := c.checkShadowsocksPassword(user.Passwd, method)
if err != nil {
newError(fmt.Errorf("[UID: %d] %s", user.UID, err)).AtError().WriteToLog()
continue
}
users[i] = &protocol.User{
Level: 0,
Email: e,
Account: serial.ToTypedMessage(&shadowsocks_2022.User{
Key: userKey,
Email: e,
Level: 0,
}),
}
} else {
users[i] = &protocol.User{
Level: 0,
Email: c.buildUserTag(&user),
Account: serial.ToTypedMessage(&shadowsocks.Account{
Password: user.Passwd,
CipherType: cipherFromString(method),
}),
}
}
users = append(users, &protocol.User{
Level: 0,
Email: c.buildUserTag(&user),
Account: serial.ToTypedMessage(ssAccount),
})
}
return users
}
func (c *Controller) buildSSPluginUser(userInfo *[]api.UserInfo) (users []*protocol.User) {
users = make([]*protocol.User, 0)
users = make([]*protocol.User, len(*userInfo))
for _, user := range *userInfo {
// Check if the cypher method is AEAD
cypherMethod := cipherFromString(user.Method)
for _, aeadMethod := range AEADMethod {
if aeadMethod == cypherMethod {
ssAccount := &shadowsocks.Account{
Password: user.Passwd,
CipherType: cypherMethod,
for i, user := range *userInfo {
// shadowsocks2022 Key = openssl rand -base64 32 and multi users needn't cipher method
if C.Contains(shadowaead_2022.List, strings.ToLower(user.Method)) {
e := c.buildUserTag(&user)
userKey, err := c.checkShadowsocksPassword(user.Passwd, user.Method)
if err != nil {
newError(fmt.Errorf("[UID: %d] %s", user.UID, err)).AtError().WriteToLog()
continue
}
users[i] = &protocol.User{
Level: 0,
Email: e,
Account: serial.ToTypedMessage(&shadowsocks_2022.User{
Key: userKey,
Email: e,
Level: 0,
}),
}
} else {
// Check if the cypher method is AEAD
cypherMethod := cipherFromString(user.Method)
if _, ok := AEADMethod[cypherMethod]; ok {
users[i] = &protocol.User{
Level: 0,
Email: c.buildUserTag(&user),
Account: serial.ToTypedMessage(&shadowsocks.Account{
Password: user.Passwd,
CipherType: cypherMethod,
}),
}
users = append(users, &protocol.User{
Level: 0,
Email: c.buildUserTag(&user),
Account: serial.ToTypedMessage(ssAccount),
})
}
}
}
return users
}
@@ -124,3 +164,23 @@ func cipherFromString(c string) shadowsocks.CipherType {
func (c *Controller) buildUserTag(user *api.UserInfo) string {
return fmt.Sprintf("%s|%s|%d", c.Tag, user.Email, user.UID)
}
func (c *Controller) checkShadowsocksPassword(password string, method string) (string, error) {
if strings.Contains(c.panelType, "V2board") {
var userKey string
if len(password) < 16 {
return "", newError("shadowsocks2022 key's length must be greater than 16").AtWarning()
}
if method == "2022-blake3-aes-128-gcm" {
userKey = password[:16]
} else {
if len(password) < 32 {
return "", newError("shadowsocks2022 key's length must be greater than 32").AtWarning()
}
userKey = password[:32]
}
return base64.StdEncoding.EncodeToString([]byte(userKey)), nil
} else {
return password, nil
}
}

View File

@@ -1,5 +1,5 @@
// Package service contains all the services used by XrayR
// To implement an service, one needs to implement the interface below.
// To implement a service, one needs to implement the interface below.
package service
// Service is the interface of all the services running in the panel