The UDP relay treated the destination address as packet-scoped while
applying ACL/outbound policy only once when a new session was created.
After an authenticated client opened a UDP session using a permitted
first destination, later packets carrying a different Addr in the same
SessionID were written via the established outbound socket without
re-checking policy, allowing the client to reach destinations that ACL
should reject — including localhost and RFC1918 from the server's
network perspective. See GHSA-vgrc-hq28-p3xp.
Add a no-I/O CheckUDP method to the Outbound / PluggableOutbound
chain. The UDP session entry now consults CheckUDP for every packet
whose destination differs from the session's first one, dropping
rejected packets before WriteTo. Decisions are cached per destination
within the session (bounded at 256 entries with simple eviction) so
steady-state cost is one map lookup per packet and no extra sockets
or dials. CheckUDP propagates through the existing chain:
- aclEngine routes through the matched outbound's CheckUDP, with
aclRejectOutbound returning the rejection error.
- directOutbound / socks5Outbound / speedtestHandler return nil.
- httpOutbound returns errHTTPUDPNotSupported.
- Resolvers (system / dot / doh) run resolve() then forward to
Next.CheckUDP so IP-based ACL rules keep matching.
Regression tests in core/internal/integration_tests/udp_acl_test.go
use an in-package stub Outbound to assert that a rejected destination
is not relayed after the session is opened on a permitted one, and
that multi-destination sessions over permitted addresses still work.
Co-authored-by: Claude Opus 4.7 <noreply@anthropic.com>
290 lines
7.4 KiB
Go
290 lines
7.4 KiB
Go
// Code generated by mockery v2.53.5. DO NOT EDIT.
|
|
|
|
package server
|
|
|
|
import (
|
|
protocol "github.com/apernet/hysteria/core/v2/internal/protocol"
|
|
mock "github.com/stretchr/testify/mock"
|
|
)
|
|
|
|
// mockUDPIO is an autogenerated mock type for the udpIO type
|
|
type mockUDPIO struct {
|
|
mock.Mock
|
|
}
|
|
|
|
type mockUDPIO_Expecter struct {
|
|
mock *mock.Mock
|
|
}
|
|
|
|
func (_m *mockUDPIO) EXPECT() *mockUDPIO_Expecter {
|
|
return &mockUDPIO_Expecter{mock: &_m.Mock}
|
|
}
|
|
|
|
// CheckUDP provides a mock function with given fields: reqAddr
|
|
func (_m *mockUDPIO) CheckUDP(reqAddr string) error {
|
|
ret := _m.Called(reqAddr)
|
|
|
|
if len(ret) == 0 {
|
|
panic("no return value specified for CheckUDP")
|
|
}
|
|
|
|
var r0 error
|
|
if rf, ok := ret.Get(0).(func(string) error); ok {
|
|
r0 = rf(reqAddr)
|
|
} else {
|
|
r0 = ret.Error(0)
|
|
}
|
|
|
|
return r0
|
|
}
|
|
|
|
// mockUDPIO_CheckUDP_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'CheckUDP'
|
|
type mockUDPIO_CheckUDP_Call struct {
|
|
*mock.Call
|
|
}
|
|
|
|
// CheckUDP is a helper method to define mock.On call
|
|
// - reqAddr string
|
|
func (_e *mockUDPIO_Expecter) CheckUDP(reqAddr interface{}) *mockUDPIO_CheckUDP_Call {
|
|
return &mockUDPIO_CheckUDP_Call{Call: _e.mock.On("CheckUDP", reqAddr)}
|
|
}
|
|
|
|
func (_c *mockUDPIO_CheckUDP_Call) Run(run func(reqAddr string)) *mockUDPIO_CheckUDP_Call {
|
|
_c.Call.Run(func(args mock.Arguments) {
|
|
run(args[0].(string))
|
|
})
|
|
return _c
|
|
}
|
|
|
|
func (_c *mockUDPIO_CheckUDP_Call) Return(_a0 error) *mockUDPIO_CheckUDP_Call {
|
|
_c.Call.Return(_a0)
|
|
return _c
|
|
}
|
|
|
|
func (_c *mockUDPIO_CheckUDP_Call) RunAndReturn(run func(string) error) *mockUDPIO_CheckUDP_Call {
|
|
_c.Call.Return(run)
|
|
return _c
|
|
}
|
|
|
|
// Hook provides a mock function with given fields: data, reqAddr
|
|
func (_m *mockUDPIO) Hook(data []byte, reqAddr *string) error {
|
|
ret := _m.Called(data, reqAddr)
|
|
|
|
if len(ret) == 0 {
|
|
panic("no return value specified for Hook")
|
|
}
|
|
|
|
var r0 error
|
|
if rf, ok := ret.Get(0).(func([]byte, *string) error); ok {
|
|
r0 = rf(data, reqAddr)
|
|
} else {
|
|
r0 = ret.Error(0)
|
|
}
|
|
|
|
return r0
|
|
}
|
|
|
|
// mockUDPIO_Hook_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'Hook'
|
|
type mockUDPIO_Hook_Call struct {
|
|
*mock.Call
|
|
}
|
|
|
|
// Hook is a helper method to define mock.On call
|
|
// - data []byte
|
|
// - reqAddr *string
|
|
func (_e *mockUDPIO_Expecter) Hook(data interface{}, reqAddr interface{}) *mockUDPIO_Hook_Call {
|
|
return &mockUDPIO_Hook_Call{Call: _e.mock.On("Hook", data, reqAddr)}
|
|
}
|
|
|
|
func (_c *mockUDPIO_Hook_Call) Run(run func(data []byte, reqAddr *string)) *mockUDPIO_Hook_Call {
|
|
_c.Call.Run(func(args mock.Arguments) {
|
|
run(args[0].([]byte), args[1].(*string))
|
|
})
|
|
return _c
|
|
}
|
|
|
|
func (_c *mockUDPIO_Hook_Call) Return(_a0 error) *mockUDPIO_Hook_Call {
|
|
_c.Call.Return(_a0)
|
|
return _c
|
|
}
|
|
|
|
func (_c *mockUDPIO_Hook_Call) RunAndReturn(run func([]byte, *string) error) *mockUDPIO_Hook_Call {
|
|
_c.Call.Return(run)
|
|
return _c
|
|
}
|
|
|
|
// ReceiveMessage provides a mock function with no fields
|
|
func (_m *mockUDPIO) ReceiveMessage() (*protocol.UDPMessage, error) {
|
|
ret := _m.Called()
|
|
|
|
if len(ret) == 0 {
|
|
panic("no return value specified for ReceiveMessage")
|
|
}
|
|
|
|
var r0 *protocol.UDPMessage
|
|
var r1 error
|
|
if rf, ok := ret.Get(0).(func() (*protocol.UDPMessage, error)); ok {
|
|
return rf()
|
|
}
|
|
if rf, ok := ret.Get(0).(func() *protocol.UDPMessage); ok {
|
|
r0 = rf()
|
|
} else {
|
|
if ret.Get(0) != nil {
|
|
r0 = ret.Get(0).(*protocol.UDPMessage)
|
|
}
|
|
}
|
|
|
|
if rf, ok := ret.Get(1).(func() error); ok {
|
|
r1 = rf()
|
|
} else {
|
|
r1 = ret.Error(1)
|
|
}
|
|
|
|
return r0, r1
|
|
}
|
|
|
|
// mockUDPIO_ReceiveMessage_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'ReceiveMessage'
|
|
type mockUDPIO_ReceiveMessage_Call struct {
|
|
*mock.Call
|
|
}
|
|
|
|
// ReceiveMessage is a helper method to define mock.On call
|
|
func (_e *mockUDPIO_Expecter) ReceiveMessage() *mockUDPIO_ReceiveMessage_Call {
|
|
return &mockUDPIO_ReceiveMessage_Call{Call: _e.mock.On("ReceiveMessage")}
|
|
}
|
|
|
|
func (_c *mockUDPIO_ReceiveMessage_Call) Run(run func()) *mockUDPIO_ReceiveMessage_Call {
|
|
_c.Call.Run(func(args mock.Arguments) {
|
|
run()
|
|
})
|
|
return _c
|
|
}
|
|
|
|
func (_c *mockUDPIO_ReceiveMessage_Call) Return(_a0 *protocol.UDPMessage, _a1 error) *mockUDPIO_ReceiveMessage_Call {
|
|
_c.Call.Return(_a0, _a1)
|
|
return _c
|
|
}
|
|
|
|
func (_c *mockUDPIO_ReceiveMessage_Call) RunAndReturn(run func() (*protocol.UDPMessage, error)) *mockUDPIO_ReceiveMessage_Call {
|
|
_c.Call.Return(run)
|
|
return _c
|
|
}
|
|
|
|
// SendMessage provides a mock function with given fields: _a0, _a1
|
|
func (_m *mockUDPIO) SendMessage(_a0 []byte, _a1 *protocol.UDPMessage) error {
|
|
ret := _m.Called(_a0, _a1)
|
|
|
|
if len(ret) == 0 {
|
|
panic("no return value specified for SendMessage")
|
|
}
|
|
|
|
var r0 error
|
|
if rf, ok := ret.Get(0).(func([]byte, *protocol.UDPMessage) error); ok {
|
|
r0 = rf(_a0, _a1)
|
|
} else {
|
|
r0 = ret.Error(0)
|
|
}
|
|
|
|
return r0
|
|
}
|
|
|
|
// mockUDPIO_SendMessage_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'SendMessage'
|
|
type mockUDPIO_SendMessage_Call struct {
|
|
*mock.Call
|
|
}
|
|
|
|
// SendMessage is a helper method to define mock.On call
|
|
// - _a0 []byte
|
|
// - _a1 *protocol.UDPMessage
|
|
func (_e *mockUDPIO_Expecter) SendMessage(_a0 interface{}, _a1 interface{}) *mockUDPIO_SendMessage_Call {
|
|
return &mockUDPIO_SendMessage_Call{Call: _e.mock.On("SendMessage", _a0, _a1)}
|
|
}
|
|
|
|
func (_c *mockUDPIO_SendMessage_Call) Run(run func(_a0 []byte, _a1 *protocol.UDPMessage)) *mockUDPIO_SendMessage_Call {
|
|
_c.Call.Run(func(args mock.Arguments) {
|
|
run(args[0].([]byte), args[1].(*protocol.UDPMessage))
|
|
})
|
|
return _c
|
|
}
|
|
|
|
func (_c *mockUDPIO_SendMessage_Call) Return(_a0 error) *mockUDPIO_SendMessage_Call {
|
|
_c.Call.Return(_a0)
|
|
return _c
|
|
}
|
|
|
|
func (_c *mockUDPIO_SendMessage_Call) RunAndReturn(run func([]byte, *protocol.UDPMessage) error) *mockUDPIO_SendMessage_Call {
|
|
_c.Call.Return(run)
|
|
return _c
|
|
}
|
|
|
|
// UDP provides a mock function with given fields: reqAddr
|
|
func (_m *mockUDPIO) UDP(reqAddr string) (UDPConn, error) {
|
|
ret := _m.Called(reqAddr)
|
|
|
|
if len(ret) == 0 {
|
|
panic("no return value specified for UDP")
|
|
}
|
|
|
|
var r0 UDPConn
|
|
var r1 error
|
|
if rf, ok := ret.Get(0).(func(string) (UDPConn, error)); ok {
|
|
return rf(reqAddr)
|
|
}
|
|
if rf, ok := ret.Get(0).(func(string) UDPConn); ok {
|
|
r0 = rf(reqAddr)
|
|
} else {
|
|
if ret.Get(0) != nil {
|
|
r0 = ret.Get(0).(UDPConn)
|
|
}
|
|
}
|
|
|
|
if rf, ok := ret.Get(1).(func(string) error); ok {
|
|
r1 = rf(reqAddr)
|
|
} else {
|
|
r1 = ret.Error(1)
|
|
}
|
|
|
|
return r0, r1
|
|
}
|
|
|
|
// mockUDPIO_UDP_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'UDP'
|
|
type mockUDPIO_UDP_Call struct {
|
|
*mock.Call
|
|
}
|
|
|
|
// UDP is a helper method to define mock.On call
|
|
// - reqAddr string
|
|
func (_e *mockUDPIO_Expecter) UDP(reqAddr interface{}) *mockUDPIO_UDP_Call {
|
|
return &mockUDPIO_UDP_Call{Call: _e.mock.On("UDP", reqAddr)}
|
|
}
|
|
|
|
func (_c *mockUDPIO_UDP_Call) Run(run func(reqAddr string)) *mockUDPIO_UDP_Call {
|
|
_c.Call.Run(func(args mock.Arguments) {
|
|
run(args[0].(string))
|
|
})
|
|
return _c
|
|
}
|
|
|
|
func (_c *mockUDPIO_UDP_Call) Return(_a0 UDPConn, _a1 error) *mockUDPIO_UDP_Call {
|
|
_c.Call.Return(_a0, _a1)
|
|
return _c
|
|
}
|
|
|
|
func (_c *mockUDPIO_UDP_Call) RunAndReturn(run func(string) (UDPConn, error)) *mockUDPIO_UDP_Call {
|
|
_c.Call.Return(run)
|
|
return _c
|
|
}
|
|
|
|
// newMockUDPIO creates a new instance of mockUDPIO. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations.
|
|
// The first argument is typically a *testing.T value.
|
|
func newMockUDPIO(t interface {
|
|
mock.TestingT
|
|
Cleanup(func())
|
|
}) *mockUDPIO {
|
|
mock := &mockUDPIO{}
|
|
mock.Mock.Test(t)
|
|
|
|
t.Cleanup(func() { mock.AssertExpectations(t) })
|
|
|
|
return mock
|
|
}
|