Initial import from FreeBSD RELENG_4:
[dragonfly.git] / lib / libncp / sap.c
1 /*
2  * Copyright (c) 1999, Boris Popov
3  * All rights reserved.
4  *
5  * Redistribution and use in source and binary forms, with or without
6  * modification, are permitted provided that the following conditions
7  * are met:
8  * 1. Redistributions of source code must retain the above copyright
9  *    notice, this list of conditions and the following disclaimer.
10  * 2. Redistributions in binary form must reproduce the above copyright
11  *    notice, this list of conditions and the following disclaimer in the
12  *    documentation and/or other materials provided with the distribution.
13  * 3. All advertising materials mentioning features or use of this software
14  *    must display the following acknowledgement:
15  *    This product includes software developed by Boris Popov.
16  * 4. Neither the name of the author nor the names of any co-contributors
17  *    may be used to endorse or promote products derived from this software
18  *    without specific prior written permission.
19  *
20  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
21  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
22  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
23  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
24  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
25  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
26  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
27  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
28  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
29  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
30  * SUCH DAMAGE.
31  *
32  * $FreeBSD: src/lib/libncp/sap.c,v 1.2 1999/10/29 12:59:59 bp Exp $
33  *
34  */
35 #include <stdlib.h>
36 #include <string.h>
37 #include <sys/types.h>
38 #include <sys/socket.h>
39 #include <sys/time.h>
40 #include <netipx/ipx.h>
41 #include <errno.h>
42 #include <unistd.h>
43 #include "ipxsap.h"
44
45 /*
46  * TODO: These should go to ipx headers
47  */
48 #define ipx_set_net(x,y) ((x).x_net.s_net[0] = (y).x_net.s_net[0]); \
49                          ((x).x_net.s_net[1]=(y).x_net.s_net[1])
50 #define ipx_set_nullnet(x) ((x).x_net.s_net[0]=0); ((x).x_net.s_net[1]=0)
51 #define ipx_set_nullhost(x) ((x).x_host.s_host[0] = 0); \
52         ((x).x_host.s_host[1] = 0); ((x).x_host.s_host[2] = 0)
53 #define ipx_set_wildnet(x)      ((x).x_net.s_net[0] = 0xFFFF); \
54                                 ((x).x_net.s_net[1]=0xFFFF)
55 #define ipx_set_wildhost(x) ((x).x_host.s_host[0] = 0xFFFF); \
56         ((x).x_host.s_host[1] = 0xFFFF); ((x).x_host.s_host[2] = 0xFFFF);
57
58
59 static struct sap_packet* sap_packet_alloc(int entries);
60 static int sap_size(int entries, u_short operation);
61 int (*sap_sendto_func)(void*,int,struct sockaddr_ipx*,int sock)=NULL;
62
63 static int
64 sap_sendto(void* buffer, int size, struct sockaddr_ipx* daddr, int sock)
65
66         if (sap_sendto_func)
67                 return sap_sendto_func(buffer,size,daddr,sock);
68         return sendto(sock, (char*)buffer, size, 0,
69             (struct sockaddr*)daddr, sizeof(*daddr));
70 }
71
72 static struct sap_packet* 
73 sap_packet_alloc(int entries)
74 {
75         if (entries > IPX_SAP_MAX_ENTRIES)
76                 return NULL;
77         return 
78             (struct sap_packet*)malloc(sap_size(entries, IPX_SAP_GENERAL_RESPONSE));
79 }
80
81 static int 
82 sap_size(int entries, u_short operation)
83 {
84         if (entries <= 0)
85                 return 0;
86         switch (operation) {
87             case IPX_SAP_GENERAL_QUERY:
88                 return entries == 1 ? IPX_SAP_REQUEST_LEN : 0;
89             case IPX_SAP_GENERAL_RESPONSE:
90                 if (entries > IPX_SAP_MAX_ENTRIES)
91                         return 0;
92                 return sizeof(struct sap_packet) + (entries - 1) * sizeof(struct sap_entry);
93             case IPX_SAP_NEAREST_QUERY:
94                 return entries == 1 ? IPX_SAP_REQUEST_LEN : 0;
95             case IPX_SAP_NEAREST_RESPONSE:
96                 return entries == 1 ? sizeof(struct sap_packet) : 0;
97             default:
98                 return 0;       
99         }
100 }
101
102 void 
103 sap_copyname(char *dest, const char *src)
104 {
105         bzero(dest, IPX_SAP_SERVER_NAME_LEN);
106         strncpy(dest, src, IPX_SAP_SERVER_NAME_LEN - 1);
107 }
108
109 int
110 sap_rq_init(struct sap_rq* rq, int sock)
111 {
112         rq->buffer = sap_packet_alloc(IPX_SAP_MAX_ENTRIES);
113         if (rq->buffer == NULL)
114                 return 0;
115         rq->entries = 0;
116         rq->buffer->operation = htons(IPX_SAP_GENERAL_QUERY);
117         rq->dest_addr.sipx_family = AF_IPX;
118         rq->dest_addr.sipx_len = sizeof(struct sockaddr_ipx);
119         rq->sock = sock;
120         return 1;
121 }
122
123 int
124 sap_rq_flush(struct sap_rq* rq)
125 {
126         int result;
127
128         if (rq->entries == 0)
129                 return 0;
130         result = sap_sendto(rq->buffer, 
131                 sap_size(rq->entries, ntohs(rq->buffer->operation)), 
132                 &rq->dest_addr, rq->sock);
133         rq->entries = 0;
134         return result;
135 }
136
137 void
138 sap_rq_general_query(struct sap_rq* rq, u_short ser_type)
139 {
140         struct sap_entry* sep;
141
142         sap_rq_flush(rq);
143         rq->buffer->operation = htons(IPX_SAP_GENERAL_QUERY);
144         sep = rq->buffer->sap_entries + rq->entries++;
145         sep->server_type = htons(ser_type);
146 }
147
148 void
149 sap_rq_gns_request(struct sap_rq* rq, u_short ser_type)
150 {
151         struct sap_entry* sep;
152
153         sap_rq_flush(rq);
154         rq->buffer->operation = htons(IPX_SAP_NEAREST_QUERY);
155         sep = rq->buffer->sap_entries + rq->entries++;
156         sep->server_type = htons(ser_type);
157 }
158
159 void
160 sap_rq_general_response(struct sap_rq* rq,u_short type,char *name,struct sockaddr_ipx* addr, u_short hops,int down_allow)
161 {
162         struct sap_entry* sep;
163
164         if (hops >= IPX_SAP_SERVER_DOWN && !down_allow) return;
165         if (rq->entries >= IPX_SAP_MAX_ENTRIES)
166                 sap_rq_flush(rq);
167         if (rq->buffer->operation != htons(IPX_SAP_GENERAL_RESPONSE)){
168                 sap_rq_flush(rq);
169                 rq->buffer->operation = htons(IPX_SAP_GENERAL_RESPONSE);
170         }
171         sep = rq->buffer->sap_entries + rq->entries;
172         sep->server_type = htons(type);
173         sap_copyname(sep->server_name, name);
174         memcpy(&sep->ipx, &addr->sipx_addr, sizeof(struct ipx_addr));
175         sep->hops = htons(hops);
176         rq->entries++;
177 }
178
179 void
180 sap_rq_gns_response(struct sap_rq* rq,u_short type,char *name,struct sockaddr_ipx* addr,u_short hops)
181 {
182         struct sap_entry* sep;
183
184         if (hops >= IPX_SAP_SERVER_DOWN) return;
185         sap_rq_flush(rq);
186         rq->buffer->operation = htons(IPX_SAP_NEAREST_RESPONSE);
187         sep = rq->buffer->sap_entries + rq->entries;
188         sep->server_type = htons(type);
189         sap_copyname(sep->server_name, name);
190         memcpy(&sep->ipx, &addr->sipx_addr, sizeof(struct ipx_addr));
191         sep->hops = htons(hops);
192         rq->entries++;
193 }
194
195 void
196 sap_rq_set_destination(struct sap_rq* rq,struct ipx_addr *dest)
197 {
198         sap_rq_flush(rq);
199         memcpy(&rq->dest_addr.sipx_addr,dest,sizeof(struct ipx_addr));
200 }
201
202 int
203 sap_getsock(int *rsock) {
204         struct sockaddr_ipx sap_addr;
205         int opt, sock, slen;
206
207         sock = socket(AF_IPX, SOCK_DGRAM, 0);
208         if (sock < 0)
209                 return (errno);
210         slen = sizeof(sap_addr);
211         bzero(&sap_addr, slen);
212         sap_addr.sipx_family = AF_IPX;
213         sap_addr.sipx_len = slen;
214         if (bind(sock, (struct sockaddr*)&sap_addr, slen) == -1) {
215                 close(sock);
216                 return(errno);
217         }
218         opt = 1;
219         if (setsockopt(sock, SOL_SOCKET, SO_BROADCAST, &opt, sizeof(opt)) != 0){
220                 close(sock);
221                 return(errno);
222         }
223         *rsock = sock;
224         return(0);
225 }
226
227 static int
228 sap_recv(int sock,void *buf,int len,int flags, int timeout){
229         fd_set rd, wr, ex;
230         struct timeval tv;
231         int result;
232
233         FD_ZERO(&rd);
234         FD_ZERO(&wr);
235         FD_ZERO(&ex);
236         FD_SET(sock, &rd);
237
238         tv.tv_sec = timeout;
239         tv.tv_usec = 0;
240
241         if ((result = select(sock + 1, &rd, &wr, &ex, &tv)) == -1) {
242                 return result;
243         }
244         if (FD_ISSET(sock, &rd)) {
245                 result = recv(sock, buf, len, flags);
246         } else {
247                 errno = ETIMEDOUT;
248                 result = -1;
249         }
250         return result;
251 }
252
253 int
254 sap_find_nearest(int server_type, struct sockaddr_ipx *daddr, char *server_name)
255 {
256         struct ipx_addr addr;
257         char data[1024];
258         int sock, error, packets, len;
259         struct sap_packet *reply = (struct sap_packet*)&data;
260         struct sap_rq sap_rq;
261         
262         error = sap_getsock(&sock);
263         if (error)
264                 return error;
265         bzero(&addr, sizeof(addr));
266         /* BAD: we should enum all ifs (and nets ?) */
267         if (ipx_iffind(NULL, &addr) != 0) {
268                 return (EPROTONOSUPPORT);
269         }
270         ipx_set_wildhost(addr);         
271         addr.x_port = htons(IPXPORT_SAP);
272
273         if (!sap_rq_init(&sap_rq, sock)) {
274                 close(sock);
275                 return(ENOMEM);
276         }
277         sap_rq_set_destination(&sap_rq, &addr);
278         sap_rq_gns_request(&sap_rq, server_type);
279         sap_rq_flush(&sap_rq);
280         packets = 5;
281         do {
282                 len = sap_recv(sock, data, sizeof(data), 0, 1);
283                 if (len >= 66 && 
284                     ntohs(reply->operation) == IPX_SAP_NEAREST_RESPONSE)
285                         break;
286                 if (len < 0)
287                         packets--;
288         } while (packets > 0);
289
290         if (packets == 0) {
291                 close(sock);
292                 return ENETDOWN;
293         }
294
295         daddr->sipx_addr = reply->sap_entries[0].ipx;
296         daddr->sipx_family = AF_IPX;
297         daddr->sipx_len = sizeof(struct sockaddr_ipx);
298         sap_copyname(server_name, reply->sap_entries[0].server_name);
299         errno = 0;
300         close(sock);
301         return 0;
302 }