Add the DragonFly cvs id and perform general cleanups on cvs/rcs/sccs ids. Most
[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  * $DragonFly: src/lib/libncp/sap.c,v 1.2 2003/06/17 04:26:50 dillon Exp $
34  *
35  */
36 #include <stdlib.h>
37 #include <string.h>
38 #include <sys/types.h>
39 #include <sys/socket.h>
40 #include <sys/time.h>
41 #include <netipx/ipx.h>
42 #include <errno.h>
43 #include <unistd.h>
44 #include "ipxsap.h"
45
46 /*
47  * TODO: These should go to ipx headers
48  */
49 #define ipx_set_net(x,y) ((x).x_net.s_net[0] = (y).x_net.s_net[0]); \
50                          ((x).x_net.s_net[1]=(y).x_net.s_net[1])
51 #define ipx_set_nullnet(x) ((x).x_net.s_net[0]=0); ((x).x_net.s_net[1]=0)
52 #define ipx_set_nullhost(x) ((x).x_host.s_host[0] = 0); \
53         ((x).x_host.s_host[1] = 0); ((x).x_host.s_host[2] = 0)
54 #define ipx_set_wildnet(x)      ((x).x_net.s_net[0] = 0xFFFF); \
55                                 ((x).x_net.s_net[1]=0xFFFF)
56 #define ipx_set_wildhost(x) ((x).x_host.s_host[0] = 0xFFFF); \
57         ((x).x_host.s_host[1] = 0xFFFF); ((x).x_host.s_host[2] = 0xFFFF);
58
59
60 static struct sap_packet* sap_packet_alloc(int entries);
61 static int sap_size(int entries, u_short operation);
62 int (*sap_sendto_func)(void*,int,struct sockaddr_ipx*,int sock)=NULL;
63
64 static int
65 sap_sendto(void* buffer, int size, struct sockaddr_ipx* daddr, int sock)
66
67         if (sap_sendto_func)
68                 return sap_sendto_func(buffer,size,daddr,sock);
69         return sendto(sock, (char*)buffer, size, 0,
70             (struct sockaddr*)daddr, sizeof(*daddr));
71 }
72
73 static struct sap_packet* 
74 sap_packet_alloc(int entries)
75 {
76         if (entries > IPX_SAP_MAX_ENTRIES)
77                 return NULL;
78         return 
79             (struct sap_packet*)malloc(sap_size(entries, IPX_SAP_GENERAL_RESPONSE));
80 }
81
82 static int 
83 sap_size(int entries, u_short operation)
84 {
85         if (entries <= 0)
86                 return 0;
87         switch (operation) {
88             case IPX_SAP_GENERAL_QUERY:
89                 return entries == 1 ? IPX_SAP_REQUEST_LEN : 0;
90             case IPX_SAP_GENERAL_RESPONSE:
91                 if (entries > IPX_SAP_MAX_ENTRIES)
92                         return 0;
93                 return sizeof(struct sap_packet) + (entries - 1) * sizeof(struct sap_entry);
94             case IPX_SAP_NEAREST_QUERY:
95                 return entries == 1 ? IPX_SAP_REQUEST_LEN : 0;
96             case IPX_SAP_NEAREST_RESPONSE:
97                 return entries == 1 ? sizeof(struct sap_packet) : 0;
98             default:
99                 return 0;       
100         }
101 }
102
103 void 
104 sap_copyname(char *dest, const char *src)
105 {
106         bzero(dest, IPX_SAP_SERVER_NAME_LEN);
107         strncpy(dest, src, IPX_SAP_SERVER_NAME_LEN - 1);
108 }
109
110 int
111 sap_rq_init(struct sap_rq* rq, int sock)
112 {
113         rq->buffer = sap_packet_alloc(IPX_SAP_MAX_ENTRIES);
114         if (rq->buffer == NULL)
115                 return 0;
116         rq->entries = 0;
117         rq->buffer->operation = htons(IPX_SAP_GENERAL_QUERY);
118         rq->dest_addr.sipx_family = AF_IPX;
119         rq->dest_addr.sipx_len = sizeof(struct sockaddr_ipx);
120         rq->sock = sock;
121         return 1;
122 }
123
124 int
125 sap_rq_flush(struct sap_rq* rq)
126 {
127         int result;
128
129         if (rq->entries == 0)
130                 return 0;
131         result = sap_sendto(rq->buffer, 
132                 sap_size(rq->entries, ntohs(rq->buffer->operation)), 
133                 &rq->dest_addr, rq->sock);
134         rq->entries = 0;
135         return result;
136 }
137
138 void
139 sap_rq_general_query(struct sap_rq* rq, u_short ser_type)
140 {
141         struct sap_entry* sep;
142
143         sap_rq_flush(rq);
144         rq->buffer->operation = htons(IPX_SAP_GENERAL_QUERY);
145         sep = rq->buffer->sap_entries + rq->entries++;
146         sep->server_type = htons(ser_type);
147 }
148
149 void
150 sap_rq_gns_request(struct sap_rq* rq, u_short ser_type)
151 {
152         struct sap_entry* sep;
153
154         sap_rq_flush(rq);
155         rq->buffer->operation = htons(IPX_SAP_NEAREST_QUERY);
156         sep = rq->buffer->sap_entries + rq->entries++;
157         sep->server_type = htons(ser_type);
158 }
159
160 void
161 sap_rq_general_response(struct sap_rq* rq,u_short type,char *name,struct sockaddr_ipx* addr, u_short hops,int down_allow)
162 {
163         struct sap_entry* sep;
164
165         if (hops >= IPX_SAP_SERVER_DOWN && !down_allow) return;
166         if (rq->entries >= IPX_SAP_MAX_ENTRIES)
167                 sap_rq_flush(rq);
168         if (rq->buffer->operation != htons(IPX_SAP_GENERAL_RESPONSE)){
169                 sap_rq_flush(rq);
170                 rq->buffer->operation = htons(IPX_SAP_GENERAL_RESPONSE);
171         }
172         sep = rq->buffer->sap_entries + rq->entries;
173         sep->server_type = htons(type);
174         sap_copyname(sep->server_name, name);
175         memcpy(&sep->ipx, &addr->sipx_addr, sizeof(struct ipx_addr));
176         sep->hops = htons(hops);
177         rq->entries++;
178 }
179
180 void
181 sap_rq_gns_response(struct sap_rq* rq,u_short type,char *name,struct sockaddr_ipx* addr,u_short hops)
182 {
183         struct sap_entry* sep;
184
185         if (hops >= IPX_SAP_SERVER_DOWN) return;
186         sap_rq_flush(rq);
187         rq->buffer->operation = htons(IPX_SAP_NEAREST_RESPONSE);
188         sep = rq->buffer->sap_entries + rq->entries;
189         sep->server_type = htons(type);
190         sap_copyname(sep->server_name, name);
191         memcpy(&sep->ipx, &addr->sipx_addr, sizeof(struct ipx_addr));
192         sep->hops = htons(hops);
193         rq->entries++;
194 }
195
196 void
197 sap_rq_set_destination(struct sap_rq* rq,struct ipx_addr *dest)
198 {
199         sap_rq_flush(rq);
200         memcpy(&rq->dest_addr.sipx_addr,dest,sizeof(struct ipx_addr));
201 }
202
203 int
204 sap_getsock(int *rsock) {
205         struct sockaddr_ipx sap_addr;
206         int opt, sock, slen;
207
208         sock = socket(AF_IPX, SOCK_DGRAM, 0);
209         if (sock < 0)
210                 return (errno);
211         slen = sizeof(sap_addr);
212         bzero(&sap_addr, slen);
213         sap_addr.sipx_family = AF_IPX;
214         sap_addr.sipx_len = slen;
215         if (bind(sock, (struct sockaddr*)&sap_addr, slen) == -1) {
216                 close(sock);
217                 return(errno);
218         }
219         opt = 1;
220         if (setsockopt(sock, SOL_SOCKET, SO_BROADCAST, &opt, sizeof(opt)) != 0){
221                 close(sock);
222                 return(errno);
223         }
224         *rsock = sock;
225         return(0);
226 }
227
228 static int
229 sap_recv(int sock,void *buf,int len,int flags, int timeout){
230         fd_set rd, wr, ex;
231         struct timeval tv;
232         int result;
233
234         FD_ZERO(&rd);
235         FD_ZERO(&wr);
236         FD_ZERO(&ex);
237         FD_SET(sock, &rd);
238
239         tv.tv_sec = timeout;
240         tv.tv_usec = 0;
241
242         if ((result = select(sock + 1, &rd, &wr, &ex, &tv)) == -1) {
243                 return result;
244         }
245         if (FD_ISSET(sock, &rd)) {
246                 result = recv(sock, buf, len, flags);
247         } else {
248                 errno = ETIMEDOUT;
249                 result = -1;
250         }
251         return result;
252 }
253
254 int
255 sap_find_nearest(int server_type, struct sockaddr_ipx *daddr, char *server_name)
256 {
257         struct ipx_addr addr;
258         char data[1024];
259         int sock, error, packets, len;
260         struct sap_packet *reply = (struct sap_packet*)&data;
261         struct sap_rq sap_rq;
262         
263         error = sap_getsock(&sock);
264         if (error)
265                 return error;
266         bzero(&addr, sizeof(addr));
267         /* BAD: we should enum all ifs (and nets ?) */
268         if (ipx_iffind(NULL, &addr) != 0) {
269                 return (EPROTONOSUPPORT);
270         }
271         ipx_set_wildhost(addr);         
272         addr.x_port = htons(IPXPORT_SAP);
273
274         if (!sap_rq_init(&sap_rq, sock)) {
275                 close(sock);
276                 return(ENOMEM);
277         }
278         sap_rq_set_destination(&sap_rq, &addr);
279         sap_rq_gns_request(&sap_rq, server_type);
280         sap_rq_flush(&sap_rq);
281         packets = 5;
282         do {
283                 len = sap_recv(sock, data, sizeof(data), 0, 1);
284                 if (len >= 66 && 
285                     ntohs(reply->operation) == IPX_SAP_NEAREST_RESPONSE)
286                         break;
287                 if (len < 0)
288                         packets--;
289         } while (packets > 0);
290
291         if (packets == 0) {
292                 close(sock);
293                 return ENETDOWN;
294         }
295
296         daddr->sipx_addr = reply->sap_entries[0].ipx;
297         daddr->sipx_family = AF_IPX;
298         daddr->sipx_len = sizeof(struct sockaddr_ipx);
299         sap_copyname(server_name, reply->sap_entries[0].server_name);
300         errno = 0;
301         close(sock);
302         return 0;
303 }