ipcd/src/tcpd.cr

130 lines
3.7 KiB
Crystal

require "option_parser"
require "ipc"
require "socket"
require "./colors"
class WrappedTCPFileDescriptor < TCPSocket
# do not close the connection when garbage collected!!
def finalize
# puts "WrappedTCPFileDescriptor garbage collection!!"
# super
end
end
service_name = "tcp"
port_to_listen = 1234
OptionParser.parse do |parser|
parser.on "-p port", "--port port", "Port to listen on." do |port|
port_to_listen = port.to_u16
end
parser.on "-s service-name", "--service-name service-name", "Service name." do |name|
service_name = name
end
parser.on "-h", "--help", "Show this help" do
puts parser
exit 0
end
end
fdlist_client = [] of TCPSocket
fdlist = [] of TCPSocket
server = TCPServer.new("localhost", port_to_listen)
service = IPC::SwitchingService.new service_name
service << server.fd
service.loop do |event|
# TODO: remove closed tcp connections
fdlist_client.select do |x| ! x.closed? end
case event
when IPC::Event::Connection
puts "#{CBLUE}IPC::Event::Connection#{CRESET}"
when IPC::Event::Disconnection
puts "#{CBLUE}IPC::Event::Disconnection#{CRESET}"
when IPC::Event::ExtraSocket
puts "#{CBLUE}IPC::Event::ExtraSocket#{CRESET}"
if server.fd == event.connection.fd
client = server.accept
fdlist << client
service << client.fd
puts "#{CBLUE}new client: #{client.fd}#{CRESET}"
next
end
# since it's an external communication
# we have to read the message here, it's not handled by default in libipc
client = WrappedTCPFileDescriptor.new(fd: event.connection.fd, family: Socket::Family::INET)
message = client.gets
if message.nil?
# disconnection
puts "#{CBLUE}disconnection of client #{event.connection.fd}#{CRESET}"
service.remove_fd event.connection.fd
fdlist.select do |x| x.fd != event.connection.fd end
client.close
else
message.chomp
puts "#{CORANGE}message from client #{client.fd} (#{message.size} bytes): #{message}#{CRESET}"
begin
requested_service = message
newservice = IPC::Connection.new requested_service
service << newservice.fd
service.switch.add event.connection.fd, newservice.fd
client << "OK\n"
rescue e
puts "#{CRED}Exception during connection to the service: #{e}#{CRESET}"
end
# client << message
end
when IPC::Event::Switch
puts "\033[36mIPC::Event::Switch#{CRESET}: from fd #{event.connection.fd}"
# IPC::Event::Message has to be the last entry
# because ExtraSocket and Switch inherit from Message class
when IPC::Event::Message
puts "#{CBLUE}IPC::Event::Message#{CRESET}: #{event.connection.fd}"
puts "\033[33mconnection to the service: #{String.new event.message.payload}\033[00m"
begin
# should be in the format: service-name IP port
payload = String.new event.message.payload
uri = URI.parse payload.chomp
host = uri.host
port = uri.port
host ||= "localhost"
port ||= 9000
requested_service = uri.path.lchop
newservice = TCPSocket.new(host, port)
puts "sending the requested service to the remote tcpd: #{requested_service}"
newservice << "#{requested_service}\n"
puts "waiting for a response from the remote tcpd"
response = newservice.gets
if response.nil?
raise "#{CRED}No response from the tcpd server#{CRESET}"
end
response.chomp
puts "#{CGREEN}response from the remote tcpd: #{CRESET}#{response}"
# TODO: when to remove this? This has to happen, memory leak otherwise
# XXX: hint, check after a select for all dead connections
fdlist_client << newservice
# newservice = IPC::Connection.new requested_service
service << newservice.fd
service.switch.add event.connection.fd, newservice.fd
# service.switch.print
event.connection.send 1.to_u8, "OK"
rescue e
puts "\033[31mException: #{e}"
event.connection.close
end
end
end