/* * Process an Urgent Data indication. */ static void telnet_exception(ndesc_t *nd, telnet_t *tp) { tp->t_flags |= TF_SYNCH | TF_URGENT; nq_init(tp->t_canq); nd_disable(nd, ND_X); }
/* * Send the contents of the canonical queue to the interactive object. */ static void telnet_eol(telnet_t *tp) { char *cp; if (tp->t_flags & TF_SYNCH) return; if (nq_full(tp->t_canq)) { tp->t_flags &= ~TF_OVFLCANQ; cp = ""; } else { nq_putc(tp->t_canq, '\0'); cp = (char *)nq_rptr(tp->t_canq); } if (tp->t_flags & TF_ATTACH) interactive_input(tp->t_ip, cp); nq_init(tp->t_canq); tp->t_flags &= ~TF_GA; }
/* * Process an Erase Line. */ static void telnet_el(telnet_t *tp) { if (tp->t_flags & (TF_OVFLCANQ | TF_SYNCH)) return; nq_init(tp->t_canq); }
static void telnet_interactive(void *vp) { telnet_t *tp = vp; char *cp; if (!(tp->t_flags & TF_ATTACH)) { tp->task = NULL; telnet_shutdown(tp->t_nd, tp); return; } if (tp->t_flags & TF_DISCONNECT) { tp->t_flags &= ~TF_DISCONNECT; if (tp->t_ip) remove_interactive(tp->t_ip, 1); } if (!(tp->t_flags & TF_ATTACH)) { tp->task = NULL; telnet_shutdown(tp->t_nd, tp); return; } if (tp->t_flags & TF_OVFLOUTQ) { tp->task = NULL; return; } if (tp->t_flags & TF_INPUT) { tp->t_flags &= ~TF_INPUT; if (nq_full(tp->t_canq)) cp = ""; else cp = (char *)nq_rptr(tp->t_canq); interactive_input(tp->t_ip, cp); nq_init(tp->t_canq); } if (!(tp->t_flags & TF_ATTACH)) { tp->task = NULL; telnet_shutdown(tp->t_nd, tp); return; } tp->t_flags &= ~TF_GA; telnet_readbytes(tp->t_nd, tp); telnet_input(tp); if (!(tp->t_flags & TF_ATTACH)) { tp->task = NULL; telnet_shutdown(tp->t_nd, tp); return; } if (tp->t_flags & (TF_INPUT|TF_DISCONNECT)) { reschedule_task(tp->task); return; } tp->task = NULL; nd_enable(tp->t_nd, ND_R); }
/* * 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); }
/* * 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); }
/* * Process a Subnegotiate End. */ static void telnet_se(telnet_t *tp) { tp->t_flags &= ~TF_OVFLOPTQ; nq_init(tp->t_optq); }
/* * 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); }