Update On Sat Feb 7 19:47:43 CET 2026

This commit is contained in:
github-action[bot]
2026-02-07 19:47:43 +01:00
parent 05f4e1b204
commit 7e419f2fca
139 changed files with 6863 additions and 4333 deletions
+1
View File
@@ -1264,3 +1264,4 @@ Update On Tue Feb 3 20:07:51 CET 2026
Update On Wed Feb 4 20:03:26 CET 2026
Update On Thu Feb 5 20:02:24 CET 2026
Update On Fri Feb 6 20:02:22 CET 2026
Update On Sat Feb 7 19:47:34 CET 2026
+1 -1
View File
@@ -178,7 +178,7 @@ func NewWireGuard(option WireGuardOption) (*WireGuard, error) {
},
}
outbound.dialer = option.NewDialer(outbound.DialOptions())
singDialer := proxydialer.NewSlowDownSingDialer(proxydialer.NewSingDialer(outbound.dialer), slowdown.New())
singDialer := proxydialer.NewSingDialer(proxydialer.NewSlowDownDialer(outbound.dialer, slowdown.New()))
var reserved [3]uint8
if len(option.Reserved) > 0 {
+10
View File
@@ -58,6 +58,11 @@ func Main(args []string) {
fmt.Println("Seed: " + seedBase64)
fmt.Println("Client: " + clientBase64)
fmt.Println("Hash32: " + hash32Base64)
fmt.Println("-----------------------")
fmt.Println(" Lazy-Config ")
fmt.Println("-----------------------")
fmt.Printf("[Server] decryption: \"mlkem768x25519plus.native.600s.%s\"\n", seedBase64)
fmt.Printf("[Client] encryption: \"mlkem768x25519plus.native.0rtt.%s\"\n", clientBase64)
case "vless-x25519":
var privateKey string
if len(args) > 1 {
@@ -70,6 +75,11 @@ func Main(args []string) {
fmt.Println("PrivateKey: " + privateKeyBase64)
fmt.Println("Password: " + passwordBase64)
fmt.Println("Hash32: " + hash32Base64)
fmt.Println("-----------------------")
fmt.Println(" Lazy-Config ")
fmt.Println("-----------------------")
fmt.Printf("[Server] decryption: \"mlkem768x25519plus.native.600s.%s\"\n", privateKeyBase64)
fmt.Printf("[Client] encryption: \"mlkem768x25519plus.native.0rtt.%s\"\n", passwordBase64)
case "sudoku-keypair":
privateKey, publicKey, err := sudoku.GenKeyPair()
if err != nil {
+18 -21
View File
@@ -8,7 +8,6 @@ import (
"strings"
_ "unsafe"
"github.com/metacubex/mihomo/common/utils"
"github.com/metacubex/mihomo/component/resolver/hosts"
"github.com/metacubex/mihomo/component/trie"
"github.com/metacubex/randv2"
@@ -66,37 +65,35 @@ type HostValue struct {
Domain string
}
func NewHostValue(value any) (HostValue, error) {
func NewHostValue(value []string) (HostValue, error) {
isDomain := true
ips := make([]netip.Addr, 0)
ips := make([]netip.Addr, 0, len(value))
domain := ""
if valueArr, err := utils.ToStringSlice(value); err != nil {
return HostValue{}, err
} else {
if len(valueArr) > 1 {
switch len(value) {
case 0:
return HostValue{}, errors.New("value is empty")
case 1:
host := value[0]
if ip, err := netip.ParseAddr(host); err == nil {
ips = append(ips, ip.Unmap())
isDomain = false
for _, str := range valueArr {
if ip, err := netip.ParseAddr(str); err == nil {
ips = append(ips, ip.Unmap())
} else {
return HostValue{}, err
}
}
} else if len(valueArr) == 1 {
host := valueArr[0]
if ip, err := netip.ParseAddr(host); err == nil {
} else {
domain = host
}
default: // > 1
isDomain = false
for _, str := range value {
if ip, err := netip.ParseAddr(str); err == nil {
ips = append(ips, ip.Unmap())
isDomain = false
} else {
domain = host
return HostValue{}, err
}
}
}
if isDomain {
return NewHostValueByDomain(domain)
} else {
return NewHostValueByIPs(ips)
}
return NewHostValueByIPs(ips)
}
func NewHostValueByIPs(ips []netip.Addr) (HostValue, error) {
+8 -7
View File
@@ -1115,22 +1115,23 @@ func parseHosts(cfg *RawConfig) (*trie.DomainTrie[resolver.HostValue], error) {
if len(cfg.Hosts) != 0 {
for domain, anyValue := range cfg.Hosts {
if str, ok := anyValue.(string); ok && str == "lan" {
hosts, err := utils.ToStringSlice(anyValue)
if err != nil {
return nil, err
}
if len(hosts) == 1 && hosts[0] == "lan" {
if addrs, err := net.InterfaceAddrs(); err != nil {
log.Errorln("insert lan to host error: %s", err)
} else {
ips := make([]netip.Addr, 0)
hosts = make([]string, 0, len(addrs))
for _, addr := range addrs {
if ipnet, ok := addr.(*net.IPNet); ok && !ipnet.IP.IsLoopback() && !ipnet.IP.IsLinkLocalUnicast() {
if ip, err := netip.ParseAddr(ipnet.IP.String()); err == nil {
ips = append(ips, ip)
}
hosts = append(hosts, ipnet.IP.String())
}
}
anyValue = ips
}
}
value, err := resolver.NewHostValue(anyValue)
value, err := resolver.NewHostValue(hosts)
if err != nil {
return nil, fmt.Errorf("%s is not a valid value", anyValue)
}
+1 -1
View File
@@ -38,7 +38,7 @@ require (
github.com/metacubex/sing-wireguard v0.0.0-20250503063753-2dc62acc626f
github.com/metacubex/smux v0.0.0-20260105030934-d0c8756d3141
github.com/metacubex/tfo-go v0.0.0-20251130171125-413e892ac443
github.com/metacubex/tls v0.1.3
github.com/metacubex/tls v0.1.4
github.com/metacubex/utls v1.8.4
github.com/metacubex/wireguard-go v0.0.0-20250820062549-a6cecdd7f57f
github.com/mroth/weightedrand/v2 v2.1.0
+2 -2
View File
@@ -141,8 +141,8 @@ github.com/metacubex/smux v0.0.0-20260105030934-d0c8756d3141 h1:DK2l6m2Fc85H2Bhi
github.com/metacubex/smux v0.0.0-20260105030934-d0c8756d3141/go.mod h1:/yI4OiGOSn0SURhZdJF3CbtPg3nwK700bG8TZLMBvAg=
github.com/metacubex/tfo-go v0.0.0-20251130171125-413e892ac443 h1:H6TnfM12tOoTizYE/qBHH3nEuibIelmHI+BVSxVJr8o=
github.com/metacubex/tfo-go v0.0.0-20251130171125-413e892ac443/go.mod h1:l9oLnLoEXyGZ5RVLsh7QCC5XsouTUyKk4F2nLm2DHLw=
github.com/metacubex/tls v0.1.3 h1:nyjA8GNYGaLVSNRSqWoNNdXSCCFiQABTyrPJmkuSL20=
github.com/metacubex/tls v0.1.3/go.mod h1:0XeVdL0cBw+8i5Hqy3lVeP9IyD/LFTq02ExvHM6rzEM=
github.com/metacubex/tls v0.1.4 h1:Gm5GrkyMUh52gYOMIAQ1kHIym4v4M3Qb87Wsmd8Kpdc=
github.com/metacubex/tls v0.1.4/go.mod h1:0XeVdL0cBw+8i5Hqy3lVeP9IyD/LFTq02ExvHM6rzEM=
github.com/metacubex/utls v1.8.4 h1:HmL9nUApDdWSkgUyodfwF6hSjtiwCGGdyhaSpEejKpg=
github.com/metacubex/utls v1.8.4/go.mod h1:kncGGVhFaoGn5M3pFe3SXhZCzsbCJayNOH4UEqTKTko=
github.com/metacubex/wireguard-go v0.0.0-20250820062549-a6cecdd7f57f h1:FGBPRb1zUabhPhDrlKEjQ9lgIwQ6cHL4x8M9lrERhbk=
+23 -10
View File
@@ -326,9 +326,9 @@ dependencies = [
[[package]]
name = "anyhow"
version = "1.0.99"
version = "1.0.101"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b0674a1ddeecb70197781e945de4b3b8ffb61fa939a5597bcf48503737663100"
checksum = "5f0e0fee31ef5ed1ba1316088939cea399010ed7731dba877ed44aeb407a75ea"
[[package]]
name = "arbitrary"
@@ -4469,6 +4469,20 @@ dependencies = [
"windows-sys 0.52.0",
]
[[package]]
name = "interprocess"
version = "2.3.0"
source = "git+https://github.com/libnyanpasu/interprocess.git?branch=fix/linux-32bit-build#33b6a835833ca7fe5276c7dd8a13bfbdac7a80a2"
dependencies = [
"doctest-file",
"futures-core",
"libc",
"recvmsg",
"tokio",
"widestring 1.2.0",
"windows-sys 0.52.0",
]
[[package]]
name = "intrusive-collections"
version = "0.9.7"
@@ -5627,8 +5641,8 @@ dependencies = [
[[package]]
name = "nyanpasu-ipc"
version = "1.4.1"
source = "git+https://github.com/libnyanpasu/nyanpasu-service.git#8f2eada8c8625a6453bdb7a6e617455cf04d857d"
version = "1.4.2"
source = "git+https://github.com/libnyanpasu/nyanpasu-service.git#3742aaaebc475f90d8ed7774aa7b6c6ff263b90e"
dependencies = [
"anyhow",
"axum",
@@ -5639,7 +5653,7 @@ dependencies = [
"hyper",
"hyper-util",
"indexmap 2.12.1",
"interprocess",
"interprocess 2.3.0 (git+https://github.com/libnyanpasu/interprocess.git?branch=fix/linux-32bit-build)",
"nyanpasu-utils",
"pin-project-lite",
"serde",
@@ -8217,7 +8231,7 @@ dependencies = [
[[package]]
name = "serde_yaml_ng"
version = "0.10.0"
source = "git+https://github.com/libnyanpasu/serde-yaml-ng.git?branch=feat/specta#3078760ab9e9a3a9e18ebb4c5d3e577eb74c4a5c"
source = "git+https://github.com/libnyanpasu/serde-yaml-ng.git?branch=feat/specta-update#363da138dc97f90ef0a356f971c2dc1d8fe04c9e"
dependencies = [
"indexmap 2.12.1",
"itoa",
@@ -8378,11 +8392,10 @@ checksum = "d66dc143e6b11c1eddc06d5c423cfc97062865baf299914ab64caa38182078fe"
[[package]]
name = "simd-json"
version = "0.15.1"
version = "0.16.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c962f626b54771990066e5435ec8331d1462576cd2d1e62f24076ae014f92112"
checksum = "90daf33666402178ddbb5d595e6d5e6d7d372da948e23ea26762f5a23e02a04e"
dependencies = [
"getrandom 0.3.3",
"halfbrown",
"ref-cast",
"serde",
@@ -9136,7 +9149,7 @@ name = "tauri-plugin-deep-link"
version = "0.1.2"
dependencies = [
"dirs 6.0.0",
"interprocess",
"interprocess 2.3.0 (registry+https://github.com/rust-lang/crates.io-index)",
"log",
"objc2 0.6.3",
"once_cell",
@@ -20,7 +20,7 @@ once_cell = "1"
tauri-utils = { version = "2" }
[target.'cfg(windows)'.dependencies]
interprocess = { version = "2" }
interprocess = { version = "2", features = ["tokio"] }
windows-sys = { version = "0.60", features = [
"Win32_Foundation",
"Win32_UI_Input_KeyboardAndMouse",
+1 -1
View File
@@ -97,7 +97,7 @@ sysproxy = { git = "https://github.com/libnyanpasu/sysproxy-rs.git", version = "
# Serialization
serde = { version = "1.0", features = ["derive"] }
serde_json = { version = "1.0", features = ["preserve_order"] }
serde_yaml = { version = "0.10", package = "serde_yaml_ng", branch = "feat/specta", git = "https://github.com/libnyanpasu/serde-yaml-ng.git", features = [
serde_yaml = { version = "0.10", package = "serde_yaml_ng", branch = "feat/specta-update", git = "https://github.com/libnyanpasu/serde-yaml-ng.git", features = [
"specta",
] }
bincode = { version = "2.0.1", default-features = false, features = [
@@ -18,7 +18,7 @@
"lodash-es": "4.17.23",
"ofetch": "1.5.1",
"react": "19.2.4",
"swr": "2.3.8"
"swr": "2.4.0"
},
"devDependencies": {
"@types/lodash-es": "4.17.12",
@@ -48,11 +48,11 @@
"country-emoji": "1.5.6",
"dayjs": "1.11.19",
"framer-motion": "12.33.0",
"i18next": "25.7.3",
"i18next": "25.8.4",
"jotai": "2.17.1",
"json-schema": "0.4.0",
"material-react-table": "3.2.1",
"monaco-editor": "0.54.0",
"monaco-editor": "0.55.1",
"mui-color-input": "7.0.0",
"react": "19.2.4",
"react-dom": "19.2.4",
@@ -65,7 +65,7 @@
"react-split-grid": "1.0.4",
"react-use": "17.6.0",
"rxjs": "7.8.2",
"swr": "2.3.8",
"swr": "2.4.0",
"virtua": "0.46.6",
"vite-bundle-visualizer": "1.2.1"
},
@@ -76,9 +76,9 @@
"@iconify/json": "2.2.436",
"@monaco-editor/react": "4.7.0",
"@tanstack/react-query": "5.90.20",
"@tanstack/react-router": "1.158.1",
"@tanstack/react-router-devtools": "1.158.1",
"@tanstack/router-plugin": "1.158.1",
"@tanstack/react-router": "1.158.4",
"@tanstack/react-router-devtools": "1.158.4",
"@tanstack/router-plugin": "1.158.4",
"@tauri-apps/plugin-clipboard-manager": "2.3.0",
"@tauri-apps/plugin-dialog": "2.4.0",
"@tauri-apps/plugin-fs": "2.4.2",
@@ -110,6 +110,6 @@
"vite-plugin-sass-dts": "1.3.35",
"vite-plugin-svgr": "4.5.0",
"vite-tsconfig-paths": "5.1.4",
"zod": "4.2.1"
"zod": "4.3.6"
}
}
@@ -5,12 +5,13 @@ import { type JSONSchema7 } from 'json-schema'
import nyanpasuMergeSchema from 'meta-json-schema/schemas/clash-nyanpasu-merge-json-schema.json'
import clashMetaSchema from 'meta-json-schema/schemas/meta-json-schema.json'
import { type editor } from 'monaco-editor'
import * as monaco from 'monaco-editor'
import { configureMonacoYaml } from 'monaco-yaml'
import { nanoid } from 'nanoid'
import { useCallback, useMemo, useRef } from 'react'
// schema
import { themeMode } from '@/store'
import MonacoEditor, { type Monaco } from '@monaco-editor/react'
import MonacoEditor from '@monaco-editor/react'
import { openThat } from '@nyanpasu/interface'
import { cn } from '@nyanpasu/ui'
@@ -30,10 +31,10 @@ export interface ProfileMonacoViewRef {
let initd = false
export const beforeEditorMount = (monaco: Monaco) => {
export const beforeEditorMount = () => {
if (!initd) {
monaco.languages.typescript.javascriptDefaults.setCompilerOptions({
target: monaco.languages.typescript.ScriptTarget.ES2020,
monaco.typescript.javascriptDefaults.setCompilerOptions({
target: monaco.typescript.ScriptTarget.ES2020,
allowNonTsExtensions: true,
allowJs: true,
})
@@ -1,8 +1,8 @@
import nyanpasuMergeSchema from 'meta-json-schema/schemas/clash-nyanpasu-merge-json-schema.json'
import clashMetaSchema from 'meta-json-schema/schemas/meta-json-schema.json'
import * as monaco from 'monaco-editor'
import { configureMonacoYaml } from 'monaco-yaml'
import { OS } from '@/consts'
import { Monaco } from '@monaco-editor/react'
export const MONACO_FONT_FAMILY =
'"Cascadia Code NF",' +
@@ -19,13 +19,13 @@ export const MONACO_FONT_FAMILY =
let initd = false
export const beforeEditorMount = (monaco: Monaco) => {
export const beforeEditorMount = () => {
if (initd) {
return
}
monaco.languages.typescript.javascriptDefaults.setCompilerOptions({
target: monaco.languages.typescript.ScriptTarget.ES2020,
monaco.typescript.javascriptDefaults.setCompilerOptions({
target: monaco.typescript.ScriptTarget.ES2020,
allowNonTsExtensions: true,
allowJs: true,
})
@@ -7,7 +7,7 @@ import 'monaco-editor/esm/vs/basic-languages/yaml/yaml.contribution.js'
import 'monaco-editor/esm/vs/editor/editor.all.js'
import 'monaco-editor/esm/vs/editor/contrib/links/browser/links.js'
// language services
import * as monaco from 'monaco-editor/esm/vs/editor/editor.api'
import * as monaco from 'monaco-editor'
import 'monaco-editor/esm/vs/language/typescript/monaco.contribution.js'
import editorWorker from 'monaco-editor/esm/vs/editor/editor.worker?worker'
import jsonWorker from 'monaco-editor/esm/vs/language/json/json.worker?worker'
+2 -2
View File
@@ -70,10 +70,10 @@
"cross-env": "10.1.0",
"dedent": "1.7.1",
"globals": "16.5.0",
"knip": "5.83.0",
"knip": "5.83.1",
"lint-staged": "16.2.7",
"npm-run-all2": "8.0.4",
"oxlint": "^1.43.0",
"oxlint": "1.43.0",
"postcss": "8.5.6",
"postcss-html": "1.8.1",
"postcss-import": "16.1.1",
+103 -90
View File
@@ -59,8 +59,8 @@ importers:
specifier: 16.5.0
version: 16.5.0
knip:
specifier: 5.83.0
version: 5.83.0(@types/node@24.10.11)(typescript@5.9.3)
specifier: 5.83.1
version: 5.83.1(@types/node@24.10.11)(typescript@5.9.3)
lint-staged:
specifier: 16.2.7
version: 16.2.7
@@ -68,7 +68,7 @@ importers:
specifier: 8.0.4
version: 8.0.4
oxlint:
specifier: ^1.43.0
specifier: 1.43.0
version: 1.43.0
postcss:
specifier: 8.5.6
@@ -149,8 +149,8 @@ importers:
specifier: 19.2.4
version: 19.2.4
swr:
specifier: 2.3.8
version: 2.3.8(react@19.2.4)
specifier: 2.4.0
version: 2.4.0(react@19.2.4)
devDependencies:
'@types/lodash-es':
specifier: 4.17.12
@@ -241,7 +241,7 @@ importers:
version: 3.13.18(react-dom@19.2.4(react@19.2.4))(react@19.2.4)
'@tanstack/router-zod-adapter':
specifier: 1.81.5
version: 1.81.5(@tanstack/react-router@1.158.1(react-dom@19.2.4(react@19.2.4))(react@19.2.4))(zod@4.2.1)
version: 1.81.5(@tanstack/react-router@1.158.4(react-dom@19.2.4(react@19.2.4))(react@19.2.4))(zod@4.3.6)
'@tauri-apps/api':
specifier: 2.8.0
version: 2.8.0
@@ -276,8 +276,8 @@ importers:
specifier: 12.33.0
version: 12.33.0(@emotion/is-prop-valid@1.3.0)(react-dom@19.2.4(react@19.2.4))(react@19.2.4)
i18next:
specifier: 25.7.3
version: 25.7.3(typescript@5.9.3)
specifier: 25.8.4
version: 25.8.4(typescript@5.9.3)
jotai:
specifier: 2.17.1
version: 2.17.1(@babel/core@7.29.0)(@babel/template@7.28.6)(@types/react@19.2.13)(react@19.2.4)
@@ -288,8 +288,8 @@ importers:
specifier: npm:@greenhat616/material-react-table@4.0.0
version: '@greenhat616/material-react-table@4.0.0(ea8a682ce6028a634854fb53683ad162)'
monaco-editor:
specifier: 0.54.0
version: 0.54.0
specifier: 0.55.1
version: 0.55.1
mui-color-input:
specifier: 7.0.0
version: 7.0.0(@emotion/react@11.14.0(@types/react@19.2.13)(react@19.2.4))(@emotion/styled@11.14.1(@emotion/react@11.14.0(@types/react@19.2.13)(react@19.2.4))(@types/react@19.2.13)(react@19.2.4))(@mui/material@7.3.7(@emotion/react@11.14.0(@types/react@19.2.13)(react@19.2.4))(@emotion/styled@11.14.1(@emotion/react@11.14.0(@types/react@19.2.13)(react@19.2.4))(@types/react@19.2.13)(react@19.2.4))(@types/react@19.2.13)(react-dom@19.2.4(react@19.2.4))(react@19.2.4))(@types/react@19.2.13)(react-dom@19.2.4(react@19.2.4))(react@19.2.4)
@@ -313,7 +313,7 @@ importers:
version: 8.2.0(5eba25cbc188b715a2272e829a88aafa)
react-i18next:
specifier: 15.7.4
version: 15.7.4(i18next@25.7.3(typescript@5.9.3))(react-dom@19.2.4(react@19.2.4))(react@19.2.4)(typescript@5.9.3)
version: 15.7.4(i18next@25.8.4(typescript@5.9.3))(react-dom@19.2.4(react@19.2.4))(react@19.2.4)(typescript@5.9.3)
react-markdown:
specifier: 10.1.0
version: 10.1.0(@types/react@19.2.13)(react@19.2.4)
@@ -327,8 +327,8 @@ importers:
specifier: 7.8.2
version: 7.8.2
swr:
specifier: 2.3.8
version: 2.3.8(react@19.2.4)
specifier: 2.4.0
version: 2.4.0(react@19.2.4)
virtua:
specifier: 0.46.6
version: 0.46.6(react-dom@19.2.4(react@19.2.4))(react@19.2.4)(solid-js@1.9.5)
@@ -350,19 +350,19 @@ importers:
version: 2.2.436
'@monaco-editor/react':
specifier: 4.7.0
version: 4.7.0(monaco-editor@0.54.0)(react-dom@19.2.4(react@19.2.4))(react@19.2.4)
version: 4.7.0(monaco-editor@0.55.1)(react-dom@19.2.4(react@19.2.4))(react@19.2.4)
'@tanstack/react-query':
specifier: 5.90.20
version: 5.90.20(react@19.2.4)
'@tanstack/react-router':
specifier: 1.158.1
version: 1.158.1(react-dom@19.2.4(react@19.2.4))(react@19.2.4)
specifier: 1.158.4
version: 1.158.4(react-dom@19.2.4(react@19.2.4))(react@19.2.4)
'@tanstack/react-router-devtools':
specifier: 1.158.1
version: 1.158.1(@tanstack/react-router@1.158.1(react-dom@19.2.4(react@19.2.4))(react@19.2.4))(@tanstack/router-core@1.158.1)(csstype@3.2.3)(react-dom@19.2.4(react@19.2.4))(react@19.2.4)
specifier: 1.158.4
version: 1.158.4(@tanstack/react-router@1.158.4(react-dom@19.2.4(react@19.2.4))(react@19.2.4))(@tanstack/router-core@1.158.4)(csstype@3.2.3)(react-dom@19.2.4(react@19.2.4))(react@19.2.4)
'@tanstack/router-plugin':
specifier: 1.158.1
version: 1.158.1(@tanstack/react-router@1.158.1(react-dom@19.2.4(react@19.2.4))(react@19.2.4))(vite@7.3.1(@types/node@24.10.11)(jiti@2.6.1)(less@4.2.0)(lightningcss@1.30.2)(sass-embedded@1.97.3)(sass@1.97.3)(stylus@0.62.0)(terser@5.36.0)(tsx@4.21.0)(yaml@2.8.1))
specifier: 1.158.4
version: 1.158.4(@tanstack/react-router@1.158.4(react-dom@19.2.4(react@19.2.4))(react@19.2.4))(vite@7.3.1(@types/node@24.10.11)(jiti@2.6.1)(less@4.2.0)(lightningcss@1.30.2)(sass-embedded@1.97.3)(sass@1.97.3)(stylus@0.62.0)(terser@5.36.0)(tsx@4.21.0)(yaml@2.8.1))
'@tauri-apps/plugin-clipboard-manager':
specifier: 2.3.0
version: 2.3.0
@@ -422,7 +422,7 @@ importers:
version: 1.19.19
monaco-yaml:
specifier: 5.4.0
version: 5.4.0(monaco-editor@0.54.0)
version: 5.4.0(monaco-editor@0.55.1)
nanoid:
specifier: 5.1.6
version: 5.1.6
@@ -457,8 +457,8 @@ importers:
specifier: 5.1.4
version: 5.1.4(typescript@5.9.3)(vite@7.3.1(@types/node@24.10.11)(jiti@2.6.1)(less@4.2.0)(lightningcss@1.30.2)(sass-embedded@1.97.3)(sass@1.97.3)(stylus@0.62.0)(terser@5.36.0)(tsx@4.21.0)(yaml@2.8.1))
zod:
specifier: 4.2.1
version: 4.2.1
specifier: 4.3.6
version: 4.3.6
frontend/ui:
dependencies:
@@ -512,7 +512,7 @@ importers:
version: 6.0.0(react@19.2.4)
react-i18next:
specifier: 15.7.4
version: 15.7.4(i18next@25.7.3(typescript@5.9.3))(react-dom@19.2.4(react@19.2.4))(react@19.2.4)(typescript@5.9.3)
version: 15.7.4(i18next@25.8.4(typescript@5.9.3))(react-dom@19.2.4(react@19.2.4))(react@19.2.4)(typescript@5.9.3)
react-use:
specifier: 17.6.0
version: 17.6.0(react-dom@19.2.4(react@19.2.4))(react@19.2.4)
@@ -575,8 +575,8 @@ importers:
specifier: 7.7.4
version: 7.7.4
zod:
specifier: 4.2.1
version: 4.2.1
specifier: 4.3.6
version: 4.3.6
devDependencies:
'@octokit/types':
specifier: 16.0.0
@@ -612,8 +612,8 @@ importers:
specifier: 2.26.22
version: 2.26.22
undici:
specifier: 7.20.0
version: 7.20.0
specifier: 7.21.0
version: 7.21.0
yargs:
specifier: 18.0.0
version: 18.0.0
@@ -3366,20 +3366,20 @@ packages:
peerDependencies:
react: ^18 || ^19
'@tanstack/react-router-devtools@1.158.1':
resolution: {integrity: sha512-H0iTfsLNkadF/JhJnu/pUxlxOiLjE0866vFqXK/7EYVcyYwx2uWQuGxEkyF7a04oXXrbEImAOoXDRBQcZ9T5Zw==}
'@tanstack/react-router-devtools@1.158.4':
resolution: {integrity: sha512-/EkrrJGTPC7MwLfcYYmZM71ANDMLbwcYvBtDA+48LqHUKal8mpWlaodiWdFFnVQ7ny/unbUxljgdrNV9YZiyFQ==}
engines: {node: '>=12'}
peerDependencies:
'@tanstack/react-router': ^1.158.1
'@tanstack/router-core': ^1.158.1
'@tanstack/react-router': ^1.158.4
'@tanstack/router-core': ^1.158.4
react: '>=18.0.0 || >=19.0.0'
react-dom: '>=18.0.0 || >=19.0.0'
peerDependenciesMeta:
'@tanstack/router-core':
optional: true
'@tanstack/react-router@1.158.1':
resolution: {integrity: sha512-ZRBhs0tJDPeYGVrBhXPkGs+mOKqKKMM4OfvYSNvWIYZGfs8KQcqxPaN8OnUvKsnAGtzwusVWDpBipqVZWJd0lA==}
'@tanstack/react-router@1.158.4':
resolution: {integrity: sha512-i15xXumgvpuM+4NSuIwgouGezuj9eHjZsgpTZSQ7E9pa8rYmhZbWnf8xU68qaLmaKIol/e75o/YzVH2QWHs3iQ==}
engines: {node: '>=12'}
peerDependencies:
react: '>=18.0.0 || >=19.0.0'
@@ -3410,30 +3410,30 @@ packages:
react: ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0
react-dom: ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0
'@tanstack/router-core@1.158.1':
resolution: {integrity: sha512-8B9X3GzN1JWsqa+OTgg2k+LrayLQYmgtv26b96difyrRS32DaDBvEpU3xXDaLNmi/+zoqG1ffAcDT4D6tyC2hw==}
'@tanstack/router-core@1.158.4':
resolution: {integrity: sha512-KikgYdyrEFqsjjgv9pMhDTMmASMAyFRvUiKFdQPQtXq3aD1qv/zck4CbA4bfzp9N9nYu/qvWwU1mlYU4u5JeXg==}
engines: {node: '>=12'}
'@tanstack/router-devtools-core@1.158.1':
resolution: {integrity: sha512-iGCqmIJ5NXMIuyFwJgfikEmRrceT3tmynMTMSuVxFiv9+Dlk1tsp8bsYS+UGhyY4beoASsRnlikAeNAMsCjhwA==}
'@tanstack/router-devtools-core@1.158.4':
resolution: {integrity: sha512-9MKzstYp/6sNRSwJY2b9ipVW8b8/x1iSFNfLhOJur2tnjB3RhwCDfy0u+to70BrRpBEWeq7jvJoVdP029gzUUg==}
engines: {node: '>=12'}
peerDependencies:
'@tanstack/router-core': ^1.158.1
'@tanstack/router-core': ^1.158.4
csstype: ^3.0.10
peerDependenciesMeta:
csstype:
optional: true
'@tanstack/router-generator@1.158.1':
resolution: {integrity: sha512-geBpsIxJNvdjw2kt/Ii/j68hIUvfGnra0HKlGrDZw8/Ny4AJ2nnOcszUlZRbuQyxByk05r4lneOShKy5V5MUCQ==}
'@tanstack/router-generator@1.158.4':
resolution: {integrity: sha512-RQmqMTT0oV8dS/3Glcq9SPzDZqOPyKb/LVFUkNoTfMwW88WyGnQcYqZAkmVk/CGBWWDfwObOUZoGq5jTF7bG8w==}
engines: {node: '>=12'}
'@tanstack/router-plugin@1.158.1':
resolution: {integrity: sha512-IPCnf1CBc0jnczuy65+3iBaoABv5TKhOJ1YLzwel4kb9D8Abcq0vF8ooR5FiPmaGnree/z3SvjgHe5eQtgcsSQ==}
'@tanstack/router-plugin@1.158.4':
resolution: {integrity: sha512-g2sytAhljw6Jd6Klu37OZ75+o+vhiGdbWtnBy/4rYLC4NN6hSnjgJQRI3+h1CI1KQ4EUgsZYZr/hgE1KHoiWYQ==}
engines: {node: '>=12'}
peerDependencies:
'@rsbuild/core': '>=1.0.2'
'@tanstack/react-router': ^1.158.1
'@tanstack/react-router': ^1.158.4
vite: '>=5.0.0 || >=6.0.0 || >=7.0.0'
vite-plugin-solid: ^2.11.10
webpack: '>=5.92.0'
@@ -3791,6 +3791,9 @@ packages:
'@types/semver@7.7.1':
resolution: {integrity: sha512-FmgJfu+MOcQ370SD0ev7EI8TlCAfKYU+B4m5T3yXc1CiRN94g/SZPtsCkk506aUDtlMnFZvasDwHHUcZUEaYuA==}
'@types/trusted-types@2.0.7':
resolution: {integrity: sha512-ScaPdn1dQczgbl0QFTeTOmVHFULt394XJgOQNoyVhZ6r2vLnMLJfBPd53SB52T/3G36VI1/g2MZaX0cwDuXsfw==}
'@types/unist@2.0.10':
resolution: {integrity: sha512-IfYcSBWE3hLpBg8+X2SEa8LVkJdJEkT2Ese2aaLs3ptGdVtABxndrMaxuFlQ1qdFf9Q5rDvDpxI3WwgvKFAsQA==}
@@ -4781,8 +4784,8 @@ packages:
resolution: {integrity: sha512-cgwlv/1iFQiFnU96XXgROh8xTeetsnJiDsTc7TYCLFd9+/WNkIqPTxiM/8pSd8VIrhXGTf1Ny1q1hquVqDJB5w==}
engines: {node: '>= 4'}
dompurify@3.1.7:
resolution: {integrity: sha512-VaTstWtsneJY8xzy7DekmYWEOZcmzIe3Qb3zPd4STve1OBTa+e+WmS1ITQec1fZYXI3HCsOZZiSMpG6oxoWMWQ==}
dompurify@3.2.7:
resolution: {integrity: sha512-WhL/YuveyGXJaerVlMYGWhvQswa7myDG17P7Vu65EWC05o8vfeNbvNf4d/BOvH99+ZW+LlQsc1GDKMa1vNK6dw==}
domutils@2.8.0:
resolution: {integrity: sha512-w96Cjofp72M5IIhpjgobBimYEfoPjx1Vx0BSX9P30WBdZW2WIKU0T1Bd0kz2eNZ9ikjKgHbEyKx8BB6H1L3h3A==}
@@ -5182,8 +5185,8 @@ packages:
hyphenate-style-name@1.1.0:
resolution: {integrity: sha512-WDC/ui2VVRrz3jOVi+XtjqkDjiVjTtFaAGiW37k6b+ohyQ5wYDOGkvCZa8+H0nx3gyvv0+BST9xuOgIyGQ00gw==}
i18next@25.7.3:
resolution: {integrity: sha512-2XaT+HpYGuc2uTExq9TVRhLsso+Dxym6PWaKpn36wfBmTI779OQ7iP/XaZHzrnGyzU4SHpFrTYLKfVyBfAhVNA==}
i18next@25.8.4:
resolution: {integrity: sha512-a9A0MnUjKvzjEN/26ZY1okpra9kA8MEwzYEz1BNm+IyxUKPRH6ihf0p7vj8YvULwZHKHl3zkJ6KOt4hewxBecQ==}
peerDependencies:
typescript: ^5
peerDependenciesMeta:
@@ -5461,8 +5464,8 @@ packages:
resolution: {integrity: sha512-dcS1ul+9tmeD95T+x28/ehLgd9mENa3LsvDTtzm3vyBEO7RPptvAD+t44WVXaUjTBRcrpFeFlC8WCruUR456hw==}
engines: {node: '>=0.10.0'}
knip@5.83.0:
resolution: {integrity: sha512-FfmaHMntpZB13B1oJQMSs1hTOZxd0TOn+FYB3oWEI02XlxTW3RH4H7d8z5Us3g0ziHCYyl7z0B1xi8ENP3QEKA==}
knip@5.83.1:
resolution: {integrity: sha512-av3ZG/Nui6S/BNL8Tmj12yGxYfTnwWnslouW97m40him7o8MwiMjZBY9TPvlEWUci45aVId0/HbgTwSKIDGpMw==}
engines: {node: '>=18.18.0'}
hasBin: true
peerDependencies:
@@ -5819,8 +5822,8 @@ packages:
mlly@1.8.0:
resolution: {integrity: sha512-l8D9ODSRWLe2KHJSifWGwBqpTZXIXTeo8mlKjY+E2HAakaTeNpqAyBZ8GSqLzHgw4XmHmC8whvpjJNMbFZN7/g==}
monaco-editor@0.54.0:
resolution: {integrity: sha512-hx45SEUoLatgWxHKCmlLJH81xBo0uXP4sRkESUpmDQevfi+e7K1VuiSprK6UpQ8u4zOcKNiH0pMvHvlMWA/4cw==}
monaco-editor@0.55.1:
resolution: {integrity: sha512-jz4x+TJNFHwHtwuV9vA9rMujcZRb0CEilTEwG2rRSpe/A7Jdkuj8xPKttCgOh+v/lkHy7HsZ64oj+q3xoAFl9A==}
monaco-languageserver-types@0.4.0:
resolution: {integrity: sha512-QQ3BZiU5LYkJElGncSNb5AKoJ/LCs6YBMCJMAz9EA7v+JaOdn3kx2cXpPTcZfKA5AEsR0vc97sAw+5mdNhVBmw==}
@@ -6958,8 +6961,8 @@ packages:
svg-tags@1.0.0:
resolution: {integrity: sha512-ovssysQTa+luh7A5Weu3Rta6FJlFBBbInjOh722LIt6klpU2/HtdUbszju/G4devcvk8PGt7FCLv5wftu3THUA==}
swr@2.3.8:
resolution: {integrity: sha512-gaCPRVoMq8WGDcWj9p4YWzCMPHzE0WNl6W8ADIx9c3JBEIdMkJGMzW+uzXvxHMltwcYACr9jP+32H8/hgwMR7w==}
swr@2.4.0:
resolution: {integrity: sha512-sUlC20T8EOt1pHmDiqueUWMmRRX03W7w5YxovWX7VR2KHEPCTMly85x05vpkP5i6Bu4h44ePSMD9Tc+G2MItFw==}
peerDependencies:
react: ^16.11.0 || ^17.0.0 || ^18.0.0 || ^19.0.0
@@ -7109,8 +7112,8 @@ packages:
resolution: {integrity: sha512-raqeBD6NQK4SkWhQzeYKd1KmIG6dllBOTt55Rmkt4HtI9mwdWtJljnrXjAFUBLTSN67HWrOIZ3EPF4kjUw80Bg==}
engines: {node: '>=14.0'}
undici@7.20.0:
resolution: {integrity: sha512-MJZrkjyd7DeC+uPZh+5/YaMDxFiiEEaDgbUSVMXayofAkDWF1088CDo+2RPg7B1BuS1qf1vgNE7xqwPxE0DuSQ==}
undici@7.21.0:
resolution: {integrity: sha512-Hn2tCQpoDt1wv23a68Ctc8Cr/BHpUSfaPYrkajTXOS9IKpxVRx/X5m1K2YkbK2ipgZgxXSgsUinl3x+2YdSSfg==}
engines: {node: '>=20.18.1'}
unicode-canonical-property-names-ecmascript@2.0.1:
@@ -7506,6 +7509,9 @@ packages:
zod@4.2.1:
resolution: {integrity: sha512-0wZ1IRqGGhMP76gLqz8EyfBXKk0J2qo2+H3fi4mcUP/KtTocoX08nmIAHl1Z2kJIZbZee8KOpBCSNPRgauucjw==}
zod@4.3.6:
resolution: {integrity: sha512-rftlrkhHZOcjDwkGlnUtZZkvaPHCsDATp4pGpuOOMDaTdDDXF91wuVDJoWoPsKX/3YPQ5fHuF3STjcYyKr+Qhg==}
zwitch@2.0.4:
resolution: {integrity: sha512-bXE4cR/kVZhKZX/RjPEflHaKVhUVl85noU3v6b8apfQEc1x4A+zBxjZ4lN8LqGd6WZ3dl98pY4o717VFmoPp+A==}
@@ -8921,10 +8927,10 @@ snapshots:
dependencies:
state-local: 1.0.7
'@monaco-editor/react@4.7.0(monaco-editor@0.54.0)(react-dom@19.2.4(react@19.2.4))(react@19.2.4)':
'@monaco-editor/react@4.7.0(monaco-editor@0.55.1)(react-dom@19.2.4(react@19.2.4))(react@19.2.4)':
dependencies:
'@monaco-editor/loader': 1.5.0
monaco-editor: 0.54.0
monaco-editor: 0.55.1
react: 19.2.4
react-dom: 19.2.4(react@19.2.4)
@@ -10357,22 +10363,22 @@ snapshots:
'@tanstack/query-core': 5.90.20
react: 19.2.4
'@tanstack/react-router-devtools@1.158.1(@tanstack/react-router@1.158.1(react-dom@19.2.4(react@19.2.4))(react@19.2.4))(@tanstack/router-core@1.158.1)(csstype@3.2.3)(react-dom@19.2.4(react@19.2.4))(react@19.2.4)':
'@tanstack/react-router-devtools@1.158.4(@tanstack/react-router@1.158.4(react-dom@19.2.4(react@19.2.4))(react@19.2.4))(@tanstack/router-core@1.158.4)(csstype@3.2.3)(react-dom@19.2.4(react@19.2.4))(react@19.2.4)':
dependencies:
'@tanstack/react-router': 1.158.1(react-dom@19.2.4(react@19.2.4))(react@19.2.4)
'@tanstack/router-devtools-core': 1.158.1(@tanstack/router-core@1.158.1)(csstype@3.2.3)
'@tanstack/react-router': 1.158.4(react-dom@19.2.4(react@19.2.4))(react@19.2.4)
'@tanstack/router-devtools-core': 1.158.4(@tanstack/router-core@1.158.4)(csstype@3.2.3)
react: 19.2.4
react-dom: 19.2.4(react@19.2.4)
optionalDependencies:
'@tanstack/router-core': 1.158.1
'@tanstack/router-core': 1.158.4
transitivePeerDependencies:
- csstype
'@tanstack/react-router@1.158.1(react-dom@19.2.4(react@19.2.4))(react@19.2.4)':
'@tanstack/react-router@1.158.4(react-dom@19.2.4(react@19.2.4))(react@19.2.4)':
dependencies:
'@tanstack/history': 1.154.14
'@tanstack/react-store': 0.8.0(react-dom@19.2.4(react@19.2.4))(react@19.2.4)
'@tanstack/router-core': 1.158.1
'@tanstack/router-core': 1.158.4
isbot: 5.1.28
react: 19.2.4
react-dom: 19.2.4(react@19.2.4)
@@ -10404,7 +10410,7 @@ snapshots:
react: 19.2.4
react-dom: 19.2.4(react@19.2.4)
'@tanstack/router-core@1.158.1':
'@tanstack/router-core@1.158.4':
dependencies:
'@tanstack/history': 1.154.14
'@tanstack/store': 0.8.0
@@ -10414,18 +10420,18 @@ snapshots:
tiny-invariant: 1.3.3
tiny-warning: 1.0.3
'@tanstack/router-devtools-core@1.158.1(@tanstack/router-core@1.158.1)(csstype@3.2.3)':
'@tanstack/router-devtools-core@1.158.4(@tanstack/router-core@1.158.4)(csstype@3.2.3)':
dependencies:
'@tanstack/router-core': 1.158.1
'@tanstack/router-core': 1.158.4
clsx: 2.1.1
goober: 2.1.16(csstype@3.2.3)
tiny-invariant: 1.3.3
optionalDependencies:
csstype: 3.2.3
'@tanstack/router-generator@1.158.1':
'@tanstack/router-generator@1.158.4':
dependencies:
'@tanstack/router-core': 1.158.1
'@tanstack/router-core': 1.158.4
'@tanstack/router-utils': 1.158.0
'@tanstack/virtual-file-routes': 1.154.7
prettier: 3.8.1
@@ -10436,7 +10442,7 @@ snapshots:
transitivePeerDependencies:
- supports-color
'@tanstack/router-plugin@1.158.1(@tanstack/react-router@1.158.1(react-dom@19.2.4(react@19.2.4))(react@19.2.4))(vite@7.3.1(@types/node@24.10.11)(jiti@2.6.1)(less@4.2.0)(lightningcss@1.30.2)(sass-embedded@1.97.3)(sass@1.97.3)(stylus@0.62.0)(terser@5.36.0)(tsx@4.21.0)(yaml@2.8.1))':
'@tanstack/router-plugin@1.158.4(@tanstack/react-router@1.158.4(react-dom@19.2.4(react@19.2.4))(react@19.2.4))(vite@7.3.1(@types/node@24.10.11)(jiti@2.6.1)(less@4.2.0)(lightningcss@1.30.2)(sass-embedded@1.97.3)(sass@1.97.3)(stylus@0.62.0)(terser@5.36.0)(tsx@4.21.0)(yaml@2.8.1))':
dependencies:
'@babel/core': 7.29.0
'@babel/plugin-syntax-jsx': 7.27.1(@babel/core@7.29.0)
@@ -10444,15 +10450,15 @@ snapshots:
'@babel/template': 7.28.6
'@babel/traverse': 7.29.0
'@babel/types': 7.29.0
'@tanstack/router-core': 1.158.1
'@tanstack/router-generator': 1.158.1
'@tanstack/router-core': 1.158.4
'@tanstack/router-generator': 1.158.4
'@tanstack/router-utils': 1.158.0
'@tanstack/virtual-file-routes': 1.154.7
chokidar: 3.6.0
unplugin: 2.3.11
zod: 3.25.76
optionalDependencies:
'@tanstack/react-router': 1.158.1(react-dom@19.2.4(react@19.2.4))(react@19.2.4)
'@tanstack/react-router': 1.158.4(react-dom@19.2.4(react@19.2.4))(react@19.2.4)
vite: 7.3.1(@types/node@24.10.11)(jiti@2.6.1)(less@4.2.0)(lightningcss@1.30.2)(sass-embedded@1.97.3)(sass@1.97.3)(stylus@0.62.0)(terser@5.36.0)(tsx@4.21.0)(yaml@2.8.1)
transitivePeerDependencies:
- supports-color
@@ -10471,10 +10477,10 @@ snapshots:
transitivePeerDependencies:
- supports-color
'@tanstack/router-zod-adapter@1.81.5(@tanstack/react-router@1.158.1(react-dom@19.2.4(react@19.2.4))(react@19.2.4))(zod@4.2.1)':
'@tanstack/router-zod-adapter@1.81.5(@tanstack/react-router@1.158.4(react-dom@19.2.4(react@19.2.4))(react@19.2.4))(zod@4.3.6)':
dependencies:
'@tanstack/react-router': 1.158.1(react-dom@19.2.4(react@19.2.4))(react@19.2.4)
zod: 4.2.1
'@tanstack/react-router': 1.158.4(react-dom@19.2.4(react@19.2.4))(react@19.2.4)
zod: 4.3.6
'@tanstack/store@0.8.0': {}
@@ -10814,6 +10820,9 @@ snapshots:
'@types/semver@7.7.1': {}
'@types/trusted-types@2.0.7':
optional: true
'@types/unist@2.0.10': {}
'@types/unist@3.0.2': {}
@@ -11858,7 +11867,9 @@ snapshots:
dependencies:
domelementtype: 2.3.0
dompurify@3.1.7: {}
dompurify@3.2.7:
optionalDependencies:
'@types/trusted-types': 2.0.7
domutils@2.8.0:
dependencies:
@@ -12290,7 +12301,7 @@ snapshots:
hyphenate-style-name@1.1.0: {}
i18next@25.7.3(typescript@5.9.3):
i18next@25.8.4(typescript@5.9.3):
dependencies:
'@babel/runtime': 7.28.4
optionalDependencies:
@@ -12492,7 +12503,7 @@ snapshots:
kind-of@6.0.3: {}
knip@5.83.0(@types/node@24.10.11)(typescript@5.9.3):
knip@5.83.1(@types/node@24.10.11)(typescript@5.9.3):
dependencies:
'@nodelib/fs.walk': 1.2.8
'@types/node': 24.10.11
@@ -12967,9 +12978,9 @@ snapshots:
pkg-types: 1.3.1
ufo: 1.6.1
monaco-editor@0.54.0:
monaco-editor@0.55.1:
dependencies:
dompurify: 3.1.7
dompurify: 3.2.7
marked: 14.0.0
monaco-languageserver-types@0.4.0:
@@ -12984,18 +12995,18 @@ snapshots:
monaco-types@0.1.0: {}
monaco-worker-manager@2.0.1(monaco-editor@0.54.0):
monaco-worker-manager@2.0.1(monaco-editor@0.55.1):
dependencies:
monaco-editor: 0.54.0
monaco-editor: 0.55.1
monaco-yaml@5.4.0(monaco-editor@0.54.0):
monaco-yaml@5.4.0(monaco-editor@0.55.1):
dependencies:
jsonc-parser: 3.3.1
monaco-editor: 0.54.0
monaco-editor: 0.55.1
monaco-languageserver-types: 0.4.0
monaco-marker-data-provider: 1.2.3
monaco-types: 0.1.0
monaco-worker-manager: 2.0.1(monaco-editor@0.54.0)
monaco-worker-manager: 2.0.1(monaco-editor@0.55.1)
path-browserify: 1.0.1
prettier: 3.8.1
vscode-languageserver-textdocument: 1.0.12
@@ -13444,11 +13455,11 @@ snapshots:
dependencies:
react: 19.2.4
react-i18next@15.7.4(i18next@25.7.3(typescript@5.9.3))(react-dom@19.2.4(react@19.2.4))(react@19.2.4)(typescript@5.9.3):
react-i18next@15.7.4(i18next@25.8.4(typescript@5.9.3))(react-dom@19.2.4(react@19.2.4))(react@19.2.4)(typescript@5.9.3):
dependencies:
'@babel/runtime': 7.28.3
html-parse-stringify: 3.0.1
i18next: 25.7.3(typescript@5.9.3)
i18next: 25.8.4(typescript@5.9.3)
react: 19.2.4
optionalDependencies:
react-dom: 19.2.4(react@19.2.4)
@@ -14125,7 +14136,7 @@ snapshots:
svg-tags@1.0.0: {}
swr@2.3.8(react@19.2.4):
swr@2.4.0(react@19.2.4):
dependencies:
dequal: 2.0.3
react: 19.2.4
@@ -14289,7 +14300,7 @@ snapshots:
dependencies:
'@fastify/busboy': 2.1.1
undici@7.20.0: {}
undici@7.21.0: {}
unicode-canonical-property-names-ecmascript@2.0.1: {}
@@ -14682,4 +14693,6 @@ snapshots:
zod@4.2.1: {}
zod@4.3.6: {}
zwitch@2.0.4: {}
+2 -2
View File
@@ -10,7 +10,7 @@
"filesize": "11.0.13",
"p-retry": "7.1.1",
"semver": "7.7.4",
"zod": "4.2.1"
"zod": "4.3.6"
},
"devDependencies": {
"@octokit/types": "16.0.0",
@@ -24,7 +24,7 @@
"picocolors": "1.1.1",
"tar": "7.5.7",
"telegram": "2.26.22",
"undici": "7.20.0",
"undici": "7.21.0",
"yargs": "18.0.0"
}
}
@@ -785,6 +785,6 @@
lockdep_is_held(&nf_ct_ecache_mutex));
- WARN_ON_ONCE(notify);
+ /* WARN_ON_ONCE(notify); */
rcu_assign_pointer(net->ct.nf_conntrack_event_cb, new);
mutex_unlock(&nf_ct_ecache_mutex);
}
if (notify != NULL) {
ret = -EBUSY;
goto out_unlock;
+2 -1
View File
@@ -27,6 +27,7 @@ import (
"github.com/enfein/mieru/v3/pkg/common"
"github.com/enfein/mieru/v3/pkg/protocol"
"github.com/enfein/mieru/v3/pkg/stderror"
"github.com/enfein/mieru/v3/pkg/trafficpattern"
)
// ValidateClientConfigSingleProfile validates a single client config profile.
@@ -79,7 +80,7 @@ func ValidateClientConfigSingleProfile(profile *pb.ClientProfile) error {
if profile.GetMtu() != 0 && (profile.GetMtu() < 1280 || profile.GetMtu() > 1500) {
return fmt.Errorf("MTU value %d is out of range, valid range is [1280, 1500]", profile.GetMtu())
}
if err := ValidateTrafficPattern(profile.GetTrafficPattern()); err != nil {
if err := trafficpattern.Validate(profile.GetTrafficPattern()); err != nil {
return fmt.Errorf("invalid traffic pattern: %w", err)
}
return nil
@@ -1,88 +0,0 @@
// Copyright (C) 2026 mieru authors
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <https://www.gnu.org/licenses/>.
package appctlcommon
import (
"encoding/hex"
"fmt"
pb "github.com/enfein/mieru/v3/pkg/appctl/appctlpb"
)
func ValidateTrafficPattern(pattern *pb.TrafficPattern) error {
if pattern == nil {
return nil
}
if err := validateTCPFragment(pattern.GetTcpFragment()); err != nil {
return err
}
if err := validateNoncePattern(pattern.GetNonce()); err != nil {
return err
}
return nil
}
func validateTCPFragment(fragment *pb.TCPFragment) error {
if fragment == nil {
return nil
}
if fragment.MaxSleepMs != nil {
if fragment.GetMaxSleepMs() < 0 {
return fmt.Errorf("TCPFragment maxSleepMs %d is negative", fragment.GetMaxSleepMs())
}
if fragment.GetMaxSleepMs() > 100 {
return fmt.Errorf("TCPFragment maxSleepMs %d exceeds maximum value 100", fragment.GetMaxSleepMs())
}
}
return nil
}
func validateNoncePattern(nonce *pb.NoncePattern) error {
if nonce == nil {
return nil
}
if nonce.MinLen != nil {
if nonce.GetMinLen() < 0 {
return fmt.Errorf("NoncePattern minLen %d is negative", nonce.GetMinLen())
}
if nonce.GetMinLen() > 12 {
return fmt.Errorf("NoncePattern minLen %d exceeds maximum value 12", nonce.GetMinLen())
}
}
if nonce.MaxLen != nil {
if nonce.GetMaxLen() < 0 {
return fmt.Errorf("NoncePattern maxLen %d is negative", nonce.GetMaxLen())
}
if nonce.GetMaxLen() > 12 {
return fmt.Errorf("NoncePattern maxLen %d exceeds maximum value 12", nonce.GetMaxLen())
}
}
if nonce.MinLen != nil && nonce.MaxLen != nil {
if nonce.GetMinLen() > nonce.GetMaxLen() {
return fmt.Errorf("NoncePattern minLen %d is greater than maxLen %d", nonce.GetMinLen(), nonce.GetMaxLen())
}
}
for i, hexStr := range nonce.GetCustomHexStrings() {
decoded, err := hex.DecodeString(hexStr)
if err != nil {
return fmt.Errorf("NoncePattern customHexStrings[%d] %q is not a valid hex string: %w", i, hexStr, err)
}
if len(decoded) > 12 {
return fmt.Errorf("NoncePattern customHexStrings[%d] decoded length %d exceeds maximum 12 bytes", i, len(decoded))
}
}
return nil
}
@@ -1,213 +0,0 @@
// Copyright (C) 2026 mieru authors
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <https://www.gnu.org/licenses/>.
package appctlcommon
import (
"strings"
"testing"
pb "github.com/enfein/mieru/v3/pkg/appctl/appctlpb"
"google.golang.org/protobuf/proto"
)
func TestValidateTrafficPattern(t *testing.T) {
cases := []struct {
name string
pattern *pb.TrafficPattern
wantErrString string
}{
{
name: "nil_pattern",
pattern: nil,
wantErrString: "",
},
{
name: "empty_pattern",
pattern: &pb.TrafficPattern{},
wantErrString: "",
},
{
name: "valid_tcp_fragment",
pattern: &pb.TrafficPattern{
TcpFragment: &pb.TCPFragment{
Enable: proto.Bool(true),
MaxSleepMs: proto.Int32(100),
},
},
wantErrString: "",
},
{
name: "tcp_fragment_max_sleep_exceeds_100",
pattern: &pb.TrafficPattern{
TcpFragment: &pb.TCPFragment{
Enable: proto.Bool(true),
MaxSleepMs: proto.Int32(101),
},
},
wantErrString: "exceeds maximum value 100",
},
{
name: "tcp_fragment_max_sleep_negative",
pattern: &pb.TrafficPattern{
TcpFragment: &pb.TCPFragment{
MaxSleepMs: proto.Int32(-1),
},
},
wantErrString: "is negative",
},
{
name: "valid_nonce_pattern",
pattern: &pb.TrafficPattern{
Nonce: &pb.NoncePattern{
MinLen: proto.Int32(12),
MaxLen: proto.Int32(12),
},
},
wantErrString: "",
},
{
name: "nonce_max_len_exceeds_12",
pattern: &pb.TrafficPattern{
Nonce: &pb.NoncePattern{
MaxLen: proto.Int32(13),
},
},
wantErrString: "maxLen 13 exceeds maximum value 12",
},
{
name: "nonce_min_len_exceeds_12",
pattern: &pb.TrafficPattern{
Nonce: &pb.NoncePattern{
MinLen: proto.Int32(13),
},
},
wantErrString: "minLen 13 exceeds maximum value 12",
},
{
name: "nonce_max_len_negative",
pattern: &pb.TrafficPattern{
Nonce: &pb.NoncePattern{
MaxLen: proto.Int32(-1),
},
},
wantErrString: "maxLen -1 is negative",
},
{
name: "nonce_min_len_negative",
pattern: &pb.TrafficPattern{
Nonce: &pb.NoncePattern{
MinLen: proto.Int32(-1),
},
},
wantErrString: "minLen -1 is negative",
},
{
name: "nonce_min_len_greater_than_max_len",
pattern: &pb.TrafficPattern{
Nonce: &pb.NoncePattern{
MinLen: proto.Int32(8),
MaxLen: proto.Int32(4),
},
},
wantErrString: "minLen 8 is greater than maxLen 4",
},
{
name: "valid_custom_hex_strings",
pattern: &pb.TrafficPattern{
Nonce: &pb.NoncePattern{
Type: pb.NonceType_NONCE_TYPE_FIXED.Enum(),
CustomHexStrings: []string{"00010203", "aabbccdd"},
},
},
wantErrString: "",
},
{
name: "valid_custom_hex_strings_max_length",
pattern: &pb.TrafficPattern{
Nonce: &pb.NoncePattern{
Type: pb.NonceType_NONCE_TYPE_FIXED.Enum(),
CustomHexStrings: []string{"000102030405060708090a0b"},
},
},
wantErrString: "",
},
{
name: "custom_hex_string_exceeds_12_bytes",
pattern: &pb.TrafficPattern{
Nonce: &pb.NoncePattern{
Type: pb.NonceType_NONCE_TYPE_FIXED.Enum(),
CustomHexStrings: []string{"000102030405060708090a0b0c"},
},
},
wantErrString: "exceeds maximum 12 bytes",
},
{
name: "custom_hex_string_invalid",
pattern: &pb.TrafficPattern{
Nonce: &pb.NoncePattern{
Type: pb.NonceType_NONCE_TYPE_FIXED.Enum(),
CustomHexStrings: []string{"not_valid_hex"},
},
},
wantErrString: "is not a valid hex string",
},
{
name: "custom_hex_string_odd_length",
pattern: &pb.TrafficPattern{
Nonce: &pb.NoncePattern{
Type: pb.NonceType_NONCE_TYPE_FIXED.Enum(),
CustomHexStrings: []string{"abc"},
},
},
wantErrString: "is not a valid hex string",
},
{
name: "valid_full_pattern",
pattern: &pb.TrafficPattern{
Seed: proto.Int32(12345),
TcpFragment: &pb.TCPFragment{
Enable: proto.Bool(true),
MaxSleepMs: proto.Int32(50),
},
Nonce: &pb.NoncePattern{
Type: pb.NonceType_NONCE_TYPE_FIXED.Enum(),
ApplyToAllUDPPacket: proto.Bool(true),
MinLen: proto.Int32(4),
MaxLen: proto.Int32(8),
CustomHexStrings: []string{"00010203"},
},
},
wantErrString: "",
},
}
for _, c := range cases {
t.Run(c.name, func(t *testing.T) {
err := ValidateTrafficPattern(c.pattern)
if c.wantErrString == "" {
if err != nil {
t.Errorf("ValidateTrafficPattern() returned unexpected error: %v", err)
}
} else {
if err == nil {
t.Errorf("ValidateTrafficPattern() expected error to contain %q, got nil", c.wantErrString)
} else if !strings.Contains(err.Error(), c.wantErrString) {
t.Errorf("ValidateTrafficPattern() error = %q, want error to contain %q", err.Error(), c.wantErrString)
}
}
})
}
}
+2 -1
View File
@@ -36,6 +36,7 @@ import (
"github.com/enfein/mieru/v3/pkg/protocol"
"github.com/enfein/mieru/v3/pkg/socks5"
"github.com/enfein/mieru/v3/pkg/stderror"
"github.com/enfein/mieru/v3/pkg/trafficpattern"
"github.com/enfein/mieru/v3/pkg/version"
"google.golang.org/grpc"
"google.golang.org/grpc/credentials/insecure"
@@ -656,7 +657,7 @@ func ValidateServerConfigPatch(patch *pb.ServerConfig) error {
return fmt.Errorf("metrics logging interval %q is less than 1 second", patch.GetAdvancedSettings().GetMetricsLoggingInterval())
}
}
if err := appctlcommon.ValidateTrafficPattern(patch.GetTrafficPattern()); err != nil {
if err := trafficpattern.Validate(patch.GetTrafficPattern()); err != nil {
return fmt.Errorf("invalid traffic pattern: %w", err)
}
return nil
+117 -15
View File
@@ -16,6 +16,8 @@
package trafficpattern
import (
"encoding/base64"
"encoding/hex"
"fmt"
"math"
@@ -26,30 +28,78 @@ import (
// Config stores the traffic pattern configuration.
type Config struct {
origin *appctlpb.TrafficPattern
original *appctlpb.TrafficPattern
effective *appctlpb.TrafficPattern
}
// NewConfig creates a new traffic pattern configuration from a protobuf message.
// Assume the origin protobuf message is valid.
func NewConfig(origin *appctlpb.TrafficPattern) *Config {
if origin == nil {
// It assumes the original protobuf message is valid.
func NewConfig(original *appctlpb.TrafficPattern) *Config {
if original == nil {
panic("TrafficPattern is nil")
}
c := &Config{
origin: origin,
effective: proto.Clone(origin).(*appctlpb.TrafficPattern),
original: original,
effective: proto.Clone(original).(*appctlpb.TrafficPattern),
}
c.generateImplicitTrafficPattern()
return c
}
// Original returns the original traffic pattern.
func (c *Config) Original() *appctlpb.TrafficPattern {
return c.original
}
// Effective returns the effective traffic pattern.
func (c *Config) Effective() *appctlpb.TrafficPattern {
return c.effective
}
// Encode returns the base64 encoded string of the traffic pattern
// from the protobuf message binary.
func Encode(pattern *appctlpb.TrafficPattern) string {
b, err := proto.Marshal(pattern)
if err != nil {
return ""
}
return base64.StdEncoding.EncodeToString(b)
}
// Decode decodes the base64 encoded string of the traffic pattern
// into the protobuf message.
func Decode(encoded string) (*appctlpb.TrafficPattern, error) {
b, err := base64.StdEncoding.DecodeString(encoded)
if err != nil {
return nil, fmt.Errorf("decode base64 string failed: %w", err)
}
pattern := &appctlpb.TrafficPattern{}
if err := proto.Unmarshal(b, pattern); err != nil {
return nil, fmt.Errorf("proto.Unmarshal() failed: %w", err)
}
return pattern, nil
}
// Validate validates the traffic pattern protobuf message.
func Validate(pattern *appctlpb.TrafficPattern) error {
if pattern == nil {
return nil
}
if err := validateTCPFragment(pattern.GetTcpFragment()); err != nil {
return err
}
if err := validateNoncePattern(pattern.GetNonce()); err != nil {
return err
}
return nil
}
func (c *Config) generateImplicitTrafficPattern() {
seed := int(c.origin.GetSeed())
if c.origin.Seed == nil {
seed := int(c.original.GetSeed())
if c.original.Seed == nil {
seed = rng.FixedIntVH(math.MaxInt32)
}
unlockAll := c.origin.GetUnlockAll()
unlockAll := c.original.GetUnlockAll()
c.generateTCPFragment(seed, unlockAll)
c.generateNoncePattern(seed, unlockAll)
}
@@ -60,7 +110,7 @@ func (c *Config) generateTCPFragment(seed int, unlockAll bool) {
}
f := c.effective.TcpFragment
if c.origin.TcpFragment == nil || c.origin.TcpFragment.Enable == nil {
if c.original.TcpFragment == nil || c.original.TcpFragment.Enable == nil {
if unlockAll {
f.Enable = proto.Bool(rng.FixedInt(2, fmt.Sprintf("%d:tcpFragment.enable", seed)) == 1)
} else {
@@ -68,7 +118,7 @@ func (c *Config) generateTCPFragment(seed int, unlockAll bool) {
}
}
if c.origin.TcpFragment == nil || c.origin.TcpFragment.MaxSleepMs == nil {
if c.original.TcpFragment == nil || c.original.TcpFragment.MaxSleepMs == nil {
maxRange := 100
// Generate a random number in [1, 100]
f.MaxSleepMs = proto.Int32(int32(rng.FixedInt(maxRange, fmt.Sprintf("%d:tcpFragment.maxSleepMs", seed))) + 1)
@@ -81,7 +131,7 @@ func (c *Config) generateNoncePattern(seed int, unlockAll bool) {
}
n := c.effective.Nonce
if c.origin.Nonce == nil || c.origin.Nonce.Type == nil {
if c.original.Nonce == nil || c.original.Nonce.Type == nil {
// Never generate NONCE_TYPE_FIXED (3) since it requires customHexStrings.
if unlockAll {
// Generate a random number in [0, 2]
@@ -94,7 +144,7 @@ func (c *Config) generateNoncePattern(seed int, unlockAll bool) {
}
}
if c.origin.Nonce == nil || c.origin.Nonce.ApplyToAllUDPPacket == nil {
if c.original.Nonce == nil || c.original.Nonce.ApplyToAllUDPPacket == nil {
if unlockAll {
n.ApplyToAllUDPPacket = proto.Bool(rng.FixedInt(2, fmt.Sprintf("%d:nonce.applyToAllUDPPacket", seed)) == 1)
} else {
@@ -102,7 +152,7 @@ func (c *Config) generateNoncePattern(seed int, unlockAll bool) {
}
}
if c.origin.Nonce == nil || c.origin.Nonce.MinLen == nil {
if c.original.Nonce == nil || c.original.Nonce.MinLen == nil {
if unlockAll {
// Generate a random number in [0, 12]
minRange := 13
@@ -114,8 +164,60 @@ func (c *Config) generateNoncePattern(seed int, unlockAll bool) {
}
}
if c.origin.Nonce == nil || c.origin.Nonce.MaxLen == nil {
if c.original.Nonce == nil || c.original.Nonce.MaxLen == nil {
minLen := int(n.GetMinLen())
n.MaxLen = proto.Int32(int32(minLen + rng.FixedInt(13-minLen, fmt.Sprintf("%d:nonce.maxLen", seed))))
}
}
func validateTCPFragment(fragment *appctlpb.TCPFragment) error {
if fragment == nil {
return nil
}
if fragment.MaxSleepMs != nil {
if fragment.GetMaxSleepMs() < 0 {
return fmt.Errorf("TCPFragment maxSleepMs %d is negative", fragment.GetMaxSleepMs())
}
if fragment.GetMaxSleepMs() > 100 {
return fmt.Errorf("TCPFragment maxSleepMs %d exceeds maximum value 100", fragment.GetMaxSleepMs())
}
}
return nil
}
func validateNoncePattern(nonce *appctlpb.NoncePattern) error {
if nonce == nil {
return nil
}
if nonce.MinLen != nil {
if nonce.GetMinLen() < 0 {
return fmt.Errorf("NoncePattern minLen %d is negative", nonce.GetMinLen())
}
if nonce.GetMinLen() > 12 {
return fmt.Errorf("NoncePattern minLen %d exceeds maximum value 12", nonce.GetMinLen())
}
}
if nonce.MaxLen != nil {
if nonce.GetMaxLen() < 0 {
return fmt.Errorf("NoncePattern maxLen %d is negative", nonce.GetMaxLen())
}
if nonce.GetMaxLen() > 12 {
return fmt.Errorf("NoncePattern maxLen %d exceeds maximum value 12", nonce.GetMaxLen())
}
}
if nonce.MinLen != nil && nonce.MaxLen != nil {
if nonce.GetMinLen() > nonce.GetMaxLen() {
return fmt.Errorf("NoncePattern minLen %d is greater than maxLen %d", nonce.GetMinLen(), nonce.GetMaxLen())
}
}
for i, hexStr := range nonce.GetCustomHexStrings() {
decoded, err := hex.DecodeString(hexStr)
if err != nil {
return fmt.Errorf("NoncePattern customHexStrings[%d] %q is not a valid hex string: %w", i, hexStr, err)
}
if len(decoded) > 12 {
return fmt.Errorf("NoncePattern customHexStrings[%d] decoded length %d exceeds maximum 12 bytes", i, len(decoded))
}
}
return nil
}
+244
View File
@@ -17,6 +17,7 @@ package trafficpattern
import (
mrand "math/rand"
"strings"
"testing"
"github.com/enfein/mieru/v3/pkg/appctl/appctlpb"
@@ -209,3 +210,246 @@ func TestMinLenMaxLen(t *testing.T) {
}
}
}
func TestEncodeDecode(t *testing.T) {
origin := &appctlpb.TrafficPattern{
UnlockAll: proto.Bool(true),
TcpFragment: &appctlpb.TCPFragment{
Enable: proto.Bool(true),
MaxSleepMs: proto.Int32(50),
},
Nonce: &appctlpb.NoncePattern{
Type: appctlpb.NonceType_NONCE_TYPE_PRINTABLE.Enum(),
ApplyToAllUDPPacket: proto.Bool(true),
MinLen: proto.Int32(6),
MaxLen: proto.Int32(8),
},
}
encoded := Encode(origin)
if encoded == "" {
t.Fatal("Encode() returned empty string")
}
t.Logf("Encoded traffic pattern: %s", encoded)
restored, err := Decode(encoded)
if err != nil {
t.Fatalf("Decode() failed: %v", err)
}
// Verify restored values match effective config
if restored.GetSeed() != origin.GetSeed() {
t.Errorf("seed mismatch: expected %d, got %d", origin.GetSeed(), restored.GetSeed())
}
if restored.GetUnlockAll() != origin.GetUnlockAll() {
t.Errorf("unlockAll mismatch: expected %v, got %v", origin.GetUnlockAll(), restored.GetUnlockAll())
}
if restored.TcpFragment.GetEnable() != origin.TcpFragment.GetEnable() {
t.Errorf("tcpFragment.enable mismatch: expected %v, got %v", origin.TcpFragment.GetEnable(), restored.TcpFragment.GetEnable())
}
if restored.TcpFragment.GetMaxSleepMs() != origin.TcpFragment.GetMaxSleepMs() {
t.Errorf("tcpFragment.maxSleepMs mismatch: expected %d, got %d", origin.TcpFragment.GetMaxSleepMs(), restored.TcpFragment.GetMaxSleepMs())
}
if restored.Nonce.GetType() != origin.Nonce.GetType() {
t.Errorf("nonce.type mismatch: expected %v, got %v", origin.Nonce.GetType(), restored.Nonce.GetType())
}
if restored.Nonce.GetApplyToAllUDPPacket() != origin.Nonce.GetApplyToAllUDPPacket() {
t.Errorf("nonce.applyToAllUDPPacket mismatch: expected %v, got %v", origin.Nonce.GetApplyToAllUDPPacket(), restored.Nonce.GetApplyToAllUDPPacket())
}
if restored.Nonce.GetMinLen() != origin.Nonce.GetMinLen() {
t.Errorf("nonce.minLen mismatch: expected %d, got %d", origin.Nonce.GetMinLen(), restored.Nonce.GetMinLen())
}
if restored.Nonce.GetMaxLen() != origin.Nonce.GetMaxLen() {
t.Errorf("nonce.maxLen mismatch: expected %d, got %d", origin.Nonce.GetMaxLen(), restored.Nonce.GetMaxLen())
}
}
func TestValidateTrafficPattern(t *testing.T) {
cases := []struct {
name string
pattern *appctlpb.TrafficPattern
wantErrString string
}{
{
name: "nil_pattern",
pattern: nil,
wantErrString: "",
},
{
name: "empty_pattern",
pattern: &appctlpb.TrafficPattern{},
wantErrString: "",
},
{
name: "valid_tcp_fragment",
pattern: &appctlpb.TrafficPattern{
TcpFragment: &appctlpb.TCPFragment{
Enable: proto.Bool(true),
MaxSleepMs: proto.Int32(100),
},
},
wantErrString: "",
},
{
name: "tcp_fragment_max_sleep_exceeds_100",
pattern: &appctlpb.TrafficPattern{
TcpFragment: &appctlpb.TCPFragment{
Enable: proto.Bool(true),
MaxSleepMs: proto.Int32(101),
},
},
wantErrString: "exceeds maximum value 100",
},
{
name: "tcp_fragment_max_sleep_negative",
pattern: &appctlpb.TrafficPattern{
TcpFragment: &appctlpb.TCPFragment{
MaxSleepMs: proto.Int32(-1),
},
},
wantErrString: "is negative",
},
{
name: "valid_nonce_pattern",
pattern: &appctlpb.TrafficPattern{
Nonce: &appctlpb.NoncePattern{
MinLen: proto.Int32(12),
MaxLen: proto.Int32(12),
},
},
wantErrString: "",
},
{
name: "nonce_max_len_exceeds_12",
pattern: &appctlpb.TrafficPattern{
Nonce: &appctlpb.NoncePattern{
MaxLen: proto.Int32(13),
},
},
wantErrString: "maxLen 13 exceeds maximum value 12",
},
{
name: "nonce_min_len_exceeds_12",
pattern: &appctlpb.TrafficPattern{
Nonce: &appctlpb.NoncePattern{
MinLen: proto.Int32(13),
},
},
wantErrString: "minLen 13 exceeds maximum value 12",
},
{
name: "nonce_max_len_negative",
pattern: &appctlpb.TrafficPattern{
Nonce: &appctlpb.NoncePattern{
MaxLen: proto.Int32(-1),
},
},
wantErrString: "maxLen -1 is negative",
},
{
name: "nonce_min_len_negative",
pattern: &appctlpb.TrafficPattern{
Nonce: &appctlpb.NoncePattern{
MinLen: proto.Int32(-1),
},
},
wantErrString: "minLen -1 is negative",
},
{
name: "nonce_min_len_greater_than_max_len",
pattern: &appctlpb.TrafficPattern{
Nonce: &appctlpb.NoncePattern{
MinLen: proto.Int32(8),
MaxLen: proto.Int32(4),
},
},
wantErrString: "minLen 8 is greater than maxLen 4",
},
{
name: "valid_custom_hex_strings",
pattern: &appctlpb.TrafficPattern{
Nonce: &appctlpb.NoncePattern{
Type: appctlpb.NonceType_NONCE_TYPE_FIXED.Enum(),
CustomHexStrings: []string{"00010203", "aabbccdd"},
},
},
wantErrString: "",
},
{
name: "valid_custom_hex_strings_max_length",
pattern: &appctlpb.TrafficPattern{
Nonce: &appctlpb.NoncePattern{
Type: appctlpb.NonceType_NONCE_TYPE_FIXED.Enum(),
CustomHexStrings: []string{"000102030405060708090a0b"},
},
},
wantErrString: "",
},
{
name: "custom_hex_string_exceeds_12_bytes",
pattern: &appctlpb.TrafficPattern{
Nonce: &appctlpb.NoncePattern{
Type: appctlpb.NonceType_NONCE_TYPE_FIXED.Enum(),
CustomHexStrings: []string{"000102030405060708090a0b0c"},
},
},
wantErrString: "exceeds maximum 12 bytes",
},
{
name: "custom_hex_string_invalid",
pattern: &appctlpb.TrafficPattern{
Nonce: &appctlpb.NoncePattern{
Type: appctlpb.NonceType_NONCE_TYPE_FIXED.Enum(),
CustomHexStrings: []string{"not_valid_hex"},
},
},
wantErrString: "is not a valid hex string",
},
{
name: "custom_hex_string_odd_length",
pattern: &appctlpb.TrafficPattern{
Nonce: &appctlpb.NoncePattern{
Type: appctlpb.NonceType_NONCE_TYPE_FIXED.Enum(),
CustomHexStrings: []string{"abc"},
},
},
wantErrString: "is not a valid hex string",
},
{
name: "valid_full_pattern",
pattern: &appctlpb.TrafficPattern{
Seed: proto.Int32(12345),
TcpFragment: &appctlpb.TCPFragment{
Enable: proto.Bool(true),
MaxSleepMs: proto.Int32(50),
},
Nonce: &appctlpb.NoncePattern{
Type: appctlpb.NonceType_NONCE_TYPE_FIXED.Enum(),
ApplyToAllUDPPacket: proto.Bool(true),
MinLen: proto.Int32(4),
MaxLen: proto.Int32(8),
CustomHexStrings: []string{"00010203"},
},
},
wantErrString: "",
},
}
for _, c := range cases {
t.Run(c.name, func(t *testing.T) {
err := Validate(c.pattern)
if c.wantErrString == "" {
if err != nil {
t.Errorf("Validate() returned unexpected error: %v", err)
}
} else {
if err == nil {
t.Errorf("Validate() expected error to contain %q, got nil", c.wantErrString)
} else if !strings.Contains(err.Error(), c.wantErrString) {
t.Errorf("Validate() error = %q, want error to contain %q", err.Error(), c.wantErrString)
}
}
})
}
}
+1 -1
View File
@@ -178,7 +178,7 @@ func NewWireGuard(option WireGuardOption) (*WireGuard, error) {
},
}
outbound.dialer = option.NewDialer(outbound.DialOptions())
singDialer := proxydialer.NewSlowDownSingDialer(proxydialer.NewSingDialer(outbound.dialer), slowdown.New())
singDialer := proxydialer.NewSingDialer(proxydialer.NewSlowDownDialer(outbound.dialer, slowdown.New()))
var reserved [3]uint8
if len(option.Reserved) > 0 {
+10
View File
@@ -58,6 +58,11 @@ func Main(args []string) {
fmt.Println("Seed: " + seedBase64)
fmt.Println("Client: " + clientBase64)
fmt.Println("Hash32: " + hash32Base64)
fmt.Println("-----------------------")
fmt.Println(" Lazy-Config ")
fmt.Println("-----------------------")
fmt.Printf("[Server] decryption: \"mlkem768x25519plus.native.600s.%s\"\n", seedBase64)
fmt.Printf("[Client] encryption: \"mlkem768x25519plus.native.0rtt.%s\"\n", clientBase64)
case "vless-x25519":
var privateKey string
if len(args) > 1 {
@@ -70,6 +75,11 @@ func Main(args []string) {
fmt.Println("PrivateKey: " + privateKeyBase64)
fmt.Println("Password: " + passwordBase64)
fmt.Println("Hash32: " + hash32Base64)
fmt.Println("-----------------------")
fmt.Println(" Lazy-Config ")
fmt.Println("-----------------------")
fmt.Printf("[Server] decryption: \"mlkem768x25519plus.native.600s.%s\"\n", privateKeyBase64)
fmt.Printf("[Client] encryption: \"mlkem768x25519plus.native.0rtt.%s\"\n", passwordBase64)
case "sudoku-keypair":
privateKey, publicKey, err := sudoku.GenKeyPair()
if err != nil {
+18 -21
View File
@@ -8,7 +8,6 @@ import (
"strings"
_ "unsafe"
"github.com/metacubex/mihomo/common/utils"
"github.com/metacubex/mihomo/component/resolver/hosts"
"github.com/metacubex/mihomo/component/trie"
"github.com/metacubex/randv2"
@@ -66,37 +65,35 @@ type HostValue struct {
Domain string
}
func NewHostValue(value any) (HostValue, error) {
func NewHostValue(value []string) (HostValue, error) {
isDomain := true
ips := make([]netip.Addr, 0)
ips := make([]netip.Addr, 0, len(value))
domain := ""
if valueArr, err := utils.ToStringSlice(value); err != nil {
return HostValue{}, err
} else {
if len(valueArr) > 1 {
switch len(value) {
case 0:
return HostValue{}, errors.New("value is empty")
case 1:
host := value[0]
if ip, err := netip.ParseAddr(host); err == nil {
ips = append(ips, ip.Unmap())
isDomain = false
for _, str := range valueArr {
if ip, err := netip.ParseAddr(str); err == nil {
ips = append(ips, ip.Unmap())
} else {
return HostValue{}, err
}
}
} else if len(valueArr) == 1 {
host := valueArr[0]
if ip, err := netip.ParseAddr(host); err == nil {
} else {
domain = host
}
default: // > 1
isDomain = false
for _, str := range value {
if ip, err := netip.ParseAddr(str); err == nil {
ips = append(ips, ip.Unmap())
isDomain = false
} else {
domain = host
return HostValue{}, err
}
}
}
if isDomain {
return NewHostValueByDomain(domain)
} else {
return NewHostValueByIPs(ips)
}
return NewHostValueByIPs(ips)
}
func NewHostValueByIPs(ips []netip.Addr) (HostValue, error) {
+8 -7
View File
@@ -1115,22 +1115,23 @@ func parseHosts(cfg *RawConfig) (*trie.DomainTrie[resolver.HostValue], error) {
if len(cfg.Hosts) != 0 {
for domain, anyValue := range cfg.Hosts {
if str, ok := anyValue.(string); ok && str == "lan" {
hosts, err := utils.ToStringSlice(anyValue)
if err != nil {
return nil, err
}
if len(hosts) == 1 && hosts[0] == "lan" {
if addrs, err := net.InterfaceAddrs(); err != nil {
log.Errorln("insert lan to host error: %s", err)
} else {
ips := make([]netip.Addr, 0)
hosts = make([]string, 0, len(addrs))
for _, addr := range addrs {
if ipnet, ok := addr.(*net.IPNet); ok && !ipnet.IP.IsLoopback() && !ipnet.IP.IsLinkLocalUnicast() {
if ip, err := netip.ParseAddr(ipnet.IP.String()); err == nil {
ips = append(ips, ip)
}
hosts = append(hosts, ipnet.IP.String())
}
}
anyValue = ips
}
}
value, err := resolver.NewHostValue(anyValue)
value, err := resolver.NewHostValue(hosts)
if err != nil {
return nil, fmt.Errorf("%s is not a valid value", anyValue)
}
+1 -1
View File
@@ -38,7 +38,7 @@ require (
github.com/metacubex/sing-wireguard v0.0.0-20250503063753-2dc62acc626f
github.com/metacubex/smux v0.0.0-20260105030934-d0c8756d3141
github.com/metacubex/tfo-go v0.0.0-20251130171125-413e892ac443
github.com/metacubex/tls v0.1.3
github.com/metacubex/tls v0.1.4
github.com/metacubex/utls v1.8.4
github.com/metacubex/wireguard-go v0.0.0-20250820062549-a6cecdd7f57f
github.com/mroth/weightedrand/v2 v2.1.0
+2 -2
View File
@@ -141,8 +141,8 @@ github.com/metacubex/smux v0.0.0-20260105030934-d0c8756d3141 h1:DK2l6m2Fc85H2Bhi
github.com/metacubex/smux v0.0.0-20260105030934-d0c8756d3141/go.mod h1:/yI4OiGOSn0SURhZdJF3CbtPg3nwK700bG8TZLMBvAg=
github.com/metacubex/tfo-go v0.0.0-20251130171125-413e892ac443 h1:H6TnfM12tOoTizYE/qBHH3nEuibIelmHI+BVSxVJr8o=
github.com/metacubex/tfo-go v0.0.0-20251130171125-413e892ac443/go.mod h1:l9oLnLoEXyGZ5RVLsh7QCC5XsouTUyKk4F2nLm2DHLw=
github.com/metacubex/tls v0.1.3 h1:nyjA8GNYGaLVSNRSqWoNNdXSCCFiQABTyrPJmkuSL20=
github.com/metacubex/tls v0.1.3/go.mod h1:0XeVdL0cBw+8i5Hqy3lVeP9IyD/LFTq02ExvHM6rzEM=
github.com/metacubex/tls v0.1.4 h1:Gm5GrkyMUh52gYOMIAQ1kHIym4v4M3Qb87Wsmd8Kpdc=
github.com/metacubex/tls v0.1.4/go.mod h1:0XeVdL0cBw+8i5Hqy3lVeP9IyD/LFTq02ExvHM6rzEM=
github.com/metacubex/utls v1.8.4 h1:HmL9nUApDdWSkgUyodfwF6hSjtiwCGGdyhaSpEejKpg=
github.com/metacubex/utls v1.8.4/go.mod h1:kncGGVhFaoGn5M3pFe3SXhZCzsbCJayNOH4UEqTKTko=
github.com/metacubex/wireguard-go v0.0.0-20250820062549-a6cecdd7f57f h1:FGBPRb1zUabhPhDrlKEjQ9lgIwQ6cHL4x8M9lrERhbk=
+1 -2
View File
@@ -1,7 +1,7 @@
include $(TOPDIR)/rules.mk
PKG_NAME:=luci-app-openclash
PKG_VERSION:=0.47.052
PKG_VERSION:=0.47.055
PKG_MAINTAINER:=vernesong <https://github.com/vernesong/OpenClash>
PKG_BUILD_DIR:=$(BUILD_DIR)/$(PKG_NAME)
@@ -137,7 +137,6 @@ define Package/$(PKG_NAME)/postrm
rm -rf /tmp/openclash.log >/dev/null 2>&1
rm -rf /tmp/openclash_start.log >/dev/null 2>&1
rm -rf /tmp/openclash_last_version >/dev/null 2>&1
rm -rf /tmp/openclash_config.tmp >/dev/null 2>&1
rm -rf /tmp/openclash.change >/dev/null 2>&1
rm -rf /tmp/Proxy_Group >/dev/null 2>&1
rm -rf /tmp/rules_name >/dev/null 2>&1
@@ -29,6 +29,7 @@ START_LOG="/tmp/openclash_start.log"
PROXY_FWMARK="0x162"
PROXY_ROUTE_TABLE="0x162"
QUICK_START_CHECK=false
QUICK_START=true
add_cron()
{
@@ -456,43 +457,34 @@ check_run_quick()
fi
QUICK_START_CHECK=true
quick_start=true
check_file="$(cat /tmp/openclash.change 2>/dev/null |awk '{print $1}')"
if [ -z "$check_file" ] || [ ! -f "/tmp/openclash_config.tmp" ]; then
quick_start=false
return
fi
cmp -s "/etc/config/openclash" "/tmp/openclash_config.tmp"
if [ "$?" -ne "0" ]; then
LOG_OUT "Tip: Because of the file【 /etc/config/openclash 】modificated, Pause quick start..."
quick_start=false
else
if [ -s "/tmp/openclash.change" ]; then
for i in $check_file; do
if [ -z "$(grep "$i $(date -r "$i")$" "/tmp/openclash.change")" ]; then
LOG_OUT "Tip: Because of the file【 $i 】modificated, Pause quick start..."
quick_start=false
break
fi
done
fi
fi
cat "/tmp/openclash.change" | while read -r i; do
file_path=$(echo "$i" |awk -F ' #edited time# ' '{print $1}')
if [ -z "$(grep "$file_path #edited time# $(date -r "$file_path")$" "/tmp/openclash.change")" ]; then
LOG_OUT "Tip: Because of the file【 $file_path 】modificated, Pause quick start..."
rm -rf /tmp/openclash.change
break
fi
done
if [ ! -f "/tmp/openclash.change" ]; then
QUICK_START=false
fi
} >/dev/null 2>&1
write_run_quick()
{
check_file="$(echo $RAW_CONFIG_FILE | tr " " "?") $(echo $CONFIG_FILE | tr " " "?") $(ls -d /etc/openclash/custom/*) $(ls -d /etc/openclash/overwrite/*) /usr/share/openclash/res/lhie1.yaml"
cmp -s "/etc/config/openclash" "/tmp/openclash_config.tmp"
if [ "$?" -ne "0" ]; then
cp "/etc/config/openclash" "/tmp/openclash_config.tmp"
fi
if [ "$quick_start" != "true" ]; then
: > "/tmp/openclash.change"
for i in $check_file; do
echo "$i $(date -r "$i")" >> "/tmp/openclash.change"
done
fi
: > "/tmp/openclash.change"
{
echo "/etc/config/openclash"
echo "$RAW_CONFIG_FILE"
echo "$CONFIG_FILE"
ls -d /etc/openclash/custom/* 2>/dev/null
ls -d /etc/openclash/overwrite/* 2>/dev/null
echo "/usr/share/openclash/res/lhie1.yaml"
} | while read -r file; do
echo "$file #edited time# $(date -r "$file")" >> "/tmp/openclash.change"
done
} >/dev/null 2>&1
#运行模式处理
@@ -747,7 +739,6 @@ check_core_status()
while ( [ -n "$(pidof clash)" ] && [ -z "$($ip_ route list |grep utun)" ] && [ "$TUN_RESTART" -le 3 ] )
do
LOG_OUT "Warning: TUN Interface Start Failed, Try to Restart Again..."
kill_clash
start_run_core
sleep 120
let TUN_RESTART++
@@ -759,17 +750,16 @@ check_core_status()
fi
if [ -n "$(pidof clash)" ]; then
ip -6 rule del oif utun table 2022
ip -6 route del default dev utun table 2022
if [ "$ipv6_mode" -eq 2 ] || [ "$ipv6_mode" -eq 3 ]; then
ip -6 rule del oif utun table 2022
ip -6 route del default dev utun table 2022
ip -6 route add default dev utun table "$PROXY_ROUTE_TABLE"
ip -6 rule add fwmark "$PROXY_FWMARK" ipproto icmp table main pref 1888
ip -6 rule add fwmark "$PROXY_FWMARK" table "$PROXY_ROUTE_TABLE" pref 1889
ip -6 rule add fwmark "$PROXY_FWMARK" table "$PROXY_ROUTE_TABLE" pref 1888
fi
if [ -n "$en_mode_tun" ]; then
ip route add default dev utun table "$PROXY_ROUTE_TABLE"
ip rule add fwmark "$PROXY_FWMARK" table "$PROXY_ROUTE_TABLE" pref 1888
fi
ip route add default dev utun table "$PROXY_ROUTE_TABLE"
ip rule add fwmark "$PROXY_FWMARK" ipproto icmp table main pref 1888
ip rule add fwmark "$PROXY_FWMARK" table "$PROXY_ROUTE_TABLE" pref 1889
fi
else
reg4='^(([0-9]|[1-9][0-9]|1[0-9][0-9]|2[0-4][0-9]|25[0-5])\.){3}([0-9]|[1-9][0-9]|1[0-9][0-9]|2[0-4][0-9]|25[0-5])$'
@@ -812,7 +802,7 @@ start_run_core()
ulimit -v unlimited
ulimit -u unlimited
if [ "$quick_start" != "true" ]; then
if ! $QUICK_START; then
mv "$TMP_CONFIG_FILE" "$CONFIG_FILE"
rm -rf "$TMP_CONFIG_FILE"
fi
@@ -2229,10 +2219,27 @@ if [ -n "$FW4" ]; then
fi
#icmp
if [ -n "$en_mode_tun" ]; then
nft insert rule inet fw4 mangle_prerouting position 0 meta nfproto {ipv4} ip protocol icmp icmp type echo-request mark set "$PROXY_FWMARK" counter accept comment \"OpenClash ICMP Redirect\"
fi
if [ "$ipv6_enable" -eq 1 ]; then
if [ "$ipv6_mode" -eq 2 ] || [ "$ipv6_mode" -eq 3 ]; then
nft insert rule inet fw4 mangle_prerouting position 0 meta nfproto {ipv6} ip6 nexthdr icmpv6 icmpv6 type echo-request mark set "$PROXY_FWMARK" counter accept comment \"OpenClash ICMPv6 Redirect\"
fi
fi
if [ "$en_mode" = "fake-ip" ]; then
nft insert rule inet fw4 input position 0 ip protocol icmp icmp type echo-request ip daddr { $fakeip_range } counter reject comment \"OpenClash ICMP INPUT REJECT\"
nft insert rule inet fw4 forward position 0 ip protocol icmp icmp type echo-request ip daddr { $fakeip_range } counter reject comment \"OpenClash ICMP FORWARD REJECT\"
nft insert rule inet fw4 output position 0 ip protocol icmp icmp type echo-request ip daddr { $fakeip_range } $noowner counter reject comment \"OpenClash ICMP OUTPUT REJECT\"
if [ -z "$en_mode_tun" ]; then
nft insert rule inet fw4 input position 0 ip protocol icmp icmp type echo-request ip daddr { $fakeip_range } counter reject comment \"OpenClash ICMP INPUT REJECT\"
nft insert rule inet fw4 forward position 0 ip protocol icmp icmp type echo-request ip daddr { $fakeip_range } counter reject comment \"OpenClash ICMP FORWARD REJECT\"
nft insert rule inet fw4 output position 0 ip protocol icmp icmp type echo-request ip daddr { $fakeip_range } $noowner counter reject comment \"OpenClash ICMP OUTPUT REJECT\"
fi
if [ "$ipv6_enable" -eq 1 ] || [ "$ipv6_dns" -eq 1 ]; then
if [ "$ipv6_mode" -ne 2 ] && [ "$ipv6_mode" -ne 3 ]; then
nft insert rule inet fw4 input position 0 ip6 nexthdr icmpv6 icmpv6 type echo-request ip6 daddr { $fakeip_range6 } counter reject with icmpv6 admin-prohibited comment \"OpenClash ICMPv6 INPUT REJECT\"
nft insert rule inet fw4 forward position 0 ip6 nexthdr icmpv6 icmpv6 type echo-request ip6 daddr { $fakeip_range6 } counter reject with icmpv6 admin-prohibited comment \"OpenClash ICMPv6 FORWARD REJECT\"
nft insert rule inet fw4 output position 0 ip6 nexthdr icmpv6 icmpv6 type echo-request ip6 daddr { $fakeip_range6 } $noowner counter reject with icmpv6 admin-prohibited comment \"OpenClash ICMPv6 OUTPUT REJECT\"
fi
fi
fi
fi
@@ -3009,10 +3016,27 @@ if [ -z "$FW4" ]; then
fi
#icmp
if [ -n "$en_mode_tun" ]; then
iptables -t mangle -I PREROUTING -p icmp --icmp-type echo-request -j MARK --set-xmark "$PROXY_FWMARK" -m comment --comment "OpenClash ICMP Redirect"
fi
if [ "$ipv6_enable" -eq 1 ]; then
if [ "$ipv6_mode" -eq 2 ] || [ "$ipv6_mode" -eq 3 ]; then
ip6tables -t mangle -I PREROUTING -p icmpv6 --icmpv6-type echo-request -j MARK --set-xmark "$PROXY_FWMARK" -m comment --comment "OpenClash ICMPv6 Redirect"
fi
fi
if [ "$en_mode" = "fake-ip" ]; then
iptables -t filter -I INPUT -p icmp --icmp-type echo-request -d "$fakeip_range" -j REJECT --reject-with icmp-admin-prohibited -m comment --comment "OpenClash ICMP INPUT REJECT"
iptables -t filter -I FORWARD -p icmp --icmp-type echo-request -d "$fakeip_range" -j REJECT --reject-with icmp-admin-prohibited -m comment --comment "OpenClash ICMP FORWARD REJECT"
iptables -t filter -I OUTPUT -p icmp --icmp-type echo-request -d "$fakeip_range" $noowner -j REJECT --reject-with icmp-admin-prohibited -m comment --comment "OpenClash ICMP OUTPUT REJECT"
if [ -z "$en_mode_tun" ]; then
iptables -t filter -I INPUT -p icmp --icmp-type echo-request -d "$fakeip_range" -j REJECT --reject-with icmp-admin-prohibited -m comment --comment "OpenClash ICMP INPUT REJECT"
iptables -t filter -I FORWARD -p icmp --icmp-type echo-request -d "$fakeip_range" -j REJECT --reject-with icmp-admin-prohibited -m comment --comment "OpenClash ICMP FORWARD REJECT"
iptables -t filter -I OUTPUT -p icmp --icmp-type echo-request -d "$fakeip_range" $noowner -j REJECT --reject-with icmp-admin-prohibited -m comment --comment "OpenClash ICMP OUTPUT REJECT"
fi
if [ "$ipv6_enable" -eq 1 ] || [ "$ipv6_dns" -eq 1 ]; then
if [ "$ipv6_mode" -ne 2 ] && [ "$ipv6_mode" -ne 3 ]; then
ip6tables -t filter -I INPUT -p icmpv6 --icmpv6-type echo-request -d "$fakeip_range6" -j REJECT --reject-with icmp6-adm-prohibited -m comment --comment "OpenClash ICMPv6 INPUT REJECT"
ip6tables -t filter -I FORWARD -p icmpv6 --icmpv6-type echo-request -d "$fakeip_range6" -j REJECT --reject-with icmp6-adm-prohibited -m comment --comment "OpenClash ICMPv6 FORWARD REJECT"
ip6tables -t filter -I OUTPUT -p icmpv6 --icmpv6-type echo-request -d "$fakeip_range6" $noowner -j REJECT --reject-with icmp6-adm-prohibited -m comment --comment "OpenClash ICMPv6 OUTPUT REJECT"
fi
fi
fi
fi
@@ -3045,10 +3069,8 @@ revert_firewall()
#TUN
ip rule del fwmark "$PROXY_FWMARK" table "$PROXY_ROUTE_TABLE"
ip rule del fwmark "$PROXY_FWMARK" ipproto icmp table main
ip route del default dev utun table "$PROXY_ROUTE_TABLE"
ip -6 rule del fwmark "$PROXY_FWMARK" table "$PROXY_ROUTE_TABLE"
ip -6 rule del fwmark "$PROXY_FWMARK" ipproto icmp table main
ip -6 route del default dev utun table "$PROXY_ROUTE_TABLE"
if [ -n "$FW4" ]; then
@@ -3067,7 +3089,7 @@ revert_firewall()
nft delete set inet fw4 handle ${handle}
done
else
for ipt in "iptables -nvL INPUT" "iptables -nvL FORWARD" "iptables -nvL OUTPUT" "iptables -nvL POSTROUTING -t nat" "iptables -nvL OUTPUT -t nat" "iptables -nvL OUTPUT -t mangle" "iptables -nvL PREROUTING -t nat" "iptables -nvL PREROUTING -t mangle" "ip6tables -nvL PREROUTING -t mangle" "ip6tables -nvL OUTPUT -t mangle" "ip6tables -nvL PREROUTING -t nat" "ip6tables -nvL INPUT" "ip6tables -nvL POSTROUTING -t nat" "ip6tables -nvL FORWARD" "ip6tables -nvL OUTPUT -t nat"; do
for ipt in "iptables -nvL INPUT" "iptables -nvL FORWARD" "iptables -nvL OUTPUT" "iptables -nvL POSTROUTING -t nat" "iptables -nvL OUTPUT -t nat" "iptables -nvL OUTPUT -t mangle" "iptables -nvL PREROUTING -t nat" "iptables -nvL PREROUTING -t mangle" "ip6tables -nvL OUTPUT" "ip6tables -nvL INPUT" "ip6tables -nvL FORWARD" "ip6tables -nvL OUTPUT -t mangle" "ip6tables -nvL PREROUTING -t nat" "ip6tables -nvL PREROUTING -t mangle" "ip6tables -nvL POSTROUTING -t nat" "ip6tables -nvL OUTPUT -t nat"; do
for comment in "openclash" "OpenClash"; do
local lines=$($ipt |sed 1,2d |sed -n "/${comment}/=" 2>/dev/null |sort -rn)
if [ -n "$lines" ]; then
@@ -3619,10 +3641,6 @@ start_service()
enable=$(uci_get_config "enable")
[ "$enable" != "1" ] && LOG_OUT "Warning: OpenClash Now Disabled, Need Start From Luci Page, Exit..." && SLOG_CLEAN && exit 0
if pidof clash >/dev/null 2>&1; then
stop
fi
LOG_OUT "Tip: OpenClash Start Running..."
{
@@ -3637,7 +3655,7 @@ start_service()
LOG_OUT "Step 2: Check The Components..."
do_run_file "$RAW_CONFIG_FILE" "$BACKUP_FILE"
if [ "$quick_start" != "true" ]; then
if ! $QUICK_START; then
LOG_OUT "Step 3: Modify The Config File..."
config_check
/usr/share/openclash/yml_change.sh \
@@ -3749,7 +3767,6 @@ stop_service()
/tmp/rules_name \
/tmp/rule_providers_name \
/tmp/openclash_last_version \
/tmp/openclash_config.tmp \
/tmp/openclash.change \
/tmp/openclash_debug.log \
/tmp/openclash_edit_file_name \
@@ -3846,7 +3863,8 @@ reload_service()
if pidof clash >/dev/null && [ "$enable" == "1" ] && [ "$1" == "restore" ]; then
do_run_mode
get_config
check_core_status &
# used for config subscribe, not background for avoiding system unready
check_core_status
fi
} >/dev/null 2>&1
@@ -504,8 +504,8 @@ sub_info_get()
LOG_OUT "Config File Format Validation Failed, Trying To Download Without Agent..."
config_download_direct
elif ! "$(ruby_read "$CFG_FILE" ".key?('proxies')")" && ! "$(ruby_read "$CFG_FILE" ".key?('proxy-providers')")" ; then
LOG_OUT "Error: Updated Config【$name】Has No Proxy Field, Trying To Download Without Agent..."
config_download_direct
LOG_OUT "Error: Updated Config【$name】Has No Proxy Field, Trying To Download Without Agent..."
config_download_direct
else
config_su_check
fi
@@ -1,9 +1,9 @@
#!/bin/bash
. /lib/functions.sh
. /usr/share/openclash/openclash_ps.sh
. /usr/share/openclash/log.sh
. /usr/share/openclash/openclash_curl.sh
. /usr/share/openclash/uci.sh
. /usr/share/openclash/openclash_curl.sh
. /usr/share/openclash/openclash_ps.sh
set_lock() {
exec 872>"/tmp/lock/openclash_core.lock" 2>/dev/null
@@ -16,7 +16,9 @@ del_lock() {
}
set_lock
inc_job_counter
restart=0
github_address_mod=$(uci_get_config "github_address_mod" || echo 0)
if [ "$github_address_mod" = "0" ] && [ -z "$(echo $2 2>/dev/null |grep -E 'http|one_key_update')" ] && [ -z "$(echo $3 2>/dev/null |grep 'http')" ]; then
LOG_OUT "Tip: If the download fails, try setting the CDN in Overwrite Settings - General Settings - Github Address Modify Options"
@@ -72,7 +74,7 @@ else
CORE_LV=$(sed -n 1p /tmp/clash_last_version 2>/dev/null)
fi
[ "$C_CORE_TYPE" = "$CORE_TYPE" ] || [ -z "$C_CORE_TYPE" ] && if_restart=1
[ "$C_CORE_TYPE" = "$CORE_TYPE" ] || [ -z "$C_CORE_TYPE" ] && restart=1
if [ "$CORE_CV" != "$CORE_LV" ] || [ -z "$CORE_CV" ]; then
if [ "$CPU_MODEL" != 0 ]; then
@@ -131,17 +133,8 @@ if [ "$CORE_CV" != "$CORE_LV" ] || [ -z "$CORE_CV" ]; then
if [ "$?" == "0" ]; then
LOG_OUT "Tip:【"$CORE_TYPE"】Core Update Successful!"
if [ "$if_restart" -eq 1 ]; then
uci -q set openclash.config.restart=1
uci -q commit openclash
if ([ -z "$2" ] || ([ -n "$2" ] && [ "$2" != "one_key_update" ])) && [ "$(unify_ps_prevent)" -eq 0 ]; then
uci -q set openclash.config.restart=0
uci -q commit openclash
/etc/init.d/openclash restart >/dev/null 2>&1 &
fi
else
SLOG_CLEAN
fi
SLOG_CLEAN
restart=1
break
else
if [ "$retry_count" -lt "$max_retries" ]; then
@@ -187,4 +180,5 @@ else
fi
rm -rf "$TMP_FILE" >/dev/null 2>&1
dec_job_counter_and_restart "$restart"
del_lock
@@ -39,9 +39,10 @@ inc_job_counter() {
exec 999>"/tmp/lock/openclash_jobs.lock"
flock -x 999
local cnt=0
[ -f "$JOB_COUNTER_FILE" ] && cnt=$(cat "$JOB_COUNTER_FILE")
local restart=0
[ -f "$JOB_COUNTER_FILE" ] && cnt=$(cat "$JOB_COUNTER_FILE" | awk '{print $1}') && restart=$(cat "$JOB_COUNTER_FILE" | awk '{print $2}')
cnt=$((cnt+1))
echo "$cnt" > "$JOB_COUNTER_FILE"
echo "$cnt $restart" > "$JOB_COUNTER_FILE"
flock -u 999
}
@@ -50,23 +51,19 @@ dec_job_counter_and_restart() {
exec 999>"/tmp/lock/openclash_jobs.lock"
flock -x 999
local cnt=0
[ -f "$JOB_COUNTER_FILE" ] && cnt=$(cat "$JOB_COUNTER_FILE")
local restart=0
[ -f "$JOB_COUNTER_FILE" ] && cnt=$(cat "$JOB_COUNTER_FILE" | awk '{print $1}') && restart=$(cat "$JOB_COUNTER_FILE" | awk '{print $2}')
cnt=$((cnt-1))
[ $cnt -lt 0 ] && cnt=0
echo "$cnt" > "$JOB_COUNTER_FILE"
if [ "$restart_flag" -eq 1 ]; then
uci -q set openclash.config.restart=1
uci -q commit openclash
restart=1
fi
if [ $cnt -eq 0 ]; then
if [ "$(uci_get_config "restart")" -eq 1 ] && [ "$(unify_ps_prevent)" -eq 0 ]; then
/etc/init.d/openclash restart >/dev/null 2>&1 &
uci -q set openclash.config.restart=0
uci -q commit openclash
fi
echo "$cnt $restart" > "$JOB_COUNTER_FILE"
if [ $cnt -eq 0 ] && [ "$restart" -eq 1 ] && [ "$(unify_ps_prevent)" -eq 0 ]; then
/etc/init.d/openclash restart >/dev/null 2>&1 &
rm -rf "$JOB_COUNTER_FILE" >/dev/null 2>&1
fi
@@ -1,7 +1,8 @@
#!/bin/bash
. /usr/share/openclash/log.sh
. /usr/share/openclash/openclash_curl.sh
. /usr/share/openclash/uci.sh
. /usr/share/openclash/openclash_curl.sh
. /usr/share/openclash/openclash_ps.sh
set_lock() {
exec 878>"/tmp/lock/openclash_update.lock" 2>/dev/null
@@ -14,6 +15,8 @@ del_lock() {
}
set_lock
inc_job_counter
restart=0
if [ -n "$1" ] && [ "$1" != "one_key_update" ]; then
[ ! -f "/tmp/openclash_last_version" ] && /usr/share/openclash/openclash_version.sh "$1" 2>/dev/null
@@ -26,6 +29,7 @@ fi
if [ ! -f "/tmp/openclash_last_version" ]; then
LOG_OUT "Error: Failed to get version information, please try again later..."
SLOG_CLEAN
dec_job_counter_and_restart "$restart"
del_lock
exit 0
fi
@@ -61,8 +65,6 @@ github_address_mod=$(uci_get_config "github_address_mod" || echo 0)
#一键更新
if [ "$1" = "one_key_update" ]; then
uci -q set openclash.config.enable=1
uci -q commit openclash
if [ "$github_address_mod" = "0" ] && [ -z "$2" ]; then
LOG_OUT "Tip: If the download fails, try setting the CDN in Overwrite Settings - General Settings - Github Address Modify Options"
fi
@@ -173,13 +175,9 @@ if [ -n "$OP_CV" ] && [ -n "$OP_LV" ] && version_compare "$OP_CV" "$OP_LV" && [
elif [ -x "/usr/bin/apk" ]; then
LOG_OUT "Error:【OpenClash - v$LAST_VER】Pre update test failed after 3 attempts, the file is saved in /tmp/openclash.apk, please try to update manually with【apk add -q --force-overwrite --clean-protected --allow-untrusted /tmp/openclash.apk】"
fi
if [ "$(uci_get_config "restart")" -eq 1 ]; then
uci -q set openclash.config.restart=0
uci -q commit openclash
/etc/init.d/openclash restart >/dev/null 2>&1 &
else
SLOG_CLEAN
fi
SLOG_CLEAN
dec_job_counter_and_restart "$restart"
del_lock
exit 0
fi
@@ -193,13 +191,8 @@ if [ -n "$OP_CV" ] && [ -n "$OP_LV" ] && version_compare "$OP_CV" "$OP_LV" && [
LOG_OUT "Error:【OpenClash - v$LAST_VER】Download Failed after 3 attempts, please check the network or try again later!"
rm -rf /tmp/openclash.ipk >/dev/null 2>&1
rm -rf /tmp/openclash.apk >/dev/null 2>&1
if [ "$(uci_get_config "restart")" -eq 1 ]; then
uci -q set openclash.config.restart=0
uci -q commit openclash
/etc/init.d/openclash restart >/dev/null 2>&1 &
else
SLOG_CLEAN
fi
SLOG_CLEAN
dec_job_counter_and_restart "$restart"
del_lock
exit 0
fi
@@ -411,12 +404,8 @@ else
else
LOG_OUT "Tip: OpenClash has not been updated, stop continuing!"
fi
if [ "$(uci_get_config "restart")" -eq 1 ]; then
uci -q set openclash.config.restart=0
uci -q commit openclash
/etc/init.d/openclash restart >/dev/null 2>&1 &
else
SLOG_CLEAN
fi
SLOG_CLEAN
dec_job_counter_and_restart "$restart"
fi
del_lock
@@ -505,7 +505,7 @@ threads << Thread.new do
'enable' => true, 'stack' => stack_type, 'device' => 'utun',
'dns-hijack' => ['127.0.0.1:53'], 'endpoint-independent-nat' => true,
'auto-route' => false, 'auto-detect-interface' => false,
'auto-redirect' => false, 'strict-route' => false
'auto-redirect' => false, 'strict-route' => false, 'disable-icmp-forwarding' => false
}
Value['tun'].delete('iproute2-table-index')
else
@@ -195,41 +195,6 @@ get_jump_ipt() {
esac
}
gen_lanlist() {
cat $RULES_PATH/lanlist_ipv4 | tr -s '\n' | grep -v "^#"
}
gen_lanlist_6() {
cat $RULES_PATH/lanlist_ipv6 | tr -s '\n' | grep -v "^#"
}
get_wan_ips() {
local family="$1"
local NET_ADDR
local iface
local INTERFACES=$(ubus call network.interface dump | jsonfilter -e '@.interface[!(@.interface ~ /lan/) && @.route[0]].interface')
for iface in $INTERFACES; do
local addr
if [ "$family" = "ip6" ]; then
network_get_ipaddr6 addr "$iface"
case "$addr" in
""|fe80*) continue ;;
esac
else
network_get_ipaddr addr "$iface"
case "$addr" in
""|"0.0.0.0") continue ;;
esac
fi
case " $NET_ADDR " in
*" $addr "*) ;;
*) NET_ADDR="${NET_ADDR:+$NET_ADDR }$addr" ;;
esac
done
echo "$NET_ADDR"
}
load_acl() {
([ "$ENABLED_ACLS" == 1 ] || ([ "$ENABLED_DEFAULT_ACL" == 1 ] && [ "$CLIENT_PROXY" == 1 ])) && echolog " - 访问控制:"
[ "$ENABLED_ACLS" == 1 ] && {
@@ -228,41 +228,6 @@ get_jump_ipt() {
esac
}
gen_lanlist() {
cat $RULES_PATH/lanlist_ipv4 | tr -s '\n' | grep -v "^#"
}
gen_lanlist_6() {
cat $RULES_PATH/lanlist_ipv6 | tr -s '\n' | grep -v "^#"
}
get_wan_ips() {
local family="$1"
local NET_ADDR
local iface
local INTERFACES=$(ubus call network.interface dump | jsonfilter -e '@.interface[!(@.interface ~ /lan/) && @.route[0]].interface')
for iface in $INTERFACES; do
local addr
if [ "$family" = "ip6" ]; then
network_get_ipaddr6 addr "$iface"
case "$addr" in
""|fe80*) continue ;;
esac
else
network_get_ipaddr addr "$iface"
case "$addr" in
""|"0.0.0.0") continue ;;
esac
fi
case " $NET_ADDR " in
*" $addr "*) ;;
*) NET_ADDR="${NET_ADDR:+$NET_ADDR }$addr" ;;
esac
done
echo "$NET_ADDR"
}
load_acl() {
([ "$ENABLED_ACLS" == 1 ] || ([ "$ENABLED_DEFAULT_ACL" == 1 ] && [ "$CLIENT_PROXY" == 1 ])) && echolog " - 访问控制:"
[ "$ENABLED_ACLS" == 1 ] && {
@@ -64,7 +64,8 @@ get_enabled_anonymous_secs() {
get_geoip() {
local geoip_code="$1"
local geoip_type_flag=""
local geoip_path="${V2RAY_LOCATION_ASSET%*/}/geoip.dat"
local geoip_path="$(config_t_get global_rules v2ray_location_asset "/usr/share/v2ray/")"
geoip_path="${geoip_path%*/}/geoip.dat"
local bin="$(first_type $(config_t_get global_app geoview_file) geoview)"
[ -n "$bin" ] && [ -s "$geoip_path" ] || { echo ""; return 1; }
case "$2" in
@@ -441,3 +442,38 @@ get_subscribe_host(){
echo "$url"
done
}
gen_lanlist() {
cat $RULES_PATH/lanlist_ipv4 | tr -s '\n' | grep -v "^#"
}
gen_lanlist_6() {
cat $RULES_PATH/lanlist_ipv6 | tr -s '\n' | grep -v "^#"
}
get_wan_ips() {
local family="$1"
local NET_ADDR
local iface
local INTERFACES=$(ubus call network.interface dump | jsonfilter -e \
'@.interface[!(@.interface ~ /lan/) && !(@.l3_device ~ /\./) && @.route[0]].interface')
for iface in $INTERFACES; do
local addr
if [ "$family" = "ip6" ]; then
network_get_ipaddr6 addr "$iface"
case "$addr" in
""|fe80*) continue ;;
esac
else
network_get_ipaddr addr "$iface"
case "$addr" in
""|"0.0.0.0") continue ;;
esac
fi
case " $NET_ADDR " in
*" $addr "*) ;;
*) NET_ADDR="${NET_ADDR:+$NET_ADDR }$addr" ;;
esac
done
echo "$NET_ADDR"
}
@@ -168,65 +168,6 @@ get_redirect_ip6t() {
echo "$(REDIRECT $2 $3)"
}
gen_lanlist() {
cat <<-EOF
0.0.0.0/8
10.0.0.0/8
100.64.0.0/10
127.0.0.0/8
169.254.0.0/16
172.16.0.0/12
192.168.0.0/16
224.0.0.0/4
240.0.0.0/4
EOF
}
gen_lanlist_6() {
cat <<-EOF
::/128
::1/128
::ffff:0:0/96
::ffff:0:0:0/96
64:ff9b::/96
100::/64
2001::/32
2001:20::/28
2001:db8::/32
2002::/16
fc00::/7
fe80::/10
ff00::/8
EOF
}
get_wan_ips() {
local family="$1"
local NET_ADDR
local iface
local INTERFACES=$(ubus call network.interface dump | jsonfilter -e '@.interface[@.route[0]].interface')
for iface in $INTERFACES; do
local addr
if [ "$family" = "ip6" ]; then
network_get_ipaddr6 addr "$iface"
case "$addr" in
""|fe80*) continue ;;
esac
else
network_get_ipaddr addr "$iface"
case "$addr" in
""|"0.0.0.0") continue ;;
esac
fi
case " $NET_ADDR " in
*" $addr "*) ;;
*) NET_ADDR="${NET_ADDR:+$NET_ADDR }$addr" ;;
esac
done
echo "$NET_ADDR"
}
gen_shunt_list() {
local node=${1}
local shunt_list4_var_name=${2}
@@ -201,65 +201,6 @@ gen_nftset() {
[ $# -gt 0 ] || [ ! -t 0 ] && insert_nftset "$nftset_name" "$timeout_argument_element" "$@"
}
gen_lanlist() {
cat <<-EOF
0.0.0.0/8
10.0.0.0/8
100.64.0.0/10
127.0.0.0/8
169.254.0.0/16
172.16.0.0/12
192.168.0.0/16
224.0.0.0/4
240.0.0.0/4
EOF
}
gen_lanlist_6() {
cat <<-EOF
::/128
::1/128
::ffff:0:0/96
::ffff:0:0:0/96
64:ff9b::/96
100::/64
2001::/32
2001:20::/28
2001:db8::/32
2002::/16
fc00::/7
fe80::/10
ff00::/8
EOF
}
get_wan_ips() {
local family="$1"
local NET_ADDR
local iface
local INTERFACES=$(ubus call network.interface dump | jsonfilter -e '@.interface[@.route[0]].interface')
for iface in $INTERFACES; do
local addr
if [ "$family" = "ip6" ]; then
network_get_ipaddr6 addr "$iface"
case "$addr" in
""|fe80*) continue ;;
esac
else
network_get_ipaddr addr "$iface"
case "$addr" in
""|"0.0.0.0") continue ;;
esac
fi
case " $NET_ADDR " in
*" $addr "*) ;;
*) NET_ADDR="${NET_ADDR:+$NET_ADDR }$addr" ;;
esac
done
echo "$NET_ADDR"
}
gen_shunt_list() {
local node=${1}
local shunt_list4_var_name=${2}
@@ -151,16 +151,13 @@ get_geoip() {
local geoip_type_flag=""
local geoip_path="$(config_t_get global_rules v2ray_location_asset)"
geoip_path="${geoip_path%*/}/geoip.dat"
[ -e "$geoip_path" ] || { echo ""; return; }
local bin="$(first_type $(config_t_get global_app geoview_file) geoview)"
[ -n "$bin" ] && [ -s "$geoip_path" ] || { echo ""; return; }
case "$2" in
"ipv4") geoip_type_flag="-ipv6=false" ;;
"ipv6") geoip_type_flag="-ipv4=false" ;;
esac
if type geoview &> /dev/null; then
geoview -input "$geoip_path" -list "$geoip_code" $geoip_type_flag -lowmem=true
else
echo ""
fi
"$bin" -input "$geoip_path" -list "$geoip_code" $geoip_type_flag -lowmem=true
}
get_host_ip() {
@@ -386,3 +383,62 @@ ln_run() {
kill_all() {
kill -9 $(pidof "$@") >/dev/null 2>&1
}
gen_lanlist() {
cat <<-EOF
0.0.0.0/8
10.0.0.0/8
100.64.0.0/10
127.0.0.0/8
169.254.0.0/16
172.16.0.0/12
192.168.0.0/16
224.0.0.0/4
240.0.0.0/4
EOF
}
gen_lanlist_6() {
cat <<-EOF
::/128
::1/128
::ffff:0:0/96
::ffff:0:0:0/96
64:ff9b::/96
100::/64
2001::/32
2001:20::/28
2001:db8::/32
2002::/16
fc00::/7
fe80::/10
ff00::/8
EOF
}
get_wan_ips() {
local family="$1"
local NET_ADDR
local iface
local INTERFACES=$(ubus call network.interface dump | jsonfilter -e \
'@.interface[!(@.interface ~ /lan/) && !(@.l3_device ~ /\./) && @.route[0]].interface')
for iface in $INTERFACES; do
local addr
if [ "$family" = "ip6" ]; then
network_get_ipaddr6 addr "$iface"
case "$addr" in
""|fe80*) continue ;;
esac
else
network_get_ipaddr addr "$iface"
case "$addr" in
""|"0.0.0.0") continue ;;
esac
fi
case " $NET_ADDR " in
*" $addr "*) ;;
*) NET_ADDR="${NET_ADDR:+$NET_ADDR }$addr" ;;
esac
done
echo "$NET_ADDR"
}
+35 -40
View File
@@ -2,51 +2,46 @@ name: build
on:
push:
push:
branches:
- master
tags:
- v*
- latest
branches: [master]
pull_request:
branches: [master]
jobs:
build:
runs-on: ubuntu-latest
strategy:
fail-fast: false
matrix:
os: [ubuntu-latest, macos-latest]
runs-on: ${{ matrix.os }}
steps:
- uses: actions/checkout@v2.3.4
- uses: actions/checkout@v4
with:
submodules: true
submodules: recursive
- uses: docker/setup-qemu-action@v1.1.0
- uses: docker/setup-buildx-action@v1.3.0
- uses: docker/login-action@v1.9.0
with:
password: ${{ secrets.DOCKER_PASSWORD }}
username: ${{ secrets.DOCKER_USERNAME }}
- name: check and set image version
id: prepare
- name: Install dependencies (Linux)
if: runner.os == 'Linux'
run: |
case ${{ github.ref }} in
refs/heads/master)
echo ::set-output name=version::edge
echo ::set-output name=push::true
;;
refs/tags/*)
echo ::set-output name=version::$(echo ${{ github.ref }} | sed -E 's|refs/tags/||')
echo ::set-output name=push::true
;;
*)
echo ::set-output name=version::${{ github.sha }}
echo ::set-output name=push::false
;;
esac;
sudo apt-get update
sudo apt-get install -y libpcre2-dev libmbedtls-dev libsodium-dev libev-dev libc-ares-dev
- name: build & push image
uses: docker/build-push-action@v2.4.0
with:
context: .
file: docker/alpine/Dockerfile
platforms: linux/amd64,linux/arm64
push: ${{ steps.prepare.outputs.push }}
tags: ${{ github.repository }}:${{ steps.prepare.outputs.version }}
- name: Install dependencies (macOS)
if: runner.os == 'macOS'
run: brew install mbedtls@3 libsodium libev c-ares pcre2
- name: Build
run: |
mkdir -p build && cd build
cmake .. -DCMAKE_BUILD_TYPE=Release -DWITH_STATIC=OFF
make -j$(nproc 2>/dev/null || sysctl -n hw.ncpu)
- name: Unit tests
run: |
cd build
ctest --output-on-failure
- name: ss-setup TUI tests
run: bash tests/test_ss_setup.sh
- name: Stress test
run: |
python3 tests/stress_test.py --bin build/shared/bin/ --size 10
+6 -8
View File
@@ -15,7 +15,7 @@ before_install:
- |
if [[ "$TRAVIS_OS_NAME" == "osx" ]]; then
# All dependencies for macOS build. Some packages has been installed by travis so use reinstall.
brew reinstall autoconf automake xmlto c-ares libev mbedtls libsodium asciidoc >> /dev/null 2>&1;
brew reinstall cmake xmlto c-ares libev mbedtls libsodium asciidoc >> /dev/null 2>&1;
else
wget https://github.com/jedisct1/libsodium/releases/download/$LIBSODIUM_VER/libsodium-$LIBSODIUM_VER.tar.gz;
tar xvf libsodium-$LIBSODIUM_VER.tar.gz;
@@ -39,19 +39,17 @@ addons:
sources:
- george-edison55-precise-backports # cmake 3.2.3 / doxygen 1.8.3
packages:
- cmake
- libc-ares-dev
- libev-dev
- asciidoc
- xmlto
script:
- ./autogen.sh
- if [[ "$TRAVIS_OS_NAME" == "osx" ]]; then
./configure --disable-documentation --with-mbedtls=/usr/local/opt/mbedtls --with-sodium=/usr/local/opt/libsodium;
else
./configure;
fi
- git submodule update --init --recursive
- mkdir -p build && cd build
- cmake ..
- make
- cd build && cmake ../ && make
- ctest --output-on-failure
branches:
only:
- master
+58 -7
View File
@@ -1,4 +1,4 @@
cmake_minimum_required(VERSION 3.2)
cmake_minimum_required(VERSION 3.5)
set(PROJECT_NAME shadowsocks-libev)
set(RELEASE_DATE 2020-09-15)
@@ -8,9 +8,14 @@ set(PROJECT_URL "https://shadowsocks.org")
set(PROJECT_ISSUES_URL "https://github.com/shadowsocks/shadowsocks-libev")
project(${PROJECT_NAME} VERSION ${PROJECT_VERSION})
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -std=gnu99")
include(GNUInstallDirs)
# Compiler flags matching autotools
# Note: -Werror is applied per-target in src/CMakeLists.txt to avoid
# breaking bundled submodules (libcork, libipset, libbloom)
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -std=gnu99 -D_GNU_SOURCE")
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -g -O2 -Wall -Wno-deprecated-declarations -fno-strict-aliasing")
#set(PROJECT_BINARY_DIR ${PROJECT_SOURCE_DIR}/out)
set(CMAKE_ARCHIVE_OUTPUT_DIRECTORY ${PROJECT_BINARY_DIR}/lib)
set(CMAKE_LIBRARY_OUTPUT_DIRECTORY ${PROJECT_BINARY_DIR}/lib)
set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${PROJECT_BINARY_DIR}/bin)
@@ -33,15 +38,48 @@ message(STATUS "Running cmake version ${CMAKE_VERSION}")
option(WITH_STATIC "build with static libraries." ON)
option(WITH_EMBEDDED_SRC "build with embedded libcork, libipset, and libbloom source." ON)
# Will set GIT_EXECUTABLE and GIT_FOUND
# find_package(Git)
option(ENABLE_CONNMARKTOS "Enable saved connmark to IP TOS QoS feature" OFF)
option(ENABLE_NFTABLES "Report malicious IP to nftables" OFF)
# When choose to not use embedded libcork, libipset and libbloom, use libs shipped by system
if (NOT WITH_EMBEDDED_SRC)
set(USE_SYSTEM_SHARED_LIB TRUE)
endif ()
# Find dependencies via Find modules
find_package(PCRE2 REQUIRED)
find_package(MbedTLS REQUIRED)
find_package(Sodium REQUIRED)
find_package(Cares REQUIRED)
# Connmarktos support
if(ENABLE_CONNMARKTOS)
if(NOT LINUX)
message(FATAL_ERROR "connmarktos is only supported on Linux")
endif()
find_library(NETFILTER_CONNTRACK_LIB netfilter_conntrack)
find_library(NFNETLINK_LIB nfnetlink)
if(NOT NETFILTER_CONNTRACK_LIB)
message(FATAL_ERROR "--enable-connmarktos specified but libnetfilter_conntrack not found")
endif()
set(USE_NFCONNTRACK_TOS 1)
message(STATUS "Connmarktos enabled")
endif()
# Nftables support
if(ENABLE_NFTABLES)
if(NOT LINUX)
message(FATAL_ERROR "nftables is only supported on Linux")
endif()
find_library(MNL_LIB mnl)
find_library(NFTNL_LIB nftnl)
if(NOT MNL_LIB OR NOT NFTNL_LIB)
message(FATAL_ERROR "--enable-nftables specified but libmnl or libnftnl not found")
endif()
set(USE_NFTABLES 1)
message(STATUS "Nftables enabled")
endif()
# Run platform tests
include(${PROJECT_SOURCE_DIR}/cmake/configure.cmake)
configure_file(${PROJECT_SOURCE_DIR}/cmake/config.h.cmake ${PROJECT_BINARY_DIR}/src/config.h)
@@ -56,7 +94,7 @@ configure_file(
)
install(FILES
${PROJECT_BINARY_DIR}/pkgconfig/shadowsocks-libev.pc
DESTINATION lib/pkgconfig
DESTINATION ${CMAKE_INSTALL_LIBDIR}/pkgconfig
)
if (WITH_EMBEDDED_SRC)
@@ -158,3 +196,16 @@ endif ()
add_subdirectory(src)
add_subdirectory(doc)
# Testing
include(CTest)
if(BUILD_TESTING)
add_subdirectory(tests)
endif()
# Install ss-nat on Linux
if(LINUX)
install(PROGRAMS src/ss-nat DESTINATION ${CMAKE_INSTALL_BINDIR})
endif()
# Install ss-setup TUI tool
install(PROGRAMS scripts/ss-setup.sh DESTINATION ${CMAKE_INSTALL_BINDIR} RENAME ss-setup)
-365
View File
@@ -1,365 +0,0 @@
Installation Instructions
*************************
Copyright (C) 1994, 1995, 1996, 1999, 2000, 2001, 2002, 2004, 2005,
2006, 2007, 2008, 2009 Free Software Foundation, Inc.
Copying and distribution of this file, with or without modification,
are permitted in any medium without royalty provided the copyright
notice and this notice are preserved. This file is offered as-is,
without warranty of any kind.
Basic Installation
==================
Briefly, the shell commands `./configure; make; make install' should
configure, build, and install this package. The following
more-detailed instructions are generic; see the `README' file for
instructions specific to this package. Some packages provide this
`INSTALL' file but do not implement all of the features documented
below. The lack of an optional feature in a given package is not
necessarily a bug. More recommendations for GNU packages can be found
in *note Makefile Conventions: (standards)Makefile Conventions.
The `configure' shell script attempts to guess correct values for
various system-dependent variables used during compilation. It uses
those values to create a `Makefile' in each directory of the package.
It may also create one or more `.h' files containing system-dependent
definitions. Finally, it creates a shell script `config.status' that
you can run in the future to recreate the current configuration, and a
file `config.log' containing compiler output (useful mainly for
debugging `configure').
It can also use an optional file (typically called `config.cache'
and enabled with `--cache-file=config.cache' or simply `-C') that saves
the results of its tests to speed up reconfiguring. Caching is
disabled by default to prevent problems with accidental use of stale
cache files.
If you need to do unusual things to compile the package, please try
to figure out how `configure' could check whether to do them, and mail
diffs or instructions to the address given in the `README' so they can
be considered for the next release. If you are using the cache, and at
some point `config.cache' contains results you don't want to keep, you
may remove or edit it.
The file `configure.ac' (or `configure.in') is used to create
`configure' by a program called `autoconf'. You need `configure.ac' if
you want to change it or regenerate `configure' using a newer version
of `autoconf'.
The simplest way to compile this package is:
1. `cd' to the directory containing the package's source code and type
`./configure' to configure the package for your system.
Running `configure' might take a while. While running, it prints
some messages telling which features it is checking for.
2. Type `make' to compile the package.
3. Optionally, type `make check' to run any self-tests that come with
the package, generally using the just-built uninstalled binaries.
4. Type `make install' to install the programs and any data files and
documentation. When installing into a prefix owned by root, it is
recommended that the package be configured and built as a regular
user, and only the `make install' phase executed with root
privileges.
5. Optionally, type `make installcheck' to repeat any self-tests, but
this time using the binaries in their final installed location.
This target does not install anything. Running this target as a
regular user, particularly if the prior `make install' required
root privileges, verifies that the installation completed
correctly.
6. You can remove the program binaries and object files from the
source code directory by typing `make clean'. To also remove the
files that `configure' created (so you can compile the package for
a different kind of computer), type `make distclean'. There is
also a `make maintainer-clean' target, but that is intended mainly
for the package's developers. If you use it, you may have to get
all sorts of other programs in order to regenerate files that came
with the distribution.
7. Often, you can also type `make uninstall' to remove the installed
files again. In practice, not all packages have tested that
uninstallation works correctly, even though it is required by the
GNU Coding Standards.
8. Some packages, particularly those that use Automake, provide `make
distcheck', which can by used by developers to test that all other
targets like `make install' and `make uninstall' work correctly.
This target is generally not run by end users.
Compilers and Options
=====================
Some systems require unusual options for compilation or linking that
the `configure' script does not know about. Run `./configure --help'
for details on some of the pertinent environment variables.
You can give `configure' initial values for configuration parameters
by setting variables in the command line or in the environment. Here
is an example:
./configure CC=c99 CFLAGS=-g LIBS=-lposix
*Note Defining Variables::, for more details.
Compiling For Multiple Architectures
====================================
You can compile the package for more than one kind of computer at the
same time, by placing the object files for each architecture in their
own directory. To do this, you can use GNU `make'. `cd' to the
directory where you want the object files and executables to go and run
the `configure' script. `configure' automatically checks for the
source code in the directory that `configure' is in and in `..'. This
is known as a "VPATH" build.
With a non-GNU `make', it is safer to compile the package for one
architecture at a time in the source code directory. After you have
installed the package for one architecture, use `make distclean' before
reconfiguring for another architecture.
On MacOS X 10.5 and later systems, you can create libraries and
executables that work on multiple system types--known as "fat" or
"universal" binaries--by specifying multiple `-arch' options to the
compiler but only a single `-arch' option to the preprocessor. Like
this:
./configure CC="gcc -arch i386 -arch x86_64 -arch ppc -arch ppc64" \
CXX="g++ -arch i386 -arch x86_64 -arch ppc -arch ppc64" \
CPP="gcc -E" CXXCPP="g++ -E"
This is not guaranteed to produce working output in all cases, you
may have to build one architecture at a time and combine the results
using the `lipo' tool if you have problems.
Installation Names
==================
By default, `make install' installs the package's commands under
`/usr/local/bin', include files under `/usr/local/include', etc. You
can specify an installation prefix other than `/usr/local' by giving
`configure' the option `--prefix=PREFIX', where PREFIX must be an
absolute file name.
You can specify separate installation prefixes for
architecture-specific files and architecture-independent files. If you
pass the option `--exec-prefix=PREFIX' to `configure', the package uses
PREFIX as the prefix for installing programs and libraries.
Documentation and other data files still use the regular prefix.
In addition, if you use an unusual directory layout you can give
options like `--bindir=DIR' to specify different values for particular
kinds of files. Run `configure --help' for a list of the directories
you can set and what kinds of files go in them. In general, the
default for these options is expressed in terms of `${prefix}', so that
specifying just `--prefix' will affect all of the other directory
specifications that were not explicitly provided.
The most portable way to affect installation locations is to pass the
correct locations to `configure'; however, many packages provide one or
both of the following shortcuts of passing variable assignments to the
`make install' command line to change installation locations without
having to reconfigure or recompile.
The first method involves providing an override variable for each
affected directory. For example, `make install
prefix=/alternate/directory' will choose an alternate location for all
directory configuration variables that were expressed in terms of
`${prefix}'. Any directories that were specified during `configure',
but not in terms of `${prefix}', must each be overridden at install
time for the entire installation to be relocated. The approach of
makefile variable overrides for each directory variable is required by
the GNU Coding Standards, and ideally causes no recompilation.
However, some platforms have known limitations with the semantics of
shared libraries that end up requiring recompilation when using this
method, particularly noticeable in packages that use GNU Libtool.
The second method involves providing the `DESTDIR' variable. For
example, `make install DESTDIR=/alternate/directory' will prepend
`/alternate/directory' before all installation names. The approach of
`DESTDIR' overrides is not required by the GNU Coding Standards, and
does not work on platforms that have drive letters. On the other hand,
it does better at avoiding recompilation issues, and works well even
when some directory options were not specified in terms of `${prefix}'
at `configure' time.
Optional Features
=================
If the package supports it, you can cause programs to be installed
with an extra prefix or suffix on their names by giving `configure' the
option `--program-prefix=PREFIX' or `--program-suffix=SUFFIX'.
Some packages pay attention to `--enable-FEATURE' options to
`configure', where FEATURE indicates an optional part of the package.
They may also pay attention to `--with-PACKAGE' options, where PACKAGE
is something like `gnu-as' or `x' (for the X Window System). The
`README' should mention any `--enable-' and `--with-' options that the
package recognizes.
For packages that use the X Window System, `configure' can usually
find the X include and library files automatically, but if it doesn't,
you can use the `configure' options `--x-includes=DIR' and
`--x-libraries=DIR' to specify their locations.
Some packages offer the ability to configure how verbose the
execution of `make' will be. For these packages, running `./configure
--enable-silent-rules' sets the default to minimal output, which can be
overridden with `make V=1'; while running `./configure
--disable-silent-rules' sets the default to verbose, which can be
overridden with `make V=0'.
Particular systems
==================
On HP-UX, the default C compiler is not ANSI C compatible. If GNU
CC is not installed, it is recommended to use the following options in
order to use an ANSI C compiler:
./configure CC="cc -Ae -D_XOPEN_SOURCE=500"
and if that doesn't work, install pre-built binaries of GCC for HP-UX.
On OSF/1 a.k.a. Tru64, some versions of the default C compiler cannot
parse its `<wchar.h>' header file. The option `-nodtk' can be used as
a workaround. If GNU CC is not installed, it is therefore recommended
to try
./configure CC="cc"
and if that doesn't work, try
./configure CC="cc -nodtk"
On Solaris, don't put `/usr/ucb' early in your `PATH'. This
directory contains several dysfunctional programs; working variants of
these programs are available in `/usr/bin'. So, if you need `/usr/ucb'
in your `PATH', put it _after_ `/usr/bin'.
On Haiku, software installed for all users goes in `/boot/common',
not `/usr/local'. It is recommended to use the following options:
./configure --prefix=/boot/common
Specifying the System Type
==========================
There may be some features `configure' cannot figure out
automatically, but needs to determine by the type of machine the package
will run on. Usually, assuming the package is built to be run on the
_same_ architectures, `configure' can figure that out, but if it prints
a message saying it cannot guess the machine type, give it the
`--build=TYPE' option. TYPE can either be a short name for the system
type, such as `sun4', or a canonical name which has the form:
CPU-COMPANY-SYSTEM
where SYSTEM can have one of these forms:
OS
KERNEL-OS
See the file `config.sub' for the possible values of each field. If
`config.sub' isn't included in this package, then this package doesn't
need to know the machine type.
If you are _building_ compiler tools for cross-compiling, you should
use the option `--target=TYPE' to select the type of system they will
produce code for.
If you want to _use_ a cross compiler, that generates code for a
platform different from the build platform, you should specify the
"host" platform (i.e., that on which the generated programs will
eventually be run) with `--host=TYPE'.
Sharing Defaults
================
If you want to set default values for `configure' scripts to share,
you can create a site shell script called `config.site' that gives
default values for variables like `CC', `cache_file', and `prefix'.
`configure' looks for `PREFIX/share/config.site' if it exists, then
`PREFIX/etc/config.site' if it exists. Or, you can set the
`CONFIG_SITE' environment variable to the location of the site script.
A warning: not all `configure' scripts look for a site script.
Defining Variables
==================
Variables not defined in a site shell script can be set in the
environment passed to `configure'. However, some packages may run
configure again during the build, and the customized values of these
variables may be lost. In order to avoid this problem, you should set
them in the `configure' command line, using `VAR=value'. For example:
./configure CC=/usr/local2/bin/gcc
causes the specified `gcc' to be used as the C compiler (unless it is
overridden in the site shell script).
Unfortunately, this technique does not work for `CONFIG_SHELL' due to
an Autoconf bug. Until the bug is fixed you can use this workaround:
CONFIG_SHELL=/bin/bash /bin/bash ./configure CONFIG_SHELL=/bin/bash
`configure' Invocation
======================
`configure' recognizes the following options to control how it
operates.
`--help'
`-h'
Print a summary of all of the options to `configure', and exit.
`--help=short'
`--help=recursive'
Print a summary of the options unique to this package's
`configure', and exit. The `short' variant lists options used
only in the top level, while the `recursive' variant lists options
also present in any nested packages.
`--version'
`-V'
Print the version of Autoconf used to generate the `configure'
script, and exit.
`--cache-file=FILE'
Enable the cache: use and save the results of the tests in FILE,
traditionally `config.cache'. FILE defaults to `/dev/null' to
disable caching.
`--config-cache'
`-C'
Alias for `--cache-file=config.cache'.
`--quiet'
`--silent'
`-q'
Do not print messages saying which checks are being made. To
suppress all normal output, redirect it to `/dev/null' (any error
messages will still be shown).
`--srcdir=DIR'
Look for the package's source code in directory DIR. Usually
`configure' can determine that directory automatically.
`--prefix=DIR'
Use DIR as the installation prefix. *note Installation Names::
for more details, including other options available for fine-tuning
the installation locations.
`--no-create'
`-n'
Run the configure checks, but stop before creating any output
files.
`configure' also accepts some other, not widely useful, options. Run
`configure --help' for more details.
-20
View File
@@ -1,20 +0,0 @@
if USE_SYSTEM_SHARED_LIB
SUBDIRS = src
else
SUBDIRS = libcork libipset libbloom src
endif
if ENABLE_DOCUMENTATION
SUBDIRS += doc
endif
ACLOCAL_AMFLAGS = -I m4
pkgconfiglibdir = $(libdir)/pkgconfig
pkgconfiglib_DATA = shadowsocks-libev.pc
EXTRA_DIST = acl Changes completions debian docker rpm scripts README.md
EXTRA_DIST += libbloom
EXTRA_DIST += libcork/include libipset/include
EXTRA_DIST += libipset/src/libipset/map/inspection-template.c.in
EXTRA_DIST += libipset/src/libipset/set/inspection-template.c.in
+21 -11
View File
@@ -79,8 +79,8 @@ git submodule update --init --recursive
### Pre-build configure guide
For a complete list of available configure-time option,
try `configure --help`.
For a complete list of available configure-time options,
try `cmake -LH` from a build directory.
### Debian & Ubuntu
@@ -203,12 +203,12 @@ nix-env -iA nixpkgs.shadowsocks-libev
In general, you need the following build dependencies:
* autotools (autoconf, automake, libtool)
* gettext
* cmake (>= 3.2)
* a C compiler (gcc or clang)
* pkg-config
* libmbedtls
* libsodium
* libpcre3 (old pcre library)
* libsodium (>= 1.0.4)
* libpcre2
* libev
* libc-ares
* asciidoc (for documentation only)
@@ -218,18 +218,18 @@ Notes: Fedora 26 libsodium version >= 1.0.12, so you can install via dnf instal
If your system is too old to provide libmbedtls and libsodium (later than **v1.0.8**), you will need to either install those libraries manually or upgrade your system.
If your system provides with those libraries, you **should not** install them from source.You should jump to this section and install them from the distribution repository instead.
If your system provides with those libraries, you **should not** install them from source. You should jump to this section and install them from the distribution repository instead.
For some of the distributions, you might install build dependencies like this:
```bash
# Installation of basic build dependencies
## Debian / Ubuntu
sudo apt-get install --no-install-recommends gettext build-essential autoconf libtool libpcre3-dev asciidoc xmlto libev-dev libc-ares-dev automake libmbedtls-dev libsodium-dev pkg-config
sudo apt-get install --no-install-recommends build-essential cmake libpcre2-dev asciidoc xmlto libev-dev libc-ares-dev libmbedtls-dev libsodium-dev pkg-config
## CentOS / Fedora / RHEL
sudo yum install gettext gcc autoconf libtool automake make asciidoc xmlto c-ares-devel libev-devel
sudo yum install gcc cmake make asciidoc xmlto c-ares-devel libev-devel
## Arch
sudo pacman -S gettext gcc autoconf libtool automake make asciidoc xmlto c-ares libev
sudo pacman -S gcc cmake make asciidoc xmlto c-ares libev
# Installation of libsodium
export LIBSODIUM_VER=1.0.16
@@ -252,10 +252,20 @@ popd
sudo ldconfig
# Start building
./autogen.sh && ./configure && make
git submodule update --init --recursive
mkdir -p build && cd build
cmake ..
make
sudo make install
```
To run unit tests:
```bash
cd build
ctest --output-on-failure
```
You may need to manually install missing softwares.
### FreeBSD
-3
View File
@@ -1,3 +0,0 @@
#!/bin/sh
autoreconf --install --force
+44
View File
@@ -0,0 +1,44 @@
# FindCares.cmake - Find c-ares library
#
# Sets:
# CARES_FOUND
# CARES_INCLUDE_DIRS
# CARES_LIBRARIES
find_path(CARES_INCLUDE_DIR
NAMES ares.h
HINTS
/opt/homebrew/include
/usr/local/include
/opt/homebrew/opt/c-ares/include
/usr/local/opt/c-ares/include
/usr/include
)
find_library(CARES_LIBRARY
NAMES cares
HINTS
/opt/homebrew/lib
/usr/local/lib
/opt/homebrew/opt/c-ares/lib
/usr/local/opt/c-ares/lib
/usr/lib
)
if(CARES_INCLUDE_DIR AND CARES_LIBRARY)
set(CARES_FOUND TRUE)
set(CARES_INCLUDE_DIRS ${CARES_INCLUDE_DIR})
set(CARES_LIBRARIES ${CARES_LIBRARY})
# Verify ares_library_init exists
include(CheckLibraryExists)
check_library_exists(cares ares_library_init "" CARES_HAS_INIT)
if(NOT CARES_HAS_INIT)
message(WARNING "c-ares found but ares_library_init not detected. Proceeding anyway.")
endif()
message(STATUS "Found c-ares: ${CARES_LIBRARY}")
else()
set(CARES_FOUND FALSE)
message(FATAL_ERROR "Could not find c-ares library. Install libc-ares-dev or equivalent.")
endif()
+88
View File
@@ -0,0 +1,88 @@
# FindMbedTLS.cmake - Find mbedTLS library with feature detection
#
# Sets:
# MBEDTLS_FOUND
# MBEDTLS_INCLUDE_DIRS
# MBEDTLS_CRYPTO_LIBRARY
# MBEDTLS_TLS_LIBRARY
include(CheckCSourceCompiles)
# mbedtls@3 is keg-only on Homebrew; also check versioned opt paths
find_path(MBEDTLS_INCLUDE_DIR
NAMES mbedtls/cipher.h
HINTS
/opt/homebrew/opt/mbedtls@3/include
/usr/local/opt/mbedtls@3/include
/opt/homebrew/opt/mbedtls/include
/usr/local/opt/mbedtls/include
/opt/homebrew/include
/usr/local/include
/usr/include
)
find_library(MBEDTLS_CRYPTO_LIBRARY
NAMES mbedcrypto
HINTS
/opt/homebrew/opt/mbedtls@3/lib
/usr/local/opt/mbedtls@3/lib
/opt/homebrew/opt/mbedtls/lib
/usr/local/opt/mbedtls/lib
/opt/homebrew/lib
/usr/local/lib
/usr/lib
)
find_library(MBEDTLS_TLS_LIBRARY
NAMES mbedtls
HINTS
/opt/homebrew/opt/mbedtls@3/lib
/usr/local/opt/mbedtls@3/lib
/opt/homebrew/opt/mbedtls/lib
/usr/local/opt/mbedtls/lib
/opt/homebrew/lib
/usr/local/lib
/usr/lib
)
if(MBEDTLS_INCLUDE_DIR AND MBEDTLS_CRYPTO_LIBRARY)
set(MBEDTLS_FOUND TRUE)
set(MBEDTLS_INCLUDE_DIRS ${MBEDTLS_INCLUDE_DIR})
# Check for required CFB mode support
set(CMAKE_REQUIRED_INCLUDES ${MBEDTLS_INCLUDE_DIR})
set(CMAKE_REQUIRED_LIBRARIES ${MBEDTLS_CRYPTO_LIBRARY})
check_c_source_compiles("
#include <mbedtls/cipher.h>
#if !defined(MBEDTLS_CIPHER_MODE_CFB)
#error CFB mode not supported
#endif
int main(void) { return 0; }
" MBEDTLS_HAS_CFB)
if(NOT MBEDTLS_HAS_CFB)
# Try mbedtls 3.x config path
check_c_source_compiles("
#include <mbedtls/build_info.h>
#include <mbedtls/cipher.h>
#if !defined(MBEDTLS_CIPHER_MODE_CFB)
#error CFB mode not supported
#endif
int main(void) { return 0; }
" MBEDTLS_HAS_CFB_V3)
if(NOT MBEDTLS_HAS_CFB_V3)
message(FATAL_ERROR "mbedTLS found but MBEDTLS_CIPHER_MODE_CFB is not enabled. "
"Please enable CFB mode in your mbedTLS configuration.")
endif()
endif()
unset(CMAKE_REQUIRED_INCLUDES)
unset(CMAKE_REQUIRED_LIBRARIES)
message(STATUS "Found mbedTLS: ${MBEDTLS_CRYPTO_LIBRARY}")
else()
set(MBEDTLS_FOUND FALSE)
message(FATAL_ERROR "Could not find mbedTLS library. Install libmbedtls-dev or equivalent.")
endif()
+63
View File
@@ -0,0 +1,63 @@
# FindPCRE2.cmake - Find PCRE2 library (8-bit)
#
# Sets:
# PCRE2_FOUND
# PCRE2_INCLUDE_DIRS
# PCRE2_LIBRARIES
include(FindPkgConfig)
if(PKG_CONFIG_FOUND)
pkg_check_modules(_PCRE2 QUIET libpcre2-8)
endif()
if(_PCRE2_FOUND)
set(PCRE2_INCLUDE_DIRS ${_PCRE2_INCLUDE_DIRS})
set(PCRE2_LIBRARIES ${_PCRE2_LIBRARIES})
set(PCRE2_FOUND TRUE)
else()
# Try pcre2-config
find_program(PCRE2_CONFIG pcre2-config)
if(PCRE2_CONFIG)
execute_process(COMMAND ${PCRE2_CONFIG} --cflags
OUTPUT_VARIABLE PCRE2_CFLAGS
OUTPUT_STRIP_TRAILING_WHITESPACE)
execute_process(COMMAND ${PCRE2_CONFIG} --libs8
OUTPUT_VARIABLE PCRE2_LDFLAGS
OUTPUT_STRIP_TRAILING_WHITESPACE)
string(REGEX REPLACE "-I" "" PCRE2_INCLUDE_DIRS "${PCRE2_CFLAGS}")
set(PCRE2_LIBRARIES ${PCRE2_LDFLAGS})
set(PCRE2_FOUND TRUE)
else()
# Manual search
find_path(PCRE2_INCLUDE_DIR
NAMES pcre2.h
HINTS
/opt/homebrew/include
/usr/local/include
/usr/include
)
find_library(PCRE2_LIBRARY
NAMES pcre2-8
HINTS
/opt/homebrew/lib
/usr/local/lib
/usr/lib
)
if(PCRE2_INCLUDE_DIR AND PCRE2_LIBRARY)
set(PCRE2_INCLUDE_DIRS ${PCRE2_INCLUDE_DIR})
set(PCRE2_LIBRARIES ${PCRE2_LIBRARY})
set(PCRE2_FOUND TRUE)
else()
set(PCRE2_FOUND FALSE)
endif()
endif()
endif()
if(PCRE2_FOUND)
message(STATUS "Found PCRE2: ${PCRE2_LIBRARIES}")
else()
message(FATAL_ERROR "Could not find PCRE2 library. Install libpcre2-dev or equivalent.")
endif()
+57
View File
@@ -0,0 +1,57 @@
# FindSodium.cmake - Find libsodium with version check
#
# Sets:
# SODIUM_FOUND
# SODIUM_INCLUDE_DIRS
# SODIUM_LIBRARIES
find_path(SODIUM_INCLUDE_DIR
NAMES sodium.h
HINTS
/opt/homebrew/include
/usr/local/include
/usr/local/opt/libsodium/include
/opt/homebrew/opt/libsodium/include
/usr/include
$ENV{LIBSODIUM_INCLUDE_DIR}
$ENV{LIBSODIUM_DIR}/include
)
find_library(SODIUM_LIBRARY
NAMES sodium
HINTS
/opt/homebrew/lib
/usr/local/lib
/usr/local/opt/libsodium/lib
/opt/homebrew/opt/libsodium/lib
/usr/lib
)
if(SODIUM_INCLUDE_DIR AND SODIUM_LIBRARY)
set(SODIUM_FOUND TRUE)
set(SODIUM_INCLUDE_DIRS ${SODIUM_INCLUDE_DIR})
set(SODIUM_LIBRARIES ${SODIUM_LIBRARY})
# Version check: require SODIUM_LIBRARY_VERSION_MAJOR >= 7 (libsodium >= 1.0.4)
include(CheckCSourceCompiles)
set(CMAKE_REQUIRED_INCLUDES ${SODIUM_INCLUDE_DIR})
set(CMAKE_REQUIRED_LIBRARIES ${SODIUM_LIBRARY})
check_c_source_compiles("
#include <sodium.h>
#if SODIUM_LIBRARY_VERSION_MAJOR < 7
#error libsodium too old
#endif
int main(void) { return 0; }
" SODIUM_VERSION_OK)
unset(CMAKE_REQUIRED_INCLUDES)
unset(CMAKE_REQUIRED_LIBRARIES)
if(NOT SODIUM_VERSION_OK)
message(FATAL_ERROR "libsodium found but version is too old. Require >= 1.0.4 (SODIUM_LIBRARY_VERSION_MAJOR >= 7)")
endif()
message(STATUS "Found libsodium: ${SODIUM_LIBRARY}")
else()
set(SODIUM_FOUND FALSE)
message(FATAL_ERROR "Could not find libsodium. Install libsodium-dev or equivalent.")
endif()
+17 -5
View File
@@ -87,11 +87,8 @@
/* Define to 1 if you have the <net/if.h> header file. */
#cmakedefine HAVE_NET_IF_H 1
/* Define to 1 if you have the <pcre.h> header file. */
#cmakedefine HAVE_PCRE_H 1
/* Define to 1 if you have the <pcre/pcre.h> header file. */
#cmakedefine HAVE_PCRE_PCRE_H 1
/* Define to 1 if you have the <pcre2.h> header file. */
#cmakedefine HAVE_PCRE2_H 1
/* Have PTHREAD_PRIO_INHERIT. */
#cmakedefine HAVE_PTHREAD_PRIO_INHERIT 1
@@ -236,6 +233,21 @@
#endif
/* Enable support for QOS netfilter mark preservation */
#cmakedefine USE_NFCONNTRACK_TOS 1
/* Enable support for nftables firewall */
#cmakedefine USE_NFTABLES 1
/* Define to 1 if you have the <linux/random.h> header file. */
#cmakedefine HAVE_LINUX_RANDOM_H 1
/* Define to 1 if you have the `get_current_dir_name' function. */
#cmakedefine HAVE_GET_CURRENT_DIR_NAME 1
/* Define to 1 if you have the `posix_memalign' function. */
#cmakedefine HAVE_POSIX_MEMALIGN 1
/* Define if use system shared lib. */
#cmakedefine USE_SYSTEM_SHARED_LIB 1
+33 -34
View File
@@ -2,9 +2,6 @@
# -------------------------------------------------------------
# config.h
# If we generate config.h by automake
#include_directories(.)
# Use cmake to generate config.h
include(CheckIncludeFiles)
include(CheckFunctionExists)
@@ -12,14 +9,20 @@ include(CheckSymbolExists)
include(CheckLibraryExists)
include(CheckTypeSize)
include(CheckCSourceCompiles)
include(CheckCCompilerFlag)
# Define if building universal (internal helper macro)
# AC_APPLE_UNIVERSAL_BUILD
set(CONNECT_IN_PROGRESS "EINPROGRESS")
set(CONNECT_IN_PROGRESS "EINPROGRESS" CACHE STRING "")
# Set CONNECT_IN_PROGRESS based on platform
if(MINGW)
set(CONNECT_IN_PROGRESS "WSAEWOULDBLOCK")
else()
set(CONNECT_IN_PROGRESS "EINPROGRESS")
endif()
if (CMAKE_SYSTEM_NAME STREQUAL Darwin)
set(CMAKE_REQUIRED_INCLUDES "/usr/local/include" "/usr/include")
set(CMAKE_REQUIRED_INCLUDES "/usr/local/include" "/usr/include" "/opt/homebrew/include")
endif ()
check_include_files(dlfcn.h HAVE_DLFCN_H)
@@ -53,8 +56,10 @@ else ()
endif ()
check_include_files(linux/tcp.h HAVE_LINUX_TCP_H)
check_include_files(net/if.h HAVE_NET_IF_H)
check_include_files(pcre.h HAVE_PCRE_H)
check_include_files(pcre/pcre.h HAVE_PCRE_PCRE_H)
set(CMAKE_REQUIRED_DEFINITIONS_SAVE ${CMAKE_REQUIRED_DEFINITIONS})
set(CMAKE_REQUIRED_DEFINITIONS "-DPCRE2_CODE_UNIT_WIDTH=8")
check_include_files(pcre2.h HAVE_PCRE2_H)
set(CMAKE_REQUIRED_DEFINITIONS ${CMAKE_REQUIRED_DEFINITIONS_SAVE})
check_symbol_exists(PTHREAD_PRIO_INHERIT pthread.h HAVE_PTHREAD_PRIO_INHERIT)
check_function_exists(select HAVE_SELECT)
@@ -78,6 +83,8 @@ check_include_files(sys/types.h HAVE_SYS_TYPES_H)
check_include_files(sys/wait.h HAVE_SYS_WAIT_H)
check_include_files(ares.h HAVE_ARES_H)
check_include_files(unistd.h HAVE_UNISTD_H)
check_include_files(arpa/inet.h HAVE_ARPA_INET_H)
check_include_files(linux/random.h HAVE_LINUX_RANDOM_H)
check_function_exists(fork HAVE_FORK)
check_function_exists(vfork HAVE_VFORK)
@@ -89,6 +96,9 @@ if (HAVE_FORK)
set(HAVE_WORKING_FORK 1)
endif ()
# Additional function checks
check_function_exists(get_current_dir_name HAVE_GET_CURRENT_DIR_NAME)
check_function_exists(posix_memalign HAVE_POSIX_MEMALIGN)
# Define to the sub-directory where libtool stores uninstalled libraries.
set(LT_OBJDIR ".libs/")
@@ -101,8 +111,6 @@ set(PACKAGE_STRING "${PROJECT_NAME} ${PACKAGE_VERSION}")
set(PACKAGE_TARNAME ${PROJECT_NAME})
set(PACKAGE_URL "")
#message(${PACKAGE_NAME} - v${PACKAGE_VERSION} - v${PROJECT_VERSION})
# PTHREAD_CREATE_JOINABLE
# Define as the return type of signal handlers (`int' or `void').
@@ -140,31 +148,7 @@ set(_TANDEM_SOURCE 1)
set(__EXTENSIONS__ 1)
# USE_SYSTEM_SHARED_LIB
set(VERSION ${PACKAGE_VERSION})
# TODO WORDS_BIGENDIAN
# _MINIX
# _POSIX_1_SOURCE
# _POSIX_SOURCE
# _UINT8_T
# Define to empty if `const' does not conform to ANSI C.
# undef const
# Define to `__inline__' or `__inline' if that's what the C compiler
# calls it, or to nothing if 'inline' is not supported under any name.
#ifndef __cplusplus
#undef inline
#endif
# TODO Assume we got inline support
# https://cmake.org/Wiki/CMakeTestInline
# Define to `int' if <sys/types.h> does not define.
# undef pid_t
# Define to the type of an unsigned integer type of width exactly 16 bits if
# such a type exists and the standard includes do not define it.
# undef uint16_t
# Define to the type of an unsigned integer type of width exactly 8 bits if
# such a type exists and the standard includes do not define it.
# undef uint8_t
set(CMAKE_EXTRA_INCLUDE_FILES sys/types.h)
check_type_size(pid_t PID_T)
check_type_size(size_t SIZE_T)
@@ -197,3 +181,18 @@ endif ()
if (NOT HAVE_WORKING_VFORK)
set(vfork fork)
endif ()
# Stack protector detection
option(DISABLE_SSP "Disable -fstack-protector" OFF)
if(NOT DISABLE_SSP)
check_c_compiler_flag(-fstack-protector HAS_STACK_PROTECTOR)
if(HAS_STACK_PROTECTOR)
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -fstack-protector")
message(STATUS "Stack protector enabled")
endif()
endif()
# MinGW/Cygwin compiler flags
if(MINGW OR CYGWIN)
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -mno-ms-bitfields")
endif()
@@ -1,9 +1,7 @@
prefix=@prefix@
exec_prefix=${prefix}/@CMAKE_INSTALL_BINDIR@
libdir=${exec_prefix}/@CMAKE_INSTALL_FULL_LIBDIR@
prefix=@CMAKE_INSTALL_PREFIX@
exec_prefix=${prefix}
libdir=${prefix}/@CMAKE_INSTALL_LIBDIR@
includedir=${prefix}/@CMAKE_INSTALL_INCLUDEDIR@
sharedir=${prefix}/@CMAKE_INSTALL_DATAROOTDIR@
mandir=${prefix}/@CMAKE_INSTALL_MANDIR@
Name: @PROJECT_NAME@
Description: @PROJECT_DESC@
@@ -11,4 +9,4 @@ URL: @PROJECT_URL@
Version: @PROJECT_VERSION@
Requires:
Cflags: -I${includedir}
Libs: -L${libdir} -lshadowsocks-libev -lcrypto
Libs: -L${libdir} -lshadowsocks-libev
-311
View File
@@ -1,311 +0,0 @@
dnl -*- Autoconf -*-
dnl Process this file with autoconf to produce a configure script.
AC_PREREQ([2.67])
AC_INIT([shadowsocks-libev], [3.3.5], [max.c.lv@gmail.com])
AC_CONFIG_SRCDIR([src/crypto.c])
AC_CONFIG_HEADERS([config.h])
AC_CONFIG_AUX_DIR(auto)
AC_CONFIG_MACRO_DIR([m4])
AC_USE_SYSTEM_EXTENSIONS
AM_INIT_AUTOMAKE([subdir-objects foreign -Wno-gnu -Werror])
m4_ifdef([AM_PROG_AR], [AM_PROG_AR])
m4_ifdef([AM_SILENT_RULES], [AM_SILENT_RULES([yes])])
AM_MAINTAINER_MODE
AM_DEP_TRACK
dnl Checks for lib
AC_DISABLE_STATIC
AC_DISABLE_SHARED
LT_INIT([dlopen])
dnl Check for pcre library
TS_CHECK_PCRE
if test "x${enable_pcre}" != "xyes"; then
AC_MSG_ERROR([Cannot find pcre library. Configure --with-pcre=DIR])
fi
dnl Checks for using shared libraries from system
AC_ARG_ENABLE(
[system-shared-lib],
AS_HELP_STRING([--enable-system-shared-lib], [build against shared libraries when possible]),
[
case "${enableval}" in
yes) enable_system_shared_lib=true ;;
no) enable_system_shared_lib=false ;;
*) AC_MSG_ERROR([bad value ${enableval} for --enable-system-shared-lib]) ;;
esac], [enable_system_shared_lib=false])
AM_CONDITIONAL([USE_SYSTEM_SHARED_LIB], [test x$enable_system_shared_lib = xtrue])
AC_ARG_ENABLE([documentation],
AS_HELP_STRING([--disable-documentation], [do not build documentation]),
[disable_documentation=true],
[disable_documentation=false])
AM_CONDITIONAL([ENABLE_DOCUMENTATION], [test x$disable_documentation = xfalse])
AM_COND_IF([ENABLE_DOCUMENTATION], [
AC_PATH_PROG([ASCIIDOC], [asciidoc])
test x"${ASCIIDOC}" != x || AC_MSG_ERROR([Cannot find `asciidoc` in PATH.])
AC_PATH_PROG([XMLTO], [xmlto])
test x"$XMLTO" != x || AC_MSG_ERROR([Cannot find `xmlto` in PATH.])
AC_PATH_PROG([GZIP], [gzip], [gzip])
AC_PATH_PROG([MV], [mv], [mv])
AC_PROG_SED
])
dnl Checks for programs.
AC_PROG_CC
AM_PROG_CC_C_O
AC_PROG_INSTALL
AC_PROG_LN_S
AC_PROG_MAKE_SET
AC_LANG_SOURCE
dnl Add library for mingw
case $host in
*-mingw*)
CFLAGS="$CFLAGS -mno-ms-bitfields"
LIBS="$LIBS -lws2_32 -liphlpapi"
;;
*-cygwin*)
CFLAGS="$CFLAGS -mno-ms-bitfields"
;;
*)
;;
esac
dnl Checks for TLS
AX_TLS([:], [:])
dnl Checks for crypto libraries
ss_MBEDTLS
ss_SODIUM
ss_CARES
dnl Checks for inet_ntop
ss_FUNC_INET_NTOP
dnl Checks for host.
AC_MSG_CHECKING(for what kind of host)
case $host in
*-linux*)
os_support=linux
AC_MSG_RESULT(Linux)
;;
*-mingw*)
os_support=mingw
AC_MSG_RESULT(MinGW)
;;
*)
AC_MSG_RESULT(transparent proxy does not support for $host)
;;
esac
dnl Checks for pthread
AX_PTHREAD([LIBS="$PTHREAD_LIBS $LIBS"
CFLAGS="$CFLAGS $PTHREAD_CFLAGS"
CC="$PTHREAD_CC"], AC_MSG_ERROR(Can not find pthreads. This is required.))
dnl Checks for stack protector
GGL_CHECK_STACK_PROTECTOR([has_stack_protector=yes], [has_stack_protector=no])
# XXX - disable -fstack-protector due to missing libssp_nonshared
case "$host_os" in
*aix*)
AC_MSG_NOTICE([-fstack-protector disabled on AIX])
has_stack_protector=no
;;
*sunos*)
AC_MSG_NOTICE([-fstack-protector disabled on SunOS])
has_stack_protector=no
;;
*solaris*)
AC_MSG_NOTICE([-fstack-protector disabled on Solaris])
has_stack_protector=no
;;
esac
AC_ARG_ENABLE(ssp,
[AS_HELP_STRING(--disable-ssp,Do not compile with -fstack-protector)],
[
enable_ssp="no"
],
[
enable_ssp="yes"
])
if test x$has_stack_protector = xyes && test x$enable_ssp = xyes; then
CFLAGS="$CFLAGS -fstack-protector"
AC_MSG_NOTICE([-fstack-protector enabled in CFLAGS])
fi
AM_CONDITIONAL(BUILD_REDIRECTOR, test "$os_support" = "linux")
AM_CONDITIONAL(BUILD_WINCOMPAT, test "$os_support" = "mingw")
dnl Checks for header files.
AC_CHECK_HEADERS([limits.h stdint.h inttypes.h arpa/inet.h fcntl.h langinfo.h locale.h linux/tcp.h netinet/tcp.h netdb.h netinet/in.h stdlib.h string.h strings.h unistd.h sys/ioctl.h linux/random.h])
dnl A special check required for <net/if.h> on Darwin. See
dnl http://www.gnu.org/software/autoconf/manual/html_node/Header-Portability.html.
AC_CHECK_HEADERS([sys/socket.h])
AC_CHECK_HEADERS([net/if.h], [], [],
[
#include <stdio.h>
#ifdef STDC_HEADERS
# include <stdlib.h>
# include <stddef.h>
#else
# ifdef HAVE_STDLIB_H
# include <stdlib.h>
# endif
#endif
#ifdef HAVE_SYS_SOCKET_H
# include <sys/socket.h>
#endif
])
case $host in
*-mingw*)
AC_DEFINE([CONNECT_IN_PROGRESS], [WSAEWOULDBLOCK], [errno for incomplete non-blocking connect(2)])
AC_CHECK_HEADERS([winsock2.h ws2tcpip.h mswsock.h], [], [AC_MSG_ERROR([Missing MinGW headers])], [])
;;
*-linux*)
AC_DEFINE([CONNECT_IN_PROGRESS], [EINPROGRESS], [errno for incomplete non-blocking connect(2)])
dnl Checks for netfilter headers
AC_CHECK_HEADERS([linux/if.h linux/netfilter_ipv4.h linux/netfilter_ipv6/ip6_tables.h],
[], [AC_MSG_ERROR([Missing netfilter headers])],
[[
#if HAVE_LIMITS_H
#include <limits.h>
#endif
/* Netfilter ip(6)tables v1.4.0 has broken headers */
#if HAVE_NETINET_IN_H
#include <netinet/in.h>
#endif
#if HAVE_LINUX_IF_H
#include <linux/if.h>
#endif
#if HAVE_SYS_SOCKET_H
#include <sys/socket.h>
#endif
]])
;;
*)
# These are POSIX-like systems using BSD-like sockets API.
AC_DEFINE([CONNECT_IN_PROGRESS], [EINPROGRESS], [errno for incomplete non-blocking connect(2)])
;;
esac
AC_C_BIGENDIAN
dnl Checks for typedefs, structures, and compiler characteristics.
AC_C_INLINE
AC_TYPE_SSIZE_T
dnl Checks for header files.
AC_HEADER_ASSERT
AC_HEADER_STDC
AC_HEADER_SYS_WAIT
dnl Checks for typedefs, structures, and compiler characteristics.
AC_C_CONST
AC_TYPE_PID_T
AC_TYPE_SIZE_T
AC_TYPE_SSIZE_T
AC_TYPE_UINT16_T
AC_TYPE_UINT8_T
AC_HEADER_TIME
dnl Checks for library functions.
AC_FUNC_FORK
AC_FUNC_SELECT_ARGTYPES
AC_TYPE_SIGNAL
AC_CHECK_FUNCS([memset select setresuid setreuid strerror get_current_dir_name getpwnam_r setrlimit])
AC_CHECK_LIB(socket, connect)
dnl Checks for library functions.
AC_CHECK_FUNCS([malloc memset posix_memalign socket])
AC_ARG_WITH(ev,
AS_HELP_STRING([--with-ev=DIR], [use a specific libev library]),
[CFLAGS="$CFLAGS -I$withval/include"
CPPFLAGS="$CPPFLAGS -I$withval/include"
LDFLAGS="$LDFLAGS -L$withval/lib"]
)
AC_CHECK_HEADERS([ev.h libev/ev.h], [], [])
AC_CHECK_LIB([ev], [ev_loop_destroy], [LIBS="-lev $LIBS"], [AC_MSG_ERROR([Couldn't find libev. Try installing libev-dev@<:@el@:>@.])])
AC_CONFIG_FILES([shadowsocks-libev.pc
Makefile
doc/Makefile
src/Makefile])
AM_COND_IF([USE_SYSTEM_SHARED_LIB],
[AC_DEFINE([USE_SYSTEM_SHARED_LIB], [1], [Define if use system shared lib.])],
[AC_CONFIG_FILES([libbloom/Makefile libcork/Makefile libipset/Makefile])])
AC_ARG_ENABLE(connmarktos,
[AS_HELP_STRING(--enable-connmarktos, Enable saved connmark to IP TOS QoS feature)],
[ enable_connmarktos="$enableval" ])
if test x"$enable_connmarktos" = "xyes" ; then
AC_MSG_NOTICE([Linux Netfilter Conntrack support requested by --enable-connmarktos: ${enable_connmarktos}])
if test "x$enable_connmarktos" != "xno"; then
PKG_CHECK_MODULES([NETFILTER_CONNTRACK], [libnetfilter_conntrack],,
[AC_SEARCH_LIBS([nfct_query], [netfilter_conntrack],,[
if test x"$enable_connmarktos" = "xyes"; then
AC_MSG_ERROR([--enable-connmarktos specified but libnetfilter-conntrack library not found])
fi
with_netfilter_conntrack=no], [-lnfnetlink])
AC_CHECK_HEADERS([libnetfilter_conntrack/libnetfilter_conntrack.h \
libnetfilter_conntrack/libnetfilter_conntrack_tcp.h],,[
if test x"$enable_connmarktos" = "xyes"; then
AC_MSG_ERROR([--enable-connmarktos specified but libnetfilter-conntrack headers not found])
fi
with_netfilter_conntrack=no])])
# If nothing is broken; enable the libraries usage.
if test "x$with_netfilter_conntrack" != "xno"; then
with_netfilter_conntrack=yes
AC_DEFINE(USE_NFCONNTRACK_TOS, 1, [Enable support for QOS netfilter mark preservation])
fi
fi
fi
AC_ARG_ENABLE(nftables,
[AS_HELP_STRING(--enable-nftables, report malicious IP to nftables)],
[ enable_nftables="$enableval" ])
if test x"$enable_nftables" = "xyes" ; then
AC_MSG_NOTICE([Linux nftables support requested by --enable-nftables: ${enable_nftables}])
if test "x$enable_nftables" != "xno"; then
PKG_CHECK_MODULES([NFTABLES], [libmnl libnftnl],,
[AC_SEARCH_LIBS([mnl_socket_open], [mnl],,[
if test x"$enable_nftables" = "xyes"; then
AC_MSG_ERROR([--enable-nftables specified but libmnl library not found])
fi
with_nftables=no], [-lmnl])
AC_CHECK_HEADERS([libmnl/libmnl.h],,[
if test x"$enable_nftables" = "xyes"; then
AC_MSG_ERROR([--enable-nftables specified but libmnl headers not found])
fi
with_nftables=no])
AC_SEARCH_LIBS([nftnl_nlmsg_build_hdr], [nftnl],,[
if test x"$enable_nftables" = "xyes"; then
AC_MSG_ERROR([--enable-nftables specified but libnftnl library not found])
fi
with_nftables=no], [-lnftnl])
AC_CHECK_HEADERS([libnftnl/common.h],,[
if test x"$enable_nftables" = "xyes"; then
AC_MSG_ERROR([--enable-nftables specified but libnftnl headers not found])
fi
with_nftables=no])])
# If nothing is broken; enable the libraries usage.
if test "x$with_nftables" != "xno"; then
with_nftables=yes
AC_DEFINE(USE_NFTABLES, 1, [Enable support for nftables firewall])
fi
fi
fi
AC_OUTPUT
-60
View File
@@ -1,60 +0,0 @@
ASCIIDOC = @ASCIIDOC@
ASCIIDOC_EXTRA =
MANPAGE_XSL = $(srcdir)/manpage-normal.xsl
XMLTO = @XMLTO@
XMLTO_EXTRA = -m $(srcdir)/manpage-bold-literal.xsl
GZIPCMD = @GZIP@
INSTALL = @INSTALL@
RM = @RM@
MV = @MV@
SED = @SED@
VERSION = `$(SED) -n 's/.*PACKAGE_VERSION "\(.*\)"/\1/p'\
../config.h`
# Guard against environment variables
MAN1_DOC =
MAN1_DOC += ss-local.1
MAN1_DOC += ss-manager.1
MAN1_DOC += ss-nat.1
MAN1_DOC += ss-redir.1
MAN1_DOC += ss-server.1
MAN1_DOC += ss-tunnel.1
MAN8_DOC =
MAN8_DOC += shadowsocks-libev.8
MAN8_XML = $(MAN8_DOC:%.8=%.xml)
MAN1_XML = $(MAN1_DOC:%.1=%.xml)
MAN_XML = $(MAN8_XML) $(MAN1_XML)
MAN8_HTML = $(MAN8_DOC:%.8=%.html)
MAN1_HTML = $(MAN1_DOC:%.1=%.html)
MAN_HTML = $(MAN8_HTML) $(MAN1_HTML)
MAN8_TXT = $(MAN8_DOC:%.8=%.asciidoc)
MAN1_TXT = $(MAN1_DOC:%.1=%.asciidoc)
MAN_TXT = $(MAN8_TXT) $(MAN1_TXT)
man_MANS = $(MAN8_DOC) $(MAN1_DOC)
html-local: $(MAN_HTML)
%.1: %.xml
$(AM_V_GEN)$(XMLTO) -m $(MANPAGE_XSL) $(XMLTO_EXTRA) man $<
%.8: %.xml
$(AM_V_GEN)$(XMLTO) -m $(MANPAGE_XSL) $(XMLTO_EXTRA) man $<
%.xml: %.asciidoc
$(AM_V_GEN)$(ASCIIDOC) -b docbook -d manpage -f $(srcdir)/asciidoc.conf \
-aversion=$(VERSION) $(ASCIIDOC_EXTRA) -o $@ $<
%.html: %.asciidoc
$(AM_V_GEN)$(ASCIIDOC) -b html4 -d article -f $(srcdir)/asciidoc.conf \
-aversion=$(VERSION) $(ASCIIDOC_EXTRA) -o $@ $<
doc_DATA = $(MAN_HTML)
CLEANFILES = $(MAN_XML) $(man_MANS) $(MAN_HTML)
EXTRA_DIST = *.asciidoc asciidoc.conf *.xsl
@@ -151,6 +151,26 @@ Only available with Linux kernel > 3.9.0.
--no-delay::
Enable TCP_NODELAY.
--tcp-incoming-sndbuf <size>::
Set TCP send buffer size for incoming connections.
+
Not available in manager mode.
--tcp-incoming-rcvbuf <size>::
Set TCP receive buffer size for incoming connections.
+
Not available in manager mode.
--tcp-outgoing-sndbuf <size>::
Set TCP send buffer size for outgoing connections.
+
Not available in manager mode.
--tcp-outgoing-rcvbuf <size>::
Set TCP receive buffer size for outgoing connections.
+
Not available in manager mode.
--acl <acl_config>::
Enable ACL (Access Control List) and specify config file.
+
@@ -166,6 +186,17 @@ Specify the executable path of `ss-server`.
+
Only available in manager mode.
-D <path>::
--workdir <path>::
Specify the working directory of ss-manager.
+
Only available in manager mode.
--nftables-sets <family:table:set>::
Specify nftables sets for reporting malicious IPs.
+
Only available in server mode, when built with nftables support.
-v::
Enable verbose mode.
+13 -1
View File
@@ -104,7 +104,7 @@ Enable UDP relay.
Enable UDP relay and disable TCP relay.
-6::
Resovle hostname to IPv6 address first.
Resolve hostname to IPv6 address first.
--fast-open::
Enable TCP fast open.
@@ -130,6 +130,18 @@ Only available with MPTCP enabled Linux kernel.
--no-delay::
Enable TCP_NODELAY.
--tcp-incoming-sndbuf <size>::
Set TCP send buffer size for incoming connections.
--tcp-incoming-rcvbuf <size>::
Set TCP receive buffer size for incoming connections.
--tcp-outgoing-sndbuf <size>::
Set TCP send buffer size for outgoing connections.
--tcp-outgoing-rcvbuf <size>::
Set TCP receive buffer size for outgoing connections.
--plugin <plugin_name>::
Enable SIP003 plugin. (Experimental)
+2 -1
View File
@@ -117,7 +117,8 @@ Specify the executable path of ss-server.
+
Only available in manager mode.
--executable <path_to_server_executable>::
-D <path>::
--workdir <path>::
Specify the working directory of ss-manager.
+
Only available in manager mode.
+13 -1
View File
@@ -99,7 +99,7 @@ Enable UDP relay and disable TCP relay.
Use tproxy instead of redirect. (for tcp)
-6::
Resovle hostname to IPv6 address first.
Resolve hostname to IPv6 address first.
--mtu <MTU>::
Specify the MTU of your network interface.
@@ -117,6 +117,18 @@ Only available with Linux kernel > 3.9.0.
--no-delay::
Enable TCP_NODELAY.
--tcp-incoming-sndbuf <size>::
Set TCP send buffer size for incoming connections.
--tcp-incoming-rcvbuf <size>::
Set TCP receive buffer size for incoming connections.
--tcp-outgoing-sndbuf <size>::
Set TCP send buffer size for outgoing connections.
--tcp-outgoing-rcvbuf <size>::
Set TCP receive buffer size for outgoing connections.
--plugin <plugin_name>::
Enable SIP003 plugin. (Experimental)
+13 -1
View File
@@ -99,7 +99,7 @@ Enable UDP relay.
Enable UDP relay and disable TCP relay.
-6::
Resovle hostname to IPv6 address first.
Resolve hostname to IPv6 address first.
-d <addr>::
Setup name servers for internal DNS resolver (libc-ares).
@@ -118,6 +118,18 @@ Only available with Linux kernel > 3.9.0.
--no-delay::
Enable TCP_NODELAY.
--tcp-incoming-sndbuf <size>::
Set TCP send buffer size for incoming connections.
--tcp-incoming-rcvbuf <size>::
Set TCP receive buffer size for incoming connections.
--tcp-outgoing-sndbuf <size>::
Set TCP send buffer size for outgoing connections.
--tcp-outgoing-rcvbuf <size>::
Set TCP receive buffer size for outgoing connections.
--acl <acl_config>::
Enable ACL (Access Control List) and specify config file.
+26 -1
View File
@@ -103,7 +103,7 @@ Enable UDP relay.
Enable UDP relay and disable TCP relay.
-6::
Resovle hostname to IPv6 address first.
Resolve hostname to IPv6 address first.
-L <addr:port>::
Specify destination server address and port for local port forwarding.
@@ -126,6 +126,18 @@ Only available with Linux kernel > 3.9.0.
--no-delay::
Enable TCP_NODELAY.
--tcp-incoming-sndbuf <size>::
Set TCP send buffer size for incoming connections.
--tcp-incoming-rcvbuf <size>::
Set TCP receive buffer size for incoming connections.
--tcp-outgoing-sndbuf <size>::
Set TCP send buffer size for outgoing connections.
--tcp-outgoing-rcvbuf <size>::
Set TCP receive buffer size for outgoing connections.
--plugin <plugin_name>::
Enable SIP003 plugin. (Experimental)
@@ -138,6 +150,19 @@ Enable verbose mode.
-h|--help::
Print help message.
EXAMPLE
-------
`ss-tunnel`(1) can be used to forward DNS queries to a remote DNS server
through the shadowsocks tunnel. Here is an example:
....
# Forward local UDP port 5353 to 8.8.8.8:53 through the ss-server
ss-tunnel -s example.com -p 12345 -l 5353 -k foobar -m aes-256-cfb -L 8.8.8.8:53 -u
# Then configure your system to use 127.0.0.1:5353 as the DNS server
dig @127.0.0.1 -p 5353 www.google.com
....
SEE ALSO
--------
`ss-local`(1),
+4 -6
View File
@@ -14,21 +14,19 @@ COPY . /tmp/repo
RUN set -x \
# Build environment setup
&& apk add --no-cache --virtual .build-deps \
autoconf \
automake \
build-base \
cmake \
c-ares-dev \
libcap \
libev-dev \
libtool \
libsodium-dev \
linux-headers \
mbedtls-dev \
pcre-dev \
pcre2-dev \
# Build & install
&& cd /tmp/repo \
&& ./autogen.sh \
&& ./configure --prefix=/usr/local --disable-documentation \
&& mkdir -p build && cd build \
&& cmake .. -DCMAKE_INSTALL_PREFIX=/usr/local -DBUILD_TESTING=OFF -DWITH_STATIC=OFF -DCMAKE_BUILD_TYPE=Release \
&& make -j$(getconf _NPROCESSORS_ONLN) \
&& make install \
&& cd /usr/local/bin \
-485
View File
@@ -1,485 +0,0 @@
# ===========================================================================
# http://www.gnu.org/software/autoconf-archive/ax_pthread.html
# ===========================================================================
#
# SYNOPSIS
#
# AX_PTHREAD([ACTION-IF-FOUND[, ACTION-IF-NOT-FOUND]])
#
# DESCRIPTION
#
# This macro figures out how to build C programs using POSIX threads. It
# sets the PTHREAD_LIBS output variable to the threads library and linker
# flags, and the PTHREAD_CFLAGS output variable to any special C compiler
# flags that are needed. (The user can also force certain compiler
# flags/libs to be tested by setting these environment variables.)
#
# Also sets PTHREAD_CC to any special C compiler that is needed for
# multi-threaded programs (defaults to the value of CC otherwise). (This
# is necessary on AIX to use the special cc_r compiler alias.)
#
# NOTE: You are assumed to not only compile your program with these flags,
# but also to link with them as well. For example, you might link with
# $PTHREAD_CC $CFLAGS $PTHREAD_CFLAGS $LDFLAGS ... $PTHREAD_LIBS $LIBS
#
# If you are only building threaded programs, you may wish to use these
# variables in your default LIBS, CFLAGS, and CC:
#
# LIBS="$PTHREAD_LIBS $LIBS"
# CFLAGS="$CFLAGS $PTHREAD_CFLAGS"
# CC="$PTHREAD_CC"
#
# In addition, if the PTHREAD_CREATE_JOINABLE thread-attribute constant
# has a nonstandard name, this macro defines PTHREAD_CREATE_JOINABLE to
# that name (e.g. PTHREAD_CREATE_UNDETACHED on AIX).
#
# Also HAVE_PTHREAD_PRIO_INHERIT is defined if pthread is found and the
# PTHREAD_PRIO_INHERIT symbol is defined when compiling with
# PTHREAD_CFLAGS.
#
# ACTION-IF-FOUND is a list of shell commands to run if a threads library
# is found, and ACTION-IF-NOT-FOUND is a list of commands to run it if it
# is not found. If ACTION-IF-FOUND is not specified, the default action
# will define HAVE_PTHREAD.
#
# Please let the authors know if this macro fails on any platform, or if
# you have any other suggestions or comments. This macro was based on work
# by SGJ on autoconf scripts for FFTW (http://www.fftw.org/) (with help
# from M. Frigo), as well as ac_pthread and hb_pthread macros posted by
# Alejandro Forero Cuervo to the autoconf macro repository. We are also
# grateful for the helpful feedback of numerous users.
#
# Updated for Autoconf 2.68 by Daniel Richard G.
#
# LICENSE
#
# Copyright (c) 2008 Steven G. Johnson <stevenj@alum.mit.edu>
# Copyright (c) 2011 Daniel Richard G. <skunk@iSKUNK.ORG>
#
# This program is free software: you can redistribute it and/or modify it
# under the terms of the GNU General Public License as published by the
# Free Software Foundation, either version 3 of the License, or (at your
# option) any later version.
#
# This program is distributed in the hope that it will be useful, but
# WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General
# Public License for more details.
#
# You should have received a copy of the GNU General Public License along
# with this program. If not, see <http://www.gnu.org/licenses/>.
#
# As a special exception, the respective Autoconf Macro's copyright owner
# gives unlimited permission to copy, distribute and modify the configure
# scripts that are the output of Autoconf when processing the Macro. You
# need not follow the terms of the GNU General Public License when using
# or distributing such scripts, even though portions of the text of the
# Macro appear in them. The GNU General Public License (GPL) does govern
# all other use of the material that constitutes the Autoconf Macro.
#
# This special exception to the GPL applies to versions of the Autoconf
# Macro released by the Autoconf Archive. When you make and distribute a
# modified version of the Autoconf Macro, you may extend this special
# exception to the GPL to apply to your modified version as well.
#serial 23
AU_ALIAS([ACX_PTHREAD], [AX_PTHREAD])
AC_DEFUN([AX_PTHREAD], [
AC_REQUIRE([AC_CANONICAL_HOST])
AC_REQUIRE([AC_PROG_CC])
AC_REQUIRE([AC_PROG_SED])
AC_LANG_PUSH([C])
ax_pthread_ok=no
# We used to check for pthread.h first, but this fails if pthread.h
# requires special compiler flags (e.g. on Tru64 or Sequent).
# It gets checked for in the link test anyway.
# First of all, check if the user has set any of the PTHREAD_LIBS,
# etcetera environment variables, and if threads linking works using
# them:
if test "x$PTHREAD_CFLAGS$PTHREAD_LIBS" != "x"; then
ax_pthread_save_CC="$CC"
ax_pthread_save_CFLAGS="$CFLAGS"
ax_pthread_save_LIBS="$LIBS"
AS_IF([test "x$PTHREAD_CC" != "x"], [CC="$PTHREAD_CC"])
CFLAGS="$CFLAGS $PTHREAD_CFLAGS"
LIBS="$PTHREAD_LIBS $LIBS"
AC_MSG_CHECKING([for pthread_join using $CC $PTHREAD_CFLAGS $PTHREAD_LIBS])
AC_LINK_IFELSE([AC_LANG_CALL([], [pthread_join])], [ax_pthread_ok=yes])
AC_MSG_RESULT([$ax_pthread_ok])
if test "x$ax_pthread_ok" = "xno"; then
PTHREAD_LIBS=""
PTHREAD_CFLAGS=""
fi
CC="$ax_pthread_save_CC"
CFLAGS="$ax_pthread_save_CFLAGS"
LIBS="$ax_pthread_save_LIBS"
fi
# We must check for the threads library under a number of different
# names; the ordering is very important because some systems
# (e.g. DEC) have both -lpthread and -lpthreads, where one of the
# libraries is broken (non-POSIX).
# Create a list of thread flags to try. Items starting with a "-" are
# C compiler flags, and other items are library names, except for "none"
# which indicates that we try without any flags at all, and "pthread-config"
# which is a program returning the flags for the Pth emulation library.
ax_pthread_flags="pthreads none -Kthread -pthread -pthreads -mthreads pthread --thread-safe -mt pthread-config"
# The ordering *is* (sometimes) important. Some notes on the
# individual items follow:
# pthreads: AIX (must check this before -lpthread)
# none: in case threads are in libc; should be tried before -Kthread and
# other compiler flags to prevent continual compiler warnings
# -Kthread: Sequent (threads in libc, but -Kthread needed for pthread.h)
# -pthread: Linux/gcc (kernel threads), BSD/gcc (userland threads), Tru64
# (Note: HP C rejects this with "bad form for `-t' option")
# -pthreads: Solaris/gcc (Note: HP C also rejects)
# -mt: Sun Workshop C (may only link SunOS threads [-lthread], but it
# doesn't hurt to check since this sometimes defines pthreads and
# -D_REENTRANT too), HP C (must be checked before -lpthread, which
# is present but should not be used directly; and before -mthreads,
# because the compiler interprets this as "-mt" + "-hreads")
# -mthreads: Mingw32/gcc, Lynx/gcc
# pthread: Linux, etcetera
# --thread-safe: KAI C++
# pthread-config: use pthread-config program (for GNU Pth library)
case $host_os in
freebsd*)
# -kthread: FreeBSD kernel threads (preferred to -pthread since SMP-able)
# lthread: LinuxThreads port on FreeBSD (also preferred to -pthread)
ax_pthread_flags="-kthread lthread $ax_pthread_flags"
;;
hpux*)
# From the cc(1) man page: "[-mt] Sets various -D flags to enable
# multi-threading and also sets -lpthread."
ax_pthread_flags="-mt -pthread pthread $ax_pthread_flags"
;;
openedition*)
# IBM z/OS requires a feature-test macro to be defined in order to
# enable POSIX threads at all, so give the user a hint if this is
# not set. (We don't define these ourselves, as they can affect
# other portions of the system API in unpredictable ways.)
AC_EGREP_CPP([AX_PTHREAD_ZOS_MISSING],
[
# if !defined(_OPEN_THREADS) && !defined(_UNIX03_THREADS)
AX_PTHREAD_ZOS_MISSING
# endif
],
[AC_MSG_WARN([IBM z/OS requires -D_OPEN_THREADS or -D_UNIX03_THREADS to enable pthreads support.])])
;;
solaris*)
# On Solaris (at least, for some versions), libc contains stubbed
# (non-functional) versions of the pthreads routines, so link-based
# tests will erroneously succeed. (N.B.: The stubs are missing
# pthread_cleanup_push, or rather a function called by this macro,
# so we could check for that, but who knows whether they'll stub
# that too in a future libc.) So we'll check first for the
# standard Solaris way of linking pthreads (-mt -lpthread).
ax_pthread_flags="-mt,pthread pthread $ax_pthread_flags"
;;
esac
# GCC generally uses -pthread, or -pthreads on some platforms (e.g. SPARC)
AS_IF([test "x$GCC" = "xyes"],
[ax_pthread_flags="-pthread -pthreads $ax_pthread_flags"])
# The presence of a feature test macro requesting re-entrant function
# definitions is, on some systems, a strong hint that pthreads support is
# correctly enabled
case $host_os in
darwin* | hpux* | linux* | osf* | solaris*)
ax_pthread_check_macro="_REENTRANT"
;;
aix*)
ax_pthread_check_macro="_THREAD_SAFE"
;;
*)
ax_pthread_check_macro="--"
;;
esac
AS_IF([test "x$ax_pthread_check_macro" = "x--"],
[ax_pthread_check_cond=0],
[ax_pthread_check_cond="!defined($ax_pthread_check_macro)"])
# Are we compiling with Clang?
AC_CACHE_CHECK([whether $CC is Clang],
[ax_cv_PTHREAD_CLANG],
[ax_cv_PTHREAD_CLANG=no
# Note that Autoconf sets GCC=yes for Clang as well as GCC
if test "x$GCC" = "xyes"; then
AC_EGREP_CPP([AX_PTHREAD_CC_IS_CLANG],
[/* Note: Clang 2.7 lacks __clang_[a-z]+__ */
# if defined(__clang__) && defined(__llvm__)
AX_PTHREAD_CC_IS_CLANG
# endif
],
[ax_cv_PTHREAD_CLANG=yes])
fi
])
ax_pthread_clang="$ax_cv_PTHREAD_CLANG"
ax_pthread_clang_warning=no
# Clang needs special handling, because older versions handle the -pthread
# option in a rather... idiosyncratic way
if test "x$ax_pthread_clang" = "xyes"; then
# Clang takes -pthread; it has never supported any other flag
# (Note 1: This will need to be revisited if a system that Clang
# supports has POSIX threads in a separate library. This tends not
# to be the way of modern systems, but it's conceivable.)
# (Note 2: On some systems, notably Darwin, -pthread is not needed
# to get POSIX threads support; the API is always present and
# active. We could reasonably leave PTHREAD_CFLAGS empty. But
# -pthread does define _REENTRANT, and while the Darwin headers
# ignore this macro, third-party headers might not.)
PTHREAD_CFLAGS="-pthread"
PTHREAD_LIBS=
ax_pthread_ok=yes
# However, older versions of Clang make a point of warning the user
# that, in an invocation where only linking and no compilation is
# taking place, the -pthread option has no effect ("argument unused
# during compilation"). They expect -pthread to be passed in only
# when source code is being compiled.
#
# Problem is, this is at odds with the way Automake and most other
# C build frameworks function, which is that the same flags used in
# compilation (CFLAGS) are also used in linking. Many systems
# supported by AX_PTHREAD require exactly this for POSIX threads
# support, and in fact it is often not straightforward to specify a
# flag that is used only in the compilation phase and not in
# linking. Such a scenario is extremely rare in practice.
#
# Even though use of the -pthread flag in linking would only print
# a warning, this can be a nuisance for well-run software projects
# that build with -Werror. So if the active version of Clang has
# this misfeature, we search for an option to squash it.
AC_CACHE_CHECK([whether Clang needs flag to prevent "argument unused" warning when linking with -pthread],
[ax_cv_PTHREAD_CLANG_NO_WARN_FLAG],
[ax_cv_PTHREAD_CLANG_NO_WARN_FLAG=unknown
# Create an alternate version of $ac_link that compiles and
# links in two steps (.c -> .o, .o -> exe) instead of one
# (.c -> exe), because the warning occurs only in the second
# step
ax_pthread_save_ac_link="$ac_link"
ax_pthread_sed='s/conftest\.\$ac_ext/conftest.$ac_objext/g'
ax_pthread_link_step=`$as_echo "$ac_link" | sed "$ax_pthread_sed"`
ax_pthread_2step_ac_link="($ac_compile) && (echo ==== >&5) && ($ax_pthread_link_step)"
ax_pthread_save_CFLAGS="$CFLAGS"
for ax_pthread_try in '' -Qunused-arguments -Wno-unused-command-line-argument unknown; do
AS_IF([test "x$ax_pthread_try" = "xunknown"], [break])
CFLAGS="-Werror -Wunknown-warning-option $ax_pthread_try -pthread $ax_pthread_save_CFLAGS"
ac_link="$ax_pthread_save_ac_link"
AC_LINK_IFELSE([AC_LANG_SOURCE([[int main(void){return 0;}]])],
[ac_link="$ax_pthread_2step_ac_link"
AC_LINK_IFELSE([AC_LANG_SOURCE([[int main(void){return 0;}]])],
[break])
])
done
ac_link="$ax_pthread_save_ac_link"
CFLAGS="$ax_pthread_save_CFLAGS"
AS_IF([test "x$ax_pthread_try" = "x"], [ax_pthread_try=no])
ax_cv_PTHREAD_CLANG_NO_WARN_FLAG="$ax_pthread_try"
])
case "$ax_cv_PTHREAD_CLANG_NO_WARN_FLAG" in
no | unknown) ;;
*) PTHREAD_CFLAGS="$ax_cv_PTHREAD_CLANG_NO_WARN_FLAG $PTHREAD_CFLAGS" ;;
esac
fi # $ax_pthread_clang = yes
if test "x$ax_pthread_ok" = "xno"; then
for ax_pthread_try_flag in $ax_pthread_flags; do
case $ax_pthread_try_flag in
none)
AC_MSG_CHECKING([whether pthreads work without any flags])
;;
-mt,pthread)
AC_MSG_CHECKING([whether pthreads work with -mt -lpthread])
PTHREAD_CFLAGS="-mt"
PTHREAD_LIBS="-lpthread"
;;
-*)
AC_MSG_CHECKING([whether pthreads work with $ax_pthread_try_flag])
PTHREAD_CFLAGS="$ax_pthread_try_flag"
;;
pthread-config)
AC_CHECK_PROG([ax_pthread_config], [pthread-config], [yes], [no])
AS_IF([test "x$ax_pthread_config" = "xno"], [continue])
PTHREAD_CFLAGS="`pthread-config --cflags`"
PTHREAD_LIBS="`pthread-config --ldflags` `pthread-config --libs`"
;;
*)
AC_MSG_CHECKING([for the pthreads library -l$ax_pthread_try_flag])
PTHREAD_LIBS="-l$ax_pthread_try_flag"
;;
esac
ax_pthread_save_CFLAGS="$CFLAGS"
ax_pthread_save_LIBS="$LIBS"
CFLAGS="$CFLAGS $PTHREAD_CFLAGS"
LIBS="$PTHREAD_LIBS $LIBS"
# Check for various functions. We must include pthread.h,
# since some functions may be macros. (On the Sequent, we
# need a special flag -Kthread to make this header compile.)
# We check for pthread_join because it is in -lpthread on IRIX
# while pthread_create is in libc. We check for pthread_attr_init
# due to DEC craziness with -lpthreads. We check for
# pthread_cleanup_push because it is one of the few pthread
# functions on Solaris that doesn't have a non-functional libc stub.
# We try pthread_create on general principles.
AC_LINK_IFELSE([AC_LANG_PROGRAM([#include <pthread.h>
# if $ax_pthread_check_cond
# error "$ax_pthread_check_macro must be defined"
# endif
static void routine(void *a) { a = 0; }
static void *start_routine(void *a) { return a; }],
[pthread_t th; pthread_attr_t attr;
pthread_create(&th, 0, start_routine, 0);
pthread_join(th, 0);
pthread_attr_init(&attr);
pthread_cleanup_push(routine, 0);
pthread_cleanup_pop(0) /* ; */])],
[ax_pthread_ok=yes],
[])
CFLAGS="$ax_pthread_save_CFLAGS"
LIBS="$ax_pthread_save_LIBS"
AC_MSG_RESULT([$ax_pthread_ok])
AS_IF([test "x$ax_pthread_ok" = "xyes"], [break])
PTHREAD_LIBS=""
PTHREAD_CFLAGS=""
done
fi
# Various other checks:
if test "x$ax_pthread_ok" = "xyes"; then
ax_pthread_save_CFLAGS="$CFLAGS"
ax_pthread_save_LIBS="$LIBS"
CFLAGS="$CFLAGS $PTHREAD_CFLAGS"
LIBS="$PTHREAD_LIBS $LIBS"
# Detect AIX lossage: JOINABLE attribute is called UNDETACHED.
AC_CACHE_CHECK([for joinable pthread attribute],
[ax_cv_PTHREAD_JOINABLE_ATTR],
[ax_cv_PTHREAD_JOINABLE_ATTR=unknown
for ax_pthread_attr in PTHREAD_CREATE_JOINABLE PTHREAD_CREATE_UNDETACHED; do
AC_LINK_IFELSE([AC_LANG_PROGRAM([#include <pthread.h>],
[int attr = $ax_pthread_attr; return attr /* ; */])],
[ax_cv_PTHREAD_JOINABLE_ATTR=$ax_pthread_attr; break],
[])
done
])
AS_IF([test "x$ax_cv_PTHREAD_JOINABLE_ATTR" != "xunknown" && \
test "x$ax_cv_PTHREAD_JOINABLE_ATTR" != "xPTHREAD_CREATE_JOINABLE" && \
test "x$ax_pthread_joinable_attr_defined" != "xyes"],
[AC_DEFINE_UNQUOTED([PTHREAD_CREATE_JOINABLE],
[$ax_cv_PTHREAD_JOINABLE_ATTR],
[Define to necessary symbol if this constant
uses a non-standard name on your system.])
ax_pthread_joinable_attr_defined=yes
])
AC_CACHE_CHECK([whether more special flags are required for pthreads],
[ax_cv_PTHREAD_SPECIAL_FLAGS],
[ax_cv_PTHREAD_SPECIAL_FLAGS=no
case $host_os in
solaris*)
ax_cv_PTHREAD_SPECIAL_FLAGS="-D_POSIX_PTHREAD_SEMANTICS"
;;
esac
])
AS_IF([test "x$ax_cv_PTHREAD_SPECIAL_FLAGS" != "xno" && \
test "x$ax_pthread_special_flags_added" != "xyes"],
[PTHREAD_CFLAGS="$ax_cv_PTHREAD_SPECIAL_FLAGS $PTHREAD_CFLAGS"
ax_pthread_special_flags_added=yes])
AC_CACHE_CHECK([for PTHREAD_PRIO_INHERIT],
[ax_cv_PTHREAD_PRIO_INHERIT],
[AC_LINK_IFELSE([AC_LANG_PROGRAM([[#include <pthread.h>]],
[[int i = PTHREAD_PRIO_INHERIT;]])],
[ax_cv_PTHREAD_PRIO_INHERIT=yes],
[ax_cv_PTHREAD_PRIO_INHERIT=no])
])
AS_IF([test "x$ax_cv_PTHREAD_PRIO_INHERIT" = "xyes" && \
test "x$ax_pthread_prio_inherit_defined" != "xyes"],
[AC_DEFINE([HAVE_PTHREAD_PRIO_INHERIT], [1], [Have PTHREAD_PRIO_INHERIT.])
ax_pthread_prio_inherit_defined=yes
])
CFLAGS="$ax_pthread_save_CFLAGS"
LIBS="$ax_pthread_save_LIBS"
# More AIX lossage: compile with *_r variant
if test "x$GCC" != "xyes"; then
case $host_os in
aix*)
AS_CASE(["x/$CC"],
[x*/c89|x*/c89_128|x*/c99|x*/c99_128|x*/cc|x*/cc128|x*/xlc|x*/xlc_v6|x*/xlc128|x*/xlc128_v6],
[#handle absolute path differently from PATH based program lookup
AS_CASE(["x$CC"],
[x/*],
[AS_IF([AS_EXECUTABLE_P([${CC}_r])],[PTHREAD_CC="${CC}_r"])],
[AC_CHECK_PROGS([PTHREAD_CC],[${CC}_r],[$CC])])])
;;
esac
fi
fi
test -n "$PTHREAD_CC" || PTHREAD_CC="$CC"
AC_SUBST([PTHREAD_LIBS])
AC_SUBST([PTHREAD_CFLAGS])
AC_SUBST([PTHREAD_CC])
# Finally, execute ACTION-IF-FOUND/ACTION-IF-NOT-FOUND:
if test "x$ax_pthread_ok" = "xyes"; then
ifelse([$1],,[AC_DEFINE([HAVE_PTHREAD],[1],[Define if you have POSIX threads libraries and header files.])],[$1])
:
else
ax_pthread_ok=no
$2
fi
AC_LANG_POP
])dnl AX_PTHREAD
-74
View File
@@ -1,74 +0,0 @@
# ===========================================================================
# http://www.gnu.org/software/autoconf-archive/ax_tls.html
# ===========================================================================
#
# SYNOPSIS
#
# AX_TLS([action-if-found], [action-if-not-found])
#
# DESCRIPTION
#
# Provides a test for the compiler support of thread local storage (TLS)
# extensions. Defines TLS if it is found. Currently knows about GCC/ICC
# and MSVC. I think SunPro uses the same as GCC, and Borland apparently
# supports either.
#
# LICENSE
#
# Copyright (c) 2008 Alan Woodland <ajw05@aber.ac.uk>
# Copyright (c) 2010 Diego Elio Petteno` <flameeyes@gmail.com>
#
# This program is free software: you can redistribute it and/or modify it
# under the terms of the GNU General Public License as published by the
# Free Software Foundation, either version 3 of the License, or (at your
# option) any later version.
#
# This program is distributed in the hope that it will be useful, but
# WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General
# Public License for more details.
#
# You should have received a copy of the GNU General Public License along
# with this program. If not, see <http://www.gnu.org/licenses/>.
#
# As a special exception, the respective Autoconf Macro's copyright owner
# gives unlimited permission to copy, distribute and modify the configure
# scripts that are the output of Autoconf when processing the Macro. You
# need not follow the terms of the GNU General Public License when using
# or distributing such scripts, even though portions of the text of the
# Macro appear in them. The GNU General Public License (GPL) does govern
# all other use of the material that constitutes the Autoconf Macro.
#
# This special exception to the GPL applies to versions of the Autoconf
# Macro released by the Autoconf Archive. When you make and distribute a
# modified version of the Autoconf Macro, you may extend this special
# exception to the GPL to apply to your modified version as well.
#serial 11
AC_DEFUN([AX_TLS], [
AC_MSG_CHECKING([for thread local storage (TLS) class])
AC_CACHE_VAL([ac_cv_tls],
[for ax_tls_keyword in __thread '__declspec(thread)' none; do
AS_CASE([$ax_tls_keyword],
[none], [ac_cv_tls=none ; break],
[AC_TRY_COMPILE(
[#include <stdlib.h>
static void
foo(void) {
static ] $ax_tls_keyword [ int bar;
exit(1);
}],
[],
[ac_cv_tls=$ax_tls_keyword ; break],
ac_cv_tls=none
)])
done
])
AC_MSG_RESULT([$ac_cv_tls])
AS_IF([test "$ac_cv_tls" != "none"],
[AC_DEFINE_UNQUOTED([TLS],[$ac_cv_tls],[If the compiler supports a TLS storage class define it to that here])
m4_ifnblank([$1],[$1])],
[m4_ifnblank([$2],[$2])])
])
-30
View File
@@ -1,30 +0,0 @@
dnl Check to find the libcares headers/libraries
AC_DEFUN([ss_CARES],
[
AC_ARG_WITH(cares,
AS_HELP_STRING([--with-cares=DIR], [The c-ares library base directory, or:]),
[cares="$withval"
CFLAGS="$CFLAGS -I$withval/include"
LDFLAGS="$LDFLAGS -L$withval/lib"]
)
AC_ARG_WITH(cares-include,
AS_HELP_STRING([--with-cares-include=DIR], [The c-ares library headers directory (without trailing /cares)]),
[cares_include="$withval"
CFLAGS="$CFLAGS -I$withval"]
)
AC_ARG_WITH(cares-lib,
AS_HELP_STRING([--with-cares-lib=DIR], [The c-ares library library directory]),
[cares_lib="$withval"
LDFLAGS="$LDFLAGS -L$withval"]
)
AC_CHECK_LIB(cares, ares_library_init,
[LIBS="-lcares $LIBS"],
[AC_MSG_ERROR([The c-ares library libraries not found.])]
)
])
-41
View File
@@ -1,41 +0,0 @@
# inet_ntop.m4 serial 19
dnl Copyright (C) 2005-2006, 2008-2013 Free Software Foundation, Inc.
dnl This file is free software; the Free Software Foundation
dnl gives unlimited permission to copy and/or distribute it,
dnl with or without modifications, as long as this notice is preserved.
AC_DEFUN([ss_FUNC_INET_NTOP],
[
AC_REQUIRE([AC_C_RESTRICT])
dnl Most platforms that provide inet_ntop define it in libc.
dnl Solaris 8..10 provide inet_ntop in libnsl instead.
dnl Solaris 2.6..7 provide inet_ntop in libresolv instead.
HAVE_INET_NTOP=1
INET_NTOP_LIB=
ss_save_LIBS=$LIBS
AC_SEARCH_LIBS([inet_ntop], [nsl resolv], [],
[AC_CHECK_FUNCS([inet_ntop])
if test $ac_cv_func_inet_ntop = no; then
HAVE_INET_NTOP=0
fi
])
LIBS=$ss_save_LIBS
if test "$ac_cv_search_inet_ntop" != "no" \
&& test "$ac_cv_search_inet_ntop" != "none required"; then
INET_NTOP_LIB="$ac_cv_search_inet_ntop"
fi
AC_CHECK_HEADERS_ONCE([netdb.h])
AC_CHECK_DECLS([inet_ntop],,,
[[#include <arpa/inet.h>
#if HAVE_NETDB_H
# include <netdb.h>
#endif
]])
if test $ac_cv_have_decl_inet_ntop = no; then
HAVE_DECL_INET_NTOP=0
fi
AC_SUBST([INET_NTOP_LIB])
])
-114
View File
@@ -1,114 +0,0 @@
dnl Check to find the mbed TLS headers/libraries
AC_DEFUN([ss_MBEDTLS],
[
AC_ARG_WITH(mbedtls,
AS_HELP_STRING([--with-mbedtls=DIR], [mbed TLS base directory, or:]),
[mbedtls="$withval"
CFLAGS="$CFLAGS -I$withval/include"
LDFLAGS="$LDFLAGS -L$withval/lib"]
)
AC_ARG_WITH(mbedtls-include,
AS_HELP_STRING([--with-mbedtls-include=DIR], [mbed TLS headers directory (without trailing /mbedtls)]),
[mbedtls_include="$withval"
CFLAGS="$CFLAGS -I$withval"]
)
AC_ARG_WITH(mbedtls-lib,
AS_HELP_STRING([--with-mbedtls-lib=DIR], [mbed TLS library directory]),
[mbedtls_lib="$withval"
LDFLAGS="$LDFLAGS -L$withval"]
)
AC_CHECK_LIB(mbedcrypto, mbedtls_cipher_setup,
[LIBS="-lmbedcrypto $LIBS"],
[AC_MSG_ERROR([mbed TLS libraries not found.])]
)
AC_MSG_CHECKING([whether mbedtls supports Cipher Feedback mode or not])
AC_COMPILE_IFELSE(
[AC_LANG_PROGRAM(
[[
#include <mbedtls/version.h>
#if MBEDTLS_VERSION_NUMBER >= 0x03000000
#include <mbedtls/mbedtls_config.h>
#else
#include <mbedtls/config.h>
#endif
]],
[[
#ifndef MBEDTLS_CIPHER_MODE_CFB
#error Cipher Feedback mode a.k.a CFB not supported by your mbed TLS.
#endif
]]
)],
[AC_MSG_RESULT([ok])],
[AC_MSG_ERROR([MBEDTLS_CIPHER_MODE_CFB required])]
)
AC_MSG_CHECKING([whether mbedtls supports the ARC4 stream cipher or not])
AC_COMPILE_IFELSE(
[AC_LANG_PROGRAM(
[[
#include <mbedtls/version.h>
#if MBEDTLS_VERSION_NUMBER >= 0x03000000
#include <mbedtls/mbedtls_config.h>
#else
#include <mbedtls/config.h>
#endif
]],
[[
#ifndef MBEDTLS_ARC4_C
#error the ARC4 stream cipher not supported by your mbed TLS.
#endif
]]
)],
[AC_MSG_RESULT([ok])],
[AC_MSG_WARN([We will continue without ARC4 stream cipher support, MBEDTLS_ARC4_C required])]
)
AC_MSG_CHECKING([whether mbedtls supports the Blowfish block cipher or not])
AC_COMPILE_IFELSE(
[AC_LANG_PROGRAM(
[[
#include <mbedtls/version.h>
#if MBEDTLS_VERSION_NUMBER >= 0x03000000
#include <mbedtls/mbedtls_config.h>
#else
#include <mbedtls/config.h>
#endif
]],
[[
#ifndef MBEDTLS_BLOWFISH_C
#error the Blowfish block cipher not supported by your mbed TLS.
#endif
]]
)],
[AC_MSG_RESULT([ok])],
[AC_MSG_WARN([We will continue without Blowfish block cipher support, MBEDTLS_BLOWFISH_C required])]
)
AC_MSG_CHECKING([whether mbedtls supports the Camellia block cipher or not])
AC_COMPILE_IFELSE(
[AC_LANG_PROGRAM(
[[
#include <mbedtls/version.h>
#if MBEDTLS_VERSION_NUMBER >= 0x03000000
#include <mbedtls/mbedtls_config.h>
#else
#include <mbedtls/config.h>
#endif
]],
[[
#ifndef MBEDTLS_CAMELLIA_C
#error the Camellia block cipher not supported by your mbed TLS.
#endif
]]
)],
[AC_MSG_RESULT([ok])],
[AC_MSG_WARN([We will continue without Camellia block cipher support, MBEDTLS_CAMELLIA_C required])]
)
])
-152
View File
@@ -1,152 +0,0 @@
dnl -------------------------------------------------------- -*- autoconf -*-
dnl Licensed to the Apache Software Foundation (ASF) under one or more
dnl contributor license agreements. See the NOTICE file distributed with
dnl this work for additional information regarding copyright ownership.
dnl The ASF licenses this file to You under the Apache License, Version 2.0
dnl (the "License"); you may not use this file except in compliance with
dnl the License. You may obtain a copy of the License at
dnl
dnl http://www.apache.org/licenses/LICENSE-2.0
dnl
dnl Unless required by applicable law or agreed to in writing, software
dnl distributed under the License is distributed on an "AS IS" BASIS,
dnl WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
dnl See the License for the specific language governing permissions and
dnl limitations under the License.
dnl
dnl TS_ADDTO(variable, value)
dnl
dnl Add value to variable
dnl
AC_DEFUN([TS_ADDTO], [
if test "x$$1" = "x"; then
test "x$verbose" = "xyes" && echo " setting $1 to \"$2\""
$1="$2"
else
ats_addto_bugger="$2"
for i in $ats_addto_bugger; do
ats_addto_duplicate="0"
for j in $$1; do
if test "x$i" = "x$j"; then
ats_addto_duplicate="1"
break
fi
done
if test $ats_addto_duplicate = "0"; then
test "x$verbose" = "xyes" && echo " adding \"$i\" to $1"
$1="$$1 $i"
fi
done
fi
])dnl
dnl
dnl TS_ADDTO_RPATH(path)
dnl
dnl Adds path to variable with the '-rpath' directive.
dnl
AC_DEFUN([TS_ADDTO_RPATH], [
AC_MSG_NOTICE([adding $1 to RPATH])
TS_ADDTO(LIBTOOL_LINK_FLAGS, [-R$1])
])dnl
dnl
dnl pcre.m4: Trafficserver's pcre autoconf macros
dnl
dnl
dnl TS_CHECK_PCRE: look for pcre libraries and headers
dnl
AC_DEFUN([TS_CHECK_PCRE], [
enable_pcre=no
AC_ARG_WITH(pcre, [AC_HELP_STRING([--with-pcre=DIR],[use a specific pcre library])],
[
if test "x$withval" != "xyes" && test "x$withval" != "x"; then
pcre_base_dir="$withval"
if test "$withval" != "no"; then
enable_pcre=yes
case "$withval" in
*":"*)
pcre_include="`echo $withval |sed -e 's/:.*$//'`"
pcre_ldflags="`echo $withval |sed -e 's/^.*://'`"
AC_MSG_CHECKING(checking for pcre includes in $pcre_include libs in $pcre_ldflags )
;;
*)
pcre_include="$withval/include"
pcre_ldflags="$withval/lib"
AC_MSG_CHECKING(checking for pcre includes in $withval)
;;
esac
fi
fi
],
[
AC_CHECK_PROG(PCRE_CONFIG, pcre-config, pcre-config)
if test "x$PCRE_CONFIG" != "x"; then
enable_pcre=yes
pcre_base_dir="`$PCRE_CONFIG --prefix`"
pcre_include="`$PCRE_CONFIG --cflags | sed -es/-I//`"
pcre_ldflags="`$PCRE_CONFIG --libs | sed -es/-lpcre// -es/-L//`"
fi
])
if test "x$pcre_base_dir" = "x"; then
AC_MSG_CHECKING([for pcre location])
AC_CACHE_VAL(ats_cv_pcre_dir,[
for dir in /usr/local /usr ; do
if test -d $dir && ( test -f $dir/include/pcre.h || test -f $dir/include/pcre/pcre.h ); then
ats_cv_pcre_dir=$dir
break
fi
done
])
pcre_base_dir=$ats_cv_pcre_dir
if test "x$pcre_base_dir" = "x"; then
enable_pcre=no
AC_MSG_RESULT([not found])
else
enable_pcre=yes
pcre_include="$pcre_base_dir/include"
pcre_ldflags="$pcre_base_dir/lib"
AC_MSG_RESULT([$pcre_base_dir])
fi
else
AC_MSG_CHECKING(for pcre headers in $pcre_include)
if test -d $pcre_include && test -d $pcre_ldflags && ( test -f $pcre_include/pcre.h || test -f $pcre_include/pcre/pcre.h ); then
AC_MSG_RESULT([ok])
else
AC_MSG_RESULT([not found])
fi
fi
pcreh=0
pcre_pcreh=0
if test "$enable_pcre" != "no"; then
saved_ldflags=$LDFLAGS
saved_cppflags=$CFLAGS
pcre_have_headers=0
pcre_have_libs=0
if test "$pcre_base_dir" != "/usr"; then
TS_ADDTO(CFLAGS, [-I${pcre_include}])
TS_ADDTO(CFLAGS, [-DPCRE_STATIC])
TS_ADDTO(LDFLAGS, [-L${pcre_ldflags}])
TS_ADDTO_RPATH(${pcre_ldflags})
fi
AC_SEARCH_LIBS([pcre_exec], [pcre], [pcre_have_libs=1])
if test "$pcre_have_libs" != "0"; then
AC_CHECK_HEADERS(pcre.h, [pcre_have_headers=1])
AC_CHECK_HEADERS(pcre/pcre.h, [pcre_have_headers=1])
fi
if test "$pcre_have_headers" != "0"; then
AC_DEFINE(HAVE_LIBPCRE,1,[Compiling with pcre support])
AC_SUBST(LIBPCRE, [-lpcre])
else
enable_pcre=no
CFLAGS=$saved_cppflags
LDFLAGS=$saved_ldflags
fi
fi
AC_SUBST(pcreh)
AC_SUBST(pcre_pcreh)
])
-40
View File
@@ -1,40 +0,0 @@
dnl Check to find the libsodium headers/libraries
AC_DEFUN([ss_SODIUM],
[
AC_ARG_WITH(sodium,
AS_HELP_STRING([--with-sodium=DIR], [The Sodium crypto library base directory, or:]),
[sodium="$withval"
CFLAGS="$CFLAGS -I$withval/include"
LDFLAGS="$LDFLAGS -L$withval/lib"]
)
AC_ARG_WITH(sodium-include,
AS_HELP_STRING([--with-sodium-include=DIR], [The Sodium crypto library headers directory (without trailing /sodium)]),
[sodium_include="$withval"
CFLAGS="$CFLAGS -I$withval"]
)
AC_ARG_WITH(sodium-lib,
AS_HELP_STRING([--with-sodium-lib=DIR], [The Sodium crypto library library directory]),
[sodium_lib="$withval"
LDFLAGS="$LDFLAGS -L$withval"]
)
AC_CHECK_LIB(sodium, sodium_init,
[LIBS="-lsodium $LIBS"],
[AC_MSG_ERROR([The Sodium crypto library libraries not found.])]
)
AC_COMPILE_IFELSE([AC_LANG_PROGRAM([
#include <sodium.h>
], [
#if SODIUM_LIBRARY_VERSION_MAJOR < 7 || SODIUM_LIBRARY_VERSION_MAJOR ==7 && SODIUM_LIBRARY_VERSION_MINOR < 6
# error
#endif
])],
[AC_MSG_RESULT([checking for version of libsodium... yes])],
[AC_MSG_ERROR([Wrong libsodium: version >= 1.0.4 required])])
])
-58
View File
@@ -1,58 +0,0 @@
#
# Copyright 2007 Google Inc.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#
# GGL_CHECK_STACK_PROTECTOR([ACTION-IF-OK], [ACTION-IF-NOT-OK])
# Check if c compiler supports -fstack-protector and -fstack-protector-all
# options.
AC_DEFUN([GGL_CHECK_STACK_PROTECTOR], [
ggl_check_stack_protector_save_CXXFLAGS="$CXXFLAGS"
ggl_check_stack_protector_save_CFLAGS="$CFLAGS"
AC_MSG_CHECKING([if -fstack-protector and -fstack-protector-all are supported.])
CXXFLAGS="$CXXFLAGS -fstack-protector"
CFLAGS="$CFLAGS -fstack-protector"
AC_COMPILE_IFELSE([AC_LANG_SOURCE([
int main() {
return 0;
}
])],
[ggl_check_stack_protector_ok=yes],
[ggl_check_stack_protector_ok=no])
CXXFLAGS="$ggl_check_stack_protector_save_CXXFLAGS -fstack-protector-all"
CFLAGS="$ggl_check_stack_protector_save_CFLAGS -fstack-protector-all"
AC_COMPILE_IFELSE([AC_LANG_SOURCE([
int main() {
return 0;
}
])],
[ggl_check_stack_protector_all_ok=yes],
[ggl_check_stack_protector_all_ok=no])
if test "x$ggl_check_stack_protector_ok" = "xyes" -a \
"x$ggl_check_stack_protector_all_ok" = "xyes"; then
AC_MSG_RESULT([yes])
ifelse([$1], , :, [$1])
else
AC_MSG_RESULT([no])
ifelse([$2], , :, [$2])
fi
CXXFLAGS="$ggl_check_stack_protector_save_CXXFLAGS"
CFLAGS="$ggl_check_stack_protector_save_CFLAGS"
]) # GGL_CHECK_STACK_PROTECTOR
+1367
View File
File diff suppressed because it is too large Load Diff
-12
View File
@@ -1,12 +0,0 @@
prefix=@prefix@
exec_prefix=@exec_prefix@
libdir=@libdir@
includedir=@includedir@
Name: shadowsocks-libev
Description: a lightweight secured socks5 proxy
URL: https://shadowsocks.org
Version: @VERSION@
Requires:
Cflags: -I${includedir}
Libs: -L${libdir} -lshadowsocks-libev
+71 -23
View File
@@ -84,25 +84,29 @@ set(SS_REDIR_SOURCE
${SS_PLUGIN_SOURCE}
)
if (CMAKE_SYSTEM_NAME STREQUAL Darwin)
find_path(LIBSODIUM_INCLUDE_DIR sodium.h
PATHS
$ENV{LIBSODIUM_INCLUDE_DIR}
$ENV{LIBSODIUM_DIR}/include
/usr/local/libsodium/include
/opt/libsodium/include
/usr/local/include
)
include_directories(${LIBSODIUM_INCLUDE_DIR})
endif ()
# Apply -Werror only to project sources (not bundled submodules)
set(SS_WERROR_FLAGS -Werror)
# Include directories from Find modules
# MbedTLS must come first to avoid conflicts with other mbedtls versions
# that may be in /opt/homebrew/include (e.g. mbedtls 4.x vs 3.x)
include_directories(BEFORE ${MBEDTLS_INCLUDE_DIRS})
include_directories(${SODIUM_INCLUDE_DIRS})
include_directories(${PCRE2_INCLUDE_DIRS})
include_directories(${CARES_INCLUDE_DIRS})
# Extract mbedTLS lib dir to ensure static/shared libs come from same version
get_filename_component(_MBEDTLS_LIB_DIR "${MBEDTLS_CRYPTO_LIBRARY}" DIRECTORY)
if (WITH_STATIC)
find_library(LIBSODIUM libsodium.a)
find_library(LIBMBEDTLS libmbedtls.a HINTS ${_MBEDTLS_LIB_DIR} NO_DEFAULT_PATH)
find_library(LIBMBEDTLS libmbedtls.a)
find_library(LIBMBEDCRYPTO libmbedcrypto.a HINTS ${_MBEDTLS_LIB_DIR} NO_DEFAULT_PATH)
find_library(LIBMBEDCRYPTO libmbedcrypto.a)
find_library(LIBEV libev.a)
find_library(LIBUDNS libcares.a)
find_library(LIBPCRE libpcre.a)
find_library(LIBPCRE2 libpcre2-8.a)
# Dependencies we need for static and shared
list(APPEND DEPS
@@ -110,7 +114,7 @@ list(APPEND DEPS
bloom
${LIBEV}
${LIBUDNS}
${LIBPCRE}
${LIBPCRE2}
${LIBSODIUM}
${LIBMBEDTLS}
${LIBMBEDCRYPTO}
@@ -118,16 +122,18 @@ list(APPEND DEPS
if (MINGW)
list(APPEND DEPS ws2_32 iphlpapi)
add_compile_definitions(CARES_STATICLIB PCRE_STATIC)
add_compile_definitions(CARES_STATICLIB PCRE2_STATIC)
endif ()
endif ()
find_library(LIBSODIUM_SHARED sodium)
find_library(LIBMBEDTLS_SHARED mbedtls HINTS ${_MBEDTLS_LIB_DIR} NO_DEFAULT_PATH)
find_library(LIBMBEDTLS_SHARED mbedtls)
find_library(LIBMBEDCRYPTO_SHARED mbedcrypto HINTS ${_MBEDTLS_LIB_DIR} NO_DEFAULT_PATH)
find_library(LIBMBEDCRYPTO_SHARED mbedcrypto)
find_library(LIBEV_SHARED ev)
find_library(LIBUDNS_SHARED cares)
find_library(LIBPCRE_SHARED pcre)
find_library(LIBPCRE2_SHARED pcre2-8)
if (WITH_EMBEDDED_SRC)
list(APPEND DEPS_SHARED
@@ -137,7 +143,7 @@ list(APPEND DEPS_SHARED
ipset-shared
${LIBEV_SHARED}
${LIBUDNS_SHARED}
${LIBPCRE_SHARED}
${LIBPCRE2_SHARED}
${LIBSODIUM_SHARED}
${LIBMBEDTLS_SHARED}
${LIBMBEDCRYPTO_SHARED}
@@ -153,13 +159,24 @@ list(APPEND DEPS_SHARED
${LIBCORKIPSET_SHARED}
${LIBEV_SHARED}
${LIBUDNS_SHARED}
${LIBPCRE_SHARED}
${LIBPCRE2_SHARED}
${LIBSODIUM_SHARED}
${LIBMBEDTLS_SHARED}
${LIBMBEDCRYPTO_SHARED}
)
endif ()
# Connmarktos/nftables libraries for ss-server
if(ENABLE_CONNMARKTOS)
list(APPEND DEPS_CONNMARKTOS ${NETFILTER_CONNTRACK_LIB})
if(NFNETLINK_LIB)
list(APPEND DEPS_CONNMARKTOS ${NFNETLINK_LIB})
endif()
endif()
if(ENABLE_NFTABLES)
list(APPEND DEPS_NFTABLES ${MNL_LIB} ${NFTNL_LIB})
endif()
find_package (Threads)
if (WITH_STATIC)
@@ -189,9 +206,24 @@ target_compile_definitions(ss-local PUBLIC -DMODULE_LOCAL)
target_compile_definitions(ss-redir PUBLIC -DMODULE_REDIR)
target_compile_definitions(shadowsocks-libev PUBLIC -DMODULE_LOCAL -DLIB_ONLY)
target_compile_options(ss-server PRIVATE ${SS_WERROR_FLAGS})
target_compile_options(ss-tunnel PRIVATE ${SS_WERROR_FLAGS})
target_compile_options(ss-manager PRIVATE ${SS_WERROR_FLAGS})
target_compile_options(ss-local PRIVATE ${SS_WERROR_FLAGS})
target_compile_options(ss-redir PRIVATE ${SS_WERROR_FLAGS})
target_compile_options(shadowsocks-libev PRIVATE ${SS_WERROR_FLAGS})
# Connmarktos/nftables compile definitions
if(ENABLE_CONNMARKTOS)
target_compile_definitions(ss-server PUBLIC -DUSE_NFCONNTRACK_TOS)
endif()
if(ENABLE_NFTABLES)
target_compile_definitions(ss-server PUBLIC -DUSE_NFTABLES)
endif()
target_include_directories(shadowsocks-libev PUBLIC ${CMAKE_CURRENT_SOURCE_DIR})
target_link_libraries(ss-server cork ipset ${DEPS})
target_link_libraries(ss-server cork ipset ${DEPS} ${DEPS_CONNMARKTOS} ${DEPS_NFTABLES})
target_link_libraries(ss-tunnel cork ${DEPS})
target_link_libraries(ss-manager m bloom cork ${LIBEV} ${LIBUDNS})
target_link_libraries(ss-local cork ipset ${DEPS})
@@ -226,9 +258,24 @@ target_compile_definitions(ss-local-shared PUBLIC -DMODULE_LOCAL)
target_compile_definitions(ss-redir-shared PUBLIC -DMODULE_REDIR)
target_compile_definitions(shadowsocks-libev-shared PUBLIC -DMODULE_LOCAL -DLIB_ONLY)
target_compile_options(ss-server-shared PRIVATE ${SS_WERROR_FLAGS})
target_compile_options(ss-tunnel-shared PRIVATE ${SS_WERROR_FLAGS})
target_compile_options(ss-manager-shared PRIVATE ${SS_WERROR_FLAGS})
target_compile_options(ss-local-shared PRIVATE ${SS_WERROR_FLAGS})
target_compile_options(ss-redir-shared PRIVATE ${SS_WERROR_FLAGS})
target_compile_options(shadowsocks-libev-shared PRIVATE ${SS_WERROR_FLAGS})
# Connmarktos/nftables compile definitions for shared
if(ENABLE_CONNMARKTOS)
target_compile_definitions(ss-server-shared PUBLIC -DUSE_NFCONNTRACK_TOS)
endif()
if(ENABLE_NFTABLES)
target_compile_definitions(ss-server-shared PUBLIC -DUSE_NFTABLES)
endif()
target_include_directories(shadowsocks-libev-shared PUBLIC ${CMAKE_CURRENT_SOURCE_DIR})
target_link_libraries(ss-server-shared ${DEPS_SHARED})
target_link_libraries(ss-server-shared ${DEPS_SHARED} ${DEPS_CONNMARKTOS} ${DEPS_NFTABLES})
target_link_libraries(ss-tunnel-shared ${DEPS_SHARED})
target_link_libraries(ss-manager-shared ${CMAKE_THREAD_LIBS_INIT} ${LIBEV_SHARED} ${LIBUDNS_SHARED} ${DEPS_SHARED})
target_link_libraries(ss-local-shared ${DEPS_SHARED})
@@ -247,6 +294,7 @@ set_target_properties(ss-server-shared ss-tunnel-shared ss-manager-shared ss-loc
)
set_target_properties(shadowsocks-libev-shared PROPERTIES OUTPUT_NAME shadowsocks-libev)
set_target_properties(shadowsocks-libev-shared PROPERTIES VERSION 2.0.0 SOVERSION 2)
target_compile_definitions(shadowsocks-libev-shared PUBLIC -DMODULE_LOCAL)
target_link_libraries(shadowsocks-libev-shared ${DEPS_SHARED})
@@ -255,17 +303,17 @@ target_link_libraries(shadowsocks-libev-shared ${DEPS_SHARED})
# Recommend to install shared by default
install(DIRECTORY ${RUNTIME_SHARED_OUTPUT_DIRECTORY}/
USE_SOURCE_PERMISSIONS
DESTINATION bin)
DESTINATION ${CMAKE_INSTALL_BINDIR})
if (WITH_STATIC)
install(TARGETS shadowsocks-libev
ARCHIVE DESTINATION lib)
ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR})
endif ()
install(TARGETS shadowsocks-libev-shared
LIBRARY DESTINATION lib)
LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR})
install(FILES shadowsocks.h DESTINATION include)
install(FILES shadowsocks.h DESTINATION ${CMAKE_INSTALL_INCLUDEDIR})
add_custom_target(distclean
-115
View File
@@ -1,115 +0,0 @@
VERSION_INFO = 2:0:0
AM_CFLAGS = -g -O2 -Wall -Werror -Wno-deprecated-declarations -fno-strict-aliasing -std=gnu99 -D_GNU_SOURCE
AM_CFLAGS += $(PTHREAD_CFLAGS)
if !USE_SYSTEM_SHARED_LIB
AM_CFLAGS += -I$(top_srcdir)/libbloom
AM_CFLAGS += -I$(top_srcdir)/libipset/include
AM_CFLAGS += -I$(top_srcdir)/libcork/include
endif
AM_CFLAGS += $(LIBPCRE_CFLAGS)
SS_COMMON_LIBS = $(INET_NTOP_LIB) $(LIBPCRE_LIBS) $(NETFILTER_CONNTRACK_LIBS) $(NFTABLES_LIBS)
if !USE_SYSTEM_SHARED_LIB
SS_COMMON_LIBS += $(top_builddir)/libbloom/libbloom.la \
$(top_builddir)/libipset/libipset.la \
$(top_builddir)/libcork/libcork.la
else
SS_COMMON_LIBS += -lbloom -lcork -lcorkipset
endif
SS_COMMON_LIBS += -lev -lsodium -lm
bin_PROGRAMS = ss-local ss-tunnel ss-server
if !BUILD_WINCOMPAT
bin_PROGRAMS += ss-manager
endif
acl_src = rule.c \
acl.c
crypto_src = crypto.c \
aead.c \
stream.c \
ppbloom.c \
base64.c
plugin_src = plugin.c
common_src = utils.c \
jconf.c \
json.c \
udprelay.c \
cache.c \
netutils.c
if BUILD_WINCOMPAT
common_src += winsock.c
endif
ss_local_SOURCES = local.c \
$(common_src) \
$(crypto_src) \
$(plugin_src) \
$(acl_src)
ss_tunnel_SOURCES = tunnel.c \
$(common_src) \
$(crypto_src) \
$(plugin_src)
ss_server_SOURCES = resolv.c \
server.c \
$(common_src) \
$(crypto_src) \
$(plugin_src) \
${acl_src}
ss_manager_SOURCES = utils.c \
jconf.c \
json.c \
netutils.c \
manager.c
ss_local_LDADD = $(SS_COMMON_LIBS)
ss_tunnel_LDADD = $(SS_COMMON_LIBS)
ss_server_LDADD = $(SS_COMMON_LIBS)
ss_manager_LDADD = $(SS_COMMON_LIBS)
ss_local_LDADD += -lcares
ss_tunnel_LDADD += -lcares
ss_server_LDADD += -lcares
ss_manager_LDADD += -lcares
ss_local_CFLAGS = $(AM_CFLAGS) -DMODULE_LOCAL
ss_tunnel_CFLAGS = $(AM_CFLAGS) -DMODULE_TUNNEL
ss_server_CFLAGS = $(AM_CFLAGS) -DMODULE_REMOTE
ss_manager_CFLAGS = $(AM_CFLAGS) -DMODULE_MANAGER
if BUILD_REDIRECTOR
bin_SCRIPTS = ss-nat
bin_PROGRAMS += ss-redir
ss_redir_SOURCES = utils.c \
jconf.c \
json.c \
netutils.c \
cache.c \
udprelay.c \
redir.c \
$(crypto_src) \
$(plugin_src)
ss_redir_CFLAGS = $(AM_CFLAGS) -DMODULE_REDIR
ss_redir_LDADD = $(SS_COMMON_LIBS)
ss_redir_LDADD += -lcares
endif
lib_LTLIBRARIES = libshadowsocks-libev.la
libshadowsocks_libev_la_SOURCES = $(ss_local_SOURCES)
libshadowsocks_libev_la_CFLAGS = $(ss_local_CFLAGS) -DLIB_ONLY
libshadowsocks_libev_la_LDFLAGS = -version-info $(VERSION_INFO)
libshadowsocks_libev_la_LIBADD = $(ss_local_LDADD)
include_HEADERS = shadowsocks.h
noinst_HEADERS = acl.h crypto.h stream.h aead.h json.h netutils.h redir.h server.h uthash.h \
cache.h local.h plugin.h resolv.h tunnel.h utils.h base64.h ppbloom.h \
common.h jconf.h manager.h rule.h socks5.h udprelay.h winsock.h
EXTRA_DIST = ss-nat
+14 -7
View File
@@ -78,16 +78,21 @@ int
init_rule(rule_t *rule)
{
if (rule->pattern_re == NULL) {
const char *reerr;
int reerroffset;
int errcode;
PCRE2_SIZE erroffset;
rule->pattern_re =
pcre_compile(rule->pattern, 0, &reerr, &reerroffset, NULL);
pcre2_compile((PCRE2_SPTR)rule->pattern, PCRE2_ZERO_TERMINATED,
0, &errcode, &erroffset, NULL);
if (rule->pattern_re == NULL) {
PCRE2_UCHAR errbuf[256];
pcre2_get_error_message(errcode, errbuf, sizeof(errbuf));
LOGE("Regex compilation of \"%s\" failed: %s, offset %d",
rule->pattern, reerr, reerroffset);
rule->pattern, errbuf, (int)erroffset);
return 0;
}
rule->match_data = pcre2_match_data_create_from_pattern(
rule->pattern_re, NULL);
}
return 1;
@@ -105,8 +110,8 @@ lookup_rule(const struct cork_dllist *rules, const char *name, size_t name_len)
cork_dllist_foreach_void(rules, curr, next) {
rule_t *rule = cork_container_of(curr, rule_t, entries);
if (pcre_exec(rule->pattern_re, NULL,
name, name_len, 0, 0, NULL, 0) >= 0)
if (pcre2_match(rule->pattern_re, (PCRE2_SPTR)name,
name_len, 0, 0, rule->match_data, NULL) >= 0)
return rule;
}
@@ -127,7 +132,9 @@ free_rule(rule_t *rule)
return;
ss_free(rule->pattern);
if (rule->match_data != NULL)
pcre2_match_data_free(rule->match_data);
if (rule->pattern_re != NULL)
pcre_free(rule->pattern_re);
pcre2_code_free(rule->pattern_re);
ss_free(rule);
}
+4 -6
View File
@@ -33,17 +33,15 @@
#include <libcork/ds.h>
#ifdef HAVE_PCRE_H
#include <pcre.h>
#elif HAVE_PCRE_PCRE_H
#include <pcre/pcre.h>
#endif
#define PCRE2_CODE_UNIT_WIDTH 8
#include <pcre2.h>
typedef struct rule {
char *pattern;
/* Runtime fields */
pcre *pattern_re;
pcre2_code *pattern_re;
pcre2_match_data *match_data;
struct cork_dllist_item entries;
} rule_t;
+123
View File
@@ -0,0 +1,123 @@
# Unit tests using CTest
include_directories(BEFORE ${MBEDTLS_INCLUDE_DIRS})
include_directories(${PROJECT_SOURCE_DIR}/src)
include_directories(${PROJECT_BINARY_DIR}/src)
include_directories(${SODIUM_INCLUDE_DIRS})
include_directories(${PCRE2_INCLUDE_DIRS})
include_directories(${CARES_INCLUDE_DIRS})
if(WITH_EMBEDDED_SRC)
include_directories(${PROJECT_SOURCE_DIR}/libcork/include)
include_directories(${PROJECT_SOURCE_DIR}/libipset/include)
include_directories(${PROJECT_SOURCE_DIR}/libbloom/murmur2)
include_directories(${PROJECT_SOURCE_DIR}/libbloom)
endif()
# Helper function to add a unit test
function(ss_add_test name sources libs)
add_executable(${name} ${sources})
target_compile_definitions(${name} PRIVATE -DHAVE_CONFIG_H)
target_link_libraries(${name} ${libs})
add_test(NAME ${name} COMMAND ${name})
endfunction()
# test_base64 - no external deps
ss_add_test(test_base64
"test_base64.c;${PROJECT_SOURCE_DIR}/src/base64.c"
"")
# test_utils - needs sodium (for ss_malloc -> uses sodium in utils.c)
ss_add_test(test_utils
"test_utils.c;${PROJECT_SOURCE_DIR}/src/utils.c"
"${LIBSODIUM_SHARED}")
# test_json - standalone json parser
ss_add_test(test_json
"test_json.c;${PROJECT_SOURCE_DIR}/src/json.c"
"m")
# test_netutils - needs cork for ip address parsing
set(TEST_NETUTILS_LIBS
${LIBSODIUM_SHARED}
${LIBEV_SHARED}
${LIBUDNS_SHARED}
)
if(WITH_EMBEDDED_SRC)
list(APPEND TEST_NETUTILS_LIBS cork-shared)
else()
list(APPEND TEST_NETUTILS_LIBS ${LIBCORK_SHARED})
endif()
ss_add_test(test_netutils
"test_netutils.c;${PROJECT_SOURCE_DIR}/src/netutils.c;${PROJECT_SOURCE_DIR}/src/utils.c"
"${TEST_NETUTILS_LIBS}")
# test_cache - needs libev for ev_time()
ss_add_test(test_cache
"test_cache.c;${PROJECT_SOURCE_DIR}/src/cache.c;${PROJECT_SOURCE_DIR}/src/utils.c"
"${LIBEV_SHARED};${LIBSODIUM_SHARED}")
# test_ppbloom - needs bloom
set(TEST_PPBLOOM_LIBS "")
if(WITH_EMBEDDED_SRC)
list(APPEND TEST_PPBLOOM_LIBS bloom-shared)
else()
list(APPEND TEST_PPBLOOM_LIBS ${LIBBLOOM_SHARED})
endif()
ss_add_test(test_ppbloom
"test_ppbloom.c;${PROJECT_SOURCE_DIR}/src/ppbloom.c;${PROJECT_SOURCE_DIR}/src/utils.c"
"${TEST_PPBLOOM_LIBS};${LIBSODIUM_SHARED};m")
# test_rule - needs pcre and cork
set(TEST_RULE_LIBS
${LIBPCRE2_SHARED}
${LIBSODIUM_SHARED}
)
if(WITH_EMBEDDED_SRC)
list(APPEND TEST_RULE_LIBS cork-shared)
else()
list(APPEND TEST_RULE_LIBS ${LIBCORK_SHARED})
endif()
ss_add_test(test_rule
"test_rule.c;${PROJECT_SOURCE_DIR}/src/rule.c;${PROJECT_SOURCE_DIR}/src/utils.c"
"${TEST_RULE_LIBS}")
# test_jconf - needs cork and json
set(TEST_JCONF_LIBS
${LIBSODIUM_SHARED}
${LIBEV_SHARED}
${LIBUDNS_SHARED}
)
if(WITH_EMBEDDED_SRC)
list(APPEND TEST_JCONF_LIBS cork-shared)
else()
list(APPEND TEST_JCONF_LIBS ${LIBCORK_SHARED})
endif()
ss_add_test(test_jconf
"test_jconf.c;${PROJECT_SOURCE_DIR}/src/jconf.c;${PROJECT_SOURCE_DIR}/src/json.c;${PROJECT_SOURCE_DIR}/src/utils.c;${PROJECT_SOURCE_DIR}/src/netutils.c"
"${TEST_JCONF_LIBS};m")
# test_buffer - needs sodium and mbedtls for crypto.c buffer functions
ss_add_test(test_buffer
"test_buffer.c;${PROJECT_SOURCE_DIR}/src/crypto.c;${PROJECT_SOURCE_DIR}/src/utils.c;${PROJECT_SOURCE_DIR}/src/base64.c;${PROJECT_SOURCE_DIR}/src/aead.c;${PROJECT_SOURCE_DIR}/src/stream.c;${PROJECT_SOURCE_DIR}/src/ppbloom.c;${PROJECT_SOURCE_DIR}/src/cache.c"
"${LIBSODIUM_SHARED};${LIBMBEDTLS_SHARED};${LIBMBEDCRYPTO_SHARED};${LIBEV_SHARED};${TEST_PPBLOOM_LIBS};m")
# test_crypto - full crypto test
set(TEST_CRYPTO_LIBS
${LIBSODIUM_SHARED}
${LIBMBEDTLS_SHARED}
${LIBMBEDCRYPTO_SHARED}
${LIBEV_SHARED}
)
if(WITH_EMBEDDED_SRC)
list(APPEND TEST_CRYPTO_LIBS bloom-shared)
else()
list(APPEND TEST_CRYPTO_LIBS ${LIBBLOOM_SHARED})
endif()
ss_add_test(test_crypto
"test_crypto.c;${PROJECT_SOURCE_DIR}/src/crypto.c;${PROJECT_SOURCE_DIR}/src/aead.c;${PROJECT_SOURCE_DIR}/src/stream.c;${PROJECT_SOURCE_DIR}/src/ppbloom.c;${PROJECT_SOURCE_DIR}/src/cache.c;${PROJECT_SOURCE_DIR}/src/base64.c;${PROJECT_SOURCE_DIR}/src/utils.c"
"${TEST_CRYPTO_LIBS};m")
# test_ss_setup - bash unit tests for the ss-setup TUI tool
add_test(NAME test_ss_setup
COMMAND bash "${PROJECT_SOURCE_DIR}/tests/test_ss_setup.sh"
WORKING_DIRECTORY "${PROJECT_SOURCE_DIR}")
+445
View File
@@ -0,0 +1,445 @@
#!/usr/bin/env python3
"""
Stress test for shadowsocks-libev: measures bandwidth on loopback
with different ciphers using ss-server and ss-tunnel, and monitors
for memory leaks.
Usage:
python3 tests/stress_test.py --bin build/bin/
python3 tests/stress_test.py --bin build/bin/ --size 50 --duration 10
python3 tests/stress_test.py --bin build/bin/ --cipher aes-256-gcm
"""
from __future__ import print_function
import argparse
import json
import os
import platform
import signal
import socket
import sys
import threading
import time
from subprocess import Popen
# Ciphers to test: AEAD (recommended) + common stream ciphers
AEAD_CIPHERS = [
"aes-128-gcm",
"aes-256-gcm",
"chacha20-ietf-poly1305",
]
STREAM_CIPHERS = [
"aes-128-cfb",
"aes-256-cfb",
"aes-256-ctr",
"chacha20-ietf",
]
ALL_CIPHERS = AEAD_CIPHERS + STREAM_CIPHERS
# Ports (picked high to avoid conflicts)
SERVER_PORT = 18388
TUNNEL_LOCAL_PORT = 18389
SINK_PORT = 18390
def get_rss_kb(pid):
"""Get resident set size in KB for a process."""
system = platform.system()
try:
if system == "Linux":
with open("/proc/%d/status" % pid) as f:
for line in f:
if line.startswith("VmRSS:"):
return int(line.split()[1])
elif system == "Darwin":
import subprocess
out = subprocess.check_output(
["ps", "-o", "rss=", "-p", str(pid)],
stderr=subprocess.DEVNULL
).decode().strip()
if out:
return int(out)
except (IOError, OSError, ValueError):
pass
return None
class SinkServer:
"""A simple TCP server that accepts connections and discards all data.
Sends back a small acknowledgement per chunk so the sender knows
data was received (for accurate bandwidth measurement)."""
def __init__(self, port):
self.port = port
self.sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
self.sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
self.sock.bind(("127.0.0.1", port))
self.sock.listen(5)
self.sock.settimeout(1.0)
self.running = True
self.bytes_received = 0
self.thread = threading.Thread(target=self._run, daemon=True)
self.thread.start()
def _run(self):
while self.running:
try:
conn, addr = self.sock.accept()
t = threading.Thread(
target=self._handle, args=(conn,), daemon=True
)
t.start()
except socket.timeout:
continue
except OSError:
break
def _handle(self, conn):
conn.settimeout(5.0)
try:
while self.running:
data = conn.recv(65536)
if not data:
break
self.bytes_received += len(data)
except (socket.timeout, OSError):
pass
finally:
conn.close()
def stop(self):
self.running = False
self.sock.close()
self.thread.join(timeout=3)
def server_args(ss_server, cipher, password, server_port):
"""Build command-line args for ss-server."""
return [
ss_server,
"-s", "127.0.0.1",
"-p", str(server_port),
"-k", password,
"-m", cipher,
]
def tunnel_args(ss_tunnel, cipher, password, server_port, local_port, fwd_host, fwd_port):
"""Build command-line args for ss-tunnel."""
return [
ss_tunnel,
"-s", "127.0.0.1",
"-p", str(server_port),
"-l", str(local_port),
"-k", password,
"-m", cipher,
"-L", "%s:%d" % (fwd_host, fwd_port),
]
def kill_proc(proc):
"""Kill a process gracefully."""
if proc and proc.poll() is None:
try:
proc.send_signal(signal.SIGTERM)
proc.wait(timeout=5)
except Exception:
try:
proc.kill()
proc.wait(timeout=3)
except Exception:
pass
def run_bandwidth_test(bin_dir, cipher, data_size_mb, password="stress_test_pw"):
"""Run a single bandwidth test with the given cipher.
Returns dict with: cipher, bandwidth_mbps, duration_sec,
server_rss_before_kb, server_rss_after_kb, tunnel_rss_before_kb,
tunnel_rss_after_kb, bytes_transferred, error
"""
result = {
"cipher": cipher,
"bandwidth_mbps": 0,
"duration_sec": 0,
"bytes_transferred": 0,
"server_rss_before_kb": None,
"server_rss_after_kb": None,
"tunnel_rss_before_kb": None,
"tunnel_rss_after_kb": None,
"error": None,
}
ss_server_bin = os.path.join(bin_dir, "ss-server")
ss_tunnel_bin = os.path.join(bin_dir, "ss-tunnel")
if not os.path.isfile(ss_server_bin) or not os.path.isfile(ss_tunnel_bin):
result["error"] = "binaries not found in %s" % bin_dir
return result
server_proc = None
tunnel_proc = None
sink = None
devnull = open(os.devnull, "w")
try:
# Start sink server (the destination ss-tunnel forwards to)
sink = SinkServer(SINK_PORT)
# Start ss-server
server_proc = Popen(
server_args(ss_server_bin, cipher, password, SERVER_PORT),
stdout=devnull, stderr=devnull, close_fds=True
)
# Start ss-tunnel forwarding to the sink
tunnel_proc = Popen(
tunnel_args(ss_tunnel_bin, cipher, password, SERVER_PORT,
TUNNEL_LOCAL_PORT, "127.0.0.1", SINK_PORT),
stdout=devnull, stderr=devnull, close_fds=True
)
# Wait for processes to start and bind ports
# Don't probe the ports as ss-tunnel crashes on empty connections
time.sleep(3.0)
if server_proc.poll() is not None:
result["error"] = "ss-server exited prematurely (code %d)" % server_proc.returncode
return result
if tunnel_proc.poll() is not None:
result["error"] = "ss-tunnel exited prematurely (code %d)" % tunnel_proc.returncode
return result
# Record RSS before
result["server_rss_before_kb"] = get_rss_kb(server_proc.pid)
result["tunnel_rss_before_kb"] = get_rss_kb(tunnel_proc.pid)
# Send data through the tunnel
chunk_size = 64 * 1024 # 64KB chunks
total_bytes = data_size_mb * 1024 * 1024
data_chunk = b"\x00" * chunk_size
sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
sock.settimeout(30)
try:
sock.connect(("127.0.0.1", TUNNEL_LOCAL_PORT))
except (socket.error, OSError) as e:
result["error"] = "connect to tunnel failed: %s" % e
return result
sent = 0
start_time = time.time()
try:
while sent < total_bytes:
remaining = total_bytes - sent
to_send = min(chunk_size, remaining)
if to_send < chunk_size:
data_chunk = b"\x00" * to_send
sock.sendall(data_chunk)
sent += to_send
except (socket.error, OSError) as e:
result["error"] = "send error at %d bytes: %s" % (sent, e)
finally:
elapsed = time.time() - start_time
try:
sock.shutdown(socket.SHUT_WR)
except OSError:
pass
sock.close()
# Wait a bit for data to flow through
time.sleep(0.5)
result["bytes_transferred"] = sent
result["duration_sec"] = elapsed
if elapsed > 0:
result["bandwidth_mbps"] = (sent * 8) / (elapsed * 1e6)
# Record RSS after
result["server_rss_after_kb"] = get_rss_kb(server_proc.pid)
result["tunnel_rss_after_kb"] = get_rss_kb(tunnel_proc.pid)
except Exception as e:
result["error"] = str(e)
finally:
kill_proc(tunnel_proc)
kill_proc(server_proc)
if sink:
sink.stop()
devnull.close()
return result
def format_size(nbytes):
if nbytes >= 1024 * 1024 * 1024:
return "%.1f GB" % (nbytes / (1024 * 1024 * 1024))
elif nbytes >= 1024 * 1024:
return "%.1f MB" % (nbytes / (1024 * 1024))
elif nbytes >= 1024:
return "%.1f KB" % (nbytes / 1024)
return "%d B" % nbytes
def format_rss(kb):
if kb is None:
return "N/A"
if kb >= 1024:
return "%.1f MB" % (kb / 1024.0)
return "%d KB" % kb
def main():
parser = argparse.ArgumentParser(
description="Stress test ss-server + ss-tunnel bandwidth on loopback"
)
parser.add_argument(
"--bin", type=str, required=True,
help="Path to directory containing ss-server and ss-tunnel binaries"
)
parser.add_argument(
"--size", type=int, default=100,
help="Data size to transfer per cipher in MB (default: 100)"
)
parser.add_argument(
"--cipher", type=str, default=None,
help="Test only this specific cipher"
)
parser.add_argument(
"--repeat", type=int, default=1,
help="Number of times to repeat each cipher test (default: 1)"
)
parser.add_argument(
"--stream", action="store_true",
help="Include stream ciphers (deprecated, insecure)"
)
parser.add_argument(
"--leak-threshold", type=int, default=10240,
help="RSS growth threshold in KB to flag as potential leak (default: 10240)"
)
parser.add_argument(
"--json", type=str, default=None,
help="Write results as JSON to this file"
)
args = parser.parse_args()
# Determine ciphers to test
if args.cipher:
ciphers = [args.cipher]
elif args.stream:
ciphers = ALL_CIPHERS
else:
ciphers = AEAD_CIPHERS
print("=" * 72)
print("shadowsocks-libev stress test")
print("=" * 72)
print(" Binaries : %s" % os.path.abspath(args.bin))
print(" Data size: %d MB per cipher" % args.size)
print(" Ciphers : %s" % ", ".join(ciphers))
print(" Repeats : %d" % args.repeat)
print("=" * 72)
print()
all_results = []
leak_warnings = []
for cipher in ciphers:
for run in range(args.repeat):
label = cipher
if args.repeat > 1:
label = "%s (run %d/%d)" % (cipher, run + 1, args.repeat)
sys.stdout.write("Testing %-35s ... " % label)
sys.stdout.flush()
result = run_bandwidth_test(args.bin, cipher, args.size)
all_results.append(result)
if result["error"]:
print("FAILED: %s" % result["error"])
continue
bw = result["bandwidth_mbps"]
dur = result["duration_sec"]
print("%8.1f Mbps (%5.2fs, %s)" % (
bw, dur, format_size(result["bytes_transferred"])
))
# Check for memory leaks
for role in ["server", "tunnel"]:
before = result["%s_rss_before_kb" % role]
after = result["%s_rss_after_kb" % role]
if before is not None and after is not None:
growth = after - before
if growth > args.leak_threshold:
msg = (
" WARNING: ss-%s RSS grew by %s "
"(%s -> %s) during %s test"
% (role, format_rss(growth),
format_rss(before), format_rss(after), cipher)
)
print(msg)
leak_warnings.append(msg)
# Summary
print()
print("=" * 72)
print("RESULTS SUMMARY")
print("=" * 72)
print()
print("%-30s %10s %8s %12s %12s" % (
"Cipher", "Bandwidth", "Time", "Server RSS", "Tunnel RSS"
))
print("-" * 72)
for r in all_results:
if r["error"]:
print("%-30s %10s" % (r["cipher"], "FAILED"))
continue
srv_rss = ""
tun_rss = ""
if r["server_rss_before_kb"] and r["server_rss_after_kb"]:
srv_rss = "%s->%s" % (
format_rss(r["server_rss_before_kb"]),
format_rss(r["server_rss_after_kb"])
)
if r["tunnel_rss_before_kb"] and r["tunnel_rss_after_kb"]:
tun_rss = "%s->%s" % (
format_rss(r["tunnel_rss_before_kb"]),
format_rss(r["tunnel_rss_after_kb"])
)
print("%-30s %7.1f Mbps %6.2fs %12s %12s" % (
r["cipher"], r["bandwidth_mbps"], r["duration_sec"],
srv_rss or "N/A", tun_rss or "N/A"
))
if leak_warnings:
print()
print("MEMORY LEAK WARNINGS:")
for w in leak_warnings:
print(w)
print()
sys.exit(1)
print()
passed = [r for r in all_results if not r["error"]]
failed = [r for r in all_results if r["error"]]
print("%d passed, %d failed" % (len(passed), len(failed)))
if args.json:
with open(args.json, "w") as f:
json.dump(all_results, f, indent=2)
print("Results written to %s" % args.json)
if failed:
sys.exit(1)
if __name__ == "__main__":
main()
+116
View File
@@ -0,0 +1,116 @@
#include <assert.h>
#include <string.h>
#include <stdint.h>
#include "base64.h"
static void
test_encode_decode(void)
{
/* Known test vector: "Hello" */
const uint8_t input[] = "Hello";
char encoded[BASE64_SIZE(5)];
uint8_t decoded[5];
char *ret = base64_encode(encoded, sizeof(encoded), input, 5);
assert(ret != NULL);
assert(strlen(encoded) > 0);
int decoded_len = base64_decode(decoded, encoded, sizeof(decoded));
assert(decoded_len == 5);
assert(memcmp(decoded, input, 5) == 0);
}
static void
test_empty_input(void)
{
char encoded[BASE64_SIZE(0)];
char *ret = base64_encode(encoded, sizeof(encoded), (const uint8_t *)"", 0);
assert(ret != NULL);
assert(strlen(encoded) == 0);
}
static void
test_single_byte(void)
{
const uint8_t input[] = { 0x41 }; /* 'A' */
char encoded[BASE64_SIZE(1)];
uint8_t decoded[1];
char *ret = base64_encode(encoded, sizeof(encoded), input, 1);
assert(ret != NULL);
int decoded_len = base64_decode(decoded, encoded, sizeof(decoded));
assert(decoded_len == 1);
assert(decoded[0] == 0x41);
}
static void
test_two_bytes(void)
{
const uint8_t input[] = { 0x41, 0x42 }; /* "AB" */
char encoded[BASE64_SIZE(2)];
uint8_t decoded[2];
char *ret = base64_encode(encoded, sizeof(encoded), input, 2);
assert(ret != NULL);
int decoded_len = base64_decode(decoded, encoded, sizeof(decoded));
assert(decoded_len == 2);
assert(decoded[0] == 0x41);
assert(decoded[1] == 0x42);
}
static void
test_three_bytes(void)
{
const uint8_t input[] = { 0x00, 0xFF, 0x80 };
char encoded[BASE64_SIZE(3)];
uint8_t decoded[3];
char *ret = base64_encode(encoded, sizeof(encoded), input, 3);
assert(ret != NULL);
int decoded_len = base64_decode(decoded, encoded, sizeof(decoded));
assert(decoded_len == 3);
assert(decoded[0] == 0x00);
assert(decoded[1] == 0xFF);
assert(decoded[2] == 0x80);
}
static void
test_roundtrip_binary(void)
{
const uint8_t input[] = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,
10, 11, 12, 13, 14, 15, 16 };
char encoded[BASE64_SIZE(17)];
uint8_t decoded[17];
char *ret = base64_encode(encoded, sizeof(encoded), input, 17);
assert(ret != NULL);
int decoded_len = base64_decode(decoded, encoded, sizeof(decoded));
assert(decoded_len == 17);
assert(memcmp(decoded, input, 17) == 0);
}
static void
test_invalid_chars(void)
{
/* Character '!' (ASCII 33) is below the base64 range start ('+' = 43) */
int decoded_len = base64_decode((uint8_t[4]){}, "!!!!", 4);
assert(decoded_len == -1);
}
int
main(void)
{
test_encode_decode();
test_empty_input();
test_single_byte();
test_two_bytes();
test_three_bytes();
test_roundtrip_binary();
test_invalid_chars();
return 0;
}
+103
View File
@@ -0,0 +1,103 @@
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif
#include <assert.h>
#include <string.h>
#include <stdlib.h>
int verbose = 0;
#include "crypto.h"
/* Provide nonce_cache symbol needed by crypto.c */
struct cache *nonce_cache = NULL;
static void
test_balloc(void)
{
buffer_t buf;
memset(&buf, 0, sizeof(buf));
int ret = balloc(&buf, 100);
assert(ret == 0);
(void)ret;
assert(buf.data != NULL);
assert(buf.capacity >= 100);
assert(buf.len == 0);
assert(buf.idx == 0);
bfree(&buf);
assert(buf.data == NULL);
assert(buf.capacity == 0);
}
static void
test_brealloc(void)
{
buffer_t buf;
memset(&buf, 0, sizeof(buf));
balloc(&buf, 50);
buf.len = 10;
/* Grow the buffer */
int ret = brealloc(&buf, 10, 200);
assert(ret == 0);
(void)ret;
assert(buf.capacity >= 200);
assert(buf.len == 10);
bfree(&buf);
}
static void
test_bprepend(void)
{
buffer_t dst, src;
memset(&dst, 0, sizeof(dst));
memset(&src, 0, sizeof(src));
balloc(&dst, 100);
balloc(&src, 100);
/* Put some data in src */
memcpy(src.data, "HEADER", 6);
src.len = 6;
/* Put some data in dst */
memcpy(dst.data, "BODY", 4);
dst.len = 4;
int ret = bprepend(&dst, &src, 200);
assert(ret == 0);
(void)ret;
assert(dst.len == 10);
assert(memcmp(dst.data, "HEADERBODY", 10) == 0);
bfree(&dst);
bfree(&src);
}
static void
test_balloc_zero(void)
{
buffer_t buf;
memset(&buf, 0, sizeof(buf));
int ret = balloc(&buf, 0);
assert(ret == 0);
(void)ret;
/* A zero-capacity buffer should still succeed */
bfree(&buf);
}
int
main(void)
{
test_balloc();
test_brealloc();
test_bprepend();
test_balloc_zero();
return 0;
}
+129
View File
@@ -0,0 +1,129 @@
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif
#include <assert.h>
#include <string.h>
#include <stdlib.h>
#include <errno.h>
int verbose = 0;
#include "cache.h"
static void
test_create_delete(void)
{
struct cache *c = NULL;
int ret = cache_create(&c, 100, NULL);
assert(ret == 0);
assert(c != NULL);
ret = cache_delete(c, 0);
assert(ret == 0);
(void)ret;
}
static void
test_create_null(void)
{
int ret = cache_create(NULL, 100, NULL);
assert(ret == EINVAL);
(void)ret;
}
static void
test_insert_lookup(void)
{
struct cache *c = NULL;
cache_create(&c, 100, NULL);
char *data = strdup("test_data");
cache_insert(c, "key1", 4, data);
char *result = NULL;
cache_lookup(c, "key1", 4, &result);
assert(result != NULL);
assert(strcmp(result, "test_data") == 0);
cache_delete(c, 0);
}
static void
test_key_exist(void)
{
struct cache *c = NULL;
cache_create(&c, 100, NULL);
char *data = strdup("value");
cache_insert(c, "mykey", 5, data);
assert(cache_key_exist(c, "mykey", 5) == 1);
assert(cache_key_exist(c, "nokey", 5) == 0);
cache_delete(c, 0);
}
static void
test_remove(void)
{
struct cache *c = NULL;
cache_create(&c, 100, NULL);
char *data = strdup("to_remove");
cache_insert(c, "rmkey", 5, data);
assert(cache_key_exist(c, "rmkey", 5) == 1);
cache_remove(c, "rmkey", 5);
assert(cache_key_exist(c, "rmkey", 5) == 0);
cache_delete(c, 0);
}
static void
test_lookup_missing(void)
{
struct cache *c = NULL;
cache_create(&c, 100, NULL);
char *result = (char *)0xdeadbeef;
cache_lookup(c, "missing", 7, &result);
assert(result == NULL);
cache_delete(c, 0);
}
static void
test_eviction(void)
{
struct cache *c = NULL;
cache_create(&c, 3, NULL);
/* Insert 3 entries to fill cache */
cache_insert(c, "k1", 2, strdup("v1"));
cache_insert(c, "k2", 2, strdup("v2"));
cache_insert(c, "k3", 2, strdup("v3"));
/* This should trigger eviction of the oldest entry */
cache_insert(c, "k4", 2, strdup("v4"));
/* k1 should have been evicted */
assert(cache_key_exist(c, "k1", 2) == 0);
/* k4 should exist */
assert(cache_key_exist(c, "k4", 2) == 1);
cache_delete(c, 0);
}
int
main(void)
{
test_create_delete();
test_create_null();
test_insert_lookup();
test_key_exist();
test_remove();
test_lookup_missing();
test_eviction();
return 0;
}
+164
View File
@@ -0,0 +1,164 @@
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif
#include <assert.h>
#include <string.h>
#include <stdlib.h>
#include <sodium.h>
int verbose = 0;
#include "crypto.h"
/* Provide nonce_cache symbol needed by crypto.c */
struct cache *nonce_cache = NULL;
static void
test_crypto_md5(void)
{
/* MD5("") = d41d8cd98f00b204e9800998ecf8427e */
unsigned char result[16];
crypto_md5((const unsigned char *)"", 0, result);
unsigned char expected[] = {
0xd4, 0x1d, 0x8c, 0xd9, 0x8f, 0x00, 0xb2, 0x04,
0xe9, 0x80, 0x09, 0x98, 0xec, 0xf8, 0x42, 0x7e
};
assert(memcmp(result, expected, 16) == 0);
(void)expected;
/* MD5("abc") = 900150983cd24fb0d6963f7d28e17f72 */
crypto_md5((const unsigned char *)"abc", 3, result);
unsigned char expected_abc[] = {
0x90, 0x01, 0x50, 0x98, 0x3c, 0xd2, 0x4f, 0xb0,
0xd6, 0x96, 0x3f, 0x7d, 0x28, 0xe1, 0x7f, 0x72
};
assert(memcmp(result, expected_abc, 16) == 0);
(void)expected_abc;
}
static void
test_crypto_derive_key(void)
{
uint8_t key[32];
/* derive_key should produce deterministic output from a password */
int ret = crypto_derive_key("password", key, 32);
assert(ret == 32);
/* Same password should produce same key */
uint8_t key2[32];
ret = crypto_derive_key("password", key2, 32);
assert(ret == 32);
assert(memcmp(key, key2, 32) == 0);
/* Different password should produce different key */
uint8_t key3[32];
ret = crypto_derive_key("different", key3, 32);
assert(ret == 32);
assert(memcmp(key, key3, 32) != 0);
(void)ret;
}
static void
test_crypto_hkdf(void)
{
/* RFC 5869 Test Case 1 */
const mbedtls_md_info_t *md = mbedtls_md_info_from_type(MBEDTLS_MD_SHA256);
assert(md != NULL);
unsigned char ikm[22];
memset(ikm, 0x0b, 22);
unsigned char salt[] = {
0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
0x08, 0x09, 0x0a, 0x0b, 0x0c
};
unsigned char info[] = {
0xf0, 0xf1, 0xf2, 0xf3, 0xf4, 0xf5, 0xf6, 0xf7,
0xf8, 0xf9
};
unsigned char okm[42];
int ret = crypto_hkdf(md, salt, sizeof(salt), ikm, sizeof(ikm),
info, sizeof(info), okm, sizeof(okm));
assert(ret == 0);
(void)ret;
unsigned char expected_okm[] = {
0x3c, 0xb2, 0x5f, 0x25, 0xfa, 0xac, 0xd5, 0x7a,
0x90, 0x43, 0x4f, 0x64, 0xd0, 0x36, 0x2f, 0x2a,
0x2d, 0x2d, 0x0a, 0x90, 0xcf, 0x1a, 0x5a, 0x4c,
0x5d, 0xb0, 0x2d, 0x56, 0xec, 0xc4, 0xc5, 0xbf,
0x34, 0x00, 0x72, 0x08, 0xd5, 0xb8, 0x87, 0x18,
0x58, 0x65
};
assert(memcmp(okm, expected_okm, 42) == 0);
(void)expected_okm;
}
static void
test_crypto_hkdf_extract(void)
{
const mbedtls_md_info_t *md = mbedtls_md_info_from_type(MBEDTLS_MD_SHA256);
assert(md != NULL);
unsigned char ikm[22];
memset(ikm, 0x0b, 22);
unsigned char salt[] = {
0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
0x08, 0x09, 0x0a, 0x0b, 0x0c
};
unsigned char prk[32];
int ret = crypto_hkdf_extract(md, salt, sizeof(salt), ikm, sizeof(ikm), prk);
assert(ret == 0);
(void)ret;
/* RFC 5869 Test Case 1 PRK */
unsigned char expected_prk[] = {
0x07, 0x77, 0x09, 0x36, 0x2c, 0x2e, 0x32, 0xdf,
0x0d, 0xdc, 0x3f, 0x0d, 0xc4, 0x7b, 0xba, 0x63,
0x90, 0xb6, 0xc7, 0x3b, 0xb5, 0x0f, 0x9c, 0x31,
0x22, 0xec, 0x84, 0x4a, 0xd7, 0xc2, 0xb3, 0xe5
};
assert(memcmp(prk, expected_prk, 32) == 0);
(void)expected_prk;
}
static void
test_crypto_parse_key(void)
{
/* base64_encode uses URL-safe base64 with -_ instead of +/ */
uint8_t key[32];
/* A known base64-encoded 32-byte key (all zeros) */
/* 32 zero bytes = "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=" in standard base64 */
/* With URL-safe: same since no +/ needed */
int ret = crypto_parse_key("AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=", key, 32);
assert(ret == 32);
(void)ret;
/* All bytes should be 0 */
for (int i = 0; i < 32; i++) {
assert(key[i] == 0);
}
}
int
main(void)
{
if (sodium_init() < 0) {
return 1;
}
test_crypto_md5();
test_crypto_derive_key();
test_crypto_hkdf();
test_crypto_hkdf_extract();
test_crypto_parse_key();
return 0;
}
+85
View File
@@ -0,0 +1,85 @@
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif
#include <assert.h>
#include <string.h>
#include <stdlib.h>
int verbose = 0;
#include "netutils.h"
#include "jconf.h"
static void
test_parse_addr_ipv4_with_port(void)
{
ss_addr_t addr;
memset(&addr, 0, sizeof(addr));
parse_addr("192.168.1.1:8080", &addr);
assert(addr.host != NULL);
assert(addr.port != NULL);
assert(strcmp(addr.host, "192.168.1.1") == 0);
assert(strcmp(addr.port, "8080") == 0);
free_addr(&addr);
}
static void
test_parse_addr_ipv6_with_port(void)
{
ss_addr_t addr;
memset(&addr, 0, sizeof(addr));
parse_addr("[::1]:443", &addr);
assert(addr.host != NULL);
assert(addr.port != NULL);
assert(strcmp(addr.host, "::1") == 0);
assert(strcmp(addr.port, "443") == 0);
free_addr(&addr);
}
static void
test_parse_addr_hostname_with_port(void)
{
ss_addr_t addr;
memset(&addr, 0, sizeof(addr));
parse_addr("example.com:1234", &addr);
assert(addr.host != NULL);
assert(addr.port != NULL);
assert(strcmp(addr.host, "example.com") == 0);
assert(strcmp(addr.port, "1234") == 0);
free_addr(&addr);
}
static void
test_parse_addr_no_port(void)
{
ss_addr_t addr;
memset(&addr, 0, sizeof(addr));
parse_addr("10.0.0.1", &addr);
assert(addr.host != NULL);
assert(strcmp(addr.host, "10.0.0.1") == 0);
/* Port may be NULL when no port is specified */
free_addr(&addr);
}
static void
test_parse_addr_ipv6_no_port(void)
{
ss_addr_t addr;
memset(&addr, 0, sizeof(addr));
parse_addr("::1", &addr);
assert(addr.host != NULL);
assert(strcmp(addr.host, "::1") == 0);
free_addr(&addr);
}
int
main(void)
{
test_parse_addr_ipv4_with_port();
test_parse_addr_ipv6_with_port();
test_parse_addr_hostname_with_port();
test_parse_addr_no_port();
test_parse_addr_ipv6_no_port();
return 0;
}
+130
View File
@@ -0,0 +1,130 @@
#include <assert.h>
#include <string.h>
#include <stdlib.h>
#include "json.h"
static void
test_parse_simple_object(void)
{
const char *json_str = "{\"key\": \"value\", \"num\": 42}";
json_value *val = json_parse(json_str, strlen(json_str));
assert(val != NULL);
assert(val->type == json_object);
assert(val->u.object.length == 2);
/* Check first entry */
assert(strcmp(val->u.object.values[0].name, "key") == 0);
assert(val->u.object.values[0].value->type == json_string);
assert(strcmp(val->u.object.values[0].value->u.string.ptr, "value") == 0);
/* Check second entry */
assert(strcmp(val->u.object.values[1].name, "num") == 0);
assert(val->u.object.values[1].value->type == json_integer);
assert(val->u.object.values[1].value->u.integer == 42);
json_value_free(val);
}
static void
test_parse_array(void)
{
const char *json_str = "[1, 2, 3]";
json_value *val = json_parse(json_str, strlen(json_str));
assert(val != NULL);
assert(val->type == json_array);
assert(val->u.array.length == 3);
assert(val->u.array.values[0]->type == json_integer);
assert(val->u.array.values[0]->u.integer == 1);
assert(val->u.array.values[1]->u.integer == 2);
assert(val->u.array.values[2]->u.integer == 3);
json_value_free(val);
}
static void
test_parse_nested(void)
{
const char *json_str = "{\"outer\": {\"inner\": true}}";
json_value *val = json_parse(json_str, strlen(json_str));
assert(val != NULL);
assert(val->type == json_object);
assert(val->u.object.length == 1);
json_value *outer = val->u.object.values[0].value;
assert(outer->type == json_object);
assert(outer->u.object.length == 1);
assert(outer->u.object.values[0].value->type == json_boolean);
assert(outer->u.object.values[0].value->u.boolean != 0);
json_value_free(val);
}
static void
test_parse_types(void)
{
const char *json_str = "{\"s\": \"hello\", \"i\": -5, \"d\": 3.14, \"b\": false, \"n\": null}";
json_value *val = json_parse(json_str, strlen(json_str));
assert(val != NULL);
assert(val->type == json_object);
assert(val->u.object.length == 5);
assert(val->u.object.values[0].value->type == json_string);
assert(val->u.object.values[1].value->type == json_integer);
assert(val->u.object.values[1].value->u.integer == -5);
assert(val->u.object.values[2].value->type == json_double);
assert(val->u.object.values[3].value->type == json_boolean);
assert(val->u.object.values[3].value->u.boolean == 0);
assert(val->u.object.values[4].value->type == json_null);
json_value_free(val);
}
static void
test_parse_invalid(void)
{
/* Missing closing brace */
assert(json_parse("{\"key\": 1", 9) == NULL);
/* Empty string */
assert(json_parse("", 0) == NULL);
/* Just garbage */
assert(json_parse("not json", 8) == NULL);
}
static void
test_parse_empty_object(void)
{
const char *json_str = "{}";
json_value *val = json_parse(json_str, strlen(json_str));
assert(val != NULL);
assert(val->type == json_object);
assert(val->u.object.length == 0);
json_value_free(val);
}
static void
test_parse_empty_array(void)
{
const char *json_str = "[]";
json_value *val = json_parse(json_str, strlen(json_str));
assert(val != NULL);
assert(val->type == json_array);
assert(val->u.array.length == 0);
json_value_free(val);
}
int
main(void)
{
test_parse_simple_object();
test_parse_array();
test_parse_nested();
test_parse_types();
test_parse_invalid();
test_parse_empty_object();
test_parse_empty_array();
return 0;
}
+115
View File
@@ -0,0 +1,115 @@
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif
#include <assert.h>
#include <string.h>
#include <stdlib.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
int verbose = 0;
#include "netutils.h"
static void
test_get_sockaddr_len(void)
{
struct sockaddr_in addr4;
struct sockaddr_in6 addr6;
struct sockaddr_storage unknown;
memset(&addr4, 0, sizeof(addr4));
addr4.sin_family = AF_INET;
assert(get_sockaddr_len((struct sockaddr *)&addr4) == sizeof(struct sockaddr_in));
memset(&addr6, 0, sizeof(addr6));
addr6.sin6_family = AF_INET6;
assert(get_sockaddr_len((struct sockaddr *)&addr6) == sizeof(struct sockaddr_in6));
memset(&unknown, 0, sizeof(unknown));
unknown.ss_family = AF_UNSPEC;
assert(get_sockaddr_len((struct sockaddr *)&unknown) == 0);
}
static void
test_sockaddr_cmp(void)
{
struct sockaddr_storage a, b;
struct sockaddr_in *a4 = (struct sockaddr_in *)&a;
struct sockaddr_in *b4 = (struct sockaddr_in *)&b;
/* Same address and port */
memset(&a, 0, sizeof(a));
memset(&b, 0, sizeof(b));
a4->sin_family = AF_INET;
b4->sin_family = AF_INET;
a4->sin_port = htons(80);
b4->sin_port = htons(80);
inet_pton(AF_INET, "127.0.0.1", &a4->sin_addr);
inet_pton(AF_INET, "127.0.0.1", &b4->sin_addr);
assert(sockaddr_cmp(&a, &b, sizeof(struct sockaddr_in)) == 0);
/* Different port */
b4->sin_port = htons(81);
assert(sockaddr_cmp(&a, &b, sizeof(struct sockaddr_in)) != 0);
}
static void
test_sockaddr_cmp_addr(void)
{
struct sockaddr_storage a, b;
struct sockaddr_in *a4 = (struct sockaddr_in *)&a;
struct sockaddr_in *b4 = (struct sockaddr_in *)&b;
memset(&a, 0, sizeof(a));
memset(&b, 0, sizeof(b));
a4->sin_family = AF_INET;
b4->sin_family = AF_INET;
a4->sin_port = htons(80);
b4->sin_port = htons(443);
inet_pton(AF_INET, "10.0.0.1", &a4->sin_addr);
inet_pton(AF_INET, "10.0.0.1", &b4->sin_addr);
/* Same address, different port - should be equal */
assert(sockaddr_cmp_addr(&a, &b, sizeof(struct sockaddr_in)) == 0);
/* Different address */
inet_pton(AF_INET, "10.0.0.2", &b4->sin_addr);
assert(sockaddr_cmp_addr(&a, &b, sizeof(struct sockaddr_in)) != 0);
}
static void
test_validate_hostname(void)
{
/* Valid hostnames */
assert(validate_hostname("example.com", 11) == 1);
assert(validate_hostname("sub.example.com", 15) == 1);
assert(validate_hostname("a", 1) == 1);
assert(validate_hostname("a-b", 3) == 1);
assert(validate_hostname("123.456", 7) == 1);
/* Invalid hostnames */
assert(validate_hostname(NULL, 0) == 0);
assert(validate_hostname("", 0) == 0);
assert(validate_hostname(".example.com", 12) == 0); /* starts with dot */
assert(validate_hostname("-example.com", 12) == 0); /* label starts with hyphen */
assert(validate_hostname("example-.com", 12) == 0); /* label ends with hyphen */
/* Too long hostname (> 255) */
char long_name[260];
memset(long_name, 'a', 259);
long_name[259] = '\0';
assert(validate_hostname(long_name, 259) == 0);
}
int
main(void)
{
test_get_sockaddr_len();
test_sockaddr_cmp();
test_sockaddr_cmp_addr();
test_validate_hostname();
return 0;
}
+74
View File
@@ -0,0 +1,74 @@
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif
#include <assert.h>
#include <stdint.h>
#include <string.h>
#include <stdlib.h>
int verbose = 0;
#include "ppbloom.h"
static void
test_init_free(void)
{
int ret = ppbloom_init(1000, 0.01);
assert(ret == 0);
(void)ret;
ppbloom_free();
}
static void
test_add_check(void)
{
ppbloom_init(1000, 0.01);
const char *item1 = "hello";
const char *item2 = "world";
const char *item3 = "missing";
/* Not in filter initially */
assert(ppbloom_check(item1, strlen(item1)) == 0);
assert(ppbloom_check(item2, strlen(item2)) == 0);
/* Add items */
ppbloom_add(item1, strlen(item1));
ppbloom_add(item2, strlen(item2));
/* Should be found */
assert(ppbloom_check(item1, strlen(item1)) == 1);
assert(ppbloom_check(item2, strlen(item2)) == 1);
/* Should not be found */
assert(ppbloom_check(item3, strlen(item3)) == 0);
(void)item3;
ppbloom_free();
}
static void
test_binary_data(void)
{
ppbloom_init(1000, 0.01);
const uint8_t data1[] = { 0x00, 0x01, 0x02, 0x03 };
const uint8_t data2[] = { 0xFF, 0xFE, 0xFD, 0xFC };
ppbloom_add(data1, sizeof(data1));
assert(ppbloom_check(data1, sizeof(data1)) == 1);
assert(ppbloom_check(data2, sizeof(data2)) == 0);
(void)data2;
ppbloom_free();
}
int
main(void)
{
test_init_free();
test_add_check();
test_binary_data();
return 0;
}
+126
View File
@@ -0,0 +1,126 @@
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif
#include <assert.h>
#include <string.h>
#include <stdlib.h>
int verbose = 0;
#include "rule.h"
#include "utils.h"
static void
test_new_rule(void)
{
rule_t *rule = new_rule();
assert(rule != NULL);
assert(rule->pattern == NULL);
assert(rule->pattern_re == NULL);
free(rule);
}
static void
test_accept_rule_arg(void)
{
rule_t *rule = new_rule();
assert(rule != NULL);
int ret = accept_rule_arg(rule, "^example\\.com$");
assert(ret == 1);
assert(rule->pattern != NULL);
assert(strcmp(rule->pattern, "^example\\.com$") == 0);
/* Second call should fail - pattern already set */
ret = accept_rule_arg(rule, "another");
assert(ret == -1);
(void)ret;
free(rule->pattern);
free(rule);
}
static void
test_init_rule(void)
{
rule_t *rule = new_rule();
accept_rule_arg(rule, "^test.*$");
int ret = init_rule(rule);
assert(ret == 1);
(void)ret;
assert(rule->pattern_re != NULL);
if (rule->match_data)
pcre2_match_data_free(rule->match_data);
if (rule->pattern_re)
pcre2_code_free(rule->pattern_re);
free(rule->pattern);
free(rule);
}
static void
test_init_rule_invalid(void)
{
rule_t *rule = new_rule();
accept_rule_arg(rule, "[invalid"); /* Unclosed bracket */
int ret = init_rule(rule);
assert(ret == 0); /* Should fail */
(void)ret;
free(rule->pattern);
free(rule);
}
static void
test_lookup_rule(void)
{
struct cork_dllist rules;
cork_dllist_init(&rules);
rule_t *rule1 = new_rule();
accept_rule_arg(rule1, "^google\\.com$");
init_rule(rule1);
add_rule(&rules, rule1);
rule_t *rule2 = new_rule();
accept_rule_arg(rule2, ".*\\.example\\.com$");
init_rule(rule2);
add_rule(&rules, rule2);
/* Should match rule1 */
rule_t *found = lookup_rule(&rules, "google.com", 10);
assert(found == rule1);
/* Should match rule2 */
found = lookup_rule(&rules, "sub.example.com", 15);
assert(found == rule2);
/* Should not match */
found = lookup_rule(&rules, "other.net", 9);
assert(found == NULL);
(void)found;
/* Clean up */
if (rule1->match_data) pcre2_match_data_free(rule1->match_data);
if (rule1->pattern_re) pcre2_code_free(rule1->pattern_re);
free(rule1->pattern);
free(rule1);
if (rule2->match_data) pcre2_match_data_free(rule2->match_data);
if (rule2->pattern_re) pcre2_code_free(rule2->pattern_re);
free(rule2->pattern);
free(rule2);
}
int
main(void)
{
test_new_rule();
test_accept_rule_arg();
test_init_rule();
test_init_rule_invalid();
test_lookup_rule();
return 0;
}
+881
View File
@@ -0,0 +1,881 @@
#!/usr/bin/env bash
###############################################################################
# test_ss_setup.sh -- Unit tests for ss-setup.sh utility functions
#
# Sources ss-setup.sh (which skips main() due to BASH_SOURCE guard) and
# exercises every pure/utility function that doesn't require a TUI backend.
###############################################################################
set -euo pipefail
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
PROJECT_ROOT="$(cd "$SCRIPT_DIR/.." && pwd)"
SS_SETUP="${PROJECT_ROOT}/scripts/ss-setup.sh"
PASS=0
FAIL=0
TMPDIR_TEST=""
cleanup_test() {
if [[ -n "$TMPDIR_TEST" && -d "$TMPDIR_TEST" ]]; then
rm -rf "$TMPDIR_TEST"
fi
}
trap cleanup_test EXIT
TMPDIR_TEST=$(mktemp -d)
# Source the script under test (main() won't run due to BASH_SOURCE guard)
# shellcheck source=../scripts/ss-setup.sh
source "$SS_SETUP"
###############################################################################
# Test helpers
###############################################################################
assert_eq() {
local test_name="$1" expected="$2" actual="$3"
if [[ "$expected" == "$actual" ]]; then
PASS=$((PASS + 1))
else
FAIL=$((FAIL + 1))
echo "FAIL: ${test_name}" >&2
echo " expected: '${expected}'" >&2
echo " actual: '${actual}'" >&2
fi
}
assert_match() {
local test_name="$1" pattern="$2" actual="$3"
if [[ "$actual" =~ $pattern ]]; then
PASS=$((PASS + 1))
else
FAIL=$((FAIL + 1))
echo "FAIL: ${test_name}" >&2
echo " expected match: '${pattern}'" >&2
echo " actual: '${actual}'" >&2
fi
}
assert_ok() {
local test_name="$1"
shift
if "$@" 2>/dev/null; then
PASS=$((PASS + 1))
else
FAIL=$((FAIL + 1))
echo "FAIL: ${test_name} (command returned non-zero)" >&2
fi
}
assert_fail() {
local test_name="$1"
shift
if "$@" 2>/dev/null; then
FAIL=$((FAIL + 1))
echo "FAIL: ${test_name} (expected failure but got success)" >&2
else
PASS=$((PASS + 1))
fi
}
assert_contains() {
local test_name="$1" needle="$2" haystack="$3"
if [[ "$haystack" == *"$needle"* ]]; then
PASS=$((PASS + 1))
else
FAIL=$((FAIL + 1))
echo "FAIL: ${test_name}" >&2
echo " expected to contain: '${needle}'" >&2
echo " actual: '${haystack}'" >&2
fi
}
assert_not_contains() {
local test_name="$1" needle="$2" haystack="$3"
if [[ "$haystack" != *"$needle"* ]]; then
PASS=$((PASS + 1))
else
FAIL=$((FAIL + 1))
echo "FAIL: ${test_name}" >&2
echo " expected NOT to contain: '${needle}'" >&2
echo " actual: '${haystack}'" >&2
fi
}
###############################################################################
# Tests: AEAD_CIPHERS constant
###############################################################################
test_aead_ciphers_count() {
assert_eq "AEAD_CIPHERS has 5 entries" "5" "${#AEAD_CIPHERS[@]}"
}
test_aead_ciphers_contains_chacha20() {
local found=0
for c in "${AEAD_CIPHERS[@]}"; do
[[ "$c" == "chacha20-ietf-poly1305" ]] && found=1
done
assert_eq "AEAD_CIPHERS contains chacha20-ietf-poly1305" "1" "$found"
}
test_aead_ciphers_contains_aes256gcm() {
local found=0
for c in "${AEAD_CIPHERS[@]}"; do
[[ "$c" == "aes-256-gcm" ]] && found=1
done
assert_eq "AEAD_CIPHERS contains aes-256-gcm" "1" "$found"
}
test_aead_ciphers_contains_aes128gcm() {
local found=0
for c in "${AEAD_CIPHERS[@]}"; do
[[ "$c" == "aes-128-gcm" ]] && found=1
done
assert_eq "AEAD_CIPHERS contains aes-128-gcm" "1" "$found"
}
###############################################################################
# Tests: validate_port
###############################################################################
test_validate_port_valid() {
assert_ok "validate_port 1" validate_port "1"
assert_ok "validate_port 80" validate_port "80"
assert_ok "validate_port 443" validate_port "443"
assert_ok "validate_port 8388" validate_port "8388"
assert_ok "validate_port 65535" validate_port "65535"
}
test_validate_port_invalid() {
assert_fail "validate_port 0" validate_port "0"
assert_fail "validate_port 65536" validate_port "65536"
assert_fail "validate_port -1" validate_port "-1"
assert_fail "validate_port abc" validate_port "abc"
assert_fail "validate_port empty" validate_port ""
assert_fail "validate_port 99999" validate_port "99999"
assert_fail "validate_port 8.8" validate_port "8.8"
}
###############################################################################
# Tests: validate_instance_name
###############################################################################
test_validate_instance_name_valid() {
assert_ok "instance: config" validate_instance_name "config"
assert_ok "instance: my-server" validate_instance_name "my-server"
assert_ok "instance: server_1" validate_instance_name "server_1"
assert_ok "instance: Test123" validate_instance_name "Test123"
assert_ok "instance: a" validate_instance_name "a"
assert_ok "instance: A-B_c-1" validate_instance_name "A-B_c-1"
}
test_validate_instance_name_invalid() {
assert_fail "instance: empty" validate_instance_name ""
assert_fail "instance: has space" validate_instance_name "has space"
assert_fail "instance: has.dot" validate_instance_name "has.dot"
assert_fail "instance: has/slash" validate_instance_name "has/slash"
assert_fail "instance: has@at" validate_instance_name "has@at"
assert_fail "instance: has:colon" validate_instance_name "has:colon"
}
###############################################################################
# Tests: generate_password
###############################################################################
test_generate_password_nonempty() {
local pw
pw=$(generate_password 32)
assert_match "password is non-empty" ".+" "$pw"
}
test_generate_password_length() {
# base64 of 16 bytes = 24 chars (before newline strip)
local pw
pw=$(generate_password 16)
# Should be at least 20 chars (base64 encoding of 16 bytes)
local len=${#pw}
if (( len >= 20 )); then
PASS=$((PASS + 1))
else
FAIL=$((FAIL + 1))
echo "FAIL: password length too short: ${len} chars" >&2
fi
}
test_generate_password_no_newlines() {
local pw
pw=$(generate_password 32)
assert_not_contains "password has no newline" $'\n' "$pw"
}
test_generate_password_different() {
local pw1 pw2
pw1=$(generate_password 32)
pw2=$(generate_password 32)
if [[ "$pw1" != "$pw2" ]]; then
PASS=$((PASS + 1))
else
FAIL=$((FAIL + 1))
echo "FAIL: two passwords are identical: '${pw1}'" >&2
fi
}
###############################################################################
# Tests: generate_random_port
###############################################################################
test_generate_random_port_range() {
local port
for _ in $(seq 1 10); do
port=$(generate_random_port)
if (( port >= 10000 && port <= 65000 )); then
PASS=$((PASS + 1))
else
FAIL=$((FAIL + 1))
echo "FAIL: random port ${port} out of range 10000-65000" >&2
fi
done
}
test_generate_random_port_numeric() {
local port
port=$(generate_random_port)
assert_match "random port is numeric" "^[0-9]+$" "$port"
}
###############################################################################
# Tests: json_escape
###############################################################################
test_json_escape_plain() {
local result
result=$(json_escape "hello world")
assert_eq "json_escape plain" "hello world" "$result"
}
test_json_escape_quotes() {
local result
result=$(json_escape 'say "hi"')
assert_eq "json_escape quotes" 'say \"hi\"' "$result"
}
test_json_escape_backslash() {
local result
result=$(json_escape 'path\to\file')
assert_eq "json_escape backslash" 'path\\to\\file' "$result"
}
test_json_escape_tab() {
local result
result=$(json_escape "col1 col2")
assert_eq "json_escape tab" 'col1\tcol2' "$result"
}
test_json_escape_newline() {
local result
result=$(json_escape "line1
line2")
assert_eq "json_escape newline" 'line1\nline2' "$result"
}
test_json_escape_empty() {
local result
result=$(json_escape "")
assert_eq "json_escape empty" "" "$result"
}
###############################################################################
# Tests: urlencode
###############################################################################
test_urlencode_plain() {
local result
result=$(urlencode "hello")
assert_eq "urlencode plain" "hello" "$result"
}
test_urlencode_space() {
local result
result=$(urlencode "hello world")
assert_eq "urlencode space" "hello%20world" "$result"
}
test_urlencode_special() {
local result
result=$(urlencode "a=b&c=d")
assert_eq "urlencode special" "a%3Db%26c%3Dd" "$result"
}
test_urlencode_semicolon() {
local result
result=$(urlencode "obfs-local;obfs=http")
assert_eq "urlencode semicolon" "obfs-local%3Bobfs%3Dhttp" "$result"
}
test_urlencode_safe_chars() {
local result
result=$(urlencode "a-b_c.d~e")
assert_eq "urlencode safe chars" "a-b_c.d~e" "$result"
}
test_urlencode_empty() {
local result
result=$(urlencode "")
assert_eq "urlencode empty" "" "$result"
}
###############################################################################
# Tests: generate_ss_uri
###############################################################################
test_ss_uri_basic() {
local uri
uri=$(generate_ss_uri "aes-256-gcm" "testpass" "1.2.3.4" "8388" "" "")
# Should start with ss://
assert_match "ss uri starts with ss://" "^ss://" "$uri"
# Should contain @host:port
assert_contains "ss uri has host:port" "@1.2.3.4:8388" "$uri"
# Should NOT have plugin query
assert_not_contains "ss uri no plugin query" "/?plugin=" "$uri"
}
test_ss_uri_with_plugin() {
local uri
uri=$(generate_ss_uri "chacha20-ietf-poly1305" "mypass" "example.com" "443" "v2ray-plugin" "server;tls;host=example.com")
assert_match "ss uri+plugin starts with ss://" "^ss://" "$uri"
assert_contains "ss uri+plugin has host:port" "@example.com:443" "$uri"
assert_contains "ss uri+plugin has plugin param" "/?plugin=" "$uri"
# v2ray-plugin should be URL-encoded with the opts
assert_contains "ss uri+plugin has v2ray" "v2ray-plugin" "$uri"
}
test_ss_uri_with_plugin_no_opts() {
local uri
uri=$(generate_ss_uri "aes-128-gcm" "pw" "10.0.0.1" "9000" "obfs-local" "")
assert_contains "ss uri plugin-no-opts has plugin" "/?plugin=" "$uri"
assert_contains "ss uri plugin-no-opts has obfs-local" "obfs-local" "$uri"
}
test_ss_uri_base64_encoding() {
# Verify the userinfo part is valid base64url
local uri
uri=$(generate_ss_uri "aes-256-gcm" "test" "1.2.3.4" "8388" "" "")
# Extract the base64 part between ss:// and @
local b64_part
b64_part=$(echo "$uri" | sed 's|^ss://\([^@]*\)@.*|\1|')
# base64url should only contain [A-Za-z0-9_-]
assert_match "ss uri base64url valid chars" "^[A-Za-z0-9_-]+$" "$b64_part"
}
###############################################################################
# Tests: write_json_config (server)
###############################################################################
test_write_json_config_basic() {
local outfile="${TMPDIR_TEST}/server_basic.json"
CFG_SERVER="0.0.0.0"
CFG_SERVER_PORT="8388"
CFG_PASSWORD="testpassword123"
CFG_TIMEOUT="300"
CFG_METHOD="aes-256-gcm"
CFG_MODE="tcp_and_udp"
CFG_FAST_OPEN="false"
CFG_PLUGIN=""
CFG_PLUGIN_OPTS=""
write_json_config "$outfile"
local content
content=$(cat "$outfile")
assert_contains "server json has server" '"server": "0.0.0.0"' "$content"
assert_contains "server json has port" '"server_port": 8388' "$content"
assert_contains "server json has password" '"password": "testpassword123"' "$content"
assert_contains "server json has timeout" '"timeout": 300' "$content"
assert_contains "server json has method" '"method": "aes-256-gcm"' "$content"
assert_contains "server json has mode" '"mode": "tcp_and_udp"' "$content"
assert_contains "server json has fast_open false" '"fast_open": false' "$content"
assert_not_contains "server json no plugin" '"plugin"' "$content"
# No trailing comma before closing brace
assert_not_contains "server json no trailing comma" ',
}' "$content"
}
test_write_json_config_fast_open_true() {
local outfile="${TMPDIR_TEST}/server_tfo.json"
CFG_SERVER="0.0.0.0"
CFG_SERVER_PORT="443"
CFG_PASSWORD="pw"
CFG_TIMEOUT="60"
CFG_METHOD="chacha20-ietf-poly1305"
CFG_MODE="tcp_only"
CFG_FAST_OPEN="true"
CFG_PLUGIN=""
CFG_PLUGIN_OPTS=""
write_json_config "$outfile"
local content
content=$(cat "$outfile")
assert_contains "server json fast_open true" '"fast_open": true' "$content"
assert_not_contains "server json fast_open not quoted" '"fast_open": "true"' "$content"
}
test_write_json_config_with_plugin() {
local outfile="${TMPDIR_TEST}/server_plugin.json"
CFG_SERVER="0.0.0.0"
CFG_SERVER_PORT="443"
CFG_PASSWORD="pw"
CFG_TIMEOUT="300"
CFG_METHOD="aes-256-gcm"
CFG_MODE="tcp_and_udp"
CFG_FAST_OPEN="false"
CFG_PLUGIN="v2ray-plugin"
CFG_PLUGIN_OPTS="server;tls;host=example.com"
write_json_config "$outfile"
local content
content=$(cat "$outfile")
assert_contains "server json has plugin" '"plugin": "v2ray-plugin"' "$content"
assert_contains "server json has plugin_opts" '"plugin_opts": "server;tls;host=example.com"' "$content"
}
test_write_json_config_with_plugin_no_opts() {
local outfile="${TMPDIR_TEST}/server_plugin_noopts.json"
CFG_SERVER="0.0.0.0"
CFG_SERVER_PORT="8388"
CFG_PASSWORD="pw"
CFG_TIMEOUT="300"
CFG_METHOD="aes-256-gcm"
CFG_MODE="tcp_and_udp"
CFG_FAST_OPEN="false"
CFG_PLUGIN="obfs-local"
CFG_PLUGIN_OPTS=""
write_json_config "$outfile"
local content
content=$(cat "$outfile")
assert_contains "server json plugin no opts has plugin" '"plugin": "obfs-local"' "$content"
assert_not_contains "server json plugin no opts has no plugin_opts" '"plugin_opts"' "$content"
}
test_write_json_config_password_special_chars() {
local outfile="${TMPDIR_TEST}/server_special.json"
CFG_SERVER="0.0.0.0"
CFG_SERVER_PORT="8388"
CFG_PASSWORD='pass"word\with/special'
CFG_TIMEOUT="300"
CFG_METHOD="aes-256-gcm"
CFG_MODE="tcp_and_udp"
CFG_FAST_OPEN="false"
CFG_PLUGIN=""
CFG_PLUGIN_OPTS=""
write_json_config "$outfile"
local content
content=$(cat "$outfile")
# Quotes and backslashes should be escaped
assert_contains "special password escaped quote" '\"' "$content"
assert_contains "special password escaped backslash" '\\' "$content"
}
###############################################################################
# Tests: write_client_json_config
###############################################################################
test_write_client_json_config_basic() {
local outfile="${TMPDIR_TEST}/client_basic.json"
CFG_CLIENT_SERVER="1.2.3.4"
CFG_CLIENT_SERVER_PORT="8388"
CFG_CLIENT_LOCAL_PORT="1080"
CFG_CLIENT_PASSWORD="clientpw"
CFG_CLIENT_METHOD="aes-256-gcm"
CFG_CLIENT_PLUGIN=""
CFG_CLIENT_PLUGIN_OPTS=""
write_client_json_config "$outfile"
local content
content=$(cat "$outfile")
assert_contains "client json has server" '"server": "1.2.3.4"' "$content"
assert_contains "client json has server_port" '"server_port": 8388' "$content"
assert_contains "client json has local_address" '"local_address": "127.0.0.1"' "$content"
assert_contains "client json has local_port" '"local_port": 1080' "$content"
assert_contains "client json has password" '"password": "clientpw"' "$content"
assert_contains "client json has method" '"method": "aes-256-gcm"' "$content"
assert_contains "client json has timeout" '"timeout": 300' "$content"
assert_contains "client json has mode" '"mode": "tcp_and_udp"' "$content"
assert_not_contains "client json no plugin" '"plugin"' "$content"
}
test_write_client_json_config_with_plugin() {
local outfile="${TMPDIR_TEST}/client_plugin.json"
CFG_CLIENT_SERVER="example.com"
CFG_CLIENT_SERVER_PORT="443"
CFG_CLIENT_LOCAL_PORT="1080"
CFG_CLIENT_PASSWORD="pw"
CFG_CLIENT_METHOD="chacha20-ietf-poly1305"
CFG_CLIENT_PLUGIN="v2ray-plugin"
CFG_CLIENT_PLUGIN_OPTS="tls;host=example.com"
write_client_json_config "$outfile"
local content
content=$(cat "$outfile")
assert_contains "client json plugin" '"plugin": "v2ray-plugin"' "$content"
assert_contains "client json plugin_opts" '"plugin_opts": "tls;host=example.com"' "$content"
}
###############################################################################
# Tests: parse_existing_config (round-trip)
###############################################################################
test_parse_existing_config_roundtrip() {
local outfile="${TMPDIR_TEST}/roundtrip.json"
# Set known values
CFG_SERVER="10.20.30.40"
CFG_SERVER_PORT="9999"
CFG_PASSWORD="roundtrip_pw"
CFG_TIMEOUT="600"
CFG_METHOD="aes-128-gcm"
CFG_MODE="udp_only"
CFG_FAST_OPEN="true"
CFG_PLUGIN="obfs-local"
CFG_PLUGIN_OPTS="obfs=http;obfs-host=example.com"
write_json_config "$outfile"
# Reset globals
CFG_SERVER=""
CFG_SERVER_PORT=""
CFG_PASSWORD=""
CFG_TIMEOUT=""
CFG_METHOD=""
CFG_MODE=""
CFG_FAST_OPEN=""
CFG_PLUGIN=""
CFG_PLUGIN_OPTS=""
# Parse back
parse_existing_config "$outfile"
assert_eq "roundtrip server" "10.20.30.40" "$CFG_SERVER"
assert_eq "roundtrip port" "9999" "$CFG_SERVER_PORT"
assert_eq "roundtrip password" "roundtrip_pw" "$CFG_PASSWORD"
assert_eq "roundtrip timeout" "600" "$CFG_TIMEOUT"
assert_eq "roundtrip method" "aes-128-gcm" "$CFG_METHOD"
assert_eq "roundtrip mode" "udp_only" "$CFG_MODE"
assert_eq "roundtrip fast_open" "true" "$CFG_FAST_OPEN"
assert_eq "roundtrip plugin" "obfs-local" "$CFG_PLUGIN"
assert_eq "roundtrip plugin_opts" "obfs=http;obfs-host=example.com" "$CFG_PLUGIN_OPTS"
}
test_parse_existing_config_no_plugin() {
local outfile="${TMPDIR_TEST}/roundtrip_noplugin.json"
CFG_SERVER="0.0.0.0"
CFG_SERVER_PORT="8388"
CFG_PASSWORD="simplepw"
CFG_TIMEOUT="300"
CFG_METHOD="aes-256-gcm"
CFG_MODE="tcp_and_udp"
CFG_FAST_OPEN="false"
CFG_PLUGIN=""
CFG_PLUGIN_OPTS=""
write_json_config "$outfile"
# Reset and parse
CFG_SERVER=""
CFG_SERVER_PORT=""
CFG_PASSWORD=""
CFG_METHOD=""
CFG_MODE=""
CFG_PLUGIN="should_be_cleared_if_found"
parse_existing_config "$outfile"
assert_eq "roundtrip-noplugin server" "0.0.0.0" "$CFG_SERVER"
assert_eq "roundtrip-noplugin port" "8388" "$CFG_SERVER_PORT"
assert_eq "roundtrip-noplugin password" "simplepw" "$CFG_PASSWORD"
assert_eq "roundtrip-noplugin method" "aes-256-gcm" "$CFG_METHOD"
assert_eq "roundtrip-noplugin mode" "tcp_and_udp" "$CFG_MODE"
}
###############################################################################
# Tests: detect_os
###############################################################################
test_detect_os() {
local os
os=$(detect_os)
# Should be one of the known values
assert_match "detect_os returns known value" "^(linux|darwin|freebsd|openbsd|netbsd)$" "$os"
}
###############################################################################
# Tests: detect_arch
###############################################################################
test_detect_arch() {
local arch
arch=$(detect_arch)
# Should be one of the known mapped values or raw uname -m
assert_match "detect_arch returns a value" ".+" "$arch"
}
###############################################################################
# Tests: KNOWN_PLUGINS constant
###############################################################################
test_known_plugins_count() {
assert_eq "KNOWN_PLUGINS has 4 entries" "4" "${#KNOWN_PLUGINS[@]}"
}
test_known_plugins_entries() {
local found_simpleobfs=0 found_v2ray=0 found_xray=0 found_kcptun=0
for p in "${KNOWN_PLUGINS[@]}"; do
case "$p" in
simple-obfs) found_simpleobfs=1 ;;
v2ray-plugin) found_v2ray=1 ;;
xray-plugin) found_xray=1 ;;
kcptun) found_kcptun=1 ;;
esac
done
assert_eq "KNOWN_PLUGINS has simple-obfs" "1" "$found_simpleobfs"
assert_eq "KNOWN_PLUGINS has v2ray-plugin" "1" "$found_v2ray"
assert_eq "KNOWN_PLUGINS has xray-plugin" "1" "$found_xray"
assert_eq "KNOWN_PLUGINS has kcptun" "1" "$found_kcptun"
}
###############################################################################
# Tests: plugin_repo function
###############################################################################
test_plugin_repos() {
assert_eq "plugin_repo simple-obfs" "shadowsocks/simple-obfs" "$(plugin_repo simple-obfs)"
assert_eq "plugin_repo v2ray-plugin" "shadowsocks/v2ray-plugin" "$(plugin_repo v2ray-plugin)"
assert_eq "plugin_repo xray-plugin" "teddysun/xray-plugin" "$(plugin_repo xray-plugin)"
assert_eq "plugin_repo kcptun" "xtaci/kcptun" "$(plugin_repo kcptun)"
assert_eq "plugin_repo unknown" "" "$(plugin_repo unknown)"
}
###############################################################################
# Tests: SS_SETUP_VERSION
###############################################################################
test_version_set() {
assert_match "SS_SETUP_VERSION is semver" "^[0-9]+\.[0-9]+\.[0-9]+$" "$SS_SETUP_VERSION"
}
###############################################################################
# Tests: Config directory constant
###############################################################################
test_config_dir() {
assert_eq "CONFIG_DIR" "/etc/shadowsocks-libev" "$CONFIG_DIR"
}
###############################################################################
# Tests: JSON output is valid (no trailing commas, booleans unquoted)
###############################################################################
test_json_no_trailing_comma() {
local outfile="${TMPDIR_TEST}/no_trailing.json"
CFG_SERVER="0.0.0.0"
CFG_SERVER_PORT="8388"
CFG_PASSWORD="pw"
CFG_TIMEOUT="300"
CFG_METHOD="aes-256-gcm"
CFG_MODE="tcp_and_udp"
CFG_FAST_OPEN="false"
CFG_PLUGIN=""
CFG_PLUGIN_OPTS=""
write_json_config "$outfile"
# Check that the file doesn't have ",\n}" pattern (trailing comma)
if grep -qP ',\s*\}' "$outfile" 2>/dev/null || grep -q ',$' "$outfile" 2>/dev/null; then
# Try a more portable check
local last_data_line
last_data_line=$(grep -v '^[[:space:]]*[{}]' "$outfile" | tail -1)
if [[ "$last_data_line" == *"," ]]; then
FAIL=$((FAIL + 1))
echo "FAIL: JSON has trailing comma on last data line: ${last_data_line}" >&2
else
PASS=$((PASS + 1))
fi
else
PASS=$((PASS + 1))
fi
}
test_json_booleans_unquoted() {
local outfile="${TMPDIR_TEST}/bool_check.json"
CFG_SERVER="0.0.0.0"
CFG_SERVER_PORT="8388"
CFG_PASSWORD="pw"
CFG_TIMEOUT="300"
CFG_METHOD="aes-256-gcm"
CFG_MODE="tcp_and_udp"
CFG_FAST_OPEN="true"
CFG_PLUGIN=""
CFG_PLUGIN_OPTS=""
write_json_config "$outfile"
local content
content=$(cat "$outfile")
assert_not_contains "boolean not quoted string" '"fast_open": "true"' "$content"
assert_contains "boolean is unquoted true" '"fast_open": true' "$content"
}
test_json_integers_unquoted() {
local outfile="${TMPDIR_TEST}/int_check.json"
CFG_SERVER="0.0.0.0"
CFG_SERVER_PORT="8388"
CFG_PASSWORD="pw"
CFG_TIMEOUT="300"
CFG_METHOD="aes-256-gcm"
CFG_MODE="tcp_and_udp"
CFG_FAST_OPEN="false"
CFG_PLUGIN=""
CFG_PLUGIN_OPTS=""
write_json_config "$outfile"
local content
content=$(cat "$outfile")
assert_contains "server_port is unquoted int" '"server_port": 8388' "$content"
assert_not_contains "server_port not quoted string" '"server_port": "8388"' "$content"
assert_contains "timeout is unquoted int" '"timeout": 300' "$content"
assert_not_contains "timeout not quoted string" '"timeout": "300"' "$content"
}
###############################################################################
# Tests: Client JSON integers unquoted
###############################################################################
test_client_json_integers_unquoted() {
local outfile="${TMPDIR_TEST}/client_int_check.json"
CFG_CLIENT_SERVER="1.2.3.4"
CFG_CLIENT_SERVER_PORT="443"
CFG_CLIENT_LOCAL_PORT="1080"
CFG_CLIENT_PASSWORD="pw"
CFG_CLIENT_METHOD="aes-256-gcm"
CFG_CLIENT_PLUGIN=""
CFG_CLIENT_PLUGIN_OPTS=""
write_client_json_config "$outfile"
local content
content=$(cat "$outfile")
assert_contains "client server_port unquoted" '"server_port": 443' "$content"
assert_contains "client local_port unquoted" '"local_port": 1080' "$content"
assert_contains "client timeout unquoted" '"timeout": 300' "$content"
}
###############################################################################
# Run all tests
###############################################################################
echo "Running ss-setup unit tests..."
echo
# Constants
test_aead_ciphers_count
test_aead_ciphers_contains_chacha20
test_aead_ciphers_contains_aes256gcm
test_aead_ciphers_contains_aes128gcm
test_known_plugins_count
test_known_plugins_entries
test_plugin_repos
test_version_set
test_config_dir
# Validation
test_validate_port_valid
test_validate_port_invalid
test_validate_instance_name_valid
test_validate_instance_name_invalid
# Password generation
test_generate_password_nonempty
test_generate_password_length
test_generate_password_no_newlines
test_generate_password_different
# Random port
test_generate_random_port_range
test_generate_random_port_numeric
# JSON escaping
test_json_escape_plain
test_json_escape_quotes
test_json_escape_backslash
test_json_escape_tab
test_json_escape_newline
test_json_escape_empty
# URL encoding
test_urlencode_plain
test_urlencode_space
test_urlencode_special
test_urlencode_semicolon
test_urlencode_safe_chars
test_urlencode_empty
# ss:// URI
test_ss_uri_basic
test_ss_uri_with_plugin
test_ss_uri_with_plugin_no_opts
test_ss_uri_base64_encoding
# Server JSON config
test_write_json_config_basic
test_write_json_config_fast_open_true
test_write_json_config_with_plugin
test_write_json_config_with_plugin_no_opts
test_write_json_config_password_special_chars
# Client JSON config
test_write_client_json_config_basic
test_write_client_json_config_with_plugin
test_client_json_integers_unquoted
# Parse existing config (round-trip)
test_parse_existing_config_roundtrip
test_parse_existing_config_no_plugin
# Platform detection
test_detect_os
test_detect_arch
# JSON validity
test_json_no_trailing_comma
test_json_booleans_unquoted
test_json_integers_unquoted
echo
echo "================================="
echo "Results: ${PASS} passed, ${FAIL} failed"
echo "================================="
if [[ $FAIL -gt 0 ]]; then
exit 1
fi
exit 0
+76
View File
@@ -0,0 +1,76 @@
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif
#include <assert.h>
#include <string.h>
#include <stdlib.h>
int verbose = 0;
#include "utils.h"
static void
test_ss_itoa(void)
{
char *s;
s = ss_itoa(0);
assert(s != NULL);
assert(strcmp(s, "0") == 0);
s = ss_itoa(42);
assert(s != NULL);
assert(strcmp(s, "42") == 0);
s = ss_itoa(-1);
assert(s != NULL);
assert(strcmp(s, "-1") == 0);
s = ss_itoa(12345);
assert(s != NULL);
assert(strcmp(s, "12345") == 0);
(void)s;
}
static void
test_ss_isnumeric(void)
{
assert(ss_isnumeric("12345") == 1);
assert(ss_isnumeric("0") == 1);
assert(ss_isnumeric("") == 0);
assert(ss_isnumeric("abc") == 0);
assert(ss_isnumeric("123abc") == 0);
assert(ss_isnumeric("12.34") == 0);
}
static void
test_ss_strndup(void)
{
char *s;
s = ss_strndup("hello world", 5);
assert(s != NULL);
assert(strcmp(s, "hello") == 0);
assert(strlen(s) == 5);
free(s);
s = ss_strndup("short", 10);
assert(s != NULL);
assert(strcmp(s, "short") == 0);
free(s);
s = ss_strndup("", 0);
assert(s != NULL);
assert(strcmp(s, "") == 0);
free(s);
}
int
main(void)
{
test_ss_itoa();
test_ss_isnumeric();
test_ss_strndup();
return 0;
}
+13 -13
View File
@@ -863,7 +863,7 @@ dependencies = [
"libc",
"option-ext",
"redox_users",
"windows-sys 0.61.2",
"windows-sys 0.59.0",
]
[[package]]
@@ -976,7 +976,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "39cab71617ae0d63f51a36d69f866391735b51691dbda63cf6f96d042b63efeb"
dependencies = [
"libc",
"windows-sys 0.52.0",
"windows-sys 0.59.0",
]
[[package]]
@@ -1506,7 +1506,7 @@ dependencies = [
"libc",
"percent-encoding",
"pin-project-lite",
"socket2 0.6.2",
"socket2 0.5.10",
"system-configuration",
"tokio",
"tower-service",
@@ -2196,7 +2196,7 @@ version = "0.50.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7957b9740744892f114936ab4a57b3f487491bbeafaf8083688b16841a4240e5"
dependencies = [
"windows-sys 0.61.2",
"windows-sys 0.59.0",
]
[[package]]
@@ -2527,7 +2527,7 @@ dependencies = [
"quinn-udp",
"rustc-hash",
"rustls",
"socket2 0.6.2",
"socket2 0.5.10",
"thiserror 2.0.18",
"tokio",
"tracing",
@@ -2565,9 +2565,9 @@ dependencies = [
"cfg_aliases",
"libc",
"once_cell",
"socket2 0.6.2",
"socket2 0.5.10",
"tracing",
"windows-sys 0.52.0",
"windows-sys 0.59.0",
]
[[package]]
@@ -2680,9 +2680,9 @@ checksum = "7a2d987857b319362043e95f5353c0535c1f58eec5336fdfcf626430af7def58"
[[package]]
name = "reqwest"
version = "0.13.1"
version = "0.13.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "04e9018c9d814e5f30cc16a0f03271aeab3571e609612d9fe78c1aa8d11c2f62"
checksum = "ab3f43e3283ab1488b624b44b0e988d0acea0b3214e694730a055cb6b2efa801"
dependencies = [
"base64",
"bytes",
@@ -2827,7 +2827,7 @@ dependencies = [
"errno",
"libc",
"linux-raw-sys",
"windows-sys 0.52.0",
"windows-sys 0.59.0",
]
[[package]]
@@ -2886,7 +2886,7 @@ dependencies = [
"security-framework 3.5.1",
"security-framework-sys",
"webpki-root-certs",
"windows-sys 0.52.0",
"windows-sys 0.59.0",
]
[[package]]
@@ -3532,7 +3532,7 @@ dependencies = [
"getrandom 0.3.4",
"once_cell",
"rustix",
"windows-sys 0.52.0",
"windows-sys 0.59.0",
]
[[package]]
@@ -4190,7 +4190,7 @@ version = "0.1.11"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c2a7b1c03c876122aa43f3020e6c3c3ee5c05081c9a00739faf7503aeba10d22"
dependencies = [
"windows-sys 0.48.0",
"windows-sys 0.59.0",
]
[[package]]
@@ -44,7 +44,7 @@ Can be `domain` or `ipcidr` or `classical`. Rule file behavior.
#### interval
The update interval for the Rule set, in seconds or /^(\d+)(s|m|h|d)?$/.
The update interval for the Rule set, in seconds or `/^(\d+)(s|m|h|d)?$/`.
#### rawQuery
@@ -276,6 +276,12 @@ return view.extend({
so.depends({type: /^(hysteria|hysteria2)$/});
so.modalonly = true;
so = ss.taboption('field_general', form.Value, 'hysteria_hop_interval', _('Port hop interval'),
_('In seconds. <code>%s</code> will be used if empty.').format('30'));
so.datatype = 'uinteger';
so.depends('type', 'hysteria2');
so.modalonly = true;
so = ss.taboption('field_general', form.Value, 'hysteria_up_mbps', _('Max upload speed'),
_('In Mbps.'));
so.datatype = 'uinteger';
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff

Some files were not shown because too many files have changed in this diff Show More