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