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