xref: /dragonfly/sys/netproto/smb/smb_trantcp.c (revision 92fc8b5c)
1 /*
2  * Copyright (c) 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  * $FreeBSD: src/sys/netsmb/smb_trantcp.c,v 1.3.2.1 2001/05/22 08:32:34 bp Exp $
33  * $DragonFly: src/sys/netproto/smb/smb_trantcp.c,v 1.21 2008/01/05 14:02:40 swildner Exp $
34  */
35 #include <sys/param.h>
36 #include <sys/systm.h>
37 #include <sys/kernel.h>
38 #include <sys/malloc.h>
39 #include <sys/mbuf.h>
40 #include <sys/proc.h>
41 #include <sys/protosw.h>
42 #include <sys/resourcevar.h>
43 #include <sys/socket.h>
44 #include <sys/socketvar.h>
45 #include <sys/socketvar2.h>
46 #include <sys/socketops.h>
47 #include <sys/poll.h>
48 #include <sys/uio.h>
49 #include <sys/fcntl.h>
50 #include <sys/sysctl.h>
51 #include <sys/thread2.h>
52 
53 #include <net/if.h>
54 #include <net/route.h>
55 
56 #include <netinet/in.h>
57 #include <netinet/tcp.h>
58 
59 #include <sys/mchain.h>
60 
61 #include "netbios.h"
62 
63 #include "smb.h"
64 #include "smb_conn.h"
65 #include "smb_tran.h"
66 #include "smb_trantcp.h"
67 #include "smb_subr.h"
68 
69 #define M_NBDATA	M_PCB
70 
71 static int smb_tcpsndbuf = 10 * 1024;
72 static int smb_tcprcvbuf = 10 * 1024;
73 
74 SYSCTL_DECL(_net_smb);
75 SYSCTL_INT(_net_smb, OID_AUTO, tcpsndbuf, CTLFLAG_RW, &smb_tcpsndbuf, 0, "");
76 SYSCTL_INT(_net_smb, OID_AUTO, tcprcvbuf, CTLFLAG_RW, &smb_tcprcvbuf, 0, "");
77 
78 #define nb_sosend(so,m,flags,p)					\
79     so_pru_sosend(so, NULL, NULL, m, NULL, flags, td)
80 
81 static int  nbssn_recv(struct nbpcb *nbp, struct mbuf **mpp, int *lenp,
82 	u_int8_t *rpcodep, struct thread *td);
83 static int  smb_nbst_disconnect(struct smb_vc *vcp, struct thread *td);
84 
85 static int
86 nb_setsockopt_int(struct socket *so, int level, int name, int val)
87 {
88 	struct sockopt sopt;
89 
90 	bzero(&sopt, sizeof(sopt));
91 	sopt.sopt_level = level;
92 	sopt.sopt_name = name;
93 	sopt.sopt_val = &val;
94 	sopt.sopt_valsize = sizeof(val);
95 	return sosetopt(so, &sopt);
96 }
97 
98 static int
99 nb_intr(struct nbpcb *nbp, struct thread *td)
100 {
101 	return 0;
102 }
103 
104 static void
105 nb_upcall(struct socket *so, void *arg, int waitflag)
106 {
107 	struct nbpcb *nbp = arg;
108 
109 	if (arg == NULL || nbp->nbp_selectid == NULL)
110 		return;
111 	wakeup(nbp->nbp_selectid);
112 }
113 
114 static int
115 nb_sethdr(struct mbuf *m, u_int8_t type, u_int32_t len)
116 {
117 	u_int32_t *p = mtod(m, u_int32_t *);
118 
119 	*p = htonl((len & 0x1FFFF) | (type << 24));
120 	return 0;
121 }
122 
123 static int
124 nb_put_name(struct mbchain *mbp, struct sockaddr_nb *snb)
125 {
126 	int error;
127 	u_char seglen, *cp;
128 
129 	cp = snb->snb_name;
130 	if (*cp == 0)
131 		return EINVAL;
132 	NBDEBUG("[%s]\n", cp);
133 	for (;;) {
134 		seglen = (*cp) + 1;
135 		error = mb_put_mem(mbp, cp, seglen, MB_MSYSTEM);
136 		if (error)
137 			return error;
138 		if (seglen == 1)
139 			break;
140 		cp += seglen;
141 	}
142 	return 0;
143 }
144 
145 static int
146 nb_connect_in(struct nbpcb *nbp, struct sockaddr_in *to, struct thread *td)
147 {
148 	struct socket *so;
149 	int error;
150 
151 	error = socreate(AF_INET, &so, SOCK_STREAM, IPPROTO_TCP, td);
152 	if (error)
153 		return error;
154 	nbp->nbp_tso = so;
155 	so->so_upcallarg = (caddr_t)nbp;
156 	so->so_upcall = nb_upcall;
157 	atomic_set_int(&so->so_rcv.ssb_flags, SSB_UPCALL);
158 	so->so_rcv.ssb_timeo = (5 * hz);
159 	so->so_snd.ssb_timeo = (5 * hz);
160 	error = soreserve(so, nbp->nbp_sndbuf, nbp->nbp_rcvbuf,
161 			  &td->td_proc->p_rlimit[RLIMIT_SBSIZE]);
162 	if (error)
163 		goto bad;
164 	nb_setsockopt_int(so, SOL_SOCKET, SO_KEEPALIVE, 1);
165 	nb_setsockopt_int(so, IPPROTO_TCP, TCP_NODELAY, 1);
166 	atomic_clear_int(&so->so_rcv.ssb_flags, SSB_NOINTR);
167 	atomic_clear_int(&so->so_snd.ssb_flags, SSB_NOINTR);
168 	error = soconnect(so, (struct sockaddr*)to, td);
169 
170 	/*
171 	 * If signals are allowed nbssn_recv() can wind up in a hard loop
172 	 * on EWOULDBLOCK.
173 	 */
174 	atomic_set_int(&so->so_rcv.ssb_flags, SSB_NOINTR);
175 	atomic_set_int(&so->so_snd.ssb_flags, SSB_NOINTR);
176 	if (error)
177 		goto bad;
178 	crit_enter();
179 	while ((so->so_state & SS_ISCONNECTING) && so->so_error == 0) {
180 		tsleep(&so->so_timeo, 0, "nbcon", 2 * hz);
181 		if ((so->so_state & SS_ISCONNECTING) && so->so_error == 0 &&
182 			(error = nb_intr(nbp, td)) != 0) {
183 			soclrstate(so, SS_ISCONNECTING);
184 			crit_exit();
185 			goto bad;
186 		}
187 	}
188 	if (so->so_error) {
189 		error = so->so_error;
190 		so->so_error = 0;
191 		crit_exit();
192 		goto bad;
193 	}
194 	crit_exit();
195 	return 0;
196 bad:
197 	smb_nbst_disconnect(nbp->nbp_vc, td);
198 	return error;
199 }
200 
201 static int
202 nbssn_rq_request(struct nbpcb *nbp, struct thread *td)
203 {
204 	struct mbchain mb, *mbp = &mb;
205 	struct mdchain md, *mdp = &md;
206 	struct mbuf *m0;
207 	struct sockaddr_in sin;
208 	u_short port;
209 	u_int8_t rpcode;
210 	int error, rplen, res;
211 
212 	error = mb_init(mbp);
213 	if (error)
214 		return error;
215 	mb_put_uint32le(mbp, 0);
216 	nb_put_name(mbp, nbp->nbp_paddr);
217 	nb_put_name(mbp, nbp->nbp_laddr);
218 	nb_sethdr(mbp->mb_top, NB_SSN_REQUEST, mb_fixhdr(mbp) - 4);
219 	error = nb_sosend(nbp->nbp_tso, mbp->mb_top, 0, td);
220 	if (!error) {
221 		nbp->nbp_state = NBST_RQSENT;
222 	}
223 	mb_detach(mbp);
224 	mb_done(mbp);
225 	if (error)
226 		return error;
227 	error = socket_wait(nbp->nbp_tso, &nbp->nbp_timo, &res);
228 	if (error == EWOULDBLOCK) {	/* Timeout */
229 		NBDEBUG("initial request timeout\n");
230 		return ETIMEDOUT;
231 	}
232 	if (error)			/* restart or interrupt */
233 		return error;
234 	error = nbssn_recv(nbp, &m0, &rplen, &rpcode, td);
235 	if (error) {
236 		NBDEBUG("recv() error %d\n", error);
237 		return error;
238 	}
239 	/*
240 	 * Process NETBIOS reply
241 	 */
242 	if (m0)
243 		md_initm(mdp, m0);
244 	error = 0;
245 	do {
246 		if (rpcode == NB_SSN_POSRESP) {
247 			nbp->nbp_state = NBST_SESSION;
248 			nbp->nbp_flags |= NBF_CONNECTED;
249 			break;
250 		}
251 		if (rpcode != NB_SSN_RTGRESP) {
252 			error = ECONNABORTED;
253 			break;
254 		}
255 		if (rplen != 6) {
256 			error = ECONNABORTED;
257 			break;
258 		}
259 		md_get_mem(mdp, (caddr_t)&sin.sin_addr, 4, MB_MSYSTEM);
260 		md_get_uint16(mdp, &port);
261 		sin.sin_port = port;
262 		nbp->nbp_state = NBST_RETARGET;
263 		smb_nbst_disconnect(nbp->nbp_vc, td);
264 		error = nb_connect_in(nbp, &sin, td);
265 		if (!error)
266 			error = nbssn_rq_request(nbp, td);
267 		if (error) {
268 			smb_nbst_disconnect(nbp->nbp_vc, td);
269 			break;
270 		}
271 	} while(0);
272 	if (m0)
273 		md_done(mdp);
274 	return error;
275 }
276 
277 static int
278 nbssn_recvhdr(struct nbpcb *nbp, int *lenp,
279 	u_int8_t *rpcodep, int flags, struct thread *td)
280 {
281 	struct socket *so = nbp->nbp_tso;
282 	struct uio auio;
283 	struct iovec aio;
284 	u_int32_t len;
285 	int error;
286 
287 	aio.iov_base = (caddr_t)&len;
288 	aio.iov_len = sizeof(len);
289 	auio.uio_iov = &aio;
290 	auio.uio_iovcnt = 1;
291 	auio.uio_segflg = UIO_SYSSPACE;
292 	auio.uio_rw = UIO_READ;
293 	auio.uio_offset = 0;
294 	auio.uio_resid = sizeof(len);
295 	auio.uio_td = td;
296 	error = so_pru_soreceive(so, NULL, &auio, NULL, NULL, &flags);
297 	if (error)
298 		return error;
299 	if (auio.uio_resid > 0) {
300 		SMBSDEBUG("short reply\n");
301 		return EPIPE;
302 	}
303 	len = ntohl(len);
304 	*rpcodep = (len >> 24) & 0xFF;
305 	len &= 0x1ffff;
306 	if (len > SMB_MAXPKTLEN) {
307 		SMBERROR("packet too long (%d)\n", len);
308 		return EFBIG;
309 	}
310 	*lenp = len;
311 	return 0;
312 }
313 
314 static int
315 nbssn_recv(struct nbpcb *nbp, struct mbuf **mpp, int *lenp,
316 	u_int8_t *rpcodep, struct thread *td)
317 {
318 	struct socket *so = nbp->nbp_tso;
319 	struct sockbuf sio;
320 	int error, rcvflg;
321 	int savelen = 0;
322 	u_int8_t rpcode = 0;
323 
324 	if (so == NULL)
325 		return ENOTCONN;
326 
327 	sbinit(&sio, 0);
328 	if (mpp)
329 		*mpp = NULL;
330 
331 	for(;;) {
332 		error = nbssn_recvhdr(nbp, &savelen, &rpcode, MSG_DONTWAIT, td);
333 		if (so->so_state &
334 		    (SS_ISDISCONNECTING | SS_ISDISCONNECTED | SS_CANTRCVMORE)) {
335 			nbp->nbp_state = NBST_CLOSED;
336 			NBDEBUG("session closed by peer\n");
337 			error = ECONNRESET;
338 			break;
339 		}
340 		if (error)
341 			break;
342 		if (savelen == 0 && nbp->nbp_state != NBST_SESSION)
343 			break;
344 		if (rpcode == NB_SSN_KEEPALIVE)
345 			continue;
346 		do {
347 			sbinit(&sio, savelen);
348 			rcvflg = MSG_WAITALL;
349 			error = so_pru_soreceive(so, NULL, NULL, &sio,
350 						 NULL, &rcvflg);
351 		} while (error == EWOULDBLOCK || error == EINTR ||
352 				 error == ERESTART);
353 		if (error)
354 			break;
355 		if (sio.sb_cc != savelen) {
356 			SMBERROR("packet is shorter than expected\n");
357 			error = EPIPE;
358 			m_freem(sio.sb_mb);
359 			break;
360 		}
361 		if (nbp->nbp_state == NBST_SESSION && rpcode == NB_SSN_MESSAGE)
362 			break;
363 		NBDEBUG("non-session packet %x\n", rpcode);
364 		m_freem(sio.sb_mb);
365 		sio.sb_mb = NULL;
366 		sio.sb_cc = 0;
367 	}
368 	if (error == 0) {
369 		if (mpp)
370 			*mpp = sio.sb_mb;
371 		else
372 			m_freem(sio.sb_mb);
373 		*lenp = sio.sb_cc;
374 		*rpcodep = rpcode;
375 	}
376 	return (error);
377 }
378 
379 /*
380  * SMB transport interface
381  */
382 static int
383 smb_nbst_create(struct smb_vc *vcp, struct thread *td)
384 {
385 	struct nbpcb *nbp;
386 
387 	MALLOC(nbp, struct nbpcb *, sizeof *nbp, M_NBDATA, M_WAITOK | M_ZERO);
388 	nbp->nbp_timo.tv_sec = 15;	/* XXX: sysctl ? */
389 	nbp->nbp_state = NBST_CLOSED;
390 	nbp->nbp_vc = vcp;
391 	nbp->nbp_sndbuf = smb_tcpsndbuf;
392 	nbp->nbp_rcvbuf = smb_tcprcvbuf;
393 	vcp->vc_tdata = nbp;
394 	return 0;
395 }
396 
397 static int
398 smb_nbst_done(struct smb_vc *vcp, struct thread *td)
399 {
400 	struct nbpcb *nbp = vcp->vc_tdata;
401 
402 	if (nbp == NULL)
403 		return ENOTCONN;
404 	smb_nbst_disconnect(vcp, td);
405 	if (nbp->nbp_laddr)
406 		kfree(nbp->nbp_laddr, M_SONAME);
407 	if (nbp->nbp_paddr)
408 		kfree(nbp->nbp_paddr, M_SONAME);
409 	kfree(nbp, M_NBDATA);
410 	return 0;
411 }
412 
413 static int
414 smb_nbst_bind(struct smb_vc *vcp, struct sockaddr *sap, struct thread *td)
415 {
416 	struct nbpcb *nbp = vcp->vc_tdata;
417 	struct sockaddr_nb *snb;
418 	int error, slen;
419 
420 	NBDEBUG("\n");
421 	error = EINVAL;
422 	do {
423 		if (nbp->nbp_flags & NBF_LOCADDR)
424 			break;
425 		/*
426 		 * It is possible to create NETBIOS name in the kernel,
427 		 * but nothing prevents us to do it in the user space.
428 		 */
429 		if (sap == NULL)
430 			break;
431 		slen = sap->sa_len;
432 		if (slen < NB_MINSALEN)
433 			break;
434 		snb = (struct sockaddr_nb*)dup_sockaddr(sap);
435 		if (snb == NULL) {
436 			error = ENOMEM;
437 			break;
438 		}
439 		nbp->nbp_laddr = snb;
440 		nbp->nbp_flags |= NBF_LOCADDR;
441 		error = 0;
442 	} while(0);
443 	return error;
444 }
445 
446 static int
447 smb_nbst_connect(struct smb_vc *vcp, struct sockaddr *sap, struct thread *td)
448 {
449 	struct nbpcb *nbp = vcp->vc_tdata;
450 	struct sockaddr_in sin;
451 	struct sockaddr_nb *snb;
452 	struct timespec ts1, ts2;
453 	int error, slen;
454 
455 	NBDEBUG("\n");
456 	if (nbp->nbp_tso != NULL)
457 		return EISCONN;
458 	if (nbp->nbp_laddr == NULL)
459 		return EINVAL;
460 	slen = sap->sa_len;
461 	if (slen < NB_MINSALEN)
462 		return EINVAL;
463 	if (nbp->nbp_paddr) {
464 		kfree(nbp->nbp_paddr, M_SONAME);
465 		nbp->nbp_paddr = NULL;
466 	}
467 	snb = (struct sockaddr_nb*)dup_sockaddr(sap);
468 	if (snb == NULL)
469 		return ENOMEM;
470 	nbp->nbp_paddr = snb;
471 	sin = snb->snb_addrin;
472 	getnanotime(&ts1);
473 	error = nb_connect_in(nbp, &sin, td);
474 	if (error)
475 		return error;
476 	getnanotime(&ts2);
477 	timespecsub(&ts2, &ts1);
478 	if (ts2.tv_sec == 0 && ts2.tv_sec == 0)
479 		ts2.tv_sec = 1;
480 	nbp->nbp_timo = ts2;
481 	timespecadd(&nbp->nbp_timo, &ts2);
482 	timespecadd(&nbp->nbp_timo, &ts2);
483 	timespecadd(&nbp->nbp_timo, &ts2);	/*  * 4 */
484 	error = nbssn_rq_request(nbp, td);
485 	if (error)
486 		smb_nbst_disconnect(vcp, td);
487 	return error;
488 }
489 
490 static int
491 smb_nbst_disconnect(struct smb_vc *vcp, struct thread *td)
492 {
493 	struct nbpcb *nbp = vcp->vc_tdata;
494 	struct socket *so;
495 
496 	if (nbp == NULL || nbp->nbp_tso == NULL)
497 		return ENOTCONN;
498 	if ((so = nbp->nbp_tso) != NULL) {
499 		nbp->nbp_flags &= ~NBF_CONNECTED;
500 		nbp->nbp_tso = NULL;
501 		soshutdown(so, SHUT_RDWR);
502 		soclose(so, FNONBLOCK);
503 	}
504 	if (nbp->nbp_state != NBST_RETARGET) {
505 		nbp->nbp_state = NBST_CLOSED;
506 	}
507 	return 0;
508 }
509 
510 static int
511 smb_nbst_send(struct smb_vc *vcp, struct mbuf *m0, struct thread *td)
512 {
513 	struct nbpcb *nbp = vcp->vc_tdata;
514 	int error;
515 
516 	if (nbp->nbp_state != NBST_SESSION) {
517 		error = ENOTCONN;
518 		goto abort;
519 	}
520 	M_PREPEND(m0, 4, MB_TRYWAIT);
521 	if (m0 == NULL)
522 		return ENOBUFS;
523 	nb_sethdr(m0, NB_SSN_MESSAGE, m_fixhdr(m0) - 4);
524 	error = nb_sosend(nbp->nbp_tso, m0, 0, td);
525 	return error;
526 abort:
527 	if (m0)
528 		m_freem(m0);
529 	return error;
530 }
531 
532 
533 static int
534 smb_nbst_recv(struct smb_vc *vcp, struct mbuf **mpp, struct thread *td)
535 {
536 	struct nbpcb *nbp = vcp->vc_tdata;
537 	u_int8_t rpcode;
538 	int error, rplen;
539 
540 	nbp->nbp_flags |= NBF_RECVLOCK;
541 	error = nbssn_recv(nbp, mpp, &rplen, &rpcode, td);
542 	nbp->nbp_flags &= ~NBF_RECVLOCK;
543 	return error;
544 }
545 
546 static void
547 smb_nbst_timo(struct smb_vc *vcp)
548 {
549 	return;
550 }
551 
552 static void
553 smb_nbst_intr(struct smb_vc *vcp)
554 {
555 	struct nbpcb *nbp = vcp->vc_tdata;
556 
557 	if (nbp == NULL || nbp->nbp_tso == NULL)
558 		return;
559 	sorwakeup(nbp->nbp_tso);
560 	sowwakeup(nbp->nbp_tso);
561 }
562 
563 static int
564 smb_nbst_getparam(struct smb_vc *vcp, int param, void *data)
565 {
566 	struct nbpcb *nbp = vcp->vc_tdata;
567 
568 	switch (param) {
569 	    case SMBTP_SNDSZ:
570 		*(int*)data = nbp->nbp_sndbuf;
571 		break;
572 	    case SMBTP_RCVSZ:
573 		*(int*)data = nbp->nbp_rcvbuf;
574 		break;
575 	    case SMBTP_TIMEOUT:
576 		*(struct timespec*)data = nbp->nbp_timo;
577 		break;
578 	    default:
579 		return EINVAL;
580 	}
581 	return 0;
582 }
583 
584 static int
585 smb_nbst_setparam(struct smb_vc *vcp, int param, void *data)
586 {
587 	struct nbpcb *nbp = vcp->vc_tdata;
588 
589 	switch (param) {
590 	    case SMBTP_SELECTID:
591 		nbp->nbp_selectid = data;
592 		break;
593 	    default:
594 		return EINVAL;
595 	}
596 	return 0;
597 }
598 
599 /*
600  * Check for fatal errors
601  */
602 static int
603 smb_nbst_fatal(struct smb_vc *vcp, int error)
604 {
605 	switch (error) {
606 	    case ENOTCONN:
607 	    case ENETRESET:
608 	    case ECONNABORTED:
609 		return 1;
610 	}
611 	return 0;
612 }
613 
614 
615 struct smb_tran_desc smb_tran_nbtcp_desc = {
616 	SMBT_NBTCP,
617 	smb_nbst_create, smb_nbst_done,
618 	smb_nbst_bind, smb_nbst_connect, smb_nbst_disconnect,
619 	smb_nbst_send, smb_nbst_recv,
620 	smb_nbst_timo, smb_nbst_intr,
621 	smb_nbst_getparam, smb_nbst_setparam,
622 	smb_nbst_fatal
623 };
624 
625