1 | /* $NetBSD: smb_trantcp.c,v 1.49 2015/05/22 22:05:32 rtr Exp $ */ |
2 | |
3 | /*- |
4 | * Copyright (c) 2008 The NetBSD Foundation, Inc. |
5 | * All rights reserved. |
6 | * |
7 | * Redistribution and use in source and binary forms, with or without |
8 | * modification, are permitted provided that the following conditions |
9 | * are met: |
10 | * 1. Redistributions of source code must retain the above copyright |
11 | * notice, this list of conditions and the following disclaimer. |
12 | * 2. Redistributions in binary form must reproduce the above copyright |
13 | * notice, this list of conditions and the following disclaimer in the |
14 | * documentation and/or other materials provided with the distribution. |
15 | * |
16 | * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS |
17 | * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED |
18 | * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR |
19 | * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS |
20 | * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR |
21 | * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF |
22 | * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS |
23 | * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN |
24 | * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) |
25 | * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE |
26 | * POSSIBILITY OF SUCH DAMAGE. |
27 | */ |
28 | |
29 | /* |
30 | * Copyright (c) 2000-2001 Boris Popov |
31 | * All rights reserved. |
32 | * |
33 | * Redistribution and use in source and binary forms, with or without |
34 | * modification, are permitted provided that the following conditions |
35 | * are met: |
36 | * 1. Redistributions of source code must retain the above copyright |
37 | * notice, this list of conditions and the following disclaimer. |
38 | * 2. Redistributions in binary form must reproduce the above copyright |
39 | * notice, this list of conditions and the following disclaimer in the |
40 | * documentation and/or other materials provided with the distribution. |
41 | * 3. All advertising materials mentioning features or use of this software |
42 | * must display the following acknowledgement: |
43 | * This product includes software developed by Boris Popov. |
44 | * 4. Neither the name of the author nor the names of any co-contributors |
45 | * may be used to endorse or promote products derived from this software |
46 | * without specific prior written permission. |
47 | * |
48 | * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND |
49 | * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE |
50 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE |
51 | * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE |
52 | * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL |
53 | * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS |
54 | * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) |
55 | * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT |
56 | * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY |
57 | * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF |
58 | * SUCH DAMAGE. |
59 | * |
60 | * FreeBSD: src/sys/netsmb/smb_trantcp.c,v 1.17 2003/02/19 05:47:38 imp Exp |
61 | */ |
62 | |
63 | #include <sys/cdefs.h> |
64 | __KERNEL_RCSID(0, "$NetBSD: smb_trantcp.c,v 1.49 2015/05/22 22:05:32 rtr Exp $" ); |
65 | |
66 | #include <sys/param.h> |
67 | #include <sys/systm.h> |
68 | #include <sys/kernel.h> |
69 | #include <sys/kmem.h> |
70 | #include <sys/mbuf.h> |
71 | #include <sys/proc.h> |
72 | #include <sys/protosw.h> |
73 | #include <sys/socket.h> |
74 | #include <sys/socketvar.h> |
75 | #include <sys/poll.h> |
76 | #include <sys/uio.h> |
77 | #include <sys/select.h> |
78 | |
79 | #include <net/if.h> |
80 | #include <net/route.h> |
81 | |
82 | #include <netinet/in.h> |
83 | #include <netinet/tcp.h> |
84 | |
85 | #include <netsmb/mchain.h> |
86 | |
87 | #include <netsmb/netbios.h> |
88 | |
89 | #include <netsmb/smb.h> |
90 | #include <netsmb/smb_conn.h> |
91 | #include <netsmb/smb_tran.h> |
92 | #include <netsmb/smb_trantcp.h> |
93 | #include <netsmb/smb_subr.h> |
94 | |
95 | static int nb_tcpsndbuf = NB_SNDQ; |
96 | static int nb_tcprcvbuf = NB_RCVQ; |
97 | |
98 | #define nb_sosend(so,m,flags,l) (*(so)->so_send)(so, NULL, (struct uio *)0, \ |
99 | m, (struct mbuf *)0, flags, l) |
100 | |
101 | static int nbssn_recv(struct nbpcb *nbp, struct mbuf **mpp, int *lenp, |
102 | u_int8_t *rpcodep, bool firstwait, struct lwp *l); |
103 | static int smb_nbst_disconnect(struct smb_vc *vcp, struct lwp *l); |
104 | |
105 | static int |
106 | nb_setsockopt_int(struct socket *so, int level, int name, int val) |
107 | { |
108 | |
109 | return so_setsockopt(NULL, so, level, name, &val, sizeof(val)); /* XXX */ |
110 | } |
111 | |
112 | static int |
113 | nb_intr(struct nbpcb *nbp, struct lwp *l) |
114 | { |
115 | return 0; |
116 | } |
117 | |
118 | static void |
119 | nb_upcall(struct socket *so, void *arg, int events, int waitflag) |
120 | { |
121 | struct nbpcb *nbp = (void *)arg; |
122 | |
123 | if (arg == NULL || nbp->nbp_selectid == NULL) |
124 | return; |
125 | wakeup(nbp->nbp_selectid); |
126 | } |
127 | |
128 | static int |
129 | nb_sethdr(struct mbuf *m, u_int8_t type, u_int32_t len) |
130 | { |
131 | u_int32_t *p = mtod(m, u_int32_t *); |
132 | |
133 | *p = htonl((len & 0x1FFFF) | (type << 24)); |
134 | return 0; |
135 | } |
136 | |
137 | static int |
138 | nb_put_name(struct mbchain *mbp, struct sockaddr_nb *snb) |
139 | { |
140 | int error; |
141 | u_char seglen, *cp; |
142 | |
143 | cp = snb->snb_name; |
144 | if (*cp == 0) |
145 | return EINVAL; |
146 | NBDEBUG(("[%s]\n" , cp)); |
147 | for (;;) { |
148 | seglen = (*cp) + 1; |
149 | error = mb_put_mem(mbp, cp, seglen, MB_MSYSTEM); |
150 | if (error) |
151 | return error; |
152 | if (seglen == 1) |
153 | break; |
154 | cp += seglen; |
155 | } |
156 | return 0; |
157 | } |
158 | |
159 | static int |
160 | nb_connect_in(struct nbpcb *nbp, struct sockaddr_in *to, struct lwp *l) |
161 | { |
162 | struct socket *so; |
163 | int error; |
164 | |
165 | error = socreate(AF_INET, &so, SOCK_STREAM, IPPROTO_TCP, l, NULL); |
166 | if (error) |
167 | return error; |
168 | solock(so); |
169 | nbp->nbp_tso = so; |
170 | so->so_upcallarg = (void *)nbp; |
171 | so->so_upcall = nb_upcall; |
172 | so->so_rcv.sb_flags |= SB_UPCALL; |
173 | so->so_rcv.sb_flags &= ~SB_NOINTR; |
174 | so->so_snd.sb_flags &= ~SB_NOINTR; |
175 | so->so_rcv.sb_timeo = NB_RCVTIMEO * hz; |
176 | so->so_snd.sb_timeo = NB_RCVTIMEO * hz; |
177 | error = soreserve(so, nb_tcpsndbuf, nb_tcprcvbuf); |
178 | sounlock(so); |
179 | if (error) |
180 | goto bad; |
181 | nb_setsockopt_int(so, SOL_SOCKET, SO_KEEPALIVE, 1); |
182 | nb_setsockopt_int(so, IPPROTO_TCP, TCP_NODELAY, 1); |
183 | solock(so); |
184 | error = soconnect(so, (struct sockaddr *)to, l); |
185 | if (error) { |
186 | sounlock(so); |
187 | goto bad; |
188 | } |
189 | while ((so->so_state & SS_ISCONNECTING) && so->so_error == 0) { |
190 | sowait(so, false, 2 * hz); |
191 | if ((so->so_state & SS_ISCONNECTING) && so->so_error == 0 && |
192 | (error = nb_intr(nbp, l)) != 0) { |
193 | so->so_state &= ~SS_ISCONNECTING; |
194 | sounlock(so); |
195 | goto bad; |
196 | } |
197 | } |
198 | if (so->so_error) { |
199 | error = so->so_error; |
200 | so->so_error = 0; |
201 | sounlock(so); |
202 | goto bad; |
203 | } |
204 | sounlock(so); |
205 | return 0; |
206 | bad: |
207 | smb_nbst_disconnect(nbp->nbp_vc, l); |
208 | return error; |
209 | } |
210 | |
211 | static int |
212 | nbssn_rq_request(struct nbpcb *nbp, struct lwp *l) |
213 | { |
214 | struct mbchain mb, *mbp = &mb; |
215 | struct mdchain md, *mdp = &md; |
216 | struct mbuf *m0; |
217 | struct sockaddr_in sin; |
218 | u_short port; |
219 | u_int8_t rpcode; |
220 | int error, rplen; |
221 | |
222 | error = mb_init(mbp); |
223 | if (error) |
224 | return error; |
225 | mb_put_uint32le(mbp, 0); |
226 | (void) nb_put_name(mbp, nbp->nbp_paddr); |
227 | (void) nb_put_name(mbp, nbp->nbp_laddr); |
228 | nb_sethdr(mbp->mb_top, NB_SSN_REQUEST, mb_fixhdr(mbp) - 4); |
229 | error = nb_sosend(nbp->nbp_tso, mbp->mb_top, 0, l); |
230 | if (!error) { |
231 | nbp->nbp_state = NBST_RQSENT; |
232 | } |
233 | mb_detach(mbp); |
234 | mb_done(mbp); |
235 | if (error) |
236 | return error; |
237 | error = nbssn_recv(nbp, &m0, &rplen, &rpcode, true, l); |
238 | if (error) { |
239 | if (error == EWOULDBLOCK) { /* Timeout */ |
240 | NBDEBUG(("initial request timeout\n" )); |
241 | return ETIMEDOUT; |
242 | } |
243 | NBDEBUG(("recv() error %d\n" , error)); |
244 | return error; |
245 | } |
246 | /* |
247 | * Process NETBIOS reply |
248 | */ |
249 | if (m0) |
250 | md_initm(mdp, m0); |
251 | error = 0; |
252 | do { |
253 | if (rpcode == NB_SSN_POSRESP) { |
254 | nbp->nbp_state = NBST_SESSION; |
255 | nbp->nbp_flags |= NBF_CONNECTED; |
256 | break; |
257 | } |
258 | if (rpcode != NB_SSN_RTGRESP) { |
259 | error = ECONNABORTED; |
260 | break; |
261 | } |
262 | if (rplen != 6) { |
263 | error = ECONNABORTED; |
264 | break; |
265 | } |
266 | md_get_mem(mdp, (void *)&sin.sin_addr, 4, MB_MSYSTEM); |
267 | md_get_uint16(mdp, &port); |
268 | sin.sin_port = port; |
269 | nbp->nbp_state = NBST_RETARGET; |
270 | smb_nbst_disconnect(nbp->nbp_vc, l); |
271 | error = nb_connect_in(nbp, &sin, l); |
272 | if (!error) |
273 | error = nbssn_rq_request(nbp, l); |
274 | if (error) { |
275 | smb_nbst_disconnect(nbp->nbp_vc, l); |
276 | break; |
277 | } |
278 | } while(0); |
279 | if (m0) |
280 | md_done(mdp); |
281 | return error; |
282 | } |
283 | |
284 | static int |
285 | nbssn_recvhdr(struct nbpcb *nbp, int *lenp, |
286 | u_int8_t *rpcodep, int flags, struct lwp *l) |
287 | { |
288 | struct socket *so = nbp->nbp_tso; |
289 | struct uio auio; |
290 | struct iovec aio; |
291 | u_int32_t len; |
292 | int error; |
293 | |
294 | aio.iov_base = (void *)&len; |
295 | aio.iov_len = sizeof(len); |
296 | auio.uio_iov = &aio; |
297 | auio.uio_iovcnt = 1; |
298 | auio.uio_rw = UIO_READ; |
299 | auio.uio_offset = 0; |
300 | auio.uio_resid = sizeof(len); |
301 | UIO_SETUP_SYSSPACE(&auio); |
302 | error = (*so->so_receive)(so, NULL, &auio, NULL, NULL, &flags); |
303 | if (error) |
304 | return error; |
305 | if (auio.uio_resid > 0) { |
306 | SMBSDEBUG(("short reply\n" )); |
307 | return EPIPE; |
308 | } |
309 | len = ntohl(len); |
310 | *rpcodep = (len >> 24) & 0xFF; |
311 | len &= 0x1ffff; |
312 | if (len > SMB_MAXPKTLEN) { |
313 | SMBERROR(("packet too long (%d)\n" , len)); |
314 | return EFBIG; |
315 | } |
316 | *lenp = len; |
317 | return 0; |
318 | } |
319 | |
320 | static int |
321 | nbssn_recv(struct nbpcb *nbp, struct mbuf **mpp, int *lenp, |
322 | u_int8_t *rpcodep, bool dowait, struct lwp *l) |
323 | { |
324 | struct socket *so = nbp->nbp_tso; |
325 | struct uio auio; |
326 | struct mbuf *m, *tm, *im; |
327 | u_int8_t rpcode; |
328 | int len, resid; |
329 | int error, rcvflg; |
330 | |
331 | len = 0; /* XXX gcc */ |
332 | rpcode = 0; /* XXX gcc */ |
333 | |
334 | if (so == NULL) |
335 | return ENOTCONN; |
336 | |
337 | if (mpp) |
338 | *mpp = NULL; |
339 | m = NULL; |
340 | for(;; dowait = false) { |
341 | /* |
342 | * Poll for a response header. |
343 | * If we don't have one waiting, return. |
344 | */ |
345 | error = nbssn_recvhdr(nbp, &len, &rpcode, |
346 | dowait ? 0 : MSG_DONTWAIT, l); |
347 | if (so->so_state & |
348 | (SS_ISDISCONNECTING | SS_ISDISCONNECTED | SS_CANTRCVMORE)) { |
349 | nbp->nbp_state = NBST_CLOSED; |
350 | NBDEBUG(("session closed by peer\n" )); |
351 | return ECONNRESET; |
352 | } |
353 | if (error) |
354 | return error; |
355 | if (len == 0 && nbp->nbp_state != NBST_SESSION) |
356 | break; |
357 | /* no data, try again */ |
358 | if (rpcode == NB_SSN_KEEPALIVE) |
359 | continue; |
360 | |
361 | /* |
362 | * Loop, blocking, for data following the response header. |
363 | * |
364 | * Note that we can't simply block here with MSG_WAITALL for the |
365 | * entire response size, as it may be larger than the TCP |
366 | * slow-start window that the sender employs. This will result |
367 | * in the sender stalling until the delayed ACK is sent, then |
368 | * resuming slow-start, resulting in very poor performance. |
369 | * |
370 | * Instead, we never request more than NB_SORECEIVE_CHUNK |
371 | * bytes at a time, resulting in an ack being pushed by |
372 | * the TCP code at the completion of each call. |
373 | */ |
374 | resid = len; |
375 | while (resid > 0) { |
376 | tm = NULL; |
377 | rcvflg = MSG_WAITALL; |
378 | memset(&auio, 0, sizeof(auio)); |
379 | auio.uio_resid = min(resid, NB_SORECEIVE_CHUNK); |
380 | /* not need to setup uio_vmspace */ |
381 | resid -= auio.uio_resid; |
382 | /* |
383 | * Spin until we have collected everything in |
384 | * this chunk. |
385 | */ |
386 | do { |
387 | rcvflg = MSG_WAITALL; |
388 | error = (*so->so_receive)(so, NULL, &auio, &tm, |
389 | NULL, &rcvflg); |
390 | } while (error == EWOULDBLOCK || error == EINTR || |
391 | error == ERESTART); |
392 | if (error) |
393 | goto out; |
394 | /* short return guarantees unhappiness */ |
395 | if (auio.uio_resid > 0) { |
396 | SMBERROR(("packet is shorter than expected\n" )); |
397 | error = EPIPE; |
398 | goto out; |
399 | } |
400 | /* append received chunk to previous chunk(s) */ |
401 | if (m == NULL) { |
402 | m = tm; |
403 | } else { |
404 | /* |
405 | * Just glue the new chain on the end. |
406 | * Consumer will pullup as required. |
407 | */ |
408 | for (im = m; im->m_next != NULL; im = im->m_next) |
409 | ; |
410 | im->m_next = tm; |
411 | } |
412 | } |
413 | /* got a session/message packet? */ |
414 | if (nbp->nbp_state == NBST_SESSION && |
415 | rpcode == NB_SSN_MESSAGE) |
416 | break; |
417 | /* drop packet and try for another */ |
418 | NBDEBUG(("non-session packet %x\n" , rpcode)); |
419 | if (m) { |
420 | m_freem(m); |
421 | m = NULL; |
422 | } |
423 | } |
424 | |
425 | out: |
426 | if (error) { |
427 | if (m) |
428 | m_freem(m); |
429 | return error; |
430 | } |
431 | if (mpp) |
432 | *mpp = m; |
433 | else |
434 | m_freem(m); |
435 | *lenp = len; |
436 | *rpcodep = rpcode; |
437 | return 0; |
438 | } |
439 | |
440 | /* |
441 | * SMB transport interface |
442 | */ |
443 | static int |
444 | smb_nbst_create(struct smb_vc *vcp, struct lwp *l) |
445 | { |
446 | struct nbpcb *nbp; |
447 | |
448 | nbp = kmem_zalloc(sizeof(*nbp), KM_SLEEP); |
449 | nbp->nbp_state = NBST_CLOSED; |
450 | nbp->nbp_vc = vcp; |
451 | vcp->vc_tdata = nbp; |
452 | return 0; |
453 | } |
454 | |
455 | static int |
456 | smb_nbst_done(struct smb_vc *vcp, struct lwp *l) |
457 | { |
458 | struct nbpcb *nbp = vcp->vc_tdata; |
459 | |
460 | if (nbp == NULL) |
461 | return ENOTCONN; |
462 | smb_nbst_disconnect(vcp, l); |
463 | if (nbp->nbp_laddr) |
464 | free(nbp->nbp_laddr, M_SONAME); |
465 | if (nbp->nbp_paddr) |
466 | free(nbp->nbp_paddr, M_SONAME); |
467 | kmem_free(nbp, sizeof(*nbp)); |
468 | return 0; |
469 | } |
470 | |
471 | static int |
472 | smb_nbst_bind(struct smb_vc *vcp, struct sockaddr *sap, struct lwp *l) |
473 | { |
474 | struct nbpcb *nbp = vcp->vc_tdata; |
475 | struct sockaddr_nb *snb; |
476 | int error, slen; |
477 | |
478 | NBDEBUG(("\n" )); |
479 | error = EINVAL; |
480 | do { |
481 | if (nbp->nbp_flags & NBF_LOCADDR) |
482 | break; |
483 | /* |
484 | * It is possible to create NETBIOS name in the kernel, |
485 | * but nothing prevents us to do it in the user space. |
486 | */ |
487 | if (sap == NULL) |
488 | break; |
489 | slen = sap->sa_len; |
490 | if (slen < NB_MINSALEN) |
491 | break; |
492 | snb = (struct sockaddr_nb*)dup_sockaddr(sap, 1); |
493 | if (snb == NULL) { |
494 | error = ENOMEM; |
495 | break; |
496 | } |
497 | nbp->nbp_laddr = snb; |
498 | nbp->nbp_flags |= NBF_LOCADDR; |
499 | error = 0; |
500 | } while(0); |
501 | return error; |
502 | } |
503 | |
504 | static int |
505 | smb_nbst_connect(struct smb_vc *vcp, struct sockaddr *sap, struct lwp *l) |
506 | { |
507 | struct nbpcb *nbp = vcp->vc_tdata; |
508 | struct sockaddr_in sin; |
509 | struct sockaddr_nb *snb; |
510 | int error, slen; |
511 | |
512 | NBDEBUG(("\n" )); |
513 | if (nbp->nbp_tso != NULL) |
514 | return EISCONN; |
515 | if (nbp->nbp_laddr == NULL) |
516 | return EINVAL; |
517 | slen = sap->sa_len; |
518 | if (slen < NB_MINSALEN) |
519 | return EINVAL; |
520 | if (nbp->nbp_paddr) { |
521 | free(nbp->nbp_paddr, M_SONAME); |
522 | nbp->nbp_paddr = NULL; |
523 | } |
524 | snb = (struct sockaddr_nb*)dup_sockaddr(sap, 1); |
525 | if (snb == NULL) |
526 | return ENOMEM; |
527 | nbp->nbp_paddr = snb; |
528 | sin = snb->snb_addrin; |
529 | error = nb_connect_in(nbp, &sin, l); |
530 | if (error) |
531 | return error; |
532 | error = nbssn_rq_request(nbp, l); |
533 | if (error) |
534 | smb_nbst_disconnect(vcp, l); |
535 | return error; |
536 | } |
537 | |
538 | static int |
539 | smb_nbst_disconnect(struct smb_vc *vcp, struct lwp *l) |
540 | { |
541 | struct nbpcb *nbp = vcp->vc_tdata; |
542 | struct socket *so; |
543 | |
544 | if (nbp == NULL || nbp->nbp_tso == NULL) |
545 | return ENOTCONN; |
546 | if ((so = nbp->nbp_tso) != NULL) { |
547 | nbp->nbp_flags &= ~NBF_CONNECTED; |
548 | nbp->nbp_tso = NULL; |
549 | solock(so); |
550 | soshutdown(so, 2); |
551 | sounlock(so); |
552 | soclose(so); |
553 | } |
554 | if (nbp->nbp_state != NBST_RETARGET) { |
555 | nbp->nbp_state = NBST_CLOSED; |
556 | } |
557 | return 0; |
558 | } |
559 | |
560 | static int |
561 | smb_nbst_send(struct smb_vc *vcp, struct mbuf *m0, struct lwp *l) |
562 | { |
563 | struct nbpcb *nbp = vcp->vc_tdata; |
564 | int error; |
565 | |
566 | if (nbp->nbp_state != NBST_SESSION) { |
567 | error = ENOTCONN; |
568 | goto abort; |
569 | } |
570 | M_PREPEND(m0, 4, M_WAITOK); |
571 | if (m0 == NULL) |
572 | return ENOBUFS; |
573 | nb_sethdr(m0, NB_SSN_MESSAGE, m_fixhdr(m0) - 4); |
574 | error = nb_sosend(nbp->nbp_tso, m0, 0, l); |
575 | return error; |
576 | abort: |
577 | if (m0) |
578 | m_freem(m0); |
579 | return error; |
580 | } |
581 | |
582 | |
583 | static int |
584 | smb_nbst_recv(struct smb_vc *vcp, struct mbuf **mpp, struct lwp *l) |
585 | { |
586 | struct nbpcb *nbp = vcp->vc_tdata; |
587 | u_int8_t rpcode; |
588 | int error, rplen; |
589 | |
590 | nbp->nbp_flags |= NBF_RECVLOCK; |
591 | error = nbssn_recv(nbp, mpp, &rplen, &rpcode, false, l); |
592 | nbp->nbp_flags &= ~NBF_RECVLOCK; |
593 | return error; |
594 | } |
595 | |
596 | static void |
597 | smb_nbst_timo(struct smb_vc *vcp) |
598 | { |
599 | |
600 | /* Nothing */ |
601 | } |
602 | |
603 | static void |
604 | smb_nbst_intr(struct smb_vc *vcp) |
605 | { |
606 | struct nbpcb *nbp = vcp->vc_tdata; |
607 | struct socket *so; |
608 | |
609 | if (nbp == NULL || (so = nbp->nbp_tso) == NULL) |
610 | return; |
611 | |
612 | solock(so); |
613 | sorwakeup(so); |
614 | sowwakeup(so); |
615 | sounlock(so); |
616 | } |
617 | |
618 | static int |
619 | smb_nbst_getparam(struct smb_vc *vcp, int param, void *data) |
620 | { |
621 | struct timeval *tvp; |
622 | switch (param) { |
623 | case SMBTP_SNDSZ: |
624 | *(int*)data = nb_tcpsndbuf; |
625 | break; |
626 | case SMBTP_RCVSZ: |
627 | *(int*)data = nb_tcprcvbuf; |
628 | break; |
629 | case SMBTP_TIMEOUT: |
630 | tvp = (struct timeval *)data; |
631 | tvp->tv_sec = NB_RCVTIMEO; |
632 | tvp->tv_usec = 0; |
633 | break; |
634 | default: |
635 | return EINVAL; |
636 | } |
637 | return 0; |
638 | } |
639 | |
640 | static int |
641 | smb_nbst_setparam(struct smb_vc *vcp, int param, void *data) |
642 | { |
643 | struct nbpcb *nbp = vcp->vc_tdata; |
644 | |
645 | switch (param) { |
646 | case SMBTP_SELECTID: |
647 | nbp->nbp_selectid = data; |
648 | break; |
649 | default: |
650 | return EINVAL; |
651 | } |
652 | return 0; |
653 | } |
654 | |
655 | /* |
656 | * Check for fatal errors |
657 | */ |
658 | static int |
659 | smb_nbst_fatal(struct smb_vc *vcp, int error) |
660 | { |
661 | switch (error) { |
662 | case ENOTCONN: |
663 | case ENETRESET: |
664 | case ECONNABORTED: |
665 | return 1; |
666 | } |
667 | return 0; |
668 | } |
669 | |
670 | |
671 | struct smb_tran_desc smb_tran_nbtcp_desc = { |
672 | SMBT_NBTCP, |
673 | smb_nbst_create, smb_nbst_done, |
674 | smb_nbst_bind, smb_nbst_connect, smb_nbst_disconnect, |
675 | smb_nbst_send, smb_nbst_recv, |
676 | smb_nbst_timo, smb_nbst_intr, |
677 | smb_nbst_getparam, smb_nbst_setparam, |
678 | smb_nbst_fatal, |
679 | { NULL, NULL }, |
680 | }; |
681 | |