2009-01-26 20:12:53 +00:00
|
|
|
// $Id: connect.c,v 1.54 2008/12/10 22:53:33 lynx Exp $ // vim:syntax=lpc
|
2009-01-26 19:21:29 +00:00
|
|
|
//
|
|
|
|
// net/connect: generic handler for active connections
|
|
|
|
// most methods are intended for overloading except for the connect2()
|
|
|
|
//
|
2009-01-26 20:12:53 +00:00
|
|
|
|
|
|
|
// local debug messages - turn them on by using psyclpc -DDconnect=<level>
|
|
|
|
#ifdef Dconnect
|
|
|
|
# undef DEBUG
|
|
|
|
# define DEBUG Dconnect
|
|
|
|
#endif
|
|
|
|
|
2009-01-26 19:21:29 +00:00
|
|
|
#include <net.h>
|
|
|
|
#include <errno.h>
|
|
|
|
|
|
|
|
virtual inherit NET_PATH "trust";
|
|
|
|
|
|
|
|
volatile mixed is_connecting;
|
|
|
|
|
2011-05-03 08:09:38 +00:00
|
|
|
connect(host, port, transport, srv, extra);
|
2009-01-26 19:21:29 +00:00
|
|
|
|
|
|
|
protected int block() { destruct(ME); return 0; }
|
|
|
|
|
|
|
|
protected connect_failure(mc, text) {
|
|
|
|
is_connecting = 0;
|
2009-04-20 06:18:13 +00:00
|
|
|
P1(("connect failure %O in %O, %O.\n", mc, ME, text))
|
2009-01-26 19:21:29 +00:00
|
|
|
monitor_report("_failure_network_connect"+ mc,
|
|
|
|
object_name(ME) +" · "+ text);
|
|
|
|
}
|
|
|
|
|
|
|
|
// psyc circuits call this "manually" via psyc/active
|
|
|
|
protected int logon(int failure) {
|
|
|
|
if (is_connecting == "s") {
|
|
|
|
is_connecting = 0;
|
2009-01-26 20:12:53 +00:00
|
|
|
#if __EFUN_DEFINED__(tls_init_connection)
|
|
|
|
P2(("%O connected to %O from %O. TLS requested.\n", ME,
|
2009-01-26 19:21:29 +00:00
|
|
|
query_ip_number(ME), query_mud_port(ME)))
|
|
|
|
tls_init_connection(ME, #'logon);
|
2009-01-26 20:12:53 +00:00
|
|
|
#else
|
|
|
|
connect_failure("_unsafe", "security not available");
|
|
|
|
return 0;
|
|
|
|
#endif
|
2009-01-26 19:21:29 +00:00
|
|
|
}
|
|
|
|
is_connecting = 0;
|
|
|
|
if (failure == -1 || !interactive(ME)) {
|
2009-04-20 06:18:13 +00:00
|
|
|
P3(("Warning: Failed connect attempt in %O\n", ME))
|
2009-01-26 19:21:29 +00:00
|
|
|
#if __EFUN_DEFINED__(errno)
|
|
|
|
connect_failure("_attempt", sprintf("connect failed: errno %s",
|
|
|
|
errno()));
|
|
|
|
#else
|
|
|
|
connect_failure("_attempt", "connect failed");
|
|
|
|
#endif
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
P2(("%O connected to %O from %O\n", ME,
|
|
|
|
query_ip_number(ME), query_mud_port(ME)))
|
|
|
|
|
|
|
|
// this used to catch connections being set up while a shutdown
|
|
|
|
// has been initiated in the meantime, but it was doing the wrong
|
|
|
|
// things to get there, so that's changed. we should put the connection
|
|
|
|
// on hold in that case. we need to implement daemon restart behaviour
|
|
|
|
// in psyclpc and as we do that we might aswell provide an efun to
|
|
|
|
// query shutdown progress TODO
|
|
|
|
#if __EFUN_DEFINED__(query_shutdown_progress)
|
|
|
|
// go on hold maybe?
|
|
|
|
if (query_shutdown_progress()) return 0;
|
|
|
|
#endif
|
|
|
|
unless (hostCheck(query_ip_number(ME), query_mud_port(ME)))
|
|
|
|
return block();
|
2009-01-26 20:12:53 +00:00
|
|
|
#if __EFUN_DEFINED__(enable_telnet)
|
|
|
|
enable_telnet(0, ME);
|
|
|
|
#endif
|
2009-01-26 19:21:29 +00:00
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
// seems to me all of this could as well happen in library/dns?
|
|
|
|
// any use for it?
|
|
|
|
protected canonical_host(cane, ip, host) {
|
|
|
|
string lh;
|
|
|
|
|
|
|
|
register_host(ip, cane);
|
|
|
|
register_host(host, cane);
|
|
|
|
lh = lower_case(host); // the host we are given is probably
|
|
|
|
// lc'd already..
|
|
|
|
if (lh != host) register_host(lh, cane);
|
|
|
|
unless (intp(cane)) {
|
|
|
|
register_host(cane, cane);
|
|
|
|
lh = lower_case(cane);
|
|
|
|
if (lh != cane) register_host(lh, cane);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2011-05-03 08:09:38 +00:00
|
|
|
private connect2(ip, port, host, extra) {
|
2009-01-26 19:21:29 +00:00
|
|
|
int rc;
|
|
|
|
|
2009-01-26 20:12:53 +00:00
|
|
|
P3(("%O connect2(%O, %O, %O) == %O\n", ME, ip, port, host, chost(ip)))
|
2009-01-26 19:21:29 +00:00
|
|
|
unless (stringp(ip)) {
|
2011-05-03 08:09:38 +00:00
|
|
|
if (sizeof(extra) && stringp(extra)) {
|
|
|
|
if (sscanf(extra, "%s:%d;%s", host, port, extra) == 3) {
|
|
|
|
P3(("fallback: %s:%d, other %O\n", host, port, extra))
|
|
|
|
is_connecting = 0;
|
2011-05-19 15:41:48 +00:00
|
|
|
// why 10 seconds here? this can be improved
|
2011-05-03 08:09:38 +00:00
|
|
|
call_out(#'connect, 10, host, port, 1, extra == "" ? 0 : extra);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
}
|
2009-01-26 19:21:29 +00:00
|
|
|
connect_failure("_resolve", host+" does not resolve");
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
// why are we checking the port _after_ dns resolution?
|
|
|
|
if (port <= 0) {
|
|
|
|
// similar message in psyc/library.i
|
|
|
|
connect_failure("_invalid_port_connect",
|
|
|
|
"no connectable port given");
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
if (interactive()) return -8;
|
|
|
|
unless (hostCheck(ip, port)) {
|
|
|
|
P2(("%O stopped connect attempt to %O on %O\n", ME, ip, port))
|
|
|
|
connect_failure("_block", "Blocked by your server policy.");
|
|
|
|
destruct(ME);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
#if __EFUN_DEFINED__(net_connect)
|
2009-01-26 20:12:53 +00:00
|
|
|
P3(("REALLY calling net_connect(%O, %O)\n", ip, port))
|
2009-01-26 19:21:29 +00:00
|
|
|
rc = net_connect(ip, port);
|
|
|
|
switch(rc) {
|
|
|
|
case 0:
|
2009-01-26 20:12:53 +00:00
|
|
|
P3(("%O connecting(%O, %O, %O) == %O\n",
|
2009-01-26 19:21:29 +00:00
|
|
|
ME, ip, port, host, chost(ip)))
|
|
|
|
break;
|
|
|
|
case EMFILE:
|
|
|
|
P1(("EMFILE. resubmitting connect to %s:%O\n", host, port))
|
|
|
|
#if 0 // better solution, but can lead to race conditions. ok, kind of race
|
|
|
|
// conditions, so all in all it's worse. maybe.
|
|
|
|
// circuit.c is supposed to handle reconnect attempts
|
|
|
|
// but this internal failure is probably better solved
|
|
|
|
// by immediate resubmission of the connect request
|
|
|
|
// (2 seconds = one cycle) rather than treating it like
|
|
|
|
// a connect_failure.
|
|
|
|
// why aren't we calling connect2() here?
|
|
|
|
call_out((: is_connecting = 0;
|
|
|
|
connect($1, $2); return; :), 2, host, port);
|
|
|
|
// wenn outgoing connections in ldmud's comm.c is raised or
|
|
|
|
// even dynamic, EMFILE should simply no longer happen.
|
|
|
|
#else
|
|
|
|
// this should work too, except for one-time tcp connections
|
|
|
|
// like net/http/fetch. the advantage of this approach is
|
|
|
|
// that a real EMFILE, when incoming descriptors are exhausted,
|
|
|
|
// does not cause an obnoxious call_out loop.
|
|
|
|
connect_failure("_attempt_exceeded",
|
|
|
|
"Too many pending connections right now. Trying again later.");
|
|
|
|
#endif
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
connect_failure("_call", "connect to "+ip+" returns "+ rc);
|
|
|
|
}
|
|
|
|
#else
|
|
|
|
P0(("%O connect to %O, %O, %O stopped:\n\t\
|
|
|
|
Driver does not provide net_connect()\n", ME, ip, port, host))
|
|
|
|
#endif
|
|
|
|
// ldmud says we sometimes get here even if we got destructed *shrug*
|
|
|
|
if (ME && !chost(ip)) dns_rresolve(ip, #'canonical_host, ip, host);
|
|
|
|
}
|
|
|
|
|
2011-05-03 08:09:38 +00:00
|
|
|
connect(host, port, transport, extra) {
|
2009-01-26 19:21:29 +00:00
|
|
|
P4(("%O connect:connect(%O, %O, %O)\n", ME, host,port,transport))
|
|
|
|
if (interactive() || !host || !port || is_connecting) return -8;
|
|
|
|
is_connecting = transport || 1;
|
|
|
|
P3(("%O connect:connect(%O, %O, %O). resolving host.\n", ME,
|
|
|
|
host,port,transport))
|
|
|
|
// even on reconnect we don't cache the dns host data as it
|
|
|
|
// may be a dynamic dns host currently rebooting..
|
2011-05-03 08:09:38 +00:00
|
|
|
dns_resolve(host, #'connect2, port, host, extra);
|
2009-01-26 19:21:29 +00:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
disconnected(remaining) {
|
2009-01-26 20:12:53 +00:00
|
|
|
P2(("%O got disconnected(%O). it was %s connected.\n", ME, remaining,
|
|
|
|
query_once_interactive(ME) ? "once" : "never"))
|
2009-01-26 19:21:29 +00:00
|
|
|
connect_failure("_disconnect", "lost connection");
|
|
|
|
return 0; // unexpected
|
|
|
|
}
|
|
|
|
|