FireNotice() private method

private FireNotice ( Npgsql.NpgsqlError e ) : void
e Npgsql.NpgsqlError
return void
Exemplo n.º 1
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));
                    }
                }
            }
        }
Exemplo n.º 3
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");
                    }
                }
            }
        }
        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");
                    }
                }
            }
        }