1 /*
2  * CDDL HEADER START
3  *
4  * The contents of this file are subject to the terms of the
5  * Common Development and Distribution License (the "License").
6  * You may not use this file except in compliance with the License.
7  *
8  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9  * or http://www.opensolaris.org/os/licensing.
10  * See the License for the specific language governing permissions
11  * and limitations under the License.
12  *
13  * When distributing Covered Code, include this CDDL HEADER in each
14  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15  * If applicable, add the following below this CDDL HEADER, with the
16  * fields enclosed by brackets "[]" replaced with your own identifying
17  * information: Portions Copyright [yyyy] [name of copyright owner]
18  *
19  * CDDL HEADER END
20  */
21 
22 /*
23  * Copyright 2006 Sun Microsystems, Inc.  All rights reserved.
24  * Use is subject to license terms.
25  *
26  */
27 
28 /* Id: nss.c 180 2006-07-20 17:33:02Z njacobs $ */
29 
30 #pragma ident	"%Z%%M%	%I%	%E% SMI"
31 
32 #include <stdio.h>
33 #include <stdlib.h>
34 #include <unistd.h>
35 #include <string.h>
36 #include <ctype.h>
37 #include <sys/types.h>
38 #include <syslog.h>
39 #include <papi.h>
40 #include <uri.h>
41 #include <papi_impl.h>
42 #ifdef NSS_EMULATION
43 #include <nss-emulation.h>
44 #elif NSS_SOLARIS
45 #include <nss_dbdefs.h>
46 #endif
47 #include <config-site.h>
48 #if defined(__sun) && defined(__SVR4)
49 #include <sys/systeminfo.h>
50 #endif
51 
52 
53 static char *
54 bsdaddr_to_uri(papi_attribute_t **list, char *bsdaddr)
55 {
56 	char *result = NULL;
57 
58 	if (bsdaddr != NULL) {
59 		char *bsd[3], *tmp, *iter = NULL;
60 		char buf[512];
61 
62 		tmp = strdup(bsdaddr);
63 
64 		bsd[0] = strtok_r(tmp, ":,", &iter);
65 		if ((bsd[1] = strtok_r(NULL, ":,", &iter)) == NULL)
66 			papiAttributeListGetString(list, NULL,
67 					"printer-name", &bsd[1]);
68 		bsd[2] = strtok_r(NULL, ":,", &iter);
69 
70 		snprintf(buf, sizeof (buf), "lpd://%s/printers/%s%s%s", bsd[0],
71 			(bsd[1] != NULL) ? bsd[1] : "",
72 			(bsd[2] != NULL) ? "#" : "",
73 			(bsd[2] != NULL) ? bsd[2] : "");
74 
75 		free(tmp);
76 
77 		result = strdup(buf);
78 	}
79 
80 	return (result);
81 }
82 
83 #if defined(__sun) && defined(__SVR4)
84 #include <sys/socket.h>
85 #include <sys/ioctl.h>
86 #include <sys/sockio.h>
87 #include <net/if.h>
88 #include <netinet/in.h>
89 #include <arpa/inet.h>
90 #include <netdb.h>
91 
92 static struct in6_addr **
93 local_interfaces()
94 {
95 	struct in6_addr **result = NULL;
96 	int s;
97 	struct lifnum n;
98 	struct lifconf c;
99 	struct lifreq *r;
100 	int count;
101 
102 	/* we need a socket to get the interfaces */
103 	if ((s = socket(AF_INET, SOCK_DGRAM, 0)) < 0)
104 		return (0);
105 
106 	/* get the number of interfaces */
107 	memset(&n, 0, sizeof (n));
108 	n.lifn_family = AF_UNSPEC;
109 	if (ioctl(s, SIOCGLIFNUM, (char *)&n) < 0) {
110 		close(s);
111 		return (0);	/* no interfaces */
112 	}
113 
114 	/* get the interface(s) configuration */
115 	memset(&c, 0, sizeof (c));
116 	c.lifc_family = AF_UNSPEC;
117 	c.lifc_buf = calloc(n.lifn_count, sizeof (struct lifreq));
118 	c.lifc_len = (n.lifn_count * sizeof (struct lifreq));
119 	if (ioctl(s, SIOCGLIFCONF, (char *)&c) < 0) {
120 		free(c.lifc_buf);
121 		close(s);
122 		return (0);	/* can't get interface(s) configuration */
123 	}
124 	close(s);
125 
126 	r = c.lifc_req;
127 	for (count = c.lifc_len / sizeof (struct lifreq);
128 	     count > 0; count--, r++) {
129 		struct in6_addr v6[1], *addr = NULL;
130 
131 		switch (r->lifr_addr.ss_family) {
132 		case AF_INET: {
133 			struct sockaddr_in *s =
134 				(struct sockaddr_in *)&r->lifr_addr;
135 			IN6_INADDR_TO_V4MAPPED(&s->sin_addr, v6);
136 			addr = v6;
137 			}
138 			break;
139 		case AF_INET6: {
140 			struct sockaddr_in6 *s =
141 				(struct sockaddr_in6 *)&r->lifr_addr;
142 			addr = &s->sin6_addr;
143 			}
144 			break;
145 		}
146 
147 		if (addr != NULL) {
148 			struct in6_addr *a = malloc(sizeof (*a));
149 
150 			memcpy(a, addr, sizeof (*a));
151 			list_append(&result, a);
152 		}
153 	}
154 	free(c.lifc_buf);
155 
156 	return (result);
157 }
158 
159 static int
160 match_interfaces(char *host)
161 {
162 	struct in6_addr **lif = local_interfaces();
163 	struct hostent *hp;
164 	int rc = 0;
165 	int errnum;
166 
167 	/* are there any local interfaces */
168 	if (lif == NULL)
169 		return (0);
170 
171 	/* cycle through the host db addresses */
172 	hp = getipnodebyname(host, AF_INET6, AI_ALL|AI_V4MAPPED, &errnum);
173 	if (hp != NULL) {
174 		struct in6_addr **tmp = (struct in6_addr **)hp->h_addr_list;
175 		int i;
176 
177 		for (i = 0; ((rc == 0) && (tmp[i] != NULL)); i++) {
178 			int j;
179 
180 			for (j = 0; ((rc == 0) && (lif[j] != NULL)); j++)
181 				if (memcmp(tmp[i], lif[j],
182 						sizeof (struct in6_addr)) == 0)
183 					rc = 1;
184 		}
185 	}
186 	free(lif);
187 
188 	return (rc);
189 }
190 
191 static int
192 is_localhost(char *host)
193 {
194 	char hostname[BUFSIZ];
195 
196 	/* is it "localhost" */
197 	if (strncasecmp(host, "localhost", 10) == 0)
198 		return (1);
199 
200 	/* is it the {nodename} */
201 	sysinfo(SI_HOSTNAME, hostname, sizeof (hostname));
202 	if (strncasecmp(host, hostname, strlen(hostname)) == 0)
203 		return (1);
204 
205 	/* does it match one of the host's configured interfaces */
206 	if (match_interfaces(host) != 0)
207 		return (1);
208 
209 	return (0);
210 }
211 
212 /*
213  * This is an awful HACK to force the dynamic PAPI library to use the
214  * lpsched support when the destination apears to be a local lpsched
215  * queue on Solaris.
216  */
217 static void
218 solaris_lpsched_shortcircuit_hack(papi_attribute_t ***list)
219 {
220 	papi_attribute_t *attribute;
221 	uri_t *uri = NULL;
222 	char *printer = NULL;
223 	char buf[128], buf2[128];
224 
225 	/* setting this in the calling env can be useful for debugging */
226 	if (getenv("DISABLE_LPSCHED_SHORTCIRCUIT") != NULL)
227 		return;
228 
229 	papiAttributeListGetString(*list, NULL,
230 				"printer-uri-supported", &printer);
231 	if (uri_from_string(printer, &uri) < 0)
232 		return;
233 
234 	/* already an lpsched URI ? */
235 	if (strcasecmp(uri->scheme, "lpsched") == 0)
236 		return;
237 
238 	if ((printer = strrchr(uri->path, '/')) == NULL)
239 		printer = uri->path;
240 	else
241 		printer++;
242 
243 	/* is there an lpsched queue (printer/class) */
244 	snprintf(buf, sizeof (buf), "/etc/lp/interfaces/%s", printer);
245 	snprintf(buf2, sizeof (buf2), "/etc/lp/classes/%s", printer);
246 	if ((access(buf, F_OK) < 0) && (access(buf2, F_OK) < 0))
247 		return;
248 
249 	/* is this the "local" host */
250 	if ((uri->host != NULL) && (is_localhost(uri->host) == 0))
251 		return;
252 
253 	snprintf(buf, sizeof (buf), "lpsched://%s/printers/%s",
254 			(uri->host ? uri->host : "localhost"), printer);
255 	papiAttributeListAddString(list, PAPI_ATTR_REPLACE,
256 			"printer-uri-supported", buf);
257 }
258 #endif
259 
260 static void
261 fill_printer_uri_supported(papi_attribute_t ***list)
262 {
263 	papi_attribute_t *attribute;
264 	char *string = NULL;
265 
266 	/* do we have a printer-uri-supported */
267 	attribute = papiAttributeListFind(*list, "printer-uri-supported");
268 	if (attribute != NULL) /* we have what we need, return */
269 		return;
270 
271 	/* do we have a printer-uri to rename */
272 	attribute = papiAttributeListFind(*list, "printer-uri");
273 	if (attribute != NULL) { /* rename it in place and return */
274 		free(attribute->name);
275 		attribute->name = strdup("printer-uri-supported");
276 		return;
277 	}
278 
279 	/* do we have a printers.conf(4) "bsdaddr" to convert */
280 	papiAttributeListGetString(*list, NULL, "bsdaddr", &string);
281 	if (string != NULL) { /* parse it, convert it, add it */
282 		char *uri = bsdaddr_to_uri(*list, string);
283 
284 		if (uri != NULL) {
285 			papiAttributeListAddString(list, PAPI_ATTR_APPEND,
286 					"printer-uri-supported", uri);
287 			papiAttributeListDelete(list, "bsdaddr");
288 			free(uri);
289 			return;
290 		}
291 	}
292 
293 	/* do we have a printers.conf(4) "rm" (and "rp") to convert */
294 	papiAttributeListGetString(*list, NULL, "rm", &string);
295 	if (string != NULL) {
296 		char *rp = NULL;
297 
298 		/* default to "printer-name", but use "rp" if we have it */
299 		papiAttributeListGetString(*list, NULL, "printer-name", &rp);
300 		papiAttributeListGetString(*list, NULL, "rp", &rp);
301 
302 		if (rp != NULL) { /* fill in the uri if we have the data */
303 			char buf[BUFSIZ];
304 
305 			snprintf(buf, sizeof (buf), "lpd://%s/printers/%s",
306 				string, rp);
307 			papiAttributeListAddString(list, PAPI_ATTR_APPEND,
308 					"printer-uri-supported", strdup(buf));
309 			return;
310 		}
311 	}
312 
313 	/* if were are here, we don't have a printer-uri-supported */
314 }
315 
316 #ifdef NEED_BROKEN_PRINTER_URI_SEMANTIC
317 static void
318 fill_printer_uri(papi_attribute_t ***list)
319 {
320 	papi_attribute_t *attribute;
321 	char *uri = NULL;
322 
323 	if ((list == NULL) || (*list == NULL))
324 		return;
325 
326 	/* do we have a printer-uri */
327 	attribute = papiAttributeListFind(*list, "printer-uri");
328 	if (attribute != NULL) /* we have what we need, return */
329 		return;
330 
331 	/*
332 	 * this is sufficient to fool libgnomeprintpapi, but not promote it's
333 	 * use in the future.
334 	 */
335 	papiAttributeListAddString(list, PAPI_ATTR_EXCL, "printer-uri",
336 			"broken printer-uri semantic");
337 }
338 #endif /* NEED_BROKEN_PRINTER_URI_SEMANTIC */
339 
340 static void
341 cvt_all_to_member_names(papi_attribute_t ***list)
342 {
343 	papi_status_t status;
344 	void *iter = NULL;
345 	char *string = NULL;
346 
347 	papiAttributeListGetString(*list, NULL, "member-names", &string);
348 	if (string != NULL) /* already have a member-names */
349 		return;
350 
351 	for (status = papiAttributeListGetString(*list, &iter, "all", &string);
352 	     status == PAPI_OK;
353 	     status = papiAttributeListGetString(*list, &iter, NULL, &string)) {
354 		char *s_iter = NULL, *value, *tmp = strdup(string);
355 
356 		for (value = strtok_r(tmp, ", \t", &s_iter);
357 		     value != NULL;
358 		     value = strtok_r(NULL, ", \t", &s_iter))
359 			papiAttributeListAddString(list, PAPI_ATTR_APPEND,
360 					"member-names", value);
361 		free(tmp);
362 	}
363 }
364 
365 static papi_attribute_t **
366 _cvt_nss_entry_to_printer(char *entry)
367 {
368 	char    *key = NULL,
369 		*cp,
370 		buf[BUFSIZ];
371 	int in_namelist = 1, buf_pos = 0;
372 	papi_attribute_t **list = NULL;
373 
374 	if (entry == NULL)
375 		return (NULL);
376 
377 	memset(buf, 0, sizeof (buf));
378 	for (cp = entry; *cp != '\0'; cp++) {
379 		switch (*cp) {
380 		case ':':	/* end of kvp */
381 			if (in_namelist != 0) {
382 				papiAttributeListAddString(&list,
383 					PAPI_ATTR_APPEND, "printer-name", buf);
384 				in_namelist = 0;
385 			} else if (key != NULL)
386 				papiAttributeListAddString(&list,
387 					PAPI_ATTR_APPEND, key, buf);
388 			memset(buf, 0, sizeof (buf));
389 			buf_pos = 0;
390 			key = NULL;
391 			break;
392 		case '=':	/* kvp seperator */
393 			if (key == NULL) {
394 				key = strdup(buf);
395 				memset(buf, 0, sizeof (buf));
396 				buf_pos = 0;
397 			} else
398 				buf[buf_pos++] = *cp;
399 			break;
400 		case '|':	/* namelist seperator */
401 			if (in_namelist != 0) {
402 				papiAttributeListAddString(&list,
403 					PAPI_ATTR_APPEND, "printer-name", buf);
404 				memset(buf, 0, sizeof (buf));
405 				buf_pos = 0;
406 			} else	/* add it to the buffer */
407 				buf[buf_pos++] = *cp;
408 			break;
409 		case '\\':	/* escape char */
410 			buf[buf_pos++] = *(++cp);
411 			break;
412 		default:
413 			buf[buf_pos++] = *cp;
414 		}
415 
416 	}
417 
418 	if (key != NULL)
419 		papiAttributeListAddString(&list, PAPI_ATTR_APPEND, key, buf);
420 
421 	/* resolve any "use" references in the configuration DB */
422 	key = NULL;
423 	papiAttributeListGetString(list, NULL, "use", &key);
424 	if (key != NULL) {
425 		papi_attribute_t **use_attrs = getprinterbyname(key, NULL);
426 
427 		list_concatenate(&list, use_attrs);
428 	}
429 
430 	fill_printer_uri_supported(&list);
431 	cvt_all_to_member_names(&list); /* convert "all" to "member-names" */
432 
433 	return (list);
434 }
435 
436 #if defined(NSS_SOLARIS) && !defined(NSS_EMULATION)
437 
438 #ifndef	NSS_DBNAM__PRINTERS	/* not in nss_dbdefs.h because it's private */
439 #define	NSS_DBNAM__PRINTERS	"_printers"
440 #endif
441 
442 static DEFINE_NSS_DB_ROOT(db_root);
443 static DEFINE_NSS_GETENT(context);
444 
445 static char *private_ns = NULL;
446 
447 static void
448 _nss_initf_printers(p)
449     nss_db_params_t *p;
450 {
451 	if (private_ns != NULL) {
452 		/*
453 		 * because we need to support a legacy interface that allows
454 		 * us to select a specific name service, we need to dummy up
455 		 * the parameters to use a private nsswitch database and set
456 		 * the * default_config entry to the name service we are
457 		 * looking into.
458 		 */
459 		p->name = NSS_DBNAM__PRINTERS;		/* "_printers" */
460 		p->default_config = private_ns;
461 	} else {
462 		/* regular behaviour */
463 		p->name = NSS_DBNAM_PRINTERS;	 /* "printers" */
464 		p->default_config = NSS_DEFCONF_PRINTERS;
465 	}
466 	syslog(LOG_DEBUG, "database: %s, default: %s",
467 		(p->name ? p->name : "NULL"),
468 		(p->default_config ? p->default_config : "NULL"));
469 }
470 
471 /*
472  * Return values: 0 = success, 1 = parse error, 2 = erange ...
473  * The structure pointer passed in is a structure in the caller's space
474  * wherein the field pointers would be set to areas in the buffer if
475  * need be. instring and buffer should be separate areas.
476  */
477 /* ARGSUSED */
478 static int
479 str2printer(const char *instr, int lenstr, void *ent, char *buffer, int buflen)
480 {
481 	if (lenstr + 1 > buflen)
482 		return (NSS_STR_PARSE_ERANGE);
483 
484 	/* skip entries that begin with '#' */
485 	if (instr[0] == '#')
486 		return (NSS_STR_PARSE_PARSE);
487 
488 	/*
489 	 * We copy the input string into the output buffer
490 	 */
491 	(void) memcpy(buffer, instr, lenstr);
492 	buffer[lenstr] = '\0';
493 
494 	return (NSS_STR_PARSE_SUCCESS);
495 }
496 #endif /* NSS_SOLARIS */
497 
498 int
499 setprinterentry(int stayopen, char *ns)
500 {
501 #ifdef NSS_EMULATION
502 	emul_setprinterentry(stayopen);
503 #elif NSS_SOLARIS
504 	private_ns = ns;
505 	nss_setent(&db_root, _nss_initf_printers, &context);
506 	private_ns = NULL;
507 #endif
508 	return (0);
509 }
510 
511 
512 int
513 endprinterentry(int i)
514 {
515 #ifdef NSS_EMULATION
516 	emul_endprinterentry();
517 #elif NSS_SOLARIS
518 	nss_endent(&db_root, _nss_initf_printers, &context);
519 	nss_delete(&db_root);
520 	private_ns = NULL;
521 #endif
522 	return (0);
523 }
524 
525 /* ARGSUSED2 */
526 papi_attribute_t **
527 getprinterentry(char *ns)
528 {
529 	papi_attribute_t **result = NULL;
530 
531 #if defined(NSS_EMULATION) || defined(NSS_SOLARIS)
532 	char buf[10240];
533 	nss_status_t	res = NSS_NOTFOUND;
534 
535 #ifdef NSS_EMULATION
536 	res = emul_getprinterentry_r(buf, sizeof (buf));
537 #elif NSS_SOLARIS
538 	nss_XbyY_args_t arg;
539 
540 	private_ns = ns;
541 	NSS_XbyY_INIT(&arg, buf, buf, sizeof (buf), str2printer);
542 	res = nss_getent(&db_root, _nss_initf_printers, &context, &arg);
543 	(void) NSS_XbyY_FINI(&arg);
544 	private_ns = NULL;
545 #endif
546 
547 	if (res != NSS_SUCCESS)
548 		buf[0] = '\0';
549 
550 	result = _cvt_nss_entry_to_printer(buf);
551 #if defined(__sun) && defined(__SVR4)
552 	solaris_lpsched_shortcircuit_hack(&result);
553 #endif
554 #ifdef NEED_BROKEN_PRINTER_URI_SEMANTIC
555 	fill_printer_uri(&result);
556 #endif /* NEED_BROKEN_PRINTER_URI_SEMANTIC */
557 #endif
558 
559 #ifdef DEBUG
560 	printf("getprinterentry(%s): 0x%8.8x\n", (ns ? ns : "NULL"), result);
561 	if (result != NULL) {
562 		char buf[4096];
563 
564 		papiAttributeListToString(result, "\n\t", buf, sizeof (buf));
565 		printf("\t%s\n", buf);
566 	}
567 #endif /* DEBUG */
568 
569 	return (result);
570 }
571 
572 
573 papi_attribute_t **
574 getprinterbyname(char *name, char *ns)
575 {
576 	papi_attribute_t **result = NULL;
577 
578 	if (strstr(name, "://") != NULL) {	/* shortcut for URI form */
579 		papiAttributeListAddString(&result, PAPI_ATTR_APPEND,
580 				"printer-name", name);
581 		papiAttributeListAddString(&result, PAPI_ATTR_APPEND,
582 				"printer-uri-supported", name);
583 	} else if (strchr(name, ':') != NULL) {	/* shortcut for POSIX form */
584 		char *uri = bsdaddr_to_uri(result, name);
585 
586 		papiAttributeListAddString(&result, PAPI_ATTR_APPEND,
587 				"printer-name", name);
588 		if (uri != NULL) {
589 			papiAttributeListAddString(&result, PAPI_ATTR_APPEND,
590 					"printer-uri-supported", uri);
591 			free(uri);
592 		}
593 	} else {				/* anything else */
594 #if defined(NSS_EMULATION) || defined(NSS_SOLARIS)
595 		char buf[10240];
596 		nss_status_t	res = NSS_NOTFOUND;
597 
598 #ifdef NSS_EMULATION
599 		res = emul_getprinterbyname_r(name, buf, sizeof (buf));
600 #elif NSS_SOLARIS
601 		nss_XbyY_args_t arg;
602 
603 		private_ns = ns;
604 		NSS_XbyY_INIT(&arg, buf, buf, sizeof (buf), str2printer);
605 		arg.key.name = name;
606 		res = nss_search(&db_root, _nss_initf_printers,
607 				NSS_DBOP_PRINTERS_BYNAME, &arg);
608 		(void) NSS_XbyY_FINI(&arg);
609 		private_ns = NULL;
610 
611 		if (res != NSS_SUCCESS)
612 			buf[0] = '\0';
613 #endif
614 
615 		result = _cvt_nss_entry_to_printer(buf);
616 #endif
617 	}
618 #if defined(__sun) && defined(__SVR4)
619 	solaris_lpsched_shortcircuit_hack(&result);
620 #endif
621 #ifdef DEBUG
622 	printf("getprinterbyname(%s): %s = 0x%8.8x\n", (ns ? ns : "NULL"),
623 		name, result);
624 	if (result != NULL) {
625 		char buf[4096];
626 
627 		papiAttributeListToString(result, "\n\t", buf, sizeof (buf));
628 		printf("\t%s\n", buf);
629 	}
630 #endif /* DEBUG */
631 
632 	return (result);
633 }
634