public BindingDiagDialog(IServiceProvider provider, Site site)
            : base(provider)
        {
            InitializeComponent();

            var container = new CompositeDisposable();

            FormClosed += (sender, args) => container.Dispose();

            container.Add(
                Observable.FromEventPattern <EventArgs>(btnGenerate, "Click")
                .ObserveOn(System.Threading.SynchronizationContext.Current)
                .Subscribe(evt =>
            {
                txtResult.Clear();
                try
                {
                    Warn("IMPORTANT: This report might contain confidential information. Mask such before sharing to others.");
                    Warn("-----");
                    Debug($"System Time: {DateTime.Now}");
                    Debug($"Processor Architecture: {Environment.GetEnvironmentVariable("PROCESSOR_ARCHITECTURE")}");
                    Debug($"OS: {Environment.OSVersion}");
                    Debug($"Server Type: {site.Server.Mode.AsString(EnumFormat.Description)}");
                    Debug("-----");

                    var adapters = Dns.GetHostEntry(string.Empty).AddressList.Where(address => !address.IsIPv6LinkLocal).ToList();
                    if (adapters.Count == 0)
                    {
                        Warn("This machine has no suitable IP address to accept external traffic.");
                    }
                    else
                    {
                        Info($"This machine has {adapters.Count} IP addresses to take external traffic.");
                        foreach (IPAddress address in adapters)
                        {
                            Info(address.AddressFamily == AddressFamily.InterNetworkV6
                                    ? $"* [{address}]."
                                    : $"* {address}.");
                        }
                    }

                    Debug("-----");

                    Debug($"[W3SVC/{site.Id}]");
                    Debug($"ServerComment  : {site.Name}");
                    Debug($"ServerAutoStart: {site.ServerAutoStart}");
                    Debug($"ServerState    : {site.State}");
                    Debug(string.Empty);
                    var feature = new ReservedUrlsFeature((Module)provider);
                    feature.Load();
                    foreach (Binding binding in site.Bindings)
                    {
                        Debug($"BINDING: {binding.Protocol} {binding}");
                        if (binding.Protocol == "https" || binding.Protocol == "http")
                        {
                            if (binding.Host != "localhost")
                            {
                                if (site.Server.Mode == WorkingMode.IisExpress)
                                {
                                    var reservation = binding.ToUrlPrefix();
                                    if (!feature.Items.Any(item => item.UrlPrefix == reservation))
                                    {
                                        Warn($"URL reservation {reservation} is missing. So this binding only works if IIS Express runs as administrator.");
                                    }
                                }

                                Info($"This site can take external traffic if,");
                                Info($" * TCP port {binding.EndPoint.Port} must be opened on Windows Firewall (or any other equivalent products).");
                            }

                            if (binding.EndPoint.Address.Equals(IPAddress.Any))
                            {
                                if (binding.Host != "localhost")
                                {
                                    Info($" * Requests from web browsers must be routed to following end points on this machine,");
                                    foreach (IPAddress address in adapters)
                                    {
                                        Info(address.AddressFamily == AddressFamily.InterNetworkV6
                                                ? $"   * [{address}]:{binding.EndPoint.Port}."
                                                : $"   * {address}:{binding.EndPoint.Port}.");
                                    }
                                }

                                if (Socket.OSSupportsIPv4)
                                {
                                    Debug($"This site can take local traffic at {IPAddress.Loopback}:{binding.EndPoint.Port}.");
                                }

                                if (Socket.OSSupportsIPv6)
                                {
                                    Debug($"This site can take local traffic at [{IPAddress.IPv6Loopback}]:{binding.EndPoint.Port}.");
                                }
                            }
                            else
                            {
                                Info($" * The networking must be properly set up to forward requests from web browsers to {binding.EndPoint} on this machine.");
                            }

                            if (binding.Host == "*" || binding.Host == string.Empty)
                            {
                                if (binding.EndPoint.Address.Equals(IPAddress.Any))
                                {
                                    Info($" * Web browsers can use several URLs, such as");
                                    foreach (IPAddress address in adapters)
                                    {
                                        Debug(address.AddressFamily == AddressFamily.InterNetworkV6
                                                ? $"   * {binding.Protocol}://[{address}]:{binding.EndPoint.Port}."
                                                : $"   * {binding.Protocol}://{address}:{binding.EndPoint.Port}.");
                                    }

                                    Info($"   * {binding.Protocol}://localhost:{binding.EndPoint.Port}.");
                                    Info($"   * {binding.Protocol}://{IPAddress.Loopback}:{binding.EndPoint.Port}.");
                                    Info($"   * {binding.Protocol}://[{IPAddress.IPv6Loopback}]:{binding.EndPoint.Port}.");
                                }
                                else
                                {
                                    Info($" * Web browsers should use URL {binding.Protocol}://{binding.EndPoint.Address}:{binding.EndPoint.Port}.");
                                }
                            }
                            else
                            {
                                Info($" * Web browsers should use URL {binding.Protocol}://{binding.Host}:{binding.EndPoint.Port}. Requests must have Host header of \"{binding.Host}\".");
                                if (!binding.Host.IsWildcard())
                                {
                                    Info($"   Start DNS query for {binding.Host}.");
                                    // IMPORTANT: wildcard host is not supported.
                                    try
                                    {
                                        var entry = Dns.GetHostEntry(binding.Host);
                                        var list  = entry.AddressList;
                                        Info($"   DNS Query returns {list.Length} result(s).");
                                        var found = false;
                                        foreach (var address in list)
                                        {
                                            Info(address.AddressFamily == AddressFamily.InterNetworkV6
                                                    ? $"    * [{address}]"
                                                    : $"    * {address}");
                                            if (adapters.Any(item => address.Equals(item)))
                                            {
                                                found = true;
                                                break;
                                            }

                                            if (address.Equals(IPAddress.Loopback))
                                            {
                                                found = true;
                                            }
                                        }

                                        if (!found)
                                        {
                                            Warn($"   DNS query of \"{binding.Host}\" does not return a known IP address for any network adapter of this machine.");
                                            Warn("   The server usally uses private IP addresses, and DNS query returns public IP addresses.");
                                            Warn("   If packets are forwarded from public IP to private IP properly, this warning can be ignored.");
                                            Warn("   Otherwise, please review DNS settings (or modify the hosts file to emulate DNS).");
                                        }
                                    }
                                    catch (SocketException ex)
                                    {
                                        Error($"DNS query failed: {ex.Message}.");
                                        Error($"Please review the host name {binding.Host}.");
                                    }
                                }
                            }

                            if (binding.Protocol == "https")
                            {
                                Warn("Binding Diagnostics does not verify certificates and other SSL/TLS related settings.");
                                Warn($"Please run SSL Diagnostics at server level to analyze SSL/TLS configuration. More information can be found at https://docs.jexusmanager.com/tutorials/ssl-diagnostics.html.");
                            }
                        }

                        Debug(string.Empty);
                    }
                }
                catch (CryptographicException ex)
                {
                    Debug(ex.ToString());
                    Rollbar.RollbarLocator.RollbarInstance.Error(ex, custom: new Dictionary <string, object> {
                        { "hResult", ex.HResult }
                    });
                }
                catch (Exception ex)
                {
                    Debug(ex.ToString());
                    Rollbar.RollbarLocator.RollbarInstance.Error(ex);
                }
            }));

            container.Add(
                Observable.FromEventPattern <EventArgs>(btnSave, "Click")
                .ObserveOn(System.Threading.SynchronizationContext.Current)
                .Subscribe(evt =>
            {
                var fileName = DialogHelper.ShowSaveFileDialog(null, "Text Files|*.txt|All Files|*.*", null);
                if (string.IsNullOrEmpty(fileName))
                {
                    return;
                }

                File.WriteAllText(fileName, txtResult.Text);
            }));
        }
Example #2
0
        public BindingDialog(IServiceProvider serviceProvider, Binding binding1, Site site)
            : base(serviceProvider)
        {
            InitializeComponent();
            Binding = binding1;
            Text    = Binding == null ? "Create Site Binding" : "Edit Site Binding";
            DialogHelper.LoadAddresses(cbAddress);
            cbAddress.SelectedIndex = -1;
            txtPort.Text            = "80";
            cbType.SelectedIndex    = 0;
            if (!site.Server.SupportsSni)
            {
                cbSniRequired.Enabled = false;
            }

            if (Binding == null)
            {
                txtHost.Text = site.Server.Mode == WorkingMode.IisExpress ? "localhost" : string.Empty;
            }
            else
            {
                cbType.Text    = Binding.Protocol;
                cbType.Enabled = false;
                cbAddress.Text = Binding.EndPoint?.Address.AddressToCombo();
                txtPort.Text   = Binding.EndPoint?.Port.ToString();
                txtHost.Text   = Binding.Host.HostToDisplay();
                if (Binding.EndPoint == null)
                {
                    var value = Binding.BindingInformation;
                    var last  = value.LastIndexOf(':');
                    if (last > 0)
                    {
                        txtHost.Text = value.Substring(last + 1).HostToDisplay();
                        var next = value.LastIndexOf(':', last - 1);
                        txtPort.Text = value.Substring(next + 1, last - next - 1);
                        if (next > -1)
                        {
                            cbAddress.Text = value.Substring(0, next);
                        }
                    }
                }

                if (site.Server.SupportsSni)
                {
                    cbSniRequired.Checked = Binding.GetIsSni();
                }
            }

            var container = new CompositeDisposable();

            FormClosed += (sender, args) => container.Dispose();

            container.Add(
                Observable.FromEventPattern <EventArgs>(btnOK, "Click")
                .ObserveOn(System.Threading.SynchronizationContext.Current)
                .Subscribe(evt =>
            {
                IPAddress address;
                try
                {
                    address = cbAddress.Text.ComboToAddress();
                }
                catch (Exception)
                {
                    ShowMessage("The specified IP address is invalid. Specify a valid IP address.", MessageBoxButtons.OK, MessageBoxIcon.Exclamation, MessageBoxDefaultButton.Button1);
                    return;
                }

                int port;
                try
                {
                    port = int.Parse(txtPort.Text);
                }
                catch (Exception)
                {
                    ShowMessage("The server port number must be a positive integer between 1 and 65535", MessageBoxButtons.OK, MessageBoxIcon.Exclamation, MessageBoxDefaultButton.Button1);
                    return;
                }

                if (port < 1 || port > 65535)
                {
                    ShowMessage("The server port number must be a positive integer between 1 and 65535", MessageBoxButtons.OK, MessageBoxIcon.Exclamation, MessageBoxDefaultButton.Button1);
                    return;
                }

                if (!txtHost.Text.IsValidHost(site.Server.SupportsWildcard))
                {
                    ShowMessage("The specified host name is incorrect. The host name must use a valid host name format and cannot contain the following characters: \"/\\[]:|<>+=;,?*$%#@{}^`. Example: www.contoso.com.", MessageBoxButtons.OK, MessageBoxIcon.Exclamation, MessageBoxDefaultButton.Button1);
                    return;
                }

                var certificate = cbCertificates.SelectedItem as CertificateInfo;
                var host        = txtHost.Text.DisplayToHost();
                var binding     = new Binding(
                    cbType.Text,
                    $"{address.AddressToDisplay()}:{port}:{host.HostToDisplay()}",
                    cbType.Text == "https" ? certificate?.Certificate.GetCertHash() : new byte[0],
                    cbType.Text == "https" ? certificate?.Store : null,
                    cbSniRequired.Checked ? SslFlags.Sni : SslFlags.None,
                    site.Bindings);
                var matched = site.Parent.FindDuplicate(binding, site, Binding);
                if (matched == true)
                {
                    var result = ShowMessage(
                        $"The binding '{binding}' is assigned to another site. If you assign the same binding to this site, you will only be able to start one of the sites. Are you sure that you want to add this duplicate binding?",
                        MessageBoxButtons.YesNo,
                        MessageBoxIcon.Question,
                        MessageBoxDefaultButton.Button1);
                    if (result != DialogResult.Yes)
                    {
                        return;
                    }
                }

                if (matched == null)
                {
                    ShowMessage(
                        "The specific port is being used by a different binding.",
                        MessageBoxButtons.OK,
                        MessageBoxIcon.Warning,
                        MessageBoxDefaultButton.Button1);
                    binding.Delete();
                    return;
                }

                var conflicts = binding.DetectConflicts();
                if (conflicts)
                {
                    var result = ShowMessage(
                        $"This binding is already being used. If you continue you might overwrite the existing certificate for this IP Address:Port or Host Name:Port combination. Do you want to use this binding anyway?",
                        MessageBoxButtons.YesNo,
                        MessageBoxIcon.Question,
                        MessageBoxDefaultButton.Button1);
                    if (result != DialogResult.Yes)
                    {
                        return;
                    }
                }

                if (Binding == null)
                {
                    Binding = binding;
                }
                else
                {
                    Binding.Reinitialize(binding);
                }

                if (site.Server.Mode == WorkingMode.IisExpress || site.Server.Mode == WorkingMode.Iis)
                {
                    var result = Binding.FixCertificateMapping(certificate?.Certificate);
                    if (!string.IsNullOrEmpty(result))
                    {
                        ShowMessage($"The binding '{Binding}' is invalid: {result}.", MessageBoxButtons.OK, MessageBoxIcon.Error, MessageBoxDefaultButton.Button1);
                        return;
                    }
                }

                if (site.Server.Mode == WorkingMode.IisExpress)
                {
                    if (Binding.Host != "localhost")
                    {
                        ShowMessage(
                            "The specific host name is not recommended for IIS Express. The host name should be localhost.",
                            MessageBoxButtons.OK,
                            MessageBoxIcon.Warning,
                            MessageBoxDefaultButton.Button1);

                        var reservation = binding.ToUrlPrefix();
                        var feature     = new ReservedUrlsFeature((Module)serviceProvider);
                        feature.Load();
                        if (feature.Items.All(item => item.UrlPrefix != reservation) && !BindingUtility.AddReservedUrl(reservation))
                        {
                            ShowMessage($"Reserved URL {reservation} cannot be added.", MessageBoxButtons.OK, MessageBoxIcon.Error, MessageBoxDefaultButton.Button1);
                            return;
                        }
                    }
                }

                DialogResult = DialogResult.OK;
            }));

            container.Add(
                Observable.FromEventPattern <EventArgs>(cbType, "SelectedIndexChanged")
                .Merge(Observable.FromEventPattern <EventArgs>(this, "Load"))
                .ObserveOn(System.Threading.SynchronizationContext.Current)
                .Subscribe(evt =>
            {
                if (Binding == null)
                {
                    txtPort.Text = cbType.Text == "http" ? "80" : "443";
                }

                txtCertificates.Visible = cbType.SelectedIndex == 1;
                cbSniRequired.Visible   = cbType.SelectedIndex == 1;
                cbCertificates.Visible  = cbType.SelectedIndex == 1;
                btnSelect.Visible       = cbType.SelectedIndex == 1;
                btnView.Visible         = cbType.SelectedIndex == 1;
            }));

            var certificatesSelected = Observable.FromEventPattern <EventArgs>(cbCertificates, "SelectedIndexChanged");

            container.Add(
                certificatesSelected
                .ObserveOn(System.Threading.SynchronizationContext.Current)
                .Subscribe(evt =>
            {
                btnView.Enabled = cbCertificates.SelectedIndex > 0;
            }));

            container.Add(
                Observable.FromEventPattern <EventArgs>(cbAddress, "TextChanged")
                .Merge(Observable.FromEventPattern <EventArgs>(txtPort, "TextChanged"))
                .Merge(certificatesSelected)
                .Sample(TimeSpan.FromSeconds(1))
                .ObserveOn(System.Threading.SynchronizationContext.Current)
                .Subscribe(evt =>
            {
                if (Helper.IsRunningOnMono())
                {
                    return;
                }

                var toElevate = BindingUtility.Verify(cbType.Text, cbAddress.Text, txtPort.Text, cbCertificates.SelectedItem as CertificateInfo);
                btnOK.Enabled = toElevate != null;
                if (!toElevate.HasValue || !toElevate.Value)
                {
                    JexusManager.NativeMethods.RemoveShieldFromButton(btnOK);
                }
                else
                {
                    JexusManager.NativeMethods.TryAddShieldToButton(btnOK);
                }
            }));

            container.Add(
                Observable.FromEventPattern <EventArgs>(btnView, "Click")
                .ObserveOn(System.Threading.SynchronizationContext.Current)
                .Subscribe(evt =>
            {
                DialogHelper.DisplayCertificate(((CertificateInfo)cbCertificates.SelectedItem).Certificate, Handle);
            }));

            container.Add(
                Observable.FromEventPattern <EventArgs>(btnSelect, "Click")
                .ObserveOn(System.Threading.SynchronizationContext.Current)
                .Subscribe(evt =>
            {
                // TODO:
            }));
        }