NetBSD/sys/ipkdb/ipkdb_ipkdb.c

987 lines
23 KiB
C

/*
* Copyright (C) 1993-1996 Wolfgang Solfrank.
* Copyright (C) 1993-1996 TooLs GmbH.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. All advertising materials mentioning features or use of this software
* must display the following acknowledgement:
* This product includes software developed by TooLs GmbH.
* 4. The name of TooLs GmbH may not be used to endorse or promote products
* derived from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY TOOLS GMBH ``AS IS'' AND ANY EXPRESS OR
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
* IN NO EVENT SHALL TOOLS GMBH BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
* OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
* WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
* OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
* ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#include <sys/param.h>
#include <sys/socket.h>
#include <sys/mbuf.h>
#include <sys/reboot.h>
#include <sys/systm.h>
#include <net/if.h>
#include <net/if_arp.h>
#include <net/if_ether.h>
#include <netinet/in.h>
#include <netinet/in_systm.h>
#include <netinet/if_inarp.h>
#include <netinet/in.h>
#include <netinet/ip.h>
#include <netinet/ip_var.h>
#include <netinet/udp.h>
#include <machine/stdarg.h>
#include <machine/cpu.h>
#include <machine/psl.h>
#include <machine/reg.h>
#include <ipkdb/ipkdb.h>
#include "debuggers.h"
#include <machine/ipkdb.h>
int ipkdbpanic = 0;
static struct ipkdb_if ipkdb_if;
#ifdef IPKDBTEST
static struct ipkdb_if new_if;
static int ipkdb_test = 0;
#endif
static u_char *ipkdbaddr __P((u_char *, int *, void **));
static void peekmem __P((struct ipkdb_if *, u_char *, void *, long));
static void pokemem __P((struct ipkdb_if *, u_char *, void *, long));
static u_int32_t getnl __P((void *));
static u_int getns __P((void *));
static void setnl __P((void *, u_int32_t));
static void setns __P((void *, int));
static u_short cksum __P((u_short, void *, int));
static int assemble __P((struct ipkdb_if *, void *));
static char *inpkt __P((struct ipkdb_if *, char *, int));
static void outpkt __P((struct ipkdb_if *, char *, int, int, int));
static void init __P((struct ipkdb_if *));
static void *chksum __P((void *, int));
static void getpkt __P((struct ipkdb_if *, char *, int *));
static void putpkt __P((struct ipkdb_if *, char *, int));
static int maskcmp __P((void *, void *, void *));
static int check_ipkdb __P((struct ipkdb_if *, struct in_addr *, u_short, u_short, char *, int));
static int connectipkdb __P((struct ipkdb_if *, char *, int));
#ifdef IPKDBKEY
static int hmac_init __P((void));
#endif
void
ipkdb_init()
{
ipkdb_if.connect = IPKDB_DEF;
ipkdbinit();
if ( ipkdbifinit(&ipkdb_if, 0) < 0
|| !(ipkdb_if.flags&IPKDB_MYHW)) {
/* Interface not found, drop IPKDB */
printf("IPKDB: No interface found!\n");
ipkdb_if.connect = IPKDB_NOIF;
boothowto &= ~RB_KDB;
}
#ifdef IPKDBKEY
if (!hmac_init())
ipkdb_if.connect = IPKDB_NO;
#endif
}
void
ipkdb_connect(when)
int when;
{
boothowto |= RB_KDB;
if (when == 0)
printf("waiting for remote debugger\n");
ipkdb_trap();
#ifdef IPKDBTEST
new_if.connect = IPKDB_ALL;
if ( ipkdbifinit(&new_if, 1) < 0
|| !(new_if.flags&IPKDB_MYHW)) {
/* Interface not found, no test */
return;
}
init(&new_if);
putpkt(&new_if, "s", 1);
for (ipkdb_test = 1; ipkdb_test;) {
static char buf[512];
int plen;
getpkt(&new_if, buf, &plen);
if (!plen)
continue;
putpkt(&new_if, "eunknown command", 16);
}
#endif
}
void
ipkdb_panic()
{
if (ipkdb_if.connect == IPKDB_NOIF)
return;
ipkdbpanic = 1;
ipkdb_trap();
}
int
ipkdbcmds()
{
static char buf[512];
char *cp;
int plen;
if (!(ipkdb_if.flags&IPKDB_MYHW)) /* no interface */
return 2;
init(&ipkdb_if);
if (ipkdbpanic > 1) {
ipkdb_if.leave(&ipkdb_if);
return 0;
}
putpkt(&ipkdb_if, "s", 1);
while (1) {
getpkt(&ipkdb_if, buf, &plen);
if (!plen) {
if (ipkdbpanic && ipkdb_poll()) {
ipkdb_if.leave(&ipkdb_if);
return 0;
} else
continue;
} else
ipkdbpanic = 0;
switch (*buf) {
default:
putpkt(&ipkdb_if, "eunknown command", 16);
break;
case 'O':
/* This is an allowed reconnect, ack it */
putpkt(&ipkdb_if, "s", 1);
break;
case 'R':
peekmem(&ipkdb_if, buf, ipkdbregs, sizeof ipkdbregs);
break;
case 'W':
if (plen != sizeof ipkdbregs + 1) {
putpkt(&ipkdb_if, "einvalid register size", 22);
break;
}
pokemem(&ipkdb_if, buf + 1, ipkdbregs, sizeof ipkdbregs);
break;
case 'M':
{
void *addr, *len;
plen--;
if ( !(cp = ipkdbaddr(buf + 1, &plen, &addr))
|| !ipkdbaddr(cp, &plen, &len)) {
putpkt(&ipkdb_if, "einvalid peek format", 20);
break;
}
peekmem(&ipkdb_if, buf, addr, (long)len);
break;
}
case 'N':
{
void *addr, *len;
plen--;
if ( !(cp = ipkdbaddr(buf + 1, &plen, &addr))
|| !(cp = ipkdbaddr(cp, &plen, &len))
|| plen < (long)len) {
putpkt(&ipkdb_if, "einvalid poke format", 20);
break;
}
pokemem(&ipkdb_if, cp, addr, (long)len);
break;
}
case 'S':
ipkdb_if.leave(&ipkdb_if);
return 1;
case 'X':
putpkt(&ipkdb_if, "ok",2);
ipkdb_if.connect = IPKDB_DEF; /* ??? */
ipkdb_if.leave(&ipkdb_if);
return 2;
case 'C':
ipkdb_if.leave(&ipkdb_if);
return 0;
}
}
}
static u_char *
ipkdbaddr(cp, pl, dp)
u_char *cp;
int *pl;
void **dp;
{
/* Assume that sizeof(void *) <= sizeof(u_long) */
u_long l;
int i;
if ((*pl -= sizeof *dp) < 0)
return 0;
for (i = sizeof *dp, l = 0; --i >= 0;) {
l <<= 8;
l |= *cp++;
}
*dp = (void *)l;
return cp;
}
static void
peekmem(ifp, buf, addr, len)
struct ipkdb_if *ifp;
u_char *buf;
void *addr;
long len;
{
u_char *cp, *p = addr;
int l;
cp = buf;
*cp++ = 'p';
for (l = len; --l >= 0;)
*cp++ = ipkdbfbyte(p++);
putpkt(ifp, buf, len + 1);
}
static void
pokemem(ifp, cp, addr, len)
struct ipkdb_if *ifp;
u_char *cp;
void *addr;
long len;
{
int c;
u_char *p = addr;
while (--len >= 0)
ipkdbsbyte(p++, *cp++);
putpkt(ifp, "ok", 2);
}
__inline static u_int32_t
getnl(vs)
void *vs;
{
u_char *s = vs;
return (*s << 24)|(s[1] << 16)|(s[2] << 8)|s[3];
}
__inline static u_int
getns(vs)
void *vs;
{
u_char *s = vs;
return (*s << 8)|s[1];
}
__inline static void
setnl(vs, l)
void *vs;
u_int32_t l;
{
u_char *s = vs;
*s++ = l >> 24;
*s++ = l >> 16;
*s++ = l >> 8;
*s = l;
}
__inline static void
setns(vs, l)
void *vs;
int l;
{
u_char *s = vs;
*s++ = l >> 8;
*s = l;
}
static u_short
cksum(st, vcp, l)
u_short st;
void *vcp;
int l;
{
u_char *cp = vcp;
u_long s;
for (s = st; (l -= 2) >= 0; cp += 2)
s += (*cp << 8) + cp[1];
if (l == -1)
s += *cp << 8;
while (s&0xffff0000)
s = (s&0xffff) + (s >> 16);
return s == 0xffff ? 0 : s;
}
static int
assemble(ifp, buf)
struct ipkdb_if *ifp;
void *buf;
{
struct ip *ip, iph;
int off, len, i;
u_char *cp, *ecp;
ip = (struct ip *)buf;
ipkdbcopy(ip, &iph, sizeof iph);
iph.ip_hl = 5;
iph.ip_tos = 0;
iph.ip_len = 0;
iph.ip_off = 0;
iph.ip_ttl = 0;
iph.ip_sum = 0;
if (ifp->asslen) {
if (ipkdbcmp(&iph, ifp->ass, sizeof iph)) {
/*
* different packet
* decide whether to keep the old
* or start a new one
*/
i = getns(&ip->ip_id)^getns(&((struct ip *)ifp->ass)->ip_id);
i ^= (i >> 2)^(i >> 4)^(i >> 8)^(i >> 12);
if (i&1)
/* keep the old */
return 0;
ifp->asslen = 0;
}
}
if (!ifp->asslen) {
ipkdbzero(ifp->assbit, sizeof ifp->assbit);
ipkdbcopy(&iph, ifp->ass, sizeof iph);
}
off = getns(&ip->ip_off);
len = ((off&IP_OFFMASK) << 3) + getns(&ip->ip_len) - ip->ip_hl * 4;
if (ifp->asslen < len)
ifp->asslen = len;
if (ifp->asslen + sizeof *ip > sizeof ifp->ass) {
/* packet too long */
ifp->asslen = 0;
return 0;
}
if (!(off&IP_MF)) {
off &= IP_OFFMASK;
cp = ifp->assbit + (off >> 3);
for (i = off & 7; i < 8; *cp |= 1 << i++);
for (; cp < ifp->assbit + sizeof ifp->assbit; *cp++ = -1);
} else {
off &= IP_OFFMASK;
cp = ifp->assbit + (off >> 3);
ecp = ifp->assbit + (len >> 6);
if (cp == ecp)
for (i = off & 7; i <= (len >> 3)&7; *cp |= 1 << i++);
else {
for (i = off & 7; i < 8; *cp |= 1 << i++);
for (; ++cp < ecp; *cp = -1);
for (i = 0; i < ((len >> 3)&7); *cp |= 1 << i++);
}
}
ipkdbcopy(buf + ip->ip_hl * 4,
ifp->ass + sizeof *ip + (off << 3),
len - (off << 3));
for (cp = ifp->assbit; cp < ifp->assbit + sizeof ifp->assbit;)
if (*cp++ != (u_char)-1)
/* not complete */
return 0;
ip = (struct ip *)ifp->ass;
setns(&ip->ip_len, sizeof *ip + ifp->asslen);
/* complete */
return 1;
}
static char *
inpkt(ifp, ibuf, poll)
struct ipkdb_if *ifp;
char *ibuf;
int poll;
{
int cnt = 1000000;
int l, ul;
struct ether_header *eh;
struct arphdr *ah;
struct ip *ip;
struct udphdr *udp;
struct ipovly ipo;
while (1) {
l = ifp->receive(ifp, ibuf, poll != 0);
if (!l) {
if (poll == 1 || (poll == 2 && --cnt <= 0))
break;
else
continue;
}
eh = (struct ether_header *)ibuf;
switch (getns(&eh->ether_type)) {
case ETHERTYPE_ARP:
ah = (struct arphdr *)(ibuf + 14);
if ( getns(&ah->ar_hrd) != ARPHRD_ETHER
|| getns(&ah->ar_pro) != ETHERTYPE_IP
|| ah->ar_hln != 6
|| ah->ar_pln != 4)
/* unsupported arp packet */
break;
switch (getns(&ah->ar_op)) {
case ARPOP_REQUEST:
if ( (ifp->flags&IPKDB_MYIP)
&& !ipkdbcmp(ar_tpa(ah),
ifp->myinetaddr,
sizeof ifp->myinetaddr)) {
/* someone requested my address */
ipkdbcopy(eh->ether_shost,
eh->ether_dhost,
sizeof eh->ether_dhost);
ipkdbcopy(ifp->myenetaddr,
eh->ether_shost,
sizeof eh->ether_shost);
setns(&ah->ar_op, ARPOP_REPLY);
ipkdbcopy(ar_sha(ah),
ar_tha(ah),
ah->ar_hln);
ipkdbcopy(ar_spa(ah),
ar_tpa(ah),
ah->ar_pln);
ipkdbcopy(ifp->myenetaddr,
ar_sha(ah),
ah->ar_hln);
ipkdbcopy(ifp->myinetaddr,
ar_sha(ah),
ah->ar_pln);
ifp->send(ifp, ibuf, 74);
continue;
}
break;
default:
break;
}
break;
case ETHERTYPE_IP:
if (ipkdbcmp(eh->ether_dhost,
ifp->myenetaddr,
sizeof ifp->myenetaddr))
/* not only for us */
break;
ip = (struct ip *)(ibuf + 14);
if ( ip->ip_v != IPVERSION
|| ip->ip_hl < 5
|| getns(&ip->ip_len) + 14 > l)
/* invalid packet */
break;
if (cksum(0, ip, ip->ip_hl * 4))
/* wrong checksum */
break;
if (ip->ip_p != IPPROTO_UDP)
break;
if (getns(&ip->ip_off)&~IP_DF) {
if (!assemble(ifp, ip))
break;
ip = (struct ip *)ifp->ass;
ifp->asslen = 0;
}
udp = (struct udphdr *)((char *)ip + ip->ip_hl * 4);
ul = getns(&ip->ip_len) - ip->ip_hl * 4;
if (getns(&udp->uh_ulen) != ul)
/* invalid UDP packet length */
break;
ipkdbcopy(ip, &ipo, sizeof ipo);
ipkdbzero(ipo.ih_x1, sizeof ipo.ih_x1);
ipo.ih_len = udp->uh_ulen;
if ( udp->uh_sum
&& cksum(cksum(0, &ipo, sizeof ipo), udp, ul))
/* wrong checksum */
break;
if (!(ifp->flags&IPKDB_MYIP)) {
if ( getns(&udp->uh_sport) == 67
&& getns(&udp->uh_dport) == 68
&& *(char *)(udp + 1) == 2) {
/* this is a BOOTP reply to our ethernet address */
/* should check a bit more? XXX */
ipkdbcopy(&ip->ip_dst,
ifp->myinetaddr,
sizeof ifp->myinetaddr);
ifp->flags |= IPKDB_MYIP;
}
/* give caller a chance to resend his request */
return 0;
}
if ( ipkdbcmp(&ip->ip_dst, ifp->myinetaddr, sizeof ifp->myinetaddr)
|| getns(&udp->uh_sport) != IPKDBPORT
|| getns(&udp->uh_dport) != IPKDBPORT)
break;
/* so now it's a UDP packet for the debugger */
{
/* Check for reconnect packet */
u_char *p;
p = (u_char *)(udp + 1);
if (!getnl(p) && p[6] == 'O') {
l = getns(p + 4);
if ( l <= ul - sizeof *udp - 6
&& check_ipkdb(ifp, &ip->ip_src, udp->uh_sport,
udp->uh_dport, p, l + 6)) {
ipkdbcopy(&ip->ip_src,
ifp->hisinetaddr,
sizeof ifp->hisinetaddr);
ipkdbcopy(eh->ether_shost,
ifp->hisenetaddr,
sizeof ifp->hisenetaddr);
ifp->flags |= IPKDB_HISHW|IPKDB_HISIP;
return p;
}
}
}
if ( (ifp->flags&IPKDB_HISIP)
&& ipkdbcmp(&ip->ip_src,
ifp->hisinetaddr, sizeof ifp->hisinetaddr))
/* It's a packet from someone else */
break;
if (!(ifp->flags&IPKDB_HISIP)) {
ifp->flags |= IPKDB_HISIP;
ipkdbcopy(&ip->ip_src,
ifp->hisinetaddr, sizeof ifp->hisinetaddr);
}
if (!(ifp->flags&IPKDB_HISHW)) {
ifp->flags |= IPKDB_HISHW;
ipkdbcopy(eh->ether_shost,
ifp->hisenetaddr, sizeof ifp->hisenetaddr);
}
return (char *)(udp + 1);
default:
/* unknown type */
break;
}
if (l)
ipkdbgotpkt(ifp, ibuf, l);
}
return 0;
}
static short ipkdb_ipid = 0;
static void
outpkt(ifp, in, l, srcport, dstport)
struct ipkdb_if *ifp;
char *in;
int l;
int srcport, dstport;
{
u_char *sp;
struct ether_header *eh;
struct ip *ip;
struct udphdr *udp;
u_char *cp;
char obuf[ETHERMTU+14];
struct ipovly ipo;
int i, off;
ipkdbzero(obuf, sizeof obuf);
eh = (struct ether_header *)obuf;
if (!(ifp->flags&IPKDB_HISHW))
for (cp = eh->ether_dhost; cp < eh->ether_dhost + sizeof eh->ether_dhost;)
*cp++ = -1;
else
ipkdbcopy(ifp->hisenetaddr, eh->ether_dhost, sizeof eh->ether_dhost);
ipkdbcopy(ifp->myenetaddr, eh->ether_shost, sizeof eh->ether_shost);
setns(&eh->ether_type, ETHERTYPE_IP);
ip = (struct ip *)(obuf + 14);
ip->ip_v = IPVERSION;
ip->ip_hl = 5;
setns(&ip->ip_id, ipkdb_ipid++);
ip->ip_ttl = 255;
ip->ip_p = IPPROTO_UDP;
ipkdbcopy(ifp->myinetaddr, &ip->ip_src, sizeof ip->ip_src);
ipkdbcopy(ifp->hisinetaddr, &ip->ip_dst, sizeof ip->ip_dst);
udp = (struct udphdr *)(ip + 1);
setns(&udp->uh_sport, srcport);
setns(&udp->uh_dport, dstport);
setns(&udp->uh_ulen, l + sizeof *udp);
ipkdbcopy(ip, &ipo, sizeof ipo);
ipkdbzero(ipo.ih_x1, sizeof ipo.ih_x1);
ipo.ih_len = udp->uh_ulen;
setns(&udp->uh_sum,
~cksum(cksum(cksum(0, &ipo, sizeof ipo),
udp, sizeof *udp),
in, l));
for (cp = (u_char *)(udp + 1), l += sizeof *udp, off = 0;
l > 0;
l -= i, in += i, off += i, cp = (u_char *)udp) {
i = l > ifp->mtu - sizeof *ip ? ((ifp->mtu - sizeof *ip)&~7) : l;
ipkdbcopy(in, cp, i);
setns(&ip->ip_len, i + sizeof *ip);
setns(&ip->ip_off, (l > i ? IP_MF : 0)|(off >> 3));
ip->ip_sum = 0;
setns(&ip->ip_sum, ~cksum(0, ip, sizeof *ip));
if (i + sizeof *ip < ETHERMIN)
i = ETHERMIN - sizeof *ip;
ifp->send(ifp, obuf, i + sizeof *ip + 14);
}
}
static void
init(ifp)
struct ipkdb_if *ifp;
{
u_char *cp;
struct ether_header *eh;
struct ip *ip;
struct udphdr *udp;
u_char buf[ETHERMTU+14];
struct ipovly ipo;
int secs = 0;
ifp->start(ifp);
#ifdef __notyet__
if (!(ifp->flags&IPKDB_MYIP))
ipkdbinet(ifp);
#endif
if (ifp->flags&IPKDB_MYIP)
return;
while (!(ifp->flags&IPKDB_MYIP)) {
ipkdbzero(buf, sizeof buf);
cp = buf;
*cp++ = 1; /* BOOTP_REQUEST */
*cp++ = 1; /* Ethernet hardware */
*cp++ = 6; /* length of address */
setnl(++cp, 0x12345678); /* some random number? */
setns(cp + 4, secs++);
ipkdbcopy(ifp->myenetaddr, cp + 24, sizeof ifp->myenetaddr);
outpkt(ifp, buf, 300, 68, 67);
inpkt(ifp, buf, 2);
if (ipkdbpanic && ipkdb_poll()) {
ipkdbpanic++;
return;
}
}
cp = ifp->myinetaddr;
printf("My IP address is %d.%d.%d.%d\n",
cp[0], cp[1], cp[2], cp[3]);
}
#ifdef IPKDBKEY
#include <sys/md5.h>
/* HMAC Checksumming routines, see draft-ietf-ipsec-hmac-md5-00.txt */
#define LENCHK 16 /* Length of checksum in bytes */
static MD5_CTX icontext, ocontext;
/*
* 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 int
hmac_init()
{
char pad[64];
char tk[16];
u_char *key = ipkdbkey;
int key_len = strlen(key);
int i;
/* Require key to be at least 16 bytes long */
if (key_len < 16) {
printf("IPKDBKEY must be at least 16 bytes long!\n");
ipkdbzero(key, key_len); /* XXX */
return 0;
}
/* 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);
ipkdbzero(key, key_len); /* XXX */
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.
*/
ipkdbzero(pad, sizeof pad);
ipkdbcopy(key, pad, key_len);
for (i = 0; i < 64; i++)
pad[i] ^= 0x36;
MD5Init(&icontext);
MD5Update(&icontext, pad, 64);
ipkdbzero(pad, sizeof pad);
ipkdbcopy(key, pad, key_len);
for (i = 0; i < 64; i++)
pad[i] ^= 0x5c;
MD5Init(&ocontext);
MD5Update(&ocontext, pad, 64);
/* Zero out the key XXX */
ipkdbzero(key, key_len);
return 1;
}
/*
* This is more or less hmac_md5 from the HMAC IETF draft, Appendix.
*/
static void *
chksum(buf, len)
void *buf;
int len;
{
u_char digest[16];
static u_char result[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
*/
ipkdbcopy(&icontext, &context, sizeof context);
MD5Update(&context, buf, len);
MD5Final(digest, &context);
/*
* perform outer MD5
*/
ipkdbcopy(&ocontext, &context, sizeof context);
MD5Update(&context, digest, 16);
MD5Final(result, &context);
return (result);
}
#else
#define LENCHK 1 /* Length of checksum in bytes */
static void *
chksum(buf, l)
void *buf;
int l;
{
static char csum[1];
int sum;
char *cp = buf;
for (sum = 0; --l >= 0; sum += *cp++);
csum[0] = sum;
return csum;
}
#endif
static void
getpkt(ifp, buf, lp)
struct ipkdb_if *ifp;
char *buf;
int *lp;
{
char *got;
int l;
char ibuf[ETHERMTU+14];
*lp = 0;
while (1) {
if (!(got = inpkt(ifp, ibuf, ipkdbpanic != 0))) {
*lp = 0;
return;
}
if ( ifp->seq == getnl(got)
&& got[6] >= 'A'
&& got[6] <= 'Z'
&& (l = getns(got + 4))
&& !ipkdbcmp(chksum(got, l + 6), got + l + 6, LENCHK)) {
ipkdbcopy(got + 6, buf, *lp = l);
return;
}
if ( ifp->pktlen
&& ((ifp->flags&(IPKDB_MYIP|IPKDB_HISIP|IPKDB_CONNECTED))
== (IPKDB_MYIP|IPKDB_HISIP|IPKDB_CONNECTED)))
outpkt(ifp, ifp->pkt, ifp->pktlen, IPKDBPORT, IPKDBPORT);
}
}
static void
putpkt(ifp, buf, l)
struct ipkdb_if *ifp;
char *buf;
int l;
{
setnl(ifp->pkt, ifp->seq++);
setns(ifp->pkt + 4, l);
ipkdbcopy(buf, ifp->pkt + 6, l);
ipkdbcopy(chksum(ifp->pkt, l + 6), ifp->pkt + 6 + l, LENCHK);
ifp->pktlen = l + 6 + LENCHK;
if ( (ifp->flags&(IPKDB_MYIP|IPKDB_HISIP|IPKDB_CONNECTED))
!= (IPKDB_MYIP|IPKDB_HISIP|IPKDB_CONNECTED))
return;
outpkt(ifp, ifp->pkt, ifp->pktlen, IPKDBPORT, IPKDBPORT);
}
static __inline int
maskcmp(vin, vmask, vmatch)
void *vin, *vmask, *vmatch;
{
int i;
u_char *in = vin, *mask = vmask, *match = vmatch;
for (i = 4; --i >= 0;)
if ((*in++&*mask++) != *match++)
return 0;
return 1;
}
static int
check_ipkdb(ifp, shost, sport, dport, p, l)
struct ipkdb_if *ifp;
struct in_addr *shost;
u_short sport, dport;
char *p;
int l;
{
u_char hisenet[6];
u_char hisinet[4];
char save;
struct ipkdb_allow *kap;
#ifndef IPKDBSECURE
if (securelevel > 0)
return 0;
#endif
if (ipkdbcmp(chksum(p, l), p + l, LENCHK))
return 0;
switch (ifp->connect) {
default:
return 0;
case IPKDB_SAME:
if (ipkdbcmp(shost, ifp->hisinetaddr, sizeof ifp->hisinetaddr))
return 0;
if (getns(&sport) != IPKDBPORT || getns(&dport) != IPKDBPORT)
return 0;
bzero(&hisinet, sizeof hisinet);
break;
case IPKDB_ALL:
for (kap = ipkdballow; kap < ipkdballow + ipkdbcount; kap++) {
if (maskcmp(shost, kap->mask, kap->match))
break;
}
if (kap >= ipkdballow + ipkdbcount)
return 0;
if (getns(&sport) != IPKDBPORT || getns(&dport) != IPKDBPORT)
return 0;
ipkdbcopy(ifp->hisenetaddr, hisenet, sizeof hisenet);
ipkdbcopy(ifp->hisinetaddr, hisinet, sizeof hisinet);
save = ifp->flags;
ipkdbcopy(shost, ifp->hisinetaddr, sizeof ifp->hisinetaddr);
ifp->flags &= ~IPKDB_HISHW;
ifp->flags |= IPKDB_HISIP;
break;
}
if (connectipkdb(ifp, p + 6, l - 6) < 0) {
if (ifp->connect == IPKDB_ALL) {
ipkdbcopy(hisenet, ifp->hisenetaddr, sizeof ifp->hisenetaddr);
ipkdbcopy(hisinet, ifp->hisinetaddr, sizeof ifp->hisinetaddr);
ipkdb_if.flags = save;
}
return 0;
}
return 1;
}
/*
* Should check whether packet came across the correct interface
*/
int
checkipkdb(shost, sport, dport, m, off, len)
struct in_addr *shost;
u_short sport, dport;
struct mbuf *m;
{
char *p;
int l;
char ibuf[ETHERMTU+50];
m_copydata(m, off, len, ibuf);
p = ibuf;
if (getnl(p) || p[6] != 'O')
return 0;
l = getns(p + 4);
if (l > len - 6 || !check_ipkdb(&ipkdb_if, shost, sport, dport, p, l + 6))
return 0;
ipkdb_connect(1);
return 1;
}
static int
connectipkdb(ifp, buf, l)
struct ipkdb_if *ifp;
char *buf;
int l;
{
char *cp;
u_char *ip;
if (*buf != 'O')
return -1;
if (getnl(buf + 1) == ifp->id)
/* It's a retry of a connect packet, ignore it */
return -1;
ip = ifp->hisinetaddr;
printf("debugged by ");
l -= 1 + sizeof(u_int32_t);
for (cp = buf + 1 + sizeof(u_int32_t); --l >= 0; printf("%c", *cp++));
printf(" (%d.%d.%d.%d)\n", ip[0], ip[1], ip[2], ip[3]);
ifp->connect = IPKDB_SAME; /* if someone once connected, he may do so again */
ifp->flags |= IPKDB_CONNECTED;
ifp->seq = 0;
ifp->pktlen = 0;
ifp->id = getnl(buf + 1);
return 0;
}