Beispiel #1
0
 internal void FireNotice(NpgsqlError e)
 {
     if (Notice != null)
     {
         try
         {
             Notice(this, new NpgsqlNoticeEventArgs(e));
         }
         catch
         {
         } //Eat exceptions from user code.
     }
 }
Beispiel #2
0
 internal NpgsqlNoticeEventArgs(NpgsqlError eNotice)
 {
     Notice = eNotice;
 }
Beispiel #3
0
        /*/// <value>Counts the numbers of Connections that share
         * /// this Connector. Used in Release() to decide wether this
         * /// connector is to be moved to the PooledConnectors list.</value>
         * // internal int mShareCount;*/

        /// <summary>
        /// Opens the physical connection to the server.
        /// </summary>
        /// <remarks>Usually called by the RequestConnector
        /// Method of the connection pool manager.</remarks>
        internal void Open()
        {
            ServerVersion = null;
            // If Connection.ConnectionString specifies a protocol version, we will
            // not try to fall back to version 2 on failure.

            _backendProtocolVersion = (settings.Protocol == ProtocolVersion.Unknown)
                                          ? ProtocolVersion.Version3
                                          : settings.Protocol;

            // Reset state to initialize new connector in pool.
            CurrentState = NpgsqlClosedState.Instance;

            // Keep track of time remaining; Even though there may be multiple timeout-able calls,
            // this allows us to still respect the caller's timeout expectation.
            int      connectTimeRemaining = this.ConnectionTimeout * 1000;
            DateTime attemptStart         = DateTime.Now;

            // Get a raw connection, possibly SSL...
            CurrentState.Open(this, connectTimeRemaining);
            try
            {
                // Establish protocol communication and handle authentication...
                CurrentState.Startup(this);
            }
            catch (NpgsqlException ne)
            {
                connectTimeRemaining -= Convert.ToInt32((DateTime.Now - attemptStart).TotalMilliseconds);

                // Check for protocol not supported.  If we have been told what protocol to use,
                // we will not try this step.
                if (settings.Protocol != ProtocolVersion.Unknown)
                {
                    throw;
                }
                // If we attempted protocol version 3, it may be possible to drop back to version 2.
                if (BackendProtocolVersion != ProtocolVersion.Version3)
                {
                    throw;
                }
                NpgsqlError Error0 = (NpgsqlError)ne.Errors[0];

                // If NpgsqlError..ctor() encounters a version 2 error,
                // it will set its own protocol version to version 2.  That way, we can tell
                // easily if the error was a FATAL: protocol error.
                if (Error0.BackendProtocolVersion != ProtocolVersion.Version2)
                {
                    throw;
                }
                // Try using the 2.0 protocol.
                _mediator.ResetResponses();
                BackendProtocolVersion = ProtocolVersion.Version2;
                CurrentState           = NpgsqlClosedState.Instance;

                // Get a raw connection, possibly SSL...
                CurrentState.Open(this, connectTimeRemaining);
                // Establish protocol communication and handle authentication...
                CurrentState.Startup(this);
            }

            // Change the state of connection to open and ready.
            _connection_state = ConnectionState.Open;
            CurrentState      = NpgsqlReadyState.Instance;

            // Fall back to the old way, SELECT VERSION().
            // This should not happen for protocol version 3+.
            if (ServerVersion == null)
            {
                //NpgsqlCommand command = new NpgsqlCommand("set DATESTYLE TO ISO;select version();", this);
                //ServerVersion = new Version(PGUtil.ExtractServerVersion((string) command.ExecuteScalar()));
                using (NpgsqlCommand command = new NpgsqlCommand("set DATESTYLE TO ISO;select version();", this))
                {
                    ServerVersion = new Version(PGUtil.ExtractServerVersion((string)command.ExecuteScalar()));
                }
            }

            // Adjust client encoding.

            NpgsqlParameterStatus clientEncodingParam = null;

            if (
                !ServerParameters.TryGetValue("client_encoding", out clientEncodingParam) ||
                (!string.Equals(clientEncodingParam.ParameterValue, "UTF8", StringComparison.OrdinalIgnoreCase) && !string.Equals(clientEncodingParam.ParameterValue, "UNICODE", StringComparison.OrdinalIgnoreCase))
                )
            {
                new NpgsqlCommand("SET CLIENT_ENCODING TO UTF8", this).ExecuteBlind();
            }

            if (!string.IsNullOrEmpty(settings.SearchPath))
            {
                /*NpgsqlParameter p = new NpgsqlParameter("p", DbType.String);
                 * p.Value = settings.SearchPath;
                 * NpgsqlCommand commandSearchPath = new NpgsqlCommand("SET SEARCH_PATH TO :p,public", this);
                 * commandSearchPath.Parameters.Add(p);
                 * commandSearchPath.ExecuteNonQuery();*/

                /*NpgsqlParameter p = new NpgsqlParameter("p", DbType.String);
                 * p.Value = settings.SearchPath;
                 * NpgsqlCommand commandSearchPath = new NpgsqlCommand("SET SEARCH_PATH TO :p,public", this);
                 * commandSearchPath.Parameters.Add(p);
                 * commandSearchPath.ExecuteNonQuery();*/

                // TODO: Add proper message when finding a semicolon in search_path.
                // This semicolon could lead to a sql injection security hole as someone could write in connection string:
                // searchpath=public;delete from table; and it would be executed.

                if (settings.SearchPath.Contains(";"))
                {
                    throw new InvalidOperationException();
                }

                // This is using string concatenation because set search_path doesn't allow type casting. ::text
                NpgsqlCommand commandSearchPath = new NpgsqlCommand("SET SEARCH_PATH=" + settings.SearchPath, this);
                commandSearchPath.ExecuteBlind();
            }

            if (!string.IsNullOrEmpty(settings.ApplicationName))
            {
                if (!SupportsApplicationName)
                {
                    //TODO
                    //throw new InvalidOperationException(resman.GetString("Exception_ApplicationNameNotSupported"));
                    throw new InvalidOperationException("ApplicationName not supported.");
                }

                if (settings.ApplicationName.Contains(";"))
                {
                    throw new InvalidOperationException();
                }

                NpgsqlCommand commandApplicationName = new NpgsqlCommand("SET APPLICATION_NAME='" + settings.ApplicationName + "'", this);
                commandApplicationName.ExecuteBlind();
            }

            /*
             * Try to set SSL negotiation to 0. As of 2010-03-29, recent problems in SSL library implementations made
             * postgresql to add a parameter to set a value when to do this renegotiation or 0 to disable it.
             * Currently, Npgsql has a problem with renegotiation so, we are trying to disable it here.
             * This only works on postgresql servers where the ssl renegotiation settings is supported of course.
             * See http://lists.pgfoundry.org/pipermail/npgsql-devel/2010-February/001065.html for more information.
             */

            try
            {
                NpgsqlCommand commandSslrenegotiation = new NpgsqlCommand("SET ssl_renegotiation_limit=0", this);
                commandSslrenegotiation.ExecuteBlind();
            }
            catch {}



            /*
             * Set precision digits to maximum value possible. For postgresql before 9 it was 2, after that, it is 3.
             * This way, we set first to 2 and then to 3. If there is an error because of 3, it will have been set to 2 at least.
             * Check bug report #1010992 for more information.
             */


            try
            {
                NpgsqlCommand commandSingleDoublePrecision = new NpgsqlCommand("SET extra_float_digits=2;SET extra_float_digits=3;", this);
                commandSingleDoublePrecision.ExecuteBlind();
            }
            catch {}


            /*
             * Set lc_monetary format to 'C' ir order to get a culture agnostic representation of money.
             * I noticed that on Windows, even when the lc_monetary is English_United States.UTF-8, negative
             * money is formatted as ($value) with parentheses to indicate negative value.
             * By going with a culture agnostic format, we get a consistent behavior.
             */

            try
            {
                NpgsqlCommand commandMonetaryFormatC = new NpgsqlCommand("SET lc_monetary='C';", this);
                commandMonetaryFormatC.ExecuteBlind();
            }
            catch
            {
            }


            // Make a shallow copy of the type mapping that the connector will own.
            // It is possible that the connector may add types to its private
            // mapping that will not be valid to another connector, even
            // if connected to the same backend version.
            _oidToNameMapping = NpgsqlTypesHelper.CreateAndLoadInitialTypesMapping(this).Clone();

            ProcessServerVersion();

            // The connector is now fully initialized. Beyond this point, it is
            // safe to release it back to the pool rather than closing it.
            IsInitialized = true;
        }
        private Object FastpathV2(Int32 fnid, Boolean resulttype, FastpathArg[] args)
        {
            // added Oct 7 1998 to give us thread safety
            lock (stream)
            {
                // send the function call
                try
                {
                    // 70 is 'F' in ASCII. Note: don't use SendChar() here as it adds padding
                    // that confuses the backend. The 0 terminates the command line.
                    stream.WriteByte((Byte)70);
                    stream.WriteByte((Byte)0);

                    PGUtil.WriteInt32(stream,fnid);
                    PGUtil.WriteInt32(stream,args.Length);


                    for (Int32 i = 0;i < args.Length;i++)
                        args[i].Send(stream);

                    // This is needed, otherwise data can be lost
                    stream.Flush();

                }
                catch (IOException ioe)
                {
                    //Should be sending exception as second arg.
                    throw new Exception("postgresql.fp.send: "  + ioe.ToString());
                }

                // Now handle the result

                // Now loop, reading the results
                Object result = null; // our result
                String errorMessage = "";
                Byte[] input_buffer = new Byte[512];
                Int32 c;
                Boolean l_endQuery = false;
                while (!l_endQuery)
                {
                    c = (Char)stream.ReadByte();

                    switch (c)
                    {
                    case 'A':	// Asynchronous Notify
                        //TODO: do something with this
                        Int32 pid = PGUtil.ReadInt32(stream,input_buffer);
                        String msg = PGUtil.ReadString(stream,conn.Connector.Encoding);


                        conn.Connector.CheckErrorsAndNotifications();

                        break;

                        //------------------------------
                        // Error message returned
                    case 'E':
                        NpgsqlError e = new NpgsqlError(conn.BackendProtocolVersion);
                        e.ReadFromStream(stream,conn.Connector.Encoding);
                        errorMessage +=  e.Message;
                        break;

                        //------------------------------
                        // Notice from backend
                    case 'N':
                        NpgsqlError notice = new NpgsqlError(conn.BackendProtocolVersion);
                        notice.ReadFromStream(stream,conn.Connector.Encoding);
                        errorMessage +=  notice.Message;
                        break;

                    case 'V':
                        Char l_nextChar = (Char)stream.ReadByte();
                        if (l_nextChar == 'G')
                        {
                            Int32 sz = PGUtil.ReadInt32(stream,input_buffer);
                            // Return an Integer if
                            if (resulttype)
                                result = PGUtil.ReadInt32(stream,input_buffer);
                            else
                            {
                                Byte[] buf = new Byte[sz];

                                Int32 bytes_from_stream = 0;
                                Int32 total_bytes_read = 0;
                                Int32 size = sz;
                                do
                                {
                                    bytes_from_stream = stream.Read(buf, total_bytes_read, size);
                                    total_bytes_read += bytes_from_stream;
                                    size -= bytes_from_stream;
                                }
                                while(size > 0);

                                result = buf;
                            }
                            //There should be a trailing '0'
                            Int32 l_endChar = (Char)stream.ReadByte();
                        }
                        else
                        {
                            //it must have been a '0', thus no results
                        }
                        break;

                    case 'Z':
                        l_endQuery = true;
                        break;

                    default:
                        throw new Exception("postgresql.fp.protocol " + c.ToString());
                    }
                }

                if ( errorMessage != null )
                    throw new Exception("postgresql.fp.error" + errorMessage);

                return result;
            }
        }
Beispiel #5
0
 internal void FireNotice(NpgsqlError e)
 {
     if (Notice != null)
     {
         try
         {
             Notice(this, new NpgsqlNoticeEventArgs(e));
         }
         catch
         {
         } //Eat exceptions from user code.
     }
 }
Beispiel #6
0
        protected IEnumerable<IServerResponseObject> ProcessBackendResponses_Ver_3(NpgsqlConnector context)
        {
            NpgsqlEventLog.LogMethodEnter(LogLevel.Debug, CLASSNAME, "ProcessBackendResponses");

            using (new ContextResetter(context))
            {
                Stream stream = context.Stream;
                NpgsqlMediator mediator = context.Mediator;

                NpgsqlRowDescription lastRowDescription = null;

                List<NpgsqlError> errors = new List<NpgsqlError>();

                for (;;)
                {
                    // Check the first Byte of response.
                    BackEndMessageCode message = (BackEndMessageCode) stream.ReadByte();
                    switch (message)
                    {
                        case BackEndMessageCode.ErrorResponse:

                            NpgsqlError error = new NpgsqlError(context.BackendProtocolVersion, stream);
                            error.ErrorSql = mediator.SqlSent;

                            errors.Add(error);

                            NpgsqlEventLog.LogMsg(resman, "Log_ErrorResponse", LogLevel.Debug, error.Message);

                            // Return imediately if it is in the startup state or connected state as
                            // there is no more messages to consume.
                            // Possible error in the NpgsqlStartupState:
                            //        Invalid password.
                            // Possible error in the NpgsqlConnectedState:
                            //        No pg_hba.conf configured.

                            if (!context.RequireReadyForQuery)
                            {
                                throw new NpgsqlException(errors);
                            }

                            break;
                        case BackEndMessageCode.AuthenticationRequest:

                            NpgsqlEventLog.LogMsg(resman, "Log_ProtocolMessage", LogLevel.Debug, "AuthenticationRequest");

                            // Get the length in case we're getting AuthenticationGSSContinue
                            int authDataLength = PGUtil.ReadInt32(stream) - 8;

                            AuthenticationRequestType authType = (AuthenticationRequestType) PGUtil.ReadInt32(stream);
                            switch (authType)
                            {
                                case AuthenticationRequestType.AuthenticationOk:
                                    NpgsqlEventLog.LogMsg(resman, "Log_AuthenticationOK", LogLevel.Debug);
                                    break;
                                case AuthenticationRequestType.AuthenticationClearTextPassword:
                                    NpgsqlEventLog.LogMsg(resman, "Log_AuthenticationClearTextRequest", LogLevel.Debug);

                                    // Send the PasswordPacket.

                                    ChangeState(context, NpgsqlStartupState.Instance);
                                    context.Authenticate(context.Password);

                                    break;
                                case AuthenticationRequestType.AuthenticationMD5Password:
                                    NpgsqlEventLog.LogMsg(resman, "Log_AuthenticationMD5Request", LogLevel.Debug);
                                    // Now do the "MD5-Thing"
                                    // for this the Password has to be:
                                    // 1. md5-hashed with the username as salt
                                    // 2. md5-hashed again with the salt we get from the backend

                                    MD5 md5 = MD5.Create();

                                    // 1.
                                    byte[] passwd = context.Password;
                                    byte[] saltUserName = ENCODING_UTF8.GetBytes(context.UserName);

                                    byte[] crypt_buf = new byte[passwd.Length + saltUserName.Length];

                                    passwd.CopyTo(crypt_buf, 0);
                                    saltUserName.CopyTo(crypt_buf, passwd.Length);

                                    StringBuilder sb = new StringBuilder();
                                    byte[] hashResult = md5.ComputeHash(crypt_buf);
                                    foreach (byte b in hashResult)
                                    {
                                        sb.Append(b.ToString("x2"));
                                    }

                                    String prehash = sb.ToString();

                                    byte[] prehashbytes = ENCODING_UTF8.GetBytes(prehash);
                                    crypt_buf = new byte[prehashbytes.Length + 4];

                                    stream.Read(crypt_buf, prehashbytes.Length, 4);
                                    // Send the PasswordPacket.
                                    ChangeState(context, NpgsqlStartupState.Instance);

                                    // 2.
                                    prehashbytes.CopyTo(crypt_buf, 0);

                                    sb = new StringBuilder("md5"); // This is needed as the backend expects md5 result starts with "md5"
                                    hashResult = md5.ComputeHash(crypt_buf);
                                    foreach (byte b in hashResult)
                                    {
                                        sb.Append(b.ToString("x2"));
                                    }

                                    context.Authenticate(ENCODING_UTF8.GetBytes(sb.ToString()));

                                    break;
            #if WINDOWS && UNMANAGED

                                case AuthenticationRequestType.AuthenticationSSPI:
                                    {
                                        if (context.IntegratedSecurity)
                                        {
                                            // For SSPI we have to get the IP-Address (hostname doesn't work)
                                            string ipAddressString = ((IPEndPoint)context.Socket.RemoteEndPoint).Address.ToString();
                                            context.SSPI = new SSPIHandler(ipAddressString, "POSTGRES");
                                            ChangeState(context, NpgsqlStartupState.Instance);
                                            context.Authenticate(context.SSPI.Continue(null));
                                            break;
                                        }
                                        else
                                        {
                                            // TODO: correct exception
                                            throw new Exception();
                                        }
                                    }

                                case AuthenticationRequestType.AuthenticationGSSContinue:
                                    {
                                        byte[] authData = new byte[authDataLength];
                                        PGUtil.CheckedStreamRead(stream, authData, 0, authDataLength);
                                        byte[] passwd_read = context.SSPI.Continue(authData);
                                        if (passwd_read.Length != 0)
                                        {
                                            context.Authenticate(passwd_read);
                                        }
                                        break;
                                    }

            #endif

                                default:
                                    // Only AuthenticationClearTextPassword and AuthenticationMD5Password supported for now.
                                    errors.Add(
                                        new NpgsqlError(context.BackendProtocolVersion,
                                                        String.Format(resman.GetString("Exception_AuthenticationMethodNotSupported"), authType)));
                                    throw new NpgsqlException(errors);
                            }
                            break;
                        case BackEndMessageCode.RowDescription:
                            yield return lastRowDescription = new NpgsqlRowDescriptionV3(stream, context.OidToNameMapping, context.CompatVersion);
                            break;
                        case BackEndMessageCode.ParameterDescription:

                            // Do nothing,for instance,  just read...
                            int lenght = PGUtil.ReadInt32(stream);
                            int nb_param = PGUtil.ReadInt16(stream);
                            for (int i = 0; i < nb_param; i++)
                            {
                                int typeoid = PGUtil.ReadInt32(stream);
                            }

                            break;

                        case BackEndMessageCode.DataRow:
                            yield return new ForwardsOnlyRow(new StringRowReaderV3(lastRowDescription, stream));
                            break;

                        case BackEndMessageCode.ReadyForQuery:

                            NpgsqlEventLog.LogMsg(resman, "Log_ProtocolMessage", LogLevel.Debug, "ReadyForQuery");

                            // Possible status bytes returned:
                            //   I = Idle (no transaction active).
                            //   T = In transaction, ready for more.
                            //   E = Error in transaction, queries will fail until transaction aborted.
                            // Just eat the status byte, we have no use for it at this time.
                            PGUtil.ReadInt32(stream);
                            stream.ReadByte();

                            ChangeState(context, NpgsqlReadyState.Instance);

                            if (errors.Count != 0)
                            {
                                throw new NpgsqlException(errors);
                            }

                            yield break;

                        case BackEndMessageCode.BackendKeyData:

                            NpgsqlEventLog.LogMsg(resman, "Log_ProtocolMessage", LogLevel.Debug, "BackendKeyData");
                            // BackendKeyData message.
                            NpgsqlBackEndKeyData backend_keydata = new NpgsqlBackEndKeyData(context.BackendProtocolVersion, stream);
                            context.BackEndKeyData = backend_keydata;

                            // Wait for ReadForQuery message
                            break;

                        case BackEndMessageCode.NoticeResponse:
                            // Notices and errors are identical except that we
                            // just throw notices away completely ignored.
                            context.FireNotice(new NpgsqlError(context.BackendProtocolVersion, stream));
                            break;

                        case BackEndMessageCode.CompletedResponse:
                            PGUtil.ReadInt32(stream);
                            yield return new CompletedResponse(stream);
                            break;
                        case BackEndMessageCode.ParseComplete:
                            NpgsqlEventLog.LogMsg(resman, "Log_ProtocolMessage", LogLevel.Debug, "ParseComplete");
                            // Just read up the message length.
                            PGUtil.ReadInt32(stream);
                            yield break;
                        case BackEndMessageCode.BindComplete:
                            NpgsqlEventLog.LogMsg(resman, "Log_ProtocolMessage", LogLevel.Debug, "BindComplete");
                            // Just read up the message length.
                            PGUtil.ReadInt32(stream);
                            yield break;
                        case BackEndMessageCode.EmptyQueryResponse:
                            NpgsqlEventLog.LogMsg(resman, "Log_ProtocolMessage", LogLevel.Debug, "EmptyQueryResponse");
                            PGUtil.ReadInt32(stream);
                            break;
                        case BackEndMessageCode.NotificationResponse:
                            // Eat the length
                            PGUtil.ReadInt32(stream);
                            context.FireNotification(new NpgsqlNotificationEventArgs(stream, true));
                            if (context.IsNotificationThreadRunning)
                            {
                                yield break;
                            }
                            break;
                        case BackEndMessageCode.ParameterStatus:
                            NpgsqlEventLog.LogMsg(resman, "Log_ProtocolMessage", LogLevel.Debug, "ParameterStatus");
                            NpgsqlParameterStatus parameterStatus = new NpgsqlParameterStatus(stream);

                            NpgsqlEventLog.LogMsg(resman, "Log_ParameterStatus", LogLevel.Debug, parameterStatus.Parameter,
                                                  parameterStatus.ParameterValue);

                            context.AddParameterStatus(parameterStatus);

                            if (parameterStatus.Parameter == "server_version")
                            {
                                // Deal with this here so that if there are
                                // changes in a future backend version, we can handle it here in the
                                // protocol handler and leave everybody else put of it.
                                string versionString = parameterStatus.ParameterValue.Trim();
                                for (int idx = 0; idx != versionString.Length; ++idx)
                                {
                                    char c = parameterStatus.ParameterValue[idx];
                                    if (!char.IsDigit(c) && c != '.')
                                    {
                                        versionString = versionString.Substring(0, idx);
                                        break;
                                    }
                                }
                                context.ServerVersion = new Version(versionString);
                            }
                            break;
                        case BackEndMessageCode.NoData:
                            // This nodata message may be generated by prepare commands issued with queries which doesn't return rows
                            // for example insert, update or delete.
                            // Just eat the message.
                            NpgsqlEventLog.LogMsg(resman, "Log_ProtocolMessage", LogLevel.Debug, "ParameterStatus");
                            PGUtil.ReadInt32(stream);
                            break;

                        case BackEndMessageCode.CopyInResponse:
                            // Enter COPY sub protocol and start pushing data to server
                            NpgsqlEventLog.LogMsg(resman, "Log_ProtocolMessage", LogLevel.Debug, "CopyInResponse");
                            ChangeState(context, NpgsqlCopyInState.Instance);
                            PGUtil.ReadInt32(stream); // length redundant
                            context.CurrentState.StartCopy(context, ReadCopyHeader(stream));
                            yield break;
                                // Either StartCopy called us again to finish the operation or control should be passed for user to feed copy data

                        case BackEndMessageCode.CopyOutResponse:
                            // Enter COPY sub protocol and start pulling data from server
                            NpgsqlEventLog.LogMsg(resman, "Log_ProtocolMessage", LogLevel.Debug, "CopyOutResponse");
                            ChangeState(context, NpgsqlCopyOutState.Instance);
                            PGUtil.ReadInt32(stream); // length redundant
                            context.CurrentState.StartCopy(context, ReadCopyHeader(stream));
                            yield break;
                                // Either StartCopy called us again to finish the operation or control should be passed for user to feed copy data

                        case BackEndMessageCode.CopyData:
                            NpgsqlEventLog.LogMsg(resman, "Log_ProtocolMessage", LogLevel.Debug, "CopyData");
                            Int32 len = PGUtil.ReadInt32(stream) - 4;
                            byte[] buf = new byte[len];
                            PGUtil.ReadBytes(stream, buf, 0, len);
                            context.Mediator.ReceivedCopyData = buf;
                            yield break; // read data from server one chunk at a time while staying in copy operation mode

                        case BackEndMessageCode.CopyDone:
                            NpgsqlEventLog.LogMsg(resman, "Log_ProtocolMessage", LogLevel.Debug, "CopyDone");
                            PGUtil.ReadInt32(stream); // CopyDone can not have content so this is always 4
                            // This will be followed by normal CommandComplete + ReadyForQuery so no op needed
                            break;

                        case BackEndMessageCode.IO_ERROR:
                            // Connection broken. Mono returns -1 instead of throwing an exception as ms.net does.
                            throw new IOException();

                        default:
                            // This could mean a number of things
                            //   We've gotten out of sync with the backend?
                            //   We need to implement this type?
                            //   Backend has gone insane?
                            // FIXME
                            // what exception should we really throw here?
                            throw new NotSupportedException(String.Format("Backend sent unrecognized response type: {0}", (Char) message));
                    }
                }
            }
        }
        protected IEnumerable <IServerResponseObject> ProcessBackendResponses_Ver_3(NpgsqlConnector context)
        {
            NpgsqlEventLog.LogMethodEnter(LogLevel.Debug, CLASSNAME, "ProcessBackendResponses");

            using (new ContextResetter(context))
            {
                Stream         stream   = context.Stream;
                NpgsqlMediator mediator = context.Mediator;

                List <NpgsqlError> errors = new List <NpgsqlError>();

                for (;;)
                {
                    // Check the first Byte of response.
                    BackEndMessageCode message = (BackEndMessageCode)stream.ReadByte();
                    switch (message)
                    {
                    case BackEndMessageCode.ErrorResponse:

                        NpgsqlError error = new NpgsqlError(context.BackendProtocolVersion, stream);
                        error.ErrorSql = mediator.SqlSent;

                        errors.Add(error);

                        NpgsqlEventLog.LogMsg(resman, "Log_ErrorResponse", LogLevel.Debug, error.Message);

                        // Return imediately if it is in the startup state or connected state as
                        // there is no more messages to consume.
                        // Possible error in the NpgsqlStartupState:
                        //        Invalid password.
                        // Possible error in the NpgsqlConnectedState:
                        //        No pg_hba.conf configured.

                        if (!context.RequireReadyForQuery)
                        {
                            throw new NpgsqlException(errors);
                        }

                        break;

                    case BackEndMessageCode.AuthenticationRequest:

                        NpgsqlEventLog.LogMsg(resman, "Log_ProtocolMessage", LogLevel.Debug, "AuthenticationRequest");

                        // Get the length in case we're getting AuthenticationGSSContinue
                        int authDataLength = PGUtil.ReadInt32(stream) - 8;

                        AuthenticationRequestType authType = (AuthenticationRequestType)PGUtil.ReadInt32(stream);
                        switch (authType)
                        {
                        case AuthenticationRequestType.AuthenticationOk:
                            NpgsqlEventLog.LogMsg(resman, "Log_AuthenticationOK", LogLevel.Debug);
                            break;

                        case AuthenticationRequestType.AuthenticationClearTextPassword:
                            NpgsqlEventLog.LogMsg(resman, "Log_AuthenticationClearTextRequest", LogLevel.Debug);

                            // Send the PasswordPacket.

                            ChangeState(context, NpgsqlStartupState.Instance);
                            context.Authenticate(context.Password);

                            break;

                        case AuthenticationRequestType.AuthenticationMD5Password:
                            NpgsqlEventLog.LogMsg(resman, "Log_AuthenticationMD5Request", LogLevel.Debug);
                            // Now do the "MD5-Thing"
                            // for this the Password has to be:
                            // 1. md5-hashed with the username as salt
                            // 2. md5-hashed again with the salt we get from the backend

                            MD5 md5 = MD5.Create();

                            // 1.
                            byte[] passwd       = context.Password;
                            byte[] saltUserName = BackendEncoding.UTF8Encoding.GetBytes(context.UserName);

                            byte[] crypt_buf = new byte[passwd.Length + saltUserName.Length];

                            passwd.CopyTo(crypt_buf, 0);
                            saltUserName.CopyTo(crypt_buf, passwd.Length);

                            StringBuilder sb         = new StringBuilder();
                            byte[]        hashResult = md5.ComputeHash(crypt_buf);
                            foreach (byte b in hashResult)
                            {
                                sb.Append(b.ToString("x2"));
                            }

                            String prehash = sb.ToString();

                            byte[] prehashbytes = BackendEncoding.UTF8Encoding.GetBytes(prehash);
                            crypt_buf = new byte[prehashbytes.Length + 4];

                            stream.Read(crypt_buf, prehashbytes.Length, 4);
                            // Send the PasswordPacket.
                            ChangeState(context, NpgsqlStartupState.Instance);

                            // 2.
                            prehashbytes.CopyTo(crypt_buf, 0);

                            sb         = new StringBuilder("md5"); // This is needed as the backend expects md5 result starts with "md5"
                            hashResult = md5.ComputeHash(crypt_buf);
                            foreach (byte b in hashResult)
                            {
                                sb.Append(b.ToString("x2"));
                            }

                            context.Authenticate(BackendEncoding.UTF8Encoding.GetBytes(sb.ToString()));

                            break;

#if WINDOWS && UNMANAGED
                        case AuthenticationRequestType.AuthenticationSSPI:
                        {
                            if (context.IntegratedSecurity)
                            {
                                // For SSPI we have to get the IP-Address (hostname doesn't work)
                                string ipAddressString = ((IPEndPoint)context.Socket.RemoteEndPoint).Address.ToString();
                                context.SSPI = new SSPIHandler(ipAddressString, "POSTGRES");
                                ChangeState(context, NpgsqlStartupState.Instance);
                                context.Authenticate(context.SSPI.Continue(null));
                                break;
                            }
                            else
                            {
                                // TODO: correct exception
                                throw new Exception();
                            }
                        }

                        case AuthenticationRequestType.AuthenticationGSSContinue:
                        {
                            byte[] authData = new byte[authDataLength];
                            PGUtil.CheckedStreamRead(stream, authData, 0, authDataLength);
                            byte[] passwd_read = context.SSPI.Continue(authData);
                            if (passwd_read.Length != 0)
                            {
                                context.Authenticate(passwd_read);
                            }
                            break;
                        }
#endif

                        default:
                            // Only AuthenticationClearTextPassword and AuthenticationMD5Password supported for now.
                            errors.Add(
                                new NpgsqlError(context.BackendProtocolVersion,
                                                String.Format(resman.GetString("Exception_AuthenticationMethodNotSupported"), authType)));
                            throw new NpgsqlException(errors);
                        }
                        break;

                    case BackEndMessageCode.RowDescription:
                        yield return(new NpgsqlRowDescriptionV3(stream, context.OidToNameMapping, context.CompatVersion));

                        break;

                    case BackEndMessageCode.ParameterDescription:

                        // Do nothing,for instance,  just read...
                        int lenght   = PGUtil.ReadInt32(stream);
                        int nb_param = PGUtil.ReadInt16(stream);
                        for (int i = 0; i < nb_param; i++)
                        {
                            int typeoid = PGUtil.ReadInt32(stream);
                        }

                        break;

                    case BackEndMessageCode.DataRow:
                        yield return(new StringRowReaderV3(stream));

                        break;

                    case BackEndMessageCode.ReadyForQuery:

//                            NpgsqlEventLog.LogMsg(resman, "Log_ProtocolMessage", LogLevel.Debug, "ReadyForQuery");

                        // Possible status bytes returned:
                        //   I = Idle (no transaction active).
                        //   T = In transaction, ready for more.
                        //   E = Error in transaction, queries will fail until transaction aborted.
                        // Just eat the status byte, we have no use for it at this time.
                        PGUtil.ReadInt32(stream);
                        stream.ReadByte();

                        ChangeState(context, NpgsqlReadyState.Instance);

                        if (errors.Count != 0)
                        {
                            throw new NpgsqlException(errors);
                        }

                        yield break;

                    case BackEndMessageCode.BackendKeyData:

                        NpgsqlEventLog.LogMsg(resman, "Log_ProtocolMessage", LogLevel.Debug, "BackendKeyData");
                        // BackendKeyData message.
                        NpgsqlBackEndKeyData backend_keydata = new NpgsqlBackEndKeyData(context.BackendProtocolVersion, stream);
                        context.BackEndKeyData = backend_keydata;

                        // Wait for ReadForQuery message
                        break;

                    case BackEndMessageCode.NoticeResponse:
                        // Notices and errors are identical except that we
                        // just throw notices away completely ignored.
                        context.FireNotice(new NpgsqlError(context.BackendProtocolVersion, stream));
                        break;

                    case BackEndMessageCode.CompletedResponse:
                        PGUtil.ReadInt32(stream);
                        yield return(new CompletedResponse(stream));

                        break;

                    case BackEndMessageCode.ParseComplete:
                        NpgsqlEventLog.LogMsg(resman, "Log_ProtocolMessage", LogLevel.Debug, "ParseComplete");
                        // Just read up the message length.
                        PGUtil.ReadInt32(stream);
                        break;

                    case BackEndMessageCode.BindComplete:
//                            NpgsqlEventLog.LogMsg(resman, "Log_ProtocolMessage", LogLevel.Debug, "BindComplete");
                        // Just read up the message length.
                        PGUtil.ReadInt32(stream);
                        break;

                    case BackEndMessageCode.EmptyQueryResponse:
                        NpgsqlEventLog.LogMsg(resman, "Log_ProtocolMessage", LogLevel.Debug, "EmptyQueryResponse");
                        PGUtil.ReadInt32(stream);
                        break;

                    case BackEndMessageCode.NotificationResponse:
                        // Eat the length
                        PGUtil.ReadInt32(stream);
                        context.FireNotification(new NpgsqlNotificationEventArgs(stream, true));
                        if (context.IsNotificationThreadRunning)
                        {
                            yield break;
                        }
                        break;

                    case BackEndMessageCode.ParameterStatus:
                        NpgsqlEventLog.LogMsg(resman, "Log_ProtocolMessage", LogLevel.Debug, "ParameterStatus");
                        NpgsqlParameterStatus parameterStatus = new NpgsqlParameterStatus(stream);

                        NpgsqlEventLog.LogMsg(resman, "Log_ParameterStatus", LogLevel.Debug, parameterStatus.Parameter,
                                              parameterStatus.ParameterValue);

                        context.AddParameterStatus(parameterStatus);

                        if (parameterStatus.Parameter == "server_version")
                        {
                            // Deal with this here so that if there are
                            // changes in a future backend version, we can handle it here in the
                            // protocol handler and leave everybody else put of it.
                            string versionString = parameterStatus.ParameterValue.Trim();
                            for (int idx = 0; idx != versionString.Length; ++idx)
                            {
                                char c = parameterStatus.ParameterValue[idx];
                                if (!char.IsDigit(c) && c != '.')
                                {
                                    versionString = versionString.Substring(0, idx);
                                    break;
                                }
                            }
                            context.ServerVersion = new Version(versionString);
                        }
                        break;

                    case BackEndMessageCode.NoData:
                        // This nodata message may be generated by prepare commands issued with queries which doesn't return rows
                        // for example insert, update or delete.
                        // Just eat the message.
                        NpgsqlEventLog.LogMsg(resman, "Log_ProtocolMessage", LogLevel.Debug, "ParameterStatus");
                        PGUtil.ReadInt32(stream);
                        break;

                    case BackEndMessageCode.CopyInResponse:
                        // Enter COPY sub protocol and start pushing data to server
                        NpgsqlEventLog.LogMsg(resman, "Log_ProtocolMessage", LogLevel.Debug, "CopyInResponse");
                        ChangeState(context, NpgsqlCopyInState.Instance);
                        PGUtil.ReadInt32(stream);     // length redundant
                        context.CurrentState.StartCopy(context, ReadCopyHeader(stream));
                        yield break;
                    // Either StartCopy called us again to finish the operation or control should be passed for user to feed copy data

                    case BackEndMessageCode.CopyOutResponse:
                        // Enter COPY sub protocol and start pulling data from server
                        NpgsqlEventLog.LogMsg(resman, "Log_ProtocolMessage", LogLevel.Debug, "CopyOutResponse");
                        ChangeState(context, NpgsqlCopyOutState.Instance);
                        PGUtil.ReadInt32(stream);     // length redundant
                        context.CurrentState.StartCopy(context, ReadCopyHeader(stream));
                        yield break;
                    // Either StartCopy called us again to finish the operation or control should be passed for user to feed copy data

                    case BackEndMessageCode.CopyData:
                        NpgsqlEventLog.LogMsg(resman, "Log_ProtocolMessage", LogLevel.Debug, "CopyData");
                        Int32  len = PGUtil.ReadInt32(stream) - 4;
                        byte[] buf = new byte[len];
                        PGUtil.ReadBytes(stream, buf, 0, len);
                        context.Mediator.ReceivedCopyData = buf;
                        yield break;     // read data from server one chunk at a time while staying in copy operation mode

                    case BackEndMessageCode.CopyDone:
                        NpgsqlEventLog.LogMsg(resman, "Log_ProtocolMessage", LogLevel.Debug, "CopyDone");
                        PGUtil.ReadInt32(stream);     // CopyDone can not have content so this is always 4
                        // This will be followed by normal CommandComplete + ReadyForQuery so no op needed
                        break;

                    case BackEndMessageCode.IO_ERROR:
                        // Connection broken. Mono returns -1 instead of throwing an exception as ms.net does.
                        throw new IOException();

                    default:
                        // This could mean a number of things
                        //   We've gotten out of sync with the backend?
                        //   We need to implement this type?
                        //   Backend has gone insane?
                        // FIXME
                        // what exception should we really throw here?
                        throw new NotSupportedException(String.Format("Backend sent unrecognized response type: {0}", (Char)message));
                    }
                }
            }
        }
Beispiel #8
0
        private Object FastpathV3(Int32 fnid, Boolean resulttype, FastpathArg[] args)
        {
            // give  thread safety
            lock (stream)
            {
                // send the function call

                {
                    Int32 l_msgLen = 0;
                    l_msgLen += 16;
                    for (Int32 i = 0; i < args.Length; i++)
                    {
                        l_msgLen += args[i].SendSize();
                    }

                    stream
                        .WriteByte(ASCIIByteArrays.FunctionCallMessageCode)
                        .WriteInt32(l_msgLen)
                        .WriteInt32(fnid)
                        .WriteInt16(1)
                        .WriteInt16(1)
                        .WriteInt16((short)args.Length);

                    for (Int32 i = 0; i < args.Length; i++)
                    {
                        args[i].Send(stream);
                    }

                    stream.WriteInt16(1);

                    // This is needed, otherwise data can be lost
                    stream.Flush();
                }

                // Now handle the result

                // Now loop, reading the results
                Object result = null; // our result
                Exception error = null;
                Int32 c;
                Boolean l_endQuery = false;

                while (!l_endQuery)
                {
                    c = (Char) stream.ReadByte();

                    switch (c)
                    {
                        case 'A': // Asynchronous Notify
                            Int32 msglen = stream.ReadInt32();
                            Int32 pid = stream.ReadInt32();
                            String msg = stream.ReadString();
                            stream.ReadString();
                            String param = stream.ReadString();

                            break;
                            //------------------------------
                            // Error message returned
                        case 'E':
                            NpgsqlError e = new NpgsqlError(stream);
                            throw new NpgsqlException(e.ToString());

                            //------------------------------
                            // Notice from backend
                        case 'N':
                            Int32 l_nlen = stream.ReadInt32();

                            conn.Connector.FireNotice(new NpgsqlError(stream));

                            break;

                        case 'V':
                            Int32 l_msgLen = stream.ReadInt32();
                            Int32 l_valueLen = stream.ReadInt32();

                            if (l_valueLen == -1)
                            {
                                //null value
                            }
                            else if (l_valueLen == 0)
                            {
                                result = new Byte[0];
                            }
                            else
                            {
                                // Return an Integer if
                                if (resulttype)
                                {
                                    result = stream.ReadInt32();
                                }
                                else
                                {
                                    Byte[] buf = new Byte[l_valueLen];

                                    Int32 bytes_from_stream = 0;
                                    Int32 total_bytes_read = 0;
                                    Int32 size = l_valueLen;
                                    do
                                    {
                                        bytes_from_stream = stream.Read(buf, total_bytes_read, size);
                                        total_bytes_read += bytes_from_stream;
                                        size -= bytes_from_stream;
                                    }
                                    while (size > 0);

                                    result = buf;
                                }
                            }
                            break;

                        case 'Z':
                            //TODO: use size better
                            if (stream.ReadInt32() != 5)
                            {
                                throw new Exception("Received Z");
                            }
                            //TODO: handle transaction status
                            Char l_tStatus = (Char) stream.ReadByte();
                            l_endQuery = true;
                            break;

                        default:
                            throw new Exception(string.Format("postgresql.fp.protocol received {0}", c));
                    }
                }

                if (error != null)
                {
                    throw error;
                }

                return result;
            }
        }
Beispiel #9
0
 internal NpgsqlException(NpgsqlError error)
     : base(error.ToString())
 {
     this.errors = new NpgsqlError[1];
     errors[0]   = error;
 }
Beispiel #10
0
        protected virtual void ProcessBackendResponses_Ver_3( NpgsqlConnector context )
        {
            NpgsqlEventLog.LogMethodEnter(LogLevel.Debug, CLASSNAME, "ProcessBackendResponses");

            Stream 	stream = context.Stream;
            NpgsqlMediator mediator = context.Mediator;

            // Often used buffers
            Byte[] inputBuffer = new Byte[ 4 ];
            String Str;

            Boolean readyForQuery = false;

            byte[] asciiRowBytes = new byte[300];
            char[] asciiRowChars = new char[300];

            while (!readyForQuery)
            {
                // Check the first Byte of response.
                Int32 message = stream.ReadByte();
                switch ( message )
                {
                case NpgsqlMessageTypes_Ver_3.ErrorResponse :

                    {
                        NpgsqlError error = new NpgsqlError(context.BackendProtocolVersion);
                        error.ReadFromStream(stream, context.Encoding);
                        error.ErrorSql = mediator.SqlSent;

                        mediator.Errors.Add(error);

                        NpgsqlEventLog.LogMsg(resman, "Log_ErrorResponse", LogLevel.Debug, error.Message);
                    }

                    // Return imediately if it is in the startup state or connected state as
                    // there is no more messages to consume.
                    // Possible error in the NpgsqlStartupState:
                    //		Invalid password.
                    // Possible error in the NpgsqlConnectedState:
                    //		No pg_hba.conf configured.

                    if (! mediator.RequireReadyForQuery)
                    {
                        return;
                    }

                    break;


                case NpgsqlMessageTypes_Ver_3.AuthenticationRequest :

                    NpgsqlEventLog.LogMsg(resman, "Log_ProtocolMessage", LogLevel.Debug, "AuthenticationRequest");

                    // Eat length
                    PGUtil.ReadInt32(stream, inputBuffer);

                    {
                        Int32 authType = PGUtil.ReadInt32(stream, inputBuffer);

                        if ( authType == NpgsqlMessageTypes_Ver_3.AuthenticationOk )
                        {
                            NpgsqlEventLog.LogMsg(resman, "Log_AuthenticationOK", LogLevel.Debug);

                            break;
                        }

                        if ( authType == NpgsqlMessageTypes_Ver_3.AuthenticationClearTextPassword )
                        {
                            NpgsqlEventLog.LogMsg(resman, "Log_AuthenticationClearTextRequest", LogLevel.Debug);

                            // Send the PasswordPacket.

                            ChangeState( context, NpgsqlStartupState.Instance );
                            context.Authenticate(context.Password);

                            break;
                        }


                        if ( authType == NpgsqlMessageTypes_Ver_3.AuthenticationMD5Password )
                        {
                            NpgsqlEventLog.LogMsg(resman, "Log_AuthenticationMD5Request", LogLevel.Debug);
                            // Now do the "MD5-Thing"
                            // for this the Password has to be:
                            // 1. md5-hashed with the username as salt
                            // 2. md5-hashed again with the salt we get from the backend


                            MD5 md5 = MD5.Create();


                            // 1.
                            byte[] passwd = context.Encoding.GetBytes(context.Password);
                            byte[] saltUserName = context.Encoding.GetBytes(context.UserName);

                            byte[] crypt_buf = new byte[passwd.Length + saltUserName.Length];

                            passwd.CopyTo(crypt_buf, 0);
                            saltUserName.CopyTo(crypt_buf, passwd.Length);



                            StringBuilder sb = new StringBuilder ();
                            byte[] hashResult = md5.ComputeHash(crypt_buf);
                            foreach (byte b in hashResult)
                            sb.Append (b.ToString ("x2"));


                            String prehash = sb.ToString();

                            byte[] prehashbytes = context.Encoding.GetBytes(prehash);



                            stream.Read(inputBuffer, 0, 4);
                            // Send the PasswordPacket.
                            ChangeState( context, NpgsqlStartupState.Instance );


                            // 2.

                            crypt_buf = new byte[prehashbytes.Length + 4];
                            prehashbytes.CopyTo(crypt_buf, 0);
                            inputBuffer.CopyTo(crypt_buf, prehashbytes.Length);

                            sb = new StringBuilder ("md5"); // This is needed as the backend expects md5 result starts with "md5"
                            hashResult = md5.ComputeHash(crypt_buf);
                            foreach (byte b in hashResult)
                            sb.Append (b.ToString ("x2"));

                            context.Authenticate(sb.ToString ());

                            break;
                        }

                        // Only AuthenticationClearTextPassword and AuthenticationMD5Password supported for now.
                        mediator.Errors.Add(new NpgsqlError(context.BackendProtocolVersion, String.Format(resman.GetString("Exception_AuthenticationMethodNotSupported"), authType)));
                    }

                    return;

                case NpgsqlMessageTypes_Ver_3.RowDescription:
                    // This is the RowDescription message.
                    NpgsqlEventLog.LogMsg(resman, "Log_ProtocolMessage", LogLevel.Debug, "RowDescription");
                    {
                        NpgsqlRowDescription rd = new NpgsqlRowDescription(context.BackendProtocolVersion);
                        rd.ReadFromStream(stream, context.Encoding, context.OidToNameMapping);

                        mediator.AddRowDescription(rd);
                    }

                    // Now wait for the AsciiRow messages.
                    break;
                case NpgsqlMessageTypes_Ver_3.ParameterDescription:
                    
                    // Do nothing,for instance,  just read...
                    int lenght = PGUtil.ReadInt32(stream, inputBuffer);
                    int nb_param = PGUtil.ReadInt16(stream, inputBuffer);
                    for (int i=0; i < nb_param; i++)
                    {
                        int typeoid = PGUtil.ReadInt32(stream, inputBuffer);
                    }
                    
                    break;

                case NpgsqlMessageTypes_Ver_3.DataRow:
                    // This is the AsciiRow message.
                    NpgsqlEventLog.LogMsg(resman, "Log_ProtocolMessage", LogLevel.Debug, "DataRow");
                    {
                        NpgsqlAsciiRow asciiRow = new NpgsqlAsciiRow(context.Mediator.LastRowDescription, context.BackendProtocolVersion, asciiRowBytes, asciiRowChars);
                        asciiRow.ReadFromStream(stream, context.Encoding);

                        // Add this row to the rows array.
                        mediator.AddAsciiRow(asciiRow);
                    }

                    // Now wait for CompletedResponse message.
                    break;

                case NpgsqlMessageTypes_Ver_3.ReadyForQuery :

                    NpgsqlEventLog.LogMsg(resman, "Log_ProtocolMessage", LogLevel.Debug, "ReadyForQuery");

                    // Possible status bytes returned:
                    //   I = Idle (no transaction active).
                    //   T = In transaction, ready for more.
                    //   E = Error in transaction, queries will fail until transaction aborted.
                    // Just eat the status byte, we have no use for it at this time.
                    PGUtil.ReadInt32(stream, inputBuffer);
                    PGUtil.ReadString(stream, context.Encoding, 1);

                    readyForQuery = true;
                    ChangeState( context, NpgsqlReadyState.Instance );

                    break;

                case NpgsqlMessageTypes_Ver_3.BackendKeyData :

                    NpgsqlEventLog.LogMsg(resman, "Log_ProtocolMessage", LogLevel.Debug, "BackendKeyData");
                    // BackendKeyData message.
                    NpgsqlBackEndKeyData backend_keydata = new NpgsqlBackEndKeyData(context.BackendProtocolVersion);
                    backend_keydata.ReadFromStream(stream);
                    mediator.SetBackendKeydata(backend_keydata);


                    // Wait for ReadForQuery message
                    break;

                case NpgsqlMessageTypes_Ver_3.NoticeResponse :

                    // Notices and errors are identical except that we
                    // just throw notices away completely ignored.
                    {
                        NpgsqlError notice = new NpgsqlError(context.BackendProtocolVersion);
                        notice.ReadFromStream(stream, context.Encoding);

                        mediator.Notices.Add(notice);

                        NpgsqlEventLog.LogMsg(resman, "Log_NoticeResponse", LogLevel.Debug, notice.Message);
                    }

                    // Wait for ReadForQuery message
                    break;

                case NpgsqlMessageTypes_Ver_3.CompletedResponse :
                    // This is the CompletedResponse message.
                    // Get the string returned.

                    PGUtil.ReadInt32(stream, inputBuffer);
                    Str = PGUtil.ReadString(stream, context.Encoding);

                    NpgsqlEventLog.LogMsg(resman, "Log_CompletedResponse", LogLevel.Debug, Str);

                    // Add result from the processing.
                    mediator.AddCompletedResponse(Str);

                    break;

                case NpgsqlMessageTypes_Ver_3.ParseComplete :
                    NpgsqlEventLog.LogMsg(resman, "Log_ProtocolMessage", LogLevel.Debug, "ParseComplete");
                    // Just read up the message length.
                    PGUtil.ReadInt32(stream, inputBuffer);
                    readyForQuery = true;
                    break;

                case NpgsqlMessageTypes_Ver_3.BindComplete :
                    NpgsqlEventLog.LogMsg(resman, "Log_ProtocolMessage", LogLevel.Debug, "BindComplete");
                    // Just read up the message length.
                    PGUtil.ReadInt32(stream, inputBuffer);
                    readyForQuery = true;
                    break;

                case NpgsqlMessageTypes_Ver_3.EmptyQueryResponse :
                    NpgsqlEventLog.LogMsg(resman, "Log_ProtocolMessage", LogLevel.Debug, "EmptyQueryResponse");
                    PGUtil.ReadInt32(stream, inputBuffer);
                    break;

                case NpgsqlMessageTypes_Ver_3.NotificationResponse  :
                    NpgsqlEventLog.LogMsg(resman, "Log_ProtocolMessage", LogLevel.Debug, "NotificationResponse");

                    // Eat the length
                    PGUtil.ReadInt32(stream, inputBuffer);
                    {
                        // Process ID sending notification
                        Int32 PID = PGUtil.ReadInt32(stream, inputBuffer);
                        // Notification string
                        String notificationResponse = PGUtil.ReadString( stream, context.Encoding );
                        // Additional info, currently not implemented by PG (empty string always), eat it
                        PGUtil.ReadString( stream, context.Encoding );
                        mediator.AddNotification(new NpgsqlNotificationEventArgs(PID, notificationResponse));
                    }
                    
                    if (context.IsNotificationThreadRunning)
                        readyForQuery = true;

                    // Wait for ReadForQuery message
                    break;

                case NpgsqlMessageTypes_Ver_3.ParameterStatus :
                    NpgsqlEventLog.LogMsg(resman, "Log_ProtocolMessage", LogLevel.Debug, "ParameterStatus");
                    NpgsqlParameterStatus parameterStatus = new NpgsqlParameterStatus();
                    parameterStatus.ReadFromStream(stream, context.Encoding);

                    NpgsqlEventLog.LogMsg(resman, "Log_ParameterStatus", LogLevel.Debug, parameterStatus.Parameter, parameterStatus.ParameterValue);

                    mediator.AddParameterStatus(parameterStatus.Parameter, parameterStatus);

                    if (parameterStatus.Parameter == "server_version")
                    {
                        // Add this one under our own name so that if the parameter name
                        // changes in a future backend version, we can handle it here in the
                        // protocol handler and leave everybody else put of it.
                        mediator.AddParameterStatus("__npgsql_server_version", parameterStatus);
                        //                        context.ServerVersionString = parameterStatus.ParameterValue;
                    }

                    break;
                case NpgsqlMessageTypes_Ver_3.NoData :
                    // This nodata message may be generated by prepare commands issued with queries which doesn't return rows
                    // for example insert, update or delete.
                    // Just eat the message.
                    NpgsqlEventLog.LogMsg(resman, "Log_ProtocolMessage", LogLevel.Debug, "ParameterStatus");
                    PGUtil.ReadInt32(stream, inputBuffer);
                    break;


                case -1:
		    // Connection broken. Mono returns -1 instead of throw an exception as ms.net does.
		    throw new IOException();

                default :
                    // This could mean a number of things
                    //   We've gotten out of sync with the backend?
                    //   We need to implement this type?
                    //   Backend has gone insane?
                    // FIXME
                    // what exception should we really throw here?
                    throw new NotSupportedException(String.Format("Backend sent unrecognized response type: {0}", (Char)message));

                }
            }
        }
Beispiel #11
0
        protected virtual void ProcessBackendResponses_Ver_2( NpgsqlConnector context )
        {
            NpgsqlEventLog.LogMethodEnter(LogLevel.Debug, CLASSNAME, "ProcessBackendResponses");

            Stream 	stream = context.Stream;
            NpgsqlMediator mediator = context.Mediator;

            // Often used buffer
            Byte[] inputBuffer = new Byte[ 4 ];

            Boolean readyForQuery = false;

            byte[] asciiRowBytes = new byte[300];
            char[] asciiRowChars = new char[300];

            while (!readyForQuery)
            {
                // Check the first Byte of response.
                switch ( stream.ReadByte() )
                {
                case NpgsqlMessageTypes_Ver_2.ErrorResponse :

                    {
                        NpgsqlError error = new NpgsqlError(context.BackendProtocolVersion);
                        error.ReadFromStream(stream, context.Encoding);
                        error.ErrorSql = mediator.SqlSent;

                        mediator.Errors.Add(error);

                        NpgsqlEventLog.LogMsg(resman, "Log_ErrorResponse", LogLevel.Debug, error.Message);
                    }

                    // Return imediately if it is in the startup state or connected state as
                    // there is no more messages to consume.
                    // Possible error in the NpgsqlStartupState:
                    //		Invalid password.
                    // Possible error in the NpgsqlConnectedState:
                    //		No pg_hba.conf configured.

                    if (! mediator.RequireReadyForQuery)
                    {
                        return;
                    }

                    break;


                case NpgsqlMessageTypes_Ver_2.AuthenticationRequest :

                    NpgsqlEventLog.LogMsg(resman, "Log_ProtocolMessage", LogLevel.Debug, "AuthenticationRequest");

                    {
                        Int32 authType = PGUtil.ReadInt32(stream, inputBuffer);

                        if ( authType == NpgsqlMessageTypes_Ver_2.AuthenticationOk )
                        {
                            NpgsqlEventLog.LogMsg(resman, "Log_AuthenticationOK", LogLevel.Debug);

                            break;
                        }

                        if ( authType == NpgsqlMessageTypes_Ver_2.AuthenticationClearTextPassword )
                        {
                            NpgsqlEventLog.LogMsg(resman, "Log_AuthenticationClearTextRequest", LogLevel.Debug);

                            // Send the PasswordPacket.

                            ChangeState( context, NpgsqlStartupState.Instance );
                            context.Authenticate(context.Password);

                            break;
                        }


                        if ( authType == NpgsqlMessageTypes_Ver_2.AuthenticationMD5Password )
                        {
                            NpgsqlEventLog.LogMsg(resman, "Log_AuthenticationMD5Request", LogLevel.Debug);
                            // Now do the "MD5-Thing"
                            // for this the Password has to be:
                            // 1. md5-hashed with the username as salt
                            // 2. md5-hashed again with the salt we get from the backend


                            MD5 md5 = MD5.Create();


                            // 1.
                            byte[] passwd = context.Encoding.GetBytes(context.Password);
                            byte[] saltUserName = context.Encoding.GetBytes(context.UserName);

                            byte[] crypt_buf = new byte[passwd.Length + saltUserName.Length];

                            passwd.CopyTo(crypt_buf, 0);
                            saltUserName.CopyTo(crypt_buf, passwd.Length);



                            StringBuilder sb = new StringBuilder ();
                            byte[] hashResult = md5.ComputeHash(crypt_buf);
                            foreach (byte b in hashResult)
                            sb.Append (b.ToString ("x2"));


                            String prehash = sb.ToString();

                            byte[] prehashbytes = context.Encoding.GetBytes(prehash);



                            byte[] saltServer = new byte[4];
                            stream.Read(saltServer, 0, 4);
                            // Send the PasswordPacket.
                            ChangeState( context, NpgsqlStartupState.Instance );


                            // 2.

                            crypt_buf = new byte[prehashbytes.Length + saltServer.Length];
                            prehashbytes.CopyTo(crypt_buf, 0);
                            saltServer.CopyTo(crypt_buf, prehashbytes.Length);

                            sb = new StringBuilder ("md5"); // This is needed as the backend expects md5 result starts with "md5"
                            hashResult = md5.ComputeHash(crypt_buf);
                            foreach (byte b in hashResult)
                            sb.Append (b.ToString ("x2"));

                            context.Authenticate(sb.ToString ());

                            break;
                        }

                        // Only AuthenticationClearTextPassword and AuthenticationMD5Password supported for now.

                        mediator.Errors.Add(new NpgsqlError(context.BackendProtocolVersion, String.Format(resman.GetString("Exception_AuthenticationMethodNotSupported"), authType)));
                    }

                    return;

                case NpgsqlMessageTypes_Ver_2.RowDescription:
                    // This is the RowDescription message.
                    NpgsqlEventLog.LogMsg(resman, "Log_ProtocolMessage", LogLevel.Debug, "RowDescription");

                    {
                        NpgsqlRowDescription rd = new NpgsqlRowDescription(context.BackendProtocolVersion);
                        rd.ReadFromStream(stream, context.Encoding, context.OidToNameMapping);

                        // Initialize the array list which will contain the data from this rowdescription.
                        mediator.AddRowDescription(rd);
                    }

                    // Now wait for the AsciiRow messages.
                    break;

                case NpgsqlMessageTypes_Ver_2.AsciiRow:
                    // This is the AsciiRow message.
                    NpgsqlEventLog.LogMsg(resman, "Log_ProtocolMessage", LogLevel.Debug, "AsciiRow");

                    {
                        NpgsqlAsciiRow asciiRow = new NpgsqlAsciiRow(context.Mediator.LastRowDescription, context.BackendProtocolVersion, asciiRowBytes, asciiRowChars);
                        asciiRow.ReadFromStream(stream, context.Encoding);

                        // Add this row to the rows array.
                        mediator.AddAsciiRow(asciiRow);
                    }

                    // Now wait for CompletedResponse message.
                    break;

                case NpgsqlMessageTypes_Ver_2.BinaryRow:
                    NpgsqlEventLog.LogMsg(resman, "Log_ProtocolMessage", LogLevel.Debug, "BinaryRow");

                    {
                        NpgsqlBinaryRow binaryRow = new NpgsqlBinaryRow(context.Mediator.LastRowDescription);
                        binaryRow.ReadFromStream(stream, context.Encoding);

                        mediator.AddBinaryRow(binaryRow);
                    }

                    break;

                case NpgsqlMessageTypes_Ver_2.ReadyForQuery :

                    NpgsqlEventLog.LogMsg(resman, "Log_ProtocolMessage", LogLevel.Debug, "ReadyForQuery");
                    readyForQuery = true;
                    ChangeState( context, NpgsqlReadyState.Instance );
                    break;

                case NpgsqlMessageTypes_Ver_2.BackendKeyData :

                    NpgsqlEventLog.LogMsg(resman, "Log_ProtocolMessage", LogLevel.Debug, "BackendKeyData");
                    // BackendKeyData message.
                    NpgsqlBackEndKeyData backend_keydata = new NpgsqlBackEndKeyData(context.BackendProtocolVersion);
                    backend_keydata.ReadFromStream(stream);
                    mediator.SetBackendKeydata(backend_keydata);


                    // Wait for ReadForQuery message
                    break;
                    ;

                case NpgsqlMessageTypes_Ver_2.NoticeResponse :

                    {
                        NpgsqlError notice = new NpgsqlError(context.BackendProtocolVersion);
                        notice.ReadFromStream(stream, context.Encoding);

                        mediator.Notices.Add(notice);

                        NpgsqlEventLog.LogMsg(resman, "Log_NoticeResponse", LogLevel.Debug, notice.Message);
                    }

                    // Wait for ReadForQuery message
                    break;

                case NpgsqlMessageTypes_Ver_2.CompletedResponse :
                    // This is the CompletedResponse message.
                    // Get the string returned.


                    String result = PGUtil.ReadString(stream, context.Encoding);

                    NpgsqlEventLog.LogMsg(resman, "Log_CompletedResponse", LogLevel.Debug, result);
                    // Add result from the processing.

                    mediator.AddCompletedResponse(result);

                    // Now wait for ReadyForQuery message.
                    break;

                case NpgsqlMessageTypes_Ver_2.CursorResponse :
                    // This is the cursor response message.
                    // It is followed by a C NULL terminated string with the name of
                    // the cursor in a FETCH case or 'blank' otherwise.
                    // In this case it should be always 'blank'.
                    // [FIXME] Get another name for this function.
                    NpgsqlEventLog.LogMsg(resman, "Log_ProtocolMessage", LogLevel.Debug, "CursorResponse");

                    String cursorName = PGUtil.ReadString(stream, context.Encoding);
                    // Continue waiting for ReadyForQuery message.
                    break;

                case NpgsqlMessageTypes_Ver_2.EmptyQueryResponse :
                    NpgsqlEventLog.LogMsg(resman, "Log_ProtocolMessage", LogLevel.Debug, "EmptyQueryResponse");
                    PGUtil.ReadString(stream, context.Encoding);
                    break;

                case NpgsqlMessageTypes_Ver_2.NotificationResponse  :

                    NpgsqlEventLog.LogMsg(resman, "Log_ProtocolMessage", LogLevel.Debug, "NotificationResponse");

                    Int32 PID = PGUtil.ReadInt32(stream, inputBuffer);
                    String notificationResponse = PGUtil.ReadString( stream, context.Encoding );
                    mediator.AddNotification(new NpgsqlNotificationEventArgs(PID, notificationResponse));
                    
                    if (context.IsNotificationThreadRunning)
                        readyForQuery = true;

                    // Wait for ReadForQuery message
                    break;

                case -1:
                    // Connection broken. Mono returns -1 instead of throw an exception as ms.net does.
                    throw new IOException();

                default :
                    // This could mean a number of things
                    //   We've gotten out of sync with the backend?
                    //   We need to implement this type?
                    //   Backend has gone insane?
                    // FIXME
                    // what exception should we really throw here?
                    throw new NotSupportedException("Backend sent unrecognized response type");

                }
            }
        }
Beispiel #12
0
        IServerMessage ReadMessageInternal()
        {
            for (;;)
            {
                // Check the first Byte of response.
                Stream.Read(_buffer, 0, 1);
                var message = (BackEndMessageCode) _buffer[0];
                switch (message)
                {
                    case BackEndMessageCode.ErrorResponse:
                        var error = new NpgsqlError(Stream);
                        _log.Trace("Received backend error: " + error.Message);
                        error.ErrorSql = Mediator.GetSqlSent();

                        // We normally accumulate errors until the query ends (ReadyForQuery). But
                        // during the connection phase errors need to be thrown immediately
                        // Possible error in the NpgsqlStartupState:
                        //        Invalid password.
                        // Possible error in the NpgsqlConnectedState:
                        //        No pg_hba.conf configured.

                        if (State == ConnectorState.Connecting) {
                            throw new NpgsqlException(error);
                        }

                        _pendingErrors.Add(error);
                        continue;

                    case BackEndMessageCode.AuthenticationRequest:
                        // Get the length in case we're getting AuthenticationGSSContinue
                        var authDataLength = Stream.ReadInt32() - 8;

                        var authType = (AuthenticationRequestType)Stream.ReadInt32();
                        _log.Trace("Received AuthenticationRequest of type " + authType);
                        switch (authType)
                        {
                            case AuthenticationRequestType.AuthenticationOk:
                                continue;
                            case AuthenticationRequestType.AuthenticationClearTextPassword:
                                // Send the PasswordPacket.
                                SendPasswordMessage(PGUtil.NullTerminateArray(Password));
                                continue;
                            case AuthenticationRequestType.AuthenticationMD5Password:
                                // Now do the "MD5-Thing"
                                // for this the Password has to be:
                                // 1. md5-hashed with the username as salt
                                // 2. md5-hashed again with the salt we get from the backend

                                var md5 = MD5.Create();

                                // 1.
                                var passwd = Password;
                                var saltUserName = BackendEncoding.UTF8Encoding.GetBytes(UserName);

                                var cryptBuf = new byte[passwd.Length + saltUserName.Length];

                                passwd.CopyTo(cryptBuf, 0);
                                saltUserName.CopyTo(cryptBuf, passwd.Length);

                                var sb = new StringBuilder();
                                var hashResult = md5.ComputeHash(cryptBuf);
                                foreach (byte b in hashResult)
                                {
                                    sb.Append(b.ToString("x2"));
                                }

                                var prehash = sb.ToString();

                                var prehashbytes = BackendEncoding.UTF8Encoding.GetBytes(prehash);
                                cryptBuf = new byte[prehashbytes.Length + 4];

                                Stream.Read(cryptBuf, prehashbytes.Length, 4);
                                // Send the PasswordPacket.

                                // 2.
                                prehashbytes.CopyTo(cryptBuf, 0);

                                sb = new StringBuilder("md5");
                                // This is needed as the backend expects md5 result starts with "md5"
                                hashResult = md5.ComputeHash(cryptBuf);
                                foreach (var b in hashResult)
                                {
                                    sb.Append(b.ToString("x2"));
                                }

                                SendPasswordMessage(PGUtil.NullTerminateArray(BackendEncoding.UTF8Encoding.GetBytes(sb.ToString())));
                                continue;

                            case AuthenticationRequestType.AuthenticationGSS:
                            {
                                if (IntegratedSecurity)
                                {
                                    // For GSSAPI we have to use the supplied hostname
                                    SSPI = new SSPIHandler(Host, "POSTGRES", true);
                                    SendPasswordMessage(SSPI.Continue(null));
                                    continue;
                                }
                                else
                                {
                                    // TODO: correct exception
                                    throw new Exception();
                                }
                            }

                            case AuthenticationRequestType.AuthenticationSSPI:
                            {
                                if (IntegratedSecurity)
                                {
                                    // For SSPI we have to get the IP-Address (hostname doesn't work)
                                    var ipAddressString = ((IPEndPoint)Socket.RemoteEndPoint).Address.ToString();
                                    SSPI = new SSPIHandler(ipAddressString, "POSTGRES", false);
                                    SendPasswordMessage(SSPI.Continue(null));
                                    continue;
                                }
                                else
                                {
                                    // TODO: correct exception
                                    throw new Exception();
                                }
                            }

                            case AuthenticationRequestType.AuthenticationGSSContinue:
                            {
                                var authData = new byte[authDataLength];
                                Stream.CheckedStreamRead(authData, 0, authDataLength);
                                var passwdRead = SSPI.Continue(authData);
                                if (passwdRead.Length != 0)
                                {
                                    SendPasswordMessage(passwdRead);
                                }
                                continue;
                            }

                            default:
                                throw new NotSupportedException(String.Format(L10N.AuthenticationMethodNotSupported, authType));
                        }

                    case BackEndMessageCode.RowDescription:
                        _log.Trace("Received RowDescription");
                        return new NpgsqlRowDescription(Stream, OidToNameMapping);

                    case BackEndMessageCode.ParameterDescription:
                        _log.Trace("Received ParameterDescription");
                        // PostgreSQL has found out what parameter types we must use

                        Stream.ReadInt32();
                        var nbParam = Stream.ReadInt16();
                        var typeoids = new int[nbParam];
                        for (var i = 0; i < nbParam; i++)
                        {
                            typeoids[i] = Stream.ReadInt32();  // typeoid
                        }
                        return new ParameterDescriptionResponse(typeoids);

                    case BackEndMessageCode.DataRow:
                        _log.Trace("Received DataRow");
                        State = ConnectorState.Fetching;
                        return new StringRowReader(Stream);

                    case BackEndMessageCode.ReadyForQuery:
                        _log.Trace("Received ReadyForQuery");

                        // Possible status bytes returned:
                        //   I = Idle (no transaction active).
                        //   T = In transaction, ready for more.
                        //   E = Error in transaction, queries will fail until transaction aborted.
                        // Just eat the status byte, we have no use for it at this time.
                        Stream.ReadInt32();
                        Stream.ReadByte();

                        State = ConnectorState.Ready;

                        if (_pendingErrors.Any()) {
                            var e = new NpgsqlException(_pendingErrors);
                            _pendingErrors.Clear();
                            throw e;
                        }
                        return ReadyForQueryMsg.Instance;

                    case BackEndMessageCode.BackendKeyData:
                        _log.Trace("Received BackendKeyData");
                        BackEndKeyData = new NpgsqlBackEndKeyData(Stream);
                        // Wait for ReadForQuery message
                        continue;

                    case BackEndMessageCode.NoticeResponse:
                        _log.Trace("Received NoticeResponse");
                        // Notices and errors are identical except that we
                        // just throw notices away completely ignored.
                        FireNotice(new NpgsqlError(Stream));
                        continue;

                    case BackEndMessageCode.CompletedResponse:
                        _log.Trace("Received CompletedResponse");
                        Stream.ReadInt32();
                        return new CompletedResponse(Stream);

                    case BackEndMessageCode.ParseComplete:
                        _log.Trace("Received ParseComplete");
                        // Just read up the message length.
                        Stream.ReadInt32();
                        continue;

                    case BackEndMessageCode.BindComplete:
                        _log.Trace("Received BindComplete");
                        // Just read up the message length.
                        Stream.ReadInt32();
                        continue;

                    case BackEndMessageCode.EmptyQueryResponse:
                        _log.Trace("Received EmptyQueryResponse");
                        Stream.ReadInt32();
                        continue;

                    case BackEndMessageCode.NotificationResponse:
                        _log.Trace("Received NotificationResponse");
                        // Eat the length
                        Stream.ReadInt32();
                        FireNotification(new NpgsqlNotificationEventArgs(Stream, true));
                        if (IsNotificationThreadRunning) {
                            throw new Exception("Internal state error, notification thread is running");
                        }
                        continue;

                    case BackEndMessageCode.ParameterStatus:
                        var paramStatus = new NpgsqlParameterStatus(Stream);
                        _log.TraceFormat("Received ParameterStatus {0}={1}", paramStatus.Parameter, paramStatus.ParameterValue);
                        AddParameterStatus(paramStatus);

                        if (paramStatus.Parameter == "server_version")
                        {
                            // Deal with this here so that if there are
                            // changes in a future backend version, we can handle it here in the
                            // protocol handler and leave everybody else put of it.
                            var versionString = paramStatus.ParameterValue.Trim();
                            for (var idx = 0; idx != versionString.Length; ++idx)
                            {
                                var c = paramStatus.ParameterValue[idx];
                                if (!char.IsDigit(c) && c != '.')
                                {
                                    versionString = versionString.Substring(0, idx);
                                    break;
                                }
                            }
                            ServerVersion = new Version(versionString);
                        }
                        continue;

                    case BackEndMessageCode.NoData:
                        // This nodata message may be generated by prepare commands issued with queries which doesn't return rows
                        // for example insert, update or delete.
                        // Just eat the message.
                        _log.Trace("Received NoData");
                        Stream.ReadInt32();
                        continue;

                    case BackEndMessageCode.CopyInResponse:
                        _log.Trace("Received CopyInResponse");
                        // Enter COPY sub protocol and start pushing data to server
                        State = ConnectorState.CopyIn;
                        Stream.ReadInt32(); // length redundant
                        StartCopyIn(ReadCopyHeader());
                        return CopyInResponseMsg.Instance;
                        // Either StartCopy called us again to finish the operation or control should be passed for user to feed copy data

                    case BackEndMessageCode.CopyOutResponse:
                        _log.Trace("Received CopyOutResponse");
                        // Enter COPY sub protocol and start pulling data from server
                        State = ConnectorState.CopyOut;
                        Stream.ReadInt32(); // length redundant
                        StartCopyOut(ReadCopyHeader());
                        return CopyOutResponseMsg.Instance;
                        // Either StartCopy called us again to finish the operation or control should be passed for user to feed copy data

                    case BackEndMessageCode.CopyData:
                        _log.Trace("Received CopyData");
                        var len = Stream.ReadInt32() - 4;
                        var buf = new byte[len];
                        Stream.ReadBytes(buf, 0, len);
                        Mediator.ReceivedCopyData = buf;
                        return CopyDataMsg.Instance;
                        // read data from server one chunk at a time while staying in copy operation mode

                    case BackEndMessageCode.CopyDone:
                        _log.Trace("Received CopyDone");
                        Stream.ReadInt32(); // CopyDone can not have content so this is always 4
                        // This will be followed by normal CommandComplete + ReadyForQuery so no op needed
                        continue;

                    case BackEndMessageCode.IO_ERROR:
                        // Connection broken. Mono returns -1 instead of throwing an exception as ms.net does.
                        throw new IOException();

                    default:
                        // This could mean a number of things
                        //   We've gotten out of sync with the backend?
                        //   We need to implement this type?
                        //   Backend has gone insane?
                        // FIXME
                        // what exception should we really throw here?
                        throw new NotSupportedException(String.Format("Backend sent unrecognized response type: {0}", (Char) message));
                }
            }
        }
Beispiel #13
0
        protected virtual void ProcessBackendResponses_Ver_3(NpgsqlConnector context)
        {
            NpgsqlEventLog.LogMethodEnter(LogLevel.Debug, CLASSNAME, "ProcessBackendResponses");

            Stream         stream   = context.Stream;
            NpgsqlMediator mediator = context.Mediator;

            // Often used buffers
            Byte[] inputBuffer = new Byte[4];
            String Str;

            Boolean readyForQuery = false;

            byte[] asciiRowBytes = new byte[300];
            char[] asciiRowChars = new char[300];

            while (!readyForQuery)
            {
                // Check the first Byte of response.
                Int32 message = stream.ReadByte();
                switch (message)
                {
                case NpgsqlMessageTypes_Ver_3.ErrorResponse:

                {
                    NpgsqlError error = new NpgsqlError(context.BackendProtocolVersion);
                    error.ReadFromStream(stream, context.Encoding);
                    error.ErrorSql = mediator.SqlSent;

                    mediator.Errors.Add(error);

                    NpgsqlEventLog.LogMsg(resman, "Log_ErrorResponse", LogLevel.Debug, error.Message);
                }

                    // Return imediately if it is in the startup state or connected state as
                    // there is no more messages to consume.
                    // Possible error in the NpgsqlStartupState:
                    //		Invalid password.
                    // Possible error in the NpgsqlConnectedState:
                    //		No pg_hba.conf configured.

                    if (!mediator.RequireReadyForQuery)
                    {
                        return;
                    }

                    break;


                case NpgsqlMessageTypes_Ver_3.AuthenticationRequest:

                    NpgsqlEventLog.LogMsg(resman, "Log_ProtocolMessage", LogLevel.Debug, "AuthenticationRequest");

                    // Eat length
                    PGUtil.ReadInt32(stream, inputBuffer);

                    {
                        Int32 authType = PGUtil.ReadInt32(stream, inputBuffer);

                        if (authType == NpgsqlMessageTypes_Ver_3.AuthenticationOk)
                        {
                            NpgsqlEventLog.LogMsg(resman, "Log_AuthenticationOK", LogLevel.Debug);

                            break;
                        }

                        if (authType == NpgsqlMessageTypes_Ver_3.AuthenticationClearTextPassword)
                        {
                            NpgsqlEventLog.LogMsg(resman, "Log_AuthenticationClearTextRequest", LogLevel.Debug);

                            // Send the PasswordPacket.

                            ChangeState(context, NpgsqlStartupState.Instance);
                            context.Authenticate(context.Password);

                            break;
                        }


                        if (authType == NpgsqlMessageTypes_Ver_3.AuthenticationMD5Password)
                        {
                            NpgsqlEventLog.LogMsg(resman, "Log_AuthenticationMD5Request", LogLevel.Debug);
                            // Now do the "MD5-Thing"
                            // for this the Password has to be:
                            // 1. md5-hashed with the username as salt
                            // 2. md5-hashed again with the salt we get from the backend


                            MD5 md5 = MD5.Create();


                            // 1.
                            byte[] passwd       = context.Encoding.GetBytes(context.Password);
                            byte[] saltUserName = context.Encoding.GetBytes(context.UserName);

                            byte[] crypt_buf = new byte[passwd.Length + saltUserName.Length];

                            passwd.CopyTo(crypt_buf, 0);
                            saltUserName.CopyTo(crypt_buf, passwd.Length);



                            StringBuilder sb         = new StringBuilder();
                            byte[]        hashResult = md5.ComputeHash(crypt_buf);
                            foreach (byte b in hashResult)
                            {
                                sb.Append(b.ToString("x2"));
                            }


                            String prehash = sb.ToString();

                            byte[] prehashbytes = context.Encoding.GetBytes(prehash);



                            stream.Read(inputBuffer, 0, 4);
                            // Send the PasswordPacket.
                            ChangeState(context, NpgsqlStartupState.Instance);


                            // 2.

                            crypt_buf = new byte[prehashbytes.Length + 4];
                            prehashbytes.CopyTo(crypt_buf, 0);
                            inputBuffer.CopyTo(crypt_buf, prehashbytes.Length);

                            sb         = new StringBuilder("md5"); // This is needed as the backend expects md5 result starts with "md5"
                            hashResult = md5.ComputeHash(crypt_buf);
                            foreach (byte b in hashResult)
                            {
                                sb.Append(b.ToString("x2"));
                            }

                            context.Authenticate(sb.ToString());

                            break;
                        }

                        // Only AuthenticationClearTextPassword and AuthenticationMD5Password supported for now.
                        mediator.Errors.Add(new NpgsqlError(context.BackendProtocolVersion, String.Format(resman.GetString("Exception_AuthenticationMethodNotSupported"), authType)));
                    }

                    return;

                case NpgsqlMessageTypes_Ver_3.RowDescription:
                    // This is the RowDescription message.
                    NpgsqlEventLog.LogMsg(resman, "Log_ProtocolMessage", LogLevel.Debug, "RowDescription");
                    {
                        NpgsqlRowDescription rd = new NpgsqlRowDescription(context.BackendProtocolVersion);
                        rd.ReadFromStream(stream, context.Encoding, context.OidToNameMapping);

                        mediator.AddRowDescription(rd);
                    }

                    // Now wait for the AsciiRow messages.
                    break;

                case NpgsqlMessageTypes_Ver_3.ParameterDescription:

                    // Do nothing,for instance,  just read...
                    int lenght   = PGUtil.ReadInt32(stream, inputBuffer);
                    int nb_param = PGUtil.ReadInt16(stream, inputBuffer);
                    for (int i = 0; i < nb_param; i++)
                    {
                        int typeoid = PGUtil.ReadInt32(stream, inputBuffer);
                    }

                    break;

                case NpgsqlMessageTypes_Ver_3.DataRow:
                    // This is the AsciiRow message.
                    NpgsqlEventLog.LogMsg(resman, "Log_ProtocolMessage", LogLevel.Debug, "DataRow");
                    {
                        NpgsqlAsciiRow asciiRow = new NpgsqlAsciiRow(context.Mediator.LastRowDescription, context.BackendProtocolVersion, asciiRowBytes, asciiRowChars);
                        asciiRow.ReadFromStream(stream, context.Encoding);

                        // Add this row to the rows array.
                        mediator.AddAsciiRow(asciiRow);
                    }

                    // Now wait for CompletedResponse message.
                    break;

                case NpgsqlMessageTypes_Ver_3.ReadyForQuery:

                    NpgsqlEventLog.LogMsg(resman, "Log_ProtocolMessage", LogLevel.Debug, "ReadyForQuery");

                    // Possible status bytes returned:
                    //   I = Idle (no transaction active).
                    //   T = In transaction, ready for more.
                    //   E = Error in transaction, queries will fail until transaction aborted.
                    // Just eat the status byte, we have no use for it at this time.
                    PGUtil.ReadInt32(stream, inputBuffer);
                    PGUtil.ReadString(stream, context.Encoding, 1);

                    readyForQuery = true;
                    ChangeState(context, NpgsqlReadyState.Instance);

                    break;

                case NpgsqlMessageTypes_Ver_3.BackendKeyData:

                    NpgsqlEventLog.LogMsg(resman, "Log_ProtocolMessage", LogLevel.Debug, "BackendKeyData");
                    // BackendKeyData message.
                    NpgsqlBackEndKeyData backend_keydata = new NpgsqlBackEndKeyData(context.BackendProtocolVersion);
                    backend_keydata.ReadFromStream(stream);
                    mediator.SetBackendKeydata(backend_keydata);


                    // Wait for ReadForQuery message
                    break;

                case NpgsqlMessageTypes_Ver_3.NoticeResponse:

                    // Notices and errors are identical except that we
                    // just throw notices away completely ignored.
                {
                    NpgsqlError notice = new NpgsqlError(context.BackendProtocolVersion);
                    notice.ReadFromStream(stream, context.Encoding);

                    mediator.Notices.Add(notice);

                    NpgsqlEventLog.LogMsg(resman, "Log_NoticeResponse", LogLevel.Debug, notice.Message);
                }

                    // Wait for ReadForQuery message
                    break;

                case NpgsqlMessageTypes_Ver_3.CompletedResponse:
                    // This is the CompletedResponse message.
                    // Get the string returned.

                    PGUtil.ReadInt32(stream, inputBuffer);
                    Str = PGUtil.ReadString(stream, context.Encoding);

                    NpgsqlEventLog.LogMsg(resman, "Log_CompletedResponse", LogLevel.Debug, Str);

                    // Add result from the processing.
                    mediator.AddCompletedResponse(Str);

                    break;

                case NpgsqlMessageTypes_Ver_3.ParseComplete:
                    NpgsqlEventLog.LogMsg(resman, "Log_ProtocolMessage", LogLevel.Debug, "ParseComplete");
                    // Just read up the message length.
                    PGUtil.ReadInt32(stream, inputBuffer);
                    readyForQuery = true;
                    break;

                case NpgsqlMessageTypes_Ver_3.BindComplete:
                    NpgsqlEventLog.LogMsg(resman, "Log_ProtocolMessage", LogLevel.Debug, "BindComplete");
                    // Just read up the message length.
                    PGUtil.ReadInt32(stream, inputBuffer);
                    readyForQuery = true;
                    break;

                case NpgsqlMessageTypes_Ver_3.EmptyQueryResponse:
                    NpgsqlEventLog.LogMsg(resman, "Log_ProtocolMessage", LogLevel.Debug, "EmptyQueryResponse");
                    PGUtil.ReadInt32(stream, inputBuffer);
                    break;

                case NpgsqlMessageTypes_Ver_3.NotificationResponse:
                    NpgsqlEventLog.LogMsg(resman, "Log_ProtocolMessage", LogLevel.Debug, "NotificationResponse");

                    // Eat the length
                    PGUtil.ReadInt32(stream, inputBuffer);
                    {
                        // Process ID sending notification
                        Int32 PID = PGUtil.ReadInt32(stream, inputBuffer);
                        // Notification string
                        String notificationResponse = PGUtil.ReadString(stream, context.Encoding);
                        // Additional info, currently not implemented by PG (empty string always), eat it
                        PGUtil.ReadString(stream, context.Encoding);
                        mediator.AddNotification(new NpgsqlNotificationEventArgs(PID, notificationResponse));
                    }

                    if (context.IsNotificationThreadRunning)
                    {
                        readyForQuery = true;
                    }

                    // Wait for ReadForQuery message
                    break;

                case NpgsqlMessageTypes_Ver_3.ParameterStatus:
                    NpgsqlEventLog.LogMsg(resman, "Log_ProtocolMessage", LogLevel.Debug, "ParameterStatus");
                    NpgsqlParameterStatus parameterStatus = new NpgsqlParameterStatus();
                    parameterStatus.ReadFromStream(stream, context.Encoding);

                    NpgsqlEventLog.LogMsg(resman, "Log_ParameterStatus", LogLevel.Debug, parameterStatus.Parameter, parameterStatus.ParameterValue);

                    mediator.AddParameterStatus(parameterStatus.Parameter, parameterStatus);

                    if (parameterStatus.Parameter == "server_version")
                    {
                        // Add this one under our own name so that if the parameter name
                        // changes in a future backend version, we can handle it here in the
                        // protocol handler and leave everybody else put of it.
                        mediator.AddParameterStatus("__npgsql_server_version", parameterStatus);
                        //                        context.ServerVersionString = parameterStatus.ParameterValue;
                    }

                    break;

                case NpgsqlMessageTypes_Ver_3.NoData:
                    // This nodata message may be generated by prepare commands issued with queries which doesn't return rows
                    // for example insert, update or delete.
                    // Just eat the message.
                    NpgsqlEventLog.LogMsg(resman, "Log_ProtocolMessage", LogLevel.Debug, "ParameterStatus");
                    PGUtil.ReadInt32(stream, inputBuffer);
                    break;


                case -1:
                    // Connection broken. Mono returns -1 instead of throw an exception as ms.net does.
                    throw new IOException();

                default:
                    // This could mean a number of things
                    //   We've gotten out of sync with the backend?
                    //   We need to implement this type?
                    //   Backend has gone insane?
                    // FIXME
                    // what exception should we really throw here?
                    throw new NotSupportedException(String.Format("Backend sent unrecognized response type: {0}", (Char)message));
                }
            }
        }
Beispiel #14
0
        protected virtual void ProcessBackendResponses_Ver_2(NpgsqlConnector context)
        {
            NpgsqlEventLog.LogMethodEnter(LogLevel.Debug, CLASSNAME, "ProcessBackendResponses");

            Stream         stream   = context.Stream;
            NpgsqlMediator mediator = context.Mediator;

            // Often used buffer
            Byte[] inputBuffer = new Byte[4];

            Boolean readyForQuery = false;

            byte[] asciiRowBytes = new byte[300];
            char[] asciiRowChars = new char[300];

            while (!readyForQuery)
            {
                // Check the first Byte of response.
                switch (stream.ReadByte())
                {
                case NpgsqlMessageTypes_Ver_2.ErrorResponse:

                {
                    NpgsqlError error = new NpgsqlError(context.BackendProtocolVersion);
                    error.ReadFromStream(stream, context.Encoding);
                    error.ErrorSql = mediator.SqlSent;

                    mediator.Errors.Add(error);

                    NpgsqlEventLog.LogMsg(resman, "Log_ErrorResponse", LogLevel.Debug, error.Message);
                }

                    // Return imediately if it is in the startup state or connected state as
                    // there is no more messages to consume.
                    // Possible error in the NpgsqlStartupState:
                    //		Invalid password.
                    // Possible error in the NpgsqlConnectedState:
                    //		No pg_hba.conf configured.

                    if (!mediator.RequireReadyForQuery)
                    {
                        return;
                    }

                    break;


                case NpgsqlMessageTypes_Ver_2.AuthenticationRequest:

                    NpgsqlEventLog.LogMsg(resman, "Log_ProtocolMessage", LogLevel.Debug, "AuthenticationRequest");

                    {
                        Int32 authType = PGUtil.ReadInt32(stream, inputBuffer);

                        if (authType == NpgsqlMessageTypes_Ver_2.AuthenticationOk)
                        {
                            NpgsqlEventLog.LogMsg(resman, "Log_AuthenticationOK", LogLevel.Debug);

                            break;
                        }

                        if (authType == NpgsqlMessageTypes_Ver_2.AuthenticationClearTextPassword)
                        {
                            NpgsqlEventLog.LogMsg(resman, "Log_AuthenticationClearTextRequest", LogLevel.Debug);

                            // Send the PasswordPacket.

                            ChangeState(context, NpgsqlStartupState.Instance);
                            context.Authenticate(context.Password);

                            break;
                        }


                        if (authType == NpgsqlMessageTypes_Ver_2.AuthenticationMD5Password)
                        {
                            NpgsqlEventLog.LogMsg(resman, "Log_AuthenticationMD5Request", LogLevel.Debug);
                            // Now do the "MD5-Thing"
                            // for this the Password has to be:
                            // 1. md5-hashed with the username as salt
                            // 2. md5-hashed again with the salt we get from the backend


                            MD5 md5 = MD5.Create();


                            // 1.
                            byte[] passwd       = context.Encoding.GetBytes(context.Password);
                            byte[] saltUserName = context.Encoding.GetBytes(context.UserName);

                            byte[] crypt_buf = new byte[passwd.Length + saltUserName.Length];

                            passwd.CopyTo(crypt_buf, 0);
                            saltUserName.CopyTo(crypt_buf, passwd.Length);



                            StringBuilder sb         = new StringBuilder();
                            byte[]        hashResult = md5.ComputeHash(crypt_buf);
                            foreach (byte b in hashResult)
                            {
                                sb.Append(b.ToString("x2"));
                            }


                            String prehash = sb.ToString();

                            byte[] prehashbytes = context.Encoding.GetBytes(prehash);



                            byte[] saltServer = new byte[4];
                            stream.Read(saltServer, 0, 4);
                            // Send the PasswordPacket.
                            ChangeState(context, NpgsqlStartupState.Instance);


                            // 2.

                            crypt_buf = new byte[prehashbytes.Length + saltServer.Length];
                            prehashbytes.CopyTo(crypt_buf, 0);
                            saltServer.CopyTo(crypt_buf, prehashbytes.Length);

                            sb         = new StringBuilder("md5"); // This is needed as the backend expects md5 result starts with "md5"
                            hashResult = md5.ComputeHash(crypt_buf);
                            foreach (byte b in hashResult)
                            {
                                sb.Append(b.ToString("x2"));
                            }

                            context.Authenticate(sb.ToString());

                            break;
                        }

                        // Only AuthenticationClearTextPassword and AuthenticationMD5Password supported for now.

                        mediator.Errors.Add(new NpgsqlError(context.BackendProtocolVersion, String.Format(resman.GetString("Exception_AuthenticationMethodNotSupported"), authType)));
                    }

                    return;

                case NpgsqlMessageTypes_Ver_2.RowDescription:
                    // This is the RowDescription message.
                    NpgsqlEventLog.LogMsg(resman, "Log_ProtocolMessage", LogLevel.Debug, "RowDescription");

                    {
                        NpgsqlRowDescription rd = new NpgsqlRowDescription(context.BackendProtocolVersion);
                        rd.ReadFromStream(stream, context.Encoding, context.OidToNameMapping);

                        // Initialize the array list which will contain the data from this rowdescription.
                        mediator.AddRowDescription(rd);
                    }

                    // Now wait for the AsciiRow messages.
                    break;

                case NpgsqlMessageTypes_Ver_2.AsciiRow:
                    // This is the AsciiRow message.
                    NpgsqlEventLog.LogMsg(resman, "Log_ProtocolMessage", LogLevel.Debug, "AsciiRow");

                    {
                        NpgsqlAsciiRow asciiRow = new NpgsqlAsciiRow(context.Mediator.LastRowDescription, context.BackendProtocolVersion, asciiRowBytes, asciiRowChars);
                        asciiRow.ReadFromStream(stream, context.Encoding);

                        // Add this row to the rows array.
                        mediator.AddAsciiRow(asciiRow);
                    }

                    // Now wait for CompletedResponse message.
                    break;

                case NpgsqlMessageTypes_Ver_2.BinaryRow:
                    NpgsqlEventLog.LogMsg(resman, "Log_ProtocolMessage", LogLevel.Debug, "BinaryRow");

                    {
                        NpgsqlBinaryRow binaryRow = new NpgsqlBinaryRow(context.Mediator.LastRowDescription);
                        binaryRow.ReadFromStream(stream, context.Encoding);

                        mediator.AddBinaryRow(binaryRow);
                    }

                    break;

                case NpgsqlMessageTypes_Ver_2.ReadyForQuery:

                    NpgsqlEventLog.LogMsg(resman, "Log_ProtocolMessage", LogLevel.Debug, "ReadyForQuery");
                    readyForQuery = true;
                    ChangeState(context, NpgsqlReadyState.Instance);
                    break;

                case NpgsqlMessageTypes_Ver_2.BackendKeyData:

                    NpgsqlEventLog.LogMsg(resman, "Log_ProtocolMessage", LogLevel.Debug, "BackendKeyData");
                    // BackendKeyData message.
                    NpgsqlBackEndKeyData backend_keydata = new NpgsqlBackEndKeyData(context.BackendProtocolVersion);
                    backend_keydata.ReadFromStream(stream);
                    mediator.SetBackendKeydata(backend_keydata);


                    // Wait for ReadForQuery message
                    break;
                    ;

                case NpgsqlMessageTypes_Ver_2.NoticeResponse:

                {
                    NpgsqlError notice = new NpgsqlError(context.BackendProtocolVersion);
                    notice.ReadFromStream(stream, context.Encoding);

                    mediator.Notices.Add(notice);

                    NpgsqlEventLog.LogMsg(resman, "Log_NoticeResponse", LogLevel.Debug, notice.Message);
                }

                    // Wait for ReadForQuery message
                    break;

                case NpgsqlMessageTypes_Ver_2.CompletedResponse:
                    // This is the CompletedResponse message.
                    // Get the string returned.


                    String result = PGUtil.ReadString(stream, context.Encoding);

                    NpgsqlEventLog.LogMsg(resman, "Log_CompletedResponse", LogLevel.Debug, result);
                    // Add result from the processing.

                    mediator.AddCompletedResponse(result);

                    // Now wait for ReadyForQuery message.
                    break;

                case NpgsqlMessageTypes_Ver_2.CursorResponse:
                    // This is the cursor response message.
                    // It is followed by a C NULL terminated string with the name of
                    // the cursor in a FETCH case or 'blank' otherwise.
                    // In this case it should be always 'blank'.
                    // [FIXME] Get another name for this function.
                    NpgsqlEventLog.LogMsg(resman, "Log_ProtocolMessage", LogLevel.Debug, "CursorResponse");

                    String cursorName = PGUtil.ReadString(stream, context.Encoding);
                    // Continue waiting for ReadyForQuery message.
                    break;

                case NpgsqlMessageTypes_Ver_2.EmptyQueryResponse:
                    NpgsqlEventLog.LogMsg(resman, "Log_ProtocolMessage", LogLevel.Debug, "EmptyQueryResponse");
                    PGUtil.ReadString(stream, context.Encoding);
                    break;

                case NpgsqlMessageTypes_Ver_2.NotificationResponse:

                    NpgsqlEventLog.LogMsg(resman, "Log_ProtocolMessage", LogLevel.Debug, "NotificationResponse");

                    Int32  PID = PGUtil.ReadInt32(stream, inputBuffer);
                    String notificationResponse = PGUtil.ReadString(stream, context.Encoding);
                    mediator.AddNotification(new NpgsqlNotificationEventArgs(PID, notificationResponse));

                    if (context.IsNotificationThreadRunning)
                    {
                        readyForQuery = true;
                    }

                    // Wait for ReadForQuery message
                    break;

                case -1:
                    // Connection broken. Mono returns -1 instead of throw an exception as ms.net does.
                    throw new IOException();

                default:
                    // This could mean a number of things
                    //   We've gotten out of sync with the backend?
                    //   We need to implement this type?
                    //   Backend has gone insane?
                    // FIXME
                    // what exception should we really throw here?
                    throw new NotSupportedException("Backend sent unrecognized response type");
                }
            }
        }
Beispiel #15
0
        BackendMessage DoReadSingleMessage(DataRowLoadingMode dataRowLoadingMode = DataRowLoadingMode.NonSequential, bool ignoreNotifications = true)
        {
            NpgsqlError error = null;

            while (true)
            {
                var buf = Buffer;

                Buffer.Ensure(5);
                var messageCode = (BackendMessageCode) Buffer.ReadByte();
                Contract.Assume(Enum.IsDefined(typeof(BackendMessageCode), messageCode), "Unknown message code: " + messageCode);
                var len = Buffer.ReadInt32() - 4;  // Transmitted length includes itself

                if (messageCode == BackendMessageCode.DataRow && dataRowLoadingMode != DataRowLoadingMode.NonSequential)
                {
                    if (dataRowLoadingMode == DataRowLoadingMode.Skip)
                    {
                        Buffer.Skip(len);
                        continue;
                    }
                }
                else if (len > Buffer.ReadBytesLeft)
                {
                    buf = buf.EnsureOrAllocateTemp(len);
                }

                var msg = ParseServerMessage(buf, messageCode, len, dataRowLoadingMode);
                if (msg != null || !ignoreNotifications && (messageCode == BackendMessageCode.NoticeResponse || messageCode == BackendMessageCode.NotificationResponse))
                {
                    if (error != null)
                    {
                        Contract.Assert(messageCode == BackendMessageCode.ReadyForQuery, "Expected ReadyForQuery after ErrorResponse");
                        throw new NpgsqlException(error);
                    }
                    return msg;
                }
                else if (messageCode == BackendMessageCode.ErrorResponse)
                {
                    error = new NpgsqlError(buf);
                }
            }
        }
Beispiel #16
0
 internal NpgsqlException(NpgsqlError error)
     : base(error.ToString())
 {
     this.errors = new NpgsqlError[1];
     errors[0] = error;
 }
Beispiel #17
0
        private Object FastpathV2(Int32 fnid, Boolean resulttype, FastpathArg[] args)
        {
            // added Oct 7 1998 to give us thread safety
            lock (stream)
            {
                // send the function call

                stream
                    .WriteBytesNullTerminated((byte)ASCIIBytes.F)
                    .WriteInt32(fnid)
                    .WriteInt32(args.Length);

                for (Int32 i = 0; i < args.Length; i++)
                {
                    args[i].Send(stream);
                }

                // This is needed, otherwise data can be lost
                stream.Flush();

                // Now handle the result

                // Now loop, reading the results
                Object result = null; // our result
                String errorMessage = "";
                Int32 c;
                Boolean l_endQuery = false;
                while (!l_endQuery)
                {
                    c = (Char) stream.ReadByte();

                    switch (c)
                    {
                        case 'A': // Asynchronous Notify
                            //TODO: do something with this
                            Int32 pid = PGUtil.ReadInt32(stream);
                            String msg = PGUtil.ReadString(stream);

                            break;

                            //------------------------------
                            // Error message returned
                        case 'E':
                            NpgsqlError e = new NpgsqlError(conn.BackendProtocolVersion, stream);
                            errorMessage += e.Message;
                            break;

                            //------------------------------
                            // Notice from backend
                        case 'N':
                            NpgsqlError notice = new NpgsqlError(conn.BackendProtocolVersion, stream);
                            errorMessage += notice.Message;
                            break;

                        case 'V':
                            Char l_nextChar = (Char) stream.ReadByte();
                            if (l_nextChar == 'G')
                            {
                                Int32 sz = PGUtil.ReadInt32(stream);
                                // Return an Integer if
                                if (resulttype)
                                {
                                    result = PGUtil.ReadInt32(stream);
                                }
                                else
                                {
                                    Byte[] buf = new Byte[sz];

                                    Int32 bytes_from_stream = 0;
                                    Int32 total_bytes_read = 0;
                                    Int32 size = sz;
                                    do
                                    {
                                        bytes_from_stream = stream.Read(buf, total_bytes_read, size);
                                        total_bytes_read += bytes_from_stream;
                                        size -= bytes_from_stream;
                                    }
                                    while (size > 0);

                                    result = buf;
                                }
                                //There should be a trailing '0'
                                Int32 l_endChar = (Char) stream.ReadByte();
                            }
                            else
                            {
                                //it must have been a '0', thus no results
                            }
                            break;

                        case 'Z':
                            l_endQuery = true;
                            break;

                        default:
                            throw new NpgsqlException(string.Format("postgresql.fp.protocol {0}", c));
                    }
                }

                if (errorMessage != null)
                {
                    throw new NpgsqlException("postgresql.fp.error" + errorMessage);
                }

                return result;
            }
        }
        protected IEnumerable <IServerResponseObject> ProcessBackendResponses_Ver_2(NpgsqlConnector context)
        {
            NpgsqlEventLog.LogMethodEnter(LogLevel.Debug, CLASSNAME, "ProcessBackendResponses");

            using (new ContextResetter(context))
            {
                Stream             stream   = context.Stream;
                NpgsqlMediator     mediator = context.Mediator;
                List <NpgsqlError> errors   = new List <NpgsqlError>();

                for (;;)
                {
                    // Check the first Byte of response.
                    switch ((BackEndMessageCode)stream.ReadByte())
                    {
                    case BackEndMessageCode.ErrorResponse:

                    {
                        NpgsqlError error = new NpgsqlError(context.BackendProtocolVersion, stream);
                        error.ErrorSql = mediator.GetSqlSent();

                        errors.Add(error);

                        NpgsqlEventLog.LogMsg(resman, "Log_ErrorResponse", LogLevel.Debug, error.Message);
                    }

                        // Return imediately if it is in the startup state or connected state as
                        // there is no more messages to consume.
                        // Possible error in the NpgsqlStartupState:
                        //        Invalid password.
                        // Possible error in the NpgsqlConnectedState:
                        //        No pg_hba.conf configured.

                        if (!context.RequireReadyForQuery)
                        {
                            throw new NpgsqlException(errors);
                        }

                        break;

                    case BackEndMessageCode.AuthenticationRequest:
                        NpgsqlEventLog.LogMsg(resman, "Log_ProtocolMessage", LogLevel.Debug, "AuthenticationRequest");
                        AuthenticationRequestType authType = (AuthenticationRequestType)PGUtil.ReadInt32(stream);
                        switch (authType)
                        {
                        case AuthenticationRequestType.AuthenticationOk:
                            NpgsqlEventLog.LogMsg(resman, "Log_AuthenticationOK", LogLevel.Debug);
                            break;

                        case AuthenticationRequestType.AuthenticationClearTextPassword:
                            NpgsqlEventLog.LogMsg(resman, "Log_AuthenticationClearTextRequest", LogLevel.Debug);
                            // Send the PasswordPacket.
                            ChangeState(context, NpgsqlStartupState.Instance);
                            context.Authenticate(context.Password);
                            context.Stream.Flush();

                            break;

                        case AuthenticationRequestType.AuthenticationMD5Password:
                            NpgsqlEventLog.LogMsg(resman, "Log_AuthenticationMD5Request", LogLevel.Debug);
                            // Now do the "MD5-Thing"
                            // for this the Password has to be:
                            // 1. md5-hashed with the username as salt
                            // 2. md5-hashed again with the salt we get from the backend

                            MD5 md5 = MD5.Create();

                            // 1.
                            byte[] passwd       = context.Password;
                            byte[] saltUserName = BackendEncoding.UTF8Encoding.GetBytes(context.UserName);

                            byte[] crypt_buf = new byte[passwd.Length + saltUserName.Length];

                            passwd.CopyTo(crypt_buf, 0);
                            saltUserName.CopyTo(crypt_buf, passwd.Length);

                            StringBuilder sb         = new StringBuilder();
                            byte[]        hashResult = md5.ComputeHash(crypt_buf);
                            foreach (byte b in hashResult)
                            {
                                sb.Append(b.ToString("x2"));
                            }

                            String prehash = sb.ToString();

                            byte[] prehashbytes = BackendEncoding.UTF8Encoding.GetBytes(prehash);

                            byte[] saltServer = new byte[4];
                            stream.Read(saltServer, 0, 4);
                            // Send the PasswordPacket.
                            ChangeState(context, NpgsqlStartupState.Instance);

                            // 2.

                            crypt_buf = new byte[prehashbytes.Length + saltServer.Length];
                            prehashbytes.CopyTo(crypt_buf, 0);
                            saltServer.CopyTo(crypt_buf, prehashbytes.Length);

                            sb         = new StringBuilder("md5"); // This is needed as the backend expects md5 result starts with "md5"
                            hashResult = md5.ComputeHash(crypt_buf);
                            foreach (byte b in hashResult)
                            {
                                sb.Append(b.ToString("x2"));
                            }

                            context.Authenticate(BackendEncoding.UTF8Encoding.GetBytes(sb.ToString()));
                            context.Stream.Flush();

                            break;

                        default:
                            // Only AuthenticationClearTextPassword and AuthenticationMD5Password supported for now.
                            errors.Add(
                                new NpgsqlError(context.BackendProtocolVersion,
                                                String.Format(resman.GetString("Exception_AuthenticationMethodNotSupported"), authType)));
                            throw new NpgsqlException(errors);
                        }
                        break;

                    case BackEndMessageCode.RowDescription:
                        yield return(new NpgsqlRowDescriptionV2(stream, context.OidToNameMapping, context.CompatVersion));

                        break;

                    case BackEndMessageCode.DataRow:
                        yield return(new StringRowReaderV2(stream));

                        break;

                    case BackEndMessageCode.BinaryRow:
                        throw new NotSupportedException();

                    case BackEndMessageCode.ReadyForQuery:
                        ChangeState(context, NpgsqlReadyState.Instance);
                        if (errors.Count != 0)
                        {
                            throw new NpgsqlException(errors);
                        }
                        yield break;

                    case BackEndMessageCode.BackendKeyData:
                        context.BackEndKeyData = new NpgsqlBackEndKeyData(context.BackendProtocolVersion, stream);
                        break;

                    case BackEndMessageCode.NoticeResponse:
                        context.FireNotice(new NpgsqlError(context.BackendProtocolVersion, stream));
                        break;

                    case BackEndMessageCode.CompletedResponse:
                        yield return(new CompletedResponse(stream));

                        break;

                    case BackEndMessageCode.CursorResponse:
                        // This is the cursor response message.
                        // It is followed by a C NULL terminated string with the name of
                        // the cursor in a FETCH case or 'blank' otherwise.
                        // In this case it should be always 'blank'.
                        // [FIXME] Get another name for this function.
                        NpgsqlEventLog.LogMsg(resman, "Log_ProtocolMessage", LogLevel.Debug, "CursorResponse");

                        String cursorName = PGUtil.ReadString(stream);
                        // Continue waiting for ReadyForQuery message.
                        break;

                    case BackEndMessageCode.EmptyQueryResponse:
                        NpgsqlEventLog.LogMsg(resman, "Log_ProtocolMessage", LogLevel.Debug, "EmptyQueryResponse");
                        PGUtil.ReadString(stream);
                        break;

                    case BackEndMessageCode.NotificationResponse:
                        context.FireNotification(new NpgsqlNotificationEventArgs(stream, false));
                        if (context.IsNotificationThreadRunning)
                        {
                            yield break;
                        }
                        break;

                    case BackEndMessageCode.IO_ERROR:
                        // Connection broken. Mono returns -1 instead of throw an exception as ms.net does.
                        throw new IOException();

                    default:
                        // This could mean a number of things
                        //   We've gotten out of sync with the backend?
                        //   We need to implement this type?
                        //   Backend has gone insane?
                        throw new DataException("Backend sent unrecognized response type");
                    }
                }
            }
        }
Beispiel #19
0
        /*/// <value>Counts the numbers of Connections that share
         * /// this Connector. Used in Release() to decide wether this
         * /// connector is to be moved to the PooledConnectors list.</value>
         * // internal int mShareCount;*/

        /// <summary>
        /// Opens the physical connection to the server.
        /// </summary>
        /// <remarks>Usually called by the RequestConnector
        /// Method of the connection pool manager.</remarks>
        internal void Open()
        {
            ServerVersion = null;
            // If Connection.ConnectionString specifies a protocol version, we will
            // not try to fall back to version 2 on failure.

            _backendProtocolVersion = (settings.Protocol == ProtocolVersion.Unknown)
                                          ? ProtocolVersion.Version3
                                          : settings.Protocol;

            // Reset state to initialize new connector in pool.
            CurrentState = NpgsqlClosedState.Instance;

            // Get a raw connection, possibly SSL...
            CurrentState.Open(this);
            try
            {
                // Establish protocol communication and handle authentication...
                CurrentState.Startup(this);
            }
            catch (NpgsqlException ne)
            {
                // Check for protocol not supported.  If we have been told what protocol to use,
                // we will not try this step.
                if (settings.Protocol != ProtocolVersion.Unknown)
                {
                    throw;
                }
                // If we attempted protocol version 3, it may be possible to drop back to version 2.
                if (BackendProtocolVersion != ProtocolVersion.Version3)
                {
                    throw;
                }
                NpgsqlError Error0 = (NpgsqlError)ne.Errors[0];

                // If NpgsqlError..ctor() encounters a version 2 error,
                // it will set its own protocol version to version 2.  That way, we can tell
                // easily if the error was a FATAL: protocol error.
                if (Error0.BackendProtocolVersion != ProtocolVersion.Version2)
                {
                    throw;
                }
                // Try using the 2.0 protocol.
                _mediator.ResetResponses();
                BackendProtocolVersion = ProtocolVersion.Version2;
                CurrentState           = NpgsqlClosedState.Instance;

                // Get a raw connection, possibly SSL...
                CurrentState.Open(this);
                // Establish protocol communication and handle authentication...
                CurrentState.Startup(this);
            }

            // Change the state of connection to open and ready.
            _connection_state = ConnectionState.Open;
            CurrentState      = NpgsqlReadyState.Instance;

            // Fall back to the old way, SELECT VERSION().
            // This should not happen for protocol version 3+.
            if (ServerVersion == null)
            {
                NpgsqlCommand command = new NpgsqlCommand("set DATESTYLE TO ISO;select version();", this);
                ServerVersion = new Version(PGUtil.ExtractServerVersion((string)command.ExecuteScalar()));
            }

            // Adjust client encoding.

            NpgsqlParameterStatus clientEncodingParam = null;

            if (
                !ServerParameters.TryGetValue("client_encoding", out clientEncodingParam) ||
                (!string.Equals(clientEncodingParam.ParameterValue, "UTF8", StringComparison.OrdinalIgnoreCase) && !string.Equals(clientEncodingParam.ParameterValue, "UNICODE", StringComparison.OrdinalIgnoreCase))
                )
            {
                new NpgsqlCommand("SET CLIENT_ENCODING TO UTF8", this).ExecuteBlind();
            }

            if (!string.IsNullOrEmpty(settings.SearchPath))
            {
                /*NpgsqlParameter p = new NpgsqlParameter("p", DbType.String);
                 * p.Value = settings.SearchPath;
                 * NpgsqlCommand commandSearchPath = new NpgsqlCommand("SET SEARCH_PATH TO :p,public", this);
                 * commandSearchPath.Parameters.Add(p);
                 * commandSearchPath.ExecuteNonQuery();*/

                /*NpgsqlParameter p = new NpgsqlParameter("p", DbType.String);
                 * p.Value = settings.SearchPath;
                 * NpgsqlCommand commandSearchPath = new NpgsqlCommand("SET SEARCH_PATH TO :p,public", this);
                 * commandSearchPath.Parameters.Add(p);
                 * commandSearchPath.ExecuteNonQuery();*/

                // TODO: Add proper message when finding a semicolon in search_path.
                // This semicolon could lead to a sql injection security hole as someone could write in connection string:
                // searchpath=public;delete from table; and it would be executed.

                if (settings.SearchPath.Contains(";"))
                {
                    throw new InvalidOperationException();
                }

                // This is using string concatenation because set search_path doesn't allow type casting. ::text
                NpgsqlCommand commandSearchPath = new NpgsqlCommand("SET SEARCH_PATH=" + settings.SearchPath, this);
                commandSearchPath.ExecuteBlind();
            }



            /*
             * Try to set SSL negotiation to 0. As of 2010-03-29, recent problems in SSL library implementations made
             * postgresql to add a parameter to set a value when to do this renegotiation or 0 to disable it.
             * Currently, Npgsql has a problem with renegotiation so, we are trying to disable it here.
             * This only works on postgresql servers where the ssl renegotiation settings is supported of course.
             * See http://lists.pgfoundry.org/pipermail/npgsql-devel/2010-February/001065.html for more information.
             */



            try

            {
                NpgsqlCommand commandSslrenegotiation = new NpgsqlCommand("SET ssl_renegotiation_limit=0", this);

                commandSslrenegotiation.ExecuteBlind();
            }

            catch {}



            // Make a shallow copy of the type mapping that the connector will own.
            // It is possible that the connector may add types to its private
            // mapping that will not be valid to another connector, even
            // if connected to the same backend version.
            _oidToNameMapping = NpgsqlTypesHelper.CreateAndLoadInitialTypesMapping(this).Clone();

            ProcessServerVersion();

            // The connector is now fully initialized. Beyond this point, it is
            // safe to release it back to the pool rather than closing it.
            IsInitialized = true;
        }
Beispiel #20
0
        protected IEnumerable<IServerResponseObject> ProcessBackendResponses_Ver_2(NpgsqlConnector context)
        {
            NpgsqlEventLog.LogMethodEnter(LogLevel.Debug, CLASSNAME, "ProcessBackendResponses");

            using (new ContextResetter(context))
            {
                Stream stream = context.Stream;
                NpgsqlMediator mediator = context.Mediator;
                NpgsqlRowDescription lastRowDescription = null;
                List<NpgsqlError> errors = new List<NpgsqlError>();

                for (;;)
                {
                    // Check the first Byte of response.
                    switch ((BackEndMessageCode) stream.ReadByte())
                    {
                        case BackEndMessageCode.ErrorResponse:

                            {
                                NpgsqlError error = new NpgsqlError(context.BackendProtocolVersion, stream);
                                error.ErrorSql = mediator.SqlSent;

                                errors.Add(error);

                                NpgsqlEventLog.LogMsg(resman, "Log_ErrorResponse", LogLevel.Debug, error.Message);
                            }

                            // Return imediately if it is in the startup state or connected state as
                            // there is no more messages to consume.
                            // Possible error in the NpgsqlStartupState:
                            //        Invalid password.
                            // Possible error in the NpgsqlConnectedState:
                            //        No pg_hba.conf configured.

                            if (!context.RequireReadyForQuery)
                            {
                                throw new NpgsqlException(errors);
                            }

                            break;

                        case BackEndMessageCode.AuthenticationRequest:
                            NpgsqlEventLog.LogMsg(resman, "Log_ProtocolMessage", LogLevel.Debug, "AuthenticationRequest");
                            AuthenticationRequestType authType = (AuthenticationRequestType) PGUtil.ReadInt32(stream);
                            switch (authType)
                            {
                                case AuthenticationRequestType.AuthenticationOk:
                                    NpgsqlEventLog.LogMsg(resman, "Log_AuthenticationOK", LogLevel.Debug);
                                    break;
                                case AuthenticationRequestType.AuthenticationClearTextPassword:
                                    NpgsqlEventLog.LogMsg(resman, "Log_AuthenticationClearTextRequest", LogLevel.Debug);
                                    // Send the PasswordPacket.
                                    ChangeState(context, NpgsqlStartupState.Instance);
                                    context.Authenticate(context.Password);
                                    break;
                                case AuthenticationRequestType.AuthenticationMD5Password:
                                    NpgsqlEventLog.LogMsg(resman, "Log_AuthenticationMD5Request", LogLevel.Debug);
                                    // Now do the "MD5-Thing"
                                    // for this the Password has to be:
                                    // 1. md5-hashed with the username as salt
                                    // 2. md5-hashed again with the salt we get from the backend

                                    MD5 md5 = MD5.Create();

                                    // 1.
                                    byte[] passwd = context.Password;
                                    byte[] saltUserName = ENCODING_UTF8.GetBytes(context.UserName);

                                    byte[] crypt_buf = new byte[passwd.Length + saltUserName.Length];

                                    passwd.CopyTo(crypt_buf, 0);
                                    saltUserName.CopyTo(crypt_buf, passwd.Length);

                                    StringBuilder sb = new StringBuilder();
                                    byte[] hashResult = md5.ComputeHash(crypt_buf);
                                    foreach (byte b in hashResult)
                                    {
                                        sb.Append(b.ToString("x2"));
                                    }

                                    String prehash = sb.ToString();

                                    byte[] prehashbytes = ENCODING_UTF8.GetBytes(prehash);

                                    byte[] saltServer = new byte[4];
                                    stream.Read(saltServer, 0, 4);
                                    // Send the PasswordPacket.
                                    ChangeState(context, NpgsqlStartupState.Instance);

                                    // 2.

                                    crypt_buf = new byte[prehashbytes.Length + saltServer.Length];
                                    prehashbytes.CopyTo(crypt_buf, 0);
                                    saltServer.CopyTo(crypt_buf, prehashbytes.Length);

                                    sb = new StringBuilder("md5"); // This is needed as the backend expects md5 result starts with "md5"
                                    hashResult = md5.ComputeHash(crypt_buf);
                                    foreach (byte b in hashResult)
                                    {
                                        sb.Append(b.ToString("x2"));
                                    }

                                    context.Authenticate(ENCODING_UTF8.GetBytes(sb.ToString()));

                                    break;
                                default:
                                    // Only AuthenticationClearTextPassword and AuthenticationMD5Password supported for now.
                                    errors.Add(
                                        new NpgsqlError(context.BackendProtocolVersion,
                                                        String.Format(resman.GetString("Exception_AuthenticationMethodNotSupported"), authType)));
                                    throw new NpgsqlException(errors);
                            }
                            break;
                        case BackEndMessageCode.RowDescription:
                            yield return lastRowDescription = new NpgsqlRowDescriptionV2(stream, context.OidToNameMapping, context.CompatVersion);
                            ;
                            break;
                        case BackEndMessageCode.DataRow:
                            yield return new ForwardsOnlyRow(new StringRowReaderV2(lastRowDescription, stream));
                            break;
                        case BackEndMessageCode.BinaryRow:
                            throw new NotSupportedException();
                        case BackEndMessageCode.ReadyForQuery:
                            ChangeState(context, NpgsqlReadyState.Instance);
                            if (errors.Count != 0)
                            {
                                throw new NpgsqlException(errors);
                            }
                            yield break;
                        case BackEndMessageCode.BackendKeyData:
                            context.BackEndKeyData = new NpgsqlBackEndKeyData(context.BackendProtocolVersion, stream);
                            break;
                        case BackEndMessageCode.NoticeResponse:
                            context.FireNotice(new NpgsqlError(context.BackendProtocolVersion, stream));
                            break;
                        case BackEndMessageCode.CompletedResponse:
                            yield return new CompletedResponse(stream);
                            break;

                        case BackEndMessageCode.CursorResponse:
                            // This is the cursor response message.
                            // It is followed by a C NULL terminated string with the name of
                            // the cursor in a FETCH case or 'blank' otherwise.
                            // In this case it should be always 'blank'.
                            // [FIXME] Get another name for this function.
                            NpgsqlEventLog.LogMsg(resman, "Log_ProtocolMessage", LogLevel.Debug, "CursorResponse");

                            String cursorName = PGUtil.ReadString(stream);
                            // Continue waiting for ReadyForQuery message.
                            break;

                        case BackEndMessageCode.EmptyQueryResponse:
                            NpgsqlEventLog.LogMsg(resman, "Log_ProtocolMessage", LogLevel.Debug, "EmptyQueryResponse");
                            PGUtil.ReadString(stream);
                            break;

                        case BackEndMessageCode.NotificationResponse:
                            context.FireNotification(new NpgsqlNotificationEventArgs(stream, false));
                            if (context.IsNotificationThreadRunning)
                            {
                                yield break;
                            }
                            break;
                        case BackEndMessageCode.IO_ERROR:
                            // Connection broken. Mono returns -1 instead of throw an exception as ms.net does.
                            throw new IOException();

                        default:
                            // This could mean a number of things
                            //   We've gotten out of sync with the backend?
                            //   We need to implement this type?
                            //   Backend has gone insane?
                            throw new DataException("Backend sent unrecognized response type");
                    }
                }
            }
        }
Beispiel #21
0
        /// <summary>
        /// Opens the physical connection to the server.
        /// </summary>
        /// <remarks>Usually called by the RequestConnector
        /// Method of the connection pool manager.</remarks>
        internal void Open()
        {
            ServerVersion = null;
            // If Connection.ConnectionString specifies a protocol version, we will
            // not try to fall back to version 2 on failure.

            _backendProtocolVersion = (settings.Protocol == ProtocolVersion.Unknown)
                                                                                  ? ProtocolVersion.Version3
                                                                                  : settings.Protocol;

            // Reset state to initialize new connector in pool.
            CurrentState = NpgsqlClosedState.Instance;

            // Get a raw connection, possibly SSL...
            CurrentState.Open(this);
            try
            {
                // Establish protocol communication and handle authentication...
                CurrentState.Startup(this);
            }
            catch (NpgsqlException ne)
            {
                // Check for protocol not supported.  If we have been told what protocol to use,
                // we will not try this step.
                if (settings.Protocol != ProtocolVersion.Unknown)
                {
                    throw;
                }
                // If we attempted protocol version 3, it may be possible to drop back to version 2.
                if (BackendProtocolVersion != ProtocolVersion.Version3)
                {
                    throw;
                }
                NpgsqlError Error0 = (NpgsqlError)ne.Errors[0];

                // If NpgsqlError..ctor() encounters a version 2 error,
                // it will set its own protocol version to version 2.  That way, we can tell
                // easily if the error was a FATAL: protocol error.
                if (Error0.BackendProtocolVersion != ProtocolVersion.Version2)
                {
                    throw;
                }
                // Try using the 2.0 protocol.
                _mediator.ResetResponses();
                BackendProtocolVersion = ProtocolVersion.Version2;
                CurrentState           = NpgsqlClosedState.Instance;

                // Get a raw connection, possibly SSL...
                CurrentState.Open(this);
                // Establish protocol communication and handle authentication...
                CurrentState.Startup(this);
            }

            // Change the state of connection to open and ready.
            State        = ConnectionState.Open;
            CurrentState = NpgsqlReadyState.Instance;

            // Fall back to the old way, SELECT VERSION().
            // This should not happen for protocol version 3+.
            if (ServerVersion == null)
            {
                using (NpgsqlCommand command = new NpgsqlCommand("set DATESTYLE TO ISO;select version();", this))
                {
                    ServerVersion = new Version(PGUtil.ExtractServerVersion((string)command.ExecuteScalar()));
                }
            }

            StringBuilder sbInit = new StringBuilder();

            // Adjust client encoding.
            NpgsqlParameterStatus clientEncodingParam = null;

            if (!ServerParameters.TryGetValue("client_encoding", out clientEncodingParam) ||
                !string.Equals(clientEncodingParam.ParameterValue, "UTF8", StringComparison.OrdinalIgnoreCase) && !string.Equals(clientEncodingParam.ParameterValue, "UNICODE", StringComparison.OrdinalIgnoreCase))
            {
                sbInit.AppendLine("SET CLIENT_ENCODING TO UTF8;");
            }

            if (!string.IsNullOrEmpty(settings.SearchPath))
            {
                // TODO: Add proper message when finding a semicolon in search_path.
                // This semicolon could lead to a sql injection security hole as someone could write in connection string:
                // searchpath=public;delete from table; and it would be executed.

                if (settings.SearchPath.Contains(";"))
                {
                    throw new InvalidOperationException();
                }

                sbInit.AppendLine("SET SEARCH_PATH=" + settings.SearchPath + ";");
            }

            if (!string.IsNullOrEmpty(settings.ApplicationName))
            {
                if (!SupportsApplicationName)
                {
                    //TODO
                    //throw new InvalidOperationException(resman.GetString("Exception_ApplicationNameNotSupported"));
                    throw new InvalidOperationException("ApplicationName not supported.");
                }

                if (settings.ApplicationName.Contains(";"))
                {
                    throw new InvalidOperationException();
                }

                sbInit.AppendLine("SET APPLICATION_NAME='" + settings.ApplicationName.Replace('\'', '-') + "';");
            }

            /*
             * Try to set SSL negotiation to 0. As of 2010-03-29, recent problems in SSL library implementations made
             * postgresql to add a parameter to set a value when to do this renegotiation or 0 to disable it.
             * Currently, Npgsql has a problem with renegotiation so, we are trying to disable it here.
             * This only works on postgresql servers where the ssl renegotiation settings is supported of course.
             * See http://lists.pgfoundry.org/pipermail/npgsql-devel/2010-February/001065.html for more information.
             */
            sbInit.AppendLine("SET ssl_renegotiation_limit=0;");

            /*
             * Set precision digits to maximum value possible. For postgresql before 9 it was 2, after that, it is 3.
             * This way, we set first to 2 and then to 3. If there is an error because of 3, it will have been set to 2 at least.
             * Check bug report #1010992 for more information.
             */
            sbInit.AppendLine("SET extra_float_digits=3;");
            try
            {
                new NpgsqlCommand(sbInit.ToString(), this).ExecuteBlind();
            }
            catch
            {
                foreach (var line in sbInit.ToString().Split(Environment.NewLine.ToCharArray()))
                {
                    try
                    {
                        if (line.Length > 0)
                        {
                            new NpgsqlCommand(line, this).ExecuteBlind();
                        }
                    }
                    catch { }
                }
            }

            // Make a shallow copy of the type mapping that the connector will own.
            // It is possible that the connector may add types to its private
            // mapping that will not be valid to another connector, even
            // if connected to the same backend version.
            _oidToNameMapping = NpgsqlTypesHelper.CreateAndLoadInitialTypesMapping(this).Clone();

            ProcessServerVersion();

            // The connector is now fully initialized. Beyond this point, it is
            // safe to release it back to the pool rather than closing it.
            IsInitialized = true;
        }
Beispiel #22
0
 internal NpgsqlNoticeEventArgs(NpgsqlError eNotice)
 {
     Notice = eNotice;
 }
        /// <value>Counts the numbers of Connections that share
        /// this Connector. Used in Release() to decide wether this
        /// connector is to be moved to the PooledConnectors list.</value>
        // internal int mShareCount;

        /// <summary>
        /// Opens the physical connection to the server.
        /// </summary>
        /// <remarks>Usually called by the RequestConnector
        /// Method of the connection pool manager.</remarks>
        internal void Open()
        {
            ProtocolVersion PV;

            // If Connection.ConnectionString specifies a protocol version, we will
            // not try to fall back to version 2 on failure.
            if (ConnectionString.Contains(ConnectionStringKeys.Protocol))
            {
                PV = ConnectionString.ToProtocolVersion(ConnectionStringKeys.Protocol);
            }
            else
            {
                PV = ProtocolVersion.Unknown;
            }

            _backendProtocolVersion = (PV == ProtocolVersion.Unknown) ? ProtocolVersion.Version3 : PV;

            // Reset state to initialize new connector in pool.
            Encoding     = Encoding.Default;
            CurrentState = NpgsqlClosedState.Instance;

            // Get a raw connection, possibly SSL...
            CurrentState.Open(this);
            // Establish protocol communication and handle authentication...
            CurrentState.Startup(this);

            // Check for protocol not supported.  If we have been told what protocol to use,
            // we will not try this step.
            if (_mediator.Errors.Count > 0 && PV == ProtocolVersion.Unknown)
            {
                // If we attempted protocol version 3, it may be possible to drop back to version 2.
                if (BackendProtocolVersion == ProtocolVersion.Version3)
                {
                    NpgsqlError Error0 = (NpgsqlError)_mediator.Errors[0];

                    // If NpgsqlError.ReadFromStream_Ver_3() encounters a version 2 error,
                    // it will set its own protocol version to version 2.  That way, we can tell
                    // easily if the error was a FATAL: protocol error.
                    if (Error0.BackendProtocolVersion == ProtocolVersion.Version2)
                    {
                        // Try using the 2.0 protocol.
                        _mediator.ResetResponses();
                        BackendProtocolVersion = ProtocolVersion.Version2;
                        CurrentState           = NpgsqlClosedState.Instance;

                        // Get a raw connection, possibly SSL...
                        CurrentState.Open(this);
                        // Establish protocol communication and handle authentication...
                        CurrentState.Startup(this);
                    }
                }
            }

            // Check for errors and do the Right Thing.
            // FIXME - CheckErrors needs to be moved to Connector
            CheckErrors();

            _backend_keydata = _mediator.BackendKeyData;

            // Change the state of connection to open and ready.
            _connection_state = ConnectionState.Open;
            CurrentState      = NpgsqlReadyState.Instance;

            String ServerVersionString = String.Empty;

            // First try to determine backend server version using the newest method.
            if (((NpgsqlParameterStatus)_mediator.Parameters["__npgsql_server_version"]) != null)
            {
                ServerVersionString = ((NpgsqlParameterStatus)_mediator.Parameters["__npgsql_server_version"]).ParameterValue;
            }


            // Fall back to the old way, SELECT VERSION().
            // This should not happen for protocol version 3+.
            if (ServerVersionString.Length == 0)
            {
                NpgsqlCommand command = new NpgsqlCommand("select version();set DATESTYLE TO ISO;", this);
                ServerVersionString = PGUtil.ExtractServerVersion((String)command.ExecuteScalar());
            }

            // Cook version string so we can use it for enabling/disabling things based on
            // backend version.
            ServerVersion = PGUtil.ParseServerVersion(ServerVersionString);

            // Adjust client encoding.

            //NpgsqlCommand commandEncoding1 = new NpgsqlCommand("show client_encoding", _connector);
            //String clientEncoding1 = (String)commandEncoding1.ExecuteScalar();

            if (ConnectionString.ToString(ConnectionStringKeys.Encoding, ConnectionStringDefaults.Encoding).ToUpper() == "UNICODE")
            {
                Encoding = Encoding.UTF8;
                NpgsqlCommand commandEncoding = new NpgsqlCommand("SET CLIENT_ENCODING TO UNICODE", this);
                commandEncoding.ExecuteNonQuery();
            }

            // Make a shallow copy of the type mapping that the connector will own.
            // It is possible that the connector may add types to its private
            // mapping that will not be valid to another connector, even
            // if connected to the same backend version.
            _oidToNameMapping = NpgsqlTypesHelper.CreateAndLoadInitialTypesMapping(this).Clone();

            ProcessServerVersion();

            // The connector is now fully initialized. Beyond this point, it is
            // safe to release it back to the pool rather than closing it.
            IsInitialized = true;
        }
Beispiel #24
0
        BackendMessage DoReadSingleMessage(DataRowLoadingMode dataRowLoadingMode = DataRowLoadingMode.NonSequential, bool ignoreNotifications = true)
        {
            NpgsqlError error = null;

            while (true)
            {
                var buf = Buffer;

                Buffer.Ensure(5);
                var messageCode = (BackendMessageCode) Buffer.ReadByte();
                Contract.Assume(Enum.IsDefined(typeof(BackendMessageCode), messageCode), "Unknown message code: " + messageCode);
                var len = Buffer.ReadInt32() - 4;  // Transmitted length includes itself

                if (messageCode == BackendMessageCode.DataRow && dataRowLoadingMode != DataRowLoadingMode.NonSequential)
                {
                    if (dataRowLoadingMode == DataRowLoadingMode.Skip)
                    {
                        Buffer.Skip(len);
                        continue;
                    }
                }
                else if (len > Buffer.ReadBytesLeft)
                {
                    buf = buf.EnsureOrAllocateTemp(len);
                }

                var msg = ParseServerMessage(buf, messageCode, len, dataRowLoadingMode);
                if (msg != null || !ignoreNotifications && (messageCode == BackendMessageCode.NoticeResponse || messageCode == BackendMessageCode.NotificationResponse))
                {
                    if (error != null)
                    {
                        Contract.Assert(messageCode == BackendMessageCode.ReadyForQuery, "Expected ReadyForQuery after ErrorResponse");
                        throw new NpgsqlException(error);
                    }
                    return msg;
                }
                else if (messageCode == BackendMessageCode.ErrorResponse)
                {
                    // An ErrorResponse is (almost) always followed by a ReadyForQuery. Save the error
                    // and throw it as an exception when the ReadyForQuery is received (next)
                    // The exception is during the startup/authentication phase, where the server closes
                    // the connection after an ErrorResponse
                    error = new NpgsqlError(buf);
                    if (State == ConnectorState.Connecting) {
                        throw new NpgsqlException(error);
                    }
                }
            }
        }
        private Object FastpathV3(Int32 fnid, Boolean resulttype, FastpathArg[] args)
        {
            // give  thread safety
            lock (stream)
            {
                // send the function call
                try
                {
                    Int32 l_msgLen = 0;
                    l_msgLen += 16;
                    for (Int32 i=0;i < args.Length;i++)
                        l_msgLen += args[i].SendSize();

                    stream.WriteByte((Byte)'F');
                    PGUtil.WriteInt32(stream,l_msgLen);
                    PGUtil.WriteInt32(stream,fnid);
                    PGUtil.WriteInt16(stream,1);
                    PGUtil.WriteInt16(stream,1);
                    PGUtil.WriteInt16(stream,(short)args.Length);

                    for (Int32 i = 0;i < args.Length;i++)
                        args[i].Send(stream);

                    PGUtil.WriteInt16(stream,1);

                    // This is needed, otherwise data can be lost
                    stream.Flush();

                }
                catch (Exception ex)
                {
                    throw new Exception(ex.ToString());
                }

                // Now handle the result

                // Now loop, reading the results
                Object result = null; // our result
                Exception error = null;
                Int32 c;
                Boolean l_endQuery = false;
                Byte[] input_buffer = new Byte[512];

                while (!l_endQuery)
                {
                    c = (Char)stream.ReadByte();

                    switch (c)
                    {
                    case 'A':	// Asynchronous Notify
                        Int32 msglen = PGUtil.ReadInt32(stream,input_buffer);
                        Int32 pid = PGUtil.ReadInt32(stream,input_buffer);
                        String msg = PGUtil.ReadString(stream,conn.Connector.Encoding);
                        PGUtil.ReadString(stream,conn.Connector.Encoding);
                        String param = PGUtil.ReadString(stream,conn.Connector.Encoding);

                        conn.Connector.CheckErrorsAndNotifications();
                        break;
                        //------------------------------
                        // Error message returned
                    case 'E':
                        NpgsqlError e = new NpgsqlError(conn.BackendProtocolVersion);
                        e.ReadFromStream(stream,conn.Connector.Encoding);
                        throw new Exception(e.ToString());

                        //------------------------------
                        // Notice from backend
                    case 'N':
                        Int32 l_nlen = PGUtil.ReadInt32(stream,input_buffer);

                        NpgsqlError e1 = new NpgsqlError(conn.BackendProtocolVersion);
                        e1.ReadFromStream(stream,conn.Connector.Encoding);
                        conn.Connector.Mediator.Errors.Add(e1);

                        break;

                    case 'V':
                        Int32 l_msgLen = PGUtil.ReadInt32(stream,input_buffer);
                        Int32 l_valueLen = PGUtil.ReadInt32(stream,input_buffer);

                        if (l_valueLen == -1)
                        {
                            //null value
                        }
                        else if (l_valueLen == 0)
                        {
                            result = new Byte[0];
                        }
                        else
                        {
                            // Return an Integer if
                            if (resulttype)

                                result = PGUtil.ReadInt32(stream,input_buffer);
                            else
                            {
                                Byte[] buf = new Byte[l_valueLen];

                                Int32 bytes_from_stream = 0;
                                Int32 total_bytes_read = 0;
                                Int32 size = l_valueLen;
                                do
                                {
                                    bytes_from_stream = stream.Read(buf, total_bytes_read, size);
                                    total_bytes_read += bytes_from_stream;
                                    size -= bytes_from_stream;
                                }
                                while(size > 0);

                                result = buf;
                            }
                        }
                        break;

                    case 'Z':
                        //TODO: use size better
                        if (PGUtil.ReadInt32(stream,input_buffer) != 5)
                            throw new Exception("Received Z" );
                        //TODO: handle transaction status
                        Char l_tStatus = (Char)stream.ReadByte();
                        l_endQuery = true;
                        break;

                    default:
                        throw new Exception("postgresql.fp.protocol received " + c.ToString());
                    }
                }

                if ( error != null )
                    throw error;

                return result;
            }
        }