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