// Qualifier for our transport has one of the following formats:
        //
        //   tcp[s]://[secret@]hostname[:port]
        //   ws[s]://[secret@]hostname[:port][/path]
        //   [secret@]hostname[:port]
        //
        // 'tcp' and 'tcps' are for connecting directly to ptvsd; 'ws' and 'wss' are for connecting through WebSocketProxy.
        // The versions ending with '...s' use SSL to secure the connection. If no scheme is specified, 'tcp' is the default.
        // If port is not specified, it defaults to 5678 for 'tcp' and 'tcps', 80 for 'ws' and 443 for 'wss'.
        public int AddPort(IDebugPortRequest2 pRequest, out IDebugPort2 ppPort)
        {
            ppPort = null;

            string name;

            pRequest.GetPortName(out name);

            // Support old-style 'hostname:port' format, as well.
            if (!name.Contains("://"))
            {
                name = "tcp://" + name;
            }

            var uri       = new Uri(name, UriKind.Absolute);
            var transport = DebuggerTransportFactory.Get(uri);

            if (transport == null)
            {
                MessageBox.Show(
                    Strings.RemoteUnrecognizedDebuggingTransport.FormatUI(uri.Scheme),
                    Strings.ProductTitle,
                    MessageBoxButtons.OK,
                    MessageBoxIcon.Error
                    );
                return(VSConstants.E_FAIL);
            }

            var validationError = transport.Validate(uri);

            if (validationError != null)
            {
                return(validationError.HResult);
            }

            var port = new PythonRemoteDebugPort(this, pRequest, uri, DebugLog);

            if (PythonDebugOptionsServiceHelper.Options.UseLegacyDebugger)
            {
                // Validate connection early. Debugger automation (DTE) objects are not consistent in error checking from this
                // point on, so errors reported from EnumProcesses and further calls may be ignored and treated as successes
                // (with empty result). Reporting an error here works around that.
                int hr = port.EnumProcesses(out IEnumDebugProcesses2 processes);
                if (hr < 0)
                {
                    return(hr);
                }
            }

            ppPort = port;
            _ports.Add(port);

            return(VSConstants.S_OK);
        }
예제 #2
0
        // Qualifier for our transport has one of the following formats:
        //
        //   tcp[s]://[secret@]hostname[:port]
        //   ws[s]://[secret@]hostname[:port][/path]
        //   [secret@]hostname[:port]
        //
        // 'tcp' and 'tcps' are for connecting directly to ptvsd; 'ws' and 'wss' are for connecting through WebSocketProxy.
        // The versions ending with '...s' use SSL to secure the connection. If no scheme is specified, 'tcp' is the default.
        // If port is not specified, it defaults to 5678 for 'tcp' and 'tcps', 80 for 'ws' and 443 for 'wss'.
        public int AddPort(IDebugPortRequest2 pRequest, out IDebugPort2 ppPort)
        {
            ppPort = null;

            string name;

            pRequest.GetPortName(out name);

            // Support old-style 'hostname:port' format, as well.
            if (!name.Contains("://"))
            {
                name = "tcp://" + name;
            }

            var uri       = new Uri(name, UriKind.Absolute);
            var transport = DebuggerTransportFactory.Get(uri);

            if (transport == null)
            {
                MessageBox.Show(
                    Strings.RemoteUnrecognizedDebuggingTransport.FormatUI(uri.Scheme),
                    Strings.ProductTitle,
                    MessageBoxButtons.OK,
                    MessageBoxIcon.Error
                    );
                return(VSConstants.E_FAIL);
            }

            var validationError = transport.Validate(uri);

            if (validationError != null)
            {
                return(validationError.HResult);
            }

            var port = new PythonRemoteDebugPort(this, pRequest, uri, DebugLog);

            ppPort = port;
            _ports.Add(port);

            return(VSConstants.S_OK);
        }
예제 #3
0
        /// <summary>
        /// Connects to and performs the initial handshake with the remote debugging server, verifying protocol signature and version number,
        /// and returns the opened stream in a state ready to receive further ptvsd commands (e.g. attach).
        /// </summary>
        public static async Task <DebugConnection> ConnectAsync(Uri uri, bool warnAboutAuthenticationErrors, CancellationToken ct)
        {
            var transport = DebuggerTransportFactory.Get(uri);

            if (transport == null)
            {
                throw new ConnectionException(ConnErrorMessages.RemoteInvalidUri);
            }

            Stream stream = null;

            do
            {
                try {
                    stream = transport.Connect(uri, warnAboutAuthenticationErrors);
                } catch (AuthenticationException ex) {
                    if (!warnAboutAuthenticationErrors)
                    {
                        // This should never happen, but if it does, we don't want to keep trying.
                        throw new ConnectionException(ConnErrorMessages.RemoteSslError, ex);
                    }

                    string errText = Strings.RemoteProcessAuthenticationErrorWarning.FormatUI(ex.Message);
                    var    dlgRes  = MessageBox.Show(errText, Strings.ProductTitle, MessageBoxButtons.YesNo, MessageBoxIcon.Warning);
                    if (dlgRes == DialogResult.Yes)
                    {
                        warnAboutAuthenticationErrors = false;
                    }
                    else
                    {
                        throw new ConnectionException(ConnErrorMessages.RemoteSslError, ex);
                    }
                }
            } while (stream == null);

            var  debugConn = new DebugConnection(stream);
            bool connected = false;

            try {
                string serverDebuggerName            = string.Empty;
                int    serverDebuggerProtocolVersion = 0;
                using (var connectedEvent = new AutoResetEvent(false)) {
                    EventHandler <LDP.RemoteConnectedEvent> eventReceived = (object sender, LDP.RemoteConnectedEvent ea) => {
                        serverDebuggerName            = ea.debuggerName;
                        serverDebuggerProtocolVersion = ea.debuggerProtocolVersion;
                        try {
                            connectedEvent.Set();
                        } catch (ObjectDisposedException) {
                        }
                        debugConn.Authenticated();
                    };

                    // When the server accepts a connection, it sends an event and then waits for a request
                    debugConn.LegacyRemoteConnected += eventReceived;
                    try {
                        debugConn.StartListening();
                        bool received = connectedEvent.WaitOne(ConnectTimeoutMs);
                        if (!received)
                        {
                            throw new ConnectionException(ConnErrorMessages.TimeOut);
                        }
                    } finally {
                        debugConn.LegacyRemoteConnected -= eventReceived;
                    }
                }

                if (serverDebuggerName != DebuggerSignature)
                {
                    throw new ConnectionException(ConnErrorMessages.RemoteUnsupportedServer);
                }

                // If we are talking the same protocol but different version, reply with signature + version before bailing out
                // so that ptvsd has a chance to gracefully close the socket on its side.
                var response = await debugConn.SendRequestAsync(new LDP.RemoteDebuggerAuthenticateRequest()
                {
                    clientSecret            = uri.UserInfo,
                    debuggerName            = DebuggerSignature,
                    debuggerProtocolVersion = DebuggerProtocolVersion,
                }, ct);

                if (serverDebuggerProtocolVersion != DebuggerProtocolVersion)
                {
                    throw new ConnectionException(ConnErrorMessages.RemoteUnsupportedServer);
                }

                if (!response.accepted)
                {
                    throw new ConnectionException(ConnErrorMessages.RemoteSecretMismatch);
                }

                connected = true;
                return(debugConn);
            } catch (IOException ex) {
                throw new ConnectionException(ConnErrorMessages.RemoteNetworkError, ex);
            } catch (SocketException ex) {
                throw new ConnectionException(ConnErrorMessages.RemoteNetworkError, ex);
            } finally {
                if (!connected)
                {
                    debugConn?.Dispose();
                }
            }
        }
        /// <summary>
        /// Connects to and performs the initial handshake with the remote debugging server, verifying protocol signature and version number,
        /// and returns the opened stream in a state ready to receive further ptvsd commands (e.g. attach).
        /// </summary>
        public static Stream Connect(Uri uri, bool warnAboutAuthenticationErrors)
        {
            var transport = DebuggerTransportFactory.Get(uri);

            if (transport == null)
            {
                throw new ConnectionException(ConnErrorMessages.RemoteInvalidUri);
            }

            Stream stream = null;

            do
            {
                try {
                    stream = transport.Connect(uri, warnAboutAuthenticationErrors);
                } catch (AuthenticationException ex) {
                    if (!warnAboutAuthenticationErrors)
                    {
                        // This should never happen, but if it does, we don't want to keep trying.
                        throw new ConnectionException(ConnErrorMessages.RemoteSslError, ex);
                    }

                    string errText = ex.Message + "\nConnect anyway?";
                    var    dlgRes  = MessageBox.Show(errText, null, MessageBoxButtons.YesNo, MessageBoxIcon.Warning);
                    if (dlgRes == DialogResult.Yes)
                    {
                        warnAboutAuthenticationErrors = false;
                    }
                    else
                    {
                        throw new ConnectionException(ConnErrorMessages.RemoteSslError, ex);
                    }
                }
            } while (stream == null);

            bool connected = false;

            try {
                string sig = stream.ReadAsciiString(DebuggerSignature.Length);
                if (sig != DebuggerSignature)
                {
                    throw new ConnectionException(ConnErrorMessages.RemoteUnsupportedServer);
                }

                long ver = stream.ReadInt64();

                // If we are talking the same protocol but different version, reply with signature + version before bailing out
                // so that ptvsd has a chance to gracefully close the socket on its side.
                stream.Write(DebuggerSignatureBytes);
                stream.WriteInt64(DebuggerProtocolVersion);

                if (ver != DebuggerProtocolVersion)
                {
                    throw new ConnectionException(ConnErrorMessages.RemoteUnsupportedServer);
                }

                stream.WriteString(uri.UserInfo);
                string secretResp = stream.ReadAsciiString(Accepted.Length);
                if (secretResp != Accepted)
                {
                    throw new ConnectionException(ConnErrorMessages.RemoteSecretMismatch);
                }

                connected = true;
                return(stream);
            } catch (IOException ex) {
                throw new ConnectionException(ConnErrorMessages.RemoteNetworkError, ex);
            } catch (SocketException ex) {
                throw new ConnectionException(ConnErrorMessages.RemoteNetworkError, ex);
            } finally {
                if (!connected)
                {
                    if (stream != null)
                    {
                        stream.Close();
                    }
                }
            }
        }