smb_smb.c revision 91022
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: head/sys/netsmb/smb_smb.c 91022 2002-02-21 16:13:19Z bp $
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
55struct smb_dialect {
56	int		d_id;
57	const char *	d_name;
58};
59
60static 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
74static int
75smb_smb_nomux(struct smb_vc *vcp, struct smb_cred *scred, const char *name)
76{
77	if (scred->scr_td->td_proc == vcp->vc_iod->iod_p)
78		return 0;
79	SMBERROR("wrong function called(%s)\n", name);
80	return EINVAL;
81}
82
83int
84smb_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, __func__) != 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	}
223bad:
224	smb_rq_done(rqp);
225	return error;
226}
227
228int
229smb_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, __func__) != 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
268			/*
269			 * The uniplen is zeroed because Samba cannot deal
270			 * with this 2nd cleartext password.  This Samba
271			 * "bug" is actually a workaround for problems in
272			 * Microsoft clients.
273			 */
274			uniplen = 0/*-= 2*/;
275			unipp = ntencpass;
276		}
277	} else {
278		/*
279		 * In the share security mode password will be used
280		 * only in the tree authentication
281		 */
282		 pp = "";
283		 plen = 1;
284		 unipp = &smb_unieol;
285		 uniplen = sizeof(smb_unieol);
286	}
287	smb_rq_wstart(rqp);
288	mbp = &rqp->sr_rq;
289	up = vcp->vc_username;
290	ulen = strlen(up) + 1;
291	mb_put_uint8(mbp, 0xff);
292	mb_put_uint8(mbp, 0);
293	mb_put_uint16le(mbp, 0);
294	mb_put_uint16le(mbp, vcp->vc_sopt.sv_maxtx);
295	mb_put_uint16le(mbp, vcp->vc_sopt.sv_maxmux);
296	mb_put_uint16le(mbp, vcp->vc_number);
297	mb_put_uint32le(mbp, vcp->vc_sopt.sv_skey);
298	mb_put_uint16le(mbp, plen);
299	if (SMB_DIALECT(vcp) < SMB_DIALECT_NTLM0_12) {
300		mb_put_uint32le(mbp, 0);
301		smb_rq_wend(rqp);
302		smb_rq_bstart(rqp);
303		mb_put_mem(mbp, pp, plen, MB_MSYSTEM);
304		smb_put_dstring(mbp, vcp, up, SMB_CS_NONE);
305	} else {
306		mb_put_uint16le(mbp, uniplen);
307		mb_put_uint32le(mbp, 0);		/* reserved */
308		mb_put_uint32le(mbp, vcp->obj.co_flags & SMBV_UNICODE ?
309				     SMB_CAP_UNICODE : 0);
310		smb_rq_wend(rqp);
311		smb_rq_bstart(rqp);
312		mb_put_mem(mbp, pp, plen, MB_MSYSTEM);
313		mb_put_mem(mbp, (caddr_t)unipp, uniplen, MB_MSYSTEM);
314		smb_put_dstring(mbp, vcp, up, SMB_CS_NONE);		/* AccountName */
315		smb_put_dstring(mbp, vcp, vcp->vc_domain, SMB_CS_NONE);	/* PrimaryDomain */
316		smb_put_dstring(mbp, vcp, "FreeBSD", SMB_CS_NONE);	/* Client's OS */
317		smb_put_dstring(mbp, vcp, "NETSMB", SMB_CS_NONE);		/* Client name */
318	}
319	smb_rq_bend(rqp);
320	if (ntencpass)
321		free(ntencpass, M_SMBTEMP);
322	error = smb_rq_simple(rqp);
323	SMBSDEBUG("%d\n", error);
324	if (error) {
325		if (rqp->sr_errclass == ERRDOS && rqp->sr_serror == ERRnoaccess)
326			error = EAUTH;
327		goto bad;
328	}
329	vcp->vc_smbuid = rqp->sr_rpuid;
330bad:
331	free(encpass, M_SMBTEMP);
332	free(pbuf, M_SMBTEMP);
333	smb_rq_done(rqp);
334	return error;
335}
336
337int
338smb_smb_ssnclose(struct smb_vc *vcp, struct smb_cred *scred)
339{
340	struct smb_rq *rqp;
341	struct mbchain *mbp;
342	int error;
343
344	if (vcp->vc_smbuid == SMB_UID_UNKNOWN)
345		return 0;
346
347	if (smb_smb_nomux(vcp, scred, __func__) != 0)
348		return EINVAL;
349
350	error = smb_rq_alloc(VCTOCP(vcp), SMB_COM_LOGOFF_ANDX, scred, &rqp);
351	if (error)
352		return error;
353	mbp = &rqp->sr_rq;
354	smb_rq_wstart(rqp);
355	mb_put_uint8(mbp, 0xff);
356	mb_put_uint8(mbp, 0);
357	mb_put_uint16le(mbp, 0);
358	smb_rq_wend(rqp);
359	smb_rq_bstart(rqp);
360	smb_rq_bend(rqp);
361	error = smb_rq_simple(rqp);
362	SMBSDEBUG("%d\n", error);
363	smb_rq_done(rqp);
364	return error;
365}
366
367static char smb_any_share[] = "?????";
368
369static char *
370smb_share_typename(int stype)
371{
372	char *pp;
373
374	switch (stype) {
375	    case SMB_ST_DISK:
376		pp = "A:";
377		break;
378	    case SMB_ST_PRINTER:
379		pp = smb_any_share;		/* can't use LPT: here... */
380		break;
381	    case SMB_ST_PIPE:
382		pp = "IPC";
383		break;
384	    case SMB_ST_COMM:
385		pp = "COMM";
386		break;
387	    case SMB_ST_ANY:
388	    default:
389		pp = smb_any_share;
390		break;
391	}
392	return pp;
393}
394
395int
396smb_smb_treeconnect(struct smb_share *ssp, struct smb_cred *scred)
397{
398	struct smb_vc *vcp;
399	struct smb_rq rq, *rqp = &rq;
400	struct mbchain *mbp;
401	char *pp, *pbuf, *encpass;
402	int error, plen, caseopt;
403
404	ssp->ss_tid = SMB_TID_UNKNOWN;
405	error = smb_rq_alloc(SSTOCP(ssp), SMB_COM_TREE_CONNECT_ANDX, scred, &rqp);
406	if (error)
407		return error;
408	vcp = rqp->sr_vc;
409	caseopt = SMB_CS_NONE;
410	if (vcp->vc_sopt.sv_sm & SMB_SM_USER) {
411		plen = 1;
412		pp = "";
413		pbuf = NULL;
414		encpass = NULL;
415	} else {
416		pbuf = malloc(SMB_MAXPASSWORDLEN + 1, M_SMBTEMP, M_WAITOK);
417		encpass = malloc(24, M_SMBTEMP, M_WAITOK);
418		iconv_convstr(vcp->vc_toupper, pbuf, smb_share_getpass(ssp));
419		iconv_convstr(vcp->vc_toserver, pbuf, pbuf);
420		if (vcp->vc_sopt.sv_sm & SMB_SM_ENCRYPT) {
421			plen = 24;
422			smb_encrypt(pbuf, vcp->vc_ch, encpass);
423			pp = encpass;
424		} else {
425			plen = strlen(pbuf) + 1;
426			pp = pbuf;
427		}
428	}
429	mbp = &rqp->sr_rq;
430	smb_rq_wstart(rqp);
431	mb_put_uint8(mbp, 0xff);
432	mb_put_uint8(mbp, 0);
433	mb_put_uint16le(mbp, 0);
434	mb_put_uint16le(mbp, 0);		/* Flags */
435	mb_put_uint16le(mbp, plen);
436	smb_rq_wend(rqp);
437	smb_rq_bstart(rqp);
438	mb_put_mem(mbp, pp, plen, MB_MSYSTEM);
439	smb_put_dmem(mbp, vcp, "\\\\", 2, caseopt);
440	pp = vcp->vc_srvname;
441	smb_put_dmem(mbp, vcp, pp, strlen(pp), caseopt);
442	smb_put_dmem(mbp, vcp, "\\", 1, caseopt);
443	pp = ssp->ss_name;
444	smb_put_dstring(mbp, vcp, pp, caseopt);
445	pp = smb_share_typename(ssp->ss_type);
446	smb_put_dstring(mbp, vcp, pp, caseopt);
447	smb_rq_bend(rqp);
448	error = smb_rq_simple(rqp);
449	SMBSDEBUG("%d\n", error);
450	if (error)
451		goto bad;
452	ssp->ss_tid = rqp->sr_rptid;
453	ssp->ss_vcgenid = vcp->vc_genid;
454	ssp->ss_flags |= SMBS_CONNECTED;
455bad:
456	if (encpass)
457		free(encpass, M_SMBTEMP);
458	if (pbuf)
459		free(pbuf, M_SMBTEMP);
460	smb_rq_done(rqp);
461	return error;
462}
463
464int
465smb_smb_treedisconnect(struct smb_share *ssp, struct smb_cred *scred)
466{
467	struct smb_rq *rqp;
468	struct mbchain *mbp;
469	int error;
470
471	if (ssp->ss_tid == SMB_TID_UNKNOWN)
472		return 0;
473	error = smb_rq_alloc(SSTOCP(ssp), SMB_COM_TREE_DISCONNECT, scred, &rqp);
474	if (error)
475		return error;
476	mbp = &rqp->sr_rq;
477	smb_rq_wstart(rqp);
478	smb_rq_wend(rqp);
479	smb_rq_bstart(rqp);
480	smb_rq_bend(rqp);
481	error = smb_rq_simple(rqp);
482	SMBSDEBUG("%d\n", error);
483	smb_rq_done(rqp);
484	ssp->ss_tid = SMB_TID_UNKNOWN;
485	return error;
486}
487
488static __inline int
489smb_smb_read(struct smb_share *ssp, u_int16_t fid,
490	int *len, int *rresid, struct uio *uio, struct smb_cred *scred)
491{
492	struct smb_rq *rqp;
493	struct mbchain *mbp;
494	struct mdchain *mdp;
495	u_int16_t resid, bc;
496	u_int8_t wc;
497	int error, rlen, blksz;
498
499	error = smb_rq_alloc(SSTOCP(ssp), SMB_COM_READ, scred, &rqp);
500	if (error)
501		return error;
502
503	blksz = SSTOVC(ssp)->vc_txmax - SMB_HDRLEN - 16;
504	rlen = *len = min(blksz, *len);
505
506	smb_rq_getrequest(rqp, &mbp);
507	smb_rq_wstart(rqp);
508	mb_put_mem(mbp, (caddr_t)&fid, sizeof(fid), MB_MSYSTEM);
509	mb_put_uint16le(mbp, rlen);
510	mb_put_uint32le(mbp, uio->uio_offset);
511	mb_put_uint16le(mbp, min(uio->uio_resid, 0xffff));
512	smb_rq_wend(rqp);
513	smb_rq_bstart(rqp);
514	smb_rq_bend(rqp);
515	do {
516		error = smb_rq_simple(rqp);
517		if (error)
518			break;
519		smb_rq_getreply(rqp, &mdp);
520		md_get_uint8(mdp, &wc);
521		if (wc != 5) {
522			error = EBADRPC;
523			break;
524		}
525		md_get_uint16le(mdp, &resid);
526		md_get_mem(mdp, NULL, 4 * 2, MB_MSYSTEM);
527		md_get_uint16le(mdp, &bc);
528		md_get_uint8(mdp, NULL);		/* ignore buffer type */
529		md_get_uint16le(mdp, &resid);
530		if (resid == 0) {
531			*rresid = resid;
532			break;
533		}
534		error = md_get_uio(mdp, uio, resid);
535		if (error)
536			break;
537		*rresid = resid;
538	} while(0);
539	smb_rq_done(rqp);
540	return error;
541}
542
543int
544smb_read(struct smb_share *ssp, u_int16_t fid, struct uio *uio,
545	struct smb_cred *scred)
546{
547	int tsize, len, resid;
548	int error = 0;
549
550	tsize = uio->uio_resid;
551	while (tsize > 0) {
552		len = tsize;
553		error = smb_smb_read(ssp, fid, &len, &resid, uio, scred);
554		if (error)
555			break;
556		tsize -= resid;
557		if (resid < len)
558			break;
559	}
560	return error;
561}
562
563static __inline int
564smb_smb_write(struct smb_share *ssp, u_int16_t fid, int *len, int *rresid,
565	struct uio *uio, struct smb_cred *scred)
566{
567	struct smb_rq *rqp;
568	struct mbchain *mbp;
569	struct mdchain *mdp;
570	u_int16_t resid;
571	u_int8_t wc;
572	int error, blksz;
573
574	blksz = SSTOVC(ssp)->vc_txmax - SMB_HDRLEN - 16;
575	if (blksz > 0xffff)
576		blksz = 0xffff;
577
578	resid = *len = min(blksz, *len);
579
580	error = smb_rq_alloc(SSTOCP(ssp), SMB_COM_WRITE, scred, &rqp);
581	if (error)
582		return error;
583	smb_rq_getrequest(rqp, &mbp);
584	smb_rq_wstart(rqp);
585	mb_put_mem(mbp, (caddr_t)&fid, sizeof(fid), MB_MSYSTEM);
586	mb_put_uint16le(mbp, resid);
587	mb_put_uint32le(mbp, uio->uio_offset);
588	mb_put_uint16le(mbp, min(uio->uio_resid, 0xffff));
589	smb_rq_wend(rqp);
590	smb_rq_bstart(rqp);
591	mb_put_uint8(mbp, SMB_DT_DATA);
592	mb_put_uint16le(mbp, resid);
593	do {
594		error = mb_put_uio(mbp, uio, resid);
595		if (error)
596			break;
597		smb_rq_bend(rqp);
598		error = smb_rq_simple(rqp);
599		if (error)
600			break;
601		smb_rq_getreply(rqp, &mdp);
602		md_get_uint8(mdp, &wc);
603		if (wc != 1) {
604			error = EBADRPC;
605			break;
606		}
607		md_get_uint16le(mdp, &resid);
608		*rresid = resid;
609	} while(0);
610	smb_rq_done(rqp);
611	return error;
612}
613
614int
615smb_write(struct smb_share *ssp, u_int16_t fid, struct uio *uio,
616	struct smb_cred *scred)
617{
618	int error = 0, len, tsize, resid;
619	struct uio olduio;
620
621	/*
622	 * review: manage iov more precisely
623	 */
624	if (uio->uio_iovcnt != 1) {
625		SMBERROR("can't handle iovcnt > 1\n");
626		return EIO;
627	}
628	tsize = uio->uio_resid;
629	olduio = *uio;
630	while (tsize > 0) {
631		len = tsize;
632		error = smb_smb_write(ssp, fid, &len, &resid, uio, scred);
633		if (error)
634			break;
635		if (resid < len) {
636			error = EIO;
637			break;
638		}
639		tsize -= resid;
640	}
641	if (error) {
642		*uio = olduio;
643	}
644	return error;
645}
646
647int
648smb_smb_echo(struct smb_vc *vcp, struct smb_cred *scred)
649{
650	struct smb_rq *rqp;
651	struct mbchain *mbp;
652	int error;
653
654	error = smb_rq_alloc(VCTOCP(vcp), SMB_COM_ECHO, scred, &rqp);
655	if (error)
656		return error;
657	mbp = &rqp->sr_rq;
658	smb_rq_wstart(rqp);
659	mb_put_uint16le(mbp, 1);
660	smb_rq_wend(rqp);
661	smb_rq_bstart(rqp);
662	mb_put_uint32le(mbp, 0);
663	smb_rq_bend(rqp);
664	error = smb_rq_simple(rqp);
665	SMBSDEBUG("%d\n", error);
666	smb_rq_done(rqp);
667	return error;
668}
669