Skip to content

Instantly share code, notes, and snippets.

@film42
Last active April 3, 2017 03:09
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save film42/ed9844b39df6f8a59a95dcf6791cb6bb to your computer and use it in GitHub Desktop.
Save film42/ed9844b39df6f8a59a95dcf6791cb6bb to your computer and use it in GitHub Desktop.
require "socket"
class Client
def initialize(addr, port, user, db)
@user = user
@db = db
@socket = TCPSocket.new(addr, port)
end
def login_packet
null = "\x0"
packet = [
[0].pack("N"), [0x30000].pack("N"),
"user", null,
@user, null,
"database", null,
@database, null,
null
]
# Set the packet size at pos 0. The null was a placeholder.
packet_size = packet.join.size
packet[0] = [packet_size].pack("N")
packet.join
end
def parse_uint32(buffer)
num = buffer[0..3].unpack("N").first
buffer = buffer[4..-1]
[num, buffer]
end
def parse_string(buffer)
null = "\x0"
pos = buffer.index(null)
fail "no valid string to parse" if pos.nil?
str = buffer[0...pos]
buffer = buffer[(pos+1)..-1]
[str, buffer]
end
def parse_message(buffer)
type = buffer[0]
size = buffer[1..4].unpack("N").first - 4
message = buffer[5...(5+size)]
buffer = buffer[(5 + size)..-1]
[type, message, buffer]
end
def connect
@socket.write(login_packet)
buffer = @socket.recv(1024)
loop do
type, message, buffer = parse_message(buffer)
case type
when "Z" then return :ok
when "R" then auth(message)
when "S" then params(message)
when "K" then backend_key_data(message)
else fail "Idk what this message is: #{type}"
end
end
ensure
@socket.close rescue nil
end
def backend_key_data(message)
process_id, message = parse_uint32(message)
puts "process_id: #{process_id}"
secret_key, message = parse_uint32(message)
puts "secret_key: #{secret_key}"
end
def params(message)
key, message = parse_string(message)
value, message = parse_string(message)
puts "param: #{key}"
puts "value: #{value}"
end
def auth(message)
code, _ = parse_uint32(message)
case code
when 0
puts "auth: ok"
else
fail "New auth code #{code}"
end
nil
end
end
host = "localhost"
port = 5432
user = "postgres"
database = "some_db"
client = Client.new(host, port, user, database)
puts client.connect
# $ ruby pg.rb
# auth: ok
# param: application_name
# value:
# param: client_encoding
# value: UTF8
# param: DateStyle
# value: ISO, MDY
# param: integer_datetimes
# value: on
# param: IntervalStyle
# value: postgres
# param: is_superuser
# value: on
# param: server_encoding
# value: UTF8
# param: server_version
# value: 9.6.1
# param: session_authorization
# value: postgres
# param: standard_conforming_strings
# value: on
# param: TimeZone
# value: US/Eastern
# process_id: 26819
# secret_key: 1782894957
# ok
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment