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 }