• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Side-channel API code for CUPS.
3  *
4  * Copyright 2007-2014 by Apple Inc.
5  * Copyright 2006 by Easy Software Products.
6  *
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
11  * missing or damaged, see the license at "http://www.cups.org/".
12  *
13  * This file is subject to the Apple OS-Developed Software exception.
14  */
15 
16 /*
17  * Include necessary headers...
18  */
19 
20 #include "sidechannel.h"
21 #include "cups-private.h"
22 #ifdef _WIN32
23 #  include <io.h>
24 #else
25 #  include <unistd.h>
26 #endif /* _WIN32 */
27 #ifndef _WIN32
28 #  include <sys/select.h>
29 #  include <sys/time.h>
30 #endif /* !_WIN32 */
31 #ifdef HAVE_POLL
32 #  include <poll.h>
33 #endif /* HAVE_POLL */
34 
35 
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 
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  *
57  * @since CUPS 1.3/macOS 10.5@
58  */
59 
60 cups_sc_status_t			/* O  - Status of command */
cupsSideChannelDoRequest(cups_sc_command_t command,char * data,int * datalen,double timeout)61 cupsSideChannelDoRequest(
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  *
96  * @since CUPS 1.3/macOS 10.5@
97  */
98 
99 int					/* O - 0 on success, -1 on error */
cupsSideChannelRead(cups_sc_command_t * command,cups_sc_status_t * status,char * data,int * datalen,double timeout)100 cupsSideChannelRead(
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 {
107   char		*buffer;		/* Message buffer */
108   ssize_t	bytes;			/* Bytes read */
109   int		templen;		/* Data length from message */
110   int		nfds;			/* Number of file descriptors */
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 
119   DEBUG_printf(("cupsSideChannelRead(command=%p, status=%p, data=%p, "
120                 "datalen=%p(%d), timeout=%.3f)", command, status, data,
121 		datalen, datalen ? *datalen : -1, timeout));
122 
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 
138   while ((nfds = poll(&pfd, 1,
139 		      timeout < 0.0 ? -1 : (int)(timeout * 1000))) < 0 &&
140 	 (errno == EINTR || errno == EAGAIN))
141     ;
142 
143 #else /* select() */
144   FD_ZERO(&input_set);
145   FD_SET(CUPS_SC_FD, &input_set);
146 
147   stimeout.tv_sec  = (int)timeout;
148   stimeout.tv_usec = (int)(timeout * 1000000) % 1000000;
149 
150   while ((nfds = select(CUPS_SC_FD + 1, &input_set, NULL, NULL,
151 			timeout < 0.0 ? NULL : &stimeout)) < 0 &&
152 	 (errno == EINTR || errno == EAGAIN))
153     ;
154 
155 #endif /* HAVE_POLL */
156 
157   if (nfds < 1)
158   {
159     *command = CUPS_SC_CMD_NONE;
160     *status  = nfds==0 ? CUPS_SC_STATUS_TIMEOUT : CUPS_SC_STATUS_IO_ERROR;
161     return (-1);
162   }
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
171   * 2-3      Data length (network byte order)
172   * 4-N      Data
173   */
174 
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)
184     if (errno != EINTR && errno != EAGAIN)
185     {
186       DEBUG_printf(("1cupsSideChannelRead: Read error: %s", strerror(errno)));
187 
188       _cupsBufferRelease(buffer);
189 
190       *command = CUPS_SC_CMD_NONE;
191       *status  = CUPS_SC_STATUS_IO_ERROR;
192 
193       return (-1);
194     }
195 
196  /*
197   * Watch for EOF or too few bytes...
198   */
199 
200   if (bytes < 4)
201   {
202     DEBUG_printf(("1cupsSideChannelRead: Short read of " CUPS_LLFMT " bytes", CUPS_LLCAST bytes));
203 
204     _cupsBufferRelease(buffer);
205 
206     *command = CUPS_SC_CMD_NONE;
207     *status  = CUPS_SC_STATUS_BAD_MESSAGE;
208 
209     return (-1);
210   }
211 
212  /*
213   * Validate the command code in the message...
214   */
215 
216   if (buffer[0] < CUPS_SC_CMD_SOFT_RESET ||
217       buffer[0] >= CUPS_SC_CMD_MAX)
218   {
219     DEBUG_printf(("1cupsSideChannelRead: Bad command %d!", buffer[0]));
220 
221     _cupsBufferRelease(buffer);
222 
223     *command = CUPS_SC_CMD_NONE;
224     *status  = CUPS_SC_STATUS_BAD_MESSAGE;
225 
226     return (-1);
227   }
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   }
246   else if (!datalen || templen > *datalen || templen > (bytes - 4))
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 
265     memcpy(data, buffer + 4, (size_t)templen);
266   }
267 
268   _cupsBufferRelease(buffer);
269 
270   DEBUG_printf(("1cupsSideChannelRead: Returning status=%d", *status));
271 
272   return (0);
273 }
274 
275 
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  *
296  * @since CUPS 1.4/macOS 10.6@
297  */
298 
299 cups_sc_status_t			/* O  - Query status */
cupsSideChannelSNMPGet(const char * oid,char * data,int * datalen,double timeout)300 cupsSideChannelSNMPGet(
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 */
308   char			*real_data;	/* Real data buffer for response */
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), "
314                 "timeout=%.3f)", oid, data, datalen, datalen ? *datalen : -1,
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,
331                            (int)strlen(oid) + 1, timeout))
332     return (CUPS_SC_STATUS_TIMEOUT);
333 
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;
338   if (cupsSideChannelRead(&rcommand, &status, real_data, &real_datalen, timeout))
339   {
340     _cupsBufferRelease(real_data);
341     return (CUPS_SC_STATUS_TIMEOUT);
342   }
343 
344   if (rcommand != CUPS_SC_CMD_SNMP_GET)
345   {
346     _cupsBufferRelease(real_data);
347     return (CUPS_SC_STATUS_BAD_MESSAGE);
348   }
349 
350   if (status == CUPS_SC_STATUS_OK)
351   {
352    /*
353     * Parse the response of the form "oid\0value"...
354     */
355 
356     real_oidlen  = (int)strlen(real_data) + 1;
357     real_datalen -= real_oidlen;
358 
359     if ((real_datalen + 1) > *datalen)
360     {
361       _cupsBufferRelease(real_data);
362       return (CUPS_SC_STATUS_TOO_BIG);
363     }
364 
365     memcpy(data, real_data + real_oidlen, (size_t)real_datalen);
366     data[real_datalen] = '\0';
367 
368     *datalen = real_datalen;
369   }
370 
371   _cupsBufferRelease(real_data);
372 
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  *
402  * @since CUPS 1.4/macOS 10.6@
403  */
404 
405 cups_sc_status_t			/* O - Status of first query of @code CUPS_SC_STATUS_OK@ on success */
cupsSideChannelSNMPWalk(const char * oid,double timeout,cups_sc_walk_func_t cb,void * context)406 cupsSideChannelSNMPWalk(
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 */
414   char			*real_data;	/* Real data buffer for response */
415   int			real_datalen;	/* Real length of data buffer */
416   size_t		real_oidlen,	/* Length of returned OID string */
417 			oidlen;		/* Length of first OID */
418   const char		*current_oid;	/* Current OID */
419   char			last_oid[2048];	/* Last OID */
420 
421 
422   DEBUG_printf(("cupsSideChannelSNMPWalk(oid=\"%s\", timeout=%.3f, cb=%p, "
423                 "context=%p)", oid, timeout, cb, context));
424 
425  /*
426   * Range check input...
427   */
428 
429   if (!oid || !*oid || !cb)
430     return (CUPS_SC_STATUS_BAD_MESSAGE);
431 
432   if ((real_data = _cupsBufferGet(_CUPS_SC_MAX_BUFFER)) == NULL)
433     return (CUPS_SC_STATUS_TOO_BIG);
434 
435  /*
436   * Loop until the OIDs don't match...
437   */
438 
439   current_oid = oid;
440   oidlen      = strlen(oid);
441   last_oid[0] = '\0';
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,
450                              current_oid, (int)strlen(current_oid) + 1, timeout))
451     {
452       _cupsBufferRelease(real_data);
453       return (CUPS_SC_STATUS_TIMEOUT);
454     }
455 
456     real_datalen = _CUPS_SC_MAX_BUFFER;
457     if (cupsSideChannelRead(&rcommand, &status, real_data, &real_datalen,
458                             timeout))
459     {
460       _cupsBufferRelease(real_data);
461       return (CUPS_SC_STATUS_TIMEOUT);
462     }
463 
464     if (rcommand != CUPS_SC_CMD_SNMP_GET_NEXT)
465     {
466       _cupsBufferRelease(real_data);
467       return (CUPS_SC_STATUS_BAD_MESSAGE);
468     }
469 
470     if (status == CUPS_SC_STATUS_OK)
471     {
472      /*
473       * Parse the response of the form "oid\0value"...
474       */
475 
476       if (strncmp(real_data, oid, oidlen) || real_data[oidlen] != '.' ||
477           !strcmp(real_data, last_oid))
478       {
479        /*
480         * Done with this set of OIDs...
481 	*/
482 
483 	_cupsBufferRelease(real_data);
484         return (CUPS_SC_STATUS_OK);
485       }
486 
487       if ((size_t)real_datalen < sizeof(real_data))
488         real_data[real_datalen] = '\0';
489 
490       real_oidlen  = strlen(real_data) + 1;
491       real_datalen -= (int)real_oidlen;
492 
493      /*
494       * Call the callback with the OID and data...
495       */
496 
497       (*cb)(real_data, real_data + real_oidlen, real_datalen, context);
498 
499      /*
500       * Update the current OID...
501       */
502 
503       current_oid = real_data;
504       strlcpy(last_oid, current_oid, sizeof(last_oid));
505     }
506   }
507   while (status == CUPS_SC_STATUS_OK);
508 
509   _cupsBufferRelease(real_data);
510 
511   return (status);
512 }
513 
514 
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  *
521  * @since CUPS 1.3/macOS 10.5@
522  */
523 
524 int					/* O - 0 on success, -1 on error */
cupsSideChannelWrite(cups_sc_command_t command,cups_sc_status_t status,const char * data,int datalen,double timeout)525 cupsSideChannelWrite(
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 {
532   char		*buffer;		/* Message buffer */
533   ssize_t	bytes;			/* Bytes written */
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 
546   if (command < CUPS_SC_CMD_SOFT_RESET || command >= CUPS_SC_CMD_MAX ||
547       datalen < 0 || datalen > _CUPS_SC_MAX_DATA || (datalen > 0 && !data))
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   }
563   else if (poll(&pfd, 1, (int)(timeout * 1000)) < 1)
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 
596   if ((buffer = _cupsBufferGet((size_t)datalen + 4)) == NULL)
597     return (-1);
598 
599   buffer[0] = command;
600   buffer[1] = status;
601   buffer[2] = (char)(datalen >> 8);
602   buffer[3] = (char)(datalen & 255);
603 
604   bytes = 4;
605 
606   if (datalen > 0)
607   {
608     memcpy(buffer + 4, data, (size_t)datalen);
609     bytes += datalen;
610   }
611 
612   while (write(CUPS_SC_FD, buffer, (size_t)bytes) < 0)
613     if (errno != EINTR && errno != EAGAIN)
614     {
615       _cupsBufferRelease(buffer);
616       return (-1);
617     }
618 
619   _cupsBufferRelease(buffer);
620 
621   return (0);
622 }
623