Created
January 11, 2023 16:02
-
-
Save prio/27d032f03c818202fb6189315a7c0fcc to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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