Ruby Socket

Lightweight Introduction

Ruby Socket Class Hierarchy

To know the socket hierarchy in ruby here a simple tree explains it.
1
IO # The basis for all input and output in Ruby
2
└── BasicSocket # Abstract base class for all socket classes
3
├── IPSocket # Super class for protocols using the Internet Protocol (AF_INET)
4
│ ├── TCPSocket # Class for Transmission Control Protocol (TCP) sockets
5
│ │ ├── SOCKSSocket # Helper class for building TCP socket servers applications
6
│ │ └── TCPServer # Helper class for building TCP socket servers
7
│ └── UDPSocket # Class for User Datagram Protocol (UDP) sockets
8
├── Socket # Base socket class that mimics that BSD Sockets API. It provides more operating system specific functionality
9
└── UNIXSocket # Class providing IPC using the UNIX domain protocol (AF_UNIX)
10
└── UNIXServer # Helper class for building UNIX domain protocol socket servers
Copied!
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

1
Socket.new(domain, socktype [, protocol])
Copied!
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
1
Client Server
2
| |
3
socket + + socket
4
| |
5
connect +--------, + bind
6
| | |
7
write ,--> +------, | + listen
8
| | | | |
9
read `----+ <--, | `-> + accept
10
| | | |
11
close +--, | `----> + <--, read <--,
12
| | | | |
13
| `--------+----' write ٨
14
| |
15
`----->------>------->----`
Copied!

General Socket usage

Get List of local IPaddreses

1
require 'socket'
2
Socket.ip_address_list
Copied!

Get Hostname

1
Socket.gethostname
Copied!

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
1
require 'socket'
2
3
server = TCPServer.new('0.0.0.0', 9911) # Server, binds/listens all interfaces on port 9911
4
client = server.accept # Wait for client to connect
5
rhost = client.peeraddr.last # peeraddr, returns remote [address_family, port, hostname, numeric_address(ip)]
6
client.puts "Hi TCP Client! #{rhost}" # Send a message to the client once it connect
7
client.gets.chomp # Read incoming message from client
8
client.close # Close the client's connection
9
server.close # Close the TCP Server
Copied!
Note: if you want to list on unused and random port, set to port 0, ruby will find vacancy port then use it. ex.
1
require 'socket'
2
server = TCPServer.new('0.0.0.0', 0)
3
server.addr[1] # Shows the picked port
Copied!

TCP Client

1
require 'socket'
2
3
client = TCPSocket.new('127.0.0.1', 9911) # Client, connects to server on port 9911
4
rhost = client.peeraddr.last # Get the remote server's IP address
5
client.gets.chomp
6
client.puts "Hi, TCP Server #{rhost}"
7
client.close
Copied!
You can put timeout/time interval for current connection in-case the server's response get delayed and the socket is still open.
1
timeval = [3, 0].pack("l_2") # Time interval 3 seconds
2
client.setsockopt Socket::SOL_SOCKET, Socket::SO_RCVTIMEO, timeval # Set socket receiving time interval
3
client.setsockopt Socket::SOL_SOCKET, Socket::SO_SNDTIMEO, timeval # Set socket sending time interval
4
client.getsockopt(Socket::SOL_SOCKET, Socket::SO_RCVTIMEO).inspect # Optional, Check if socket option has been set
5
client.getsockopt(Socket::SOL_SOCKET, Socket::SO_SNDTIMEO).inspect # Optional, Check if socket option has been set
Copied!
There are some alternatives for puts and gets methods.You can see the difference and its classes using method method in Pry interpreter console
1
>> s = TCPSocket.new('0.0.0.0', 9911)
2
=> #<TCPSocket:fd 11>
3
>> s.method :puts
4
=> #<Method: TCPSocket(IO)#puts>
5
>> s.method :write
6
=> #<Method: TCPSocket(IO)#write>
7
>> s.method :send
8
=> #<Method: TCPSocket(BasicSocket)#send>
Copied!
1
>> s = TCPSocket.new('0.0.0.0', 9911)
2
=> #<TCPSocket:fd 11>
3
>> s.method :gets
4
=> #<Method: TCPSocket(IO)#gets>
5
>> s.method :read
6
=> #<Method: TCPSocket(IO)#read>
7
>> s.method :recv
8
=> #<Method: TCPSocket(BasicSocket)#recv>
Copied!

UDP Socket

UDP Server

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

UDP Client

1
require 'socket'
2
client = UDPSocket.new
3
client.connect('localhost', 9911) # Connect to server on port 991
4
client.puts "Hi, UDP Server!", 0 # Send message
5
server.recv(1024) # Receive 1024 bytes of the server message
Copied!
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
1
require 'gserver'
2
3
class HelloServer < GServer # Inherit GServer class
4
def serve(io)
5
io.puts("What's your name?")
6
line = io.gets.chomp
7
io.puts "Hi, #{line}!"
8
self.stop if io.gets =~ /shutdown/ # Stop the server if you get shutdown string
9
end
10
end
11
12
server = HelloServer.new(1234, '0.0.0.0') # Start the server on port 1234
13
server.audit = true # Enable logging
14
server.start # Start the service
15
server.join
Copied!

Port knocking

Simple port knocker using the socket standard library.
1
require 'socket'
2
3
ports = [42, 1337, 10420, 6969, 63000]
4
5
ports.each do |port|
6
puts "[+] Port: #{port}"
7
sleep 1
8
begin
9
s = TCPSocket.new '10.10.70.53', port
10
s.close
11
rescue Errno::ECONNREFUSED, Errno::EHOSTUNREACH
12
next
13
end
14
end
Copied!
Ref.: