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)); } } } }
public void AddParameterStatus(NpgsqlParameterStatus ps) { if (_serverParameters.ContainsKey(ps.Parameter)) { _serverParameters[ps.Parameter] = ps; } else { _serverParameters.Add(ps.Parameter, ps); } _useConformantStrings = null; }
public void AddParameterStatus(NpgsqlParameterStatus ps) { if (_serverParameters.ContainsKey(ps.Parameter)) { _serverParameters[ps.Parameter] = ps; } else { _serverParameters.Add(ps.Parameter, ps); } if (ps.Parameter == "standard_conforming_strings") { NativeToBackendTypeConverterOptions.UseConformantStrings = (ps.ParameterValue == "on"); } }
public void AddParameterStatus(String Key, NpgsqlParameterStatus PS) { _parameters[Key] = PS; }
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)); } } }
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)); } } }
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)); } } } }