aboutsummaryrefslogtreecommitdiff
path: root/MediaBrowser.Server.Implementations/EntryPoints/ExternalPortForwarding.cs
blob: a7e5396eb99dae5bcc99d64840adf67b16f0073a (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
using MediaBrowser.Controller;
using MediaBrowser.Controller.Configuration;
using MediaBrowser.Controller.Dlna;
using MediaBrowser.Controller.Plugins;
using MediaBrowser.Model.Logging;
using System;
using System.Collections.Generic;
using System.Globalization;
using System.IO;
using System.Net;
using System.Text;
using MediaBrowser.Common.Threading;
using Open.Nat;
using System.Threading;
using System.Threading.Tasks;

namespace MediaBrowser.Server.Implementations.EntryPoints
{
    public class ExternalPortForwarding : IServerEntryPoint
    {
        private readonly IServerApplicationHost _appHost;
        private readonly ILogger _logger;
        private readonly IServerConfigurationManager _config;
        private readonly ISsdpHandler _ssdp;
        private CancellationTokenSource _currentCancellationTokenSource;
        private TimeSpan _interval = TimeSpan.FromHours(1);

        public ExternalPortForwarding(ILogManager logmanager, IServerApplicationHost appHost, IServerConfigurationManager config, ISsdpHandler ssdp)
        {
            _logger = logmanager.GetLogger("PortMapper");
            _appHost = appHost;
            _config = config;
            _ssdp = ssdp;

            _config.ConfigurationUpdated += _config_ConfigurationUpdated;
        }

        private void _config_ConfigurationUpdated(object sender, EventArgs e)
        {
        }

        public void Run()
        {
            Discover();
        }

        private async void Discover()
        {
            if (!_config.Configuration.EnableUPnP)
            {
                return;
            }

            var discoverer = new NatDiscoverer();

            var cancellationTokenSource = new CancellationTokenSource(10000);
            _currentCancellationTokenSource = cancellationTokenSource;

            try
            {
                var device = await discoverer.DiscoverDeviceAsync(PortMapper.Upnp, cancellationTokenSource).ConfigureAwait(false);

                await CreateRules(device).ConfigureAwait(false);
            }
            catch (OperationCanceledException)
            {

            }
            catch (Exception ex)
            {
                _logger.ErrorException("Error discovering NAT devices", ex);
            }
            finally
            {
                _currentCancellationTokenSource = null;
            }

            if (_config.Configuration.EnableUPnP)
            {
                await Task.Delay(_interval).ConfigureAwait(false);
                Discover();
            }
        }

        private async Task CreateRules(NatDevice device)
        {
            // On some systems the device discovered event seems to fire repeatedly
            // This check will help ensure we're not trying to port map the same device over and over

            await CreatePortMap(device, _appHost.HttpPort, _config.Configuration.PublicPort).ConfigureAwait(false);
            await CreatePortMap(device, _appHost.HttpsPort, _config.Configuration.PublicHttpsPort).ConfigureAwait(false);
        }

        private async Task CreatePortMap(NatDevice device, int privatePort, int publicPort)
        {
            _logger.Debug("Creating port map on port {0}", privatePort);

            try
            {
                await device.CreatePortMapAsync(new Mapping(Protocol.Tcp, privatePort, publicPort, _appHost.Name)).ConfigureAwait(false);
            }
            catch (Exception ex)
            {
                _logger.ErrorException("Error creating port map", ex);
            }
        }

        public void Dispose()
        {
            DisposeNat();
        }

        private void DisposeNat()
        {
            if (_currentCancellationTokenSource != null)
            {
                try
                {
                    _currentCancellationTokenSource.Cancel();
                }
                catch (Exception ex)
                {
                    _logger.ErrorException("Error calling _currentCancellationTokenSource.Cancel", ex);
                }
            }
        }
    }
}