1/*
2 * Copyright (c) 2000, 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 * $Id: rap.c,v 1.8 2001/02/24 15:56:05 bp Exp $
33 * $FreeBSD$
34 *
35 * This is very simple implementation of RAP protocol.
36 */
37#include <sys/param.h>
38#include <sys/endian.h>
39#include <sys/errno.h>
40#include <sys/stat.h>
41#include <ctype.h>
42#include <err.h>
43#include <stdio.h>
44#include <unistd.h>
45#include <string.h>
46#include <stdlib.h>
47#include <sysexits.h>
48
49#include <netsmb/smb_lib.h>
50#include <netsmb/smb_conn.h>
51#include <netsmb/smb_rap.h>
52
53/*#include <sys/ioctl.h>*/
54
55static int
56smb_rap_parserqparam(const char *s, char **next, int *rlen)
57{
58	char *np;
59	int len, m;
60
61	m = 1;
62	switch (*s++) {
63	    case 'L':
64	    case 'T':
65	    case 'W':
66		len = 2;
67		break;
68	    case 'D':
69	    case 'O':
70		len = 4;
71		break;
72	    case 'b':
73	    case 'F':
74		len = 1;
75		break;
76	    case 'r':
77	    case 's':
78		len = 0;
79		break;
80	    default:
81		return EINVAL;
82	}
83	if (isdigit(*s)) {
84		len *= strtoul(s, &np, 10);
85		s = np;
86	}
87	*rlen = len;
88	*(const char**)next = s;
89	return 0;
90}
91
92static int
93smb_rap_parserpparam(const char *s, char **next, int *rlen)
94{
95	char *np;
96	int len, m;
97
98	m = 1;
99	switch (*s++) {
100	    case 'e':
101	    case 'h':
102		len = 2;
103		break;
104	    case 'i':
105		len = 4;
106		break;
107	    case 'g':
108		len = 1;
109		break;
110	    default:
111		return EINVAL;
112	}
113	if (isdigit(*s)) {
114		len *= strtoul(s, &np, 10);
115		s = np;
116	}
117	*rlen = len;
118	*(const char**)next = s;
119	return 0;
120}
121
122static int
123smb_rap_parserpdata(const char *s, char **next, int *rlen)
124{
125	char *np;
126	int len, m;
127
128	m = 1;
129	switch (*s++) {
130	    case 'B':
131		len = 1;
132		break;
133	    case 'W':
134		len = 2;
135		break;
136	    case 'D':
137	    case 'O':
138	    case 'z':
139		len = 4;
140		break;
141	    default:
142		return EINVAL;
143	}
144	if (isdigit(*s)) {
145		len *= strtoul(s, &np, 10);
146		s = np;
147	}
148	*rlen = len;
149	*(const char**)next = s;
150	return 0;
151}
152
153static int
154smb_rap_rqparam_z(struct smb_rap *rap, const char *value)
155{
156	int len = strlen(value) + 1;
157
158	bcopy(value, rap->r_npbuf, len);
159	rap->r_npbuf += len;
160	rap->r_plen += len;
161	return 0;
162}
163
164static int
165smb_rap_rqparam(struct smb_rap *rap, char ptype, char plen, long value)
166{
167	char *p = rap->r_npbuf;
168	int len;
169
170	switch (ptype) {
171	    case 'L':
172	    case 'W':
173		setwle(p, 0, value);
174		len = 2;
175		break;
176	    case 'D':
177		setdle(p, 0, value);
178		len = 4;
179		break;
180	    case 'b':
181		memset(p, value, plen);
182		len = plen;
183	    default:
184		return EINVAL;
185	}
186	rap->r_npbuf += len;
187	rap->r_plen += len;
188	return 0;
189}
190
191int
192smb_rap_create(int fn, const char *param, const char *data,
193	struct smb_rap **rapp)
194{
195	struct smb_rap *rap;
196	char *p;
197	int plen, len;
198
199	rap = malloc(sizeof(*rap));
200	if (rap == NULL)
201		return ENOMEM;
202	bzero(rap, sizeof(*rap));
203	p = rap->r_sparam = rap->r_nparam = strdup(param);
204	rap->r_sdata = rap->r_ndata = strdup(data);
205	/*
206	 * Calculate length of request parameter block
207	 */
208	len = 2 + strlen(param) + 1 + strlen(data) + 1;
209
210	while (*p) {
211		if (smb_rap_parserqparam(p, &p, &plen) != 0)
212			break;
213		len += plen;
214	}
215	rap->r_pbuf = rap->r_npbuf = malloc(len);
216	smb_rap_rqparam(rap, 'W', 1, fn);
217	smb_rap_rqparam_z(rap, rap->r_sparam);
218	smb_rap_rqparam_z(rap, rap->r_sdata);
219	*rapp = rap;
220	return 0;
221}
222
223void
224smb_rap_done(struct smb_rap *rap)
225{
226	if (rap->r_sparam)
227		free(rap->r_sparam);
228	if (rap->r_sdata)
229		free(rap->r_sdata);
230	free(rap);
231}
232
233int
234smb_rap_setNparam(struct smb_rap *rap, long value)
235{
236	char *p = rap->r_nparam;
237	char ptype = *p;
238	int error, plen;
239
240	error = smb_rap_parserqparam(p, &p, &plen);
241	if (error)
242		return error;
243	switch (ptype) {
244	    case 'L':
245		rap->r_rcvbuflen = value;
246		/* FALLTHROUGH */
247	    case 'W':
248	    case 'D':
249	    case 'b':
250		error = smb_rap_rqparam(rap, ptype, plen, value);
251		break;
252	    default:
253		return EINVAL;
254	}
255	rap->r_nparam = p;
256	return 0;
257}
258
259int
260smb_rap_setPparam(struct smb_rap *rap, void *value)
261{
262	char *p = rap->r_nparam;
263	char ptype = *p;
264	int error, plen;
265
266	error = smb_rap_parserqparam(p, &p, &plen);
267	if (error)
268		return error;
269	switch (ptype) {
270	    case 'r':
271		rap->r_rcvbuf = value;
272		break;
273	    default:
274		return EINVAL;
275	}
276	rap->r_nparam = p;
277	return 0;
278}
279
280static int
281smb_rap_getNparam(struct smb_rap *rap, long *value)
282{
283	char *p = rap->r_nparam;
284	char ptype = *p;
285	int error, plen;
286
287	error = smb_rap_parserpparam(p, &p, &plen);
288	if (error)
289		return error;
290	switch (ptype) {
291	    case 'h':
292		*value = le16toh(*(u_int16_t*)rap->r_npbuf);
293		break;
294	    default:
295		return EINVAL;
296	}
297	rap->r_npbuf += plen;
298	rap->r_nparam = p;
299	return 0;
300}
301
302int
303smb_rap_request(struct smb_rap *rap, struct smb_ctx *ctx)
304{
305	u_int16_t *rp, conv;
306	u_int32_t *p32;
307	char *dp, *p = rap->r_nparam;
308	char ptype;
309	int error, rdatacnt, rparamcnt, entries, done, dlen;
310
311	rdatacnt = rap->r_rcvbuflen;
312	rparamcnt = rap->r_plen;
313	error = smb_t2_request(ctx, 0, 0, "\\PIPE\\LANMAN",
314	    rap->r_plen, rap->r_pbuf,		/* int tparamcnt, void *tparam */
315	    0, NULL,				/* int tdatacnt, void *tdata */
316	    &rparamcnt, rap->r_pbuf,		/* rparamcnt, void *rparam */
317	    &rdatacnt, rap->r_rcvbuf		/* int *rdatacnt, void *rdata */
318	);
319	if (error)
320		return error;
321	rp = (u_int16_t*)rap->r_pbuf;
322	rap->r_result = le16toh(*rp++);
323	conv = le16toh(*rp++);
324	rap->r_npbuf = (char*)rp;
325	rap->r_entries = entries = 0;
326	done = 0;
327	while (!done && *p) {
328		ptype = *p;
329		switch (ptype) {
330		    case 'e':
331			rap->r_entries = entries = le16toh(*(u_int16_t*)rap->r_npbuf);
332			rap->r_npbuf += 2;
333			p++;
334			break;
335		    default:
336			done = 1;
337		}
338/*		error = smb_rap_parserpparam(p, &p, &plen);
339		if (error) {
340			smb_error("reply parameter mismath %s", 0, p);
341			return EBADRPC;
342		}*/
343	}
344	rap->r_nparam = p;
345	/*
346	 * In general, unpacking entries we may need to relocate
347	 * entries for proper alingning. For now use them as is.
348	 */
349	dp = rap->r_rcvbuf;
350	while (entries--) {
351		p = rap->r_sdata;
352		while (*p) {
353			ptype = *p;
354			error = smb_rap_parserpdata(p, &p, &dlen);
355			if (error) {
356				smb_error("reply data mismath %s", 0, p);
357				return EBADRPC;
358			}
359			switch (ptype) {
360			    case 'z':
361				p32 = (u_int32_t*)dp;
362				*p32 = (*p32 & 0xffff) - conv;
363				break;
364			}
365			dp += dlen;
366		}
367	}
368	return error;
369}
370
371int
372smb_rap_error(struct smb_rap *rap, int error)
373{
374	if (error)
375		return error;
376	if (rap->r_result == 0)
377		return 0;
378	return rap->r_result | SMB_RAP_ERROR;
379}
380
381int
382smb_rap_NetShareEnum(struct smb_ctx *ctx, int sLevel, void *pbBuffer,
383	int cbBuffer, int *pcEntriesRead, int *pcTotalAvail)
384{
385	struct smb_rap *rap;
386	long lval;
387	int error;
388
389	error = smb_rap_create(0, "WrLeh", "B13BWz", &rap);
390	if (error)
391		return error;
392	smb_rap_setNparam(rap, sLevel);		/* W - sLevel */
393	smb_rap_setPparam(rap, pbBuffer);	/* r - pbBuffer */
394	smb_rap_setNparam(rap, cbBuffer);	/* L - cbBuffer */
395	error = smb_rap_request(rap, ctx);
396	if (error == 0) {
397		*pcEntriesRead = rap->r_entries;
398		error = smb_rap_getNparam(rap, &lval);
399		*pcTotalAvail = lval;
400	}
401	error = smb_rap_error(rap, error);
402	smb_rap_done(rap);
403	return error;
404}
405