xref: /illumos-gate/usr/src/lib/print/libpapi-dynamic/common/nss.c (revision 22f5594a529d50114d839d4ddecc2c499731a3d7)
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 2008 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 (in URI form) to rename */
272 	attribute = papiAttributeListFind(*list, "printer-uri");
273 	if ((attribute != NULL) &&
274 	    (attribute->type == PAPI_STRING) &&
275 	    (attribute->values != NULL) &&
276 	    (attribute->values[0]->string != NULL) &&
277 	    (strstr(attribute->values[0]->string, "://") != NULL)) {
278 			/* rename it in place and return */
279 		free(attribute->name);
280 		attribute->name = strdup("printer-uri-supported");
281 		return;
282 	}
283 
284 	/* do we have a printers.conf(4) "bsdaddr" to convert */
285 	papiAttributeListGetString(*list, NULL, "bsdaddr", &string);
286 	if (string != NULL) { /* parse it, convert it, add it */
287 		char *uri = bsdaddr_to_uri(*list, string);
288 
289 		if (uri != NULL) {
290 			papiAttributeListAddString(list, PAPI_ATTR_APPEND,
291 					"printer-uri-supported", uri);
292 			papiAttributeListDelete(list, "bsdaddr");
293 			free(uri);
294 			return;
295 		}
296 	}
297 
298 	/* do we have a printers.conf(4) "rm" (and "rp") to convert */
299 	papiAttributeListGetString(*list, NULL, "rm", &string);
300 	if (string != NULL) {
301 		char *rp = NULL;
302 
303 		/* default to "printer-name", but use "rp" if we have it */
304 		papiAttributeListGetString(*list, NULL, "printer-name", &rp);
305 		papiAttributeListGetString(*list, NULL, "rp", &rp);
306 
307 		if (rp != NULL) { /* fill in the uri if we have the data */
308 			char buf[BUFSIZ];
309 
310 			snprintf(buf, sizeof (buf), "lpd://%s/printers/%s",
311 				string, rp);
312 			papiAttributeListAddString(list, PAPI_ATTR_APPEND,
313 					"printer-uri-supported", strdup(buf));
314 			return;
315 		}
316 	}
317 
318 	/* if were are here, we don't have a printer-uri-supported */
319 }
320 
321 #ifdef NEED_BROKEN_PRINTER_URI_SEMANTIC
322 static void
323 fill_printer_uri(papi_attribute_t ***list)
324 {
325 	papi_attribute_t *attribute;
326 	char *uri = NULL;
327 
328 	if ((list == NULL) || (*list == NULL))
329 		return;
330 
331 	/* do we have a printer-uri */
332 	attribute = papiAttributeListFind(*list, "printer-uri");
333 	if (attribute != NULL) /* we have what we need, return */
334 		return;
335 
336 	/*
337 	 * this is sufficient to fool libgnomeprintpapi, but not promote it's
338 	 * use in the future.
339 	 */
340 	papiAttributeListAddString(list, PAPI_ATTR_EXCL, "printer-uri",
341 			"broken printer-uri semantic");
342 }
343 #endif /* NEED_BROKEN_PRINTER_URI_SEMANTIC */
344 
345 static void
346 cvt_all_to_member_names(papi_attribute_t ***list)
347 {
348 	papi_status_t status;
349 	void *iter = NULL;
350 	char *string = NULL;
351 
352 	papiAttributeListGetString(*list, NULL, "member-names", &string);
353 	if (string != NULL) /* already have a member-names */
354 		return;
355 
356 	for (status = papiAttributeListGetString(*list, &iter, "all", &string);
357 	     status == PAPI_OK;
358 	     status = papiAttributeListGetString(*list, &iter, NULL, &string)) {
359 		char *s_iter = NULL, *value, *tmp = strdup(string);
360 
361 		for (value = strtok_r(tmp, ", \t", &s_iter);
362 		     value != NULL;
363 		     value = strtok_r(NULL, ", \t", &s_iter))
364 			papiAttributeListAddString(list, PAPI_ATTR_APPEND,
365 					"member-names", value);
366 		free(tmp);
367 	}
368 }
369 
370 static papi_attribute_t **
371 _cvt_nss_entry_to_printer(char *entry)
372 {
373 	char    *key = NULL,
374 		*cp,
375 		buf[BUFSIZ];
376 	int in_namelist = 1, buf_pos = 0;
377 	papi_attribute_t **list = NULL;
378 
379 	if (entry == NULL)
380 		return (NULL);
381 
382 	memset(buf, 0, sizeof (buf));
383 	for (cp = entry; *cp != '\0'; cp++) {
384 		switch (*cp) {
385 		case ':':	/* end of kvp */
386 			if (in_namelist != 0) {
387 				papiAttributeListAddString(&list,
388 					PAPI_ATTR_APPEND, "printer-name", buf);
389 				in_namelist = 0;
390 			} else if (key != NULL)
391 				papiAttributeListAddString(&list,
392 					PAPI_ATTR_APPEND, key, buf);
393 			memset(buf, 0, sizeof (buf));
394 			buf_pos = 0;
395 			key = NULL;
396 			break;
397 		case '=':	/* kvp seperator */
398 			if (key == NULL) {
399 				key = strdup(buf);
400 				memset(buf, 0, sizeof (buf));
401 				buf_pos = 0;
402 			} else
403 				buf[buf_pos++] = *cp;
404 			break;
405 		case '|':	/* namelist seperator */
406 			if (in_namelist != 0) {
407 				papiAttributeListAddString(&list,
408 					PAPI_ATTR_APPEND, "printer-name", buf);
409 				memset(buf, 0, sizeof (buf));
410 				buf_pos = 0;
411 			} else	/* add it to the buffer */
412 				buf[buf_pos++] = *cp;
413 			break;
414 		case '\\':	/* escape char */
415 			buf[buf_pos++] = *(++cp);
416 			break;
417 		default:
418 			buf[buf_pos++] = *cp;
419 		}
420 
421 	}
422 
423 	if (key != NULL)
424 		papiAttributeListAddString(&list, PAPI_ATTR_APPEND, key, buf);
425 
426 	/* resolve any "use" references in the configuration DB */
427 	key = NULL;
428 	papiAttributeListGetString(list, NULL, "use", &key);
429 	if (key != NULL) {
430 		papi_attribute_t **use_attrs = getprinterbyname(key, NULL);
431 
432 		list_concatenate(&list, use_attrs);
433 	}
434 
435 	fill_printer_uri_supported(&list);
436 	cvt_all_to_member_names(&list); /* convert "all" to "member-names" */
437 
438 	return (list);
439 }
440 
441 #if defined(NSS_SOLARIS) && !defined(NSS_EMULATION)
442 
443 #ifndef	NSS_DBNAM__PRINTERS	/* not in nss_dbdefs.h because it's private */
444 #define	NSS_DBNAM__PRINTERS	"_printers"
445 #endif
446 
447 static DEFINE_NSS_DB_ROOT(db_root);
448 static DEFINE_NSS_GETENT(context);
449 
450 static char *private_ns = NULL;
451 
452 static void
453 _nss_initf_printers(p)
454     nss_db_params_t *p;
455 {
456 	if (private_ns != NULL) {
457 		/*
458 		 * because we need to support a legacy interface that allows
459 		 * us to select a specific name service, we need to dummy up
460 		 * the parameters to use a private nsswitch database and set
461 		 * the * default_config entry to the name service we are
462 		 * looking into.
463 		 */
464 		p->name = NSS_DBNAM__PRINTERS;		/* "_printers" */
465 		p->default_config = private_ns;
466 	} else {
467 		/* regular behaviour */
468 		p->name = NSS_DBNAM_PRINTERS;	 /* "printers" */
469 		p->default_config = NSS_DEFCONF_PRINTERS;
470 	}
471 	syslog(LOG_DEBUG, "database: %s, default: %s",
472 		(p->name ? p->name : "NULL"),
473 		(p->default_config ? p->default_config : "NULL"));
474 }
475 
476 /*
477  * Return values: 0 = success, 1 = parse error, 2 = erange ...
478  * The structure pointer passed in is a structure in the caller's space
479  * wherein the field pointers would be set to areas in the buffer if
480  * need be. instring and buffer should be separate areas.
481  */
482 /* ARGSUSED */
483 static int
484 str2printer(const char *instr, int lenstr, void *ent, char *buffer, int buflen)
485 {
486 	if (lenstr + 1 > buflen)
487 		return (NSS_STR_PARSE_ERANGE);
488 
489 	/* skip entries that begin with '#' */
490 	if (instr[0] == '#')
491 		return (NSS_STR_PARSE_PARSE);
492 
493 	/*
494 	 * We copy the input string into the output buffer
495 	 */
496 	(void) memcpy(buffer, instr, lenstr);
497 	buffer[lenstr] = '\0';
498 
499 	return (NSS_STR_PARSE_SUCCESS);
500 }
501 #endif /* NSS_SOLARIS */
502 
503 int
504 setprinterentry(int stayopen, char *ns)
505 {
506 #ifdef NSS_EMULATION
507 	emul_setprinterentry(stayopen);
508 #elif NSS_SOLARIS
509 	private_ns = ns;
510 	nss_setent(&db_root, _nss_initf_printers, &context);
511 	private_ns = NULL;
512 #endif
513 	return (0);
514 }
515 
516 
517 int
518 endprinterentry(int i)
519 {
520 #ifdef NSS_EMULATION
521 	emul_endprinterentry();
522 #elif NSS_SOLARIS
523 	nss_endent(&db_root, _nss_initf_printers, &context);
524 	nss_delete(&db_root);
525 	private_ns = NULL;
526 #endif
527 	return (0);
528 }
529 
530 /* ARGSUSED2 */
531 papi_attribute_t **
532 getprinterentry(char *ns)
533 {
534 	papi_attribute_t **result = NULL;
535 
536 #if defined(NSS_EMULATION) || defined(NSS_SOLARIS)
537 	char buf[10240];
538 	nss_status_t	res = NSS_NOTFOUND;
539 
540 #ifdef NSS_EMULATION
541 	res = emul_getprinterentry_r(buf, sizeof (buf));
542 #elif NSS_SOLARIS
543 	nss_XbyY_args_t arg;
544 
545 	private_ns = ns;
546 	NSS_XbyY_INIT(&arg, buf, buf, sizeof (buf), str2printer);
547 	res = nss_getent(&db_root, _nss_initf_printers, &context, &arg);
548 	(void) NSS_XbyY_FINI(&arg);
549 	private_ns = NULL;
550 #endif
551 
552 	if (res != NSS_SUCCESS)
553 		buf[0] = '\0';
554 
555 	result = _cvt_nss_entry_to_printer(buf);
556 #if defined(__sun) && defined(__SVR4)
557 	solaris_lpsched_shortcircuit_hack(&result);
558 #endif
559 #ifdef NEED_BROKEN_PRINTER_URI_SEMANTIC
560 	fill_printer_uri(&result);
561 #endif /* NEED_BROKEN_PRINTER_URI_SEMANTIC */
562 #endif
563 
564 #ifdef DEBUG
565 	printf("getprinterentry(%s): 0x%8.8x\n", (ns ? ns : "NULL"), result);
566 	if (result != NULL) {
567 		char buf[4096];
568 
569 		papiAttributeListToString(result, "\n\t", buf, sizeof (buf));
570 		printf("\t%s\n", buf);
571 	}
572 #endif /* DEBUG */
573 
574 	return (result);
575 }
576 
577 
578 papi_attribute_t **
579 getprinterbyname(char *name, char *ns)
580 {
581 	papi_attribute_t **result = NULL;
582 
583 	if (strstr(name, "://") != NULL) {	/* shortcut for URI form */
584 		papiAttributeListAddString(&result, PAPI_ATTR_APPEND,
585 				"printer-name", name);
586 		papiAttributeListAddString(&result, PAPI_ATTR_APPEND,
587 				"printer-uri-supported", name);
588 	} else if (strchr(name, ':') != NULL) {	/* shortcut for POSIX form */
589 		char *uri = bsdaddr_to_uri(result, name);
590 
591 		papiAttributeListAddString(&result, PAPI_ATTR_APPEND,
592 				"printer-name", name);
593 		if (uri != NULL) {
594 			papiAttributeListAddString(&result, PAPI_ATTR_APPEND,
595 					"printer-uri-supported", uri);
596 			free(uri);
597 		}
598 	} else {				/* anything else */
599 #if defined(NSS_EMULATION) || defined(NSS_SOLARIS)
600 		char buf[10240];
601 		nss_status_t	res = NSS_NOTFOUND;
602 
603 #ifdef NSS_EMULATION
604 		res = emul_getprinterbyname_r(name, buf, sizeof (buf));
605 #elif NSS_SOLARIS
606 		nss_XbyY_args_t arg;
607 
608 		private_ns = ns;
609 		NSS_XbyY_INIT(&arg, buf, buf, sizeof (buf), str2printer);
610 		arg.key.name = name;
611 		res = nss_search(&db_root, _nss_initf_printers,
612 				NSS_DBOP_PRINTERS_BYNAME, &arg);
613 		(void) NSS_XbyY_FINI(&arg);
614 		private_ns = NULL;
615 
616 		if (res != NSS_SUCCESS)
617 			buf[0] = '\0';
618 #endif
619 
620 		result = _cvt_nss_entry_to_printer(buf);
621 #endif
622 	}
623 #if defined(__sun) && defined(__SVR4)
624 	solaris_lpsched_shortcircuit_hack(&result);
625 #endif
626 #ifdef DEBUG
627 	printf("getprinterbyname(%s): %s = 0x%8.8x\n", (ns ? ns : "NULL"),
628 		name, result);
629 	if (result != NULL) {
630 		char buf[4096];
631 
632 		papiAttributeListToString(result, "\n\t", buf, sizeof (buf));
633 		printf("\t%s\n", buf);
634 	}
635 #endif /* DEBUG */
636 
637 	return (result);
638 }
639