
/*
    LinuxWare daemon - Netware like server for Linux

    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 <unistd.h>
#include <netinet/in.h>
#include <sys/socket.h>
#include "config.h"
#include "global.h"
#include "bindery.h"
#include "bindops.h"
#include "bindauth.h"
#include "logging.h"
#include "sap2bind.h"

static SapOutput sap_out;

void send_sap_raw(void* buffer,int size,struct sockaddr_ipx* daddr)
{
	LOG_START(LL_DEBUG)
	ipx_sap_fdump(log_file,(SapPacket*)buffer,size);
	LOG_END

	if (!setid(ROOT_UID,ROOT_GID)) return;
	if (sendto(sap_sock,(char*)buffer,size,
		0,(struct sockaddr*)daddr,sizeof(*daddr))==-1)
	{
		LPRINTF(LL_ERROR,("SAP sendto: %s\n",strerror(errno)));
	}
}

static void sap_notify_low(int hops,int down_allow)
{
	LPRINTF(LL_FULL,("sending SAP notification\n"));
	ipx_sap_output_response(&sap_out,BTYPE_SERVER,SERVER_NAME,&my_addr,hops,down_allow);
	ipx_sap_output_flush(&sap_out);
}

void sap_notify()
{
	sap_notify_low(1,0);
}

void sap_query()
{
	LPRINTF(LL_FULL,("sending SAP query\n"));
	ipx_sap_output_request(&sap_out,BTYPE_SERVER);
	ipx_sap_output_flush(&sap_out);
}

static int lookup_se(bind_list* bl,void* data)
{
	SapEntry* se=(SapEntry*)data;
	if (bind_type_cmp(bl->obj.type,ntohs(se->ser_type)) &&
	    strcasecmp(se->ser_name,bl->obj.name.name)==0)
		return 1;
	return 0;
}

#define MAX_TRY_ID (100)

dword get_bind_unused_id()
{
	dword id;
	int n;
	for(n=MAX_TRY_ID;n--;)
	{
		id=rand();
		if (bind_first_that(get_name_find,&id)==NULL) return id;
	}
	return NOT_LOGGED;
}

void handle_sap_response(SapEntry* se,IPXNode node)
{
#if 0
	if (!ipx_node_equal(node,my_addr.sipx_node))
	{
		LPRINTF(LL_ERROR,("SAP authorization failed: bad source node\n"));
		
	}
#endif
	{
		bind_list* bl;
		
		bl=bind_first_that(lookup_se,se);
		if (bl==NULL)
		{
			dword id=get_bind_unused_id();
			if (id==NOT_LOGGED) {LPRINTF(LL_ERROR,("can't get unused bindery id\n"));return;}
			bl=bind_alloc();
			if (bl==NULL)
			{
				LPRINTF(LL_ERROR,("can't add service %48s (no memory)\n",se->ser_name));
			}
			else
			{
				object_property* aprop;
				aprop=prop_net_address(se->network,se->node,se->port);
				if (aprop==NULL)
				{
					LPRINTF(LL_ERROR,("can't add service %48s (no memory)\n",se->ser_name));
				}
				else
				{
					bl->obj.id=id;
					bl->obj.type=ntohs(se->ser_type);
					memcpy(bl->obj.name.name,se->ser_name,sizeof(bl->obj.name.name));
					bl->obj.password=bpass_nologin;
					bl->obj.flag=BFLAG_DYNAMIC;
					bl->obj.timer=0;
					bl->obj.prop_list=NULL; 
					prop_add(&(bl->obj),aprop);
					bind_add(bl);
					LPRINTF(LL_INFO,("Adding %s to bindery\n",bl->obj.name.name));
				}
			}
		}
		else
		{
			LPRINTF(LL_FULL,("server %48s already known\n",bl->obj.name.name));
			bl->obj.timer=0;
		}
	}
}

/* use generic ?*/
void handle_sap(SapPacket* pkt,int len,struct sockaddr_ipx* sipx)
{
	SapEntry* se=pkt->sap_entries;
	int nent=(len-2)/sizeof(SapEntry);

	if (len<2)
	{
		LPRINTF(LL_ERROR,("SAP packet too small len=%i (ignored)\n",len));
		return;
	}
	switch (ntohs(pkt->operation))
	{
	case IPX_SAP_OP_REQUEST:
		break;
	case IPX_SAP_OP_GNS_REQUEST:
		break;
	case IPX_SAP_OP_RESPONSE:
		if ((unsigned)sipx->sipx_port!=htons(IPX_SAP_PORT)) return;
		for(;nent--;se++) handle_sap_response(se,sipx->sipx_node);
		break;
	case IPX_SAP_OP_GNS_RESPONSE:
		break;
	default:
		LPRINTF(LL_ERROR,("Unknown SAP operation\n"));
		break;
	} 
}

void init_sap()
{
	struct sockaddr_ipx sap_addr;

	sap_sock=socket(AF_IPX,SOCK_DGRAM,PF_IPX);
	if(sap_sock==-1)
	{
		LPRINTF(LL_FATAL,("SAP client socket: %s\n",strerror(errno)));
		exit(1);
	}
	sap_addr=my_addr;
	sap_addr.sipx_port=IPX_AUTO_PORT;
	sap_addr.sipx_type=IPX_SAP_PTYPE;
	if (bind(sap_sock,(struct sockaddr*)&sap_addr,sizeof(sap_addr))==-1)
	{
		LPRINTF(LL_FATAL,("SAP client bind: %s\n",strerror(errno)));
		exit(1);
	}
	if (ipx_kern_enable_broadcast(sap_sock)!=0)
	{
		LPRINTF(LL_FATAL,("Can't enable broadcasts on SAP socket: %s\n",ipx_err_string));
		exit(1); 
	}
	
	if (!ipx_sap_output_sap_output(&sap_out,my_addr.sipx_network))
	{
		LPRINTF(LL_FATAL,("Can't allocate SAP output buffer\n"));
		exit(1);
	}
	
	ipx_sap_output_set_destination(&sap_out,IPX_BROADCAST,IPX_SAP_PORT);
	ipx_sap_output_func=send_sap_raw;
	
	sap_notify();
	sap_query();
	LPRINTF(LL_INFO,("SAP initialized\n"));
}

void done_sap()
{
	sap_notify_low(IPX_SAP_SERVER_DOWN,1);
	close(sap_sock);
	LPRINTF(LL_INFO,("SAP shutdown\n"));
}
