Next Previous Contents

14. RFC 1179 - Line Printer Daemon Protocol

RFC1179 can be obtained from the LPRng distribution, in the DOC/rfc1179 directory, or from one of many sites which mirror the RFCs.

This RFC is an informational RFC, which means that the information in it is meant as a guide to users, and not as a fixed standard. In addition, the RFC tried to document the behavior of the BSD LPD print server, and left out many details dealing with error recover, error messages, extensions to the protocol, etc.

In this section, I will try to explain what RFC1179 specifies as a protocol, and many of the problems encountered in trying to use it.

14.1 Ports and Connections

Options used:

RFC1179 requires that the lpd server listen for TCP/IP connections on port 515. This port is registered with the Internet Naming Authority, and the /etc/services file or TCP/IP services database usually has an entry:

printer     515/tcp     spooler     # line printer spooler

RFC1179 explicitly states that all connections to port 515 must originate from ports 721-731. The reason for this restriction is due to the UNIX concept of reserved and privileged ports. By convention, ports in the range 1-1023 can only bound by processes whose Effective User ID (EUID) is 0 (root). This, ordinary users could not originate a connection from the reserved or privileged port range.

In a UNIX environment, this means that the user programs lpr, lprm, lpq, and lpc would have to be SETUID root.

As experience has shown, for security purposes, the fewer programs that need to have privileged status, the better. LPRng uses the lpd_port=printer configuration option to set the actual port to be use. By default, this is port 515, but can be set to other values.

The restriction of originating ports to 721-731 causes another set of problems. Part of the TCP/IP protocol is concerned with avoiding communications problems resulting from the arrival of old or stale packets. When a connection between sourcehost, sourceport and desthost, destport is made, a set of sequence numbers is established and used for sending and acknowledgement of data. When the connection terminates, the TCP/IP protocol restricts the establishment of a new connection between sourcehost, sourceport and desthost, destport for a period long enough for all stale packets to be removed from the system. This is approximately 10 minutes long.

In order to simplify assignments of ports, timing out connections, and other matters, many TCP/IP packages do keep track of explicit connections originating from a port, but simply prevent the port from being reused for either origination or reception of a connection. They do, however, keep track of the active connections to a port, and perform timeouts on these. This is usually much simpler to implement, as it can be done with a list attached to the port.

This implementation method creates some problems when a large number of connections must be originated from a relatively small number of port numbers. Observe what happens when host 1 tries to send a large number of jobs to a server 2. The following connections are established and terminated:
host 1, port 721 and host 2, port 515
host 1, port 722 and host 2, port 515
host 1, port 723 and host 2, port 515
host 1, port 724 and host 2, port 515
host 1, port 725 and host 2, port 515
host 1, port 726 and host 2, port 515
host 1, port 727 and host 2, port 515
host 1, port 728 and host 2, port 515
host 1, port 729 and host 2, port 515
host 1, port 730 and host 2, port 515
host 1, port 731 and host 2, port 515

Now according to the RFC1179 rules and the TCP/IP protocol, we will have to wait until one of these connections terminates before we can make another. On the originating system, if the TCP/IP implementation does timeouts on the originating port, we will have to wait for the timeout to elapse before we can make a new connection. Unfortunately, there is no way to find out what the status of the port is, so we will have to try them each in turn until we get a successful connection.

The LPRng code has tried to provide several methods to deal with these problems. Firstly, the originate_port=512 1023 option specifies the range of ports used to originate connections when the software is running either as ROOT or SETUID root. By strict RFC1179 rules, this should be originate_port=721 731, but it turns out that most BSD LPD based implementations only check for a reserved originating port. By using 512 ports we get a greatly reduced rate of errors due to lack of ports due to pending timeouts.

However, on some systems which are acting as servers for a large number of printers even increasing this port range is insufficient, and steps need to be taken use the originating port numbers more efficiently. The Berkeley TCP/IP implementation getsockopt() and setsockopt() allows the user to manipulate some of the underlying timeouts and options of the TCP/IP network. When a TCP/IP connection is established, the setsockopt() facility can be used to set the SO_REUSEADDR flag on the connection. This flag effectively sets the timeout value on the ports and connections to 0, allowing immediate reuse of the ports. When done on an originating end of a connection, this will allow the originating port number to be reused immediately.

It would appear that by setting SO_REUSEADDR on the originating end that we have solved our problems. However, unless the destination end of the connection sets its SO_REUSEADDR flag on the connection, it will still do a timeout. Thus when we try to make a connection from a port that was active within a short period of time to the same host, then it will reject the connection until the timeout is over.

The reuse_addr flag (default off) forces the LPRng software to set the SO_REUSEADDR flag on originating connections. As indicated, this will allow ports to be reused immediately for outgoing connections, rather than waiting for a timeout.

While the reuse_addr flag usually allows us to reuse ports, there is still the problem of dealing with connections failing due to the remote site rejecting the connection due to a pending timeout from a previous connection. A careful study of the original BSD TCP/IP network code and of some others indicates that when a connection fails due to a pending timeout, an ECONNREFUSED error code is returned to a connect() system call. If this happens and we suspect that the remote site is rejecting the connection due to a timeout problem, then we should retry making the connection but from a new port, and continue retrying until all possible ports are used.

The retry_econnrefused (default on) flag is used to specify that we retry connections in this manner. When this is set, a connection refused error causes the connection to be retried using a new port. This will be repeated until all available ports have been tried.

When printing a job and the lpd server connection to a remote site or device open fails, the retry_nolink (default on) will cause the attempt to be retried indefinately. The combination of retry_econnrefused and retry_nolink will provide robust connection attempts to remote systems.

While the above problems cause difficulties when making connections, there are also problems when terminating connections. After closing a socket, the TCP/IP software will try to flush any pending data to the destination. Unfortunately, on some systems it will only do this while the process is active. This has caused problems on systems which terminate a process it has received an abnormal (signal caused) termination.

The setsockopt() SO_LINGER option allows the user to specify that when a socket is closed normally, that the process should block until pending data is flushed or for the socket_linger period. If socket_linger is 0, then no SO_LINGER operation is done.

In summary, if you experience problems with connection failures due to port exhaustion, first try setting the reuse_port flag, and you should see a reduction. Check to ensure that the retry_econnrefused and retry_nolink flags are set, and the error code in the log and status files. If the failures continue, then the problem is caused by the remote end having timeout limitations and there is little you can do except to set a very long connect_retry interval, say connect_retry=120 (2 minutes).

14.2 Protocol Requests and Replies

Options used:

After a connection has been established, a request can be sent to the lpd server. The request consists of a single octet indicating the request type, followed by the printer (or print queue) name, followed by a set of options for the request, followed by a LF (line feed) character.

\NNNprinter[ options]\n
  NNN    Operation


NNN
RFC1179Operationprogram
1yesstart printlpc
2yestransfer a printer joblpr
3yesprint short form of queue statuslpq
4yesprint long form of queue statuslpq
5yesremove jobslprm
6LPRngdo control operationlpc
7LPRngtransfer a block format print joblpr
8LPRngsecure command transferlpc
9LPRngverbose status informationlpq

After the request has been sent, then a reply will be returned. In general the reply has the following form:

\000\n    Success
\NNN\n    Failure (NNN is error code)
text\n    Text or status information

As can be seen, this protocol is extremely simple, but there are a set of problems due to the loosely written language of RFC1179.

  1. Firstly, while RFC1179 sets limits on the lengths of commands, it does not strictly set limits on the characters set used in the commands. This can result in problems when trying to print status information, headers on banners, and other details.
  2. The original RFC1179 protocol did not provide any way to do remote control of queues or LPD servers. This has been added to the protocol. As a side effect, if you try to use lpc to control a non-LPRng printer, it will not work.
  3. You can specify that a network printer is non-LPRng by using the remote_support=RQVMC option. The letters R, Q, V, M, and C stand for lpr, lpq, lpq -v (verbose), verbose lpq, lprm, and lpc operations respectively. If remote_support does not allow a particular operation, then the LPRng software will not send a corresponding request to the printer.

14.3 Job Transfer

Options used:

A job transfer operation starts with a job transfer request, followed by several file transfer operations. At the end of the file transfers, the connection should be closed.

A file transfer request has the form:


Command
Purpose
\001\nabort
\002nnnn cfnamecontrol file transfer
\003nnnn dfnamedata file transfer

The abort operation is used to terminate job transfer and indicate that the job should not be processed for printing. The connection will be closed and the partly transferred job will be discarded.

The control file and data file transfer commands have a length (in bytes) of the file and the name of the file to be transferred. When the command is received, the server will reply with a status line:


Status
Purpose
\000Accepted, proceed
\nnnRejected with error code

The reply is only a single octet. Some defective implementations of RFC1179 send a LF after the octet, which makes life very difficult. LPRng makes an effort to detect these non-conforming RFC1179 systems and will accept jobs from them. However, it will not send jobs to them.

If LPRng sends a reject code, as an extension to RFC1179 it also sends an error message. Note that the values for error codes are not defined, nor are their causes. LPRng uses the following values for error codes, which appear to be compatible with many, but not all, of the BSD LPD based systems:


Code
Error
\000Accepted, proceed
\001Queue not accepting jobs
\002Queue temporarily full, retry later
\003Bad job format, do not retry

When the sender gets the reply indicating success, it sends the nnnn bytes of the control or data file, followed by a \000 octet. The receiver will then reply as above; a single \000 octet indicating success.

The above procedure is carried out until all data files and the control file of a job are transferred.

RFC1179 is silent on the following issues:

  1. When sending a job, do you send the control file first, followed by the data file(s), or the data files first?
  2. When sending multiple jobs, can you send them on a single connection, or do you have to establish a new connection for each job?

LPRng will accept jobs whether they are sent control or data files first. By default, it sends the control file first, followed by the data file. If the destination system requires that the data files be sent first, the send_data_first printcap option can be used to force data files to be sent first.

RFC1179 states that:

The name of the control file ... should start with ASCII "cfA", followed by a three digit job number, followed by the host name which has constructed the control file.

The should in this wording indicates that this is simply a guideline, and that other formats are possible. Some of the major problems with this format are as follows:

  1. The restriction to 3 digits means that at most 1000 jobs can be in a queue. Strangely, some systems generate far more than 1000 jobs a day, and need to archive them on a regular basis. The longnumber option will allow LPRng to use a 6 digit job number for files in the print queue.
  2. The host name format is not specified. Some implementations consider that this is the short host name, while others think it is the fully qualified domain name (FQDN). LPRng, by default, will use the FQDN host name. However, the use_shorthost option will force it to use short host names in control and data files.
  3. The cfA control file name was modified to allow the job priority to be used as the A letter of the control file. By default, this is A (lowest, i.e. cfA) and but can range to Z (highest, i.e. cfZ). All known spoolers except LPRng seem to ignore the actual value of the letter.

14.4 Data File Transfer

As discussed, a data file is transferred using the command below.


Command
Purpose
\003nnnn dfnamedata file transfer

From RFC1179:

The data file may contain any 8 bit values at all. The total number of bytes in the stream may be sent as the first operand, otherwise the field should be cleared to 0. The name of the data file should start with ASCII "dfA". This should be followed by a three digit job number. The job number should be followed by the host name which has constructed the data file. Interpretation of the contents of the data file is determined by the contents of the corresponding control file.

There are several surprises in RFC1179.

  1. Apparently a job should only consist of a single data file. This is a severe limitation, and in fact the BSD LPR and other print spoolers process jobs with multiple data files. By convention, these data files have names of the form dfA, dfB, ... dfZ, dfa, dfz.
  2. The RFC does not specify that the control file and data file job numbers must be identical. Most implementations follow this convention, which simplifies life tremendously.
  3. The RFC does not specify that the control file and data file job host names must be identical. Most implementations follow this convention, which simplifies life tremendously.
  4. A zero length data file does not cause a data transfer to take place. LPRng modifies this action to be slightly different. When a zero length data file transfer is indicated, all of the input until the connection is closed is used as the contents of the data file.

    When 'piping' into the lpr program, this can be very useful as it eliminates the need to create temporary files on the senders host. The lpr -k option for details. Note that some print spoolers do not use this interpretation, and this option should be used carefully.

14.5 Control File Contents

The control file consists of a set of lines which either provide printing information or specify data files to be printed. The information lines start with upper case letters or digits, while the data files lines start with lower case letters. Here is a sample control file:

Hastart4.astart.com
J(stdin)
CA
Lpapowell
Apapowell@astart4+955
Ppapowell
fdfA955astart4.astart.com
N(stdin)
UdfA955astart4.astart.com

The following are the letters and their meanings in the control file.


X
RFC1179Meaning
ALPRngIdentifier for job
CRFC1179Class for banner page
HRFC1179Host name
IRFC1179Indent Printing
JRFC1179Job name for banner page
LRFC1179Print banner page
MRFC1179Mail When Printed
NRFC1179Name of source file
PRFC1179User identification
QLPRngQueue name
RLPRngAccounting info
SRFC1179Symbolic link data
TRFC1179Title for pr
URFC1179Unlink data file
WRFC1179Width of output
ZLPRngFilter options
1RFC1179troff R font
2RFC1179troff I font
3RFC1179troff B font
4RFC1179troff S font
cRFC1179Plot CIF file
dRFC1179Print DVI file
fRFC1179Print formatted file
gRFC1179Plot file
kRFC1179Reserved for use by Kerberized LPR clients and servers.
lRFC1179Print file leaving control characters
nRFC1179Print ditroff output file
oRFC1179Print Postscript output file
pRFC1179Print file with 'pr' format
rRFC1179File to print with FORTRAN carriage control
tRFC1179Print troff output file
vRFC1179Print raster file
zRFC1179Reserved for future use with the Palladium print system.

The A (Identifier) line was introduced to record a unique system wide job identifier for LPRng submitted jobs. This is basically formed from the user name, job number, and host at the time of submission. For example: papowell@astart4+955 is job number 995 submitted by papowell from host astart4.

The C (Class) line is set by the lpr -C class option, and the value can be used to control printing. For example, the lpc class zone command would restrict job printing to only jobs with class zone.

The H (hostname), P (username), and J (jobname) fields are used to identify the host and user which sent the job, and to provide information to be displayed by lpq when reporting job status.

The L (print banner page) field is one that has caused many problems for users. RFC1179 indicates that its presence causes the banner page to be printed, and its absense suppresses banner pages. The lpr -h option suppresses putting this line into the control file. Usually the L field is a duplicate of the P field.

The M (mail information) field supplies a mail address for LPRng to send mail to when a job is completed. See LPR -m and user logging for more details.

The N (file name) field is usually provided to identify the file name corresponding to the data file. This can be used to print names on page separators, etc. LPRng largely ignores this line.

The I (indent) and W (width) fields are supposed to specify a page indent and width for printing. These fields are passed to filters if they are present.

The Q (queue name) field is an LPRng extension, and contains the name of the print queue the job was originally sent to. See qq printcap option for details.

The R (accounting info) field was added by LPRng to allow a specified account to be billed for job printing. The lpr -Rname option can be used to specify the accounting name.

The S (symbolic link) and U (unlink after printing) lines were used by the original BSD LPD print system to control how it passed files to the print server. LPRng ignores these lines. In fact, it will remove S lines and force the U lines to refer only to job data files. This closes a nasty security loophole on non-LPRng print spoolers.

The T (pr job title) is used with the lpr -p operation to supply a banner to the pr program.

The Z (filter options) value is specified with lpr -Zoption and is passed to the data file filters during the printing operation. See Filters for details on how the this is used during the printing process.

All of the lower case letters are reserved for format specifications for data files. In the control file, these are followed by the name of the data file to which they correspond. While in principle different data files in the control file can have different formats, this has not been implemented in any known spooling system. See Filters for details on how the data file formats are used during the printing process.

14.6 LPQ Requests

The RFC1179 protocol specifies that lpq print status requests can be sent to the lpd server. The lpq requests have the format:

\003printer [id]* \n    short
\004printer [id]* \n    long
\009printer [id]* \n    LPRng extension- verbose

The lpd print server will then return queue status and close the data connection.

RFC1179 does not state in any manner what the format of the queue status should be. Thus, implementors have been free to augment or change the status as they like. Even the BSD LPR status format has been changed from different versions.

See Status Monitoring and Logging for information on the formats returned.

The id values are used to select the jobs to be displayed. LPRng displays any job whose ID, hostname, or user name information from the control file A, H, or P fields match any of the id values.

Note that since there is no identification of the information requestor, then restriction of information is almost impossible.

14.7 LPRM Requests

The RFC1179 protocol specifies that lprm job removal requests can be sent to the lpd server. The lpq requests have the format:

\003printer user [id]* \n

The lpd print server will search the specified print queue and remove any job whose ID, hostname, or user name information from the control file A, H, or P fields match any of the id values and for which the user has permission to perform a removal operation. See the /etc/lpd.perms file for details on permissions.

Most RFC1179 compatible spoolers use the user information in the request as the name of the user which spooled the job. However, in a network environment this is extremely easy to fabricate, and is at best a weak type of authentication.

14.8 LPC Requests

LPRng has extended the RFC1179 protocol to allow queue and printer control commands to be sent to the LPD server. The format of these commands are:

\006printer user key [options]

The following commands are supported.


Command
Operation
active [printer[@host]]check to see if server accepting connections
abort (printer[@host] | all) terminate server process printing job
disable (printer[@host] | all) disable queueing
debug (printer[@host] | all) debugparms set debug level for printer
enable (printer[@host] | all) enable queueing
hold (printer[@host] | all) (name[@host] | job | all)* hold job
holdall (printer[@host] | all) hold all jobs on
kill (printer[@host] | all) stop and restart server
lpd [printer[@host]] get LPD PID for server
lpq (printer[@host] | all) (name[@host] | job | all)* invoke LPQ
lprm (printer[@host] | all) (name[@host]|host|job| all)* invoke LPRM
move printer (user|jobid)* target move jobs to new queue
noholdall (printer[@host] | all) hold all jobs off
printcap (printer[@host] | all) report printcap values
quit exit LPC
redirect (printer[@host] | all) (printer@host | off )* redirect jobs
release (printer[@host] | all) (name[@host] | job | all)* release job
reread [printer[@host]]LPD reread database information
start (printer[@host] | all) start printing
status (printer[@host] | all) status of printers
stop (printer[@host] | all) stop printing
topq (printer[@host] | all) (name[@host] | job | all)* reorder job
defaultq default queue for LPD server

Many of these commands support extremely specialized operations for print queue management, However, the following are the most commonly used and are supported by the BSD LPD print spooling system as well:

The following commands are extensions to the basic set provided by the BSD LPD system.

14.9 Block Job Transfer

Options used:

In normal job transfer operations, the sender and receiver have a handshake interaction in order to transfer a print job. Each file is sent individually. The send_block_format option forces a Block Job Transfer operation. This causes the sender to transfer a single file containing all the job printing information, including control file and data files.

The transfer command line has the form:

\006printer user@host size\n

The receiver will return any acknowledgement of a single 0 octet, and then the size bytes of the job will be transferred by the sender. At the end of the transfer a single 0 octet is added, and the receiver will indicate success by returning a single 0 octet. Any other value returned by the receiver indicates an error condition.

The file transferred by the sender is simply the command lines that it would have normally sent for job transfer, followed by the control or data file values.

14.10 Authenticated Transfer

RFC1179 does not provide any authentication or encryption mechanism for the transfer of jobs or commands to the lpd print server. The Authenticated Transfer operation was added to allow an encrypted or authenticated transfer of print jobs or commands.

Since there are various restrictions on the incorporation of authentication facilities into programs, LPRng supports authentication by providing a simple interface to encryption programs.

The idea is that when authentication is required when sending a job, LPRng will generate a block transfer job as described for the Block Transfer operation, and then invoke a set of programs to encryt and transfer the file, and encrypt and transfer the returned status.

Similarly, when sending a command, the command information will be placed in a file and the encrypted file will be transferred.

This technique means that the programs and support to do encryption are external to LPRng, and can use any type of method that they choose to implement the secure and/or authenticated transfer.

See Authentication and Encryption for details on the authentication interface.


Next Previous Contents