/**************************************************************************/
// dawnstat.cpp - provide statistics on dawn based muds to dawnoftime.org
/***************************************************************************
* The Dawn of Time v1.69r (c)1997-2004 Michael Garratt *
* >> A number of people have contributed to the Dawn codebase, with the *
* majority of code written by Michael Garratt - www.dawnoftime.org *
* >> To use this source code, you must fully comply with the dawn license *
* in licenses.txt... In particular, you may not remove this copyright *
* notice. *
**************************************************************************/
// About: The code within this module sends to dawnoftime.org statistical
// information. Using this information dawnoftime.org is able to
// get an idea of the number of dawn based muds running and
// hopefully the number of players.
//
// The statistical information submitted to dawnoftime.org is a
// summary of mudstats, lastonstats and mudclientstats, combined
// with a few other things specific to your mud environment
// (such as the name of the mud). All information is kept private
// and not made publically available through dawnoftime.org... but
// in the future dawnoftime.org may include the statistical
// information to report how many dawn based muds are running, how
// popular various mud clients are etc.
//
// This information may be made publically available at
// either the dawnoftime.org website or
// http://statistics.dawnoftime.org in the future (once enough
// muds are running dawn to make it worth while).
//
// By default the mud after about 10 minutes of running will
// send the stats to http://dawnstat2.dawnoftime.org/dawnstat2.php
// This does not lag the mud in anyway, (unless your dns resolver
// is broken - use sockets to determine this), if the dns resolver
// is broken there may be a one off small delay (ordinarily less
// than 5 seconds) while the mud resolves the ip address of
// dawnstat.dawnoftime.org in order to know where to send the
// stats to.
//
//
/**************************************************************************/
//#define DAWNSTAT_LOG_PROGRESS
#define DAWNSTAT_SUBMIT_DOMAIN "dawnstat2.dawnoftime.org"
#define DAWNSTAT_SUBMIT_URL "/dawnstat2.php"
#define DAWNSTAT_DELAY 1800
/**************************************************************************/
#include "network.h"
#define __SEE_NETIO_INTERNAL_STRUCTURES__
#include "comm.h"
#include "include.h"
#include "laston.h"
#ifdef unix
typedef int SOCKET;
typedef struct sockaddr_in SOCKADDR_IN;
#define SOCKET_ERROR (-1)
#define INVALID_SOCKET (SOCKET)(~0)
#endif
#ifdef IPV6_SUPPORT_ENABLED
struct addrinfo *res=NULL;
#else
ipv4only_addrinfo *res=NULL;
#endif
#ifndef EISCONN
#define EISCONN 56 // is already connected
#endif
/**************************************************************************/
static void dawnstat_logf(char * fmt, ...)
{
#ifndef DAWNSTAT_LOG_PROGRESS
return;
#endif
char buf[HSL];
va_list args;
va_start(args, fmt);
vsnprintf(buf, HSL-MIL, fmt, args);
va_end(args);
logf("dawnstat: %s", buf);
}
/**************************************************************************/
int count_player_list(void)
{
int count=0;
char_data *ch=player_list;
for ( ; ch; ch= ch->next_player){
count++;
}
return count;
}
/**************************************************************************/
int count_active_players(void);
char *get_compile_time (bool show_parent_codebase_version);
extern time_t laston_since;
void do_lastonstats( char_data *ch, char *argument);
char *doWebFunction(DO_FUN * do_fun, char *argument, int level);
/**************************************************************************/
static char *dawnstat_generate_statistics_text()
{
static char result[45000];
char stats[45000];
stats[0]='\0';
char *binded_sockets=netio_return_binded_sockets();
#define ENCODE_INT(field) strcat(stats, FORMATF("&" # field"=%d", field))
#define ENCODE_INTH(field, header) strcat(stats, FORMATF("&" header"=%d", field))
#define ENCODE_STR(field) strcat(stats, FORMATF("&" # field"=%s", url_encode_post_data(field)))
#define ENCODE_STRH(field, header) strcat(stats, FORMATF("&" header"=%s", url_encode_post_data(field)))
ENCODE_STRH(game_settings->gamename, "gamename");
ENCODE_INTH(game_settings->unique_id, "unique_id");
ENCODE_STRH(game_settings->listen_on, "listen_on");
ENCODE_STR(binded_sockets);
ENCODE_INTH(mainport, "port");
ENCODE_STRH(DAWN_RELEASE_VERSION, "release_version");
ENCODE_STRH(DAWN_RELEASE_DATE, "release_date");
ENCODE_STRH(fwrite_wordflag(game_settings_flags, game_settings->flags,"gamesettings_flags",NULL), "gamesettings_flags");
ENCODE_STRH(fwrite_wordflag(game_settings_flags2, game_settings->flags2,"gamesettings_flags2",NULL), "gamesettings_flags2");
ENCODE_STRH(fwrite_wordflag(game_settings_flags3, game_settings->flags3,"gamesettings_flags3",NULL), "gamesettings_flags3");
ENCODE_STRH(fwrite_wordflag(game_settings_flags4, game_settings->flags4,"gamesettings_flags4",NULL), "gamesettings_flags4");
ENCODE_STRH(get_compile_time(false), "compile_time_text");
ENCODE_INTH(game_settings->damage_scale_value, "damage_scale_value");
ENCODE_INTH(game_settings->global_xp_scale_value,"global_xp_scale_value");
ENCODE_INTH(game_settings->newbie_start_gold,"newbie_start_gold");
ENCODE_INTH(game_settings->newbie_start_silver,"newbie_start_silver");
ENCODE_INTH(game_settings->newbie_start_practice,"newbie_start_practice");
ENCODE_INTH(game_settings->newbie_start_train,"newbie_start_train");
ENCODE_INT(top_area);
ENCODE_INT(top_room);
ENCODE_INT(top_shop);
ENCODE_INT(top_mob_index);
ENCODE_INT(mobile_count);
ENCODE_INT(mobprog_count);
ENCODE_INT(top_obj_index);
ENCODE_INT(top_help);
ENCODE_INT(social_count);
ENCODE_INTH(count_active_players(), "active_player_count");
ENCODE_INTH(count_player_list(), "current_playerlist_count");
ENCODE_INT(max_on);
ENCODE_INT(LEVEL_IMMORTAL);
ENCODE_INT(MAX_LEVEL);
ENCODE_INT((int)current_time);
ENCODE_INT((int)boot_time);
ENCODE_INT((int)laston_since);
#ifdef __DATE__
ENCODE_STRH(__DATE__,"compiled_date");
#endif
#ifdef __TIME__
ENCODE_STRH(__TIME__,"compiled_time");
#endif
#if defined (__CYGWIN__)
ENCODE_STRH("cygwin","compiled_platform");
#elif defined (WIN32)
ENCODE_STRH("Win32","compiled_platform");
#elif defined (unix) || defined (__unix__)
# if defined (linux)
ENCODE_STRH("linux","compiled_platform");
# elif defined (__OpenBSD__)
ENCODE_STRH("OpenBSD","compiled_platform");
# elif defined (__FreeBSD__)
ENCODE_STRH("FreeBSD","compiled_platform");
# elif defined (__NetBSD__)
ENCODE_STRH("NetBSD","compiled_platform");
# elif defined(__APPLE__) && defined(__MACH__)
ENCODE_STRH("MacOSX","compiled_platform");
# elif defined(BSD)
ENCODE_STRH("BSD","compiled_platform");
# else
ENCODE_STRH("unix","compiled_platform");
# endif
#elif
ENCODE_STRH("unknown","compiled_platform");
#endif
// possible compiler thing of interest
#ifdef __VERSION__ // e.g. "2.96 20000731 (Red Hat Linux 7.1 2.96-85)"
ENCODE_STRH(__VERSION__,"compiler_version");
#endif
ENCODE_STRH(PLATFORM_INFO,"platform_info");
// retrieve a condensed version of laston stats output
// will be able to be used to see how common classes/races are
// between muds, in addition stats about mccp usage etc.
chImmortal->desc->colour_mode = CT_NOCOLOUR;
char *laston_stats=str_dup(doWebFunction(&do_lastonstats, "", MAX_LEVEL));
chImmortal->desc->colour_mode = CT_HTML;
laston_stats=string_replace_all(laston_stats, " ", " ");
laston_stats=string_replace_all(laston_stats, "==", "=");
laston_stats=string_replace_all(laston_stats, "\r", "");
char *laston_stats_no_header=strstr(laston_stats, "-=");
if(!laston_stats_no_header){
laston_stats_no_header=laston_stats;
}
ENCODE_STRH(laston_stats_no_header,"laston_stats");
free_string(laston_stats);
laston_stats=str_dup(laston_generate_mud_client_stats());
laston_stats=string_replace_all(laston_stats, "\r", "");
ENCODE_STRH(laston_stats,"mudclient_stats");
free_string(laston_stats);
char *encoded=&stats[1];
sprintf(result,
"POST " DAWNSTAT_SUBMIT_URL " HTTP/1.1\r\n"
"Content-Type: application/x-www-form-urlencoded\r\n"
"Host: " DAWNSTAT_SUBMIT_DOMAIN "\r\n"
"User-Agent: Mozilla/4.0 (compatible; DawnWebSubmit/1.0)\r\n"
"Content-Length: %d\r\n"
"Cache-Control: no-cache\r\n"
"\r\n%s",
str_len(encoded),
encoded);
return result;
}
/**************************************************************************/
char *dawnstat_stattext_to_post;
char *dawnstat_post_response_position;
char dawnstat_post_response[MSL];
/**************************************************************************/
const char *get_winsock_error_text(int errorcode);
bool init_winsock();
int write_to_socket( dawn_socket output_socket, const char *txt, int length );
/**************************************************************************/
enum dawnstat_stages
{
DAWNSTATSTAGE_WAIT,
DAWNSTATSTAGE_RESOLVING_DOMAIN_REMOTE,
DAWNSTATSTAGE_RESOLVING_DOMAIN_LOCAL,
DAWNSTATSTAGE_DOMAIN_RESOLVED,
DAWNSTATSTAGE_INITIATE_CONNECTION,
DAWNSTATSTAGE_CONNECT_IN_PROGRESS,
DAWNSTATSTAGE_GENERATE_STATS,
DAWNSTATSTAGE_POSTING,
DAWNSTATSTAGE_CHECK_POST_ACCEPTANCE,
DAWNSTATSTAGE_CLOSE_SOCKET,
DAWNSTATSTAGE_COMPLETED,
DAWNSTATSTAGE_ABORTED
};
dawnstat_stages dawnstat_stage=DAWNSTATSTAGE_WAIT;
time_t dawnstat_connect_timeout=0;
time_t dawnstat_resolve_timeout=0;
static dawn_socket dawnstat_socket;
int dawnstat_address_count;
char dawnstat_connection_address_and_port[MIL];
/**************************************************************************/
// - resolve domain name, performed once per reboot
static void dawnstat_resolve_domain_directly()
{
resolver_address_found=false;
resolver_address_failed=false;
dawnstat_stage=DAWNSTATSTAGE_RESOLVING_DOMAIN_LOCAL;
dawnstat_resolve_timeout=current_time+20;
resolverlocal_queue_command(FORMATF("resolve system %s.", DAWNSTAT_SUBMIT_DOMAIN));
}
/**************************************************************************/
// - resolve domain name, performed once per reboot
static void dawnstat_resolve_domain()
{
dawnstat_logf("starting resolving of '%s.'", DAWNSTAT_SUBMIT_DOMAIN);
if(!resolver_running){
dawnstat_logf("The resolver isn't running, using direct resolution.");
dawnstat_resolve_domain_directly();
return;
}
if(resolver_version<1500){
dawnstat_logf("Old resolver version running, resolving directly.");
dawnstat_resolve_domain_directly();
return;
}
resolver_send_data(FORMATF("resolve system %s.", DAWNSTAT_SUBMIT_DOMAIN));
resolver_address_found=false;
resolver_address_failed=false;
dawnstat_stage=DAWNSTATSTAGE_RESOLVING_DOMAIN_REMOTE;
dawnstat_resolve_timeout=current_time+100;
}
/**************************************************************************/
// if a connection failed on a given ip address, move onto the next ip
// if there are no remaining ip's abort the entire process
static void dawnstat_connection_attempt_failed()
{
dawnstat_address_count++;
resolve_result_address *addr=resolve_result_address_list->get(dawnstat_address_count);
if(!addr || IS_NULLSTR(addr->address)){
dawnstat_stage=DAWNSTATSTAGE_ABORTED;
dawnstat_logf("dawnstat connect aborted.");
}else{
dawnstat_stage=DAWNSTATSTAGE_INITIATE_CONNECTION;
}
}
/**************************************************************************/
// this should only be run once per address in the list of addresses
static void dawnstat_initiate_connection()
{
// initiate connection to DAWNSTAT_SUBMIT_DOMAIN
resolve_result_address *addr=resolve_result_address_list->get(dawnstat_address_count);
if(!addr || IS_NULLSTR(addr->address)){
dawnstat_connection_attempt_failed();
return;
}
if(addr->ipv6){
sprintf(dawnstat_connection_address_and_port, "[%s]:80", addr->address);
}else{
sprintf(dawnstat_connection_address_and_port, "%s:80", addr->address);
}
dawnstat_logf("initiating connection to %s", dawnstat_connection_address_and_port);
// free a previous socket if it hasn't been already freed
if(dawnstat_socket){
closesocket(dawnstat_socket);
dawnstat_socket=0;
}
// setup the socket address structure for where we want to connect to
#ifdef IPV6_SUPPORT_ENABLED
struct addrinfo hints;
memset(&hints, 0, sizeof(struct addrinfo));
hints.ai_flags=AI_NUMERICHOST; // we have an ip address
if(addr->ipv6){
hints.ai_family = AF_INET6;
}else{
hints.ai_family = AF_INET;
}
hints.ai_socktype = SOCK_STREAM;
if(res){
freeaddrinfo(res);
res=NULL;
}
int r = getaddrinfo(addr->address, // ip address in text form
"80", &hints, &res);
// check that it was converted successfully
if (r) {
#ifdef WIN32
if(r==WSAHOST_NOT_FOUND){
dawnstat_logf("getaddrinfo error %d - couldn't convert '%s' to a valid ip address.",
r, addr->address);
}else if(r==WSAEAFNOSUPPORT && addr->ipv6){
dawnstat_logf("\n"
"getaddrinfo error %d - couldn't convert '%s' to a valid ip address\n"
" - getaddrinfo reported this system has no support for the AF_INET6 address family.\n"
" This error message is normal if this system doesn't support ipv6, as it\n"
" was an ipv6 address getaddrinfo() was asked to convert.",
r, addr->address);
}else{
dawnstat_logf("dawnstat_initiate_connection(): getaddrinfo(%s) error %d",
addr->address, r);
}
#else
{
dawnstat_logf("dawnstat_initiate_connection(): getaddrinfo(%s) error %d - '%s'",
addr->address, r, gai_strerror(r));
}
#endif
dawnstat_connection_attempt_failed();
return;
}
#else // !IPV6_SUPPORT_ENABLED
if(addr->ipv6){
dawnstat_logf("skipping connection to %s "
"- ipv6 address format not supported on this compile",
dawnstat_connection_address_and_port);
dawnstat_connection_attempt_failed();
return;
}
if(res){
delete res;
res=NULL;
}
res=new ipv4only_addrinfo;
res->ai_next=NULL;
res->ai_family=AF_INET;
res->ai_socktype=SOCK_STREAM;
res->ai_protocol=IPPROTO_TCP;
struct sockaddr_in sa;
memset(&sa, 0, sizeof(sa));
sa.sin_family = AF_INET;
sa.sin_port = htons(80);
sa.sin_addr.s_addr= inet_addr( addr->address);
if(sa.sin_addr.s_addr==INADDR_NONE){
// we had an invalid address supplied
// technically INADDR_NONE can be returned by the valid ip 255.255.255.255
// but 255.255.255.255 isn't useful in this context so we assume input was invalid.
dawnstat_logf("'%s' was not a valid ipv4 address.", addr->address);
dawnstat_connection_attempt_failed();
return;
}
res->ai_addr=(struct sockaddr *) &sa;
res->ai_addrlen=sizeof(sa);
#endif
dawnstat_socket= socket(res->ai_family, res->ai_socktype, res->ai_protocol);
if (dawnstat_socket== dawn_socket_INVALID_SOCKET){
dawnstat_logf("Error creating connection socket");
dawnstat_connection_attempt_failed();
return;
}
{ // set the socket to non-blocking mode
#ifdef WIN32
unsigned long blockmode = 1;
if(ioctlsocket(dawnstat_socket, FIONBIO, &blockmode)== SOCKET_ERROR)
{
dawnstat_logf("ioctlsocket: error setting new socket to nonblocking, "
"WSAGetLastError=%d", WSAGetLastError());
dawnstat_connection_attempt_failed();
return;
}
#else
if ( fcntl(dawnstat_socket, F_SETFL, O_NONBLOCK)< 0 ){
dawnstat_logf("fcntl: error setting new socket to nonblocking");
dawnstat_connection_attempt_failed();
return;
}
#endif
}
// start the connection
int nRet= connect(dawnstat_socket, res->ai_addr, (socklen_t)res->ai_addrlen);
// check for instant results
if(nRet==0){ // successful connection, jump straight to generating the stats to post
dawnstat_logf("dawnstat_initiate_connection(): immediate connect, jumping to generate stats.");
dawnstat_stage=DAWNSTATSTAGE_GENERATE_STATS;
return;
}
// check the error codes, if we have anything other than
// what is normal for a blocked connect call, we abort the connection
#ifdef WIN32
int WSALastError=WSAGetLastError();
if(WSALastError!=WSAEWOULDBLOCK){
dawnstat_logf("dawnstat_initiate_connection(): connect() error %s",
get_winsock_error_text(WSALastError));
dawnstat_connection_attempt_failed();
return;
}
#else
if(nRet<0){
if (errno != EINPROGRESS && errno != EALREADY)
{
dawnstat_logf("dawnstat_initiate_connection(): connect() error %d", errno);
dawnstat_connection_attempt_failed();
return;
}
}
#endif
// the connection process has started, and is now in progress
dawnstat_connect_timeout=current_time+120; // 2 minutes to connect
dawnstat_logf("connection initiation successful");
dawnstat_stage=DAWNSTATSTAGE_CONNECT_IN_PROGRESS;
return;
}
/**************************************************************************/
static void dawnstat_process_connect()
{
// handle the timeout first
if(dawnstat_connect_timeout<current_time){
dawnstat_logf("dawnstat_process_connect(): pending connection timed out.");
dawnstat_connection_attempt_failed();
return;
}
int nRet;
dawnstat_logf("processing connection %s", dawnstat_connection_address_and_port);
#ifdef WIN32
{
// use a select call to see if the socket to be ready for writing
// set our select_timeout, to 0 seconds
struct timeval select_timeout;
select_timeout.tv_sec=0;
select_timeout.tv_usec=0;
// prepare our file descriptor set
fd_set fdWrite; // connect success is reported here
FD_ZERO(&fdWrite);
FD_SET(dawnstat_socket, &fdWrite);
fd_set fdExcept; // connect failure is reported here
FD_ZERO(&fdExcept);
FD_SET(dawnstat_socket, &fdExcept);
// check if the socket is ready
nRet=select((int)dawnstat_socket+1, NULL, &fdWrite, &fdExcept, &select_timeout);
dawnstat_logf("select() returned %d", nRet);
if(nRet<1){
if(nRet==0){
// it isn't ready yet - wait longer
}else{
dawnstat_logf("dawnstat_process_connect(): select() returned error %d", nRet);
dawnstat_connection_attempt_failed();
}
return;
}
if(FD_ISSET( dawnstat_socket, &fdWrite )){
// socket is ready for writing
dawnstat_logf("dawnstat_process_connect(): connection established.");
dawnstat_stage=DAWNSTATSTAGE_GENERATE_STATS;
return;
}
if(FD_ISSET( dawnstat_socket, &fdExcept )){
// connection failed
dawnstat_logf("dawnstat_process_connect(): connection failed.");
}else{
dawnstat_logf("dawnstat_process_connect(): don't know how we got here!");
}
dawnstat_connection_attempt_failed();
return;
}
#else
// just attempt to connect the socket again using connect(),
// if the socket is now connected, connect() will return 0 or error 56 (already connected)
nRet= connect(dawnstat_socket, res->ai_addr, res->ai_addrlen);
if(nRet==0){
// successful connection, generate the stats to post
dawnstat_stage=DAWNSTATSTAGE_GENERATE_STATS;
return;
}
if(nRet<0){
if(errno==EISCONN){
// we consider the connection successful, generate the stats to post
dawnstat_logf("dawnstat_process_connect(): socket connected.");
dawnstat_stage=DAWNSTATSTAGE_GENERATE_STATS;
return;
}
if (errno != EINPROGRESS && errno != EALREADY){
dawnstat_logf("dawnstat_process_connect(): connect() error %d", errno);
dawnstat_connection_attempt_failed();
return;
}
}
#endif
return;
}
/**************************************************************************/
static void dawnstat_generate_stats()
{
dawnstat_stattext_to_post=dawnstat_generate_statistics_text();
dawnstat_stage=DAWNSTATSTAGE_POSTING;
}
/**************************************************************************/
static void dawnstat_post()
{
char *msg=dawnstat_stattext_to_post;
int written;
int msglen=str_len(msg);
written=write_to_socket(dawnstat_socket, msg, msglen);
if(written<0){ // check for an error
dawnstat_logf("dawnstat_post(): An error occured posting statistics.");
dawnstat_connection_attempt_failed();
return;
}
// check for an incomplete write
if(written<msglen){
dawnstat_logf("Incomplete write, sent %d bytes of %d, write rest later", written, msglen);
dawnstat_stattext_to_post+=written;
return;
}
// completed write
dawnstat_logf("Submitted %d bytes", written);
memset(dawnstat_post_response, 0, sizeof(dawnstat_post_response));
dawnstat_post_response_position=dawnstat_post_response;
dawnstat_stage=DAWNSTATSTAGE_CHECK_POST_ACCEPTANCE;
}
/**************************************************************************/
static void dawnstat_check_post_acceptance()
{
int len=sizeof(dawnstat_post_response)-str_len(dawnstat_post_response)-10;
int read=recv(dawnstat_socket, dawnstat_post_response_position, len, 0);
if(read<0){ // check for an error
dawnstat_logf("dawnstat_check_post_acceptance(): An error occured checking for "
"the post acceptance, posting to next ip or failing.");
dawnstat_connection_attempt_failed();
return;
}
// the response will be formatted in the form:
// "HTTP/1.1" <space> <status code> <space> <text description of status code>\n"
// where <status code> is a 3 http status digit code, 200 is considered successful
// we only need to read 2 space characters, in order to get the status code
if(count_char(dawnstat_post_response, ' ')<2){
dawnstat_logf("Incomplete read, will continue read next time around.");
dawnstat_logf("Read response so far: '%s'", dawnstat_post_response);
dawnstat_post_response_position=&dawnstat_post_response[str_len(dawnstat_post_response)];
return;
}
// completed read, get the status code
char *start=strstr(dawnstat_post_response," ");
if(!start){
// shouldn't be possible to get here unless someone has introduced bugs into count_char()
dawnstat_logf("dawnstat_check_post_acceptance(): can't find spaces to get start of status code!");
dawnstat_connection_attempt_failed();
return;
}
start++;
char *end=strstr(start," ");
if(!end){
// shouldn't be possible to get here unless someone has introduced bugs into count_char()
dawnstat_logf("dawnstat_check_post_acceptance(): can't find spaces to get end of status code!");
dawnstat_connection_attempt_failed();
return;
}
*end='\0';
if(!is_number(start)){
// confirm the status code we are checking is an actual number
dawnstat_logf("dawnstat_check_post_acceptance(): status code found isn't a number for some reason!");
dawnstat_connection_attempt_failed();
return;
}
int status_code=atoi(start);
*end=' ';
if(status_code!=200){
dawnstat_logf("post unsuccessful - http status code %d.", status_code);
dawnstat_connection_attempt_failed();
return;
}
dawnstat_logf("post accepted - http status code 200.");
dawnstat_stage=DAWNSTATSTAGE_CLOSE_SOCKET;
}
/**************************************************************************/
void dawnstat_update()
{
static time_t wait_until=0;
// this function is called every 10 seconds by default (PULSE_DAWNSTAT)
// it is necessary to be called this often due to is non blocking io
if((int)dawnstat_stage<(int)DAWNSTATSTAGE_COMPLETED){
if(!wait_until || dawnstat_stage!=DAWNSTATSTAGE_WAIT){
dawnstat_logf("dawnstat_update(%d)", (int)dawnstat_stage);
}
}
switch(dawnstat_stage){
case DAWNSTATSTAGE_WAIT:
{
if(wait_until==0){
wait_until=current_time+DAWNSTAT_DELAY;
}else if(wait_until<current_time){
// time to move on
wait_until=0;
dawnstat_logf("moving on to resolving stage.");
dawnstat_resolve_domain();
}
}
break;
case DAWNSTATSTAGE_RESOLVING_DOMAIN_REMOTE:
dawnstat_logf("checking if domain has been resolved");
if(resolver_address_found){
dawnstat_stage=DAWNSTATSTAGE_DOMAIN_RESOLVED;
for(resolve_result_address *node=resolve_result_address_list; node; node=node->next){
dawnstat_logf("dawnstat address resolved as ipv%d '%s'", node->ipv6?6:4, node->address);
}
}
if(dawnstat_resolve_timeout<current_time){
dawnstat_logf("domain resolution timed out for resolver, trying local resolution.");
dawnstat_resolve_domain_directly();
return;
}
if(resolver_address_failed){
dawnstat_logf("domain resolution failed via resolver, trying local resolution.");
dawnstat_resolve_domain_directly();
return;
}
break;
case DAWNSTATSTAGE_RESOLVING_DOMAIN_LOCAL:
dawnstat_logf("checking if domain has been resolved locally");
if(resolver_address_found){
dawnstat_stage=DAWNSTATSTAGE_DOMAIN_RESOLVED;
for(resolve_result_address *node=resolve_result_address_list; node; node=node->next){
dawnstat_logf("dawnstat address resolved as ipv%d '%s'", node->ipv6?6:4, node->address);
}
return;
}
if(dawnstat_resolve_timeout<current_time){
dawnstat_logf("domain resolution timed out.");
dawnstat_stage=DAWNSTATSTAGE_ABORTED;
return;
}
if(resolver_address_failed){
dawnstat_logf("domain resolution failed.");
dawnstat_stage=DAWNSTATSTAGE_ABORTED;
return;
}
break;
case DAWNSTATSTAGE_DOMAIN_RESOLVED:
dawnstat_address_count=0;
dawnstat_stage=DAWNSTATSTAGE_INITIATE_CONNECTION;
break;
case DAWNSTATSTAGE_INITIATE_CONNECTION:
dawnstat_initiate_connection();
break;
case DAWNSTATSTAGE_CONNECT_IN_PROGRESS:
dawnstat_logf("processing connection.");
dawnstat_process_connect();
break;
case DAWNSTATSTAGE_GENERATE_STATS:
dawnstat_logf("generating statistics.");
dawnstat_generate_stats();
break;
case DAWNSTATSTAGE_POSTING:
dawnstat_logf("posting statistics.");
dawnstat_post();
break;
case DAWNSTATSTAGE_CHECK_POST_ACCEPTANCE:
dawnstat_logf("checking for post acceptance.");
dawnstat_check_post_acceptance();
break;
case DAWNSTATSTAGE_CLOSE_SOCKET:
dawnstat_logf("closing socket.");
if(dawnstat_socket){
closesocket(dawnstat_socket);
dawnstat_socket=0;
}
dawnstat_stage=DAWNSTATSTAGE_COMPLETED;
break;
case DAWNSTATSTAGE_COMPLETED:
case DAWNSTATSTAGE_ABORTED:
// redo dawnstats once per day if successful
// or try again in 7 hours time if failed last time
{
static time_t redo_in=0;
if(redo_in){
if(redo_in<current_time){
dawnstat_logf("restarting dawnstats");
dawnstat_stage=DAWNSTATSTAGE_WAIT;
redo_in=0;
}
}else{
if(dawnstat_stage==DAWNSTATSTAGE_ABORTED){
redo_in=current_time + (7*60*60); // every 7 hours
}else{
redo_in=current_time + (24*60*60); // every 24 hours
}
}
}
default:
break;
};
}
/**************************************************************************/
/**************************************************************************/