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
|
|
|
|
|
|
|
|