lib/pwnlib/tubes/sock.rb
# encoding: ASCII-8BIT
# frozen_string_literal: true
require 'socket'
require 'pwnlib/errors'
require 'pwnlib/tubes/tube'
module Pwnlib
module Tubes
# Socket!
class Sock < Tube
attr_reader :sock # @return [TCPSocket] The socket object.
# Instantiate a {Pwnlib::Tubes::Sock} object.
#
# @param [String] host
# The host to connect.
# @param [Integer] port
# The port to connect.
# @param [Float?] timeout
# See {Pwnlib::Tubes::Tube#initialize}.
def initialize(host, port, timeout: nil)
super(timeout: timeout)
@sock = TCPSocket.new(host, port)
@sock.binmode
@timeout = nil
@closed = { read: false, write: false }
end
# Close the TCPSocket if no arguments passed.
# Or close the direction in +sock+.
#
# @param [:both, :recv, :read, :send, :write] direction
# * Close the TCPSocket if +:both+ or no arguments passed.
# * Disallow further read in +sock+ if +:recv+ or +:read+ passed.
# * Disallow further write in +sock+ if +:send+ or +:write+ passed.
#
# @return [void]
#
# @diff In pwntools-python, method +shutdown(direction)+ is for closing socket one side,
# +close()+ is for closing both side. We merge these two methods into one here.
def close(direction = :both)
if direction == :both
return if @sock.closed?
@closed[:read] = @closed[:write] = true
@sock.close
else
shutdown(*normalize_direction(direction))
end
end
private
alias io_out sock
def shutdown(direction)
return if @closed[direction]
@closed[direction] = true
case direction
when :read
@sock.close_read
when :write
@sock.close_write
end
end
def timeout_raw=(timeout)
@timeout = timeout
end
def send_raw(data)
raise ::Pwnlib::Errors::EndOfTubeError if @closed[:write]
begin
@sock.write(data)
rescue Errno::EPIPE, Errno::ECONNRESET, Errno::ECONNREFUSED
shutdown(:write)
raise ::Pwnlib::Errors::EndOfTubeError
end
end
def recv_raw(size)
raise ::Pwnlib::Errors::EndOfTubeError if @closed[:read]
begin
rs, = IO.select([@sock], [], [], @timeout)
return if rs.nil?
@sock.readpartial(size)
rescue Errno::ECONNREFUSED, Errno::ECONNRESET, Errno::ECONNABORTED, EOFError
shutdown(:read)
raise ::Pwnlib::Errors::EndOfTubeError
end
end
end
end
end