/* * Write data from the output queue to the Telnet session. */ static void telnet_write(ndesc_t *nd, telnet_t *tp) { if (!nq_empty(tp->t_outq)) { if (nq_send(tp->t_outq, nd_fd(nd), &tp->t_sblen) == -1) { switch (errno) { case EWOULDBLOCK: case EINTR: case EPROTO: break; default: telnet_disconnect(tp); return; } } } if (tp->t_flags & TF_OVFLOUTQ) { if (nq_len(tp->t_outq) < TELNET_OUTQ_LOWAT) { tp->t_flags &= ~TF_OVFLOUTQ; nq_puts(tp->t_outq, (u_char *)"*** Truncated. ***\r\n"); } } if (tp->t_flags & TF_FLOWC) { if (nq_len(tp->t_outq) < TELNET_OUTQ_HIWAT) { tp->t_flags &= ~TF_FLOWC; nd_enable(nd, ND_C); } } if (!nq_empty(tp->t_outq)) return; nq_init(tp->t_outq); tp->t_flags |= TF_ENABW; nd_disable(nd, ND_W); }
/* * Process an Erase Character/Backspace/Delete. */ static INLINE void telnet_ec(telnet_t *tp) { if (tp->t_flags & (TF_OVFLCANQ | TF_SYNCH)) return; if (!nq_empty(tp->t_canq)) nq_unputc(tp->t_canq); }
/* * Write data from the output queue to the Telnet session. */ static void telnet_write(ndesc_t *nd, telnet_t *tp) { if (!nq_empty(tp->t_outq)) { if (nq_send(tp->t_outq, nd_fd(nd), &tp->t_sblen) == -1) { switch (errno) { case EWOULDBLOCK: case EINTR: case EPROTO: break; default: telnet_disconnect(tp); return; } } } if (tp->t_flags & TF_OVFLOUTQ) { if (nq_len(tp->t_outq) < TELNET_OUTQ_LOWAT) { tp->t_flags &= ~TF_OVFLOUTQ; if (tp->t_flags & (TF_INPUT|TF_DISCONNECT) && !tp->task) /* Reenable command processing */ tp->task = create_task(telnet_interactive, tp); } } if (!nq_empty(tp->t_outq)) return; nq_init(tp->t_outq); nd_disable(nd, ND_W); }
/* * Flush the output queue. */ static void telnet_flush(telnet_t *tp) { if (!nq_empty(tp->t_outq)) nq_send(tp->t_outq, nd_fd(tp->t_nd), &tp->t_sblen); }
/* * Perform Telnet protocol processing, copying the contents of the raw input * queue to the canonical queue. */ static void telnet_input(telnet_t *tp) { u_char c; while (!nq_empty(tp->t_rawq)) { c = nq_getc(tp->t_rawq); switch (tp->t_state) { case TS_DATA: switch (c) { case NUL: break; case BS: telnet_ec(tp); break; case LF: telnet_eol(tp); return; case CR: tp->t_state = TS_CR; break; case DEL: telnet_ec(tp); break; case IAC: tp->t_state = TS_IAC; break; default: telnet_canq_putc(tp, c); break; } break; case TS_CR: telnet_eol(tp); tp->t_state = TS_DATA; return; case TS_IAC: tp->t_state = TS_DATA; switch (c) { case DM: telnet_dm(tp); break; case AYT: telnet_ayt(tp); break; case EC: telnet_ec(tp); break; case EL: telnet_el(tp); break; case SB: tp->t_state = TS_IAC_SB; break; case WILL: tp->t_state = TS_IAC_WILL; break; case WONT: tp->t_state = TS_IAC_WONT; break; case DO: tp->t_state = TS_IAC_DO; break; case DONT: tp->t_state = TS_IAC_DONT; break; case IAC: telnet_canq_putc(tp, c); break; } break; case TS_IAC_SB: telnet_sb(tp, c); tp->t_state = TS_IAC_SB_DATA; break; case TS_IAC_SB_DATA: switch (c) { case IAC: tp->t_state = TS_IAC_SB_IAC; break; default: telnet_optq_putc(tp, c); break; } break; case TS_IAC_SB_IAC: tp->t_state = TS_IAC_SB_DATA; switch (c) { case SE: telnet_se(tp); tp->t_state = TS_DATA; break; case IAC: telnet_optq_putc(tp, c); break; } break; case TS_IAC_WILL: telnet_will(tp, c); tp->t_state = TS_DATA; break; case TS_IAC_WONT: telnet_wont(tp, c); tp->t_state = TS_DATA; break; case TS_IAC_DO: telnet_do(tp, c); tp->t_state = TS_DATA; break; case TS_IAC_DONT: telnet_dont(tp, c); tp->t_state = TS_DATA; break; } } if ((tp->t_flags & (TF_SGA | TF_GA)) == 0) { if (nq_avail(tp->t_outq) >= 2) { tp->t_flags |= TF_GA; telnet_send_ga(tp); } } nq_init(tp->t_rawq); }