Remove a bunch of unnecessary semicolons.
[dragonfly.git] / lib / libc / net / getservent.c
1 /*
2  * Copyright (c) 1983, 1993
3  *      The Regents of the University of California.  All rights reserved.
4  *
5  * Redistribution and use in source and binary forms, with or without
6  * modification, are permitted provided that the following conditions
7  * are met:
8  * 1. Redistributions of source code must retain the above copyright
9  *    notice, this list of conditions and the following disclaimer.
10  * 2. Redistributions in binary form must reproduce the above copyright
11  *    notice, this list of conditions and the following disclaimer in the
12  *    documentation and/or other materials provided with the distribution.
13  * 3. Neither the name of the University nor the names of its contributors
14  *    may be used to endorse or promote products derived from this software
15  *    without specific prior written permission.
16  *
17  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
18  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
19  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
20  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
21  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
22  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
23  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
24  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
25  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
26  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
27  * SUCH DAMAGE.
28  *
29  * @(#)getservent.c     8.1 (Berkeley) 6/4/93
30  * $FreeBSD: src/lib/libc/net/getservent.c,v 1.23 2007/01/09 00:28:02 imp Exp $
31  */
32
33 #include <sys/param.h>
34 #include <sys/types.h>
35 #include <sys/socket.h>
36 #include <arpa/inet.h>
37 #include <errno.h>
38 #include <limits.h>
39 #include <netdb.h>
40 #include <nsswitch.h>
41 #include <stdio.h>
42 #include <string.h>
43 #include <stdlib.h>
44 #include <unistd.h>
45 #ifdef YP
46 #include <rpc/rpc.h>
47 #include <rpcsvc/yp_prot.h>
48 #include <rpcsvc/ypclnt.h>
49 #endif
50 #include "namespace.h"
51 #include "reentrant.h"
52 #include "un-namespace.h"
53 #include "netdb_private.h"
54 #ifdef NS_CACHING
55 #include "nscache.h"
56 #endif
57 #include "nss_tls.h"
58
59 enum constants
60 {
61         SETSERVENT              = 1,
62         ENDSERVENT              = 2,
63         SERVENT_STORAGE_INITIAL = 1 << 10, /* 1 KByte */
64         SERVENT_STORAGE_MAX     = 1 << 20, /* 1 MByte */
65 };
66
67 struct servent_mdata
68 {
69         enum nss_lookup_type how;
70         int compat_mode;
71 };
72
73 static const ns_src defaultsrc[] = {
74         { NSSRC_COMPAT, NS_SUCCESS },
75         { NULL, 0 }
76 };
77
78 static int servent_unpack(char *, struct servent *, char **, size_t, int *);
79
80 /* files backend declarations */
81 struct files_state
82 {
83         FILE *fp;
84         int stayopen;
85
86         int compat_mode_active;
87 };
88 static void files_endstate(void *);
89 NSS_TLS_HANDLING(files);
90
91 static int files_servent(void *, void *, va_list);
92 static int files_setservent(void *, void *, va_list);
93
94 #ifdef YP
95 /* nis backend declarations */
96 static  int     nis_servent(void *, void *, va_list);
97 static  int     nis_setservent(void *, void *, va_list);
98
99 struct nis_state
100 {
101         int yp_stepping;
102         char yp_domain[MAXHOSTNAMELEN];
103         char *yp_key;
104         int yp_keylen;
105 };
106 static void nis_endstate(void *);
107 NSS_TLS_HANDLING(nis);
108
109 static int nis_servent(void *, void *, va_list);
110 static int nis_setservent(void *, void *, va_list);
111 #endif
112
113 /* compat backend declarations */
114 static int compat_setservent(void *, void *, va_list);
115
116 /* get** wrappers for get**_r functions declarations */
117 struct servent_state {
118         struct servent serv;
119         char *buffer;
120         size_t bufsize;
121 };
122 static  void    servent_endstate(void *);
123 NSS_TLS_HANDLING(servent);
124
125 struct key {
126         const char *proto;
127         union {
128                 const char *name;
129                 int port;
130         };
131 };
132
133 static int wrap_getservbyname_r(struct key, struct servent *, char *, size_t,
134     struct servent **);
135 static int wrap_getservbyport_r(struct key, struct servent *, char *, size_t,
136     struct servent **);
137 static int wrap_getservent_r(struct key, struct servent *, char *, size_t,
138     struct servent **);
139 static struct servent *getserv(int (*fn)(struct key, struct servent *, char *,
140     size_t, struct servent **), struct key);
141
142 #ifdef NS_CACHING
143 static int serv_id_func(char *, size_t *, va_list, void *);
144 static int serv_marshal_func(char *, size_t *, void *, va_list, void *);
145 static int serv_unmarshal_func(char *, size_t, void *, va_list, void *);
146 #endif
147
148 static int
149 servent_unpack(char *p, struct servent *serv, char **aliases,
150     size_t aliases_size, int *errnop)
151 {
152         char *cp, **q, *endp;
153         long l;
154
155         if (*p == '#')
156                 return -1;
157
158         memset(serv, 0, sizeof(struct servent));
159
160         cp = strpbrk(p, "#\n");
161         if (cp != NULL)
162                 *cp = '\0';
163         serv->s_name = p;
164
165         p = strpbrk(p, " \t");
166         if (p == NULL)
167                 return -1;
168         *p++ = '\0';
169         while (*p == ' ' || *p == '\t')
170                 p++;
171         cp = strpbrk(p, ",/");
172         if (cp == NULL)
173                 return -1;
174
175         *cp++ = '\0';
176         l = strtol(p, &endp, 10);
177         if (endp == p || *endp != '\0' || l < 0 || l > USHRT_MAX)
178                 return -1;
179         serv->s_port = htons((in_port_t)l);
180         serv->s_proto = cp;
181
182         q = serv->s_aliases = aliases;
183         cp = strpbrk(cp, " \t");
184         if (cp != NULL)
185                 *cp++ = '\0';
186         while (cp && *cp) {
187                 if (*cp == ' ' || *cp == '\t') {
188                         cp++;
189                         continue;
190                 }
191                 if (q < &aliases[aliases_size - 1]) {
192                         *q++ = cp;
193                 } else {
194                         *q = NULL;
195                         *errnop = ERANGE;
196                         return -1;
197                 }
198                 cp = strpbrk(cp, " \t");
199                 if (cp != NULL)
200                         *cp++ = '\0';
201         }
202         *q = NULL;
203
204         return 0;
205 }
206
207 /* files backend implementation */
208 static  void
209 files_endstate(void *p)
210 {
211         FILE * f;
212
213         if (p == NULL)
214                 return;
215
216         f = ((struct files_state *)p)->fp;
217         if (f != NULL)
218                 fclose(f);
219
220         free(p);
221 }
222
223 /*
224  * compat structures. compat and files sources functionalities are almost
225  * equal, so they all are managed by files_servent function
226  */
227 static int
228 files_servent(void *retval, void *mdata, va_list ap)
229 {
230         static const ns_src compat_src[] = {
231 #ifdef YP
232                 { NSSRC_NIS, NS_SUCCESS },
233 #endif
234                 { NULL, 0 }
235         };
236         ns_dtab compat_dtab[] = {
237 #ifdef YP
238                 { NSSRC_NIS, nis_servent,
239                         (void *)((struct servent_mdata *)mdata)->how },
240 #endif
241                 { NULL, NULL, NULL }
242         };
243
244         struct files_state *st;
245         int rv;
246         int stayopen;
247
248         struct servent_mdata *serv_mdata;
249         char *name;
250         char *proto;
251         int port;
252
253         struct servent *serv;
254         char *buffer;
255         size_t bufsize;
256         int *errnop;
257
258         char **aliases;
259         int aliases_size;
260         size_t linesize;
261         char *line;
262         char **cp;
263
264         name = NULL;
265         proto = NULL;
266         serv_mdata = (struct servent_mdata *)mdata;
267         switch (serv_mdata->how) {
268         case nss_lt_name:
269                 name = va_arg(ap, char *);
270                 proto = va_arg(ap, char *);
271                 break;
272         case nss_lt_id:
273                 port = va_arg(ap, int);
274                 proto = va_arg(ap, char *);
275                 break;
276         case nss_lt_all:
277                 break;
278         default:
279                 return NS_NOTFOUND;
280         }
281
282         serv = va_arg(ap, struct servent *);
283         buffer  = va_arg(ap, char *);
284         bufsize = va_arg(ap, size_t);
285         errnop = va_arg(ap,int *);
286
287         *errnop = files_getstate(&st);
288         if (*errnop != 0)
289                 return (NS_UNAVAIL);
290
291         if (st->fp == NULL)
292                 st->compat_mode_active = 0;
293
294         if (st->fp == NULL && (st->fp = fopen(_PATH_SERVICES, "r")) == NULL) {
295                 *errnop = errno;
296                 return (NS_UNAVAIL);
297         }
298
299         if (serv_mdata->how == nss_lt_all)
300                 stayopen = 1;
301         else {
302                 rewind(st->fp);
303                 stayopen = st->stayopen;
304         }
305
306         rv = NS_NOTFOUND;
307         do {
308                 if (!st->compat_mode_active) {
309                         if ((line = fgetln(st->fp, &linesize)) == NULL) {
310                                 *errnop = errno;
311                                 rv = NS_RETURN;
312                                 break;
313                         }
314
315                         if (*line=='+') {
316                                 if (serv_mdata->compat_mode != 0)
317                                         st->compat_mode_active = 1;
318                         } else {
319                                 if (bufsize <= linesize + _ALIGNBYTES +
320                                     sizeof(char *)) {
321                                         *errnop = ERANGE;
322                                         rv = NS_RETURN;
323                                         break;
324                                 }
325                                 aliases = (char **)_ALIGN(&buffer[linesize+1]);
326                                 aliases_size = (buffer + bufsize -
327                                     (char *)aliases) / sizeof(char *);
328                                 if (aliases_size < 1) {
329                                         *errnop = ERANGE;
330                                         rv = NS_RETURN;
331                                         break;
332                                 }
333
334                                 memcpy(buffer, line, linesize);
335                                 buffer[linesize] = '\0';
336                         }
337                 }
338
339                 if (st->compat_mode_active != 0) {
340                         switch (serv_mdata->how) {
341                         case nss_lt_name:
342                                 rv = nsdispatch(retval, compat_dtab,
343                                     NSDB_SERVICES_COMPAT, "getservbyname_r",
344                                     compat_src, name, proto, serv, buffer,
345                                     bufsize, errnop);
346                                 break;
347                         case nss_lt_id:
348                                 rv = nsdispatch(retval, compat_dtab,
349                                     NSDB_SERVICES_COMPAT, "getservbyport_r",
350                                     compat_src, port, proto, serv, buffer,
351                                         bufsize, errnop);
352                                 break;
353                         case nss_lt_all:
354                                 rv = nsdispatch(retval, compat_dtab,
355                                     NSDB_SERVICES_COMPAT, "getservent_r",
356                                     compat_src, serv, buffer, bufsize, errnop);
357                                 break;
358                         }
359
360                         if (!(rv & NS_TERMINATE) ||
361                             serv_mdata->how != nss_lt_all)
362                                 st->compat_mode_active = 0;
363
364                         continue;
365                 }
366
367                 rv = servent_unpack(buffer, serv, aliases, aliases_size,
368                     errnop);
369                 if (rv !=0 ) {
370                         if (*errnop == 0) {
371                                 rv = NS_NOTFOUND;
372                                 continue;
373                         }
374                         else {
375                                 rv = NS_RETURN;
376                                 break;
377                         }
378                 }
379
380                 rv = NS_NOTFOUND;
381                 switch (serv_mdata->how) {
382                 case nss_lt_name:
383                         if (strcmp(name, serv->s_name) == 0)
384                                 goto gotname;
385                         for (cp = serv->s_aliases; *cp; cp++)
386                                 if (strcmp(name, *cp) == 0)
387                                         goto gotname;
388
389                         continue;
390                 gotname:
391                         if (proto == NULL || strcmp(serv->s_proto, proto) == 0)
392                                 rv = NS_SUCCESS;
393                         break;
394                 case nss_lt_id:
395                         if (port != serv->s_port)
396                                 continue;
397
398                         if (proto == NULL || strcmp(serv->s_proto, proto) == 0)
399                                 rv = NS_SUCCESS;
400                         break;
401                 case nss_lt_all:
402                         rv = NS_SUCCESS;
403                         break;
404                 }
405
406         } while (!(rv & NS_TERMINATE));
407
408         if (!stayopen && st->fp != NULL) {
409                 fclose(st->fp);
410                 st->fp = NULL;
411         }
412
413         if ((rv == NS_SUCCESS) && (retval != NULL))
414                 *(struct servent **)retval=serv;
415
416         return (rv);
417 }
418
419 static int
420 files_setservent(void *retval __unused, void *mdata, va_list ap)
421 {
422         struct files_state *st;
423         int rv;
424         int f;
425
426         rv = files_getstate(&st);
427         if (rv != 0)
428                 return (NS_UNAVAIL);
429
430         switch ((enum constants)mdata) {
431         case SETSERVENT:
432                 f = va_arg(ap,int);
433                 if (st->fp == NULL)
434                         st->fp = fopen(_PATH_SERVICES, "r");
435                 else
436                         rewind(st->fp);
437                 st->stayopen |= f;
438                 break;
439         case ENDSERVENT:
440                 if (st->fp != NULL) {
441                         fclose(st->fp);
442                         st->fp = NULL;
443                 }
444                 st->stayopen = 0;
445                 break;
446         default:
447                 break;
448         }
449
450         st->compat_mode_active = 0;
451         return (NS_UNAVAIL);
452 }
453
454 /* nis backend implementation */
455 #ifdef YP
456 static  void
457 nis_endstate(void *p)
458 {
459         if (p == NULL)
460                 return;
461
462         free(((struct nis_state *)p)->yp_key);
463         free(p);
464 }
465
466 static int
467 nis_servent(void *retval, void *mdata, va_list ap)
468 {
469         char *resultbuf, *lastkey;
470         int resultbuflen;
471         char buf[YPMAXRECORD + 2];
472
473         struct nis_state *st;
474         int rv;
475
476         enum nss_lookup_type how;
477         char *name;
478         char *proto;
479         int port;
480
481         struct servent *serv;
482         char *buffer;
483         size_t bufsize;
484         int *errnop;
485
486         char **aliases;
487         int aliases_size;
488
489         name = NULL;
490         proto = NULL;
491         how = (enum nss_lookup_type)mdata;
492         switch (how) {
493         case nss_lt_name:
494                 name = va_arg(ap, char *);
495                 proto = va_arg(ap, char *);
496                 break;
497         case nss_lt_id:
498                 port = va_arg(ap, int);
499                 proto = va_arg(ap, char *);
500                 break;
501         case nss_lt_all:
502                 break;
503         default:
504                 return NS_NOTFOUND;
505         }
506
507         serv = va_arg(ap, struct servent *);
508         buffer  = va_arg(ap, char *);
509         bufsize = va_arg(ap, size_t);
510         errnop = va_arg(ap, int *);
511
512         *errnop = nis_getstate(&st);
513         if (*errnop != 0)
514                 return (NS_UNAVAIL);
515
516         if (st->yp_domain[0] == '\0') {
517                 if (getdomainname(st->yp_domain, sizeof st->yp_domain)) {
518                         *errnop = errno;
519                         return (NS_UNAVAIL);
520                 }
521         }
522
523         do {
524                 switch (how) {
525                 case nss_lt_name:
526                         snprintf(buf, sizeof(buf), "%s/%s", name, proto);
527                         if (yp_match(st->yp_domain, "services.byname", buf,
528                             strlen(buf), &resultbuf, &resultbuflen)) {
529                                 rv = NS_NOTFOUND;
530                                 goto fin;
531                         }
532                         break;
533                 case nss_lt_id:
534                         snprintf(buf, sizeof(buf), "%d/%s", ntohs(port),
535                             proto);
536
537                         /*
538                          * We have to be a little flexible
539                          * here. Ideally you're supposed to have both
540                          * a services.byname and a services.byport
541                          * map, but some systems have only
542                          * services.byname. FreeBSD cheats a little by
543                          * putting the services.byport information in
544                          * the same map as services.byname so that
545                          * either case will work. We allow for both
546                          * possibilities here: if there is no
547                          * services.byport map, we try services.byname
548                          * instead.
549                          */
550                         rv = yp_match(st->yp_domain, "services.byport", buf,
551                             strlen(buf), &resultbuf, &resultbuflen);
552                         if (rv) {
553                                 if (rv == YPERR_MAP) {
554                                         if (yp_match(st->yp_domain,
555                                             "services.byname", buf,
556                                             strlen(buf), &resultbuf,
557                                             &resultbuflen)) {
558                                                 rv = NS_NOTFOUND;
559                                                 goto fin;
560                                         }
561                                 } else {
562                                         rv = NS_NOTFOUND;
563                                         goto fin;
564                                 }
565                         }
566
567                         break;
568                 case nss_lt_all:
569                         if (!st->yp_stepping) {
570                                 free(st->yp_key);
571                                 rv = yp_first(st->yp_domain, "services.byname",
572                                     &st->yp_key, &st->yp_keylen, &resultbuf,
573                                     &resultbuflen);
574                                 if (rv) {
575                                         rv = NS_NOTFOUND;
576                                         goto fin;
577                                 }
578                                 st->yp_stepping = 1;
579                         } else {
580                                 lastkey = st->yp_key;
581                                 rv = yp_next(st->yp_domain, "services.byname",
582                                     st->yp_key, st->yp_keylen, &st->yp_key,
583                                     &st->yp_keylen, &resultbuf, &resultbuflen);
584                                 free(lastkey);
585                                 if (rv) {
586                                         st->yp_stepping = 0;
587                                         rv = NS_NOTFOUND;
588                                         goto fin;
589                                 }
590                         }
591                         break;
592                 }
593
594                 /* we need a room for additional \n symbol */
595                 if (bufsize <=
596                     resultbuflen + 1 + _ALIGNBYTES + sizeof(char *)) {
597                         *errnop = ERANGE;
598                         rv = NS_RETURN;
599                         break;
600                 }
601
602                 aliases = (char **)_ALIGN(&buffer[resultbuflen + 2]);
603                 aliases_size =
604                     (buffer + bufsize - (char *)aliases) / sizeof(char *);
605                 if (aliases_size < 1) {
606                         *errnop = ERANGE;
607                         rv = NS_RETURN;
608                         break;
609                 }
610
611                 /*
612                  * servent_unpack expects lines terminated with \n --
613                  * make it happy
614                  */
615                 memcpy(buffer, resultbuf, resultbuflen);
616                 buffer[resultbuflen] = '\n';
617                 buffer[resultbuflen + 1] = '\0';
618
619                 if (servent_unpack(buffer, serv, aliases, aliases_size,
620                     errnop) != 0) {
621                         if (*errnop == 0)
622                                 rv = NS_NOTFOUND;
623                         else
624                                 rv = NS_RETURN;
625                 } else
626                         rv = NS_SUCCESS;
627                 free(resultbuf);
628
629         } while (!(rv & NS_TERMINATE) && how == nss_lt_all);
630
631 fin:
632         if (rv == NS_SUCCESS && retval != NULL)
633                 *(struct servent **)retval = serv;
634
635         return (rv);
636 }
637
638 static int
639 nis_setservent(void *result __unused, void *mdata, va_list ap __unused)
640 {
641         struct nis_state *st;
642         int rv;
643
644         rv = nis_getstate(&st);
645         if (rv != 0)
646                 return (NS_UNAVAIL);
647
648         switch ((enum constants)mdata) {
649         case SETSERVENT:
650         case ENDSERVENT:
651                 free(st->yp_key);
652                 st->yp_key = NULL;
653                 st->yp_stepping = 0;
654                 break;
655         default:
656                 break;
657         }
658
659         return (NS_UNAVAIL);
660 }
661 #endif
662
663 /* compat backend implementation */
664 static int
665 compat_setservent(void *retval, void *mdata, va_list ap)
666 {
667         static const ns_src compat_src[] = {
668 #ifdef YP
669                 { NSSRC_NIS, NS_SUCCESS },
670 #endif
671                 { NULL, 0 }
672         };
673         ns_dtab compat_dtab[] = {
674 #ifdef YP
675                 { NSSRC_NIS, nis_setservent, mdata },
676 #endif
677                 { NULL, NULL, NULL }
678         };
679         int f;
680
681         files_setservent(retval, mdata, ap);
682
683         switch ((enum constants)mdata) {
684         case SETSERVENT:
685                 f = va_arg(ap, int);
686                 nsdispatch(retval, compat_dtab, NSDB_SERVICES_COMPAT,
687                     "setservent", compat_src, f);
688                 break;
689         case ENDSERVENT:
690                 nsdispatch(retval, compat_dtab, NSDB_SERVICES_COMPAT,
691                     "endservent", compat_src);
692                 break;
693         default:
694                 break;
695         }
696
697         return (NS_UNAVAIL);
698 }
699
700 #ifdef NS_CACHING
701 static int
702 serv_id_func(char *buffer, size_t *buffer_size, va_list ap, void *cache_mdata)
703 {
704         char *name;
705         char *proto;
706         int port;
707
708         size_t desired_size, size, size2;
709         enum nss_lookup_type lookup_type;
710         int res = NS_UNAVAIL;
711
712         lookup_type = (enum nss_lookup_type)cache_mdata;
713         switch (lookup_type) {
714         case nss_lt_name:
715                 name = va_arg(ap, char *);
716                 proto = va_arg(ap, char *);
717
718                 size = strlen(name);
719                 desired_size = sizeof(enum nss_lookup_type) + size + 1;
720                 if (proto != NULL) {
721                         size2 = strlen(proto);
722                         desired_size += size2 + 1;
723                 } else
724                         size2 = 0;
725
726                 if (desired_size > *buffer_size) {
727                         res = NS_RETURN;
728                         goto fin;
729                 }
730
731                 memcpy(buffer, &lookup_type, sizeof(enum nss_lookup_type));
732                 memcpy(buffer + sizeof(enum nss_lookup_type), name, size + 1);
733
734                 if (proto != NULL)
735                         memcpy(buffer + sizeof(enum nss_lookup_type) + size + 1,
736                             proto, size2 + 1);
737
738                 res = NS_SUCCESS;
739                 break;
740         case nss_lt_id:
741                 port = va_arg(ap, int);
742                 proto = va_arg(ap, char *);
743
744                 desired_size = sizeof(enum nss_lookup_type) + sizeof(int);
745                 if (proto != NULL) {
746                         size = strlen(proto);
747                         desired_size += size + 1;
748                 } else
749                         size = 0;
750
751                 if (desired_size > *buffer_size) {
752                         res = NS_RETURN;
753                         goto fin;
754                 }
755
756                 memcpy(buffer, &lookup_type, sizeof(enum nss_lookup_type));
757                 memcpy(buffer + sizeof(enum nss_lookup_type), &port,
758                     sizeof(int));
759
760                 if (proto != NULL)
761                         memcpy(buffer + sizeof(enum nss_lookup_type) +
762                             sizeof(int), proto, size + 1);
763
764                 res = NS_SUCCESS;
765                 break;
766         default:
767                 /* should be unreachable */
768                 return (NS_UNAVAIL);
769         }
770
771 fin:
772         *buffer_size = desired_size;
773         return (res);
774 }
775
776 int
777 serv_marshal_func(char *buffer, size_t *buffer_size, void *retval __unused,
778     va_list ap, void *cache_mdata)
779 {
780         char *name;
781         char *proto;
782         int port;
783         struct servent *serv;
784         char *orig_buf;
785         size_t orig_buf_size;
786
787         struct servent new_serv;
788         size_t desired_size;
789         char **alias;
790         char *p;
791         size_t size;
792         size_t aliases_size;
793
794         switch ((enum nss_lookup_type)cache_mdata) {
795         case nss_lt_name:
796                 name = va_arg(ap, char *);
797                 proto = va_arg(ap, char *);
798                 break;
799         case nss_lt_id:
800                 port = va_arg(ap, int);
801                 proto = va_arg(ap, char *);
802                 break;
803         case nss_lt_all:
804                 break;
805         default:
806                 /* should be unreachable */
807                 return (NS_UNAVAIL);
808         }
809
810         serv = va_arg(ap, struct servent *);
811         orig_buf = va_arg(ap, char *);
812         orig_buf_size = va_arg(ap, size_t);
813
814         desired_size = _ALIGNBYTES + sizeof(struct servent) + sizeof(char *);
815         if (serv->s_name != NULL)
816                 desired_size += strlen(serv->s_name) + 1;
817         if (serv->s_proto != NULL)
818                 desired_size += strlen(serv->s_proto) + 1;
819
820         aliases_size = 0;
821         if (serv->s_aliases != NULL) {
822                 for (alias = serv->s_aliases; *alias; ++alias) {
823                         desired_size += strlen(*alias) + 1;
824                         ++aliases_size;
825                 }
826
827                 desired_size += _ALIGNBYTES +
828                     sizeof(char *) * (aliases_size + 1);
829         }
830
831         if (*buffer_size < desired_size) {
832                 /* this assignment is here for future use */
833                 *buffer_size = desired_size;
834                 return (NS_RETURN);
835         }
836
837         memcpy(&new_serv, serv, sizeof(struct servent));
838         memset(buffer, 0, desired_size);
839
840         *buffer_size = desired_size;
841         p = buffer + sizeof(struct servent) + sizeof(char *);
842         memcpy(buffer + sizeof(struct servent), &p, sizeof(char *));
843         p = (char *)_ALIGN(p);
844
845         if (new_serv.s_name != NULL) {
846                 size = strlen(new_serv.s_name);
847                 memcpy(p, new_serv.s_name, size);
848                 new_serv.s_name = p;
849                 p += size + 1;
850         }
851
852         if (new_serv.s_proto != NULL) {
853                 size = strlen(new_serv.s_proto);
854                 memcpy(p, new_serv.s_proto, size);
855                 new_serv.s_proto = p;
856                 p += size + 1;
857         }
858
859         if (new_serv.s_aliases != NULL) {
860                 p = (char *)_ALIGN(p);
861                 memcpy(p, new_serv.s_aliases, sizeof(char *) * aliases_size);
862                 new_serv.s_aliases = (char **)p;
863                 p += sizeof(char *) * (aliases_size + 1);
864
865                 for (alias = new_serv.s_aliases; *alias; ++alias) {
866                         size = strlen(*alias);
867                         memcpy(p, *alias, size);
868                         *alias = p;
869                         p += size + 1;
870                 }
871         }
872
873         memcpy(buffer, &new_serv, sizeof(struct servent));
874         return (NS_SUCCESS);
875 }
876
877 int
878 serv_unmarshal_func(char *buffer, size_t buffer_size, void *retval, va_list ap,
879     void *cache_mdata)
880 {
881         char *name;
882         char *proto;
883         int port;
884         struct servent *serv;
885         char *orig_buf;
886         char *p;
887         char **alias;
888         size_t orig_buf_size;
889         int *ret_errno;
890
891         switch ((enum nss_lookup_type)cache_mdata) {
892         case nss_lt_name:
893                 name = va_arg(ap, char *);
894                 proto = va_arg(ap, char *);
895                 break;
896         case nss_lt_id:
897                 port = va_arg(ap, int);
898                 proto = va_arg(ap, char *);
899                 break;
900         case nss_lt_all:
901                 break;
902         default:
903                 /* should be unreachable */
904                 return (NS_UNAVAIL);
905         }
906
907         serv = va_arg(ap, struct servent *);
908         orig_buf = va_arg(ap, char *);
909         orig_buf_size = va_arg(ap, size_t);
910         ret_errno = va_arg(ap, int *);
911
912         if (orig_buf_size <
913             buffer_size - sizeof(struct servent) - sizeof(char *)) {
914                 *ret_errno = ERANGE;
915                 return (NS_RETURN);
916         }
917
918         memcpy(serv, buffer, sizeof(struct servent));
919         memcpy(&p, buffer + sizeof(struct servent), sizeof(char *));
920
921         orig_buf = (char *)_ALIGN(orig_buf);
922         memcpy(orig_buf, buffer + sizeof(struct servent) + sizeof(char *) +
923             (_ALIGN(p) - (size_t)p),
924             buffer_size - sizeof(struct servent) - sizeof(char *) -
925             (_ALIGN(p) - (size_t)p));
926         p = (char *)_ALIGN(p);
927
928         NS_APPLY_OFFSET(serv->s_name, orig_buf, p, char *);
929         NS_APPLY_OFFSET(serv->s_proto, orig_buf, p, char *);
930         if (serv->s_aliases != NULL) {
931                 NS_APPLY_OFFSET(serv->s_aliases, orig_buf, p, char **);
932
933                 for (alias = serv->s_aliases; *alias; ++alias)
934                         NS_APPLY_OFFSET(*alias, orig_buf, p, char *);
935         }
936
937         if (retval != NULL)
938                 *((struct servent **)retval) = serv;
939         return (NS_SUCCESS);
940 }
941
942 NSS_MP_CACHE_HANDLING(services);
943 #endif /* NS_CACHING */
944
945 /* get**_r functions implementation */
946 int
947 getservbyname_r(const char *name, const char *proto, struct servent *serv,
948     char *buffer, size_t bufsize, struct servent **result)
949 {
950         static const struct servent_mdata mdata = { nss_lt_name, 0 };
951         static const struct servent_mdata compat_mdata = { nss_lt_name, 1 };
952 #ifdef NS_CACHING
953         static const nss_cache_info cache_info =
954         NS_COMMON_CACHE_INFO_INITIALIZER(
955                 services, (void *)nss_lt_name,
956                 serv_id_func, serv_marshal_func, serv_unmarshal_func);
957 #endif /* NS_CACHING */
958         static const ns_dtab dtab[] = {
959                 { NSSRC_FILES, files_servent, (void *)&mdata },
960 #ifdef YP
961                 { NSSRC_NIS, nis_servent, (void *)nss_lt_name },
962 #endif
963                 { NSSRC_COMPAT, files_servent, (void *)&compat_mdata },
964 #ifdef NS_CACHING
965                 NS_CACHE_CB(&cache_info)
966 #endif
967                 { NULL, NULL, NULL }
968         };
969         int     rv, ret_errno;
970
971         ret_errno = 0;
972         *result = NULL;
973         rv = nsdispatch(result, dtab, NSDB_SERVICES, "getservbyname_r",
974             defaultsrc, name, proto, serv, buffer, bufsize, &ret_errno);
975
976         if (rv == NS_SUCCESS)
977                 return (0);
978         else
979                 return (ret_errno);
980 }
981
982 int
983 getservbyport_r(int port, const char *proto, struct servent *serv,
984     char *buffer, size_t bufsize, struct servent **result)
985 {
986         static const struct servent_mdata mdata = { nss_lt_id, 0 };
987         static const struct servent_mdata compat_mdata = { nss_lt_id, 1 };
988 #ifdef NS_CACHING
989         static const nss_cache_info cache_info =
990         NS_COMMON_CACHE_INFO_INITIALIZER(
991                 services, (void *)nss_lt_id,
992                 serv_id_func, serv_marshal_func, serv_unmarshal_func);
993 #endif
994         static const ns_dtab dtab[] = {
995                 { NSSRC_FILES, files_servent, (void *)&mdata },
996 #ifdef YP
997                 { NSSRC_NIS, nis_servent, (void *)nss_lt_id },
998 #endif
999                 { NSSRC_COMPAT, files_servent, (void *)&compat_mdata },
1000 #ifdef NS_CACHING
1001                 NS_CACHE_CB(&cache_info)
1002 #endif
1003                 { NULL, NULL, NULL }
1004         };
1005         int rv, ret_errno;
1006
1007         ret_errno = 0;
1008         *result = NULL;
1009         rv = nsdispatch(result, dtab, NSDB_SERVICES, "getservbyport_r",
1010             defaultsrc, port, proto, serv, buffer, bufsize, &ret_errno);
1011
1012         if (rv == NS_SUCCESS)
1013                 return (0);
1014         else
1015                 return (ret_errno);
1016 }
1017
1018 int
1019 getservent_r(struct servent *serv, char *buffer, size_t bufsize,
1020     struct servent **result)
1021 {
1022         static const struct servent_mdata mdata = { nss_lt_all, 0 };
1023         static const struct servent_mdata compat_mdata = { nss_lt_all, 1 };
1024 #ifdef NS_CACHING
1025         static const nss_cache_info cache_info = NS_MP_CACHE_INFO_INITIALIZER(
1026                 services, (void *)nss_lt_all,
1027                 serv_marshal_func, serv_unmarshal_func);
1028 #endif
1029         static const ns_dtab dtab[] = {
1030                 { NSSRC_FILES, files_servent, (void *)&mdata },
1031 #ifdef YP
1032                 { NSSRC_NIS, nis_servent, (void *)nss_lt_all },
1033 #endif
1034                 { NSSRC_COMPAT, files_servent, (void *)&compat_mdata },
1035 #ifdef NS_CACHING
1036                 NS_CACHE_CB(&cache_info)
1037 #endif
1038                 { NULL, NULL, NULL }
1039         };
1040         int rv, ret_errno;
1041
1042         ret_errno = 0;
1043         *result = NULL;
1044         rv = nsdispatch(result, dtab, NSDB_SERVICES, "getservent_r",
1045             defaultsrc, serv, buffer, bufsize, &ret_errno);
1046
1047         if (rv == NS_SUCCESS)
1048                 return (0);
1049         else
1050                 return (ret_errno);
1051 }
1052
1053 void
1054 setservent(int stayopen)
1055 {
1056 #ifdef NS_CACHING
1057         static const nss_cache_info cache_info = NS_MP_CACHE_INFO_INITIALIZER(
1058                 services, (void *)nss_lt_all,
1059                 NULL, NULL);
1060 #endif
1061         static const ns_dtab dtab[] = {
1062                 { NSSRC_FILES, files_setservent, (void *)SETSERVENT },
1063 #ifdef YP
1064                 { NSSRC_NIS, nis_setservent, (void *)SETSERVENT },
1065 #endif
1066                 { NSSRC_COMPAT, compat_setservent, (void *)SETSERVENT },
1067 #ifdef NS_CACHING
1068                 NS_CACHE_CB(&cache_info)
1069 #endif
1070                 { NULL, NULL, NULL }
1071         };
1072
1073         nsdispatch(NULL, dtab, NSDB_SERVICES, "setservent", defaultsrc,
1074             stayopen);
1075 }
1076
1077 void
1078 endservent(void)
1079 {
1080 #ifdef NS_CACHING
1081         static const nss_cache_info cache_info = NS_MP_CACHE_INFO_INITIALIZER(
1082                 services, (void *)nss_lt_all,
1083                 NULL, NULL);
1084 #endif
1085         static const ns_dtab dtab[] = {
1086                 { NSSRC_FILES, files_setservent, (void *)ENDSERVENT },
1087 #ifdef YP
1088                 { NSSRC_NIS, nis_setservent, (void *)ENDSERVENT },
1089 #endif
1090                 { NSSRC_COMPAT, compat_setservent, (void *)ENDSERVENT },
1091 #ifdef NS_CACHING
1092                 NS_CACHE_CB(&cache_info)
1093 #endif
1094                 { NULL, NULL, NULL }
1095         };
1096
1097         nsdispatch(NULL, dtab, NSDB_SERVICES, "endservent", defaultsrc);
1098 }
1099
1100 /* get** wrappers for get**_r functions implementation */
1101 static void
1102 servent_endstate(void *p)
1103 {
1104         if (p == NULL)
1105                 return;
1106
1107         free(((struct servent_state *)p)->buffer);
1108         free(p);
1109 }
1110
1111 static int
1112 wrap_getservbyname_r(struct key key, struct servent *serv, char *buffer,
1113     size_t bufsize, struct servent **res)
1114 {
1115         return (getservbyname_r(key.name, key.proto, serv, buffer, bufsize,
1116             res));
1117 }
1118
1119 static int
1120 wrap_getservbyport_r(struct key key, struct servent *serv, char *buffer,
1121     size_t bufsize, struct servent **res)
1122 {
1123         return (getservbyport_r(key.port, key.proto, serv, buffer, bufsize,
1124             res));
1125 }
1126
1127 static  int
1128 wrap_getservent_r(struct key key __unused, struct servent *serv, char *buffer,
1129     size_t bufsize, struct servent **res)
1130 {
1131         return (getservent_r(serv, buffer, bufsize, res));
1132 }
1133
1134 static struct servent *
1135 getserv(int (*fn)(struct key, struct servent *, char *, size_t,
1136     struct servent **), struct key key)
1137 {
1138         int rv;
1139         struct servent *res;
1140         struct servent_state * st;
1141
1142         rv = servent_getstate(&st);
1143         if (rv != 0) {
1144                 errno = rv;
1145                 return NULL;
1146         }
1147
1148         if (st->buffer == NULL) {
1149                 st->buffer = malloc(SERVENT_STORAGE_INITIAL);
1150                 if (st->buffer == NULL)
1151                         return (NULL);
1152                 st->bufsize = SERVENT_STORAGE_INITIAL;
1153         }
1154         do {
1155                 rv = fn(key, &st->serv, st->buffer, st->bufsize, &res);
1156                 if (res == NULL && rv == ERANGE) {
1157                         free(st->buffer);
1158                         if ((st->bufsize << 1) > SERVENT_STORAGE_MAX) {
1159                                 st->buffer = NULL;
1160                                 errno = ERANGE;
1161                                 return (NULL);
1162                         }
1163                         st->bufsize <<= 1;
1164                         st->buffer = malloc(st->bufsize);
1165                         if (st->buffer == NULL)
1166                                 return (NULL);
1167                 }
1168         } while (res == NULL && rv == ERANGE);
1169         if (rv != 0)
1170                 errno = rv;
1171
1172         return (res);
1173 }
1174
1175 struct servent *
1176 getservbyname(const char *name, const char *proto)
1177 {
1178         struct key key;
1179
1180         key.name = name;
1181         key.proto = proto;
1182
1183         return (getserv(wrap_getservbyname_r, key));
1184 }
1185
1186 struct servent *
1187 getservbyport(int port, const char *proto)
1188 {
1189         struct key key;
1190
1191         key.port = port;
1192         key.proto = proto;
1193
1194         return (getserv(wrap_getservbyport_r, key));
1195 }
1196
1197 struct servent *
1198 getservent(void)
1199 {
1200         struct key key;
1201
1202         key.proto = NULL;
1203         key.port = 0;
1204
1205         return (getserv(wrap_getservent_r, key));
1206 }