# frozen_string_literal: true
#
# ronin-support-web - A web support library for ronin-rb.
#
# Copyright (c) 2023-2024 Hal Brodigan (postmodern.mod3@gmail.com)
#
# ronin-support-web is free software: you can redistribute it and/or modify
# it under the terms of the GNU Lesser General Public License as published
# by the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# ronin-support-web is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU Lesser General Public License for more details.
#
# You should have received a copy of the GNU Lesser General Public License
# along with ronin-support-web. If not, see .
#
require 'ronin/support/web/websocket/url_methods'
module Ronin
module Support
module Web
module WebSocket
#
# Base class for all WebSockets.
#
# @abstract
#
# @api private
#
class Socket
# The underlying socket.
#
# @return [TCPSocket, OpenSSL::SSL::SSLSocket]
attr_reader :socket
# The WebSocket handshake information.
#
# @return [::WebSocket::Handshake::Client, WebSocket::Handshake::Server]
attr_reader :handshake
#
# Indicates whether the handshake has finished.
#
# @return [Boolean]
#
# @api public
#
def handshake_finished?
@handshake.finished?
end
#
# Indicates whether the handshake was valid.
#
# @return [Boolean]
#
# @api public
#
def handshake_valid?
@handshake.valid?
end
#
# Sends a data frame.
#
# @param [#to_s] data
# The data to send.
#
# @param [:text, :binary, :ping, :pong, :close] type
# The data frame type.
#
# @api public
#
def send_frame(data, type: :text)
outgoing_frame = @outgoing_frame_class.new(
version: @handshake.version,
data: data,
type: type
)
@socket.write(outgoing_frame.to_s)
end
#
# Sends a data frame.
#
# @param [#to_s] data
# The data to send.
#
# @param [Hash{Symbol => Object}] kwargs
# Additional keyword arguments for {#send_frame}.
#
# @option kwargs [:text, :binary, :ping, :pong, :close] :type (:text)
# The data frame type.
#
# @api public
#
# @see #send_frame
#
def send(data,**kwargs)
send_frame(data,**kwargs)
end
#
# Receives a data frame from the WebSocket.
#
# @return [WebSocket::Frame::Incoming::Client,
# WebSocket::Frame::Incoming::Server]
# The received websocket data frame.
#
# @api public
#
def recv_frame
frame = @incoming_frame_class.new(version: @handshake.version)
begin
# read data into the input frame
frame << @socket.readpartial(1024)
rescue EOFError
return nil
end
return frame.next
end
#
# Receives a data frame.
#
# @return [String, nil]
#
# @api public
#
def recv
data = recv_frame.data
data unless data.empty?
end
#
# Closes the socket.
#
# @api public
#
def close
@socket.close
end
#
# Determines if the socket is closed.
#
# @return [Boolean]
#
# @api public
#
def closed?
@socket.closed?
end
private
#
# Sets the frame classes to use.
#
# @param [Class,
# Class] incoming_frame_class
#
# @param [Class,
# Class] outgoing_frame_class
#
# @api private
#
def set_frame_classes(incoming_frame_class,outgoing_frame_class)
@incoming_frame_class = incoming_frame_class
@outgoing_frame_class = outgoing_frame_class
end
end
end
end
end
end