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.
Options used:
lpd_port=
Port for LPD to accept connectionoriginate_port=
Ports to originate connections onreuse_addr
Set SO_REUSEADDR flag on connectionretry_econnrefused
Retry on connect ECONNREFUSED errorretry_nolink
Retry on device open or connection ffailuresocket_linger#
Linger time for socketsRFC1179 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).
Options used:
remote_support=
Remote operations supportedAfter 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.
lpc
to control a non-LPRng printer,
it will not work.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.Options used:
longnumber
Long job number (6 digits)send_data_first
Send data files firstuse_shorthost
Use short hostnameA 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:
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:
longnumber
option will allow LPRng to use a 6 digit
job number for files in the print queue.use_shorthost
option will force it to
use short host names in control and data files.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.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.
dfA
,
dfB
,
...
dfZ
,
dfa
,
dfz
.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.
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 |
r | RFC1179 | File to print with FORTRAN carriage control |
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 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.
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.
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.
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:
start, stop, enable, disable
abort, kill
topq
Places selected jobs at the top of the print queue. status
The following commands are extensions to the basic set provided by the BSD LPD system.
lpq, lprm
hold, holdall, release
move, redirect
active, lpd, reread
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
Options used:
send_block_format
Transfer job as a blockIn 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.
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.