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: stable/9/sys/netncp/ncp_rq.c 206361 2010-04-07 16:50:38Z joel $");
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 
52 static MALLOC_DEFINE(M_NCPRQ, "NCPRQ", "NCP request");
53 
54 static int ncp_sign_packet(struct ncp_conn *conn, struct ncp_rq *rqp, int *size);
55 
56 int
ncp_rq_alloc_any(u_int32_t ptype,u_int8_t fn,struct ncp_conn * ncp,struct thread * td,struct ucred * cred,struct ncp_rq ** rqpp)57 ncp_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 
75 int
ncp_rq_alloc(u_int8_t fn,struct ncp_conn * ncp,struct thread * td,struct ucred * cred,struct ncp_rq ** rqpp)76 ncp_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 
82 int
ncp_rq_alloc_subfn(u_int8_t fn,u_int8_t subfn,struct ncp_conn * ncp,struct thread * td,struct ucred * cred,struct ncp_rq ** rqpp)83 ncp_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 
98 int
ncp_rq_init_any(struct ncp_rq * rqp,u_int32_t ptype,u_int8_t fn,struct ncp_conn * ncp,struct thread * td,struct ucred * cred)99 ncp_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 
135 void
ncp_rq_done(struct ncp_rq * rqp)136 ncp_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 
149 static int
ncp_rq_pathstrhelp(struct mbchain * mbp,c_caddr_t src,caddr_t dst,size_t * srclen,size_t * dstlen)150 ncp_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 
166 int
ncp_rq_pathstring(struct ncp_rq * rqp,int size,const char * name,struct ncp_nlstables * nt)167 ncp_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 
178 int
ncp_rq_pstring(struct ncp_rq * rqp,const char * s)179 ncp_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 
192 int
ncp_rq_dbase_path(struct ncp_rq * rqp,u_int8_t vol_num,u_int32_t dir_base,int namelen,u_char * path,struct ncp_nlstables * nt)193 ncp_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  */
227 static int
ncp_sign_packet(struct ncp_conn * conn,struct ncp_rq * rqp,int * size)228 ncp_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  */
250 int
ncp_request_int(struct ncp_rq * rqp)251 ncp_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  */
428 static __inline int
ncp_restore_login(struct ncp_conn * conn)429 ncp_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 
444 int
ncp_request(struct ncp_rq * rqp)445 ncp_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);
476 out:
477 	if (error && (rqp->nr_flags & NCPR_DONTFREEONERR) == 0)
478 		ncp_rq_done(rqp);
479 	return error;
480 }
481