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