blob: 7608d8fc3d4621af14be46f03d48c9e4804b6980 [file] [log] [blame]
/*
* Mudlibseite des iplookup systems, mit dem aus numerischen IP Adressen
* der Hostname und der Ort, an dem sich der Rechner befinden berechnet wird.
* Ergebnisse werden gecachet.
*
* (c)2010 rumata @ morgengrauen
*/
#pragma strict_types,save_types
#pragma no_clone,no_shadow
// Format der ipmap:
// Key: numerische ip als string (ohne fuehrende nullen)
// Data:
// [ip,0] Hostname, der vom externen Server geliefert wurde.
// [ip,1] Locationinformation, die vom externen Server kommt.
// dieser sollte den Ort, oder falls es nicht besser geht, das Land
// im Klartext liefern.
// [ip,2] Zeitstempel, bei dessen erreichen der Wert geloescht werden
// soll.
// [ip,3] Status der Anfrage
private mapping ipmap;
#define IPMAP_HOST 0
#define IPMAP_CITY 1
#define IPMAP_EXPIRE 2
#define IPMAP_STATUS 3
// externer server, der der lokationdaten berechnet
#define IPLOOKUP_HOST "127.0.0.1"
#define IPLOOKUP_PORT 8711
#define IPLOOKUP_SAVE "/p/daemon/save/iplookup"
// moegliche statuswerte (platz zum zaehlen der anfragen gelassen)
#define STATUS_QUERYING 1
#define STATUS_AUTHORITATIVE 128
// so lange werden die daten im cache gehalten
#define CACHE_TIME 86400 /* 1 day */
// Ab dieser Groesse betrache ich den cache als "gross genug",
// um einen cache update in etappen zu rechtfertigen.
#define LARGE_CACHE_LIMIT 2000
// so lange sind wir bereit auf die antwort einer anfrage beim server zu warten
#define QUERY_TIME 60 /* 1 min */
#include <config.h>
#if MUDNAME == "MorgenGrauen"
#define LOG(x) log_file("rumata/iplookup.log",strftime("%Y-%m-%d %H:%M:%S: ")+(x)+"\n")
#else
#define LOG(x)
#endif
// PROTOTYPES
public void create();
public void reset();
static int active_entry( string key, mixed* data );
static void expire_cache_small();
static void expire_cache_large( string* keys );
public void update( string udp_reply );
// IMPLEMENTATION
public void create() {
seteuid(getuid());
restore_object( IPLOOKUP_SAVE );
if( !mappingp(ipmap) || widthof(ipmap)!=4 ) {
ipmap = m_allocate( 0, 4 );
LOG("restore failed, creating new ipmap");
} else {
LOG(sprintf("created (%d entries)",sizeof(ipmap)));
}
}
public void reset() {
if( sizeof(ipmap) < LARGE_CACHE_LIMIT ) {
expire_cache_small();
} else {
LOG(sprintf("reset %d ->",sizeof(ipmap)));
expire_cache_large( m_indices(ipmap) );
}
set_next_reset(10800);
}
static int active_entry( string key, mixed* data ) {
return time() < data[IPMAP_EXPIRE];
}
// Den cache auf einfache weise aufraeumen.
static void expire_cache_small() {
int s = sizeof(ipmap);
ipmap = filter( ipmap, #'active_entry );
save_object( IPLOOKUP_SAVE );
LOG(sprintf("reset (%d -> %d)",s,sizeof(ipmap)));
}
// Kompliziertere routine, die den cache etappenweise abarbeitet.
static void expire_cache_large( string* keys ) {
if( !pointerp(keys) ) return;
int next=0;
foreach( string key: keys ) {
if( get_eval_cost() < 100000 ) break;
if( ipmap[key,IPMAP_EXPIRE] < time() ) {
m_delete( ipmap, key );
}
next++;
}
LOG(sprintf("checking %d of %d",next,sizeof(keys)));
if( next<sizeof(keys) ) {
call_out( #'expire_cache_large, 6, keys[next..] );
} else {
save_object( IPLOOKUP_SAVE );
LOG(sprintf("reset -> %d (done)",sizeof(ipmap)));
}
}
#define SEARCHING "<auf der Suche...>"
/* Erzeugt einen temporaeren Eintrag, der fuer eine Suche steht.
* Wenn die Antwort kommt, wird der Eintrag mit den entgueltigen
* Daten ueberschrieben.
*/
static void make_request( string ipnum ) {
send_udp( IPLOOKUP_HOST, IPLOOKUP_PORT, ipnum );
ipmap += ([ ipnum : ipnum ; SEARCHING ;
time()+QUERY_TIME ; STATUS_QUERYING
]);
}
/* Liefert zu einer gegebenen ipnum den Ort.
* diese Funktion wird von simul_efun aufgerufen.
* @param ipnum eine numerische ip-adresse oder ein interactive
* @return den Ort (oder das Land) in dem sich die ip-adresse
* laut externem Server befindet.
*/
public string country( mixed ipnum ) {
string host,city;
int expire,state;
if( objectp(ipnum) ) {
ipnum = query_ip_number(ipnum);
}
if( !stringp(ipnum) ) {
return "<undefined>";
}
if( !m_contains( &host, &city, &expire, &state, ipmap, ipnum ) ) {
make_request( ipnum );
return SEARCHING;
}
return city;
}
/* Liefert zu einer gegebenen ipnum den Hostnamen.
* diese Funktion wird von simul_efun aufgerufen.
* @param ipnum eine numerische ip-adresse oder ein interactive
* @return den Hostnamen der zu der angegebenen ip-adresse gehoert.
* wenn der hostname nicht bekannt ist, wird die ipadresse zurueckgegeben.
*/
public string host( mixed ipnum ) {
string host,city;
int expire,state;
if( objectp(ipnum) ) {
ipnum = query_ip_number(ipnum);
}
if( !stringp(ipnum) ) {
return "<undefined>";
}
if( !m_contains( &host, &city, &expire, &state, ipmap, ipnum ) ) {
make_request( ipnum );
return ipnum;
}
return host;
}
/* wird vom master aufgerufen, wenn eine antwort vom externen
* iplookup dienst eintrudelt.
*/
public void update( string udp_reply ) {
string* reply;
if( previous_object()!=master() ) return;
reply = explode(udp_reply,"\n");
if( sizeof(reply)<4 ) return;
if( reply[3] == "<unknown>" ) reply[3] = reply[1];
if( reply[2] == "<unknown>" ) reply[2] = "irgendwoher";
ipmap += ([ reply[1] : reply[3] ; reply[2] ; time()+CACHE_TIME ;
STATUS_AUTHORITATIVE ]);
//save_object( IPLOOKUP_SAVE );
}
int remove(int silent) {
save_object( IPLOOKUP_SAVE );
destruct(this_object());
return 1;
}
// DEBUGGING CODE
#if 0
public void dump( string key, mixed* data ) {
printf( "%s: %s (%s) s=%d bis %d\n", key, data[0], data[1],
data[3], data[2] );
}
public void load(string s) {
restore_object(s);
if( !mappingp(ipmap) || widthof(ipmap)!=4 ) {
ipmap = m_allocate( 0, 4 );
LOG("restore failed, creating new ipmap");
} else {
LOG(sprintf("created (%d entries)",sizeof(ipmap)));
}
}
public void debug() {
map( ipmap, #'dump );
printf( "= %d Eintraege\n", sizeof(ipmap) );
}
#endif