From 5947a205b3f36462fc1fe5ed5a08c7d8293ab744 Mon Sep 17 00:00:00 2001 From: djk Date: Fri, 5 May 2000 17:00:22 +0000 Subject: [PATCH] rewrote parts of Msg.pm and client.c so that the messages no longer use a length word at the front of each one. They are simply strings of characters separated by a \n. No binary characters are allowed (they are encoded as %nn) a la HTTP. This hopefully cures Arnold's problem and also make it more secure anyway. It also paves the way for the ax25/ip multicast client. --- Changes | 6 +++ perl/Msg.pm | 103 +++++++++++++++++++----------------------------- perl/client.pl | 2 +- perl/console.pl | 8 ++-- src/client.c | 88 ++++++++++++++++++++++++++++++----------- 5 files changed, 117 insertions(+), 90 deletions(-) diff --git a/Changes b/Changes index c56dfd4c..acc45f67 100644 --- a/Changes +++ b/Changes @@ -1,3 +1,9 @@ +05May00======================================================================= +1. rewrote parts of Msg.pm and client.c so that the messages no longer use +a length word at the front of each one. They are simply strings of characters +separated by a \n. No binary characters are allowed (they are encoded as %nn) +a la HTTP. This hopefully cures Arnold's problem and also make it more +secure anyway. It also paves the way for the ax25/ip multicast client. 30Apr00======================================================================= 1. put some extra checks and balances in to message send routine in Msg.pm to see if I can prevent the error seen by Arnold (which I think is only likely to diff --git a/perl/Msg.pm b/perl/Msg.pm index 33a0af80..d067f27e 100644 --- a/perl/Msg.pm +++ b/perl/Msg.pm @@ -56,7 +56,7 @@ sub connect { }; if ($rcvd_notification_proc) { - my $callback = sub {_rcv($conn, 0)}; + my $callback = sub {_rcv($conn)}; set_event_handler ($sock, "read" => $callback); } return bless $conn, $pkg; @@ -89,8 +89,8 @@ sub _enqueue { my ($conn, $msg) = @_; # prepend length (encoded as network long) my $len = length($msg); - $msg = pack ('N', $len) . $msg; - push (@{$conn->{queue}}, $msg); + $msg =~ s/(\x00-\x2f\x7e-\xff%])/sprintf("%%%02X", ord($1))/eg; + push (@{$conn->{queue}}, $msg . "\n"); } sub _send { @@ -198,71 +198,50 @@ sub new_server { $g_login_proc = $login_proc; $g_pkg = $pkg; } -sub rcv_now { - my ($conn) = @_; - my ($msg, $err) = _rcv ($conn, 1); # 1 ==> rcv now - return wantarray ? ($msg, $err) : $msg; -} - sub _rcv { # Complement to _send - my ($conn, $rcv_now) = @_; # $rcv_now complement of $flush + my $conn = shift; # $rcv_now complement of $flush # Find out how much has already been received, if at all my ($msg, $offset, $bytes_to_read, $bytes_read); my $sock = $conn->{sock}; return unless defined($sock); - if (exists $conn->{msg}) { - $msg = $conn->{msg}; - $offset = length($msg) - 1; # sysread appends to it. - $bytes_to_read = $conn->{bytes_to_read}; - delete $conn->{'msg'}; # have made a copy - } else { - # The typical case ... - $msg = ""; # Otherwise -w complains - $offset = 0 ; - $bytes_to_read = 0 ; # Will get set soon - } - # We want to read the message length in blocking mode. Quite - # unlikely that we'll get blocked too long reading 4 bytes - if (!$bytes_to_read) { # Get new length - my $buf; - $conn->set_blocking(); - $bytes_read = sysread($sock, $buf, 4); - if ($! || ($bytes_read != 4)) { - goto FINISH; - } - $bytes_to_read = unpack ('N', $buf); - } - $conn->set_non_blocking() unless $rcv_now; - while ($bytes_to_read) { - $bytes_read = sysread ($sock, $msg, $bytes_to_read, $offset); - if (defined ($bytes_read)) { - if ($bytes_read == 0) { - last; - } - $bytes_to_read -= $bytes_read; - $offset += $bytes_read; - } else { - if (_err_will_block($!)) { - # Should come here only in non-blocking mode - $conn->{msg} = $msg; - $conn->{bytes_to_read} = $bytes_to_read; - return ; # .. _rcv will be called later - # when socket is readable again - } else { - last; - } - } - } - FINISH: - if (length($msg) == 0) { - $conn->disconnect(); - } - if ($rcv_now) { - return ($msg, $!); - } else { - &{$conn->{rcvd_notification_proc}}($conn, $msg, $!); + my @lines; + $conn->set_non_blocking(); + $bytes_read = sysread ($sock, $msg, 1024, 0); + if (defined ($bytes_read)) { + if ($bytes_read > 0) { + if ($msg =~ /\n/) { + @lines = split /\n/, $msg; + $lines[0] = $conn->{msg} . $lines[0] if $conn->{msg}; + if ($msg =~ /\n$/) { + delete $conn->{msg}; + } else { + $conn->{msg} = pop @lines; + } + } else { + $conn->{msg} .= $msg; + } + } + } else { + if (_err_will_block($!)) { + return ; + } else { + $bytes_read = 0; + } } + +FINISH: + if (defined $bytes_read == 0) { + $conn->disconnect(); + &{$conn->{rcvd_notification_proc}}($conn, undef, $!); + } + + while (@lines){ + $msg = shift @lines; + $msg =~ s/%([0-9A-Fa-f]{2})/chr(hex($1))/eg; + &{$conn->{rcvd_notification_proc}}($conn, $msg, $!); + $! = 0; + } } sub _new_client { @@ -275,7 +254,7 @@ sub _new_client { &$g_login_proc ($conn, $sock->peerhost(), $sock->peerport()); if ($rcvd_notification_proc) { $conn->{rcvd_notification_proc} = $rcvd_notification_proc; - my $callback = sub {_rcv($conn,0)}; + my $callback = sub {_rcv($conn)}; set_event_handler ($sock, "read" => $callback); } else { # Login failed $conn->disconnect(); diff --git a/perl/client.pl b/perl/client.pl index bc2d46c7..1935b892 100755 --- a/perl/client.pl +++ b/perl/client.pl @@ -53,7 +53,7 @@ sub cease { my $sendz = shift; if ($conn && $sendz) { - $conn->send_now("Z$call|bye...\n"); + $conn->send_now("Z$call|bye..."); sleep(1); } $stdout->flush if $stdout; diff --git a/perl/console.pl b/perl/console.pl index e6f96aad..d5224cf9 100755 --- a/perl/console.pl +++ b/perl/console.pl @@ -104,7 +104,7 @@ sub cease { my $sendz = shift; if ($conn && $sendz) { - $conn->send_now("Z$call|bye...\n"); + $conn->send_now("Z$call|bye..."); } endwin(); dbgclose(); @@ -438,9 +438,9 @@ do_initscr(); $SIG{__DIE__} = \&sig_term; -$conn->send_now("A$call|$connsort"); -$conn->send_now("I$call|set/page $maxshist"); -$conn->send_now("I$call|set/nobeep"); +$conn->send_later("A$call|$connsort"); +$conn->send_later("I$call|set/page $maxshist"); +$conn->send_later("I$call|set/nobeep"); Msg->set_event_handler(\*STDIN, "read" => \&rec_stdin); diff --git a/src/client.c b/src/client.c index 8c52d53f..22e06f69 100644 --- a/src/client.c +++ b/src/client.c @@ -237,24 +237,35 @@ void send_text(fcb_t *f, char *s, int l) flush_text(f); } -void send_msg(fcb_t *f, char let, char *s, int l) +void send_msg(fcb_t *f, char let, unsigned char *s, int l) { cmsg_t *mp; int ln; int myl = strlen(call)+2+l; mp = cmsg_new(myl+4+1, f->sort, f); - ln = htonl(myl); - memcpy(mp->inp, &ln, 4); - mp->inp += 4; *mp->inp++ = let; strcpy(mp->inp, call); mp->inp += strlen(call); *mp->inp++ = '|'; if (l > 0) { - memcpy(mp->inp, s, l); - mp->inp += l; + unsigned char *p; + for (p = s; p < s+l; ++p) { + if (mp->inp >= mp->data + (myl - 4)) { + int off = mp->inp - mp->data; + myl += 256; + mp = realloc(mp, myl); + mp->inp = mp->data + off; + } + + if (*p < 0x20 || *p > 0x7e || *p == '%') { + sprintf(mp->inp, "%%%02X", *p & 0xff); + mp->inp += strlen(mp->inp); + } else + *mp->inp++ = *p; + } } + *mp->inp++ = '\n'; *mp->inp = 0; cmsg_send(f->outq, mp, 0); f->sp->flags |= SEL_OUTPUT; @@ -268,6 +279,7 @@ int fcb_handler(sel_t *sp, int in, int out, int err) { fcb_t *f = sp->fcb; cmsg_t *mp, *omp; + unsigned char c; /* input modes */ if (in) { @@ -372,30 +384,60 @@ int fcb_handler(sel_t *sp, int in, int out, int err) case MSG: p = buf; while (r > 0 && p < &buf[r]) { + unsigned char ch = *p++; + + if (mp->inp >= mp->data + (MAXBUFL-1)) { + mp->state = 0; + mp->inp = mp->data; + dbg(DMSG, "Message longer than %d received", MAXBUFL); + } - /* build up the size into the likely message length (yes I know it's a short) */ switch (mp->state) { - case 0: - case 1: - mp->state++; - break; - case 2: - case 3: - mp->size = (mp->size << 8) | (*p++ & 0xff); - if (mp->size > MAXBUFL) - die("Message size too big from node (%d > %d)", mp->size, MAXBUFL); - mp->state++; - break; - default: - if (mp->inp - mp->data < mp->size) { - *mp->inp++ = *p++; - } - if (mp->inp - mp->data >= mp->size) { + case 0: + if (ch == '%') { + c = 0; + mp->state = 1; + } else if (ch == '\n') { /* kick it upstairs */ + *mp->inp = 0; dbgdump(DMSG, "QUEUE MSG", mp->data, mp->inp - mp->data); cmsg_send(f->inq, mp, 0); mp = f->in = cmsg_new(MAXBUFL+1, f->sort, f); + } else if (ch < 0x20 || ch > 0x7e) { + dbg(DMSG, "Illegal character (0x%02X) received", *p); + mp->inp = mp->data; + } else { + *mp->inp++ = ch; + } + break; + + case 1: + mp->state = 2; + if (ch >= '0' && ch <= '9') + c = (ch - '0') << 4; + else if (ch >= 'A' && ch <= 'F') + c = (ch - 'A' + 10) << 4; + else if (ch >= 'a' && ch <= 'a') + c = (ch - 'a' + 10) << 4; + else { + dbg(DMSG, "Illegal hex char (%c) received in state %d", ch, mp->state); + mp->inp = mp->data; + mp->state = 0; + } + break; + + case 2: + if (ch >= '0' && ch <= '9') + *mp->inp++ = c | (ch - '0'); + else if (ch >= 'A' && ch <= 'F') + *mp->inp++ = c | (ch - 'A' + 10); + else if (ch >= 'a' && ch <= 'a') + *mp->inp++ = c | (ch - 'a' + 10); + else { + dbg(DMSG, "Illegal hex char (%c) received in state %d", ch, mp->state); + mp->inp = mp->data; } + mp->state = 0; } } break; -- 2.34.1