1*af27b3ccSclaudio /* $OpenBSD: frontend_lpr.c,v 1.5 2024/11/21 13:34:51 claudio Exp $ */ 23b188dabSeric 33b188dabSeric /* 43b188dabSeric * Copyright (c) 2017 Eric Faurot <eric@openbsd.org> 53b188dabSeric * 63b188dabSeric * Permission to use, copy, modify, and distribute this software for any 73b188dabSeric * purpose with or without fee is hereby granted, provided that the above 83b188dabSeric * copyright notice and this permission notice appear in all copies. 93b188dabSeric * 103b188dabSeric * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 113b188dabSeric * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 123b188dabSeric * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 133b188dabSeric * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 143b188dabSeric * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 153b188dabSeric * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 163b188dabSeric * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 173b188dabSeric */ 183b188dabSeric 193b188dabSeric #include <sys/types.h> 203b188dabSeric #include <sys/socket.h> 213b188dabSeric #include <netinet/in.h> 223b188dabSeric 233b188dabSeric #include <ctype.h> 243b188dabSeric #include <errno.h> 253b188dabSeric #include <limits.h> 263b188dabSeric #include <netdb.h> 273b188dabSeric #include <stdarg.h> 283b188dabSeric #include <stdlib.h> 293b188dabSeric #include <stdio.h> 303b188dabSeric #include <string.h> 313b188dabSeric #include <unistd.h> 323b188dabSeric 333b188dabSeric #include "lpd.h" 343b188dabSeric #include "lp.h" 353b188dabSeric 363b188dabSeric #include "io.h" 373b188dabSeric #include "log.h" 383b188dabSeric #include "proc.h" 393b188dabSeric 403b188dabSeric #define SERVER_TIMEOUT 30000 413b188dabSeric #define CLIENT_TIMEOUT 5000 423b188dabSeric 433b188dabSeric #define MAXARG 50 443b188dabSeric 453b188dabSeric #define F_ZOMBIE 0x1 463b188dabSeric #define F_WAITADDRINFO 0x2 473b188dabSeric 483b188dabSeric #define STATE_READ_COMMAND 0 493b188dabSeric #define STATE_READ_FILE 1 503b188dabSeric 513b188dabSeric struct lpr_conn { 523b188dabSeric SPLAY_ENTRY(lpr_conn) entry; 533b188dabSeric uint32_t id; 543b188dabSeric char hostname[NI_MAXHOST]; 553b188dabSeric struct io *io; 563b188dabSeric int state; 573b188dabSeric int flags; 583b188dabSeric int recvjob; 593b188dabSeric int recvcf; 603b188dabSeric size_t expect; 613b188dabSeric FILE *ofp; /* output file when receiving data */ 623b188dabSeric int ifd; /* input file for displayq/rmjob */ 633b188dabSeric 643b188dabSeric char *cmd; 653b188dabSeric int ai_done; 663b188dabSeric struct addrinfo *ai; 673b188dabSeric struct io *iofwd; 683b188dabSeric }; 693b188dabSeric 703b188dabSeric SPLAY_HEAD(lpr_conn_tree, lpr_conn); 713b188dabSeric 723b188dabSeric static int lpr_conn_cmp(struct lpr_conn *, struct lpr_conn *); 733b188dabSeric SPLAY_PROTOTYPE(lpr_conn_tree, lpr_conn, entry, lpr_conn_cmp); 743b188dabSeric 753b188dabSeric static void lpr_on_allowedhost(struct lpr_conn *, const char *, const char *); 763b188dabSeric static void lpr_on_recvjob(struct lpr_conn *, int); 773b188dabSeric static void lpr_on_recvjob_file(struct lpr_conn *, int, size_t, int, int); 783b188dabSeric static void lpr_on_request(struct lpr_conn *, int, const char *, const char *); 793b188dabSeric static void lpr_on_getaddrinfo(void *, int, struct addrinfo *); 803b188dabSeric 813b188dabSeric static void lpr_io_dispatch(struct io *, int, void *); 823b188dabSeric static int lpr_readcommand(struct lpr_conn *); 833b188dabSeric static int lpr_readfile(struct lpr_conn *); 843b188dabSeric static int lpr_parsejobfilter(struct lpr_conn *, struct lp_jobfilter *, 853b188dabSeric int, char **); 863b188dabSeric 873b188dabSeric static void lpr_free(struct lpr_conn *); 883b188dabSeric static void lpr_close(struct lpr_conn *); 893b188dabSeric static void lpr_ack(struct lpr_conn *, char); 903b188dabSeric static void lpr_reply(struct lpr_conn *, const char *); 913b188dabSeric static void lpr_stream(struct lpr_conn *); 923b188dabSeric static void lpr_forward(struct lpr_conn *); 933b188dabSeric 943b188dabSeric static void lpr_iofwd_dispatch(struct io *, int, void *); 953b188dabSeric 963b188dabSeric static struct lpr_conn_tree conns; 973b188dabSeric 983b188dabSeric void 993b188dabSeric lpr_init(void) 1003b188dabSeric { 1013b188dabSeric SPLAY_INIT(&conns); 1023b188dabSeric } 1033b188dabSeric 1043b188dabSeric void 1053b188dabSeric lpr_conn(uint32_t connid, struct listener *l, int sock, 1063b188dabSeric const struct sockaddr *sa) 1073b188dabSeric { 1083b188dabSeric struct lpr_conn *conn; 1093b188dabSeric 1103b188dabSeric if ((conn = calloc(1, sizeof(*conn))) == NULL) { 1113b188dabSeric log_warn("%s: calloc", __func__); 1123b188dabSeric close(sock); 1133b188dabSeric frontend_conn_closed(connid); 1143b188dabSeric return; 1153b188dabSeric } 1163b188dabSeric conn->id = connid; 1173b188dabSeric conn->ifd = -1; 1183b188dabSeric conn->io = io_new(); 1193b188dabSeric if (conn->io == NULL) { 1203b188dabSeric log_warn("%s: io_new", __func__); 1213b188dabSeric free(conn); 1223b188dabSeric close(sock); 1233b188dabSeric frontend_conn_closed(connid); 1243b188dabSeric return; 1253b188dabSeric } 1263b188dabSeric SPLAY_INSERT(lpr_conn_tree, &conns, conn); 1273b188dabSeric io_set_callback(conn->io, lpr_io_dispatch, conn); 1283b188dabSeric io_set_timeout(conn->io, CLIENT_TIMEOUT); 1293b188dabSeric io_set_write(conn->io); 1303b188dabSeric io_attach(conn->io, sock); 1313b188dabSeric 1323b188dabSeric conn->state = STATE_READ_COMMAND; 1333b188dabSeric m_create(p_engine, IMSG_LPR_ALLOWEDHOST, conn->id, 0, -1); 1343b188dabSeric m_add_sockaddr(p_engine, sa); 1353b188dabSeric m_close(p_engine); 1363b188dabSeric } 1373b188dabSeric 1383b188dabSeric void 1393b188dabSeric lpr_dispatch_engine(struct imsgproc *proc, struct imsg *imsg) 1403b188dabSeric { 1413b188dabSeric struct lpr_conn *conn = NULL, key; 1423b188dabSeric const char *hostname, *reject, *cmd; 1433b188dabSeric size_t sz; 1443b188dabSeric int ack, cf = 0; 1453b188dabSeric 1463b188dabSeric key.id = imsg->hdr.peerid; 1473b188dabSeric if (key.id) { 1483b188dabSeric conn = SPLAY_FIND(lpr_conn_tree, &conns, &key); 1493b188dabSeric if (conn == NULL) { 1503b188dabSeric log_debug("%08x dead-session", key.id); 1513b188dabSeric return; 1523b188dabSeric } 1533b188dabSeric } 1543b188dabSeric 1553b188dabSeric switch (imsg->hdr.type) { 1563b188dabSeric case IMSG_LPR_ALLOWEDHOST: 1573b188dabSeric m_get_string(proc, &hostname); 1583b188dabSeric m_get_string(proc, &reject); 1593b188dabSeric m_end(proc); 1609d855d3dSeric lpr_on_allowedhost(conn, hostname, reject); 1613b188dabSeric break; 1623b188dabSeric 1633b188dabSeric case IMSG_LPR_RECVJOB: 1643b188dabSeric m_get_int(proc, &ack); 1653b188dabSeric m_end(proc); 1663b188dabSeric lpr_on_recvjob(conn, ack); 1673b188dabSeric break; 1683b188dabSeric 1693b188dabSeric case IMSG_LPR_RECVJOB_CF: 1703b188dabSeric cf = 1; 1713b188dabSeric case IMSG_LPR_RECVJOB_DF: 1723b188dabSeric m_get_int(proc, &ack); 1733b188dabSeric m_get_size(proc, &sz); 1743b188dabSeric m_end(proc); 175*af27b3ccSclaudio lpr_on_recvjob_file(conn, ack, sz, cf, imsg_get_fd(imsg)); 1763b188dabSeric break; 1773b188dabSeric 1783b188dabSeric case IMSG_LPR_DISPLAYQ: 1793b188dabSeric case IMSG_LPR_RMJOB: 1803b188dabSeric m_get_string(proc, &hostname); 1813b188dabSeric m_get_string(proc, &cmd); 1823b188dabSeric m_end(proc); 183*af27b3ccSclaudio lpr_on_request(conn, imsg_get_fd(imsg), hostname, cmd); 1843b188dabSeric break; 1853b188dabSeric 1863b188dabSeric default: 1873b188dabSeric fatalx("%s: unexpected imsg %s", __func__, 1883b188dabSeric log_fmt_imsgtype(imsg->hdr.type)); 1893b188dabSeric } 1903b188dabSeric } 1913b188dabSeric 1923b188dabSeric static void 1933b188dabSeric lpr_on_allowedhost(struct lpr_conn *conn, const char *hostname, 1943b188dabSeric const char *reject) 1953b188dabSeric { 1963b188dabSeric strlcpy(conn->hostname, hostname, sizeof(conn->hostname)); 1973b188dabSeric if (reject) 1983b188dabSeric lpr_reply(conn, reject); 1993b188dabSeric else 2003b188dabSeric io_set_read(conn->io); 2013b188dabSeric } 2023b188dabSeric 2033b188dabSeric static void 2043b188dabSeric lpr_on_recvjob(struct lpr_conn *conn, int ack) 2053b188dabSeric { 2063b188dabSeric if (ack == LPR_ACK) 2073b188dabSeric conn->recvjob = 1; 2083b188dabSeric else 2093b188dabSeric log_debug("%08x recvjob failed", conn->id); 2103b188dabSeric lpr_ack(conn, ack); 2113b188dabSeric } 2123b188dabSeric 2133b188dabSeric static void 2143b188dabSeric lpr_on_recvjob_file(struct lpr_conn *conn, int ack, size_t sz, int cf, int fd) 2153b188dabSeric { 2163b188dabSeric if (ack != LPR_ACK) { 2173b188dabSeric lpr_ack(conn, ack); 2183b188dabSeric return; 2193b188dabSeric } 2203b188dabSeric 2213b188dabSeric if (fd == -1) { 2223b188dabSeric log_warnx("%s: failed to get fd", __func__); 2233b188dabSeric lpr_ack(conn, LPR_NACK); 2243b188dabSeric return; 2253b188dabSeric } 2263b188dabSeric 2273b188dabSeric conn->ofp = fdopen(fd, "w"); 2283b188dabSeric if (conn->ofp == NULL) { 2293b188dabSeric log_warn("%s: fdopen", __func__); 2303b188dabSeric close(fd); 2313b188dabSeric lpr_ack(conn, LPR_NACK); 2323b188dabSeric return; 2333b188dabSeric } 2343b188dabSeric 2353b188dabSeric conn->expect = sz; 2363b188dabSeric if (cf) 2373b188dabSeric conn->recvcf = cf; 2383b188dabSeric conn->state = STATE_READ_FILE; 2393b188dabSeric 2403b188dabSeric lpr_ack(conn, LPR_ACK); 2413b188dabSeric } 2423b188dabSeric 2433b188dabSeric static void 2443b188dabSeric lpr_on_request(struct lpr_conn *conn, int fd, const char *hostname, 2453b188dabSeric const char *cmd) 2463b188dabSeric { 2473b188dabSeric struct addrinfo hints; 2483b188dabSeric 2493b188dabSeric if (fd == -1) { 2503b188dabSeric log_warnx("%s: no fd received", __func__); 2513b188dabSeric lpr_close(conn); 2523b188dabSeric return; 2533b188dabSeric } 2543b188dabSeric 2553b188dabSeric log_debug("%08x stream init", conn->id); 2563b188dabSeric conn->ifd = fd; 2573b188dabSeric 2583b188dabSeric /* Prepare for command forwarding if necessary. */ 2593b188dabSeric if (cmd) { 2603b188dabSeric log_debug("%08x forwarding to %s: \\%d%s", conn->id, hostname, 2613b188dabSeric cmd[0], cmd + 1); 2623b188dabSeric conn->cmd = strdup(cmd); 2633b188dabSeric if (conn->cmd == NULL) 2643b188dabSeric log_warn("%s: strdup", __func__); 2653b188dabSeric else { 2663b188dabSeric memset(&hints, 0, sizeof(hints)); 2673b188dabSeric hints.ai_socktype = SOCK_STREAM; 2683b188dabSeric conn->flags |= F_WAITADDRINFO; 2693b188dabSeric /* 2703a50f0a9Sjmc * The callback might run immediately, so conn->ifd 2713b188dabSeric * must be set before, to block lpr_forward(). 2723b188dabSeric */ 2733b188dabSeric resolver_getaddrinfo(hostname, "printer", &hints, 2743b188dabSeric lpr_on_getaddrinfo, conn); 2753b188dabSeric } 2763b188dabSeric } 2773b188dabSeric 2783b188dabSeric lpr_stream(conn); 2793b188dabSeric } 2803b188dabSeric 2813b188dabSeric static void 2823b188dabSeric lpr_on_getaddrinfo(void *arg, int r, struct addrinfo *ai) 2833b188dabSeric { 2843b188dabSeric struct lpr_conn *conn = arg; 2853b188dabSeric 2863b188dabSeric conn->flags &= ~F_WAITADDRINFO; 2873b188dabSeric if (conn->flags & F_ZOMBIE) { 2883b188dabSeric if (ai) 2893b188dabSeric freeaddrinfo(ai); 2903b188dabSeric lpr_free(conn); 2913b188dabSeric } 2923b188dabSeric else { 2933b188dabSeric conn->ai_done = 1; 2943b188dabSeric conn->ai = ai; 2953b188dabSeric lpr_forward(conn); 2963b188dabSeric } 2973b188dabSeric } 2983b188dabSeric 2993b188dabSeric static void 3003b188dabSeric lpr_io_dispatch(struct io *io, int evt, void *arg) 3013b188dabSeric { 3023b188dabSeric struct lpr_conn *conn = arg; 3033b188dabSeric int r; 3043b188dabSeric 3053b188dabSeric switch (evt) { 3063b188dabSeric case IO_DATAIN: 3073b188dabSeric switch(conn->state) { 3083b188dabSeric case STATE_READ_COMMAND: 3093b188dabSeric r = lpr_readcommand(conn); 3103b188dabSeric break; 3113b188dabSeric case STATE_READ_FILE: 3123b188dabSeric r = lpr_readfile(conn); 3133b188dabSeric break; 3143b188dabSeric default: 3153b188dabSeric fatal("%s: unexpected state %d", __func__, conn->state); 3163b188dabSeric } 3173b188dabSeric 3183b188dabSeric if (r == 0) 3193b188dabSeric io_set_write(conn->io); 3203b188dabSeric return; 3213b188dabSeric 3223b188dabSeric case IO_LOWAT: 3233b188dabSeric if (conn->recvjob) 3243b188dabSeric io_set_read(conn->io); 3253b188dabSeric else if (conn->ifd != -1) 3263b188dabSeric lpr_stream(conn); 3273b188dabSeric else if (conn->cmd == NULL) 3283b188dabSeric lpr_close(conn); 3293b188dabSeric return; 3303b188dabSeric 3313b188dabSeric case IO_DISCONNECTED: 3323b188dabSeric log_debug("%08x disconnected", conn->id); 3333b188dabSeric /* 3343b188dabSeric * Some clients don't wait for the last acknowledgment to close 3353b188dabSeric * the session. So just consider it is closed normally. 3363b188dabSeric */ 3373b188dabSeric case IO_CLOSED: 3383b188dabSeric if (conn->recvcf && conn->state == STATE_READ_COMMAND) { 3393b188dabSeric /* 3403b188dabSeric * Commit the transaction if we received a control file 3413b188dabSeric * and the last file was received correctly. 3423b188dabSeric */ 3433b188dabSeric m_compose(p_engine, IMSG_LPR_RECVJOB_COMMIT, conn->id, 3443b188dabSeric 0, -1, NULL, 0); 3453b188dabSeric conn->recvjob = 0; 3463b188dabSeric } 3473b188dabSeric break; 3483b188dabSeric 3493b188dabSeric case IO_TIMEOUT: 3503b188dabSeric log_debug("%08x timeout", conn->id); 3513b188dabSeric break; 3523b188dabSeric 3533b188dabSeric case IO_ERROR: 3543b188dabSeric log_debug("%08x io-error", conn->id); 3553b188dabSeric break; 3563b188dabSeric 3573b188dabSeric default: 3583b188dabSeric fatalx("%s: unexpected event %d", __func__, evt); 3593b188dabSeric } 3603b188dabSeric 3613b188dabSeric lpr_close(conn); 3623b188dabSeric } 3633b188dabSeric 3643b188dabSeric static int 3653b188dabSeric lpr_readcommand(struct lpr_conn *conn) 3663b188dabSeric { 3673b188dabSeric struct lp_jobfilter jf; 3683b188dabSeric size_t count; 3693b188dabSeric const char *errstr; 3703b188dabSeric char *argv[MAXARG], *line; 3713b188dabSeric int i, argc, cmd; 3723b188dabSeric 3733b188dabSeric line = io_getline(conn->io, NULL); 3743b188dabSeric if (line == NULL) { 3753b188dabSeric if (io_datalen(conn->io) >= LPR_MAXCMDLEN) { 3763b188dabSeric lpr_reply(conn, "Request line too long"); 3773b188dabSeric return 0; 3783b188dabSeric } 3793b188dabSeric return -1; 3803b188dabSeric } 3813b188dabSeric 3823b188dabSeric cmd = line[0]; 3833b188dabSeric line++; 3843b188dabSeric 3853b188dabSeric if (cmd == 0) { 3863b188dabSeric lpr_reply(conn, "No command"); 3873b188dabSeric return 0; 3883b188dabSeric } 3893b188dabSeric 3903b188dabSeric log_debug("%08x cmd \\%d", conn->id, cmd); 3913b188dabSeric 3923b188dabSeric /* Parse the command. */ 3933b188dabSeric for (argc = 0; argc < MAXARG; ) { 3943b188dabSeric argv[argc] = strsep(&line, " \t"); 3953b188dabSeric if (argv[argc] == NULL) 3963b188dabSeric break; 3973b188dabSeric if (argv[argc][0] != '\0') 3983b188dabSeric argc++; 3993b188dabSeric } 4003b188dabSeric if (argc == MAXARG) { 4013b188dabSeric lpr_reply(conn, "Argument list too long"); 4023b188dabSeric return 0; 4033b188dabSeric } 4043b188dabSeric 4053b188dabSeric if (argc == 0) { 4063b188dabSeric lpr_reply(conn, "No queue specified"); 4073b188dabSeric return 0; 4083b188dabSeric } 4093b188dabSeric 4103b188dabSeric #define CMD(c) ((int)(c)) 4113b188dabSeric #define SUBCMD(c) (0x100 | (int)(c)) 4123b188dabSeric 4133b188dabSeric if (conn->recvjob) 4143b188dabSeric cmd |= 0x100; 4153b188dabSeric switch (cmd) { 4163b188dabSeric case CMD('\1'): /* PRINT <prn> */ 4173b188dabSeric m_create(p_engine, IMSG_LPR_PRINTJOB, 0, 0, -1); 4183b188dabSeric m_add_string(p_engine, argv[0]); 4193b188dabSeric m_close(p_engine); 4203b188dabSeric lpr_ack(conn, LPR_ACK); 4213b188dabSeric return 0; 4223b188dabSeric 4233b188dabSeric case CMD('\2'): /* RECEIVE JOB <prn> */ 4243b188dabSeric m_create(p_engine, IMSG_LPR_RECVJOB, conn->id, 0, -1); 4253b188dabSeric m_add_string(p_engine, conn->hostname); 4263b188dabSeric m_add_string(p_engine, argv[0]); 4273b188dabSeric m_close(p_engine); 4283b188dabSeric return 0; 4293b188dabSeric 4303b188dabSeric case CMD('\3'): /* QUEUE STATE SHORT <prn> [job#...] [user..] */ 4313b188dabSeric case CMD('\4'): /* QUEUE STATE LONG <prn> [job#...] [user..] */ 4323b188dabSeric if (lpr_parsejobfilter(conn, &jf, argc - 1, argv + 1) == -1) 4333b188dabSeric return 0; 4343b188dabSeric 4353b188dabSeric m_create(p_engine, IMSG_LPR_DISPLAYQ, conn->id, 0, -1); 4363b188dabSeric m_add_int(p_engine, (cmd == '\3') ? 0 : 1); 4373b188dabSeric m_add_string(p_engine, conn->hostname); 4383b188dabSeric m_add_string(p_engine, argv[0]); 4393b188dabSeric m_add_int(p_engine, jf.njob); 4403b188dabSeric for (i = 0; i < jf.njob; i++) 4413b188dabSeric m_add_int(p_engine, jf.jobs[i]); 4423b188dabSeric m_add_int(p_engine, jf.nuser); 4433b188dabSeric for (i = 0; i < jf.nuser; i++) 4443b188dabSeric m_add_string(p_engine, jf.users[i]); 4453b188dabSeric m_close(p_engine); 4463b188dabSeric return 0; 4473b188dabSeric 4483b188dabSeric case CMD('\5'): /* REMOVE JOBS <prn> <agent> [job#...] [user..] */ 4493b188dabSeric if (argc < 2) { 4503b188dabSeric lpr_reply(conn, "No agent specified"); 4513b188dabSeric return 0; 4523b188dabSeric } 4533b188dabSeric if (lpr_parsejobfilter(conn, &jf, argc - 2, argv + 2) == -1) 4543b188dabSeric return 0; 4553b188dabSeric 4563b188dabSeric m_create(p_engine, IMSG_LPR_RMJOB, conn->id, 0, -1); 4573b188dabSeric m_add_string(p_engine, conn->hostname); 4583b188dabSeric m_add_string(p_engine, argv[0]); 4593b188dabSeric m_add_string(p_engine, argv[1]); 4603b188dabSeric m_add_int(p_engine, jf.njob); 4613b188dabSeric for (i = 0; i < jf.njob; i++) 4623b188dabSeric m_add_int(p_engine, jf.jobs[i]); 4633b188dabSeric m_add_int(p_engine, jf.nuser); 4643b188dabSeric for (i = 0; i < jf.nuser; i++) 4653b188dabSeric m_add_string(p_engine, jf.users[i]); 4663b188dabSeric m_close(p_engine); 4673b188dabSeric return 0; 4683b188dabSeric 4693b188dabSeric case SUBCMD('\1'): /* ABORT */ 4703b188dabSeric m_compose(p_engine, IMSG_LPR_RECVJOB_CLEAR, conn->id, 0, -1, 4713b188dabSeric NULL, 0); 4723b188dabSeric conn->recvcf = 0; 4733b188dabSeric lpr_ack(conn, LPR_ACK); 4743b188dabSeric return 0; 4753b188dabSeric 4763b188dabSeric case SUBCMD('\2'): /* CONTROL FILE <size> <filename> */ 4773b188dabSeric case SUBCMD('\3'): /* DATA FILE <size> <filename> */ 4783b188dabSeric if (argc != 2) { 4793b188dabSeric log_debug("%08x invalid number of argument", conn->id); 4803b188dabSeric lpr_ack(conn, LPR_NACK); 4813b188dabSeric return 0; 4823b188dabSeric } 4833b188dabSeric errstr = NULL; 4843b188dabSeric count = strtonum(argv[0], 1, LPR_MAXFILESIZE, &errstr); 4853b188dabSeric if (errstr) { 4863b188dabSeric log_debug("%08x invalid file size: %s", conn->id, 4873b188dabSeric strerror(errno)); 4883b188dabSeric lpr_ack(conn, LPR_NACK); 4893b188dabSeric return 0; 4903b188dabSeric } 4913b188dabSeric 4923b188dabSeric if (cmd == SUBCMD('\2')) { 4933b188dabSeric if (conn->recvcf) { 4943b188dabSeric log_debug("%08x cf file already received", 4953b188dabSeric conn->id); 4963b188dabSeric lpr_ack(conn, LPR_NACK); 4973b188dabSeric return 0; 4983b188dabSeric } 4993b188dabSeric m_create(p_engine, IMSG_LPR_RECVJOB_CF, conn->id, 0, 5003b188dabSeric -1); 5013b188dabSeric } 5023b188dabSeric else 5033b188dabSeric m_create(p_engine, IMSG_LPR_RECVJOB_DF, conn->id, 0, 5043b188dabSeric -1); 5053b188dabSeric m_add_size(p_engine, count); 5063b188dabSeric m_add_string(p_engine, argv[1]); 5073b188dabSeric m_close(p_engine); 5083b188dabSeric return 0; 5093b188dabSeric 5103b188dabSeric default: 5113b188dabSeric if (conn->recvjob) 5123b188dabSeric lpr_reply(conn, "Protocol error"); 5133b188dabSeric else 5143b188dabSeric lpr_reply(conn, "Illegal service request"); 5153b188dabSeric return 0; 5163b188dabSeric } 5173b188dabSeric } 5183b188dabSeric 5193b188dabSeric static int 5203b188dabSeric lpr_readfile(struct lpr_conn *conn) 5213b188dabSeric { 5223b188dabSeric size_t len, w; 5233b188dabSeric char *data; 5243b188dabSeric 5253b188dabSeric if (conn->expect) { 5263b188dabSeric /* Read file content. */ 5273b188dabSeric data = io_data(conn->io); 5283b188dabSeric len = io_datalen(conn->io); 5293b188dabSeric if (len > conn->expect) 5303b188dabSeric len = conn->expect; 5313b188dabSeric 5323b188dabSeric log_debug("%08x %zu bytes received", conn->id, len); 5333b188dabSeric 5343b188dabSeric w = fwrite(data, 1, len, conn->ofp); 5353b188dabSeric if (w != len) { 5363b188dabSeric log_warnx("%s: fwrite", __func__); 5373b188dabSeric lpr_close(conn); 5383b188dabSeric return -1; 5393b188dabSeric } 5403b188dabSeric io_drop(conn->io, w); 5413b188dabSeric conn->expect -= w; 5423b188dabSeric if (conn->expect) 5433b188dabSeric return -1; 5443b188dabSeric 5453b188dabSeric fclose(conn->ofp); 5463b188dabSeric conn->ofp = NULL; 5473b188dabSeric 5483b188dabSeric log_debug("%08x file received", conn->id); 5493b188dabSeric } 5503b188dabSeric 5513b188dabSeric /* Try to read '\0'. */ 5523b188dabSeric len = io_datalen(conn->io); 5533b188dabSeric if (len == 0) 5543b188dabSeric return -1; 5553b188dabSeric data = io_data(conn->io); 5563b188dabSeric io_drop(conn->io, 1); 5573b188dabSeric 5583b188dabSeric log_debug("%08x eof %d", conn->id, (int)*data); 5593b188dabSeric 5603b188dabSeric if (*data != '\0') { 5613b188dabSeric lpr_close(conn); 5623b188dabSeric return -1; 5633b188dabSeric } 5643b188dabSeric 5653b188dabSeric conn->state = STATE_READ_COMMAND; 5663b188dabSeric lpr_ack(conn, LPR_ACK); 5673b188dabSeric return 0; 5683b188dabSeric } 5693b188dabSeric 5703b188dabSeric static int 5713b188dabSeric lpr_parsejobfilter(struct lpr_conn *conn, struct lp_jobfilter *jf, int argc, 5723b188dabSeric char **argv) 5733b188dabSeric { 5743b188dabSeric const char *errstr; 5753b188dabSeric char *arg; 5763b188dabSeric int i, jobnum; 5773b188dabSeric 5783b188dabSeric memset(jf, 0, sizeof(*jf)); 5793b188dabSeric 5803b188dabSeric for (i = 0; i < argc; i++) { 5813b188dabSeric arg = argv[i]; 582895f7d11Sbenno if (isdigit((unsigned char)arg[0])) { 5833b188dabSeric if (jf->njob == LP_MAXREQUESTS) { 5843b188dabSeric lpr_reply(conn, "Too many requests"); 5853b188dabSeric return -1; 5863b188dabSeric } 5873b188dabSeric errstr = NULL; 5883b188dabSeric jobnum = strtonum(arg, 0, INT_MAX, &errstr); 5893b188dabSeric if (errstr) { 5903b188dabSeric lpr_reply(conn, "Invalid job number"); 5913b188dabSeric return -1; 5923b188dabSeric } 5933b188dabSeric jf->jobs[jf->njob++] = jobnum; 5943b188dabSeric } 5953b188dabSeric else { 5963b188dabSeric if (jf->nuser == LP_MAXUSERS) { 5973b188dabSeric lpr_reply(conn, "Too many users"); 5983b188dabSeric return -1; 5993b188dabSeric } 6003b188dabSeric jf->users[jf->nuser++] = arg; 6013b188dabSeric } 6023b188dabSeric } 6033b188dabSeric 6043b188dabSeric return 0; 6053b188dabSeric } 6063b188dabSeric 6073b188dabSeric static void 6083b188dabSeric lpr_free(struct lpr_conn *conn) 6093b188dabSeric { 6103b188dabSeric if ((conn->flags & F_WAITADDRINFO) == 0) 6113b188dabSeric free(conn); 6123b188dabSeric } 6133b188dabSeric 6143b188dabSeric static void 6153b188dabSeric lpr_close(struct lpr_conn *conn) 6163b188dabSeric { 6173b188dabSeric uint32_t connid = conn->id; 6183b188dabSeric 6193b188dabSeric SPLAY_REMOVE(lpr_conn_tree, &conns, conn); 6203b188dabSeric 6213b188dabSeric if (conn->recvjob) 6223b188dabSeric m_compose(p_engine, IMSG_LPR_RECVJOB_ROLLBACK, conn->id, 0, -1, 6233b188dabSeric NULL, 0); 6243b188dabSeric 6253b188dabSeric io_free(conn->io); 6263b188dabSeric free(conn->cmd); 6273b188dabSeric if (conn->ofp) 6283b188dabSeric fclose(conn->ofp); 6293b188dabSeric if (conn->ifd != -1) 6303b188dabSeric close(conn->ifd); 6313b188dabSeric if (conn->ai) 6323b188dabSeric freeaddrinfo(conn->ai); 6333b188dabSeric if (conn->iofwd) 6343b188dabSeric io_free(conn->iofwd); 6353b188dabSeric 6363b188dabSeric conn->flags |= F_ZOMBIE; 6373b188dabSeric lpr_free(conn); 6383b188dabSeric 6393b188dabSeric frontend_conn_closed(connid); 6403b188dabSeric } 6413b188dabSeric 6423b188dabSeric static void 6433b188dabSeric lpr_ack(struct lpr_conn *conn, char c) 6443b188dabSeric { 6453b188dabSeric if (c == 0) 6463b188dabSeric log_debug("%08x ack", conn->id); 6473b188dabSeric else 6483b188dabSeric log_debug("%08x nack %d", conn->id, (int)c); 6493b188dabSeric 6503b188dabSeric io_write(conn->io, &c, 1); 6513b188dabSeric } 6523b188dabSeric 6533b188dabSeric static void 6543b188dabSeric lpr_reply(struct lpr_conn *conn, const char *s) 6553b188dabSeric { 6563b188dabSeric log_debug("%08x reply: %s", conn->id, s); 6573b188dabSeric 6583b188dabSeric io_printf(conn->io, "%s\n", s); 6593b188dabSeric } 6603b188dabSeric 6613b188dabSeric /* 6623a50f0a9Sjmc * Stream response file to the client. 6633b188dabSeric */ 6643b188dabSeric static void 6653b188dabSeric lpr_stream(struct lpr_conn *conn) 6663b188dabSeric { 6673b188dabSeric char buf[BUFSIZ]; 6683b188dabSeric ssize_t r; 6693b188dabSeric 6703b188dabSeric for (;;) { 6713b188dabSeric if (io_queued(conn->io) > 65536) 6723b188dabSeric return; 6733b188dabSeric 6743b188dabSeric r = read(conn->ifd, buf, sizeof(buf)); 6753b188dabSeric if (r == -1) { 6763b188dabSeric if (errno == EINTR) 6773b188dabSeric continue; 6783b188dabSeric log_warn("%s: read", __func__); 6793b188dabSeric break; 6803b188dabSeric } 6813b188dabSeric 6823b188dabSeric if (r == 0) { 6833b188dabSeric log_debug("%08x stream done", conn->id); 6843b188dabSeric break; 6853b188dabSeric } 6863b188dabSeric log_debug("%08x stream %zu bytes", conn->id, r); 6873b188dabSeric 6883b188dabSeric if (io_write(conn->io, buf, r) == -1) { 6893b188dabSeric log_warn("%s: io_write", __func__); 6903b188dabSeric break; 6913b188dabSeric } 6923b188dabSeric } 6933b188dabSeric 6943b188dabSeric close(conn->ifd); 6953b188dabSeric conn->ifd = -1; 6963b188dabSeric 6973b188dabSeric if (conn->cmd) 6983b188dabSeric lpr_forward(conn); 6993b188dabSeric 7003b188dabSeric else if (io_queued(conn->io) == 0) 7013b188dabSeric lpr_close(conn); 7023b188dabSeric } 7033b188dabSeric 7043b188dabSeric /* 7053b188dabSeric * Forward request to the remote printer. 7063b188dabSeric */ 7073b188dabSeric static void 7083b188dabSeric lpr_forward(struct lpr_conn *conn) 7093b188dabSeric { 7103b188dabSeric /* 7113b188dabSeric * Do not start forwarding the command if the address is not resolved 7123b188dabSeric * or if the local response is still being sent to the client. 7133b188dabSeric */ 7143b188dabSeric if (!conn->ai_done || conn->ifd == -1) 7153b188dabSeric return; 7163b188dabSeric 7173b188dabSeric if (conn->ai == NULL) { 7183b188dabSeric if (io_queued(conn->io) == 0) 7193b188dabSeric lpr_close(conn); 7203b188dabSeric return; 7213b188dabSeric } 7223b188dabSeric 7233b188dabSeric log_debug("%08x forward start", conn->id); 7243b188dabSeric 7253b188dabSeric conn->iofwd = io_new(); 7263b188dabSeric if (conn->iofwd == NULL) { 7273b188dabSeric log_warn("%s: io_new", __func__); 7283b188dabSeric if (io_queued(conn->io) == 0) 7293b188dabSeric lpr_close(conn); 7303b188dabSeric return; 7313b188dabSeric } 7323b188dabSeric io_set_callback(conn->iofwd, lpr_iofwd_dispatch, conn); 7333b188dabSeric io_set_timeout(conn->io, SERVER_TIMEOUT); 7343b188dabSeric io_connect(conn->iofwd, conn->ai); 7353b188dabSeric conn->ai = NULL; 7363b188dabSeric } 7373b188dabSeric 7383b188dabSeric static void 7393b188dabSeric lpr_iofwd_dispatch(struct io *io, int evt, void *arg) 7403b188dabSeric { 7413b188dabSeric struct lpr_conn *conn = arg; 7423b188dabSeric 7433b188dabSeric switch (evt) { 7443b188dabSeric case IO_CONNECTED: 7453b188dabSeric log_debug("%08x forward connected", conn->id); 7463b188dabSeric /* Send the request. */ 7473b188dabSeric io_print(io, conn->cmd); 7483b188dabSeric io_print(io, "\n"); 7493b188dabSeric io_set_write(io); 7503b188dabSeric return; 7513b188dabSeric 7523b188dabSeric case IO_DATAIN: 7533b188dabSeric /* Relay. */ 7543b188dabSeric io_write(conn->io, io_data(io), io_datalen(io)); 7553b188dabSeric io_drop(io, io_datalen(io)); 7563b188dabSeric return; 7573b188dabSeric 7583b188dabSeric case IO_LOWAT: 7593b188dabSeric /* Read response. */ 7603b188dabSeric io_set_read(io); 7613b188dabSeric return; 7623b188dabSeric 7633b188dabSeric case IO_CLOSED: 7643b188dabSeric break; 7653b188dabSeric 7663b188dabSeric case IO_DISCONNECTED: 7673b188dabSeric log_debug("%08x forward disconnected", conn->id); 7683b188dabSeric break; 7693b188dabSeric 7703b188dabSeric case IO_TIMEOUT: 7713b188dabSeric log_debug("%08x forward timeout", conn->id); 7723b188dabSeric break; 7733b188dabSeric 7743b188dabSeric case IO_ERROR: 7753b188dabSeric log_debug("%08x forward io-error", conn->id); 7763b188dabSeric break; 7773b188dabSeric 7783b188dabSeric default: 7793b188dabSeric fatalx("%s: unexpected event %d", __func__, evt); 7803b188dabSeric } 7813b188dabSeric 7823b188dabSeric log_debug("%08x forward done", conn->id); 7833b188dabSeric 7843b188dabSeric io_free(io); 7853b188dabSeric free(conn->cmd); 7863b188dabSeric conn->cmd = NULL; 7873b188dabSeric conn->iofwd = NULL; 7883b188dabSeric if (io_queued(conn->io) == 0) 7893b188dabSeric lpr_close(conn); 7903b188dabSeric } 7913b188dabSeric 7923b188dabSeric static int 7933b188dabSeric lpr_conn_cmp(struct lpr_conn *a, struct lpr_conn *b) 7943b188dabSeric { 7953b188dabSeric if (a->id < b->id) 7963b188dabSeric return -1; 7973b188dabSeric if (a->id > b->id) 7983b188dabSeric return 1; 7993b188dabSeric return 0; 8003b188dabSeric } 8013b188dabSeric 8023b188dabSeric SPLAY_GENERATE(lpr_conn_tree, lpr_conn, entry, lpr_conn_cmp); 803