LPRng-HOWTO: RFC 1179 - Line Printer Daemon Protocol

Next Previous Contents

## 18. RFC 1179 - Line Printer Daemon Protocol

RFC1179 can be obtained from the LPRng distribution, in the LPRng_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.

## 18.1 Ports and Connections

Options used:

- `lpd_port=`Port for LPD to accept connection
- `originate_port=`Ports to originate connections on
- `reuse_addr` Set SO_REUSEADDR flag on connection
- `retry_econnrefused` Retry on connect ECONNREFUSED error
- `retry_nolink` Retry on device open or connection ffailure
- `socket_linger#` Linger time for sockets

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 indefinitely. 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).

## 18.2 Protocol Requests and Replies

Options used:

- `remote_support=`Remote operations supported

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 | RFC1179 | Operation | program
1 | yes | start print | `lpc`
2 | yes | transfer a printer job | `lpr`
3 | yes | print short form of queue status | `lpq`
4 | yes | print long form of queue status | `lpq`
5 | yes | remove jobs | `lprm`
6 | LPRng | do control operation | `lpc`
7 | LPRng | transfer a block format print job | `lpr`
8 | LPRng | secure command transfer | `lpc`
9 | LPRng | verbose status information | `lpq`

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 and specify the operations supported by the printer. The letters R, Q, V, M, and C stand for `lpr`, `lpq`, `lpq -v` (verbose), verbose `lpq`, `lprm`, and `lpc` operations respectively, and indicate that these are supported. If `remote_support` does not allow a particular operation, then the LPRng software will not send a corresponding request to the printer. For example, `remote_support=R` would restrict operations to spooling jobs only, and the LPRng software would not query the printer for status.

## 18.3 Job Transfer

Options used:

- `longnumber` Long job number (6 digits)
- `send_data_first` Send data files first
- `use_shorthost` Use short hostname

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\n | abort
\002nnnn cfname | control file transfer
\003nnnn dfname | data 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
\000 | Accepted, proceed
\nnn | Rejected 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
\000 | Accepted, proceed
\001 | Queue not accepting jobs
\002 | Queue temporarily full, retry later
\003 | Bad 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.

## 18.4 Data File Transfer

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

Command | Purpose
\003nnnn dfname | data 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.

## 18.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 | RFC1179 | Meaning
A | LPRng | Identifier for job
C | RFC1179 | Class for banner page
H | RFC1179 | Host name
I | RFC1179 | Indent Printing
J | RFC1179 | Job name for banner page
L | RFC1179 | Print banner page
M | RFC1179 | Mail When Printed
N | RFC1179 | Name of source file
P | RFC1179 | User identification
Q | LPRng | Queue name
R | LPRng | Accounting info
S | RFC1179 | Symbolic link data
T | RFC1179 | Title for pr
U | RFC1179 | Unlink data file
W | RFC1179 | Width of output
Z | LPRng | Filter options
1 | RFC1179 | troff R font
2 | RFC1179 | troff I font
3 | RFC1179 | troff B font
4 | RFC1179 | troff S font
c | RFC1179 | Plot CIF file
d | RFC1179 | Print DVI file
f | RFC1179 | Print formatted file
g | RFC1179 | Plot file
k | RFC1179 | Reserved for use by Kerberized LPR clients and servers.
l | RFC1179 | Print file leaving control characters
n | RFC1179 | Print ditroff output file
o | RFC1179 | Print Postscript output file
p | RFC1179 | Print file with 'pr' format
t | RFC1179 | Print troff output file
v | RFC1179 | Print raster file
z | RFC1179 | Reserved 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 absence 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.

## 18.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.

## 18.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:

> `

>     \005printer 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.

## 18.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
`local (printer | all)` | client printcap and configuration information
`server (printer | all)` | server printcap and configuration information

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:

- `start, stop, enable, disable`
  Start and stop will start and stop printing for a specified queue. Enable and disable enable and disable sending and/or accepting jobs for the queue.
- `abort, kill`
  Abort will cause the process doing the actual job printing to be terminated. Kill does an abort, and then restarts the printing process. These commands are used to restart a queue printing after some disaster.
- `topq` Places selected jobs at the top of the print queue.
- `status`
  Shows a status display of the print spools on the server.

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

- `lpq, lprm`
  Invokes the lpq or lprm program from lpc. Useful when in the interactive mode.
- `hold, holdall, release`
  The hold command will cause the selected jobs to be held until released. The holdall jobs sets all jobs submitted to the queue to be held until released. The release command releases jobs for printing. If a job has had an error and is in the error state, the release command will cause it to be reprinted.
- `move, redirect`
  The move command will move selected jobs to the specified spool queue. The redirect command sends all jobs submitted to the queue to be sent to the specified queue.
- `active, lpd, reread`
  The active command will connect to the server for the printer. This is used to check to see if non-LPRng print servers are active. The lpd command will connect to the server and get the process id (PID) of the `lpd` server. The reread command causes a SIGHUP signal to be sent to the lpd process, causing it to reread the `/etc/lpd.conf`, `/etc/printcap`, or `/etc/lpd.perms` files. This is usually done when some important configuration information has been modified and the administrator wants to have the server use the new information.
- `debug`
  This is a desperation facility for developers that allows dynamic enabling of debug information generation. Not normally used in general operation.
- `local, server`
  These commands will print out the configuration information in the local `/etc/lpd.conf` file, as well as the printcap information for the specified printers; `local` prints what the LPRng clients (`lpr, lpq, ...`) would use while `server` prints what the LPRng server (`lpd`) would use if running on this host. This is an extremely useful diagnostic tool for administrators. Not normally used in general operation.

## 18.9 Block Job Transfer

Options used:

- `send_block_format` Transfer job as a block

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:

> `

>     \007printer 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.

## 18.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