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