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