File Coverage

blib/lib/Acme/RFC4824.pm
Criterion Covered Total %
statement 7 9 77.7
branch n/a
condition n/a
subroutine 3 3 100.0
pod n/a
total 10 12 83.3


line stmt bran cond sub pod time code
1             package Acme::RFC4824;
2              
3 2     2   44372 use warnings;
  2         5  
  2         60  
4 2     2   12 use strict;
  2         4  
  2         62  
5              
6 2     2   1756 use Moose;
  0            
  0            
7             use Carp;
8             use bytes;
9              
10             our $VERSION = '0.02';
11              
12             # a hash ref of mappings from ASCII to ASCII art representations
13             has 'ascii2art_map' => (
14             is => 'ro',
15             );
16              
17             # the default SFS frame size in bytes
18             has 'default_framesize' => (
19             is => 'ro',
20             isa => 'Int',
21             default => 255,
22             );
23              
24             sub BUILD {
25             my $self = shift;
26             my $arg_ref = shift;
27              
28             if (exists $arg_ref->{'DEFAULT_FRAMESIZE'}) {
29             if ($arg_ref->{'DEFAULT_FRAMESIZE'} > 255) {
30             croak "Frame size too large, can at most be 255";
31             }
32             $self->{'default_framesize'} = $arg_ref->{'DEFAULT_FRAMESIZE'};
33             }
34             # initialize mapping from characters to ASCII art
35             # ASCII-Art comes directly from RFC4824
36             $self->{'ascii2art_map'}->{'A'} = << 'XEOF';
37             0
38             /||
39             / \
40             XEOF
41             $self->{'ascii2art_map'}->{'B'} = << 'XEOF';
42             __0
43             ||
44             / \
45             XEOF
46             $self->{'ascii2art_map'}->{'C'} = << 'XEOF';
47             \0
48             ||
49             / \
50             XEOF
51             $self->{'ascii2art_map'}->{'D'} = << 'XEOF';
52             |0
53             ||
54             / \
55             XEOF
56             $self->{'ascii2art_map'}->{'E'} = << 'XEOF';
57             0/
58             ||
59             / \
60             XEOF
61             $self->{'ascii2art_map'}->{'F'} = << 'XEOF';
62             0__
63             ||
64             / \
65             XEOF
66             $self->{'ascii2art_map'}->{'G'} = << 'XEOF';
67             0
68             ||\
69             / \
70             XEOF
71             $self->{'ascii2art_map'}->{'H'} = << 'XEOF';
72             __0
73             /|
74             / \
75             XEOF
76             $self->{'ascii2art_map'}->{'I'} = << 'XEOF';
77             \0
78             /|
79             / \
80             XEOF
81             $self->{'ascii2art_map'}->{'J'} = << 'XEOF';
82             |0__
83             |
84             / \
85             XEOF
86             $self->{'ascii2art_map'}->{'K'} = << 'XEOF';
87             0|
88             /|
89             / \
90             XEOF
91             $self->{'ascii2art_map'}->{'L'} = << 'XEOF';
92             0/
93             /|
94             / \
95             XEOF
96             $self->{'ascii2art_map'}->{'M'} = << 'XEOF';
97             0__
98             /|
99             / \
100             XEOF
101             $self->{'ascii2art_map'}->{'N'} = << 'XEOF';
102             0
103             /|\
104             / \
105             XEOF
106             $self->{'ascii2art_map'}->{'O'} = << 'XEOF';
107             _\0
108             |
109             / \
110             XEOF
111             $self->{'ascii2art_map'}->{'P'} = << 'XEOF';
112             __0|
113             |
114             / \
115             XEOF
116             $self->{'ascii2art_map'}->{'Q'} = << 'XEOF';
117             __0/
118             |
119             / \
120             XEOF
121             $self->{'ascii2art_map'}->{'R'} = << 'XEOF';
122             __0__
123             |
124             / \
125             XEOF
126             $self->{'ascii2art_map'}->{'S'} = << 'XEOF';
127             __0
128             |\
129             / \
130             XEOF
131             $self->{'ascii2art_map'}->{'T'} = << 'XEOF';
132             \0|
133             |
134             / \
135             XEOF
136             $self->{'ascii2art_map'}->{'U'} = << 'XEOF';
137             \0/
138             |
139             / \
140             XEOF
141             $self->{'ascii2art_map'}->{'V'} = << 'XEOF';
142             |0
143             |\
144             / \
145             XEOF
146             $self->{'ascii2art_map'}->{'W'} = << 'XEOF';
147             0/_
148             |
149             / \
150             XEOF
151             $self->{'ascii2art_map'}->{'X'} = << 'XEOF';
152             0/
153             |\
154             / \
155             XEOF
156             $self->{'ascii2art_map'}->{'Y'} = << 'XEOF';
157             \0__
158             |
159             / \
160             XEOF
161             $self->{'ascii2art_map'}->{'Z'} = << 'XEOF';
162             0__
163             |\
164             / \
165             XEOF
166             return 1;
167             }
168              
169             sub decode {
170             my $self = shift;
171             my $arg_ref = shift;
172              
173             my $frame = $arg_ref->{FRAME};
174             if (! defined $frame) {
175             croak "You need to pass a frame to be decoded.";
176             }
177             my $last_frame_undo = rindex $frame, 'T';
178             if ($last_frame_undo > 0) {
179             # if a FUN was found, take everything to the right to be the
180             # new frame.
181             $frame = 'Q' . substr($frame, $last_frame_undo + 2);
182             }
183             while ($frame =~ m{ (.*) [^S]S (.*) }xms) {
184             # delete the signal before a 'S' (SUN, signal undo)
185             $frame = $1 . $2;
186             }
187             $frame =~ s/[U-Y]//g; # ignore ACK, KAL, NAK, RTR and RTT signals
188             my ($header, $payload, $checksum) =
189             ($frame =~ m{\A Q([A-E][A-B][A-P]{2}) ([A-P]+) ([A-P]{4})R \z}xms);
190             if (! defined $header || ! defined $payload || ! defined $checksum) {
191             croak "Invalid frame format.";
192             }
193             return $self->__pack($payload);
194             }
195              
196             sub __pack {
197             my $self = shift;
198             my $frame = shift;
199              
200             # convert from ASCII to hex
201             $frame =~ tr/A-J/0-9/;
202             $frame =~ tr/K-P/a-f/;
203             return pack('H*', $frame);
204             }
205              
206             sub __unpack {
207             my $self = shift;
208             my $data = shift;
209              
210             # unpack
211             my $result = unpack('H*', $data);
212             $result =~ tr/0-9/A-J/;
213             $result =~ tr/a-f/K-P/;
214             return $result;
215             }
216              
217             sub encode {
218             my $self = shift;
219             my $arg_ref = shift;
220              
221             my $sfs_frame = 'Q'; # Frame Start FST
222              
223             # type is ASCII or ASCII-ART
224             my $type = 'ASCII';
225             if (defined $arg_ref->{TYPE}) {
226             $type = $arg_ref->{TYPE};
227             }
228             if ($type ne 'ASCII' && $type ne 'ASCII art') {
229             croak "Invalid output type";
230             }
231              
232             my $packet = $arg_ref->{PACKET};
233             if (! defined $packet || ! length($packet)) {
234             croak "You need to pass an IP packet";
235             }
236              
237             my $checksum = 0;
238             if (defined $arg_ref->{CHECKSUM}) {
239             $checksum = $arg_ref->{CHECKSUM};
240             };
241             # TODO - implement CRC 16 support
242             if ($checksum == 1) {
243             croak "CRC 16 support not implemented (yet).";
244             }
245             elsif ($checksum > 1) {
246             croak "Invalid checksum type";
247             }
248              
249             my $framesize = $self->{default_framesize};
250             if (exists $arg_ref->{FRAMESIZE}) {
251             $framesize = $arg_ref->{FRAMESIZE};
252             }
253             # TODO - implement fragmenting
254             # note: honor DF bit in IP packets
255             if (length($packet) > $framesize) {
256             croak "Fragmenting not implemented (yet).";
257             }
258              
259             # TODO - implement support for gzipped frames
260             my $gzip = $arg_ref->{GZIP};
261             if ($gzip) {
262             croak "GZIP support not implemented (yet).";
263             }
264              
265             my $packet_ascii = $self->__unpack($packet);
266             if (substr($packet_ascii, 0, 1) eq 'E') { # E=4: IPv4
267             $sfs_frame .= 'B';
268             }
269             elsif (substr($packet_ascii, 0, 1) eq 'G') { # G=6: IPv6
270             $sfs_frame .= 'C';
271             }
272             else {
273             croak "Invalid IP version";
274             }
275              
276             $sfs_frame .= 'A'; # Checksum Type: none
277             $sfs_frame .= 'AA'; # Frame number 0x00
278              
279             $sfs_frame .= $packet_ascii;
280              
281             $sfs_frame .= 'AAAA'; # No checksum, so we just set it zeros
282             $sfs_frame .= 'R'; # Frame End, FEN
283              
284             if ($type eq 'ASCII') {
285             return $sfs_frame;
286             }
287             else { # ASCII-ART
288             my @sfss_ascii_art_frames = ();
289             for (my $i = 0; $i < length($sfs_frame); $i++) {
290             my $char = substr($sfs_frame, $i, 1);
291             my $aa_repr = $self->ascii2art_map->{$char};
292             if (! defined $aa_repr) {
293             die "No ASCII-Art representation for '$char'";
294             }
295             push @sfss_ascii_art_frames, $aa_repr;
296             }
297             if (wantarray) {
298             return @sfss_ascii_art_frames;
299             }
300             else {
301             return join "\n", @sfss_ascii_art_frames;
302             }
303             }
304             }
305             1;
306             __END__
307              
308             =head1 NAME
309              
310             Acme::RFC4824 - Internet Protocol over Semaphore Flag Signaling System (SFSS)
311              
312             =head1 VERSION
313              
314             Version 0.01
315              
316             =head1 SYNOPSIS
317              
318             This module is used to help you implement RFC 4824 - The Transmission
319             of IP Datagrams over the Semaphore Flag Signaling System (SFSS).
320              
321             It can be used to convert IP datagrams to SFS frames and the other
322             way round. Furthemore, it can be used to display an ASCII art representation
323             of the SFS frame.
324              
325             use Acme::RFC4824;
326              
327             my $sfss = Acme::RFC4824->new();
328            
329             # get IP datagram from somewhere (for example Net::Pcap)
330             # print a representation of the SFS frame
331             print $sfss->encode({
332             TYPE => 'ASCII art',
333             PACKET => $datagram,
334             });
335              
336             # get an ASCII representation of the SFS frame
337             my $sfs_frame = $sfss->encode({
338             TYPE => 'ASCII',
339             PACKET => $datagram,
340             });
341              
342             # get an SFS frame from somewhere
343             # (for example from someone signaling you)
344             # get an IP datagram from the frame
345             my $datagram = $sfss->decode({
346             FRAME => $frame,
347             });
348              
349             =head1 EXPORT
350              
351             As this module is supposed to be used in an object oriented fashion, it
352             does not export anything.
353              
354             =head1 FUNCTIONS
355              
356             =head2 BUILD
357              
358             see new()
359              
360             =head2 new
361              
362             Constructs a new object for you. Takes the following named parameters:
363              
364             =over 1
365              
366             =item * DEFAULT_FRAMESIZE (optional)
367              
368             The framesize in bytes that is used whenever the FRAMESIZE paramter is
369             not given for encode. Defaults to 255 bytes (the maximum SFS frame size).
370              
371             =back
372              
373             =head2 encode
374              
375             Encodes an IP datagram into one or more SFS frames. Currently, fragmenting
376             is not (yet) supported, so it will always encode into one frame (or
377             complain that the IP packet is too large to encode into one frame).
378              
379             Takes the following named parameters:
380              
381             =over 4
382              
383             =item * TYPE
384              
385             Determines the output format. Can either be 'ASCII' or 'ASCII art'.
386             In the first case, a string representation of the SFS frame is returned.
387             In the second case, an ASCII art representation is returned - as an
388             array of ASCII art strings in list context or as the concatenation in
389             scalar context.
390              
391             =item * PACKET
392              
393             The IP packet that you want to convert
394              
395             =item * CHECKSUM (optional)
396              
397             The checksum algorithm. Only 0 (no checksum) is implemented at the moment.
398              
399             =item * FRAMESIZE (optional)
400              
401             The optional maximal frame size of the SFS frame. Will later be used
402             to fragment, currently only limits the size of the packet you can encode.
403              
404             =item * GZIP (optional)
405              
406             Not implemented yet, meant to support the gzipped frame variant of RFC 4824.
407              
408             =back
409              
410             =head2 decode
411              
412             Decodes one or more SFS frame into an IP datagram.
413              
414             Takes the following named parameters:
415              
416             =over 4
417              
418             =item * FRAME
419              
420             An ASCII representation of the SFS frame which you would like to decode
421             into an IP datagram.
422              
423             =back
424              
425             =head2 ascii2art_map
426              
427             Read-only accessor for the attribute with the same name.
428             Returns a hash reference that maps SFS ASCII characters to an ASCII art
429             representation of the given character. There is probably no need to use
430             this from the outside.
431              
432             =head2 default_framesize
433              
434             Read-only accessor for the attribute with the same name.
435             Returns the default SFS framesize. There is probably no need to use this
436             from the outside.
437              
438             =head2 meta
439              
440             From Moose.pm: This is a method which provides access to the current class's
441             meta-class. Only used internally.
442              
443             =head1 AUTHOR
444              
445             Alexander Klink, C<< <alech at cpan.org> >>
446              
447             =head1 BUGS
448              
449             Please report any bugs or feature requests to
450             C<bug-acme-rfc4824 at rt.cpan.org>, or through the web interface at
451             L<http://rt.cpan.org/NoAuth/ReportBug.html?Queue=Acme-RFC4824>.
452             I will be notified, and then you'll automatically be notified of progress on
453             your bug as I make changes.
454              
455             =head1 SUPPORT
456              
457             You can find documentation for this module with the perldoc command.
458              
459             perldoc Acme::RFC4824
460              
461             You can also look for information at:
462              
463             =over 4
464              
465             =item * AnnoCPAN: Annotated CPAN documentation
466              
467             L<http://annocpan.org/dist/Acme-RFC4824>
468              
469             =item * CPAN Ratings
470              
471             L<http://cpanratings.perl.org/d/Acme-RFC4824>
472              
473             =item * RT: CPAN's request tracker
474              
475             L<http://rt.cpan.org/NoAuth/Bugs.html?Dist=Acme-RFC4824>
476              
477             =item * Search CPAN
478              
479             L<http://search.cpan.org/dist/Acme-RFC4824>
480              
481             =back
482              
483             =head1 ACKNOWLEDGEMENTS
484              
485             Thanks to the RFC 4824 authors for letting me use their ASCII art in this
486             module.
487              
488             =head1 COPYRIGHT & LICENSE
489              
490             Copyright 2007 Alexander Klink, all rights reserved.
491              
492             This program is free software; you can redistribute it and/or modify it
493             under the same terms as Perl itself.
494              
495