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