internal void FireNotice(NpgsqlError e) { if (Notice != null) { try { Notice(this, new NpgsqlNoticeEventArgs(e)); } catch { } //Eat exceptions from user code. } }
internal NpgsqlNoticeEventArgs(NpgsqlError eNotice) { Notice = eNotice; }
/*/// <value>Counts the numbers of Connections that share * /// this Connector. Used in Release() to decide wether this * /// connector is to be moved to the PooledConnectors list.</value> * // internal int mShareCount;*/ /// <summary> /// Opens the physical connection to the server. /// </summary> /// <remarks>Usually called by the RequestConnector /// Method of the connection pool manager.</remarks> internal void Open() { ServerVersion = null; // If Connection.ConnectionString specifies a protocol version, we will // not try to fall back to version 2 on failure. _backendProtocolVersion = (settings.Protocol == ProtocolVersion.Unknown) ? ProtocolVersion.Version3 : settings.Protocol; // Reset state to initialize new connector in pool. CurrentState = NpgsqlClosedState.Instance; // Keep track of time remaining; Even though there may be multiple timeout-able calls, // this allows us to still respect the caller's timeout expectation. int connectTimeRemaining = this.ConnectionTimeout * 1000; DateTime attemptStart = DateTime.Now; // Get a raw connection, possibly SSL... CurrentState.Open(this, connectTimeRemaining); try { // Establish protocol communication and handle authentication... CurrentState.Startup(this); } catch (NpgsqlException ne) { connectTimeRemaining -= Convert.ToInt32((DateTime.Now - attemptStart).TotalMilliseconds); // Check for protocol not supported. If we have been told what protocol to use, // we will not try this step. if (settings.Protocol != ProtocolVersion.Unknown) { throw; } // If we attempted protocol version 3, it may be possible to drop back to version 2. if (BackendProtocolVersion != ProtocolVersion.Version3) { throw; } NpgsqlError Error0 = (NpgsqlError)ne.Errors[0]; // If NpgsqlError..ctor() encounters a version 2 error, // it will set its own protocol version to version 2. That way, we can tell // easily if the error was a FATAL: protocol error. if (Error0.BackendProtocolVersion != ProtocolVersion.Version2) { throw; } // Try using the 2.0 protocol. _mediator.ResetResponses(); BackendProtocolVersion = ProtocolVersion.Version2; CurrentState = NpgsqlClosedState.Instance; // Get a raw connection, possibly SSL... CurrentState.Open(this, connectTimeRemaining); // Establish protocol communication and handle authentication... CurrentState.Startup(this); } // Change the state of connection to open and ready. _connection_state = ConnectionState.Open; CurrentState = NpgsqlReadyState.Instance; // Fall back to the old way, SELECT VERSION(). // This should not happen for protocol version 3+. if (ServerVersion == null) { //NpgsqlCommand command = new NpgsqlCommand("set DATESTYLE TO ISO;select version();", this); //ServerVersion = new Version(PGUtil.ExtractServerVersion((string) command.ExecuteScalar())); using (NpgsqlCommand command = new NpgsqlCommand("set DATESTYLE TO ISO;select version();", this)) { ServerVersion = new Version(PGUtil.ExtractServerVersion((string)command.ExecuteScalar())); } } // Adjust client encoding. NpgsqlParameterStatus clientEncodingParam = null; if ( !ServerParameters.TryGetValue("client_encoding", out clientEncodingParam) || (!string.Equals(clientEncodingParam.ParameterValue, "UTF8", StringComparison.OrdinalIgnoreCase) && !string.Equals(clientEncodingParam.ParameterValue, "UNICODE", StringComparison.OrdinalIgnoreCase)) ) { new NpgsqlCommand("SET CLIENT_ENCODING TO UTF8", this).ExecuteBlind(); } if (!string.IsNullOrEmpty(settings.SearchPath)) { /*NpgsqlParameter p = new NpgsqlParameter("p", DbType.String); * p.Value = settings.SearchPath; * NpgsqlCommand commandSearchPath = new NpgsqlCommand("SET SEARCH_PATH TO :p,public", this); * commandSearchPath.Parameters.Add(p); * commandSearchPath.ExecuteNonQuery();*/ /*NpgsqlParameter p = new NpgsqlParameter("p", DbType.String); * p.Value = settings.SearchPath; * NpgsqlCommand commandSearchPath = new NpgsqlCommand("SET SEARCH_PATH TO :p,public", this); * commandSearchPath.Parameters.Add(p); * commandSearchPath.ExecuteNonQuery();*/ // TODO: Add proper message when finding a semicolon in search_path. // This semicolon could lead to a sql injection security hole as someone could write in connection string: // searchpath=public;delete from table; and it would be executed. if (settings.SearchPath.Contains(";")) { throw new InvalidOperationException(); } // This is using string concatenation because set search_path doesn't allow type casting. ::text NpgsqlCommand commandSearchPath = new NpgsqlCommand("SET SEARCH_PATH=" + settings.SearchPath, this); commandSearchPath.ExecuteBlind(); } if (!string.IsNullOrEmpty(settings.ApplicationName)) { if (!SupportsApplicationName) { //TODO //throw new InvalidOperationException(resman.GetString("Exception_ApplicationNameNotSupported")); throw new InvalidOperationException("ApplicationName not supported."); } if (settings.ApplicationName.Contains(";")) { throw new InvalidOperationException(); } NpgsqlCommand commandApplicationName = new NpgsqlCommand("SET APPLICATION_NAME='" + settings.ApplicationName + "'", this); commandApplicationName.ExecuteBlind(); } /* * Try to set SSL negotiation to 0. As of 2010-03-29, recent problems in SSL library implementations made * postgresql to add a parameter to set a value when to do this renegotiation or 0 to disable it. * Currently, Npgsql has a problem with renegotiation so, we are trying to disable it here. * This only works on postgresql servers where the ssl renegotiation settings is supported of course. * See http://lists.pgfoundry.org/pipermail/npgsql-devel/2010-February/001065.html for more information. */ try { NpgsqlCommand commandSslrenegotiation = new NpgsqlCommand("SET ssl_renegotiation_limit=0", this); commandSslrenegotiation.ExecuteBlind(); } catch {} /* * Set precision digits to maximum value possible. For postgresql before 9 it was 2, after that, it is 3. * This way, we set first to 2 and then to 3. If there is an error because of 3, it will have been set to 2 at least. * Check bug report #1010992 for more information. */ try { NpgsqlCommand commandSingleDoublePrecision = new NpgsqlCommand("SET extra_float_digits=2;SET extra_float_digits=3;", this); commandSingleDoublePrecision.ExecuteBlind(); } catch {} /* * Set lc_monetary format to 'C' ir order to get a culture agnostic representation of money. * I noticed that on Windows, even when the lc_monetary is English_United States.UTF-8, negative * money is formatted as ($value) with parentheses to indicate negative value. * By going with a culture agnostic format, we get a consistent behavior. */ try { NpgsqlCommand commandMonetaryFormatC = new NpgsqlCommand("SET lc_monetary='C';", this); commandMonetaryFormatC.ExecuteBlind(); } catch { } // Make a shallow copy of the type mapping that the connector will own. // It is possible that the connector may add types to its private // mapping that will not be valid to another connector, even // if connected to the same backend version. _oidToNameMapping = NpgsqlTypesHelper.CreateAndLoadInitialTypesMapping(this).Clone(); ProcessServerVersion(); // The connector is now fully initialized. Beyond this point, it is // safe to release it back to the pool rather than closing it. IsInitialized = true; }
private Object FastpathV2(Int32 fnid, Boolean resulttype, FastpathArg[] args) { // added Oct 7 1998 to give us thread safety lock (stream) { // send the function call try { // 70 is 'F' in ASCII. Note: don't use SendChar() here as it adds padding // that confuses the backend. The 0 terminates the command line. stream.WriteByte((Byte)70); stream.WriteByte((Byte)0); PGUtil.WriteInt32(stream,fnid); PGUtil.WriteInt32(stream,args.Length); for (Int32 i = 0;i < args.Length;i++) args[i].Send(stream); // This is needed, otherwise data can be lost stream.Flush(); } catch (IOException ioe) { //Should be sending exception as second arg. throw new Exception("postgresql.fp.send: " + ioe.ToString()); } // Now handle the result // Now loop, reading the results Object result = null; // our result String errorMessage = ""; Byte[] input_buffer = new Byte[512]; Int32 c; Boolean l_endQuery = false; while (!l_endQuery) { c = (Char)stream.ReadByte(); switch (c) { case 'A': // Asynchronous Notify //TODO: do something with this Int32 pid = PGUtil.ReadInt32(stream,input_buffer); String msg = PGUtil.ReadString(stream,conn.Connector.Encoding); conn.Connector.CheckErrorsAndNotifications(); break; //------------------------------ // Error message returned case 'E': NpgsqlError e = new NpgsqlError(conn.BackendProtocolVersion); e.ReadFromStream(stream,conn.Connector.Encoding); errorMessage += e.Message; break; //------------------------------ // Notice from backend case 'N': NpgsqlError notice = new NpgsqlError(conn.BackendProtocolVersion); notice.ReadFromStream(stream,conn.Connector.Encoding); errorMessage += notice.Message; break; case 'V': Char l_nextChar = (Char)stream.ReadByte(); if (l_nextChar == 'G') { Int32 sz = PGUtil.ReadInt32(stream,input_buffer); // Return an Integer if if (resulttype) result = PGUtil.ReadInt32(stream,input_buffer); else { Byte[] buf = new Byte[sz]; Int32 bytes_from_stream = 0; Int32 total_bytes_read = 0; Int32 size = sz; do { bytes_from_stream = stream.Read(buf, total_bytes_read, size); total_bytes_read += bytes_from_stream; size -= bytes_from_stream; } while(size > 0); result = buf; } //There should be a trailing '0' Int32 l_endChar = (Char)stream.ReadByte(); } else { //it must have been a '0', thus no results } break; case 'Z': l_endQuery = true; break; default: throw new Exception("postgresql.fp.protocol " + c.ToString()); } } if ( errorMessage != null ) throw new Exception("postgresql.fp.error" + errorMessage); return result; } }
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)); } } } }
private Object FastpathV3(Int32 fnid, Boolean resulttype, FastpathArg[] args) { // give thread safety lock (stream) { // send the function call { Int32 l_msgLen = 0; l_msgLen += 16; for (Int32 i = 0; i < args.Length; i++) { l_msgLen += args[i].SendSize(); } stream .WriteByte(ASCIIByteArrays.FunctionCallMessageCode) .WriteInt32(l_msgLen) .WriteInt32(fnid) .WriteInt16(1) .WriteInt16(1) .WriteInt16((short)args.Length); for (Int32 i = 0; i < args.Length; i++) { args[i].Send(stream); } stream.WriteInt16(1); // This is needed, otherwise data can be lost stream.Flush(); } // Now handle the result // Now loop, reading the results Object result = null; // our result Exception error = null; Int32 c; Boolean l_endQuery = false; while (!l_endQuery) { c = (Char) stream.ReadByte(); switch (c) { case 'A': // Asynchronous Notify Int32 msglen = stream.ReadInt32(); Int32 pid = stream.ReadInt32(); String msg = stream.ReadString(); stream.ReadString(); String param = stream.ReadString(); break; //------------------------------ // Error message returned case 'E': NpgsqlError e = new NpgsqlError(stream); throw new NpgsqlException(e.ToString()); //------------------------------ // Notice from backend case 'N': Int32 l_nlen = stream.ReadInt32(); conn.Connector.FireNotice(new NpgsqlError(stream)); break; case 'V': Int32 l_msgLen = stream.ReadInt32(); Int32 l_valueLen = stream.ReadInt32(); if (l_valueLen == -1) { //null value } else if (l_valueLen == 0) { result = new Byte[0]; } else { // Return an Integer if if (resulttype) { result = stream.ReadInt32(); } else { Byte[] buf = new Byte[l_valueLen]; Int32 bytes_from_stream = 0; Int32 total_bytes_read = 0; Int32 size = l_valueLen; do { bytes_from_stream = stream.Read(buf, total_bytes_read, size); total_bytes_read += bytes_from_stream; size -= bytes_from_stream; } while (size > 0); result = buf; } } break; case 'Z': //TODO: use size better if (stream.ReadInt32() != 5) { throw new Exception("Received Z"); } //TODO: handle transaction status Char l_tStatus = (Char) stream.ReadByte(); l_endQuery = true; break; default: throw new Exception(string.Format("postgresql.fp.protocol received {0}", c)); } } if (error != null) { throw error; } return result; } }
internal NpgsqlException(NpgsqlError error) : base(error.ToString()) { this.errors = new NpgsqlError[1]; errors[0] = error; }
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)); } } }
protected virtual void ProcessBackendResponses_Ver_2( NpgsqlConnector context ) { NpgsqlEventLog.LogMethodEnter(LogLevel.Debug, CLASSNAME, "ProcessBackendResponses"); Stream stream = context.Stream; NpgsqlMediator mediator = context.Mediator; // Often used buffer Byte[] inputBuffer = new Byte[ 4 ]; Boolean readyForQuery = false; byte[] asciiRowBytes = new byte[300]; char[] asciiRowChars = new char[300]; while (!readyForQuery) { // Check the first Byte of response. switch ( stream.ReadByte() ) { case NpgsqlMessageTypes_Ver_2.ErrorResponse : { NpgsqlError error = new NpgsqlError(context.BackendProtocolVersion); error.ReadFromStream(stream, context.Encoding); error.ErrorSql = mediator.SqlSent; mediator.Errors.Add(error); NpgsqlEventLog.LogMsg(resman, "Log_ErrorResponse", LogLevel.Debug, error.Message); } // Return imediately if it is in the startup state or connected state as // there is no more messages to consume. // Possible error in the NpgsqlStartupState: // Invalid password. // Possible error in the NpgsqlConnectedState: // No pg_hba.conf configured. if (! mediator.RequireReadyForQuery) { return; } break; case NpgsqlMessageTypes_Ver_2.AuthenticationRequest : NpgsqlEventLog.LogMsg(resman, "Log_ProtocolMessage", LogLevel.Debug, "AuthenticationRequest"); { Int32 authType = PGUtil.ReadInt32(stream, inputBuffer); if ( authType == NpgsqlMessageTypes_Ver_2.AuthenticationOk ) { NpgsqlEventLog.LogMsg(resman, "Log_AuthenticationOK", LogLevel.Debug); break; } if ( authType == NpgsqlMessageTypes_Ver_2.AuthenticationClearTextPassword ) { NpgsqlEventLog.LogMsg(resman, "Log_AuthenticationClearTextRequest", LogLevel.Debug); // Send the PasswordPacket. ChangeState( context, NpgsqlStartupState.Instance ); context.Authenticate(context.Password); break; } if ( authType == NpgsqlMessageTypes_Ver_2.AuthenticationMD5Password ) { NpgsqlEventLog.LogMsg(resman, "Log_AuthenticationMD5Request", LogLevel.Debug); // Now do the "MD5-Thing" // for this the Password has to be: // 1. md5-hashed with the username as salt // 2. md5-hashed again with the salt we get from the backend MD5 md5 = MD5.Create(); // 1. byte[] passwd = context.Encoding.GetBytes(context.Password); byte[] saltUserName = context.Encoding.GetBytes(context.UserName); byte[] crypt_buf = new byte[passwd.Length + saltUserName.Length]; passwd.CopyTo(crypt_buf, 0); saltUserName.CopyTo(crypt_buf, passwd.Length); StringBuilder sb = new StringBuilder (); byte[] hashResult = md5.ComputeHash(crypt_buf); foreach (byte b in hashResult) sb.Append (b.ToString ("x2")); String prehash = sb.ToString(); byte[] prehashbytes = context.Encoding.GetBytes(prehash); byte[] saltServer = new byte[4]; stream.Read(saltServer, 0, 4); // Send the PasswordPacket. ChangeState( context, NpgsqlStartupState.Instance ); // 2. crypt_buf = new byte[prehashbytes.Length + saltServer.Length]; prehashbytes.CopyTo(crypt_buf, 0); saltServer.CopyTo(crypt_buf, prehashbytes.Length); sb = new StringBuilder ("md5"); // This is needed as the backend expects md5 result starts with "md5" hashResult = md5.ComputeHash(crypt_buf); foreach (byte b in hashResult) sb.Append (b.ToString ("x2")); context.Authenticate(sb.ToString ()); break; } // Only AuthenticationClearTextPassword and AuthenticationMD5Password supported for now. mediator.Errors.Add(new NpgsqlError(context.BackendProtocolVersion, String.Format(resman.GetString("Exception_AuthenticationMethodNotSupported"), authType))); } return; case NpgsqlMessageTypes_Ver_2.RowDescription: // This is the RowDescription message. NpgsqlEventLog.LogMsg(resman, "Log_ProtocolMessage", LogLevel.Debug, "RowDescription"); { NpgsqlRowDescription rd = new NpgsqlRowDescription(context.BackendProtocolVersion); rd.ReadFromStream(stream, context.Encoding, context.OidToNameMapping); // Initialize the array list which will contain the data from this rowdescription. mediator.AddRowDescription(rd); } // Now wait for the AsciiRow messages. break; case NpgsqlMessageTypes_Ver_2.AsciiRow: // This is the AsciiRow message. NpgsqlEventLog.LogMsg(resman, "Log_ProtocolMessage", LogLevel.Debug, "AsciiRow"); { NpgsqlAsciiRow asciiRow = new NpgsqlAsciiRow(context.Mediator.LastRowDescription, context.BackendProtocolVersion, asciiRowBytes, asciiRowChars); asciiRow.ReadFromStream(stream, context.Encoding); // Add this row to the rows array. mediator.AddAsciiRow(asciiRow); } // Now wait for CompletedResponse message. break; case NpgsqlMessageTypes_Ver_2.BinaryRow: NpgsqlEventLog.LogMsg(resman, "Log_ProtocolMessage", LogLevel.Debug, "BinaryRow"); { NpgsqlBinaryRow binaryRow = new NpgsqlBinaryRow(context.Mediator.LastRowDescription); binaryRow.ReadFromStream(stream, context.Encoding); mediator.AddBinaryRow(binaryRow); } break; case NpgsqlMessageTypes_Ver_2.ReadyForQuery : NpgsqlEventLog.LogMsg(resman, "Log_ProtocolMessage", LogLevel.Debug, "ReadyForQuery"); readyForQuery = true; ChangeState( context, NpgsqlReadyState.Instance ); break; case NpgsqlMessageTypes_Ver_2.BackendKeyData : NpgsqlEventLog.LogMsg(resman, "Log_ProtocolMessage", LogLevel.Debug, "BackendKeyData"); // BackendKeyData message. NpgsqlBackEndKeyData backend_keydata = new NpgsqlBackEndKeyData(context.BackendProtocolVersion); backend_keydata.ReadFromStream(stream); mediator.SetBackendKeydata(backend_keydata); // Wait for ReadForQuery message break; ; case NpgsqlMessageTypes_Ver_2.NoticeResponse : { NpgsqlError notice = new NpgsqlError(context.BackendProtocolVersion); notice.ReadFromStream(stream, context.Encoding); mediator.Notices.Add(notice); NpgsqlEventLog.LogMsg(resman, "Log_NoticeResponse", LogLevel.Debug, notice.Message); } // Wait for ReadForQuery message break; case NpgsqlMessageTypes_Ver_2.CompletedResponse : // This is the CompletedResponse message. // Get the string returned. String result = PGUtil.ReadString(stream, context.Encoding); NpgsqlEventLog.LogMsg(resman, "Log_CompletedResponse", LogLevel.Debug, result); // Add result from the processing. mediator.AddCompletedResponse(result); // Now wait for ReadyForQuery message. break; case NpgsqlMessageTypes_Ver_2.CursorResponse : // This is the cursor response message. // It is followed by a C NULL terminated string with the name of // the cursor in a FETCH case or 'blank' otherwise. // In this case it should be always 'blank'. // [FIXME] Get another name for this function. NpgsqlEventLog.LogMsg(resman, "Log_ProtocolMessage", LogLevel.Debug, "CursorResponse"); String cursorName = PGUtil.ReadString(stream, context.Encoding); // Continue waiting for ReadyForQuery message. break; case NpgsqlMessageTypes_Ver_2.EmptyQueryResponse : NpgsqlEventLog.LogMsg(resman, "Log_ProtocolMessage", LogLevel.Debug, "EmptyQueryResponse"); PGUtil.ReadString(stream, context.Encoding); break; case NpgsqlMessageTypes_Ver_2.NotificationResponse : NpgsqlEventLog.LogMsg(resman, "Log_ProtocolMessage", LogLevel.Debug, "NotificationResponse"); Int32 PID = PGUtil.ReadInt32(stream, inputBuffer); String notificationResponse = PGUtil.ReadString( stream, context.Encoding ); mediator.AddNotification(new NpgsqlNotificationEventArgs(PID, notificationResponse)); if (context.IsNotificationThreadRunning) readyForQuery = true; // Wait for ReadForQuery message break; case -1: // Connection broken. Mono returns -1 instead of throw an exception as ms.net does. throw new IOException(); default : // This could mean a number of things // We've gotten out of sync with the backend? // We need to implement this type? // Backend has gone insane? // FIXME // what exception should we really throw here? throw new NotSupportedException("Backend sent unrecognized response type"); } } }
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 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)); } } }
protected virtual void ProcessBackendResponses_Ver_2(NpgsqlConnector context) { NpgsqlEventLog.LogMethodEnter(LogLevel.Debug, CLASSNAME, "ProcessBackendResponses"); Stream stream = context.Stream; NpgsqlMediator mediator = context.Mediator; // Often used buffer Byte[] inputBuffer = new Byte[4]; Boolean readyForQuery = false; byte[] asciiRowBytes = new byte[300]; char[] asciiRowChars = new char[300]; while (!readyForQuery) { // Check the first Byte of response. switch (stream.ReadByte()) { case NpgsqlMessageTypes_Ver_2.ErrorResponse: { NpgsqlError error = new NpgsqlError(context.BackendProtocolVersion); error.ReadFromStream(stream, context.Encoding); error.ErrorSql = mediator.SqlSent; mediator.Errors.Add(error); NpgsqlEventLog.LogMsg(resman, "Log_ErrorResponse", LogLevel.Debug, error.Message); } // Return imediately if it is in the startup state or connected state as // there is no more messages to consume. // Possible error in the NpgsqlStartupState: // Invalid password. // Possible error in the NpgsqlConnectedState: // No pg_hba.conf configured. if (!mediator.RequireReadyForQuery) { return; } break; case NpgsqlMessageTypes_Ver_2.AuthenticationRequest: NpgsqlEventLog.LogMsg(resman, "Log_ProtocolMessage", LogLevel.Debug, "AuthenticationRequest"); { Int32 authType = PGUtil.ReadInt32(stream, inputBuffer); if (authType == NpgsqlMessageTypes_Ver_2.AuthenticationOk) { NpgsqlEventLog.LogMsg(resman, "Log_AuthenticationOK", LogLevel.Debug); break; } if (authType == NpgsqlMessageTypes_Ver_2.AuthenticationClearTextPassword) { NpgsqlEventLog.LogMsg(resman, "Log_AuthenticationClearTextRequest", LogLevel.Debug); // Send the PasswordPacket. ChangeState(context, NpgsqlStartupState.Instance); context.Authenticate(context.Password); break; } if (authType == NpgsqlMessageTypes_Ver_2.AuthenticationMD5Password) { NpgsqlEventLog.LogMsg(resman, "Log_AuthenticationMD5Request", LogLevel.Debug); // Now do the "MD5-Thing" // for this the Password has to be: // 1. md5-hashed with the username as salt // 2. md5-hashed again with the salt we get from the backend MD5 md5 = MD5.Create(); // 1. byte[] passwd = context.Encoding.GetBytes(context.Password); byte[] saltUserName = context.Encoding.GetBytes(context.UserName); byte[] crypt_buf = new byte[passwd.Length + saltUserName.Length]; passwd.CopyTo(crypt_buf, 0); saltUserName.CopyTo(crypt_buf, passwd.Length); StringBuilder sb = new StringBuilder(); byte[] hashResult = md5.ComputeHash(crypt_buf); foreach (byte b in hashResult) { sb.Append(b.ToString("x2")); } String prehash = sb.ToString(); byte[] prehashbytes = context.Encoding.GetBytes(prehash); byte[] saltServer = new byte[4]; stream.Read(saltServer, 0, 4); // Send the PasswordPacket. ChangeState(context, NpgsqlStartupState.Instance); // 2. crypt_buf = new byte[prehashbytes.Length + saltServer.Length]; prehashbytes.CopyTo(crypt_buf, 0); saltServer.CopyTo(crypt_buf, prehashbytes.Length); sb = new StringBuilder("md5"); // This is needed as the backend expects md5 result starts with "md5" hashResult = md5.ComputeHash(crypt_buf); foreach (byte b in hashResult) { sb.Append(b.ToString("x2")); } context.Authenticate(sb.ToString()); break; } // Only AuthenticationClearTextPassword and AuthenticationMD5Password supported for now. mediator.Errors.Add(new NpgsqlError(context.BackendProtocolVersion, String.Format(resman.GetString("Exception_AuthenticationMethodNotSupported"), authType))); } return; case NpgsqlMessageTypes_Ver_2.RowDescription: // This is the RowDescription message. NpgsqlEventLog.LogMsg(resman, "Log_ProtocolMessage", LogLevel.Debug, "RowDescription"); { NpgsqlRowDescription rd = new NpgsqlRowDescription(context.BackendProtocolVersion); rd.ReadFromStream(stream, context.Encoding, context.OidToNameMapping); // Initialize the array list which will contain the data from this rowdescription. mediator.AddRowDescription(rd); } // Now wait for the AsciiRow messages. break; case NpgsqlMessageTypes_Ver_2.AsciiRow: // This is the AsciiRow message. NpgsqlEventLog.LogMsg(resman, "Log_ProtocolMessage", LogLevel.Debug, "AsciiRow"); { NpgsqlAsciiRow asciiRow = new NpgsqlAsciiRow(context.Mediator.LastRowDescription, context.BackendProtocolVersion, asciiRowBytes, asciiRowChars); asciiRow.ReadFromStream(stream, context.Encoding); // Add this row to the rows array. mediator.AddAsciiRow(asciiRow); } // Now wait for CompletedResponse message. break; case NpgsqlMessageTypes_Ver_2.BinaryRow: NpgsqlEventLog.LogMsg(resman, "Log_ProtocolMessage", LogLevel.Debug, "BinaryRow"); { NpgsqlBinaryRow binaryRow = new NpgsqlBinaryRow(context.Mediator.LastRowDescription); binaryRow.ReadFromStream(stream, context.Encoding); mediator.AddBinaryRow(binaryRow); } break; case NpgsqlMessageTypes_Ver_2.ReadyForQuery: NpgsqlEventLog.LogMsg(resman, "Log_ProtocolMessage", LogLevel.Debug, "ReadyForQuery"); readyForQuery = true; ChangeState(context, NpgsqlReadyState.Instance); break; case NpgsqlMessageTypes_Ver_2.BackendKeyData: NpgsqlEventLog.LogMsg(resman, "Log_ProtocolMessage", LogLevel.Debug, "BackendKeyData"); // BackendKeyData message. NpgsqlBackEndKeyData backend_keydata = new NpgsqlBackEndKeyData(context.BackendProtocolVersion); backend_keydata.ReadFromStream(stream); mediator.SetBackendKeydata(backend_keydata); // Wait for ReadForQuery message break; ; case NpgsqlMessageTypes_Ver_2.NoticeResponse: { NpgsqlError notice = new NpgsqlError(context.BackendProtocolVersion); notice.ReadFromStream(stream, context.Encoding); mediator.Notices.Add(notice); NpgsqlEventLog.LogMsg(resman, "Log_NoticeResponse", LogLevel.Debug, notice.Message); } // Wait for ReadForQuery message break; case NpgsqlMessageTypes_Ver_2.CompletedResponse: // This is the CompletedResponse message. // Get the string returned. String result = PGUtil.ReadString(stream, context.Encoding); NpgsqlEventLog.LogMsg(resman, "Log_CompletedResponse", LogLevel.Debug, result); // Add result from the processing. mediator.AddCompletedResponse(result); // Now wait for ReadyForQuery message. break; case NpgsqlMessageTypes_Ver_2.CursorResponse: // This is the cursor response message. // It is followed by a C NULL terminated string with the name of // the cursor in a FETCH case or 'blank' otherwise. // In this case it should be always 'blank'. // [FIXME] Get another name for this function. NpgsqlEventLog.LogMsg(resman, "Log_ProtocolMessage", LogLevel.Debug, "CursorResponse"); String cursorName = PGUtil.ReadString(stream, context.Encoding); // Continue waiting for ReadyForQuery message. break; case NpgsqlMessageTypes_Ver_2.EmptyQueryResponse: NpgsqlEventLog.LogMsg(resman, "Log_ProtocolMessage", LogLevel.Debug, "EmptyQueryResponse"); PGUtil.ReadString(stream, context.Encoding); break; case NpgsqlMessageTypes_Ver_2.NotificationResponse: NpgsqlEventLog.LogMsg(resman, "Log_ProtocolMessage", LogLevel.Debug, "NotificationResponse"); Int32 PID = PGUtil.ReadInt32(stream, inputBuffer); String notificationResponse = PGUtil.ReadString(stream, context.Encoding); mediator.AddNotification(new NpgsqlNotificationEventArgs(PID, notificationResponse)); if (context.IsNotificationThreadRunning) { readyForQuery = true; } // Wait for ReadForQuery message break; case -1: // Connection broken. Mono returns -1 instead of throw an exception as ms.net does. throw new IOException(); default: // This could mean a number of things // We've gotten out of sync with the backend? // We need to implement this type? // Backend has gone insane? // FIXME // what exception should we really throw here? throw new NotSupportedException("Backend sent unrecognized response type"); } } }
BackendMessage DoReadSingleMessage(DataRowLoadingMode dataRowLoadingMode = DataRowLoadingMode.NonSequential, bool ignoreNotifications = true) { NpgsqlError error = null; while (true) { var buf = Buffer; Buffer.Ensure(5); var messageCode = (BackendMessageCode) Buffer.ReadByte(); Contract.Assume(Enum.IsDefined(typeof(BackendMessageCode), messageCode), "Unknown message code: " + messageCode); var len = Buffer.ReadInt32() - 4; // Transmitted length includes itself if (messageCode == BackendMessageCode.DataRow && dataRowLoadingMode != DataRowLoadingMode.NonSequential) { if (dataRowLoadingMode == DataRowLoadingMode.Skip) { Buffer.Skip(len); continue; } } else if (len > Buffer.ReadBytesLeft) { buf = buf.EnsureOrAllocateTemp(len); } var msg = ParseServerMessage(buf, messageCode, len, dataRowLoadingMode); if (msg != null || !ignoreNotifications && (messageCode == BackendMessageCode.NoticeResponse || messageCode == BackendMessageCode.NotificationResponse)) { if (error != null) { Contract.Assert(messageCode == BackendMessageCode.ReadyForQuery, "Expected ReadyForQuery after ErrorResponse"); throw new NpgsqlException(error); } return msg; } else if (messageCode == BackendMessageCode.ErrorResponse) { error = new NpgsqlError(buf); } } }
private Object FastpathV2(Int32 fnid, Boolean resulttype, FastpathArg[] args) { // added Oct 7 1998 to give us thread safety lock (stream) { // send the function call stream .WriteBytesNullTerminated((byte)ASCIIBytes.F) .WriteInt32(fnid) .WriteInt32(args.Length); for (Int32 i = 0; i < args.Length; i++) { args[i].Send(stream); } // This is needed, otherwise data can be lost stream.Flush(); // Now handle the result // Now loop, reading the results Object result = null; // our result String errorMessage = ""; Int32 c; Boolean l_endQuery = false; while (!l_endQuery) { c = (Char) stream.ReadByte(); switch (c) { case 'A': // Asynchronous Notify //TODO: do something with this Int32 pid = PGUtil.ReadInt32(stream); String msg = PGUtil.ReadString(stream); break; //------------------------------ // Error message returned case 'E': NpgsqlError e = new NpgsqlError(conn.BackendProtocolVersion, stream); errorMessage += e.Message; break; //------------------------------ // Notice from backend case 'N': NpgsqlError notice = new NpgsqlError(conn.BackendProtocolVersion, stream); errorMessage += notice.Message; break; case 'V': Char l_nextChar = (Char) stream.ReadByte(); if (l_nextChar == 'G') { Int32 sz = PGUtil.ReadInt32(stream); // Return an Integer if if (resulttype) { result = PGUtil.ReadInt32(stream); } else { Byte[] buf = new Byte[sz]; Int32 bytes_from_stream = 0; Int32 total_bytes_read = 0; Int32 size = sz; do { bytes_from_stream = stream.Read(buf, total_bytes_read, size); total_bytes_read += bytes_from_stream; size -= bytes_from_stream; } while (size > 0); result = buf; } //There should be a trailing '0' Int32 l_endChar = (Char) stream.ReadByte(); } else { //it must have been a '0', thus no results } break; case 'Z': l_endQuery = true; break; default: throw new NpgsqlException(string.Format("postgresql.fp.protocol {0}", c)); } } if (errorMessage != null) { throw new NpgsqlException("postgresql.fp.error" + errorMessage); } return result; } }
protected IEnumerable <IServerResponseObject> ProcessBackendResponses_Ver_2(NpgsqlConnector context) { NpgsqlEventLog.LogMethodEnter(LogLevel.Debug, CLASSNAME, "ProcessBackendResponses"); using (new ContextResetter(context)) { Stream stream = context.Stream; NpgsqlMediator mediator = context.Mediator; List <NpgsqlError> errors = new List <NpgsqlError>(); for (;;) { // Check the first Byte of response. switch ((BackEndMessageCode)stream.ReadByte()) { case BackEndMessageCode.ErrorResponse: { NpgsqlError error = new NpgsqlError(context.BackendProtocolVersion, stream); error.ErrorSql = mediator.GetSqlSent(); errors.Add(error); NpgsqlEventLog.LogMsg(resman, "Log_ErrorResponse", LogLevel.Debug, error.Message); } // Return imediately if it is in the startup state or connected state as // there is no more messages to consume. // Possible error in the NpgsqlStartupState: // Invalid password. // Possible error in the NpgsqlConnectedState: // No pg_hba.conf configured. if (!context.RequireReadyForQuery) { throw new NpgsqlException(errors); } break; case BackEndMessageCode.AuthenticationRequest: NpgsqlEventLog.LogMsg(resman, "Log_ProtocolMessage", LogLevel.Debug, "AuthenticationRequest"); AuthenticationRequestType authType = (AuthenticationRequestType)PGUtil.ReadInt32(stream); switch (authType) { case AuthenticationRequestType.AuthenticationOk: NpgsqlEventLog.LogMsg(resman, "Log_AuthenticationOK", LogLevel.Debug); break; case AuthenticationRequestType.AuthenticationClearTextPassword: NpgsqlEventLog.LogMsg(resman, "Log_AuthenticationClearTextRequest", LogLevel.Debug); // Send the PasswordPacket. ChangeState(context, NpgsqlStartupState.Instance); context.Authenticate(context.Password); context.Stream.Flush(); break; case AuthenticationRequestType.AuthenticationMD5Password: NpgsqlEventLog.LogMsg(resman, "Log_AuthenticationMD5Request", LogLevel.Debug); // Now do the "MD5-Thing" // for this the Password has to be: // 1. md5-hashed with the username as salt // 2. md5-hashed again with the salt we get from the backend MD5 md5 = MD5.Create(); // 1. byte[] passwd = context.Password; byte[] saltUserName = BackendEncoding.UTF8Encoding.GetBytes(context.UserName); byte[] crypt_buf = new byte[passwd.Length + saltUserName.Length]; passwd.CopyTo(crypt_buf, 0); saltUserName.CopyTo(crypt_buf, passwd.Length); StringBuilder sb = new StringBuilder(); byte[] hashResult = md5.ComputeHash(crypt_buf); foreach (byte b in hashResult) { sb.Append(b.ToString("x2")); } String prehash = sb.ToString(); byte[] prehashbytes = BackendEncoding.UTF8Encoding.GetBytes(prehash); byte[] saltServer = new byte[4]; stream.Read(saltServer, 0, 4); // Send the PasswordPacket. ChangeState(context, NpgsqlStartupState.Instance); // 2. crypt_buf = new byte[prehashbytes.Length + saltServer.Length]; prehashbytes.CopyTo(crypt_buf, 0); saltServer.CopyTo(crypt_buf, prehashbytes.Length); sb = new StringBuilder("md5"); // This is needed as the backend expects md5 result starts with "md5" hashResult = md5.ComputeHash(crypt_buf); foreach (byte b in hashResult) { sb.Append(b.ToString("x2")); } context.Authenticate(BackendEncoding.UTF8Encoding.GetBytes(sb.ToString())); context.Stream.Flush(); break; default: // Only AuthenticationClearTextPassword and AuthenticationMD5Password supported for now. errors.Add( new NpgsqlError(context.BackendProtocolVersion, String.Format(resman.GetString("Exception_AuthenticationMethodNotSupported"), authType))); throw new NpgsqlException(errors); } break; case BackEndMessageCode.RowDescription: yield return(new NpgsqlRowDescriptionV2(stream, context.OidToNameMapping, context.CompatVersion)); break; case BackEndMessageCode.DataRow: yield return(new StringRowReaderV2(stream)); break; case BackEndMessageCode.BinaryRow: throw new NotSupportedException(); case BackEndMessageCode.ReadyForQuery: ChangeState(context, NpgsqlReadyState.Instance); if (errors.Count != 0) { throw new NpgsqlException(errors); } yield break; case BackEndMessageCode.BackendKeyData: context.BackEndKeyData = new NpgsqlBackEndKeyData(context.BackendProtocolVersion, stream); break; case BackEndMessageCode.NoticeResponse: context.FireNotice(new NpgsqlError(context.BackendProtocolVersion, stream)); break; case BackEndMessageCode.CompletedResponse: yield return(new CompletedResponse(stream)); break; case BackEndMessageCode.CursorResponse: // This is the cursor response message. // It is followed by a C NULL terminated string with the name of // the cursor in a FETCH case or 'blank' otherwise. // In this case it should be always 'blank'. // [FIXME] Get another name for this function. NpgsqlEventLog.LogMsg(resman, "Log_ProtocolMessage", LogLevel.Debug, "CursorResponse"); String cursorName = PGUtil.ReadString(stream); // Continue waiting for ReadyForQuery message. break; case BackEndMessageCode.EmptyQueryResponse: NpgsqlEventLog.LogMsg(resman, "Log_ProtocolMessage", LogLevel.Debug, "EmptyQueryResponse"); PGUtil.ReadString(stream); break; case BackEndMessageCode.NotificationResponse: context.FireNotification(new NpgsqlNotificationEventArgs(stream, false)); if (context.IsNotificationThreadRunning) { yield break; } break; case BackEndMessageCode.IO_ERROR: // Connection broken. Mono returns -1 instead of throw an exception as ms.net does. throw new IOException(); default: // This could mean a number of things // We've gotten out of sync with the backend? // We need to implement this type? // Backend has gone insane? throw new DataException("Backend sent unrecognized response type"); } } } }
/*/// <value>Counts the numbers of Connections that share * /// this Connector. Used in Release() to decide wether this * /// connector is to be moved to the PooledConnectors list.</value> * // internal int mShareCount;*/ /// <summary> /// Opens the physical connection to the server. /// </summary> /// <remarks>Usually called by the RequestConnector /// Method of the connection pool manager.</remarks> internal void Open() { ServerVersion = null; // If Connection.ConnectionString specifies a protocol version, we will // not try to fall back to version 2 on failure. _backendProtocolVersion = (settings.Protocol == ProtocolVersion.Unknown) ? ProtocolVersion.Version3 : settings.Protocol; // Reset state to initialize new connector in pool. CurrentState = NpgsqlClosedState.Instance; // Get a raw connection, possibly SSL... CurrentState.Open(this); try { // Establish protocol communication and handle authentication... CurrentState.Startup(this); } catch (NpgsqlException ne) { // Check for protocol not supported. If we have been told what protocol to use, // we will not try this step. if (settings.Protocol != ProtocolVersion.Unknown) { throw; } // If we attempted protocol version 3, it may be possible to drop back to version 2. if (BackendProtocolVersion != ProtocolVersion.Version3) { throw; } NpgsqlError Error0 = (NpgsqlError)ne.Errors[0]; // If NpgsqlError..ctor() encounters a version 2 error, // it will set its own protocol version to version 2. That way, we can tell // easily if the error was a FATAL: protocol error. if (Error0.BackendProtocolVersion != ProtocolVersion.Version2) { throw; } // Try using the 2.0 protocol. _mediator.ResetResponses(); BackendProtocolVersion = ProtocolVersion.Version2; CurrentState = NpgsqlClosedState.Instance; // Get a raw connection, possibly SSL... CurrentState.Open(this); // Establish protocol communication and handle authentication... CurrentState.Startup(this); } // Change the state of connection to open and ready. _connection_state = ConnectionState.Open; CurrentState = NpgsqlReadyState.Instance; // Fall back to the old way, SELECT VERSION(). // This should not happen for protocol version 3+. if (ServerVersion == null) { NpgsqlCommand command = new NpgsqlCommand("set DATESTYLE TO ISO;select version();", this); ServerVersion = new Version(PGUtil.ExtractServerVersion((string)command.ExecuteScalar())); } // Adjust client encoding. NpgsqlParameterStatus clientEncodingParam = null; if ( !ServerParameters.TryGetValue("client_encoding", out clientEncodingParam) || (!string.Equals(clientEncodingParam.ParameterValue, "UTF8", StringComparison.OrdinalIgnoreCase) && !string.Equals(clientEncodingParam.ParameterValue, "UNICODE", StringComparison.OrdinalIgnoreCase)) ) { new NpgsqlCommand("SET CLIENT_ENCODING TO UTF8", this).ExecuteBlind(); } if (!string.IsNullOrEmpty(settings.SearchPath)) { /*NpgsqlParameter p = new NpgsqlParameter("p", DbType.String); * p.Value = settings.SearchPath; * NpgsqlCommand commandSearchPath = new NpgsqlCommand("SET SEARCH_PATH TO :p,public", this); * commandSearchPath.Parameters.Add(p); * commandSearchPath.ExecuteNonQuery();*/ /*NpgsqlParameter p = new NpgsqlParameter("p", DbType.String); * p.Value = settings.SearchPath; * NpgsqlCommand commandSearchPath = new NpgsqlCommand("SET SEARCH_PATH TO :p,public", this); * commandSearchPath.Parameters.Add(p); * commandSearchPath.ExecuteNonQuery();*/ // TODO: Add proper message when finding a semicolon in search_path. // This semicolon could lead to a sql injection security hole as someone could write in connection string: // searchpath=public;delete from table; and it would be executed. if (settings.SearchPath.Contains(";")) { throw new InvalidOperationException(); } // This is using string concatenation because set search_path doesn't allow type casting. ::text NpgsqlCommand commandSearchPath = new NpgsqlCommand("SET SEARCH_PATH=" + settings.SearchPath, this); commandSearchPath.ExecuteBlind(); } /* * Try to set SSL negotiation to 0. As of 2010-03-29, recent problems in SSL library implementations made * postgresql to add a parameter to set a value when to do this renegotiation or 0 to disable it. * Currently, Npgsql has a problem with renegotiation so, we are trying to disable it here. * This only works on postgresql servers where the ssl renegotiation settings is supported of course. * See http://lists.pgfoundry.org/pipermail/npgsql-devel/2010-February/001065.html for more information. */ try { NpgsqlCommand commandSslrenegotiation = new NpgsqlCommand("SET ssl_renegotiation_limit=0", this); commandSslrenegotiation.ExecuteBlind(); } catch {} // Make a shallow copy of the type mapping that the connector will own. // It is possible that the connector may add types to its private // mapping that will not be valid to another connector, even // if connected to the same backend version. _oidToNameMapping = NpgsqlTypesHelper.CreateAndLoadInitialTypesMapping(this).Clone(); ProcessServerVersion(); // The connector is now fully initialized. Beyond this point, it is // safe to release it back to the pool rather than closing it. IsInitialized = true; }
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"); } } } }
/// <summary> /// Opens the physical connection to the server. /// </summary> /// <remarks>Usually called by the RequestConnector /// Method of the connection pool manager.</remarks> internal void Open() { ServerVersion = null; // If Connection.ConnectionString specifies a protocol version, we will // not try to fall back to version 2 on failure. _backendProtocolVersion = (settings.Protocol == ProtocolVersion.Unknown) ? ProtocolVersion.Version3 : settings.Protocol; // Reset state to initialize new connector in pool. CurrentState = NpgsqlClosedState.Instance; // Get a raw connection, possibly SSL... CurrentState.Open(this); try { // Establish protocol communication and handle authentication... CurrentState.Startup(this); } catch (NpgsqlException ne) { // Check for protocol not supported. If we have been told what protocol to use, // we will not try this step. if (settings.Protocol != ProtocolVersion.Unknown) { throw; } // If we attempted protocol version 3, it may be possible to drop back to version 2. if (BackendProtocolVersion != ProtocolVersion.Version3) { throw; } NpgsqlError Error0 = (NpgsqlError)ne.Errors[0]; // If NpgsqlError..ctor() encounters a version 2 error, // it will set its own protocol version to version 2. That way, we can tell // easily if the error was a FATAL: protocol error. if (Error0.BackendProtocolVersion != ProtocolVersion.Version2) { throw; } // Try using the 2.0 protocol. _mediator.ResetResponses(); BackendProtocolVersion = ProtocolVersion.Version2; CurrentState = NpgsqlClosedState.Instance; // Get a raw connection, possibly SSL... CurrentState.Open(this); // Establish protocol communication and handle authentication... CurrentState.Startup(this); } // Change the state of connection to open and ready. State = ConnectionState.Open; CurrentState = NpgsqlReadyState.Instance; // Fall back to the old way, SELECT VERSION(). // This should not happen for protocol version 3+. if (ServerVersion == null) { using (NpgsqlCommand command = new NpgsqlCommand("set DATESTYLE TO ISO;select version();", this)) { ServerVersion = new Version(PGUtil.ExtractServerVersion((string)command.ExecuteScalar())); } } StringBuilder sbInit = new StringBuilder(); // Adjust client encoding. NpgsqlParameterStatus clientEncodingParam = null; if (!ServerParameters.TryGetValue("client_encoding", out clientEncodingParam) || !string.Equals(clientEncodingParam.ParameterValue, "UTF8", StringComparison.OrdinalIgnoreCase) && !string.Equals(clientEncodingParam.ParameterValue, "UNICODE", StringComparison.OrdinalIgnoreCase)) { sbInit.AppendLine("SET CLIENT_ENCODING TO UTF8;"); } if (!string.IsNullOrEmpty(settings.SearchPath)) { // TODO: Add proper message when finding a semicolon in search_path. // This semicolon could lead to a sql injection security hole as someone could write in connection string: // searchpath=public;delete from table; and it would be executed. if (settings.SearchPath.Contains(";")) { throw new InvalidOperationException(); } sbInit.AppendLine("SET SEARCH_PATH=" + settings.SearchPath + ";"); } if (!string.IsNullOrEmpty(settings.ApplicationName)) { if (!SupportsApplicationName) { //TODO //throw new InvalidOperationException(resman.GetString("Exception_ApplicationNameNotSupported")); throw new InvalidOperationException("ApplicationName not supported."); } if (settings.ApplicationName.Contains(";")) { throw new InvalidOperationException(); } sbInit.AppendLine("SET APPLICATION_NAME='" + settings.ApplicationName.Replace('\'', '-') + "';"); } /* * Try to set SSL negotiation to 0. As of 2010-03-29, recent problems in SSL library implementations made * postgresql to add a parameter to set a value when to do this renegotiation or 0 to disable it. * Currently, Npgsql has a problem with renegotiation so, we are trying to disable it here. * This only works on postgresql servers where the ssl renegotiation settings is supported of course. * See http://lists.pgfoundry.org/pipermail/npgsql-devel/2010-February/001065.html for more information. */ sbInit.AppendLine("SET ssl_renegotiation_limit=0;"); /* * Set precision digits to maximum value possible. For postgresql before 9 it was 2, after that, it is 3. * This way, we set first to 2 and then to 3. If there is an error because of 3, it will have been set to 2 at least. * Check bug report #1010992 for more information. */ sbInit.AppendLine("SET extra_float_digits=3;"); try { new NpgsqlCommand(sbInit.ToString(), this).ExecuteBlind(); } catch { foreach (var line in sbInit.ToString().Split(Environment.NewLine.ToCharArray())) { try { if (line.Length > 0) { new NpgsqlCommand(line, this).ExecuteBlind(); } } catch { } } } // Make a shallow copy of the type mapping that the connector will own. // It is possible that the connector may add types to its private // mapping that will not be valid to another connector, even // if connected to the same backend version. _oidToNameMapping = NpgsqlTypesHelper.CreateAndLoadInitialTypesMapping(this).Clone(); ProcessServerVersion(); // The connector is now fully initialized. Beyond this point, it is // safe to release it back to the pool rather than closing it. IsInitialized = true; }
/// <value>Counts the numbers of Connections that share /// this Connector. Used in Release() to decide wether this /// connector is to be moved to the PooledConnectors list.</value> // internal int mShareCount; /// <summary> /// Opens the physical connection to the server. /// </summary> /// <remarks>Usually called by the RequestConnector /// Method of the connection pool manager.</remarks> internal void Open() { ProtocolVersion PV; // If Connection.ConnectionString specifies a protocol version, we will // not try to fall back to version 2 on failure. if (ConnectionString.Contains(ConnectionStringKeys.Protocol)) { PV = ConnectionString.ToProtocolVersion(ConnectionStringKeys.Protocol); } else { PV = ProtocolVersion.Unknown; } _backendProtocolVersion = (PV == ProtocolVersion.Unknown) ? ProtocolVersion.Version3 : PV; // Reset state to initialize new connector in pool. Encoding = Encoding.Default; CurrentState = NpgsqlClosedState.Instance; // Get a raw connection, possibly SSL... CurrentState.Open(this); // Establish protocol communication and handle authentication... CurrentState.Startup(this); // Check for protocol not supported. If we have been told what protocol to use, // we will not try this step. if (_mediator.Errors.Count > 0 && PV == ProtocolVersion.Unknown) { // If we attempted protocol version 3, it may be possible to drop back to version 2. if (BackendProtocolVersion == ProtocolVersion.Version3) { NpgsqlError Error0 = (NpgsqlError)_mediator.Errors[0]; // If NpgsqlError.ReadFromStream_Ver_3() encounters a version 2 error, // it will set its own protocol version to version 2. That way, we can tell // easily if the error was a FATAL: protocol error. if (Error0.BackendProtocolVersion == ProtocolVersion.Version2) { // Try using the 2.0 protocol. _mediator.ResetResponses(); BackendProtocolVersion = ProtocolVersion.Version2; CurrentState = NpgsqlClosedState.Instance; // Get a raw connection, possibly SSL... CurrentState.Open(this); // Establish protocol communication and handle authentication... CurrentState.Startup(this); } } } // Check for errors and do the Right Thing. // FIXME - CheckErrors needs to be moved to Connector CheckErrors(); _backend_keydata = _mediator.BackendKeyData; // Change the state of connection to open and ready. _connection_state = ConnectionState.Open; CurrentState = NpgsqlReadyState.Instance; String ServerVersionString = String.Empty; // First try to determine backend server version using the newest method. if (((NpgsqlParameterStatus)_mediator.Parameters["__npgsql_server_version"]) != null) { ServerVersionString = ((NpgsqlParameterStatus)_mediator.Parameters["__npgsql_server_version"]).ParameterValue; } // Fall back to the old way, SELECT VERSION(). // This should not happen for protocol version 3+. if (ServerVersionString.Length == 0) { NpgsqlCommand command = new NpgsqlCommand("select version();set DATESTYLE TO ISO;", this); ServerVersionString = PGUtil.ExtractServerVersion((String)command.ExecuteScalar()); } // Cook version string so we can use it for enabling/disabling things based on // backend version. ServerVersion = PGUtil.ParseServerVersion(ServerVersionString); // Adjust client encoding. //NpgsqlCommand commandEncoding1 = new NpgsqlCommand("show client_encoding", _connector); //String clientEncoding1 = (String)commandEncoding1.ExecuteScalar(); if (ConnectionString.ToString(ConnectionStringKeys.Encoding, ConnectionStringDefaults.Encoding).ToUpper() == "UNICODE") { Encoding = Encoding.UTF8; NpgsqlCommand commandEncoding = new NpgsqlCommand("SET CLIENT_ENCODING TO UNICODE", this); commandEncoding.ExecuteNonQuery(); } // Make a shallow copy of the type mapping that the connector will own. // It is possible that the connector may add types to its private // mapping that will not be valid to another connector, even // if connected to the same backend version. _oidToNameMapping = NpgsqlTypesHelper.CreateAndLoadInitialTypesMapping(this).Clone(); ProcessServerVersion(); // The connector is now fully initialized. Beyond this point, it is // safe to release it back to the pool rather than closing it. IsInitialized = true; }
BackendMessage DoReadSingleMessage(DataRowLoadingMode dataRowLoadingMode = DataRowLoadingMode.NonSequential, bool ignoreNotifications = true) { NpgsqlError error = null; while (true) { var buf = Buffer; Buffer.Ensure(5); var messageCode = (BackendMessageCode) Buffer.ReadByte(); Contract.Assume(Enum.IsDefined(typeof(BackendMessageCode), messageCode), "Unknown message code: " + messageCode); var len = Buffer.ReadInt32() - 4; // Transmitted length includes itself if (messageCode == BackendMessageCode.DataRow && dataRowLoadingMode != DataRowLoadingMode.NonSequential) { if (dataRowLoadingMode == DataRowLoadingMode.Skip) { Buffer.Skip(len); continue; } } else if (len > Buffer.ReadBytesLeft) { buf = buf.EnsureOrAllocateTemp(len); } var msg = ParseServerMessage(buf, messageCode, len, dataRowLoadingMode); if (msg != null || !ignoreNotifications && (messageCode == BackendMessageCode.NoticeResponse || messageCode == BackendMessageCode.NotificationResponse)) { if (error != null) { Contract.Assert(messageCode == BackendMessageCode.ReadyForQuery, "Expected ReadyForQuery after ErrorResponse"); throw new NpgsqlException(error); } return msg; } else if (messageCode == BackendMessageCode.ErrorResponse) { // An ErrorResponse is (almost) always followed by a ReadyForQuery. Save the error // and throw it as an exception when the ReadyForQuery is received (next) // The exception is during the startup/authentication phase, where the server closes // the connection after an ErrorResponse error = new NpgsqlError(buf); if (State == ConnectorState.Connecting) { throw new NpgsqlException(error); } } } }
private Object FastpathV3(Int32 fnid, Boolean resulttype, FastpathArg[] args) { // give thread safety lock (stream) { // send the function call try { Int32 l_msgLen = 0; l_msgLen += 16; for (Int32 i=0;i < args.Length;i++) l_msgLen += args[i].SendSize(); stream.WriteByte((Byte)'F'); PGUtil.WriteInt32(stream,l_msgLen); PGUtil.WriteInt32(stream,fnid); PGUtil.WriteInt16(stream,1); PGUtil.WriteInt16(stream,1); PGUtil.WriteInt16(stream,(short)args.Length); for (Int32 i = 0;i < args.Length;i++) args[i].Send(stream); PGUtil.WriteInt16(stream,1); // This is needed, otherwise data can be lost stream.Flush(); } catch (Exception ex) { throw new Exception(ex.ToString()); } // Now handle the result // Now loop, reading the results Object result = null; // our result Exception error = null; Int32 c; Boolean l_endQuery = false; Byte[] input_buffer = new Byte[512]; while (!l_endQuery) { c = (Char)stream.ReadByte(); switch (c) { case 'A': // Asynchronous Notify Int32 msglen = PGUtil.ReadInt32(stream,input_buffer); Int32 pid = PGUtil.ReadInt32(stream,input_buffer); String msg = PGUtil.ReadString(stream,conn.Connector.Encoding); PGUtil.ReadString(stream,conn.Connector.Encoding); String param = PGUtil.ReadString(stream,conn.Connector.Encoding); conn.Connector.CheckErrorsAndNotifications(); break; //------------------------------ // Error message returned case 'E': NpgsqlError e = new NpgsqlError(conn.BackendProtocolVersion); e.ReadFromStream(stream,conn.Connector.Encoding); throw new Exception(e.ToString()); //------------------------------ // Notice from backend case 'N': Int32 l_nlen = PGUtil.ReadInt32(stream,input_buffer); NpgsqlError e1 = new NpgsqlError(conn.BackendProtocolVersion); e1.ReadFromStream(stream,conn.Connector.Encoding); conn.Connector.Mediator.Errors.Add(e1); break; case 'V': Int32 l_msgLen = PGUtil.ReadInt32(stream,input_buffer); Int32 l_valueLen = PGUtil.ReadInt32(stream,input_buffer); if (l_valueLen == -1) { //null value } else if (l_valueLen == 0) { result = new Byte[0]; } else { // Return an Integer if if (resulttype) result = PGUtil.ReadInt32(stream,input_buffer); else { Byte[] buf = new Byte[l_valueLen]; Int32 bytes_from_stream = 0; Int32 total_bytes_read = 0; Int32 size = l_valueLen; do { bytes_from_stream = stream.Read(buf, total_bytes_read, size); total_bytes_read += bytes_from_stream; size -= bytes_from_stream; } while(size > 0); result = buf; } } break; case 'Z': //TODO: use size better if (PGUtil.ReadInt32(stream,input_buffer) != 5) throw new Exception("Received Z" ); //TODO: handle transaction status Char l_tStatus = (Char)stream.ReadByte(); l_endQuery = true; break; default: throw new Exception("postgresql.fp.protocol received " + c.ToString()); } } if ( error != null ) throw error; return result; } }