Bonjour Printing and DNS-SD records
Hugo Monteiro <[email protected]> Mon, 26 Jul 2010 17:42:53 +0100
| Newsgroups | gmane.network.djbdns |
|---|---|
| Message-ID | <[email protected]> |
Hello list, Some time ago I posted to the list with the subject "unescaped double quotes in TXT record". Just to let know that i cooked up a small perl script the other day to create such tinydns records. I shamelessly borrowed code from Anders Brownworth's buildRecord.cgi script. There's a strong probability that it contains bugs, but for the use i gave it, it worked. I should only notice that sometimes specifying the Product attribute present in the PPD file doesn't work. However, following the Bonjour Printing specification, if you choose to use the generic MFG+MDL (manufacturer + model) records, it should work flawlessly. Attached goes the script. Bug reports, suggestions, etc are welcomed. Regards, Hugo Monteiro. -- fct.unl.pt:~# cat .signature Hugo Monteiro Email : [email protected] Telefone : +351 212948300 Ext.15307 Web : http://hmonteiro.net Divisão de Informática Faculdade de Ciências e Tecnologia da Universidade Nova de Lisboa Quinta da Torre 2829-516 Caparica Portugal Telefone: +351 212948596 Fax: +351 212948548 www.fct.unl.pt [email protected] fct.unl.pt:~# _
add-printer.pl
(text/x-perl, 5.2 KB)
#!/usr/bin/perl
#
# Global vars
#
use vars qw/ %opt /;
use Switch;
%rkeys = ();
# Defaults
# Everything in this hash will also be part of the dns-sd TXT record.
$rkeys{"txtvers"} = "1";
$rkeys{"qtotl"} = "3";
$rkeys{"pdl"} = "application/postscript";
# Stop edit here
#
# Default values
#
$ttl = 60;
#
# Command line options processing
#
sub init()
{
use Getopt::Std;
my $opt_string = 'hemt:n:d:s:z:f:p:N:';
%opt=();
getopts( "$opt_string", \%opt );
if ((!%opt || $opt{h} || !$opt{t} || !$opt{n} || !$opt{d} || !$opt{s} || !$opt{z}) || ($opt{f} && $opt{p}) || (!$opt{f} && !$opt{p})) { usage(); }
}
#
# Message about this program and how to use it
#
sub usage()
{
print STDERR << "EOF";
How it works.
usage: $0 [-h] [-e] [-m] [-N <note>] <-t type> <-n printername> <-d description> <-s server:priority:weight:port> <-z zone> <[-f ppd_file|-p product_string]>
-h : (this) help message.
-e : include extra dns-sd service discovery records.
-m : use Manufacturer and Model name from PPD. Default is to use Product name.
-p : use product_string instead of extracting information from PPD file.
-t : service type, can be one of "lpr", "ipp" or "other".
-n : printer name (no spaces, dots or special characters).
-d : printer description (printer friendly name).
-s : string prividing FQDN/IP of the print server, priority, weight and port of the service.
-z : name of the zone where the service is to be published.
-f : path to PPD file.
$0 will output results to the standard output.
examples:
$0 -e -t ipp -n HP -d "HP LaserJet 4050" -s print.hmonteiro.net:0:0:631 -z home.hmonteiro.net -p "(HP LaserJet 4050 Series )"
$0 -e -t ipp -n HP -d "HP LaserJet 4050" -s print.hmonteiro.net:0:0:631 -z home.hmonteiro.net -f HP.ppd
EOF
exit;
}
# Adds dns-sd SRV browsing records
sub dnssd
{
print "\n";
print "# Service browsing\n";
print "^b._dns-sd._udp.$zone:$zone:60::in\n";
print "^lb._dns-sd._udp.$zone:$zone:60::in\n";
print "\n";
print "# Available services\n";
print "^_services._dns-sd._udp.$zone:_ipp._tcp.$zone:60::in\n";
print "^_services._dns-sd._udp.$zone:_printer._tcp.$zone:60::in\n";
print "^_services._dns-sd._udp.$zone:_pdl-datastream._tcp.$zone:60::in\n";
print "\n";
}
sub buildsrv
{
my ($srv) = @_;
my ($server, $prio, $weight, $port, $service) = split /:/, $srv;
if ( ( $prio >= 0 && $prio <= 65535 ) &&
( $weight >= 0 && $weight <= 65535 ) &&
( $port >= 0 && $port <= 65535 ) ) {
my $target = "";
@chunks = split /\./, $server;
foreach $chunk ( @chunks ) {
$target = $target . characterCount( $chunk ) . $chunk;
}
$result = ":" . escapeText( $service ) . ":33:" . escapeNumber( $prio ) .
escapeNumber( $weight ) . escapeNumber( $port ) .
$target . "\\000" . ":" . $ttl;
return $result;
}
else {
print "priority, weight or port not within 0 - 65535\n";
exit (1);
}
}
sub escapeNumber {
my $number = pop @_;
my $highNumber = 0;
if ( $number - 256 >= 0 ) {
$highNumber = int( $number / 256 );
$number = $number - ( $highNumber * 256 );
}
$out = sprintf "\\%.3lo", $highNumber;
$out = $out . sprintf "\\%.3lo", $number;
return( $out );
}
sub escapeText {
my $line = pop @_;
my $out;
my @chars = split //, $line;
foreach $char ( @chars ) {
if ( $char =~ /[\r\n\t: \\\/]/ ) {
$out = $out . sprintf "\\%.3lo", ord $char;
} else {
$out = $out . $char;
}
}
return( $out );
}
sub characterCount {
my $line = pop @_;
my @chars = split //, $line;
my $count = @chars;
return( sprintf "\\%.3lo", $count );
}
sub txtlen {
my (%hash) = @_;
my $ref = \%hash;
my $str = "";
my $strlen = 0;
my $len = 0;
my $ret = "";
foreach (keys(%rkeys)) {
$str = $_."=".$$ref{$_};
$strlen = length($str);
$len = 0;
$len = $strlen+int($strlen/8)*2;
$len = sprintf("%3d", $len);
$len=~ tr/ /0/;
$ret = $ret."\\$len$str";
}
return $ret;
}
sub getprod {
my ($ppd, $use_mm) = @_;
my @data = ();
my $product = "";
my $manufacturer = "";
my $model = "";
open(PPD, "< $ppd") or die "Can't open $ppd : $!";
while (<PPD>) {
chomp();
($attribute,$value) = split(/:/,$_);
if ($use_mm) {
$manufacturer = $value if ($attribute eq "\*Manufacturer");
$manufacturer =~ s/^\s+//;
$model = $value if ($attribute eq "\*ModelName");
$model =~ s/^\s+//;
} else {
$product = $value if ($attribute eq "\*Product");
$product =~ s/^\s+//;
}
}
close(PPD);
if ($use_mm) {
$product = "($manufacturer $model)";
}
$product =~ s/"//g;
return $product;
}
# START of program
init();
$name = $opt{n};
if ($opt{t} eq "ipp") {
$type = "_ipp";
$rkeys{"rp"} = "printers/".$name;
}
if ($opt{t} eq "lpr") {
$type = "_printer";
$rkeys{"rp"} = $name;
}
if ($opt{t} eq "other") {
$type = "_pdl-datastream";
$rkeys{"rp"} = $name;
}
if ($opt{N}) {
$rkeys{"note"} = $opt{N};
}
$rkeys{"ty"} = $opt{d};
$zone = $opt{z};
$srv = $opt{s}.":".$name.".".$type."._tcp.".$zone;
if ($opt{f}) {
$ppd = $opt{f};
$rkeys{"product"} = getprod($ppd,$opt{m});
} else {
$rkeys{"product"} = $opt{p};
}
if ( $opt{e} ) {
dnssd($zone);
}
print "^$type._tcp.$zone:".$name.".".$type."._tcp.".$zone.":60::in\n";
print buildsrv($srv)."\n";
print ":".$name.".$type._tcp.".$zone.":16:".txtlen(%rkeys).":60\n";;
# END of program