mirror of
https://github.com/XTLS/Xray-docs-next.git
synced 2026-10-12 01:08:24 +03:00
Xray-core: Add Lua script for dns and routing
https://github.com/XTLS/Xray-core/pull/6823
This commit is contained in:
@@ -0,0 +1,215 @@
|
||||
# Data Types
|
||||
|
||||
This page summarizes data types shared by the modules and explains how they are represented and used in Lua. Types specific to a module are documented on that module's page.
|
||||
|
||||
## net.IP
|
||||
|
||||
| Property | Description |
|
||||
| ------------------ | ------------------------- |
|
||||
| Underlying Go type | `net.IP`, `common/net.IP` |
|
||||
| Lua representation | `userdata` |
|
||||
|
||||
You usually do not need to compare individual `net.IP` values in Lua, especially on frequently executed paths. For IP matching, prefer [`IPMatcher`](./module-geodata.md#ipmatcher), which can directly handle the `net.IP` values returned by APIs.
|
||||
|
||||
If you need to manipulate IPs directly, remember that `net.IP` and IP address strings are different types. Use the following two methods for conversion and comparison:
|
||||
|
||||
### String
|
||||
|
||||
```lua
|
||||
local text = ip:String()
|
||||
```
|
||||
|
||||
Converts an IP to address text.
|
||||
|
||||
**Parameters**
|
||||
|
||||
No additional parameters.
|
||||
|
||||
**Return Values**
|
||||
|
||||
| Return value | Type | Description |
|
||||
| ------------ | -------- | ------------------------------ |
|
||||
| `text` | `string` | IP address string; never `nil` |
|
||||
|
||||
### Equal
|
||||
|
||||
```lua
|
||||
local equal = ip:Equal(otherIP)
|
||||
```
|
||||
|
||||
Checks whether two IPs are equal.
|
||||
|
||||
**Parameters**
|
||||
|
||||
| Parameter | Type | Description |
|
||||
| --------- | ---------------------------- | -------------------------------------------- |
|
||||
| `otherIP` | [`net.IP`](#net-ip) or `nil` | The IP to compare; explicit `nil` is allowed |
|
||||
|
||||
**Return Values**
|
||||
|
||||
| Return value | Type | Description |
|
||||
| ------------ | --------- | ----------------------------------------------- |
|
||||
| `equal` | `boolean` | `true` if equal, otherwise `false`; never `nil` |
|
||||
|
||||
**Behavior**
|
||||
|
||||
Comparing a valid IP with `nil` returns `false`.
|
||||
|
||||
**Example**
|
||||
|
||||
```lua
|
||||
if ip ~= nil then
|
||||
local text = ip:String()
|
||||
local equal = ip:Equal(otherIP)
|
||||
end
|
||||
```
|
||||
|
||||
## slice
|
||||
|
||||
A Go slice is represented as `userdata` in Lua, retains its element type, and is indexed starting at **1**. The following two slice types share the same length, indexing, and iteration operations.
|
||||
|
||||
| Type | Lua representation | Element type |
|
||||
| ----------------------- | ------------------ | ------------------- |
|
||||
| [`[]net.IP`](#net-ip-1) | `userdata` | [`net.IP`](#net-ip) |
|
||||
| [`[]uint32`](#uint32) | `userdata` | `number` |
|
||||
|
||||
### []net.IP
|
||||
|
||||
Tip: For matching or filtering IP lists, prefer [`IPMatcher`](./module-geodata.md#ipmatcher), which can directly handle `[]net.IP`.
|
||||
|
||||
### []uint32
|
||||
|
||||
Elements are 32-bit unsigned integers in the range `0 .. 4294967295`.
|
||||
|
||||
### #values
|
||||
|
||||
```lua
|
||||
local count = #values
|
||||
```
|
||||
|
||||
Returns the number of elements in a slice.
|
||||
|
||||
**Return Values**
|
||||
|
||||
| Return value | Type | Description |
|
||||
| ------------ | -------- | ---------------------------------------------- |
|
||||
| `count` | `number` | A non-negative integer; `0` for an empty slice |
|
||||
|
||||
**Behavior**
|
||||
|
||||
Lists returned by APIs may be `nil` or slices of length 0. `values` must be non-`nil` before these operations are used; checking only `if values then` does not tell you whether the list has elements.
|
||||
|
||||
### values[i]
|
||||
|
||||
```lua
|
||||
local value = values[i]
|
||||
```
|
||||
|
||||
Reads one element from a slice.
|
||||
|
||||
**Parameters**
|
||||
|
||||
| Parameter | Type | Description |
|
||||
| --------- | -------- | -------------------------------------------------------- |
|
||||
| `i` | `number` | Required; must be an integer in the range `1 .. #values` |
|
||||
|
||||
**Return Values**
|
||||
|
||||
| Return value | Type | Description |
|
||||
| ------------ | ------------------------------- | ---------------------------------------------------- |
|
||||
| `value` | [`net.IP`](#net-ip) or `number` | An element of `[]net.IP` or `[]uint32`, respectively |
|
||||
|
||||
**Behavior**
|
||||
|
||||
Out-of-bounds access raises a Lua exception instead of returning `nil`. Avoid modifying slices returned by APIs directly.
|
||||
|
||||
**Example**
|
||||
|
||||
```lua
|
||||
if ips ~= nil and #ips > 0 then
|
||||
local firstIP = ips[1]:String()
|
||||
end
|
||||
```
|
||||
|
||||
### values()
|
||||
|
||||
```lua
|
||||
for i, value in values() do
|
||||
-- Use index i and element value
|
||||
end
|
||||
```
|
||||
|
||||
Returns a slice iterator for a generic `for` loop, with indices starting at 1.
|
||||
|
||||
**Parameters**
|
||||
|
||||
No additional parameters.
|
||||
|
||||
**Return Values**
|
||||
|
||||
| Return value | Type | Description |
|
||||
| ------------ | ---------- | ----------------------------------------------- |
|
||||
| Iterator | `function` | Provides each index and element, as shown above |
|
||||
|
||||
**Behavior**
|
||||
|
||||
Slices do not support `pairs` or `ipairs`; a numeric `for` loop can also be used.
|
||||
|
||||
**Example**
|
||||
|
||||
```lua
|
||||
if ips ~= nil then
|
||||
for i = 1, #ips do
|
||||
local text = ips[i]:String()
|
||||
end
|
||||
|
||||
for i, ip in ips() do
|
||||
local text = ip:String()
|
||||
end
|
||||
end
|
||||
```
|
||||
|
||||
## IP List Input
|
||||
|
||||
When passing an IP list to `IPMatcher`, you can use the following forms:
|
||||
|
||||
| Input form | Lua representation | Description |
|
||||
| ---------------------------- | ------------------ | ------------------------------------------------------------------------------------- |
|
||||
| [`[]net.IP`](#net-ip-1) | `userdata` | An IP list; empty slices are allowed |
|
||||
| Array of [`net.IP`](#net-ip) | `table` | Consecutive IPs starting at index 1; an empty array `{}` is allowed, but gaps are not |
|
||||
| `nil` | `nil` | Must be passed explicitly; see each method for its result |
|
||||
|
||||
IP address strings are not parsed into `net.IP`. An empty string `""` is not a valid IP list; passing it to `AnyMatch`, `Matches`, or `FilterIPs` raises a Lua exception.
|
||||
|
||||
## error
|
||||
|
||||
| Property | Description |
|
||||
| ------------------ | ------------------------------------------- |
|
||||
| Underlying Go type | `error` |
|
||||
| Lua representation | `userdata` for an error; `nil` for no error |
|
||||
|
||||
An API's `err` return value is `nil` when there is no error; otherwise it is an error object. An error object can be returned unchanged as a Hook's `err` value for the Xray core to handle.
|
||||
|
||||
**Hook Return Convention**
|
||||
|
||||
| Error value | Meaning |
|
||||
| ---------------- | -------------------------------------------------------------------------------------- |
|
||||
| `nil` | No error |
|
||||
| Error `userdata` | Preserves the original error |
|
||||
| `string` | An error message supplied by the script; an empty string `""` still indicates an error |
|
||||
|
||||
String errors apply only to Hook return values. Xray APIs themselves return an error or `nil`. See the relevant Hook for validation and failure behavior.
|
||||
|
||||
**Logging Error Messages**
|
||||
|
||||
You cannot obtain a Go error object's message with `tostring(err)`. To log it, pass `err` directly to [`xray.log`](./module-log.md).
|
||||
|
||||
```lua
|
||||
local log = require("xray.log")
|
||||
local ips, ttl, err = serverObj:Query(domain, ipv4, ipv6, fake)
|
||||
if err ~= nil then
|
||||
log.Warning("DNS query failed: ", err)
|
||||
elseif ips ~= nil and #ips > 0 then
|
||||
local firstIP = ips[1]:String()
|
||||
end
|
||||
```
|
||||
@@ -0,0 +1,67 @@
|
||||
# DNS Hook
|
||||
|
||||
The core calls the global `HandleDNSQuery` function in `dns.script` during built-in DNS queries. See the [DNS Scripts guide](../guide/dns.md) for configuration and complete examples.
|
||||
|
||||
## API Index
|
||||
|
||||
| Category | Member | Description |
|
||||
| -------- | ---------------------------------------- | ------------------ |
|
||||
| Hook | [`HandleDNSQuery(...)`](#handlednsquery) | Handle DNS queries |
|
||||
|
||||
## Hook Interface
|
||||
|
||||
### HandleDNSQuery(...)
|
||||
|
||||
```lua
|
||||
function HandleDNSQuery(domain, ipv4, ipv6, fake)
|
||||
return ips, ttl, err
|
||||
end
|
||||
```
|
||||
|
||||
Handles one DNS query, returning an IP slice, TTL, or error.
|
||||
|
||||
#### Parameters
|
||||
|
||||
The core always passes all four parameters, and none is `nil`.
|
||||
|
||||
| Parameter | Type | Description |
|
||||
| --------- | --------- | ----------------------------------------- |
|
||||
| `domain` | `string` | Domain to query; non-empty and lowercase |
|
||||
| `ipv4` | `boolean` | Whether IPv4 address queries are allowed |
|
||||
| `ipv6` | `boolean` | Whether IPv6 address queries are allowed |
|
||||
| `fake` | `boolean` | Whether FakeDNS is allowed for this query |
|
||||
|
||||
`ipv4` and `ipv6` are already restricted by the global [`queryStrategy`](../../../config/dns.md#dnsobject); at least one is `true`. Normally, pass these three boolean parameters unchanged when querying an upstream.
|
||||
|
||||
#### Return Values
|
||||
|
||||
| Return value | Type | Description |
|
||||
| ------------ | ---------------------------------------------------- | ------------------------------------------------------------------------------------ |
|
||||
| `ips` | [`[]net.IP`](./data-types.md#net-ip-1) or `nil` | Query results; must be a slice, not a Lua array |
|
||||
| `ttl` | `number` or `nil` | TTL in seconds; when `err == nil`, must be an integer in the range `0 .. 4294967295` |
|
||||
| `err` | [`error`](./data-types.md#error), `string`, or `nil` | Only `nil` means no error |
|
||||
|
||||
#### Behavior
|
||||
|
||||
The core validates `err`, `ttl`, and `ips` in that order, stopping at the first error. When `err` is non-`nil`, the other two values are ignored. When there is no error, a valid TTL must be provided even if the IP result is empty.
|
||||
|
||||
To return an empty response, use `return nil, 0, nil`. An error belongs in the third position, for example `return nil, nil, "query failed"`.
|
||||
|
||||
| Script result | Core behavior |
|
||||
| ----------------------------------------------------------------- | ---------------------------------- |
|
||||
| Validation succeeds and `ips` is non-empty | Query succeeds |
|
||||
| `err == nil`, valid TTL, but `ips` is `nil` or an empty slice | Query fails with an empty response |
|
||||
| Returned error, invalid return-value type, or execution exception | Query fails |
|
||||
|
||||
#### Example
|
||||
|
||||
You can forward all three return values from [`serverObj:Query`](./module-dns.md#query) directly:
|
||||
|
||||
```lua
|
||||
local dns = require("xray.dns")
|
||||
local serverObj = dns.Servers[1]
|
||||
|
||||
function HandleDNSQuery(domain, ipv4, ipv6, fake)
|
||||
return serverObj:Query(domain, ipv4, ipv6, fake)
|
||||
end
|
||||
```
|
||||
@@ -0,0 +1,181 @@
|
||||
# Routing Hook
|
||||
|
||||
The core calls the global `HandleRoute` function in `routing.script` when selecting an outbound. See the [Routing Scripts guide](../guide/routing.md) for configuration and complete examples.
|
||||
|
||||
## API Index
|
||||
|
||||
| Category | Member | Description |
|
||||
| -------- | --------------------------------------- | ------------------------------------- |
|
||||
| Hook | [`HandleRoute(...)`](#handleroute) | Select an outbound for a request |
|
||||
| Object | [`routing.Context`](#routing-context) | The current request's routing context |
|
||||
| Method | [`ctx:GetSourceIPs()`](#getsourceips) | Get source IPs |
|
||||
| Method | [`ctx:GetTargetIPs()`](#gettargetips) | Get target IPs |
|
||||
| Method | [`ctx:GetLocalIPs()`](#getlocalips) | Get local IPs |
|
||||
| Method | [`ctx:GetAttributes()`](#getattributes) | Get request attributes |
|
||||
|
||||
## Hook Interface
|
||||
|
||||
### HandleRoute(...)
|
||||
|
||||
```lua
|
||||
function HandleRoute(ctx, inboundTag, sourcePort, targetPort,
|
||||
localPort, targetDomain, network, protocol, user,
|
||||
vlessRoute, skipDNSResolve)
|
||||
return outboundTag, ruleTag, err
|
||||
end
|
||||
```
|
||||
|
||||
Selects an outbound for the current request, optionally returning a rule name or an error.
|
||||
|
||||
#### Parameters
|
||||
|
||||
The core always passes all 11 parameters in the order shown above, and none is `nil`. You can omit unused trailing parameters from the function definition; this does not change the values passed by the core.
|
||||
|
||||
| Parameter | Type | Description | Empty and Default Values |
|
||||
| ---------------- | ------------------------------------- | --------------------------------------------------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------- |
|
||||
| `ctx` | [`routing.Context`](#routing-context) | Current request context | Always valid `userdata` |
|
||||
| `inboundTag` | `string` | Inbound tag | `""` if the inbound has no tag or inbound information is unavailable |
|
||||
| `sourcePort` | `number` | Source port, `0 .. 65535` | `0` if no valid source address is available |
|
||||
| `targetPort` | `number` | Target port, `0 .. 65535` | `0` if no valid target address is available |
|
||||
| `localPort` | `number` | Local port of the inbound connection, `0 .. 65535` | `0` if no valid local address is available |
|
||||
| `targetDomain` | `string` | The effective sniffed domain takes precedence over the connection's target domain; converted to lowercase | `""` if the target is an IP without an effective sniffed domain, or target information is unavailable |
|
||||
| `network` | `number` | Network type; compare with constants such as [`router.NetworkTCP`](./module-router.md#constants) | `NetworkUnknown` (`0`) if outbound information is unavailable or the network type is unknown |
|
||||
| `protocol` | `string` | Sniffed protocol | `""` if sniffed protocol information is unavailable |
|
||||
| `user` | `string` | User email | `""` if inbound or user information is unavailable, or no email is set |
|
||||
| `vlessRoute` | `number` | Routing value formed from bytes 7 and 8 of the VLESS UUID, `0 .. 65535` | `0` if unavailable; the value itself may also be 0 |
|
||||
| `skipDNSResolve` | `boolean` | When `true`, DNS queries must be skipped to avoid loops | `false` if the flag or additional request information is unavailable |
|
||||
|
||||
#### Return Values
|
||||
|
||||
| Return value | Type | Description |
|
||||
| ------------- | ---------------------------------------------------- | ---------------------------------------------------------- |
|
||||
| `outboundTag` | `string` or `nil` | Outbound tag; `nil` or `""` means no outbound was selected |
|
||||
| `ruleTag` | `string` or `nil` | Optional rule name; `nil`, omission, or `""` means no name |
|
||||
| `err` | [`error`](./data-types.md#error), `string`, or `nil` | Only `nil` means no error |
|
||||
|
||||
#### Behavior
|
||||
|
||||
The core validates `err`, `outboundTag`, and `ruleTag` in that order, stopping at the first error. When `err` is non-`nil`, the first two values are ignored. When no outbound is selected, `ruleTag` is ignored.
|
||||
|
||||
On success, trailing `ruleTag` and `err` values may be omitted, for example `return "direct"`. An error must be in the third position, for example `return nil, nil, "routing failed"`; `return nil, "routing failed"` does not report an error.
|
||||
|
||||
| Script result | Core behavior |
|
||||
| ----------------------------------------------------------------- | -------------------------------------------------------------------------------- |
|
||||
| Validation succeeds and `outboundTag` is non-empty | Uses the corresponding outbound; closes the connection if the tag does not exist |
|
||||
| No outbound selected | Uses the default outbound |
|
||||
| Returned error, invalid return-value type, or execution exception | Logs the error and tries the default outbound |
|
||||
|
||||
The default outbound is the first outbound in the configuration; the connection is closed if no default outbound is available. To block traffic, explicitly return a configured [`blackhole`](../../../config/outbounds/blackhole.md) outbound tag instead of relying on this behavior with a nonexistent tag.
|
||||
|
||||
## Related Objects
|
||||
|
||||
### routing.Context
|
||||
|
||||
`routing.Context` represents the current request's routing context, accessed through the `ctx` parameter in `HandleRoute`.
|
||||
|
||||
| Property | Description |
|
||||
| ------------------ | ------------------------------------------------------ |
|
||||
| Underlying Go type | The `routing.Context` interface in `features/routing` |
|
||||
| Lua representation | `userdata` |
|
||||
| Obtained from | The first argument passed by the core to `HandleRoute` |
|
||||
|
||||
All methods below take no additional parameters. See [Data Types](./data-types.md) for IP and slice operations.
|
||||
|
||||
#### GetSourceIPs
|
||||
|
||||
```lua
|
||||
local ips = ctx:GetSourceIPs()
|
||||
```
|
||||
|
||||
Gets the request's source IPs.
|
||||
|
||||
**Parameters**
|
||||
|
||||
No additional parameters.
|
||||
|
||||
**Return Values**
|
||||
|
||||
| Return value | Type | Description |
|
||||
| ------------ | ----------------------------------------------- | ------------------------------------- |
|
||||
| `ips` | [`[]net.IP`](./data-types.md#net-ip-1) or `nil` | IP list for the corresponding address |
|
||||
|
||||
**Behavior**
|
||||
|
||||
Returns `nil` if there is no inbound, the source address is invalid, or it is not an IP address.
|
||||
|
||||
#### GetTargetIPs
|
||||
|
||||
```lua
|
||||
local ips = ctx:GetTargetIPs()
|
||||
```
|
||||
|
||||
Gets the request's target IPs.
|
||||
|
||||
**Parameters**
|
||||
|
||||
No additional parameters.
|
||||
|
||||
**Return Values**
|
||||
|
||||
| Return value | Type | Description |
|
||||
| ------------ | ----------------------------------------------- | ------------------------------------- |
|
||||
| `ips` | [`[]net.IP`](./data-types.md#net-ip-1) or `nil` | IP list for the corresponding address |
|
||||
|
||||
**Behavior**
|
||||
|
||||
Returns `nil` if there is no outbound, the target address is invalid, or it is a domain name.
|
||||
|
||||
#### GetLocalIPs
|
||||
|
||||
```lua
|
||||
local ips = ctx:GetLocalIPs()
|
||||
```
|
||||
|
||||
Gets the local IPs of the inbound connection.
|
||||
|
||||
**Parameters**
|
||||
|
||||
No additional parameters.
|
||||
|
||||
**Return Values**
|
||||
|
||||
| Return value | Type | Description |
|
||||
| ------------ | ----------------------------------------------- | ------------------------------------- |
|
||||
| `ips` | [`[]net.IP`](./data-types.md#net-ip-1) or `nil` | IP list for the corresponding address |
|
||||
|
||||
**Behavior**
|
||||
|
||||
Returns `nil` if there is no inbound, the local address is invalid, or it is not an IP address.
|
||||
|
||||
#### GetAttributes
|
||||
|
||||
```lua
|
||||
local attributes = ctx:GetAttributes()
|
||||
```
|
||||
|
||||
Gets the current request's attributes.
|
||||
|
||||
**Parameters**
|
||||
|
||||
No additional parameters.
|
||||
|
||||
**Return Values**
|
||||
|
||||
| Return value | Type | Description |
|
||||
| ------------ | ------------------- | ------------------------------ |
|
||||
| `attributes` | `map[string]string` | Always `userdata`; never `nil` |
|
||||
|
||||
**Behavior**
|
||||
|
||||
Read attributes with `attributes[key]`, where `key` must be a string. An existing key returns a string (possibly `""`); a missing key returns `nil`.
|
||||
|
||||
See the [`attrs`](../../../config/routing.md#ruleobject) field in the routing configuration for the meaning and use of attributes.
|
||||
|
||||
**Example**
|
||||
|
||||
```lua
|
||||
local attributes = ctx:GetAttributes()
|
||||
if attributes[":method"] == "GET" then
|
||||
return "direct", "lua-get"
|
||||
end
|
||||
```
|
||||
@@ -0,0 +1,139 @@
|
||||
# xray.dns
|
||||
|
||||
Provides interfaces for DNS queries and upstream server access.
|
||||
|
||||
```lua
|
||||
local dns = require("xray.dns")
|
||||
```
|
||||
|
||||
## API Index
|
||||
|
||||
| Category | Member | Description |
|
||||
| -------- | ----------------------------------------------------- | ---------------------------------- |
|
||||
| Function | [`dns.Query(domain, ipv4, ipv6, fake)`](#dns-query) | Query through Xray's DNS client |
|
||||
| Field | [`dns.Servers`](#dns-servers) | Upstream server array |
|
||||
| Object | [`serverObj`](#server) | A single DNS upstream server |
|
||||
| Field | [`serverObj.ID`](#id) | Server identifier |
|
||||
| Method | [`serverObj:Query(domain, ipv4, ipv6, fake)`](#query) | Query a specific upstream directly |
|
||||
|
||||
## Functions
|
||||
|
||||
### dns.Query
|
||||
|
||||
```lua
|
||||
local ips, ttl, err = dns.Query(domain, ipv4, ipv6, fake)
|
||||
```
|
||||
|
||||
Queries a domain through Xray's DNS client. Unavailable in the DNS Hook, because the script takes over that very query flow.
|
||||
|
||||
**Parameters**
|
||||
|
||||
| Parameter | Type | Description |
|
||||
| --------- | --------- | -------------------------------- |
|
||||
| `domain` | `string` | Domain to query |
|
||||
| `ipv4` | `boolean` | Whether IPv4 queries are allowed |
|
||||
| `ipv6` | `boolean` | Whether IPv6 queries are allowed |
|
||||
| `fake` | `boolean` | Whether FakeDNS is allowed |
|
||||
|
||||
**Return Values**
|
||||
|
||||
| Return value | Type | Description |
|
||||
| ------------ | ----------------------------------------------- | ----------------------------------------------------------- |
|
||||
| `ips` | [`[]net.IP`](./data-types.md#net-ip-1) or `nil` | Query results |
|
||||
| `ttl` | `number` | TTL in seconds, in the range `0 .. 4294967295`; never `nil` |
|
||||
| `err` | [`error`](./data-types.md#error) or `nil` | Query error; `nil` means no error |
|
||||
|
||||
**Behavior**
|
||||
|
||||
All four parameters must be passed explicitly with the correct types; otherwise a Lua exception is raised.
|
||||
|
||||
The query follows the client's global query-type restrictions, Hosts, and upstream configuration. If a [DNS script](../guide/dns.md) is configured, the query is also handled by that script. System DNS is used if built-in DNS is not configured.
|
||||
|
||||
Returns a non-empty IP slice on success; returns `nil, 0, err` if the query fails or no address is available.
|
||||
|
||||
**Example**
|
||||
|
||||
```lua
|
||||
local ips, ttl, err = dns.Query("example.com", true, true, false)
|
||||
if err == nil and ips ~= nil and #ips > 0 then
|
||||
local firstIP = ips[1]:String()
|
||||
end
|
||||
```
|
||||
|
||||
## Fields
|
||||
|
||||
### dns.Servers
|
||||
|
||||
```lua
|
||||
local servers = dns.Servers
|
||||
```
|
||||
|
||||
| Name | Type | Description |
|
||||
| ------------- | ------- | ----------------------------------------------------------------------- |
|
||||
| `dns.Servers` | `table` | Array of [`serverObj`](#server), indexed from 1, in configuration order |
|
||||
|
||||
When built-in DNS is used, the list contains the configured upstreams. If DNS or upstream servers are not configured, the list contains only `localhost` (system DNS).
|
||||
|
||||
Use `ipairs` to iterate over the list; `servers[i]` is `nil` for an out-of-range index.
|
||||
|
||||
**Example**
|
||||
|
||||
```lua
|
||||
for _, serverObj in ipairs(dns.Servers) do
|
||||
local id = serverObj.ID
|
||||
end
|
||||
```
|
||||
|
||||
## Objects
|
||||
|
||||
### server
|
||||
|
||||
`serverObj` represents a DNS upstream server, including its identifier and query method.
|
||||
|
||||
| Property | Description |
|
||||
| ------------------ | ------------------------------------------------- |
|
||||
| Go implementation | `luaDNSServer` in `app/dns` |
|
||||
| Lua representation | `table` |
|
||||
| Obtained from | An array element of [`dns.Servers`](#dns-servers) |
|
||||
|
||||
#### ID
|
||||
|
||||
```lua
|
||||
local id = serverObj.ID
|
||||
```
|
||||
|
||||
| Name | Type | Description |
|
||||
| -------------- | -------- | ----------------------------------------------------------------------------------------------------- |
|
||||
| `serverObj.ID` | `string` | The `id` field in the [DNS server configuration](../../../config/dns.md#dnsserverobject); never `nil` |
|
||||
|
||||
If `id` is omitted, it is `""`, including for servers configured as strings. The automatically added system DNS upstream has ID `"localhost"`.
|
||||
|
||||
If you need to distinguish servers by ID, configure unique values yourself; the core does not check for duplicates.
|
||||
|
||||
#### Query
|
||||
|
||||
```lua
|
||||
local ips, ttl, err = serverObj:Query(domain, ipv4, ipv6, fake)
|
||||
```
|
||||
|
||||
Queries the upstream represented by `serverObj`.
|
||||
|
||||
**Parameters and Return Values**
|
||||
|
||||
Parameter requirements and return-value types are the same as for [`dns.Query`](#dns-query).
|
||||
|
||||
**Behavior**
|
||||
|
||||
A normally configured upstream returns a non-empty IP slice, TTL, and `nil` on success. If the query fails, no address meets the conditions, or FakeDNS is queried with `fake == false`, returns `nil, 0, err`.
|
||||
|
||||
Queries this server directly, bypassing global Hosts and the DNS script. The server's own `queryStrategy`, cache, `timeoutMs`, `clientIP`, `tag`, and `expectedIPs` / `unexpectedIPs` with their corresponding `actPrior` / `actUnprior` settings still take effect.
|
||||
|
||||
**Example**
|
||||
|
||||
Return the first upstream's query result directly from the DNS Hook:
|
||||
|
||||
```lua
|
||||
function HandleDNSQuery(domain, ipv4, ipv6, fake)
|
||||
return dns.Servers[1]:Query(domain, ipv4, ipv6, fake)
|
||||
end
|
||||
```
|
||||
@@ -0,0 +1,344 @@
|
||||
# xray.geodata
|
||||
|
||||
Provides rule-based matching for domains and IPs.
|
||||
|
||||
```lua
|
||||
local geodata = require("xray.geodata")
|
||||
```
|
||||
|
||||
## API Index
|
||||
|
||||
| Category | Member | Description |
|
||||
| -------- | ---------------------------------------------------------------- | ---------------------------------------- |
|
||||
| Function | [`geodata.BuildDomainMatcher(...)`](#geodata-builddomainmatcher) | Create a `DomainMatcher` |
|
||||
| Function | [`geodata.BuildIPMatcher(...)`](#geodata-buildipmatcher) | Create an `IPMatcher` |
|
||||
| Object | [`DomainMatcher`](#domainmatcher) | Domain matcher |
|
||||
| Method | [`domainMatcherObj:MatchAny(domain)`](#matchany) | Check whether a domain matches any rule |
|
||||
| Method | [`domainMatcherObj:Match(domain)`](#match) | Get the indices of matching domain rules |
|
||||
| Object | [`IPMatcher`](#ipmatcher) | IP matcher |
|
||||
| Method | [`ipMatcherObj:Match(ip)`](#match-1) | Match a single IP |
|
||||
| Method | [`ipMatcherObj:AnyMatch(ips)`](#anymatch) | Check whether any IP matches |
|
||||
| Method | [`ipMatcherObj:Matches(ips)`](#matches) | Check whether the entire IP list matches |
|
||||
| Method | [`ipMatcherObj:FilterIPs(ips)`](#filterips) | Separate matching and non-matching IPs |
|
||||
| Method | [`ipMatcherObj:SetReverse(reverse)`](#setreverse) | Set the reverse flag |
|
||||
| Method | [`ipMatcherObj:ToggleReverse()`](#togglereverse) | Toggle the reverse flag |
|
||||
|
||||
## Functions
|
||||
|
||||
### geodata.BuildDomainMatcher
|
||||
|
||||
```lua
|
||||
local domainMatcherObj = geodata.BuildDomainMatcher(rule1, rule2, ...)
|
||||
```
|
||||
|
||||
Creates a [`DomainMatcher`](#domainmatcher) from domain rules.
|
||||
|
||||
**Parameters**
|
||||
|
||||
| Parameter | Type | Description / Requirements |
|
||||
| ------------------- | -------- | ------------------------------------------------------- |
|
||||
| `rule1, rule2, ...` | `string` | Required; at least one domain rule, passed individually |
|
||||
|
||||
**Return Values**
|
||||
|
||||
| Return value | Type | Description |
|
||||
| ------------------ | --------------------------------- | ------------------------- |
|
||||
| `domainMatcherObj` | [`DomainMatcher`](#domainmatcher) | A domain matcher instance |
|
||||
|
||||
**Behavior**
|
||||
|
||||
Raises a Lua exception if no rules are supplied, an argument has the wrong type, or rule parsing, resource loading, or construction fails.
|
||||
|
||||
Supports `domain:`, `full:`, `keyword:`, `regexp:`, `dotless:`, `geosite:`, and `ext:` (also written as `ext-domain:` / `ext-site:`). See [routing domain rules](../../../config/routing.md#ruleobject) for the formats. GeoSite files are loaded from the [resource directory](../../../config/env.md#resource-file-path).
|
||||
|
||||
Strings without a prefix use `domain:` rules by default.
|
||||
|
||||
Except for regular expressions, rules are converted to lowercase during construction.
|
||||
|
||||
**Example**
|
||||
|
||||
```lua
|
||||
local sitesObj = geodata.BuildDomainMatcher(
|
||||
"example.com", "full:other.example"
|
||||
)
|
||||
```
|
||||
|
||||
### geodata.BuildIPMatcher
|
||||
|
||||
```lua
|
||||
local ipMatcherObj = geodata.BuildIPMatcher(rule1, rule2, ...)
|
||||
```
|
||||
|
||||
Creates an [`IPMatcher`](#ipmatcher) from IP rules.
|
||||
|
||||
**Parameters**
|
||||
|
||||
| Parameter | Type | Description / Requirements |
|
||||
| ------------------- | -------- | --------------------------------------------------- |
|
||||
| `rule1, rule2, ...` | `string` | Required; at least one IP rule, passed individually |
|
||||
|
||||
**Return Values**
|
||||
|
||||
| Return value | Type | Description |
|
||||
| -------------- | ------------------------- | ---------------------- |
|
||||
| `ipMatcherObj` | [`IPMatcher`](#ipmatcher) | An IP matcher instance |
|
||||
|
||||
**Behavior**
|
||||
|
||||
Raises a Lua exception if no rules are supplied, an argument has the wrong type, a rule is an empty string, or rule parsing, resource loading, or construction fails.
|
||||
|
||||
Supports IPs, CIDR, `geoip:`, `ext:` (also written as `ext-ip:`), and `!` reverse rules. See [routing IP rules](../../../config/routing.md#ruleobject) for how rules are combined. GeoIP files are loaded from the [resource directory](../../../config/env.md#resource-file-path).
|
||||
|
||||
**Example**
|
||||
|
||||
```lua
|
||||
local privateIPsObj = geodata.BuildIPMatcher(
|
||||
"10.0.0.0/8", "192.168.0.0/16", "fc00::/7"
|
||||
)
|
||||
```
|
||||
|
||||
## Objects
|
||||
|
||||
### DomainMatcher
|
||||
|
||||
| Property | Description |
|
||||
| ------------------ | ------------------------------------------------------------------------------------ |
|
||||
| Underlying Go type | An implementation of the `geodata.DomainMatcher` interface |
|
||||
| Lua representation | `userdata` |
|
||||
| Obtained from | The return value of [`geodata.BuildDomainMatcher(...)`](#geodata-builddomainmatcher) |
|
||||
|
||||
**Common Conventions**
|
||||
|
||||
Matching methods take one domain string. `nil`, omitted arguments, incorrect types, or an incorrect argument count raise a Lua exception.
|
||||
|
||||
The input domain is not automatically converted to lowercase; the script must do this as needed.
|
||||
|
||||
#### MatchAny
|
||||
|
||||
```lua
|
||||
local matched = domainMatcherObj:MatchAny(domain)
|
||||
```
|
||||
|
||||
Checks whether a domain matches at least one rule.
|
||||
|
||||
**Parameters**
|
||||
|
||||
| Parameter | Type | Description / Requirements |
|
||||
| --------- | -------- | -------------------------- |
|
||||
| `domain` | `string` | Required; domain to match |
|
||||
|
||||
**Return Values**
|
||||
|
||||
| Return value | Type | Description |
|
||||
| ------------ | --------- | ------------------------------------ |
|
||||
| `matched` | `boolean` | `true` if matched, otherwise `false` |
|
||||
|
||||
**Example**
|
||||
|
||||
```lua
|
||||
local matched = sitesObj:MatchAny("www.example.com")
|
||||
```
|
||||
|
||||
#### Match
|
||||
|
||||
```lua
|
||||
local indices = domainMatcherObj:Match(domain)
|
||||
```
|
||||
|
||||
Gets the indices of rules matched by the domain.
|
||||
|
||||
**Parameters**
|
||||
|
||||
| Parameter | Type | Description / Requirements |
|
||||
| --------- | -------- | -------------------------- |
|
||||
| `domain` | `string` | Required; domain to match |
|
||||
|
||||
**Return Values**
|
||||
|
||||
| Return value | Type | Description |
|
||||
| ------------ | --------------------------------------------- | ---------------------------------------------------------------------- |
|
||||
| `indices` | [`[]uint32`](./data-types.md#uint32) or `nil` | Matching rule indices; `nil` or a slice of length 0 if nothing matches |
|
||||
|
||||
**Behavior**
|
||||
|
||||
Rule indices start at **0** and correspond to the order of rules supplied during construction. Result order is not guaranteed, and duplicate indices may occur. Entries expanded from GeoSite retain the index of their parent rule.
|
||||
|
||||
If you store the same rules in a Lua array, use `rules[ruleNumber + 1]` to get the original rule. See [Data Types](./data-types.md#slice) for slice access.
|
||||
|
||||
**Example**
|
||||
|
||||
```lua
|
||||
local indices = sitesObj:Match("www.example.com")
|
||||
if indices ~= nil then
|
||||
for i = 1, #indices do
|
||||
local ruleNumber = indices[i]
|
||||
end
|
||||
end
|
||||
```
|
||||
|
||||
### IPMatcher
|
||||
|
||||
| Property | Description |
|
||||
| ------------------ | ---------------------------------------------------------------------------- |
|
||||
| Underlying Go type | An implementation of the `geodata.IPMatcher` interface |
|
||||
| Lua representation | `userdata` |
|
||||
| Obtained from | The return value of [`geodata.BuildIPMatcher(...)`](#geodata-buildipmatcher) |
|
||||
|
||||
**Common Conventions**
|
||||
|
||||
For a single IP, use [`net.IP`](./data-types.md#net-ip). For matching or filtering lists, see [IP List Input](./data-types.md#ip-list-input). IP address strings are not automatically parsed into `net.IP`.
|
||||
|
||||
Matching and filtering methods all accept explicit `nil`. Omitted arguments, incorrect types, or an incorrect argument count raise a Lua exception.
|
||||
|
||||
#### Match
|
||||
|
||||
```lua
|
||||
local matched = ipMatcherObj:Match(ip)
|
||||
```
|
||||
|
||||
Checks whether a single IP matches the rules.
|
||||
|
||||
**Parameters**
|
||||
|
||||
| Parameter | Type | Description / Requirements |
|
||||
| --------- | ------------------------------------------- | ----------------------------------- |
|
||||
| `ip` | [`net.IP`](./data-types.md#net-ip) or `nil` | Required; must be passed explicitly |
|
||||
|
||||
**Return Values**
|
||||
|
||||
| Return value | Type | Description |
|
||||
| ------------ | --------- | ---------------------------------------------------------------- |
|
||||
| `matched` | `boolean` | `true` if a valid IP matches; `false` for `nil` or an invalid IP |
|
||||
|
||||
**Behavior**
|
||||
|
||||
Passing `""` or an empty table `{}` is treated as an empty IP and returns `false`. Strings are not parsed as IP address text.
|
||||
|
||||
#### AnyMatch
|
||||
|
||||
```lua
|
||||
local matched = ipMatcherObj:AnyMatch(ips)
|
||||
```
|
||||
|
||||
Checks whether at least one valid IP matches the rules.
|
||||
|
||||
**Parameters**
|
||||
|
||||
| Parameter | Type | Description / Requirements |
|
||||
| --------- | ---------------------------------------------- | ----------------------------------------------------- |
|
||||
| `ips` | [IP List Input](./data-types.md#ip-list-input) | Required; must be passed explicitly; `nil` is allowed |
|
||||
|
||||
**Return Values**
|
||||
|
||||
| Return value | Type | Description |
|
||||
| ------------ | --------- | -------------------------------------------------------------------------------------- |
|
||||
| `matched` | `boolean` | `true` if at least one valid IP matches; `false` for `nil`, an empty list, or no match |
|
||||
|
||||
#### Matches
|
||||
|
||||
```lua
|
||||
local matched = ipMatcherObj:Matches(ips)
|
||||
```
|
||||
|
||||
Checks whether the entire IP list matches the rules.
|
||||
|
||||
**Parameters**
|
||||
|
||||
| Parameter | Type | Description / Requirements |
|
||||
| --------- | ---------------------------------------------- | ----------------------------------------------------- |
|
||||
| `ips` | [IP List Input](./data-types.md#ip-list-input) | Required; must be passed explicitly; `nil` is allowed |
|
||||
|
||||
**Return Values**
|
||||
|
||||
| Return value | Type | Description |
|
||||
| ------------ | --------- | ----------------------------------------------------------------------------------------------------------------------------- |
|
||||
| `matched` | `boolean` | `true` if the entire list meets the matching requirements; `false` for `nil`, an empty list, or a list containing invalid IPs |
|
||||
|
||||
**Behavior**
|
||||
|
||||
Combined rules may contain multiple internal matchers. A single internal matcher must match the entire list.
|
||||
|
||||
For example, with `geodata.BuildIPMatcher("192.168.1.0/24", "geoip:us")`, a list containing both `192.168.1.1` and `8.8.8.8` makes `Matches` return `false`: the first IP matches the custom CIDR, and the second matches `geoip:us`, but these belong to different internal matchers, and neither matches the entire list.
|
||||
|
||||
If you only need to check whether any address matches, use [`AnyMatch`](#anymatch).
|
||||
|
||||
#### FilterIPs
|
||||
|
||||
```lua
|
||||
local matched, unmatched = ipMatcherObj:FilterIPs(ips)
|
||||
```
|
||||
|
||||
Separates valid IPs into matching and non-matching groups.
|
||||
|
||||
**Parameters**
|
||||
|
||||
| Parameter | Type | Description / Requirements |
|
||||
| --------- | ---------------------------------------------- | ----------------------------------------------------- |
|
||||
| `ips` | [IP List Input](./data-types.md#ip-list-input) | Required; must be passed explicitly; `nil` is allowed |
|
||||
|
||||
**Return Values**
|
||||
|
||||
| Return value | Type | Description |
|
||||
| ------------ | -------------------------------------- | ----------------------------- |
|
||||
| `matched` | [`[]net.IP`](./data-types.md#net-ip-1) | Matching IPs; never `nil` |
|
||||
| `unmatched` | [`[]net.IP`](./data-types.md#net-ip-1) | Non-matching IPs; never `nil` |
|
||||
|
||||
**Behavior**
|
||||
|
||||
A group with no results is an empty slice. For `nil`, an empty list, or a list containing only invalid IPs, both groups are empty slices.
|
||||
|
||||
Invalid IPs are discarded, and result order is not guaranteed to match the input order.
|
||||
|
||||
#### SetReverse
|
||||
|
||||
```lua
|
||||
ipMatcherObj:SetReverse(reverse)
|
||||
```
|
||||
|
||||
Sets the reverse flag on each internal matcher.
|
||||
|
||||
**Parameters**
|
||||
|
||||
| Parameter | Type | Description / Requirements |
|
||||
| --------- | --------- | -------------------------------------------------------------- |
|
||||
| `reverse` | `boolean` | Required; `true` enables reverse matching, `false` disables it |
|
||||
|
||||
**Return Values**
|
||||
|
||||
No return values.
|
||||
|
||||
**Behavior**
|
||||
|
||||
`nil`, omission, or an incorrect type raises a Lua exception. This operation overrides each internal matcher's existing reverse flag, including flags specified with `!` in rules. Reverse matching applies only to address families present in the original rules and does not match invalid IPs.
|
||||
|
||||
The resulting reverse state is stored in the current matcher object. If later Hook calls reuse that object, the modified state remains in effect. See [State in Pooled Instances](../guide/lifecycle.md#state-in-pooled-instances).
|
||||
|
||||
**Example**
|
||||
|
||||
Reversing `10.0.0.0/8` matches valid IPv4 addresses outside that range:
|
||||
|
||||
```lua
|
||||
local ipMatcherObj = geodata.BuildIPMatcher("10.0.0.0/8")
|
||||
ipMatcherObj:SetReverse(true)
|
||||
```
|
||||
|
||||
#### ToggleReverse
|
||||
|
||||
```lua
|
||||
ipMatcherObj:ToggleReverse()
|
||||
```
|
||||
|
||||
Toggles the reverse flag on each internal matcher.
|
||||
|
||||
**Parameters**
|
||||
|
||||
No additional parameters.
|
||||
|
||||
**Return Values**
|
||||
|
||||
No return values.
|
||||
|
||||
**Behavior**
|
||||
|
||||
Each internal matcher in combined rules toggles its own reverse flag. This is not equivalent to logically negating the combined result.
|
||||
The resulting reverse state is stored in the current matcher object. If later Hook calls reuse that object, the modified state remains in effect. See [State in Pooled Instances](../guide/lifecycle.md#state-in-pooled-instances).
|
||||
@@ -0,0 +1,69 @@
|
||||
# xray.log
|
||||
|
||||
Writes logs to Xray's logging system.
|
||||
|
||||
```lua
|
||||
local log = require("xray.log")
|
||||
```
|
||||
|
||||
## API Index
|
||||
|
||||
| Category | Member | Description |
|
||||
| -------- | ---------------------------------- | ------------------- |
|
||||
| Function | [`log.Debug(...)`](#log-debug) | Write a debug log |
|
||||
| Function | [`log.Info(...)`](#log-info) | Write an info log |
|
||||
| Function | [`log.Warning(...)`](#log-warning) | Write a warning log |
|
||||
| Function | [`log.Error(...)`](#log-error) | Write an error log |
|
||||
|
||||
## Functions
|
||||
|
||||
### log.Debug
|
||||
|
||||
```lua
|
||||
log.Debug(...)
|
||||
```
|
||||
|
||||
Writes a log at the `debug` level.
|
||||
|
||||
**Parameters**
|
||||
|
||||
| Parameter | Type | Description / Requirements |
|
||||
| --------- | ------------- | --------------------------------------------------------- |
|
||||
| `...` | Any Lua value | Optional; any number of log values, concatenated in order |
|
||||
|
||||
**Return Values**
|
||||
|
||||
No return values.
|
||||
|
||||
**Argument Conversion**
|
||||
|
||||
Accepts no arguments, `nil`, `""`, `{}`, and empty slices. Arguments are converted to text and concatenated in order, without automatically inserting spaces or separators.
|
||||
|
||||
- [`error`](./data-types.md#error) values are converted to their error messages first.
|
||||
- Other values with a function-valued `__tostring` use that conversion; exceptions it raises propagate to the caller.
|
||||
- Remaining values use their default string representation. `nil` becomes `"nil"`, and empty strings add no text. Tables and slices are not expanded into element lists.
|
||||
|
||||
**Output Conditions**
|
||||
|
||||
Logs are prefixed with the calling script's filename. Whether they are written depends on [`log.loglevel`](../../../config/log.md#logobject). For example, at `warning`, `Debug` and `Info` produce no output and do not invoke their arguments' `__tostring` conversions.
|
||||
|
||||
None of the four functions returns `err`. Assigning a call to a variable gives `nil`, which cannot be used to determine whether a log was successfully written.
|
||||
|
||||
**Example**
|
||||
|
||||
```lua
|
||||
log.Info("Query domain: ", domain)
|
||||
log.Warning("Upstream query failed: ", err)
|
||||
```
|
||||
|
||||
### log.Info
|
||||
|
||||
Same as above.
|
||||
|
||||
### log.Warning
|
||||
|
||||
Same as above.
|
||||
|
||||
### log.Error
|
||||
|
||||
Same as above.
|
||||
@@ -0,0 +1,119 @@
|
||||
# xray.router
|
||||
|
||||
Provides constants and helpers for [routing scripts](../guide/routing.md). See [Routing Hook](./hook-routing.md) for request parameters and context.
|
||||
|
||||
```lua
|
||||
local router = require("xray.router")
|
||||
```
|
||||
|
||||
## API Index
|
||||
|
||||
| Category | Member | Description |
|
||||
| -------- | ---------------------------------------------------------- | --------------------------------------------------- |
|
||||
| Constant | [`router.NetworkUnknown`](#constants) | Unknown network |
|
||||
| Constant | [`router.NetworkTCP`](#constants) | TCP |
|
||||
| Constant | [`router.NetworkUDP`](#constants) | UDP |
|
||||
| Constant | [`router.NetworkUNIX`](#constants) | UNIX |
|
||||
| Constant | [`router.LocalOS`](#constants) | Runtime platform |
|
||||
| Function | [`router:PickOutbound(balancerTag)`](#router-pickoutbound) | Select an outbound through a balancer |
|
||||
| Function | [`router.FindProcess(ctx)`](#router-findprocess) | Find the process associated with a local connection |
|
||||
|
||||
## Constants
|
||||
|
||||
| Name | Type | Value / Description |
|
||||
| ----------------------- | -------- | --------------------------------------------------------------------------- |
|
||||
| `router.NetworkUnknown` | `number` | `0`, unknown network |
|
||||
| `router.NetworkTCP` | `number` | `2`, TCP |
|
||||
| `router.NetworkUDP` | `number` | `3`, UDP |
|
||||
| `router.NetworkUNIX` | `number` | `4`, UNIX |
|
||||
| `router.LocalOS` | `string` | Current runtime platform, for example `"windows"`, `"linux"`, or `"darwin"` |
|
||||
|
||||
Compare the [Routing Hook](./hook-routing.md#handleroute)'s `network` parameter with the network constants:
|
||||
|
||||
```lua
|
||||
if network == router.NetworkUDP then
|
||||
return "direct", "lua-udp"
|
||||
end
|
||||
```
|
||||
|
||||
## Functions
|
||||
|
||||
### router:PickOutbound
|
||||
|
||||
```lua
|
||||
local outboundTag, err = router:PickOutbound(balancerTag)
|
||||
```
|
||||
|
||||
Selects an outbound through the specified balancer.
|
||||
|
||||
**Parameters**
|
||||
|
||||
| Parameter | Type | Description / Requirements |
|
||||
| ------------- | -------- | ---------------------------------------------------------------------------------------------------------------- |
|
||||
| `balancerTag` | `string` | The `tag` of the balancer to use, configured in [`routing.balancers`](../../../config/routing.md#balancerobject) |
|
||||
|
||||
**Return Values**
|
||||
|
||||
| Return value | Type | Description |
|
||||
| ------------- | ----------------------------------------- | --------------------------------------------------- |
|
||||
| `outboundTag` | `string` or `nil` | On success, the selected outbound's non-empty `tag` |
|
||||
| `err` | [`error`](./data-types.md#error) or `nil` | `nil` indicates success |
|
||||
|
||||
**Behavior**
|
||||
|
||||
A string must be passed explicitly; otherwise a Lua exception is raised. An empty string `""` looks up an empty `tag`.
|
||||
|
||||
Check `err` before using the result. If the balancer does not exist, returns `nil, err`; if it exists but selects no outbound, returns `"", err`. When the configured `fallbackTag` takes effect, returns that outbound tag and `nil`.
|
||||
|
||||
**Example**
|
||||
|
||||
The following uses the balancer with `tag` `"proxy-pool"` and passes on the result according to the [Routing Hook's return-value convention](./hook-routing.md#return-values):
|
||||
|
||||
```lua
|
||||
local outboundTag, err = router:PickOutbound("proxy-pool")
|
||||
return outboundTag, "lua-balance", err
|
||||
```
|
||||
|
||||
See the [Routing Scripts guide](../guide/routing.md#relationship-with-routing-configuration) for a complete example.
|
||||
|
||||
### router.FindProcess
|
||||
|
||||
```lua
|
||||
local pid, name, path, err = router.FindProcess(ctx)
|
||||
```
|
||||
|
||||
Finds the process on the local machine associated with the current connection.
|
||||
|
||||
**Parameters**
|
||||
|
||||
| Parameter | Type | Description / Requirements |
|
||||
| --------- | ------------------------------------------------------ | --------------------------------------------------- |
|
||||
| `ctx` | [`routing.Context`](./hook-routing.md#routing-context) | Current request context, passed to the Routing Hook |
|
||||
|
||||
**Return Values**
|
||||
|
||||
| Return value | Type | Description |
|
||||
| ------------ | ----------------------------------------- | -------------------------------------- |
|
||||
| `pid` | `number` | Process ID; usually `0` if unavailable |
|
||||
| `name` | `string` | Process name; `""` if unavailable |
|
||||
| `path` | `string` | Executable path; `""` if unavailable |
|
||||
| `err` | [`error`](./data-types.md#error) or `nil` | `nil` indicates success |
|
||||
|
||||
**Behavior**
|
||||
|
||||
A valid routing context must be passed explicitly; otherwise a Lua exception is raised. Returns an error if there is no source IP, the network type is not TCP/UDP, lookup fails, or the platform is unsupported.
|
||||
|
||||
`pid`, `name`, and `path` are never `nil`, but may retain partial information on failure; check `err` before using them. If there is no source IP or the network type is unsupported, returns `0, "", "", err`; otherwise returns the platform lookup results unchanged.
|
||||
|
||||
The lookup uses the first source IP and source port. If target IPs are available, it also passes the first target IP and target port. The platform lookup implementation determines the exact matching behavior.
|
||||
|
||||
Lookup capabilities depend on the platform and permissions. Windows, Linux, and macOS provide built-in implementations. Android requires the runtime environment to register a lookup implementation, and even successful results may have no name or path. iOS and other unsupported platforms return an error.
|
||||
|
||||
**Example**
|
||||
|
||||
```lua
|
||||
local pid, name, path, err = router.FindProcess(ctx)
|
||||
if err == nil and name == "curl" then
|
||||
return "direct", "lua-process"
|
||||
end
|
||||
```
|
||||
Reference in New Issue
Block a user