www.pudn.com > test11.rar > plugs.c, change:2008-07-31,size:85842b


// file: plugs.c 
// 
// Implementation of the plugs 
// tcp/ip stack for embedded systems 
// 
// Author: dvb, 2001-2002 
// 
// ex:set tabstop=4: 
// ex:set expandtab: 
// ex:set shiftwidth=4: 
 
#include "plugs.h" 
#include "plugs_private.h" 
 
 
// +----------------- 
// | The Globals 
 
s_plugs_globals ng_plugs_globals; 
 
// +------------------------ 
// | Debug bit 4 means show debug on system plugs 
// | 
 
#if (PLUGS_DEBUG & 4) 
	#define nk_plugs_default_debug (ne_plugs_flag_debug_tx | ne_plugs_flag_debug_rx) 
#else 
	#define nk_plugs_default_debug (0) 
#endif 
 
// +------------------------ 
// | Local Prototypes 
 
static void r_add_to_arp_cache_from_ip_packet 
		( 
		s_plugs_adapter_entry *a, 
		ns_plugs_ethernet_packet *p 
		); 
 
static void r_add_to_arp_cache 
		( 
		s_plugs_adapter_entry *a, 
		net_32 ip_address, 
		net_32 ethu32,net_16 ethl16 
		); 
 
static int r_plugs_dns_proc 
		( 
		int plug_handle, 
		void *context, 
		ns_plugs_packet *p, 
		void *payload, 
		int payload_length 
		); 
 
static int r_tcp_dispatch 
		( 
		s_plugs_adapter_entry *a, 
		int plug_handle, 
		s_plugs_plug *plug, 
		ns_plugs_packet *sub_protocol, 
		void *payload, 
		int payload_length 
		); 
 
static int r_tcp_send 
		( 
		int plug_handle, 
		s_plugs_plug *plug, 
		int flags, 
		void *payload, 
		long payload_length 
		); 
 
void r_tcp_plug_set_inert (s_plugs_plug* plug); 
 
 
static int wellspring_of_sequence_numbers(void) 
{ 
   static int seed; 
 
   seed += 70000013; 
   seed += nr_timer_milliseconds() * 1000219; 
 
   return seed; 
} 
    
 
static s_plugs_plug *r_plug_from_handle(int plug_handle); 
 
static void r_plugs_irq_handler(int context,int irq_number, int 
                                interruptee_pc); 
 
static void r_set_irqs_for_plug (s_plugs_plug* plug, int on_off); 
 
 
// --------------------------- 
// Byte-swapping subroutines 
 
host_16 nr_n2h16(net_16 n) 
	{ 
	return nm_n2h16(n); 
	} 
 
host_32 nr_n2h32(net_32 n) 
	{ 
	return nm_n2h32(n); 
	} 
 
 
 
static s_plugs_plug *r_plug_from_handle(int plug_handle) 
	{ 
	s_plugs_plug *plug; 
	s_plugs_globals *g = &ng_plugs_globals; 
 
	plug_handle -= nk_plugs_plug_number_base; 
	plug = &g->plug[plug_handle]; 
 
	return plug; 
	} 
 
// -------------------- 
// r_calculate_checksum(base,byte_count,ip_header) 
// 
// Return complement of 1's-complement 
// checksum of the specified range 
// Return in network byte order. 
// 
// If ip_header pointer is included, add in 
// the pseudo header as used by udp & tcp 
// And possibly add in the (host-ordered) 
// pseudo_length, too. 
// 
 
net_16 r_calculate_checksum 
		( 
		void *base, 
		int byte_count, 
		ns_plugs_ip_packet *ip_packet, 
		host_16 pseudo_length 
		) 
	{ 
	net_16 *w = base; 
	host_32 x; 
	unsigned long result = 0; 
 
	while(byte_count > 0) 
		{ 
		x = nr_n2h16(*w++); 
		if(byte_count == 1) 
			x &= 0xff00; 
 
		result += x; 
		 
		byte_count -= 2; 
		} 
	 
	// | Add in udp/tcp pseudo-header, maybe 
	if(ip_packet) 
		{ 
		result += nr_n2h16(ip_packet->source_ip_address & 0x0000ffff); 
		result += nr_n2h16(ip_packet->source_ip_address >> 16); 
		result += nr_n2h16(ip_packet->destination_ip_address & 0x0000ffff); 
		result += nr_n2h16(ip_packet->destination_ip_address >> 16); 
		result += ip_packet->protocol; 
		result += pseudo_length; 
		} 
 
	 
	// | strangely, tcp/ip checksums require 
	// | that the carry-bit be added back in. 
	// | And, do it twice, to account for the case 
	// | where adding the halves overflows. You 
	// | only need to do it twice at most. Really. 
	// | 
 
	result = (result & 0xffff) + (result >> 16); 
	result = (result & 0xffff) + (result >> 16); 
 
	// | 
	// | Never have checksum == 0 
	// | 
 
	if(result == 0) 
		result = 0xffff; 
 
	return ~(nr_h2n16((host_16)result)); 
	} 
 
#if PLUGS_PING 
// -------------------------------------------------------- 
// ICMP Handler 
// 
// This handler only responds to one thing: a ping (echo request) 
// 
 
#define k_plugs_ping_reply_size 1536 
 
static int r_plugs_icmp_proc 
		( 
		int plug_handle, 
		void *context, 
		ns_plugs_packet *p, 
		void *payload, 
		int payload_length 
		) 
	{ 
	char response_b[k_plugs_ping_reply_size]; // (raw bytes) upper bound for ping we can reply to! 
	ns_plugs_icmp_packet *icmpp = payload; 
	ns_plugs_icmp_packet *response = (ns_plugs_icmp_packet *)response_b; 
	if(icmpp->type == ne_plugs_icmp_ping_request) 
		{ 
		char *ws,*wd; 
		int i; 
		ns_plugs_ip_packet *ipp; 
 
		// | copy payload portion of ping request 
		ws = payload; 
		ws += 4; 
		wd = response->payload; 
 
		if(payload_length >= k_plugs_ping_reply_size) 
			payload_length = k_plugs_ping_reply_size; 
 
		for(i = payload_length - 4; i > 0; i--) 
			*wd++ = *ws++; 
			 
		response->type = ne_plugs_icmp_ping_reply; 
		response->code = 0; 
		 
		// | Send the almost-identical packet on out 
		// | Use send_to to send it to the originator 
 
		ipp = p[ne_plugs_ip].header; 
		nr_plugs_send_to(plug_handle,response,payload_length,0,ipp->source_ip_address,0); 
		} 
	 
	return 0; 
	} 
#endif // PLUGS_PING 
 
 
// --------------------------------------------------------- 
 
// Arp Handler 
// 
// When we get an arp "reply" packet, 
// we'll save its information into the 
// cache IF there is an entry for it already 
// there. 
// When we get an arp "request" packet, 
// we'll reply to it if its about us. 
 
int r_plugs_arp_proc 
		( 
		int plug_handle, 
		void *context, 
		ns_plugs_packet *p, 
		void *payload, 
		int payload_length 
		) 
	{ 
	ns_plugs_arp_packet *ap = payload; 
	ns_plugs_arp_packet response; 
	s_plugs_adapter_entry *a = context; 
 
	switch(ap->op) 
		{ 
		case nm_h2n16(1):		// arp request 
			if(a->settings.ip_address == ap->target_ip_address) 
				{ 
				// | Request for us! 
				// | construct an answer and send it. 
 
				response.hardware_type = nm_h2n16(1); 
				response.protocol_type = nm_h2n16(0x0800); 
				response.hardware_size = 6;		// 6 for ethernet 
				response.protocol_size = 4;		// 4 for IP 
				response.op = nm_h2n16(2);			// reply 
				response.sender_ethernet_address = a->settings.ethernet_address; 
				response.sender_ip_address = a->settings.ip_address; 
				response.target_ethernet_address = ap->sender_ethernet_address; 
				response.target_ip_address = ap->sender_ip_address; 
 
				nr_plugs_send 
						( 
						plug_handle, 
						&response, 
						28, 
						ne_plugs_flag_ethernet_broadcast 
						); 
				} 
			break; 
 
		case nm_h2n16(2):		// arp reply 
			// | 
			// | walk through our arp cache, and see if we want this reply 
			// | 
				{ 
				s_plugs_arp_cache_entry *w; 
				int i; 
 
				w = a->arp_cache; 
				for(i = 0; i < nk_plugs_arp_cache_count; i++) 
					{ 
					if(w->ip_address == ap->sender_ip_address) 
						w->ethernet_address = ap->sender_ethernet_address; 
					w++; 
					} 
				} 
			break; 
			} // switch 
	return 0; 
	} 
 
// Nothing wrong with two plugs listening 
// on the same port. No sir! Perfectly 
// legal. But we wont let it happen 
// accidentally. 
 
#define nk_plugs_minimum_fresh_port_number 32769 
 
net_16 get_port_number(s_plugs_globals *g) 
	{ 
	int v; 
	s_plugs_plug *plug; 
	int i; 
 
try_again: 
	v = g->port_number++; // will be zero the very first time... 
	if(v == 0) 
		v = g->port_number = nk_plugs_minimum_fresh_port_number; 
	 
	v = nr_h2n16(v); 
	 
	plug = g->plug; 
	for(i = 0; i < PLUGS_PLUG_COUNT; i++) 
		if(v == (int)plug++->local_port) 
			goto try_again; 
 
	return v; 
	} 
 
 
 
 
 
 
 
 
 
 
 
// +--------------------------------------- 
// | This routine takes the index of an uninitialized 
// | adapter. The "network settings" have already been 
// | stashed in it, but only the Ethernet address is 
// | relevant for now. 
// | 
// | This routine will initialize the adapter (low level), 
// | and perform a dhcp lookup using it. 
// | 
// | If successful, it will fill in the dhcp-assigned 
// | network settings in the adapter's settings, and 
// | return zero-or-positive. 
// | 
// | If unsuccessful, will return a negative value 
// | 
 
static int r_plugs_adapter_dhcp(int adapter_index) 
    { 
	s_plugs_globals *g = &ng_plugs_globals; 
	s_plugs_adapter_entry *a = &g->adapter[adapter_index]; 
    ns_plugs_network_settings ns; // for dhcp routine to fill in... 
    int result = 0; 
 
    // | 
    // | Step 1: initialize the adapter hardware 
    // | 
 
	result = (a->adapter_description->reset_proc) 
			( 
			a->adapter_address, 
			&a->adapter_storage, 
			&a->settings 
			); 
	if(result < 0) 
		goto go_home; 
 
#if PLUGS_IRQ 
	if((a->adapter_irq != 0) && (a->adapter_description->set_irq_proc)) 
		{ 
		nr_installuserisr2(a->adapter_irq,r_plugs_irq_handler,(int)a); 
		result = (a->adapter_description->set_irq_proc) 
				( 
				a->adapter_address, 
				&a->adapter_storage, 
				1 
				); 
        if(result < 0) 
            goto go_home; 
		} 
#endif // PLUGS_IRQ 
 
    // | Step 2: let the dhcp routine do all the hard work... 
 
    result = r_plugs_dhcp(adapter_index,&ns); 
    if(result < 0) 
        goto go_home; 
 
    // | Step 3: stash the settings received for just us 
 
    a->settings = ns; 
 
go_home: 
    nr_plugs_print_error_message("[r_plugs_adapter]",result); 
    return result; 
    } 
 
 
 
 
 
 
 
 
 
int nr_plugs_initialize 
		( 
		long flags, 
		ns_plugs_network_settings *network_settings, 
		void *adapter_address, 
		int adapter_irq, 
		ns_plugs_adapter_description *adapter_description 
		) 
	{ 
	s_plugs_globals *g = &ng_plugs_globals; 
	int result = 0; 
	s_plugs_adapter_entry *a; 
    int first_adapter = !(flags & ne_plugs_flag_add_adapter); 
    int adapter_index; 
 
 
	// +--------------------------------- 
	// | Initialize can be used to start up the plugs 
	// | stack from scratch, or to add one more adapter 
	// | 
 
	if(first_adapter) 
		{ 
		// +---------------------- 
		// | clear and reset everything 
		// | Only the 1st adapter gets a DNS socket 
		// | 
 
		nr_zerorange((char *)g,sizeof(s_plugs_globals)); 
 
		// +--------------- 
		// | Mark the globals as Up And Running 
 
		g->running = 1; 
        } 
 
    // +--------------------------------------- 
    // | Allocate the adapter spot, and bump 
    // | the adapter_count. At the end, if there was any problem, we'll 
    // | decrement the adapter_count and return and error 
 
    adapter_index = g->adapter_count++; 
 
	a = &g->adapter[adapter_index]; 
	nr_zerorange((char *)a,sizeof(s_plugs_adapter_entry)); 
 
	a->adapter_address = adapter_address; 
	a->adapter_irq = adapter_irq; 
	a->adapter_description = adapter_description; 
 
	// +----------------- 
	// | Install this adapter's network settings. 
	// | If there's a flash, maybe get settings from flash 
	// | 
 
#ifdef nasys_plugs_persistent_network_settings 
 
	// | 0 means use the stored settings in flash 
 
	if((int)network_settings == 0) 
		network_settings = &nasys_plugs_persistent_network_settings->settings; 
 
#endif // nasys_plugs_persistent_network_settings 
 
	a->settings = *network_settings; 
    a->settings.flags |= flags;      // use the flags passed in, too. 
 
	// +-------------------- 
	// | Install this adapter's arp proc 
	// | Every adapter needs one of these, it is essential for IP 
    // | 
    // | Turns out to be ok to create the plug before the 
    // | possible DHCP negotiation, or hardware initialization. 
 
	result = nr_plugs_create 
			( 
			&a->arp_plug, 
			ne_plugs_arp, 
			1, 
			r_plugs_arp_proc, 
			a, // | use context to hold "our adapter". 
			ne_plugs_flag_ethernet_broadcast + nk_plugs_default_debug + adapter_index 
			); 
    if(result < 0) 
        goto go_home; 
 
#if PLUGS_DHCP 
    // +-------------------------------------- 
    // | If the dhcp flag is set (either in the network_settings 
    // | flags or in the plugs_initialize flags) then call 
    // | the dhcp proc to get network_settings. 
    // | (if so, the settings still needs to start with a unique 
    // | ethernet address; the rest is filled in by dhcp) 
    // | 
 
    if(a->settings.flags & ne_plugs_flag_dhcp)      // includes flags parameter already 
        { 
        result = r_plugs_adapter_dhcp(adapter_index); 
        if(result < 0) 
            goto go_home; 
        } 
 
#endif // PLUGS_DHCP 
 
    // +---------------------------------------- 
    // | Initialize the adapter (possibly for a second time, 
    // | if a dhcp negotiation occurred... 
    // | 
 
	result = (a->adapter_description->reset_proc) 
			( 
			a->adapter_address, 
			&a->adapter_storage, 
			&a->settings 
			); 
	if(result < 0) 
		goto go_home; 
 
#if PLUGS_IRQ 
	// +-------------------- 
	// | If requested and available, install 
	// | the irq handler 
	// | 
 
	if((a->adapter_irq != 0) && (a->adapter_description->set_irq_proc)) 
		{ 
		// | 
		// | Install our handler 
		// | 
 
		nr_installuserisr2(a->adapter_irq,r_plugs_irq_handler,(int)a); 
 
		// | 
		// | Tell adapter to irq us 
		// | 
 
		result = (a->adapter_description->set_irq_proc) 
				( 
				a->adapter_address, 
				&a->adapter_storage, 
				1 
				); 
        if(result < 0) 
            goto go_home; 
		} 
#endif // PLUGS_IRQ 
 
#if PLUGS_DEBUG 
	printf("   [plugs] +-----------------------\n"); 
	printf("   [plugs] | initialized adapter %s at 0x%08x, ", 
			a->adapter_description->adapter_name, 
			a->adapter_address); 
	nr_plugs_print_ip_address(a->settings.ip_address); 
	printf("\n"); 
#endif // PLUGS_DEBUG 
 
    // +---------------------------- 
    // | For the first adapter only, 
    // | (conditionalized code allowing), 
    // | install a plug for doing DNS queries 
    // | 
 
#if PLUGS_DNS 
    if(first_adapter) 
        { 
		// +------ 
		// | Install a DNS handler 
		// | 
 
		result = nr_plugs_create 
				( 
				&g->dns_plug, 
				ne_plugs_udp, 
				0, 
				r_plugs_dns_proc, 
				0, 
				nk_plugs_default_debug); 
        if(result < 0) 
            goto go_home; 
 
		result = nr_plugs_connect 
				( 
				g->dns_plug, 
				0, 
				nr_n2h32(a->settings.nameserver_ip_address), 
				ne_plugs_udp_dns 
				); 
        if(result < 0) 
            goto go_home; 
		} 
#endif // PLUGS_DNS 
 
 
#if PLUGS_PING 
	// +------ 
	// | Install this adapter's ICMP ping handler 
	// | 
 
	result = nr_plugs_create 
			( 
			&a->icmp_plug, 
			ne_plugs_icmp, 
			1, 
			r_plugs_icmp_proc, 
			0, 
			nk_plugs_default_debug + adapter_index 
			); 
        if(result < 0) 
            goto go_home; 
 
	result = nr_plugs_connect 
			( 
			a->icmp_plug, 
			0, 
			-1, 
			-1 // accept from anyone who sends to us 
			); 
        if(result < 0) 
            goto go_home; 
#endif // PLUGS_PING 
 
go_home: 
    if(result < 0) 
        { 
        // +------------------------- 
        // | If there was any problem initializing this adapter, 
        // | we roll it back out. 
        // | 
 
        // (if we had a "destroy adapter" loop, we would iterate 
        // through every plug and destroy any associated with this 
        // adapter index. but we dont, we do it explicitlike here.) 
 
#if PLUGS_PING 
        if(a->icmp_plug) 
            { 
            nr_plugs_destroy(a->icmp_plug); 
            a->icmp_plug = 0; 
            } 
#endif 
 
#if PLUGS_DNS 
        if(first_adapter && g->dns_plug) 
            { 
            nr_plugs_destroy(g->dns_plug); 
            g->dns_plug = 0; 
            } 
#endif 
 
        if(a->arp_plug) 
            { 
            nr_plugs_destroy(a->arp_plug); 
            a->arp_plug = 0; 
            } 
 
        // (reset the adapter only to disable its interrupts) 
        // (if the description proc ptr was bad, we crashed 
        // already and arent running here.) 
 
	    (a->adapter_description->reset_proc) 
			    ( 
			    a->adapter_address, 
			    &a->adapter_storage, 
			    &a->settings 
			    ); 
         
        g->adapter_count--; 
        } 
 
	return result; 
	} 
 
int nr_plugs_terminate(void) 
	{ 
	s_plugs_globals *g = &ng_plugs_globals; 
	int i; 
	s_plugs_adapter_entry *a; 
 
	// | 
	// | Turn off interrupts right now 
	// | 
 
	a = g->adapter; 
	for(i = 0; i < g->adapter_count; i++) 
		{ 
		(a->adapter_description->set_irq_proc) 
				( 
				a->adapter_address, 
				&a->adapter_storage, 
				0 
				); 
		a++; 
		} 
 
	// | 
	// | Delete each plug 
	// | 
 
	for(i = 0; i < PLUGS_PLUG_COUNT; i++) 
		nr_plugs_destroy(i + nk_plugs_plug_number_base); 
 
	// | 
	// | and wipe the globals. 
	// | 
	nr_zerorange((char *)g,sizeof(s_plugs_globals)); 
 
	return 0; 
	} 
 
// +----------------------------- 
// | nr_plugs_force_timeout is intended to allow a 
// | user intervention for certain long synchronous 
// | activities. 
// | 
// | dhcp lookup, for example, has a 60-second timeout, 
// | which is a long time. If you get tired of waiting, 
// | you can call nr_plugs_force_timeout to cut it short, 
// | and the routine will return "failed". 
// | 
// | Has to be called from interrupt level, obviously, since 
// | your foreground process is waiting for 
// | nr_plugs_initialize to return. 
// | 
// | Presently (2003): only affects nr_plugs_initialize 
// | if dhcp flag is set. 
// | 
 
int nr_plugs_force_timeout 
        ( 
        void 
        ) 
    { 
    ng_plugs_globals.force_timeout = 1; 
    return 0; 
    } 
 
 
int nr_plugs_get_settings 
		( 
		int adapter_index, 
		ns_plugs_network_settings *network_settings_out 
		) 
	{ 
	s_plugs_globals *g = &ng_plugs_globals; 
	s_plugs_adapter_entry *a = &g->adapter[adapter_index]; 
 
	*network_settings_out = a->settings; 
	return 0; 
	} 
 
int ethernet_address_equal(net_48 *ea1,net_48 *ea2) 
	{ 
	return ea1->u32 == ea2->u32 
			&& ea1->l16 == ea2->l16; 
	} 
 
// 
// Accept ip match, or filter = -1 gets everything 
int ip_address_equal(net_32 packet_ip_address,net_32 filter_ip_address) 
	{ 
	return (filter_ip_address == 0xffffffff) || (packet_ip_address == filter_ip_address); 
	} 
 
// 
// Accept ip match, or filter = -1 gets everything 
int port_number_equal(net_16 packet_port,net_16 filter_port) 
	{ 
	return (filter_port == 0xffff) || (packet_port == filter_port); 
	} 
 
// debug routine: get_protocol_name 
#if PLUGS_DEBUG 
char *get_protocol_name(int protocol) 
	{ 
	switch(protocol) 
		{ 
		case ne_plugs_ethernet: 
					return "ethernet"; 
		case ne_plugs_arp: 
					return "arp"; 
		case ne_plugs_ip: 
					return "ip"; 
		case ne_plugs_icmp: 
					return "icmp"; 
		case ne_plugs_udp: 
					return "udp"; 
		case ne_plugs_tcp_raw: 
		case ne_plugs_tcp: 
					return "tcp"; 
		default: 
					return "unknown"; 
		} 
	} 
#endif // PLUGS_DEBUG 
 
// nr_plugs_dispatch_packet 
// 
// Examine an ethernet packet and find out what protocol 
// it is.  
// Then loop through every plug and see which ones should 
// receive this packet. 
// 
// This routine is called by an adapters polling proc. 
// 
int nr_plugs_dispatch_packet(void *pp,int packet_length,void *context) 
	{ 
	ns_plugs_ethernet_packet *packet = pp;	// only one link_layer_type today. 
	s_plugs_globals *g = &ng_plugs_globals; 
	//int is_protocol[ne_plugs_last_protocol]; 
	ns_plugs_packet sub_protocol[ne_plugs_last_protocol]; 
	int i; 
	s_plugs_plug *plug; 
	int p; 
	long plug_bitmask; 
	int got_tcp = 0; // so only one tcp plug gets a tcp packet 
	s_plugs_adapter_entry *a = context; 
         
 
	// | since most interesting packets are ip-derivative, 
	// | we figure out the sizes &c and put them in these vars 
	// | 
 
	ns_plugs_ip_packet *ip_packet = 0; // if incoming packet is ip or better... 
	void *past_ip_packet = 0; // if incoming packet is ip or better 
	int ip_payload_length = 0; 
 
	// | Not running? go home now. 
	// | 
 
	if(!g->running) 
		return ne_plugs_error_not_initialized; // | plugs not initialized 
 
	g->in_dispatch++; 
 
	// | 
	// | Start with no protocol set 
 
	for(i = 0; i < ne_plugs_last_protocol; i++) 
		{ 
		sub_protocol[i].header = 0; 
		sub_protocol[i].length = 0; 
		} 
 
	// | 
	// | Every packet is "ethernet" 
 
	sub_protocol[ne_plugs_ethernet].header = (void *)packet; 
	sub_protocol[ne_plugs_ethernet].length = packet_length; 
 
	// | 
	// | Check progressive layers of protocol numbers 
 
	p = nm_n2h16(packet->type); 
 
	if(p == ne_plugs_ethernet_arp) 
		{ 
		sub_protocol[ne_plugs_arp].header = &packet->payload; 
		sub_protocol[ne_plugs_arp].length = 28; 
		} 
	else if(p == ne_plugs_ethernet_ip) 
		{ 
		// 
		// | It's an IP packet! but what kind? 
 
		ip_packet = (ns_plugs_ip_packet *)packet->payload; 
 
#if PLUGS_DEBUG 
	if (ip_packet->flags_fragment & nm_h2n16(0xbfff)) 
		printf("   [plugs] | Ignored fragmented IP packet (0x%04x)\n",ip_packet->flags_fragment); 
#endif // PLUGS_DEBUG 
 
		if 
				( 
				((ip_packet->version_header_length & 0x00f0) == 0x0040)	// ipv4 only 
				&& 
				((ip_packet->flags_fragment & nm_h2n16(0xbfff)) == 0)	// ignore fragments! 
				) 
			{ 
			int ip_packet_length; 
			void *ip_payload; 
 
			// | stash the common ip packet info 
			// | 
	 
			ip_packet_length = nr_n2h16(ip_packet->length); 
			sub_protocol[ne_plugs_ip].header = (void *)ip_packet; 
			sub_protocol[ne_plugs_ip].length = ip_packet_length; 
 
			// temporarily put header-length into this ip_payload_length variable 
			ip_payload_length = (ip_packet->version_header_length & 0x0f) * 4; 
			past_ip_packet = (void *)((int)ip_packet + ip_payload_length); 
 
			ip_payload = ((void *)ip_packet) + ip_payload_length; 
 
			// And now put the actual payload length into it 
			ip_payload_length = ip_packet_length - ip_payload_length; 
 
			if(ip_packet->protocol == ne_plugs_ip_icmp) 
				{ 
				sub_protocol[ne_plugs_icmp].header = ip_payload; 
				sub_protocol[ne_plugs_icmp].length = ip_payload_length; 
				} 
			else if(ip_packet->protocol == ne_plugs_ip_udp) 
				{ 
				sub_protocol[ne_plugs_udp].header = ip_payload; 
				sub_protocol[ne_plugs_udp].length = ip_payload_length; 
				} 
			else if(ip_packet->protocol == ne_plugs_ip_tcp) 
				{ 
				sub_protocol[ne_plugs_tcp_raw].header = ip_payload; 
				sub_protocol[ne_plugs_tcp_raw].length = ip_payload_length; 
				sub_protocol[ne_plugs_tcp].header = ip_payload; 
				sub_protocol[ne_plugs_tcp].length = ip_payload_length; 
				} 
			} 
		} 
	 
	// | 
	// | The array sub_protocol now shows what protocol(s) 
	// | this packet is. 
 
	// | Loop through the plugs and call any 
	// | plug that has a receiver-proc and wants this packet. 
	// | 
 
	plug_bitmask = 0;				// start with no plug-recipients 
	got_tcp = 0; // at most one tcp plug gets each tcp packet 
 
	plug = g->plug; 
	for(i = 0; i < PLUGS_PLUG_COUNT; i++) 
		{ 
		// | 
		// | Each plug is associated with a particular adapter. 
		// | We only look at plugs for the adapter that got 
		// | this packet, else skip along. 
		// | 
 
		if(a != &g->adapter[plug->flags & ne_plugs_flag_adapter_index_mask]) 
			goto next_plug; 
 
 
#if PLUGS_DEBUG 
		if(plug->flags & ne_plugs_flag_debug_rx) 
			{ 
			int j; 
 
			// Dont print the "ignores" messages unless debug level 2 
			if( sub_protocol[plug->protocol].header || (PLUGS_DEBUG & 2) ) 
				{ 
				for(j = ne_plugs_last_protocol - 1; j > 0; j--) 
					if(sub_protocol[j].header) 
						break; 
				printf("   [plugs] +-----------------------\n"); 
				printf("   [plugs] | plug %d (%s): %s a %d byte %s packet\n",i, 
					get_protocol_name(plug->protocol), 
					sub_protocol[plug->protocol].header ? "examines" : "ignores", 
					packet_length, 
					get_protocol_name(j)); 
				} 
			} 
#endif // PLUGS_DEBUG 
		if(sub_protocol[plug->protocol].header) 
			{ 
			void *payload = 0; 
			int payload_length = 0; 
			int for_the_plug = 0;		// well check this later 
			int ip_address_match; 
			int ethernet_address_match; 
			int port_match = 0; 
			ns_plugs_udp_packet *udpp = 0; // set to packet for udp OR tcp, for port numbers 
 
			// First things first: 
			// Do we even care the slightest bit about 
			// this ethernet packet? 
			// The hardware may or may not already 
			// be in promiscuous mode, but regardless, 
			// if the packet gets to this routine, we 
			// should verify that we want it. 
 
			ethernet_address_match = 
					((plug->flags & ne_plugs_flag_ethernet_all) != 0) 
					|| 
						( 
						((plug->flags & ne_plugs_flag_ethernet_broadcast) != 0) 
						&& 
						(packet->destination_address.u32 == 0xffffFFFF) 
						&& 
						(packet->destination_address.l16 == 0xffff) 
						) 
					|| 
						ethernet_address_equal 
								(&packet->destination_address, 
								&a->settings.ethernet_address 
								); 
 
			// 
			// Since ip, icmp, udp, and tcp all rely on 
			// matching source & destination ip addresses, 
			// we check those once out here, if the 
			// packet is ip flavored. 
 
			ip_address_match = 
					ethernet_address_match 
					&& 
                    (packet->type == nm_h2n16(ne_plugs_ethernet_ip)) 
                    && 
                        ( 
                            ((plug->flags & ne_plugs_flag_ip_all) != 0) 
                            || 
                            ( 
					            ip_address_equal 
							            ( 
							            ip_packet->destination_ip_address, 
							            a->settings.ip_address 
							            ) 
					            && 
					            ip_address_equal 
							            ( 
                                        ip_packet->source_ip_address, 
							            plug->remote_ip_address 
                                        ) 
                            ) 
                        ); 
 
			// 
			// Since udp & tcp rely on port matching, we'll do that here, too 
			// udp & tcp have port info in same position (thankyou!) 
 
			if(sub_protocol[ne_plugs_udp].header || sub_protocol[ne_plugs_tcp_raw].header) 
				{ 
				// | 
				// | Get port number from udp or tcp header -- first bytes 
				// | are port numbers in either case, so cast as udp header. 
				// | 
 
				udpp = sub_protocol[ne_plugs_udp].header; 
				if(!udpp) 
					udpp = sub_protocol[ne_plugs_tcp_raw].header; 
				port_match = 
						ip_address_match 
						&& 
						port_number_equal 
								( 
								udpp->destination_port, 
								plug->local_port 
								) 
						&& 
						port_number_equal 
								( 
								udpp->source_port, 
								plug->remote_port 
								); 
				} 
 
#if PLUGS_DEBUG 
			if(plug->flags & ne_plugs_flag_debug_rx) 
                { 
				printf("   [plugs] | "); 
				if(sub_protocol[ne_plugs_udp].header || sub_protocol[ne_plugs_tcp_raw].header) 
					printf("port_match = %d, ",port_match); 
				if(sub_protocol[ne_plugs_ip].header) 
					printf("ip_address_match = %d, ",ip_address_match); 
				printf("ethernet_address_match = %d\n",ethernet_address_match); 
				//if(sub_protocol[ne_plugs_ip].header && !ip_address_match) 
					{ 
					printf("   [plugs] |      packet source ip address: "); 
					nr_plugs_print_ip_address(ip_packet->source_ip_address); 
 
                    if(udpp) 
                        printf("/%d",nr_n2h16(udpp->source_port)); 
 
					printf("\n   [plugs] | packet destination ip address: "); 
					nr_plugs_print_ip_address(ip_packet->destination_ip_address); 
 
                    if(udpp) 
                        printf("/%d",nr_n2h16(udpp->destination_port)); 
 
					printf("\n   [plugs] |              local ip address: "); 
					nr_plugs_print_ip_address(a->settings.ip_address); 
                    printf("/%d",nr_n2h16(plug->local_port)); 
 
					printf("\n   [plugs] |             remote ip address: "); 
					nr_plugs_print_ip_address(plug->remote_ip_address); 
                    printf("/%d",nr_n2h16(plug->remote_port)); 
					printf("\n"); 
					} 
				//if(!ethernet_address_match) 
					{ 
					printf("   [plugs] |      packet source ethernet address: "); 
					nr_plugs_print_ethernet_address(&packet->source_address); 
					printf("\n   [plugs] | packet destination ethernet address: "); 
					nr_plugs_print_ethernet_address(&packet->destination_address); 
					printf("\n   [plugs] |              local ethernet address: "); 
					nr_plugs_print_ethernet_address(&a->settings.ethernet_address); 
					printf("\n"); 
					} 
				} 
 
#endif // PLUGS_DEBUG 
			// 
			// The packet is a type this plug listens to. 
			// Check out other filtration (ethernet address, 
			// port numbers, &c.) 
 
			switch(plug->protocol) 
				{ 
				case ne_plugs_ethernet:		// Ethernet proc gets whole packet 
					// 
					// Filter by ethernet address 
 
					if(ethernet_address_match) 
						{ 
						payload = packet; 
						payload_length = packet_length; 
						for_the_plug = 1; 
						} 
					break; 
 
				case ne_plugs_arp:		// ARP proc gets ARP header 
					// 
					// Filter by ethernet address 
 
					if(ethernet_address_match) 
						{ 
						payload = packet->payload; 
						payload_length = sizeof(ns_plugs_arp_packet); 
						for_the_plug = 1; 
						} 
					break; 
 
				case ne_plugs_icmp:	// ICMP proc gets ICMP header, IP gets own payload 
				case ne_plugs_ip: 
					if(ip_address_match) 
						{ 
						payload = past_ip_packet; 
						payload_length = ip_payload_length; 
						for_the_plug = 1; 
						} 
					break; 
 
				case ne_plugs_udp: 
					if(port_match) 
						{ 
						payload = ((ns_plugs_udp_packet *)past_ip_packet)->payload; 
						payload_length = ip_payload_length - sizeof(ns_plugs_udp_packet); 
						for_the_plug = 1; 
						} 
					break; 
 
				case ne_plugs_tcp_raw: 
#if PLUGS_TCP 
				case ne_plugs_tcp: 
#endif // PLUGS_TCP 
					if(port_match && !got_tcp) 
						{ 
						payload = past_ip_packet; 
						payload = (void *)((int)payload 
								+ (nr_n2h16(((ns_plugs_tcp_packet *)past_ip_packet)->header_length_flags) >> 12) * 4); 
						payload_length = nr_n2h16(ip_packet->length) 
								- ((int)payload - (int)ip_packet); 
 
#if PLUGS_TCP 
						if(plug->protocol == ne_plugs_tcp) 
							{ 
							// | Handle specially elsewhere 
 
							int r; 
 
							r = r_tcp_dispatch( 
									a, 
									i + nk_plugs_plug_number_base, 
									plug, 
									sub_protocol, 
									payload, 
									payload_length); 
 
							// | only one tcp plug can get each 
							// | tcp packet to us. 
							if(!r) 
								got_tcp = 1; 
	 
							} 
						else 
#endif // PLUGS_TCP 
							{ 
							for_the_plug = 1; 
							got_tcp = 1; 
							} 
			// The interaction between tcp and tcp_raw is 
			// tricky. A tcp_raw plug *could* steal your 
			// regular tcp packet, after all! best not to 
			// mix them, you know? 
						} 
					break; 
 
				} // switch 
 
			// 
			// If we've determined that this packet should 
			// be received by this plug, then either 
			// call the plug's callback proc right now, 
			// or mark the packet to be received on demand 
			// by the plug. 
 
			if(for_the_plug) 
				{ 
#if PLUGS_DEBUG 
				if(plug->flags & ne_plugs_flag_debug_rx) 
					{ 
					printf("   [plugs] | packet accepted for plug\n"); 
					nr_plugs_print_ethernet_packet(packet,packet_length,"   [plugs] | "); 
					} 
#endif // PLUGS_DEBUG 
 
				// We know *some* plug wants it. 
				// If it happens to be in IP packet 
				// of any kind, let's just make sure 
				// that we have the originator in 
				// our ARP cache. Easy info! 
 
				if(sub_protocol[ne_plugs_ip].header) 
					r_add_to_arp_cache_from_ip_packet(a,packet); 
 
				if(plug->callback_proc) 
					{ 
#if PLUGS_DEBUG 
					if(plug->flags & ne_plugs_flag_debug_rx) 
						printf("   [plugs] | packet dispatched to callback proc %08x\n", 
								plug->callback_proc); 
#endif // PLUGS_DEBUG 
 
                    // Check to see if an exclusive plug is set. If it 
                    // is, only call callback routines for the exclusive plug 
                    // and the APR plug. 
                    // We must also always respond to ARP packets, otherwise, 
                    // The sender might lose track of who we are. 
                    if ((g->exclusive_plug_handle == 0) ||  
                        (plug==r_plug_from_handle(g->exclusive_plug_handle)) || 
                        (plug == r_plug_from_handle (a->arp_plug))) 
                        { 
					    (plug->callback_proc) 
							(i + nk_plugs_plug_number_base, 
							plug->callback_context, 
							sub_protocol, 
							payload, 
							payload_length); 
                        } 
					} 
				else 
					plug_bitmask |= 1 << i; 
				} 
 
#if PLUGS_DEBUG 
			else 
				{ 
				if((plug->flags & ne_plugs_flag_debug_rx) 
						&& !got_tcp) 
					{ 
					printf("   [plugs] | packet not sent to plug\n"); 
					} 
				} 
#endif // PLUGS_DEBUG 
 
			} // packet protocol include plug protocol 
 
	next_plug: 
		plug++; 
		} // for (i), each plug 
 
#if PLUGS_TCP 
	// | 
	// | If it was a tcp packet to this IP address, and 
	// | nobody handled it, reply with an incisive RST 
	// | 
	if(sub_protocol[ne_plugs_tcp].header 
			&& ethernet_address_equal 
				( 
				&packet->destination_address, 
				&a->settings.ethernet_address 
				) 
			&& ip_address_equal(ip_packet->destination_ip_address,a->settings.ip_address) 
			&& !got_tcp) 
		{ 
		int tcp_flags; 
		ns_plugs_tcp_packet *tcp_packet; 
		// | 
		// | Make sure we have the ethernet address 
		// | for this packet before we try to send 
		// | 
 
		tcp_packet = sub_protocol[ne_plugs_tcp].header; 
		tcp_flags = nr_n2h16(tcp_packet->header_length_flags); 
 
		if(!(tcp_flags & ne_plugs_flag_tcp_rst)) 
			{ 
			// | 
			// | But, don't respond to rst with rst! 
			// | 
			r_add_to_arp_cache_from_ip_packet(a,packet); 
 
                        // nr_plugs_print_ethernet_packet 
                        //  ( 
                        //   packet, 
                        //   packet_length, 
                        //   "NO: " 
                        //   ); 
 
			r_tcp_send(0,0, 
					ne_plugs_flag_tcp_rst | ne_plugs_flag_tcp_ack, 
					sub_protocol,0); 
			} 
		} 
#endif // PLUGS_TCP 
 
	// If any plug_bitmask, and queing enabled, queue the packet 
go_home: 
 
	g->in_dispatch--; 
 
	return 0; 
	} 
 
int nr_plugs_create(int *plugHandleOut, 
		int protocol, 
		host_16 port, 
		nr_plugs_receive_callback_proc callback, 
		void *callback_context, 
		long flags) 
	{ 
	s_plugs_globals *g = &ng_plugs_globals; 
	int i; 
	s_plugs_plug *plug; 
 
	if((!g->running) 
            || ( ((unsigned long)(flags & ne_plugs_flag_adapter_index_mask)) >= ((unsigned long)g->adapter_count)) ) 
		return ne_plugs_error_not_initialized; // uninitialized error 
 
	port = nr_h2n16(port); 
 
	// port number zero means "give me a fresh port number" 
	if(port == 0) 
		port = get_port_number(g); 
 
 
	plug = g->plug; 
 
	for(i = 0; i < PLUGS_PLUG_COUNT; i++) 
		{ 
		if(plug->protocol == 0) 
			{ 
			// 
			// We found an unused plug, lets use it. 
			// (??? how to mark it until its connected?) 
			// 
 
			// zero out all fields? 
 
			plug->protocol = protocol; 
			plug->local_port = port; 
			plug->callback_proc = callback; 
			plug->callback_context = callback_context; 
			plug->flags = flags; 
 
#if PLUGS_TCP 
			plug->tcp_state = e_plugs_tcp_inert; 
                        plug->tcp_manual_window_size = 1024; 
                        plug->tcp_sn_local = wellspring_of_sequence_numbers(); 
 
                        // This statement amounts to: "the current  
                        //  sequence number hasn't been acknowledged yet." 
                        plug->tcp_last_acknowledged_sn_local = 
                          plug->tcp_sn_local - 0x100; 
#endif // PLUGS_TCP 
 
			*plugHandleOut = i + nk_plugs_plug_number_base; 
	 
			return 0; 
			} 
 
		plug++; 
		} 
 
	return ne_plugs_error_too_many_plugs; 
	} 
 
int nr_plugs_destroy(int plug_handle) 
	{ 
	int result = 0; 
	s_plugs_globals *g = &ng_plugs_globals; 
	s_plugs_plug *plug = r_plug_from_handle(plug_handle); 
 
	// | First, we'll close the connection 
	// | (Only does anything if it happens to 
	// | be a tcp plug, and then only if tcp is 
	// | even in the build.) 
 
#if PLUGS_TCP 
	result = nr_plugs_connect(plug_handle,0,0,0); 
#endif 
 
	// | "Destroying" a plug is easy: just 
	// | mark it as unused, and everyone 
	// | will ignore it. 
 
	plug->protocol = 0; 
	return result; 
	} 
 
// +----------------------- 
// | nr_plugs_tcp_close 
// |  
// | Graceful closure of TCP connection 
// |  
// | We send a FIN.  We either do or don't wait for the other side to  
// | ACK, depending on how polite the caller wants to be. 
// |  
nr_plugs_tcp_close (int plug_handle, unsigned int ack_wait_timeout) 
{ 
#if PLUGS_TCP 
  s_plugs_plug* plug; 
  int result; 
  unsigned long t_now; 
  unsigned long t_start; 
 
  plug = r_plug_from_handle(plug_handle); 
 
  // If this isn't a TCP plug, or it wasn't connected in the first 
  // place, then it was a short (and successful) trip. 
  // 
  if((plug->protocol != ne_plugs_tcp) || 
     (plug->tcp_state <= e_plugs_tcp_listening)) 
    { 
      return 0;    // Consider it closed. 
    } 
 
  plug->tcp_state = e_plugs_tcp_disconnecting; 
 
  // Send a FIN packet. 
  // 
  result = r_tcp_send ( 
                       plug_handle, 
                       plug, 
                       ne_plugs_flag_tcp_fin | ne_plugs_flag_tcp_ack, 
                       0, 
                       0 
                       ); 
 
  if (result < 0)  
    return result; 
 
  // We expect them to ACK the FIN we just sent them. 
  // 
  if (ack_wait_timeout > 0) { 
    result = r_tcp_wait_for_ack (plug, ack_wait_timeout); 
    if (result)  
      return result; 
  } 
 
  // Now we expect them to send a FIN of their own. 
  // When they send the FIN, the TCP incoming-packet-handling 
  // routine (r_tcp_dispatch) will see it and flip our state to  
  // "inert."  All we have to do is watch this variable. 
  // 
  t_start = nr_timer_milliseconds(); 
  t_now = t_start; 
 
  while (t_now - t_start < ack_wait_timeout){ 
    nr_plugs_idle(); 
    if (plug->tcp_state != e_plugs_tcp_disconnecting) { 
      // Plug went "inert" because  a FIN came in. 
      return 0; 
    } 
    t_now = nr_timer_milliseconds(); 
  } 
 
  // If we got to here: we never got our FIN from the other guy. 
  return -1; 
#else // PLUGS_TCP 
  // this shouldn't be called when PLUGS_TCP is zero, 
  // but we will return zero if it is 
  return 0; 
#endif // PLUGS_TCP 
} 
 
 
// 
// nr_plugs_connect 
// 
// Some kinds of port _must_ be connected  
// before they can send or receive packets. 
 
int nr_plugs_connect(int plug_handle, 
		char *remote_name, 
		host_32 remote_ip_address, 
		host_16 remote_port) 
	{ 
	s_plugs_globals *g = &ng_plugs_globals; 
	s_plugs_plug *plug; 
	int result = 0; 
 
	plug = r_plug_from_handle(plug_handle); 
 
	if(remote_name) 
		{ 
		result = nr_plugs_name_to_ip(remote_name,&remote_ip_address); 
		if(result) 
			goto go_home; 
		remote_ip_address = nr_n2h32(remote_ip_address); 
		} 
 
#if PLUGS_TCP 
	// | 
	// | Any existing TCP connection? 
	// | Close it. We do a partial 
	// | close: we send a FIN and pretend 
	// | we're completely done. When they 
	// | send their ACK and FIN, we'll just 
	// | slam them with a RST. This is 
	// | impolite, but happens to be what 
	// | Windows NT does, too, so I don't 
	// | feel too bad, actually. -- dvb 
	// | 
	if((plug->protocol == ne_plugs_tcp) 
			&& (plug->tcp_state > e_plugs_tcp_listening)) 
		{ 
                  // Use zero-timeout: don't wait for acknowledge. 
                  printf ("Connect: already open.  Closing\n"); 
                  result = nr_plugs_tcp_close (plug_handle, 0); 
                  if(result) 
                    goto go_home; 
		} 
#endif // PLUGS_TCP 
 
	plug->remote_ip_address = nr_h2n32(remote_ip_address); 
	plug->remote_port = nm_h2n16(remote_port); 
 
#if PLUGS_TCP 
	// | 
	// | Open a new connection? 
	// | They may have said connect to 0.0.0.0/0, 
	// | which just means "hang up and forget it." 
	// | If not, well, initiate connection. 
	// | 
 
	if((plug->protocol == ne_plugs_tcp) && plug->remote_ip_address) 
		{ 
		int retry_count; 
		long t0,t; 
 
		plug->tcp_sn_local = wellspring_of_sequence_numbers(); 
 
		for(retry_count = 0; retry_count < nk_plugs_tcp_retry_count; retry_count++) 
			{ 
			t0 = nr_timer_milliseconds(); 
 
			result = r_tcp_send(plug_handle,plug,ne_plugs_flag_tcp_syn,0,0); 
			if(result) 
				goto go_home; 
			plug->tcp_state = e_plugs_tcp_connecting; 
                        printf ("\n[%08X]: Connecting... |", plug); 
 
 
			while(nr_timer_milliseconds() - t0 < nk_plugs_tcp_retry_interval) 
				{ 
				nr_plugs_idle(); 
				if(plug->tcp_state == e_plugs_tcp_connected) 
                                   goto done_trying_to_connect; 
				if(plug->tcp_state == e_plugs_tcp_inert) 
					{ 
					result = ne_plugs_error_tcp_connection_refused; 
                                         
					goto done_trying_to_connect; 
					} 
					 
				} 
			} 
		result = ne_plugs_error_tcp_timed_out; 
                printf ("\n[%08X]: Connection timed out. |"); 
                 
done_trying_to_connect: 
		; 
		} 
#endif // PLUGS_TCP 
 
 
go_home: 
	return result; 
	} 
 
// +-------- 
// | Bracketing the tcp-specific routines... 
 
#if PLUGS_TCP 
 
// +------------------------ 
// | nr_plugs_get_tcp_local_sequence_number 
// | 
// | For your plug (specified by-handle), gives  
// | the current local sequence number.  Zero if not connected. 
// |  
unsigned long nr_plugs_get_tcp_local_sequence_number (int plug_handle) 
{ 
  s_plugs_plug* plug = r_plug_from_handle (plug_handle); 
   
  // Simple relevance check: 
  if ((!plug) || 
       (plug->protocol != ne_plugs_tcp)) 
     return ne_plugs_error_not_initialized; 
 
  if (plug->tcp_state < e_plugs_tcp_connected) 
    return 0; 
   
  return plug->tcp_sn_local; 
} 
 
// +------------------------ 
// | nr_plugs_get_tcp_last_acknowledged_sequence_number 
// | 
// | For your plug (specified by-handle), gives  
// | the last local sequence-number officially acknowledged 
// | by the remote side.  This is the sequence-number of the  
// | last packet known to have been transmitted correctly. 
// |  
unsigned long nr_plugs_get_tcp_last_acknowledged_sequence_number 
(int plug_handle) 
{ 
  s_plugs_plug* plug = r_plug_from_handle (plug_handle); 
   
  // Simple relevance check: 
  if ((!plug) || 
       (plug->protocol != ne_plugs_tcp)) 
     return ne_plugs_error_not_initialized; 
 
  if (plug->tcp_state < e_plugs_tcp_connected) 
    return 0; 
 
  return plug->tcp_last_acknowledged_sn_local; 
} 
 
// +------------------------ 
// | nr_plugs_get_tcp_remote_window_size 
// | 
// | For your plug (specified by-handle), gives  
// | the last window-size officially reported 
// | by the remote side.   
// |  
// | Negative result means: Plug no longer connected. 
// |  
unsigned int nr_plugs_get_tcp_remote_window_size (int plug_handle) 
{ 
  s_plugs_plug* plug = r_plug_from_handle (plug_handle); 
 
  // Simple relevance check: 
  if ((!plug) || 
       (plug->protocol != ne_plugs_tcp)) 
     return ne_plugs_error_not_initialized; 
 
  // If the plug has gotten disconnected for some reason, 
  if (plug->tcp_state < e_plugs_tcp_connected) 
    return -1; 
 
  return plug->tcp_remote_window_size; 
} 
 
 
// +------------------------ 
// | nr_plugs_tcp_rewind_local_sequence_number 
// | 
// | Call this to reset your plug's local sequence number 
// | to a past value (which you provide, on the honor system). 
// |  
// | You only need to do this if you're trying to re-send  
// | a packet that you have reason to believe failed in the  
// | past. 
// |  
int nr_plugs_tcp_rewind_local_sequence_number(int plug_handle,  
                                              unsigned long old_sn_local) 
{ 
  s_plugs_plug* plug = r_plug_from_handle (plug_handle); 
   
  // Simple relevance check: 
  if ((!plug) || 
       (plug->protocol != ne_plugs_tcp)) 
     return ne_plugs_error_not_initialized; 
 
  plug->tcp_sn_local = old_sn_local; 
 
  return 0; 
} 
 
 
// +------------------------ 
// | nr_plugs_wait_for_tcp_acknowledge 
// | 
// | Just a wrapper around r_tcp_wait_for_ack(), 
// | but is more user-friendly because it takes a  
// | plug-handle instead of a plug. 
// |  
 
int nr_plugs_wait_for_tcp_acknowledge  
(int plug_handle,  
 int timeout_milliseconds) 
{ 
  s_plugs_plug* plug = r_plug_from_handle (plug_handle); 
  return r_tcp_wait_for_ack (plug, timeout_milliseconds); 
} 
 
// +------------------------ 
// | r_tcp_wait_for_ack 
// | 
// | Sit around waiting for our most-recent TCP packet to be  
// | acknowledged.   
// | 
// | Returns zero if the packet is acknowledged within the timeout 
// | period--nonzero otherwise. 
// | 
// | Default timeout period is 100ms 
// |  
 
int r_tcp_wait_for_ack 
(s_plugs_plug* plug, 
 unsigned int timeout_milliseconds) 
{ 
  unsigned int t_start = nr_timer_milliseconds(); 
  unsigned int t_now; 
  unsigned int gap; 
 
  if (timeout_milliseconds <= 0) 
    return 0; 
 
  t_now = t_start; 
 
  while ((t_now - t_start) < timeout_milliseconds) { 
    nr_plugs_idle(); 
    if (plug->tcp_last_acknowledged_sn_local == plug->tcp_sn_local) 
      return 0;          // Success! 
     
    // While we're waiting around, someone might send us a RST, 
    // which would result in our immediate disconnection. 
    // That would happen in the r_tcp_dispatch routine that 
    // copes with incoming packets. (during nr_plugs_idle, or IRQs) 
    // 
    if (plug->tcp_state < e_plugs_tcp_connected) 
      return -1; 
     
    t_now = nr_timer_milliseconds(); 
  } 
 
  gap = ((unsigned int) plug->tcp_sn_local) -  
        ((unsigned int) plug->tcp_last_acknowledged_sn_local); 
 
  printf ("  No ack.  local=%d.  Last-ack=%d.  Diff=%d\n", 
          plug->tcp_sn_local, plug->tcp_last_acknowledged_sn_local, 
          gap); 
   
  return -1;  // Timeout.  No ack.  Return "NO" code. 
} 
 
// +--------------------------------------- 
// | Listen -- let a tcp port wait for a connection 
 
int nr_plugs_listen( 
		int plug_handle, 
		nr_plugs_listen_callback_proc listen_proc, 
		void *listen_proc_context 
		) 
	{ 
	s_plugs_globals *g = &ng_plugs_globals; 
	s_plugs_plug *plug; 
 
 
	plug = r_plug_from_handle(plug_handle); 
 
 
	if(plug->tcp_state != e_plugs_tcp_inert) 
		nr_plugs_connect(plug_handle,0,0,0); 
 
		//return ne_plugs_error; 
	 
	plug->tcp_listen_proc = listen_proc; 
	plug->tcp_listen_proc_context = listen_proc_context; 
	plug->tcp_state = e_plugs_tcp_listening; 
 
	plug->remote_ip_address = -1; 
	plug->remote_port = -1; // accept from anyone... 
 
	return 0; 
	} 
 
 
int nr_plugs_tcp_ready_for_next (int plug_handle, int window_size) 
{ 
   s_plugs_plug *plug; 
 
   plug = r_plug_from_handle(plug_handle); 
 
   if (!plug)   
      return ne_plugs_error_not_initialized; 
 
   if (!plug->flags & ne_plugs_flag_tcp_manual_flow) 
      return ne_plugs_error_not_initialized; 
 
   plug->tcp_manual_window_size = window_size; 
   return r_tcp_send(plug_handle,  
                     plug,  
                     ne_plugs_flag_tcp_ack, 
                     0, 
                     0 
                     ); 
 
} 
 
 
 
// +-------------------------------------------- 
// | Utility routine for composing and sending 
// | tcp packets. Mostly it can figure out 
// | what to do just from the flags.  
// |  
// | Exceptions: 
// |     if plug is 0, then payload is actually the 
// |     sub_protocol array. 
// | 
 
int r_tcp_send(int plug_handle,s_plugs_plug *plug,int tcp_flags,void *payload,long payload_length) 
	{ 
	int result; 
 
	unsigned char x[1500]; 
	ns_plugs_tcp_packet *tcp_reply; // reply 
	net_32 destination_ip_address; 
	int send_to_flags = plug->flags & ne_plugs_flag_adapter_index_mask; 
 
        //r_set_irqs_for_plug (plug, 0); 
	tcp_reply = (ns_plugs_tcp_packet *)x; 
	nr_zerorange((char *)tcp_reply,sizeof(ns_plugs_tcp_packet)); 
 
	if(!plug) 
		{ 
		// | 
		// | If plug is 0, the *only* thing we can 
		// | do is send an RST to the sender, presumed 
		// | to be the source of an ip_packet overloading 
		// | the input parameter "payload" 
		// | 
 
		ns_plugs_packet *sub_protocol = payload; 
		ns_plugs_ip_packet *ipp = sub_protocol[ne_plugs_ip].header; // what we reply to 
		ns_plugs_tcp_packet *tcpp = sub_protocol[ne_plugs_tcp].header; 
 
		destination_ip_address = ipp->source_ip_address; 
 
		tcp_reply->source_port = tcpp->destination_port; 
		tcp_reply->destination_port = tcpp->source_port; 
		tcp_reply->sequence_number = tcpp->acknowledgement_number; 
		tcp_reply->acknowledgement_number = nr_h2n32(nr_n2h32(tcpp->sequence_number) + 1); 
 
		tcp_reply->header_length_flags = 
				nr_h2n16(0x5000 | ne_plugs_flag_tcp_rst | ne_plugs_flag_tcp_ack); 
		payload = 0; 
		payload_length = 0; 
		send_to_flags = nk_plugs_default_debug; 
		} 
	else 
		{ 
		destination_ip_address = plug->remote_ip_address; 
 
		tcp_reply->source_port = plug->local_port; 
		tcp_reply->destination_port = plug->remote_port; 
 
 
                if ((plug->flags & ne_plugs_flag_tcp_manual_flow) && 
                    (plug->tcp_state >= e_plugs_tcp_connected)    ) 
                  tcp_reply->window_size = nm_h2n16(plug->tcp_manual_window_size);	 
                else 
                  tcp_reply->window_size = nm_h2n16(1024);	 
 
		// | Always send ack & seq numbers... 
 
		tcp_reply->acknowledgement_number = nr_h2n32(plug->tcp_sn_remote); 
		tcp_reply->sequence_number = nr_h2n32(plug->tcp_sn_local); 
		tcp_reply->header_length_flags = 
				nr_h2n16(0x5000 | (tcp_flags & ne_plugs_flag_tcp_mask)); 
 
		// | If SYN or FIN, bump our local seq by 1, 
		// | else, if PSH, put payload bump local seq by payload size 
 
		if(tcp_flags & (ne_plugs_flag_tcp_syn | ne_plugs_flag_tcp_fin)) 
			plug->tcp_sn_local++; 
		else if(tcp_flags & ne_plugs_flag_tcp_psh) 
			{ 
			// | 
			// | Copy (byte by byte) the payload) 
			// | 
	 
			int i; 
	 
			plug->tcp_sn_local += payload_length; 
			for(i = 0; i < payload_length; i++) 
				tcp_reply->payload[i] = ((char *)payload)[i]; 
			} 
	 
		if(tcp_flags & ne_plugs_flag_tcp_rst) // reset? window is zero, thank you. 
			tcp_reply->window_size = 0;	 
		} 
	 
	// | 
	// | Use the overload-feature of nr_plugs_send, and treat 
	// | our carefully constructed packet as an ip packet, 
	// | since we're giving it the payload already all 
	// | put together. 
	// | 
 
	// | (payload_length now includes tcp header + tcp payload) 
 
	payload_length += sizeof(ns_plugs_tcp_packet); 
	result = nr_plugs_send_to(ne_plugs_ip,tcp_reply,payload_length, 
			ne_plugs_flag_tcp_checksum | send_to_flags, 
			destination_ip_address,tcp_reply->destination_port); 
	 
        // r_set_irqs_for_plug (plug, 1); 
	return result; 
	} 
 
// +----------------------------------------- 
// | Routine for handling non-raw tcp connections 
// | Return 0 if the packet is handled by this 
// | port, else return -1. Whoever calls this 
// | routine may decide to send a tcp RST to 
// | the sender if no plug handles the tcp connection. 
 
int r_tcp_dispatch( 
		s_plugs_adapter_entry *a, 
		int plug_handle, 
		s_plugs_plug *plug, 
		ns_plugs_packet *sub_protocol, 
		void *payload, 
		int payload_length) 
	{ 
	int result = 0; 
	host_32 seq; 
	host_32 ack; 
	host_16 tcp_flags; 
        net_16  remote_window; 
	ns_plugs_ip_packet *ipp = sub_protocol[ne_plugs_ip].header; 
	ns_plugs_tcp_packet *tcpp = sub_protocol[ne_plugs_tcp].header; 
 
	// | 
	// | Ensure that we can respond to this 
	// | packet, mac-address-wise... 
	// | 
 
	r_add_to_arp_cache_from_ip_packet(a,sub_protocol[ne_plugs_ethernet].header); 
 
	// | 
	// | Extract some basic info from the incoming packet 
	// | 
 
	seq = nr_n2h32(tcpp->sequence_number); 
	ack = nr_n2h32(tcpp->acknowledgement_number); 
	tcp_flags = nr_n2h16(tcpp->header_length_flags); 
        remote_window = nr_n2h16(tcpp->window_size); 
	// | 
	// | Handle the various tcp packet kinds in 
	// | an exploded if{do and go home} list 
	// | 
 
	if(tcp_flags & ne_plugs_flag_tcp_syn) // | connection request or confirm 
		{ 
		int x = nr_timer_milliseconds(); 
 
		if(plug->tcp_state == e_plugs_tcp_connecting) 
			{ 
			// | 
			// | We initiated a connection, and 
			// | our remote target has, apparently, 
			// | responded. 
			// | Swell, we are connected. 
			// | 
 
			plug->tcp_sn_remote = seq + 1; 
			result = r_tcp_send(plug_handle,plug,ne_plugs_flag_tcp_ack,0,0); 
			if(!result){ 
                           plug->tcp_state = e_plugs_tcp_connected; 
                           printf ("\n[%08X]: Connection granted. |", plug); 
                        } 
                                 
			} // if connecting & got syn 
		else if(plug->tcp_state == e_plugs_tcp_listening) 
			{ 
			// | 
			// | Listening -- call application acceptance routine 
			// | (Really, it's an application rejection routine, 
			// | since if there isn't one, we automatically accept. 
			// | 
 
			if(plug->tcp_listen_proc) 
				{ 
				result = (plug->tcp_listen_proc) 
						(plug_handle, 
						plug->tcp_listen_proc_context, 
						nr_n2h32(ipp->source_ip_address), 
						nr_n2h16(tcpp->source_port)); 
 
				if(result) 
					goto go_home; 
				} 
 
			plug->tcp_connection_time = x; 
	 
			plug->remote_ip_address = ipp->source_ip_address; 
			plug->remote_port = tcpp->source_port; 
			plug->tcp_sn_remote = seq + 1; 
                        plug->tcp_remote_window_size = remote_window; 
                        plug->tcp_sn_local = 
                          wellspring_of_sequence_numbers(); 
                        plug->tcp_last_acknowledged_sn_local =  
                          plug->tcp_sn_local - 0x100; 
 
			// | 
			// | Send ACK & SYN in return, to accept the connection 
			// | 
 
			result = r_tcp_send(plug_handle,plug,ne_plugs_flag_tcp_ack | ne_plugs_flag_tcp_syn,0,0); 
			if(!result) 
                        { 
                           plug->tcp_state = e_plugs_tcp_connected; 
                        } 
                            
 
			} // if listening & got syn 
		else 
			{ 
			// | 
			// | Not listening or connecting? Then 
			// | a SYN has no business with us. 
			// | Return an error code, we cannot accept. 
			// | 
                          printf ("TCP connection refused.\n"); 
	return_error: 
			result = ne_plugs_error; 
			} 
		goto go_home; 
		} // if syn 
 
	// +--------------------------------- 
	// | If we're not connected, no other packet is interesting 
 
	if(plug->tcp_state <= e_plugs_tcp_listening) 
		{ 
                  goto return_error; 
		} 
 
	 
	// +------------------------------------------- 
	// | If the packet has an ACK in it, we'll check 
	// | it against our own numbers, and maybe resent 
	// | an ack to get back into phase... 
	// | 
	// | We do this before checking the payload, 
	// | because the remote count in the packet 
	// | does not include the current payload. 
	// | 
        // | We also stash-away the local sequence number  
        // | that they're acknowledging.  This lets us  
        // | check (elsewhere) if they've been paying attention. 
 
	if(tcp_flags & ne_plugs_flag_tcp_ack) 
        { 
           // | 
           // | Just sending us an ack? 
           // | eh, ok, great. But if we're out of sync, resend 
           // | our own ack. (This also ignores a FIN if we're 
           // | out of phase.) 
           // | 
	 
           if(seq != plug->tcp_sn_remote) 
           { 
             //printf ("Out-of-sequence (remote) packet detected.  SEQ!\n"); 
              r_tcp_send(plug_handle,plug,ne_plugs_flag_tcp_ack,0,0); 
              goto go_home; 
           } 
           plug->tcp_last_acknowledged_sn_local = ack; 
           plug->tcp_remote_window_size         = remote_window; 
        } 
		 
	// +--------------------------------- 
	// | Second things second: if there's any 
	// | data with the packet -- and there could 
	// | be, even with a FIN packet! -- we must 
	// | pass it straight to the plug's 
	// | callback. 
	 
	if(payload_length) 
		{ 
                  int do_ack_packet = 1; 
		// | 
		// | They're sending us some bytes! 
		// | (By the way, the "PSH" flag is 
		// | irrelevant, it's used to indicate 
		// | that the bytes are special or something.) 
		// | 
 
		if(seq == plug->tcp_sn_remote) 
			{ 
                          int callback_result; 
			// | 
			// | We got some legit bytes, let's acknoweldge 
			// | them and pass the data on to the application. 
			// | 
 
			plug->tcp_sn_remote += payload_length; 
                        plug->tcp_remote_window_size = remote_window; 
                         
                        // This is only meaningful (or useful) for 
                        // manual flow-control, if enabled. 
                        // If manual-flow is disabled, this variable 
                        // is ignored. 
                        // 
			callback_result = (plug->callback_proc) 
                                            (plug_handle, 
                                             plug->callback_context, 
                                             sub_protocol, 
                                             payload, 
                                             payload_length); 
                        if (plug->flags & ne_plugs_flag_tcp_manual_flow) { 
                          if (callback_result >= 0) 
                            // In manual-flow mode, positive result is 
                            // new window-size 
                            // 
                            plug->tcp_manual_window_size = callback_result; 
                          else  
                            // Negative result means: Don't ack this 
                            // one. 
                            do_ack_packet = 0; 
                        } 
 
			} 
		else 
			{ 
			// | 
			// | Something bad has happened: they have a 
			// | sequence number that is not what we expect. 
			// | 
			// | The best we can do is send an ACK for the 
			// | sequence number we think we do want. 
			// | 
			 
			// | (actually, this "else" clause is just a 
			// | comment, to fall to the ack below.) 
			} 
                 
                if (do_ack_packet) 
                  r_tcp_send(plug_handle,plug,ne_plugs_flag_tcp_ack,0,0); 
		} // payload present 
 
	// +--------------------------------- 
	// | FIN 
	// | If they wish to close connection, then do it. 
 
	if (tcp_flags & ne_plugs_flag_tcp_fin) { 
           
          if (plug->tcp_state == e_plugs_tcp_disconnecting) { 
            // We expected this..  We sent a FIN earlier, 
            // and this is their FIN in reply. 
            // We don't need to do anything special, except 
            // close-down the connection, below. 
          }  
 
          // They were good enough to send us a packet... 
          // We have to ack it, even though it's the end. 
          // This protocol sure is polite. 
          // 
          plug->tcp_sn_remote++; // | Bump the ack 
          plug->tcp_remote_window_size = remote_window; 
  
 
 
          if(plug->tcp_state == e_plugs_tcp_connected) { 
            // | OK, they're requesting that the connection  
            // | be closed by sending us an unsolicited FIN. 
            // | Send a FIN of our own, and wait (a little 
            // | while) for their ACK: 
            // | 
  
	    r_tcp_send(plug_handle,plug,ne_plugs_flag_tcp_fin|ne_plugs_flag_tcp_ack,0,0); 
          }else{ 
	    r_tcp_send(plug_handle,plug,ne_plugs_flag_tcp_ack,0,0);                 
          } 
	  r_tcp_plug_set_inert(plug); 
                 
          // | 
          // | Notify the application that the 
          // | tcp connection has been closed 
          // | by sending a payload size of 0. 
          // 
          (plug->callback_proc) 
            (plug_handle, 
             plug->callback_context, 
             sub_protocol, 
             0, 
             0); 
 
          goto go_home; 
        } 
	 
	if(tcp_flags & ne_plugs_flag_tcp_rst) { 
          // | 
          // | They want an immediate disconnect. Fine. 
          // | 
          r_tcp_plug_set_inert(plug); 
          // | 
          // | Notify the application that the 
          // | tcp connection has been closed 
          // | by sending a payload size of 0. 
          // 
          (plug->callback_proc) 
            (plug_handle, 
             plug->callback_context, 
             sub_protocol, 
             0, 
             0); 
          goto go_home; 
        } 
go_home: 
  return result; 
} 
 
void r_tcp_plug_set_inert (s_plugs_plug* plug) 
{ 
#if PLUGS_TCP 
  plug->tcp_state = e_plugs_tcp_inert; 
  plug->tcp_manual_window_size = 0; 
#endif // PLUGS_TCP 
 
  plug->remote_ip_address = 0; 
  plug->remote_port = 0; 
} 
 
 
#endif // PLUGS_TCP 
 
// -------------------------- 
// DNS lookup 
 
#if PLUGS_DNS 
// support routines for parsing those 
// troublesome unaligned dns queries 
 
// Given a pointer to some bytes to emit to, 
// and an int, emit the two bytes bigendian. 
// This is done byte-by-byte, so is ok 
// to be unaligned. 
 
static void p16(unsigned char **wp,int v) 
	{ 
	unsigned char *w; 
 
	w = *wp; 
	*w++ = (v & 0x0000ff00) >> 8; 
	*w++ = (v & 0x000000ff); 
	*wp = w; 
	} 
 
// Given a pointer to some bytes to emit to, 
// and a host name, put in the host name 
// in the weird form required by dns. 
// Advance the pointerpointer. 
 
static void pn(unsigned char **wp,char *name) 
	{ 
	unsigned char *w; 
	unsigned char *label_begin; 
	unsigned char label_len; 
	int c; 
 
	// | Hey man, sorry about the gotos. -- dvb 2001.03.23 
 
	w = *wp; 
 
do_label: 
	label_begin = w++; 
	label_len = 0; 
do_label_char: 
	c = *name++; 
	if(c == 0 || c == '.') 
		{ 
		*label_begin = label_len; 
		if(c) 
			goto do_label; 
		else 
			{ 
			*w++ = 0; 
			*wp = w; 
			return; 
			} 
		} 
	label_len++; 
	*w++ = c; 
	goto do_label_char; 
	} 
 
int g16(unsigned char **wp) 
	{ 
	unsigned char *w = *wp; 
	int v; 
 
	v = *w++; 
	v = (v << 8) + *w++; 
 
	*wp = w; 
	return v; 
	} 
 
// Skip over strange length-delimited dns "label" 
 
void skip_label(unsigned char **wp) 
	{ 
	unsigned char *w = *wp; 
	int label_length; 
 
	while((label_length = *w++) != 0) 
		if(label_length > 64) 
			{ 
			w++; 
			goto go_home; 
			} 
		else 
			w += label_length; 
 
go_home: 
	*wp = w; 
	} 
 
static int r_plugs_dns_proc 
		( 
		int plug_handle, 
		void *context, 
		ns_plugs_packet *p, 
		void *payload, 
		int payload_length 
		) 
	{ 
	unsigned char *w = payload; 
	unsigned char **wp = &w; 
	int v; 
	int questions; 
	int answers; 
	int result = 0; 
	s_plugs_globals *g = &ng_plugs_globals; 
 
	// identifier... is it the one we sent out? 
	v = g16(wp); 
	if(v != g->dns_identifier) // not for us, just ignore 
		goto go_home; 
	 
	// flags... did we get an answer? 
	v = g16(wp); 
	if( 
			(!(v & 0x8000))	// it's not an answer? 
			|| 
			((v & 0x000f) == 3) // it's an error? 
			) 
		{ 
	failure: 
		g->dns_query_state = 3; 
		goto go_home; 
		} 
	 
	// questions & answers 
	questions = g16(wp); 
	answers = g16(wp); 
	if(!answers) 
		goto failure; 
 
	w += 4;	// skip over authorities & additional 
 
	// skip over questions 
	while(questions--) 
		{ 
		skip_label(wp); 
 
		// skip over query type & class 
		w += 4; 
		} 
	 
	// take first answer of type 1 as the answer! 
	while(answers--) 
		{ 
		skip_label(wp); 
		v = g16(wp);	// query type 
		if(v == 1)	// 'A' (ip address) 
			{ 
			long vl; 
			w += 8; // skip type, class, & ttl, and assume lenght = 4... 
 
			vl = g16(wp); 
			vl = (vl << 16) + g16(wp); 
			g->dns_query_state = 2; // answered ok 
			g->dns_query_result = nr_h2n32(vl); 
 
			goto go_home; 
			} 
		else 
			{ 
			// some query type we dont care about... 
			w += 6; 
			v = g16(wp);	// resource length 
			w += v; 
			} 
		} 
	 
	// got to here? No answers with our IP address?? aww. 
	result = ne_plugs_error_unwanted_reply; 
 
go_home: 
	return result; 
	} 
 
#endif // PLUGS_DNS 
 
 
int nr_plugs_name_to_ip(char *host_name,net_32 *host_ip_address_out) 
	{ 
#if PLUGS_DNS 
	unsigned char p[256];	// arbitrary upper bound on query size 
	int len;		// length of constructed query 
	unsigned char *w; 
	unsigned char **wp; 
	s_plugs_globals *g = &ng_plugs_globals; 
	int id; 
	long t1,ti;		// current time, last req time 
	int retry_count; 
	net_32 host_ip_address; 
	int result = 0; 
 
	w = p; 
	wp = &w; 
 
	g->dns_identifier = g->ip_identifier++; 
 
	// Build the dns message, and then possibly reuse it several times 
	// dns header 
 
	p16(wp,g->dns_identifier); // dns id, to recognize answer 
	p16(wp,0x0100); // dns flags: recursion desired 
	p16(wp,1); // 1 question 
	p16(wp,0); // 0 answers 
	p16(wp,0); // 0 authorities 
	p16(wp,0); // 0 additional 
 
	// dns question 
	pn(wp,host_name); // host name to look up 
	p16(wp,1); // query type: 'A' IP address 
	p16(wp,1); // query class: 1 = internet, blah blah 
 
	w = *wp; 
	len = w - p; 
 
	ti = nr_timer_milliseconds() - nk_plugs_dns_retry_interval; // so first timeout always true 
	g->dns_query_state = 0; // so we can wait for it to change... 
	g->dns_query_result = 0; 
	retry_count = nk_plugs_dns_retry_count; 
 
dns_query_loop: 
	// Time to send another query? 
	t1 = nr_timer_milliseconds(); 
	if(t1 - ti > nk_plugs_dns_retry_interval) 
		{ 
		retry_count--; 
		if(retry_count < 0) 
			{ 
			result = ne_plugs_error_dns_timed_out; 
			goto go_home; 
			} 
		nr_plugs_send(g->dns_plug,p,len,0); 
		ti = t1; 
		} 
 
	// Idle the system, and wait for godot. 
	nr_plugs_idle(); 
	if(g->dns_query_state) // godot here already? 
		{ 
		if(g->dns_query_state == 3)	// failure? 
			result = ne_plugs_error_dns_not_found; 
		goto go_home;			// we are done, in any case. 
		} 
	goto dns_query_loop; 
 
go_home: 
	*host_ip_address_out = g->dns_query_result; 
	return result; 
#else // PLUGS_DNS 
	*host_ip_address_out = 0; 
	return ne_plugs_error_feature_disabled; 
#endif // else PLUGS_DNS 
	} 
 
 
// ---------------------------- 
// r_add_to_arp_cache 
// 
// insert an entry to the cache (at extant spot, if possible) 
static void r_add_to_arp_cache(s_plugs_adapter_entry *a,net_32 ip_address,net_32 ethu32,net_16 ethl16) 
	{ 
	s_plugs_arp_cache_entry *w; 
	int i; 
 
	w = a->arp_cache; 
	for(i = 0; i < nk_plugs_arp_cache_count; i++) 
		{ 
		if(w->ip_address == ip_address) 
			goto found_a_spot; 
		w++; 
		} 
 
	// no existing ip entry? choose the next one 
	w = &a->arp_cache[a->arp_cache_walker]; 
	a->arp_cache_walker++; 
	if(a->arp_cache_walker == nk_plugs_arp_cache_count) 
		a->arp_cache_walker = 0; 
found_a_spot: 
 
	w->ip_address = ip_address; 
	w->ethernet_address.u32 = ethu32; 
	w->ethernet_address.l16 = ethl16; 
	} 
 
// -------------------------- 
// r_add_to_arp_cache_from_ip_packet 
// 
// Given an ethernet packet known to contain an IP header, 
// add the source's address to the cache. 
// This is great for imminent replies to packets. 
 
static void r_add_to_arp_cache_from_ip_packet(s_plugs_adapter_entry *a,ns_plugs_ethernet_packet *p) 
	{ 
	net_32 ip_address; 
	net_48 ethernet_address; 
 
	// pull the info from the packet 
	ip_address = ((ns_plugs_ip_packet *)(&p->payload))->source_ip_address; 
	ethernet_address = p->source_address; 
 
	// Is it outside our LAN? If so, any ethernet 
	// address actually refers to the router. 
 
	if((ip_address ^ a->settings.ip_address) & a->settings.subnet_mask) 
		ip_address = a->settings.gateway_ip_address; 
	 
	// And put in cache! 
	 
	r_add_to_arp_cache(a,ip_address,ethernet_address.u32,ethernet_address.l16); 
	} 
 
 
// -------------------------- 
// nr_plugs_ip_to_ethernet: 
// invokes arp if necessary, but ONLY if we're not in_dispatch. Reentrant, doncha know. 
// The "flags" parameter is taken from the plug that's calling 
// us, and the routine prints errors if ne_plugs_flag_debug_tx is set. 
// 
 
int nr_plugs_ip_to_ethernet(int adapter_index,net_32 ip_address,net_48 *ethernet_address_out,long flags) 
	{ 
	s_plugs_globals *g = &ng_plugs_globals; 
	int result = 0; 
	int i; 
	s_plugs_arp_cache_entry *w; 
	int requests_sent; 
	long t0,t1;			// time we started, current time 
	long ti;			// time of last arp-request 
	long retry_count;	// how many times we've polled 
	s_plugs_adapter_entry *a = &g->adapter[adapter_index]; 
 
    // | 
    // | Two things indicate an ethernet broadcast: 
    // | The ethernet broadcast flag, or ip address 0xffffffff 
    // | 
    // | The ethernet address for an ethernet broadcast is easy! 0xffffFFFFffff 
    // | 
 
    if((flags & ne_plugs_flag_ethernet_broadcast) || (ip_address == 0xffffFFFF)) 
        { 
        ethernet_address_out->u32 = 0xffffFFFF; 
        ethernet_address_out->l16 = 0xFFFF; 
        goto go_home; 
        } 
 
	// Is it outside our LAN? If so, we must 
	// send to the router, not direct 
 
	if((ip_address ^ a->settings.ip_address) & a->settings.subnet_mask) 
		ip_address = a->settings.gateway_ip_address; 
	 
	t0 = nr_timer_milliseconds(); 
	ti = t0 - nk_plugs_arp_retry_interval;	// so first timeout test always does it 
	retry_count = 0; 
 
scan_cache: 
	// Already in the cache? 
	// 
 
	// Receive it right now if possible! 
	// nr_plugs_idle(); 
 
	w = a->arp_cache; 
	for(i = 0; i < nk_plugs_arp_cache_count; i++) 
		{ 
		if(w->ip_address == ip_address) 
			{ 
			// entry there, but no arp-response yet? 
			if(!(w->ethernet_address.u32 || w->ethernet_address.l16)) 
				goto present_but_unresolved; 
			*ethernet_address_out = w->ethernet_address; 
			goto go_home; 
			} 
		w++; 
		} 
	// Add a spot in the ARP cache, with zeroed ethernet 
 
	r_add_to_arp_cache(a,ip_address,0,0); 
 
present_but_unresolved: 
	// We put entries in the cache with ethernet_address == 0 
	// to signify that we want it. The ARP handler only fills 
	// in mac addr's that already have an entry for the ip. 
 
	// Not found in cache 
	// If we're already in dispatch, we cannot conduct a 
	// transaction, so just fail 
	if(g->in_dispatch) 
		{ 
		result = ne_plugs_error_arp; 
		goto go_home; 
		} 
 
	// 
	// Should we send an ARP request? 
	t1 = nr_timer_milliseconds(); 
 
	if(t1 - ti < nk_plugs_arp_retry_interval) 
		goto scan_cache; 
 
	 
	ti = t1;	// retry timer shall go again from here... 
 
	if(retry_count >= nk_plugs_arp_retry_count) 
		{ 
		result = ne_plugs_error_arp_timed_out; 
		goto go_home; 
		} 
	 
	retry_count++; 
	 
	// Not in the cache, & is time to send out a request, and wait... 
	// If we wait too long, well, report an error then. 
		{ 
		ns_plugs_arp_packet request; 
 
		request.hardware_type = nm_h2n16(1); 
		request.protocol_type = nm_h2n16(0x0800); 
		request.hardware_size = 6;		// 6 for ethernet 
		request.protocol_size = 4;		// 4 for IP 
		request.op = nm_h2n16(1);			// request 
		request.sender_ethernet_address = a->settings.ethernet_address; 
		request.sender_ip_address = a->settings.ip_address; 
		request.target_ethernet_address.u32 = 0; 
		request.target_ethernet_address.l16 = 0; 
		request.target_ip_address = ip_address; 
 
#if PLUGS_DEBUG 
		if(flags & ne_plugs_flag_debug_tx) 
			{ 
			printf("   [plugs] | sending arp request for "); 
			nr_plugs_print_ip_address(request.target_ip_address); 
			printf("\n"); 
			} 
#endif // PLUGS_DEBUG 
		result = nr_plugs_send(a->arp_plug,&request,28,0); 
		if(result) 
			goto go_home; 
		} 
	goto scan_cache; 
 
go_home: 
	return result; 
	} 
 
void packet_append_bytes(char **dst,void *src, int length) 
	{ 
	char *src_c = (char *)src; 
	char *w = *dst; 
 
	while(length--) 
		*w++ = *src_c++; 
	 
	*dst = w; 
	} 
 
void packet_append_n16(char **dst,net_16 x) 
	{ 
	packet_append_bytes(dst,&x,2); 
	} 
 
// Send must construct an entire ethernet packet 
// for whatever protocol it is sending. 
// The address can be deduced from the packet 
 
// Note -- the adapter's send routine 
// is not reentrant, so it should disable 
// interrupts until it has made its 
// best attempt to send. 
 
// Note -- In the case of an ip packet, 
// this routine can end up recursively 
// calling itself as part of the ARP exchange. 
// (The nr_plugs_ip_to_ethernet routine will need 
// to send an arp request the first time an 
// ip address is encountered.) (If the adapter 
// requires mac address.) Usually we'll have 
// the address in cache already, though, from the 
// incoming ip message. 
 
// plug_handle -- if this is a small integer, 
// then it is presumed to be just a protocol 
// number. This is so we can send a packet 
// without a plug allocated, in some cases. 
// Specifically, this lets us send tcp rst 
// messages in response to spurious packets. 
// If used like this, the adapter index must 
// be coded into the low flags bits. 
 
int nr_plugs_send_to(int plug_handle, 
		void *payload, 
		int payload_length, 
		long flags, 
		net_32 ip_address, 
		net_16 port) 
	{ 
	s_plugs_globals *g = &ng_plugs_globals; 
	int result = 0; 
	ns_plugs_ethernet_packet *ep; 
	char ep_contents[1500];			// this should be supplied by each adapter 
	char *ep_w;						// progressive walk along the packet... 
	int plug_protocol; 
	net_16 plug_local_port; 
	int adapter_index; 
	s_plugs_adapter_entry *a; 
 
	// | 
	// | Extract the protocol, flags, and source port 
	// | either from the plug pointer, or inferentially 
	// | from the (fake) plug_handle 
	// | 
 
	if(plug_handle < ne_plugs_last_protocol) // not a plug handle! 
		{ 
		plug_protocol = plug_handle; 
		plug_local_port = 0; 
		plug_handle = -1; // so debug printing reveals the fake plug-ness 
		adapter_index = flags & ne_plugs_flag_adapter_index_mask; 
		} 
	else 
		{ 
		s_plugs_plug *plug; 
 
		plug_handle -= nk_plugs_plug_number_base; 
		plug = &g->plug[plug_handle]; 
		 
		plug_protocol = plug->protocol; 
		plug_local_port = plug->local_port; 
		flags |= plug->flags; 
		adapter_index = plug->flags & ne_plugs_flag_adapter_index_mask; 
		} 
 
	// | 
	// | Make sure the adapter index is reasonable, 
	// | and then get the adapter description into "a". 
	// | 
	 
	if(adapter_index >= g->adapter_count) 
		{ 
		result = ne_plugs_error_not_initialized; 
		goto go_home; 
		} 
	 
	a = &g->adapter[adapter_index]; 
         
        // Turn off interrupts while sending. 
        // 
#if 0 
        PLUGS_IRQ 
        if (nr_plugs_interrupts_enabled()) 
           (a->adapter_description->set_irq_proc) 
              ( 
                 a->adapter_address, 
                 &a->adapter_storage, 
                 0 
                 ); 
#endif // PLUGS_IRQ 
 
#if PLUGS_DEBUG 
	if(flags & ne_plugs_flag_debug_tx) 
		{ 
		printf("   [plugs] +-----------------------\n"); 
		printf("   [plugs] | plug %d (%s): sends %d byte payload\n", 
			plug_handle, 
			get_protocol_name(plug_protocol), 
			payload_length); 
		} 
#endif // PLUGS_DEBUG 
 
	ep = (ns_plugs_ethernet_packet *)ep_contents; 
	ep_w = (char *)ep_contents; 
 
	if(plug_protocol == ne_plugs_ethernet) 
		{ 
		// ethernet protocol means your "payload" is, in fact, 
		// the whole ethernet packet. 1st 14 bytes is the header. 
 
		packet_append_bytes(&ep_w,payload,payload_length); 
		} 
	else if(plug_protocol == ne_plugs_arp) 
		{ 
		// arp protocol: payload is presumed to be 
		// a 28-byte arp packet. We don't even 
		// check the length. 
 
		ns_plugs_arp_packet *arp_packet; 
		net_48 broadcast_ethernet_address; 
 
		arp_packet = (ns_plugs_arp_packet *)payload; 
 
		// ethernet header 
		broadcast_ethernet_address.u32 = 0xffffFFFF; 
		broadcast_ethernet_address.l16 = 0xffff; 
		packet_append_bytes(&ep_w, 
				(flags & ne_plugs_flag_ethernet_broadcast) 
					? &broadcast_ethernet_address 
					: &arp_packet->target_ethernet_address, 
				6); 
		packet_append_bytes(&ep_w,&a->settings.ethernet_address,6); 
		packet_append_n16(&ep_w,nm_h2n16(ne_plugs_ethernet_arp)); 
 
		// arp packet as delivered to us 
		packet_append_bytes(&ep_w,payload,28); 
		} 
 
	else if 
			( 
			plug_protocol == ne_plugs_ip 
			|| plug_protocol == ne_plugs_icmp 
			|| plug_protocol == ne_plugs_udp 
			|| plug_protocol == ne_plugs_tcp_raw 
			) 
		{ 
		// | icmp, udp, tcp: payload is just the data to be 
		// | sent to the port and address specified in 
		// | the nr_plugs_connect call. 
 
		ns_plugs_ip_packet *ip_packet; 
		net_48 ep_ethernet_address; 
 
		// | look up ethernet destination 
		result = nr_plugs_ip_to_ethernet 
				( 
				adapter_index, 
				ip_address, 
				&ep_ethernet_address,flags 
				); 
		if(result) 
			goto go_home; 
 
		// | 
		// | ethernet header 
 
		packet_append_bytes(&ep_w,&ep_ethernet_address,6); 
		packet_append_bytes(&ep_w,&a->settings.ethernet_address,6); 
		packet_append_n16(&ep_w,nm_h2n16(ne_plugs_ethernet_ip)); 
 
		// | 
		// | construct ip header in place (not appending) 
 
		ip_packet = (ns_plugs_ip_packet *)ep_w; 
		ep_w += sizeof(ns_plugs_ip_packet); 
 
		ip_packet->version_header_length = 0x45; // 0x45 for no options 
		ip_packet->tos = 0;			// zero 
		ip_packet->length = nr_h2n16(sizeof(ns_plugs_ip_packet) + payload_length 
				+ ((plug_protocol == ne_plugs_udp) 
				? sizeof(ns_plugs_udp_packet) : 0)); // header + payload length 
 
		ip_packet->identifier = nr_h2n16(g->ip_identifier++);		// bump for each packet 
		ip_packet->flags_fragment = nm_h2n16(0);		// fragment miscellany 
		ip_packet->time_to_live = 128;		// some typical value 
 
		ip_packet->protocol = (plug_protocol == ne_plugs_icmp) ? ne_plugs_ip_icmp : 
				(plug_protocol == ne_plugs_udp) ? ne_plugs_ip_udp : 
				ne_plugs_ip_tcp; 
 
		ip_packet->header_checksum = 0; 
		ip_packet->source_ip_address = a->settings.ip_address; 
		ip_packet->destination_ip_address = ip_address; 
 
		// | 
		// | maybe append udp header 
		 
		if(plug_protocol == ne_plugs_udp) 
			{ 
			int udp_packet_length = sizeof(ns_plugs_udp_packet) + payload_length; 
 
			packet_append_n16(&ep_w,plug_local_port); 
			packet_append_n16(&ep_w,port); 
			packet_append_n16(&ep_w,nr_n2h16(udp_packet_length)); 
			packet_append_n16(&ep_w,0);		// no stinking checksum 
			} 
 
		// | 
		// | append the payload 
 
		packet_append_bytes(&ep_w,payload,payload_length); 
 
		// | 
		// | After the whole packet is together, 
		// | work backwards out and do the checksums. 
		// | maybe calculate icmp checksum, so courteously! 
 
		if(plug_protocol == ne_plugs_icmp) 
			{ 
			ns_plugs_icmp_packet *icmp_packet; 
 
			icmp_packet = (ns_plugs_icmp_packet *)ip_packet->payload; 
 
			// | clear checksum, first 
			icmp_packet->checksum = 0; 
			icmp_packet->checksum = r_calculate_checksum(ip_packet->payload,payload_length,0,0); 
			} 
		else if(plug_protocol == ne_plugs_udp) 
			{ 
			ns_plugs_udp_packet *udp_packet = (ns_plugs_udp_packet *)ip_packet->payload; 
			int udp_packet_length = sizeof(ns_plugs_udp_packet) + payload_length; 
 
			udp_packet->checksum = r_calculate_checksum( 
					udp_packet, 
					udp_packet_length, 
					ip_packet, 
					udp_packet_length); 
			} 
		else if(plug_protocol == ne_plugs_tcp_raw 
				|| (flags & ne_plugs_flag_tcp_checksum)) 
			{ 
			ns_plugs_tcp_packet *tcp_packet = (ns_plugs_tcp_packet *)ip_packet->payload; 
 
			tcp_packet->checksum = 0; 
			tcp_packet->checksum = r_calculate_checksum( 
					tcp_packet, 
					payload_length, 
					ip_packet, 
					payload_length); 
			} 
 
		ip_packet->header_checksum = r_calculate_checksum(ip_packet, 
				sizeof(ns_plugs_ip_packet),0,0); 
		} 
#if PLUGS_DEBUG 
	else 
		printf("   [plugs] | nr_plugs_send: unknown plug %d protocol %d\n", 
				plug_handle,plug_protocol); 
#endif // PLUGS_DEBUG 
	 
 
#if PLUGS_DEBUG 
	if(flags & ne_plugs_flag_debug_tx) 
		nr_plugs_print_ethernet_packet(ep,ep_w - ep_contents,"   [plugs] | "); 
#endif // PLUGS_DEBUG 
 
	// | 
	// | Send the packet to the hardware adapter 
	// | 
 
	{ 
		int watchdog = 0; // we will try a few time 
 
send_again: 
                // TPA: Before sending, check the mailbox. 
                nr_plugs_idle(); 
		result = (a->adapter_description->tx_frame_proc) 
				( 
				a->adapter_address, 
				&a->adapter_storage, 
				ep_contents, 
				ep_w - ep_contents 
				); 
 
 
                // Positive, nonzero result means: retry. 
		if(result > 0 && watchdog < 20) 
		{ 
			// failed to send for some reason? 
			// bump the watchdog and do it again 
			// (unless weve tried enough: just return the error) 
			watchdog++; 
			nr_plugs_idle(); 
			nr_delay(2); 
                        printf ("T"); 
			goto send_again; 
		} 
                // Negative result means: "I hate you." 
                if (result < 0)  
                  goto go_home; 
	} 
 
go_home: 
 
#if PLUGS_DEBUG 
	if(result) 
		{ 
		if(flags & ne_plugs_flag_debug_tx) 
			printf("   [plugs] | ERROR nr_plugs_send_to result = %d\n",result); 
		} 
#endif // PLUGS_DEBUG 
 
 
        // Turn on interrupts when done sending, if we're using 
        // interrupts. 
         
#if 0 
        PLUGS_IRQ 
        if (nr_plugs_interrupts_enabled() &&  
            (adapter_index < g->adapter_count)) 
           (a->adapter_description->set_irq_proc) 
                ( 
                 a->adapter_address, 
                 &a->adapter_storage, 
                 1 
                 ); 
        // Interrupts might have happened 
        // while we were off-line.  Check. 
        nr_plugs_idle(); 
#endif //PLUGS_IRQ 
 
	return result; 
	} 
 
int nr_plugs_send(int plug_handle, 
		void *payload, 
		int payload_length, 
		long flags) 
	{ 
	s_plugs_globals *g = &ng_plugs_globals; 
	s_plugs_plug *plug; 
	int plugIndex; 
	int result; 
 
 
	plug = r_plug_from_handle(plug_handle); 
 
	// | 
	// | For a tcp connection, shunt it through 
	// | r_tcp_send (which eventually 
	// | calls through to nr_plugs_send_to, 
	// | after building the tcp packet &c.) 
	// | 
 
#if PLUGS_TCP 
	if(plug->protocol == ne_plugs_tcp) 
		{ 
		result = r_tcp_send 
				( 
				plug_handle, 
				plug, 
				ne_plugs_flag_tcp_ack | ne_plugs_flag_tcp_psh, 
				payload, 
				payload_length 
				); 
		} 
	else 
#endif // PLUGS_TCP 
		{ 
		result = nr_plugs_send_to(plug_handle,payload,payload_length,flags, 
				plug->remote_ip_address, 
				plug->remote_port); 
		} 
 
	return result; 
	} 
 
 
// +------------------------ 
// | Mundane stuff, like setting 
// | the LED and such 
 
int nr_plugs_set_mac_led(int adapter_index,int led_onoff) 
	{ 
	s_plugs_globals *g = &ng_plugs_globals; 
	s_plugs_adapter_entry *a = &g->adapter[adapter_index]; 
 
	return (a->adapter_description->set_led_proc) 
			( 
			a->adapter_address, 
			&a->adapter_storage, 
			led_onoff 
			); 
	} 
 
int nr_plugs_set_mac_loopback(int adapter_index,int loopback_onoff) 
	{ 
	s_plugs_globals *g = &ng_plugs_globals; 
	s_plugs_adapter_entry *a = &g->adapter[adapter_index]; 
 
	return (a->adapter_description->set_loopback_proc) 
			( 
			a->adapter_address, 
			&a->adapter_storage, 
			loopback_onoff 
			); 
	} 
 
#if PLUGS_IRQ 
 
void r_plugs_irq_handler(int context, int irq_unumber, int interruptee_pc) 
	{ 
	int result; // gets lost 
	s_plugs_globals *g = &ng_plugs_globals; 
	s_plugs_adapter_entry *a = (s_plugs_adapter_entry *)context; 
 
    // | Store interruptee_pc in case some callback routine (debug) 
    // | needs to do something with it (put a trap there) 
    g->interruptee_pc = interruptee_pc<<1; 
 
 
	result = (a->adapter_description->check_for_events_proc) 
			( 
			a->adapter_address, 
			&a->adapter_storage, 
			nr_plugs_dispatch_packet, 
			a				// context is adapter pointer 
			); 
 
    // | Clear this before leaving just to be neat 
    g->interruptee_pc = 0; 
 
	} 
#endif 
 
// +----------------------------------- 
// | nr_plugs_get_interruptee_pc 
// | Return the interruptee_pc  stored 
// | by the ISR above 
int nr_plugs_get_interruptee_pc () 
    { 
	s_plugs_globals *g = &ng_plugs_globals; 
    return (g->interruptee_pc); 
    } 
 
// +----------------------------------- 
// | nr_plugs_interrupts_enalbed 
// | Return TRUE if plugs interrupts are enabled 
// | otherwise false 
int nr_plugs_interrupts_enabled () 
    { 
	s_plugs_globals *g = &ng_plugs_globals; 
	s_plugs_adapter_entry *a = g->adapter; 
 
    return (a->adapter_irq); 
    } 
 
void r_set_irqs_for_plug (s_plugs_plug* plug, int on_off) 
{ 
   s_plugs_adapter_entry *a;  
   s_plugs_globals *g = &ng_plugs_globals; 
 
   if (!nr_plugs_interrupts_enabled()) 
      return; 
 
   a = &g->adapter[plug->flags & ne_plugs_flag_adapter_index_mask]; 
   (a->adapter_description->set_irq_proc) 
      ( 
         a->adapter_address, 
         &a->adapter_storage, 
         on_off 
         ); 
 
 
   // Interrupts might have transpired while 
   // we were off-line.  check. 
   if (on_off) 
      nr_plugs_idle(); 
} 
 
 
 
 
// +----------------------------------- 
// | nr_plugs_idle 
// | Poll for events and other interesting things. 
// 
int nr_plugs_idle(void) 
	{ 
	s_plugs_globals *g = &ng_plugs_globals; 
	int adapter_index; 
	s_plugs_adapter_entry *a; 
	int result = 0; 
 
	if(!g->running) 
		return ne_plugs_error_not_initialized; // | plugs not initialized 
 
	if(g->in_idle) 
		return ne_plugs_error; 
 
	// +-------------------- 
	// | Can't call more than one deep for check_for_events, thank you. 
	// | 
 
 
	a = g->adapter; 
	for(adapter_index = 0; adapter_index < g->adapter_count; adapter_index++) 
		{ 
#if PLUGS_IRQ 
		if(!a->adapter_irq) 
#endif // PLUGS_IRQ 
			{ 
	        g->in_idle++; 
			result = (a->adapter_description->check_for_events_proc) 
					( 
					a->adapter_address, 
					&a->adapter_storage, 
					nr_plugs_dispatch_packet, 
					a				// context is adapter pointer 
					); 
	        g->in_idle--; 
			} 
		a++; 
		} 
 
	return result; 
	} 
 
int nr_plugs_idle_exclusive (int plug_handle) 
	{ 
	s_plugs_globals *g = &ng_plugs_globals; 
	s_plugs_adapter_entry *a = g->adapter; 
 
    // | Save and clear adapter_irq so nr_plugs_idle call 
    // | will always check for events 
    int adapter_irq = a->adapter_irq; 
    a->adapter_irq = 0; 
 
    // | Set the exclusive plug 
    g->exclusive_plug_handle = plug_handle; 
 
    // | Now check for events 
    nr_plugs_idle (); 
 
    // | Clear the exclusive plug 
    g->exclusive_plug_handle = 0; 
 
    // | Reset the adapter_irq 
    a->adapter_irq = adapter_irq; 
	} 
 
 
 
 
// end of file