isc_result_t res_nupdate(res_state statp, ns_updrec *rrecp_in) { ns_updrec *rrecp; double answer[PACKETSZ / sizeof (double)]; double packet[2*PACKETSZ / sizeof (double)]; struct zonegrp *zptr, tgrp; int nscount = 0; unsigned n; unsigned rval; struct sockaddr_in nsaddrs[MAXNS]; ns_tsig_key *key; void *zcookie = 0; void *zcookp = &zcookie; isc_result_t rcode; again: /* Make sure all the updates are in the same zone, and find out what zone they are in. */ zptr = NULL; for (rrecp = rrecp_in; rrecp; rrecp = ISC_LIST_NEXT(rrecp, r_link)) { /* Find the origin for it if there is one. */ tgrp.z_class = rrecp->r_class; rcode = res_findzonecut(statp, rrecp->r_dname, tgrp.z_class, RES_EXHAUSTIVE, tgrp.z_origin, sizeof tgrp.z_origin, tgrp.z_nsaddrs, MAXNS, &tgrp.z_nscount, zcookp); if (rcode != ISC_R_SUCCESS) goto done; if (tgrp.z_nscount <= 0) { rcode = ISC_R_NOTZONE; goto done; } /* Make a group for it if there isn't one. */ if (zptr == NULL) { zptr = malloc(sizeof *zptr); if (zptr == NULL) { rcode = ISC_R_NOMEMORY; goto done; } *zptr = tgrp; zptr->z_flags = 0; ISC_LIST_INIT(zptr->z_rrlist); } else if (ns_samename(tgrp.z_origin, zptr->z_origin) == 0 || tgrp.z_class != zptr->z_class) { /* Some of the records are in different zones. */ rcode = ISC_R_CROSSZONE; goto done; } /* Thread this rrecp onto the zone group. */ ISC_LIST_APPEND(zptr->z_rrlist, rrecp, r_glink); } /* Construct zone section and prepend it. */ rrecp = res_mkupdrec(ns_s_zn, zptr->z_origin, zptr->z_class, ns_t_soa, 0); if (rrecp == NULL) { rcode = ISC_R_UNEXPECTED; goto done; } ISC_LIST_PREPEND(zptr->z_rrlist, rrecp, r_glink); zptr->z_flags |= ZG_F_ZONESECTADDED; /* Marshall the update message. */ n = sizeof packet; rcode = res_nmkupdate(statp, ISC_LIST_HEAD(zptr->z_rrlist), packet, &n); if (rcode != ISC_R_SUCCESS) goto done; /* Temporarily replace the resolver's nameserver set. */ nscount = nscopy(nsaddrs, statp->nsaddr_list, statp->nscount); statp->nscount = nsprom(statp->nsaddr_list, zptr->z_nsaddrs, zptr->z_nscount); /* Send the update and remember the result. */ key = (ns_tsig_key *)0; rcode = find_tsig_key (&key, zptr->z_origin, zcookie); if (rcode == ISC_R_SUCCESS) { rcode = res_nsendsigned(statp, packet, n, key, answer, sizeof answer, &rval); tkey_free (&key); } else if (rcode == ISC_R_NOTFOUND || rcode == ISC_R_KEY_UNKNOWN) { rcode = res_nsend(statp, packet, n, answer, sizeof answer, &rval); } if (rcode != ISC_R_SUCCESS) goto undone; rcode = ns_rcode_to_isc (((HEADER *)answer)->rcode); if (zcookie && rcode == ISC_R_BADSIG) { repudiate_zone (&zcookie); } undone: /* Restore resolver's nameserver set. */ statp->nscount = nscopy(statp->nsaddr_list, nsaddrs, nscount); nscount = 0; done: if (zptr) { if ((zptr->z_flags & ZG_F_ZONESECTADDED) != 0) res_freeupdrec(ISC_LIST_HEAD(zptr->z_rrlist)); free(zptr); } /* If the update failed because we used a cached zone and it didn't work, try it again without the cached zone. */ if (zcookp && (rcode == ISC_R_NOTZONE || rcode == ISC_R_BADSIG)) { zcookp = 0; goto again; } if (zcookie) forget_zone (&zcookie); return rcode; }
int res_nupdate(res_state statp, ns_updrec *rrecp_in, ns_tsig_key *key) { ns_updrec *rrecp; u_char answer[PACKETSZ]; u_char *packet; struct zonegrp *zptr, tgrp; LIST(struct zonegrp) zgrps; int nzones = 0, nscount = 0, n; union res_sockaddr_union nsaddrs[MAXNS]; packet = malloc(NS_MAXMSG); if (packet == NULL) { DPRINTF(("malloc failed")); return (0); } /* Thread all of the updates onto a list of groups. */ INIT_LIST(zgrps); memset(&tgrp, 0, sizeof (tgrp)); for (rrecp = rrecp_in; rrecp; rrecp = LINKED(rrecp, r_link) ? NEXT(rrecp, r_link) : NULL) { int nscnt; /* Find the origin for it if there is one. */ tgrp.z_class = rrecp->r_class; nscnt = res_findzonecut2(statp, rrecp->r_dname, tgrp.z_class, RES_EXHAUSTIVE, tgrp.z_origin, sizeof tgrp.z_origin, tgrp.z_nsaddrs, MAXNS); if (nscnt <= 0) { DPRINTF(("res_findzonecut failed (%d)", nscnt)); goto done; } tgrp.z_nscount = nscnt; /* Find the group for it if there is one. */ for (zptr = HEAD(zgrps); zptr != NULL; zptr = NEXT(zptr, z_link)) if (ns_samename(tgrp.z_origin, zptr->z_origin) == 1 && tgrp.z_class == zptr->z_class) break; /* Make a group for it if there isn't one. */ if (zptr == NULL) { zptr = malloc(sizeof *zptr); if (zptr == NULL) { DPRINTF(("malloc failed")); goto done; } *zptr = tgrp; zptr->z_flags = 0; INIT_LINK(zptr, z_link); INIT_LIST(zptr->z_rrlist); APPEND(zgrps, zptr, z_link); } /* Thread this rrecp onto the right group. */ APPEND(zptr->z_rrlist, rrecp, r_glink); } for (zptr = HEAD(zgrps); zptr != NULL; zptr = NEXT(zptr, z_link)) { /* Construct zone section and prepend it. */ rrecp = res_mkupdrec(ns_s_zn, zptr->z_origin, zptr->z_class, ns_t_soa, 0); if (rrecp == NULL) { DPRINTF(("res_mkupdrec failed")); goto done; } PREPEND(zptr->z_rrlist, rrecp, r_glink); zptr->z_flags |= ZG_F_ZONESECTADDED; /* Marshall the update message. */ n = res_nmkupdate(statp, HEAD(zptr->z_rrlist), packet, NS_MAXMSG); DPRINTF(("res_mkupdate -> %d", n)); if (n < 0) goto done; /* Temporarily replace the resolver's nameserver set. */ nscount = res_getservers(statp, nsaddrs, MAXNS); res_setservers(statp, zptr->z_nsaddrs, zptr->z_nscount); /* Send the update and remember the result. */ if (key != NULL) n = res_nsendsigned(statp, packet, n, key, answer, sizeof answer); else n = res_nsend(statp, packet, n, answer, sizeof answer); if (n < 0) { DPRINTF(("res_nsend: send error, n=%d (%s)\n", n, strerror(errno))); goto done; } if (((HEADER *)answer)->rcode == NOERROR) nzones++; /* Restore resolver's nameserver set. */ res_setservers(statp, nsaddrs, nscount); nscount = 0; } done: while (!EMPTY(zgrps)) { zptr = HEAD(zgrps); if ((zptr->z_flags & ZG_F_ZONESECTADDED) != 0) res_freeupdrec(HEAD(zptr->z_rrlist)); UNLINK(zgrps, zptr, z_link); free(zptr); } if (nscount != 0) res_setservers(statp, nsaddrs, nscount); free(packet); return (nzones); }