PeerIPHelper.cs source code in C# .NET

Source code for the .NET framework in C#

                        

Code:

/ WCF / WCF / 3.5.30729.1 / untmp / Orcas / SP / ndp / cdf / src / WCF / ServiceModel / System / ServiceModel / Channels / PeerIPHelper.cs / 1 / PeerIPHelper.cs

                            //------------------------------------------------------------ 
// Copyright (c) Microsoft Corporation.  All rights reserved.
//-----------------------------------------------------------
namespace System.ServiceModel.Channels
{ 
    using System.Collections.Generic;
    using System.ServiceModel; 
    using System.Collections.ObjectModel; 
    using System.Net;
    using System.Net.NetworkInformation; 
    using System.Net.Sockets;
    using System.Threading;
    using System.Runtime.InteropServices;
 

    // IP address helper class for multi-homing support 
    class PeerIPHelper 
    {
        public event EventHandler AddressChanged; 

        bool isOpen;
        readonly IPAddress listenAddress;       // To listen on a single IP address.
        IPAddress[] localAddresses; 
        AddressChangeHelper addressChangeHelper;
        Socket ipv6Socket; 
        object thisLock; 

        const uint Six2FourPrefix       = 0x220; 
        const uint TeredoPrefix         = 0x00000120;
        const uint IsatapIdentifier     = 0xfe5e0000;

        enum AddressType 
        {
            Unknown, 
            Teredo, 
            Isatap,
            Six2Four 
        }

        public PeerIPHelper()
        { 
            Initialize();
        } 
 
        public PeerIPHelper(IPAddress listenAddress)
        { 
            if (!(listenAddress != null))
            {
                DiagnosticUtility.DebugAssert("listenAddress expected to be non-null");
                throw DiagnosticUtility.ExceptionUtility.ThrowHelperInternal(false); 
            }
            this.listenAddress = listenAddress; 
            Initialize(); 
        }
 
        void Initialize()
        {
            this.localAddresses = new IPAddress[0];
            this.thisLock = new object(); 
        }
 
        // NOTE: This is just for suites -- to skip timeout usage 
        internal int AddressChangeWaitTimeout
        { 
            set { this.addressChangeHelper.Timeout = value; }
        }

        object ThisLock 
        {
            get { return this.thisLock; } 
        } 

        // Compares if the specified collection matches our local cache. Return true on mismatch. 
        public bool AddressesChanged(ReadOnlyCollection addresses)
        {
            bool changed = false;
            lock (ThisLock) 
            {
                if (addresses.Count != this.localAddresses.Length) 
                { 
                    changed = true;
                } 
                else
                {
                    // If every specified addresses exist in the cache, addresses haven't changed
                    foreach (IPAddress address in this.localAddresses) 
                    {
                        if (!addresses.Contains(address)) 
                        { 
                            changed = true;
                            break; 
                        }
                    }
                }
            } 

            return changed; 
        } 

        // Since scope ID of IPAddress is mutable, you want to be able to clone an IP address 
        public static IPAddress CloneAddress(IPAddress source, bool maskScopeId)
        {
            IPAddress clone = null;
            if (maskScopeId || V4Address(source)) 
                clone = new IPAddress(source.GetAddressBytes());
            else 
                clone = new IPAddress(source.GetAddressBytes(), source.ScopeId); 
            return clone;
        } 

        // Since scope ID of IPAddress is mutable, you want to be able to clone IP addresses in an array or collection
        static ReadOnlyCollection CloneAddresses(IPAddress[] sourceArray)
        { 
            IPAddress [] cloneArray = new IPAddress[sourceArray.Length];
            for (int i = 0; i < sourceArray.Length; i++) 
            { 
                cloneArray[i] = CloneAddress(sourceArray[i], false);
            } 
            return new ReadOnlyCollection(cloneArray);
        }

        public static ReadOnlyCollection CloneAddresses(ReadOnlyCollection sourceCollection, bool maskScopeId) 
        {
            IPAddress[] cloneArray = new IPAddress[sourceCollection.Count]; 
            for (int i = 0; i < sourceCollection.Count; i++) 
            {
                cloneArray[i] = CloneAddress(sourceCollection[i], maskScopeId); 
            }
            return new ReadOnlyCollection(cloneArray);
        }
 
        // When listening on a specific IP address, creates an array containing just that address
        static IPAddress [] CreateAddressArray(IPAddress address) 
        { 
            IPAddress[] addressArray = new IPAddress[1];
            addressArray[0] = CloneAddress(address, false); 
            return addressArray;
        }

        public void Close() 
        {
            if (this.isOpen) 
            { 
                lock (ThisLock)
                { 
                    if (this.isOpen)
                    {
                        this.addressChangeHelper.Unregister();
                        if (this.ipv6Socket != null) 
                        {
                            this.ipv6Socket.Close(); 
                        } 
                        this.isOpen = false;
                        this.addressChangeHelper = null; 
                    }
                }
            }
        } 

        // Retrieve the IP addresses configured on the machine. 
        IPAddress [] GetAddresses() 
        {
            List addresses = new List(); 
            List temporaryAddresses = new List();

            if (this.listenAddress != null)     // single local address scenario?
            { 
                // Check if the specified address is configured
                if (ValidAddress(this.listenAddress)) 
                { 
                    return (CreateAddressArray(this.listenAddress));
                } 
            }

            // Walk the interfaces
            NetworkInterface[] networkIfs = NetworkInterface.GetAllNetworkInterfaces(); 
            foreach (NetworkInterface networkIf in networkIfs)
            { 
                if (!ValidInterface(networkIf)) 
                {
                    continue; 
                }

                // Process each unicast address for the interface. Pick at most one IPv4 and one IPv6 address.
                // Add remaining eligible addresses on the list to surplus list. We can add these back to the list 
                // being returned, if there is room.
                IPInterfaceProperties properties = networkIf.GetIPProperties(); 
                if (properties != null) 
                {
                    foreach (UnicastIPAddressInformation unicastAddress in properties.UnicastAddresses) 
                    {
                        if (NonTransientAddress(unicastAddress))
                        {
                            if(unicastAddress.SuffixOrigin == SuffixOrigin.Random) 
                                temporaryAddresses.Add(unicastAddress.Address);
                            else 
 	                            addresses.Add(unicastAddress.Address); 
                        }
 
                    }

                }
 
            }
            if(addresses.Count > 0) 
                return ReorderAddresses(addresses); 
            else
                return temporaryAddresses.ToArray(); 
        }


        internal static IPAddress[] ReorderAddresses(IEnumerable sourceAddresses) 
        {
            List result = new List(); 
            List notAdded = new List(); 

            AddressType addressType = AddressType.Unknown; 
            IPAddress v4Address = null, v6Address = null, isatapAddress = null, teredoAddress = null, six2FourAddress = null;

            foreach (IPAddress address in sourceAddresses)
            { 
                if (address.AddressFamily == AddressFamily.InterNetwork)
                { 
                    if (v4Address != null) 
                        notAdded.Add(address);
                    else 
                    {
                        v4Address = address;
                    }
                    continue; 
                }
                if(address.AddressFamily != AddressFamily.InterNetworkV6 ) 
                { 
                    notAdded.Add(address);
                    continue; 
                }
                if (address.IsIPv6LinkLocal  || address.IsIPv6SiteLocal)
                {
                    notAdded.Add(address); 
                    continue;
                } 
                addressType = GetAddressType(address); 
                switch (addressType)
                { 
                    case AddressType.Teredo:
                        {
                            if (teredoAddress == null)
                            { 
                                teredoAddress = address;
                            } 
                            else 
                            {
                                notAdded.Add(address); 
                            }
                            continue;
                        }
                    case AddressType.Six2Four: 
                        {
                            if (six2FourAddress == null) 
                            { 
                                six2FourAddress = address;
                            } 
                            else
                            {
                                notAdded.Add(address);
                            } 
                            continue;
                        } 
                    case AddressType.Isatap: 
                        {
                            if (isatapAddress == null) 
                            {
                                isatapAddress = address;
                            }
                            else 
                            {
                                notAdded.Add(address); 
                            } 
                            continue;
                        } 
                    default:
                        {
                            if (v6Address != null)
                                notAdded.Add(address); 
                            else
                            { 
                                v6Address = address; 
                            }
                            continue; 
                        }
                }
            }
            if (six2FourAddress != null) 
                result.Add(six2FourAddress);
            if (teredoAddress != null) 
                result.Add(teredoAddress); 
            if (isatapAddress != null)
                result.Add(isatapAddress); 
            if (v6Address != null)
                result.Add(v6Address);
            if (v4Address != null)
                result.Add(v4Address); 

            result.AddRange(notAdded); 
 
            return result.ToArray();
 
        }

        static AddressType GetAddressType(IPAddress address)
        { 
            AddressType result = AddressType.Unknown;
 
            byte[] bytes = address.GetAddressBytes(); 
            if (BitConverter.ToUInt16(bytes, 0) == Six2FourPrefix)
                result = AddressType.Six2Four; 
            else if(BitConverter.ToUInt32(bytes, 0) == TeredoPrefix)
                    result = AddressType.Teredo;
            else if(BitConverter.ToUInt32(bytes, 8) == IsatapIdentifier)
                        result = AddressType.Isatap; 

            return result; 
        } 

 
        // Given an EPR, replaces its URI with the specified IP address
        public static EndpointAddress GetIPEndpointAddress(EndpointAddress epr, IPAddress address)
        {
            EndpointAddressBuilder eprBuilder = new EndpointAddressBuilder(epr); 
            eprBuilder.Uri = GetIPUri(epr.Uri, address);
            return eprBuilder.ToEndpointAddress(); 
        } 

        // Given a hostName based URI, replaces hostName with the IP address 
        public static Uri GetIPUri(Uri uri, IPAddress ipAddress)
        {
            UriBuilder uriBuilder = new UriBuilder(uri);
            if (V6Address(ipAddress) && (ipAddress.IsIPv6LinkLocal || ipAddress.IsIPv6SiteLocal)) 
            {
                // We make a copy of the IP address because scopeID will not be part of ToString() if set after IP address was created 
                uriBuilder.Host = new IPAddress(ipAddress.GetAddressBytes(), ipAddress.ScopeId).ToString(); 
            }
            else 
            {
                uriBuilder.Host = ipAddress.ToString();
            }
            return uriBuilder.Uri; 
        }
 
        // Retrieve the currently configured addresses (cached) 
        public ReadOnlyCollection GetLocalAddresses()
        { 
            lock (ThisLock)
            {
                // Return a clone of the address cache
                return CloneAddresses(this.localAddresses); 
            }
        } 
 
	 // An address is valid if it is a non-transient addresss (if global, must be DNS-eligible as well)
        static bool NonTransientAddress(UnicastIPAddressInformation address) 
        {
            return (!address.IsTransient);
        }
 

 
        public static bool V4Address(IPAddress address) 
        {
            return address.AddressFamily == AddressFamily.InterNetwork; 
        }

        public static bool V6Address(IPAddress address)
        { 
            return address.AddressFamily == AddressFamily.InterNetworkV6;
        } 
 
        // Returns true if the specified address is configured on the machine
        public static bool ValidAddress (IPAddress address) 
        {
            // Walk the interfaces
            NetworkInterface[] networkIfs = NetworkInterface.GetAllNetworkInterfaces();
            foreach (NetworkInterface networkIf in networkIfs) 
            {
                if (ValidInterface(networkIf)) 
                { 
                    IPInterfaceProperties properties = networkIf.GetIPProperties();
                    if (properties != null) 
                    {
                        foreach (UnicastIPAddressInformation unicastAddress in properties.UnicastAddresses)
                        {
                            if (address.Equals(unicastAddress.Address)) 
                            {
                                return true; 
                            } 
                        }
                    } 
                }
            }
            return false;
        } 

        static bool ValidInterface (NetworkInterface networkIf) 
        { 
            return (networkIf.NetworkInterfaceType != NetworkInterfaceType.Loopback &&
                    networkIf.OperationalStatus == OperationalStatus.Up); 
        }

        // Process the address change notification from the system and check if the addresses
        // have really changed. If so, update the local cache and notify interested parties. 
        void OnAddressChanged()
        { 
            bool changed = false; 

            IPAddress[] newAddresses = GetAddresses(); 
            lock (ThisLock)
            {
                if (AddressesChanged(Array.AsReadOnly(newAddresses)))
                { 
                    this.localAddresses = newAddresses;
                    changed = true; 
                } 
            }
 
            if (changed)
            {
                EventHandler handler = AddressChanged;
                if (handler != null && this.isOpen) 
                {
                    handler(this, EventArgs.Empty); 
                } 
            }
        } 

        public void Open()
        {
            lock (ThisLock) 
            {
                DiagnosticUtility.DebugAssert(!this.isOpen, "Helper not expected to be open"); 
 
                // Register for addr changed event and retrieve addresses from the system
                this.addressChangeHelper = new AddressChangeHelper(OnAddressChanged); 
                this.localAddresses = GetAddresses();
                if (Socket.OSSupportsIPv6)
                {
                    this.ipv6Socket = new Socket(AddressFamily.InterNetworkV6, SocketType.Stream, ProtocolType.IP); 
                }
                this.isOpen = true; 
            } 
        }
 
        // Sorts the collection of addresses using sort ioctl
        public ReadOnlyCollection SortAddresses(ReadOnlyCollection addresses)
        {
            ReadOnlyCollection sortedAddresses = SocketAddressList.SortAddresses(this.ipv6Socket, listenAddress, addresses); 

            // If listening on specific address, copy the scope ID that we're listing on for the 
            // link and site local addresses in the sorted list (so that the connect will work) 
            if (this.listenAddress != null)
            { 
                if (this.listenAddress.IsIPv6LinkLocal)
                {
                    foreach (IPAddress address in sortedAddresses)
                    { 
                        if (address.IsIPv6LinkLocal)
                        { 
                            address.ScopeId = this.listenAddress.ScopeId; 
                        }
                    } 
                }
                else if (this.listenAddress.IsIPv6SiteLocal)
                {
                    foreach (IPAddress address in sortedAddresses) 
                    {
                        if (address.IsIPv6SiteLocal) 
                        { 
                            address.ScopeId = this.listenAddress.ScopeId;
                        } 
                    }
                }
            }
            return sortedAddresses; 
        }
 
        // 
        // Helper class to handle system address change events. Because multiple events can be fired as a result of
        // a single significant change (such as interface being enabled/disabled), we try to handle the event just 
        // once using the below mechanism:
        // Start a timer that goes off after Timeout seconds. If get another address change event from the system
        // within this timespan, reset the timer to go off after another Timeout secs. When the timer finally fires,
        // the registered handlers are notified. This should minimize (but not completely eliminate -- think wireless 
        // DHCP interface being enabled, for instance -- this could take longer) reacting multiple times for
        // a single change. 
        // 
        class AddressChangeHelper
        { 
            public delegate void AddedChangedCallback();

            // To avoid processing multiple addr change events within this time span (5 seconds)
            public int Timeout = 5000; 
            IOThreadTimer timer;
            AddedChangedCallback addressChanged; 
 
            public AddressChangeHelper(AddedChangedCallback addressChanged)
            { 
                DiagnosticUtility.DebugAssert(addressChanged != null, "addressChanged expected to be non-null");
                this.addressChanged = addressChanged;
                this.timer = new IOThreadTimer(new WaitCallback(FireAddressChange), null, true);
                NetworkChange.NetworkAddressChanged += OnAddressChange; 
            }
 
            public void Unregister() 
            {
                NetworkChange.NetworkAddressChanged -= OnAddressChange; 
            }

            void OnAddressChange(object sender, EventArgs args)
            { 
                this.timer.Set(Timeout);
            } 
 
            // Now fire address change event to the interested parties
            void FireAddressChange(object asyncState) 
            {
                this.timer.Cancel();
                this.addressChanged();
            } 
        }
    } 
} 

// File provided for Reference Use Only by Microsoft Corporation (c) 2007.
// Copyright (c) Microsoft Corporation. All rights reserved.


                        

Link Menu

Network programming in C#, Network Programming in VB.NET, Network Programming in .NET
This book is available now!
Buy at Amazon US or
Buy at Amazon UK