/* ============================================================================ * Copyright (C) 1998-2000 Angus Mackay. All rights reserved; * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2, or (at your option) * any later version. * * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES, * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE * AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, * OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. * ============================================================================ */ /* * ==================================================================== * Patched by Michael Fung for miniDNS.net at 06-Apr-2001 * Service type "justlinux" is modified to run for miniDNS.net. * My apology to "justlinux". * Please go to http://www.miniDNS.net/ to download the conf file for miniDNS * ==================================================================== */ /* * ez-ipupdate * * a very simple dynDNS client for the ez-ip dynamic dns service * (http://www.ez-ip.net). * * why this program when something like: * curl -u user:pass http://www.ez-ip.net/members/update/?key=val&... * would do the trick? because there are nicer clients for other OSes and * I don't like to see UNIX get the short end of the stick. * * tested under Linux and Solaris. * */ #ifdef HAVE_CONFIG_H # include #endif // you man very well need to edit this, don't worry though, email is only sent // if bad things happend and it has to exit when in daemon mode. #define SEND_EMAIL_CMD "mail" #define EZIP_DEFAULT_SERVER "www.EZ-IP.Net" #define EZIP_DEFAULT_PORT "80" #define EZIP_REQUEST "/members/update/" #define PGPOW_DEFAULT_SERVER "www.penguinpowered.com" #define PGPOW_DEFAULT_PORT "2345" #define PGPOW_REQUEST "update" #define PGPOW_VERSION "1.0" #define DHS_DEFAULT_SERVER "members.dhs.org" #define DHS_DEFAULT_PORT "80" #define DHS_REQUEST "/nic/hosts" #define DHS_SUCKY_TIMEOUT 60 #define DYNDNS_DEFAULT_SERVER "members.dyndns.org" #define DYNDNS_DEFAULT_PORT "80" #define DYNDNS_REQUEST "/nic/update" #define DYNDNS_STAT_REQUEST "/nic/update" #define ODS_DEFAULT_SERVER "update.ods.org" #define ODS_DEFAULT_PORT "7070" #define ODS_REQUEST "update" #define TZO_DEFAULT_SERVER "cgi.tzo.com" #define TZO_DEFAULT_PORT "80" #define TZO_REQUEST "/webclient/signedon.html" #define GNUDIP_DEFAULT_SERVER "" #define GNUDIP_DEFAULT_PORT "3495" #define GNUDIP_REQUEST "0" #define EASYDNS_DEFAULT_SERVER "members.easydns.com" #define EASYDNS_DEFAULT_PORT "80" #define EASYDNS_REQUEST "/dyn/ez-ipupdate.php" #define JUSTL_DEFAULT_SERVER "www.minidns.net" #define JUSTL_DEFAULT_PORT "80" #define JUSTL_REQUEST "/areg.php" #define JUSTL_VERSION "2.0" #define DYNS_DEFAULT_SERVER "www.dyns.cx" #define DYNS_DEFAULT_PORT "80" #define DYNS_REQUEST "/postscript.php" #define HN_DEFAULT_SERVER "www.hn.org" #define HN_DEFAULT_PORT "80" #define HN_REQUEST "/vanity/update" #ifdef USE_MD5 # define SERVICES_STR "ezip, pgpow, dhs, dyndns, dyndns-static, ods, tzo, gnudip, easydns, justlinux, dyns, hn" # define SERVICES_HELP_STR "ezip, pgpow, dhs, dyndns, \n\t\t\t\tdyndns-static, ods, tzo, gnudip, easydns, \n\t\t\t\tjustlinux, dyns, hn" #else # define SERVICES_STR "ezip, pgpow, dhs, dyndns, dyndns-static, ods, tzo, easydns, justlinux, dyns, hn" # define SERVICES_HELP_STR "ezip, pgpow, dhs, dyndns, \n\t\t\t\tdyndns-static, ods, tzo, easydns, \n\t\t\t\tjustlinux, dyns, hn" #endif #define DEFAULT_TIMEOUT 120 #define DEFAULT_UPDATE_PERIOD 120 #define DEFAULT_RESOLV_PERIOD 30 #ifdef DEBUG # define BUFFER_SIZE (16*1024) #else # define BUFFER_SIZE (4*1024-1) #endif #ifdef HAVE_GETOPT_H # include #endif #include #include #include #include #if HAVE_FCNTL_H # include #endif #include #if HAVE_ARPA_INET_H # include #endif #if HAVE_ERRNO_H # include #endif #include #include #if HAVE_SYS_TYPES_H # include #endif #if HAVE_SIGNAL_H # include #endif #if HAVE_SYS_TIME_H # include #endif #if HAVE_SYS_WAIT_H # include #endif #if HAVE_SYSLOG_H # include #endif #if HAVE_STDARG_H # include #endif #include #if HAVE_PWD_H && HAVE_GRP_H # include # include #endif #if defined(HAVE_FORK) && !defined(HAVE_VFORK) # define vfork fork #endif #if USE_MD5 # include # define MD5_DIGEST_BYTES (16) #endif #if __linux__ || __SVR4 || __OpenBSD__ || __FreeBSD__ || __NetBSD__ # define IF_LOOKUP 1 # include # include # ifdef HAVE_SYS_SOCKIO_H # include # endif #endif #include #include #include #include #if !defined(__GNUC__) && !defined(HAVE_SNPRINTF) #error "get gcc, fix this code, or find yourself a snprintf!" #else # if HAVE_SNPRINTF # define snprintf(x, y, z...) snprintf(x, y, ## z) # else # define snprintf(x, y, z...) sprintf(x, ## z) # endif #endif #if HAVE_VSNPRINTF # define vsnprintf(x, y, z...) vsnprintf(x, y, ## z) #else # define vsnprintf(x, y, z...) vsprintf(x, ## z) #endif #ifndef HAVE_HERROR # define herror(x) fprintf(stderr, "%s: error\n", x) #endif #define N_STR(x) (x == NULL ? "" : x) #ifndef OS # define OS "unknown" #endif // the min period for checking the interface #define MIN_UPDATE_PERIOD 10 // the min/max time to wait if we fail to update #define MIN_WAIT_PERIOD 300 #define MAX_WAIT_PERIOD (2*3600) // the min time that max-period can be set to #define MIN_MAXINTERVAL (24*3600) // the max time we will wait if the server tells us to #define MAX_WAITRESPONSE_WAIT (24*3600) #define MAX_MESSAGE_LEN 256 #define ARGLENGTH 32 /**************************************************/ enum { SERV_NULL, SERV_EZIP, SERV_PGPOW, SERV_DHS, SERV_DYNDNS, SERV_DYNDNS_STAT, SERV_ODS, SERV_TZO, SERV_GNUDIP, SERV_EASYDNS, SERV_JUSTL, SERV_DYNS, SERV_HN, }; struct service_t { int type; char *name; int (*update_entry)(void); int (*check_info)(void); char **fields_used; char *default_server; char *default_port; char *default_request; }; enum { UPDATERES_OK = 0, UPDATERES_ERROR, UPDATERES_SHUTDOWN, }; /**************************************************/ char *program_name = NULL; char *cache_file = NULL; char *config_file = NULL; char *server = NULL; char *port = NULL; char user[256]; char auth[512]; char user_name[128]; char password[128]; char *address = NULL; char *request = NULL; int wildcard = 0; char *mx = NULL; char *url = NULL; char *host = NULL; char *cloak_title = NULL; char *interface = NULL; int ntrys = 1; int update_period = DEFAULT_UPDATE_PERIOD; int resolv_period = DEFAULT_RESOLV_PERIOD; struct timeval timeout; int max_interval = 0; int service_set = 0; char *post_update_cmd = NULL; char *post_update_cmd_arg = NULL; int connection_type = 1; time_t last_update = 0; char *notify_email = NULL; char *pid_file = NULL; static volatile int client_sockfd; static volatile int last_sig = 0; /* service objects for various services */ // this one is for when people don't configure a default service as build time int NULL_check_info(void); static char *NULL_fields_used[] = { NULL }; static struct service_t NULL_service = { SERV_NULL, "NULL", NULL, NULL_check_info, NULL_fields_used, "", "", "" }; int EZIP_update_entry(void); int EZIP_check_info(void); static char *EZIP_fields_used[] = { "server", "user", "address", "wildcard", "mx", "url", "host", NULL }; static struct service_t EZIP_service = { SERV_EZIP, "ez-ip", EZIP_update_entry, EZIP_check_info, EZIP_fields_used, EZIP_DEFAULT_SERVER, EZIP_DEFAULT_PORT, EZIP_REQUEST }; int PGPOW_update_entry(void); int PGPOW_check_info(void); static char *PGPOW_fields_used[] = { "server", "host", NULL }; static struct service_t PGPOW_service = { SERV_PGPOW, "justlinux v1.0 (penguinpowered)", PGPOW_update_entry, PGPOW_check_info, PGPOW_fields_used, PGPOW_DEFAULT_SERVER, PGPOW_DEFAULT_PORT, PGPOW_REQUEST }; int DHS_update_entry(void); int DHS_check_info(void); static char *DHS_fields_used[] = { "server", "user", "address", "wildcard", "mx", "url", "host", NULL }; static struct service_t DHS_service = { SERV_DHS, "dhs", DHS_update_entry, DHS_check_info, DHS_fields_used, DHS_DEFAULT_SERVER, DHS_DEFAULT_PORT, DHS_REQUEST }; int DYNDNS_update_entry(void); int DYNDNS_check_info(void); static char *DYNDNS_fields_used[] = { "server", "user", "address", "wildcard", "mx", "host", NULL }; static struct service_t DYNDNS_service = { SERV_DYNDNS, "dyndns", DYNDNS_update_entry, DYNDNS_check_info, DYNDNS_fields_used, DYNDNS_DEFAULT_SERVER, DYNDNS_DEFAULT_PORT, DYNDNS_REQUEST }; static char *DYNDNS_STAT_fields_used[] = { "server", "user", "address", "wildcard", "mx", "host", NULL }; static struct service_t DYNDNS_STAT_service = { SERV_DYNDNS_STAT, "dyndns-static", DYNDNS_update_entry, DYNDNS_check_info, DYNDNS_STAT_fields_used, DYNDNS_DEFAULT_SERVER, DYNDNS_DEFAULT_PORT, DYNDNS_STAT_REQUEST }; int ODS_update_entry(void); int ODS_check_info(void); static char *ODS_fields_used[] = { "server", "host", "address", NULL }; static struct service_t ODS_service = { SERV_ODS, "ods", ODS_update_entry, ODS_check_info, ODS_fields_used, ODS_DEFAULT_SERVER, ODS_DEFAULT_PORT, ODS_REQUEST }; int TZO_update_entry(void); int TZO_check_info(void); static char *TZO_fields_used[] = { "server", "user", "address", "host", "connection-type", NULL }; static struct service_t TZO_service = { SERV_TZO, "tzo", TZO_update_entry, TZO_check_info, TZO_fields_used, TZO_DEFAULT_SERVER, TZO_DEFAULT_PORT, TZO_REQUEST }; int EASYDNS_update_entry(void); int EASYDNS_check_info(void); static char *EASYDNS_fields_used[] = { "server", "user", "address", "wildcard", "mx", "host", NULL }; static struct service_t EASYDNS_service = { SERV_EASYDNS, "easydns", EASYDNS_update_entry, EASYDNS_check_info, EASYDNS_fields_used, EASYDNS_DEFAULT_SERVER, EASYDNS_DEFAULT_PORT, EASYDNS_REQUEST }; #ifdef USE_MD5 int GNUDIP_update_entry(void); int GNUDIP_check_info(void); static char *GNUDIP_fields_used[] = { "server", "user", "host", "address", NULL }; static struct service_t GNUDIP_service = { SERV_GNUDIP, "gnudip", GNUDIP_update_entry, GNUDIP_check_info, GNUDIP_fields_used, GNUDIP_DEFAULT_SERVER, GNUDIP_DEFAULT_PORT, GNUDIP_REQUEST }; #endif int JUSTL_update_entry(void); int JUSTL_check_info(void); static char *JUSTL_fields_used[] = { "server", "user", "host", NULL }; static struct service_t JUSTL_service = { SERV_JUSTL, "justlinux v2.0 (penguinpowered)", JUSTL_update_entry, JUSTL_check_info, JUSTL_fields_used, JUSTL_DEFAULT_SERVER, JUSTL_DEFAULT_PORT, JUSTL_REQUEST }; int DYNS_update_entry(void); int DYNS_check_info(void); static char *DYNS_fields_used[] = { "server", "user", "host", NULL }; static struct service_t DYNS_service = { SERV_DYNS, "dyns", DYNS_update_entry, DYNS_check_info, DYNS_fields_used, DYNS_DEFAULT_SERVER, DYNS_DEFAULT_PORT, DYNS_REQUEST }; int HN_update_entry(void); int HN_check_info(void); static char *HN_fields_used[] = { "server", "user", "address", NULL }; static struct service_t HN_service = { SERV_HN, "hn", HN_update_entry, HN_check_info, HN_fields_used, HN_DEFAULT_SERVER, HN_DEFAULT_PORT, HN_REQUEST }; static struct service_t *service = &DEF_SERVICE; int options; #define OPT_DEBUG 0x0001 #define OPT_DAEMON 0x0004 #define OPT_QUIET 0x0008 #define OPT_FOREGROUND 0x0010 enum { CMD__start = 1, CMD_service_type, CMD_server, CMD_user, CMD_address, CMD_wildcard, CMD_mx, CMD_max_interval, CMD_url, CMD_host, CMD_cloak_title, CMD_interface, CMD_retrys, CMD_resolv_period, CMD_period, CMD_daemon, CMD_debug, CMD_execute, CMD_foreground, CMD_quiet, CMD_timeout, CMD_run_as_user, CMD_connection_type, CMD_cache_file, CMD_notify_email, CMD_pid_file, CMD__end }; int conf_handler(struct conf_cmd *cmd, char *arg); static struct conf_cmd conf_commands[] = { { CMD_address, "address", CONF_NEED_ARG, 1, conf_handler, "%s=" }, { CMD_cache_file, "cache-file", CONF_NEED_ARG, 1, conf_handler, "%s=" }, { CMD_cloak_title, "cloak-title", CONF_NEED_ARG, 1, conf_handler, "%s=" }, { CMD_daemon, "daemon", CONF_NO_ARG, 1, conf_handler, "%s=<command>" }, { CMD_execute, "execute", CONF_NEED_ARG, 1, conf_handler, "%s=<shell command>" }, { CMD_debug, "debug", CONF_NO_ARG, 1, conf_handler, "%s" }, { CMD_foreground, "foreground", CONF_NO_ARG, 1, conf_handler, "%s" }, { CMD_pid_file, "pid-file", CONF_NEED_ARG, 1, conf_handler, "%s=<file>" }, { CMD_host, "host", CONF_NEED_ARG, 1, conf_handler, "%s=<host>" }, { CMD_interface, "interface", CONF_NEED_ARG, 1, conf_handler, "%s=<interface>" }, { CMD_mx, "mx", CONF_NEED_ARG, 1, conf_handler, "%s=<mail exchanger>" }, { CMD_max_interval, "max-interval", CONF_NEED_ARG, 1, conf_handler, "%s=<number of seconds between updates>" }, { CMD_notify_email, "notify-email", CONF_NEED_ARG, 1, conf_handler, "%s=<address to email if bad things happen>" }, { CMD_retrys, "retrys", CONF_NEED_ARG, 1, conf_handler, "%s=<number of trys>" }, { CMD_server, "server", CONF_NEED_ARG, 1, conf_handler, "%s=<server name>" }, { CMD_service_type, "service-type", CONF_NEED_ARG, 1, conf_handler, "%s=<service type>" }, { CMD_timeout, "timeout", CONF_NEED_ARG, 1, conf_handler, "%s=<sec.millisec>" }, { CMD_resolv_period, "resolv-period", CONF_NEED_ARG, 1, conf_handler, "%s=<time between failed resolve attempts>" }, { CMD_period, "period", CONF_NEED_ARG, 1, conf_handler, "%s=<time between update attempts>" }, { CMD_url, "url", CONF_NEED_ARG, 1, conf_handler, "%s=<url>" }, { CMD_user, "user", CONF_NEED_ARG, 1, conf_handler, "%s=<user name>[:password]" }, { CMD_run_as_user, "run-as-user", CONF_NEED_ARG, 1, conf_handler, "%s=<user>" }, { CMD_wildcard, "wildcard", CONF_NO_ARG, 1, conf_handler, "%s" }, { CMD_quiet, "quiet", CONF_NO_ARG, 1, conf_handler, "%s" }, { CMD_connection_type, "connection-type", CONF_NEED_ARG, 1, conf_handler, "%s=<connection type>" }, { 0, 0, 0, 0, 0 } }; /**************************************************/ void print_usage( void ); void print_version( void ); void parse_args( int argc, char **argv ); int do_connect(int *sock, char *host, char *port); void base64Encode(char *intext, char *output); int main( int argc, char **argv ); void warn_fields(char **okay_fields); /**************************************************/ void print_usage( void ) { fprintf(stdout, "usage: "); fprintf(stdout, "%s [options] \n\n", program_name); fprintf(stdout, " Options are:\n"); fprintf(stdout, " -a, --address <ip address>\tstring to send as your ip address\n"); fprintf(stdout, " -b, --cache-file <file>\tfile to use for caching the ipaddress\n"); fprintf(stdout, " -c, --config-file <file>\tconfiguration file, almost all arguments can be\n"); fprintf(stdout, "\t\t\t\tgiven with: <name>[=<value>]\n\t\t\t\tto see a list of possible config commands\n"); fprintf(stdout, "\t\t\t\ttry \"echo help | %s -c -\"\n", program_name); fprintf(stdout, " -d, --daemon\t\t\trun as a daemon periodicly updating if \n\t\t\t\tnecessary\n"); #ifdef DEBUG fprintf(stdout, " -D, --debug\t\t\tturn on debuggin\n"); #endif fprintf(stdout, " -e, --execute <command>\tshell command to execute after a successful\n\t\t\t\tupdate\n"); fprintf(stdout, " -f, --foreground\t\twhen running as a daemon run in the foreground\n"); fprintf(stdout, " -F, --pidfile <file>\t\tuse <file> as a pid file\n"); fprintf(stdout, " -h, --host <host>\t\tstring to send as host parameter\n"); fprintf(stdout, " -i, --interface <iface>\twhich interface to use\n"); fprintf(stdout, " -L, --cloak_title <host>\tsome stupid thing for DHS only\n"); fprintf(stdout, " -m, --mx <mail exchange>\tstring to send as your mail exchange\n"); fprintf(stdout, " -M, --max-interval <# of sec>\tmax time in between updates\n"); fprintf(stdout, " -N, --notify-email <email>\taddress to send mail to if bad things happen\n"); fprintf(stdout, " -p, --resolv-period <sec>\tperiod to check IP if it can't be resolved\n"); fprintf(stdout, " -P, --period <# of sec>\tperiod to check IP in daemon \n\t\t\t\tmode (default: 1800 seconds)\n"); fprintf(stdout, " -q, --quiet \t\t\tbe quiet\n"); fprintf(stdout, " -r, --retrys <num>\t\tnumber of trys (default: 1)\n"); fprintf(stdout, " -R, --run-as-user <user>\tchange to <user> for running, be ware\n\t\t\t\tthat this can cause problems with handeling\n\t\t\t\tSIGHUP properly if that user can't read the\n\t\t\t\tconfig file\n"); fprintf(stdout, " -s, --server <server[:port]>\tthe server to connect to\n"); fprintf(stdout, " -S, --service-type <server>\tthe type of service that you are using\n"); fprintf(stdout, "\t\t\t\ttry one of: %s\n", SERVICES_HELP_STR); fprintf(stdout, " -t, --timeout <sec.millisec>\tthe amount of time to wait on I/O\n"); fprintf(stdout, " -T, --connection-type <num>\tnumber sent to TZO as your connection \n\t\t\t\ttype (default: 1)\n"); fprintf(stdout, " -U, --url <url>\t\tstring to send as the url parameter\n"); fprintf(stdout, " -u, --user <user[:passwd]>\tuser ID and password, if either is left blank \n\t\t\t\tthey will be prompted for\n"); fprintf(stdout, " -w, --wildcard\t\tset your domain to have a wildcard alias\n"); fprintf(stdout, " --help\t\t\tdisplay this help and exit\n"); fprintf(stdout, " --version\t\t\toutput version information and exit\n"); fprintf(stdout, " --credits\t\t\tprint the credits and exit\n"); fprintf(stdout, " --signalhelp\t\tprint help about signals\n"); fprintf(stdout, "\n"); } void print_version( void ) { fprintf(stdout, "%s: - %s - $Id: ez-ipupdate.c,v 1.39 2000/12/17 19:30:06 amackay Exp $\n", program_name, VERSION); } void print_credits( void ) { fprintf( stdout, "AUTHORS / CONTRIBUTORS\n" " Angus Mackay <amackay@gusnet.cx>\n" " Jeremy Bopp <jbopp@mail.utexas.edu>\n" " Mark Jeftovic <markjr@easydns.com>\n" " Stefaan Ponnet <webmaster@dyns.cx>\n" "\n" ); } void print_signalhelp( void ) { fprintf(stdout, "\nsignals are only really used when in daemon mode.\n\n"); fprintf(stdout, "signals: \n"); fprintf(stdout, " HUP\t\tcauses it to re-read its config file\n"); fprintf(stdout, " TERM\t\twake up and possibly perform an update\n"); fprintf(stdout, " QUIT\t\tshutdown\n"); fprintf(stdout, "\n"); } #if HAVE_SIGNAL_H RETSIGTYPE sigint_handler(int sig) { char message[] = "interupted.\n"; close(client_sockfd); write(2, message, sizeof(message)-1); #if HAVE_GETPID if(pid_file) { pid_file_delete(pid_file); } #endif exit(1); } RETSIGTYPE generic_sig_handler(int sig) { last_sig = sig; } #endif /* * like "chomp" in perl, take off trailing newline chars */ char *chomp(char *buf) { char *p; for(p=buf; *p != '\0'; p++); if(p != buf) { p--; } while(p>=buf && (*p == '\n' || *p == '\r')) { *p-- = '\0'; } return(buf); } /* * show_message * * if we are running in daemon mode then log to syslog, if not just output to * stderr. * */ void show_message(char *fmt, ...) { va_list args; va_start(args, fmt); if(options & OPT_DAEMON && !(options & OPT_FOREGROUND)) { char buf[MAX_MESSAGE_LEN]; #if defined(HAVE_VSPRINTF) || defined(HAVE_VSNPRINTF) vsnprintf(buf, sizeof(buf), fmt, args); #else sprintf(buf, "message incomplete because your OS sucks: %s\n", fmt); #endif syslog(LOG_NOTICE, buf); } else { #ifdef HAVE_VFPRINTF vfprintf(stderr, fmt, args); #else fprintf(stderr, "message incomplete because your OS sucks: %s\n", fmt); #endif } va_end(args); } int option_handler(int id, char *optarg) { #if HAVE_PWD_H && HAVE_GRP_H struct passwd *pw; #endif char *tmp; int i; switch(id) { case CMD_address: if(address) { free(address); } address = strdup(optarg); dprintf((stderr, "address: %s\n", address)); break; case CMD_daemon: options |= OPT_DAEMON; dprintf((stderr, "daemon mode\n")); break; case CMD_debug: #ifdef DEBUG options |= OPT_DEBUG; dprintf((stderr, "debugging on\n")); #else fprintf(stderr, "debugging was not enabled at compile time\n"); #endif break; case CMD_execute: #if defined(HAVE_WAITPID) || defined(HAVE_WAIT) if(post_update_cmd) { free(post_update_cmd); } post_update_cmd = malloc(strlen(optarg) + 1 + ARGLENGTH + 1); post_update_cmd_arg = post_update_cmd + strlen(optarg) + 1; sprintf(post_update_cmd, "%s ", optarg); dprintf((stderr, "post_update_cmd: %s\n", post_update_cmd)); #else fprintf(stderr, "command execution not enabled at compile time\n"); exit(1); #endif break; case CMD_foreground: options |= OPT_FOREGROUND; dprintf((stderr, "fork()ing off\n")); break; case CMD_pid_file: #if HAVE_GETPID if(pid_file) { free(pid_file); } pid_file = strdup(optarg); dprintf((stderr, "pid file: %s\n", pid_file)); #else fprintf(stderr, "pid file support not enabled at compile time\n"); #endif break; case CMD_host: if(host) { free(host); } host = strdup(optarg); dprintf((stderr, "host: %s\n", host)); break; case CMD_interface: #ifdef IF_LOOKUP if(interface) { free(interface); } interface = strdup(optarg); dprintf((stderr, "interface: %s\n", interface)); #else fprintf(stderr, "interface lookup not enabled at compile time\n"); exit(1); #endif break; case CMD_mx: if(mx) { free(mx); } mx = strdup(optarg); dprintf((stderr, "mx: %s\n", mx)); break; case CMD_max_interval: max_interval = atoi(optarg); if(max_interval < MIN_MAXINTERVAL) { fprintf(stderr, "WARNING: max-interval of %d is too short, using %d\n", max_interval, MIN_MAXINTERVAL); max_interval = MIN_MAXINTERVAL; } dprintf((stderr, "max_interval: %d\n", max_interval)); break; case CMD_notify_email: if(notify_email) { free(notify_email); } notify_email = strdup(optarg); dprintf((stderr, "notify_email: %s\n", notify_email)); break; case CMD_period: update_period = atoi(optarg); if(update_period < MIN_UPDATE_PERIOD) { fprintf(stderr, "WARNING: period of %d is too short, using %d\n", update_period, MIN_UPDATE_PERIOD); update_period = MIN_UPDATE_PERIOD; } dprintf((stderr, "update_period: %d\n", update_period)); break; case CMD_resolv_period: resolv_period = atoi(optarg); if(resolv_period < 1) { fprintf(stderr, "WARNING: period of %d is too short, using %d\n", resolv_period, 1); resolv_period = 1; } dprintf((stderr, "resolv_period: %d\n", resolv_period)); break; case CMD_quiet: options |= OPT_QUIET; dprintf((stderr, "quiet mode\n")); break; case CMD_retrys: ntrys = atoi(optarg); dprintf((stderr, "ntrys: %d\n", ntrys)); break; case CMD_server: if(server) { free(server); } server = strdup(optarg); tmp = strchr(server, ':'); if(tmp) { *tmp++ = '\0'; if(port) { free(port); } port = strdup(tmp); } dprintf((stderr, "server: %s\n", server)); dprintf((stderr, "port: %s\n", port)); break; case CMD_service_type: if(strcmp("ezip", optarg) == 0 || strcmp("ez-ip", optarg) == 0) { service = &EZIP_service; } else if(strcmp("pgpow", optarg) == 0 || strcmp("penguinpowered", optarg) == 0) { service = &PGPOW_service; } else if(strcmp("dhs", optarg) == 0) { service = &DHS_service; } else if(strcmp("dyndns", optarg) == 0) { service = &DYNDNS_service; } else if(strcmp("dyndns-stat", optarg) == 0 || strcmp("dyndns-static", optarg) == 0 || strcmp("statdns", optarg) == 0) { service = &DYNDNS_STAT_service; } else if(strcmp("ods", optarg) == 0) { service = &ODS_service; } else if(strcmp("tzo", optarg) == 0) { service = &TZO_service; } else if(strcmp("easydns", optarg) == 0) { service = &EASYDNS_service; } #ifdef USE_MD5 else if(strcmp("gnudip", optarg) == 0) { service = &GNUDIP_service; } #endif else if(strcmp("justlinux", optarg) == 0) { service = &JUSTL_service; } else if(strcmp("dyns", optarg) == 0) { service = &DYNS_service; } else if(strcmp("hn", optarg) == 0) { service = &HN_service; } else { fprintf(stderr, "unknown service type: %s\n", optarg); fprintf(stderr, "try one of: %s\n", SERVICES_STR); exit(1); } service_set = 1; dprintf((stderr, "service_type: %s\n", service->name)); dprintf((stderr, "service->type: %d\n", service->type)); break; case CMD_user: strncpy(user, optarg, sizeof(user)); user[sizeof(user)-1] = '\0'; dprintf((stderr, "user: %s\n", user)); tmp = strchr(optarg, ':'); if(tmp) { tmp++; while(*tmp) { *tmp++ = '*'; } } break; case CMD_run_as_user: #if HAVE_PWD_H && HAVE_GRP_H if((pw=getpwnam(optarg)) == NULL) { i = atoi(optarg); } else { if(setgid(pw->pw_gid) != 0) { fprintf(stderr, "error changing group id\n"); } dprintf((stderr, "GID now %d\n", pw->pw_gid)); i = pw->pw_uid; } if(setuid(i) != 0) { fprintf(stderr, "error changing user id\n"); } dprintf((stderr, "UID now %d\n", i)); #else fprintf(stderr, "option \"daemon-user\" not supported on this system\n"); #endif break; case CMD_url: if(url) { free(url); } url = strdup(optarg); dprintf((stderr, "url: %s\n", url)); break; case CMD_wildcard: wildcard = 1; dprintf((stderr, "wildcard: %d\n", wildcard)); break; case CMD_cloak_title: if(cloak_title) { free(cloak_title); } cloak_title = strdup(optarg); dprintf((stderr, "cloak_title: %s\n", cloak_title)); break; case CMD_timeout: timeout.tv_sec = atoi(optarg); timeout.tv_usec = (atof(optarg) - timeout.tv_sec) * 1000000L; dprintf((stderr, "timeout: %ld.%06ld\n", timeout.tv_sec, timeout.tv_usec)); break; case CMD_connection_type: connection_type = atoi(optarg); dprintf((stderr, "connection_type: %d\n", connection_type)); break; case CMD_cache_file: if(cache_file) { free(cache_file); } cache_file = strdup(optarg); dprintf((stderr, "cache_file: %s\n", cache_file)); break; default: dprintf((stderr, "case not handled: %d\n", id)); break; } return 0; } int conf_handler(struct conf_cmd *cmd, char *arg) { return(option_handler(cmd->id, arg)); } #ifdef HAVE_GETOPT_LONG # define xgetopt( x1, x2, x3, x4, x5 ) getopt_long( x1, x2, x3, x4, x5 ) #else # define xgetopt( x1, x2, x3, x4, x5 ) getopt( x1, x2, x3 ) #endif void parse_args( int argc, char **argv ) { #ifdef HAVE_GETOPT_LONG struct option long_options[] = { {"address", required_argument, 0, 'a'}, {"cache-file", required_argument, 0, 'b'}, {"config_file", required_argument, 0, 'c'}, {"config-file", required_argument, 0, 'c'}, {"daemon", no_argument, 0, 'd'}, {"debug", no_argument, 0, 'D'}, {"execute", required_argument, 0, 'e'}, {"foreground", no_argument, 0, 'f'}, {"pid-file", required_argument, 0, 'F'}, {"host", required_argument, 0, 'h'}, {"interface", required_argument, 0, 'i'}, {"cloak_title", required_argument, 0, 'L'}, {"mx", required_argument, 0, 'm'}, {"max-interval", required_argument, 0, 'M'}, {"notify-email", required_argument, 0, 'N'}, {"resolv-period", required_argument, 0, 'p'}, {"period", required_argument, 0, 'P'}, {"quiet", no_argument, 0, 'q'}, {"retrys", required_argument, 0, 'r'}, {"run-as-user", required_argument, 0, 'R'}, {"server", required_argument, 0, 's'}, {"service-type", required_argument, 0, 'S'}, {"timeout", required_argument, 0, 't'}, {"connection-type", required_argument, 0, 'T'}, {"url", required_argument, 0, 'U'}, {"user", required_argument, 0, 'u'}, {"wildcard", no_argument, 0, 'w'}, {"help", no_argument, 0, 'H'}, {"version", no_argument, 0, 'V'}, {"credits", no_argument, 0, 'C'}, {"signalhelp", no_argument, 0, 'Z'}, {0,0,0,0} }; #else # define long_options NULL #endif int opt; while((opt=xgetopt(argc, argv, "a:b:c:dDe:fF:h:i:L:m:M:N:p:P:qr:R:s:S:t:T:U:u:wHVCZ", long_options, NULL)) != -1) { switch (opt) { case 'a': option_handler(CMD_address, optarg); break; case 'b': option_handler(CMD_cache_file, optarg); break; case 'c': if(config_file) { free(config_file); } config_file = strdup(optarg); dprintf((stderr, "config_file: %s\n", config_file)); if(config_file) { if(parse_conf_file(config_file, conf_commands) != 0) { fprintf(stderr, "error parsing config file \"%s\"\n", config_file); exit(1); } } break; case 'd': option_handler(CMD_daemon, optarg); break; case 'D': option_handler(CMD_debug, optarg); break; case 'e': option_handler(CMD_execute, optarg); break; case 'f': option_handler(CMD_foreground, optarg); break; case 'F': option_handler(CMD_pid_file, optarg); break; case 'h': option_handler(CMD_host, optarg); break; case 'i': option_handler(CMD_interface, optarg); break; case 'L': option_handler(CMD_cloak_title, optarg); break; case 'm': option_handler(CMD_mx, optarg); break; case 'M': option_handler(CMD_max_interval, optarg); break; case 'N': option_handler(CMD_notify_email, optarg); break; case 'p': option_handler(CMD_resolv_period, optarg); break; case 'P': option_handler(CMD_period, optarg); break; case 'q': option_handler(CMD_quiet, optarg); break; case 'r': option_handler(CMD_retrys, optarg); break; case 'R': option_handler(CMD_run_as_user, optarg); break; case 's': option_handler(CMD_server, optarg); break; case 'S': option_handler(CMD_service_type, optarg); break; case 't': option_handler(CMD_timeout, optarg); break; case 'T': option_handler(CMD_connection_type, optarg); break; case 'u': option_handler(CMD_user, optarg); break; case 'U': option_handler(CMD_url, optarg); break; case 'w': option_handler(CMD_wildcard, optarg); break; case 'H': print_usage(); exit(0); break; case 'V': print_version(); exit(0); break; case 'C': print_credits(); exit(0); break; case 'Z': print_signalhelp(); exit(0); break; default: #ifdef HAVE_GETOPT_LONG fprintf(stderr, "Try `%s --help' for more information\n", argv[0]); #else fprintf(stderr, "Try `%s -H' for more information\n", argv[0]); fprintf(stderr, "warning: this program was compilied without getopt_long\n"); fprintf(stderr, " as such all long options will not work!\n"); #endif exit(1); break; } } } /* * do_connect * * connect a socket and return the file descriptor * */ int do_connect(int *sock, char *host, char *port) { struct sockaddr_in address; int len; int result; struct hostent *hostinfo; struct servent *servinfo; // set up the socket if((*sock=socket(AF_INET, SOCK_STREAM, 0)) == -1) { if(!(options & OPT_QUIET)) { perror("socket"); } return(-1); } address.sin_family = AF_INET; // get the host address hostinfo = gethostbyname(host); if(!hostinfo) { if(!(options & OPT_QUIET)) { herror("gethostbyname"); } close(*sock); return(-1); } address.sin_addr = *(struct in_addr *)*hostinfo -> h_addr_list; // get the host port servinfo = getservbyname(port, "tcp"); if(servinfo) { address.sin_port = servinfo -> s_port; } else { address.sin_port = htons(atoi(port)); } // connect the socket len = sizeof(address); if((result=connect(*sock, (struct sockaddr *)&address, len)) == -1) { if(!(options & OPT_QUIET)) { perror("connect"); } close(*sock); return(-1); } // print out some info if(!(options & OPT_QUIET)) { fprintf(stderr, "connected to %s (%s) on port %d.\n", host, inet_ntoa(address.sin_addr), ntohs(address.sin_port)); } return 0; } static char table64[]= "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"; void base64Encode(char *intext, char *output) { unsigned char ibuf[3]; unsigned char obuf[4]; int i; int inputparts; while(*intext) { for (i = inputparts = 0; i < 3; i++) { if(*intext) { inputparts++; ibuf[i] = *intext; intext++; } else ibuf[i] = 0; } obuf [0] = (ibuf [0] & 0xFC) >> 2; obuf [1] = ((ibuf [0] & 0x03) << 4) | ((ibuf [1] & 0xF0) >> 4); obuf [2] = ((ibuf [1] & 0x0F) << 2) | ((ibuf [2] & 0xC0) >> 6); obuf [3] = ibuf [2] & 0x3F; switch(inputparts) { case 1: /* only one byte read */ sprintf(output, "%c%c==", table64[obuf[0]], table64[obuf[1]]); break; case 2: /* two bytes read */ sprintf(output, "%c%c%c=", table64[obuf[0]], table64[obuf[1]], table64[obuf[2]]); break; default: sprintf(output, "%c%c%c%c", table64[obuf[0]], table64[obuf[1]], table64[obuf[2]], table64[obuf[3]] ); break; } output += 4; } *output=0; } #if IF_LOOKUP # if !defined(HAVE_INET_ATON) # if defined(HAVE_INET_ADDR) int inet_aton(const char *cp, struct in_addr *inp) { (*inp).s_addr = inet_addr(cp); } # else # error "sorry, can't compile with IF_LOOKUP and no inet_aton" # endif #endif #endif void output(void *buf) { fd_set writefds; int max_fd; struct timeval tv; int ret; dprintf((stderr, "I say: %s\n", (char *)buf)); // set up our fdset and timeout FD_ZERO(&writefds); FD_SET(client_sockfd, &writefds); max_fd = client_sockfd; memcpy(&tv, &timeout, sizeof(struct timeval)); ret = select(max_fd + 1, NULL, &writefds, NULL, &tv); dprintf((stderr, "ret: %d\n", ret)); if(ret == -1) { dprintf((stderr, "select: %s\n", error_string)); } else if(ret == 0) { fprintf(stderr, "timeout\n"); } else { /* if we woke up on client_sockfd do the data passing */ if(FD_ISSET(client_sockfd, &writefds)) { if(send(client_sockfd, buf, strlen(buf), 0) == -1) { fprintf(stderr, "error send()ing request: %s\n", error_string); } } else { dprintf((stderr, "error: case not handled.")); } } } int read_input(void *buf, int len) { fd_set readfds; int max_fd; struct timeval tv; int ret; int bread = -1; // set up our fdset and timeout FD_ZERO(&readfds); FD_SET(client_sockfd, &readfds); max_fd = client_sockfd; memcpy(&tv, &timeout, sizeof(struct timeval)); ret = select(max_fd + 1, &readfds, NULL, NULL, &tv); dprintf((stderr, "ret: %d\n", ret)); if(ret == -1) { dprintf((stderr, "select: %s\n", error_string)); } else if(ret == 0) { fprintf(stderr, "timeout\n"); } else { /* if we woke up on client_sockfd do the data passing */ if(FD_ISSET(client_sockfd, &readfds)) { if((bread=recv(client_sockfd, buf, len, 0)) == -1) { fprintf(stderr, "error recv()ing reply: %s\n", error_string); } } else { dprintf((stderr, "error: case not handled.")); } } return(bread); } int get_if_addr(int sock, char *name, struct sockaddr_in *sin) { #ifdef IF_LOOKUP struct ifreq ifr; memset(&ifr, 0, sizeof(ifr)); strcpy(ifr.ifr_name, name); /* why does this need to be done twice? */ if(ioctl(sock, SIOCGIFADDR, &ifr) < 0) { perror("ioctl(SIOCGIFADDR)"); memset(sin, 0, sizeof(struct sockaddr_in)); dprintf((stderr, "%s: %s\n", name, "unknown interface")); return -1; } if(ioctl(sock, SIOCGIFADDR, &ifr) < 0) { perror("ioctl(SIOCGIFADDR)"); memset(sin, 0, sizeof(struct sockaddr_in)); dprintf((stderr, "%s: %s\n", name, "unknown interface")); return -1; } if(ifr.ifr_addr.sa_family == AF_INET) { memcpy(sin, &(ifr.ifr_addr), sizeof(struct sockaddr_in)); dprintf((stderr, "%s: %s\n", name, inet_ntoa(sin->sin_addr))); return 0; } else { memset(sin, 0, sizeof(struct sockaddr_in)); dprintf((stderr, "%s: %s\n", name, "could not resolve interface")); return -1; } return -1; #else return -1; #endif } static int PGPOW_read_response(char *buf) { int bytes; bytes = read_input(buf, BUFFER_SIZE); if(bytes < 1) { close(client_sockfd); return(-1); } buf[bytes] = '\0'; dprintf((stderr, "server says: %s\n", buf)); if(strncmp("OK", buf, 2) != 0) { return(1); } else { return(0); } } static int ODS_read_response(char *buf) { int bytes; bytes = read_input(buf, BUFFER_SIZE); if(bytes < 1) { close(client_sockfd); return(-1); } buf[bytes] = '\0'; dprintf((stderr, "server says: %s\n", buf)); return(atoi(buf)); } int NULL_check_info(void) { char buf[64]; if(options & OPT_DAEMON) { fprintf(stderr, "no compile time default service was set therefor you must " "specify a service type.\n"); return(-1); } printf("service: "); *buf = '\0'; fgets(buf, sizeof(buf), stdin); chomp(buf); option_handler(CMD_service_type, buf); return(0); } int EZIP_check_info(void) { warn_fields(service->fields_used); return 0; } int EZIP_update_entry(void) { char buf[BUFFER_SIZE+1]; char *bp = buf; int bytes; int btot; int ret; buf[BUFFER_SIZE] = '\0'; if(do_connect((int*)&client_sockfd, server, port) != 0) { if(!(options & OPT_QUIET)) { show_message("error connecting to %s:%s\n", server, port); } return(UPDATERES_ERROR); } snprintf(buf, BUFFER_SIZE, "GET %s?mode=update&", request); output(buf); if(address) { snprintf(buf, BUFFER_SIZE, "%s=%s&", "ipaddress", address); output(buf); } snprintf(buf, BUFFER_SIZE, "%s=%s&", "wildcard", wildcard ? "yes" : "no"); output(buf); snprintf(buf, BUFFER_SIZE, "%s=%s&", "mx", mx); output(buf); snprintf(buf, BUFFER_SIZE, "%s=%s&", "url", url); output(buf); snprintf(buf, BUFFER_SIZE, "%s=%s&", "host", host); output(buf); snprintf(buf, BUFFER_SIZE, " HTTP/1.0\015\012"); output(buf); snprintf(buf, BUFFER_SIZE, "Authorization: Basic %s\015\012", auth); output(buf); snprintf(buf, BUFFER_SIZE, "User-Agent: %s-%s %s (%s)\015\012", "ez-update", VERSION, OS, "by Angus Mackay"); output(buf); snprintf(buf, BUFFER_SIZE, "Host: %s\015\012", server); output(buf); snprintf(buf, BUFFER_SIZE, "\015\012"); output(buf); bp = buf; bytes = 0; btot = 0; while((bytes=read_input(bp, BUFFER_SIZE-btot)) > 0) { bp += bytes; btot += bytes; dprintf((stderr, "btot: %d\n", btot)); } close(client_sockfd); buf[btot] = '\0'; dprintf((stderr, "server output: %s\n", buf)); if(sscanf(buf, " HTTP/1.%*c %3d", &ret) != 1) { ret = -1; } switch(ret) { case -1: if(!(options & OPT_QUIET)) { show_message("strange server response, are you connecting to the right server?\n"); } return(UPDATERES_ERROR); break; case 200: if(!(options & OPT_QUIET)) { printf("request successful\n"); } break; case 401: if(!(options & OPT_QUIET)) { show_message("authentication failure\n"); } return(UPDATERES_SHUTDOWN); break; default: if(!(options & OPT_QUIET)) { // reuse the auth buffer *auth = '\0'; sscanf(buf, " HTTP/1.%*c %*3d %255[^\r\n]", auth); show_message("unknown return code: %d\n", ret); fprintf(stderr, "server response: %s\n", auth); } return(UPDATERES_ERROR); break; } return(UPDATERES_OK); } int DYNDNS_check_info(void) { char buf[BUFSIZ+1]; if((host == NULL) || (*host == '\0')) { if(options & OPT_DAEMON) { return(-1); } if(host) { free(host); } printf("host: "); *buf = '\0'; fgets(buf, BUFSIZ, stdin); host = strdup(buf); chomp(host); } if(interface == NULL && address == NULL) { if(options & OPT_DAEMON) { fprintf(stderr, "you must provide either an interface or an address\n"); return(-1); } if(interface) { free(interface); } printf("interface: "); *buf = '\0'; fgets(buf, BUFSIZ, stdin); chomp(buf); option_handler(CMD_interface, buf); } warn_fields(service->fields_used); return 0; } int DYNDNS_update_entry(void) { char buf[BUFFER_SIZE+1]; char *bp = buf; int bytes; int btot; int ret; int retval = UPDATERES_OK; buf[BUFFER_SIZE] = '\0'; if(do_connect((int*)&client_sockfd, server, port) != 0) { if(!(options & OPT_QUIET)) { show_message("error connecting to %s:%s\n", server, port); } return(UPDATERES_ERROR); } snprintf(buf, BUFFER_SIZE, "GET %s?", request); output(buf); if(service->type == SERV_DYNDNS_STAT) { snprintf(buf, BUFFER_SIZE, "%s=%s&", "system", "statdns"); output(buf); } snprintf(buf, BUFFER_SIZE, "%s=%s&", "hostname", host); output(buf); if(address != NULL) { snprintf(buf, BUFFER_SIZE, "%s=%s&", "myip", address); output(buf); } snprintf(buf, BUFFER_SIZE, "%s=%s&", "wildcard", wildcard ? "ON" : "OFF"); output(buf); if(mx != NULL && *mx != '\0') { snprintf(buf, BUFFER_SIZE, "%s=%s&", "mx", mx); output(buf); } //snprintf(buf, BUFFER_SIZE, "%s=%s&", "backmx", "NO"); //output(buf); snprintf(buf, BUFFER_SIZE, " HTTP/1.0\015\012"); output(buf); snprintf(buf, BUFFER_SIZE, "Authorization: Basic %s\015\012", auth); output(buf); snprintf(buf, BUFFER_SIZE, "User-Agent: %s-%s %s (%s)\015\012", "ez-update", VERSION, OS, "by Angus Mackay"); output(buf); snprintf(buf, BUFFER_SIZE, "Host: %s\015\012", server); output(buf); snprintf(buf, BUFFER_SIZE, "\015\012"); output(buf); bp = buf; bytes = 0; btot = 0; while((bytes=read_input(bp, BUFFER_SIZE-btot)) > 0) { bp += bytes; btot += bytes; dprintf((stderr, "btot: %d\n", btot)); } close(client_sockfd); buf[btot] = '\0'; dprintf((stderr, "server output: %s\n", buf)); if(sscanf(buf, " HTTP/1.%*c %3d", &ret) != 1) { ret = -1; } switch(ret) { case -1: if(!(options & OPT_QUIET)) { show_message("strange server response, are you connecting to the right server?\n"); } retval = UPDATERES_ERROR; break; case 200: if(strstr(buf, "\ngood ") != NULL) { if(!(options & OPT_QUIET)) { printf("request successful\n"); } } else { if(strstr(buf, "\nnohost") != NULL) { show_message("invalid hostname: %s\n", host); retval = UPDATERES_SHUTDOWN; } else if(strstr(buf, "\nnotfqdn") != NULL) { show_message("malformed hostname: %s\n", host); retval = UPDATERES_SHUTDOWN; } else if(strstr(buf, "\n!yours") != NULL) { show_message("host \"%s\" is not under your control\n", host); retval = UPDATERES_SHUTDOWN; } else if(strstr(buf, "\nabuse") != NULL) { show_message("host \"%s\" has been blocked for abuse\n", host); retval = UPDATERES_SHUTDOWN; } else if(strstr(buf, "\nnochg") != NULL) { show_message("%s says that your IP address has not changed since the last update\n", server); // lets say that this counts as a successful update // but we'll roll back the last update time to max_interval/2 if(max_interval > 0) { last_update = time(NULL) - max_interval/2; } retval = UPDATERES_OK; } else if(strstr(buf, "\nbadauth") != NULL) { show_message("authentication failure\n"); retval = UPDATERES_SHUTDOWN; } else if(strstr(buf, "\nnumhost") != NULL) { show_message("Too many or too few hosts found\n"); retval = UPDATERES_SHUTDOWN; } else if(strstr(buf, "\ndnserr") != NULL) { char *p = strstr(buf, "\ndnserr"); show_message("dyndns internal error, please report this number to " "their support people: %s\n", N_STR(p)); retval = UPDATERES_ERROR; } else if(strstr(buf, "\n911") != NULL) { show_message("Ahhhh! call 911!\n"); retval = UPDATERES_SHUTDOWN; } else if(strstr(buf, "\n999") != NULL) { show_message("Ahhhh! call 999!\n"); retval = UPDATERES_SHUTDOWN; } // this one should be last as it is a stupid string to signify waits // with as it is so short else if(strstr(buf, "\nw") != NULL) { int howlong = 0; char *p = strstr(buf, "\nw"); char reason[256]; char mult; // get time and reason if(strlen(p) >= 2) { sscanf(p, "%d%c %255[^\r\n]", &howlong, &mult, reason); if(mult == 'h') { howlong *= 3600; } else if(mult == 'm') { howlong *= 60; } if(howlong > MAX_WAITRESPONSE_WAIT) { howlong = MAX_WAITRESPONSE_WAIT; }; } else { sprintf(reason, "problem parsing reason for wait response"); } show_message("Wait response received, waiting for %d seconds before next update.\n", howlong); show_message("Wait response reason: %d\n", N_STR(reason)); sleep(howlong); retval = UPDATERES_ERROR; } else { show_message("error processing request\n"); if(!(options & OPT_QUIET)) { fprintf(stderr, "==== server output: ====\n%s\n", buf); } retval = UPDATERES_ERROR; } } break; case 401: if(!(options & OPT_QUIET)) { show_message("authentication failure\n"); } retval = UPDATERES_SHUTDOWN; break; default: if(!(options & OPT_QUIET)) { // reuse the auth buffer *auth = '\0'; sscanf(buf, " HTTP/1.%*c %*3d %255[^\r\n]", auth); show_message("unknown return code: %d\n", ret); fprintf(stderr, "server response: %s\n", auth); } retval = UPDATERES_ERROR; break; } return(retval); } int PGPOW_check_info(void) { char buf[BUFSIZ+1]; if((host == NULL) || (*host == '\0')) { if(options & OPT_DAEMON) { return(-1); } if(host) { free(host); } printf("host: "); *buf = '\0'; fgets(buf, BUFSIZ, stdin); host = strdup(buf); chomp(host); } if(interface == NULL && address == NULL) { if(options & OPT_DAEMON) { fprintf(stderr, "you must provide either an interface or an address\n"); return(-1); } if(interface) { free(interface); } printf("interface: "); *buf = '\0'; fgets(buf, BUFSIZ, stdin); chomp(buf); option_handler(CMD_interface, buf); } warn_fields(service->fields_used); return 0; } int PGPOW_update_entry(void) { char buf[BUFFER_SIZE+1]; buf[BUFFER_SIZE] = '\0'; if(do_connect((int*)&client_sockfd, server, port) != 0) { if(!(options & OPT_QUIET)) { show_message("error connecting to %s:%s\n", server, port); } return(UPDATERES_ERROR); } /* read server message */ if(PGPOW_read_response(buf) != 0) { show_message("strange server response, are you connecting to the right server?\n"); close(client_sockfd); return(UPDATERES_ERROR); } /* send version command */ snprintf(buf, BUFFER_SIZE, "VER %s [%s-%s %s (%s)]\015\012", PGPOW_VERSION, "ez-update", VERSION, OS, "by Angus Mackay"); output(buf); if(PGPOW_read_response(buf) != 0) { if(strncmp("ERR", buf, 3) == 0) { show_message("error talking to server: %s\n", &(buf[3])); } else { show_message("error talking to server:\n\t%s\n", buf); } close(client_sockfd); return(UPDATERES_ERROR); } /* send user command */ snprintf(buf, BUFFER_SIZE, "USER %s\015\012", user_name); output(buf); if(PGPOW_read_response(buf) != 0) { if(strncmp("ERR", buf, 3) == 0) { show_message("error talking to server: %s\n", &(buf[3])); } else { show_message("error talking to server:\n\t%s\n", buf); } close(client_sockfd); return(UPDATERES_ERROR); } /* send pass command */ snprintf(buf, BUFFER_SIZE, "PASS %s\015\012", password); output(buf); if(PGPOW_read_response(buf) != 0) { if(strncmp("ERR", buf, 3) == 0) { show_message("error talking to server: %s\n", &(buf[3])); } else { show_message("error talking to server:\n\t%s\n", buf); } close(client_sockfd); return(UPDATERES_ERROR); } /* send host command */ snprintf(buf, BUFFER_SIZE, "HOST %s\015\012", host); output(buf); if(PGPOW_read_response(buf) != 0) { if(strncmp("ERR", buf, 3) == 0) { show_message("error talking to server: %s\n", &(buf[3])); } else { show_message("error talking to server:\n\t%s\n", buf); } close(client_sockfd); return(UPDATERES_ERROR); } /* send oper command */ snprintf(buf, BUFFER_SIZE, "OPER %s\015\012", request); output(buf); if(PGPOW_read_response(buf) != 0) { if(strncmp("ERR", buf, 3) == 0) { show_message("error talking to server: %s\n", &(buf[3])); } else { show_message("error talking to server:\n\t%s\n", buf); } close(client_sockfd); return(UPDATERES_ERROR); } if(strcmp("update", request) == 0) { /* send ip command */ snprintf(buf, BUFFER_SIZE, "IP %s\015\012", address); output(buf); if(PGPOW_read_response(buf) != 0) { if(strncmp("ERR", buf, 3) == 0) { show_message("error talking to server: %s\n", &(buf[3])); } else { show_message("error talking to server:\n\t%s\n", buf); } close(client_sockfd); return(UPDATERES_ERROR); } } /* send done command */ snprintf(buf, BUFFER_SIZE, "DONE\015\012"); output(buf); if(PGPOW_read_response(buf) != 0) { if(strncmp("ERR", buf, 3) == 0) { show_message("error talking to server: %s\n", &(buf[3])); } else { show_message("error talking to server:\n\t%s\n", buf); } close(client_sockfd); return(UPDATERES_ERROR); } if(!(options & OPT_QUIET)) { printf("request successful\n"); } close(client_sockfd); return(UPDATERES_OK); } int DHS_check_info(void) { char buf[BUFSIZ+1]; if((host == NULL) || (*host == '\0')) { if(options & OPT_DAEMON) { return(-1); } if(host) { free(host); } printf("host: "); fgets(buf, BUFSIZ, stdin); host = strdup(buf); chomp(host); } if(interface == NULL && address == NULL) { if(options & OPT_DAEMON) { fprintf(stderr, "you must provide either an interface or an address\n"); return(-1); } if(interface) { free(interface); } printf("interface: "); *buf = '\0'; fgets(buf, BUFSIZ, stdin); chomp(buf); option_handler(CMD_interface, buf); } warn_fields(service->fields_used); return 0; } /* * grrrrr, it seems that dhs.org requires us to use POST * also DHS doesn't update both the mx record and the address at the same * time, this service really stinks. go with justlinix.com (penguinpowered) * instead, the only advantage is short host names. */ int DHS_update_entry(void) { char buf[BUFFER_SIZE+1]; char putbuf[BUFFER_SIZE+1]; char *bp = buf; int bytes; int btot; int ret; char *domain = NULL; char *hostname = NULL; char *p; int limit; int retval = UPDATERES_OK; buf[BUFFER_SIZE] = '\0'; putbuf[BUFFER_SIZE] = '\0'; /* parse apart the domain and hostname */ hostname = strdup(host); if((p=strchr(hostname, '.')) == NULL) { if(!(options & OPT_QUIET)) { show_message("error parsing hostname from host %s\n", host); } return(UPDATERES_ERROR); } *p = '\0'; p++; if(*p == '\0') { if(!(options & OPT_QUIET)) { show_message("error parsing domain from host %s\n", host); } return(UPDATERES_ERROR); } domain = strdup(p); dprintf((stderr, "hostname: %s, domain: %s\n", hostname, domain)); if(do_connect((int*)&client_sockfd, server, port) != 0) { if(!(options & OPT_QUIET)) { show_message("error connecting to %s:%s\n", server, port); } return(UPDATERES_ERROR); } snprintf(buf, BUFFER_SIZE, "POST %s HTTP/1.0\015\012", request); output(buf); snprintf(buf, BUFFER_SIZE, "Authorization: Basic %s\015\012", auth); output(buf); snprintf(buf, BUFFER_SIZE, "User-Agent: %s-%s %s (%s)\015\012", "ez-update", VERSION, OS, "by Angus Mackay"); output(buf); snprintf(buf, BUFFER_SIZE, "Host: %s\015\012", server); output(buf); p = putbuf; *p = '\0'; limit = BUFFER_SIZE - 1 - strlen(buf); snprintf(p, limit, "hostscmd=edit&hostscmdstage=2&type=4&"); p += strlen(p); limit = BUFFER_SIZE - 1 - strlen(buf); snprintf(p, limit, "%s=%s&", "updatetype", "Online"); p += strlen(p); limit = BUFFER_SIZE - 1 - strlen(buf); snprintf(p, limit, "%s=%s&", "ip", address); p += strlen(p); limit = BUFFER_SIZE - 1 - strlen(buf); snprintf(p, limit, "%s=%s&", "mx", mx); p += strlen(p); limit = BUFFER_SIZE - 1 - strlen(buf); snprintf(p, limit, "%s=%s&", "offline_url", url); p += strlen(p); limit = BUFFER_SIZE - 1 - strlen(buf); if(cloak_title) { snprintf(p, limit, "%s=%s&", "cloak", "Y"); p += strlen(p); limit = BUFFER_SIZE - 1 - strlen(buf); snprintf(p, limit, "%s=%s&", "cloak_title", cloak_title); p += strlen(p); limit = BUFFER_SIZE - 1 - strlen(buf); } else { snprintf(p, limit, "%s=%s&", "cloak_title", ""); p += strlen(p); limit = BUFFER_SIZE - 1 - strlen(buf); } snprintf(p, limit, "%s=%s&", "submit", "Update"); p += strlen(p); limit = BUFFER_SIZE - 1 - strlen(buf); snprintf(p, limit, "%s=%s&", "domain", domain); p += strlen(p); limit = BUFFER_SIZE - 1 - strlen(buf); snprintf(p, limit, "%s=%s", "hostname", hostname); p += strlen(p); limit = BUFFER_SIZE - 1 - strlen(buf); snprintf(buf, BUFFER_SIZE, "Content-length: %d\015\012", strlen(putbuf)); output(buf); snprintf(buf, BUFFER_SIZE, "\015\012"); output(buf); output(putbuf); snprintf(buf, BUFFER_SIZE, "\015\012"); output(buf); bp = buf; bytes = 0; btot = 0; while((bytes=read_input(bp, BUFFER_SIZE-btot)) > 0) { bp += bytes; btot += bytes; dprintf((stderr, "btot: %d\n", btot)); } close(client_sockfd); buf[btot] = '\0'; dprintf((stderr, "server output: %s\n", buf)); if(sscanf(buf, " HTTP/1.%*c %3d", &ret) != 1) { ret = -1; } switch(ret) { case -1: if(!(options & OPT_QUIET)) { show_message("strange server response, are you connecting to the right server?\n"); } retval = UPDATERES_ERROR; break; case 200: if(!(options & OPT_QUIET)) { printf("request successful\n"); } break; case 401: if(!(options & OPT_QUIET)) { show_message("authentication failure\n"); } retval = UPDATERES_SHUTDOWN; break; default: if(!(options & OPT_QUIET)) { // reuse the auth buffer *auth = '\0'; sscanf(buf, " HTTP/1.%*c %*3d %255[^\r\n]", auth); show_message("unknown return code: %d\n", ret); show_message("server response: %s\n", auth); } retval = UPDATERES_ERROR; break; } // this stupid service requires us to do seperate request if we want to // update the mail exchanger (mx). grrrrrr if(*mx != '\0') { // okay, dhs's service is incredibly stupid and will not work with two // requests right after each other. I could care less that this is ugly, // I personally will NEVER use dhs, it is laughable. sleep(DHS_SUCKY_TIMEOUT < timeout.tv_sec ? DHS_SUCKY_TIMEOUT : timeout.tv_sec); if(do_connect((int*)&client_sockfd, server, port) != 0) { if(!(options & OPT_QUIET)) { show_message("error connecting to %s:%s\n", server, port); } return(UPDATERES_ERROR); } snprintf(buf, BUFFER_SIZE, "POST %s HTTP/1.0\015\012", request); output(buf); snprintf(buf, BUFFER_SIZE, "Authorization: Basic %s\015\012", auth); output(buf); snprintf(buf, BUFFER_SIZE, "User-Agent: %s-%s %s (%s)\015\012", "ez-update", VERSION, OS, "by Angus Mackay"); output(buf); snprintf(buf, BUFFER_SIZE, "Host: %s\015\012", server); output(buf); p = putbuf; *p = '\0'; limit = BUFFER_SIZE - 1 - strlen(buf); snprintf(p, limit, "hostscmd=edit&hostscmdstage=2&type=4&"); p += strlen(p); limit = BUFFER_SIZE - 1 - strlen(buf); snprintf(p, limit, "%s=%s&", "updatetype", "Update+Mail+Exchanger"); p += strlen(p); limit = BUFFER_SIZE - 1 - strlen(buf); snprintf(p, limit, "%s=%s&", "ip", address); p += strlen(p); limit = BUFFER_SIZE - 1 - strlen(buf); snprintf(p, limit, "%s=%s&", "mx", mx); p += strlen(p); limit = BUFFER_SIZE - 1 - strlen(buf); snprintf(p, limit, "%s=%s&", "offline_url", url); p += strlen(p); limit = BUFFER_SIZE - 1 - strlen(buf); if(cloak_title) { snprintf(p, limit, "%s=%s&", "cloak", "Y"); p += strlen(p); limit = BUFFER_SIZE - 1 - strlen(buf); snprintf(p, limit, "%s=%s&", "cloak_title", cloak_title); p += strlen(p); limit = BUFFER_SIZE - 1 - strlen(buf); } else { snprintf(p, limit, "%s=%s&", "cloak_title", ""); p += strlen(p); limit = BUFFER_SIZE - 1 - strlen(buf); } snprintf(p, limit, "%s=%s&", "submit", "Update"); p += strlen(p); limit = BUFFER_SIZE - 1 - strlen(buf); snprintf(p, limit, "%s=%s&", "domain", domain); p += strlen(p); limit = BUFFER_SIZE - 1 - strlen(buf); snprintf(p, limit, "%s=%s", "hostname", hostname); p += strlen(p); limit = BUFFER_SIZE - 1 - strlen(buf); snprintf(buf, BUFFER_SIZE, "Content-length: %d\015\012", strlen(putbuf)); output(buf); snprintf(buf, BUFFER_SIZE, "\015\012"); output(buf); output(putbuf); snprintf(buf, BUFFER_SIZE, "\015\012"); output(buf); bp = buf; bytes = 0; btot = 0; while((bytes=read_input(bp, BUFFER_SIZE-btot)) > 0) { bp += bytes; btot += bytes; dprintf((stderr, "btot: %d\n", btot)); } close(client_sockfd); buf[btot] = '\0'; dprintf((stderr, "server output: %s\n", buf)); if(sscanf(buf, " HTTP/1.%*c %3d", &ret) != 1) { ret = -1; } switch(ret) { case -1: if(!(options & OPT_QUIET)) { show_message("strange server response, are you connecting to the right server?\n"); } retval = UPDATERES_ERROR; break; case 200: if(!(options & OPT_QUIET)) { printf("request successful\n"); } break; case 401: if(!(options & OPT_QUIET)) { show_message("authentication failure\n"); } retval = UPDATERES_SHUTDOWN; break; default: if(!(options & OPT_QUIET)) { // reuse the auth buffer *auth = '\0'; sscanf(buf, " HTTP/1.%*c %*3d %255[^\r\n]", auth); show_message("unknown return code: %d\n", ret); show_message("server response: %s\n", auth); } retval = UPDATERES_ERROR; break; } } return(retval); } int ODS_check_info(void) { char buf[BUFSIZ+1]; if((host == NULL) || (*host == '\0')) { if(options & OPT_DAEMON) { return(-1); } if(host) { free(host); } printf("host: "); *buf = '\0'; fgets(buf, BUFSIZ, stdin); host = strdup(buf); chomp(host); } if(interface == NULL && address == NULL) { if(options & OPT_DAEMON) { fprintf(stderr, "you must provide either an interface or an address\n"); return(-1); } if(interface) { free(interface); } printf("interface: "); *buf = '\0'; fgets(buf, BUFSIZ, stdin); chomp(buf); option_handler(CMD_interface, buf); } warn_fields(service->fields_used); return 0; } int ODS_update_entry(void) { char buf[BUFFER_SIZE+1]; int response; buf[BUFFER_SIZE] = '\0'; if(do_connect((int*)&client_sockfd, server, port) != 0) { if(!(options & OPT_QUIET)) { show_message("error connecting to %s:%s\n", server, port); } return(UPDATERES_ERROR); } /* read server message */ if(ODS_read_response(buf) != 100) { show_message("strange server response, are you connecting to the right server?\n"); close(client_sockfd); return(UPDATERES_ERROR); } /* send login command */ snprintf(buf, BUFFER_SIZE, "LOGIN %s %s\012", user_name, password); output(buf); response = ODS_read_response(buf); if(!(response == 225 || response == 226)) { if(strlen(buf) > 4) { show_message("error talking to server: %s\n", &(buf[4])); } else { show_message("error talking to server\n"); } close(client_sockfd); return(UPDATERES_ERROR); } /* send delete command */ snprintf(buf, BUFFER_SIZE, "DELRR %s A\012", host); output(buf); if(ODS_read_response(buf) != 0) { // what is the correct response to a DELRR? } /* send address command */ snprintf(buf, BUFFER_SIZE, "ADDRR %s A %s\012", host, address); output(buf); response = ODS_read_response(buf); if(!(response == 795 || response == 796)) { if(strlen(buf) > 4) { show_message("error talking to server: %s\n", &(buf[4])); } else { show_message("error talking to server\n"); } close(client_sockfd); return(UPDATERES_ERROR); } if(!(options & OPT_QUIET)) { printf("request successful\n"); } close(client_sockfd); return(UPDATERES_OK); } int TZO_check_info(void) { char buf[BUFSIZ+1]; if((host == NULL) || (*host == '\0')) { if(options & OPT_DAEMON) { return(-1); } if(host) { free(host); } printf("host: "); *buf = '\0'; fgets(buf, BUFSIZ, stdin); host = strdup(buf); chomp(host); } if(interface == NULL && address == NULL) { if(options & OPT_DAEMON) { fprintf(stderr, "you must provide either an interface or an address\n"); return(-1); } if(interface) { free(interface); } printf("interface: "); *buf = '\0'; fgets(buf, BUFSIZ, stdin); chomp(buf); option_handler(CMD_interface, buf); } warn_fields(service->fields_used); return 0; } int TZO_update_entry(void) { char buf[BUFFER_SIZE+1]; char *bp = buf; int bytes; int btot; int ret; buf[BUFFER_SIZE] = '\0'; if(do_connect((int*)&client_sockfd, server, port) != 0) { if(!(options & OPT_QUIET)) { show_message("error connecting to %s:%s\n", server, port); } return(UPDATERES_ERROR); } snprintf(buf, BUFFER_SIZE, "GET %s?", request); output(buf); snprintf(buf, BUFFER_SIZE, "%s=%s&", "TZOName", host); output(buf); snprintf(buf, BUFFER_SIZE, "%s=%s&", "Email", user_name); output(buf); snprintf(buf, BUFFER_SIZE, "%s=%s&", "TZOKey", password); output(buf); snprintf(buf, BUFFER_SIZE, "%s=%s&", "IPAddress", address); output(buf); snprintf(buf, BUFFER_SIZE, " HTTP/1.0\015\012"); output(buf); snprintf(buf, BUFFER_SIZE, "User-Agent: %s-%s %s (%s)\015\012", "ez-update", VERSION, OS, "by Angus Mackay"); output(buf); snprintf(buf, BUFFER_SIZE, "Host: %s\015\012", server); output(buf); snprintf(buf, BUFFER_SIZE, "\015\012"); output(buf); bp = buf; bytes = 0; btot = 0; while((bytes=read_input(bp, BUFFER_SIZE-btot)) > 0) { bp += bytes; btot += bytes; dprintf((stderr, "btot: %d\n", btot)); } close(client_sockfd); buf[btot] = '\0'; dprintf((stderr, "server output: %s\n", buf)); if(sscanf(buf, " HTTP/1.%*c %3d", &ret) != 1) { ret = -1; } switch(ret) { case -1: if(!(options & OPT_QUIET)) { show_message("strange server response, are you connecting to the right server?\n"); } return(UPDATERES_ERROR); break; case 200: if(!(options & OPT_QUIET)) { printf("request successful\n"); } break; case 302: // There is no neat way to determine the exact error other than to // parse the Location part of the mime header to find where we're // being redirected. if(!(options & OPT_QUIET)) { // reuse the auth buffer *auth = '\0'; bp = strstr(buf, "Location: "); if((bp < strstr(buf, "\r\n\r\n")) && (sscanf(bp, "Location: http://%*[^/]%255[^\r\n]", auth) == 1)) { bp = strrchr(auth, '/') + 1; } else { bp = ""; } dprintf((stderr, "location: %s\n", bp)); if(!(strncmp(bp, "domainmismatch.htm", strlen(bp)) && strncmp(bp, "invname.htm", strlen(bp)))) { show_message("invalid host name\n"); } else if(!strncmp(bp, "invkey.htm", strlen(bp))) { show_message("invalid password(tzo key)\n"); } else if(!(strncmp(bp, "emailmismatch.htm", strlen(bp)) && strncmp(bp, "invemail.htm", strlen(bp)))) { show_message("invalid user name(email address)\n"); } else { show_message("unknown error\n"); } } return(UPDATERES_ERROR); break; default: if(!(options & OPT_QUIET)) { // reuse the auth buffer *auth = '\0'; sscanf(buf, " HTTP/1.%*c %*3d %255[^\r\n]", auth); show_message("unknown return code: %d\n", ret); show_message("server response: %s\n", auth); } return(UPDATERES_ERROR); break; } return(UPDATERES_OK); } int EASYDNS_check_info(void) { char buf[BUFSIZ+1]; if((host == NULL) || (*host == '\0')) { if(options & OPT_DAEMON) { return(-1); } if(host) { free(host); } printf("host: "); *buf = '\0'; fgets(buf, BUFSIZ, stdin); host = strdup(buf); chomp(host); } if(interface == NULL && address == NULL) { if(options & OPT_DAEMON) { fprintf(stderr, "you must provide either an interface or an address\n"); return(-1); } if(interface) { free(interface); } printf("interface: "); *buf = '\0'; fgets(buf, BUFSIZ, stdin); chomp(buf); option_handler(CMD_interface, buf); } warn_fields(service->fields_used); return 0; } int EASYDNS_update_entry(void) { char buf[BUFFER_SIZE+1]; char *bp = buf; int bytes; int btot; int ret; buf[BUFFER_SIZE] = '\0'; if(do_connect((int*)&client_sockfd, server, port) != 0) { if(!(options & OPT_QUIET)) { show_message("error connecting to %s:%s\n", server, port); } return(UPDATERES_ERROR); } snprintf(buf, BUFFER_SIZE, "GET %s?action=edit&", request); output(buf); snprintf(buf, BUFFER_SIZE, "%s=%s&", "myip", address); output(buf); snprintf(buf, BUFFER_SIZE, "%s=%s&", "wildcard", wildcard ? "ON" : "OFF"); output(buf); snprintf(buf, BUFFER_SIZE, "%s=%s&", "mx", mx); output(buf); snprintf(buf, BUFFER_SIZE, "%s=%s&", "backmx", *mx == '\0' ? "NO" : "YES"); output(buf); snprintf(buf, BUFFER_SIZE, "%s=%s&", "host_id", host); output(buf); snprintf(buf, BUFFER_SIZE, " HTTP/1.0\015\012"); output(buf); snprintf(buf, BUFFER_SIZE, "Authorization: Basic %s\015\012", auth); output(buf); snprintf(buf, BUFFER_SIZE, "User-Agent: %s-%s %s (%s)\015\012", "ez-update", VERSION, OS, "by Angus Mackay"); output(buf); snprintf(buf, BUFFER_SIZE, "Host: %s\015\012", server); output(buf); snprintf(buf, BUFFER_SIZE, "\015\012"); output(buf); bp = buf; bytes = 0; btot = 0; while((bytes=read_input(bp, BUFFER_SIZE-btot)) > 0) { bp += bytes; btot += bytes; dprintf((stderr, "btot: %d\n", btot)); } close(client_sockfd); buf[btot] = '\0'; dprintf((stderr, "server output: %s\n", buf)); if(sscanf(buf, " HTTP/1.%*c %3d", &ret) != 1) { ret = -1; } switch(ret) { case -1: if(!(options & OPT_QUIET)) { show_message("strange server response, are you connecting to the right server?\n"); } return(UPDATERES_ERROR); break; case 200: if(strstr(buf, "NOERROR\n") != NULL) { if(!(options & OPT_QUIET)) { printf("request successful\n"); } } else { show_message("error processing request\n"); if(!(options & OPT_QUIET)) { fprintf(stderr, "server output: %s\n", buf); } return(UPDATERES_ERROR); } break; case 401: if(!(options & OPT_QUIET)) { show_message("authentication failure\n"); } return(UPDATERES_SHUTDOWN); break; default: if(!(options & OPT_QUIET)) { // reuse the auth buffer *auth = '\0'; sscanf(buf, " HTTP/1.%*c %*3d %255[^\r\n]", auth); show_message("unknown return code: %d\n", ret); show_message("server response: %s\n", auth); } return(UPDATERES_ERROR); break; } return(UPDATERES_OK); } #ifdef USE_MD5 int GNUDIP_check_info(void) { char buf[BUFSIZ+1]; if((server == NULL) || (*server == '\0')) { if(options & OPT_DAEMON) { return(-1); } if(server) { free(server); } printf("server: "); *buf = '\0'; fgets(buf, BUFSIZ, stdin); server = strdup(buf); chomp(server); } if((host == NULL) || (*host == '\0')) { if(options & OPT_DAEMON) { return(-1); } if(host) { free(host); } printf("host: "); *buf = '\0'; fgets(buf, BUFSIZ, stdin); host = strdup(buf); chomp(host); } if((address) && (strcmp(address, "0.0.0.0") != 0)) { if(!(options & OPT_QUIET)) { fprintf(stderr, "warning: for GNUDIP the \"address\" parpameter is only used if set to \"0.0.0.0\" thus making an offline request.\n"); } } warn_fields(service->fields_used); return 0; } int GNUDIP_update_entry(void) { unsigned char digestbuf[MD5_DIGEST_BYTES]; char buf[BUFFER_SIZE+1]; char *p; int bytes; int ret; int i; char *domainname; char gnudip_request[2]; // send an offline request if address 0.0.0.0 is used // otherwise, we ignore the address and send an update request gnudip_request[0] = strcmp(address, "0.0.0.0") == 0 ? '1' : '0'; gnudip_request[1] = '\0'; // find domainname for(p=host; *p != '\0' && *p != '.'; p++); if(*p != '\0') { p++; } if(*p == '\0') { return(UPDATERES_ERROR); } domainname = p; if(do_connect((int*)&client_sockfd, server, port) != 0) { if(!(options & OPT_QUIET)) { show_message("error connecting to %s:%s\n", server, port); } return(UPDATERES_ERROR); } if((bytes=read_input(buf, BUFFER_SIZE)) <= 0) { close(client_sockfd); return(UPDATERES_ERROR); } buf[bytes] = '\0'; dprintf((stderr, "bytes: %d\n", bytes)); dprintf((stderr, "server output: %s\n", buf)); // buf holds the shared secret chomp(buf); // use the auth buffer md5_buffer(password, strlen(password), digestbuf); for(i=0, p=auth; i<MD5_DIGEST_BYTES; i++, p+=2) { sprintf(p, "%02x", digestbuf[i]); } strncat(auth, ".", 255-strlen(auth)); strncat(auth, buf, 255-strlen(auth)); dprintf((stderr, "auth: %s\n", auth)); md5_buffer(auth, strlen(auth), digestbuf); for(i=0, p=buf; i<MD5_DIGEST_BYTES; i++, p+=2) { sprintf(p, "%02x", digestbuf[i]); } strcpy(auth, buf); dprintf((stderr, "auth: %s\n", auth)); snprintf(buf, BUFFER_SIZE, "%s:%s:%s:%s\n", user_name, auth, domainname, gnudip_request); output(buf); bytes = 0; if((bytes=read_input(buf, BUFFER_SIZE)) <= 0) { close(client_sockfd); return(UPDATERES_ERROR); } buf[bytes] = '\0'; dprintf((stderr, "bytes: %d\n", bytes)); dprintf((stderr, "server output: %s\n", buf)); close(client_sockfd); if(sscanf(buf, "%d", &ret) != 1) { ret = -1; } switch(ret) { case -1: if(!(options & OPT_QUIET)) { show_message("strange server response, are you connecting to the right server?\n"); } return(UPDATERES_ERROR); break; case 0: if(!(options & OPT_QUIET)) { printf("update request successful\n"); } break; case 1: if(!(options & OPT_QUIET)) { show_message("invalid login attempt\n"); } return(UPDATERES_ERROR); break; case 2: if(!(options & OPT_QUIET)) { fprintf(stderr, "offline request successful\n"); } break; default: if(!(options & OPT_QUIET)) { show_message("unknown return code: %d\n", ret); } return(UPDATERES_ERROR); break; } return(UPDATERES_OK); } #endif int JUSTL_check_info(void) { char buf[BUFSIZ+1]; if(host == NULL) { if(options & OPT_DAEMON) { return(-1); } printf("host: "); *buf = '\0'; fgets(buf, BUFSIZ, stdin); chomp(buf); host = strdup(buf); } if(interface == NULL && address == NULL) { if(options & OPT_DAEMON) { fprintf(stderr, "you must provide either an interface or an address\n"); return(-1); } if(interface) { free(interface); } printf("interface: "); *buf = '\0'; fgets(buf, BUFSIZ, stdin); chomp(buf); option_handler(CMD_interface, buf); } warn_fields(service->fields_used); return 0; } int JUSTL_update_entry(void) { char buf[BUFFER_SIZE+1]; char *bp = buf; int bytes; int btot; int ret; buf[BUFFER_SIZE] = '\0'; if(do_connect((int*)&client_sockfd, server, port) != 0) { if(!(options & OPT_QUIET)) { show_message("error connecting to %s:%s\n", server, port); } return(UPDATERES_ERROR); } snprintf(buf, BUFFER_SIZE, "GET %s?direct=1&", request); output(buf); snprintf(buf, BUFFER_SIZE, "%s=%s&", "username", user_name); output(buf); snprintf(buf, BUFFER_SIZE, "%s=%s&", "password", password); output(buf); snprintf(buf, BUFFER_SIZE, "%s=%s&", "host", host); output(buf); snprintf(buf, BUFFER_SIZE, "%s=%s&", "ip", address); output(buf); snprintf(buf, BUFFER_SIZE, " HTTP/1.0\015\012"); output(buf); snprintf(buf, BUFFER_SIZE, "Authorization: Basic %s\015\012", auth); output(buf); snprintf(buf, BUFFER_SIZE, "User-Agent: %s-%s %s (%s)\015\012", "ez-update", VERSION, OS, "by Angus Mackay"); output(buf); snprintf(buf, BUFFER_SIZE, "Host: %s\015\012", server); output(buf); snprintf(buf, BUFFER_SIZE, "\015\012"); output(buf); bp = buf; bytes = 0; btot = 0; while((bytes=read_input(bp, BUFFER_SIZE-btot)) > 0) { bp += bytes; btot += bytes; dprintf((stderr, "btot: %d\n", btot)); } close(client_sockfd); buf[btot] = '\0'; dprintf((stderr, "server output: %s\n", buf)); if(sscanf(buf, " HTTP/1.%*c %3d", &ret) != 1) { ret = -1; } switch(ret) { case -1: if(!(options & OPT_QUIET)) { show_message("strange server response, are you connecting to the right server?\n"); } return(UPDATERES_ERROR); break; case 200: if(strstr(buf, "okay") != NULL) { if(!(options & OPT_QUIET)) { printf("request successful\n"); } } else { show_message("error processing request\n"); if(!(options & OPT_QUIET)) { fprintf(stderr, "server output: %s\n", buf); } return(UPDATERES_ERROR); } break; case 401: if(!(options & OPT_QUIET)) { show_message("authentication failure\n"); } return(UPDATERES_SHUTDOWN); break; default: if(!(options & OPT_QUIET)) { // reuse the auth buffer *auth = '\0'; sscanf(buf, " HTTP/1.%*c %*3d %255[^\r\n]", auth); show_message("unknown return code: %d\n", ret); show_message("server response: %s\n", auth); } return(UPDATERES_ERROR); break; } return(UPDATERES_OK); } int DYNS_check_info(void) { char buf[BUFSIZ+1]; if(host == NULL) { if(options & OPT_DAEMON) { return(-1); } printf("host: "); *buf = '\0'; fgets(buf, BUFSIZ, stdin); chomp(buf); host = strdup(buf); } if(interface == NULL && address == NULL) { if(options & OPT_DAEMON) { fprintf(stderr, "you must provide either an interface or an address\n"); return(-1); } if(interface) { free(interface); } printf("interface: "); *buf = '\0'; fgets(buf, BUFSIZ, stdin); chomp(buf); option_handler(CMD_interface, buf); } warn_fields(service->fields_used); return 0; } int DYNS_update_entry(void) { char buf[BUFFER_SIZE+1]; char *bp = buf; int bytes; int btot; int ret; buf[BUFFER_SIZE] = '\0'; if(do_connect((int*)&client_sockfd, server, port) != 0) { if(!(options & OPT_QUIET)) { show_message("error connecting to %s:%s\n", server, port); } return(UPDATERES_ERROR); } snprintf(buf, BUFFER_SIZE, "GET %s?", request); output(buf); snprintf(buf, BUFFER_SIZE, "%s=%s&", "username", user_name); output(buf); snprintf(buf, BUFFER_SIZE, "%s=%s&", "password", password); output(buf); snprintf(buf, BUFFER_SIZE, "%s=%s&", "host", host); output(buf); snprintf(buf, BUFFER_SIZE, "%s=%s", "ip", address); output(buf); snprintf(buf, BUFFER_SIZE, " HTTP/1.0\015\012"); output(buf); snprintf(buf, BUFFER_SIZE, "Authorization: Basic %s\015\012", auth); output(buf); snprintf(buf, BUFFER_SIZE, "User-Agent: %s-%s %s (%s)\015\012", "ez-update", VERSION, OS, "by Angus Mackay"); output(buf); snprintf(buf, BUFFER_SIZE, "Host: %s\015\012", server); output(buf); snprintf(buf, BUFFER_SIZE, "\015\012"); output(buf); bp = buf; bytes = 0; btot = 0; while((bytes=read_input(bp, BUFFER_SIZE-btot)) > 0) { bp += bytes; btot += bytes; dprintf((stderr, "btot: %d\n", btot)); } close(client_sockfd); buf[btot] = '\0'; dprintf((stderr, "server output: %s\n", buf)); if(sscanf(buf, " HTTP/1.%*c %3d", &ret) != 1) { ret = -1; } switch(ret) { case -1: if(!(options & OPT_QUIET)) { show_message("strange server response, are you connecting to the right server?\n"); } return(UPDATERES_ERROR); break; case 200: if(strstr(buf, "200 Host") != NULL) { if(!(options & OPT_QUIET)) { printf("request successful\n"); } } else if(strstr(buf, "400 Bad Request") != NULL) { if(!(options & OPT_QUIET)) { printf("bad request\n"); } } else if(strstr(buf, "401 User") != NULL) { if(!(options & OPT_QUIET)) { printf("authentication failure (username/password)\n"); } } else if(strstr(buf, "405 Hostname") != NULL) { if(!(options & OPT_QUIET)) { printf("authentication failure (hostname not found)\n"); } } else { show_message("error processing request\n"); if(!(options & OPT_QUIET)) { fprintf(stderr, "server output: %s\n", buf); } return(UPDATERES_ERROR); } break; case 405: if(!(options & OPT_QUIET)) { show_message("authentication failure\n"); } return(UPDATERES_ERROR); break; default: if(!(options & OPT_QUIET)) { // reuse the auth buffer *auth = '\0'; sscanf(buf, " HTTP/1.%*c %*3d %255[^\r\n]", auth); show_message("unknown return code: %d\n", ret); show_message("server response: %s\n", auth); } return(UPDATERES_ERROR); break; } return(UPDATERES_OK); } int HN_check_info(void) { warn_fields(service->fields_used); return 0; } int HN_update_entry(void) { char buf[BUFFER_SIZE+1]; char *bp = buf; int bytes; int btot; int ret; buf[BUFFER_SIZE] = '\0'; if(do_connect((int*)&client_sockfd, server, port) != 0) { if(!(options & OPT_QUIET)) { show_message("error connecting to %s:%s\n", server, port); } return(UPDATERES_ERROR); } snprintf(buf, BUFFER_SIZE, "GET %s?ver=%d&", request, 1); output(buf); if(address) { snprintf(buf, BUFFER_SIZE, "%s=%s&", "IP", address); output(buf); } snprintf(buf, BUFFER_SIZE, " HTTP/1.0\015\012"); output(buf); snprintf(buf, BUFFER_SIZE, "Authorization: Basic %s\015\012", auth); output(buf); snprintf(buf, BUFFER_SIZE, "User-Agent: %s-%s %s (%s)\015\012", "ez-update", VERSION, OS, "by Angus Mackay"); output(buf); snprintf(buf, BUFFER_SIZE, "Host: %s\015\012", server); output(buf); snprintf(buf, BUFFER_SIZE, "\015\012"); output(buf); bp = buf; bytes = 0; btot = 0; while((bytes=read_input(bp, BUFFER_SIZE-btot)) > 0) { bp += bytes; btot += bytes; dprintf((stderr, "btot: %d\n", btot)); } close(client_sockfd); buf[btot] = '\0'; dprintf((stderr, "server output: %s\n", buf)); if(sscanf(buf, " HTTP/1.%*c %3d", &ret) != 1) { ret = -1; } switch(ret) { char *p; case -1: if(!(options & OPT_QUIET)) { show_message("strange server response, are you connecting to the right server?\n"); } return(UPDATERES_ERROR); break; case 200: ret = -1; if((p=strstr(buf, "DDNS_Response_")) != NULL) { sscanf(p, "DDNS_Response_%*code=%3d", &ret); } /* * 101 - Successfully Updated * 201 - Failure because previous update occured * less than 300 seconds ago * 202 - Failure because of server error * 203 - Failure because account is frozen (by admin) * 204 - Failure because account is locked (by user) */ switch(ret) { case -1: if(!(options & OPT_QUIET)) { show_message("strange server response, are you connecting to the right server?\n"); } return(UPDATERES_ERROR); break; case 101: if(!(options & OPT_QUIET)) { printf("request successful\n"); } break; case 201: show_message("Last update was less than %d seconds ago.\n", 300); return(UPDATERES_ERROR); break; case 202: show_message("Server error.\n"); return(UPDATERES_ERROR); break; case 203: show_message("Failure because account is frozen (by admin).\n"); return(UPDATERES_SHUTDOWN); break; case 204: show_message("Failure because account is locked (by user).\n"); return(UPDATERES_SHUTDOWN); break; default: if(!(options & OPT_QUIET)) { show_message("unknown return code: %d\n", ret); fprintf(stderr, "server response: %s\n", buf); } return(UPDATERES_ERROR); break; } break; case 401: if(!(options & OPT_QUIET)) { show_message("authentication failure\n"); } return(UPDATERES_SHUTDOWN); break; default: if(!(options & OPT_QUIET)) { // reuse the auth buffer *auth = '\0'; sscanf(buf, " HTTP/1.%*c %*3d %255[^\r\n]", auth); show_message("unknown return code: %d\n", ret); fprintf(stderr, "server response: %s\n", auth); } return(UPDATERES_ERROR); break; } return(UPDATERES_OK); } static int is_in_list(char *needle, char **haystack) { char **p; int found = 0; for(p=haystack; *p != NULL; p++) { if(strcmp(needle, *p) == 0) { found = 1; break; } } return(found); } void warn_fields(char **okay_fields) { if(wildcard != 0 && !is_in_list("wildcard", okay_fields)) { fprintf(stderr, "warning: this service does not support the %s option\n", "wildcard"); } if(!(mx == NULL || *mx == '\0') && !is_in_list("mx", okay_fields)) { fprintf(stderr, "warning: this service does not support the %s option\n", "mx"); } if(!(url == NULL || *url == '\0') && !is_in_list("url", okay_fields)) { fprintf(stderr, "warning: this service does not support the %s option\n", "url"); } if(!(cloak_title == NULL || *cloak_title == '\0') && !is_in_list("cloak_title", okay_fields)) { fprintf(stderr, "warning: this service does not support the %s option\n", "cloak_title"); } if(connection_type != 1 && !is_in_list("connection-type", okay_fields)) { fprintf(stderr, "warning: this service does not support the %s option\n", "connection-type"); } } int exec_cmd(char *cmd) { #if (defined(HAVE_WAITPID) || defined(HAVE_WAIT)) && (defined(HAVE_VFORK) || defined(HAVE_FORK)) int kid; int exit_code; switch((kid=vfork())) { case -1: if(!(options & OPT_QUIET)) { perror("fork"); } return(-1); break; case 0: /* child */ execl("/bin/sh", "sh", "-c", cmd, (char *)0); if(!(options & OPT_QUIET)) { perror("exec"); } exit(1); break; default: /* parent */ dprintf((stderr, "forked kid: %d\n", kid)); break; } # ifdef HAVE_WAITPID if(waitpid(kid, &exit_code, 0) != kid) { return(1); } # else if(wait(&exit_code) != kid) { return(1); } # endif exit_code = WEXITSTATUS(exit_code); return(exit_code); #else return(-1); #endif } void handle_sig(int sig) { switch(sig) { case SIGHUP: if(config_file) { show_message("SIGHUP recieved, re-reading config file\n"); if(parse_conf_file(config_file, conf_commands) != 0) { show_message("error parsing config file \"%s\"\n", config_file); } } break; case SIGTERM: /* * this is used to wake up the client so that it will perform an update */ break; case SIGQUIT: show_message("received SIGQUIT, shutting down\n"); #if HAVE_SYSLOG_H closelog(); #endif #if HAVE_GETPID if(pid_file) { pid_file_delete(pid_file); } #endif exit(0); default: dprintf((stderr, "case not handled: %d\n", sig)); break; } } int main(int argc, char **argv) { int ifresolve_warned = 0; int i; int retval = 1; #ifdef IF_LOOKUP int sock = -1; #endif #if defined(DEBUG) && defined(__linux__) mcheck(NULL); #endif dprintf((stderr, "staring...\n")); program_name = argv[0]; options = 0; *user = '\0'; timeout.tv_sec = DEFAULT_TIMEOUT; timeout.tv_usec = 0; #if HAVE_SIGNAL_H // catch user interupts signal(SIGINT, sigint_handler); signal(SIGHUP, generic_sig_handler); signal(SIGTERM, generic_sig_handler); signal(SIGQUIT, generic_sig_handler); #endif parse_args(argc, argv); if(!(options & OPT_QUIET) && !(options & OPT_DAEMON)) { fprintf(stderr, "ez-ipupdate Version %s\nCopyright (C) 1999-2000 Angus Mackay.\n", VERSION); } dprintf((stderr, "options: 0x%04X\n", options)); dprintf((stderr, "interface: %s\n", interface)); dprintf((stderr, "ntrys: %d\n", ntrys)); dprintf((stderr, "server: %s:%s\n", server, port)); dprintf((stderr, "address: %s\n", address)); dprintf((stderr, "wildcard: %d\n", wildcard)); dprintf((stderr, "mx: %s\n", mx)); dprintf((stderr, "auth: %s\n", auth)); if(service->type == SERV_NULL) { if(service->check_info() != 0) { fprintf(stderr, "invalid data to perform requested action.\n"); exit(1); } } if(server == NULL) { server = strdup(service->default_server); } if(port == NULL) { port = strdup(service->default_port); } *user_name = '\0'; *password = '\0'; if(*user != '\0') { sscanf(user, "%127[^:]:%127[^\n]", user_name, password); dprintf((stderr, "user_name: %s\n", user_name)); dprintf((stderr, "password: %s\n", password)); } if(*user_name == '\0') { printf("user name: "); fgets(user_name, sizeof(user_name), stdin); chomp(user_name); } if(*password == '\0') { strncpy(password, getpass("password: "), sizeof(password)); } sprintf(user, "%s:%s", user_name, password); base64Encode(user, auth); request = strdup(service->default_request); if(service->check_info() != 0) { fprintf(stderr, "invalid data to perform requested action.\n"); exit(1); } if(mx == NULL) { mx = strdup(""); } if(url == NULL) { url = strdup(""); } #ifdef IF_LOOKUP if(options & OPT_DAEMON) { sock = socket(AF_INET, SOCK_STREAM, 0); } #endif if(options & OPT_DAEMON) { int local_update_period = update_period; #if IF_LOOKUP struct sockaddr_in sin; struct sockaddr_in sin2; if(interface == NULL) { fprintf(stderr, "invalid data to perform requested action.\n"); fprintf(stderr, "you must provide an interface for daemon mode"); exit(1); } /* background our selves */ if(!(options & OPT_FOREGROUND)) { # if HAVE_SYSLOG_H close(0); close(1); close(2); # endif if(fork() > 0) { exit(0); } /* parent */ } #if HAVE_GETPID if(pid_file && pid_file_create(pid_file) != 0) { fprintf(stderr, "exiting...\n"); exit(1); } #endif # if HAVE_SYSLOG_H openlog(program_name, LOG_PID, LOG_USER ); options |= OPT_QUIET; # endif show_message("ez-ipupdate Version %s, Copyright (C) 1998-2000 Angus Mackay.\n", VERSION); show_message("%s started for interface %s host %s using server %s and service %s\n", program_name, N_STR(interface), N_STR(host), server, service->name); memset(&sin, 0, sizeof(sin)); if(cache_file) { time_t ipdate; char *ipstr; int cache_file_read = 0; if(read_cache_file(cache_file, &ipdate, &ipstr) == 0) { dprintf((stderr, "cache date: %ld\n", ipdate)); dprintf((stderr, "cache IP: %s\n", ipstr)); cache_file_read = 1; if(ipstr && strchr(ipstr, '.')) { struct tm *ts; char timebuf[64]; inet_aton(ipstr, &sin.sin_addr); last_update = ipdate; ts = localtime(&ipdate); strftime(timebuf, sizeof(timebuf), "%Y/%m/%d %H:%M", ts); show_message("got last update %s on %s from cache file\n", ipstr, timebuf); } else { cache_file_read = 0; } if(ipstr) { free(ipstr); ipstr = NULL; } } if(!cache_file_read) { show_message("error reading cache file \"%s\"\n", cache_file); } } for(;;) { #if HAVE_SIGNAL_H /* check for signals */ if(last_sig != 0) { handle_sig(last_sig); last_sig = 0; } #endif if(get_if_addr(sock, interface, &sin2) == 0) { ifresolve_warned = 0; if(memcmp(&sin.sin_addr, &sin2.sin_addr, sizeof(struct in_addr)) != 0 || (max_interval > 0 && time(NULL) - last_update > max_interval)) { int updateres; // save this new ipaddr memcpy(&sin, &sin2, sizeof(sin)); // update the address buffer if(address) { free(address); } address = strdup(inet_ntoa(sin.sin_addr)); if((updateres=service->update_entry()) == UPDATERES_OK) { last_update = time(NULL); local_update_period = update_period; show_message("successful update for %s->%s (%s)\n", interface, inet_ntoa(sin.sin_addr), N_STR(host)); if(post_update_cmd) { int res; if(post_update_cmd) { sprintf(post_update_cmd_arg, "%s", inet_ntoa(sin.sin_addr)); if((res=exec_cmd(post_update_cmd)) != 0) { if(res == -1) { show_message("(%s) error running post update command: %s\n", N_STR(host), error_string); } else { show_message( "(%s) error running post update command, command exit code: %d\n", N_STR(host), res); } } } } if(cache_file) { char ipbuf[64]; snprintf(ipbuf, sizeof(ipbuf), "%s", inet_ntoa(sin.sin_addr)); if(write_cache_file(cache_file, last_update, ipbuf) != 0) { show_message("unable to write cache file: %s\n", error_string); } } } else { show_message("failure to update %s->%s (%s)\n", interface, inet_ntoa(sin.sin_addr), N_STR(host)); memset(&sin, 0, sizeof(sin)); // double the time between attempts between each failure to update // this gets set back to the normal value the next time we get a // successful update if(local_update_period < MIN_WAIT_PERIOD) { local_update_period = MIN_WAIT_PERIOD; } else { local_update_period *= 2; } if(local_update_period > MAX_WAIT_PERIOD) { local_update_period = MAX_WAIT_PERIOD; } dprintf((stderr, "local_update_period: %d\n", local_update_period)); dprintf((stderr, "updateres: %d\n", updateres)); if(updateres == UPDATERES_SHUTDOWN) { show_message("shuting down updater for %s due to fatal error\n", N_STR(host)); if(notify_email && *notify_email != '\0') { char buf[1024]; dprintf((stderr, "sending email to %s\n", notify_email)); snprintf(buf, sizeof(buf), "echo \"ez-ipupdate shuting down" " updater for %s due to fatal error.\" | %s %s", host, SEND_EMAIL_CMD, notify_email); system(buf); } break; } } } sleep(local_update_period); } else { if(!ifresolve_warned) { ifresolve_warned = 1; show_message("(%s) unable to resolve interface %s\n", N_STR(host), interface); } sleep(resolv_period); } } #if HAVE_GETPID if(pid_file) { pid_file_delete(pid_file); } #endif #else fprintf(stderr, "sorry, this mode is only available on platforms that the "); fprintf(stderr, "IP address \ncan be determined. feel free to hack the code"); fprintf(stderr, " though.\n"); exit(1); #endif } else { int need_update = 1; if(cache_file) { time_t ipdate; char *ipstr; char ipbuf[64]; if(read_cache_file(cache_file, &ipdate, &ipstr) != 0) { exit(1); } dprintf((stderr, "cache date: %ld\n", ipdate)); dprintf((stderr, "cache IP: %s\n", ipstr)); // check that the cache file contained something if(ipstr != NULL) { if(address == NULL || *address == '\0') { #ifdef IF_LOOKUP struct sockaddr_in sin; int sock; sock = socket(AF_INET, SOCK_STREAM, 0); if(get_if_addr(sock, interface, &sin) != 0) { exit(1); } close(sock); snprintf(ipbuf, sizeof(ipbuf), "%s", inet_ntoa(sin.sin_addr)); #else fprintf(stderr, "interface lookup not enabled at compile time\n"); exit(1); #endif } else { snprintf(ipbuf, sizeof(ipbuf), "%s", address); } // check for a change in the IP if(strcmp(ipstr, ipbuf) == 0) { dprintf((stderr, "cache IP doesn't need updating\n")); need_update = 0; } // check the date if(max_interval > 0) { if(time(NULL) - ipdate > max_interval) { dprintf((stderr, "cache IP is passed max_interval of %d\n", max_interval)); need_update = 1; } } } if(ipstr) { free(ipstr); ipstr = NULL; } } if(need_update) { int res; if(address == NULL && interface != NULL) { struct sockaddr_in sin; int sock; sock = socket(AF_INET, SOCK_STREAM, 0); if(get_if_addr(sock, interface, &sin) == 0) { if(address) { free(address); } address = strdup(inet_ntoa(sin.sin_addr)); } else { show_message("could not resolve ip address for %s.\n", interface); exit(1); } close(sock); } for(i=0; i<ntrys; i++) { if(service->update_entry() == UPDATERES_OK) { retval = 0; break; } if(i+1 != ntrys) { sleep(10 + 10*i); } } if(retval == 0 && post_update_cmd) { if((res=exec_cmd(post_update_cmd)) != 0) { if(!(options & OPT_QUIET)) { if(res == -1) { fprintf(stderr, "error running post update command: %s\n", error_string); } else { fprintf(stderr, "error running post update command, command exit code: %d\n", res); } } } } // write cache file if(retval == 0 && cache_file) { char ipbuf[64]; if(address == NULL || *address == '\0') { #ifdef IF_LOOKUP struct sockaddr_in sin; int sock; sock = socket(AF_INET, SOCK_STREAM, 0); if(get_if_addr(sock, interface, &sin) != 0) { exit(1); } close(sock); snprintf(ipbuf, sizeof(ipbuf), "%s", inet_ntoa(sin.sin_addr)); #else fprintf(stderr, "interface lookup not enabled at compile time\n"); exit(1); #endif } else { snprintf(ipbuf, sizeof(ipbuf), "%s", address); } if(write_cache_file(cache_file, time(NULL), ipbuf) != 0) { exit(1); } } } else { fprintf(stderr, "no update needed at this time\n"); } } #ifdef IF_LOOKUP if(sock > 0) { close(sock); } #endif if(address) { free(address); } if(cache_file) { free(cache_file); } if(config_file) { free(config_file); } if(host) { free(host); } if(interface) { free(interface); } if(mx) { free(mx); } if(port) { free(port); } if(request) { free(request); } if(server) { free(server); } if(url) { free(url); } dprintf((stderr, "done\n")); return(retval); }