Skip to content

Instantly share code, notes, and snippets.

@prio
Created January 11, 2023 16:02
Show Gist options
  • Save prio/27d032f03c818202fb6189315a7c0fcc to your computer and use it in GitHub Desktop.
Save prio/27d032f03c818202fb6189315a7c0fcc to your computer and use it in GitHub Desktop.
using System.Net;
using System.Net.Sockets;
using System.Text;
namespace PgWire;
class PgBuffer
{
// private NetworkStream _stream;
//
// public PgBuffer(NetworkStream stream)
// {
// this._stream = stream;
// }
//
public static Byte[] Read(NetworkStream stream, int n)
{
var bytes = new Byte[n];
var bytesRead = stream.Read(bytes, 0, n);
// if (bytesRead != n)
// throw new Exception($"Unable to read {n} bytes");
return bytes;
}
public static int ReadInt32(NetworkStream stream)
{
var bytes = Read(stream, 4);
if (BitConverter.IsLittleEndian)
Array.Reverse(bytes);
return BitConverter.ToInt32(bytes, 0);
}
public static string[] ReadParameters(NetworkStream stream, int n)
{
var bytes = Read(stream, n);
var s = new string(Encoding.ASCII.GetChars(bytes));
var strings = s.Split(new char[] {'\0'}, StringSplitOptions.RemoveEmptyEntries);
return strings;
}
}
public class PgTcpListener
{
public void Start()
{
TcpListener? server = null;
try
{
Int32 port = 55432;
IPAddress localAddr = IPAddress.Parse("127.0.0.1");
server = new TcpListener(localAddr, port);
server.Start();
String data = null;
while(true)
{
Console.Write("Waiting for a connection... ");
// Perform a blocking call to accept requests.
// You could also use server.AcceptSocket() here.
using TcpClient client = server.AcceptTcpClient();
Console.WriteLine("Connected!");
data = null;
// Get a stream object for reading and writing
NetworkStream stream = client.GetStream();
int i;
// Handshake
ReadSslRequest(stream);
WriteNotice(stream);
ReadStartupMessage(stream);
// Authentication
// SendAuthenticationRequest(stream);
// ReadAuthentication(stream);
SendAuthenticationOk(stream);
while (true)
{
Console.Write("Waiting for query... ");
SendReadyForQuery(stream);
Byte[] _bytes = new Byte[256];
// Loop to receive all the data sent by the client.
while((i = stream.Read(_bytes, 0, _bytes.Length))!=0)
{
// Translate data bytes to a ASCII string.
data = System.Text.Encoding.ASCII.GetString(_bytes, 0, i);
Console.WriteLine("Received: {0}", data);
// Process the data sent by the client.
// data = data.ToUpper();
//
// byte[] msg = System.Text.Encoding.ASCII.GetBytes(data);
//
// // Send back a response.
// stream.Write(msg, 0, msg.Length);
// Console.WriteLine("Sent: {0}", data);
}
}
}
}
catch(SocketException e)
{
Console.WriteLine("SocketException: {0}", e);
}
finally
{
server.Stop();
}
Console.WriteLine("\nHit enter to continue...");
Console.Read();
}
private void ReadSslRequest(NetworkStream stream)
{
int msgLen = PgBuffer.ReadInt32(stream);
int sslCode = PgBuffer.ReadInt32(stream);
if ((msgLen != 8) && (sslCode != 80877103))
throw new Exception("Unsupported SSL request");
}
private void ReadStartupMessage(NetworkStream stream)
{
var msgLen = PgBuffer.ReadInt32(stream);
var version = PgBuffer.ReadInt32(stream);
var vMaj = version >> 16;
var vMin = version & 0xffff;
var msg = PgBuffer.ReadParameters(stream, msgLen - 8);
var _msg = string.Join(", ", msg);
Console.WriteLine($"PSQ {vMaj}.{vMin} - [{_msg}]");
}
private void WriteNotice(NetworkStream stream)
{
byte[] msg = { (byte)'N' } ;
stream.Write(msg, 0, msg.Length);
}
private void SendAuthenticationRequest(NetworkStream stream)
{
var a = (byte)'R';
var b = BitConverter.GetBytes(8);
var c = BitConverter.GetBytes(3);
if (BitConverter.IsLittleEndian)
{
Array.Reverse(b);
Array.Reverse(c);
}
var outputBytes = new List<byte> {a};
outputBytes.AddRange(b);
outputBytes.AddRange(c);
var bytes = outputBytes.ToArray();
stream.Write(bytes, 0, bytes.Length);
}
private void ReadAuthentication(NetworkStream stream)
{
Byte[] _bytes = new Byte[256];
string data;
int i;
// Loop to receive all the data sent by the client.
while((i = stream.Read(_bytes, 0, _bytes.Length)) != 0)
{
// Translate data bytes to a ASCII string.
data = System.Text.Encoding.ASCII.GetString(_bytes, 0, i);
Console.WriteLine("Received: {0}", data);
}
// var typeCode = PgBuffer.Read(stream, 16);
// // if (typeCode[0] != (byte)'p')
// // {
// // //self.send_error("FATAL", "28000", "authentication failure")
// // throw new Exception($"Only 'Password' auth is supported, got {typeCode}");
// // }
//
// var msglen = PgBuffer.ReadInt32(stream);
// var password = PgBuffer.Read(stream, msglen - 4);
// Console.WriteLine(password);
}
private byte[] Encode(int n)
{
var bytes = BitConverter.GetBytes(n);
if (BitConverter.IsLittleEndian)
Array.Reverse(bytes);
return bytes;
}
private byte[] Encode(char c)
{
return new []{ (byte)c };
}
private byte[] Encode(char message, params int[] ints, params char[] tail)
{
var outputBytes = new List<byte> { };
outputBytes.AddRange(Encode(message));
foreach (int i in ints)
outputBytes.AddRange(Encode(i));
foreach (char c in tail)
outputBytes.AddRange(Encode(c));
return outputBytes.ToArray();
}
private void SendAuthenticationOk(NetworkStream stream)
{
var bytes = Encode('R', 8, 0);
stream.Write(bytes, 0, bytes.Length);
}
private void SendReadyForQuery(NetworkStream stream)
{
var bytes = Encode('R', 5, 'I');
stream.Write(bytes, 0, bytes.Length);
// var a = (byte)'Z';
// var b = BitConverter.GetBytes(5);
// var c = (byte)'I';
// if (BitConverter.IsLittleEndian)
// {
// Array.Reverse(b);
// }
// var outputBytes = new List<byte> {a};
// outputBytes.AddRange(b);
// outputBytes.Add(c);
// var bytes = outputBytes.ToArray();
// stream.Write(bytes, 0, bytes.Length);
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment