networkd: v0.1
commit
02b1acc040
|
@ -0,0 +1,103 @@
|
||||||
|
|
||||||
|
Networkd is a program to handle networking for all other software.
|
||||||
|
|
||||||
|
# WARNING
|
||||||
|
|
||||||
|
Security is TBD. Currently, only TCPd is implemented, which means no communication security.
|
||||||
|
|
||||||
|
# Networkd functionalities
|
||||||
|
|
||||||
|
## firewall
|
||||||
|
|
||||||
|
`Networkd` has to filter the connections to local services.
|
||||||
|
|
||||||
|
```Warning
|
||||||
|
WIP.
|
||||||
|
```
|
||||||
|
|
||||||
|
## authentication
|
||||||
|
|
||||||
|
`Networkd` has to authenticate clients asking for a service.
|
||||||
|
|
||||||
|
```Warning
|
||||||
|
WIP.
|
||||||
|
```
|
||||||
|
|
||||||
|
## redirection
|
||||||
|
|
||||||
|
Central networking management allows for functionalities such as redirections.
|
||||||
|
For example, a local client asking for the authentication can be authenticated with a distant authentication service.
|
||||||
|
|
||||||
|
## encapsulation
|
||||||
|
|
||||||
|
```Warning
|
||||||
|
TBD. WIP.
|
||||||
|
```
|
||||||
|
|
||||||
|
|
||||||
|
# Configuration
|
||||||
|
|
||||||
|
Configuration is yet to be defined.
|
||||||
|
|
||||||
|
* redirection
|
||||||
|
* firewall
|
||||||
|
* authentication
|
||||||
|
|
||||||
|
# Usage
|
||||||
|
|
||||||
|
This program can be used as follow:
|
||||||
|
|
||||||
|
```sh
|
||||||
|
# with some static rules
|
||||||
|
networkd --allow in authd tls:example.com --deny in * * --allow out pong tls:pong.example.com:9000
|
||||||
|
networkd --redirect authd nextversion-authd
|
||||||
|
```
|
||||||
|
|
||||||
|
## usage examples
|
||||||
|
|
||||||
|
`networkd` is requested each time a client is launched when the right environment variable is used.
|
||||||
|
For example, we want to connect to a distant `authd` service:
|
||||||
|
|
||||||
|
IPC_NETWORKD="authd tls://user@passwd:example.com:9000/authd"
|
||||||
|
|
||||||
|
|
||||||
|
```Warning
|
||||||
|
Currently, the networkd only works with tcp and unix routes.
|
||||||
|
```
|
||||||
|
|
||||||
|
IPC_NETWORKD="pongd tcp://example.com:9000/pongd"
|
||||||
|
|
||||||
|
# Changelog
|
||||||
|
|
||||||
|
* v0.1: (current) networkd (redirections), tcpd
|
||||||
|
|
||||||
|
* `networkd` understands URIs (`tcp://example.com/service` or `unix:///service`)
|
||||||
|
* `tcp` scheme is understood: `networkd` contacts the `tcpd` service
|
||||||
|
* `unix` scheme is understood: `networkd` performs a redirection
|
||||||
|
|
||||||
|
|
||||||
|
# Roadmap
|
||||||
|
|
||||||
|
|
||||||
|
* v0.2: webipcd, documentation
|
||||||
|
* v0.3: firewall + redirections
|
||||||
|
* v0.4: static configuration: default routes, authentication
|
||||||
|
* v0.5: tlsd built-in, pre-shared keys
|
||||||
|
* v0.6: udpd
|
||||||
|
* v1.0: TBD
|
||||||
|
|
||||||
|
|
||||||
|
# Networkd explanations
|
||||||
|
|
||||||
|
1. client contacts `networkd`
|
||||||
|
1. `networkd` understand the request from the client then contacts the local service responsible for the communication protocol required
|
||||||
|
1. once the distant connection is established (between the two `tlsd` services for example) `networkd` provides a file descriptor to the client
|
||||||
|
1. finally, the client can perform requests to the distant service transparently
|
||||||
|
|
||||||
|
during the connection:
|
||||||
|
|
||||||
|
client <-> networkd <-> tlsd <=> tlsd <-> networkd <-> service
|
||||||
|
|
||||||
|
then:
|
||||||
|
|
||||||
|
client <-> tlsd <=> tlsd <-> server
|
|
@ -0,0 +1,34 @@
|
||||||
|
name: networkd
|
||||||
|
version: 0.1.0
|
||||||
|
|
||||||
|
authors:
|
||||||
|
- karchnu <karchnu@karchnu.fr>
|
||||||
|
|
||||||
|
description: |
|
||||||
|
Short description of networkd
|
||||||
|
|
||||||
|
dependencies:
|
||||||
|
ipc:
|
||||||
|
git: https://git.karchnu.fr/JunkOS/ipc.cr
|
||||||
|
branch: idontknowwhatimdoinglol
|
||||||
|
|
||||||
|
targets:
|
||||||
|
pongc:
|
||||||
|
main: src/pongc.cr
|
||||||
|
pongd:
|
||||||
|
main: src/pongd.cr
|
||||||
|
networkd:
|
||||||
|
main: src/main.cr
|
||||||
|
tcpd:
|
||||||
|
main: src/tcpd.cr
|
||||||
|
tcp:
|
||||||
|
main: src/tcp.cr
|
||||||
|
|
||||||
|
# webipc:
|
||||||
|
# main: src/webipcd.cr
|
||||||
|
# websocketclient:
|
||||||
|
# main: src/websocket-client.cr
|
||||||
|
# websocketserver:
|
||||||
|
# main: src/websocket-server.cr
|
||||||
|
|
||||||
|
license: ISC
|
|
@ -0,0 +1,5 @@
|
||||||
|
CRED = "\033[31m"
|
||||||
|
CBLUE = "\033[36m"
|
||||||
|
CGREEN = "\033[32m"
|
||||||
|
CRESET = "\033[00m"
|
||||||
|
CORANGE = "\033[33m"
|
|
@ -0,0 +1,34 @@
|
||||||
|
require "./networkd"
|
||||||
|
|
||||||
|
networkd = NetworkD.new "network"
|
||||||
|
|
||||||
|
# --deny <in|out> <service> <url>
|
||||||
|
# --allow <in|ou> <service> <url>
|
||||||
|
# --redirect <service> <service>
|
||||||
|
# --redirect <service-name> <new-service-name> <origin-url> <dest-url>
|
||||||
|
|
||||||
|
networkd.parse_cli ARGV
|
||||||
|
puts networkd.to_s
|
||||||
|
|
||||||
|
networkd.loop do |event|
|
||||||
|
puts "there is an event!"
|
||||||
|
case event
|
||||||
|
when IPC::Event::Connection
|
||||||
|
puts "\033[32mConnection:\033[00m fd #{event.connection.fd}"
|
||||||
|
when IPC::Event::Disconnection
|
||||||
|
puts "\033[32mDisconnection:\033[00m fd #{event.connection.fd}"
|
||||||
|
when IPC::Event::ExtraSocket
|
||||||
|
puts "\033[32mExtrasocket:\033[00m fd #{event.connection.fd}"
|
||||||
|
when IPC::Event::Switch
|
||||||
|
puts "\033[31mSwitch\033[00m"
|
||||||
|
when IPC::Exception
|
||||||
|
puts "\033[31mException\033[00m"
|
||||||
|
when IPC::Event::Message
|
||||||
|
puts "\033[32mthere is a message\033[00m"
|
||||||
|
puts event.message.to_s
|
||||||
|
|
||||||
|
networkd.service_lookup event.message, event.connection
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
# pp! rules.authorized? Rule::Direction::In, "authd", "tls:192.168.0.42"
|
|
@ -0,0 +1,146 @@
|
||||||
|
|
||||||
|
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
|
|
@ -0,0 +1,38 @@
|
||||||
|
|
||||||
|
class NetworkDCLIParser
|
||||||
|
def self.pack_args (argv : Array(String))
|
||||||
|
last_flag = nil : String?
|
||||||
|
|
||||||
|
argv.chunks do |x|
|
||||||
|
if x[0..1] == "--"
|
||||||
|
last_flag = x
|
||||||
|
end
|
||||||
|
|
||||||
|
last_flag
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def self.parse_rules (argv : Array(String), rules : RuleSet, redirections : RedirectionSet)
|
||||||
|
args = NetworkDCLIParser.pack_args argv
|
||||||
|
|
||||||
|
args.each do |flag, parameters|
|
||||||
|
# puts "flag: #{flag}, params: #{parameters.join(' ')}"
|
||||||
|
if flag == "--allow" || flag == "--deny"
|
||||||
|
parameters[3] # will crash if non-existant
|
||||||
|
|
||||||
|
rules << Rule.from_args parameters
|
||||||
|
elsif flag == "--redirect"
|
||||||
|
if parameters.size == 3
|
||||||
|
redirections << Redirection.new parameters[1], parameters[2]
|
||||||
|
elsif parameters.size == 5
|
||||||
|
raise "--redirect with 4 parameters not implemented, yet"
|
||||||
|
else
|
||||||
|
raise "--redirect <service> <newservice> [<origin-url> <dest-url>]"
|
||||||
|
end
|
||||||
|
else
|
||||||
|
raise "oh no"
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
|
@ -0,0 +1,35 @@
|
||||||
|
require "ipc"
|
||||||
|
|
||||||
|
client = IPC::Client.new("pong")
|
||||||
|
|
||||||
|
# client.send(LibIPC::MessageType::Data, 42, "salut ça va ?")
|
||||||
|
client.send(42.to_u8, "salut ça va ?")
|
||||||
|
|
||||||
|
# # client.send(LibIPC::MessageType::Data, 42, "salut ça va ?")
|
||||||
|
# m = client.read
|
||||||
|
#
|
||||||
|
# puts "message received: #{m.to_s}"
|
||||||
|
#
|
||||||
|
# sleep 1
|
||||||
|
#
|
||||||
|
# # client.send(LibIPC::MessageType::Data, 42, "salut ça va ?")
|
||||||
|
# client.send(42.to_u8, "autre truc")
|
||||||
|
|
||||||
|
# # client.send(LibIPC::MessageType::Data, 42, "salut ça va ?")
|
||||||
|
# m = client.read
|
||||||
|
#
|
||||||
|
# puts "message received: #{m.to_s}"
|
||||||
|
#
|
||||||
|
# sleep 1
|
||||||
|
#
|
||||||
|
# client.close
|
||||||
|
|
||||||
|
client.loop do |event|
|
||||||
|
case event
|
||||||
|
when IPC::Event::Message
|
||||||
|
puts "\033[32mthere is a message\033[00m"
|
||||||
|
puts event.message.to_s
|
||||||
|
client.close
|
||||||
|
exit
|
||||||
|
end
|
||||||
|
end
|
|
@ -0,0 +1,15 @@
|
||||||
|
require "ipc"
|
||||||
|
require "./colors"
|
||||||
|
|
||||||
|
IPC::Service.new ("pong") do |event|
|
||||||
|
case event
|
||||||
|
when IPC::Event::Connection
|
||||||
|
puts "#{CBLUE}IPC::Event::Connection#{CRESET}, client: #{event.connection.fd}"
|
||||||
|
when IPC::Event::Disconnection
|
||||||
|
puts "#{CBLUE}IPC::Event::Disconnection#{CRESET}, client: #{event.connection.fd}"
|
||||||
|
when IPC::Event::Message
|
||||||
|
puts "#{CGREEN}IPC::Event::Message#{CRESET}"
|
||||||
|
puts event.message.to_s
|
||||||
|
event.connection.send event.message
|
||||||
|
end
|
||||||
|
end
|
|
@ -0,0 +1,85 @@
|
||||||
|
|
||||||
|
class Redirection
|
||||||
|
property origin : String
|
||||||
|
property destination : String
|
||||||
|
|
||||||
|
property originurl : String | Nil
|
||||||
|
property destinationurl : String | Nil
|
||||||
|
|
||||||
|
def initialize (@origin, @destination)
|
||||||
|
end
|
||||||
|
|
||||||
|
def initialize (@origin, @destination, @originurl, @destinationurl)
|
||||||
|
end
|
||||||
|
|
||||||
|
def to_s
|
||||||
|
"Redirection #{origin} #{destination}"
|
||||||
|
# if @originurl
|
||||||
|
# "#{origin} #{destination}"
|
||||||
|
# else
|
||||||
|
# "#{origin} #{destination} #{originurl} #{destinationurl}"
|
||||||
|
# end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
class RedirectionSet < Array(Redirection)
|
||||||
|
def to_s
|
||||||
|
map(&.to_s).join("\n")
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
class Rule
|
||||||
|
enum Type
|
||||||
|
Allow
|
||||||
|
Deny
|
||||||
|
end
|
||||||
|
|
||||||
|
enum Direction
|
||||||
|
In
|
||||||
|
Out
|
||||||
|
end
|
||||||
|
|
||||||
|
getter service : String
|
||||||
|
getter url : Regex
|
||||||
|
getter type : Type
|
||||||
|
getter direction : Direction
|
||||||
|
|
||||||
|
def initialize (@type, @direction, @service, @url)
|
||||||
|
end
|
||||||
|
|
||||||
|
def matches_service?(service : String)
|
||||||
|
@service == "*" || @service == service
|
||||||
|
end
|
||||||
|
|
||||||
|
def matches_uri?(uri)
|
||||||
|
!@url.match(uri).nil?
|
||||||
|
end
|
||||||
|
|
||||||
|
def self.from_args(args : Array(String))
|
||||||
|
Rule.new(
|
||||||
|
Type.parse(args[0][2..]),
|
||||||
|
Rule::Direction.parse(args[1]),
|
||||||
|
args[2],
|
||||||
|
Regex.new args[3]
|
||||||
|
)
|
||||||
|
end
|
||||||
|
|
||||||
|
def to_s
|
||||||
|
"#{type} #{direction} #{service} #{url}"
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
class RuleSet < Array(Rule)
|
||||||
|
def authorized?(direction : Rule::Direction, service : String, uri : String) : Bool
|
||||||
|
self.select(&.direction.==(direction))
|
||||||
|
.select(&.matches_service?(service))
|
||||||
|
.select(&.matches_uri?(uri))
|
||||||
|
.[0]?.try &.type.allow? || false
|
||||||
|
end
|
||||||
|
|
||||||
|
def to_s
|
||||||
|
map(&.to_s).join("\n")
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,51 @@
|
||||||
|
require "ipc"
|
||||||
|
require "option_parser"
|
||||||
|
|
||||||
|
|
||||||
|
service_name = "tcp"
|
||||||
|
port_to_listen = 1234
|
||||||
|
hostname = "localhost"
|
||||||
|
requested_service_name = "pong"
|
||||||
|
|
||||||
|
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 "-h hostname", "--host-name hostname", "Hostname." do |name|
|
||||||
|
hostname = name
|
||||||
|
end
|
||||||
|
|
||||||
|
parser.on "-s service-name", "--service-name service-name", "Service name." do |name|
|
||||||
|
service_name = name
|
||||||
|
end
|
||||||
|
|
||||||
|
parser.on "-r requested-service-name", "--requested-service-name requested-service-name", "Requested service name." do |name|
|
||||||
|
requested_service_name = name
|
||||||
|
end
|
||||||
|
|
||||||
|
parser.on "-h", "--help", "Show this help" do
|
||||||
|
puts parser
|
||||||
|
exit 0
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
|
service = IPC::Client.new service_name
|
||||||
|
|
||||||
|
# 1. send service name
|
||||||
|
service.send 1.to_u8, "tcp://#{hostname}:#{port_to_listen}/#{requested_service_name}"
|
||||||
|
|
||||||
|
# 2. receive "OK"
|
||||||
|
message = service.read
|
||||||
|
puts "message read: #{String.new message.payload}"
|
||||||
|
|
||||||
|
# 3. sending a message to the pong service
|
||||||
|
puts "sending 'coucou' to the pong service"
|
||||||
|
service.send 2.to_u8, "coucou"
|
||||||
|
|
||||||
|
# 4. receiving a response
|
||||||
|
message = service.read
|
||||||
|
puts "message read: #{String.new message.payload}"
|
||||||
|
|
||||||
|
service.close
|
|
@ -0,0 +1,129 @@
|
||||||
|
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
|
Reference in New Issue