1 /*-
2 * Copyright (c) 2009, Sun Microsystems, Inc.
3 * 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 are met:
7 * - Redistributions of source code must retain the above copyright notice,
8 * this list of conditions and the following disclaimer.
9 * - Redistributions in binary form must reproduce the above copyright notice,
10 * this list of conditions and the following disclaimer in the documentation
11 * and/or other materials provided with the distribution.
12 * - Neither the name of Sun Microsystems, Inc. nor the names of its
13 * contributors may be used to endorse or promote products derived
14 * from this software without specific prior written permission.
15 *
16 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
17 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
18 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
19 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
20 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
21 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
22 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
23 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
24 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
25 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
26 * POSSIBILITY OF SUCH DAMAGE.
27 *
28 * @(#)getnetconfig.c 1.12 91/12/19 SMI
29 * $NetBSD: getnetconfig.c,v 1.3 2000/07/06 03:10:34 christos Exp $
30 * $FreeBSD: src/lib/libc/rpc/getnetconfig.c,v 1.14 2007/09/20 22:35:24 matteo Exp $
31 */
32
33 /*
34 * Copyright (c) 1989 by Sun Microsystems, Inc.
35 */
36
37 #include "namespace.h"
38 #include "reentrant.h"
39 #include <stdio.h>
40 #include <errno.h>
41 #include <netconfig.h>
42 #include <stddef.h>
43 #include <stdlib.h>
44 #include <string.h>
45 #include <rpc/rpc.h>
46 #include <unistd.h>
47 #include "un-namespace.h"
48 #include "rpc_com.h"
49
50 /*
51 * The five library routines in this file provide application access to the
52 * system network configuration database, /etc/netconfig. In addition to the
53 * netconfig database and the routines for accessing it, the environment
54 * variable NETPATH and its corresponding routines in getnetpath.c may also be
55 * used to specify the network transport to be used.
56 */
57
58
59 /*
60 * netconfig errors
61 */
62
63 #define NC_NONETCONFIG ENOENT
64 #define NC_NOMEM ENOMEM
65 #define NC_NOTINIT EINVAL /* setnetconfig was not called first */
66 #define NC_BADFILE EBADF /* format for netconfig file is bad */
67 #define NC_NOTFOUND ENOPROTOOPT /* specified netid was not found */
68
69 /*
70 * semantics as strings (should be in netconfig.h)
71 */
72 #define NC_TPI_CLTS_S "tpi_clts"
73 #define NC_TPI_COTS_S "tpi_cots"
74 #define NC_TPI_COTS_ORD_S "tpi_cots_ord"
75 #define NC_TPI_RAW_S "tpi_raw"
76
77 /*
78 * flags as characters (also should be in netconfig.h)
79 */
80 #define NC_NOFLAG_C '-'
81 #define NC_VISIBLE_C 'v'
82 #define NC_BROADCAST_C 'b'
83
84 /*
85 * Character used to indicate there is no name-to-address lookup library
86 */
87 #define NC_NOLOOKUP "-"
88
89 static const char * const _nc_errors[] = {
90 "Netconfig database not found",
91 "Not enough memory",
92 "Not initialized",
93 "Netconfig database has invalid format",
94 "Netid not found in netconfig database"
95 };
96
97 struct netconfig_info {
98 int eof; /* all entries has been read */
99 int ref; /* # of times setnetconfig() has been called */
100 struct netconfig_list *head; /* head of the list */
101 struct netconfig_list *tail; /* last of the list */
102 };
103
104 struct netconfig_list {
105 char *linep; /* hold line read from netconfig */
106 struct netconfig *ncp;
107 struct netconfig_list *next;
108 };
109
110 struct netconfig_vars {
111 int valid; /* token that indicates a valid netconfig_vars */
112 int flag; /* first time flag */
113 struct netconfig_list *nc_configs; /* pointer to the current netconfig entry */
114 };
115
116 #define NC_VALID 0xfeed
117 #define NC_STORAGE 0xf00d
118 #define NC_INVALID 0
119
120
121 static int *__nc_error(void);
122 static int parse_ncp(char *, struct netconfig *);
123 static struct netconfig *dup_ncp(struct netconfig *);
124
125
126 static FILE *nc_file; /* for netconfig db */
127 static struct netconfig_info ni = { 0, 0, NULL, NULL};
128
129 #define MAXNETCONFIGLINE 1000
130
131 static int *
__nc_error(void)132 __nc_error(void)
133 {
134 static pthread_mutex_t nc_lock = PTHREAD_MUTEX_INITIALIZER;
135 static thread_key_t nc_key = 0;
136 static int nc_error = 0;
137 int error, *nc_addr;
138
139 /*
140 * Use the static `nc_error' if we are the main thread
141 * (including non-threaded programs), or if an allocation
142 * fails.
143 */
144 if (thr_main())
145 return (&nc_error);
146 if (nc_key == 0) {
147 error = 0;
148 mutex_lock(&nc_lock);
149 if (nc_key == 0)
150 error = thr_keycreate(&nc_key, free);
151 mutex_unlock(&nc_lock);
152 if (error)
153 return (&nc_error);
154 }
155 if ((nc_addr = (int *)thr_getspecific(nc_key)) == NULL) {
156 nc_addr = (int *)malloc(sizeof (int));
157 if (thr_setspecific(nc_key, (void *) nc_addr) != 0) {
158 if (nc_addr)
159 free(nc_addr);
160 return (&nc_error);
161 }
162 *nc_addr = 0;
163 }
164 return (nc_addr);
165 }
166
167 #define nc_error (*(__nc_error()))
168 /*
169 * A call to setnetconfig() establishes a /etc/netconfig "session". A session
170 * "handle" is returned on a successful call. At the start of a session (after
171 * a call to setnetconfig()) searches through the /etc/netconfig database will
172 * proceed from the start of the file. The session handle must be passed to
173 * getnetconfig() to parse the file. Each call to getnetconfig() using the
174 * current handle will process one subsequent entry in /etc/netconfig.
175 * setnetconfig() must be called before the first call to getnetconfig().
176 * (Handles are used to allow for nested calls to setnetpath()).
177 *
178 * A new session is established with each call to setnetconfig(), with a new
179 * handle being returned on each call. Previously established sessions remain
180 * active until endnetconfig() is called with that session's handle as an
181 * argument.
182 *
183 * setnetconfig() need *not* be called before a call to getnetconfigent().
184 * setnetconfig() returns a NULL pointer on failure (for example, if
185 * the netconfig database is not present).
186 */
187 void *
setnetconfig(void)188 setnetconfig(void)
189 {
190 struct netconfig_vars *nc_vars;
191
192 if ((nc_vars = (struct netconfig_vars *)malloc(sizeof
193 (struct netconfig_vars))) == NULL) {
194 return(NULL);
195 }
196
197 /*
198 * For multiple calls, i.e. nc_file is not NULL, we just return the
199 * handle without reopening the netconfig db.
200 */
201 ni.ref++;
202 if ((nc_file != NULL) || (nc_file = fopen(NETCONFIG, "r")) != NULL) {
203 nc_vars->valid = NC_VALID;
204 nc_vars->flag = 0;
205 nc_vars->nc_configs = ni.head;
206 return ((void *)nc_vars);
207 }
208 ni.ref--;
209 nc_error = NC_NONETCONFIG;
210 free(nc_vars);
211 return (NULL);
212 }
213
214
215 /*
216 * When first called, getnetconfig() returns a pointer to the first entry in
217 * the netconfig database, formatted as a struct netconfig. On each subsequent
218 * call, getnetconfig() returns a pointer to the next entry in the database.
219 * getnetconfig() can thus be used to search the entire netconfig file.
220 * getnetconfig() returns NULL at end of file.
221 */
222
223 struct netconfig *
getnetconfig(void * handlep)224 getnetconfig(void *handlep)
225 {
226 struct netconfig_vars *ncp = (struct netconfig_vars *)handlep;
227 char *stringp; /* tmp string pointer */
228 struct netconfig_list *list;
229 struct netconfig *np;
230
231 /*
232 * Verify that handle is valid
233 */
234 if (ncp == NULL || nc_file == NULL) {
235 nc_error = NC_NOTINIT;
236 return (NULL);
237 }
238
239 switch (ncp->valid) {
240 case NC_VALID:
241 /*
242 * If entry has already been read into the list,
243 * we return the entry in the linked list.
244 * If this is the first time call, check if there are any entries in
245 * linked list. If no entries, we need to read the netconfig db.
246 * If we have been here and the next entry is there, we just return
247 * it.
248 */
249 if (ncp->flag == 0) { /* first time */
250 ncp->flag = 1;
251 ncp->nc_configs = ni.head;
252 if (ncp->nc_configs != NULL) /* entry already exist */
253 return(ncp->nc_configs->ncp);
254 }
255 else if (ncp->nc_configs != NULL && ncp->nc_configs->next != NULL) {
256 ncp->nc_configs = ncp->nc_configs->next;
257 return(ncp->nc_configs->ncp);
258 }
259
260 /*
261 * If we cannot find the entry in the list and is end of file,
262 * we give up.
263 */
264 if (ni.eof == 1) return(NULL);
265 break;
266 default:
267 nc_error = NC_NOTINIT;
268 return (NULL);
269 }
270
271 stringp = (char *) malloc(MAXNETCONFIGLINE);
272 if (stringp == NULL)
273 return (NULL);
274
275 #ifdef MEM_CHK
276 if (malloc_verify() == 0) {
277 fprintf(stderr, "memory heap corrupted in getnetconfig\n");
278 exit(1);
279 }
280 #endif
281
282 /*
283 * Read a line from netconfig file.
284 */
285 do {
286 if (fgets(stringp, MAXNETCONFIGLINE, nc_file) == NULL) {
287 free(stringp);
288 ni.eof = 1;
289 return (NULL);
290 }
291 } while (*stringp == '#');
292
293 list = (struct netconfig_list *) malloc(sizeof (struct netconfig_list));
294 if (list == NULL) {
295 free(stringp);
296 return(NULL);
297 }
298 np = (struct netconfig *) malloc(sizeof (struct netconfig));
299 if (np == NULL) {
300 free(stringp);
301 free(list);
302 return(NULL);
303 }
304 list->ncp = np;
305 list->next = NULL;
306 list->ncp->nc_lookups = NULL;
307 list->linep = stringp;
308 if (parse_ncp(stringp, list->ncp) == -1) {
309 free(stringp);
310 free(np);
311 free(list);
312 return (NULL);
313 }
314 else {
315 /*
316 * If this is the first entry that's been read, it is the head of
317 * the list. If not, put the entry at the end of the list.
318 * Reposition the current pointer of the handle to the last entry
319 * in the list.
320 */
321 if (ni.head == NULL) { /* first entry */
322 ni.head = ni.tail = list;
323 }
324 else {
325 ni.tail->next = list;
326 ni.tail = ni.tail->next;
327 }
328 ncp->nc_configs = ni.tail;
329 return(ni.tail->ncp);
330 }
331 }
332
333 /*
334 * endnetconfig() may be called to "unbind" or "close" the netconfig database
335 * when processing is complete, releasing resources for reuse. endnetconfig()
336 * may not be called before setnetconfig(). endnetconfig() returns 0 on
337 * success and -1 on failure (for example, if setnetconfig() was not called
338 * previously).
339 */
340 int
endnetconfig(void * handlep)341 endnetconfig(void *handlep)
342 {
343 struct netconfig_vars *nc_handlep = (struct netconfig_vars *)handlep;
344
345 struct netconfig_list *q, *p;
346
347 /*
348 * Verify that handle is valid
349 */
350 if (nc_handlep == NULL || (nc_handlep->valid != NC_VALID &&
351 nc_handlep->valid != NC_STORAGE)) {
352 nc_error = NC_NOTINIT;
353 return (-1);
354 }
355
356 /*
357 * Return 0 if anyone still needs it.
358 */
359 nc_handlep->valid = NC_INVALID;
360 nc_handlep->flag = 0;
361 nc_handlep->nc_configs = NULL;
362 if (--ni.ref > 0) {
363 free(nc_handlep);
364 return(0);
365 }
366
367 /*
368 * Noone needs these entries anymore, then frees them.
369 * Make sure all info in netconfig_info structure has been reinitialized.
370 */
371 q = p = ni.head;
372 ni.eof = ni.ref = 0;
373 ni.head = NULL;
374 ni.tail = NULL;
375 while (q) {
376 p = q->next;
377 if (q->ncp->nc_lookups != NULL) free(q->ncp->nc_lookups);
378 free(q->ncp);
379 free(q->linep);
380 free(q);
381 q = p;
382 }
383 free(nc_handlep);
384
385 fclose(nc_file);
386 nc_file = NULL;
387 return (0);
388 }
389
390 /*
391 * getnetconfigent(netid) returns a pointer to the struct netconfig structure
392 * corresponding to netid. It returns NULL if netid is invalid (that is, does
393 * not name an entry in the netconfig database). It returns NULL and sets
394 * errno in case of failure (for example, if the netconfig database cannot be
395 * opened).
396 */
397
398 struct netconfig *
getnetconfigent(const char * netid)399 getnetconfigent(const char *netid)
400 {
401 FILE *file; /* NETCONFIG db's file pointer */
402 char *linep; /* holds current netconfig line */
403 char *stringp; /* temporary string pointer */
404 struct netconfig *ncp = NULL; /* returned value */
405 struct netconfig_list *list; /* pointer to cache list */
406
407 nc_error = NC_NOTFOUND; /* default error. */
408 if (netid == NULL || strlen(netid) == 0) {
409 return (NULL);
410 }
411
412 /*
413 * Look up table if the entries have already been read and parsed in
414 * getnetconfig(), then copy this entry into a buffer and return it.
415 * If we cannot find the entry in the current list and there are more
416 * entries in the netconfig db that has not been read, we then read the
417 * db and try find the match netid.
418 * If all the netconfig db has been read and placed into the list and
419 * there is no match for the netid, return NULL.
420 */
421 if (ni.head != NULL) {
422 for (list = ni.head; list; list = list->next) {
423 if (strcmp(list->ncp->nc_netid, netid) == 0) {
424 return(dup_ncp(list->ncp));
425 }
426 }
427 if (ni.eof == 1) /* that's all the entries */
428 return(NULL);
429 }
430
431
432 if ((file = fopen(NETCONFIG, "r")) == NULL) {
433 nc_error = NC_NONETCONFIG;
434 return (NULL);
435 }
436
437 if ((linep = malloc(MAXNETCONFIGLINE)) == NULL) {
438 fclose(file);
439 nc_error = NC_NOMEM;
440 return (NULL);
441 }
442 do {
443 ptrdiff_t len;
444 char *tmpp; /* tmp string pointer */
445
446 do {
447 if ((stringp = fgets(linep, MAXNETCONFIGLINE, file)) == NULL) {
448 break;
449 }
450 } while (*stringp == '#');
451 if (stringp == NULL) { /* eof */
452 break;
453 }
454 if ((tmpp = strpbrk(stringp, "\t ")) == NULL) { /* can't parse file */
455 nc_error = NC_BADFILE;
456 break;
457 }
458 if (strlen(netid) == (size_t) (len = tmpp - stringp) && /* a match */
459 strncmp(stringp, netid, (size_t)len) == 0) {
460 if ((ncp = (struct netconfig *)
461 malloc(sizeof (struct netconfig))) == NULL) {
462 break;
463 }
464 ncp->nc_lookups = NULL;
465 if (parse_ncp(linep, ncp) == -1) {
466 free(ncp);
467 ncp = NULL;
468 }
469 break;
470 }
471 } while (stringp != NULL);
472 if (ncp == NULL) {
473 free(linep);
474 }
475 fclose(file);
476 return(ncp);
477 }
478
479 /*
480 * freenetconfigent(netconfigp) frees the netconfig structure pointed to by
481 * netconfigp (previously returned by getnetconfigent()).
482 */
483
484 void
freenetconfigent(struct netconfig * netconfigp)485 freenetconfigent(struct netconfig *netconfigp)
486 {
487 if (netconfigp != NULL) {
488 free(netconfigp->nc_netid); /* holds all netconfigp's strings */
489 if (netconfigp->nc_lookups != NULL)
490 free(netconfigp->nc_lookups);
491 free(netconfigp);
492 }
493 return;
494 }
495
496 /*
497 * Parse line and stuff it in a struct netconfig
498 * Typical line might look like:
499 * udp tpi_cots vb inet udp /dev/udp /usr/lib/ip.so,/usr/local/ip.so
500 *
501 * We return -1 if any of the tokens don't parse, or malloc fails.
502 *
503 * Note that we modify stringp (putting NULLs after tokens) and
504 * we set the ncp's string field pointers to point to these tokens within
505 * stringp.
506 */
507
508 static int
parse_ncp(char * stringp,struct netconfig * ncp)509 parse_ncp(char *stringp, /* string to parse */
510 struct netconfig *ncp) /* where to put results */
511 {
512 char *tokenp; /* for processing tokens */
513 char *lasts;
514 char **nc_lookups;
515
516 nc_error = NC_BADFILE; /* nearly anything that breaks is for this reason */
517 stringp[strlen(stringp)-1] = '\0'; /* get rid of newline */
518 /* netid */
519 if ((ncp->nc_netid = strtok_r(stringp, "\t ", &lasts)) == NULL) {
520 return (-1);
521 }
522
523 /* semantics */
524 if ((tokenp = strtok_r(NULL, "\t ", &lasts)) == NULL) {
525 return (-1);
526 }
527 if (strcmp(tokenp, NC_TPI_COTS_ORD_S) == 0)
528 ncp->nc_semantics = NC_TPI_COTS_ORD;
529 else if (strcmp(tokenp, NC_TPI_COTS_S) == 0)
530 ncp->nc_semantics = NC_TPI_COTS;
531 else if (strcmp(tokenp, NC_TPI_CLTS_S) == 0)
532 ncp->nc_semantics = NC_TPI_CLTS;
533 else if (strcmp(tokenp, NC_TPI_RAW_S) == 0)
534 ncp->nc_semantics = NC_TPI_RAW;
535 else
536 return (-1);
537
538 /* flags */
539 if ((tokenp = strtok_r(NULL, "\t ", &lasts)) == NULL) {
540 return (-1);
541 }
542 for (ncp->nc_flag = NC_NOFLAG; *tokenp != '\0';
543 tokenp++) {
544 switch (*tokenp) {
545 case NC_NOFLAG_C:
546 break;
547 case NC_VISIBLE_C:
548 ncp->nc_flag |= NC_VISIBLE;
549 break;
550 case NC_BROADCAST_C:
551 ncp->nc_flag |= NC_BROADCAST;
552 break;
553 default:
554 return (-1);
555 }
556 }
557 /* protocol family */
558 if ((ncp->nc_protofmly = strtok_r(NULL, "\t ", &lasts)) == NULL) {
559 return (-1);
560 }
561 /* protocol name */
562 if ((ncp->nc_proto = strtok_r(NULL, "\t ", &lasts)) == NULL) {
563 return (-1);
564 }
565 /* network device */
566 if ((ncp->nc_device = strtok_r(NULL, "\t ", &lasts)) == NULL) {
567 return (-1);
568 }
569 if ((tokenp = strtok_r(NULL, "\t ", &lasts)) == NULL) {
570 return (-1);
571 }
572 if (strcmp(tokenp, NC_NOLOOKUP) == 0) {
573 ncp->nc_nlookups = 0;
574 ncp->nc_lookups = NULL;
575 } else {
576 char *cp; /* tmp string */
577
578 if (ncp->nc_lookups != NULL) /* from last visit */
579 free(ncp->nc_lookups);
580 ncp->nc_lookups = NULL;
581 ncp->nc_nlookups = 0;
582 while ((cp = tokenp) != NULL) {
583 if ((nc_lookups = realloc(ncp->nc_lookups,
584 (ncp->nc_nlookups + 1) * sizeof *ncp->nc_lookups)) == NULL) {
585 free(ncp->nc_lookups);
586 ncp->nc_lookups = NULL;
587 return (-1);
588 }
589 tokenp = _get_next_token(cp, ',');
590 ncp->nc_lookups = nc_lookups;
591 ncp->nc_lookups[ncp->nc_nlookups++] = cp;
592 }
593 }
594 return (0);
595 }
596
597
598 /*
599 * Returns a string describing the reason for failure.
600 */
601 char *
nc_sperror(void)602 nc_sperror(void)
603 {
604 const char *message;
605
606 switch(nc_error) {
607 case NC_NONETCONFIG:
608 message = _nc_errors[0];
609 break;
610 case NC_NOMEM:
611 message = _nc_errors[1];
612 break;
613 case NC_NOTINIT:
614 message = _nc_errors[2];
615 break;
616 case NC_BADFILE:
617 message = _nc_errors[3];
618 break;
619 case NC_NOTFOUND:
620 message = _nc_errors[4];
621 break;
622 default:
623 message = "Unknown network selection error";
624 }
625 /* LINTED const castaway */
626 return ((char *)message);
627 }
628
629 /*
630 * Prints a message onto standard error describing the reason for failure.
631 */
632 void
nc_perror(const char * s)633 nc_perror(const char *s)
634 {
635 fprintf(stderr, "%s: %s\n", s, nc_sperror());
636 }
637
638 /*
639 * Duplicates the matched netconfig buffer.
640 */
641 static struct netconfig *
dup_ncp(struct netconfig * ncp)642 dup_ncp(struct netconfig *ncp)
643 {
644 struct netconfig *p;
645 char *tmp;
646 u_int i;
647
648 if ((tmp=malloc(MAXNETCONFIGLINE)) == NULL)
649 return(NULL);
650 if ((p=(struct netconfig *)malloc(sizeof(struct netconfig))) == NULL) {
651 free(tmp);
652 return(NULL);
653 }
654 /*
655 * First we dup all the data from matched netconfig buffer. Then we
656 * adjust some of the member pointer to a pre-allocated buffer where
657 * contains part of the data.
658 * To follow the convention used in parse_ncp(), we store all the
659 * necessary information in the pre-allocated buffer and let each
660 * of the netconfig char pointer member point to the right address
661 * in the buffer.
662 */
663 *p = *ncp;
664 p->nc_netid = (char *)strcpy(tmp,ncp->nc_netid);
665 tmp = strchr(tmp, '\0') + 1;
666 p->nc_protofmly = (char *)strcpy(tmp,ncp->nc_protofmly);
667 tmp = strchr(tmp, '\0') + 1;
668 p->nc_proto = (char *)strcpy(tmp,ncp->nc_proto);
669 tmp = strchr(tmp, '\0') + 1;
670 p->nc_device = (char *)strcpy(tmp,ncp->nc_device);
671 p->nc_lookups = (char **)malloc((size_t)(p->nc_nlookups+1) * sizeof(char *));
672 if (p->nc_lookups == NULL) {
673 free(p->nc_netid);
674 free(p);
675 return(NULL);
676 }
677 for (i=0; i < p->nc_nlookups; i++) {
678 tmp = strchr(tmp, '\0') + 1;
679 p->nc_lookups[i] = (char *)strcpy(tmp,ncp->nc_lookups[i]);
680 }
681 return(p);
682 }
683