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