Merge from vendor branch BSDTAR:
[dragonfly.git] / contrib / smbfs / lib / smb / nbns_rq.c
1 /*
2  * Copyright (c) 2000, 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  * $Id: nbns_rq.c,v 1.5 2001/02/17 03:07:24 bp Exp $
33  */
34 #include <sys/param.h>
35 #include <sys/socket.h>
36 #include <sys/time.h>
37
38 #include <ctype.h>
39 #include <netdb.h>
40 #include <err.h>
41 #include <errno.h>
42 #include <stdlib.h>
43 #include <string.h>
44 #include <stdio.h>
45 #include <unistd.h>
46
47 #define NB_NEEDRESOLVER
48 #include <netsmb/netbios.h>
49 #include <netsmb/smb_lib.h>
50 #include <netsmb/nb_lib.h>
51
52
53 static int  nbns_rq_create(int opcode, struct nb_ctx *ctx, struct nbns_rq **rqpp);
54 static void nbns_rq_done(struct nbns_rq *rqp);
55 static int  nbns_rq_getrr(struct nbns_rq *rqp, struct nbns_rr *rrp);
56 static int  nbns_rq_prepare(struct nbns_rq *rqp);
57 static int  nbns_rq(struct nbns_rq *rqp);
58
59 static struct nb_ifdesc *nb_iflist;
60
61 int
62 nbns_resolvename(const char *name, struct nb_ctx *ctx, struct sockaddr **adpp)
63 {
64         struct nbns_rq *rqp;
65         struct nb_name nn;
66         struct nbns_rr rr;
67         struct sockaddr_in *dest;
68         int error, rdrcount, len;
69
70         if (strlen(name) > NB_NAMELEN)
71                 return NBERROR(NBERR_NAMETOOLONG);
72         error = nbns_rq_create(NBNS_OPCODE_QUERY, ctx, &rqp);
73         if (error)
74                 return error;
75         bzero(&nn, sizeof(nn));
76         strcpy(nn.nn_name, name);
77         nn.nn_scope = ctx->nb_scope;
78         nn.nn_type = NBT_SERVER;
79         rqp->nr_nmflags = NBNS_NMFLAG_RD;
80         rqp->nr_qdname = &nn;
81         rqp->nr_qdtype = NBNS_QUESTION_TYPE_NB;
82         rqp->nr_qdclass = NBNS_QUESTION_CLASS_IN;
83         rqp->nr_qdcount = 1;
84         dest = &rqp->nr_dest;
85         *dest = ctx->nb_ns;
86         dest->sin_family = AF_INET;
87         dest->sin_len = sizeof(*dest);
88         if (dest->sin_port == 0)
89                 dest->sin_port = htons(137);
90         if (dest->sin_addr.s_addr == INADDR_ANY)
91                 dest->sin_addr.s_addr = htonl(INADDR_BROADCAST);
92         if (dest->sin_addr.s_addr == INADDR_BROADCAST)
93                 rqp->nr_flags |= NBRQF_BROADCAST;
94         error = nbns_rq_prepare(rqp);
95         if (error) {
96                 nbns_rq_done(rqp);
97                 return error;
98         }
99         rdrcount = NBNS_MAXREDIRECTS;
100         for (;;) {
101                 error = nbns_rq(rqp);
102                 if (error)
103                         break;
104                 if ((rqp->nr_rpnmflags & NBNS_NMFLAG_AA) == 0) {
105                         if (rdrcount-- == 0) {
106                                 error = NBERROR(NBERR_TOOMANYREDIRECTS);
107                                 break;
108                         }
109                         error = nbns_rq_getrr(rqp, &rr);
110                         if (error)
111                                 break;
112                         error = nbns_rq_getrr(rqp, &rr);
113                         if (error)
114                                 break;
115                         bcopy(rr.rr_data, &dest->sin_addr, 4);
116                         rqp->nr_flags &= ~NBRQF_BROADCAST;
117                         continue;
118                 }
119                 if (rqp->nr_rpancount == 0) {
120                         error = NBERROR(NBERR_HOSTNOTFOUND);
121                         break;
122                 }
123                 error = nbns_rq_getrr(rqp, &rr);
124                 if (error)
125                         break;
126                 len = sizeof(struct sockaddr_in);
127                 dest = malloc(len);
128                 if (dest == NULL)
129                         return ENOMEM;
130                 bzero(dest, len);
131                 dest->sin_len = len;
132                 dest->sin_family = AF_INET;
133                 bcopy(rr.rr_data + 2, &dest->sin_addr.s_addr, 4);
134                 dest->sin_port = htons(SMB_TCP_PORT);
135                 *adpp = (struct sockaddr*)dest;
136                 ctx->nb_lastns = rqp->nr_sender;
137                 break;
138         }
139         nbns_rq_done(rqp);
140         return error;
141 }
142
143 int
144 nbns_rq_create(int opcode, struct nb_ctx *ctx, struct nbns_rq **rqpp)
145 {
146         struct nbns_rq *rqp;
147         static u_int16_t trnid;
148         int error;
149
150         rqp = malloc(sizeof(*rqp));
151         if (rqp == NULL)
152                 return ENOMEM;
153         bzero(rqp, sizeof(*rqp));
154         error = mb_init(&rqp->nr_rq, NBDG_MAXSIZE);
155         if (error) {
156                 free(rqp);
157                 return error;
158         }
159         rqp->nr_opcode = opcode;
160         rqp->nr_nbd = ctx;
161         rqp->nr_trnid = trnid++;
162         *rqpp = rqp;
163         return 0;
164 }
165
166 void
167 nbns_rq_done(struct nbns_rq *rqp)
168 {
169         if (rqp == NULL)
170                 return;
171         if (rqp->nr_fd >= 0)
172                 close(rqp->nr_fd);
173         mb_done(&rqp->nr_rq);
174         mb_done(&rqp->nr_rp);
175         free(rqp);
176 }
177
178 /*
179  * Extract resource record from the packet. Assume that there is only
180  * one mbuf.
181  */
182 int
183 nbns_rq_getrr(struct nbns_rq *rqp, struct nbns_rr *rrp)
184 {
185         struct mbdata *mbp = &rqp->nr_rp;
186         u_char *cp;
187         int error, len;
188
189         bzero(rrp, sizeof(*rrp));
190         cp = mbp->mb_pos;
191         len = nb_encname_len(cp);
192         if (len < 1)
193                 return NBERROR(NBERR_INVALIDRESPONSE);
194         rrp->rr_name = cp;
195         error = mb_get_mem(mbp, NULL, len);
196         if (error)
197                 return error;
198         mb_get_uint16be(mbp, &rrp->rr_type);
199         mb_get_uint16be(mbp, &rrp->rr_class);
200         mb_get_uint32be(mbp, &rrp->rr_ttl);
201         mb_get_uint16be(mbp, &rrp->rr_rdlength);
202         rrp->rr_data = mbp->mb_pos;
203         error = mb_get_mem(mbp, NULL, rrp->rr_rdlength);
204         return error;
205 }
206
207 int
208 nbns_rq_prepare(struct nbns_rq *rqp)
209 {
210         struct nb_ctx *ctx = rqp->nr_nbd;
211         struct mbdata *mbp = &rqp->nr_rq;
212         u_int8_t nmflags;
213         u_char *cp;
214         int len, error;
215
216         error = mb_init(&rqp->nr_rp, NBDG_MAXSIZE);
217         if (error)
218                 return error;
219         if (rqp->nr_dest.sin_addr.s_addr == INADDR_BROADCAST) {
220                 rqp->nr_nmflags |= NBNS_NMFLAG_BCAST;
221                 if (nb_iflist == NULL) {
222                         error = nb_enum_if(&nb_iflist, 100);
223                         if (error)
224                                 return error;
225                 }
226         } else
227                 rqp->nr_nmflags &= ~NBNS_NMFLAG_BCAST;
228         mb_put_uint16be(mbp, rqp->nr_trnid);
229         nmflags = ((rqp->nr_opcode & 0x1F) << 3) | ((rqp->nr_nmflags & 0x70) >> 4);
230         mb_put_uint8(mbp, nmflags);
231         mb_put_uint8(mbp, (rqp->nr_nmflags & 0x0f) << 4 /* rcode */);
232         mb_put_uint16be(mbp, rqp->nr_qdcount);
233         mb_put_uint16be(mbp, rqp->nr_ancount);
234         mb_put_uint16be(mbp, rqp->nr_nscount);
235         mb_put_uint16be(mbp, rqp->nr_arcount);
236         if (rqp->nr_qdcount) {
237                 if (rqp->nr_qdcount > 1)
238                         return EINVAL;
239                 len = nb_name_len(rqp->nr_qdname);
240                 error = mb_fit(mbp, len, (char**)&cp);
241                 if (error)
242                         return error;
243                 nb_name_encode(rqp->nr_qdname, cp);
244                 mb_put_uint16be(mbp, rqp->nr_qdtype);
245                 mb_put_uint16be(mbp, rqp->nr_qdclass);
246         }
247         m_lineup(mbp->mb_top, &mbp->mb_top);
248         if (ctx->nb_timo == 0)
249                 ctx->nb_timo = 1;       /* by default 1 second */
250         return 0;
251 }
252
253 static int
254 nbns_rq_recv(struct nbns_rq *rqp)
255 {
256         struct mbdata *mbp = &rqp->nr_rp;
257         void *rpdata = mtod(mbp->mb_top, void *);
258         fd_set rd, wr, ex;
259         struct timeval tv;
260         struct sockaddr_in sender;
261         int s = rqp->nr_fd;
262         int n, len;
263
264         FD_ZERO(&rd);
265         FD_ZERO(&wr);
266         FD_ZERO(&ex);
267         FD_SET(s, &rd);
268
269         tv.tv_sec = rqp->nr_nbd->nb_timo;
270         tv.tv_usec = 0;
271
272         n = select(s + 1, &rd, &wr, &ex, &tv);
273         if (n == -1)
274                 return -1;
275         if (n == 0)
276                 return ETIMEDOUT;
277         if (FD_ISSET(s, &rd) == 0)
278                 return ETIMEDOUT;
279         len = sizeof(sender);
280         n = recvfrom(s, rpdata, mbp->mb_top->m_maxlen, 0,
281             (struct sockaddr*)&sender, &len);
282         if (n < 0)
283                 return errno;
284         mbp->mb_top->m_len = mbp->mb_count = n;
285         rqp->nr_sender = sender;
286         return 0;
287 }
288
289 static int
290 nbns_rq_opensocket(struct nbns_rq *rqp)
291 {
292         struct sockaddr_in locaddr;
293         int opt, s;
294
295         s = rqp->nr_fd = socket(AF_INET, SOCK_DGRAM, 0);
296         if (s < 0)
297                 return errno;
298         if (rqp->nr_flags & NBRQF_BROADCAST) {
299                 opt = 1;
300                 if (setsockopt(s, SOL_SOCKET, SO_BROADCAST, &opt, sizeof(opt)) < 0)
301                         return errno;
302                 if (rqp->nr_if == NULL)
303                         return NBERROR(NBERR_NOBCASTIFS);
304                 bzero(&locaddr, sizeof(locaddr));
305                 locaddr.sin_family = AF_INET;
306                 locaddr.sin_len = sizeof(locaddr);
307                 locaddr.sin_addr = rqp->nr_if->id_addr;
308                 rqp->nr_dest.sin_addr.s_addr = rqp->nr_if->id_addr.s_addr | ~rqp->nr_if->id_mask.s_addr;
309                 if (bind(s, (struct sockaddr*)&locaddr, sizeof(locaddr)) < 0)
310                         return errno;
311         }
312         return 0;
313 }
314
315 static int
316 nbns_rq_send(struct nbns_rq *rqp)
317 {
318         struct mbdata *mbp = &rqp->nr_rq;
319         int s = rqp->nr_fd;
320
321         if (sendto(s, mtod(mbp->mb_top, char *), mbp->mb_count, 0,
322               (struct sockaddr*)&rqp->nr_dest, sizeof(rqp->nr_dest)) < 0)
323                 return errno;
324         return 0;
325 }
326
327 int
328 nbns_rq(struct nbns_rq *rqp)
329 {
330         struct mbdata *mbp = &rqp->nr_rq;
331         u_int16_t rpid;
332         u_int8_t nmflags;
333         int error, retrycount;
334
335         rqp->nr_if = nb_iflist;
336 again:
337         error = nbns_rq_opensocket(rqp);
338         if (error)
339                 return error;
340         retrycount = 3; /* XXX - configurable */
341         for (;;) {
342                 error = nbns_rq_send(rqp);
343                 if (error)
344                         return error;
345                 error = nbns_rq_recv(rqp);
346                 if (error) {
347                         if (error != ETIMEDOUT || retrycount == 0) {
348                                 if ((rqp->nr_nmflags & NBNS_NMFLAG_BCAST) &&
349                                     rqp->nr_if != NULL &&
350                                     rqp->nr_if->id_next != NULL) {
351                                         rqp->nr_if = rqp->nr_if->id_next;
352                                         close(rqp->nr_fd);
353                                         goto again;
354                                 } else
355                                         return error;
356                         }
357                         retrycount--;
358                         continue;
359                 }
360                 mbp = &rqp->nr_rp;
361                 if (mbp->mb_count < 12)
362                         return NBERROR(NBERR_INVALIDRESPONSE);
363                 mb_get_uint16be(mbp, &rpid);
364                 if (rpid != rqp->nr_trnid)
365                         return NBERROR(NBERR_INVALIDRESPONSE);
366                 break;
367         }
368         mb_get_uint8(mbp, &nmflags);
369         rqp->nr_rpnmflags = (nmflags & 7) << 4;
370         mb_get_uint8(mbp, &nmflags);
371         rqp->nr_rpnmflags |= (nmflags & 0xf0) >> 4;
372         rqp->nr_rprcode = nmflags & 0xf;
373         if (rqp->nr_rprcode)
374                 return NBERROR(rqp->nr_rprcode);
375         mb_get_uint16be(mbp, &rpid);    /* QDCOUNT */
376         mb_get_uint16be(mbp, &rqp->nr_rpancount);
377         mb_get_uint16be(mbp, &rqp->nr_rpnscount);
378         mb_get_uint16be(mbp, &rqp->nr_rparcount);
379         return 0;
380 }