-
Notifications
You must be signed in to change notification settings - Fork 3
/
Copy pathsocksMysql.rb
executable file
·80 lines (71 loc) · 2.88 KB
/
socksMysql.rb
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
# Proxy for Mysql through SOCKS
###################################################################################################
# External gems we need
require 'socket'
###################################################################################################
class SocksMysql
#################################################################################################
def initialize(dbConfig)
@host = dbConfig['host']
@port = dbConfig['port']
timestamp = Time.now.to_f
@sockName = ".mysqlpsk.#{@port}.#{Process.getpgrp}.#{timestamp}"
# Blow away obsolete sockets from prior runs
Dir.glob(".mysqlpsk.#{@port}.*").each { |fn|
fn =~ /\.mysqlpsk\.\d+\.(\d+)/
next if Process.getpgrp == $1.to_i
puts "Deleting obsolete #{fn}."
File.delete(fn)
}
# Fire up a thread to create and service the Unix socket we'll use to proxy MySQL traffic
ready = Queue.new
Thread.new { self.service(ready) }
ready.pop # wait for thread to become ready
# Reconfigure MySQL to connect through our socket
dbConfig.delete 'host'
dbConfig.delete 'port'
dbConfig['socket'] = @sockName
end
#################################################################################################
def service(ready)
Socket.unix_server_socket(@sockName) { |server|
ready << true
Socket.accept_loop(server) { |localSock, client_addrinfo|
# Serve each in a new thread, since Sequel might start multiple connections
Thread.new {
begin
TCPSocket.open(@host, @port) { |remoteSock|
bidiTransfer(localSock, remoteSock)
}
rescue Exception => e
puts "Proxy communication exception: #{e.inspect}.\n\t#{e.backtrace.join("\n\t")}"
end
}
}
}
rescue Exception => e
puts "Proxy communication exception: #{e.inspect}.\n\t#{e.backtrace.join("\n\t")}"
end
#################################################################################################
def bidiTransfer(localSock, remoteSock)
first = true
while true
rds, wrs, ers = IO.select([localSock, remoteSock], [])
rds.each { |r|
data = r.recv(8192)
data.empty? and return
if first
# Kludge alert! I can't figure out why, but sometimes we miss the first four bytes of the
# server's initial response. A correct response seems to always start with these, so add
# them back in.
!data.start_with? "N\x00\x00\x00" and data = "N\x00\x00\x00" + data
first = false
end
#puts "Tranferring data from #{r == localSock ? "local" : r == remoteSock ? "remote" : r}: #{data.inspect}"
(r == remoteSock ? localSock : remoteSock).write(data)
}
end
rescue Exception => e
puts "Proxy communication exception: #{e.inspect}.\n\t#{e.backtrace.join("\n\t")}"
end
end