2 * Copyright (c) 1999-2001 Boris Popov
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
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.
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
32 * Routines to prepare request and fetch reply
35 #include <sys/cdefs.h>
36 __FBSDID("$FreeBSD$");
38 #include <sys/param.h>
39 #include <sys/systm.h>
40 #include <sys/errno.h>
41 #include <sys/kernel.h>
42 #include <sys/malloc.h>
46 #include <sys/socket.h>
47 #include <sys/socketvar.h>
50 #include <netncp/ncp.h>
51 #include <netncp/ncp_conn.h>
52 #include <netncp/ncp_rq.h>
53 #include <netncp/ncp_subr.h>
54 #include <netncp/ncp_ncp.h>
55 #include <netncp/ncp_sock.h>
56 #include <netncp/ncp_nls.h>
58 static MALLOC_DEFINE(M_NCPRQ, "NCPRQ", "NCP request");
60 static int ncp_sign_packet(struct ncp_conn *conn, struct ncp_rq *rqp, int *size);
63 ncp_rq_alloc_any(u_int32_t ptype, u_int8_t fn, struct ncp_conn *ncp,
64 struct thread *td, struct ucred *cred,
70 rqp = malloc(sizeof(*rqp), M_NCPRQ, M_WAITOK);
71 error = ncp_rq_init_any(rqp, ptype, fn, ncp, td, cred);
72 rqp->nr_flags |= NCPR_ALLOCED;
82 ncp_rq_alloc(u_int8_t fn, struct ncp_conn *ncp,
83 struct thread *td, struct ucred *cred, struct ncp_rq **rqpp)
85 return ncp_rq_alloc_any(NCP_REQUEST, fn, ncp, td, cred, rqpp);
89 ncp_rq_alloc_subfn(u_int8_t fn, u_int8_t subfn, struct ncp_conn *ncp,
90 struct thread *td, struct ucred *cred, struct ncp_rq **rqpp)
95 error = ncp_rq_alloc_any(NCP_REQUEST, fn, ncp, td, cred, &rqp);
98 mb_reserve(&rqp->rq, 2);
99 mb_put_uint8(&rqp->rq, subfn);
105 ncp_rq_init_any(struct ncp_rq *rqp, u_int32_t ptype, u_int8_t fn,
106 struct ncp_conn *ncp,
107 struct thread *td, struct ucred *cred)
109 struct ncp_rqhdr *rq;
110 struct ncp_bursthdr *brq;
114 bzero(rqp, sizeof(*rqp));
115 error = ncp_conn_access(ncp, cred, NCPM_EXECUTE);
122 if (mb_init(mbp) != 0)
125 case NCP_PACKET_BURST:
126 brq = (struct ncp_bursthdr*)mb_reserve(mbp, sizeof(*brq));
127 brq->bh_type = ptype;
128 brq->bh_streamtype = 0x2;
131 rq = (struct ncp_rqhdr*)mb_reserve(mbp, sizeof(*rq));
133 rq->seq = 0; /* filled later */
137 rqp->nr_minrplen = -1;
142 ncp_rq_done(struct ncp_rq *rqp)
146 if (rqp->nr_flags & NCPR_ALLOCED)
152 * Routines to fill the request
156 ncp_rq_pathstrhelp(struct mbchain *mbp, c_caddr_t src, caddr_t dst,
157 size_t *srclen, size_t *dstlen)
161 if (*srclen < *dstlen) {
168 ncp_pathcopy(src, dst, len, mbp->mb_udata);
173 ncp_rq_pathstring(struct ncp_rq *rqp, int size, const char *name,
174 struct ncp_nlstables *nt)
176 struct mbchain *mbp = &rqp->rq;
178 mb_put_uint8(mbp, size);
179 mbp->mb_copy = ncp_rq_pathstrhelp;
181 return mb_put_mem(mbp, (c_caddr_t)name, size, MB_MCUSTOM);
185 ncp_rq_pstring(struct ncp_rq *rqp, const char *s)
187 u_int len = strlen(s);
192 error = mb_put_uint8(&rqp->rq, len);
195 return mb_put_mem(&rqp->rq, s, len, MB_MSYSTEM);
199 ncp_rq_dbase_path(struct ncp_rq *rqp, u_int8_t vol_num, u_int32_t dir_base,
200 int namelen, u_char *path, struct ncp_nlstables *nt)
202 struct mbchain *mbp = &rqp->rq;
205 mb_put_uint8(mbp, vol_num);
206 mb_put_mem(mbp, (c_caddr_t)&dir_base, sizeof(dir_base), MB_MSYSTEM);
207 mb_put_uint8(mbp, 1); /* with dirbase */
208 if (path != NULL && path[0]) {
211 mb_put_uint8(mbp, namelen);
212 for(; namelen; namelen--) {
214 mb_put_uint8(mbp, complen);
215 mb_put_mem(mbp, path, complen, MB_MSYSTEM);
219 mb_put_uint8(mbp, 1); /* 1 component */
220 ncp_rq_pathstring(rqp, namelen, path, nt);
223 mb_put_uint8(mbp, 0);
224 mb_put_uint8(mbp, 0);
230 * Make a signature for the current packet and add it at the end of the
234 ncp_sign_packet(struct ncp_conn *conn, struct ncp_rq *rqp, int *size)
239 bzero(data, sizeof(data));
240 bcopy(conn->sign_root, data, 8);
241 setdle(data, 8, *size);
242 m_copydata(rqp->rq.mb_top, sizeof(struct ncp_rqhdr) - 1,
243 min((*size) - sizeof(struct ncp_rqhdr)+1, 52), data + 12);
244 ncp_sign(conn->sign_state, data, conn->sign_state);
245 error = mb_put_mem(&rqp->rq, (caddr_t)conn->sign_state, 8, MB_MSYSTEM);
253 * Low level send rpc, here we do not attempt to restore any connection,
254 * Connection expected to be locked
257 ncp_request_int(struct ncp_rq *rqp)
259 struct ncp_conn *conn = rqp->nr_conn;
260 struct thread *td = conn->td;
261 struct socket *so = conn->ncp_so;
262 struct ncp_rqhdr *rq;
263 struct ncp_rphdr *rp=NULL;
265 struct mbuf *m, *mreply = NULL;
267 int error, len, dosend, plen = 0, gotpacket;
270 printf("%s: ncp_so is NULL !\n",__func__);
271 ncp_conn_invalidate(conn);
275 td = curthread; /* XXX maybe procpage ? */
277 * Flush out replies on previous reqs
281 while (selsocket(so, POLLIN, &tv, td) == 0) {
282 if (ncp_sock_recv(so, &m, &len) != 0)
287 len = mb_fixhdr(mbp);
288 rq = mtod(mbp->mb_top, struct ncp_rqhdr *);
293 case 0x15: case 0x16: case 0x17: case 0x23:
294 *(u_int16_t*)(rq + 1) = htons(len - 2 - sizeof(*rq));
297 if (conn->flags & NCPFL_SIGNACTIVE) {
298 error = ncp_sign_packet(conn, rqp, &len);
301 mbp->mb_top->m_pkthdr.len = len;
303 rq->conn_low = conn->connid & 0xff;
304 /* rq->task = p->p_pgrp->pg_id & 0xff; */ /*p->p_pid*/
305 /* XXX: this is temporary fix till I find a better solution */
306 rq->task = rq->conn_low;
307 rq->conn_high = conn->connid >> 8;
308 rqp->rexmit = conn->li.retry_count;
311 if (rqp->rexmit-- == 0) {
317 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,
318 mbp->mb_top->m_pkthdr.len, rq->seq, rq->task
320 error = ncp_sock_send(so, mbp->mb_top, rqp);
324 tv.tv_sec = conn->li.timeout;
326 error = selsocket(so, POLLIN, &tv, td);
327 if (error == EWOULDBLOCK ) /* timeout expired */
329 error = ncp_chkintr(conn, td);
333 * At this point it is possible to get more than one
334 * reply from server. In general, last reply should be for
335 * current request, but not always. So, we loop through
336 * all replies to find the right answer and flush others.
338 gotpacket = 0; /* nothing good found */
339 dosend = 1; /* resend rq if error */
344 if (selsocket(so, POLLIN, &tv, td) != 0)
346 /* if (so->so_rcv.sb_cc == 0) {
349 error = ncp_sock_recv(so, &m, &len);
351 break; /* must be more checks !!! */
352 if (m->m_len < sizeof(*rp)) {
353 m = m_pullup(m, sizeof(*rp));
355 printf("%s: reply too short\n",__func__);
359 rp = mtod(m, struct ncp_rphdr*);
360 if (len == sizeof(*rp) && rp->type == NCP_POSITIVE_ACK) {
361 NCPSDEBUG("got positive acknowledge\n");
363 rqp->rexmit = conn->li.retry_count;
364 dosend = 0; /* server just busy and will reply ASAP */
367 NCPSDEBUG("recv:%04x c=%d l=%d s=%d t=%d cc=%02x cs=%02x\n",rp->type,
368 (rp->conn_high << 8) + rp->conn_low, len, rp->seq, rp->task,
369 rp->completion_code, rp->connection_state);
371 if ( (rp->type == NCP_REPLY) &&
372 ((rq->type == NCP_ALLOC_SLOT) ||
373 ((rp->conn_low == rq->conn_low) &&
374 (rp->conn_high == rq->conn_high)
376 if (rq->seq > rp->seq || (rq->seq == 0 && rp->seq == 0xff)) {
379 if (rp->seq == rq->seq) {
387 continue; /* look up other for other packets */
391 NCPSDEBUG("reply mismatch\n");
393 if (error || gotpacket)
395 /* try to resend, or just wait */
399 NCPSDEBUG("error=%d\n", error);
401 * Any error except interruped call means that we have
402 * to reconnect. So, eliminate future timeouts by invalidating
406 ncp_conn_invalidate(conn);
409 if (conn->flags & NCPFL_SIGNACTIVE) {
410 /* XXX: check reply signature */
414 rp = mtod(mreply, struct ncp_rphdr*);
415 md_initm(&rqp->rp, mreply);
416 rqp->nr_rpsize = plen - sizeof(*rp);
417 rqp->nr_cc = error = rp->completion_code;
419 error |= 0x8900; /* server error */
420 rqp->nr_cs = rp->connection_state;
421 if (rqp->nr_cs & (NCP_CS_BAD_CONN | NCP_CS_SERVER_DOWN)) {
422 NCPSDEBUG("server drop us\n");
423 ncp_conn_invalidate(conn);
426 md_get_mem(&rqp->rp, NULL, sizeof(*rp), MB_MSYSTEM);
431 * Here we will try to restore any loggedin & dropped connection,
432 * connection should be locked on entry
435 ncp_restore_login(struct ncp_conn *conn)
439 printf("ncprq: Restoring connection, flags = %x\n", conn->flags);
440 conn->flags |= NCPFL_RESTORING;
441 error = ncp_conn_reconnect(conn);
442 if (!error && (conn->flags & NCPFL_WASLOGGED))
443 error = ncp_conn_login(conn, conn->td, conn->ucred);
445 ncp_ncp_disconnect(conn);
446 conn->flags &= ~NCPFL_RESTORING;
451 ncp_request(struct ncp_rq *rqp)
453 struct ncp_conn *ncp = rqp->nr_conn;
456 error = ncp_conn_lock(ncp, rqp->nr_td, rqp->nr_cred, NCPM_EXECUTE);
459 rcnt = NCP_RESTORE_COUNT;
461 if (ncp->flags & NCPFL_ATTACHED) {
462 error = ncp_request_int(rqp);
463 if (ncp->flags & NCPFL_ATTACHED)
471 * Do not attempt to restore connection recursively
473 if (ncp->flags & NCPFL_RESTORING) {
477 error = ncp_restore_login(ncp);
481 ncp_conn_unlock(ncp, rqp->nr_td);
483 if (error && (rqp->nr_flags & NCPR_DONTFREEONERR) == 0)