new object $daemon: $network;
var $daemon connection = 0;
var $daemon current_port = 0;
var $daemon current_ports = 0;
var $daemon listen = 0;
var $daemon next_connection = 0;
var $root created_on = 809051864;
var $root defined_settings = #[["connection", #[['get, ['get_connection]], ['set, ['set_connection]], ['parse, ['parse_connection_setting]]]], ["listen", #[['get, ['get_listen]], ['set, ['set_listen]], ['parse, ['parse_listen]], ['format, ['format_listen]]]]];
var $root flags = ['methods, 'code, 'core, 'variables];
var $root inited = 1;
var $root managed = [$daemon];
var $root manager = $daemon;
driver method .connect() {
arg remote, local, socket;
var conn;
if ($sys.host_denied(remote)) {
close_connection();
return;
}
if (!valid(next_connection))
next_connection = connection.new_connection();
conn = next_connection;
reassign_connection(conn);
next_connection = connection.new_connection();
conn.start(remote, local, socket, (current_ports[1])[1]);
};
root method .core_daemon() {
.stop_listening();
if (this() != definer())
.set_setting("listen", $daemon, tostr(((.get_setting("listen", $daemon))[1])[1]));
};
public method .current_port() {
return (current_ports[1])[1];
};
public method .current_ports() {
return current_ports;
};
public method .format_listen() {
arg value, @noresolv;
var p, out;
out = [];
for p in (value) {
if (listlen(p) > 1)
out += [((noresolv ? (p[2]) : ($dns.hostname(p[2]))) + ":") + (p[1])];
else
out += [p[1]];
}
return out.join(", ");
};
protected method .get_connection() {
arg @args;
return connection;
};
public method .get_listen() {
arg @args;
return listen || [];
};
public method .parse_connection_setting() {
arg value, @args;
var obj;
obj = (> $object_lib.to_dbref(value) <);
if (!(obj.is($connection)))
throw(~perm, "Connection object must be descended from $connection.");
return obj;
};
public method .parse_listen() {
arg value, @args;
var out, p, m, port, ip;
if (!value)
throw(~parse, "Invalid listen setting, must be a list of ports or host:ports.");
out = map p in (value.explode_english_list()) to ((> .parse_port(p) <));
if (!out)
throw(~parse, "Invalid listen setting, must be a list of ports or host:ports.");
return out;
};
public method .parse_port() {
arg str;
var m, host, port, ip;
if ((m = str.regexp("^([^:]+):(.*)$"))) {
[host, port] = m;
host = host.trim();
port = port.trim();
if (host) {
ip = $dns.ip(host);
if (ip == "-1")
throw(~parse, "Unable to resolv hostname: " + host);
host = ip;
}
} else {
port = str;
host = "";
}
if (!(port.is_numeric()))
throw(~parse, "Invalid port: " + port);
port = toint(port);
if ((port <= 0) || (port > 65535))
throw(~parse, ("Invalid port " + port) + " (out of range, must be 0 .. 65535)");
if (host)
return [port, host];
else
return [port];
};
protected method .set_connection() {
arg name, definer, value, @args;
connection = value;
};
protected method .set_listen() {
arg name, definer, value;
listen = value;
};
public method .shutdown() {
arg @args;
(> .perms(caller(), $sys) <);
(> .stop_listening() <);
};
public method .start_listening() {
arg @ports;
var p, last;
(> .perms(sender()) <);
(| .stop_listening() |);
ports ?= listen;
current_ports = [];
catch any {
for p in (ports) {
last = p;
bind_port(@p);
current_ports += [p];
}
} with {
for p in (current_ports)
(| unbind_port(p[1]) |);
current_ports = [];
throw(error(), (traceback()[1])[2], last);
}
next_connection = connection.new_connection();
};
public method .startup(): forked {
arg @args;
var match, port, rx, str, o, ports, msg;
(> .perms(caller(), 'system) <);
rx = tostr(.objname());
rx = ("-(p)" + substr(rx, 1, ("_" in rx) - 1)) + "=(.*)";
if (find str in (args) where ((match = regexp(str, rx)))) {
[o, ports] = match;
ports = .parse_port(ports);
if (strcmp(o, "p") == 0)
ports = listen + ports;
} else {
ports = listen;
}
catch any {
(> .start_listening(@ports) <);
if (listlen(ports) > 1)
msg = " on ports ";
else
msg = " on port ";
$sys.log(((("** Starting " + this()) + msg) + (.format_listen(ports, 1))) + " **");
} with {
switch (error()) {
case ~bind:
if ((traceback()[1])[3])
$sys.log(("** Unable to bind to port " + ((((traceback()[1])[3]).reverse()).join(":"))) + " **");
else
$sys.log(("** Unable to bind " + this()) + " **");
default:
$sys.log($parse_lib.traceback(traceback()));
}
}
};
public method .stop_listening() {
var p;
(> .perms(sender()) <);
if (valid(connection))
(> connection.daemon_shutdown() <);
(| clear_var('next_connection) |);
for p in (current_ports || [])
(| unbind_port(p[1]) |);
(| clear_var('current_ports) |);
};