| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235 | 
							- //
 
- // Authors:
 
- //   Alan McGovern alan.mcgovern@gmail.com
 
- //   Ben Motmans <ben.motmans@gmail.com>
 
- //   Nicholas Terry <nick.i.terry@gmail.com>
 
- //
 
- // Copyright (C) 2006 Alan McGovern
 
- // Copyright (C) 2007 Ben Motmans
 
- // Copyright (C) 2014 Nicholas Terry
 
- //
 
- // Permission is hereby granted, free of charge, to any person obtaining
 
- // a copy of this software and associated documentation files (the
 
- // "Software"), to deal in the Software without restriction, including
 
- // without limitation the rights to use, copy, modify, merge, publish,
 
- // distribute, sublicense, and/or sell copies of the Software, and to
 
- // permit persons to whom the Software is furnished to do so, subject to
 
- // the following conditions:
 
- //
 
- // The above copyright notice and this permission notice shall be
 
- // included in all copies or substantial portions of the Software.
 
- //
 
- // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
 
- // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
 
- // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
 
- // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
 
- // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
 
- // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
 
- // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
 
- //
 
- using System;
 
- using System.Collections.Generic;
 
- using System.Text;
 
- using System.Net;
 
- using Mono.Nat.Pmp;
 
- using System.Net.NetworkInformation;
 
- using System.Net.Sockets;
 
- using System.Threading.Tasks;
 
- using Microsoft.Extensions.Logging;
 
- using System.Linq;
 
- namespace Mono.Nat
 
- {
 
-     internal class PmpSearcher : ISearcher, IDisposable
 
-     {
 
-         private ILogger _logger;
 
-         private int timeout = 250;
 
-         private DateTime nextSearch;
 
-         public event EventHandler<DeviceEventArgs> DeviceFound;
 
-         public PmpSearcher(ILogger logger)
 
-         {
 
-             _logger = logger;
 
-             CreateSocketsAndAddGateways();
 
-         }
 
-         public void Dispose()
 
-         {
 
-             var list = sockets.ToList();
 
-             sockets.Clear();
 
-             foreach (var s in list)
 
-             {
 
-                 using (s)
 
-                 {
 
-                     s.Close();
 
-                 }
 
-             }
 
-         }
 
-         private List<UdpClient> sockets;
 
-         private Dictionary<UdpClient, List<IPEndPoint>> gatewayLists;
 
-         private void CreateSocketsAndAddGateways()
 
-         {
 
-             sockets = new List<UdpClient>();
 
-             gatewayLists = new Dictionary<UdpClient, List<IPEndPoint>>();
 
-             try
 
-             {
 
-                 foreach (var n in NetworkInterface.GetAllNetworkInterfaces())
 
-                 {
 
-                     if (n.OperationalStatus != OperationalStatus.Up && n.OperationalStatus != OperationalStatus.Unknown)
 
-                         continue;
 
-                     IPInterfaceProperties properties = n.GetIPProperties();
 
-                     var gatewayList = new List<IPEndPoint>();
 
-                     foreach (GatewayIPAddressInformation gateway in properties.GatewayAddresses)
 
-                     {
 
-                         if (gateway.Address.AddressFamily == AddressFamily.InterNetwork)
 
-                         {
 
-                             gatewayList.Add(new IPEndPoint(gateway.Address, PmpConstants.ServerPort));
 
-                         }
 
-                     }
 
-                     if (gatewayList.Count == 0)
 
-                     {
 
-                         /* Mono on OSX doesn't give any gateway addresses, so check DNS entries */
 
-                         foreach (var gw2 in properties.DnsAddresses)
 
-                         {
 
-                             if (gw2.AddressFamily == AddressFamily.InterNetwork)
 
-                             {
 
-                                 gatewayList.Add(new IPEndPoint(gw2, PmpConstants.ServerPort));
 
-                             }
 
-                         }
 
-                         foreach (UnicastIPAddressInformation unicast in properties.UnicastAddresses)
 
-                         {
 
-                             if (/*unicast.DuplicateAddressDetectionState == DuplicateAddressDetectionState.Preferred
 
-                                 && unicast.AddressPreferredLifetime != UInt32.MaxValue
 
-                                 && */unicast.Address.AddressFamily == AddressFamily.InterNetwork)
 
-                             {
 
-                                 var bytes = unicast.Address.GetAddressBytes();
 
-                                 bytes[3] = 1;
 
-                                 gatewayList.Add(new IPEndPoint(new IPAddress(bytes), PmpConstants.ServerPort));
 
-                             }
 
-                         }
 
-                     }
 
-                     if (gatewayList.Count > 0)
 
-                     {
 
-                         foreach (var address in properties.UnicastAddresses)
 
-                         {
 
-                             if (address.Address.AddressFamily == AddressFamily.InterNetwork)
 
-                             {
 
-                                 UdpClient client;
 
-                                 try
 
-                                 {
 
-                                     client = new UdpClient(new IPEndPoint(address.Address, 0));
 
-                                 }
 
-                                 catch (SocketException)
 
-                                 {
 
-                                     continue; // Move on to the next address.
 
-                                 }
 
-                                 gatewayLists.Add(client, gatewayList);
 
-                                 sockets.Add(client);
 
-                             }
 
-                         }
 
-                     }
 
-                 }
 
-             }
 
-             catch (Exception)
 
-             {
 
-                 // NAT-PMP does not use multicast, so there isn't really a good fallback.
 
-             }
 
-         }
 
-         public async void Search()
 
-         {
 
-             foreach (UdpClient s in sockets)
 
-             {
 
-                 try
 
-                 {
 
-                     await Search(s).ConfigureAwait(false);
 
-                 }
 
-                 catch
 
-                 {
 
-                     // Ignore any search errors
 
-                 }
 
-             }
 
-         }
 
-         async Task Search(UdpClient client)
 
-         {
 
-             // Sort out the time for the next search first. The spec says the
 
-             // timeout should double after each attempt. Once it reaches 64 seconds
 
-             // (and that attempt fails), assume no devices available
 
-             nextSearch = DateTime.Now.AddMilliseconds(timeout);
 
-             timeout *= 2;
 
-             // We've tried 9 times as per spec, try searching again in 5 minutes
 
-             if (timeout == 128 * 1000)
 
-             {
 
-                 timeout = 250;
 
-                 nextSearch = DateTime.Now.AddMinutes(10);
 
-                 return;
 
-             }
 
-             // The nat-pmp search message. Must be sent to GatewayIP:53531
 
-             byte[] buffer = new byte[] { PmpConstants.Version, PmpConstants.OperationCode };
 
-             foreach (IPEndPoint gatewayEndpoint in gatewayLists[client])
 
-             {
 
-                 await client.SendAsync(buffer, buffer.Length, gatewayEndpoint).ConfigureAwait(false);
 
-             }
 
-         }
 
-         bool IsSearchAddress(IPAddress address)
 
-         {
 
-             foreach (var gatewayList in gatewayLists.Values)
 
-                 foreach (var gatewayEndpoint in gatewayList)
 
-                     if (gatewayEndpoint.Address.Equals(address))
 
-                         return true;
 
-             return false;
 
-         }
 
-         public void Handle(IPAddress localAddress, byte[] response, IPEndPoint endpoint)
 
-         {
 
-             if (!IsSearchAddress(endpoint.Address))
 
-                 return;
 
-             if (response.Length != 12)
 
-                 return;
 
-             if (response[0] != PmpConstants.Version)
 
-                 return;
 
-             if (response[1] != PmpConstants.ServerNoop)
 
-                 return;
 
-             int errorcode = IPAddress.NetworkToHostOrder(BitConverter.ToInt16(response, 2));
 
-             if (errorcode != 0)
 
-                 _logger.LogDebug("Non zero error: {0}", errorcode);
 
-             var publicIp = new IPAddress(new byte[] { response[8], response[9], response[10], response[11] });
 
-             nextSearch = DateTime.Now.AddMinutes(5);
 
-             timeout = 250;
 
-             OnDeviceFound(new DeviceEventArgs(new PmpNatDevice(endpoint.Address, publicIp, _logger)));
 
-         }
 
-         public DateTime NextSearch
 
-         {
 
-             get { return nextSearch; }
 
-         }
 
-         private void OnDeviceFound(DeviceEventArgs args)
 
-         {
 
-             if (DeviceFound != null)
 
-                 DeviceFound(this, args);
 
-         }
 
-         public NatProtocol Protocol
 
-         {
 
-             get { return NatProtocol.Pmp; }
 
-         }
 
-     }
 
- }
 
 
  |