1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540154115421543154415451546154715481549 |
- /*
- * libtelnet - TELNET protocol handling library
- *
- * Sean Middleditch
- * sean@sourcemud.org
- *
- * 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.
- */
- #ifdef HAVE_CONFIG_H
- #include <config.h>
- #endif
- #include <stdlib.h>
- #include <string.h>
- #include <stdio.h>
- #include <errno.h>
- #include <string.h>
- #include <stdarg.h>
- /* Win32 compatibility */
- #if defined(_WIN32)
- # define vsnprintf _vsnprintf
- # define __func__ __FUNCTION__
- # define ZLIB_WINAPI 1
- #endif
- #if defined(HAVE_ZLIB)
- # include <zlib.h>
- #endif
- #include "libtelnet.h"
- /* inlinable functions */
- #if defined(__GNUC__) || __STDC_VERSION__ >= 199901L
- # define INLINE __inline__
- #else
- # define INLINE
- #endif
- /* helper for Q-method option tracking */
- #define Q_US(q) ((q).state & 0x0F)
- #define Q_HIM(q) (((q).state & 0xF0) >> 4)
- #define Q_MAKE(us,him) ((us) | ((him) << 4))
- /* helper for the negotiation routines */
- #define NEGOTIATE_EVENT(telnet,cmd,opt) \
- ev.type = (cmd); \
- ev.neg.telopt = (opt); \
- (telnet)->eh((telnet), &ev, (telnet)->ud);
- /* telnet state codes */
- enum telnet_state_t {
- TELNET_STATE_DATA = 0,
- TELNET_STATE_IAC,
- TELNET_STATE_WILL,
- TELNET_STATE_WONT,
- TELNET_STATE_DO,
- TELNET_STATE_DONT,
- TELNET_STATE_SB,
- TELNET_STATE_SB_DATA,
- TELNET_STATE_SB_DATA_IAC
- };
- typedef enum telnet_state_t telnet_state_t;
- /* telnet state tracker */
- struct telnet_t {
- /* user data */
- void *ud;
- /* telopt support table */
- const telnet_telopt_t *telopts;
- /* event handler */
- telnet_event_handler_t eh;
- #if defined(HAVE_ZLIB)
- /* zlib (mccp2) compression */
- z_stream *z;
- #endif
- /* RFC1143 option negotiation states */
- struct telnet_rfc1143_t *q;
- /* sub-request buffer */
- char *buffer;
- /* current size of the buffer */
- size_t buffer_size;
- /* current buffer write position (also length of buffer data) */
- size_t buffer_pos;
- /* current state */
- enum telnet_state_t state;
- /* option flags */
- unsigned char flags;
- /* current subnegotiation telopt */
- unsigned char sb_telopt;
- /* length of RFC1143 queue */
- unsigned char q_size;
- };
- /* RFC1143 option negotiation state */
- typedef struct telnet_rfc1143_t {
- unsigned char telopt;
- unsigned char state;
- } telnet_rfc1143_t;
- /* RFC1143 state names */
- #define Q_NO 0
- #define Q_YES 1
- #define Q_WANTNO 2
- #define Q_WANTYES 3
- #define Q_WANTNO_OP 4
- #define Q_WANTYES_OP 5
- /* buffer sizes */
- static const size_t _buffer_sizes[] = { 0, 512, 2048, 8192, 16384, };
- static const size_t _buffer_sizes_count = sizeof(_buffer_sizes) /
- sizeof(_buffer_sizes[0]);
- /* error generation function */
- static telnet_error_t _error(telnet_t *telnet, unsigned line,
- const char* func, telnet_error_t err, int fatal, const char *fmt,
- ...) {
- telnet_event_t ev;
- char buffer[512];
- va_list va;
- /* format informational text */
- va_start(va, fmt);
- vsnprintf(buffer, sizeof(buffer), fmt, va);
- va_end(va);
- /* send error event to the user */
- ev.type = fatal ? TELNET_EV_ERROR : TELNET_EV_WARNING;
- ev.error.file = __FILE__;
- ev.error.func = func;
- ev.error.line = line;
- ev.error.msg = buffer;
- telnet->eh(telnet, &ev, telnet->ud);
-
- return err;
- }
- #if defined(HAVE_ZLIB)
- /* initialize the zlib box for a telnet box; if deflate is non-zero, it
- * initializes zlib for delating (compression), otherwise for inflating
- * (decompression). returns TELNET_EOK on success, something else on
- * failure.
- */
- telnet_error_t _init_zlib(telnet_t *telnet, int deflate, int err_fatal) {
- z_stream *z;
- int rs;
- /* if compression is already enabled, fail loudly */
- if (telnet->z != 0)
- return _error(telnet, __LINE__, __func__, TELNET_EBADVAL,
- err_fatal, "cannot initialize compression twice");
- /* allocate zstream box */
- if ((z= (z_stream *)calloc(1, sizeof(z_stream))) == 0)
- return _error(telnet, __LINE__, __func__, TELNET_ENOMEM, err_fatal,
- "malloc() failed: %s", strerror(errno));
- /* initialize */
- if (deflate) {
- if ((rs = deflateInit(z, Z_DEFAULT_COMPRESSION)) != Z_OK) {
- free(z);
- return _error(telnet, __LINE__, __func__, TELNET_ECOMPRESS,
- err_fatal, "deflateInit() failed: %s", zError(rs));
- }
- telnet->flags |= TELNET_PFLAG_DEFLATE;
- } else {
- if ((rs = inflateInit(z)) != Z_OK) {
- free(z);
- return _error(telnet, __LINE__, __func__, TELNET_ECOMPRESS,
- err_fatal, "inflateInit() failed: %s", zError(rs));
- }
- telnet->flags &= ~TELNET_PFLAG_DEFLATE;
- }
- telnet->z = z;
- return TELNET_EOK;
- }
- #endif /* defined(HAVE_ZLIB) */
- /* push bytes out, compressing them first if need be */
- static void _send(telnet_t *telnet, const char *buffer,
- size_t size) {
- telnet_event_t ev;
- #if defined(HAVE_ZLIB)
- /* if we have a deflate (compression) zlib box, use it */
- if (telnet->z != 0 && telnet->flags & TELNET_PFLAG_DEFLATE) {
- char deflate_buffer[1024];
- int rs;
- /* initialize z state */
- telnet->z->next_in = (unsigned char *)buffer;
- telnet->z->avail_in = (unsigned int)size;
- telnet->z->next_out = (unsigned char *)deflate_buffer;
- telnet->z->avail_out = sizeof(deflate_buffer);
- /* deflate until buffer exhausted and all output is produced */
- while (telnet->z->avail_in > 0 || telnet->z->avail_out == 0) {
- /* compress */
- if ((rs = deflate(telnet->z, Z_SYNC_FLUSH)) != Z_OK) {
- _error(telnet, __LINE__, __func__, TELNET_ECOMPRESS, 1,
- "deflate() failed: %s", zError(rs));
- deflateEnd(telnet->z);
- free(telnet->z);
- telnet->z = 0;
- break;
- }
- /* send event */
- ev.type = TELNET_EV_SEND;
- ev.data.buffer = deflate_buffer;
- ev.data.size = sizeof(deflate_buffer) - telnet->z->avail_out;
- telnet->eh(telnet, &ev, telnet->ud);
- /* prepare output buffer for next run */
- telnet->z->next_out = (unsigned char *)deflate_buffer;
- telnet->z->avail_out = sizeof(deflate_buffer);
- }
- /* do not continue with remaining code */
- return;
- }
- #endif /* defined(HAVE_ZLIB) */
- ev.type = TELNET_EV_SEND;
- ev.data.buffer = buffer;
- ev.data.size = size;
- telnet->eh(telnet, &ev, telnet->ud);
- }
- /* to send bags of unsigned chars */
- #define _sendu(t, d, s) _send((t), (const char*)(d), (s))
- /* check if we support a particular telopt; if us is non-zero, we
- * check if we (local) supports it, otherwise we check if he (remote)
- * supports it. return non-zero if supported, zero if not supported.
- */
- static INLINE int _check_telopt(telnet_t *telnet, unsigned char telopt,
- int us) {
- int i;
- /* if we have no telopts table, we obviously don't support it */
- if (telnet->telopts == 0)
- return 0;
- /* loop unti found or end marker (us and him both 0) */
- for (i = 0; telnet->telopts[i].telopt != -1; ++i) {
- if (telnet->telopts[i].telopt == telopt) {
- if (us && telnet->telopts[i].us == TELNET_WILL)
- return 1;
- else if (!us && telnet->telopts[i].him == TELNET_DO)
- return 1;
- else
- return 0;
- }
- }
- /* not found, so not supported */
- return 0;
- }
- /* retrieve RFC1143 option state */
- static INLINE telnet_rfc1143_t _get_rfc1143(telnet_t *telnet,
- unsigned char telopt) {
- telnet_rfc1143_t empty;
- int i;
- /* search for entry */
- for (i = 0; i != telnet->q_size; ++i) {
- if (telnet->q[i].telopt == telopt) {
- return telnet->q[i];
- }
- }
- /* not found, return empty value */
- empty.telopt = telopt;
- empty.state = 0;
- return empty;
- }
- /* save RFC1143 option state */
- static INLINE void _set_rfc1143(telnet_t *telnet, unsigned char telopt,
- char us, char him) {
- telnet_rfc1143_t *qtmp;
- int i;
- /* search for entry */
- for (i = 0; i != telnet->q_size; ++i) {
- if (telnet->q[i].telopt == telopt) {
- telnet->q[i].state = Q_MAKE(us,him);
- return;
- }
- }
- /* we're going to need to track state for it, so grow the queue
- * by 4 (four) elements and put the telopt into it; bail on allocation
- * error. we go by four because it seems like a reasonable guess as
- * to the number of enabled options for most simple code, and it
- * allows for an acceptable number of reallocations for complex code.
- */
- if ((qtmp = (telnet_rfc1143_t *)realloc(telnet->q,
- sizeof(telnet_rfc1143_t) * (telnet->q_size + 4))) == 0) {
- _error(telnet, __LINE__, __func__, TELNET_ENOMEM, 0,
- "realloc() failed: %s", strerror(errno));
- return;
- }
- memset(&qtmp[telnet->q_size], 0, sizeof(telnet_rfc1143_t) * 4);
- telnet->q = qtmp;
- telnet->q[telnet->q_size].telopt = telopt;
- telnet->q[telnet->q_size].state = Q_MAKE(us, him);
- telnet->q_size += 4;
- }
- /* send negotiation bytes */
- static INLINE void _send_negotiate(telnet_t *telnet, unsigned char cmd,
- unsigned char telopt) {
- unsigned char bytes[3];
- bytes[0] = TELNET_IAC;
- bytes[1] = cmd;
- bytes[2] = telopt;
- _sendu(telnet, bytes, 3);
- }
- /* negotiation handling magic for RFC1143 */
- static void _negotiate(telnet_t *telnet, unsigned char telopt) {
- telnet_event_t ev;
- telnet_rfc1143_t q;
- /* in PROXY mode, just pass it thru and do nothing */
- if (telnet->flags & TELNET_FLAG_PROXY) {
- switch ((int)telnet->state) {
- case TELNET_STATE_WILL:
- NEGOTIATE_EVENT(telnet, TELNET_EV_WILL, telopt);
- break;
- case TELNET_STATE_WONT:
- NEGOTIATE_EVENT(telnet, TELNET_EV_WONT, telopt);
- break;
- case TELNET_STATE_DO:
- NEGOTIATE_EVENT(telnet, TELNET_EV_DO, telopt);
- break;
- case TELNET_STATE_DONT:
- NEGOTIATE_EVENT(telnet, TELNET_EV_DONT, telopt);
- break;
- }
- return;
- }
- /* lookup the current state of the option */
- q = _get_rfc1143(telnet, telopt);
- /* start processing... */
- switch ((int)telnet->state) {
- /* request to enable option on remote end or confirm DO */
- case TELNET_STATE_WILL:
- switch (Q_HIM(q)) {
- case Q_NO:
- if (_check_telopt(telnet, telopt, 0)) {
- _set_rfc1143(telnet, telopt, Q_US(q), Q_YES);
- _send_negotiate(telnet, TELNET_DO, telopt);
- NEGOTIATE_EVENT(telnet, TELNET_EV_WILL, telopt);
- } else
- _send_negotiate(telnet, TELNET_DONT, telopt);
- break;
- case Q_WANTNO:
- _set_rfc1143(telnet, telopt, Q_US(q), Q_NO);
- NEGOTIATE_EVENT(telnet, TELNET_EV_WONT, telopt);
- _error(telnet, __LINE__, __func__, TELNET_EPROTOCOL, 0,
- "DONT answered by WILL");
- break;
- case Q_WANTNO_OP:
- _set_rfc1143(telnet, telopt, Q_US(q), Q_YES);
- NEGOTIATE_EVENT(telnet, TELNET_EV_WILL, telopt);
- _error(telnet, __LINE__, __func__, TELNET_EPROTOCOL, 0,
- "DONT answered by WILL");
- break;
- case Q_WANTYES:
- _set_rfc1143(telnet, telopt, Q_US(q), Q_YES);
- NEGOTIATE_EVENT(telnet, TELNET_EV_WILL, telopt);
- break;
- case Q_WANTYES_OP:
- _set_rfc1143(telnet, telopt, Q_US(q), Q_WANTNO);
- _send_negotiate(telnet, TELNET_DONT, telopt);
- NEGOTIATE_EVENT(telnet, TELNET_EV_WILL, telopt);
- break;
- }
- break;
- /* request to disable option on remote end, confirm DONT, reject DO */
- case TELNET_STATE_WONT:
- switch (Q_HIM(q)) {
- case Q_YES:
- _set_rfc1143(telnet, telopt, Q_US(q), Q_NO);
- _send_negotiate(telnet, TELNET_DONT, telopt);
- NEGOTIATE_EVENT(telnet, TELNET_EV_WONT, telopt);
- break;
- case Q_WANTNO:
- _set_rfc1143(telnet, telopt, Q_US(q), Q_NO);
- NEGOTIATE_EVENT(telnet, TELNET_EV_WONT, telopt);
- break;
- case Q_WANTNO_OP:
- _set_rfc1143(telnet, telopt, Q_US(q), Q_WANTYES);
- NEGOTIATE_EVENT(telnet, TELNET_EV_DO, telopt);
- break;
- case Q_WANTYES:
- case Q_WANTYES_OP:
- _set_rfc1143(telnet, telopt, Q_US(q), Q_NO);
- break;
- }
- break;
- /* request to enable option on local end or confirm WILL */
- case TELNET_STATE_DO:
- switch (Q_US(q)) {
- case Q_NO:
- if (_check_telopt(telnet, telopt, 1)) {
- _set_rfc1143(telnet, telopt, Q_YES, Q_HIM(q));
- _send_negotiate(telnet, TELNET_WILL, telopt);
- NEGOTIATE_EVENT(telnet, TELNET_EV_DO, telopt);
- } else
- _send_negotiate(telnet, TELNET_WONT, telopt);
- break;
- case Q_WANTNO:
- _set_rfc1143(telnet, telopt, Q_NO, Q_HIM(q));
- NEGOTIATE_EVENT(telnet, TELNET_EV_DONT, telopt);
- _error(telnet, __LINE__, __func__, TELNET_EPROTOCOL, 0,
- "WONT answered by DO");
- break;
- case Q_WANTNO_OP:
- _set_rfc1143(telnet, telopt, Q_YES, Q_HIM(q));
- NEGOTIATE_EVENT(telnet, TELNET_EV_DO, telopt);
- _error(telnet, __LINE__, __func__, TELNET_EPROTOCOL, 0,
- "WONT answered by DO");
- break;
- case Q_WANTYES:
- _set_rfc1143(telnet, telopt, Q_YES, Q_HIM(q));
- NEGOTIATE_EVENT(telnet, TELNET_EV_DO, telopt);
- break;
- case Q_WANTYES_OP:
- _set_rfc1143(telnet, telopt, Q_WANTNO, Q_HIM(q));
- _send_negotiate(telnet, TELNET_WONT, telopt);
- NEGOTIATE_EVENT(telnet, TELNET_EV_DO, telopt);
- break;
- }
- break;
- /* request to disable option on local end, confirm WONT, reject WILL */
- case TELNET_STATE_DONT:
- switch (Q_US(q)) {
- case Q_YES:
- _set_rfc1143(telnet, telopt, Q_NO, Q_HIM(q));
- _send_negotiate(telnet, TELNET_WONT, telopt);
- NEGOTIATE_EVENT(telnet, TELNET_EV_DONT, telopt);
- break;
- case Q_WANTNO:
- _set_rfc1143(telnet, telopt, Q_NO, Q_HIM(q));
- NEGOTIATE_EVENT(telnet, TELNET_EV_WONT, telopt);
- break;
- case Q_WANTNO_OP:
- _set_rfc1143(telnet, telopt, Q_WANTYES, Q_HIM(q));
- _send_negotiate(telnet, TELNET_WILL, telopt);
- NEGOTIATE_EVENT(telnet, TELNET_EV_WILL, telopt);
- break;
- case Q_WANTYES:
- case Q_WANTYES_OP:
- _set_rfc1143(telnet, telopt, Q_NO, Q_HIM(q));
- break;
- }
- break;
- }
- }
- /* process an ENVIRON/NEW-ENVIRON subnegotiation buffer
- *
- * the algorithm and approach used here is kind of a hack,
- * but it reduces the number of memory allocations we have
- * to make.
- *
- * we copy the bytes back into the buffer, starting at the very
- * beginning, which makes it easy to handle the ENVIRON ESC
- * escape mechanism as well as ensure the variable name and
- * value strings are NUL-terminated, all while fitting inside
- * of the original buffer.
- */
- static int _environ_telnet(telnet_t *telnet, unsigned char type,
- char* buffer, size_t size) {
- telnet_event_t ev;
- struct telnet_environ_t *values = 0;
- char *c, *last, *out;
- size_t index, count;
- /* if we have no data, just pass it through */
- if (size == 0) {
- return 0;
- }
- /* first byte must be a valid command */
- if ((unsigned)buffer[0] != TELNET_ENVIRON_SEND &&
- (unsigned)buffer[0] != TELNET_ENVIRON_IS &&
- (unsigned)buffer[0] != TELNET_ENVIRON_INFO) {
- _error(telnet, __LINE__, __func__, TELNET_EPROTOCOL, 0,
- "telopt %d subneg has invalid command", type);
- return 0;
- }
- /* store ENVIRON command */
- ev.environ.cmd = buffer[0];
- /* if we have no arguments, send an event with no data end return */
- if (size == 1) {
- /* no list of variables given */
- ev.environ.values = 0;
- ev.environ.size = 0;
- /* invoke event with our arguments */
- ev.type = TELNET_EV_ENVIRON;
- telnet->eh(telnet, &ev, telnet->ud);
- return 1;
- }
- /* very second byte must be VAR or USERVAR, if present */
- if ((unsigned)buffer[1] != TELNET_ENVIRON_VAR &&
- (unsigned)buffer[1] != TELNET_ENVIRON_USERVAR) {
- _error(telnet, __LINE__, __func__, TELNET_EPROTOCOL, 0,
- "telopt %d subneg missing variable type", type);
- return 0;
- }
- /* ensure last byte is not an escape byte (makes parsing later easier) */
- if ((unsigned)buffer[size - 1] == TELNET_ENVIRON_ESC) {
- _error(telnet, __LINE__, __func__, TELNET_EPROTOCOL, 0,
- "telopt %d subneg ends with ESC", type);
- return 0;
- }
- /* count arguments; each valid entry starts with VAR or USERVAR */
- count = 0;
- for (c = buffer + 1; c < buffer + size; ++c) {
- if (*c == TELNET_ENVIRON_VAR || *c == TELNET_ENVIRON_USERVAR) {
- ++count;
- } else if (*c == TELNET_ENVIRON_ESC) {
- /* skip the next byte */
- ++c;
- }
- }
- /* allocate argument array, bail on error */
- if ((values = (struct telnet_environ_t *)calloc(count,
- sizeof(struct telnet_environ_t))) == 0) {
- _error(telnet, __LINE__, __func__, TELNET_ENOMEM, 0,
- "calloc() failed: %s", strerror(errno));
- return 0;
- }
- /* parse argument array strings */
- out = buffer;
- c = buffer + 1;
- for (index = 0; index != count; ++index) {
- /* remember the variable type (will be VAR or USERVAR) */
- values[index].type = *c++;
- /* scan until we find an end-marker, and buffer up unescaped
- * bytes into our buffer */
- last = out;
- while (c < buffer + size) {
- /* stop at the next variable or at the value */
- if ((unsigned)*c == TELNET_ENVIRON_VAR ||
- (unsigned)*c == TELNET_ENVIRON_VALUE ||
- (unsigned)*c == TELNET_ENVIRON_USERVAR) {
- break;
- }
- /* buffer next byte (taking into account ESC) */
- if (*c == TELNET_ENVIRON_ESC) {
- ++c;
- }
- *out++ = *c++;
- }
- *out++ = '\0';
- /* store the variable name we have just received */
- values[index].var = last;
- values[index].value = "";
- /* if we got a value, find the next end marker and
- * store the value; otherwise, store empty string */
- if (c < buffer + size && *c == TELNET_ENVIRON_VALUE) {
- ++c;
- last = out;
- while (c < buffer + size) {
- /* stop when we find the start of the next variable */
- if ((unsigned)*c == TELNET_ENVIRON_VAR ||
- (unsigned)*c == TELNET_ENVIRON_USERVAR) {
- break;
- }
- /* buffer next byte (taking into account ESC) */
- if (*c == TELNET_ENVIRON_ESC) {
- ++c;
- }
- *out++ = *c++;
- }
- *out++ = '\0';
- /* store the variable value */
- values[index].value = last;
- }
- }
- /* pass values array and count to event */
- ev.environ.values = values;
- ev.environ.size = count;
- /* invoke event with our arguments */
- ev.type = TELNET_EV_ENVIRON;
- telnet->eh(telnet, &ev, telnet->ud);
- /* clean up */
- free(values);
- return 1;
- }
- /* process an MSSP subnegotiation buffer */
- static int _mssp_telnet(telnet_t *telnet, char* buffer, size_t size) {
- telnet_event_t ev;
- struct telnet_environ_t *values;
- char *var = 0;
- char *c, *last, *out;
- size_t i, count;
- unsigned char next_type;
- /* if we have no data, just pass it through */
- if (size == 0) {
- return 0;
- }
- /* first byte must be a VAR */
- if ((unsigned)buffer[0] != TELNET_MSSP_VAR) {
- _error(telnet, __LINE__, __func__, TELNET_EPROTOCOL, 0,
- "MSSP subnegotiation has invalid data");
- return 0;
- }
- /* count the arguments, any part that starts with VALUE */
- for (count = 0, i = 0; i != size; ++i) {
- if ((unsigned)buffer[i] == TELNET_MSSP_VAL) {
- ++count;
- }
- }
- /* allocate argument array, bail on error */
- if ((values = (struct telnet_environ_t *)calloc(count,
- sizeof(struct telnet_environ_t))) == 0) {
- _error(telnet, __LINE__, __func__, TELNET_ENOMEM, 0,
- "calloc() failed: %s", strerror(errno));
- return 0;
- }
- ev.mssp.values = values;
- ev.mssp.size = count;
- /* allocate strings in argument array */
- out = last = buffer;
- next_type = buffer[0];
- for (i = 0, c = buffer + 1; c < buffer + size;) {
- /* search for end marker */
- while (c < buffer + size && (unsigned)*c != TELNET_MSSP_VAR &&
- (unsigned)*c != TELNET_MSSP_VAL) {
- *out++ = *c++;
- }
- *out++ = '\0';
- /* if it's a variable name, just store the name for now */
- if (next_type == TELNET_MSSP_VAR) {
- var = last;
- } else if (next_type == TELNET_MSSP_VAL && var != 0) {
- values[i].var = var;
- values[i].value = last;
- ++i;
- } else {
- _error(telnet, __LINE__, __func__, TELNET_EPROTOCOL, 0,
- "invalid MSSP subnegotiation data");
- free(values);
- return 0;
- }
- /* remember our next type and increment c for next loop run */
- last = out;
- next_type = *c++;
- }
- /* invoke event with our arguments */
- ev.type = TELNET_EV_MSSP;
- telnet->eh(telnet, &ev, telnet->ud);
- /* clean up */
- free(values);
- return 0;
- }
- /* parse ZMP command subnegotiation buffers */
- static int _zmp_telnet(telnet_t *telnet, const char* buffer, size_t size) {
- telnet_event_t ev;
- char **argv;
- const char *c;
- size_t i, argc;
- /* make sure this is a valid ZMP buffer */
- if (size == 0 || buffer[size - 1] != 0) {
- _error(telnet, __LINE__, __func__, TELNET_EPROTOCOL, 0,
- "incomplete ZMP frame");
- return 0;
- }
- /* count arguments */
- for (argc = 0, c = buffer; c != buffer + size; ++argc)
- c += strlen(c) + 1;
- /* allocate argument array, bail on error */
- if ((argv = (char **)calloc(argc, sizeof(char *))) == 0) {
- _error(telnet, __LINE__, __func__, TELNET_ENOMEM, 0,
- "calloc() failed: %s", strerror(errno));
- return 0;
- }
- /* populate argument array */
- for (i = 0, c = buffer; i != argc; ++i) {
- argv[i] = (char *)c;
- c += strlen(c) + 1;
- }
- /* invoke event with our arguments */
- ev.type = TELNET_EV_ZMP;
- ev.zmp.argv = (const char**)argv;
- ev.zmp.argc = argc;
- telnet->eh(telnet, &ev, telnet->ud);
- /* clean up */
- free(argv);
- return 0;
- }
- /* parse TERMINAL-TYPE command subnegotiation buffers */
- static int _ttype_telnet(telnet_t *telnet, const char* buffer, size_t size) {
- telnet_event_t ev;
- /* make sure request is not empty */
- if (size == 0) {
- _error(telnet, __LINE__, __func__, TELNET_EPROTOCOL, 0,
- "incomplete TERMINAL-TYPE request");
- return 0;
- }
- /* make sure request has valid command type */
- if (buffer[0] != TELNET_TTYPE_IS &&
- buffer[0] != TELNET_TTYPE_SEND) {
- _error(telnet, __LINE__, __func__, TELNET_EPROTOCOL, 0,
- "TERMINAL-TYPE request has invalid type");
- return 0;
- }
- /* send proper event */
- if (buffer[0] == TELNET_TTYPE_IS) {
- char *name;
- /* allocate space for name */
- if ((name = (char *)malloc(size)) == 0) {
- _error(telnet, __LINE__, __func__, TELNET_ENOMEM, 0,
- "malloc() failed: %s", strerror(errno));
- return 0;
- }
- memcpy(name, buffer + 1, size - 1);
- name[size - 1] = '\0';
- ev.type = TELNET_EV_TTYPE;
- ev.ttype.cmd = TELNET_TTYPE_IS;
- ev.ttype.name = name;
- telnet->eh(telnet, &ev, telnet->ud);
- /* clean up */
- free(name);
- } else {
- ev.type = TELNET_EV_TTYPE;
- ev.ttype.cmd = TELNET_TTYPE_SEND;
- ev.ttype.name = 0;
- telnet->eh(telnet, &ev, telnet->ud);
- }
- return 0;
- }
- /* process a subnegotiation buffer; return non-zero if the current buffer
- * must be aborted and reprocessed due to COMPRESS2 being activated
- */
- static int _subnegotiate(telnet_t *telnet) {
- telnet_event_t ev;
- /* standard subnegotiation event */
- ev.type = TELNET_EV_SUBNEGOTIATION;
- ev.sub.telopt = telnet->sb_telopt;
- ev.sub.buffer = telnet->buffer;
- ev.sub.size = telnet->buffer_pos;
- telnet->eh(telnet, &ev, telnet->ud);
- switch (telnet->sb_telopt) {
- #if defined(HAVE_ZLIB)
- /* received COMPRESS2 begin marker, setup our zlib box and
- * start handling the compressed stream if it's not already.
- */
- case TELNET_TELOPT_COMPRESS2:
- if (telnet->sb_telopt == TELNET_TELOPT_COMPRESS2) {
- if (_init_zlib(telnet, 0, 1) != TELNET_EOK)
- return 0;
- /* notify app that compression was enabled */
- ev.type = TELNET_EV_COMPRESS;
- ev.compress.state = 1;
- telnet->eh(telnet, &ev, telnet->ud);
- return 1;
- }
- return 0;
- #endif /* defined(HAVE_ZLIB) */
- /* specially handled subnegotiation telopt types */
- case TELNET_TELOPT_ZMP:
- return _zmp_telnet(telnet, telnet->buffer, telnet->buffer_pos);
- case TELNET_TELOPT_TTYPE:
- return _ttype_telnet(telnet, telnet->buffer, telnet->buffer_pos);
- case TELNET_TELOPT_ENVIRON:
- case TELNET_TELOPT_NEW_ENVIRON:
- return _environ_telnet(telnet, telnet->sb_telopt, telnet->buffer,
- telnet->buffer_pos);
- case TELNET_TELOPT_MSSP:
- return _mssp_telnet(telnet, telnet->buffer, telnet->buffer_pos);
- default:
- return 0;
- }
- }
- /* initialize a telnet state tracker */
- telnet_t *telnet_init(const telnet_telopt_t *telopts,
- telnet_event_handler_t eh, unsigned char flags, void *user_data) {
- /* allocate structure */
- struct telnet_t *telnet = (telnet_t*)calloc(1, sizeof(telnet_t));
- if (telnet == 0)
- return 0;
- /* initialize data */
- telnet->ud = user_data;
- telnet->telopts = telopts;
- telnet->eh = eh;
- telnet->flags = flags;
- return telnet;
- }
- /* free up any memory allocated by a state tracker */
- void telnet_free(telnet_t *telnet) {
- /* free sub-request buffer */
- if (telnet->buffer != 0) {
- free(telnet->buffer);
- telnet->buffer = 0;
- telnet->buffer_size = 0;
- telnet->buffer_pos = 0;
- }
- #if defined(HAVE_ZLIB)
- /* free zlib box */
- if (telnet->z != 0) {
- if (telnet->flags & TELNET_PFLAG_DEFLATE)
- deflateEnd(telnet->z);
- else
- inflateEnd(telnet->z);
- free(telnet->z);
- telnet->z = 0;
- }
- #endif /* defined(HAVE_ZLIB) */
- /* free RFC1143 queue */
- if (telnet->q) {
- free(telnet->q);
- telnet->q = 0;
- telnet->q_size = 0;
- }
- /* free the telnet structure itself */
- free(telnet);
- }
- /* push a byte into the telnet buffer */
- static telnet_error_t _buffer_byte(telnet_t *telnet,
- unsigned char byte) {
- char *new_buffer;
- size_t i;
- /* check if we're out of room */
- if (telnet->buffer_pos == telnet->buffer_size) {
- /* find the next buffer size */
- for (i = 0; i != _buffer_sizes_count; ++i) {
- if (_buffer_sizes[i] == telnet->buffer_size) {
- break;
- }
- }
- /* overflow -- can't grow any more */
- if (i >= _buffer_sizes_count - 1) {
- _error(telnet, __LINE__, __func__, TELNET_EOVERFLOW, 0,
- "subnegotiation buffer size limit reached");
- return TELNET_EOVERFLOW;
- }
- /* (re)allocate buffer */
- new_buffer = (char *)realloc(telnet->buffer, _buffer_sizes[i + 1]);
- if (new_buffer == 0) {
- _error(telnet, __LINE__, __func__, TELNET_ENOMEM, 0,
- "realloc() failed");
- return TELNET_ENOMEM;
- }
- telnet->buffer = new_buffer;
- telnet->buffer_size = _buffer_sizes[i + 1];
- }
- /* push the byte, all set */
- telnet->buffer[telnet->buffer_pos++] = byte;
- return TELNET_EOK;
- }
- static void _process(telnet_t *telnet, const char *buffer, size_t size) {
- telnet_event_t ev;
- unsigned char byte;
- size_t i, start;
- for (i = start = 0; i != size; ++i) {
- byte = buffer[i];
- switch (telnet->state) {
- /* regular data */
- case TELNET_STATE_DATA:
- /* on an IAC byte, pass through all pending bytes and
- * switch states */
- if (byte == TELNET_IAC) {
- if (i != start) {
- ev.type = TELNET_EV_DATA;
- ev.data.buffer = buffer + start;
- ev.data.size = i - start;
- telnet->eh(telnet, &ev, telnet->ud);
- }
- telnet->state = TELNET_STATE_IAC;
- }
- break;
- /* IAC command */
- case TELNET_STATE_IAC:
- switch (byte) {
- /* subnegotiation */
- case TELNET_SB:
- telnet->state = TELNET_STATE_SB;
- break;
- /* negotiation commands */
- case TELNET_WILL:
- telnet->state = TELNET_STATE_WILL;
- break;
- case TELNET_WONT:
- telnet->state = TELNET_STATE_WONT;
- break;
- case TELNET_DO:
- telnet->state = TELNET_STATE_DO;
- break;
- case TELNET_DONT:
- telnet->state = TELNET_STATE_DONT;
- break;
- /* IAC escaping */
- case TELNET_IAC:
- /* event */
- ev.type = TELNET_EV_DATA;
- ev.data.buffer = (char*)&byte;
- ev.data.size = 1;
- telnet->eh(telnet, &ev, telnet->ud);
- /* state update */
- start = i + 1;
- telnet->state = TELNET_STATE_DATA;
- break;
- /* some other command */
- default:
- /* event */
- ev.type = TELNET_EV_IAC;
- ev.iac.cmd = byte;
- telnet->eh(telnet, &ev, telnet->ud);
- /* state update */
- start = i + 1;
- telnet->state = TELNET_STATE_DATA;
- }
- break;
- /* negotiation commands */
- case TELNET_STATE_WILL:
- case TELNET_STATE_WONT:
- case TELNET_STATE_DO:
- case TELNET_STATE_DONT:
- _negotiate(telnet, byte);
- start = i + 1;
- telnet->state = TELNET_STATE_DATA;
- break;
- /* subnegotiation -- determine subnegotiation telopt */
- case TELNET_STATE_SB:
- telnet->sb_telopt = byte;
- telnet->buffer_pos = 0;
- telnet->state = TELNET_STATE_SB_DATA;
- break;
- /* subnegotiation -- buffer bytes until end request */
- case TELNET_STATE_SB_DATA:
- /* IAC command in subnegotiation -- either IAC SE or IAC IAC */
- if (byte == TELNET_IAC) {
- telnet->state = TELNET_STATE_SB_DATA_IAC;
- } else if (telnet->sb_telopt == TELNET_TELOPT_COMPRESS && byte == TELNET_WILL) {
- /* In 1998 MCCP used TELOPT 85 and the protocol defined an invalid
- * subnegotiation sequence (IAC SB 85 WILL SE) to start compression.
- * Subsequently MCCP version 2 was created in 2000 using TELOPT 86
- * and a valid subnegotiation (IAC SB 86 IAC SE). libtelnet for now
- * just captures and discards MCCPv1 sequences.
- */
- start = i + 2;
- telnet->state = TELNET_STATE_DATA;
- /* buffer the byte, or bail if we can't */
- } else if (_buffer_byte(telnet, byte) != TELNET_EOK) {
- start = i + 1;
- telnet->state = TELNET_STATE_DATA;
- }
- break;
- /* IAC escaping inside a subnegotiation */
- case TELNET_STATE_SB_DATA_IAC:
- switch (byte) {
- /* end subnegotiation */
- case TELNET_SE:
- /* return to default state */
- start = i + 1;
- telnet->state = TELNET_STATE_DATA;
- /* process subnegotiation */
- if (_subnegotiate(telnet) != 0) {
- /* any remaining bytes in the buffer are compressed.
- * we have to re-invoke telnet_recv to get those
- * bytes inflated and abort trying to process the
- * remaining compressed bytes in the current _process
- * buffer argument
- */
- telnet_recv(telnet, &buffer[start], size - start);
- return;
- }
- break;
- /* escaped IAC byte */
- case TELNET_IAC:
- /* push IAC into buffer */
- if (_buffer_byte(telnet, TELNET_IAC) !=
- TELNET_EOK) {
- start = i + 1;
- telnet->state = TELNET_STATE_DATA;
- } else {
- telnet->state = TELNET_STATE_SB_DATA;
- }
- break;
- /* something else -- protocol error. attempt to process
- * content in subnegotiation buffer, then evaluate the
- * given command as an IAC code.
- */
- default:
- _error(telnet, __LINE__, __func__, TELNET_EPROTOCOL, 0,
- "unexpected byte after IAC inside SB: %d",
- byte);
- /* enter IAC state */
- start = i + 1;
- telnet->state = TELNET_STATE_IAC;
- /* process subnegotiation; see comment in
- * TELNET_STATE_SB_DATA_IAC about invoking telnet_recv()
- */
- if (_subnegotiate(telnet) != 0) {
- telnet_recv(telnet, &buffer[start], size - start);
- return;
- } else {
- /* recursive call to get the current input byte processed
- * as a regular IAC command. we could use a goto, but
- * that would be gross.
- */
- _process(telnet, (char *)&byte, 1);
- }
- break;
- }
- break;
- }
- }
- /* pass through any remaining bytes */
- if (telnet->state == TELNET_STATE_DATA && i != start) {
- ev.type = TELNET_EV_DATA;
- ev.data.buffer = buffer + start;
- ev.data.size = i - start;
- telnet->eh(telnet, &ev, telnet->ud);
- }
- }
- /* push a bytes into the state tracker */
- void telnet_recv(telnet_t *telnet, const char *buffer,
- size_t size) {
- #if defined(HAVE_ZLIB)
- /* if we have an inflate (decompression) zlib stream, use it */
- if (telnet->z != 0 && !(telnet->flags & TELNET_PFLAG_DEFLATE)) {
- char inflate_buffer[1024];
- int rs;
- /* initialize zlib state */
- telnet->z->next_in = (unsigned char*)buffer;
- telnet->z->avail_in = (unsigned int)size;
- telnet->z->next_out = (unsigned char *)inflate_buffer;
- telnet->z->avail_out = sizeof(inflate_buffer);
- /* inflate until buffer exhausted and all output is produced */
- while (telnet->z->avail_in > 0 || telnet->z->avail_out == 0) {
- /* reset output buffer */
- /* decompress */
- rs = inflate(telnet->z, Z_SYNC_FLUSH);
- /* process the decompressed bytes on success */
- if (rs == Z_OK || rs == Z_STREAM_END)
- _process(telnet, inflate_buffer, sizeof(inflate_buffer) -
- telnet->z->avail_out);
- else
- _error(telnet, __LINE__, __func__, TELNET_ECOMPRESS, 1,
- "inflate() failed: %s", zError(rs));
- /* prepare output buffer for next run */
- telnet->z->next_out = (unsigned char *)inflate_buffer;
- telnet->z->avail_out = sizeof(inflate_buffer);
- /* on error (or on end of stream) disable further inflation */
- if (rs != Z_OK) {
- telnet_event_t ev;
- /* disable compression */
- inflateEnd(telnet->z);
- free(telnet->z);
- telnet->z = 0;
- /* send event */
- ev.type = TELNET_EV_COMPRESS;
- ev.compress.state = 0;
- telnet->eh(telnet, &ev, telnet->ud);
- break;
- }
- }
- /* COMPRESS2 is not negotiated, just process */
- } else
- #endif /* defined(HAVE_ZLIB) */
- _process(telnet, buffer, size);
- }
- /* send an iac command */
- void telnet_iac(telnet_t *telnet, unsigned char cmd) {
- unsigned char bytes[2];
- bytes[0] = TELNET_IAC;
- bytes[1] = cmd;
- _sendu(telnet, bytes, 2);
- }
- /* send negotiation */
- void telnet_negotiate(telnet_t *telnet, unsigned char cmd,
- unsigned char telopt) {
- telnet_rfc1143_t q;
- /* if we're in proxy mode, just send it now */
- if (telnet->flags & TELNET_FLAG_PROXY) {
- unsigned char bytes[3];
- bytes[0] = TELNET_IAC;
- bytes[1] = cmd;
- bytes[2] = telopt;
- _sendu(telnet, bytes, 3);
- return;
- }
-
- /* get current option states */
- q = _get_rfc1143(telnet, telopt);
- switch (cmd) {
- /* advertise willingess to support an option */
- case TELNET_WILL:
- switch (Q_US(q)) {
- case Q_NO:
- _set_rfc1143(telnet, telopt, Q_WANTYES, Q_HIM(q));
- _send_negotiate(telnet, TELNET_WILL, telopt);
- break;
- case Q_WANTNO:
- _set_rfc1143(telnet, telopt, Q_WANTNO_OP, Q_HIM(q));
- break;
- case Q_WANTYES_OP:
- _set_rfc1143(telnet, telopt, Q_WANTYES, Q_HIM(q));
- break;
- }
- break;
- /* force turn-off of locally enabled option */
- case TELNET_WONT:
- switch (Q_US(q)) {
- case Q_YES:
- _set_rfc1143(telnet, telopt, Q_WANTNO, Q_HIM(q));
- _send_negotiate(telnet, TELNET_WONT, telopt);
- break;
- case Q_WANTYES:
- _set_rfc1143(telnet, telopt, Q_WANTYES_OP, Q_HIM(q));
- break;
- case Q_WANTNO_OP:
- _set_rfc1143(telnet, telopt, Q_WANTNO, Q_HIM(q));
- break;
- }
- break;
- /* ask remote end to enable an option */
- case TELNET_DO:
- switch (Q_HIM(q)) {
- case Q_NO:
- _set_rfc1143(telnet, telopt, Q_US(q), Q_WANTYES);
- _send_negotiate(telnet, TELNET_DO, telopt);
- break;
- case Q_WANTNO:
- _set_rfc1143(telnet, telopt, Q_US(q), Q_WANTNO_OP);
- break;
- case Q_WANTYES_OP:
- _set_rfc1143(telnet, telopt, Q_US(q), Q_WANTYES);
- break;
- }
- break;
- /* demand remote end disable an option */
- case TELNET_DONT:
- switch (Q_HIM(q)) {
- case Q_YES:
- _set_rfc1143(telnet, telopt, Q_US(q), Q_WANTNO);
- _send_negotiate(telnet, TELNET_DONT, telopt);
- break;
- case Q_WANTYES:
- _set_rfc1143(telnet, telopt, Q_US(q), Q_WANTYES_OP);
- break;
- case Q_WANTNO_OP:
- _set_rfc1143(telnet, telopt, Q_US(q), Q_WANTNO);
- break;
- }
- break;
- }
- }
- /* send non-command data (escapes IAC bytes) */
- void telnet_send(telnet_t *telnet, const char *buffer,
- size_t size) {
- size_t i, l;
- for (l = i = 0; i != size; ++i) {
- /* dump prior portion of text, send escaped bytes */
- if (buffer[i] == (char)TELNET_IAC) {
- /* dump prior text if any */
- if (i != l) {
- _send(telnet, buffer + l, i - l);
- }
- l = i + 1;
- /* send escape */
- telnet_iac(telnet, TELNET_IAC);
- }
- }
- /* send whatever portion of buffer is left */
- if (i != l) {
- _send(telnet, buffer + l, i - l);
- }
- }
- /* send subnegotiation header */
- void telnet_begin_sb(telnet_t *telnet, unsigned char telopt) {
- unsigned char sb[3];
- sb[0] = TELNET_IAC;
- sb[1] = TELNET_SB;
- sb[2] = telopt;
- _sendu(telnet, sb, 3);
- }
- /* send complete subnegotiation */
- void telnet_subnegotiation(telnet_t *telnet, unsigned char telopt,
- const char *buffer, size_t size) {
- unsigned char bytes[5];
- bytes[0] = TELNET_IAC;
- bytes[1] = TELNET_SB;
- bytes[2] = telopt;
- bytes[3] = TELNET_IAC;
- bytes[4] = TELNET_SE;
- _sendu(telnet, bytes, 3);
- telnet_send(telnet, buffer, size);
- _sendu(telnet, bytes + 3, 2);
- #if defined(HAVE_ZLIB)
- /* if we're a proxy and we just sent the COMPRESS2 marker, we must
- * make sure all further data is compressed if not already.
- */
- if (telnet->flags & TELNET_FLAG_PROXY &&
- telopt == TELNET_TELOPT_COMPRESS2) {
- telnet_event_t ev;
- if (_init_zlib(telnet, 1, 1) != TELNET_EOK)
- return;
- /* notify app that compression was enabled */
- ev.type = TELNET_EV_COMPRESS;
- ev.compress.state = 1;
- telnet->eh(telnet, &ev, telnet->ud);
- }
- #endif /* defined(HAVE_ZLIB) */
- }
- void telnet_begin_compress2(telnet_t *telnet) {
- #if defined(HAVE_ZLIB)
- static const unsigned char compress2[] = { TELNET_IAC, TELNET_SB,
- TELNET_TELOPT_COMPRESS2, TELNET_IAC, TELNET_SE };
- telnet_event_t ev;
- /* attempt to create output stream first, bail if we can't */
- if (_init_zlib(telnet, 1, 0) != TELNET_EOK)
- return;
- /* send compression marker. we send directly to the event handler
- * instead of passing through _send because _send would result in
- * the compress marker itself being compressed.
- */
- ev.type = TELNET_EV_SEND;
- ev.data.buffer = (const char*)compress2;
- ev.data.size = sizeof(compress2);
- telnet->eh(telnet, &ev, telnet->ud);
- /* notify app that compression was successfully enabled */
- ev.type = TELNET_EV_COMPRESS;
- ev.compress.state = 1;
- telnet->eh(telnet, &ev, telnet->ud);
- #endif /* defined(HAVE_ZLIB) */
- }
- /* send formatted data with \r and \n translation in addition to IAC IAC */
- int telnet_vprintf(telnet_t *telnet, const char *fmt, va_list va) {
- static const char CRLF[] = { '\r', '\n' };
- static const char CRNUL[] = { '\r', '\0' };
- char buffer[1024];
- char *output = buffer;
- int rs, i, l;
- /* format */
- rs = vsnprintf(buffer, sizeof(buffer), fmt, va);
- if (rs >= sizeof(buffer)) {
- output = (char*)malloc(rs + 1);
- if (output == 0) {
- _error(telnet, __LINE__, __func__, TELNET_ENOMEM, 0,
- "malloc() failed: %s", strerror(errno));
- return -1;
- }
- rs = vsnprintf(output, rs + 1, fmt, va);
- }
- /* send */
- for (l = i = 0; i != rs; ++i) {
- /* special characters */
- if (output[i] == (char)TELNET_IAC || output[i] == '\r' ||
- output[i] == '\n') {
- /* dump prior portion of text */
- if (i != l)
- _send(telnet, output + l, i - l);
- l = i + 1;
- /* IAC -> IAC IAC */
- if (output[i] == (char)TELNET_IAC)
- telnet_iac(telnet, TELNET_IAC);
- /* automatic translation of \r -> CRNUL */
- else if (output[i] == '\r')
- _send(telnet, CRNUL, 2);
- /* automatic translation of \n -> CRLF */
- else if (output[i] == '\n')
- _send(telnet, CRLF, 2);
- }
- }
- /* send whatever portion of output is left */
- if (i != l) {
- _send(telnet, output + l, i - l);
- }
- /* free allocated memory, if any */
- if (output != buffer) {
- free(output);
- }
- return rs;
- }
- /* see telnet_vprintf */
- int telnet_printf(telnet_t *telnet, const char *fmt, ...) {
- va_list va;
- int rs;
- va_start(va, fmt);
- rs = telnet_vprintf(telnet, fmt, va);
- va_end(va);
- return rs;
- }
- /* send formatted data through telnet_send */
- int telnet_raw_vprintf(telnet_t *telnet, const char *fmt, va_list va) {
- char buffer[1024];
- char *output = buffer;
- int rs;
- /* format; allocate more space if necessary */
- rs = vsnprintf(buffer, sizeof(buffer), fmt, va);
- if (rs >= sizeof(buffer)) {
- output = (char*)malloc(rs + 1);
- if (output == 0) {
- _error(telnet, __LINE__, __func__, TELNET_ENOMEM, 0,
- "malloc() failed: %s", strerror(errno));
- return -1;
- }
- rs = vsnprintf(output, rs + 1, fmt, va);
- }
- /* send out the formatted data */
- telnet_send(telnet, output, rs);
- /* release allocated memory, if any */
- if (output != buffer) {
- free(output);
- }
- return rs;
- }
- /* see telnet_raw_vprintf */
- int telnet_raw_printf(telnet_t *telnet, const char *fmt, ...) {
- va_list va;
- int rs;
- va_start(va, fmt);
- rs = telnet_raw_vprintf(telnet, fmt, va);
- va_end(va);
- return rs;
- }
- /* begin NEW-ENVIRON subnegotation */
- void telnet_begin_newenviron(telnet_t *telnet, unsigned char cmd) {
- telnet_begin_sb(telnet, TELNET_TELOPT_NEW_ENVIRON);
- telnet_send(telnet, (const char *)&cmd, 1);
- }
- /* send a NEW-ENVIRON value */
- void telnet_newenviron_value(telnet_t *telnet, unsigned char type,
- const char *string) {
- telnet_send(telnet, (const char*)&type, 1);
- if (string != 0) {
- telnet_send(telnet, string, strlen(string));
- }
- }
- /* send TERMINAL-TYPE SEND command */
- void telnet_ttype_send(telnet_t *telnet) {
- static const unsigned char SEND[] = { TELNET_IAC, TELNET_SB,
- TELNET_TELOPT_TTYPE, TELNET_TTYPE_SEND, TELNET_IAC, TELNET_SE };
- _sendu(telnet, SEND, sizeof(SEND));
- }
- /* send TERMINAL-TYPE IS command */
- void telnet_ttype_is(telnet_t *telnet, const char* ttype) {
- static const unsigned char IS[] = { TELNET_IAC, TELNET_SB,
- TELNET_TELOPT_TTYPE, TELNET_TTYPE_IS };
- _sendu(telnet, IS, sizeof(IS));
- _send(telnet, ttype, strlen(ttype));
- telnet_finish_sb(telnet);
- }
- /* send ZMP data */
- void telnet_send_zmp(telnet_t *telnet, size_t argc, const char **argv) {
- size_t i;
- /* ZMP header */
- telnet_begin_zmp(telnet, argv[0]);
- /* send out each argument, including trailing NUL byte */
- for (i = 1; i != argc; ++i)
- telnet_zmp_arg(telnet, argv[i]);
- /* ZMP footer */
- telnet_finish_zmp(telnet);
- }
- /* send ZMP data using varargs */
- void telnet_send_vzmpv(telnet_t *telnet, va_list va) {
- const char* arg;
- /* ZMP header */
- telnet_begin_sb(telnet, TELNET_TELOPT_ZMP);
- /* send out each argument, including trailing NUL byte */
- while ((arg = va_arg(va, const char *)) != 0)
- telnet_zmp_arg(telnet, arg);
- /* ZMP footer */
- telnet_finish_zmp(telnet);
- }
- /* see telnet_send_vzmpv */
- void telnet_send_zmpv(telnet_t *telnet, ...) {
- va_list va;
- va_start(va, telnet);
- telnet_send_vzmpv(telnet, va);
- va_end(va);
- }
- /* begin a ZMP command */
- void telnet_begin_zmp(telnet_t *telnet, const char *cmd) {
- telnet_begin_sb(telnet, TELNET_TELOPT_ZMP);
- telnet_zmp_arg(telnet, cmd);
- }
- /* send a ZMP argument */
- void telnet_zmp_arg(telnet_t *telnet, const char* arg) {
- telnet_send(telnet, arg, strlen(arg) + 1);
- }
|