Ruby Socket

Lightweight Introduction

Ruby Socket Class Hierarchy

To know the socket hierarchy in ruby here a simple tree explains it.

IO                              # The basis for all input and output in Ruby
└── BasicSocket                 # Abstract base class for all socket classes
    ├── IPSocket                # Super class for protocols using the Internet Protocol (AF_INET)
    │   ├── TCPSocket           # Class for Transmission Control Protocol (TCP) sockets
    │   │   ├── SOCKSSocket     # Helper class for building TCP socket servers applications
    │   │   └── TCPServer       # Helper class for building TCP socket servers
    │   └── UDPSocket           # Class for User Datagram Protocol (UDP) sockets
    ├── Socket                  # Base socket class that mimics that BSD Sockets API. It provides more operating system specific functionality
    └── UNIXSocket              # Class providing IPC using the UNIX domain protocol (AF_UNIX)
        └── UNIXServer          # Helper class for building UNIX domain protocol socket servers

I'll verbosely mention some of Socket::Constants here since I didn't find an obvious reference listing it except Programming Ruby1.9 The Pragmatic Programmers' Guide; Otherwise you've to ri Socket::Constants from command line which is a good way to get the description of each constant.

Socket Types

  • SOCK_RAW

  • SOCK_PACKET

  • SOCK_STREAM

  • SOCK_DRAM

  • SOCK_RDM

  • SOCK_SEQPACKET

Address Families(Socket Domains)

  • AF_APPLETALK

  • AF_ATM

  • AF_AX25

  • AF_CCITT

  • AF_CHAOS

  • AF_CNT

  • AF_COIP

  • AF_DATAKIT

  • AF_DEC

  • AF_DLI

  • AF_E164

  • AF_ECMA

  • AF_HYLINK

  • AF_IMPLINK

  • AF_INET(IPv4)

  • AF_INET6(IPv6)

  • AF_IPX

  • AF_ISDN

  • AF_ISO

  • AF_LAT

  • AF_LINK

  • AF_LOCAL(UNIX)

  • AF_MAX

  • AF_NATM

  • AF_NDRV

  • AF_NETBIOS

  • AF_NETGRAPH

  • AF_NS

  • AF_OSI

  • AF_PACKET

  • AF_PPP

  • AF_PUP

  • AF_ROUTE

  • AF_SIP

  • AF_SNA

  • AF_SYSTEM

  • AF_UNIX

  • AF_UNSPEC

Socket Protocol

  • IPPROTO_SCTP

  • IPPROTO_TCP

  • IPPROTO_UDP

Protocol Families

  • PF_APPLETALK

  • PF_ATM

  • PF_AX25

  • PF_CCITT

  • PF_CHAOS

  • PF_CNT

  • PF_COIP

  • PF_DATAKIT

  • PF_DEC

  • PF_DLI

  • PF_ECMA

  • PF_HYLINK

  • PF_IMPLINK

  • PF_INET

  • PF_INET6

  • PF_IPX

  • PF_ISDN

  • PF_ISO

  • PF_KEY

  • PF_LAT

  • PF_LINK

  • PF_LOCAL

  • PF_MAX

  • PF_NATM

  • PF_NDRV

  • PF_NETBIOS

  • PF_NETGRAPH

  • PF_NS

  • PF_OSI

  • PF_PACKET

  • PF_PIP

  • PF_PPP

  • PF_PUP

  • PF_ROUTE

  • PF_RTIP

  • PF_SIP

  • PF_SNA

  • PF_SYSTEM

  • PF_UNIX

  • PF_UNSPEC

  • PF_XTP

Socket options

  • SO_ACCEPTCONN

  • SO_ACCEPTFILTER

  • SO_ALLZONES

  • SO_ATTACH_FILTER

  • SO_BINDTODEVICE

  • SO_BINTIME

  • SO_BROADCAST

  • SO_DEBUG

  • SO_DETACH_FILTER

  • SO_DONTROUTE

  • SO_DONTTRUNC

  • SO_ERROR

  • SO_KEEPALIVE

  • SO_LINGER

  • SO_MAC_EXEMPT

  • SO_NKE

  • SO_NOSIGPIPE

  • SO_NO_CHECK

  • SO_NREAD

  • SO_OOBINLINE

  • SO_PASSCRED

  • SO_PEERCRED

  • SO_PEERNAME

  • SO_PRIORITY

  • SO_RCVBUF

  • SO_RCVLOWAT

  • SO_RCVTIMEO

  • SO_RECVUCRED

  • SO_REUSEADDR

  • SO_REUSEPORT

  • SO_SECURITY_AUTHENTICATION

  • SO_SECURITY_ENCRYPTION_NETWORK

  • SO_SECURITY_ENCRYPTION_TRANSPORT

  • SO_SNDBUF

  • SO_SNDLOWAT

  • SO_SNDTIMEO

  • SO_TIMESTAMP

  • SO_TIMESTAMPNS

  • SO_TYPE

  • SO_USELOOPBACK

  • SO_WANTMORE

  • SO_WANTOOBFLAG

Creating Socket Template

Socket.new(domain, socktype [, protocol])

domain(Address\/Protocol Families): like AF_INET, PF_PACKET, etc

socktype: like SOCK_RAW, SOCK_STREAM

protocol: by default, it's 0m it should be a protocol defined (we'll manipulate that later)

TCP Socket

Server\/Client life cycle

            Client        Server
              |             |                  
   socket     +             +      socket
              |             |
   connect    +--------,    +      bind
              |         |   |
   write ,--> +------,  |   +      listen
         |    |      |  |   |
   read  `----+ <--, |  `-> +      accept
              |    | |      |
   close      +--, | `----> + <--, read <--,
                 | |        |    |         |
                 | `--------+----' write   ٨
                 |                         |
                 `----->------>------->----`

General Socket usage

Get List of local IPaddreses

require 'socket'
Socket.ip_address_list

Get Hostname

Socket.gethostname

TCP Server

Here we'll represent an absolute TCP server. This server will access connect from one client and send a message to it once connected then close the client and server connection

require 'socket'

server = TCPServer.new('0.0.0.0', 9911) # Server, binds/listens all interfaces on port 9911
client = server.accept                  # Wait for client to connect
rhost  = client.peeraddr.last           # peeraddr, returns remote [address_family, port, hostname, numeric_address(ip)]
client.puts "Hi TCP Client! #{rhost}"   # Send a message to the client once it connect
client.gets.chomp                       # Read incoming message from client
client.close                            # Close the client's connection
server.close                            # Close the TCP Server

Note: if you want to list on unused and random port, set to port 0, ruby will find vacancy port then use it. ex.

require 'socket'
server = TCPServer.new('0.0.0.0', 0)
server.addr[1]    # Shows the picked port

TCP Client

require 'socket'

client = TCPSocket.new('127.0.0.1', 9911)   # Client, connects to server on port 9911
rhost  = client.peeraddr.last               # Get the remote server's IP address 
client.gets.chomp
client.puts "Hi, TCP Server #{rhost}"
client.close

You can put timeout/time interval for current connection in-case the server's response get delayed and the socket is still open.

timeval = [3, 0].pack("l_2")        # Time interval 3 seconds 
client.setsockopt Socket::SOL_SOCKET, Socket::SO_RCVTIMEO, timeval      # Set socket receiving time interval 
client.setsockopt Socket::SOL_SOCKET, Socket::SO_SNDTIMEO, timeval      # Set socket sending time interval
client.getsockopt(Socket::SOL_SOCKET, Socket::SO_RCVTIMEO).inspect      # Optional, Check if socket option has been set
client.getsockopt(Socket::SOL_SOCKET, Socket::SO_SNDTIMEO).inspect      # Optional, Check if socket option has been set

There are some alternatives for puts and gets methods.You can see the difference and its classes using method method in Pry interpreter console

>> s = TCPSocket.new('0.0.0.0', 9911)
=> #<TCPSocket:fd 11>
>> s.method :puts
=> #<Method: TCPSocket(IO)#puts>
>> s.method :write
=> #<Method: TCPSocket(IO)#write>
>> s.method :send
=> #<Method: TCPSocket(BasicSocket)#send>
>> s = TCPSocket.new('0.0.0.0', 9911)
=> #<TCPSocket:fd 11>
>> s.method :gets
=> #<Method: TCPSocket(IO)#gets>
>> s.method :read
=> #<Method: TCPSocket(IO)#read>
>> s.method :recv
=> #<Method: TCPSocket(BasicSocket)#recv>

UDP Socket

UDP Server

require 'socket'

server = UDPSocket.new                                  # Start UDP socket
server.bind('0.0.0.0', 9911)                            # Bind all interfaces to port 9911
mesg, addr = server.recvfrom(1024)                      # Receive 1024 bytes of the message and the sender IP
server puts "Hi, UDP Client #{addr}", addr[3], addr[1]  # Send a message to the client 
server.recv(1024)                                       # Receive 1024 bytes of the message

UDP Client

require 'socket'
client = UDPSocket.new
client.connect('localhost', 9911)       # Connect to server on port 991
client.puts "Hi, UDP Server!", 0        # Send message 
server.recv(1024)                       # Receive 1024 bytes of the server message

There alternative for sending and receiving too, figure it out, RubyDoc.

GServer

GServer standard library implements a generic server, featuring thread pool management, simple logging, and multi-server management. Any kind of application-level server can be implemented using this class:

  • It accepts multiple simultaneous connections from clients

  • Several services (i.e. one service per TCP port)

    • can be run simultaneously,

    • can be stopped at any time through the class method GServer.stop(port)

  • All the threading issues are handled

  • All events are optionally logged

  • Very basic GServer

require 'gserver'

class HelloServer < GServer                 # Inherit GServer class
  def serve(io)
    io.puts("What's your name?")
    line = io.gets.chomp
    io.puts "Hi, #{line}!"
    self.stop if io.gets =~ /shutdown/      # Stop the server if you get shutdown string
  end
end

server = HelloServer.new(1234, '0.0.0.0')   # Start the server on port 1234
server.audit = true     # Enable logging
server.start            # Start the service 
server.join

Port knocking

Simple port knocker using the socket standard library.

require 'socket'

ports = [42, 1337, 10420, 6969, 63000]

ports.each do |port|
  puts "[+] Port: #{port}"
  sleep 1
  begin
    s = TCPSocket.new '10.10.70.53', port
    s.close
  rescue Errno::ECONNREFUSED, Errno::EHOSTUNREACH
    next
  end
end

Ref.:

Last updated