-
Notifications
You must be signed in to change notification settings - Fork 70
/
README.TELNET
590 lines (411 loc) · 21.7 KB
/
README.TELNET
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
===============================================================================
libtelnet - TELNET protocol handling library
===============================================================================
http://github.com/seanmiddleditch/libtelnet
PUBLIC DOMAIN
The author or authors of this code dedicate any and all copyright
interest in this code to the public domain. We make this dedication
for the benefit of the public at large and to the detriment of our
heirs and successors. We intend this dedication to be an overt act of
relinquishment in perpetuity of all present and future rights to this
code under copyright law.
Sean Middleditch
sean@sourcemud.org
"Fish" (David B. Trout)
fish@softdevlabs.com
I. INTRODUCTION
===============================================================================
libtelnet provides safe and correct handling of the core TELNET protocol. In
addition to the base TELNET protocol, libtelnet also implements the Q method of
TELNET option negotiation. libtelnet can be used for writing servers, clients,
or proxies.
For information on the TELNET standards supported by libtelnet, visit the
following websites:
http://www.rfc-editor.org/info/rfc1091
http://www.rfc-editor.org/info/rfc1143
http://www.rfc-editor.org/info/rfc1408
http://www.rfc-editor.org/info/rfc1571
http://www.rfc-editor.org/info/rfc1572
http://www.rfc-editor.org/info/rfc854
http://www.rfc-editor.org/info/rfc855
For information on the TELNET TN3270 standard visit the following websites:
http://www.rfc-editor.org/info/rfc1576
http://www.rfc-editor.org/info/rfc2355
http://www.rfc-editor.org/info/rfc856
http://www.rfc-editor.org/info/rfc885
II. LIBTELNET API
===============================================================================
The libtelnet API contains several distinct parts. The first part is the basic
initialization and deinitialization routines. The second part is a single
function for pushing received data into the telnet processor. The third part
is the libtelnet output functions, which generate TELNET commands and ensure
data is properly formatted before sending over the wire. The final part is the
event handler interface.
This document covers only the most basic functions. See the libtelnet manual
pages or HTML documentation for a complete reference.
IIa. Initialization
-------------------------------------------------------------------------------
Using libtelnet requires the initialization of a telnet_t structure which
stores all current state for a single TELNET connection.
Initializing a telnet_t structure requires several pieces of data. One of
these is the telopt support table, which specifies which TELNET options your
application supports both locally and remotely. This table is comprised of
telnet_telopt_t structures, one for each supported option. Each entry
specifies the option supported, whether the option is supported locally or
remotely:
struct telnet_telopt_t
{
short telopt;
unsigned char us;
unsigned char them;
};
The us field denotes whether your application supports the telopt locally.
It should be set to TELNET_WILL if you support it and to TELNET_WONT if you
don't. The them field denotes whether the telopt is supported on the remote
end, and should be TELNET_DO if yes and TELNET_DONT if not.
When definition the telopt table you must include an end marker entry, which
is simply an entry with telopt set to -1. For example:
static const telnet_telopt_t my_telopts[] =
{
{ TELNET_TELOPT_ECHO, TELNET_WILL, TELNET_DONT },
{ TELNET_TELOPT_TTYPE, TELNET_WILL, TELNET_DONT },
{ TELNET_TELOPT_BINARY, TELNET_WILL, TELNET_DO },
{ TELNET_TELOPT_NAWS, TELNET_WILL, TELNET_DONT },
{ -1, 0, 0 }
};
If you need to dynamically alter supported options on a per-connection basis
then you may use a different table (dynamically allocated if necessary) per
call to telnet_init() or you share a single constant table like the above
example between all connections if you support a fixed set of options. Most
applications will support only a fixed set of options.
telnet_t* telnet_init
(
const telnet_telopts_t *telopts,
telnet_event_handler_t handler,
unsigned char flags,
void *user_data
);
The telnet_init() function is responsible for allocating memory and
initializing the data in a telnet_t structure. It must be called
immediately after establishing a connection and before any other libtelnet
API calls are made.
The telopts field is the telopt support table as described above.
The handler parameter must be a function matching the telnet_event_handler_t
definition. More information about events can be found in section IId.
The user_data parameter is passed to the event handler whenver it is
invoked. This will usually be a structure container information about the
connection, including a socket descriptor for implementing TELNET_EV_SEND
event handling.
The flags parameter can be any of the following flag constants bit-or'd
together, or 0 to leave all options disabled.
TELNET_FLAG_PROXY_MODE
Operate in proxy mode. This disables the RFC1143 support.
If telnet_init() fails to allocate the required memory the returned pointer
will be zero.
void telnet_free( telnet_t *telnet );
Releases any internal memory allocated by libtelnet for the given telnet
pointer. This must be called whenever a connection is closed, or you will
incur memory leaks. The pointer passed in may no longer be used afterwards.
IIb. Receiving Data
-------------------------------------------------------------------------------
void telnet_recv
(
telnet_t *telnet,
const char *buffer,
unsigned int size,
void *user_data
);
When your application receives data over the socket from the remote end,
it must pass the received bytes into this function.
As the TELNET stream is parsed, events will be generated and passed to the
event handler given to telnet_init(). Of particular interest for data
receiving is the TELNET_EV_DATA event, which is triggered for any regular
data such as user input or server process output.
IIc. Sending Data
-------------------------------------------------------------------------------
All of the output functions will invoke the TELNET_EV_SEND event.
Note: it is very important that ALL data sent to the remote end of the
connection be passed through libtelnet. All user input or process output that
you wish to send over the wire should be given to one of the following
functions. Do NOT send or buffer unprocessed output data directly!
void telnet_iac( telnet_t *telnet, unsigned char cmd );
Sends a single "simple" TELNET command, such as the GO-AHEAD commands (255
249).
void telnet_negotiate( telnet_t *telnet, unsigned char cmd,
unsigned char opt );
Sends a TELNET negotiation command. The cmd parameter must be one of
TELNET_WILL, TELNET_WONT, TELNET_DO, or TELNET_DONT. The opt parameter
is the option to negotiate.
Unless in PROXY mode, the RFC1143 support may delay or ellide the request
entirely, as appropriate. It will ignore duplicate invocations, such as
asking for WILL NAWS when NAWS is already on or is currently awaiting
response from the remote end.
void telnet_send( telnet_t *telnet, const char *buffer, unsigned int size );
Sends raw data, which would be either the process output from a server or
the user input from a client.
For sending regular text is may be more convenient to use telnet_printf().
void telnet_ttype_send( telnet_t* telnet);
Begins terminal type sub-negotiation, effectively sending a request to your
peer asking them to respond back to you with their terminal type. Their
response will be received by you as a TELNET_EV_TTYPE event (see "Event
Handling" further below).
void telnet_begin_sb( telnet_t *telnet, unsigned char telopt );
Sends the header for a TELNET sub-negotiation command for the specified
option. All send data following this command will be part of the sub-
negotiation data until a call is made to telnet_finish_subnegotiation().
You should not use telnet_printf() for sending sub-negotiation data as it
will perform newline translations that usually do not need to be done for
sub-negotiation data, and may cause problems.
void telnet_finish_sb( telnet_t *telnet );
Sends the end marker for a TELNET sub-negotiation command. This must be
called after (and only after) a call has been made to
telnet_begin_subnegotiation() and any negotiation data has been sent.
void telnet_subnegotiation( telnet_t *telnet, unsigned char telopt,
const char *buffer, unsigned int size );
Sends a TELNET sub-negotiation command. The telopt parameter is the sub-
negotiation option.
Note that this function is just a shorthand for:
telnet_begin_sb (telnet, telopt);
telnet_send (telnet, buffer, size);
telnet_finish_sb (telnet);
For some sub-negotiations that involve a lot of complex formatted data to
be sent, it may be easier to make calls to both telnet_begin_sb() and
telnet_finish_sb() and using telnet_send() or telnet_printf2() to format
the data.
int telnet_printf( telnet_t *telnet, const char *fmt, ... );
This functions very similarly to fprintf, except that output is sent through
libtelnet for processing. IAC bytes are properly escaped, C newlines (\n)
are translated into CR LF, and C carriage returns (\r) are translated into
CR NULL, all as required by RFC854. The return code is the length of the
formatted text.
NOTE: due to an internal implementation detail, the maximum lenth of the
formatted text is 1024 characters.
IId. Event Handling
-------------------------------------------------------------------------------
libtelnet relies on an event-handling mechanism for processing the parsed
TELNET protocol stream as well as for buffering and sending output data.
When you initialize a telnet_t structure with telnet_init() you had to pass in
an event handler function. This function must meet the following prototype:
void ( telnet_t *telnet, telnet_event_t *event, void *user_data );
Except for the TELNET_EV_SEND (which must always be implemented; see below),
all events are data reception events. That is to say, each of the defined
events means data of the specified type was received from the other side.
The event handler will never be called in response to your program sending
a telnet command to the other side. It will ony be called when it receives
a response.
The event structure is detailed below. The user_data value is the pointer
passed to telnet_init().
The following is a summary of the most important parts of the telnet_event_t
data type. Please see the libtelnet manual pages HTML document for a complete
reference.
union telnet_event_t {
enum telnet_event_type_t type;
struct data_t {
enum telnet_event_type_t _type;
const char *buffer;
unsigned int size;
} data;
struct error_t {
enum telnet_event_type_t _type;
const char *file;
const char *func;
const char *msg;
int line;
telnet_error_t errcode;
} error;
struct iac_t {
enum telnet_event_type_t _type;
unsigned char cmd;
} iac;
struct negotiate_t {
enum telnet_event_type_t _type;
unsigned char telopt;
} neg;
struct subnegotiate_t {
enum telnet_event_type_t _type;
const char *buffer;
unsigned int size;
unsigned char telopt;
} sub;
};
The enumeration values of telnet_event_type_t are described in detail below.
Whenever the the event handler is invoked, the application must look at the
event->type value and do any necessary processing.
The only event that MUST be implemented is TELNET_EV_SEND. Most applications
will also always want to implement the event TELNET_EV_DATA.
Here is an example event handler implementation which includes handlers for
several important events.
void my_event_handler( telnet_t *telnet, telnet_event_t *ev,
void *user_data )
{
user_info* user = (user_info*) user_data;
switch (ev->type)
{
case TELNET_EV_DATA:
process_user_input( user, event->data.buffer, event->data.size );
break;
case TELNET_EV_SEND:
write_to_descriptor( user, event->data.buffer, event->data.size );
break;
case TELNET_EV_ERROR:
fatal_error( "TELNET error: %s", event->error.msg );
break;
}
}
TELNET_EV_DATA:
The DATA event is triggered whenever regular data (not part of any special
TELNET command) is received. For a client, this will be process output
from the server. For a server, this will be input typed by the user.
The event->data.buffer value will contain the bytes received and the
event->data.size value will contain the number of bytes received. Note that
event->data.buffer is not NULL terminated!
NOTE: there is no guarantee that user input or server output will be
received in whole lines. If you wish to process data a line at a time, you
are responsible for buffering the data and checking for line terminators
yourself!
TELNET_EV_SEND:
This event is sent whenever libtelnet has generated data that must be sent
over the wire to the remove end. Generally that means calling send() or
adding the data to your application's output buffer.
The event->data.buffer value will contain the bytes to send and the
event->data.size value will contain the number of bytes to send. Note that
event->data.buffer is not NULL terminated, and may include NULL characters
in its data, so always use event->data.size!
NOTE: Your SEND event handler must send or buffer the data in its raw form
as provided by libtelnet. If you wish to perform any kind of preprocessing
on data you want to send to the other side you must do it before calling
the telnet_send function and never during a TELNET_EV_SEND event.
TELNET_EV_IAC:
The IAC event is triggered whenever a simple IAC command is received, such
as the IAC EOR (end of record, also called go ahead or GA) command.
The command received is in the event->iac.cmd value.
The necessary processing depends on the specific commands; see the TELNET
RFC for more information.
TELNET_EV_WILL:
TELNET_EV_DO:
The WILL and DO events are sent when a TELNET negotiation command of the
same name is received.
WILL events are sent by the remote end when they wish to be allowed to turn
an option on on their end, or in confirmation after you have sent a DO
command to them.
DO events are sent by the remote end when they wish for you to turn on an
option on your end, or in confirmation after you have sent a WILL command
to them.
In either case, the TELNET option under negotiation will be in the
event->neg.telopt field.
libtelnet manages most of the pecularities of negotiation for you. For
information on libtelnet's negotiation method, see:
http://www.rfc-editor.org/info/rfc1143
Note that in PROXY mode libtelnet will do no processing of its own for you.
TELNET_EV_WONT:
TELNET_EV_DONT:
The WONT and DONT events are sent when the remote end of the connection
wishes to disable an option, when they are refusing to a support an option
that you have asked for, or in confirmation of an option you have asked to
be disabled.
Most commonly WONT and DONT events are sent as rejections of features you
requested by sending DO or WILL events. Receiving these events means the
TELNET option is not or will not be supported by the remote end, so give up.
Sometimes WONT or DONT will be sent for TELNET options that are already
enabled, but the remote end wishes to stop using. You cannot decline.
These events are demands that must be complied with. libtelnet will always
send the appropriate response back without consulting your application.
These events are sent to allow your application to disable its own use of
the features.
In either case, the TELNET option under negotiation will be in the
event->neg.telopt field.
Note that in PROXY mode libtelnet will do no processing of its own for you.
TELNET_EV_SUBNEGOTIATION:
Triggered whenever a TELNET sub-negotiation has been received. Sub-
negotiations include the NAWS option for communicating terminal size to a
server, the NEW-ENVIRON and TTYPE options for negotiating terminal features.
The event->sub->telopt value is the option under sub-negotiation. The
remaining data (if any) is passed in event->sub.buffer and event->sub.size.
Note that most sub-negotiation commands can include embedded NULL bytes in
the sub-negotiation data, and the data event->sub.buffer is not NULL
terminated, so always use the event->sub.size value!
The meaning and necessary processing for sub-negotiations are defined in
various TELNET RFCs and other informal specifications. A sub-negotiation
should never be sent unless the specific option has been enabled through
the use of the telnet negotiation feature.
TTYPE/ENVIRON/NEW-ENVIRON SUPPORT:
libtelnet parses these sub-negotiation commands. A special event will be
sent for each, after the SUBNEGOTIATION event is sent. Except in special
circumstances, the SUBNEGOTIATION event should be ignored for these options
and the special events should be handled explicitly.
TELNET_EV_TTYPE:
The event->ttype.cmd field will be either TELNET_TTYPE_SEND or
TELNET_TTYPE_IS.
When event->ttype.cmd is "TELNET_TTYPE_IS", it means your peer is sending
you their terminal type (per your earlier request), and event->ttype.name
points to their terminal type.
When event->ttype.cmd is "TELNET_TTYPE_SEND", your peer is requesting that
you send them your terminal type. You should use the telnet_ttype_is()
function to respond to such requests:
void telnet_ttype_is( telnet_t* telnet, const char* ttype );
TELNET_EV_ENVIRON:
The event->environ.cmd field will be either TELNET_ENVIRON_IS,
TELNET_ENVIRON_SEND, or TELNET_ENVIRON_INFO.
The actual environment variable sent or requested will be sent in the
event->environ.values field. This is an array of structures with the
following format:
struct telnet_environ_t
{
unsigned char type;
const char* var;
const char* value;
};
The number of entries in the event->environ.values array is stored in
event->environ.count.
Note that libtelnet does not support the ESC byte for ENVIRON/NEW-ENVIRON.
Data using escaped bytes will not be parsed correctly.
TELNET_EV_WARNING:
The WARNING event is sent whenever something has gone wrong inside of
libtelnet (possibly due to malformed data sent by the other end) but which
recovery is (likely) possible. It may be safe to continue using the
connection, but some data may have been lost or incorrectly interpreted.
The event->error.msg field will contain a NULL terminated string explaining
the error.
TELNET_EV_ERROR:
Similar to the WARNING event, the ERROR event is sent whenever something has
gone wrong. ERROR events are non-recoverable, however, and the application
should immediately close the connection. Whatever has happened is likely
going only to result in garbage from libtelnet.
The event->error.msg field will contain a NULL terminated string explaining
the error.
III. SAFETY AND CORRECTNESS CONSIDERATIONS
===============================================================================
Your existing application may make heavy use of its own output buffering and
transmission commands, including hand-made routines for sending TELNET commands
and sub-negotiation requests. There are at times subtle issues that need to be
handled when communication over the TELNET protocol, not least of which is the
need to escape any byte value 0xFF with a special TELNET command.
For these reasons, it is very important that applications making use of
libtelnet always make use of the libtelnet output functions for all data being
sent over the TELNET connection.
In particular, if you are writing a client, all user input must be passed
through to telnet_send(). This also includes any input generated automatically
by scripts, triggers, or macros.
For a server, any and all output -- including ANSI/VT100 escape codes, regular
text, newlines, and so on -- must be passed through to telnet_send().
Any TELNET commands that are to be sent must be given to one of the following:
telnet_iac, telnet_negotiate, or telnet_subnegotiation().
IV. TELNET PROXY UTILITY
===============================================================================
The telnet-proxy utility is a small application that serves both as a testbed
for libtelnet and as a powerful debugging tool for TELNET servers and clients.
To use telnet-proxy, you must first compile it using:
$ make
To run telnet-proxy, you simply give it the server's host name or IP address,
the server's port number, and the port number that telnet-proxy should listen
on. For example, to connect to the server on telnet.example.com port 7800
and to listen on port 5000, run:
$ ./telnet-proxy telnet.example.com 7800 5000
You can then connect to the host where telnet-proxy is running (e.g. 127.0.0.1)
on port 5000 and you will automatically be proxied into telnet.example.com port
7800.
telnet-proxy will display status information about the data passing through
both ends of the tunnel. telnet-proxy can only support a single tunnel at a
time. It will continue running until an error occurs or a terminating signal
is sent to the proxy process.
===============================================================================