Back to index

glibc  2.9
nis-service.c
Go to the documentation of this file.
00001 /* Copyright (C) 1996-2004, 2006 Free Software Foundation, Inc.
00002    This file is part of the GNU C Library.
00003    Contributed by Thorsten Kukuk <kukuk@suse.de>, 1996.
00004 
00005    The GNU C Library is free software; you can redistribute it and/or
00006    modify it under the terms of the GNU Lesser General Public
00007    License as published by the Free Software Foundation; either
00008    version 2.1 of the License, or (at your option) any later version.
00009 
00010    The GNU C Library is distributed in the hope that it will be useful,
00011    but WITHOUT ANY WARRANTY; without even the implied warranty of
00012    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
00013    Lesser General Public License for more details.
00014 
00015    You should have received a copy of the GNU Lesser General Public
00016    License along with the GNU C Library; if not, write to the Free
00017    Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
00018    02111-1307 USA.  */
00019 
00020 #include <nss.h>
00021 #include <netdb.h>
00022 #include <ctype.h>
00023 #include <errno.h>
00024 #include <string.h>
00025 #include <bits/libc-lock.h>
00026 #include <rpcsvc/yp.h>
00027 #include <rpcsvc/ypclnt.h>
00028 
00029 #include "nss-nis.h"
00030 #include <libnsl.h>
00031 
00032 
00033 /* Get the declaration of the parser function.  */
00034 #define ENTNAME servent
00035 #define EXTERN_PARSER
00036 #include <nss/nss_files/files-parse.c>
00037 
00038 __libc_lock_define_initialized (static, lock)
00039 
00040 static intern_t intern;
00041 
00042 struct search_t
00043 {
00044   const char *name;
00045   const char *proto;
00046   int port;
00047   enum nss_status status;
00048   struct servent *serv;
00049   char *buffer;
00050   size_t buflen;
00051   int *errnop;
00052 };
00053 
00054 static int
00055 dosearch (int instatus, char *inkey, int inkeylen, char *inval,
00056          int invallen, char *indata)
00057 {
00058   struct search_t *req = (struct search_t *) indata;
00059 
00060   if (__builtin_expect (instatus != YP_TRUE, 0))
00061     return 1;
00062 
00063   if (inkey && inkeylen > 0 && inval && invallen > 0)
00064     {
00065       if (__builtin_expect ((size_t) (invallen + 1) > req->buflen, 0))
00066        {
00067          *req->errnop = ERANGE;
00068          req->status = NSS_STATUS_TRYAGAIN;
00069          return 1;
00070        }
00071 
00072       char *p = strncpy (req->buffer, inval, invallen);
00073       req->buffer[invallen] = '\0';
00074       while (isspace (*p))
00075         ++p;
00076 
00077       int parse_res = _nss_files_parse_servent (p, req->serv,
00078                                           (void *) req->buffer,
00079                                           req->buflen, req->errnop);
00080       if (parse_res == -1)
00081        {
00082          req->status = NSS_STATUS_TRYAGAIN;
00083          return 1;
00084        }
00085 
00086       if (!parse_res)
00087         return 0;
00088 
00089       if (req->proto != NULL && strcmp (req->serv->s_proto, req->proto) != 0)
00090        return 0;
00091 
00092       if (req->port != -1 && req->serv->s_port != req->port)
00093        return 0;
00094 
00095       if (req->name != NULL && strcmp (req->serv->s_name, req->name) != 0)
00096        {
00097          char **cp;
00098          for (cp = req->serv->s_aliases; *cp; cp++)
00099            if (strcmp (req->name, *cp) == 0)
00100              break;
00101 
00102          if (*cp == NULL)
00103            return 0;
00104        }
00105 
00106       req->status = NSS_STATUS_SUCCESS;
00107       return 1;
00108     }
00109 
00110   return 0;
00111 }
00112 
00113 static void
00114 internal_nis_endservent (void)
00115 {
00116   struct response_t *curr = intern.next;
00117 
00118   while (curr != NULL)
00119     {
00120       struct response_t *last = curr;
00121       curr = curr->next;
00122       free (last);
00123     }
00124 
00125   intern.next = intern.start = NULL;
00126 }
00127 
00128 enum nss_status
00129 _nss_nis_endservent (void)
00130 {
00131   __libc_lock_lock (lock);
00132 
00133   internal_nis_endservent ();
00134 
00135   __libc_lock_unlock (lock);
00136 
00137   return NSS_STATUS_SUCCESS;
00138 }
00139 
00140 static enum nss_status
00141 internal_nis_setservent (void)
00142 {
00143   char *domainname;
00144   struct ypall_callback ypcb;
00145   enum nss_status status;
00146 
00147   if (yp_get_default_domain (&domainname))
00148     return NSS_STATUS_UNAVAIL;
00149 
00150   internal_nis_endservent ();
00151 
00152   ypcb.foreach = _nis_saveit;
00153   ypcb.data = (char *) &intern;
00154   status = yperr2nss (yp_all (domainname, "services.byname", &ypcb));
00155 
00156   /* Mark the last buffer as full.  */
00157   if (intern.next != NULL)
00158     intern.next->size = intern.offset;
00159 
00160   intern.next = intern.start;
00161   intern.offset = 0;
00162 
00163   return status;
00164 }
00165 
00166 enum nss_status
00167 _nss_nis_setservent (int stayopen)
00168 {
00169   enum nss_status status;
00170 
00171   __libc_lock_lock (lock);
00172 
00173   status = internal_nis_setservent ();
00174 
00175   __libc_lock_unlock (lock);
00176 
00177   return status;
00178 }
00179 
00180 static enum nss_status
00181 internal_nis_getservent_r (struct servent *serv, char *buffer,
00182                         size_t buflen, int *errnop)
00183 {
00184   struct parser_data *pdata = (void *) buffer;
00185   int parse_res;
00186   char *p;
00187 
00188   if (intern.start == NULL)
00189     internal_nis_setservent ();
00190 
00191   if (intern.next == NULL)
00192     /* Not one entry in the map.  */
00193     return NSS_STATUS_NOTFOUND;
00194 
00195   /* Get the next entry until we found a correct one.  */
00196   do
00197     {
00198       struct response_t *bucket = intern.next;
00199 
00200       if (__builtin_expect (intern.offset >= bucket->size, 0))
00201        {
00202          if (bucket->next == NULL)
00203            return NSS_STATUS_NOTFOUND;
00204 
00205          /* We look at all the content in the current bucket.  Go on
00206             to the next.  */
00207          bucket = intern.next = bucket->next;
00208          intern.offset = 0;
00209        }
00210 
00211       for (p = &bucket->mem[intern.offset]; isspace (*p); ++p)
00212         ++intern.offset;
00213 
00214       size_t len = strlen (p) + 1;
00215       if (__builtin_expect (len > buflen, 0))
00216        {
00217          *errnop = ERANGE;
00218          return NSS_STATUS_TRYAGAIN;
00219        }
00220 
00221       /* We unfortunately have to copy the data in the user-provided
00222         buffer because that buffer might be around for a very long
00223         time and the servent structure must remain valid.  If we would
00224         rely on the BUCKET memory the next 'setservent' or 'endservent'
00225         call would destroy it.
00226 
00227         The important thing is that it is a single NUL-terminated
00228         string.  This is what the parsing routine expects.  */
00229       p = memcpy (buffer, &bucket->mem[intern.offset], len);
00230 
00231       parse_res = _nss_files_parse_servent (p, serv, pdata, buflen, errnop);
00232       if (__builtin_expect (parse_res == -1, 0))
00233         return NSS_STATUS_TRYAGAIN;
00234 
00235       intern.offset += len;
00236     }
00237   while (!parse_res);
00238 
00239   return NSS_STATUS_SUCCESS;
00240 }
00241 
00242 enum nss_status
00243 _nss_nis_getservent_r (struct servent *serv, char *buffer, size_t buflen,
00244                      int *errnop)
00245 {
00246   enum nss_status status;
00247 
00248   __libc_lock_lock (lock);
00249 
00250   status = internal_nis_getservent_r (serv, buffer, buflen, errnop);
00251 
00252   __libc_lock_unlock (lock);
00253 
00254   return status;
00255 }
00256 
00257 enum nss_status
00258 _nss_nis_getservbyname_r (const char *name, const char *protocol,
00259                        struct servent *serv, char *buffer, size_t buflen,
00260                        int *errnop)
00261 {
00262   if (name == NULL)
00263     {
00264       *errnop = EINVAL;
00265       return NSS_STATUS_UNAVAIL;
00266     }
00267 
00268   char *domain;
00269   if (__builtin_expect (yp_get_default_domain (&domain), 0))
00270     return NSS_STATUS_UNAVAIL;
00271 
00272   /* If the protocol is given, we could try if our NIS server knows
00273      about services.byservicename map. If yes, we only need one query.  */
00274   size_t keylen = strlen (name) + (protocol ? 1 + strlen (protocol) : 0);
00275   char key[keylen + 1];
00276 
00277   /* key is: "name/proto" */
00278   char *cp = stpcpy (key, name);
00279   if (protocol != NULL)
00280     {
00281       *cp++ = '/';
00282       strcpy (cp, protocol);
00283     }
00284 
00285   char *result;
00286   int int_len;
00287   int status = yp_match (domain, "services.byservicename", key,
00288                       keylen, &result, &int_len);
00289   size_t len = int_len;
00290 
00291   /* If we found the key, it's ok and parse the result. If not,
00292      fall through and parse the complete table. */
00293   if (__builtin_expect (status == YPERR_SUCCESS, 1))
00294     {
00295       if (__builtin_expect ((size_t) (len + 1) > buflen, 0))
00296        {
00297          free (result);
00298          *errnop = ERANGE;
00299          return NSS_STATUS_TRYAGAIN;
00300        }
00301 
00302       char *p = strncpy (buffer, result, len);
00303       buffer[len] = '\0';
00304       while (isspace (*p))
00305        ++p;
00306       free (result);
00307 
00308       int parse_res = _nss_files_parse_servent (p, serv, (void *) buffer,
00309                                           buflen, errnop);
00310       if (__builtin_expect (parse_res < 0, 0))
00311        {
00312          if (parse_res == -1)
00313            return NSS_STATUS_TRYAGAIN;
00314          else
00315            return NSS_STATUS_NOTFOUND;
00316        }
00317       else
00318        return NSS_STATUS_SUCCESS;
00319     }
00320 
00321   /* Check if it is safe to rely on services.byservicename.  */
00322   if (_nsl_default_nss () & NSS_FLAG_SERVICES_AUTHORITATIVE)
00323     return yperr2nss (status);
00324 
00325   struct ypall_callback ypcb;
00326   struct search_t req;
00327 
00328   ypcb.foreach = dosearch;
00329   ypcb.data = (char *) &req;
00330   req.name = name;
00331   req.proto = protocol;
00332   req.port = -1;
00333   req.serv = serv;
00334   req.buffer = buffer;
00335   req.buflen = buflen;
00336   req.errnop = errnop;
00337   req.status = NSS_STATUS_NOTFOUND;
00338   status = yp_all (domain, "services.byname", &ypcb);
00339 
00340   if (__builtin_expect (status != YPERR_SUCCESS, 0))
00341     return yperr2nss (status);
00342 
00343   return req.status;
00344 }
00345 
00346 enum nss_status
00347 _nss_nis_getservbyport_r (int port, const char *protocol,
00348                        struct servent *serv, char *buffer,
00349                        size_t buflen, int *errnop)
00350 {
00351   char *domain;
00352   if (__builtin_expect (yp_get_default_domain (&domain), 0))
00353     return NSS_STATUS_UNAVAIL;
00354 
00355   /* If the protocol is given, we only need one query.
00356      Otherwise try first port/tcp, then port/udp and then fallback
00357      to sequential scanning of services.byname.  */
00358   const char *proto = protocol != NULL ? protocol : "tcp";
00359   do
00360     {
00361       /* key is: "port/proto" */
00362       char key[sizeof (int) * 3 + strlen (proto) + 2];
00363       size_t keylen = snprintf (key, sizeof (key), "%d/%s", ntohs (port),
00364                             proto);
00365 
00366       char *result;
00367       int int_len;
00368       int status = yp_match (domain, "services.byname", key, keylen, &result,
00369                           &int_len);
00370       size_t len = int_len;
00371 
00372       /* If we found the key, it's ok and parse the result. If not,
00373         fall through and parse the complete table. */
00374       if (__builtin_expect (status == YPERR_SUCCESS, 1))
00375        {
00376          if (__builtin_expect ((size_t) (len + 1) > buflen, 0))
00377            {
00378              free (result);
00379              *errnop = ERANGE;
00380              return NSS_STATUS_TRYAGAIN;
00381            }
00382 
00383          char  *p = strncpy (buffer, result, len);
00384          buffer[len] = '\0';
00385          while (isspace (*p))
00386            ++p;
00387          free (result);
00388          int parse_res = _nss_files_parse_servent (p, serv, (void *) buffer,
00389                                               buflen, errnop);
00390          if (__builtin_expect (parse_res < 0, 0))
00391            {
00392              if (parse_res == -1)
00393               return NSS_STATUS_TRYAGAIN;
00394              else
00395               return NSS_STATUS_NOTFOUND;
00396            }
00397 
00398          return NSS_STATUS_SUCCESS;
00399        }
00400     }
00401   while (protocol == NULL && (proto[0] == 't' ? (proto = "udp") : NULL));
00402 
00403   if (port == -1)
00404     return NSS_STATUS_NOTFOUND;
00405 
00406   struct ypall_callback ypcb;
00407   struct search_t req;
00408 
00409   ypcb.foreach = dosearch;
00410   ypcb.data = (char *) &req;
00411   req.name = NULL;
00412   req.proto = protocol;
00413   req.port = port;
00414   req.serv = serv;
00415   req.buffer = buffer;
00416   req.buflen = buflen;
00417   req.errnop = errnop;
00418   req.status = NSS_STATUS_NOTFOUND;
00419   int status = yp_all (domain, "services.byname", &ypcb);
00420 
00421   if (__builtin_expect (status != YPERR_SUCCESS, 0))
00422     return yperr2nss (status);
00423 
00424   return req.status;
00425 }