NetBSD/gnu/usr.bin/gdb/remote-ipkdb.c

773 lines
15 KiB
C
Raw Blame History

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

/* $NetBSD: remote-ipkdb.c,v 1.3 2000/08/07 15:20:36 ws Exp $ */
/*
* Part of this code is derived from software copyrighted by the
* Free Software Foundation.
*
* Modified 1993-2000 by Wolfgang Solfrank.
*/
/* Remote target communications for NetBSD targets via UDP (aka IPKDB).
Copyright (C) 1988-1991 Free Software Foundation, Inc.
This file is part of GDB.
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foudation; either version 2 of the License, or
(at your option) any later version.
This program is distributed in the hope that is will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */
/* Remote communication protocol.
Data is sent in binary.
The first byte is the command code.
Packets have a sequence number and the data length prepended
and a signature appended. The signature is computed according
to the HMAC algorithm (see draft-ietf-ipsec-hmac-md5-00.txt).
The answer has the same sequence number. On a timeout, packets
are rerequested by resending the request.
Request Packet
read registers R
reply pXX...X Register data is sent in target format.
emessage for an error.
write regs WXX...X Same as above.
reply ok for success.
emessage for an error.
read mem MAA...ALL...L AA...A is address, LL...L is length
(both as CORE_ADDR).
reply pXX...X XX...X is mem contents.
emessage for an error.
write mem NAA...ALL...LXX...X
AA...A is address, LL...L is length,
XX...X is data.
reply ok for success.
emessage for an error.
cont C
step S
open debug OII...IWW...W II...I is an arbitrary id of the session,
WW...W is the user and machine running gdb.
There is no immediate reply to step, cont or open.
The reply comes when the machines stops (again).
It is s
detach X
reply ok
*/
#include "defs.h"
#include "command.h"
#include "gdbcmd.h"
#include "inferior.h"
#include "target.h"
#include "valprint.h"
#include "wait.h"
#include <string.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <sys/time.h>
#include <netinet/in.h>
#include <netdb.h>
#include <md5.h>
static struct target_ops ipkdb_ops;
static int ipkdb_desc = -1;
static char host[512];
#define DBGPORT 1138
#define PBUFSIZ 400
static void remote_send();
static void putpkt();
static void getpkt();
static int startprot();
static void netread();
static void netwrite();
static void trykey();
/* Clean up connection to remote debugger. */
static void
ipkdb_close(quitting)
int quitting;
{
if (ipkdb_desc >= 0)
close(ipkdb_desc);
ipkdb_desc = -1;
}
/* Open a connection to a remote debugger.
The argument NAME, being the hostname of the target,
may optionally have the HMAC key for this appended. */
static void
ipkdb_open(name, from_tty)
char *name;
int from_tty;
{
struct hostent *he;
struct sockaddr_in sin;
if (name == 0)
error(
"To open a remote debug connection, you need to specify\n\
the name of the target machine.");
trykey(name, from_tty);
strcpy(host, name);
ipkdb_close(0);
if (!(he = gethostbyname(host))
|| he->h_addrtype != AF_INET)
error("host '%s' unknown\n", host);
target_preopen(from_tty);
push_target(&ipkdb_ops);
if ((ipkdb_desc = socket(AF_INET, SOCK_DGRAM, 0)) < 0)
perror_with_name(host);
sin.sin_len = sizeof sin;
sin.sin_family = AF_INET;
sin.sin_port = htons(DBGPORT);
sin.sin_addr = *(struct in_addr *)he->h_addr;
if (connect(ipkdb_desc, (struct sockaddr *)&sin, sizeof sin) < 0)
perror_with_name(host);
if (!catch_errors(startprot, NULL,
"Couldn't establish connection to remote target\n",
RETURN_MASK_ALL))
pop_target();
else if (from_tty)
printf_filtered("Remote debugging on %s\n", name);
}
/* Close the open connection to the remote debugger.
Use this when you want to detach and do something else
with your gdb. */
static void
ipkdb_detach(args, from_tty)
char *args;
int from_tty;
{
char buf[PBUFSIZ];
int l;
*buf = 'X';
l = 1;
remote_send(buf, &l);
pop_target();
if (from_tty)
puts_filtered("Ending remote debugging IPKDB.\n");
}
/* Tell the remote machine to resume. */
static void
ipkdb_resume(pid, step, ssignal)
int pid, step;
enum target_signal ssignal;
{
if (ssignal != TARGET_SIGNAL_0)
error("Can't send signals to a remote IPKDB system.");
putpkt(step ? "S" : "C", 1);
}
/* Wait until the remote machine stops, then return. */
static int
ipkdb_wait(pid, status)
int pid;
struct target_waitstatus *status;
{
unsigned char buf[PBUFSIZ];
int l;
getpkt(buf, &l);
if (l > 0 && buf[0] == 'e') {
buf[l] = 0;
error("Remote failure reply: '%s'", buf + 1);
}
if (buf[0] != 's')
error("Invalid remote reply: '%s'", buf);
status->kind = TARGET_WAITKIND_STOPPED;
status->value.sig = TARGET_SIGNAL_TRAP;
return 0;
}
/* Read the remote registers. */
static void
ipkdb_fetch_register(regno)
int regno;
{
char buf[PBUFSIZ];
int l;
*buf = 'R';
l = 1;
remote_send(buf, &l);
/* Reply describes registers byte by byte.
Suck them all up, and supply them to the
register cacheing/storage mechanism. */
for (l = 0; l < NUM_REGS; l++)
supply_register(l, &buf[1 + REGISTER_BYTE(l)]);
}
/* Prepare to store registers. Since we send them all, we have to
read out the ones we don't want to change first. */
static void
ipkdb_prepare_to_store()
{
ipkdb_fetch_register(-1);
}
/* Store the remote registers. */
static void
ipkdb_store_register(regno)
int regno;
{
char buf[PBUFSIZ];
int l;
buf[0] = 'W';
memcpy(buf + 1, registers, REGISTER_BYTES);
l = 1 + REGISTER_BYTES;
remote_send(buf, &l);
}
/* Put addr into buf */
static void
addrput(addr, buf)
CORE_ADDR addr;
char *buf;
{
int i;
buf += sizeof addr;
for (i = sizeof addr; --i >= 0; addr >>= 8)
*--buf = addr;
}
/* Write memory data directly to the remote machine. */
static void
ipkdb_write_bytes(memaddr, myaddr, len)
CORE_ADDR memaddr;
char *myaddr;
int len;
{
char buf[PBUFSIZ];
*buf = 'N';
addrput(memaddr, buf + 1);
addrput((CORE_ADDR)len, buf + 1 + sizeof(CORE_ADDR));
memcpy(buf + 1 + 2 * sizeof(CORE_ADDR), myaddr, len);
len += 1 + 2 * sizeof(CORE_ADDR);
remote_send(buf, &len);
}
/* Read memory data directly from the remote machine. */
static void
ipkdb_read_bytes(memaddr, myaddr, len)
CORE_ADDR memaddr;
char *myaddr;
int len;
{
char buf[PBUFSIZ];
int l;
char *p;
*buf = 'M';
addrput(memaddr, buf + 1);
addrput((CORE_ADDR)len, buf + 1 + sizeof(CORE_ADDR));
l = 1 + 2 * sizeof(CORE_ADDR);
remote_send(buf, &l);
if (l != len + 1)
error("ipkdb_read_bytes got wrong size of answer");
memcpy(myaddr, buf + 1, len);
}
/* Read or write LEN bytes from inferior memory at MEMADDR */
static int
ipkdb_xfer_memory(memaddr, myaddr, len, should_write, target)
CORE_ADDR memaddr;
char *myaddr;
int len;
int should_write;
struct target_ops *target;
{
int origlen = len;
int xfersize;
while (len > 0) {
xfersize = min(len, PBUFSIZ - 3 * sizeof(CORE_ADDR));
if (should_write)
ipkdb_write_bytes(memaddr, myaddr, xfersize);
else
ipkdb_read_bytes(memaddr, myaddr, xfersize);
memaddr += xfersize;
myaddr += xfersize;
len -= xfersize;
}
return origlen;
}
static void
ipkdb_files_info()
{
printf_unfiltered("\tAttached to IPKDB at %s.\n", host);
}
#ifdef BREAKPOINT
static char bpt[] = BREAKPOINT;
#endif
static int
ipkdb_insert_breakpoint(addr, save)
CORE_ADDR addr;
char *save;
{
#ifdef BREAKPOINT
ipkdb_read_bytes(addr, save, sizeof bpt);
ipkdb_write_bytes(addr, bpt, sizeof bpt);
return 0;
#else
error("Don't know how to insert breakpoint");
#endif
}
static int
ipkdb_remove_breakpoint(addr, save)
CORE_ADDR addr;
char *save;
{
#ifdef BREAKPOINT
ipkdb_write_bytes(addr, save, sizeof bpt);
return 0;
#else
error("Don't know how to remove breakpoint");
#endif
}
static void
ipkdb_kill()
{
}
/* Send the command in BUF to the remote machine,
and read the reply into BUF. */
static void
remote_send(buf, lp)
char *buf;
int *lp;
{
putpkt(buf, *lp);
getpkt(buf, lp);
if (buf[0] == 'e') {
buf[*lp] = 0;
error("Remote failure reply: '%s'", buf + 1);
}
}
/* HMAC Checksumming routines */
/*
* The following code is more or less stolen from the hmac_md5
* function in the Appendix of the HMAC IETF draft, but is
* optimized as suggested in this same paper.
*/
static char *key;
static MD5_CTX icontext, ocontext;
static void
hmac_init()
{
char pad[64];
char tk[16];
int key_len;
int i;
/* Require key to be at least 16 bytes long */
if (!key || (key_len = strlen(key)) < 16) {
if (key)
/* This doesn't make too much sense, but... */
memset(key, 0, key_len);
error("ipkdbkey must be at least 16 bytes long.");
}
/* if key is longer than 64 bytes reset it to key=MD5(key) */
if (key_len > 64) {
MD5Init(&icontext);
MD5Update(&icontext, key, key_len);
MD5Final(tk, &icontext);
/* This doesn't make too much sense, but... */
memset(key, 0, key_len);
key = tk;
key_len = 16;
}
/*
* the HMAC_MD5 transform looks like:
*
* MD5(K XOR opad, MD5(K XOR ipad, text))
*
* where K is and n byte key
* ipad is the byte 0x36 repeated 64 times
* opad is the byte 0x5c repeated 64 times
* and text is the data being protected
*/
/*
* We do the initial part of MD5(K XOR ipad)
* and MD5(K XOR opad) here, in order to
* speed up the computation later on.
*/
memset(pad, 0, sizeof pad);
memcpy(pad, key, key_len);
for (i = 0; i < 64; i++)
pad[i] ^= 0x36;
MD5Init(&icontext);
MD5Update(&icontext, pad, 64);
memset(pad, 0, sizeof pad);
memcpy(pad, key, key_len);
for (i = 0; i < 64; i++)
pad[i] ^= 0x5c;
MD5Init(&ocontext);
MD5Update(&ocontext, pad, 64);
/*
* Zero out the key.
* This doesn't make too much sense, but...
*/
memset(key, 0, key_len);
}
/*
* This is more or less hmac_md5 from the HMAC IETF draft, Appendix.
*/
static void *
chksum(buf, len)
void *buf;
int len;
{
static u_char digest[16];
struct MD5Context context;
/*
* the HMAC_MD5 transform looks like:
*
* MD5(K XOR opad, MD5(K XOR ipad, text))
*
* where K is an n byte key
* ipad is the byte 0x36 repeated 64 times
* opad is the byte 0x5c repeated 64 times
* and text is the data being protected
*/
/*
* Since we've already done the precomputation,
* we can now stuff the data into the relevant
* preinitialized contexts to get the result.
*/
/*
* perform inner MD5
*/
memcpy(&context, &icontext, sizeof context);
MD5Update(&context, buf, len);
MD5Final(digest, &context);
/*
* perform outer MD5
*/
memcpy(&context, &ocontext, sizeof context);
MD5Update(&context, digest, 16);
MD5Final(digest, &context);
return digest;
}
static struct cmd_list_element *setkeycmd;
static void
trykey(name, from_tty)
char *name;
int from_tty;
{
char *k;
for (k = name; *k && !isspace(*k); k++);
if (*k)
*k++ = 0;
for (; *k && isspace(*k); k++);
if (*k)
do_setshow_command(k, from_tty, setkeycmd);
else if (!key)
error("No valid ipkdbkey yet");
}
/* net I/O routines */
static long netseq;
static long
getnl(p)
void *p;
{
u_char *s = p;
return (s[0] << 24)|(s[1] << 16)|(s[2] << 8)|s[3];
}
static int
getns(p)
void *p;
{
u_char *s = p;
return (s[0] << 8)|s[1];
}
static void
setnl(p, l)
void *p;
long l;
{
u_char *s = p;
*s++ = l >> 24;
*s++ = l >> 16;
*s++ = l >> 8;
*s = l;
}
static void
setns(p, l)
void *p;
int l;
{
u_char *s = p;
*s++ = l >> 8;
*s = l;
}
static int
startprot(dummy)
char *dummy;
{
char buf[PBUFSIZ];
char myname[128];
netseq = 0;
gethostname(myname, sizeof myname);
buf[0] = 'O';
setnl(buf + 1, time(NULL));
sprintf(buf + 5, "%s@%s", getlogin(), myname);
putpkt(buf, 5 + strlen(buf + 5));
/* Don't print character strings by default. This might break the target. */
print_max = 0;
start_remote();
return 1;
}
static char ibuf[512], obuf[512];
static int olen;
static void
putpkt(buf, len)
char *buf;
int len;
{
if (len > sizeof obuf - 10)
error("putpkt: packet too large");
setnl(obuf, netseq);
setns(obuf + 4, len);
memcpy(obuf + 6, buf, len + 1);
memcpy(obuf + len + 6, chksum(obuf, len + 6), 16);
olen = len + 6 + 16;
if (write(ipkdb_desc, obuf, olen) < 0
&& (errno != EINTR
#ifdef ECONNREFUSED
&& errno != ECONNREFUSED
#endif
#ifdef EHOSTDOWN
&& errno != EHOSTDOWN
#endif
))
fatal("putpkt: write");
}
static void
getpkt(buf, lp)
char *buf;
int *lp;
{
int len;
struct timeval tv, deadline;
fd_set rd;
gettimeofday(&deadline, NULL);
deadline.tv_sec++;
while (1) {
notice_quit();
if (quit_flag)
quit();
FD_ZERO(&rd);
FD_SET(ipkdb_desc, &rd);
gettimeofday(&tv, NULL);
tv.tv_sec = deadline.tv_sec - tv.tv_sec;
tv.tv_usec = deadline.tv_usec - tv.tv_usec;
if (tv.tv_usec < 0) {
tv.tv_usec += 1000000;
tv.tv_sec--;
}
if (tv.tv_sec < 0)
tv.tv_sec = tv.tv_usec = 0;
switch (select(ipkdb_desc + 1, &rd, NULL, NULL, &tv)) {
case -1:
if (errno == EINTR
#ifdef ECONNREFUSED
|| errno == ECONNREFUSED /* Solaris lossage??? */
#endif
)
continue;
fatal("getpkt: select");
default:
if (read(ipkdb_desc, ibuf, sizeof ibuf) < 0) {
if (errno == EINTR
#ifdef ECONNREFUSED
|| errno == ECONNREFUSED /* Solaris lossage??? */
#endif
)
continue;
fatal("getpkt: recvfrom");
}
if (getnl(ibuf) != netseq)
break;
len = getns(ibuf + 4);
if (!memcmp(chksum(ibuf, len + 6), ibuf + 6 + len, 16)) {
netseq++;
memcpy(buf, ibuf + 6, len);
*lp = len;
return;
}
/*
* Packet had correct sequence number, but
* wrong checksum, do an immediate retry, so...
*/
/* FALLTHROUGH */
case 0:
if (write(ipkdb_desc, obuf, olen) < 0) {
if (errno == EINTR
#ifdef ECONNREFUSED
|| errno == ECONNREFUSED /* Solaris lossage??? */
#endif
)
continue;
fatal("getpkt: write");
}
gettimeofday(&deadline, NULL);
deadline.tv_sec++;
break;
}
}
}
/* Defint the target subroutine names */
static struct target_ops ipkdb_ops = {
"ipkdb",
"Remote IPKDB target via UDP/IP",
"Debug a remote computer via UDP/IP.\n\"
Specify the name of the target machine",
ipkdb_open,
ipkdb_close,
0, /* attach */
ipkdb_detach,
ipkdb_resume,
ipkdb_wait,
ipkdb_fetch_register,
ipkdb_store_register,
ipkdb_prepare_to_store,
ipkdb_xfer_memory,
ipkdb_files_info,
ipkdb_insert_breakpoint,
ipkdb_remove_breakpoint,
0, /* terminal_init */
0, /* terminal_inferior */
0, /* terminal_ours_for_output */
0, /* terminal_ours */
0, /* terminal_info */
ipkdb_kill,
0, /* load */
0, /* lookup_symbol */
0, /* create_inferior */
0, /* mourn_inferior */
0, /* can_run */
0, /* notice_signals */
0, /* thread_alive */
0, /* stop */
process_stratum, /* stratum */
0, /* next (unused) */
1, /* has_all_memory */
1, /* has_memory */
1, /* has_stack */
1, /* has_registers */
1, /* has_execution */
0, /* sections */
0, /* sections_end */
OPS_MAGIC /* Always the last thing */
};
void
_initialize_ipkdb()
{
add_target(&ipkdb_ops);
setkeycmd = add_set_cmd("ipkdbkey", class_support, var_string,
(char *)&key,
"Set the key to be used for authentication of IPKDB debugging.\n\
The key given must be at least 16 bytes in length.",
&setlist);
setkeycmd->function.sfunc = hmac_init;
}