Skip to content

Commit f9ae19c

Browse files
committed
Support transparent proxy related unix socket options as possible
1 parent 6923954 commit f9ae19c

File tree

3 files changed

+240
-43
lines changed

3 files changed

+240
-43
lines changed

src/socket.rs

-42
Original file line numberDiff line numberDiff line change
@@ -1186,48 +1186,6 @@ impl Socket {
11861186
}
11871187
}
11881188

1189-
/// Get the value of the `IP_TRANSPARENT` option on this socket.
1190-
///
1191-
/// For more information about this option, see [`set_ip_transparent`].
1192-
///
1193-
/// [`set_ip_transparent`]: Socket::set_ip_transparent
1194-
#[cfg(all(feature = "all", target_os = "linux"))]
1195-
#[cfg_attr(docsrs, doc(cfg(all(feature = "all", target_os = "linux"))))]
1196-
pub fn ip_transparent(&self) -> io::Result<bool> {
1197-
unsafe {
1198-
getsockopt::<c_int>(self.as_raw(), sys::IPPROTO_IP, libc::IP_TRANSPARENT)
1199-
.map(|transparent| transparent != 0)
1200-
}
1201-
}
1202-
1203-
/// Set the value of the `IP_TRANSPARENT` option on this socket.
1204-
///
1205-
/// Setting this boolean option enables transparent proxying
1206-
/// on this socket. This socket option allows the calling
1207-
/// application to bind to a nonlocal IP address and operate
1208-
/// both as a client and a server with the foreign address as
1209-
/// the local endpoint. NOTE: this requires that routing be
1210-
/// set up in a way that packets going to the foreign address
1211-
/// are routed through the TProxy box (i.e., the system
1212-
/// hosting the application that employs the IP_TRANSPARENT
1213-
/// socket option). Enabling this socket option requires
1214-
/// superuser privileges (the `CAP_NET_ADMIN` capability).
1215-
///
1216-
/// TProxy redirection with the iptables TPROXY target also
1217-
/// requires that this option be set on the redirected socket.
1218-
#[cfg(all(feature = "all", target_os = "linux"))]
1219-
#[cfg_attr(docsrs, doc(cfg(all(feature = "all", target_os = "linux"))))]
1220-
pub fn set_ip_transparent(&self, transparent: bool) -> io::Result<()> {
1221-
unsafe {
1222-
setsockopt(
1223-
self.as_raw(),
1224-
sys::IPPROTO_IP,
1225-
libc::IP_TRANSPARENT,
1226-
transparent as c_int,
1227-
)
1228-
}
1229-
}
1230-
12311189
/// Join a multicast group using `IP_ADD_MEMBERSHIP` option on this socket.
12321190
///
12331191
/// This function specifies a new multicast group for this socket to join.

src/sys/unix.rs

+212
Original file line numberDiff line numberDiff line change
@@ -3063,6 +3063,218 @@ impl crate::Socket {
30633063
)
30643064
}
30653065
}
3066+
3067+
/// Get the value of the `IP_TRANSPARENT` option on this socket.
3068+
///
3069+
/// For more information about this option, see [`set_ip_transparent`].
3070+
///
3071+
/// [`set_ip_transparent`]: crate::Socket::set_ip_transparent
3072+
#[cfg(all(
3073+
feature = "all",
3074+
any(target_os = "android", target_os = "linux", target_os = "fuchsia")
3075+
))]
3076+
#[cfg_attr(
3077+
docsrs,
3078+
doc(cfg(all(
3079+
feature = "all",
3080+
any(target_os = "android", target_os = "linux", target_os = "fuchsia")
3081+
)))
3082+
)]
3083+
pub fn ip_transparent(&self) -> io::Result<bool> {
3084+
unsafe {
3085+
getsockopt::<c_int>(self.as_raw(), libc::IPPROTO_IP, libc::IP_TRANSPARENT)
3086+
.map(|transparent| transparent != 0)
3087+
}
3088+
}
3089+
3090+
/// Set the value of the `IP_TRANSPARENT` option on this socket.
3091+
///
3092+
/// Setting this boolean option enables transparent proxying
3093+
/// on this socket. This socket option allows the calling
3094+
/// application to bind to a nonlocal IP address and operate
3095+
/// both as a client and a server with the foreign address as
3096+
/// the local endpoint. NOTE: this requires that routing be
3097+
/// set up in a way that packets going to the foreign address
3098+
/// are routed through the TProxy box (i.e., the system
3099+
/// hosting the application that employs the IP_TRANSPARENT
3100+
/// socket option). Enabling this socket option requires
3101+
/// superuser privileges (the `CAP_NET_ADMIN` capability).
3102+
///
3103+
/// TProxy redirection with the iptables TPROXY target also
3104+
/// requires that this option be set on the redirected socket.
3105+
#[cfg(all(
3106+
feature = "all",
3107+
any(target_os = "android", target_os = "linux", target_os = "fuchsia")
3108+
))]
3109+
#[cfg_attr(
3110+
docsrs,
3111+
doc(cfg(all(
3112+
feature = "all",
3113+
any(target_os = "android", target_os = "linux", target_os = "fuchsia")
3114+
)))
3115+
)]
3116+
pub fn set_ip_transparent(&self, transparent: bool) -> io::Result<()> {
3117+
unsafe {
3118+
setsockopt(
3119+
self.as_raw(),
3120+
libc::IPPROTO_IP,
3121+
libc::IP_TRANSPARENT,
3122+
transparent as c_int,
3123+
)
3124+
}
3125+
}
3126+
3127+
/// Get the value of the `IPV6_TRANSPARENT` option on this socket.
3128+
///
3129+
/// For more information about this option, see [`set_ip_transparent_v6`].
3130+
///
3131+
/// [`set_ip_transparent_v6`]: crate::Socket::set_ip_transparent_v6
3132+
#[cfg(all(feature = "all", any(target_os = "android", target_os = "linux")))]
3133+
#[cfg_attr(
3134+
docsrs,
3135+
doc(cfg(all(feature = "all", any(target_os = "android", target_os = "linux"))))
3136+
)]
3137+
pub fn ip_transparent_v6(&self) -> io::Result<bool> {
3138+
unsafe {
3139+
getsockopt::<c_int>(self.as_raw(), libc::IPPROTO_IPV6, libc::IPV6_TRANSPARENT)
3140+
.map(|transparent| transparent != 0)
3141+
}
3142+
}
3143+
3144+
/// Set the value of the `IPV6_TRANSPARENT` option on this socket.
3145+
///
3146+
/// Setting this boolean option enables transparent proxying
3147+
/// on this socket. This socket option allows the calling
3148+
/// application to bind to a nonlocal IP address and operate
3149+
/// both as a client and a server with the foreign address as
3150+
/// the local endpoint. NOTE: this requires that routing be
3151+
/// set up in a way that packets going to the foreign address
3152+
/// are routed through the TProxy box (i.e., the system
3153+
/// hosting the application that employs the IPV6_TRANSPARENT
3154+
/// socket option). Enabling this socket option requires
3155+
/// superuser privileges (the `CAP_NET_ADMIN` capability).
3156+
///
3157+
/// TProxy redirection with the iptables TPROXY target also
3158+
/// requires that this option be set on the redirected socket.
3159+
#[cfg(all(feature = "all", any(target_os = "android", target_os = "linux")))]
3160+
#[cfg_attr(
3161+
docsrs,
3162+
doc(cfg(all(feature = "all", any(target_os = "android", target_os = "linux"))))
3163+
)]
3164+
pub fn set_ip_transparent_v6(&self, transparent: bool) -> io::Result<()> {
3165+
unsafe {
3166+
setsockopt(
3167+
self.as_raw(),
3168+
libc::IPPROTO_IPV6,
3169+
libc::IPV6_TRANSPARENT,
3170+
transparent as c_int,
3171+
)
3172+
}
3173+
}
3174+
3175+
/// Get the value of the `SO_BINDANY` option on this socket.
3176+
///
3177+
/// For more information about this option, see [`set_so_bindany`].
3178+
///
3179+
/// [`set_so_bindany`]: crate::Socket::set_so_bindany
3180+
#[cfg(all(feature = "all", target_os = "openbsd"))]
3181+
#[cfg_attr(docsrs, doc(cfg(all(feature = "all", target_os = "openbsd"))))]
3182+
pub fn so_bindany(&self) -> io::Result<bool> {
3183+
unsafe {
3184+
getsockopt::<c_int>(self.as_raw(), libc::SOL_SOCKET, libc::SO_BINDANY)
3185+
.map(|bindany| bindany != 0)
3186+
}
3187+
}
3188+
3189+
/// Set the value of the `SO_BINDANY` option on this socket.
3190+
///
3191+
/// SO_BINDANY allows the socket to be bound to addresses which are not
3192+
/// local to the machine, so it can be used to make a transparent proxy.
3193+
/// Note that this option is limited to the superuser. In order to
3194+
/// receive packets for these addresses, SO_BINDANY needs to be combined
3195+
/// with matching outgoing pf(4) rules with the divert-reply parameter.
3196+
#[cfg(all(feature = "all", target_os = "openbsd"))]
3197+
#[cfg_attr(docsrs, doc(cfg(all(feature = "all", target_os = "openbsd"))))]
3198+
pub fn set_so_bindany(&self, bindany: bool) -> io::Result<()> {
3199+
unsafe {
3200+
setsockopt(
3201+
self.as_raw(),
3202+
libc::IPPROTO_IP,
3203+
libc::SO_BINDANY,
3204+
bindany as c_int,
3205+
)
3206+
}
3207+
}
3208+
3209+
/// Get the value of the `IP_BINDANY` option on this socket.
3210+
///
3211+
/// For more information about this option, see [`set_ip_bindany`].
3212+
///
3213+
/// [`set_ip_bindany`]: crate::Socket::set_ip_bindany
3214+
#[cfg(all(feature = "all", target_os = "freebsd"))]
3215+
#[cfg_attr(docsrs, doc(cfg(all(feature = "all", target_os = "freebsd"))))]
3216+
pub fn ip_bindany(&self) -> io::Result<bool> {
3217+
unsafe {
3218+
getsockopt::<c_int>(self.as_raw(), libc::IPPROTO_IP, libc::IP_BINDANY)
3219+
.map(|bindany| bindany != 0)
3220+
}
3221+
}
3222+
3223+
/// Set the value of the `IP_BINDANY` option on this socket.
3224+
///
3225+
/// If the IP_BINDANY option is enabled on a SOCK_STREAM, SOCK_DGRAM or a
3226+
/// SOCK_RAW socket, one can bind(2) to any address, even one not bound to
3227+
/// any available network interface in the system. This functionality (in
3228+
/// conjunction with special firewall rules) can be used for implementing a
3229+
/// transparent proxy. The PRIV_NETINET_BINDANY privilege is needed to set
3230+
/// this option.
3231+
#[cfg(all(feature = "all", target_os = "freebsd"))]
3232+
#[cfg_attr(docsrs, doc(cfg(all(feature = "all", target_os = "freebsd"))))]
3233+
pub fn set_ip_bindany(&self, bindany: bool) -> io::Result<()> {
3234+
unsafe {
3235+
setsockopt(
3236+
self.as_raw(),
3237+
libc::IPPROTO_IP,
3238+
libc::IP_BINDANY,
3239+
bindany as c_int,
3240+
)
3241+
}
3242+
}
3243+
3244+
/// Get the value of the `IPV6_BINDANY` option on this socket.
3245+
///
3246+
/// For more information about this option, see [`set_ip_bindany_v6`].
3247+
///
3248+
/// [`set_ip_bindany_v6`]: crate::Socket::set_ip_bindany_v6
3249+
#[cfg(all(feature = "all", target_os = "freebsd"))]
3250+
#[cfg_attr(docsrs, doc(cfg(all(feature = "all", target_os = "freebsd"))))]
3251+
pub fn ip_bindany_v6(&self) -> io::Result<bool> {
3252+
unsafe {
3253+
getsockopt::<c_int>(self.as_raw(), libc::IPPROTO_IPV6, libc::IPV6_BINDANY)
3254+
.map(|bindany| bindany != 0)
3255+
}
3256+
}
3257+
3258+
/// Set the value of the `IPV6_BINDANY` option on this socket.
3259+
///
3260+
/// If the IPV6_BINDANY option is enabled on a SOCK_STREAM, SOCK_DGRAM or a
3261+
/// SOCK_RAW socket, one can bind(2) to any address, even one not bound to
3262+
/// any available network interface in the system. This functionality (in
3263+
/// conjunction with special firewall rules) can be used for implementing a
3264+
/// transparent proxy. The PRIV_NETINET_BINDANY privilege is needed to set
3265+
/// this option.
3266+
#[cfg(all(feature = "all", target_os = "freebsd"))]
3267+
#[cfg_attr(docsrs, doc(cfg(all(feature = "all", target_os = "freebsd"))))]
3268+
pub fn set_ip_bindany_v6(&self, bindany: bool) -> io::Result<()> {
3269+
unsafe {
3270+
setsockopt(
3271+
self.as_raw(),
3272+
libc::IPPROTO_IPV6,
3273+
libc::IPV6_BINDANY,
3274+
bindany as c_int,
3275+
)
3276+
}
3277+
}
30663278
}
30673279

30683280
/// See [`Socket::dccp_available_ccids`].

tests/socket.rs

+28-1
Original file line numberDiff line numberDiff line change
@@ -1350,12 +1350,39 @@ test!(
13501350
mss,
13511351
set_mss(256)
13521352
);
1353-
#[cfg(all(feature = "all", target_os = "linux"))]
1353+
#[cfg(all(
1354+
feature = "all",
1355+
any(target_os = "android", target_os = "linux", target_os = "fuchsia")
1356+
))]
13541357
test!(
13551358
#[ignore = "setting `IP_TRANSPARENT` requires the `CAP_NET_ADMIN` capability (works when running as root)"]
13561359
ip_transparent,
13571360
set_ip_transparent(true)
13581361
);
1362+
#[cfg(all(feature = "all", any(target_os = "android", target_os = "linux")))]
1363+
test!(
1364+
#[ignore = "setting `IPV6_TRANSPARENT` requires the `CAP_NET_ADMIN` capability (works when running as root)"]
1365+
ip_transparent_v6,
1366+
set_ip_transparent_v6(true)
1367+
);
1368+
#[cfg(all(feature = "all", target_os = "openbsd"))]
1369+
test!(
1370+
#[ignore = "setting `SO_BINDANY` is limited to the superuser"]
1371+
so_bindany,
1372+
set_so_bindany(true)
1373+
);
1374+
#[cfg(all(feature = "all", target_os = "freebsd"))]
1375+
test!(
1376+
#[ignore = "setting `IP_BINDANY` requires the `PRIV_NETINET_BINDANY` privilege (works when running as root)"]
1377+
ip_bindany,
1378+
set_ip_bindany(true)
1379+
);
1380+
#[cfg(all(feature = "all", target_os = "freebsd"))]
1381+
test!(
1382+
#[ignore = "setting `IPV6_BINDANY` requires the `PRIV_NETINET_BINDANY` privilege (works when running as root)"]
1383+
ip_bindany_v6,
1384+
set_ip_bindany_v6(true)
1385+
);
13591386
#[cfg(all(feature = "all", any(target_os = "fuchsia", target_os = "linux")))]
13601387
test!(
13611388
#[ignore = "setting `SO_MARK` requires the `CAP_NET_ADMIN` capability (works when running as root)"]

0 commit comments

Comments
 (0)