internal sealed override bool Write(WriteBuffer buf) { Contract.Assume(Length < buf.UsableSize, $"Message of type {GetType().Name} has length {Length} which is bigger than the buffer ({buf.UsableSize})"); if (buf.WriteSpaceLeft < Length) return false; WriteFully(buf); return true; }
internal void WriteCancelRequest(int backendProcessId, int backendSecretKey) { const int len = sizeof(int) + // Length sizeof(int) + // Cancel request code sizeof(int) + // Backend process id sizeof(int); // Backend secret key Debug.Assert(backendProcessId != 0); if (WriteBuffer.WriteSpaceLeft < len) { Flush(false).GetAwaiter().GetResult(); } WriteBuffer.WriteInt32(len); WriteBuffer.WriteInt32(1234 << 16 | 5678); WriteBuffer.WriteInt32(backendProcessId); WriteBuffer.WriteInt32(backendSecretKey); }
internal async Task WriteParse(string sql, string statementName, List <NpgsqlParameter> inputParameters, bool async) { Debug.Assert(statementName.All(c => c < 128)); var queryByteLen = TextEncoding.GetByteCount(sql); if (WriteBuffer.WriteSpaceLeft < 1 + 4 + statementName.Length + 1) { await Flush(async); } var messageLength = sizeof(byte) + // Message code sizeof(int) + // Length statementName.Length + // Statement name sizeof(byte) + // Null terminator for the statement name queryByteLen + sizeof(byte) + // SQL query length plus null terminator sizeof(ushort) + // Number of parameters inputParameters.Count * sizeof(int); // Parameter OIDs WriteBuffer.WriteByte(FrontendMessageCode.Parse); WriteBuffer.WriteInt32(messageLength - 1); WriteBuffer.WriteNullTerminatedString(statementName); await WriteBuffer.WriteString(sql, queryByteLen, async); if (WriteBuffer.WriteSpaceLeft < 1 + 2) { await Flush(async); } WriteBuffer.WriteByte(0); // Null terminator for the query WriteBuffer.WriteUInt16((ushort)inputParameters.Count); foreach (var p in inputParameters) { if (WriteBuffer.WriteSpaceLeft < 4) { await Flush(async); } WriteBuffer.WriteInt32((int)p.Handler !.PostgresType.OID); } }
internal async Task WritePassword(byte[] payload, int offset, int count, bool async, CancellationToken cancellationToken = default) { if (WriteBuffer.WriteSpaceLeft < sizeof(byte) + sizeof(int)) { await WriteBuffer.Flush(async, cancellationToken); } WriteBuffer.WriteByte(FrontendMessageCode.Password); WriteBuffer.WriteInt32(sizeof(int) + count); if (count <= WriteBuffer.WriteSpaceLeft) { // The entire array fits in our WriteBuffer, copy it into the WriteBuffer as usual. WriteBuffer.WriteBytes(payload, offset, count); return; } await WriteBuffer.Flush(async, cancellationToken); await WriteBuffer.DirectWrite(new ReadOnlyMemory <byte>(payload, offset, count), async, cancellationToken); }
internal async Task WritePassword(byte[] payload, int offset, int count, bool async) { if (WriteBuffer.WriteSpaceLeft < sizeof(byte) + sizeof(int)) { await WriteBuffer.Flush(async); } WriteBuffer.WriteByte(FrontendMessageCode.Password); WriteBuffer.WriteInt32(sizeof(int) + count); if (count <= WriteBuffer.WriteSpaceLeft) { // The entire array fits in our WriteBuffer, copy it into the WriteBuffer as usual. WriteBuffer.WriteBytes(payload, offset, count); return; } await WriteBuffer.Flush(async); await WriteBuffer.DirectWrite(payload, offset, count, async); }
internal sealed override async Task WriteWithLength(object value, WriteBuffer buf, LengthCache lengthCache, NpgsqlParameter parameter, bool async, CancellationToken cancellationToken) { if (value == null || value is DBNull) { if (buf.WriteSpaceLeft < 4) { await buf.Flush(async, cancellationToken); } buf.WriteInt32(-1); return; } var elementLen = ValidateAndGetLength(value, parameter); if (buf.WriteSpaceLeft < 4 + elementLen) { await buf.Flush(async, cancellationToken); } buf.WriteInt32(elementLen); Write(value, buf, parameter); }
internal NpgsqlRawCopyStream(NpgsqlConnector connector, string copyCommand) { _connector = connector; _readBuf = connector.ReadBuffer; _writeBuf = connector.WriteBuffer; _connector.SendQuery(copyCommand); var msg = _connector.ReadMessage(DataRowLoadingMode.NonSequential); switch (msg.Code) { case BackendMessageCode.CopyInResponse: var copyInResponse = (CopyInResponseMessage) msg; IsBinary = copyInResponse.IsBinary; _canWrite = true; break; case BackendMessageCode.CopyOutResponse: var copyOutResponse = (CopyOutResponseMessage) msg; IsBinary = copyOutResponse.IsBinary; _canRead = true; break; default: throw _connector.UnexpectedMessageReceived(msg.Code); } }
internal async Task WriteQuery(string sql, bool async) { var queryByteLen = TextEncoding.GetByteCount(sql); if (WriteBuffer.WriteSpaceLeft < 1 + 4) { await Flush(async); } WriteBuffer.WriteByte(FrontendMessageCode.Query); WriteBuffer.WriteInt32( sizeof(int) + // Message length (including self excluding code) queryByteLen + // Query byte length sizeof(byte)); // Null terminator await WriteBuffer.WriteString(sql, queryByteLen, async); if (WriteBuffer.WriteSpaceLeft < 1) { await Flush(async); } WriteBuffer.WriteByte(0); // Null terminator }
/// <param name="value">the value to be written</param> /// <param name="buf"></param> /// <param name="lengthCache">a cache in which to store length(s) of values to be written</param> /// <param name="parameter"> /// the <see cref="NpgsqlParameter"/> containing <paramref name="value"/>. Consulted for settings /// which impact how to send the parameter, e.g. <see cref="NpgsqlParameter.Size"/>. Can be null. /// <see cref="NpgsqlParameter.Size"/>. /// </param> public abstract void PrepareWrite(object value, WriteBuffer buf, LengthCache lengthCache, [CanBeNull] NpgsqlParameter parameter);
public override void PrepareWrite(object value, WriteBuffer buf, LengthCache lengthCache, NpgsqlParameter parameter = null) { Contract.Requires(buf != null); Contract.Requires(value != null); }
async Task AuthenticateSASL(List <string> mechanisms, bool async, CancellationToken cancellationToken) { // At the time of writing PostgreSQL only supports SCRAM-SHA-256 if (!mechanisms.Contains("SCRAM-SHA-256")) { throw new NpgsqlException("No supported SASL mechanism found (only SCRAM-SHA-256 is supported for now). " + "Mechanisms received from server: " + string.Join(", ", mechanisms)); } var mechanism = "SCRAM-SHA-256"; var passwd = GetPassword() ?? throw new NpgsqlException($"No password has been provided but the backend requires one (in SASL/{mechanism})"); // Assumption: the write buffer is big enough to contain all our outgoing messages var clientNonce = GetNonce(); await new SASLInitialResponseMessage(mechanism, PGUtil.UTF8Encoding.GetBytes("n,,n=*,r=" + clientNonce)) .Write(WriteBuffer, async, cancellationToken); await WriteBuffer.Flush(async, cancellationToken); var saslContinueMsg = await ReadExpecting <AuthenticationSASLContinueMessage>(async); if (saslContinueMsg.AuthRequestType != AuthenticationRequestType.AuthenticationSASLContinue) { throw new NpgsqlException("[SASL] AuthenticationSASLFinal message expected"); } var firstServerMsg = new AuthenticationSCRAMServerFirstMessage(saslContinueMsg.Payload); if (!firstServerMsg.Nonce.StartsWith(clientNonce)) { throw new InvalidOperationException("[SCRAM] Malformed SCRAMServerFirst message: server nonce doesn't start with client nonce"); } var scramFinalClientMsg = new SCRAMClientFinalMessage(passwd, firstServerMsg.Nonce, firstServerMsg.Salt, firstServerMsg.Iteration, clientNonce); await scramFinalClientMsg.Write(WriteBuffer, async, cancellationToken); await WriteBuffer.Flush(async, cancellationToken); var saslFinalServerMsg = await ReadExpecting <AuthenticationSASLFinalMessage>(async); if (saslFinalServerMsg.AuthRequestType != AuthenticationRequestType.AuthenticationSASLFinal) { throw new NpgsqlException("[SASL] AuthenticationSASLFinal message expected"); } var scramFinalServerMsg = new AuthenticationSCRAMServerFinalMessage(saslFinalServerMsg.Payload); if (scramFinalServerMsg.ServerSignature != Convert.ToBase64String(scramFinalClientMsg.ServerSignature)) { throw new NpgsqlException("[SCRAM] Unable to verify server signature"); } var okMsg = await ReadExpecting <AuthenticationRequestMessage>(async); if (okMsg.AuthRequestType != AuthenticationRequestType.AuthenticationOk) { throw new NpgsqlException("[SASL] Expected AuthenticationOK message"); } string GetNonce() { var nonceLength = 18; var rncProvider = RandomNumberGenerator.Create(); var nonceBytes = new byte[nonceLength]; rncProvider.GetBytes(nonceBytes); return(Convert.ToBase64String(nonceBytes)); } }
public abstract void Write(object value, WriteBuffer buf, NpgsqlParameter parameter);
internal void Flush() => WriteBuffer.Flush(false).GetAwaiter().GetResult();
internal Task Flush(bool async) => WriteBuffer.Flush(async);
public void SetUp() { Underlying = new MemoryStream(); WriteBuffer = new WriteBuffer(null, Underlying, ReadBuffer.DefaultBufferSize, PGUtil.UTF8Encoding); }
/// <param name="buf">the buffer into which to write the message.</param> /// <returns> /// Whether there was enough space in the buffer to contain the entire message. /// If false, the buffer should be flushed and write should be called again. /// </returns> internal abstract bool Write(WriteBuffer buf);
protected abstract Task Write(object value, WriteBuffer buf, LengthCache lengthCache, NpgsqlParameter parameter, bool async, CancellationToken cancellationToken);
/// <summary> /// Writes the message contents into the buffer. /// </summary> internal abstract void WriteFully(WriteBuffer buf);
void Cleanup() { _connector = null; _readBuf = null; _writeBuf = null; _isDisposed = true; }
internal Task Flush(bool async, CancellationToken cancellationToken = default) => WriteBuffer.Flush(async, cancellationToken);
protected abstract void Write(object value, WriteBuffer buf, NpgsqlParameter parameter = null);
/// <param name="buf">the buffer into which to write the message.</param> /// <param name="async"></param> /// <param name="cancellationToken"></param> /// <returns> /// Whether there was enough space in the buffer to contain the entire message. /// If false, the buffer should be flushed and write should be called again. /// </returns> internal abstract Task Write(WriteBuffer buf, bool async, CancellationToken cancellationToken);
internal abstract Task WriteWithLength([CanBeNull] object value, WriteBuffer buf, LengthCache lengthCache, NpgsqlParameter parameter, bool async, CancellationToken cancellationToken);
internal async Task WriteBind( List <NpgsqlParameter> inputParameters, string portal, string statement, bool allResultTypesAreUnknown, bool[]?unknownResultTypeList, bool async) { Debug.Assert(statement.All(c => c < 128)); Debug.Assert(portal.All(c => c < 128)); var headerLength = sizeof(byte) + // Message code sizeof(int) + // Message length sizeof(byte) + // Portal is always empty (only a null terminator) statement.Length + sizeof(byte) + // Statement name plus null terminator sizeof(short); // Number of parameter format codes that follow if (WriteBuffer.WriteSpaceLeft < headerLength) { Debug.Assert(WriteBuffer.Size >= headerLength, "Write buffer too small for Bind header"); await Flush(async); } var formatCodesSum = 0; var paramsLength = 0; foreach (var p in inputParameters) { formatCodesSum += (int)p.FormatCode; p.LengthCache?.Rewind(); paramsLength += p.ValidateAndGetLength(); } var formatCodeListLength = formatCodesSum == 0 ? 0 : formatCodesSum == inputParameters.Count ? 1 : inputParameters.Count; var messageLength = headerLength + sizeof(short) * formatCodeListLength + // List of format codes sizeof(short) + // Number of parameters sizeof(int) * inputParameters.Count + // Parameter lengths paramsLength + // Parameter values sizeof(short) + // Number of result format codes sizeof(short) * (unknownResultTypeList?.Length ?? 1); // Result format codes WriteBuffer.WriteByte(FrontendMessageCode.Bind); WriteBuffer.WriteInt32(messageLength - 1); Debug.Assert(portal == string.Empty); WriteBuffer.WriteByte(0); // Portal is always empty WriteBuffer.WriteNullTerminatedString(statement); WriteBuffer.WriteInt16(formatCodeListLength); // 0 length implicitly means all-text, 1 means all-binary, >1 means mix-and-match if (formatCodeListLength == 1) { if (WriteBuffer.WriteSpaceLeft < 2) { await Flush(async); } WriteBuffer.WriteInt16((short)FormatCode.Binary); } else if (formatCodeListLength > 1) { foreach (var p in inputParameters) { if (WriteBuffer.WriteSpaceLeft < 2) { await Flush(async); } WriteBuffer.WriteInt16((short)p.FormatCode); } } if (WriteBuffer.WriteSpaceLeft < 2) { await Flush(async); } WriteBuffer.WriteInt16(inputParameters.Count); foreach (var param in inputParameters) { param.LengthCache?.Rewind(); await param.WriteWithLength(WriteBuffer, async); } if (unknownResultTypeList != null) { if (WriteBuffer.WriteSpaceLeft < 2 + unknownResultTypeList.Length * 2) { await Flush(async); } WriteBuffer.WriteInt16(unknownResultTypeList.Length); foreach (var t in unknownResultTypeList) { WriteBuffer.WriteInt16(t ? 0 : 1); } } else { if (WriteBuffer.WriteSpaceLeft < 4) { await Flush(async); } WriteBuffer.WriteInt16(1); WriteBuffer.WriteInt16(allResultTypesAreUnknown ? 0 : 1); } }
internal Task WriteWithLength(WriteBuffer buf, bool async, CancellationToken cancellationToken) => Handler.WriteWithLength(Value, buf, LengthCache, this, async, cancellationToken);