FreeBSD/Linux Kernel Cross Reference
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,
155 size_t *srclen, size_t *dstlen)
156 {
157 int len;
158
159 if (*srclen < *dstlen) {
160 *dstlen = *srclen;
161 len = (int)*srclen;
162 } else {
163 *srclen = *dstlen;
164 len = (int)*dstlen;
165 }
166 ncp_pathcopy(src, dst, len, mbp->mb_udata);
167 return 0;
168 }
169
170 int
171 ncp_rq_pathstring(struct ncp_rq *rqp, int size, const char *name,
172 struct ncp_nlstables *nt)
173 {
174 struct mbchain *mbp = &rqp->rq;
175
176 mb_put_uint8(mbp, size);
177 mbp->mb_copy = ncp_rq_pathstrhelp;
178 mbp->mb_udata = nt;
179 return mb_put_mem(mbp, (c_caddr_t)name, size, MB_MCUSTOM);
180 }
181
182 int
183 ncp_rq_pstring(struct ncp_rq *rqp, const char *s)
184 {
185 u_int len = strlen(s);
186 int error;
187
188 if (len > 255)
189 return EINVAL;
190 error = mb_put_uint8(&rqp->rq, len);
191 if (error)
192 return error;
193 return mb_put_mem(&rqp->rq, s, len, MB_MSYSTEM);
194 }
195
196 int
197 ncp_rq_dbase_path(struct ncp_rq *rqp, u_int8_t vol_num, u_int32_t dir_base,
198 int namelen, u_char *path, struct ncp_nlstables *nt)
199 {
200 struct mbchain *mbp = &rqp->rq;
201 int complen;
202
203 mb_put_uint8(mbp, vol_num);
204 mb_put_mem(mbp, (c_caddr_t)&dir_base, sizeof(dir_base), MB_MSYSTEM);
205 mb_put_uint8(mbp, 1); /* with dirbase */
206 if (path != NULL && path[0]) {
207 if (namelen < 0) {
208 namelen = *path++;
209 mb_put_uint8(mbp, namelen);
210 for(; namelen; namelen--) {
211 complen = *path++;
212 mb_put_uint8(mbp, complen);
213 mb_put_mem(mbp, path, complen, MB_MSYSTEM);
214 path += complen;
215 }
216 } else {
217 mb_put_uint8(mbp, 1); /* 1 component */
218 ncp_rq_pathstring(rqp, namelen, path, nt);
219 }
220 } else {
221 mb_put_uint8(mbp, 0);
222 mb_put_uint8(mbp, 0);
223 }
224 return 0;
225 }
226
227 /*
228 * Make a signature for the current packet and add it at the end of the
229 * packet.
230 */
231 static int
232 ncp_sign_packet(struct ncp_conn *conn, struct ncp_rq *rqp, int *size)
233 {
234 u_char data[64];
235 int error;
236
237 bzero(data, sizeof(data));
238 bcopy(conn->sign_root, data, 8);
239 setdle(data, 8, *size);
240 m_copydata(rqp->rq.mb_top, sizeof(struct ncp_rqhdr) - 1,
241 min((*size) - sizeof(struct ncp_rqhdr)+1, 52), data + 12);
242 ncp_sign(conn->sign_state, data, conn->sign_state);
243 error = mb_put_mem(&rqp->rq, (caddr_t)conn->sign_state, 8, MB_MSYSTEM);
244 if (error)
245 return error;
246 (*size) += 8;
247 return 0;
248 }
249
250 /*
251 * Low level send rpc, here we do not attempt to restore any connection,
252 * Connection expected to be locked
253 */
254 int
255 ncp_request_int(struct ncp_rq *rqp)
256 {
257 struct ncp_conn *conn = rqp->nr_conn;
258 struct thread *td = conn->td;
259 struct socket *so = conn->ncp_so;
260 struct ncp_rqhdr *rq;
261 struct ncp_rphdr *rp=NULL;
262 struct timeval tv;
263 struct mbuf *m, *mreply = NULL;
264 struct mbchain *mbp;
265 int error, len, dosend, plen = 0, gotpacket;
266
267 if (so == NULL) {
268 printf("%s: ncp_so is NULL !\n",__func__);
269 ncp_conn_invalidate(conn);
270 return ENOTCONN;
271 }
272 if (td == NULL)
273 td = curthread; /* XXX maybe procpage ? */
274 /*
275 * Flush out replies on previous reqs
276 */
277 while (ncp_poll(so, POLLIN) != 0) {
278 if (ncp_sock_recv(so, &m, &len) != 0)
279 break;
280 m_freem(m);
281 }
282 mbp = &rqp->rq;
283 len = mb_fixhdr(mbp);
284 rq = mtod(mbp->mb_top, struct ncp_rqhdr *);
285 rq->seq = conn->seq;
286 m = rqp->rq.mb_top;
287
288 switch (rq->fn) {
289 case 0x15: case 0x16: case 0x17: case 0x23:
290 *(u_int16_t*)(rq + 1) = htons(len - 2 - sizeof(*rq));
291 break;
292 }
293 if (conn->flags & NCPFL_SIGNACTIVE) {
294 error = ncp_sign_packet(conn, rqp, &len);
295 if (error)
296 return error;
297 mbp->mb_top->m_pkthdr.len = len;
298 }
299 rq->conn_low = conn->connid & 0xff;
300 /* rq->task = p->p_pgrp->pg_id & 0xff; */ /*p->p_pid*/
301 /* XXX: this is temporary fix till I find a better solution */
302 rq->task = rq->conn_low;
303 rq->conn_high = conn->connid >> 8;
304 rqp->rexmit = conn->li.retry_count;
305 error = 0;
306 for(dosend = 1;;) {
307 if (rqp->rexmit-- == 0) {
308 error = ETIMEDOUT;
309 break;
310 }
311 error = 0;
312 if (dosend) {
313 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,
314 mbp->mb_top->m_pkthdr.len, rq->seq, rq->task
315 );
316 error = ncp_sock_send(so, mbp->mb_top, rqp);
317 if (error)
318 break;
319 }
320 tv.tv_sec = conn->li.timeout;
321 tv.tv_usec = 0;
322 error = ncp_sock_rselect(so, td, &tv, POLLIN);
323 if (error == EWOULDBLOCK ) /* timeout expired */
324 continue;
325 error = ncp_chkintr(conn, td);
326 if (error)
327 break;
328 /*
329 * At this point it is possible to get more than one
330 * reply from server. In general, last reply should be for
331 * current request, but not always. So, we loop through
332 * all replies to find the right answer and flush others.
333 */
334 gotpacket = 0; /* nothing good found */
335 dosend = 1; /* resend rq if error */
336 for (;;) {
337 error = 0;
338 if (ncp_poll(so, POLLIN) == 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 }
Cache object: 6a2be684a222d7435e060c067cef033e
|