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