ronin-rb/ronin-support

View on GitHub
lib/ronin/support/network/unix/mixin.rb

Summary

Maintainability
A
0 mins
Test Coverage
# frozen_string_literal: true
#
# Copyright (c) 2006-2023 Hal Brodigan (postmodern.mod3 at gmail.com)
#
# ronin-support 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 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.  If not, see <https://www.gnu.org/licenses/>.
#

require 'socket'
require 'timeout'

module Ronin
  module Support
    module Network
      module UNIX
        #
        # Provides helper methods for communicating with UNIX sockets.
        #
        module Mixin
          #
          # Tests whether a UNIX socket is open.
          #
          # @param [String] path
          #   The path to the socket.
          #
          # @param [Integer] timeout (5)
          #   The maximum time to attempt connecting.
          #
          # @return [Boolean, nil]
          #   Specifies whether the UNIX socket is open.
          #   If the connection was not accepted, `nil` will be returned.
          #
          # @api public
          #
          # @since 0.5.0
          #
          def unix_open?(path,timeout=nil)
            timeout ||= 5

            begin
              Timeout.timeout(timeout) do
                socket = unix_connect(path)
                socket.close
              end
              return true
            rescue Timeout::Error
              return nil
            rescue SocketError, SystemCallError
              return false
            end
          end

          #
          # Connects to a UNIX socket.
          #
          # @param [String] path
          #   The path to the UNIX socket.
          #
          # @yield [socket]
          #   If a block is given, it will be passed an UNIX socket object.
          #   Once the block has returned, the UNIX socket will be closed.
          #
          # @yieldparam [UNIXSocket] socket
          #   The UNIX socket.
          #
          # @return [UNIXSocket, nil]
          #   The UNIX socket. If a block was given, `nil` will be returned.
          #
          # @example
          #   unix_connect('/tmp/haproxy.stats.socket')
          #
          # @example
          #   unix_connect('/tmp/haproxy.stats.socket') do |socket|
          #     # ...
          #   end
          #
          # @see https://rubydoc.info/stdlib/socket/UNIXSocket
          #
          # @api public
          #
          def unix_connect(path)
            socket = UNIXSocket.new(path)

            if block_given?
              yield socket
              socket.close
            else
              return socket
            end
          end

          #
          # Connects to a UNIX Socket and sends the given data.
          #
          # @param [String] data
          #   The data to send to the socket.
          #
          # @param [String] path
          #   The path to the socket.
          #
          # @yield [socket]
          #   If a block is given, it will be passed the newly created socket.
          #
          # @yieldparam [UNIXSocket] socket
          #   The newly created UNIXSocket object.
          #
          # @return [UNIXSocket]
          #   The newly created UNIXSocket object.
          #
          # @api public
          #
          def unix_connect_and_send(data,path)
            socket = unix_connect(path)
            socket.write(data)

            yield socket if block_given?
            return socket
          end

          #
          # Connects to a UNIX socket, sends the given data and then closes the
          # socket.
          #
          # @param [String] data
          #   The data to send to the UNIX socket.
          #
          # @param [String] path
          #   The UNIX socket to connect to.
          #
          # @return [true]
          #   The data was successfully sent.
          #
          # @example
          #   buffer = "GET /" + ('A' * 4096) + "\n\r"
          #   unix_send(buffer,'/tmp/thin.socket')
          #   # => true
          #
          # @api public
          #
          def unix_send(data,path)
            unix_connect(path) do |socket|
              socket.write(data)
            end

            return true
          end

          #
          # Opens a UNIX socket.
          #
          # @param [String] path
          #   The path for the new UNIX socket.
          #
          # @yield [server]
          #   If a block is given, it will be passed an UNIX socket object.
          #
          # @yieldparam [UNIXServer] server
          #   The new UNIX socket.
          #
          # @return [UNIXServer]
          #   The new UNIX socket.
          #
          # @example
          #   unix_server('/tmp/test.socket')
          #
          # @see https://rubydoc.info/stdlib/socket/UNIXServer
          #
          # @api public
          #
          def unix_server(path)
            socket = UNIXServer.new(path)

            yield socket if block_given?
            return socket
          end

          #
          # Temporarily opens a UNIX socket.
          #
          # @param [String] path
          #   The path for the new UNIX socket.
          #
          # @yield [server]
          #   If a block is given, it will be passed an UNIX socket object.
          #
          # @yieldparam [UNIXServer] server
          #   The new UNIX socket.
          #
          # @example
          #   unix_server_session('/tmp/test.socket') do |server|
          #     # ...
          #   end
          #
          # @api public
          #
          def unix_server_session(path,&block)
            socket = unix_server(path,&block)
            socket.close
            return nil
          end

          #
          # Opens a UNIX socket, accepts connections in a loop.
          #
          # @param [String] path
          #   The path for the new UNIX socket.
          #
          # @yield [client]
          #   If a block is given, it will be passed each accepted connection.
          #
          # @yieldparam [UNIXSocket] client
          #   An accepted connection to UNIX socket.
          #
          # @example
          #   unix_server_loop('/tmp/test.socket') do |client|
          #     # ...
          #   end
          #
          # @api public
          #
          def unix_server_loop(path)
            unix_server_session(path) do |server|
              loop do
                client = server.accept

                yield client if block_given?
                client.close
              end
            end
          end

          #
          # Opens a UNIX socket, accepts a connection, then closes the socket.
          #
          # @param [String] path
          #   The path for the new UNIX socket.
          #
          # @yield [client]
          #   If a block is given, it will be passed the accepted connection.
          #
          # @yieldparam [UNIXSocket] client
          #   The accepted connection to UNIX socket.
          #
          # @example
          #   unix_accept('/tmp/test.socket') do |client|
          #     # ...
          #   end
          #
          # @api public
          #
          def unix_accept(path)
            unix_server_session(path) do |server|
              client = server.accept

              yield client if block_given?
              client.close
            end
          end
        end
      end
    end
  end
end