152153Sbp/*
252153Sbp * Copyright (c) 1999, Boris Popov
352153Sbp * All rights reserved.
452153Sbp *
552153Sbp * Redistribution and use in source and binary forms, with or without
652153Sbp * modification, are permitted provided that the following conditions
752153Sbp * are met:
852153Sbp * 1. Redistributions of source code must retain the above copyright
952153Sbp *    notice, this list of conditions and the following disclaimer.
1052153Sbp * 2. Redistributions in binary form must reproduce the above copyright
1152153Sbp *    notice, this list of conditions and the following disclaimer in the
1252153Sbp *    documentation and/or other materials provided with the distribution.
13165920Simp * 3. Neither the name of the author nor the names of any co-contributors
1452153Sbp *    may be used to endorse or promote products derived from this software
1552153Sbp *    without specific prior written permission.
1652153Sbp *
1752153Sbp * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
1852153Sbp * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
1952153Sbp * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
2052153Sbp * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
2152153Sbp * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
2252153Sbp * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
2352153Sbp * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
2452153Sbp * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
2552153Sbp * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
2652153Sbp * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
2752153Sbp * SUCH DAMAGE.
2852153Sbp */
2984213Sdillon
3084213Sdillon#include <sys/cdefs.h>
3184213Sdillon__FBSDID("$FreeBSD$");
3284213Sdillon
3352153Sbp#include <stdlib.h>
3452153Sbp#include <string.h>
3552153Sbp#include <sys/types.h>
3652153Sbp#include <sys/socket.h>
3752153Sbp#include <sys/time.h>
3890868Smike#include <arpa/inet.h>
3952153Sbp#include <netipx/ipx.h>
4052153Sbp#include <errno.h>
4152153Sbp#include <unistd.h>
4252153Sbp#include "ipxsap.h"
4352153Sbp
4452153Sbp/*
4552153Sbp * TODO: These should go to ipx headers
4652153Sbp */
4752153Sbp#define ipx_set_net(x,y) ((x).x_net.s_net[0] = (y).x_net.s_net[0]); \
4852153Sbp			 ((x).x_net.s_net[1]=(y).x_net.s_net[1])
4952153Sbp#define ipx_set_nullnet(x) ((x).x_net.s_net[0]=0); ((x).x_net.s_net[1]=0)
5052153Sbp#define ipx_set_nullhost(x) ((x).x_host.s_host[0] = 0); \
5152153Sbp	((x).x_host.s_host[1] = 0); ((x).x_host.s_host[2] = 0)
5252153Sbp#define ipx_set_wildnet(x)	((x).x_net.s_net[0] = 0xFFFF); \
5352153Sbp				((x).x_net.s_net[1]=0xFFFF)
5452153Sbp#define ipx_set_wildhost(x) ((x).x_host.s_host[0] = 0xFFFF); \
5552153Sbp	((x).x_host.s_host[1] = 0xFFFF); ((x).x_host.s_host[2] = 0xFFFF);
5652153Sbp
5752153Sbp
5852153Sbpstatic struct sap_packet* sap_packet_alloc(int entries);
5952153Sbpstatic int sap_size(int entries, u_short operation);
6052153Sbpint (*sap_sendto_func)(void*,int,struct sockaddr_ipx*,int sock)=NULL;
6152153Sbp
6252153Sbpstatic int
6352153Sbpsap_sendto(void* buffer, int size, struct sockaddr_ipx* daddr, int sock)
6452153Sbp{
6552153Sbp	if (sap_sendto_func)
6652153Sbp		return sap_sendto_func(buffer,size,daddr,sock);
6752153Sbp	return sendto(sock, (char*)buffer, size, 0,
6852153Sbp	    (struct sockaddr*)daddr, sizeof(*daddr));
6952153Sbp}
7052153Sbp
7152153Sbpstatic struct sap_packet*
7252153Sbpsap_packet_alloc(int entries)
7352153Sbp{
7452153Sbp	if (entries > IPX_SAP_MAX_ENTRIES)
7552153Sbp		return NULL;
7652153Sbp	return
7752153Sbp	    (struct sap_packet*)malloc(sap_size(entries, IPX_SAP_GENERAL_RESPONSE));
7852153Sbp}
7952153Sbp
8052153Sbpstatic int
8152153Sbpsap_size(int entries, u_short operation)
8252153Sbp{
8352153Sbp	if (entries <= 0)
8452153Sbp		return 0;
8552153Sbp	switch (operation) {
8652153Sbp	    case IPX_SAP_GENERAL_QUERY:
8752153Sbp    		return entries == 1 ? IPX_SAP_REQUEST_LEN : 0;
8852153Sbp	    case IPX_SAP_GENERAL_RESPONSE:
8952153Sbp        	if (entries > IPX_SAP_MAX_ENTRIES)
9052153Sbp			return 0;
9152153Sbp    		return sizeof(struct sap_packet) + (entries - 1) * sizeof(struct sap_entry);
9252153Sbp	    case IPX_SAP_NEAREST_QUERY:
9352153Sbp                return entries == 1 ? IPX_SAP_REQUEST_LEN : 0;
9452153Sbp	    case IPX_SAP_NEAREST_RESPONSE:
9552153Sbp        	return entries == 1 ? sizeof(struct sap_packet) : 0;
9652153Sbp	    default:
9752153Sbp        	return 0;
9852153Sbp	}
9952153Sbp}
10052153Sbp
10152153Sbpvoid
10252153Sbpsap_copyname(char *dest, const char *src)
10352153Sbp{
10452153Sbp	bzero(dest, IPX_SAP_SERVER_NAME_LEN);
10552153Sbp	strncpy(dest, src, IPX_SAP_SERVER_NAME_LEN - 1);
10652153Sbp}
10752153Sbp
10852153Sbpint
10952153Sbpsap_rq_init(struct sap_rq* rq, int sock)
11052153Sbp{
11152153Sbp	rq->buffer = sap_packet_alloc(IPX_SAP_MAX_ENTRIES);
11252153Sbp	if (rq->buffer == NULL)
11352153Sbp		return 0;
11452153Sbp	rq->entries = 0;
11552153Sbp	rq->buffer->operation = htons(IPX_SAP_GENERAL_QUERY);
11652153Sbp	rq->dest_addr.sipx_family = AF_IPX;
11752153Sbp	rq->dest_addr.sipx_len = sizeof(struct sockaddr_ipx);
11852153Sbp	rq->sock = sock;
11952153Sbp	return 1;
12052153Sbp}
12152153Sbp
12252153Sbpint
12352153Sbpsap_rq_flush(struct sap_rq* rq)
12452153Sbp{
12552153Sbp	int result;
12652153Sbp
12752153Sbp	if (rq->entries == 0)
12852153Sbp		return 0;
12952153Sbp	result = sap_sendto(rq->buffer,
13052153Sbp		sap_size(rq->entries, ntohs(rq->buffer->operation)),
13152153Sbp		&rq->dest_addr, rq->sock);
13252153Sbp	rq->entries = 0;
13352153Sbp	return result;
13452153Sbp}
13552153Sbp
13652153Sbpvoid
13752153Sbpsap_rq_general_query(struct sap_rq* rq, u_short ser_type)
13852153Sbp{
13952153Sbp	struct sap_entry* sep;
14052153Sbp
14152153Sbp	sap_rq_flush(rq);
14252153Sbp	rq->buffer->operation = htons(IPX_SAP_GENERAL_QUERY);
14352153Sbp	sep = rq->buffer->sap_entries + rq->entries++;
14452153Sbp	sep->server_type = htons(ser_type);
14552153Sbp}
14652153Sbp
14752153Sbpvoid
14852153Sbpsap_rq_gns_request(struct sap_rq* rq, u_short ser_type)
14952153Sbp{
15052153Sbp	struct sap_entry* sep;
15152153Sbp
15252153Sbp	sap_rq_flush(rq);
15352153Sbp	rq->buffer->operation = htons(IPX_SAP_NEAREST_QUERY);
15452153Sbp	sep = rq->buffer->sap_entries + rq->entries++;
15552153Sbp	sep->server_type = htons(ser_type);
15652153Sbp}
15752153Sbp
15852153Sbpvoid
15952153Sbpsap_rq_general_response(struct sap_rq* rq,u_short type,char *name,struct sockaddr_ipx* addr, u_short hops,int down_allow)
16052153Sbp{
16152153Sbp	struct sap_entry* sep;
16252153Sbp
16352153Sbp	if (hops >= IPX_SAP_SERVER_DOWN && !down_allow) return;
16452153Sbp	if (rq->entries >= IPX_SAP_MAX_ENTRIES)
16552153Sbp		sap_rq_flush(rq);
16652153Sbp	if (rq->buffer->operation != htons(IPX_SAP_GENERAL_RESPONSE)){
16752153Sbp		sap_rq_flush(rq);
16852153Sbp		rq->buffer->operation = htons(IPX_SAP_GENERAL_RESPONSE);
16952153Sbp	}
17052153Sbp	sep = rq->buffer->sap_entries + rq->entries;
17152153Sbp	sep->server_type = htons(type);
17252153Sbp	sap_copyname(sep->server_name, name);
17352153Sbp	memcpy(&sep->ipx, &addr->sipx_addr, sizeof(struct ipx_addr));
17452153Sbp	sep->hops = htons(hops);
17552153Sbp	rq->entries++;
17652153Sbp}
17752153Sbp
17852153Sbpvoid
17952153Sbpsap_rq_gns_response(struct sap_rq* rq,u_short type,char *name,struct sockaddr_ipx* addr,u_short hops)
18052153Sbp{
18152153Sbp	struct sap_entry* sep;
18252153Sbp
18352153Sbp	if (hops >= IPX_SAP_SERVER_DOWN) return;
18452153Sbp	sap_rq_flush(rq);
18552153Sbp	rq->buffer->operation = htons(IPX_SAP_NEAREST_RESPONSE);
18652153Sbp	sep = rq->buffer->sap_entries + rq->entries;
18752153Sbp	sep->server_type = htons(type);
18852153Sbp	sap_copyname(sep->server_name, name);
18952153Sbp	memcpy(&sep->ipx, &addr->sipx_addr, sizeof(struct ipx_addr));
19052153Sbp	sep->hops = htons(hops);
19152153Sbp	rq->entries++;
19252153Sbp}
19352153Sbp
19452153Sbpvoid
19552153Sbpsap_rq_set_destination(struct sap_rq* rq,struct ipx_addr *dest)
19652153Sbp{
19752153Sbp	sap_rq_flush(rq);
19852153Sbp	memcpy(&rq->dest_addr.sipx_addr,dest,sizeof(struct ipx_addr));
19952153Sbp}
20052153Sbp
20152153Sbpint
20252153Sbpsap_getsock(int *rsock) {
20352153Sbp	struct sockaddr_ipx sap_addr;
20452153Sbp	int opt, sock, slen;
20552153Sbp
20652153Sbp	sock = socket(AF_IPX, SOCK_DGRAM, 0);
20752153Sbp	if (sock < 0)
20852153Sbp		return (errno);
20952153Sbp	slen = sizeof(sap_addr);
21052153Sbp	bzero(&sap_addr, slen);
21152153Sbp	sap_addr.sipx_family = AF_IPX;
21252153Sbp	sap_addr.sipx_len = slen;
21352153Sbp	if (bind(sock, (struct sockaddr*)&sap_addr, slen) == -1) {
21452153Sbp		close(sock);
21552153Sbp		return(errno);
21652153Sbp	}
21752153Sbp	opt = 1;
21852153Sbp	if (setsockopt(sock, SOL_SOCKET, SO_BROADCAST, &opt, sizeof(opt)) != 0){
21952153Sbp		close(sock);
22052153Sbp		return(errno);
22152153Sbp	}
22252153Sbp	*rsock = sock;
22352153Sbp	return(0);
22452153Sbp}
22552153Sbp
22652153Sbpstatic int
22752153Sbpsap_recv(int sock,void *buf,int len,int flags, int timeout){
22852153Sbp	fd_set rd, wr, ex;
22952153Sbp	struct timeval tv;
23052153Sbp	int result;
23152153Sbp
23252153Sbp	FD_ZERO(&rd);
23352153Sbp	FD_ZERO(&wr);
23452153Sbp	FD_ZERO(&ex);
23552153Sbp	FD_SET(sock, &rd);
23652153Sbp
23752153Sbp	tv.tv_sec = timeout;
23852153Sbp	tv.tv_usec = 0;
23952153Sbp
24052153Sbp	if ((result = select(sock + 1, &rd, &wr, &ex, &tv)) == -1) {
24152153Sbp		return result;
24252153Sbp	}
24352153Sbp	if (FD_ISSET(sock, &rd)) {
24452153Sbp		result = recv(sock, buf, len, flags);
24552153Sbp	} else {
24652153Sbp		errno = ETIMEDOUT;
24752153Sbp		result = -1;
24852153Sbp	}
24952153Sbp	return result;
25052153Sbp}
25152153Sbp
25252153Sbpint
25352153Sbpsap_find_nearest(int server_type, struct sockaddr_ipx *daddr, char *server_name)
25452153Sbp{
25552153Sbp	struct ipx_addr addr;
25652153Sbp	char data[1024];
25752153Sbp	int sock, error, packets, len;
25852153Sbp	struct sap_packet *reply = (struct sap_packet*)&data;
25952153Sbp	struct sap_rq sap_rq;
26052153Sbp
26152153Sbp	error = sap_getsock(&sock);
26252153Sbp	if (error)
26352153Sbp		return error;
26452153Sbp	bzero(&addr, sizeof(addr));
26552153Sbp	/* BAD: we should enum all ifs (and nets ?) */
26652153Sbp	if (ipx_iffind(NULL, &addr) != 0) {
26752153Sbp		return (EPROTONOSUPPORT);
26852153Sbp	}
26952153Sbp	ipx_set_wildhost(addr);
27052153Sbp	addr.x_port = htons(IPXPORT_SAP);
27152153Sbp
27252153Sbp	if (!sap_rq_init(&sap_rq, sock)) {
27352153Sbp		close(sock);
27452153Sbp		return(ENOMEM);
27552153Sbp	}
27652153Sbp	sap_rq_set_destination(&sap_rq, &addr);
27752153Sbp	sap_rq_gns_request(&sap_rq, server_type);
27852153Sbp	sap_rq_flush(&sap_rq);
27952153Sbp	packets = 5;
28052153Sbp	do {
28152153Sbp		len = sap_recv(sock, data, sizeof(data), 0, 1);
28252626Sbp		if (len >= 66 &&
28352626Sbp		    ntohs(reply->operation) == IPX_SAP_NEAREST_RESPONSE)
28452626Sbp			break;
28552626Sbp		if (len < 0)
28652626Sbp			packets--;
28752626Sbp	} while (packets > 0);
28852153Sbp
28952153Sbp	if (packets == 0) {
29052153Sbp		close(sock);
29152153Sbp		return ENETDOWN;
29252153Sbp	}
29352153Sbp
29452153Sbp	daddr->sipx_addr = reply->sap_entries[0].ipx;
29552153Sbp	daddr->sipx_family = AF_IPX;
29652153Sbp	daddr->sipx_len = sizeof(struct sockaddr_ipx);
29752153Sbp	sap_copyname(server_name, reply->sap_entries[0].server_name);
29852153Sbp	errno = 0;
29952153Sbp	close(sock);
30052153Sbp	return 0;
30152153Sbp}
302