public override void WriteToStream(Stream outputStream) { outputStream.WriteByte((byte)FrontEndMessageCode.Parse); // message length = // Int32 self // name of prepared statement + 1 null string terminator + // query string + 1 null string terminator // + Int16 // + Int32 * number of parameters. Int32 messageLength = 4 + UTF8Encoding.GetByteCount(_prepareName) + 1 + UTF8Encoding.GetByteCount(_queryString) + 1 + 2 + (_parameterIDs.Length * 4); //Int32 messageLength = 4 + _prepareName.Length + 1 + _queryString.Length + 1 + 2 + (_parameterIDs.Length * 4); PGUtil.WriteInt32(outputStream, messageLength); PGUtil.WriteString(_prepareName, outputStream); PGUtil.WriteString(_queryString, outputStream); PGUtil.WriteInt16(outputStream, (Int16)_parameterIDs.Length); for (Int32 i = 0; i < _parameterIDs.Length; i++) { PGUtil.WriteInt32(outputStream, _parameterIDs[i]); } }
private void AbandonShip() { //field size will always be smaller than message size //but if we fall out of sync with the stream due to an error then we will probably hit //such a situation soon as bytes from elsewhere in the stream get interpreted as a size. //so if we see this happens, we know we've lost the stream - our best option is to just give up on it, //and have the connector recovered later. try { Stream.WriteByte((byte)FrontEndMessageCode.Termination); PGUtil.WriteInt32(Stream, 4); Stream.Flush(); } catch { } try { Stream.Close(); } catch { } throw new DataException(); }
public override void WriteToStream(Stream outputStream) { outputStream.WriteByte((Byte)'p'); PGUtil.WriteInt32(outputStream, 4 + password.Length + 1); // Write String. PGUtil.WriteBytes(password, outputStream); }
/// <summary> /// Sends given packet to server as a CopyData message. /// Does not check for notifications! Use another thread for that. /// </summary> public override void SendCopyData(NpgsqlConnector context, byte[] buf, int off, int len) { Stream toServer = context.Stream; toServer.WriteByte((byte)FrontEndMessageCode.CopyData); PGUtil.WriteInt32(toServer, len + 4); toServer.Write(buf, off, len); }
public override void WriteToStream(Stream outputStream) { outputStream.WriteByte((byte)FrontEndMessageCode.Execute); PGUtil.WriteInt32(outputStream, 4 + UTF8Encoding.GetByteCount(_portalName) + 1 + 4); PGUtil.WriteString(_portalName, outputStream); PGUtil.WriteInt32(outputStream, _maxRows); }
public override void WriteToStream(Stream outputStream) { PGUtil.WriteInt32(outputStream, CancelRequestMessageSize); PGUtil.WriteInt32(outputStream, CancelRequestCode); PGUtil.WriteInt32(outputStream, BackendKeydata.ProcessID); PGUtil.WriteInt32(outputStream, BackendKeydata.SecretKey); outputStream.Flush(); }
public static void Send(char what, string portal, Stream outputStream) { outputStream.WriteByte((byte)FrontEndMessageCode.Describe); PGUtil.WriteInt32(outputStream, 4 + 1 + UTF8Encoding.GetByteCount(portal) + 1); outputStream.WriteByte((Byte)what); PGUtil.WriteString(portal, outputStream); }
/// <summary> /// Sends CopyDone message to server. Handles responses, ie. may throw an exception. /// </summary> public override void SendCopyDone(NpgsqlConnector context) { Stream toServer = context.Stream; toServer.WriteByte((byte)FrontEndMessageCode.CopyDone); PGUtil.WriteInt32(toServer, 4); // message without data toServer.Flush(); ProcessBackendResponses(context); }
/// <summary> /// Sends CopyFail message to server. Handles responses, ie. should always throw an exception: /// in CopyIn state the server responds to CopyFail with an error response; /// outside of a CopyIn state the server responds to CopyFail with an error response; /// without network connection or whatever, there's going to eventually be a failure, timeout or user intervention. /// </summary> public override void SendCopyFail(NpgsqlConnector context, String message) { Stream toServer = context.Stream; toServer.WriteByte((byte)FrontEndMessageCode.CopyFail); byte[] buf = ENCODING_UTF8.GetBytes((message ?? string.Empty) + '\x00'); PGUtil.WriteInt32(toServer, 4 + buf.Length); toServer.Write(buf, 0, buf.Length); toServer.Flush(); ProcessBackendResponses(context); }
public static void Send(NpgsqlCommand command, Stream stream) { var commandStream = command.GetCommandStream(); commandStream.Position = 0; // Log the string being sent. //if (NpgsqlEventLog.Level >= LogLevel.Debug) //PGUtil.LogStringWritten(commandText.ToString()); // This method needs refactory. // The code below which deals with writing string to stream needs to be redone to use // PGUtil.WriteString() as before. The problem is that WriteString is using too much strings (concatenation). // Find a way to optimize that. // Tell to mediator what command is being sent. //TODO command.Connector.Mediator.SqlSent = command.CommandText; // Workaround for seek exceptions when running under ms.net. TODO: Check why Npgsql may be letting behind data in the stream. stream.Flush(); // Send the query to server. // Write the byte 'Q' to identify a query message. stream.WriteByte((byte)FrontEndMessageCode.Query); //Work out the encoding of the string (null-terminated) once and take the length from having done so //rather than doing so repeatedly. // Write message length. Int32 + string length + null terminator. PGUtil.WriteInt32(stream, 4 + (int)commandStream.Length + 1); var cms = commandStream as Revenj.Utility.ChunkedMemoryStream; if (cms != null) { cms.CopyTo(stream); stream.WriteByte(0); } else { commandStream.CopyTo(stream); stream.WriteByte(0); } Thread.Yield(); }
public override void Close(NpgsqlConnector context) { Stream stream = context.Stream; try { stream.WriteByte((byte)FrontEndMessageCode.Termination); PGUtil.WriteInt32(stream, 4); stream.Flush(); } catch { //Error writting termination message to stream, nothing we can do. } try { stream.Close(); } catch { } context.Stream = null; ChangeState(context, NpgsqlClosedState.Instance); }
public override void WriteToStream(Stream output_stream) { PGUtil.WriteInt32(output_stream, 4 + 4 + 5 + (UTF8Encoding.GetByteCount(user_name) + 1) + 9 + (UTF8Encoding.GetByteCount(database_name) + 1) + 10 + 4 + 1); PGUtil.WriteInt32(output_stream, 196608); // User name. PGUtil.WriteString("user", output_stream); // User name. PGUtil.WriteString(user_name, output_stream); // Database name. PGUtil.WriteString("database", output_stream); // Database name. PGUtil.WriteString(database_name, output_stream); // DateStyle. PGUtil.WriteString("DateStyle", output_stream); // DateStyle. PGUtil.WriteString("ISO", output_stream); output_stream.WriteByte(0); output_stream.Flush(); }
public override void Open(NpgsqlConnector context) { try { IPAddress[] ips = ResolveIPHost(context.Host); Socket socket = null; // try every ip address of the given hostname, use the first reachable one foreach (IPAddress ip in ips) { IPEndPoint ep = new IPEndPoint(ip, context.Port); socket = new Socket(ep.AddressFamily, SocketType.Stream, ProtocolType.Tcp); try { IAsyncResult result = socket.BeginConnect(ep, null, null); if (!result.AsyncWaitHandle.WaitOne(context.ConnectionTimeout * 1000, true)) { socket.Close(); throw new Exception("Connection establishment timeout. Increase Timeout value in ConnectionString."); } socket.EndConnect(result); // connect was successful, leave the loop break; } catch (Exception) { socket.Close(); } } if (socket == null || !socket.Connected) { throw new Exception(string.Format("Failed to establish a connection to '{0}'.", context.Host)); } Stream stream = new NpgsqlNetworkStream(context, socket, true); // If the PostgreSQL server has SSL connectors enabled Open SslClientStream if (response == 'S') { if (context.SSL || (context.SslMode == SslMode.Require) || (context.SslMode == SslMode.Prefer)) { PGUtil.WriteInt32(stream, 8); PGUtil.WriteInt32(stream, 80877103); // Receive response Char response = (Char)stream.ReadByte(); if (response == 'S') { //create empty collection X509CertificateCollection clientCertificates = new X509CertificateCollection(); //trigger the callback to fetch some certificates context.DefaultProvideClientCertificatesCallback(clientCertificates); stream = new SslClientStream( stream, context.Host, true, SecurityProtocolType.Default, clientCertificates); ((SslClientStream)stream).ClientCertSelectionDelegate = new CertificateSelectionCallback(context.DefaultCertificateSelectionCallback); ((SslClientStream)stream).ServerCertValidationDelegate = new CertificateValidationCallback(context.DefaultCertificateValidationCallback); ((SslClientStream)stream).PrivateKeyCertSelectionDelegate = new PrivateKeySelectionCallback(context.DefaultPrivateKeySelectionCallback); } else if (context.SslMode == SslMode.Require) { throw new InvalidOperationException("Ssl connection requested. No Ssl enabled connection from this host is configured."); } } context.Stream = new NpgsqlBufferedStream(stream); context.Socket = socket; ChangeState(context, NpgsqlConnectedState.Instance); } //FIXME: Exceptions that come from what we are handling should be wrapped - e.g. an error connecting to //the server should definitely be presented to the uesr as an NpgsqlError. Exceptions from userland should //be passed untouched - e.g. ThreadAbortException because the user started this in a thread they created and //then aborted should be passed through. //Are there any others that should be pass through? Alternatively, are there a finite number that should //be wrapped? catch (ThreadAbortException) { throw; } catch (Exception e) { throw new NpgsqlException(e.Message, e); } }
// Logging related values //private static readonly String CLASSNAME = MethodBase.GetCurrentMethod().DeclaringType.Name; public override void WriteToStream(Stream outputStream) { outputStream.WriteByte((byte)FrontEndMessageCode.Flush); PGUtil.WriteInt32(outputStream, 4); }
public override void WriteToStream(Stream outputStream) { Int32 messageLength = 4 + UTF8Encoding.GetByteCount(_portalName) + 1 + UTF8Encoding.GetByteCount(_preparedStatementName) + 1 + 2 + (_parameterFormatCodes.Length * 2) + 2; // Get size of parameter values. Int32 i; if (_parameterValues != null) { for (i = 0; i < _parameterValues.Length; i++) { messageLength += 4; if (_parameterValues[i] != null) { if (((_parameterFormatCodes.Length == 1) && (_parameterFormatCodes[0] == (Int16)FormatCode.Binary)) || ((_parameterFormatCodes.Length != 1) && (_parameterFormatCodes[i] == (Int16)FormatCode.Binary))) { messageLength += ((Byte[])_parameterValues[i]).Length; } else { messageLength += UTF8Encoding.GetByteCount((String)_parameterValues[i]); } } } } messageLength += 2 + (_resultFormatCodes.Length * 2); outputStream.WriteByte((byte)FrontEndMessageCode.Bind); PGUtil.WriteInt32(outputStream, messageLength); PGUtil.WriteString(_portalName, outputStream); PGUtil.WriteString(_preparedStatementName, outputStream); PGUtil.WriteInt16(outputStream, (Int16)_parameterFormatCodes.Length); for (i = 0; i < _parameterFormatCodes.Length; i++) { PGUtil.WriteInt16(outputStream, _parameterFormatCodes[i]); } if (_parameterValues != null) { PGUtil.WriteInt16(outputStream, (Int16)_parameterValues.Length); for (i = 0; i < _parameterValues.Length; i++) { if (((_parameterFormatCodes.Length == 1) && (_parameterFormatCodes[0] == (Int16)FormatCode.Binary)) || ((_parameterFormatCodes.Length != 1) && (_parameterFormatCodes[i] == (Int16)FormatCode.Binary))) { Byte[] parameterValue = (Byte[])_parameterValues[i]; if (parameterValue == null) { PGUtil.WriteInt32(outputStream, -1); } else { PGUtil.WriteInt32(outputStream, parameterValue.Length); outputStream.Write(parameterValue, 0, parameterValue.Length); } } else { if ((_parameterValues[i] == null)) { PGUtil.WriteInt32(outputStream, -1); } else { Byte[] parameterValueBytes = UTF8Encoding.GetBytes((String)_parameterValues[i]); PGUtil.WriteInt32(outputStream, parameterValueBytes.Length); outputStream.Write(parameterValueBytes, 0, parameterValueBytes.Length); } } } } else { PGUtil.WriteInt16(outputStream, 0); } PGUtil.WriteInt16(outputStream, (Int16)_resultFormatCodes.Length); for (i = 0; i < _resultFormatCodes.Length; i++) { PGUtil.WriteInt16(outputStream, _resultFormatCodes[i]); } }
// Logging related values //private static readonly String CLASSNAME = MethodBase.GetCurrentMethod().DeclaringType.Name; public static void Send(Stream stream) { stream.WriteByte((byte)FrontEndMessageCode.Sync); PGUtil.WriteInt32(stream, 4); }
protected IEnumerable <IServerResponseObject> ProcessBackendResponses_Ver_3(NpgsqlConnector context) { try { Stream stream = context.Stream; NpgsqlMediator mediator = context.Mediator; var buffer = context.TmpBuffer; var queue = context.ArrayBuffer; List <NpgsqlError> errors = null; SCRAM scram = null; for (; ;) { // Check the first Byte of response. BackEndMessageCode message = (BackEndMessageCode)stream.ReadByte(); switch (message) { case BackEndMessageCode.ErrorResponse: NpgsqlError error = new NpgsqlError(stream, buffer, queue); error.ErrorSql = mediator.SqlSent; if (errors == null) { errors = new List <NpgsqlError>(); } errors.Add(error); // 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: // Get the length in case we're getting AuthenticationGSSContinue int authDataLength = PGUtil.ReadInt32(stream, buffer) - 8; AuthenticationRequestType authType = (AuthenticationRequestType)PGUtil.ReadInt32(stream, buffer); switch (authType) { case AuthenticationRequestType.AuthenticationOk: break; case AuthenticationRequestType.AuthenticationClearTextPassword: // Send the PasswordPacket. ChangeState(context, NpgsqlStartupState.Instance); context.Authenticate(context.Password); break; 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 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 case AuthenticationRequestType.AuthenticationSASL: var saslAuthMechanism = PGUtil.ReadString(stream, queue); if (saslAuthMechanism == "SCRAM-SHA-256") { stream.ReadByte(); scram = new SCRAM(saslAuthMechanism, context.UserName); stream.WriteByte((byte)FrontEndMessageCode.SASL); var schemeBytes = Encoding.UTF8.GetBytes(scram.Scheme); var clientFirstMessageBytes = Encoding.UTF8.GetBytes(scram.getClientFirstMessage()); PGUtil.WriteInt32(stream, 9 + schemeBytes.Length + clientFirstMessageBytes.Length); stream.Write(schemeBytes, 0, schemeBytes.Length); stream.WriteByte(0); PGUtil.WriteInt32(stream, clientFirstMessageBytes.Length); stream.Write(clientFirstMessageBytes, 0, clientFirstMessageBytes.Length); stream.Flush(); } else { throw new NpgsqlException("Only Scram SHA 256 is supported"); } break; case AuthenticationRequestType.AuthenticationSASLContinue: if (scram == null) { throw new NpgsqlException("Invalid authentication message"); } var continueData = new byte[authDataLength]; PGUtil.CheckedStreamRead(stream, continueData, 0, authDataLength); scram.parseServerFirstMessage(Encoding.UTF8.GetString(continueData)); scram.Password = Encoding.UTF8.GetString(context.Password); var mesageBytes = Encoding.UTF8.GetBytes(scram.getClientFinalMessage()); stream.WriteByte((byte)FrontEndMessageCode.SASL); PGUtil.WriteInt32(stream, 4 + mesageBytes.Length); stream.Write(mesageBytes, 0, mesageBytes.Length); stream.Flush(); break; case AuthenticationRequestType.AuthenticationSASLFinal: if (scram == null) { throw new NpgsqlException("Invalid authentication message"); } var finalData = new byte[authDataLength]; PGUtil.CheckedStreamRead(stream, finalData, 0, authDataLength); scram.verifyServerSignature(Encoding.UTF8.GetString(finalData)); break; default: // Only AuthenticationClearTextPassword, AuthenticationMD5Password and AuthenticationSASL supported for now. if (errors == null) { errors = new List <NpgsqlError>(); } errors.Add( new NpgsqlError(String.Format(resman.GetString("Exception_AuthenticationMethodNotSupported"), authType))); throw new NpgsqlException(errors); } break; case BackEndMessageCode.RowDescription: yield return(context.RowDescription()); break; case BackEndMessageCode.ParameterDescription: // Do nothing,for instance, just read... int length = PGUtil.ReadInt32(stream, buffer); int nb_param = PGUtil.ReadInt16(stream, buffer); //WTF for (int i = 0; i < nb_param; i++) { int typeoid = PGUtil.ReadInt32(stream, buffer); } break; case BackEndMessageCode.DataRow: yield return(context.NextRow()); break; case BackEndMessageCode.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, buffer); stream.ReadByte(); ChangeState(context, NpgsqlReadyState.Instance); if (errors != null) { throw new NpgsqlException(errors); } yield break; case BackEndMessageCode.BackendKeyData: // BackendKeyData message. NpgsqlBackEndKeyData backend_keydata = new NpgsqlBackEndKeyData(stream, buffer); 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(stream, buffer, queue)); break; case BackEndMessageCode.CompletedResponse: PGUtil.ReadInt32(stream, buffer); yield return(new CompletedResponse(stream, queue)); break; case BackEndMessageCode.ParseComplete: // Just read up the message length. PGUtil.ReadInt32(stream, buffer); yield break; case BackEndMessageCode.BindComplete: // Just read up the message length. PGUtil.ReadInt32(stream, buffer); yield break; case BackEndMessageCode.EmptyQueryResponse: PGUtil.ReadInt32(stream, buffer); break; case BackEndMessageCode.NotificationResponse: // Eat the length PGUtil.ReadInt32(stream, buffer); context.FireNotification(new NpgsqlNotificationEventArgs(stream, true, buffer, queue)); if (context.IsNotificationThreadRunning) { yield break; } break; case BackEndMessageCode.ParameterStatus: NpgsqlParameterStatus parameterStatus = new NpgsqlParameterStatus(stream, queue); 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. PGUtil.ReadInt32(stream, buffer); break; case BackEndMessageCode.CopyInResponse: // Enter COPY sub protocol and start pushing data to server ChangeState(context, NpgsqlCopyInState.Instance); PGUtil.ReadInt32(stream, buffer); // length redundant context.CurrentState.StartCopy(context, ReadCopyHeader(stream, buffer)); 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 ChangeState(context, NpgsqlCopyOutState.Instance); PGUtil.ReadInt32(stream, buffer); // length redundant context.CurrentState.StartCopy(context, ReadCopyHeader(stream, buffer)); 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: Int32 len = PGUtil.ReadInt32(stream, buffer) - 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: PGUtil.ReadInt32(stream, buffer); // 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)); } } } finally { context.RequireReadyForQuery = true; } }