#!/usr/bin/perl
# $Header: /u/cvsroot/env/b/make-srv,v 1.3 2002/10/30 17:26:42 mayoff Exp $

use strict;
use Getopt::Long;
use Pod::Usage;

my $priority = 0;
my $weight = 0;
my $service;
my $port;
my $target;
my $help = 0;

my $result = GetOptions(
    'help|?' => \$help,
    'service=s' => \$service,
    'priority=i' => \$priority,
    'weight=i' => \$weight,
    'port=i' => \$port,
    'target=s' => \$target
) || pod2usage(2);

pod2usage(1) if $help;
pod2usage(3) if (!defined($service) || !defined($port) || !defined($target));

my $data = tinydns_escape(
    pack("nnn", $priority, $weight, $port)
    . dotted_sequence_to_label_sequence($target));

print ":$service:33:$data\n";

exit 0;

sub tinydns_escape {
    my ($data) = @_;

    $data =~ s{[\000-\037:\\\177-\377]}{
	sprintf("\\%03o", unpack('C', $&))
    }ge;

    return $data;
}

sub dotted_sequence_to_label_sequence {
    my ($dotted) = @_;
    my @chars = split(//, $dotted);
    my $l = scalar(@chars);
    my $out = '';
    my $label = '';

    my $i = 0;
    while (1) {
	my $c;
	if ($i < $l) {
	    $c = $chars[$i];
	    $i++;
	}

	else {
	    $c = '.';
	}

	if ($c eq '.') {
	    my $ll = length($label);
	    if ($ll > 63) {
		die "$dotted contains a label of length $ll, but max length is 63";
	    }
	    if ($ll > 0) {
		$out .= pack('C', $ll);
		$out .= $label;
		$label = '';
	    }

	    last if ($i >= $l);

	    next;
	}

	if ($c eq '\\') {
	    if ($i < $l) {
		$c = $chars[$i];
		$i++;
		if ($c ge '0' && $c le '7') {
		    my $o = $c;
		    if ($i < $l) {
			$c = $chars[$i];
			if ($c ge '0' && $c le '7') {
			    $i++;
			    $o .= $c;
			    if ($i < $l) {
				$c = $chars[$i];
				if ($c ge '0' && $c le '7') {
				    $i++;
				    $o .= $c;
				}
			    }
			}
		    }
		    $c = pack('C', oct($o));
		}
	    }
	}

	$label .= $c;
    }

    $out .= "\000";
    return $out;
}

__END__

=head1 NAME

make-srv - Make an SRV record for tinydns

=head1 SYNOPSIS

make-srv -service I<service> -target I<target> -port I<port> [I<options>]

Options:

=over 4

=item -priority I<priority>

=item -weight I<weight>

=head1 DESCRIPTION

This command prints a DNS SRV record to standard output in
C<tinydns-data> format. This record is defined by RFC 2052.

The C<-service> flag specifies the domain name for which
the SRV record is defined. This name should have the format
I<service>C<.>I<protocol>C<.>I<domain>. For example:
C<http.tcp.dqd.com>.  You must specify the C<-service> flag.

The C<-target> flag specifies the target domain name of the record.  The
I<target> must be a domain name with an associated A record.  (This
command doesn't verify that, but the RFC says it's a requirement.)  You
must specify the C<-target> flag.

The C<-port> flag specifies the port number of the record.  You must
specify the C<-port> flag.

The C<-priority> and C<-weight> flags specify the priority and weight of
the record.  These flags are optional; I<priority> and I<weight> are
zero by default.

Example:

    $ make-srv -service http.tcp.dqd.com -target zot.dqd.com -port 80
    :http.tcp.dqd.com:33:\000\000\000\000\000P\003zot\003dqd\003com\000

You may add the I<ttl>, I<timestamp>, and I<lo> fields to the end of the
line yourself if necessary.  For example,

    :http.tcp.dqd.com:33:\000\000\000\000\000P\003zot\003dqd\003com\000:::in

is the same SRV record as in the example above, but will be visible only
to clients in the C<in> location.  See the C<tinydns-data> documentation
for help with these fields.

=head1 LINKS

=over

=item *

RFC 2052: L<http://www.ietf.org/rfc/rfc2052.txt>

=item *

C<tinydns-data> documentation: L<http://cr.yp.to/djbdns/tinydns-data.html>

=back

=cut

