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