
/*
    IPX support library - SAP

    Copyright (C) 1994, 1995  Ales Dryak <e-mail: A.Dryak@sh.cvut.cz>

    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 <stdlib.h>
#include <string.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include "ipxsap.h"

SapPacket* ipx_sap_alloc(int n)
{
  if (n>IPX_SAP_MAX_ENTRIES) return NULL;
  return (SapPacket*)malloc(ipx_sap_size(n,IPX_SAP_OP_RESPONSE));
}

int ipx_sap_size(int n,unsigned short int operation)
{
  if (n<=0) return 0;
  switch (operation)
  {
    case IPX_SAP_OP_REQUEST:
      return n==1?IPX_SAP_REQUEST_LEN:0;
    case IPX_SAP_OP_RESPONSE:
      if (n>IPX_SAP_MAX_ENTRIES) return 0;
      return sizeof(SapPacket)+(n-1)*sizeof(SapEntry);
    case IPX_SAP_OP_GNS_REQUEST:
      return n==1?IPX_SAP_REQUEST_LEN:0;
    case IPX_SAP_OP_GNS_RESPONSE:
      return n==1?sizeof(SapPacket):0;
      break;
    default:
      return 0;	
  }
}

void ipx_sap_assign_ser_name(ser_name_t dest,ser_name_t src)
{
	memset(dest,0,IPX_SAP_SERVER_NAME_LEN);
	strncpy(dest,src,IPX_SAP_SERVER_NAME_LEN);
}

int ipx_sap_name_equal(ser_name_t n1,ser_name_t n2)
{
	return strncmp(n1,n2,IPX_SAP_SERVER_NAME_LEN)==0;
}

int ipx_sap_type_equal(ser_type_t t1,ser_type_t t2)
{
	return t1==IPX_SAP_GENERAL_RQ || t2==IPX_SAP_GENERAL_RQ || t1==t2;
}

static void ipx_sap_sendto(void* buffer,int size,struct sockaddr_ipx* daddr)
{ 
  if (ipx_sap_output_func!=NULL) ipx_sap_output_func(buffer,size,daddr);
}

int ipx_sap_output_sap_output(SapOutput* out,IPXNet iface)
{
	out->entries=0;
	out->buffer=ipx_sap_alloc(IPX_SAP_MAX_ENTRIES);
	if (out->buffer==NULL) return 0;
	out->buffer->operation=htons(IPX_SAP_OP_REQUEST);
	out->dest_addr.sipx_family=AF_IPX;
	out->dest_addr.sipx_type=IPX_SAP_PTYPE;
	out->dest_addr.sipx_network=iface;
	return 1;
}

void ipx_sap_output_flush(SapOutput* out)
{
	if (out->entries==0) return;
	ipx_sap_sendto(out->buffer,ipx_sap_size(out->entries,ntohs(out->buffer->operation)),&(out->dest_addr));
	out->entries=0;
}

void ipx_sap_output_request(SapOutput* out,ser_type_t ser_type)
{
	SapEntry* se;
	ipx_sap_output_flush(out);
	out->buffer->operation=htons(IPX_SAP_OP_REQUEST);
	se=out->buffer->sap_entries+out->entries;
	se->ser_type=htons(ser_type);
	out->entries++;
}

void ipx_sap_output_gns_request(SapOutput* out,ser_type_t ser_type)
{
	SapEntry* se;
	ipx_sap_output_flush(out);
	out->buffer->operation=htons(IPX_SAP_OP_GNS_REQUEST);
	se=out->buffer->sap_entries+out->entries;
	se->ser_type=htons(ser_type);
	out->entries++;
}

void ipx_sap_output_response(SapOutput* out,ser_type_t type,ser_name_t name,struct sockaddr_ipx* addr,hop_t hops,int down_allow)
{
	SapEntry* se;
	if (hops>=IPX_SAP_SERVER_DOWN && !down_allow) return;
	if (out->entries>=IPX_SAP_MAX_ENTRIES)
		ipx_sap_output_flush(out);
	if (out->buffer->operation!=htons(IPX_SAP_OP_RESPONSE))
	{
		ipx_sap_output_flush(out);
		out->buffer->operation=htons(IPX_SAP_OP_RESPONSE);
	}
	se=out->buffer->sap_entries+out->entries;
	se->ser_type=htons(type);
	ipx_sap_assign_ser_name(se->ser_name,name);
	se->network=addr->sipx_network;
	ipx_assign_node(se->node,addr->sipx_node);
	se->port=addr->sipx_port;
	se->hops=htons(hops);
	out->entries++;
}

void ipx_sap_output_gns_response(SapOutput* out,ser_type_t type,ser_name_t name,struct sockaddr_ipx* addr,hop_t hops)
{
	SapEntry* se;
	if (hops>=IPX_SAP_SERVER_DOWN) return;
	ipx_sap_output_flush(out);
	out->buffer->operation=htons(IPX_SAP_OP_GNS_RESPONSE);
	se=out->buffer->sap_entries+out->entries;
	se->ser_type=htons(type);
	ipx_sap_assign_ser_name(se->ser_name,name);
	se->network=addr->sipx_network;
	ipx_assign_node(se->node,addr->sipx_node);
	se->port=addr->sipx_port;
	se->hops=htons(hops);
	out->entries++;
}

void ipx_sap_output_set_destination(SapOutput* out,IPXNode node,IPXPort port)
{
	ipx_sap_output_flush(out);
	ipx_assign_node(out->dest_addr.sipx_node,node);
	out->dest_addr.sipx_port=htons(port);
}

void ipx_sap_fprint_name(FILE* file,char* sname)
{
	char name[IPX_SAP_SERVER_NAME_LEN+1];
	int len;
	
	memcpy(name,sname,IPX_SAP_SERVER_NAME_LEN);
	name[IPX_SAP_SERVER_NAME_LEN]=0;
	len=strlen(name);
	memset(name+len,' ',IPX_SAP_SERVER_NAME_LEN-len);
	fprintf(file,name);
}

static void fprint_entry(FILE* file,SapEntry* se)
{
	fprintf(file,"SAP: type: %04X name: ",ntohs(se->ser_type));
	ipx_sap_fprint_name(file,se->ser_name);
	fprintf(file," \nhops: %i addr: ",ntohs(se->hops));
	ipx_fprint_network(file,se->network);
	fprintf(file,":");
	ipx_fprint_node(file,se->node);
	fprintf(file,":");
	ipx_fprint_port(file,se->port);
	fprintf(file,"\n");
}

void ipx_sap_dump(SapPacket* pkt,int len)
{
	ipx_sap_fdump(stdout,pkt,len);
}

void ipx_sap_fdump(FILE* file,SapPacket* pkt,int len)
{
  int ent;
  SapEntry* se=pkt->sap_entries;
  if (len<2) return;
  fprintf(file,"Operation: %i size: %i ",ntohs(pkt->operation),len);
  switch (ntohs(pkt->operation))
  {
    case IPX_SAP_OP_REQUEST:
      fprintf(file,"(Request)\n");
      if (ipx_sap_size(1,IPX_SAP_OP_REQUEST)!=len) 
      {
        fprintf(file,"Warning: Bad SAP size\n");
      }
      fprintf(file,"SAP: Type: %04X\n",ntohs(se->ser_type));
      break;
    case IPX_SAP_OP_GNS_REQUEST:
      fprintf(file,"(Get Nearest Server Request)\n");
      if (ipx_sap_size(1,IPX_SAP_OP_GNS_REQUEST)!=len) 
      {
        fprintf(file,"Warning: Bad SAP size\n");
      }
      fprintf(file,"SAP: Type: %04X\n",ntohs(se->ser_type));
      break;
    case IPX_SAP_OP_RESPONSE:
      fprintf(file,"(Response)\n");
      if (ipx_sap_size(ent=(len-2)/sizeof(SapEntry),IPX_SAP_OP_RESPONSE)!=len) 
      {
        fprintf(file,"Warning: Bad SAP size\n");
      }
      for(;ent--;se++)
      {
      	fprint_entry(file,se);
      }
      break;
    case IPX_SAP_OP_GNS_RESPONSE:
      fprintf(file,"(Get Nearest Server Response)\n");
      if (ipx_sap_size(1,IPX_SAP_OP_RESPONSE)!=len) 
      {
        fprintf(file,"Warning: Bad SAP size\n");
      }
      fprint_entry(file,se);
      break;
    default:
      fprintf(file,"(Unknown)\n");
      break;
  }
}

void (*ipx_sap_output_func)(void*,int,struct sockaddr_ipx*)=NULL;

