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