/* * 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 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Library General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ #include #include #include #include #include "iface.h" #include "opendd.h" #include "globals.h" #include "./include/wrapper.h" #include "./include/sendmail.h" #include "./include/base64.h" #include "./include/option.h" char *update_table[] = { /* Update Syntax Errors */ "badsys", "badagent", /* Account-Related Errors */ "badauth", "!donator", /* Update Complete */ RETURN_CODE_GOOD, RETURN_CODE_NOCHG, /* Hostname-Related Errors */ "notfqdn", "nohost", "!yours", "abuse", /* DynDNS Server errors conditions */ "numhost", "dnserr", /* DynDNS emergency conditions */ "911", NULL }; char *message_table[] = { "The sycodestem parameter given is not valid. Valid system parameters are dyndns, statdns and custom.", "The user agent that was sent has been blocked for not following these specifications or no user agent was specified.", "The username or password specified are incorrect.", "An option available only to credited users was specified, but the user is not a credited user.", "The update was successful, and the hostname is now updated.", "The update changed no settings, and is considered abusive. Additional nochg updates will cause the hostname to become blocked.", "The hostname specified is not a fully-qualified domain name (not in the form hostname.dyndns.org or domain.com).", "The hostname specified does not exist (or is not in the service specified in the system parameter).", "The hostname specified exists, but not under the username specified.", "The hostname specified is blocked for update abuse.", "Too many or too few hosts found. Please contact the DynDNS support at support@dyndns.com .", "DNS error encountered. Please contact the DynDNS support at support@dyndns.com .", "A serious problem occurred in the DynDNS system, such as a database or DNS server failure. " "Check the status page at http://www.dyndns.org/news/status/ until the service is back up.", NULL }; /* Store the lock / pid file descriptor */ static int g_lock_fd = -1; static int g_second_lifetime = 0; static int g_force_update_status = 0; #ifdef __Linux__ void linux_setproctitle(char *fmt, ...) { /* ----------------------------------------------------------------------------- * Linux's setproctitle() version * * @param char * * -------------------------------------------------------------------------- */ /* TO BE IMPLEMENTED */ } #endif int check_config_file_mode(const char *filepath) { /* ----------------------------------------------------------------------------- * Check if the given config file is only "user read access". * * @param const char * * @return int * -------------------------------------------------------------------------- */ struct stat st; if (stat(filepath, &st) == 0) { if (!S_ISREG(st.st_mode)) logmsg(LOG_ERR, "check_config_mode() : %s is not a regular file", filepath); else { if ((st.st_mode & S_IRUSR) != S_IRUSR) logmsg(LOG_ERR, "check_config_mode() : %s is not readable", filepath); else if (((st.st_mode & S_IROTH) == S_IROTH) || ((st.st_mode & S_IRGRP) == S_IRGRP)) logmsg(LOG_ERR, "check_config_mode() : %s should only be readable by owner", filepath); else return 1; } } else logmsg(LOG_ERR, "check_config_mode() : cannot state %s : %s", filepath, strerror(errno)); return 0; } char *get_pidfile_path() { /* ----------------------------------------------------------------------------- * Get the pid file path * * @return char * * -------------------------------------------------------------------------- */ option_type type; void *value = NULL; value = get_option("pidfile_path", &type); if (value != NULL) { switch(type) { case OPTION_STRING: return (char *)value; break; default: break; } } return PID_FILE; } int open_lock_pidfile() { /* ----------------------------------------------------------------------------- * Create pid file and use it as a lock. * If the file already exists and is locked, then it means that another * opendd is running. * * @return int, 1 if successful, * -1 if file is already lock, * 0 for any other errors * -------------------------------------------------------------------------- */ char *pidfile = NULL; struct flock sl; /* Get pidfile path */ pidfile = get_pidfile_path(); g_lock_fd = open(pidfile, O_RDWR | O_CREAT); if (g_lock_fd < 0) { logmsg(LOG_ERR, "open_lock_pidfile() : cannot open %s : %s", pidfile, strerror(errno)); return 0; } if (fchmod(g_lock_fd, S_IRUSR | S_IWUSR) < 0) { logmsg(LOG_ERR, "open_lock_pidfile() : cannot chmod 0600 %s : %s", pidfile, strerror(errno)); close_lock_pidfile(); return 0; } memset((struct flock *)&sl, 0, sizeof(sl)); sl.l_type = F_WRLCK; if (fcntl(g_lock_fd, F_SETLK, &sl) < 0) { /* EAGAIN means that another opendd process is locking the file. * Only log another error type. */ if (errno != EAGAIN) logmsg(LOG_ERR, "open_lock_pidfile() : cannot lock %s : %s", pidfile, strerror(errno)); close_lock_pidfile(); return -1; } return 1; } int write_pid_lock_pidfile() { /* ----------------------------------------------------------------------------- * Write pid number in the lock file * * @return int * -------------------------------------------------------------------------- */ pid_t pid; char *s_pid = NULL; if (g_lock_fd >= 0) { pid = getpid(); asprintf(&s_pid, "%d", pid); if (s_pid != NULL) { if (write(g_lock_fd, s_pid, strlen(s_pid)) == -1) { logmsg(LOG_ERR, "write_pid_lock_pidfile() : cannot write pid %d : %s", pid, strerror(errno)); close_lock_pidfile(); FREE(s_pid); return 0; } FREE(s_pid); return 1; } } return 0; } int close_lock_pidfile() { /* ----------------------------------------------------------------------------- * Close lock pid file * * @return int * -------------------------------------------------------------------------- */ int retval = 1; if (g_lock_fd >= 0) { retval = close(g_lock_fd); g_lock_fd = -1; if (retval == -1) { logmsg(LOG_ERR, "close_lock_pidfile() ; %s", strerror(errno)); return 0; } } return retval; } int empty_lock_pidfile() { /* ----------------------------------------------------------------------------- * Unlink lock pid file * * @return int * -------------------------------------------------------------------------- */ char *pidfile = NULL; int fd; if (g_lock_fd < 0) { /* We must be sure that the lock file is closed */ pidfile = get_pidfile_path(); if (unlink(pidfile) < 0) { /* If we cannot unlink the lock file, we try to truncate it */ fd = open(pidfile, O_WRONLY | O_TRUNC); if (fd < 0) { logmsg(LOG_ERR, "empty_lock_pidfile() : cannot unlink or truncate %s", pidfile); return 0; } close(fd); return 1; } else return 1; } return 0; } int get_second_lifetime() { /* ----------------------------------------------------------------------------- * Get the current IP's lifetime * * @return int * -------------------------------------------------------------------------- */ return g_second_lifetime; } void set_second_lifetime(int sec) { /* ----------------------------------------------------------------------------- * Set the current IP's lifetime * This should be done, after a successful update * * @param int * -------------------------------------------------------------------------- */ g_second_lifetime = sec; } int get_force_update_status() { /* ----------------------------------------------------------------------------- * Get the force update status * * @return int * -------------------------------------------------------------------------- */ return g_force_update_status; } void set_force_update_status(int status) { /* ----------------------------------------------------------------------------- * Set the force update status * * @param int * -------------------------------------------------------------------------- */ g_force_update_status = status; } void update_proc_title() { /* ----------------------------------------------------------------------------- * Set the process title, seen by a "ps" command * * -------------------------------------------------------------------------- */ if (g_second_lifetime == 0) SETPROCTITLE("[lifetime] 0 days"); else { SETPROCTITLE("[lifetime] %d days (%d seconds)", g_second_lifetime / (60 * 60 * 24), g_second_lifetime); } } int send_report(const char *additional_header, const char *msg) { /* ----------------------------------------------------------------------------- * This send a mail report on DynDNS update * * @param const char *, some additionnal header or NULL * @param const char *, the message body * @return int * -------------------------------------------------------------------------- */ void *value = NULL; option_type type; char *smtp = NULL, *user = NULL, *pwd = NULL; char *mailfrom = NULL, *mailto = NULL; int retcode = 0; int dyndns_use_ssl = 0; /* Get the SMTP parameters */ value = get_option("smtpservername", &type); if (value == NULL) return 0; switch(type) { case OPTION_STRING: smtp = (char *)value; break; default: logmsg(LOG_ERR, "send_report() : \"smtpservername\" is not a valid option !"); return 0; break; } value = get_option("mailto", &type); if (value == NULL) return 0; switch(type) { case OPTION_STRING: mailto = (char *)value; break; default: logmsg(LOG_ERR, "send_report() : \"mailto\" is not a valid option !"); return 0; break; } value = get_option("mailfrom", &type); if (value == NULL) return 0; switch(type) { case OPTION_STRING: mailfrom = (char *)value; break; default: logmsg(LOG_ERR, "send_report() : \"mailfrom\" is not a valid option !"); return 0; break; } value = get_option("smtpauthuser", &type); if (value != NULL) { switch(type) { case OPTION_STRING: user = (char *)value; break; default: logmsg(LOG_ERR, "send_report() : \"smtpauthuser\" is not a valid option !"); return 0; break; } } value = get_option("smtpauthpwd", &type); if (value != NULL) { switch(type) { case OPTION_STRING: pwd = (char *)value; break; default: logmsg(LOG_ERR, "send_report() : \"smtpauthpwd\" is not a valid option !"); return 0; break; } } /* Increase the select() timeout for the sendmail function */ set_sendmail_timeout(20); /* First we must disable SSL socket */ dyndns_use_ssl = get_socket_ssl(); if (dyndns_use_ssl) set_socket_ssl(0); /* Send to your SMTP server */ retcode = sendmail(smtp, "25", mailfrom, mailto, user, pwd, PLAIN, additional_header, msg); /* Then reenable it */ if (dyndns_use_ssl) set_socket_ssl(1); return retcode; } int getdyndnshostnames(const char *ip, struct chain_string **strings, int force_update) { /* ----------------------------------------------------------------------------- * Get DynDNS hostnames to be updated * * @param const char *, IP address to compared with hostname resolved * @param struct chain_string ** * @param int, if true, return all hostnames in "hostname" config values * else, only the hostnames to be updated are returned * @return int, 0 if no hostnames is updatable, -1 on error * -------------------------------------------------------------------------- */ struct chain *chain_tmp = NULL; char *ip_resolv = NULL; option_type type; option *opt; void *value = NULL; /* Get the parameters from config file */ /* hostname (mandatory) */ value = get_option("hostname", &type); if (value == NULL) { logmsg(LOG_ERR, "dyndns() : \"hostname\" is mandatory !"); return -1; } switch(type) { case OPTION_STRING: if (force_update) { /* Force an update, so the hostname must be returned */ *strings = new_chain_string((const char *)value); return 1; } if (getaddrbyname(&ip_resolv, (const char *)value)) { if (strcmp(ip_resolv, ip) != 0) { /* IP and hostname resolved are not the same, so * this hostname needs to be updated */ FREE(ip_resolv); *strings = new_chain_string((const char *)value); return 1; } else { /* IP and hostname resolved are the same, so * this hostname does not need to be updated */ FREE(ip_resolv); logmsg(LOG_INFO, "getdyndnshostnames() : no need to update %s with %s", (const char *)value, ip); return 0; } } else { /* Cannot resolve hostname, so we consider this hostname to be updated, anyway */ logmsg(LOG_ERR, "getdyndnshostnames() : %s cannot be resolved", (const char *)value); *strings = new_chain_string((const char *)value); return 1; } break; case OPTION_CHAIN: for(chain_tmp = (struct chain *)value; chain_tmp != NULL; chain_tmp = chain_tmp->next) { opt = (option *)chain_tmp->value; switch(opt->type) { case OPTION_STRING: if (force_update) { /* Force an update, so the hostnames must be returned */ if (*strings == NULL) *strings = new_chain_string((const char *)opt->value); else append_chain_string(new_chain_string((const char *)opt->value), *strings); } else { if (getaddrbyname(&ip_resolv, (const char *)opt->value)) { if (strcmp(ip_resolv, ip) != 0) { /* IP and hostname resolved are not the same, so * this hostname needs to be updated */ if (*strings == NULL) *strings = new_chain_string((const char *)opt->value); else append_chain_string(new_chain_string((const char *)opt->value), *strings); } else logmsg(LOG_INFO, "getdyndnshostnames() : no need to update %s with %s", (const char *)opt->value, ip); FREE(ip_resolv); } else { /* Cannot resolve hostname, so we consider this hostname to be updated, anyway */ logmsg(LOG_ERR, "getdyndnshostnames() : %s cannot be resolved", (const char *)opt->value); if (*strings == NULL) *strings = new_chain_string((const char *)opt->value); else append_chain_string(new_chain_string((const char *)opt->value), *strings); } } break; default: logmsg(LOG_ERR, "dyndns() : \"hostname\" is not a valid option !"); return -1; break; } } return (*strings != NULL); /* true, if one or more hostnames are to be updated */ break; default: logmsg(LOG_ERR, "dyndns() : \"hostname\" is not a valid option !"); return -1; break; } return 0; } int getifaceaddr(char **ip) { /* ----------------------------------------------------------------------------- * Get IP address from interface name, from config file * * @param char **, where to store the IP address * @return int * -------------------------------------------------------------------------- */ option_type type; void *value = NULL; struct sockaddr addr; /* iface (mandatory) */ /* Get my IP from an interface */ value = get_option("iface", &type); if (value == NULL) { logmsg(LOG_ERR, "dyndns() : \"iface\" is mandatory !"); return 0; } switch(type) { case OPTION_STRING: if (getaddrbyif((const char *)value, &addr)) { *ip = strdup(inet_ntoa(((struct sockaddr_in *)&addr)->sin_addr)); if (*ip != NULL) { return 1; } } else logmsg(LOG_ERR, "dyndns() : Cannot find interface %s", (const char *)value); break; default: logmsg(LOG_ERR, "dyndns() : \"iface\" is not a valid option !"); break; } return 0; } int runasdaemon() { /* ----------------------------------------------------------------------------- * Determine if OpenDD must be run as daemon * * @return int * -------------------------------------------------------------------------- */ option_type type; void *value = NULL; value = get_option("runasdaemon", &type); if (value != NULL) { switch(type) { case OPTION_INT: if (*((int *)value)) return 1; break; default: break; } } return 0; } int drop_privileges() { /* ----------------------------------------------------------------------------- * Try to drop privileges, if the program is run by root * * @return int, 1 if successful, 0 for nothing, -1 for fatal error * --------------------------------------------------------------------------- */ option_type type; void *value_param = NULL; int uid = -1, gid = -1; const char *name = NULL, *group = NULL; int retval = 0; if (getuid() == 0) { /* Get uid by name, or by numeric value */ value_param = get_option("system_user", &type); if (value_param != NULL) { switch(type) { case OPTION_STRING: name = (const char *)value_param; uid = getuidbyname(name); if (uid == -1) logmsg(LOG_INFO, "drop_privileges() : cannot find user %s", name); break; default: break; } } value_param = get_option("system_uid", &type); if (value_param != NULL) { if (name != NULL) { logmsg(LOG_INFO, "drop_privileges() : define \"system_user\"" "or \"system_uid\", not both"); return -1; } switch(type) { case OPTION_INT: uid = *((int *)value_param); break; default: break; } } /* Then, get gid by name, or by numeric value */ value_param = get_option("system_group", &type); if (value_param != NULL) { switch(type) { case OPTION_STRING: group = (const char *)value_param; gid = getgidbyname(group); if (gid == -1) logmsg(LOG_INFO, "drop_privileges() : cannot find group %s", group); break; default: break; } } value_param = get_option("system_gid", &type); if (value_param != NULL) { if (group != NULL) { logmsg(LOG_INFO, "drop_privileges() : define \"system_group\"" "or \"system_gid\", not both"); return -1; } switch(type) { case OPTION_INT: gid = *((int *)value_param); break; default: break; } } if (g_lock_fd >= 0) { if (uid > 0) { /* Try to chown lock file if exists */ if (fchown(g_lock_fd, uid, gid) < 0) { if (gid > 0) { logmsg(LOG_ERR, "drop_privileges() : cannot chown lock file" " to uid %d, gid %d : %s", uid, gid, strerror(errno)); } else logmsg(LOG_ERR, "drop_privileges() : cannot chown lock file" " to uid %d : %s", uid, strerror(errno)); } } } if (gid > 0) { /* Try to setgid */ if (setgid(gid) == 0) { if (group != NULL) logmsg(LOG_INFO, "drop_privileges() : setgid to %s (gid %d)", group, gid); else logmsg(LOG_INFO, "drop_privileges() : setgid to %d", gid); retval = 1; } else { logmsg(LOG_ERR, "drop_privileges() : cannot setgid to %d : %s", gid, strerror(errno)); retval = -1; } } if (uid > 0) { /* Try to setuid */ if (setuid(uid) == 0) { if (name != NULL) logmsg(LOG_INFO, "drop_privileges() : setuid to %s (uid %d)", name, uid); else logmsg(LOG_INFO, "drop_privileges() : setuid to %d", uid); retval = 1; } else { logmsg(LOG_ERR, "drop_privileges() : cannot setuid to %d : %s", uid, strerror(errno)); retval = -1; } } } else logmsg(LOG_ERR, "drop_privileges() : only root can drop privileges"); return retval; } int getpollfrequency() { /* ----------------------------------------------------------------------------- * Get the poll frequency in second * * @return int * -------------------------------------------------------------------------- */ option_type type; void *value = NULL; value = get_option("pollfrequency", &type); if (value != NULL) { switch(type) { case OPTION_INT: return *((int *)value); break; default: break; } } return DEFAULT_POLL_FREQUENCY; } int get_domain_lifetime() { /* ----------------------------------------------------------------------------- * Get the number of days in order to force a dyndns update * * @return int * -------------------------------------------------------------------------- */ option_type type; void *value = NULL; value = get_option("domain_lifetime", &type); if (value != NULL) { switch(type) { case OPTION_INT: return *((int *)value); break; default: break; } } return DEFAULT_DOMAIN_LIFETIME; } int use_syslog() { /* ----------------------------------------------------------------------------- * See if syslog should be enabled * It is only used in non daemon mode * * @return int * -------------------------------------------------------------------------- */ option_type type; void *value = NULL; value = get_option("use_syslog", &type); if (value != NULL) { switch(type) { case OPTION_INT: return *((int *)value); break; default: break; } } return 0; } int syslog_facility() { /* ----------------------------------------------------------------------------- * Get the syslog facility * If unknown, LOG_DAEMON is returned * * @param const char * * @return int * -------------------------------------------------------------------------- */ option_type type; void *value = NULL; const char *name = NULL; value = get_option("syslog_facility", &type); if (value != NULL) { switch(type) { case OPTION_STRING: name = (const char *)value; break; default: break; } } if (name == NULL) return LOG_DAEMON; if (strcmp(name, "auth") == 0) return LOG_AUTH; else if (strcmp(name, "authpriv") == 0) return LOG_AUTHPRIV; #if defined __FreeBSD__ else if (strcmp(name, "console") == 0) return LOG_CONSOLE; #endif else if (strcmp(name, "cron") == 0) return LOG_CRON; else if (strcmp(name, "daemon") == 0) return LOG_DAEMON; else if (strcmp(name, "ftp") == 0) return LOG_FTP; else if (strcmp(name, "lpr") == 0) return LOG_LPR; else if (strcmp(name, "mail") == 0) return LOG_MAIL; else if (strcmp(name, "news") == 0) return LOG_NEWS; #if defined __FreeBSD__ else if (strcmp(name, "ntp") == 0) return LOG_NTP; else if (strcmp(name, "security") == 0) return LOG_SECURITY; #endif else if (strcmp(name, "syslog") == 0) return LOG_SYSLOG; else if (strcmp(name, "user") == 0) return LOG_USER; else if (strcmp(name, "uucp") == 0) return LOG_UUCP; else if (strcmp(name, "local0") == 0) return LOG_LOCAL0; else if (strcmp(name, "local1") == 0) return LOG_LOCAL1; else if (strcmp(name, "local2") == 0) return LOG_LOCAL2; else if (strcmp(name, "local3") == 0) return LOG_LOCAL3; else if (strcmp(name, "local4") == 0) return LOG_LOCAL4; else if (strcmp(name, "local5") == 0) return LOG_LOCAL5; else if (strcmp(name, "local6") == 0) return LOG_LOCAL6; else if (strcmp(name, "local7") == 0) return LOG_LOCAL7; return LOG_DAEMON; } int dyndns(const char *ip, struct chain_string *hostnames) { /* ----------------------------------------------------------------------------- * Do the DynDNS update * * @param const char *, the IP address to use for update * @param struct chain_string *, the hostnames to update * @return int * -------------------------------------------------------------------------- */ Socket *s = NULL; option_type type; int retval = 0; void *value = NULL; char *request = NULL, *servername = NULL; struct chain_string *chain_tmp = NULL; char *username = NULL, *password = NULL, *auth = NULL; size_t size_request = 0, size; char *auth_b64 = NULL; char http_port[4]; char *ssl_pathstore = NULL; value = get_option("servername", &type); if (value == NULL) { logmsg(LOG_ERR, "dyndns() : you must define the variable \"servername\" in your config file !"); return 0; } else if (type != OPTION_STRING) { logmsg(LOG_ERR, "dyndns() : the variable \"servername\" must be a valid string !"); return 0; } /* All is OK, here */ servername = (char *)value; request = strdup("GET /nic/update?system=dyndns"); if (request == NULL) { logmsg(LOG_ERR, "dyndns() : %s !", strerror(errno)); return 0; } size_request = strlen(request); /* username (mandatory) */ value = get_option("username", &type); if (value == NULL) { logmsg(LOG_ERR, "dyndns() : \"username\" is mandatory !"); FREE(request); return 0; } switch(type) { case OPTION_STRING: username = (char *)value; break; default: logmsg(LOG_ERR, "dyndns() : \"username\" is not a valid option !"); FREE(request); return 0; break; } /* password (mandatory) */ value = get_option("password", &type); if (value == NULL) { logmsg(LOG_ERR, "dyndns() : \"password\" is mandatory !"); FREE(request); return 0; } switch(type) { case OPTION_STRING: password = (char *)value; break; default: logmsg(LOG_ERR, "dyndns() : \"password\" is not a valid option !"); FREE(request); return 0; break; } /* hostname (mandatory) */ size_request += strlen("&hostname=") + 1; if (!saferealloc((void **)&request, (void *)request, size_request * sizeof(char))) { FREE(request); return 0; } strlcat(request, "&hostname=", size_request * sizeof(char)); for (chain_tmp = hostnames; chain_tmp != NULL; chain_tmp = chain_tmp->next) { size_request += strlen(chain_tmp->value) + (chain_tmp->next == NULL ? 1 : 2); /* For a comma */ if (!saferealloc((void **)&request, (void *)request, size_request * sizeof(char))) { FREE(request); return 0; } strlcat(request, (const char *)chain_tmp->value, size_request * sizeof(char)); if (chain_tmp->next != NULL) /* Add a comma because we are not at the end ot the chain_string */ strlcat(request, ",", size_request * sizeof(char)); } /* wildcard (optional) */ value = get_option("wildcard", &type); if (value != NULL) { switch(type) { case OPTION_INT: size_request += strlen("&wildcard=") + 1; if (!saferealloc((void **)&request, request, size_request * sizeof(char))) { FREE(request); return 0; } strlcat(request, "&wildcard=", size_request * sizeof(char)); if (*((int *)value)) { size_request += strlen("ON") + 1; if (!saferealloc((void **)&request, request, size_request * sizeof(char))) { FREE(request); return 0; } strlcat(request, "ON", size_request * sizeof(char)); } else { size_request += strlen("OFF") + 1; if (!saferealloc((void **)&request, request, size_request * sizeof(char))) { FREE(request); return 0; } strlcat(request, "OFF", size_request * sizeof(char)); } break; default: logmsg(LOG_ERR, "dyndns() : \"wildcard\" is not a valid option !"); FREE(request); return 0; break; } } /* mx (optional) */ value = get_option("mx", &type); if (value != NULL) { switch(type) { case OPTION_STRING: size_request += strlen("&mx=") + 1 + strlen((const char *)value) + 1; if (!saferealloc((void **)&request, request, size_request * sizeof(char))) { FREE(request); return 0; } strlcat(request, "&mx=", size_request * sizeof(char)); strlcat(request, (const char *)value, size_request * sizeof(char)); break; default: logmsg(LOG_ERR, "dyndns() : \"mx\" is not a valid option !"); FREE(request); return 0; break; } } /* backupmx (optional) */ value = get_option("backupmx", &type); if (value != NULL) { switch(type) { case OPTION_INT: size_request += strlen("&backmx=") + 1; if (!saferealloc((void **)&request, request, size_request * sizeof(char))) { FREE(request); return 0; } strlcat(request, "&backmx=", size_request * sizeof(char)); if (*((int *)value)) { size_request += strlen("YES") + 1; if (!saferealloc((void **)&request, request, size_request * sizeof(char))) { FREE(request); return 0; } strlcat(request, "YES", size_request * sizeof(char)); } else { size_request += strlen("NO")+1; if (!saferealloc((void **)&request, request, size_request * sizeof(char))) { FREE(request); return 0; } strlcat(request, "NO", size_request * sizeof(char)); } break; default: logmsg(LOG_ERR, "dyndns() : \"backupmx\" is not a valid option !"); FREE(request); return 0; break; } } /* offline (optional) */ value = get_option("offline", &type); if (value != NULL) { switch(type) { case OPTION_INT: size_request += strlen("&offline=") + 1; if (!saferealloc((void **)&request, request, size_request * sizeof(char))) { FREE(request); return 0; } strlcat(request, "&offline=", size_request * sizeof(char)); if (*((int *)value)) { size_request += strlen("YES") + 1; if (!saferealloc((void **)&request, request, size_request * sizeof(char))) { FREE(request); return 0; } strlcat(request, "YES", size_request * sizeof(char)); } else { size_request += strlen("NO") + 1; if (!saferealloc((void **)&request, request, size_request * sizeof(char))) { FREE(request); return 0; } strlcat(request, "NO", size_request * sizeof(char)); } break; default: logmsg(LOG_ERR, "dyndns() : \"offline\" is not a valid option !"); FREE(request); return 0; break; } } /* use_ssl (optional) */ value = get_option("use_ssl", &type); if (value != NULL) { switch(type) { case OPTION_INT: if (*((int *)value)) { strlcpy(http_port, "443", sizeof(http_port)); /* use HTTPS */ set_socket_ssl(1); /* Define a new trust certificate path */ value = get_option("cert_path", &type); if (value != NULL) { switch(type) { case OPTION_STRING: /* Retrieve certificate file path from config file */ ssl_pathstore = strdup((char *)value); break; default: logmsg(LOG_ERR, "dyndns() : \"cert_path\" is not a valid option !"); FREE(request); return 0; break; } } else { /* Pick the default one */ ssl_pathstore = strdup(CERT_TRUST_STORE); } if (ssl_pathstore == NULL) { logmsg(LOG_ERR, "dyndns() : %s", socket_strerror(errno)); FREE(request); return 0; } set_ssl_pathstore(ssl_pathstore); logmsg(LOG_INFO, "dyndns() : Setting SSL trust certificate store to %s", ssl_pathstore); } else strlcpy(http_port, "80", sizeof(http_port)); /* use HTTP */ break; default: logmsg(LOG_ERR, "dyndns() : \"use_ssl\" is not a valid option !"); FREE(request); return 0; break; } } else strlcpy(http_port, "80", sizeof(http_port)); /* use HTTP by default */ /* IP address */ size_request += strlen("&myip=")+1 + strlen(ip) + 1; if (!saferealloc((void **)&request, request, size_request * sizeof(char))) { FREE(request); return 0; } strlcat(request, "&myip=", size_request * sizeof(char)); strlcat(request, ip, size_request * sizeof(char)); /* Send the request to the DynDNS server */ size_request += strlen(" HTTP/1.0") + 1; if (!saferealloc((void **)&request, request, size_request * sizeof(char))) { FREE(request); return 0; } strlcat(request, " HTTP/1.0", size_request * sizeof(char)); /* Create a new Socket */ s = socket_new(servername, http_port, "tcp"); if (s == NULL) { logmsg(LOG_ERR, "dyndns() : cannot create socket : %s", socket_strerror(errno)); FREE(request); return 0; } /* Connect to DynDNS server */ if (!socket_connect(s)) { logmsg(LOG_ERR, "dyndns() : connect() error on %s : %s", servername, socket_strerror(errno)); FREE(request); socket_destroy(s); return 0; } logmsg(LOG_INFO, "dyndns() : connected to %s:%s", servername, http_port); logmsg(LOG_INFO, "dyndns() : %s", request); if (!socket_write(s, 0, "%s\n", request)) { logmsg(LOG_ERR, "dyndns() : %s", socket_strerror(errno)); FREE(request); socket_destroy(s); return 0; } FREE(request); /* Send the HTTP header to the DynDNS server */ if (!socket_write(s, 0, "Host: %s\n", servername)) { logmsg(LOG_ERR, "dyndns() : %s", socket_strerror(errno)); socket_destroy(s); return 0; }; /* Base64 encoding username:password */ size = strlen(username) + strlen(password) + 2; auth = malloc(size * sizeof(char)); if (auth == NULL) { logmsg(LOG_ERR, "dyndns() : %s", strerror(errno)); socket_destroy(s); return 0; } *auth = '\0'; strlcat(auth, username, size * sizeof(char)); strlcat(auth, ":", size * sizeof(char)); strlcat(auth, password, size * sizeof(char)); if (!base64_encode(&auth_b64, auth, strlen(auth))) { logmsg(LOG_ERR, "dyndns() : base64_encode error"); socket_destroy(s); return 0; } FREE(auth); /* Send Authentification info */ if (!socket_write(s, 0, "Authorization: Basic %s\n", auth_b64)) { logmsg(LOG_ERR, "dyndns() : %s", socket_strerror(errno)); FREE(auth_b64); socket_destroy(s); return 0; } FREE(auth_b64); /* Send User-Agent */ if (!socket_write(s, 0, "User-Agent: OpenDD %s\n", VERSION)) { logmsg(LOG_ERR, "dyndns() : %s", socket_strerror(errno)); socket_destroy(s); return 0; } /* End Request */ if (!socket_write(s, 0, "\n")) { logmsg(LOG_ERR, "dyndns() : %s", socket_strerror(errno)); socket_destroy(s); return 0; } /* * Get HTTP response from server. * listen_response() will destroy the socket, * so "s" will be freed */ retval = listen_response(s); s = NULL; logmsg(LOG_INFO, "dyndns() : connection closed"); switch(retval) { case EXIT_DYNDNS_FAILED: /* Only returned an error if error(s) occured */ return EXIT_DYNDNS_FAILED; break; default: return EXIT_DYNDNS_SUCCESS; } } int listen_response(Socket *s) { /* ----------------------------------------------------------------------------- * Interpret response from DynDNS server * * @param Socket *, the client socket connected to DynDNS server * @return int, return code can be EXIT_DYNDNS_SUCCESS or EXIT_DYNDNS_FAILED * -------------------------------------------------------------------------- */ fd_set fds; struct timeval tv; char buffer[255]; int retval = EXIT_DYNDNS_SUCCESS, len_buffer = 0; int n = 0, byte_read = 0; int http_header_read = 0; char *server_response = NULL; size_t sizemsg_prec = 0, sizemsg = 0; /* Variables for mail result by DynDNS hostname */ char *mailmsg = NULL; char *host_result_report = NULL; size_t size_result = 0, size_result_prec = 0; int offset = 0, i = 0; /* Variables for getting "hostname" parameter */ void *value = NULL; char *hostname = NULL; int len_hostname = 0; option_type type; option *opt = NULL; struct chain *chain_tmp = NULL; value = get_option("hostname", &type); for(;;) { FD_ZERO(&fds); FD_SET(s->fd, &fds); tv.tv_sec = 2; tv.tv_usec = 0; if ((n = select(s->fd+1, &fds, NULL, NULL, &tv)) != -1) { if ((n > 0) && (FD_ISSET(s->fd, &fds))) { memset(buffer, 0, 255); if ((byte_read = socket_readline(s, buffer, sizeof(buffer))) > 0) { stripcrlf(buffer); len_buffer = strlen(buffer); if (len_buffer > 0) { logmsg(LOG_INFO, "listen_response() : %s", buffer); sizemsg_prec = sizemsg; sizemsg += len_buffer + 1; /* buffer + '\n' */ if (!saferealloc((void **)&server_response, server_response, (sizemsg + 1) * sizeof(char))) { logmsg(LOG_ERR, "listen_response() : %s", strerror(errno)); return 0; } memcpy(&server_response[sizemsg_prec], buffer, len_buffer * sizeof(char)); server_response[sizemsg-1] = '\n'; server_response[sizemsg] = '\0'; if (http_header_read) { /* The response comes after the HTTP header */ hostname = NULL; switch(type) { case OPTION_STRING: hostname = (char *)value; break; case OPTION_CHAIN: if (chain_tmp == NULL) chain_tmp = (struct chain *)value; opt = (option *)chain_tmp->value; switch(opt->type) { case OPTION_STRING: hostname = (char *)opt->value; break; default: break; } chain_tmp = chain_tmp->next; break; default: break; } if (hostname != NULL) { /* Response analysis */ len_hostname = strlen(hostname); size_result_prec = size_result; #define SEPARATOR1 " -> " #define SEPARATOR2 " : " for (i = 0; update_table[i] != NULL; i++) { if (strncmp(buffer, update_table[i], strlen(update_table[i])) == 0) { size_result += len_hostname + strlen(SEPARATOR1) + len_buffer + strlen(SEPARATOR2) + strlen(message_table[i]) + 1; break; } } if (update_table[i] != NULL) { if (!saferealloc((void **)&host_result_report, host_result_report, (size_result + 1) * sizeof(char))) { FREE(server_response); logmsg(LOG_ERR, "listen_response() : %s", strerror(errno)); return 0; } offset = size_result_prec; memcpy(&host_result_report[offset], hostname, len_hostname * sizeof(char)); offset += len_hostname * sizeof(char); memcpy(&host_result_report[offset], SEPARATOR1, strlen(SEPARATOR1) * sizeof(char)); offset += strlen(SEPARATOR1) * sizeof(char); memcpy(&host_result_report[offset], buffer, len_buffer * sizeof(char)); offset += len_buffer * sizeof(char); memcpy(&host_result_report[offset], SEPARATOR2, strlen(SEPARATOR2) * sizeof(char)); offset += strlen(SEPARATOR2) * sizeof(char); memcpy(&host_result_report[offset], message_table[i], strlen(message_table[i]) * sizeof(char)); offset += strlen(message_table[i]) * sizeof(char); host_result_report[offset] = '\n'; offset += sizeof(char); host_result_report[offset] = '\0'; logmsg(LOG_INFO, "listen_response() : %s", message_table[i]); if (strcmp(update_table[i], RETURN_CODE_GOOD) == 0) { /* Successful update */ retval = EXIT_DYNDNS_SUCCESS; } else if (strcmp(update_table[i], RETURN_CODE_NOCHG) == 0) { /* Force abuse update : * Warn the user */ retval = EXIT_DYNDNS_SUCCESS; } else { /* Update error occurred * DynDNS update process must be stopped */ retval = EXIT_DYNDNS_FAILED; } } else { /* Here, we have received an unexpected response code. * That means the dyndns protocol could have been changed * Contact the OpenDD developer(s) */ FREE(server_response); FREE(host_result_report); logmsg(LOG_ERR, "listen_response() : unexpected response : %s", buffer); logmsg(LOG_ERR, "listen_response() : Please contact the developers at %s", DEVELOPER_EMAIL_CONTACT); return 0; } } else { /* We should not be here !! */ logmsg(LOG_ERR, "listen_response() : cannot find hostname for report"); } } } else { /* A simple carriage return */ http_header_read = 1; sizemsg_prec = sizemsg; sizemsg += 1; /* '\n' */ if (!saferealloc((void **)&server_response, server_response, sizemsg * sizeof(char))) { FREE(server_response); FREE(host_result_report); logmsg(LOG_ERR, "listen_response() : %s", strerror(errno)); return 0; } server_response[sizemsg_prec] = '\n'; } } else { break; } } else { break; } } else break; } FD_ZERO(&fds); /* Destroy the socket, immediately */ socket_destroy(s); /* Some results have been retrieved for, at least, one hostname */ if ((server_response != NULL) && (host_result_report != NULL)) { #define MAIL_SUBJECT "Subject: Status Report from OpenDD" #define MAIL_HEAD "---------------------- BEGIN SERVER RESPONSE ----------------------\n" #define MAIL_FOOT "----------------------- END SERVER RESPONSE ------------------------\n\n" #define SIGNATURE "\n\n Regards,\nOpenDD " /* Contruct a mail message to send */ mailmsg = strdup(MAIL_HEAD); if (mailmsg == NULL) { FREE(server_response); FREE(host_result_report); logmsg(LOG_ERR, "listen_response() : %s", strerror(errno)); return 0; } sizemsg += strlen(mailmsg) + strlen(MAIL_FOOT) + strlen(host_result_report) + strlen(SIGNATURE) + strlen(VERSION) + 1 + /* \n */ 1; if (!saferealloc((void **)&mailmsg, mailmsg, sizemsg * sizeof(char))) { FREE(server_response); FREE(mailmsg); FREE(host_result_report); logmsg(LOG_ERR, "listen_response() : %s", strerror(errno)); return 0; } strlcat(mailmsg, server_response, sizemsg * sizeof(char)); strlcat(mailmsg, MAIL_FOOT, sizemsg * sizeof(char)); strlcat(mailmsg, host_result_report, sizemsg * sizeof(char)); strlcat(mailmsg, SIGNATURE, sizemsg * sizeof(char)); strlcat(mailmsg, VERSION, sizemsg * sizeof(char)); strlcat(mailmsg, "\n", sizemsg * sizeof(char)); FREE(server_response); FREE(host_result_report); /* We send the report */ if (!send_report(MAIL_SUBJECT, mailmsg)) logmsg(LOG_ERR, "listen_response() : cannot send mail report"); else logmsg(LOG_INFO, "listen_response() : mail report sent !"); FREE(mailmsg); } else if (host_result_report != NULL) FREE(host_result_report); return retval; }