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
null(int i)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
net_open(char * host,int timeout)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, 0, 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
main(int argc,char * argv[])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
usage_exit()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
pipehandler(int i)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