Merge branch 'vendor/OPENSSL'
[dragonfly.git] / sys / netproto / smb / smb_trantcp.c
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