1139823Simp/*-
274647Sbp * Copyright (c) 1999-2001 Boris Popov
351852Sbp * All rights reserved.
451852Sbp *
551852Sbp * Redistribution and use in source and binary forms, with or without
651852Sbp * modification, are permitted provided that the following conditions
751852Sbp * are met:
851852Sbp * 1. Redistributions of source code must retain the above copyright
951852Sbp *    notice, this list of conditions and the following disclaimer.
1051852Sbp * 2. Redistributions in binary form must reproduce the above copyright
1151852Sbp *    notice, this list of conditions and the following disclaimer in the
1251852Sbp *    documentation and/or other materials provided with the distribution.
1351852Sbp *
1451852Sbp * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
1551852Sbp * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
1651852Sbp * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
1751852Sbp * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
1851852Sbp * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
1951852Sbp * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
2051852Sbp * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
2151852Sbp * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
2251852Sbp * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
2351852Sbp * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
2451852Sbp * SUCH DAMAGE.
2551852Sbp *
2651852Sbp * Routines to prepare request and fetch reply
2751852Sbp */
28116189Sobrien
29116189Sobrien#include <sys/cdefs.h>
30116189Sobrien__FBSDID("$FreeBSD$");
31116189Sobrien
3251852Sbp#include <sys/param.h>
3351852Sbp#include <sys/systm.h>
3451852Sbp#include <sys/errno.h>
3574060Sbp#include <sys/kernel.h>
3674060Sbp#include <sys/malloc.h>
3751852Sbp#include <sys/mbuf.h>
3874060Sbp#include <sys/poll.h>
3976166Smarkm#include <sys/proc.h>
40174647Sjeff#include <sys/socket.h>
41174647Sjeff#include <sys/socketvar.h>
4251852Sbp#include <sys/uio.h>
4351852Sbp
4451852Sbp#include <netncp/ncp.h>
4551852Sbp#include <netncp/ncp_conn.h>
4651852Sbp#include <netncp/ncp_rq.h>
4751852Sbp#include <netncp/ncp_subr.h>
4851852Sbp#include <netncp/ncp_ncp.h>
4974060Sbp#include <netncp/ncp_sock.h>
5051852Sbp#include <netncp/ncp_nls.h>
5151852Sbp
5274060Sbpstatic MALLOC_DEFINE(M_NCPRQ, "NCPRQ", "NCP request");
5374060Sbp
5474060Sbpstatic int ncp_sign_packet(struct ncp_conn *conn, struct ncp_rq *rqp, int *size);
5574060Sbp
5651852Sbpint
5774060Sbpncp_rq_alloc_any(u_int32_t ptype, u_int8_t fn, struct ncp_conn *ncp,
58111577Sfjoe	struct thread *td, struct ucred *cred,
5974060Sbp	struct ncp_rq **rqpp)
6051852Sbp{
6174060Sbp	struct ncp_rq *rqp;
6274060Sbp	int error;
6374060Sbp
64184205Sdes	rqp = malloc(sizeof(*rqp), M_NCPRQ, M_WAITOK);
65111577Sfjoe	error = ncp_rq_init_any(rqp, ptype, fn, ncp, td, cred);
6674060Sbp	rqp->nr_flags |= NCPR_ALLOCED;
6774060Sbp	if (error) {
6874060Sbp		ncp_rq_done(rqp);
6974060Sbp		return error;
7074060Sbp	}
7174060Sbp	*rqpp = rqp;
7274060Sbp	return 0;
7374060Sbp}
7474060Sbp
7574060Sbpint
7674060Sbpncp_rq_alloc(u_int8_t fn, struct ncp_conn *ncp,
77111577Sfjoe	struct thread *td, struct ucred *cred, struct ncp_rq **rqpp)
7874060Sbp{
79111577Sfjoe	return ncp_rq_alloc_any(NCP_REQUEST, fn, ncp, td, cred, rqpp);
8074060Sbp}
8174060Sbp
8274060Sbpint
8374060Sbpncp_rq_alloc_subfn(u_int8_t fn, u_int8_t subfn, struct ncp_conn *ncp,
84111577Sfjoe	struct thread *td, struct ucred *cred, struct ncp_rq **rqpp)
8574060Sbp{
8674060Sbp	struct ncp_rq *rqp;
8774060Sbp	int error;
8874060Sbp
89111577Sfjoe	error = ncp_rq_alloc_any(NCP_REQUEST, fn, ncp, td, cred, &rqp);
9074060Sbp	if (error)
9174060Sbp		return error;
9274060Sbp	mb_reserve(&rqp->rq, 2);
9374060Sbp	mb_put_uint8(&rqp->rq, subfn);
9474060Sbp	*rqpp = rqp;
9574060Sbp	return 0;
9674060Sbp}
9774060Sbp
9874060Sbpint
9974060Sbpncp_rq_init_any(struct ncp_rq *rqp, u_int32_t ptype, u_int8_t fn,
10074060Sbp	struct ncp_conn *ncp,
101111577Sfjoe	struct thread *td, struct ucred *cred)
10274060Sbp{
10351852Sbp	struct ncp_rqhdr *rq;
10451852Sbp	struct ncp_bursthdr *brq;
10574060Sbp	struct mbchain *mbp;
10674060Sbp	int error;
10751852Sbp
10851852Sbp	bzero(rqp, sizeof(*rqp));
10974060Sbp	error = ncp_conn_access(ncp, cred, NCPM_EXECUTE);
11074060Sbp	if (error)
11174060Sbp		return error;
112111577Sfjoe	rqp->nr_td = td;
11374060Sbp	rqp->nr_cred = cred;
11474060Sbp	rqp->nr_conn = ncp;
11574060Sbp	mbp = &rqp->rq;
11674060Sbp	if (mb_init(mbp) != 0)
11774060Sbp		return ENOBUFS;
11851852Sbp	switch(ptype) {
11951852Sbp	    case NCP_PACKET_BURST:
12074060Sbp		brq = (struct ncp_bursthdr*)mb_reserve(mbp, sizeof(*brq));
12151852Sbp		brq->bh_type = ptype;
12251852Sbp		brq->bh_streamtype = 0x2;
12351852Sbp		break;
12451852Sbp	    default:
12574060Sbp		rq = (struct ncp_rqhdr*)mb_reserve(mbp, sizeof(*rq));
12651852Sbp		rq->type = ptype;
12751852Sbp		rq->seq = 0;	/* filled later */
12851852Sbp		rq->fn = fn;
12951852Sbp		break;
13051852Sbp	}
13174060Sbp	rqp->nr_minrplen = -1;
13251852Sbp	return 0;
13351852Sbp}
13451852Sbp
13574060Sbpvoid
13674060Sbpncp_rq_done(struct ncp_rq *rqp)
13774060Sbp{
13874060Sbp	mb_done(&rqp->rq);
13974060Sbp	md_done(&rqp->rp);
14074060Sbp	if (rqp->nr_flags & NCPR_ALLOCED)
14174060Sbp		free(rqp, M_NCPRQ);
14274060Sbp	return;
14351852Sbp}
14451852Sbp
14551852Sbp/*
14651852Sbp * Routines to fill the request
14751852Sbp */
14851852Sbp
14974060Sbpstatic int
150148517Simurancp_rq_pathstrhelp(struct mbchain *mbp, c_caddr_t src, caddr_t dst,
151148517Simura    size_t *srclen, size_t *dstlen)
15274060Sbp{
153148517Simura	int len;
154148517Simura
155148517Simura	if (*srclen < *dstlen) {
156148517Simura		*dstlen = *srclen;
157148517Simura		len = (int)*srclen;
158148517Simura	} else {
159148517Simura		*srclen = *dstlen;
160148517Simura		len = (int)*dstlen;
161148517Simura	}
16274060Sbp	ncp_pathcopy(src, dst, len, mbp->mb_udata);
16374060Sbp	return 0;
16451852Sbp}
16551852Sbp
16651852Sbpint
16774060Sbpncp_rq_pathstring(struct ncp_rq *rqp, int size, const char *name,
16874060Sbp	struct ncp_nlstables *nt)
16974060Sbp{
17074060Sbp	struct mbchain *mbp = &rqp->rq;
17151852Sbp
17274060Sbp	mb_put_uint8(mbp, size);
17374060Sbp	mbp->mb_copy = ncp_rq_pathstrhelp;
17474060Sbp	mbp->mb_udata = nt;
17574060Sbp	return mb_put_mem(mbp, (c_caddr_t)name, size, MB_MCUSTOM);
17651852Sbp}
17751852Sbp
17874060Sbpint
17974060Sbpncp_rq_pstring(struct ncp_rq *rqp, const char *s)
18074060Sbp{
18174060Sbp	u_int len = strlen(s);
18274060Sbp	int error;
18351852Sbp
18474060Sbp	if (len > 255)
18574060Sbp		return EINVAL;
18674060Sbp	error = mb_put_uint8(&rqp->rq, len);
18774060Sbp	if (error)
18874060Sbp		return error;
18974060Sbp	return mb_put_mem(&rqp->rq, s, len, MB_MSYSTEM);
19051852Sbp}
19151852Sbp
19274060Sbpint
19351852Sbpncp_rq_dbase_path(struct ncp_rq *rqp, u_int8_t vol_num, u_int32_t dir_base,
19451852Sbp                    int namelen, u_char *path, struct ncp_nlstables *nt)
19551852Sbp{
19674060Sbp	struct mbchain *mbp = &rqp->rq;
19751852Sbp	int complen;
19851852Sbp
19974060Sbp	mb_put_uint8(mbp, vol_num);
20074060Sbp	mb_put_mem(mbp, (c_caddr_t)&dir_base, sizeof(dir_base), MB_MSYSTEM);
20174060Sbp	mb_put_uint8(mbp, 1);	/* with dirbase */
20251852Sbp	if (path != NULL && path[0]) {
20351852Sbp		if (namelen < 0) {
20451852Sbp			namelen = *path++;
20574060Sbp			mb_put_uint8(mbp, namelen);
20651852Sbp			for(; namelen; namelen--) {
20751852Sbp				complen = *path++;
20874060Sbp				mb_put_uint8(mbp, complen);
20974060Sbp				mb_put_mem(mbp, path, complen, MB_MSYSTEM);
21051852Sbp				path += complen;
21151852Sbp			}
21251852Sbp		} else {
21374060Sbp			mb_put_uint8(mbp, 1);	/* 1 component */
21451852Sbp			ncp_rq_pathstring(rqp, namelen, path, nt);
21551852Sbp		}
21651852Sbp	} else {
21774060Sbp		mb_put_uint8(mbp, 0);
21874060Sbp		mb_put_uint8(mbp, 0);
21951852Sbp	}
22074060Sbp	return 0;
22151852Sbp}
22274060Sbp
22374060Sbp/*
22474060Sbp * Make a signature for the current packet and add it at the end of the
22574060Sbp * packet.
22651852Sbp */
22774060Sbpstatic int
22874060Sbpncp_sign_packet(struct ncp_conn *conn, struct ncp_rq *rqp, int *size)
22974060Sbp{
23074060Sbp	u_char data[64];
23174060Sbp	int error;
23251852Sbp
23374060Sbp	bzero(data, sizeof(data));
23474060Sbp	bcopy(conn->sign_root, data, 8);
23574060Sbp	setdle(data, 8, *size);
23674060Sbp	m_copydata(rqp->rq.mb_top, sizeof(struct ncp_rqhdr) - 1,
23774060Sbp		min((*size) - sizeof(struct ncp_rqhdr)+1, 52), data + 12);
23874060Sbp	ncp_sign(conn->sign_state, data, conn->sign_state);
23974060Sbp	error = mb_put_mem(&rqp->rq, (caddr_t)conn->sign_state, 8, MB_MSYSTEM);
24074060Sbp	if (error)
24174060Sbp		return error;
24274060Sbp	(*size) += 8;
24374060Sbp	return 0;
24451852Sbp}
24551852Sbp
24674060Sbp/*
24774060Sbp * Low level send rpc, here we do not attempt to restore any connection,
24874060Sbp * Connection expected to be locked
24974060Sbp */
250111577Sfjoeint
25174060Sbpncp_request_int(struct ncp_rq *rqp)
25274060Sbp{
25374060Sbp	struct ncp_conn *conn = rqp->nr_conn;
254111577Sfjoe	struct thread *td = conn->td;
25574060Sbp	struct socket *so = conn->ncp_so;
25674060Sbp	struct ncp_rqhdr *rq;
25774060Sbp	struct ncp_rphdr *rp=NULL;
25874060Sbp	struct timeval tv;
25974060Sbp	struct mbuf *m, *mreply = NULL;
26074060Sbp	struct mbchain *mbp;
26174060Sbp	int error, len, dosend, plen = 0, gotpacket;
26251852Sbp
26374060Sbp	if (so == NULL) {
26487599Sobrien		printf("%s: ncp_so is NULL !\n",__func__);
26574646Sbp		ncp_conn_invalidate(conn);
26674060Sbp		return ENOTCONN;
26751852Sbp	}
268111577Sfjoe	if (td == NULL)
269111577Sfjoe		td = curthread;	/* XXX maybe procpage ? */
27074060Sbp	/*
27174060Sbp	 * Flush out replies on previous reqs
27274060Sbp	 */
273174647Sjeff	tv.tv_sec = 0;
274174647Sjeff	tv.tv_usec = 0;
275174647Sjeff	while (selsocket(so, POLLIN, &tv, td) == 0) {
27674060Sbp		if (ncp_sock_recv(so, &m, &len) != 0)
27774060Sbp			break;
27874060Sbp		m_freem(m);
27974060Sbp	}
28074060Sbp	mbp = &rqp->rq;
28174060Sbp	len = mb_fixhdr(mbp);
28274060Sbp	rq = mtod(mbp->mb_top, struct ncp_rqhdr *);
28374060Sbp	rq->seq = conn->seq;
28474060Sbp	m = rqp->rq.mb_top;
28551852Sbp
28674060Sbp	switch (rq->fn) {
28774060Sbp	    case 0x15: case 0x16: case 0x17: case 0x23:
28874060Sbp		*(u_int16_t*)(rq + 1) = htons(len - 2 - sizeof(*rq));
28974060Sbp		break;
29051852Sbp	}
29174060Sbp	if (conn->flags & NCPFL_SIGNACTIVE) {
29274060Sbp		error = ncp_sign_packet(conn, rqp, &len);
29374060Sbp		if (error)
29474060Sbp			return error;
29574060Sbp		mbp->mb_top->m_pkthdr.len = len;
29651852Sbp	}
29774060Sbp	rq->conn_low = conn->connid & 0xff;
29874060Sbp	/* rq->task = p->p_pgrp->pg_id & 0xff; */ /*p->p_pid*/
29974060Sbp	/* XXX: this is temporary fix till I find a better solution */
30074060Sbp	rq->task = rq->conn_low;
30174060Sbp	rq->conn_high = conn->connid >> 8;
30274060Sbp	rqp->rexmit = conn->li.retry_count;
30374060Sbp	error = 0;
30474060Sbp	for(dosend = 1;;) {
30574060Sbp		if (rqp->rexmit-- == 0) {
30674060Sbp			error = ETIMEDOUT;
30774060Sbp			break;
30874060Sbp		}
30974060Sbp		error = 0;
31074060Sbp		if (dosend) {
31174060Sbp			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,
31274060Sbp				mbp->mb_top->m_pkthdr.len, rq->seq, rq->task
31374060Sbp			);
31474060Sbp			error = ncp_sock_send(so, mbp->mb_top, rqp);
31574060Sbp			if (error)
31674060Sbp				break;
31774060Sbp		}
31874060Sbp		tv.tv_sec = conn->li.timeout;
31974060Sbp		tv.tv_usec = 0;
320174647Sjeff		error = selsocket(so, POLLIN, &tv, td);
32174060Sbp		if (error == EWOULDBLOCK )	/* timeout expired */
32274060Sbp			continue;
323111577Sfjoe		error = ncp_chkintr(conn, td);
32474060Sbp		if (error)
32574060Sbp			break;
32674060Sbp		/*
32774060Sbp		 * At this point it is possible to get more than one
32874060Sbp		 * reply from server. In general, last reply should be for
32974060Sbp		 * current request, but not always. So, we loop through
33074060Sbp		 * all replies to find the right answer and flush others.
33174060Sbp		 */
33274060Sbp		gotpacket = 0;	/* nothing good found */
33374060Sbp		dosend = 1;	/* resend rq if error */
33474060Sbp		for (;;) {
33574060Sbp			error = 0;
336174647Sjeff			tv.tv_sec = 0;
337174647Sjeff			tv.tv_usec = 0;
338174647Sjeff			if (selsocket(so, POLLIN, &tv, td) != 0)
33974060Sbp				break;
34074060Sbp/*			if (so->so_rcv.sb_cc == 0) {
34174060Sbp				break;
34274060Sbp			}*/
34374060Sbp			error = ncp_sock_recv(so, &m, &len);
34474060Sbp			if (error)
34574060Sbp				break; 		/* must be more checks !!! */
34674060Sbp			if (m->m_len < sizeof(*rp)) {
34774060Sbp				m = m_pullup(m, sizeof(*rp));
34874060Sbp				if (m == NULL) {
34987599Sobrien					printf("%s: reply too short\n",__func__);
35074060Sbp					continue;
35174060Sbp				}
35274060Sbp			}
35374060Sbp			rp = mtod(m, struct ncp_rphdr*);
35474060Sbp			if (len == sizeof(*rp) && rp->type == NCP_POSITIVE_ACK) {
35574060Sbp				NCPSDEBUG("got positive acknowledge\n");
35674060Sbp				m_freem(m);
35774060Sbp				rqp->rexmit = conn->li.retry_count;
35874060Sbp				dosend = 0;	/* server just busy and will reply ASAP */
35974060Sbp				continue;
36074060Sbp			}
36174060Sbp			NCPSDEBUG("recv:%04x c=%d l=%d s=%d t=%d cc=%02x cs=%02x\n",rp->type,
36274060Sbp			    (rp->conn_high << 8) + rp->conn_low, len, rp->seq, rp->task,
36374060Sbp			     rp->completion_code, rp->connection_state);
36474060Sbp			NCPDDEBUG(m);
36574060Sbp			if ( (rp->type == NCP_REPLY) &&
36674060Sbp			    ((rq->type == NCP_ALLOC_SLOT) ||
36774060Sbp			    ((rp->conn_low == rq->conn_low) &&
36874060Sbp			     (rp->conn_high == rq->conn_high)
36974060Sbp			    ))) {
37074060Sbp				if (rq->seq > rp->seq || (rq->seq == 0 && rp->seq == 0xff)) {
37174060Sbp					dosend = 1;
37274060Sbp				}
37374060Sbp				if (rp->seq == rq->seq) {
37474060Sbp					if (gotpacket) {
37574060Sbp						m_freem(m);
37674060Sbp					} else {
37774060Sbp						gotpacket = 1;
37874060Sbp						mreply = m;
37974060Sbp						plen = len;
38074060Sbp					}
38174060Sbp					continue;	/* look up other for other packets */
38274060Sbp				}
38374060Sbp			}
38474060Sbp			m_freem(m);
38574060Sbp			NCPSDEBUG("reply mismatch\n");
38674060Sbp		} /* for receive */
38774060Sbp		if (error || gotpacket)
38874060Sbp			break;
38974060Sbp		/* try to resend, or just wait */
39051852Sbp	}
39174060Sbp	conn->seq++;
39274060Sbp	if (error) {
39374060Sbp		NCPSDEBUG("error=%d\n", error);
39474060Sbp		/*
39574060Sbp		 * Any error except interruped call means that we have
39674060Sbp		 * to reconnect. So, eliminate future timeouts by invalidating
39774060Sbp		 * connection now.
39874060Sbp		 */
39974060Sbp		if (error != EINTR)
40074646Sbp			ncp_conn_invalidate(conn);
40174060Sbp		return (error);
40251852Sbp	}
40374060Sbp	if (conn->flags & NCPFL_SIGNACTIVE) {
40474060Sbp		/* XXX: check reply signature */
40574060Sbp		m_adj(mreply, -8);
40674060Sbp		plen -= 8;
40751852Sbp	}
40874060Sbp	rp = mtod(mreply, struct ncp_rphdr*);
40974060Sbp	md_initm(&rqp->rp, mreply);
41074060Sbp	rqp->nr_rpsize = plen - sizeof(*rp);
41174060Sbp	rqp->nr_cc = error = rp->completion_code;
41274060Sbp	if (error)
41374060Sbp		error |= 0x8900;	/* server error */
41474060Sbp	rqp->nr_cs = rp->connection_state;
41574060Sbp	if (rqp->nr_cs & (NCP_CS_BAD_CONN | NCP_CS_SERVER_DOWN)) {
41674060Sbp		NCPSDEBUG("server drop us\n");
41774646Sbp		ncp_conn_invalidate(conn);
41874060Sbp		error = ECONNRESET;
41951852Sbp	}
42074060Sbp	md_get_mem(&rqp->rp, NULL, sizeof(*rp), MB_MSYSTEM);
42174060Sbp	return error;
42251852Sbp}
42351852Sbp
42474060Sbp/*
42574060Sbp * Here we will try to restore any loggedin & dropped connection,
42674060Sbp * connection should be locked on entry
42774060Sbp */
42874060Sbpstatic __inline int
42974060Sbpncp_restore_login(struct ncp_conn *conn)
43074060Sbp{
43151852Sbp	int error;
43251852Sbp
43374060Sbp	printf("ncprq: Restoring connection, flags = %x\n", conn->flags);
43474060Sbp	conn->flags |= NCPFL_RESTORING;
43574060Sbp	error = ncp_conn_reconnect(conn);
43674060Sbp	if (!error && (conn->flags & NCPFL_WASLOGGED))
437111577Sfjoe		error = ncp_conn_login(conn, conn->td, conn->ucred);
43874060Sbp	if (error)
43974646Sbp		ncp_ncp_disconnect(conn);
44074060Sbp	conn->flags &= ~NCPFL_RESTORING;
44174060Sbp	return error;
44251852Sbp}
44351852Sbp
44451852Sbpint
44574060Sbpncp_request(struct ncp_rq *rqp)
44651852Sbp{
44774060Sbp	struct ncp_conn *ncp = rqp->nr_conn;
44874060Sbp	int error, rcnt;
44951852Sbp
450111577Sfjoe	error = ncp_conn_lock(ncp, rqp->nr_td, rqp->nr_cred, NCPM_EXECUTE);
45174060Sbp	if (error)
45274060Sbp		goto out;
45374060Sbp	rcnt = NCP_RESTORE_COUNT;
45474060Sbp	for(;;) {
45574646Sbp		if (ncp->flags & NCPFL_ATTACHED) {
45674060Sbp			error = ncp_request_int(rqp);
45774646Sbp			if (ncp->flags & NCPFL_ATTACHED)
45874060Sbp				break;
45951852Sbp		}
46074060Sbp		if (rcnt-- == 0) {
46174060Sbp			error = ECONNRESET;
46274060Sbp			break;
46351852Sbp		}
46474060Sbp		/*
46574060Sbp		 * Do not attempt to restore connection recursively
46674060Sbp		 */
46774060Sbp		if (ncp->flags & NCPFL_RESTORING) {
46874060Sbp			error = ENOTCONN;
46974060Sbp			break;
47051852Sbp		}
47174060Sbp		error = ncp_restore_login(ncp);
47274060Sbp		if (error)
47374060Sbp			continue;
47451852Sbp	}
475111577Sfjoe	ncp_conn_unlock(ncp, rqp->nr_td);
47674060Sbpout:
47774060Sbp	if (error && (rqp->nr_flags & NCPR_DONTFREEONERR) == 0)
47874060Sbp		ncp_rq_done(rqp);
47974060Sbp	return error;
48051852Sbp}
481