Module: sip-router
Branch: master
Commit: fa09f630c284cb13ad9e3ad34d433bdbdda6750b
URL:
http://git.sip-router.org/cgi-bin/gitweb.cgi/sip-router/?a=commit;h=fa09f63…
Author: Miklos Tirpak <miklos(a)iptel.org>
Committer: Miklos Tirpak <miklos(a)iptel.org>
Date: Tue Jun 15 10:15:46 2010 +0200
dns_cache: dns_cache_delete_single_record() added
The function deletes a single resource record
of an existing DNS entry. If the entry has only one
record then the entire entry is deleted.
The resource record is identified by its name, type,
and value, i.e. ip addres in case of A/AAAA record,
target name in case of SRV record.
Only A, AAAA, and SRV records are supported.
---
dns_cache.c | 151 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
dns_cache.h | 13 +++++-
2 files changed, 163 insertions(+), 1 deletions(-)
diff --git a/dns_cache.c b/dns_cache.c
index 7605ee6..675df80 100644
--- a/dns_cache.c
+++ b/dns_cache.c
@@ -4313,6 +4313,157 @@ static void dns_cache_delete_record(rpc_t* rpc, void* ctx,
unsigned short type)
rpc->fault(ctx, 400, "Not found");
}
+/* Delete a single record from the cache,
+ * i.e. the record with the same name and value
+ * (ip address in case of A/AAAA record, name in case of SRV record).
+ *
+ * Currently only A, AAAA, and SRV records are supported.
+ */
+int dns_cache_delete_single_record(unsigned short type,
+ str *name,
+ str *value,
+ int flags)
+{
+ struct dns_hash_entry *old=NULL, *new=NULL;
+ struct dns_rr *rr, **next_p;
+ str rr_name;
+ struct ip_addr *ip_addr;
+ int err, h;
+
+ /* eliminate gcc warnings */
+ rr_name.s = NULL;
+ rr_name.len = 0;
+ ip_addr = 0;
+
+ if (!cfg_get(core, core_cfg, use_dns_cache)){
+ LOG(L_ERR, "ERROR: dns cache support disabled (see use_dns_cache)\n");
+ return -1;
+ }
+
+ if ((type != T_A) && (type != T_AAAA) && (type != T_SRV)) {
+ LOG(L_ERR, "ERROR: rr type %d is not implemented\n",
+ type);
+ return -1;
+ }
+
+ if (!flags) {
+ /* fix-up the values */
+ switch(type) {
+ case T_A:
+ ip_addr = str2ip(value);
+ if (!ip_addr) {
+ LOG(L_ERR, "ERROR: Malformed ip address: %.*s\n",
+ value->len, value->s);
+ return -1;
+ }
+ break;
+ case T_AAAA:
+#ifdef USE_IPV6
+ ip_addr = str2ip6(value);
+ if (!ip_addr) {
+ LOG(L_ERR, "ERROR: Malformed ip address: %.*s\n",
+ value->len, value->s);
+ return -1;
+ }
+ break;
+#else /* USE_IPV6 */
+ LOG(L_ERR, "ERROR: IPv6 support is disabled\n");
+ return -1;
+#endif /* USE_IPV6 */
+ case T_SRV:
+ rr_name = *value;
+ break;
+ }
+ }
+
+ /* check whether there is a matching entry in the cache */
+ if ((old = dns_hash_get(name, type, &h, &err)) == NULL)
+ goto not_found;
+
+ if ((old->type != type) /* may be CNAME */
+ || (old->err_flags != flags)
+ )
+ goto not_found;
+
+ if (flags) /* negative record, there is no value */
+ goto delete;
+
+ /* check whether there is an rr with the same value */
+ for (rr=old->rr_lst, next_p=&old->rr_lst;
+ rr;
+ next_p=&rr->next, rr=rr->next
+ )
+ if ((((type == T_A) || (type == T_AAAA)) &&
+ (memcmp(ip_addr->u.addr, ((struct a_rdata*)rr->rdata)->ip,
+ ip_addr->len)==0))
+ || ((type == T_SRV) &&
+ (((struct srv_rdata*)rr->rdata)->name_len == rr_name.len) &&
+ (memcmp(rr_name.s, ((struct srv_rdata*)rr->rdata)->name,
+ rr_name.len)==0)))
+ break;
+
+ if (!rr)
+ goto not_found;
+
+ if ((rr == old->rr_lst) && (rr->next == NULL)) {
+ /* There is a single rr value, hence the whole
+ * hash entry can be deleted */
+ goto delete;
+ } else {
+ /* we must modify the entry, so better to clone it, modify the new
+ * one, and replace the old with the new entry in the hash table,
+ * because the entry might be in use (even if the dns hash is
+ * locked). The old entry will be removed from the hash and
+ * automatically destroyed when its refcnt will be 0*/
+ new = dns_cache_clone_entry(old, 0, 0, 0);
+ if (!new) {
+ LOG(L_ERR, "ERROR: Failed to clone an existing "
+ "DNS cache entry\n");
+ dns_hash_put(old);
+ return -1;
+ }
+ /* let rr and next_p point to the new structure */
+ rr = (struct dns_rr*)translate_pointer((char*)new,
+ (char*)old,
+ (char*)rr);
+ next_p = (struct dns_rr**)translate_pointer((char*)new,
+ (char*)old,
+ (char*)next_p);
+ /* unlink rr from the list. The memory will be freed
+ * when the whole record is freed */
+ *next_p = rr->next;
+ }
+
+delete:
+ LOCK_DNS_HASH();
+ if (new) {
+ /* delete the old entry only if the new one can be added */
+ if (dns_cache_add_unsafe(new)) {
+ LOG(L_ERR, "ERROR: Failed to add the entry to the cache\n");
+ UNLOCK_DNS_HASH();
+ if (old)
+ dns_hash_put(old);
+ return -1;
+ } else {
+ /* remove the old entry from the list */
+ if (old)
+ _dns_hash_remove(old);
+ }
+ } else if (old) {
+ _dns_hash_remove(old);
+ }
+ UNLOCK_DNS_HASH();
+
+ if (old)
+ dns_hash_put(old);
+ return 0;
+
+not_found:
+ LOG(L_ERR, "ERROR: No matching record found\n");
+ if (old)
+ dns_hash_put(old);
+ return -1;
+}
/* performs a dns lookup over rpc */
void dns_cache_rpc_lookup(rpc_t* rpc, void* ctx)
diff --git a/dns_cache.h b/dns_cache.h
index a8a4bd4..ffacd02 100644
--- a/dns_cache.h
+++ b/dns_cache.h
@@ -350,7 +350,7 @@ void dns_set_server_state(int state);
int dns_get_server_state(void);
#endif /* DNS_WATCHDOG_SUPPORT */
-/* Adds a new record to the cache.
+/** @brief Adds a new record to the cache.
* If there is an existing record with the same name and value
* (ip address in case of A/AAAA record, name in case of SRV record)
* only the remaining fields are updated.
@@ -366,5 +366,16 @@ int dns_cache_add_record(unsigned short type,
int port,
int flags);
+/** @brief Delete a single record from the cache,
+ * i.e. the record with the same name and value
+ * (ip address in case of A/AAAA record, name in case of SRV record).
+ *
+ * Currently only A, AAAA, and SRV records are supported.
+ */
+int dns_cache_delete_single_record(unsigned short type,
+ str *name,
+ str *value,
+ int flags);
+
#endif