ncp_rq.c revision 74646
151852Sbp/*
274060Sbp * Copyright (c) 1999, 2000, 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 * 3. All advertising materials mentioning features or use of this software
1451852Sbp *    must display the following acknowledgement:
1551852Sbp *    This product includes software developed by Boris Popov.
1651852Sbp * 4. Neither the name of the author nor the names of any co-contributors
1751852Sbp *    may be used to endorse or promote products derived from this software
1851852Sbp *    without specific prior written permission.
1951852Sbp *
2051852Sbp * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
2151852Sbp * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
2251852Sbp * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
2351852Sbp * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
2451852Sbp * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
2551852Sbp * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
2651852Sbp * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
2751852Sbp * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
2851852Sbp * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
2951852Sbp * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
3051852Sbp * SUCH DAMAGE.
3151852Sbp *
3251852Sbp * Routines to prepare request and fetch reply
3351852Sbp *
3451852Sbp * $FreeBSD: head/sys/netncp/ncp_rq.c 74646 2001-03-22 10:29:39Z bp $
3551852Sbp */
3651852Sbp#include <sys/param.h>
3751852Sbp#include <sys/systm.h>
3851852Sbp#include <sys/errno.h>
3974060Sbp#include <sys/kernel.h>
4074060Sbp#include <sys/malloc.h>
4151852Sbp#include <sys/mbuf.h>
4274060Sbp#include <sys/poll.h>
4351852Sbp#include <sys/uio.h>
4451852Sbp
4551852Sbp#include <netncp/ncp.h>
4651852Sbp#include <netncp/ncp_conn.h>
4751852Sbp#include <netncp/ncp_rq.h>
4851852Sbp#include <netncp/ncp_subr.h>
4951852Sbp#include <netncp/ncp_ncp.h>
5074060Sbp#include <netncp/ncp_sock.h>
5151852Sbp#include <netncp/ncp_nls.h>
5251852Sbp
5374060Sbpstatic MALLOC_DEFINE(M_NCPRQ, "NCPRQ", "NCP request");
5474060Sbp
5574060Sbpstatic int ncp_sign_packet(struct ncp_conn *conn, struct ncp_rq *rqp, int *size);
5674060Sbp
5751852Sbpint
5874060Sbpncp_rq_alloc_any(u_int32_t ptype, u_int8_t fn, struct ncp_conn *ncp,
5974060Sbp	struct proc *p, struct ucred *cred,
6074060Sbp	struct ncp_rq **rqpp)
6151852Sbp{
6274060Sbp	struct ncp_rq *rqp;
6374060Sbp	int error;
6474060Sbp
6574060Sbp	MALLOC(rqp, struct ncp_rq *, sizeof(*rqp), M_NCPRQ, M_WAITOK);
6674060Sbp	error = ncp_rq_init_any(rqp, ptype, fn, ncp, p, cred);
6774060Sbp	rqp->nr_flags |= NCPR_ALLOCED;
6874060Sbp	if (error) {
6974060Sbp		ncp_rq_done(rqp);
7074060Sbp		return error;
7174060Sbp	}
7274060Sbp	*rqpp = rqp;
7374060Sbp	return 0;
7474060Sbp}
7574060Sbp
7674060Sbpint
7774060Sbpncp_rq_alloc(u_int8_t fn, struct ncp_conn *ncp,
7874060Sbp	struct proc *p, struct ucred *cred, struct ncp_rq **rqpp)
7974060Sbp{
8074060Sbp	return ncp_rq_alloc_any(NCP_REQUEST, fn, ncp, p, cred, rqpp);
8174060Sbp}
8274060Sbp
8374060Sbpint
8474060Sbpncp_rq_alloc_subfn(u_int8_t fn, u_int8_t subfn, struct ncp_conn *ncp,
8574060Sbp	struct proc *p,	struct ucred *cred, struct ncp_rq **rqpp)
8674060Sbp{
8774060Sbp	struct ncp_rq *rqp;
8874060Sbp	int error;
8974060Sbp
9074060Sbp	error = ncp_rq_alloc_any(NCP_REQUEST, fn, ncp, p, cred, &rqp);
9174060Sbp	if (error)
9274060Sbp		return error;
9374060Sbp	mb_reserve(&rqp->rq, 2);
9474060Sbp	mb_put_uint8(&rqp->rq, subfn);
9574060Sbp	*rqpp = rqp;
9674060Sbp	return 0;
9774060Sbp}
9874060Sbp
9974060Sbpint
10074060Sbpncp_rq_init_any(struct ncp_rq *rqp, u_int32_t ptype, u_int8_t fn,
10174060Sbp	struct ncp_conn *ncp,
10274060Sbp	struct proc *p,	struct ucred *cred)
10374060Sbp{
10451852Sbp	struct ncp_rqhdr *rq;
10551852Sbp	struct ncp_bursthdr *brq;
10674060Sbp	struct mbchain *mbp;
10774060Sbp	int error;
10851852Sbp
10951852Sbp	bzero(rqp, sizeof(*rqp));
11074060Sbp	error = ncp_conn_access(ncp, cred, NCPM_EXECUTE);
11174060Sbp	if (error)
11274060Sbp		return error;
11374060Sbp	rqp->nr_p = p;
11474060Sbp	rqp->nr_cred = cred;
11574060Sbp	rqp->nr_conn = ncp;
11674060Sbp	mbp = &rqp->rq;
11774060Sbp	if (mb_init(mbp) != 0)
11874060Sbp		return ENOBUFS;
11951852Sbp	switch(ptype) {
12051852Sbp	    case NCP_PACKET_BURST:
12174060Sbp		brq = (struct ncp_bursthdr*)mb_reserve(mbp, sizeof(*brq));
12251852Sbp		brq->bh_type = ptype;
12351852Sbp		brq->bh_streamtype = 0x2;
12451852Sbp		break;
12551852Sbp	    default:
12674060Sbp		rq = (struct ncp_rqhdr*)mb_reserve(mbp, sizeof(*rq));
12751852Sbp		rq->type = ptype;
12851852Sbp		rq->seq = 0;	/* filled later */
12951852Sbp		rq->fn = fn;
13051852Sbp		break;
13151852Sbp	}
13274060Sbp	rqp->nr_minrplen = -1;
13351852Sbp	return 0;
13451852Sbp}
13551852Sbp
13674060Sbpvoid
13774060Sbpncp_rq_done(struct ncp_rq *rqp)
13874060Sbp{
13974060Sbp	mb_done(&rqp->rq);
14074060Sbp	md_done(&rqp->rp);
14174060Sbp	if (rqp->nr_flags & NCPR_ALLOCED)
14274060Sbp		free(rqp, M_NCPRQ);
14374060Sbp	return;
14451852Sbp}
14551852Sbp
14651852Sbp/*
14751852Sbp * Routines to fill the request
14851852Sbp */
14951852Sbp
15074060Sbpstatic int
15174060Sbpncp_rq_pathstrhelp(struct mbchain *mbp, c_caddr_t src, caddr_t dst, int len)
15274060Sbp{
15374060Sbp	ncp_pathcopy(src, dst, len, mbp->mb_udata);
15474060Sbp	return 0;
15551852Sbp}
15651852Sbp
15751852Sbpint
15874060Sbpncp_rq_pathstring(struct ncp_rq *rqp, int size, const char *name,
15974060Sbp	struct ncp_nlstables *nt)
16074060Sbp{
16174060Sbp	struct mbchain *mbp = &rqp->rq;
16251852Sbp
16374060Sbp	mb_put_uint8(mbp, size);
16474060Sbp	mbp->mb_copy = ncp_rq_pathstrhelp;
16574060Sbp	mbp->mb_udata = nt;
16674060Sbp	return mb_put_mem(mbp, (c_caddr_t)name, size, MB_MCUSTOM);
16751852Sbp}
16851852Sbp
16974060Sbpint
17074060Sbpncp_rq_pstring(struct ncp_rq *rqp, const char *s)
17174060Sbp{
17274060Sbp	u_int len = strlen(s);
17374060Sbp	int error;
17451852Sbp
17574060Sbp	if (len > 255)
17674060Sbp		return EINVAL;
17774060Sbp	error = mb_put_uint8(&rqp->rq, len);
17874060Sbp	if (error)
17974060Sbp		return error;
18074060Sbp	return mb_put_mem(&rqp->rq, s, len, MB_MSYSTEM);
18151852Sbp}
18251852Sbp
18374060Sbpint
18451852Sbpncp_rq_dbase_path(struct ncp_rq *rqp, u_int8_t vol_num, u_int32_t dir_base,
18551852Sbp                    int namelen, u_char *path, struct ncp_nlstables *nt)
18651852Sbp{
18774060Sbp	struct mbchain *mbp = &rqp->rq;
18851852Sbp	int complen;
18951852Sbp
19074060Sbp	mb_put_uint8(mbp, vol_num);
19174060Sbp	mb_put_mem(mbp, (c_caddr_t)&dir_base, sizeof(dir_base), MB_MSYSTEM);
19274060Sbp	mb_put_uint8(mbp, 1);	/* with dirbase */
19351852Sbp	if (path != NULL && path[0]) {
19451852Sbp		if (namelen < 0) {
19551852Sbp			namelen = *path++;
19674060Sbp			mb_put_uint8(mbp, namelen);
19751852Sbp			for(; namelen; namelen--) {
19851852Sbp				complen = *path++;
19974060Sbp				mb_put_uint8(mbp, complen);
20074060Sbp				mb_put_mem(mbp, path, complen, MB_MSYSTEM);
20151852Sbp				path += complen;
20251852Sbp			}
20351852Sbp		} else {
20474060Sbp			mb_put_uint8(mbp, 1);	/* 1 component */
20551852Sbp			ncp_rq_pathstring(rqp, namelen, path, nt);
20651852Sbp		}
20751852Sbp	} else {
20874060Sbp		mb_put_uint8(mbp, 0);
20974060Sbp		mb_put_uint8(mbp, 0);
21051852Sbp	}
21174060Sbp	return 0;
21251852Sbp}
21374060Sbp
21474060Sbp/*
21574060Sbp * Make a signature for the current packet and add it at the end of the
21674060Sbp * packet.
21751852Sbp */
21874060Sbpstatic int
21974060Sbpncp_sign_packet(struct ncp_conn *conn, struct ncp_rq *rqp, int *size)
22074060Sbp{
22174060Sbp	u_char data[64];
22274060Sbp	int error;
22351852Sbp
22474060Sbp	bzero(data, sizeof(data));
22574060Sbp	bcopy(conn->sign_root, data, 8);
22674060Sbp	setdle(data, 8, *size);
22774060Sbp	m_copydata(rqp->rq.mb_top, sizeof(struct ncp_rqhdr) - 1,
22874060Sbp		min((*size) - sizeof(struct ncp_rqhdr)+1, 52), data + 12);
22974060Sbp	ncp_sign(conn->sign_state, data, conn->sign_state);
23074060Sbp	error = mb_put_mem(&rqp->rq, (caddr_t)conn->sign_state, 8, MB_MSYSTEM);
23174060Sbp	if (error)
23274060Sbp		return error;
23374060Sbp	(*size) += 8;
23474060Sbp	return 0;
23551852Sbp}
23651852Sbp
23774060Sbp/*
23874060Sbp * Low level send rpc, here we do not attempt to restore any connection,
23974060Sbp * Connection expected to be locked
24074060Sbp */
24174060Sbpint
24274060Sbpncp_request_int(struct ncp_rq *rqp)
24374060Sbp{
24474060Sbp	struct ncp_conn *conn = rqp->nr_conn;
24574060Sbp	struct proc *p = conn->procp;
24674060Sbp	struct socket *so = conn->ncp_so;
24774060Sbp	struct ncp_rqhdr *rq;
24874060Sbp	struct ncp_rphdr *rp=NULL;
24974060Sbp	struct timeval tv;
25074060Sbp	struct mbuf *m, *mreply = NULL;
25174060Sbp	struct mbchain *mbp;
25274060Sbp	int error, len, dosend, plen = 0, gotpacket;
25351852Sbp
25474060Sbp	if (so == NULL) {
25574060Sbp		printf("%s: ncp_so is NULL !\n",__FUNCTION__);
25674646Sbp		ncp_conn_invalidate(conn);
25774060Sbp		return ENOTCONN;
25851852Sbp	}
25974060Sbp	if (p == NULL)
26074060Sbp		p = curproc;	/* XXX maybe procpage ? */
26174060Sbp	/*
26274060Sbp	 * Flush out replies on previous reqs
26374060Sbp	 */
26474060Sbp	while (ncp_poll(so, POLLIN) != 0) {
26574060Sbp		if (ncp_sock_recv(so, &m, &len) != 0)
26674060Sbp			break;
26774060Sbp		m_freem(m);
26874060Sbp	}
26974060Sbp	mbp = &rqp->rq;
27074060Sbp	len = mb_fixhdr(mbp);
27174060Sbp	rq = mtod(mbp->mb_top, struct ncp_rqhdr *);
27274060Sbp	rq->seq = conn->seq;
27374060Sbp	m = rqp->rq.mb_top;
27451852Sbp
27574060Sbp	switch (rq->fn) {
27674060Sbp	    case 0x15: case 0x16: case 0x17: case 0x23:
27774060Sbp		*(u_int16_t*)(rq + 1) = htons(len - 2 - sizeof(*rq));
27874060Sbp		break;
27951852Sbp	}
28074060Sbp	if (conn->flags & NCPFL_SIGNACTIVE) {
28174060Sbp		error = ncp_sign_packet(conn, rqp, &len);
28274060Sbp		if (error)
28374060Sbp			return error;
28474060Sbp		mbp->mb_top->m_pkthdr.len = len;
28551852Sbp	}
28674060Sbp	rq->conn_low = conn->connid & 0xff;
28774060Sbp	/* rq->task = p->p_pgrp->pg_id & 0xff; */ /*p->p_pid*/
28874060Sbp	/* XXX: this is temporary fix till I find a better solution */
28974060Sbp	rq->task = rq->conn_low;
29074060Sbp	rq->conn_high = conn->connid >> 8;
29174060Sbp	rqp->rexmit = conn->li.retry_count;
29274060Sbp	error = 0;
29374060Sbp	for(dosend = 1;;) {
29474060Sbp		if (rqp->rexmit-- == 0) {
29574060Sbp			error = ETIMEDOUT;
29674060Sbp			break;
29774060Sbp		}
29874060Sbp		error = 0;
29974060Sbp		if (dosend) {
30074060Sbp			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,
30174060Sbp				mbp->mb_top->m_pkthdr.len, rq->seq, rq->task
30274060Sbp			);
30374060Sbp			error = ncp_sock_send(so, mbp->mb_top, rqp);
30474060Sbp			if (error)
30574060Sbp				break;
30674060Sbp		}
30774060Sbp		tv.tv_sec = conn->li.timeout;
30874060Sbp		tv.tv_usec = 0;
30974060Sbp		error = ncp_sock_rselect(so, p, &tv, POLLIN);
31074060Sbp		if (error == EWOULDBLOCK )	/* timeout expired */
31174060Sbp			continue;
31274060Sbp		error = ncp_chkintr(conn, p);
31374060Sbp		if (error)
31474060Sbp			break;
31574060Sbp		/*
31674060Sbp		 * At this point it is possible to get more than one
31774060Sbp		 * reply from server. In general, last reply should be for
31874060Sbp		 * current request, but not always. So, we loop through
31974060Sbp		 * all replies to find the right answer and flush others.
32074060Sbp		 */
32174060Sbp		gotpacket = 0;	/* nothing good found */
32274060Sbp		dosend = 1;	/* resend rq if error */
32374060Sbp		for (;;) {
32474060Sbp			error = 0;
32574060Sbp			if (ncp_poll(so, POLLIN) == 0)
32674060Sbp				break;
32774060Sbp/*			if (so->so_rcv.sb_cc == 0) {
32874060Sbp				break;
32974060Sbp			}*/
33074060Sbp			error = ncp_sock_recv(so, &m, &len);
33174060Sbp			if (error)
33274060Sbp				break; 		/* must be more checks !!! */
33374060Sbp			if (m->m_len < sizeof(*rp)) {
33474060Sbp				m = m_pullup(m, sizeof(*rp));
33574060Sbp				if (m == NULL) {
33674060Sbp					printf("%s: reply too short\n",__FUNCTION__);
33774060Sbp					continue;
33874060Sbp				}
33974060Sbp			}
34074060Sbp			rp = mtod(m, struct ncp_rphdr*);
34174060Sbp			if (len == sizeof(*rp) && rp->type == NCP_POSITIVE_ACK) {
34274060Sbp				NCPSDEBUG("got positive acknowledge\n");
34374060Sbp				m_freem(m);
34474060Sbp				rqp->rexmit = conn->li.retry_count;
34574060Sbp				dosend = 0;	/* server just busy and will reply ASAP */
34674060Sbp				continue;
34774060Sbp			}
34874060Sbp			NCPSDEBUG("recv:%04x c=%d l=%d s=%d t=%d cc=%02x cs=%02x\n",rp->type,
34974060Sbp			    (rp->conn_high << 8) + rp->conn_low, len, rp->seq, rp->task,
35074060Sbp			     rp->completion_code, rp->connection_state);
35174060Sbp			NCPDDEBUG(m);
35274060Sbp			if ( (rp->type == NCP_REPLY) &&
35374060Sbp			    ((rq->type == NCP_ALLOC_SLOT) ||
35474060Sbp			    ((rp->conn_low == rq->conn_low) &&
35574060Sbp			     (rp->conn_high == rq->conn_high)
35674060Sbp			    ))) {
35774060Sbp				if (rq->seq > rp->seq || (rq->seq == 0 && rp->seq == 0xff)) {
35874060Sbp					dosend = 1;
35974060Sbp				}
36074060Sbp				if (rp->seq == rq->seq) {
36174060Sbp					if (gotpacket) {
36274060Sbp						m_freem(m);
36374060Sbp					} else {
36474060Sbp						gotpacket = 1;
36574060Sbp						mreply = m;
36674060Sbp						plen = len;
36774060Sbp					}
36874060Sbp					continue;	/* look up other for other packets */
36974060Sbp				}
37074060Sbp			}
37174060Sbp			m_freem(m);
37274060Sbp			NCPSDEBUG("reply mismatch\n");
37374060Sbp		} /* for receive */
37474060Sbp		if (error || gotpacket)
37574060Sbp			break;
37674060Sbp		/* try to resend, or just wait */
37751852Sbp	}
37874060Sbp	conn->seq++;
37974060Sbp	if (error) {
38074060Sbp		NCPSDEBUG("error=%d\n", error);
38174060Sbp		/*
38274060Sbp		 * Any error except interruped call means that we have
38374060Sbp		 * to reconnect. So, eliminate future timeouts by invalidating
38474060Sbp		 * connection now.
38574060Sbp		 */
38674060Sbp		if (error != EINTR)
38774646Sbp			ncp_conn_invalidate(conn);
38874060Sbp		return (error);
38951852Sbp	}
39074060Sbp	if (conn->flags & NCPFL_SIGNACTIVE) {
39174060Sbp		/* XXX: check reply signature */
39274060Sbp		m_adj(mreply, -8);
39374060Sbp		plen -= 8;
39451852Sbp	}
39574060Sbp	rp = mtod(mreply, struct ncp_rphdr*);
39674060Sbp	md_initm(&rqp->rp, mreply);
39774060Sbp	rqp->nr_rpsize = plen - sizeof(*rp);
39874060Sbp	rqp->nr_cc = error = rp->completion_code;
39974060Sbp	if (error)
40074060Sbp		error |= 0x8900;	/* server error */
40174060Sbp	rqp->nr_cs = rp->connection_state;
40274060Sbp	if (rqp->nr_cs & (NCP_CS_BAD_CONN | NCP_CS_SERVER_DOWN)) {
40374060Sbp		NCPSDEBUG("server drop us\n");
40474646Sbp		ncp_conn_invalidate(conn);
40574060Sbp		error = ECONNRESET;
40651852Sbp	}
40774060Sbp	md_get_mem(&rqp->rp, NULL, sizeof(*rp), MB_MSYSTEM);
40874060Sbp	return error;
40951852Sbp}
41051852Sbp
41174060Sbp/*
41274060Sbp * Here we will try to restore any loggedin & dropped connection,
41374060Sbp * connection should be locked on entry
41474060Sbp */
41574060Sbpstatic __inline int
41674060Sbpncp_restore_login(struct ncp_conn *conn)
41774060Sbp{
41851852Sbp	int error;
41951852Sbp
42074060Sbp	printf("ncprq: Restoring connection, flags = %x\n", conn->flags);
42174060Sbp	conn->flags |= NCPFL_RESTORING;
42274060Sbp	error = ncp_conn_reconnect(conn);
42374060Sbp	if (!error && (conn->flags & NCPFL_WASLOGGED))
42474060Sbp		error = ncp_login_object(conn, conn->li.user, conn->li.objtype, conn->li.password,conn->procp,conn->ucred);
42574060Sbp	if (error)
42674646Sbp		ncp_ncp_disconnect(conn);
42774060Sbp	conn->flags &= ~NCPFL_RESTORING;
42874060Sbp	return error;
42951852Sbp}
43051852Sbp
43151852Sbpint
43274060Sbpncp_request(struct ncp_rq *rqp)
43351852Sbp{
43474060Sbp	struct ncp_conn *ncp = rqp->nr_conn;
43574060Sbp	int error, rcnt;
43651852Sbp
43774060Sbp	error = ncp_conn_lock(ncp, rqp->nr_p, rqp->nr_cred, NCPM_EXECUTE);
43874060Sbp	if (error)
43974060Sbp		goto out;
44074060Sbp	rcnt = NCP_RESTORE_COUNT;
44174060Sbp	for(;;) {
44274646Sbp		if (ncp->flags & NCPFL_ATTACHED) {
44374060Sbp			error = ncp_request_int(rqp);
44474646Sbp			if (ncp->flags & NCPFL_ATTACHED)
44574060Sbp				break;
44651852Sbp		}
44774060Sbp		if (rcnt-- == 0) {
44874060Sbp			error = ECONNRESET;
44974060Sbp			break;
45051852Sbp		}
45174060Sbp		/*
45274060Sbp		 * Do not attempt to restore connection recursively
45374060Sbp		 */
45474060Sbp		if (ncp->flags & NCPFL_RESTORING) {
45574060Sbp			error = ENOTCONN;
45674060Sbp			break;
45751852Sbp		}
45874060Sbp		error = ncp_restore_login(ncp);
45974060Sbp		if (error)
46074060Sbp			continue;
46151852Sbp	}
46274060Sbp	ncp_conn_unlock(ncp, rqp->nr_p);
46374060Sbpout:
46474060Sbp	if (error && (rqp->nr_flags & NCPR_DONTFREEONERR) == 0)
46574060Sbp		ncp_rq_done(rqp);
46674060Sbp	return error;
46751852Sbp}
468