147 lines
4.4 KiB
Crystal
147 lines
4.4 KiB
Crystal
|
|
require "./rules"
|
|
require "./networkdcliparser"
|
|
require "ipc"
|
|
require "uri"
|
|
|
|
class IPC::NetworkD < IPC::Service
|
|
|
|
# The client asks to networkd to open a connection to a service
|
|
# the service name is used unless there is a redirection that is provided to the client through
|
|
# a IPC_NETWORK environment variable
|
|
# This environment variable is sent from the client to networkd, that's what is parsed here
|
|
|
|
# parse_lookup_payload extract the right service URI to use from the IPC_NETWORK content
|
|
# sent by the client in the form:
|
|
# `requested-service-name;service1 uri;service2 uri;...` etc.
|
|
def self.parse_lookup_payload (payload : String) : URI
|
|
items = payload.split (";")
|
|
requested_service_name = items.delete_at(0).chomp()
|
|
requested_service = URI.parse "unix:///#{requested_service_name}"
|
|
|
|
services_redirections = {} of String => URI
|
|
|
|
# from each item (separated by a semicolon), get the service name and the new uri to use
|
|
# format: `service-name uri;service-name uri`
|
|
# uri can be:
|
|
# * distant service (with protocol to use): https://some.example.com/pong
|
|
# * local service: "local:newpong" or simply "newpong"
|
|
items.each do |item|
|
|
x = /([^ ]+) ([^ ]+)/.match(item)
|
|
unless x.nil?
|
|
service, newuri = x.captures()
|
|
if service.nil?
|
|
next
|
|
elsif newuri.nil?
|
|
next
|
|
end
|
|
|
|
puts "service: #{service} redirection uri: #{newuri}"
|
|
uri = URI.parse newuri
|
|
services_redirections[service] = uri
|
|
end
|
|
end
|
|
|
|
services_redirections.each do |k, v|
|
|
puts "\033[36mpossible redirection (from env. var.):\033[00m service: #{k} uri: #{v}"
|
|
if k == requested_service_name
|
|
requested_service = v
|
|
end
|
|
end
|
|
|
|
requested_service
|
|
end
|
|
|
|
# XXX: WIP
|
|
def service_lookup (message : IPC::Message, origin : IPC::Connection)
|
|
payload = String.new message.payload
|
|
|
|
requested_service = IPC::NetworkD.parse_lookup_payload payload
|
|
|
|
# TODO: connect to the service then provide the file descriptor to the client
|
|
begin
|
|
scheme = requested_service.scheme
|
|
if scheme.nil?
|
|
raise "no SCHEME in redirection"
|
|
end
|
|
|
|
service_name = scheme
|
|
if scheme == "unix"
|
|
# scheme == unix => simple redirection
|
|
|
|
# the URI is "unix:///service" so the path is "/service"
|
|
# first, remove its slash prefix
|
|
service_name = requested_service.path.lchop
|
|
end
|
|
|
|
puts "service name: #{service_name}"
|
|
service = IPC::Connection.new service_name
|
|
|
|
# TODO: for remote services, we have to connect to communication service
|
|
# these communication services need an URI to work on
|
|
# The protocol:
|
|
# networkd sends an URI to the communication service, which responds with a "OK" message
|
|
if scheme != "unix"
|
|
service.send 1.to_u8, "#{requested_service.to_s}\n"
|
|
response = service.read
|
|
payload = String.new response.payload
|
|
if payload.chomp != "OK"
|
|
raise "service #{service_name} response was #{payload.chomp}"
|
|
end
|
|
end
|
|
|
|
# Then we provide the file descriptor to the client
|
|
r = LibIPC.ipc_provide_fd(origin.fd, service.fd)
|
|
if r != 0
|
|
m = String.new LibIPC.ipc_errors_get (r)
|
|
raise Exception.new "cannot send the file descriptor of the requested service: #{m}"
|
|
end
|
|
|
|
# finally, the service should be closed in networkd
|
|
service.close
|
|
rescue e
|
|
puts "\033[31mException during the connection to the requested service #{requested_service}: #{e}\033[00m"
|
|
# when a problem occurs, close the client connection
|
|
|
|
begin
|
|
# LibIPC.ipc_connections_print pointerof(@connections)
|
|
remove_fd origin.fd
|
|
|
|
origin.close
|
|
|
|
rescue ex
|
|
puts "\033[31mException during a client removal: #{ex}\033[00m"
|
|
end
|
|
end
|
|
end
|
|
|
|
def wait_event(server : IPC::Connection?, &block) : Tuple(LibIPC::EventType, IPC::Message, IPC::Connection)
|
|
event = LibIPC::Event.new
|
|
|
|
# TODO: networkd should be able to transfer messages???
|
|
r = LibIPC.ipc_wait_event self.pointer, @service_info.pointer, pointerof(event)
|
|
if r != 0
|
|
m = String.new LibIPC.ipc_errors_get (r)
|
|
yield IPC::Exception.new "error waiting for a new event: #{m}"
|
|
end
|
|
|
|
connection = IPC::Connection.new event.origin.unsafe_as(Pointer(LibIPC::Connection)).value
|
|
message = event.message.unsafe_as(Pointer(LibIPC::Message))
|
|
|
|
return event.type, IPC::Message.new(message), connection
|
|
end
|
|
end
|
|
|
|
class NetworkD < IPC::NetworkD
|
|
@rules = RuleSet.new
|
|
@redirections = RedirectionSet.new
|
|
|
|
def parse_cli (argv : Array(String))
|
|
NetworkDCLIParser.parse_rules argv, @rules, @redirections
|
|
end
|
|
|
|
def to_s
|
|
@rules.to_s + "\n" + @redirections.to_s
|
|
end
|
|
end
|