Update On Fri May 10 20:29:28 CEST 2024

This commit is contained in:
github-action[bot]
2024-05-10 20:29:29 +02:00
parent 3c2b6ed29b
commit b461e630c3
341 changed files with 13067 additions and 3236 deletions
+1
View File
@@ -642,3 +642,4 @@ Update On Mon May 6 20:31:49 CEST 2024
Update On Tue May 7 20:29:39 CEST 2024
Update On Thu May 9 16:22:44 CEST 2024
Update On Thu May 9 20:28:46 CEST 2024
Update On Fri May 10 20:29:17 CEST 2024
+186 -116
View File
@@ -4,7 +4,7 @@
A cross-platform programmable network tool.
# Sponsor
**❤️ [Shiliew - China Optimized Network App](https://www.txthinking.com/shiliew.html)**
**❤️ [Shiliew - A network app designed for those who value their time](https://www.txthinking.com/shiliew.html)**
# Getting Started
## Server
@@ -28,11 +28,7 @@ brook server -l :9999 -p hello
| [![](https://brook.app/images/appstore.png)](https://apps.apple.com/us/app/brook-network-tool/id1216002642) | [![](https://brook.app/images/android.png)](https://github.com/txthinking/brook/releases/latest/download/Brook.apk) | [![](https://brook.app/images/mac.png)](https://apps.apple.com/us/app/brook-network-tool/id1216002642) | [![Windows](https://brook.app/images/windows.png)](https://github.com/txthinking/brook/releases/latest/download/Brook.msix) | [![](https://brook.app/images/linux.png)](https://github.com/txthinking/brook/releases/latest/download/Brook.bin) | [![OpenWrt](https://brook.app/images/openwrt.png)](https://github.com/txthinking/brook/releases) |
| / | / | [App Mode](https://www.txthinking.com/talks/articles/macos-app-mode-en.article) | [How](https://www.txthinking.com/talks/articles/msix-brook-en.article) | [How](https://www.txthinking.com/talks/articles/linux-app-brook-en.article) | [How](https://www.txthinking.com/talks/articles/brook-openwrt-en.article) |
## CLI Client
```
brook client -s 1.2.3.4:9999 -p hello --socks5 127.0.0.1:1080
```
> You may want to use `brook link` to customize some parameters
# GUI Documentation
## Software for which this article applies
@@ -43,11 +39,12 @@ brook client -s 1.2.3.4:9999 -p hello --socks5 127.0.0.1:1080
## Programmable
```
Brook GUI will pass different global variables to the script at different times, and the script only needs to assign the processing result to the global variable out
```
Brook GUI will pass different _global variables_ to the script at different times, and the script only needs to assign the processing result to the global variable `out`
### Introduction to incoming variables
- address: We call it address which includes both host and port. For example, an ip address contains an ip and a port; a domain address contains a domain and a port.
- Fake DNS: Fake DNS can allow you to obtain domain address on `in_address` step. [How Fake DNS works](https://www.txthinking.com/talks/articles/brook-fakedns-en.article)
### Variables
| variable | type | condition | timing | description | out type |
| ------------------------------ | ---- | ----------- | --------------------------------- | ------------------------------------------------- | -------- |
@@ -139,7 +136,53 @@ Brook GUI will pass different global variables to the script at different times,
`out`, must be set to a response
## Write script
## Module
There are already some modules: https://github.com/txthinking/brook/blob/master/programmable/modules/
### Brook GUI
In Brook GUI, scripts are abstracted into modules, and it will automatically combine [_header.tengo](https://github.com/txthinking/brook/blob/master/programmable/modules/_header.tengo) and [_footer.tengo](https://github.com/txthinking/brook/blob/master/programmable/modules/_footer.tengo), so you only need to write the module itself.
```
modules = append(modules, {
// If you want to predefine multiple brook links, and then programmatically specify which one to connect to, then define `brooklinks` key a function
brooklinks: func(m) {
// Please refer to the example in `brooklinks.tengo`
},
// If you want to intercept and handle a DNS query, then define `dnsquery` key a function, `m` is the `in_dnsquery`
dnsquery: func(m) {
// Please refer to the example in `block_aaaa.tengo`
},
// If you want to intercept and handle an address, then define `address` key a function, `m` is the `in_address`
address: func(m) {
// Please refer to the example in `block_google_secure_dns.tengo`
},
// If you want to intercept and handle a http request, then define `httprequest` key a function, `request` is the `in_httprequest`
httprequest: func(request) {
// Please refer to the example in `ios_app_downgrade.tengo` or `redirect_google_cn.tengo`
},
// If you want to intercept and handle a http response, then define `httpresponse` key a function, `request` is the `in_httprequest`, `response` is the `in_httpresponse`
httpresponse: func(request, response) {
// Please refer to the example in `response_sample.tengo`
}
})
```
### tun2brook
If you are using tun2brook, you can combine multiple modules into a complete script in the following way. For example:
```
cat _header.tengo > my.tengo
cat block_google_secure_dns.tengo >> my.tengo
cat block_aaaa.tengo >> my.tengo
cat _footer.tengo >> my.tengo
```
## Syntax
[Tengo Language Syntax](https://github.com/d5/tengo/blob/master/docs/tutorial.md)
@@ -186,9 +229,9 @@ Library
* hexencode(s string) => string/error: returns the hexadecimal encoding of src
```
## Debug script
## Debug
It is recommended to use [tun2brook](https://github.com/txthinking/tun2brook) on desktop to debug with `fmt.println`
If you are writing complex scripts, the GUI may not be convenient for debugging. It is recommended to use [tun2brook](https://github.com/txthinking/tun2brook) on desktop to debug with `fmt.println`
## Install CA
@@ -232,8 +275,7 @@ https://txthinking.github.io/ca/ca.pem
| [Socks5 Configurator](https://chromewebstore.google.com/detail/socks5-configurator/hnpgnjkeaobghpjjhaiemlahikgmnghb) | If you prefer CLI brook client |
| [IPvBar](https://chromewebstore.google.com/detail/ipvbar/nepjlegfiihpkcdhlmaebfdfppckonlj) | See domain, IP and country in browser |
| [TxThinking SSH](https://www.txthinking.com/ssh.html) | A SSH Terminal |
| [Brook Deploy](https://www.txthinking.com/deploy.html) | Deploy brook with GUI |
| [brook-manager](https://github.com/txthinking/brook-manager) | Brook Manager is a Brook management system for medium to large merchants |
| [brook-user-system](https://github.com/txthinkinginc/brook-user-system) | A Brook User System |
| [TxThinking](https://www.txthinking.com) | Everything |
# CLI Documentation
@@ -257,6 +299,16 @@ Brook [GLOBAL OPTIONS] command [COMMAND OPTIONS] [ARGUMENTS...]
# GLOBAL OPTIONS
- **--blockCIDR4List**="": One CIDR per line, https://, http:// or local file absolute path, like: https://raw.githubusercontent.com/txthinking/brook/master/programmable/list/example_cidr4.txt. Works with server/wsserver/wssserver/quicserver
- **--blockCIDR6List**="": One CIDR per line, https://, http:// or local file absolute path, like: https://raw.githubusercontent.com/txthinking/brook/master/programmable/list/example_cidr6.txt. Works with server/wsserver/wssserver/quicserver
- **--blockDomainList**="": One domain per line, suffix match mode. https://, http:// or local file absolute path. Like: https://raw.githubusercontent.com/txthinking/brook/master/programmable/list/example_domain.txt. Works with server/wsserver/wssserver/quicserver
- **--blockGeoIP**="": Block IP by Geo country code, such as US. Works with server/wsserver/wssserver/quicserver
- **--blockListUpdateInterval**="": Update list --blockDomainList,--blockCIDR4List,--blockCIDR6List interval, second. default 0, only read one time on start (default: 0)
- **--clientHKDFInfo**="": client HKDF info, most time you don't need to change this, if changed, all and each brook links in client side must be same, I mean each (default: "brook")
- **--dialWithDNS**="": When a domain name needs to be resolved, use the specified DNS. Such as 8.8.8.8:53 or https://dns.google/dns-query?address=8.8.8.8%3A443, the address is required. Note that for client-side commands, this does not affect the client passing the domain address to the server
@@ -287,7 +339,9 @@ Brook [GLOBAL OPTIONS] command [COMMAND OPTIONS] [ARGUMENTS...]
- **--ipLimitWait**="": How long (s) to wait for recovery after exceeding ipLimitMax (default: 0)
- **--log**="": Enable log. A valid value is file path or 'console'. If you want to debug SOCKS5 lib, set env SOCKS5_DEBUG=true
- **--log**="": Enable log. A valid value is file path or 'console'. Send SIGUSR1 to me to reset the log file on unix system. If you want to debug SOCKS5 lib, set env SOCKS5_DEBUG=true
- **--pid**="": A file path used to store pid. Send SIGUSR1 to me to reset the --serverLog file on unix system
- **--pprof**="": go http pprof listen addr, such as :6060
@@ -297,11 +351,17 @@ Brook [GLOBAL OPTIONS] command [COMMAND OPTIONS] [ARGUMENTS...]
- **--serverHKDFInfo**="": server HKDF info, most time you don't need to change this, if changed, all and each brook links in client side must be same, I mean each (default: "brook")
- **--serverLog**="": Enable server log, traffic and more. A valid value is file path or 'console'. Mutually exclusive with the --log parameter. Works with server/wsserver/wssserver/quicserver
- **--serverLog**="": Enable server log, traffic and more. A valid value is file path or 'console'. Send SIGUSR1 to me to reset the log file on unix system. Mutually exclusive with the --log parameter. Works with server/wsserver/wssserver/quicserver with brook protocol
- **--speedLimit**="": Limit speed (b), such as 500kb/s: 500000, works with server/wsserver/wssserver/quicserver (default: 0)
- **--speedLimit**="": Limit speed (b), 500kb/s such as: 500000, works with server/wsserver/wssserver/quicserver (default: 0)
- **--tag**="": Tag can be used to the process, will be append into log or serverLog, such as: 'key1:value1'
- **--tag**="": Tag can be used to the process, will be append into log or serverLog, such as: 'key1:value1'. All tags will also be appended as query parameters one by one to the userAPI
- **--userAPI**="": When you build your own user system, Brook Server will send GET request to your userAPI to check if token is valid, for example: https://your-api-server.com/a_unpredictable_path. Yes, it is recommended to add an unpredictable path to your https API, of course, you can also use the http api for internal network communication. The request format is https://your-api-server.com/a_unpredictable_path?token=xxx. When the response is 200, the body should be the user's unique identifier, such as user ID; all other status codes are considered to represent an illegitimate user, and in these cases, the body should be a string describing the error. It should be used with --serverLog and server/wsserver/wssserver/quicserver with brook protocol. For more information, please read https://github.com/txthinking/brook/blob/master/protocol/user.md
- **--userAPIInvalidCacheTime**="": Once a token is checked and invalid, the userAPI will not be requested to validate again for a certain period (s). A reasonable value must be set, otherwise it will affect the performance of each incoming connection. Note that this may affect the user experience, when you change the user status from invalid to valid in your user system (default: 1800)
- **--userAPIValidCacheTime**="": Once a token is checked and valid, the userAPI will not be requested to validate again for a certain period (s). A reasonable value must be set, otherwise it will affect the performance of each incoming connection (default: 3600)
- **--version, -v**: print the version
@@ -310,15 +370,17 @@ Brook [GLOBAL OPTIONS] command [COMMAND OPTIONS] [ARGUMENTS...]
## server
Run as brook server, both TCP and UDP
Start a brook server that supports tcp and udp
- **--blockCIDR4List**="": One CIDR per line, https://, http:// or local file absolute path, like: https://raw.githubusercontent.com/txthinking/brook/master/programmable/list/example_cidr4.txt
- **--blockCIDR4List**="": This option will be removed in a future version, please use the global option instead
- **--blockCIDR6List**="": One CIDR per line, https://, http:// or local file absolute path, like: https://raw.githubusercontent.com/txthinking/brook/master/programmable/list/example_cidr6.txt
- **--blockCIDR6List**="": This option will be removed in a future version, please use the global option instead
- **--blockDomainList**="": One domain per line, suffix match mode. https://, http:// or local file absolute path. Like: https://raw.githubusercontent.com/txthinking/brook/master/programmable/list/example_domain.txt
- **--blockDomainList**="": This option will be removed in a future version, please use the global option instead
- **--blockGeoIP**="": Block IP by Geo country code, such as US
- **--blockGeoIP**="": This option will be removed in a future version, please use the global option instead
- **--example**: Show a minimal example of usage
- **--listen, -l**="": Listen address, like: ':9999'
@@ -328,14 +390,18 @@ Run as brook server, both TCP and UDP
- **--udpTimeout**="": time (s) (default: 0)
- **--updateListInterval**="": Update list interval, second. default 0, only read one time on start (default: 0)
- **--updateListInterval**="": This option will be removed in a future version, please use the global option instead (default: 0)
## client
Run as brook client, both TCP and UDP, to start a socks5 proxy, [src <-> socks5 <-> $ brook client <-> $ brook server <-> dst]
Start a brook client that supports tcp and udp. It can open a socks5 proxy, [src <-> socks5 <-> $ brook client <-> $ brook server <-> dst]
- **--example**: Show a minimal example of usage
- **--http**="": Where to listen for HTTP proxy connections
- **--link**="": brook link, you can get it via $ brook link. The wssserver and password parameters will be ignored
- **--password, -p**="": Brook server password
- **--server, -s**="": Brook server address, like: 1.2.3.4:9999
@@ -348,19 +414,19 @@ Run as brook client, both TCP and UDP, to start a socks5 proxy, [src <-> socks5
- **--udpTimeout**="": time (s) (default: 0)
- **--udpovertcp**: UDP over TCP
## wsserver
Run as brook wsserver, both TCP and UDP, it will start a standard http server and websocket server
Start a brook wsserver that supports tcp and udp. It opens a standard http server and a websocket server
- **--blockCIDR4List**="": One CIDR per line, https://, http:// or local file absolute path, like: https://raw.githubusercontent.com/txthinking/brook/master/programmable/list/example_cidr4.txt
- **--blockCIDR4List**="": This option will be removed in a future version, please use the global option instead
- **--blockCIDR6List**="": One CIDR per line, https://, http:// or local file absolute path, like: https://raw.githubusercontent.com/txthinking/brook/master/programmable/list/example_cidr6.txt
- **--blockCIDR6List**="": This option will be removed in a future version, please use the global option instead
- **--blockDomainList**="": One domain per line, suffix match mode. https://, http:// or local file absolute path. Like: https://raw.githubusercontent.com/txthinking/brook/master/programmable/list/example_domain.txt
- **--blockDomainList**="": This option will be removed in a future version, please use the global option instead
- **--blockGeoIP**="": Block IP by Geo country code, such as US
- **--blockGeoIP**="": This option will be removed in a future version, please use the global option instead
- **--example**: Show a minimal example of usage
- **--listen, -l**="": Listen address, like: ':80'
@@ -372,7 +438,7 @@ Run as brook wsserver, both TCP and UDP, it will start a standard http server an
- **--udpTimeout**="": time (s) (default: 0)
- **--updateListInterval**="": Update list interval, second. default 0, only read one time on start (default: 0)
- **--updateListInterval**="": This option will be removed in a future version, please use the global option instead (default: 0)
- **--withoutBrookProtocol**: The data will not be encrypted with brook protocol
@@ -380,12 +446,14 @@ Run as brook wsserver, both TCP and UDP, it will start a standard http server an
## wsclient
Run as brook wsclient, both TCP and UDP, to start a socks5 proxy, [src <-> socks5 <-> $ brook wsclient <-> $ brook wsserver <-> dst]
Start a brook wsclient that supports tcp and udp. It can open a socks5 proxy, [src <-> socks5 <-> $ brook wsclient <-> $ brook wsserver <-> dst]
- **--address**="": Specify address instead of resolving addresses from host, such as 1.2.3.4:443
- **--example**: Show a minimal example of usage
- **--http**="": Where to listen for HTTP proxy connections
- **--link**="": brook link, you can get it via $ brook link. The wssserver and password parameters will be ignored
- **--password, -p**="": Brook wsserver password
- **--socks5**="": Where to listen for SOCKS5 connections (default: 127.0.0.1:1080)
@@ -396,21 +464,19 @@ Run as brook wsclient, both TCP and UDP, to start a socks5 proxy, [src <-> socks
- **--udpTimeout**="": time (s) (default: 0)
- **--withoutBrookProtocol**: The data will not be encrypted with brook protocol
- **--wsserver, -s**="": Brook wsserver address, like: ws://1.2.3.4:80, if no path then /ws will be used. Do not omit the port under any circumstances
## wssserver
Run as brook wssserver, both TCP and UDP, it will start a standard https server and websocket server
Start a brook wssserver that supports tcp and udp. It opens a standard https server and a websocket server
- **--blockCIDR4List**="": One CIDR per line, https://, http:// or local file absolute path, like: https://raw.githubusercontent.com/txthinking/brook/master/programmable/list/example_cidr4.txt
- **--blockCIDR4List**="": This option will be removed in a future version, please use the global option instead
- **--blockCIDR6List**="": One CIDR per line, https://, http:// or local file absolute path, like: https://raw.githubusercontent.com/txthinking/brook/master/programmable/list/example_cidr6.txt
- **--blockCIDR6List**="": This option will be removed in a future version, please use the global option instead
- **--blockDomainList**="": One domain per line, suffix match mode. https://, http:// or local file absolute path. Like: https://raw.githubusercontent.com/txthinking/brook/master/programmable/list/example_domain.txt
- **--blockDomainList**="": This option will be removed in a future version, please use the global option instead
- **--blockGeoIP**="": Block IP by Geo country code, such as US
- **--blockGeoIP**="": This option will be removed in a future version, please use the global option instead
- **--cert**="": The cert file absolute path for the domain, such as /path/to/cert.pem. If cert or certkey is empty, a certificate will be issued automatically
@@ -418,6 +484,8 @@ Run as brook wssserver, both TCP and UDP, it will start a standard https server
- **--domainaddress**="": Such as: domain.com:443. If you choose to automatically issue certificates, the domain must have been resolved to the server IP and 80 port also will be used
- **--example**: Show a minimal example of usage
- **--password, -p**="": Server password
- **--path**="": URL path (default: /ws)
@@ -426,13 +494,15 @@ Run as brook wssserver, both TCP and UDP, it will start a standard https server
- **--udpTimeout**="": time (s) (default: 0)
- **--updateListInterval**="": Update list interval, second. default 0, only read one time on start (default: 0)
- **--updateListInterval**="": This option will be removed in a future version, please use the global option instead (default: 0)
- **--withoutBrookProtocol**: The data will not be encrypted with brook protocol
## wssclient
Run as brook wssclient, both TCP and UDP, to start a socks5 proxy, [src <-> socks5 <-> $ brook wssclient <-> $ brook wssserver <-> dst]
Start a brook wssclient that supports tcp and udp. It can open a socks5 proxy, [src <-> socks5 <-> $ brook wssclient <-> $ brook wssserver <-> dst]
- **--example**: Show a minimal example of usage
- **--http**="": Where to listen for HTTP proxy connections
@@ -452,15 +522,15 @@ Run as brook wssclient, both TCP and UDP, to start a socks5 proxy, [src <-> sock
## quicserver
Run as brook quicserver, both TCP and UDP
Start a brook quicserver that supports tcp and udp.
- **--blockCIDR4List**="": One CIDR per line, https://, http:// or local file absolute path, like: https://raw.githubusercontent.com/txthinking/brook/master/programmable/list/example_cidr4.txt
- **--blockCIDR4List**="": This option will be removed in a future version, please use the global option instead
- **--blockCIDR6List**="": One CIDR per line, https://, http:// or local file absolute path, like: https://raw.githubusercontent.com/txthinking/brook/master/programmable/list/example_cidr6.txt
- **--blockCIDR6List**="": This option will be removed in a future version, please use the global option instead
- **--blockDomainList**="": One domain per line, suffix match mode. https://, http:// or local file absolute path. Like: https://raw.githubusercontent.com/txthinking/brook/master/programmable/list/example_domain.txt
- **--blockDomainList**="": This option will be removed in a future version, please use the global option instead
- **--blockGeoIP**="": Block IP by Geo country code, such as US
- **--blockGeoIP**="": This option will be removed in a future version, please use the global option instead
- **--cert**="": The cert file absolute path for the domain, such as /path/to/cert.pem. If cert or certkey is empty, a certificate will be issued automatically
@@ -468,31 +538,27 @@ Run as brook quicserver, both TCP and UDP
- **--domainaddress**="": Such as: domain.com:443. If you choose to automatically issue certificates, the domain must have been resolved to the server IP and 80 port also will be used
- **--example**: Show a minimal example of usage
- **--password, -p**="": Server password
- **--tcpTimeout**="": time (s) (default: 0)
- **--udpTimeout**="": time (s) (default: 0)
- **--updateListInterval**="": Update list interval, second. default 0, only read one time on start (default: 0)
- **--updateListInterval**="": This option will be removed in a future version, please use the global option instead (default: 0)
- **--withoutBrookProtocol**: The data will not be encrypted with brook protocol
## quicclient
Run as brook quicclient, both TCP and UDP, to start a socks5 proxy, [src <-> socks5 <-> $ brook quicclient <-> $ brook quicserver <-> dst]. (Note that the global dial parameter is ignored now)
Start a brook quicclient that supports tcp and udp. It can open a socks5 proxy, [src <-> socks5 <-> $ brook quicclient <-> $ brook quicserver <-> dst]. (The global-dial-parameter is ignored)
- **--address**="": Specify address instead of resolving addresses from host, such as 1.2.3.4:443
- **--ca**="": Specify ca instead of insecure, such as /path/to/ca.pem
- **--example**: Show a minimal example of usage
- **--http**="": Where to listen for HTTP proxy connections
- **--insecure**: Client do not verify the server's certificate chain and host name
- **--password, -p**="": Brook quicserver password
- **--quicserver, -s**="": Brook quicserver address, like: quic://google.com:443. Do not omit the port under any circumstances
- **--link**="": brook link, you can get it via $ brook link. The wssserver and password parameters will be ignored
- **--socks5**="": Where to listen for SOCKS5 connections (default: 127.0.0.1:1080)
@@ -502,11 +568,11 @@ Run as brook quicclient, both TCP and UDP, to start a socks5 proxy, [src <-> soc
- **--udpTimeout**="": time (s) (default: 0)
- **--withoutBrookProtocol**: The data will not be encrypted with brook protocol
## relayoverbrook
Run as relay over brook, both TCP and UDP, this means access [from address] is equal to [to address], [src <-> from address <-> $ brook server/wsserver/wssserver/quicserver <-> to address]
Relay network traffic over brook, which supports TCP and UDP. Accessing [from address] is equal to accessing [to address], [src <-> from address <-> $ brook server/wsserver/wssserver/quicserver <-> to address]
- **--example**: Show a minimal example of usage
- **--from, -f, -l**="": Listen address: like ':9999'
@@ -524,7 +590,7 @@ Run as relay over brook, both TCP and UDP, this means access [from address] is e
## dnsserveroverbrook
Run as dns server over brook, both TCP and UDP, [src <-> $ brook dnserversoverbrook <-> $ brook server/wsserver/wssserver/quicserver <-> dns] or [src <-> $ brook dnsserveroverbrook <-> dnsForBypass]
Run a dns server over brook, which supports TCP and UDP, [src <-> $ brook dnserversoverbrook <-> $ brook server/wsserver/wssserver/quicserver <-> dns] or [src <-> $ brook dnsserveroverbrook <-> dnsForBypass]
- **--blockDomainList**="": One domain per line, suffix match mode. https://, http:// or local absolute file path. Like: https://raw.githubusercontent.com/txthinking/brook/master/programmable/list/example_domain.txt
@@ -538,6 +604,8 @@ Run as dns server over brook, both TCP and UDP, [src <-> $ brook dnserversoverbr
- **--dnsForBypass**="": DNS server for resolving domains in bypass list. Such as 223.5.5.5:53 or https://dns.alidns.com/dns-query?address=223.5.5.5:443, the address is required (default: 223.5.5.5:53)
- **--example**: Show a minimal example of usage
- **--link**="": brook link, you can get it via $ brook link. The server and password parameters will be ignored
- **--listen, -l**="": Listen address, like: 127.0.0.1:53
@@ -552,7 +620,7 @@ Run as dns server over brook, both TCP and UDP, [src <-> $ brook dnserversoverbr
## link
Generate brook link
Generate a brook link
- **--address**="": When server is brook wsserver or brook wssserver or brook quicserver, specify address instead of resolving addresses from host, such as 1.2.3.4:443
@@ -560,7 +628,9 @@ Generate brook link
- **--clientHKDFInfo**="": client HKDF info, most time you don't need to change this, read brook protocol if you don't know what this is
- **--fragment**="": When server is brook wssserver, split the ClientHello into multiple fragments and then send them one by one with delays (millisecond). The format is min_length:max_length:min_delay:max_delay, cannot be zero, such as 50:100:10:50, Note that: This is an experimental feature, currently only supported by the brook CLI and tun2brook.
- **--example**: Show a minimal example of usage
- **--fragment**="": When server is brook wssserver, split the ClientHello into multiple fragments and then send them one by one with delays (millisecond). The format is min_length:max_length:min_delay:max_delay, cannot be zero, such as 50:100:10:50
- **--insecure**: When server is brook wssserver or brook quicserver, client do not verify the server's certificate chain and host name
@@ -574,6 +644,10 @@ Generate brook link
- **--tlsfingerprint**="": When server is brook wssserver, select tls fingerprint, value can be: chrome
- **--token**="": A token represents a user's identity. A string encoded in hexadecimal. Server needs to have --userAPI enabled. Note that: Only supported by the brook GUI(except for OpenWrt) and tun2brook
- **--udpoverstream**: When server is brook quicserver, UDP over Stream. Under normal circumstances, you need this parameter because the max datagram size for QUIC is very small. Note: only brook CLI and tun2brook suppport for now
- **--udpovertcp**: When server is brook server, UDP over TCP
- **--username, -u**="": Username, when server is socks5 server
@@ -582,7 +656,9 @@ Generate brook link
## connect
Run as client and connect to brook link, both TCP and UDP, to start a socks5 proxy, [src <-> socks5 <-> $ brook connect <-> $ brook server/wsserver/wssserver/quicserver <-> dst]
Run a client and connect with a brook link, which supports TCP and UDP. It can start a socks5 proxy, [src <-> socks5 <-> $ brook connect <-> $ brook server/wsserver/wssserver/quicserver <-> dst]
- **--example**: Show a minimal example of usage
- **--http**="": Where to listen for HTTP proxy connections
@@ -598,7 +674,9 @@ Run as client and connect to brook link, both TCP and UDP, to start a socks5 pro
## relay
Run as standalone relay, both TCP and UDP, this means access [from address] is equal to access [to address], [src <-> from address <-> to address]
Run a standalone relay, which supports TCP and UDP. Accessing [from address] is equal to accessing [to address], [src <-> from address <-> to address]
- **--example**: Show a minimal example of usage
- **--from, -f, -l**="": Listen address: like ':9999'
@@ -610,7 +688,7 @@ Run as standalone relay, both TCP and UDP, this means access [from address] is e
## dnsserver
Run as standalone dns server
Run a standalone dns server
- **--blockDomainList**="": One domain per line, suffix match mode. https://, http:// or local absolute file path. Like: https://raw.githubusercontent.com/txthinking/brook/master/programmable/list/example_domain.txt
@@ -620,6 +698,8 @@ Run as standalone dns server
- **--dns**="": DNS server which forward to. Such as 8.8.8.8:53 or https://dns.google/dns-query?address=8.8.8.8%3A443, the address is required (default: 8.8.8.8:53)
- **--example**: Show a minimal example of usage
- **--listen, -l**="": Listen address, like: 127.0.0.1:53
- **--tcpTimeout**="": time (s) (default: 0)
@@ -634,13 +714,15 @@ Send a dns query
- **--domain, -d**="": Domain
- **--example**: Show a minimal example of usage
- **--short**: Short for A/AAAA
- **--type, -t**="": Type, such as A (default: A)
## dohserver
Run as standalone doh server
Run a standalone doh server
- **--blockDomainList**="": One domain per line, suffix match mode. https://, http:// or local absolute file path. Like: https://raw.githubusercontent.com/txthinking/brook/master/programmable/list/example_domain.txt
@@ -656,6 +738,8 @@ Run as standalone doh server
- **--domainaddress**="": Such as: domain.com:443, if you want to create a https server. If you choose to automatically issue certificates, the domain must have been resolved to the server IP and 80 port also will be used
- **--example**: Show a minimal example of usage
- **--listen**="": listen address, if you want to create a http server behind nico
- **--path**="": URL path (default: /dns-query)
@@ -672,13 +756,15 @@ Send a dns query
- **--domain, -d**="": Domain
- **--example**: Show a minimal example of usage
- **--short**: Short for A/AAAA
- **--type, -t**="": Type, such as A (default: A)
## dhcpserver
Run as standalone dhcp server. Note that you need to stop other dhcp servers, if there are.
Run a standalone dhcp server. Other running dhcp servers need to be stopped.
- **--cache**="": Cache file, local absolute file path, default is $HOME/.brook.dhcpserver
@@ -686,6 +772,8 @@ Run as standalone dhcp server. Note that you need to stop other dhcp servers, if
- **--dnsserver**="": The dns server which you want to assign to clients, such as: 192.168.1.1 or 8.8.8.8
- **--example**: Show a minimal example of usage
- **--gateway**="": The router gateway which you want to assign to clients, such as: 192.168.1.1
- **--interface**="": Select interface on multi interface device. Linux only
@@ -698,7 +786,9 @@ Run as standalone dhcp server. Note that you need to stop other dhcp servers, if
## socks5
Run as standalone standard socks5 server, both TCP and UDP
Run a standalone standard socks5 server, which supports TCP and UDP
- **--example**: Show a minimal example of usage
- **--limitUDP**: The server MAY use this information to limit access to the UDP association. This usually causes connection failures in a NAT environment, where most clients are.
@@ -716,7 +806,9 @@ Run as standalone standard socks5 server, both TCP and UDP
## socks5tohttp
Convert socks5 to http proxy, [src <-> listen address(http proxy) <-> socks5 address <-> dst]
Convert a socks5 proxy to a http proxy, [src <-> listen address(http proxy) <-> socks5 address <-> dst]
- **--example**: Show a minimal example of usage
- **--listen, -l**="": HTTP proxy which will be create: like: 127.0.0.1:8010
@@ -730,10 +822,12 @@ Convert socks5 to http proxy, [src <-> listen address(http proxy) <-> socks5 add
## pac
Run as PAC server or save PAC to file
Run a PAC server or save PAC to a file
- **--bypassDomainList, -b**="": One domain per line, suffix match mode. http(s):// or local absolute file path. Like: https://raw.githubusercontent.com/txthinking/brook/master/programmable/list/example_domain.txt
- **--example**: Show a minimal example of usage
- **--file, -f**="": Save PAC to file, this will ignore listen address
- **--listen, -l**="": Listen address, like: 127.0.0.1:1980
@@ -742,12 +836,14 @@ Run as PAC server or save PAC to file
## testsocks5
Test UDP and TCP of socks5 server
Test a socks5 server to see if it works properly
- **--dns**="": DNS server for connecting (default: 8.8.8.8:53)
- **--domain**="": Domain for query (default: http3.ooo)
- **--example**: Show a minimal example of usage
- **--password, -p**="": Socks5 password
- **--socks5, -s**="": Like: 127.0.0.1:1080
@@ -758,12 +854,14 @@ Test UDP and TCP of socks5 server
## testbrook
Test UDP and TCP of brook server/wsserver/wssserver/quicserver. (Note that the global dial parameter is ignored now)
Test UDP and TCP of a brook server/wsserver/wssserver/quicserver connection. (The global-dial-parameter is ignored)
- **--dns**="": DNS server for connecting (default: 8.8.8.8:53)
- **--domain**="": Domain for query (default: http3.ooo)
- **--example**: Show a minimal example of usage
- **--link, -l**="": brook link. Get it via $ brook link
- **--socks5**="": Temporarily listening socks5 (default: 127.0.0.1:11080)
@@ -774,12 +872,16 @@ Test UDP and TCP of brook server/wsserver/wssserver/quicserver. (Note that the g
Echo server, echo UDP and TCP address of routes
- **--example**: Show a minimal example of usage
- **--listen, -l**="": Listen address, like: ':7777'
## echoclient
Connect to echoserver, echo UDP and TCP address of routes
- **--example**: Show a minimal example of usage
- **--server, -s**="": Echo server address, such as 1.2.3.4:7777
- **--times**="": Times of interactions (default: 0)
@@ -788,18 +890,24 @@ Connect to echoserver, echo UDP and TCP address of routes
Get country of IP
- **--example**: Show a minimal example of usage
- **--ip**="": 1.1.1.1
## completion
Generate shell completions
- **--example**: Show a minimal example of usage
- **--file, -f**="": Write to file (default: brook_autocomplete)
## mdpage
Generate markdown page
- **--example**: Show a minimal example of usage
- **--file, -f**="": Write to file, default print to stdout
- **--help, -h**: show help
@@ -812,6 +920,8 @@ Shows a list of commands or help for one command
Generate man.1 page
- **--example**: Show a minimal example of usage
- **--file, -f**="": Write to file, default print to stdout. You should put to /path/to/man/man1/brook.1 on linux or /usr/local/share/man/man1/brook.1 on macos
## help, h
@@ -1094,43 +1204,3 @@ brook pac --file proxy.pac --proxy 'SOCKS5 127.0.0.1:1080; SOCKS 127.0.0.1:1080;
```
## There are countless examples; for more feature suggestions, it's best to look at the commands and parameters in the CLI documentation one by one, and blog, YouTube...
# Diagram
> Maybe outdated
## overview
![overview](https://txthinking.github.io/brook/svg/overview.svg)
## withoutBrookProtocol
![wbp](https://txthinking.github.io/brook/svg/wbp.svg)
## relayoverbrook
![relayoverbrook](https://txthinking.github.io/brook/svg/relayoverbrook.svg)
## dnsserveroverbrook
![dnsserveroverbrook](https://txthinking.github.io/brook/svg/dnsserveroverbrook.svg)
## relay
![relay](https://txthinking.github.io/brook/svg/relay.svg)
## dnsserver
![dnsserver](https://txthinking.github.io/brook/svg/dnsserver.svg)
## tproxy
![tproxy](https://txthinking.github.io/brook/svg/tproxy.svg)
## gui
![gui](https://txthinking.github.io/brook/svg/gui.svg)
## script
![script](https://txthinking.github.io/brook/svg/script.svg)
+11 -23
View File
@@ -158,7 +158,7 @@ func main() {
},
&cli.StringFlag{
Name: "pid",
Usage: "A file path used to store pid",
Usage: "A file path used to store pid. Send SIGUSR1 to me to reset the --serverLog file on unix system",
},
}
app.Before = func(c *cli.Context) error {
@@ -1192,16 +1192,6 @@ func main() {
}
},
Flags: []cli.Flag{
&cli.StringFlag{
Name: "quicserver",
Aliases: []string{"s"},
Usage: "Brook quicserver address, like: quic://google.com:443. Do not omit the port under any circumstances",
},
&cli.StringFlag{
Name: "password",
Aliases: []string{"p"},
Usage: "Brook quicserver password",
},
&cli.StringFlag{
Name: "link",
Usage: "brook link, you can get it via $ brook link. The wssserver and password parameters will be ignored",
@@ -1236,21 +1226,13 @@ func main() {
},
Action: func(c *cli.Context) error {
if c.Bool("example") {
fmt.Println("brook quicclient --quicserver quic://domain.com:9999 --password hello --socks5 127.0.0.1:1080")
fmt.Println("brook quicclient --link 'brook://...' --socks5 127.0.0.1:1080")
return nil
}
if c.String("quicserver") == "" && c.String("link") == "" {
if c.String("link") == "" {
return cli.ShowSubcommandHelp(c)
}
var link = ""
if c.String("quicserver") != "" {
v := url.Values{}
v.Set("password", c.String("password"))
link = brook.Link("quicserver", c.String("quicserver"), v)
}
if c.String("link") != "" {
link = c.String("link")
}
link := c.String("link")
h, p, err := net.SplitHostPort(c.String("socks5"))
if err != nil {
return err
@@ -1376,6 +1358,9 @@ func main() {
if strings.HasPrefix(c.String("server"), "quic://") {
kind = "quicserver"
}
if kind == "quicserver" {
return errors.New("It is recommended to use brook link and specify --udpoverstream")
}
v := url.Values{}
v.Set("password", c.String("password"))
link = brook.Link(kind, c.String("server"), v)
@@ -1514,6 +1499,9 @@ func main() {
if strings.HasPrefix(c.String("server"), "quic://") {
kind = "quicserver"
}
if kind == "quicserver" {
return errors.New("It is recommended to use brook link and specify --udpoverstream")
}
v := url.Values{}
v.Set("password", c.String("password"))
link = brook.Link(kind, c.String("server"), v)
@@ -1585,7 +1573,7 @@ func main() {
},
&cli.BoolFlag{
Name: "udpoverstream",
Usage: "When server is brook quicserver, UDP over Stream. Note: only brook CLI and tun2brook suppport for now",
Usage: "When server is brook quicserver, UDP over Stream. Under normal circumstances, you need this parameter because the max datagram size for QUIC is very small. Note: only brook CLI and tun2brook suppport for now",
},
&cli.StringFlag{
Name: "address",
+1 -5
View File
@@ -21,8 +21,4 @@ brook server -l :9999 -p hello
| [![](https://brook.app/images/appstore.png)](https://apps.apple.com/us/app/brook-network-tool/id1216002642) | [![](https://brook.app/images/android.png)](https://github.com/txthinking/brook/releases/latest/download/Brook.apk) | [![](https://brook.app/images/mac.png)](https://apps.apple.com/us/app/brook-network-tool/id1216002642) | [![Windows](https://brook.app/images/windows.png)](https://github.com/txthinking/brook/releases/latest/download/Brook.msix) | [![](https://brook.app/images/linux.png)](https://github.com/txthinking/brook/releases/latest/download/Brook.bin) | [![OpenWrt](https://brook.app/images/openwrt.png)](https://github.com/txthinking/brook/releases) |
| / | / | [App Mode](https://www.txthinking.com/talks/articles/macos-app-mode-en.article) | [How](https://www.txthinking.com/talks/articles/msix-brook-en.article) | [How](https://www.txthinking.com/talks/articles/linux-app-brook-en.article) | [How](https://www.txthinking.com/talks/articles/brook-openwrt-en.article) |
## CLI Client
```
brook client -s 1.2.3.4:9999 -p hello --socks5 127.0.0.1:1080
```
> You may want to use `brook link` to customize some parameters
+54 -7
View File
@@ -8,11 +8,12 @@
## Programmable
```
Brook GUI will pass different global variables to the script at different times, and the script only needs to assign the processing result to the global variable out
```
Brook GUI will pass different _global variables_ to the script at different times, and the script only needs to assign the processing result to the global variable `out`
### Introduction to incoming variables
- address: We call it address which includes both host and port. For example, an ip address contains an ip and a port; a domain address contains a domain and a port.
- Fake DNS: Fake DNS can allow you to obtain domain address on `in_address` step. [How Fake DNS works](https://www.txthinking.com/talks/articles/brook-fakedns-en.article)
### Variables
| variable | type | condition | timing | description | out type |
| ------------------------------ | ---- | ----------- | --------------------------------- | ------------------------------------------------- | -------- |
@@ -104,7 +105,53 @@ Brook GUI will pass different global variables to the script at different times,
`out`, must be set to a response
## Write script
## Module
There are already some modules: https://github.com/txthinking/brook/blob/master/programmable/modules/
### Brook GUI
In Brook GUI, scripts are abstracted into modules, and it will automatically combine [_header.tengo](https://github.com/txthinking/brook/blob/master/programmable/modules/_header.tengo) and [_footer.tengo](https://github.com/txthinking/brook/blob/master/programmable/modules/_footer.tengo), so you only need to write the module itself.
```
modules = append(modules, {
// If you want to predefine multiple brook links, and then programmatically specify which one to connect to, then define `brooklinks` key a function
brooklinks: func(m) {
// Please refer to the example in `brooklinks.tengo`
},
// If you want to intercept and handle a DNS query, then define `dnsquery` key a function, `m` is the `in_dnsquery`
dnsquery: func(m) {
// Please refer to the example in `block_aaaa.tengo`
},
// If you want to intercept and handle an address, then define `address` key a function, `m` is the `in_address`
address: func(m) {
// Please refer to the example in `block_google_secure_dns.tengo`
},
// If you want to intercept and handle a http request, then define `httprequest` key a function, `request` is the `in_httprequest`
httprequest: func(request) {
// Please refer to the example in `ios_app_downgrade.tengo` or `redirect_google_cn.tengo`
},
// If you want to intercept and handle a http response, then define `httpresponse` key a function, `request` is the `in_httprequest`, `response` is the `in_httpresponse`
httpresponse: func(request, response) {
// Please refer to the example in `response_sample.tengo`
}
})
```
### tun2brook
If you are using tun2brook, you can combine multiple modules into a complete script in the following way. For example:
```
cat _header.tengo > my.tengo
cat block_google_secure_dns.tengo >> my.tengo
cat block_aaaa.tengo >> my.tengo
cat _footer.tengo >> my.tengo
```
## Syntax
[Tengo Language Syntax](https://github.com/d5/tengo/blob/master/docs/tutorial.md)
@@ -151,9 +198,9 @@ Library
* hexencode(s string) => string/error: returns the hexadecimal encoding of src
```
## Debug script
## Debug
It is recommended to use [tun2brook](https://github.com/txthinking/tun2brook) on desktop to debug with `fmt.println`
If you are writing complex scripts, the GUI may not be convenient for debugging. It is recommended to use [tun2brook](https://github.com/txthinking/tun2brook) on desktop to debug with `fmt.println`
## Install CA
+204 -135
View File
@@ -1158,13 +1158,12 @@
<li><a href="#getting-started">Getting Started</a><ul>
<li><a href="#server">Server</a></li>
<li><a href="#gui-client">GUI Client</a></li>
<li><a href="#cli-client">CLI Client</a></li>
</ul>
</li>
<li><a href="#gui-documentation">GUI Documentation</a><ul>
<li><a href="#software-for-which-this-article-applies">Software for which this article applies</a></li>
<li><a href="#programmable">Programmable</a><ul>
<li><a href="#introduction-to-incoming-variables">Introduction to incoming variables</a></li>
<li><a href="#variables">Variables</a></li>
<li><a href="#in_brooklinks">in_brooklinks</a></li>
<li><a href="#in_dnsquery">in_dnsquery</a></li>
<li><a href="#in_address">in_address</a></li>
@@ -1172,8 +1171,13 @@
<li><a href="#in_httpresponse">in_httpresponse</a></li>
</ul>
</li>
<li><a href="#write-script">Write script</a></li>
<li><a href="#debug-script">Debug script</a></li>
<li><a href="#module">Module</a><ul>
<li><a href="#brook-gui">Brook GUI</a></li>
<li><a href="#tun2brook">tun2brook</a></li>
</ul>
</li>
<li><a href="#syntax">Syntax</a></li>
<li><a href="#debug">Debug</a></li>
<li><a href="#install-ca">Install CA</a></li>
</ul>
</li>
@@ -1243,18 +1247,6 @@
<li><a href="#there-are-countless-examples-for-more-feature-suggestions-its-best-to-look-at-the-commands-and-parameters-in-the-cli-documentation-one-by-one-and-blog-youtube">There are countless examples; for more feature suggestions, it&#39;s best to look at the commands and parameters in the CLI documentation one by one, and blog, YouTube...</a></li>
</ul>
</li>
<li><a href="#diagram">Diagram</a><ul>
<li><a href="#overview">overview</a></li>
<li><a href="#withoutbrookprotocol-1">withoutBrookProtocol</a></li>
<li><a href="#relayoverbrook-1">relayoverbrook</a></li>
<li><a href="#dnsserveroverbrook-1">dnsserveroverbrook</a></li>
<li><a href="#relay-1">relay</a></li>
<li><a href="#dnsserver-1">dnsserver</a></li>
<li><a href="#tproxy">tproxy</a></li>
<li><a href="#gui">gui</a></li>
<li><a href="#script">script</a></li>
</ul>
</li>
</ul>
</div>
@@ -1268,13 +1260,12 @@
<li><a href="#getting-started">Getting Started</a><ul>
<li><a href="#server">Server</a></li>
<li><a href="#gui-client">GUI Client</a></li>
<li><a href="#cli-client">CLI Client</a></li>
</ul>
</li>
<li><a href="#gui-documentation">GUI Documentation</a><ul>
<li><a href="#software-for-which-this-article-applies">Software for which this article applies</a></li>
<li><a href="#programmable">Programmable</a><ul>
<li><a href="#introduction-to-incoming-variables">Introduction to incoming variables</a></li>
<li><a href="#variables">Variables</a></li>
<li><a href="#in_brooklinks">in_brooklinks</a></li>
<li><a href="#in_dnsquery">in_dnsquery</a></li>
<li><a href="#in_address">in_address</a></li>
@@ -1282,8 +1273,13 @@
<li><a href="#in_httpresponse">in_httpresponse</a></li>
</ul>
</li>
<li><a href="#write-script">Write script</a></li>
<li><a href="#debug-script">Debug script</a></li>
<li><a href="#module">Module</a><ul>
<li><a href="#brook-gui">Brook GUI</a></li>
<li><a href="#tun2brook">tun2brook</a></li>
</ul>
</li>
<li><a href="#syntax">Syntax</a></li>
<li><a href="#debug">Debug</a></li>
<li><a href="#install-ca">Install CA</a></li>
</ul>
</li>
@@ -1353,18 +1349,6 @@
<li><a href="#there-are-countless-examples-for-more-feature-suggestions-its-best-to-look-at-the-commands-and-parameters-in-the-cli-documentation-one-by-one-and-blog-youtube">There are countless examples; for more feature suggestions, it&#39;s best to look at the commands and parameters in the CLI documentation one by one, and blog, YouTube...</a></li>
</ul>
</li>
<li><a href="#diagram">Diagram</a><ul>
<li><a href="#overview">overview</a></li>
<li><a href="#withoutbrookprotocol-1">withoutBrookProtocol</a></li>
<li><a href="#relayoverbrook-1">relayoverbrook</a></li>
<li><a href="#dnsserveroverbrook-1">dnsserveroverbrook</a></li>
<li><a href="#relay-1">relay</a></li>
<li><a href="#dnsserver-1">dnsserver</a></li>
<li><a href="#tproxy">tproxy</a></li>
<li><a href="#gui">gui</a></li>
<li><a href="#script">script</a></li>
</ul>
</li>
</ul>
</div>
@@ -1372,7 +1356,7 @@
<!--G-R3M673HK5V-->
<p>A cross-platform programmable network tool.</p>
<h1 id="sponsor">Sponsor</h1>
<p><strong>❤️ <a href="https://www.txthinking.com/shiliew.html">Shiliew - China Optimized Network App</a></strong></p>
<p><strong>❤️ <a href="https://www.txthinking.com/shiliew.html">Shiliew - A network app designed for those who value their time</a></strong></p>
<h1 id="getting-started">Getting Started</h1>
<h2 id="server">Server</h2>
<pre><code>bash &lt;(curl https://bash.ooo/nami.sh)
@@ -1410,9 +1394,9 @@
<td><a href="https://www.txthinking.com/talks/articles/brook-openwrt-en.article">How</a></td>
</tr>
</tbody></table>
<h2 id="cli-client">CLI Client</h2>
<pre><code>brook client -s 1.2.3.4:9999 -p hello --socks5 127.0.0.1:1080
</code></pre>
<blockquote>
<p>You may want to use <code>brook link</code> to customize some parameters</p>
</blockquote>
<h1 id="gui-documentation">GUI Documentation</h1>
<h2 id="software-for-which-this-article-applies">Software for which this article applies</h2>
<ul>
@@ -1421,9 +1405,12 @@
<li><a href="https://github.com/txthinking/tun2brook">tun2brook</a></li>
</ul>
<h2 id="programmable">Programmable</h2>
<pre><code>Brook GUI will pass different global variables to the script at different times, and the script only needs to assign the processing result to the global variable out
</code></pre>
<h3 id="introduction-to-incoming-variables">Introduction to incoming variables</h3>
<p>Brook GUI will pass different <em>global variables</em> to the script at different times, and the script only needs to assign the processing result to the global variable <code>out</code></p>
<ul>
<li>address: We call it address which includes both host and port. For example, an ip address contains an ip and a port; a domain address contains a domain and a port.</li>
<li>Fake DNS: Fake DNS can allow you to obtain domain address on <code>in_address</code> step. <a href="https://www.txthinking.com/talks/articles/brook-fakedns-en.article">How Fake DNS works</a></li>
</ul>
<h3 id="variables">Variables</h3>
<table>
<thead>
<tr>
@@ -1794,7 +1781,43 @@
</tr>
</tbody></table>
<p><code>out</code>, must be set to a response</p>
<h2 id="write-script">Write script</h2>
<h2 id="module">Module</h2>
<p>There are already some modules: <a href="https://github.com/txthinking/brook/blob/master/programmable/modules/">https://github.com/txthinking/brook/blob/master/programmable/modules/</a></p>
<h3 id="brook-gui">Brook GUI</h3>
<p>In Brook GUI, scripts are abstracted into modules, and it will automatically combine <a href="https://github.com/txthinking/brook/blob/master/programmable/modules/_header.tengo">_header.tengo</a> and <a href="https://github.com/txthinking/brook/blob/master/programmable/modules/_footer.tengo">_footer.tengo</a>, so you only need to write the module itself.</p>
<pre><code>modules = append(modules, {
// If you want to predefine multiple brook links, and then programmatically specify which one to connect to, then define `brooklinks` key a function
brooklinks: func(m) {
// Please refer to the example in `brooklinks.tengo`
},
// If you want to intercept and handle a DNS query, then define `dnsquery` key a function, `m` is the `in_dnsquery`
dnsquery: func(m) {
// Please refer to the example in `block_aaaa.tengo`
},
// If you want to intercept and handle an address, then define `address` key a function, `m` is the `in_address`
address: func(m) {
// Please refer to the example in `block_google_secure_dns.tengo`
},
// If you want to intercept and handle a http request, then define `httprequest` key a function, `request` is the `in_httprequest`
httprequest: func(request) {
// Please refer to the example in `ios_app_downgrade.tengo` or `redirect_google_cn.tengo`
},
// If you want to intercept and handle a http response, then define `httpresponse` key a function, `request` is the `in_httprequest`, `response` is the `in_httpresponse`
httpresponse: func(request, response) {
// Please refer to the example in `response_sample.tengo`
}
})
</code></pre>
<h3 id="tun2brook">tun2brook</h3>
<p>If you are using tun2brook, you can combine multiple modules into a complete script in the following way. For example:</p>
<pre><code>cat _header.tengo &gt; my.tengo
cat block_google_secure_dns.tengo &gt;&gt; my.tengo
cat block_aaaa.tengo &gt;&gt; my.tengo
cat _footer.tengo &gt;&gt; my.tengo
</code></pre>
<h2 id="syntax">Syntax</h2>
<p><a href="https://github.com/d5/tengo/blob/master/docs/tutorial.md">Tengo Language Syntax</a></p>
<p>Library</p>
<ul>
@@ -1847,8 +1870,8 @@ Functions
</code></pre>
</li>
</ul>
<h2 id="debug-script">Debug script</h2>
<p>It is recommended to use <a href="https://github.com/txthinking/tun2brook">tun2brook</a> on desktop to debug with <code>fmt.println</code></p>
<h2 id="debug">Debug</h2>
<p>If you are writing complex scripts, the GUI may not be convenient for debugging. It is recommended to use <a href="https://github.com/txthinking/tun2brook">tun2brook</a> on desktop to debug with <code>fmt.println</code></p>
<h2 id="install-ca">Install CA</h2>
<p><a href="https://txthinking.github.io/ca/ca.pem">https://txthinking.github.io/ca/ca.pem</a></p>
<table>
@@ -1983,12 +2006,8 @@ Functions
<td>A SSH Terminal</td>
</tr>
<tr>
<td><a href="https://www.txthinking.com/deploy.html">Brook Deploy</a></td>
<td>Deploy brook with GUI</td>
</tr>
<tr>
<td><a href="https://github.com/txthinking/brook-manager">brook-manager</a></td>
<td>Brook Manager is a Brook management system for medium to large merchants</td>
<td><a href="https://github.com/txthinkinginc/brook-user-system">brook-user-system</a></td>
<td>A Brook User System</td>
</tr>
<tr>
<td><a href="https://www.txthinking.com">TxThinking</a></td>
@@ -2007,6 +2026,16 @@ Functions
</code></pre>
<h1 id="global-options">GLOBAL OPTIONS</h1>
<ul>
<li><p><strong>--blockCIDR4List</strong>=&quot;&quot;: One CIDR per line, https://, http:// or local file absolute path, like: <a href="https://raw.githubusercontent.com/txthinking/brook/master/programmable/list/example_cidr4.txt">https://raw.githubusercontent.com/txthinking/brook/master/programmable/list/example_cidr4.txt</a>. Works with server/wsserver/wssserver/quicserver</p>
</li>
<li><p><strong>--blockCIDR6List</strong>=&quot;&quot;: One CIDR per line, https://, http:// or local file absolute path, like: <a href="https://raw.githubusercontent.com/txthinking/brook/master/programmable/list/example_cidr6.txt">https://raw.githubusercontent.com/txthinking/brook/master/programmable/list/example_cidr6.txt</a>. Works with server/wsserver/wssserver/quicserver</p>
</li>
<li><p><strong>--blockDomainList</strong>=&quot;&quot;: One domain per line, suffix match mode. https://, http:// or local file absolute path. Like: <a href="https://raw.githubusercontent.com/txthinking/brook/master/programmable/list/example_domain.txt">https://raw.githubusercontent.com/txthinking/brook/master/programmable/list/example_domain.txt</a>. Works with server/wsserver/wssserver/quicserver</p>
</li>
<li><p><strong>--blockGeoIP</strong>=&quot;&quot;: Block IP by Geo country code, such as US. Works with server/wsserver/wssserver/quicserver</p>
</li>
<li><p><strong>--blockListUpdateInterval</strong>=&quot;&quot;: Update list --blockDomainList,--blockCIDR4List,--blockCIDR6List interval, second. default 0, only read one time on start (default: 0)</p>
</li>
<li><p><strong>--clientHKDFInfo</strong>=&quot;&quot;: client HKDF info, most time you don&#39;t need to change this, if changed, all and each brook links in client side must be same, I mean each (default: &quot;brook&quot;)</p>
</li>
<li><p><strong>--dialWithDNS</strong>=&quot;&quot;: When a domain name needs to be resolved, use the specified DNS. Such as 8.8.8.8:53 or <a href="https://dns.google/dns-query?address=8.8.8.8%3A443">https://dns.google/dns-query?address=8.8.8.8%3A443</a>, the address is required. Note that for client-side commands, this does not affect the client passing the domain address to the server</p>
@@ -2037,7 +2066,9 @@ Functions
</li>
<li><p><strong>--ipLimitWait</strong>=&quot;&quot;: How long (s) to wait for recovery after exceeding ipLimitMax (default: 0)</p>
</li>
<li><p><strong>--log</strong>=&quot;&quot;: Enable log. A valid value is file path or &#39;console&#39;. If you want to debug SOCKS5 lib, set env SOCKS5_DEBUG=true</p>
<li><p><strong>--log</strong>=&quot;&quot;: Enable log. A valid value is file path or &#39;console&#39;. Send SIGUSR1 to me to reset the log file on unix system. If you want to debug SOCKS5 lib, set env SOCKS5_DEBUG=true</p>
</li>
<li><p><strong>--pid</strong>=&quot;&quot;: A file path used to store pid. Send SIGUSR1 to me to reset the --serverLog file on unix system</p>
</li>
<li><p><strong>--pprof</strong>=&quot;&quot;: go http pprof listen addr, such as :6060</p>
</li>
@@ -2047,26 +2078,34 @@ Functions
</li>
<li><p><strong>--serverHKDFInfo</strong>=&quot;&quot;: server HKDF info, most time you don&#39;t need to change this, if changed, all and each brook links in client side must be same, I mean each (default: &quot;brook&quot;)</p>
</li>
<li><p><strong>--serverLog</strong>=&quot;&quot;: Enable server log, traffic and more. A valid value is file path or &#39;console&#39;. Mutually exclusive with the --log parameter. Works with server/wsserver/wssserver/quicserver</p>
<li><p><strong>--serverLog</strong>=&quot;&quot;: Enable server log, traffic and more. A valid value is file path or &#39;console&#39;. Send SIGUSR1 to me to reset the log file on unix system. Mutually exclusive with the --log parameter. Works with server/wsserver/wssserver/quicserver with brook protocol</p>
</li>
<li><p><strong>--speedLimit</strong>=&quot;&quot;: Limit speed (b), such as 500kb/s: 500000, works with server/wsserver/wssserver/quicserver (default: 0)</p>
<li><p><strong>--speedLimit</strong>=&quot;&quot;: Limit speed (b), 500kb/s such as: 500000, works with server/wsserver/wssserver/quicserver (default: 0)</p>
</li>
<li><p><strong>--tag</strong>=&quot;&quot;: Tag can be used to the process, will be append into log or serverLog, such as: &#39;key1:value1&#39;</p>
<li><p><strong>--tag</strong>=&quot;&quot;: Tag can be used to the process, will be append into log or serverLog, such as: &#39;key1:value1&#39;. All tags will also be appended as query parameters one by one to the userAPI</p>
</li>
<li><p><strong>--userAPI</strong>=&quot;&quot;: When you build your own user system, Brook Server will send GET request to your userAPI to check if token is valid, for example: <a href="https://your-api-server.com/a_unpredictable_path">https://your-api-server.com/a_unpredictable_path</a>. Yes, it is recommended to add an unpredictable path to your https API, of course, you can also use the http api for internal network communication. The request format is <a href="https://your-api-server.com/a_unpredictable_path?token=xxx">https://your-api-server.com/a_unpredictable_path?token=xxx</a>. When the response is 200, the body should be the user&#39;s unique identifier, such as user ID; all other status codes are considered to represent an illegitimate user, and in these cases, the body should be a string describing the error. It should be used with --serverLog and server/wsserver/wssserver/quicserver with brook protocol. For more information, please read <a href="https://github.com/txthinking/brook/blob/master/protocol/user.md">https://github.com/txthinking/brook/blob/master/protocol/user.md</a></p>
</li>
<li><p><strong>--userAPIInvalidCacheTime</strong>=&quot;&quot;: Once a token is checked and invalid, the userAPI will not be requested to validate again for a certain period (s). A reasonable value must be set, otherwise it will affect the performance of each incoming connection. Note that this may affect the user experience, when you change the user status from invalid to valid in your user system (default: 1800)</p>
</li>
<li><p><strong>--userAPIValidCacheTime</strong>=&quot;&quot;: Once a token is checked and valid, the userAPI will not be requested to validate again for a certain period (s). A reasonable value must be set, otherwise it will affect the performance of each incoming connection (default: 3600)</p>
</li>
<li><p><strong>--version, -v</strong>: print the version</p>
</li>
</ul>
<h1 id="commands">COMMANDS</h1>
<h2 id="server-1">server</h2>
<p>Run as brook server, both TCP and UDP</p>
<p>Start a brook server that supports tcp and udp</p>
<ul>
<li><p><strong>--blockCIDR4List</strong>=&quot;&quot;: One CIDR per line, https://, http:// or local file absolute path, like: <a href="https://raw.githubusercontent.com/txthinking/brook/master/programmable/list/example_cidr4.txt">https://raw.githubusercontent.com/txthinking/brook/master/programmable/list/example_cidr4.txt</a></p>
<li><p><strong>--blockCIDR4List</strong>=&quot;&quot;: This option will be removed in a future version, please use the global option instead</p>
</li>
<li><p><strong>--blockCIDR6List</strong>=&quot;&quot;: One CIDR per line, https://, http:// or local file absolute path, like: <a href="https://raw.githubusercontent.com/txthinking/brook/master/programmable/list/example_cidr6.txt">https://raw.githubusercontent.com/txthinking/brook/master/programmable/list/example_cidr6.txt</a></p>
<li><p><strong>--blockCIDR6List</strong>=&quot;&quot;: This option will be removed in a future version, please use the global option instead</p>
</li>
<li><p><strong>--blockDomainList</strong>=&quot;&quot;: One domain per line, suffix match mode. https://, http:// or local file absolute path. Like: <a href="https://raw.githubusercontent.com/txthinking/brook/master/programmable/list/example_domain.txt">https://raw.githubusercontent.com/txthinking/brook/master/programmable/list/example_domain.txt</a></p>
<li><p><strong>--blockDomainList</strong>=&quot;&quot;: This option will be removed in a future version, please use the global option instead</p>
</li>
<li><p><strong>--blockGeoIP</strong>=&quot;&quot;: Block IP by Geo country code, such as US</p>
<li><p><strong>--blockGeoIP</strong>=&quot;&quot;: This option will be removed in a future version, please use the global option instead</p>
</li>
<li><p><strong>--example</strong>: Show a minimal example of usage</p>
</li>
<li><p><strong>--listen, -l</strong>=&quot;&quot;: Listen address, like: &#39;:9999&#39;</p>
</li>
@@ -2076,14 +2115,18 @@ Functions
</li>
<li><p><strong>--udpTimeout</strong>=&quot;&quot;: time (s) (default: 0)</p>
</li>
<li><p><strong>--updateListInterval</strong>=&quot;&quot;: Update list interval, second. default 0, only read one time on start (default: 0)</p>
<li><p><strong>--updateListInterval</strong>=&quot;&quot;: This option will be removed in a future version, please use the global option instead (default: 0)</p>
</li>
</ul>
<h2 id="client">client</h2>
<p>Run as brook client, both TCP and UDP, to start a socks5 proxy, [src &lt;-&gt; socks5 &lt;-&gt; $ brook client &lt;-&gt; $ brook server &lt;-&gt; dst]</p>
<p>Start a brook client that supports tcp and udp. It can open a socks5 proxy, [src &lt;-&gt; socks5 &lt;-&gt; $ brook client &lt;-&gt; $ brook server &lt;-&gt; dst]</p>
<ul>
<li><p><strong>--example</strong>: Show a minimal example of usage</p>
</li>
<li><p><strong>--http</strong>=&quot;&quot;: Where to listen for HTTP proxy connections</p>
</li>
<li><p><strong>--link</strong>=&quot;&quot;: brook link, you can get it via $ brook link. The wssserver and password parameters will be ignored</p>
</li>
<li><p><strong>--password, -p</strong>=&quot;&quot;: Brook server password</p>
</li>
<li><p><strong>--server, -s</strong>=&quot;&quot;: Brook server address, like: 1.2.3.4:9999</p>
@@ -2096,19 +2139,19 @@ Functions
</li>
<li><p><strong>--udpTimeout</strong>=&quot;&quot;: time (s) (default: 0)</p>
</li>
<li><p><strong>--udpovertcp</strong>: UDP over TCP</p>
</li>
</ul>
<h2 id="wsserver">wsserver</h2>
<p>Run as brook wsserver, both TCP and UDP, it will start a standard http server and websocket server</p>
<p>Start a brook wsserver that supports tcp and udp. It opens a standard http server and a websocket server</p>
<ul>
<li><p><strong>--blockCIDR4List</strong>=&quot;&quot;: One CIDR per line, https://, http:// or local file absolute path, like: <a href="https://raw.githubusercontent.com/txthinking/brook/master/programmable/list/example_cidr4.txt">https://raw.githubusercontent.com/txthinking/brook/master/programmable/list/example_cidr4.txt</a></p>
<li><p><strong>--blockCIDR4List</strong>=&quot;&quot;: This option will be removed in a future version, please use the global option instead</p>
</li>
<li><p><strong>--blockCIDR6List</strong>=&quot;&quot;: One CIDR per line, https://, http:// or local file absolute path, like: <a href="https://raw.githubusercontent.com/txthinking/brook/master/programmable/list/example_cidr6.txt">https://raw.githubusercontent.com/txthinking/brook/master/programmable/list/example_cidr6.txt</a></p>
<li><p><strong>--blockCIDR6List</strong>=&quot;&quot;: This option will be removed in a future version, please use the global option instead</p>
</li>
<li><p><strong>--blockDomainList</strong>=&quot;&quot;: One domain per line, suffix match mode. https://, http:// or local file absolute path. Like: <a href="https://raw.githubusercontent.com/txthinking/brook/master/programmable/list/example_domain.txt">https://raw.githubusercontent.com/txthinking/brook/master/programmable/list/example_domain.txt</a></p>
<li><p><strong>--blockDomainList</strong>=&quot;&quot;: This option will be removed in a future version, please use the global option instead</p>
</li>
<li><p><strong>--blockGeoIP</strong>=&quot;&quot;: Block IP by Geo country code, such as US</p>
<li><p><strong>--blockGeoIP</strong>=&quot;&quot;: This option will be removed in a future version, please use the global option instead</p>
</li>
<li><p><strong>--example</strong>: Show a minimal example of usage</p>
</li>
<li><p><strong>--listen, -l</strong>=&quot;&quot;: Listen address, like: &#39;:80&#39;</p>
</li>
@@ -2120,7 +2163,7 @@ Functions
</li>
<li><p><strong>--udpTimeout</strong>=&quot;&quot;: time (s) (default: 0)</p>
</li>
<li><p><strong>--updateListInterval</strong>=&quot;&quot;: Update list interval, second. default 0, only read one time on start (default: 0)</p>
<li><p><strong>--updateListInterval</strong>=&quot;&quot;: This option will be removed in a future version, please use the global option instead (default: 0)</p>
</li>
<li><p><strong>--withoutBrookProtocol</strong>: The data will not be encrypted with brook protocol</p>
</li>
@@ -2128,12 +2171,14 @@ Functions
</li>
</ul>
<h2 id="wsclient">wsclient</h2>
<p>Run as brook wsclient, both TCP and UDP, to start a socks5 proxy, [src &lt;-&gt; socks5 &lt;-&gt; $ brook wsclient &lt;-&gt; $ brook wsserver &lt;-&gt; dst]</p>
<p>Start a brook wsclient that supports tcp and udp. It can open a socks5 proxy, [src &lt;-&gt; socks5 &lt;-&gt; $ brook wsclient &lt;-&gt; $ brook wsserver &lt;-&gt; dst]</p>
<ul>
<li><p><strong>--address</strong>=&quot;&quot;: Specify address instead of resolving addresses from host, such as 1.2.3.4:443</p>
<li><p><strong>--example</strong>: Show a minimal example of usage</p>
</li>
<li><p><strong>--http</strong>=&quot;&quot;: Where to listen for HTTP proxy connections</p>
</li>
<li><p><strong>--link</strong>=&quot;&quot;: brook link, you can get it via $ brook link. The wssserver and password parameters will be ignored</p>
</li>
<li><p><strong>--password, -p</strong>=&quot;&quot;: Brook wsserver password</p>
</li>
<li><p><strong>--socks5</strong>=&quot;&quot;: Where to listen for SOCKS5 connections (default: 127.0.0.1:1080)</p>
@@ -2144,21 +2189,19 @@ Functions
</li>
<li><p><strong>--udpTimeout</strong>=&quot;&quot;: time (s) (default: 0)</p>
</li>
<li><p><strong>--withoutBrookProtocol</strong>: The data will not be encrypted with brook protocol</p>
</li>
<li><p><strong>--wsserver, -s</strong>=&quot;&quot;: Brook wsserver address, like: ws://1.2.3.4:80, if no path then /ws will be used. Do not omit the port under any circumstances</p>
</li>
</ul>
<h2 id="wssserver">wssserver</h2>
<p>Run as brook wssserver, both TCP and UDP, it will start a standard https server and websocket server</p>
<p>Start a brook wssserver that supports tcp and udp. It opens a standard https server and a websocket server</p>
<ul>
<li><p><strong>--blockCIDR4List</strong>=&quot;&quot;: One CIDR per line, https://, http:// or local file absolute path, like: <a href="https://raw.githubusercontent.com/txthinking/brook/master/programmable/list/example_cidr4.txt">https://raw.githubusercontent.com/txthinking/brook/master/programmable/list/example_cidr4.txt</a></p>
<li><p><strong>--blockCIDR4List</strong>=&quot;&quot;: This option will be removed in a future version, please use the global option instead</p>
</li>
<li><p><strong>--blockCIDR6List</strong>=&quot;&quot;: One CIDR per line, https://, http:// or local file absolute path, like: <a href="https://raw.githubusercontent.com/txthinking/brook/master/programmable/list/example_cidr6.txt">https://raw.githubusercontent.com/txthinking/brook/master/programmable/list/example_cidr6.txt</a></p>
<li><p><strong>--blockCIDR6List</strong>=&quot;&quot;: This option will be removed in a future version, please use the global option instead</p>
</li>
<li><p><strong>--blockDomainList</strong>=&quot;&quot;: One domain per line, suffix match mode. https://, http:// or local file absolute path. Like: <a href="https://raw.githubusercontent.com/txthinking/brook/master/programmable/list/example_domain.txt">https://raw.githubusercontent.com/txthinking/brook/master/programmable/list/example_domain.txt</a></p>
<li><p><strong>--blockDomainList</strong>=&quot;&quot;: This option will be removed in a future version, please use the global option instead</p>
</li>
<li><p><strong>--blockGeoIP</strong>=&quot;&quot;: Block IP by Geo country code, such as US</p>
<li><p><strong>--blockGeoIP</strong>=&quot;&quot;: This option will be removed in a future version, please use the global option instead</p>
</li>
<li><p><strong>--cert</strong>=&quot;&quot;: The cert file absolute path for the domain, such as /path/to/cert.pem. If cert or certkey is empty, a certificate will be issued automatically</p>
</li>
@@ -2166,6 +2209,8 @@ Functions
</li>
<li><p><strong>--domainaddress</strong>=&quot;&quot;: Such as: domain.com:443. If you choose to automatically issue certificates, the domain must have been resolved to the server IP and 80 port also will be used</p>
</li>
<li><p><strong>--example</strong>: Show a minimal example of usage</p>
</li>
<li><p><strong>--password, -p</strong>=&quot;&quot;: Server password</p>
</li>
<li><p><strong>--path</strong>=&quot;&quot;: URL path (default: /ws)</p>
@@ -2174,14 +2219,16 @@ Functions
</li>
<li><p><strong>--udpTimeout</strong>=&quot;&quot;: time (s) (default: 0)</p>
</li>
<li><p><strong>--updateListInterval</strong>=&quot;&quot;: Update list interval, second. default 0, only read one time on start (default: 0)</p>
<li><p><strong>--updateListInterval</strong>=&quot;&quot;: This option will be removed in a future version, please use the global option instead (default: 0)</p>
</li>
<li><p><strong>--withoutBrookProtocol</strong>: The data will not be encrypted with brook protocol</p>
</li>
</ul>
<h2 id="wssclient">wssclient</h2>
<p>Run as brook wssclient, both TCP and UDP, to start a socks5 proxy, [src &lt;-&gt; socks5 &lt;-&gt; $ brook wssclient &lt;-&gt; $ brook wssserver &lt;-&gt; dst]</p>
<p>Start a brook wssclient that supports tcp and udp. It can open a socks5 proxy, [src &lt;-&gt; socks5 &lt;-&gt; $ brook wssclient &lt;-&gt; $ brook wssserver &lt;-&gt; dst]</p>
<ul>
<li><p><strong>--example</strong>: Show a minimal example of usage</p>
</li>
<li><p><strong>--http</strong>=&quot;&quot;: Where to listen for HTTP proxy connections</p>
</li>
<li><p><strong>--link</strong>=&quot;&quot;: brook link, you can get it via $ brook link. The wssserver and password parameters will be ignored</p>
@@ -2200,15 +2247,15 @@ Functions
</li>
</ul>
<h2 id="quicserver">quicserver</h2>
<p>Run as brook quicserver, both TCP and UDP</p>
<p>Start a brook quicserver that supports tcp and udp.</p>
<ul>
<li><p><strong>--blockCIDR4List</strong>=&quot;&quot;: One CIDR per line, https://, http:// or local file absolute path, like: <a href="https://raw.githubusercontent.com/txthinking/brook/master/programmable/list/example_cidr4.txt">https://raw.githubusercontent.com/txthinking/brook/master/programmable/list/example_cidr4.txt</a></p>
<li><p><strong>--blockCIDR4List</strong>=&quot;&quot;: This option will be removed in a future version, please use the global option instead</p>
</li>
<li><p><strong>--blockCIDR6List</strong>=&quot;&quot;: One CIDR per line, https://, http:// or local file absolute path, like: <a href="https://raw.githubusercontent.com/txthinking/brook/master/programmable/list/example_cidr6.txt">https://raw.githubusercontent.com/txthinking/brook/master/programmable/list/example_cidr6.txt</a></p>
<li><p><strong>--blockCIDR6List</strong>=&quot;&quot;: This option will be removed in a future version, please use the global option instead</p>
</li>
<li><p><strong>--blockDomainList</strong>=&quot;&quot;: One domain per line, suffix match mode. https://, http:// or local file absolute path. Like: <a href="https://raw.githubusercontent.com/txthinking/brook/master/programmable/list/example_domain.txt">https://raw.githubusercontent.com/txthinking/brook/master/programmable/list/example_domain.txt</a></p>
<li><p><strong>--blockDomainList</strong>=&quot;&quot;: This option will be removed in a future version, please use the global option instead</p>
</li>
<li><p><strong>--blockGeoIP</strong>=&quot;&quot;: Block IP by Geo country code, such as US</p>
<li><p><strong>--blockGeoIP</strong>=&quot;&quot;: This option will be removed in a future version, please use the global option instead</p>
</li>
<li><p><strong>--cert</strong>=&quot;&quot;: The cert file absolute path for the domain, such as /path/to/cert.pem. If cert or certkey is empty, a certificate will be issued automatically</p>
</li>
@@ -2216,31 +2263,27 @@ Functions
</li>
<li><p><strong>--domainaddress</strong>=&quot;&quot;: Such as: domain.com:443. If you choose to automatically issue certificates, the domain must have been resolved to the server IP and 80 port also will be used</p>
</li>
<li><p><strong>--example</strong>: Show a minimal example of usage</p>
</li>
<li><p><strong>--password, -p</strong>=&quot;&quot;: Server password</p>
</li>
<li><p><strong>--tcpTimeout</strong>=&quot;&quot;: time (s) (default: 0)</p>
</li>
<li><p><strong>--udpTimeout</strong>=&quot;&quot;: time (s) (default: 0)</p>
</li>
<li><p><strong>--updateListInterval</strong>=&quot;&quot;: Update list interval, second. default 0, only read one time on start (default: 0)</p>
<li><p><strong>--updateListInterval</strong>=&quot;&quot;: This option will be removed in a future version, please use the global option instead (default: 0)</p>
</li>
<li><p><strong>--withoutBrookProtocol</strong>: The data will not be encrypted with brook protocol</p>
</li>
</ul>
<h2 id="quicclient">quicclient</h2>
<p>Run as brook quicclient, both TCP and UDP, to start a socks5 proxy, [src &lt;-&gt; socks5 &lt;-&gt; $ brook quicclient &lt;-&gt; $ brook quicserver &lt;-&gt; dst]. (Note that the global dial parameter is ignored now)</p>
<p>Start a brook quicclient that supports tcp and udp. It can open a socks5 proxy, [src &lt;-&gt; socks5 &lt;-&gt; $ brook quicclient &lt;-&gt; $ brook quicserver &lt;-&gt; dst]. (The global-dial-parameter is ignored)</p>
<ul>
<li><p><strong>--address</strong>=&quot;&quot;: Specify address instead of resolving addresses from host, such as 1.2.3.4:443</p>
</li>
<li><p><strong>--ca</strong>=&quot;&quot;: Specify ca instead of insecure, such as /path/to/ca.pem</p>
<li><p><strong>--example</strong>: Show a minimal example of usage</p>
</li>
<li><p><strong>--http</strong>=&quot;&quot;: Where to listen for HTTP proxy connections</p>
</li>
<li><p><strong>--insecure</strong>: Client do not verify the server&#39;s certificate chain and host name</p>
</li>
<li><p><strong>--password, -p</strong>=&quot;&quot;: Brook quicserver password</p>
</li>
<li><p><strong>--quicserver, -s</strong>=&quot;&quot;: Brook quicserver address, like: quic://google.com:443. Do not omit the port under any circumstances</p>
<li><p><strong>--link</strong>=&quot;&quot;: brook link, you can get it via $ brook link. The wssserver and password parameters will be ignored</p>
</li>
<li><p><strong>--socks5</strong>=&quot;&quot;: Where to listen for SOCKS5 connections (default: 127.0.0.1:1080)</p>
</li>
@@ -2250,12 +2293,12 @@ Functions
</li>
<li><p><strong>--udpTimeout</strong>=&quot;&quot;: time (s) (default: 0)</p>
</li>
<li><p><strong>--withoutBrookProtocol</strong>: The data will not be encrypted with brook protocol</p>
</li>
</ul>
<h2 id="relayoverbrook">relayoverbrook</h2>
<p>Run as relay over brook, both TCP and UDP, this means access [from address] is equal to [to address], [src &lt;-&gt; from address &lt;-&gt; $ brook server/wsserver/wssserver/quicserver &lt;-&gt; to address]</p>
<p>Relay network traffic over brook, which supports TCP and UDP. Accessing [from address] is equal to accessing [to address], [src &lt;-&gt; from address &lt;-&gt; $ brook server/wsserver/wssserver/quicserver &lt;-&gt; to address]</p>
<ul>
<li><p><strong>--example</strong>: Show a minimal example of usage</p>
</li>
<li><p><strong>--from, -f, -l</strong>=&quot;&quot;: Listen address: like &#39;:9999&#39;</p>
</li>
<li><p><strong>--link</strong>=&quot;&quot;: brook link, you can get it via $ brook link. The server and password parameters will be ignored</p>
@@ -2272,7 +2315,7 @@ Functions
</li>
</ul>
<h2 id="dnsserveroverbrook">dnsserveroverbrook</h2>
<p>Run as dns server over brook, both TCP and UDP, [src &lt;-&gt; $ brook dnserversoverbrook &lt;-&gt; $ brook server/wsserver/wssserver/quicserver &lt;-&gt; dns] or [src &lt;-&gt; $ brook dnsserveroverbrook &lt;-&gt; dnsForBypass]</p>
<p>Run a dns server over brook, which supports TCP and UDP, [src &lt;-&gt; $ brook dnserversoverbrook &lt;-&gt; $ brook server/wsserver/wssserver/quicserver &lt;-&gt; dns] or [src &lt;-&gt; $ brook dnsserveroverbrook &lt;-&gt; dnsForBypass]</p>
<ul>
<li><p><strong>--blockDomainList</strong>=&quot;&quot;: One domain per line, suffix match mode. https://, http:// or local absolute file path. Like: <a href="https://raw.githubusercontent.com/txthinking/brook/master/programmable/list/example_domain.txt">https://raw.githubusercontent.com/txthinking/brook/master/programmable/list/example_domain.txt</a></p>
</li>
@@ -2286,6 +2329,8 @@ Functions
</li>
<li><p><strong>--dnsForBypass</strong>=&quot;&quot;: DNS server for resolving domains in bypass list. Such as 223.5.5.5:53 or <a href="https://dns.alidns.com/dns-query?address=223.5.5.5:443">https://dns.alidns.com/dns-query?address=223.5.5.5:443</a>, the address is required (default: 223.5.5.5:53)</p>
</li>
<li><p><strong>--example</strong>: Show a minimal example of usage</p>
</li>
<li><p><strong>--link</strong>=&quot;&quot;: brook link, you can get it via $ brook link. The server and password parameters will be ignored</p>
</li>
<li><p><strong>--listen, -l</strong>=&quot;&quot;: Listen address, like: 127.0.0.1:53</p>
@@ -2300,7 +2345,7 @@ Functions
</li>
</ul>
<h2 id="link">link</h2>
<p>Generate brook link</p>
<p>Generate a brook link</p>
<ul>
<li><p><strong>--address</strong>=&quot;&quot;: When server is brook wsserver or brook wssserver or brook quicserver, specify address instead of resolving addresses from host, such as 1.2.3.4:443</p>
</li>
@@ -2308,7 +2353,9 @@ Functions
</li>
<li><p><strong>--clientHKDFInfo</strong>=&quot;&quot;: client HKDF info, most time you don&#39;t need to change this, read brook protocol if you don&#39;t know what this is</p>
</li>
<li><p><strong>--fragment</strong>=&quot;&quot;: When server is brook wssserver, split the ClientHello into multiple fragments and then send them one by one with delays (millisecond). The format is min_length:max_length:min_delay:max_delay, cannot be zero, such as 50:100:10:50, Note that: This is an experimental feature, currently only supported by the brook CLI and tun2brook.</p>
<li><p><strong>--example</strong>: Show a minimal example of usage</p>
</li>
<li><p><strong>--fragment</strong>=&quot;&quot;: When server is brook wssserver, split the ClientHello into multiple fragments and then send them one by one with delays (millisecond). The format is min_length:max_length:min_delay:max_delay, cannot be zero, such as 50:100:10:50</p>
</li>
<li><p><strong>--insecure</strong>: When server is brook wssserver or brook quicserver, client do not verify the server&#39;s certificate chain and host name</p>
</li>
@@ -2322,6 +2369,10 @@ Functions
</li>
<li><p><strong>--tlsfingerprint</strong>=&quot;&quot;: When server is brook wssserver, select tls fingerprint, value can be: chrome</p>
</li>
<li><p><strong>--token</strong>=&quot;&quot;: A token represents a user&#39;s identity. A string encoded in hexadecimal. Server needs to have --userAPI enabled. Note that: Only supported by the brook GUI(except for OpenWrt) and tun2brook</p>
</li>
<li><p><strong>--udpoverstream</strong>: When server is brook quicserver, UDP over Stream. Under normal circumstances, you need this parameter because the max datagram size for QUIC is very small. Note: only brook CLI and tun2brook suppport for now</p>
</li>
<li><p><strong>--udpovertcp</strong>: When server is brook server, UDP over TCP</p>
</li>
<li><p><strong>--username, -u</strong>=&quot;&quot;: Username, when server is socks5 server</p>
@@ -2330,8 +2381,10 @@ Functions
</li>
</ul>
<h2 id="connect">connect</h2>
<p>Run as client and connect to brook link, both TCP and UDP, to start a socks5 proxy, [src &lt;-&gt; socks5 &lt;-&gt; $ brook connect &lt;-&gt; $ brook server/wsserver/wssserver/quicserver &lt;-&gt; dst]</p>
<p>Run a client and connect with a brook link, which supports TCP and UDP. It can start a socks5 proxy, [src &lt;-&gt; socks5 &lt;-&gt; $ brook connect &lt;-&gt; $ brook server/wsserver/wssserver/quicserver &lt;-&gt; dst]</p>
<ul>
<li><p><strong>--example</strong>: Show a minimal example of usage</p>
</li>
<li><p><strong>--http</strong>=&quot;&quot;: Where to listen for HTTP proxy connections</p>
</li>
<li><p><strong>--link, -l</strong>=&quot;&quot;: brook link, you can get it via $ brook link</p>
@@ -2346,8 +2399,10 @@ Functions
</li>
</ul>
<h2 id="relay">relay</h2>
<p>Run as standalone relay, both TCP and UDP, this means access [from address] is equal to access [to address], [src &lt;-&gt; from address &lt;-&gt; to address]</p>
<p>Run a standalone relay, which supports TCP and UDP. Accessing [from address] is equal to accessing [to address], [src &lt;-&gt; from address &lt;-&gt; to address]</p>
<ul>
<li><p><strong>--example</strong>: Show a minimal example of usage</p>
</li>
<li><p><strong>--from, -f, -l</strong>=&quot;&quot;: Listen address: like &#39;:9999&#39;</p>
</li>
<li><p><strong>--tcpTimeout</strong>=&quot;&quot;: time (s) (default: 0)</p>
@@ -2358,7 +2413,7 @@ Functions
</li>
</ul>
<h2 id="dnsserver">dnsserver</h2>
<p>Run as standalone dns server</p>
<p>Run a standalone dns server</p>
<ul>
<li><p><strong>--blockDomainList</strong>=&quot;&quot;: One domain per line, suffix match mode. https://, http:// or local absolute file path. Like: <a href="https://raw.githubusercontent.com/txthinking/brook/master/programmable/list/example_domain.txt">https://raw.githubusercontent.com/txthinking/brook/master/programmable/list/example_domain.txt</a></p>
</li>
@@ -2368,6 +2423,8 @@ Functions
</li>
<li><p><strong>--dns</strong>=&quot;&quot;: DNS server which forward to. Such as 8.8.8.8:53 or <a href="https://dns.google/dns-query?address=8.8.8.8%3A443">https://dns.google/dns-query?address=8.8.8.8%3A443</a>, the address is required (default: 8.8.8.8:53)</p>
</li>
<li><p><strong>--example</strong>: Show a minimal example of usage</p>
</li>
<li><p><strong>--listen, -l</strong>=&quot;&quot;: Listen address, like: 127.0.0.1:53</p>
</li>
<li><p><strong>--tcpTimeout</strong>=&quot;&quot;: time (s) (default: 0)</p>
@@ -2382,13 +2439,15 @@ Functions
</li>
<li><p><strong>--domain, -d</strong>=&quot;&quot;: Domain</p>
</li>
<li><p><strong>--example</strong>: Show a minimal example of usage</p>
</li>
<li><p><strong>--short</strong>: Short for A/AAAA</p>
</li>
<li><p><strong>--type, -t</strong>=&quot;&quot;: Type, such as A (default: A)</p>
</li>
</ul>
<h2 id="dohserver">dohserver</h2>
<p>Run as standalone doh server</p>
<p>Run a standalone doh server</p>
<ul>
<li><p><strong>--blockDomainList</strong>=&quot;&quot;: One domain per line, suffix match mode. https://, http:// or local absolute file path. Like: <a href="https://raw.githubusercontent.com/txthinking/brook/master/programmable/list/example_domain.txt">https://raw.githubusercontent.com/txthinking/brook/master/programmable/list/example_domain.txt</a></p>
</li>
@@ -2404,6 +2463,8 @@ Functions
</li>
<li><p><strong>--domainaddress</strong>=&quot;&quot;: Such as: domain.com:443, if you want to create a https server. If you choose to automatically issue certificates, the domain must have been resolved to the server IP and 80 port also will be used</p>
</li>
<li><p><strong>--example</strong>: Show a minimal example of usage</p>
</li>
<li><p><strong>--listen</strong>=&quot;&quot;: listen address, if you want to create a http server behind nico</p>
</li>
<li><p><strong>--path</strong>=&quot;&quot;: URL path (default: /dns-query)</p>
@@ -2420,13 +2481,15 @@ Functions
</li>
<li><p><strong>--domain, -d</strong>=&quot;&quot;: Domain</p>
</li>
<li><p><strong>--example</strong>: Show a minimal example of usage</p>
</li>
<li><p><strong>--short</strong>: Short for A/AAAA</p>
</li>
<li><p><strong>--type, -t</strong>=&quot;&quot;: Type, such as A (default: A)</p>
</li>
</ul>
<h2 id="dhcpserver">dhcpserver</h2>
<p>Run as standalone dhcp server. Note that you need to stop other dhcp servers, if there are.</p>
<p>Run a standalone dhcp server. Other running dhcp servers need to be stopped.</p>
<ul>
<li><p><strong>--cache</strong>=&quot;&quot;: Cache file, local absolute file path, default is $HOME/.brook.dhcpserver</p>
</li>
@@ -2434,6 +2497,8 @@ Functions
</li>
<li><p><strong>--dnsserver</strong>=&quot;&quot;: The dns server which you want to assign to clients, such as: 192.168.1.1 or 8.8.8.8</p>
</li>
<li><p><strong>--example</strong>: Show a minimal example of usage</p>
</li>
<li><p><strong>--gateway</strong>=&quot;&quot;: The router gateway which you want to assign to clients, such as: 192.168.1.1</p>
</li>
<li><p><strong>--interface</strong>=&quot;&quot;: Select interface on multi interface device. Linux only</p>
@@ -2446,8 +2511,10 @@ Functions
</li>
</ul>
<h2 id="socks5">socks5</h2>
<p>Run as standalone standard socks5 server, both TCP and UDP</p>
<p>Run a standalone standard socks5 server, which supports TCP and UDP</p>
<ul>
<li><p><strong>--example</strong>: Show a minimal example of usage</p>
</li>
<li><p><strong>--limitUDP</strong>: The server MAY use this information to limit access to the UDP association. This usually causes connection failures in a NAT environment, where most clients are.</p>
</li>
<li><p><strong>--listen, -l</strong>=&quot;&quot;: Socks5 server listen address, like: :1080 or 1.2.3.4:1080</p>
@@ -2464,8 +2531,10 @@ Functions
</li>
</ul>
<h2 id="socks5tohttp">socks5tohttp</h2>
<p>Convert socks5 to http proxy, [src &lt;-&gt; listen address(http proxy) &lt;-&gt; socks5 address &lt;-&gt; dst]</p>
<p>Convert a socks5 proxy to a http proxy, [src &lt;-&gt; listen address(http proxy) &lt;-&gt; socks5 address &lt;-&gt; dst]</p>
<ul>
<li><p><strong>--example</strong>: Show a minimal example of usage</p>
</li>
<li><p><strong>--listen, -l</strong>=&quot;&quot;: HTTP proxy which will be create: like: 127.0.0.1:8010</p>
</li>
<li><p><strong>--socks5, -s</strong>=&quot;&quot;: Socks5 server address, like: 127.0.0.1:1080</p>
@@ -2478,10 +2547,12 @@ Functions
</li>
</ul>
<h2 id="pac">pac</h2>
<p>Run as PAC server or save PAC to file</p>
<p>Run a PAC server or save PAC to a file</p>
<ul>
<li><p><strong>--bypassDomainList, -b</strong>=&quot;&quot;: One domain per line, suffix match mode. http(s):// or local absolute file path. Like: <a href="https://raw.githubusercontent.com/txthinking/brook/master/programmable/list/example_domain.txt">https://raw.githubusercontent.com/txthinking/brook/master/programmable/list/example_domain.txt</a></p>
</li>
<li><p><strong>--example</strong>: Show a minimal example of usage</p>
</li>
<li><p><strong>--file, -f</strong>=&quot;&quot;: Save PAC to file, this will ignore listen address</p>
</li>
<li><p><strong>--listen, -l</strong>=&quot;&quot;: Listen address, like: 127.0.0.1:1980</p>
@@ -2490,12 +2561,14 @@ Functions
</li>
</ul>
<h2 id="testsocks5">testsocks5</h2>
<p>Test UDP and TCP of socks5 server</p>
<p>Test a socks5 server to see if it works properly</p>
<ul>
<li><p><strong>--dns</strong>=&quot;&quot;: DNS server for connecting (default: 8.8.8.8:53)</p>
</li>
<li><p><strong>--domain</strong>=&quot;&quot;: Domain for query (default: http3.ooo)</p>
</li>
<li><p><strong>--example</strong>: Show a minimal example of usage</p>
</li>
<li><p><strong>--password, -p</strong>=&quot;&quot;: Socks5 password</p>
</li>
<li><p><strong>--socks5, -s</strong>=&quot;&quot;: Like: 127.0.0.1:1080</p>
@@ -2506,12 +2579,14 @@ Functions
</li>
</ul>
<h2 id="testbrook">testbrook</h2>
<p>Test UDP and TCP of brook server/wsserver/wssserver/quicserver. (Note that the global dial parameter is ignored now)</p>
<p>Test UDP and TCP of a brook server/wsserver/wssserver/quicserver connection. (The global-dial-parameter is ignored)</p>
<ul>
<li><p><strong>--dns</strong>=&quot;&quot;: DNS server for connecting (default: 8.8.8.8:53)</p>
</li>
<li><p><strong>--domain</strong>=&quot;&quot;: Domain for query (default: http3.ooo)</p>
</li>
<li><p><strong>--example</strong>: Show a minimal example of usage</p>
</li>
<li><p><strong>--link, -l</strong>=&quot;&quot;: brook link. Get it via $ brook link</p>
</li>
<li><p><strong>--socks5</strong>=&quot;&quot;: Temporarily listening socks5 (default: 127.0.0.1:11080)</p>
@@ -2522,11 +2597,16 @@ Functions
<h2 id="echoserver">echoserver</h2>
<p>Echo server, echo UDP and TCP address of routes</p>
<ul>
<li><strong>--listen, -l</strong>=&quot;&quot;: Listen address, like: &#39;:7777&#39;</li>
<li><p><strong>--example</strong>: Show a minimal example of usage</p>
</li>
<li><p><strong>--listen, -l</strong>=&quot;&quot;: Listen address, like: &#39;:7777&#39;</p>
</li>
</ul>
<h2 id="echoclient">echoclient</h2>
<p>Connect to echoserver, echo UDP and TCP address of routes</p>
<ul>
<li><p><strong>--example</strong>: Show a minimal example of usage</p>
</li>
<li><p><strong>--server, -s</strong>=&quot;&quot;: Echo server address, such as 1.2.3.4:7777</p>
</li>
<li><p><strong>--times</strong>=&quot;&quot;: Times of interactions (default: 0)</p>
@@ -2535,16 +2615,24 @@ Functions
<h2 id="ipcountry">ipcountry</h2>
<p>Get country of IP</p>
<ul>
<li><strong>--ip</strong>=&quot;&quot;: 1.1.1.1</li>
<li><p><strong>--example</strong>: Show a minimal example of usage</p>
</li>
<li><p><strong>--ip</strong>=&quot;&quot;: 1.1.1.1</p>
</li>
</ul>
<h2 id="completion">completion</h2>
<p>Generate shell completions</p>
<ul>
<li><strong>--file, -f</strong>=&quot;&quot;: Write to file (default: brook_autocomplete)</li>
<li><p><strong>--example</strong>: Show a minimal example of usage</p>
</li>
<li><p><strong>--file, -f</strong>=&quot;&quot;: Write to file (default: brook_autocomplete)</p>
</li>
</ul>
<h2 id="mdpage">mdpage</h2>
<p>Generate markdown page</p>
<ul>
<li><p><strong>--example</strong>: Show a minimal example of usage</p>
</li>
<li><p><strong>--file, -f</strong>=&quot;&quot;: Write to file, default print to stdout</p>
</li>
<li><p><strong>--help, -h</strong>: show help</p>
@@ -2555,7 +2643,10 @@ Functions
<h2 id="manpage">manpage</h2>
<p>Generate man.1 page</p>
<ul>
<li><strong>--file, -f</strong>=&quot;&quot;: Write to file, default print to stdout. You should put to /path/to/man/man1/brook.1 on linux or /usr/local/share/man/man1/brook.1 on macos</li>
<li><p><strong>--example</strong>: Show a minimal example of usage</p>
</li>
<li><p><strong>--file, -f</strong>=&quot;&quot;: Write to file, default print to stdout. You should put to /path/to/man/man1/brook.1 on linux or /usr/local/share/man/man1/brook.1 on macos</p>
</li>
</ul>
<h2 id="help-h-1">help, h</h2>
<p>Shows a list of commands or help for one command</p>
@@ -2721,28 +2812,6 @@ Functions
<pre><code>brook pac --file proxy.pac --proxy &#39;SOCKS5 127.0.0.1:1080; SOCKS 127.0.0.1:1080; DIRECT&#39; --bypassDomainList ...
</code></pre>
<h2 id="there-are-countless-examples-for-more-feature-suggestions-its-best-to-look-at-the-commands-and-parameters-in-the-cli-documentation-one-by-one-and-blog-youtube">There are countless examples; for more feature suggestions, it&#39;s best to look at the commands and parameters in the CLI documentation one by one, and blog, YouTube...</h2>
<h1 id="diagram">Diagram</h1>
<blockquote>
<p>Maybe outdated</p>
</blockquote>
<h2 id="overview">overview</h2>
<p><img src="https://txthinking.github.io/brook/svg/overview.svg" alt="overview"></p>
<h2 id="withoutbrookprotocol-1">withoutBrookProtocol</h2>
<p><img src="https://txthinking.github.io/brook/svg/wbp.svg" alt="wbp"></p>
<h2 id="relayoverbrook-1">relayoverbrook</h2>
<p><img src="https://txthinking.github.io/brook/svg/relayoverbrook.svg" alt="relayoverbrook"></p>
<h2 id="dnsserveroverbrook-1">dnsserveroverbrook</h2>
<p><img src="https://txthinking.github.io/brook/svg/dnsserveroverbrook.svg" alt="dnsserveroverbrook"></p>
<h2 id="relay-1">relay</h2>
<p><img src="https://txthinking.github.io/brook/svg/relay.svg" alt="relay"></p>
<h2 id="dnsserver-1">dnsserver</h2>
<p><img src="https://txthinking.github.io/brook/svg/dnsserver.svg" alt="dnsserver"></p>
<h2 id="tproxy">tproxy</h2>
<p><img src="https://txthinking.github.io/brook/svg/tproxy.svg" alt="tproxy"></p>
<h2 id="gui">gui</h2>
<p><img src="https://txthinking.github.io/brook/svg/gui.svg" alt="gui"></p>
<h2 id="script">script</h2>
<p><img src="https://txthinking.github.io/brook/svg/script.svg" alt="script"></p>
</div>
</div>
+1 -1
View File
@@ -27,6 +27,6 @@
| [Socks5 Configurator](https://chromewebstore.google.com/detail/socks5-configurator/hnpgnjkeaobghpjjhaiemlahikgmnghb) | If you prefer CLI brook client |
| [IPvBar](https://chromewebstore.google.com/detail/ipvbar/nepjlegfiihpkcdhlmaebfdfppckonlj) | See domain, IP and country in browser |
| [TxThinking SSH](https://www.txthinking.com/ssh.html) | A SSH Terminal |
| [brook-dashboard](https://github.com/txthinkinginc/brook-dashboard) | A Brook User System |
| [brook-user-system](https://github.com/txthinkinginc/brook-user-system) | A Brook User System |
| [TxThinking](https://www.txthinking.com) | Everything |
+1 -1
View File
@@ -1,5 +1,5 @@
{
"version": "20240404",
"version": "20240606",
"text": "Clean the web with Brook",
"link": "https://www.txthinking.com/talks/articles/brook-clean-the-web-en.article",
"text_zh": "使用 Brook 净化互联网",
-199
View File
@@ -1,199 +0,0 @@
text := import("text")
brook := import("brook")
json := import("json")
dnsquery_handler := func(m){
// local dev example
if m.domain == "myapi.local" {
if m.type == "A" {
return {ip: "10.211.1.76"}
}
if m.type == "AAAA" {
return {"block": true}
}
}
// block secure dns
if m.domain == "dns.google" {
return {"block": true}
}
// block ipv6
if m.type == "AAAA" {
return {"block": true}
}
// do not use fake dns, instagram 90% ?
l := [
"facebook.com",
"fbcdn.net",
"facebook.net",
"akamaihd.net",
"thefacebook.com",
"tfbnw.net",
"messenger.com",
"fb.me",
"fbsbx.com",
"fb.com",
"whatsapp.net",
"whatsapp.com",
"instagram.com",
"akamai.net",
"aaplimg.com",
"alibabadns.com",
"akamaiedge.net",
"apple-dns.net",
"akadns.net",
"cdninstagram.com"
]
for v in l {
if text.has_suffix(m.domain, v) {
return {"system": true}
}
}
// use bypass dns to resolve ip, apple push
l = [
"apple.com",
"icloud.com",
"cdn-apple.com",
"mzstatic.com",
"entrust.net",
"digicert.com",
"verisign.net",
"apple",
"itunes-apple.com.akadns.net",
"cdn-apple.com.akadns.net",
"ks-cdn.com",
"ksyuncdn.com",
"cdn-apple.com.edgekey.net",
"e2885.e9.akamaiedge.net",
"apple.com.edgekey.net",
"e2490.dscb.akamaiedge.net",
"idms-apple.com.akadns.net",
"apple.com.edgekey.net.globalredir.akadns.net",
"e6858.dscx.akamaiedge.net",
"ioshost.qtlcdn.com"
]
for v in l {
if text.has_suffix(m.domain, v) {
return {"bypass": true}
}
}
}
address_handler := func(m) {
if m.ipaddress {
// block secure dns
if m.ipaddress == "8.8.8.8:853" || m.ipaddress == "8.8.8.8:443" || m.ipaddress == "8.8.4.4:853" || m.ipaddress == "8.8.4.4:443" || m.ipaddress == "[2001:4860:4860::8888]:853" || m.ipaddress == "[2001:4860:4860::8888]:443" || m.ipaddress == "[2001:4860:4860::8844]:853" || m.ipaddress == "[2001:4860:4860::8844]:443" {
return { "block": true }
}
// extract ip
r := brook.splithostport(m.ipaddress)
if is_error(r) {
return r
}
// block an ip
if r.host == "1.2.4.8" {
return { "block": true }
}
// bypass zz and cn ip
s := brook.country(r.host)
if s == "ZZ" || s == "CN" {
return { "bypass": true }
}
// bypass apple push
l := [
"17.0.0.0/8",
"103.81.148.0/22",
"103.81.148.0/24",
"103.81.149.0/24",
"2620:149:a44::/48",
"2403:300:a42::/48",
"2403:300:a51::/48",
"2a01:b740:a42::/48"
]
for v in l {
if brook.cidrcontainsip(v, r.host) {
return {"bypass": true}
}
}
}
if m.domainaddress {
// block secure dns
if text.has_prefix(m.domainaddress, "dns.google:") {
return { "block": true }
}
if m.network == "tcp" {
// Packet Capture and Modify
if m.domainaddress == "httpbin.org:80" {
return {"mitm": true, "mitmprotocol": "http"}
}
if m.domainaddress == "httpbin.org:443" {
return {"mitm": true, "mitmprotocol": "https", "mitmwithbody": true, "mitmautohandlecompress": true}
}
// connect this ip and bypass it
if m.domainaddress == "myapi2.local:80" {
return {"ipaddress": "10.211.1.76:8080", "bypass": true, "mitm": true, "mitmprotocol": "http", "mitmwithbody": true, "mitmautohandlecompress": true}
}
// get A via bypass dns, then connect it and bypass it
if m.domainaddress == "myip.ipip.net:443" {
return {"ipaddressfrombypassdns": "A", "bypass": true, "mitm": true, "mitmprotocol": "https", "mitmwithbody": true, "mitmautohandlecompress": true}
}
}
if m.network == "udp" {
// block http3
if m.domainaddress == "httpbin.org:443" {
return { "block": true }
}
}
}
}
httprequest_handler := func(request){
// redirect
if text.has_prefix(request["URL"], "http://httpbin.org") {
response := {
"StatusCode": 301,
"Location": text.replace(request["URL"], "http://", "https://", 1)
}
return response
}
// Packet Modify request header and body
if request["URL"] == "https://httpbin.org/post" && request["Method"] == "POST" && request["Content-Type"] == "application/x-www-form-urlencoded" {
request["User-Agent"] = "curl/7.79.1"
request["Body"] = bytes("hello=world")
return request
}
return request
}
httpresponse_handler := func(request, response){
delete(response, "Alt-Svc") // Avoid upgrading to http3 from http1 or http2
// Packet Modify response body
if text.has_prefix(request["URL"], "https://httpbin.org") && !text.has_prefix(request["URL"], "https://httpbin.org/stream/") && response["Content-Type"] == "application/json" {
j := json.decode(response["Body"])
j.origin = "M.A.R.S"
response["Body"] = json.encode(j)
return response
}
// Packet Modify response body
if text.has_prefix(request["URL"], "https://myip.ipip.net") {
response["Body"] = bytes(text.split(string(response["Body"]), "IP")[0] + "来自: 火星")
return response
}
return response
}
handler := func(){
if in_dnsquery {
return dnsquery_handler(in_dnsquery)
}
if in_address {
return address_handler(in_address)
}
if in_httprequest && !in_httpresponse {
return httprequest_handler(in_httprequest)
}
if in_httprequest && in_httpresponse {
return httpresponse_handler(in_httprequest, in_httpresponse)
}
}
out := handler()
+1 -1
View File
@@ -8,7 +8,7 @@ modules = append(modules, {
return r
}
s := brook.country(r.host)
if s == "ZZ" || s == "CN" {
if s == "ZZ" || s == "CN" { // All private IPs are ZZ
return { bypass: true }
}
}
+33
View File
@@ -1,3 +1,36 @@
## Brook GUI
In Brook GUI, scripts are abstracted into modules, and it will automatically combine `_header.tengo` and `_footer.tengo`, so you only need to write the module itself.
```
modules = append(modules, {
// If you want to predefine multiple brook links, and then programmatically specify which one to connect to, then define `brooklinks` key a function
brooklinks: func(m) {
// Please refer to the example in `brooklinks.tengo`
},
// If you want to intercept and handle a DNS query, then define `dnsquery` key a function, `m` is the `in_dnsquery`
dnsquery: func(m) {
// Please refer to the example in `block_aaaa.tengo`
},
// If you want to intercept and handle an address, then define `address` key a function, `m` is the `in_address`
address: func(m) {
// Please refer to the example in `block_google_secure_dns.tengo`
},
// If you want to intercept and handle a http request, then define `httprequest` key a function, `request` is the `in_httprequest`
httprequest: func(request) {
// Please refer to the example in `ios_app_downgrade.tengo` or `redirect_google_cn.tengo`
},
// If you want to intercept and handle a http response, then define `httpresponse` key a function, `request` is the `in_httprequest`, `response` is the `in_httpresponse`
httpresponse: func(request, response) {
// Please refer to the example in `response_sample.tengo`
}
})
```
## tun2brook
If you are using tun2brook, you can combine multiple modules into a complete script in the following way. For example:
```
cat _header.tengo > my.tengo
+25
View File
@@ -0,0 +1,25 @@
// Unlock Xbox country limit
// [CA]
modules = append(modules, {
address: func(m) {
if m.domainaddress {
if m.domainaddress == "www.xbox.com:443" || m.domainaddress == "xgpuweb.gssv-play-prod.xboxlive.com:443" {
if m.network == "tcp" {
return {"mitm": true, "mitmprotocol": "https"}
}
if m.network == "udp" {
return { "block": true }
}
}
}
},
httprequest: func(request) {
text := import("text")
if text.has_prefix(request["URL"], "https://www.xbox.com") || text.has_prefix(request["URL"], "https://xgpuweb.gssv-play-prod.xboxlive.com") {
request["X-Forwarded-For"] = "4.2.2.2" // Any country IP you want
return request
}
}
})
@@ -98,4 +98,3 @@ API.
This software is released under the GPL-3.0 license.
[![FOSSA Status](https://app.fossa.io/api/projects/git%2Bgithub.com%2FMetaCubeX%2Fmihomo.svg?type=large)](https://app.fossa.io/projects/git%2Bgithub.com%2FMetaCubeX%2Fmihomo?ref=badge_large)
@@ -166,12 +166,6 @@ func (ss *ShadowSocks) ListenPacketContext(ctx context.Context, metadata *C.Meta
// ListenPacketWithDialer implements C.ProxyAdapter
func (ss *ShadowSocks) ListenPacketWithDialer(ctx context.Context, dialer C.Dialer, metadata *C.Metadata) (_ C.PacketConn, err error) {
if len(ss.option.DialerProxy) > 0 {
dialer, err = proxydialer.NewByName(ss.option.DialerProxy, dialer)
if err != nil {
return nil, err
}
}
if ss.option.UDPOverTCP {
tcpConn, err := ss.DialContextWithDialer(ctx, dialer, metadata)
if err != nil {
@@ -179,6 +173,12 @@ func (ss *ShadowSocks) ListenPacketWithDialer(ctx context.Context, dialer C.Dial
}
return ss.ListenPacketOnStreamConn(ctx, tcpConn, metadata)
}
if len(ss.option.DialerProxy) > 0 {
dialer, err = proxydialer.NewByName(ss.option.DialerProxy, dialer)
if err != nil {
return nil, err
}
}
addr, err := resolveUDPAddrWithPrefer(ctx, "udp", ss.addr, ss.prefer)
if err != nil {
return nil, err
@@ -13,7 +13,10 @@ import (
"github.com/zhangyunhao116/fastrand"
)
var DisableSystemHosts, _ = strconv.ParseBool(os.Getenv("DISABLE_SYSTEM_HOSTS"))
var (
DisableSystemHosts, _ = strconv.ParseBool(os.Getenv("DISABLE_SYSTEM_HOSTS"))
UseSystemHosts bool
)
type Hosts struct {
*trie.DomainTrie[HostValue]
@@ -51,7 +54,8 @@ func (h *Hosts) Search(domain string, isDomain bool) (*HostValue, bool) {
return &hostValue, false
}
if !isDomain && !DisableSystemHosts {
if !isDomain && !DisableSystemHosts && UseSystemHosts {
addr, _ := lookupStaticHost(domain)
if hostValue, err := NewHostValue(addr); err == nil {
return &hostValue, true
@@ -114,6 +114,7 @@ type DNS struct {
PreferH3 bool `yaml:"prefer-h3"`
IPv6 bool `yaml:"ipv6"`
IPv6Timeout uint `yaml:"ipv6-timeout"`
UseSystemHosts bool `yaml:"use-system-hosts"`
NameServer []dns.NameServer `yaml:"nameserver"`
Fallback []dns.NameServer `yaml:"fallback"`
FallbackFilter FallbackFilter `yaml:"fallback-filter"`
@@ -209,6 +210,7 @@ type RawDNS struct {
IPv6 bool `yaml:"ipv6" json:"ipv6"`
IPv6Timeout uint `yaml:"ipv6-timeout" json:"ipv6-timeout"`
UseHosts bool `yaml:"use-hosts" json:"use-hosts"`
UseSystemHosts bool `yaml:"use-system-hosts" json:"use-system-hosts"`
NameServer []string `yaml:"nameserver" json:"nameserver"`
Fallback []string `yaml:"fallback" json:"fallback"`
FallbackFilter RawFallbackFilter `yaml:"fallback-filter" json:"fallback-filter"`
@@ -456,12 +458,13 @@ func UnmarshalRawConfig(buf []byte) (*RawConfig, error) {
Interval: 30,
},
DNS: RawDNS{
Enable: false,
IPv6: false,
UseHosts: true,
IPv6Timeout: 100,
EnhancedMode: C.DNSMapping,
FakeIPRange: "198.18.0.1/16",
Enable: false,
IPv6: false,
UseHosts: true,
UseSystemHosts: true,
IPv6Timeout: 100,
EnhancedMode: C.DNSMapping,
FakeIPRange: "198.18.0.1/16",
FallbackFilter: RawFallbackFilter{
GeoIP: true,
GeoIPCode: "CN",
@@ -1285,12 +1288,13 @@ func parseDNS(rawCfg *RawConfig, hosts *trie.DomainTrie[resolver.HostValue], rul
}
dnsCfg := &DNS{
Enable: cfg.Enable,
Listen: cfg.Listen,
PreferH3: cfg.PreferH3,
IPv6Timeout: cfg.IPv6Timeout,
IPv6: cfg.IPv6,
EnhancedMode: cfg.EnhancedMode,
Enable: cfg.Enable,
Listen: cfg.Listen,
PreferH3: cfg.PreferH3,
IPv6Timeout: cfg.IPv6Timeout,
IPv6: cfg.IPv6,
UseSystemHosts: cfg.UseSystemHosts,
EnhancedMode: cfg.EnhancedMode,
FallbackFilter: FallbackFilter{
IPCIDR: []netip.Prefix{},
GeoSite: []router.DomainMatcher{},
@@ -20,7 +20,7 @@ require (
github.com/mdlayher/netlink v1.7.2
github.com/metacubex/gopacket v1.1.20-0.20230608035415-7e2f98a3e759
github.com/metacubex/quic-go v0.43.1-0.20240428051621-a109abfb4cf6
github.com/metacubex/sing-quic v0.0.0-20240429040940-fa3a4ff2533e
github.com/metacubex/sing-quic v0.0.0-20240501013754-2a2b0f262f9f
github.com/metacubex/sing-shadowsocks v0.2.6
github.com/metacubex/sing-shadowsocks2 v0.2.0
github.com/metacubex/sing-tun v0.2.6
@@ -108,8 +108,8 @@ github.com/metacubex/quic-go v0.43.1-0.20240428051621-a109abfb4cf6 h1:qqIZ8CjoxV
github.com/metacubex/quic-go v0.43.1-0.20240428051621-a109abfb4cf6/go.mod h1:uXHODgJFUfUnkkCMWLd5Er6L5QY/LFRZb9LD5jyyhsk=
github.com/metacubex/sing v0.0.0-20240408015159-aa61c96df764 h1:+czGKoynxYA90YaL3NlCAIJHnlqwoUlLWgmOhdm5ZU8=
github.com/metacubex/sing v0.0.0-20240408015159-aa61c96df764/go.mod h1:+60H3Cm91RnL9dpVGWDPHt0zTQImO9Vfqt9a4rSambI=
github.com/metacubex/sing-quic v0.0.0-20240429040940-fa3a4ff2533e h1:UW+yfzoLzjEhAoJ6A4mVnov3Aergi7uFM/h7ReNekKI=
github.com/metacubex/sing-quic v0.0.0-20240429040940-fa3a4ff2533e/go.mod h1:nfqibK+vkBtE6Ch8vYqrFTcaX4BC6TwKqNNl5rORll4=
github.com/metacubex/sing-quic v0.0.0-20240501013754-2a2b0f262f9f h1:Xyd8SHaPHS3iX3xXPnus9PsYhJIo/2e7sJ6vTKLKUm8=
github.com/metacubex/sing-quic v0.0.0-20240501013754-2a2b0f262f9f/go.mod h1:nfqibK+vkBtE6Ch8vYqrFTcaX4BC6TwKqNNl5rORll4=
github.com/metacubex/sing-shadowsocks v0.2.6 h1:6oEB3QcsFYnNiFeoevcXrCwJ3sAablwVSgtE9R3QeFQ=
github.com/metacubex/sing-shadowsocks v0.2.6/go.mod h1:zIkMeSnb8Mbf4hdqhw0pjzkn1d99YJ3JQm/VBg5WMTg=
github.com/metacubex/sing-shadowsocks2 v0.2.0 h1:hqwT/AfI5d5UdPefIzR6onGHJfDXs5zgOM5QSgaM/9A=
@@ -253,6 +253,7 @@ func updateDNS(c *config.DNS, ruleProvider map[string]provider.RuleProvider, gen
resolver.DefaultResolver = r
resolver.DefaultHostMapper = m
resolver.DefaultLocalServer = dns.NewLocalServer(r, m)
resolver.UseSystemHosts = c.UseSystemHosts
if pr.Invalid() {
resolver.ProxyServerHostResolver = pr
@@ -16,7 +16,6 @@ import (
"time"
mihomoHttp "github.com/metacubex/mihomo/component/http"
"github.com/metacubex/mihomo/constant"
C "github.com/metacubex/mihomo/constant"
"github.com/metacubex/mihomo/log"
@@ -52,6 +51,10 @@ func init() {
if runtime.GOARCH == "amd64" && cpuid.CPU.X64Level() < 3 {
amd64Compatible = "-compatible"
}
if !strings.HasPrefix(C.Version, "alpha") {
baseURL = "https://github.com/MetaCubeX/mihomo/releases/latest/download/mihomo"
versionURL = "https://github.com/MetaCubeX/mihomo/releases/latest/download/version.txt"
}
}
type updateError struct {
@@ -73,9 +76,9 @@ func Update(execPath string) (err error) {
return err
}
log.Infoln("current version %s, latest version %s", constant.Version, latestVersion)
log.Infoln("current version %s, latest version %s", C.Version, latestVersion)
if latestVersion == constant.Version {
if latestVersion == C.Version {
err := &updateError{Message: "already using latest version"}
return err
}
@@ -48,7 +48,7 @@ require (
github.com/metacubex/gvisor v0.0.0-20240320004321-933faba989ec // indirect
github.com/metacubex/mihomo v1.7.0 // indirect
github.com/metacubex/quic-go v0.43.1-0.20240428051621-a109abfb4cf6 // indirect
github.com/metacubex/sing-quic v0.0.0-20240429040940-fa3a4ff2533e // indirect
github.com/metacubex/sing-quic v0.0.0-20240501013754-2a2b0f262f9f // indirect
github.com/metacubex/sing-shadowsocks v0.2.6 // indirect
github.com/metacubex/sing-shadowsocks2 v0.2.0 // indirect
github.com/metacubex/sing-tun v0.2.6 // indirect
@@ -102,8 +102,8 @@ github.com/metacubex/quic-go v0.43.1-0.20240428051621-a109abfb4cf6 h1:qqIZ8CjoxV
github.com/metacubex/quic-go v0.43.1-0.20240428051621-a109abfb4cf6/go.mod h1:uXHODgJFUfUnkkCMWLd5Er6L5QY/LFRZb9LD5jyyhsk=
github.com/metacubex/sing v0.0.0-20240111014253-f1818b6a82b2 h1:upEO8dt9WDBavhgcgkXB3hRcwVNbkTbnd+xyzy6ZQZo=
github.com/metacubex/sing v0.0.0-20240111014253-f1818b6a82b2/go.mod h1:9pfuAH6mZfgnz/YjP6xu5sxx882rfyjpcrTdUpd6w3g=
github.com/metacubex/sing-quic v0.0.0-20240429040940-fa3a4ff2533e h1:UW+yfzoLzjEhAoJ6A4mVnov3Aergi7uFM/h7ReNekKI=
github.com/metacubex/sing-quic v0.0.0-20240429040940-fa3a4ff2533e/go.mod h1:nfqibK+vkBtE6Ch8vYqrFTcaX4BC6TwKqNNl5rORll4=
github.com/metacubex/sing-quic v0.0.0-20240501013754-2a2b0f262f9f h1:Xyd8SHaPHS3iX3xXPnus9PsYhJIo/2e7sJ6vTKLKUm8=
github.com/metacubex/sing-quic v0.0.0-20240501013754-2a2b0f262f9f/go.mod h1:nfqibK+vkBtE6Ch8vYqrFTcaX4BC6TwKqNNl5rORll4=
github.com/metacubex/sing-shadowsocks v0.2.6 h1:6oEB3QcsFYnNiFeoevcXrCwJ3sAablwVSgtE9R3QeFQ=
github.com/metacubex/sing-shadowsocks v0.2.6/go.mod h1:zIkMeSnb8Mbf4hdqhw0pjzkn1d99YJ3JQm/VBg5WMTg=
github.com/metacubex/sing-shadowsocks2 v0.2.0 h1:hqwT/AfI5d5UdPefIzR6onGHJfDXs5zgOM5QSgaM/9A=
@@ -55,7 +55,7 @@ require (
github.com/metacubex/gopacket v1.1.20-0.20230608035415-7e2f98a3e759 // indirect
github.com/metacubex/gvisor v0.0.0-20240320004321-933faba989ec // indirect
github.com/metacubex/quic-go v0.43.1-0.20240428051621-a109abfb4cf6 // indirect
github.com/metacubex/sing-quic v0.0.0-20240429040940-fa3a4ff2533e // indirect
github.com/metacubex/sing-quic v0.0.0-20240501013754-2a2b0f262f9f // indirect
github.com/metacubex/sing-shadowsocks v0.2.6 // indirect
github.com/metacubex/sing-shadowsocks2 v0.2.0 // indirect
github.com/metacubex/sing-tun v0.2.6 // indirect
@@ -102,8 +102,8 @@ github.com/metacubex/quic-go v0.43.1-0.20240428051621-a109abfb4cf6 h1:qqIZ8CjoxV
github.com/metacubex/quic-go v0.43.1-0.20240428051621-a109abfb4cf6/go.mod h1:uXHODgJFUfUnkkCMWLd5Er6L5QY/LFRZb9LD5jyyhsk=
github.com/metacubex/sing v0.0.0-20240111014253-f1818b6a82b2 h1:upEO8dt9WDBavhgcgkXB3hRcwVNbkTbnd+xyzy6ZQZo=
github.com/metacubex/sing v0.0.0-20240111014253-f1818b6a82b2/go.mod h1:9pfuAH6mZfgnz/YjP6xu5sxx882rfyjpcrTdUpd6w3g=
github.com/metacubex/sing-quic v0.0.0-20240429040940-fa3a4ff2533e h1:UW+yfzoLzjEhAoJ6A4mVnov3Aergi7uFM/h7ReNekKI=
github.com/metacubex/sing-quic v0.0.0-20240429040940-fa3a4ff2533e/go.mod h1:nfqibK+vkBtE6Ch8vYqrFTcaX4BC6TwKqNNl5rORll4=
github.com/metacubex/sing-quic v0.0.0-20240501013754-2a2b0f262f9f h1:Xyd8SHaPHS3iX3xXPnus9PsYhJIo/2e7sJ6vTKLKUm8=
github.com/metacubex/sing-quic v0.0.0-20240501013754-2a2b0f262f9f/go.mod h1:nfqibK+vkBtE6Ch8vYqrFTcaX4BC6TwKqNNl5rORll4=
github.com/metacubex/sing-shadowsocks v0.2.6 h1:6oEB3QcsFYnNiFeoevcXrCwJ3sAablwVSgtE9R3QeFQ=
github.com/metacubex/sing-shadowsocks v0.2.6/go.mod h1:zIkMeSnb8Mbf4hdqhw0pjzkn1d99YJ3JQm/VBg5WMTg=
github.com/metacubex/sing-shadowsocks2 v0.2.0 h1:hqwT/AfI5d5UdPefIzR6onGHJfDXs5zgOM5QSgaM/9A=
+5 -5
View File
@@ -2486,7 +2486,7 @@ dependencies = [
"httpdate",
"itoa 1.0.11",
"pin-project-lite",
"socket2 0.5.7",
"socket2 0.4.10",
"tokio",
"tower-service",
"tracing",
@@ -5672,9 +5672,9 @@ checksum = "e1fc403891a21bcfb7c37834ba66a547a8f402146eba7265b5a6d88059c9ff2f"
[[package]]
name = "tauri"
version = "1.6.3"
version = "1.6.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7a4fab012dcf1e72762561cef2c08a6538aec2ca44d282158128117f79eb9d39"
checksum = "13ce04f77bcd40bb57ec7061725c9c415d30b2bf80257637b857ee067f2fa198"
dependencies = [
"anyhow",
"base64 0.21.7",
@@ -5830,9 +5830,9 @@ dependencies = [
[[package]]
name = "tauri-runtime-wry"
version = "0.14.6"
version = "0.14.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a777eaa0d88ae47d8081cdc667eba8941b3a18af0f0ccb22640d8f0c3431d6d1"
checksum = "ef2af45aeb15b1cadb4ca91248423f4438a0864b836298cecb436892afbfdff4"
dependencies = [
"cocoa 0.24.1",
"gtk",
+1 -1
View File
@@ -102,7 +102,7 @@
},
"windows": [],
"security": {
"csp": "script-src 'unsafe-eval' 'self'; default-src blob: data: filesystem: ws: wss: http: https: tauri: 'unsafe-eval' 'unsafe-inline' 'self'; img-src data: 'self';"
"csp": "script-src 'unsafe-eval' 'self'; default-src blob: data: filesystem: ws: wss: http: https: tauri: 'unsafe-eval' 'unsafe-inline' 'self'; img-src data: 'self' https:;"
}
}
}
@@ -4,7 +4,7 @@
"main": "index.ts",
"module": "index.ts",
"dependencies": {
"@tauri-apps/api": "1.5.4",
"@tauri-apps/api": "1.5.5",
"ofetch": "1.3.4",
"react": "18.3.1",
"swr": "2.2.5"
@@ -22,12 +22,12 @@
"@mui/x-data-grid": "7.3.2",
"@nyanpasu/interface": "workspace:^",
"@nyanpasu/ui": "workspace:^",
"@tauri-apps/api": "1.5.4",
"@tauri-apps/api": "1.5.5",
"ahooks": "3.7.11",
"axios": "1.6.8",
"dayjs": "1.11.11",
"framer-motion": "11.1.9",
"i18next": "23.11.3",
"i18next": "23.11.4",
"jotai": "2.8.0",
"monaco-editor": "0.48.0",
"mui-color-input": "2.0.3",
@@ -1,79 +0,0 @@
import { useEffect, useMemo } from "react";
import { alpha, Theme } from "@mui/material";
import { appWindow } from "@tauri-apps/api/window";
import { defaultTheme, defaultDarkTheme } from "@/pages/_theme";
import { useNyanpasu } from "@nyanpasu/interface";
import { createMDYTheme } from "@nyanpasu/ui";
import { useAtom } from "jotai";
import { themeMode } from "@/store";
const applyRootStyleVar = (mode: "light" | "dark", theme: Theme) => {
const root = document.documentElement;
root.style.setProperty(
"--background-color",
mode === "light" ? "#ffffff" : "#121212",
);
root.style.setProperty(
"--selection-color",
mode === "light" ? "#f5f5f5" : "#d5d5d5",
);
root.style.setProperty(
"--scroller-color",
mode === "light" ? "#90939980" : "#54545480",
);
root.style.setProperty("--primary-main", theme.palette.primary.main);
root.style.setProperty(
"--background-color-alpha",
alpha(theme.palette.primary.main, 0.1),
);
};
/**
* custom theme
*/
export const useCustomTheme = () => {
const { nyanpasuConfig } = useNyanpasu();
const [mode, setMode] = useAtom(themeMode);
useEffect(() => {
if (nyanpasuConfig?.theme_mode === "system") {
appWindow.theme().then((m) => m && setMode(m));
const unlisten = appWindow.onThemeChanged((e) => setMode(e.payload));
return () => {
unlisten.then((fn) => fn());
};
}
if (nyanpasuConfig?.theme_mode) {
setMode(nyanpasuConfig?.theme_mode);
} else {
setMode("light");
}
}, [nyanpasuConfig?.theme_mode]);
const theme = useMemo(() => {
const dt = mode === "light" ? defaultTheme : defaultDarkTheme;
const theme = createMDYTheme(
{
...dt,
...nyanpasuConfig?.theme_setting,
},
mode,
);
applyRootStyleVar(mode, theme);
return theme;
}, [mode, nyanpasuConfig?.theme_setting]);
return { theme };
};
@@ -0,0 +1,83 @@
import { useEffect, useMemo } from "react";
import { alpha, Theme, useColorScheme } from "@mui/material";
import { appWindow } from "@tauri-apps/api/window";
import { defaultTheme } from "@/pages/_theme";
import { useNyanpasu } from "@nyanpasu/interface";
import { createMDYTheme } from "@nyanpasu/ui";
import { useAtomValue, useSetAtom } from "jotai";
import { themeMode as themeModeAtom } from "@/store";
const applyRootStyleVar = (mode: "light" | "dark", theme: Theme) => {
const root = document.documentElement;
const palette = theme.colorSchemes[mode].palette;
const isLightMode = mode !== "light";
const backgroundColor = isLightMode ? "#121212" : "#ffffff";
const selectionColor = isLightMode ? "#d5d5d5" : "#f5f5f5";
const scrollerColor = isLightMode ? "#54545480" : "#90939980";
root.style.setProperty("--background-color", backgroundColor);
root.style.setProperty("--selection-color", selectionColor);
root.style.setProperty("--scroller-color", scrollerColor);
root.style.setProperty("--primary-main", palette.primary.main);
root.style.setProperty(
"--background-color-alpha",
alpha(palette.primary.main, 0.1),
);
};
/**
* custom theme
*/
export const useCustomTheme = () => {
const { nyanpasuConfig } = useNyanpasu();
const themeMode = useAtomValue(themeModeAtom);
const theme = useMemo(() => {
const mergedTheme = createMDYTheme({
...defaultTheme,
...nyanpasuConfig?.theme_setting,
});
applyRootStyleVar(themeMode, mergedTheme);
return mergedTheme;
}, [nyanpasuConfig?.theme_setting, themeMode]);
return { theme };
};
export const ThemeModeProvider = () => {
const { nyanpasuConfig } = useNyanpasu();
const setThemeMode = useSetAtom(themeModeAtom);
const { setMode } = useColorScheme();
useEffect(() => {
if (nyanpasuConfig?.theme_mode === "system") {
appWindow.theme().then((m) => {
if (m) {
setThemeMode(m);
setMode(m);
}
});
const unlisten = appWindow.onThemeChanged((e) => {
setThemeMode(e.payload);
setMode(e.payload);
});
return () => {
unlisten.then((fn) => fn());
};
}
const chosenThemeMode = nyanpasuConfig?.theme_mode || "light";
setThemeMode(chosenThemeMode);
setMode(chosenThemeMode);
}, [nyanpasuConfig?.theme_mode]);
return null;
};
@@ -1,6 +1,9 @@
import { LayoutControl } from "@/components/layout/layout-control";
import { LayoutTraffic } from "@/components/layout/layout-traffic";
import { useCustomTheme } from "@/components/layout/use-custom-theme";
import {
ThemeModeProvider,
useCustomTheme,
} from "@/components/layout/use-custom-theme";
import { NotificationType, useNotification } from "@/hooks/use-notification";
import { useVerge } from "@/hooks/use-verge";
import { getAxios } from "@/services/api";
@@ -26,6 +29,7 @@ import { classNames } from "@/utils";
import AnimatedLogo from "@/components/layout/animated-logo";
import { FallbackProps } from "react-error-boundary";
import styles from "./_app.module.scss";
import { Experimental_CssVarsProvider as CssVarsProvider } from "@mui/material/styles";
dayjs.extend(relativeTime);
@@ -142,7 +146,8 @@ export default function App() {
return (
<SWRConfig value={{ errorRetryCount: 3 }}>
<ThemeProvider theme={theme}>
<CssVarsProvider theme={theme}>
<ThemeModeProvider />
<Paper
square
elevation={0}
@@ -200,7 +205,7 @@ export default function App() {
</AnimatePresence>
</div>
</Paper>
</ThemeProvider>
</CssVarsProvider>
</SWRConfig>
);
}
@@ -216,7 +221,7 @@ export const Catch = ({ error }: FallbackProps) => {
>
<h1>Oops!</h1>
<p>Something went wrong... Caught at _app error boundary.</p>
<pre>{error.message}</pre>
<pre>{error}</pre>
</div>
);
};
@@ -1,4 +1,4 @@
import createTheme from "@mui/material/styles/createTheme";
import extendTheme from "@mui/material/styles/experimental_extendTheme";
import createPalette from "@mui/material/styles/createPalette";
import {
argbFromHex,
@@ -30,33 +30,41 @@ interface ThemeSchema {
font_family?: string;
}
export const createMDYTheme = (
themeSchema: ThemeSchema,
mode: "light" | "dark",
) => {
export const createMDYTheme = (themeSchema: ThemeSchema) => {
const materialColor = themeFromSourceColor(
argbFromHex(themeSchema.primary_color),
);
const palette = createPalette({
mode,
primary: {
main: hexFromArgb(materialColor.schemes[mode].primary),
},
secondary: {
main: hexFromArgb(materialColor.schemes[mode].secondary),
},
error: {
main: hexFromArgb(materialColor.schemes[mode].error),
},
text: {
primary: hexFromArgb(materialColor.schemes[mode].onPrimaryContainer),
secondary: hexFromArgb(materialColor.schemes[mode].onSecondaryContainer),
},
});
const generatePalette = (mode: "light" | "dark") => {
return createPalette({
mode,
primary: {
main: hexFromArgb(materialColor.schemes[mode].primary),
},
secondary: {
main: hexFromArgb(materialColor.schemes[mode].secondary),
},
error: {
main: hexFromArgb(materialColor.schemes[mode].error),
},
text: {
primary: hexFromArgb(materialColor.schemes[mode].onPrimaryContainer),
secondary: hexFromArgb(
materialColor.schemes[mode].onSecondaryContainer,
),
},
});
};
return createTheme({
palette,
const theme = extendTheme({
colorSchemes: {
light: {
palette: generatePalette("light"),
},
dark: {
palette: generatePalette("dark"),
},
},
typography: {
fontFamily: themeSchema?.font_family,
},
@@ -70,7 +78,7 @@ export const createMDYTheme = (
MuiDialogContent,
MuiDialogTitle,
// MuiPaper,
MuiSwitch: MuiSwitch(palette),
MuiSwitch,
},
breakpoints: {
values: {
@@ -82,4 +90,8 @@ export const createMDYTheme = (
},
},
});
console.log(theme);
return theme;
};
@@ -1,116 +1,123 @@
import { Theme } from "@mui/material";
import { Components } from "@mui/material/styles/components";
import { Palette } from "@mui/material/styles/createPalette";
import type {} from "@mui/material/themeCssVarsAugmentation";
export const MuiSwitch = (palette: Palette): Components<Theme>["MuiSwitch"] => {
const isDark = palette.mode === "dark";
export const MuiSwitch: Components<Theme>["MuiSwitch"] = {
styleOverrides: {
root: ({ theme }) => ({
padding: 0,
margin: 0,
return {
styleOverrides: {
root: {
padding: 0,
margin: 0,
"& .Mui-checked": {
"& .MuiSwitch-thumb": {
color: theme.palette.grey.A100,
},
},
"&:has(.Mui-checked) .MuiSwitch-track::before": {
opacity: 0,
},
"&:has(.Mui-disabled) .MuiSwitch-track": {
opacity: "0.5 !important",
cursor: "not-allowed",
},
}),
track: ({ theme }) => ({
borderRadius: "48px",
backgroundColor: theme.palette.grey.A200,
opacity: `1 !important`,
[theme.getColorSchemeSelector("dark")]: {
backgroundColor: theme.palette.grey.A700,
opacity: `0.7 !important`,
},
"&::before": {
content: '""',
border: `solid 2px ${theme.palette.grey.A700}`,
width: "100%",
height: "100%",
opacity: 1,
position: "absolute",
borderRadius: "inherit",
boxSizing: "border-box",
transitionProperty: "opacity, background-color",
transitionTimingFunction: "linear",
transitionDuration: "100ms",
},
}),
thumb: ({ theme }) => ({
boxShadow: "none",
color: theme.palette.grey.A700,
[theme.getColorSchemeSelector("dark")]: {
backgroundColor: theme.palette.grey.A200,
},
}),
},
variants: [
{
props: {
size: "medium",
},
style: {
height: 32,
"& .MuiSwitch-switchBase": {
padding: "6px",
},
"& .MuiSwitch-thumb": {
width: 14,
height: 14,
margin: 3,
},
"& .Mui-checked": {
"&.MuiSwitch-switchBase": {
marginLeft: "6px",
},
"& .MuiSwitch-thumb": {
color: palette.grey.A100,
width: 24,
height: 24,
margin: -2,
},
},
"&:has(.Mui-checked) .MuiSwitch-track::before": {
opacity: 0,
},
"&:has(.Mui-disabled) .MuiSwitch-track": {
opacity: "0.5 !important",
cursor: "not-allowed",
},
},
track: {
borderRadius: "48px",
backgroundColor: isDark ? palette.grey.A700 : palette.grey.A200,
opacity: `${isDark ? 0.7 : 1} !important`,
"&::before": {
content: '""',
border: `solid 2px ${palette.grey.A700}`,
width: "100%",
height: "100%",
opacity: 1,
position: "absolute",
borderRadius: "inherit",
boxSizing: "border-box",
transitionProperty: "opacity, background-color",
transitionTimingFunction: "linear",
transitionDuration: "100ms",
},
},
thumb: {
boxShadow: "none",
color: isDark ? palette.grey.A200 : palette.grey.A700,
},
},
variants: [
{
props: {
size: "medium",
},
style: {
height: 32,
{
props: {
size: "small",
},
style: {
height: 24,
"& .MuiSwitch-switchBase": {
padding: "6px",
"& .MuiSwitch-switchBase": {
padding: "3px",
},
"& .MuiSwitch-thumb": {
width: 12,
height: 12,
margin: 3,
},
"& .Mui-checked": {
"&.MuiSwitch-switchBase": {
marginLeft: "1px",
},
"& .MuiSwitch-thumb": {
width: 14,
height: 14,
margin: 3,
},
"& .Mui-checked": {
"&.MuiSwitch-switchBase": {
marginLeft: "6px",
},
"& .MuiSwitch-thumb": {
width: 24,
height: 24,
margin: -2,
},
width: 17,
height: 17,
margin: 0,
},
},
},
{
props: {
size: "small",
},
style: {
height: 24,
"& .MuiSwitch-switchBase": {
padding: "3px",
},
"& .MuiSwitch-thumb": {
width: 12,
height: 12,
margin: 3,
},
"& .Mui-checked": {
"&.MuiSwitch-switchBase": {
marginLeft: "1px",
},
"& .MuiSwitch-thumb": {
width: 17,
height: 17,
margin: 0,
},
},
},
},
],
};
},
],
};
+15 -15
View File
@@ -143,8 +143,8 @@ importers:
frontend/interface:
dependencies:
'@tauri-apps/api':
specifier: 1.5.4
version: 1.5.4
specifier: 1.5.5
version: 1.5.5
ofetch:
specifier: 1.3.4
version: 1.3.4
@@ -201,8 +201,8 @@ importers:
specifier: workspace:^
version: link:../ui
'@tauri-apps/api':
specifier: 1.5.4
version: 1.5.4
specifier: 1.5.5
version: 1.5.5
ahooks:
specifier: 3.7.11
version: 3.7.11(react@19.0.0-beta-e7d213dfb0-20240507)
@@ -216,8 +216,8 @@ importers:
specifier: 11.1.9
version: 11.1.9(@emotion/is-prop-valid@1.2.2)(react-dom@19.0.0-beta-e7d213dfb0-20240507(react@19.0.0-beta-e7d213dfb0-20240507))(react@19.0.0-beta-e7d213dfb0-20240507)
i18next:
specifier: 23.11.3
version: 23.11.3
specifier: 23.11.4
version: 23.11.4
jotai:
specifier: 2.8.0
version: 2.8.0(react@19.0.0-beta-e7d213dfb0-20240507)(types-react@19.0.0-beta.1)
@@ -244,7 +244,7 @@ importers:
version: 7.51.4(react@19.0.0-beta-e7d213dfb0-20240507)
react-i18next:
specifier: 14.1.1
version: 14.1.1(i18next@23.11.3)(react-dom@19.0.0-beta-e7d213dfb0-20240507(react@19.0.0-beta-e7d213dfb0-20240507))(react@19.0.0-beta-e7d213dfb0-20240507)
version: 14.1.1(i18next@23.11.4)(react-dom@19.0.0-beta-e7d213dfb0-20240507(react@19.0.0-beta-e7d213dfb0-20240507))(react@19.0.0-beta-e7d213dfb0-20240507)
react-markdown:
specifier: 9.0.1
version: 9.0.1(react@19.0.0-beta-e7d213dfb0-20240507)(types-react@19.0.0-beta.1)
@@ -1453,8 +1453,8 @@ packages:
'@taplo/lib@0.4.0-alpha.2':
resolution: {integrity: sha512-DV/Re3DPVY+BhBtLZ3dmP4mP6YMLSsgq9qGLXwOV38lvNF/fBlgvQswzlXmzCEefL/3q2eMoefZpOI/+GLuCNA==}
'@tauri-apps/api@1.5.4':
resolution: {integrity: sha512-LKYae9URbdEdbHrOXBeXb/lZgVyWTX0E98rSFBuQlmkLr8OeG+akuE41PfLjBVyk1Q+fq7wxo4ieenLSMUAUhA==}
'@tauri-apps/api@1.5.5':
resolution: {integrity: sha512-Jgwj8BK/9YXZNzcqVDk1Al7+u5V9sWrZ8MhV41A1AKgJaicHuqlkc/qdx06sNDXvc+qprTPpBAaqnt891qOUIQ==}
engines: {node: '>= 14.6.0', npm: '>= 6.6.0', yarn: '>= 1.19.1'}
'@tauri-apps/cli-darwin-arm64@1.5.13':
@@ -2864,8 +2864,8 @@ packages:
resolution: {integrity: sha512-AB6lFlbwwyIqMdHYhwPe+kjOC3Oc5P3nThEoW/AaO2BX3vJDjWPFxYLxokUZOo6RNX20He3AaT8sESs9NJcmEw==}
engines: {node: '>=18'}
i18next@23.11.3:
resolution: {integrity: sha512-Pq/aSKowir7JM0rj+Wa23Kb6KKDUGno/HjG+wRQu0PxoTbpQ4N89MAT0rFGvXmLkRLNMb1BbBOKGozl01dabzg==}
i18next@23.11.4:
resolution: {integrity: sha512-CCUjtd5TfaCl+mLUzAA0uPSN+AVn4fP/kWCYt/hocPUwusTpMVczdrRyOBUwk6N05iH40qiKx6q1DoNJtBIwdg==}
iconv-lite@0.6.3:
resolution: {integrity: sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw==}
@@ -5875,7 +5875,7 @@ snapshots:
dependencies:
'@taplo/core': 0.1.1
'@tauri-apps/api@1.5.4': {}
'@tauri-apps/api@1.5.5': {}
'@tauri-apps/cli-darwin-arm64@1.5.13':
optional: true
@@ -7558,7 +7558,7 @@ snapshots:
husky@9.0.11: {}
i18next@23.11.3:
i18next@23.11.4:
dependencies:
'@babel/runtime': 7.24.5
@@ -8698,11 +8698,11 @@ snapshots:
dependencies:
react: 19.0.0-beta-e7d213dfb0-20240507
react-i18next@14.1.1(i18next@23.11.3)(react-dom@19.0.0-beta-e7d213dfb0-20240507(react@19.0.0-beta-e7d213dfb0-20240507))(react@19.0.0-beta-e7d213dfb0-20240507):
react-i18next@14.1.1(i18next@23.11.4)(react-dom@19.0.0-beta-e7d213dfb0-20240507(react@19.0.0-beta-e7d213dfb0-20240507))(react@19.0.0-beta-e7d213dfb0-20240507):
dependencies:
'@babel/runtime': 7.24.5
html-parse-stringify: 3.0.1
i18next: 23.11.3
i18next: 23.11.4
react: 19.0.0-beta-e7d213dfb0-20240507
optionalDependencies:
react-dom: 19.0.0-beta-e7d213dfb0-20240507(react@19.0.0-beta-e7d213dfb0-20240507)
+41 -10
View File
@@ -17,7 +17,7 @@ SYSTEMD_SERVICES_DIR="/etc/systemd/system"
# curl command line flags.
# To using a proxy, please specify ALL_PROXY in the environ variable, such like:
# export ALL_PROXY=socks5h://192.0.2.1:1080
CURL_FLAGS=(-L -f -q --retry 5 --retry-delay 10 --retry-max-time 60)
CURL_FLAGS=(-L --retry 5 --retry-delay 10 --retry-max-time 60)
###
# ARGUMENTS
@@ -66,12 +66,12 @@ function _check_install_required() {
# check jq and curl
if ! command -v jq &>/dev/null; then
_print_error_msg "jq is required to parse JSON data."
_print_error_msg "jq is required to parse JSON data. please use apt/yum to install jq."
exit 1
fi
if ! command -v curl &>/dev/null; then
_print_error_msg "curl is required to download files."
_print_error_msg "curl is required to download files. please use apt/yum to install curl."
exit 1
fi
}
@@ -97,6 +97,7 @@ function _detect_arch() {
echo "Unsupported architecture: $arch" >&2
;;
esac
_print_warning_msg "Detected architecture: $TARGET_ARCH"
}
function _print_error_msg() {
@@ -119,9 +120,10 @@ function _set_default_version() {
# TODO check the checksum and current bin file, if the same, skip download
function _download_bin() {
printf "Downloading Ehco version: %s\n" "$VERSION"
local api_url="https://api.github.com/repos/Ehco1996/ehco/releases/tags/$VERSION"
local _assets_json
_assets_json=$(curl -s "${CURL_FLAGS[@]}" "$api_url")
_assets_json=$(curl "${CURL_FLAGS[@]}" "$api_url")
# Extract the download URL for the target architecture using jq
download_url=$(echo "$_assets_json" | jq -r --arg TARGET_ARCH "$TARGET_ARCH" '.assets[] | select(.name | contains("ehco_" + $TARGET_ARCH)) | .browser_download_url')
@@ -135,6 +137,11 @@ function _download_bin() {
chmod +x "$EXECUTABLE_INSTALL_PATH"
}
function _update_bin() {
rm -f "$EXECUTABLE_INSTALL_PATH"
_download_bin
}
function _install_systemd_service() {
local _service_name="ehco.service"
local _service_path="$SYSTEMD_SERVICES_DIR/$_service_name"
@@ -144,6 +151,11 @@ function _install_systemd_service() {
systemctl start "$_service_name"
}
function _reload_systemd_service() {
systemctl daemon-reload
systemctl restart ehco.service
}
function _remove_systemd_service_and_delete_bin() {
local _service_name="ehco.service"
local _service_path="$SYSTEMD_SERVICES_DIR/$_service_name"
@@ -155,6 +167,15 @@ function _remove_systemd_service_and_delete_bin() {
rm -f "$EXECUTABLE_INSTALL_PATH"
}
function _check_systemd_service() {
local _service_name="ehco.service"
local _service_path="$SYSTEMD_SERVICES_DIR/$_service_name"
if [ ! -f "$_service_path" ]; then
_print_error_msg "Ehco service not found. please install it first."
exit 1
fi
}
function print_help() {
echo "Usage: $SCRIPT_NAME [options]"
echo
@@ -164,7 +185,7 @@ function print_help() {
echo " -i, --install Install the Ehco."
echo " -c, --config Specify the configuration file path or api endpoint."
echo " -r, --remove Remove the Ehco."
# echo " -u, --check-update Check if an update is available."
echo " -u, --check-update Check And Update if an update is available."
}
function parse_arguments() {
@@ -188,9 +209,9 @@ function parse_arguments() {
-r | --remove)
OPERATION="remove"
;;
# -u | --check-update)
# OPERATION="check_update"
# ;;
-u | --check-update)
OPERATION="check-update"
;;
*)
_print_error_msg "Unknown argument: $1"
exit 1
@@ -220,6 +241,16 @@ function perform_remove() {
_print_warning_msg "Ehco has been removed."
}
function perform_check_update() {
_check_systemd_service
_set_default_version
_detect_arch
_update_bin
_reload_systemd_service
_print_warning_msg "Ehco has been Updated."
}
###
# Entrypoint
###
@@ -232,8 +263,8 @@ function main() {
"remove")
perform_remove
;;
"check_update")
# perform_check_update
"check-update")
perform_check_update
;;
*)
_print_error_msg "Unknown operation: '$OPERATION'."
+7 -7
View File
@@ -19,8 +19,8 @@ const (
// MetricGroup name format for each user.
UserMetricGroupFormat = "user - %s"
UserMetricReadBytes = "ReadBytes"
UserMetricWriteBytes = "WriteBytes"
UserMetricUploadBytes = "UploadBytes"
UserMetricDownloadBytes = "DownloadBytes"
)
var (
@@ -36,12 +36,12 @@ var (
// Current number of established connections.
CurrEstablished = RegisterMetric("connections", "CurrEstablished", GAUGE)
// Number of bytes receive from proxy connections.
InBytes = RegisterMetric("traffic", "InBytes", COUNTER)
// Number of bytes from client to server.
UploadBytes = RegisterMetric("traffic", "UploadBytes", COUNTER)
// Number of bytes send to proxy connections.
OutBytes = RegisterMetric("traffic", "OutBytes", COUNTER)
// Number of bytes from server to client.
DownloadBytes = RegisterMetric("traffic", "DownloadBytes", COUNTER)
// Number of padding bytes send to proxy connections.
OutPaddingBytes = RegisterMetric("traffic", "OutPaddingBytes", COUNTER)
OutputPaddingBytes = RegisterMetric("traffic", "OutputPaddingBytes", COUNTER)
)
+21 -19
View File
@@ -106,8 +106,8 @@ type Session struct {
ackOnDataRecv atomic.Bool // whether ack should be sent due to receive of new data
unreadBuf []byte // payload removed from the recvQueue that haven't been read by application
readBytes metrics.Metric // number of bytes delivered to the application
writeBytes metrics.Metric // number of bytes sent from the application
uploadBytes metrics.Metric // number of bytes from client to server, only used by server
downloadBytes metrics.Metric // number of bytes from server to client, only used by server
rttStat *congestion.RTTStats
sendAlgorithm *congestion.CubicSendAlgorithm
@@ -190,8 +190,8 @@ func (s *Session) Read(b []byte) (n int, err error) {
if log.IsLevelEnabled(log.TraceLevel) {
log.Tracef("%v read %d bytes", s, n)
}
if s.readBytes != nil {
s.readBytes.Add(int64(n))
if !s.isClient && s.uploadBytes != nil {
s.uploadBytes.Add(int64(n))
}
return n, nil
}
@@ -245,8 +245,8 @@ func (s *Session) Read(b []byte) (n int, err error) {
if log.IsLevelEnabled(log.TraceLevel) {
log.Tracef("%v read %d bytes", s, n)
}
if s.readBytes != nil {
s.readBytes.Add(int64(n))
if !s.isClient && s.uploadBytes != nil {
s.uploadBytes.Add(int64(n))
}
return n, nil
}
@@ -307,8 +307,8 @@ func (s *Session) Write(b []byte) (n int, err error) {
if log.IsLevelEnabled(log.TraceLevel) {
log.Tracef("%v wrote %d bytes", s, n)
}
if s.writeBytes != nil {
s.writeBytes.Add(int64(n))
if !s.isClient && s.downloadBytes != nil {
s.downloadBytes.Add(int64(n))
}
return n, nil
}
@@ -724,11 +724,13 @@ func (s *Session) input(seg *segment) error {
}
if seg.block != nil {
s.block = seg.block
if s.readBytes == nil && s.block.BlockContext().UserName != "" {
s.readBytes = metrics.RegisterMetric(fmt.Sprintf(metrics.UserMetricGroupFormat, s.block.BlockContext().UserName), metrics.UserMetricReadBytes, metrics.COUNTER_TIME_SERIES)
}
if s.writeBytes == nil && s.block.BlockContext().UserName != "" {
s.writeBytes = metrics.RegisterMetric(fmt.Sprintf(metrics.UserMetricGroupFormat, s.block.BlockContext().UserName), metrics.UserMetricWriteBytes, metrics.COUNTER_TIME_SERIES)
if !s.isClient {
if s.uploadBytes == nil && s.block.BlockContext().UserName != "" {
s.uploadBytes = metrics.RegisterMetric(fmt.Sprintf(metrics.UserMetricGroupFormat, s.block.BlockContext().UserName), metrics.UserMetricUploadBytes, metrics.COUNTER_TIME_SERIES)
}
if s.downloadBytes == nil && s.block.BlockContext().UserName != "" {
s.downloadBytes = metrics.RegisterMetric(fmt.Sprintf(metrics.UserMetricGroupFormat, s.block.BlockContext().UserName), metrics.UserMetricDownloadBytes, metrics.COUNTER_TIME_SERIES)
}
}
}
s.lastRXTime = time.Now()
@@ -958,19 +960,19 @@ func (s *Session) checkQuota(userName string) (ok bool, err error) {
if metricGroup == nil {
return true, fmt.Errorf("metric group %s is not found", metricGroupName)
}
readBytes, found := metricGroup.GetMetric(metrics.UserMetricReadBytes)
uploadBytes, found := metricGroup.GetMetric(metrics.UserMetricUploadBytes)
if !found {
return true, fmt.Errorf("metric %s in group %s is not found", metrics.UserMetricReadBytes, metricGroupName)
return true, fmt.Errorf("metric %s in group %s is not found", metrics.UserMetricUploadBytes, metricGroupName)
}
writeBytes, found := metricGroup.GetMetric(metrics.UserMetricWriteBytes)
downloadBytes, found := metricGroup.GetMetric(metrics.UserMetricDownloadBytes)
if !found {
return true, fmt.Errorf("metric %s in group %s is not found", metrics.UserMetricWriteBytes, metricGroupName)
return true, fmt.Errorf("metric %s in group %s is not found", metrics.UserMetricDownloadBytes, metricGroupName)
}
for _, quota := range user.GetQuotas() {
now := time.Now()
then := now.Add(-time.Duration(quota.GetDays()) * 24 * time.Hour)
totalBytes := readBytes.(*metrics.Counter).DeltaBetween(then, now)
totalBytes += writeBytes.(*metrics.Counter).DeltaBetween(then, now)
totalBytes := uploadBytes.(*metrics.Counter).DeltaBetween(then, now)
totalBytes += downloadBytes.(*metrics.Counter).DeltaBetween(then, now)
if totalBytes/1048576 > int64(quota.GetMegabytes()) {
return false, nil
}
+43 -11
View File
@@ -303,7 +303,11 @@ func (t *TCPUnderlay) readOneSegment() (*segment, error, stderror.ErrorType) {
if _, err := io.ReadFull(t.conn, encryptedMeta); err != nil {
return nil, fmt.Errorf("metadata: read %d bytes from TCPUnderlay failed: %w", readLen, err), stderror.NETWORK_ERROR
}
metrics.InBytes.Add(int64(len(encryptedMeta)))
if t.isClient {
metrics.DownloadBytes.Add(int64(len(encryptedMeta)))
} else {
metrics.UploadBytes.Add(int64(len(encryptedMeta)))
}
if tcpReplayCache.IsDuplicate(encryptedMeta[:cipher.DefaultOverhead], replay.EmptyTag) {
if firstRead {
replay.NewSession.Add(1)
@@ -374,7 +378,11 @@ func (t *TCPUnderlay) readSessionSegment(ss *sessionStruct) (*segment, error, st
if _, err := io.ReadFull(t.conn, encryptedPayload); err != nil {
return nil, fmt.Errorf("payload: read %d bytes from TCPUnderlay failed: %w", ss.payloadLen+cipher.DefaultOverhead, err), stderror.NETWORK_ERROR
}
metrics.InBytes.Add(int64(len(encryptedPayload)))
if t.isClient {
metrics.DownloadBytes.Add(int64(len(encryptedPayload)))
} else {
metrics.UploadBytes.Add(int64(len(encryptedPayload)))
}
if tcpReplayCache.IsDuplicate(encryptedPayload[:cipher.DefaultOverhead], replay.EmptyTag) {
replay.KnownSession.Add(1)
}
@@ -398,7 +406,11 @@ func (t *TCPUnderlay) readSessionSegment(ss *sessionStruct) (*segment, error, st
if _, err := io.ReadFull(t.conn, padding); err != nil {
return nil, fmt.Errorf("padding: read %d bytes from TCPUnderlay failed: %w", ss.suffixLen, err), stderror.NETWORK_ERROR
}
metrics.InBytes.Add(int64(len(padding)))
if t.isClient {
metrics.DownloadBytes.Add(int64(len(padding)))
} else {
metrics.UploadBytes.Add(int64(len(padding)))
}
}
return &segment{
@@ -418,14 +430,22 @@ func (t *TCPUnderlay) readDataAckSegment(das *dataAckStruct) (*segment, error, s
if _, err := io.ReadFull(t.conn, padding1); err != nil {
return nil, fmt.Errorf("padding: read %d bytes from TCPUnderlay failed: %w", das.prefixLen, err), stderror.NETWORK_ERROR
}
metrics.InBytes.Add(int64(len(padding1)))
if t.isClient {
metrics.DownloadBytes.Add(int64(len(padding1)))
} else {
metrics.UploadBytes.Add(int64(len(padding1)))
}
}
if das.payloadLen > 0 {
encryptedPayload := make([]byte, das.payloadLen+cipher.DefaultOverhead)
if _, err := io.ReadFull(t.conn, encryptedPayload); err != nil {
return nil, fmt.Errorf("payload: read %d bytes from TCPUnderlay failed: %w", das.payloadLen+cipher.DefaultOverhead, err), stderror.NETWORK_ERROR
}
metrics.InBytes.Add(int64(len(encryptedPayload)))
if t.isClient {
metrics.DownloadBytes.Add(int64(len(encryptedPayload)))
} else {
metrics.UploadBytes.Add(int64(len(encryptedPayload)))
}
if tcpReplayCache.IsDuplicate(encryptedPayload[:cipher.DefaultOverhead], replay.EmptyTag) {
replay.KnownSession.Add(1)
}
@@ -449,7 +469,11 @@ func (t *TCPUnderlay) readDataAckSegment(das *dataAckStruct) (*segment, error, s
if _, err := io.ReadFull(t.conn, padding2); err != nil {
return nil, fmt.Errorf("padding: read %d bytes from TCPUnderlay failed: %w", das.suffixLen, err), stderror.NETWORK_ERROR
}
metrics.InBytes.Add(int64(len(padding2)))
if t.isClient {
metrics.DownloadBytes.Add(int64(len(padding2)))
} else {
metrics.UploadBytes.Add(int64(len(padding2)))
}
}
return &segment{
@@ -499,8 +523,12 @@ func (t *TCPUnderlay) writeOneSegment(seg *segment) error {
if _, err := t.conn.Write(dataToSend); err != nil {
return fmt.Errorf("Write() failed: %w", err)
}
metrics.OutBytes.Add(int64(len(dataToSend)))
metrics.OutPaddingBytes.Add(int64(len(padding)))
if t.isClient {
metrics.UploadBytes.Add(int64(len(dataToSend)))
} else {
metrics.DownloadBytes.Add(int64(len(dataToSend)))
}
metrics.OutputPaddingBytes.Add(int64(len(padding)))
} else if das, ok := toDataAckStruct(seg.metadata); ok {
padding1 := newPadding(paddingOpts{
maxLen: MaxPaddingSize(t.mtu, t.IPVersion(), t.TransportProtocol(), int(das.payloadLen), 0),
@@ -534,9 +562,13 @@ func (t *TCPUnderlay) writeOneSegment(seg *segment) error {
if _, err := t.conn.Write(dataToSend); err != nil {
return fmt.Errorf("Write() failed: %w", err)
}
metrics.OutBytes.Add(int64(len(dataToSend)))
metrics.OutPaddingBytes.Add(int64(len(padding1)))
metrics.OutPaddingBytes.Add(int64(len(padding2)))
if t.isClient {
metrics.UploadBytes.Add(int64(len(dataToSend)))
} else {
metrics.DownloadBytes.Add(int64(len(dataToSend)))
}
metrics.OutputPaddingBytes.Add(int64(len(padding1)))
metrics.OutputPaddingBytes.Add(int64(len(padding2)))
} else {
return stderror.ErrInvalidArgument
}
+18 -6
View File
@@ -365,7 +365,11 @@ func (u *UDPUnderlay) readOneSegment() (*segment, *net.UDPAddr, error) {
continue
}
b = b[:n]
metrics.InBytes.Add(int64(n))
if u.isClient {
metrics.DownloadBytes.Add(int64(n))
} else {
metrics.UploadBytes.Add(int64(n))
}
// Read encrypted metadata.
encryptedMeta := b[:udpNonHeaderPosition]
@@ -662,8 +666,12 @@ func (u *UDPUnderlay) writeOneSegment(seg *segment, addr *net.UDPAddr) error {
if _, err := u.conn.WriteToUDP(dataToSend, addr); err != nil {
return fmt.Errorf("WriteToUDP() failed: %w", err)
}
metrics.OutBytes.Add(int64(len(dataToSend)))
metrics.OutPaddingBytes.Add(int64(len(padding)))
if u.isClient {
metrics.UploadBytes.Add(int64(len(dataToSend)))
} else {
metrics.DownloadBytes.Add(int64(len(dataToSend)))
}
metrics.OutputPaddingBytes.Add(int64(len(padding)))
} else if das, ok := toDataAckStruct(seg.metadata); ok {
padding1 := newPadding(paddingOpts{
maxLen: MaxPaddingSize(u.mtu, u.IPVersion(), u.TransportProtocol(), int(das.payloadLen), 0),
@@ -695,9 +703,13 @@ func (u *UDPUnderlay) writeOneSegment(seg *segment, addr *net.UDPAddr) error {
if _, err := u.conn.WriteToUDP(dataToSend, addr); err != nil {
return fmt.Errorf("WriteToUDP() failed: %w", err)
}
metrics.OutBytes.Add(int64(len(dataToSend)))
metrics.OutPaddingBytes.Add(int64(len(padding1)))
metrics.OutPaddingBytes.Add(int64(len(padding2)))
if u.isClient {
metrics.UploadBytes.Add(int64(len(dataToSend)))
} else {
metrics.DownloadBytes.Add(int64(len(dataToSend)))
}
metrics.OutputPaddingBytes.Add(int64(len(padding1)))
metrics.OutputPaddingBytes.Add(int64(len(padding2)))
} else {
return stderror.ErrInvalidArgument
}
+4 -4
View File
@@ -40,8 +40,8 @@ func BidiCopyUDP(udpConn *net.UDPConn, tunnelConn *UDPAssociateTunnelConn) error
errCh <- fmt.Errorf("ReadFrom UDP connection failed: %w", err)
break
}
UDPAssociateInPkts.Add(1)
UDPAssociateInBytes.Add(int64(n))
UDPAssociateUploadPackets.Add(1)
UDPAssociateUploadBytes.Add(int64(n))
udpAddr := addr.Load()
if udpAddr == nil {
addr.Store(a)
@@ -74,8 +74,8 @@ func BidiCopyUDP(udpConn *net.UDPConn, tunnelConn *UDPAssociateTunnelConn) error
errCh <- fmt.Errorf("WriteTo UDP connetion failed: %w", err)
break
}
UDPAssociateOutPkts.Add(1)
UDPAssociateOutBytes.Add(int64(ws))
UDPAssociateDownloadPackets.Add(1)
UDPAssociateDownloadBytes.Add(int64(ws))
}
}()
+8 -8
View File
@@ -290,8 +290,8 @@ func (s *Server) handleAssociate(ctx context.Context, req *Request, conn io.Read
log.Debugf("UDP associate [%v - %v] WriteToUDP() failed: %v", udpConn.LocalAddr(), dstAddr, err)
UDPAssociateErrors.Add(1)
} else {
UDPAssociateInPkts.Add(1)
UDPAssociateInBytes.Add(int64(ws))
UDPAssociateUploadPackets.Add(1)
UDPAssociateUploadBytes.Add(int64(ws))
}
case 0x03:
fqdnLen := buf[4]
@@ -308,8 +308,8 @@ func (s *Server) handleAssociate(ctx context.Context, req *Request, conn io.Read
log.Debugf("UDP associate [%v - %v] WriteToUDP() failed: %v", udpConn.LocalAddr(), dstAddr, err)
UDPAssociateErrors.Add(1)
} else {
UDPAssociateInPkts.Add(1)
UDPAssociateInBytes.Add(int64(ws))
UDPAssociateUploadPackets.Add(1)
UDPAssociateUploadBytes.Add(int64(ws))
}
case 0x04:
dstAddr := &net.UDPAddr{
@@ -322,8 +322,8 @@ func (s *Server) handleAssociate(ctx context.Context, req *Request, conn io.Read
log.Debugf("UDP associate [%v - %v] WriteToUDP() failed: %v", udpConn.LocalAddr(), dstAddr, err)
UDPAssociateErrors.Add(1)
} else {
UDPAssociateInPkts.Add(1)
UDPAssociateInBytes.Add(int64(ws))
UDPAssociateUploadPackets.Add(1)
UDPAssociateUploadBytes.Add(int64(ws))
}
}
}
@@ -365,8 +365,8 @@ func (s *Server) handleAssociate(ctx context.Context, req *Request, conn io.Read
}
return
}
UDPAssociateOutPkts.Add(1)
UDPAssociateOutBytes.Add(int64(n))
UDPAssociateDownloadPackets.Add(1)
UDPAssociateDownloadBytes.Add(int64(n))
}
}()
+4 -4
View File
@@ -35,10 +35,10 @@ var (
ConnectionRefusedErrors = metrics.RegisterMetric("socks5", "ConnectionRefusedErrors", metrics.COUNTER)
UDPAssociateErrors = metrics.RegisterMetric("socks5", "UDPAssociateErrors", metrics.COUNTER)
UDPAssociateInBytes = metrics.RegisterMetric("socks5 UDP associate", "InBytes", metrics.COUNTER)
UDPAssociateOutBytes = metrics.RegisterMetric("socks5 UDP associate", "OutBytes", metrics.COUNTER)
UDPAssociateInPkts = metrics.RegisterMetric("socks5 UDP associate", "InPkts", metrics.COUNTER)
UDPAssociateOutPkts = metrics.RegisterMetric("socks5 UDP associate", "OutPkts", metrics.COUNTER)
UDPAssociateUploadBytes = metrics.RegisterMetric("socks5 UDP associate", "UploadBytes", metrics.COUNTER)
UDPAssociateDownloadBytes = metrics.RegisterMetric("socks5 UDP associate", "DownloadBytes", metrics.COUNTER)
UDPAssociateUploadPackets = metrics.RegisterMetric("socks5 UDP associate", "UploadPackets", metrics.COUNTER)
UDPAssociateDownloadPackets = metrics.RegisterMetric("socks5 UDP associate", "DownloadPackets", metrics.COUNTER)
)
// Config is used to setup and configure a socks5 server.
+6 -6
View File
@@ -109,8 +109,8 @@ func TestSocks5Connect(t *testing.T) {
}
func TestSocks5UDPAssociation(t *testing.T) {
udpInPktsCnt := UDPAssociateInPkts.Load()
udpOutPktsCnt := UDPAssociateOutPkts.Load()
udpUploadPktsCnt := UDPAssociateUploadPackets.Load()
udpDownloadPktsCnt := UDPAssociateDownloadPackets.Load()
// Create a local listener as the destination target.
udpListenerAddr, err := net.ResolveUDPAddr("udp", "127.0.0.1:0")
@@ -235,10 +235,10 @@ func TestSocks5UDPAssociation(t *testing.T) {
}
// Verify metrics are updated.
if UDPAssociateInPkts.Load() <= udpInPktsCnt {
t.Errorf("UDPAssociateInPkts value %d is not increased", UDPAssociateInPkts.Load())
if UDPAssociateUploadPackets.Load() <= udpUploadPktsCnt {
t.Errorf("UDPAssociateUploadPackets value %d is not increased", UDPAssociateUploadPackets.Load())
}
if UDPAssociateOutPkts.Load() <= udpOutPktsCnt {
t.Errorf("UDPAssociateOutPkts value %d is not increased", UDPAssociateOutPkts.Load())
if UDPAssociateDownloadPackets.Load() <= udpDownloadPktsCnt {
t.Errorf("UDPAssociateDownloadPackets value %d is not increased", UDPAssociateDownloadPackets.Load())
}
}
+2 -2
View File
@@ -10,11 +10,11 @@ include $(TOPDIR)/rules.mk
PKG_ARCH_quickstart:=$(ARCH)
PKG_NAME:=quickstart
PKG_VERSION:=0.8.13
PKG_VERSION:=0.8.14
PKG_RELEASE:=1
PKG_SOURCE:=$(PKG_NAME)-binary-$(PKG_VERSION).tar.gz
PKG_SOURCE_URL:=https://github.com/linkease/istore-packages/releases/download/prebuilt/
PKG_HASH:=2b1974b75f43bb01d25fb9a7d21695c9e0f5a80297fdcfc51215cc4f6cbfe985
PKG_HASH:=287d60f90d7df57e325dee417166bf5eb6e05cf6089fd3ef6e1111f2fe896cf6
PKG_BUILD_DIR:=$(BUILD_DIR)/$(PKG_NAME)-binary-$(PKG_VERSION)
@@ -767,8 +767,8 @@ function gen_config(var)
end
if node.protocol == "_shunt" then
local proxy_node_id = node["main_node"]
local proxy_tag = "main"
local proxy_tag = node.preproxy_enabled == "1" and "main" or nil
local proxy_node_id = proxy_tag and node["main_node"] or nil
local proxy_balancer_tag
local proxy_nodes
@@ -862,7 +862,6 @@ function gen_config(var)
end
return outbound_tag, nil
elseif _node.protocol == "_balancing" then
return nil, gen_balancer(_node, rule_name)
elseif _node.protocol == "_iface" then
if _node.iface then
@@ -884,7 +883,7 @@ function gen_config(var)
end
--proxy_node
if node.preproxy_enabled == "1" and proxy_node_id then
if proxy_tag and proxy_node_id then
local proxy_outbound_tag
proxy_outbound_tag, proxy_balancer_tag = gen_shunt_node(proxy_tag, proxy_node_id)
if proxy_balancer_tag then
+4
View File
@@ -2,6 +2,10 @@
icon: material/alert-decagram
---
#### 1.9.0-rc.17
* Fixes and improvements
#### 1.9.0-rc.16
* Mitigating TunnelVision attacks **1**
+1 -1
View File
@@ -33,7 +33,7 @@ require (
github.com/sagernet/sing-shadowsocks v0.2.6
github.com/sagernet/sing-shadowsocks2 v0.2.0
github.com/sagernet/sing-shadowtls v0.1.4
github.com/sagernet/sing-tun v0.3.0-beta.1
github.com/sagernet/sing-tun v0.3.0-beta.2
github.com/sagernet/sing-vmess v0.1.8
github.com/sagernet/smux v0.0.0-20231208180855-7041f6ea79e7
github.com/sagernet/tfo-go v0.0.0-20231209031829-7b5343ac1dc6
+2 -2
View File
@@ -120,8 +120,8 @@ github.com/sagernet/sing-shadowsocks2 v0.2.0 h1:wpZNs6wKnR7mh1wV9OHwOyUr21VkS3wK
github.com/sagernet/sing-shadowsocks2 v0.2.0/go.mod h1:RnXS0lExcDAovvDeniJ4IKa2IuChrdipolPYWBv9hWQ=
github.com/sagernet/sing-shadowtls v0.1.4 h1:aTgBSJEgnumzFenPvc+kbD9/W0PywzWevnVpEx6Tw3k=
github.com/sagernet/sing-shadowtls v0.1.4/go.mod h1:F8NBgsY5YN2beQavdgdm1DPlhaKQlaL6lpDdcBglGK4=
github.com/sagernet/sing-tun v0.3.0-beta.1 h1:OdVK+/hoD6qWHy/SUwpwqFR+IScI3FIQvBDLR05xWSk=
github.com/sagernet/sing-tun v0.3.0-beta.1/go.mod h1:xPaOkQhngPMILx+/9DMLCFl4vSxUU2tMnCPSlf05HLo=
github.com/sagernet/sing-tun v0.3.0-beta.2 h1:sfeHWnBTKGpFUjXpT+O/JEwFP8oVAo3M0Xx94ghesjU=
github.com/sagernet/sing-tun v0.3.0-beta.2/go.mod h1:xPaOkQhngPMILx+/9DMLCFl4vSxUU2tMnCPSlf05HLo=
github.com/sagernet/sing-vmess v0.1.8 h1:XVWad1RpTy9b5tPxdm5MCU8cGfrTGdR8qCq6HV2aCNc=
github.com/sagernet/sing-vmess v0.1.8/go.mod h1:vhx32UNzTDUkNwOyIjcZQohre1CaytquC5mPplId8uA=
github.com/sagernet/smux v0.0.0-20231208180855-7041f6ea79e7 h1:DImB4lELfQhplLTxeq2z31Fpv8CQqqrUwTbrIRumZqQ=
+17 -3
View File
@@ -287,8 +287,23 @@ func (g *URLTestGroup) Close() error {
func (g *URLTestGroup) Select(network string) (adapter.Outbound, bool) {
var minDelay uint16
var minTime time.Time
var minOutbound adapter.Outbound
switch network {
case N.NetworkTCP:
if g.selectedOutboundTCP != nil {
if history := g.history.LoadURLTestHistory(RealTag(g.selectedOutboundTCP)); history != nil {
minOutbound = g.selectedOutboundTCP
minDelay = history.Delay
}
}
case N.NetworkUDP:
if g.selectedOutboundUDP != nil {
if history := g.history.LoadURLTestHistory(RealTag(g.selectedOutboundUDP)); history != nil {
minOutbound = g.selectedOutboundUDP
minDelay = history.Delay
}
}
}
for _, detour := range g.outbounds {
if !common.Contains(detour.Network(), network) {
continue
@@ -297,9 +312,8 @@ func (g *URLTestGroup) Select(network string) (adapter.Outbound, bool) {
if history == nil {
continue
}
if minDelay == 0 || minDelay > history.Delay+g.tolerance || minDelay > history.Delay-g.tolerance && minTime.Before(history.Time) {
if minDelay == 0 || minDelay > history.Delay+g.tolerance {
minDelay = history.Delay
minTime = history.Time
minOutbound = detour
}
}
+1
View File
@@ -284,6 +284,7 @@ func NewLogicalDNSRule(router adapter.Router, logger log.ContextLogger, options
},
disableCache: options.DisableCache,
rewriteTTL: options.RewriteTTL,
clientSubnet: (*netip.Addr)(options.ClientSubnet),
}
switch options.Mode {
case C.LogicalTypeAnd:
+2 -2
View File
@@ -5,12 +5,12 @@
include $(TOPDIR)/rules.mk
PKG_NAME:=brook
PKG_VERSION:=20240404
PKG_VERSION:=20240606
PKG_RELEASE:=1
PKG_SOURCE:=$(PKG_NAME)-$(PKG_VERSION).tar.gz
PKG_SOURCE_URL:=https://codeload.github.com/txthinking/brook/tar.gz/v$(PKG_VERSION)?
PKG_HASH:=6eda9a348f9c3555a1c27711e81c0982ea9999bf2878e73cf2eaaee90e8cc2e7
PKG_HASH:=eee1c6173daff3199c23396f4661d7f81d701dc0f4eb1662b39041a6ca10703b
PKG_MAINTAINER:=Tianling Shen <cnsztl@immortalwrt.org>
PKG_LICENSE:=GPL-3.0
@@ -767,8 +767,8 @@ function gen_config(var)
end
if node.protocol == "_shunt" then
local proxy_node_id = node["main_node"]
local proxy_tag = "main"
local proxy_tag = node.preproxy_enabled == "1" and "main" or nil
local proxy_node_id = proxy_tag and node["main_node"] or nil
local proxy_balancer_tag
local proxy_nodes
@@ -862,7 +862,6 @@ function gen_config(var)
end
return outbound_tag, nil
elseif _node.protocol == "_balancing" then
return nil, gen_balancer(_node, rule_name)
elseif _node.protocol == "_iface" then
if _node.iface then
@@ -884,7 +883,7 @@ function gen_config(var)
end
--proxy_node
if node.preproxy_enabled == "1" and proxy_node_id then
if proxy_tag and proxy_node_id then
local proxy_outbound_tag
proxy_outbound_tag, proxy_balancer_tag = gen_shunt_node(proxy_tag, proxy_node_id)
if proxy_balancer_tag then
+1 -1
View File
@@ -18,7 +18,7 @@ on:
schedule:
- cron: '0 16 * * *'
env:
CACHE_EPOCH: 125-2
CACHE_EPOCH: 126-1
GOPROXY: direct
jobs:
win-clang-tidy:
+1 -1
View File
@@ -33,7 +33,7 @@ concurrency:
group: ${{ github.workflow }} / ${{ startsWith(github.event_name, 'pull') && github.ref_name || github.sha }}
cancel-in-progress: ${{ startsWith(github.event_name, 'pull') }}
env:
CACHE_EPOCH: 125-2
CACHE_EPOCH: 126-1
GOPROXY: direct
jobs:
win-compiler-compatible-2022:
+5 -1
View File
@@ -35,7 +35,7 @@ concurrency:
group: ${{ github.workflow }} / ${{ startsWith(github.event_name, 'pull') && github.ref_name || github.sha }}
cancel-in-progress: ${{ startsWith(github.event_name, 'pull') }}
env:
CACHE_EPOCH: 125-2
CACHE_EPOCH: 126-1
GOPROXY: direct
jobs:
android-binary-release:
@@ -131,6 +131,10 @@ jobs:
run: |
cd tools
go build
- name: Replace Android NDK (to latest)
run: |
echo "ANDROID_NDK_ROOT=${ANDROID_NDK_LATEST_HOME}" >> $GITHUB_ENV
echo "ANDROID_NDK_HOME=${ANDROID_NDK_LATEST_HOME}" >> $GITHUB_ENV
- name: "Install dependency: tun2proxy"
run: |
./scripts/setup-android-rust.sh
+1 -1
View File
@@ -35,7 +35,7 @@ concurrency:
group: ${{ github.workflow }} / ${{ startsWith(github.event_name, 'pull') && github.ref_name || github.sha }}
cancel-in-progress: ${{ startsWith(github.event_name, 'pull') }}
env:
CACHE_EPOCH: 125-2
CACHE_EPOCH: 126-1
GOPROXY: direct
DH_QUIET: 1
jobs:
+1 -1
View File
@@ -35,7 +35,7 @@ concurrency:
group: ${{ github.workflow }} / ${{ startsWith(github.event_name, 'pull') && github.ref_name || github.sha }}
cancel-in-progress: ${{ startsWith(github.event_name, 'pull') }}
env:
CACHE_EPOCH: 125-2
CACHE_EPOCH: 126-1
GOPROXY: direct
jobs:
freebsd-binary-release:
+1 -1
View File
@@ -21,7 +21,7 @@ concurrency:
group: ${{ github.workflow }} / ${{ startsWith(github.event_name, 'pull') && github.ref_name || github.sha }}
cancel-in-progress: ${{ startsWith(github.event_name, 'pull') }}
env:
CACHE_EPOCH: 125-2
CACHE_EPOCH: 126-1
GOPROXY: direct
jobs:
ios-simulator-release:
+1 -1
View File
@@ -35,7 +35,7 @@ concurrency:
group: ${{ github.workflow }} / ${{ startsWith(github.event_name, 'pull') && github.ref_name || github.sha }}
cancel-in-progress: ${{ startsWith(github.event_name, 'pull') }}
env:
CACHE_EPOCH: 125-2
CACHE_EPOCH: 126-1
GOPROXY: direct
jobs:
linux-binary-release:
+1 -1
View File
@@ -35,7 +35,7 @@ concurrency:
group: ${{ github.workflow }} / ${{ startsWith(github.event_name, 'pull') && github.ref_name || github.sha }}
cancel-in-progress: ${{ startsWith(github.event_name, 'pull') }}
env:
CACHE_EPOCH: 125-2
CACHE_EPOCH: 126-1
GOPROXY: direct
jobs:
mac-release:
+1 -1
View File
@@ -35,7 +35,7 @@ concurrency:
group: ${{ github.workflow }} / ${{ startsWith(github.event_name, 'pull') && github.ref_name || github.sha }}
cancel-in-progress: ${{ startsWith(github.event_name, 'pull') }}
env:
CACHE_EPOCH: 125-2
CACHE_EPOCH: 126-1
GOPROXY: direct
jobs:
mingw64-release:
+1 -1
View File
@@ -18,7 +18,7 @@ on:
schedule:
- cron: '0 16 * * *'
env:
CACHE_EPOCH: 125-2
CACHE_EPOCH: 126-1
GOPROXY: direct
jobs:
mingw64-release:
+1 -1
View File
@@ -35,7 +35,7 @@ concurrency:
group: ${{ github.workflow }} / ${{ startsWith(github.event_name, 'pull') && github.ref_name || github.sha }}
cancel-in-progress: ${{ startsWith(github.event_name, 'pull') }}
env:
CACHE_EPOCH: 125-2
CACHE_EPOCH: 126-1
GOPROXY: direct
jobs:
openwrt-binary-release:
+1 -1
View File
@@ -35,7 +35,7 @@ concurrency:
group: ${{ github.workflow }} / ${{ startsWith(github.event_name, 'pull') && github.ref_name || github.sha }}
cancel-in-progress: ${{ startsWith(github.event_name, 'pull') }}
env:
CACHE_EPOCH: 125-2
CACHE_EPOCH: 126-1
GOPROXY: direct
jobs:
docker_publish:
+1 -1
View File
@@ -24,7 +24,7 @@ defaults:
run:
shell: cmd
env:
CACHE_EPOCH: 125-2
CACHE_EPOCH: 126-1
GOPROXY: direct
jobs:
windows-release:
+1 -1
View File
@@ -18,7 +18,7 @@ on:
schedule:
- cron: '0 16 * * *'
env:
CACHE_EPOCH: 125-2
CACHE_EPOCH: 126-1
GOPROXY: direct
jobs:
sanitizer-linux:
+1 -1
View File
@@ -1 +1 @@
llvmorg-19-init-8091-gab037c4f-1
llvmorg-19-init-9433-g76ea5feb-1
+1 -1
View File
@@ -1 +1 @@
1cea0473a5a1f3124f0d3a95643c573296a2bac5
dc489055ed8ecb1eb3fb8e22a7a4c5c4d9b1459e
+2 -2
View File
@@ -171,7 +171,7 @@ config("default") {
]
cflags_c = [ "-std=c11" ]
cflags_cc = [ "-std=c++17" ]
cflags_cc = [ "-std=c++20" ]
cflags_objc = cflags_c
cflags_objcc = cflags_cc
@@ -274,7 +274,7 @@ config("default") {
]
cflags_cc = [
"/std:c++17",
"/std:c++20",
"/Zc:__cplusplus",
]
+5 -5
View File
@@ -26,13 +26,13 @@ rustup target add aarch64-linux-android armv7-linux-androideabi i686-linux-andro
echo "Adding rustup android target...done"
mkdir -p ~/.cargo
HAS_CARGO_ANDROID="$(grep target.aarch64-linux-android ~/.cargo/config || :)"
HAS_CARGO_ANDROID="$(grep target.aarch64-linux-android ~/.cargo/config.toml || :)"
if [ ! -z "$HAS_CARGO_ANDROID" ]; then
echo "Skip patching cargo config..."
echo "Skip patching cargo config.toml ..."
exit 0
fi
echo "Patching cargo config..."
echo "Patching cargo config.toml ..."
ARCH=$(uname -s)
case "$ARCH" in
@@ -51,7 +51,7 @@ case "$ARCH" in
;;
esac
cat >> ~/.cargo/config << EOF
cat >> ~/.cargo/config.toml << EOF
[target.aarch64-linux-android]
ar = "$ANDROID_NDK_ROOT/toolchains/llvm/prebuilt/${HOST_OS}-x86_64/bin/llvm-ar"
@@ -70,4 +70,4 @@ ar = "$ANDROID_NDK_ROOT/toolchains/llvm/prebuilt/${HOST_OS}-x86_64/bin/llvm-ar"
linker = "$ANDROID_NDK_ROOT/toolchains/llvm/prebuilt/${HOST_OS}-x86_64/bin/x86_64-linux-android24-clang"
EOF
echo "Patching cargo config...done"
echo "Patching cargo config.toml ...done"
+5 -5
View File
@@ -24,15 +24,15 @@ fi
echo "Adding rustup toolchain...done"
mkdir -p ~/.cargo
HAS_CARGO_HARMONY="$(grep target.aarch64-unknown-linux-ohos ~/.cargo/config || :)"
HAS_CARGO_HARMONY="$(grep target.aarch64-unknown-linux-ohos ~/.cargo/config.toml || :)"
if [ ! -z "$HAS_CARGO_HARMONY" ]; then
echo "Skip patching cargo config..."
echo "Skip patching cargo config.toml ..."
exit 0
fi
echo "Patching cargo config..."
echo "Patching cargo config.toml ..."
cat >> ~/.cargo/config << EOF
cat >> ~/.cargo/config.toml << EOF
[target.aarch64-unknown-linux-ohos]
ar = "$HARMONY_NDK_ROOT/native/llvm/bin/llvm-ar"
linker = "$PWD/scripts/aarch64-unknown-linux-ohos-clang.sh"
@@ -46,4 +46,4 @@ ar = "$HARMONY_NDK_ROOT/native/llvm/bin/llvm-ar"
linker = "$PWD/scripts/x86_64-unknown-linux-ohos-clang.sh"
EOF
echo "Patching cargo config...done"
echo "Patching cargo config.toml ...done"
+225 -204
View File
@@ -13,6 +13,8 @@
#include "net/base64.hpp"
#include "net/http_parser.hpp"
#include "net/padding.hpp"
#include "net/socks4_request_parser.hpp"
#include "net/socks5_request_parser.hpp"
#include <build/build_config.h>
@@ -629,9 +631,9 @@ asio::error_code CliConnection::OnReadRedirHandshake(std::shared_ptr<IOBuf> buf)
asio::error_code CliConnection::OnReadSocks5MethodSelect(std::shared_ptr<IOBuf> buf) {
scoped_refptr<CliConnection> self(this);
socks5::method_select_request_parser parser;
socks5::method_select_request_parser::result_type result;
std::tie(result, std::ignore) =
method_select_request_parser_.parse(method_select_request_, buf->data(), buf->data() + buf->length());
std::tie(result, std::ignore) = parser.parse(method_select_request_, buf->data(), buf->data() + buf->length());
if (result == socks5::method_select_request_parser::good) {
DCHECK_LE(method_select_request_.length(), buf->length());
@@ -647,8 +649,9 @@ asio::error_code CliConnection::OnReadSocks5MethodSelect(std::shared_ptr<IOBuf>
asio::error_code CliConnection::OnReadSocks5Handshake(std::shared_ptr<IOBuf> buf) {
VLOG(2) << "Connection (client) " << connection_id() << " try socks5 handshake";
socks5::request_parser parser;
socks5::request_parser::result_type result;
std::tie(result, std::ignore) = request_parser_.parse(s5_request_, buf->data(), buf->data() + buf->length());
std::tie(result, std::ignore) = parser.parse(s5_request_, buf->data(), buf->data() + buf->length());
if (result == socks5::request_parser::good) {
DCHECK_LE(s5_request_.length(), buf->length());
@@ -664,9 +667,9 @@ asio::error_code CliConnection::OnReadSocks5Handshake(std::shared_ptr<IOBuf> buf
asio::error_code CliConnection::OnReadSocks4Handshake(std::shared_ptr<IOBuf> buf) {
VLOG(2) << "Connection (client) " << connection_id() << " try socks4 handshake";
socks4::request_parser parser;
socks4::request_parser::result_type result;
std::tie(result, std::ignore) = s4_request_parser_.parse(s4_request_, buf->data(), buf->data() + buf->length());
std::tie(result, std::ignore) = parser.parse(s4_request_, buf->data(), buf->data() + buf->length());
if (result == socks4::request_parser::good) {
DCHECK_LE(s4_request_.length(), buf->length());
buf->trimStart(s4_request_.length());
@@ -1036,196 +1039,26 @@ try_again:
#endif
if (upstream_https_fallback_) {
if (upstream_handshake_) {
upstream_handshake_ = false;
HttpResponseParser parser;
bool ok;
int nparsed = parser.Parse(buf, &ok);
if (nparsed) {
VLOG(3) << "Connection (client) " << connection_id()
<< " http: " << std::string(reinterpret_cast<const char*>(buf->data()), nparsed);
}
if (ok && parser.status_code() == 200) {
buf->trimStart(nparsed);
buf->retreat(nparsed);
if (buf->empty()) {
ec = asio::error::try_again;
return nullptr;
}
} else {
if (!ok) {
LOG(WARNING) << "Connection (client) " << connection_id()
<< " upstream server unhandled: " << parser.ErrorMessage() << ": "
<< std::string(reinterpret_cast<const char*>(buf->data()), nparsed);
} else {
LOG(WARNING) << "Connection (client) " << connection_id()
<< " upstream server returns: " << parser.status_code();
}
ec = asio::error::connection_refused;
disconnected(ec);
ReadUpstreamHttpsHandshake(buf, ec);
if (ec) {
return nullptr;
}
}
downstream_.push_back(buf);
} else {
auto method = absl::GetFlag(FLAGS_method).method;
if (CIPHER_METHOD_IS_SOCKS5(method) && socks5_method_select_handshake_) {
if (buf->length() < sizeof(socks5::method_select_response)) {
ec = asio::error::connection_refused;
disconnected(ec);
if (CIPHER_METHOD_IS_SOCKS5(method()) && socks5_method_select_handshake_) {
ReadUpstreamMethodSelectHandshake(buf, ec);
if (ec) {
return nullptr;
}
socks5_method_select_handshake_ = false;
auto response = reinterpret_cast<const socks5::method_select_response*>(buf->data());
if (response->ver != socks5::version && response->method != socks5::no_auth_required) {
ec = asio::error::connection_refused;
disconnected(ec);
}
if (CIPHER_METHOD_IS_SOCKS(method()) && socks_handshake_) {
ReadUpstreamSocksHandshake(buf, ec);
if (ec) {
return nullptr;
}
VLOG(2) << "Connection (client) " << connection_id() << " socks5 method select response";
buf->trimStart(sizeof(socks5::method_select_response));
{
socks5::request_header header;
header.version = socks5::version;
header.command = socks5::cmd_connect;
header.null_byte = 0;
ByteRange req(reinterpret_cast<const uint8_t*>(&header), sizeof(header));
std::shared_ptr<IOBuf> buf = IOBuf::copyBuffer(req);
absl::Span<uint8_t> address;
std::string domain_name;
uint8_t address_type = socks5::ipv4;
if (ss_request_->address_type() == ss::domain) {
address_type = socks5::domain;
domain_name = ss_request_->domain_name();
address = absl::Span<uint8_t>((uint8_t*)domain_name.c_str(), domain_name.size());
} else if (ss_request_->address_type() == ss::ipv6) {
address_type = socks5::ipv6;
address = absl::Span<uint8_t>((uint8_t*)&ss_request_->address6(), sizeof(ss_request_->address6()));
} else {
address_type = socks5::ipv4;
address = absl::Span<uint8_t>((uint8_t*)&ss_request_->address4(), sizeof(ss_request_->address4()));
}
buf->reserve(0, sizeof(address_type));
memcpy(buf->mutable_tail(), &address_type, sizeof(address_type));
buf->append(sizeof(address_type));
if (ss_request_->address_type() == ss::domain) {
uint8_t address_len = address.size();
buf->reserve(0, sizeof(address_len));
memcpy(buf->mutable_tail(), &address_len, sizeof(address_len));
buf->append(sizeof(address_len));
}
buf->reserve(0, address.size());
memcpy(buf->mutable_tail(), address.data(), address.size());
buf->append(address.size());
uint8_t port_high_byte = ss_request_->port_high_byte();
uint8_t port_low_byte = ss_request_->port_low_byte();
buf->reserve(0, sizeof(uint16_t));
*buf->mutable_tail() = port_high_byte;
buf->append(sizeof(port_high_byte));
*buf->mutable_tail() = port_low_byte;
buf->append(sizeof(port_low_byte));
upstream_.replace_front(buf);
WriteUpstreamInPipe();
}
if (buf->empty()) {
ec = asio::error::try_again;
goto out;
}
}
if (CIPHER_METHOD_IS_SOCKS(method) && socks_handshake_) {
switch (method) {
case CRYPTO_SOCKS4:
case CRYPTO_SOCKS4A: {
if (buf->length() < sizeof(socks4::reply_header)) {
ec = asio::error::connection_refused;
disconnected(ec);
return nullptr;
}
socks_handshake_ = false;
auto response = reinterpret_cast<const socks4::reply_header*>(buf->data());
if (response->null_byte != 0 || response->status != socks4::reply::request_granted) {
ec = asio::error::connection_refused;
disconnected(ec);
return nullptr;
}
VLOG(2) << "Connection (client) " << connection_id() << " socks4 handshake response";
buf->trimStart(sizeof(socks4::reply_header));
break;
};
case CRYPTO_SOCKS5:
case CRYPTO_SOCKS5H: {
if (buf->length() < sizeof(socks5::reply_header)) {
ec = asio::error::connection_refused;
disconnected(ec);
return nullptr;
}
socks_handshake_ = false;
auto response = reinterpret_cast<const socks5::reply_header*>(buf->data());
if (response->version != socks5::version || response->status != socks5::reply::request_granted ||
response->null_byte != 0) {
ec = asio::error::connection_refused;
disconnected(ec);
return nullptr;
}
if (response->address_type == socks5::ipv4) {
uint32_t expected_len =
sizeof(socks5::reply_header) + sizeof(asio::ip::address_v4::bytes_type) + sizeof(uint16_t);
if (buf->length() < expected_len) {
ec = asio::error::connection_refused;
disconnected(ec);
return nullptr;
}
buf->trimStart(expected_len);
} else if (response->address_type == socks5::ipv6) {
uint32_t expected_len =
sizeof(socks5::reply_header) + sizeof(asio::ip::address_v6::bytes_type) + sizeof(uint16_t);
if (buf->length() < expected_len) {
ec = asio::error::connection_refused;
disconnected(ec);
return nullptr;
}
buf->trimStart(expected_len);
} else if (response->address_type == socks5::domain) {
uint32_t expected_len = sizeof(socks5::reply_header) + sizeof(uint8_t) + sizeof(uint16_t);
if (buf->length() < expected_len) {
ec = asio::error::connection_refused;
disconnected(ec);
return nullptr;
}
expected_len += *reinterpret_cast<const uint8_t*>(buf->data() + sizeof(*response));
if (buf->length() < expected_len) {
ec = asio::error::connection_refused;
disconnected(ec);
return nullptr;
}
buf->trimStart(expected_len);
} else {
ec = asio::error::connection_refused;
disconnected(ec);
return nullptr;
}
VLOG(2) << "Connection (client) " << connection_id() << " socks5 handshake response";
break;
};
default:
break;
}
if (buf->empty()) {
goto out;
}
}
if (CIPHER_METHOD_IS_SOCKS(method)) {
if (CIPHER_METHOD_IS_SOCKS(method())) {
downstream_.push_back(buf);
} else {
decoder_->process_bytes(buf);
@@ -1252,6 +1085,202 @@ out:
return downstream_.front();
}
void CliConnection::ReadUpstreamHttpsHandshake(std::shared_ptr<IOBuf> buf, asio::error_code& ec) {
DCHECK(upstream_handshake_);
upstream_handshake_ = false;
HttpResponseParser parser;
bool ok;
int nparsed = parser.Parse(buf, &ok);
if (nparsed) {
VLOG(3) << "Connection (client) " << connection_id()
<< " http: " << std::string(reinterpret_cast<const char*>(buf->data()), nparsed);
}
if (ok && parser.status_code() == 200) {
buf->trimStart(nparsed);
buf->retreat(nparsed);
} else {
if (!ok) {
LOG(WARNING) << "Connection (client) " << connection_id()
<< " upstream server unhandled: " << parser.ErrorMessage() << ": "
<< std::string(reinterpret_cast<const char*>(buf->data()), nparsed);
} else {
LOG(WARNING) << "Connection (client) " << connection_id() << " upstream server returns: " << parser.status_code();
}
ec = asio::error::connection_refused;
disconnected(ec);
return;
}
if (buf->empty()) {
ec = asio::error::try_again;
return;
}
}
void CliConnection::ReadUpstreamMethodSelectHandshake(std::shared_ptr<IOBuf> buf, asio::error_code& ec) {
DCHECK(socks5_method_select_handshake_);
socks5_method_select_handshake_ = false;
auto response = reinterpret_cast<const socks5::method_select_response*>(buf->data());
if (buf->length() < sizeof(socks5::method_select_response)) {
goto err_out;
}
if (response->ver != socks5::version && response->method != socks5::no_auth_required) {
goto err_out;
}
VLOG(2) << "Connection (client) " << connection_id() << " upstream socks5 method select response";
buf->trimStart(sizeof(socks5::method_select_response));
buf->retreat(sizeof(socks5::method_select_response));
WriteUpstreamMethodSelectResponse();
if (buf->empty()) {
ec = asio::error::try_again;
return;
}
return;
err_out:
LOG(WARNING) << "Connection (client) " << connection_id() << " malformed upstream method select handshake response";
ec = asio::error::connection_refused;
disconnected(ec);
return;
}
void CliConnection::WriteUpstreamMethodSelectResponse() {
socks5::request_header header;
header.version = socks5::version;
header.command = socks5::cmd_connect;
header.null_byte = 0;
ByteRange req(reinterpret_cast<const uint8_t*>(&header), sizeof(header));
std::shared_ptr<IOBuf> buf = IOBuf::copyBuffer(req);
absl::Span<uint8_t> address;
std::string domain_name;
uint8_t address_type = socks5::ipv4;
if (ss_request_->address_type() == ss::domain) {
address_type = socks5::domain;
domain_name = ss_request_->domain_name();
address = absl::Span<uint8_t>((uint8_t*)domain_name.c_str(), domain_name.size());
} else if (ss_request_->address_type() == ss::ipv6) {
address_type = socks5::ipv6;
address = absl::Span<uint8_t>((uint8_t*)&ss_request_->address6(), sizeof(ss_request_->address6()));
} else {
address_type = socks5::ipv4;
address = absl::Span<uint8_t>((uint8_t*)&ss_request_->address4(), sizeof(ss_request_->address4()));
}
buf->reserve(0, sizeof(address_type));
memcpy(buf->mutable_tail(), &address_type, sizeof(address_type));
buf->append(sizeof(address_type));
if (ss_request_->address_type() == ss::domain) {
uint8_t address_len = address.size();
buf->reserve(0, sizeof(address_len));
memcpy(buf->mutable_tail(), &address_len, sizeof(address_len));
buf->append(sizeof(address_len));
}
buf->reserve(0, address.size());
memcpy(buf->mutable_tail(), address.data(), address.size());
buf->append(address.size());
uint8_t port_high_byte = ss_request_->port_high_byte();
uint8_t port_low_byte = ss_request_->port_low_byte();
buf->reserve(0, sizeof(uint16_t));
*buf->mutable_tail() = port_high_byte;
buf->append(sizeof(port_high_byte));
*buf->mutable_tail() = port_low_byte;
buf->append(sizeof(port_low_byte));
upstream_.replace_front(buf);
WriteUpstreamInPipe();
}
void CliConnection::ReadUpstreamSocksHandshake(std::shared_ptr<IOBuf> buf, asio::error_code& ec) {
DCHECK(socks_handshake_);
socks_handshake_ = false;
switch (method()) {
case CRYPTO_SOCKS4:
case CRYPTO_SOCKS4A: {
if (buf->length() < sizeof(socks4::reply_header)) {
goto err_out;
}
auto response = reinterpret_cast<const socks4::reply_header*>(buf->data());
if (response->null_byte != 0 || response->status != socks4::reply::request_granted) {
goto err_out;
}
VLOG(2) << "Connection (client) " << connection_id() << " upstream socks4 handshake response";
buf->trimStart(sizeof(socks4::reply_header));
buf->retreat(sizeof(socks4::reply_header));
break;
};
case CRYPTO_SOCKS5:
case CRYPTO_SOCKS5H: {
if (buf->length() < sizeof(socks5::reply_header)) {
goto err_out;
}
auto response = reinterpret_cast<const socks5::reply_header*>(buf->data());
if (response->version != socks5::version || response->status != socks5::reply::request_granted ||
response->null_byte != 0) {
goto err_out;
}
if (response->address_type == socks5::ipv4) {
uint32_t expected_len =
sizeof(socks5::reply_header) + sizeof(asio::ip::address_v4::bytes_type) + sizeof(uint16_t);
if (buf->length() < expected_len) {
goto err_out;
}
buf->trimStart(expected_len);
buf->retreat(expected_len);
} else if (response->address_type == socks5::ipv6) {
uint32_t expected_len =
sizeof(socks5::reply_header) + sizeof(asio::ip::address_v6::bytes_type) + sizeof(uint16_t);
if (buf->length() < expected_len) {
goto err_out;
}
buf->trimStart(expected_len);
buf->retreat(expected_len);
} else if (response->address_type == socks5::domain) {
uint32_t expected_len = sizeof(socks5::reply_header) + sizeof(uint8_t) + sizeof(uint16_t);
if (buf->length() < expected_len) {
goto err_out;
}
expected_len += *reinterpret_cast<const uint8_t*>(buf->data() + sizeof(*response));
if (buf->length() < expected_len) {
goto err_out;
}
buf->trimStart(expected_len);
buf->retreat(expected_len);
} else {
goto err_out;
return;
}
VLOG(2) << "Connection (client) " << connection_id() << " upstream socks5 handshake response";
break;
};
default:
CHECK(false);
break;
}
if (buf->empty()) {
ec = asio::error::try_again;
return;
}
return;
err_out:
LOG(WARNING) << "Connection (client) " << connection_id() << " malformed upstream socks handshake response";
ec = asio::error::connection_refused;
disconnected(ec);
return;
}
void CliConnection::WriteUpstreamInPipe() {
asio::error_code ec;
size_t bytes_transferred = 0U, wbytes_transferred = 0U;
@@ -1339,8 +1368,7 @@ void CliConnection::WriteUpstreamInPipe() {
std::shared_ptr<IOBuf> CliConnection::GetNextUpstreamBuf(asio::error_code& ec, size_t* bytes_transferred) {
if (!upstream_.empty()) {
// pending on upstream handshake
auto method = absl::GetFlag(FLAGS_method).method;
if (CIPHER_METHOD_IS_SOCKS5(method) && socks5_method_select_handshake_ && upstream_.front()->empty()) {
if (CIPHER_METHOD_IS_SOCKS5(method()) && socks5_method_select_handshake_ && upstream_.front()->empty()) {
ec = asio::error::try_again;
return nullptr;
}
@@ -1404,8 +1432,7 @@ std::shared_ptr<IOBuf> CliConnection::GetNextUpstreamBuf(asio::error_code& ec, s
if (upstream_https_fallback_) {
upstream_.push_back(buf);
} else {
auto method = absl::GetFlag(FLAGS_method).method;
if (CIPHER_METHOD_IS_SOCKS(method)) {
if (CIPHER_METHOD_IS_SOCKS(method())) {
upstream_.push_back(buf);
} else {
EncryptData(&upstream_, buf);
@@ -1622,8 +1649,7 @@ void CliConnection::OnCmdConnect(const asio::ip::tcp::endpoint& endpoint) {
void CliConnection::OnCmdConnect(const std::string& domain_name, uint16_t port) {
DCHECK_LE(domain_name.size(), (unsigned int)TLSEXT_MAXLEN_host_name);
auto method = absl::GetFlag(FLAGS_method).method;
if (CIPHER_METHOD_IS_SOCKS_NON_DOMAIN_NAME(method)) {
if (CIPHER_METHOD_IS_SOCKS_NON_DOMAIN_NAME(method())) {
scoped_refptr<CliConnection> self(this);
resolver_.AsyncResolve(domain_name, port,
[this, self](const asio::error_code& ec, asio::ip::tcp::resolver::results_type results) {
@@ -1704,8 +1730,7 @@ void CliConnection::OnStreamRead(std::shared_ptr<IOBuf> buf) {
if (upstream_https_fallback_) {
upstream_.push_back(buf);
} else {
auto method = absl::GetFlag(FLAGS_method).method;
if (CIPHER_METHOD_IS_SOCKS(method)) {
if (CIPHER_METHOD_IS_SOCKS(method())) {
upstream_.push_back(buf);
} else {
EncryptData(&upstream_, buf);
@@ -1793,8 +1818,7 @@ void CliConnection::connected() {
VLOG(2) << "Connection (client) " << connection_id()
<< " remote: established upstream connection with: " << remote_domain();
const auto method = absl::GetFlag(FLAGS_method).method;
bool http2 = CIPHER_METHOD_IS_HTTP2(method);
bool http2 = CIPHER_METHOD_IS_HTTP2(method());
if (http2 && channel_->https_fallback()) {
http2 = false;
upstream_https_fallback_ = true;
@@ -1819,11 +1843,9 @@ void CliConnection::connected() {
// padding_support_ = absl::GetFlag(FLAGS_padding_support);
} else {
DCHECK(!http2);
auto method = absl::GetFlag(FLAGS_method).method;
if (!CIPHER_METHOD_IS_SOCKS(method)) {
encoder_ =
std::make_unique<cipher>("", absl::GetFlag(FLAGS_password), absl::GetFlag(FLAGS_method).method, this, true);
decoder_ = std::make_unique<cipher>("", absl::GetFlag(FLAGS_password), absl::GetFlag(FLAGS_method).method, this);
if (!CIPHER_METHOD_IS_SOCKS(method())) {
encoder_ = std::make_unique<cipher>("", absl::GetFlag(FLAGS_password), method(), this, true);
decoder_ = std::make_unique<cipher>("", absl::GetFlag(FLAGS_password), method(), this);
}
}
@@ -1880,7 +1902,7 @@ void CliConnection::connected() {
FillNonindexHeaderValue(RandUint64(), &padding[0], padding.size());
headers.emplace_back("padding"s, padding);
}
stream_id_ = adapter_->SubmitRequest(GenerateHeaders(headers), std::move(data_frame), nullptr);
stream_id_ = adapter_->SubmitRequest(GenerateHeaders(headers), std::move(data_frame), false, nullptr);
data_frame_->set_stream_id(stream_id_);
SendIfNotProcessing();
} else
@@ -1915,9 +1937,8 @@ void CliConnection::connected() {
// write variable address directly as https header
upstream_.push_back(hdr.data(), hdr.size());
} else {
auto method = absl::GetFlag(FLAGS_method).method;
if (CIPHER_METHOD_IS_SOCKS(method)) {
switch (method) {
if (CIPHER_METHOD_IS_SOCKS(method())) {
switch (method()) {
case CRYPTO_SOCKS4: {
socks4::request_header header;
header.version = socks4::version;
+7 -8
View File
@@ -17,10 +17,8 @@
#include "net/resolver.hpp"
#include "net/socks4.hpp"
#include "net/socks4_request.hpp"
#include "net/socks4_request_parser.hpp"
#include "net/socks5.hpp"
#include "net/socks5_request.hpp"
#include "net/socks5_request_parser.hpp"
#include "net/ss_request.hpp"
#include "net/ssl_stream.hpp"
#include "net/stream.hpp"
@@ -327,13 +325,10 @@ class CliConnection : public RefCountedThreadSafe<CliConnection>,
/// state machine
state state_;
/// parser of method select request
socks5::method_select_request_parser method_select_request_parser_;
private:
/// copy of method select request
socks5::method_select_request method_select_request_;
/// parser of handshake request
socks5::request_parser request_parser_;
/// copy of handshake request
socks5::request s5_request_;
@@ -342,8 +337,6 @@ class CliConnection : public RefCountedThreadSafe<CliConnection>,
/// copy of handshake response
socks5::reply s5_reply_;
/// parser of handshake request
socks4::request_parser s4_request_parser_;
/// copy of handshake request
socks4::request s4_request_;
@@ -367,6 +360,12 @@ class CliConnection : public RefCountedThreadSafe<CliConnection>,
int num_padding_recv_ = 0;
std::shared_ptr<IOBuf> padding_in_middle_buf_;
private:
void ReadUpstreamHttpsHandshake(std::shared_ptr<IOBuf> buf, asio::error_code& ec);
void ReadUpstreamMethodSelectHandshake(std::shared_ptr<IOBuf> buf, asio::error_code& ec);
void WriteUpstreamMethodSelectResponse();
void ReadUpstreamSocksHandshake(std::shared_ptr<IOBuf> buf, asio::error_code& ec);
/// the state of https fallback handshake (upstream)
bool upstream_handshake_ = true;
/// the state of socks5 method select handshake (upstream)
+6
View File
@@ -295,6 +295,12 @@ class Connection {
private:
/// the callback invoked when disconnect event happens
absl::AnyInvocable<void()> disconnect_cb_;
public:
cipher_method method() const { return method_; }
private:
cipher_method method_ = absl::GetFlag(FLAGS_method).method;
};
enum ConnectionFactoryType {
+17 -20
View File
@@ -21,6 +21,7 @@
#include "net/socks5.hpp"
#include "net/socks5_request.hpp"
#include "net/socks5_request_parser.hpp"
#include "net/ss_request_parser.hpp"
#include "version.h"
ABSL_FLAG(bool, hide_via, true, "If true, the Via heaeder will not be added.");
@@ -206,8 +207,7 @@ void ServerConnection::close() {
}
void ServerConnection::Start() {
const auto method = absl::GetFlag(FLAGS_method).method;
bool http2 = CIPHER_METHOD_IS_HTTP2(method);
bool http2 = CIPHER_METHOD_IS_HTTP2(method());
if (http2 && downlink_->https_fallback()) {
http2 = false;
}
@@ -245,13 +245,11 @@ void ServerConnection::Start() {
ReadHandshakeViaHttps();
} else {
DCHECK(!http2);
auto method = absl::GetFlag(FLAGS_method).method;
if (CIPHER_METHOD_IS_SOCKS(method)) {
if (CIPHER_METHOD_IS_SOCKS(method())) {
ReadHandshakeViaSocks();
} else {
encoder_ =
std::make_unique<cipher>("", absl::GetFlag(FLAGS_password), absl::GetFlag(FLAGS_method).method, this, true);
decoder_ = std::make_unique<cipher>("", absl::GetFlag(FLAGS_password), absl::GetFlag(FLAGS_method).method, this);
encoder_ = std::make_unique<cipher>("", absl::GetFlag(FLAGS_password), method(), this, true);
decoder_ = std::make_unique<cipher>("", absl::GetFlag(FLAGS_password), method(), this);
ReadHandshake();
}
}
@@ -534,8 +532,9 @@ void ServerConnection::ReadHandshake() {
DumpHex("HANDSHAKE->", buf.get());
ss::request_parser parser;
ss::request_parser::result_type result;
std::tie(result, std::ignore) = request_parser_.parse(request_, buf->data(), buf->data() + bytes_transferred);
std::tie(result, std::ignore) = parser.parse(request_, buf->data(), buf->data() + bytes_transferred);
if (result == ss::request_parser::good) {
buf->trimStart(request_.length());
@@ -700,8 +699,7 @@ void ServerConnection::OnReadHandshakeViaSocks() {
}
buf->append(bytes_transferred);
auto method = absl::GetFlag(FLAGS_method).method;
switch (method) {
switch (method()) {
case CRYPTO_SOCKS4:
case CRYPTO_SOCKS4A: {
socks4::request_parser request_parser;
@@ -757,12 +755,14 @@ void ServerConnection::OnReadHandshakeViaSocks() {
break;
};
default:
CHECK(false);
break;
}
}
void ServerConnection::WriteHandshakeResponse() {
scoped_refptr<ServerConnection> self(this);
DCHECK(CIPHER_METHOD_IS_SOCKS(method()));
downlink_->async_write_some([this, self](asio::error_code ec) {
if (closed_ || closing_) {
@@ -776,9 +776,8 @@ void ServerConnection::WriteHandshakeResponse() {
return;
}
std::shared_ptr<IOBuf> buf;
auto method = absl::GetFlag(FLAGS_method).method;
DCHECK(CIPHER_METHOD_IS_SOCKS(method));
if (method == CRYPTO_SOCKS4 || method == CRYPTO_SOCKS4A) {
DCHECK(CIPHER_METHOD_IS_SOCKS(method()));
if (method() == CRYPTO_SOCKS4 || method() == CRYPTO_SOCKS4A) {
socks4::reply reply;
asio::ip::tcp::endpoint endpoint{asio::ip::tcp::v4(), 0};
reply.set_endpoint(endpoint);
@@ -895,8 +894,7 @@ void ServerConnection::OnReadHandshakeViaSocks5() {
buf->append(bytes_transferred);
}
auto method = absl::GetFlag(FLAGS_method).method;
DCHECK(CIPHER_METHOD_IS_SOCKS5(method));
DCHECK(CIPHER_METHOD_IS_SOCKS5(method()));
socks5::request_parser request_parser;
socks5::request request;
@@ -1144,8 +1142,7 @@ std::shared_ptr<IOBuf> ServerConnection::GetNextDownstreamBuf(asio::error_code&
if (downlink_->https_fallback()) {
downstream_.push_back(buf);
} else {
const auto method = absl::GetFlag(FLAGS_method).method;
if (CIPHER_METHOD_IS_SOCKS(method)) {
if (CIPHER_METHOD_IS_SOCKS(method())) {
downstream_.push_back(buf);
} else {
EncryptData(&downstream_, buf);
@@ -1316,8 +1313,7 @@ try_again:
if (downlink_->https_fallback()) {
upstream_.push_back(buf);
} else {
const auto method = absl::GetFlag(FLAGS_method).method;
if (CIPHER_METHOD_IS_SOCKS(method)) {
if (CIPHER_METHOD_IS_SOCKS(method())) {
upstream_.push_back(buf);
} else {
decoder_->process_bytes(buf);
@@ -1465,7 +1461,8 @@ void ServerConnection::OnConnect() {
}
headers.emplace_back("padding"s, padding);
}
int submit_result = adapter_->SubmitResponse(stream_id_, GenerateHeaders(headers, 200), std::move(data_frame));
int submit_result =
adapter_->SubmitResponse(stream_id_, GenerateHeaders(headers, 200), std::move(data_frame), false);
SendIfNotProcessing();
if (submit_result != 0) {
OnDisconnect(asio::error::connection_aborted);
-3
View File
@@ -15,7 +15,6 @@
#include "net/protocol.hpp"
#include "net/ss.hpp"
#include "net/ss_request.hpp"
#include "net/ss_request_parser.hpp"
#include "net/ssl_stream.hpp"
#include "net/stream.hpp"
@@ -285,8 +284,6 @@ class ServerConnection : public RefCountedThreadSafe<ServerConnection>,
/// state machine
state state_;
/// parser of handshake request
ss::request_parser request_parser_;
/// copy of handshake request
ss::request request_;
+7
View File
@@ -66,6 +66,7 @@ set(ABSL_INTERNAL_DLL_FILES
"cleanup/internal/cleanup.h"
"container/btree_map.h"
"container/btree_set.h"
"container/hash_container_defaults.h"
"container/fixed_array.h"
"container/flat_hash_map.h"
"container/flat_hash_set.h"
@@ -123,6 +124,8 @@ set(ABSL_INTERNAL_DLL_FILES
"debugging/internal/address_is_readable.h"
"debugging/internal/demangle.cc"
"debugging/internal/demangle.h"
"debugging/internal/demangle_rust.cc"
"debugging/internal/demangle_rust.h"
"debugging/internal/elf_mem_image.cc"
"debugging/internal/elf_mem_image.h"
"debugging/internal/examine_stack.cc"
@@ -608,6 +611,9 @@ set(ABSL_INTERNAL_TEST_DLL_FILES
"random/internal/mock_overload_set.h"
"random/mocking_bit_gen.h"
"random/mock_distributions.h"
"status/status_matchers.h"
"status/internal/status_matchers.cc"
"status/internal/status_matchers.h"
"strings/cordz_test_helpers.h"
"strings/cord_test_helpers.h"
)
@@ -620,6 +626,7 @@ set(ABSL_INTERNAL_TEST_DLL_TARGETS
"random_internal_distribution_test_util"
"random_internal_mock_overload_set"
"scoped_mock_log"
"status_matchers"
)
include(CheckCXXSourceCompiles)
+5
View File
@@ -702,6 +702,11 @@
_Pragma("GCC diagnostic ignored \"-Wdeprecated-declarations\"")
#define ABSL_INTERNAL_RESTORE_DEPRECATED_DECLARATION_WARNING \
_Pragma("GCC diagnostic pop")
#elif defined(_MSC_VER)
#define ABSL_INTERNAL_DISABLE_DEPRECATED_DECLARATION_WARNING \
_Pragma("warning(push)") _Pragma("warning(disable: 4996)")
#define ABSL_INTERNAL_RESTORE_DEPRECATED_DECLARATION_WARNING \
_Pragma("warning(pop)")
#else
#define ABSL_INTERNAL_DISABLE_DEPRECATED_DECLARATION_WARNING
#define ABSL_INTERNAL_RESTORE_DEPRECATED_DECLARATION_WARNING
+3 -2
View File
@@ -89,7 +89,8 @@ class ABSL_LOCKABLE ABSL_ATTRIBUTE_WARN_UNUSED SpinLock {
// acquisition was successful. If the lock was not acquired, false is
// returned. If this SpinLock is free at the time of the call, TryLock
// will return true with high probability.
inline bool TryLock() ABSL_EXCLUSIVE_TRYLOCK_FUNCTION(true) {
ABSL_MUST_USE_RESULT inline bool TryLock()
ABSL_EXCLUSIVE_TRYLOCK_FUNCTION(true) {
ABSL_TSAN_MUTEX_PRE_LOCK(this, __tsan_mutex_try_lock);
bool res = TryLockImpl();
ABSL_TSAN_MUTEX_POST_LOCK(
@@ -120,7 +121,7 @@ class ABSL_LOCKABLE ABSL_ATTRIBUTE_WARN_UNUSED SpinLock {
// Determine if the lock is held. When the lock is held by the invoking
// thread, true will always be returned. Intended to be used as
// CHECK(lock.IsHeld()).
inline bool IsHeld() const {
ABSL_MUST_USE_RESULT inline bool IsHeld() const {
return (lockword_.load(std::memory_order_relaxed) & kSpinLockHeld) != 0;
}
+16 -4
View File
@@ -126,6 +126,7 @@ cc_library(
linkopts = ABSL_DEFAULT_LINKOPTS,
deps = [
":compressed_tuple",
"//absl/base:base_internal",
"//absl/base:config",
"//absl/base:core_headers",
"//absl/memory",
@@ -247,7 +248,7 @@ cc_library(
linkopts = ABSL_DEFAULT_LINKOPTS,
deps = [
":container_memory",
":hash_function_defaults",
":hash_container_defaults",
":raw_hash_map",
"//absl/algorithm:container",
"//absl/base:core_headers",
@@ -284,7 +285,7 @@ cc_library(
linkopts = ABSL_DEFAULT_LINKOPTS,
deps = [
":container_memory",
":hash_function_defaults",
":hash_container_defaults",
":raw_hash_set",
"//absl/algorithm:container",
"//absl/base:core_headers",
@@ -323,7 +324,7 @@ cc_library(
linkopts = ABSL_DEFAULT_LINKOPTS,
deps = [
":container_memory",
":hash_function_defaults",
":hash_container_defaults",
":node_slot_policy",
":raw_hash_map",
"//absl/algorithm:container",
@@ -358,7 +359,7 @@ cc_library(
linkopts = ABSL_DEFAULT_LINKOPTS,
deps = [
":container_memory",
":hash_function_defaults",
":hash_container_defaults",
":node_slot_policy",
":raw_hash_set",
"//absl/algorithm:container",
@@ -432,6 +433,17 @@ cc_library(
],
)
cc_library(
name = "hash_container_defaults",
hdrs = ["hash_container_defaults.h"],
copts = ABSL_DEFAULT_COPTS,
linkopts = ABSL_DEFAULT_LINKOPTS,
deps = [
":hash_function_defaults",
"//absl/base:config",
],
)
cc_test(
name = "hash_function_defaults_test",
srcs = ["internal/hash_function_defaults_test.cc"],
+18 -4
View File
@@ -176,6 +176,7 @@ absl_cc_library(
COPTS
${ABSL_DEFAULT_COPTS}
DEPS
absl::base_internal
absl::compressed_tuple
absl::config
absl::core_headers
@@ -288,7 +289,7 @@ absl_cc_library(
DEPS
absl::container_memory
absl::core_headers
absl::hash_function_defaults
absl::hash_container_defaults
absl::raw_hash_map
absl::algorithm_container
absl::memory
@@ -325,7 +326,7 @@ absl_cc_library(
${ABSL_DEFAULT_COPTS}
DEPS
absl::container_memory
absl::hash_function_defaults
absl::hash_container_defaults
absl::raw_hash_set
absl::algorithm_container
absl::core_headers
@@ -367,7 +368,7 @@ absl_cc_library(
DEPS
absl::container_memory
absl::core_headers
absl::hash_function_defaults
absl::hash_container_defaults
absl::node_slot_policy
absl::raw_hash_map
absl::algorithm_container
@@ -403,7 +404,7 @@ absl_cc_library(
DEPS
absl::container_memory
absl::core_headers
absl::hash_function_defaults
absl::hash_container_defaults
absl::node_slot_policy
absl::raw_hash_set
absl::algorithm_container
@@ -429,6 +430,19 @@ absl_cc_test(
GTest::gmock_main
)
absl_cc_library(
NAME
hash_container_defaults
HDRS
"hash_container_defaults.h"
COPTS
${ABSL_DEFAULT_COPTS}
DEPS
absl::config
absl::hash_function_defaults
PUBLIC
)
# Internal-only target, do not depend on directly.
absl_cc_library(
NAME
+4 -6
View File
@@ -31,16 +31,15 @@
#define ABSL_CONTAINER_FLAT_HASH_MAP_H_
#include <cstddef>
#include <new>
#include <memory>
#include <type_traits>
#include <utility>
#include "absl/algorithm/container.h"
#include "absl/base/macros.h"
#include "absl/container/hash_container_defaults.h"
#include "absl/container/internal/container_memory.h"
#include "absl/container/internal/hash_function_defaults.h" // IWYU pragma: export
#include "absl/container/internal/raw_hash_map.h" // IWYU pragma: export
#include "absl/memory/memory.h"
namespace absl {
ABSL_NAMESPACE_BEGIN
@@ -119,9 +118,8 @@ struct FlatHashMapPolicy;
// if (result != ducks.end()) {
// std::cout << "Result: " << result->second << std::endl;
// }
template <class K, class V,
class Hash = absl::container_internal::hash_default_hash<K>,
class Eq = absl::container_internal::hash_default_eq<K>,
template <class K, class V, class Hash = DefaultHashContainerHash<K>,
class Eq = DefaultHashContainerEq<K>,
class Allocator = std::allocator<std::pair<const K, V>>>
class flat_hash_map : public absl::container_internal::raw_hash_map<
absl::container_internal::FlatHashMapPolicy<K, V>,
+3 -3
View File
@@ -36,8 +36,8 @@
#include "absl/algorithm/container.h"
#include "absl/base/macros.h"
#include "absl/container/hash_container_defaults.h"
#include "absl/container/internal/container_memory.h"
#include "absl/container/internal/hash_function_defaults.h" // IWYU pragma: export
#include "absl/container/internal/raw_hash_set.h" // IWYU pragma: export
#include "absl/memory/memory.h"
@@ -114,8 +114,8 @@ struct FlatHashSetPolicy;
// if (ducks.contains("dewey")) {
// std::cout << "We found dewey!" << std::endl;
// }
template <class T, class Hash = absl::container_internal::hash_default_hash<T>,
class Eq = absl::container_internal::hash_default_eq<T>,
template <class T, class Hash = DefaultHashContainerHash<T>,
class Eq = DefaultHashContainerEq<T>,
class Allocator = std::allocator<T>>
class flat_hash_set
: public absl::container_internal::raw_hash_set<
@@ -0,0 +1,45 @@
// Copyright 2024 The Abseil Authors
//
// 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
//
// https://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.
#ifndef ABSL_CONTAINER_HASH_CONTAINER_DEFAULTS_H_
#define ABSL_CONTAINER_HASH_CONTAINER_DEFAULTS_H_
#include "absl/base/config.h"
#include "absl/container/internal/hash_function_defaults.h"
namespace absl {
ABSL_NAMESPACE_BEGIN
// DefaultHashContainerHash is a convenience alias for the functor that is used
// by default by Abseil hash-based (unordered) containers for hashing when
// `Hash` type argument is not explicitly specified.
//
// This type alias can be used by generic code that wants to provide more
// flexibility for defining underlying containers.
template <typename T>
using DefaultHashContainerHash = absl::container_internal::hash_default_hash<T>;
// DefaultHashContainerEq is a convenience alias for the functor that is used by
// default by Abseil hash-based (unordered) containers for equality check when
// `Eq` type argument is not explicitly specified.
//
// This type alias can be used by generic code that wants to provide more
// flexibility for defining underlying containers.
template <typename T>
using DefaultHashContainerEq = absl::container_internal::hash_default_eq<T>;
ABSL_NAMESPACE_END
} // namespace absl
#endif // ABSL_CONTAINER_HASH_CONTAINER_DEFAULTS_H_
@@ -27,6 +27,7 @@
#include "absl/base/attributes.h"
#include "absl/base/config.h"
#include "absl/base/internal/identity.h"
#include "absl/base/macros.h"
#include "absl/container/internal/compressed_tuple.h"
#include "absl/memory/memory.h"
@@ -82,16 +83,6 @@ using IsMoveAssignOk = std::is_move_assignable<ValueType<A>>;
template <typename A>
using IsSwapOk = absl::type_traits_internal::IsSwappable<ValueType<A>>;
template <typename T>
struct TypeIdentity {
using type = T;
};
// Used for function arguments in template functions to prevent ADL by forcing
// callers to explicitly specify the template parameter.
template <typename T>
using NoTypeDeduction = typename TypeIdentity<T>::type;
template <typename A, bool IsTriviallyDestructible =
absl::is_trivially_destructible<ValueType<A>>::value>
struct DestroyAdapter;
@@ -139,7 +130,7 @@ struct MallocAdapter {
};
template <typename A, typename ValueAdapter>
void ConstructElements(NoTypeDeduction<A>& allocator,
void ConstructElements(absl::internal::type_identity_t<A>& allocator,
Pointer<A> construct_first, ValueAdapter& values,
SizeType<A> construct_size) {
for (SizeType<A> i = 0; i < construct_size; ++i) {
+4 -6
View File
@@ -37,14 +37,13 @@
#define ABSL_CONTAINER_NODE_HASH_MAP_H_
#include <cstddef>
#include <tuple>
#include <memory>
#include <type_traits>
#include <utility>
#include "absl/algorithm/container.h"
#include "absl/base/macros.h"
#include "absl/container/hash_container_defaults.h"
#include "absl/container/internal/container_memory.h"
#include "absl/container/internal/hash_function_defaults.h" // IWYU pragma: export
#include "absl/container/internal/node_slot_policy.h"
#include "absl/container/internal/raw_hash_map.h" // IWYU pragma: export
#include "absl/memory/memory.h"
@@ -114,9 +113,8 @@ class NodeHashMapPolicy;
// if (result != ducks.end()) {
// std::cout << "Result: " << result->second << std::endl;
// }
template <class Key, class Value,
class Hash = absl::container_internal::hash_default_hash<Key>,
class Eq = absl::container_internal::hash_default_eq<Key>,
template <class Key, class Value, class Hash = DefaultHashContainerHash<Key>,
class Eq = DefaultHashContainerEq<Key>,
class Alloc = std::allocator<std::pair<const Key, Value>>>
class node_hash_map
: public absl::container_internal::raw_hash_map<
+4 -5
View File
@@ -36,12 +36,12 @@
#define ABSL_CONTAINER_NODE_HASH_SET_H_
#include <cstddef>
#include <memory>
#include <type_traits>
#include "absl/algorithm/container.h"
#include "absl/base/macros.h"
#include "absl/container/hash_container_defaults.h"
#include "absl/container/internal/container_memory.h"
#include "absl/container/internal/hash_function_defaults.h" // IWYU pragma: export
#include "absl/container/internal/node_slot_policy.h"
#include "absl/container/internal/raw_hash_set.h" // IWYU pragma: export
#include "absl/memory/memory.h"
@@ -109,9 +109,8 @@ struct NodeHashSetPolicy;
// if (ducks.contains("dewey")) {
// std::cout << "We found dewey!" << std::endl;
// }
template <class T, class Hash = absl::container_internal::hash_default_hash<T>,
class Eq = absl::container_internal::hash_default_eq<T>,
class Alloc = std::allocator<T>>
template <class T, class Hash = DefaultHashContainerHash<T>,
class Eq = DefaultHashContainerEq<T>, class Alloc = std::allocator<T>>
class node_hash_set
: public absl::container_internal::raw_hash_set<
absl::container_internal::NodeHashSetPolicy<T>, Hash, Eq, Alloc> {
@@ -123,8 +123,8 @@ uint64_t V128_Extract64(const V128 l);
// Extracts the low 64 bits from V128.
int64_t V128_Low64(const V128 l);
// Left-shifts packed 64-bit integers in l by r.
V128 V128_ShiftLeft64(const V128 l, const V128 r);
// Add packed 64-bit integers in |l| and |r|.
V128 V128_Add64(const V128 l, const V128 r);
#endif
@@ -193,8 +193,8 @@ inline uint64_t V128_Extract64(const V128 l) {
inline int64_t V128_Low64(const V128 l) { return _mm_cvtsi128_si64(l); }
inline V128 V128_ShiftLeft64(const V128 l, const V128 r) {
return _mm_sll_epi64(l, r);
inline V128 V128_Add64(const V128 l, const V128 r) {
return _mm_add_epi64(l, r);
}
#elif defined(ABSL_CRC_INTERNAL_HAVE_ARM_SIMD)
@@ -289,9 +289,7 @@ inline int64_t V128_Low64(const V128 l) {
return vgetq_lane_s64(vreinterpretq_s64_u64(l), 0);
}
inline V128 V128_ShiftLeft64(const V128 l, const V128 r) {
return vshlq_u64(l, vreinterpretq_s64_u64(r));
}
inline V128 V128_Add64(const V128 l, const V128 r) { return vaddq_u64(l, r); }
#endif
@@ -130,8 +130,8 @@ ABSL_ATTRIBUTE_NO_SANITIZE_UNDEFINED inline void LargeTailCopy(
size_t data_index = i * kIntLoadsPerVec + j;
int_data[data_index] = *(usrc + j);
crcs[region] = crc32c_t{static_cast<uint32_t>(CRC32_u64(
static_cast<uint32_t>(crcs[region]), int_data[data_index]))};
crcs[region] = crc32c_t{CRC32_u64(static_cast<uint32_t>(crcs[region]),
int_data[data_index])};
*(udst + j) = int_data[data_index];
}
@@ -299,8 +299,8 @@ AcceleratedCrcMemcpyEngine<vec_regions, int_regions>::Compute(
// Load and CRC the data.
int_data[data_index] = *(usrc + i * kIntLoadsPerVec + k);
crcs[region] = crc32c_t{static_cast<uint32_t>(CRC32_u64(
static_cast<uint32_t>(crcs[region]), int_data[data_index]))};
crcs[region] = crc32c_t{CRC32_u64(static_cast<uint32_t>(crcs[region]),
int_data[data_index])};
// Store the data.
*(udst + i * kIntLoadsPerVec + k) = int_data[data_index];
@@ -101,13 +101,17 @@ constexpr size_t kMediumCutoff = 2048;
namespace {
uint32_t multiply(uint32_t a, uint32_t b) {
V128 shifts = V128_From64WithZeroFill(1);
V128 power = V128_From64WithZeroFill(a);
V128 crc = V128_From64WithZeroFill(b);
V128 res = V128_PMulLow(power, crc);
// Combine crc values
res = V128_ShiftLeft64(res, shifts);
// Combine crc values.
//
// Adding res to itself is equivalent to multiplying by 2,
// or shifting left by 1. Addition is used as not all compilers
// are able to generate optimal code without this hint.
// https://godbolt.org/z/rr3fMnf39
res = V128_Add64(res, res);
return static_cast<uint32_t>(V128_Extract32<1>(res)) ^
CRC32_u32(0, static_cast<uint32_t>(V128_Low64(res)));
}
+22 -2
View File
@@ -219,8 +219,14 @@ cc_library(
cc_library(
name = "demangle_internal",
srcs = ["internal/demangle.cc"],
hdrs = ["internal/demangle.h"],
srcs = [
"internal/demangle.cc",
"internal/demangle_rust.cc",
],
hdrs = [
"internal/demangle.h",
"internal/demangle_rust.h",
],
copts = ABSL_DEFAULT_COPTS,
linkopts = ABSL_DEFAULT_LINKOPTS,
visibility = [
@@ -234,6 +240,20 @@ cc_library(
],
)
cc_test(
name = "demangle_rust_test",
srcs = ["internal/demangle_rust_test.cc"],
copts = ABSL_TEST_COPTS,
linkopts = ABSL_DEFAULT_LINKOPTS,
deps = [
":demangle_internal",
"//absl/base:config",
"//absl/base:core_headers",
"@com_google_googletest//:gtest",
"@com_google_googletest//:gtest_main",
],
)
cc_test(
name = "demangle_test",
srcs = ["internal/demangle_test.cc"],
@@ -196,8 +196,10 @@ absl_cc_library(
demangle_internal
HDRS
"internal/demangle.h"
"internal/demangle_rust.h"
SRCS
"internal/demangle.cc"
"internal/demangle_rust.cc"
COPTS
${ABSL_DEFAULT_COPTS}
DEPS
@@ -206,6 +208,19 @@ absl_cc_library(
PUBLIC
)
absl_cc_test(
NAME
demangle_rust_test
SRCS
"internal/demangle_rust_test.cc"
COPTS
${ABSL_TEST_COPTS}
DEPS
absl::demangle_internal
absl::config
GTest::gmock_main
)
absl_cc_test(
NAME
demangle_test
@@ -19,6 +19,7 @@
#include "absl/debugging/internal/demangle.h"
#include <cstddef>
#include <cstdint>
#include <cstdio>
#include <cstdlib>
@@ -26,6 +27,7 @@
#include <string>
#include "absl/base/config.h"
#include "absl/debugging/internal/demangle_rust.h"
#if ABSL_INTERNAL_HAS_CXA_DEMANGLE
#include <cxxabi.h>
@@ -2110,6 +2112,10 @@ static bool Overflowed(const State *state) {
// The demangler entry point.
bool Demangle(const char* mangled, char* out, size_t out_size) {
if (mangled[0] == '_' && mangled[1] == 'R') {
return DemangleRustSymbolEncoding(mangled, out, out_size);
}
State state;
InitState(&state, mangled, out, out_size);
return ParseTopLevelMangledName(&state) && !Overflowed(&state) &&
@@ -56,6 +56,8 @@ namespace debugging_internal {
//
// See the unit test for more examples.
//
// Support for Rust mangled names is in development; see demangle_rust.h.
//
// Note: we might want to write demanglers for ABIs other than Itanium
// C++ ABI in the future.
bool Demangle(const char* mangled, char* out, size_t out_size);
@@ -0,0 +1,432 @@
// Copyright 2024 The Abseil Authors
//
// 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
//
// https://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.
#include "absl/debugging/internal/demangle_rust.h"
#include <cstddef>
#include <cstdint>
#include <cstring>
#include <limits>
#include "absl/base/attributes.h"
#include "absl/base/config.h"
namespace absl {
ABSL_NAMESPACE_BEGIN
namespace debugging_internal {
namespace {
// Same step limit as the C++ demangler in demangle.cc uses.
constexpr int kMaxReturns = 1 << 17;
bool IsDigit(char c) { return '0' <= c && c <= '9'; }
bool IsLower(char c) { return 'a' <= c && c <= 'z'; }
bool IsUpper(char c) { return 'A' <= c && c <= 'Z'; }
bool IsAlpha(char c) { return IsLower(c) || IsUpper(c); }
bool IsIdentifierChar(char c) { return IsAlpha(c) || IsDigit(c) || c == '_'; }
// Parser for Rust symbol mangling v0, whose grammar is defined here:
//
// https://doc.rust-lang.org/rustc/symbol-mangling/v0.html#symbol-grammar-summary
class RustSymbolParser {
public:
// Prepares to demangle the given encoding, a Rust symbol name starting with
// _R, into the output buffer [out, out_end). The caller is expected to
// continue by calling the new object's Parse function.
RustSymbolParser(const char* encoding, char* out, char* const out_end)
: encoding_(encoding), out_(out), out_end_(out_end) {
if (out_ != out_end_) *out_ = '\0';
}
// Parses the constructor's encoding argument, writing output into the range
// [out, out_end). Returns true on success and false for input whose
// structure was not recognized or exceeded implementation limits, such as by
// nesting structures too deep. In either case *this should not be used
// again.
ABSL_MUST_USE_RESULT bool Parse() && {
// Recursively parses the grammar production named by callee, then resumes
// execution at the next statement.
//
// Recursive-descent parsing is a beautifully readable translation of a
// grammar, but it risks stack overflow if implemented by naive recursion on
// the C++ call stack. So we simulate recursion by goto and switch instead,
// keeping a bounded stack of "return addresses" in the stack_ member.
//
// The callee argument is a statement label. We goto that label after
// saving the "return address" on stack_. The next continue statement in
// the for loop below "returns" from this "call".
//
// The caller argument names the return point. Each value of caller must
// appear in only one ABSL_DEMANGLER_RECURSE call and be listed in the
// definition of enum ReturnAddress. The switch implements the control
// transfer from the end of a "called" subroutine back to the statement
// after the "call".
//
// Note that not all the grammar productions have to be packed into the
// switch, but only those which appear in a cycle in the grammar. Anything
// acyclic can be written as ordinary functions and function calls, e.g.,
// ParseIdentifier.
#define ABSL_DEMANGLER_RECURSE(callee, caller) \
do { \
if (depth_ == data_stack_pointer_) return false; \
/* The next continue will switch on this saved value ... */ \
stack_[depth_++] = caller; \
goto callee; \
/* ... and will land here, resuming the suspended code. */ \
case caller: {} \
} while (0)
// Parse the encoding, counting completed recursive calls to guard against
// excessively complex input and infinite-loop bugs.
int iter = 0;
goto whole_encoding;
for (; iter < kMaxReturns && depth_ > 0; ++iter) {
// This switch resumes the code path most recently suspended by
// ABSL_DEMANGLER_RECURSE.
switch (static_cast<ReturnAddress>(stack_[--depth_])) {
//
// symbol-name ->
// _R decimal-number? path instantiating-crate? vendor-specific-suffix?
whole_encoding:
if (!Eat('_') || !Eat('R')) return false;
// decimal-number? is always empty today, so proceed to path, which
// can't start with a decimal digit.
ABSL_DEMANGLER_RECURSE(path, kInstantiatingCrate);
if (IsAlpha(Peek())) {
++silence_depth_; // Print nothing more from here on.
ABSL_DEMANGLER_RECURSE(path, kVendorSpecificSuffix);
}
switch (Take()) {
case '.': case '$': case '\0': return true;
}
return false; // unexpected trailing content
// path -> crate-root | inherent-impl | trait-impl | trait-definition |
// nested-path | generic-args | backref
path:
switch (Take()) {
case 'C': goto crate_root;
case 'M': return false; // inherent-impl not yet implemented
case 'X': return false; // trait-impl not yet implemented
case 'Y': return false; // trait-definition not yet implemented
case 'N': goto nested_path;
case 'I': return false; // generic-args not yet implemented
case 'B': return false; // backref not yet implemented
default: return false;
}
// crate-root -> C identifier (C consumed above)
crate_root:
if (!ParseIdentifier()) return false;
continue;
// nested-path -> N namespace path identifier (N consumed above)
// namespace -> lower | upper
nested_path:
// Uppercase namespaces must be saved on the stack so we can print
// ::{closure#0} or ::{shim:vtable#0} or ::{X:name#0} as needed.
if (IsUpper(Peek())) {
if (!PushByte(static_cast<std::uint8_t>(Take()))) return false;
ABSL_DEMANGLER_RECURSE(path, kIdentifierInUppercaseNamespace);
if (!Emit("::")) return false;
if (!ParseIdentifier(static_cast<char>(PopByte()))) return false;
continue;
}
// Lowercase namespaces, however, are never represented in the output;
// they all emit just ::name.
if (IsLower(Take())) {
ABSL_DEMANGLER_RECURSE(path, kIdentifierInLowercaseNamespace);
if (!Emit("::")) return false;
if (!ParseIdentifier()) return false;
continue;
}
// Neither upper or lower
return false;
}
}
return false; // hit iteration limit or a bug in our stack handling
}
private:
// Enumerates resumption points for ABSL_DEMANGLER_RECURSE calls.
enum ReturnAddress : std::uint8_t {
kInstantiatingCrate,
kVendorSpecificSuffix,
kIdentifierInUppercaseNamespace,
kIdentifierInLowercaseNamespace,
};
// Element count for the stack_ array. A larger kStackSize accommodates more
// deeply nested names at the cost of a larger footprint on the C++ call
// stack.
enum { kStackSize = 256 };
// Returns the next input character without consuming it.
char Peek() const { return encoding_[pos_]; }
// Consumes and returns the next input character.
char Take() { return encoding_[pos_++]; }
// If the next input character is the given character, consumes it and returns
// true; otherwise returns false without consuming a character.
ABSL_MUST_USE_RESULT bool Eat(char want) {
if (encoding_[pos_] != want) return false;
++pos_;
return true;
}
// Provided there is enough remaining output space, appends c to the output,
// writing a fresh NUL terminator afterward, and returns true. Returns false
// if the output buffer had less than two bytes free.
ABSL_MUST_USE_RESULT bool EmitChar(char c) {
if (silence_depth_ > 0) return true;
if (out_end_ - out_ < 2) return false;
*out_++ = c;
*out_ = '\0';
return true;
}
// Provided there is enough remaining output space, appends the C string token
// to the output, followed by a NUL character, and returns true. Returns
// false if not everything fit into the output buffer.
ABSL_MUST_USE_RESULT bool Emit(const char* token) {
if (silence_depth_ > 0) return true;
const std::size_t token_length = std::strlen(token);
const std::size_t bytes_to_copy = token_length + 1; // token and final NUL
if (static_cast<std::size_t>(out_end_ - out_) < bytes_to_copy) return false;
std::memcpy(out_, token, bytes_to_copy);
out_ += token_length;
return true;
}
// Provided there is enough remaining output space, appends the decimal form
// of disambiguator (if it's nonnegative) or "?" (if it's negative) to the
// output, followed by a NUL character, and returns true. Returns false if
// not everything fit into the output buffer.
ABSL_MUST_USE_RESULT bool EmitDisambiguator(int disambiguator) {
if (disambiguator < 0) return EmitChar('?'); // parsed but too large
if (disambiguator == 0) return EmitChar('0');
// Convert disambiguator to decimal text. Three digits per byte is enough
// because 999 > 256. The bound will remain correct even if future
// maintenance changes the type of the disambiguator variable.
char digits[3 * sizeof(disambiguator)] = {};
std::size_t leading_digit_index = sizeof(digits) - 1;
for (; disambiguator > 0; disambiguator /= 10) {
digits[--leading_digit_index] =
static_cast<char>('0' + disambiguator % 10);
}
return Emit(digits + leading_digit_index);
}
// Consumes an optional disambiguator (s123_) from the input.
//
// On success returns true and fills value with the encoded value if it was
// not too big, otherwise with -1. If the optional disambiguator was omitted,
// value is 0. On parse failure returns false and sets value to -1.
ABSL_MUST_USE_RESULT bool ParseDisambiguator(int& value) {
value = -1;
// disambiguator = s base-62-number
//
// Disambiguators are optional. An omitted disambiguator is zero.
if (!Eat('s')) {
value = 0;
return true;
}
int base_62_value = 0;
if (!ParseBase62Number(base_62_value)) return false;
value = base_62_value < 0 ? -1 : base_62_value + 1;
return true;
}
// Consumes a base-62 number like _ or 123_ from the input.
//
// On success returns true and fills value with the encoded value if it was
// not too big, otherwise with -1. On parse failure returns false and sets
// value to -1.
ABSL_MUST_USE_RESULT bool ParseBase62Number(int& value) {
value = -1;
// base-62-number = (digit | lower | upper)* _
//
// An empty base-62 digit sequence means 0.
if (Eat('_')) {
value = 0;
return true;
}
// A nonempty digit sequence denotes its base-62 value plus 1.
int encoded_number = 0;
bool overflowed = false;
while (IsAlpha(Peek()) || IsDigit(Peek())) {
const char c = Take();
if (encoded_number >= std::numeric_limits<int>::max()/62) {
// If we are close to overflowing an int, keep parsing but stop updating
// encoded_number and remember to return -1 at the end. The point is to
// avoid undefined behavior while parsing crate-root disambiguators,
// which are large in practice but not shown in demangling, while
// successfully computing closure and shim disambiguators, which are
// typically small and are printed out.
overflowed = true;
} else {
int digit;
if (IsDigit(c)) {
digit = c - '0';
} else if (IsLower(c)) {
digit = c - 'a' + 10;
} else {
digit = c - 'A' + 36;
}
encoded_number = 62 * encoded_number + digit;
}
}
if (!Eat('_')) return false;
if (!overflowed) value = encoded_number + 1;
return true;
}
// Consumes an identifier from the input, returning true on success.
//
// A nonzero uppercase_namespace specifies the character after the N in a
// nested-identifier, e.g., 'C' for a closure, allowing ParseIdentifier to
// write out the name with the conventional decoration for that namespace.
ABSL_MUST_USE_RESULT bool ParseIdentifier(char uppercase_namespace = '\0') {
// identifier -> disambiguator? undisambiguated-identifier
int disambiguator = 0;
if (!ParseDisambiguator(disambiguator)) return false;
// undisambiguated-identifier -> u? decimal-number _? bytes
const bool is_punycoded = Eat('u');
if (!IsDigit(Peek())) return false;
int num_bytes = 0;
if (!ParseDecimalNumber(num_bytes)) return false;
(void)Eat('_'); // optional separator, needed if a digit follows
// Emit the beginnings of braced forms like {shim:vtable#0}.
if (uppercase_namespace == '\0') {
if (is_punycoded && !Emit("{Punycode ")) return false;
} else {
switch (uppercase_namespace) {
case 'C':
if (!Emit("{closure")) return false;
break;
case 'S':
if (!Emit("{shim")) return false;
break;
default:
if (!EmitChar('{') || !EmitChar(uppercase_namespace)) return false;
break;
}
if (num_bytes > 0 && !Emit(":")) return false;
}
// Emit the name itself.
for (int i = 0; i < num_bytes; ++i) {
const char c = Take();
if (!IsIdentifierChar(c) &&
// The spec gives toolchains the choice of Punycode or raw UTF-8 for
// identifiers containing code points above 0x7f, so accept bytes with
// the high bit set if this is not a u... encoding.
(is_punycoded || (c & 0x80) == 0)) {
return false;
}
if (!EmitChar(c)) return false;
}
// Emit the endings of braced forms: "#42}" or "}".
if (uppercase_namespace != '\0') {
if (!EmitChar('#')) return false;
if (!EmitDisambiguator(disambiguator)) return false;
}
if (uppercase_namespace != '\0' || is_punycoded) {
if (!EmitChar('}')) return false;
}
return true;
}
// Consumes a decimal number like 0 or 123 from the input. On success returns
// true and fills value with the encoded value. If the encoded value is too
// large or otherwise unparsable, returns false and sets value to -1.
ABSL_MUST_USE_RESULT bool ParseDecimalNumber(int& value) {
value = -1;
if (!IsDigit(Peek())) return false;
int encoded_number = Take() - '0';
if (encoded_number == 0) {
// Decimal numbers are never encoded with extra leading zeroes.
value = 0;
return true;
}
while (IsDigit(Peek()) &&
// avoid overflow
encoded_number < std::numeric_limits<int>::max()/10) {
encoded_number = 10 * encoded_number + (Take() - '0');
}
if (IsDigit(Peek())) return false; // too big
value = encoded_number;
return true;
}
// Pushes byte onto the data stack (the right side of stack_) and returns
// true if stack_ is not full, else returns false.
ABSL_MUST_USE_RESULT bool PushByte(std::uint8_t byte) {
if (depth_ == data_stack_pointer_) return false;
stack_[--data_stack_pointer_] = byte;
return true;
}
// Pops the last pushed data byte from stack_. Requires that the data stack
// is not empty (data_stack_pointer_ < kStackSize).
std::uint8_t PopByte() { return stack_[data_stack_pointer_++]; }
// Call and data stacks reside in stack_. The leftmost depth_ elements
// contain ReturnAddresses pushed by ABSL_DEMANGLER_RECURSE. The elements
// from index data_stack_pointer_ to the right edge of stack_ contain bytes
// pushed by PushByte.
std::uint8_t stack_[kStackSize] = {};
int data_stack_pointer_ = kStackSize;
int depth_ = 0;
// Anything parsed while silence_depth_ > 0 contributes nothing to the
// demangled output. For constructs omitted from the demangling, such as
// impl-path and the contents of generic-args, we will increment
// silence_depth_ on the way in and decrement silence_depth_ on the way out.
int silence_depth_ = 0;
// Input: encoding_ points just after the _R in a Rust mangled symbol, and
// encoding_[pos_] is the next input character to be scanned.
int pos_ = 0;
const char* encoding_ = nullptr;
// Output: *out_ is where the next output character should be written, and
// out_end_ points past the last byte of available space.
char* out_ = nullptr;
char* out_end_ = nullptr;
};
} // namespace
bool DemangleRustSymbolEncoding(const char* mangled, char* out,
std::size_t out_size) {
return RustSymbolParser(mangled, out, out + out_size).Parse();
}
} // namespace debugging_internal
ABSL_NAMESPACE_END
} // namespace absl
@@ -0,0 +1,47 @@
// Copyright 2024 The Abseil Authors
//
// 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
//
// https://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.
#ifndef ABSL_DEBUGGING_INTERNAL_DEMANGLE_RUST_H_
#define ABSL_DEBUGGING_INTERNAL_DEMANGLE_RUST_H_
#include <cstddef>
#include "absl/base/config.h"
namespace absl {
ABSL_NAMESPACE_BEGIN
namespace debugging_internal {
// Demangle the Rust encoding `mangled`. On success, return true and write the
// demangled symbol name to `out`. Otherwise, return false, leaving unspecified
// contents in `out`. For example, calling DemangleRustSymbolEncoding with
// `mangled = "_RNvC8my_crate7my_func"` will yield `my_crate::my_func` in `out`,
// provided `out_size` is large enough for that value and its trailing NUL.
//
// DemangleRustSymbolEncoding is async-signal-safe and runs in bounded C++
// call-stack space. It is suitable for symbolizing stack traces in a signal
// handler.
//
// The demangling logic is under development. In this version of Abseil,
// DemangleRustSymbolEncoding parses a few simple kinds of symbol names, but
// nothing having backreferences in the input or angle brackets in the
// demangling, and it emits raw Punycode instead of the UTF-8 represented by it.
bool DemangleRustSymbolEncoding(const char* mangled, char* out,
std::size_t out_size);
} // namespace debugging_internal
ABSL_NAMESPACE_END
} // namespace absl
#endif // ABSL_DEBUGGING_INTERNAL_DEMANGLE_RUST_H_
@@ -0,0 +1,216 @@
// Copyright 2024 The Abseil Authors
//
// 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
//
// https://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.
#include "absl/debugging/internal/demangle_rust.h"
#include <cstddef>
#include <string>
#include "gtest/gtest.h"
#include "absl/base/config.h"
namespace absl {
ABSL_NAMESPACE_BEGIN
namespace debugging_internal {
namespace {
// If DemangleRustSymbolEncoding(mangled, <buffer with room for buffer_size
// chars>, buffer_size) returns true and seems not to have overrun its output
// buffer, returns the string written by DemangleRustSymbolEncoding; otherwise
// returns an error message.
std::string ResultOfDemangling(const char* mangled, std::size_t buffer_size) {
// Fill the buffer with something other than NUL so we test whether Demangle
// appends trailing NUL as expected.
std::string buffer(buffer_size + 1, '~');
constexpr char kCanaryCharacter = 0x7f; // arbitrary unlikely value
buffer[buffer_size] = kCanaryCharacter;
if (!DemangleRustSymbolEncoding(mangled, &buffer[0], buffer_size)) {
return "Failed parse";
}
if (buffer[buffer_size] != kCanaryCharacter) {
return "Buffer overrun by output: " + buffer.substr(0, buffer_size + 1)
+ "...";
}
return buffer.data(); // Not buffer itself: this trims trailing padding.
}
// Tests that DemangleRustSymbolEncoding converts mangled into plaintext given
// enough output buffer space but returns false and avoids overrunning a buffer
// that is one byte too short.
//
// The lambda wrapping allows ASSERT_EQ to branch out the first time an
// expectation is not satisfied, preventing redundant errors for the same bug.
//
// We test first with excess space so that if the algorithm just computes the
// wrong answer, it will be clear from the error log that the bounds checks are
// unlikely to be the code at fault.
#define EXPECT_DEMANGLING(mangled, plaintext) \
do { \
[] { \
constexpr std::size_t plenty_of_space = sizeof(plaintext) + 128; \
constexpr std::size_t just_enough_space = sizeof(plaintext); \
constexpr std::size_t one_byte_too_few = sizeof(plaintext) - 1; \
const char* expected_plaintext = plaintext; \
const char* expected_error = "Failed parse"; \
ASSERT_EQ(ResultOfDemangling(mangled, plenty_of_space), \
expected_plaintext); \
ASSERT_EQ(ResultOfDemangling(mangled, just_enough_space), \
expected_plaintext); \
ASSERT_EQ(ResultOfDemangling(mangled, one_byte_too_few), \
expected_error); \
}(); \
} while (0)
// Tests that DemangleRustSymbolEncoding rejects the given input (typically, a
// truncation of a real Rust symbol name).
#define EXPECT_DEMANGLING_FAILS(mangled) \
do { \
constexpr std::size_t plenty_of_space = 1024; \
const char* expected_error = "Failed parse"; \
EXPECT_EQ(ResultOfDemangling(mangled, plenty_of_space), expected_error); \
} while (0)
// Piping grep -C 1 _R demangle_test.cc into your favorite c++filt
// implementation allows you to verify that the goldens below are reasonable.
TEST(DemangleRust, EmptyDemangling) {
EXPECT_TRUE(DemangleRustSymbolEncoding("_RC0", nullptr, 0));
}
TEST(DemangleRust, FunctionAtCrateLevel) {
EXPECT_DEMANGLING("_RNvC10crate_name9func_name", "crate_name::func_name");
EXPECT_DEMANGLING(
"_RNvCs09azAZ_10crate_name9func_name", "crate_name::func_name");
}
TEST(DemangleRust, TruncationsOfFunctionAtCrateLevel) {
EXPECT_DEMANGLING_FAILS("_R");
EXPECT_DEMANGLING_FAILS("_RN");
EXPECT_DEMANGLING_FAILS("_RNvC");
EXPECT_DEMANGLING_FAILS("_RNvC10");
EXPECT_DEMANGLING_FAILS("_RNvC10crate_nam");
EXPECT_DEMANGLING_FAILS("_RNvC10crate_name");
EXPECT_DEMANGLING_FAILS("_RNvC10crate_name9");
EXPECT_DEMANGLING_FAILS("_RNvC10crate_name9func_nam");
EXPECT_DEMANGLING_FAILS("_RNvCs");
EXPECT_DEMANGLING_FAILS("_RNvCs09azAZ");
EXPECT_DEMANGLING_FAILS("_RNvCs09azAZ_");
}
TEST(DemangleRust, VendorSuffixes) {
EXPECT_DEMANGLING("_RNvC10crate_name9func_name.!@#", "crate_name::func_name");
EXPECT_DEMANGLING("_RNvC10crate_name9func_name$!@#", "crate_name::func_name");
}
TEST(DemangleRust, UnicodeIdentifiers) {
EXPECT_DEMANGLING("_RNvC7ice_cap17Eyjafjallajökull",
"ice_cap::Eyjafjallajökull");
EXPECT_DEMANGLING("_RNvC7ice_caps_u19Eyjafjallajkull_jtb",
"ice_cap::{Punycode Eyjafjallajkull_jtb}");
}
TEST(DemangleRust, FunctionInModule) {
EXPECT_DEMANGLING("_RNvNtCs09azAZ_10crate_name11module_name9func_name",
"crate_name::module_name::func_name");
}
TEST(DemangleRust, FunctionInFunction) {
EXPECT_DEMANGLING(
"_RNvNvCs09azAZ_10crate_name15outer_func_name15inner_func_name",
"crate_name::outer_func_name::inner_func_name");
}
TEST(DemangleRust, ClosureInFunction) {
EXPECT_DEMANGLING(
"_RNCNvCs09azAZ_10crate_name9func_name0",
"crate_name::func_name::{closure#0}");
EXPECT_DEMANGLING(
"_RNCNvCs09azAZ_10crate_name9func_name0Cs123_12client_crate",
"crate_name::func_name::{closure#0}");
}
TEST(DemangleRust, ClosureNumbering) {
EXPECT_DEMANGLING(
"_RNCNvCs09azAZ_10crate_name9func_names_0Cs123_12client_crate",
"crate_name::func_name::{closure#1}");
EXPECT_DEMANGLING(
"_RNCNvCs09azAZ_10crate_name9func_names0_0Cs123_12client_crate",
"crate_name::func_name::{closure#2}");
EXPECT_DEMANGLING(
"_RNCNvCs09azAZ_10crate_name9func_names9_0Cs123_12client_crate",
"crate_name::func_name::{closure#11}");
EXPECT_DEMANGLING(
"_RNCNvCs09azAZ_10crate_name9func_namesa_0Cs123_12client_crate",
"crate_name::func_name::{closure#12}");
EXPECT_DEMANGLING(
"_RNCNvCs09azAZ_10crate_name9func_namesz_0Cs123_12client_crate",
"crate_name::func_name::{closure#37}");
EXPECT_DEMANGLING(
"_RNCNvCs09azAZ_10crate_name9func_namesA_0Cs123_12client_crate",
"crate_name::func_name::{closure#38}");
EXPECT_DEMANGLING(
"_RNCNvCs09azAZ_10crate_name9func_namesZ_0Cs123_12client_crate",
"crate_name::func_name::{closure#63}");
EXPECT_DEMANGLING(
"_RNCNvCs09azAZ_10crate_name9func_names10_0Cs123_12client_crate",
"crate_name::func_name::{closure#64}");
EXPECT_DEMANGLING(
"_RNCNvCs09azAZ_10crate_name9func_namesg6_0Cs123_12client_crate",
"crate_name::func_name::{closure#1000}");
}
TEST(DemangleRust, ClosureNumberOverflowingInt) {
EXPECT_DEMANGLING(
"_RNCNvCs09azAZ_10crate_name9func_names1234567_0Cs123_12client_crate",
"crate_name::func_name::{closure#?}");
}
TEST(DemangleRust, UnexpectedlyNamedClosure) {
EXPECT_DEMANGLING(
"_RNCNvCs123_10crate_name9func_name12closure_nameCs456_12client_crate",
"crate_name::func_name::{closure:closure_name#0}");
EXPECT_DEMANGLING(
"_RNCNvCs123_10crate_name9func_names2_12closure_nameCs456_12client_crate",
"crate_name::func_name::{closure:closure_name#4}");
}
TEST(DemangleRust, ItemNestedInsideClosure) {
EXPECT_DEMANGLING(
"_RNvNCNvCs123_10crate_name9func_name015inner_func_nameCs_12client_crate",
"crate_name::func_name::{closure#0}::inner_func_name");
}
TEST(DemangleRust, Shim) {
EXPECT_DEMANGLING(
"_RNSNvCs123_10crate_name9func_name6vtableCs456_12client_crate",
"crate_name::func_name::{shim:vtable#0}");
}
TEST(DemangleRust, UnknownUppercaseNamespace) {
EXPECT_DEMANGLING(
"_RNXNvCs123_10crate_name9func_name14mystery_objectCs456_12client_crate",
"crate_name::func_name::{X:mystery_object#0}");
}
TEST(DemangleRust, NestedUppercaseNamespaces) {
EXPECT_DEMANGLING(
"_RNCNXNYCs123_10crate_names0_1ys1_1xs2_0Cs456_12client_crate",
"crate_name::{Y:y#2}::{X:x#3}::{closure#4}");
}
} // namespace
} // namespace debugging_internal
ABSL_NAMESPACE_END
} // namespace absl
@@ -300,6 +300,15 @@ TEST(Demangle, AbiTags) {
EXPECT_STREQ("C[abi:bar][abi:foo]()", tmp);
}
// Test one Rust symbol to exercise Demangle's delegation path. Rust demangling
// itself is more thoroughly tested in demangle_rust_test.cc.
TEST(Demangle, DelegatesToDemangleRustSymbolEncoding) {
char tmp[80];
EXPECT_TRUE(Demangle("_RNvC8my_crate7my_func", tmp, sizeof(tmp)));
EXPECT_STREQ("my_crate::my_func", tmp);
}
// Tests that verify that Demangle footprint is within some limit.
// They are not to be run under sanitizers as the sanitizers increase
// stack consumption by about 4x.
+12 -3
View File
@@ -98,9 +98,9 @@ ABSL_NAMESPACE_BEGIN
// `AnyInvocable` also properly respects `const` qualifiers, reference
// qualifiers, and the `noexcept` specification (only in C++ 17 and beyond) as
// part of the user-specified function type (e.g.
// `AnyInvocable<void()&& const noexcept>`). These qualifiers will be applied to
// the `AnyInvocable` object's `operator()`, and the underlying invocable must
// be compatible with those qualifiers.
// `AnyInvocable<void() const && noexcept>`). These qualifiers will be applied
// to the `AnyInvocable` object's `operator()`, and the underlying invocable
// must be compatible with those qualifiers.
//
// Comparison of const and non-const function types:
//
@@ -151,6 +151,12 @@ ABSL_NAMESPACE_BEGIN
//
// Attempting to call `absl::AnyInvocable` multiple times in such a case
// results in undefined behavior.
//
// Invoking an empty `absl::AnyInvocable` results in undefined behavior:
//
// // Create an empty instance using the default constructor.
// AnyInvocable<void()> empty;
// empty(); // WARNING: Undefined behavior!
template <class Sig>
class AnyInvocable : private internal_any_invocable::Impl<Sig> {
private:
@@ -167,6 +173,7 @@ class AnyInvocable : private internal_any_invocable::Impl<Sig> {
// Constructors
// Constructs the `AnyInvocable` in an empty state.
// Invoking it results in undefined behavior.
AnyInvocable() noexcept = default;
AnyInvocable(std::nullptr_t) noexcept {} // NOLINT
@@ -277,6 +284,8 @@ class AnyInvocable : private internal_any_invocable::Impl<Sig> {
// In other words:
// std::function<void()> f; // empty
// absl::AnyInvocable<void()> a = std::move(f); // not empty
//
// Invoking an empty `AnyInvocable` results in undefined behavior.
explicit operator bool() const noexcept { return this->HasValue(); }
// Invokes the target object of `*this`. `*this` must not be empty.
@@ -19,11 +19,11 @@
////////////////////////////////////////////////////////////////////////////////
// //
// This implementation of the proposed `any_invocable` uses an approach that //
// chooses between local storage and remote storage for the contained target //
// object based on the target object's size, alignment requirements, and //
// whether or not it has a nothrow move constructor. Additional optimizations //
// are performed when the object is a trivially copyable type [basic.types]. //
// This implementation chooses between local storage and remote storage for //
// the contained target object based on the target object's size, alignment //
// requirements, and whether or not it has a nothrow move constructor. //
// Additional optimizations are performed when the object is a trivially //
// copyable type [basic.types]. //
// //
// There are three datamembers per `AnyInvocable` instance //
// //
@@ -39,7 +39,7 @@
// target object, directly returning the result. //
// //
// When in the logically empty state, the manager function is an empty //
// function and the invoker function is one that would be undefined-behavior //
// function and the invoker function is one that would be undefined behavior //
// to call. //
// //
// An additional optimization is performed when converting from one //

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