]> git.ipfire.org Git - thirdparty/cups.git/blame - cups/sidechannel.c
Fix source file header text duplication text duplication.
[thirdparty/cups.git] / cups / sidechannel.c
CommitLineData
f7deaa1a 1/*
5a1d7a17 2 * Side-channel API code for CUPS.
f7deaa1a 3 *
7e86f2f6 4 * Copyright 2007-2014 by Apple Inc.
5a1d7a17 5 * Copyright 2006 by Easy Software Products.
f7deaa1a 6 *
5a1d7a17
MS
7 * These coded instructions, statements, and computer programs are the
8 * property of Apple Inc. and are protected by Federal copyright
9 * law. Distribution and use rights are outlined in the file "LICENSE.txt"
10 * which should have been included with this file. If this file is
57b7b66b 11 * missing or damaged, see the license at "http://www.cups.org/".
f7deaa1a 12 *
5a1d7a17 13 * This file is subject to the Apple OS-Developed Software exception.
f7deaa1a 14 */
15
16/*
17 * Include necessary headers...
18 */
19
20#include "sidechannel.h"
dcb445bc 21#include "cups-private.h"
536bc2c6
MS
22#ifdef WIN32
23# include <io.h>
24#else
25# include <unistd.h>
26#endif /* WIN32 */
f7deaa1a 27#ifndef WIN32
5a1d7a17 28# include <sys/select.h>
f7deaa1a 29# include <sys/time.h>
30#endif /* !WIN32 */
31#ifdef HAVE_POLL
dcb445bc 32# include <poll.h>
f7deaa1a 33#endif /* HAVE_POLL */
34
35
dcb445bc
MS
36/*
37 * Buffer size for side-channel requests...
38 */
39
40#define _CUPS_SC_MAX_DATA 65535
41#define _CUPS_SC_MAX_BUFFER 65540
42
43
f7deaa1a 44/*
45 * 'cupsSideChannelDoRequest()' - Send a side-channel command to a backend and wait for a response.
46 *
47 * This function is normally only called by filters, drivers, or port
48 * monitors in order to communicate with the backend used by the current
49 * printer. Programs must be prepared to handle timeout or "not
50 * implemented" status codes, which indicate that the backend or device
51 * do not support the specified side-channel command.
52 *
53 * The "datalen" parameter must be initialized to the size of the buffer
54 * pointed to by the "data" parameter. cupsSideChannelDoRequest() will
55 * update the value to contain the number of data bytes in the buffer.
56 *
8072030b 57 * @since CUPS 1.3/macOS 10.5@
f7deaa1a 58 */
59
60cups_sc_status_t /* O - Status of command */
61cupsSideChannelDoRequest(
62 cups_sc_command_t command, /* I - Command to send */
63 char *data, /* O - Response data buffer pointer */
64 int *datalen, /* IO - Size of data buffer on entry, number of bytes in buffer on return */
65 double timeout) /* I - Timeout in seconds */
66{
67 cups_sc_status_t status; /* Status of command */
68 cups_sc_command_t rcommand; /* Response command */
69
70
71 if (cupsSideChannelWrite(command, CUPS_SC_STATUS_NONE, NULL, 0, timeout))
72 return (CUPS_SC_STATUS_TIMEOUT);
73
74 if (cupsSideChannelRead(&rcommand, &status, data, datalen, timeout))
75 return (CUPS_SC_STATUS_TIMEOUT);
76
77 if (rcommand != command)
78 return (CUPS_SC_STATUS_BAD_MESSAGE);
79
80 return (status);
81}
82
83
84/*
85 * 'cupsSideChannelRead()' - Read a side-channel message.
86 *
87 * This function is normally only called by backend programs to read
88 * commands from a filter, driver, or port monitor program. The
89 * caller must be prepared to handle incomplete or invalid messages
90 * and return the corresponding status codes.
91 *
92 * The "datalen" parameter must be initialized to the size of the buffer
93 * pointed to by the "data" parameter. cupsSideChannelDoRequest() will
94 * update the value to contain the number of data bytes in the buffer.
95 *
8072030b 96 * @since CUPS 1.3/macOS 10.5@
f7deaa1a 97 */
98
99int /* O - 0 on success, -1 on error */
100cupsSideChannelRead(
101 cups_sc_command_t *command, /* O - Command code */
102 cups_sc_status_t *status, /* O - Status code */
103 char *data, /* O - Data buffer pointer */
104 int *datalen, /* IO - Size of data buffer on entry, number of bytes in buffer on return */
105 double timeout) /* I - Timeout in seconds */
106{
dcb445bc 107 char *buffer; /* Message buffer */
7e86f2f6 108 ssize_t bytes; /* Bytes read */
f7deaa1a 109 int templen; /* Data length from message */
ef55b745 110 int nfds; /* Number of file descriptors */
f7deaa1a 111#ifdef HAVE_POLL
112 struct pollfd pfd; /* Poll structure for poll() */
113#else /* select() */
114 fd_set input_set; /* Input set for select() */
115 struct timeval stimeout; /* Timeout value for select() */
116#endif /* HAVE_POLL */
117
118
20fbc903 119 DEBUG_printf(("cupsSideChannelRead(command=%p, status=%p, data=%p, "
e07d4801 120 "datalen=%p(%d), timeout=%.3f)", command, status, data,
20fbc903
MS
121 datalen, datalen ? *datalen : -1, timeout));
122
f7deaa1a 123 /*
124 * Range check input...
125 */
126
127 if (!command || !status)
128 return (-1);
129
130 /*
131 * See if we have pending data on the side-channel socket...
132 */
133
134#ifdef HAVE_POLL
135 pfd.fd = CUPS_SC_FD;
136 pfd.events = POLLIN;
137
82cc1f9a 138 while ((nfds = poll(&pfd, 1,
7e86f2f6 139 timeout < 0.0 ? -1 : (int)(timeout * 1000))) < 0 &&
ef55b745
MS
140 (errno == EINTR || errno == EAGAIN))
141 ;
f7deaa1a 142
143#else /* select() */
144 FD_ZERO(&input_set);
145 FD_SET(CUPS_SC_FD, &input_set);
146
ef55b745
MS
147 stimeout.tv_sec = (int)timeout;
148 stimeout.tv_usec = (int)(timeout * 1000000) % 1000000;
149
82cc1f9a 150 while ((nfds = select(CUPS_SC_FD + 1, &input_set, NULL, NULL,
ef55b745
MS
151 timeout < 0.0 ? NULL : &stimeout)) < 0 &&
152 (errno == EINTR || errno == EAGAIN))
153 ;
154
155#endif /* HAVE_POLL */
156
82cc1f9a 157 if (nfds < 1)
f7deaa1a 158 {
dcb445bc
MS
159 *command = CUPS_SC_CMD_NONE;
160 *status = nfds==0 ? CUPS_SC_STATUS_TIMEOUT : CUPS_SC_STATUS_IO_ERROR;
ef55b745 161 return (-1);
f7deaa1a 162 }
f7deaa1a 163
164 /*
165 * Read a side-channel message for the format:
166 *
167 * Byte(s) Description
168 * ------- -------------------------------------------
169 * 0 Command code
170 * 1 Status code
dcb445bc 171 * 2-3 Data length (network byte order)
f7deaa1a 172 * 4-N Data
173 */
174
dcb445bc
MS
175 if ((buffer = _cupsBufferGet(_CUPS_SC_MAX_BUFFER)) == NULL)
176 {
177 *command = CUPS_SC_CMD_NONE;
178 *status = CUPS_SC_STATUS_TOO_BIG;
179
180 return (-1);
181 }
182
183 while ((bytes = read(CUPS_SC_FD, buffer, _CUPS_SC_MAX_BUFFER)) < 0)
f7deaa1a 184 if (errno != EINTR && errno != EAGAIN)
20fbc903 185 {
e07d4801 186 DEBUG_printf(("1cupsSideChannelRead: Read error: %s", strerror(errno)));
dcb445bc
MS
187
188 _cupsBufferRelease(buffer);
189
18ecb428
MS
190 *command = CUPS_SC_CMD_NONE;
191 *status = CUPS_SC_STATUS_IO_ERROR;
dcb445bc 192
f7deaa1a 193 return (-1);
20fbc903 194 }
f7deaa1a 195
18ecb428
MS
196 /*
197 * Watch for EOF or too few bytes...
198 */
199
200 if (bytes < 4)
201 {
7e86f2f6 202 DEBUG_printf(("1cupsSideChannelRead: Short read of " CUPS_LLFMT " bytes", CUPS_LLCAST bytes));
dcb445bc
MS
203
204 _cupsBufferRelease(buffer);
205
18ecb428
MS
206 *command = CUPS_SC_CMD_NONE;
207 *status = CUPS_SC_STATUS_BAD_MESSAGE;
dcb445bc 208
18ecb428
MS
209 return (-1);
210 }
211
f7deaa1a 212 /*
213 * Validate the command code in the message...
214 */
215
20fbc903 216 if (buffer[0] < CUPS_SC_CMD_SOFT_RESET ||
f14324a7 217 buffer[0] >= CUPS_SC_CMD_MAX)
20fbc903 218 {
e07d4801 219 DEBUG_printf(("1cupsSideChannelRead: Bad command %d!", buffer[0]));
dcb445bc
MS
220
221 _cupsBufferRelease(buffer);
222
18ecb428
MS
223 *command = CUPS_SC_CMD_NONE;
224 *status = CUPS_SC_STATUS_BAD_MESSAGE;
dcb445bc 225
f7deaa1a 226 return (-1);
20fbc903 227 }
f7deaa1a 228
229 *command = (cups_sc_command_t)buffer[0];
230
231 /*
232 * Validate the data length in the message...
233 */
234
235 templen = ((buffer[2] & 255) << 8) | (buffer[3] & 255);
236
237 if (templen > 0 && (!data || !datalen))
238 {
239 /*
240 * Either the response is bigger than the provided buffer or the
241 * response is bigger than we've read...
242 */
243
244 *status = CUPS_SC_STATUS_TOO_BIG;
245 }
1f0275e3 246 else if (!datalen || templen > *datalen || templen > (bytes - 4))
f7deaa1a 247 {
248 /*
249 * Either the response is bigger than the provided buffer or the
250 * response is bigger than we've read...
251 */
252
253 *status = CUPS_SC_STATUS_TOO_BIG;
254 }
255 else
256 {
257 /*
258 * The response data will fit, copy it over and provide the actual
259 * length...
260 */
261
262 *status = (cups_sc_status_t)buffer[1];
263 *datalen = templen;
264
07623986 265 memcpy(data, buffer + 4, (size_t)templen);
f7deaa1a 266 }
267
dcb445bc
MS
268 _cupsBufferRelease(buffer);
269
e07d4801 270 DEBUG_printf(("1cupsSideChannelRead: Returning status=%d", *status));
20fbc903 271
f7deaa1a 272 return (0);
273}
274
275
20fbc903
MS
276/*
277 * 'cupsSideChannelSNMPGet()' - Query a SNMP OID's value.
278 *
279 * This function asks the backend to do a SNMP OID query on behalf of the
280 * filter, port monitor, or backend using the default community name.
281 *
282 * "oid" contains a numeric OID consisting of integers separated by periods,
283 * for example ".1.3.6.1.2.1.43". Symbolic names from SNMP MIBs are not
284 * supported and must be converted to their numeric forms.
285 *
286 * On input, "data" and "datalen" provide the location and size of the
287 * buffer to hold the OID value as a string. HEX-String (binary) values are
288 * converted to hexadecimal strings representing the binary data, while
289 * NULL-Value and unknown OID types are returned as the empty string.
290 * The returned "datalen" does not include the trailing nul.
291 *
292 * @code CUPS_SC_STATUS_NOT_IMPLEMENTED@ is returned by backends that do not
293 * support SNMP queries. @code CUPS_SC_STATUS_NO_RESPONSE@ is returned when
294 * the printer does not respond to the SNMP query.
295 *
8072030b 296 * @since CUPS 1.4/macOS 10.6@
20fbc903
MS
297 */
298
299cups_sc_status_t /* O - Query status */
300cupsSideChannelSNMPGet(
301 const char *oid, /* I - OID to query */
302 char *data, /* I - Buffer for OID value */
303 int *datalen, /* IO - Size of OID buffer on entry, size of value on return */
304 double timeout) /* I - Timeout in seconds */
305{
306 cups_sc_status_t status; /* Status of command */
307 cups_sc_command_t rcommand; /* Response command */
dcb445bc 308 char *real_data; /* Real data buffer for response */
20fbc903
MS
309 int real_datalen, /* Real length of data buffer */
310 real_oidlen; /* Length of returned OID string */
311
312
313 DEBUG_printf(("cupsSideChannelSNMPGet(oid=\"%s\", data=%p, datalen=%p(%d), "
e07d4801 314 "timeout=%.3f)", oid, data, datalen, datalen ? *datalen : -1,
20fbc903
MS
315 timeout));
316
317 /*
318 * Range check input...
319 */
320
321 if (!oid || !*oid || !data || !datalen || *datalen < 2)
322 return (CUPS_SC_STATUS_BAD_MESSAGE);
323
324 *data = '\0';
325
326 /*
327 * Send the request to the backend and wait for a response...
328 */
329
330 if (cupsSideChannelWrite(CUPS_SC_CMD_SNMP_GET, CUPS_SC_STATUS_NONE, oid,
b9faaae1 331 (int)strlen(oid) + 1, timeout))
20fbc903
MS
332 return (CUPS_SC_STATUS_TIMEOUT);
333
dcb445bc
MS
334 if ((real_data = _cupsBufferGet(_CUPS_SC_MAX_BUFFER)) == NULL)
335 return (CUPS_SC_STATUS_TOO_BIG);
336
337 real_datalen = _CUPS_SC_MAX_BUFFER;
20fbc903 338 if (cupsSideChannelRead(&rcommand, &status, real_data, &real_datalen, timeout))
dcb445bc
MS
339 {
340 _cupsBufferRelease(real_data);
20fbc903 341 return (CUPS_SC_STATUS_TIMEOUT);
dcb445bc 342 }
20fbc903
MS
343
344 if (rcommand != CUPS_SC_CMD_SNMP_GET)
dcb445bc
MS
345 {
346 _cupsBufferRelease(real_data);
20fbc903 347 return (CUPS_SC_STATUS_BAD_MESSAGE);
dcb445bc 348 }
20fbc903
MS
349
350 if (status == CUPS_SC_STATUS_OK)
351 {
352 /*
353 * Parse the response of the form "oid\0value"...
354 */
355
7e86f2f6 356 real_oidlen = (int)strlen(real_data) + 1;
20fbc903
MS
357 real_datalen -= real_oidlen;
358
359 if ((real_datalen + 1) > *datalen)
dcb445bc
MS
360 {
361 _cupsBufferRelease(real_data);
20fbc903 362 return (CUPS_SC_STATUS_TOO_BIG);
dcb445bc 363 }
20fbc903 364
07623986 365 memcpy(data, real_data + real_oidlen, (size_t)real_datalen);
20fbc903
MS
366 data[real_datalen] = '\0';
367
368 *datalen = real_datalen;
369 }
370
dcb445bc
MS
371 _cupsBufferRelease(real_data);
372
20fbc903
MS
373 return (status);
374}
375
376
377/*
378 * 'cupsSideChannelSNMPWalk()' - Query multiple SNMP OID values.
379 *
380 * This function asks the backend to do multiple SNMP OID queries on behalf
381 * of the filter, port monitor, or backend using the default community name.
382 * All OIDs under the "parent" OID are queried and the results are sent to
383 * the callback function you provide.
384 *
385 * "oid" contains a numeric OID consisting of integers separated by periods,
386 * for example ".1.3.6.1.2.1.43". Symbolic names from SNMP MIBs are not
387 * supported and must be converted to their numeric forms.
388 *
389 * "timeout" specifies the timeout for each OID query. The total amount of
390 * time will depend on the number of OID values found and the time required
391 * for each query.
392 *
393 * "cb" provides a function to call for every value that is found. "context"
394 * is an application-defined pointer that is sent to the callback function
395 * along with the OID and current data. The data passed to the callback is the
396 * same as returned by @link cupsSideChannelSNMPGet@.
397 *
398 * @code CUPS_SC_STATUS_NOT_IMPLEMENTED@ is returned by backends that do not
399 * support SNMP queries. @code CUPS_SC_STATUS_NO_RESPONSE@ is returned when
400 * the printer does not respond to the first SNMP query.
401 *
8072030b 402 * @since CUPS 1.4/macOS 10.6@
20fbc903
MS
403 */
404
405cups_sc_status_t /* O - Status of first query of @code CUPS_SC_STATUS_OK@ on success */
406cupsSideChannelSNMPWalk(
407 const char *oid, /* I - First numeric OID to query */
408 double timeout, /* I - Timeout for each query in seconds */
409 cups_sc_walk_func_t cb, /* I - Function to call with each value */
410 void *context) /* I - Application-defined pointer to send to callback */
411{
412 cups_sc_status_t status; /* Status of command */
413 cups_sc_command_t rcommand; /* Response command */
dcb445bc 414 char *real_data; /* Real data buffer for response */
7e86f2f6
MS
415 int real_datalen; /* Real length of data buffer */
416 size_t real_oidlen, /* Length of returned OID string */
20fbc903
MS
417 oidlen; /* Length of first OID */
418 const char *current_oid; /* Current OID */
18ecb428 419 char last_oid[2048]; /* Last OID */
20fbc903
MS
420
421
422 DEBUG_printf(("cupsSideChannelSNMPWalk(oid=\"%s\", timeout=%.3f, cb=%p, "
e07d4801 423 "context=%p)", oid, timeout, cb, context));
20fbc903
MS
424
425 /*
426 * Range check input...
427 */
428
429 if (!oid || !*oid || !cb)
430 return (CUPS_SC_STATUS_BAD_MESSAGE);
431
dcb445bc
MS
432 if ((real_data = _cupsBufferGet(_CUPS_SC_MAX_BUFFER)) == NULL)
433 return (CUPS_SC_STATUS_TOO_BIG);
434
20fbc903
MS
435 /*
436 * Loop until the OIDs don't match...
437 */
438
439 current_oid = oid;
7e86f2f6 440 oidlen = strlen(oid);
18ecb428 441 last_oid[0] = '\0';
20fbc903
MS
442
443 do
444 {
445 /*
446 * Send the request to the backend and wait for a response...
447 */
448
449 if (cupsSideChannelWrite(CUPS_SC_CMD_SNMP_GET_NEXT, CUPS_SC_STATUS_NONE,
b9faaae1 450 current_oid, (int)strlen(current_oid) + 1, timeout))
dcb445bc
MS
451 {
452 _cupsBufferRelease(real_data);
20fbc903 453 return (CUPS_SC_STATUS_TIMEOUT);
dcb445bc 454 }
20fbc903 455
dcb445bc 456 real_datalen = _CUPS_SC_MAX_BUFFER;
20fbc903
MS
457 if (cupsSideChannelRead(&rcommand, &status, real_data, &real_datalen,
458 timeout))
dcb445bc
MS
459 {
460 _cupsBufferRelease(real_data);
20fbc903 461 return (CUPS_SC_STATUS_TIMEOUT);
dcb445bc 462 }
20fbc903
MS
463
464 if (rcommand != CUPS_SC_CMD_SNMP_GET_NEXT)
dcb445bc
MS
465 {
466 _cupsBufferRelease(real_data);
20fbc903 467 return (CUPS_SC_STATUS_BAD_MESSAGE);
dcb445bc 468 }
20fbc903
MS
469
470 if (status == CUPS_SC_STATUS_OK)
471 {
472 /*
473 * Parse the response of the form "oid\0value"...
474 */
475
18ecb428
MS
476 if (strncmp(real_data, oid, oidlen) || real_data[oidlen] != '.' ||
477 !strcmp(real_data, last_oid))
20fbc903
MS
478 {
479 /*
480 * Done with this set of OIDs...
481 */
482
dcb445bc 483 _cupsBufferRelease(real_data);
20fbc903
MS
484 return (CUPS_SC_STATUS_OK);
485 }
486
7e86f2f6 487 if ((size_t)real_datalen < sizeof(real_data))
d1c13e16
MS
488 real_data[real_datalen] = '\0';
489
20fbc903 490 real_oidlen = strlen(real_data) + 1;
7d5824d6 491 real_datalen -= (int)real_oidlen;
20fbc903
MS
492
493 /*
494 * Call the callback with the OID and data...
495 */
496
82cc1f9a 497 (*cb)(real_data, real_data + real_oidlen, real_datalen, context);
20fbc903
MS
498
499 /*
500 * Update the current OID...
501 */
502
503 current_oid = real_data;
18ecb428 504 strlcpy(last_oid, current_oid, sizeof(last_oid));
20fbc903
MS
505 }
506 }
507 while (status == CUPS_SC_STATUS_OK);
508
dcb445bc
MS
509 _cupsBufferRelease(real_data);
510
20fbc903
MS
511 return (status);
512}
513
514
f7deaa1a 515/*
516 * 'cupsSideChannelWrite()' - Write a side-channel message.
517 *
518 * This function is normally only called by backend programs to send
519 * responses to a filter, driver, or port monitor program.
520 *
8072030b 521 * @since CUPS 1.3/macOS 10.5@
f7deaa1a 522 */
523
524int /* O - 0 on success, -1 on error */
525cupsSideChannelWrite(
526 cups_sc_command_t command, /* I - Command code */
527 cups_sc_status_t status, /* I - Status code */
528 const char *data, /* I - Data buffer pointer */
529 int datalen, /* I - Number of bytes of data */
530 double timeout) /* I - Timeout in seconds */
531{
dcb445bc 532 char *buffer; /* Message buffer */
7e86f2f6 533 ssize_t bytes; /* Bytes written */
f7deaa1a 534#ifdef HAVE_POLL
535 struct pollfd pfd; /* Poll structure for poll() */
536#else /* select() */
537 fd_set output_set; /* Output set for select() */
538 struct timeval stimeout; /* Timeout value for select() */
539#endif /* HAVE_POLL */
540
541
542 /*
543 * Range check input...
544 */
545
f14324a7 546 if (command < CUPS_SC_CMD_SOFT_RESET || command >= CUPS_SC_CMD_MAX ||
dcb445bc 547 datalen < 0 || datalen > _CUPS_SC_MAX_DATA || (datalen > 0 && !data))
f7deaa1a 548 return (-1);
549
550 /*
551 * See if we can safely write to the side-channel socket...
552 */
553
554#ifdef HAVE_POLL
555 pfd.fd = CUPS_SC_FD;
556 pfd.events = POLLOUT;
557
558 if (timeout < 0.0)
559 {
560 if (poll(&pfd, 1, -1) < 1)
561 return (-1);
562 }
7e86f2f6 563 else if (poll(&pfd, 1, (int)(timeout * 1000)) < 1)
f7deaa1a 564 return (-1);
565
566#else /* select() */
567 FD_ZERO(&output_set);
568 FD_SET(CUPS_SC_FD, &output_set);
569
570 if (timeout < 0.0)
571 {
572 if (select(CUPS_SC_FD + 1, NULL, &output_set, NULL, NULL) < 1)
573 return (-1);
574 }
575 else
576 {
577 stimeout.tv_sec = (int)timeout;
578 stimeout.tv_usec = (int)(timeout * 1000000) % 1000000;
579
580 if (select(CUPS_SC_FD + 1, NULL, &output_set, NULL, &stimeout) < 1)
581 return (-1);
582 }
583#endif /* HAVE_POLL */
584
585 /*
586 * Write a side-channel message in the format:
587 *
588 * Byte(s) Description
589 * ------- -------------------------------------------
590 * 0 Command code
591 * 1 Status code
592 * 2-3 Data length (network byte order) <= 16384
593 * 4-N Data
594 */
595
7e86f2f6 596 if ((buffer = _cupsBufferGet((size_t)datalen + 4)) == NULL)
dcb445bc
MS
597 return (-1);
598
f7deaa1a 599 buffer[0] = command;
600 buffer[1] = status;
7e86f2f6
MS
601 buffer[2] = (char)(datalen >> 8);
602 buffer[3] = (char)(datalen & 255);
f7deaa1a 603
604 bytes = 4;
605
606 if (datalen > 0)
607 {
07623986 608 memcpy(buffer + 4, data, (size_t)datalen);
f7deaa1a 609 bytes += datalen;
610 }
611
7e86f2f6 612 while (write(CUPS_SC_FD, buffer, (size_t)bytes) < 0)
f7deaa1a 613 if (errno != EINTR && errno != EAGAIN)
dcb445bc
MS
614 {
615 _cupsBufferRelease(buffer);
f7deaa1a 616 return (-1);
dcb445bc
MS
617 }
618
619 _cupsBufferRelease(buffer);
f7deaa1a 620
621 return (0);
622}