]> CyberLeo.Net >> Repos - FreeBSD/releng/8.1.git/blob - sys/netncp/ncp_rq.c
Copy stable/8 to releng/8.1 in preparation for 8.1-RC1.
[FreeBSD/releng/8.1.git] / sys / netncp / ncp_rq.c
1 /*-
2  * Copyright (c) 1999-2001 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  * Routines to prepare request and fetch reply
33  */ 
34
35 #include <sys/cdefs.h>
36 __FBSDID("$FreeBSD$");
37
38 #include <sys/param.h>
39 #include <sys/systm.h>
40 #include <sys/errno.h>
41 #include <sys/kernel.h>
42 #include <sys/malloc.h>
43 #include <sys/mbuf.h>
44 #include <sys/poll.h>
45 #include <sys/proc.h>
46 #include <sys/socket.h>
47 #include <sys/socketvar.h>
48 #include <sys/uio.h>
49
50 #include <netncp/ncp.h>
51 #include <netncp/ncp_conn.h>
52 #include <netncp/ncp_rq.h>
53 #include <netncp/ncp_subr.h>
54 #include <netncp/ncp_ncp.h>
55 #include <netncp/ncp_sock.h>
56 #include <netncp/ncp_nls.h>
57
58 static MALLOC_DEFINE(M_NCPRQ, "NCPRQ", "NCP request");
59
60 static int ncp_sign_packet(struct ncp_conn *conn, struct ncp_rq *rqp, int *size);
61
62 int
63 ncp_rq_alloc_any(u_int32_t ptype, u_int8_t fn, struct ncp_conn *ncp,
64         struct thread *td, struct ucred *cred,
65         struct ncp_rq **rqpp)
66 {
67         struct ncp_rq *rqp;
68         int error;
69
70         rqp = malloc(sizeof(*rqp), M_NCPRQ, M_WAITOK);
71         error = ncp_rq_init_any(rqp, ptype, fn, ncp, td, cred);
72         rqp->nr_flags |= NCPR_ALLOCED;
73         if (error) {
74                 ncp_rq_done(rqp);
75                 return error;
76         }
77         *rqpp = rqp;
78         return 0;
79 }
80
81 int
82 ncp_rq_alloc(u_int8_t fn, struct ncp_conn *ncp,
83         struct thread *td, struct ucred *cred, struct ncp_rq **rqpp)
84 {
85         return ncp_rq_alloc_any(NCP_REQUEST, fn, ncp, td, cred, rqpp);
86 }
87
88 int
89 ncp_rq_alloc_subfn(u_int8_t fn, u_int8_t subfn, struct ncp_conn *ncp,
90         struct thread *td, struct ucred *cred, struct ncp_rq **rqpp)
91 {
92         struct ncp_rq *rqp;
93         int error;
94
95         error = ncp_rq_alloc_any(NCP_REQUEST, fn, ncp, td, cred, &rqp);
96         if (error)
97                 return error;
98         mb_reserve(&rqp->rq, 2);
99         mb_put_uint8(&rqp->rq, subfn);
100         *rqpp = rqp;
101         return 0;
102 }
103
104 int
105 ncp_rq_init_any(struct ncp_rq *rqp, u_int32_t ptype, u_int8_t fn,
106         struct ncp_conn *ncp,
107         struct thread *td, struct ucred *cred)
108 {
109         struct ncp_rqhdr *rq;
110         struct ncp_bursthdr *brq;
111         struct mbchain *mbp;
112         int error;
113
114         bzero(rqp, sizeof(*rqp));
115         error = ncp_conn_access(ncp, cred, NCPM_EXECUTE);
116         if (error)
117                 return error;
118         rqp->nr_td = td;
119         rqp->nr_cred = cred;
120         rqp->nr_conn = ncp;
121         mbp = &rqp->rq;
122         if (mb_init(mbp) != 0)
123                 return ENOBUFS;
124         switch(ptype) {
125             case NCP_PACKET_BURST:
126                 brq = (struct ncp_bursthdr*)mb_reserve(mbp, sizeof(*brq));
127                 brq->bh_type = ptype;
128                 brq->bh_streamtype = 0x2;
129                 break;
130             default:
131                 rq = (struct ncp_rqhdr*)mb_reserve(mbp, sizeof(*rq));
132                 rq->type = ptype;
133                 rq->seq = 0;    /* filled later */
134                 rq->fn = fn;
135                 break;
136         }
137         rqp->nr_minrplen = -1;
138         return 0;
139 }
140
141 void
142 ncp_rq_done(struct ncp_rq *rqp)
143 {
144         mb_done(&rqp->rq);
145         md_done(&rqp->rp);
146         if (rqp->nr_flags & NCPR_ALLOCED)
147                 free(rqp, M_NCPRQ);
148         return;
149 }
150
151 /*
152  * Routines to fill the request
153  */
154
155 static int
156 ncp_rq_pathstrhelp(struct mbchain *mbp, c_caddr_t src, caddr_t dst,
157     size_t *srclen, size_t *dstlen)
158 {
159         int len;
160
161         if (*srclen < *dstlen) {
162                 *dstlen = *srclen;
163                 len = (int)*srclen;
164         } else {
165                 *srclen = *dstlen;
166                 len = (int)*dstlen;
167         }
168         ncp_pathcopy(src, dst, len, mbp->mb_udata);
169         return 0;
170 }
171
172 int
173 ncp_rq_pathstring(struct ncp_rq *rqp, int size, const char *name,
174         struct ncp_nlstables *nt)
175 {
176         struct mbchain *mbp = &rqp->rq;
177
178         mb_put_uint8(mbp, size);
179         mbp->mb_copy = ncp_rq_pathstrhelp;
180         mbp->mb_udata = nt;
181         return mb_put_mem(mbp, (c_caddr_t)name, size, MB_MCUSTOM);
182 }
183
184 int 
185 ncp_rq_pstring(struct ncp_rq *rqp, const char *s)
186 {
187         u_int len = strlen(s);
188         int error;
189
190         if (len > 255)
191                 return EINVAL;
192         error = mb_put_uint8(&rqp->rq, len);
193         if (error)
194                 return error;
195         return mb_put_mem(&rqp->rq, s, len, MB_MSYSTEM);
196 }
197
198 int 
199 ncp_rq_dbase_path(struct ncp_rq *rqp, u_int8_t vol_num, u_int32_t dir_base,
200                     int namelen, u_char *path, struct ncp_nlstables *nt)
201 {
202         struct mbchain *mbp = &rqp->rq;
203         int complen;
204
205         mb_put_uint8(mbp, vol_num);
206         mb_put_mem(mbp, (c_caddr_t)&dir_base, sizeof(dir_base), MB_MSYSTEM);
207         mb_put_uint8(mbp, 1);   /* with dirbase */
208         if (path != NULL && path[0]) {
209                 if (namelen < 0) {
210                         namelen = *path++;
211                         mb_put_uint8(mbp, namelen);
212                         for(; namelen; namelen--) {
213                                 complen = *path++;
214                                 mb_put_uint8(mbp, complen);
215                                 mb_put_mem(mbp, path, complen, MB_MSYSTEM);
216                                 path += complen;
217                         }
218                 } else {
219                         mb_put_uint8(mbp, 1);   /* 1 component */
220                         ncp_rq_pathstring(rqp, namelen, path, nt);
221                 }
222         } else {
223                 mb_put_uint8(mbp, 0);
224                 mb_put_uint8(mbp, 0);
225         }
226         return 0;
227 }
228
229 /* 
230  * Make a signature for the current packet and add it at the end of the
231  * packet.
232  */
233 static int
234 ncp_sign_packet(struct ncp_conn *conn, struct ncp_rq *rqp, int *size)
235 {
236         u_char data[64];
237         int error;
238
239         bzero(data, sizeof(data));
240         bcopy(conn->sign_root, data, 8);
241         setdle(data, 8, *size);
242         m_copydata(rqp->rq.mb_top, sizeof(struct ncp_rqhdr) - 1,
243                 min((*size) - sizeof(struct ncp_rqhdr)+1, 52), data + 12);
244         ncp_sign(conn->sign_state, data, conn->sign_state);
245         error = mb_put_mem(&rqp->rq, (caddr_t)conn->sign_state, 8, MB_MSYSTEM);
246         if (error)
247                 return error;
248         (*size) += 8;
249         return 0;
250 }
251
252 /*
253  * Low level send rpc, here we do not attempt to restore any connection,
254  * Connection expected to be locked
255  */
256 int
257 ncp_request_int(struct ncp_rq *rqp)
258 {
259         struct ncp_conn *conn = rqp->nr_conn;
260         struct thread *td = conn->td;
261         struct socket *so = conn->ncp_so;
262         struct ncp_rqhdr *rq;
263         struct ncp_rphdr *rp=NULL;
264         struct timeval tv;
265         struct mbuf *m, *mreply = NULL;
266         struct mbchain *mbp;
267         int error, len, dosend, plen = 0, gotpacket;
268
269         if (so == NULL) {
270                 printf("%s: ncp_so is NULL !\n",__func__);
271                 ncp_conn_invalidate(conn);
272                 return ENOTCONN;
273         }
274         if (td == NULL)
275                 td = curthread; /* XXX maybe procpage ? */
276         /*
277          * Flush out replies on previous reqs
278          */
279         tv.tv_sec = 0;
280         tv.tv_usec = 0;
281         while (selsocket(so, POLLIN, &tv, td) == 0) {
282                 if (ncp_sock_recv(so, &m, &len) != 0)
283                         break;
284                 m_freem(m);
285         }
286         mbp = &rqp->rq;
287         len = mb_fixhdr(mbp);
288         rq = mtod(mbp->mb_top, struct ncp_rqhdr *);
289         rq->seq = conn->seq;
290         m = rqp->rq.mb_top;
291
292         switch (rq->fn) {
293             case 0x15: case 0x16: case 0x17: case 0x23:
294                 *(u_int16_t*)(rq + 1) = htons(len - 2 - sizeof(*rq));
295                 break;
296         }
297         if (conn->flags & NCPFL_SIGNACTIVE) {
298                 error = ncp_sign_packet(conn, rqp, &len);
299                 if (error)
300                         return error;
301                 mbp->mb_top->m_pkthdr.len = len;
302         }
303         rq->conn_low = conn->connid & 0xff;
304         /* rq->task = p->p_pgrp->pg_id & 0xff; */ /*p->p_pid*/
305         /* XXX: this is temporary fix till I find a better solution */
306         rq->task = rq->conn_low;
307         rq->conn_high = conn->connid >> 8;
308         rqp->rexmit = conn->li.retry_count;
309         error = 0;
310         for(dosend = 1;;) {
311                 if (rqp->rexmit-- == 0) {
312                         error = ETIMEDOUT;
313                         break;
314                 }
315                 error = 0;
316                 if (dosend) {
317                         NCPSDEBUG("send:%04x f=%02x c=%d l=%d s=%d t=%d\n",rq->type, rq->fn, (rq->conn_high << 8) + rq->conn_low,
318                                 mbp->mb_top->m_pkthdr.len, rq->seq, rq->task
319                         );
320                         error = ncp_sock_send(so, mbp->mb_top, rqp);
321                         if (error)
322                                 break;
323                 }
324                 tv.tv_sec = conn->li.timeout;
325                 tv.tv_usec = 0;
326                 error = selsocket(so, POLLIN, &tv, td);
327                 if (error == EWOULDBLOCK )      /* timeout expired */
328                         continue;
329                 error = ncp_chkintr(conn, td);
330                 if (error)
331                         break;
332                 /*
333                  * At this point it is possible to get more than one
334                  * reply from server. In general, last reply should be for
335                  * current request, but not always. So, we loop through
336                  * all replies to find the right answer and flush others.
337                  */
338                 gotpacket = 0;  /* nothing good found */
339                 dosend = 1;     /* resend rq if error */
340                 for (;;) {
341                         error = 0;
342                         tv.tv_sec = 0;
343                         tv.tv_usec = 0;
344                         if (selsocket(so, POLLIN, &tv, td) != 0)
345                                 break;
346 /*                      if (so->so_rcv.sb_cc == 0) {
347                                 break;
348                         }*/
349                         error = ncp_sock_recv(so, &m, &len);
350                         if (error)
351                                 break;          /* must be more checks !!! */
352                         if (m->m_len < sizeof(*rp)) {
353                                 m = m_pullup(m, sizeof(*rp));
354                                 if (m == NULL) {
355                                         printf("%s: reply too short\n",__func__);
356                                         continue;
357                                 }
358                         }
359                         rp = mtod(m, struct ncp_rphdr*);
360                         if (len == sizeof(*rp) && rp->type == NCP_POSITIVE_ACK) {
361                                 NCPSDEBUG("got positive acknowledge\n");
362                                 m_freem(m);
363                                 rqp->rexmit = conn->li.retry_count;
364                                 dosend = 0;     /* server just busy and will reply ASAP */
365                                 continue;
366                         }
367                         NCPSDEBUG("recv:%04x c=%d l=%d s=%d t=%d cc=%02x cs=%02x\n",rp->type,
368                             (rp->conn_high << 8) + rp->conn_low, len, rp->seq, rp->task,
369                              rp->completion_code, rp->connection_state);
370                         NCPDDEBUG(m);
371                         if ( (rp->type == NCP_REPLY) && 
372                             ((rq->type == NCP_ALLOC_SLOT) || 
373                             ((rp->conn_low == rq->conn_low) &&
374                              (rp->conn_high == rq->conn_high)
375                             ))) {
376                                 if (rq->seq > rp->seq || (rq->seq == 0 && rp->seq == 0xff)) {
377                                         dosend = 1;
378                                 }
379                                 if (rp->seq == rq->seq) {
380                                         if (gotpacket) {
381                                                 m_freem(m);
382                                         } else {
383                                                 gotpacket = 1;
384                                                 mreply = m;
385                                                 plen = len;
386                                         }
387                                         continue;       /* look up other for other packets */
388                                 }
389                         }
390                         m_freem(m);
391                         NCPSDEBUG("reply mismatch\n");
392                 } /* for receive */
393                 if (error || gotpacket)
394                         break;
395                 /* try to resend, or just wait */
396         }
397         conn->seq++;
398         if (error) {
399                 NCPSDEBUG("error=%d\n", error);
400                 /*
401                  * Any error except interruped call means that we have
402                  * to reconnect. So, eliminate future timeouts by invalidating
403                  * connection now.
404                  */
405                 if (error != EINTR)
406                         ncp_conn_invalidate(conn);
407                 return (error);
408         }
409         if (conn->flags & NCPFL_SIGNACTIVE) {
410                 /* XXX: check reply signature */
411                 m_adj(mreply, -8);
412                 plen -= 8;
413         }
414         rp = mtod(mreply, struct ncp_rphdr*);
415         md_initm(&rqp->rp, mreply);
416         rqp->nr_rpsize = plen - sizeof(*rp);
417         rqp->nr_cc = error = rp->completion_code;
418         if (error)
419                 error |= 0x8900;        /* server error */
420         rqp->nr_cs = rp->connection_state;
421         if (rqp->nr_cs & (NCP_CS_BAD_CONN | NCP_CS_SERVER_DOWN)) {
422                 NCPSDEBUG("server drop us\n");
423                 ncp_conn_invalidate(conn);
424                 error = ECONNRESET;
425         }
426         md_get_mem(&rqp->rp, NULL, sizeof(*rp), MB_MSYSTEM);
427         return error;
428 }
429
430 /*
431  * Here we will try to restore any loggedin & dropped connection,
432  * connection should be locked on entry
433  */
434 static __inline int
435 ncp_restore_login(struct ncp_conn *conn)
436 {
437         int error;
438
439         printf("ncprq: Restoring connection, flags = %x\n", conn->flags);
440         conn->flags |= NCPFL_RESTORING;
441         error = ncp_conn_reconnect(conn);
442         if (!error && (conn->flags & NCPFL_WASLOGGED))
443                 error = ncp_conn_login(conn, conn->td, conn->ucred);
444         if (error)
445                 ncp_ncp_disconnect(conn);
446         conn->flags &= ~NCPFL_RESTORING;
447         return error;
448 }
449
450 int
451 ncp_request(struct ncp_rq *rqp)
452 {
453         struct ncp_conn *ncp = rqp->nr_conn;
454         int error, rcnt;
455
456         error = ncp_conn_lock(ncp, rqp->nr_td, rqp->nr_cred, NCPM_EXECUTE);
457         if (error)
458                 goto out;
459         rcnt = NCP_RESTORE_COUNT;
460         for(;;) {
461                 if (ncp->flags & NCPFL_ATTACHED) {
462                         error = ncp_request_int(rqp);
463                         if (ncp->flags & NCPFL_ATTACHED)
464                                 break;
465                 }
466                 if (rcnt-- == 0) {
467                         error = ECONNRESET;
468                         break;
469                 }
470                 /*
471                  * Do not attempt to restore connection recursively
472                  */
473                 if (ncp->flags & NCPFL_RESTORING) {
474                         error = ENOTCONN;
475                         break;
476                 }
477                 error = ncp_restore_login(ncp);
478                 if (error)
479                         continue;
480         }
481         ncp_conn_unlock(ncp, rqp->nr_td);
482 out:
483         if (error && (rqp->nr_flags & NCPR_DONTFREEONERR) == 0)
484                 ncp_rq_done(rqp);
485         return error;
486 }