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 2012 Nexenta Systems, Inc. All rights reserved.
24 * Copyright 2008 Sun Microsystems, Inc. All rights reserved.
25 * Use is subject to license terms.
26 *
27 */
28
29 /* $Id: ipp-listener.c 146 2006-03-24 00:26:54Z njacobs $ */
30
31 #include <stdio.h>
32 #include <stdlib.h>
33 #include <string.h>
34 #include <netinet/in.h>
35 #include <assert.h>
36 #include <errno.h>
37 #include <syslog.h>
38 #include <sys/types.h>
39 #include <sys/stat.h>
40 #include <fcntl.h>
41 #include <unistd.h>
42 #include <sys/systeminfo.h>
43
44 #include <papi.h>
45 #include <ipp-listener.h>
46 #include <uri.h>
47
48 typedef papi_status_t (ipp_handler_t)(papi_service_t svc,
49 papi_attribute_t **request,
50 papi_attribute_t ***response,
51 ipp_reader_t iread, void *fd);
52
53 /*
54 * protocol request handlers are inserted below. The handler must be
55 * declared extern immediately below this comment and then an entry
56 * must be inserted in the "handlers" table a little further down.
57 */
58 extern ipp_handler_t ipp_print_job;
59 extern ipp_handler_t ipp_validate_job;
60 extern ipp_handler_t ipp_create_job;
61 extern ipp_handler_t ipp_get_printer_attributes;
62 extern ipp_handler_t ipp_get_jobs;
63 extern ipp_handler_t ipp_pause_printer;
64 extern ipp_handler_t ipp_resume_printer;
65 extern ipp_handler_t ipp_disable_printer;
66 extern ipp_handler_t ipp_enable_printer;
67 extern ipp_handler_t ipp_purge_jobs;
68 extern ipp_handler_t ipp_send_document;
69 extern ipp_handler_t ipp_cancel_job;
70 extern ipp_handler_t ipp_get_job_attributes;
71 extern ipp_handler_t ipp_release_job;
72 extern ipp_handler_t ipp_hold_job;
73 extern ipp_handler_t ipp_restart_job;
74 extern ipp_handler_t ipp_set_job_attributes;
75 extern ipp_handler_t ipp_set_printer_attributes;
76 extern ipp_handler_t cups_get_default;
77 extern ipp_handler_t cups_get_printers;
78 extern ipp_handler_t cups_get_classes;
79 extern ipp_handler_t cups_accept_jobs;
80 extern ipp_handler_t cups_reject_jobs;
81 extern ipp_handler_t cups_move_job;
82
83 /* ARGSUSED0 */
84 static papi_status_t
85 default_handler(papi_service_t svc, papi_attribute_t **request,
86 papi_attribute_t ***response, ipp_reader_t iread, void *fd)
87 {
88 int result = (int)PAPI_INTERNAL_ERROR;
89
90 if (response != NULL)
91 (void) papiAttributeListGetInteger(*response, NULL,
92 "status-code", &result);
93
94 return ((papi_status_t)result);
95 }
96
97 static struct {
98 int16_t id;
99 char *name;
100 ipp_handler_t *function;
101 enum { OP_REQUIRED, OP_OPTIONAL, OP_VENDOR } type;
102 } handlers[] = {
103 /* Printer Operations */
104 { 0x0002, "print-job", ipp_print_job, OP_REQUIRED },
105 { 0x0003, "print-uri", NULL, OP_OPTIONAL },
106 { 0x0004, "validate-job", ipp_validate_job,
107 OP_REQUIRED },
108 { 0x0005, "create-job", ipp_create_job, OP_OPTIONAL },
109 { 0x000a, "get-jobs", ipp_get_jobs, OP_REQUIRED },
110 { 0x000b, "get-printer-attributes", ipp_get_printer_attributes,
111 OP_REQUIRED },
112 { 0x0010, "pause-printer", ipp_pause_printer,
113 OP_OPTIONAL },
114 { 0x0011, "resume-printer", ipp_resume_printer,
115 OP_OPTIONAL },
116 { 0x0012, "purge-jobs", ipp_purge_jobs, OP_OPTIONAL },
117 { 0x0013, "set-printer-attributes", ipp_set_printer_attributes,
118 OP_OPTIONAL },
119 { 0x0014, "set-job-attributes", ipp_set_job_attributes,
120 OP_OPTIONAL },
121 { 0x0022, "enable-printer", ipp_enable_printer,
122 OP_OPTIONAL },
123 { 0x0023, "disable-printer", ipp_disable_printer,
124 OP_OPTIONAL },
125 /* Job Operations */
126 { 0x0006, "send-document", ipp_send_document,
127 OP_OPTIONAL },
128 { 0x0007, "send-uri", NULL, OP_OPTIONAL },
129 { 0x0008, "cancel-job", ipp_cancel_job, OP_REQUIRED },
130 { 0x0009, "get-job-attributes", ipp_get_job_attributes,
131 OP_REQUIRED },
132 { 0x000c, "hold-job", ipp_hold_job, OP_OPTIONAL },
133 { 0x000d, "release-job", ipp_release_job,
134 OP_OPTIONAL },
135 { 0x000e, "restart-job", ipp_restart_job,
136 OP_OPTIONAL },
137 /* Other Operations */
138 { 0x4001, "cups-get-default", cups_get_default,
139 OP_VENDOR },
140 { 0x4002, "cups-get-printers", cups_get_printers,
141 OP_VENDOR },
142 { 0x4005, "cups-get-classes", cups_get_classes,
143 OP_VENDOR },
144 { 0x4008, "cups-accept-jobs", cups_accept_jobs,
145 OP_VENDOR },
146 { 0x4009, "cups-reject-jobs", cups_reject_jobs,
147 OP_VENDOR },
148 { 0x400D, "cups-move-job", cups_move_job, OP_VENDOR },
149 { 0, NULL, NULL, OP_VENDOR }
150 };
151
152 static int
153 ipp_operation_name_to_index(const char *name)
154 {
155 int i;
156
157 for (i = 0; handlers[i].name != NULL; i++)
158 if (strcasecmp(name, handlers[i].name) == 0)
159 return (i);
160
161 return (-1);
162 }
163
164 static int
165 ipp_operation_id_to_index(int16_t id)
166 {
167 int i;
168
169 for (i = 0; handlers[i].name != NULL; i++)
170 if (id == handlers[i].id)
171 return (i);
172
173 return (-1);
174 }
175
176 static ipp_handler_t *
177 ipp_operation_handler(papi_attribute_t **request, papi_attribute_t ***response)
178 {
179 int id = 0;
180 int index;
181 papi_attribute_t **ops = NULL;
182 papi_status_t status;
183 char configured = PAPI_FALSE;
184
185 /* get the operation from the request */
186 status = papiAttributeListGetInteger(request, NULL,
187 "operation-id", &id);
188 if (status != PAPI_OK) {
189 ipp_set_status(response, PAPI_BAD_ARGUMENT,
190 "no operation specified in request");
191 return (default_handler);
192 }
193
194 /* find the operation in the handler table */
195 index = ipp_operation_id_to_index(id);
196 #ifdef DEBUG
197 if (index == -1)
198 fprintf(stderr, "Operation: 0x%4.4x\n", id);
199 else
200 fprintf(stderr, "Operation: 0x%4.4x(%s)\n", id,
201 handlers[index].name);
202 fflush(stderr);
203 #endif
204
205 if ((index == -1) || (handlers[index].function == NULL)) {
206 ipp_set_status(response, PAPI_OPERATION_NOT_SUPPORTED,
207 "operation (0x%4.4x) not implemented by server",
208 id);
209 return (default_handler);
210 }
211
212 /* find the configured operations */
213 status = papiAttributeListGetCollection(request, NULL,
214 "operations", &ops);
215 if (status != PAPI_OK) { /* this should not be possible */
216 ipp_set_status(response, PAPI_INTERNAL_ERROR,
217 "sofware error, no operations configured");
218 return (default_handler);
219 }
220
221 /* check if the requested operation is configured */
222 status = papiAttributeListGetBoolean(ops, NULL,
223 handlers[index].name, &configured);
224 if ((status != PAPI_OK) || (configured != PAPI_TRUE)) {
225 ipp_set_status(response, PAPI_OPERATION_NOT_SUPPORTED,
226 "operation (%s 0x%4.4x) not enabled on server",
227 handlers[index].name, id);
228 return (default_handler);
229 }
230
231 return (handlers[index].function);
232 }
233
234 static char
235 type_to_boolean(const char *type)
236 {
237 char result = PAPI_FALSE;
238
239 if ((strcasecmp(type, "true") == 0) ||
240 (strcasecmp(type, "yes") == 0) ||
241 (strcasecmp(type, "on") == 0) ||
242 (strcasecmp(type, "enable") == 0))
243 result = PAPI_TRUE;
244
245 return (result);
246 }
247
248 static papi_status_t
249 ipp_configure_required_operations(papi_attribute_t ***list, char boolean)
250 {
251 papi_status_t result = PAPI_OK;
252 int i;
253
254 for (i = 0; ((result == PAPI_OK) && (handlers[i].name != NULL)); i++)
255 if (handlers[i].type == OP_REQUIRED)
256 result = papiAttributeListAddBoolean(list,
257 PAPI_ATTR_REPLACE, handlers[i].name,
258 boolean);
259
260 return (result);
261
262 }
263
264 static papi_status_t
265 ipp_configure_all_operations(papi_attribute_t ***list, char boolean)
266 {
267 papi_status_t result = PAPI_OK;
268 int i;
269
270 for (i = 0; ((result == PAPI_OK) && (handlers[i].name != NULL)); i++)
271 result = papiAttributeListAddBoolean(list, PAPI_ATTR_REPLACE,
272 handlers[i].name, boolean);
273
274 return (result);
275 }
276
277 papi_status_t
278 ipp_configure_operation(papi_attribute_t ***list, const char *operation, const char *type)
279 {
280 papi_status_t result = PAPI_OPERATION_NOT_SUPPORTED;
281 char boolean = PAPI_FALSE;
282
283 if ((list == NULL) || (operation == NULL) || (type == NULL))
284 return (PAPI_BAD_ARGUMENT);
285
286 boolean = type_to_boolean(type);
287
288 if (strcasecmp(operation, "all") == 0) {
289 result = ipp_configure_all_operations(list, boolean);
290 } else if (strcasecmp(operation, "required") == 0) {
291 result = ipp_configure_required_operations(list, boolean);
292 } else if (ipp_operation_name_to_index(operation) != -1) {
293 result = papiAttributeListAddBoolean(list, PAPI_ATTR_REPLACE,
294 operation, boolean);
295 }
296
297 return (result);
298 }
299
300 void
301 ipp_operations_supported(papi_attribute_t ***list, papi_attribute_t **request)
302 {
303 papi_attribute_t **group = NULL;
304
305 (void) papiAttributeListGetCollection(request, NULL,
306 "operations", &group);
307 if (group != NULL) {
308 int i;
309
310 for (i = 0; handlers[i].name != NULL; i++) {
311 char boolean = PAPI_FALSE;
312 (void) papiAttributeListGetBoolean(group, NULL,
313 handlers[i].name, &boolean);
314
315 if (boolean == PAPI_TRUE)
316 (void) papiAttributeListAddInteger(list,
317 PAPI_ATTR_APPEND,
318 "operations-supported",
319 handlers[i].id);
320 }
321 }
322 }
323
324 static papi_status_t
325 ipp_initialize_response(papi_attribute_t **request,
326 papi_attribute_t ***response)
327 {
328 papi_attribute_t **operational = NULL;
329 int i;
330
331 if ((request == NULL) || (response == NULL))
332 return (PAPI_BAD_ARGUMENT);
333
334 /* If the response was initialized, start over */
335 if (*response != NULL) {
336 papiAttributeListFree(*response);
337 *response = NULL;
338 }
339
340 /* Add the basic ipp header information to the response */
341 (void) papiAttributeListGetInteger(request, NULL, "version-major", &i);
342 (void) papiAttributeListAddInteger(response, PAPI_ATTR_REPLACE,
343 "version-major", i);
344 (void) papiAttributeListGetInteger(request, NULL, "version-minor", &i);
345 (void) papiAttributeListAddInteger(response, PAPI_ATTR_REPLACE,
346 "version-minor", i);
347
348 (void) papiAttributeListGetInteger(request, NULL, "request-id", &i);
349 (void) papiAttributeListAddInteger(response, PAPI_ATTR_REPLACE,
350 "request-id", i);
351
352 /* Add a default operational attributes group to the response */
353 (void) papiAttributeListAddString(&operational, PAPI_ATTR_EXCL,
354 "attributes-charset", "utf-8");
355 (void) papiAttributeListAddString(&operational, PAPI_ATTR_EXCL,
356 "attributes-natural-language", "en-us");
357
358 (void) papiAttributeListAddCollection(response, PAPI_ATTR_REPLACE,
359 "operational-attributes-group", operational);
360 papiAttributeListFree(operational);
361
362 return (PAPI_OK);
363 }
364
365 /* simplistic check for cyclical service references */
366 static int
367 cyclical_service_check(char *svc_name, int port)
368 {
369 papi_attribute_t **list;
370 char buf[BUFSIZ];
371 uri_t *uri = NULL;
372 char *s = NULL;
373
374 /* was there a service_uri? */
375 if (svc_name == NULL)
376 return (0);
377
378 if ((list = getprinterbyname(svc_name, NULL)) == NULL)
379 return (0); /* if it doesnt' resolve, we will fail later */
380
381 papiAttributeListGetString(list, NULL, "printer-uri-supported", &s);
382 if ((s == NULL) || (strcasecmp(svc_name, s) != 0))
383 return (0); /* they don't match */
384
385 /* is it in uri form? */
386 if (uri_from_string(s, &uri) < 0)
387 return (0);
388
389 if ((uri == NULL) || (uri->scheme == NULL) || (uri->host == NULL)) {
390 uri_free(uri);
391 return (0);
392 }
393
394 /* is it ipp form */
395 if (strcasecmp(uri->scheme, "ipp") != 0) {
396 uri_free(uri);
397 return (0);
398 }
399
400 /* does the host match up */
401 if (is_localhost(uri->host) != 0) {
402 uri_free(uri);
403 return (0);
404 }
405
406 /* does the port match our own */
407 if (((uri->port == NULL) && (port != 631)) ||
408 ((uri->port != NULL) && (atoi(uri->port) != port))) {
409 uri_free(uri);
410 return (0);
411 }
412
413 uri_free(uri);
414
415 return (1);
416 }
417
418 static papi_status_t
419 print_service_connect(papi_service_t *svc, papi_attribute_t **request,
420 papi_attribute_t ***response)
421 {
422 papi_status_t status;
423 papi_attribute_t **operational = NULL;
424 char *printer_uri = NULL;
425 char *svc_name = NULL;
426 char *user = NULL;
427 int port = 631;
428
429 /* Get the operational attributes group from the request */
430 (void) papiAttributeListGetCollection(request, NULL,
431 "operational-attributes-group", &operational);
432
433 /* get the user name */
434 (void) papiAttributeListGetString(request, NULL, "default-user", &user);
435 (void) papiAttributeListGetString(operational, NULL,
436 "requesting-user-name", &user);
437
438 /* get the printer or service name */
439 (void) papiAttributeListGetString(request, NULL,
440 "default-service", &svc_name);
441 get_printer_id(operational, &svc_name, NULL);
442
443 /* get the port that we are listening on */
444 (void) papiAttributeListGetInteger(request, NULL, "uri-port", &port);
445
446 if (cyclical_service_check(svc_name, port) != 0) {
447 status = PAPI_NOT_POSSIBLE;
448 ipp_set_status(response, status, "printer-uri is cyclical");
449 return (status);
450 }
451
452 status = papiServiceCreate(svc, svc_name, user, NULL, NULL,
453 PAPI_ENCRYPT_NEVER, NULL);
454 if (status != PAPI_OK) {
455 ipp_set_status(response, status, "print service: %s",
456 papiStatusString(status));
457 return (status);
458 }
459
460 /*
461 * Trusted Solaris can't be trusting of intermediaries. Pass
462 * the socket connection to the print service to retrieve the
463 * sensativity label off of a multi-level port.
464 */
465 {
466 int fd = -1;
467
468 (void) papiAttributeListGetInteger(request, NULL,
469 "peer-socket", &fd);
470 if (fd != -1)
471 papiServiceSetPeer(*svc, fd);
472 }
473
474 return (status);
475 }
476
477 papi_status_t
478 ipp_process_request(papi_attribute_t **request, papi_attribute_t ***response,
479 ipp_reader_t iread, void *fd)
480 {
481 papi_status_t result = PAPI_OK;
482
483 ipp_initialize_response(request, response);
484
485 #ifdef DEBUG
486 fprintf(stderr, "REQUEST:");
487 papiAttributeListPrint(stderr, request, " %d ", getpid());
488 fprintf(stderr, "\n");
489 #endif
490
491 /* verify that the request is "well-formed" */
492 if ((result = ipp_validate_request(request, response)) == PAPI_OK) {
493 papi_service_t svc = NULL;
494 ipp_handler_t *handler;
495
496 result = print_service_connect(&svc, request, response);
497 handler = ipp_operation_handler(request, response);
498
499 /* process the request */
500 if ((result == PAPI_OK) && (handler != NULL))
501 result = (handler)(svc, request, response, iread, fd);
502 #ifdef DEBUG
503 fprintf(stderr, "RESULT: %s\n", papiStatusString(result));
504 #endif
505 papiServiceDestroy(svc);
506 }
507
508 (void) papiAttributeListAddInteger(response, PAPI_ATTR_EXCL,
509 "status-code", result);
510 massage_response(request, *response);
511
512 #ifdef DEBUG
513 fprintf(stderr, "RESPONSE:");
514 papiAttributeListPrint(stderr, *response, " %d ", getpid());
515 fprintf(stderr, "\n");
516 #endif
517
518 return (result);
519 }