]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - sys/netsmb/smb_smb.c
Sync with recent KAME.
[FreeBSD/FreeBSD.git] / sys / netsmb / smb_smb.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$
33  */
34 /*
35  * various SMB requests. Most of the routines merely packs data into mbufs.
36  */
37 #include <sys/param.h>
38 #include <sys/systm.h>
39 #include <sys/kernel.h>
40 #include <sys/malloc.h>
41 #include <sys/proc.h>
42 #include <sys/lock.h>
43 #include <sys/sysctl.h>
44 #include <sys/socket.h>
45 #include <sys/uio.h>
46
47 #include <sys/iconv.h>
48
49 #include <netsmb/smb.h>
50 #include <netsmb/smb_subr.h>
51 #include <netsmb/smb_rq.h>
52 #include <netsmb/smb_conn.h>
53 #include <netsmb/smb_tran.h>
54
55 struct smb_dialect {
56         int             d_id;
57         const char *    d_name;
58 };
59
60 static struct smb_dialect smb_dialects[] = {
61         {SMB_DIALECT_CORE,      "PC NETWORK PROGRAM 1.0"},
62         {SMB_DIALECT_COREPLUS,  "MICROSOFT NETWORKS 1.03"},
63         {SMB_DIALECT_LANMAN1_0, "MICROSOFT NETWORKS 3.0"},
64         {SMB_DIALECT_LANMAN1_0, "LANMAN1.0"},
65         {SMB_DIALECT_LANMAN2_0, "LM1.2X002"},
66         {SMB_DIALECT_LANMAN2_0, "Samba"},
67         {SMB_DIALECT_NTLM0_12,  "NT LANMAN 1.0"},
68         {SMB_DIALECT_NTLM0_12,  "NT LM 0.12"},
69         {-1,                    NULL}
70 };
71
72 #define SMB_DIALECT_MAX (sizeof(smb_dialects) / sizeof(struct smb_dialect) - 2)
73
74 static int
75 smb_smb_nomux(struct smb_vc *vcp, struct smb_cred *scred, const char *name)
76 {
77         if (scred->scr_p == vcp->vc_iod->iod_p)
78                 return 0;
79         SMBERROR("wrong function called(%s)\n", name);
80         return EINVAL;
81 }
82
83 int
84 smb_smb_negotiate(struct smb_vc *vcp, struct smb_cred *scred)
85 {
86         struct smb_dialect *dp;
87         struct smb_sopt *sp = NULL;
88         struct smb_rq *rqp;
89         struct mbchain *mbp;
90         struct mdchain *mdp;
91         u_int8_t wc, stime[8], sblen;
92         u_int16_t dindex, tw, tw1, swlen, bc;
93         int error, maxqsz;
94
95         if (smb_smb_nomux(vcp, scred, __FUNCTION__) != 0)
96                 return EINVAL;
97         vcp->vc_hflags = 0;
98         vcp->vc_hflags2 = 0;
99         vcp->obj.co_flags &= ~(SMBV_ENCRYPT);
100         sp = &vcp->vc_sopt;
101         bzero(sp, sizeof(struct smb_sopt));
102         error = smb_rq_alloc(VCTOCP(vcp), SMB_COM_NEGOTIATE, scred, &rqp);
103         if (error)
104                 return error;
105         smb_rq_getrequest(rqp, &mbp);
106         smb_rq_wstart(rqp);
107         smb_rq_wend(rqp);
108         smb_rq_bstart(rqp);
109         for(dp = smb_dialects; dp->d_id != -1; dp++) {
110                 mb_put_uint8(mbp, SMB_DT_DIALECT);
111                 smb_put_dstring(mbp, vcp, dp->d_name, SMB_CS_NONE);
112         }
113         smb_rq_bend(rqp);
114         error = smb_rq_simple(rqp);
115         SMBSDEBUG("%d\n", error);
116         if (error)
117                 goto bad;
118         smb_rq_getreply(rqp, &mdp);
119         do {
120                 error = md_get_uint8(mdp, &wc);
121                 if (error)
122                         break;
123                 error = md_get_uint16le(mdp, &dindex);
124                 if (error)
125                         break;
126                 if (dindex > 7) {
127                         SMBERROR("Don't know how to talk with server %s (%d)\n", "xxx", dindex);
128                         error = EBADRPC;
129                         break;
130                 }
131                 dp = smb_dialects + dindex;
132                 sp->sv_proto = dp->d_id;
133                 SMBSDEBUG("Dialect %s (%d, %d)\n", dp->d_name, dindex, wc);
134                 error = EBADRPC;
135                 if (dp->d_id >= SMB_DIALECT_NTLM0_12) {
136                         if (wc != 17)
137                                 break;
138                         md_get_uint8(mdp, &sp->sv_sm);
139                         md_get_uint16le(mdp, &sp->sv_maxmux);
140                         md_get_uint16le(mdp, &sp->sv_maxvcs);
141                         md_get_uint32le(mdp, &sp->sv_maxtx);
142                         md_get_uint32le(mdp, &sp->sv_maxraw);
143                         md_get_uint32le(mdp, &sp->sv_skey);
144                         md_get_uint32le(mdp, &sp->sv_caps);
145                         md_get_mem(mdp, stime, 8, MB_MSYSTEM);
146                         md_get_uint16le(mdp, (u_int16_t*)&sp->sv_tz);
147                         md_get_uint8(mdp, &sblen);
148                         if (sblen && (sp->sv_sm & SMB_SM_ENCRYPT)) {
149                                 if (sblen != SMB_MAXCHALLENGELEN) {
150                                         SMBERROR("Unexpected length of security blob (%d)\n", sblen);
151                                         break;
152                                 }
153                                 error = md_get_uint16(mdp, &bc);
154                                 if (error)
155                                         break;
156                                 if (sp->sv_caps & SMB_CAP_EXT_SECURITY)
157                                         md_get_mem(mdp, NULL, 16, MB_MSYSTEM);
158                                 error = md_get_mem(mdp, vcp->vc_ch, sblen, MB_MSYSTEM);
159                                 if (error)
160                                         break;
161                                 vcp->vc_chlen = sblen;
162                                 vcp->obj.co_flags |= SMBV_ENCRYPT;
163                         }
164                         vcp->vc_hflags2 |= SMB_FLAGS2_KNOWS_LONG_NAMES;
165                         if (dp->d_id == SMB_DIALECT_NTLM0_12 &&
166                             sp->sv_maxtx < 4096 &&
167                             (sp->sv_caps & SMB_CAP_NT_SMBS) == 0) {
168                                 vcp->obj.co_flags |= SMBV_WIN95;
169                                 SMBSDEBUG("Win95 detected\n");
170                         }
171                 } else if (dp->d_id > SMB_DIALECT_CORE) {
172                         md_get_uint16le(mdp, &tw);
173                         sp->sv_sm = tw;
174                         md_get_uint16le(mdp, &tw);
175                         sp->sv_maxtx = tw;
176                         md_get_uint16le(mdp, &sp->sv_maxmux);
177                         md_get_uint16le(mdp, &sp->sv_maxvcs);
178                         md_get_uint16le(mdp, &tw);      /* rawmode */
179                         md_get_uint32le(mdp, &sp->sv_skey);
180                         if (wc == 13) {         /* >= LANMAN1 */
181                                 md_get_uint16(mdp, &tw);                /* time */
182                                 md_get_uint16(mdp, &tw1);               /* date */
183                                 md_get_uint16le(mdp, (u_int16_t*)&sp->sv_tz);
184                                 md_get_uint16le(mdp, &swlen);
185                                 if (swlen > SMB_MAXCHALLENGELEN)
186                                         break;
187                                 md_get_uint16(mdp, NULL);       /* mbz */
188                                 if (md_get_uint16(mdp, &bc) != 0)
189                                         break;
190                                 if (bc < swlen)
191                                         break;
192                                 if (swlen && (sp->sv_sm & SMB_SM_ENCRYPT)) {
193                                         error = md_get_mem(mdp, vcp->vc_ch, swlen, MB_MSYSTEM);
194                                         if (error)
195                                                 break;
196                                         vcp->vc_chlen = swlen;
197                                         vcp->obj.co_flags |= SMBV_ENCRYPT;
198                                 }
199                         }
200                         vcp->vc_hflags2 |= SMB_FLAGS2_KNOWS_LONG_NAMES;
201                 } else {        /* an old CORE protocol */
202                         sp->sv_maxmux = 1;
203                 }
204                 error = 0;
205         } while (0);
206         if (error == 0) {
207                 vcp->vc_maxvcs = sp->sv_maxvcs;
208                 if (vcp->vc_maxvcs <= 1) {
209                         if (vcp->vc_maxvcs == 0)
210                                 vcp->vc_maxvcs = 1;
211                 }
212                 if (sp->sv_maxtx <= 0 || sp->sv_maxtx > 0xffff)
213                         sp->sv_maxtx = 1024;
214                 SMB_TRAN_GETPARAM(vcp, SMBTP_SNDSZ, &maxqsz);
215                 vcp->vc_txmax = min(sp->sv_maxtx, maxqsz);
216                 SMBSDEBUG("TZ = %d\n", sp->sv_tz);
217                 SMBSDEBUG("CAPS = %x\n", sp->sv_caps);
218                 SMBSDEBUG("MAXMUX = %d\n", sp->sv_maxmux);
219                 SMBSDEBUG("MAXVCS = %d\n", sp->sv_maxvcs);
220                 SMBSDEBUG("MAXRAW = %d\n", sp->sv_maxraw);
221                 SMBSDEBUG("MAXTX = %d\n", sp->sv_maxtx);
222         }
223 bad:
224         smb_rq_done(rqp);
225         return error;
226 }
227
228 int
229 smb_smb_ssnsetup(struct smb_vc *vcp, struct smb_cred *scred)
230 {
231         struct smb_rq *rqp;
232         struct mbchain *mbp;
233 /*      u_int8_t wc;
234         u_int16_t tw, tw1;*/
235         smb_uniptr unipp, ntencpass = NULL;
236         char *pp, *up, *pbuf, *encpass;
237         int error, plen, uniplen, ulen;
238
239         vcp->vc_smbuid = SMB_UID_UNKNOWN;
240
241         if (smb_smb_nomux(vcp, scred, __FUNCTION__) != 0)
242                 return EINVAL;
243
244         error = smb_rq_alloc(VCTOCP(vcp), SMB_COM_SESSION_SETUP_ANDX, scred, &rqp);
245         if (error)
246                 return error;
247         pbuf = malloc(SMB_MAXPASSWORDLEN + 1, M_SMBTEMP, M_WAITOK);
248         encpass = malloc(24, M_SMBTEMP, M_WAITOK);
249         if (vcp->vc_sopt.sv_sm & SMB_SM_USER) {
250                 iconv_convstr(vcp->vc_toupper, pbuf, smb_vc_getpass(vcp));
251                 iconv_convstr(vcp->vc_toserver, pbuf, pbuf);
252                 if (vcp->vc_sopt.sv_sm & SMB_SM_ENCRYPT) {
253                         uniplen = plen = 24;
254                         smb_encrypt(pbuf, vcp->vc_ch, encpass);
255                         ntencpass = malloc(uniplen, M_SMBTEMP, M_WAITOK);
256                         iconv_convstr(vcp->vc_toserver, pbuf, smb_vc_getpass(vcp));
257                         smb_ntencrypt(pbuf, vcp->vc_ch, (u_char*)ntencpass);
258                         pp = encpass;
259                         unipp = ntencpass;
260                 } else {
261                         plen = strlen(pbuf) + 1;
262                         pp = pbuf;
263                         uniplen = plen * 2;
264                         ntencpass = malloc(uniplen, M_SMBTEMP, M_WAITOK);
265                         smb_strtouni(ntencpass, smb_vc_getpass(vcp));
266                         plen--;
267                         uniplen = 0/*-= 2*/;
268                         unipp = ntencpass;
269                 }
270         } else {
271                 /*
272                  * In the share security mode password will be used
273                  * only in the tree authentication
274                  */
275                  pp = "";
276                  plen = 1;
277                  unipp = &smb_unieol;
278                  uniplen = sizeof(smb_unieol);
279         }
280         smb_rq_wstart(rqp);
281         mbp = &rqp->sr_rq;
282         up = vcp->vc_username;
283         ulen = strlen(up) + 1;
284         mb_put_uint8(mbp, 0xff);
285         mb_put_uint8(mbp, 0);
286         mb_put_uint16le(mbp, 0);
287         mb_put_uint16le(mbp, vcp->vc_sopt.sv_maxtx);
288         mb_put_uint16le(mbp, vcp->vc_sopt.sv_maxmux);
289         mb_put_uint16le(mbp, vcp->vc_number);
290         mb_put_uint32le(mbp, vcp->vc_sopt.sv_skey);
291         mb_put_uint16le(mbp, plen);
292         if (SMB_DIALECT(vcp) < SMB_DIALECT_NTLM0_12) {
293                 mb_put_uint32le(mbp, 0);
294                 smb_rq_wend(rqp);
295                 smb_rq_bstart(rqp);
296                 mb_put_mem(mbp, pp, plen, MB_MSYSTEM);
297                 smb_put_dstring(mbp, vcp, up, SMB_CS_NONE);
298         } else {
299                 mb_put_uint16le(mbp, uniplen);
300                 mb_put_uint32le(mbp, 0);                /* reserved */
301                 mb_put_uint32le(mbp, 0);                /* my caps */
302                 smb_rq_wend(rqp);
303                 smb_rq_bstart(rqp);
304                 mb_put_mem(mbp, pp, plen, MB_MSYSTEM);
305                 mb_put_mem(mbp, (caddr_t)unipp, uniplen, MB_MSYSTEM);
306                 smb_put_dstring(mbp, vcp, up, SMB_CS_NONE);             /* AccountName */
307                 smb_put_dstring(mbp, vcp, vcp->vc_domain, SMB_CS_NONE); /* PrimaryDomain */
308                 smb_put_dstring(mbp, vcp, "FreeBSD", SMB_CS_NONE);      /* Client's OS */
309                 smb_put_dstring(mbp, vcp, "NETSMB", SMB_CS_NONE);               /* Client name */
310         }
311         smb_rq_bend(rqp);
312         if (ntencpass)
313                 free(ntencpass, M_SMBTEMP);
314         error = smb_rq_simple(rqp);
315         SMBSDEBUG("%d\n", error);
316         if (error) {
317                 if (rqp->sr_errclass == ERRDOS && rqp->sr_serror == ERRnoaccess)
318                         error = EAUTH;
319                 goto bad;
320         }
321         vcp->vc_smbuid = rqp->sr_rpuid;
322 bad:
323         free(encpass, M_SMBTEMP);
324         free(pbuf, M_SMBTEMP);
325         smb_rq_done(rqp);
326         return error;
327 }
328
329 int
330 smb_smb_ssnclose(struct smb_vc *vcp, struct smb_cred *scred)
331 {
332         struct smb_rq *rqp;
333         struct mbchain *mbp;
334         int error;
335
336         if (vcp->vc_smbuid == SMB_UID_UNKNOWN)
337                 return 0;
338
339         if (smb_smb_nomux(vcp, scred, __FUNCTION__) != 0)
340                 return EINVAL;
341
342         error = smb_rq_alloc(VCTOCP(vcp), SMB_COM_LOGOFF_ANDX, scred, &rqp);
343         if (error)
344                 return error;
345         mbp = &rqp->sr_rq;
346         smb_rq_wstart(rqp);
347         mb_put_uint8(mbp, 0xff);
348         mb_put_uint8(mbp, 0);
349         mb_put_uint16le(mbp, 0);
350         smb_rq_wend(rqp);
351         smb_rq_bstart(rqp);
352         smb_rq_bend(rqp);
353         error = smb_rq_simple(rqp);
354         SMBSDEBUG("%d\n", error);
355         smb_rq_done(rqp);
356         return error;
357 }
358
359 static char smb_any_share[] = "?????";
360
361 static char *
362 smb_share_typename(int stype)
363 {
364         char *pp;
365
366         switch (stype) {
367             case SMB_ST_DISK:
368                 pp = "A:";
369                 break;
370             case SMB_ST_PRINTER:
371                 pp = smb_any_share;             /* can't use LPT: here... */
372                 break;
373             case SMB_ST_PIPE:
374                 pp = "IPC";
375                 break;
376             case SMB_ST_COMM:
377                 pp = "COMM";
378                 break;
379             case SMB_ST_ANY:
380             default:
381                 pp = smb_any_share;
382                 break;
383         }
384         return pp;
385 }
386
387 int
388 smb_smb_treeconnect(struct smb_share *ssp, struct smb_cred *scred)
389 {
390         struct smb_vc *vcp;
391         struct smb_rq rq, *rqp = &rq;
392         struct mbchain *mbp;
393         char *pp, *pbuf, *encpass;
394         int error, plen, caseopt;
395
396         ssp->ss_tid = SMB_TID_UNKNOWN;
397         error = smb_rq_alloc(SSTOCP(ssp), SMB_COM_TREE_CONNECT_ANDX, scred, &rqp);
398         if (error)
399                 return error;
400         vcp = rqp->sr_vc;
401         caseopt = SMB_CS_NONE;
402         if (vcp->vc_sopt.sv_sm & SMB_SM_USER) {
403                 plen = 1;
404                 pp = "";
405                 pbuf = NULL;
406                 encpass = NULL;
407         } else {
408                 pbuf = malloc(SMB_MAXPASSWORDLEN + 1, M_SMBTEMP, M_WAITOK);
409                 encpass = malloc(24, M_SMBTEMP, M_WAITOK);
410                 iconv_convstr(vcp->vc_toupper, pbuf, smb_share_getpass(ssp));
411                 iconv_convstr(vcp->vc_toserver, pbuf, pbuf);
412                 if (vcp->vc_sopt.sv_sm & SMB_SM_ENCRYPT) {
413                         plen = 24;
414                         smb_encrypt(pbuf, vcp->vc_ch, encpass);
415                         pp = encpass;
416                 } else {
417                         plen = strlen(pbuf) + 1;
418                         pp = pbuf;
419                 }
420         }
421         mbp = &rqp->sr_rq;
422         smb_rq_wstart(rqp);
423         mb_put_uint8(mbp, 0xff);
424         mb_put_uint8(mbp, 0);
425         mb_put_uint16le(mbp, 0);
426         mb_put_uint16le(mbp, 0);                /* Flags */
427         mb_put_uint16le(mbp, plen);
428         smb_rq_wend(rqp);
429         smb_rq_bstart(rqp);
430         mb_put_mem(mbp, pp, plen, MB_MSYSTEM);
431         smb_put_dmem(mbp, vcp, "\\\\", 2, caseopt);
432         pp = vcp->vc_srvname;
433         smb_put_dmem(mbp, vcp, pp, strlen(pp), caseopt);
434         smb_put_dmem(mbp, vcp, "\\", 1, caseopt);
435         pp = ssp->ss_name;
436         smb_put_dstring(mbp, vcp, pp, caseopt);
437         pp = smb_share_typename(ssp->ss_type);
438         smb_put_dstring(mbp, vcp, pp, caseopt);
439         smb_rq_bend(rqp);
440         error = smb_rq_simple(rqp);
441         SMBSDEBUG("%d\n", error);
442         if (error)
443                 goto bad;
444         ssp->ss_tid = rqp->sr_rptid;
445         ssp->ss_vcgenid = vcp->vc_genid;
446         ssp->ss_flags |= SMBS_CONNECTED;
447 bad:
448         if (encpass)
449                 free(encpass, M_SMBTEMP);
450         if (pbuf)
451                 free(pbuf, M_SMBTEMP);
452         smb_rq_done(rqp);
453         return error;
454 }
455
456 int
457 smb_smb_treedisconnect(struct smb_share *ssp, struct smb_cred *scred)
458 {
459         struct smb_rq *rqp;
460         struct mbchain *mbp;
461         int error;
462
463         if (ssp->ss_tid == SMB_TID_UNKNOWN)
464                 return 0;
465         error = smb_rq_alloc(SSTOCP(ssp), SMB_COM_TREE_DISCONNECT, scred, &rqp);
466         if (error)
467                 return error;
468         mbp = &rqp->sr_rq;
469         smb_rq_wstart(rqp);
470         smb_rq_wend(rqp);
471         smb_rq_bstart(rqp);
472         smb_rq_bend(rqp);
473         error = smb_rq_simple(rqp);
474         SMBSDEBUG("%d\n", error);
475         smb_rq_done(rqp);
476         ssp->ss_tid = SMB_TID_UNKNOWN;
477         return error;
478 }
479
480 static __inline int
481 smb_smb_read(struct smb_share *ssp, u_int16_t fid,
482         int *len, int *rresid, struct uio *uio, struct smb_cred *scred)
483 {
484         struct smb_rq *rqp;
485         struct mbchain *mbp;
486         struct mdchain *mdp;
487         u_int16_t resid, bc;
488         u_int8_t wc;
489         int error, rlen, blksz;
490
491         error = smb_rq_alloc(SSTOCP(ssp), SMB_COM_READ, scred, &rqp);
492         if (error)
493                 return error;
494
495         blksz = SSTOVC(ssp)->vc_txmax - SMB_HDRLEN - 16;
496         rlen = *len = min(blksz, *len);
497
498         smb_rq_getrequest(rqp, &mbp);
499         smb_rq_wstart(rqp);
500         mb_put_mem(mbp, (caddr_t)&fid, sizeof(fid), MB_MSYSTEM);
501         mb_put_uint16le(mbp, rlen);
502         mb_put_uint32le(mbp, uio->uio_offset);
503         mb_put_uint16le(mbp, min(uio->uio_resid, 0xffff));
504         smb_rq_wend(rqp);
505         smb_rq_bstart(rqp);
506         smb_rq_bend(rqp);
507         do {
508                 error = smb_rq_simple(rqp);
509                 if (error)
510                         break;
511                 smb_rq_getreply(rqp, &mdp);
512                 md_get_uint8(mdp, &wc);
513                 if (wc != 5) {
514                         error = EBADRPC;
515                         break;
516                 }
517                 md_get_uint16le(mdp, &resid);
518                 md_get_mem(mdp, NULL, 4 * 2, MB_MSYSTEM);
519                 md_get_uint16le(mdp, &bc);
520                 md_get_uint8(mdp, NULL);                /* ignore buffer type */
521                 md_get_uint16le(mdp, &resid);
522                 if (resid == 0) {
523                         *rresid = resid;
524                         break;
525                 }
526                 error = md_get_uio(mdp, uio, resid);
527                 if (error)
528                         break;
529                 *rresid = resid;
530         } while(0);
531         smb_rq_done(rqp);
532         return error;
533 }
534
535 int
536 smb_read(struct smb_share *ssp, u_int16_t fid, struct uio *uio,
537         struct smb_cred *scred)
538 {
539         int tsize, len, resid;
540         int error = 0;
541
542         tsize = uio->uio_resid;
543         while (tsize > 0) {
544                 len = tsize;
545                 error = smb_smb_read(ssp, fid, &len, &resid, uio, scred);
546                 if (error)
547                         break;
548                 tsize -= resid;
549                 if (resid < len)
550                         break;
551         }
552         return error;
553 }
554
555 static __inline int
556 smb_smb_write(struct smb_share *ssp, u_int16_t fid, int *len, int *rresid,
557         struct uio *uio, struct smb_cred *scred)
558 {
559         struct smb_rq *rqp;
560         struct mbchain *mbp;
561         struct mdchain *mdp;
562         u_int16_t resid;
563         u_int8_t wc;
564         int error, blksz;
565
566         blksz = SSTOVC(ssp)->vc_txmax - SMB_HDRLEN - 16;
567         if (blksz > 0xffff)
568                 blksz = 0xffff;
569
570         resid = *len = min(blksz, *len);
571
572         error = smb_rq_alloc(SSTOCP(ssp), SMB_COM_WRITE, scred, &rqp);
573         if (error)
574                 return error;
575         smb_rq_getrequest(rqp, &mbp);
576         smb_rq_wstart(rqp);
577         mb_put_mem(mbp, (caddr_t)&fid, sizeof(fid), MB_MSYSTEM);
578         mb_put_uint16le(mbp, resid);
579         mb_put_uint32le(mbp, uio->uio_offset);
580         mb_put_uint16le(mbp, min(uio->uio_resid, 0xffff));
581         smb_rq_wend(rqp);
582         smb_rq_bstart(rqp);
583         mb_put_uint8(mbp, SMB_DT_DATA);
584         mb_put_uint16le(mbp, resid);
585         do {
586                 error = mb_put_uio(mbp, uio, resid);
587                 if (error)
588                         break;
589                 smb_rq_bend(rqp);
590                 error = smb_rq_simple(rqp);
591                 if (error)
592                         break;
593                 smb_rq_getreply(rqp, &mdp);
594                 md_get_uint8(mdp, &wc);
595                 if (wc != 1) {
596                         error = EBADRPC;
597                         break;
598                 }
599                 md_get_uint16le(mdp, &resid);
600                 *rresid = resid;
601         } while(0);
602         smb_rq_done(rqp);
603         return error;
604 }
605
606 int
607 smb_write(struct smb_share *ssp, u_int16_t fid, struct uio *uio,
608         struct smb_cred *scred)
609 {
610         int error = 0, len, tsize, resid;
611         struct uio olduio;
612
613         /*
614          * review: manage iov more precisely
615          */
616         if (uio->uio_iovcnt != 1) {
617                 SMBERROR("can't handle iovcnt > 1\n");
618                 return EIO;
619         }
620         tsize = uio->uio_resid;
621         olduio = *uio;
622         while (tsize > 0) {
623                 len = tsize;
624                 error = smb_smb_write(ssp, fid, &len, &resid, uio, scred);
625                 if (error)
626                         break;
627                 if (resid < len) {
628                         error = EIO;
629                         break;
630                 }
631                 tsize -= resid;
632         }
633         if (error) {
634                 *uio = olduio;
635         }
636         return error;
637 }
638
639 int
640 smb_smb_echo(struct smb_vc *vcp, struct smb_cred *scred)
641 {
642         struct smb_rq *rqp;
643         struct mbchain *mbp;
644         int error;
645
646         error = smb_rq_alloc(VCTOCP(vcp), SMB_COM_ECHO, scred, &rqp);
647         if (error)
648                 return error;
649         mbp = &rqp->sr_rq;
650         smb_rq_wstart(rqp);
651         mb_put_uint16le(mbp, 1);
652         smb_rq_wend(rqp);
653         smb_rq_bstart(rqp);
654         mb_put_uint32le(mbp, 0);
655         smb_rq_bend(rqp);
656         error = smb_rq_simple(rqp);
657         SMBSDEBUG("%d\n", error);
658         smb_rq_done(rqp);
659         return error;
660 }