+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
};
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;
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 {
$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 {
&$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();
{
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;
{
my $sendz = shift;
if ($conn && $sendz) {
- $conn->send_now("Z$call|bye...\n");
+ $conn->send_now("Z$call|bye...");
}
endwin();
dbgclose();
$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);
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;
{
fcb_t *f = sp->fcb;
cmsg_t *mp, *omp;
+ unsigned char c;
/* input modes */
if (in) {
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;