Browse code

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.

Miklos Tirpak authored on 15/06/2010 08:15:46
Showing 2 changed files
... ...
@@ -4313,6 +4313,157 @@ static void dns_cache_delete_record(rpc_t* rpc, void* ctx, unsigned short type)
4313 4313
 		rpc->fault(ctx, 400, "Not found");
4314 4314
 }
4315 4315
 
4316
+/* Delete a single record from the cache,
4317
+ * i.e. the record with the same name and value
4318
+ * (ip address in case of A/AAAA record, name in case of SRV record).
4319
+ *
4320
+ * Currently only A, AAAA, and SRV records are supported.
4321
+ */
4322
+int dns_cache_delete_single_record(unsigned short type,
4323
+			str *name,
4324
+			str *value,
4325
+			int flags)
4326
+{
4327
+	struct dns_hash_entry *old=NULL, *new=NULL;
4328
+	struct dns_rr *rr, **next_p;
4329
+	str rr_name;
4330
+	struct ip_addr *ip_addr;
4331
+	int err, h;
4332
+
4333
+	/* eliminate gcc warnings */
4334
+	rr_name.s = NULL;
4335
+	rr_name.len = 0;
4336
+	ip_addr = 0;
4337
+
4338
+	if (!cfg_get(core, core_cfg, use_dns_cache)){
4339
+		LOG(L_ERR, "ERROR: dns cache support disabled (see use_dns_cache)\n");
4340
+		return -1;
4341
+	}
4342
+	
4343
+	if ((type != T_A) && (type != T_AAAA) && (type != T_SRV)) {
4344
+		LOG(L_ERR, "ERROR: rr type %d is not implemented\n",
4345
+			type);
4346
+		return -1;
4347
+	}
4348
+
4349
+	if (!flags) {
4350
+		/* fix-up the values */
4351
+		switch(type) {
4352
+		case T_A:
4353
+			ip_addr = str2ip(value);
4354
+			if (!ip_addr) {
4355
+				LOG(L_ERR, "ERROR: Malformed ip address: %.*s\n",
4356
+					value->len, value->s);
4357
+				return -1;
4358
+			}
4359
+			break;
4360
+		case T_AAAA:
4361
+#ifdef USE_IPV6
4362
+			ip_addr = str2ip6(value);
4363
+			if (!ip_addr) {
4364
+				LOG(L_ERR, "ERROR: Malformed ip address: %.*s\n",
4365
+					value->len, value->s);
4366
+				return -1;
4367
+			}
4368
+			break;
4369
+#else /* USE_IPV6 */
4370
+			LOG(L_ERR, "ERROR: IPv6 support is disabled\n");
4371
+			return -1;
4372
+#endif /* USE_IPV6 */
4373
+		case T_SRV:
4374
+			rr_name = *value;
4375
+			break;
4376
+		}
4377
+	}
4378
+
4379
+	/* check whether there is a matching entry in the cache */
4380
+	if ((old = dns_hash_get(name, type, &h, &err)) == NULL)
4381
+		goto not_found;
4382
+
4383
+	if ((old->type != type) /* may be CNAME */
4384
+		|| (old->err_flags != flags)
4385
+	)
4386
+		goto not_found;
4387
+
4388
+	if (flags) /* negative record, there is no value */
4389
+		goto delete;
4390
+
4391
+	/* check whether there is an rr with the same value */
4392
+	for (rr=old->rr_lst, next_p=&old->rr_lst;
4393
+		rr;
4394
+		next_p=&rr->next, rr=rr->next
4395
+	)
4396
+		if ((((type == T_A) || (type == T_AAAA)) &&
4397
+			(memcmp(ip_addr->u.addr, ((struct a_rdata*)rr->rdata)->ip,
4398
+								ip_addr->len)==0))
4399
+		|| ((type == T_SRV) &&
4400
+			(((struct srv_rdata*)rr->rdata)->name_len == rr_name.len) &&
4401
+			(memcmp(rr_name.s, ((struct srv_rdata*)rr->rdata)->name,
4402
+								rr_name.len)==0)))
4403
+		break;
4404
+
4405
+	if (!rr)
4406
+		goto not_found;
4407
+
4408
+	if ((rr == old->rr_lst) && (rr->next == NULL)) {
4409
+		/* There is a single rr value, hence the whole
4410
+		 * hash entry can be deleted */
4411
+		goto delete;
4412
+	} else {
4413
+		/* we must modify the entry, so better to clone it, modify the new 
4414
+		* one, and replace the old with the new entry in the hash table,
4415
+		* because the entry might be in use (even if the dns hash is 
4416
+		* locked). The old entry will be removed from the hash and 
4417
+		* automatically destroyed when its refcnt will be 0*/
4418
+		new = dns_cache_clone_entry(old, 0, 0, 0);
4419
+		if (!new) {
4420
+			LOG(L_ERR, "ERROR: Failed to clone an existing "
4421
+				"DNS cache entry\n");
4422
+			dns_hash_put(old);
4423
+			return -1;
4424
+		}
4425
+		/* let rr and next_p point to the new structure */
4426
+		rr = (struct dns_rr*)translate_pointer((char*)new,
4427
+						(char*)old,
4428
+						(char*)rr);
4429
+		next_p = (struct dns_rr**)translate_pointer((char*)new,
4430
+						(char*)old,
4431
+						(char*)next_p);
4432
+		/* unlink rr from the list. The memory will be freed
4433
+		 * when the whole record is freed */
4434
+		*next_p = rr->next;
4435
+	}
4436
+
4437
+delete:
4438
+	LOCK_DNS_HASH();
4439
+	if (new) {
4440
+		/* delete the old entry only if the new one can be added */
4441
+		if (dns_cache_add_unsafe(new)) {
4442
+			LOG(L_ERR, "ERROR: Failed to add the entry to the cache\n");
4443
+			UNLOCK_DNS_HASH();
4444
+			if (old)
4445
+				dns_hash_put(old);
4446
+			return -1;
4447
+		} else {
4448
+			/* remove the old entry from the list */
4449
+			if (old)
4450
+				_dns_hash_remove(old);
4451
+		}
4452
+	} else if (old) {
4453
+		_dns_hash_remove(old);
4454
+	}
4455
+	UNLOCK_DNS_HASH();
4456
+
4457
+	if (old)
4458
+		dns_hash_put(old);
4459
+	return 0;
4460
+
4461
+not_found:
4462
+	LOG(L_ERR, "ERROR: No matching record found\n");
4463
+	if (old)
4464
+		dns_hash_put(old);
4465
+	return -1;
4466
+}
4316 4467
 
4317 4468
 /* performs  a dns lookup over rpc */
4318 4469
 void dns_cache_rpc_lookup(rpc_t* rpc, void* ctx)
... ...
@@ -350,7 +350,7 @@ void dns_set_server_state(int state);
350 350
 int dns_get_server_state(void);
351 351
 #endif /* DNS_WATCHDOG_SUPPORT */
352 352
 
353
-/* Adds a new record to the cache.
353
+/** @brief Adds a new record to the cache.
354 354
  * If there is an existing record with the same name and value
355 355
  * (ip address in case of A/AAAA record, name in case of SRV record)
356 356
  * only the remaining fields are updated.
... ...
@@ -366,5 +366,16 @@ int dns_cache_add_record(unsigned short type,
366 366
 			int port,
367 367
 			int flags);
368 368
 
369
+/** @brief Delete a single record from the cache,
370
+ * i.e. the record with the same name and value
371
+ * (ip address in case of A/AAAA record, name in case of SRV record).
372
+ *
373
+ * Currently only A, AAAA, and SRV records are supported.
374
+ */
375
+int dns_cache_delete_single_record(unsigned short type,
376
+			str *name,
377
+			str *value,
378
+			int flags);
379
+
369 380
 
370 381
 #endif