/*
* lib/krb5/krb/walk_rtree.c
*
- * Copyright 1990,1991,2008 by the Massachusetts Institute of Technology.
+ * Copyright 1990,1991,2008,2009 by the Massachusetts Institute of Technology.
* All Rights Reserved.
*
* Export of this software from the United States of America may
* require a specific license from the United States Government.
* It is the responsibility of any person or organization contemplating
* export to obtain such a license before exporting.
- *
+ *
* WITHIN THAT CONSTRAINT, permission to use, copy, modify, and
* distribute this software and its documentation for any purpose and
* without fee is hereby granted, provided that the above copyright
* M.I.T. makes no representations about the suitability of
* this software for any purpose. It is provided "as is" without express
* or implied warranty.
- *
*
* krb5_walk_realm_tree()
+ *
+ * internal function, used by krb5_get_cred_from_kdc()
*/
+#include "k5-int.h"
+#include "int-proto.h"
+
+/*
+ * Structure to help with finding the common suffix between client and
+ * server realm during hierarchical traversal.
+ */
+struct hstate {
+ char *str;
+ size_t len;
+ char *tail;
+ char *dot;
+};
+
+static krb5_error_code
+rtree_capath_tree(
+ krb5_context context,
+ const krb5_data *client,
+ const krb5_data *server,
+ char **vals,
+ krb5_principal **tree);
+
+static krb5_error_code
+rtree_capath_vals(
+ krb5_context context,
+ const krb5_data *client,
+ const krb5_data *server,
+ char ***vals);
+
+static krb5_error_code
+rtree_hier_tree(
+ krb5_context context,
+ const krb5_data *client,
+ const krb5_data *server,
+ krb5_principal **rettree,
+ int sep);
+
+static krb5_error_code
+rtree_hier_realms(
+ krb5_context context,
+ const krb5_data *client,
+ const krb5_data *server,
+ krb5_data **realms,
+ size_t *nrealms,
+ int sep);
+
+static krb5_error_code
+rtree_hier_tweens(
+ krb5_context context,
+ struct hstate *realm,
+ krb5_data **tweens,
+ size_t *ntweens,
+ int dotail,
+ int sep);
+
+static void
+adjtail(struct hstate *c, struct hstate *s, int sep);
+
+static void
+comtail(struct hstate *c, struct hstate *s, int sep);
+
+krb5_error_code
+krb5_walk_realm_tree(
+ krb5_context context,
+ const krb5_data *client,
+ const krb5_data *server,
+ krb5_principal **tree,
+ int realm_sep)
+{
+ krb5_error_code retval = 0;
+ char **capvals;
+
+ if (client->data == NULL || server->data == NULL)
+ return KRB5_NO_TKT_IN_RLM;
+
+ if (client->length == server->length &&
+ memcmp(client->data, server->data, server->length) == 0) {
+ return KRB5_NO_TKT_IN_RLM;
+ }
+ retval = rtree_capath_vals(context, client, server, &capvals);
+ if (retval)
+ return retval;
+
+ if (capvals != NULL) {
+ retval = rtree_capath_tree(context, client, server, capvals, tree);
+ return retval;
+ }
+
+ retval = rtree_hier_tree(context, client, server, tree, realm_sep);
+ return retval;
+}
+
/* ANL - Modified to allow Configurable Authentication Paths.
* This modification removes the restriction on the choice of realm
* names, i.e. they nolonger have to be hierarchical. This
* NERSC.GOV = ES.NET
* PNL.GOV = ES.NET
* ES.NET = .
- * HAL.COM = K5.MOON
- * HAL.COM = K5.JUPITER
+ * HAL.COM = K5.MOON
+ * HAL.COM = K5.JUPITER
* }
* NERSC.GOV = {
* ANL.GOV = ES.NET
* ANL.GOV = ES.NET
* }
* ES.NET = {
- * ANL.GOV = .
+ * ANL.GOV = .
* }
* HAL.COM = {
* ANL.GOV = K5.JUPITER
* will work together.
* DEE - 5/23/95
*/
-#include "k5-int.h"
-#include "int-proto.h"
-/* internal function, used by krb5_get_cred_from_kdc() */
+/*
+ * Build a tree given a set of profile values retrieved by
+ * walk_rtree_capath_vals().
+ */
+static krb5_error_code
+rtree_capath_tree(
+ krb5_context context,
+ const krb5_data *client,
+ const krb5_data *server,
+ char **vals,
+ krb5_principal **rettree)
+{
+ krb5_error_code retval = 0;
+ unsigned int nvals, nlinks, nprincs, i;
+ krb5_data srcrealm, dstrealm;
+ krb5_principal *tree, *pprinc;
-#ifndef min
-#define min(x,y) ((x) < (y) ? (x) : (y))
-#define max(x,y) ((x) > (y) ? (x) : (y))
-#endif
+ *rettree = NULL;
+ tree = pprinc = NULL;
+ for (nvals = 0; vals[nvals] != NULL; nvals++)
+ ;
+ if (vals[0] != NULL && *vals[0] == '.') {
+ nlinks = 0;
+ } else {
+ nlinks = nvals;
+ }
+ nprincs = nlinks + 2;
+ tree = calloc(nprincs + 1, sizeof(krb5_principal));
+ if (tree == NULL) {
+ retval = ENOMEM;
+ goto error;
+ }
+ for (i = 0; i < nprincs + 1; i++)
+ tree[i] = NULL;
+ /* Invariant: PPRINC points one past end of list. */
+ pprinc = &tree[0];
+ /* Local TGS name */
+ retval = krb5_tgtname(context, client, client, pprinc++);
+ if (retval) goto error;
+ srcrealm = *client;
+ for (i = 0; i < nlinks; i++) {
+ dstrealm.data = vals[i];
+ dstrealm.length = strcspn(vals[i], "\t ");
+ retval = krb5_tgtname(context, &dstrealm, &srcrealm, pprinc++);
+ if (retval) goto error;
+ srcrealm = dstrealm;
+ }
+ retval = krb5_tgtname(context, server, &srcrealm, pprinc++);
+ if (retval) goto error;
+ *rettree = tree;
+
+error:
+ profile_free_list(vals);
+ if (retval) {
+ while (pprinc != NULL && pprinc > &tree[0]) {
+ /* krb5_free_principal() correctly handles null input */
+ krb5_free_principal(context, *--pprinc);
+ *pprinc = NULL;
+ }
+ free(tree);
+ }
+ return retval;
+}
/*
- * xxx The following function is very confusing to read and probably
- * is buggy. It should be documented better. Here is what I've
- * learned about it doing a quick bug fixing walk through. The
- * function takes a client and server realm name and returns the set
- * of realms (in a field called tree) that you need to get tickets in
- * in order to get from the source realm to the destination realm. It
- * takes a realm separater character (normally ., but presumably there
- * for all those X.500 realms) . There are two modes it runs in: the
- * ANL krb5.conf mode and the hierarchy mode. The ANL mode is
- * fairly obvious. The hierarchy mode looks for common components in
- * both the client and server realms. In general, the pointer scp and
- * ccp are used to walk through the client and server realms. The
- * com_sdot and com_cdot pointers point to (I think) the beginning of
- * the common part of the realm names. I.E. strcmp(com_cdot,
- * com_sdot) ==0 is roughly an invarient. However, there are cases
- * where com_sdot and com_cdot are set to point before the start of
- * the client or server strings. I think this only happens when there
- * are no common components. --hartmans 2002/03/14
+ * Get realm list from "capaths" section of the profile. Deliberately
+ * returns success but leaves VALS null if profile_get_values() fails
+ * by not finding anything.
*/
+static krb5_error_code
+rtree_capath_vals(
+ krb5_context context,
+ const krb5_data *client,
+ const krb5_data *server,
+ char ***vals)
+{
+ krb5_error_code retval = 0;
+ /* null-terminated realm names */
+ char *clientz = NULL, *serverz = NULL;
+ const char *key[4];
-krb5_error_code
-krb5_walk_realm_tree(krb5_context context, const krb5_data *client, const krb5_data *server, krb5_principal **tree, int realm_branch_char)
+ *vals = NULL;
+
+ clientz = calloc(client->length + 1, 1);
+ if (clientz == NULL) {
+ retval = ENOMEM;
+ goto error;
+ }
+ memcpy(clientz, client->data, client->length);
+
+ serverz = calloc(server->length + 1, 1);
+ if (clientz == NULL) {
+ retval = ENOMEM;
+ goto error;
+ }
+ memcpy(serverz, server->data, server->length);
+
+ key[0] = "capaths";
+ key[1] = clientz;
+ key[2] = serverz;
+ key[3] = NULL;
+ retval = profile_get_values(context->profile, key, vals);
+ switch (retval) {
+ case PROF_NO_SECTION:
+ case PROF_NO_RELATION:
+ /*
+ * Not found; don't return an error.
+ */
+ retval = 0;
+ break;
+ default:
+ break;
+ }
+error:
+ free(clientz);
+ free(serverz);
+ return retval;
+}
+
+/*
+ * Build tree by hierarchical traversal.
+ */
+static krb5_error_code
+rtree_hier_tree(
+ krb5_context context,
+ const krb5_data *client,
+ const krb5_data *server,
+ krb5_principal **rettree,
+ int sep)
{
krb5_error_code retval;
- krb5_principal *rettree;
- register char *ccp, *scp;
- register char *prevccp = 0, *prevscp = 0;
- char *com_sdot = 0, *com_cdot = 0;
- register int i, links = 0;
- int clen, slen = -1;
- krb5_data tmpcrealm, tmpsrealm;
- int nocommon = 1;
-
- const char *cap_names[4];
- char *cap_client, *cap_server;
- char **cap_nodes;
- krb5_error_code cap_code;
-
-#ifdef DEBUG_REFERRALS
- printf("krb5_walk_realm_tree starting\n");
- printf(" client is %s\n",client->data);
- printf(" server is %s\n",server->data);
-#endif
-
- if (!(client->data &&server->data))
- return KRB5_NO_TKT_IN_RLM;
- if ((cap_client = (char *)malloc(client->length + 1)) == NULL)
- return ENOMEM;
- strncpy(cap_client, client->data, client->length);
- cap_client[client->length] = '\0';
- if ((cap_server = (char *)malloc(server->length + 1)) == NULL) {
- krb5_xfree(cap_client);
- return ENOMEM;
+ krb5_data *realms;
+ const krb5_data *dstrealm, *srcrealm;
+ krb5_principal *tree, *pprinc;
+ size_t nrealms, nprincs, i;
+
+ *rettree = NULL;
+ retval = rtree_hier_realms(context, client, server,
+ &realms, &nrealms, sep);
+ if (retval)
+ return retval;
+ nprincs = nrealms;
+ pprinc = tree = calloc(nprincs + 1, sizeof(krb5_principal));
+ if (tree == NULL) {
+ retval = ENOMEM;
+ goto error;
}
- strncpy(cap_server, server->data, server->length);
- cap_server[server->length] = '\0';
- cap_names[0] = "capaths";
- cap_names[1] = cap_client;
- cap_names[2] = cap_server;
- cap_names[3] = 0;
- cap_code = profile_get_values(context->profile, cap_names, &cap_nodes);
- krb5_xfree(cap_client); /* done with client string */
- cap_names[1] = 0;
- if (cap_code == 0) { /* found a path, so lets use it */
- links = 0;
- if (*cap_nodes[0] != '.') { /* a link of . means direct */
- while(cap_nodes[links]) {
- links++;
- }
- }
- if (cap_nodes[links] != NULL)
- krb5_xfree(cap_nodes[links]);
-
- cap_nodes[links] = cap_server; /* put server on end of list */
- /* this simplifies the code later and make */
- /* cleanup eaiser as well */
- links++; /* count the null entry at end */
- } else { /* no path use hierarchical method */
- krb5_xfree(cap_server); /* failed, don't need server string */
- cap_names[2] = 0;
-
- clen = client->length;
- slen = server->length;
-
- for (com_cdot = ccp = client->data + clen - 1,
- com_sdot = scp = server->data + slen - 1;
- clen && slen && *ccp == *scp ;
- ccp--, scp--, clen--, slen--) {
- if (*ccp == realm_branch_char) {
- com_cdot = ccp;
- com_sdot = scp;
- nocommon = 0;
- }
- }
+ for (i = 0; i < nrealms; i++)
+ tree[i] = NULL;
+ srcrealm = client;
+ for (i = 0; i < nrealms; i++) {
+ dstrealm = &realms[i];
+ retval = krb5_tgtname(context, dstrealm, srcrealm, pprinc++);
+ if (retval) goto error;
+ srcrealm = dstrealm;
+ }
+ *rettree = tree;
+ return 0;
+error:
+ while (pprinc != NULL && pprinc > tree) {
+ krb5_free_principal(context, *--pprinc);
+ *pprinc = NULL;
+ }
+ free(tree);
+ return retval;
+}
- /* ccp, scp point to common root.
- com_cdot, com_sdot point to common components. */
- /* handle case of one ran out */
- if (!clen) {
- /* construct path from client to server, down the tree */
- if (!slen)
- /* in the same realm--this means there is no ticket
- in this realm. */
- return KRB5_NO_TKT_IN_RLM;
- if (*scp == realm_branch_char) {
- /* one is a subdomain of the other */
- com_cdot = client->data;
- com_sdot = scp;
- nocommon = 0;
- } /* else normal case of two sharing parents */
- }
- if (!slen) {
- /* construct path from client to server, up the tree */
- if (*ccp == realm_branch_char) {
- /* one is a subdomain of the other */
- com_sdot = server->data;
- com_cdot = ccp;
- nocommon = 0;
- } /* else normal case of two sharing parents */
- }
- /* determine #links to/from common ancestor */
- if (nocommon)
- links = 1;
- else
- links = 2;
- /* if no common ancestor, artificially set up common root at the last
- component, then join with special code */
- for (ccp = client->data; ccp < com_cdot; ccp++) {
- if (*ccp == realm_branch_char) {
- links++;
- if (nocommon)
- prevccp = ccp;
- }
- }
+/*
+ * Construct list of realms between client and server.
+ */
+static krb5_error_code
+rtree_hier_realms(
+ krb5_context context,
+ const krb5_data *client,
+ const krb5_data *server,
+ krb5_data **realms,
+ size_t *nrealms,
+ int sep)
+{
+ krb5_error_code retval;
+ struct hstate c, s;
+ krb5_data *ctweens, *stweens, *twp, *r, *rp;
+ size_t nctween, nstween;
- for (scp = server->data; scp < com_sdot; scp++) {
- if (*scp == realm_branch_char) {
- links++;
- if (nocommon)
- prevscp = scp;
- }
- }
- if (nocommon) {
- if (prevccp)
- com_cdot = prevccp;
- if (prevscp)
- com_sdot = prevscp;
-
- if(com_cdot == client->data + client->length -1)
- com_cdot = client->data - 1 ;
- if(com_sdot == server->data + server->length -1)
- com_sdot = server->data - 1 ;
- }
- } /* end of if use hierarchical method */
+ r = rp = NULL;
+ c.str = client->data;
+ c.len = client->length;
+ c.dot = c.tail = NULL;
+ s.str = server->data;
+ s.len = server->length;
+ s.dot = s.tail = NULL;
+
+ comtail(&c, &s, sep);
+ adjtail(&c, &s, sep);
+
+ retval = rtree_hier_tweens(context, &c, &ctweens, &nctween, 1, sep);
+ if (retval) goto error;
+ retval = rtree_hier_tweens(context, &s, &stweens, &nstween, 0, sep);
+ if (retval) goto error;
- if (!(rettree = (krb5_principal *)calloc(links+2,
- sizeof(krb5_principal)))) {
- return ENOMEM;
+ *nrealms = nctween + nstween;
+ rp = r = calloc(*nrealms, sizeof(krb5_data));
+ if (r == NULL) {
+ retval = ENOMEM;
+ goto error;
}
- i = 1;
- if ((retval = krb5_tgtname(context, client, client, &rettree[0]))) {
- krb5_xfree(rettree);
- return retval;
+ /* Copy client realm "tweens" forward. */
+ for (twp = ctweens; twp < &ctweens[nctween]; twp++) {
+ retval = krb5int_copy_data_contents(context, twp, rp++);
+ if (retval) goto error;
}
- links--; /* dont count the null entry on end */
- if (cap_code == 0) { /* found a path above */
- tmpcrealm.data = client->data;
- tmpcrealm.length = client->length;
- while( i-1 <= links) {
-
- tmpsrealm.data = cap_nodes[i-1];
- /* don't count trailing whitespace from profile_get */
- tmpsrealm.length = strcspn(cap_nodes[i-1],"\t ");
- if ((retval = krb5_tgtname(context,
- &tmpsrealm,
- &tmpcrealm,
- &rettree[i]))) {
- while (i) {
- krb5_free_principal(context, rettree[i-1]);
- i--;
- }
- krb5_xfree(rettree);
- /* cleanup the cap_nodes from profile_get */
- for (i = 0; i<=links; i++) {
- krb5_xfree(cap_nodes[i]);
- }
- krb5_xfree((char *)cap_nodes);
- return retval;
- }
- tmpcrealm.data = tmpsrealm.data;
- tmpcrealm.length = tmpsrealm.length;
- i++;
- }
- /* cleanup the cap_nodes from profile_get last one has server */
- for (i = 0; i<=links; i++) {
- krb5_xfree(cap_nodes[i]);
- }
- krb5_xfree((char *)cap_nodes);
- } else { /* if not cap then use hierarchical method */
- for (prevccp = ccp = client->data;
- ccp <= com_cdot;
- ccp++) {
- if (*ccp != realm_branch_char)
- continue;
- ++ccp; /* advance past dot */
- tmpcrealm.data = prevccp;
- tmpcrealm.length = client->length -
- (prevccp - client->data);
- tmpsrealm.data = ccp;
- tmpsrealm.length = client->length -
- (ccp - client->data);
- if ((retval = krb5_tgtname(context, &tmpsrealm, &tmpcrealm,
- &rettree[i]))) {
- while (i) {
- krb5_free_principal(context, rettree[i-1]);
- i--;
- }
- krb5_xfree(rettree);
- return retval;
- }
- prevccp = ccp;
- i++;
- }
- if (nocommon) {
- tmpcrealm.data = com_cdot + 1;
- tmpcrealm.length = client->length -
- (com_cdot + 1 - client->data);
- tmpsrealm.data = com_sdot + 1;
- tmpsrealm.length = server->length -
- (com_sdot + 1 - server->data);
- if ((retval = krb5_tgtname(context, &tmpsrealm, &tmpcrealm,
- &rettree[i]))) {
- while (i) {
- krb5_free_principal(context, rettree[i-1]);
- i--;
- }
- krb5_xfree(rettree);
- return retval;
- }
- i++;
+ /* Copy server realm "tweens" backward. */
+ for (twp = &stweens[nstween]; twp-- > stweens;) {
+ krb5int_copy_data_contents(context, twp, rp++);
+ if (retval) goto error;
+ }
+error:
+ if (retval) {
+ *nrealms = 0;
+ while (rp > r) {
+ krb5_free_data_contents(context, --rp);
}
+ free(r);
+ r = NULL;
+ }
+ free(ctweens);
+ free(stweens);
+ *realms = r;
+ return retval;
+}
- for (prevscp = com_sdot + 1, scp = com_sdot - 1;
- scp > server->data;
- scp--) {
- if (*scp != realm_branch_char)
- continue;
- if (scp - 1 < server->data)
- break; /* XXX only if . starts realm? */
- tmpcrealm.data = prevscp;
- tmpcrealm.length = server->length -
- (prevscp - server->data);
- tmpsrealm.data = scp + 1;
- tmpsrealm.length = server->length -
- (scp + 1 - server->data);
- if ((retval = krb5_tgtname(context, &tmpsrealm, &tmpcrealm,
- &rettree[i]))) {
- while (i) {
- krb5_free_principal(context, rettree[i-1]);
- i--;
- }
- krb5_xfree(rettree);
- return retval;
- }
- prevscp = scp + 1;
- i++;
- }
- if (slen && com_sdot >= server->data) {
- /* only necessary if building down tree from ancestor or client */
- /* however, we can get here if we have only one component
- in the server realm name, hence we make sure we found a component
- separator there... */
- tmpcrealm.data = prevscp;
- tmpcrealm.length = server->length -
- (prevscp - server->data);
- if ((retval = krb5_tgtname(context, server, &tmpcrealm,
- &rettree[i]))) {
- while (i) {
- krb5_free_principal(context, rettree[i-1]);
- i--;
- }
- krb5_xfree(rettree);
- return retval;
- }
+/*
+ * Build a list of realms between a given realm and the common
+ * suffix. The original realm is included, but the "tail" is only
+ * included if DOTAIL is true.
+ *
+ * Warning: This function intentionally aliases memory. Caller must
+ * make copies as needed and not call krb5_free_data_contents, etc.
+ */
+static krb5_error_code
+rtree_hier_tweens(
+ krb5_context context,
+ struct hstate *realm,
+ krb5_data **tweens,
+ size_t *ntweens,
+ int dotail,
+ int sep)
+{
+ char *p, *r, *rtail, *lp;
+ size_t rlen, n;
+ krb5_data *tws, *ntws;
+
+ r = realm->str;
+ rlen = realm->len;
+ rtail = realm->tail;
+ *tweens = ntws = tws = NULL;
+ *ntweens = n = 0;
+
+ for (lp = p = r; p < &r[rlen]; p++) {
+ if (*p != sep && &p[1] != &r[rlen])
+ continue;
+ if (lp == rtail && !dotail)
+ break;
+ ntws = realloc(tws, (n + 1) * sizeof(krb5_data));
+ if (ntws == NULL) {
+ free(tws);
+ return ENOMEM;
}
+ tws = ntws;
+ tws[n].data = lp;
+ tws[n].length = &r[rlen] - lp;
+ n++;
+ if (lp == rtail)
+ break;
+ lp = &p[1];
}
- *tree = rettree;
-
-#ifdef DEBUG_REFERRALS
- printf("krb5_walk_realm_tree ending; tree (length %d) is:\n",links);
- for(i=0;i<links+2;i++) {
- if ((*tree)[i])
- krb5int_dbgref_dump_principal("krb5_walk_realm_tree tree",(*tree)[i]);
- else
- printf("tree element %i null\n");
- }
-#endif
+ *tweens = tws;
+ *ntweens = n;
return 0;
}
-#ifdef DEBUG_REFERRALS
-void krb5int_dbgref_dump_principal(char *d, krb5_principal p)
+/*
+ * Adjust suffixes that each starts at the beginning of a component,
+ * to avoid the problem where "BC.EXAMPLE.COM" is erroneously reported
+ * as a parent of "ABC.EXAMPLE.COM".
+ */
+static void
+adjtail(struct hstate *c, struct hstate *s, int sep)
{
- int n;
-
- printf(" **%s: ",d);
- for (n=0;n<p->length;n++)
- printf("%s<%.*s>",(n>0)?"/":"",p->data[n].length,p->data[n].data);
- printf("@<%.*s> (length %d, type %d)\n",p->realm.length,p->realm.data,
- p->length, p->type);
+ int cfull, sfull;
+ char *cp, *sp;
+
+ cp = c->tail;
+ sp = s->tail;
+ if (cp == NULL || sp == NULL)
+ return;
+ /*
+ * Is it a full component? Yes, if it's the beginning of the
+ * string or there's a separator to the left.
+ *
+ * The index of -1 is valid because it only gets evaluated if the
+ * pointer is not at the beginning of the string.
+ */
+ cfull = (cp == c->str || cp[-1] == sep);
+ sfull = (sp == s->str || sp[-1] == sep);
+ /*
+ * If they're both full components, we're done.
+ */
+ if (cfull && sfull) {
+ return;
+ } else if (c->dot != NULL && s->dot != NULL) {
+ cp = c->dot + 1;
+ sp = s->dot + 1;
+ /*
+ * Out of bounds? Can only happen if there are trailing dots.
+ */
+ if (cp >= &c->str[c->len] || sp >= &s->str[s->len]) {
+ cp = sp = NULL;
+ }
+ } else {
+ cp = sp = NULL;
+ }
+ c->tail = cp;
+ s->tail = sp;
+}
+
+/*
+ * Find common suffix of C and S.
+ *
+ * C->TAIL and S->TAIL will point to the respective suffixes. C->DOT
+ * and S->DOT will point to the nearest instances of SEP to the right
+ * of the start of each suffix. Caller must initialize TAIL and DOT
+ * pointers to null.
+ */
+static void
+comtail(struct hstate *c, struct hstate *s, int sep)
+{
+ char *cp, *sp, *cdot, *sdot;
+
+ if (c->len == 0 || s->len == 0)
+ return;
+
+ cdot = sdot = NULL;
+ /*
+ * ANSI/ISO C allows a pointer one past the end but not one
+ * before the beginning of an array.
+ */
+ cp = &c->str[c->len];
+ sp = &s->str[s->len];
+ /*
+ * Set CP and SP to point to the common suffix of each string.
+ * When we run into separators (dots, unless someone has a X.500
+ * style realm), keep pointers to the latest pair.
+ */
+ while (cp > c->str && sp > s->str) {
+ if (*--cp != *--sp) {
+ /*
+ * Didn't match, so most recent match is one byte to the
+ * right (or not at all).
+ */
+ cp++;
+ sp++;
+ break;
+ }
+ /*
+ * Keep track of matching dots.
+ */
+ if (*cp == sep) {
+ cdot = cp;
+ sdot = sp;
+ }
+ }
+ /* No match found at all. */
+ if (cp == &c->str[c->len])
+ return;
+ c->tail = cp;
+ s->tail = sp;
+ c->dot = cdot;
+ s->dot = sdot;
}
-#endif