summary refs log tree commit diff
path: root/nis/nis_table.c
diff options
context:
space:
mode:
Diffstat (limited to 'nis/nis_table.c')
-rw-r--r--nis/nis_table.c399
1 files changed, 239 insertions, 160 deletions
diff --git a/nis/nis_table.c b/nis/nis_table.c
index ed4b3740dd..e0885ca024 100644
--- a/nis/nis_table.c
+++ b/nis/nis_table.c
@@ -110,6 +110,40 @@ __create_ib_request (const_nis_name name, u_long flags)
   return ibreq;
 }
 
+static struct timeval RPCTIMEOUT = {10, 0};
+
+static char *
+__get_tablepath (char *name, dir_binding *bptr)
+{
+  enum clnt_stat result;
+  nis_result *res = calloc (1, sizeof (nis_result));
+  struct ns_request req;
+
+  if (res == NULL)
+    return NULL;
+
+  req.ns_name = name;
+  req.ns_object.ns_object_len = 0;
+  req.ns_object.ns_object_val = NULL;
+
+  result = clnt_call (bptr->clnt, NIS_LOOKUP, (xdrproc_t) _xdr_ns_request,
+		      (caddr_t) &req, (xdrproc_t) _xdr_nis_result,
+		      (caddr_t) res, RPCTIMEOUT);
+
+  if (result == RPC_SUCCESS && NIS_RES_STATUS (res) == NIS_SUCCESS &&
+      __type_of (NIS_RES_OBJECT (res)) == NIS_TABLE_OBJ)
+    {
+      char *cptr = strdup (NIS_RES_OBJECT (res)->TA_data.ta_path);
+      nis_freeresult (res);
+      return cptr;
+    }
+  else
+    {
+      nis_freeresult (res);
+      return strdup ("");
+    }
+}
+
 nis_result *
 nis_list (const_nis_name name, u_long flags,
 	  int (*callback) (const_nis_name name,
@@ -120,12 +154,16 @@ nis_list (const_nis_name name, u_long flags,
   nis_result *res = NULL;
   ib_request *ibreq;
   int status;
+  enum clnt_stat clnt_status;
   int count_links = 0;		/* We will only follow NIS_MAXLINKS links! */
   int done = 0;
   nis_name *names;
   nis_name namebuf[2] = {NULL, NULL};
   int name_nr = 0;
   nis_cb *cb = NULL;
+  char *tableptr, *tablepath = NULL;
+  int have_tablepath = 0;
+  int first_try = 0; /* Do we try the old binding at first ? */
 
   res = calloc (1, sizeof (nis_result));
   if (res == NULL)
@@ -164,189 +202,230 @@ nis_list (const_nis_name name, u_long flags,
 
   cb = NULL;
 
-  if (flags & FOLLOW_PATH || flags & ALL_RESULTS)
+  while (!done)
     {
-      nis_result *lres;
-      u_long newflags = flags & ~FOLLOW_PATH & ~ALL_RESULTS;
-      char table_path[NIS_MAXPATH + 3];
-      char *ntable, *p;
-      u_long done = 0, failures = 0;
-
-      while (names[name_nr] != NULL && !done)
-	{
-	  lres = nis_lookup (names[name_nr], newflags | NO_AUTHINFO);
-	  if (lres == NULL || NIS_RES_STATUS (lres) != NIS_SUCCESS)
-	    {
-	      NIS_RES_STATUS (res) = NIS_RES_STATUS (lres);
-	      nis_freeresult (lres);
-	      ++name_nr;
-	      continue;
-	    }
-
-	  /* nis_lookup handles FOLLOW_LINKS,
-	     so we must have a table object.*/
-	  if (__type_of (NIS_RES_OBJECT (lres)) != NIS_TABLE_OBJ)
-	    {
-	      nis_freeresult (lres);
-	      NIS_RES_STATUS (res) = NIS_INVALIDOBJ;
-	      break;
-	    }
+      dir_binding bptr;
+      directory_obj *dir = NULL;
 
-	  /* Save the path, discard everything else.  */
-	  p = __stpncpy (table_path, names[name_nr], NIS_MAXPATH);
-	  *p++ = ':';
-	  p = __stpncpy (p, NIS_RES_OBJECT (lres)->TA_data.ta_path,
-			 NIS_MAXPATH - (p - table_path));
-	  *p = '\0';
-	  nis_freeresult (lres);
-	  free (res);
-	  res = NULL;
+      memset (res, '\0', sizeof (nis_result));
 
-	  p = table_path;
+      status = __nisfind_server (ibreq->ibr_name, &dir);
+      if (status != NIS_SUCCESS)
+        {
+          NIS_RES_STATUS (res) = status;
+          return res;
+        }
 
-	  while (((ntable = strsep (&p, ":")) != NULL) && !done)
-	    {
-	      char *c;
+      status = __nisbind_create (&bptr, dir->do_servers.do_servers_val,
+                                 dir->do_servers.do_servers_len, flags);
+      if (status != NIS_SUCCESS)
+        {
+          NIS_RES_STATUS (res) = status;
+          nis_free_directory (dir);
+          return res;
+        }
 
-	      if (res != NULL)
-		nis_freeresult (res);
+      while (__nisbind_connect (&bptr) != NIS_SUCCESS)
+	if (__nisbind_next (&bptr) != NIS_SUCCESS)
+	  {
+	    __nisbind_destroy (&bptr);
+	    nis_free_directory (dir);
+	    NIS_RES_STATUS (res) = NIS_NAMEUNREACHABLE;
+	    return res;
+	  }
 
-	      /* Do the job recursive here!  */
-	      if ((c = strchr(name, ']')) != NULL)
-		{
-		  /* Have indexed name ! */
-		  int index_len = c - name + 2;
-		  char buf[index_len + strlen (ntable) + 1];
-
-		  c = __stpncpy (buf, name, index_len);
-		  strcpy (c, ntable);
-		  res = nis_list (buf, newflags, callback,userdata);
-		}
-	      else
-		res = nis_list (ntable, newflags, callback, userdata);
-	      if (res == NULL)
-		return NULL;
-	      switch (NIS_RES_STATUS (res))
-		{
-		case NIS_SUCCESS:
-		case NIS_CBRESULTS:
-		  if (!(flags & ALL_RESULTS))
-		    done = 1;
-		  break;
-		case NIS_PARTIAL: /* The table is correct, we doesn't found
-				     the entry */
-		  break;
-		default:
-		  if (flags & ALL_RESULTS)
-		    ++failures;
-		  else
-		    done = 1;
-		  break;
-		}
-	    }
-	  if (NIS_RES_STATUS (res) == NIS_SUCCESS && failures)
-	    NIS_RES_STATUS (res) = NIS_S_SUCCESS;
-	  if (NIS_RES_STATUS (res) == NIS_NOTFOUND && failures)
-	    NIS_RES_STATUS (res) = NIS_S_NOTFOUND;
-	  break;
-	}
-    }
-  else
-    {
       if (callback != NULL)
 	{
 	  cb = __nis_create_callback (callback, userdata, flags);
 	  ibreq->ibr_cbhost.ibr_cbhost_len = 1;
 	  ibreq->ibr_cbhost.ibr_cbhost_val = cb->serv;
-	  }
-
-      while (!done)
-	{
-	  memset (res, '\0', sizeof (nis_result));
-
-	  status = __do_niscall (ibreq->ibr_name, NIS_IBLIST,
-				 (xdrproc_t) _xdr_ib_request,
-				 (caddr_t) ibreq, (xdrproc_t) _xdr_nis_result,
-				 (caddr_t) res, flags, cb);
-	  if (status != NIS_SUCCESS)
-	    NIS_RES_STATUS (res) = status;
+	}
 
-	  switch (NIS_RES_STATUS (res))
-	    {
-	    case NIS_PARTIAL:
-	    case NIS_SUCCESS:
-	    case NIS_S_SUCCESS:
-	      if (__type_of (NIS_RES_OBJECT (res)) == NIS_LINK_OBJ &&
-		  flags & FOLLOW_LINKS)		/* We are following links.  */
-		{
-		  /* If we hit the link limit, bail.  */
-		  if (count_links > NIS_MAXLINKS)
+    again:
+      clnt_status = clnt_call (bptr.clnt, NIS_IBLIST,
+			       (xdrproc_t) _xdr_ib_request, (caddr_t) ibreq,
+			       (xdrproc_t) _xdr_nis_result,
+			       (caddr_t) res, RPCTIMEOUT);
+
+      if (clnt_status != RPC_SUCCESS)
+	NIS_RES_STATUS (res) = NIS_RPCERROR;
+      else
+	switch (NIS_RES_STATUS (res))
+	  { /* start switch */
+	  case NIS_PARTIAL:
+	  case NIS_SUCCESS:
+	  case NIS_S_SUCCESS:
+	    if (__type_of (NIS_RES_OBJECT (res)) == NIS_LINK_OBJ &&
+		flags & FOLLOW_LINKS)		/* We are following links.  */
+	      {
+		free (ibreq->ibr_name);
+		/* If we hit the link limit, bail.  */
+		if (count_links > NIS_MAXLINKS)
+		  {
+		    NIS_RES_STATUS (res) = NIS_LINKNAMEERROR;
+		    ++done;
+		    break;
+		  }
+		++count_links;
+		ibreq->ibr_name =
+		  strdup (NIS_RES_OBJECT (res)->LI_data.li_name);
+		if (NIS_RES_OBJECT (res)->LI_data.li_attrs.li_attrs_len)
+		  if (ibreq->ibr_srch.ibr_srch_len == 0)
 		    {
-		      NIS_RES_STATUS (res) = NIS_LINKNAMEERROR;
-		      ++done;
-		      break;
+		      ibreq->ibr_srch.ibr_srch_len =
+			NIS_RES_OBJECT (res)->LI_data.li_attrs.li_attrs_len;
+		      ibreq->ibr_srch.ibr_srch_val =
+			NIS_RES_OBJECT (res)->LI_data.li_attrs.li_attrs_val;
 		    }
-		  if (count_links)
-		    free (ibreq->ibr_name);
-		  ++count_links;
-		  free (ibreq->ibr_name);
-		  ibreq->ibr_name =
-		    strdup (NIS_RES_OBJECT (res)->LI_data.li_name);
-		  if (NIS_RES_OBJECT (res)->LI_data.li_attrs.li_attrs_len)
-		    if (ibreq->ibr_srch.ibr_srch_len == 0)
+		nis_freeresult (res);
+		res = calloc (1, sizeof (nis_result));
+		if (res == NULL)
+		  {
+		    if (have_tablepath)
+		      free (tablepath);
+		    __nisbind_destroy (&bptr);
+		    nis_free_directory (dir);
+		    return NULL;
+		  }
+		first_try = 1; /* Try at first the old binding */
+		goto again;
+	      }
+	    else if ((flags & FOLLOW_PATH) &&
+		     NIS_RES_STATUS (res) == NIS_PARTIAL)
+	      {
+		if (!have_tablepath)
+		  {
+		    tablepath = __get_tablepath (ibreq->ibr_name, &bptr);
+		    tableptr = tablepath;
+		    have_tablepath = 1;
+		  }
+		if (tableptr == NULL)
+		  {
+		    ++done;
+		    break;
+		  }
+		free (ibreq->ibr_name);
+		ibreq->ibr_name = strsep (&tableptr, ":");
+		if (ibreq->ibr_name == NULL || ibreq->ibr_name[0] == '\0')
+		  {
+		    ibreq->ibr_name = strdup ("");
+		    ++done;
+		  }
+		else
+		  {
+		    ibreq->ibr_name = strdup (ibreq->ibr_name);
+		    nis_freeresult (res);
+		    res = calloc (1, sizeof (nis_result));
+		    if (res == NULL)
 		      {
-			ibreq->ibr_srch.ibr_srch_len =
-			  NIS_RES_OBJECT (res)->LI_data.li_attrs.li_attrs_len;
-			ibreq->ibr_srch.ibr_srch_val =
-			  NIS_RES_OBJECT (res)->LI_data.li_attrs.li_attrs_val;
+			if (have_tablepath)
+			  free (tablepath);
+			__nisbind_destroy (&bptr);
+			nis_free_directory (dir);
+			return NULL;
 		      }
-		  nis_freeresult (res);
-		  res = calloc (1, sizeof (nis_result));
-		}
-	      else
-		++done;
-	      break;
-	    case NIS_CBRESULTS:
-	      /* Calback is handled in nis_call.c (__do_niscall2),
-		 but we have to change the error code */
-	      NIS_RES_STATUS (res) = cb->result;
+		    first_try = 1;
+		    goto again;
+		  }
+	      }
+	    else
 	      ++done;
-	      break;
-	    case NIS_UNAVAIL:
-	      /* NIS+ is not installed, or all servers are down.  */
-	      ++done;
-	      break;
-	    default:
-	      /* Try the next domainname if we don't follow a link.  */
-	      if (count_links)
-		{
-		  free (ibreq->ibr_name);
-		  NIS_RES_STATUS (res) = NIS_LINKNAMEERROR;
-		  ++done;
-		  break;
-		}
-	      ++name_nr;
-	      if (names[name_nr] == NULL)
-		{
+	    break;
+	  case NIS_CBRESULTS:
+	    if (cb != NULL)
+	      {
+		__nis_do_callback (&bptr, &res->cookie, cb);
+		NIS_RES_STATUS (res) = cb->result;
+
+		if (!(flags & ALL_RESULTS))
 		  ++done;
-		  break;
-		}
-	      ibreq->ibr_name = names[name_nr];
-	      break;
-	    }
+		else
+		  {
+		    if (!have_tablepath)
+		      {
+			tablepath = __get_tablepath (ibreq->ibr_name, &bptr);
+			tableptr = tablepath;
+			have_tablepath = 1;
+		      }
+		    if (tableptr == NULL)
+		      {
+			++done;
+			break;
+		      }
+		    free (ibreq->ibr_name);
+		    ibreq->ibr_name = strsep (&tableptr, ":");
+		    if (ibreq->ibr_name == NULL || ibreq->ibr_name[0] == '\0')
+		      {
+			ibreq->ibr_name = strdup ("");
+			++done;
+		      }
+		    else
+		      ibreq->ibr_name = strdup (ibreq->ibr_name);
+		  }
+	      }
+	    break;
+	  case NIS_SYSTEMERROR:
+	  case NIS_NOSUCHNAME:
+	  case NIS_NOT_ME:
+	    /* If we had first tried the old binding, do nothing, but
+	       get a new binding */
+	    if (!first_try)
+	      {
+		if (__nisbind_next (&bptr) != NIS_SUCCESS)
+		  {
+		    ++done;
+		    break; /* No more servers to search */
+		  }
+		while (__nisbind_connect (&bptr) != NIS_SUCCESS)
+		  {
+		    if (__nisbind_next (&bptr) != NIS_SUCCESS)
+		      {
+			++done;
+			break; /* No more servers to search */
+		      }
+		  }
+		goto again;
+	      }
+	    break;
+	  default:
+	    if (!first_try)
+	      {
+		/* Try the next domainname if we don't follow a link.  */
+		if (count_links)
+		  {
+		    free (ibreq->ibr_name);
+		    NIS_RES_STATUS (res) = NIS_LINKNAMEERROR;
+		    ++done;
+		    break;
+		  }
+		++name_nr;
+		if (names[name_nr] == NULL)
+		  {
+		    ++done;
+		    break;
+		  }
+		ibreq->ibr_name = names[name_nr];
+		first_try = 1; /* Try old binding at first */
+		goto again;
+	      }
+	    break;
+	  }
+      first_try = 0;
+
+      if (cb)
+	{
+	  __nis_destroy_callback (cb);
+	  ibreq->ibr_cbhost.ibr_cbhost_len = 0;
+	  ibreq->ibr_cbhost.ibr_cbhost_val = NULL;
 	}
-    }				/* End of not FOLLOW_PATH.  */
+
+      __nisbind_destroy (&bptr);
+      nis_free_directory (dir);
+    }
 
   if (names != namebuf)
     nis_freenames (names);
 
-  if (cb)
-    {
-      __nis_destroy_callback (cb);
-      ibreq->ibr_cbhost.ibr_cbhost_len = 0;
-      ibreq->ibr_cbhost.ibr_cbhost_val = NULL;
-    }
-
   nis_free_request (ibreq);
 
   return res;