File Coverage

blib/lib/Net/Telnet/Options.pm
Criterion Covered Total %
statement 133 206 64.5
branch 54 128 42.1
condition 26 82 31.7
subroutine 15 17 88.2
pod 11 16 68.7
total 239 449 53.2


line stmt bran cond sub pod time code
1             # Net::Telnet::Options
2             # Module to deal with telnet options via code refs that are called when
3             # an option is encountered. Defaults to refusing to do any offered options.
4              
5             =head1 NAME
6              
7             Net::Telnet::Options - Telnet options over any socket
8              
9             =head1 VERSION
10            
11             This document describes Net::Telnet::Options version 0.0.1
12              
13             =head1 SYNOPSIS
14              
15             use Net::Telnet::Options;
16              
17             my %options = (BINARY => { 'DO' => sub {} },
18             MSP => { 'DO' => sub {} } );
19              
20             my $nto = Net::Telnet::Options->new(%options);
21              
22             OR
23              
24             my $nto = Net::Telnet::Options->new();
25              
26             # Accept and deal with incoming TERM TYPE option requests
27             $nto->acceptDoOption('TTYPE', {'SB' => \&ttype_sb_callback } );
28              
29             sub ttype_sb_callback
30             {
31             my ($cmd, $subcmd, $data, $pos) = @_;
32              
33             $nto->sendOpt($socket, 'SB', 24, 'IS', 'VT100');
34             return ;
35             }
36              
37             # Actively ask the connected party to do the NAWS option
38             $nto->activeDoOption('NAWS', {'SB' => \&naws_sb_callback } );
39              
40             # Parse and use the NAWS information
41             sub naws_sb_callback
42             {
43             my ($cmd, $subcmd, $data, $pos) = @_;
44             print ("NAWS SB: $cmd\n");
45             return unless($cmd eq 'IS');
46             my ($width, $height) = unpack('nn', $subcmd);
47             print ("NAWS width, height: $width, $height\n");
48             return ;
49             }
50              
51             # Add a callback to deal with incoming ECHO requests
52             $nto->addOption(0, 'ECHO', {'WILL' => \&willecho_callback,
53             'WONT' => \&wontecho_callback });
54              
55             # Agree to to let them do MXP
56             $nto->acceptWillOption('MXP');
57              
58             # Send the active options to the connected party
59             $nto->doActiveOptions($socket);
60              
61             # Assuming $socket is an IO::Handle/Socket
62              
63             # Let NTO parse telnet options from incoming data:
64             my $data;
65             recv($socket, $data, 1024, 0);
66             $data = $nto->answerTelnetOpts($socket, $data);
67              
68             =head1 DESCRIPTION
69              
70             This module is intended to handle Telnet Option requests on any given socket. Per default it refuses to do any option asked of it by the connected party. Any known telnet option can be set to be explicitly accepted or actively asked of the other side. Callback subroutines allow the user to execute some action upon receiving a particular option.
71              
72             An instance of Net::Telnet::Options must be created for each socket, to keep track of the current state of each client.
73              
74             =head1 INTERFACE
75              
76             =head2 new (%options)
77              
78             The new method can be passed a hash of default option callbacks to set. The keys of the hash are either option names, as given in the %possoptions hash at the top of this module, or option numbers from the appropriate RFCs.
79              
80             The values of each item are hash references, with the keys being the option types to be handled, one of either WILL, WONT, DO, DONT, SB or RAW. The values should contain sub references of the callbacks to be called.
81              
82             =head2 addOption ($activeflag, %opts)
83              
84             The addOption method can be used to add more options in the same format as the new command, after the instance has been created. The additional activeflag can be set to either 0 or 1 to activate an option. An active option is one that is actively requested of the connected party.
85              
86             =head2 acceptDoOption (option name/number or hash reference)
87              
88             Agree to do the specific option. If just an option name/number is given, just agrees to do this option whenever requested by the connected party. (Sets up an empty callback). Can also be passed a hashref as with the hash references passed to the new method, containing callbacks to be called.
89              
90             =head2 acceptWillOption (option name/number or hash reference)
91              
92             Agree that the connected party will do a specific option. If just an option name/number is given, just agrees that the other party will do this option wheneverrrequested. (Sets up an empty callback). Can also be passed a hash reference as above to run callbacks.
93              
94             =head2 activeDoOption (option name/number or hash reference)
95              
96             Actively ask the connected party to DO a specific option. If just passed an option name/number, will just ask and ignore the response. If passed a hash reference containing WILL/WONT callbacks, these will be run when the connected party answers the request.
97              
98             =head2 activeWillOption (option name/number or hash reference)
99              
100             Actively declare that we want to do a specific option. If just passes an option name/number, will just declare what we want to do, and ignore any response. If passed a hash reference containing DO/DONT callbacks, these will be run when the connected party answers the request.
101              
102             =head2 removeOption (option name/number)
103              
104             Remove the given option from the set of options we are answering.
105              
106             =head2 doActiveOptions (socket)
107              
108             Requests any options set as active, and not yet negotiated, of the connected party.
109              
110             =head2 getTelnetOptState (option name/number)
111              
112             Returns a hash reference containing any callbacks set for any particular command, plus 'STATUS_ME', 'STATUS_YOU' which can be any of NONE, ASKING, or WILL. STATUS_ME is the status of the local socket with regard to the option, and STATUS_ME is the status of the connected party.
113              
114             =head2 answerTelnetOpts (socket, data)
115              
116             This method must be called whenever data has been received from the connected party, before any other operations are done. It parses any telnet option information out of the given data, answering options as appropriate. The cleaned data is passed back. The socket is needed in order to send answers to any option requests.
117              
118             If a incomplete telnet option is found in the data, eg an IAC WILL and no option number, the data will be retained, and prepended to the data given in the next call, to be checked again. Thus you can also pass in data a byte at a time and still have the options parsed.
119              
120             =head2 sendOpt (socket, command, option name/number, suboptioncmd, suboption)
121              
122             Send a raw telnet option to the connected party. But only if it makes sense. Eg. if DOing NAWS has been negotiated, attempting a DO NAWS with sendOpt will refuse, on the grounds that we are already doing this. Useful for testing and turning off options by hand.
123              
124             =head1 CALLBACKS
125              
126             Methods are provided for adding callbacks to each part of the negotiation process. Callbacks for WILL, WONT, DO, DONT commands are passed out the passed in data string, with the option removed. This string may still have other following options in it, so should be treated with caution! The callback is also passed the position which the option was found in the string. It may return a new copy of the data string which will be used for further parsing. In most cases you will not need to look at or return the data string. Be sure to return undef as the last statement in your callback, if you are not changing the data, to not accidently return the value of your last statement.
127              
128             Sub option callbacks are slightly more complex. Suboptions generally have one of 2 different subcommands, either a SEND which means please send me some information, or IS, which defines the wanted information. The callback is passed either 'SEND' or 'IS' or 'SB' as the first parameter, if IS is passed, the 2nd parameter contains the information being passed, for SEND it is empty and for SB it contains the subcommand and the any information together. The 3rd parameter contains the data string, minus the actual telnet option commands, and the 4th parameter is the position the suboption was in the string.
129              
130             Currently it is assumed that the module user will remember which callback has been associated with which option. (Thus the callback does not get passed the option name/number for which it is fired)
131              
132             =head1 Telnet Options Explained
133              
134             Telnet options are simple but complex. The definition is simple, the implementation is complex as one has to avoid entering endless negotiation loops. The simple explanation should suffice here, as this module intends to hide the complex negotiating from you.
135              
136             Each Telnet connection consists of two parties, one on either side of the connection. When dealing with telnet options it is unimportant which side is the server and which is the client, important is just 'us' and 'them'. 'us' here is the side using this module, and 'them' is the party we are connected to.
137              
138             There are 4 basic telnet option commands, WILL, WONT, DO, DONT. If we receive a WILL command, it means the other side wishes to start using an option themselves. If we receive a DO command, the other side wants us to start using an option. These are two separate things. We can use an option that they are not using, and vice versa. WONT and DONT are used to refuse an option.
139              
140             Example:
141              
142             1a. We receive a DO TTYPE from them.
143             1b i. We wish to comply, so we send a WILL TTYPE.
144             1b ii. We do not wish to comply so we send a WONT TTYPE.
145              
146             2a. We would like to echo, so we send a WILL ECHO.
147             2b i. Thats ok with them, so they send a DO ECHO.
148             2b ii. They dont want us to use echo, so they send us a DONT ECHO.
149              
150             Once an option has been set in this manner, each side has to remember which options both ends are using. The default for all options is WONT/DONT.
151              
152             Some options have additional suboptions to set internal parameters. These are sent enclosed in SB and SE commands. (suboption, suboptionend). For example, settting the TTYPE option just indicates we are willing to answer 'what is your terminal type' questions, these are posed and answered using SB commands.
153              
154             =head1 DIAGNOSTICS
155              
156             (TODO)
157              
158             =head1 CONFIGURATION AND ENVIRONMENT
159              
160             Net:Telnet::Options requires no configuration files or environment variables.
161              
162             =head1 DEPENDENCIES
163              
164             None.
165              
166             =head1 INCOMPATIBILITIES
167            
168             None.
169              
170             =head1 BUGS AND LIMITATIONS
171            
172             No bugs have been reported.
173            
174             Please report any bugs or feature requests to
175             C@rt.cpan.org>, or through the web interface at
176             L.
177              
178             =head1 FURTHER INFORMATION
179              
180             A list of available options: http://www.networksorcery.com/enp/protocol/telnet.htm
181              
182             =head1 TODO
183              
184             Support multiple sockets per instance.
185             Output list of options/names.
186            
187             =head1 AUTHOR
188            
189             Jess Robinson C<< castaway@desert-island.m.isar.de >>
190            
191             =head1 LICENCE AND COPYRIGHT
192            
193             Copyright (c) 2004,2005, Jess Robinson C<< castaway@desert-island.m.isar.de >>. All rights reserved.
194              
195             This module is free software; you can redistribute it and/or
196             modify it under the same terms as Perl itself.
197            
198             =head1 DISCLAIMER OF WARRANTY
199            
200             BECAUSE THIS SOFTWARE IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY
201             FOR THE SOFTWARE, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN
202             OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES
203             PROVIDE THE SOFTWARE "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER
204             EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
205             WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE
206             ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE SOFTWARE IS WITH
207             YOU. SHOULD THE SOFTWARE PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL
208             NECESSARY SERVICING, REPAIR, OR CORRECTION.
209            
210             IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
211             WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR
212             REDISTRIBUTE THE SOFTWARE AS PERMITTED BY THE ABOVE LICENCE, BE
213             LIABLE TO YOU FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL,
214             OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE
215             THE SOFTWARE (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING
216             RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A
217             FAILURE OF THE SOFTWARE TO OPERATE WITH ANY OTHER SOFTWARE), EVEN IF
218             SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF
219             SUCH DAMAGES.
220              
221             =cut
222              
223             package Net::Telnet::Options;
224 2     2   49202 use Data::Dumper;
  2         19550  
  2         13313  
225              
226             my $DEBUG = 0;
227             our $VERSION = '0.01';
228              
229             my %possoptions = (
230             BINARY => 0, # Binary Transmission - RFC 856
231             ECHO => 1, # Echo - RFC 857
232             RCP => 2, # Reconnection -
233             SGA => 3, # Suppress Go Ahead - RFC 858
234             NAMS => 4, # Approx Message Size Negotiation -
235             STATUS => 5, # Status - RFC 859
236             TM => 6, # Timing Mark - 860
237             RCTE => 7, # Remote Con. Trans,Echo - RFC 563,726
238             NAOL => 8, # Output Line Width - NIC50005
239             NAOP => 9, # Output Page Size - NIC50005
240             NAOCRD => 10, # Out. CR Disposition - RFC 652
241             NAOHTS => 11, # Out. Horiz. Tab Stops - RFC 653
242             NAOHTD => 12, # Out. Horiz. Tab Dispo. - RFC 654
243             NAOFFD => 13, # Out. Formfeed Disposition - RFC 655
244             NAOVTS => 14, # Out. Vertical Tabstops - RFC 656
245             NAOVTD => 15, # Out. Vertical Tab Dispo. - RFC 657
246             NAOLFD => 16, # Out. Linefeed Disposition - RFC 658
247             XASCII => 17, # Extended ASCII - RFC 698
248             LOGOUT => 18, # Logout - RFC 727
249             BM => 19, # Byte Macro - RFC 735
250             DET => 20, # Data Entry Terminal - RFC 732,1043
251             SUPDUP => 21, # SUPDUP - RFC 734,736
252             SUPDUPOUTPUT => 22, # SUPDUP Output - RFC 749
253             SNDLOC => 23, # Send Location - RFC 779
254             TTYPE => 24, # Terminal Type - RFC 1091
255             EOR => 25, # End of Record - RFC 885
256             TUID => 26, # TACACS User Ident. - RFC 927
257             OUTMRK => 27, # Output Marking - RFC 933
258             TTYLOC => 28, # Terminal Location Number - RFC 946
259             '3270REGIME' => 29, # Telnet 3270 Regime - RFC 1041
260             X3PAD => 20, # X.3 PAD - RFC 1053
261             NAWS => 31, # Negotiate Ab. Win. Size - RFC 1073
262             TSPEED => 32, # Terminal Speed - RFC 1079
263             LFLOW => 33, # Remote Flow Control - RFC 1372
264             LINEMODE => 34, # Linemode - RFC 1184
265             XDISPLOC => 35, # X Display Location - RFC 1096
266             OLD_ENVIRON => 36, # Environment Option - RFC 1408
267             AUTHENTICATION => 37, # Auth.- RFC 1416,2941,2942,2943,2951
268             ENCRYPT => 38, # Encryption Option - RFC 2946
269             NEW_ENVIRON => 39, # New Environment Option - RFC 1572
270             TN3270E => 40, # TN3270 ? - RFC 2355
271             XAUTH => 41, # XAUTH -
272             CHARSET => 42, # Negotiate charset to use - RFC 2066
273             RSP => 43, # Telnet remote serial port - RFC
274             CPCO => 44, # Com port control option - RFC 2217
275             TSLE => 45, # Telnet suppress local echo -
276             STARTTLS => 46, # Telnet Start TLS -
277             KERMIT => 46, # Kermit ? - RFC 2840
278             SENDURL => 47, # Send URL -
279             FORWARDX => 48, # X forwarding
280             MCCP1 => 85, # Mud Compression Protocol (v1)
281             MCCP2 => 86, # Mud Compression Protocol (v2)
282             MSP => 90, # Mud Sound Protocol
283             MXP => 91, # Mud eXtension Protocol
284             PRAGMALOGON => 138, # Telnet option pragma logon
285             SSPILOGON => 139, # Telnet option SSPI login
286             PRAGMAHB => 140, # Telnet option pragma heartbeat
287             EXOPL => 255, # Extended-Options-List - RFC 861
288             );
289             my %optposs = reverse %possoptions;
290              
291             my %posscommands = (
292             GA => 249, # you may reverse the line
293             EL => 248, # erase the current line
294             EC => 247, # erase the current character
295             AYT => 246, # are you there
296             AO => 245, # abort output--but let prog finish
297             IP => 244, # interrupt process--permanently
298             BREAK => 243, # break
299             DM => 242, # data mark--for connect. cleaning
300             NOP => 241, # nop
301             SE => 240, # end sub negotiation
302             EOR => 239, # end of record (transparent mode)
303             ABORT => 238, # Abort process
304             SUSP => 237, # Suspend process
305             EOF => 236, # End of file
306             # SYNCH => 242, # Data mark in urgent mode
307             );
308             my %commposs = reverse %posscommands;
309              
310             my $chIAC = chr(255);
311             my $chDONT = chr(254);
312             my $chDO = chr(253);
313             my $chWONT = chr(252);
314             my $chWILL = chr(251);
315             my $chSB = chr(250);
316             my $chSE = chr(240);
317              
318             # Auth commands
319             my $chNAME = chr(3);
320             my $chREPLY = chr(2);
321             my $chSEND = chr(1);
322             my $chIS = chr(0);
323             # Auth types
324             my %authtypes = (
325             NULL => 0, # RFC 2941
326             KERBEROS_V4 => 1, # RFC 2941
327             KERBEROS_V5 => 2, # RFC 2942
328             SPX => 3, # RFC 2941
329             MINK => 4, # RFC 2941
330             SRP => 5, # RFC 2944
331             RSA => 6, # RFC 2941
332             SSL => 7, # RFC 2941
333             '' => 8, # Unassigned
334             '' => 9, # Unassigned
335             LOKI => 10, # RFC 2941
336             SSA => 11, # Schoch ?
337             KEA_SJ => 12, # RFC 2951
338             KEA_SJ_INTEG=> 13, # RFC 2951
339             DSS => 14, # RFC 2943
340             NTLM => 15, # Kahn ?
341             );
342              
343             my $OPT_LINEMODE_MODE = 1;
344             my $OPT_LINEMODE_MODE_EDIT = 1;
345              
346             # The 4 telnet negotiation types, Subnegotiation, and Raw (call sub whenever
347             # option encountered, ie EOR)
348             my @opttypes = ('WILL', 'WONT', 'DO', 'DONT', 'SB', 'RAW');
349              
350             sub new
351             {
352             # Create new Net::Telnet::Options
353             # Parameter: Class-Name/Reference, Properties
354 4     4 1 1476 my $class = shift;
355 4   33     31 $class = ref($class) || $class;
356 4         8 my $self = {};
357 4         12 bless($self, $class);
358 4         20 $self->initModule(@_);
359 4         11 return $self;
360             }
361              
362             sub initModule
363             {
364             # Set default values
365             # Parameter: Object, Option list (eg: NAWS => {WILL => coderef, WONT => coderef .. }
366 4     4 0 11 my ($mod, %opts) = @_;
367              
368 4         193 $mod->{'telnetopts'} = {};
369              
370 4         22 foreach $opt (keys %opts)
371             {
372 2         9 $mod->addOption(0, $opt => $opts{$opt});
373             }
374              
375 4         11 $mod->{'lastcommand'} = '';
376 4         14 $mod->{'olddata'} = '';
377             }
378              
379             sub addOption
380             {
381             # Add a new set of option responses
382             # Parameter: Object, Option hash
383 4     4 1 14 my ($mod, $active, %opts) = @_;
384              
385 4         12 my $opt = $orgopt = (keys(%opts))[0];
386 4 50 66     30 if (defined($possoptions{uc($opt)}) || defined($opt = $optposs{$opt}))
387             {
388 4         10 foreach my $opttype (@opttypes)
389             {
390 24 100       74 if (exists($opts{$orgopt}->{$opttype}))
391             {
392 4         18 $mod->{'telnetopts'}{uc($opt)}{$opttype} = $opts{$orgopt}->{$opttype};
393             }
394             }
395 4         13 $mod->{'telnetopts'}{uc($opt)}{'STATUS_ME'} = 'NONE';
396 4         15 $mod->{'telnetopts'}{uc($opt)}{'STATUS_YOU'} = 'NONE';
397 4 100       25 $mod->{'telnetopts'}{uc($opt)}{'ACTIVE'} = 1 if($active);
398             }
399             # debug("addOption: ". Dumper($mod->{'telnetopts'}). "\n");
400             }
401              
402             sub acceptDoOption
403             {
404             # I'll DO option, when the other side requests it
405             # Add a new option that defaults to 'DO' with no callback
406             # Parameter: Object, Active, Option hash
407 1     1 1 11 my ($mod, @opts) = @_;
408 1         2 my %opts;
409 1 50       8 %opts = (@opts == 1 ? (@opts, 1) : @opts);
410              
411 1         5 my $opt = (keys(%opts))[0];
412 1 50 33     10 if (defined($possoptions{uc($opt)}) || defined($opt = $optposs{$opt}))
413             {
414 1         6 $mod->{'telnetopts'}{uc $opt}{'DO'} = \&empty_callback;
415 1         4 $mod->{'telnetopts'}{uc $opt}{'DONT'} = \&empty_callback;
416             }
417             # debug("acceptDoOption: ". Dumper($mod->{'telnetopts'}). "\n");
418 1         7 $mod->addOption(0, %opts);
419             }
420              
421             sub acceptWillOption
422             {
423             # I'll accept WILL option, when the other side wants to do it
424             # Add a new option that defaults to 'DO' with no callback
425             # Parameter: Object, Active, Option hash
426 0     0 1 0 my ($mod, @opts) = @_;
427 0         0 my %opts;
428 0 0       0 %opts = (@opts == 1 ? (@opts, 1) : @opts);
429              
430 0         0 my $opt = (keys(%opts))[0];
431 0 0 0     0 if (defined($possoptions{uc($opt)}) || defined($opt = $optposs{$opt}))
432             {
433 0         0 $mod->{'telnetopts'}{uc $opt}{'WILL'} = \&empty_callback;
434 0         0 $mod->{'telnetopts'}{uc $opt}{'WONT'} = \&empty_callback;
435             }
436 0         0 debug("acceptWillOption: ". Dumper($mod->{'telnetopts'}). "\n");
437 0         0 $mod->addOption(0, %opts);
438             }
439              
440             sub activeDoOption
441             {
442             # I'm asking the other side to DO option
443             # Add a new option that defaults to 'DO' with no callback
444             # Parameter: Object, Active, Option hash
445 1     1 1 18 my ($mod, @opts) = @_;
446 1         3 my %opts;
447 1 50       5 %opts = (@opts == 1 ? (@opts, 1) : @opts);
448              
449 1         15 my $opt = (keys(%opts))[0];
450 1 50 33     9 if (defined($possoptions{uc($opt)}) || defined($opt = $optposs{$opt}))
451             {
452 1         10 $mod->{'telnetopts'}{uc $opt}{'WILL'} = \&empty_callback;
453 1         3 $mod->{'telnetopts'}{uc $opt}{'WONT'} = \&empty_callback;
454 1         5 $mod->{'telnetopts'}{uc $opt}{'ACTIVE'} = 1;
455             }
456             # debug("activeDoOption: ". Dumper($mod->{'telnetopts'}). "\n");
457 1         5 $mod->addOption(1, %opts);
458             }
459              
460             sub activeWillOption
461             {
462             # I WILL do option when requested
463             # Add a new option that defaults to 'DO' with no callback
464             # Parameter: Object, Active, Option hash
465 0     0 1 0 my ($mod, @opts) = @_;
466 0         0 my %opts;
467 0 0       0 %opts = (@opts == 1 ? (@opts, 1) : @opts);
468              
469 0         0 my $opt = (keys(%opts))[0];
470 0 0 0     0 if (defined($possoptions{uc($opt)}) || defined($opt = $optposs{$opt}))
471             {
472 0         0 $mod->{'telnetopts'}{uc $opt}{'DO'} = \&empty_callback;
473 0         0 $mod->{'telnetopts'}{uc $opt}{'DONT'} = \&empty_callback;
474 0         0 $mod->{'telnetopts'}{uc $opt}{'ACTIVE'} = 1;
475             }
476 0         0 debug("activeWillOption: ". Dumper($mod->{'telnetopts'}). "\n");
477 0         0 $mod->addOption(1, %opts);
478             }
479              
480             sub removeOption
481             {
482             # Remove a set of option responses
483             # Parameter: Object, Option
484 1     1 1 2 my ($mod, $opt) = @_;
485              
486 1 50 33     15 if(defined $possoptions{uc($opt)} || defined($opt = $optposs{$opt}))
487             {
488 1 50       6 if(exists($mod->{'telnetopts'}{uc $opt}))
489             {
490 1         4 delete $mod->{'telnetopts'}{uc $opt};
491 1         3 return 1;
492             }
493             }
494             }
495              
496             sub doActiveOptions
497             {
498             # Go through the options that are set as active, and not yet negotiated.
499             # If passed a socket, send to socket, else option_callback..
500             # Parameters: Object, Socket?
501 1     1 1 455 my ($mod, $sock) = @_;
502              
503 1         3 foreach my $opt (keys %{$mod->{'telnetopts'}})
  1         4  
504             {
505 1 50       5 if($mod->{'telnetopts'}{$opt}{'ACTIVE'})
506             {
507 1 50       5 if($mod->{'telnetopts'}{$opt}{'WILL'})
    0          
508             {
509 1         3 $mod->sendOpt($sock, 'DO', $opt);
510             }
511             elsif($mod->{'telnetopts'}{$opt}{'DO'})
512             {
513 0         0 $mod->sendOpt($sock, 'WILL', $opt);
514             }
515             }
516             }
517             }
518              
519             sub getTelnetOptState
520             {
521             # Return the current state of an option by name/number
522             # If not available, assume '0' = dont know this option
523             # Parameter: Object, Option
524 1     1 1 448 my ($mod, $opt) = @_;
525 1 50       6 $opt = $optposs{$opt} if(!$possoptions{uc $opt});
526 1 50       3 return if(!$opt);
527              
528 1 50       6 if (exists($mod->{'telnetopts'}{uc($opt)}))
529             {
530 1         16 return $mod->{'telnetopts'}{uc($opt)};
531             }
532 0         0 return 'NONE';
533             }
534              
535             # Callback for answers to options? (ie dont pass in socket)
536              
537             sub answerTelnetOpts
538             {
539             # Answer all telnetoptions and remove from the given stream,
540             # according to settings.
541             # Object, Socket (for answers), Data
542 8     8 1 7413 my ($mod, $sock, $data) = @_;
543 8         11 my $pos = -1;
544 8         11 my $option;
545              
546             {
547 8         11 $data = $mod->{'lastcommand'} . $data;
  8         21  
548 8         37 $mod->{'lastcommand'} = '';
549             }
550              
551 8         30 while (($pos = index($data, $chIAC, $pos)) > -1)
552             {
553             # debug("Found IAC\n");
554 7         19 my $nextchar = substr($data, $pos + 1, 1);
555 7 100       21 if (!length($nextchar))
556             {
557 1         3 $mod->{'lastcommand'} = $chIAC;
558 1         4 chop($data);
559 1         2 last;
560             }
561 6 100 66     54 if ($nextchar eq $chIAC)
    100 66        
    50 100        
562             {
563 2         4 substr($data, $pos, 1) = '';
564 2         7 $pos++;
565             }
566             elsif ($nextchar eq $chDONT or $nextchar eq $chDO or
567             $nextchar eq $chWONT or $nextchar eq $chWILL)
568             {
569 2         5 $option = substr($data, $pos + 2, 1);
570 2 50       6 if (!length($option))
571             {
572 0         0 $mod->{'lastcommand'} .= $chIAC . $nextchar;
573 0         0 chop($data);
574 0         0 chop($data);
575 0         0 last;
576             }
577 2         4 substr($data, $pos, 3) = '';
578              
579 2         9 $data = $mod->negotiate_option($sock, $data, $nextchar, ord($option), $pos);
580             }
581             elsif ($nextchar eq $chSB)
582             {
583 2         4 my $endpos = index($data, $chSE, $pos);
584 2 50       7 if ($endpos == -1)
585             {
586 0         0 $mod->{'lastcommand'} .= substr($data, $pos);
587 0         0 substr($data, $pos) = '';
588 0         0 last;
589             }
590 2         5 my $subcmd = substr($data, $pos + 2, $endpos - $pos + 1);
591 2         5 substr($data, $pos, $endpos - $pos + 1) = '';
592              
593 2         8 $data = $mod->negotiate_suboption($sock, $data, $nextchar, $subcmd, $pos);
594             }
595             # elsif ($nextchar eq $chEOR)
596             # {
597             # # extract prompt from datastring
598             # # debug("Extracting prompt..\n");
599             # $testdata = $mod->{'olddata'} . $data;
600             # substr($data, $pos, 2) = '';
601             # }
602             else # Unknown option, delete
603             {
604 0         0 debug("Command: " . $commposs{ord($nextchar)} . "\n");
605 0         0 substr($data, $pos, 2) = '';
606 0 0 0     0 if($commposs{ord($nextchar)} &&
      0        
607             $mod->{'telnetopts'}{$commposs{ord($nextchar)}} &&
608             (my $coderef = $mod->{'telnetopts'}{$commposs{ord($nextchar)}}{'RAW'}))
609             {
610 0         0 $coderef->($mod->{'olddata'} . $data, $pos+
611             length($mod->{'olddata'}));
612             }
613             }
614             }
615             # Add previous data line because of EORs/prompts
616 8         15 $mod->{'olddata'} = $data;
617              
618 8         22 return $data;
619             }
620              
621             sub negotiate_option
622             {
623             # Given a telnet option found, do the actual answering etc.
624             # Parameter: Object, Socket, Data stream, Option Type, Option #, Position
625 2     2 0 4 my ($mod, $socket, $data, $opt_req, $opt, $optpos) = @_;
626 2         12 debug("Mud sent option request:" . ord($opt_req) . ":" . $opt . "\n");
627 2 100       8 if ($opt_req eq $chDO)
    50          
    0          
    0          
628             {
629 1         5 debug("Do " . $optposs{$opt} . "\n");
630 1 50 33     10 if (exists($mod->{'telnetopts'}{$optposs{$opt}}) &&
631             $mod->{'telnetopts'}{$optposs{$opt}}{'DO'})
632             {
633 1 50       4 if ($mod->{'telnetopts'}{$optposs{$opt}}{'STATUS_ME'} ne 'ASKING')
634             {
635 1         7 print $socket $chIAC . $chWILL . chr($opt);
636             }
637 1         3 $mod->{'telnetopts'}{$optposs{$opt}}{'STATUS_ME'} = 'WILL';
638 1         5 my $res = $mod->{'telnetopts'}{$optposs{$opt}}{'DO'}->
639             ($data, $optpos);
640 1 50       4 $data = $res if(defined($res));
641             }
642             else
643             {
644 0         0 print $socket $chIAC . $chWONT . chr($opt);
645             }
646             }
647             elsif ($opt_req eq $chWILL)
648             {
649 1         7 debug("Will ". $optposs{$opt} . "\n");
650 1 50 33     10 if (exists($mod->{'telnetopts'}{$optposs{$opt}}) &&
651             $mod->{'telnetopts'}{$optposs{$opt}}{'WILL'})
652             {
653 1 50       15 if ($mod->{'telnetopts'}{$optposs{$opt}}{'STATUS_YOU'} ne 'ASKING')
654             {
655 0         0 print $socket $chIAC . $chDO . chr($opt);
656             }
657 1         4 $mod->{'telnetopts'}{$optposs{$opt}}{'STATUS_YOU'} = 'WILL';
658 1         13 my $res = $mod->{'telnetopts'}{$optposs{$opt}}{'WILL'}->
659             ($data, $optpos);
660 1 50       5 $data = $res if(defined($res));
661             }
662             else
663             {
664 0         0 print $socket $chIAC . $chDONT . chr($opt);
665             }
666             }
667             elsif ($opt_req eq $chWONT)
668             {
669 0         0 debug("Wont " . $optposs{$opt} . "\n");
670 0 0 0     0 if (exists($mod->{'telnetopts'}{$optposs{$opt}}) &&
671             $mod->{'telnetopts'}{$optposs{$opt}}{'WONT'})
672             {
673 0 0       0 if ($mod->{'telnetopts'}{$optposs{$opt}}{'STATUS_YOU'} eq 'ASKING')
674             {
675 0         0 $mod->{'telnetopts'}{$optposs{$opt}}{'STATUS_YOU'} = 'NONE';
676 0         0 print $socket $chIAC . $chDONT . chr($opt);
677             }
678 0         0 my $res = $mod->{'telnetopts'}{$optposs{$opt}}{'WONT'}->
679             ($data, $optpos);
680 0 0       0 $data = $res if(defined($res));
681             }
682             else
683             {
684 0         0 print $socket $chIAC . $chDONT . chr($opt);
685             }
686             }
687             elsif ($opt_req eq $chDONT)
688             {
689 0         0 debug("Wont ". $optposs{$opt} . "\n");
690 0 0 0     0 if (exists($mod->{'telnetopts'}{$optposs{$opt}}) &&
691             $mod->{'telnetopts'}{$optposs{$opt}}{'DONT'})
692             {
693 0 0       0 if ($mod->{'telnetopts'}{$optposs{$opt}}{'STATUS_ME'} eq 'ASKING')
694             {
695 0         0 $mod->{'telnetopts'}{$optposs{$opt}}{'STATUS_ME'} = 'NONE';
696 0         0 print $socket $chIAC . $chWONT . chr($opt);
697             }
698 0         0 my $res = $mod->{'telnetopts'}{$optposs{$opt}}{'DONT'}->
699             ($data, $optpos);
700 0 0       0 $data = $res if(defined($res));
701             }
702             else
703             {
704 0         0 print $socket $chIAC . $chWONT . chr($opt);
705             }
706             }
707 2         10 return $data;
708             }
709              
710             sub negotiate_suboption
711             {
712 2     2 0 4 my ($mod, $socket, $data, $opt_req, $cmd, $optpos) = @_;
713 2         5 my $option = substr($cmd, 0, 1);
714             # IAC, SB, TTYPE, SEND, IAC, SE
715 2         12 debug("Got suboption request:" . ord($opt_req) . " :" .
716             ord($option). "\n");
717              
718 2         9 debug("Option: ". $optposs{ord($option)}."\n");
719 2         26 debug(Dumper($mod->{'telnetopts'}{$optposs{ord($option)}})."\n");
720              
721 2 50       28 return $data unless(exists($mod->{'telnetopts'}{$optposs{ord($option)}}));
722              
723 2 50 66     17 if($mod->{'telnetopts'}{$optposs{ord($option)}}{'STATUS_ME'} eq 'WILL' ||
724             $mod->{'telnetopts'}{$optposs{ord($option)}}{'STATUS_YOU'} eq 'WILL')
725             {
726 2         7 my $coderef = $mod->{'telnetopts'}{$optposs{ord($option)}}{'SB'};
727 2 50       5 return $data unless($coderef);
728 2 100       11 if (substr($cmd, 1, 1) eq $chSEND)
    50          
729             {
730 1         4 my $res = $coderef->('SEND', '', $data, $optpos);
731 1 50       6 $data = $res if(defined($res));
732             }
733             elsif(substr($cmd, 1, 1) eq $chIS)
734             {
735 1         8 my $res = $coderef->('IS', substr($cmd, 1), $data, $optpos);
736 1 50       1171 $data = $res if(defined($res));
737             }
738             else
739             {
740 0         0 my $res = $coderef->('SB', $cmd, $data, $pos);
741 0 0       0 $data = $res if(defined($res));
742             }
743             }
744              
745 2         12 return $data;
746             }
747              
748             sub sendOpt
749             {
750             # Request a telnet option direct
751             # Parameter: Object, Socket, DO/WILL/DONT/WONT/SB, Option number, suboption
752 2     2 1 428 my ($mod, $socket, $req, $opt, $subopt1, $subopt2) = @_;
753 2   100     18 $subopt1 ||='';
754 2   100     8 $subopt2 ||='';
755 2         11 debug("SendOpt: $req, $opt, $subopt1, $subopt2\n");
756              
757 2 50 66     23 if (defined($possoptions{uc($opt)}) || ($opt = $optposs{$opt}))
758             # $opt = $optposs{$opt} if(!defined($possoptions{$opt}));
759             # return if(!$opt);
760             {
761 2 50       6 if ($req eq 'WILL')
762             {
763 0 0 0     0 if ($mod->{'telnetopts'}{$opt}{'STATUS_ME'} eq 'ASKING' ||
764             $mod->{'telnetopts'}{$opt}{'STATUS_ME'} eq 'WILL')
765             {
766 0         0 carp("Already asking $opt.\n");
767 0         0 return;
768             }
769 0         0 print $socket $chIAC . $chWILL . chr($possoptions{$opt});
770 0         0 $mod->{'telnetopts'}{$opt}{'STATUS_ME'} = 'ASKING';
771             }
772 2 50       6 if ($req eq 'WONT')
773             {
774 0 0 0     0 if ($mod->{'telnetopts'}{$opt}{'STATUS_ME'} ne 'WILL' &&
775             $mod->{'telnetopts'}{$opt}{'STATUS_ME'} ne 'ASKING')
776             {
777 0         0 carp("We're not wanting that anyway!\n");
778 0         0 return;
779             }
780 0         0 print $socket $chIAC . $chWONT . chr($possoptions{$opt});
781 0         0 $mod->{'telnetopts'}{$opt}{'STATUS_ME'} = 'NONE';
782             }
783 2 100       6 if ($req eq 'DO')
784             {
785 1 50 33     20 if ($mod->{'telnetopts'}{$opt}{'STATUS_YOU'} eq 'WILL' ||
786             $mod->{'telnetopts'}{$opt}{'STATUS_YOU'} eq 'ASKING')
787             {
788 0         0 carp("Already wanting $opt.\n");
789 0         0 return;
790             }
791 1         6 print $socket $chIAC . $chDO . chr($possoptions{$opt});
792 1         4 $mod->{'telnetopts'}{$opt}{'STATUS_YOU'} = 'ASKING';
793             }
794 2 50       6 if ($req eq 'DONT')
795             {
796 0 0 0     0 if ($mod->{'telnetopts'}{$opt}{'STATUS_YOU'} ne 'WILL' &&
797             $mod->{'telnetopts'}{$opt}{'STATUS_YOU'} ne 'ASKING')
798             {
799 0         0 carp("We're not wanting that anyway!\n");
800 0         0 return;
801             }
802 0         0 print $socket $chIAC . $chDONT . chr($possoptions{$opt});
803 0         0 $mod->{'telnetopts'}{$opt}{'STATUS_YOU'} = 'ASKING';
804             }
805 2 100       10 if ($req eq 'SB')
806             {
807 1         7 debug("Sendopt: $opt, Status: $mod->{telnetopts}{$opt}\n");
808 1 50 33     13 if($mod->{'telnetopts'}{$opt}{'STATUS_YOU'} eq 'WILL' &&
    50 33        
    0 0        
      0        
809             $subopt1 eq 'SEND')
810             {
811 0         0 print $socket $chIAC . $chSB . chr($possoptions{$opt}) .
812             $chSEND . $subopt2 . $chIAC . $chSE;
813             }
814             elsif($mod->{'telnetopts'}{$opt}{'STATUS_ME'} eq 'WILL' &&
815             $subopt1 eq 'IS')
816             {
817 1         9 print $socket $chIAC . $chSB . chr($possoptions{$opt}) .
818             $chIS . $subopt2 . $chIAC . $chSE;
819             }
820             elsif($mod->{'telnetopts'}{$opt} &&
821             ($mod->{'telnetopts'}{$opt}{'STATUS_YOU'} eq 'WILL' ||
822             $mod->{'telnetopts'}{$opt}{'STATUS_ME'} eq 'WILL'
823             ))
824             {
825 0         0 print $socket $chIAC . $chSB . chr($possoptions{$opt}) .
826             $subopt2 . $chIAC . $chSE;
827             }
828             else
829             {
830 0         0 carp("Option $opt not turned on!\n");
831             }
832             }
833             }
834             }
835              
836              
837             sub empty_callback
838 2     2 0 2 {
839             }
840              
841             sub debug
842             {
843             # ::main::debug(@_);
844              
845 13 50   13 0 254 print @_, "\n" if($DEBUG);
846             }
847              
848             1;
849              
850             # Deafult 'NONE' means 'WONT' ?
851             # POD:
852             # Calls WILL, DO, WONT, DONT, RAW coderefs with $data and $pos
853             # Calls SB coderef with SEND/IS/SB, Rest, $data, $pos
854             #
855             # Active doing/wanting of options..
856              
857             #
858             # Us: DO X -- X_YOU = 'ASKING'
859             # Them: WONT X
860             # If X_YOU eq 'ASKING' -> 'NONE', confirm change of plan with 'DONT X'
861             # Else agree 'DONT X'
862             #
863             # Them: WILL X
864             # if X_YOU eq 'ASKING' -> 'DO', no reply
865             # Else if 'WILL X' set, answer 'DO X' 'DO' (we havent requested, but would like)
866             # Else refuse 'DONT X'
867             #
868             # Us: WILL X -- X_ME = 'ASKING'
869             # Them: DONT X
870             # If X_ME eq 'ASKING' -> 'NONE', confirm change of plan with 'WONT X'
871             # Else agree WONT X
872             #
873             # Them: DO X
874             # If X_ME eq 'ASKING' -> 'WILL', no reply
875             # Else if 'DO X' set, answer 'WILL X' (will do) -> 'WILL'
876             # Else refuse 'WONT X'
877              
878              
879              
880