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