/* * Copyright (c) 2018 Lars Thrane A/S * SPDX-License-Identifier: GPL-2.0 * * Telnet RFC-2217 handling - server */ #include #include "lib/nvt_log.h" #include "telnet.h" #include "telnet_param.h" void telnet_rfc2217_init(tn_ctx_t *tcc, int when) { switch (when) { default: /* Ignore */ break; case TNI_INIT: /* Init context */ tcc->baudrate = 115200; tcc->datasize = 8; /* 8 bit */ tcc->parity = 1; /* None */ tcc->stopsize = 1; /* 1 bit */ tcc->linestate = 0; tcc->modemstate = 0; tcc->linestate_mask = 0; tcc->modemstate_mask = 0xff; tcc->dtr_state = 0; tcc->rts_state = 0; break; case TNI_ON: /* RFC-2217 activated */ tcc->mode_rfc2217 = 1; telnet_rfc2217_change_modemstate(tcc, tcc->modemstate); break; } } void telnet_rfc2217_handle_opt(tn_ctx_t *tcc, int opt, unsigned char *pu, int nd) { unsigned int sub, vali, valo, val4; sub = pu[0]; switch (sub) { default: // case 20-127: /* Available for Future Use */ nvt_log(LOG_WARNING, "Unhandled SB: Opt/prm = %d/%d", opt, sub); break; case 0: /* SIGNATURE */ telnet_reply_opt(tcc, opt, 100 + sub, "LT", 2); DBG("RFC2217: Set signature"); break; case 1: /* SET-BAUDRATE */ if (nd < 4) break; /* Incorrect */ vali = (pu[1] << 24) | (pu[2] << 16) | (pu[3] << 8) | pu[4]; if (vali != 0) /* Set value */ tcc->baudrate = vali; valo = tcc->baudrate; DBG("RFC2217: Set baud %u -> %u", vali, valo); val4 = htonl(valo); telnet_reply_opt(tcc, opt, 100 + sub, &val4, 4); break; case 2: /* SET-DATASIZE */ valo = vali = pu[1]; #if 0 /* Request only */ if (vali != 0) /* Set value */ tcc->datasize = vali; else /* Request value */ #endif valo = tcc->datasize; DBG("RFC2217: Set data %u -> %u", vali, valo); telnet_reply_opt(tcc, opt, 100 + sub, &valo, 1); break; case 3: /* SET-PARITY */ valo = vali = pu[1]; #if 0 /* Request only */ if (vali != 0) /* Set value */ tcc->parity = vali; else #endif valo = tcc->parity; DBG("RFC2217: Set parity %u -> %u", vali, valo); telnet_reply_opt(tcc, opt, 100 + sub, &valo, 1); break; case 4: /* SET-STOPSIZE */ valo = vali = pu[1]; #if 0 /* Request only */ if (vali != 0) /* Set value */ tcc->stopsize = vali; else #endif valo = tcc->stopsize; DBG("RFC2217: Set stop %u -> %u", vali, valo); telnet_reply_opt(tcc, opt, 100 + sub, &valo, 1); break; case 5: /* SET-CONTROL */ vali = pu[1]; valo = 0; switch (vali) { default: nvt_log(LOG_WARNING, "Unexpected SB: Opt/prm/val = %d/%d/%u", opt, sub, vali); break; case 0: /* Request Com Port Flow Control Setting (outbound/both) */ case 1: /* Use No Flow Control (outbound/both) */ valo = 1; /* No flow control */ goto do_ctl_reply; case 2: /* Use XON/XOFF Flow Control (outbound/both) */ valo = 1; /* No flow control */ nvt_log(LOG_WARNING, "RFC2217: Flow O %d -> %d", vali, valo); goto do_ctl_reply; case 3: /* Use HARDWARE Flow Control (outbound/both) */ valo = 3; /* HW flow control */ goto do_ctl_reply; case 4: /* Request BREAK State */ valo = 6; /* BREAK off */ nvt_log(LOG_WARNING, "RFC2217: Break st %d -> %d", vali, valo); goto do_ctl_reply; case 5: /* Set BREAK State ON */ case 6: /* Set BREAK State OFF */ valo = 6; /* BREAK off */ goto do_ctl_reply; case 7: /* Request DTR Signal State */ valo = (tcc->dtr_state) ? 8 : 9; goto do_ctl_reply; case 8: /* Set DTR Signal State ON */ case 9: /* Set DTR Signal State OFF */ valo = vali; tcc->dtr_state = (valo == 8) ? 1 : 0; nvt_log(LOG_INFO, "RFC2217: Set DTR %s", tcc->dtr_state ? "on" : "off"); goto do_ctl_reply; case 10: /* Request RTS Signal State */ valo = (tcc->rts_state) ? 11 : 12; goto do_ctl_reply; case 11: /* Set RTS Signal State ON */ case 12: /* Set RTS Signal State OFF */ valo = vali; tcc->rts_state = (valo == 11) ? 1 : 0; nvt_log(LOG_INFO, "RFC2217: Set RTS %s", tcc->rts_state ? "on" : "off"); goto do_ctl_reply; case 13: /* Request Com Port Flow Control Setting (inbound) */ case 14: /* Use No Flow Control (inbound) */ valo = 14; /* No flow control */ goto do_ctl_reply; case 16: /* Use HARDWARE Flow Control (inbound) */ valo = 16; /* HW flow control */ goto do_ctl_reply; case 15: /* Use XON/XOFF Flow Control (inbound) */ case 17: /* Use DCD Flow Control (outbound/both) */ case 18: /* Use DTR Flow Control (inbound) */ case 19: /* Use DSR Flow Control (outbound/both) */ valo = 14; /* No flow control */ nvt_log(LOG_WARNING, "RFC2217: Flow I %d -> %d", vali, valo); goto do_ctl_reply; do_ctl_reply: DBG("RFC2217: Set control %u -> %u", vali, valo); telnet_reply_opt(tcc, opt, 100 + sub, &valo, 1); switch (vali) { default: /* Do noting */ break; case 8: /* Set DTR Signal State ON */ case 9: /* Set DTR Signal State OFF */ if (tcc->cli_ss) tcc->cli_ss(tcc->cctx, TNS_DTR, tcc->dtr_state); break; case 11: /* Set RTS Signal State ON */ case 12: /* Set RTS Signal State OFF */ if (tcc->cli_ss) tcc->cli_ss(tcc->cctx, TNS_RTS, tcc->rts_state); break; } break; } break; #if 0 /* Server -> client only */ case 6: /* NOTIFY-LINESTATE */ case 7: /* NOTIFY-MODEMSTATE */ break; #endif case 8: /* FLOWCONTROL-SUSPEND */ case 9: /* FLOWCONTROL-RESUME */ nvt_log(LOG_WARNING, "RFC2217: Flowcontrol %s", (sub == 8) ? "suspend" : "resume"); /* NB! Do not reply */ break; case 10: /* SET-LINESTATE-MASK */ valo = vali = pu[1]; tcc->linestate_mask = vali; DBG("RFC2217: Set linestate mask 0x%02x", vali); telnet_reply_opt(tcc, opt, 100 + sub, &valo, 1); break; case 11: /* SET-MODEMSTATE-MASK */ valo = vali = pu[1]; tcc->modemstate_mask = vali; DBG("RFC2217: Set modemstate mask 0x%02x", vali); telnet_reply_opt(tcc, opt, 100 + sub, &valo, 1); break; case 12: /* PURGE-DATA */ valo = vali = pu[1]; DBG("RFC2217: Purge %d", vali); telnet_reply_opt(tcc, opt, 100 + sub, &valo, 1); break; } } void telnet_rfc2217_change_linestate(tn_ctx_t *tcc, int mask) { unsigned char buf[1]; if (!tcc->mode_rfc2217) goto done; buf[0] = mask & tcc->linestate_mask; buf[0] &= 0xf0; /* Drop change bits */ DBG("RFC2217: Notify linestate 0x%02x->0x%02x: 0x%02x", tcc->linestate, mask, buf[0]); telnet_reply_opt(tcc, TNO_CPCO, 100 + 6, buf, 1); done: tcc->linestate = mask; } void telnet_rfc2217_change_modemstate(tn_ctx_t *tcc, int mask) { unsigned char buf[1]; if (!tcc->mode_rfc2217) goto done; buf[0] = mask & tcc->modemstate_mask; DBG("RFC2217: Notify modemstate 0x%02x->0x%02x: 0x%02x", tcc->modemstate, mask, buf[0]); telnet_reply_opt(tcc, TNO_CPCO, 100 + 7, buf, 1); done: tcc->modemstate = mask; }