Skip to content

Commit

Permalink
outbound/direct: allow to specify an outbound interface
Browse files Browse the repository at this point in the history
  • Loading branch information
eycorsican committed Jul 19, 2024
1 parent 250b910 commit 6f9d426
Show file tree
Hide file tree
Showing 10 changed files with 181 additions and 41 deletions.
21 changes: 15 additions & 6 deletions leaf/src/app/outbound/manager.rs
Original file line number Diff line number Diff line change
Expand Up @@ -112,12 +112,21 @@ impl OutboundManager {

let h: AnyOutboundHandler = match outbound.protocol.as_str() {
#[cfg(feature = "outbound-direct")]
"direct" => HandlerBuilder::default()
.tag(tag.clone())
.color(colored::Color::Green)
.stream_handler(Box::new(direct::StreamHandler))
.datagram_handler(Box::new(direct::DatagramHandler))
.build(),
"direct" => {
let settings =
config::DirectOutboundSettings::parse_from_bytes(&outbound.settings)
.map_err(|e| anyhow!("invalid [{}] outbound settings: {}", &tag, e))?;
HandlerBuilder::default()
.tag(tag.clone())
.color(colored::Color::Green)
.stream_handler(Box::new(direct::StreamHandler {
interface: settings.interface.clone(),
}))
.datagram_handler(Box::new(direct::DatagramHandler {
interface: settings.interface.clone(),
}))
.build()
}
#[cfg(feature = "outbound-drop")]
"drop" => HandlerBuilder::default()
.tag(tag.clone())
Expand Down
2 changes: 1 addition & 1 deletion leaf/src/app/outbound/selector_cache.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
// This file is generated by rust-protobuf 3.4.0. Do not edit
// .proto file is parsed by protoc 26.1
// .proto file is parsed by protoc 27.1
// @generated

// https://github.com/rust-lang/rust-clippy/issues/702
Expand Down
16 changes: 12 additions & 4 deletions leaf/src/config/conf/config.rs
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,8 @@ pub struct General {
pub struct Proxy {
pub tag: String,
pub protocol: String,
pub interface: String,

pub interface: Option<String>,

// common
pub address: Option<String>,
Expand Down Expand Up @@ -88,7 +89,7 @@ impl Default for Proxy {
Proxy {
tag: "".to_string(),
protocol: "".to_string(),
interface: (&*crate::option::UNSPECIFIED_BIND_ADDR).ip().to_string(),
interface: None,
address: None,
port: None,
encrypt_method: Some("chacha20-ietf-poly1305".to_string()),
Expand Down Expand Up @@ -452,7 +453,7 @@ pub fn from_lines(lines: Vec<io::Result<String>>) -> Result<Config> {
}
"quic" => proxy.quic = if v == "true" { Some(true) } else { Some(false) },
"interface" => {
proxy.interface = v.to_string();
proxy.interface = Some(v.to_string());
}
_ => {}
}
Expand Down Expand Up @@ -851,7 +852,14 @@ pub fn to_internal(conf: &mut Config) -> Result<internal::Config> {
outbound.protocol = ext_protocol.to_string();
outbound.tag = ext_proxy.tag.clone();
match outbound.protocol.as_str() {
"direct" | "drop" => {
"drop" => {
outbounds.push(outbound);
}
"direct" => {
let mut settings = internal::DirectOutboundSettings::new();
settings.interface = ext_proxy.interface.clone();
let settings = settings.write_to_bytes().unwrap();
outbound.settings = settings;
outbounds.push(outbound);
}
"redirect" => {
Expand Down
2 changes: 1 addition & 1 deletion leaf/src/config/geosite.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
// This file is generated by rust-protobuf 3.4.0. Do not edit
// .proto file is parsed by protoc 26.1
// .proto file is parsed by protoc 27.1
// @generated

// https://github.com/rust-lang/rust-clippy/issues/702
Expand Down
4 changes: 4 additions & 0 deletions leaf/src/config/internal/config.proto
Original file line number Diff line number Diff line change
Expand Up @@ -88,6 +88,10 @@ message Inbound {
bytes settings = 5;
}

message DirectOutboundSettings {
optional string interface = 1;
}

message RedirectOutboundSettings {
string address = 1;
uint32 port = 2;
Expand Down
92 changes: 91 additions & 1 deletion leaf/src/config/internal/config.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
// This file is generated by rust-protobuf 3.4.0. Do not edit
// .proto file is parsed by protoc 26.1
// .proto file is parsed by protoc 27.1
// @generated

// https://github.com/rust-lang/rust-clippy/issues/702
Expand Down Expand Up @@ -1599,6 +1599,96 @@ impl ::protobuf::Message for Inbound {
}
}

// @@protoc_insertion_point(message:DirectOutboundSettings)
#[derive(PartialEq,Clone,Default,Debug)]
pub struct DirectOutboundSettings {
// message fields
// @@protoc_insertion_point(field:DirectOutboundSettings.interface)
pub interface: ::std::option::Option<::std::string::String>,
// special fields
// @@protoc_insertion_point(special_field:DirectOutboundSettings.special_fields)
pub special_fields: ::protobuf::SpecialFields,
}

impl<'a> ::std::default::Default for &'a DirectOutboundSettings {
fn default() -> &'a DirectOutboundSettings {
<DirectOutboundSettings as ::protobuf::Message>::default_instance()
}
}

impl DirectOutboundSettings {
pub fn new() -> DirectOutboundSettings {
::std::default::Default::default()
}
}

impl ::protobuf::Message for DirectOutboundSettings {
const NAME: &'static str = "DirectOutboundSettings";

fn is_initialized(&self) -> bool {
true
}

fn merge_from(&mut self, is: &mut ::protobuf::CodedInputStream<'_>) -> ::protobuf::Result<()> {
while let Some(tag) = is.read_raw_tag_or_eof()? {
match tag {
10 => {
self.interface = ::std::option::Option::Some(is.read_string()?);
},
tag => {
::protobuf::rt::read_unknown_or_skip_group(tag, is, self.special_fields.mut_unknown_fields())?;
},
};
}
::std::result::Result::Ok(())
}

// Compute sizes of nested messages
#[allow(unused_variables)]
fn compute_size(&self) -> u64 {
let mut my_size = 0;
if let Some(v) = self.interface.as_ref() {
my_size += ::protobuf::rt::string_size(1, &v);
}
my_size += ::protobuf::rt::unknown_fields_size(self.special_fields.unknown_fields());
self.special_fields.cached_size().set(my_size as u32);
my_size
}

fn write_to_with_cached_sizes(&self, os: &mut ::protobuf::CodedOutputStream<'_>) -> ::protobuf::Result<()> {
if let Some(v) = self.interface.as_ref() {
os.write_string(1, v)?;
}
os.write_unknown_fields(self.special_fields.unknown_fields())?;
::std::result::Result::Ok(())
}

fn special_fields(&self) -> &::protobuf::SpecialFields {
&self.special_fields
}

fn mut_special_fields(&mut self) -> &mut ::protobuf::SpecialFields {
&mut self.special_fields
}

fn new() -> DirectOutboundSettings {
DirectOutboundSettings::new()
}

fn clear(&mut self) {
self.interface = ::std::option::Option::None;
self.special_fields.clear();
}

fn default_instance() -> &'static DirectOutboundSettings {
static instance: DirectOutboundSettings = DirectOutboundSettings {
interface: ::std::option::Option::None,
special_fields: ::protobuf::SpecialFields::new(),
};
&instance
}
}

// @@protoc_insertion_point(message:RedirectOutboundSettings)
#[derive(PartialEq,Clone,Default,Debug)]
pub struct RedirectOutboundSettings {
Expand Down
6 changes: 1 addition & 5 deletions leaf/src/option/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -214,11 +214,7 @@ lazy_static! {
let binds = get_env_var_or("OUTBOUND_INTERFACE", "0.0.0.0,::".to_string());
let mut outbound_binds = Vec::new();
for item in binds.split(',').map(str::trim) {
if let Ok(addr) = crate::common::net::parse_bind_addr(item) {
outbound_binds.push(crate::proxy::OutboundBind::Ip(addr));
} else {
outbound_binds.push(crate::proxy::OutboundBind::Interface(item.to_owned()));
}
outbound_binds.push(item.into());
}
outbound_binds
};
Expand Down
6 changes: 4 additions & 2 deletions leaf/src/proxy/direct/datagram.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,12 +4,14 @@ use async_trait::async_trait;

use crate::{proxy::*, session::Session};

pub struct Handler;
pub struct Handler {
pub interface: Option<String>,
}

#[async_trait]
impl OutboundDatagramHandler for Handler {
fn connect_addr(&self) -> OutboundConnect {
OutboundConnect::Direct
OutboundConnect::Direct(self.interface.as_ref().map(|x| x.as_str().into()))
}

fn transport_type(&self) -> DatagramTransportType {
Expand Down
6 changes: 4 additions & 2 deletions leaf/src/proxy/direct/stream.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,12 +4,14 @@ use async_trait::async_trait;

use crate::{proxy::*, session::Session};

pub struct Handler;
pub struct Handler {
pub interface: Option<String>,
}

#[async_trait]
impl OutboundStreamHandler for Handler {
fn connect_addr(&self) -> OutboundConnect {
OutboundConnect::Direct
OutboundConnect::Direct(self.interface.as_ref().map(|x| x.as_str().into()))
}

async fn handle<'a>(
Expand Down
Loading

0 comments on commit 6f9d426

Please sign in to comment.