/* 
 *  Unix SMB/CIFS implementation.
 *  RPC Pipe client / server routines
 *  Copyright (C) Jean Fran�ois Micouleau      1998-2002.
 *  
 *  This program is free software; you can redistribute it and/or modify
 *  it under the terms of the GNU General Public License as published by
 *  the Free Software Foundation; either version 2 of the License, or
 *  (at your option) any later version.
 *  
 *  This program is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *  GNU General Public License for more details.
 *  
 *  You should have received a copy of the GNU General Public License
 *  along with this program; if not, write to the Free Software
 *  Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
 */

#include "includes.h"
#include "wins_repl.h"

extern TALLOC_CTX *mem_ctx;

/****************************************************************************
grow the send buffer if necessary
****************************************************************************/
BOOL grow_buffer(struct BUFFER *buffer, int more)
{
	char *temp;

	DEBUG(10,("grow_buffer: size is: %d offet is:%d growing by %d\n", buffer->length, buffer->offset, more));
	
	/* grow by at least 256 bytes */
	if (more<256)
		more=256;

	if (buffer->offset+more >= buffer->length) {
		temp=(char *)talloc_realloc(mem_ctx, buffer->buffer, sizeof(char)* (buffer->length+more) );
		if (temp==NULL) {
			DEBUG(0,("grow_buffer: can't grow buffer\n"));
			return False;
		}
		buffer->length+=more;
		buffer->buffer=temp;
	}

	return True;
}

/****************************************************************************
check if the buffer has that much data
****************************************************************************/
static BOOL check_buffer(struct BUFFER *buffer, int more)
{
	DEBUG(10,("check_buffer: size is: %d offet is:%d growing by %d\n", buffer->length, buffer->offset, more));
	
	if (buffer->offset+more > buffer->length) {
		DEBUG(10,("check_buffer: buffer smaller than requested, size is: %d needed: %d\n", buffer->length, buffer->offset+more));
		return False;
	}

	return True;
}

/****************************************************************************
decode a WINS_OWNER struct
****************************************************************************/
static void decode_wins_owner(struct BUFFER *inbuf, WINS_OWNER *wins_owner)
{
	if(!check_buffer(inbuf, 24))
		return;

	wins_owner->address.s_addr=IVAL(inbuf->buffer, inbuf->offset);
	wins_owner->max_version=((SMB_BIG_UINT)RIVAL(inbuf->buffer, inbuf->offset+4))<<32;
	wins_owner->max_version|=RIVAL(inbuf->buffer, inbuf->offset+8);
	wins_owner->min_version=((SMB_BIG_UINT)RIVAL(inbuf->buffer, inbuf->offset+12))<<32;
	wins_owner->min_version|=RIVAL(inbuf->buffer, inbuf->offset+16);
	wins_owner->type=RIVAL(inbuf->buffer, inbuf->offset+20);
	inbuf->offset+=24;

}

/****************************************************************************
decode a WINS_NAME struct
****************************************************************************/
static void decode_wins_name(struct BUFFER *outbuf, WINS_NAME *wins_name)
{	
	char *p;
	int i;

	if(!check_buffer(outbuf, 40))
		return;

	wins_name->name_len=RIVAL(outbuf->buffer, outbuf->offset);
	outbuf->offset+=4;
	memcpy(wins_name->name,outbuf->buffer+outbuf->offset, 15);
	wins_name->name[15]='\0';
	if((p = strchr(wins_name->name,' ')) != NULL)
		*p = 0;

	outbuf->offset+=15;

	wins_name->type=(int)outbuf->buffer[outbuf->offset++];
	
	/*
	 * fix to bug in WINS replication,
	 * present in all versions including W2K SP2 !
	 */
	if (wins_name->name[0]==0x1B) {
		wins_name->name[0]=(char)wins_name->type;
		wins_name->type=0x1B;
	}
	
	wins_name->empty=RIVAL(outbuf->buffer, outbuf->offset);
	outbuf->offset+=4;
	
	wins_name->name_flag=RIVAL(outbuf->buffer, outbuf->offset);
	outbuf->offset+=4;
	wins_name->group_flag=RIVAL(outbuf->buffer, outbuf->offset);
	outbuf->offset+=4;
	wins_name->id=((SMB_BIG_UINT)RIVAL(outbuf->buffer, outbuf->offset))<<32;
	outbuf->offset+=4;
	wins_name->id|=RIVAL(outbuf->buffer, outbuf->offset);
	outbuf->offset+=4;
	
	/* special groups have multiple address */
	if (wins_name->name_flag & 2) {
		if(!check_buffer(outbuf, 4))
			return;
		wins_name->num_ip=IVAL(outbuf->buffer, outbuf->offset);
		outbuf->offset+=4;
	}
	else
		wins_name->num_ip=1;

	if(!check_buffer(outbuf, 4))
		return;
	wins_name->owner.s_addr=IVAL(outbuf->buffer, outbuf->offset);
	outbuf->offset+=4;

	if (wins_name->name_flag & 2) {
		wins_name->others=(struct in_addr *)talloc(mem_ctx, sizeof(struct in_addr)*wins_name->num_ip);
		if (wins_name->others==NULL)
			return;

		if(!check_buffer(outbuf, 4*wins_name->num_ip))
			return;
		for (i=0; i<wins_name->num_ip; i++) {
			wins_name->others[i].s_addr=IVAL(outbuf->buffer, outbuf->offset);
			outbuf->offset+=4;
		}
	}

	if(!check_buffer(outbuf, 4))
		return;
	wins_name->foo=RIVAL(outbuf->buffer, outbuf->offset);
	outbuf->offset+=4;

}

/****************************************************************************
decode a update notification request
****************************************************************************/
static void decode_update_notify_request(struct BUFFER *inbuf, UPDATE_NOTIFY_REQUEST *un_rq)
{
	int i;

	if(!check_buffer(inbuf, 4))
		return;
	un_rq->partner_count=RIVAL(inbuf->buffer, inbuf->offset);
	inbuf->offset+=4;

	un_rq->wins_owner=(WINS_OWNER *)talloc(mem_ctx, un_rq->partner_count*sizeof(WINS_OWNER));
	if (un_rq->wins_owner==NULL)
		return;

	for (i=0; i<un_rq->partner_count; i++)
		decode_wins_owner(inbuf, &un_rq->wins_owner[i]);

	if(!check_buffer(inbuf, 4))
		return;
	un_rq->initiating_wins_server.s_addr=IVAL(inbuf->buffer, inbuf->offset);
	inbuf->offset+=4;
}

/****************************************************************************
decode a send entries request
****************************************************************************/
static void decode_send_entries_request(struct BUFFER *inbuf, SEND_ENTRIES_REQUEST *se_rq)
{
	decode_wins_owner(inbuf, &se_rq->wins_owner);
}

/****************************************************************************
decode a send entries reply
****************************************************************************/
static void decode_send_entries_reply(struct BUFFER *inbuf, SEND_ENTRIES_REPLY *se_rp)
{
	int i;

	if(!check_buffer(inbuf, 4))
		return;
	se_rp->max_names = RIVAL(inbuf->buffer, inbuf->offset);
	inbuf->offset+=4;

	se_rp->wins_name=(WINS_NAME *)talloc(mem_ctx, se_rp->max_names*sizeof(WINS_NAME));
	if (se_rp->wins_name==NULL)
		return;

	for (i=0; i<se_rp->max_names; i++)
		decode_wins_name(inbuf, &se_rp->wins_name[i]);
}

/****************************************************************************
decode a add version number map table reply
****************************************************************************/
static void decode_add_version_number_map_table_reply(struct BUFFER *inbuf, AVMT_REP *avmt_rep)
{
	int i;

	if(!check_buffer(inbuf, 4))
		return;

	avmt_rep->partner_count=RIVAL(inbuf->buffer, inbuf->offset);
	inbuf->offset+=4;

	avmt_rep->wins_owner=(WINS_OWNER *)talloc(mem_ctx, avmt_rep->partner_count*sizeof(WINS_OWNER));
	if (avmt_rep->wins_owner==NULL)
		return;

	for (i=0; i<avmt_rep->partner_count; i++)
		decode_wins_owner(inbuf, &avmt_rep->wins_owner[i]);

	if(!check_buffer(inbuf, 4))
		return;
	avmt_rep->initiating_wins_server.s_addr=IVAL(inbuf->buffer, inbuf->offset);
	inbuf->offset+=4;
}

/****************************************************************************
decode a replicate packet and fill a structure
****************************************************************************/
static void decode_replicate(struct BUFFER *inbuf, REPLICATE *rep)
{
	if(!check_buffer(inbuf, 4))
		return;

	rep->msg_type = RIVAL(inbuf->buffer, inbuf->offset);

	inbuf->offset+=4;

	switch (rep->msg_type) {
		case 0:
			break;
		case 1:
			/* add version number map table reply */
			decode_add_version_number_map_table_reply(inbuf, &rep->avmt_rep);
			break;
		case 2:
			/* send entry request */
			decode_send_entries_request(inbuf, &rep->se_rq);
			break;
		case 3:
			/* send entry request */
			decode_send_entries_reply(inbuf, &rep->se_rp);
			break;
		case 4:
			/* update notification request */
			decode_update_notify_request(inbuf, &rep->un_rq);
			break;
		default:
			DEBUG(0,("decode_replicate: unknown message type:%d\n", rep->msg_type));
			break;
	}
}

/****************************************************************************
read the generic header and fill the struct.
****************************************************************************/
static void read_generic_header(struct BUFFER *inbuf, generic_header *q)
{
	if(!check_buffer(inbuf, 16))
		return;

	q->data_size = RIVAL(inbuf->buffer, inbuf->offset+0);
	q->opcode    = RIVAL(inbuf->buffer, inbuf->offset+4);
	q->assoc_ctx = RIVAL(inbuf->buffer, inbuf->offset+8);
	q->mess_type = RIVAL(inbuf->buffer, inbuf->offset+12);
}

/*******************************************************************
decode a start association request
********************************************************************/
static void decode_start_assoc_request(struct BUFFER *inbuf, START_ASSOC_REQUEST *q)
{
	if(!check_buffer(inbuf, 8))
		return;

	q->assoc_ctx = RIVAL(inbuf->buffer, inbuf->offset+0);
	q->min_ver = RSVAL(inbuf->buffer, inbuf->offset+4);
	q->maj_ver = RSVAL(inbuf->buffer, inbuf->offset+6);
}

/*******************************************************************
decode a start association reply
********************************************************************/
static void decode_start_assoc_reply(struct BUFFER *inbuf, START_ASSOC_REPLY *r)
{
	if(!check_buffer(inbuf, 8))
		return;

	r->assoc_ctx=RIVAL(inbuf->buffer, inbuf->offset+0);
	r->min_ver = RSVAL(inbuf->buffer, inbuf->offset+4);
	r->maj_ver = RSVAL(inbuf->buffer, inbuf->offset+6);
}

/*******************************************************************
decode a start association reply
********************************************************************/
static void decode_stop_assoc(struct BUFFER *inbuf, STOP_ASSOC *r)
{
	if(!check_buffer(inbuf, 4))
		return;

	r->reason=RIVAL(inbuf->buffer, inbuf->offset);
}

/****************************************************************************
decode a packet and fill a generic structure
****************************************************************************/
void decode_generic_packet(struct BUFFER *inbuf, GENERIC_PACKET *q)
{
	read_generic_header(inbuf, &q->header);

	inbuf->offset+=16;

	switch (q->header.mess_type) {
		case 0:
			decode_start_assoc_request(inbuf, &q->sa_rq);
			break;
		case 1:
			decode_start_assoc_reply(inbuf, &q->sa_rp);
			break;
		case 2:
			decode_stop_assoc(inbuf, &q->so);
			break;
		case 3:
			decode_replicate(inbuf, &q->rep);
			break;
		default:
			DEBUG(0,("decode_generic_packet: unknown message type:%d\n", q->header.mess_type));
			break;
	}
}

/****************************************************************************
encode a WINS_OWNER struct
****************************************************************************/
static void encode_wins_owner(struct BUFFER *outbuf, WINS_OWNER *wins_owner)
{
	if (!grow_buffer(outbuf, 24))
		return;

	SIVAL(outbuf->buffer, outbuf->offset, wins_owner->address.s_addr);
	outbuf->offset+=4;
	RSIVAL(outbuf->buffer, outbuf->offset, (int)(wins_owner->max_version>>32));
	outbuf->offset+=4;
	RSIVAL(outbuf->buffer, outbuf->offset, (int)(wins_owner->max_version&0xffffffff));
	outbuf->offset+=4;
	RSIVAL(outbuf->buffer, outbuf->offset, wins_owner->min_version>>32);
	outbuf->offset+=4;
	RSIVAL(outbuf->buffer, outbuf->offset, wins_owner->min_version&0xffffffff);
	outbuf->offset+=4;
	RSIVAL(outbuf->buffer, outbuf->offset, wins_owner->type);
	outbuf->offset+=4;
	
}

/****************************************************************************
encode a WINS_NAME struct
****************************************************************************/
static void encode_wins_name(struct BUFFER *outbuf, WINS_NAME *wins_name)
{	
	int i;

	if (!grow_buffer(outbuf, 48+(4*wins_name->num_ip)))
		return;

	RSIVAL(outbuf->buffer, outbuf->offset, wins_name->name_len);
	outbuf->offset+=4;
	
	memset(outbuf->buffer+outbuf->offset, ' ', 15);

	/* to prevent copying the leading \0 */
	memcpy(outbuf->buffer+outbuf->offset, wins_name->name, strlen(wins_name->name));
	outbuf->offset+=15;		

	outbuf->buffer[outbuf->offset++]=(char)wins_name->type;

	RSIVAL(outbuf->buffer, outbuf->offset, wins_name->empty);
	outbuf->offset+=4;

	RSIVAL(outbuf->buffer, outbuf->offset, wins_name->name_flag);
	outbuf->offset+=4;
	RSIVAL(outbuf->buffer, outbuf->offset, wins_name->group_flag);
	outbuf->offset+=4;
	RSIVAL(outbuf->buffer, outbuf->offset, wins_name->id>>32);
	outbuf->offset+=4;
	RSIVAL(outbuf->buffer, outbuf->offset, wins_name->id);
	outbuf->offset+=4;

	if (wins_name->name_flag & 2) {
		SIVAL(outbuf->buffer, outbuf->offset, wins_name->num_ip);
		outbuf->offset+=4;
	}	

	SIVAL(outbuf->buffer, outbuf->offset, wins_name->owner.s_addr);
	outbuf->offset+=4;

	if (wins_name->name_flag & 2) {
		for (i=0;i<wins_name->num_ip;i++) {
			SIVAL(outbuf->buffer, outbuf->offset, wins_name->others[i].s_addr);
			outbuf->offset+=4;
		}
	}	

	RSIVAL(outbuf->buffer, outbuf->offset, wins_name->foo);
	outbuf->offset+=4;
}

/****************************************************************************
encode a update notification request
****************************************************************************/
static void encode_update_notify_request(struct BUFFER *outbuf, UPDATE_NOTIFY_REQUEST *un_rq)
{
	int i;

	if (!grow_buffer(outbuf, 8))
		return;
		
	RSIVAL(outbuf->buffer, outbuf->offset, un_rq->partner_count);
	outbuf->offset+=4;

	for (i=0; i<un_rq->partner_count; i++)
		encode_wins_owner(outbuf,  &un_rq->wins_owner[i]);

	SIVAL(outbuf->buffer, outbuf->offset, un_rq->initiating_wins_server.s_addr);
	outbuf->offset+=4;
	
}

/****************************************************************************
decode a send entries request
****************************************************************************/
static void encode_send_entries_request(struct BUFFER *outbuf, SEND_ENTRIES_REQUEST *se_rq)
{
	encode_wins_owner(outbuf, &se_rq->wins_owner);
}

/****************************************************************************
decode a send entries reply
****************************************************************************/
static void encode_send_entries_reply(struct BUFFER *outbuf, SEND_ENTRIES_REPLY *se_rp)
{
	int i;

	if (!grow_buffer(outbuf, 4))
		return;
		
	RSIVAL(outbuf->buffer, outbuf->offset, se_rp->max_names);
	outbuf->offset+=4;

	for (i=0; i<se_rp->max_names; i++)
		encode_wins_name(outbuf, &se_rp->wins_name[i]);

}

/****************************************************************************
encode a add version number map table reply
****************************************************************************/
static void encode_add_version_number_map_table_reply(struct BUFFER *outbuf, AVMT_REP *avmt_rep)
{
	int i;

	if (!grow_buffer(outbuf, 8))
		return;

	RSIVAL(outbuf->buffer, outbuf->offset, avmt_rep->partner_count);
	outbuf->offset+=4;
	
	for (i=0; i<avmt_rep->partner_count; i++)
		encode_wins_owner(outbuf, &avmt_rep->wins_owner[i]);

	SIVAL(outbuf->buffer, outbuf->offset, avmt_rep->initiating_wins_server.s_addr);
	outbuf->offset+=4;
	
}

/****************************************************************************
decode a replicate packet and fill a structure
****************************************************************************/
static void encode_replicate(struct BUFFER *outbuf, REPLICATE *rep)
{
	if (!grow_buffer(outbuf, 4))
		return;

	RSIVAL(outbuf->buffer, outbuf->offset, rep->msg_type);
	outbuf->offset+=4;

	switch (rep->msg_type) {
		case 0:
			break;
		case 1:
			/* add version number map table reply */
			encode_add_version_number_map_table_reply(outbuf, &rep->avmt_rep);
			break;
		case 2:
			/* send entry request */
			encode_send_entries_request(outbuf, &rep->se_rq);
			break;
		case 3:
			/* send entry request */
			encode_send_entries_reply(outbuf, &rep->se_rp);
			break;
		case 4:
			/* update notification request */
			encode_update_notify_request(outbuf, &rep->un_rq);
			break;
		default:
			DEBUG(0,("encode_replicate: unknown message type:%d\n", rep->msg_type));
			break;
	}
}

/****************************************************************************
write the generic header.
****************************************************************************/
static void write_generic_header(struct BUFFER *outbuf, generic_header *r)
{
	RSIVAL(outbuf->buffer, 0, r->data_size);
	RSIVAL(outbuf->buffer, 4, r->opcode);
	RSIVAL(outbuf->buffer, 8, r->assoc_ctx);
	RSIVAL(outbuf->buffer,12, r->mess_type);
}

/*******************************************************************
decode a start association request
********************************************************************/
static void encode_start_assoc_request(struct BUFFER *outbuf, START_ASSOC_REQUEST *q)
{
	if (!grow_buffer(outbuf, 45))
		return;

	RSIVAL(outbuf->buffer, outbuf->offset, q->assoc_ctx);
	RSSVAL(outbuf->buffer, outbuf->offset+4, q->min_ver);
	RSSVAL(outbuf->buffer, outbuf->offset+6, q->maj_ver);
	
	outbuf->offset=45;
}

/*******************************************************************
decode a start association reply
********************************************************************/
static void encode_start_assoc_reply(struct BUFFER *outbuf, START_ASSOC_REPLY *r)
{
	if (!grow_buffer(outbuf, 45))
		return;

	RSIVAL(outbuf->buffer, outbuf->offset, r->assoc_ctx);
	RSSVAL(outbuf->buffer, outbuf->offset+4, r->min_ver);
	RSSVAL(outbuf->buffer, outbuf->offset+6, r->maj_ver);

	outbuf->offset=45;
}

/*******************************************************************
decode a start association reply
********************************************************************/
static void encode_stop_assoc(struct BUFFER *outbuf, STOP_ASSOC *r)
{
	if (!grow_buffer(outbuf, 44))
		return;

	RSIVAL(outbuf->buffer, outbuf->offset, r->reason);
	
	outbuf->offset=44;
}

/****************************************************************************
write the generic header size.
****************************************************************************/
static void write_generic_header_size(generic_header *r, int size)
{
	/* the buffer size is the total size minus the size field */
	r->data_size=size-4;
}

/****************************************************************************
encode a packet and read a generic structure
****************************************************************************/
void encode_generic_packet(struct BUFFER *outbuf, GENERIC_PACKET *q)
{
	if (!grow_buffer(outbuf, 16))
		return;

	outbuf->offset=16;

	switch (q->header.mess_type) {
		case 0:
			encode_start_assoc_request(outbuf, &q->sa_rq);
			break;
		case 1:
			encode_start_assoc_reply(outbuf, &q->sa_rp);
			break;
		case 2:
			encode_stop_assoc(outbuf, &q->so);
			break;
		case 3:
			encode_replicate(outbuf, &q->rep);
			break;
		default:
			DEBUG(0,("encode_generic_packet: unknown message type:%d\n", q->header.mess_type));
			break;
	}
	
	write_generic_header_size(&q->header, outbuf->offset);
	write_generic_header(outbuf, &q->header);
}


/****************************************************************************
dump a WINS_OWNER structure
****************************************************************************/
static void dump_wins_owner(WINS_OWNER *wins_owner)
{
	DEBUGADD(10,("\t\t\t\taddress         : %s\n", inet_ntoa(wins_owner->address)));
	DEBUGADD(10,("\t\t\t\tmax version: %d\n", (int)wins_owner->max_version));
	DEBUGADD(10,("\t\t\t\tmin version: %d\n", (int)wins_owner->min_version));
	DEBUGADD(10,("\t\t\t\ttype            : %d\n", wins_owner->type));
}

/****************************************************************************
dump a WINS_NAME structure
****************************************************************************/
static void dump_wins_name(WINS_NAME *wins_name)
{
	fstring name;
	int i;

	strncpy(name, wins_name->name, 15);

	DEBUGADD(10,("name: %d, %s<%02x> %x,%x, %d %s %d ", wins_name->name_len, name, wins_name->type,
		    wins_name->name_flag, wins_name->group_flag, (int)wins_name->id,
		    inet_ntoa(wins_name->owner), wins_name->num_ip));

	if (wins_name->num_ip!=1)
		for (i=0; i<wins_name->num_ip; i++)
			DEBUGADD(10,("%s ", inet_ntoa(wins_name->others[i])));	

	DEBUGADD(10,("\n"));
}

/****************************************************************************
dump a replicate structure
****************************************************************************/
static void dump_replicate(REPLICATE *rep)
{
	int i;

	DEBUGADD(5,("\t\tmsg_type: %d ", rep->msg_type));

	switch (rep->msg_type) {
		case 0:
			DEBUGADD(5,("(Add Version Map Table Request)\n"));
			break;
		case 1:
			DEBUGADD(5,("(Add Version Map Table Reply)\n"));
			DEBUGADD(5,("\t\t\tpartner_count         : %d\n", rep->avmt_rep.partner_count));
			for (i=0; i<rep->avmt_rep.partner_count; i++)
				dump_wins_owner(&rep->avmt_rep.wins_owner[i]);
			DEBUGADD(5,("\t\t\tinitiating_wins_server: %s\n", inet_ntoa(rep->avmt_rep.initiating_wins_server)));
			break;
		case 2:
			DEBUGADD(5,("(Send Entries Request)\n"));
			dump_wins_owner(&rep->se_rq.wins_owner);
			break;
		case 3:
			DEBUGADD(5,("(Send Entries Reply)\n"));
			DEBUGADD(5,("\t\t\tmax_names         : %d\n", rep->se_rp.max_names));
			for (i=0; i<rep->se_rp.max_names; i++)
				dump_wins_name(&rep->se_rp.wins_name[i]);
			break;
		case 4:
			DEBUGADD(5,("(Update Notify Request)\n"));
			DEBUGADD(5,("\t\t\tpartner_count         : %d\n", rep->un_rq.partner_count));
			for (i=0; i<rep->un_rq.partner_count; i++)
				dump_wins_owner(&rep->un_rq.wins_owner[i]);
			DEBUGADD(5,("\t\t\tinitiating_wins_server: %s\n", inet_ntoa(rep->un_rq.initiating_wins_server)));
			break;
		default:
			DEBUG(5,("\n"));
			break;
	}
}

/****************************************************************************
dump a generic structure
****************************************************************************/
void dump_generic_packet(GENERIC_PACKET *q)
{
	DEBUG(5,("dump_generic_packet:\n"));
	DEBUGADD(5,("\tdata_size: %08x\n", q->header.data_size));
	DEBUGADD(5,("\topcode   : %08x\n", q->header.opcode));
	DEBUGADD(5,("\tassoc_ctx: %08x\n", q->header.assoc_ctx));
	DEBUGADD(5,("\tmess_type: %08x ", q->header.mess_type));

	switch (q->header.mess_type) {
		case 0:
			DEBUGADD(5,("(Start Association Request)\n"));
			DEBUGADD(5,("\t\tassoc_ctx: %08x\n", q->sa_rq.assoc_ctx));
			DEBUGADD(5,("\t\tmin_ver  : %04x\n", q->sa_rq.min_ver));
			DEBUGADD(5,("\t\tmaj_ver  : %04x\n", q->sa_rq.maj_ver));
			break;
		case 1:
			DEBUGADD(5,("(Start Association Reply)\n"));
			DEBUGADD(5,("\t\tassoc_ctx: %08x\n", q->sa_rp.assoc_ctx));
			DEBUGADD(5,("\t\tmin_ver  : %04x\n", q->sa_rp.min_ver));
			DEBUGADD(5,("\t\tmaj_ver  : %04x\n", q->sa_rp.maj_ver));
			break;
		case 2:
			DEBUGADD(5,("(Stop Association)\n"));
			DEBUGADD(5,("\t\treason: %08x\n", q->so.reason));
			break;
		case 3:
			DEBUGADD(5,("(Replication Message)\n"));
			dump_replicate(&q->rep);
			break;
		default:
			DEBUG(5,("\n"));
			break;
	}

}

/****************************************************************************
generate a stop packet
****************************************************************************/
void stop_packet(GENERIC_PACKET *q, GENERIC_PACKET *r, int reason)
{
	r->header.opcode=OPCODE_NON_NBT;
	r->header.assoc_ctx=get_server_assoc(q->header.assoc_ctx);
	r->header.mess_type=MESSAGE_TYPE_STOP_ASSOC;
	r->so.reason=reason;
	
}