/* * The client for performing dynamic DNS updates, called by dotsd.c * * (c) Columbia University, 2004-2006, All Rights Reserved. * Author: Weibin Zhao */ #include #include // #include // #include // can replace and #include #include #include #include #include "mod_dots.h" /* * Make DNS query for IP address, use the first RR * Parameters: * query: query name * Return value: * -1 error * 0 success */ int dns_ip_query(char *query, char *ip) { union { HEADER hdr; u_char buf[8*1024]; } response; ns_msg handle; ns_rr rr; int len; char name[MAXNAME]; if (query == NULL) { ap_log_error(APLOG_MARK, APLOG_NOTICE, 0, NULL, "DNS query is NULL"); return -1; } // add trailing dot if "query" does not have it len = strlen(query); if (query[len-1] == '.') { strcpy(name, query); } else { sprintf(name, "%s.", query); } // make query, the return value is response length len = res_query(name, C_IN, T_A, (u_char *)&response, sizeof(response)); // initialize message handle ns_initparse(response.buf, len, &handle); // make sure that the answer section has at least one RR if (ns_msg_count(handle, ns_s_an) == 0) { ap_log_error(APLOG_MARK, APLOG_NOTICE, 0, NULL, "Answer section has zero RR"); return -1; } // get the first RR from the answer section if (ns_parserr(&handle, ns_s_an, 0, &rr) < 0) { ap_log_error(APLOG_MARK, APLOG_NOTICE, 0, NULL, "Parse error for the first RR in answer section"); return -1; } strcpy(ip, inet_ntoa(*(struct in_addr *)ns_rr_rdata(rr))); ap_log_error(APLOG_MARK, APLOG_NOTICE, 0, NULL, "name=%s, ip=%s", ns_rr_name(rr), ip); return 0; } /* * Make DNS query for SRV resource records, use the first RR * Parameters: * query: query name in the form of _Service._Proto.Name * target: SRV target * port: SRV port * Return value: * -1 error * 0 success */ int dns_srv_query(char *query, char *target, short *port) { union { HEADER hdr; u_char buf[4*1024]; } response; ns_msg handle; ns_rr rr; u_char buf[256], *p; int t, len, priority, weight; // make query, the return value is response length len = res_query(query, C_IN, T_SRV, (u_char *)&response, sizeof(response)); // initialize message handle ns_initparse(response.buf, len, &handle); // make sure that the answer section has at least one RR if (ns_msg_count(handle, ns_s_an) == 0) { ap_log_error(APLOG_MARK, APLOG_NOTICE, 0, NULL, "Answer section has zero RR"); return -1; } // get the first RR from the answer section if (ns_parserr(&handle, ns_s_an, 0, &rr) < 0) { ap_log_error(APLOG_MARK, APLOG_NOTICE, 0, NULL, "Parse error for the first RR in answer section"); return -1; } len = ns_rr_rdlen(rr); memcpy(buf, ns_rr_rdata(rr), len); p = buf; priority = ntohs(*(short *)p); p += 2; weight = ntohs(*(short *)p); p += 2; *port = ntohs(*(short *)p); p += 2; while (*p > 0) { t = *p; *p = '.'; p += t+1; } p = buf + 7; strcpy(target, p); ap_log_error(APLOG_MARK, APLOG_NOTICE, 0, NULL, "name=%s port=%d target=%s", ns_rr_name(rr), *port, target); return 0; } /* * Performing dynamic updates in DNS (unsigned) * Parameters: * update_opcode: ns_uop_delete, ns_uop_add * update_name: FQDN * update_type: ns_t_a, ns_t_cname, ns_t_any, etc. * update_data: IP address, NULL, etc. * Return value: * -1 error * 0 update fail * 1 update success */ int dns_update_unsigned(int update_opcode, char *update_name, int update_type, char *update_data) { int rval, r_ttl; ns_updrec *update_sec; char opstr[10]; /* Choose a right TTL based on update_opcode */ if (update_opcode == ns_uop_add) { r_ttl = 1800; /* 30 minutes */ strcpy(opstr, "add"); } else if (update_opcode == ns_uop_delete) { r_ttl = 0; /* MUST be zero */ strcpy(opstr, "delete"); } else { /* other update_opcode is NOT supported */ ap_log_error(APLOG_MARK, APLOG_NOTICE, 0, NULL, "invalid update_opcode: %d", update_opcode); return -1; } /* * Compose an update record for the update section "ns_s_ud". * We do not need to worry about the zone section since * res_update() will find the proper zone automatically * and prepend a zone section for us. However, we must * use "ns_c_in" as the CLASS */ update_sec = (ns_updrec *) res_mkupdrec(ns_s_ud, update_name, ns_c_in, update_type, r_ttl); if (update_sec == NULL) { ap_log_error(APLOG_MARK, APLOG_NOTICE, 0, NULL, "error in res_mkupdrec: %s", strerror(errno)); return -1; } /* Set the update record properly: r_opcode/r_data/r_size */ update_sec->r_opcode = update_opcode; if (update_data != NULL) { update_sec->r_size = strlen(update_data); update_sec->r_data = (u_char *) update_data; } else { update_sec->r_size = 0; update_sec->r_data = NULL; } // Set DNS update server for efficiency if (ddns_server_set == 1) dns_set_update_server(ddns_server, 53); /* Perform the real DNS update */ rval = res_update(update_sec); ap_log_error(APLOG_MARK, APLOG_NOTICE, 0, NULL, "%d zone has been updated: %s %s(%s)", rval, opstr, update_name, update_data); /* Be sure to free the update record space */ res_freeupdrec(update_sec); /* Indicate the number of zone has been updated */ return rval; } // Set DNS update server for efficiency int dns_set_update_server(char *serv, int serv_port) { struct hostent *hp; char **p; static struct in_addr serv_addr; static int first_time = 1; if (first_time == 1) { hp = gethostbyname(serv); // try domain name first if (hp == NULL) { // also try IP address if domain name fail hp = gethostbyaddr(serv, strlen(serv), AF_INET); if (hp == NULL) { ap_log_error(APLOG_MARK, APLOG_CRIT, 0, NULL, "cannot resolve: %s", serv); return -1; } } first_time = 0; p = hp->h_addr_list; memcpy(&serv_addr.s_addr, *p, sizeof(serv_addr.s_addr)); ap_log_error(APLOG_MARK, APLOG_NOTICE, 0, NULL, "DNS update server has been set to [%s:%d]", inet_ntoa(serv_addr), serv_port); } res_init(); _res.nsaddr_list[0].sin_addr = serv_addr; _res.nsaddr_list[0].sin_port = htons(serv_port); return 0; } int dns_update(int op, char *name, char *ip) { char opstr[10], cmd[1024], fname[64], server[64]; int opcode, rval, i; if (op == DNS_ADD) { strcpy(opstr, "add"); opcode = ns_uop_add; } else if (op == DNS_DEL) { strcpy(opstr, "delete"); opcode = ns_uop_delete; } else { return -1; } if (name == NULL || ip == NULL) return -1; i = strlen(name); if (i>0 && name[i-1] == '.') { strcpy(fname, name); } else { sprintf(fname, "%s.", name); // add trailing dot to name if needed } if (ddns_server_set) { // set the DNS server for sending updates strcpy(server, ddns_server); } else { strcpy(server, "NULL"); } if (dns_tsig_set) { // signed sprintf(cmd, "%s %s:%s %s %s %s %s", ddns_script, my_tsigkey, my_tsigsecret, opstr, fname, ip, server); rval = system(cmd); ap_log_error(APLOG_MARK, APLOG_NOTICE, 0, NULL, "nsupdate: %s %s(%s), rval=%d", opstr, fname, ip, rval); } else { // unsigned rval = dns_update_unsigned(opcode, fname, ns_t_a, ip); } return rval; }