1#!/usr/bin/perl
2
3# Copyright (C) Internet Systems Consortium, Inc. ("ISC")
4#
5# SPDX-License-Identifier: MPL-2.0
6#
7# This Source Code Form is subject to the terms of the Mozilla Public
8# License, v. 2.0.  If a copy of the MPL was not distributed with this
9# file, you can obtain one at https://mozilla.org/MPL/2.0/.
10#
11# See the COPYRIGHT file distributed with this work for additional
12# information regarding copyright ownership.
13
14# This is a tool for sending an arbitrary packet via UDP or TCP to an
15# arbitrary address and port.  The packet is specified in a file or on
16# the standard input, in the form of a series of bytes in hexadecimal.
17# Whitespace is ignored, as is anything following a '#' symbol.
18#
19# For example, the following input would generate normal query for
20# isc.org/NS/IN":
21#
22#     # QID:
23#     0c d8
24#     # header:
25#     01 00 00 01 00 00 00 00 00 00
26#     # qname isc.org:
27#     03 69 73 63 03 6f 72 67 00
28#     # qtype NS:
29#     00 02
30#     # qclass IN:
31#     00 01
32#
33# Note that we do not wait for a response for the server.  This is simply
34# a way of injecting arbitrary packets to test server resposnes.
35#
36# Usage: packet.pl [-a <address>] [-d] [-p <port>] [-t (udp|tcp)] [-r <repeats>] [filename]
37#
38# Options:
39# -a <address>:  specify address (XXX: no IPv6 support yet)
40# -p <port>:     specify port
41# -t <protocol>: specify UDP or TCP
42# -r <num>:      send packet <num> times
43# -d:            dump response packets
44#
45# If not specified, address defaults to 127.0.0.1, port to 53, protocol
46# to udp, and file to stdin.
47
48require 5.006.001;
49
50use strict;
51use Getopt::Std;
52use IO::File;
53use IO::Socket;
54
55sub usage {
56    print ("Usage: packet.pl [-a address] [-d] [-p port] [-t (tcp|udp)] [-r <repeats>] [file]\n");
57    exit 1;
58}
59
60my $sock;
61my $proto;
62
63sub dumppacket {
64    use Net::DNS;
65    use Net::DNS::Packet;
66
67    my $rin;
68    my $rout;
69    $rin = '';
70    vec($rin, fileno($sock), 1) = 1;
71    select($rout = $rin, undef, undef, 1);
72    if (vec($rout, fileno($sock), 1)) {
73        my $buf;
74
75        if ($proto eq "udp") {
76            $sock->recv($buf, 512);
77        } else {
78            my $n = $sock->sysread($buf, 2);
79            return unless $n == 2;
80            my $len = unpack("n", $buf);
81            $n = $sock->sysread($buf, $len);
82            return unless $n == $len;
83        }
84
85        my $response;
86        if ($Net::DNS::VERSION > 0.68) {
87            $response = new Net::DNS::Packet(\$buf, 0);
88            $@ and die $@;
89        } else {
90            my $err;
91            ($response, $err) = new Net::DNS::Packet(\$buf, 0);
92            $err and die $err;
93        }
94        $response->print;
95    }
96}
97
98my %options={};
99getopts("a:dp:t:r:", \%options);
100
101my $addr = "127.0.0.1";
102$addr = $options{a} if defined $options{a};
103
104my $port = 53;
105$port = $options{p} if defined $options{p};
106
107$proto = "udp";
108$proto = lc $options{t} if defined $options{t};
109usage if ($proto !~ /^(udp|tcp)$/);
110
111my $repeats = 1;
112$repeats = $options{r} if defined $options{r};
113
114my $file = "STDIN";
115if (@ARGV >= 1) {
116    my $filename = shift @ARGV;
117    open FH, "<$filename" or die "$filename: $!";
118    $file = "FH";
119}
120
121my $input = "";
122while (defined(my $line = <$file>) ) {
123    chomp $line;
124    $line =~ s/#.*$//;
125    $input .= $line;
126}
127
128$input =~ s/\s+//g;
129my $data = pack("H*", $input);
130my $len = length $data;
131
132my $output = unpack("H*", $data);
133print ("sending $repeats time(s): $output\n");
134
135$sock = IO::Socket::INET->new(PeerAddr => $addr, PeerPort => $port,
136				 Blocking => 0,
137				 Proto => $proto,) or die "$!";
138
139STDOUT->autoflush(1);
140
141my $bytes = 0;
142while ($repeats > 0) {
143    if ($proto eq "udp") {
144	$bytes += $sock->send($data);
145    } else {
146	$bytes += $sock->syswrite(pack("n", $len), 2);
147	$bytes += $sock->syswrite($data, $len);
148    }
149
150    $repeats = $repeats - 1;
151
152    if ($repeats % 1000 == 0) {
153	print ".";
154    }
155}
156
157$sock->shutdown(SHUT_WR);
158if (defined $options{d}) {
159    dumppacket;
160}
161
162$sock->close;
163close $file;
164print ("\nsent $bytes bytes to $addr:$port\n");
165