@@ -54,6 +54,8 @@ constexpr bool isEspurnaMinimal() {
54
54
55
55
namespace build {
56
56
57
+ constexpr size_t LineBufferSize { TELNET_LINE_BUFFER_SIZE };
58
+
57
59
constexpr size_t ClientsMax { TELNET_MAX_CLIENTS };
58
60
static_assert (ClientsMax > 0 , " " );
59
61
@@ -269,6 +271,7 @@ namespace message {
269
271
270
272
PROGMEM_STRING (PasswordRequest, " Password (disconnects after 1 failed attempt): " );
271
273
PROGMEM_STRING (InvalidPassword, " -ERROR: Invalid password\n " );
274
+ PROGMEM_STRING (BufferOverflow, " -ERROR: Buffer overflow\n " );
272
275
PROGMEM_STRING (OkPassword, " +OK\n " );
273
276
274
277
} // namespace message
@@ -312,6 +315,11 @@ struct Address {
312
315
uint16_t port;
313
316
};
314
317
318
+ ::terminal::LineView line_view (pbuf* pb) {
319
+ auto * payload = reinterpret_cast <const char *>(pb->payload );
320
+ return StringView{payload, payload + pb->len };
321
+ }
322
+
315
323
Address address (tcp_pcb* pcb) {
316
324
Address out;
317
325
ip_addr_copy (out.ip , pcb->remote_ip );
@@ -530,46 +538,100 @@ class Client {
530
538
}
531
539
#endif
532
540
541
+ struct ProcessLineResult {
542
+ StringView message;
543
+ bool close { false };
544
+ };
545
+
546
+ auto process_line (StringView line) -> ProcessLineResult {
547
+ ProcessLineResult out;
548
+ if (!line.length ()) {
549
+ return out;
550
+ }
551
+
552
+ switch (_state) {
553
+ case State::Idle:
554
+ case State::Connecting:
555
+ break ;
556
+ case State::Active:
557
+ #if TERMINAL_SUPPORT
558
+ process (line.toString ());
559
+ #endif
560
+ break ;
561
+ case State::Authenticating:
562
+ if (!systemPasswordEquals (stripNewline (line))) {
563
+ out.message = StringView{message::InvalidPassword};
564
+ out.close = true ;
565
+ return out;
566
+ }
567
+
568
+ out.message = StringView{message::OkPassword};
569
+ _state = State::Active;
570
+ break ;
571
+ }
572
+
573
+ return out;
574
+ }
575
+
533
576
err_t on_tcp_recv (pbuf* pb, err_t err) {
534
577
if (!pb || (err != ERR_OK)) {
535
578
return close ();
536
579
}
537
580
538
- const auto * payload = reinterpret_cast < const char *>(pb-> payload );
539
- espurna::terminal::LineView lines ({payload, payload + pb-> len });
540
-
541
- while (lines ) {
542
- const auto line = lines. line ( );
543
- if (!line. length ()) {
544
- break ;
581
+ // We always attempt to parse the network buffer directly first,
582
+ // socat, netcat, etc. usually send a single packet per line.
583
+ // Otherwise, try to buffer it and everything else in the chain.
584
+ for ( auto it = pb; it != nullptr ; it = it-> next ) {
585
+ auto view = line_view (it );
586
+ if (_line_buffer. size ()) {
587
+ goto next ;
545
588
}
546
589
547
- switch (_state) {
548
- case State::Idle:
549
- case State::Connecting:
550
- break ;
551
- case State::Active:
552
- #if TERMINAL_SUPPORT
553
- process (String (line));
554
- #endif
555
- break ;
556
- case State::Authenticating:
557
- if (!systemPasswordEquals (stripNewline (line))) {
558
- write_message (message::InvalidPassword);
590
+ for (auto line = view.line (); line.length () > 0 ; line = view.line ()) {
591
+ auto result = process_line (line);
592
+ if (result.message .length ()) {
593
+ write_message (result.message );
594
+ }
595
+
596
+ if (result.close ) {
559
597
return close ();
560
598
}
599
+ }
561
600
562
- write_message (message::OkPassword);
601
+ next:
602
+ if (view.length ()) {
603
+ _line_buffer.append (view.get ());
604
+ }
605
+ }
563
606
564
- _state = State::Active;
607
+ if (_line_buffer.overflow ()) {
608
+ write_message (message::BufferOverflow);
609
+ return close ();
610
+ }
611
+
612
+ for (;;) {
613
+ const auto line_result = _line_buffer.line ();
614
+ if (line_result.overflow ) {
615
+ write_message (message::BufferOverflow);
616
+ return close ();
617
+ }
618
+
619
+ if (!line_result.line .length ()) {
565
620
break ;
566
621
}
622
+
623
+ auto result = process_line (line_result.line );
624
+ if (result.message .length ()) {
625
+ write_message (result.message );
626
+ }
627
+
628
+ if (result.close ) {
629
+ return close ();
630
+ }
567
631
}
568
632
569
- // Right now, only accept simple payloads that are limited by TCP_MSS
570
- // In case there are more than one `pbuf` chained together, we discrard
571
- // everything else and only use the first available one
572
- // (and, only if it contains line breaks; everything else is lost)
633
+ // expect everything to be handled above, we don't allow lingering pbufs
634
+ // (as extra buffers, for retries, or anything else)
573
635
tcp_recved (_pcb, pb->tot_len );
574
636
pbuf_free (pb);
575
637
@@ -608,6 +670,7 @@ class Client {
608
670
bool _request_auth { false };
609
671
610
672
#if TERMINAL_SUPPORT
673
+ ::terminal::LineBuffer<build::LineBufferSize> _line_buffer;
611
674
std::list<String> _cmds;
612
675
#endif
613
676
ClientWriter _writer;
0 commit comments