From owner-p4-projects@FreeBSD.ORG Mon Jul 16 01:12:58 2007 Return-Path: X-Original-To: p4-projects@freebsd.org Delivered-To: p4-projects@freebsd.org Received: by hub.freebsd.org (Postfix, from userid 32767) id C7DEF16A404; Mon, 16 Jul 2007 01:12:57 +0000 (UTC) X-Original-To: perforce@FreeBSD.org Delivered-To: perforce@FreeBSD.org Received: from mx1.freebsd.org (mx1.freebsd.org [69.147.83.52]) by hub.freebsd.org (Postfix) with ESMTP id 9C94C16A403 for ; Mon, 16 Jul 2007 01:12:57 +0000 (UTC) (envelope-from fli@FreeBSD.org) Received: from repoman.freebsd.org (repoman.freebsd.org [69.147.83.41]) by mx1.freebsd.org (Postfix) with ESMTP id 8B9C713C491 for ; Mon, 16 Jul 2007 01:12:57 +0000 (UTC) (envelope-from fli@FreeBSD.org) Received: from repoman.freebsd.org (localhost [127.0.0.1]) by repoman.freebsd.org (8.14.1/8.14.1) with ESMTP id l6G1Cvne089618 for ; Mon, 16 Jul 2007 01:12:57 GMT (envelope-from fli@FreeBSD.org) Received: (from perforce@localhost) by repoman.freebsd.org (8.14.1/8.14.1/Submit) id l6G1CvWc089615 for perforce@freebsd.org; Mon, 16 Jul 2007 01:12:57 GMT (envelope-from fli@FreeBSD.org) Date: Mon, 16 Jul 2007 01:12:57 GMT Message-Id: <200707160112.l6G1CvWc089615@repoman.freebsd.org> X-Authentication-Warning: repoman.freebsd.org: perforce set sender to fli@FreeBSD.org using -f From: Fredrik Lindberg To: Perforce Change Reviews Cc: Subject: PERFORCE change 123568 for review X-BeenThere: p4-projects@freebsd.org X-Mailman-Version: 2.1.5 Precedence: list List-Id: p4 projects tree changes List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , X-List-Received-Date: Mon, 16 Jul 2007 01:12:58 -0000 http://perforce.freebsd.org/chv.cgi?CH=123568 Change 123568 by fli@fli_nexus on 2007/07/16 01:12:45 - Revisit/fix record and resource construction, they now properly follow the system state. - Introduce a "pointer" concept where the resource data of one record can point to name of another record. This useful for PTR records that should follow a coresponding A/AAAA record automatically. - Add record probing and annoucing. When probing for a new name a "probe context" is created to which records are added, upon collision the record is removed from the context which will self-destruct when no records are left. A probe is initiated when ever a record changes name. The "announce context" works in a similar way. - Style fixes. Affected files ... .. //depot/projects/soc2007/fli-mdns_sd/mdnsd/dbrec.c#2 edit .. //depot/projects/soc2007/fli-mdns_sd/mdnsd/dbrec.h#1 add Differences ... ==== //depot/projects/soc2007/fli-mdns_sd/mdnsd/dbrec.c#2 (text+ko) ==== @@ -26,14 +26,33 @@ #include #include +#include #include "mdnsd.h" +#include "objalloc.h" +#include "output.h" #include "record.h" #include "log.h" #include "var.h" static void record_type_del_cb(struct record_type *, void *); +static void rec_update(struct dbr_rec *, struct vt_data *, size_t); +static void rec_var_update(void *); +static inline void rec_pastop(struct dbr_rec *); +static void rec_del(struct dbr_rec *); +static void res_update(struct dbr_res *, wchar_t **, size_t); +static void res_var_update(void *); +static void res_del(struct dbr_res *); + +static int dbr_pac_add(struct dbr_pac *, struct dbr_rec *, int); +static int dbr_pac_del(struct dbr_rec *, int); +static void dbr_pac_start(struct dbr_pac *, int, int, void *); +static int probe_step(const struct event_tmr *, const ev_arg); +static int an_step(const struct event_tmr *, const ev_arg); +/* + * Initialize a database record system + */ void dbr_init(struct dbr *dbr, void *context) { @@ -41,6 +60,7 @@ dbr->dbr_ctx = context; MDNS_INIT_SET(dbr, dbr_magic); + hashtbl_init(&dbr->dbr_ident, 8, 512, 3); var_init(&dbr->dbr_vars, mif->mif_glob->g_evl, dbr); /* We only do the IN class */ @@ -58,6 +78,9 @@ dbr_del(dr); } +/* + * Destroy a database record system + */ void dbr_destroy(struct dbr *dbr) { @@ -69,179 +92,211 @@ records_foreach(&dbr->dbr_recs, record_del_cb, NULL); records_destroy(&dbr->dbr_recs); var_destroy(&dbr->dbr_vars, mif->mif_glob->g_evl); + hashtbl_destroy(&dbr->dbr_ident); MDNS_INIT_UNSET(dbr, dbr_magic); + dprintf(DEBUG_DBR, "Database records destroyed"); } -static struct dbr_res * -clone_res(struct dbr_res *orig) +/* + * Clone a record + */ +static struct dbr_rec * +clone_rec(struct dbr_rec *orig) +{ + struct dbr_rec *dr; + + dr = malloc(sizeof(struct dbr_rec)); + if (dr == NULL) + return (NULL); + memcpy(dr, orig, sizeof(struct dbr_rec)); + dr->dr_flags |= DR_CLONE; + TAILQ_INSERT_TAIL(&orig->dr_clone.head, dr, dr_clone.next); + dprintf(DEBUG_DBR, "Database record cloned dr=%x, source=%x", dr, orig); + return (dr); +} + +/* + * Clone all resources from `orig' onto `dr' + */ +static void +clone_rec_res(struct dbr_rec *orig, struct dbr_rec *dr) { + struct record *r; + struct record_type *rt; + struct record_res *rr; struct dbr_res *ds; + struct dbr_rec *dr2; + void *res; + int ptr; - ds = malloc(sizeof(struct dbr_res)); - if (ds == NULL) - return (NULL); - memcpy(ds, orig, sizeof(struct dbr_res)); - ds->ds_data = NULL; - ds->ds_flags |= DS_CLONE; - TAILQ_INSERT_TAIL(&orig->ds_clone.head, ds, ds_clone.next); - dprintf(DEBUG_DBR, "Database resource cloned ds=%x, source=%x", ds, orig); - return (ds); + MDNS_INIT_ASSERT(dr, dr_magic); + MDNS_INIT_ASSERT(orig, dr_magic); + r = &orig->dr_rec; + MDNS_INIT_ASSERT(r, r_magic); + + record_foreach(rt, r) { + record_type_foreach(rr, rt) { + ds = record_res_getparent(rr); + ptr = ds->ds_flags & DS_POINTER; + if (ptr) { + dr2 = ds->ds_vdata.rec; + res = dr2->dr_ident; + } + else + res = ds->ds_vdata.wp; + + dbr_res_add(dr, rt->rt_type, ds->ds_ttl, res, ptr); + } + } + dprintf(DEBUG_DBR, "Cloned resources from %x to %x", orig, dr); } /* - * Variable update handler to resource data, will re-expand vaiables - * and update affected resources. - * This is an asynchronous callback routine which is called from the - * variable event system. + * Update a database record + * dr - Database record + * vtd - Arrays of expanded names + * vtd_len - Array length * - * ptr - Opaque pointer passed to var_reg(), to this routine it's - * always of type dbr_res {} + * A record clone will be created for each excessive member of `vtd', if + * there currently are more clones than the length of the provided array, + * then those clones are removed. */ static void -res_var_update(void *ptr) +rec_update(struct dbr_rec *dr, struct vt_data *vtd, size_t vtd_len) { - struct dbr_res *ds, *ds_clone, *ds_tmp; - struct dbr_type *dt; - struct dbr_rec *dr; + struct dbr_rec *dr_clone, *dr_tmp; + struct dbr_res *ds, *ds2; struct dbr *dbr; - struct record_type *rt; - struct record_res *rr; struct record *r; - struct vt_data *vtd; - size_t vtd_len, rlen; uint32_t i; int32_t diff; - char *p; + wchar_t **names; + char *nam; - ds = ptr; - MDNS_INIT_ASSERT(ds, ds_magic); - dt = ds->ds_type; - MDNS_INIT_ASSERT(dt, dt_magic); - dr = dt->dt_rec; MDNS_INIT_ASSERT(dr, dr_magic); dbr = dr->dr_dbr; MDNS_INIT_ASSERT(dbr, dbr_magic); - rt = &dt->dt_type; + assert(dr == dr->dr_chead); - /* TODO: locking */ + /* Stop pending probes/announces */ + rec_pastop(dr); - vtd = var_expand(&dbr->dbr_vars, ds->ds_vdata, &vtd_len); + /* + * If the provided array length is 0 there are no available + * names, invalidate record and remove all clones. + */ + if (vtd_len == 0) { + if (dr->dr_name != NULL) + free(dr->dr_name); + dr->dr_name = NULL; + dr->dr_flags |= DR_INVALID; + dr->dr_flags &= ~DR_OK; - if (vtd_len == 0) { - if (!(ds->ds_flags & DS_INVALID)) { - /* TODO: annonuce old res with ttl 0 */ - free(ds->ds_data); + TAILQ_FOREACH_SAFE(dr_clone, &dr->dr_clone.head, + dr_clone.next, dr_tmp) { + rec_del(dr_clone); } - record_res_setdata(&ds->ds_res, NULL, 0); - ds->ds_flags |= DS_INVALID; - dprintf(DEBUG_DBR, "Removing clones on %x, clones=%d", - ds, ds->ds_clones); - TAILQ_FOREACH(ds_clone, &ds->ds_clone.head, ds_clone.next) { - TAILQ_REMOVE(&ds->ds_clone.head, ds_clone, ds_clone.next); - record_res_del(&ds_clone->ds_res); + + /* + * Invalidate all database resources that are using this + * record name as their data source. + */ + TAILQ_FOREACH(ds, &dr->dr_res_ptr, ds_ptr_next) { + ds->ds_flags |= DS_INVALID; free(ds->ds_data); - free(ds_clone); - ds->ds_clones--; + ds->ds_data = NULL; } - goto out; + return; + } + + i = 1; + nam = mdns_name_encode(vtd[0].vtd_str, vtd[0].vtd_len, MDNS_ENC_AUTO); + r = &dr->dr_rec; + if (dr->dr_name != NULL) + free(dr->dr_name); + record_setname(r, nam); + + free(nam); + dr->dr_name = vtd[0].vtd_str; + dr->dr_flags &= ~(DR_OK | DR_INVALID); + + /* + * Replace names as long as there are enough clones + * and results available. + */ + TAILQ_FOREACH(dr_clone, &dr->dr_clone.head, dr_clone.next) { + if ((i - 1) == dr->dr_clones || i == vtd_len) + break; + r = &dr_clone->dr_rec; + nam = mdns_name_encode(vtd[i].vtd_str, vtd[i].vtd_len, + MDNS_ENC_AUTO); + record_setname(r, nam); + free(nam); + if (dr_clone->dr_name != NULL) + free(dr_clone->dr_name); + dr_clone->dr_name = vtd[i].vtd_str; + dr_clone->dr_flags &= ~DR_OK; + i++; } - else if (vtd_len >= 1) { - if (!(ds->ds_flags & DS_INVALID)) - free(ds->ds_data); - ds->ds_data = vtd[0].vtd_str; - p = mdns_res_encode(mdns_c_in, rt->rt_type, ds->ds_data, - MDNS_ENC_WCHAR, vtd[0].vtd_len, &rlen); - record_res_setdata(&ds->ds_res, p, rlen); - i = 1; - /* - * Replace resource data as long as there is enough clones - * and results available. - */ - TAILQ_FOREACH(ds_clone, &ds->ds_clone.head, ds_clone.next) { - if (i == ds->ds_clones || i == vtd_len) - break; - free(ds_clone->ds_data); - ds->ds_data = vtd[i].vtd_str; - p = mdns_res_encode(mdns_c_in, rt->rt_type, ds->ds_data, - MDNS_ENC_WCHAR, vtd[i].vtd_len, &rlen); - record_res_setdata(&ds->ds_res, p, rlen); + /* More results than clones, expand */ + if (vtd_len > i) { + diff = vtd_len - i; + while (diff-- > 0) { + dr_clone = clone_rec(dr); + dr_clone->dr_clones = 0; + dr->dr_clones++; + r = &dr_clone->dr_rec; + nam = mdns_name_encode(vtd[i].vtd_str, vtd[i].vtd_len, + MDNS_ENC_AUTO); + record_get(&dbr->dbr_recs, &r, RECORD_NOALLOC, nam); + record_setparent(r, dr_clone); + clone_rec_res(dr, dr_clone); + free(nam); + dr_clone->dr_name = vtd[i].vtd_str; i++; } + } + /* More clones than results, shrink */ + else if (dr->dr_clones >= i) { + diff = dr->dr_clones - i; + while (diff-- >= 0) { + dr_tmp = TAILQ_LAST(&dr->dr_clone.head, recclone_head); + rec_del(dr_tmp); + } + } - /* More results than clones, expand */ - if (vtd_len > i) { - diff = vtd_len - i; - r = ds->ds_res.rr_type->rt_record; - MDNS_INIT_ASSERT(r, r_magic); - while (diff-- > 0) { - ds_clone = clone_res(ds); - ds_clone->ds_clones = 0; - ds->ds_clones++; - ds_clone->ds_data = vtd[i].vtd_str; - rr = &ds_clone->ds_res; - p = mdns_res_encode(mdns_c_in, rt->rt_type, ds_clone->ds_data, - MDNS_ENC_WCHAR, vtd[i].vtd_len, &rlen); - record_res_add(r, &rr, RECORD_NOALLOC, rt->rt_type, p, rlen); - record_res_setparent(&ds_clone->ds_res, ds); - i++; - } - } - /* More clones than results, shrink */ - else if (ds->ds_clones >= i) { - diff = ds->ds_clones - i; - while (diff-- >= 0) { - ds_tmp = TAILQ_LAST(&ds->ds_clone.head, clone_head); - TAILQ_REMOVE(&ds->ds_clone.head, ds_tmp, ds_clone.next); - record_res_del(&ds_tmp->ds_res); - free(ds_tmp); - ds->ds_clones--; - } - } + /* Update resources that points to this record */ + names = malloc(sizeof(wchar_t *) * vtd_len); + TAILQ_FOREACH_SAFE(ds, &dr->dr_res_ptr, ds_ptr_next, ds2) { + for (i = 0; i < vtd_len; i++) + names[i] = _wcsdup(vtd[i].vtd_str); + res_update(ds, names, vtd_len); } + free(names); - ds->ds_flags &= ~DS_INVALID; - /* TODO: cache flush annonuce */ - free(vtd); dprintf(DEBUG_DBR, - "Database resource %x updated, type=%d, ttl=%d, clones=%d", - ds, rt->rt_type, ds->ds_ttl, vtd_len-1); - return; -out: - var_vtdfree(vtd, vtd_len); - dprintf(DEBUG_DBR, "Database resource %x marked as invalid", ds); + "Database record r=%x, (%ls) updated, flags=%x, clones=%d", + dr, dr->dr_names[dr->dr_cur], dr->dr_flags, dr->dr_clones); } -static struct dbr_rec * -clone_rec(struct dbr_rec *orig) -{ - struct dbr_rec *dr; - - dr = malloc(sizeof(struct dbr_rec)); - if (dr == NULL) - return (NULL); - memcpy(dr, orig, sizeof(struct dbr_rec)); - dr->dr_flags |= DR_CLONE; - TAILQ_INSERT_TAIL(&orig->dr_clone.head, dr, dr_clone.next); - dprintf(DEBUG_DBR, "Database record cloned dr=%x, source=%x", dr, orig); - return (dr); -} - - +/* + * Variable update handler to record names, callback routine from + * variable system. + */ static void rec_var_update(void *ptr) { - struct dbr_rec *dr, *dr_clone, *dr_tmp; struct dbr *dbr; + struct dbr_rec *dr, *dr2; + struct dbr_pac *pac; + struct dbr_res *ds; struct record *r; struct vt_data *vtd; size_t vtd_len; wchar_t *wp; - char *nam; - uint32_t i; - int32_t diff; dr = ptr; MDNS_INIT_ASSERT(dr, dr_magic); @@ -249,91 +304,58 @@ MDNS_INIT_ASSERT(dbr, dbr_magic); r = &dr->dr_rec; + assert(dr == dr->dr_chead); + wp = dr->dr_names[dr->dr_cur]; vtd = var_expand(&dbr->dbr_vars, wp, &vtd_len); - if (vtd_len >= 1) { - nam = mdns_name_encode(vtd[0].vtd_str, vtd[0].vtd_len, - MDNS_ENC_AUTO); - record_setname(r, nam); - free(nam); - i = 1; + rec_update(dr, vtd, vtd_len); + free(vtd); - /* - * Replace names as long as there are enough clones - * and results available. - */ - TAILQ_FOREACH(dr_clone, &dr->dr_clone.head, dr_clone.next) { - if (i == dr->dr_clones || i == vtd_len) - break; - nam = mdns_name_encode(vtd[i].vtd_str, vtd[i].vtd_len, - MDNS_ENC_AUTO); - record_setname(r, nam); - free(nam); - i++; - } - - /* More results than clones, expand */ - if (vtd_len > i) { - diff = vtd_len - i; - while (diff-- > 0) { - dr_clone = clone_rec(dr); - dr_clone->dr_clones = 0; - dr->dr_clones++; - r = &dr_clone->dr_rec; - nam = mdns_name_encode(vtd[i].vtd_str, vtd[i].vtd_len, - MDNS_ENC_AUTO); - record_get(&dbr->dbr_recs, &r, RECORD_NOALLOC, nam); - record_setparent(r, dr_clone); -// FIXME clone all resource data - free(nam); - i++; - } - } - /* More clones than results, shrink */ - else if (dr->dr_clones >= i) { - diff = dr->dr_clones - i; - while (diff-- >= 0) { - dr_tmp = TAILQ_LAST(&dr->dr_clone.head, recclone_head); - TAILQ_REMOVE(&dr->dr_clone.head, dr_tmp, dr_clone.next); - r = &dr_tmp->dr_rec; - record_foreach_type(r, record_type_del_cb, NULL); - record_release(r); - MDNS_INIT_UNSET(dr_tmp, dr_magic); - free(dr_tmp); - dr->dr_clones--; - } - } - - if (dr->dr_type == DR_SHARED) - dr->dr_state = DR_OK; - else { - dr->dr_state = DR_TENTATIVE; - /* TODO: relaunch probe */ - } + /* + * Create a probe context and and affected records to it + */ + pac = dbr_pac_new(dbr, PAC_PROBE); + dbr_probe_add(pac, dr); + TAILQ_FOREACH(dr2, &dr->dr_clone.head, dr_clone.next) { + dbr_probe_add(pac, dr2); } - else { - dr->dr_state = DR_INVALID; + /* Resources pointing to this record */ + TAILQ_FOREACH(ds, &dr->dr_res_ptr, ds_ptr_next) { + MDNS_INIT_ASSERT(ds, ds_magic); + dr2 = ds->ds_type->dt_rec; + MDNS_INIT_ASSERT(dr2, dr_magic); + dbr_probe_add(pac, dr2); } - - if (vtd != NULL) - var_vtdfree(vtd, vtd_len); - - dprintf(DEBUG_DBR, - "Database record r=%x updated name=%ls, state=%d, clones=%d", - dr, wp, dr->dr_state, dr->dr_clones); + dbr_probe_start(pac); } +/* + * Add a new database record + * dbr - Record database + * ident - Unique record set identifier + * names - Array of un-expanded alternativ names (NULL-terminated) + * shared - Shared record? + * + */ struct dbr_rec * -dbr_add(struct dbr *dbr, wchar_t **names, int type) +dbr_add(struct dbr *dbr, char *ident, wchar_t **names, int shared) { wchar_t *wp; struct vt_data *vtd; size_t vtd_len; - struct dbr_rec *dr, *dr_clone; + struct dbr_rec *dr; struct record *r; - char *nam; int error; - uint32_t i, j; + uint32_t i; + + dr = hashtbl_find(&dbr->dbr_ident, ident, strlen(ident)); + if (dr != NULL) { + dprintf(DEBUG_DBR, "Record set %s does already exists dr=%x", + ident, dr); + return (dr); + } + else if (names[0] == NULL) + return (NULL); i = 0; error = -1; @@ -341,78 +363,61 @@ TAILQ_INIT(&dr->dr_clone.head); dr->dr_clones = 0; dr->dr_dbr = dbr; + dr->dr_name = NULL; + dr->dr_ident = strdup(ident); dr->dr_names = names; - dr->dr_type = type; dr->dr_cur = 0; + dr->dr_cols = 0; + dr->dr_col_ts = 0; dr->dr_chead = dr; + dr->dr_flags = DR_INVALID; + r = &dr->dr_rec; + record_get(&dbr->dbr_recs, &r, RECORD_NOALLOC, ident); + record_setparent(r, dr); + + if (shared) + dr->dr_flags |= DR_SHARED; + TAILQ_INIT(&dr->dr_res_ptr); MDNS_INIT_SET(dr, dr_magic); - r = &dr->dr_rec; + do { wp = names[i++]; if (wp == NULL) break; vtd = var_expand(&dbr->dbr_vars, wp, &vtd_len); - if (vtd_len >= 1) { - nam = mdns_name_encode(vtd[0].vtd_str, vtd[0].vtd_len, - MDNS_ENC_AUTO); - record_get(&dbr->dbr_recs, &r, RECORD_NOALLOC, nam); - record_setparent(r, dr); - free(nam); - } - error = var_reg(&dbr->dbr_vars, wp, rec_var_update, dr); - if (error != 0) { - dprintf(DEBUG_DBR, "Variable registration failed"); - var_dereg(&dbr->dbr_vars, wp, dr); - if (vtd_len >= 1) - record_release(r); + if (vtd_len > 0) { + var_reg(&dbr->dbr_vars, wp, rec_var_update, dr); + rec_update(dr, vtd, vtd_len); } else { - dr->dr_cur = i - 1; - for (j = 1; j < vtd_len; j++) { - nam = mdns_name_encode(vtd[j].vtd_str, vtd[j].vtd_len, - MDNS_ENC_AUTO); - dr_clone = clone_rec(dr); - dr->dr_clones++; - dr_clone->dr_chead = dr; - r = &dr_clone->dr_rec; - record_get(&dbr->dbr_recs, &r, RECORD_NOALLOC, nam); - record_setparent(r, dr_clone); - free(nam); - } + var_vtdfree(vtd, vtd_len); } + } while (vtd_len == 0); - var_vtdfree(vtd, vtd_len); - } while (error != 0); - /* * If all alternative names fail to expand just set it to the * first name and mark the record as invalid. */ - if (error != 0) { + if (vtd_len == 0) { wp = names[0]; var_reg(&dbr->dbr_vars, wp, rec_var_update, dr); - dr->dr_state = DR_INVALID; dr->dr_cur = 0; - - /* - * This is slightly "incorrect" as we add the unexpanded - * name to the record database, but if we do not add it - * we can't add any resources to this record. - */ - nam = mdns_name_encode(wp, wcslen(wp), MDNS_ENC_WCHAR); - record_get(&dbr->dbr_recs, &r, RECORD_NOALLOC, nam); - record_setparent(&dr->dr_rec, r); } else { - dr->dr_state = DR_TENTATIVE; + dr->dr_cur = i - 1; } - if (type == DR_SHARED && dr->dr_state == DR_TENTATIVE) - dr->dr_state = DR_OK; + dr->dr_flags &= ~DR_OK; + hashtbl_add(&dbr->dbr_ident, dr->dr_ident, strlen(dr->dr_ident), dr, 0); + + if ((dr->dr_flags & DR_SHARED) && !(dr->dr_flags & DR_INVALID)) + dr->dr_flags |= DR_OK; - dprintf(DEBUG_DBR, "New database record dr=%x, name=%ls, clones=%d", - dr, dr->dr_names[dr->dr_cur], dr->dr_clones); + dprintf(DEBUG_DBR, + "New database record ident=%s, dr=%x, name=%ls, clones=%d, " + "flags=%x", dr->dr_ident, dr, dr->dr_names[dr->dr_cur], + dr->dr_clones, dr->dr_flags); return (dr); } @@ -434,20 +439,68 @@ MDNS_INIT_ASSERT(rt, rt_magic); dt = record_type_getparent(rt); - record_type_foreach_res(rt, record_res_del_cb, NULL); assert(record_type_refcnt(rt) == 0); free(dt); } +/* + * Free a database record and resources allocated to it + * dr - Database record + */ +static void +rec_del(struct dbr_rec *dr) +{ + struct dbr_rec *drh; + struct record *r; + wchar_t *wp; + int i; + + MDNS_INIT_ASSERT(dr, dr_magic); + r = &dr->dr_rec; + drh = dr->dr_chead; + MDNS_INIT_ASSERT(drh, dr_magic); + + if (dr->dr_flags & DR_PROBING) + dbr_probe_del(dr); + if (dr->dr_flags & DR_ANNOUNCE) + dbr_an_del(dr); + + if (dr->dr_flags & DR_CLONE) { + TAILQ_REMOVE(&drh->dr_clone.head, dr, dr_clone.next); + drh->dr_clones--; + } + else { + i = 0; + do { + wp = dr->dr_names[i++]; + if (wp != NULL) + free(wp); + } while(wp != NULL); + free(dr->dr_names); + free(dr->dr_ident); + } + + record_foreach_type(r, record_type_del_cb, NULL); + record_release(r); + assert(record_refcnt(r) == 0); + if (!(dr->dr_flags & DR_INVALID)) + free(dr->dr_name); + MDNS_INIT_UNSET(dr, dr_magic); + free(dr); +} + +/* + * Remove a database record, any existing clones and also removes + * resources that are using this record name as their resource data. + * dr - Database record + */ void dbr_del(struct dbr_rec *dr) { - struct record *r; struct dbr *dbr; struct dbr_rec *dr2, *dr_tmp; - int i; - wchar_t *wp; + struct dbr_res *ds, *ds2; MDNS_INIT_ASSERT(dr, dr_magic); dbr = dr->dr_dbr; @@ -455,58 +508,253 @@ dr = dr->dr_chead; var_dereg(&dbr->dbr_vars, dr->dr_names[dr->dr_cur], dr); + /* + * Remove any resources that are using this records name + * as their resource data. + */ + TAILQ_FOREACH_SAFE(ds, &dr->dr_res_ptr, ds_ptr_next, ds2) + dbr_res_del(ds); TAILQ_FOREACH_SAFE(dr2, &dr->dr_clone.head, dr_clone.next, dr_tmp) { - /* TODO: clear ouput queue */ - MDNS_INIT_ASSERT(dr2, dr_magic); - r = &dr2->dr_rec; - TAILQ_REMOVE(&dr->dr_clone.head, dr2, dr_clone.next); - dr->dr_clones--; - dprintf(DEBUG_DBR, "Removing record clone dr=%x, orig=%x", dr2, dr); - record_foreach_type(r, record_type_del_cb, NULL); - record_release(r); - assert(record_refcnt(r) == 0); - MDNS_INIT_UNSET(dr2, dr_magic); - free(dr2); + rec_del(dr2); + } + rec_del(dr); + dprintf(DEBUG_DBR, "Removed record dr=%x", dr); +} + +/* + * Clone a resource + */ +static struct dbr_res * +clone_res(struct dbr_res *orig) +{ + struct dbr_res *ds; + + ds = malloc(sizeof(struct dbr_res)); + if (ds == NULL) + return (NULL); + memcpy(ds, orig, sizeof(struct dbr_res)); + ds->ds_data = NULL; + ds->ds_flags |= DS_CLONE | DS_INVALID; + ds->ds_time.tv_nsec = ds->ds_time.tv_sec = 0; + TAILQ_INSERT_TAIL(&orig->ds_clone.head, ds, ds_clone.next); + dprintf(DEBUG_DBR, "Database resource cloned ds=%x, source=%x", + ds, orig); + return (ds); +} + +/* + * Update a resource with new data + */ +static void +res_update(struct dbr_res *ds, wchar_t **res, size_t reslen) +{ + struct dbr_rec *dr; + struct dbr_type *dt; + struct dbr_res *ds_clone, *ds_tmp; + struct record *r; + struct record_type *rt; + struct record_res *rr; + char *p; + size_t rlen; + int32_t diff; + uint32_t i; + + MDNS_INIT_ASSERT(ds, ds_magic); + dt = ds->ds_type; + MDNS_INIT_ASSERT(dt, dt_magic); + dr = dt->dt_rec; + MDNS_INIT_ASSERT(dr, dr_magic); + rt = &dt->dt_type; + rr = &ds->ds_res; + r = rt->rt_record; + + assert(ds == ds->ds_chead); + + /* + * If there are no resources available in the provided array, + * invalidate the record and remove existing clones. + */ + if (reslen == 0) { + if (ds->ds_data != NULL) + free(ds->ds_data); + record_res_setdata(&ds->ds_res, NULL, 0); + ds->ds_data = NULL; + ds->ds_flags |= DS_INVALID; + + TAILQ_FOREACH_SAFE(ds_clone, &ds->ds_clone.head, + ds_clone.next, ds_tmp) { + res_del(ds_clone); + } + return; + } + + i = 1; + p = mdns_res_encode(mdns_c_in, rt->rt_type, res[0], + MDNS_ENC_WCHAR, wcslen(res[0]), &rlen); + if (ds->ds_data != NULL) + free(ds->ds_data); + record_res_setdata(&ds->ds_res, p, rlen); + ds->ds_flags &= ~DS_INVALID; + ds->ds_data = res[0]; + + /* + * Replace resource data as long as there is enough clones + * and results available. + */ + TAILQ_FOREACH(ds_clone, &ds->ds_clone.head, ds_clone.next) { + if ((i - 1) == ds->ds_clones || i == reslen) + break; + if (!(ds_clone->ds_flags & DS_INVALID)) + free(ds_clone->ds_data); + ds_clone->ds_data = res[i]; + p = mdns_res_encode(mdns_c_in, rt->rt_type, res[i], + MDNS_ENC_WCHAR, wcslen(res[i]), &rlen); + record_res_setdata(&ds_clone->ds_res, p, rlen); + ds_clone->ds_flags &= ~DS_INVALID; + i++; + } + + /* More results than clones, expand */ + if (reslen > i) { + diff = reslen - i; + r = ds->ds_res.rr_type->rt_record; + MDNS_INIT_ASSERT(r, r_magic); + while (diff-- > 0) { + ds_clone = clone_res(ds); + ds_clone->ds_clones = 0; + ds->ds_clones++; + ds_clone->ds_data = res[i]; + rr = &ds_clone->ds_res; + p = mdns_res_encode(mdns_c_in, rt->rt_type, + res[i], MDNS_ENC_WCHAR, wcslen(res[i]), &rlen); + record_res_add(r, &rr, RECORD_NOALLOC, rt->rt_type, + p, rlen); + record_res_setparent(&ds_clone->ds_res, ds); + i++; + ds_clone->ds_flags &= ~DS_INVALID; + } + } + /* More clones than results, shrink */ + else if (ds->ds_clones >= i) { + diff = ds->ds_clones - i; + while (diff-- >= 0) { + ds_tmp = TAILQ_LAST(&ds->ds_clone.head, clone_head); + res_del(ds_tmp); + } } - r = &dr->dr_rec; - record_foreach_type(r, record_type_del_cb, NULL); - record_release(r); - assert(record_refcnt(r) == 0); - i = 0; - do { - wp = dr->dr_names[i++]; - if (wp != NULL) - free(wp); - } while(wp != NULL); - free(dr->dr_names); - MDNS_INIT_UNSET(dr, dr_magic); - free(dr); - dprintf(DEBUG_DBR, "Removed record dr=%x", dr); + dprintf(DEBUG_DBR, "Resource ds=%x updated, clones=%d", + ds, ds->ds_clones); } +/* + * Variable update handler to resource data, will re-expand vaiables + * and update affected resources. + * This is an asynchronous callback routine which is called from the + * variable event system. + * + * ptr - Opaque pointer passed to var_reg(), to this routine it's + * always of type dbr_res {} + */ +static void +res_var_update(void *ptr) +{ + struct dbr *dbr; + struct dbr_rec *dr; + struct dbr_type *dt; + struct dbr_res *ds; + struct dbr_pac *pac; + struct vt_data *vtd; + size_t vtd_len; + uint32_t i; + wchar_t **names; + + ds = ptr; + MDNS_INIT_ASSERT(ds, ds_magic); + dt = ds->ds_type; + MDNS_INIT_ASSERT(dt, dt_magic); + dr = dt->dt_rec; + MDNS_INIT_ASSERT(dr, dr_magic); + dbr = dr->dr_dbr; + MDNS_INIT_ASSERT(dbr, dbr_magic); + + /* + * We should never get a "pointer resource" here, they are updated + * when the rrset they point to are updated. + */ + assert(!(ds->ds_flags & DS_POINTER)); + + if (dr->dr_flags & DR_PROBING) + dbr_probe_del(dr); + if (dr->dr_flags & DR_ANNOUNCE) + dbr_an_del(dr); + + vtd = var_expand(&dbr->dbr_vars, ds->ds_vdata.wp, &vtd_len); + names = malloc(sizeof(wchar_t *) * vtd_len); + for (i = 0; i < vtd_len; i++) { + names[i] = vtd[i].vtd_str; + } + res_update(ds, names, vtd_len); + free(vtd); + + if (dr->dr_flags & DR_OK) { + pac = dbr_pac_new(dbr, PAC_ANNOUNCE); + dbr_an_add(pac, dr); + dbr_an_start(pac); + } + else { + pac = dbr_pac_new(dbr, PAC_PROBE); + dbr_probe_add(pac, dr); + dbr_probe_start(pac); + } +} +/* + * Add a resource to database record + * dr - Parent database record + * type - DNS type + * ttl - Resource TTL + * res - Unexpanded resource string + */ struct dbr_res * -dbr_res_add(struct dbr_rec *dr, uint16_t type, uint32_t ttl, wchar_t *res) +dbr_res_add(struct dbr_rec *dr, uint16_t type, uint32_t ttl, void *res, int ptr) { struct record *r; + struct record_type *rt, *rt2; struct record_res *rr; - struct record_type *rt, *rt2; struct dbr *dbr; - struct dbr_rec *dr_orig; - struct dbr_res *ds, *ds2; + struct dbr_rec *dr_orig, *drp, *drp2; + struct dbr_res *ds; struct dbr_type *dt; struct vt_data *vtd; - size_t vtd_len, i, j, rlen; - char *p; + size_t vtd_len, j, namlen; + wchar_t *wp, **names; + char *ident; MDNS_INIT_ASSERT(dr, dr_magic); dbr = dr->dr_dbr; MDNS_INIT_ASSERT(dbr, dbr_magic); + wp = res; + ident = res; + names = NULL; + namlen = 0; + + /* + * If we where passed a pointer, make sure it exists. + */ + if (ptr) { + drp = hashtbl_find(&dbr->dbr_ident, ident, strlen(ident)); + if (drp == NULL) + return (NULL); >>> TRUNCATED FOR MAIL (1000 lines) <<<