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 * Copyright 2008 Sun Microsystems, Inc. All rights reserved. 23 * Use is subject to license terms. 24 */ 25 26 #pragma ident "%Z%%M% %I% %E% SMI" 27 28 #include <stdio.h> 29 #include <stdlib.h> 30 #include <libintl.h> 31 #include <locale.h> 32 #include <signal.h> 33 #include <errno.h> 34 #include <string.h> 35 #include <unistd.h> 36 #include <sys/mman.h> 37 #include <sys/socket.h> 38 #include <netinet/in.h> 39 #include <arpa/inet.h> 40 #include <netdb.h> 41 #include <fcntl.h> 42 #include <syslog.h> 43 #include <sys/utsname.h> 44 #include "netpr.h" 45 46 47 static void usage_exit(); 48 49 static void pipehandler(int); 50 char data_file_type = 0; 51 52 /* 53 * null() is to be used as a signal handler that does nothing. It is used in 54 * place of SIG_IGN, because we want the signal to be delivered and 55 * interupt the current system call. 56 */ 57 static void 58 null(int i) 59 { 60 syslog(LOG_DEBUG, "null(%d)", i); 61 } 62 63 /* 64 * net_open() opens a tcp connection to the printer port on the host specified 65 * in the arguments passed in. If the connection is not made in the 66 * timeout (in seconds) passed in, an error it returned. If the host is 67 * unknown, an error is returned. If all is well, a file descriptor is 68 * returned to be used for future communications. 69 */ 70 int 71 net_open(char *host, int timeout) 72 { 73 struct hostent *hp; 74 struct servent *sp; 75 struct sockaddr_in6 sin; 76 void (*old_handler)(); 77 static struct utsname uts; 78 79 int s, 80 lport, 81 err, 82 error_num; 83 unsigned timo = 1; 84 85 syslog(LOG_DEBUG, "net_open(%s, %d)", (host != NULL ? host : "NULL"), 86 timeout); 87 /* 88 * Get the host address and port number to connect to. 89 */ 90 if (host == NULL) { 91 return (-1); 92 } 93 94 (void) memset((char *)&sin, NULL, sizeof (sin)); 95 if ((hp = getipnodebyname(host, AF_INET6, AI_DEFAULT, 96 &error_num)) == NULL) { 97 syslog(LOG_DEBUG|LOG_ERR, "unknown host %s " 98 "getipnodebyname() returned %d", host, error_num); 99 return (NETWORK_ERROR_HOST); 100 } 101 (void) memcpy((caddr_t)&sin.sin6_addr, hp->h_addr, hp->h_length); 102 sin.sin6_family = hp->h_addrtype; 103 freehostent(hp); 104 105 if ((sp = getservbyname("printer", "tcp")) == NULL) { 106 syslog(LOG_DEBUG|LOG_ERR, "printer/tcp: unknown service"); 107 return (NETWORK_ERROR_SERVICE); 108 } 109 sin.sin6_port = sp->s_port; 110 111 retry: 112 /* 113 * Try connecting to the server. 114 * 115 * Use 0 as lport means that rresvport_af() will bind to a port in 116 * the anonymous privileged port range. 117 */ 118 lport = 0; 119 s = rresvport_af(&lport, AF_INET6); 120 if (s < 0) 121 return (NETWORK_ERROR_PORT); 122 123 old_handler = signal(SIGALRM, null); 124 (void) alarm(timeout); 125 if (connect(s, (struct sockaddr *)&sin, sizeof (sin)) < 0) { 126 (void) alarm(0); 127 (void) signal(SIGALRM, old_handler); 128 err = errno; 129 (void) close(s); 130 errno = err; 131 if (errno == EADDRINUSE) { 132 goto retry; 133 } 134 /* 135 * If connecting to the local system fails, try 136 * again with "localhost" address instead. 137 */ 138 if (uts.nodename[0] == '\0') 139 (void) uname(&uts); 140 if (strcmp(host, uts.nodename) == 0) { 141 IN6_IPADDR_TO_V4MAPPED(htonl(INADDR_LOOPBACK), 142 &sin.sin6_addr); 143 sin.sin6_family = AF_INET6; 144 goto retry; 145 } 146 if (errno == ECONNREFUSED && timo <= 16) { 147 (void) sleep(timo); 148 timo *= 2; 149 goto retry; 150 } 151 return (NETWORK_ERROR_UNKNOWN); 152 } 153 (void) alarm(0); 154 (void) signal(SIGALRM, old_handler); 155 return (s); 156 } 157 158 int 159 main(int argc, char *argv[]) 160 { 161 extern char *optarg; 162 extern int optind; 163 int opt; 164 np_job_t *job_data; 165 char *destination = NULL; 166 np_bsdjob_t *bsdjob; 167 np_tcpjob_t *tcpjob; 168 int sockfd; 169 int pr_order = CONTROL_FIRST; 170 char *vendor_pr_name = NULL; 171 char *tcp_port = NULL; 172 size_t filesize; 173 int fd; 174 caddr_t pa; 175 int jobstatus; 176 int exit_status = 0; 177 int on = 1; 178 179 180 (void) setlocale(LC_ALL, ""); 181 #if !defined(TEXT_DOMAIN) /* Should be defined by cc -D */ 182 #define TEXT_DOMAIN "SYS_TEST" /* Use this only if it weren't */ 183 #endif 184 (void) textdomain(TEXT_DOMAIN); 185 186 openlog("netpr", LOG_PID, LOG_LPR); 187 (void) signal(SIGPIPE, pipehandler); 188 189 /* reduce privileges until needed to open reserved port */ 190 if (seteuid(getuid())) { 191 syslog(LOG_DEBUG, "seteuid failed, exiting netpr"); 192 exit(E_FAILURE); 193 } 194 195 if ((job_data = init_job()) == NULL) { 196 fprintf(stderr, gettext("init_job(): out of memory\n")); 197 exit(E_RETRY); 198 } 199 200 while ((opt = getopt(argc, argv, "f:I:p:d:T:P:t:U:c:b")) != EOF) 201 switch (opt) { 202 case 'f': 203 data_file_type = optarg[0]; 204 break; 205 case 'I': /* foo-49 */ 206 job_data->request_id = alloc_str((char *)optarg); 207 syslog(LOG_DEBUG, "request_id: %s", 208 job_data->request_id); 209 break; 210 case 'U': /* awe172-126!wendyp */ 211 job_data->username = alloc_str((char *)optarg); 212 syslog(LOG_DEBUG, "username: %s", 213 job_data->username); 214 break; 215 case 'p': /* foo */ 216 job_data->printer = alloc_str((char *)optarg); 217 syslog(LOG_DEBUG, "printer: %s", 218 job_data->printer); 219 break; 220 case 'd': /* server for printer */ 221 job_data->dest = alloc_str((char *)optarg); 222 syslog(LOG_DEBUG, "dest: %s", 223 job_data->dest); 224 break; 225 case 'T': /* /tmp/file2 */ 226 job_data->title = alloc_str((char *)optarg); 227 syslog(LOG_DEBUG, "title: %s", 228 job_data->title); 229 break; 230 case 'P': 231 if ((strcmp(optarg, "bsd")) == 0) 232 job_data->protocol = BSD; 233 else if ((strcmp(optarg, "tcp")) == 0) 234 job_data->protocol = TCP; 235 else 236 usage_exit(); 237 238 syslog(LOG_DEBUG, "protocol: %d", 239 job_data->protocol); 240 break; 241 case 't': 242 job_data->timeout = atoi(optarg); 243 if (job_data->timeout < 0) 244 usage_exit(); 245 break; 246 case 'c': 247 if ((strcmp(optarg, "first")) == 0) 248 pr_order = CONTROL_FIRST; 249 else if ((strcmp(optarg, "last")) == 0) 250 pr_order = DATA_FIRST; 251 else 252 usage_exit(); 253 254 syslog(LOG_DEBUG, "bsd print order: %d", pr_order); 255 break; 256 case 'b': 257 job_data->banner = NOBANNER; 258 syslog(LOG_DEBUG, "banner : %d", 259 job_data->banner); 260 break; 261 case '?': 262 usage_exit(); 263 } 264 265 266 if ((job_data->dest == NULL) || (job_data->request_id == NULL) || 267 (job_data->printer == NULL) || (job_data->username == NULL)) 268 usage_exit(); 269 270 /* 271 * Check that there is a file 272 */ 273 if (optind == argc) { 274 usage_exit(); 275 } 276 277 job_data->filename = alloc_str(argv[optind]); 278 syslog(LOG_DEBUG, "filename : %s", job_data->filename); 279 280 281 /* 282 * Sanity check the file 283 * returns filesize 284 */ 285 286 if ((filesize = check_file(job_data->filename)) == -1) { 287 syslog(LOG_DEBUG, "Skipping file %s", 288 job_data->filename ? job_data->filename : "Error NULL file"); 289 290 switch (errno) { 291 case EISDIR: 292 (void) fprintf(stderr, 293 gettext("Netpr: %s: Not a regular file\n"), 294 (job_data->filename ? job_data->filename : "Noname")); 295 syslog(LOG_DEBUG, "Not a regular file"); 296 break; 297 case ESRCH: 298 (void) fprintf(stderr, 299 gettext("Netpr: %s: Empty file\n"), 300 (job_data->filename ? job_data->filename : "Noname")); 301 syslog(LOG_DEBUG, "Empty file"); 302 break; 303 default: 304 perror(job_data->filename); 305 (void) fprintf(stderr, 306 gettext("Netpr: Cannot access file %s\n"), 307 (job_data->filename ? job_data->filename : "Noname")); 308 syslog(LOG_DEBUG, "Cannot access file."); 309 break; 310 311 } 312 313 /* 314 * This file not valid, so bail 315 * Exit with zero so system will keep printing 316 */ 317 exit(0); 318 } 319 320 /* 321 * file looks ok, open and mmap it 322 */ 323 if ((fd = open(job_data->filename, O_RDONLY)) < 0) { 324 (void) fprintf(stderr, gettext("Netpr: Cannot open file %s\n"), 325 (job_data->filename ? job_data->filename : "Error: NULL file")); 326 syslog(LOG_DEBUG, "Cannot open file: %s", 327 job_data->filename ? job_data->filename : "Error NULL file"); 328 exit(E_BAD_FILE); 329 } 330 331 if ((pa = mmap((caddr_t)0, filesize, PROT_READ, 332 (MAP_SHARED | MAP_NORESERVE), fd, (off_t)0)) == MAP_FAILED) { 333 334 (void) close(fd); 335 (void) fprintf(stderr, gettext("Netpr: Cannot mmap file %s"), 336 (job_data->filename ? job_data->filename : "Error: NULL file")); 337 338 syslog(LOG_DEBUG, "Cannot mmap file: %s", 339 job_data->filename ? job_data->filename : "Error NULL file"); 340 341 exit(E_RETRY); 342 } 343 344 345 if (job_data->protocol == BSD) { 346 bsdjob = (np_bsdjob_t *) 347 create_bsd_job(job_data, pr_order, filesize); 348 if (bsdjob == NULL) 349 exit(E_FAILURE); 350 } else { 351 tcpjob = (np_tcpjob_t *)create_tcp_job(job_data, filesize); 352 if (tcpjob == NULL) 353 exit(E_FAILURE); 354 } 355 356 /* 357 * Parse destination 358 */ 359 360 if ((strpbrk(job_data->dest, DEST_SEP)) != NULL) { 361 if (job_data->protocol == BSD) { 362 parse_dest(job_data->dest, &destination, 363 &vendor_pr_name, DEST_SEP); 364 if (vendor_pr_name != NULL) { 365 bsdjob->np_printer = vendor_pr_name; 366 syslog(LOG_DEBUG, "bsd vendor name: %s", 367 bsdjob->np_printer); 368 } 369 } else { 370 parse_dest(job_data->dest, &destination, &tcp_port, 371 DEST_SEP); 372 if (tcp_port != NULL) 373 tcpjob->np_port = tcp_port; 374 syslog(LOG_DEBUG, "tcp_port %s", 375 tcpjob->np_port); 376 } 377 if (destination == NULL || 378 (job_data->protocol == TCP && tcp_port == NULL)) { 379 (void) fprintf(stderr, 380 gettext("Netpr: system error parsing destination %s\n"), 381 job_data->dest); 382 syslog(LOG_DEBUG, "system error parsing destination %s", 383 job_data->dest); 384 385 exit(E_FAILURE); 386 } 387 388 } else { 389 destination = job_data->dest; 390 } 391 syslog(LOG_DEBUG, "destination : %s", destination); 392 393 /* 394 * We are now ready to open a connection to the printer 395 * and print each of the files 396 */ 397 398 if (job_data->protocol == BSD) { 399 400 /* set privileges to get reserved port */ 401 if (seteuid(0)) { 402 syslog(LOG_DEBUG, "seteuid(0) failed, exiting netpr"); 403 exit(E_FAILURE); 404 } 405 if ((sockfd = net_open(destination, 20)) < 0) { 406 (void) fprintf(stderr, 407 gettext("Netpr: Cannot open connection to <%s>\n"), 408 destination); 409 syslog(LOG_DEBUG, 410 "Cannot open connection to %s: retrying", 411 destination); 412 exit(E_RETRY); 413 } 414 } else { 415 if ((sockfd = tcp_open(destination, tcpjob, 20)) == -1) { 416 exit(E_RETRY); 417 } 418 } 419 420 /* lower privileges as we now have the reserved port */ 421 if (setuid(getuid())) { 422 syslog(LOG_DEBUG, "setuid() failed, exiting netpr"); 423 exit(E_FAILURE); 424 } 425 426 427 /* Set SO_KEEPALIVE on socket to keep open */ 428 if ((setsockopt(sockfd, SOL_SOCKET, SO_KEEPALIVE, 429 (char *)&on, sizeof (on))) < 0) { 430 syslog(LOG_DEBUG, "setsocket (SO_KEEPALIVE): %m"); 431 } 432 433 if (job_data->protocol == BSD) { 434 if ((jobstatus = bsd_print(sockfd, pa, bsdjob)) != 0) { 435 (void) fprintf(stderr, 436 gettext("Netpr: Error return from bsd_print <%d>\n"), 437 jobstatus); 438 syslog(LOG_DEBUG, 439 "Error return from bsd_print <%d>", jobstatus); 440 exit_status = E_RETRY; 441 } 442 } else { 443 if ((jobstatus = 444 tcp_print(sockfd, pa, tcpjob)) != 0) { 445 (void) fprintf(stderr, 446 gettext("Netpr: Error return from tcp_print <%d>\n"), 447 jobstatus); 448 syslog(LOG_DEBUG, 449 "Error return from tcp_print <%d>", jobstatus); 450 exit_status = E_RETRY; 451 } 452 } 453 454 (void) close(fd); 455 (void) close(sockfd); 456 (void) munmap(pa, filesize); 457 458 syslog(LOG_DEBUG, "exit status: %d", exit_status); 459 return (exit_status); 460 } 461 462 static void 463 usage_exit() 464 { 465 (void) fprintf(stderr, 466 gettext("Usage: netpr -I request_id -p printer -d destination\n")); 467 (void) fprintf(stderr, 468 gettext("\t\t-U username [ -f type ] [ -T title ] [ -P protocol ]\n")); 469 (void) fprintf(stderr, 470 gettext("\t\t[-t timeout] [ -c ] [ -b ]\n")); 471 (void) fprintf(stderr, gettext("\t\tfiles\n")); 472 exit(E_BAD_INPUT); 473 } 474 475 /*ARGSUSED*/ 476 void 477 pipehandler(int i) 478 { 479 (void) signal(SIGPIPE, pipehandler); 480 syslog(LOG_DEBUG, "Received SIGPIPE, connection to printer broken"); 481 exit(E_SIGPIPE); 482 } 483