📄 pcbootp.c
字号:
/*
*
* BOOTP - Boot Protocol (RFC 854)
*
* These extensions get called if _bootphost is set to an IP address or
* to 0xffffffff.
*
* Version
*
* 0.4 : May 11, 1999 : S. Lawson - added DHCP
* 0.3 : Feb 1, 1992 : J. Dent - patched up various things
* 0.2 : May 22, 1991 : E.J. Sutcliffe - added RFC_1048 vendor fields
* 0.1 : May 9, 1991 : E. Engelke - made part of the library
* 0.0 : May 3, 1991 : E. Engelke - original program as an application
*
*/
#include <copyright.h>
#include <stdio.h>
#include <wattcp.h>
#include <mem.h>
#include <bootp.h>
#include <conio.h>
#include <string.h> // S. Lawson
/* global variables */
longword _bootphost = 0xffffffffL;
longword _dhcphost = 0xffffffffL; // S. Lawson
word _bootptimeout = 30;
word _bootpon = 0;
static longword dhcp_life; // S. Lawson
static char dhcp_seen, dhcp_bind; // S. Lawson
static char got_ns, got_gw, got_cy; // S. Lawson
static udp_Socket bsock; // S. Lawson
// S. Lawson - we now use these
char *getdomainname( char *name, int length );
char *setdomainname( char *string );
char *sethostname( char *name );
#define VM_RFC1048 0x63825363L /* I think this is correct */
/*
* _dobootpc - Checks global variables _bootptimeout, _bootphost
* if no host specified, the broadcast address
* returns 0 on success and sets ip address
*/
int _dobootp( void )
{
// S. Lawson - moved udp_Socket bsock;
extern char defaultdomain[]; // S. Lawson - in pcconfig.c
longword sendtimeout, bootptimeout;
word magictimeout;
word len, templen;
struct bootp sendbootp; /* outgoing data */
struct bootp _bootp; /* incoming data */
int status;
longword xid;
unsigned char *p ,*t; // R. Whitby - uncomment *t
longword *l; // S. Lawson
/*
const char dhcp_opt[]={DHCP_VN_TYPE,1,DHCP_TY_REQ,DHCP_VN_OPTS,6,1,
3,6,42,12,15,0}; */ /* S. Lawson - keep even! */
const char dhcp_opt[]={DHCP_VN_OPTS,6,1,3,6,42,12,15,0}; /* Shaun Jackman 2000/3/10 */
// if ( _pktdevclass == PD_SLIP ) return( -1 );
/* We must get Waterloo TCP to use IP address 0 for sending */
xid = set_timeout(1); /* random... well, actually time based */
my_ip_addr = 0;
dhcp_seen=dhcp_bind=0; // S. Lawson
got_ns=got_gw=got_cy=0; // S. Lawson
if (!udp_open( &bsock, IPPORT_BOOTPC, _bootphost, IPPORT_BOOTPS, NULL )) {
outs("\n\rUnable to resolve bootp server\n\r");
return( -1 );
}
bootptimeout = set_timeout( _bootptimeout );
magictimeout = (xid & 7) + 7; /* between 7 and 14 seconds */
memset( &sendbootp, 0, sizeof( struct bootp ));
sendbootp.bp_op = BOOTREQUEST;
sendbootp.bp_htype = _pktdevclass;
/* Copy into position the Magic Number used by Bootp */
/* avoid static storage and pushf/call assembler instructions */
*(longword *)(&sendbootp.bp_vend) = intel(VM_RFC1048);
// S. Lawson - append DHCPDISCOVER to vendor field. I'm expecting that
// a BOOTP server configured with this machines MAC address will simply
// ignore it and answer anyway, possibly a bad assumption?
p=&sendbootp.bp_vend[4];
*p++=DHCP_VN_TYPE;
*p++=1;
*p++=DHCP_TY_DSC;
*p=255;
if (_pktdevclass == PD_ETHER) sendbootp.bp_hlen = 6;
sendbootp.bp_xid = xid;
sendbootp.bp_secs = intel16( 1 );
// movmem( &_eth_addr, &sendbootp.bp_chaddr, sizeof(eth_address));
// 94.11.19 ??
movmem( &_eth_addr, sendbootp.bp_chaddr, sizeof(eth_address));
while ( 1 ) {
sock_fastwrite( (sock_type*)&bsock, (byte *)&sendbootp, sizeof( struct bootp ));
sendbootp.bp_secs = intel16( intel16( sendbootp.bp_secs ) + magictimeout ); /* for next time */
sendtimeout = set_timeout( magictimeout += (xid >> 5) & 7 );
while ( !chk_timeout( sendtimeout )) {
if (chk_timeout( bootptimeout))
goto give_up;
kbhit();
sock_tick( (sock_type*)&bsock, &status );
status = status; /* get rid of warning */
if ((len = sock_dataready( (sock_type*)&bsock)) != 0 ) {
/* got a response, lets consider it */
templen = sock_fastread( (sock_type*)&bsock, (byte *)&_bootp, sizeof( struct bootp ));
// S. Lawson - vendor area increased (bootp: 64, dhcp: 312)
// if ( templen < sizeof( struct bootp )) {
if ( templen < sizeof( struct bootp ) - (312-64) ) {
/* too small, not a bootp packet */
memset( &_bootp, 0, sizeof( struct bootp ));
continue;
}
/* we must see if this is for us */
if (_bootp.bp_xid != sendbootp.bp_xid) {
memset( &_bootp, 0, sizeof( struct bootp ));
continue;
}
/* we must have found it */
my_ip_addr = intel( _bootp.bp_yiaddr );
// S. Lawson - just in case
if (!my_ip_addr) {
outs("Rejecting zero address\r\n");
memset( &_bootp, 0, sizeof( struct bootp ));
continue;
}
dhcp_seen = 0; // S. Lawson
dhcp_life=0L; // S. Lawson
if ( intel( *(longword*)(&_bootp.bp_vend)) == VM_RFC1048 ) {
/*RFC1048 complient BOOTP vendor field */
/* Based heavily on NCSA Telnet BOOTP */
p = &_bootp.bp_vend[4]; /* Point just after vendor field */
// S. Lawson - vendor area increased for dhcp (bootp: 64, dhcp: 312)
// while ((*p!=255) && (p <= &_bootp.bp_vend[63])) {
while ((*p!=255) && (p <= &_bootp.bp_vend[311])) {
switch(*p) {
case 0: /* Nop Pad character */
p++;
break;
case 1: /* Subnet Mask */
sin_mask = intel( *(longword *)( &p[2] ));
/* and fall through */
case 2: /* Time offset */
p += *(p+1) + 2;
break;
case 3: /* gateways */
/* only add first */
if (!got_gw) // S. Lawson
_arp_add_gateway( NULL,
intel( *(longword*)(&p[2])));
p +=*(p+1)+2;
got_gw=1; // S. Lawson
break;
/* and fall through */
case 4: /*time servers */
/* fall through */
case 5: /* IEN=116 name server */
p +=*(p+1)+2;
break;
case 6: /* Domain Name Servers (BIND) */
if (!got_ns) // S. Lawson
for ( len = 0; len < *(p+1) ; len += 4 )
_add_server( &_last_nameserver,
MAX_NAMESERVERS, def_nameservers,
intel( *(longword*)(&p[2+len])));
got_ns=1; // S. Lawson
/* and fall through */
case 7: /* log server */
p += *(p+1)+2;
break;
case 8: /* cookie server */
if (!got_cy) // S. Lawson
for ( len = 0; len < *(p+1) ; len += 4 )
_add_server( &_last_cookie, MAX_COOKIES,
_cookie, intel( *(longword*)(&p[2+len])));
/* and fall through */
p +=*(p+1)+2;
got_cy=1; // S. Lawson
break;
case 9: /* lpr server */
case 10: /* impress server */
case 11: /* rlp server */
p +=*(p+1)+2;
break;
#ifdef NOTUSED // R. Whitby - handle bootp/dhcp setting of domain name too
case 12: /* Client Hostname */
movmem( &p[2] , _hostname, MAX_STRING );
/* bootpfix - 94.09.30 mdurkin@tsoft.net */
/* hostname field is *not* null terminated; need to use
length byte. Extra bytes used to be tacked on the
end, usually just a single 0xFF char (the end tag).
Technically should also probably make the
_hostname buffer 256 chars long so we'll never
truncate the name arbitrarily... see CMU bootpd. */
if( *(p+1) < MAX_STRING ) {
_hostname[ *(p+1) ] = '\0';
} else _hostname[ MAX_STRING - 1 ] = '\0';
p += *(p+1)+2;
break;
#else
case 12: /* Client Hostname */
len = *(++p);
movmem(p+1, p, len); *(p+len) = 0;
if ((t = strchr((char *)p, '.')) != 0) {
*t++ = 0;
if (!getdomainname(NULL, 0)) {
strncpy(defaultdomain, (char *)t, MAX_STRING);
defaultdomain[MAX_STRING-1] = 0;
setdomainname(defaultdomain);
}
}
strncpy(_hostname, (char *)p, MAX_STRING);
_hostname[MAX_STRING-1] = 0;
sethostname(_hostname);
p += len+1;
break;
case 15: /* Client Domain */
len = *(++p);
if (!getdomainname(NULL, 0)) {
movmem(p+1, p, len); *(p+len) = 0;
strncpy(defaultdomain, (char *)p, MAX_STRING);
defaultdomain[MAX_STRING-1] = 0;
setdomainname(defaultdomain);
}
p += len+1;
break;
#endif
// S. Lawson - handle DHCP type
case DHCP_VN_TYPE:
dhcp_seen=1;
if (!dhcp_bind) {
if (*(p+2)!=DHCP_TY_OFR) my_ip_addr=0L;
} else {
if (*(p+2)!=DHCP_TY_ACK) my_ip_addr=0L;
}
p += *(p+1)+2;
break;
// S. Lawson - handle server id
case DHCP_VN_SRVRID:
_dhcphost=intel(*(longword*)(&p[2]));
p += *(p+1)+2;
break;
// S. Lawson - handle lease expiration time
case DHCP_VN_T2TIME:
dhcp_life=intel(*(longword*)(&p[2]));
p += *(p+1)+2;
break;
case 255:
break;
default:
p +=*(p+1)+2;
break;
} /* end of switch */
} /* end of while */
}/* end of RFC_1048 if */
if (dhcp_seen && my_ip_addr==0L) continue; // S. Lawson
if (!dhcp_seen || dhcp_bind) // S. Lawson
goto give_up;
// S. Lawson - append DHCPREQUEST and server/offered IP
// addresses to vendor field. We should get get a DHCPACK
// in response
dhcp_bind=1;
p=&sendbootp.bp_vend[4];
*p++=DHCP_VN_TYPE;
*p++=1;
*p++=DHCP_TY_REQ;
movmem(dhcp_opt,p,sizeof(dhcp_opt));
p+=sizeof(dhcp_opt);
*p++=DHCP_VN_REQIP;
*p++=4;
l=(longword *) p, *l++=intel(my_ip_addr), p=(char *) l;
*p++=DHCP_VN_SRVRID;
*p++=4;
l=(longword *) p, *l++=intel(_dhcphost), p=(char *) l;
*p=255;
my_ip_addr=0L;
break;
}
}
}
give_up:
sock_close( (sock_type *)&bsock );
return (my_ip_addr == 0 ); /* return 0 on success */
sock_err:
/* major network error if UDP fails */
sock_close( (sock_type *)&bsock );
return( -1 );
}
// S. Lawson - countdown the DHCP lease, renew or expire it as needed (mostly
// copied from above and trimmed down alot)
int dhcp_expired(void) {
static char nested=false;
static char expired=false;
static char sockopen=false;
static longword timeout;
static longword xid=0;
static longword seconds=0, renew=0;
longword ltime;
word len;
char acknak; // -1 NAK, 0 unseen, 1 ACK
struct bootp _bootp;
unsigned char *p;
longword *l;
// once expired, stop talking
if (expired) return true;
// skip if nested, not dhcp, or the lease is permanent
if (nested || !dhcp_bind || dhcp_life==0xFFFFFFFF) return false;
nested=true; // avoid tcp_tick() recursion
// on first pass, init the renewal time and start the timer
if (seconds==0) {
renew=dhcp_life>>1; // renew at 1/2 lifetime
if ((seconds=renew)>3600) seconds=3600;
timeout=set_timeout((int) seconds);
nested=false;
return false;
}
// if the bootp socket is open, check for a response
if (sockopen && (len = sock_dataready( (sock_type*)&bsock)) != 0 ) {
memset( &_bootp, 0, sizeof( struct bootp ));
len = sock_fastread( (sock_type*)&bsock, (byte *)&_bootp, sizeof( struct bootp ));
// (312-64) gives size of original bootp packet - struct bigger for dhcp
if ( len >= sizeof( struct bootp ) - (312-64) &&
_bootp.bp_xid == xid &&
my_ip_addr == intel( _bootp.bp_yiaddr ) &&
intel( *(longword*)(&_bootp.bp_vend)) == VM_RFC1048 ) {
acknak=0;
ltime=0;
p=&_bootp.bp_vend[4];
while (*p!=255 && p<=&_bootp.bp_vend[311]) {
switch(*p) {
case 0: /* Nop Pad character */
p++;
break;
case DHCP_VN_TYPE: /* DHCP type */
if (*(p+2)==DHCP_TY_ACK) acknak=1;
if (*(p+2)==DHCP_TY_NAK) acknak=-1;
p += *(p+1)+2;
break;
case DHCP_VN_T2TIME: /* expiration time */
ltime=intel(*(longword*)(&p[2]));
p += *(p+1)+2;
break;
default:
p +=*(p+1)+2;
break;
} /* end of switch */
} /* end of while */
if (acknak==-1) { // NAK!
dhcp_life=renew=seconds=0; // fall through and expire
} else if (acknak==1 && ltime) { // ACK!
sock_close((sock_type*)&bsock);
sockopen=false;
dhcp_life=ltime;
renew=dhcp_life>>1; // renew at 1/2 lifetime
if ((seconds=renew)>3600) seconds=3600;
timeout=set_timeout(seconds);
nested=false;
return false;
}
}
}
// count down the timeout till it expires
if (!chk_timeout(timeout)) {
nested=false;
return false;
}
// keep counting every hour (or less) till renewal time
renew-=seconds;
dhcp_life-=seconds;
if (renew>0) {
if ((seconds=renew)>3600) seconds=3600;
timeout=set_timeout((int) seconds);
nested=false;
return false;
}
// expire if dhcp_life gets below 2 seconds
if (dhcp_life<2) {
outs("\n\rDHCP lease expired, network disabled\n\r");
if (sockopen) {
sock_close((sock_type*)&bsock);
sockopen=false;
}
// we really should should terminate all connections here
expired=true;
nested=false;
return true;
}
// send out a DHCPREQUEST to renew the lease and restart timer
if (!sockopen && !udp_open( &bsock, IPPORT_BOOTPC, _dhcphost, IPPORT_BOOTPS, NULL )) {
outs("\n\rUnable to resolve dhcp server\n\r");
} else {
sockopen=true;
memset( &_bootp, 0, sizeof( struct bootp ));
_bootp.bp_op = BOOTREQUEST;
_bootp.bp_htype = _pktdevclass;
if (_pktdevclass == PD_ETHER) _bootp.bp_hlen = 6;
_bootp.bp_ciaddr=intel(my_ip_addr);
if (xid==0) xid=intel(my_ip_addr);
_bootp.bp_xid = ++xid;
_bootp.bp_secs = intel16( 1 );
*(longword *)(&_bootp.bp_vend) = intel(VM_RFC1048);
p=&_bootp.bp_vend[4];
*p++=DHCP_VN_TYPE;
*p++=1;
*p++=DHCP_TY_REQ;
*p++=0;
*p++=DHCP_VN_REQIP;
*p++=4;
l=(longword *) p, *l++=intel(my_ip_addr), p=(char *) l;
*p++=DHCP_VN_SRVRID;
*p++=4;
l=(longword *) p, *l++=intel(_dhcphost), p=(char *) l;
*p=255;
movmem( &_eth_addr, _bootp.bp_chaddr, sizeof(eth_address));
sock_fastwrite( (sock_type*)&bsock, (byte *)&_bootp, sizeof( struct bootp ));
}
renew=dhcp_life>>1; // renew at 1/2 lifetime
if ((seconds=renew)>3600) seconds=3600;
timeout=set_timeout(seconds);
nested=false;
return false;
}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -