async Task AuthenticateMD5(string username, byte[] salt, bool async, CancellationToken cancellationToken) { var passwd = Settings.Password; if (passwd == null) { // No password was provided. Attempt to pull the password from the pgpass file. var matchingEntry = PgPassFile.LoadDefaultFile()?.GetFirstMatchingEntry(Settings.Host, Settings.Port, Settings.Database, Settings.Username); if (matchingEntry != null) { Log.UsingPgpassFile(); passwd = matchingEntry.Password; } } if (passwd == null) { throw new NpgsqlException("No password has been provided but the backend requires one (in MD5)"); } await PasswordMessage .CreateMD5(passwd, username, salt) .Write(WriteBuffer, async, cancellationToken); await WriteBuffer.Flush(async); await ReadExpecting <AuthenticationRequestMessage>(async); }
internal async Task WriteSASLInitialResponse(string mechanism, byte[] initialResponse, bool async) { var len = sizeof(byte) + // Message code sizeof(int) + // Length PGUtil.UTF8Encoding.GetByteCount(mechanism) + sizeof(byte) + // Mechanism plus null terminator sizeof(int) + // Initial response length (initialResponse?.Length ?? 0); // Initial response payload if (WriteBuffer.WriteSpaceLeft < len) { await WriteBuffer.Flush(async); } WriteBuffer.WriteByte(FrontendMessageCode.Password); WriteBuffer.WriteInt32(len - 1); WriteBuffer.WriteString(mechanism); WriteBuffer.WriteByte(0); // null terminator if (initialResponse == null) { WriteBuffer.WriteInt32(-1); } else { WriteBuffer.WriteInt32(initialResponse.Length); WriteBuffer.WriteBytes(initialResponse); } }
async Task FlushAndWrite(WriteBuffer buf, bool async, CancellationToken cancellationToken) { await buf.Flush(async, cancellationToken); Debug.Assert(Length <= buf.WriteSpaceLeft, $"Message of type {GetType().Name} has length {Length} which is bigger than the buffer ({buf.WriteSpaceLeft})"); WriteFully(buf); }
/// <summary> /// Starts writing a single row, must be invoked before writing any columns. /// </summary> public void StartRow() { CheckDisposed(); if (_column != -1 && _column != NumColumns) { throw new InvalidOperationException("Row has already been started and must be finished"); } if (_buf.WriteSpaceLeft < 2) { _buf.Flush(); } _buf.WriteInt16(NumColumns); _column = 0; }
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); }
public override void Write(byte[] buffer, int offset, int count) { CheckDisposed(); if (!CanWrite) { throw new InvalidOperationException("Stream not open for writing"); } if (count == 0) { return; } EnsureDataMessage(); if (count <= _writeBuf.WriteSpaceLeft) { _writeBuf.WriteBytes(buffer, offset, count); return; } try { // Value is too big. Flush whatever is in the buffer, then write a new CopyData // directly with the buffer. Flush(); _writeBuf.WriteByte((byte)BackendMessageCode.CopyData); _writeBuf.WriteInt32(count + 4); _writeBuf.Flush(); _writeBuf.DirectWrite(buffer, offset, count); EnsureDataMessage(); } catch { _connector.Break(); Cleanup(); throw; } }
async Task AuthenticateCleartext(bool async, CancellationToken cancellationToken) { var passwd = GetPassword(); if (passwd == null) { throw new NpgsqlException("No password has been provided but the backend requires one (in cleartext)"); } await PasswordMessage .CreateClearText(passwd) .Write(WriteBuffer, async, cancellationToken); await WriteBuffer.Flush(async); await ReadExpecting <AuthenticationRequestMessage>(async); }
async Task AuthenticateMD5(string username, byte[] salt, bool async, CancellationToken cancellationToken) { var passwd = GetPassword(); if (passwd == null) { throw new NpgsqlException("No password has been provided but the backend requires one (in MD5)"); } await PasswordMessage .CreateMD5(passwd, username, salt) .Write(WriteBuffer, async, cancellationToken); await WriteBuffer.Flush(async); await ReadExpecting <AuthenticationRequestMessage>(async); }
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 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); }
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)); } }
void DoWrite <T>(TypeHandler handler, [CanBeNull] T value) { try { if (_buf.WriteSpaceLeft < 4) { FlushAndStartDataMessage(); } var asObject = (object)value; // TODO: Implement boxless writing in the future if (asObject == null) { _buf.WriteInt32(-1); _column++; return; } _dummyParam.ConvertedValue = null; var asSimple = handler as ISimpleTypeHandler; if (asSimple != null) { var len = asSimple.ValidateAndGetLength(asObject, _dummyParam); _buf.WriteInt32(len); if (_buf.WriteSpaceLeft < len) { Contract.Assume(_buf.Size >= len); FlushAndStartDataMessage(); } asSimple.Write(asObject, _buf, _dummyParam); _column++; return; } var asChunking = handler as IChunkingTypeHandler; if (asChunking != null) { _lengthCache.Clear(); var len = asChunking.ValidateAndGetLength(asObject, ref _lengthCache, _dummyParam); _buf.WriteInt32(len); // If the type handler used the length cache, rewind it to skip the first position: // it contains the entire value length which we already have in len. if (_lengthCache.Position > 0) { _lengthCache.Rewind(); _lengthCache.Position++; } asChunking.PrepareWrite(asObject, _buf, _lengthCache, _dummyParam); var directBuf = new DirectBuffer(); while (!asChunking.Write(ref directBuf)) { Flush(); // The following is an optimization hack for writing large byte arrays without passing // through our buffer if (directBuf.Buffer != null) { len = directBuf.Size == 0 ? directBuf.Buffer.Length : directBuf.Size; _buf.WritePosition = 1; _buf.WriteInt32(len + 4); _buf.Flush(); _writingDataMsg = false; _buf.DirectWrite(directBuf.Buffer, directBuf.Offset, len); directBuf.Buffer = null; directBuf.Size = 0; } EnsureDataMessage(); } _column++; return; } throw PGUtil.ThrowIfReached(); } catch { _connector.Break(); Cleanup(); throw; } }
internal Task Flush(bool async) => WriteBuffer.Flush(async);
internal void Flush() => WriteBuffer.Flush(false).GetAwaiter().GetResult();
public override void Flush() { CheckDisposed(); _writeBuf.Flush(); }
internal Task Flush(bool async, CancellationToken cancellationToken = default) => WriteBuffer.Flush(async, cancellationToken);