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