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