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