portforwarder_rs/
port_forwarder.rs

1// Copyright (c) 2018 Paweł Zmarzły
2//
3// This software is released under the MIT License.
4// https://opensource.org/licenses/MIT
5
6use igd::{self, AddAnyPortError, AddPortError, RemovePortError, SearchError, SearchOptions};
7use std::net::{Ipv4Addr, SocketAddr, SocketAddrV4};
8
9pub use igd::PortMappingProtocol;
10
11#[derive(Debug)]
12pub struct Port {
13    pub proto: PortMappingProtocol,
14    pub num: u16,
15}
16
17impl PartialEq for Port {
18    fn eq(&self, other: &Port) -> bool {
19        self.proto == other.proto && self.num == other.num
20    }
21}
22
23#[derive(Debug)]
24pub struct Forwarder {
25    pub gateway: igd::Gateway,
26    pub network_interface: Ipv4Addr,
27    pub open_ports: Vec<Port>,
28}
29
30pub fn create_forwarder(interface_ip: Ipv4Addr) -> Result<Forwarder, SearchError> {
31    igd::search_gateway(SearchOptions {
32        bind_addr: SocketAddr::V4(SocketAddrV4::new(interface_ip, 0)),
33        ..Default::default()
34    })
35    .map(|gateway| Forwarder {
36        gateway,
37        network_interface: interface_ip,
38        open_ports: Vec::new(),
39    })
40}
41
42pub fn create_forwarder_from_any<I>(interface_ips: I) -> Result<Forwarder, Vec<SearchError>>
43where
44    I: IntoIterator<Item = Ipv4Addr>,
45{
46    let mut errors = Vec::new();
47    for interface_ip in interface_ips {
48        match create_forwarder(interface_ip) {
49            Ok(forwarder) => return Ok(forwarder),
50            Err(error) => errors.push(error),
51        }
52    }
53    Err(errors)
54}
55
56impl Forwarder {
57    pub fn forward_any_port(
58        &mut self,
59        local_port: u16,
60        proto: PortMappingProtocol,
61        name: &str,
62    ) -> Result<u16, AddAnyPortError> {
63        self.gateway
64            .add_any_port(
65                proto,
66                SocketAddrV4::new(self.network_interface, local_port),
67                0,
68                name,
69            )
70            .map(|port| {
71                self.open_ports.push(Port { proto, num: port });
72                port
73            })
74    }
75    pub fn forward_port(
76        &mut self,
77        local_port: u16,
78        remote_port: u16,
79        proto: PortMappingProtocol,
80        name: &str,
81    ) -> Result<(), AddPortError> {
82        self.gateway
83            .add_port(
84                proto,
85                remote_port,
86                SocketAddrV4::new(self.network_interface, local_port),
87                0,
88                name,
89            )
90            .map(|()| {
91                self.open_ports.push(Port {
92                    proto,
93                    num: remote_port,
94                });
95            })
96    }
97    pub fn remove_port(
98        &mut self,
99        remote_port: u16,
100        proto: PortMappingProtocol,
101    ) -> Result<(), RemovePortError> {
102        if let Some(pos) = self.open_ports.iter().position(|el| {
103            *el == Port {
104                proto,
105                num: remote_port,
106            }
107        }) {
108            self.open_ports.remove(pos);
109        } else {
110            println!(
111                "Remote port {} {} was not opened by this Forwarder! Removing anyway...",
112                proto, remote_port
113            );
114        }
115        self.gateway.remove_port(proto, remote_port)
116    }
117}
118
119impl Drop for Forwarder {
120    fn drop(&mut self) {
121        println!("Closing open ports...");
122        for port in &self.open_ports {
123            let num = port.num;
124            let proto = port.proto;
125            println!("Closing port {} {}...", proto, num);
126            if self.gateway.remove_port(proto, num).is_err() {
127                println!("Failed to close port {} {} on exit!", proto, num);
128            }
129        }
130    }
131}