
I've add to add code to allow the environment to specify the upstream tiemout so we don't have to wait 30s for this test to happen.
155 lines
3.1 KiB
Ruby
155 lines
3.1 KiB
Ruby
require 'socket'
|
|
require 'timeout'
|
|
|
|
require 'flexnbd/constants'
|
|
|
|
module FlexNBD
|
|
class FakeDest
|
|
class Client
|
|
def initialize(sock)
|
|
@sock = sock
|
|
end
|
|
|
|
def write_hello(opts = {})
|
|
@sock.write('NBDMAGIC')
|
|
|
|
if opts[:magic] == :wrong
|
|
write_rand(@sock, 8)
|
|
else
|
|
@sock.write("\x00\x00\x42\x02\x81\x86\x12\x53")
|
|
end
|
|
|
|
if opts[:size] == :wrong
|
|
write_rand(@sock, 8)
|
|
else
|
|
@sock.write("\x00\x00\x00\x00\x00\x00\x10\x00")
|
|
end
|
|
|
|
@sock.write("\x00" * 128)
|
|
end
|
|
|
|
def write_rand(sock, len)
|
|
len.times { sock.write(rand(256).chr) }
|
|
end
|
|
|
|
def read_request
|
|
req = @sock.read(28)
|
|
|
|
magic_s = req[0...4]
|
|
type_s = req[4...8]
|
|
handle_s = req[8...16]
|
|
from_s = req[16...24]
|
|
len_s = req[24...28]
|
|
|
|
{
|
|
magic: magic_s,
|
|
type: type_s.unpack('N').first,
|
|
handle: handle_s,
|
|
from: self.class.parse_be64(from_s),
|
|
len: len_s.unpack('N').first
|
|
}
|
|
end
|
|
|
|
def write_error(handle)
|
|
write_reply(handle, 1)
|
|
end
|
|
|
|
def nread
|
|
@sock.nread
|
|
end
|
|
|
|
def disconnected?
|
|
Timeout.timeout(2) do
|
|
@sock.read(1).nil?
|
|
end
|
|
rescue Timeout::Error
|
|
return false
|
|
end
|
|
|
|
def write_reply(handle, err = 0, opts = {})
|
|
if opts[:magic] == :wrong
|
|
write_rand(@sock, 4)
|
|
else
|
|
@sock.write(::FlexNBD::REPLY_MAGIC)
|
|
end
|
|
|
|
@sock.write([err].pack('N'))
|
|
@sock.write(handle)
|
|
end
|
|
|
|
def close
|
|
@sock.close
|
|
end
|
|
|
|
def read_data(len)
|
|
@sock.read(len)
|
|
end
|
|
|
|
def write_data(len)
|
|
@sock.write(len)
|
|
end
|
|
|
|
def getsockopt(level, optname)
|
|
@sock.getsockopt(level, optname)
|
|
end
|
|
|
|
def self.parse_be64(str)
|
|
raise "String is the wrong length: 8 bytes expected (#{str.length} received)" unless
|
|
str.length == 8
|
|
|
|
top, bottom = str.unpack('NN')
|
|
(top << 32) + bottom
|
|
end
|
|
|
|
def receive_mirror(opts = {})
|
|
write_hello
|
|
loop do
|
|
req = read_request
|
|
case req[:type]
|
|
when 1
|
|
read_data(req[:len])
|
|
write_reply(req[:handle])
|
|
when 65_536
|
|
write_reply(req[:handle], opts[:err] == :entrust ? 1 : 0)
|
|
break
|
|
else
|
|
raise "Unexpected request: #{req.inspect}"
|
|
end
|
|
end
|
|
|
|
disc = read_request
|
|
|
|
if disc[:type] == 2
|
|
close
|
|
else
|
|
raise "Not a disconnect: #{req.inspect}"
|
|
end
|
|
end
|
|
end # class Client
|
|
|
|
def initialize(addr, port)
|
|
@sock = TCPServer.new(addr, port)
|
|
end
|
|
|
|
def accept(err_msg = 'Timed out waiting for a connection', timeout = 5)
|
|
client_sock = nil
|
|
|
|
begin
|
|
Timeout.timeout(timeout) do
|
|
client_sock = @sock.accept
|
|
end
|
|
rescue Timeout::Error
|
|
raise Timeout::Error, err_msg
|
|
end
|
|
|
|
client_sock
|
|
|
|
Client.new(client_sock)
|
|
end
|
|
|
|
def close
|
|
@sock.close
|
|
end
|
|
end # module FakeDest
|
|
end # module FlexNBD
|