1/*- 2 * Copyright (c) 1999-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 * 14 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 15 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 16 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 17 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 18 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 19 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 20 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 21 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 22 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 23 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 24 * SUCH DAMAGE. 25 * 26 * Routines to prepare request and fetch reply 27 */ 28 29#include <sys/cdefs.h> 30__FBSDID("$FreeBSD$"); 31 32#include <sys/param.h> 33#include <sys/systm.h> 34#include <sys/errno.h> 35#include <sys/kernel.h> 36#include <sys/malloc.h> 37#include <sys/mbuf.h> 38#include <sys/poll.h> 39#include <sys/proc.h> 40#include <sys/socket.h> 41#include <sys/socketvar.h> 42#include <sys/uio.h> 43 44#include <netncp/ncp.h> 45#include <netncp/ncp_conn.h> 46#include <netncp/ncp_rq.h> 47#include <netncp/ncp_subr.h> 48#include <netncp/ncp_ncp.h> 49#include <netncp/ncp_sock.h> 50#include <netncp/ncp_nls.h> 51 52static MALLOC_DEFINE(M_NCPRQ, "NCPRQ", "NCP request"); 53 54static int ncp_sign_packet(struct ncp_conn *conn, struct ncp_rq *rqp, int *size); 55 56int 57ncp_rq_alloc_any(u_int32_t ptype, u_int8_t fn, struct ncp_conn *ncp, 58 struct thread *td, struct ucred *cred, 59 struct ncp_rq **rqpp) 60{ 61 struct ncp_rq *rqp; 62 int error; 63 64 rqp = malloc(sizeof(*rqp), M_NCPRQ, M_WAITOK); 65 error = ncp_rq_init_any(rqp, ptype, fn, ncp, td, cred); 66 rqp->nr_flags |= NCPR_ALLOCED; 67 if (error) { 68 ncp_rq_done(rqp); 69 return error; 70 } 71 *rqpp = rqp; 72 return 0; 73} 74 75int 76ncp_rq_alloc(u_int8_t fn, struct ncp_conn *ncp, 77 struct thread *td, struct ucred *cred, struct ncp_rq **rqpp) 78{ 79 return ncp_rq_alloc_any(NCP_REQUEST, fn, ncp, td, cred, rqpp); 80} 81 82int 83ncp_rq_alloc_subfn(u_int8_t fn, u_int8_t subfn, struct ncp_conn *ncp, 84 struct thread *td, struct ucred *cred, struct ncp_rq **rqpp) 85{ 86 struct ncp_rq *rqp; 87 int error; 88 89 error = ncp_rq_alloc_any(NCP_REQUEST, fn, ncp, td, cred, &rqp); 90 if (error) 91 return error; 92 mb_reserve(&rqp->rq, 2); 93 mb_put_uint8(&rqp->rq, subfn); 94 *rqpp = rqp; 95 return 0; 96} 97 98int 99ncp_rq_init_any(struct ncp_rq *rqp, u_int32_t ptype, u_int8_t fn, 100 struct ncp_conn *ncp, 101 struct thread *td, struct ucred *cred) 102{ 103 struct ncp_rqhdr *rq; 104 struct ncp_bursthdr *brq; 105 struct mbchain *mbp; 106 int error; 107 108 bzero(rqp, sizeof(*rqp)); 109 error = ncp_conn_access(ncp, cred, NCPM_EXECUTE); 110 if (error) 111 return error; 112 rqp->nr_td = td; 113 rqp->nr_cred = cred; 114 rqp->nr_conn = ncp; 115 mbp = &rqp->rq; 116 if (mb_init(mbp) != 0) 117 return ENOBUFS; 118 switch(ptype) { 119 case NCP_PACKET_BURST: 120 brq = (struct ncp_bursthdr*)mb_reserve(mbp, sizeof(*brq)); 121 brq->bh_type = ptype; 122 brq->bh_streamtype = 0x2; 123 break; 124 default: 125 rq = (struct ncp_rqhdr*)mb_reserve(mbp, sizeof(*rq)); 126 rq->type = ptype; 127 rq->seq = 0; /* filled later */ 128 rq->fn = fn; 129 break; 130 } 131 rqp->nr_minrplen = -1; 132 return 0; 133} 134 135void 136ncp_rq_done(struct ncp_rq *rqp) 137{ 138 mb_done(&rqp->rq); 139 md_done(&rqp->rp); 140 if (rqp->nr_flags & NCPR_ALLOCED) 141 free(rqp, M_NCPRQ); 142 return; 143} 144 145/* 146 * Routines to fill the request 147 */ 148 149static int 150ncp_rq_pathstrhelp(struct mbchain *mbp, c_caddr_t src, caddr_t dst, 151 size_t *srclen, size_t *dstlen) 152{ 153 int len; 154 155 if (*srclen < *dstlen) { 156 *dstlen = *srclen; 157 len = (int)*srclen; 158 } else { 159 *srclen = *dstlen; 160 len = (int)*dstlen; 161 } 162 ncp_pathcopy(src, dst, len, mbp->mb_udata); 163 return 0; 164} 165 166int 167ncp_rq_pathstring(struct ncp_rq *rqp, int size, const char *name, 168 struct ncp_nlstables *nt) 169{ 170 struct mbchain *mbp = &rqp->rq; 171 172 mb_put_uint8(mbp, size); 173 mbp->mb_copy = ncp_rq_pathstrhelp; 174 mbp->mb_udata = nt; 175 return mb_put_mem(mbp, (c_caddr_t)name, size, MB_MCUSTOM); 176} 177 178int 179ncp_rq_pstring(struct ncp_rq *rqp, const char *s) 180{ 181 u_int len = strlen(s); 182 int error; 183 184 if (len > 255) 185 return EINVAL; 186 error = mb_put_uint8(&rqp->rq, len); 187 if (error) 188 return error; 189 return mb_put_mem(&rqp->rq, s, len, MB_MSYSTEM); 190} 191 192int 193ncp_rq_dbase_path(struct ncp_rq *rqp, u_int8_t vol_num, u_int32_t dir_base, 194 int namelen, u_char *path, struct ncp_nlstables *nt) 195{ 196 struct mbchain *mbp = &rqp->rq; 197 int complen; 198 199 mb_put_uint8(mbp, vol_num); 200 mb_put_mem(mbp, (c_caddr_t)&dir_base, sizeof(dir_base), MB_MSYSTEM); 201 mb_put_uint8(mbp, 1); /* with dirbase */ 202 if (path != NULL && path[0]) { 203 if (namelen < 0) { 204 namelen = *path++; 205 mb_put_uint8(mbp, namelen); 206 for(; namelen; namelen--) { 207 complen = *path++; 208 mb_put_uint8(mbp, complen); 209 mb_put_mem(mbp, path, complen, MB_MSYSTEM); 210 path += complen; 211 } 212 } else { 213 mb_put_uint8(mbp, 1); /* 1 component */ 214 ncp_rq_pathstring(rqp, namelen, path, nt); 215 } 216 } else { 217 mb_put_uint8(mbp, 0); 218 mb_put_uint8(mbp, 0); 219 } 220 return 0; 221} 222 223/* 224 * Make a signature for the current packet and add it at the end of the 225 * packet. 226 */ 227static int 228ncp_sign_packet(struct ncp_conn *conn, struct ncp_rq *rqp, int *size) 229{ 230 u_char data[64]; 231 int error; 232 233 bzero(data, sizeof(data)); 234 bcopy(conn->sign_root, data, 8); 235 setdle(data, 8, *size); 236 m_copydata(rqp->rq.mb_top, sizeof(struct ncp_rqhdr) - 1, 237 min((*size) - sizeof(struct ncp_rqhdr)+1, 52), data + 12); 238 ncp_sign(conn->sign_state, data, conn->sign_state); 239 error = mb_put_mem(&rqp->rq, (caddr_t)conn->sign_state, 8, MB_MSYSTEM); 240 if (error) 241 return error; 242 (*size) += 8; 243 return 0; 244} 245 246/* 247 * Low level send rpc, here we do not attempt to restore any connection, 248 * Connection expected to be locked 249 */ 250int 251ncp_request_int(struct ncp_rq *rqp) 252{ 253 struct ncp_conn *conn = rqp->nr_conn; 254 struct thread *td = conn->td; 255 struct socket *so = conn->ncp_so; 256 struct ncp_rqhdr *rq; 257 struct ncp_rphdr *rp=NULL; 258 struct timeval tv; 259 struct mbuf *m, *mreply = NULL; 260 struct mbchain *mbp; 261 int error, len, dosend, plen = 0, gotpacket; 262 263 if (so == NULL) { 264 printf("%s: ncp_so is NULL !\n",__func__); 265 ncp_conn_invalidate(conn); 266 return ENOTCONN; 267 } 268 if (td == NULL) 269 td = curthread; /* XXX maybe procpage ? */ 270 /* 271 * Flush out replies on previous reqs 272 */ 273 tv.tv_sec = 0; 274 tv.tv_usec = 0; 275 while (selsocket(so, POLLIN, &tv, td) == 0) { 276 if (ncp_sock_recv(so, &m, &len) != 0) 277 break; 278 m_freem(m); 279 } 280 mbp = &rqp->rq; 281 len = mb_fixhdr(mbp); 282 rq = mtod(mbp->mb_top, struct ncp_rqhdr *); 283 rq->seq = conn->seq; 284 m = rqp->rq.mb_top; 285 286 switch (rq->fn) { 287 case 0x15: case 0x16: case 0x17: case 0x23: 288 *(u_int16_t*)(rq + 1) = htons(len - 2 - sizeof(*rq)); 289 break; 290 } 291 if (conn->flags & NCPFL_SIGNACTIVE) { 292 error = ncp_sign_packet(conn, rqp, &len); 293 if (error) 294 return error; 295 mbp->mb_top->m_pkthdr.len = len; 296 } 297 rq->conn_low = conn->connid & 0xff; 298 /* rq->task = p->p_pgrp->pg_id & 0xff; */ /*p->p_pid*/ 299 /* XXX: this is temporary fix till I find a better solution */ 300 rq->task = rq->conn_low; 301 rq->conn_high = conn->connid >> 8; 302 rqp->rexmit = conn->li.retry_count; 303 error = 0; 304 for(dosend = 1;;) { 305 if (rqp->rexmit-- == 0) { 306 error = ETIMEDOUT; 307 break; 308 } 309 error = 0; 310 if (dosend) { 311 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, 312 mbp->mb_top->m_pkthdr.len, rq->seq, rq->task 313 ); 314 error = ncp_sock_send(so, mbp->mb_top, rqp); 315 if (error) 316 break; 317 } 318 tv.tv_sec = conn->li.timeout; 319 tv.tv_usec = 0; 320 error = selsocket(so, POLLIN, &tv, td); 321 if (error == EWOULDBLOCK ) /* timeout expired */ 322 continue; 323 error = ncp_chkintr(conn, td); 324 if (error) 325 break; 326 /* 327 * At this point it is possible to get more than one 328 * reply from server. In general, last reply should be for 329 * current request, but not always. So, we loop through 330 * all replies to find the right answer and flush others. 331 */ 332 gotpacket = 0; /* nothing good found */ 333 dosend = 1; /* resend rq if error */ 334 for (;;) { 335 error = 0; 336 tv.tv_sec = 0; 337 tv.tv_usec = 0; 338 if (selsocket(so, POLLIN, &tv, td) != 0) 339 break; 340/* if (so->so_rcv.sb_cc == 0) { 341 break; 342 }*/ 343 error = ncp_sock_recv(so, &m, &len); 344 if (error) 345 break; /* must be more checks !!! */ 346 if (m->m_len < sizeof(*rp)) { 347 m = m_pullup(m, sizeof(*rp)); 348 if (m == NULL) { 349 printf("%s: reply too short\n",__func__); 350 continue; 351 } 352 } 353 rp = mtod(m, struct ncp_rphdr*); 354 if (len == sizeof(*rp) && rp->type == NCP_POSITIVE_ACK) { 355 NCPSDEBUG("got positive acknowledge\n"); 356 m_freem(m); 357 rqp->rexmit = conn->li.retry_count; 358 dosend = 0; /* server just busy and will reply ASAP */ 359 continue; 360 } 361 NCPSDEBUG("recv:%04x c=%d l=%d s=%d t=%d cc=%02x cs=%02x\n",rp->type, 362 (rp->conn_high << 8) + rp->conn_low, len, rp->seq, rp->task, 363 rp->completion_code, rp->connection_state); 364 NCPDDEBUG(m); 365 if ( (rp->type == NCP_REPLY) && 366 ((rq->type == NCP_ALLOC_SLOT) || 367 ((rp->conn_low == rq->conn_low) && 368 (rp->conn_high == rq->conn_high) 369 ))) { 370 if (rq->seq > rp->seq || (rq->seq == 0 && rp->seq == 0xff)) { 371 dosend = 1; 372 } 373 if (rp->seq == rq->seq) { 374 if (gotpacket) { 375 m_freem(m); 376 } else { 377 gotpacket = 1; 378 mreply = m; 379 plen = len; 380 } 381 continue; /* look up other for other packets */ 382 } 383 } 384 m_freem(m); 385 NCPSDEBUG("reply mismatch\n"); 386 } /* for receive */ 387 if (error || gotpacket) 388 break; 389 /* try to resend, or just wait */ 390 } 391 conn->seq++; 392 if (error) { 393 NCPSDEBUG("error=%d\n", error); 394 /* 395 * Any error except interruped call means that we have 396 * to reconnect. So, eliminate future timeouts by invalidating 397 * connection now. 398 */ 399 if (error != EINTR) 400 ncp_conn_invalidate(conn); 401 return (error); 402 } 403 if (conn->flags & NCPFL_SIGNACTIVE) { 404 /* XXX: check reply signature */ 405 m_adj(mreply, -8); 406 plen -= 8; 407 } 408 rp = mtod(mreply, struct ncp_rphdr*); 409 md_initm(&rqp->rp, mreply); 410 rqp->nr_rpsize = plen - sizeof(*rp); 411 rqp->nr_cc = error = rp->completion_code; 412 if (error) 413 error |= 0x8900; /* server error */ 414 rqp->nr_cs = rp->connection_state; 415 if (rqp->nr_cs & (NCP_CS_BAD_CONN | NCP_CS_SERVER_DOWN)) { 416 NCPSDEBUG("server drop us\n"); 417 ncp_conn_invalidate(conn); 418 error = ECONNRESET; 419 } 420 md_get_mem(&rqp->rp, NULL, sizeof(*rp), MB_MSYSTEM); 421 return error; 422} 423 424/* 425 * Here we will try to restore any loggedin & dropped connection, 426 * connection should be locked on entry 427 */ 428static __inline int 429ncp_restore_login(struct ncp_conn *conn) 430{ 431 int error; 432 433 printf("ncprq: Restoring connection, flags = %x\n", conn->flags); 434 conn->flags |= NCPFL_RESTORING; 435 error = ncp_conn_reconnect(conn); 436 if (!error && (conn->flags & NCPFL_WASLOGGED)) 437 error = ncp_conn_login(conn, conn->td, conn->ucred); 438 if (error) 439 ncp_ncp_disconnect(conn); 440 conn->flags &= ~NCPFL_RESTORING; 441 return error; 442} 443 444int 445ncp_request(struct ncp_rq *rqp) 446{ 447 struct ncp_conn *ncp = rqp->nr_conn; 448 int error, rcnt; 449 450 error = ncp_conn_lock(ncp, rqp->nr_td, rqp->nr_cred, NCPM_EXECUTE); 451 if (error) 452 goto out; 453 rcnt = NCP_RESTORE_COUNT; 454 for(;;) { 455 if (ncp->flags & NCPFL_ATTACHED) { 456 error = ncp_request_int(rqp); 457 if (ncp->flags & NCPFL_ATTACHED) 458 break; 459 } 460 if (rcnt-- == 0) { 461 error = ECONNRESET; 462 break; 463 } 464 /* 465 * Do not attempt to restore connection recursively 466 */ 467 if (ncp->flags & NCPFL_RESTORING) { 468 error = ENOTCONN; 469 break; 470 } 471 error = ncp_restore_login(ncp); 472 if (error) 473 continue; 474 } 475 ncp_conn_unlock(ncp, rqp->nr_td); 476out: 477 if (error && (rqp->nr_flags & NCPR_DONTFREEONERR) == 0) 478 ncp_rq_done(rqp); 479 return error; 480} 481