PERFORCE change 127139 for review

Fredrik Lindberg fli at FreeBSD.org
Wed Oct 3 13:55:58 PDT 2007


http://perforce.freebsd.org/chv.cgi?CH=127139

Change 127139 by fli at fli_nexus on 2007/10/03 20:55:24

	- Add proper support for other classes than IN.
	- Make the "API" more flexible and easier to use.
	- Add better search and traverse functions.

Affected files ...

.. //depot/projects/soc2007/fli-mdns_sd/mdnsd/record.c#6 edit
.. //depot/projects/soc2007/fli-mdns_sd/mdnsd/record.h#4 edit

Differences ...

==== //depot/projects/soc2007/fli-mdns_sd/mdnsd/record.c#6 (text+ko) ====

@@ -37,97 +37,166 @@
 	dprintf(DEBUG_REC, "Refcount on r=%x increased to %d",\
 	    r, (r)->r_refcnt);
 
+#define record_class_aquire(rc) (rc)->rc_refcnt++; \
+	dprintf(DEBUG_REC, "Refcount on rc=%x increased to %d",\
+	    rc, (rc)->rc_refcnt);
+
 #define record_type_aquire(rt) (rt)->rt_refcnt++; \
 	dprintf(DEBUG_REC, "Refcount on rt=%x increased to %d",\
 	    rt, (rt)->rt_refcnt);
 
+static inline int notify_parent(struct recpar *);
+
 /*
  * Initialize a record set for `class'
  */
-void
-records_init(struct records *recs, int class)
+int
+records_init(struct records *recs)
 {
+	int error;
 
-	recs->r_class = class;
-	hashtbl_init(&recs->r_recs, 2, 131072, 5);
+	error = hashtbl_init(&recs->r_recs, 2, 131072, 5);
+	if (error != 0)
+		return (-1);
+	TAILQ_INIT(&recs->r_rec_list);
 	MDNS_INIT_SET(recs, r_magic);
+	return (0);
 }
 
-static void
-rec_free(__unused struct hashtbl *ht, __unused const void *key,
-    __unused size_t keylen, void *data, __unused void *arg)
+void
+records_destroy(struct records *recs)
 {
-	struct record *r = (struct record *)data;
+	struct record *r, *r2;
+	struct record_class *rc, *rc2;
 	struct record_type *rt, *rt2;
 	struct record_res *rr, *rr2;
+	int refcnt;
+
+	MDNS_INIT_ASSERT(recs, r_magic);
+
+	TAILQ_FOREACH_SAFE(r, &recs->r_rec_list, r_next, r2) {
+		r->r_flags |= REC_FORCE;
+		TAILQ_FOREACH_SAFE(rc, &r->r_list, rc_next, rc2) {
+			rc->rc_flags |= REC_FORCE;
+			TAILQ_FOREACH_SAFE(rt, &rc->rc_list, rt_next, rt2) {
+				rt->rt_flags |= REC_FORCE;
+				TAILQ_FOREACH_SAFE(rr, &rt->rt_list, rr_next,
+				    rr2) {
+					rr->rr_flags |= REC_FORCE;
+					record_res_del(rr);	
+				}
+			}
+		}
+	}
 
-	MDNS_INIT_ASSERT(r, r_magic);
+	TAILQ_FOREACH_SAFE(r, &recs->r_rec_list, r_next, r2) {
+		TAILQ_FOREACH_SAFE(rc, &r->r_list, rc_next, rc2) {
+			TAILQ_FOREACH_SAFE(rt, &rc->rc_list, rt_next, rt2) {
+				refcnt = rt->rt_refcnt;
+				while (refcnt-- > 0)
+					record_type_release(rt);
+			}
+		}
+	}
 
-	/*
-	 * Release record resources, note that we do not need an explicit call
-	 * to record_type_release, it will be called by record_res_del.
-	 */
-	TAILQ_FOREACH_SAFE(rt, &r->r_list, rt_next, rt2) {
-		TAILQ_FOREACH_SAFE(rr, &rt->rt_list, rr_next, rr2) {
-			record_res_del(rr);
+	TAILQ_FOREACH_SAFE(r, &recs->r_rec_list, r_next, r2) {
+		TAILQ_FOREACH_SAFE(rc, &r->r_list, rc_next, rc2) {
+			refcnt = rc->rc_refcnt;
+			while (refcnt-- > 0) {
+				record_class_release(rc);
+			}
 		}
 	}
+
+	TAILQ_FOREACH_SAFE(r, &recs->r_rec_list, r_next, r2) {
+		refcnt = r->r_refcnt;
+		while (refcnt-- > 0)
+			record_release(r);
+	}
+
+	hashtbl_destroy(&recs->r_recs);
+	MDNS_INIT_UNSET(recs, r_magic);
 }
 
-/*
- * Destroy a record set and free all resources
- */
-void
-records_destroy(struct records *recs)
+static inline int
+notify_parent(struct recpar *rp)
 {
+	int error;
 
-	hashtbl_walk(&recs->r_recs, rec_free, NULL);
-	hashtbl_destroy(&recs->r_recs);
-	MDNS_INIT_UNSET(recs, r_magic);
+	if (rp->rp_del_cb != NULL && rp->rp_handle != NULL) {
+		error = rp->rp_del_cb(rp->rp_handle);
+		if (error != 0) {
+			dprintf(DEBUG_REC, "Parent handle failed to remove "
+			    "itself, handle=%p, cb=%p", rp->rp_handle,
+			    rp->rp_del_cb);
+			return (-1);
+		}
+	}
+	return (0);
 }
 
 /*
  * Get handle to record `name', initialize if this is the first reference
- *  recs - Record set
- *  r    - Pointer to record
- *  name - Resource name
- *  flags - RECORD_NOALLOC will supress memory allocation
+ *  recs   - Record set
+ *  r      - Pointer to record
+ *  parent - Pointer to parent record or NULL
+ *  flags  - RECORD_* flags
+ *  name   - Record resource name (utf-8)
  */
 int
-record_get(struct records *recs, struct record **r, int flags, char *name)
+record_get(struct records *recs, struct record **r, struct recpar *parent,
+    int flags, const char *name)
 {
-	ssize_t len;
+	int error;
+	ssize_t len, namlen;
 	struct record *rec;
-	char namlc[MDNS_RECORD_LEN+1];
+	char namlc[MDNS_RECORD_LEN];
 
 	MDNS_INIT_ASSERT(recs, r_magic);
 
 	len = utf8_tolower(name, namlc, MDNS_RECORD_LEN);
-	if (len <= 0)
-		return (-1);
+	if (len <= 0 || len >= MDNS_RECORD_LEN) {
+		goto record_get_fail;
+	}
 	
 	rec = hashtbl_find(&recs->r_recs, namlc, len);
-	if (rec == NULL && (flags & RECORD_NOINIT)) {
+	if (rec == NULL && (flags & REC_NOINIT)) {
 		*r = NULL;
 		return (0);
 	}
 
-	if (len > MDNS_RECORD_LEN) {
-		*r = NULL;
-		return (-1);
-	}
-
 	if (rec == NULL) {
-		if (!(flags & RECORD_NOALLOC))
+		namlen = strlen(name);
+		if (namlen <= 0 || namlen >= MDNS_RECORD_LEN)
+			goto record_get_fail;
+		if (!(flags & REC_NOALLOC))
 			*r = malloc(sizeof(struct record));
+		if (*r == NULL)
+			goto record_get_fail;
+
 		MDNS_INIT_SET((*r), r_magic);
 		(*r)->r_recs = recs;
-		(*r)->r_parent = NULL;
+
+		if (parent != NULL)
+			memcpy(&((*r)->r_parent), parent,
+			    sizeof(struct recpar));
+		else {
+			(*r)->r_parent.rp_handle = NULL;
+			(*r)->r_parent.rp_del_cb = NULL;
+		}
+
 		(*r)->r_refcnt = 0;
 		(*r)->r_flags = flags;
 		TAILQ_INIT(&((*r)->r_list));
-		memcpy((*r)->r_name, name, len+1);
-		hashtbl_add(&recs->r_recs, namlc, len, *r, HASHTBL_KEYDUP);
+		memcpy((*r)->r_name, name, namlen+1);
+		error = hashtbl_add(&recs->r_recs, namlc, len, *r,
+		    HASHTBL_KEYDUP);
+		if (error < 0) {
+			if (!(flags & REC_NOALLOC))
+				free(*r);
+			goto record_get_fail;
+		}
+		TAILQ_INSERT_TAIL(&recs->r_rec_list, (*r), r_next);
 	}
 	else {
 		*r = rec;
@@ -136,6 +205,9 @@
 	dprintf(DEBUG_REC, "Record aquired r=%x, name=%s, refcnt=%d",
 	    *r, (*r)->r_name, (*r)->r_refcnt);
 	return (0);
+record_get_fail:
+	dprintf(DEBUG_REC, "Failed to aquire record name=%s", name);
+	return (-1);
 }
 
 /*
@@ -143,12 +215,13 @@
  * was the last reference.
  *  r    - Record to release
  */
-void
+int
 record_release(struct record *r)
 {
+	int error, flags;
 	ssize_t len;
 	struct records *recs;
-	char namlc[MDNS_RECORD_LEN+1];
+	char namlc[MDNS_RECORD_LEN];
 
 	MDNS_INIT_ASSERT(r, r_magic);
 	recs = r->r_recs;
@@ -156,112 +229,291 @@
 
 	if (--r->r_refcnt == 0) {
 		assert(TAILQ_EMPTY(&r->r_list) == 1);
+		flags = r->r_flags;
+
 		len = utf8_tolower(r->r_name, namlc, MDNS_RECORD_LEN);
-		hashtbl_del(&recs->r_recs, namlc, len);
+		if (len <= 0 || len >= MDNS_RECORD_LEN)
+			goto record_release_fail;
+
+		error = hashtbl_del(&recs->r_recs, namlc, len);
+		if (error < 0)
+			goto record_release_fail;
+		TAILQ_REMOVE(&recs->r_rec_list, r, r_next);
 		MDNS_INIT_UNSET(r, r_magic);
-		if (!(r->r_flags & RECORD_NOALLOC))
+
+		error = notify_parent(&r->r_parent);
+		if (error != 0 && !(flags & REC_FORCE)) {
+			MDNS_INIT_SET(r, r_magic);
+			TAILQ_INSERT_TAIL(&recs->r_rec_list, r, r_next);
+			hashtbl_add(&recs->r_recs, namlc, len, r,
+			    HASHTBL_KEYDUP);
+			goto record_release_fail;
+		}
+
+		if (!(flags & REC_NOALLOC))
 			free(r);
-		dprintf(DEBUG_REC, "References cleared on r=%x, removed", r);
+		dprintf(DEBUG_REC, "References cleared on r=%p, removed", r);
 	}
 	else {
-		dprintf(DEBUG_REC, "Record released r=%x, refs=%d",
+		dprintf(DEBUG_REC, "Record released r=%p, refs=%d",
 		    r, r->r_refcnt);
 	}
+	return (0);
+record_release_fail:
+	r->r_refcnt++;
+	dprintf(DEBUG_REC, "Failed to release record %p", r);
+	return (-1);
 }
 
 /*
  * Modify the name of a record in-place without releasing
  * types and resources.
  */
-void
-record_setname(struct record *r, char *name)
+int
+record_setname(struct record *r, const char *name)
 {
 	struct records *recs;
-	size_t len;
+	size_t len, namlen, rlen;
+	int error;
+	char namlc[MDNS_RECORD_LEN], namlc2[MDNS_RECORD_LEN];
 
 	MDNS_INIT_ASSERT(r, r_magic);
 	recs = r->r_recs;
 	MDNS_INIT_ASSERT(recs, r_magic);
 
-	len = strlen(r->r_name);
-	hashtbl_del(&recs->r_recs, r->r_name, len);
-	len = strlen(name);
-	memcpy(r->r_name, name, len+1);
-	hashtbl_add(&recs->r_recs, r->r_name, len, r, 0);
-	dprintf(DEBUG_REC, "Record name set to %s on r=%x", r->r_name, r);
+	namlen = strlen(name);
+	if (namlen <= 0 || namlen >= MDNS_RECORD_LEN)
+		goto record_setname_fail;
+
+	len = utf8_tolower(name, namlc, MDNS_RECORD_LEN);
+	if (len <= 0 || len >= MDNS_RECORD_LEN)
+		goto record_setname_fail;
+
+	rlen = utf8_tolower(r->r_name, namlc2, MDNS_RECORD_LEN);
+	if (rlen <= 0 || rlen >= MDNS_RECORD_LEN)
+		goto record_setname_fail;
+
+	error = hashtbl_add(&recs->r_recs, namlc, len, r, HASHTBL_KEYDUP);
+	if (error < 0)
+		goto record_setname_fail;
+
+	error = hashtbl_del(&recs->r_recs, namlc2, rlen);
+	if (error < 0) {
+		hashtbl_del(&recs->r_recs, namlc, len);
+		goto record_setname_fail;
+	}
+
+	memcpy(r->r_name, name, namlen + 1);
+
+	dprintf(DEBUG_REC, "Record name set to %s on r=%p", r->r_name, r);
+	return (0);
+record_setname_fail:
+	dprintf(DEBUG_REC, "Failed to set name on r=%p to %s", r, name);
+	return (-1);
+}
+
+int
+record_class_get(struct records *recs, struct record_class **rc, int flags,
+    struct recpar *rp, void *record, uint16_t class)
+{
+	int error, nflags;
+	struct record *r;
+	struct record_class *rc2;
+
+	MDNS_INIT_ASSERT(recs, r_magic);
+
+	if (flags & REC_OBJNAME) {
+		r = record;
+		MDNS_INIT_ASSERT(r, r_magic);
+		record_aquire(r);
+	}
+	else {
+		nflags = flags & ~(REC_NOALLOC | REC_NOINIT);
+		error = record_get(recs, &r, NULL, nflags, record);
+		if (error < 0)
+			goto record_class_get_fail;
+	}
+
+	TAILQ_FOREACH(rc2, &r->r_list, rc_next)
+		if (class == rc2->rc_class)
+			break;
+	if (rc2 == NULL && (flags & REC_NOINIT)) {
+		record_release(r);
+		*rc = NULL;
+		return (0);
+	}
+
+	if (rc2 == NULL) {
+		if (!(flags & REC_NOALLOC))
+			*rc = malloc(sizeof(struct record_class));
+		if (*rc == NULL)
+			goto record_class_get_fail;
+		MDNS_INIT_SET((*rc), rc_magic);
+		(*rc)->rc_class = class;
+		(*rc)->rc_record = r;
+		(*rc)->rc_refcnt = 0;
+		(*rc)->rc_flags = flags;
+
+		if (rp != NULL)
+			memcpy(&(*rc)->rc_parent, rp, sizeof(struct recpar));
+		else {
+			(*rc)->rc_parent.rp_handle = NULL;
+			(*rc)->rc_parent.rp_del_cb = NULL;
+		}
+
+		TAILQ_INIT(&((*rc)->rc_list));
+		TAILQ_FOREACH(rc2, &r->r_list, rc_next)
+			if (rc2->rc_class > class)
+				break;
+		if (rc2 != NULL)
+			TAILQ_INSERT_BEFORE(rc2, *rc, rc_next);
+		else
+			TAILQ_INSERT_TAIL(&r->r_list, *rc, rc_next);
+	}
+	else {
+		*rc = rc2;
+	}
+
+	record_class_aquire(*rc);
+	dprintf(DEBUG_REC, "Record class aquired rc=%x, refcnt=%d", *rc,
+	    (*rc)->rc_refcnt);
+	return (0);
+record_class_get_fail:
+	dprintf(DEBUG_REC, "Failed to aquire class %d", class);
+	return (-1);
 }
 
 /*
- * Lookup if a record exists
+ * Release a record class
  */
-struct record *
-record_find(struct records *recs, char *name)
+int
+record_class_release(struct record_class *rc)
 {
 	struct record *r;
-	ssize_t len;
-	char namlc[MDNS_RECORD_LEN+1];
+	struct record_class *rc2;
+	int error, flags;
+
+	MDNS_INIT_ASSERT(rc, rc_magic);
+	r = rc->rc_record;
+	MDNS_INIT_ASSERT(r, r_magic);
+
+	if (--rc->rc_refcnt == 0) {
+		assert(TAILQ_EMPTY(&rc->rc_list) == 1);
+		flags = rc->rc_flags;
 
-	MDNS_INIT_ASSERT(recs, r_magic);
+		TAILQ_REMOVE(&r->r_list, rc, rc_next);
+		MDNS_INIT_UNSET(rc, rc_magic);
 
-	len = utf8_tolower(name, namlc, MDNS_RECORD_LEN);
-	if (len <= 0)
-		return (NULL);
+		error = notify_parent(&rc->rc_parent);
+		if (error != 0 && !(flags & REC_FORCE)) {
+			MDNS_INIT_SET(rc, rc_magic);
+			TAILQ_FOREACH(rc2, &r->r_list, rc_next)
+			if (rc->rc_class > rc2->rc_class)
+				break;
+			if (rc2 != NULL)
+				TAILQ_INSERT_BEFORE(rc2, rc, rc_next);
+			else
+				TAILQ_INSERT_TAIL(&r->r_list, rc, rc_next);
+			goto record_class_release_fail;
+		}
 
-	r = hashtbl_find(&recs->r_recs, namlc, len);
-	return (r);
+		if (!(flags & REC_NOALLOC))
+			free(rc);
+		dprintf(DEBUG_REC, "References cleared on rc=%x, removed", rc);
+	}
+	else {
+		dprintf(DEBUG_REC, "Record class released rc=%x, refcnt=%d",
+		    rc, rc->rc_refcnt);
+	}
+	record_release(r);
+	return (0);
+record_class_release_fail:
+	rc->rc_refcnt++;
+	dprintf(DEBUG_REC, "Failed to release record class rc=%p", rc);
+	return (-1);
 }
 
+
 /*
- * Get a record type for a record, will be intialized if needed
- *  r  - Record
- *  rt - Pointer to the new record type
- *  flags - RECORD_NOALLOC will supress memory allocation
- *  type - DNS type
+ * Aquire reference to a record type
  */
 int
-record_type_get(struct record *r, struct record_type **rt, int flags,
-    uint16_t type)
+record_type_get(struct records *recs, struct record_type **rt, int flags,
+    struct recpar *rp, void *record, uintptr_t class, uint16_t type)
 {
+	int error, nflags;
 	struct record_type *rt2;
+	struct record_class *rc = NULL;
+
+	MDNS_INIT_ASSERT(recs, r_magic);
 
-	MDNS_INIT_ASSERT(r, r_magic);
+	if (flags & REC_OBJCLASS) {
+		rc = (struct record_class *)class;
+		MDNS_INIT_ASSERT(rc, rc_magic);
+		record_aquire(rc->rc_record);
+		record_class_aquire(rc);
+	}
+	else {
+		nflags = flags & ~(REC_NOALLOC | REC_NOINIT);
+		error = record_class_get(recs, &rc, nflags, NULL,
+		    record, class);
+		if (error < 0)
+			goto record_type_get_fail2;
+	}
+
+	assert(rc != NULL);
 
-	TAILQ_FOREACH(rt2, &r->r_list, rt_next) {
+	TAILQ_FOREACH(rt2, &rc->rc_list, rt_next) {
 		if (type == rt2->rt_type)
 			break;
 	}
-	if (rt2 == NULL && (flags & RECORD_NOINIT)) {
+
+	if ((rt2 == NULL) && (flags & REC_NOINIT)) {
 		*rt = NULL;
+		record_class_release(rc);
 		return (0);
 	}
+	else if (rt2 != NULL) {
+		*rt = rt2;
+		goto record_type_get_out;
+	}
+
+	assert(rt2 == NULL);
+	if (!(flags & REC_NOALLOC))
+		*rt = malloc(sizeof(struct record_type));
+	if (*rt == NULL)
+		goto record_type_get_fail;
 
-	if (rt2 == NULL) {
-		if (!(flags & RECORD_NOALLOC))
-			*rt = malloc(sizeof(struct record_type));
-		MDNS_INIT_SET((*rt), rt_magic);
-		(*rt)->rt_parent = NULL;
-		(*rt)->rt_type = type;
-		(*rt)->rt_flags = flags;
-		(*rt)->rt_record = r;
-		(*rt)->rt_refcnt = 0;
-		record_aquire(r);
-		TAILQ_INIT(&((*rt)->rt_list));
-		TAILQ_FOREACH(rt2, &r->r_list, rt_next) {
-			if (type > rt2->rt_type)
-				break;
-		}
-		if (rt2 != NULL)
-			TAILQ_INSERT_BEFORE(rt2, *rt, rt_next);
-		else
-			TAILQ_INSERT_TAIL(&r->r_list, *rt, rt_next);
-	}
+	MDNS_INIT_SET((*rt), rt_magic);
+	if (rp != NULL)
+		memcpy(&((*rt)->rt_parent), rp, sizeof(struct recpar));
 	else {
-		*rt = rt2;
+		(*rt)->rt_parent.rp_handle = NULL;
+		(*rt)->rt_parent.rp_del_cb = NULL;
 	}
+	(*rt)->rt_type = type;
+	(*rt)->rt_flags = flags;
+	(*rt)->rt_class = rc;
+	(*rt)->rt_refcnt = 0;
+	TAILQ_INIT(&((*rt)->rt_list));
+	TAILQ_FOREACH(rt2, &rc->rc_list, rt_next)
+		if (rt2->rt_type > type)
+			break;
+	if (rt2 != NULL)
+		TAILQ_INSERT_BEFORE(rt2, *rt, rt_next);
+	else
+		TAILQ_INSERT_TAIL(&rc->rc_list, *rt, rt_next);
+
+record_type_get_out:
 	record_type_aquire(*rt);
 	dprintf(DEBUG_REC, "Record type aquired rt=%x, refcnt=%d", *rt,
 	    (*rt)->rt_refcnt);
 	return (0);
+record_type_get_fail:
+	record_class_release(rc);
+record_type_get_fail2:
+	dprintf(DEBUG_REC, "Failed to aquire record type %d", type);
+	return (-1);
 }
 
 /*
@@ -269,79 +521,154 @@
  * was the last reference.
  *  rt - Record type to release
  */
-void
+int
 record_type_release(struct record_type *rt)
 {
-	struct record *r;
+	struct record_class *rc;
+	struct record_type *rt2;
+	int error, flags;
 
 	MDNS_INIT_ASSERT(rt, rt_magic);
+	rc = rt->rt_class;
+	MDNS_INIT_ASSERT(rc, rc_magic);
+
 	if (--rt->rt_refcnt == 0) {
-		r = rt->rt_record;
-		TAILQ_REMOVE(&r->r_list, rt, rt_next);
+		assert(TAILQ_EMPTY(&rt->rt_list) == 1);
+		flags = rt->rt_flags;
+
+		TAILQ_REMOVE(&rc->rc_list, rt, rt_next);
 		MDNS_INIT_UNSET(rt, rt_magic);
-		if (!(rt->rt_flags & RECORD_NOALLOC))
+
+		error = notify_parent(&rt->rt_parent);
+		if (error != 0 && !(flags & REC_FORCE)) {
+			MDNS_INIT_SET(rt, rt_magic);
+			TAILQ_FOREACH(rt2, &rc->rc_list, rt_next)
+				if (rt->rt_type > rt2->rt_type)
+					break;
+			if (rt2 != NULL)
+				TAILQ_INSERT_BEFORE(rt2, rt, rt_next);
+			else
+				TAILQ_INSERT_TAIL(&rc->rc_list, rt, rt_next);
+			goto record_type_release_fail;
+		}
+		if (!(flags & REC_NOALLOC))
 			free(rt);
+
 		dprintf(DEBUG_REC, "References cleared on rt=%x, removed", rt);
-		record_release(r);
 	}
 	else {
 		dprintf(DEBUG_REC, "Record type released rt=%x, refcnt=%d",
 		    rt, rt->rt_refcnt);
 	}
+	record_class_release(rc);
+	return (0);
+record_type_release_fail:
+	rt->rt_refcnt++;
+	dprintf(DEBUG_REC, "Failed to release record type rt=%p", rt);
+	return (-1);
 }
 
 /*
  * Add a resource record to a resource type
- *  r - Parent record
- *  rr - Initialized to the new resource record
- *  flags - RECORD_ALLOC will cause it to allocated memory if needed
- *  type - DNS type
- *  data - Pointer to resource data
- *  dlen - Length of resource data
  */
 int
-record_res_add(struct record *r, struct record_res **rr, int flags,
-	uint16_t type, void *data, size_t dlen)
+record_res_add(struct records *recs, struct record_res **rr, int flags,
+    struct recpar *rp, void *record, uintptr_t class, uintptr_t type,
+    void *data, size_t dlen)
 {
 	struct record_type *rt;
+	int error, nflags;
+
+	MDNS_INIT_ASSERT(recs, r_magic);
 
-	MDNS_INIT_ASSERT(r, r_magic);
+	if (flags & REC_OBJTYPE) {
+		rt = (struct record_type *)type;
+		MDNS_INIT_ASSERT(rt, rt_magic);
+		record_aquire(rt->rt_class->rc_record);
+		record_class_aquire(rt->rt_class);
+		record_type_aquire(rt);
+	}
+	else {
+		nflags = flags & ~(REC_NOALLOC | REC_NOINIT);
+		error = record_type_get(recs, &rt, nflags, NULL, record,
+		    class, type);
+		if (error != 0)
+			goto record_res_add_fail;
+	}
+
+	assert(rt != NULL);
 
-	record_type_get(r, &rt, 0, type);
-	if (!(flags & RECORD_NOALLOC))
+	if (!(flags & REC_NOALLOC))
 		*rr = malloc(sizeof(struct record_res));
+	if (*rr == NULL)
+		goto record_res_add_fail;
 	MDNS_INIT_SET((*rr), rr_magic);
-	(*rr)->rr_parent = NULL;
 	(*rr)->rr_flags = flags;
-	(*rr)->rr_data = data; /* FIXME: assumption, new flag */
+
+	if (rp != NULL)
+		memcpy(&((*rr)->rr_parent), rp, sizeof(struct recpar));
+	else {
+		(*rr)->rr_parent.rp_handle = NULL;
+		(*rr)->rr_parent.rp_del_cb = NULL;
+	}
+
+	if (flags & REC_RESDUP) {
+		(*rr)->rr_data = malloc(dlen);
+		if ((*rr)->rr_data == NULL)
+			goto record_res_add_fail;
+		memcpy((*rr)->rr_data, data, dlen);
+	}
+	else
+		(*rr)->rr_data = data;
 	(*rr)->rr_len = dlen;
 	(*rr)->rr_type = rt;
 	TAILQ_INSERT_TAIL(&rt->rt_list, *rr, rr_next);
-	dprintf(DEBUG_REC, "Resource rr=%x added on r=%x, rt=%x", *rr, r, rt);
+
+	dprintf(DEBUG_REC, "Resource rr=%p added rt=%p", *rr, rt);
 	return (0);
+record_res_add_fail:
+	if ((!(flags & REC_NOALLOC)) && *rr != NULL)
+		free(*rr);
+	record_type_release(rt);
+	dprintf(DEBUG_REC, "Failed to add resource data=%p, rt=%p", data, rt);
+	return (-1);
 }
 
 /*
  * Delete a resource record
  *  rr - Pointer to resource record
  */
-void
+int
 record_res_del(struct record_res *rr)
 {
-	int flags;
+	int flags, error;
 	struct record_type *rt;
+	void *dptr;
 
 	MDNS_INIT_ASSERT(rr, rr_magic);
 	rt = rr->rr_type;
 	MDNS_INIT_ASSERT(rt, rt_magic);
 	flags = rr->rr_flags;
+	dptr = rr->rr_data;
+
 	TAILQ_REMOVE(&rt->rt_list, rr, rr_next);
-	free(rr->rr_data); /* FIXME assumption */
-	record_type_release(rt);
 	MDNS_INIT_UNSET(rr, rr_magic);
-	if (!(flags & RECORD_NOALLOC))
+	error = notify_parent(&rr->rr_parent);
+	if (error != 0 && !(flags & REC_FORCE)) {
+		MDNS_INIT_SET(rr, rr_magic);
+		TAILQ_INSERT_TAIL(&rt->rt_list, rr, rr_next);
+		goto record_res_del_fail;
+	}
+	if (!(flags & REC_NOALLOC))
 		free(rr);
+	if (flags & (REC_RESOWN | REC_RESDUP))
+		free(dptr);
+	record_type_release(rt);
 	dprintf(DEBUG_REC, "Resource rr=%x removed, rt=%x", rr, rt);
+	return (0);
+record_res_del_fail:
+	dprintf(DEBUG_REC, "Failed to remove resource rr=%p", rr);
+	return (-1);
 }
 
 /*
@@ -350,34 +677,54 @@
  *   data - Data pointer
  *   dlen - Length of data
  */
-void
-record_res_setdata(struct record_res *rr, void *data, size_t dlen)
+int
+record_res_setdata(struct record_res *rr, int flags, void *data, size_t dlen)
 {
+	void *dptr, *dup;
 
 	MDNS_INIT_ASSERT(rr, rr_magic);
-	free(rr->rr_data); /* FIXME: assumption */
-	rr->rr_data = data;
+	dptr = rr->rr_data;
+
+	if (flags & REC_RESDUP) {
+		dup = malloc(dlen);
+		if (dup == NULL)
+			goto record_res_setdata_fail;
+		memcpy(dup, data, dlen);
+		rr->rr_data = dup;
+		rr->rr_flags |= REC_RESDUP;
+	}
+	else {
+		rr->rr_data = data;
+		rr->rr_flags &= ~REC_RESDUP;
+	}
+
+	if (rr->rr_flags & (REC_RESOWN | REC_RESDUP))
+		free(dptr);
+
+	if (flags & REC_RESOWN)
+		rr->rr_flags |= REC_RESOWN;
+	else
+		rr->rr_flags &= ~(REC_RESOWN);
+
 	rr->rr_len = dlen;
 	dprintf(DEBUG_REC, "Resource data set on rr=%x, data=%x, dlen=%d",
 	    rr, data, dlen);
+	return (0);
+record_res_setdata_fail:
+	dprintf(DEBUG_REC, "Failed to set resource data on rr=%p, data=%p, "
+	    "dlen=%d", rr, data, dlen);
+	return (-1);
 }
 
-
 /*
- * Returns a pointer to the first resource record identified by (name, type)
- *  recs - Record set
- *  name - Resource name
- *  type - Resource type
+ * Lookup if a record exists
  */
-struct record_res *
-record_res_find(struct records *recs, char *name, uint16_t type, char *data,
-    size_t dlen)
+struct record *
+record_find(struct records *recs, const char *name)
 {
 	struct record *r;
-	struct record_type *rt;
-	struct record_res *rr;
 	ssize_t len;
-	char namlc[MDNS_RECORD_LEN+1];
+	char namlc[MDNS_RECORD_LEN];
 
 	MDNS_INIT_ASSERT(recs, r_magic);
 
@@ -386,110 +733,154 @@
 		return (NULL);
 
 	r = hashtbl_find(&recs->r_recs, namlc, len);
-	if (r == NULL)
-		return (NULL);
+	return (r);
+}
+
+struct record_class *
+record_class_find(struct records *recs, int flags, const void *record,
+    uint16_t class)
+{
+	const struct record *r;
+	struct record_class *rc;
+
+	MDNS_INIT_ASSERT(recs, r_magic);
+	if (flags & REC_OBJNAME) {
+		r = (const struct record *)record;
+		MDNS_INIT_ASSERT(r, r_magic);
+	}
+	else {
+		r = record_find(recs, record);
+		if (r == NULL)
+			return (NULL);
+	}
+
+	if ((flags & REC_CTANY) && rc->rc_class == mdns_c_any) {
+		rc = record_class_first(r);
+		if (rc != NULL)
+			rc->rc_flags |= REC_CTANY;
+		return (rc);
+	}
 
-	TAILQ_FOREACH(rt, &r->r_list, rt_next) {
-		if (rt->rt_type == type)
-			break;
+	rc = NULL;
+	TAILQ_FOREACH(rc, &r->r_list, rc_next) {
+		if (rc->rc_class < class)
+			continue;
+		if (rc->rc_class > class)
+			rc = NULL;
+		break;
 	}
-	if (rt == NULL)
+	return (rc);
+}
+
+/*
+ * Obtain next logical record class, only applicable when the record class
+ * is mdns_c_any and the flag REC_CTANY is set.
+ */
+struct record_class *
+record_class_find_next(struct record_class *rc, uint16_t class)
+{
+	struct record_class *rc2;
+
+	if (!(rc->rc_flags & REC_CTANY))
+		return (NULL);
+	else if (class != mdns_c_any)
 		return (NULL);
 
-	TAILQ_FOREACH(rr, &rt->rt_list, rr_next) {
-		if (rr->rr_len == dlen)
-			if (memcmp(rr->rr_data, data, dlen) == 0)	
-				break;
-	}
-	return (rr);
+	rc->rc_flags &= ~REC_CTANY;
+	rc2 = record_class_next(rc);
+	if (rc2 != NULL)
+		rc2->rc_flags |= REC_CTANY;
+	return (rc2);
 }
 
 struct record_type *
-record_type_find(struct records *recs, char *name, uint16_t type)
+record_type_find(struct records *recs, int flags, const void *record,
+    uintptr_t class, uint16_t type)
 {
-	struct record *r;
+	struct record_class *rc;
 	struct record_type *rt;
-	ssize_t len;
-	char namlc[MDNS_RECORD_LEN+1];
 
 	MDNS_INIT_ASSERT(recs, r_magic);
 
-	len = utf8_tolower(name, namlc, MDNS_RECORD_LEN);
-	if (len <= 0)
-		return (NULL);
+	if (flags & REC_OBJCLASS) {
+		rc = (struct record_class *)class;
+		MDNS_INIT_ASSERT(rc, rc_magic);
+	}
+	else {
+		rc = record_class_find(recs, flags, record, class);
+		if (rc == NULL)
+			return (NULL);
+	}
 
-	r = hashtbl_find(&recs->r_recs, namlc, len);
-	if (r == NULL)
-		return (NULL);
+	if ((flags & REC_CTANY) && rt->rt_type == mdns_t_any) {
+		rt = record_type_first(rc);
+		if (rt != NULL)
+			rt->rt_flags |= REC_CTANY;
+		return (rt);
+	}
 
-	TAILQ_FOREACH(rt, &r->r_list, rt_next) {
-		if (rt->rt_type == type)
-			break;
+	rt = NULL;
+	TAILQ_FOREACH(rt, &rc->rc_list, rt_next) {
+		if (rt->rt_type < type)
+			continue;
+		if (rt->rt_type > type)
+			rt = NULL;
+		break;
 	}
 	return (rt);
 }
 
-static void
-rec_walk(__unused struct hashtbl *ht, __unused const void *key,
-    __unused size_t keylen, void *data, void *arg)
+struct record_type *
+record_type_find_next(struct record_type *rt, uint16_t type)
 {
-	void **args = (void **)arg;
-	record_foreach cb = args[0];
-	void *cbarg = args[1];
-	struct record *r = data;
+	struct record_type *rt2;
+
+	if (!(rt->rt_flags & REC_CTANY))
+		return (NULL);
+	else if (type != mdns_t_any)
+		return (NULL);
 
-	cb(r, cbarg);
+	rt->rt_flags &= ~REC_CTANY;
+	rt2 = record_type_next(rt);
+	if (rt2 != NULL)
+		rt2->rt_flags |= REC_CTANY;
+	return (rt2);
 }
 
-void

>>> TRUNCATED FOR MAIL (1000 lines) <<<


More information about the p4-projects mailing list