1#!/usr/bin/perl -w 2 3use strict; 4use IO::File; 5 6### 7# Read in services entries in the format of the file 8# located at: 9# 10# http://www.iana.org/assignments/port-numbers 11# 12# and merge new entries into the existing services file 13### 14 15if (!defined($ARGV[0]) || !defined($ARGV[1])) { 16 die "usage: update-services.pl services port-numbers\n"; 17} 18 19sub parse_services { 20 my $file = shift; 21 my $names = shift; 22 my $descs = shift; 23 my $emails = shift; 24 25 my $service = qr/[a-zA-Z0-9_+\/.*-]+/; 26 my $protocol = qr/[0-9]+\/[ut][dc]p/; 27 my $description = qr/.*/; 28 29 my $prevserv; 30 31 my $handle = new IO::File; 32 open($handle, $file) or die "$file: $!"; 33 while (<$handle>) { 34 ### Capture lines that look like service entires and 35 ### add them to the hash tables. 36 if (m/^#?\s*($service)\s+($protocol)\s+($description)$/) { 37 my $key = $2; 38 my $str = $3; 39 40 $prevserv = $key; 41 42 $names->{$key} = $1; 43 $str =~ s/\x0d//g; 44 chomp($str); 45 $descs->{$key} = $str; 46 ### Capture the email address line that immediately 47 ### follows service entries. 48 } elsif (defined($prevserv) && m/^#\s+([^ ]+)\s*$/) { 49 my $str = $1; 50 $str =~ s/\x0d//g; 51 chomp($str); 52 $emails->{$prevserv} = $str; 53 $prevserv = undef; 54 } 55 } 56 close($handle); 57} 58 59my %iana_names = (); 60my %iana_descs = (); 61my %iana_emails = (); 62my %local_names = (); 63my %local_descs = (); 64my %local_emails = (); 65 66&parse_services($ARGV[0], \%local_names, \%local_descs, \%local_emails); 67&parse_services($ARGV[1], \%iana_names, \%iana_descs, \%iana_emails); 68 69### 70### Utility functions for parsing and sorting protocol fields (i.e. 1234/tcp). 71### 72sub protonum { 73 my ($a) = split(/\//, shift); 74 return $a; 75} 76 77sub _cmpproto { 78 my $a = shift; 79 my $b = shift; 80 my $res = protonum($a) <=> protonum($b); 81 if ($res == 0) { 82 $res = $a cmp $b; 83 } 84 return $res; 85} 86sub cmpproto { 87 return &_cmpproto($a, $b); 88} 89 90### 91### Find services recently added by IANA 92### 93my @additions = (); 94foreach my $key (sort cmpproto keys(%iana_names)) { 95 if (not exists $local_names{$key}) { 96 push @additions, $key; 97 } 98} 99 100### 101### Find services recently deleted by IANA 102### 103my @deletions = (); 104foreach my $key (sort cmpproto keys(%local_names)) { 105 if (not exists $iana_names{$key}) {; 106 push @deletions, $key; 107 } 108} 109 110### 111### Find services whose local definition conflicts with IANA 112### 113my @conflicts = (); 114foreach my $key (sort cmpproto keys(%local_names)) { 115 if (exists $iana_names{$key} && 116 exists $local_names{$key} && 117 $iana_names{$key} ne $local_names{$key}) { 118 push @conflicts, $key; 119 } 120} 121 122### 123### Merge Services 124### 125my $service = qr/[a-zA-Z0-9_+\/.*-]+/; 126my $protocol = qr/[0-9]+\/[ut][dc]p/; 127my $description = qr/.*/; 128 129my $prev_add; 130my $next_add = shift @additions; 131 132my $prev_del; 133my $next_del = shift @deletions; 134 135my $handle = new IO::File; 136open($handle, $ARGV[0]) or die "$ARGV[0]: $!"; 137my $line = <$handle>; 138 139### 140### Read the existing services file, and process each line. 141### Walk the list of additions and deletions, inserting new service 142### entries and suppressing existing entries in the output. 143### 144while (defined($next_add) && defined($line)) { 145 if ($line =~ m/^#?\s*($service)\s+($protocol)\s+($description)$/) { 146 my $proto = $2; 147 148 my $res; 149 150 ### Deletions (replace with Unassigned) 151 if (undef) { 152 print "# "; 153 print sprintf "% -11s ", protonum($next_del); 154 print "Unassigned\n"; 155 $prev_del = $next_del; 156 $next_del = shift @deletions; 157 $line = <$handle>; 158 next; 159 } 160 161 ### Additions 162 $res = &_cmpproto($next_add, $proto); 163 if ($res == 1) { 164 ### 165 ### Output Unassigned comment if necessary 166 ### 167 if (defined($prev_add) && _cmpproto($prev_add, $proto) == -1) { 168 my $start = &protonum($prev_add) + 1; 169 my $end = &protonum($proto) - 1; 170 print "# "; 171 if ($start < $end) { 172 print sprintf "% -11s ", "$start-$end"; 173 } else { 174 print sprintf "% -11s ", "$start"; 175 } 176 print "Unassigned\n"; 177 $prev_add = undef; 178 } 179 print "$line"; 180 $line = <$handle>; 181 next; 182 } elsif ($res == 0) { 183 # XXX conflict 184 die "conflicting entry: $next_add"; 185 print "$line"; 186 $line = <$handle>; 187 } elsif ($res == -1) { 188 if (defined($prev_add)) { 189 ### 190 ### Update Unassigned Range (if applicable) 191 ### 192 my $start = &protonum($prev_add); 193 my $end = &protonum($next_add); 194 if (($end - $start) > 1) { 195 ++$start; 196 --$end; 197 print "# "; 198 if ($start < $end) { 199 print sprintf "% -11s ", "$start-$end"; 200 } else { 201 print sprintf "% -11s ", "$start"; 202 } 203 print "Unassigned\n"; 204 } 205 } 206 207 ### 208 ### Print the new IANA entry (addition to file) 209 ### 210 print sprintf "% -15s ", $iana_names{$next_add}; 211 print sprintf "% -11s ", $next_add; 212 print "# ". $iana_descs{$next_add} if exists $iana_descs{$next_add}; 213 print "\n"; 214 ### 215 ### Print email address / other comment after new entry 216 ### 217 if (exists $iana_emails{$next_add}) { 218 print "# "; 219 print $iana_emails{$next_add}; 220 print "\n"; 221 } 222 $prev_add = $next_add; 223 $next_add = shift @additions; 224 } 225 } elsif ($line =~ m/^#\s+([0-9]+)-?([0-9]*)\s+Unassigned.*$/) { 226 if ($1 != &protonum($next_add)) { 227 print "$line"; 228 } 229 $line = <$handle>; 230 } else { 231 print "$line"; 232 $line = <$handle>; 233 } 234} 235close($handle); 236