Subversion Repositories planix.SVN

Rev

Rev 2 | Details | Compare with Previous | Last modification | View Log | RSS feed

Rev Author Line No. Line
2 - 1
#include "headers.h"
2
 
3
typedef struct Args Args;
4
 
5
struct Args {
6
	ulong pcount;
7
	ulong poffset;
8
	ulong pdisplacement;
9
	ulong dcount;
10
	ulong doffset;
11
	ulong ddisplacement;
12
	uchar scount;
13
};
14
 
15
int
16
_smbtransactiondecodeprimary(SmbTransaction *t, SmbHeader *h, uchar *pdata, SmbBuffer *b, int hasname, char **errmsgp)
17
{
18
	ushort poffset, doffset;
19
 
20
	if (h->wordcount < 14) {
21
		smbstringprint(errmsgp, "word count less than 14");
22
		return -1;
23
	}
24
	t->in.scount = pdata[13 * 2];
25
	if (h->wordcount != 14 + t->in.scount) {
26
		smbstringprint(errmsgp, "smbcomtransaction: word count invalid\n");
27
		return -1;
28
	}
29
	t->in.tpcount = smbnhgets(pdata); pdata += 2;
30
	t->in.tdcount = smbnhgets(pdata); pdata += 2;
31
	t->in.maxpcount = smbnhgets(pdata); pdata += 2;
32
	t->in.maxdcount = smbnhgets(pdata); pdata += 2;
33
	t->in.maxscount = *pdata++;
34
	pdata++;
35
	t->in.flags = smbnhgets(pdata); pdata += 2;
36
	pdata += 4; /* timeout */
37
	pdata += 2;
38
	t->in.pcount = smbnhgets(pdata); pdata += 2;
39
	poffset = smbnhgets(pdata); pdata += 2;
40
	t->in.dcount = smbnhgets(pdata); pdata += 2;
41
	doffset = smbnhgets(pdata); pdata += 2;
42
	pdata++; /* scount */
43
	pdata++; /* reserved */
44
	smbfree(&t->in.setup);
45
	if (t->in.scount) {
46
		int x;
47
		t->in.setup = smbemalloc(t->in.scount * sizeof(ushort));
48
		for (x = 0; x < t->in.scount; x++) {
49
			t->in.setup[x] = smbnhgets(pdata);
50
			pdata += 2;
51
		}
52
	}
53
	smbfree(&t->in.name);
54
	if (hasname && !smbbuffergetstring(b, h, SMB_STRING_PATH, &t->in.name)) {
55
		smbstringprint(errmsgp, "not enough bdata for name");
56
		return -1;
57
	}
58
	if (poffset + t->in.pcount > smbbufferwriteoffset(b)) {
59
		smbstringprint(errmsgp, "not enough bdata for parameters");
60
		return -1;
61
	}
62
	if (t->in.pcount > t->in.tpcount) {
63
		smbstringprint(errmsgp, "too many parameters");
64
		return -1;
65
	}
66
	smbfree(&t->in.parameters);
67
	t->in.parameters = smbemalloc(t->in.tpcount);
68
	memcpy(t->in.parameters, smbbufferpointer(b, poffset), t->in.pcount);
69
	if (doffset + t->in.dcount > smbbufferwriteoffset(b)) {
70
		smbstringprint(errmsgp, "not enough bdata for data");
71
		return -1;
72
	}
73
	if (t->in.dcount > t->in.tdcount) {
74
		smbstringprint(errmsgp, "too much data");
75
		return -1;
76
	}
77
	smbfree(&t->in.data);
78
	t->in.data = smbemalloc(t->in.tdcount);
79
	memcpy(t->in.data, smbbufferpointer(b, doffset), t->in.dcount);
80
	if (t->in.dcount < t->in.tdcount || t->in.pcount < t->in.tpcount)
81
		return 0;
82
	return 1;
83
}
84
 
85
int
86
decoderesponse(SmbTransaction *t, Args *a, SmbBuffer *b, char **errmsgp)
87
{
88
	if (t->out.tpcount > smbbufferwritemaxoffset(t->out.parameters)) {
89
		smbstringprint(errmsgp, "decoderesponse: too many parameters for buffer");
90
		return 0;
91
	}
92
	if (t->out.tdcount > smbbufferwritemaxoffset(t->out.data)) {
93
		smbstringprint(errmsgp, "decoderesponse: too much data for buffer");
94
		return 0;
95
	}
96
	if (a->pdisplacement + a->pcount > t->out.tpcount) {
97
		smbstringprint(errmsgp, "decoderesponse: more parameters than tpcount");
98
		return 0;
99
	}
100
	if (a->pdisplacement != smbbufferwriteoffset(t->out.parameters)) {
101
		smbstringprint(errmsgp, "decoderesponse: parameter displacement inconsistent");
102
		return 0;
103
	}
104
	if (a->ddisplacement + a->dcount > t->out.tdcount) {
105
		smbstringprint(errmsgp, "decoderesponse: more data than tdcount");
106
		return 0;
107
	}
108
	if (a->ddisplacement != smbbufferwriteoffset(t->out.data)) {
109
		smbstringprint(errmsgp, "decoderesponse: data displacement inconsistent");
110
		return 0;
111
	}
112
	assert(a->scount == 0);
113
	if (a->pcount) {
114
		if (!smbbufferreadskipto(b, a->poffset)) {
115
			smbstringprint(errmsgp, "smbtransactiondecoderesponse: invalid parameter offset");
116
			return 0;
117
		}
118
		if (!smbbuffercopy(t->out.parameters, b, a->pcount)) {
119
			smbstringprint(errmsgp, "smbtransactiondecoderesponse: not enough data for parameters");
120
			return 0;
121
		}
122
	}
123
	if (a->dcount) {
124
		if (!smbbufferreadskipto(b, a->doffset)) {
125
			smbstringprint(errmsgp, "smbtransactiondecoderesponse: invalid data offset");
126
			return 0;
127
		}
128
		if (!smbbuffercopy(t->out.data, b, a->dcount)) {
129
			smbstringprint(errmsgp, "smbtransactiondecoderesponse: not enough data for data");
130
			return 0;
131
		}
132
	}
133
	return 1;
134
}
135
 
136
int
137
smbtransactiondecoderesponse(SmbTransaction *t, SmbHeader *h, uchar *pdata, SmbBuffer *b, char **errmsgp)
138
{
139
	Args a;
140
 
141
	if (h->command != SMB_COM_TRANSACTION) {
142
		smbstringprint(errmsgp, "smbtransactiondecoderesponse: not an SMB_COM_TRANSACTION");
143
		return 0;
144
	}
145
	if (h->wordcount < 10) {
146
		smbstringprint(errmsgp, "smbtransactiondecoderesponse: word count less than 10");
147
		return -1;
148
	}
149
	t->out.tpcount = smbnhgets(pdata); pdata += 2;
150
	t->out.tdcount = smbnhgets(pdata); pdata += 2;
151
	pdata += 2;
152
	a.pcount = smbnhgets(pdata); pdata += 2;
153
	a.poffset =  smbnhgets(pdata); pdata += 2;
154
	a.pdisplacement = smbnhgets(pdata); pdata += 2;
155
	a.dcount = smbnhgets(pdata); pdata += 2;
156
	a.doffset =  smbnhgets(pdata); pdata += 2;
157
	a.ddisplacement = smbnhgets(pdata); pdata += 2;
158
	a.scount = *pdata;
159
	if (a.scount != h->wordcount - 10) {
160
		smbstringprint(errmsgp, "smbtransactiondecoderesponse: scount inconsistent");
161
		return 0;
162
	}
163
	return decoderesponse(t, &a, b, errmsgp);
164
}
165
 
166
int
167
smbtransactiondecoderesponse2(SmbTransaction *t, SmbHeader *h, uchar *pdata, SmbBuffer *b, char **errmsgp)
168
{
169
	Args a;
170
 
171
	if (h->command != SMB_COM_TRANSACTION2) {
172
		smbstringprint(errmsgp, "smbtransactiondecoderesponse2: not an SMB_COM_TRANSACTION2");
173
		return 0;
174
	}
175
	if (h->wordcount < 10) {
176
		smbstringprint(errmsgp, "smbtransactiondecoderesponse2: word count less than 10");
177
		return -1;
178
	}
179
	t->out.tpcount = smbnhgets(pdata); pdata += 2;
180
	t->out.tdcount = smbnhgets(pdata); pdata += 2;
181
	pdata += 2;
182
	a.pcount = smbnhgets(pdata); pdata += 2;
183
	a.poffset =  smbnhgets(pdata); pdata += 2;
184
	a.pdisplacement = smbnhgets(pdata); pdata += 2;
185
	a.dcount = smbnhgets(pdata); pdata += 2;
186
	a.doffset =  smbnhgets(pdata); pdata += 2;
187
	a.ddisplacement = smbnhgets(pdata); pdata += 2;
188
	a.scount = *pdata;
189
	if (a.scount != h->wordcount - 10) {
190
		smbstringprint(errmsgp, "smbtransactiondecoderesponse2: scount inconsistent");
191
		return 0;
192
	}
193
	return decoderesponse(t, &a, b, errmsgp);
194
}
195
 
196
int
197
smbtransactiondecodeprimary(SmbTransaction *t, SmbHeader *h, uchar *pdata, SmbBuffer *b, char **errmsgp)
198
{
199
	return _smbtransactiondecodeprimary(t, h, pdata, b, 1, errmsgp);
200
}
201
 
202
int
203
smbtransactiondecodeprimary2(SmbTransaction *t, SmbHeader *h, uchar *pdata, SmbBuffer *b, char **errmsgp)
204
{
205
	return _smbtransactiondecodeprimary(t, h, pdata, b, 0, errmsgp);
206
}
207
 
208
void
209
smbtransactionfree(SmbTransaction *t)
210
{
211
	free(t->in.parameters);
212
	free(t->in.data);
213
	free(t->in.setup);
214
	free(t->in.name);
215
	smbbufferfree(&t->out.parameters);
216
	smbbufferfree(&t->out.data);
217
}
218
 
219
static int
220
_transactionencodeprimary(SmbTransaction *t, uchar cmd, SmbHeader *h, SmbPeerInfo *p, SmbBuffer *ob,
221
	uchar *wordcountp, ushort *bytecountp, char **errmsgp)
222
{
223
	SmbHeader mh;
224
	ulong countsfixupoffset, bytecountfixupoffset;
225
	int x;
226
	mh = *h;
227
	*wordcountp = mh.wordcount = 14 + t->in.scount;
228
	mh.flags &= ~SMB_FLAGS_SERVER_TO_REDIR;
229
	mh.command = cmd;
230
	if (!smbbufferputheader(ob, &mh, p)) {
231
	toosmall:
232
		smbstringprint(errmsgp, "output buffer too small");
233
		return 0;
234
	}
235
	if (t->in.tpcount > 65535 || t->in.tdcount > 65535 || t->in.maxpcount > 65535 || t->in.maxdcount > 65535) {
236
		smbstringprint(errmsgp, "counts too big");
237
		return 0;
238
	}
239
	if (!smbbufferputs(ob, t->in.tpcount)
240
		|| !smbbufferputs(ob, t->in.tdcount)
241
		|| !smbbufferputs(ob, t->in.maxpcount)
242
		|| !smbbufferputs(ob, t->in.maxdcount)
243
		|| !smbbufferputb(ob, t->in.maxscount)
244
		|| !smbbufferputb(ob, 0)
245
		|| !smbbufferputs(ob, t->in.flags)
246
		|| !smbbufferputl(ob, 0)
247
		|| !smbbufferputs(ob, 0))
248
		goto toosmall;
249
	countsfixupoffset = smbbufferwriteoffset(ob);
250
	if (!smbbufferputs(ob, 0)
251
		|| !smbbufferputs(ob, 0)
252
		|| !smbbufferputs(ob, 0)
253
		|| !smbbufferputs(ob, 0))
254
		goto toosmall;
255
	if (!smbbufferputb(ob, t->in.scount)
256
		|| !smbbufferputb(ob, 0))
257
		goto toosmall;
258
	for (x = 0; x < t->in.scount; x++)
259
		if (!smbbufferputs(ob, t->in.setup[x]))
260
			goto toosmall;
261
	bytecountfixupoffset = smbbufferwriteoffset(ob);
262
	if (!smbbufferputs(ob, 0))
263
		goto toosmall;
264
	smbbufferwritelimit(ob, smbbufferwriteoffset(ob) + 65535);
265
	if (!smbbufferputstring(ob, p, SMB_STRING_UPCASE, t->in.name))
266
		goto toosmall;
267
	if (t->in.pcount < t->in.tpcount) {
268
		ulong align = smbbufferwriteoffset(ob) & 1;
269
		ulong pthistime;
270
		pthistime = smbbufferwritespace(ob) - align;
271
		if (pthistime > t->in.tpcount - t->in.pcount)
272
			pthistime = t->in.tpcount - t->in.pcount;
273
		if (pthistime > 65535)
274
			pthistime = 65535;
275
		if (smbbufferwriteoffset(ob) > 65535)
276
			pthistime = 0;
277
		if (pthistime) {
278
			assert(smbbufferalignl2(ob, 0));
279
			assert(smbbufferoffsetputs(ob, countsfixupoffset, pthistime));
280
			assert(smbbufferoffsetputs(ob, countsfixupoffset + 2, smbbufferwriteoffset(ob)));
281
			assert(smbbufferputbytes(ob, t->in.parameters + t->in.pcount, pthistime));
282
		}
283
		t->in.pcount += pthistime;
284
	}
285
	if (t->in.dcount < t->in.tdcount) {
286
		ulong align = smbbufferwriteoffset(ob) & 1;
287
		ulong dthistime;
288
		dthistime = smbbufferwritespace(ob) - align;
289
		if (dthistime > t->in.tdcount - t->in.dcount)
290
			dthistime = t->in.tdcount - t->in.dcount;
291
		if (dthistime > 65535)
292
			dthistime = 65535;
293
		if (smbbufferwriteoffset(ob) > 65535)
294
			dthistime = 0;
295
		if (dthistime) {
296
			assert(smbbufferalignl2(ob, 0));
297
			assert(smbbufferoffsetputs(ob, countsfixupoffset + 4, dthistime));
298
			assert(smbbufferoffsetputs(ob, countsfixupoffset + 6, smbbufferwriteoffset(ob)));
299
			assert(smbbufferputbytes(ob, t->in.data + t->in.dcount, dthistime));
300
		}
301
		t->in.dcount += dthistime;
302
	}
303
	*bytecountp = smbbufferwriteoffset(ob) - bytecountfixupoffset - 2;
304
	assert(smbbufferoffsetputs(ob, bytecountfixupoffset, *bytecountp));
305
	return 1;
306
}
307
 
308
int
309
smbtransactionencodeprimary(SmbTransaction *t, SmbHeader *h, SmbPeerInfo *p, SmbBuffer *ob,
310
	uchar *wordcountp, ushort *bytecountp, char **errmsgp)
311
{
312
	return _transactionencodeprimary(t, SMB_COM_TRANSACTION, h, p,ob, wordcountp, bytecountp, errmsgp);
313
};
314
 
315
int
316
smbtransactionencodeprimary2(SmbTransaction *t, SmbHeader *h, SmbPeerInfo *p, SmbBuffer *ob,
317
	uchar *wordcountp, ushort *bytecountp, char **errmsgp)
318
{
319
	return _transactionencodeprimary(t, SMB_COM_TRANSACTION2, h, p,ob, wordcountp, bytecountp, errmsgp);
320
};
321
 
322
int
323
_transactionencoderesponse(SmbTransaction *t, SmbHeader *h, SmbPeerInfo *p, SmbBuffer *ob, uchar cmd,
324
	char **errmsgp)
325
{
326
	SmbHeader mh;
327
	ulong countsfixupoffset, bytecountfixupoffset;
328
	int palign, dalign;
329
	ulong pbytecount, dbytecount;
330
	ulong poffset, doffset;
331
 
332
	if (t->in.maxpcount > 65535 || t->in.maxdcount > 65535) {
333
		smbstringprint(errmsgp, "counts too big");
334
		return 0;
335
	}
336
	mh = *h;
337
	mh.wordcount = 10;
338
	mh.flags &= ~SMB_FLAGS_SERVER_TO_REDIR;
339
	mh.command = cmd;
340
	mh.errclass = SUCCESS;
341
	mh.error = SUCCESS;
342
	if (!smbbufferputheader(ob, &mh, p)
343
		|| !smbbufferputs(ob, smbbufferwriteoffset(t->out.parameters))
344
		|| !smbbufferputs(ob, smbbufferwriteoffset(t->out.data))
345
		|| !smbbufferputs(ob, 0)) {
346
	toosmall:
347
		smbstringprint(errmsgp, "output buffer too small");
348
		goto toosmall;
349
	}
350
	countsfixupoffset = smbbufferwriteoffset(ob);
351
	if (!smbbufferputbytes(ob, nil, 6 * sizeof(ushort))
352
		|| !smbbufferputb(ob, 0)	// scount == 0
353
		|| !smbbufferputb(ob, 0))	// reserved2
354
		goto toosmall;
355
	/* now the byte count */
356
	bytecountfixupoffset = smbbufferwriteoffset(ob);
357
	if (!smbbufferputs(ob, 0))
358
		goto toosmall;
359
	smbbufferwritelimit(ob, smbbufferwriteoffset(ob) + 65535);
360
	palign = bytecountfixupoffset & 1;
361
	if (palign && !smbbufferputb(ob, 0))
362
		goto toosmall;
363
	pbytecount = smbbufferreadspace(t->out.parameters);
364
	if (pbytecount > smbbufferwritespace(ob))
365
		pbytecount = smbbufferwritespace(ob);
366
	poffset = smbbufferwriteoffset(ob);
367
	if (poffset > 65535)
368
		goto toosmall;
369
	if (!smbbufferputbytes(ob, smbbufferreadpointer(t->out.parameters), pbytecount))
370
		goto toosmall;
371
	dalign = smbbufferwritespace(ob) > 0 && (smbbufferwriteoffset(ob) & 1) != 0;
372
	if (dalign && !smbbufferputb(ob, 0))
373
		goto toosmall;
374
	dbytecount = smbbufferreadspace(t->out.data);
375
	if (dbytecount > smbbufferwritespace(ob))
376
		dbytecount = smbbufferwritespace(ob);
377
	doffset = smbbufferwriteoffset(ob);
378
	if (doffset > 65535)
379
		goto toosmall;
380
	if (!smbbufferputbytes(ob, smbbufferreadpointer(t->out.data), dbytecount))
381
		goto toosmall;
382
	if (!smbbufferoffsetputs(ob, bytecountfixupoffset, palign + pbytecount + dalign + dbytecount)
383
		|| !smbbufferoffsetputs(ob, countsfixupoffset, pbytecount)
384
		|| !smbbufferoffsetputs(ob, countsfixupoffset + 2, poffset)
385
		|| !smbbufferoffsetputs(ob, countsfixupoffset + 4, smbbufferreadoffset(t->out.parameters))
386
		|| !smbbufferoffsetputs(ob, countsfixupoffset + 6, dbytecount)
387
		|| !smbbufferoffsetputs(ob, countsfixupoffset + 8, doffset)
388
		|| !smbbufferoffsetputs(ob, countsfixupoffset + 10, smbbufferreadoffset(t->out.data)))
389
		goto toosmall;
390
	assert(smbbufferoffsetputs(ob, bytecountfixupoffset, smbbufferwriteoffset(ob) - bytecountfixupoffset - 2));
391
	smbbuffergetbytes(t->out.parameters, nil, pbytecount);
392
	smbbuffergetbytes(t->out.data, nil, dbytecount);
393
	return 1;
394
}
395
 
396
int
397
smbtransactionencoderesponse(SmbTransaction *t, SmbHeader *h, SmbPeerInfo *p, SmbBuffer *ob, char **errmsgp)
398
{
399
	return _transactionencoderesponse(t, h, p, ob, SMB_COM_TRANSACTION, errmsgp);
400
}
401
 
402
int
403
smbtransactionencoderesponse2(SmbTransaction *t, SmbHeader *h, SmbPeerInfo *p, SmbBuffer *ob, char **errmsgp)
404
{
405
	return _transactionencoderesponse(t, h, p, ob, SMB_COM_TRANSACTION2, errmsgp);
406
}
407
 
408
int
409
smbtransactionrespond(SmbTransaction *t, SmbHeader *h, SmbPeerInfo *p, SmbBuffer *ob, SmbTransactionMethod *method, void *magic, char **errmsgp)
410
{
411
	/* generate one or more responses */
412
	while (smbbufferreadspace(t->out.parameters) || smbbufferreadspace(t->out.data)) {
413
		assert(method->encoderesponse);
414
		if (!(*method->encoderesponse)(t, h, p, ob, errmsgp))
415
			return 0;
416
		assert(method->sendresponse);
417
		if (!(*method->sendresponse)(magic, ob, errmsgp))
418
			return 0;
419
	}
420
	return 1;
421
}
422
 
423
int
424
smbtransactionnbdgramsend(void *magic, SmbBuffer *ob, char **errmsgp)
425
{
426
	NbDgramSendParameters *p = magic;
427
//print("sending to %B\n", p->to);
428
//nbdumpdata(smbbufferreadpointer(ob), smbbufferreadspace(ob));
429
	if (!nbdgramsend(p, smbbufferreadpointer(ob), smbbufferreadspace(ob))) {
430
		smbstringprint(errmsgp, "dgram send failed");
431
		return 0;
432
	}
433
	return 1;
434
}
435
 
436
SmbTransactionMethod smbtransactionmethoddgram = {
437
	.encodeprimary = smbtransactionencodeprimary,
438
	.sendrequest = smbtransactionnbdgramsend,
439
	.encoderesponse = smbtransactionencoderesponse,
440
};
441
 
442
int
443
smbtransactionexecute(SmbTransaction *t, SmbHeader *h, SmbPeerInfo *p, SmbBuffer *iob, SmbTransactionMethod *method, void *magic, SmbHeader *rhp, char **errmsgp)
444
{
445
	uchar sentwordcount;
446
	ushort sentbytecount;
447
	SmbHeader rh;
448
	smbbufferreset(iob);
449
	if (!(*method->encodeprimary)(t, h, p, iob, &sentwordcount, &sentbytecount, errmsgp))
450
		return 0;
451
//	smblogprint(-1, "sent...\n");
452
//	smblogdata(-1, smblogprint, smbbufferreadpointer(iob), smbbufferreadspace(iob));
453
	if (!(*method->sendrequest)(magic, iob, errmsgp))
454
		return 0;
455
	if (t->in.pcount < t->in.tpcount || t->in.dcount < t->in.tdcount) {
456
		uchar wordcount;
457
		ushort bytecount;
458
		/* secondary needed */
459
		if (method->encodesecondary == nil || method->receiveintermediate == nil) {
460
			smbstringprint(errmsgp, "buffer too small and secondaries not allowed");
461
			return 0;
462
		}
463
		if (!(*method->receiveintermediate)(magic, &wordcount, &bytecount, errmsgp))
464
			return 0;
465
		if (sentwordcount != wordcount || sentbytecount != bytecount) {
466
			smbstringprint(errmsgp, "server intermediate reply counts differ");
467
			return 0;
468
		}
469
		do {
470
			if (!(*method->encodesecondary)(t, h, iob, errmsgp))
471
				return 0;
472
			if (!(*method->sendrequest)(magic, iob, errmsgp))
473
				return 0;
474
		} while (t->in.pcount < t->in.tpcount || t->in.dcount < t->in.tdcount);
475
	}
476
	if (method->receiveresponse == nil || method->decoderesponse == nil)
477
		return 1;
478
	do {
479
		uchar *pdata;
480
		ushort bytecount;
481
 
482
		if (!(*method->receiveresponse)(magic, iob, errmsgp))
483
			return 0;
484
		if (!smbbuffergetheader(iob, &rh, &pdata, &bytecount)) {
485
			smbstringprint(errmsgp, "smbtransactionexecute: invalid response header");
486
			return 0;
487
		}
488
		if (!smbcheckheaderdirection(&rh, 1, errmsgp))
489
			return 0;
490
		if (rh.errclass != SUCCESS) {
491
			smbstringprint(errmsgp, "smbtransactionexecute: remote error %d/%d", rh.errclass, rh.error);
492
			return 0;
493
		}
494
		if (!smbbuffertrimreadlen(iob, bytecount)) {
495
			smbstringprint(errmsgp, "smbtransactionexecute: invalid bytecount");
496
			return 0;
497
		}
498
//		smblogprint(-1, "received...\n");
499
//		smblogdata(-1, smblogprint, smbbufferreadpointer(iob), smbbufferreadspace(iob));
500
		if (!(*method->decoderesponse)(t, &rh, pdata, iob, errmsgp))
501
			return 0;
502
	} while (smbbufferwriteoffset(t->out.parameters) < t->out.tpcount || smbbufferwriteoffset(t->out.data) < t->out.tdcount);
503
	if (rhp)
504
		*rhp = rh;
505
	return 1;
506
}
507